Vladimir del Corte Informe sobre librerías Qt INFORME SOBRE LIBRERIAS Qt GVA-ELAI-UPM 1 Vladimir del Corte Informe sobre librerías Qt INDICE GENERAL 1. INTRODUCCIÓN...............................................................................................pag02 2.COMO CREAR VENTANAS DE DIALOGO CON LIBRERIAS Qt ...........pag03 1.1 DISEÑO DE VENTANAS DE DIALOGO CON Qt DESIGNER ................pag03 1.2 SLOTS Y SIGNALS .......................................................................................... pag08 1.3 LAS HERRAMIENTAS UIC , MOC Y TMAKE ......................................... pag10 1.4CÓDIGO FUENTE DE LA APLICACIÓN..................................................... pag15 3. CÓMO INTEGRAR Qt DESIGNER EN VISUAL STUDIO.............................pag20 4. CÓMO CREAR UNA VENTANA PRINCIPAL (MAIN WINDOW). .........pag22 4.1 COMO AÑADIR EL MENU A LA VENTANA PRINCIPAL................................ pag22 4.2 CÓMO AÑADIR EL WIDGET CENTRAL A LA VENTANA PRINCIPAL....... pag23 4.3 CÓMO LLAMAR A LA VENTANA DE DIALOGO............................................... pag23 5.DIAGRAMA DE CLASES.................................................................................pag36 6.INSTALACIÓN DE Qt.......................................................................................pag36 6.1 INSTALACIÓN Qt/WINDOWS. ..............................................................................pag36 6.1 INSTALACIÓN Qt/X11..............................................................................................pag37 7.BIBLIOGRAFÍA................................................................................................pag40 GVA-ELAI-UPM 2 Vladimir del Corte Informe sobre librerías Qt 1. INTRODUCCIÓN . Qt es un producto de la empresa noruega de software Trolltech AS, esta empresa se dedica a desarrollar librerías y herramientas de desarrollo de software, además es experta en servicios de consultoría. Qt son un conjunto de librerías multi-plataforma para el desarrollo del esqueleto de aplicaciones GUI, escritas en código C++. Qt además esta completamente orientado a objetos. Qt comenzó a distribuirse comercialmente en 1996 y desde entonces ha sido la base para numerosas aplicaciones incluyendo la popular interfaz gráfica para Linux llamada KDE, disponible en todas las grandes distribuciones de Linux . Las librerías Qt están diseñadas para funcionar en distintas plataformas como son: • • • MS\Windows - 95, 98, NT, and 2000 Unix/X11 - Linux, Sun Solaris, HP-UX, Digital Unix, IBM AIX, SGI IRIX and a wide range of others Embedded – Plataformas Linux con soporte framebuffer En el mercado se pueden encontrar las siguientes distribuciones de Qt: • • • • Qt Enterprise Edition y Qt Profesional Edition, disponibles para el desarrollo de software con fines comerciales. Incluye servicio de soporte técnico y están disponibles ampliaciones. Qt Free Edition Es la versión para Unix/X11 para el desarrollo de software gratuito y de código abierto. Se puede obtener gratis sujeto a los términos de la Q Public License and the GNU General Public License. Para plataformas windows también esta disponible la versión Qt non comercial. Qt Educational Edition es una versión de la Qt Profesional Edition con licencia únicamente para fines educacionales. Qt/Embedded Free Edition. Para la realización de este informe se ha utilizado la distribución Qt Educational Edition en su versión 2.3, esta incluye una serie de útiles herramientas como pueden ser: - uic: User interface compiler. - moc : Meta object compiler. - tmake : Una herramienta que permite crear makefiles de una forma rápida y sencilla en cualquier plataforma. - Qt Designer : Una aplicación que permite el diseño de ventanas de diálogo de forma grafica. - Qcanvas : Él módulo canvas permite una alta optimización para gráficos 2D. - Él módulo QtOpenGL permite la integración de Open GL con Qt. - Etc. Recientemente acaba de salir al mercado la versión QT 3.0 incluyendo nuevas clases además de nuevas y mejoradas herramientas. Para cualquier información acerca de Qt se puede consultar la pagina web www.trolltech.com GVA-ELAI-UPM 3 Vladimir del Corte Informe sobre librerías Qt 2. APLICACIONES SIMPLES (VENTANAS DE DIÁLOGO) CON Qt. 2.1 Diseño con Qt Designer Existe una herramienta muy potente que permite diseñar de una forma muy sencilla y rápida ventanas de dialogo con las librerías Qt . Esta herramienta es Qt Designer una aplicación mediante la cual se puede realizar el diseño de aplicaciones GUI de forma gráfica y muy intuitiva. El proceso a seguir para desarrollar una aplicación GUI con este software es el siguiente. Tras lanzar la aplicación Qt Designer el primer paso es abrir un nuevo proyecto, para ello basta hacer click en el menú file en la opción new . Seguidamente aparecerá en pantalla una ventana de dialogo donde se podrá elegir entre las plantillas existentes para comenzar el diseño. Dependiendo de la versión de Qt Designer con la que se esta trabajando se contará con diferentes plantillas , las más comunes son: - - - Dialog: Es la plantilla más simple , crea una ventana que incluye únicamente los botones de minimizar , maximizar y cerrar. De esta derivan el resto de plantillas. Wizard: Permite realizar aplicaciones paginadas , además de esto incluye los botones de cancelar , anterior(back) , siguiente(next) o finalizar (finish). Dialog with buttons : Igualmente deriva de la primera e incluye lo botones de ayuda ,apply ,ok y cancel. Todas estas plantillas tienen algo en común y es que todas ellas derivan de la misma clase, de la clase QDialog y esta a su vez deriva de la clase QWidget y esta de QObject. Se pueden ver estas relaciones de una forma más clara en el diagrama de clases (realizado en lenguaje UML) del capitulo 5. OBJETIVO DE LA APLICACIÓN. Para este ejemplo se trata de desarrollar una aplicación utilizando la plantilla wizard (clase QWizard que deriva de la clase QDialog y esta a su vez de QWidget) que constará de dos páginas, en la primera página de la ventana de diálogo se pedirá que se introduzcan una serie de datos personales como pueden ser nombre, apellidos, ciudad etc., al pasar a la segunda pagina aparecerán en esta impresos los datos introducidos en la pagina 1 de forma ordenada. Siguiendo con el ejemplo,tras seleccionar la plantilla wizard aparecerá en pantalla la ventana de dialogo de la aplicación y además otra con el título Property Editor(editor de propiedades), esta ventana sirve para modificar las propiedades de la ventana de dialogo y de los objetos que se irán añadiendo a esta. Estas propiedades pueden ser el título, color del fondo, tamaño, etc. La ventana tendrá como nombre por defecto Form1(en este ejemplo pasará a llamarse Wizard), si se desea cambiar esto en la ventana del editor de propiedades habrá que modificar los apartados de name, y si se GVA-ELAI-UPM 4 Vladimir del Corte Informe sobre librerías Qt desea cambiar el título de la ventana se hará en el apartado caption, y si lo que se quiere es cambiar el título de la página en el apartado pagetitle. Como se dijo anteriormente la plantilla Wizard (clase Qwizard) permite realizar ventanas de diálogo paginadas, para añadir una nueva página a la aplicación basta situar el puntero del ratón sobre la ventana y pulsar el botón derecho de este, aparecerá un menú en el que se podrá ver que la primera opción es Add page, tras realizar esto la ventana pasa a tener dos páginas, pudiendo pasar de una a otra mediante los botones anteriormente comentados (back y next). El siguiente paso es añadir los objetos que se necesitan para completar la aplicación, estos objetos pueden ser: - Botones: Se pueden añadir diversas clases de botones. Los que se pueden encontrar son pushbutton, toolbutton, radiobutton o check button, estos difieren en su forma pero básicamente pueden realizar las mismas funciones. Ejemplos de Botones - Containers : Se pueden definir como contenedores que se pueden etiquetar con un titulo y pueden contener texto , imágenes incluso otros objetos tales como botones. Igualmente existen varios tipos Groupbox, Buttongroup,Frame, etc. Ejemplos de containers - Inputs : Este apartado incluye LineEdit (líneas de edición) pueden servir para recoger datos que se introducen por teclado , MultiLineEdit (similar) , combobox (listas desplegables) y otros como slider,spinbox o dial . Ejemplos de Inputs GVA-ELAI-UPM 5 Vladimir del Corte Informe sobre librerías Qt -Displays: En este apartado se incluyen textlabel (etiquetas de texto), pixmapLabel (imágenes) , progressbar (barras de progreso) además de LCDnumber, Line etc. Ejemplos de Displays -Views: Son objetos que pueden albergar iconos, también en este apartado se incluyen las tablas. Ejemplo de views Todos estos se encuentran en el menú Tools aunque también se puede encontrarlos en las barras de herramientas con sus respectivos iconos. Siguiendo con el ejemplo, se puede completar de la siguiente manera, en la primera página se situan los siguientes objetos : un comboBox (o lista desplegable ) y tres lineEdit , estos objetos irán acompañados con cuatro textLabel(etiquetas de texto) para identificar cada uno de ellos. Además se colocara un pushbutton (después especificaremos su función).Y pondremos como titulo de página “Datos”. Se acompañara el comboBox con una etiqueta de texto en la que se lea “ciudad”, para colocar este texto en las etiquetas basta con pulsar dos veces con el botón derecho del ratón sobre el objeto y se mostrará una ventana que permitirá cambiarlo. Para las lineEdit se colocarán las etiquetas con los siguientes textos ,”Nombre”,”Apellidos” y “Teléfono”. El texto para el pushbutton será borrar. De igual modo que se puede cambiar los textos que acompañan a cada objeto , se puede cambiar el nombre que los identifica , como ya se ha explicado anteriormente para la ventana de la aplicación (recordar editor de propiedades) ,cada objeto tendrá su ventana con el editor de propiedades. Qt Designer dispone además de una función importante llamada Layout que permite ajustar el tamaño relativo de los objetos así como las posiciones relativas entre GVA-ELAI-UPM 6 Vladimir del Corte Informe sobre librerías Qt cada uno de ellos (alinear horizontalmente o verticalmente), definir las distancias entre ellos, etc. A continuación podemos ver una imagen de cómo queda la primera página de la ventana de diálogo. pagina-1 La segunda página se utilizará para mostrar los datos introducidos por teclado en la primera pagina, el título de la página podría ser “resultados” , a esta página se pueden añadir los siguientes objetos añadir los siguientes objetos: Cuatro etiquetas textLabel con el texto “Nombre”,”Apellidos”,”Ciudad” y “Teléfono”, y otras cuatro vacías , estas ultimas servirán para albergar los datos introducidos en la anterior pagina. Para poder realizar la presentación de los datos introducidos por teclado habrá q implementar una función que realica esto, pero esto ya se verá más adelante cuando se comente la forma de implementar la clase de ventana de dialogo que se está creando con esta aplicación. También se puede incluir en esta página una etiqueta con fondo de color por ejemplo, que pida la confirmación de los datos . En la siguiente figura podemos ver como quedaría esta segunda página. GVA-ELAI-UPM 7 Vladimir del Corte Informe sobre librerías Qt pagina-2 2.2 Slots y Signals. En la primera página de la ventana de diálogo se añadió un pushbutton al que se le asignó el texto borrar. Pero en realidad este botón no estaba asociado con ninguna función , es decir no tenía utilidad alguna. Los slots y signals (señales) son un mecanismo de comunicación entre objetos, esta es la principal característica de Qt y es el rasgo que hace distintas las librerías Qt del resto de herramientas para la elaboración de GUI, es un mecanismo de comunicación seguro, flexible y totalmente orientado a objetos y por supuesto implementado en C++. En la programación con GUI se busca que los cambios producidos en un objeto sean comunicados a otros objetos, por ejemplo cuando hacemos click en un botón para que se cierre una ventana, lo que se hace es posibilitar la comunicación entre los dos objetos. Otras herramientas de diseño de GUI llevan a cabo la comunicación entre objetos usando los llamados callbacks. Un callback es un puntero a una función, con este mecanismo si se quiere procesar una determinada función cada vez que ocurre un evento en un objeto , lo que se hace es pasar un puntero a otra función (el callback) a la función deseada y será esta la que se encargue de llamar al callback en el momento apropiado. Este tipo de comunicación tiene el inconveniente de no ser totalmente seguro puesto que no se sabe si se llamará al callback con los argumentos apropiados y además la función que llama al callback debe saber exactamente a que callback llamar, ademas es un sistema inflexible y no esta orientado a objetos. El mecanismo de slots y señales es diferente, los objetos Qt emiten señales cada vez que ocurre un evento, por ejemplo cuando se hace click sobre un PushButton este emitirá la señal ‘clicked’, el programador puede asociar esa señal con una función que hará de respuesta a la señal (a esta función se le denomina slot), y así llamar a la función connect para conectar ese slot con la señal deseada. GVA-ELAI-UPM 8 Vladimir del Corte Informe sobre librerías Qt Un ejemplo de esto puede ser el asociar la señal emitida por un boton cuando se hace click sobre él, con un slot que cierra la aplicación. Esto se implementaría de la siguiente forma. connect( PushButton, SIGNAL( clicked() ), qapp, SLOT( quit() ) ); Las signals y los slots pueden llevar cualquier numero de argumentos y de cualquier tipo. Las signals (señales) son emitidas por los objetos cuando cambia el estado de estos (por ejemplo cuando se pulsa un botón). Los slots son funciones a las que se llama como respuesta ante una señal emitida, los objetos también tienen asociados slots predefinidos aunque en este caso se pueden implementar nuevos slots y asociarlos a un objeto determinado. Como ya se ha dicho este mecanismo es más seguro, un objeto envía una señal sin importarle el slot que se encargara de recibirla. Este mecanismo asegura que si un slot esta conectado a una señal este será llamado por la señal pasándole los parámetros de forma correcta. Todas las clases que derivan de la clase Qobject o de alguna de sus subclases (por ejemplo Qwidget como es el caso de nuestra ventana) pueden se conectados por medio del mecanismo de slots y signals. Se pueden conectar tantas señales como se quiera a un slots, y una señal puede ser conectada a tantos slots como se quiera, incluso se puede conectar una señal a otra señal( la segunda será emitida inmediatamente después de emitirse la primera). Los slots son simplemente funciones miembro que se utilizan para el mecanismo de comunicación de objetos pero además pueden utilizarse como una función miembro más. Las señales son generadas automáticamente por el meta object compiler (se verá mas adelante) no pueden ser implementadas en los archivos .cpp y nunca devuelven ningún tipo de dato, es decir son de tipo void. Un slot es llamado cuando la señal asociada a él es emitida. Los slots son simplemente funciones miembro que se utilizan para el mecanismo de comunicación de objetos pero además pueden ser de manera normal sin tener que estar asociado a una señal. Como se ha dicho los slots son simples funciones miembro y como estas tienen definidos y restringido su acceso. El tipo de derecho al acceso al slot determina quien puede ser asociado a él. Public slots: cualquier clase puede asociar señales a ellos. Protected slots: Sólo la clase en la que esta definido y las derivadas de esta pueden asociar señales a él. Private slots: Solo la clase en que esta definido puede asociarle señales. Además se pueden definir slots virtuales. El inconveniente que podemos encontrar a este mecanismo de comunicación entre objetos es que es algo más lento que el mecanismo de callbacks. Qt Designer permite conectar los objetos mediante slots y señales de una manera fácil y rápida, en el caso que se esta tratando en este informe se asoció el botón de borrar con las lineEdit. Para ello en el menú Tools se elige la opción conect signals/slots GVA-ELAI-UPM 9 Vladimir del Corte Informe sobre librerías Qt pinchando en el botón borrar y sin soltar se une con una de las lineEdit, al soltar aparecerá una ventana donde se podrá elegir en primer lugar la señal , y como se verá al tratarse de un botón se puede elegir entre las señales ‘pressed’ ,’clicked’,’released’ etc. en este caso se eligirá como señal que emite el botón ‘clicked’ esta señal se emitirá por tanto cuando se haga click sobre en botón, en segundo lugar hay que elegir el slot, como, en este caso va a actuar sobre una lineEdit existen otras opciones que son: ‘clear’, ‘deselect’, etc.en este caso se eligirá ‘clear’( esto borrará el contenido de la LineEdit). Después de esto se repite el proceso para el resto de LineEdit. Ventana de dialogo para editar las conexiones entre objetos mediante señales y slots en Qt Designer. La línea de código que realiza esto es la siguiente: connect( PushButton1, SIGNAL( clicked() ), LineEdit1, SLOT( clear() ) ); La forma genérica de esta función es la siguiente: Connect (&a, SIGNAL (signal()), &b, SLOT(slot())); El primer miembro de la función es un puntero al objeto que emite la señal , el segundo indica la señal (la función signal())que emite el objeto, el tercer miembro es un puntero al objeto que recibe la señal y que tendrá como función miembro el slot asociado a la señal (slot()). Después de realizar esto ya esta asociado la señal que se produce al hacer click sobre el botón con un slot que lo que hace es borrar el contenido de las lineEdit. También existe la opción de asociar una señal con un slot que puede ser definido por el programador y más tarde implementarlo. Como nota se puede añadir que los botones de next, back, cancel que aparecían con la plantilla wizard(clase QWizard) ya llevan asociados sus señales y slots predefinidos, por ejemplo el botón next esta asociado con el slot showPage (más tarde se estudiará esta función en profundidad). GVA-ELAI-UPM 10 Vladimir del Corte Informe sobre librerías Qt Una vez hecho esto se puede decir que se ha acabado de realizar el diseño del modelo de ventana de dialogo. El siguiente paso será guardarlo para posteriormente implementarlo, este tipo de archivos se guarda con la extensión *.ui. 2.3 Las herramientas uic, moc y tmake. Con el paquete de librerías Qt además de disponer de Qt Designer se incluyen otras herramientas para la implementación de las aplicaciones. Estas herramientas se encuentran en la carpeta ...Qt/bin, funcionan sobre la línea de comandos, a continuación se explicará como funciona cada una de ellas. - La aplicación uic permite implementar la clase del modelo realizado con Qt Designer a partir del archivo .ui, es decir obtener los archivos .cpp y .h para ello utiliza la siguiente instrucción. Para obtener el archivo de cabecera (.h): uic -o <ejemploqt.h> <ejemploqt.ui> Para obtener el archivo de implementación (.cpp): uic -o <ejemploqt.cpp> -impl <ejemploqt.h> <ejemploqt.ui> - Con esto ya están los archivos implementados, es decir la clase de la ventana creada ya esta definida ( el nombre que habíamos elegido es Wizard), pero aun falta algo. La herramienta moc (meta object compiler) sirve para obtener archivos de extensión .cpp que se encargan de implementar el mecanismo de slots/signals. moc -o <moc_ejemploqt.cpp> <ejemploqt.h> Para ver todas las opciones que pueden ofrecer estas aplicaciones se puede utilizar la opción uic -? y moc-?. - Por último tmake es una herramienta que permite generar makefiles independientemente de la plataforma en la que se esta trabajando a partir de un archivo de proyecto de extensión .pro. Estos archivos (Makefiles) sirven para organizar la compilación y linkado de los programas. Esta herramienta puede generar además archivos de extensión dsp , es decir proyectos para Visual C++. Antes de poder generar el archivo Makefile o el de proyecto de visual (.dsp) es preciso implementar el main de la aplicación, un ejemplo de este archivo podría ser este que se presenta a continuación. /**************************************************************************** ** main.cpp ** ** Gva-elai-upm ** ** Ejemplo de ventana de dialogo con librerias Qt ** *****************************************************************************/ GVA-ELAI-UPM 11 Vladimir del Corte Informe sobre librerías Qt #include "informeqt.h" #include <qapplication.h> int main(int argc,char **argv) { QApplication a(argc,argv); Wizard wizard; wizard.setCaption("Qt Example - Wizard"); return wizard.exec(); } A continuación se comentarán brevemente algunas de las líneas de este archivo: #include "informeqt.h" Esta línea incluye la definición de la clase de ventana que hemos creado. #include <qapplication.h> Esta línea incluye la definición de clase QApplication. Debe haber exactamente un solo objeto QApplication en cada aplicación que use Qt. QApplication gestiona varios recursos a lo largo de la aplicación, así como el conjunto de caracteres estándar y el cursor. int main(int argc,char **argv) La función main() es el punto de entrada al programa. Casi siempre usando Qt, main() solo necesita ejecutar una especie de inicialización antes de pasar el control a las librerías Qt, éstas le van diciendo al programa las acciones del usuario vía eventos. argc guarda el número de argumentos que se le pasan al programa y argv es un array que contiene cada uno de estos argumentos. Esa es la especificación C/C++. Esto no es del todo preciso en Qt ya que Qt necesita procesar esos argumentos con podemos ver en la siguiente instrucción. QApplication a(argc,argv); a es esa aplicación del programa. Aquí se crea y son procesados algunos de los argumentos que se pasan al programa desde la línea de comandos. Nota que todos los argumentos de la línea de comandos reconocidos por Qt son sacados de argv (y en consecuencia argc disminuye). Wizard ejemplo; Aquí se crea un objeto de la clase que se ha creado. return ejemplo.exec(); Así es como main() pasa el control a Qt. exec() éste lo devolverá cuando la aplicación finalice. Una vez definida e implementada la clase de la aplicación e implementado el archivo main se puede pasar a crear el archivo que contendrá el proyecto ( el archivo GVA-ELAI-UPM 12 Vladimir del Corte Informe sobre librerías Qt .dsp) con el tmake (al utilizar tmake para generar el proyecto no es necesario haber utilizado antes el meta object compiler para generar el moc_ejemplo1.cpp puesto que tmake lo genera automáticamente). La aplicación tmake lee un archivo de proyecto especial con extensión (.pro) que podemos generar con un editor de texto, un archivo de este tipo seria el siguiente: TEMPLATE= app CONFIG+= qt warn_on release HEADERS= ejemploqt.h SOURCES= ejemploqt.cpp \ main.cpp TARGET= application DEFINES+=QT_DLL QT_THREAD_SUPPORT DEPENDPATH=../../include REQUIRES=full-config tmake lee este archivo y genera un makefile determinado para el sistema operativo y el compilador que estemos utilizando, este archivo .pro es el mismo independientemente de la plataforma que estemos utilizando. La línea “DEFINES+=QT_DLL QT_THREAD_SUPPORT” será la que permita generar los archivo .dsp (proyecto de Visual Studio). Si se quisiera utilizar este archivo para otra aplicación solo habría que modificar los archivos de cabecera y los archivos fuente. La manera de generar el dsp es esta: tmake –t vcapp <archivo.pro> -o <proyecto.dsp> Después de generar este archivo .dsp ya se puede compilar la aplicación , pero no hay que olvidar que antes hay que añadir algunas líneas de código para implementar la manera en que la aplicación lee los datos introducidos por teclado en la primera página para presentarlos ordenados en la segunda página. Si se está trabajando sobre plataforma Linux evidentemente es inutil generar archivos .dsp, en este caso no será necesario incluir la linea DEFINES+=QT_DLL QT_THREAD_SUPPORT, y la instrucción para generar el archivo Makefile toma esta forma. tmake <archivo.pro> -o Makefile Una vez generado el Makefile, mediante el comando make podemos realizar el compilado y linkado de nuestra aplicación. A continuación se implementará el código de las funciones que se van a añadir a la aplicación , y se explicará brevemente algunas de las funciones miembro de cada objeto que se utilizaran. Como se dijo cuando se explicaba el diseño de la ventana con Qt Designer , la plantilla elegida Wizard , incluía una serie de botones y se vio que el botón de next estaba asociado a un slot llamado showPage que no es si no una función miembro de la clase QWizard de la cual deriva la aplicación , esta función lo que hace es mostrar la GVA-ELAI-UPM 13 Vladimir del Corte Informe sobre librerías Qt siguiente ventana cada vez que se pulsa next . Como lo que se quiere es que cada vez que se pulse next para pasar a la página dos se presenten los datos que introducidos en la página uno y puesto que la ventana creada en este ejemplo deriva de la clase QWizard directamente lo que se hará será redefinir la función showPage para acomodarla a nuestras necesidades. Es decir se sobrecargará la función miembro showPage. Esta función es la siguiente: void Wizard::showPage(QWidget *page) { if(page==page1){ }else if(page == page2){ Result1->setText(ComboBox1->currentText()); result2->setText(LineEdit1->text()); result3->setText(LineEdit2->text()); result4->setText(LineEdit3->text()); } QWizard::showPage(page); if(page==page1){ }else if(page == page2){ finishButton()->setEnabled(TRUE); finishButton()->setFocus(); } Además se puede hacer que el botón de next de la pagina uno no se active hasta que no se hallan completado todos los datos , para esto habrá que añadir las siguientes líneas de codigo. connect( LineEdit1, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); connect( LineEdit2, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); connect( LineEdit3, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); Con estas líneas se conectan las LineEdit con el slot dataChanged() este slot se encargará de comprobar si las LineEdit están vacías o no. Para implementar este slot en primer lugar hay que añadir la declaración en el archivo .h , de esta forma: protected slots: void dataChanged( const QString & ); A continuación se muestra la implementación del slot en el archivo .cpp. void Wizard::dataChanged( const QString & ) { if ( !LineEdit1->text().isEmpty() && !LineEdit2->text().isEmpty() && !LineEdit3->text().isEmpty() ) nextButton()->setEnabled( TRUE ); else nextButton()->setEnabled( FALSE ); } Además hay que añadir la línea dataChanged( showPage() resultando este slot como se puede ver aquí. GVA-ELAI-UPM 14 LineEdit1->text() ); en el slot Vladimir del Corte Informe sobre librerías Qt void Wizard::showPage(QWidget *page) { if(page==page1){ }else if(page == page2){ Result1->setText(ComboBox1->currentText()); result2->setText(LineEdit1->text()); result3->setText(LineEdit2->text()); result4->setText(LineEdit3->text()); } QWizard::showPage(page); if(page==page1){ dataChanged( LineEdit1->text() ); }else if(page == page2){ finishButton()->setEnabled(TRUE); finishButton()->setFocus(); } De esta forma cada vez que se valla a pasar de pagina se comprobará el estado de las LineEdit. A continuación podemos ver el código de los archivos generados para implementar la aplicación. /**************************************************************************** ** main.cpp ** ** Gva-elai-upm ** ** Ejemplo de ventana de dialogo con librerias Qt ** *****************************************************************************/ #include "informeqt.h" #include <qapplication.h> int main(int argc,char **argv) { QApplication a(argc,argv); Wizard wizard; wizard.setCaption("Qt Example - Wizard"); return wizard.exec(); } El archivo informeqt.h: /**************************************************************************** ** Form interface generated from reading ui file 'informeqt.ui' ** ** Created: Tue Jan 15 19:15:12 2002 ** by: The User Interface Compiler (uic) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ #ifndef WIZARD_H #define WIZARD_H GVA-ELAI-UPM 15 Vladimir del Corte Informe sobre librerías Qt #include <qvariant.h> #include <qwizard.h> class QVBoxLayout; class QHBoxLayout; class QGridLayout; class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QWidget; class Wizard : public QWizard { Q_OBJECT public: Wizard( QWidget* parent = 0, const char* name = 0, bool modal = FALSE, WFlags fl = 0 ); ~Wizard(); void showPage(QWidget* page); QWidget* page1; QComboBox* ComboBox1; QLineEdit* LineEdit1; QLineEdit* LineEdit2; QLineEdit* LineEdit3; QLabel* ciudad1; QLabel* apellidos1; QLabel* nombre1; QLabel* telefono1; QPushButton* PushButton1; QWidget* page2; QLabel* ciudad2; QLabel* nombre2; QLabel* telefono2; QLabel* apellidos2; QLabel* Result1; QLabel* result2; QLabel* result3; QLabel* result4; QLabel* TextLabel1; protected slots: void dataChanged( const QString & ); }; #endif // WIZARD_H GVA-ELAI-UPM 16 Vladimir del Corte Informe sobre librerías Qt Este es el archivo implementación de la clase de ventana que hemos creado, informeqt.cpp /**************************************************************************** ** Form implementation generated from reading ui file 'informeqt.ui' ** ** Created: Tue Jan 15 19:15:31 2002 ** by: The User Interface Compiler (uic) ** ** WARNING! All changes made in this file will be lost! ****************************************************************************/ #include "informeqt.h" #include <qcombobox.h> #include <qlabel.h> #include <qlineedit.h> #include <qpushbutton.h> #include <qwidget.h> #include <qlayout.h> #include <qvariant.h> #include <qtooltip.h> #include <qwhatsthis.h> /* * Constructs a Wizard which is a child of 'parent', with the * name 'name' and widget flags set to 'f' * * The wizard will by default be modeless, unless you set 'modal' to * TRUE to construct a modal wizard. */ Wizard::Wizard( QWidget* parent, const char* name, bool modal, WFlags fl ) : QWizard( parent, name, TRUE, fl ) { if ( !name ) setName( "Wizard" ); resize( 343, 263 ); setProperty( "caption", tr( "Ejemplo qt" ) ); page1 = new QWidget( this, "page1" ); LineEdit1 = new QLineEdit( page1, "LineEdit1" ); LineEdit1->setGeometry( QRect( 144, 39, 181, 22 ) ); LineEdit3 = new QLineEdit( page1, "LineEdit3" ); LineEdit3->setGeometry( QRect( 144, 99, 181, 22 ) ); LineEdit2 = new QLineEdit( page1, "LineEdit2" ); LineEdit2->setGeometry( QRect( 144, 69, 181, 22 ) ); ciudad1 = new QLabel( page1, "ciudad1" ); ciudad1->setGeometry( QRect( 4, 9, 121, 21 ) ); ciudad1->setProperty( "text", tr( "Ciudad" ) ); apellidos1 = new QLabel( page1, "apellidos1" ); apellidos1->setGeometry( QRect( 4, 39, 121, 21 ) ); apellidos1->setProperty( "text", tr( "Apellidos" ) ); nombre1 = new QLabel( page1, "nombre1" ); nombre1->setGeometry( QRect( 4, 69, 121, 21 ) ); nombre1->setProperty( "text", tr( "Nombre" ) ); telefono1 = new QLabel( page1, "telefono1" ); telefono1->setGeometry( QRect( 4, 99, 131, 21 ) ); telefono1->setProperty( "text", tr( "Telefono" ) ); ComboBox1 = new QComboBox( FALSE, page1, "ComboBox1" ); ComboBox1->insertItem( tr( "Madrid" ) ); ComboBox1->insertItem( tr( "Bilbao" ) ); ComboBox1->insertItem( tr( QString::fromUtf8( "A Coruña" ) ) ); ComboBox1->insertItem( tr( "Valencia" ) ); ComboBox1->setGeometry( QRect( 144, 9, 180, 20 ) ); PushButton1 = new QPushButton( page1, "PushButton1" ); PushButton1->setGeometry( QRect( 110, 150, 93, 26 ) ); GVA-ELAI-UPM 17 Vladimir del Corte Informe sobre librerías Qt PushButton1->setProperty( "text", tr( "Borrar" ) ); addPage( page1, tr( "Datos" ) ); page2 = new QWidget( this, "page2" ); ciudad2 = new QLabel( page2, "ciudad2" ); ciudad2->setGeometry( QRect( 4, 19, 131, 31 ) ); ciudad2->setProperty( "text", tr( "Ciudad:" ) ); nombre2 = new QLabel( page2, "nombre2" ); nombre2->setGeometry( QRect( 4, 79, 131, 31 ) ); nombre2->setProperty( "text", tr( "Nombre:" ) ); Result1 = new QLabel( page2, "Result1" ); Result1->setGeometry( QRect( 164, 19, 151, 31 ) ); Result1->setProperty( "text", tr( "-----" ) ); result2 = new QLabel( page2, "result2" ); result2->setGeometry( QRect( 164, 49, 151, 31 ) ); result2->setProperty( "text", tr( "-----" ) ); result3 = new QLabel( page2, "result3" ); result3->setGeometry( QRect( 164, 79, 161, 31 ) ); result3->setProperty( "text", tr( "-----" ) ); result4 = new QLabel( page2, "result4" ); result4->setGeometry( QRect( 164, 109, 161, 31 ) ); result4->setProperty( "text", tr( "-----" ) ); TextLabel1 = new QLabel( page2, "TextLabel1" ); TextLabel1->setGeometry( QRect( 14, 139, 311, 41 ) ); TextLabel1->setProperty( "text", tr( QString::fromUtf8( " ! Pulse finish para confirmar los datos ¡" ) ) ); telefono2 = new QLabel( page2, "telefono2" ); telefono2->setGeometry( QRect( 4, 109, 130, 30 ) ); telefono2->setProperty( "text", tr( "Telefono:" ) ); apellidos2 = new QLabel( page2, "apellidos2" ); apellidos2->setGeometry( QRect( 4, 49, 131, 31 ) ); apellidos2->setProperty( "text", tr( "Apellidos:" ) ); addPage( page2, tr( "Resultados" ) ); // signals and slots connections connect( PushButton1, SIGNAL( clicked() ), LineEdit1, SLOT( clear() ) ); connect( PushButton1, SIGNAL( clicked() ), LineEdit2, SLOT( clear() ) ); connect( PushButton1, SIGNAL( clicked() ), LineEdit3, SLOT( clear() ) ); connect( LineEdit1, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); connect( LineEdit2, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); connect( LineEdit3, SIGNAL( textChanged( const QString & ) ), this, SLOT( dataChanged( const QString & ) ) ); } /* * Destroys the object and frees any allocated resources */ Wizard::~Wizard() { // no need to delete child widgets, Qt does it all for us } void Wizard::showPage(QWidget *page) { if(page==page1){ }else if(page == page2){ Result1->setText(ComboBox1->currentText()); result2->setText(LineEdit1->text()); result3->setText(LineEdit2->text()); result4->setText(LineEdit3->text()); } GVA-ELAI-UPM 18 Vladimir del Corte Informe sobre librerías Qt QWizard::showPage(page); if(page==page1){ dataChanged( LineEdit1->text() ); }else if(page == page2){ finishButton()->setEnabled(TRUE); finishButton()->setFocus(); } } void Wizard::dataChanged( const QString & ) { if ( !LineEdit1->text().isEmpty() && !LineEdit2->text().isEmpty() && !LineEdit3->text().isEmpty() ) nextButton()->setEnabled( TRUE ); else nextButton()->setEnabled( FALSE ); } GVA-ELAI-UPM 19 Vladimir del Corte Informe sobre librerías Qt 3. CÓMO INTEGRAR Qt DESIGNER EN VISUAL STUDIO Existe una manera de integrar Qt Designer en Visual studio compilando el proyecto qmsdev.dsp suministrado con las librerías Qt. Los pasos a seguir son los siguientes: Una vez Visual Studio se abre el archivo de proyecto (file| open workspace) situado en QTDIR%\tools\designer\integration\ qmsdev\qmsdev.dsp. Hecho esto se hace click en el menú Build|Set Active Configuration y después sobre la lista que aparece otra vez click en 'QMsDev - Win32 Release' y finalmente se pulsa OK. Ahora hacemos clic en Build|Build qmsdev.dll. y se copia el archivo generado %QTDIR%\tools\designer\integration\qmsdev\Release\qmsdev.dll en el directorio Microsoft Visual Studio\Common\MSDev98\AddIns. Con esto no queda más que hacer click en el menú Tools|customize , hacemos click en Add-in Macro Files tab ,pulsamos el Browse button , se cambia el tipo de archivos a 'Add-ins (.dll)' , y nos situamos en el directorio Microsoft Visual Studio\Common\MSDev98\AddIns , y se selecciona el archivo qmsdev.dll y finalmente se pulsa open para finalizar. Una vez echo esto una nueva barra herramientas aparecerá en Visual Studio , con los siguientes botones: • • • • • • • New Qt Project – Abre un nuevo Proyecto Qt. Generate Qt Project – Arranca qmake similar a tmake. New Qt Dialog – Añade un nuevo Qt Dialog (ventana de dialogo) vacío al proyecto activo. Qt GUI Designer – Arranca Qt Designer. Use Qt – Añade las librerías Qt al proyecto activo. Add MOC – Añade el precompilador moc al archivo activo. Add UIC – Añade el precompilador uic al archivo activo De esta manera también es muy fácil crea una pequeña aplicación Qt, es decir una sencilla ventana de dialogo, para ello basta con pulsar el botón de nuevo proyecto, automáticamente aparecerá una ventana donde se puede especificar el nombre del proyecto y la ubicación de esta además se puede elegir el tipo de aplicación entre una simple ventana de dialogo (Dialog) o una ventana principal (Main Window), en este elegiremos Dialog, y el tipo de librerías que se quieran usar, estáticas o dinámicas. Automáticamente se añadirán los siguientes archivos al proyecto: • • • Este fichero contiene las declaraciones de las subclases y es donde las especificaras. Newdocumentimpl.cpp Este fichero contiene una subclase de la ventana de dialogo y es donde se añade nuestro código para especificarla. newdocument.ui Este es el fichero de Qt Designer que contiene el diseño de la ventana de dialogo, a partir de este se generan el resto de ficheros. newdocumentimpl.h GVA-ELAI-UPM 20 Vladimir del Corte • • • • Informe sobre librerías Qt Este es el fichero que contiene la implementación de la clase de la ventana de dialogo, se crea y actualiza mediante Qt Designer. moc_newdocument.cpp: Ya comentamos este tipo de archivos , puede ser ignorado. moc_newdocumentimpl.cpp: Ídem. newdocument.h: Es el fichero de cabecera de la ventana de dialogo. newdocument.cpp: Automáticamente también se abrirá Qt Designer, ya solo thay que diseñar la ventana de diálogo como se explicó anteriormente; cuando se finaliza esto se guarda el archivo y automáticamente se completan los anteriores archivos, ya no queda más que implementar el main de la aplicación y compilar el proyecto. GVA-ELAI-UPM 21 Vladimir del Corte Informe sobre librerías Qt 4. CÓMO CREAR UNA VENTANA PRINCIPAL (MAIN WINDOW) CON ACCIONES Y MENUS POPUP. A continuación se explicará como implementar una ventana principal (Main Window), que contenga una barra de menús con acciones, barra de herramientas y barra de estado.. Para realizar esto se puede utilizar la barra de herramientas integrada en Visual Studio. En primer lugar se pincha en el botón de nuevo proyecto a continuación aparecerá la ventana de diálogo donde se nos pedirá el nombre del proyecto y la dirección donde queremos ubicarlo y elegimos como tipo de aplicación Main Window (ventana principal). Al elegir esta opción también se ofrecerá la opción de crear una aplicación con soporte para MDI (multi document interface). En este caso por tratarse de un simple ejemplo no haremos uso de ella. Una vez echo esto automáticamente se incluirán lo siguientes archivos al proyecto: • • • • main.cpp ventanaprincipal.cpp ventanaprincipal.h moc_ventanaprincipal.cpp 4.1 Cómo añadir el menú a la ventana principal. Se abre el archivo ventanaprincipal.cpp y se deben añadir las siguientes líneas (también se podrían añadir en el archivo ventanaprincipal.h). #include <qaction.h> #include <qpopupmenu.h> #include <qmenubar.h> Se añade el siguiente código al constructor ventanaprincipal. QAction *fileNewAction = new QAction( "New", "&New", CTRL+Key_N, this, "new" ); QAction *fileQuitAction = new QAction( "Quit", "&Quit", CTRL+Key_Q, this, "quit" ); QPopupMenu *fileMenu = new QPopupMenu( this ); menuBar()->insertItem( "&File", fileMenu ); fileNewAction->addTo( fileMenu ); fileQuitAction->addTo( fileMenu ); Con esto se crean dos acciones, las acciones se pueden usar para teclas rápidas , opciones de menú y barra de herramientas. A continuación se crea un menú y se añadiran las acciones a este menú. El siguiente paso será asociar estas acciones a sus respectivas funciones. Para conseguir que la opción quit funcione se necesita añadir otro fichero fuente y una conexión mediante slots/signals como vimos anteriormente. El fichero fuente es include <qapplication.h> , qApp es un puntero global, disponible en todos lo programas Qt que apunta a la instancia QAplication. GVA-ELAI-UPM 22 Vladimir del Corte Informe sobre librerías Qt Para asociar un mecanismo de slots/signals con la opción Quit se incluirá la siguiente línea. connect( fileQuitAction, SIGNAL( activated() ), qApp, SLOT( quit() ) ); 4.2 Cómo añadir el Widget central a la ventana principal. Las aplicaciones Qt del estilo de Main Window (ventana principal) se construyen alrededor de un Widget central. Para este ejemplo se utilizará como Widget central un objeto de la clase Qvbox, para ello se necesita incluir otro fichero de cabecera y unas cuantas líneas de código. Se añade el fichero de cabecera #include <qvbox.h> y las siguientes líneas de código. QVBox* wcentral = new QVBox( this ); wcentral->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); wcentral->setFocus(); setCentralWidget(wcentral); 4.3 Cómo llamar a una ventana de dialogo. En primer lugar se pincha el icono de la barra de herramientas para crear una nueva ventana de dialogo en ese momento se abrirá Qt Designer y se seguirá el proceso explicado anteriormente para crear la ventana de dialogo. A continuación se explicará como llamar a la ventana de dialogo, que fue creada en el paso anterior, desde la ventana principal, para ello se implementará un nuevo slot el cual se asociará con la acción new anteriormente creada. En primer lugar se cambia la definición de ventanaPrincipal en el archivo ventanaprincipal.h por esta: class ventanaprincipal : public QMainWindow { Q_OBJECT public: ventanaprincipal( QWidget* parent = 0, const char* name = 0, WFlags f = WType_TopLevel ); public slots: void newDocument(); private: QVBox* wcentral; }; Lo siguiente es implementar el slot newdocument en el archivo ventanaPrincipal.cpp. Para ello se añadirá el fichero de cabecera de la ventana de dialogo, en este caso era #include <wizard.h> La implementación del nuevo slot será: GVA-ELAI-UPM 23 Vladimir del Corte Informe sobre librerías Qt void ventanaprincipal::newDocument() { newdocument *docDlg = new newdocument( this, "new", TRUE ); If ( docDlg->exec() ) { ; // user clicked OK } else { ; // user clicked Cancel } delete docDlg; } Por ultimo sólo queda asociar la acción del menú File|menu con el slot newdocument , esto se hace como anteriormente, añadiendo otra llamada de conexión en el constructor de ventanaPrincipal : connect( fileNewAction, SIGNAL( activated() ), this, SLOT( newDocument() ) ); Finalmente se puede compilar la aplicación y se podrá ver que las aciones de menú File|New y File|Quit están operativas. 4.4 Un ejemplo de main window. Para comprender mejor la implementación de una ventana principal a continuación se explicará un ejemplo completo de Main Window, en este caso se trata de un sencillo editor de texto que tiene barra de menús, barra de herramientas y barra de estado. Para realizar esta aplicación no se ha utilizado la barra de herramientas integrada en Visual. El editor constará de una barra de menús con tres menús despegables (popup), los menús Archivo ,Edición y Ayuda. El menú Archivo constará de las siguientes acciones Nuevo, Abrir Archivo, Guardar, Guardar como, Salir y Cerrar, el menú Edición tendrá las acciones de Copiar, Cortar y Pegar y finalmente el menú Ayuda tendrá las acciones Acerca de , Acerca de Qt y Que es esto?(Whats this). La barra de herramientas tendrá los iconos de las acciones Abrir, Guardar y Que es esto?. En primer lugar se comentará el archivo de extensión .h que hay que generar. El archivo es el siguiente. /**************************************************************/ // - Grupo de Visión Artificial // Departamento de Electrónica, Automática // e Informática Industrial // /**************************************************************/ //Título: Ejemplo de aplicación con Qt //Archivo: VentanaPrincipal.h // Este ejemplo ha sido creado con la version 2.3.0 de Qt // Copyright (C) 1992-2000 Trolltech AS. All rights reserved. /**************************************************************/ GVA-ELAI-UPM 24 Vladimir del Corte Informe sobre librerías Qt #ifndef APPLICATION_H #define APPLICATION_H #include <qmainwindow.h> class QMultiLineEdit; class QAction; class VentanaPrincipal: public QMainWindow { Q_OBJECT public: VentanaPrincipal(); ~VentanaPrincipal(); protected: void closeEvent( QCloseEvent* ); private slots: void nuevoDoc(); void abrir(); void abrir( const char *fileName ); void guardar(); void guardarcomo(); void about(); void aboutQt(); private: void iniciarvista(); void iniciarActions(); void iniciarMenuBar(); void iniciarToolBar(); void iniciarStatusBar(); QAction *archivoNuevoAction, *archivoAbrirAction, *archivoGuardarAction, * archivoGuardarcomoAction, *archivoCerrarAction,*archivoSalirAction, *copiarAction ,*cortarAction ,*pegarAction,*undoAction,*redoAction; QPopupMenu * archivo; QPopupMenu * editar; QPopupMenu * ayuda; QMultiLineEdit *MultiLine; QString filename; }; #endif En este archivo lo primero que se puede ver es la declaración de tres clases, una es QAction, de esta clase se crearán las acciones necesarias para añadirlas a los menús desplegables, otra es QpopupMenu de la que se crearan los menús desplegables, y la última es QMultiLineEdit, se creará un objeto de esta clase que será el objeto central del editor( será la superficie sobre la que se escribe en el editor). Seguidamente se encuentra la declaración la clase creada que como se puede ver deriva de la clase QMainWindow: class VentanaPrincipal: public QmainWindow Tras esto se puede ver la declaración de cada uno de sus miembros, a continuación se comenta cada uno de ellos brevemente: GVA-ELAI-UPM 25 Vladimir del Corte Informe sobre librerías Qt El constructor y el destructor public: VentanaPrincipal(); ~VentanaPrincipal(); Un miembro de tipo protected void closeEvent( QCloseEvent* ); este es un miembro de la clase QWidget como la clase de esta aplicación deriva de la clase QMainWindow y esta deriva a su vez de la clase Qwidget la clase de esta aplicación heredará este miembro y aquí será reimplementado como se verá mas tarde en el archivo de implementación .cpp. Los siguientes miembros de la clase son los slots que serán asociados a cada una de las acciones de los menús de la aplicación. Son de tipo private es decir solo puede acceder a ellos la clase en la que están definidos. private slots: void nuevoDoc(); void abrir(); void abrir( const char *fileName ); void guardar(); void guardarcomo(); void about(); void aboutQt(); El resto son la declaración de las demás funciones miembro de la clase ( serán explicadas más tarde) y la declaración de los objetos que utilizará la clase como son las Actions, los menús popup, la MultiLineEdit que hará de objeto central etc. private: void iniciarvista(); void iniciarActions(); void iniciarMenuBar(); void iniciarToolBar(); void iniciarStatusBar(); QAction *archivoNuevoAction, *archivoAbrirAction, *archivoGuardarAction, * archivoGuardarcomoAction, *archivoCerrarAction,*archivoSalirAction, *copiarAction ,*cortarAction ,*pegarAction; QPopupMenu * archivo; QPopupMenu * editar; QPopupMenu * ayuda; QMultiLineEdit *MultiLine; QString filename; A continuación se comentará el archivo de extensión .cpp. Este archivo lo podemos ver a continuación. /**************************************************************/ // - Grupo de Visión Artificial // Departamento de Electrónica, Automática // e Informática Industrial // /**************************************************************/ GVA-ELAI-UPM 26 Vladimir del Corte Informe sobre librerías Qt //Título: Ejemplo de aplicacion con Qt //Archivo :Ventanaprincipal.cpp // Este ejemplo ha sido creado con la versión 2.3.0 de Qt // Copyright (C) 1992-2000 Trolltech AS. All rights reserved. /**************************************************************/ #include "ventanaprincipal.h" #include <qimage.h> #include <qpixmap.h> #include <qtoolbar.h> #include <qtoolbutton.h> #include <qpopupmenu.h> #include <qmenubar.h> #include <qkeycode.h> #include <qmultilineedit.h> #include <qfile.h> #include <qfiledialog.h> #include <qstatusbar.h> #include <qmessagebox.h> #include <qprinter.h> #include <qapplication.h> #include <qaccel.h> #include <qtextstream.h> #include <qpainter.h> #include <qpaintdevicemetrics.h> #include <qwhatsthis.h> #include "filesave.xpm" #include "fileopen.xpm" #include "fileprint.xpm" const char * TextoAbrirArchivo = "<img source=\"fileopen\"> " "Haz click en este botón para abrir un archivo nuevo.<br>" "También puede seleccionar el comando <b>Abrir Archivo</b> desde el menú Archivo."; const char * TextoGuardar = "Guarda el archivo activo.\n\n" "También puedes seleccionar en el menú Edición el comando Guardar y Guardar como ...\n"; VentanaPrincipal::VentanaPrincipal() : QMainWindow( 0, "Ejemplo de ventana principal con Qt", WDestructiveClose ) { iniciarvista(); iniciarActions(); iniciarMenuBar(); iniciarToolBar(); iniciarStatusBar(); } VentanaPrincipal::~VentanaPrincipal() { } void VentanaPrincipal::iniciarvista() { // Se crea y define el objeto principal ( es decir la MultiLineEdit ) MultiLine = new QMultiLineEdit( this, "editor" ); MultiLine->setFocus(); setCentralWidget( MultiLine ); resize( 600, 600 ); } void VentanaPrincipal::iniciarActions() { // Se crean las acciones de los menús popup // Acciones del menú Archivo archivoNuevoAction = new QAction( "Nuevo", "&Nuevo", CTRL+Key_N, this, "Nuevo" ); connect( archivoNuevoAction, SIGNAL( activated() ) , this, SLOT( nuevoDoc() ) ); archivoAbrirAction = new QAction( "Abrir archivo", QPixmap( fileopen ), "&Abrir archivo", CTRL+Key_A, this, "open" ); GVA-ELAI-UPM 27 Vladimir del Corte Informe sobre librerías Qt connect(archivoAbrirAction, SIGNAL( activated() ) , this, SLOT( abrir() ) ); QMimeSourceFactory::defaultFactory()->setPixmap( "fileopen", QPixmap( fileopen ) ); archivoAbrirAction->setWhatsThis( TextoAbrirArchivo ); archivoGuardarAction = new QAction( "Guardar ", QPixmap( filesave ), "&Guardar", CTRL+Key_G, this, "Guardar" ); connect( archivoGuardarAction, SIGNAL( activated() ) , this, SLOT( guardar() ) ); archivoGuardarAction->setWhatsThis( TextoGuardar ); archivoGuardarAction = new QAction( "Guardar como", "G&uardar como", 0, this, "Guardar como" ); connect( archivoGuardarcomoAction, SIGNAL( activated() ) , this, SLOT( guardarcomo() ) ); archivoGuardarAction->setWhatsThis( TextoGuardar ); archivoCerrarAction = new QAction( "Cerrar", "&Cerrar", CTRL+Key_C, this, "Cerrar" ); connect( archivoCerrarAction, SIGNAL( activated() ) , this, SLOT( close() ) ); archivoSalirAction = new QAction( "Salir", "&Salir", CTRL+Key_S, this, "Salir" ); connect(archivoSalirAction, SIGNAL( activated() ) , qApp, SLOT( closeAllWindows() ) ); //acciones del menú Edición copiarAction = new QAction( "Copiar", "&Copiar", CTRL+Key_C, this, "copiar" ); connect( copiarAction, SIGNAL( activated() ) , MultiLine, SLOT( copy() ) ); cortarAction = new QAction( "Cortar", "&Cortar", CTRL+Key_X, this, "Cortar" ); connect( cortarAction, SIGNAL( activated() ) ,MultiLine, SLOT( cut() ) ); pegarAction= new QAction( "Pegar", "&Pegar", CTRL+Key_V, this, "pegar" ); connect(pegarAction, SIGNAL( activated() ) ,MultiLine, SLOT( paste() ) ); } void VentanaPrincipal::iniciarMenuBar() { archivo = new QPopupMenu( this ); // Se crea el menú popup llamado Archivo menuBar()->insertItem( "&Archivo", archivo ); // Se inserta en la barra de menús archivoNuevoAction->addTo( archivo); //Se le añaden las acciones archivoAbrirAction->addTo(archivo); archivoGuardarAction->addTo(archivo); archivoGuardarcomoAction->addTo(archivo); archivo->insertSeparator(); archivoCerrarAction->addTo(archivo); archivoSalirAction->addTo(archivo); editar = new QPopupMenu( this ); menuBar()->insertItem( "&Edición", editar ); editar->insertSeparator(); copiarAction->addTo(editar); cortarAction->addTo(editar); pegarAction->addTo(editar); // Se crea el menu popup llamado Edición //Se inserta en la barra de menús ayuda = new QPopupMenu( this ); // Se crea el menu popup llamado ayuda menuBar()->insertSeparator(); menuBar()->insertItem( "&Ayuda", ayuda); //se inserta en la barra de menús ayuda->insertItem( "&Acerca de ...", this, SLOT(about()), Key_F1 ); //se añaden las acciones ayuda->insertItem( "Arcerca de &Qt", this, SLOT(aboutQt()) ); ayuda->insertSeparator(); ayuda->insertItem( "&Que es esto?", this, SLOT(whatsThis()), SHIFT+Key_F1 ); } void VentanaPrincipal::iniciarToolBar() { // se crea la barra de herramientas TOOLBAR QToolBar* fileTools = new QToolBar( this, "Barra de Herramientas" ); fileTools->setLabel( tr( "Barra de Herramientas" ) ); archivoAbrirAction->addTo( fileTools );//Se añaden las acciones archivoGuardarAction->addTo( fileTools ); (void)QWhatsThis::whatsThisButton( fileTools ); } GVA-ELAI-UPM 28 Vladimir del Corte Informe sobre librerías Qt void VentanaPrincipal::iniciarStatusBar() { // inicializacion de la barra de estado STATUSBAR statusBar()->message( "Listo,"); } //Implementación de los SLOTS creados void VentanaPrincipal::nuevoDoc() { VentanaPrincipal *ed = new VentanaPrincipal; ed->show(); } void VentanaPrincipal::abrir()//abre un archivo existente { QString fn = QFileDialog::getOpenFileName( QString::null, QString::null, this); if ( !fn.isEmpty() ) abrir( fn ); else //si no se especifica el nombre del archivo aparece un mensaje de error statusBar()->message( "No se puede abrir el archivo", 2000 ); } void VentanaPrincipal::abrir( const char *fileName )//Se abre el archivo de nombre ... { QFile f( fileName ); if ( !f.open( IO_ReadOnly ) ) return; filename = fileName; MultiLine->setAutoUpdate( FALSE ); MultiLine->clear(); QTextStream t(&f); while ( !t.eof() ) { QString s = t.readLine(); MultiLine->append( s ); } f.close(); MultiLine->setAutoUpdate( TRUE ); MultiLine->repaint(); MultiLine->setEdited( FALSE ); setCaption( fileName ); QString s; s.sprintf( "Documento listo %s", fileName ); statusBar()->message( s, 2000 ); } void VentanaPrincipal::guardar() { if ( filename.isEmpty() ) { guardarcomo(); return; } QString text = MultiLine->text(); QFile f( filename ); if ( !f.open( IO_WriteOnly ) ) { statusBar()->message( QString("Could not write to %1").arg(filename), 2000 ); return; } QTextStream t( &f ); t << text; f.close(); MultiLine->setEdited( FALSE ); GVA-ELAI-UPM 29 Vladimir del Corte Informe sobre librerías Qt setCaption( filename ); statusBar()->message( QString( "File %1 saved" ).arg( filename ), 2000 ); } void VentanaPrincipal::guardarcomo() { QString fn = QFileDialog::getSaveFileName( QString::null, QString::null, this ); if ( !fn.isEmpty() ) { filename = fn; guardar(); } else { statusBar()->message( "Error!. Abortado", 2000 ); } } void VentanaPrincipal::closeEvent( QCloseEvent* ce ) { if ( !MultiLine->edited() ) { ce->accept(); return; } switch( QMessageBox::information( this, "Ejemplo de aplicación Qt", "El documento ha sido modificado ", "Guardar", "Cancel", "Abandonar", 0, 1 ) ){ case 0: guardar(); ce->accept(); break; case 1: default: // just for sanity ce->ignore(); break; case 2: ce->accept(); break; } } void VentanaPrincipal::about() { QMessageBox::about( this, "Ejemplo de aplicación Qt", "Este es un ejemplo de ventana principal con " "\n librerías Qt."); } void VentanaPrincipal::aboutQt() { QMessageBox::aboutQt( this, "Ejemplo de aplicación Qt" ); } GVA-ELAI-UPM 30 Vladimir del Corte Informe sobre librerías Qt En primer lugar se incluyen son los includes necesarios y se declaran una serie de cadenas de texto que se utilizaran en la función “Que es esto?”(Whats This). Después se implementa el constructor de la clase de la aplicación, en este constructor se puede ver que se llama a cuatro funciones miembro declaradas como private en el archivo de extensión .h. A continuación a explicar brevemente que hace cada una de estas funciones. La función iniciarvista(); se encarga de crear y definir el objeto central de la aplicación que como se ha vista anteriormente se trata de un objeto de la clase QMultiLineEdit. void VentanaPrincipal::iniciarvista() { // Se crea y define el objeto principal ( es decir la multiLineEdit ) MultiLine = new QMultiLineEdit( this, "editor" ); MultiLine->setFocus(); setCentralWidget( MultiLine ); // Se establece el objeto como objeto principal de la aplicación resize( 600, 600 ); // Se define su tamaño } La siguiente función a la que se llama es iniciarActions(); aquí se definen las acciones que posteriormente se añadiran a los menús popup. A continuación se comentaran un ejemplo en detalle. En primer lugar se crea la acción definida anteriormente como puntero en el archivo .h. archivoAbrirAction = new QAction( "Abrir archivo", QPixmap( fileopen ), "&Abrir archivo", CTRL+Key_A, this, "abrir" ); Al constructor de la QAction se le pasan como argumentos una cadena de texto como identificación, el icono que acompaña a la acción, el texto que aparecerá en el menu desplegable, la secuencia de teclas para activar la acción, un puntero al objeto padre ( en este caso es this por que se trata del objeto de la clase en que se esta definiendo la acción) y finalmente se le pasa una cadena de texto para definir el nombre. En la siguiente instrucción se conecta la acción anterior con el slot que se desea, en este caso la señal que manda la acción es activated() y se conecta con el slot abrir() este slot se definirá en la clase de la aplicación, por ello nuevamente aparece el puntero this . connect(archivoAbrirAction, SIGNAL( activated() ) , this, SLOT( abrir() ) ); Para definir el icono que acompañara a la acción: QMimeSourceFactory::defaultFactory()->setPixmap( "fileopen", QPixmap( fileopen ) ); Finalmente se define el texto que aparecerá cuando se aplique la función “Que es esto?” sobre esta acción. archivoAbrirAction->setWhatsThis( TextoAbrirArchivo ); GVA-ELAI-UPM 31 Vladimir del Corte Informe sobre librerías Qt La siguiente función es iniciarMenuBar(), en esta función se crean los menús popup y se añaden las acciones correspondientes a cada uno de ellos. A continuación se puede ver el ejemplo del menu Archivo. En primer lugar se crea el menu popup y se añade a la barra de menús archivo = new QPopupMenu( this ); menuBar()->insertItem( "&Archivo", archivo ); En segundo lugar se añaden las acciones al menu, podemos señalar que la función addTo es una función miembro de la clase QAction que sirve para añadir la acción indicada al objeto que se pasa como argumento a la función, en este caso al menu popup llamado archivo. archivoNuevoAction->addTo( archivo); archivoAbrirAction->addTo(archivo); archivoGuardarAction->addTo(archivo); archivoGuardarcomoAction->addTo(archivo); archivo->insertSeparator(); archivoCerrarAction->addTo(archivo); archivoSalirAction->addTo(archivo); La siguiente función es iniciarToolBar() en esta función se define la barra de herramientas y se añaden las acciones que disponen de iconos. Además se configura la barra de herramientas para la función “Que es esto?”(Whats this). void VentanaPrincipal::iniciarToolBar() { // se crea la barra de herramientas TOOLBAR QToolBar* fileTools = new QToolBar( this, "Barra de Herramientas" ); fileTools->setLabel( tr( "Barra de Herramientas" ) ); archivoAbrirAction->addTo( fileTools );//Se añaden las acciones archivoGuardarAction->addTo( fileTools ); (void)QWhatsThis::whatsThisButton( fileTools ); } La ultima función a la que se llama desde el constructor de la clase VentanaPrincipal es iniciarStatusBar(), esta función inicializa la barra de estado al comenzar la aplicación con el mensaje “ Listo”. void VentanaPrincipal::iniciarStatusBar() { // Iniciación de la barra de estado STATUSBAR statusBar()->message( "Listo,"); } A continuación se explicará la implementación de algunos de los Slots asociados a cada una de las acciones de los menús popup. En primer lugar se encuentra el slot nuevoDoc() este slot esta asociado a la acción Nuevo del menu Archivo, este slot se encarga de abrir una nueva aplicación. Lo que hace es crear un nuevo objeto de la clase VentanaPrincipal, es decir crea una nueva aplicación, una vez que crea el objeto se muestra por pantalla con la función miembro show(). La implementación de este slot se puede ver a continuación. GVA-ELAI-UPM 32 Vladimir del Corte Informe sobre librerías Qt void VentanaPrincipal::nuevoDoc() { VentanaPrincipal *ed = new VentanaPrincipal; ed->show(); } Los siguientes slots a comentar son los siguientes abrir() y abrir( const char *fileName ) ambos están relacionados. La implementación del slot abrir() es la siguiente: void VentanaPrincipal::abrir() //abre un archivo existente { QString fn = QFileDialog::getOpenFileName( QString::null, QString::null, this); if ( !fn.isEmpty() ) abrir( fn ); else //si no se especifica el nombre del archivo aparece un mensaje de error statusBar()->message( " No se seleccionó ningún archivo ", 2000 ); } La instrucción QFileDialog::getOpenFileName( QString::null, QString::null, this); abre una ventana de navegador de archivos para seleccionar un archivo para abrir, y devuelve un puntero que es una cadena de caracteres que se corresponde con el nombre del archivo seleccionado. Si no se especifica ningún archivo sobre la barra de estado aparecerá el mensaje “ No se seleccionó ningún archivo”, ( statusBar()->message( " No se seleccionó ningún archivo ", 2000 );), si por el contrario se seleccionó algún archivo se llama a la función abrir de nuevo pasándole como argumento el puntero del archivo(abrir( fn );). El siguiente slot abrir(const char *fileName ) se encarga de abrir el archivo especificado, su implementación es la siguiente: void VentanaPrincipal::abrir( const char *fileName )//Se abre el archivo de nombre ... { QFile f( fileName ); if ( !f.open( IO_ReadOnly ) ) return; filename = fileName; MultiLine->setAutoUpdate( FALSE ); MultiLine->clear(); QTextStream t(&f); while ( !t.eof() ) { QString s = t.readLine(); MultiLine->append( s ); } f.close(); MultiLine->setAutoUpdate( TRUE ); MultiLine->repaint(); MultiLine->setEdited( FALSE ); setCaption( fileName ); QString s; s.sprintf( "Documento listo %s", fileName ); statusBar()->message( s, 2000 ); } GVA-ELAI-UPM 33 Vladimir del Corte Informe sobre librerías Qt Los siguientes a comentar slots son guardarcomo() y guardar(), estos al igual que los anteriores están relacionados. Cuando se llama al slot guardarcomo() se abre de nuevo una ventana de explorador de archivos y cuando se selecciona el nombre del archivo se llama al slot guardar(). La implementación de estos slots se presenta a continuación. void VentanaPrincipal::guardar() { if ( filename.isEmpty() ) { guardarcomo(); return; } QString text = MultiLine->text(); QFile f( filename ); if ( !f.open( IO_WriteOnly ) ) { statusBar()->message( QString("Could not write to %1").arg(filename), 2000 ); return; } QTextStream t( &f ); t << text; f.close(); MultiLine->setEdited( FALSE ); setCaption( filename ); statusBar()->message( QString( "File %1 saved" ).arg( filename ), 2000 ); } void VentanaPrincipal::guardarcomo() { QString fn = QFileDialog::getSaveFileName( QString::null, QString::null, this ); if ( !fn.isEmpty() ) { filename = fn; guardar(); } else { statusBar()->message( "Error!. Abortado", 2000 ); } } Otro slot importante a comentar es closeEvent( QcloseEvent* ce), esta función es una función miembro de la clase QWidget, como la clase de la aplicación(VentanaPrincipal) deriva de la clase QmainWindow y esta a su vez deriva de QWidget heredara esta función miembro. Esta función es llamada cuando se cierra la aplicación y lo que se hace en esta clase es reimplementarla de manera que cuando se cierra la aplicación (el editor de texto) se llame a esta función y se compruebe si se ha modificado el argumento, si es así aparecerá una ventana de diálogo con las opciones Guardar (para guardar los cambios en el documento), Cancelar (para cancelar la acción) y Abandonar (para cerrar la aplicación sin guardar los cambios). GVA-ELAI-UPM 34 Vladimir del Corte Informe sobre librerías Qt void VentanaPrincipal::closeEvent( QCloseEvent* ce ) { if ( !MultiLine->edited() ) { //Se comprueba si ha habido cambios en el documento ce->accept(); //Si no los ha habido se cierra la aplicación. return; } //Si ha habido cambios aparece una ventana de dialogo que informa que el documento ha sido //modificado y presenta las tres opciones switch( QMessageBox::information( this, "Ejemplo de aplicación Qt", "El documento ha sido modificado ", "Guardar", "Cancel", "Abandonar", 0, 1 ) ) { case 0: guardar(); ce->accept(); break; case 1: default: // just for sanity ce->ignore(); break; case 2: ce->accept(); break; } // Opción guardar //Se llama al slot guardar // Opción Cancelar // Se ignora la acción // Opción Abandonar // Se cierra la aplicación } Por ultimo comentar cabe los slot asociados a las acciones del menu Ayuda, estos slots lo que hacen es abrir una ventana de dialogo con información para el usuario. void VentanaPrincipal::about() { QMessageBox::about( this, "Ejemplo de aplicación Qt", "Este es un ejemplo de ventana principal con " "\n librerías Qt."); } void VentanaPrincipal::aboutQt() { QMessageBox::aboutQt( this, "Ejemplo de aplicación Qt" ); } GVA-ELAI-UPM 35 Vladimir del Corte Informe sobre librerías Qt 5. DIAGRAMA DE CLASES. En este diagrama de clases realizado en lenguaje UML se puede ver de una forma clara las relaciones que existen entre las principales clases de las librerías Qt utilizadas para la elaboración de las aplicaciones de este informe. Qt QObject QApplication QWidget QAction QLineEdit QToolBar QButton QStatusBar QMainWindow QDialog 6. INSTALACIÓN DE Qt. 6.1 Instalación Qt/Windows. La distribución Qt/Windows esta distribuida como un archivo .exe autoextraíble y autoinstalable, bastará con ejecutarlo y seguir las instrucciones del programa asistente. Una vez hecho esto lo siguiente es la instalación de tmake, esta herramienta viene distribuida como un archivo comprimido .zip, bastará con descomprimirlo en el directorio Qt. Por último el paso más importante es declarar las variables de entorno $QTDIR y TMAKEPATH, e incluir en el PATH del sistema los directorios de los includes de las librerías estáticas y dinámicas, y el directorio de las aplicaciones moc,uic y tmake. Para hacer esto hay que seguir los siguientes pasos: En sistemas con Windows 9x/Milenium basta con incluir las siguientes líneas en el archivo Autoexec.bat SET TMAKEPATH=C:\QT\tmake\lib\win32-msvc SET QTDIR=C:\QT SET PATH=C:\QT\BIN;C:QT\INCLUDE;C:QT\LIB;C:\QT\TMAKE\BIN; GVA-ELAI-UPM 36 Vladimir del Corte Informe sobre librerías Qt ( En este caso el directorio Qt es C:\Qt) En sistemas Windows NT/ 2000, se tiene la opción de abrir el panel de control y se pincha en sistema, una vez hecho esto se abrirá una ventana de diálogo, se pincha en la lengüeta de entorno y aquí se podrá declarar dichas variables para cada usuario. Tambien podemos realizar esto desde la linea de comandos de DOS introduciendo las siguientes instrucciones. set TMAKEPATH=C:\QT\tmake\lib\win32-msvc set QTDIR=C:\QT set PATH=C:\QT\BIN;C:QT\INCLUDE;C:QT\LIB;C:\QT\TMAKE\BIN; 6.1 Instalación Qt/X11. Dependiendo de los permisos donde se valla a instalar Qt, puede ser necesario entrar como root del sistema. • La distribución Qt/11 viene distribuida en un archivo .tar.gz, el primer paso será pues desempaquetar el archivo, por ejemplo en el directorio /usr/local, esto es: cd /usr/local gunzip qt-x11-version.tar.gz tar xf qt-x11-version.tar # uncompress the archive # unpack it Esto creará el directorio /usr/local/qt-version que contendrá los archivos contenidos en el archivo principal. Se puede renombrar el directorio de qtversion a qt con la instrucción. mv qt-version qt • El siguiente paso es declarar una serie de variables de entorno en el archivo .profile (o .login, dependiendo del shell que se esta utilizando) en el directorio /home de cada usuario. (Si no existe este archivo habrá que crearlo.). En ocasiones cuando se utiliza el shell bash es posible encontrar el archivo .bash_profile en lugar del archivo .profile. Estas variables son: QTDIR donde se instalaron Qt, PATH para localizar herramientas como moc y otras, MANPATH para accerder a las paginas del manual Qt, LD_LIBRARY_PATH para las librerías compartidas Qt. Para declarar estas variables: En el archivo .profile (si el shell es bash, ksh, zsh or sh), se añaden las siguientes lineas: QTDIR=/usr/local/qt PATH=$QTDIR/bin:$PATH MANPATH=$QTDIR/man:$MANPATH LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH GVA-ELAI-UPM 37 Vladimir del Corte Informe sobre librerías Qt export QTDIR PATH MANPATH LD_LIBRARY_PATH Nota importante : En ocasiones puede ser necesario incluir estas lineas en el archivo /etc/.profile (como root) en lugar de hacerlo en el archivo ./bash_profile del directorio /home de cada usuario. En el archivo .login (en el caso de que el shell sea csh or tcsh), hay que añadir las siguientes lineas: setenv setenv setenv setenv QTDIR /usr/local/qt PATH $QTDIR/bin:$PATH MANPATH $QTDIR/man:$MANPATH LD_LIBRARY_PATH $QTDIR/lib:$LD_LIBRARY_PATH Después de hacer esto será necesario reiniciar la sesión, de este modo las variables estarán declaradas correctamente, si no es asi al continuar con la instalación ocurrirá un error. • Instalación del archivo de licencia. Para la versión free edition, no es necesario ningún archivo de licencia. Para las versiones Professional y Enterprise , instalar el archivo de licencia como se describe en cada una de las distribuciones. • Construyendo (Building). Este paso compila las librerías Qt, y construye los programas ejemplo, el tutorial, y las herramientas (ejemplo Designer). La instrucción es: ./configure Este paso configurará la librería Qt.. Si se quiere conseguir una lista completa de las opciones de ./configure, se puede teclear ./configure -help . En siguiente paso creará la librería, compilará los ejemplos y el tutorial. Make Si la plataforma sobre la que estamos trabajando no es compatible, convendrá echar un vistazo a PORTING. Si la plataforma es soportada pero ocurren problemas será mejor echar un vistazo a <http://www.trolltech.com/platforms/> • En algunos casos llegado a este punto puede ser necesario ejecutar /sbin/ldconfig si se están usando librerías compartidas. Si se tienen problemas como este ejecutando los programas de ejemplo can't load library 'libqt.so.2' Probablemente será necesario poner una referencia a la libreria Qt en un archivo de configuración y ejecutar /sbin/ldconfig como root. Sin olvidar declarar la variable LD_LIBRARY_PATH como se explicó en el paso 2. GVA-ELAI-UPM 38 Vladimir del Corte • Informe sobre librerías Qt Por último la documentación html online esta instalada en /usr/local/qt/doc/html/, la página principal es /usr/local/qt/doc/html/index.html. Las páginas man están instaladas en /usr/local/qt/doc/man/ GVA-ELAI-UPM 39 Vladimir del Corte Informe sobre librerías Qt 7. BIBLIOGRAFIA. • www.troltech.com : En esta web se puede encontrar desde tutoriales y manuales para iniciarse en al uso de las librerías Qt, hasta aplicaciones que se podran tomar como ejemplo, y por supuesto desde aquí se puede descargar las librerías Qt en sus versiones no comerciales así como tmake. • Libros sobre Qt: http://www.trolltech.com/developer/documentation/literature.html GVA-ELAI-UPM 40