APRENDA  Qt4  DESDE  HOY  MISMO  

 
Anónimo  
Octubre  de  2010  (versión  de  distribución  libre)  

           

 

2  

 

 

3  

    APRENDA  QT4  DESDE  HOY  MISMO  
    Este   pretende   ser   el   primer   libro   en   español   del   mejor   Framework   de   desarrollo   multiplataforma   de   todos   los   tiempos,   y   que   cada   día   nos   brinda   nuevas   posibilidades.   Es   una   lástima   pero   a   día   de   hoy   no   hay   ninguna   traducción,   ni   libro   dedicado   seriamente   a   Qt   4   en   nuestro  idioma,  por  lo  que  me  dispuse  a  cambiar  esta  situación.     El   enfoque   que   vamos   a   usar   es   por   supuesto   desde   C++,   y   por   lo   tanto   es   importante   poseer   de   antemano   unos   conocimientos   previos   de   este   lenguaje.   El   nivel   necesario   para   comprender  este  libro  lo  completa  perfectamente  el  libro  de  Bjarne  Stroustrup  ,  “El  lenguaje  de   programación  C++”  publicado  en  español  por  Addison-­‐Wesley  en  2001  (ISBN:  84-­‐7829-­‐046-­‐X).     C++  es  un  lenguaje  que  ha  evolucionado  mucho  desde  que  fue  creado  en  1983  por  Bjarne   Stroustrup   en   los   laboratorios   AT&T,   donde   también   nació   su   predecesor   C,   unos   diez   años   antes   (1972),   de   la   mano   de   Dennis   Ritchie.   El   lenguaje   C,   que   había   nacido   para   sustituir   al   ensamblador   en   el   desarrollo   de   sistemas   operativos,   y   así   poder   portarlos   más   fácilmente   a   otras  plataformas  físicas,  y  así  se  reescribió  completamente  UNIX  de  los  Laboratorios  Bell  creado   en  1969.  C++  surgió  como  una  necesidad  de  añadir  nuevos  paradigmas  a  los  ya  incorporados  por   C,   pero   sin   perder   la   potencia   del   mismo,   al   poder   acceder   al   hardware   de   manera   sencilla.   Así   C++  añadió  el  lenguaje  orientado  a  objetos,  por  ello  se  le  conoce  como  C  con  clases.  Sin  embargo   cuando   alguien   que   viene   de   C,   comienza   a   estudiar   C++,   debe   de   evitar   pensar   que   es   una   extensión   del   lenguaje   C   lo   que   está   estudiando,   ya   que   C++   es   mucho   más   que   eso.   C++,   se   ha   desvinculado   de   muchas   prácticas   habituales   en   C   que   eran   motivo   de   múltiples   fallos   en   la   programación,   como   el   casting   incontrolado,   el   uso   de   la   memoria,   de   los   punteros,   el   paso   de   parámetros   por   referencia,   incorporación   de   elementos   muy   útiles   como   las   Colecciones   o   Contenedores  y  la  creación  de  una  librería  de  incalculable  valor  para  el  programador  en  C++,  las   STL   (Standard   Template   Libraries).   Aunque   UNIX   y   Linux   fue   creado   desde   C,   otros   operativos   como   Windows,   nacieron   de   C++.   Los   sistemas   con   entorno   gráfico,   eran   mucho   más   sencillos   de   desarrollar   usando   el   paradigma   del   lenguaje   orientado   a   objetos,   que   la   mera   programación   estructurada  de  C.     En   1991,   dos   programadores   noruegos   (Eirik   Eng   y   Haavard   Nord   de   Quasar   Technologies,  más  tarde  Trolltech)  crearon  un  Framework  en  C++  que  permitiera  usar  el  mismo   código  en  el  desarrollo  de  una  GUI  (interfaz  gráfica)  tanto  para  Windows  como  para  UNIX/X11.   Inicialmente  era  código  abierto,  pero  la  licencia  no  era  libre.  Entre  1995  y  1998  se  desarrolló  con   este  Framework  un  escritorio  para  GNU/Linux  que  tuvo  mucho  éxito,  llamado  KDE  que  hoy  sigue   siendo  uno  de  los  escritorios  más  usados  de  Linux.  GNU  miraba  con  recelo  que  el  escritorio  más   usado   en   Linux   no   fuera   libre,   así   que   hubo   una   reunión   entre   los   desarrolladores   de   KDE   y   Trolltech,  para  asegurar  el  futuro  de  Qt  en  una  liberación  bajo  licencia  BSD.  Entre  los  años  2000  y   2005   se   produjo   la   liberación   bajo   licencia   GPL   de   las   versiones   de   Qt   para   Windows,   Linux   y   Macintosh.   En   Junio   de   2008,   Nokia   se   aseguró   el   desarrollo   para   sus   móviles   tanto   Symbian   como   Maemo   (proyecto   que   al   final   se   ha   fusionado   con   Moblin   de   Intel,   para   crear   MeeGo,   y   competir  con  Android  de  Google)  con  la  adquisición  de  Qt.   Actualmente   a   finales   de   2010,   Qt   acaba   de   salir   en   la   versión   4.7   donde   se   añaden   nuevas   características   extraordinarias   para   la   creación   de   diseños   de   front-­‐ends   para   dispositivos   móviles   y   electrodomésticos,   con   la   entrada   de   QML   (Qt   Declarative)   un   lenguaje   declarativo   para   crear   interfaces   gráficos   muy   vistosos.     Qt   ya   ha   sido   portado   y   soportado   por   Nokia   para   las   siguientes   plataformas:   Linux/X11   (y   otros   UNIX,   como   Solaris,   AIX,   HP-­‐UX,   IRIX),   Windows,   Macintosh,   Embedded   Linux,   Windows   CE/Mobile,   Symbian   y   MeeGo.   Y   con   desarrollo   externo  se  ha  portado  o  se  está  portando  a  plataformas  como:  Open  Solaris,  Haiku(BeOS  libre),   OS/2(Plataforma  eCS  de  IBM),  iPhone  y  Android.  Como  podemos  apreciar,  Qt  es  un  Framework   que   no   para   de   crecer,   y   el   secreto   de   este   éxito,   es   no   sólo   su   potencia   y   velocidad   frente   a   otras   alternativas   como   Java   (donde   es   entre   3   y   6   veces   más   rápido),   si   no   porque   es   atractivo   para   el   programador  y  rápido  para  desarrollar,  desde  la  salida  de  QtCreator  2.0,  el  entorno  IDE  para  Qt.  

 

4  

 
Qt  incorpora  un  estilo  de  programación  a  C++,  que  le  hace  menos  vulnerable  a  los  fallos   de   programación,   sobre   todo   en   el   manejo   de   memoria   (memory   leaks),   y   que   lo   hacen   muy   agradable  de  cara  al  desarrollador.     ¿Qué  vamos  a  aprender  en  este  libro?.  Este  libro  pretende  ser  el  primer  escalón  a  subir   para   alguien   de   habla   hispana   que   quiera   aprender   a   desarrollar   aplicaciones   multiplataforma   con  Qt4.  Su  contenido  ha  sido  escogido,  para  poder  cubrir  el  desarrollo  de  cualquier  aplicación  de   propósito   general.   Cualquiera   que   complete   el   contenido   de   este   libro   con   sus   ejercicios   y   ejemplos,   podrá   desarrollar   este   tipo   de   aplicaciones   sin   grandes   problemas.   Sin   embargo,   Qt   crece   todos   los   días,   añadiendo   nuevas   posibilidades,   nuevos   módulos   y   nuevas   clases,   y   a   día   de   hoy   es   tan   extenso   que   ningún   libro   en   ningún   idioma   contiene   una   descripción   completa   del   mismo.   Por   lo   que   la   única   fuente   donde   podemos   recoger   esa   información,   es   la   misma   ayuda   que   el   SDK   del   Framework   nos   proporciona   con   su   asistente   (Qt   Assistant),   pero   esta   documentación  está  escrita  en  inglés  únicamente.  Es  por  tanto  necesario  poder  leer  y  ser  capaz   de  desenvolverse  en  este  idioma,  para  usar  toda  la  capacidad  que  Qt  nos  pueda  ofrecer.     Por  lo  tanto,  este  libro  consigue  sentar  unas  bases  claras  en  el  conocimiento  de  Qt4,  pero   para  ir  más  allá  es  necesario  saber  usar  la  documentación  en  inglés  que  acompaña  a  toda  versión   del  Framework.  Los  temas  están  ordenados  de  manera  progresiva,  para  que  nadie  se  pierda.  El   lenguaje   usado   es   sencillo   y   directo,   por   lo   que   el   contenido   de   ningún   tema   excede   las   8   páginas   de   extensión,   y   por   tanto   puede   cubrirse   en   un   solo   día.   Acompañando   a   estos   temas,   está   el   código  de  los  ejemplos,  y  los  ejercicios  resueltos,  que  nos  complementan  lo  aprendido.  No  pase   de   un   tema   al   siguiente,   sin   leer   todo   este   código,   comprenderlo,   e   intentar   hacer   los   ejercicios   que  se  proponen.  Este  libro  propone  una  base  fundamental  para  el  conocimiento  de  Qt  orientado   al   desarrollo   de   aplicaciones   de   propósito   general,   y   sin   dominar   esta   base   sólida,   no   podrá   seguir   avanzando   conforme   a   sus   necesidades   futuras   en   nuevos   aspectos   del   Framework.   Trataremos   por   tanto   todos   los   aspectos   fundamentales   del   desarrollo   de   aplicaciones   GUI   con   todos  sus  componentes  posibles,  capacidades  como  el  dibujo,  impresión  de  documentos,  acceso  a   bases  de  datos,  acceso  a  la  red,  la  programación  concurrente,  acceso  al  sistema  de  ficheros  y  el   uso  de  los  principales  tipos  de  recursos.     Y   lo   mejor   de   todo,   es   que   este   libro   es   libre,   y   se   podrá   distribuir   libremente   y   sin   coste   alguno.   Tratamos   de   beneficiarnos   con   la   creación   de   una   comunidad   de   programadores   en   español,   nutrida   y   prolífica.   Por   todo   ello,   bienvenido   a   esta   maravillosa   aventura   que   es   aprender  C++/Qt4.                            

 

5  

  Índice  de  Contenidos    
1.-­‐  Instalación  del  Qt  SDK     2.-­‐  Compilación                                                                                                                                                                                                                                                                                        7          9      11      15      17      23      27      33      35      39      43      45      47      49      55      61      65      67      69      75      83      89      93      99   105   107   109   111   119  

3.-­‐  Estructura  de  una  aplicación     4.-­‐  La  primera  aplicación  Qt    

5.-­‐  QObject,  metaobjetos  y  propiedades   6.-­‐  El  manejo  de  la  memoria   7.-­‐  Señales  y  Slots   8.-­‐  QSignalMapper                    

9.-­‐  Manejo  de  cadenas  en  Qt   10.-­‐  Colecciones  en  Qt   11.-­‐  Tipos  Qt        

12.-­‐  Sistema  de  ficheros    

13.-­‐  Escritura  y  Lectura  de  ficheros   14.-­‐  Widgets  y  Layouts     15.-­‐  Ventanas  de  Diálogo        

16.-­‐  Modelo,  Vista  y  Controlador.  Introducción.   17.-­‐  QListWidget  y  QTableWidget     18.-­‐  Qt  Creator     19.-­‐  Main  Windows                

20.-­‐  Desarrollo  de  una  aplicación  completa   21.-­‐  Layout  a  fondo   22.-­‐  Bases  de  datos   23.-­‐  Redes  TCP/IP                                

24.-­‐  Programación  concurrente     25.-­‐  Gráficos  e  Impresión     26.-­‐  Recursos  Externos     27.-­‐  Búscate  la  vida   28.-­‐  Instaladores     Bibliografía                    

   

6  

                                                           

 

  En   el   próximo   tema.  Ya  puedes  ir  directo   al  siguiente  tema.   eso   sí.   y   el   Qt   Linguistic   que   nos   ayudará   a   implementar   traducciones  a  otros  idiomas  de  nuestra  aplicación.     Bajarse  el  SDK  de  Internet     Qt  se  puede  empezar  a  conocer  e  incluso  a  usar  únicamente  en  su  versión  Open  Source   (GPL   o   LGPL).  Linux  y  MacOSX.  tenemos  el  Qt  Assistant  que  será   nuestro  asistente  y  ayuda  documental  para  buscar  la  información  que  necesitemos  del  SDK.   antes   de   nada.  y  en  la  subcarpeta  Tools.  Por   ello.  recomendamos  pulsar  en  la  sección  LGPL  y   bajarse  las   versiones  “Complete  Development  Environment”.  El  Qt   Designer   que   nos   servirá   para   diseñar   GUIs.  por  lo  que  vamos  a  ver  como  se  instala   en  los  3  tipos  de  sistemas  más  extendidos  hoy  día.  aparece  el  grupo  “Qt   SDK   by   Nokia   …”.   por   lo   que   antes   de   comenzar   a   recibir   una   ingente  cantidad  de  información  teórica.   en   perfecto   inglés.xx.   mientras   vamos   aprendiendo   nuevos   conceptos.  hay  que  estar  en  la  disposición  de  poder  probar  todo  lo   que  se  recibe  de  una  manera  práctica.  Esta  instalación  lleva  incluído  el   paquete  MinGW  que  comprende  el  compilado  y  herramientas  gcc  .  a  saber:  Windows.exe   (donde   las   xx.nokia.  para  que  así  el  ciclo  didáctico  se  culmine  por  completo.  No  obstante  puedes  ver  que  en  el  menú  de  Aplicaciones.   se   aprende   programando.   donde   podremos   ejecutarlas   pues   ya   vienen   compiladas  para  nuestra  plataforma.     .com/downloads  .  y  seguir  las  instrucciones  por  defecto.     El  sistema  ya  está  listo  y  preparado  para  ser  usado  en  nuestro  curso.   veremos   la   estructura   de   lo   que   nos   ofrece  dicho  IDE.   podemos   ver   herramientas   como   Qt   Demo.         Instalación  en  Windows  32  bits       Una   vez   nos   hemos   bajado   el   fichero   qt-­‐sdk-­‐win-­‐opensource-­‐20xx.  g++  y  make.     Instalación  en  Linux  32  y  64  bits     Aunque   algunas   distribuciones   Linux   pueden   bajarse   de   sus   repositorios   oficiales   una   versión   de   Qt   e   incluso   del   Qt   Designer.   la   última   versión   completa  en  nuestra  distro.   la   cual   podemos   bajar   en   su   última   versión   de   la   web   http://qt.  además   de   muchas   otras   herramientas   de   desarrollo.   nosotros   vamos   a   optar   por   instalar.xx  son  números  referentes  a  año  y  versión  del  empaquetado).   Por   lo   que   es   de   gran   ayuda   al   menos   ser   capaz   de   leer   y   comprender   un   poco   el   inglés   informático.     Solamente  hay  que  ejecutarlo.   y   dentro   de   él.   ejemplos   y   documentación.   donde   se   nos   presenta   una   serie   de   aplicaciones   de   ejemplo.  si  no  también  el  Qt  Creator.  que  es  el  IDE  de  desarrollo.   ya  que  no  sólo  incluyen  el  SDK.       Qt  es  un  Framework  completo  multiplataforma.  Al  final  obtenemos   un   icono   en   el   Escritorio   del   Qt   Creator.7     TEMA  1   INSTALACION  DEL  QT  SDK       A   programar.  que  además  son  las  más  extensas.   vamos   a   prepararnos   un   entorno   de   desarrollo   lo   suficientemente   cómodo   para   poder   acudir   a   él   cada   vez   que   lo   necesitemos.   De  todos  los  archivos  que  allí  se  nos  ofrece.

  buscando   el   fichero   en   /opt/qtsdk-­‐20xx.   como   al   Qt   Creator.     Un  vez  bajados  los  ficheros.   donde   podremos   ejecutarlas   pues   ya   vienen   compiladas   para   nuestra   plataforma.   No   obstante   puedes   obtener   más   información   al   respecto   en   http://doc.   El   Qt   Assistant   que   será  nuestro  asistente  y  ayuda  documental  para  buscar  la  información  que  necesitemos  del  SDK.     Instalación  en  MacOSX     Bajado   el   fichero   qt-­‐sdk-­‐mac-­‐opensource-­‐20xx.  para  Linux  32  bits  o  el  fichero   qt-­‐sdk-­‐linux-­‐x86_64-­‐opensource-­‐20xx.   El  sistema  ya  está  listo  y  preparado  para  ser  usado  en  nuestro  curso.05/bin   Volvemos   a   reiniciar   la   sesión.   sólo   tenemos   que   instalarlo   siguiendo   las   opciones   por   defecto  del  instalador.8       Para   ello   nos   bajamos   el   fichero   qt-­‐sdk-­‐linux-­‐x86-­‐opensource-­‐20xx.  que  en  las  versiones  derivadas  de  Debian  se  hace  con  un  mero   “apt-­‐get   install   build-­‐essential”   y   en   las   basadas   en   Red   Hat   se   hace   con   una   “yum   install   gcc   gcc-­‐ c++  make”.xx/).05/qt/bin:  /opt/qtsdk-­‐2010.   donde   se   nos   presenta   una   serie   de   aplicaciones   de   ejemplo.   El   Qt   Designer   que   nos   servirá   para   diseñar   GUIs.  G++  y  Make.   al   Qt   Assistant.  en  .   Así   por   ejemplo  en  el  directorio  de  usuario.   ya   que   no   se   trata   de   un   tema   de   carácter   introductorio   ni   de   propósito   general.trolltech.xx/bin   (Nokia-­‐QtCreator-­‐ 128.   al   Qt   Demo.   vamos   a   preparar   el   PATH   para   nuestra   sesión   de   usuario   que   podrá   acceder   al   IDE.xx  son  números  referentes  a  año  y  versión  del  empaquetado).  Ya  puedes  ir  directo   al  siguiente  tema.  Antes  de  instalarlo.   En   el   Qt   Demo.  añadiremos  al  PATH  actual  lo  siguiente:   PATH=$PATH:/opt/qtsdk-­‐2010.  podemos  añadir  accesos  directos  a  nuestra  barra  a  las   herramientas   que   podremos   encontrarlas   en   /Developer/Applications/Qt.png).xx.xx.   Una   vez   se   acaba   la   instalación.   no   está   explicada   en   este   libro.com/4.   tecleando   qtcreator.xx.  tecleando  linguistic.   tecleando   qtdemo.     Puedes   hacerte   unos   disparadores   (accesos   directos)   en   tu   escritorio.bin   (donde   las   xx.  al  Qt  Linguistic.   Los   ejemplos   se   han   instalado  en  /Developer/Examples/Qt.     El  sistema  ya  está  listo  y  preparado  para  ser  usado  en  nuestro  curso.     Otras  plataformas     La   instalación   en   otras   plataformas.dmg   y   previamente   instalado   XCode   del   disco   de   MacOSX   correspondiente.xx/qt/bin.bash_profile.   y   el   Qt   Linguistic   que   nos   ayudará   a   implementar  traducciones  a  otros  idiomas  de  nuestra  aplicación.  desde  root  le  damos  permiso  de  ejecución  (chmod  +x).       Una  vez  terminada  la  instalación.  es  necesario   tener  instalados  GCC.  y  lo   ejecutamos   desde   la   consola.   Se   abrirá   una   ventana   de   diálogo   y   podemos   instalarlo   en   el   directorio   que   queramos   (por   ejemplo   /opt/qtsdk-­‐20xx.  tecleando  assistant.   y   el   icono   en   /opt/qtsdk-­‐20xx.bin  para  Linux  64  bits.   y   ya   podremos   acceder   desde   la   consola   a   las   diferentes   herramientas.           .  Ya  puedes  ir  directo   al  siguiente  tema.6/supported-­‐platforms.html   y   en   otras   secciones  de  la  web  de  Qt  Nokia.

      Vamos   por   tanto   ya   a   abrir   el   Qt   Creator.  vamos  a  seleccionar  el  fichero  application.   vamos  a  entrar  en  la  carpeta  application.   encontramos   un   icono   de   una   pantalla   de   ordenador.       Bien  vamos  a  pulsar  en  Open  Project…  y  vamos  ir  a  la  carpeta  tema02  y  dentro  de  ella.       Al   abrirse.   Projects   (que   nos   permite   configurar   opciones   del   proyecto)   y   Help   (para   acceder   al   asistente).   de   manera   que   si   destinamos   la   carpeta   /qtsrc   para   este   menester.   En   esa   misma   carpeta   puedes   también   descomprimir   todos   los   ejemplos   que   van   acompañando   a   este   libro.   Si   activamos   el   tab   Develop.   Design   (acceso   al   Qt   Designer.   A   la   izquierda.   y   si   el   modo   Build   es   Debug   o   Release   (por   defecto   lo   tendremos  en  Debug.     Antes   de   comenzar   a   programar.  Edit  (editor.   los   ejemplos   estarán   en   carpetas   como   /qtsrc/temaXX   (donde   XX   es   el   número   del   tema).   Se   nos   abrirá   el   IDE   con   una   ventana  sobre  él  como  ésta:     Podemos  pulsar  sobre  Create  Project…  para  crear  un  nuevo  proyecto.  Puede  ser  en  cualquier   carpeta  o  directorio.   se   puede   elegir   la   herramienta   de   desarrollo  a  usar.       .   hay   una   barra   de   herramientas.   Debug   (para   debugear   un   ejecutable).  hasta  que  vayamos  a  generar  una  aplicación  para  ser  distribuida).ui).   indica   el   nombre   del   proyecto   actual   por   defecto.   podemos   ver   la   disposición   del   Qt   Creator.  De  arriba  a  abajo:  Welcome  (Bienvenida).   donde   en   la   parte   superior.  y  vamos   a  abrirlo.  y  dentro  de  esta  se  irán  guardando  en  carpetas  cada  uno  de  los  proyectos   que   vayamos   usando.   En   la   zona   inferior   de   esta   misma   barra.  Debajo   están  las  herramientas  de  Run  (ejecutar).   vamos   a   configurar   el   Qt   Creator.   para   crear   la   GUI   editando   ficheros   tipo   *.   compilando   un   ejemplo.  u  Open  Project…  para  abrir   uno   preexistente.pro.   ejecutándolo.   sería   interesante   destinar   un   directorio   o   carpeta   del   sistema  local  para  guardar  los  ejemplos  y  ejercicios  que  vamos  a  hacer.  que  es  su  forma  habitual   para   introducir   código).  Start  Debug  (comenzar  debug)  y  Build  All  (construirlo   todo).   donde   en   la   parte   superior   de   la   misma.   podremos   ver   las   sesiones   recientes   que   hemos   abierto  y  así  acceder  a  ellas  directamente.   y   a   explicar   someramente   su   uso.9     TEMA  2   COMPILACIÓN       Una   vez   ya   tenemos   nuestros   SDK   y   herramientas   de   desarrollo   perfectamente   instaladas.

  abrirlo   para   edición.  nos  queda  en   la  zona  superior.   Nos   aparecerá   una   ventana   con   opciones.   Si   en   la   barra   de   la   izquierda   sobre   la   zona   inferior.  por  lo  que  habría  que  revisar  que  la  instalación  sea  correcta.  pero  aún  en  el  lado  izquierdo.  etc.   está   en   Tools-­‐>Options.   iremos   profundizando   en   el   uso   del   Qt   Creator   como   IDE   de   desarrollo  del  SDK  de  Qt.   está   en   Qt   Creator-­‐>Preferences.   nos   vamos   a   cerciorar   de   tener   la   opción   “Aggregation   as   a   pointer  member”  y  nada  más  (como  puedes  ver  en  el  gráfico  inferior).   y   en   MacOSX.  pero  aunque  haya  conseguido  compilarlas  en  su  versión  Release  (no  Debug).   y   nos   vamos   abajo   para   pulsar   en   Run.   que   corresponde   a   la   aplicación   que   acabamos   de   compilar   y   ejecutar.   y   haciendo   doble   clic   sobre   él.   de   manera   que   podemos  acceder  directamente  a  ellos.  los  mensajes  del  compilador.   Allí   seleccionamos   Editor  Settings.       La   parte   derecha   superior.  el  árbol  lógico  de  los  ficheros  que  componen  el  proyecto.   es   donde   podremos   ver   el   código   de   los   ficheros.  o  cerrarlos.  y  ponemos  como  Default  File  Encoding  “UTF-­‐8”.   pero   si   se   lleva   los   ejecutables   a   otra  máquina  sin  el  SDK  instalado.   vamos   a   aprender   a   desarrollar.10       A  la  derecha  de  esta  barra  de  herramientas.  esto  nos  indicaría  que  hay  errores  en   la  compilación  o  en  el  linkado.   Para   ellos   entramos   a   las   preferencias.  pulsando  sobre  sus  nombres.   no   podrá   portarlas   tal   cual   a   otra   máquina   para   que   funcionen   tal   cual.   y   habrá   que   tomar   ciertas   cuestiones   en   consideración   para   que   se   puedan   implementar   e   instalar   sin   problemas  en  un  sistema  final  de  producción.   cualquier   fichero.   las   cuales   vamos   a   dejar   todas   por   defecto   excepto   en   Projects.   y   en   Designer.   hubiera   aparecido  una  barra  en  rojo  con  un  número  (el  de  errores).  y  la   aplicación  que  se  ha  cargado.       Pulsamos   ahora   en   Edit.   De   esta   manera   se   compilará  el  proyecto  actual.  debugger.       Tenga   en   cuenta   que   hemos   preparado   el   entorno   para   desarrollar   nuestras   aplicaciones.  Nos  aparecerá  una  ventana  de  un   sencillo   editor   de   texto   con   su   barra   de   herramientas.  sepa  que  no  van  a  funcionar  de  antemano.  Todo  ello  lo  veremos  con  detenimiento  en  el   tema   28.       Pulsamos   en   OK   y   nos   vamos   a   Projects   en   la   herramientas   de   la   izquierda.   donde   vamos   a   fijar   el   directorio   para   nuestros   proyectos.  y  se  ejecutará  una  vez  compilado.     En   los   siguientes   temas.         .  pulsando  sobre   la  X  que  aparece  si  dejamos  un  tiempo  el  cursor  sobre  un  nombre  de  fichero.   y   en   la   inferior.  Podemos  seleccionar   dentro   de   él.   que   en   Windows   y   Linux.   En   la   parte   inferior   aparecerán   el   listado   de   todos   los   ficheros   que   tengamos   abiertos.     Vamos   ahora   a   configurar   el   entorno   para   trabajar   con   los   ejemplos   de   nuestro   libro.   De   momento.

  Esta   es   la   forma   más   deseable   de   comenzar   un   estudio   que   pretende   ser   los   más   aprovechable   posible.  No  es  necesario  conocer  la  estructura   exacta   del   mismo.-­‐   Fichero   de   proyecto   (project).   si   vamos   a   desarrollar   una   clase   llamada   calculator.   Estos   ficheros   son   diseñados   gráficamente   en   el   Qt   Designer.   si   en   el   Qt   Creator.   y   de   hecho.   haces   doble   click   sobre   un   fichero   *.   3.  y   deba   antes   ser   creador   con   el   comando   “qmake   –project”   antes   de   la   compilación   definitiva.   de   esta   manera   comenzaremos   a   ver   el   cuadro   desde   lejos.   posee   una   forma   de   ordenar   el   código.   <?xml version="1.  si  el  código  se  estructura   bien.ui.  para  crear  código  sencillo  y  legible  desde  el  primer   momento.cpp”.  tendrás  que  pasar  del  modo  “Design”  al  modo  “Edit”  para  poder  ver   el  código  XML  que  siempre  tendrá  la  siguiente  estructura.  En  la  barra  de   herramientas  de  la  izquierda.    y  el  nombre  del  fichero  coincida  con  el   nombre   de   la   clase   que   se   declara.pro”.   Si   nuestro   proyecto   se   llama  calculator.  estaría  en  el  fichero  de  nombre  “calculator.  vamos  a  ver  cual  es  la  estructura   de   una   aplicación   en   Qt.-­‐   Con   la   extensión   *.   Es   un   fichero   que   puede   generarse   y   rellenarse  automáticamente  si  hemos  creado  el  código  con  el  Qt  Creator.  el  fichero  proyecto  tomará  el  nombre  “calculator.h  .ui.   Lo   más   recomendable   es   que   la   implementación  o  desarrollo  de  cada  miembro  de  una  clase.pro   .  cada  fichero  describe  en  lenguaje  XML   el  interfaz  gráfico  de  formularios  o  ventana  de  la  aplicación.  o  que  puede  no  existir.  Lo  más  recomendable  es  que  la  declaración   de  cada  clase  vaya  en  un  fichero  cabecera  por  separado.-­‐   Ficheros   Fuente   (sources).   Veamos   ahora   la   estructura  interna  de  cada  uno  de  los  tipos  de  ficheros  mencionados.  Por  lo  tanto  cada  fichero  *.   pero   no   todas   las   ventanas   de   la   aplicación   tienen   que   estar   en   un   fichero   *.  el  fichero  cabecera  que  la  declara  por  completo  la  llamaríamos  “calculator.  es  recomendable  que  se  encuentren  en  un  mismo   directorio   raíz.cpp”.-­‐  Con  la  extensión  *.  tiene  los  mismos  elementos  que  son:   1.       Todos  los  ficheros  de  un  proyecto  Qt.   El   nombre   del   mismo   coincidirá   con   el   de   la   carpeta   de   proyecto   creada.-­‐   Con   la   extensión   *.0" encoding="UTF-8"?>   .-­‐  Ficheros  Cabecera  (headers).   Así   por   ejemplo.   2.   que   desde  el  principio  es  fundamental  conocerlo.11     TEMA  3   ESTRUCTURA  DE  UNA  APLICACIÓN  QT4       Antes  de  comenzar  a  ver  la  primera  aplicación  en  Qt4.h”.  el  cual  genera  automáticamente  el  código  XML  de  dichos  ficheros.  esté  contenido  por  completo  en  un   fichero  con  el  mismo  nombre  que  la  clase  que  implementa.   Estructura  de  un  proyecto     Todo  proyecto  con  interfaz  visual  (GUI)  en  Qt.   4.ui.  No  importa  lo  sencillo  o  complejo  que  pueda  ser  un  proyecto.cpp   .  la  implementación  de   la  clase  calculator.   antes   de   empezar   a   adentrarnos   en   los   detalles.   cuyo   nombre   coincida   con   el   nombre   completo   del   proyecto.   ya   que   se   pueden   implementar   con   código   C++.   siempre  hay  uno  que  contiene  la  función  principal  (main)  y  cuyo  nombre  será  “main.  de  manera  que  se  mostrará  la  interfaz  gráfica.  Así  por  ejemplo.   se   te   abrirá  directamente  el  Qt  Designer.  Dentro  de  los  fichero  fuente.-­‐  Con  la  extensión  *.-­‐  Ficheros  de  formularios  (forms).   Qt.     Fichero  formulario     Se  trata  de  un  fichero  escrito  en  código  XML  con  la  descripción  de  todos  los  elementos   del  interfaz  visual  de  uno  de  los  formularios  del  proyecto.  siempre  podrá  ser  mantenido  de  una  manera  más  sencilla  y  eficaz.ui  es  una   ventana.

cpp)     #include "classname..   segunda   y   última   líneas   del   mismo. // Cuerpo con la declaración de todos los miembros de la clase ………………………………………… ………………………………………. }. ………………………… Vamos  a  partir  de  la  idea  de  solamente  declarar  una  clase  en  un  solo  fichero.       Fichero  fuente  (classname.0"> …………………………………………………………….   las   condicionales   al   preprocesador   para   evitar   la   reinclusión   de   este   código   más   de   una   vez.  y  la  declaración  de  clases  nuestras  incluídas  en  este  proyecto  que   son   usadas   en   la   implementación   de   esta   clase.   Finalmente   tenemos   la   declaración   de   la   clase   misma  con  todos  los  elementos  miembro.. class Classname : public QClasePadre { // el contructor con un solo parámetro lo declararemos // siempre como explicit.   Después.   </ui>     Fichero  cabecera  (classname..   incluímos   con   #include   las   clases   Qt   usadas   en   la   implementación  de  esta  clase.12     <ui version="4. y al menos con el puntero a la // clase padre inicializado a NULL explicit Classname (QClasePadre *parent = 0)..h" Classname:: Classname (QClasePadre *parent) : QClasePadre (parent) { // implementación del contructor de la clase } // implementación del resto de miembros de la clase classname ……………………….   es   muy   recomendable   el   poner   en   la   primera.   …………………………………………………………….h)       #ifndef CLASSNAME_H #define CLASSNAME_H #include <QClasePadre> // podemos declarar aquí otras clases del proyecto que // sean usadas en la implementación de esta clase class Claseincluida.     . #endif     Como   puedes   observar.

 no  se   usarán. argv).  Si  se  trata  de  una  aplicación  de  consola.   mostramos   dicho   interfaz.                                 .  una  ventana  de  diálogo  (QDialog)  o  una  ventana   principal   (QMainWindow).  se  haría  uso  de  código  no  gráfico  para  consola.exec().   e  inicializamos  el  constructor  de  la  clase  padre  siempre  lo  primero.   y   finalmente   con   QApplication::exec()   entramos   en   el   bucle   de   eventos   para   interactuar  con  el  interfaz  mostrado.   Primeramente   creamos   una   instancia   de   QApplication   que   puede   recibir   argumentos   de   la   linea   de   comandos.  que  crea  la  ventana  principal.   Haremos   uso   de   programas   para   consola. w.cpp) #include <QModulo> #include "claseinterfaz.   Fichero fuente principal (main.  Lo  más  lógico  es  que  vaya  incluída  la  clase  interfaz. // Mucho más código que sea necesario return a.h" int main(int argc. }         Primeramente  incluímos  el  módulo  necesario  para  el  código  que  vamos  a  incluir  en  la   funcion  main().show().  Después  implementamos  el   resto  de  los  miembros  de  la  clase.         La   primera   y   última   líneas   de   código   de   la   funcion   main().   Luego   instanciamos   la   clase   interfaz.   Dicha  ventana  podrá  ser  un  widget  (QWidget).13     Primero   incluímos   la   cabecera   de   nuestra   clase. claseinterfaz w.  para  simplificar  el  código  y  facilitar  el  aprendizaje  de  los  conceptos.  y  en  puesto  de  instanciar  un  clase  interfaz.   sólo   son   necesarias   si   se   trata  de  una  aplicación  con  interfaz  gráfico  (GUI).   Luego   implementamos   el   constructor.   para   estudiar     y   reforzar   clases   de   Qt   que   no   son   gráficas. char **argv) { QApplication a(argc.

14                                                                 .

 De  esta  forma  no  se  nos  creará   ningún   código   automáticamente.  Pulsamos  Continue  y  Done.cpp  dentro  de  ella.  Aparecerá  una  ventana  como  la  de  abajo.  A  la  izquierda  en  la  columna  de  proyectos  (Projects)   nos   aparece.  Sinceramente  me  parece  que  es  una  trivialidad.  vamos  a  ver  las   dos  posibles  aplicaciones  Hola  Mundo.cpp”  en  Name.  o  si  ya  lo  tenemos  abierto.  por  lo  que  un  Hola  Mundo  queda  aún  más  descolocado.  vamos  a  usar  el  Qt  Creator.   Pulsamos   el   botón   Choose…   y   aparece   una   nueva   ventana.  si  no  también  aplicaciones  de  consola.  sucumbiremos  a   la  tendencia  generalizada.  pero  en  fin.   Escogemos   C++   en   Files  and  Classes.     Elegimos  Other  Project  -­‐>  Empy  Qt  Project  (un  proyecto  vacío).15     TEMA  4   LA  PRIMERA  APLICACIÓN  Qt       La  mayoría  de  libros  que  enseñan  un  lenguaje  de  programación.   escogeremos   un   directorio   donde   tendremos   todos   nuestros   proyectos.  y  C++  Source  File  en  la  columna  de  la  derecha.  Pulsamos  Continue  y  luego  Done.   Y  es   una   tradición   que   se   ha  perpetuado  por  los  tiempos  de  los  tiempos.  Ahora  aparece  una  nueva   carpeta  llamada  Sources  con  un  fichero  main.pro   que   Qt   Creator  se  encargará  de  ir  rellenando  por  nosotros.     Hola  mundo  con  GUI     Abrimos  Qt  Creator.   donde   pondremos   el   nombre   del   proyecto   que   será   “holamundo”   y   abajo   en   Create   in.  Hacemos  clic  con  el  botón  derecho  sobre  el   proyecto   y   del   menu   emergente   escogemos  la   penúltima   opción   (Add   New…).   si   no   un  Framework  que  en  este  libro  en  particular  lo  hemos  dedicado  al  desarrollo  de  Qt  en  C++.  es  un  conocimiento  previo  que  se  presupone  antes  de  leer  la  primera  línea   de  este  libro.  Pulsamos  Choose…  y  le  ponemos   el  nombre  al  fichero  “main.   y   sólo   el   fichero   holamundo.   ha   terminado   imponiéndose.       .  Así   que  el  lenguaje  C++.   Bien   es   cierto   que   Qt   no   es   un   lenguaje.       Para  desarrollar  dicha  aplicación.  ya   que   no   enseña   absolutamente   nada   sobre   el   lenguaje   más   allá   de   imprimir   un   mensaje   en   pantalla.  y  puesto  que  Qt  se  puede   usar  no  sólo  para  crear  interfaces  gráficas.   todo   lo   haremos   nosotros   manualmente.  abrimos  un  nuevo  proyecto  pulsando  en   File-­‐>New  File  or  Project.  pero  en  un  mundo  donde  todos  vamos  corriendo  y  necesitamos  ver  los  resultados  antes   de   recorrer   el   camino.   la   carpeta   de   nuestro   nuevo   proyecto.  siempre  empiezan  por   demostrar   la   sencillez   del   lenguaje   con   la   típica   aplicación   Hola   Mundo.

