Hogar / Oficina / Descripción de la biblioteca del creador de Qt. Instalación y configuración de Qt Creator. Un ejemplo sencillo de cómo colocar widgets en la ventana de una aplicación

Descripción de la biblioteca del creador de Qt. Instalación y configuración de Qt Creator. Un ejemplo sencillo de cómo colocar widgets en la ventana de una aplicación

Este es un conjunto de herramientas de desarrollo de software multiplataforma en el lenguaje de programación C++. También existen "enlaces" a muchos otros lenguajes de programación: Python - PyQt, Ruby - QtRuby, Java - Qt Jambi, PHP - PHP-Qt y otros.
Le permite ejecutar software escrito con su ayuda en la mayoría de los modernos sistemas operativos simplemente compilando el programa para cada sistema operativo sin cambiar el código fuente. Incluye todas las clases básicas que pueden ser necesarias al desarrollar software de aplicaciones, desde elementos de interfaz gráfica hasta clases para trabajar con la red, bases de datos y XML. Qt está totalmente orientado a objetos, es fácilmente extensible y admite técnicas de programación basadas en componentes.
En este artículo, te mostraré cómo escribir un programa sencillo de "¡Hola, mundo!". usando la biblioteca Qt4

Entorno de desarrollo

Primero, definamos el entorno de desarrollo. Personalmente, uso el IDE multiplataforma Code::Blocks para escribir un programa (puedes leer más sobre cómo trabajar en este IDE con Qt4). También hay complementos para trabajar con Qt en Eclipse. Se puede integrar una versión comercial de Qt para MS Windows en MSVS. Los programas también se pueden escribir en cualquier editor de texto y luego compílelos desde la línea de comando.
Para mayor claridad, mostraré cómo compilar programas escritos en Qt manualmente.

primer programa

Primero, cree un archivo en cualquier editor de texto y asígnele un nombre, por ejemplo, main.cpp.
Escribamos lo siguiente en él:
  1. #incluir
  2. #incluir
  3. Aplicación QApplication (argc, argv);
  4. QDialog *dialog = nuevo QDialog;
  5. QLabel *etiqueta = nueva QLabel(diálogo);
  6. etiqueta->setText( "¡Hola Mundo!" );
  7. diálogo->mostrar();
  8. devolver aplicación.exec();

En las líneas 1 y 2 incluimos los archivos de encabezado Qt en los que se encuentran las clases principales.
En la línea 4 declaramos la función principal: función principal, a partir del cual comienza la ejecución de cualquier programa. Devuelve un número entero (el resultado del programa; 0 - si todo está en orden) y toma dos variables como entrada: el número de parámetros de la línea de comando y la matriz en la que están almacenados.
En la línea 5 creamos un objeto de aplicación. Pasamos variables de línea de comando a este objeto.
En la línea 6 creamos un cuadro de diálogo: una ventana gráfica de forma rectangular con una barra de título y botones en la esquina superior derecha. Crea una etiqueta (línea 7). Cuando creamos una etiqueta, pasamos un puntero al cuadro de diálogo a su constructor, que se convierte en su padre. Cuando eliminas un padre, todos sus hijos se eliminan automáticamente, lo cual es muy conveniente. Luego configuramos el texto de la etiqueta llamando a la función setText() (línea 8). Como puede ver en el ejemplo, puede utilizar etiquetas html para el texto mostrado.
En la línea 9 mostramos nuestro cuadro de diálogo de etiqueta en la pantalla.
Finalmente, en la línea 10, iniciamos el bucle de eventos del sistema operativo de la aplicación. Devolvemos el resultado de la operación del objeto como resultado del programa.

Compilacion

Ahora compilaremos el programa escrito.
Vayamos al directorio donde guardamos nuestro archivo main.cpp y ejecutemos el comando

$ qmake-proyecto

Esto creará una plantilla de proyecto Qt4, que incluirá automáticamente todos los archivos de código fuente ubicados en este directorio. El resultado será un archivo con el mismo nombre que el directorio actual y la extensión .pro. Se verá así:

PLANTILLA=aplicación
OBJETIVO =
RUTA DEPENDIENTE += .
INCLUYERUTA += .

#Aporte
FUENTES += principal.cpp

Como resultado, obtendremos un Makefile, que usaremos para compilar el programa ejecutando el siguiente comando:

Esperemos hasta que finalice el proceso de compilación y ejecutemos nuestro primer programa. Se verá algo como esto:

Segundo programa

