Desarrollo de Interfaces Grácas de Usuario con GTKmm Desarrollo de Interfaces Grácas de Usuario con GTKmm Software Freedom Day, Bogotá Hugo Franco Ubuntu-CO, Bioingenium (U.N.) 17 de septiembre de 2009 Desarrollo de Interfaces Grácas de Usuario con GTKmm Diseño de interfaces grácas de usuario bajo el enfoque orientado a objetos 1 Diseño de interfaces grácas de usuario bajo el enfoque orientado a objetos 2 Usabilidad/Accessibilidad/Guías de diseño 3 Nociones de Autotools 4 Estructura de la API GTKmm 5 Taller Generación de la aplicación básica en el EID Anjuta Uso de controles de disposición y posicionamiento Adición de funcionalidad a controles básicos Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Desarrollo de Interfaces Grácas de Usuario con GTKmm Diseño de interfaces grácas de usuario bajo el enfoque orientado a objetos Diseño de Interfaces grácas de usuario mediante ADOO Enfoque DocumentoVista Abstracción de la ventana padre para dar funcionalidad a la aplicación Uso intensivo de la herencia Desarrollo de Interfaces Grácas de Usuario con GTKmm Usabilidad/Accessibilidad/Guías de diseño Lineamientos a tener en cuenta en el diseño de una GUI Líneas guía de GNOME en http://library.gnome.org/devel/hig-book/stable/ Diseñar para la gente (usuario antes que desarrollador) No limitar el público objetivo (accesibilidadidioma) Relacionar la aplicación con el mundo real Ser consistente en las estructuras de la GUI Mantener informado al usuario (alertas, noticaciones) Mantener las cosas simples (y agradables) KISS Poner el usuario al mando Perdonar al usuario (deshacer, guardar automáticamente) Proveer manipulación directa Desarrollo de Interfaces Grácas de Usuario con GTKmm Nociones de Autotools Herramientas de desarrollo en *NIX Autotools Tutorial completo en http://www.seul.org/docs/autotut Organizar archivos del código fuente Crear archivos de conguración congure.in Makele.am Ejecutar aclocal Ejecutar autoheader Ejecutar autoconf Ejecutar automake (generalmente con la opción −−add−missing) Listo para usar la secuencia ./congure make sudo make install Desarrollo de Interfaces Grácas de Usuario con GTKmm Estructura de la API GTKmm Estructura de la API GTKmm Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Generación de la aplicación básica en el EID Anjuta Creación del esqueleto de la aplicación 1 2 3 4 5 Abrir el Anjuta Crear un proyecto C++→ escoger la opción gtkmm Seleccionar carpeta del proyecto en /home/ubuntu/Proyectos/nombre_proyecto Editar el archivo src/main.cc para quitar el subdirectorio de la macro GLADE_FILE Construir el proyecto: generar con la conguración "predeterminado" src Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Generación de la aplicación básica en el EID Anjuta Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Generación de la aplicación básica en el EID Anjuta Creación de la clase de la ventana principal Ir al menú Archivo→Nuevo→Clase C++/GObject Establecer nombre de la clase, miembros y atributos (derivada de Gtk::Window) Seleccionar objetivo (target) al que está asociada la clase (nombre del proyecto) y aceptar (crea los archivos fuente) Añadir gtkmm.h a archivo de cabecera de la clase Añadir llamado del constructor de la clase base en el constructor de la clase derivada (Parent) Añadir archivo de cabecera a la función principal Modicar el código de la función principal (main.cc) para usar la clase derivada y no la obtenida mediante glade (borrar bloque trycatch del glade). Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Generación de la aplicación básica en el EID Anjuta Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Uso de controles de disposición y posicionamiento Las Clases Gtk::HPaned y GTK::VPaned Añadir un Gtk::HPaned (a la aplicación en la declaración de la clase Parent Crear controles arbitrarios en el constructor del Parent Gtk::Button *btn=new Gtk::Button("Botón de Prueba"); Gtk::Label *label=new Gtk::Label("Muestra de una etiqueta"); Añadir el Paned a la ventana para que se dibuje: add(m_Paned); Agregar los controles creados al Paned m_Paned.add1(*btn); m_Paned.add2(*label); Repintar el Parent (control principal) show_all_children(); Ejercicio: cambiar HPaned por VPaned y ver el resultado, cambiar VPaned por HBox y VBox, añadiendo un control Gtk::Entry, Gtk::CheckBox, Gtk::RadioButton Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Uso de controles de disposición y posicionamiento Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Uso de controles de disposición y posicionamiento Contenedores anidados y Grupos I Volver a HPaned. Añadir dos Gtk::VBox en la declaración de Parent Gtk::VBox m_RadioBox; Gtk::VBox m_ButtonBox; Crear un grupo de radio y varios botones de radio en el constructor de la clase Parent antes de añadir el Paned Gtk::RadioButton::Group rbGrp; Gtk::RadioButton *rb1=new Gtk::RadioButton(rbGrp,"1a. opción"); Gtk::RadioButton *rb2=new Gtk::RadioButton(rbGrp,"2a. opción"); Gtk::RadioButton *rb3=new Gtk::RadioButton(rbGrp,"3a. opción"); Añadir, tras el Paned, los RadioButtons a la primera VBox m_RadioBox.add(*rb1); m_RadioBox.add(*rb2); m_RadioBox.add(*rb3); Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Uso de controles de disposición y posicionamiento Contenedores anidados y Grupos II Cambiar el botón y la etiqueta a la segunda caja vertical m_ButtonBox.add(*btn); m_ButtonBox.add(*label); Por último, añadir de forma anidada cada box a una de las mitades del Paned m_Paned.add1(m_RadioBox); m_Paned.add2(m_ButtonBox); Ejercicio: añadir más cajas con otros controles, cambiar el orden, anidar diferentes tipos de contenedores Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Uso de controles de disposición y posicionamiento Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Adición de funcionalidad a controles básicos Controles básicos I Uso de sigc::memfun con Gtk::Button Declarar en el constructor de la clase Parent una función handler de evento: void onButtonClicked() Conectar la señal de click sobre el botón a la función handler mediante btn->signal_clicked().connect( sigc::mem_(*this,&Parent::onButtonClicked)); La función signal_clicked, perteneciente al widget, devuelve la señal asociada al evento click A su vez, la función connect de dicha señal recibe el puntero a la función encargada de manejar el evento click mediante sigc::memfun (libsigc++ 2.0) , cuyos parámetros son: referencia al objeto que manejará el evento puntero a la función handler dentro de la clase a la que pertenece ese objeto Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Adición de funcionalidad a controles básicos Controles básicos: Gtk::Button II Uso de Gtk::Label y Diálogos (Gtk::MessageDialog) Convertir el label en el constructor en un atributo de la clase parent (incluir Gtk::Label *m_label en el archivo parent.h) Denición de la función handler del evento asociado al botón void Parent::onButtonClicked(){ Gtk::MessageDialog md(*this,"¾Desea cerrar este ejemplo?", false,Gtk::MESSAGE_QUESTION,Gtk::BUTTONS_OK_CANCEL); int salida=md.run(); switch(salida){ case(Gtk::RESPONSE_OK): hide();//oculta ventana principal→cierra la aplicación break; default: //Gtk::RESPONSE_CANCEL m_label->set_text("Se presionó \"Cancelar\"."); break; }//switch }//onButtonClicked Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Adición de funcionalidad a controles básicos Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Creación de la ventana principal: Menú Glib::RefPtr<Gtk::UIManager> y Glib::RefPtr<Gtk::ActionGroup> La parte más truculenta Tomar ejemplo inicial que incluye la clase Parent, en limpio Denir título y tamaño: funciones set_title y set_default_size Agregar en la declaración de la clase Parent un atributo de tipo Gtk::VBox, además de un Gtk::Notebook y una Gtk::StatusBar Agregar también un UI Manager (Glib::RefPtr<Gtk::UIManager>) y un Action Group (Glib::RefPtr<Gtk::ActionGroup>) Adicionar en la parte pública de la declaración de la clase las funciones handler de los eventos de menú: void onAbrir(), void onCerrar(), void onSalir() Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Apariencia de la clase Parent class Parent: public Gtk::Window { public: Parent(); ~Parent(); void onSalir(); void onAbrir(); void onCerrar(); void crearMenu(); protected: Gtk::VBox m_vBox; Gtk::Notebook *m_pClientArea; Gtk::Statusbar *m_pSBar; Glib::RefPtr<Gtk::UIManager> m_refUIManager; Glib::RefPtr<Gtk::ActionGroup> m_refActionGroup; private: }; Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Funcionalidad del menú I Denir la función crearMenu usando los atributos actionGroup y UIManager void Parent::crearMenu(){ m_refActionGroup = Gtk::ActionGroup::create(); m_refActionGroup->add(Gtk::Action::create("FileOpen", Gtk::Stock::OPEN, "_Abrir", "Abre un archivo"), sigc::mem_fun(*this, &Parent::onAbrir)); m_refActionGroup->add(Gtk::Action::create("FileClose", Gtk::Stock::CLOSE, "_Cerrar", "Cierra un archivo"), sigc::mem_fun(*this, &Parent::onCerrar)); m_refActionGroup->add(Gtk::Action::create("FileQuit", Gtk::Stock::CLOSE, "_Salir", "Termina la aplicación"), sigc::mem_fun(*this, &Parent::onSalir)); m_refActionGroup->add(Gtk::Action::create("FileMenu", "_Archivo")); m_refUIManager = Gtk::UIManager::create(); m_refUIManager->insert_action_group(m_refActionGroup); add_accel_group(m_refUIManager->get_accel_group()); m_refUIManager->add_ui_from_string(ui_info); Gtk::Widget* pMenubar = m_refUIManager->get_widget("/MenuBar"); if(pMenubar) m_vBox.pack_start(*pMenubar, Gtk::PACK_SHRINK); } Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Descripción física del menú Creación de una cadena ui_info (XML) Añadir la conguración de los elementos y las acciones en la barra de menú con sintaxis XML (en este caso, el menú Archivo, declarado en el archivo de cabecera: const Glib::ustring ui_info = "<ui>" " <menubar name='MenuBar'>" " <menu action='FileMenu'>" " <menuitem action='FileOpen'/>" " <menuitem action='FileClose'/>" " <menuitem action='FileQuit'/>" " </menu>" " </menubar>" "</ui>"; Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. La clase Gtk::Notebook I Referirse al paso 6 del taller en: /home/ubuntu/taller/taller-gtkmm6 Añadir el notebook a la ventana Padre y activar el desplazamiento; es necesario crear los controles que hijos del notebook (tabs). Los childs naturales de un notebook son ventanas o sus clases derivadas: m_pClientArea=new Gtk::Notebook; m_pClientArea->set_scrollable(); Gtk::ScrolledWindow *sw=new Gtk::ScrolledWindow; Gtk::TextView *tv=new Gtk::TextView; sw->add(*tv); Tras hacer las operaciones de carga del documento en la clase de control (en este caso, Gtk::TextView), se añade el child al notebook usando un label (texto en la pestaña) y la ventana Gtk::Label l(fName); int npos=m_pClientArea->append_page(*sw->get_ancestor(GTK_TYPE_SCROLLED_WINDOW), l); sw->show(); m_pClientArea->show_all_children(); m_pClientArea->set_current_page(npos); show_all_children(); Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. La clase Gtk::Notebook II Cierre seguro de Tabs (childs) Para evitar errores de punteros internos, es recomendable usar los índices (únicos) de los hijos de un notebook: void Parent::onCerrar() { int nPag=m_pClientArea->get_current_page(); if(nPag>=0) { Gtk::Widget *wgt=m_pClientArea->get_nth_page(nPag); m_pClientArea->remove_page(*wgt); m_pClientArea->queue_draw(); } } Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Aplicación hasta el momento Desarrollo de Interfaces Grácas de Usuario con GTKmm Taller Implementación sencilla de aplicaciones con interfaz de múltiples documentos. Notas nales Usar Gtk::manage(...) para crear widgets que no estarán almacenados en atributos de las clases; esto permite usar el recolector de basura de la librería al terminar la aplicación Se debe preferir siempre la implementación GtkGlib pues contiene pasos de inicialización y manejo de punteros no explícitos.