16       Haciendo   doble   clic   sobre   main.  Vamos  a  escribir  el  siguiente  código:   #include <QApplication> #include <QLabel> int main(int argc.cpp.   hemos  tenido  que  incluir  sus  cabeceras.   Puesto   que   usamos   dos   clases   en   este   fichero   de   Qt   (QApplication   y   QLabel).argv). return a.     Hola  mundo  sin  GUI   #include <QDebug> int main(int argc. char **argv) { qDebug() << "Hola mundo\n". return 0.     Sigue  los  ejercicios  de  este  tema  donde  se  compila  y  se  ejecutan  estos  2  simples  ejemplos.show(). }   Como   se   puede   ver   hemos   usado   una   clase   QLabel   para   crear   el   letrero   donde   pondrá   el   mensaje   “Hola   mundo”. char **argv){ QApplication a(argc.   podremos   ver   a   la   derecha   el   lienzo   en   blanco   preparado   para  empezar  a  escribir  el  código  en  él. }   La  clase  QDebug  es  perfecta  para  sacar  mensajes  por  consola.                     .  y  tiene  sobrecargado  el  operador   <<  para  que  actue  de  manera  similar  a  como  lo  hacía  std::cout  en  la  STL  de  C++. QLabel l("Hola mundo").exec(). l.

 deriva  de  la  clase  QObject. .   el   nombre   de   su   clase. }     .   toda   instancia   de   la   misma   es   única.  son  únicos  e  incopiables  en   tiempo  de  ejecución  como  por  ejemplo  sí  lo  es  un  QString  (una  cadena).   Clases   que   necesiten   ser   copiables.   ya   que   las   clases   derivadas   de   QObject   no   son   copiables.   Una  de  ellas.  QHash….   Esto   nos   permite   una   de   las   más   importantes   características  de  Qt.   y   las   conexiones   entre   objetos   mediante   señales   y   slots.  no  de  todas..  QMap.  siendo  más  sencillas  como:   QGradient.   toda   una   serie   de   informaciones   sobre   la   clase   del   mismo.   Finalmente   cada   objeto.  Por  todas  estas  razones...   y   para   ello   mejor   es   empezar   a   conocer   las   particularidades   de   Qt   que   la   hacen   diferente   a   las   STL.  metaobjetos  y  propiedades         Llegó   el   momento   de   meternos   en   harina.  y  esto  es  fundamental   para   implementar   la   introspección.   sin   embargo.     QObject.  que  es   la  clase  principal.   Cada   objeto.   y   eso   no   requiere   ser   copiable.  los  objetos  de  QObject.  podríamos   obtener  de  él  información  sobre  el  mismo  a  través  de  los  metadatos.  Las  clases  que  no  dependen  de  QObject  son:   -­‐ -­‐ -­‐ Clases  de  primitivas  gráficas  que  requieren  tener  poco  peso..   C++  es  extendido  en  Qt  para  dotarlo  de  características  fundamentales  para  el  framework.  De  hecho  Qt   despliega  gran  parte  de  lo  que  Qt  es:  señales  y  slots.     Los  metadatos.  y  eso  no   puede  ser  copiado.     Una  forma.  etc).  Luego   veremos  la  estructura  global  de  clases  que  componen  el  Framework  y  trabajaremos  con  una  de   las  cualidades  más  interesantes  de  Qt.  PHP.17     TEMA  5   QObject..   Todo   objecto   QObject   es   único   e   individual.       La  clase  QObject     Gran  parte  de  la  magia  desplegada  en  el  Framework  Qt.   sus   propiedades.   que   nos   permitiría   hacer   un   casting   dinámico   sin   usar   el   RTTI   (lo   cual   puede   evitar   muchos   errores  del  programador):   if(object->inherits("QAbstractItemView")){ QAbstractItemView *view = static_cast<QAbstractItemView *>(widget).  Todos  los  widgets  usados  para  crear  la  GUI  derivan  de  esta  clase.  de  las  cuales  dos  son  introducidas  por  esta  misma   clase   como   son:   el   modelo   de   introspección.   sus   objetos   hijos.  la  introspección.  QScreen.   es   que   no   es   copiable..   Para   ello   lo   mejor   es   comenzar   por   la   clase  principal.   esta   situado   en   algún   lugar   específico   de   la   jerarquía   de   clases   QObject.   por   tanto...  QPalette…   Clases  contenedoras  de  datos  como:  QChar.  padre  de  la  mayoría  de  las  clases  que  componen  Qt4.   sus   señales   y   slots.  y  este  es  único  para  cada  objeto.  QList.   La   primera   características   importante   que   tiene   QObject.   Si   revisas   la   documentación   de   QObject   en   el   asistente   (Qt   Assistant).  tales  como  el  nombre  del   objeto.   verás   entre   sus   miembros  todas  estas  características.   Qt  extiende  a  C++  con  nuevas  características.  Python.   Porque   los   objetos   tienen  nombre  (propiedad  objectName).  que  es  la  introspección..  sería  mediante  el  método  miembro  QObject::inherits(const  char*  className).  QString..  QPainter.  son  los  metadatos  .  esa  clase  es  QObject.  propiedades  y  manejo  de  memoria  sencillo.  llevan  información  sobre  el  objeto  instanciado..   tiene   conexiones  con  otros  objetos  mediante  conexiones  señal-­‐slot  que  veremos  más  adelante.  Instanciado  un  QObject.   es   la   clase   base   de   la   mayoría   de   las   clases   del   framework   Qt.  que  facilita  mucho  integrar  Qt  con  otros  lenguajes   de  scripting  y  entornos  dinámicos  (Ruby.

  Luego   busca   MACROS   del   tipo   Q_   .   Lo   primero   que   busca   el   moc   es   que   la   clase   se   derive   de   QObject   de   manera   directa   o   indirecta.   para   obtener   una   lista   con   todos   los   botones   del   tipo   QPushButton.  podemos  usar  un  código  como  este:   QList<QWidget *> widgets = parentWidget.  La  firma  de  la  misma  es:   QList<T>  QObject::findChildren  (  const_QString_&_name_=  QString()  )  const   que   nos   devuelve   una   colección   de   objetos   hijos   de   la   clase   cuyo   nombre   se   pase   como   argumento.   para   buscar   en   ella   todos   los   metadatos   y   generar   el   código   necesario   para   ellos.findChildren<QWidget *>("widgetname").  podemos  pulsar  sobre  cualquiera  de  los   elementos  que  hay  en  el  form.     object->metaObject()->className().   El   moc   con   todo   esto   genera   automáticamente  código  que  guarda  en  ficheros  que  comienzan  por  moc_*.   son   recabados   por   el   moc   (meta-­‐object   compiler)   del   propio   código   declarativo   de   las   clases   en   los   archivos   cabecera.     Por   ejemplo.     Por   ejemplo.   si   queremos   obtener   los   widgets   que   son   hijos   de   un   widget   en   particular   a   través  de  su  nombre  (widgetname).   y   los   valores   que   esas   propiedades   toma   para   ese   objeto   en   particular.   Q_CLASSINFO.ui).   QList   es   una   clase   template   que   veremos   más   adelante.  ver  todas  las  propiedades  que   dicho   objeto   tiene.   pero   similar   a   una   lista   o   vector  de  STL. // devuelve un char* con el nombre de la clase object->metaObject()->indexOfProperty("width").findChildren<QPushButton *>().  En  el  Qt  Designer.   Devuelve   un   objeto   de   la   clase   QMetaObject.   como:   Q_OBJECT.  podemos  usar  un  código  como  el  que  sigue:   QList<QPushButton *> allPushButtons = parentWidget.     Propiedades  de  un  objeto     Todo  objeto.  que  añaden  información  de  la  clase.   Qt   guarda  esas  propiedades  como  metadatos.  etc.   podremos   observar   toda   la   información  que  podemos  conseguir  en  run-­time  de  un  objeto  cualquiera.  y  hace  uso  de  una  macro  propia  para  ello:   Q_PROPERTY(type name READ getFunction [WRITE setFunction] [RESET resetFunction] [NOTIFY notifySignal] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] )     .   esto   es   lo   que   llamamos   propiedades   de   un   objeto.  donde  se  defina  por  completo  el   estado   del   mismo   y   todas   sus   particularidades.18     Uno  de  los  miembros  más  importantes  para  acceder  a  los  metadatos  de  una  clase.  Finalmente  busca  palabras  clave  añadidas   por  Qt  como  slots  y  signals.   que   si   miramos   en   el   asistente   todos   sus   miembros.  y  a  la  derecha  en  la  parte  inferior. Los   metadatos.   Otra   función   miembro   introspectiva   muy   importante   que   incorpora   QObject   es   findchildren.  a  abrir  un  fichero  form  (*.cpp  .   contenidos  como  hijos  de  parentWidget.  que  declaran  los  métodos  miembros  de  la  clase  que  se  encargarán  de   la   conectividad   de   la   misma   con   objetos   de   otras   clases.  y  por   tanto   para   implementar   la   introspección   es   metaObject()   .  necesita  guardar  su  estado  en  algún  sitio.

  y   establecen   el   estado   de   un   objeto   en   un   momento  concreto.     .   Pongamos   un   ejemplo   sencillo   de   propiedad   en   una   clase.     Constant.  los  getter  y  setter  de  una  propiedad.   Primero   comenzaremos   con   la   declaración  de  los  miembros  de  la  clase  en  su  fichero  cabecera.           La   gran   mayoría   del   tiempo   usaremos   meramente   las   secciones   READ   y   WRITE   para   nuestras   propiedades.  si  no  entre  instanciaciones  de  clases.  width  es  parte  de  size)  y  no   son  guardadas.  el  resto  de  la  aplicación  podrá  acceder   a  dicha  propiedad.19       Esta   macro   se   pone   en   la   declaración   de   la   clase.-­‐   es   el   tipo   de   la   variable   que   guardará   el   estado   de   dicha   propiedad   del   objeto   (QString.  otras  toman  valores  de  otras  propiedades  (por  ej.   Scriptable.  son  el  interfaz  de  la  propiedad  de   dicha  clase  con  el  resto  de  objetos.  añadiremos  DESIGNABLE  true.   en   el   fichero   cabecera   de   la   misma.   isChecked()   en   una   QCheckBox).  no  pueden  ser  cambiadas  en  la  instanciación  de   la  clase.  y  así  poder  inicializar  su  valor  por  parte  del  programador  en  su  instanciación.-­‐  las  propiedades  declaradas  constant.-­‐  nos  dice  si  dicha  propiedad  es  accesible  desde  el  entorno  de  diseño.   -­‐ -­‐ En  una  palabra.-­‐   nombre   de   la   señal   (signal)   que   se   producirá   inmediatamente   después   de   que   el   valor  de  la  propiedad  sea  cambiado.   Las   partes   entre   corchetes   ([])   no   son   obligatorias.   int…).  height….   y   si   nuestra   clase   es   parte   de   un   nuevo   widget   visual   diseñado   por   nosotros.-­‐  nos  dice  si  dicha  propiedad  es  accesible  desde  el  entorno  de  script  de  Qt.)   getFunction.   Debe  de  haber  una  función  miembro  que  se  encargará  de  la  recogida  del  valor  actual  de   esa   propiedad.  de  manera  que  esta  no  pueda  ser  directamente  accesible  fuera  de  la  clase.  y  puede  que  USER  true.   Sería   bueno   que   se   llamara   getPropiedad()   o   Propiedad().-­‐   nombre   de   la   función   setter   o   que   establece   o   cambia   el   valor   de   la   propiedad   (lo   veremos  más  adelante)   resetFunction.   User.   e   isEstado_Propiedad()  si  devuelve  un  bool.  Qt  Designer.   name.  la  cual  también  podría  ser  un  parámetro   del  mismo.-­‐   la   mayoría   de   propiedades   son   guardadas.  implica  una  serie  de  añadidos  en  los  miembros   de  la  clase  a  la  que  pertenece.  Sería  bueno  que  se  llamara  setPropiedad(valor_de_la_propiedad).-­‐   nombre   de   la   función   getter   o   que   lee   el   valor   de   la   propiedad   (lo   veremos   más   adelante)   setFunction.  si  esta  puede  ser  cambiada  por   el  usuario  que  use  la  aplicación  (no  el  programador).  a  la  que  llamamos  setter  de   la   propiedad.   Es   lo   que   llamamos   un   getter.   notifySignal.   Stored.   Vamos   a   detallar  cada  parte  de  esta  declaración:   type.   Designable.  y  estos  son:   -­‐ -­‐ El  constructor  debe  de  ponerle  un  valor  inicial.   que   contenga   el   estado   de   la   propiedad   en   cada   momento.   después   de   la   macro   Q_OBJECT.   Y   esta   función   debe   estar   en   la   parte   pública  para  poder  ser  usada  desde  fuera  de  la  clase  misma.  y  sólo  a  través  de  ellos.   Puede  haber  una  función  miembro  que  se  encargue  de  establecer  o  cambiar  el  valor  de   propiedad.-­‐  nombre  de  la  función  que  pone  el  valor  por  defecto  a  la  propiedad.-­‐  es  el  nombre  propio  de  la  propiedad  en  cuestion  (width.   Debe   de   haber   una   variable   privada.-­‐   es   una   propiedad   modificable   por   el   usuario   de   la   aplicación   (por   ej.       La  declaración  de  una  nueva  propiedad.

  Otra   forma   hubiera   sido   que   dentro   de   la   implementación   del   constructor. QObject *parent) { QObject(parent). m_angle(angle) { } qreal AngleObject::angle() const { return m_angle. m_angle = angle.   La   implementación   de   la   función   getter   es   tan   simple   como   devolver   el   valor   de   la   propiedad   que   se   almacena   en   la   variable   interna   propia. QObject *parent) : QObject(parent).20     // Fichero angleobject.   dicha   propiedad   hubiera   tomado   un   valor   fijo. QObject *parent=0). qreal angle() const.   que   luego   el   programador   hubiera   podido  cambiar  usando  la  función  setter  correspondiente  a  dicha  propiedad.  y  luego  poner  un  código  que  se  encargue  de   la   notificación   de   dicho   cambio. } void AngleObject::setAngle(qreal angle) { m_angle = angle. void setAngle(qreal). }   Vemos  como  en  la  implementación  del  constructor.   primero   cambiar   el   valor   de   la   variable  interna  asociado  a  la  propiedad  en  cuestión.   La   función   getter   es   angle()   que   como   vemos   al   no   cambiar   el   estado   del   objeto   podemos   declararla   como   const. private: qreal m_angle.   y   la   toma   de   decisiones   al   respecto   (lo   hemos   representado   todo   eso  como  un  mero  hazAlgo()).   tendremos   que   poner   al   menos   como   parámetro   el   valor   inicial   de   dicha   propiedad. }   Si   observas.  Quizás  no  estés  acostumbrado  a  este  tipo  de  notación  y  lo  veas  mejor  así:   AngleObject::AngleObject(qreal angle. hazAlgo().  La  variable  la   ponemos   como   privada   bajo   el   nombre   de   m_angle   (notación   especial   para   distinguir   variables   miembro).  un  número  real  de  doble  precisión).   La   función   setter   va   a   ser   setAngle()   y   en   la   instanciación   al   crear   un   objeto   nuevo   de   esta   clase.   Para  entrar  un  poco  más  en  materia  vamos  a  ver  la  implementación  de  esta  clase:   AngleObject::AngleObject(qreal angle.  se  inicializa  llamando  al  constructor   de   QObject.   suele   hacer   dos   cosas.   y   la   implementación   de   la   función   setter.     .h class AngleObject : public QObject { Q_OBJECT QPROPERTY(qreal angle READ angle WRITE setAngle) public: AngleObject(qreal angle. } Pero   es   exactamente   lo   mismo.   la   clase   padre   y   la   varible   privada   m_angle   con   el   valor   pasado   como   parámetro   en   la   instanciación.   verás   que   angle   es   una   propiedad   que   se   va   a   guardar   en   una   variable   del   tipo  qreal  (que  es  un  typedef  de  double  en  Qt.

 Si  es  un  nombre  compuesto. Grados}.       Todas   las   variables   miembro   que   están   asociadas   a   propiedades   que   definen   el   estado   del  objeto.   y   no   acceder   directamente   a   la   variable   privada   del   mismo.  setWidth().h   y   la   implementación   de   la   misma  en  un  fichero  *.  isDigit().  es   importante   que   cada   parte   del   mismo   se   destaque   con   una   mayúscula.   permite   que   un   código   pueda   evaluar   dicho   valor   previamente   antes   de   asignarlo   a   dicha   variable.   usando  la  macro  Q_ENUM  de  la  siguiente  forma:   Q_OBJECT Q_ENUM(AngleMode) Q_PROPERTY(AngleMode angleMode READ angleMode WRITE setAngleMode)   También   es   posible   que   ese   enum.   Es   adecuado   para   crear   código   fácil   de   leer.   si   se   siguen   unas   reglas   que   se   han   ido   añadiendo   al   estilo   de   programación  de  Qt.   defina   Flags   numéricos   que   puedan   combinarse   entre   sí  con  OR  (|).   En  ese  caso.  dockWidgetArea().  y  de  esta  manera  hacer  honor  a  la  propiedad  de  encapsulación  C++  como  lenguaje   orientado  a  objetos.  Su  nombre  debe  dejar  claro.  dicho  tipo  es  un  enum. // 00000001 ExportExternalSymbolsHint = 0x02.  el  resto  de   partes  las  comenzamos  con  mayúsculas.  y  si  su  nombre  es  compuesto.  Algo  así  como:   Q_OBJECT Q_FLAGS(LoadHint LoadHints) Q_PROPERTY(LoadHint hint READ hint WRITE setHint) public: enum LoadHint { ResolveAllSymbolsHint = 0x01.   y   su   nombre   debe   ser   descriptivo  de  lo  que  representan.  tendremos  que  notificar  previamente  al  moc  de  que. // 00000010 LoadArchiveMemberHint = 0x04 // 00000100 }.  empiezan  por  m_.  claro  esta.     .   sus   miembros   y   la   organización   de   las   mismas.   y   por   tanto   fácil   de   mantener.   puede   ser   de   ayuda   el   llamarlas   de   la   misma   forma   pero   sin   la   Q   inicial.  Por  ejemplo  m_height  (para  señalar  la  altura  del  objeto).  algo  así  como:   public: enum AngleMode {Radianes.  Por  ejemplo.21     El   usar   un   setter   para   cambiar   el   valor   de   una   propiedad.     Los   nombres   de   las   clases   deben   comenzar   en   mayúscula.   puesto   que   estas   siempre   comienzan   por   Q.   y   así   controlar   dicho   valor   dentro   de   un   rango   admisible   dentro   de   la  aplicación. LoadHint)     Notación  especial  del  código     Aunque  C++  y  Qt  por  tanto  admita  libremente  la  elección  de  los  nombres  de  las  clases.   El  tipo  de  una  función  podría  ser  un  enum.   A   veces   si   derivamos   nuestra   clase   de   otra   preexistente   de   Qt.   la   declaración   de   la   clase   y   sus   miembros   en   un   fichero   *.  como:   class MainWindow : public QMainWindow   Todas  las  funciones  comienzan  en  minúscula.   por   ejemplo   VendedoresDeRopa. Q_DECLARE_FLAGS(LoadHints.  cual  es  su  cometido  dentro   de  la  clase  a  la  que  pertenecen.     Todas  las  clases  deben  de  estar  definidas  en  ficheros  aparte  con  el  nombre  de  la  clase  en   minúsculas.  o  del  trabajo  que  van  a  realizar.cpp  .

  Es   muy   importante   que   tomes   en   consideración.  pero  es  muy  aconsejable  si  quieres  que  todo  el  mundo  entienda  tu  código  de  manera   sencilla.   clases   y   funciones   tengan     sigan   una   notación   clara   y   tomen   un   nombre   altamente   descriptivo.   como   el   tan   temido   “memory   leak”   o   “fuga   de   memoria”.  Hacer  anotaciones  en   el   código   es   interesante   siempre   que   se   expliquen   cosas   que   no   salten   a   la   vista   en   el   mismo   código.   vamos   a   ver   en   el   próximo   capítulo.   aunque   no   muy  largo.   es   que   las   variables.       Finalizados   estos   rudimentos.  incluido  tú  mismo  unos  meses  o  años  después  de  haberlo  escrito.   Una   buena   programación   en   este   sentido   nos   evitará   de   muchos   de   los   problemas   propios   de   la   memoria.  De  hecho.                                                         .22     Como   ya   hemos   señalado   anteriormente.   la   parte   más   importante   de   una   buena   programación   en   C++.   no   es   obligatoria   este   tipo   de   notación   del   código  Qt.  y  dediques  el  máximo  esfuerzo  al  contenido  del  próximo  tema.   y   es   el   manejo   de   la   memoria.   por   lo   que   una   forma   de   evitar   tener   que   comentar.  comentar  en  exceso  el  código  es  la  mejor  forma  de  perder  la  visión  global  del   mismo.

 el  último  que  entra  es  el  primero  que  sale.   algo   que   quizás   en   su   momento  no  comprendieron.   es   la   fuente   de   la   mayoría  de  los  errores  y  problemas  de  aquellos  que  programan  en  C  o  en  C++.   dichas   variables   ya   no   existen.   b) Problemas  en  el  heap:   -­‐ Intento  de  liberación  de  memoria  ya  liberada.  Cada  vez  que  el  sistema  operativo  carga  en  memoria  un  programa   para  ejecutarlo.   fuera   de   las   llaves   que   cierran   el   ámbito   de   la   función.  una  nueva  palabra  clave  de  Qt.   Esto   nos   puede   llevar   a   no   poder   alcanzar   correctamente  la  función.  pero   para   aquel   que   ama   C++.   o   delete   en   C++.  Un  programador   de  Java.  nos   evitamos  el  uso  de  callbacks.   De   hecho   todo   programador   de   sistemas   que   se   precie.     El  manejo  de  la  memoria  en  Qt     Antes   de   nada   vamos   a   recordar   a   unos   y   a   explicar   a   otros.  es  donde  va  a  estar  el  origen  de  gran  parte   de  los  desaguisados  de  nuestra  programación  en  C++.  nos  dirá  que  esa  es  la  gran  desventaja  de  programar  en  C.   no   es   razón   suficiente   como   para   abandonar   el   desarrollo   en   este   lenguaje   tan   potente   y   versátil   a   la   vez.  de  hecho  con  el   mecanismo  de  conexiones  entre  objetos  mediante  funciones  señal  y  funciones  slots.-­‐   En   ella   se   carga   el   código   binario   del   programa   que   se   va   a   ejecutar   con   todas   sus   instrucciones   máquina   tal   cual   el   compilador  las  creó.   -­‐ Intento  de  escritura  en  memoria  ya  liberada.   y   la   alternativa   que   quiero   plantearte   es   usar  el  estilo  Qt  junto  con  su  Framework  para  evitar  éste  y  otros  problemas  típicos.   -­‐ Corrupción   de   un   puntero   a   función.  y  que  ellos  con  su  magnífico   recolector  de  basura  se  evitan  esos  problemas.   La   pila   o   stack.   -­‐ Intento  de  escritura  en  memoria  no  asignada   -­‐ Error  en  la  asignación  de  memoria     .   y   new   en   C++.   Todas   estas   variables   son   destruidas   una   vez   se   vuelve   de   la   llamada   a   la   función.   -­‐ En  la  memoria.  first  out).  y  principalmente  en  el  heap.   -­‐ Liberación  de  memoria  no  asignada.     Veamos  antes  cuales  son  los  problemas  típicos  en  la  gestión  de  la  memoria:   a) Problemas  en  el  stack:   -­‐ Leer   o   escribir   fuera   de   los   límites   de   una   array   estático.  El  programador  tiene  la  obligación  de  liberar   esos   espacios   de   memoria   usando   free()   en   C.  La  solución  es  no  usar  punteros  a  funciones.   y   permanecen  todo  el  tiempo  que  se  quiera.     El   montón   o   heap.   Esta   zona   se   reserva   con   peticiones   malloc()   en   C.23     TEMA  6   EL  MANEJO  DE  LA  MEMORIA       El   mal   manejo   de   la   memoria.  Sin  duda  es  una  buena  solución  para  ellos.   y   entra   en   ella.   usando  el  método  LIFO  (last  in.   entonces   el   programa   tiene   una   fuga   de   memoria   (memory   leak)   y   le   puede   conducir  a  un  violento  crash.  el  sistema  reserva  unas  determinadas  zonas  de  memoria  para  el  mismo:   -­‐ -­‐ La   zona   del   código   o   segmento   de   código.   Esto   se   puede   solucionar   usando  los  contenedores  que  suministra  Qt  para  manejar  colecciones  y  usar  iteradores   para  recorrerlos  o  bucles  controlados  con  foreach.   En   la   pila   se   guardan:   los   parámetros   que   se   pasan   a   la   función.   las   variables   locales   declaradas   dentro   del   ámbito   de   la   misma.   Esta   zona   es   donde   se   van   apilando   variables   como   en   una   pila   de   platos.   Lo   que   es   lo   mismo.  La   pila   es   usada   cada   vez   que   el   código   llama   a   una   función.   siempre   antes   o   temprano   tendrá   que   hacer   uso   de   C.   junto   con   el   mal   uso   de   punteros.   y   el   valor   de   retorno   de   la   función.   son   destruidas  inexorablemente.   Si   dicho   espacio   no   es   liberado.

dialog. QPushButton *button = new QPushButton(parent).   o   sean   a  su  vez  padre  de  otro  objeto. Dialog dialog.exec().  De  esta  forma.  hemos  evitado  el  uso  de  código  liberador. }     Al   derivar   Dialog   de   QDialog. #endif // DIALOG_H     Es  el  mínimo  código.  es  un  árbol  jerárquico  del  que  penden  box  y  button  de  parent.h” int main(int argc.  y  de   box  penden  option1  y  option2.  Este  se  puede   resolver  de  igual  manera  al  array  estático  en  el  stack.   de   esa   manera   serán   creados   inmediatamente   al   ser   creado   dialog   en   el   stack.24     -­‐ Lectura-­‐escritura  de  la  memoria  fuera  de  los  límites  de  un  array  dinámico.  El  único  objeto  que  no  tiene  padre.   aparece   un   estilo   de   programación  que  incorporaremos  a  Qt.argv).  y  al  borrarse  dialog  del  stack. }.  De  esta  manera.  es   colocar  su  creación  en  el  stack.show().  ya  que  no  vamos  a  redefinir  ningún  método.   QDialog *parent = new QDialog().  y  ese  será  el  propósito  del  próximo  parágrafo. QRadioButton *option2 = new QRadioButton(box).  con  el  siguiente  contenido:   #ifndef DIALOG_H #define DIALOG_H #include <QDialog> class Dialog : public QDialog { public: explicit Dialog(QWidget *parent = 0).  ningún  objeto  que  esté  en  el  heap  será  huérfano.     Para   ello   vamos   a   tener   que   crear   una   clase   derivada   de   QDialog.     Vamos  a  poner  un  ejemplo  con  los  objetos  que  componen  una  ventana  de  diálogo.  que  sería  la  ventana   de  diálogo.     Herencia  encadenada     La   mejor   forma   de   no   tener   que   poner   código   para   liberar   memoria   con   delete.   Para   resolver   los   cinco   problemas   señalados   en   el   heap.  por  dependencia  heredada  serán  liberados  los   espacios  de  todos  sus  hijos.  Luego   en   la   implementación.   a   la   que   llamaremos   Dialog.  y  vamos  a  crear  una  instancia  de  la  misma  en  el  stack  de  esta  manera:   #include <QApplication> #include “dialog.  ni  a  añadir  nada.  es  parent.   tenemos   que   declararlo   en   un   fichero   que   llamaremos   dialog.   Como  se  puede  ver.   es   que   todos   los   objetos   que   componen   la   aplicación   tengan   un   padre   que   o   bien   esté   en   el   stack.   y   permanecerán   en   la   memoria   una   vez   acabado   el   ámbito   del   constructor.  Luego  el  truco  para  que  no  haya  que  añadir  ningún  código  que  libere  su  espacio. return a.   deberemos   crear   los   objetos   que   dependen   del   objeto   dialog   en   su   constructor   con   new. QGroupBox *box = new QGroupBox(parent).  que  nos     . QRadioButton *option1 = new QRadioButton(box).h. char **argv){ QApplication a(argc.   hasta   que   la   aplicación  se  acabe.

QRadioButton *option2 = new QRadioButton(box).   Así. }     Es   importante   por   lo   tanto   que   te   quedes   con   esta   filosofía   de   programación.  es  donde  crearemos  en  el   heap   los   objetos   que   dependan   jerárquicamente   de   éste.  implementan  varios  constructores.  Veamos  pues  el  código   de  implementación  que  pondríamos  en  el  fichero  dialog.  y  en  el  constructor  de  dicha  clase.  Es  otra  de  las  características  fundamentales  de  QObject.                                       .   En   una   GUI   es   por   tanto   muy   habitual   el   jerarquizar   conforme   al   contenido.25     podría  haber  llevado  a  problemas  con  un  código  más  grande  y  complejo.   todo   objeto   que   sea   huérfano   ha   de   crearse   en   el   stack   que   requiera   de   acuerdo   al   tiempo   que   deba   de   vivir   (en   main.   si   un   objeto   de   una   clase   está   contenido   gráficamente   dentro  de  los  límites  de  un  objeto  de  otra  clase.   donde   siempre.h" Dialog::Dialog(QWidget *parent) : QDialog(parent) { QGroupBox *box = new QGroupBox(this).cpp:   #include "dialog.   Para   ello   sería   necesario   derivar   dicho   objeto   de   una   clase   derivada   de   una   de   Qt.     Para  ello. QPushButton *button = new QPushButton(this).   generando   así   dos   ficheros   para  implementar  dicha  nueva  clase. QRadioButton *option1 = new QRadioButton(box).     En   el   siguiente   tema   vamos   a   ver   como   en   Qt   se   han   evitado   el   uso   de   callbacks   para   conectar  funciones  de  dos  o  más  objetos.  donde  al  menos  uno  lleva   sólo   el   parámetro   del   padre   inicializado   a   NULL   pointer   por   defecto.   de   manera   que   todas   las   versiones  del  constructor  tiene  como  último  parámetro  el  NULL  pointer  por  defecto  de  la  clase   padre  de  la  que  deriva.  las  clases  de  Qt.   viviría   todo   el   tiempo   de   la   aplicación).  ésta  será  la  clase  base  de  aquél.

26                                                                 .

27     TEMA  7   SEÑALES  Y  SLOTS       El   mecanismo   de   señales   y   slots  de  Qt.       Como  se  puede  ver  en  el  gráfico  superior.     Veamos  como  es  la  firma  del  método  connect:   bool QObject::connect ( const QObject *sender.   para   llevar   a   cabo   diferentes   actividades. SIGNAL(*signal).  que  se  encarga  de  establecer  esa  comunicación  entre  los   dos  objetos.  Lo  excepcional  de   este   mecanismo. const QObject *receiver.   También   varios   señales   diferentes   pueden   conectarse   con   un   mismo   slot.   Para   ello   QObject   la   clase   base   de   gran   parte   de   las   clases   que   conforman   Qt.-­‐   funciones   que   son   el   final   de   la   conexión. SLOT(*slot) )     Usamos   las   macros   SIGNAL   y   SLOT   para   envolver   a   las   funciones   signal   y   slot   con   los   tipos  de  sus  parámetros.  una  señal  puede  conectarse  a  más  de  un  slot.   y   que   ejecutan   una   serie   de   acciones   una   vez   reciben  el  mensaje  de  la  señal.-­‐   funciones   que   permiten   emitir   una   señal   cuando   hay   algún   cambio   de   estado   en   el   objeto  al  que  pertenecen.   es   que   los   objetos   que   se   comunican   de   esta   forma.   y   tendríamos   una   actividad   que   puede   ser   desatada   de   varias   formas   diferentes.   slots.   no   requieren   conocerse   mutuamente.   incorpora  un  método  llamado  connect.       Todo   objeto   derivado   de   QObject   puede   poseer   dos   tipos   de   funciones   propias   especiales:   signals.  crea  una  forma  dinámica   de   comunicar   eventos   con  los  cambios  de  estado  que  estos  provocan  y  las  reacciones  de  los  mismos.       .

QPushButton button("Salir").   el   moc   se   encarga   de   hacerlo   automáticamente.   Una   señal   es   emitida   desde   cualquier   parte   del   código   de   la   clase   por   la   palabra   emit   (emit  nombre_signal(params).  Por  otro  lado  la  clase  QPushButton.28       Pongamos   un   ejemplo.&a.     Es  importante  tener  en  cuenta  las  firmas  de  las  señales  y  los  slots  que  son  conectados. char **argv){ QApplication a(argc.  las  propiedades  de  una  función  slot  son  las  siguientes:   Es  implementada  como  una  función  ordinaria.  por  lo  que  no  se   puede   conectar   cualquier   señal   con   cualquier   slot.  con  el  slot   quit()   de   la   aplicación.   pero   nunca   en   conexiones.   tiene   un   slot   llamado   quit()   que   simplemente  hace  que  la  aplicación  se  cierre  y  termine.   Los  slots  son  activados  por  las  señales  en  orden  arbitrario. return a.   Una  señal  es  una  función  que  siempre  retorna  void.   si   no   entre   compatibles   entre   sí.   La   clase   QApplication.  ya   que  como  regla  Qt  no  permite  crear.SLOT(quit())).SIGNAL(clicked()).  que  es   emitida  cuando  el  estado  del  botón  cambia  al  ser  pulsado  por  el  usuario.   y   en   consecuencia   una   vez   establecida   tal   conexión.exec().  ).  Y  se  declara  en  la  seccion  con  la  palabra  slots.   protected   o   public   de   una   clase.  ni  convertir  valores  entre  los  parámetros.  posee  una  signal  llamada  clicked().   Un   slot   puede   ser   declarado   en   la   zona   private.   Cualquier  número  de  señales  puede  ser  conectadas  a  un  mismo  slot.         Las  propiedades  de  una  señal  son  las  siguientes:   -­‐ -­‐ -­‐ -­‐ -­‐ -­‐ -­‐ Una  señal  es  declarada  en  la  sección  con  la  palabra  signals.   al   pulsar   el   botón.show(). QObject::connect(&button. }     -­‐ -­‐ -­‐ Bien.   #include <QApplication> #include <QObject> #include <QPushButton> int main(int argc.   conforme   a   la   citada  regla.   claro. int) ---------------------------> updateDialog()   -­‐ -­‐   .   Una  señal  puede  ser  conectada  a  cualquier  número  de  slots  a  la  vez. int) ---------------------------> setValue(int) rangeChanged(int.   Pondremos  ejemplos  de  compatibilidad:   rangeChanged(int.  que   sirve  para  implementar  botones  en  el  interfaz  gráfico.  Como  slot  siempre  podrá  ser   conectada   con   objetos   de   otra   clase   independientemente   de   la   zona   donde   se   haya   definido  en  su  clase. int) ---------------------------> setRange(int.   y   por   tanto   es   segura   tanto   en   llamadas   entre   threads   (hilos)  como  entre  sockets  (red).   Como   función   puede   devolver   un   valor   de   retorno.   eso   haría  que  la  aplicación  se  cerrará  y  terminará.   sólo   cuando  es  llamada  como  función.   Una   señal   nunca   puede   ser   implementada.  no  como  slot.   Es   como   una   llamada   directa.argv). int) rangeChanged(int.   pero   eso   sólo  le  afectará  si  es  llamada  como  función. button.   Puede  ser  llamada  como  una  función  ordinaria.  Así  que  podríamos  crear   una  conexión  entre  el  botón  y  la  aplicación  que  conecten  la  señal  clicked()  del  botón.

// nunca olvides esta parte m_angle = angle.   de   nuevo   un   Empty   Project   (un   proyecto   vacio).  también  slot.   eligiendo  Add  New…  .cpp.   y   de   esta  manera  ir  avanzando  en  el  desarrollo  de  aplicaciones  Qt  con  interfaz  gráfico. qreal angle() const.29     Y  ahora  de  incompatibilidad:   clicked()---------------------------> setValue(int)   textChanged(QString) --------> setValue(int) Vamos  a  ver  un  ejemplo  de  implementación  de  una  clase  con  signals  y  slots:   // Fichero angleobject.   y   si   no   es   así.  Ponemos  main.h class AngleObject : public QObject { Q_OBJECT Q_PROPERTY(real angle READ angle WRITE setAngle NOTIFY angleChanged) public: AngleObject(qreal angle. emit angleChanged(m_angle). QObject *parent=0).  haciendo  clic  derecho  sobre  la  carpeta. }     Como  puedes  ver  en  la  implementación  del  setter. // los setters son perfectos slots signals: void angleChanged(qreal).   el   valor   es   nuevo.  conteniendo  el  fichero  del  proyecto  que  el  Creator  ha  generado  él  solo.cpp   en  Name.     Ahora  vamos  a  añadir  un  fichero  fuente  main.       Es  muy  típico  que  dos  widgets  estén  conectados  entre  sí.  luego  C++  -­‐>  C++  Source  File  en  la  ventana  emergente.   salir   directamente.   y   le   vamos   a   llamar   “tempconverter”.     .   Vamos   a   ver   un   ejemplo   completo   de   un   convertidor   de   temperaturas  de  grados  Celsius  a  Fahrenheit  y  viceversa. public slots: void setAngle(qreal).   Esta   es   la   forma   en   como   se   implementan   señales   y   slots  en  las  clases  derivadas  de  QObject.   entonces  primeramente  actualizaremos  el  estado  del  mismo  y  finalmente  emitiremos  la  señal  de   que   ha   habido   un   cambio   en   dicho   estado.cpp void AngleObject::setAngle(qreal angle) { if(m_angle == angle) return.   revisando   si   el   valor   que   entra   de   angle.  de  manera  que  la  señal  de  uno   este   conectado   con   el   slot   del   otro.   Si.   primeramente   para   evitar   que   se   creen   bucles   infinitos   al   conectar   signal   y   slot.     Vamos   a   aprovechar   este   desarrollo   para   usar   todo   lo   aprendido   hasta   el   momento.  es  donde  tenemos  que.   Con   ello   ya   tenemos   la   carpeta   tempconverter  creada.   es   nuevo   o   no. }   // Parte del fichero angleobject.     Conversor  de  temperaturas       Vamos   a   abrir   un   nuevo   proyecto   en   el   Qt   Creator. // declarado en NOTIFY Q_PROPERTY arriba private: qreal m_angle.

