t>,,qu que etr trab abaj aje ede def for orma maó ópt ptim ima. a. Para fi finalizar, ex existen la las ve versiones QMultiMap y QMultiHash ,paracuandounamisma key,puedetenerasociadosvariosvaloresalavez. key,puedetenerasociados variosvaloresalavez.Enestoscasos,nosepuedeusareloperado Enestoscasos,nosepuedeusareloperador[] r[] paraintroducirnuevosdatos,yseutilizainsert().Sisequiererecogerelvalorkeyunasolavez, seusalafunciónuniqueKeys seusalafunciónuniqueKeys().Veamosunejemplo ().Veamosunejemploparailustrarlo. parailustrarlo. QMultiMap
42
43
TEMA11 TIPOSQt
Comoen moenelte eltema maan ante terriorhemo rhemosv sviisto,de to,debi bido do aqueel aqueel pro propós pósitodeQt todeQtes es pode poderrser multiplataformasintenerquemodific multiplataformasintenerquemodificarelcódigo,otrac arelcódigo,otracosaquehuboquemodifi osaquehuboquemodificarsonlostipos carsonlostipos comolosenteros,quedependendels comolosenteros,quedependendelsistemaoperativo istemaoperativo,ydelaCPU. ,ydelaCPU. Así Asílo los sti tipo pos squ qued edan anf fij ijad ados osd de ees esta tam man aner era aen enQ QtG tGlo loba bal: l: qint8------>8bitsentero qint16------>16bitsentero qint32------>32bitsentero qint64------>64bitsentero quint8------>8bitsenterosinsigno quint16------>16bitsen quint16------>16bitsenterosinsi terosinsigno gno quint32------>32bitsen quint32------>32bitsenterosinsi terosinsigno gno quint64------>64bitsen quint64------>64bitsenterosinsi terosinsigno gno qreal------>double
QVariant Esun Esunti tipo poq que uepu pued edeg egua uard rdar arun unt tip ipo ocu cual alqu quie iera ra.S .Sin inec eces esit itam amos oss ser erca capa pace cesd sded edev evol olve ver r cual cualqu quie ier r tipo tipo,, a trav través és de un inte interf rfac ace e QVar QVaria iant nt nos nos pued puede e ayud ayudar ar,, y fina finamen mente te pode podemo mos s convertirloasutipofinalmediantelasfuncionesdeltipotoTipo()(ej.toInt(),toFloat()).Veamos unejemplo. QVariant var(5 var( 5); int i; i=var.toInt(); // 5
Castingdinámico Para hacer casting dinámico entre clases, pero sin requerir el uso del RTTI del compilador,enQtexisteeloperado compilador,enQtexisteeloperadorenQtGlobal renQtGloballlamado llamadoqobject_casting . Sino Sinote tene nemo mosc scla laro ro,s ,sie ielc lcas asti ting ngse seva vaare areso solv lver erbi bien en,e ,ent nton once cesu susa sare remo mose sest steti etipo pode de casting,yaquedenoresolv casting,yaquedenoresolversebien,devo ersebien,devolveráunpuntero lveráunpunteronulo. nulo. QObject *obj = new QTimer QTimer; ;
// QTimer hereda de QObject
QTimer *timer = qobject_cast< QTimer *>(obj); // timer == (QObject *)obj QAbstractButton *button = qobject_cast< QAbstractButton *>(obj); // button == 0
44
45
TEMA12 SISTEMASDEFICHEROS
Otro Otrodelo delossi ssiti tios osdon donde deest estámu ámuyj yjus usti tifi fica cado doelus elusode odeunsi unsist stem emain ainde depe pend ndie ient ntede edela la plataform plataformaesen aesen losaccesosasistemas losaccesosasistemasde de ficheros ficheros,yaqueen ,yaqueen cadaoperativ cadaoperativo,estoesdifer o,estoesdiferente. ente. De esta esta mane manera ra surg surge e una una seri serie e de clas clases es en Qt que que nos nos ayud ayudan an a abst abstra raer erno nos s de dich dichas as particularidades.
QDir Esta Estacla clase senos nospro prove veede edelma lmane nejo jodel delsis siste tema madefi defich cher eros osdema demane nera ratra trans nspa pare rent nte. e.Los Los directoriosmástípicos directoriosmástípicosestándefinido estándefinidoscomométodos scomométodosestáticos. estáticos. QDir dir = QDir QDir::current(); ::current(); // directorio actual QDir dir = QDir QDir::home(); ::home(); // /home QDir dir = QDir QDir::temp(); ::temp(); // directorio temporal QDir dir = QDir QDir::root(); ::root(); // directorio raiz QDir dir = QDir QDir( (QApplication ::applicationDirPath()); // directorio de la aplicacion
QFileInf QFileInfo o es una clase que puede contenertoda contenertoda la informac informaciónreferent iónreferente e a un fichero fichero (nombre,path,permisos,etc).QFileInfo (nombre,path,permiso s,etc).QFileInfoListesunaQList
Se podrí podría a hacer hacer un filtr filtrado ado de dic dicho ho listad listado o usando usando los sigui siguient entes es filtr filtros os,, puesto puestos s y combinadosconOR(|)como combinadosconOR(|)comoprimerparámetrodeen primerparámetrodeentryInfoLi tryInfoList(filter,so st(filter,sort): rt): QDir::Dirs
directorios
QDir:: Files
ficheros
QDir::NoSymLinks
no vínculos
QDir::Readable
con premiso de lectura
QDir::Writeable
con permiso de escritura
QDir::Executable
con permiso de ejecución
QDir::Hidden
ficheros ocultos
QDir::System
ficheros de sistema
Elordendelistadosepuedeenviarcomosegundoparámetroyestossonlosfiltrosde ordenación: QDir::Name
por nombre
QDir::Type
por tipo (extensión)
QDir::Size
por tamaño
46
QDir::Time
por fecha de creación
QDir::DirsFirst
directorios primero
QDir::DirsLast
directorios lo último
QDir::Reversed
en orden inverso
EntryInf EntryInfoLis oList t también también tiene tiene un construc constructor tor donde delante delante de estos estos parámetro parámetros s vistos vistos antes,selepuedeañadirunparámetroqueiríaantesdequeestosconunaQStringLi antes,selepuedeañadirunparámetroqueiríaantes dequeestosconunaQStringListconfiltros stconfiltros wildcard(deltipo*.h,*.cpp,etc) wildcard(deltipo*.h,*.cpp,etc).Veamosunejemplo: .Veamosunejemplo: QFileInfoList infos = QDir QDir::root().entryInfoList( ::root().entryInfoList( QStringList << "*.h" << "*.cpp", "*.cpp" ,QDir QDir::Files, ::Files, QDir QDir::Name); ::Name); foreach( foreach (const QFileInfo &info, infos){ qDebug("%s" qDebug( "%s",qPrintable(info.fileName())); ,qPrintable(info.fileName())); }
Lasdistintas Lasdistintas partesdel partesdel pathdeun fichero fichero están gráficam gráficamenterelat enterelatadasenel adasenel siguient siguiente e gráfico.
47
TEMA13 ESCRITURAYLECTURADEFICHEROS
Paral Paralal alect ectura uray yesc escri ritur turae aenf nfic icher heros os,t ,tene enemos mosqu quea ealm lmeno enosi simpl mplic icar ar3c 3clas lases esdif difere erent ntes, es, tantoenelcasodequeseaunficherobinario,comosiesdetexto.Bienvamosavercadaunade esasclases,ylamaneraenqueserelac esasclases,ylamaneraenqueserelacionanenelpro ionanenelprocesodelecturay cesodelecturayescritura. escritura. La clase QFilequederivadeQIODevice ,eslaqueseusaparalaoperacióndeapertura, escritura,lecturaycierredelar escritura,lecturaycierredelarchivo.Los chivo.Losflagsquerepresent flagsquerepresentanlosdiferen anlosdiferentesmodosdeaccesoal tesmodosdeaccesoal ficheroestándefinidosc ficheroestándefinidoscomounenumenQI omounenumenQIODevice,yso ODevice,ysonlossiguien nlossiguientes: tes: 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, los enter los pasa al sistema local
DelaclaseQIODevic DelaclaseQIODevicevamosausarmera evamosausarmeramente mentelosmodos losmodosdeaccesoalosfich deaccesoalosficheros eros,yde ,yde laclaseQFile,vamosausarlosmétodosopen()par laclaseQFile,vamosausarlosmétodosopen()paralaaperturadelfichero, alaaperturadelfichero,close()paraelcierre, close()paraelcierre, write()paralaescrituraenelmismo,yread()ysimilaresparalalectura.Tambiénvamosausar métodoscomoatEn métodoscomoatEnd()pararecon d()parareconocer ocerelEOF(finaldelfich elEOF(finaldelfichero), ero),o o seek()parasituarn seek()parasituarnosenuna osenuna posiciónconcretadelfi posiciónconcretadelfichero.Veamosun chero.Veamosunejemplodelectura: ejemplodelectura:
QFile f("fichero.txt" f("fichero.txt" ); if(!f.open( if (!f.open( QIODevice ::ReadOnly)) qFatal("No qFatal( "No puedo abrir el fichero para lectura." ); while(!f.atEnd()){ while(!f.atEnd()){ QByteArray data = f.read(1024 f.read( 1024); ); // Aqui puedes usar los datos leidos } f.close();
Esto Estomis mismo mo pued puedese eserhe rhech chode ode unama unamane nera ra másin másintu tuit itiv ivaus ausan ando do stre stream ams. s. Lascl Lasclas ases es inter interfac face,quese e,quese encarg encargande ande crear crear unstreamconlosdatos unstreamconlosdatos que proced procedeno eno van alfichero alfichero en nuestr nuestro o caso, caso, aunque aunque tambi también én se puede puede usar usar con con cualqu cualquier ier otro otro dispos dispositi itivo vo.. Estas Estas clases clases son QTextStream paraflujosdetexto,yQDataStream paraflujosdedatosbinarios.Deestasclases vamo amos a usar usar sol solamen amentte el constr nstruc ucttor, or, indica dican ndo como pará paráme mettro el disp dispo ositi sitiv vo de entrada/salida. Veamosunejemplodelecturadefic Veamosunejemplodelecturadeficherodetextousando herodetextousandounstream: unstream: QFile f("fichero.txt" f("fichero.txt" ); QTextStream in(&f); if(!f.open( if (!f.open( QIODevice ::ReadOnly) qFatal("No qFatal( "No puedo abrir el fichero para lectura." ); while(!in.atEnd()){ while (!in.atEnd()){ qDebug("%s" qDebug( "%s",qPrintable(in.readLine())); ,qPrintable(in.readLine())); } f.close();
48
Veamosahoraunejemplodeescritur Veamosahoraunejemplodeescrituradeficherode adeficherodetexto: texto: QFile f("fichero.txt" f("fichero.txt" ); QTextStream out(&f); if(!f.open( if (!f.open( QIODevice ::WriteOnly | QIODevice QIODevice::Append) ::Append) qFatal("No qFatal( "No puedo abrir el fichero para escritura." ); out << "Esto es un fichero de texto" << str << endl; f.close();
En elcaso deleero escrib escribirdato irdatos,debem s,debemosdeci osdeciral ral compil compilado ador r deQt laversión laversión que estamosusandodelmismo,yaqueesposiblequeendiferentesversiones,laimplementacióndel datastreamseadiferente.Veamosunejemplo datastreamseadiferente.Veamosunejemplodeescritura. deescritura. QFile f("fichero.bin" f("fichero.bin" ); QDataStream out(&f); out.setVersion( QDataStream ::Qt_4_6 ::Qt_4_6); ); if(!f.open( if (!f.open( QIODevice ::WriteOnly) qFatal("No qFatal( "No puedo abrir el fichero para escritura" ); out << 0xFFFC 0xFFFC; ; f.close();
StreamsyTipos Sepuedeserializaraunfichero Sepuedeserializaraunficherounaclasecualqui unaclasecualquiercreadapornoso ercreadapornosotros,simplement tros,simplementesi esi enlamismaimplementamoslosoperadores enlamismaimplement amoslosoperadores<<y>>.Vamosaver <<y>>.Vamosaverunejemplodenuevo unejemplodenuevoconla conla clasePersonaysusmiembrosedad()ynombre(). QDataStream Persona::& operator operator<< << (QDataStream &out, const Persona &p) { out << p.nombre(); out << p.edad(); return out; } QDataStream Persona::& operator operator>> >> (QDataStream &in, const Persona &p) { QString nombre; int edad; in >> nombre; in >> edad; p = Persona(nombre, edad); return in; }
49
TEMA14 WIDGETSYLAYOUTS
Losprimeros temas, temas, del 1 al 13, contenía contenían n los principi principios os básicosque básicosque diferenc diferenciana iana la prog progra rama maci ción ón C++/ C++/ST STL L de la prog progra rama maci ción ón C++/ C++/Qt Qt.. Es fund fundam amen enta tal l que que teng tenga a todo todos s esos esos conceptosclaros,yquehayahechotodoslo conceptosclaros,yquehayahechotodoslosejemplos,yejerci sejemplos,yejerciciosposiblesparaaf ciosposiblesparaafianzarlosbien ianzarlosbien.. Siestonoesasí,eselmomentodequesepare,yvuelvasobrelos13temasqueacabamosde pasar. Des Desde el temaac emaactu tual al,ha ,hast stael ael tema tema 20 inclui cluido do,va ,vamo mosave saverr losco losconc ncep epto tosbá sbási siccos y todaslasherramientasnecesariasparadesarrollaruninterfazgráficocompleto.Esfundamental portantoquetrateestebloquedetemascomountodo,yqueseejerciteenellosantesdeseguir adelante.Desdeeltema21enadelante,yasetratanparticularidadesyampli adelante.Desdeeltema21enadelante,yasetr atanparticularidadesyampliaciones,queco aciones,queconuna nuna basesólidaenlos2bloquesanteriores(1al13y14al20),podráavanzarporellosdemanera segurayfiable. Un inter terfaz gráf gráfiico mues muestr tra a al usu usuari ario venta entan nas con elem elemen ento tos s gráf gráfiicos cos con con los los que que puedeinteractuar,seleccionandoopciones,introduciendotexto,pulsandoenbotones,etc.Toda aplicaci aplicacióntieneunavent óntieneunaventanaprinc anaprincipal, ipal,quees quees laqueseabreal principi principioy oy semantienehastael semantienehastael final,cuando final,cuandoel el usuariodecidecerra usuariodecidecerrarla rla aplicació aplicación.A n.A parte, pueden haberventanas haberventanasde de Diálogo, Diálogo, que se superp superpong ongan an a la princi principal pal en el moment momento o en que pid piden en alguna alguna acción acción por parte parte del usuari usuario. o. Cualqu Cualquier iera a de estas estas ventan ventanas, as, esta esta compue compuesta sta a su vez por unos unos elemen elementos tos gráfi gráfico cos s básicosquedividiremosen básicosquedividiremosen2tipos:lo 2tipos:loswidgetsylo swidgetsyloslayouts. slayouts. Loswidg swidget etsso sson n elem elemen ento tosgr sgráf áfiicosint sinterac eracti tiv voso no,que ,que se dib dibujan ujan den dentro tro de una una ventanaocupandounárearectangular,comounbotón,unaetiqueta,uncuadrodeselección,etc. UnLayoutesuncomponentequeseusaparadisponerespacialmenteloswidgetsdentrodela ventana,es ventana,es decir, decir, para organiza organizar r su posición posición en ella. Layouts Layouts y widgets widgets negocian negocian el espacio espacio a ocupar.EnQtseusanunoswidgetsllamadosspacers(verticalyhorizontal)queactúancomo muelle muelles, s, empuja empujando ndo los widge widgets ts hacia hacia una zona zona de la ventan ventana, a, con todo todo esto esto se consig consigue ue un interfazelástico,queseadaptaalasdimensionesquesequieradaralaventanaqueloscontiene, y es muy eficaz eficaz en el caso de querer querer internac internaciona ionaliza lizar r un interfaz interfaz,, traducién traduciéndolo dolo a diferente diferentes s idio idioma mas, s, dond donde e los los text textos os,, mide miden n dife difere rent nte e en cada cada idio idioma ma,, y dond donde e no quer querem emos os que que se trunquennitextos,nig trunquennitextos,nigráficos. ráficos.
Loswidgets Ene Enel lQt QtD Des esig igne ner rno nos spo pode demo mos sen enco cont ntra rar rmá más sde de4 45 5wi widg dget ets spa para rau usa sar, r,y yc cer erca cad de e60 60s son on derivados derivados de la clase clase principal principal QWidget .. Los widget widgets s están están organ organiza izados dos en catego categorí rías, as, y una categoría categoría important importante e sonlos widgets widgets contenedo contenedores, res, que pueden conteneren conteneren su interio interior r otros otros widgets,demaneraquesirv widgets,demaneraquesirvenparaorgani enparaorganizarlo zarlos,yademáspuedenactuar s,yademáspuedenactuaren en suinteractiv suinteractividad, idad, como porejemplo,cuandometemosvario porejemplo,cuandometemosvariosradio-bu sradio-button ttons s (QRadioBu (QRadioButton tton)dentrode )dentrode group-bo group-box x (QGr (QGrou oupB pBox ox), ), cons conseg egui uimo mos s que que al sele selecc ccio iona nar r uno uno de ello ellos, s, los los demá demás s se dese desele lecc ccio ione nen n automá automátic ticame amente nte,, por lo que se puede puede decir decir que los widge widgets ts conten contenedo edores res pueden pueden afecta afectar r y modificarlainteractivi modificarlainteractividaddeunwidgetco daddeunwidgetcontenidoenél. ntenidoenél. Otra Otrapro propi pied edad adimp impor orta tant ntede edelos loswid widge gets ts,es ,esque queson soncap capac aces esdere dereci cibi birev reven ento tosde sdesd sde e losdispositivosdeentrada(porejemploelratón).Cuandoporcausadeunevento,oporotra causa,elestadodelwidgetescambiado,emiteunaseñalnotificandotalcambio.Loswidgetspor tantopuedenconectarseconotroswidgetsyobjetos,medianteelmecanismodesignal-slotque vimosenlosQObjects,y vimosenlosQObjects,yaqueQWidgetderiv aqueQWidgetderivadeaquél. adeaquél.
50
LosLayouts Los Los obje objeto tos s layo layout ut deri deriva van n de la clas clase e QLay QLayou out. t. Hay 3 tipo tipos s dife difere rent ntes es de layo layout uts: s: QHBoxLayout(queorganizaelespacioqueocupanloswidgetshorizontalmente),QVBoxLayout( queorganizaelespacioqueocupanloswidgetsverticamente)yQGridLayout(queorganizael espaci espacio o que ocupa ocupan n los widget widgets, s, sobre sobre un grid, grid, dividi dividiend endo o el espaci espacio o en filas filas y column columnas, as, y situandoacadawidgetenlaceldaquelecorresponda).Losspacers,rellenanlosespaciosvacíos, y de esta esta manera manera,, layout layouts s y widget widgets, s, negoci negocian an su tamaño tamaño y el espaci espacio o para para crear crear un inter interfaz faz elásticoadaptableatodaslassituaciones. Un Layo Layout ut,pue ,puede de cont conten ener er en su inte interi rior or otro otrosLa sLayo yout utsyotr syotros os Widg Widget ets. s. AsíQL AsíQLay ayou out t posee unas funcione funciones s miembroespecia miembroespecialespara lespara añadirestosconteni añadirestoscontenidosa dosa suinterior,y suinterior,y cuando cuando esta estas s funci uncion ones es son son usad usadas as, , inme inmedi diat atam amen ente te dich dicho o cont conten enid ido o pasa pasa a ser ser hijo hijo de la clas clase e contenedora.Estemecanismofac contenedora.Estemecanismofacilitaentonc ilitaentonceslatransmisió eslatransmisióndeeventosypr ndeeventosypropiedadesalolargo opiedadesalolargo de la jera jerarq rquí uía a fami famili liar ar,, dánd dándol ole e más más cons consis iste tenc ncia ia al inte interf rfaz az gráf gráfic ico o crea creado do por por obje objeto tos s continen continentesy tesy objetoscont objetoscontenido enidos.También s.También facilita facilita elmecanismode elmecanismode liberaci liberaciónde ónde memoriaque memoriaque vimos vimos en el tema tema 6, evitan evitando do fugas fugas de memori memoria. a. Veamos Veamos las 3 funci funcione ones s básica básicas s para para añadir añadir elementosaunLayout: void QBoxLayout::addStretch ( int stretch= stretch= 0 )
Añad Añade e un spac spacer er que que ocup ocupar ará á por por defe defect cto o como como míni mínimo mo 0 pixe pixele les, s, hast hasta a el lími límite te de su cont conten ened edor or,, empu empuja jand ndo o al widg widget et en esa esa dire direcc cció ión n (la (la del del cont conten ened edor or). ). Sólo Sólo BoxL BoxLay ayou outs ts verticalesyhorizontales verticalesyhorizontales(novaleparaGri (novaleparaGridLayouts). dLayouts). void QLayout::addWidget ( QWidget *w )
AñadeunwidgetdentrodelLayoutdelti AñadeunwidgetdentrodelLayoutdeltipoqueseaéste.Desdees poqueseaéste.Desdeesemomentodichow emomentodichowidgetpasaa idgetpasaa serhijodelLayoutquelocontiene. void QLayout::addLayout ( QLayout *layout , int stretch = 0 )
Añad Añade e un Layo Layout ut dent dentro ro de otro otro (caj (cajas as pequ pequeñ eñas as dent dentro ro de caja cajas s más más gran grande des) s).. Desd Desde e ese ese momentodicholayoutpasaráaserhijodesucontenedor,yporherenciaasuvezdelascosas queaquélcontenga. Dehec Dehecho ho,en ,entod todav aven enta tana nagr gráf áfic ica, a,sepu sepued ededi edibu buja jarun runárb árbol oljer jerár árqu quic icoq oque uerec recre reela elas s relacione relacionesparenta sparentalesdetodossusobjeto lesdetodossusobjetos.Porlotanto,todoelconten s.Porlotanto,todoelcontenidodeunaventana idodeunaventanadebe debe estarsometidojerárquicamentealobjetoquerepresentalaventanacompleta.Asícuandodicha ventan ventana a sea borra borrada da de la memori memoria, a, con ella ella se borrar borrarán án todos todos sus elemen elementos tos,, evitan evitando do así prob proble lema mas s de memo memory ry leak leaks. s. Es muy muy habi habitu tual al por por tant tanto o que que los los elem elemen ento tos s gráf gráfic icos os de una una ventanaseancreadosmediantenewdent ventanaseancreadosmediantenewdentrodelconstruc rodelconstructordelobjeto tordelobjetoqueharádeventana. queharádeventana. Pues Puesto to quehay quehay much mucho os widg widget etss,y sus pro propied piedad ades es son much muchas as,y ,y sus méto método dosso sson n muchos,noespropósitodeestelibroelverlostodos.Portanto,esconvenientequeconformelos vayas ayas nec necesi esitando ando,, los vayas ayas rev revisan sando en el Qt Assi Assisstan tant, y apr aprendi endien endo do a usar sar esta esta documentacióncomoguíaalaprogramación.Elautorrellen documentacióncomoguíaalaprogramación.ElautorrellenadodelQtCreator,facilitatambién adodelQtCreator,facilitatambiénla la labordeencontrarfácilmen labordeencontrarfácilmenteloselementosde teloselementosdelaprogramación laprogramaciónquerequerimos querequerimos. . Vamosportantoacontinuaciónaplantearnoslaconstruccióndeuncuadrodediálogo, primer primero o en códig código o puro, puro, y luego luego en el Qt Desig Designer ner.. De esta esta manera manera dejare dejaremos mos concre concretad tada a la enseña enseñanza nza que preten pretendía díamos mos para para este este tema,que tema,que es el diseño diseño de ventan ventanas as usando usando Layout Layouts s y Widgets.
51
ConstruccióndeunaventanadeDiálogoconcódigo Vamo Vamos sa aco cons nstr trui uir run una ave vent ntan ana ade ded diá iálo logo goq que uet ten enga gae el las aspe pect cto ode del laf afot oto oqu que emo most stra ramo mos s acontinuación.
Pode Podemo moso sobs bser erva varq rque uepr prim imer eram amen ente tehay hay3ca 3caja jash shor oriz izon onta tale les( s(re rect ctán ángu gulo losr sroj ojos os), ),qu que e soncontenedoresLayoutqueensuinteriortienenwidgetsospacers(stretch).Vamosasuponer quelaconstrucc quelaconstruccióndetodoelloeshecho ióndetodoelloeshechoen en elconstruct elconstructordelaventanadediálog ordelaventanadediálogo,porloque o,porloque loscontendoressuperiores,queson3,tomaráncomopadrealaventanayportantousaremosel punterothiscomoargumentode punterothiscomoargumentodelosconstruc losconstructoresdelosmis toresdelosmismos. mos. QHBoxLayout *topLayout = new QHBoxLayout (this this); ); QHBoxLayout *groupLayout = new QHBoxLayout (this this); ); QHBoxLayout *buttonsLayout = new QHBoxLayout (this this); );
Empe Empece cemo mos spo por rel elt top opLa Layo yout utd don onde deh hay ayu un nQL QLab abel elc con one el lte text xto oPr Prin inte ter ry yun unQ QCo Comb mboB oBox ox,, alqueluegoqueremosañadirleítemsdedo alqueluegoqueremosañadirleítemsdedondeseleccionar ndeseleccionaropciones.Por opciones.Porloqueañadimosdeb loqueañadimosdebajo ajo delaprimeralíneadelcódigodeantes: QComboBox *c; QHBoxLayout *topLayout = new QHBoxLayout (this this); ); topLayout->addWidget( new QLabel QLabel( ("Printer:" )); topLayout->addWidget(c= new QComboBox ());
Dat Datecuen ecuenttade quela quela Labe Labell sólo lahemo ahemosin sinsstan tanciad ciado o,pero ,pero no la hemos emos asi asignad gnadoa oa ningunavariable,yaquenorequerimoshacerlenadaenelcódigomásadelante,norequerimos accedera accedera ningunavaria ningunavariablequelamaneje,mientr blequelamaneje,mientrasquela asquela ComboBox ComboBox sílahemosasignadoa sílahemosasignadoa la variablec,paramásadelantepoderañadir variablec,paramásadelantepoderañadirleítemsdeselecci leítemsdeselecciónmediantedicha ónmediantedichavariable. variable. Ahor Ahorav avam amos osapo aporel relbut butto tons nsLa Layo yout ut,d ,don onde dete tene nemo mosq sque ueco colo loca caru runs nspa pace cerp rpri rime mero ro,y ,y luegolos2botones. QHBoxLayout *buttonsLayout = new QHBoxLayout (this this); ); buttonsLayout->addStretch(); buttonsLayout->addWidget( new QPushButton ("Print" "Print")); )); buttonsLayout->addWidget( new QPushButton ("Cancel" "Cancel")); ));
Vamo Vamos sah ahor oraa aapo por rla laca caja jace cent ntra ralq lque uees esla lamá másc scom ompl plej ejad adee eest stee eeje jemp mplo lo.P .Pri rime mera rame ment nte e vamosaañadirleagroupLayoutlo vamosaañadirleagroupLayoutlos2GroupBoxes s2GroupBoxes.. QHBoxLayout *groupLayout = new QHBoxLayout (this this); ); QGroupBox *orientationGroup = new QGroupBox (); groupLayout->addWidget(orientationGroup); QGroupBox *colorGroup = new QGroupBox (); groupLayout->addWidget(colorGroup);
52
Fíja Fíjate tequ quea eaqu quíh íhem emos osi ins nsta tanc ncia iado dopr prim imer erol olos osGr Grou oupB pBox oxes esy ylu lueg egol olos oshe hemo mosa saña ñadi dido doal al Layout.Bien,ahoravamosausarunLayoutverticaldentrodecadaGrupoBoxparaorganizarel espaciodeloswidgetsquevanai espaciodeloswidgetsquevanairdentro. rdentro. QGroupBox *orientationGroup = new QGroupBox (); QVBoxLayout *orientationLayout = new QVBoxLayout (orientationGroup); // nueva groupLayout->addWidget(orientationGroup); QGroupBox *colorGroup = new QGroupBox (); QVBoxLayout *colorLayout = new QVBoxLayout (colorGroup); // nueva groupLayout->addWidget(colorGroup);
Ahor Ahora ava vamo mos sa aañ añad adir irle lea ac cad ada aVe Vert rtia ialL lLay ayou out tlo los swi widg dget ets squ que ele lec cor orre resp spon onde den. n.Po Por rlo loq que ue elcódigocompletoreferente elcódigocompletoreferentealLayoutdelmedio alLayoutdelmediosequedaasí. sequedaasí. QHBoxLayout *groupLayout = new QHBoxLayout (this this); ); QGroupBox *orientationGroup = new QGroupBox (); QVBoxLayout *orientationLayout = new QVBoxLayout (orientationGroup); orientationLayout->addWidget( new QRadioButton ("Landscape" )); orientationLayout->addWidget( new QRadioButton ("Portrait" )); groupLayout->addWidget(orientationGroup); QGroupBox *colorGroup = new QGroupBox (); QVBoxLayout *colorLayout = new QVBoxLayout (colorGroup); colorLayout->addWidget( new QRadioButton ("Black and White" White")); )); colorLayout->addWidget( new QRadioButton ("Color" "Color")); )); groupLayout->addWidget(colorGroup);
Siteha Sitehasda sdado docue cuent nta, a,no nohem hemos ospue puest stoaú oaúnel nelspa space cerv rver erti tica calqu lqueha ehayen yentr treel eelLay Layou out t centralyelinferior(elde centralyelinferior(eldelosbotones). losbotones).Parahaceresousando ParahaceresousandolafunciónaddS lafunciónaddStretch,tenemosque tretch,tenemosque referi referirno rnos s a una Verti Vertical calLay Layout out,, por lo que vamos vamos a tener tener que conten contenerl erlo o todo todo dentro dentro de un Layo Layout ut más más gran grande de.. Como Como este este QVBo QVBoxL xLay ayou out, t, cont conten ened edor or de todo todo,, serí sería a el padre padre de todo todo,, entoncestendríamosquereferirlos3Layoutsinicialesaéste,yéstepasaríaaserelmáscercano enjerarquíaalaventanadeDiálog enjerarquíaalaventanadeDiálogo.Yasíquedarí o.Yasíquedaríatodo: atodo: QStringList options; options << "Office Printer" << "HP LaserJet" << "Canon OfficeJet" ; QComboBox *c; QVBoxLayout *outerLayout = new QVBoxLayout (this this); ); // Layout superior QHBoxLayout *topLayout = new QHBoxLayout (); topLayout->addWidget( new QLabel QLabel( ("Printer:" )); topLayout->addWidget(c= new QComboBox ()); // Fin Layout superior outerLayout->addLayout(topLayout); // Layout Central QHBoxLayout *groupLayout = new QHBoxLayout (); QGroupBox *orientationGroup = new QGroupBox( "Page Orientation" ); QVBoxLayout *orientationLayout = new QVBoxLayout (orientationGroup); orientationLayout->addWidget( new QRadioButton ("Landscape" )); orientationLayout->addWidget( new QRadioButton ("Portrait" )); groupLayout->addWidget(orientationGroup); QGroupBox *colorGroup = new QGroupBox (("Coloroptions" ); QVBoxLayout *colorLayout = new QVBoxLayout (colorGroup); colorLayout->addWidget( new QRadioButton ("Black and White" White")); )); colorLayout->addWidget( new QRadioButton ("Color" "Color")); )); groupLayout->addWidget(colorGroup); // Fin Layout Central outerLayout->addLayout(groupLayout); outerLayout->addStretch(); // Layout Inferior QHBoxLayout *buttonsLayout = new QHBoxLayout (); buttonsLayout->addStretch(); buttonsLayout->addWidget( new QPushButton ("Print" "Print")); )); buttonsLayout->addWidget( new QPushButton ("Cancel" "Cancel")); )); // Fin Layout Inferior outerLayout->addLayout(buttonsLayout);
c->addItems(options);
53
Hemo Hemoss ssan angr grad adol olos osbl bloq oque uesq sque ueya yate tení níam amos osco cons nstr trui uido dosp spar arad adej ejar arcl clar aroe oelc lcód ódig igoq oque ue hemos hemos añadid añadido, o, y el sentid sentido o del mismo mismo con respec respecto to a los bloque bloques s que contie contiene, ne, y el spacer spacer vertical.TambiénnoshemospermitidoellujodeañadirlosítemsdelComboBoxqueharánde opciones,medianteunaStringList.
Construccióndeunave ConstruccióndeunaventanadeDiál ntanadeDiálogoconQtD ogoconQtDesigner esigner Algunos pensarán que soy un masoquista, diseñando la ventana a tecla, pero es fundament fundamentalaprender alaprender antes elcódigo quehaydetrásdetodo,antesqueabando quehaydetrásdetodo,antesqueabandonarse narseal al placer placer delmeroypurodiseño,quenosseparadelaprogramaciónyportantodelentendimientodelas basesfundamentales. Vamos a crear un nuevo proyecto, donde elegiremos Qt C++ Project -> Qt GUI Applicati Application.Lepodemo on.Lepodemosllamaralproye sllamaralproyecto“dial cto“dialog”. og”.La La clasebasesellamará“Dial clasebasesellamará“Dialog”ylaclase og”ylaclase basevaaserQWidget. Unave Unavezse zsehan hancre cread adoto otodo doslo slosfi sfich cher eros os,ab ,abri rimo mos“d s“dia ialo log. g.ui ui”qu ”quese eseno nosab sabri rirá ráene enelQt lQt Design Designer. er. Vamos Vamos a ir dibuja dibujando ndo en él, el inter interfac face e del diseño diseño que estamo estamos s sigui siguiend endo. o. Iremos Iremos colo coloca cand ndo o prim primer ero o en la parte parte supe superi rior or,, lo mejo mejor r que que poda podamo mos, s, un Labe Label l y un Comb ComboB oBox ox,, arrastrándolodelasherramientasalformularioysoltándolo.Hacemosdobleclicsobreeltexto del del Labe Label l y pone ponemo mos s “Pri “Print nter er:” :”.. Lueg Luego o en el bloq bloque ue cent centra ral l vamo vamos s a situ situar ar prim primer ero o los los 2 GroupBoxunoalladodelotro.Lescambiamoslostextos,porlosquelescorresponden.Luego añadimosenlaparteinferior añadimosenlaparteinferiorlos2PushButton los2PushButtons,ylescambi s,ylescambiamossutexto. amossutexto. Ahor Ahorav avam amos osap apon oner erlo losR sRad adio ioBu Butt tton onse sens nsus usre resp spec ecti tivo vosG sGro roup upBo Boxe xes, s,y yle lesp spon onem emos osel el textoquelescorresponden.Ahorasituamoselspacerhorizontalyelvertical,conformealdibujo deldiseñoqueestamossiguiendo. Si te das cuenta, por mucho que lo intentes, no va a quedar todo lo alineado que querem queremos, os, por eso, eso, es el moment momento o de comen comenzar zar a crear crear los Layout Layouts s que organi organicen cen todo todo este este espacio.Estosehacesiemprededentrohaciafuera,empezaremosporagruparlosRadioButton, selecc seleccio ionan nando do los del grupo grupo izquie izquierdo rdo (ambos (ambos manten mantenien iendo do la tecla tecla Ctrl Ctrl pulsad pulsada, a, Cmd en los Mac),ylosagrupamospulsandoelbotónLayOutVertically.Apareceunacajarojaalrededorde ellosquelosalineaperfectamenteentresí.Vamosahacerlomismoconlosdeladerecha.Luego vamosaseleccionarelGroupBoxizqui vamosaseleccionarelGroupBoxizquierdo,ypulsamosLayOutVerti erdo,ypulsamosLayOutVertically,queloorgan cally,queloorganizaconsu izaconsu contenido,losradiobutto contenido,losradiobuttons.Hacemoslos ns.HacemoslosmismoconelG mismoconelGroupBoxdeladerech roupBoxdeladerecha. a. Lueg Luegose osele lecc ccio iona namo mosro srode dean ando do conel conelrat ratón óntod todoel oel bloq bloque uecen centr tral al ypuls ypulsam amos os en Lay Lay OutHorizontally.Luegohacemoslomismoconelgruposuperior(labelycombobox)yconel grupoinferior(spacerho grupoinferior(spacerhorizontalyl rizontalylos2botones). os2botones). Ahor Ahoran anos osqu qued edan anlo los3 s3gr grup upos osyel yelspa space cerv rver erti tica cal. l.Pu Puls lsam amos osso sobr breun eunaz azon onav avac acía íade del l formular formulario,paraselecci io,paraseleccionar onarloa loa élporcompleto,y élporcompleto,y terminamos terminamos pulsandoen pulsandoen LayOutVertically. LayOutVertically. Yasenoshaorganizadotodosloswi Yasenoshaorganizadotodosloswidgetsconloslay dgetsconloslayouts.Dehechopuedesverco outs.Dehechopuedesvercomoeldiseñose moeldiseñose adaptaflexiblementealtamañolibredelaventanasiarrastrasunodelosbordesdelformulario, cambiandosutamaño,veráscomo cambiandosutamaño,veráscomotodoseorgani todoseorganizaentamañoy zaentamañoyposiciónperf posiciónperfectamente. ectamente. Con esto damos por terminado el tema referente a los elementos de una ventana (widgetsylayouts),enelpróximotemavamosapasaraverlasventanasdediálogo,elemento indispensabledeunaaplicación indispensabledeunaaplicacióngráfica. gráfica. Sipo Sipor rcu curi rios osid idad adq qui uier eres esañ añad adir irl los osí íte tems msd del elC Com ombo boBo Box, x,a abr bre eel elc cód ódig igo ode de“ “di dial alog og.c .cpp pp” ”y y déjalodeestaforma: QStringList options; options << "Office Printer" << "HP LaserJet" << "Canon OfficeJet" ; ui->setupUi( this this); ); ui->comboBox->addItems(options);
54
Fíjatequehemosaccedidodesdeelobjetouiquerepresentaelinterfazdiseñadoenel fichero*.ui,alacombobox,porsunombre(objectName),yentoncesyapodemosllamarasus métodoslibremente.Yaveremoses métodoslibremente.Yaveremosestoconmásdetalle toconmásdetallemásadelante. másadelante.
55
TEMA15 VENTANASDEDIALOGO
Unwidget, Unwidget, quenotengapadreselellamatop-lev quenotengapadreselellamatop-levelwidget. elwidget.Un Un top-level top-levelpuedeusarel puedeusarel métodoshow()paramostrarseenpantalla,ypuedeportantoconstituirsecomoventana.Asíhay 3tiposdetop-levelWindows(ventanas): .-Cualquierwidgetsinpadreesconstituidoautomáticamentecomounaventana,una QWidget .-Cualquierwidgetsinpadreesconstituidoautomáticamentecomounaventana,una vent ventan ana a plan plana, a, como como la que que hemo hemos s crea creado do en el tema tema ante anteri rior or,, o como como la que que se gene genera raba ba simplementemostrandounQLabelenel simplementemostrandounQLabelenelejemplodeHolaMundo. ejemplodeHolaMundo. widget et espe especi cial al,, que que conf confor orma ma una una vent ventan ana a que que ademá además s devue devuelv lve e un valo valor r QDialog.- Es un widg siempretrascerrarse(OK,Cancel,etc…) .-Esotrowidgetespecial,queconfo l,queconformalaventan rmalaventanamáscompletayportantola amáscompletayportantola QMainWindow.-Esotrowidgetespecia principaldeunaaplicacióng principaldeunaaplicacióngráfica,con ráfica,conmenús,herramient menús,herramientas,statusbar,doc as,statusbar,dock,etc. k,etc. Puesto que ya hemos visto a los widgets funcionando como ventanas, y las MainWi MainWindo ndowslastrat wslastratare aremos mos afondo eneltema19,en este este tema tema vamosa vamosa tratar tratar decercalas decercalas ventanasdediálogo,descendientes ventanasdediálogo,descendientesdeQDialog. deQDialog. Toda ventana de Diálogo lleva una serie de widgets que la componen, pero lo fundament fundamentalesquellevabotone alesquellevabotonesparaaceptary/o sparaaceptary/orechazar rechazaruna una determina determinadaacción daacción,yque ,yque esa decisiónesdevueltaalprogramamedi decisiónesdevueltaalprogramamedianteunvalor. anteunvalor. Enest Enestec ecap apít ítul ulov ovam amos osave avere relc lcód ódig igoq oque ueco conf nfor ormaa maauna unave vent ntan anaDi aDiál álog ogop opar aras asuus uuso o dentrode dentrode unaaplicación unaaplicación mayor,perono mayor,perono vamos adesarrollarunejemplocomplet adesarrollarunejemplocompleto,ya o,ya quenos faltan faltan elemen elemento tos s que no debemo debemos s aún tocar tocar aquí aquí y que una vez conoc conocido idos s podrem podremos os uni unirlo rlos s todoseneltema20. Una Unave vent ntan ana ade depe pend ndie iend ndo, o,d de ela lai int nter erac acti tivi vida dad dqu que ebl bloq oque uea, a,p pue uede des ser er: : bloquea a ningu ninguna na ventan ventana a que le acompa acompañe, ñe, y permit permite e estand estando o ella ella abiert abierta, a, No Modal Modal.- Si no bloque interactuarconotrasvent interactuarconotrasventanastambiénabier anastambiénabiertas. tas. .-Sólobloquealaventanapadredela apadredela misma,nopudiendo misma,nopudiendointerac interacturar turarconella, conella, WindowModal.-Sólobloquealaventan hastaqueéstaescerrada,aunquepermitei hastaqueéstaescerrada,aunquepermiteinteractuarcon nteractuarconelresto. elresto.
ApplicationModal .-Bloqueaatodaslasventanasdelaaplicación,sólopudiendointeractuarcon ellamientrasestáabierta. Todave odavent ntan anade ade Diál Diálo ogo, hered eredade ade QDial Dialo og, y deb debellev ellevar ar boto otones par parasele aselecccio cionar opcionesypushbuttonsparaaceptarorechazarlaacción.Vamosacrearunaventanadediálogo conelQtDesigner,talcomo conelQtDesigner,talcomohemosaprendidoenel hemosaprendidoeneltemapasado,conesta temapasado,conestaforma: forma:
56
Así que esta vez el proyecto lo basaremos en la clase QDialog y le llamaremos SearchDialog.Unavezabiertoelfichero*.uienelQtDesigner,seleccionaelformulario,yala derecha,cambialapropiedadWindowTitleenQWidget,por“Search”paraquesalgaeltítuloque vemoseneldibujo.Constru vemoseneldibujo.Construyealgosimila yealgosimilaraesto: raesto:
Ypulsandoenelcuadroazulinferiorderechodelaseleccióndelformulario,hazeltamañolo mínimoquesepuedadealto,perohazloalmenoseldobledeanchoquedealto,demaneraque se pare parezc zca a al dibu dibujo jo orig origin inal al.. Camb Cambia ia el nomb nombre re del del QLin QLineE eEdi dit t por por “sea “searc rchT hTex ext” t” y el de los los radiobuttonspor“directi radiobuttonspor“directionForward”y onForward”y“directionB “directionBackward”respecti ackward”respectivamente. vamente. Unav Unavez ezhec hecho hoeld eldis iseñ eño, o,vam vamos osave avere relc lcód ódig igoq oque uelo loin inte tegr grar aráen áenlaf lafun unci cion onal alid idad adde de unaaplicaciónmáscompleja. Ene Enel lar arch chiv ivo ose sear arch chdi dial alog og.h .he en nel elc con onst stru ruct ctor orp pon ondr drem emos osu un ncó códi digo goc com omo oés éste te: : class SearchDialog : public QDialog { Q_OBJECT public: public : explicit SearchDialog( const QString &initialText, bool isBackward, QWidget *parent = 0); ~SearchDialog(); // los getters que recogerán las opciones bool isBackward() const const; ; const QString &searchText() const const; ; private : private: Ui::SearchDialog *ui; };
Hemo Hemosp spue uest stoa oarg rgum umen ento tosp spar arai aini nici cial aliz izar arel elte text xtod odeb ebús úsqu qued eda, a,po pore reje jemp mplo lo,y ,yas asíq íque ue seconserveentrelasbúsquedas.Tambiénseinicializadeigualmaneralaopcióndedirecciónde la búsq búsque ueda da. . Usar Usarem emos os 2 gett getter ers s para para reco recoge ger r el esta estado do de las las opci opcion ones es. . Vamo Vamos s a ver ver la implementacióndeestecódigoen implementacióndeestecódigoensearchdialog.c searchdialog.cpp. pp. SearchDialog::SearchDialog( const QString &initialText, bool isBackward, QWidget *parent) : QDialog QDialog(parent), (parent), ui( new Ui::SearchDialog) { ui->setupUi( this this); ); ui->searchText->setText(initialText); if(isBackward) if (isBackward) ui->directionBackward->setChecked( true true); ); else ui->directionForward->setChecked( true true); ); } bool SearchDialog::isBackward() const { return ui->directionBackward->isChecked(); } const QString &SearchDialog::searchText() const { return ui->searchText->text(); }
57
Ahoravamosavercomoseinteg Ahoravamosavercomoseintegraríaelcódi raríaelcódigodemostrarlav godemostrarlaventanaenelresto entanaenelrestodela dela aplicación. SearchDialog dlg(settings.value( "searchText" ).toString(),settings.value( "searchBackward" ).toBool(), this); this ); if(dlg.exec() if (dlg.exec() == QDialog QDialog::Accepted){ ::Accepted){ QString text = dlg.searchText(); bool backwards = dlg.isBackward(); ....................... }
Por defecto la ventana es modal, pero puede ser cambiado mediante el método setWindow setWindowModal Modality( ity(). ). Estábamos Estábamos acostumbr acostumbradosa adosa mostrar mostrar la ventana ventana con show() show() (no modal pordefecto),pero pordefecto),peroahoraalhacerloconexec( ahoraalhacerloconexec()(modalpordefec )(modalpordefecto),loquesedevolve to),loquesedevolveráunavez ráunavez cerrada,seráunvalorentero, cerrada,seráunvalorentero,queesunDialogCo queesunDialogCode. de. Constant
Value
QDialog::Accepted
1
QDialog::Rejected
0
VentanasdeDiálogoStandard Adem Además ásde depo pode derd rdiiseñ señarve arven ntan tanasanu asanues esttrogu rogusstoyne toynece cesi sida dade des, s,Qt Qtin inco corrpor poraun auna seriedeventanasquesonmuytípicasdelasGUIsdetodaslasplataformas.Enconcretoson6 tipos tipos más los subtip subtipos os de cada cada una de ellas. ellas. Vamos Vamos a verlas verlas desde desde la más senci sencilla lla a las más avanzada. ventana a de diálog diálogo o que provee provee automá automáti ticam cament ente e de un Label, Label, un 1.- QInputDialog QInputDialog.- .- Es una ventan LineEditounComboBox,ylosbotonesOKyCancel.Puederecoger:unInt,unDouble,untextoo un Itemde un combo combo box, y lo hacecon las funcion funciones es correspon correspondien dientes tes getInt(),getDoubl getInt(),getDouble(), e(), getText()ygetItem().Todasestasfuncionesdevuelvencomoúltimoparámetrounboolquedice si se elig eligió ió el botó botón n OK (tru (true) e),, o el Canc Cancel el (fal (false se). ). Vamo Vamos s a ver ver el códi código go ejem ejempl plo o de las las 4 posibilidades: EjemplocongetInt: bool ok; int i = QInputDialog ::getInt(this ::getInt(this, ,"Ejemplo con getInt" ,"Porcentaje:" , 25 25, , 0, 100 100, , 1, &ok);
EjemplocongetDouble: double d = QInputDialog ::getDouble( this this, ," Ejemplo con getDouble" , "Cantidad:" , 37.56 37.56, , -10000 10000, , 10000 10000, , 2, &ok);
EjemplocongetItem: QStringList items; items << "Primavera" << "Verano" << "Otoño" << "Invierno" ; QString item = item = QInputDialog ::getItem( this this, , " Ejemplo con getItem" , "Estación:" , items, 0, false false, , &ok); if (ok && !item.isEmpty()) itemLabel->setText(item);
EjemplocongetText: QString text = QInputDialog ::getText( this this, , "Ejemplo con getText" , "Usuario:" , QLineEdit ::Normal, QDir QDir::home().dirName(), ::home().dirName(), &ok); if (ok && !text.isEmpty()) textLabel->setText(text);
58
.-EsunaventanadeerrorqueproveedeunTextLabel oveedeunTextLabelyunCheckBox. yunCheckBox. 2.-QErrorMessage.-Esunaventanadeerrorquepr errorMessageDialog = new QErrorMessage (this this); ); errorMessageDialog->showMessage( "Esta ventana muestra un error. " "Si el checkbox se deja, volverá a mostrarse, " "si no se deja, ya no volverá a mostrarse. ");
Con Con show showMe Mess ssag age( e() ) pone ponemo mos s el text texto o del del erro error, r, auto automá máti tica came ment nte e apar aparec ecer erá á el chec checkb kbox ox dicien dic iendo do si quiere quiere que se muestr muestre e el error error de nuevo nuevo,, si decide decide que no, no, ya no volve volverá rá a salir salir,, aunqueseallamadaenelcódigolafun aunqueseallamadaenel códigolafunciónshowMes ciónshowMessage()denuevo. sage()denuevo. ventana a modal modal que pregun pregunta ta al usuari usuario o una pregun pregunta, ta, y recib recibe e una 3.-QMessageBox .- Esuna ventan respuesta.Lashayde4tipos: warning.- QMessageBox msgBox(QMessageBox msgBox(QMessageBox ::Warning, "warning" ,MESSAGE, 0, this this); ); msgBox.addButton( "Save &Again" &Again", , QMessageBox ::AcceptRole); msgBox.addButton( "&Continue" , QMessageBox ::RejectRole); if (msgBox.exec() == QMessageBox ::AcceptRole) // codigo de aceptacion else // codigo de rechazo
question .- QMessageBox ::StandardButton reply; reply = QMessageBox ::question( this this, ,"question" ,MESSAGE , QMessageBox ::Yes | QMessageBox ::No | QMessageBox ::Cancel); if (reply == QMessageBox ::Yes) // codigo de Yes else if (reply == QMessageBox ::No) // codigo de No else // codigo de Cancel
information.- QMessageBox ::StandardButton reply; reply = QMessageBox ::information( this this, ," informatio information" n", , MESSAGE); if (reply == QMessageBox ::Ok) // codigo de OK else // codigo de Escape (tecla escape) no todos los sistemas lo admiten
critical.- QMessageBox ::StandardButton reply; reply = QMessageBox ::critical( this this, ,"critical" ,MESSAGE, 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
Muestraelcuadrodeseleccióndecolor,ydevuelv lor,ydevuelveelcódigodelmi eelcódigodelmismo. smo. 4.-QColorDialog.-Muestraelcuadrodeseleccióndeco QColor color = QColorDialog ::getColor( Qt Qt::green, ::green, this this); ); // color inicial verde if (color.isValid()) { // codigo de uso de color }
59
5.-QFontDialog .-Paraelegirlafuente. bool ok; QFont font = QFontDialog ::getFont(&ok, this this); ); if (ok) { // codigo de uso de la fuente }
.-Parahaceroperacionesdeficherosodirec cherosodirectorios.Hay torios.Hayde4tipos: de4tipos: 6.-QFileDialog .-Parahaceroperacionesdefi getOpenFileName.QString selectedFilter; QString fileName = QFileDialog ::getOpenFileName( this this, , "getOpenFileName" , defaultFileName, "All Files (*);;Text Files (*.txt)" (*.txt)", , &selectedFilter, options); if (!fileName.isEmpty()) // codigo de apertura del fichero
getSaveFileName.QString selectedFilter; QString fileName = QFileDialog ::getSaveFileName( this this, , "getSaveFileName" , defaultFileName, "All Files (*);;Text Files (*.txt)" (*.txt)", , &selectedFilter, options); if (!fileName.isEmpty())// codigo de guardado del fichero
getOpenFileNames.QString selectedFilter; QStringList files = QFileDialog ::getOpenFileNames( this this, , "getOpenFileNames" , openFilesPath, "All Files (*);;Text Files (*.txt)" (*.txt)", , &selectedFilter, options); if (files.count()) { openFilesPath = files[0 files[ 0]; // codigo de apertura de ficheros }
getExistingDirectory.QFileDialog ::Options options = QFileDialog ::DontResolveSymlinks | QFileDialog ::ShowDirsOnly; QString directory = QFileDialog ::getExistingDirectory( this this, , "getExistingDirectory" , defaultDirName, options); if (!directory.isEmpty())// codigo de apertura de directorio
60
61
TEMA16 MODELO,VISTAYCONTROLADOR,INTRODUCCION
El primer primer lengua lenguaje je que por primer primera a vez introd introdujo ujo el paradi paradigma gma MVC (Model (Modelo, o, Vis Vista, ta, Controlador)fueSmalltalken1979enellaboratoriodeXerox.Actualmentemuchoslenguajesy frameworkslohanincorporadocomo frameworkslohanincorporadocomounaventajaaldesarrolloi unaventajaaldesarrolloinestimable,yporsupuesto nestimable,yporsupuestoQtno Qtno ibaasermenos. En la gran gran mayo mayorríade apl aplicac caciones nes,se acc accedeafu edeafuen ente tess de dat datos, quepu quepued eden en ser ser,un fichero, fichero,un un sistemadearchivo sistemadearchivos,unabasededatos,unstrea s,unabasededatos,unstreamde mde datos,etc.Luegoesos datos,etc.Luegoesosdatosse datosse mues muestr tran an al usua usuari rio o de dive divers rsas as form formas as,, y el usua usuari rio o inte intera ract ctúa úa con con ello ellos, s, modi modifi ficá cánd ndol olos os,, leyéndolosotratándolosdealgunamaneraylafuentedelosmismosesactualizadacondicha modificaciónotratamiento.ElparadigmaMVCseparaen3aspectosdiferentesdeestaactividad, porunladoestaelModelo,queestáencontactodirectoconlafuentededatos,demaneraquees quienrecogelosdatosdelamismaylosactualiza.Esosdatossonenviadosporelmodeloala Vista(view),queseencargadevisualizarlosdeunamaneraespecífica.Unmismomodelopuede alimentar alimentar varias varias vistas vistas diferent diferentes es y ambassincronizar ambassincronizar su contenid contenido o a la vez. Sobre Sobre la vista vista el usuari usuario o inter interact actúa, úa, y la misma misma puede puede provee proveer r de método métodos s para para enviar enviar modif modifica icaci cione ones s de los dato datos s al Mode Modelo lo (mod (model el), ), y este este a la fuen fuente te de dato datos, s, o exis existe ten n tamb tambié ién n obje objeto tos s espe especi cial ales es,, especiali especializado zadosen sen lainteracción lainteracción delusuario conlavista,queaquíno sellamaControlador sellamaControlador sino Delegado(delegate). Lav Laver erda dad des esq que uese setr trat atad adec econ once cept ptos osmu muy yab abst stra ract ctos os,y ,yc cre reo oqu quel elom omej ejor ores espo pone nerl rlos osa a funcionarparaverdeloqueestamoshablando.Supongaquequierepresentarenunaventanaen formadeárbolelcontenidocompletodeldirectoriodesuordenador,conficherosydirectorios. SindudaseríaalgodifícildehacermeramenteconQTreeWidgetyQDir(QFileInfo,etc),ysial final final lo lograra,seguro lograra,seguro que ocuparíabastante ocuparíabastantes s líneas líneas de código.Pues código.Pues bien, bien, el paradigma paradigma MVC viene viene a nuestr nuestroresca orescate, te, para para hacerde hacerde esta esta tareaalgo tareaalgo senci sencillo llo yconpococódigo, yconpococódigo, dehecho se podr podría ía hace hacer r con con sola solame ment nte e 4 líne líneas as de códi código go,, si no fuer fuera a porq porque ue nos nos gust gusta a camb cambia iar r unas unas determ determin inada adas s propie propiedad dades es de la vista, vista, pero pero en cualqu cualquier ier caso caso nova a pasar pasar de las 10 líneas líneas.. ¿Cómoesesoposible?. Qtpro Qtprove veede edeuna unaser serie iedevi devist stas assuf sufic icie ient ntes espar paralo aloque quecua cualq lqui uier erasu asuel elepr eprog ogra rama mar, r,y y tamb tambié ién n de los los mode modelo los s para para lo mism mismo, o, pero pero si esto esto no fuer fuera a así, así, serí sería a much mucho o más más senc sencil illo lo crearlos crearlos parapoder ponerlosa ponerlosa trabajarjuntos, trabajarjuntos, a hacer un programaespecífi programaespecífico co conun código código complejoypocoreutilizabl complejoypocoreutilizable.Cuandounprog e.Cuandounprogramadorseponeaescr ramadorseponeaescribirmuchocó ibirmuchocódigo,esporq digo,esporque ue sabequelovaarentabilizaren sabequelovaarentabilizareneltiempo,deloc eltiempo,delocontrario,s ontrario,seestáquieto. eestáquieto. Bien Bien,pu ,pues espar parall allev evar ar acabo acabo la apli aplica caci ción ón quehe quehemo mosco scome ment ntad ado, o, vamo vamosaus sausar arcom como o Vista Vista,, la clase clase QTreeV QTreeView iew,, que nos nos provee provee de un magníf magnífic ico o gráfi gráfico co en árbol árbol muy avanza avanzado do y parametrizable,ycomoModelo,vamosausarlaclaseQDirModel,queserelacionadirectamente conelsistemade conelsistemade archivos archivoscomosu comosu fuentededatos.Aunqueparez fuentededatos.Aunqueparezcamentir camentira,simplem a,simplementehay entehay quecombinarambasclases,ModeloyVistaparaquesurjalachispamágica,yesosehaceconel método setModel(modelname) ,queproveentodaslasvistas ,queproveentodaslasvistas.Deestamaneralavistaescargad .Deestamaneralavistaescargada a condatosqueconsigueelmodel condatosqueconsigueelmodeloparaella.Vamosapor oparaella.Vamosaporelcódigo. elcódigo. Esta Estave vezv zvam amos osac acre rear arun unpr proy oyec ecto tova vací cío( o(Em Empt ptyP yPro roje ject ct), ),al alqu quev evam amos osal alla lama mar“ r“mv mvc” c”.. Luegovamosaañadirleunficherofuente“main.cpp”yvamosaempezaraescribirdirectamente enél. #include
62
// Vamos a cambiar el aspecto de la vista view.setAnimated( false false); ); view.setIndentation( 20 20); ); view.setSortingEnabled( true true); ); view.setWindowTitle( "Dir View" View"); ); view.resize( 640 640, ,480 480); ); // vamos a mostrar la vista view.show(); return app.exec(); }
Esoestodoelcódigo,vamo Esoestodoelcódigo,vamosavercualesel savercualeselresultadofin resultadofinaldeesaspocaslíneas aldeesaspocaslíneas..
Efectiva Efectivamente mente es impresion impresionanteel anteel resultado resultado obtenido obtenido contan escueto escueto código.Con código.Con esto queda justificadosobradamenteelus justificadosobradamenteelusodelparadigmaMVCsi odelparadigmaMVCsiemprequeseaposibl emprequeseaposible. e. Ahor Ahoraq aque uehe heco cons nseg egui uido dotu tuat aten enci ción ón,e ,esh shor orad adeq eque uete tepr pres esen ente tela lasv svis ista tasy sylo losm smod odel elos os dequevasadisponerenQt,quecomo dequevasadisponerenQt,quecomoyaapuntamo yaapuntamosvanasersufici svanasersuficientes entesparalagranmayor paralagranmayoría ía deaplicacionesquevasaacometer.Siestonoesasí,enuncursomásavanzadopodrásaprender acreartuspropiosmodelos,otuspropiasvistas,peroesoyasesaledelpropósitodeesteprimer libro.
LasVistas LasvistasqueproporcionaQtsontodasderivadasdelaclase QAbstractItemView .Las vistasquedeellasederivanson vistasquedeellasederivansontrestipospri trestiposprincipalmente:QLi ncipalmente:QListView,QTableVi stView,QTableViewyQTreeView. ewyQTreeView. QLis QListV tVie iew, w,pr prop opor orci cion ona aun unav avis ista taun unid idim imen ensi sion onal al,,do dond nde eha hay ysó sólo lou una nac col olum umna na( (co colu lumn mn) ) ymuchasfilas(row).Disponedeunaseriedemétodosparacambiarlapresentacióndelosítems delamisma. QTab QTable leVi View ew,e ,esl slam amás ásus usad ada, a,so sobr bret etod odoe oenl nlap apre rese sent ntac ació iónd ndet etab abla lasd sdeb ebas ases esde deda dato tos. s. Tieneformabidimensionalconvariascolumnasyvariasfilas,ademásposeecabeceratantopara lascolumnascomoparalasfil lascolumnascomoparalasfilas,paraponersust as,paraponersustítulosenellas ítulosenellas..
63
QTre QTreeV eVie iew, w,e es sus usad ada aso sobr bre eto todo doe en nac acce ceso soa adi dire rect ctor orio ios, s,c com omo ohe hemo mos svi vist sto oen ene el lej ejem empl plo, o, y dispone dispone la informac información ión en forma forma de árbol. árbol. Además también también dispone dispone de cabecera cabecera horizont horizontal al (header),paraponertítulosalaspro (header),paraponertítulosalaspropiedadesdelosítems. piedadesdelosítems.
Losmodelos Losm Losmod odel elos osqu quep epro ropo porc rcio iona naQt Qtpa para raac acce cede dera ralo losd sdif ifer eren ente test stip ipos osde defu fuen ente tesd sded edat atos os,, deri deriva van n de QAbstramItemModel , y entr entre e los los que que prop propor orci cion ona a está están: n: QStr QStrin ingL gLis istM tMod odel el, , QStandardItemModel,QFileSystemModely QStandardItemModel,QFileSystemModelyQSqlRelation QSqlRelationalTableModel. alTableModel. QStringListModel ,esusadoparaguardarunalistadeQStrings. QStandardItemModel ,seusaparaelmanejodeestructurasmáscomplicadasenformade árbol,quepuedenguardarcualquiert árbol,quepuedenguardarcualquiertipodedatos. ipodedatos. QFileSystemModel ,proveedelainformaciónqueobtienedelsistemadeficheros,como ,peromásavanzado. QDirModel ,peromásavanzado.
paratratardatos rdatos proven provenien ientes tes deunabasede datos datos QSqlRelationalTableModel ,se usa paratrata relacional.DeelladerivaQSqlTableModel queseusaparaguardarlosdatosdeunatablaconcreta queseusaparaguardarlosdatosdeunatablaconcreta deunabasededatos.DeelladerivaQSqlQueryModel queguardalosdatosdelarespuestaauna QSqlQueryModel queguardalosdatosdelarespuestaauna queryaunabasededatos.
Observacionesfinales Como pued puedes es imagi magin nar, ar, comb ombinan nando unavi unavissta o varia ariass vistas stas con un mode modelo lo,pue ,puedo do obtenerelinterfazgráficoquenecesitoparainteractuarconlosdatosquequiero.Elcasomás típi típico co es con con una una base base de dato datos s rela relaci cion onal al,, como como MySQ MySQL L u Orac Oracle le.. Se trat tratar aría ía por por tant tanto o de combi combinar nar una QTable QTableVie View w con una QSqlTa QSqlTable bleMod Model el o una QSqlQu QSqlQuery eryMod Model. el. Veremo Veremos s casos casos concretosenlostemasqueco concretosenlostemasquecorrespondanabasesde rrespondanabasesdedatos. datos.
64
65
TEMA17 QLISTWIDGETYQTABLEWIDGET
Der Derivadas adas de QLi QListVi tView y QTab QTable leV View, iew, tenem enemos os 2 clas lases que repr epresen esenta tan n vistas stas en forma forma de lista lista y tablas tablas,, respec respecti tivam vament ente, e, con con un modelo modelo predef predefin inido ido.. Ambos Ambos son QWidge QWidgets ts prop propor orci cion onad ados os por por la barr barra a de herr herram amie ient ntas as del del Qt Desi Design gner er, , y suel suelen en ser ser usad usados os para para aplicacionessencillasdondenoseusanmodel aplicacionessencillasdondenoseusanmodeloscombinadosco oscombinadosconellas.Estánformados nellas.Estánformadosporítems porítems delasclasesQListWidgetItemyQTab delasclasesQListWidgetItemyQTableWidgetItem,respect leWidgetItem,respectivamente. ivamente.
QListWidget Par Paraañ aañad adiirele eleme men ntos tosa aes esttaLi aLisstah tahay ay 2f 2formas rmas:: 1.-Construyendolosítems 1.-Construyendolosítemsconestaclasec conestaclasecomopadre. omopadre. new QLIstWidgetItem ("Texto elemento 1" 1",listWidget); ,listWidget);
2.-Construyendoelitemsin 2.-Construyendoelitemsinpadreyañadiéndol padreyañadiéndoloalalistamást oalalistamástarde. arde. QListWidgetViewItem *newItem = new QListWidgetViewItem ; newItem->setText( "Texto Elemento 1" 1"); ); listWidget->insertItem(row,newItem);
En la segu segund nda a opci opción ón tamb tambié ién n se pued puede e usar usar el méto método do insetIntems(QStringList) , y tambiénaddItem(QString),queañadealfinaldelanterio ,queañadealfinaldelanterior,ynoesun r,ynoesunaposiciónex aposiciónexacta. acta. El modo modo de selec elecci ció ón de los los elem elemen ento toss en la lista sta se pued puede e leer leer con la funci unció ón puede e esta establ blec ecer er con con la func funció ión n setSelectionMode(selmode) . Los difere diferente ntes s selectMode() , y se pued modosdeselecciónson:Singl modosdeselecciónson:SingleSelection(unso eSelection(unsoloelemento),MultiS loelemento),MultiSelection(vari election(varioselementosala oselementosala vez), vez), Conti Contiguo guousS usSele electi ction ons s (vario (varios s elemen elementos tos contig contiguos uos a la vez) vez) y NoSel NoSelect ectio ion n (no se puede puede seleccionarnada). LarazóndeusarQListWidgetItemenvezdeQStringsenlainsercióndeelementos,es porque,éstepermitenosóloinsertartextoenlaslistas,sinotambiéniconos.Asítambienhay dosModosdevista:IconMode(drag&dropdesabilitadopordefecto)yListMode(drand&drop habilitadopordefecto).Tambiénsepuedecambiareltamañodeliconocon setIconSize(QSize) ,y elespaciadoconsetSpacing(value). Paraborrarítems,sepuedeusartakeItem(row) takeItem(row),ylosItemspuedenaparecerenlalista marcadosconQListViewItem::setCheckS QListViewItem::setCheckState(Qt::Chec tate(Qt::CheckState) kState). LaúnicaseñalqueusamosenestaclaseescurrentItemChanged(current,previous),quese produc produce e al cambia cambiar r el foco foco de item. item. La funci función ón currentItem(), nos nos devu devuel elve ve el item item actu actual al,, y currentRow(),nosdevuelveelrow(fila)delitemactual.
QTableWidget Se puede dimensionar en la creación en el constructor (new new QTab QTable leWi Widg dget et (rows,cols,parent) ),yluegoredimensionarlaconsetRowCount(rows) setRowCount(rows)ysetColumnCount(cols) . Podemos añadir ítems con setItem(row,col,newItem) , donde newItem es un QTableWidgetItem.Sepuedenborrarítemscon takeItem(row,col).Podemosponeretiquetasen las las cabe cabece cera ras s hori horizo zont ntal ales es con con setHorizontalHeaderLabels setHorizontalHeaderLabels(QStringList (QStringList)) y en las cabeceras
66
verticales con setVerticalHeaderLabels(QSt También ordenarpor ordenarpor una columnausando columnausando setVerticalHeaderLabels(QStringList) ringList). También sortItems(col,Qt:SortOrder) . Con rowCount ()podemoscontarlasfilas,con ()podemoscontarlasfilas,con columnCount ()lascolumnas,ycon ()lascolumnas,ycon clear () () borrartodoelcontenidodelasceldas. QTab TableWi leWidg dget etIt Item em, ,ttiene eneffunci uncio onesi esin nter teresan esanttesc esco omo setIcon(),setFont ()y ()ysetText (). (). Las señales más usadas son: cellClic cellClicked(i ked(int nt row,int row,int col), col), cellChang cellChanged(i ed(int nt row,int row,int col), col), itemClicked(QTableWidgetItemitem),itemChanged(QTableWidgetItemitem) .
Veam Veamos osa aho hora rau un nej ejem empl plo ode det tab abla lac con onl las asc cos osas asq que ueh hem emos osv vis isto to: : #include
67
TEMA18 QtCREATOR
Quer Querem emos osded dedic icar arunpo unpoco codeti detiem empo poale alent ntor orno nodede dedesa sarr rrol ollo loin inte tegr grad adode odeQt Qt,su ,suIDE IDE llamadoQtCreato llamadoQtCreator.Enunprinc r.Enunprincipio ipioQtnoposeíaunIDE,todo QtnoposeíaunIDE,todoel el códigotenía códigoteníaqueserescrit queserescritoen oen un edit editor or de text texto o avan avanza zado do,, o inte integr grad ado o en otro otro IDE IDE como como Ecli Eclips pse. e. Y aún aún se pued puede e segu seguir ir haciendoasí,perodesdelaversión4.0delSDK,TrolltechincluyóQtCreatorcomounentornode desarr desarroll ollo o comple completo, to, desde desde donde donde se puede puede accede acceder r al Qt Assist Assistant ant,, al Qt Design Designer, er, se puede puede compilar,debugeardesdeél.Perounadelascaracterísticasmásinteresantesparaelquepica códig código, o, es el autor autorrel rellen lenado ado,, y las teclas teclas de acceso acceso rápido rápido.. Por ello, ello, querem queremos os detene detenerno rnos s un tiempoparavercomopodemossacar tiempoparavercomopodemossacarventajaennuestr ventajaennuestrodesarrollo odesarrollodeesteIDEavanzado. deesteIDEavanzado.
Elautorellenado Cuando abrimos un fichero de código *.h , *.cpp, y nos situamos en una línea para empeza empezar r a teclea teclear r códig código, o, y querem queremos os que el siste sistema ma nos ayude ayude a rellen rellenar, ar, desde desde un fiche fichero ro cabecera,unaclase,unavariableyadefinida,sólotenemosquepulsarlateclaCtrl+espacio,para que que apar aparez ezca ca toda toda una una seri serie e de alte altern rnat ativ ivas as fact factib ible les, s, de acue acuerd rdo o a lo que que ya hay hay escr escrit ito o y decl declar arad ado o en el fich ficher ero. o. Tecl Teclea eand ndo o las las prim primer eras as letr letras as de lo que que quer querem emos os pone poner, r, nos nos irán irán apareciendolasopcionescadavezmásespecíficasdemaneraquemuchasvecesnohacefalta acordarsedelnombreexactodeunaclase,unficheroounavariable.Sientrelasopcionesque nosdan,noapareceunaclaseovariableespecífica,esquenoesaccesibledesdeesapartedel documento,porcualquierrazón,quepuedeserdesdequenoestáincluidosuficherocabecera,a quedichavariablenoesaccesi quedichavariablenoesaccesibleendichazo bleendichazona. na. Tamb Tambié iénn nnos osay ayud udaa aaac acce cede dera ralo losm smie iemb mbro rosd sdel elas ascl clas ases es,y ,yno nosd sdal alos osar argu gume ment ntos osde de lasfuncionescuandoescri lasfuncionescuandoescribimostrasel bimostrasellas,losparéntesi las,losparéntesis. s. Cuan Cuando do nossi nossitu tuam amos os sobr sobreun eun elem elemen ento to delcó delcódi digo go,se ,se nosre nosrecu cuad adra ra este este,yto ,ytodo doslo slos s mismoselementosdentrodelamismazonadeámbito.Deestaformapodemosverlosámbitos,y mismoselementosdentrodelamismazonadeámbito.Deestaformapodemosver losámbitos,y hacerleunseguimientoaunavariable. Si nec necesi esitamos amos ayud ayudade adell Qt Ass Assistan stantt sobr sobreal ealg gún eleme lemen nto delcó delcódi dig go, solo solo hay hay que situarsesobreélypulsarF1,entoncessenosabriráaladerechadelcódigolaventanadeayuda correspondiente. Conf Confor orme mesees seescr crib ibe, e,siha sihayal yalgú gúner nerro rorsi rsint ntác ácti tico co,ap ,apar arec ecesu esubr bray ayad adoen oenroj rojo, o,ysi ysinos nos situamossobreél,nosapareceun situamossobreél,nosapareceunmensajeemergente, mensajeemergente,describiendo describiendoelerror. elerror.
Atajosdeteclado(keyboardshortcuts) Ctrl+cursorderecho.-Tell Ctrl+cursorderecho.-Tellevaalfinaldel evaalfinaldelalíneaactual. alíneaactual. Ctrl+cursorizquierdo. Ctrl+cursorizquierdo.-Tellevaalprin -Tellevaalprincipiodelalí cipiodelalíneaactual. neaactual. Shift+cursores.-Selecc Shift+cursores.-Seleccióndetexto. ióndetexto. Ctrl+Up/Down.-Pasapáginasdecó Ctrl+Up/Down.-Pasapáginasdecódigoarribay digoarribayabajo. abajo. Ctrl+u(Cmd+UenMac).-Seleccionasbloques,pulsandovariasvecesaumentaselbloquede selección. Ctrl+i(Cmd+ienMac).-Haceque Ctrl+i(Cmd+ienMac).-Hacequeelcódigoselec elcódigoseleccionadotomela cionadotomelasangríarequeri sangríarequerida. da.
68
Ctrl+Shift+Up/Down(Cmd+Shift+Up/DownenMac).-Permitemoverlíneascompletashacia arr arriba o haci hacia a abaj abajo o. Fun Funcio ciona tamb ambién con sele selecc cciiones ones compl omplet etas as,, mov moviendo endo el text texto o seleccionado. Ctrl+/(Cmd+/enMac).-Coment Ctrl+/(Cmd+/enMac).-Comentaydescomentatodo aydescomentatodounbloqueselec unbloqueseleccionado. cionado. Shift+Del.-Borratodauna Shift+Del.-Borratodaunalíneacompleta. líneacompleta. F4.-Pasadeficherocabeceraaf F4.-Pasadeficherocabeceraaficherodecódi icherodecódigoyvicev goyviceversa. ersa. F2.-Pasadeladeclaraciónalaimpl F2.-Pasadeladeclaraciónalaimplementacióndeunaf ementacióndeunafuncióno unciónométodo. método. Ctrl+K.-Abreellocalizador Ctrl+K.-Abreellocalizador,parapodernavegar ,parapodernavegarporayudasydoc porayudasydocumentosrápidamente. umentosrápidamente. Ctrl+Shift+R(Cmd+Shift+RenMac).-Permitecambiarelnombredeunavariablealavezen todaslaspartesdondetieneámbito.
69
TEMA19 MAINWINDOWS
Este Estees esel elte tema mamá mási simpo mport rtan ante tede deto todo dosl slos osre refe fere rent ntes esal alde desa sarr rrol ollo logr gráf áfic ico, o,y yaq aque ueen enél él se encu encuen entr tra a la base base fund fundam amen enta tal l de toda todas s las las aplic aplicac acio ione nes s gráf gráfic icas as.. La vent ventan ana a prin princi cipa pal l (QMainWindow),eselcomponentefun (QMainWindow),eselcomponentefundamental,sobrelacualpueden damental,sobrelacualpuedenaparecertodaunaseriede aparecertodaunaseriede widgets:QMenuBar(barrademenu),QToolBar(barrasdeherramientas),QStatusBar(barrade esta estado do), ), QDoc DockWi kWidget dget (wi (widget dgetss acop acopllable abless), QMen Menu (men (menus us en gener eneral al,, inclu ncluid ido os los los contextuales)yotrosqueyahemo contextuales)yotrosqueyahemosvistooq svistooqueveremos. ueveremos.
El inte interf rfaz az prin princi cipa palde lde unaap unaapli lica caci ción ón,la ,la Main Main Wind Window ow (ven (venta tana na prin princi cipa pal) l),po ,pose seelo elos s elementos elementosque que vemosen vemosen lafotodearriba:un lafotodearriba:un menúprincipal menúprincipal enlapartesuperior,unao enlapartesuperior,unao varias varias barras barras de herramien herramientas tas coniconos, justo debajo, debajo, que proporcio proporciona na las opciones opciones del menú más típic típicasen asen eluso cotidi cotidiano ano,, mejora mejorando ndo larapidezde larapidezde uso dela aplic aplicaci ación ón,, luegoestael luegoestael widge widget t pri princi ncipal pal (cen (centtral ral widg widget et), ), que en este este caso aso está está en la zon zona cen central tral izqui quierda erda,, y es un QPlainTextEdit,aladerechavemosunaventanaadosable(dockablewindow),quesediferencia poreliconodelaesquinasuperiorderecha,quenosindicaquepulsandosobrelabarrasuperior yarrastrando,dichaventanasesepararíadelaventanaprincipal,proporcionandootraventana, finalmenteenlaparteinferior,seencuentralabarradeestado(statusbar)dondesesueleponer mensajesinformativosalusuarioalrespectodeloqueestaacontec mensajesinformativosalusuarioalrespectodeloqueestaaconteciendoenlaaplicaciónencada iendoenlaaplicaciónencada moment momento. o. Tambi También én cada cada elemen elemento to de la ventan ventana a princi principal pal,, puede puede tener tener un menú menú conte contextu xtual al asociado,quealpulsarelbotónderechodelratónnospermitallevaracaboaccionespropiasde dicho dicho elemento elemento gráfico gráfico.. A parte de estos estos elementos elementos,, una aplicació aplicación n puede añadir añadir ventanas ventanas de diálogo,derivadasdeQDialog,quepodránaparecercomoconsecuenciadeaccionesdelusuario, comopulsar comopulsar sobreuna sobreuna opció opción n del menú,una menú,una atajodel atajodel teclad teclado,un o,un menúsconte menúscontextu xtual, al, o algún algún eventocualquiera(informaci eventocualquiera(información,aviso, ón,aviso,error,etc). error,etc).
QAction Lapri Laprinc ncip ipal almis misió iónde ndeuna unaQMa QMain inWi Wind ndow ow,es ,escen centr tral aliz izar artod todoel oelint inter erfa fazqu zqueco ecomu muni nica ca alusuario alusuario,, con laactivid laactividadque adque laaplicaci laaplicación ón llevaa llevaa cabo,porellotodo cabo,porellotodos s los elemen elementos tos que la componen componen están enfocado enfocados s a proporcio proporcionar nar dicha interfaz interfaz.. Sin embargo, embargo, una aplicació aplicación n puede recibi recibir r órdene órdenes s por parte parte del usuari usuario o para para llevar llevar a cabo cabo una misma misma acció acción n desde desde difere diferente ntes s
70
compo componen nentes tes,, como como por ejempl ejemplo, o, pegar pegar una selecc selecció ión n previa previa en el widget widget princi principal pal,, podrí podría a hacerse hacerse seleccion seleccionado ado la opción opción Editar-> Editar->Pegar Pegar en el menúprincipal, menúprincipal, o pulsando pulsando sobre sobre el icono icono corre correspo spondi ndient ente e en la barra barra de herram herramien ientas tas,, o selecc seleccio ionad nado o Pegar, Pegar, del menú menú contex contextua tual l del widgetprincipal,osimplementepulsandounacombinacióndeteclas(shortcut)quelleveacabo dichaacción.Debidoaqueunamismaacciónpodrí dichaacción.Debidoaqueunamismaacciónpodríadesencadenarladiferen adesencadenarladiferenteselementos,espor teselementos,espor loquetodaslasaccionessehancentralizadoentornoaunaclasequenotienerepresentación gráficafísi gráficafísica,llamad ca,llamadaQAction aQAction.Cadaopcióndelmenúprinci .Cadaopcióndelmenúprincipal,delastoolbars pal,delastoolbarso o deloscontext menus, menus, se corre correspo sponde nden n con un objeto objeto QActio QAction n (una (una acció acción), n), y una misma misma acción acción puede puede ser alcanzadadesdevario alcanzadadesdevariosde sde estos elementos elementos,lo ,lo cualvaa repercuti repercutiren ren comoha comoha dedefinirseuna dedefinirseuna acción.Vamosaverladefin acción.Vamosaverladefinicióndeunaac icióndeunaacción: ción: QAction *actionNew = new QAction QAction( ("&New" "&New", ,this this); ); actionNew->setIcon( QIcon QIcon( (":/images/new.png" )); actionNew->setShortcut( "Ctrl+N" "Ctrl+N"); ); actionNew->setStatusTip( "Crear un nuevo documento" ); actionNew->setToolTip( "Crear un nuevo documento" ); actionNew->setCheckable( false false); ); // set connect(actionNew, SIGNAL SIGNAL(triggered()), (triggered()), this this, , SLOT SLOT(newFile())); (newFile()));
Podemosverqueprimerocreamosunobjetoacciónquetendráeltextoasociado“&New”(donde el&vadelantedelaletraquequeremosvayasubrayadaindicandolacombinacióndeteclasal usuarioquenosllevarátambiénalamismaacción).Luegomedianteunaseriedemétodosde QAct QActio ion, n, vamo vamos s defi defini nien endo do la acci acción ón,, que que pued puede e tene tener r asoc asocia iado do un icon icono o (ya (ya vere veremo mos s más más adelanteestoconmásdetalle),establecemoselshortcutdeteclado,establecemoseltextoque apareceráenelstatusbar,alejecutarestaacción,tambieneltooltipotextoemergenteconla descripcióndedichaacciónsinossituamossobreellaconelpunterodelratón,decidiremossies unaopcióncheckable,esdecirquepuedallevarunamarcadeactivadaodesactivada,finalmente establecemoslaconexiónentrelaseñaltriggered(),queseproducealpulsarsobreesaacción,y elslotqueenestecasopuedeserunafunc elslotqueenestecasopuedeserunafunción iónquenosotr quenosotrosmismo osmismosvamosadefin svamosadefinirloquevaa irloquevaa hacer.
Menúprincipal Cada Cadaopc opció iónpr nprin inci cipa palde ldelme lmenú núpri princ ncip ipal al,va ,valg lgala alared redun unda danc ncia ia,es ,esdec decir ir,la ,lasqu squese eseven ven enlacabeceradelaventanasinpulsarsobreellas,sonobjetosQMenuBarquesoncreadosporla QMainWin QMainWindow, dow,cada cada vezquesellamaa sufunciónmiembr sufunciónmiembro omenuBar(),yconellopasanaser hechashijasde hechashijasde estaventana.Cadaopción estaventana.Cadaopción principa principal,portantorequier l,portantorequierellamarla ellamarla primeraveza primeraveza dich dicha a func funció ión n para para crea crears rse, e, y lueg luego o va añad añadie iend ndo o acci accion ones es (sub (subop opci cion ones es de la mism misma) a) y separadores. QMenu *fileMenu; fileMenu = menuBar()->addMenu( "&File" "&File"); ); fileMenu->addAction(actionNew); fileMenu->addAction(openAction); fileMenu->addSeparator(); fileMenu->addAction(exitAction);
QToolBar(barradeherramientas) QToolBar *toolbar =addToolBar( "&File" "&File"); ); toolbar->addAction(actionNew); toolbar->addAction(openAction);
LafunciónmiembrodeMainWindow,addToolBar,añadeunabarraconunaseriadeacciones, quellevansupropioicono.
71
Menucontextual(contextmenu) Seaso Seasoci ciad adire irecta ctamen mente teel elwid widget getqu quel elov ovaa aamo mostr strar, ar,y ysól sóloh ohay ayque queañ añadi adirle rlela lasa sacci ccion ones es quevaamostrar,yfinalmentecambiardedichowidgetlapropiedadContextMenuPolicypara queaparezcaalpulsarclicderecho. QPlainTextEdit *editor; editor->addAction(copyAction); editor->addAction(pasteAction); editor->setContextMenuPolicy( Qt Qt::ActionsContextMenu); ::ActionsContextMenu);
Barradeestado(statusbar) Labar Labarra radee deest stad adon onor orma malm lmen ente teco cont nten endr dráwi áwidg dget etsde sdelt ltip ipoQ oQLa Labe bel, l,par paramo amost stra rarte rtext xto o conavisosalusuario.Paraello conavisosalusuario.Paraello,primeramenterecogemo ,primeramenterecogemoselpunteroalabarradeest selpunteroalabarradeestadoconlala adoconlala funci función ón de QMain QMainWin Window dow,, statusBar() . Luego uego añad añadiimos mos los los widget dgets, s, usan usando do la funci unció ón ,porloquecomoseve,unastatusbaresuncon atusbaresuncontenedor. tenedor. addWidget() ,porloquecomoseve,unast QLabel *label = new QLabel QLabel(); (); label->setAlignment( Qt Qt::AlignHCenter); ::AlignHCenter); label->setMinimumSize(label->sizeHint()); statusBar()->addWidget(label);
Laventanaprincipal(QMain Laventanaprincipal(QMainWindow) Window) La ventana principal al ser declarada e imp mpllementada, debe de contener todos los elementosquehemosdescritoantes, elementosquehemosdescritoantes,todaunaseriedesl todaunaseriedeslotsentrelos otsentreloscualesestaránlasf cualesestaránlasfunciones unciones queejecutanlasacciones,comonewFi queejecutanlasacciones,comonewFile(),quehemosvistoenelej le(),quehemosvistoenelejemplodeQAction.También emplodeQAction.Tambiénes es interesan interesante te el redefini redefinir r la función función virtual virtual closeEvent(QCloseEvent para por ejempl ejemplo o closeEvent(QCloseEvent *event), para inte interc rcep epta tar r la seña señal l de cier cierre re de la apli aplica caci ción ón con con algu alguna na vent ventan ana a de diál diálog ogo o que que pued pueda a preguntar,siestásegurodecerrar.Otracosaesenc preguntar,siestásegur odecerrar.Otracosaesencialquedebedetenerenlaimplementació ialquedebedetenerenlaimplementaciónde nde su constr construct uctor or,, es la design designaci ación ón como como widge widget t centra central, l, esto esto se hace hace con su funció función n miembr miembro o setCentralWidget(QWidget*) .
Ficherosderecursos(*.qrc) Los rec recurso ursos s que usa usa una una apl aplicac cación, ón, como pued pueden en ser ser ico iconos, os, bit bitmaps maps y otro tros, van desc descri rito tos s en un fich ficher ero o de recu recurs rsos os mult multip ipla lata tafo form rma a que que llev lleva a la exte extens nsió ión n qrc. qrc. Qt sopo soport rta a muchostiposdeimágeneseiconostalescomo:BMP,GIF,JPG,PNG,PNM,XMByXPM.Elfichero qrcformapartedelproyectoantesdesercompiladoeincluidoenelejecutable.Setratadeun ficheroescritoenXMLc ficheroescritoenXMLconelsiguien onelsiguienteformato: teformato:
Enelficherodeproyecto( Enelficherodeproyecto(*.pro)seañadeunal *.pro)seañadeunalineaquepone: ineaquepone: RESOURCES=recursos.qrc En el códi código go C++ C++ ya se pued puede e acce accede derr a eso esos ficher chero os dire direct ctam amen ente te medi median antte la URL URL “:/images/icon.png”,po “:/images/icon.png”,porejemplo: rejemplo: action->setIcon( QIcon QIcon( (":/images/new.png" ));
72
Donde vemos que el fichero del icono se encuentra en la carpeta del proyecto en “images/new.png”.
QSettings(ajustes) Esmuy Esmuyúti útilyag lyagra rada dabl blepa epara raelus elusua uari rio, o,que queelas elaspe pect ctoq oque ueelin elinte terf rfac acete etení níaal aalcie cierr rre, e, vuelvaarecuperarsealreabrirlaaplicaciónaldíasiguiente.Porloque,esmuyusualquelas apli aplica caci cion ones es mode modern rnas as guar guarde den n una una seri serie e de pará paráme metr tros os que que dete determ rmin inan an todo todo esto esto que que llamamos“settings”,ylorec llamamos“settings”,ylorecuperenlapróximav uperenlapróximavezqueseaejecutada. ezqueseaejecutada. Cada Cadasis siste tema maope opera rati tivo voopla oplata tafo form rma, a,ges gesti tion onaes aesto todeun deunama amane nera radis disti tint nta, a,ylog ylogua uard rda a ensitiosdifer ensitiosdiferentes entes,poresoQt,prove ,poresoQt,proveedeunaclaseymétodosparatrat edeunaclaseymétodosparatratarestetemademanera arestetemademanera uniforme uniforme e independi independientede entede la plataform plataforma. a. Dicha Dicha clase es QSettings QSettings,, y mediante mediante sus métodos métodos setValue()yvalue(),elsetterygetterdelamisma,gestionaelalmacenami setValue()yvalue(),elsetteryget terdelamisma,gestionaelalmacenamientoyrecuperació entoyrecuperaciónde nde dichosajustes(settings). QSettings::QSettings(const QString QString& &organization,constQString ,const QString& &application=QString(),QObject =QString(), QObject * * parent=0)
Ejemplodecreacióndesettings: QSettings settings( "Qt Software, Inc" Inc", , "TextEditor" );
voidQSettings::setValue(const QString QString& &key ,constQVariant ,const QVariant & &value)
Ejemplodeguardarciertossettings: settings.setValue( "geometry" , geometry()); settings.setValue( "showGrid" ,showGridAction->isCheched());
QVariant QSettings::value(constQString QSettings::value(const QString& &key ,constQVariant ,constQVariant & &defaultValue=QVariant())const
Ejemploderecuperarciertossettings: QRect rect = settings.value( "geometry" ,QRect QRect( (200 200, ,200 200, ,400 400, ,400 400)).toRect(); )).toRect(); bool showGrid = settings.value( "showGrid" ,true true).toBool(); ).toBool();
Splashscreens Sonla Sonlasve svent ntan anas asque queapa apare rece cenmi nmien entr tras asseca secarg rgan anlos losele eleme ment ntos osdeun deunpro progr gram ama, a,por por diversosmotivoscomo,indicaralusuarioqueseestáncargandoydarleinformaciónduranteel tiempo tiempo de espera espera o por pura pura autopr autopromo omoci ción. ón. En cualqu cualquier ier caso caso es un element elemento o más de una aplicaciónquemuyamenudoseusa. Lomás Lomáshab habit itua uale lesq sque ueel elcó códi digo goqu quemu emues estr trae aels lspl plas ashs hscr cree een, n,se seen encu cuen entr treen eenma main in() () antesdequesellameaQApplication::exec(). La cl clase qu que la la de describe es es QSplashScreen , y usa funcio funciones nes deQWidgetcomo deQWidgetcomo show() show() paramostrarseysetPixmap(QPixmap)paraestablecerelfondoamostrar,finalmenteelmétodo showMessage()permitesobreimpresio showMessage()permit esobreimpresionarunmensajeso narunmensajesobreelfondode breelfondodedichapantalla. dichapantalla. voidQSplashScreen::setPixmap(const QPixmap QPixmap& & pixmap) voidQSplashScreen::showMessage(const QString QString& &message,intalignment=Qt::AlignLeft,constQColor =Qt::AlignLeft,const QColor& &color= Qt::black)
73
Veamosunejemplosencillo: #include
Date Date cuenta cuenta de que, que, se muestr muestra a el splash splash screen screen con con su gráfi gráfico, co, luego luego se muestr muestra a el primer primer mensajesobreelPixmapenlazonasuperiorderechaentextoblanco,luegosellevaacabola acciónqueindica acciónqueindicael el mensaje,luego mensaje,luegoel el siguient siguientemensaje, emensaje, luegola luegola acciónqueindicaelmismo,y acciónqueindicaelmismo,y asíhasta que una veztodo estacargadoe iniciado iniciado,, entonces entonces muestra muestra la ventana ventana principal principal.. La funciónfinish(con funciónfinish(constQWidget&),loq stQWidget&),loquehaceesesperaraquesec uehaceesesperaraquesecarguecompletamenteel arguecompletamenteelWidget, Widget, enestecasolaMainWindow,antes enestecasolaMainWindow,antesdehacerunclos dehacerunclose()sobreélmismo e()sobreélmismo. . Enel Enelpr próx óxim imot otem ema, a,va vamo mosa sade desa sarr rrol olla lard rdes esde dece cero roun unaa aapl plic icac ació iónc ncom ompl plet etac acon onto todo dolo lo quehemosaprendidohastaeldíadehoy,yloharemosprimeroacódigopuro,sindiseñovisual, paracomprenderyrepasartodoslosconceptosbien,yfinalmenteveremoscomoesomismose puedehacerdemaneravisualenelQtDesigner.Porlotanto,esmuyimportantequetodoslos conc concep epto tos s qued queden en perf perfec ecta tame ment nte e acla aclara rado dos s con con este este ejem ejempl plo, o, ya que que hast hasta a aquí aquí hemo hemos s compl completa etado do el bloque bloque princ principa ipal l y básico básico para para constr construi uir r aplica aplicaci cione ones s en Qt, y sobre sobre esta esta base base vamos vamos a apoyar apoyar el resto resto de la enseña enseñanz nza a que viene viene tras tras el tema tema 20, donde donde abunda abundarem remos os en conceptos conceptos ya presentado presentados, s, e introduci introduciremos remos nuevos nuevos conceptos conceptos,, pero todos fundament fundamentados ados en estoprimeros20temas,queconf estoprimeros20temas,queconformanelnivel ormanelnivelBásico. Básico.
74
75
TEMA20 DESARROLLODEUNAAPLICACIÓNCOMPLETA
Enestetemavamosa Enestetemavamosa desarroll desarrollaruneditor aruneditorde de texto,concapaci texto,concapacidadparacut/p dadparacut/paste, aste,con con menúprincipal, menúprincipal, barra deherramientasystatusbar.Introduc deherramientasystatusbar.Introducirem iremosalgunos osalgunos conceptos conceptos nuevos, nuevos, pero pero pocos, pocos, como como hemos hemos hechoen hechoen los temas temas anteri anteriore ores,para s,para seguir seguir siendo siendo fiele fieles s alestilo alestilo de enseñanzagradualqueestamosusandoenestelibro.Elproyect enseñanzagradualqueestamosusan doenestelibro.Elproyectopuedesabrirlodirectament opuedesabrirlodirectamenteen een elQtCreator,peroterecomiendoquesigaslasexplicacionesquesevanadarenestetema,yque luegoportucuenta,intentes luegoportucuenta,intentesdesarrollarlomi desarrollarlomismo,oalgos smo,oalgosimilardesdecero imilardesdecero.. Vamo Vamosa sacr crea earu runn nnue uevo vopro proye yect cto“ o“Qt QtGu GuiA iApp ppli lica cati tion on”, ”,al alqu quell ellam amar arem emos os“ap “appl plic icat atio ion” n”,, laclasese llamaráMainWin llamaráMainWindow,suclasebaseserá dow,suclasebaseserá QMainWind QMainWindow,y ow,y desactiva desactivaremos remos laopción “Generateform”paraasícrearnosotrosmismoselinterfazgráficoencódigopuro.Losiguiente que que vamo vamos s a hace hacer r es copi copiar ar los los icon iconos os que que vamo vamos s a usar usar (dir (direc ecto tori rio o imag images es) ) en nues nuestr tro o proyecto,tambiénenunacarpeta proyecto,tambiénenunacarpetallamadaimages,dentro llamadaimages,dentrodenuestroproy denuestroproyecto. ecto. Denue Denuevo vo hace hacemo moscl sclic icder derec echo ho sobr sobrele elenue nuevo vo proy proyec ecto to yañad yañadim imos osunnu unnuev evoQt oQt-> ->Qt Qt ResourceFile,alquellamaremostambién“application”.AlabrirseelficheroqrcenelQtCreator, apareceunaventanasuperiorvacía,yabajounbotónAdd.PulsamossobreélyluegosobreAdd Prefix.EnelcampodetextoPrefi Prefix.EnelcampodetextoPrefix,vamosaponer“/”. x,vamosaponer“/”.Volvemo Volvemosa sa pulsarenAdd->AddFiles,y pulsarenAdd->AddFiles,y nave navega gamo mos s hast hasta a el dire direct ctor orio io imag images es dond donde e hemo hemos s copi copiad ado o los los icon iconos os que que vamo vamos s a usar usar,, seleccionamoslos6iconosylosabrimos. seleccionamoslos6iconosylosabrimos.Yaaparecenlos6iconosadscrito Yaaparecenlos6iconosadscritosalprefijo“/”,asíque salprefijo“/”,asíque ya pode podemo mos s acce accede der r a ello ellos s usan usando do una una URL URL del del tipo tipo “:/i “:/ima mage ges/ s/ic icon ono. o.pn png” g”,, dond donde e “:” “:” hace hace referenciaalosrecursos referenciaalosrecursosinternos. internos.
Ficheromain.cpp #include
Usamos Usamos lamacro Q_INIT_RE Q_INIT_RESOURC SOURCE(no E(nombre_ mbre_base_ base_del_ del_qrc), qrc), aunque aunque enla mayoríade mayoríade loscasos noseanecesarioylosrecursossecarguenautomáticamente,peroenalgunasplataformas,los recursos recursosse se guardanenuna guardanenuna librería librería estática. estática. Todolo Todolo demásyalohemosvistoanter demásyalohemosvistoanterior iormente mente,a ,a part parte e de los los dos dos miem miembr bros os de QApp QAppli lica cati tion on que que se enca encarg rgan an de guar guarda dar r en la aplic aplicac ació ión n el nombredelaempresaquehizolaaplicació nombredelaempresaquehi zolaaplicaciónyelnombredelamis nyelnombredelamisma.Comovemos,vamo ma.Comovemos,vamosausar sausar una una Main MainWi Wind ndow ow que que hemo hemos s deri deriva vado do de QMai QMainW nWin indo dow w y que que esta esta impl implem emen enta tada da en los los ficherosmainwindow.hy.cpp.
LaclaseMainWindow
76
Gran Granpa part rted edel elcó códi digo goyde ydela laes estr truc uctu tura rade delm lmis ismo mode dees esta tacl clas ase, e,po podr drán ánse seru rusa sado dose sen n otras otras aplic aplicaci acion ones. es. Vamos Vamos a ver primer primerame amente nte el widget widget que vamos vamos a design designar ar como como widget widget centraloprincipaldelaVentan centraloprincipaldelaVentanaPrincipal(Main aPrincipal(MainWindow). Window). Al ser una una apli apliccaci ación edi editora ora de tex texto,va to,vamo moss ausar ausar QPlai Plain nText TextEd Ediitcomo tcomo el widget dget principa principal.Si l.Si consultas consultas dicha clase clase enel asistente(QtAssitan asistente(QtAssitant),podrásverentresusmiembros t),podrásverentresusmiembros,, losslotsqueusaremosenlosmenús,comocut(),copy(),paste().Tambiénpodrásverlafunción más más impo import rtan ante te,, que que es docu docume ment nt() (),, que que devu devuel elve ve un punt punter ero o al text texto o del del mism mismo o del del tipo tipo QTextDocument.Deentrelosmiembrosdeésteúltimo,usaremosisModified()parasabersiel documentohasidomodificado documentohasidomodificadoenalgúnmomento. enalgúnmomento. Lueg Luegote otend ndre remo mosqu squemi emira rarlo rlossl sslot otsqu squeva evamo mosanec saneces esit itar ar defi defini niren ren la nuev nuevacl aclas ase. e. Puesto Puesto que cada cada entrad entrada a del menú menú requie requiere re de una conexi conexión ón entre entre la señal señal trigge triggered red() () de la acción,yunslotqueejecutalaacción.Porloquetendremosquedarunrepasoalasopcionesde menúquevamosaponer,parasaberlos menúquevamosaponer,parasaberlosslotsquetendr slotsquetendremosquedeclararen emosquedeclararenestaclase. estaclase. Tend Tendre remo mosu sunm nmen enúF úFil ilec econ onlo loss ssig igui uien ente tess ssub ubme menú nús( s(Nu Nuev evo, o,Ab Abri rir, r,Gu Guar arda dar, r,Gu Guar arda dar r comoySalir).AexcepcióndelaopciónSalir,quelaejecutaremosconelslotclose()deQWidget, elrestoseránslotsadeclarareimplementar. Tene Tenemo mosta stamb mbié iénel nel menú menú Edit Edit conlo conlossi ssigu guie ient ntes es subm submen enús ús (Cor (Corta tar, r, Copi Copiar ar yPega yPegar) r). . Pues Puesto to que que todo todos s ello ellos s pued pueden en ejec ejecut utar arse se desd desde e los los slot slots s pred predef efin inid idos os en QPla QPlain inTe Text xtEd Edit it,, respec respectiv tivame amente nte cut(), cut(), copy( copy() ) y paste( paste(), ), no es necesa necesario rio implem implement entar ar nuevos nuevos slots slots para para este este menú. Fina Finalm lmen ente tete tene nemo mose selm lmen enúA úAyu yuda daco conl nlos ossu subm bmen enús ús(A (Ace cerc rcad adey eyAc Acer erca cade deQt Qt), ),de delo los s cuales,acercadeQtvieneyapreimplemen cuales,acercadeQtvieneyapreimplementadocomoelslo tadocomoelslotaboutQt()deQApplic taboutQt()deQApplication. ation. Cuan Cuando doel eldo docu cume ment ntoe oesm smod odif ific icad ado, o,de debe bede deha habe beru runa naac acci ción ónqu quec ecam ambi biel elap apro ropi pied edad ad del widget widget modific modificado, ado, windowMo windowModifi dified, ed, que lo hace el setter setter setWindow setWindowModi Modified fied.. Por tanto tanto vamosacrearunslotqueejecutedi vamosacrearunslotqueejecutedichocambioal chocambioalquellamaremosdocumen quellamaremosdocumentWasModified(). tWasModified(). Asílosslotsquevamosadeclar Asílosslotsquevamosadeclararfinalmentes arfinalmenteson: on: private slots slots: : void newFile(); void open(); bool save(); bool saveAs(); void about(); void documentWasModified();
Losi Losigu guie ient nteq eque uev vamo amosa sade decl clar arar ars son onla laf fun unci cion ones espr priv ivada adas, s,q que uese seen enca carg rgar arán ánde dell llev evar ar a cabo cabo las tareas tareas princi principal pales es asign asignadas adas a los los slots, slots, y tareas tareas secund secundari arias as propia propias s del tipo tipo de aplica aplicaci ción. ón. Todas Todas las tareas tareas privad privadas as que vamos vamos a defin definir, ir, son propia propias s de una aplica aplicaci ción ón GUI genérica: private : private: void void void void
createActions(); createMenus(); createToolBars(); createStatusBar();
void readSettings(); void writeSettings(); void writeSettings(); bool maybeSave(); void loadFile( const QString &fileName); bool saveFile( const QString &fileName); void setCurrentFile( const QString &fileName); QString strippedName( const QString &fullFileName);
77
Vamosaverlasunaporuna,yaquesonfundamentalesparaeldesarrollodecualquieraplicación GUI, GUI, aunq aunque ue pued puedan an ser ser decl declar arad adas as e impl implem emen enta tada das s a gust gusto o del del prog progra rama mado dor, r, pero pero es convenienteelestructurartusaplicacionesdeestamaneraparahacermáslegibleymantenible tucódigo. createActions.-Aquípondremoselcódigoquecreatodaslasaccionesdetodoslosmenús,talcual vimoseneltemaanteriorconQAction,ylasconectaremosconlosslotsquedeclaramosantes. Tambiénpodemosusarestasecciónparadeshabilitarlasaccionesqueinicialmentelodebande estar.
Aquí es dond donde e vamo vamos s a pone poner r el códi código go que que crea crea los los menú menús, s, en nues nuestr tro o caso caso createMenus.- Aquí Fichero,EditaryAyuda,con Fichero,EditaryAyuda,consusseparadoresy susseparadoresytodo. todo. donde pondre pondremos mos el códig código o que crea crea las barras barras de herram herramien ientas tas,, en createToolBars.-Aquí es donde nuestrocasovanaser2,unapar nuestrocasovanaser2,unaparaopcionesdeFi aopcionesdeFicheroylaotr cheroylaotraparaopciones aparaopcionesdeEditar. deEditar. .-Aquíesdondepondremoselcódigoparacrearlabarradeestado,cont earlabarradeestado,contodossus odossus createStatusBar .-Aquíesdondepondremoselcódigoparacr elementos,ypodremosmostrarunmen elementos,ypodremosmostrarunmensajeinici sajeinicialsilovemos alsilovemosoportuno. oportuno. readSettings.-Aquípondremoselcódigoquesevaaencargardeleerlossettingscomovimosen eltemaanterior,ylosvaaaplicar.
podremosel osel códig códigoque oque seva a encarg encargarde arde guarda guardarlos rlos settin settingsante gsantesde sde writeSettings.-Aquí podrem salirdelaaplicación. setCurrentFile.-EselsetterdelapropiedadCurrentFilequeseencargarádellevarelnombredel fiche fichero ro actual actual al que se hace hace alusió alusión. n. Podrá Podrá hacer hacer una serie serie de accion acciones es relaci relaciona onadas das con el manejodeficherosynoti manejodeficherosynotificación ficacióndelcambioenelc delcambioenelcontenidodedi ontenidodedichofichero chofichero..
Este bloq bloque ue se enca encarg rgar ará á de abri abrir r el fich ficher ero o y leer leer su cont conten enid ido o carg cargán ándo dolo lo en loadFile .- Este QPlainText QPlainTextEditcon Editcon setPlainT setPlainText() ext().. Además harátodas lasnotificaci lasnotificaciones ones que requierasobre requierasobre la cargadelfichero,comoestab cargadelfichero,comoestablecerelnuevoCur lecerelnuevoCurrentFileon rentFileonotificarsu otificarsuacciónenel acciónenelstatusbar. statusbar. Este bloq bloque ue se enca encarg rgar ará á de abri abrir r el fich ficher ero, o, escr escrib ibir ir su cont conten enid ido o y noti notifi fica car r saveFile.- Este igualmentesobreelmismo. maybeSave.-Estebloquepuedeestarintegradoenlos2anteriores,opuedeserseparadopara dejarmás dejarmás claroel claroel códig código.Su o.Su funció función n sería, sería, siel docume documento nto ha sido sido modif modifica icado, do, muestr muestra a una ventanadediálogowarning,paraqueelusuariodecidasiguardaonoguardaloscambios.Es muytípicoenlasaplicacion muytípicoenlasaplicacionesmodernas. esmodernas.
bloque simpl simple e que extrae extrae elnombre elnombre del fiche fichero ro,, extray extrayénd éndolo olo del path path strippedName.-Es una bloque completo.
Lasvar Lasvaria iabl bles es priv privad adas as quevam quevamos os ausarcor ausarcorre resp spon onde dencon ncon:lapro :lapropi pied edad ad curr curren entF tFil ile e descritaantes,elwidgetcentralqueesunQPlainTextEdit,los3menusQMenu,las2barrasde herramien herramientasQTool tasQToolBarylas Barylas acciones accionespropiasa propiasa losmenúsQAction.Estas losmenúsQAction.Estasson son lasvariablesque lasvariablesque vamosausar: QString curFile; QPlainTextEdit *textEdit; QMenu *fileMenu; QMenu *editMenu; QMenu *helpMenu; QToolBar *fileToolBar; QToolBar *editToolBar; QAction QAction QAction QAction
*newAct; *openAct; *saveAct; *saveAsAct;
78
QAction QAction QAction QAction QAction QAction
*exitAct; *cutAct; *copyAct; *pasteAct; *aboutAct; *aboutQtAct;
Para Paraf fin inal aliz izar arl la ade decl clar arac ació ión nde del la acl clas ase eMa Main inWi Wind ndow ow, ,va vamo mos sta tamb mbié ién na are reim impl plem emen enta tar r unafunciónvirtualdeQWidgetllamadacloseEvent(QCloseEvent)quenospermitiráinterceptar elcierredelaaplicación,paranotificaralusuariosieldocumentotienecambiossinguardar,una caract caracterí erísti stica ca muy usual usual tambié también n en aplic aplicaci acione ones s modern modernas. as. La vamos vamos a declar declarar ar en la zona zona prot protec ecte ted d para para que que pued pueda a ser ser reim reimpl plem emen enta tada da en hijo hijos s que que pued puedan an deri deriva vars rse e de la clas clase e MainWindowenfuturasaplicaciones.
ImplementacióndeMainWindow Aco Acont ntin inua uaci ción ónv vam amos osa av ver erl la aim impl plem emen enta taci ción ónd de eto todo dos ses esto tos sbl bloq oque ues sy ymi miem embr bros osq que ue hemosvistoanteriormente. Come Comenz nzam amos osc con one el lco cons nstr truc ucto tor rqu que ede defi fine nee el lin inte terf rfaz azi ini nici cial ald de ela laa apl plic icac ació ión: n: MainWindow::MainWindow() { textEdit = new QPlainTextEdit ; setCentralWidget(textEdit); createActions(); createMenus(); createToolBars(); createStatusBar(); readSettings(); connect(textEdit->document(), SIGNAL SIGNAL(contentsChanged()), (contentsChanged()), this, this , SLOT SLOT(documentWasModified())); (documentWasModified())); setCurrentFile( "" ""); ); }
Quedanmuyclarastodaslasaccionesquellevaacabo:estableceralQPlainTextcomoelwidget centr entral al, , luego uego crea crearr las acc acciones par para los los men menús, ús, lueg uego crear rear los los men menus, us, las bar barras de herr herram amie ient ntas as,, la barr barra a de esta estado do,, leer leer los los sett settin ings gs de el últi último mo cier cierre re,, cone conect ctar ar la seña señal l de cambioseneldocumentoconelslot cambioseneldocumentoconelslotquehemoscreadoparaello, quehemoscreadoparaello,yestablecercomof yestablecercomoficheroactual icheroactual (CurrentFile)aninguno. void MainWindow::closeEvent( QCloseEvent *event) { if (maybeSave()) { writeSettings(); event->accept(); } else { event->ignore(); } }
La funci función ón closeE closeEven vent() t(),, es ejecut ejecutada ada automá automátic ticame amente nte cada cada vez que un top-le top-level vel widge widget t es cerrado.Sieleventoesaceptado(accept())en cerrado.Sieleventoesaceptado(accept())entoncessalesinpr toncessalesinproblemas,siesig oblemas,siesignorado(igno norado(ignore()) re()) entoncesnoescerrado.AquíserámaybeSavequienmuestralaventanadeelecciónalusuario,y devolveráunbooldeacuerdoalb devolveráunbooldeacuerdoalbotónpulsado: otónpulsado: bool MainWindow::maybeSave() { if (textEdit->document()->isModified()) { QMessageBox ::StandardButton ret; // es un int con un codigo ret = QMessageBox ::warning( this this, , tr("Application" tr("Application" ), tr("El tr("El documento ha sido modificado.\n" "_Quiere guardar los cambios?" ), QMessageBox ::Save | QMessageBox ::Discard | QMessageBox ::Cancel); if (ret == QMessageBox ::Save)
79
return save(); else if (ret == QMessageBox ::Cancel) return false false; ; } return true true; ; }
Elres lresttodelcó delcódi digo gopue puede dese serrente enten ndido didope perf rfe ectam ctamen ente teco con nloqu loqueh ehem emo osestu sestudi diad ado o hasta ahora. Sólo haremos la introducción a un concepto nuevo que es la función QObj QObjec ect: t::t :tr( r(QS QStr trin ing) g) que que devu devuel elve ve la trad traduc ucci ción ón del del argu argume ment nto o pasa pasado do, , y es usad usado o para para internacionalizarunaapli internacionalizarunaaplicación. cación. Ahor Ahora all lleg egó óel elm mom omen ento tod de eha hace cer rla lam mis isma maa apl plic icac ació ión npe pero rou usa sand ndo oel elQ Qt tDe Desi sign gner erp par ara a diseñarelinterfazgráfico.
Crearlaaplicacióngráficamente Hace Haceru ruso sode deQt QtDe Desi sign gner erpa para raha hace ceru runa naap apli lica caci ción ón,f ,fac acil ilit itam amuc ucho hola lasc scos osas as,n ,nos osol olo o paradibujarelinterfazsiestoesnecesario,sinoparacrearlosmenus,lasaccionesdelmismo,y lasconexionesconlosslots.Puesbienvamosaabrirunnuevoproyecto,yestavezsivamosa crearelfichero*.ui,lepondremo crearelfichero*.ui,lepondremosdenombreapplicatio sdenombreapplication2.Alabrireluiver n2.Alabrireluiveremosqueapareceun emosqueapareceun formulariocomoéste:
Así Asíqu que ele lea aña ñadi dimo mos sun unP Pla lain inTe Text xtEd Edit ity yo ocu cupa pamo mos sco con nél élt tod odo oel elá áre rea aqu que eno nos spe perm rmit ite, e, veremosqueenelinspectordeob veremosqueenelinspectordeobjetosdeladerechasu jetosdeladerechasuperior,lohapues perior,lohapuestocomocentr tocomocentralWidget. alWidget. Vamos a ir rellenado los menús haciendo doble clic sobre “Type Here” y pulsamos mos sobre sobre Add Separa Separator tor para para añadir añadir un separa separador dor de menú, menú, por ejempl ejemplo o antes antes del Salir Salir.. Lo que tendremosalfinalesalgocomoesto:
80
Asíiremosrellenandolosmenúsconformehemosdescritoalprincipiodeestetema,opcióna opción.Verásqueenlapartein opción.Verásqueenlaparteinferior,en ferior,enelActionEdit elActionEditor,empiezaarellen or,empiezaarellenarseelsóloc arseelsólocomoesto: omoesto:
Haciendodobleclicencadaacc Haciendodobleclicencadaacción,podemosedi ión,podemoseditarsuspartesdelas tarsuspartesdelasiguientemanera: iguientemanera:
Porsupuesto,sehateneryacreadoycargadoelqrctalcomolovimosenelejemploanterior, para para que aparez aparezcan can los icono iconos.En s.En elinspecto elinspector r de propie propiedad dadesponem esponemosel osel status statusTip Tip a mano. mano. Vamosahacerlomismocontodoslossubmenús,ydejaremosparadespuéslapartereferentea lasconexiones. Ahor Ahora ava vamo mos sac acre rear arl los oss slo lots tsq que uev van ana ar rec ecib ibir irl las asa acc ccio ione nes, s,e exc xcep epto tol los osq que uey ya avi vien enen en de herenc herencia. ia. Para Para ello, ello, hacemo hacemos s clic clic derech derecho o sobre sobre accion accion_Nu _Nuevo evo,, y elegi elegimos mos “go to slot”, slot”, y pulsamossobrelaseñaltriggered(),queeslaquevamosaconectar.Inmediatamentenosllevaal código del slot que se acaba de crear, on_action_Nuevo_triggered(), que se ha creado automáticamente. Todos los slots creados en el Qt Designer, llevan esta forma, on_Ac on_Actio tionNa nName_ me_Sig Signal nalNam Name() e().. Así vamos vamos a crear crear slots slots tambi también én para para abrir, abrir, guarda guardar, r, guarda guardar r como como,, acer acerca ca de. de. El rest resto o de acci accion ones es las las vamo vamos s a llev llevar ar a slot slots s pred predef efin inid idos os en las las clas clases es correspondientesdeQtcomohemo correspondientesdeQtcomohemosvistoya. svistoya. Los Losslo slots ts que quese seha han ncre cread ado oaut auto omát máticame camen nte dee deesstam taman aner era aso son n:
81
private slots slots: : void on_actionAcerca_de_triggered(); void on_actionGuardar_como_triggered(); void on_actionGuardar_triggered(); void on_actionAbrir_triggered(); void on_action_Nuevo_triggered();
y además además se ha establ estableci ecido do la conexi conexión ón automá automáti ticam cament ente e entre entre la señal señal trigge triggered red() () de dic dicha ha accióncondic accióncondichosslot hosslots,porloquenohacefalta s,porloquenohacefaltaestable establecerla cerlasenelcódigofue senelcódigofuente,yaestá nte,yaestánenel nenel *.ui. Aho Ahora,no a,nosv svam amo osalS salSiignal gnal&Sl &Slo otsed tsediitor tor,do ,dondees deesta tato todo dova vaccío.Pul .Pulsa samo moss ssob obrreel eel botón botón verde verde +, y lo hacemo hacemos s justo justo el número número de veces veces que conexi conexion ones es necesi necesitam tamos os para para las accionesdelosmenúsquevanaslotspredefinidosenQt(son4).Ahoravamosairrellenando todaslasconexionesdecadaacción,conlosslotsquelescorresponden,conloqueunavezlleno quedaríadeestamanera:
Como omopued puedes esob obse serrvarno arnohe hemo mosspodi podido dopo pone nerrlaco lacone nex xiónde óndela lab boutQ utQtAct tActde deb bidoa doa quenoapareceelreceiverqApp,porloqueespartetendremosqueponerlaenelconstructorde MainWindowamano. Volvamos a activar el Action Editor, y ahora hacemos clic derecho en la clase MainWi MainWindo ndow w en el inspec inspector tor de objeto objetos s y elegi elegimos mos “add “add toolba toolbar”. r”. Ya tenemo tenemos s 2 toolb toolbars ars,, la primer primera a la llamam llamamos os fileT fileToo oolBa lBar r y la segund segunda a editTo editToolB olBar, ar, cambia cambiando ndo su object objectNam Name e en el inspectordepropiedades.ArrastrandolosiconosdelActioneditordeNuevo,AbiertoyGuardar sobrelaprimeraToolBar,creamoslos3botonesdedichabarradeherramientas.Hacemoslo mismoconCortar,CopiaryPegarenlasegundabarradeherramientas.Elresultadofinalesalgo así:
Ya tenemos toda la parte gráfica construida, y ya sólo nos queda ir rellenando el códigocorrespondientedelaclaseMainWindow.AhoraeligiendolasaccionesdeCortaryPegar, vamo vamos s a desa desact ctiv ivar arla las s por por defe defect cto o en el pane panel l de prop propie ieda dade des, s, desh deshab abil ilit itan ando do el cuad cuadro ro “enabled”.Verásquelosiconossevuelvenmásapagados.Yestoestodoloquesepuedehacer conelQtDesigner,elresto conelQtDesigner,elrestohayqueteclearloaman hayqueteclearloamano. o.
82
Si revisamos el código de mainwindow.h, veremos que hay algo nuevo que hace referenciaalcontenidoq referenciaalcontenidoquehayenelfi uehayenelfichero*.ui,y chero*.ui,yeselcódigo: eselcódigo: namespace Ui { class MainWindow; } private : private: Ui::MainWindow *ui;
Loq Loque ued des esig igna nae ese sec cód ódig igo oes esu un npu punt nter ero oal ali int nter erfa face ceq que ueh hay aye en nui ui, ,y ypo por rlo lot tan anto top par ara a accederalosobjetosdentrodelmismo,tendremosquehacerlodesdeesepuntero,locualestan sencillocomoesto: ui->actionGuardar->setEnabled( false false); );
quedesactivaríadirectamentelaacciónGuardar,yquesipruebasaescribirloencualquierparte delaimplementacióndeMainWindow,verásqueelauto delaimplementacióndeMain Window,verásqueelautorellenadotevadandopis rellenadotevadandopistasdetodoslos tasdetodoslos objetosymiembrosaccesiblesdesdeui. Dejamos por por tanto como ejercicio de repaso par para el lector, la cumplimentación del del códigonecesarioparaqueestaaplicació códigonecesarioparaqueestaaplicaciónfuncionei nfuncioneigualquesuantecesorahechaco gualquesuantecesorahechacompletamente mpletamente encódigo.NoobstanteteobsequiamosconelcódigodelconstructordelaclaseMainWindow paraquetesirvacomoorient paraquetesirvacomoorientación: ación: MainWindow::MainWindow( QWidget *parent) : QMainWindow (parent), ui(new ui(new Ui::MainWindow) { ui->setupUi( this this); ); setCentralWidget(ui->plainTextEdit); connect(ui->actionAcerca_de_Qt, SIGNAL SIGNAL(triggered()), (triggered()), qApp, SLOT SLOT(aboutQt())); (aboutQt())); connect(ui->plainTextEdit, SIGNAL SIGNAL(copyAvailable( (copyAvailable( bool bool)), )), ui->actionCor_tar, SLOT SLOT(setEnabled( (setEnabled( bool bool))); ))); connect(ui->plainTextEdit, SIGNAL SIGNAL(copyAvailable( (copyAvailable( bool bool)), )), ui->action_Copiar, SLOT SLOT(setEnabled( (setEnabled( bool bool))); ))); ui->statusBar->showMessage(tr( "Preparado" )); readSettings(); connect(ui->plainTextEdit->document(), SIGNAL SIGNAL(contentsChanged()), (contentsChanged()), this, this , SLOT SLOT(documentWasModified())); (documentWasModified())); setCurrentFile( "" ""); ); }
Resu Resumi mire remo mos spo por rta tant nto olo los s7 7pa paso sos sa ase segu guir irp par ara acr crea ear run una aap apli lica caci ción ónc con ona ayu yuda dad del elQ Qt t Designer(formavisual): 1.-SituarelcenterWidgetdebajo 1.-SituarelcenterWidgetdebajodeltoolbary deltoolbaryencimadelstatusb encimadelstatusbar. ar. 2.-Escribirtodaslasopci 2.-Escribirtodaslasopcionesdelmenúpri onesdelmenúprincipal. ncipal. 3.-Editarlasaccionesquesecreanconelmenúprincipalcumplimentadoparaañadirlesiconos, statustips,suestadoinicial,etc. 4.-Crearlastoolbarquesereq 4.-Crearlastoolbarqueserequieranarrastran uieranarrastrandolosicon dolosiconosdelasaccio osdelasaccionessobreaquell nessobreaquellas. as. 5.-Crearlosslotsqueserequierandel 5.-Crearlosslotsqueserequierandelasaccionescon asaccionesconbotónderechobotónderecho->gotoslot,luego >gotoslot,luegodarlosde darlosde altaenelQtDesignercomomiemb altaenelQtDesignercomomiembrosdeMainWin rosdeMainWindow. dow. 6.-Hacemostodaslasconexion 6.-Hacemostodaslasconexionesentrelasacci esentrelasaccionesylossl onesylosslotsenelsign otsenelsignal-slotedito al-sloteditor. r. 7.-Yapodemosañadirelcódigoquehagafalta,conayudadelnuevopuntero*uiparadesignarel diseño.
83
TEMA21 LAYOUTAFONDO
Enel Enelte tema ma12 12ya yato toca camo mose selt ltem emad adel ella layo yout utco como moel elme medi diom omed edia iant ntee eelc lcua ual, l,lo losw swid idge gets ts negociab negociabanelespaciodelaventanaquelosconti anelespaciodelaventanaqueloscontiene, ene,creandounainter creandounainterfaceflex faceflexible iblea a múltiples múltiples circunstanciascomocambiodeidiomassintruncadodepalabras,oadaptacióndelGUIaotras plataf plataform ormas. as. En este este capítu capítulo lo vamos vamos a añadir añadir nuevas nuevas clases clases a este este estudi estudio o para para dispon disponer er el espaci espacio o de una ventan ventana a viendo viendo QSpli QSplitte tter, r, que crea crea barras barras que divide dividen n una ventan ventana a en varios varios espaci espacios, os, QScrol QScrollAr lArea, ea, que permit permite e que una zona zona pueda pueda verse verse en parte parte y el resto resto pueda pueda ser accedi accedido do median mediante te una barra barra de scroll scroll,, QWorkS QWorkSpac pace e o QMdiAr QMdiArea ea que nos introd introduci ucirá rá en las aplicacionesMDI(multi-documento),dondesepodránabrirmásdeundocumentoalavezenla mismaventana. Como Comodij dijim imos osent enton once ces, s,tod todowi owidg dget etocu ocupa paunes unespac pacio iorec recta tang ngul ular ar,co ,como mouna unacaj caja, a,yla ylas s coordenad coordenadas as de las vértices vértices superior superior izquierd izquierdo o e inferio inferior r derecho derecho se guardan guardan en la propiedad propiedad “geometry”,quepuedeseraccedidamedianteelgettergeometry()comovimosenelejemplodel tema tema anteri anterior or para para los los settin settings, gs, y puede puede ser cambi cambiado ado median mediante te el setter setter setGeo setGeomet metry( ry(). ). Sin embargo,nadieusaestosmétodosparaposicionardemaneraabsolutaningúnwidgetdentrode laventana,sinoqueusadelayo laventana,sinoqueusadelayoutsparaello. utsparaello.
QStackedLayout Esta sta clas lase crea rea un conjun njunto to de widget dgetss hijo hijoss que pres presen enttan pági págin nas api apilada ladas, s, y sólo sólo puedemostrarunaalavez,ocultandoelrestoalusuario.QStackedWidget,esunawidgetque proveeQt,yquellevadentrounQStackedLayout.Noseproveedeningúncontrolparaqueel usuariopuedanavegarporlaspáginas,asíqueestosólopuedehacermedianteelusodecódigo. Laspáginasestánnume Laspáginasestánnumeradas radasdel0 del0 enadelante,ypodemos enadelante,ypodemoshacerunavisib hacerunavisiblemedian lemedianteelsetter teelsetter setCurrentIndex(page).ParaaveriguarelindexdeunwidgetcontenidoenesteLayoutseusala funciónindexOf(QWidget), funciónindexOf(QWidget),quedevuelvesuent quedevuelvesuentero. ero. Bien Bien,,un unad ade eta tant ntas asf for ormas masde deh hac acer erf fun unci cion onar ares este teL Lay ayou out, t,es esv vin incu cula lar rla las sop opci cion ones esde deu un n widgetconlasdiferentespáginasdelstackedlayout.EstosepuedehacerconunComboBox,o unaQListWidget.Vamosaverunejemplo: PreferenceDialog::PreferenceDialog( QWidget *parent) : QDialog QDialog(parent) (parent) { //appearancePage, webBrowserPage, mailAndNewsPage, advancedPage listWidget = new QListWidget ; listWidget->addItem(tr( "Apariencia" )); listWidget->addItem(tr( "Navegador" )); listWidget->addItem(tr( "Correo y foros" foros")); )); listWidget->addItem(tr( "Avanzado" )); stackedLayout = new QStackedLayout ; stackedLayout->addWidget(appearancePage); stackedLayout->addWidget(webBrowserPage); stackedLayout->addWidget(mailAndNewsPage); stackedLayout->addWidget(advancedPage); connect(listWidget, SIGNAL SIGNAL(currentRowChanged( (currentRowChanged( int int)), )), stackedLayout, SLOT SLOT(setCurrentIndex( (setCurrentIndex( int int))); ))); ……. listWidget->setCurrentRow( 0); }
Aquí previament previamente, e, appearanceP appearancePage,webBrow age,webBrowserPa serPage, ge, mailAndNew mailAndNewsPage sPage y advancedPa advancedPage, ge, son widgets,porejemplolayoutscontodaunaseriedewidgetsdentro,queconformanunaventana dediálogocompleta.Sonañadidoscomowidgetsasuvezdelstackedlayout,yloquehacemoses vincularunalistWidgetconélmediantelaseñalcurrentRowChanged(int)quevimoseneltema
84
17,conelsetteryademásslotsetCurrentIndex(int),yaqueambospasanuníndiceigual(desde0 aN).Deestamanerapodemosdisponerespacialmenteaamboswidgetscontenendores,dentro deuna ventan ventana a dediálogode dediálogode selecc selecció ión n depreferen depreferenci cias. as. Fíjate Fíjate enel detall detalle,de e,de que fijam fijamosla osla posicióninicialdelamb posicióninicialdelambosconsetCur osconsetCurrentRow(0). rentRow(0). ¿Cómoseharíaestomismoenel ¿CómoseharíaestomismoenelQtDesigner?. QtDesigner?.Puessiguiendo Puessiguiendoestossencillo estossencillospasos: spasos: 1.-Crearunformularionuev 1.-CrearunformularionuevobasadoenQDi obasadoenQDialogoenQWidg alogoenQWidget. et. 2.-AñadirunQListWidgetyun 2.-AñadirunQListWidgetyunQStackedWidgetal QStackedWidgetalmismo. mismo. 3.-Rellenarcadapáginaconwidgetsylayoutsconformeseanecesario.(haciendoclicderecho sobreellayout,yescogiendo“InsertPage”paraañadirnuevaspáginas,yparanavegarporellas, usandolasflechitasenlazon usandolasflechitasenlazonasuperiorderech asuperiorderecha). a). 4.-Conectaramboswidgets,pulsan 4.-Conectaramboswidgets,pulsandoenelbotón“ doenelbotón“Editsignal”de Editsignal”dearribadelDesign arribadelDesigner,ypulsamos er,ypulsamos sobr sobre e el List ListWi Widg dget et y arra arrast stra ramo mos s haci hacia a el Stac Stacke kedW dWid idge get, t, de mane manera ra que que una una flec flecha ha los los cone conect ctar ará. á. Al solt soltar ar el rató ratón n apar aparec ecer erá á la vent ventan ana a para para eleg elegir ir la seña señal l y el slot slot que que esta estará rán n conectados.
5.- Añadim Añadimos os los ítems ítems de las opcio opciones nes en el ListWi ListWidge dget t con clic clic derech derechoo->ed >edit it ítems ítems.. Luego Luego ponemoslapropiedadcurrentRowdell ponemoslapropiedadcurrentRowdellistWidgeta0. istWidgeta0. Asídefácilseríadiseñarlo,luegohabríaqueañadirlosbotonesdelaventanadediálogotípicos, OKyCancel(QButtonBo OKyCancel(QButtonBox),yde x),yde igualmaneraconect igualmaneraconectarlo arlosconla sconla ventanadediálogo ventanadediálogo,y ,y conectar conectar lasseñales,accepted()departedelosbotones,conaccept()deldiálogo,yrejected()departede
85
losbotones,conreject() losbotones,conreject() deldiálogo.Ya deldiálogo.Ya podríamosusarestediálogo podríamosusarestediálogo enla aplicació aplicacióncompleta, ncompleta, usandodialog.exec()paraejecutarloenmodomodalyquenosdevuelvaunvalorconnuestra selección,QDialog::Accept selección,QDialog::AcceptedóQDialog:: edóQDialog::Rejected. Rejected.
QSplitter Esun Esunwi widg dget etqu quec econ onti tien eneo eotr tros oswi widg dget ets, s,yes yesto tose sest stán ánse sepa para rado dosp spor orse sepa para rado dore res. s.Lo Los s usuariospuedencambiareltamañodeloswidgetsseparados,desplazandolosseparadores.Los widge widgetssepar tsseparado ados,se s,se van ponien poniendounoal dounoal lado lado del otroconf otroconform orme e sevancreandoy sevancreandoy segúnla segúnla disposición. int main(int main(int argc, char *argv[]) { QApplication app(argc, argv); QTextEdit *editor1 = new QTextEdit ; QTextEdit *editor2 = new QTextEdit ; QTextEdit *editor3 = new QTextEdit ; QSplitter splitter( Qt Qt::Horizontal); ::Horizontal); splitter.addWidget(editor1); splitter.addWidget(editor2); splitter.addWidget(editor3); splitter.show(); return app.exec(); }
Lossplitterspueden Lossplitterspueden anidarsehorizo anidarsehorizontale ntales s converticalesy converticalesy crear disposici disposiciones ones máscomplejas. máscomplejas. Los settin setting g pueden pueden guarda guardar r la posic posició ión n de los mismos mismos,, usando usando grupos grupos en la grabac grabación ión de los los mismos. void MailClient::writeSettings() { QSettings settings( "Software Inc." Inc.", , "Mail Client" Client"); ); settings.beginGroup( "mainWindow" ); settings.setValue( "size" "size", , size()); settings.setValue( "mainSplitter" , mainSplitter->saveState()); settings.setValue( "rightSplitter" , rightSplitter->saveState()); settings.endGroup(); } void MailClient::readSettings() { QSettings settings( "Software Inc." Inc.", , "Mail Client" Client"); ); settings.beginGroup( "mainWindow" ); resize(settings.value( "size" "size", , QSize QSize( (480 480, , 360 360)).toSize()); )).toSize()); mainSplitter->restoreState(settings.value( "mainSplitter" ).toByteArray()); rightSplitter->restoreState(settings.value( "rightSplitter" ).toByteArray()); settings.endGroup(); }
¿Cómodiseñar ¿Cómodiseñar consplittersenel consplittersenel QtDesigner?. QtDesigner?. Essimilara Essimilara como disponía disponíamosloswidgets mosloswidgets con layoutsverticalesyhorizontales.Pones2omáswidgetsjuntos,losseleccionas,yenlabarrade herramien herramientassuperio tassuperiorpodemoselegir, rpodemoselegir, “LayoutHorizon “LayoutHorizontally tally inSplitter”o inSplitter”o “LayoutVertic “LayoutVerticallyin allyin Splitter”.
QScrollArea Pro Provee una zonavi onavisi sibl ble(v e(vie iewp wpor ort) t) y 2barra 2barrass de scro scroll ll (ver (verti ticcal y hori horizzontal ntal). ). Par Para añadirbarrasdescrollingaunwidget,solohayquecrearunobjetoQScrollAreayañadirledicho
86
obje objeto to con con setW setWid idge get( t(QW QWid idge get t *). *). Pode Podemo mos s acce accede der r al widg widget et y sus sus miem miembr bros os medi median ante te QScrollArea::viewport()q QScrollArea::viewport()quedevuelveunpunter uedevuelveunpunterodelmismo. odelmismo. int main(int main(int argc, char *argv[]) { QApplication app(argc, argv); IconEditor *iconEditor = new IconEditor; iconEditor->setIconImage( QImage QImage( (":/images/mouse.png" )); QScrollArea scrollArea; scrollArea.setWidget(iconEditor); scrollArea.viewport()->setBackgroundRole( QPalette QPalette::Dark); ::Dark); scrollArea.viewport()->setAutoFillBackground( true true); ); scrollArea.setWindowTitle( QObject QObject::tr( ::tr("Icon "Icon Editor" Editor")); )); scrollArea.show(); return app.exec(); }
QTextEdit QTextEdit y QAbstract QAbstractItemV ItemView iew (y derivados) derivados),, al estar derivados derivados de QAbstract QAbstractScro ScrollAre llArea, a, no requierenestoparaposeerbarras requierenestoparaposeerbarrasdedesplazamiento. dedesplazamiento.
QDockWidgetyQMainWindow Undo Undock ckwi widg dget etes esun unwi widg dget etqu quep epue uede deap apar arccarse arseen enun unla lado dode deun unaM aMai ain nWin Window dow,o puedequedarseflo puedequedarseflotando tandocomounaventana comounaventanaaparte.Lasáreadeenganch aparte.Lasáreadeenganchede ede lasdockwindows, lasdockwindows, enMainWindowson,unaarri enMainWindowson,unaarriba,otraabajo, ba,otraabajo,yunaacadaladodelc yunaacadaladodelcentralWidget. entralWidget. Esta Estasven sventa tana nastie stiene ne su prop propio io títu título lo incl inclus usoeng oengan anch chad adas as alaprinc alaprincip ipal al,y ,y el usua usuari rio o puedemoverlassolocogiendodeltítuloyarrastrandodeellas.Puedensercerradascomouna ventananormal.Conlafunc ventananormal.ConlafunciónsetWidget(Q iónsetWidget(QWidget),seañadeunwi Widget),seañadeunwidgetaundockwi dgetaundockwindow. ndow. QMainWindow, tiene una función para añadir docks widgets, que se llama addDockWidget(zona,widget). QDockWidget *shapesDockWidget = new QDockWidget (tr("Shapes" (tr( "Shapes")); )); shapesDockWidget->setWidget(treeWidget); shapesDockWidget->setAllowedAreas( Qt Qt::LeftDockWidgetArea ::LeftDockWidgetArea | Qt Qt::RightDockWidgetArea); ::RightDockWidgetArea); addDockWidget( Qt Qt::RightDockWidgetArea, ::RightDockWidgetArea, shapesDockWidget);
MDI Toda Todaapl aplic icac ació iónqu nquede edent ntro rodesu desu vent ventan anace acent ntra ralpu lpued edaab aabri rirva rvari rios osdoc docum umen ento tosala sala vez, ez, es llam llamad ada a una una apli apliccaci ación MDI. MDI. Se hac hace hac haciendo endo que la clas lase QWo QWorkspa kspacce sea sea el centralWidget,yentoncescadado centralWidget,yentoncescadadocumentoqueseabr cumentoqueseabra,queseahijodeQ a,queseahijodeQWorkspace. Workspace. Suel Sueles eser ermu muyt ytíp ípic icoq oque ueun unaa aapl plic icac ació iónM nMDI DI,p ,pro rove vead adeu eunm nmen enuW uWin indo dow, w,qu quep eper ermi mite te manejarlosdistintosdocumentos,yactivarlosodesactivarlos,yesaopciónsuelesercheckable indicandoencadamomentolav indicandoencadamomentolaventanadeldocumento entanadeldocumentoqueestáactiva. queestáactiva. MainWindow::MainWindow() { workspace = new QWorkspace ; setCentralWidget(workspace); connect(workspace, SIGNAL SIGNAL(windowActivated( (windowActivated( QWidget *)), this, this , SLOT SLOT(updateMenus())); (updateMenus())); createActions(); createMenus(); createToolBars(); createStatusBar(); setWindowTitle(tr( "MDI Editor" Editor")); )); setWindowIcon( QPixmap QPixmap( (":/images/icon.png" )); }
87
Este es un constructor típico de una aplicación MDI. Conectamos la señal windowActivated()conunslotnuestroqueprogramaremosqueactulizaráelcontenidodelos menús(porejemplo,haciendotogg menús(porejemplo,haciendotoggleenelmenúWindo leenelmenúWindow). w). Cada Cadave vezq zque uese sepu puls lsae aene nelm lmen enúF úFic iche hero ro-> ->Nu Nuev evo, o,no nose sebo borr rrar aráe áeld ldoc ocum umen ento toac actu tual al,s ,si i noquesecrearáunonuevodev noquesecrearáunonuevodevolviendounpun olviendounpunteroalmismo. teroalmismo. Elco Elcons nstr truc ucto torde rdelos loswid widge gets tsdoc docum umen ento tosqu squese eseabr abren en,de ,debe bedeac deacti tiva varel relatr atrib ibut utode ode borradoalcerrar,paraevitar borradoalcerrar,paraevitarlosmemoryleaks losmemoryleaks.. setAttribute( Qt Qt::WA_DeleteOnClose); ::WA_DeleteOnClose);
Para pod poder manejar cada documento, es necesario poder recuperar un puntero al mismo,locualseríaimposib mismo,locualseríaimposiblesinelusodeu lesinelusodeuncastingdin ncastingdinámico. ámico. Editor *MainWindow::activeEditor() { return qobject_cast
Aquí,lafunciónactiveEdi Aquí,lafunciónactiveEditordeMainWin tordeMainWindow,puederecuperarelpun dow,puederecuperarelpunteroadichodoc teroadichodocumento(en umento(en estecasoesunEditor),haciendouncastingalvalordevueltoporQWorkspace::activeWindow(), queesunQWidget*.SinohayactivaningunaventanaentoncesdevolveráunNULLpointer,por lo que esimportan esimportanteque teque cuando cuando sellame a este este método método desdecualq desdecualquie uier r parte, parte, seobserve seobserve si devuelveunvalornulo. Enes Eneste tete tema mava vamo mosa saha hace cerq rque uenu nues estr traa aapl plic icac ació ióne nedi dito tora rade dete text xtod odel elte tema ma20 20,p ,pue ueda da ser MDI, MDI, pero pero lo haremo haremos s usando usando QMdiAr QMdiArea ea y QMdiSu QMdiSubWi bWindo ndow, w, como como nuevo nuevo enfoqu enfoque e a las aplicacionesMDI.
88
89
TEMA22 BASESDEDATOS
Lagr Lagran anma mayo yorí ríad adea eapl plic icac acio ione nesd sdeg eges esti tión ón,s ,sue uele leng ngua uard rdar arto toda dala lain inge gent ntec ecan anti tida dadd dde e datosquemanejaenunmotordebasededatos.Qtensuversiónprivativaincluyelosdriversde lamayoríademotoresdedatos.QtensuversiónOpenSource,noincluyeaquellosquetienen licen licenci cia. a. Para Para aquell aquellos os usuari usuarios os acostu acostumbr mbrado ados s a usar usar la sintax sintaxis is SQL, SQL, Qt provee provee de la clase clase QSqlQuer QSqlQuery.Parausuar y.Parausuariosquepref iosqueprefieren ierenevitaresto evitaresto,ydeseantrabaj ,ydeseantrabajaraun araun nivelmásalto,hay2 nivelmásalto,hay2 modelo modelos s inc inclui luidos dos en las clases clases QSqlTa QSqlTable bleMod Model el y QSqlRe QSqlRelat latio ional nalTab TableM leMode odel, l, las cuales cuales ya mencionamoseneltema16.
ConectareinterrogarconSQL Vamos a empezar tratando la primera forma de trabajar con bases de datos, donde conectaremosconunabasededatos conectaremosconunabasededatosMySQL,ylai MySQL,ylainterrogaremosmedi nterrogaremosmediantelasintaxi antelasintaxisSQL. sSQL. bool createConnection() { QSqlDatabase db = QSqlDatabase ::addDatabase( "QMYSQL" "QMYSQL"); ); db.setHostName( "localhost" ); db.setDatabaseName( "megafonia" ); db.setUserName( "root" "root"); ); db.setPassword( "admin" "admin"); ); if (!db.open()) { QMessageBox ::critical( 0, QObject QObject::tr( ::tr("Database "Database Error" Error"),db.lastError().text()); ),db.lastError().text()); return false false; ; } return true true; ; }
Esta Estaesu esuna naco cone nexi xión ónala alaba base seded dedat atos osMy MySQ SQL, L,ene enelh lhos ostl tloc ocal al,c ,con onno nomb mbre remeg megaf afon onia ia,, conelusuarioypassworddelusuarioquetengaaccesoalamisma,deacuerdoalasqueriesSQL quevamosaenviarle. Los Losig igui uien ente tes ser erá áen envi viar arle leu una naq que uery ry, ,y yes esto tos se eha hace cea así sí: : QSqlQuery query; query.exec( "SELECT almacen, ntiendas FROM almacenes" ); while (query.next()) { QString almacen = query.value( 0).toString(); int ntiendas = query.value( 1).toInt(); qDebug("Nombre: qDebug("Nombre: %s - Tiendas: %d" %d",qPrintable(almacen),ntiendas); ,qPrintable(almacen),ntiendas); }
Como Comop pue uede des sve ver rla laf fun unci ción óne exe xec( c() )ej ejec ecut uta ael elq que uery ry,,ne next xt() ()r rec ecor orre ree el lpu punt nter ero opo por rla lat tab abla la respuesta,yvalueesunalistaQVariant,dondeloscampostieneníndicesdel0enadelante,de estaformavalue(0)contendríaelcampoalmace estaformavalue(0)contendríaelcampoalmacenqueeselprimeroenlar nqueeselprimeroenlarespuesta,yenvalue(1) espuesta,yenvalue(1) estaríaelcampontiendas. Para Paraqu quees eeste tecó códi digo goco compi mpile lebi bien en,es ,esne nece cesa sari rioq oque uesea seaña ñada daelm elmód ódul ulos osql qlalp alpro roye yect cto, o, esdeciralfichero*.proc esdeciralfichero*.proconlalinea: onlalinea: QT+=sql
90
MVCaplicadoaBasesdeDatos Como Comoya yavi vimo mose sene nelt ltem ema1 a16, 6,po pode demo mosc scom ombi bina naru runa navi vist stay ayun unmo mode delo loqu quea eacc cced edaa aala la basededatoscomosufuente.AquílavistamástípicaparaverunatabladedatosesQTableView, yelmodelopuedeserQSqlTableModel,si yelmodelopuedeserQSqlTableModel,sivamosaverun vamosaverunatablaenconc atablaenconcreto,sinmás. reto,sinmás. Las func unciones nesmás mást tíípic picasd asde ees esttemo emode delo lo, ,so son n: setTable(QStringtableName)->quef setTable(QStringtableName)->quefijalatabladelab ijalatabladelabasededatosafij asededatosafijar ar setEditSt setEditStrateg rategy(st y(strateg rategy)->Descri y)->Describelaestrateg belaestrategiaausarparaactuali iaausarparaactualizarlosdatos zarlosdatosde de labasede datos,quepuedeser:OnFieldChange(todocambioenlavistaesinmediatoenlabasededatos), OnRowChange(seguardanlosdatosalc OnRowChange(seguardanlosdatosalcambiardefilaor ambiardefilaoregistro),OnMan egistro),OnManualSubmit(seguardaen ualSubmit(seguardaen unacachehastaqueelprogramaejecutaun unacachehastaqueelprogramaejecutaunsubmitAll()oun submitAll()ounrevertAll()). revertAll()). select()->Rellenaelmodeloconlo select()->Rellenaelmodeloconlosdatosdelatabla. sdatosdelatabla. setH setHea eade derD rDat ata( a(in int t sect sectio ion, n, Qt:: Qt::or orie ient ntat atio ion, n, QVar QVaria iant nt head header er) ) -> Marc Marca a los los nomb nombre res s de las las cabeceras. clear( clear() ) -> borra borra el modelo modelo de todos todos los datos datos que previ previame amente nte tuvier tuviera a cargad cargados os en su caché caché (buffer). Para Paralos lossig sigui uien ente tesej sejem empl plos osvam vamos osausa ausarun runaba abase sededa dedato tosgr sgrab abad adaen aenSQL SQLit iteen eenun un ficheroquellamaremosdatabase.db,en ficheroquellamaremosdatabase.db,enlosejercic losejerciciosveremosc iosveremoscomoesestabasededato omoesestabasededatos. s. #include
91
Pero Perolo lomá másn snor orma male lesq sque uete teng ngam amos osva vari rias asta tabl blas as,r ,rel elac acio iona nada dase sent ntre reel ella lasm smed edia iant nteu eun n identificadornumérico(enSQLitemedianteelrowid),yloquequeremosespresentarunatabla donde donde aparecela aparecela relaciónentreellas. relaciónentreellas. Paraellousaremos Paraellousaremos elmodeloQSqlRelatio elmodeloQSqlRelationalTa nalTableMo bleModel. del. Veamosunejemplosimilarcon Veamosunejemplosimilarconlamismabasededatos lamismabasededatosylas3tablas. ylas3tablas. #include
92
93
TEMA23 REDESTCP/IP ClienteTCP(QTcpSocket) Explicar los protocolos TCP/I P/IP no es el objetivo de este libro, por lo que se da por supu supues esto to el prev previo io cono conoci cimi mien ento to y dife difere renc ncia iaci ción ón entr entre e prot protoc ocol olos os TCP TCP y UDP. UDP. Así Así que que comenzaremosconlaimplementació comenzaremosconlaimplementacióndeunclient ndeunclienteTCP. eTCP. Set Setra rata tad de eun uns ser ervi vido dor rqu queu euna nas sir irve vep por oru un npu puer erto toT TCP CP,,fr fras ases esd de ela laf for ortu tuna na,,co como mol las asd de e losrollitosdeprimavera.Asíquecuandoelclienteseconecta,elservidorlesirveunafraseal azarsobresufortuna. Vamos amos a usar usar QDat QDataS aStr trea eam m par para env enviar los los dat datos des desde el servi ervido dorr, y tamb ambién para para recogerlasenelcliente.Elflujodedatosesmuysenc recogerlasenelcliente.Elflujodedatosesmuysencillo,tansolollev illo,tansolollevacomocabeceraunquint16 acomocabeceraunquint16 conlalongituddelmensajequel conlalongituddelmensajequelesigue,luegov esigue,luegovieneunacadenaco ieneunacadenaconlafrase. nlafrase. Bien, en programación de sockets hay 2 aproximaciones distintas en cuanto a si la conexió conexión n esbloqueanteo esbloqueanteo no-bloqu no-bloqueante eante.Una .Una conexión conexión bloqueant bloqueante,es e,es aquellaque aquellaque espera espera yno hacenadahastaqueelservidorharespondidoyhasidoestablecidaporcompleto.Sueleusarse en aplicaciones multi-hilo o de consola donde se hará uso de funciones como QTcpSocket::waitForConnected.Ennuestrocaso,vamosausarlaaproximaciónno-bloqueante, donde donde llamar llamaremo emos s a la funci función ón conne connectT ctToHo oHost( st(), ), y seguir seguiremo emos s a lo nuestr nuestro, o, y mediant mediante e una conexióndeQt,unavezelso conexióndeQt,unavezelsocketseestablezcaQ cketseestablezcaQTcpSocketemitir TcpSocketemitirálaseñalconnec álaseñalconnected(). ted(). Vamo Vamosa sade deri riva varl rlac acla lase seCl Clie ient nted edeQ eQDi Dial alog og,y ,yle leva vamo mosa saañ añad adir irlo loss sslo lots tsne nece cesa sari rios osy yla las s propiedadesprivadasnecesarias. class Client : public QDialog { Q_OBJECT public : public: Client(QWidget Client(QWidget *parent = 0); private slots slots: : void requestNewFortune(); void readFortune(); void displayError( QAbstractSocket ::SocketError socketError); void enableGetFortuneButton(); private : private: QLabel *hostLabel; QLabel *portLabel; QLineEdit *hostLineEdit; QLineEdit *portLineEdit; QLabel *statusLabel; QPushButton *getFortuneButton; QPushButton *quitButton; QDialogButtonBox *buttonBox; QTcpSocket *tcpSocket; QString currentFortune; quint16 blockSize; };
Apartedeloscomponentesvisuales,v Apartedeloscompone ntesvisuales,vamosausarlacadenacur amosausarlacadenacurrentFortuneparagu rentFortuneparaguardarlafrase ardarlafrase actual,ysutamañoenblockSiz actual,ysutamañoenblockSize.Losslotsq e.Losslotsquevamosausarso uevamosausarson: n: requestNewFortune->quevaaser requestNewFortune->quevaaserejecutadocuandose ejecutadocuandosehagaclicenel hagaclicenelbotónde“Vermi botónde“Vermifortuna”. fortuna”.
94
readFortune->queserádisparadocuandoelsocket readFortune->queserádi sparadocuandoelsocketTCPmedianteelQDataStream,envi TCPmedianteelQDataStream,envielaseñal elaseñal QIODev QIODevic ice:: e::rea readyR dyRead ead(), (), dic dicien iendo do que nuevo nuevos s datos datos están están dispon disponibl ibles es en el dispos dispositi itivo vo de entrada/salida. disp displa layE yErr rror or -> va a esta estar r cone conect ctad ado o con con la seña señal l erro error( r() ) que que se disp dispar ara a si hay hay erro error r en la conexión. enableGetFortuneButton->este enableGetFortuneButton->esteesunmeroslotde esunmeroslotdeGUIparaquesehabil GUIparaquesehabiliteunbotón. iteunbotón. Vamo Vamos sah ahor ora aa apo por rla lai imp mple leme ment ntac ació ión nde del lco cons nstr truc ucto tor rqu que ees esd don onde des se ein inic icia iae el lso sock cket et: : Client::Client(QWidget Client::Client( QWidget *parent) :: QDialog QDialog(parent) (parent) { hostLabel hostLabel = new QLabel QLabel(("&IPServidor:" "&IPServidor:"); ); portLabel portLabel = new QLabel QLabel(("&Puerto:" "&Puerto:"); ); // // Buscatodaslasdireccionesdelhostactual QString QString ipAddress; QList QList<
Aquí Aquí a parte parte de los widget widgets s visua visuales les,, inici iniciali alizam zamos os la IP del ordena ordenador dor donde donde estamo estamos s en la LineEdit,tambiénleañadimosunvalidadoralLineEditdelpuertoparaquenoadmitapuertos fueradel fueradel rangode rangode 1-6553 1-65535. 5. Se inici iniciali aliza za el socket socket y sehacen las conexi conexione ones s Qt entre entre slots slots y señalescomentadasantes.
95
Cuan uando se puls pulsael ael botó otónde Ver Mi Fortun rtuna, a, sedis edispar parael slo slot corr orrespo espond ndiiente ente que establecelaconexiónconel establecelaconexiónconelservidordeesta servidordeestamanera: manera: void Client::requestNewFortune() { getFortuneButton->setEnabled( false false); ); blockSize = 0; tcpSocket->abort(); tcpSocket->connectToHost(hostLineEdit->text(),portLineEdit->text().toInt()); }
InicializamoslapropiedadblockSizeconeltamañoa0,luegoabortalaconexiónactualsilahay, reseteandoelsocket,luegocon reseteandoelsocket,luegoconectaconelhos ectaconelhostdandosudirecc tdandosudirecciónIPypuert iónIPypuerto. o. Cuando el socket ya haya sido conectado correctamente, y haya datos nuevos en el dispositivo,entoncesse dispositivo,entoncesselanzaráelslotc lanzaráelslotcorrespondien orrespondiente: te: void Client::readFortune() { QDataStream in(tcpSocket); in.setVersion( QDataStream ::Qt_4_6 ::Qt_4_6); ); if (blockSize == 0) { if (tcpSocket->bytesAvailable() < (int int) )sizeof sizeof(quint16)) (quint16)) return; return ; in >> blockSize; } if (tcpSocket->bytesAvailable() < blockSize) return; return ; QString nextFortune; in >> nextFortune; if (nextFortune == currentFortune) { QTimer::singleShot( QTimer ::singleShot( 0, this this, , SLOT SLOT(requestNewFortune())); (requestNewFortune())); return; return ; } currentFortune = nextFortune; statusLabel->setText(currentFortune); getFortuneButton->setEnabled( true true); ); }
Losdatos,notienenporquévenirtodosdegolpe,yportantorequieradevariasejecucionesde esteslot,paratenerlostodos.Porello,sieslaprimeralectura(blockSize==0),ysilosbytesque están están prepar preparado ados s para para ser leídos leídos son menosque menosque 2 bytes, bytes, el tamaño tamaño del header header que indic indica a el tamañodelafrase,entoncessalimosdelslotyesperamosaquenosllamenconmásdatos.Sihay más datos, datos, pues leemos leemos primero primero el blockSiz blockSize. e. Luego igualment igualmente e salimos salimos del slot, slot, mientras mientras no estén todoslosdatosdisponi todoslosdatosdisponibles bles.Finalmen .Finalmenteleemoselmensajequerecoge teleemoselmensajequerecogemosenuna mosenuna cadena cadena QString.Sielmensajeeselmismoqueelanteriorquenosdioelservidor,entoncesusamosun Time Timerr de úni único dis dispar paro, para para que disp dispar are e de nuev uevo el slot lot requ equestN estNew ewFo Forrtun tune que que ya describimosantes,pidiendoun describimosantes,pidiendounmensajenuevo. mensajenuevo. Fina Finalm lmen ente tev vam amos osa av ver ere el lcó códi digo goq que uev va aa atr trat atar arl los osd dif ifer eren ente tes ser erro rore res sde dec con onex exió ión: n: void Client::displayError( QAbstractSocket ::SocketError socketError) { switch (socketError) { case QAbstractSocket ::RemoteHostClosedError: break; break ; case QAbstractSocket ::HostNotFoundError: QMessageBox ::information( this this, , "Cliente TCP" TCP", ,"No se encontró el servidor." ); break; break ; case QAbstractSocket ::ConnectionRefusedError: QMessageBox ::information( this this, ,"Cliente TCP" TCP", ,"Conexión rechazada por el cliente. Servidor apagado." ); break; break ; default: default : QMessageBox ::information( this this, , "Cliente TCP" TCP", , QString( QString ("Error: %1." %1.") ) .arg(tcpSocket->errorString())); } getFortuneButton->setEnabled( true true); ); }
96
Nohaycomentariosqueañadiraesto.
ServidorTCP(QTcpSocket) Elser lservido vidorr,es tamb tambiién der derivadode adode QDi QDialo alog.Su úni único slotes lotes sen sendFo dFortun tune, quees queestá tá conectadoalaseñalnewConnection(). class Server : public QDialog { Q_OBJECT public : public: Server(QWidget Server(QWidget *parent = 0); private slots slots: : void sendFortune(); private : private: QLabel *statusLabel; QPushButton *quitButton; QTcpServer *tcpServer; QStringList fortunes; };
Lai aimp mple leme men ntac taciónde ndel lcconstr nstruc ucto tor res esm mu uysi ysimp mple le:: Server::Server( QWidget *parent) : QDialog QDialog(parent) (parent) { statusLabel = new QLabel QLabel; ; quitButton = new QPushButton ("Salir" "Salir"); ); quitButton->setAutoDefault( false false); ); tcpServer = new QTcpServer (this this); ); if (!tcpServer->listen()) { QMessageBox ::critical( this this, , "Servidor TCP" TCP", , QString( QString ("Imposible iniciar servidor: %1." %1.") ) .arg(tcpServer->errorString())); close(); return; return ; } QString ipAddress; QList< QList
97
ApartedeinicializarlaGUIydebuscarlaIPlocalcomoelcliente,solamenteinicialaescuchade clientesconlisten()yco clientesconlisten()yconectalaseñalnewC nectalaseñalnewConnection() onnection()conelslotqu conelslotquevaaenviarlas evaaenviarlasfrases: frases: void Server::sendFortune() { QByteArray block; QDataStream out(&block, QIODevice ::WriteOnly); out.setVersion( QDataStream ::Qt_4_6 ::Qt_4_6); ); out << (quint16) 0; out << fortunes.at(qrand() % fortunes.size()); out.device()->seek( 0); out << (quint16)(block.size() - sizeof sizeof(quint16)); (quint16)); QTcpSocket *clientConnection = tcpServer->nextPendingConnection(); connect(clientConnection, SIGNAL SIGNAL(disconnected()), (disconnected()), clientConnection, SLOT SLOT(deleteLater())); (deleteLater())); clientConnection->write(block); clientConnection->disconnectFromHost(); }
UnaveziniciadoelDataStreamcomounByteArray,primeroponemosenelbufferdelmismola cabeceraa0,luegomedian cabeceraa0,luegomedianteunafunci teunafunciónrando ónrandom,seelijeunafras m,seelijeunafrasealazar,yseenvíaalbuffe ealazar,yseenvíaalbuffer, r, luegorebobinamosalaposición0“out.device()->seek( out.device()->seek(0 0)” ,leponemoseltamañodesólola frase(poresorestamosaltamañodel frase(poresorestamosaltamañodelbloque,eltamaño bloque,eltamañodelacabecera–2by delacabecera–2bytes). tes). Llama laman ndo a next extPend PendiingConn Connec ecti tio on(), n(), se dev devuel uelve el soc socket ket que que ha sido con conecta ectado do. . Cone Conect ctam amos os la seña señal l de disc discon onne nect cted ed al slot slot QObj QObjec ect: t::d :del elet eteL eLat ater er() (), , que que hace hace que que si hay hay desconexióndelsocket,elob desconexióndelsocket,elobjetoquerepresent jetoquerepresentadichaconexió adichaconexiónseaborradopo nseaborradoporQt. rQt. Yapo Yapode demo mose senv nvia iare relb lblo loqu quec ecom ompl plet eto, o,po pord rdic icho hoso sock cket etco cone nect ctad ado, o,yun yunav avez ezhec hecho ho,y ,ya a podemosdesconectaralcliente,y podemosdesconectaralcliente,yseborraráel seborraráelobjetodelsock objetodelsocketcomoacabamosde etcomoacabamosdever. ver.
EmisorUDP(QUdpSocket) Elem Elemis isor orde deda data tagr gram amas asUD UDPv Pvaa aaus usar arun unQT QTim imer erpar parac acad adas aseg egun undo doen envi viar arpaq paque uete tes s porunpuerto.Veamoslaimplementaci porunpuerto.Veamoslaimplementacióncompleta: óncompleta: Sender::Sender( QWidget *parent) : QDialog QDialog(parent) (parent) { statusLabel = new QLabel QLabel( ("Preparado para enviar datagramas por el puerto 45454" 45454"); ); startButton = new QPushButton ("Enviar" "Enviar"); ); quitButton = new QPushButton ("Salir" "Salir"); ); buttonBox = new QDialogButtonBox ; buttonBox->addButton(startButton, QDialogButtonBox ::ActionRole); buttonBox->addButton(quitButton, QDialogButtonBox ::RejectRole); timer = new QTimer QTimer( (this this); ); udpSocket = new QUdpSocket (this this); ); messageNo = 1; connect(startButton, SIGNAL SIGNAL(clicked()), (clicked()), this this, , SLOT SLOT(startBroadcasting())); (startBroadcasting())); connect(quitButton, SIGNAL SIGNAL(clicked()), (clicked()), this this, , SLOT SLOT(close())); (close())); connect(timer, SIGNAL SIGNAL(timeout()), (timeout()), this this, , SLOT SLOT(broadcastDatagram())); (broadcastDatagram())); QVBoxLayout *mainLayout = new QVBoxLayout ; mainLayout->addWidget(statusLabel); mainLayout->addWidget(buttonBox); setLayout(mainLayout); setWindowTitle( "Emisor UDP" UDP"); ); }
void Sender::startBroadcasting() { startButton->setEnabled( false false); ); timer->start( 1000 1000); ); }
98
void Sender::broadcastDatagram() { statusLabel->setText( QString QString( ("Enviando datagrama %1" %1").arg(messageNo)); ).arg(messageNo)); QByteArray datagram = "Mensaje enviado " + QByteArray ::number(messageNo); udpSocket->writeDatagram(datagram.data(), datagram.size(), QHostAddress ::Broadcast, 45454 45454); ); ++messageNo; }
Inic Inicia iamo mos s el time timer r con con star start( t(10 1000 00) ) una una vez vez apre apreta tamo mos s el botó botón n de Envi Enviar ar,, como como tene tenemo mos s conectada conectada la señal del timer, timer, timeout() timeout() conbroadcastDatag conbroadcastDatagram, ram, éstase encarga encarga de escribir escribir el datagramaaenviarconwriteDatagram(datos,tamaño_datos,IP_receptor,puerto).Muysencillo comovemos.
ReceptorUDP(QUdpSocket) Elre Elrece cept ptor or,me ,mera rame ment ntev evin incu cula lael elpue puert rtoy oyla ladi dire recc cció iónI nIPde Pdeli lint nter erfa fazd zder ered edpo pord rdon onde de vaaescucharconbind().AquídenuevoconectamosQIODevice::readyRead(),quenosindicaque haynuevosdatosesperandoserleí haynuevosdatosesperandoserleídos,connuest dos,connuestroslotdelect roslotdelecturaprocessPending uraprocessPendingDatagrams. Datagrams. Esto Estolo loha hace ceme medi dian ante teun unbu bucl cleu eusa sand ndoh ohas asPe Pend ndin ingD gDat atag agra rams ms() ()pa para rasa sabe bers rsia iaún únqu qued eda a algo algo por por proc proces esar ar.. La func funcio ion n pend pendin ingD gDat atag agra ramS mSiz ize( e() ) nos nos devu devuel elve ve los los byte bytes s que que qued quedan an pendientes pendientes de lectura.Con lectura.Con readDatagr readDatagram(dat am(datos,tamaño)leemos os,tamaño)leemos losdatos que se pondrán pondrán en QByteArray::data(). Veamos la implementación: Receiver::Receiver( QWidget *parent) : QDialog QDialog(parent) (parent) { statusLabel = new QLabel QLabel( ("Escuchando mensajes enviados" ); quitButton = new QPushButton ("Salir" "Salir"); ); udpSocket = new QUdpSocket (this this); ); udpSocket->bind( 45454 45454, , QUdpSocket ::ShareAddress); connect(udpSocket, SIGNAL SIGNAL(readyRead()), (readyRead()), this this, , SLOT(processPendingDatagrams())); SLOT (processPendingDatagrams())); connect(quitButton, SIGNAL SIGNAL(clicked()), (clicked()), this this, , SLOT SLOT(close())); (close())); QHBoxLayout *buttonLayout = new QHBoxLayout ; buttonLayout->addStretch( 1); buttonLayout->addWidget(quitButton); buttonLayout->addStretch( 1); QVBoxLayout *mainLayout = new QVBoxLayout ; mainLayout->addWidget(statusLabel); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); setWindowTitle( "Receptor UDP" UDP"); ); } void Receiver::processPendingDatagrams() { while (udpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(udpSocket->pendingDatagramSize()); udpSocket->readDatagram(datagram.data(), datagram.size()); statusLabel->setText( QString QString( ("Datagrama recibido:\"%1\"" ).arg(datagram.data())); } }
Conlaayudadeltemasiguiente,quetratadelaprogramaciónconcurrenteomulti-hilo, vamosa vamosa implementa implementarelejemplodelserv relejemplodelservidor idorTCP,peroestavezconmúlti TCP,peroestavezconmúltipleshilo pleshilosparapoder sparapoder manejaramuchosclientesalavez.
99
TEMA24 PROGRAMACIÓNCONCURRENTE
Unaa Unaapl plic icac ació iónn nnor orma malm lmen ente teus usau auns nsol oloh ohil ilod odee eeje jecu cuci ción ón,d ,don onde dell llev evaa aaca cabo boun unas asol ola a tare tarea a a la vez. vez. Pero Pero exis existe te la posi posibi bili lida dad d de que que la tare tarea a prin princi cipa pal, l, gene genere re nuev nuevos os hilo hilos s de ejecuciónindependientes,yqueest ejecuciónindependientes,yqueestossecomuniquen ossecomuniquenconlatareaprinci conlatareaprincipal,oquetrabaj pal,oquetrabajenjuntos enjuntos enunamismacosa,teniendoquesincronizarsutrabajo.Noesnuestraidea,explicareltrasfondo delaprogramaciónmulti delaprogramaciónmulti-hil -hiloen oen estelibro,yaqueeso estelibro,yaqueeso perteneceaun perteneceaun bagajequese bagajequese suponeya suponeya adquir adquirido ido,, lo que sí vamos vamos es a explic explicar ar de qué manera manera Qt implem implement enta a las aplica aplicaci cione ones s más típicasmulti-hilo.
QThread Esta Estaes esl la acl clas ase equ que epr prov ovee eed deh ehil ilos osd de ema mane nera ram mul ulti tipl plat ataf afor orma ma..Ca Cada dao obj bjet eto ode dee est sta acl clas ase e representaunhilodeejecucióndiferentealosdemásydiferentealprincipalqueseiniciayse term termin ina a con con main main() (). . Un hilo hilo comp compar arte te dato datos s con con otro otros s hilo hilos, s, pero pero se ejec ejecut uta a de mane manera ra indep independ endien iente te de los demás. demás. En vez de comenz comenzar ar en main() main() como como el hilo hilo princi principal pal,, los hilos hilos QThreadscomienzanyterminanensufunciónrun().Asíquerun()ejecutaríaunhiloeiniciaría supropiobucledeeventoscon supropiobucledeeventosconexec(),comohacema exec(),comohacemain(). in(). Paracreartuspropioshilos,sólohayquederivarunaclasedeQThreadyreimplementar lafunciónrun(),conl lafunciónrun(),conlastareasqueestehil astareasqueestehilovayaaejecutar ovayaaejecutar. . class MyThread : public QThread { public: public : void run(); }; void MyThread::run() { QTcpSocket socket; // conecta las señales de QTcpSocket ... socket.connectToHost(hostName, portNumber); exec(); }
Esto Estoser sería íaunhi unhilo loque queco cone nect ctaun aunsoc socke ketco tconun nunhos hosty tylue luego goin inic icia iasupr suprop opio iobuc bucle lede de eventos.Paracomenzarlaejecucióndeunhilo,unavezsehainstanciadoenelhiloprincipal,es usandolafunciónstart().Recuerdainstanciarcadahilo,poniendocomoargumentopadrealhilo delquenace,deesa manera,cuandoelpadrefinali manera,cuandoelpadrefinalice,todos ce,todoslos los hiloscreadosdeél hiloscreadosdeél seborrarán seborrarán también,evitandomemoryleaks.Laejecucióndeunhiloacabacuandosesalederun(),igualque unprogramacuandoterminamain(). QThr QThrea eadn dnot otif ific icac acon onse seña ñale lesc scua uand ndou ounh nhil ilo, o,co comi mien enza za(s (sta tart rted ed() ())o )ofi fina nali liza za(f (fin inis ishe hed( d()) )).. TambiénsepuedenusarlosgettersisFinished()oisRunning()parasabersihafinalizadooaún estacorriendo. Nosepuedenusarwidgetsenunhilo.SísepuedenusarQTimersysockets,ysepueden conectarseñalesyslotsentr conectarseñalesyslotsentrediferenteshi ediferenteshilos. los. Paraverunusorealdeestaconcurrenciadehilos,vamosavercomoquedaríaelcódigo del del serv servid idor or TCP TCP del del tema tema ante anteri rior or,, de mane manera ra que que pued pueda a admi admiti tir r múlt múltip iple les s cone conexi xion ones es de clientes,cadaunaenunhiloi clientes,cadaunaenunhiloindependiente. ndependiente.
100
Comoloswidge Comoloswidgets, ts, hemosdich hemosdichoque oque no pueden pueden correr correr enun hilo, hilo, han decorreren decorreren la tareaprincipalqueiniciamain(),hadesepararseelcódigoquegeneratodalainterfazgráficay queinicialaescuchaconlisten(). FortuneThread::FortuneThread( int socketDescriptor, const QString &fortune, QObject *parent) : QThread QThread(parent), (parent), socketDescriptor(socketDescriptor), text(fortune) { } void FortuneThread::run() { QTcpSocket tcpSocket; if (!tcpSocket.setSocketDescriptor(socketDescriptor)) emit error(tcpSocket.error()); return; return ; }
{
QByteArray block; QDataStream out(&block, QIODevice ::WriteOnly); out.setVersion( QDataStream ::Qt_4_0 ::Qt_4_0); ); out << (quint16) 0; out << text; out.device()->seek( 0); out << (quint16)(block.size() - sizeof sizeof(quint16)); (quint16)); tcpSocket.write(block); tcpSocket.disconnectFromHost(); tcpSocket.waitForDisconnected(); }
Ahora, Ahora, hemos hemos cread creado o una clase clase hilo hilo del servi servidor dor que envía envía las frase frases s y en run() run() es donde donde se encargadehacertodoloquehace,desdequeesdespertadoparaconectarseconelcliente,hasta que que enví envía a la fras frase, e, se desc descon onec ecta ta y espe espera ra la desc descon onex exió ión n (mod (modo o bloq bloque ueo) o). . Crea Creamo mos s un QTcpSocket y lo asociamos al descriptor de socket que nos va a pasar el servidor (socketDescriptor),queidentificaacualdelosclientesnosvamosaconectar.Bien,estoesloque haceelhiloquedespachaauncliente. FortuneServer::FortuneServer( QObject *parent) : QTcpServer (parent) { fortunes << tr("Has tr("Has llevado una vida de perros. Bajate del sofa." sofa.") ) << tr("Tienes tr("Tienes que pensar en el ma_ana." ma_ana.") ) << tr("Te tr("Te ha sorprendido una sonido fuerte." fuerte.") ) << tr("Tendr_s tr("Tendr_s hambre dentro de una hora." hora.") ) << tr("Tienes tr("Tienes nuevo correo." correo."); ); } void FortuneServer::incomingConnection( int socketDescriptor) { QString fortune = fortunes.at(qrand() % fortunes.size()); FortuneThread *thread = new FortuneThread(socketDescriptor, fortune, this this); ); connect(thread, SIGNAL SIGNAL(finished()), (finished()), thread, SLOT SLOT(deleteLater())); (deleteLater())); thread->start(); }
Yaquíestálaparteprincipaldelservidor, Yaquíestálaparteprincipaldelservi dor,y y estavezsederivadeQTcpServe estavezsederivadeQTcpServer,asíelcódigo r,asíelcódigoque que pondríamo pondríamos s enel hilo principa principal,crearíaun l,crearíaun servidor servidor FortuneSe FortuneServer rver alque ponemosa ponemosa escuchar escuchar con con list listen en() (),, cuan cuando do un clie client nte e cone conect ctar ara a su sock socket et con con él, él, auto automá máti tica came ment nte e se envi enviar aría ía la ejec ejecuc ució ión n a inco incommi mming ngCo Conn nnec ecti tion on,, que que pasa pasa como como pará paráme metr tro o el ente entero ro que que repr repres esen enta ta el descriptordelsocketconectado.Yenestafuncióncreamosunhilodeejecuciónparadespachar al client cliente, e, como como hemos hemos visto visto arrib arriba, a, que pasarí pasaría a el descri descripto ptor r del socket socket TCP para para que no se confundadecliente, confundadecliente, yla frasea frasea enviarle. enviarle. Tambiénconecta Tambiénconectamoslaseñalfinish moslaseñalfinished()de ed()de dicho hilo consuborrado,para consuborrado,para quecuandoacabesuejecució quecuandoacabesuejecuciónsea nsea borrado,y borrado,y finalmen finalmenteinicia teiniciamosdicho mosdicho hiloconstart().
101
Sincronizacióndehilos Yahem Yahemos osvis visto toelej elejem empl plomá omásse ssenc ncil illo lodehi dehilo los, s,don donde deunpr unprog ogra rama mapri princ ncip ipal al,ge ,gene nera ra hilos hilos que hacen hacen cosas cosas difere diferente ntes, s, y donde donde no hay intera interacci cción ón entre entre los los hilos. hilos. Pero, Pero, hay casos casos dond donde e vari varios os hilo hilos s pued pueden en comp compar arti tir r dato datos s o códi código go de mane manera ra que que pued pueda a esto esto afec afecta tar r al resultado. Vamo Vamos sap apon oner er2 2su supu pues esto tos sre resu suel elto tosd sdem eman aner erad adif ifer eren ente te.S .Sup upon onga gamo mos squ quee eelc lcód ódig igo ode de varioshilosaccedealavezalcódigoqueaumentauncontadorco varioshilosaccedealavezalcódigoqueaumentauncontadorcomún.Podríaocurri mún.Podríaocurrirqueunhilo rqueunhilo accedaalvalorantesdeincrementarlo,mientrasotrohiloyaloestaincrementando,demanera queambosdejaríanelmismoresultadoenelcontador,cuan queambosdejaríanelmismore sultadoenelcontador,cuandodebierancontars dodebierancontarseelpasodelos2 eelpasodelos2 hilosde hilosde maneraindepen maneraindependien diente.Laúnicaform te.Laúnicaformade ade resolveresto resolveresto,es ,es usarun usarun Mutex,esdeciruna Mutex,esdeciruna zonabloq zonabloquea ueada, da, a dondesólo dondesólo unhilo puedeentr puedeentrara ara lavez, deestamanera deestamanera elhilo que entra, entra, bloqueaesazona,sóloélrecogeelvalordelcontadorahora,yloaumenta,luegosaledelazona, permitiendoqueotrohiloen permitiendoqueotrohiloentre,yasí,s tre,yasí,seevitaríaelpro eevitaríaelproblemainicial blemainicial.. En Qt esto esto es hec hecho con con la clase lase QMut QMutex ex,, quese quese dec declar lararí aríacomo acomo pri privadade adadent ntro ro del del QThread,yllamandoalafunciónl QThread,yllamandoa lafunciónlock()seproduceelbl ock()seproduceelbloqueo,yllamando oqueo,yllamandoalafunciónunl alafunciónunlock()el ock()el desbloqueodelazona. void Thread::run() { mutex.lock(); counter++; mutex.unlock(); }
ParamáscomodidadsecreoQMutexLocker(QMutex*)queelobjetoquecrea,alnacerbloq ParamáscomodidadsecreoQMutexLocker(QMutex*)queelobj etoquecrea,alnacerbloqueael ueael mutex, mutex, y al borrar borrarse, se, desblo desbloque quea a el mutex. mutex. De esta esta manera manera el mismo mismo códig código o quedar quedaría ía así de simple: void Thread::run() { QMutexLocker locker(&mutex); counter++; }
El segu segund ndosu osupu pues esto to,se ,se trat tratadel adel típi típico co prob proble lema ma delpr delprod oduc ucto torr-co cons nsum umid idor or,do ,dond nde2 e2 hiloscompa hiloscomparte rten n un mismo mismo buffer buffer de memor memoria, ia, donde donde unescribey unescribey elotrolee de lasiguient lasiguiente e manera.Elhiloproductor,escribeendichobufferhastaquellegaasufinal,yunavezallívuelve de nuevo nuevo al princ principi ipio o para para seguir seguir escri escribie biendo ndo.. El hilo hilo consum consumido idor, r, lee lo que el otro otro escrib escribe e conformeesproducidoylo conformeesproducidoylosacaporlasalida sacaporlasalidaestándardeerror(c estándardeerror(cerr). err). Sius Siusár áram amos osso sola lame ment nteu eunm nmut utex expa para rabl bloq oque uear arel elac acce ceso sosi simu mult ltán áneo eoal albu buff ffer er,b ,baj ajar aría íala la productividaddelmismo,yaquenopermitiríamosaccederalbufferaunodeloshilos,mientras elotro,estátrabajandoenél,cuandorealmentenohab elotro,estátrabajandoenél,cuandorealmentenohabríaproblemaalguno,si ríaproblemaalguno,sicadaunotrabajara cadaunotrabajara enzonasdiferentesentodomomento. Para resolverlo vamos a usar 2 QWaitCondition y 1 QMutex. Veamos antes qué es QWaitCondition. QWai WaitCo tCondi ndition ion, prov provee ee una una vari ariable able condi ondici ció ón par para sin sincro croniz nizar hil hilos. os. Los Los hilo hiloss se quedanenesperacuandolleganalafunciónwait(&mutex).Waitloquehaceesqueunmutex previ previame amente nte bloque bloqueado ado,, esdesbloqu esdesbloquead eado o y elhilo, sequeda enespera,hasta enespera,hasta que otro otro hilo hilo lo desp despie iert rte, e, cuan cuando do es desp desper erta tado do,, el mute mutex x vuel vuelve ve a bloq bloque uear arse se y el hilo hilo sigu sigue e la ejec ejecuc ució ión n desp despué ués s de wait wait() ().. Para Para desp desper erta tar r a los los hilo hilos s que que espe espera ran, n, un hilo hilo exte extern rno o (por (por ejem ejempl plo, o, el principal),lohaceusandowakeAll()paradesper principal),lohaceusandowakeAll()paradespertarlosatodoso tarlosatodosowakeOne()paradespertarsól wakeOne()paradespertarsóloa oa unodeellos,elordenenqueest unodeellos,elordenenqueestoocurreesaleato oocurreesaleatorioyno rioynosepuedecontrolar sepuedecontrolar. .
102
Vamos amos ento enton nces aho ahora areso aresollver ver el pro problem blemade adell pro product ductor or ycon yconsumi sumido dorr,para ,para ell ello vamosadefinirlassiguientesvariablesglobalesquevanacompartirtodosloshilos,incluidoel principal: const int DataSize = 100000 100000; ; const int BufferSize = 8192 8192; ; char buffer[BufferSize]; QWaitCondition bufferNotEmpty; QWaitCondition bufferNotFull; QMutex mutex; int numUsedBytes = 0;
Data Datasi size zeen enla laca cant ntid idad adde deby byte tesq sque ueva vaa aes escr crib ibir irel elco cons nsum umid idor oren enbu buff ffer er.E .Elt ltam amañ añod odel el bufferesinferi bufferesinferioraesacantidad oraesacantidad,porloqueel ,porloqueel consumido consumidortendráquedarvari rtendráquedarvariasvuelta asvueltas,loque s,loque haceconuncódigocomoéste: buffer[i % BufferSize] = "ACGT" "ACGT"[( [(int int)qrand() )qrand() % 4];
Loqueescribesonaleatoriamentelas LoqueescribesonaleatoriamentelasletrasACGT(basesdelcódi letrasACGT(basesdelcódigogenético). gogenético).Deestamanerano Deestamanerano sesaledelbufferyloescri sesaledelbufferyloescribecíclicament becíclicamente. e. Lacon acondic dición de espe esperrabuf abufferNo erNotE tEmp mptty,ser ,seráseñ áseñalad aladapo aporr elpro lproduct ductor or cuan uando hay haya escr escrit ito o algú algún n dato dato, , desp desper erta tand ndo o al cons consum umid idor or para para que que lo lea. lea. La cond condic ició ión n de espe espera ra buff buffer erNo NotF tFul ull, l, la seña señala lará rá el cons consum umid idor or cuan cuando do haya haya leíd leído o algú algún n dato dato, , desp desper erta tand ndo o al consumido consumidorparaque rparaque sigaescribie sigaescribiendo. ndo.numUsedBy numUsedBytes, tes, vaa serelnúmerodebytesenel buffer buffer que contie contienedatos nedatos escrit escritospor ospor el produc productor tor,, y que nohan sido sido leídos leídos por elconsumid elconsumidor or.. El mutex,seencargarádebloquearelaccesoalavariablecomúnnumUsedBytes,paraquesealeída ycambiadaporunsolohiloencadamomento.numUsedBytesnopodráexcedereltamañodel buffernunca,oelconsu buffernunca,oelconsumidorestarásob midorestarásobrescribiendo rescribiendodatosaúnnol datosaúnnoleídosporelco eídosporelconsumidor. nsumidor. Por Porlo lot tan anto toe el lcó códi digo goq que ueg gob obie iern rna ael elh hil ilo opr prod oduc ucto tor res es: : class Producer : public QThread { public: public : void run(); }; void Producer::run() { qsrand(QTime qsrand(QTime( (0,0,0).secsTo( QTime QTime::currentTime())); ::currentTime())); for (int i = 0; i < DataSize; ++i) { mutex.lock(); if (numUsedBytes == BufferSize) bufferNotFull.wait(&mutex); mutex.unlock(); buffer[i % BufferSize] = "ACGT" "ACGT"[( [(int int)qrand() )qrand() % 4]; mutex.lock(); ++numUsedBytes; bufferNotEmpty.wakeAll(); mutex.unlock(); } }
Inicializalasemillarandomconeltimer. Inicializalasemillarandomconeltimer.AntesdeaccederanumUsedBytesbloqueaco AntesdeaccederanumUsedBytesbloqueaconelmutex nelmutex su acce acceso so,, si se ha llen llenad ado o el buff buffer er comp comple leto to de dato datos, s, ento entonc nces es se qued queda a en espe espera ra de la condiciónbufferNotFullyliberaelmutex.Sinohallenadoelbuffer,liberaelmutex,escribeenla posic posició ión n sigui siguient ente e y de nuevo nuevo antes antes de inc increm rement entar ar numUse numUsedBy dBytes tes (el contad contador or común común del buffer),bloqueestasección,incrementasuvalor,ydespiertaalhiloconsumidor,luegoliberael mutex,ysigueelbucleasuritmo.
103
Elc Elcód ódig igo ode del lhi hilo loc con onsu sumi mido dor rse serí ría aco comp mple leme ment ntar ario ioa aé ést ste: e: class Consumer : public QThread { public: public : void run(); }; void Consumer::run() { for (int i = 0; i < DataSize; ++i) { mutex.lock(); if (numUsedBytes == 0) bufferNotEmpty.wait(&mutex); mutex.unlock(); fprintf(stderr, "%c" "%c", , buffer[i % BufferSize]); mutex.lock(); --numUsedBytes; bufferNotFull.wakeAll(); mutex.unlock(); } fprintf(stderr, "\n" "\n"); ); }
El hilo hilo cons consum umid idor or tamb tambié ién n tien tiene e su bucl bucle e para para leer leer todo todos s los los dato datos s que que el cons consum umid idor or va dejandoenelbuffer,ysacándolosporlasalidaestándardeerror.Antesderevisarelvalorde numUsedBytes,bloquealasecciónconunmutex,sinohaydatosqueleernuevos,entoncesse quedaesperando quedaesperandoquese quese cumplala cumplala condici condicióndeesperabuff óndeesperabufferNo erNotEmpt tEmptyyliberaelmutex.Sino yyliberaelmutex.Sino esasí,tambiénliber esasí,tambiénliberaelmutex,leeelsiguien aelmutex,leeelsiguientedatodelbuffer tedatodelbuffer,y ,y luegobloqueadenuevo luegobloqueadenuevoconel conel mutexelcambiodenumUsedBytes,queesdecrementado,indicandoquehayundatomenospor leer,luegodespiertaalhiloproductorconsuseñalbufferNotFull,yliberaelmutex.Ysigueensu bucletambiénasuritmo. Dees Deesta taman maner era, a,am ambo bosh shil ilos osse seaut autos osin incr cron oniz izan an,s ,sin inpi pisa sars rsee eelu luno noal alot otro ro.S .Sin inemb embar argo go,, existeunasegundaformadehaceresto existeunasegundaformadehacerestodemaneratambiénópt demaneratambiénóptima,usandosemáfor ima,usandosemáforos.EnQtesto os.EnQtesto sehaceconlaclaseQSemaphore.Por sehaceconlaclaseQSemaphore.Portantoveamosantes tantoveamosantescomofunci comofuncionaestaclase. onaestaclase. QSema Semaph pho ore, prov provee ee de un contado tadorge rgene nerral consemá nsemáfforo oro.Mien .Mientr tras as un mute mutex x,sólo ,sólo puede puede ser bloque bloqueado ado una vez, vez, hasta hasta que es desblo desbloque queado ado,, un semáfo semáforo ro puede puede ser bloque bloqueado ado múltiplesvece múltiplesveces.Paraelloelsemáfor s.Paraelloelsemáforoinici oiniciael ael contadorconunnúmero contadorconunnúmeroque que definelosrecurs definelosrecursos os disponibles,ycadavezquesellamaalsemáforoconlafunciónacquire()sepidenrecursos,silo hay, hay, el hilo hilo sigu sigue e la ejec ejecuc ució ión, n, pero pero si no los los hay hay para para adqu adquir irir irlo los, s, ento entonc nces es se bloq bloque uea a la ejecucióndelhiloenesepuntohastaquehayanrecursossuficientesparaseradquiridos,porlo querealmentefuncionacomou querealmentefuncionacomounmutexespecial.Veamo nmutexespecial.Veamosunejemplosenc sunejemplosencillo: illo: QSemaphore bytes(6 bytes( 6); bytes.acquire( 4); bytes.acquire( 3);
Aquí,secreaunsemáforobytesconelcontadora6,cuandoelhilollegaalaprimeraadquisición de 4 byte bytes, s, no hay hay prob proble lemaya maya que que aún aún qued quedan an 2, por por lo que que la ejec ejecuc ució ión n sigu sigue, e, pero pero en la siguientealintentaradquirir3,yanopuede,asíqueelhilosequedaríaahí,hastaquehayabytes disponiblesparaseguir.Paradevolverlosbyteshabríaquellamaralafunciónrelease(intn).Por defectosinoseponeunnúmer defectosinoseponeunnúmeroenacquireo oenacquireoenrelease,sepresupon enrelease,sepresuponequees1. equees1. Dees Deesta taman maner erapo apode demo mosc scre rear ar2se 2semá máfo foro rosen sennue nuest stro roca caso so,c ,con onlo los2c s2cas asos osext extre remo mos s que pararí pararían an la ejecuc ejecució ión n de alguno alguno de los los hilos. hilos. El semáf semáforo oro freeB freeByte ytes s por ejempl ejemplo o podría podría indicarelnúmerodebyteslibre indicarelnúmerodebyteslibresparaserescrito sparaserescritos,y s,y queelproductorpodría queelproductorpodríaadquirir adquirirantesde antesde versipuedeseguirescribiendo,yelconsumidorpodríaliberarconrelease(),unavezlohaya leído.Loinicializarí leído.Loinicializaríamosconunvalo amosconunvalorigualalnúmeroto rigualalnúmerototaldebytesdelbuff taldebytesdelbuffer.Porotrolado er.Porotroladootro otro semáforollamadousedBytespodríaindicarelnúmerodebytesdisponiblesparalectura,elhilo consumido consumidoradquiri radquiriríaantesuno,parapoderleer,ysi ríaantesuno,parapoderleer,ysi nolohay,entoncesquedaríabloq nolohay,entoncesquedaríabloqueado ueado,, hastaquelohubiera, hastaquelohubiera,y y elproductor, elproductor,liberar liberaríabytes íabytesenestesemáfor enestesemáforounavezhayaescri ounavezhayaescritoenel toenel buffer buffer.. Dichosemáf Dichosemáforo oro seinicial seinicializa izarí ría a con elvalor a 0, o loque eslo mismo mismo insta instanci nciado ado por
104
defecto.Deestamanera,cadahilotienesupropiosemáforo,yamboshilosindicanalotrolas nuevassituaci nuevassituaciones ones,, liberando liberando delsemáforo delsemáforo contrari contrario,y o,y adquirien adquiriendodel dodel suyo propio.Así propio.Así verás queelcódigoquedamuchomássenci queelcódigoquedamuchomássencilloqueusandoel lloqueusandoelmutexylas2condi mutexylas2condicionesdeespera. cionesdeespera. const int DataSize = 100000 100000; ; const int BufferSize = 8192 8192; ; char buffer[BufferSize]; QSemaphore freeBytes(BufferSize); QSemaphore usedBytes; class Producer : public QThread { public: public : void run(); }; void Producer::run() { qsrand(QTime qsrand(QTime( (0,0,0).secsTo( QTime QTime::currentTime())); ::currentTime())); for (int i = 0; i < DataSize; ++i) { freeBytes.acquire(); buffer[i % BufferSize] = "ACGT" "ACGT"[( [(int int)qrand() )qrand() % 4]; usedBytes.release(); } } class Consumer : public QThread { public: public : void run(); }; void Consumer::run() { for (int i = 0; i < DataSize; ++i) { usedBytes.acquire(); fprintf(stderr, "%c" "%c", , buffer[i % BufferSize]); freeBytes.release(); } fprintf(stderr, "\n" "\n"); ); }
Comprendid Comprendidoel oel mecanismo mecanismo quehemosexplicadode quehemosexplicadode lossemáforos, lossemáforos, suiniciación suiniciación,adquisic ,adquisicióny ióny liberación,nohaymucho liberación,nohaymuchoquecomentardeeste quecomentardeestecódigo. código. Buen Buenoy oyhe hemo mosd sdej ejad adop opar arae aelf lfin inal al,e ,elh lhil ilop opri rinc ncip ipal al,q ,que ueen enam ambo bosc scas asos osse serí ríae aelm lmis ismo mo.. Desde main(),seiniciar main(),seiniciaríanambos íanambos hilosconstart(),yluegosellamaríaenambosa hilosconstart(),yluegosellamaríaenambosa wait(),para wait(),para quenoacabelafunciónmain()ymueranloshilosantesdequeterminensutrabajo.Loquehace esafunción(wait)esesperaraq esafunción(wait)esesperaraquedichohiloac uedichohiloacabe,yunavezac abe,yunavezacaba,devuelve aba,devuelvetrue true. int main(int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); Producer producer; Consumer consumer; producer.start(); consumer.start(); producer.wait(); consumer.wait(); return 0; }
Amboswait()seejecutanunodetrásdelotro,yaquesonhilosdiferentes,yseesperaaqueestos acaben,paraterminarlaejecucióndemain().
105
TEMA25 GRÁFICOSEIMPRESIÓN
Los gráficos 2D en Qt están basados en QPainter, que pue puede dibujar desde formas geométricascomo,puntos,lineas,rectángulos,etc,hastamapasdebits,imágenesytextos.Puede aplicar aplicar efectos efectos como antialias antialiasing ing,, alpha-blend alpha-blending, ing, rellenado rellenado con gradientes gradientes,, etc. Puede aplicar aplicar tran transf sfor orma maci cion ones es y más más cosa cosas. s. Pued Puede e hace hacer r todo todo esto esto sobr sobre e un QWid QWidge get, t, un QPix QPixma map p o un QImage.PuedeserusadoenconjunciónconQPrinterparaimprimiroparagenerarPDFs,asíque el mism mismo o códi código go que que usam usamos os para para dibu dibuja jar r nos nos puede puede perm permit itir ir impr imprim imir ir.. Una Una alte altern rnat ativ iva a a QPaintereselmóduloQtOpenGL,q QPaintereselmóduloQtOpenGL,queaccedealalibrer ueaccedealalibrería2Dy3DOpenGL. ía2Dy3DOpenGL. Lafor aforma mástí mástípi piccade usar usar QPai QPain nter ter en un Widg Widget et es por por ejem ejempl plore orede deffiniendo endollela funciónpaintEvent()quese funciónpaintEvent()queseencargadedibujarl encargadedibujarlo.Estoesunej o.Estoesunejemplosencillo emplosencillo: : void SimpleExampleWidget::paintEvent( QPaintEvent *) { QPainter painter( this this); ); painter.setPen( Qt Qt::blue); ::blue); painter.setFont( QFont QFont( ("Arial" "Arial", , 30 30)); )); painter.drawText(rect(), Qt Qt::AlignCenter, ::AlignCenter, "Qt" "Qt"); ); }
Todaslasfuncionesdraw…(),sonlasencargadasdedibujarconQPainter.Previamentea dibu dibuja jar r hay hay que que defi defini nir r las las cara caract cter erís ísti tica cas s del del pint pintad ado o con con las las func funcio ione nes s sett setter er setP setPen en() (),, setBru setBrush( sh() ) y setFon setFont() t(),, que define definen n el lápiz lápiz de trazad trazado o de dibujo dibujo,, el rodill rodillo o de rellen rellenado ado de formas,yla formas,yla fuenteparael fuenteparael texto,respecti texto,respectivamen vamente.Esasherrami te.Esasherramientas entas pueden sercambiadasen sercambiadasen cualquiermomento. Las funci uncio ones de dibu dibujjado ado de formasmá masmásim simpo porrtan tantes tes son: draw drawPo Poin int( t(), ), draw drawLi Lin ne(), e(), draw drawPo Poly lyli line ne() (),, draw drawPo Poin ints ts() (),, draw drawLi Line nes( s(), ), draw drawPo Poly lygo gon, n, draw drawRe Rect ct() (),, draw drawRo Roun undR dRec ect( t(), ), drawEllipse(),drawArc(),drawChord(),dr drawEllipse(),drawArc(),drawChord(),drawPie(),drawText(),drawPi awPie(),drawText(),drawPixmap()ydrawPath(). xmap()ydrawPath(). QPai QPaint nter er sitú sitúaelor aelorig igen en de coor coorde dena nada daspo sporde rdefe fect ctoen oen la esqu esquin inasup asuper erio ioriz rizqu quie ierd rda a (0,0).Sinembargoseríamáscómodotenerunav (0,0).Sinembargoseríamáscómodotenerunaventanaconcoor entanaconcoordenadaslógicasyqueelsi denadaslógicasyqueelsistema stema laspasaraafísicassinqueelprogramadortengaquehacerlasconversión.Puesbién,llamamos viewp viewpor ort t al rectán rectángul gulo o con con coord coordena enadas das físic físicas, as, y Win Window dows s al rectán rectángul gulo o con coorde coordenad nadas as lógicas.EstosehaceconQ lógicas.EstosehaceconQPainter::setW Painter::setWindow(). indow(). Luego uego esta esta lamatri amatrizz mun mundo, do, quees quees unama unamatr triizpar zparaapli aapliccar tran ransfo sformac maciones ones afi afines nes como como rotaci rotacion ones es y transl translaci acione ones. s. QMatri QMatrix x es quien quien implem implement enta a dic dicha ha matri matriz, z, y usando usando sus funcionesrotate()ytranslate()sellevanacabolastransformaciones,yfinalmenteseaplicanal QPaintermediantesufunción QPaintermediantesufunciónsetMatrix(QMatri setMatrix(QMatrix). x). Sire Sirequ quer erim imos osde demá másc scal alid idad ad,a ,aun unqu quee eest stor ored edun unda daen enme meno nosv svel eloc ocid idad ad,p ,pod odem emos osus usar ar QImageparadibujar.ParaellocreamosunobjetoQImage()conuntamañoyformatodecolor,y luego luego instan instanci ciamo amos s con con QPain QPainter ter(QI (QImag mage e *), para para ir dibuja dibujando ndo con con él, sería sería su dispos disposit itivo ivo de pintado.
QPrinter Imprim Imprimir iren enQt Qtes essi simil milar arad adibu ibujar jarco conQ nQPai Painte ntery ryse setr trata atade dese segui guirl rlos ossi sigui guient entes espas pasos: os: 1.-CrearQPrinterparaquesir 1.-CrearQPrinterparaquesirvacomoeldispo vacomoeldispositivodepin sitivodepintado(painting tado(paintingdevice). device). 2.-AbrirunaventanaQPrin 2.-AbrirunaventanaQPrintDialog,quepermi tDialog,quepermitaalusuarioajus taalusuarioajustarlaimpresoray tarlaimpresoraydemásajustes. demásajustes. 3.-CrearunQPainterqueoperec 3.-CrearunQPainterqueopereconQPrinter. onQPrinter. 4.-PintarunapáginausandoQPainter.
106
5.-LlamaraQPrinter::newPage() 5.-LlamaraQPrinter::newPage()paraavanzaralapági paraavanzaralapáginasiguiente. nasiguiente. 6.-Repetirlospasos4y5hastaque 6.-Repetirlospasos4y5hastaqueseimprimatodo. seimprimatodo. SepuedetambiénimprimiraunPDFllaman Sepuedetambiénimpr imiraunPDFllamandoasetOutputFormat(Q doasetOutputFormat(QPrinter::Pdf Printer::PdfFormat). Format). QPrinter printer( QPrinter QPrinter::HighResolution); ::HighResolution); QPrintDialog *dialog = new QPrintDialog (&printer, this this); ); dialog->setWindowTitle( "Imprimir Documento" ); if (dialog->exec() != QDialog QDialog::Accepted) ::Accepted) return; return ; QPainter painter; painter.begin(&printer); for (int page = 0; page < numberOfPages; ++page) { // Usar painter para imprimir la pagina. if (page != lastPage) printer.newPage(); } painter.end();
El tamaño tamaño de la zona zona imprim imprimib ible le del papel papel viene viene determ determin inado ado por QPrin QPrinter ter:pa :pageR geRect ect(), (), que devuelveelQRectdelazonaimprimible,conellosepuedenhacerloscál devuelveelQRectdelazonaimprimi ble,conellosepuedenhacerloscálculosparadeterminarla culosparadeterminarla variablenumberOfPagesylastPage.
107
TEMA26 RECURSOSEXTERNOS
Acab Acaban ando doya yalo lost stem emas asqu quer eres esum umen ento todo dolo loqu quee eesn snec eces esar ario iosa sabe berp rpar arad ades esar arro roll llar arun una a aplica aplicaci ción ón de propós propósito ito genera general, l, hemos hemos querid querido o meter meter en un mismo mismo tema tema con el nombr nombre e de recursosexternos,unaseriedeprácticasquenospermitenhacercolaboraraQtconsuexterior. Comenzandoporlaposibilidaddecarg Comenzandoporlaposibilidaddecargarentiempodeejecuci arentiempodeejecuciónundiseñodeven ónundiseñodeventanadediálogo tanadediálogo enformato*.uiconlasuitools,pasandoporlaposibilidaddeejecutarprocesosoaplicaciones externasmediantelaclaseQProcess,yacabandoporlaposibilidaddeañadirlacapacidaddeuso delibreríasexternasaQt.
Diálogosdinámicos(QUiLoader) Llam Llamam amos osdi diál álog ogos osdin dinám ámic icos osaaqu aaquel ello losq sque uehan hansi sido docr crea eado dosen senelQ elQtDe tDesi sign gner erco como mo fiche fichero ros s *.ui, *.ui, pero pero que no han sido sido compi compilad lados os junto junto con la aplic aplicaci ación ón,, por lo que el uic (ui compiler) compiler),nohapodidopasa ,nohapodidopasarlos rlosa a C++,yluegosercompil C++,yluegosercompilados, ados,sinoquevanasercargado sinoquevanasercargadosen sen tiempodeejecuciónporlaaplicaciónprincipalcomo.uitalcual.Paraelloexisteunaclasecapaz de tal manejo manejo,, que es QUILoa QUILoader der,, que median mediante te su funció función n load( load(QFi QFile) le),, puede puede cargar cargar dic dicho ho ficherocontodasuinterfaz. QUiLoader uiLoader; QFile file("dialog.ui" file("dialog.ui" ); QWidget *dialog = uiLoader.load(&file); if(dialog){ if (dialog){ // código para recoger los los widgets widgets QComboBox *combo1 = dialog->findChild< QComboBox *>("combo1" *>("combo1"); ); // si combo1 no existe devuelve un puntero nulo if (combo1)......... }
Date cuenta que aquí cargamos el fichero .ui con el UiLoader, y recuperamos su estructuraenunpunteroQWidget(dialog).UsandodelaintrospeccióndeQt,podemosmediante findChil findChild, d, ir recuperan recuperando do punteros punteros a sus component componentes, es, conocido conocidos s sus nombres nombres (objectNa (objectName). me). Podemosde Podemosde estamaneraya estamaneraya usardialog->s usardialog->show( how()para )para mostrarl mostrarlode ode manerano-moda manerano-modalo lo dialogdialog>exec()parahacerlodemaneramodal. Para usar QUiLoader tenemos que añadir a *.pro el módulo uitools de la siguiente manera: CONFIG
+= uitools
Si vas ausar ausar el compi ompillar MSVC MSVC 6, es nec necesar esariio que en vez de usar usar findC ndChild hild
Programasexternos(QProcess) Unade Unadelas lasuti utili lida dade desde sdedes desar arro roll llar aruna unaGUI GUI,es ,esfac facil ilit itar arelma elmane nejo jodeun deunaap aapli lica caci ción ónde de consolaquepreviamenteexistía,ycuyalíneadecomandoseramuylargaeincómodadeusar. Nos podemo podemos s plante plantearel arel constr construir uirleuna leuna GUI para para poderusarl poderusarla a deuna manera manera más gráfi gráficay cay cómodaporlotanto.Qt,no cómodaporlotanto.Qt,nospermiteesomediant spermiteesomediantelaclaseQProcess. elaclaseQProcess.
108
Par Parasimp asimple leme men nte ejec ejecut utar ar una apli apliccaci ación ext extern erna, usar usarem emo oslafun slafunció ción exec execut ute( e() ) de manerasimilaraésta: QString cmd = "cp a.txt b.txt" b.txt"; ; QProcess::execute(cmd); QProcess ::execute(cmd);
Si lo que queremos es establecer una comun municación entre el programa externo y la aplicación,siaquéllopermite,enesecasousaremoslafunciónstart(),yusaremoswrite()para envi enviar arle le dato datos, s, y read readAl All( l() ) para para leer leer del del mism mismo. o. Usar Usarem emos os clos close( e() ) para para cerr cerrar ar el cana canal l de comunicación.Unejemplodeestetipodecomunicaciónesposibleconprogramascomoelbash deLinux,oTelnet,ocmd.exedeWin deLinux,oTelnet,ocmd.exedeWindows.Veamosunej dows.Veamosunejemplo: emplo: QProcess bash; bash.start( "bash" "bash"); ); if(!bash.waitForStarted()){ if (!bash.waitForStarted()){ qDebug() << "NO RULA" RULA"; ; return -1; } bash.write( "ls" "ls"); ); bash.closeWriteChannel(); if(!bash.waitForFinished()){ if (!bash.waitForFinished()){ qDebug() << "NO RULA" RULA"; ; return -1; } QByteArray response = bash.readAll(); qDebug() << response.data(); bash.close();
Usodelibreríasexternas Cuan Cuando doqu quer erem emos osusa usarl rlib ibre rerí rías aspar parali alink nkar arla lasn snue uest stro ropro progr gram ama, a,si simp mple leme ment nteh ehay ayqu que e añadiranuestrofichero* añadiranuestrofichero*.prounalíneac .prounalíneacomoesta: omoesta: LIBS LIBS
+=+=-ln lnom ombr bre_ e_li libr brer eria ia
SiembrequecompilemosconelGNUCompiler.Asílalibreríamatemáticasería–lm,lalibrería curlsería–lcurl,yasísucesivamente. Pero Peroha hayv yvec eces esqu queq eque uerr rrem emos osll llam amar aren enti tiem empo pode deej ejec ecuc ució ión naf afun unci cion ones esde deun unal alib ibre rerí ría a dinámica,queenWindowstieneextens dinámica,queenWindowstieneextensión.dll,enMacO ión.dll,enMacOSXsueleser.dylib SXsueleser.dylib,yenLinuxes. ,yenLinuxes.so.Para so.Para talcaso,Qtproveedeunsistemamu talcaso,Qtproveedeunsistemamultiplataformaatr ltiplataformaatravésdelaclaseQLi avésdelaclaseQLibrary. brary. Toda librería tiene sus funciones exportadas como símbolos. Supongamos que una librería librería llamada llamada “mylib” “mylib” con la extensión extensión correspo correspondien ndiente te del operativo operativo donde esté instalada, instalada, tieneunafunciónqueenCtienecomoprototipo“intavg(int,int)”queloquehaceesrealizarla mediaaritméticadelos2parámetrosqueselepasan,ydevuelveesamediaenformatoentero.Si queremosllamaradichafunció queremosllamaradichafuncióndesdeQt,habríaq ndesdeQt,habríaqueusarelsigui ueusarelsiguientecódigo: entecódigo: QLibrary library( "mylib" "mylib"); ); typedef int (*AvgFunction)( int int, , int int); ); AvgFunction avg = (AvgFunction) library->resolve( "avg" "avg"); ); if (avg) return avg(5 avg( 5, 8); else return -1;
109
TEMA27 BÚSCATELAVIDA
Sí,h Sí,has asle leíd ídob obie ien, n,es este teca capí pítu tulo lose sell llam ama“ a“bú búsc scat atel elav avid ida” a”,e ,est ston onoq oqui uier ered edec ecir irqu quey eyam ame e hayacansadodeescribirestelibro,nideexplicarnuevascosas.QtesunFrameworkqueyatiene casilaveintenadeaños,porloqueadíadehoyhacrecidoenormemente,tantoqueningúnlibro quelotratepuedeaspiraratocarabsolutamentetodassusclases.Tambiénesciertoquehemos tocadolasclasesquemásvasausarsiemprequedesarrollesaplicacionesdepropósitogeneral, pero pero esposible esposible que alguna alguna vez teplantees teplantees hacerotra hacerotra cosa,y cosa,y esto esto no esté esté cubier cubierto to enningún enningún libr libro. o. Hay Hay que que pens pensar ar que que Qt, Qt, segu seguir irá á crec crecie iend ndo o en el futu futuro ro,, por por lo que que la únic única a form forma a de mantenersealdía,esusandoelQtAssi mantenersealdía,esusandoelQtAssistant,esdecir stant,esdecir,buscándoseuno ,buscándoseunomismolavida. mismolavida. Por Porta tant nto, o,l lo oqu que epr pret eten ende demo mos sco con nes este tec cap apít ítul ulo oes esc cub ubri rir res esa apa part rteq eque uev vie iene ned des espu pués ésde de cuando cuando acabes acabes este este libro, libro, y tengas tengas que empezar empezar a escri escribir bir códig código, o, y en alguna algunas s ocasio ocasiones nes te tropiecesconquetienesqueusarcosas,quenohasestudiadoantes,ynuncalashasusado,e inclusoquenopueda inc lusoquenopuedasencon sencontrar trarenlosforosanadiequelohaya enlosforosanadiequelohayausadoantes usadoantes.Qtestámuybien .Qtestámuybien docu docume ment ntad ado, o, pero pero en ingl inglés és,, por por lo que que es fund fundam amen enta tal l ente entend nder er al meno menos s el idio idioma ma de Shakespeareaniveldelecturatécnica. Pues Puesbi bien en,pa ,para rapra pract ctic icar ar,se ,semeh mehaoc aocur urri rido dopla plant ntea earun runas ascl clas ases esque queno nohem hemos osvi vist sto, o,y y quecuriosamen quecuriosamenteechesde teechesde menos,si menos,si hasprogramadoenotrosentor hasprogramadoenotrosentornos. nos.Se Se tratade tratade lahora,la fechae fechae incl inclusodelusodetimers, usodelusodetimers,aunqueeneltema23 aunqueeneltema23 hemosvistounejemplo hemosvistounejemplode de ésteúltimo. ésteúltimo. Porellovamosapracticarenestetemaabuscarnoslavidaenelasistente.Realmentepodría habercentradolabúsquedaencualquierotrotemamáscomplejo,perosimplementeloseríapor tenermásfunciones,propiedades,señalesyslots,oporqueeltextodeladescripciónesmucho másextensa.Asíquesiseguim másextensa.Asíquesiseguimosbienestas osbienestas3 3 clases,nohabrí clases,nohabríaporquéhaberningú aporquéhaberningúnproblem nproblema a encualquierotra. Loprime primerroser oserííasab asaberqu erquéc écla lassespo espodr drííanen anenca carrgar garsedelaho edelahorra,del a,delaf afec echa ha,o ,odeun deun proporcionaruntimer.Debidoaqueunpocodeingléssíquesabemos,ymássihemostrabajado conlibreríassimilaresenC,C++uotrolenguaje,sabemosquelahoraesTime,quelafechaes DateyTimerestáclaroloquees,asíquenoseríamuydescabelladopensarquedichasclasesse llamenQTime,QDateyQTimer.Asíquenosvamosalasistente,ybuscamoslas3clases,veremos quehemosacertadoentodo.¡Caramba,quesuertetenemos!.Detodasformassinotuviéramos claroelconcept claroelconceptodenuestra odenuestraclase,siemp clase,siemprepuedeunodars repuedeunodarseunapaseoportoda eunapaseoportodas,paradetec s,paradetectar tar dentrodesunombreunatisbodeluz.Porejemplo,¿dequécreequetratarálaclaseQMimeData oQFtp?.
QTime Esev Esevid iden ente tequ quee eelc lcon onst stru ruct ctor ordee deest stac acla lase se,d ,deb ebed edep eper ermi miti tiri rini nici ciar arun unai ains nsta tanc ncia iaco con n unahoradeterminada.Vemosentrelas unahoradeterminada.Vemosentrelas public public function functions,siempreen s,siempreen primerlugarlos primerlugarlos distinto distintos s constructores,unosinparámetros,porloqueesunamerainstanciación,ydebedeexistirpor tant tanto o una una func funció ión n que que más más tard tarde e pued pueda a pone poner r la hora hora,, que que por por su prot protot otip ipad ado, o, debe debe de ser ser setHMS(). Otra de las cosas impor portantes que hay que mirar en una clase, es quedarse con las propie propiedad dades es o variab variables les privad privadas, as, ya que asocia asociadas das a ellas ellas vamos vamos a encont encontrar rar una serie serie de funcionessetterygetterimportantesparasumanejo.Asínosencontramosconhour(),minute() ysecond()enlosgetter,yely ysecond()enlosgetter,yelyamencionados amencionadosetHMS()comoelúni etHMS()comoelúnicosetter. cosetter. Luego nos encontraremos con una serie de funciones que llevan a cabo una labor especial,quepodemosleerensudescripcióndequésetrata.Buscaportanto,lalabordestart(), restar restart() t() y ellaps ellapsed( ed(). ). Están Están muy relaci relacion onada adas s entre entre sí, y así aparec aparece e en sus descr descripc ipcio iones nes.. Tamb Tambié ién n es muy muy impo import rtan ante te aque aquell lla a que que reco recoge ge la hora hora exac exacta ta actu actual al, , que que de segu seguro ro es
110
currentTime(),entrelosstaticpublic.Dentrodelosoperadores,vemosquesepuedencomparar 2objetosQTimeentresí,locualpuedesermuyútil.Yaúnmásútilunaseriedefuncionesque permitenañadiralahora,inter permitenañadiralahora,intervalosdetiempov valosdetiempovariables,enseg ariables,ensegundos(addSecs). undos(addSecs). Tea Teani nimo moa aq que ueh hag agas asp pru rueb ebas asc con one ell llas ase en nun unc cód ódig igo ose senc ncil illo lod de eco cons nsol ola. a.
QDate Setr Setrat atad adeu euna nacl clas ased edes esim imil ilar ardi disp spos osic ició ióna nala laan ante teri rior or.P .Por orel ello lote tean anim imam amos osaqu aquel ela a describastúmismo,yluegoc describastúmismo,yluegocompruebessufunci ompruebessufuncionalidadenunc onalidadenuncódigotambién ódigotambiénsencillo. sencillo.
QTimer Esta Estacla clase se,er ,erafu afund ndam amen enta talex lexpl plic icar arla la,ya ,yaque queesmu esmuyút yútil ilsie siemp mpre reelus elusode odetim timer ers. s.El El dejarlaparauntemaautos dejarlaparauntemaautosufic uficient ientecomoéste, ecomoéste,nosvaa nosvaa permitirser permitirsermásautónom másautónomosdeaquíen osdeaquíen adel adelan ante te a la hora hora de mane maneja jar r el Fram Framew ewor ork k de Qt, Qt, porq porque ue otra otra posi posibi bili lida dad d que que nos nos pued puede e ocurrir,esquesenosolvidelafuncionalidaddealgunasdelasclasesqueyahemosexplicado,y notengascercaestelibropararepasarlas,mientrasqueteniendoelasistente,siemprepodrás sacareltrabajoadelante. Lopr Loprim imer eroq oque ueno nosd sdic icee eela lasi sist sten ente tees esqu queQ eQTi Time merp rpro rove veed edet etim imer ersr srep epet etit itiv ivos osy yde deun un solo solo dispar disparo. o. Los timer timers s repeti repetitiv tivos os son aquell aquellos os que cada cada inter interval valo, o, se dispar dispara a el evento evento del timer,ylosdeunsolodisparo,sóloseprogramarysedisparanunasolavez.Esdesuponerque para para indic indicarque arque hallegadoal hallegadoal final final desu contad contador, or, dispar dispare e una señal. señal. Y así es, dehecho sólo sólo podemosverlaseñaltimeout(). Veam Veamos ossu susp spro ropi pied edad ades es,q ,que ueso son: n:ac acti tive ve,q ,que uees esun unbo bool olqu quee eest star aráa áatr true uecu cuan ando does esté té activo,yfalsecuando activo,yfalsecuandono no loesté;interval loesté;interval,quees ,quees unentero,quede unentero,quede segurocontie segurocontieneelinterval neelintervalo o programado;ysingleShotqueesunboolqueestarátruesiesdeúnicodisparo,oafalse,sies repetitiv repetitivo. o. En torno torno a estas propiedad propiedades, es, deben hacer getters getters y setters.Como setters.Como setters setters tenemos tenemos setInterval()ysetSingleShot setInterval()ysetSingleShot(),ycomogetter (),ycomogetterstenemos,isAct stenemos,isActive()eisSin ive()eisSingleShot(). gleShot(). Como publ publiicfun cfuncti ctions, ons, tenem enemo os las másimp másimpo ortan tantes, tes, queso queson n star tart()par t()parapon aponer er en marc marcha ha el time timer, r, y stop stop() () para para para pararl rlo o en cual cualqu quie ier r mome moment nto. o. Hay Hay otra otra func funció ión n llam llamad ada a singl singleSh eShot( ot() ) que nos nos permit permite, e, progra programar mar el inter interval valo o y el slot slot a ejecut ejecutar ar de un determ determin inado ado objeto,cuandodichointe objeto,cuandodichointervalohayapasado. rvalohayapasado. Pues Puesto toqu queQ eQTi Time merp rpro rove veed edeu euna nase seña ñal, l,ti time meou out( t()es )esta tamb mbié iénm nmuy uyló lógi gico coel elus usar arla lapa para ra conectarlaaslotsdeotroso conectarlaaslotsdeotrosobjetosquehagan bjetosquehaganalgounavezpasado algounavezpasadoeltiempo. eltiempo. Tean Teanim imam amos osaqu aquee eesc scri riba basu sunp npro rogr gram amas asen enci cill llo, o,qu quec ecad adas aseg egun undo do,a ,aum umen ente teel elva valo lor r deuncontadorenunQLCDNumber.
Quiz Quizás áses este tete tema mate teha haya yapa pare reci cido domu muyt ytri rivi vial al,s ,sie iesa sasí sí,e ,esq sque ueya yaes esta tasp spre repa para rado dopa para ra nadartúsóloeneluniversoQt.Peroaúnnotevayas,porqueelúltimotemaeselsiguiente,y tratadealgoquemuypocossuelentratar,estoes,decómoprepararuninstaladorparanuestra aplicaciónparalos3operativ aplicaciónparalos3operativosmásimportante osmásimportantes:Linux,MacO s:Linux,MacOSXyWindows. SXyWindows.
111
TEMA28 INSTALADORES
Alfi Alfina nald ldel elte tema ma2, 2,cu cuan ando dopr prep epar aram amos osel elen ento torn rnod odede edesa sarr rrol ollo lo,p ,par arap apod oder erde desa sarr rrol olla lar r todoslosejemplosdellibrosinproblemas,avisábamosdequelasaplicacionesdesarrolladaspor defectoconestasinstalacionespordefectodelSDKdeQt,nopodíanserimplementadastalcual ensistemasdeproduccióndondeel ensistemasdeproduccióndondeelSDKnovaaestar SDKnovaaestarinstalado. instalado. Llegó Llegó el momento momento de tomar en consider consideració ación n qué archivosdebemos archivosdebemos de llevarno llevarnos s con nuestrasaplica nuestrasaplicacion cionesfueradel esfueradel ordenador ordenador donde seefectuó desarroll desarrollo,para o,para quetodofuncione quetodofuncione correctamente. El ento entorn rno o de desa desarr rrol ollo lo inst instal alad ado o por por defe defect cto, o, func funcio iona na con con libr librer ería ías s de enla enlace ce dinámico,porloquevamosacompilarejecutablesquesonpocopesados,peroqueenlazanen tiempodeejecuciónconunaseriedelibreríasdinámicas,porloquenostendremosquellevar juntoconelejecutable,todaunaseriedelibreríasyplugins,sobrelaqueelmismosevaaapoyar parapoderfuncion parapoderfuncionar.Porelloesmuy ar.Porelloesmuy interesan interesantedispon tedisponer,nosólodeun er,nosólodeun ordenadoropera ordenadoroperativo tivo paraeldesarrollo,sinotambién,disponerdeotro(máquinarealovirtual)dondeestéinstalado eloperativo eloperativopordefectosin pordefectosinmás,dondevamos más,dondevamosa a probarqueentrega probarqueentregamostodo mostodolonecesari lonecesariopara opara quenuestraaplica quenuestraaplicación ciónfuncion funcione.Unaveztengamos e.Unaveztengamosen en unacarpetaa unacarpetaa parte,todosloselement parte,todosloselementos os nece necesa sari rios os para para que que la aplic aplicac ació ión n func funcio ione ne autó autóno noma mame ment nte, e, podr podrem emos os recu recurr rrir ir a crea crear r un sist sistem ema a de inst instal alac ació ión n que que se enca encarg rgue ue de copi copiar ar dich dichos os elem elemen ento tos s en la mane manera ra debi debida da al sistemaenproducción. Vamosportantoaseguirunejemplo Vamosportantoaseguirunejemplo,enlos3siste ,enlos3sistemasoperativosdesi masoperativosdesiempre(Windows, empre(Windows, LinuxyMacOSX),ylovamosahacercon LinuxyMacOSX),ylovamosahacerconlaaplicaciónquedesarr laaplicaciónquedesarrollamoseneltema20queesun ollamoseneltema20queesun editor editor de textos textos donde donde están están presen presentes tes los elemen elemento tos s típico típicos s de una aplic aplicaci ación ón de propós propósito ito general.Vamospuesausarlaparacrearelpaquetedeinstalacióncorrespondienteacadaunode lossistemasoperativosobj lossistemasoperativosobjetivo. etivo.
Windows Sinolo Sinolohas hashec hecho hoant anter erio iorm rmen ente te,ab ,abre reelar elarch chiv ivode odepro proye yect cto(. o(.pr pro) o)entu entuent entor orno node de desarrollo(QtCreator),ypulsandoenelmodoProjects(iconosdelaizquierda),seleccionaen “EditBuildconfiguration “EditBuildconfiguration”laopciónRelease.Luego ”laopciónRelease.Luego,vamosareconstrui ,vamosareconstruirlaaplicaciónco rlaaplicaciónconRebuild nRebuild Proj Projec ect. t. Pode Podemo mos s incl inclus uso o ejec ejecut utar arla la. . No debe debe de habe haber r ning ningún ún prob proble lema ma, , ya que que qued quedó ó completamen completamentecreaday tecreaday probadaen probadaen eltema20. Yatenemosen Yatenemosen eldirectoriodonde eldirectoriodonde guardemos guardemos nues nuestr tros os fuen fuente tes, s, una una carp carpet eta a que que se llam llamar ará á appl applic icat atio ionn-bu buil ildd-de desk skto top, p, en cuyo cuyo inte interi rior or encontra encontraremos remos enla carpetaRelease,el carpetaRelease,el ejecutabl ejecutable e final(applicati final(application.e on.exe).Si xe).Si usasVisual Studio Studio 2008 2008 como omo comp compiilado ladorr, y en el sis sistema tema está está regi egistrad trada a la run run-time time de Vis Visual ual C++ C++ 9.0 9.0 “msvcp90.dll”,podrásejecutarsinproblemaslaaplicación.SiusaselcompiladorMinGWqueva en el paqu paquet ete e de Qt4, Qt4, las las libe liberí rías as diná dinámi mica cas s corr corres espo pond ndie ient ntes es no esta estará rán n regi regist stra rada das s en el sistema,ylaejecucióndelaaplicacióndesdesucarpeta,tedaráerrorindicándotelaprimera libreríadinámicaqueinten libreríadinámicaqueintentaenlazarperoq taenlazarperoquenoencuentra. uenoencuentra. Sea como como fuer uere, debe debemo moss prep prepar arar arlleal ejec ejecut utab ablle, todo todo lo quere querequ quiiereen ereen su prop propiia carpeta,asíquevamosaello. Para Parasa sabe berd rdeq equé uédep depen ende denu nues estr traa aapl plic icac ació ión, n,en enWi Wind ndow owsv svam amos osat aten ener erqu queu eusa saru runa na herramientaquenovieneincluidaenelSDKdeQt,llamadaDependencyWalker.Puedesbajarla dehttp://www.dependencywalker.com/ dehttp://www.dependencywalker.com/.Ennuestroc .Ennuestrocasohemosbajadolav asohemosbajadolaversiónde32bit ersiónde32bits(for s(for x86).Al x86).Al ejecutarl ejecutarla,vamosaFile->Open,yabrimosnues a,vamosaFile->Open,yabrimosnuestroejec troejecutabl utable(applica e(application tion.exe) .exe).Senos .Senos despli despliega ega en la ventan ventana a superi superior or izquie izquierda rda todo todo un árbol árbol de depend dependenc encias ias,, pero pero lo que nos nos inter interesa esa,, está está en las ramas ramas princi principal pales es de dic dichos hos árbole árboles. s. Si colaps colapsamo amos s dic dichas has ramas, ramas, en un sistemaconVS2008tendríamosalgoasí:
112
Comopodemosobser Comopodemosobservar, var,nuestraaplica nuestraaplicació cióncompila ncompiladaconVisualC daconVisualC++2008(9.0)dependede4 ++2008(9.0)dependede4 dlls,perounadeellas,yavieneprovistaporelsistemaoperativoWindows(kernel32.dll),porlo querealmentevamosarequerirdelas querealmentevamosarequerirdelasDll’sQtGui4. Dll’sQtGui4.dll,QtCore4.dlly dll,QtCore4.dllyMSVCR90.dll. MSVCR90.dll. Dehec Dehecho hoest estas as3dep 3depen ende denc ncia ias, s,las laspod podía íamo mospr sprev evee eer, r,yaq yaque uesimi simira ramo mosel selcon conte teni nido do delficherodeproyecto(applic delficherodeproyecto(application.pro)ver ation.pro)veremosquepideañadir emosquepideañadirlosmodulos“c losmodulos“core”y“gui” ore”y“gui”: : QT
+= core gui
Porloque,esdesuponerquerequeriráambaslibreríasdinámicas.Laterceradependenciaes típicadequienusaelcompiladorVS2008.QuienuseMinGWseencontrarácon2dependencias, lasdelosficherosmingwm10.dl lasdelosficherosmingwm10.dllylibgcc_ lylibgcc_s_dw2-1.dll. s_dw2-1.dll. ¿Dón ¿Dónde depo pode demo mose senc ncon ontr trar ares esas asli libr brer ería ías? s?.P .Pue uesl slas asli libr brer ería íasd sdeQ eQts tsee eenc ncue uent ntra rane nene nel l direct directori orio o /binde /binde lainstala lainstalaci cióndelQt óndelQt SDK. SDK. Lalibrerí Lalibrería a deVisual deVisual C++ está está enel direct director oriode iode instalacióndeVS2008(\ArchivosdeProgramas\MicrosoftVisualStudio9.0\VC\ce\dll),donde podráselegirlaarquitecturadeCPU, podráselegirlaarquitecturadeCPU,ennuestrocaso ennuestrocasoenlacarpetax86(paraPC enlacarpetax86(paraPC). ). Asíq Asíque uetr tras asla lada damo mose sesa sas3 s3de depe pend nden enci cias asal almi mism smos osit itio iodo dond ndee eest stan anue uest stro roej ejec ecut utab able le. . Sillevamosesos4ficherosenunacarpetaacual Sillevamosesos4ficherosenunacarpetaacualquierPCconWindo quierPCconWindowsXP,Vistao7,verásquese wsXP,Vistao7,verásquese puedeejecutarsinproblemaalguno. Hab Habrán apli apliccaci aciones queha quehaccen uso uso de plug plugiins,co ns,como mo pue puedan dan ser ser dri drivers ers de bas bases de datos,driversdeformatosgr datos,driversdeformatosgráficos,etc áficos,etc.Veremosluegoen .Veremosluegoenunapartadoquéhacer unapartadoquéhacerenesecaso. enesecaso.
Linux Sinolo Sinolohas hashec hecho hoant anter erio iorm rmen ente te,ab ,abre reelar elarch chiv ivode odepro proye yect cto(. o(.pr pro) o)entu entuent entor orno node de desarrollo(QtCreator),ypulsandoenelmodoProjects(iconosdelaizquierda),seleccionaen “EditBuildconfiguration “EditBuildconfiguration”laopciónRelease.Luego ”laopciónRelease.Luego,vamosareconstrui ,vamosareconstruirlaaplicaciónco rlaaplicaciónconRebuild nRebuild Proj Projec ect. t. Pode Podemo mos s incl inclus uso o ejec ejecut utar arla la. . No debe debe de habe haber r ning ningún ún prob proble lema ma, , ya que que qued quedó ó compl complet etam amen ente te crea creada da y prob probad ada a en el tema tema 20. 20. La apli aplica caci ción ón qued quedar ará á en su dire direct ctor orio io de mane manera ra que que pued puede e ejec ejecut utar arse se dire direct ctam amen ente te desd desde e él (./a (./app ppli lica cati tion on). ). Para Para comp compro roba bar r sus sus dependencias,hayunaherramientaquey dependencias,hayunaherramientaqueyavienecon avieneconlasherramientasG lasherramientasGNU,quees NU,queesldd ldd .. ldd./application Nos Nos resp respon onde derá rá con con una una seri serie e de libr librer ería ías s diná dinámi mica cas s del del tipo tipo .so .so . La gran gran mayo mayorí ría a de esas esas libreríassuelenestarinstaladasenunsistemalimpiocongestordeventanasGnomeoKDE.Pero esconvenien esconvenientequeantestecercior tequeantestecercioresdeello.Y esdeello.Y siesasí,es posiblequedichalibr posiblequedichalibreríaseinsta eríaseinstale le mediante mediante algún paquete paquete que se puedabajar de losrepositorio losrepositorios s oficial oficiales es de la distribu distribució ción n de Linux nux en cuest uestiión. ón. Fin Finalme almen nte dich dichas as librer breríías siempr empre e acab acabar arán án en /lib lib, /usr /usr/ /lib lib ó /usr/l /usr/loca ocal/l l/lib. ib. De esta esta manera manera son siempr siempre e accesi accesible bles s a la aplica aplicaci ción. ón. Pero Pero a parte parte de estas estas libreríasveremosotrasqu libreríasveremosotrasquenoestánenesos enoestánenesosdirectorio directoriosquedeseguroso squedeseguroson: n:
113
libQtGui.so.4ylibQtCore.so.4.Estaslasvamosatenerqueponeralalcancedenuestraaplicación ensupropiodirectorio.Puestoquesetratandelinksaficherosdeotronombrelohacemosdela manera: cp–Rqtsdk/qt/lib/libQtGu cp–Rqtsdk/qt/lib/libQtGui.so.4*applicat i.so.4*application_folder ion_folder cp–Rqtsdk/qt/lib/libQtCo cp–Rqtsdk/qt/lib/libQtCore.so.4*applicat re.so.4*application_folder ion_folder Hab Habrán apli apliccaci aciones queha quehaccen uso uso de plug plugiins,co ns,como mo pue puedan dan ser ser dri drivers ers de bas bases de datos,driversdeformatosgr datos,driversdeformatosgráficos,etc áficos,etc.Veremosluegoen .Veremosluegoenunapartadoquéhacer unapartadoquéhacerenesecaso. enesecaso.
MacOSX Unaap Unaapli lica caci ción ónpar paraMa aMac, c,ll llev evala alaext exten ensi sión ón.a .app pp,y ,yen ensí sími mism smoes oesuna unaca carp rpet etaen aencu cuyo yo interiorllevaelverdaderoejecutableytodoslosrecursos,plugins,frameworks,etc,quenecesita para para funcio funcionar nar.En .En reali realidad dad un.appes unbundley unbundley tienela tienela forma forma que defini definimos mos enel gráfi gráfico co siguiente:
Sinolohashechoanteriormente,abreelarchivodeproyecto(.pro)entuentornode desarrollo(QtCreator),ypulsandoenelmodoProjects(iconosdelaizquierda),seleccionaen “EditBuildconfiguration “EditBuildconfiguration”laopciónRelease.Luego ”laopciónRelease.Luego,vamosareconstrui ,vamosareconstruirlaaplicaciónco rlaaplicaciónconRebuild nRebuild Proj Projec ect. t. Pode Podemo mos s incl inclus uso o ejec ejecut utar arla la. . No debe debe de habe haber r ning ningún ún prob proble lema ma, , ya que que qued quedó ó completamentecreadayprobadaeneltema20.I completamentecreaday probadaeneltema20.Inicialmenteappli nicialmenteapplication.appcont cation.appcontiene: iene: Info.plist
MacOS
PkgInfo
Resources
Paraverlasdependenciasdelejecutableensí(nodelbundle),usamosunaherramientaquelleva elXCode,quesellamaotool :: otool-Lapplication.app/Co otool-Lapplication.app/Contents/MacOs/ ntents/MacOs/application application Ennuestrocasorespondeasí EnnuestrocasorespondeasíenMacOSX10.6.4: enMacOSX10.6.4: application.app/Contents/MacOs/application: QtGui.framework/Versions/4/QtGui(compatibilityversion4.7.0,currentversion4.7.0) QtCor QtCore.f e.fram ramew ewor ork/V k/Vers ersio ions ns/4/ /4/QtC QtCore ore(c (com ompat patib ibil ility ityve vers rsio ion n4.7 4.7.0, .0,c curr urren entv tver ersi sion on4. 4.7.0 7.0) ) /usr /usr/l /lib ib/l /lib ibst stdc dc++ ++.6 .6.d .dyl ylib ib( (co comp mpat atib ibil ilit ity yve vers rsio ion n7. 7.0. 0.0, 0,c cur urre rent ntv ver ersi sion on7 7.9 .9.0 .0) ) /usr/l /usr/lib ib/li /libg bgcc cc_s _s.1. .1.dyl dylib ib(c (com ompat patib ibili ility tyver versi sion on1. 1.0.0 0.0,c ,curr urrent entve vers rsio ion n625 625.0. .0.0) 0) /usr/l /usr/lib ib/li /libS bSyst ystem. em.B.d B.dyli ylib( b(co compa mpati tibi bili lity tyver versi sion on1. 1.0.0 0.0,,cur curre rent ntver versi sion on12 125.2 5.2.0) .0)
114
Las3libreríasinferiores(.dylib)sonpropiasdetodosistemapordefecto.Las2superiores,son librería libreríasque sque sehaninstaladoen sehaninstaladoen formade formade Framework Framework,que ,que escomose instalanpordefect instalanpordefectolas olas libreríasdelQt4SDKenMac.Dehech libreríasdelQt4SDKenMac.Dehechopuedesverlasatodas opuedesverlasatodasen/Library en/Library/Frameworks. /Frameworks. Cómo Cómocr crea eara rama mano noel elbu bund ndle leco cont ntod odol oloq oque uene nece cesi sita ta,s ,ser ería íala larg rgoy oyte tedi dios oso, o,No Noki kian anos os ofreceunaherramientaconelSDKquelohacepornosotrosdemanerasencillaycómoda.Se llama“macdeployqt”,quepidelossi llama“macdeployqt”,quepidelossiguientesrequi guientesrequisitosparalaco sitosparalaconstrucción nstruccióndelbundlefinal delbundlefinal:: 1.-Lasversionesdebugdelos 1.-Lasversionesdebugdelospluginsnoso pluginsnosonañadidasalbundl nañadidasalbundle. e. 2.-Lospluginsdesignerno 2.-Lospluginsdesignernosonañadidos sonañadidosalbundle. albundle. 3.-Lospluginsparaformato 3.-Lospluginsparaformatodeimágenessíque deimágenessíquesonañadidosal sonañadidosalbundle. bundle. 4.- 4.- SQL SQL driv driver ers, s, Scri Script pts s plug plugin ins, s, Phon Phonon on back back-e -end nd plug plugin ins, s, SVG SVG Icon Icon plug plugin ins, s, siem siempr pre e son son añadidosal añadidosal bundle bundle sila aplicació aplicaciónusa nusa losmóduloscorrespo losmóduloscorrespondie ndientesQtSql ntesQtSql,QtScrip ,QtScript,Phonony t,Phonony QtSvg. 5.-Si quieres quieres añadir añadir una libreríano-Qt libreríano-Qt al bundle, bundle, tendrás tendrás que antes añadirla añadirla al proyecto proyecto .pro comounalibreríaexplícit comounalibreríaexplícita.Entalcaso,s a.Entalcaso,seañadiríasin eañadiríasinproblemasabundle. problemasabundle. Bien,co en,como mo en nues uestro tro cas caso cump cumpllimo imos sin sin prob proble lema mass toda todases sesta tass prem premiisas, as, vamos amos a crearelbundledeunavezyporto crearelbundledeunavezyportodashaciendo: dashaciendo: macdeployqtapplication.app Traslocual,elcontenidodel Traslocual,elcontenidodelbundlehabrácambiado bundlehabrácambiadoa: a: Frameworks
Info.plist
MacOS
PkgInfo
P l u g I ns
R es ou r c e s
PodemosverqueenFrameworkssehanañadidolosquehacíanfalta,yalgunomás.Podemos borraramanolosquesobran borraramanolosquesobransiqueremosahor siqueremosahorrarespacio. rarespacio. Ya pode podemo mosdi sdist stri ribu buir ir la apli aplica caci ción ón sinpr sinprob oble lema mas, s, ypara ypara inst instal alar arla la en otro otro equi equipo po el usua usuari rio o sola solame ment nte e tend tendrí ría a que que arra arrast stra rar r el icon icono o corr corres espo pond ndie ient nte e al .app .app a la carp carpet eta a Aplicaciones. Habrán Habrán aplic aplicaci acione ones s que hagan hagan uso de plugin plugins,comopuedan s,comopuedan ser driver drivers s de bases bases de datos, datos, driver drivers s de forma formatos tos gráfi gráficos cos,, etc. etc. Veremo Veremos s en el siguie siguiente nte aparta apartado do como como hacer hacer esto, esto, aunquenuestraherramientayasehayaencargadotambiéndecolocarlospluginsqueconsidera necesariosensusrespectivas necesariosensusrespectivascarpetasdentrodel carpetasdentrodelbundle. bundle.
Plugins Losplu Losplugi gins ns en Qt,per Qt,permi mite tenañ nañad adir ir nuev nuevas as func funcio iona nali lida dade desa sa lasapl lasaplic icac acio ione nes. s. Asíha Asíhay y pluginsquepermitenañadirnuevo pluginsquepermitenañadirnuevosformatosdeimág sformatosdeimágeneslegiblesenQ eneslegiblesenQt,onuevosti t,onuevostiposdebases posdebases dedatos,etc.LospluginsincluídosenelSDK,estáneneldirectorio/qt/pluginsensucarpetade insta instalac lación ión.. Los nombr nombres es de esas esas carpet carpetas as son fijo fijos,y s,y sise trasla trasladan dan tal cual cual a la carpet carpeta a del ejec ejecut utab able le de la apli aplica caci ción ón,, la mism misma a no tend tendrá rá prob proble lema mas s en enco encont ntra rar r dich dicho o plug plugin in si lo requ requie iere re,, ya que que por por defe defect cto o es ahí ahí dond donde e los los busc buscar ará. á. Si quer querem emos os pone ponerl rlos os en otro otro siti sitio o dife difere rent nte, e, ento entonc nces es en la func funció ión n main main() () de la apli aplica caci ción ón tend tendre remo mos s que que deci decirl rle e dond donde e encontrarlausandoQCoreApplication::addLibraryPath encontrarlausandoQCoreApplication::addLibraryPath(). (). Losp Losplu lugi gins nspa para raba base sesd sded edat atos osqu quee eelS lSDK DKpr prov ovee eeso sonm nmuy uyli limi mita tado dos( s(OD ODBC BCyS ySQL QLit ite) e). . Nosotros Nosotroshemosañadidoelde hemosañadidoelde PostGres PostGresqly qly eldeMySQLen nuestros nuestrosejemplos, ejemplos, paracualquiera paracualquiera delas3 plataform plataformasquehemosexplica asquehemosexplicado.Puedescompi do.Puedescompilartúmismocualqui lartúmismocualquierplugin erpluginbajando bajando losfuentesdelSDK(qt-everyo losfuentesdelSDK(qt-everyone-src),yco ne-src),ycompilandoloquen mpilandoloquenecesitesconf ecesitesconformealosREADMEque ormealosREADMEque le acompañan acompañan.. El último último parágrafo parágrafo de estetema explica explica el procesosde procesosde compilaci compilación ón del driver driver paraMySQL.
115
InstaladoresenWindowsyLinux Sabiendo ya el paquete que deb debemos copiar, podríamos nosotros mismos crear un instaladormedianteunaaplicación,scriptosimi instaladormedianteunaaplic ación,scriptosimilar.Vamosaproponerteunaopción lar.Vamosaproponerteunaopciónmássencilla mássencilla paraquepuedastenerencuent paraquepuedastenerencuentaa aa lahoradeentregartusaplic lahoradeentregartusaplicacio aciones. nes.Y Y vamosa vamosa empezarpor empezarpor Wind Window ows s y Linu Linux. x. Aunq Aunque ue hay hay much muchas as apli aplica caci cion ones es que que está están n espe especi cial aliz izad adas as en hace hacer r instalado instaladoresde resde unpaquete,nosotros unpaquete,nosotros hemos escogido escogido unaen particular particular desarroll desarrolladaen adaen Tk/Tcl Tk/Tcl quepermitecrearinstaladoresparaWindows,LinuxyotrosUNIX.SunombreesInstallJammer, queesgratuito,decódigoabi queesgratuito,decódigoabiertoymultipl ertoymultiplataforma,comoa ataforma,comoanosotrosn nosotrosnosgusta. osgusta. Crea Crearu runi nins nsta tala lado dorc rcon ones esta tahe herr rram amie ient ntae aesm smuy uyse senc ncil illo loei eint ntui uiti tivo voape apesa sard rdeq eque uees esté té eninglés.Vamosaseguircon eninglés.Vamosaseguirconnuestraaplicaci nuestraaplicaciónparahacerelej ónparahacerelejemplodecreación emplodecreacióndelinstalador. delinstalador. Alar Alarra ranc ncar arIn Inst stal allJ lJamm ammer er,p ,pul ulsa samo mose senN nNew ewPr Proj ojec ect, t,ys ysen enos osab abre reun unwi wiza zard rdpa para racr crea ear r elproyectodelinstal elproyectodelinstalador. ador.Rellenamo Rellenamoslosdatosquesenosvanpidiendo slosdatosquesenosvanpidiendo.Contest .Contestadastodas adastodaslas las pregun preguntas tas,, pulsam pulsamos os en Finish Finish,, y se nos nos abre abre el Instal Install l Design Designer er con con una serie serie de ítems ítems que podemosircambiandoanuestrogusto,odejandosuvalorpordefecto.Dejamosalusuarioel husmearportodaunaingentecantidaddeparámetros.Vamosaconstruirconlosvalorespor defectoparaWindowsoparaLinuxcon defectoparaWindowsoparaLinuxconformenecesitemo formenecesitemos.PulsamossobreB s.PulsamossobreBuildInstall,yv uildInstall,yvemos emos en la vent ventan ana a como como se nos nos crea crea Appli Applica cati tion on-1 -1.0 .0-S -Set etup up.e .exe xe (por (por ejem ejempl plo o para para Wind Window ows) s).. Si pulsamossobr pulsamossobresulink,senosabrelacarpetadonde esulink,senosabrelacarpetadondese se hacreadoel hacreadoel ejecutabl ejecutableinstala einstalador, dor,listo listo paraserusado. Así Asíde des sen enci cill llo oes esc cre rear aru un nin inst stal alad ador orc con one est ste ese senc ncil illo lop pro rogr gram ama. a.
InstaladoresenMacOSX Para Para crea crearin rinst stal alad ador ores es en Mac, Mac,noha nohayni yning ngun unaap aapli lica caci ción ón simp simple le ylibr ylibreco ecomo mo la que que acabamosdever,perosípodemosusarherramientasquevienenennuestrosistemapordefecto, una una vez vez hemo hemos s inst instal alad ado o XCod XCode, e, y que que nos nos perm permit itir irán án crea crear r un inst instal alad ador or con con aspe aspect cto o profesional.Vamosahacerlode2manerasdiferentes:unamedianteunaimagen.dmgdondese arrastrael.appsobrelacarpetaAplicación,yotracreandounapaqueteinstalablemediantela herramientaPackageMakerdeXCode. Segu Seguim imos osus usan ando donu nues estr troe oeje jemp mplo loya yaem empa paqu quet etad adoe oenu nunb nbun undl dlec ecom ompl plet eto, o,ta talc lcom omol olo o dejamos,preparadoparallevaraotro dejamos,preparadopara llevaraotroequipo(applicatio equipo(application.app). n.app). Vamos a crear una imagen DMG con la utilidad de discos. Pul Pulsaremos sobre Nueva Imagen,yvamosaponerlosajus Imagen,yvamosaponerlosajustesdelaimagendeaco tesdelaimagendeacontinuación ntinuación: :
116
Lacreamosdeltamañosuficienteparaquequepan.appyunafondoenPNGquecrearemosde 700x400,similaraéste:
MontamosconundoblecliclaimagenDMGvacíareciéncreada,yarrastramosaellaelbundle .app.Luegonosvamosaldiscoraiz,ycreamosunaliasdelacarpetaAplicación,laarrastramosal DMG DMG y le camb cambia iamo mos s el nomb nombre re a Aplic Aplicac ació ión. n. Con Con el DMG DMG mont montad ado o y abie abiert rto o en el find finder er,, pulsamosCmd+J.Seabrenlasopcion pulsamosCmd+J.Seab renlasopcionesdecarpeta,yhacemo esdecarpeta,yhacemosalgocomoest salgocomoesto: o:
El fondo fondo lo elegi elegimos mos hacien haciendo do doble doble click click en el cuadro cuadro de arrast arrastrar rar la imagen,yseleccionamoselPNG. imagen,yseleccionamoselPNG.YaseveráelDMGmo YaseveráelDMGmontadoconlaimag ntadoconlaimagen en defondo,y podremossituar podremossituar losiconos,y losiconos,y redimensi redimensionar onar laventana para ajustarlaaltamañodelfondo.Expulsamosel ajustarlaaltamañodelf ondo.ExpulsamoselDMGmontado,yno DMGmontado,ynosvamosa svamosa la util utilid idad ad de disc discos os,, y elig eligie iend ndo o la imag imagen en DMG DMG crea creada da,, puls pulsam amos os en conv conver erti tir, r, y la guar guardam damos os esta esta vez vez comp compri rimi mida da.. Se crea crea el DMG DMG fina final l prepar preparado ado para para ser distri distribui buido. do. Al montar montarlo lo quedar quedará á algo algo como como esto: esto:
Dondequedaclaralaintencióndequesearrastreeliconodeapplication.appsobrelacarpeta Applicaciones,paraqueseproduzcal Applicaciones,paraqueseproduzcalainstalación ainstalación. . Pase Pasemo mosal salase asegu gund ndaop aopci ción ón,qu ,queco econs nsis iste teenc encre rear arunpa unpaqu quet etede edein inst stal alac ació iónde ndelti ltipo po *.pkg con el PackageMaker del XCode. Dicha aplicación la puedes encontrar en /Develope /Developer/App r/Applica lication tions/Uti s/Utiliti lities/Pa es/Package ckageMaker Maker.. Al arrancarl arrancarla, a, loprimeroque nospide es la organización(tipocom.nombre_org organización(tipocom.nombre_organizacion),yeloperativoobjetiv anizacion),yeloperativoobjetivomínimo.Pulsandosobreel omínimo.Pulsandosobreel
117
+enlaesquinainferiorizquierdapodemosañadirloscomponentesainstalar,queennuestro casoessolamenteuno(application.app). Si navegas por esta herramienta, verás que tambiénesmuyintuitivay potente,yestáeninglés.En la zona zona de cont conten enid idos os,, se pueden poner varios elem elemen ento tos s (apl (aplic icac acio ione nes, s, librerías, frameworks, ayudas, etc). En Conf onfigura gurattion ion, pode podemo mos s elegirquévamosainstalar y d o n d e , s i va m o s a permitir que el usuario reubiquelainstalaciónysi se va a requerir aute autent ntif ific icac ació ión n por por part parte e del administrador para inst instal alar arla la,, de mane manera ra que que podrem podremos os tambi también én copiar copiar ficheros en zonas del sistema.EnContentspodemoselegirelusuarioygrupoalqueperteneceráestecontenidoysus permisos.Siseleccionamoselpaquetededistribución(encimadeContents),podremoseditarlas opcio opciones nes del paquet paquete e compl completo eto,, ponién poniéndol dole e un nombre nombre,, los tipos tipos de selecc selección ión accesi accesible bles s al usuario,eldestinodeinstalación,podremosprogramarrequisitosparainstalarlo,comoespacio libr libre e en disc disco o duro duro,, CPU, CPU, vers versió ión n del del sist sistem ema a oper operat ativ ivo o y much muchos os más, más, tamb tambié ién n se podr podrán án prog progra rama mar r acci accion ones es a llev llevar ar a cabo cabo ante antes s de proc proced eder er a la inst instal alac ació ión, n, y una una vez vez ésta ésta ha finalizado.Enlazonasuperiorderecha,tenemoselicono“EditInterface”,quenospermiteeditar el inte interf rfaz az de inst instal alac ació ión, n, camb cambia iand ndo o su fond fondo, o, y el text texto o de cada cada uno uno de los los paso pasos s de la instalación. Una vez, ya tenemos mos todas las opciones rellenadas conforme a nuestra necesidad, podemospulsarenel podemospulsarenel iconode iconode lazonasuperiorizqui lazonasuperiorizquierda“Buil erda“Build”,queconstr d”,queconstruirá uiráel el paquetede paquetede instalaci instalación.Dicho ón.Dicho paquete paquete puedeentregarse puedeentregarse como tal,o puedemeterseen unaDMG tal como aprendimosanteriormente.
CompilacióndeundriverSQL Elúl Elúlti timo model delos oses esco coll llos osaso asort rtea earp rpar aradi adist stri ribu buir irnu nues estr traap aapli lica caci ción ónesq esque ueten tenga gamo mosel sel driverdeltipodebasededatosquevamosausarennuestraaplicacióncompilado.Laversión OpenSourcedeQt4,porcuestiones OpenSourcedeQt4,porcuestionesdelicenciasno delicenciasnollevacompiladosl llevacompiladoslosdriversmásq osdriversmásquedeODBC uedeODBC ySQLite(quevaintegradoenQt4),yquizásconestohayasuficienteparamuchos,perounagran cantid cantidad ad de gente gente usa otras otras bases bases de datos, datos, y aunque aunque podría podrían n usar usar el driver driver ODBC ODBC para para ello, ello, siempreesrecomendableusareldriver siempreesrecomendableusareldrivernativosi nativosinmásañadidos. nmásañadidos. Vamo Vamosp spor orta tant ntoa oaex expl plic icar ar,c ,com omoc ocom ompi pila lare reld ldri rive verp rpar arau auna nadel delas asba base sesd sdeda edato tosm smás ás usadas usadas,, que essin duda duda MySQL. MySQL. Para Para poder poder hacerl hacerlo, o, además además detener instal instalado ado elsistemade elsistemade desarrollocompleto,comoyatenemos,parapoderejecutarqmake,g++ymakedesdelalineade comandos,tendremosquebajarno comandos,tendremosquebajarnoslosfuentes slosfuentesdeMySQLydeQt4. deMySQLydeQt4. Losfu fuentesde deMy MySQL,po podemosba bajarlosde de http://www.my http://www.mysql.com/downl sql.com/downloads/mysql/ oads/mysql/ en la versi ersio on esse essen ntial tialss. A part parte e de la insta nstallaci ación compl omplet eta, a, ten tendre dremos mos dos dos car carpeta petass fundament fundamentalesque alesque son/liby /include, /include, donde tendremos tendremos respectiv respectivament amente e lalibrería lalibrería necesaria necesaria paranuestrocliente,ylosf paranuestrocliente,ylosfuentesdeMySQL. uentesdeMySQL.
118
Los fuentes de Qt4, están en la web de Nokia http://qt.nokia.com/ , y se llam llama a qtqteverywhere-opensource-src-4.x.x.Enestepaqueteencontraremoslosfuentesquenecesitamos denuestrodriverenlacarpeta$QT_SOURCES/s denuestrodriverenlacarpeta$QT_SOURCES/src/plugins/ rc/plugins/sqldrivers/ sqldrivers/mysql.Ahíencontr mysql.Ahíencontraremos aremos unficheromain.cppconel unficheromain.cppconelcódigodeldri códigodeldriveryelfi veryelficherodeproyect cherodeproyectomysql.pro. omysql.pro. Situ Situad ados osene eneldi ldire rect ctor orio iodelo delosf sfue uent ntes esdel deldri drive verpa rpara raQt4 Qt4,ej ,ejec ecut utar arem emos oslo lossi ssigu guie ient ntes es comandosenlínea: 1.-Windows(VS2008): Presuponemos%PATH_MYSQL%comoelraiz Presuponemos%PATH_MYS QL%comoelraizdelosfuentesde delosfuentesdeMySQL. MySQL. qmake"INCLUDEPATH+=%PATH_MYSQL%\include""LIBS+=%PATH_MYSQL%\lib\opt\libmysql.lib"mysql.pro nmakedebug nmakerelease
Ten Tendrem dremo os al final inal las las versi ersio ones debu debug g y relea elease se de los los dri drivers, ers, está estáti ticca y diná dinámi micca (qsqlmysql4.dll/qsql_mysql.lib).
2.-Linux: Presuponemos$PATH_MYSQLcomolar Presuponemos$PATH_ MYSQLcomolaraizdelosfuent aizdelosfuentesdeMySQL esdeMySQL qmake"INCLUDEPATH+=$PATH_MYSQL/include""LIBS+=-L$PATH_MYSQL/lib-lmysqlclient_r"mysql.pro make
Secrearálaversiónreleasedin Secrearálaversiónreleasedinámicalibqsql ámicalibqsqlmysql.so. mysql.so.
3.-MacOSX: Presuponemos$PATH_MYSQLcomolar Presuponemos$PATH_ MYSQLcomolaraizdelosfuent aizdelosfuentesdeMySQL esdeMySQL qmake-specmacx-g++"INCLUDEPATH+=PATH_MYSQL/include""LIBS+=-LPATH_MYSQL/lib-lmysqlclient_r"mysql.pro makedebug makerelease
Secrearánlaversiónreleasey Secrearánlaversiónreleaseydebugdinámicas debugdinámicaslibqsqlmysql. libqsqlmysql.dylibylib dylibylibqsqlmysql_debu qsqlmysql_debug.dylib. g.dylib.
Con Con esto esto damospor damospor final finaliz izado ado el conte contenid nido o de este este libro, libro, en el que hemos hemos inten intentad tado o dotarl dotarlo o de todo todo el conten contenido ido prácti práctico co necesa necesario rio para para el desarr desarroll ollo o eficaz eficaz de aplica aplicaci cione ones s de propósitogeneralenQt4,peroaún propósitogeneralenQt4,peroaúnquedamuchocaminopo quedamuchocaminoporandar,perosin randar,perosindudayatenemoslos dudayatenemoslos fundamentosparaqueesteviajeseamás fundamentosparaqueesteviajeseamásagradableysenci agradableysencillo. llo.
119
BIBLIOGRAFIA
AnintroductiontodesignpatternsinC++withQt4-PrenticeHall-Alan&PaulEzust ThebookofQt4.Theart ThebookofQt4.Theartofbuilding ofbuildingQtapplicati Qtapplications-NoSta ons-NoStarchPress rchPress-DanielMol -DanielMolkentin kentin C++GUIProgrammingwithQt4-PrenticeHall-JasminBlanchette&MarkSummerfeld QtAssistant–QTNOKIASDK TheC++ProgrammingLangua TheC++ProgrammingLanguage–AddisonWesl ge–AddisonWesley–Bjarn ey–BjarneStroustrup eStroustrup
120
Finalizadoel20deOctubre Finalizadoel20deOctubrede2010– de2010–anónimo anónimo