Para obtener control total sobre las ventanas creadas y otros widgets, necesita crear clases derivadas de ellas. Creemos una clase derivada MyDialog. Usaremos la clase QDialog como clase principal. Colocaremos la descripción de nuestra clase en el archivo de encabezado mydialog.h:
  1. #incluir
  2. #incluir
  3. #incluir
  4. #incluir
  5. clase MyDialog: QDialog público (
  6. Q_OBJECT
  7. público:
  8. MiDiálogo(QWidget *padre = 0);
* Este código fuente fue resaltado con Resaltador de código fuente.
En las primeras cuatro líneas incluimos los archivos de encabezado necesarios de los elementos gráficos utilizados: el cuadro de diálogo, el botón, la etiqueta y el administrador de diseño vertical. Utilice archivos de encabezado tan grandes como , etc. en proyectos grandes no se recomienda, ya que aumenta el tiempo de compilación.
En la línea seis, definimos nuestra clase para que derive de QDialog.
En la siguiente línea especificamos la macro Q_OBJECT, que le dice al preprocesador Qt que esta clase usará características Qt adicionales, por ejemplo, el sistema de señal y ranura.
En la línea 9 especificamos el constructor de nuestro cuadro de diálogo. Tiene solo un parámetro de entrada: un puntero al objeto principal (0 si no hay ningún padre).
Definiremos el constructor de nuestra clase en el archivo mydialog.cpp:
  1. #incluir "midialog.h"
  2. MiDiálogo::MiDiálogo(QWidget *padre): QDialog(padre) (
  3. QVBoxLayout *diseño = nuevo QVBoxLayout(este);
  4. QLabel *etiqueta = nueva QLabel(esta);
  5. etiqueta->setText( "¡Hola Mundo!" );
  6. QPushButton *botón = nuevo QPushButton(este);
  7. botón->setText("Cerrar");
  8. diseño->addWidget(etiqueta);
  9. diseño->addWidget(botón);
  10. conectar(botón, SEÑAL(se hizo clic()), esto, RANURA(cerrar()));
* Este código fuente fue resaltado con Resaltador de código fuente.

En la línea 4 creamos un administrador de diseño que mostrará automáticamente todos los widgets agregados verticalmente. Crear una inscripción es similar al ejemplo anterior.
En las líneas 7 y 8 creamos un botón y configuramos su texto. En las dos líneas siguientes agregamos nuestros widgets al administrador de diseño para que los organice automáticamente.
En la línea 11, conectamos la señal de clic() del botón a la ranura close() de nuestro cuadro de diálogo. Cada objeto Qt puede tener sus propias señales y ranuras, que pueden conectarse a las señales y ranuras de otros objetos y así comunicarse entre elementos del programa.
El archivo main.cpp se verá así:
  1. #incluir
  2. #incluir "midialog.h"
  3. int principal(int argc, char * argv) (
  4. Aplicación QApplication (argc, argv);
  5. MiDiálogo *diálogo = nuevo MiDiálogo;
  6. diálogo->mostrar();
  7. devolver aplicación.exec();
* Este código fuente fue resaltado con Resaltador de código fuente.

Reconstruyendo el proyecto en equipo

$ qmake-proyecto

Para que automáticamente se le agreguen nuevos archivos y lo compilamos. Así es como se ve nuestro nuevo programa:

Tercer programa

Si un cuadro de diálogo contiene muchos elementos gráficos, crear dichas ventanas puede resultar bastante tedioso. Para simplificar este proceso, existe una herramienta llamada Qt Designer. vamos a lanzarlo

Y elija crear un cuadro de diálogo sin botones. Le agregamos una etiqueta y un botón y editamos su texto. Usando la herramienta Editor de señal/ranura, conectamos la señal en la que se hizo clic () del botón a la ranura de cierre () del cuadro de diálogo. Los organizamos verticalmente usando el administrador de diseño. Guarde el archivo resultante con el nombre mydialog.ui. Posteriormente se convertirá automáticamente en un archivo de encabezado llamado ui_mydialog.h.
Cambiamos el archivo de encabezado de nuestro cuadro de diálogo mydialog.h de la siguiente manera:

Un programador novato duda constantemente de qué tecnologías empezar a dominar, esta o cualquier otra. ¿Debería un programador principiante aprender Qt? ¡Definitivamente necesario! Por ejemplo, lea esta publicación o busque algo en Internet. ¿Aún no estás seguro de si necesitas Qt? Si escribe en C++, debe saber Qt; simplemente no tiene otra alternativa.

Entonces vamos...

Por ejemplo, escribamos una calculadora simple: sumar dos números.

Nosotros creamos nuevo proyecto. Como podemos ver, existen varios tipos de aplicaciones. Nosotros, como principiantes, seleccionamos "Aplicación Qt Widgets":

Indicamos el nombre del proyecto y la carpeta donde colocar los archivos, para mí: C:\projects\qt

Qt genera automáticamente los siguientes archivos:

  • lección1.pro - archivo de proyecto
  • main.cpp - archivo principal con la función main()
  • mainwindow.cpp: código fuente de la ventana principal
  • mainwindow.h: archivo de encabezado de la ventana principal
  • mainwindow.ui: archivo de formulario de la ventana principal

Haga clic en el botón "Finalizar": se abre el editor para desarrollar programas en Qt.

Por ahora, todo debería estar claro para un desarrollador Qt novato...

Abra el formulario principal (para hacer esto, vaya a mainwindow.ui).

A la izquierda están los componentes para crear un formulario en pantalla, a la derecha está el formulario en sí, está vacío. Agreguemos los componentes necesarios y no olvidemos nombrar los campos de entrada de QLineEdit así: edtA, edtB y edtC, respectivamente.

Vemos los campos de entrada en la pantalla, sus títulos a la izquierda y el botón “A + B =”. Cuando hacemos clic en este botón debemos sumar A y B y colocar el resultado en C.

¿Qué tengo que hacer? Cualquier desarrollador novato de Qt asumirá que necesita adjuntar un controlador de clic al botón, ¡y tendrá razón! En cualquier otro marco existe algo como evento. Pero Qt decidió presumir y creó señales/ranuras. Aunque, en esencia, esto es prácticamente lo mismo.

Haga clic derecho en el botón “A + B =”, se abre un menú emergente:

Haga clic en "Ir al espacio"

Seleccione la señal clicked() y en el editor de código que se abre, escriba un pequeño código en Qt:

Void MainWindow::on_btnOK_clicked() ( int a = ui->edtA->text().toInt(); // Toma el texto edtA y conviértelo en un número a int b = ui->edtB->text() .toInt (); // Toma el texto edtB y conviértelo en un número b int c = a + b; // Agrega los números QString s = QString::number(c); ->edtC->setText (s); // Imprime el resultado en edtC )

La función de manejo de señales clicked() se llama ranura on_btnOK_clicked().

Un ejemplo sencillo de montaje manual.

Para comprender mejor Qt, sería correcto crear manualmente al menos un ejemplo simple en la consola. La técnica de montaje es la misma para proyectos de cualquier tamaño y es importante entender cómo se hace.

Veamos un ejemplo simple de una aplicación GUI que abre una ventana vacía en la pantalla. Seleccione un directorio en el disco donde crearemos el archivo principal.cpp con el siguiente contenido. Se ha agregado numeración de líneas al ejemplo para simplificar comentarios adicionales sobre el ejemplo. Debe entenderse que la numeración de líneas no debe incluirse en el archivo de trabajo, ya que no forma parte de la sintaxis de expresión válida de C++.

01. #incluir 02. #incluir 03. 04. int main(int argc, char *argv) 05. ( 06. QApplication app(argc, argv); 07. QWidget wgt; 08. wgt.setWindowTitle(tr("Hola mundo")); 09. 10 11. devolver aplicación.exec();

Las líneas 01 y 02 incluyen archivos de encabezado QAplicación Y QWidget, que, entre otras cosas, contiene declaraciones de clase. QAplicación Y QWidget. En nuestro ejemplo, creamos instancias de estas clases.

Vale la pena señalar la conveniente tradición de nombrar archivos de encabezado utilizados en Qt. Desde la versión 4 de Qt, si necesitabas una clase llamada Qxxx, entonces lo más probable es que su definición esté en el archivo de encabezado Qxxx.

En este ejemplo, la clase QAplicación Nos interesa como organizador del ciclo de recopilación de mensajes que llegarán a la ventana de nuestra aplicación GUI. Consulte la línea 11 (inicio del bucle de mensajes de la ventana). Clase QAplicación implementado a semejanza de un Singleton. Para aquellos que no estén familiarizados con este término procedente de la teoría de los patrones de diseño (o, patrones, del inglés. patrones) demos una pequeña explicación. La esencia de un singleton es que la implementación de una clase impide la posibilidad de crear más de una instancia de una clase determinada. Esta característica es importante para nosotros porque implica la posibilidad de definir una variable global dentro de la biblioteca Qt con un puntero a una única instancia de una clase determinada. Ver descripción para el símbolo. qApp en la ayuda de Qt.

La biblioteca Qt utiliza terminología de widgets. widget- artilugio, dispositivo) como elemento de la interfaz GUI. El conjunto mínimo común de propiedades de dichos elementos está representado por la clase QWidget.

Las líneas 06 y 07 crean los objetos de clase aplicación y widget. En ambos casos, los objetos se crean estáticamente. La vida útil de dichos objetos está limitada por el bloque de instrucciones (...) en el que fueron creados. Tan pronto como la ejecución del código del programa llega al corchete de cierre, ambos objetos se destruyen automáticamente. Por lo tanto, en este ejemplo no pensaremos en cómo destruir objetos Qt y analizaremos este tema más adelante.

Al crear un objeto QAplicación Se utiliza un constructor parametrizado al que se pasan los argumentos de inicio de la aplicación (copiados de los argumentos). funciones principales()). Dentro del objeto de clase, estos argumentos se analizan. La clase de aplicación admite una serie de parámetros de inicio, que se pueden encontrar en la ayuda del constructor correspondiente. QAplicación. Los parámetros de lanzamiento no incluidos en esta lista deben analizarse de forma independiente. Al final de esta lección, después de haber colocado varios controles en la ventana principal de la aplicación, le ofreceremos experimentar con el parámetro de inicio -style, para el cual, en cualquier compilación de Qt, son posibles los siguientes valores: motivo, ventanas, platino.

La línea 08 cambia uno de los atributos del objeto widget. Usando el método setWindowTitle(), configuramos nuestro propio texto para el título de nuestra futura ventana. Tenga en cuenta que la imagen de la cadena está envuelta en una llamada a la función de traducción. tr(). Este es un requisito para internacionalizar la aplicación. Para todos estos contenedores, se pueden usar herramientas especiales de Qt para crear archivos especiales con traducciones a diferentes idiomas, que se pueden usar en la aplicación para realizar reemplazos automáticos. Normalmente, estos archivos se incluyen en la compilación de la aplicación como recursos.

La línea 10 abre la ventana de la aplicación. Antes de Qt4, el widget se declaraba explícitamente como el widget principal de la aplicación antes de abrir la ventana. Esto se hizo usando el siguiente código.

App.setMainWidget(&wgt);

A partir de Qt4, dicha comunicación se realiza automáticamente accediendo al puntero global qApp a una instancia de la clase de aplicación.

La línea 11 inicia el bucle de procesamiento de mensajes del sistema operativo dirigido a la ventana de la aplicación. El bucle finaliza cuando se ejecuta cualquiera de los comandos de cierre de la aplicación. El código de salida de la aplicación lo devuelve exec() cuando sale el método. Es este código el que se convierte en el código de retorno de la función main() al pasarlo a través del operador devolver.

Ahora intentemos construir la aplicación. La forma más sencilla de hacerlo es en Linux. Para hacer esto, solo necesitas abrir la consola y ejecutar algunos comandos, que a continuación describiremos. Para Windows, dicho trabajo puede requerir establecer rutas al directorio donde se encuentra la utilidad qmake del Qt SDK. Esta utilidad implementa las reglas del sistema de construcción de proyectos QMake.

Primero debemos averiguar qué está disponible para nosotros desde la consola. Si estamos en la consola bash (*nix), entonces esto es bastante simple. Escribe el comando qhacer y presione tabulador dos veces. Deberíamos ver una lista de todos los comandos que comienzan con la combinación qhacer. Por ejemplo, en mi caso veo dos comandos: qhacer Y qmake-qt4. Esto significa que tengo dos versiones de la biblioteca instaladas desde el repositorio. Equipo qhacer corresponde a la versión Qt5 (predeterminada para ultima versión), y el equipo qmake-qt4 corresponde en consecuencia a Qt4. Ahora, dependiendo del comando que use, compilaré usando la versión Qt5 o usando la versión Qt4.

Si todo en nuestro sistema está configurado normalmente y las versiones de Qt SDK son exitosas, entonces el proyecto debe construirse usando los siguientes tres comandos.

$ qmake -proyecto $ qmake $ hacer

El primer comando creará un archivo de proyecto. El archivo del proyecto tiene el sufijo .Pro. En el contexto de Windows, sería correcto decir "extensión Pro". Conceptos sufijo de nombre de archivo Y extensión de archivo significan cosas completamente diferentes, aunque parezcan similares. Asegúrate de usarlo correctamente.

El segundo comando debería crear un archivo de script de compilación: Makefile. El tercer comando debería ejecutar un script de compilación que debería producir un archivo de aplicación ejecutable.

Si esto no sucede, intentaremos encontrar el problema.

Abra el archivo del proyecto. Intente encontrar la siguiente línea allí.

QT += interfaz gráfica de usuario

Si dicha línea no está allí, entonces debe agregarla; de lo contrario, dependiendo de la versión del SDK, el proyecto puede percibirse como un proyecto de consola y las rutas a los archivos de encabezado de la clase GUI no se incluirán en él. Esto dará lugar a errores de compilación que indicarán que no se encontraron los archivos de encabezado incluidos.

Tenga en cuenta que si se trata de Qt SDK versión 5, esta definición también debe incluir el grupo widgets Como se muestra abajo.

QT += widgets de interfaz gráfica de usuario

Un ejemplo de creación de una plantilla de aplicación GUI desde QtCreator

Abra QtCreator. Para crear un nuevo proyecto, inicie el asistente de creación de proyectos desde el menú "Archivo->Nuevo archivo o proyecto...". En la ventana que se abre en la primera página del asistente, se le solicita que seleccione una plantilla para el proyecto futuro. Para el grupo de proyectos "Aplicación", seleccione la opción "Aplicación Qt GUI" y haga clic en el botón "Elegir" para ir a la siguiente página del asistente.

En la segunda página del asistente de creación de proyectos, se le solicita que seleccione un nombre de proyecto y su directorio de ubicación. Usando el nombre del proyecto especificado, se creará un subdirectorio en el que se ubicarán los archivos del proyecto. El subdirectorio se creará en el directorio de ubicación especificado. Por lo tanto, el nombre del proyecto debe estar determinado por las reglas que debe obedecer el nombre del directorio. Sin embargo, para evitar problemas, no utilice letras ni espacios rusos. Utilice letras, números, guiones bajos y guiones en inglés (signo menos). Que nuestro proyecto se llame aplicación1. Escribámoslo en la línea. nombre, y en la línea de selección del directorio del proyecto indicaremos el directorio donde continuaremos creando proyectos en Qt. Se deben evitar los caminos con letras y espacios rusos. Tarde o temprano pueden causar problemas. Si necesita recordar esta ruta la próxima vez, marque la casilla de verificación "Usar como ubicación predeterminada del proyecto". Haga clic en el botón "Siguiente" para ir a la siguiente página del asistente.

En la tercera página del asistente de creación de proyectos, se le solicita que seleccione un SDK de Qt de la lista de los encontrados y registrados en QtCreator. Seleccione la opción Qt4. Para la versión de SDK seleccionada, debe definir perfiles de compilación del proyecto. Se ofrecen las opciones "Versión" y "Depuración". La compilación "Versión" no contiene símbolos de depuración en el archivo ejecutable y se recomienda transferirla para uso real. En todos los demás casos, es más conveniente utilizar la compilación "Depurar". En el ejemplo actual, la elección del montaje no importa. Puedes dejar ambos perfiles habilitados. A la derecha del nombre del perfil hay un campo de entrada en el que se escribe la ruta por la que se realizará el montaje correspondiente. A menudo, estos caminos se editan en función de diferentes tradiciones. En nuestro ejemplo, podemos dejar estos caminos sin cambios. Haga clic en el botón "Siguiente" para ir a la siguiente página del asistente.

En la cuarta página del asistente de creación de proyectos, se le solicita que seleccione el nombre de la clase para el formulario principal del proyecto, la clase base de la cual se debe heredar el formulario principal del proyecto y los nombres de los archivos donde se encuentra la interfaz. Se ubicará la implementación de la clase creada del formulario principal. Además, la página debe indicar si se utilizará un diseño de formulario visual. Como no utilizaremos diseño visual, debemos asegurarnos de que la casilla de verificación "Generar formulario" no esté marcada.

Lo más interesante de la cuarta página del Asistente para nuevos proyectos es elegir una clase base para crear una clase de formulario. Se ofrecen tres opciones.

  1. QVentana principal- en la mayoría de los casos, la elección más adecuada. Al heredar de esta clase, obtenemos herramientas listas para usar para colocar un menú, una barra de estado y un campo central, que se pueden implementar tanto en el estilo SDI (Interfaz de documento único) como en el estilo MDI (Interfaz de documento múltiple).
  2. QWidget— esta clase es el widget más simple. En terminología Qt, este es el elemento más simple al que se asocia algún tipo de área gráfica en la pantalla. Como clase base para la ventana principal, se utiliza, por regla general, al crear aplicaciones simples de un solo formulario y es excelente para propósitos iniciales de "estudiantes" debido a que no contiene nada "superfluo".
  3. QDiálogo— clase base para crear cuadros de diálogo modales.

Para nuestro ejemplo, elegiremos como clase base, la opción más simple es QWidget. Los nombres de la clase principal del formulario y de los archivos donde se ubicará su interfaz e implementación se pueden dejar como valores predeterminados. Haga clic en el botón "Siguiente" para ir a la siguiente página del asistente.

La quinta página del asistente de creación de proyectos le permite determinar la relación del proyecto que se está creando con los proyectos ya abiertos y especificar la elección de cualquier sistema de control de versiones de proyectos disponible. En el ejemplo actual, no estamos interesados ​​en estas funciones, por lo que debemos completar el asistente haciendo clic en el botón "Finalizar".

EN diferentes versiones QtCreator Los pasos para crear un proyecto pueden ser ligeramente diferentes, pero esencialmente estos son los puntos principales que debe comprender al crear un proyecto GUI. La descripción actual se realizó en base a la versión 2.7.0 de QtCreator.

Después de completar el Asistente para nuevo proyecto en QtCreator, se abrirá una vista con el proyecto creado. Ejecute el proyecto para verificar la configuración de su entorno de desarrollo. Si todo está en orden, entonces la plantilla de proyecto creada debería compilarse y ejecutarse. Para iniciar un proyecto normalmente, puede hacer clic en el botón con un triángulo verde ubicado en la barra de herramientas. El botón con la imagen de un triángulo verde con un escarabajo realiza las funciones de iniciar el modo de depuración. En este caso, si se establecen puntos de interrupción en el proyecto, cuando se alcancen, la ejecución aplicación en ejecución se detendrá y la línea donde se realizó la parada se resaltará en el editor de depuración.

QtCreator es notablemente diferente en diseño de otros entornos de desarrollo. Sin embargo, su diseño es sorprendentemente ergonómico. Entre otras cosas, en la era de las pantallas anchas, la barra de herramientas ubicada a la izquierda en la ventana principal del entorno de desarrollo parece muy ventajosa. En la parte inferior de la barra de herramientas, como ya se mencionó, hay botones para iniciar la aplicación y en la parte superior hay botones para seleccionar pestañas para la ventana principal del entorno de desarrollo. Veamos la lista de pestañas principales.

  • "Bienvenido" — Selección de proyectos
  • "Editar" — Editar el programa.
  • "Depurar" - Depuración del proyecto. Incluye las ventanas necesarias para el seguimiento de objetos.
  • "Proyectos": configuraciones para proyectos cargados en el entorno de desarrollo. Debe estudiar detenidamente las opciones presentadas en esta pestaña.
  • "Analizar": análisis de proyectos utilizando herramientas especiales para detectar cuellos de botella en el rendimiento y problemas de pérdida de memoria.
  • "Ayuda" Ventana de ayuda integrada. Quizás prefiera trabajar con la aplicación de ayuda independiente, QtAssistant.

Un ejemplo sencillo de cómo colocar widgets en la ventana de una aplicación

Tomemos el proyecto que creamos en la sección anterior usando el asistente del entorno de desarrollo QtCreator. El proyecto creado incluye los siguientes archivos.

  • aplicación1.pro- archivo de proyecto
  • principal.cpp widget.h y widget.cpp./li>
  • widget.h— interfaz de la clase de ventana principal de la aplicación.
  • widget.cpp— implementación de la clase de ventana principal de la aplicación.

Los nombres de los archivos en su proyecto pueden ser ligeramente diferentes. Esto podría deberse a que los especificó explícitamente en otro lugar al crear el proyecto o a que los valores predeterminados para su versión de QtCreator son diferentes.

Abramos el archivo de implementación de la ventana principal de la aplicación: widget.cpp. Cambiémoslo al estado presentado en el siguiente ejemplo. Recuerde que los números de línea se proporcionan únicamente para facilitar los comentarios.

01. #incluir 02. #incluir 03. #incluir 04. 05. #include "widget.h" 06. 07. Widget::Widget(QWidget *padre) 08. : QWidget(padre) 09. ( 10. setWindowTitle(tr("¡¡¡Hola mundo!!!")); 11. setMinimumSize(200, 80); 12. 13. QLabel * plb = new QLabel(tr("Test"), this); QLineEdit * ple = new QLineEdit(this); , 80, 24); 18. 19. QPushButton * ppb = nuevo QPushButton(tr("Ok"), this); ->setGeometry(20, 50, 80, 24. ) 22. 23. Widget:: ~Widget() 24. ( 25. 26. )

Las líneas 01-03 incluyen archivos con las interfaces de las siguientes clases de widgets.

  1. Etiqueta Q- clase de etiqueta. A menudo se utiliza para colocar información de texto estático. Entiende algunas etiquetas HTML para formatear. Se puede utilizar para colocar estáticamente una imagen. Por ejemplo, de un archivo con una imagen. Heredado de Marco Q, por lo que se puede adaptar a diferentes formas de bordes (bordes).
  2. QLine— una clase para crear campos de entrada de información de texto de una línea.
  3. QPulsador- clase de botón. Se utiliza con mayor frecuencia para el procesamiento de señales. hecho clic()- haga clic en el botón.

Cuerpo constructor de clase widget contiene dos líneas para configurar los atributos de la ventana (líneas 10-11) y 8 líneas para crear y colocar otros tres widgets en el campo de la ventana (líneas 13-20).

La configuración de los atributos de la ventana consiste en el comando para establecer el nombre de la ventana de la aplicación y el tamaño mínimo de la ventana de la aplicación. Para establecer el tamaño mínimo, se utiliza un método que toma el ancho y el alto de la ventana en píxeles.

La línea 13 contiene la creación de una instancia de la clase QLabel. Un objeto se crea dinámicamente utilizando el nuevo operador. Para crear un objeto se utiliza un constructor, cuyo primer parámetro especifica la cadena que debe representar el objeto creado. El segundo parámetro de este constructor debe especificar la dirección del objeto que se convertirá en el propietario del objeto etiqueta creado. La dirección del propietario está configurada como este. Según las reglas del lenguaje C++, este es un puntero al objeto dentro del cual se utiliza. Es decir, en este contexto, es un puntero a la instancia creada de la clase. widget. Por lo tanto, la línea 13 crea un objeto de clase de etiqueta que debe representar el texto especificado y cuyo propietario está asignado al objeto actual.

Ahora es el momento de hablar sobre las cadenas de propiedad, que se implementan en el sistema de clases Qt para resolver el problema de la destrucción de objetos y evitar pérdidas accidentales de memoria. Cabe recordar que los objetos creados dinámicamente, es decir. usando el operador nuevo, están ubicados en un área de memoria especial llamada montón y que viven en el montón hasta que el operador los destruye explícitamente borrar. Si el programador no monitorea la destrucción de objetos que se han vuelto innecesarios y no llama al operador para destruirlos borrar, entonces esto se convierte en la causa de una pérdida de memoria en la aplicación, lo cual es un problema grave para varios lenguajes de programación, incluido el lenguaje C++.

Existen varios esquemas bien conocidos para rastrear automáticamente la destrucción de objetos creados dinámicamente. Uno de ellos es el uso de punteros inteligentes, que se analizarán más adelante. Otra forma es crear cadenas de propiedad, de lo que hablaremos ahora. La tercera forma es crear un subsistema de recolección de basura, que debería rastrear objetos innecesarios y destruirlos. Este último método, tradicional para los núcleos de muchos lenguajes modernos, prácticamente no se utiliza en C++. Los dos primeros métodos son mucho más populares en las tradiciones del lenguaje C++.

Entonces, las cadenas de propiedad implementan la siguiente idea simple. Se crea un objeto cuya destrucción nos comprometemos a controlar. La forma más sencilla es crear dicho objeto mediante una definición estática y luego se destruirá automáticamente cuando la ejecución del programa llegue al final del bloque de instrucciones (...) en el que se definió. A continuación, cuando creación dinámica Para otros objetos, les asignaremos objetos propietarios. Las responsabilidades de los propietarios incluirán la destrucción de los objetos propios en el cuerpo de su propio destructor. Recuerde que un destructor es un método especial que se llama cuando se destruye un objeto. Por lo tanto, es posible construir una cadena de propiedad en relación con el primer objeto, cuyos elementos se destruirán automáticamente cuando se destruya el primer objeto. Al organizar un esquema de este tipo, sólo es necesario tener en cuenta correctamente la vida útil de un objeto designado como propietario de otro objeto, para que los objetos no se destruyan prematuramente.

Este es exactamente el esquema de propiedad implementado en el sistema de clases Qt. Al crear muchas clases a partir de esta biblioteca, puede utilizar un constructor cuyo único o último parámetro toma un puntero a un objeto asignado como propietario del objeto que se está creando. Este parámetro se describe como un parámetro con un valor predeterminado, que se define como cero. Por lo tanto, si no se especifica la dirección del propietario, el parámetro se establece en cero y el esquema de propiedad para dicho objeto se desactiva. En este caso, debes recordar destruir explícitamente dicho objeto.

Al implementar diagramas de circuitos de propiedad, algunas bibliotecas utilizan un parámetro llamado dueño, que se traduce del inglés como dueño. Sin embargo, en la biblioteca Qt este parámetro se llama padre, que se traduce del inglés como padre. Como resultado, algunos novatos tienen un malentendido debido al hecho de que el concepto de "padre" tradicionalmente se refiere a cadenas de herencia en programación orientada a objetos, pero las cadenas de herencia y las cadenas de propiedad no tienen nada en común. Tenga cuidado y no sea víctima de conceptos erróneos en este asunto.

Volvamos una vez más a la línea 13. Allí creamos un objeto cuyo propietario se asigna al objeto actual de la ventana principal de la aplicación. Variable plb, que almacena la dirección del objeto creado, se destruirá automáticamente cuando se llegue al final del código del constructor. Sin embargo, el objeto que se asignó en la memoria seguirá vivo y vivirá hasta que se destruya el objeto de la ventana principal de la aplicación. La destrucción del objeto de la ventana principal destruirá automáticamente todos los objetos que posee el objeto de la ventana.

En la línea 14, accedemos a un método para configurar atributos de geometría que determinan la ubicación del objeto personalizado en relación con su propietario. El primer y segundo valor indican las coordenadas horizontales y verticales de la esquina superior izquierda del objeto. El tercer y cuarto valor indican el ancho y el alto del objeto personalizado.

Si se ha asegurado de que el ejemplo creado en la sección anterior se haya ensamblado y ejecutado sin errores, entonces este ejemplo, que es una extensión del anterior, también debería ejecutarse.

Usando esta aplicación Puede experimentar con las opciones de inicio de la aplicación. La forma más sencilla de hacerlo es en la consola. Vaya al directorio de compilación de la aplicación y siga las siguientes opciones para ejecutar la aplicación con parámetros. En cada uno de los lanzamientos debería haber una diferencia notable en el estilo de dibujo de los widgets.

$ ./app1 -style=motivo $ ./app1 -style=windows $ ./app1 -style=platino

En este ejemplo, el archivo ejecutable de la aplicación se especifica con el nombre aplicación1. Es posible que en tu caso el archivo ejecutable tenga un nombre diferente. en el quirófano sistema windows, los archivos ejecutables tienen la extensión exe. Además, en el sistema operativo Windows, puede iniciar un archivo ejecutable desde el directorio actual sin especificar una ruta relativa, es decir. sin indicación ( ./ ): el carácter de punto es sinónimo del directorio actual y el carácter de barra diagonal es un carácter separador en la entrada de la ruta del archivo. Además, tenga en cuenta que el símbolo del dólar es el símbolo de solicitud estándar en la consola *nix para un usuario normal y no es necesario escribirlo como parte del comando. En la consola de Windows, el carácter de solicitud suele ser el carácter de corchete angular ( > ).

Las opciones de inicio también se pueden especificar al iniciar una aplicación desde el entorno de desarrollo QtCreator. Para hacer esto, haga clic en el icono en la barra de herramientas izquierda Proyectos. Se abrirá la pestaña de configuración correspondiente. En la parte superior de la ventana puedes ver un sistema jerárquico de pestañas. Las pestañas de nivel superior definen el proyecto, ya que se pueden abrir varios proyectos en el entorno de desarrollo. El siguiente nivel de pestañas debe contener, entre otras cosas, una pestaña Construir y ejecutar, que necesitamos. En esta pestaña, luego selecciona la versión Qt SDK, en caso de que estés creando un proyecto para varias versiones a la vez. Si está armando un proyecto para una versión, la selección constará de un elemento. Dentro del widget de selección de versión de Qt SDK, hay dos botones estilizados con bordes redondeados: botones Construir Y Correr. Clic en el botón Correr para seleccionar el grupo apropiado de configuraciones. Allí, en el grupo de parámetros del mismo nombre. Correr encontrará un campo de entrada de una línea frente a la etiqueta Argumentos. Este es el objetivo final de nuestra elección. Escribamos allí la siguiente línea.

Estilo = motivo

Iniciar la aplicacion. Luego prueba con otros valores: ventanas Y platino. Recuerde que el objeto de clase QAplicación admite una lista de un par de docenas de parámetros de lanzamiento, sobre los cuales se puede leer en la ayuda de los constructores de clases correspondientes.

A medida que avanza en el ejemplo, familiarícese con información de contexto según las clases utilizadas en el ejemplo. Intente agregar otras instancias de la etiqueta, el campo de entrada de una sola línea y las clases de botones al formulario. Además, intente leer la ayuda y agregar objetos de las siguientes clases al formulario.

  1. QComboBox— clase de lista desplegable.
  2. QCheckBox— clase de bandera (verificador).
  3. QText— clase de un campo de entrada multilínea. Se utiliza tanto para editar como para presentar texto. Contiene capacidades muy ricas para presentar documentos al separar las funciones de los compositores de documentos especiales en clases separadas.

Como conclusión de la lección, cabe señalar que el método utilizado de colocación "rígida" de objetos en el campo del formulario, mediante la indicación explícita de la geometría de colocación, no es tradicional y se recomienda en la biblioteca de clases Qt. Las siguientes lecciones analizarán los administradores de diseño, que son medios modernos y convenientes para colocar widgets en un formulario.

Programando con Qt

Parte 1. Introducción. Herramientas de desarrollo y modelo de objeto

Serie de contenido:

1. Introducción

Existen versiones de Qt para sistemas operativos tipo Unix con X Window System (por ejemplo, X.Org (EN), Mac OS X y Windows OS). Qt Software también adapta su producto a plataformas móviles: Embedded Linux (EN), S60 (EN) y Windows CE. Qt ofrece grandes oportunidades para el desarrollo multiplataforma de la mayoría diferentes programas, no necesariamente con una interfaz gráfica. En particular, el popular entorno de escritorio KDE se basa en él.

El conjunto de herramientas se divide en módulos, cada uno de los cuales está ubicado en una biblioteca separada. Las clases base están en QtCore, los componentes de la GUI están en QtGui, las clases de red están en QtNetwork, etc. Por lo tanto, es posible compilar programas incluso para plataformas donde no existe X11 u otro subsistema de gráficos compatible.

2. Instalación de Qt

Necesitaremos instalar el entorno de desarrollo Qt. Software distribuido bajo los términos de la licencia gratuita GPL 3.0 o LGPL 2.1. Se puede obtener en http://www.qtsoftware.com/downloads (EN).

2.1. Bibliotecas y herramientas básicas.

Los repositorios de distribuciones populares de GNU/Linux ya cuentan con paquetes listos para usar con el entorno de desarrollo Qt (por ejemplo, Debian, Fedora, Gentoo, Mandriva, Ubuntu). Sin embargo, el usuario puede crear e instalar el kit de herramientas desde el código fuente.

Para sistemas que usan X11, debe descargar el archivo qt-x11-opensource-src-4.x.y.tar.gz, donde 4.x.y es la última versión estable disponible. Instalaremos la versión 4.5.0.

En el directorio con el archivo qt-x11-opensource-src-4.5.0.tar.gz, ejecute los siguientes comandos:

tar xvfz qt-x11-opensource-src-4.5.0.tar.gz cd qt-x11-opensource-src-4.5.0

Antes de compilar Qt, ejecute el script de configuración. El conjunto completo de sus opciones se muestra usando el comando ./configure -help , pero generalmente se pueden usar las configuraciones estándar.

El parámetro -prefix especifica el directorio de instalación (el valor predeterminado es /usr/local/Trolltech/Qt-4.5.0). También hay claves para instalar varios componentes (archivos ejecutables, bibliotecas, documentación, etc.) en diferentes directorios.

Cuando se inicia, el script requiere que el usuario acepte los términos de la licencia GPL/LGPL. Después de la ejecución

./configurar

puede iniciar la compilación y la instalación usando los comandos:

hacer y hacer instalar

Tenga en cuenta que la compilación lleva mucho tiempo y la instalación de Qt puede requerir privilegios de root (los archivos se escriben en /usr/local/).

Si luego necesita reconfigurar y reconstruir Qt en el mismo directorio, elimine todos los rastros de la configuración anterior usando make confclean antes de ejecutar ./configure nuevamente.

La ruta a los ejecutables de Qt debe agregarse a la variable de entorno PATH. En los shells bash, ksh, zsh y sh, esto se puede hacer agregando las siguientes líneas al archivo ~/.profile:

PATH=/usr/local/Trolltech/Qt-4.5.0/bin:$PATH exportar PATH

En csh y tcsh necesitas agregar la siguiente línea a ~/.login:

setenv RUTA /usr/local/Trolltech/Qt-4.5.0/bin:$RUTA

Si está utilizando un shell diferente, consulte las secciones correspondientes de la documentación.

Además, debe agregar la línea /usr/local/Trolltech/Qt-4.5.0/lib a la variable LD_LIBRARY_PATH si el compilador no admite RPATH. Estamos usando GNU/Linux y GCC (EN), por lo que nos saltamos este paso.

Luego use la utilidad qtdemo para ejecutar aplicaciones de demostración y probar la funcionalidad de las herramientas instaladas.

2.2. SDK

Recientemente ha aparecido el entorno de desarrollo multiplataforma Qt Creator. El SDK completo, incluido el IDE (además de las bibliotecas y las herramientas principales de desarrollo), se puede encontrar en el sitio web de Qt Software. Descargue el binario qt-sdk-linux-x86-opensource-xxx.bin y ejecute el asistente de instalación:

chmod +x ./qt-sdk-linux-x86-opensource-2009.01.bin ./qt-sdk-linux-x86-opensource-2009.01.bin

Si no va a instalar el SDK en su directorio de inicio, ejecute el instalador con derechos de superusuario.


3. Herramientas para desarrolladores

Qt incluye herramientas de desarrollador con interfaz gráfica o de consola. Entre ellos:

  • Assistant es una herramienta gráfica para ver documentación de hipertexto para herramientas y bibliotecas Qt.
  • diseñador: una herramienta gráfica para crear y ensamblar interfaces de usuario basado en componentes Qt.
  • qmake es un generador Makefile multiplataforma.
  • moc es un compilador de metaobjetos (controlador de extensión Qt para C++).
  • uic es un compilador de interfaces de usuario a partir de archivos .ui creados en Qt Designer.
  • rcc: compilador de recursos a partir de archivos .qrc.
  • qtconfig es una herramienta gráfica para configurar las preferencias del usuario para aplicaciones Qt.
  • qtdemo: lanza ejemplos y programas de demostración.
  • qt3to4 es una herramienta para migrar proyectos de Qt 3 a Qt 4.
  • Lingüist es una herramienta para localizar aplicaciones.
  • pixeltool – lupa de pantalla.


3.1. qhacer

La utilidad qmake se utiliza para generar Makefiles automáticamente en varias plataformas.

En general, qmake está orientado a Qt. Si está interesado en sistemas de compilación multiplataforma para propósitos más amplios, puede recurrir a CMake, que también es compatible con Qt.

Los principiantes deberían seguir con qmake.

Puede encontrar documentación completa para esta utilidad en Qt Assistant. Qt también viene con páginas man, incluyendo qmake(1) (escriba línea de comando hombre qhacer). Aquí le proporcionaremos pautas básicas para ayudarle a crear el código del artículo, así como sus propios proyectos simples.

Como ejemplo, creemos un directorio myproject y agreguemos allí los archivos hello.h, hello.cpp y main.cpp. En hello.h describimos el prototipo de la función hello():

Listado 1.1. Declaraciones de funciones del programa “¡Hola, Mundo!”
// hola.h void hola();

La implementación de hello() se colocará en hello.cpp:

Listado 1.2. Implementación de las funciones del programa “¡Hola Mundo!”
// hola.cpp #incluir #incluye "hola.h" void hola() ( qDebug()<< "Hello, World!"; }

Aquí qDebug() se utiliza para generar información de depuración. Se puede eliminar declarando el símbolo QT_NO_DEBUG_OUTPUT durante la compilación. También hay una función qWarning(), que emite advertencias, y qFatal(), que finaliza la aplicación después de imprimir un mensaje de error crítico en STDERR (qCritical() hace lo mismo, pero sin finalizar).

En el archivo de encabezado contiene declaraciones que agregan una sintaxis de operador más conveniente para qDebug(), qWarning() y qCritical()<< . При этом между аргументами (как в случае qDebug() << a << b << c;) автоматически расставляются пробелы, поддерживается вывод многих типов C++ и Qt, а в конце автоматически добавляется перевод строки.

Código de la aplicación principal (aquí seguimos la convención de poner main() en el archivo main.cpp):

Listado 1.3. La función main() del programa "¡Hola, mundo!"
// main.cpp #include "hola.h" int main() ( hola(); devolver 0; )

Para crear un archivo de proyecto, ejecute

Después de esto, debería aparecer un archivo myproject.pro con aproximadamente el siguiente contenido:

################################### # Generado automáticamente por qmake ######### # ########################### PLANTILLA = aplicación OBJETIVO = DEPENDPATH +=. INCLUYERUTA += . # Entrada ENCABEZADOS += hola.h FUENTES += hola.cpp main.cpp

El operador = se usa para asignar valores a variables, += agrega una nueva opción a una variable, -= elimina la opción especificada.

PLANTILLA = aplicación significa que estamos creando una aplicación; para la biblioteca use TEMPLATE = lib.

OBJETIVO: el nombre del archivo de destino (especifique TARGET = foobar para obtener el archivo ejecutable de foobar).

DEPENDPATH: directorios para buscar al resolver dependencias.

INCLUDEPATH: directorios con archivos de encabezado.

Después del lanzamiento

Se creará un Makefile normal basado en myproject.pro en GNU/Linux:

####### Compile hola.o: hola.cpp hola.h $(CXX) -c $(CXXFLAGS) $ (INCPATH) -o hola.o hola.cpp main.o: main.cpp hola.h $(CXX) -c $(CXXFLAGS) $ (INCPATH) -o main.o main.cpp ####### Instalar instalar: FORCE desinstalar: FORCE FORCE:

Las opciones de qmake afectan el contenido del Makefile. Por ejemplo, qmake -Wall agregará -Wall a los indicadores del compilador, generando todas las advertencias.

Usando el comando make, obtendremos el archivo ejecutable myproject, que muestra la cadena "¡Hola, mundo!"

Este esquema puede parecer demasiado complicado, pero en proyectos reales qmake hace la mayor parte del trabajo de compilación (por ejemplo, ejecutar el compilador de metaobjetos).

3.2. Creador de Qt

Las herramientas descritas anteriormente son suficientes para el desarrollo de aplicaciones. Puede utilizar su editor de texto favorito, como GNU Emacs o Vim. Los IDE tradicionales como KDevelop también funcionan con Qt.

Sin embargo, no hace mucho Qt Software lanzó su IDE multiplataforma Qt Creator. Tiene todas las herramientas de desarrollo integradas, un editor con resaltado y finalización de código, un depurador (interfaz gráfica para gdb) y soporte para Perforce, SVN y Git.

Cuando se trabaja en Qt Creator, se utilizan varios modos, que corresponden a pestañas en el panel de la izquierda. Para cambiar rápidamente entre modos, puede utilizar las combinaciones de teclas Ctrl+1, Ctrl+2, etc. Principalmente modo de edición corresponde a Ctrl+2 .


Para navegar en el editor, use la combinación de teclas Ctrl+K. Después de hacer clic en él, debe especificar uno de los prefijos:

Tabla 1. Prefijos para navegación en Qt Creator

Después del prefijo, presione la barra espaciadora e ingrese la información apropiada. Por ejemplo, para ir a la línea 93 del archivo actual, debe escribir “l 93” (se puede hacer lo mismo usando Ctrl+L), para ir a la documentación sobre el tema qobject_cast – “? qobject_cast”, etc.

En la parte inferior de la ventana se muestra un campo con finalización automática.

Figura 5. Campo de navegación en Qt Creator

Tabla 2. Atajos de teclado para Qt Creator

Ctrl+[Ir al principio del bloque.
Ctrl+]Ir al final del bloque
Ctrl+USeleccionar bloque
Ctrl+Mayús+UDeseleccionar bloque
Ctrl+IAlinear bloque
Ctrl+< Colapsar bloque
Ctrl+>Expandir bloque
Ctrl+/Comentar el bloque
Ctrl+Mayús+Mover fila
Ctrl+Mayús+↓Mover línea hacia abajo
mayús+suprSDeliminar línea

El editor integrado implementa la adición de código "inteligente", llamada mediante la combinación de teclas Ctrl+<Пробел>. La base de símbolos se compila en función de los archivos de encabezado del proyecto de INCLUDEPATH.

Hay una sección separada para leer la documentación en el IDE. modo de ayuda. Para obtener ayuda contextual sobre una clase o método, simplemente mueva el cursor de texto al nombre y presione F1. La tecla F2 también es útil para pasar a la definición en los archivos de encabezado.

Para cambiar del modo Ayuda o Depuración al modo Edición básica, presione Esc. En el modo de edición, Esc mueve el foco de ventanas adicionales (como el resultado de la compilación o la ayuda contextual) al editor. Si presiona Esc nuevamente, las ventanas adicionales se cierran.

Al igual que qmake, Qt Creator utiliza archivos .pro, por lo que los proyectos antiguos hechos a mano se pueden importar fácilmente al IDE. También está disponible un asistente con el que puede crear una plantilla para un nuevo proyecto.

Qt Creator se encuentra actualmente en desarrollo activo, pero si desea un IDE Qt clásico que funcione en una variedad de plataformas, esta es la mejor opción.

4. Estilo Qt

En Qt se usa CamelCasing: Los nombres de las clases se parecen a MyClassName y los nombres de los métodos se parecen a myMethodName.

Además, todos los nombres de clases Qt comienzan con Q, por ejemplo QObject, QList o QFont.

La mayoría de las clases tienen archivos de encabezado correspondientes con el mismo nombre (sin la extensión .h), es decir necesitará usar:

#incluir #incluir #incluir

Por lo tanto, en el futuro no estipularemos por separado dónde se declara tal o cual clase.

Métodos para obtener y configurar. propiedades (adquiridor Y setter) se nombran de la siguiente manera: La propiedad fooBar se puede obtener usando el método fooBar() y configurar usando setFooBar() .

T fooBar() const; void setFooBar(T val);

Al desarrollar sus propias aplicaciones en Qt, debe seguir este estilo.

5. modelo de objetos

Para trabajar eficazmente con clases en tiempo de ejecución, Qt utiliza un modelo de objetos especial que extiende el modelo de C++. En particular, se añaden las siguientes características:

  • jerarquías de objetos en forma de árbol;
  • un análogo de Dynamic_cast para una biblioteca que no usa RTTI;
  • interacción de objetos a través señales Y tragamonedas;
  • propiedades de los objetos.

Muchos objetos se definen por el valor de varias propiedades, estados internos y conexiones con otros objetos. Son entidades individuales, y para ellos la operación de copiar literalmente, así como compartir datos en la memoria, no tiene sentido. En Qt, estos objetos heredan las propiedades de QObject.

En los casos en los que un objeto no necesita ser tratado como una entidad, sino como un valor (por ejemplo, cuando se almacena en un contenedor), se utilizan punteros. A veces, un puntero a un objeto que hereda de QObject se denomina simplemente objeto.

El conjunto de herramientas está diseñado para que para QObject y todos sus descendientes el constructor de copia y el operador de asignación no estén disponibles; se declaran en la sección privada a través de la macro Q_DISABLE_COPY():

clase FooBar: QObject público (privado: Q_DISABLE_COPY(FooBar));

Tenga cuidado y no utilice la estructura.

Foo barra = Foo(baz);

Otros objetos (como contenedores y cadenas) están completamente definidos por los datos que representan, por lo que sus clases correspondientes tienen un operador de asignación y un constructor de copia. Además, los objetos que representan los mismos datos pueden separarlos en la memoria de forma transparente para el programador.


5.1. Sistema de metaobjetos

Algunas de las extensiones se implementan utilizando métodos estándar de C++, pero Qt también usa extensiones sintácticas más complejas, por lo que utiliza la generación automática de código.

Para este propósito, C++ implementa un mecanismo de plantilla, pero no proporciona todas las capacidades necesarias de Qt, es poco compatible con el modelo de objetos dinámicos y no es totalmente compatible con todas las versiones de compiladores.

En situaciones difíciles, Qt utiliza su compilador de metaobjetos moc, que convierte código con extensiones a código C++ estándar. Para indicar que una clase usa capacidades de metaobjeto (y por lo tanto debe ser manejada por moc), se debe especificar la macro Q_OBJECT en la sección privada.

Si encuentras errores de compilación extraños que dicen que la clase no tiene un constructor definido, o no tiene una tabla de funciones virtuales (vtbl), lo más probable es que hayas olvidado el código generado por moc. Esto suele suceder si no se especifica la macro Q_OBJECT.

Para evitar errores, es mejor usar Q_OBJECT en todas las clases que heredan de QObject (directa o indirectamente).

El uso de un enfoque dinámico está asociado con ciertas pérdidas de rendimiento en comparación con uno estático, pero estos gastos generales pueden despreciarse si se tienen en cuenta los beneficios obtenidos.

Entre otras cosas, el código del metaobjeto agrega un método.

constante virtual QMetaObject* QObject::metaObject() constante;

que devuelve un puntero al metaobjeto del objeto.

Las señales, las ranuras y las propiedades se basan en el sistema de metaobjetos.

Al heredar de QObject, tenga en cuenta las restricciones impuestas por moc:

  1. Con herencia múltiple, el descendiente de QObject debe ser la primera y única clase heredada: clase MyClass: QObject público, Foo público, barra pública (//...);
  2. La herencia virtual no es compatible con QObject.

5.2. qobject_cast

Para emitir dinámicamente un QObject, use la función

T qobject_cast(QObject *objeto);

Funciona como la operación dinámica_cast estándar en C++, pero no requiere soporte RTTI.

Tengamos una clase MyClass1, heredada de QObject y MyClass2, heredada de MyClass1:

#incluir clase MiClase1: QObject público ( Q_OBJECT público: MiClase1(); // ... ); clase MiClase2: pública MiClase1 ( Q_OBJECT público: MiClase2(); // ... );

La conversión dinámica se ilustra con el siguiente código:

QObject *a = nuevo MiClase2; MiClase1 *b = qobject_cast (a); MiClase2 *c = qobject_cast (b);

Estas operaciones funcionarán correctamente en tiempo de ejecución.

Al igual que condynamic_cast, se puede comprobar el resultado de la conversión:

si (b = qobject_cast (a)) ( // ... )

El sistema de metaobjetos también le permite comprobar si a hereda la clase MyClass1:

if (a->hereda("MiClase1")) ( b = static_cast (a);

//... )

Sin embargo, se prefiere la opción anterior con qobject_cast.

5.3. Árboles de objetos Los objetos de clases que heredan de QObject se pueden organizar en una estructura de árbol. Cuando se elimina un objeto, Qt lo elimina objetos infantiles

, que a su vez eliminan sus objetos secundarios, etc. En otras palabras, al eliminar un objeto se elimina todo el subárbol del que es raíz.

Listado 2.1. Declaración MyClass para un programa que demuestra cómo se crean y eliminan objetos
// miclase.h #incluir clase MiClase: QObject público (público: MiClase (id de char, QObject *parent = 0); ~MyClass(); privado: id de char_;);
Listado 2.2. Definición de métodos MyClass para un programa que demuestra cómo se crean y eliminan objetos
// miclase.cpp #include #incluir #include "miclase.h" MiClase::MiClase (id de carácter, QObject *padre): QObject(padre), id_(id) ( qDebug()<< "+" >> identificación_; ) MiClase::~MiClase() ( qDebug()<< "-" >> identificación_; )

Aquí el objeto principal se establece en el constructor QObject:

QObject::QObject(QObject *padre = 0);

Se puede configurar más tarde usando el método setParent() y recuperar usando parent() :

void QObject::setParent(QObject *padre);
QObject* QObject::parent() const;

Si crea uno de cada uno de los objetos A y B en la pila, primero se creará A y luego B. De acuerdo con el estándar C++, la eliminación se produce en orden inverso: primero B, luego A:

Listado 2.3. Creando instancias MyClass en la pila
// main.cpp #include "miclase.h" int main() ( MiClase a ("A"); MiClase b ("B"); devuelve 0; )

Si crea B en el montón y lo asigna como hijo de A, B se eliminará automáticamente junto con A:

Listado 2.4. Crear una instancia A de la clase MyClass en la pila y una instancia B en el montón como hija de A
// main.cpp #include "miclase.h" int main() ( MiClase a ("A"); MiClase *b = nueva MiClase ("B", &a); devuelve 0; )

De manera similar para árboles más complejos:

Figura 8. Ejemplo de un árbol de objetos multinivel
Listado 2.5. Árbol multinivel de objetos enraizados en la pila.
// main.cpp #include "miclase.h" int main() ( MiClase a ("A"); MiClase *b = nueva MiClase ("B", &a); MiClase *c = nueva MiClase ("C", &a); MiClase *d = nueva MiClase("D", c);

Después de eliminar A, se eliminará todo el árbol.

Por lo tanto, el programador debe crear objetos en el montón y establecer las jerarquías apropiadas, y Qt se encarga de la gestión de la memoria.

Si un objeto y sus hijos se crean en la pila, este orden de eliminación puede provocar errores.

int main() ( MiClase b ("B"); MiClase a ("A"); b.setParent(&a); // ... devuelve 0; )

Aquí, al salir del alcance, el objeto A se eliminará primero, ya que fue el último creado. En este caso, Qt también eliminará su objeto hijo B. Pero luego intentará eliminar B, lo que generará un error.

De lo contrario, no habrá ningún problema, porque cuando se llama al destructor QObject, el objeto se elimina a sí mismo de la lista de objetos secundarios del objeto principal:

int main() ( MiClase a ("A"); MiClase b ("B", &a); // ... devuelve 0; )

En términos generales, los objetos secundarios deben asignarse en el montón.

Cada QObject tiene una propiedad objectName, a la que se accede mediante métodos

QString nombre de objeto() const; void setObjectName(const QString& nombre);

De forma predeterminada, objectName es una cadena vacía. A través de esta propiedad, a los objetos del árbol se les pueden asignar nombres para su posterior búsqueda.

Lista Q constante &niños() const;

– devuelve una lista de objetos secundarios.

T findChild(const QString& nombre = QString()) const;

– devuelve un objeto hijo llamado nombre, que se puede convertir al tipo T, o 0 si no se encuentra dicho objeto. Sin argumento nombre devuelve todos los objetos secundarios. La búsqueda se realiza de forma recursiva.

Lista Q QObject::findChildren(const QString& nombre = QString()) const;

– devuelve todos los objetos secundarios con el nombre nombre, que se puede convertir a tipo t, o una lista vacía si no se encuentran dichos objetos. Sin argumento nombre devuelve todos los objetos secundarios. La búsqueda se realiza de forma recursiva.

Lista Q QObject::findChildren (const QRegExp& regExp) const;

– similar, pero con búsqueda de expresiones regulares expresión regular.

vacío dumpObjectTree();

– muestra información de depuración sobre el árbol de objetos con la raíz dada.

5.4. Señales y ranuras

Al crear interfaces gráficas de usuario, la interacción de objetos a menudo se realiza mediante devoluciones de llamada, es decir, pasar código para su posterior ejecución (en forma de punteros de función, functores, etc.). También concepto popular eventos Y procesadores, en el que el controlador actúa como interceptor de eventos para un objeto específico.

Qt introduce el concepto de señales y ranuras.

Señal enviado cuando se llama a su método correspondiente. El programador sólo necesita especificar el prototipo del método en la sección de señales.

Ranura Es un método que se ejecuta cuando se recibe una señal. Las franjas horarias se pueden declarar en franjas públicas, franjas protegidas o franjas privadas. En este caso, el nivel de protección sólo afecta a la capacidad de llamar a los slots como métodos normales, pero no a la capacidad de conectar señales a los slots.

El modelo de señal y ranura se diferencia del modelo de evento y controlador en que una ranura se puede conectar a cualquier cantidad de señales y una señal se puede conectar a cualquier cantidad de ranuras. Al enviar una señal, se llamarán todas las ranuras conectadas a ella (el orden de las llamadas no está definido).

5.4.1. Anunciando señales y slots, enviando señales.

Como ejemplo típico de una ranura, considere el método para obtener la propiedad ( adquiridor). Método de configuración de propiedad ( setter) en este caso la señal corresponderá.

Listado 3.1. Clase MyClass con slot void setValue(int x) y señal void valueChanged(int x)
// miclase.h #incluir clase MyClass: QObject público ( Q_OBJECT público: MyClass(int x, QObject *parent = 0); int value() const; ranuras públicas: void setValue (int x); señales: void valueChanged (int x); privado: int x_ ;

Tenga en cuenta la macro Q_OBJECT, que indica a Qt que se está utilizando el sistema de metaobjetos.

Listado 3.2. Implementación de métodos de clase MyClass con ranura void setValue(int x) y señal void valueChanged(int x)
// miclase.cpp #include #include "miclase.h" MiClase::MiClase (int x, QObject *padre): QObject(padre) ( setValue (x); ) int MiClase::valor() const ( return x_; ) void MiClase::setValue ( int x) (if (x_ == x) retorno; x_ = x; emitir valorCambiado (x); )

La palabra clave emit es responsable de enviar una señal.

Solo se especifica un prototipo para una señal y la señal no puede devolver un valor (es decir, se especifica void). El compilador de metaobjetos es responsable de la implementación; también convierte la sintaxis extendida con las palabras clave señales, ranuras, emitir en código C++ estándar.

De hecho, las palabras clave pueden sustituirse por las macros Q_SIGNALS, Q_SLOTS y Q_EMIT. Esto es útil si utiliza bibliotecas de terceros que ya utilizan las palabras señales, ranuras o emitir.

El procesamiento de palabras clave está deshabilitado con el indicador no_keywords. En el archivo del proyecto qmake (.pro) agregue

CONFIG += no_palabras clave

Puede ver la salida del compilador de metaobjetos en el archivo moc_slots.cpp, que se genera a partir de slots.h y se compila junto con rest.cpp.

5.4.2. Conexión de una señal a una ranura

Listado 3.3. Conexión de la señal void MyClass::valueChanged (int x) a la ranura void MyClass::setValue (int x)
// principal.cpp #incluir #incluir #include "miclase.h" int main() ( MiClase a(1); MiClase b(2); QObject::connect (&a, SEÑAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue(3);<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 return 0; }

Aquí, usando QObject::connect, la señal del objeto a se conecta a la ranura del objeto b (se pasan punteros a QObject). Las macros SIGNAL y SLOT forman firmas de cadena para métodos. Sus argumentos deben contener prototipos sin especificar nombres de variables, es decir SIGNAL(valueChanged(int x)) no es una opción válida.

Las firmas se utilizan para la coincidencia de tipos: la firma de la señal debe coincidir con la firma de la ranura. En este caso, la firma de la ranura puede ser más corta si se ignoran argumentos adicionales.

Otra opción para llamar a QObject::connect:

b.connect(&a, SEÑAL(valueChanged(int)), RANURA(setValue(int)));

Entonces, aquí, llamar a MyClass::setValue en a activará MyClass::setValue en b .

Preste atención a la línea if (x_ == x) return; . Es necesario para evitar problemas con las conexiones cíclicas. Por ejemplo, el siguiente código funcionará:

Listado 3.4. Conexión cíclica de señales void MyClass::valueChanged (int x) con ranuras void MyClass::setValue (int x)
// principal.cpp #incluir #incluir #include "slots.h" int main() ( MiClase a(0); MiClase b(1); MiClase c(2); QObject::connect (&a, SEÑAL(valueChanged(int)), &b, SLOT(setValue (int))); QObject::connect (&b, SEÑAL(valueChanged(int)), &c, SLOT(setValue(int))); QObject::connect (&c, SEÑAL(valueChanged(int)), &a, SLOT (setValue(int)));<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 qDebug() << "c:" << c.value(); // 3 return 0; }

QObject::connect devuelve verdadero si la conexión se realizó correctamente y falso en caso contrario, por ejemplo, cuando no se encuentra la señal o la ranura o sus firmas son incompatibles.

Si agrega conexiones idénticas usando QObject::connect, la ranura se llamará varias veces.

5.4.3. Cerrar

Para desconectar la señal de una ranura, use QObject::disconnect:

QObject::desconectar (&a, SEÑAL(valueChanged(int)), &b, SLOT(setValue(int)));

Si el cierre se realiza correctamente, se devuelve verdadero.

Si en lugar de la señal (SEÑAL(...)) especifica 0, entonces este receptor de señal (b) y la ranura se desconectan de cualquier señal:

QObject::desconectar (&a, 0, &b, SLOT(setValue(int)));

Si especifica 0 en lugar del receptor de señal (b) y la ranura (SLOT(...)), entonces todo lo conectado a esta señal se desactivará:

QObject::desconectar (&a, SEÑAL(valorCambiado(int)), 0, 0);

Si especifica 0 en lugar de ranura (SLOT(...)), entonces todo lo conectado a este receptor de señal (b) se desactivará:

QObject::desconectar (&a, SEÑAL(valorCambiado(int)), &b, 0);

Obtenemos las siguientes opciones para llamar a QObject::disconnect:

// Desconecta todo de las señales enviadas por el objeto a: QObject::disconnect (&a, 0, 0, 0); // Lo mismo, pero en forma de método: a.disconnect(); // Desconecta todo de la señal SIGNAL(...) enviada por el objeto a: QObject::disconnect (&a, SIGNAL(...), 0, 0); // Lo mismo, pero en forma de método: a.disconnect (SIGNAL(...)); // Desconecta este receptor b: QObject::disconnect (&a, 0, &b, 0); // Lo mismo, pero en forma de método a: a.disconnect (&b);

Cuando elimina uno de los objetos de conexión, Qt elimina automáticamente la conexión misma.

5.4.4. Restricciones

El compilador de metaobjetos tiene una serie de limitaciones que también se aplican al trabajar con señales y ranuras.

  1. moc no maneja plantillas ni macros, por lo que las plantillas de clase no pueden definir señales ni ranuras, y las macros no se pueden utilizar al declarar señales y ranuras (incluso al especificar parámetros).

    Las macros no se pueden utilizar en ninguna área de código que deba ser manejada por moc. En particular, no puedes especificar una clase base a través de ellos.

    Sin embargo, se pueden utilizar algunas características del preprocesador. Están disponibles construcciones condicionales simples (con directivas #if, #ifdef, #ifndef, #else, #elif, #endif, así como el operador especial definido). Para crear declaraciones, moc tiene una opción de línea de comando -D. La utilidad qmake pasa a moc todas las declaraciones enumeradas en el parámetro del proyecto DEFINES.

    Por ejemplo, moc procesará correctamente

    #if 0 // Bloque ignorado #endif
    Bloquear #ifdef FOO // ... #endif

    también se procesará solo si se llama a moc -DFOO, o si hay una línea #define FOO antes.

  2. Los tipos deben especificarse en su totalidad porque QObject::connect() los compara literalmente. En particular, si la enumeración Bar se define dentro de la clase Foo, entonces se debe especificar Foo::Bar en los argumentos de la señal:

    clase Foo: QObject público ( Q_OBJECT enumeración Bar ( a, b, c ); señales: anular algo sucedió (Foo::Bar x); );
  3. Los punteros de función no se pueden utilizar como parámetros de señal y ranura. Por ejemplo,

    int (*divertido)(int)

    no es un argumento válido. Puedes usar typedef:

    typedef int (*diversión)(int);

    Generalmente es mejor utilizar herencia y funciones virtuales en lugar de punteros.

  4. Las clases anidadas no pueden contener señales ni espacios.
  5. Las señales y espacios que devuelven referencias se tratan como si devolvieran void.
  6. Las secciones de señales y slots sólo pueden declarar señales y slots.

5.5. Propiedades

Una clase que hereda QObject puede contener una declaración de propiedad usando la macro Q_PROPERTY():

Q_PROPIEDAD( escribe un nombre

LEER obtener función

Parámetros macro requeridos:

  • tipo- tipo de propiedad;
  • nombre- nombre de la propiedad;
  • obtener función– método constante para leer un valor; el tipo de devolución debe ser tipo, tipo* o tipo&.

Parámetros macro opcionales:

  • establecerFunción– un método para establecer el valor de una propiedad, debe devolver void y tomar solo un argumento de tipo tipo, tipo*, o tipo&;
  • restablecerFunción– un método para establecer el valor predeterminado de una propiedad que depende del contexto no debe tener argumentos y devolver void;

Los métodos pueden ser virtuales o heredados de la clase base. Para herencia múltiple, deben pertenecer a la primera clase de la lista.

Para atributos opcionales DESIGNABLE, SCRIPTABLE, STORED, USER, se permiten valores booleanos:

  • DISEÑABLE: si se debe mostrar la propiedad en Qt Designer y programas gráficos similares. El valor predeterminado es verdadero, pero también puede especificar un método booleano.
  • SCRIPTABLE: si la propiedad debe ser visible para el motor de script. El valor predeterminado es verdadero, pero también puede especificar un método booleano.
  • ALMACENADO: si la propiedad debe guardarse cuando se guarda el estado del objeto o si se calcula a través de otras propiedades. El valor predeterminado es verdadero.
  • USUARIO: si la propiedad es editable por el usuario. Normalmente, las clases correspondientes a controles tienen una de esas propiedades. El valor predeterminado es falso.

Por ejemplo, QWidget declara, entre otras, las siguientes propiedades:

Q_PROPERTY (QSize tamaño mínimo LEER tamaño mínimo ESCRIBIR setMinimumSize) Q_PROPERTY(int Ancho mínimo LEER Ancho mínimo ESCRIBIR setMinimumWidth ALMACENADO falso DESIGNABLE falso) Q_PROPERTY(int Altura mínima LEER Altura mínima ESCRIBIR setMinimumHeight ALMACENADO falso DISEÑABLE falso)

La propiedad de tamaño mínimo es de tipo QSize y se puede obtener usando QSize minimalSize() const y configurar usando void setMinimumSize (const QSize&). El ancho mínimo y la altura mínima se calculan mediante el tamaño mínimo, por lo que se establecen en STORED false.

Ejemplo de una propiedad con el atributo USUARIO – texto en QLineEdit:

Q_PROPERTY(QString texto LEER texto ESCRIBIR setText USUARIO verdadero)

Los siguientes métodos se utilizan para leer y escribir propiedades:

QVariant QObject::property (const char * nombre) const; bool QObject::setProperty (const char * nombre, const QVariant& valor);

propiedad() devuelve el valor de la propiedad opción equivocada QVariante si no existe dicha propiedad.

setProperty() devuelve verdadero si el objeto tiene la propiedad especificada con un tipo compatible con el valor pasado. De lo contrario, se devuelve falso.

Si la clase no tiene la propiedad especificada, entonces se agrega propiedad dinámica objeto. La lista de propiedades dinámicas se puede obtener usando

Lista Q QObject::dynamicPropertyNames() const;

Veamos un ejemplo del uso de propiedades. Deje que la clase MyClass tenga un texto de propiedad de cadena (de tipo QString):

Listado 4.1. Declaración de la clase MyClass con la propiedad text
// miclase.h #incluir #incluir clase MiClase: QObject público ( Q_OBJECT Q_PROPERTY(texto QString LEER texto ESCRIBIR setText) público: MiClase(texto QString, QObject *parent = 0); texto QString() const; void setText(const QString& texto); privado: QString text_; ) ;
Listado 4.2. Definiendo métodos de la clase MyClass con la propiedad text
// miclase.cpp #include #incluir #include "myclass.h" MyClass::MyClass(QString text, QObject *parent): QObject(parent) ( setText(texto); ) QString MyClass::text() const ( return text_; ) void MyClass::setText( const QString& texto) ( texto_ = texto; )

Trabajando con una propiedad:

Listado 4.3. Trabajar con la propiedad de texto del objeto MyClass
// principal.cpp #incluir #incluir #incluir #incluir #incluir #incluir #include "miclase.h" int main() ( MiClase str("foo"); qDebug()<< "text:" << str.text(); // Через метод: str.setText("bar"); qDebug() << "text:" << str.text(); // Через setProperty() / property(): str.setProperty("text", QVariant("baz")); QVariant prop = str.property("text"); qDebug() << "text:" << prop.toString(); // Добавление динамического свойства: str.setProperty("foo", QVariant("bob")); str.setProperty("bar", QVariant("slack")); QListd_props = str.dynamicPropertyNames(); QListIterador iter(d_props); // (Veremos contenedores e iteradores por separado) while (iter.hasNext()) ( const char* d_prop_name = iter.next().data(); QVariant d_prop = str.property(d_prop_name); qDebug()<< "" << d_prop_name << ":" << d_prop.toString(); } return 0; }

El programa debería mostrar lo siguiente:

texto: "foo" texto: "bar" texto: "baz" foo: "bob" bar: "slack"

Por supuesto, es más seguro y rápido llamar a métodos de una clase específica para leer y escribir propiedades. property() y setProperty() son necesarios cuando no se sabe nada sobre la clase excepto los nombres y tipos de las propiedades.

Si ni siquiera conoce la lista de propiedades y métodos de una clase, puede utilizar un metaobjeto.

5.6. Trabajar con metaobjetos

El metaobjeto es devuelto por el método.

QObjeto::metaObjeto()

Se puede utilizar para recuperar dinámicamente información sobre una clase, como en la API Java Reflecion.

5.6.1. información básica

El nombre de la clase regresa

const char * QMetaObject::className() const;

Puntero al metaobjeto de clase base –

const QMetaObject* superClase() const;

5.6.2. Métodos

Sólo esos métodos y constructores están disponibles a través del sistema de metaobjetos si se especifica la macro Q_INVOKABLE antes de sus declaraciones:

clase MyClass: QObject público ( Q_OBJECT public: Q_INVOKABLE MyClass(); // visible para el sistema de metaobjetos Q_INVOKABLE void foo(); // visible void foo(); // no visible);

Para acceder a los métodos (incluidas señales y ranuras), utilice

int QMetaObject::methodCount() const; int QMetaObject::methodOffset() const; QMetaMethod QMetaObject::método (índice int) const;

Los métodos y propiedades de clase están indexados. Se accede a un método mediante índice a través de QMetaObject::method() .

QMetaObject::methodCount() devuelve el número total de métodos, incluidos los heredados. Inclinación Los métodos de clase son devueltos por QMetaObject::methodOffset() , muestra en qué índice comienzan los métodos de esta clase. El desplazamiento aumenta con la herencia y muestra la cantidad de métodos de clase base.

Ejemplo de recorrido por un método:

const QMetaObject* m_obj = obj.metaObject(); para (int i = m_obj->methodOffset(); i< m_obj->métodoCount(); i++) ( qDepurar()<< m_obj->método(i).firma(); )

Si comenzamos en el índice 0, obtendríamos métodos de todas las clases base, incluido QObject:

destruido(QObject*) destruido() eliminarLater() _q_reregisterTimers(void*) ...

Qt utiliza internamente los métodos que comienzan con _q_ y no forman parte de la API.

Los constructores se especifican por separado:

QMetaMethod QMetaObject::constructor (índice int) const; int QMetaObject::constructorCount() const;

Por ejemplo, obtenemos una lista de constructores de QObject:

Listado 5. Inferir constructores de QObject a través del sistema de metaobjetos
#incluir #incluir #incluir #incluir int main() ( QObject obj; const QMetaObject* m_obj = obj.metaObject(); para (int i = 0; i< m_obj->constructorCount(); i++) ( qDepurar()<< m_obj->constructor(i).firma(); ) devuelve 0; )

Resultado:

QObjeto(QObjeto*) QObjeto()

El índice de un método, señal, slot o constructor se puede obtener mediante su firma:

int QMetaObject::indexOfConstructor (const char * constructor) const; int QMetaObject::indexOfMethod (const char * método) const; int QMetaObject::indexOfSignal (const char * señal) const; int QMetaObject::indexOfSlot (const char * ranura) const;

Para los constructores, se esperan métodos o señales. firmas normalizadas. Se pueden obtener mediante un método estático.

QByteArray estático QMetaObject::normalizedSignature(const char * método);

Por ejemplo,

QMetaObject::normalizedSignature("int * foo(const QString &, QObject *)")

devuelve "int*foo(QString,QObject*)".

Funciona de la misma manera

QByteArray QMetaObject estático::normalizedType (const char * tipo);

Se trata de una conversión textual a forma canónica, que se utiliza en particular para comprobar la compatibilidad de señales y ranuras.

5.6.3. Propiedades

Puedes trabajar con propiedades de la misma manera.

int QMetaObject::propertyCount() const; int QMetaObject::propertyOffset() const; QMetaProperty QMetaObject::property (índice int) const;const QMetaObject* m_obj = obj.metaObject(); para (int i = m_obj->propertyOffset(); i< m_obj->>propiedadCount(); i++) ( qDepurar()<< m_obj->propiedad(i).nombre(); )

(Si observa todas las propiedades, incluidas las heredadas, verá al menos objectName de QObject).

El índice de una propiedad se puede obtener por su nombre:

int QMetaObject::indexOfProperty (const char * nombre) const;

5.6.4. Transferencias

Las enumeraciones se registran en una clase usando la macro Q_ENUMS().

Una enumeración cuyos valores se pueden combinar usando un OR bit a bit se llama bandera y debe registrarse usando Q_FLAGS() .

QMetaEnum QMetaObject: :enumerador (índice int) const; int QMetaObject::enumeratorCount() const; int QMetaObject::enumeratorOffset() const;
Listado 6.1. Clase MyClass con enumeración de tipo y bandera de modo
class MyClass ( Q_OBJECT Q_ENUMS(Tipo) Q_FLAGS(Modo) public: enum Tipo ( A, B, C ); enum Modo ( Lectura = 0x1, Escritura = 0x2, Ejecutar = 0x4 ); // ... );

Las banderas se utilizan de la siguiente manera:

modo int = MiClase::Leer | MiClase::Escribir; // ... if (mode & MyClass::Write) // ¿Está configurado el indicador de escritura? (//...)

Trabajo dinámico con enumeraciones:

Listado 6.2. Generación de enumeraciones y indicadores de MyClass a través del sistema de metaobjetos
MiClassobj; const QMetaObject* m_obj = obj.metaObject(); para (int i = m_obj->enumeratorOffset(); i< m_obj->enumeradorCount(); i++) ( QMetaEnum me = m_obj->enumerator(i); if (me.isValid()) // Hay un nombre ( if (me.isFlag()) // Bandera ( qDebug()<< "" << me.scope() << "::" << me.name(); } else { qDebug() << me.scope() << "::" << me.name(); } } }

Resultado:

MiClase::Tipo MiClase::Modo

El índice de una enumeración se puede obtener por su nombre:

int QMetaObject::indexOfEnumerator (const char * nombre) const;

5.6.5. INFORMACIÓN DE CLASE

Usando la macro Q_CLASSINFO(), puede agregar pares de nombre-valor a un metaobjeto. Por ejemplo,

Listado 7.1. Clase MiClase con CLASSINFO
clase MiClase ( Q_OBJECT Q_CLASSINFO("autor", "Bob Dobbs") Q_CLASSINFO("versión", "0.23") // ... );

Estos pares se heredan y se pueden obtener del metaobjeto usando el mismo esquema:

QMetaClassInfo QMetaObject:: classInfo(int index) const; int QMetaObject::classInfoCount() const; int QMetaObject::classInfoOffset() const;

Para el ejemplo anterior:

Listado 7.2. Salida de CLASSINFO de la clase MyClass
MiClassobj; const QMetaObject* m_obj = obj.metaObject(); para (int i = m_obj->classInfoOffset(); i< m_obj->claseInfoCount(); i++) ( QMetaClassInfo mci = m_obj->classInfo(i); qDebug()<< mci.name() << ":" << mci.value(); }

Resultado:

El índice CLASSINFO se puede recuperar por su nombre:

int QMetaObject::indexOfClassInfo (const char * nombre) const;

5.6.6. Llamar a constructores y métodos

Los argumentos se pasan a través de los objetos QGenericArgument y QGenericReturnArgument. Son creados por las macros Q_ARG y Q_RETURN_ARG.

// referencia constante para pasar un valor: Q_ARG (T, valor T& constante) // referencia para devolver un valor: Q_RETURN_ARG (valor T, T&)

Ejemplo de uso:

Q_ARG(QString, "foo") Q_ARG(int, 23) Q_RETURN_ARG(QString, cadena)

Para crear una nueva instancia de una clase, utilice el método newInstance() del metaobjeto, al que se le pueden pasar hasta 10 argumentos.

QObject* QMetaObject::newInstance (QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const;

En caso de error, se devuelve 0.

Para llamar a un método, use invokeMethod() :

bool estático QMetaObject::invokeMethod (QObject* obj, const char * miembro, Qt::ConnectionType tipo, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = ricoArgumento());
  • objeto– puntero a un objeto;
  • miembro– nombre del método;
  • tipo- tipo de llamada:
    • Qt::DirectConnection – inmediatamente,
    • Qt::QueuedConnection – cuando QCoreApplication::exec() comienza a ejecutarse,
    • Qt::AutoConnection - sincrónico si el objeto está en el mismo hilo, asincrónico en caso contrario;
  • retirado– valor de retorno;

Cuando se llama de forma asincrónica, el valor no se puede calcular.

Hay sobrecargas de invokeMethod(). Si no especifica un tipo de llamada, se utilizará Qt::AutoConnection. Si no especifica un valor de retorno, se ignorará.

La clase QMetaMethod proporciona las mismas capacidades:

bool QMetaMethod::invoke (objeto QObject*, Qt::ConnectionType tipo de conexión, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument ( ), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const;

Asimismo, no se podrá especificar el tipo de conexión y/o valor de retorno.

Una llamada asincrónica se utiliza cuando un cálculo tarda demasiado en completarse, por lo que su resultado no es el esperado en el momento de la llamada. Estos cálculos generalmente se colocan en un subproceso separado, por lo que, de forma predeterminada (Qt::AutoConnection), los métodos de objetos de subprocesos externos se llaman de forma asincrónica.

Considere la siguiente clase:

Listado 8.1. Clase MyClass con constructor y métodos disponibles para el sistema de metaobjetos
clase MyClass: QObject público ( Q_OBJECT público: Q_INVOKABLE MyClass (QString texto, QObject *parent = 0); Q_INVOKABLE QString text() const; Q_INVOKABLE void setText (const QString& texto); privado: QString text_; );

Llamando al constructor y a los métodos:

Listado 8.2. Llamar a constructores y métodos de la clase MyClass a través del sistema de metaobjetos
MiClase foo("foo"); const QMetaObject* m_foo = foo.metaObject(); // Crea una nueva instancia: MyClass *bar = qobject_cast (m_foo->newInstance(Q_ARG(QString,"bar"))); si (!barra) ( qCritical()<< "Can"t invoke constructor!"; } else { bar->setParent(&foo); qDepurar()<< bar->texto(); // "bar" ) // Llama al método: if (!QMetaObject::invokeMethod (&foo, "setText", Q_ARG(QString,"baz"))) qCritical()<< "Can"t invoke method!"; QString val; // Вызвать метод и получить возвращенное значение: if (!QMetaObject::invokeMethod (&foo, "text", Q_RETURN_ARG(QString, val))) qCritical() << "Can"t invoke method!"; qDebug() << val; // "baz"

text() y setText() se llaman de esta manera sólo como un ejemplo simple de cómo funciona QMetaObject::invokeMethod(). Como ya sabes, estos dos métodos deben estar asociados a una propiedad.

También tenga en cuenta que text() devuelve un QString , pero no un QString& constante. De lo contrario, el sistema de metaobjetos pensaría que text() devuelve void.

Conclusión

Para trabajar eficazmente con clases en tiempo de ejecución, Qt utiliza un modelo de objetos especial, en el que, utilizando la herencia de QObject y la generación de código por parte del compilador de metaobjetos, se implementa lo siguiente:

  • jerarquía de objetos;
  • un análogo especial dedynamic_cast, independiente de RTTI;
  • sistema de señales y tragamonedas;
  • sistema de propiedades de objetos;
  • Trabajo dinámico con clases.

En el próximo artículo veremos tipos, variantes, referencias y partición de datos.

Después de un día de post-vida, comencé a notar una fuga de karma, por lo que pido disculpas de antemano por el estilo de presentación posiblemente inaceptable en el artículo y la subjetividad.

¡Hola Habrahabr!

Últimamente no pude evitar prestar atención a la popularidad del tema Qt en Hubrik, pero sin embargo, en los comentarios sigo viendo personas que dicen cosas completamente falsas e incomprensibles. Con este post quería disipar algunas ideas erróneas sobre Qt y contaros por qué. deberías cambiar de tu Java/Obj-C/.NET a Qt suave y esponjoso.

Debajo del corte habrá muchas impresiones, subjetividades y mis humildes opiniones al respecto. el mas maravilloso Marco para el desarrollo de aplicaciones. Sin embargo, intentaré agregar algunas cosas interesantes para que mi artículo adquiera al menos algún significado técnicamente útil. Espero que resulte una lectura entretenida y la disfrutes.

¿Aquí vamos?

Vesch No. 1. API C++

No es ningún secreto que Qt tiene una API muy conveniente y, más específicamente, el módulo qtbase contiene una cantidad suficiente de clases para la mayoría de las tareas cotidianas ( Qt es más que un marco GUI jajaja). Ya hablé sobre envoltorios para contenedores STL en mi artículo de hace tres años: tytsk. También se incluyen clases para trabajar con cadenas, depurar resultados y mucho, mucho más.

QString frutas = "manzana, plátano, naranja, plátano"; QStringList frutasList = frutas.split(", "); qDepurar()<< fruitsList; // выведет в консоль [ "apple", "banana", "orange", "banana" ] fruitsList.removeDuplicates(); fruits = fruitsList.join(", "); qDebug() << fruits; // выведет в консоль "apple, banana, orange"
Vale la pena decir que Qt también tiene módulos para trabajar cómodamente con XML y bases de datos ( con integración del delicioso-delicioso sistema MVC kutesh), OpenGL, trabajo de audio/vídeo (Phonon), programación en red, WebKit2. Para las tareas a las que se enfrenta un proyecto medio, esta cocina es suficiente en el 90% de los casos y rara vez surgen problemas con los módulos.

Dado mi amor por C++, estoy muy, muy satisfecho con el soporte que Qt brinda para varias cosas no triviales a nivel multiplataforma. Un par de veces tuve que resolver momentos particularmente incomprensibles, pero así es.

Vesch No. 2. Qt rápido

Qt Quick es un enfoque súper ingenioso para crear una interfaz gráfica de usuario. Usando un lenguaje declarativo llamado QML (adivina dónde se inventó jajaja), similar a JavaScript, puedes lograr una alta productividad al crear prototipos de la interfaz en aplicaciones. cualquier dificultades. Y lo curioso es que con las cosas así, Incluso un diseñador que conozca la sintaxis de JavaScript puede manejar la creación de prototipos de interfaz.. Todas estas serían palabras vacías si no les hubiera mostrado un ejemplo de código funcional (puede encontrar más en el Proyecto Qt, allí).

Importar QtQuick 2.0 Rectángulo ( id: ancho de página: 320; alto: 480 color: "gris claro" Texto ( id: helloText texto: "¡Hola mundo!" y: 30 anclajes.horizontalCenter: page.horizontalCenter font.pointSize: 24; font. negrita: verdadero) Cuadrícula (id: colorPicker x: 4; anclajes.bottom: página.bottom; anclajes.bottomMargin: 4 filas: 2; columnas: 3; espaciado: 3 Celda (cellColor: "rojo"; onClicked: helloText.color = cellColor ) Celda ( cellColor: "verde"; onClicked: helloText.color = cellColor ) Celda ( cellColor: "azul"; onClicked: helloText.color = cellColor ) Celda ( cellColor: "amarillo"; onClicked: helloText.color = cellColor ) Celda ( cellColor: "steelblue"; onClicked: helloText.color = cellColor ) Celda ( cellColor: "negro"; onClicked: helloText.color = cellColor ) )

La implementación del objeto Cell es extremadamente trivial y se define de esta manera.

importar elemento QtQuick 2.0 (id: propiedad del contenedor alias cellColor: rectángulo.color señal en la que se hizo clic (color cellColor) ancho: 40; altura: 25 Rectángulo (id: borde del rectángulo.color: anclajes "blancos".relleno: padre) MouseArea (anclas. llenar: padre onClicked: contenedor.clicked(container.cellColor) ) )

No hay una sola línea de C++ en este código y funciona bien. Está bueno, ¿no? Incluso me hizo sentir como un mago: es más fácil ir a la tienda a comprar pan que crear una aplicación como esta. Sin embargo, en aplicaciones complejas, QML por sí solo no es suficiente y lo combinamos con C++. Esto se discutió en muchos artículos del centro Qt Software, por ejemplo, allí.

Vesch No. 3. Comunidad

Bueno, hemos llegado a un momento agradable. Si hablamos de mí, llevo relativamente poco tiempo trabajando con Qt: sólo 5 años. Qt organiza eventos anuales: Qt Developer Days y Qt Contributors "Summit. Estuve en cada uno de ellos una vez, el año pasado, y me gustó mucho: el nivel de preparación es alto y las impresiones se transmiten. También tuve que comunicarme con Qt "veteranos": personas que han asistido a la cumbre durante 10 años. Me imagino lo genial que es ver el crecimiento de un proyecto de este tipo ante tus propios ojos y estar en el epicentro de todo el desarrollo, simplemente delicioso.

Estas personas son muy tolerantes y tratan bien a los recién llegados; fue muy fácil y conveniente para mí establecer contactos con personas tan maravillosas. El Proyecto Qt tiene foros donde cualquiera puede obtener una respuesta a su pregunta. Es gracioso, pero realmente apesta. muy vivo y ahí en realidad Responder a las preguntas que surgen en el proceso de aprendizaje de Qt.

Vesch No. 4. Revisión de código y fuente abierta

Soretz Cut está desarrollado abiertamente principalmente por Digia (comm. support +), KDAB, ICS y desarrolladores entusiastas. Todo está alojado en Gitorious - Tadamts. Para contribuir al desarrollo del proyecto es necesario pasar por estricto verificación de código: automatizada (cumplimiento del estilo de código sobre el que ya escribí anteriormente: ptsss) y humana: su código será revisado por hombres barbudos que no confían en usted y buscarán puertas traseras en su código. Todo esto es un proceso bastante complicado (problemas con Git/revisiones en el Comité de Revisión) y probablemente escribiré un artículo sobre ello uno de estos días.

Por cierto, tengo un par de confirmaciones en el árbol qtbase, así que puedes preguntarme por mensaje privado e intentaré responder tus preguntas.

Vesch No. 5. Dinámica de desarrollo de proyectos

Qt lleva muchos años en desarrollo, desde finales de los 90. Durante este tiempo, empresas como Trolltech y Nokia ya han jugado bastante con su versión comercial, y ahora Digia lo está haciendo. Pero una cosa es segura: el proyecto está vivo y prosperando. Durante unos años más, todo el mundo escribía diseño en widgets (clases de C++, todas ellas basadas en QWidget), pero hoy en día hasta un niño pequeño puede hacerlo. No creo que valga la pena decir que, en paralelo, se está desarrollando activamente lo más poderoso: Qt Creator, que hoy no solo agrada a los programadores de Qt.

^ genial Qt Creator, en el que puedes crear milagros y no obtendrás nada a cambio.

Desafortunadamente, no tengo números estrictos, pero dicen que el código se optimiza todos los días y la base del código se expande cuidadosamente: se agregan nuevas funciones y se corrigen errores antiguos (ya lo he visto muchas veces). Todo esto es muy adecuado y no puedo dejar de alegrarme.

Además, las plataformas ahora se están desarrollando activamente. iOS, Android, Windows Phone, ¡ahora puedes compilar programas para ellos!

Línea de fondo

Creo que entiendes que Qt es realmente genial y después de leer el artículo te enamoraste tanto como yo.
¡Gracias por su atención!