QObject *parent = 0).0)*m_tempCelsius+32.  se  escriba  en  un  sistema  o  en  otro. private: int m_tempCelsius.  vamos  a  hacer  esto  en  el  slot  setTempCelsius.   para   que   sea   una   de   ellas   la   que   haga   el   cambio.  para  guardar. void tempFahrenheitChanged(int).h" TempConverter::TempConverter(QObject *parent) : QObject(parent) { m_tempCelsius = 0.   Como   class   name   le   ponemos  “TempConverter”.   inmediatamente  debe  cambiar  el  otro  y  viceversa.30       Ahora   creamos   Nuevo. // funciones getter int tempCelsius() const.  Le  damos  a  continue.  será  la  que  haga  el  cambio  a  Celsius.  y  como  Base  class  ponemos  QObject.cpp   con   parte   de   código   generado  automáticamente.       El  código  fuente  de  la  implementación  de  esta  clase  quedará  de  esta  forma.   si   cambia   uno.  Sin  embargo  vamos  a   usar   de   dos   funciones   setter. void setTempFahrenheit(int).h   y   tempconverter.  tenemos   que   cuidar   en   el   setter   poner   antes   de   nada   el   código   que   revise   el   cambio   de   estado. signals: void tempCelsiusChanged(int). int tempFahrenheit() const.       Puesto   que   ambos   diales   están   interconectados   entre   sí. } int TempConverter::tempCelsius() { return m_tempCelsius.  para  evitar  caer  en  un  bucle  infinito.0/5.       Vamos  a  ver  en  primer  lugar  el  código  que  describe  la  clase  y  sus  miembros  en  el  fichero   cabecera  correspondiente. public slots: // funciones setter y slots void setTempCelsius(int).  sólo  cambiar  el  valor  de  número   que  la  representa. return tempFahrenheit. // cortamos el bucle   .   ya  que  la  temperatura  es  una. } void TempConverter::setTempCelsius(int tempCelsius) { if(m_tempCelsius == tempCelsius) return.   Por   ejemplo   setTempFahrenheit. }.   #include "tempconverter.   es   decir. #endif // TEMPCONVERTER_H     Es  suficiente  con  usar  una  sola  variable  como  descriptora  del  estado  de  la  temperatura.   y   esta   vez   elegimos   C++   -­‐>   C++   Class.  Tomamos  por  tanto  la  escala  Celsius  como  la  principal.   #ifndef TEMPCONVERTER_H #define TEMPCONVERTER_H #include <QObject> class TempConverter : public QObject { Q_OBJECT public: TempConverter(int tempCelsius.  y  done  y   se   nos   han   creado   los   ficheros   tempconverter.   Como   el   estado  lo  hemos  definido  en  Celsius. } int TempConverter::tempFahrenheit() { int tempFahrenheit = (9.

 Pero  vamos  a  definir  ahora  el  interfaz  con  los  widgets   que  van  a  permitir  cambiar  la  temperatura  mediante  unos  diales. }     La  clase  TempConverter  que  acabamos  de  definir  va  a  ser  la  encargada  de  convertir  los   valores  de  Celsius  a  Fahrenheit  y  viceversa.0)*(tempFahrenheit-32).       .  Slot  -­‐>  setValue(int)   El  LCD  Celsius  (celsiusLcd)  =>  Slot  -­‐>  display(int)   El  LCD  Fahrenheit  (fahrenheitLcd)  =>  Slot  -­‐>  display(int)     La   forma   en   como   se   conectarían   estos   4   widgets   gráfico   con   la   clase   conversora   TempConverter.   queremos   que   cuando   giremos   los   diales. } void TempConverter::setTempFahrenheit(int tempFahrenheit) { int tempCelsius = (5.   cuales   son   las   señales   y   slots   que   vamos   a   usar   en   cada   elemento  gráfico:   El  dial  Celsius  (celsiusDial)  =>  Señal  -­‐>  valueChanged(int)  .   automáticamente   se   cambie   la   temperatura  de  ambos  LCD  cada  uno  con  sus  valores  de  acuerdo  a  su  escala  de  temperaturas.  sería  como  vemos  en  el  gráfico.31     m_tempCelsius = tempCelsius.   Como   aún   no   hemos   estudiado   las   ventanas   de   diálogo   con   widgets   y   layouts.   y   cuando   lleguemos   al   tema   14. emit tempFahrenheitChanged(tempFahrenheit()).   entonces  aprovecharemos  para  hacer  el  interfaz  gráfico  de  este  ejemplo.       Bien.       Bueno   pues   vamos   a   ver.   vamos   simplemente   a   definir   el   código   de   las   conexiones   implicadas.0/9. setTempCelsius(tempCelsius).  y   que  ambos  diales  giren  el  uno  por  acción  del  otro.  y  mostrarla  mediante  unos  LCD   numbers. // actualizamos el nuevo estado // emitimos las dos se_ales del cambio ocurrido emit tempCelsiusChanged(m_tempCelsius).  Slot  -­‐>  setValue(int)   El  dial  Fahrenheit  (fahrenheitDial)  =>  Señal  -­‐>  valueChanged(int)  .

  éste   ultimo   slot   emite   las   2   señales   tempCelsiusChanged(int)   y   tempFahrenheitChanged(int). celsiusLcd. SIGNAL(valueChanged(int)).SIGNAL(valueChanged(int)).tempConverter.fahrenheitDial. connect(tempConverter.   a   su   slot   setTempCelsius(int).   valueChanged(int).SLOT(setValue(int)). connect(fahrenheitDial.   Vamos   por   tanto   a   ver   el   código   que   implementaría   esas  conexiones.32       La   acción   comenzaría   al   girar   uno   de   los   diales. connect(fahrenheitDial.   el   de   los   grados   Celsius.   que   iría   a   2   sitios.                                             .celsiusDial.SLOT(setTempFahrenheit( int)).   y   a   TempConverter.   connect(celsiusDial.SLOT(setTempCelsius(int)).   Al   celsiusLCD   directamente   mediante   su   slot   display(int). SLOT(display(int)). connect(celsiusDial.   cada   una   conectada   con   los   slots   correspondientes   de   celsiusDial   y   fahrenheitDial.SIGNAL(tempFahrenheitChanged(int)).   supongamos   por   ejemplo.   Como   podemos   ver   en   el   código. connect(tempConverter.SIGNAL(tempCelsiusChanged(int)). SLOT(display(int)).   setValue(int)   en   ambos   casos.SIGNAL(valueChanged(int)). fahrenheitLcd.   De   esa   manera.   Esto   dispararía   la   señal   de   celsiusDial.tempConverter.   tenemos   todo   perfectamente   interconectado. SIGNAL(valueChanged(int)).SLOT(setValue( int)).

m.   representaría   las   relaciones   implicadas   en   este   método   de   conectar   elementos.. b=new QPushButton("1").  Como   vemos   en   el   último   connect.  como  un  marcador  telefónico.       Bien.  es  muy  interesante.  sin  embargo  esto  no  es  posible....1).   y   si   alguna   vez   nos   planteamos   hacer   algún   cambio. m. connect(b.33     TEMA  8   QSignalMapper       Una   vez   hemos   visto   como   funciona   el   sistema   de   conexiones   entre   objetos   en   Qt   mediante   señales   y   slots.   Si   buscas   información   de   la   misma   en   el   asistente. SLOT(map()). SIGNAL(mapped(int)).  tendremos  que  estar  atentos  en  hacerlo  en  10  sitios  diferentes.   de   acuerdo   al   botón   pulsado. SLOT(keyPressed(int))).  como  intermediaria  de  un  sistema  de  transmisión  de  señales  donde   necesitamos  enviar  de  alguna  manera  también  un  valor  junto  con  la  señal. SIGNAL(clicked()).   y   que   una   vez.  sería  crear  10  slots  para  cada  tecla.  supongamos  que  tenemos  un  conjunto   de  teclas  representando  los  números  del  0  al  9.2).   verás   que   es   bastante   sencilla   en   cuanto   a   miembros.. connect(b..  Por  ejemplo.   QSignalMapper *m = QSignalMapper(this).  nos  lleguemos  a  plantear  el  enviar  un  valor  junto   con  la  señal.  que  en  algunos  casos...   Mediante   el   método   setMapping   lo   que   hacemos   es   vincular   al   objeto   del   que   recibe   la   señal   el   QSignalMapper. connect(m.. QPushButton *b. b=new QPushButton("2"). this. SLOT(map()).   para   estos   casos   existe   una   clase   muy   útil   llamada   QSignalMapper.   El   gráfico   inferior.  sin  embargo.. m->setMapping(b.  pero  eso   supone   repetir   casi   el   mismo   código   diez   veces..   vamos   ahora   a   plantearnos   un   problema   más   complejo   de   afrontar   mediante  este  esquema  de  trabajo.   éste   generará   una  señal  mapped(int)  que  lleva  el  valor  entero  asignado  con  setMapping  a  dicho  objeto.       Claro  una  forma  de  implementar  este  caso.     Es  muy  normal..   slots  y  signals. SIGNAL(clicked()).   reciba   la   señal   en   el   slot   map(). .           ...  o  los  numerales  de   una   calculadora..   conectamos   dicha   señal   mapped(int)   con   el   slot   keyPressed(int)   que   podemos   ya   redefinirlo   para   llevar   a   cabo   las   acciones   que   queramos.   Nos   gustaría   sin   duda   el   poder   enviar   junto   con   la   señal   del   clicked()   el   valor   de   la  tecla  pulsada.     Vamos   conectando   cada   botón   mediante   la   señal   clicked()   al   slot   map()   del   QSignalMapper. m->setMapping(b.

34  

                                                           

 

35  

  TEMA  9   MANEJO  DE  CADENAS  EN  Qt      
Antes  de  seguir  con  la  programación  gráfica  (GUI),  veo  muy  importante  el  conocer  toda   una  serie  de  elementos  fundamentales  que  usaremos  en  los  programas  con  entorno  gráfico,  y  que   nos   permitirán   hacer   aplicaciones   útiles.   Muchos   libros   de   Qt,   enfocan   siempre   sólo   el   aspecto   gráfico,  e  incorporan  estos  elementos  junto  con  aquellos,  de  manera  que  a  veces  pueden  agobiar   al   que   empieza,   con   multitud   de   conceptos,   por   ello   aquí   he   preferido   separar   ambos   aspectos   completamente,  y  por  tanto  incluso  los  ejemplos  que  vamos  a  usar,  van  a  ir  desprovistos  de  GUI,   y  su  salida  será  en  la  consola.     Ya  hemos  visto  en  el  tema  4,  el  ejemplo  de  Hola  Mundo  con  salida  a  consola,  por  lo  que   usaremos  qDebug(),  para  nuestros  ejemplos.     Qt   es   un   Framework,   cuya   principal   motivación   es   la   de   que   usando   el   mismo   código,   podamos   compilarlo   en   diferentes   plataformas   sin   tener   que   hacer   cambios.   Por   ello,   ha   tenido   que   abstraer   muchos   de   los   tipos   que   ya   existían   en   C++/STL   para   hacerlo   completamente   multiplataforma.  Así  ocurre  con  el  tratamiento  de  cadenas,  en  el  que  std::string  ha  sido  mejorado,   añadiéndole   capacidad   de   representar   caracteres   unicote   (chino,   hebreo,   árabe,   etc),   y   de   esta   manera   internacionalizar   el   ámbito   aplicable   del   tratamiento   de   cadenas.   Esto   se   ha   hecho,   creando   una   clase   llamada   QString.     Si   la   buscas   en   el   Qt   Assistant   podrás   ver   todas   las   posibilidades   que   esta   clase   nos   brinda   para   el   manejo   de   cadenas   que   en   C,   siempre   fue   complicado,   frente   a   otros   lenguajes   de   programación.   Así,   QString   soporta   las   conversiones   a   otros  sistemas  (QString::toAscii,  QString::toLatin1  …  ),  permite  la  representación  en  unicode  de   prácticamente   la   totalidad   de   sistemas   de   escritura   actuales   y   también   permite   métodos   de   inspección  y  manipulación  de  cadenas  de  texto.     Construcción  de  cadenas     Hay  tres  métodos  para  construir  cadenas  por  concatenación:  

1.-­‐  Mediante  el  operador  +  
QString res = "Hola " + "amigos ";

  2.-­‐  Mediante  el  operador  %  de  QStringBuilder  
#include <QStringBuilder> .............. QString str = "Hola " % "amigos";

  3.-­‐  Mediante  el  método  arg  de  QString  
QString str = QString("Hola %1, tengo %2 años").arg("amigos").arg(42);

    El   método   1,   es   mejor   que   el   2   si   se   va   a   componer   la   cadena   en   muchas   veces,   con   muchas   asignaciones.   El   método   3   permite   formatear   la   cadena,   y   dejar   los   huecos   que   luego   serán   rellenados   por   orden,   por   el   método   arg   que   permite   introducir   tanto   cadenas   como   números.      

 

36  

 
Subcadenas   Vamos   a   ver   una   serie   de   métodos   para   tratar   subcadenas   dentro   de   cadenas   más   grandes.   Funciones  left,  right,  mid,  replace:  
QString str = "Hola mundo"; str.left(4); // "Hola" str.right(5); // "mundo" str.mid(3,5); // "a mun" str.replace("mundo","tronco"); // Hola tronco

 

  qDebug()  y  QString  (salidas  por  consola)     qDebug   se   usa   para   sacar   mensajes   por   consola.   Incluído   en   la   clase   QDebug,   tiene   diferentes  formas  de  trabajar.   Como   printf()   en   C   pero   añadiendo   un   retorno   de   carro   “\n”.   Para   imprimir   un   QString   antes   debe   ser   pasado   por   la   funcion   qPrintable   incluida   en   QtGlobal   (no   requiere   por   tanto   incluir   ninguna  cabecera  para  su  uso):  
QString str = "Antonio"; qDebug("Hola me llamo %s, y tengo %d años",qPrintable(str),42);

  También  podemos  usarlo  como  un  stream,  como  std::cout  de  C++/STL:  
qDebug() << "Hola me llamo " << str << ", y tengo " << 42 << " años";

    Números  y  cadenas   Convertir  un  número  en  una  cadena:  
QString years = QString::number(42); // "42"

  Convertir  de  cadena  a  número:  
bool ok; years.toInt(&ok); // ok = true si lo convirtió bien

También  existe  toDouble(),  toFloat(),  etc.       STL  y  QString       Si   usamos   una   librería   creada   con   las   STL,   y   queremos   hacer   un   interfaz   con   ella,   para   recibir  cadenas  de  ella,  o  enviarle  cadenas  hacia  ella,  entonces  tendremos  que  adaptar  la  cadena   para  que  todo  funcione  bien.  Para  ello  existen  unas  funciones  que  vamos  a  ver.  
std::string str = "Hola mundo"; QString qstr = QString::fromStdString(str);

 
QString qstr = "Hola mundo"; std::string str = QString::fromStdString(qstr);

 

Se  supone  que  str  está  en  ASCII.    

 

/* ciudades = {Madrid.   En   realidad  es  una  QList<QString>. // str esta apuntando a un espacio vacío str.Bilbao . i++){ qDebug() << verbos[i].37     Cadenas  vacías  y  cadenas  nulas     Decimos  que  una  cadena  es  nula. i < verbos. verbos = "correr" << "comer" << "coser". // "Madrid. // {comer.  similar  a  una  array  de  cadenas. Sevilla} */ QString new_str = ciudades.replaceInStrings("er"."). coser} verbos. // {comer}     Iterando  sobre  QStringList     Podemos   movernos   por   una   QStringList   usando   el   operador   []   con   ayuda   de   lenght()   para  no  sobrepasar  sus  dimensiones. "). // true QString str = ""."iendo"). // true str.     Esta  lista  puede  ser  rellenada  usando  el  operador  <<  como  en  un  stream.   QString str = "Madrid .   QStringList verbos. }     .   que   son   muy   interesantes:   verbos.  si  no  está  apuntando  a  ninguna  cadena. // {corriendo.join(".length().at(i).isNull(). Barcelona.split(" . // {comer. // false str.isEmpty(). QStringList ciudades = str.isNull(). // {correr.  y  decimos  que   está  vacía  si  no  contiene  ningún  carácter. Barcelona.  que  vamos  a  ver  ahora.       QStringList     Es   una   clase   especializada   en   contener   QStrings   en   un   contener   tipo   lista   (QList). coser} for(int i.   QStringList verbos. correr. // verbos.   todas   las   cadenas   que   lleva   consigo. comiendo. Bilbao. comer} verbos. verbos = "correr" << "comer" << "coser". comer. comer.Barcelona . // {comer} verbos << "comer".filter("m").isEmpty().     Quedará  aclarado  con  este  ejemplo:   QString str = QString.removeDuplicates(). coser}     QStringList   posee   métodos   para   tratar. Bilbao. cosiendo} verbos. // str no esta apuntando a nada str. // {correr. // true       Romper  y  unir  cadenas   Métodos  split  y  join.sort().Sevilla".  o  con  la  propiedad  de  solo  lectura  at(). Sevilla"   QStringList  es  un  contenedor.

 desde  el  primero  hasta  el  último.  a  la  derecha  como  segundo  parámetro  esta  una   colección   de   elementos   de   un   determinado   tipo.38       Pero  la  forma  más  cómoda  y  segura  de  recorrer  una  QStringList  es  con  foreach. }     Usamos  una  referencia  const  para  optimizar  el  rendimiento  del  bucle. verbos){ qDebug() << verbo.  pasando  solo  una   referencia.       Foreach  siempre  tiene  la  misma  forma.     Bien.                                               .   y   a   la   izquierda   como   primer   parámetro.   que   es   la   variable   que   va   a   ir   recogiendo   los   valores  recorridos  de  la  colección.   ponemos   una   variable   del   tipo   de   los   elementos.   Es   conveniente   que   pruebes   el  código  que  hemos  ido  viendo  en  este  tema.   foreach(const QString &verbo.  incluyéndolo  en  el  esqueleto  de  una  aplicación  de   consola  usando  qDebug().  incluído   en  QtGlobal  por  defecto.   en   el   próximo   tema   vamos   a   ver   las   colecciones   en   Qt.

// 2.4. // 2. pares.   QList<int> pares.  Los  métodos  usados  son  los  mismos  que   QList.6.  de  manera  que  si  tenemos  una  lista  de  objetos.4.6.39     TEMA  10   COLECCIONES  EN  Qt       Las  colecciones  en  Qt  están  fundamentadas  sobre  las  mismas  de  STL. pares){ qDebug("Numero par: %d".4.next()).  Vamos  a  ir  viéndolas  una  a  una.  takeFirst().  Vamos  a  ver   unos  ejemplos  sencillos. while (i.removeFirst().  son  QLinkedList  que  implementa  una  lista  vinculada.4.8 pares.  y  la  función  value().8 pares.8.   y  QVector  que  es  una  colección  vector  como  las  de  STL.8.8).insert(4. }     Otras  alternativas  similares  a  QList.valor). // 0.2.10 pares. // 2. // 0.10 pares.  pero   llevadas  a  una  implementación  multiplataforma.     Podemos  acceder  a  sus  elementos  mediante  el  operador  [].  usaremos  una  lista.  y  la  forma  de  rellenarla  es  igual.  Dependiendo  del  uso  que  les  vayamos  a  hacer.         .  como  las  de  STL.hasNext()){ qDebug("Numero par: %d". // 0.i.append(10).4.removeAt(2). // nada     Con  takeAt().4.6.removeAll(). }     También  podemos  usar  foreach  para  iterar  toda  la  lista  completa:   foreach(const int &valor.       QListIterator<int> i(pares).     Para   iterar   una   lista   usamos   QListIterator<T>.10 pares.removeLast(). pares << 2 << 4 << 6.6.prepend(0).  pues  nos  devolvería  la  referencia  a  dicho   objeto.6 pares.2.  takeLast().  nos  devuelve  el  elemento  en  sí  y  además  lo  borra  de   la  lista.   Que   tiene   funciones   como   hasNext()   para  asegurarse  que  no  hemos  llegado  al  final.  una  lista  vinculada  o  un   vector.2.     QList<T>       Es  una  lista.

  hubiéramos   tenido  que  usar  un  QMultiSet.   Veamos   ejemplos:   foreach(const QString &key. // true y borra el 3     Podemos  también  convertir  una  lista  en  un  conjunto  y  viceversa:   QList<int> pares.cola.     La  función  key()  recoge  el  valor  del  primer  elemento. qDebug("Top: %d".4. cola. pila.enqueue (2). // false conjunto. pila.T>  y  QHash<keyT.cola. qDebug("Top: %d".pop()). qDebug("Top: %d".  keys()  devuelve  una  QList<keyT>   con   todas   las   keys.pop()). edades. cola.dequeue()). // true   // // // // 1 1 2 3 Conjuntos  con  QSet   Los  conjuntos  o  sets.contains(3).  Para  introducir  elementos  se  hace  de  la  siguiente  manera:   QMap<QString.pila. pila.4.toSet().  como  en  las  STL  (o  las  matrices  de  PHP).cola.qPrintable(key)). qDebug("Top: %d".dequeue()).remove(8). // 2.4. 8.     QSet<int> conjunto.40     Pilas  con  QStack   QStack<int> pila. = 42.8 QSet<int> conjunto = pares.top()).2.isEmpty().pop()). pares << 2 << 2 << 4 << 4 << 8. pila. qDebug("Top: %d".   y   value(key)   devuelve   el   valor   asociado   con   un   key   en   particular.   Si   hubiéramos   querido   conservar   los   elementos   repetidos   en   el   conjunto.remove(3). 42.enqueue (3).contains(8).T>       Se  trata  de  pares  key  =>  value.  también  podemos  asimilarlos  a  los  Set  de  STL. // true   // // // // 3 3 2 1 Colas  con  QQueue   QQueue<int> cola. cola.pila.head()).cola.int> edades["Antonio"] edades["Maria"] = edades["Pablo"] = edades.pila.push(2).8 QList<int> pares_no_repes = conjunto. // 2.enqueue(1).push(1). conjunto << 1 << 3 << 7 << 9. qDebug("Top: %d".keys()){ qDebug("Nombre: %s". // true conjunto.  donde  una  key   se  corresponde  con  un  único  value.toList().       QMap<keyT. pila.push(3).isEmpty().pila. conjunto. qDebug("Top: %d". qDebug("Top: %d".  Vamos  a  ver  un  poco   de  código  para  asimilarlos.dequeue()). // false y no borra nada conjunto. }     .

  key2).   se  usa  la  función  uniqueKeys().value("Pablo")).   Dicha  función  hash. "PHP").  ha  de  escogerse  de  manera  que  no  se  repita  ningún  valor  uint  que  dificulte  su   rendimiento.  debe  de  proveer  de  2  métodos:  uint  qHash  (tipoKey).T>.  existen  las  versiones  QMultiMap  y  QMultiHash. lang.   solo   que   la   key   se   pasa   por   una   función   hash.  para  cuando  una  misma   key.   como   miembros   de   una   clase   hipotética   Persona.   que   tiene   dos  miembros  básicos  llamados  edad()  y  nombre(). Java. Erlang} solo pasa una vez el bucle }                         .insert("Antonio".  Si  se  quiere  recoger  el  valor  key  una  sola  vez.nombre()) && (p1.   QHash<keyT.  En  estos  casos.values(nombre).nombre() == p2.   uint Persona::qHash (const Persona &p) { return p.  Para  ello  el  tipo  de  key.insert("Antonio".  Veamos  un  ejemplo  para  ilustrarlo.insert("Antonio". lang.insert("Antonio". }     QHash   es   lo   mismo   que   QMap.   la   cual   permite   mayor   rendimiento   a   la   colección. const Persona &p2) { return (p1.   Si   así   se   hace   de   manera   óptima.  podemos  ver  si  una  determinada  key  esta  en  la  colección.contains("Pablo")){ qDebug("Pablo tiene %d". foreach(const QString &nombre. lang.   if(edades.nombre()). } bool Persona::operator== (const Persona &p1.   QMultiMap<QString. // {C/C++.   Veamos   un   ejemplo   de   implementación   de   estas   funciones. lang. "C/C++").edad() + qHash(p.QString> lang. edades. }     Con  esto  ya  podríamos  crear  un  QHash<Person. lang. "Erlang").nombre()).edad() == p2.41       Con  la  función  contains(key).T>   dará   mejor   rendimiento   en   todas   las   operaciones   que   QMap<keyT.  y  bool   operator==   (key1. PHP.int>. "Java").  puede  tener  asociados  varios  valores  a  la  vez.uniqueKeys()){ QStringList program_lang = lang.  que  trabaje  de  forma  óptima.  y  se  utiliza  insert().  no  se  puede  usar  el  operador  []   para  introducir  nuevos  datos.     Para  finalizar.   para   convertirla   en   un   valor   uint   (unsigned   int).

42                                                                 .

43  

  TEMA  11   TIPOS  Qt  
    Como   en   el   tema   anterior   hemos   visto,   debido     a   que   el   propósito   de   Qt   es   poder   ser   multiplataforma  sin  tener  que  modificar  el  código,  otra  cosa  que  hubo  que  modificar  son  los  tipos   como  los  enteros,  que  dependen  del  sistema  operativo,  y  de  la  CPU.     Así  los  tipos  quedan  fijados  de  esta  manera  en  QtGlobal:   qint8  -­‐-­‐-­‐-­‐-­‐-­‐>  8  bits  entero   qint16  -­‐-­‐-­‐-­‐-­‐-­‐>  16  bits  entero   qint32  -­‐-­‐-­‐-­‐-­‐-­‐>  32  bits  entero   qint64  -­‐-­‐-­‐-­‐-­‐-­‐>  64  bits  entero   quint8  -­‐-­‐-­‐-­‐-­‐-­‐>  8  bits  entero  sin  signo   quint16  -­‐-­‐-­‐-­‐-­‐-­‐>  16  bits  entero  sin  signo   quint32  -­‐-­‐-­‐-­‐-­‐-­‐>  32  bits  entero  sin  signo     quint64  -­‐-­‐-­‐-­‐-­‐-­‐>  64  bits  entero  sin  signo   qreal  -­‐-­‐-­‐-­‐-­‐-­‐>  double     QVariant     Es  un  tipo  que  puede  guardar  un  tipo  cualquiera.  Si  necesitamos  ser  capaces  de  devolver   cualquier   tipo,   a   través   de   un   interface   QVariant   nos   puede   ayudar,   y   finamente   podemos   convertirlo   a   su   tipo   final   mediante   las   funciones   del   tipo   toTipo()   (ej.   toInt(),   toFloat()).   Veamos   un  ejemplo.  
QVariant var(5); int i; i=var.toInt(); // 5

    Casting  dinámico     Para   hacer   casting   dinámico   entre   clases,   pero   sin   requerir   el   uso   del   RTTI   del   compilador,  en  Qt  existe  el  operador  en  QtGlobal  llamado  qobject_casting.       Si  no  tenemos  claro,  si  el  casting  se  va  a  resolver  bien,  entonces  usaremos  este  tipo  de   casting,  ya  que  de  no  resolverse  bien,  devolverá  un  puntero  nulo.    
QObject *obj = new QTimer; // QTimer hereda de QObject

QTimer *timer = qobject_cast<QTimer *>(obj); // timer == (QObject *)obj QAbstractButton *button = qobject_cast<QAbstractButton *>(obj); // button == 0

 

 

 

44  

                                                           

 

45  

  TEMA  12   SISTEMAS  DE  FICHEROS    
  Otro   de   los   sitios   donde   está   muy   justificado   el   uso   de   un   sistema   independiente   de   la   plataforma  es  en  los  accesos  a  sistemas  de  ficheros,  ya  que  en  cada  operativo,  esto  es  diferente.   De   esta   manera   surge   una   serie   de   clases   en   Qt   que   nos   ayudan   a   abstraernos   de   dichas   particularidades.     QDir     Esta   clase   nos   provee   del   manejo   del   sistema   de   ficheros   de   manera   transparente.   Los   directorios  más  típicos  están  definidos  como  métodos  estáticos.  
QDir dir = QDir::current(); // directorio actual QDir dir = QDir::home(); // /home QDir dir = QDir::temp(); // directorio temporal QDir dir = QDir::root(); // directorio raiz QDir dir = QDir(QApplication::applicationDirPath()); // directorio de la aplicacion  

QFileInfo   es   una   clase   que   puede   contener   toda   la   información   referente   a   un   fichero   (nombre,  path,  permisos,  etc).  QFileInfoList  es  una  QList<QFileInfo>.  Una  función  muy  imporante   de   QDir   es   entryInfoList()   que   devuelve   toda   lista   de   un   directorio   (es   como   un   dir   o   un   ls).   Veamos  un  ejemplo  de  cómo  listar  un  directorio.  
QFileInfoList infos = QDir::root().entryInfoList(); foreach(const QFileInfo &info, infos){ qDebug("%s",qPrintable(info.fileName())); }

  Se   podría   hacer   un   filtrado   de   dicho   listado   usando   los   siguientes   filtros,   puestos   y   combinados  con  OR  (|)  como  primer  parámetro  de  entryInfoList(filter,sort):   QDir::Dirs   QDir::  Files                       directorios   ficheros   no  vínculos    

QDir::NoSymLinks   QDir::Readable     QDir::Writeable     QDir::Executable   QDir::Hidden   QDir::System      

con  premiso  de  lectura   con  permiso  de  escritura   con  permiso  de  ejecución   ficheros  ocultos   ficheros  de  sistema  

El  orden   de   listado   se   puede   enviar   como   segundo   parámetro   y   estos   son   los   filtros   de   ordenación:   QDir::Name   QDir::Type   QDir::Size               por  nombre   por  tipo  (extensión)   por  tamaño  

 

 etc).qPrintable(info.QDir::Files.  Veamos  un  ejemplo:   QFileInfoList infos = QDir::root().cpp". }   Las  distintas  partes  del  path  de  un  fichero  están  gráficamente  relatadas  en  el  siguiente   gráfico.h.entryInfoList(QStringList << "*.  se  le  puede  añadir  un  parámetro  que  iría  antes  de  que  estos  con  una  QStringList  con  filtros   wildcard  (del  tipo  *.QDir::Name).46     QDir::Time             por  fecha  de  creación   directorios  primero   directorios  lo  último   en  orden  inverso   QDir::DirsFirst     QDir::DirsLast     QDir::Reversed     EntryInfoList   también   tiene   un   constructor   donde   delante   de   estos   parámetros   vistos   antes.  *.                                   . foreach(const QFileInfo &info.fileName())). infos){ qDebug("%s".cpp.h" << "*.

atEnd()){ qDebug("%s".open(QIODevice::ReadOnly) qFatal("No puedo abrir el fichero para lectura.close(). if(!f. QTextStream in(&f).").  y  read()  y  similares  para  la  lectura.         .  close()  para  el  cierre.atEnd()){ QByteArray data = f.   aunque   también   se   puede   usar   con   cualquier   otro   dispositivo. if(!f.  De  estas  clases   vamos   a   usar   solamente   el   constructor.  es  la  que  se  usa  para  la  operación  de  apertura.  y  son  los  siguientes:   QIODevice::ReadOnly QIODevice::WriteOnly QIODevice::ReadWrite QIODevice::Append QIODevice::Text abierto para lectura abierto para escritura abierto para lectura y escritura abierto para añadir al final del fichero cuando lee los enter los pasa a “\n” y cuando escribe.     La  clase  QFile  que  deriva  de  QIODevice. while(!in.   tanto  en  el  caso  de  que  sea  un  fichero  binario. } f.  tenemos  que  al  menos  implicar  3  clases  diferentes.  Bien  vamos  a  ver  cada  una  de   esas  clases.   Veamos  un  ejemplo  de  lectura  de  fichero  de  texto  usando  un  stream:   QFile f("fichero.   indicando   como   parámetro   el   dispositivo   de   entrada/salida.txt").close().").  y  la  manera  en  que  se  relacionan  en  el  proceso  de  lectura  y  escritura.txt"). while(!f.open(QIODevice::ReadOnly)) qFatal("No puedo abrir el fichero para lectura.  Los  flags  que  representan  los  diferentes  modos  de  acceso  al   fichero  están  definidos  como  un  enum  en  QIODevice.   escritura.   Estas   clases   son   QTextStream  para  flujos  de  texto.  y  QDataStream  para  flujos  de  datos  binarios.  o  seek()  para  situarnos  en  una   posición  concreta  del  fichero.   Las   clases   interface.read(1024).  vamos  a  usar  los  métodos  open()  para  la  apertura  del  fichero. // Aqui puedes usar los datos leidos } f.   write()  para  la  escritura  en  el  mismo.  y  de   la  clase  QFile.   que   se   encargan   de   crear   un   stream   con   los   datos   que   proceden   o   van   al   fichero   en   nuestro   caso.47     TEMA  13   ESCRITURA  Y  LECTURA  DE  FICHEROS       Para  la  lectura  y  escritura  en  ficheros.  Veamos  un  ejemplo  de  lectura:         QFile f("fichero.  lectura  y  cierre  del  archivo.readLine())).  como  si  es  de  texto.qPrintable(in. los enter los pasa al sistema local   De  la  clase  QIODevice  vamos  a  usar  meramente  los  modos  de  acceso  a  los  ficheros.  También  vamos  a  usar   métodos  como  atEnd()  para  reconocer  el  EOF  (final  del  fichero).       Esto   mismo   puede   ser   hecho   de   una   manera   más   intuitiva   usando   streams.

nombre(). edad).   debemos   decir   al   compilador   de   Qt   la   versión   que   estamos   usando   del   mismo. return out.  Vamos  a  ver  un  ejemplo  de  nuevo  con  la   clase  Persona  y  sus  miembros  edad()  y  nombre(). int edad. out << p. } QDataStream Persona::&operator>> (QDataStream &in. QDataStream out(&f). if(!f.  Veamos  un  ejemplo  de  escritura.   la   implementación   del   datastream  sea  diferente.open(QIODevice::WriteOnly | QIODevice::Append) qFatal("No puedo abrir el fichero para escritura.  simplemente  si   en  la  misma  implementamos  los  operadores  <<  y  >>. out << 0xFFFC.   En   el   caso   de   leer   o   escribir   datos.close(). f."). return in. f. QTextStream out(&f). if(!f. in >> edad.edad(). const Persona &p) { out << p.48     Veamos  ahora  un  ejemplo  de  escritura  de  fichero  de  texto:   QFile f("fichero. in >> nombre. const Persona &p) { QString nombre.txt"). p = Persona(nombre. out << "Esto es un fichero de texto" << str << endl.   QFile f("fichero. }                     .   QDataStream Persona::&operator<< (QDataStream &out. out.close().setVersion(QDataStream::Qt_4_6).open(QIODevice::WriteOnly) qFatal("No puedo abrir el fichero para escritura").bin").     Streams  y  Tipos     Se  puede  serializar  a  un  fichero  una  clase  cualquier  creada  por  nosotros.   ya   que   es   posible   que   en   diferentes   versiones.

  que   se   superpongan   a   la   principal   en   el   momento   en   que   piden   alguna   acción   por   parte   del   usuario.   hasta   el   tema   20   incluido.  cuando  el  usuario  decide  cerrar  la  aplicación.   esta   compuesta   a   su   vez   por   unos   elementos   gráficos   básicos  que  dividiremos  en  2  tipos:  los  widgets  y  los  layouts.         .   los   demás   se   deseleccionen   automáticamente.  Desde  el  tema  21  en  adelante.   En   Qt   se   usan   unos   widgets   llamados   spacers   (vertical   y   horizontal)   que   actúan   como   muelles.     Los   widgets   son   elementos   gráficos   interactivos   o   no.  pueden  haber  ventanas  de  Diálogo.   es   decir.   para   organizar   su   posición   en   ella.  es  que  son  capaces  de  recibir  eventos  desde   los   dispositivos   de   entrada   (por   ejemplo   el   ratón).   el   estado   del   widget   es   cambiado.     Los  widgets     En  el  Qt  Designer  nos  podemos  encontrar  más  de  45  widgets  para  usar.   cuando   metemos   varios   radio-­‐buttons   (QRadioButton)   dentro   de   group-­‐box   (QGroupBox).   emite   una   señal   notificando   tal   cambio.  pulsando  en  botones.   un   cuadro   de   selección.   Cualquiera   de   estas   ventanas.   y   vuelva   sobre   los   13   temas   que   acabamos   de   pasar.  y  ejercicios  posibles  para  afianzarlos  bien.   Los   widgets   están   organizados   en   categorías.  que  con  una   base  sólida  en  los  2  bloques  anteriores  (1  al  13  y  14  al  20).   traduciéndolo   a   diferentes   idiomas.   vamos   a   ver   los   conceptos   básicos   y   todas  las  herramientas  necesarias  para  desarrollar  un  interfaz  gráfico  completo.  A  parte.  Toda   aplicación  tiene  una  ventana  principal.   miden   diferente   en   cada   idioma.49       TEMA  14   WIDGETS  Y  LAYOUTS       Los   primeros   temas.   donde   los   textos.   contenían   los   principios   básicos   que   diferencian   a   la   programación   C++/STL   de   la   programación   C++/Qt.   Layouts   y   widgets   negocian   el   espacio   a   ocupar.       Un   interfaz   gráfico   muestra   al   usuario   ventanas   con   elementos   gráficos   con   los   que   puede  interactuar.   es   el   momento   de   que   se   pare.  ni  gráficos.  y  además  pueden  actuar  en  su  interactividad.  que  se  adapta  a  las  dimensiones  que  se  quiera  dar  a  la  ventana  que  los  contiene.     Desde   el   tema   actual.   empujando   los   widgets   hacia   una   zona   de   la   ventana.  ya  que  QWidget  deriva  de  aquél.   y   una   categoría   importante   son   los   widgets   contenedores.   y   donde   no   queremos   que   se   trunquen  ni  textos.   que   pueden   contener   en   su   interior   otros   widgets.  Es  fundamental   por   tanto   que   trate   este   bloque   de   temas   como   un   todo.  ya  se  tratan  particularidades  y  ampliaciones.  etc.   etc.  y  cerca  de  60  son   derivados   de   la   clase   principal   QWidget.  seleccionando  opciones.   Si   esto   no   es   así.   como   un   botón.   Cuando   por   causa   de   un   evento.   o   por   otra   causa.  y  que  haya  hecho  todos  los  ejemplos.   por   lo   que   se   puede   decir   que   los   widgets   contenedores   pueden   afectar   y   modificar  la  interactividad  de  un  widget  contenido  en  él.   y   que   se   ejercite   en   ellos   antes   de   seguir   adelante.  que  es  la  que  se  abre  al  principio  y  se  mantiene  hasta  el   final.   Un   Layout   es   un   componente   que   se   usa   para   disponer   espacialmente   los   widgets   dentro   de   la   ventana.   Los   widgets   por   tanto  pueden  conectarse  con  otros  widgets  y  objetos.   y   es   muy   eficaz   en   el   caso   de   querer   internacionalizar   un   interfaz.   una   etiqueta.     Otra  propiedad  importante  de  los  widgets.  introduciendo  texto.  podrá  avanzar  por  ellos  de  manera   segura  y  fiable.  mediante  el  mecanismo  de  signal-­‐slot  que   vimos  en  los  QObjects.   como   por   ejemplo.   con   todo   esto   se   consigue   un   interfaz  elástico.   que   se   dibujan   dentro   de   una   ventana   ocupando   un   área   rectangular.  de  manera  que  sirven  para  organizarlos.   Es   fundamental   que   tenga   todos   esos   conceptos  claros.   conseguimos   que   al   seleccionar   uno   de   ellos.   del   1   al   13.

  y   de   esta   manera.   dándole   más   consistencia   al   interfaz   gráfico   creado   por   objetos   continentes   y   objetos   contenidos.   evitando   así   problemas   de   memory   leaks.  Por  lo  tanto.   se   puede   dibujar   un   árbol   jerárquico   que   recree   las   relaciones  parentales  de  todos  sus  objetos. int stretch = 0 ) Añade   un   Layout   dentro   de   otro   (cajas   pequeñas   dentro   de   cajas   más   grandes).   primero   en   código   puro.  facilita  también  la   labor  de  encontrar  fácilmente  los  elementos  de  la  programación  que  requerimos.     Vamos  por  tanto  a  continuación  a  plantearnos  la  construcción  de  un  cuadro  de  diálogo.   QVBoxLayout   (   que   organiza   el   espacio   que   ocupan   los   widgets   verticamente)   y   QGridLayout   (que   organiza   el   espacio   que   ocupan   los   widgets.   evitando   fugas   de   memoria.   Hay   3   tipos   diferentes   de   layouts:   QHBoxLayout   (que   organiza   el   espacio   que   ocupan   los   widgets   horizontalmente).     Un   Layout.   hasta   el   límite   de   su   contenedor.  todo  el  contenido  de  una  ventana  debe   estar  sometido  jerárquicamente  al  objeto  que  representa  la  ventana  completa.   negocian   su   tamaño   y   el   espacio   para   crear   un   interfaz   elástico  adaptable  a  todas  las  situaciones.   Por   tanto.   y   cuando   estas   funciones   son   usadas.   dividiendo   el   espacio   en   filas   y   columnas.   De   esta   manera   dejaremos   concretada   la   enseñanza   que   pretendíamos   para   este   tema.   y   sus   propiedades   son   muchas.   que   es   el   diseño   de   ventanas   usando   Layouts   y   Widgets.  Así  cuando  dicha   ventana   sea   borrada   de   la   memoria.             .   y   luego   en   el   Qt   Designer.   en   toda   ventana   gráfica.  Este  mecanismo  facilita  entonces  la  transmisión  de  eventos  y  propiedades  a  lo  largo   de   la   jerarquía   familiar.   y   sus   métodos   son   muchos.  Desde  ese  momento  dicho  widget  pasa  a   ser  hijo  del  Layout  que  lo  contiene.   sobre   un   grid.   Los   spacers.     De   hecho.   y   aprendiendo   a   usar   esta   documentación  como  guía  a  la  programación.  El  autorrellenado  del  Qt  Creator.   layouts   y   widgets.   empujando   al   widget   en   esa   dirección   (la   del   contenedor).   También   facilita   el   mecanismo   de   liberación   de   memoria   que   vimos   en   el   tema   6.   void QLayout::addLayout (QLayout *layout.   inmediatamente   dicho   contenido   pasa   a   ser   hijo   de   la   clase   contenedora.     Puesto   que   hay   muchos   widgets.50     Los  Layouts     Los   objetos   layout   derivan   de   la   clase   QLayout.   Es   muy   habitual   por   tanto   que   los   elementos   gráficos   de   una   ventana  sean  creados  mediante  new  dentro  del  constructor  del  objeto  que  hará  de  ventana.   no   es   propósito   de   este   libro   el   verlos   todos.   es   conveniente   que   conforme   los   vayas   necesitando.   void QLayout::addWidget (QWidget *w) Añade  un  widget  dentro  del  Layout  del  tipo  que  sea  éste.   Así   QLayout   posee   unas   funciones   miembro   especiales   para   añadir   estos   contenidos   a   su   interior.   Veamos   las   3   funciones   básicas   para   añadir   elementos  a  un  Layout:   void QBoxLayout::addStretch ( int stretch= 0 ) Añade   un   spacer   que   ocupará   por   defecto   como   mínimo   0   pixeles.   y   situando   a   cada   widget   en   la   celda   que   le   corresponda).   Sólo   BoxLayouts   verticales  y  horizontales  (no  vale  para  GridLayouts).   rellenan   los   espacios   vacíos.   y   por   herencia   a   su   vez   de   las   cosas   que  aquél  contenga.   los   vayas   revisando   en   el   Qt   Assistant.   con   ella   se   borrarán   todos   sus   elementos.   puede   contener   en   su   interior   otros   Layouts   y   otros   Widgets.   Desde   ese   momento   dicho   layout   pasará   a   ser   hijo   de   su   contenedor.

 mientras  que  la  ComboBox  sí  la   hemos  asignado  a  la   variable  c.   QHBoxLayout *groupLayout = new QHBoxLayout(this).  para  más  adelante  poder  añadirle  ítems  de  selección  mediante  dicha  variable.  Por  lo  que  añadimos  debajo   de  la  primera  línea  del  código  de  antes:   QComboBox *c. groupLayout->addWidget(colorGroup). buttonsLayout->addWidget(new QPushButton("Cancel")). QHBoxLayout *buttonsLayout = new QHBoxLayout(this).  Primeramente   vamos  a  añadirle  a  groupLayout  los  2  GroupBoxes.       Podemos  observar  que  primeramente  hay  3  cajas  horizontales  (rectángulos  rojos).   al  que  luego  queremos  añadirle  ítems  de  donde  seleccionar  opciones.     Vamos  ahora  a  por  la  caja  central  que  es  la  más  compleja  de  este  ejemplo.     . groupLayout->addWidget(orientationGroup).   que   son   3. topLayout->addWidget(new QLabel("Printer:")).   QHBoxLayout *buttonsLayout = new QHBoxLayout(this).  no  requerimos   acceder  a  ninguna  variable  que  la  maneje.   Vamos   a   suponer   que  la  construcción  de  todo  ello  es  hecho  en  el  constructor  de  la  ventana  de  diálogo. buttonsLayout->addWidget(new QPushButton("Print")).   QHBoxLayout *topLayout = new QHBoxLayout(this). QGroupBox *colorGroup = new QGroupBox().  ya  que  no  requerimos  hacerle  nada  en  el  código  más  adelante. topLayout->addWidget(c=new QComboBox()).  por  lo  que   los   contendores   superiores.   pero   no   la   hemos   asignado   a   ninguna  variable. QHBoxLayout *groupLayout = new QHBoxLayout(this). QHBoxLayout *topLayout = new QHBoxLayout(this).   donde   tenemos   que   colocar   un   spacer   primero. QGroupBox *orientationGroup = new QGroupBox().   tomarán   como   padre   a   la   ventana   y   por   tanto   usaremos   el   puntero  this  como  argumento  de  los  constructores  de  los  mismos.     Date   cuenta   de   que   la   Label   sólo   la   hemos   instanciado.  que   son   contenedores   Layout   que   en   su   interior   tienen   widgets   o   spacers   (stretch).51     Construcción  de  una  ventana  de  Diálogo  con  código     Vamos  a  construir  una  ventana  de  diálogo  que  tenga  el  aspecto  de  la  foto  que  mostramos   a  continuación.       Empecemos  por  el  topLayout  donde  hay  un  QLabel  con  el  texto  Printer  y  un  QComboBox. buttonsLayout->addStretch().   y   luego  los  2  botones.     Ahora   vamos   a   por   el   buttonsLayout.

// nueva groupLayout->addWidget(colorGroup).  Bien. colorLayout->addWidget(new QRadioButton("Black and White")).   Como   este   QVBoxLayout. groupLayout->addWidget(orientationGroup).  ahora  vamos  a  usar  un  Layout  vertical  dentro  de  cada  GrupoBox  para  organizar  el   espacio  de  los  widgets  que  van  a  ir  dentro. // Layout superior QHBoxLayout *topLayout = new QHBoxLayout(). QComboBox *c. orientationLayout->addWidget(new QRadioButton("Landscape")).   entonces  tendríamos  que  referir  los  3  Layouts  iniciales  a  éste. buttonsLayout->addWidget(new QPushButton("Print")).  Para  hacer  eso  usando  la  función  addStretch.   por   lo   que   vamos   a   tener   que   contenerlo   todo   dentro   de   un   Layout   más   grande. // Fin Layout Central outerLayout->addLayout(groupLayout). QVBoxLayout *colorLayout = new QVBoxLayout(colorGroup).  Por  lo  que   el  código  completo  referente  al  Layout  del  medio  se  queda  así. orientationLayout->addWidget(new QRadioButton("Portrait")). colorLayout->addWidget(new QRadioButton("Color")). QGroupBox *orientationGroup = new QGroupBox("Page Orientation").  y  éste  pasaría  a  ser  el  más  cercano   en  jerarquía  a  la  ventana  de  Diálogo. // Fin Layout Inferior outerLayout->addLayout(buttonsLayout). buttonsLayout->addStretch().  tenemos  que   referirnos   a   una   VerticalLayout. QGroupBox *orientationGroup = new QGroupBox().  Y  así  quedaría  todo:   QStringList options. QGroupBox *colorGroup = new QGroupBox(). QGroupBox *colorGroup = new QGroupBox().     . outerLayout->addStretch(). groupLayout->addWidget(orientationGroup). groupLayout->addWidget(colorGroup). // Fin Layout superior outerLayout->addLayout(topLayout).   QGroupBox *orientationGroup = new QGroupBox(). groupLayout->addWidget(colorGroup). options << "Office Printer" << "HP LaserJet" << "Canon OfficeJet".     Si   te   has   dado   cuenta. topLayout->addWidget(new QLabel("Printer:")). topLayout->addWidget(c=new QComboBox()).   QHBoxLayout *groupLayout = new QHBoxLayout(this). colorLayout->addWidget(new QRadioButton("Color")). QVBoxLayout *outerLayout = new QVBoxLayout(this). QVBoxLayout *orientationLayout = new QVBoxLayout(orientationGroup).52       Fíjate   que   aquí   hemos   instanciado   primero   los   GroupBoxes   y   luego   los   hemos   añadido   al   Layout. buttonsLayout->addWidget(new QPushButton("Cancel")).   no   hemos   puesto   aún   el   spacer   vertical   que   hay   entre   el   Layout   central  y  el  inferior  (el  de  los  botones).     Ahora  vamos  a  añadirle  a  cada  VertialLayout  los  widgets  que  le  corresponden.   c->addItems(options).   sería   el   padre   de   todo. QGroupBox *colorGroup = new QGroupBox(("Color  options"). // Layout Central QHBoxLayout *groupLayout = new QHBoxLayout(). // Layout Inferior QHBoxLayout *buttonsLayout = new QHBoxLayout(). orientationLayout->addWidget(new QRadioButton("Portrait")). QVBoxLayout *orientationLayout = new QVBoxLayout(orientationGroup). QVBoxLayout *orientationLayout = new QVBoxLayout(orientationGroup). // nueva groupLayout->addWidget(orientationGroup). QVBoxLayout *colorLayout = new QVBoxLayout(colorGroup). colorLayout->addWidget(new QRadioButton("Black and White")).   contenedor   de   todo. orientationLayout->addWidget(new QRadioButton("Landscape")). QVBoxLayout *colorLayout = new QVBoxLayout(colorGroup).

  donde   elegiremos   Qt   C++   Project   -­‐>   Qt   GUI   Application.  elemento   indispensable  de  una  aplicación  gráfica.53       Hemos   sangrado   los   bloques   que   ya   teníamos   construidos   para   dejar   claro   el   código   que   hemos   añadido.  los  radiobuttons.     Construcción  de  una  ventana  de  Diálogo  con  Qt  Designer     Algunos   pensarán   que   soy   un   masoquista.   por   eso.       Una  vez  se  han  creado  todos  los  ficheros.   y   el   spacer   vertical.cpp”  y   déjalo  de  esta  forma:   QStringList options.   Vamos   a   ir   dibujando   en   él.  Vamos  a  hacer  lo  mismo  con  los  de  la  derecha.   cambiando  su  tamaño. options << "Office Printer" << "HP LaserJet" << "Canon OfficeJet".       Luego   seleccionamos   rodeando   con   el   ratón   todo   el   bloque   central   y   pulsamos   en   Lay   Out   Horizontally.   Iremos   colocando   primero   en   la   parte   superior.   Les   cambiamos   los   textos.  y  pulsamos  Lay  Out  Vertically.   y   les   ponemos   el   texto   que   les   corresponden.   Luego   en   el   bloque   central   vamos   a   situar   primero   los   2   GroupBox   uno   al   lado   del   otro.   Luego   añadimos  en  la  parte  inferior  los  2  PushButtons.  Hacemos  doble  clic  sobre  el  texto   del   Label   y   ponemos   “Printer:”.       Ahora  nos  quedan  los  3  grupos  y  el  spacer  vertical.  Luego   vamos  a  seleccionar  el  GroupBox  izquierdo.   lo   mejor   que   podamos.   conforme   al   dibujo   del  diseño  que  estamos  siguiendo.   y   terminamos   pulsando   en   Lay  Out   Vertically.     Si  por  curiosidad  quieres  añadir  los  ítems  del  ComboBox.  Esto  se  hace  siempre  de  dentro  hacia  fuera.  Aparece  una  caja  roja  alrededor  de   ellos  que  los  alinea  perfectamente  entre  sí.   También   nos   hemos   permitido   el   lujo   de   añadir   los   ítems  del   ComboBox   que   harán   de   opciones.  y  los  agrupamos  pulsando  el  botón  Lay  Out  Vertically.     .   no   va   a   quedar   todo   lo   alineado   que   queremos.  mediante  una  StringList.   que   nos   separa   de   la   programación   y   por   tanto   del   entendimiento   de   las   bases  fundamentales. ui->setupUi(this).   Cmd   en   los   Mac).ui”  que  se  nos  abrirá  en  el  Qt   Designer.       Vamos   a   crear   un   nuevo   proyecto.  De  hecho  puedes  ver  como  el  diseño  se   adapta  flexiblemente  al  tamaño  libre  de  la  ventana  si  arrastras  uno  de  los  bordes  del  formulario.  Hacemos  los  mismo  con  el  GroupBox  de  la  derecha.   diseñando   la   ventana   a   tecla.   por   mucho   que   lo   intentes.   arrastrándolo  de  las  herramientas  al  formulario  y  soltándolo.     Si   te   das   cuenta.  abrimos  “dialog.   es   el   momento   de   comenzar   a   crear   los   Layouts   que   organicen   todo   este   espacio.  que  lo  organiza  con  su   contenido.   Luego   hacemos   lo   mismo   con   el   grupo   superior   (label   y   combobox)   y   con   el   grupo  inferior  (spacer  horizontal  y  los  2  botones).   por   los   que   les   corresponden.  en  el  próximo  tema  vamos  a  pasar  a  ver  las  ventanas  de  diálogo.  La  clase  base  se  llamará  “Dialog”  y  la  clase   base  va  a  ser  QWidget.   un   Label   y   un   ComboBox.   seleccionando   los   del   grupo   izquierdo   (ambos   manteniendo   la   tecla   Ctrl   pulsada.  antes  que  abandonarse  al  placer   del   mero   y   puro   diseño.   el   interface   del   diseño   que   estamos   siguiendo.   Ya  se  nos  ha  organizado  todos  los  widgets  con  los  layouts.  y  les  cambiamos  su  texto.   para   seleccionarlo   a   él   por   completo.   pero   es   fundamental  aprender  antes  el  código  que  hay  detrás  de  todo.  Pulsamos  sobre  una  zona  vacía  del   formulario.  abre  el  código  de  “dialog. ui->comboBox->addItems(options).     Con   esto   damos   por   terminado   el   tema   referente   a   los   elementos   de   una   ventana   (widgets  y  layouts).  Le  podemos  llamar  al  proyecto  “dialog”.   Ahora   situamos   el   spacer   horizontal   y   el   vertical.   y   el   sentido   del   mismo   con   respecto   a   los   bloques   que   contiene.  empezaremos  por  agrupar  los  RadioButton.  verás  como  todo  se  organiza  en  tamaño  y  posición  perfectamente.     Ahora   vamos   a   poner   los   RadioButtons   en   sus   respectivos   GroupBoxes.

54     Fíjate   que   hemos   accedido   desde   el   objeto   ui   que   representa   el   interfaz   diseñado   en   el   fichero   *.                                                             .ui.   y   entonces   ya   podemos   llamar   a   sus   métodos  libremente.   a   la   combobox.  Ya  veremos  esto  con  más  detalle  más  adelante.   por   su   nombre   (objectName).

 sólo  pudiendo  interactuar  con   ella  mientras  está  abierta.   que   conforma   una   ventana   que   además   devuelve   un   valor   siempre  tras  cerrarse  (OK.-­‐  Es  otro  widget  especial.  pero   no   vamos   a   desarrollar   un   ejemplo   completo.   en   este   tema   vamos   a   tratar   de   cerca   las   ventanas  de  diálogo.     Puesto   que   ya   hemos   visto   a   los   widgets   funcionando   como   ventanas.  no  pudiendo  interacturar  con  ella.  que  no  tenga  padre  se  le  llama  top-­‐level  widget.-­‐  Bloquea  a  todas  las  ventanas  de  la  aplicación.   una   ventana   plana.       Toda   ventana   de   Diálogo.   y   permite   estando   ella   abierta.  aunque  permite  interactuar  con  el  resto.       En  este  capítulo  vamos  a  ver  el  código  que  conforma  a  una  ventana  Diálogo  para  su  uso   dentro  de  una  aplicación  mayor.   ya   que   nos   faltan   elementos   que   no   debemos   aún   tocar   aquí   y   que   una   vez   conocidos   podremos   unirlos   todos  en  el  tema  20.  herramientas.  etc.  puede  ser:   No   Modal.   ApplicationModal.   Vamos   a   crear   una   ventana   de   diálogo   con  el  Qt  Designer.     Una  ventana  dependiendo.   y   debe   llevar   botones   para   seleccionar   opciones   y   pushbuttons   para   aceptar   o   rechazar   la   acción.  y  que  esa   decisión  es  devuelta  al  programa  mediante  un  valor.   QDialog.  Un  top-­‐level  puede  usar  el   método  show()  para  mostrarse  en  pantalla.   o   como   la   que   se   generaba   simplemente  mostrando  un  QLabel  en  el  ejemplo  de  HolaMundo.     Toda   ventana   de   Diálogo   lleva   una   serie   de   widgets   que   la   componen.   y   las   MainWindows   las   trataremos   a   fondo   en   el   tema   19.   pero   lo   fundamental  es  que  lleva  botones  para  aceptar  y/o  rechazar  una  determinada  acción.-­‐   Es   un   widget   especial.  status  bar.  dock.-­‐  Sólo  bloquea  la  ventana  padre  de  la  misma.  y  puede  por  tanto  constituirse  como  ventana.-­‐   Cualquier   widget   sin   padre   es   constituido   automáticamente   como   una   ventana.  de  la  interactividad  que  bloquea.  Cancel.55     TEMA  15   VENTANAS  DE  DIALOGO       Un  widget  .  descendientes  de  QDialog.  con  esta  forma:           .-­‐   Si   no   bloquea   ninguna   ventana   que   le   acompañe.   hasta  que  ésta  es  cerrada.   interactuar  con  otras  ventanas  también  abiertas.  con  menús.   como   la   que   hemos   creado   en   el   tema   anterior.   WindowModal.  tal  como  hemos  aprendido  en  el  tema  pasado.  que  conforma  la  ventana  más  completa  y  por  tanto  la   principal  de  una  aplicación  gráfica.  etc…)   QMainWindow.   hereda   de   QDialog.  Así  hay   3  tipos  de  top-­‐level  Windows  (ventanas):     QWidget.

ui   en   el   Qt   Designer. } bool SearchDialog::isBackward() const { return ui->directionBackward->isChecked().     Hemos  puesto  argumentos  para  inicializar  el  texto  de  búsqueda.  por  ejemplo. QWidget *parent) : QDialog(parent). else ui->directionForward->setChecked(true). }.   haz   el   tamaño   lo   mínimo   que     se   pueda   de   alto. QWidget *parent = 0). ui(new Ui::SearchDialog) { ui->setupUi(this).  y  así  que   se   conserve   entre   las   búsquedas.   Usaremos   2   getters   para   recoger   el   estado   de   las   opciones. // los getters que recogerán las opciones bool isBackward() const.  Construye  algo  similar  a  esto:     Y   pulsando   en   el   cuadro   azul   inferior   derecho   de   la   selección   del   formulario.   Vamos   a   ver   la   implementación  de  este  código  en  searchdialog.   Cambia   el   nombre   del   QLineEdit   por   “searchText”   y   el   de   los   radiobuttons  por  “directionForward”  y  “directionBackward”  respectivamente.h  en  el  constructor  pondremos  un  código  como  éste:   class SearchDialog : public QDialog { Q_OBJECT public: explicit SearchDialog(const QString &initialText.56       Así   que   esta   vez   el   proyecto   lo   basaremos   en   la   clase   QDialog   y   le   llamaremos   SearchDialog.     Una   vez   hecho   el   diseño.   También   se   inicializa   de   igual   manera   la   opción   de   dirección   de   la   búsqueda.   Una   vez   abierto   el   fichero   *. if(isBackward) ui->directionBackward->setChecked(true). private: Ui::SearchDialog *ui.   por   “Search”   para   que   salga   el   título   que   vemos  en  el  dibujo. ~SearchDialog(). }   .       En  el  archivo  searchdialog. bool isBackward.cpp  .   cambia   la   propiedad   WindowTitle   en   QWidget.   pero   hazlo   al   menos   el   doble   de   ancho   que   de   alto.   de   manera   que   se   parezca   al   dibujo   original. const QString &searchText() const. } const QString &SearchDialog::searchText() const { return ui->searchText->text().   SearchDialog::SearchDialog(const QString &initialText.   selecciona   el   formulario.   y   a   la   derecha. bool isBackward. ui->searchText->setText(initialText).   vamos   a   ver   el   código   que   lo   integrará   en   la   funcionalidad   de   una  aplicación  más  compleja.

isEmpty()) itemLabel->setText(item).57     Ahora  vamos  a  ver  como  se  integraría  el  código  de  mostrar  la  ventana  en  el  resto  de  la   aplicación.  pero  ahora  al  hacerlo  con  exec()  (modal  por  defecto).   o   el   Cancel   (false). -10000...   En   concreto   son   6   tipos   más   los   subtipos   de   cada   una   de   ellas. &ok).settings..   Vamos   a   ver   el   código   ejemplo   de   las   4   posibilidades:     Ejemplo  con  getInt:   bool ok. 25. if (ok && !text. QString item = QInputDialog::getItem(this.value("searchBackward"). "Usuario:".56.. 37.     1.. false.   pero   puede   ser   cambiado   mediante   el   método   setWindowModality(). this).  será  un  valor  entero.. if(dlg.-­     Es   una   ventana   de   diálogo   que   provee   automáticamente   de   un   Label. " Ejemplo con getItem".. &ok). &ok).   un   Double. QDir::home().  lo  que  se  devolverá  una  vez   cerrada.exec() == QDialog::Accepted){ QString text = dlg.   Qt   incorpora   una   serie   de   ventanas   que   son   muy   típicas   de   las   GUIs   de   todas   las   plataformas.   Puede   recoger:   un   Int.dirName().toBool().   y   lo   hace   con   las   funciones   correspondientes   getInt(). &ok).searchText()..   Estábamos   acostumbrados   a   mostrar   la   ventana   con   show()   (no   modal   por  defecto).isBackward().  que  es  un  DialogCode. . "Cantidad:".isEmpty()) textLabel->setText(text).         . 100.   un   LineEdit   o   un   ComboBox...   Vamos   a   verlas   desde   la   más   sencilla   a   las   más   avanzada.  Todas  estas  funciones  devuelven  como  último  parámetro  un  bool  que  dice   si   se   eligió   el   botón   OK   (true).-­   QInputDialog. if (ok && !item. items << "Primavera" << "Verano" << "Otoño" << "Invierno"." Ejemplo con getDouble".   y   los   botones   OK   y   Cancel.value("searchText").. 0.   getText()  y  getItem(). 2...toString(). 10000. QLineEdit::Normal..       SearchDialog dlg(settings. Ejemplo  con  getText:   QString text = QInputDialog::getText(this.......     Constant Value QDialog::Accepted QDialog::Rejected 1 0     Ventanas  de  Diálogo  Standard       Además   de   poder   diseñar   ventanas   a   nuestro   gusto   y   necesidades. }   Por   defecto   la   ventana   es   modal. "Estación:".. 1. Ejemplo  con  getItem:   QStringList items. items. int i = QInputDialog::getInt(this. bool backwards = dlg."Porcentaje:".   un   texto   o   un   Item   de   un   combo   box..   getDouble(). "Ejemplo con getText"."Ejemplo con getInt". Ejemplo  con  getDouble:   double d = QInputDialog::getDouble(this. 0.

if (reply == QMessageBox::Yes) // codigo de Yes else if (reply == QMessageBox::No) // codigo de No else // codigo de Cancel         information.  Las  hay  de  4  tipos:       warning. msgBox. QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel). ya no volverá a mostrarse. QMessageBox::AcceptRole).   y   recibe   una   respuesta.-­QColorDialog. reply = QMessageBox::question(this.-­‐   QMessageBox::StandardButton reply.exec() == QMessageBox::AcceptRole) // codigo de aceptacion else // codigo de rechazo       question.-­‐  Muestra  el  cuadro  de  selección  de  color." information". errorMessageDialog->showMessage("Esta ventana muestra un error. QMessageBox::RejectRole). this).-­‐   QMessageBox::StandardButton reply.MESSAGE . ").-­‐   Es   una   ventana   modal   que   pregunta   al   usuario   una   pregunta. if (reply == QMessageBox::Ok) // codigo de OK else // codigo de Escape (tecla escape) no todos los sistemas lo admiten       critical. this).58       2.     3."critical".   ya   no   volverá   a   salir. QMessageBox::Abort | QMessageBox::Retry | QMessageBox::Ignore). if (reply == QMessageBox::Abort) // codigo de Abort else if (reply == QMessageBox::Retry) // codigo de Retry else // codigo de Ignore   4.MESSAGE.addButton("&Continue".     errorMessageDialog = new QErrorMessage(this)."warning".-­‐     QMessageBox::StandardButton reply.-­‐  Es  una  ventana  de  error  que  provee  de  un  TextLabel  y  un  CheckBox.MESSAGE.   aunque  sea  llamada  en  el  código  la  función  showMessage()  de  nuevo. " "Si el checkbox se deja.   Con   showMessage()   ponemos   el   texto   del   error.-­QMessageBox. msgBox. reply = QMessageBox::information(this.-­‐   QMessageBox msgBox(QMessageBox::Warning. volverá a mostrarse. " "si no se deja. 0. // color inicial verde if (color. reply = QMessageBox::critical(this.addButton("Save &Again".   QColor color = QColorDialog::getColor(Qt::green. MESSAGE).   automáticamente   aparecerá   el   checkbox   diciendo   si   quiere   que   se   muestre   el   error   de   nuevo.isValid()) { // codigo de uso de color }   .  y  devuelve  el  código  del  mismo.   si   decide   que   no."question".-­QErrorMessage. if (msgBox.

options).isEmpty()) // codigo de apertura del fichero         getSaveFileName. openFilesPath. options).  Hay  de  4  tipos:       getOpenFileName. "getOpenFileName".-­QFileDialog.Text Files (*. if (ok) { // codigo de uso de la fuente }   6. options). QFont font = QFontDialog::getFont(&ok. "getSaveFileName".-­‐   QString selectedFilter.-­‐   QFileDialog::Options options = QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly. "All Files (*). "All Files (*). QStringList files = QFileDialog::getOpenFileNames(this. QString fileName = QFileDialog::getSaveFileName(this.-­‐  Para  elegir  la  fuente.. if (files. if (!directory.Text Files (*.59       5. QString fileName = QFileDialog::getOpenFileName(this. if (!fileName.-­‐   QString selectedFilter.isEmpty())// codigo de apertura de directorio             . "getExistingDirectory". if (!fileName.. &selectedFilter. options). "getOpenFileNames".txt)". defaultFileName.. this). "All Files (*). // codigo de apertura de ficheros }       getExistingDirectory.-­‐  Para  hacer  operaciones  de  ficheros  o  directorios.count()) { openFilesPath = files[0].-­‐   QString selectedFilter.txt)". &selectedFilter.Text Files (*.-­  QFontDialog. defaultFileName.isEmpty())// codigo de guardado del fichero         getOpenFileNames.txt)". defaultDirName. QString directory = QFileDialog::getExistingDirectory(this.     bool ok. &selectedFilter.

60                                                                 .

 Vamos  a  por  el  código.     La  verdad  es  que  se  trata  de  conceptos  muy  abstractos.  un  stream  de  datos. QDirModel model.   y   el   usuario   interactúa   con   ellos.   Esos   datos   son   enviados   por   el   modelo   a   la   Vista  (view).   y   también   de   los   modelos   para   lo   mismo.   Controlador)  fue  Smalltalk  en  1979  en  el  laboratorio  de  Xerox.       Qt   provee   de   una   serie   de   vistas   suficientes   para   lo   que   cualquiera   suele   programar.   y   si   al   final   lo   lograra.  un  sistema  de  archivos.   Pues   bien.   vamos   a   usar   como   Vista.  al  que  vamos  a  llamar  “mvc”.   ¿Cómo  es  eso  posible?.   para   hacer   de   esta   tarea   algo   sencillo   y   con   poco   código.  Suponga  que  quiere  presentar  en  una  ventana  en   forma  de  árbol  el  contenido  completo  del  directorio  de  su  ordenador.   modificándolos.  Modelo  y  Vista  para  que  surja  la  chispa  mágica.   y   la   misma   puede   proveer   de   métodos   para   enviar   modificaciones   de   los   datos   al   Modelo   (model).   Sobre   la   vista   el   usuario   interactúa.   se   accede   a   fuentes   de   datos.   Vista.  que  se  encarga  de  visualizarlos  de  una  manera  específica.   pero   si   esto   no   fuera   así.   Sin   duda   sería   algo   difícil   de   hacer   meramente   con   QTreeWidget   y   QDir   (QFileInfo. QTreeView view.   a   hacer   un   programa   específico   con   un   código   complejo  y  poco  reutilizable.   etc).  que  se  relaciona  directamente   con  el  sistema  de  archivos  como  su  fuente  de  datos.  que  está  en  contacto  directo  con  la  fuente  de  datos.  etc.   un   fichero.   sería   mucho   más   sencillo   crearlos   para   poder   ponerlos   a   trabajar   juntos.   pero   en   cualquier   caso   no   va   a   pasar   de   las   10   líneas.  es  porque   sabe  que  lo  va  a  rentabilizar  en  el  tiempo.  Actualmente  muchos  lenguajes  y   frameworks  lo  han  incorporado  como  una  ventaja  al  desarrollo  inestimable.  que  proveen  todas  las  vistas.   por  un  lado  esta  el  Modelo.  Aunque  parezca  mentira.   la   clase   QTreeView.61     TEMA  16   MODELO.   #include <QtGui> #include <QApplication> int main(int argc.argv).   o   existen   también   objetos   especiales.  De  esta  manera  la  vista  es  cargada   con  datos  que  consigue  el  modelo  para  ella.  Cuando  un  programador  se  pone  a  escribir  mucho  código.   seguro   que   ocuparía   bastantes   líneas   de   código.  Un  mismo  modelo  puede   alimentar   varias   vistas   diferentes   y   ambas   sincronizar   su   contenido   a   la   vez. char **argv) { QApplication app(argc.  INTRODUCCION       El   primer   lenguaje   que   por   primera   vez   introdujo   el   paradigma   MVC   (Modelo.   leyéndolos   o   tratándolos   de   alguna   manera   y   la   fuente   de   los   mismos   es   actualizada   con   dicha   modificación   o   tratamiento.     En   la   gran   mayoría   de   aplicaciones.  vamos  a  usar  la  clase  QDirModel.  y  por  supuesto  Qt  no   iba  a  ser  menos.  simplemente  hay   que  combinar  ambas  clases.cpp”   y   vamos   a   empezar   a   escribir   directamente   en  él.   que   nos   provee   de   un   magnífico   gráfico   en   árbol   muy   avanzado   y   parametrizable.  y  como  Modelo.   especializados   en   la   interacción   del   usuario   con   la   vista.   .   que   aquí   no   se   llama   Controlador   sino   Delegado  (delegate).  Luego  esos  datos  se   muestran   al   usuario   de   diversas   formas.   que   pueden   ser.   de   hecho   se   podría   hacer   con   solamente   4   líneas   de   código.   si   no   fuera   porque   nos   gusta   cambiar   unas   determinadas   propiedades   de   la   vista.  una  base  de  datos.  y  creo  que  lo  mejor  es  ponerlos  a   funcionar  para  ver  de  lo  que  estamos  hablando.  se  está  quieto.   Luego   vamos   a   añadirle   un   fichero   fuente   “main.   y   este   a   la   fuente   de   datos.   pues   para   llevar   a   cabo   la   aplicación   que   hemos   comentado. view.  de  manera  que  es   quien   recoge   los   datos   de   la   misma   y   los   actualiza.  de  lo  contrario.   el   paradigma   MVC   viene   a   nuestro   rescate.     Bien.setModel(&model).  y  eso  se  hace  con  el   método  setModel(modelname)  .   El   paradigma   MVC   separa   en   3   aspectos   diferentes   de   esta   actividad.  VISTA  Y  CONTROLADOR.  con  ficheros  y  directorios.     Esta  vez  vamos  a  crear  un  proyecto  vacío  (Empty  Project).

view. view.  vamos  a  ver  cual  es  el  resultado  final  de  esas  pocas  líneas.  que  como  ya  apuntamos  van  a  ser  suficientes  para  la  gran  mayoría   de   aplicaciones   que   vas   a   acometer.setWindowTitle("Dir View").  donde  hay  sólo  una  columna  (column)   y  muchas  filas  (row).   además   posee   cabecera   tanto   para   las  columnas  como  para  las  filas.     .     Efectivamente   es   impresionante   el   resultado   obtenido   con   tan   escueto   código.   Con   esto   queda   justificado  sobradamente  el  uso  del  paradigma  MVC  siempre  que  sea  posible.  para  poner  sus  títulos  en  ellas.   Si   esto   no   es   así.     QListView.exec(). // vamos a mostrar la vista view.  Dispone  de  una  serie  de  métodos  para  cambiar  la  presentación  de  los  ítems   de  la  misma.  o  tus  propias  vistas. return app.     QTableView.setSortingEnabled(true).  sobre  todo  en  la  presentación  de  tablas  de  bases  de  datos. view.setAnimated(false).62     // Vamos a cambiar el aspecto de la vista view.  es  la  más  usada.  es  hora  de  que  te  presente  las  vistas  y  los  modelos   de  que  vas  a  disponer  en  Qt.   Tiene   forma   bidimensional   con   varias   columnas   y   varias   filas.  proporciona  una  vista  unidimensional.     Las  Vistas     Las  vistas  que  proporciona  Qt  son  todas  derivadas  de  la  clase  QAbstractItemView  .  pero  eso  ya  se  sale  del  propósito  de  este  primer   libro.  QTableView  y  QTreeView.  Las   vistas  que  de  ella  se  derivan  son  tres  tipos  principalmente:  QListView. }   Eso  es  todo  el  código. view.       Ahora  que  he  conseguido  tu  atención.   en   un   curso   más   avanzado   podrás   aprender   a  crear  tus  propios  modelos.show().setIndentation(20).resize(640.480).

  provee   de   la   información   que   obtiene   del   sistema   de   ficheros.  que  pueden  guardar  cualquier  tipo  de  datos.   y   dispone   la   información   en   forma   de   árbol.63       QTreeView.       QFileSystemModel.   combinando   una   vista   o   varias   vistas   con   un   modelo.     Los  modelos     Los  modelos  que  proporciona  Qt  para  acceder  a  los  diferentes  tipos  de  fuentes  de  datos.   como   QDirModel.   Además   también   dispone   de   cabecera   horizontal   (header).  para  poner  títulos  a  las  propiedades  de  los  ítems.   Veremos   casos   concretos  en  los  temas  que  correspondan  a  bases  de  datos.       Observaciones  finales     Como   puedes   imaginar.   puedo   obtener   el   interfaz   gráfico   que   necesito   para   interactuar   con   los   datos   que   quiero.  se  usa  para  el  manejo  de  estructuras  más  complicadas  en  forma  de   árbol.                                 .  es  usado  para  guardar  una  lista  de  QStrings.   y   entre   los   que   proporciona   están:   QStringListModel.     QStandardItemModel.   El   caso   más   típico   es   con   una   base   de   datos   relacional.  como  hemos  visto  en  el  ejemplo.  pero  más  avanzado.  De  ella  deriva  QSqlQueryModel  que  guarda  los  datos  de  la  respuesta  a  una   query  a  una  base  de  datos.   Se   trataría   por   tanto   de   combinar   una   QTableView   con   una   QSqlTableModel   o   una   QSqlQueryModel.   se   usa   para   tratar   datos   provenientes   de   una   base   de   datos   relacional.   como   MySQL   u   Oracle.   derivan   de   QAbstramItemModel.  QFileSystemModel  y  QSqlRelationalTableModel.   QStandardItemModel.     QStringListModel.  es  usada  sobre  todo  en  acceso  a  directorios.  De  ella  deriva  QSqlTableModel  que  se  usa  para  guardar  los  datos  de  una  tabla  concreta   de  una  base  de  datos.     QSqlRelationalTableModel.

64                                                                 .

  con   un   modelo   predefinido.   Los   diferentes   modos  de  selección  son:  SingleSelection  (un  solo  elemento).-­‐  Construyendo  los  ítems  con  esta  clase  como  padre.  nos  devuelve  el  row  (fila)  del  item  actual.  y   el  espaciado  con  setSpacing(value).   y   también  addItem(QString).previous).     La  única  señal  que  usamos  en  esta  clase  es  currentItemChanged(current.parent)  ). newItem->setText("Texto Elemento 1").  y  luego  redimensionarla  con  setRowCount(rows)  y  setColumnCount(cols).     QListWidget     Para  añadir  elementos  a  esta  Lista  hay  2  formas:   1.   La   razón   de   usar   QListWidgetItem   en   vez   de   QStrings   en   la   inserción   de   elementos.newItem).   2.   respectivamente.col).   Para  borrar  ítems.   y   se   puede   establecer   con   la   función   setSelectionMode(selmode).65     TEMA  17   QLISTWIDGET  Y  QTABLEWIDGET       Derivadas   de   QListView   y   QTableView.     QTableWidget     Se   puede   dimensionar   en   la   creación   en   el   constructor   (new   QTableWidget   (rows.   si   no   también   iconos.  que  se   produce   al   cambiar   el   foco   de   item.  que  añade  al  final  del  anterior.   y   suelen   ser   usados   para   aplicaciones  sencillas  donde  no  se  usan  modelos  combinados  con  ellas.   ContiguousSelections   (varios   elementos   contiguos   a   la   vez)   y   NoSelection   (no   se   puede   seleccionar  nada).   tenemos   2   clases   que   representan   vistas   en   forma   de   lista   y   tablas.   El   modo   de   selección   de   los   elementos   en   la   lista   se   puede   leer   con   la   función   selectMode().-­‐  Construyendo  el  item  sin  padre  y  añadiéndolo  a  la  lista  más  tarde.   new QLIstWidgetItem ("Texto elemento 1".   éste   permite   no   sólo   insertar   texto   en   las   listas.col.   Ambos   son   QWidgets   proporcionados   por   la   barra   de   herramientas   del   Qt   Designer.  MultiSelection  (varios  elementos  a  la   vez).  También  se  puede  cambiar  el  tamaño  del  icono  con  setIconSize(QSize).       Podemos   añadir   ítems   con   setItem(row.   es   porque.   En   la   segunda   opción   también   se   puede   usar   el   método   insetIntems(QStringList).  Se  pueden  borrar  ítems  con  takeItem(row.  Podemos  poner  etiquetas  en   las   cabeceras   horizontales   con   setHorizontalHeaderLabels(QStringList)   y   en   las   cabeceras     .   Así   tambien   hay   dos  Modos  de  vista:  IconMode  (drag  &  drop  desabilitado  por  defecto)  y  ListMode  (drand  &  drop   habilitado  por  defecto).   QListWidgetViewItem *newItem = new QListWidgetViewItem.   La   función   currentItem().   y   currentRow().   nos   devuelve   el   item   actual.listWidget).  respectivamente.  y  los  Items  pueden  aparecer  en  la  lista   marcados  con  QListViewItem::setCheckState(Qt::CheckState).  y  no  es  una  posición  exacta.   donde   newItem   es   un   QTableWidgetItem.cols.  se  puede  usar  takeItem(row). listWidget->insertItem(row.  Están  formados  por  ítems   de  las  clases  QListWidgetItem  y  QTableWidgetItem.newItem).

 y  con  clear()   borrar  todo  el  contenido  de  las  celdas.setItem(mes. QStringList meses. mes < 12. return app.       Con  rowCount()  podemos  contar  las  filas.   También   ordenar   por   una   columna   usando   sortItems(col. mes < 12.  con  columnCount()  las  columnas.resize(240. } tabla.  itemChanged(QTableWidgetItem  item).new QTableWidgetItem(dias[mes])).   cellChanged(int   row. mes++){ tabla. tabla.       Las   señales   más   usadas   son:   cellClicked(int   row.2).exec(). tabla.  tiene  funciones  interesantes  como  setIcon(). }                         .setItem(mes.int   col). mes++){ tabla.1. for(int mes=0. QTableWidget tabla(12.show().400).  setFont()  y  setText().new QTableWidgetItem(meses[mes])). char **argv) { QApplication app(argc. dias.int   col). dias << "31" << "28" << "31" << "30" << "31" << "30" << "31" << "31" << "30" << "31" << "30" << "31".argv). } for(int mes=0. QStringList headers.   itemClicked(QTableWidgetItem  item).0. headers << "Meses" << "Dias".setHorizontalHeaderLabels(headers).  Qt:SortOrder).66     verticales   con   setVerticalHeaderLabels(QStringList). meses << "Enero" << "Febrero" << "Marzo" << "Abril" << "Mayo" << "Junio" << "Julio" << "Agosto" << "Septiembre" << "Octubre" << "Noviembre" << "Diciembre".       Veamos  ahora  un  ejemplo  de  tabla  con  las  cosas  que  hemos  visto:   #include <QtGui> int main(int argc.       QTableWidgetItem.

-­‐  Te  lleva  al  principio  de  la  línea  actual.67     TEMA  18   Qt  CREATOR       Queremos   dedicar   un   poco   de   tiempo   al   entorno   de   desarrollo   integrado   de   Qt.   al   Qt   Designer.   Ctrl   +   u   (Cmd   +   U   en   Mac).   es   que   no   es   accesible   desde   esa   parte   del   documento.   nos   irán   apareciendo   las   opciones   cada   vez   más   específicas   de   manera   que   muchas   veces   no   hace   falta   acordarse   del   nombre   exacto   de   una   clase.   y   nos   situamos   en   una   línea   para   empezar   a   teclear   código.     Atajos  de  teclado  (keyboard  shortcuts)   Ctrl  +  cursor  derecho  .  una  clase.-­‐  Hace  que  el  código  seleccionado  tome  la  sangría  requerida.  En  un  principio  Qt  no  poseía  un  IDE.-­‐  Pasa  páginas  de  código  arriba  y  abajo.   desde   donde   se   puede   acceder   al   Qt   Assistant.   pero   desde   la   versión   4.     .       Si   necesitamos   ayuda   del   Qt   Assistant   sobre   algún   elemento   del   código.     Conforme   se   escribe.  sólo  tenemos  que  pulsar  la  tecla  Ctrl+espacio.   que   puede   ser   desde   que   no   está   incluido   su   fichero   cabecera.   y   queremos   que   el   sistema   nos   ayude   a   rellenar.  los  paréntesis.   y   las   teclas   de   acceso   rápido.   y   si   nos   situamos  sobre  él.   debugear   desde   él.-­‐   Seleccionas   bloques.   aparece   subrayado   en   rojo.       También  nos  ayuda  a  acceder  a  los  miembros  de  las  clases.  una  variable  ya  definida.-­‐  Selección  de  texto.   o   integrado   en   otro   IDE   como   Eclipse.  y  nos  da  los  argumentos  de   las  funciones  cuando  escribimos  tras  ellas.  entonces  se  nos  abrirá  a  la  derecha  del  código  la  ventana  de  ayuda   correspondiente.   se   nos   recuadra   este.  nos  aparece  un  mensaje  emergente.  todo  el  código  tenía  que  ser  escrito  en   un   editor   de   texto   avanzado.   Por   ello.   no   aparece   una   clase   o   variable   específica.  De  esta  forma  podemos  ver  los  ámbitos.   Ctrl  +  i  (Cmd  +  i  en  Mac).   si   hay   algún   error   sintáctico.   de   acuerdo   a   lo   que   ya   hay   escrito   y   declarado   en   el   fichero.   Ctrl  +  cursor  izquierdo  .   desde   un   fichero   cabecera.   solo   hay   que   situarse  sobre  él  y  pulsar  F1.   queremos   detenernos   un   tiempo  para  ver  como  podemos  sacar  ventaja  en  nuestro  desarrollo  de  este  IDE  avanzado.0   del   SDK.   Si   entre   las   opciones   que   nos   dan.  describiendo  el  error.   su   IDE   llamado  Qt  Creator.   Pero   una   de   las   características   más   interesantes   para   el   que   pica   código.   y   todos   los   mismos  elementos  dentro  de  la  misma  zona  de  ámbito.   Ctrl  +  Up/Down.       Cuando   nos   situamos   sobre   un   elemento   del   código.   es   el   autorrellenado.   pulsando   varias   veces   aumentas   el   bloque   de   selección.   Trolltech   incluyó   Qt   Creator   como   un   entorno   de   desarrollo   completo.   por   cualquier   razón.   se   puede   compilar.-­‐  Te  lleva  al  final  de  la  línea  actual.   a   que  dicha  variable  no  es  accesible  en  dicha  zona.   un   fichero   o   una   variable.   Shift  +  cursores  .   *.h   .  para   que   aparezca   toda   una   serie   de   alternativas   factibles.     El  autorellenado     Cuando   abrimos   un   fichero   de   código   *.   Tecleando   las   primeras   letras   de   lo   que   queremos   poner.cpp.   Y   aún   se   puede   seguir   haciendo   así.  y   hacerle  un  seguimiento  a  una  variable.

                                              .   Ctrl  +  /  (Cmd  +  /  en  Mac).-­‐   Permite   mover   líneas   completas   hacia   arriba   o   hacia   abajo.-­‐  Pasa  de  fichero  cabecera  a  fichero  de  código  y  viceversa.   Ctrl  +  K  .  para  poder  navegar  por  ayudas  y  documentos  rápidamente.   F4  .   Ctrl   +   Shift   +   R   (Cmd   +   Shift   +   R   en   Mac).-­‐  Abre  el  localizador.-­‐  Comenta  y  descomenta  todo  un  bloque  seleccionado.   F2  .-­‐   Permite   cambiar   el   nombre   de   una   variable   a   la   vez   en   todas  las  partes  donde  tiene  ámbito.68     Ctrl   +   Shift   +   Up/Down   (Cmd   +   Shift   +   Up/Down   en   Mac).   moviendo   el   texto   seleccionado.-­‐  Borra  toda  una  línea  completa.-­‐  Pasa  de  la  declaración  a  la  implementación  de  una  función  o  método.   Shift  +  Del  .   Funciona   también   con   selecciones   completas.

  QMenu   (menus   en   general.   La   ventana   principal   (QMainWindow).   una   atajo   del   teclado.   También   cada   elemento   de   la   ventana   principal.   QDockWidget   (widgets   acoplables).  etc).   A   parte   de   estos   elementos.  que  podrán  aparecer  como  consecuencia  de  acciones  del  usuario.       El   interfaz   principal   de   una   aplicación.  QStatusBar  (barra  de   estado).   una   aplicación   puede   recibir   órdenes   por   parte   del   usuario   para   llevar   a   cabo   una   misma   acción   desde   diferentes     .   por   ello   todos   los   elementos   que   la   componen   están   enfocados   a   proporcionar   dicha   interfaz.  error.69     TEMA  19   MAIN  WINDOWS       Este  es  el  tema  más  importante  de  todos  los  referentes  al   desarrollo  gráfico.   justo   debajo.   que   en   este   caso   está   en   la   zona   central   izquierda.   con   la   actividad   que   la   aplicación   lleva   a   cabo.  que  al  pulsar  el  botón  derecho  del  ratón  nos  permita  llevar  a  cabo  acciones  propias  de   dicho   elemento   gráfico.   como   pulsar   sobre   una   opción   del   menú.   una   aplicación   puede   añadir   ventanas   de   diálogo.  ya  que  en  él   se   encuentra   la   base   fundamental   de   todas   las   aplicaciones   gráficas.   Sin   embargo.  una  o  varias   barras   de   herramientas   con   iconos.  es  el  componente  fundamental.  es  centralizar  todo  el  interfaz  que  comunica   al   usuario.   que   nos   indica   que   pulsando   sobre   la   barra   superior   y  arrastrando.   posee   los   elementos  que  vemos  en  la  foto  de  arriba:  un  menú  principal  en  la  parte  superior.  QToolBar  (barras  de  herramientas).  proporcionando  otra  ventana.   mejorando   la   rapidez   de   uso   de   la   aplicación.   o   algún   evento  cualquiera  (información.  aviso.  sobre  la  cual  pueden  aparecer  toda  una  serie  de   widgets:  QMenuBar  (barra  de  menu).   y   es   un   QPlainTextEdit.  dicha  ventana  se  separaría  de  la  ventana  principal.       QAction     La  principal  misión  de  una  QMainWindow.  a  la  derecha  vemos  una  ventana  adosable  (dockable  window).   luego   esta   el   widget   principal   (central   widget).   se   encuentra   la   barra   de   estado   (status   bar)   donde   se   suele   poner   mensajes  informativos  al  usuario  al  respecto  de  lo  que  esta  aconteciendo  en  la  aplicación  en  cada   momento.   puede   tener   un   menú   contextual   asociado.   que   proporciona   las   opciones   del   menú   más   típicas   en   el   uso   cotidiano.   un   menús   contextual.   finalmente   en   la   parte   inferior.   la   Main   Window   (ventana   principal).   incluidos   los   contextuales)  y  otros  que  ya  hemos  visto  o  que  veremos.  derivadas  de  QDialog.  que  se  diferencia   por   el   icono   de   la   esquina   superior   derecha.

  vamos   definiendo   la   acción.   Luego   mediante   una   serie   de   métodos   de   QAction.  valga  la  redundancia. fileMenu->addSeparator(). fileMenu->addAction(exitAction).  Cada  opción  del  menú  principal.     Menú  principal     Cada  opción  principal  del  menú  principal.   se   corresponden   con   un   objeto   QAction   (una   acción).  es  por   lo   que   todas   las   acciones   se   han   centralizado   entorno   a   una   clase   que   no   tiene   representación   gráfica  física.   QMenu *fileMenu.   es   decir   que   pueda   llevar   una   marca   de   activada   o   desactivada. toolbar->addAction(openAction).   que  llevan  su  propio  icono.   podría   hacerse   seleccionado   la   opción   Editar-­‐>Pegar   en   el   menú   principal. toolbar->addAction(actionNew).     QToolBar  (barra  de  herramientas)   QToolBar *toolbar =addToolBar("&File"). actionNew->setIcon(QIcon(":/images/new. fileMenu->addAction(openAction). this.   como   por   ejemplo.   añade   una   barra   con   una   seria   de   acciones.   o   seleccionado   Pegar.   son   objetos   QMenuBar   que   son   creados   por   la   QMainWindow. actionNew->setCheckable(false).  y  con  ello  pasan  a  ser   hechas   hijas   de   esta   ventana. actionNew->setStatusTip("Crear un nuevo documento").  es  decir. actionNew->setToolTip("Crear un nuevo documento").this). fileMenu = menuBar()->addMenu("&File"). SLOT(newFile())).   establecemos   el   shortcut   de   teclado.  llamada  QAction.  Debido  a  que  una  misma  acción  podría  desencadenarla  diferentes  elementos.  decidiremos  si  es   una   opción   checkable.70     componentes. actionNew->setShortcut("Ctrl+N").         .  Vamos  a  ver  la  definición  de  una  acción:   QAction *actionNew = new QAction("&New".   al   ejecutar   esta   acción.   addToolBar.   finalmente   establecemos  la  conexión  entre  la  señal  triggered().png")).   La   función   miembro   de   MainWindow.   del   menú   contextual   del   widget  principal.   y   una   misma   acción   puede   ser   alcanzada  desde  varios  de  estos  elementos. SIGNAL(triggered()). fileMenu->addAction(actionNew).  y   el  slot  que  en  este  caso  puede  ser  una  función  que  nosotros  mismos  vamos  a  definir  lo  que  va  a   hacer.  lo  cual  va   a  repercutir  en  como  ha  de  definirse  una   acción.   que   puede   tener   asociado   un   icono   (ya   veremos   más   adelante   esto   con   más   detalle).   por   tanto   requiere   llamar   la   primera   vez   a   dicha   función   para   crearse.   establecemos   el   texto   que   aparecerá   en   el   status   bar.  de  las  toolbars  o  de  los  context   menus.  que  se  produce  al  pulsar  sobre  esa  acción.   pegar   una   selección   previa   en   el   widget   principal.  cada  vez  que  se  llama  a  su  función  miembro  menuBar().  las  que  se  ven   en   la   cabecera   de   la   ventana   sin   pulsar   sobre   ellas.   Podemos  ver  que  primero  creamos  un  objeto  acción  que  tendrá  el  texto  asociado  “&New”  (donde   el  &  va  delante  de  la  letra  que  queremos  vaya  subrayada  indicando  la  combinación  de  teclas  al   usuario   que   nos   llevará   también   a   la   misma   acción).   y   luego   va   añadiendo   acciones   (subopciones   de   la   misma)   y   separadores. // set connect(actionNew.   tambien   el   tooltip   o   texto   emergente   con   la   descripción  de  dicha  acción  si  nos  situamos  sobre  ella  con  el  puntero  del  ratón.   Cada   opción   principal.  o  simplemente  pulsando  una  combinación  de  teclas  (shortcut)  que  lleve  a  cabo   dicha  acción.   o   pulsando   sobre   el   icono   correspondiente   en   la   barra   de   herramientas.

 PNM.  Para  ello.     . label->setAlignment(Qt::AlignHCenter).   La  ventana  principal  (QMainWindow)     La   ventana   principal   al   ser   declarada   e   implementada.   Barra  de  estado  (status  bar)     La  barra  de  estado  normalmente  contendrá  widgets  del  tipo  QLabel.   bitmaps   y   otros.qrc)     Los   recursos   que   usa   una   aplicación.   es   la   designación   como   widget   central.  GIF.  El  fichero   qrc   forma   parte   del   proyecto   antes   de   ser   compilado   e   incluido   en   el   ejecutable.  por  ejemplo:   action->setIcon(QIcon(":/images/new.   QPlainTextEdit *editor.png”.  por  lo  que  como  se  ve. label->setMinimumSize(label->sizeHint()).     Ficheros  de  recursos  (*.  primeramente  recogemos  el  puntero  a  la  barra  de  estado  con  la  la   función   de   QMainWindow.png")).   statusBar().   Luego   añadimos   los   widgets.png</file>    •••     <file>images/gotocell.  para  mostrar  texto   con  avisos  al  usuario. editor->addAction(copyAction).  una  statusbar  es  un  contenedor.  que  hemos  visto  en  el  ejemplo  de  QAction.71     Menu  contextual  (context  menu)     Se  asocia  directamente  el  widget  que  lo  va  a  mostrar.   y   finalmente   cambiar   de   dicho   widget   la   propiedad   ContextMenuPolicy   para   que  aparezca  al  pulsar  clic  derecho.   Se   trata   de   un   fichero  escrito  en  XML  con  el  siguiente  formato:   <!DOCTYPE  RCC>   <RCC  version="1.  También  es   interesante   el   redefinir   la   función   virtual   closeEvent(QCloseEvent   *event).   Qt   soporta   muchos  tipos  de  imágenes  e  iconos  tales  como:  BMP.  XMB  y  XPM.png</file>   </qresource>     </RCC>   En  el  fichero  de  proyecto  (*. editor->setContextMenuPolicy(Qt::ActionsContextMenu).   debe   de   contener   todos   los   elementos  que  hemos  descrito  antes.pro)  se  añade  una  linea  que  pone:   RESOURCES  =  recursos.   QLabel *label = new QLabel().  y  sólo  hay  que  añadirle  las  acciones   que   va   a   mostrar.  JPG.qrc   En   el   código   C++   ya   se   puede   acceder   a   esos   ficheros   directamente   mediante   la   URL   “:/images/icon. statusBar()->addWidget(label).   van   descritos   en   un   fichero   de   recursos   multiplataforma   que   lleva   la   extensión   qrc.  toda  una  serie  de  slots  entre  los  cuales  estarán  las  funciones   que  ejecutan  las  acciones.   como   pueden   ser   iconos.  como  newFile(). editor->addAction(pasteAction).  si  está  seguro  de  cerrar.  PNG.   para   por   ejemplo   interceptar   la   señal   de   cierre   de   la   aplicación   con   alguna   ventana   de   diálogo   que   pueda   preguntar.   usando   la   función   addWidget().  Otra  cosa  esencial  que  debe  de  tener  en  la  implementación  de   su   constructor.   esto   se   hace   con   su   función   miembro   setCentralWidget(QWidget  *).0">     <qresource>   <file>images/icon.

 se  encuentre  en  main()   antes  de  que  se  llame  a  QApplication::exec().png”.       QSettings  (ajustes)     Es   muy   útil   y   agradable   para   el   usuario.toBool().  y  lo  recuperen  la  próxima  vez  que  sea  ejecutada.   QVariant  QSettings::value  (  const  QString  &key.  const  QString  &application=  QString(). Inc".   y   usa   funciones   de   QWidget   como   show()   para  mostrarse  y  setPixmap(QPixmap)  para  establecer  el  fondo  a  mostrar.QRect(200.value("showGrid".QObject  *parent  =  0  )   Ejemplo  de  creación  de  settings:   QSettings settings("Qt Software.     Cada  sistema  operativo  o  plataforma.72     Donde   vemos   que   el   fichero   del   icono   se   encuentra   en   la   carpeta   del   proyecto   en   “images/new.   Dicha   clase   es   QSettings.   QSettings::QSettings  (  const  QString  &organization.  finalmente  el  método   showMessage()  permite  sobreimpresionar  un  mensaje  sobre  el  fondo  de  dicha  pantalla.   que   el   aspecto   que   el   interface   tenía   al   cierre.toRect().  provee  de  una  clase  y  métodos  para  tratar  este  tema  de  manera   uniforme   e   independiente   de   la   plataforma. "TextEditor").400.showGridAction->isCheched()).  por  eso  Qt.  int  alignment  =  Qt::AlignLeft.   En   cualquier   caso   es   un   elemento   más   de   una   aplicación  que  muy  a  menudo  se  usa.  y  lo  guarda   en  sitios  diferentes.setValue("showGrid".   por   diversos  motivos  como.400)).value("geometry". settings. geometry()).  indicar  al  usuario  que  se  están  cargando  y  darle  información  durante  el   tiempo   de   espera   o   por   pura   autopromoción.true).     La   clase   que   la   describe   es   QSplashScreen.  const  QVariant  &defaultValue  =  QVariant()  )  const   Ejemplo  de  recuperar  ciertos  settings:   QRect rect = settings.  el  setter  y  getter  de  la  misma.   es   muy   usual   que   las   aplicaciones   modernas   guarden   una   serie   de   parámetros   que   determinan   todo   esto   que   llamamos  “settings”.   void  QSplashScreen::setPixmap  (  const  QPixmap  &pixmap)   void  QSplashScreen::showMessage  (  const  QString  &message.   void  QSettings::setValue  (  const  QString  &key.  const  QColor  &color  =   Qt::black  )         .  const  QVariant  &value)   Ejemplo  de  guardar  ciertos  settings:   settings.     Lo  más  habitual  es  que  el  código  que  muestra  el  splash  screen.   y   mediante   sus   métodos   setValue()  y  value().  gestiona  el  almacenamiento  y  recuperación  de   dichos  ajustes  (settings).setValue("geometry".200.  gestiona  esto  de  una  manera  distinta. bool showGrid = settings.   Por   lo   que.   vuelva   a   recuperarse   al   reabrir   la   aplicación   al   día   siguiente.   Splash  screens     Son   las   ventanas   que   aparecen   mientras   se   cargan   los   elementos   de   un   programa.

73       Veamos  un  ejemplo  sencillo:   #include <QtGui/QApplication> #include "mainwindow.   se   muestra   el   splash   screen   con   su   gráfico.   y   sobre   esta   base   vamos   a   apoyar   el   resto   de   la   enseñanza   que   viene   tras   el   tema   20.  Por  lo  tanto. splash->show(). w. Qt::AlignRight | Qt::AlignTop.   luego   se   muestra   el   primer   mensaje   sobre   el   Pixmap   en   la   zona   superior   derecha   en   texto   blanco.   e   introduciremos   nuevos   conceptos.  y  lo  haremos  primero  a  código  puro.       En   el   próximo   tema... establishConnections(). splash->finish(&w). splash->showMessage("Iniciand ventana principal .   en  este  caso  la  MainWindow.                   . delete splash.  luego  el  siguiente  mensaje. char *argv[]) { QApplication a(argc.  luego  la  acción  que  indica  el  mismo.  lo  que  hace  es  esperar  a  que  se  cargue  completamente  el  Widget.   para  comprender  y  repasar  todos  los  conceptos  bien. Qt::white). return a. splash->showMessage("Estableciendo conexiones ..   entonces   muestra   la   ventana   principal.exec(). loadModules(). MainWindow w.   pero   todos   fundamentados   en   esto  primeros  20  temas.  que  conforman  el  nivel  Básico.".h" int main(int argc.  es  muy  importante  que  todos  los   conceptos   queden   perfectamente   aclarados   con   este   ejemplo.png")). splash->setPixmap(QPixmap(":/images/splash. topRight.  y  finalmente  veremos  como  eso  mismo  se   puede  hacer  de  manera  visual  en  el  Qt  Designer.   La   función  finish(const  QWidget&). Qt::white).   ya   que   hasta   aquí   hemos   completado   el   bloque   principal   y   básico   para   construir   aplicaciones   en   Qt.  sin  diseño  visual.   luego   se   lleva   a   cabo   la   acción  que  indica  el  mensaje..".   vamos   a   desarrollar   desde   cero   una   aplicación   completa   con   todo   lo   que  hemos  aprendido  hasta  el  día  de  hoy. }   Date   cuenta   de   que.  y   así   hasta   que   una   vez   todo   esta   cargado   e   iniciado. Qt::AlignRight | Qt::AlignTop.   donde   abundaremos   en   conceptos   ya   presentados.".. argv).show().. splash->showMessage("Cargando modulos . QSplashScreen *splash = new QSplashScreen.  antes  de  hacer  un  close()  sobre  él  mismo. Qt::white).

74                                                                   .

    La  clase  MainWindow     .   seleccionamos  los  6  iconos  y  los  abrimos. app.  o  algo  similar  desde  cero.   los   recursos  se  guardan  en  una  librería  estática.  y   navegamos   hasta   el   directorio   images   donde   hemos   copiado   los   iconos   que   vamos   a   usar.exec().  Volvemos  a  pulsar  en  Add-­‐>  Add  Files. MainWindow w.   Al   abrirse   el   fichero   qrc   en   el   Qt   Creator.     De   nuevo   hacemos   clic   derecho   sobre   le   nuevo   proyecto   y   añadimos   un   nuevo   Qt-­‐>Qt   Resource   File.   Introduciremos   algunos   conceptos   nuevos.h  y  .   su   clase   base   será   QMainWindow.   pero   en   algunas   plataformas.  dentro  de  nuestro  proyecto.  con   menú   principal.setOrganizationName("Trolltech").  y  que   luego  por  tu  cuenta.   donde   “:”   hace   referencia  a  los  recursos  internos.  así  que   ya   podemos   acceder   a   ellos   usando   una   URL   del   tipo   “:/images/icono.  Lo  siguiente   que   vamos   a   hacer   es   copiar   los   iconos   que   vamos   a   usar   (directorio   images)   en   nuestro   proyecto. }   Usamos   la   macro   Q_INIT_RESOURCE(nombre_base_del_qrc).75       TEMA  20   DESARROLLO  DE  UNA  APLICACIÓN  COMPLETA       En  este  tema  vamos  a  desarrollar  un  editor  de  texto. w.  vamos  a  usar   una   MainWindow   que   hemos   derivado   de   QMainWindow   y   que   esta   implementada   en   los   ficheros  mainwindow.   pero   pocos.  El  proyecto  puedes  abrirlo  directamente  en   el  Qt  Creator.  con  capacidad  para  cut/paste.  también  en  una  carpeta  llamada  images.png”.  intentes  desarrollar  lo  mismo.cpp   #include <QApplication> #include "mainwindow. app. argv).setApplicationName("Application Ejemplo").  pero  te  recomiendo  que  sigas  las  explicaciones  que  se  van  a  dar  en  este  tema.  Ya  aparecen  los  6  iconos  adscritos  al  prefijo  “/”.   al   que   llamaremos   también   “application”.  a   parte   de   los   dos   miembros   de   QApplication   que   se   encargan   de   guardar   en   la   aplicación   el   nombre  de  la  empresa  que  hizo  la  aplicación  y  el  nombre  de  la  misma. return app.  Todo  lo  demás  ya  lo  hemos  visto  anteriormente.  En  el  campo  de  texto  Prefix.cpp  .h" int main(int argc.  vamos  a  poner  “/”.   como   hemos   hecho   en   los   temas   anteriores.  Pulsamos  sobre  él  y  luego  sobre  Add   Prefix.     Fichero  main.   aunque   en   la   mayoría   de   los   casos   no   sea   necesario   y   los   recursos   se   carguen   automáticamente.  al  que  llamaremos  “application”.  y  abajo  un  botón  Add.   aparece  una  ventana  superior  vacía.  Como  vemos.   la   clase   se   llamará   MainWindow.   y   desactivaremos   la   opción   “Generate  form”  para  así  crear  nosotros  mismos  el  interfaz  gráfico  en  código  puro.     Vamos  a  crear  un  nuevo  proyecto  “Qt  Gui  Application”.   barra   de   herramientas   y   status   bar.show().   para   seguir   siendo   fieles   al   estilo   de   enseñanza  gradual  que  estamos  usando  en  este  libro. QApplication app(argc. char *argv[]) { Q_INIT_RESOURCE(application).

 para  saber  los  slots  que  tendremos  que  declarar  en  esta  clase. void writeSettings().   que   devuelve   un   puntero   al   texto   del   mismo   del   tipo   QTextDocument.   usaremos   isModified()   para   saber   si   el   documento  ha  sido  modificado  en  algún  momento.     Lo  siguiente  que  vamos  a  declarar  son  la  funciones  privadas. bool saveAs().     .76       Gran  parte  del  código  y  de  la  estructura  del  mismo  de  esta  clase.     Tenemos   también   el   menú   Edit   con   los   siguientes   submenús   (Cortar.  copy().   Puesto   que   cada   entrada   del   menú   requiere   de   una   conexión   entre   la   señal   triggered()   de   la   acción.   windowModified.  También  podrás  ver  la  función   más   importante.   el  resto  serán  slots  a  declarar  e  implementar.   podrás   ver   entre   sus   miembros. void open().   y   un   slot   que   ejecuta   la   acción.     Finalmente   tenemos   el   menú   Ayuda   con   los   submenús   (Acerca   de   y   Acerca   de   Qt).  A  excepción  de  la  opción  Salir.   Por   lo   que   tendremos   que   dar   un   repaso   a   las   opciones   de   menú  que  vamos  a  poner.     Tendremos  un  menú  File  con  los  siguientes  submenús  (Nuevo. bool save().   de   los   cuales. void loadFile(const QString &fileName).   vamos   a   usar   QPlainTextEdit   como   el   widget   principal.   los  slots  que  usaremos  en  los  menús. bool maybeSave(). void about(). createToolBars(). void readSettings().  Abrir.       Cuando   el   documento   es   modificado.   que   lo   hace   el   setter   setWindowModified.   Vamos   a   ver   primeramente   el   widget   que   vamos   a   designar   como   widget   central  o  principal  de  la  Ventana  Principal  (MainWindow).  acerca  de  Qt  viene  ya  preimplementado  como  el  slot  aboutQt()  de  QApplication.   son   propias   de   una   aplicación   GUI   genérica:   private: void void void void createActions().   Copiar   y   Pegar).   no   es   necesario   implementar   nuevos   slots   para   este   menú.   Puesto   que   todos   ellos   pueden   ejecutarse   desde   los   slots   predefinidos   en   QPlainTextEdit. bool saveFile(const QString &fileName).   Si   consultas   dicha   clase   en   el   asistente   (Qt   Assitant).  paste().   que   es   document(). QString strippedName(const QString &fullFileName).  Guardar   como  y  Salir). void documentWasModified().   Así  los  slots  que  vamos  a  declarar  finalmente  son:   private slots: void newFile(). createMenus().  que  se  encargarán  de  llevar   a   cabo   las   tareas   principales   asignadas   a   los   slots. void setCurrentFile(const QString &fileName).  podrán  ser  usados  en   otras   aplicaciones.       Luego   tendremos   que   mirar   los   slots   que   vamos   a   necesitar   definir   en   la   nueva   clase.     Al   ser   una   aplicación   editora   de   texto.   De   entre   los   miembros   de   éste   último. createStatusBar().   debe   de   haber   una   acción   que   cambie   la   propiedad   del   widget   modificado.   Por   tanto   vamos  a  crear  un  slot  que  ejecute  dicho  cambio  al  que  llamaremos  documentWasModified().   respectivamente   cut().  como  cut().  Guardar.  que  la  ejecutaremos  con  el  slot  close()  de  QWidget.   y   tareas   secundarias   propias   del   tipo   de   aplicación.   copy()   y   paste().   Todas   las   tareas   privadas   que   vamos   a   definir.

  para   que   el   usuario   decida   si   guarda   o   no   guarda   los   cambios.-­‐   Es   una   bloque   simple   que   extrae   el   nombre   del   fichero.       Las   variables   privadas   que   vamos   a   usar   corresponden   con:   la   propiedad   currentFile   descrita   antes.   las   2   barras   de   herramientas  QToolBar  y  las  acciones  propias  a  los  menús  QAction.-­‐   Aquí   es   donde   vamos   a   poner   el   código   que   crea   los   menús. *saveAct.-­‐   Aquí   es   donde   pondremos   el   código   que   crea   las   barras   de   herramientas.   aunque   puedan   ser   declaradas   e   implementadas   a   gusto   del   programador.-­‐  Aquí  es  donde  pondremos  el  código  para  crear  la  barra  de  estado.   createStatusBar.  con  sus  separadores  y  todo.-­‐   Este   bloque   puede   estar   integrado   en   los   2   anteriores.   muestra   una   ventana   de   diálogo   warning.   los   3   menus   QMenu.   o   puede   ser   separado   para   dejar   más   claro   el   código.   También  podemos  usar  esta  sección  para  deshabilitar  las  acciones  que  inicialmente  lo  deban  de   estar. *saveAsAct.   .-­‐   Aquí   podremos   el   código   que   se   va   a   encargar   de   guardar   los   settings   antes   de   salir  de  la  aplicación.   si   el   documento   ha   sido   modificado.   maybeSave.  Editar  y  Ayuda. QToolBar *editToolBar.   extrayéndolo   del   path   completo.   createMenus.   escribir   su   contenido   y   notificar   igualmente  sobre  el  mismo.   createActions.   strippedName.   loadFile.  como  establecer  el  nuevo  CurrentFile  o  notificar  su  acción  en  el  status  bar.-­‐   Este   bloque   se   encargará   de   abrir   el   fichero   y   leer   su   contenido   cargándolo   en   QPlainTextEdit   con   setPlainText().-­‐  Es  el  setter  de  la  propiedad  CurrentFile  que  se  encargará  de  llevar  el  nombre  del   fichero   actual   al   que   se   hace   alusión. QMenu *helpMenu.  ya  que  son  fundamentales  para  el  desarrollo  de  cualquier  aplicación   GUI.  y  los  va  a  aplicar.77     Vamos  a  verlas  una  por  una.   setCurrentFile.   Es   muy  típico  en  las  aplicaciones  modernas.-­‐   Este   bloque   se   encargará   de   abrir   el   fichero.   el   widget   central   que   es   un   QPlainTextEdit. *openAct.   en   nuestro   caso   Fichero. QPlainTextEdit *textEdit. QMenu *fileMenu.  con  todos  sus   elementos. QAction QAction QAction QAction *newAct.  tal  cual   vimos   en   el   tema   anterior   con   QAction.   Podrá   hacer   una   serie   de   acciones   relacionadas   con   el   manejo  de  ficheros  y  notificación  del  cambio  en  el  contenido  de  dicho  fichero. QToolBar *fileToolBar.  y  podremos  mostrar  un  mensaje  inicial  si  lo  vemos  oportuno.   Su   función   sería.   readSettings.-­‐  Aquí  pondremos  el  código  que  se  va  a  encargar  de  leer  los  settings  como  vimos  en   el  tema  anterior.   pero   es   conveniente  el  estructurar  tus  aplicaciones  de  esta  manera  para  hacer  más  legible  y  mantenible   tu  código.   en   nuestro  caso  van  a  ser  2. QMenu *editMenu.   saveFile.   Además   hará   todas   las   notificaciones   que   requiera   sobre   la   carga  del  fichero.  Estas  son  las  variables  que   vamos  a  usar:   QString curFile.   y   las   conectaremos   con   los   slots   que   declaramos   antes.   writeSettings.  una  para  opciones  de  Fichero  y  la  otra  para  opciones  de  Editar.-­‐  Aquí  pondremos  el  código  que  crea  todas  las  acciones  de  todos  los  menús.   createToolBars.

createActions().   es   ejecutada   automáticamente   cada   vez   que   un   top-­‐level   widget   es   cerrado. *cutAct.   la   barra   de   estado. SIGNAL(contentsChanged()). if (ret == QMessageBox::Save)   .78     QAction QAction QAction QAction QAction QAction *exitAct. tr("Application"). createToolBars().   leer   los   settings   de   el   último   cierre.   La   vamos   a   declarar   en   la   zona   protected   para   que   pueda   ser   reimplementada   en   hijos   que   puedan   derivarse   de   la   clase   MainWindow  en  futuras  aplicaciones.   luego   crear   los   menus.   luego   crear   las   acciones   para   los   menús.   las   barras   de   herramientas. tr("El documento ha sido modificado.     void MainWindow::closeEvent(QCloseEvent *event) { if (maybeSave()) { writeSettings(). *aboutQtAct.  Si  el  evento  es  aceptado  (accept())  entonces  sale  sin  problemas.  vamos  también  a  reimplementar   una  función  virtual  de  QWidget  llamada  closeEvent(QCloseEvent)  que  nos  permitirá  interceptar   el  cierre  de  la  aplicación. *pasteAct. // es un int con un codigo ret = QMessageBox::warning(this.\n" "_Quiere guardar los cambios?").  y   devolverá  un  bool  de  acuerdo  al  botón  pulsado:     bool MainWindow::maybeSave() { if (textEdit->document()->isModified()) { QMessageBox::StandardButton ret. *aboutAct. this.  para  notificar  al  usuario  si  el  documento  tiene  cambios  sin  guardar.  una   característica   muy   usual   también   en   aplicaciones   modernas. event->accept().       Implementación  de  MainWindow         A  continuación  vamos  a  ver  la  implementación  de  todos  estos  bloques  y  miembros  que   hemos  visto  anteriormente. QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel).  y  establecer  como  fichero  actual   (CurrentFile)  a  ninguno. setCentralWidget(textEdit).  si  es  ignorado  (ignore())   entonces  no  es  cerrado.  Aquí  será  maybeSave  quien  muestra  la  ventana  de  elección  al  usuario. setCurrentFile(""). } }   La   función   closeEvent().   Para  finalizar  la  declaración  de  la  clase  MainWindow. SLOT(documentWasModified())). } else { event->ignore(). *copyAct. connect(textEdit->document(). createStatusBar().       Comenzamos  con  el  constructor  que  define  el  interfaz  inicial  de  la  aplicación:     MainWindow::MainWindow() { textEdit = new QPlainTextEdit. }   Quedan  muy  claras  todas  las  acciones  que  lleva  a  cabo:  establecer  al  QPlainText  como  el  widget   central. createMenus().   conectar   la   señal   de   cambios  en  el  documento  con  el  slot  que  hemos  creado  para  ello. readSettings().

  Lo   que   tendremos  al  final  es  algo  como  esto:       . }     El   resto   del   código   puede   ser   entendido   perfectamente   con   lo   que   hemos   estudiado   hasta   ahora.  le  pondremos  de  nombre  application2.   y   las   conexiones   con   los   slots.       Ahora  llegó  el  momento  de  hacer  la  misma  aplicación  pero  usando  el  Qt  Designer  para   diseñar  el  interfaz  gráfico.       Crear  la  aplicación  gráficamente       Hacer  uso  de  Qt  Designer  para  hacer  una  aplicación.  Al  abrir  el  ui  veremos  que  aparece  un   formulario  como  éste:           Así   que   le   añadimos   un   PlainTextEdit   y   ocupamos   con   él   todo   el   área   que   nos   permite.   Pues   bien   vamos   a   abrir   un   nuevo   proyecto.  facilita  mucho  las  cosas.   Sólo   haremos   la   introducción   a   un   concepto   nuevo   que   es   la   función   QObject::tr(QString)   que   devuelve   la   traducción   del   argumento   pasado.  no  solo   para   dibujar   el   interfaz   si   esto   es   necesario.   por   ejemplo   antes   del   Salir.79     return save().   veremos  que  en  el  inspector  de  objetos  de  la  derecha  superior.   y   esta   vez   si   vamos   a   crear  el  fichero  *.   si   no   para   crear   los   menus.   las   acciones   del   mismo. } return true.         Vamos   a   ir   rellenado   los   menús   haciendo   doble   clic   sobre   “Type   Here”   y   pulsamos   sobre   Add   Separator   para   añadir   un   separador   de   menú.  lo  ha  puesto  como  centralWidget.ui  . else if (ret == QMessageBox::Cancel) return false.   y   es   usado   para   internacionalizar  una  aplicación.

  opción   a   opción.   guardar.   y   pulsamos   sobre   la   señal   triggered().   Para   ello.     on_action_Nuevo_triggered().   excepto   los   que   ya   vienen   de   herencia.   y   elegimos   “go   to   slot”.   acerca   de.       Los  slots  que  se  han  creado  automáticamente  de  esta  manera  son:     .   on_ActionName_SignalName().   para   que   aparezcan   los   iconos.   hacemos   clic   derecho   sobre   accion_Nuevo.  empieza  a  rellenarse  el  sólo  como  esto:         Haciendo  doble  clic  en  cada  acción.         Ahora   vamos   a   crear   los   slots   que   van   a   recibir   las   acciones.   guardar   como.   que   se   ha   creado   automáticamente.   que   es   la   que   vamos   a   conectar.   Vamos  a  hacer  lo  mismo  con  todos  los  submenús.   Todos   los   slots   creados   en   el   Qt   Designer.   llevan   esta   forma.   En   el   inspector   de   propiedades   ponemos   el   statusTip   a   mano.   Así   vamos   a   crear   slots   también   para   abrir.   El   resto   de   acciones   las   vamos   a   llevar   a   slots   predefinidos   en   las   clases   correspondientes  de  Qt  como  hemos  visto  ya.  Verás  que  en  la  parte  inferior.80       Así   iremos   rellenando   los   menús   conforme   hemos   descrito   al   principio   de   este   tema.   se   ha   tener   ya   creado   y   cargado   el   qrc   tal   como   lo   vimos   en   el   ejemplo   anterior.   Inmediatamente   nos   lleva  al   código   del   slot   que   se   acaba   de   crear.  y  dejaremos  para  después  la  parte  referente  a   las  conexiones.  en  el  Action  Editor.  podemos  editar  sus  partes  de  la  siguiente  manera:     Por   supuesto.

81     private slots: void on_actionAcerca_de_triggered(). void on_action_Nuevo_triggered().  el  resto  hay  que  teclearlo  a  mano.  Y  esto  es  todo  lo  que  se  puede  hacer   con  el  Qt  Designer.       y   además   se   ha   establecido   la   conexión   automáticamente   entre   la   señal   triggered()   de   dicha   acción  con  dichos  slots.   donde   esta   todo   vacío.   por   lo   que   es   parte   tendremos   que   ponerla   en   el   constructor   de   MainWindow  a  mano. void on_actionGuardar_como_triggered().   cambiando   su   objectName   en   el   inspector  de  propiedades. void on_actionGuardar_triggered().   Hacemos   lo   mismo   con   Cortar.  con  lo  que  una  vez  lleno   quedaría  de  esta  manera:             Como   puedes   observar   no   hemos   podido   poner   la   conexión   del   aboutQtAct   debido   a   que   no   aparece   el   receiver   qApp.   y   ahora   hacemos   clic   derecho   en   la   clase   MainWindow   en   el   inspector   de   objetos   y   elegimos   “add   toolbar”.ui.   y   lo   hacemos   justo   el   número   de   veces   que   conexiones   necesitamos   para   las   acciones   de   los   menús   que   van   a   slots   predefinidos   en   Qt   (son   4).   creamos   los   3   botones   de   dicha   barra   de   herramientas.  Abierto  y  Guardar   sobre   la   primera   ToolBar.   El   resultado   final   es   algo   así:           Ya   tenemos   toda   la   parte   gráfica   construida.   y   ya   sólo   nos   queda   ir   rellenando   el   código   correspondiente   de   la   clase   MainWindow.   Ya   tenemos   2   toolbars.       Ahora.  por  lo  que  no  hace  falta  establecerlas  en  el  código  fuente.   vamos   a   desactivarlas   por   defecto   en   el   panel   de   propiedades.   nos   vamos   al   Signal   &   Slots   editor.   Copiar   y   Pegar   en   la   segunda   barra   de   herramientas.         .  Arrastrando  los  iconos  del  Action  editor  de  Nuevo.  con  los  slots  que  les  corresponden.   la   primera   la   llamamos   fileToolBar   y   la   segunda   editToolBar.   Ahora   eligiendo   las   acciones   de   Cortar   y   Pegar.   Pulsamos   sobre   el   botón   verde   +.  Verás  que  los  iconos  se  vuelven  más  apagados. void on_actionAbrir_triggered().   Ahora   vamos   a   ir   rellenando   todas  las  conexiones  de  cada  acción.  ya  están  en  el   *.         Volvamos   a   activar   el   Action   Editor.   deshabilitando   el   cuadro   “enabled”.

  4.     5. SIGNAL(copyAvailable(bool)).   6.  verás  que  el  autorellenado  te  va  dando  pistas  de  todos  los   objetos  y  miembros  accesibles  desde  ui. readSettings().-­‐  Editar  las  acciones  que  se  crean  con  el  menú  principal  cumplimentado  para  añadirles  iconos.-­‐  Situar  el  centerWidget  debajo  del  toolbar  y  encima  del  status  bar. connect(ui->actionAcerca_de_Qt.   7.   status  tips.-­‐  Escribir  todas  las  opciones  del  menú  principal.  etc.   2.  y  es  el  código:   namespace Ui { class MainWindow. setCurrentFile(""). SLOT(setEnabled(bool))). SLOT(setEnabled(bool))).   y   por   lo   tanto   para   acceder  a  los  objetos  dentro  del  mismo. }   private: Ui::MainWindow *ui. SLOT(aboutQt())).-­‐  Crear  los  slots  que  se  requieran  de  las  acciones  con  botón  derecho-­‐>  go  to  slot. ui(new Ui::MainWindow) { ui->setupUi(this). SIGNAL(triggered()).-­‐   Ya   podemos   añadir   el   código   que   haga   falta. SIGNAL(copyAvailable(bool)). ui->statusBar->showMessage(tr("Preparado")).-­‐  Crear  las  toolbar  que  se  requieran  arrastrando  los  iconos  de  las  acciones  sobre  aquellas. SLOT(documentWasModified())). connect(ui->plainTextEdit->document().  luego  darlos  de   alta  en  el  Qt  Designer  como  miembros  de  MainWindow. setCentralWidget(ui->plainTextEdit). ui->action_Copiar.       Dejamos   por   tanto   como   ejercicio   de   repaso   para   el   lector.   con   ayuda   del   nuevo   puntero   *ui   para   designar   el   diseño.   3. this.             . qApp.ui.   que   desactivaría   directamente   la   acción   Guardar.   No   obstante   te   obsequiamos   con   el   código   del   constructor   de   la   clase   MainWindow   para  que  te  sirva  como  orientación:   MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent).   y   que   si   pruebas   a   escribirlo   en   cualquier   parte   de  la  implementación  de  MainWindow. SIGNAL(contentsChanged()).     Lo   que   designa   ese   código   es   un   puntero   al   interface   que   hay   en   ui. }       Resumiremos  por  tanto  los  7  pasos  a  seguir  para  crear  una  aplicación  con  ayuda  del  Qt   Designer  (forma  visual):     1.  lo  cual  es  tan   sencillo  como  esto:   ui->actionGuardar->setEnabled(false).82       Si   revisamos   el   código   de   mainwindow.   veremos   que   hay   algo   nuevo   que   hace   referencia  al  contenido  que  hay  en  el  fichero  *. ui->actionCor_tar.   la   cumplimentación   del   código  necesario  para  que  esta  aplicación  funcione  igual  que  su  antecesora  hecha  completamente   en   código.  tendremos  que  hacerlo  desde  ese  puntero.  su  estado  inicial.-­‐  Hacemos  todas  las  conexiones  entre  las  acciones  y  los  slots  en  el  signal-­‐slot  editor. connect(ui->plainTextEdit. connect(ui->plainTextEdit.h.

  y   que   lleva   dentro   un   QStackedLayout.   mailAndNewsPage   y   advancedPage.  y  lo  que  hacemos  es   vincular  una  listWidget  con  él  mediante  la  señal  currentRowChanged(int)  que  vimos  en  el  tema     .   que   crea   barras   que   dividen   una   ventana   en   varios   espacios.   ocultando   el   resto   al   usuario.  Para  averiguar  el  index  de  un  widget  contenido  en  este  Layout  se  usa  la   función  indexOf(QWidget). connect(listWidget. SLOT(setCurrentIndex(int))).   appearancePage.  Son  añadidos  como  widgets  a  su  vez  del  stacked  layout. stackedLayout = new QStackedLayout.   No   se   provee   de   ningún   control   para   que   el   usuario  pueda  navegar  por  las  páginas. listWidget->addItem(tr("Navegador")). listWidget->addItem(tr("Avanzado")).83     TEMA  21   LAYOUT  A  FONDO       En  el  tema  12  ya  tocamos  el  tema  del  layout  como  el  medio  mediante  el  cual.  y  podemos  hacer  una  visible  mediante  el  setter   setCurrentIndex(page).  donde  se  podrán  abrir  más  de  un  documento  a  la  vez  en  la   misma  ventana.  así  que  esto  sólo  puede  hacer  mediante  el  uso  de  código. }   Aquí   previamente.   son   widgets. stackedLayout->addWidget(webBrowserPage).  es  vincular  las  opciones  de  un   widget   con   las   diferentes   páginas   del   stacked   layout.   y   puede   ser   cambiado   mediante   el   setter   setGeometry().   Sin   embargo.     QStackedLayout     Esta   clase   crea   un   conjunto   de   widgets   hijos   que   presentan   páginas   apiladas.   webBrowserPage.  que  conforman  una  ventana   de  diálogo  completa. mailAndNewsPage.  los  widgets   negociaban  el  espacio  de  la  ventana  que  los  contiene. …….   En   este   capítulo   vamos   a   añadir   nuevas   clases   a   este   estudio   para   disponer   el   espacio   de   una   ventana   viendo   QSplitter. listWidget->setCurrentRow(0).  nadie  usa  estos  métodos  para  posicionar  de  manera  absoluta  ningún  widget  dentro  de   la  ventana.   QScrollArea. stackedLayout->addWidget(appearancePage).  todo  widget  ocupa  un  espacio  rectangular. listWidget->addItem(tr("Correo y foros")).   QWorkSpace   o   QMdiArea   que   nos   introducirá   en   las   aplicaciones  MDI  (multi-­‐documento).   o   una  QListWidget.   y   sólo   puede   mostrar   una   a   la   vez.  por  ejemplo  layouts  con  toda  una  serie  de  widgets  dentro. SIGNAL(currentRowChanged(int)).   Las  páginas  están  numeradas  del  0  en  adelante.   Esto   se   puede   hacer   con   un   ComboBox.  si  no  que  usa  de  layouts  para  ello.  que  devuelve  su  entero. stackedLayout->addWidget(mailAndNewsPage).     Como  dijimos  entonces.  creando  una  interface  flexible  a  múltiples   circunstancias   como   cambio   de   idiomas   sin   truncado   de   palabras. listWidget->addItem(tr("Apariencia")).   QStackedWidget. advancedPage listWidget = new QListWidget. stackedLayout.     Bien.  y  las   coordenadas   de   las   vértices   superior   izquierdo   e   inferior   derecho   se   guardan   en   la   propiedad   “geometry”.  como  una  caja.   es   una   widget   que   provee   Qt.   o   adaptación   del   GUI   a   otras   plataformas. stackedLayout->addWidget(advancedPage). webBrowserPage.  una  de  tantas  formas  de  hacer  funcionar  este  Layout.   que   puede   ser   accedida   mediante   el   getter   geometry()   como   vimos   en   el   ejemplo   del   tema   anterior   para   los   settings.   que   permite   que   una   zona   pueda   verse   en   parte   y   el   resto   pueda   ser   accedido   mediante   una   barra   de   scroll.  Vamos  a  ver  un  ejemplo:   PreferenceDialog::PreferenceDialog(QWidget *parent) : QDialog(parent) { //appearancePage.

-­‐  Añadir  un  QListWidget  y  un  QStackedWidget  al  mismo.  pulsando  en  el  botón  “Edit  signal”  de  arriba  del  Designer.  con  el  setter  y  además  slot  setCurrentIndex(int).   Fíjate   en   el   detalle.   de   que   fijamos   la   posición  inicial  del  ambos  con  setCurrentRow(0).   usando  las  flechitas  en  la  zona  superior  derecha).         5.   OK  y  Cancel  (QButtonBox).   4.  dentro   de   una   ventana   de   diálogo   de   selección   de   preferencias.84     17.  ya  que  ambos  pasan  un  índice  igual  (desde  0   a  N).  y  escogiendo  “Insert  Page”  para  añadir  nuevas  páginas.-­‐  Conectar  ambos  widgets.  luego  habría  que  añadir  los  botones  de  la  ventana  de  diálogo  típicos.  De  esta  manera  podemos  disponer  espacialmente  a  ambos  widgets  contenendores.   (haciendo   clic   derecho   sobre  el  layout.  y  pulsamos   sobre   el   ListWidget   y   arrastramos   hacia   el   StackedWidget.   Al   soltar   el   ratón   aparecerá   la   ventana   para   elegir   la   señal   y   el   slot   que   estarán   conectados.-­‐   Rellenar   cada   página   con   widgets   y   layouts   conforme   sea   necesario.  accepted()  de  parte  de  los  botones.  Pues  siguiendo  estos  sencillos  pasos:   1.  y  rejected()  de  parte  de     .   3.  y  de  igual  manera  conectarlos  con  la  ventana  de  diálogo.  y  para  navegar  por  ellas.   ¿Cómo  se  haría  esto  mismo  en  el  Qt  Designer?.   2.-­‐  Crear  un  formulario  nuevo  basado  en  QDialog  o  en  QWidget.  con  accept()  del  diálogo.   Luego   ponemos  la  propiedad  currentRow  del  listWidget  a  0.-­‐   Añadimos   los   ítems   de   las   opciones   en   el   ListWidget   con   clic   derecho-­‐>edit   ítems.   de   manera   que   una   flecha   los   conectará.  y  conectar   las  señales.   Así  de  fácil  sería  diseñarlo.

splitter. settings.exec()   para   ejecutarlo   en   modo   modal   y   que   nos   devuelva   un   valor   con   nuestra   selección.toByteArray()). settings.   Para   añadir   barras   de   scrolling   a   un   widget.   Ya   podríamos   usar   este   diálogo   en   la   aplicación   completa.   usando   dialog.   se   van   poniendo   uno   al   lado   del   otro   conforme   se   van   creando   y   según   la   disposición. mainSplitter->saveState()). settings. }   ¿Cómo  diseñar  con  splitters  en  el  Qt  Designer?.setValue("rightSplitter". QTextEdit *editor1 = new QTextEdit.   “Layout   Horizontally   in   Splitter”   o   “Layout   Vertically   in   Splitter”.   solo   hay   que   crear   un   objeto   QScrollArea   y   añadirle   dicho     .  desplazando  los  separadores.addWidget(editor3).     int main(int argc.   void MailClient::writeSettings() { QSettings settings("Software Inc. }   Los   splitters   pueden   anidarse   horizontales   con   verticales   y   crear   disposiciones   más   complejas.  Los   widgets   separados. settings. settings. QSplitter splitter(Qt::Horizontal).  y  estos  están  separados  por  separadores.       QScrollArea     Provee   una   zona   visible   (viewport)   y   2   barras   de   scroll   (vertical   y   horizontal). QSize(480.beginGroup("mainWindow"). QTextEdit *editor3 = new QTextEdit. resize(settings.value("mainSplitter").   con   reject()   del   diálogo. argv). rightSplitter->saveState()).".beginGroup("mainWindow"). settings.exec().show(). "Mail Client").addWidget(editor2).  los  seleccionas. size()).   Los   setting   pueden   guardar   la   posición   de   los   mismos.85     los   botones. return app.toByteArray()).setValue("size".setValue("mainSplitter". splitter. QTextEdit *editor2 = new QTextEdit.endGroup().  QDialog::Accepted  ó  QDialog::Rejected.  y  en  la  barra  de   herramientas   superior   podemos   elegir.  Es  similar  a  como  disponíamos  los  widgets  con   layouts  verticales  y  horizontales. splitter.value("size". mainSplitter->restoreState(settings.       QSplitter     Es  un  widget  que  contiene  otros  widgets.".value("rightSplitter").toSize()). rightSplitter->restoreState(settings.addWidget(editor1). char *argv[]) { QApplication app(argc.  Pones  2  o  más  widgets  juntos.  Los   usuarios  pueden  cambiar  el  tamaño  de  los  widgets  separados.endGroup(). } void MailClient::readSettings() { QSettings settings("Software Inc. splitter. settings. 360)). "Mail Client").   usando   grupos   en   la   grabación   de   los   mismos.

  que   se   llama   addDockWidget(zona.  otra  abajo.  una  arriba.png")). this. char *argv[]) { QApplication app(argc.  se  añade  un  widget  a  un  dock  window.     MainWindow::MainWindow() { workspace = new QWorkspace. argv).   en    MainWindow  son. return app. shapesDockWidget->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea). scrollArea.86     objeto   con   setWidget(QWidget   *). SLOT(updateMenus())).  widget).setWindowTitle(QObject::tr("Icon Editor")).show().   o   puede  quedarse  flotando  como  una  ventana  aparte.viewport()->setBackgroundRole(QPalette::Dark).  y  esa  opción  suele  ser  checkable   indicando  en  cada  momento  la  ventana  del  documento  que  está  activa.  y  entonces  cada  documento  que  se  abra. scrollArea.   Pueden   ser   cerradas   como   una   ventana  normal.       Suele  ser  muy  típico  que  una  aplicación  MDI. }   . setWindowIcon(QPixmap(":/images/icon. scrollArea. createActions().  y  activarlos  o  desactivarlos.exec(). }   QTextEdit   y   QAbstractItemView   (y   derivados). scrollArea. IconEditor *iconEditor = new IconEditor. createToolBars().   y   el   usuario   puede   moverlas   solo   cogiendo   del   título   y   arrastrando   de   ellas.  provea  de  un  menu  Window.     QDockWidget *shapesDockWidget = new QDockWidget(tr("Shapes")). createStatusBar().       Estas   ventanas   tiene   su   propio   título   incluso   enganchadas   a   la   principal. QScrollArea scrollArea.  Con  la  función  setWidget(QWidget). SIGNAL(windowActivated(QWidget *)).png")).  Las  área  de  enganche  de  las  dock  windows.   MDI     Toda   aplicación   que   dentro   de   su   ventana   central   pueda   abrir   varios   documentos   a   la   vez.   Se   hace   haciendo   que   la   clase   QWorkspace   sea   el   centralWidget. scrollArea.  que  sea  hijo  de  QWorkspace.   no   requieren  esto  para  poseer  barras  de  desplazamiento.   Podemos   acceder   al   widget   y   sus   miembros   mediante   QScrollArea  ::viewport()  que  devuelve  un  puntero  del  mismo. shapesDockWidget). createMenus().   tiene   una   función   para   añadir   docks   widgets. setWindowTitle(tr("MDI Editor")). addDockWidget(Qt::RightDockWidgetArea.viewport()->setAutoFillBackground(true).   es   llamada   una   aplicación   MDI. connect(workspace. setCentralWidget(workspace).setWidget(iconEditor). shapesDockWidget->setWidget(treeWidget).   al   estar   derivados   de   QAbstractScrollArea.     QMainWindow. iconEditor->setIconImage(QImage(":/images/mouse.     int main(int argc.     QDockWidget  y  QMainWindow     Un   dock   widget   es   un   widget   que   puede   aparcarse   en   un   lado   de   una   MainWindow.  que  permite   manejar  los  distintos  documentos.  y  una  a  cada  lado  del  central  Widget.

 haciendo  un  casting  al  valor  devuelto  por  QWorkspace::activeWindow().   como   nuevo   enfoque   a   las   aplicaciones  MDI.     El   constructor   de   los   widgets   documentos   que   se   abren.  por   lo   que   es   importante   que   cuando   se   llame   a   este   método   desde   cualquier   parte.  la  función  activeEditor  de  MainWindow.   pero   lo   haremos   usando   QMdiArea   y   QMdiSubWindow.   setAttribute(Qt::WA_DeleteOnClose).  haciendo  toggle  en  el  menú  Window).         En  este  tema  vamos  a  hacer  que  nuestra  aplicación  editora  de  texto  del  tema  20.  pueda   ser   MDI.   es   necesario   poder   recuperar   un   puntero   al   mismo.  Si  no  hay  activa  ninguna  ventana  entonces  devolverá  un  NULL  pointer.   Conectamos   la   señal   windowActivated()   con   un   slot   nuestro   que   programaremos   que   actulizará   el   contenido   de   los   menús  (por  ejemplo.   se   observe   si   devuelve  un  valor  nulo.  para  evitar  los  memory  leaks. } Aquí.     Para   poder   manejar   cada   documento.   si   no  que  se  creará  uno  nuevo  devolviendo  un  puntero  al  mismo.  puede  recuperar  el  puntero  a  dicho  documento  (en   este  caso  es  un  Editor).   no   se   borrará   el   documento   actual.   Editor *MainWindow::activeEditor() { return qobject_cast<Editor *>(workspace->activeWindow()).87       Este   es   un   constructor   típico   de   una   aplicación   MDI.   debe   de   activar   el   atributo   de   borrado  al  cerrar.  lo  cual  sería  imposible  sin  el  uso  de  un  casting  dinámico.                                   .   que  es  un  QWidget  *.       Cada   vez   que   se   pulsa   en   el   menú   Fichero-­‐>Nuevo.

88                                                                 .

db. db.   con   el   usuario   y   password   del   usuario   que   tenga   acceso   a   la   misma. }       Esta  es  una  conexión  a  la  base  de  datos  MySQL. db.ntiendas).     Conectar  e  interrogar  con  SQL     Vamos   a   empezar   tratando   la   primera   forma   de   trabajar   con   bases   de   datos. ntiendas FROM almacenes").  y  en  value(1)   estaría  el  campo  ntiendas.     bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL").     Lo  siguiente  será  enviarle  una  query.   donde   conectaremos  con  una  base  de  datos  MySQL.qPrintable(almacen).  suelen  guardar  toda  la  ingente  cantidad  de   datos   que   maneja   en   un   motor   de   base   de   datos.  hay  2   modelos   incluidos   en   las   clases   QSqlTableModel   y   QSqlRelationalTableModel.  y  la  interrogaremos  mediante  la  sintaxis  SQL.  Para  usuarios  que  prefieren  evitar  esto. QObject::tr("Database Error").   las   cuales   ya   mencionamos  en  el  tema  16. int ntiendas = query.   Qt   provee   de   la   clase   QSqlQuery.  es  necesario  que  se  añada  el  módulo  sql  al  proyecto.   donde   los   campos   tienen   índices   del   0   en   adelante.  y  desean  trabajar  a  un  nivel  más  alto.Tiendas: %d". db.text()).value(1). if (!db.pro  con  la  linea:   QT  +=  sql           .  en  el  host  local. query.   Qt   en   su   versión   Open   Source.next()) { QString almacen = query. qDebug("Nombre: %s .   no   incluye   aquellos   que   tienen   licencia.setDatabaseName("megafonia").setPassword("admin").setUserName("root").   de   acuerdo   a   las   queries   SQL   que  vamos  a  enviarle.open()) { QMessageBox::critical(0.  con  nombre  megafonia. } return true.   de   esta  forma  value(0)  contendría  el  campo  almacen  que  es  el  primero  en  la  respuesta.   Qt   en   su   versión   privativa   incluye   los   drivers   de   la   mayoría   de   motores   de   datos.  y  esto  se  hace  así:   QSqlQuery query.value(0).     Para  que  este  código  compile  bien. return false.   es  decir  al  fichero  *.toInt().exec("SELECT almacen.setHostName("localhost"). }     Como  puedes  ver  la  función  exec()  ejecuta  el  query. db.   y   value   es   una   lista   QVariant.lastError().   Para   aquellos   usuarios   acostumbrados   a   usar   la   sintaxis   SQL. while (query.89     TEMA  22   BASES  DE  DATOS       La  gran  mayoría  de  aplicaciones  de  gestión.  next()  recorre  el  puntero  por  la  tabla   respuesta.toString().

Qt::Horizontal.   y  el  modelo  puede  ser  QSqlTableModel.  son:   setTable(QString  tableName)  -­‐>  que  fija  la  tabla  de  la  base  de  datos  a  fijar   setEditStrategy(strategy)  -­‐>  Describe  la  estrategia  a  usar  para  actualizar  los  datos  de  la  base  de   datos.     Para   los   siguientes   ejemplos   vamos   a   usar   una   base   de   datos   grabada   en   SQLite   en   un   fichero  que  llamaremos  database. view2->move(view1->x() + view1->width() + 20. QSqlTableModel *model) { QTableView *view = new QTableView. model->setHeaderData(1.". "ID"). db. &model).db. QTableView *view2 = createView("Table Model (Vista 2)".db").   setHeaderData(int   section. "Imposible establecer una conexi_n con la base de datos.   #include <QtGui> #include <QtSql> static bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"). return app.  sin  más. QTableView *view1 = createView("Table Model (Vista 1)". &model).   QVariant   header)   -­‐>   Marca   los   nombres   de   las   cabeceras.\n" "Pulsar Cancel para salir. "First name").open()) { QMessageBox::critical(0. model->setHeaderData(0. return view. if (!createConnection()) return 1. model->select().  OnManualSubmit  (se  guarda  en   una  cache  hasta  que  el  programa  ejecuta  un  submitAll()  o  un  revertAll()  ).  en  los  ejercicios  veremos  como  es  esta  base  de  datos. return false. argv). }   . view1->y()). QSqlTableModel model.  Aquí  la  vista  más  típica  para  ver  una  tabla  de  datos  es  QTableView.  si  vamos  a  ver  una  tabla  en  concreto. } QTableView *createView(const QString &title.   clear()   -­‐>   borra   el   modelo   de   todos   los   datos   que   previamente   tuviera   cargados   en   su   caché   (buffer).setDatabaseName("database. QMessageBox::Cancel). "Last name").  que  puede  ser:  OnFieldChange  (todo  cambio  en  la  vista  es  inmediato  en  la  base  de  datos). } void initializeModel(QSqlTableModel *model) { model->setTable("person"). model->setEditStrategy(QSqlTableModel::OnManualSubmit).   select()  -­‐>  Rellena  el  modelo  con  los  datos  de  la  tabla.   podemos   combinar   una   vista   y   un   modelo   que   acceda   a   la   base  de  datos  como  su  fuente.     Las  funciones  más  típicas  de  este  modelo. model->setHeaderData(2. view1->show(). if (!db.90     MVC  aplicado  a  Bases  de  Datos     Como   ya   vimos   en   el   tema   16. Qt::Horizontal. char *argv[]) { QApplication app(argc. } int main(int argc. view->setWindowTitle(title).   Qt::orientation. initializeModel(&model).   OnRowChange(se  guardan  los  datos  al  cambiar  de  fila  o  registro). view->setModel(model). Qt::Horizontal. view2->show().exec(). "No se puede abrir la base de datos". } return true.

Qt::Horizontal. } void createRelationalTables() { QSqlQuery query. "City").exec("insert into employee values(1. 47)").exec("insert into country values(47.   #include <QtGui> #include <QtSql> static bool createConnection() { QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE"). country int)"). "name")). model->setHeaderData(2. QSqlRelation("country". Qt::Horizontal. QMessageBox::Cancel). char *argv[]) { QApplication app(argc. query. "id". name varchar(20))"). query. query. QSqlTableModel *model) { QTableView *view = new QTableView. } return true.exec("insert into employee values(2. 'Germany')"). return false. view->setItemDelegate(new QSqlRelationalDelegate(view)). if (!createConnection()) return 1. if (!db. "No se puede abrir la base de datos". 'San Jose')").exec("insert into city values(5000.open()) { QMessageBox::critical(0.exec("create table city(id int. 'Oslo')"). view->show(). 80000. query. query. 'USA')"). initializeModel(&model).exec("insert into country values(49. } int main(int argc.   Para   ello   usaremos   el   modelo   QSqlRelationalTableModel.\n" "Pulsar Cancel para salir. model->setHeaderData(1.".exec("create table country(id int. 100. }           . model->setRelation(2. "Name"). query. return view. QSqlRelation("city". } QTableView *createView(const QString &title. query.  y  lo  que  queremos  es  presentar  una  tabla   donde   aparece   la   relación   entre   ellas.  relacionadas  entre  ellas  mediante  un   identificador  numérico  (en  SQLite  mediante  el  rowid).exec(). query. 'Espen'. query. return app. &model).exec("insert into city values(80000.91       Pero  lo  más  normal  es  que  tengamos  varias  tablas. "Imposible establecer una conexi_n con la base de datos. 1)"). model->setHeaderData(0. model->setEditStrategy(QSqlTableModel::OnManualSubmit). QTableView *view = createView("Relational Table Model". argv). } void initializeModel(QSqlRelationalTableModel *model) { model->setTable("employee"). view->setModel(model). Qt::Horizontal. "Country"). model->select(). 'Harald'. "id". city int. Qt::Horizontal. view->setWindowTitle(title). model->setHeaderData(3. "ID"). query. model->setRelation(3.exec("insert into employee values(3. "name")).exec("insert into city values(100.   Veamos  un  ejemplo  similar  con  la  misma  base  de  datos  y  las  3  tablas. 'Norway')"). query. name varchar(20))"). db.setDatabaseName("database. createRelationalTables(). 'Sam'. name varchar(20). 5000. query. 49)"). 'Munich')").exec("insert into country values(1. QSqlRelationalTableModel model.exec("create table employee(id int primary key.db").

92                                                                 .

QLineEdit *portLineEdit.  frases  de  la  fortuna. QLineEdit *hostLineEdit.   Así   que   comenzaremos  con  la  implementación  de  un  cliente  TCP. private: QLabel *hostLabel.   es   aquella   que   espera   y   no   hace  nada  hasta  que  el  servidor  ha  respondido  y  ha  sido  establecida  por  completo.     Vamos  a  derivar  la  clase  Cliente  de  QDialog.  El  flujo  de  datos  es  muy  sencillo. QTcpSocket *tcpSocket.     . QDialogButtonBox *buttonBox.   y   seguiremos   a   lo   nuestro.  Suele  usarse   en   aplicaciones   multi-­‐hilo   o   de   consola   donde   se   hará   uso   de   funciones   como   QTcpSocket::waitForConnected.   Así   que   cuando   el   cliente   se   conecta.  tan  solo  lleva  como  cabecera  un  quint16   con  la  longitud  del  mensaje  que  le  sigue. QPushButton *quitButton.   el   servidor   le   sirve   una   frase   al   azar  sobre  su  fortuna.     Se  trata  de  un  servidor  que  una  sirve  por  un  puerto  TCP.   y   mediante   una   conexión  de  Qt.   donde   llamaremos   a   la   función   connectToHost().   Aparte  de  los  componentes  visuales.   y   también   para   recogerlas  en  el  cliente.  vamos  a  usar  la  cadena  currentFortune  para  guardar  la  frase   actual.         Vamos   a   usar   QDataStream   para   enviar   los   datos   desde   el   servidor. QPushButton *getFortuneButton.93     TEMA  23   REDES  TCP/IP     Cliente  TCP  (QTcpSocket)     Explicar   los   protocolos   TCP/IP   no   es   el   objetivo   de   este   libro. QLabel *statusLabel.   En   nuestro   caso. quint16 blockSize. }.  Los  slots  que  vamos  a  usar  son:   requestNewFortune  -­‐>  que  va  a  ser  ejecutado  cuando  se  haga  clic  en  el  botón  de  “Ver  mi  fortuna”.  luego  viene  una  cadena  con  la  frase. QString currentFortune.     Bien. void displayError(QAbstractSocket::SocketError socketError).  una  vez  el  socket  se  establezca  QTcpSocket  emitirá  la  señal  connected().   por   lo   que   se   da   por   supuesto   el   previo   conocimiento   y   diferenciación   entre   protocolos   TCP   y   UDP. void readFortune().   class Client : public QDialog { Q_OBJECT public: Client(QWidget *parent = 0).   Una   conexión   bloqueante.   en   programación   de   sockets   hay   2   aproximaciones   distintas   en   cuanto   a   si   la   conexión   es   bloqueante   o   no-­‐bloqueante. void enableGetFortuneButton().  y  su  tamaño  en  blockSize. QLabel *portLabel.   vamos   a   usar   la   aproximación   no-­‐bloqueante. private slots: void requestNewFortune().  como  las  de   los   rollitos   de   primavera.  y  le  vamos  a  añadir  los  slots  necesarios  y  las   propiedades  privadas  necesarias.

                   connect(hostLineEdit.  2).   también   le   añadimos   un   validador   al   LineEdit   del   puerto   para   que   no   admita   puertos   fuera   del   rango   de   1-­‐65535.          buttonBox-­‐>addButton(getFortuneButton.          //  va  a  usar  la  primera  IP  no  localhost  (127.          hostLabel-­‐>setBuddy(hostLineEdit).          mainLayout-­‐>addWidget(statusLabel.  65535.  SLOT(enableGetFortuneButton())).94     readFortune  -­‐>  que  será  disparado  cuando  el  socket  TCP  mediante  el  QDataStream.                          break.          mainLayout-­‐>addWidget(hostLabel.   displayError   -­‐>   va   a   estar   conectado   con   la   señal   error()   que   se   dispara   si   hay   error   en   la   conexión.          buttonBox  =  new  QDialogButtonBox.  2.   diciendo   que   nuevos   datos   están   disponibles   en   el   dispositivo   de   entrada/salida.  SLOT(close())).   Se   inicializa   el   socket   y   se   hacen   las   conexiones   Qt   entre   slots   y   señales  comentadas  antes.          connect(tcpSocket.          connect(quitButton.at(i)  !=  QHostAddress::LocalHost  &&                          ipAddressesList.toString().  1.this.          QList<QHostAddress>  ipAddressesList  =  QNetworkInterface::allAddresses().  QDialogButtonBox::RejectRole).  0.1)          if  (ipAddress.  SIGNAL(clicked()).  0).this.          //  Busca  todas  las  direcciones  del  host  actual          QString  ipAddress.toString().          connect(tcpSocket.          connect(portLineEdit.this.  2).0.  0.  3.  SIGNAL(textChanged(QString)).          buttonBox-­‐>addButton(quitButton.          portLabel  =  new  QLabel("&Puerto:").  envie  la  señal   QIODevice::readyRead().   enableGetFortuneButton  -­‐>  este  es  un  mero  slot  de  GUI  para  que  se  habilite  un  botón.  SIGNAL(readyRead()).          mainLayout-­‐>addWidget(hostLineEdit.  0).  SLOT(requestNewFortune())).     .size().  ++i)  {                  if  (ipAddressesList.          hostLineEdit  =  new  QLineEdit(ipAddress).  SIGNAL(error(QAbstractSocket::SocketError)).   inicializamos   la   IP   del   ordenador   donde   estamos   en   la   LineEdit.0.                  }          }          //  si  no  encuentra  ninguna  usará  la  localhost  (127.0.  1).          portLabel-­‐>setBuddy(portLineEdit).  1.0.     Vamos  ahora  a  por  la  implementación  del  constructor  que  es  donde  se  inicia  el  socket:   Client::Client(QWidget  *parent)          :  QDialog(parent)   {          hostLabel  =  new  QLabel("&IP  Servidor:").          setWindowTitle("Cliente  TCP")).          statusLabel  =  new  QLabel(("Este  ejemplo  requiere  que  usted  ejecute  el  Fortune  server")).at(i).at(i).          mainLayout-­‐>addWidget(buttonBox.  0.  SIGNAL(clicked()).  1).  SLOT(readFortune())).this.   } Aquí   a   parte   de   los   widgets   visuales.  1.          getFortuneButton-­‐>setDefault(true).            QGridLayout  *mainLayout  =  new  QGridLayout.          mainLayout-­‐>addWidget(portLineEdit.1)  tipo  IPv4          for  (int  i  =  0.isEmpty())                  ipAddress  =  QHostAddress(QHostAddress::LocalHost).          mainLayout-­‐>addWidget(portLabel.          getFortuneButton-­‐>setEnabled(false).          portLineEdit-­‐>setValidator(new  QIntValidator(1.  QDialogButtonBox::ActionRole).          setLayout(mainLayout).  this.  i  <  ipAddressesList.  0.toIPv4Address())  {                          ipAddress  =  ipAddressesList.          tcpSocket  =  new  QTcpSocket(this).  this)).          portLineEdit-­‐>setFocus().  1.  SIGNAL(textChanged(QString)).          connect(getFortuneButton.          quitButton  =  new  QPushButton("Salir").  SLOT(enableGetFortuneButton())).  this.  SLOT(displayError(QAbstractSocket::SocketError))).          getFortuneButton  =  new  QPushButton("Ver  mi  Fortuna").          portLineEdit  =  new  QLineEdit.

  y   si   los   bytes   que   están   preparados   para   ser   leídos   son   menos   que   2   bytes.  Finalmente  leemos  el  mensaje  que  recogemos  en  una  cadena   QString. case QAbstractSocket::HostNotFoundError: QMessageBox::information(this. if (blockSize == 0) { if (tcpSocket->bytesAvailable() < (int)sizeof(quint16)) return. "Cliente TCP". getFortuneButton->setEnabled(true)."). break. break. if (nextFortune == currentFortune) { QTimer::singleShot(0.  entonces  salimos  del  slot  y  esperamos  a  que  nos  llamen  con  más  datos.portLineEdit->text().   reseteando  el  socket.   el   tamaño   del   header   que   indica   el   tamaño  de  la  frase."Conexión rechazada por el cliente.  Si  el  mensaje  es  el  mismo  que  el  anterior  que  nos  dio  el  servidor. } currentFortune = nextFortune.arg(tcpSocket->errorString())).  y  por  tanto  requiera  de  varias  ejecuciones  de   este   slot. tcpSocket->abort().  Si  hay   más   datos. tcpSocket->connectToHost(hostLineEdit->text(). }   .   pues   leemos   primero   el   blockSize. return.  luego  aborta  la  conexión  actual  si  la  hay."No se encontró el servidor.   si   es   la   primera   lectura   (blockSize   ==   0)."). SLOT(requestNewFortune())). } Inicializamos  la  propiedad  blockSize  con  el  tamaño  a  0. case QAbstractSocket::ConnectionRefusedError: QMessageBox::information(this. in.95       Cuando   se   pulsa   el   botón   de   Ver   Mi   Fortuna.   para   que   dispare   de   nuevo   el   slot   requestNewFortune   que   ya   describimos  antes.setVersion(QDataStream::Qt_4_6).   se   dispara   el   slot   correspondiente   que   establece  la  conexión  con  el  servidor  de  esta  manera:   void Client::requestNewFortune() { getFortuneButton->setEnabled(false). blockSize = 0. default: QMessageBox::information(this. in >> nextFortune.  entonces  se  lanzará  el  slot  correspondiente:   void Client::readFortune() { QDataStream in(tcpSocket). statusLabel->setText(currentFortune).toInt()).") .   y   haya   datos   nuevos   en   el   dispositivo. Servidor apagado.  pidiendo  un  mensaje  nuevo.       Finalmente  vamos  a  ver  el  código  que  va  a  tratar  los  diferentes  errores  de  conexión:   void Client::displayError(QAbstractSocket::SocketError socketError) { switch (socketError) { case QAbstractSocket::RemoteHostClosedError: break. "Cliente TCP".   Luego   igualmente   salimos   del   slot. QString("Error: %1.   mientras   no   estén  todos  los  datos  disponibles.   para   tenerlos   todos.  no  tienen  por  qué  venir  todos  de  golpe.   Por   ello."Cliente TCP". } if (tcpSocket->bytesAvailable() < blockSize) return.     Cuando   el   socket   ya   haya   sido   conectado   correctamente. } Los  datos.  entonces  usamos  un   Timer   de   único   disparo.  luego  conecta  con  el  host  dando  su  dirección  IP  y  puerto. this. } getFortuneButton->setEnabled(true). in >> blockSize. QString nextFortune.

