După mulţi nervi pierduţi prin toate chichiţele și meandrele C++-ului cu OpenGL și MFC, trecerea la C# şi Java a fost ca o adiere de primăvară. Dar într-o zi cu ploaie şi vânt irlandez, într-un cubicle slab iluminat într-un colț de birou, am dat de Qt și o dată cu el o lume minunată de C++ şi-a deschis porţile, cu revelații noi şi noi de atunci încoace. După ce am trecut la Qt, lucrul cu C++ a devenit o bucurie din nou fiind unul dintre mediile în care lucrez cu cea mai mare plăcere. Dezvoltare este o adevărată experienţă, totul rămânând nativ, rapid și ușor de întreținut în același timp.
C++ este unul dintre cele mai folosite limbaje de programare. Are rădăcini istorice puternice și încă este unul dintre cele mai populare limbaje, mai ales acolo unde eficienţa şi performanţa sunt aspecte cheie. E folosit în medii industriale și în sisteme embedded sau de înaltă performanță, în dezvoltare de aplicaţii, şi multe alte domenii. C++ e rapid, eficient, multi-paradigmatic, compatibil cu limbajul C, în același timp suportând funcţionalităţi moderne. La aceste calități o adăugăm și pe aceea de a fi simultan un limbaj low-level și high-level.
Există totuși unele probleme. Pe de o parte, limbajul în sine este deseori prea greoi, flexibilitatea se mai diminuează într-o complexitate adăugată, care cauzează probleme legate de pointere, demonstrând că limbajul nu e chiar multi-platformă. Pe de altă parte, pentru a dezvolta aplicații, avem nevoie de un framework potrivit care se integrează bine în filosofia limbajului și e practic de folosit.
Qt, cu elementele sale care complementează deficiențele limbajului C++ și framework-ul său cu adevărat multiplatformă care acoperă practic toate necesitățile de bază, rezolvă ambele probleme, asigurând o dezvoltare foarte plăcută și curată. Astfel a devenit Qt standardul de-facto de framework de dezvoltare C++.
Qt e un framework UI și de aplicații cuprinzător. Are un API consistent și curat. Furnizează un mecanism robust de comunicare între obiecte. Este cu adevărat multi-platformă, dar în acelaşi timp reușește să ofere toate funcţionalităţile de nivel scăzut necesare și comportamentele UI native ale sistemelor de operare pe care le sprijină. Oferă, de asemenea, un set de instrumente de dezvoltare pentru rapid application development (RAD), care pot înlocui sau se pot integra în IDE-uri majore, cum ar fi Visual Studio sau Eclipse. Este profund și bine documentat, are o comunitate mare și puternică în spatele lui și este adoptat pe scară largă. Ar fi indicat să-l folosiţi.
Qt aduce o colecție de biblioteci pentru a satisface toate nevoile de bază în dezvoltarea de aplicații. Desigur, cum este cazul cu orice alt framework, Qt nu a fost gândit să satisfacă orice capriciu al oricărui dezvoltator din lume, dar acoperă, practic, tot asemenea un framework general.
Qt are un set de biblioteci numit QtCore, care acoperă, după cum sugerează și numele, un set de funcționalități de bază. Fără a oferi o enumerare exhaustivă, acest pachet de bază include următoarele:
În plus, există o serie de alte pachete, cele mai multe dintre ele fiind mature și bogate în feature-uri în domeniile respective. Aceste domenii includ procesare XML, capacități multimedia, suport de baze de date SQL, unit testing, OpenGL, integrare WebKit, biblioteci şi instrumente de internaționalizare, manipulare SVG, comunicare D-Bus, controale ActiveX și altele.
Qt oferă unele extensii de limbă prin intermediul unor macro-uri care sunt prelucrate de către generatorul de cod numit Meta-object Compiler (MOC). Acest lucru aduce o introspecție run-time mai avansată și posibilitatea de a avea anumite mecanisme speciale în clase care folosesc Qt. Principalul mecanism de acest tip special este comunicarea între obiecte numit signals and slots.
Signals and slots oferă o alternativă mai bună la callback-uri, fiind loosely-coupled și type-safe. Acesta înseamnă că, atunci când se utilizează conexiuni între signals şi slots, obiectele nu sunt conştiente de obiectele conectate la ele. De asemenea aspectul type-safe este garantat prin limitarea conexiunilor bazată pe compatibilitatea dintre argumentele de signals și slots.
Un signal e emis atunci când un eveniment trebuie să fie anunțat. Un signal e de fapt o funcţie fără body.
Slot-urile sunt funcţii regulate care sunt apelate ca răspuns la un signal.
Conexiunea este configurată prin intermediul unui apel QObject::connect(), care permite să fie conectate numai acele signals și slots care au argumente compatibile, garantând astfel că totul e type-safe. Obiectele nu sunt conștiente de semnalele la care slot-urile lor sunt înscrise, nici de slot-urile care sunt conectate la semnalele lor, asigurând astfel loose coupling.
Mai jos găsiţi un exemplu despre cum o clasă C++ "normală" poate fi extinsă pentru a suporta signals and slots și modul în care se realizează acest comportament.
Declaraţia minimală C++:
class Counter
{
public:
Counter() { m_value = 0; }
int value() const {
return m_value;
}
void setValue(int value);
private:
int m_value;
};
Extinderea cu elemente specifice Qt:
#include
class Counter : public QObject
{
Q_OBJECT
public:
Counter() { m_value = 0; }
Versiunea Qt are aceeași stare internă, dar pentru a suporta signals și slots, codul a fost extins cu următoarele:
Aceste macro-uri speciale sunt preluate de către generatorul de cod MOC care generează clasele și funcțiile corespunzătoare care realizează toate mecanismele specifice Qt.
Slot-urile trebuie implementate de către dezvoltatorul aplicației. Mai jos este o posibilă implementare al slot-ului
Counter::setValue():
void Counter::setValue(int value)
{
if (value != m_value) {
m_value = value;
emit valueChanged(value);
}
}
Se poate vedea apelul emit, care emite un signal valueChanged()cu noua valoare ca argument, de fiecare dată când o nouă valoare este asignată.
Mai jos aveţi un exemplu de un fragment de cod despre utilizarea a două obiecte Counter, a și b , în care signal-ul valueChanged() al obiectului a este conectat la slot-ul setValue() al obiectului b :
Counter a, b;
QObject::connect(&a, &Counter::valueChanged,
&b, &Counter::setValue);
a.setValue(12);
// a.value() == 12, b.value() == 12
b.setValue(48);
// a.value() == 12, b.value() == 48
Observați comportamentul mecanismului signals and slots:
În diagrama de mai sus este arătat modul în care conexiunile sunt formate între diferite obiecte, ca rezultat al apelelor funcției connect():
Cele două mecanisme de managementul resurselor din Qt sunt ownership hierarchy şi implicit sharing.
Ownership hierarchy constă într-un arbore de obiecte care se ocupă de distrugerea descendenților. Ori de câte ori un nou obiect moştenind de la QObject este creat pe heap (folosind new), acestuia se poate atribui un părinte QObject, ceea ce va rezulta eventual într-un arbore de obiecte. Ori de câte ori un obiect este distrus din arbore, toți descendenții săi sunt de asemenea distruşi.
În următorul fragment de cod obj2 va fi distrus în timpul distrugerii obj1 (notăm, că crearea lui obj2 nu utilizează un copy-constructor, căci aceştia sunt privaţi în QObject).
QObject *obj1 = new QObject();
QObject *obj2 = new QObject(obj1);
//this also sets obj1 as the parent of obj2
delete obj1;
Implicit sharing se referă la un mecanism intern folosit mai ales pentru container-e Qt și obiecte de mari dimensiuni. El implementează abordarea copy-on-write, adică copiază doar un pointer la structura de date la fiecare asignare, și copiază întreaga structură de date numai în cazul în care există o operațiune de scriere. Acest lucru garantează că nu sunt create copii redundante a structurilor mari de date, îmbunătățind astfel performanța .
În codul de mai jos se presupune că list1 este o listă foarte mare. Când este creat list2, list1 nu va fi copiat, doar un pointer este copiat în interior, Qt ocupându-se de aceasta în fundal:
// suppose list1 is a very large list of type QList
QList list2 = list1;
QString str1 = list2[100];
Când list2[100] este citit, acesta va fi, de fapt, exact aceeași locație de memorie ca list1[100], din moment ce nici o operație de scriere n-a fost efectuată, iar lista mare nu a fost copiată.
Qt este cu adevărat cross-platform. Acest lucru înseamnă un singur codebase și un stream unic de dezvoltare pentru orice număr de platforme suportate. Este nevoie doar de a seta diferite configurații de build pentru a face deployment pe sisteme diferite. Aceasta a fost una dintre cele mai importante motive pentru care Qt a fost ales într-un proiect industrial mare la care am lucrat, deoarece era necesar să fie suportat pe sisteme embedded bazate pe Linux, precum și PC-uri Windows.
Sistemele de operare majore suportate sunt Microsoft Windows, Linux și Mac OS X. Alte sisteme include, printre altele, Android , Solaris şi HP UX.
Executabile generate sunt cu adevărat native. Qt utilizează elemente GUI native și API-urile de nivel scăzut ale platformelor pe care le suportă, așa cum se arată în imaginea de mai jos. Pentru dezvoltatori aceasta oferă o încapsulare a funcţionalităţilor de sistem de operare independent de platformă, cum ar fi procesarea de fișiere, funcţionalităţi de reţea sau imprimare.
API-ul de Qt nu este diferit de cele mai multe binecunoscute framework-uri în a fi intuitiv, robust și convenabil. După o oarecare familiarizare, navigarea define uşoară: lucrurile sunt, de obicei, numite cum ne-am aștepta ca ele să fie numite și sunt amplasate în locul în care ne-am aștepta ca ele să fie amplasate.
Documentația API este relevantă și simplă, nu e supraîncărcată . Uneori este nevoie de ceva timp pentru a găsi ceea ce căutăm. Dar, în general, aceasta este aprofundată, extensivă, având exemple relevante, frumos organizată și atrăgătore, fiind la linie și, ocazional, chiar mai bună decât cele mai bune sisteme de documentaţie din industrie, cum ar fi MSDN sau documentaţia Java API .
Qt oferă mai multe tipuri de licențe, cele mai importante două fiind LGPL și cea comercială. LGPL poate fi folosit pentru dynamic linking, adică pur și simplu folosind Qt prin DLL-uri. Licența comercială este destinată pentru cei care doresc să modifice bibliotecile Qt și nu doresc să facă aceste modificări publice.
Qt nu este un framework recent. A fost pe piaţă practic de două decenii, fiind folosit pe scară largă atât într-un mediu personal cât şi în cel industrial. Stă la baza lui KDE, al doilea cel mai popular mediu desktop pe Linux, de mai mult de cincisprezece ani, dovedind stabilitate prin milioane de utilizatori. A fost folosit de o mare varietate a jucătorilor industriali și în multe domenii de aplicare, crescând până la un nivel la care poate fi numit cu încredere stabil, matur și robust.
Qt aduce, de asemenea, "binding"-uri la un număr de limbi în afară de C++, cum ar fi Python, Java, C#, Ruby și altele. Astfel, Qt este atât de apreciat, încât a apărut nevoia de a-l folosi şi din alte limbaje.
Există un set de instrumente care este furnizat împreună cu Qt pentru a accelera procesul de dezvoltare.
Acestea includ următoarele :
Există, de asemenea, unele instrumente de linie de comandă pentru compilarea proiectelor Qt, pentru generarea de clase de suport specifice Qt, sau compilarea fișierelor UI.
Integrarea Visual Studio are unele limitări, dar cu excepția unor paşi suplimentari care trebuie să fie făcute manual, este în cea mai mare parte bine realizat, debugging-ul fiind şi el funcţional.
Qt a crescut mult, devenind o platformă atât pentru desktop cât și pentru dezvoltarea mobilă, în scenarii de la uzul personal până la software-ul industrial sau embedded. Ca atare, serveşte multe nevoi, toate acestea neputând fi cuprinse în cadrul acestui articol. Cu toate acestea, având în vedere o descriere mai întreagă, vom enumera câteva alte domenii în care Qt poate fi o soluție adecvată:
QObject obj1;
//adds a new property to obj1
obj1.setProperty("new property", 2.5);
//d == 2.5
double d = obj1.propety("new property");
Fără îndoială, există dezavantaje la Qt. La dezvoltarea exclusiv pentru platforma Windows, o soluție bazată pe .NET ar putea fi mai la îndemână pentru unii. Qt a ajuns să fie un framework destul de mare, aşa că ar putea fi un pic mai greu a-l folosi. De asemenea, există uneori probleme cu driver-ele de baze de date și cu threading-ul. Apoi, pot apărea probleme atunci când îl vom utiliza la o scară largă, de altfel ca în cazul oricărui framework.
Dar avantajele depășesc dezavantajele cu mult. Putem găsi întotdeauna soluţii la probleme cu ajutorul unor forumuri sau cercetând documentaţia API.
Din păcate, m-am îndepărtat de Qt deja de câteva luni bune. Dar odată am avut nevoie să verific ceva despre modelul MVC în general, și m-am gândit că descrierea mecanismului de modele şi view-uri al Qt ar putea clarifica unele chestiuni. Și într-adevăr, oprindu-mă cu o privire rapidă în documentația Qt API a fost nu numai absolut util pentru lucruri chiar ne-legate de Qt, dar m-a umplut de bucurie și de nostalgie după acele momente când m-am afundat în lumea Qt atât de minunată.