return. QString("Imposible iniciar servidor: %1.     La  implementación  del  constructor  es  muy  simple:   Server::Server(QWidget *parent) : QDialog(parent) { statusLabel = new QLabel. setLayout(mainLayout).   es   también   derivado   de   QDialog. QList<QHostAddress> ipAddressesList = QNetworkInterface::allAddresses(). quitButton->setAutoDefault(false). ++i) { if (ipAddressesList. QVBoxLayout *mainLayout = new QVBoxLayout. buttonLayout->addStretch(1).at(i). SLOT(sendFortune())). break. QStringList fortunes. SIGNAL(newConnection()).") << tr("Tienes nuevo correo.toIPv4Address()) { ipAddress = ipAddressesList.isEmpty()) ipAddress = QHostAddress(QHostAddress::LocalHost). statusLabel->setText(tr("Servidor corriendo en \n\nIP: %1\npuerto: %2\n\n" "Ejecute el cliente TCP ahora. }. } } if (ipAddress. this."). }     . for (int i = 0.size().   que   está   conectado  a  la  señal  newConnection(). close().toString(). if (!tcpServer->listen()) { QMessageBox::critical(this. this.   class Server : public QDialog { Q_OBJECT public: Server(QWidget *parent = 0). buttonLayout->addStretch(1). i < ipAddressesList. QPushButton *quitButton.at(i). fortunes << tr("Has llevado una vida de perros.") . QTcpServer *tcpServer. private: QLabel *statusLabel.at(i) != QHostAddress::LocalHost && ipAddressesList.") << tr("Tendr_s hambre dentro de una hora. setWindowTitle("Servidor TCP"). quitButton = new QPushButton("Salir"). mainLayout->addWidget(statusLabel).   Su   único   slot   es   sendFortune. SIGNAL(clicked()). buttonLayout->addWidget(quitButton).") . "Servidor TCP".toString(). connect(quitButton. QHBoxLayout *buttonLayout = new QHBoxLayout.96     No  hay  comentarios  que  añadir  a  esto. connect(tcpServer.     Servidor  TCP  (QTcpSocket)     El   servidor.") << tr("Tienes que pensar en el ma_ana. mainLayout->addLayout(buttonLayout).") << tr("Te ha sorprendido una sonido fuerte.arg(ipAddress). SLOT(close())). private slots: void sendFortune().arg(tcpServer->serverPort())). } QString ipAddress.arg(tcpServer->errorString())). tcpServer = new QTcpServer(this). Bajate del sofa.

QTcpSocket *clientConnection = tcpServer->nextPendingConnection(). clientConnection. buttonBox = new QDialogButtonBox. out << fortunes. SLOT(close())). connect(startButton. this.  le  ponemos  el  tamaño  de  sólo  la   frase  (por  eso  restamos  al  tamaño  del  bloque.  luego  mediante  una  función  random. buttonBox->addButton(startButton.  y  se  envía  al  buffer. buttonBox->addButton(quitButton. timer->start(1000). QIODevice::WriteOnly). out.   Conectamos   la   señal   de   disconnected   al   slot   QObject::deleteLater().  Veamos  la  implementación  completa:   Sender::Sender(QWidget *parent) : QDialog(parent) { statusLabel = new QLabel("Preparado para enviar datagramas por el puerto 45454").device()->seek(0). udpSocket = new QUdpSocket(this). QDialogButtonBox::ActionRole).  el  objeto  que  representa  dicha  conexión  sea  borrado  por  Qt. }   . SIGNAL(clicked()).  solamente  inicia  la  escucha  de   clientes  con  listen()  y  conecta  la  señal  newConnection()  con  el  slot  que  va  a  enviar  las  frases:   void Server::sendFortune() { QByteArray block. QDialogButtonBox::RejectRole).at(qrand() % fortunes. mainLayout->addWidget(statusLabel). clientConnection->disconnectFromHost(). quitButton = new QPushButton("Salir"). messageNo = 1. out << (quint16)(block. } Una  vez  iniciado  el  DataStream  como  un  ByteArray.  ya   podemos  desconectar  al  cliente.sizeof(quint16)). out << (quint16)0. setWindowTitle("Emisor UDP"). clientConnection->write(block). SIGNAL(timeout()). timer = new QTimer(this).97     Aparte  de  inicializar  la  GUI  y  de  buscar  la  IP  local  como  el  cliente. } void Sender::startBroadcasting() { startButton->setEnabled(false). SLOT(broadcastDatagram())).device()->seek(0)” . SLOT(deleteLater())). mainLayout->addWidget(buttonBox).     Ya  podemos  enviar  el  bloque  completo.  se  elije  una  frase  al  azar. QVBoxLayout *mainLayout = new QVBoxLayout.   luego  rebobinamos  a  la  posición  0  “out.     Emisor  UDP  (QUdpSocket)     El  emisor  de  datagramas  UDP  va  a  usar  un  QTimer  para  cada  segundo  enviar  paquetes   por  un  puerto.  primero  ponemos  en  el  buffer  del  mismo  la   cabecera  a  0.size() . this. out.  y  una  vez  hecho. connect(clientConnection.   se   devuelve   el   socket   que   ha   sido   conectado.setVersion(QDataStream::Qt_4_6).  por  dicho  socket  conectado. startButton = new QPushButton("Enviar").  y  se  borrará  el  objeto  del  socket  como  acabamos  de  ver.size()). QDataStream out(&block. connect(timer. connect(quitButton.   que   hace   que   si   hay   desconexión  del  socket. SLOT(startBroadcasting())). setLayout(mainLayout).       Llamando   a   nextPendingConnection(). SIGNAL(clicked()). this. SIGNAL(disconnected()).  el  tamaño  de  la  cabecera  –  2  bytes).

size()). setLayout(mainLayout). 45454). udpSocket = new QUdpSocket(this). buttonLayout->addWidget(quitButton). QVBoxLayout *mainLayout = new QVBoxLayout. this. SLOT(close())). buttonLayout->addStretch(1).   timeout()   con   broadcastDatagram.   Muy   sencillo   como  vemos.  meramente  vincula  el  puerto  y  la  dirección  IP  del  interfaz  de  red  por  donde   va  a  escuchar  con  bind(). quitButton = new QPushButton("Salir"). connect(udpSocket. } }   Con  la  ayuda  del  tema  siguiente. udpSocket->writeDatagram(datagram. setWindowTitle("Receptor UDP"). udpSocket->readDatagram(datagram. SLOT(processPendingDatagrams())). connect(quitButton.98     void Sender::broadcastDatagram() { statusLabel->setText(QString("Enviando datagrama %1").   tamaño_datos. statusLabel->setText(QString("Datagrama recibido:\"%1\""). datagram.   puerto). SIGNAL(clicked()).this.  que  trata  de  la  programación  concurrente  o  multi-­‐hilo.     Receptor  UDP  (QUdpSocket)     El  receptor.       .resize(udpSocket->pendingDatagramSize()).size(). mainLayout->addLayout(buttonLayout).   ésta   se   encarga   de   escribir   el   datagrama   a   enviar   con   writeDatagram(datos. } void Receiver::processPendingDatagrams() { while (udpSocket->hasPendingDatagrams()) { QByteArray datagram. QByteArray datagram = "Mensaje enviado " + QByteArray::number(messageNo).arg(messageNo)).   vamos  a  implementar  el  ejemplo  del  servidor  TCP. QHBoxLayout *buttonLayout = new QHBoxLayout. buttonLayout->addStretch(1).  pero  esta  vez  con  múltiples  hilos  para  poder   manejar  a  muchos  clientes  a  la  vez. mainLayout->addWidget(statusLabel).  con  nuestro  slot  de  lectura  processPendingDatagrams.data())). QHostAddress::Broadcast. datagram.   como   tenemos   conectada   la   señal   del   timer. datagram.   IP_receptor.  Aquí  de  nuevo  conectamos  QIODevice::readyRead().   tamaño)   leemos   los   datos   que   se   pondrán   en   QByteArray::data().   Con   readDatagram(datos. ++messageNo.data().   La   funcion   pendingDatagramSize()   nos   devuelve   los   bytes   que   quedan   pendientes   de   lectura.  que  nos  indica  que   hay  nuevos  datos  esperando  ser  leídos. SIGNAL(readyRead()). }   Iniciamos   el   timer   con   start(1000)   una   vez   apretamos   el   botón   de   Enviar.arg(datagram.     Veamos  la  implementación:   Receiver::Receiver(QWidget *parent) : QDialog(parent) { statusLabel = new QLabel("Escuchando mensajes enviados"). QUdpSocket::ShareAddress). udpSocket->bind(45454.     Esto   lo   hace   mediante   un   bucle   usando   hasPendingDatagrams()   para   saber   si   aún   queda   algo   por   procesar.data().

// conecta las señales de QTcpSocket .  cuando  el  padre  finalice.99     TEMA  24   PROGRAMACIÓN  CONCURRENTE       Una  aplicación  normalmente  usa  un  solo  hilo  de  ejecución.   vamos   a   ver   como   quedaría   el   código   del   servidor   TCP   del   tema   anterior. }     Esto   sería   un   hilo   que   conecta   un   socket   con   un   host   y   luego   inicia   su   propio   bucle   de   eventos.  como  hace  main().  donde  lleva  a  cabo  una  sola   tarea   a   la   vez.   Recuerda   instanciar   cada   hilo..  y  que  estos  se  comuniquen  con  la  tarea  principal.   Pero   existe   la   posibilidad   de   que   la   tarea   principal.     Para   ver   un   uso   real   de   esta   concurrencia   de   hilos.   comienza   (started())   o   finaliza   (finished()).   lo   que   sí   vamos   es   a   explicar   de   qué   manera   Qt   implementa   las   aplicaciones   más   típicas  multi-­‐hilo.  Para  comenzar  la  ejecución  de  un  hilo.       QThread   notifica   con   señales   cuando   un   hilo.  teniendo  que  sincronizar  su  trabajo.  todos  los  hilos  creados  de  él  se  borrarán   también.   Un   hilo   comparte   datos   con   otros   hilos.   Sí   se   pueden   usar   QTimers   y   sockets.     class MyThread : public QThread { public: void run(). void MyThread::run() { QTcpSocket socket..  sólo  hay  que  derivar  una  clase  de  QThread  y  reimplementar   la  función  run(). portNumber).  Cada  objeto  de  esta  clase   representa  un  hilo  de  ejecución  diferente  a  los  demás  y  diferente  al  principal  que  se  inicia  y  se   termina   con   main().  La  ejecución  de  un  hilo  acaba  cuando  se  sale  de  run().  o  que  trabajen  juntos   en  una  misma  cosa.   y   se   pueden   conectar  señales  y  slots  entre  diferentes  hilos.  una  vez  se  ha  instanciado  en  el  hilo  principal.   genere   nuevos   hilos   de   ejecución  independientes.  cada  una  en  un  hilo  independiente. socket.  ya  que  eso  pertenece  a  un  bagaje  que  se  supone  ya   adquirido.  Así  que  run()  ejecutaría  un  hilo  e  iniciaría   su  propio  bucle  de  eventos  con  exec().   los   hilos   QThreads  comienzan  y  terminan  en  su  función  run().   poniendo   como   argumento   padre   al   hilo   del  que  nace.       .     Para  crear  tus  propios  hilos.connectToHost(hostName. }.   En   vez   de   comenzar   en   main()   como   el   hilo   principal.  de  esa  manera.     No   se   pueden   usar   widgets   en   un   hilo.  es   usando   la   función   start().  explicar  el  trasfondo   de  la  programación  multi-­‐hilo  en  este  libro. exec().   También  se  pueden  usar  los  getters  isFinished()  o  isRunning()  para  saber  si  ha  finalizado  o  aún   esta  corriendo.  con  las  tareas  que  este  hilo  vaya  a  ejecutar.   pero   se   ejecuta   de   manera   independiente   de   los   demás.  evitando  memory  leaks.       QThread     Esta  es  la  clase  que  provee  de  hilos  de  manera  multiplataforma.   de   manera   que   pueda   admitir   múltiples   conexiones   de   clientes.  igual  que   un  programa  cuando  termina  main().  No  es  nuestra  idea.

}   Y  aquí  está  la  parte  principal  del  servidor.size()).  que  identifica  a  cual  de  los  clientes  nos  vamos  a  conectar.   cuando   un   cliente   conectara   su   socket   con   él. const QString &fortune. thread->start(). return. } void FortuneServer::incomingConnection(int socketDescriptor) { QString fortune = fortunes. out << text.   se   desconecta   y   espera   la   desconexión   (modo   bloqueo). out.   desde   que   es   despertado   para   conectarse   con   el   cliente.device()->seek(0). socketDescriptor(socketDescriptor). tcpSocket.   que   pasaría   el   descriptor   del   socket   TCP   para   que   no   se   confunda   de   cliente. tcpSocket.  esto  es  lo  que   hace  el  hilo  que  despacha  a  un  cliente.   y   finalmente   iniciamos   dicho   hilo  con  start().   crearía   un   servidor   FortuneServer   al   que   ponemos   a   escuchar   con   listen(). QIODevice::WriteOnly).   También   conectamos   la   señal   finished()   de   dicho   hilo   con   su   borrado.  Bien.error()). Bajate del sofa. SLOT(deleteLater())).") << tr("Te ha sorprendido una sonido fuerte.  ha  de  separarse  el  código  que  genera  toda  la  interfaz  gráfica  y   que  inicia  la  escucha  con  listen(). fortune.   Creamos   un   QTcpSocket   y   lo   asociamos   al   descriptor   de   socket   que   nos   va   a   pasar   el   servidor   (socketDescriptor).setVersion(QDataStream::Qt_4_0).   como   hemos   visto   arriba. out << (quint16)(block.   para   que   cuando   acabe   su   ejecución   sea   borrado.   y   la   frase   a   enviarle.write(block). } Ahora.  y  esta  vez  se  deriva  de  QTcpServer.waitForDisconnected(). FortuneThread *thread = new FortuneThread(socketDescriptor. this).100     Como   los   widgets. out. SIGNAL(finished()).             . tcpSocket. thread.   automáticamente   se   enviaría   la   ejecución   a   incommingConnection.   que   pasa   como   parámetro   el   entero   que   representa   el   descriptor  del  socket  conectado.") << tr("Tienes que pensar en el ma_ana.  así  el  código  que   pondríamos   en   el   hilo   principal.at(qrand() % fortunes. text(fortune) { } void FortuneThread::run() { QTcpSocket tcpSocket."). } QByteArray block.size() .sizeof(quint16)).disconnectFromHost().  Y  en  esta  función  creamos  un  hilo  de  ejecución  para  despachar   al   cliente. if (!tcpSocket.setSocketDescriptor(socketDescriptor)) { emit error(tcpSocket. connect(thread.   hemos   dicho   que   no   pueden   correr   en   un   hilo. QObject *parent) : QThread(parent).   FortuneServer::FortuneServer(QObject *parent) : QTcpServer(parent) { fortunes << tr("Has llevado una vida de perros. out << (quint16)0. QDataStream out(&block.   hemos   creado   una   clase   hilo   del   servidor   que   envía   las   frases   y   en   run()   es   donde   se   encarga   de   hacer   todo   lo   que   hace.   han   de   correr   en   la   tarea  principal  que  inicia  main().") << tr("Tendr_s hambre dentro de una hora.") << tr("Tienes nuevo correo.   hasta   que   envía   la   frase.     FortuneThread::FortuneThread(int socketDescriptor.

      Vamos  a  poner  2  supuestos  resueltos  de  manera  diferente.   se   queda   en   espera.  El  hilo  productor.     En   Qt   esto   es   hecho   con   la   clase   QMutex.   y   donde   no   hay   interacción   entre   los   hilos.   hay   casos   donde   varios   hilos   pueden   compartir   datos   o   código   de   manera   que   pueda   esto   afectar   al   resultado.  mientras   el  otro.   cuando   es   despertado.lock().   genera   hilos   que   hacen   cosas   diferentes. mutex.  Supongamos  que  el  código  de   varios  hilos  accede  a  la  vez  al  código  que  aumenta  un  contador  común.  La  única  forma  de  resolver  esto. }   Para  más  comodidad  se  creo  QMutexLocker(QMutex  *)  que  el  objeto  que  crea. }     El   segundo   supuesto.   De   esta   manera   el   mismo   código   quedaría   así   de   simple:   void Thread::run() { QMutexLocker locker(&mutex).  al  nacer  bloquea  el   mutex.   provee   una   variable   condición   para   sincronizar   hilos.   Veamos   antes   qué   es   QWaitCondition.  y  llamando  a  la  función  unlock()  el   desbloqueo  de  la  zona.   desbloquea   el   mutex.   a   donde   sólo   un   hilo   puede   entrar   a   la   vez.  lo  hace  usando  wakeAll()  para  despertarlos  a  todos  o  wakeOne()  para  despertar  sólo  a   uno  de  ellos.  luego  sale  de  la  zona. counter++.   se   trata   del   típico   problema   del   productor-­‐consumidor.  el  orden  en  que  esto  ocurre  es  aleatorio  y  no  se  puede  controlar.  cuando  debieran  contarse  el  paso  de  los  2   hilos  de  manera  independiente.   donde   un   escribe   y   el   otro   lee   de   la   siguiente   manera.   de   esta   manera   el   hilo   que   entra.   el   principal).   Wait   lo   que   hace   es   que   un   mutex   previamente   bloqueado.  y  llamando  a  la  función  lock()  se  produce  el  bloqueo.   hasta   que   otro   hilo   lo   despierte.   que   se   declararía   como   privada   dentro   del   QThread.       .  se  evitaría  el  problema  inicial.       Para   resolverlo   vamos   a   usar   2   QWaitCondition   y   1   QMutex.   y   al   borrarse.   El   hilo   consumidor.   Los   hilos   se   quedan   en   espera   cuando   llegan   a   la   función   wait(&mutex).   es   desbloqueado   y   el   hilo.   permitiendo  que  otro  hilo  entre.  y  así.       Si   usáramos   solamente   un   mutex   para   bloquear   el   acceso   simultáneo   al   buffer.   donde   un   programa   principal.  es  decir  una   zona   bloqueada.       QWaitCondition.  si  cada  uno  trabajara   en  zonas  diferentes  en  todo  momento.  cuando  realmente  no  habría  problema  alguno.   bajaría   la   productividad  del  mismo.  Podría  ocurrir  que  un  hilo   acceda  al  valor  antes  de  incrementarlo. counter++.  es  usar  un  Mutex.  y  una  vez  allí  vuelve   de   nuevo   al   principio   para   seguir   escribiendo.   un   hilo   externo   (por   ejemplo.   lee   lo   que   el   otro   escribe   conforme  es  producido  y  lo  saca  por  la  salida  estándar  de  error  (cerr).  ya  que  no  permitiríamos  acceder  al  buffer  a  uno  de  los  hilos.   bloquea  esa  zona.101     Sincronización  de  hilos     Ya   hemos   visto   el   ejemplo   más   sencillo   de   hilos.   void Thread::run() { mutex.unlock().  de  manera   que  ambos  dejarían  el  mismo  resultado  en  el  contador.  mientras  otro  hilo  ya  lo  esta  incrementando.  está  trabajando  en  él.   Para   despertar   a   los   hilos   que   esperan.   donde   2   hilos   comparten   un   mismo   buffer   de   memoria.  sólo  él  recoge  el  valor  del  contador  ahora.   el   mutex   vuelve   a   bloquearse   y   el   hilo   sigue   la   ejecución   después   de   wait().  escribe  en  dicho  buffer  hasta  que  llega  a  su  final.   Pero.  y  lo  aumenta.

unlock().lock().   escribe   en   la   posición   siguiente   y   de   nuevo   antes   de   incrementar   numUsedBytes   (el   contador   común   del   buffer).0). }.  numUsedBytes.   libera   el   mutex.   entonces   se   queda   en   espera   de   la   condición   bufferNotFull   y   libera   el   mutex.  y  despierta  al  hilo  consumidor.  va  a  ser  el  número  de  bytes  en  el  buffer   que   contiene   datos   escritos   por   el   productor. } } Inicializa  la  semilla  random  con  el  timer.     Por  lo  tanto  el  código  que  gobierna  el  hilo  productor  es:   class Producer : public QThread { public: void run(). bufferNotEmpty. mutex.           .  por  lo  que  el  consumidor  tendrá  que  dar  varias  vueltas.   será   señalada   por   el   productor   cuando   haya   escrito   algún   dato.secsTo(QTime::currentTime())). const int BufferSize = 8192.0. for (int i = 0.  se  encargará  de  bloquear  el  acceso  a  la  variable  común  numUsedBytes.  lo  que   hace  con  un  código  como  éste:     buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4]. QMutex mutex.  Antes  de  acceder  a  numUsedBytes  bloquea  con  el  mutex   su   acceso. mutex.  incrementa  su  valor.wakeAll().   Si   no   ha   llenado   el   buffer.  El  tamaño  del   buffer  es  inferior  a  esa  cantidad. void Producer::run() { qsrand(QTime(0. QWaitCondition bufferNotFull.  o  el  consumidor  estará  sobrescribiendo  datos  aún  no  leídos  por  el  consumidor.wait(&mutex).   La   condición   de   espera   bufferNotFull.  luego  libera  el   mutex.   numUsedBytes   no   podrá   exceder   el   tamaño   del   buffer  nunca.  bloque  esta  sección. QWaitCondition bufferNotEmpty.   El   mutex. if (numUsedBytes == BufferSize) bufferNotFull.unlock().     Datasize  en  la  cantidad  de  bytes  que  va  a  escribir  el  consumidor  en  buffer.lock().   si   se   ha   llenado   el   buffer   completo   de   datos.   la   señalará   el   consumidor   cuando   haya   leído   algún   dato. i < DataSize.   despertando   al   consumidor   para   que   lo   lea. int numUsedBytes = 0. ++numUsedBytes.   despertando   al   consumidor  para  que  siga  escribiendo.  para  que  sea  leída   y   cambiada   por   un   solo   hilo   en   cada   momento.102       Vamos   entonces   ahora   a   resolver   el   problema   del   productor   y   consumidor. buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4]. ++i) { mutex.       La   condición   de   espera   bufferNotEmpty.  incluido  el   principal:   const int DataSize = 100000. char buffer[BufferSize].  y  sigue  el  bucle  a  su  ritmo. Lo  que  escribe  son  aleatoriamente  las  letras  ACGT  (bases  del  código  genético).   para   ello   vamos  a  definir  las  siguientes  variables  globales  que  van  a  compartir  todos  los  hilos. mutex.   y   que   no   han   sido   leídos   por   el   consumidor.  De  esta  manera  no   se  sale  del  buffer  y  lo  escribe  cíclicamente.

i < DataSize.   Y   sigue   en   su   bucle  también  a  su  ritmo.  liberaría  bytes  en  este  semáforo  una  vez  haya  escrito  en  el   buffer.   bloquea   la   sección   con   un   mutex. bytes.  y  cada  vez  que  se  llama  al  semáforo  con  la  función  acquire()  se  piden  recursos.   provee   de   un   contador   general   con   semáforo. ++i) { mutex. bytes.   con   los   2   casos   extremos   que   pararían   la   ejecución   de   alguno   de   los   hilos. }.   pero   en   la   siguiente   al   intentar   adquirir   3.  Lo  inicializaríamos  con  un  valor  igual  al  número  total  de  bytes  del  buffer. void Consumer::run() { for (int i = 0.   ya   no   puede.  Por   defecto  si  no  se  pone  un  número  en  acquire  o  en  release.       De   esta   manera   podemos   crear   2   semáforos   en   nuestro   caso.  el  hilo   consumidor  adquiriría  antes  uno.   se   crea   un   semáforo   bytes   con   el   contador   a   6.   y   sacándolos   por   la   salida   estándar   de   error.lock().     QSemaphore.  también  libera  el  mutex.  lee  el  siguiente  dato  del  buffer. buffer[i % BufferSize]). bufferNotFull. "\n").unlock().acquire(4).  por  lo   que  realmente  funciona  como  un  mutex  especial.   existe  una  segunda  forma  de  hacer  esto  de  manera  también  óptima.  y  si  no  lo  hay. "%c".  Si  no   es  así.   hasta   que   haya   bytes   disponibles  para  seguir.  Para  devolver  los  bytes  habría  que  llamar  a  la  función  release(int  n).   indicando   que   hay   un   dato   menos   por   leer.   el   hilo   sigue   la   ejecución.  Para  ello  el  semáforo  inicia  el  contador  con  un  número  que  define  los  recursos   disponibles. mutex.wakeAll().   o   lo   que   es   lo   mismo   instanciado   por     .   una   vez   lo   haya   leído.   Dicho   semáforo   se   inicializaría   con   el   valor   a   0.  Sin  embargo.   que   es   decrementado.   por   lo   que   la   ejecución   sigue.   entonces   se   bloquea   la   ejecución  del  hilo  en  ese  punto  hasta  que  hayan  recursos  suficientes  para  ser  adquiridos.   y   libera   el   mutex.  se  presupone  que  es  1.  ambos  hilos  se  autosincronizan.   cuando   el   hilo   llega   a   la   primera   adquisición   de   4   bytes.   si   no   hay   datos   que   leer   nuevos.  Veamos  un  ejemplo  sencillo:   QSemaphore bytes(6).unlock().   y   el   consumidor   podría   liberar   con   release(). } El   hilo   consumidor   también   tiene   su   bucle   para   leer   todos   los   datos   que   el   consumidor   va   dejando   en   el   buffer.  En  Qt  esto   se  hace  con  la  clase  QSemaphore.  usando  semáforos. if (numUsedBytes == 0) bufferNotEmpty.   pero   si   no   los   hay   para   adquirirlos.   sólo   puede   ser   bloqueado   una   vez.   Antes   de   revisar   el   valor   de   numUsedBytes. --numUsedBytes.lock(). mutex.   entonces   se   queda  esperando  que  se  cumpla  la  condición  de  espera  bufferNotEmpty  y  libera  el  mutex.   así   que   el   hilo   se  quedaría   ahí. fprintf(stderr.  y  el  productor.  Por  otro  lado  otro   semáforo  llamado  usedBytes  podría  indicar  el  número  de  bytes  disponibles  para  lectura.   hasta   que   es   desbloqueado.   no   hay   problema   ya   que   aún   quedan   2.103       El  código  del  hilo  consumidor  sería  complementario  a  éste:   class Consumer : public QThread { public: void run().wait(&mutex).   Mientras   un   mutex.   El   semáforo   freeBytes   por   ejemplo   podría   indicar  el  número  de  bytes  libres  para  ser  escritos.  Por  tanto  veamos  antes  como  funciona  esta  clase.  si  lo   hay.  sin  pisarse  el  uno  al  otro.   Aquí. } fprintf(stderr.  entonces  quedaría  bloqueado.acquire(3).   un   semáforo   puede   ser   bloqueado   múltiples  veces.   luego   despierta   al   hilo   productor   con   su   señal   bufferNotFull. mutex.  y  luego  bloquea  de  nuevo  con  el   mutex   el   cambio   de   numUsedBytes.   hasta  que  lo  hubiera.  para  poder  leer.     De  esta  manera.  y  que  el  productor  podría  adquirir  antes  de   ver   si   puede   seguir   escribiendo.

argv).acquire().   su   iniciación.  y  se  espera  a  que  estos   acaben.  se  iniciarían  ambos  hilos  con  start().   Así   verás   que  el  código  queda  mucho  más  sencillo  que  usando  el  mutex  y  las  2  condiciones  de  espera. const int BufferSize = 8192. }   Comprendido   el   mecanismo   que   hemos   explicado   de   los   semáforos. } fprintf(stderr. "\n"). QSemaphore usedBytes. ++i) { freeBytes. for (int i = 0. producer. Consumer consumer. }   Ambos  wait()  se  ejecutan  uno  detrás  del  otro. buffer[i % BufferSize] = "ACGT"[(int)qrand() % 4].   De   esta   manera.release().acquire().start(). void Producer::run() { qsrand(QTime(0. char buffer[BufferSize]. fprintf(stderr. }.   que   en   ambos   casos   sería   el   mismo.0).104     defecto.   adquisición   y   liberación.   const int DataSize = 100000. Producer producer.  para   que   no   acabe   la   función   main()   y   mueran   los   hilos   antes   de   que   terminen   su   trabajo. return 0. buffer[i % BufferSize]).   cada   hilo   tiene   su   propio   semáforo. consumer.secsTo(QTime::currentTime())). producer. QSemaphore freeBytes(BufferSize).  no  hay  mucho  que  comentar  de  este  código.   liberando   del   semáforo   contrario.  devuelve  true. i < DataSize.0.   Lo   que   hace   esa  función  (wait)  es  esperar  a  que  dicho  hilo  acabe.wait().wait().  para  terminar  la  ejecución  de  main().release(). consumer.         .start().   y   ambos   hilos   indican   al   otro   las   nuevas   situaciones. }.  y  una  vez  acaba. } } class Consumer : public QThread { public: void run(). usedBytes.     Bueno   y   hemos   dejado   para   el   final. freeBytes.  y  luego  se  llamaría  en  ambos  a  wait(). ++i) { usedBytes.   Desde  main(). class Producer : public QThread { public: void run(). "%c". char *argv[]) { QCoreApplication app(argc.   int main(int argc.   y   adquiriendo   del   suyo   propio. void Consumer::run() { for (int i = 0.  ya  que  son  hilos  diferentes.   el   hilo   principal. i < DataSize.

    QPainter   sitúa   el   origen   de   coordenadas   por   defecto   en   la   esquina   superior   izquierda   (0.setFont(QFont("Arial".   setBrush()   y   setFont().   imágenes   y   textos. painter.   alpha-­‐blending.  que  accede  a  la  librería  2D  y  3D  OpenGL.   Puede   aplicar   efectos   como   antialiasing.       . 30)).  Esto  se  hace  con  QPainter::setWindow().   y   Windows   al   rectángulo   con   coordenadas   lógicas.-­‐  Pintar  una  página  usando  QPainter.  drawArc().  Previamente  a   dibujar   hay   que   definir   las   características   del   pintado   con   las   funciones   setter   setPen().   etc.   drawLines().   drawLine().   un   QPixmap   o   un   QImage.   4.  que  permita  al  usuario  ajustar  la  impresora  y  demás  ajustes.  y  finalmente  se  aplican  al   QPainter  mediante  su  función  setMatrix(QMatrix).       Luego   esta   la   matriz   mundo.   hasta   mapas   de   bits.  drawPie().   drawPoints().  Puede  ser  usado  en  conjunción  con  QPrinter  para  imprimir  o  para  generar  PDFs.  son  las  encargadas  de  dibujar  con  QPainter.0).   drawPolygon.   para   ir   dibujando   con   él.   drawRect().   etc.   drawPolyline().   que   definen   el   lápiz   de   trazado   de   dibujo.-­‐  Crear  QPrinter  para  que  sirva  como  el  dispositivo  de  pintado  (painting  device).  Esto  es  un  ejemplo  sencillo:   void SimpleExampleWidget::paintEvent(QPaintEvent *) { QPainter painter(this).105     TEMA  25   GRÁFICOS  E  IMPRESIÓN       Los   gráficos   2D   en   Qt   están   basados   en   QPainter.   rellenado   con   gradientes.-­‐  Crear  un  QPainter  que  opere  con  QPrinter.  Pues  bién.   y   usando   sus   funciones  rotate()  y  translate()  se  llevan  a  cabo  las  transformaciones.   drawRoundRect(). painter.   sería   su   dispositivo   de   pintado.   el   rodillo   de   rellenado   de   formas.  drawText().   Puede   aplicar   transformaciones   y   más   cosas.   drawEllipse(). painter.   2.  y   luego   instanciamos   con   QPainter(QImage   *).  Sin  embargo  sería  más  cómodo  tener  una  ventana  con  coordenadas  lógicas  y  que  el  sistema   las  pasara  a  físicas  sin  que  el  programador  tenga  que  hacer  las  conversión.  aunque  esto  redunda  en  menos  velocidad.  Esas  herramientas  pueden  ser  cambiadas  en   cualquier  momento.  podemos  usar   QImage  para  dibujar.   Puede   hacer   todo   esto   sobre   un   QWidget.  drawPixmap()  y  drawPath().   3.  y  la  fuente  para  el  texto.  llamamos   viewport   al   rectángulo   con   coordenadas   físicas.     Si  requerimos  de  más  calidad. }   Todas  las  funciones  draw…().  Para  ello  creamos  un  objeto  QImage()  con  un  tamaño  y  formato  de  color. "Qt").  respectivamente.-­‐  Abrir  una  ventana  QPrintDialog.     La   forma   más   típica   de   usar   QPainter   en   un   Widget   es   por   ejemplo   redefiniendole   la   función  paintEvent()  que  se  encarga  de  dibujarlo.     Las   funciones   de   dibujado   de   formas   más   importantes   son:   drawPoint().  drawChord().  así  que   el   mismo   código   que   usamos   para   dibujar   nos   puede   permitir   imprimir.   lineas.   rectángulos.   que   es   una   matriz   para   aplicar   transformaciones   afines   como   rotaciones   y   translaciones.   que   puede   dibujar   desde   formas   geométricas   como.drawText(rect(). Qt::AlignCenter.     QPrinter     Imprimir  en  Qt  es  similar  a  dibujar  con  QPainter  y  se  trata  de  seguir  los  siguientes  pasos:   1.setPen(Qt::blue).   Una   alternativa   a   QPainter  es  el  módulo  QtOpenGL.   QMatrix   es   quien   implementa   dicha   matriz.   puntos.

end().newPage().106     5. this). } painter.   Se  puede  también  imprimir  a  un  PDF  llamando  a  setOutputFormat(QPrinter::PdfFormat). painter. dialog->setWindowTitle("Imprimir Documento").   QPrinter printer(QPrinter::HighResolution).begin(&printer). ++page) { // Usar painter para imprimir la pagina.                                           . for (int page = 0. page < numberOfPages.   El   tamaño   de   la   zona   imprimible   del   papel   viene   determinado   por   QPrinter:pageRect(). QPrintDialog *dialog = new QPrintDialog(&printer.   6.-­‐  Repetir  los  pasos  4  y  5  hasta  que  se  imprima  todo. if (dialog->exec() != QDialog::Accepted) return. if (page != lastPage) printer.  con  ello  se  pueden  hacer  los  cálculos  para  determinar  la   variable  numberOfPages  y  lastPage.   que   devuelve  el  QRect  de  la  zona  imprimible. QPainter painter.-­‐  Llamar  a  QPrinter::newPage()  para  avanzar  a  la  página  siguiente.

ui")..  y  luego  ser  compilados.     Diálogos  dinámicos  (QUiLoader)     Llamamos   diálogos   dinámicos   a   aquellos   que   han   sido   creados   en   el   Qt   Designer   como   ficheros   *.  una  serie  de  prácticas  que  nos  permiten  hacer  colaborar  a  Qt  con  su  exterior. QFile file("dialog.  Qt.     Para   usar   QUiLoader   tenemos   que   añadir   a   *.   puede   cargar   dicho   fichero  con  toda  su  interfaz.   es   necesario   que   en   vez   de   usar   findChild<T>()   de   QWidget...     QUiLoader uiLoader.   Podemos  de  esta  manera  ya  usar  dialog-­‐>show()  para  mostrarlo  de  manera  no-­‐modal  o  dialog-­‐ >exec()  para  hacerlo  de  manera  modal.pro   el   módulo   uitools   de   la   siguiente   manera:     CONFIG     +=  uitools     Si   vas   a   usar   el   compilar   MSVC   6.   y   recuperamos   su   estructura  en  un  puntero  QWidget  (dialog).   conocidos   sus   nombres   (objectName).   Comenzando  por  la  posibilidad  de  cargar  en  tiempo  de  ejecución  un  diseño  de  ventana  de  diálogo   en   formato   *.  Para  ello  existe  una  clase  capaz   de   tal   manejo.  si  no  que  van  a  ser  cargados  en   tiempo  de  ejecución  por  la  aplicación  principal  como  .   hemos   querido   meter   en   un   mismo   tema   con   el   nombre   de   recursos  externos. }     Date   cuenta   que   aquí   cargamos   el   fichero   . QWidget *dialog = uiLoader.. // si combo1 no existe devuelve un puntero nulo if (combo1).   y   cuya   línea   de   comandos   era   muy   larga   e   incómoda   de   usar.         .107     TEMA  26   RECURSOS  EXTERNOS         Acabando   ya   los   temas   que   resumen   todo   lo   que   es   necesario   saber   para   desarrollar   una   aplicación   de   propósito   general.  Usando  de  la  introspección  de  Qt..   por   lo   que   el   uic   (ui   compiler).load(&file)..  nos  permite  eso  mediante  la  clase  QProcess.   Nos   podemos   plantear   el   construirle   una   GUI   para   poder   usarla   de   una   manera   más   gráfica   y   cómoda  por  lo  tanto.   ya   que   el   primero   no   es   soportado   por   dicho   compilador.   uses   qFindChild<T>   de   QtGlobal.  es  facilitar  el  manejo  de  una  aplicación  de   consola   que   previamente   existía.   ir   recuperando   punteros   a   sus   componentes. if(dialog){ // código para recoger los widgets QComboBox *combo1 = dialog->findChild<QComboBox *>("combo1").ui   con   las   uitools.  podemos  mediante   findChild.   y   acabando   por   la   posibilidad   de   añadir   la   capacidad   de   uso   de  librerías  externas  a  Qt.   pasando   por   la   posibilidad   de   ejecutar   procesos   o   aplicaciones   externas   mediante   la   clase   QProcess.     Programas  externos  (QProcess)     Una  de  las  utilidades  de  desarrollar  una  GUI.   que   mediante   su   función   load(QFile)..ui  tal  cual.ui..   pero   que   no   han   sido   compilados   junto   con   la   aplicación.  no  ha  podido  pasarlos  a  C++.ui   con   el   UiLoader.   que   es   QUILoader.

 Qt  provee  de  un  sistema  multiplataforma  a  través  de  la  clase  QLibrary.     Toda   librería   tiene   sus   funciones   exportadas   como   símbolos.   y   readAll()   para   leer   del   mismo.  Un  ejemplo  de  este  tipo  de  comunicación  es  posible  con  programas  como  el  bash   de  Linux. } bash.start("bash"). AvgFunction avg = (AvgFunction) library->resolve("avg").  si  aquél  lo  permite.             .  y  así  sucesivamente.waitForFinished()){ qDebug() << "NO RULA". QProcess::execute(cmd).dll.readAll().108       Para   simplemente   ejecutar   una   aplicación   externa.   Supongamos   que   una   librería   llamada   “mylib”   con   la   extensión   correspondiente   del   operativo   donde   esté   instalada. return -1.   tiene  una  función  que  en  C  tiene  como  prototipo  “int  avg(int. if (avg) return avg(5.   la   librería   curl  sería  –lcurl. bash.  Para   tal  caso.  en  ese  caso  usaremos  la  función  start().     Pero  hay  veces  que  querremos  llamar  en  tiempo  de  ejecución  a  funciones  de  una  librería   dinámica. int).closeWriteChannel().  int)”  que  lo  que  hace  es  realizar  la   media  aritmética  de  los  2  parámetros  que  se  le  pasan. else return -1.   Usaremos   close()   para   cerrar   el   canal   de   comunicación. qDebug() << response.data().  simplemente  hay  que   añadir  a  nuestro  fichero  *. } QByteArray response = bash. if(!bash.  y  devuelve  esa  media  en  formato  entero.close().write("ls").   Así   la   librería   matemática   sería   –lm.waitForStarted()){ qDebug() << "NO RULA".  Veamos  un  ejemplo:   QProcess bash.  y  usaremos  write()  para   enviarle   datos.  en  MacOSX  suele  ser  .  y  en  Linux  es  .exe  de  Windows. typedef int (*AvgFunction)(int.  o  cmd.pro  una  línea  como  esta:   LIBS   +=  -­‐lnombre_libreria   Siembre   que   compilemos   con   el   GNU   Compiler.  habría  que  usar  el  siguiente  código:   QLibrary library("mylib"). bash.     Si   lo   que   queremos   es   establecer   una   comunicación   entre   el   programa   externo   y   la   aplicación.txt". 8). bash.  que  en  Windows  tiene  extensión  . if(!bash. return -1.     Uso  de  librerías  externas     Cuando  queremos  usar  librerías  para  linkarlas  nuestro  programa.so  .  Si   queremos  llamar  a  dicha  función  desde  Qt.dylib.   usaremos   la   función   execute()   de   manera  similar  a  ésta:   QString cmd = "cp a.txt b.  o  Telnet.

  este   capítulo   se   llama   “búscate   la   vida”.   que   no   has   estudiado   antes.  aunque  en  el  tema  23  hemos  visto  un  ejemplo  de  éste  último.   la  labor   de   start().     Luego   nos   encontraremos   con   una   serie   de   funciones   que   llevan   a   cabo   una   labor   especial.  ni  de  explicar  nuevas  cosas.  Debido  a  que  un  poco  de  inglés  sí  que  sabemos.   por   lo   que   es   una   mera   instanciación.  Así  que  si  seguimos  bien  estas  3  clases.  De  todas  formas  si  no  tuviéramos   claro  el  concepto  de  nuestra  clase.   Por   ello   vamos   a   practicar   en   este   tema   a   buscarnos   la   vida   en   el   asistente.   Busca   por   tanto.   y   nunca   las   has   usado.   por   lo   que   la   única   forma   de   mantenerse  al  día.  ¡  Caramba.       Lo   primero   sería   saber   qué   clases   podrían   encargarse   de   la   hora.   esto   no   quiere   decir   que   ya   me   haya  cansado  de  escribir  este  libro.  la   fecha  e  incluso  del  uso  de  timers.   que   podemos   leer   en   su   descripción   de   qué   se   trata.  Qt  es  un  Framework  que  ya  tiene   casi   la   veintena   de   años.109     TEMA  27   BÚSCATE  LA  VIDA       Sí.   de   la   fecha.   Hay   que   pensar   que   Qt.  siempre  puede  uno  darse  una  paseo  por  todas.  pero  simplemente  lo  sería  por   tener  más  funciones.       Por  tanto.  Se  trata  de  la  hora.  y   que  curiosamente  eches  de  menos.  señales  y  slots.   También   es   muy   importante   aquella   que   recoge   la   hora   exacta   actual.   C++   u   otro   lenguaje.   es   quedarse   con   las   propiedades   o   variables   privadas.  Qt  está  muy  bien   documentado.   pero   en   inglés.   Por   ejemplo.   has   leído   bien.  Así  nos  encontramos  con  hour().  es  decir.  buscándose  uno  mismo  la  vida.   y   debe   de   existir   por   tanto   una   función   que   más   tarde   pueda   poner   la   hora.   seguirá   creciendo   en   el   futuro.  no  habría  por  qué  haber  ningún  problema   en  cualquier  otra.   y   tengas   que   empezar   a   escribir   código.  y  el  ya  mencionado  setHMS()  como  el  único  setter.  minute()   y  second()  en  los  getter.       Otra   de   las   cosas   importantes   que   hay   que   mirar   en   una   clase.   por   lo   que   es   fundamental   entender   al   menos   el   idioma   de   Shakespeare  a  nivel  de  lectura  técnica.   pero   es   posible   que   alguna   vez   te   plantees   hacer   otra   cosa.   o   de   un   proporcionar  un  timer.   restart()   y   ellapsed().  lo  que  pretendemos  con  este  capítulo  es  cubrir  esa  parte  que  viene  después  de   cuando   acabes   este   libro.       QTime     Es  evidente  que  el  constructor  de  esta  clase.  debe  de  permitir  iniciar  una  instancia  con   una   hora   determinada.  También  es  cierto  que  hemos   tocado  las  clases  que  más  vas  a  usar  siempre  que  desarrolles  aplicaciones  de  propósito  general.  se  me  ha  ocurrido  plantear  unas  clases  que  no  hemos  visto.   tanto   que   ningún   libro   que  lo  trate  puede  aspirar  a  tocar  absolutamente  todas  sus  clases.   y   en   algunas   ocasiones   te   tropieces   con   que   tienes   que   usar   cosas.   y   así   aparece   en   sus   descripciones.   debe   de   ser   setHMS().   e   incluso  que  no  puedas  encontrar  en  los  foros  a  nadie  que  lo  haya  usado  antes.   que   la   fecha   es   Date  y  Timer  está  claro  lo  que  es.  propiedades.   y   esto   no   esté   cubierto   en   ningún   libro.  y  más  si  hemos  trabajado   con   librerías   similares   en   C.  que  suerte  tenemos  !.   Están   muy   relacionadas   entre   sí.  para  detectar   dentro   de   su   nombre   un   atisbo   de   luz.   sabemos   que   la   hora   es   Time.   por   lo   que   a   día   de   hoy   ha   crecido   enormemente.   que   de   seguro   es     .   uno   sin   parámetros.  o  por  que  el  texto  de  la  descripción  es  mucho   más  extensa.   QDate   y   QTimer.   Así   que   nos   vamos   al   asistente.   Vemos   entre   las   public   functions.   veremos   que  hemos  acertado  en  todo.   y   buscamos   las   3   clases.   ¿   de   qué   cree   que   tratará   la   clase   QMimeData   o  QFtp  ?.  si  has  programado  en  otros  entornos.  para  practicar.  es  usando  el  Qt  Assistant.       Pues  bien.   que   por   su   prototipado.   Realmente   podría   haber  centrado  la  búsqueda  en  cualquier  otro  tema  más  complejo.   ya   que   asociadas   a   ellas   vamos   a   encontrar   una   serie   de   funciones  setter  y  getter  importantes  para  su  manejo.   siempre   en   primer   lugar   los   distintos   constructores.  así  que  no  sería  muy  descabellado  pensar  que  dichas  clases  se   llamen   QTime.

 si  es  así.   porque   el   último   tema   es   el   siguiente.     Como   public   functions.   y   singleShot   que   es   un   bool   que   estará   true   si   es   de   único   disparo.  MacOSX  y  Windows.   Y   así   es.  que  de  seguro  contiene  el  intervalo   programado.   si   es   repetitivo.  y  los  de  un  solo  disparo.   se   dispara   el   evento   del   timer.  Por  ello  te  animamos  a  que  la   describas  tú  mismo.   Los   timers   repetitivos   son   aquellos   que   cada   intervalo.  es  que  ya  estas  preparado  para   nadar   tú   sólo   en   el   universo   Qt.   dispare   una   señal.   mientras   que   teniendo   el   asistente.  isActive()  e  isSingleShot().  en  segundos  (addSecs).  que  son:  active.     Lo   primero   que   nos   dice   el   asistente   es   que   QTimer   provee   de   timers   repetitivos   y   de   un   solo   disparo.   y   trata  de  algo  que  muy  pocos  suelen  tratar.   porque   otra   posibilidad   que   nos   puede   ocurrir.  que  es  un  bool  que  estará  a  true  cuando  esté   activo.   tenemos   las   más   importantes.   y   stop()   para   pararlo   en   cualquier   momento.                 .110     currentTime().     Te  animamos  a  que  escribas  un  programa  sencillo.  interval.   deben   hacer   getters   y   setters.   o   a   false.  que  cada  segundo.  aumente  el  valor   de  un  contador  en  un  QLCDNumber.   Como   setters   tenemos   setInterval()  y  setSingleShot().  lo  cual  puede  ser  muy  útil.  de  cómo  preparar  un  instalador  para  nuestra   aplicación  para  los  3  operativos  más  importantes:  Linux.     Puesto  que  QTimer  provee  de  una  señal.  y  como  getters  tenemos.   programar   el   intervalo   y   el   slot   a   ejecutar   de   un   determinado   objeto.     QTimer     Esta   clase.  y  false  cuando  no  lo  esté.   ya   que   es   muy   útil   siempre   el   uso   de   timers.   El   dejarla  para  un  tema  autosuficiente  como  éste.   que   son   start()   para   poner   en   marcha   el   timer.  es  que  se  nos  olvide  la  funcionalidad  de  algunas  de  las  clases  que  ya  hemos  explicado.  timeout()  es  también  muy  lógico  el  usarla  para   conectarla  a  slots  de  otros  objetos  que  hagan  algo  una  vez  pasado  el  tiempo.  y   no   tengas   cerca   este   libro   para   repasarlas.   era   fundamental   explicarla.   Hay   otra   función   llamada   singleShot()   que   nos   permite.  Y  aún  más  útil  una  serie  de  funciones  que   permiten  añadir  a  la  hora.  esto  es.  Es  de  suponer  que   para   indicar   que   ha   llegado   al   final   de   su   contador.  entre  los  static  public.  cuando  dicho  intervalo  haya  pasado.   Pero   aún   no   te   vayas.  Dentro  de  los  operadores.   de   hecho   sólo   podemos  ver  la  señal  timeout().  vemos  que  se  pueden  comparar   2  objetos  QTime  entre  sí.   En   torno   a   estas   propiedades.  sólo  se  programar  y  se  disparan  una  sola  vez.  que  es  un  entero.  y  luego  compruebes  su  funcionalidad  en  un  código  también  sencillo.  intervalos  de  tiempo  variables.  nos  va  a  permitir  ser  más  autónomos  de  aquí  en   adelante   a   la   hora   de   manejar   el   Framework   de   Qt.       QDate     Se  trata  de  una  clase  de  similar  disposición  a  la  anterior.   siempre   podrás   sacar  el  trabajo  adelante.       Veamos  sus  propiedades.         Te  animo  a  que  hagas  pruebas  con  ellas  en  un  código  sencillo  de  consola.     Quizás  este  tema  te  haya  parecido  muy  trivial.

  selecciona   en   “Edit  Build  configuration”  la  opción  Release.exe).  todos  los  elementos   necesarios   para   que   la   aplicación   funcione   autónomamente.  en  Windows  vamos  a  tener  que  usar  una   herramienta  que  no  viene  incluida  en  el  SDK  de  Qt.   una   carpeta   que   se   llamará   application-­‐build-­‐desktop.  En  nuestro  caso  hemos  bajado  la  versión  de  32  bits  (for   x86).   No   debe   de   haber   ningún   problema.   abre   el   archivo   de   proyecto   (.       Windows     Si   no   lo   has   hecho   anteriormente.     Llegó   el   momento   de   tomar   en   consideración   qué   archivos   debemos   de   llevarnos   con   nuestras   aplicaciones   fuera   del   ordenador   donde   se   efectuó   desarrollo.  Ya  tenemos  en  el  directorio  donde  guardemos   nuestros   fuentes.   y   en   el   sistema   está   registrada   la   run-­‐time   de   Visual   C++   9.   y   pulsando   en   el   modo   Projects   (iconos   de   la   izquierda).  donde  vamos  a  probar  que  entregamos  todo  lo  necesario  para   que  nuestra  aplicación  funcione.     El   entorno   de   desarrollo   instalado   por   defecto.   Vamos   pues   a   usarla   para   crear   el   paquete   de   instalación   correspondiente   a   cada   uno   de   los  sistemas  operativos  objetivo.  cuando  preparamos  el  entorno  de  desarrollo.   avisábamos   de   que   las   aplicaciones   desarrolladas   por   defecto  con  estas  instalaciones  por  defecto  del  SDK  de  Qt.  vamos  a  File  -­‐>  Open.  y  abrimos  nuestro  ejecutable  (application.  en  los  3  sistemas  operativos  de  siempre  (Windows.exe).   funciona   con   librerías   de   enlace   dinámico.   las   liberías   dinámicas   correspondientes   no   estarán   registradas   en   el   sistema.   debemos   prepararle   al   ejecutable.   el   ejecutable   final   (application.  Por  ello  es  muy  interesante  disponer.   todo   lo   que   requiere   en   su   propia   carpeta.  Luego.   en   un   sistema  con  VS2008  tendríamos  algo  así:     .   está   en   las   ramas   principales   de   dichos   árboles.  así  que  vamos  a  ello.  toda  una  serie  de  librerías  y  plugins.  llamada  Dependency  Walker.   ya   que   quedó   completamente  creada  y  probada  en  el  tema  20.   pero   que   enlazan   en   tiempo   de   ejecución   con   una   serie   de   librerías   dinámicas.111     TEMA  28   INSTALADORES       Al  final  del  tema  2.pro)   en   tu   entorno   de   desarrollo   (Qt   Creator).   Linux  y  Mac  OSX).  Si  usas  el  compilador  MinGW  que  va   en   el   paquete   de   Qt4.  sobre  la  que  el  mismo  se  va  a  apoyar   para  poder  funcionar.   pero   lo   que   nos   interesa.  para  poder  desarrollar   todos   los   ejemplos   del   libro   sin   problemas.   y   la   ejecución   de   la   aplicación   desde   su   carpeta.   por   lo   que   vamos   a   compilar   ejecutables   que   son   poco   pesados.     Vamos  por  tanto  a  seguir  un  ejemplo.  Puedes  bajarla   de  http://www.   Si   usas   Visual   Studio   2008   como   compilador.   Podemos   incluso   ejecutarla.  no  podían  ser  implementadas  tal  cual   en  sistemas  de  producción  donde  el  SDK  no  va  a  estar  instalado.   podremos   recurrir   a   crear   un   sistema   de   instalación   que   se   encargue   de   copiar   dichos   elementos   en   la   manera   debida   al   sistema  en  producción.  y  lo  vamos  a  hacer  con  la  aplicación  que  desarrollamos  en  el  tema  20  que  es  un   editor   de   textos   donde   están   presentes   los   elementos   típicos   de   una   aplicación   de   propósito   general.  disponer  de  otro  (máquina  real  o  virtual)  donde  esté  instalado   el  operativo  por  defecto  sin  más.  podrás  ejecutar  sin  problemas  la  aplicación.   por   lo   que   nos   tendremos   que   llevar   junto  con  el  ejecutable.   en   cuyo   interior   encontraremos   en   la   carpeta   Release.dll”.  Una  vez  tengamos  en  una  carpeta  a  parte.   Sea   como   fuere.dependencywalker.  si  no  también.  no  sólo  de  un  ordenador  operativo   para  el  desarrollo.       Para  saber  de  qué  depende  nuestra  aplicación.   para   que   todo   funcione   correctamente.   te   dará   error   indicándote   la   primera   librería  dinámica  que  intenta  enlazar  pero  que  no  encuentra.0   “msvcp90.com/.  Al  ejecutarla.  Se  nos   despliega   en   la   ventana   superior   izquierda   todo   un   árbol   de   dependencias.  vamos  a  reconstruir  la  aplicación  con  Rebuild   Project.   Si   colapsamos   dichas   ramas.

  abre   el   archivo   de   proyecto   (.     Linux       Si   no   lo   has   hecho   anteriormente.  Quien  use  MinGW  se  encontrará  con  2  dependencias.  Pues  las  librerías  de  Qt  se  encuentran  en  el   directorio   /bin   de   la   instalación   del   Qt   SDK.  vamos  a  reconstruir  la  aplicación  con  Rebuild   Project.   ldd  .   como   puedan   ser   drivers   de   bases   de   datos.  en  nuestro  caso  en  la  carpeta  x86  (para  PC).  hay  una  herramienta  que  ya  viene  con  las  herramientas  GNU.  ya  viene  provista  por  el  sistema  operativo  Windows  (kernel32./application   Nos   responderá   con   una   serie   de   librerías   dinámicas   del   tipo   .0\VC\ce\dll).so   .     ¿Dónde  podemos  encontrar  esas  librerías?.   La   aplicación   quedará   en   su   directorio   de   manera   que   puede   ejecutarse   directamente   desde   él   (.pro)  veremos  que  pide  añadir  los  modulos  “core”  y  “gui”:   QT   +=  core  gui   Por   lo   que.   las   podíamos   preveer.  Luego.  nuestra  aplicación  compilada  con  VisualC++  2008  (9.dll  .dll).   ya   que   quedó   completamente   creada   y   probada   en   el   tema   20.  es  posible  que  dicha  librería  se  instale   mediante   algún   paquete   que   se   pueda   bajar   de   los   repositorios   oficiales   de   la   distribución   de   Linux   en   cuestión.   selecciona   en   “Edit  Build  configuration”  la  opción  Release.   /usr/lib   ó   /usr/local/lib.0)  depende  de  4   dlls./application).  drivers  de  formatos  gráficos.pro)   en   tu   entorno   de   desarrollo   (Qt   Creator).   ya   que   si   miramos   el   contenido   del  fichero  de  proyecto  (application.     De   hecho   estas   3   dependencias.dll  y  libgcc_s_dw2-­‐1.   No   debe   de   haber   ningún   problema.   es   de   suponer   que   requerirá   ambas   librerías   dinámicas.   y   pulsando   en   el   modo   Projects   (iconos   de   la   izquierda).       Habrán   aplicaciones   que   hacen   uso   de   plugins.  por  lo   que  realmente  vamos  a  requerir  de  las  Dll’s  QtGui4.112       Como  podemos  observar.   Podemos   incluso   ejecutarla.   donde   podrás  elegir  la  arquitectura  de  CPU.     Así  que  trasladamos  esas  3  dependencias  al  mismo  sitio  donde  esta  nuestro  ejecutable.  verás  que  se   puede  ejecutar  sin  problema  alguno.dll  y  MSVCR90.   Pero   a   parte   de   estas   librerías  veremos  otras  que  no  están  en  esos  directorios  que  de  seguro  son:     .  Y  si  es  así.   Si  llevamos  esos  4  ficheros  en  una  carpeta  a  cualquier  PC  con  Windows  XP.   las  de  los  ficheros  mingwm10.dll.  QtCore4.   La   gran   mayoría   de   esas   librerías   suelen   estar   instaladas   en   un   sistema   limpio   con   gestor   de   ventanas   Gnome   o   KDE.  pero  una  de  ellas.  etc.  Veremos  luego  en  un  apartado  qué  hacer  en  ese  caso.   La   tercera   dependencia   es   típica  de  quien  usa  el  compilador  VS2008.   Finalmente   dichas   librerías   siempre   acabarán   en   /lib.   La   librería   de   Visual   C++   está   en   el   directorio   de   instalación   de   VS2008   (\Archivos   de   Programas\Microsoft   Visual   Studio   9.   Para   comprobar   sus   dependencias.  que  es  ldd.   Pero   es  conveniente  que  antes  te  cerciores  de  ello.  Vista  o  7.   De   esta   manera   son   siempre   accesibles   a   la   aplicación.dll  .

4:   application.app/Contents/MacOs/application   En  nuestro  caso  responde  así  en  Mac  OSX  10.dylib  (compatibility  version  1.  vamos  a  reconstruir  la  aplicación  con  Rebuild   Project.  Veremos  luego  en  un  apartado  qué  hacer  en  ese  caso.   Podemos   incluso   ejecutarla.  usamos  una  herramienta  que  lleva   el  XCode.so.0.0.   frameworks.4*  application_folder       Habrán   aplicaciones   que   hacen   uso   de   plugins.6.  Inicialmente  application.so.   como   puedan   ser   drivers   de   bases   de   datos.   ya   que   quedó   completamente  creada  y  probada  en  el  tema  20.       Mac  OSX     Una   aplicación   para   Mac.app   es   un   bundle   y   tiene   la   forma   que   definimos   en   el   gráfico   siguiente:     Si   no   lo   has   hecho   anteriormente.so.0)   /usr/lib/libgcc_s.plist   MacOS     PkgInfo     Resources   Para  ver  las  dependencias  del  ejecutable  en  sí  (no  del  bundle).framework/Versions/4/QtGui  (compatibility  version  4.framework/Versions/4/QtCore  (compatibility  version  4.113     libQtGui.1.0.4*  application_folder   cp  –R  qtsdk/qt/lib/libQtCore.  current  version  7.   selecciona   en   “Edit  Build  configuration”  la  opción  Release.7.dylib  (compatibility  version  1.   En   realidad   un   .  current  version  4.   No   debe   de   haber   ningún   problema.7.app/Contents/MacOs/application:               QtGui.0.   plugins.0.   etc.   abre   el   archivo   de   proyecto   (.0.app  contiene:    Info.dylib  (compatibility  version  7.0)   QtCore.  drivers  de  formatos  gráficos.   y   en   sí   mismo   es   una   carpeta   en   cuyo   interior   lleva   el   verdadero   ejecutable   y   todos   los   recursos.  etc.   Puesto   que   se   tratan   de   links   a   ficheros   de   otro   nombre   lo   hacemos   de   la   manera:   cp  –R  qtsdk/qt/lib/libQtGui.B.0.0.  que  se  llama  otool:   otool  -­‐L  application.0)   /usr/lib/libstdc++.   que   necesita   para   funcionar.app.   y   pulsando   en   el   modo   Projects   (iconos   de   la   izquierda).4  .4  y  libQtCore.  current  version  625.9.6.7.7.   lleva   la   extensión   .0)   /usr/lib/libSystem.  current  version  4.0.  Estas  las  vamos  a  tener  que  poner  al  alcance  de  nuestra  aplicación   en   su   propio   directorio.2.  current  version  125.pro)   en   tu   entorno   de   desarrollo   (Qt   Creator).0)     .so.  Luego.

 que  pide  los  siguientes  requisitos  para  la  construcción  del  bundle  final: 1.     Los  plugins  para  bases  de  datos  que  el  SDK  provee  son  muy  limitados  (ODBC  y  SQLite).  para  cualquiera   de  las  3  plataformas  que  hemos  explicado.  Nokia  nos   ofrece   una   herramienta   con   el   SDK   que   lo   hace   por   nosotros   de   manera   sencilla   y   cómoda.  el  contenido  del  bundle  habrá  cambiado  a:   Frameworks   Info.   siempre   son   añadidos   al   bundle   si   la   aplicación   usa   los   módulos   correspondientes   QtSql.   QtScript.  De  hecho  puedes  verlas  a  todas  en  /Library/Frameworks.  Las  2  superiores.-­‐   Si   quieres   añadir   una   librería   no-­‐Qt   al   bundle. 5.     .pro   como  una  librería  explícita.dylib)  son  propias  de  todo  sistema  por  defecto.   Scripts   plugins.  y  compilando  lo  que  necesites  conforme  a  los  README  que   le   acompañan.   El   último   parágrafo   de   este   tema   explica   el   procesos   de   compilación   del   driver   para  MySQL.   vamos   a   crear  el  bundle  de  una  vez  y  por  todas  haciendo: macdeployqt  application.   Se   llama  “macdeployqt”.app   Tras  lo  cual.   permiten   añadir   nuevas   funcionalidades   a   las   aplicaciones.  En  tal  caso.   Los   nombres   de   esas   carpetas   son   fijos.     Cómo  crear  a  mano  el  bundle  con  todo  lo  que  necesita.     Plugins     Los   plugins   en   Qt.  están  en  el  directorio  /qt/plugins  en  su  carpeta  de   instalación. 2.-­‐  Los  plugins  designer  no  son  añadidos  al  bundle.   Nosotros  hemos  añadido  el  de  PostGresql  y  el  de  MySQL  en  nuestros  ejemplos.   aunque  nuestra  herramienta  ya  se  haya  encargado  también  de  colocar  los  plugins  que  considera   necesarios  en  sus  respectivas  carpetas  dentro  del  bundle.  se  añadiría  sin  problemas  a  bundle.   y   alguno   más.   SVG   Icon   plugins.   Bien.   Podemos   borrar  a  mano  los  que  sobran  si  queremos  ahorrar  espacio.  Puedes  compilar  tú  mismo  cualquier  plugin  bajando   los  fuentes  del  SDK  (qt-­‐everyone-­‐src).   ya   que   por   defecto   es   ahí   donde   los   buscará.   Phonon   back-­‐end   plugins.       Ya   podemos   distribuir   la   aplicación   sin   problemas.plist   MacOS     PkgInfo     PlugIns     Resources   Podemos   ver   que   en   Frameworks   se   han   añadido   los   que   hacían   falta.  o  nuevos  tipos  de  bases   de  datos.   como   puedan   ser   drivers   de   bases   de   datos.  Los  plugins  incluídos  en  el  SDK.   entonces   en   la   función   main()   de   la   aplicación   tendremos   que   decirle   donde   encontrarla  usando  QCoreApplication::addLibraryPath()  .   la   misma   no   tendrá   problemas   en   encontrar   dicho   plugin   si   lo   requiere.-­‐  Las  versiones  debug  de  los  plugins  no  son  añadidas  al  bundle. 3.114     Las  3  librerías  inferiores  (.   Phonon   y   QtSvg. 4.  etc.-­‐   SQL   drivers.   y   si   se   trasladan   tal   cual   a   la   carpeta   del   ejecutable   de   la   aplicación.   etc.  son   librerías  que  se  han  instalado  en  forma  de  Framework.   y   para   instalarla   en   otro   equipo   el   usuario   solamente   tendría   que   arrastrar   el   icono   correspondiente   al   .     Habrán   aplicaciones   que   hagan   uso   de   plugins.   tendrás   que   antes   añadirla   al   proyecto   .  sería  largo  y  tedioso.app   a   la   carpeta   Aplicaciones.   Así   hay   plugins  que  permiten  añadir  nuevos  formatos  de  imágenes  legibles  en  Qt.-­‐  Los  plugins  para  formato  de  imágenes  sí  que  son  añadidos  al  bundle.   drivers   de   formatos   gráficos.   Si   queremos   ponerlos   en   otro   sitio   diferente.   Veremos   en   el   siguiente   apartado   como   hacer   esto.   como   en   nuestro   caso   cumplimos   sin   problemas   todas   estas   premisas.  que  es  como  se  instalan  por  defecto  las   librerías  del  Qt4  SDK  en  Mac.

      Crear  un  instalador  con  esta  herramienta  es  muy  sencillo  e  intuitivo  a  pesar  de  que  esté   en  inglés.     Al   arrancar   InstallJammer.app   sobre   la   carpeta   Aplicación.  Y  vamos  a  empezar  por   Windows   y   Linux.   pulsamos   en   New   Project.  Vamos  a  hacerlo  de  2  maneras  diferentes:  una  mediante  una  imagen  .  Su  nombre  es  InstallJammer.   o   dejando   su   valor   por   defecto.   y   se   nos   abre   un   wizard   para   crear   el  proyecto  del  instalador.  listo   para  ser  usado.       Vamos   a   crear   una   imagen   DMG   con   la   utilidad   de   discos.  Vamos  a  seguir  con  nuestra  aplicación  para  hacer  el  ejemplo  de  creación  del  instalador.  Linux  y  otros  UNIX.   y   que   nos   permitirán   crear   un   instalador   con   aspecto   profesional.0-­‐Setup.   Pulsaremos   sobre   Nueva   Imagen.  script  o  similar.  pero  sí  podemos  usar  herramientas  que  vienen  en  nuestro  sistema  por  defecto.   Vamos   a   construir   con   los   valores   por   defecto  para  Windows  o  para  Linux  conforme  necesitemos.   Aunque   hay   muchas   aplicaciones   que   están   especializadas   en   hacer   instaladores   de   un   paquete.  y  vamos  a  poner  los  ajustes  de  la  imagen  de  a  continuación:   Así  de  sencillo  es  crear  un  instalador  con  este  sencillo  programa.app).  Rellenamos  los  datos  que  se  nos  van  pidiendo.dmg  donde  se   arrastra   el   .  Contestadas  todas  las   preguntas.     Seguimos  usando  nuestro  ejemplo  ya  empaquetado  en  un  bundle  completo.exe   (por   ejemplo   para   Windows).  preparado  para  llevar  a  otro  equipo  (application.   Dejamos   al   usuario   el   husmear   por   toda   una   ingente   cantidad   de   parámetros.  se  nos  abre  la  carpeta  donde  se  ha  creado  el  ejecutable  instalador.         Instaladores  en  Mac  OSX     Para   crear   instaladores   en   Mac.   no   hay   ninguna   aplicación   simple   y   libre   como   la   que   acabamos  de  ver.   y   otra   creando   una   paquete   instalable   mediante   la   herramienta  PackageMaker  de  XCode.   nosotros   hemos   escogido   una   en   particular   desarrollada   en   Tk/Tcl   que  permite  crear  instaladores  para  Windows.115     Instaladores  en  Windows  y  Linux     Sabiendo   ya   el   paquete   que   debemos   copiar.  de  código  abierto  y  multiplataforma.  y  vemos   en   la   ventana   como   se   nos   crea   Application-­‐1.  como  a  nosotros  nos  gusta.   y   se   nos   abre   el   Install   Designer   con   una   serie   de   ítems   que   podemos   ir   cambiando   a   nuestro   gusto.  Vamos  a  proponerte  una  opción  más  sencilla     para  que  puedas  tener  en  cuenta  a  la  hora  de  entregar  tus  aplicaciones.  Pulsamos  sobre  Build  Install.       .  tal  como  lo   dejamos.   una   vez   hemos   instalado   XCode.   pulsamos   en   Finish.   que  es  gratuito.   Si   pulsamos  sobre  su  link.   podríamos   nosotros   mismos   crear   un   instalador  mediante  una  aplicación.

  Se   crea   el   DMG   final   preparado   para   ser   distribuido.   y   eligiendo   la   imagen   DMG   creada.  para  que  se  produzca  la  instalación.app  y  una  fondo  en  PNG  que  crearemos  de   700x400.   Dicha   aplicación   la   puedes   encontrar   en   /Developer/Applications/Utilities/PackageMaker.   pulsamos  Cmd+J.app.   y   arrastramos   a   ella   el   bundle   .   Con   el   DMG   montado   y   abierto   en   el   finder.   y   la   guardamos   esta   vez   comprimida.116     La  creamos  del  tamaño  suficiente  para  que  quepan  .  similar  a  éste:     Montamos   con   un   doble   clic   la   imagen   DMG   vacía   recién   creada.  Ya  se  verá  el  DMG  montado  con  la  imagen   de   fondo.   Pulsando   sobre   el     .   y   el   operativo   objetivo   mínimo.  y  hacemos  algo  como  esto:     El   fondo   lo   elegimos   haciendo   doble   click   en   el   cuadro   de   arrastrar   la   imagen.   y   podremos   situar   los   iconos.   lo   primero   que   nos   pide   es   la   organización   (tipo   com.   y   redimensionar   la   ventana   para   ajustarla  al  tamaño  del  fondo.pkg     con   el   PackageMaker   del   XCode.   Al   montarlo   quedará   algo   como   esto:           Donde   queda   clara   la   intención   de   que   se   arrastre   el   icono   de   application.app   sobre   la   carpeta   Applicaciones.  la  arrastramos  al   DMG   y   le   cambiamos   el   nombre   a   Aplicación.       Pasemos   a   la   segunda   opción.  Se  abren  las  opciones  de  carpeta.nombre_organizacion).  Expulsamos  el  DMG  montado.   pulsamos   en   convertir.   que   consiste   en   crear   un   paquete   de   instalación   del   tipo   *.  y  seleccionamos  el  PNG.  y  creamos  un  alias  de  la  carpeta  Aplicación.  y  nos  vamos  a   la   utilidad   de   discos.     Al   arrancarla.  Luego  nos  vamos  al  disco  raiz.

 Si  seleccionamos  el  paquete  de  distribución  (encima  de  Contents).   g++   y   make   desde   la   linea   de   comandos.   tenemos   el   icono   “Edit   Interface”.   ya   tenemos   todas   las   opciones   rellenadas   conforme   a   nuestra   necesidad.   donde   tendremos   respectivamente   la   librería   necesaria   para  nuestro  cliente.  podremos  programar  requisitos  para  instalarlo.   frameworks.     Los  fuentes  de  MySQL.   que   es   sin   duda   MySQL.   además   de   tener   instalado   el   sistema   de   desarrollo   completo.  y  los  fuentes  de  MySQL.   de   manera   que   podremos   también   copiar   ficheros   en   zonas   del   sistema.  tendremos  que  bajarnos  los  fuentes  de  MySQL  y  de  Qt4.   A   parte   de   la   instalación   completa.   se   pueden   poner   varios   elementos   (aplicaciones.  y  está  en  inglés.   o   puede   meterse   en   una   DMG   tal   como   aprendimos  anteriormente.   cambiando   su   fondo.   si   vamos   a   permitir   que   el   usuario   reubique   la   instalación   y   si     se   va   a   requerir   autentificación   por   parte   del   administrador   para   instalarla.     .  por  cuestiones  de  licencias  no  lleva  compilados  los  drivers  más  que  de  ODBC   y  SQLite  (que  va  integrado  en  Qt4).app).   siempre  es  recomendable  usar  el  driver  nativo  sin  más  añadidos.   versión   del   sistema   operativo   y   muchos   más.  y  quizás  con  esto  haya  suficiente  para  muchos.   poniéndole   un   nombre.   que   en   nuestro   caso  es  solamente  uno  (application.   y   una   vez   ésta   ha   finalizado.   verás   que   también   es   muy   intuitiva   y   potente.mysql.  que  construirá  el  paquete  de   instalación.   Para   poder   hacerlo.   podemos   elegir  qué  vamos  a  instalar   y   donde.   En   la   zona   superior   derecha.       Vamos  por  tanto  a  explicar.   Dicho   paquete   puede   entregarse   como   tal.   CPU.   librerías.  como  compilar  el  driver  para  una  de  las  bases  de  datos  más   usadas.   tendremos   dos   carpetas   fundamentales   que   son   /lib   y   /include.   podemos  pulsar  en  el  icono  de  la  zona  superior  izquierda  “Build”.       Una   vez.  podemos  bajarlos  de   http://www.  como  espacio   libre   en   disco   duro.   para   poder   ejecutar   qmake.com/downloads/mysql/   en   la   version   essentials.   como   ya   tenemos.   y   el   texto   de   cada   uno   de   los   pasos   de   la   instalación.   también   se   podrán   programar   acciones   a   llevar   a   cabo   antes   de   proceder   a   la   instalación.  pero  una  gran   cantidad   de   gente   usa   otras   bases   de   datos.  En  Contents  podemos  elegir  el  usuario  y  grupo  al  que  pertenecerá  este  contenido  y  sus   permisos.  En   la   zona   de   contenidos.   Si   navegas   por   esta   herramienta.   y   aunque   podrían   usar   el   driver   ODBC   para   ello.  el  destino  de  instalación.   los   tipos   de   selección   accesibles   al   usuario.   ayudas.   etc).117     +   en   la   esquina   inferior   izquierda   podemos   añadir   los   componentes   a   instalar.   En   Configuration.   La   versión   Open  Source  de  Qt4.  podremos  editar  las   opciones   del   paquete   completo.       Compilación  de  un  driver  SQL     El  último  de  los  escollos  a  sortear  para  distribuir  nuestra  aplicación  es  que  tengamos  el   driver   del   tipo   de   base   de   datos   que   vamos   a   usar   en   nuestra   aplicación   compilado.   que   nos  permite   editar   el   interfaz   de   instalación.

 Ahí  encontraremos   un  fichero  main.dylib  y  libqsqlmysql_debug.lib"  mysql.     Situados  en  el  directorio  de  los  fuentes  del  driver  para  Qt4.pro  .  pero  aún  queda  mucho  camino  por  andar.com/   .   estática   y   dinámica   (qsqlmysql4.lib).x.cpp  con  el  código  del  driver  y  el  fichero  de  proyecto  mysql.   están   en   la   web   de   Nokia   http://qt.pro   nmake  debug   nmake  release     Tendremos   al   final   las   versiones   debug   y   release   de   los   drivers.pro   make     Se  creará  la  versión  release  dinámica  libqsqlmysql.dll  /  qsql_mysql.nokia.so    .     2.   en   el   que   hemos   intentado   dotarlo   de   todo   el   contenido   práctico   necesario   para   el   desarrollo   eficaz   de   aplicaciones   de   propósito  general  en  Qt4.   En   este   paquete   encontraremos   los   fuentes   que   necesitamos   de  nuestro  driver  en  la  carpeta  $QT_SOURCES/src/plugins/sqldrivers/mysql.118       Los   fuentes   de   Qt4.     Con   esto   damos   por   finalizado   el   contenido   de   este   libro.-­‐  Windows  (VS2008):   Presuponemos  %PATH_MYSQL%  como  el  raiz  de  los  fuentes  de  MySQL.dylib  .     3.-­‐  Linux:   Presuponemos  $PATH_MYSQL  como  la  raiz  de  los  fuentes  de  MySQL   qmake  "INCLUDEPATH+=$PATH_MYSQL/include"  "LIBS+=-­‐L$PATH_MYSQL/lib  -­‐lmysqlclient_r"  mysql.   y   se   llama   qt-­‐ everywhere-­‐opensource-­‐src-­‐4.x   .-­‐  Mac  OSX:   Presuponemos  $PATH_MYSQL  como  la  raiz  de  los  fuentes  de  MySQL   qmake  -­‐spec  macx-­‐g++  "INCLUDEPATH+=PATH_MYSQL/include"  "LIBS+=-­‐LPATH_MYSQL/lib  -­‐lmysqlclient_r"  mysql.  ejecutaremos  los  siguientes   comandos  en  línea:   1.pro   make  debug   make  release     Se  crearán  la  versión  release  y  debug  dinámicas  libqsqlmysql.   qmake  "INCLUDEPATH+=%PATH_MYSQL%\include"  "LIBS+=%PATH_MYSQL%\  lib\opt\libmysql.                   .  pero  sin  duda  ya  tenemos  los   fundamentos  para  que  este  viaje  sea  más  agradable  y  sencillo.

119     BIBLIOGRAFIA     An  introduction  to  design  patterns  in  C++  with  Qt4  -­‐  Prentice  Hall  -­‐  Alan  &  Paul  Ezust     The  book  of  Qt4.  The  art  of  building  Qt  applications  -­‐  No  Starch  Press  -­‐  Daniel  Molkentin     C++  GUI  Programming  with  Qt4  -­‐  Prentice  Hall  -­‐  Jasmin  Blanchette  &  Mark  Summerfeld     Qt  Assistant  –  QT  NOKIA  SDK   The  C++  Programming  Language  –  Addison  Wesley  –  Bjarne  Stroustrup                                                     .

120                                                                 Finalizado  el  20  de  Octubre  de  2010  –  anónimo     .