Acasă / Birou / Descrierea bibliotecii Qt Creator. Instalarea și configurarea Qt Creator. Un exemplu simplu de plasare a widget-urilor într-o fereastră de aplicație

Descrierea bibliotecii Qt Creator. Instalarea și configurarea Qt Creator. Un exemplu simplu de plasare a widget-urilor într-o fereastră de aplicație

Acesta este un set de instrumente de dezvoltare software multiplatformă în limbajul de programare C++. Există, de asemenea, „legături” la multe alte limbaje de programare: Python - PyQt, Ruby - QtRuby, Java - Qt Jambi, PHP - PHP-Qt și altele.
Vă permite să rulați software scris cu ajutorul acestuia în cele mai moderne sisteme de operare prin simpla compilare a programului pentru fiecare sistem de operare fără a schimba codul sursă. Include toate clasele de bază care pot fi necesare la dezvoltarea aplicației software, variind de la elemente de interfață grafică la clase pentru lucrul cu rețeaua, bazele de date și XML. Qt este complet orientat pe obiecte, ușor de extensibil și acceptă tehnici de programare bazate pe componente.
În acest articol, vă voi arăta cum să scrieți un program simplu „Hello, World!”. folosind biblioteca Qt4

Mediul de dezvoltare

Mai întâi, să definim mediul de dezvoltare. Personal, folosesc IDE-ul multiplatform Code::Blocks pentru a scrie un program (puteți citi mai multe despre lucrul în acest IDE cu Qt4). Există, de asemenea, pluginuri pentru lucrul cu Qt în Eclipse. O versiune comercială a Qt pentru MS Windows poate fi integrată în MSVS. Programele pot fi scrise și în orice editor de text, apoi compilați-le din linia de comandă.
Pentru claritate, voi arăta cum să compilați manual programele scrise în Qt.

Primul program

Mai întâi, creați un fișier în orice editor de text și numiți-l, de exemplu, main.cpp
Să scriem următoarele în el:
  1. #include
  2. #include
  3. Aplicația QApplication (argc, argv);
  4. QDialog *dialog = QDialog nou;
  5. QLabel *label = nou QLabel(dialog);
  6. label->setText( "Salut Lume!" );
  7. dialog->arata();
  8. return app.exec();

În rândurile 1 și 2 am inclus fișierele antet Qt în care se află clasele principale.
Pe linia 4 am declarat funcția principală - functia principala, de la care începe execuția oricărui program. Returnează un număr întreg (rezultatul programului; 0 - dacă totul este în ordine) și ia ca intrare două variabile - numărul de parametri din linia de comandă și matricea în care sunt stocați.
Pe linia 5 creăm un obiect de aplicație. Transmitem variabile de linie de comandă acestui obiect.
Pe linia 6 creăm un dialog - o fereastră grafică dreptunghiulară cu o bară de titlu și butoane în colțul din dreapta sus. Creați o etichetă (linia 7). Când creăm o etichetă, trecem un pointer către dialog către constructorul său, care devine părintele său. Când ștergeți un părinte, toți copiii acestuia sunt șterși automat, ceea ce este foarte convenabil. Apoi setăm textul etichetei apelând funcția setText() (linia 8). După cum puteți vedea din exemplu, puteți utiliza etichete html pentru textul afișat.
Pe linia 9 afișăm pe ecran caseta de dialog pentru etichete.
În cele din urmă, pe linia 10, începem bucla de evenimente a sistemului de operare a aplicației. Returnăm rezultatul operației obiectului ca rezultat al programului.

Compilare

Acum să compilam programul scris.
Să mergem la directorul în care am salvat fișierul main.cpp și să rulăm comanda

$ qmake -proiect

Aceasta va crea un șablon de proiect Qt4, care va include automat toate fișierele de cod sursă aflate în acest director. Rezultatul va fi un fișier cu același nume ca directorul curent și extensia .pro. Va arata asa:

ȘABLON=aplicație
ȚINTĂ =
DEPENDPATH += .
INCLUDEPATH += .

#Intrare
SURSE += main.cpp

Ca rezultat, vom obține un Makefile, pe care îl folosim pentru a compila programul rulând următoarea comandă:

Să așteptăm până când procesul de compilare se termină și să rulăm primul nostru program. Va arata cam asa:

Al doilea program

Pentru a obține control deplin asupra ferestrelor create și a altor widget-uri, trebuie să creați clase derivate din acestea. Să creăm o clasă derivată MyDialog. Vom folosi clasa QDialog ca clasă părinte. Vom plasa descrierea clasei noastre în fișierul antet mydialog.h:
  1. #include
  2. #include
  3. #include
  4. #include
  5. clasa MyDialog: QDialog public (
  6. Q_OBJECT
  7. public:
  8. MyDialog(QWidget *parent = 0);
* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.
În primele patru rânduri includem fișierele de antet necesare ale elementelor grafice utilizate - dialogul, butonul, eticheta și managerul de layout vertical. Utilizați fișiere de antet atât de mari precum , etc. în proiectele mari nu este recomandat, deoarece crește timpul de compilare.
Pe linia șase, am definit clasa noastră ca să derive din QDialog.
Pe linia următoare am specificat macro-ul Q_OBJECT, care îi spune preprocesorului Qt că această clasă va folosi caracteristici Qt suplimentare, de exemplu, sistemul de semnal și slot.
Pe linia 9 specificăm constructorul casetei noastre de dialog. Are un singur parametru de intrare - un pointer către obiectul părinte (0 dacă nu există niciun părinte).
Vom defini constructorul clasei noastre în fișierul mydialog.cpp:
  1. #include „mydialog.h”
  2. MyDialog::MyDialog(QWidget *parent) : QDialog(parent) (
  3. QVBoxLayout *layout = nou QVBoxLayout(this);
  4. QLabel *label = nou QLabel(this);
  5. label->setText( "Salut Lume!" );
  6. QPushButton *button = QPushButton nou (acest );
  7. buton->setText(„Închidere”);
  8. layout->addWidget(etichetă);
  9. layout->addWidget(button);
  10. connect(button, SIGNAL(clicked()), this , SLOT(close()));
* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

Pe linia 4 creăm un manager de layout care va afișa automat toate widget-urile adăugate pe verticală. Crearea unei inscripții este similară cu exemplul anterior.
În rândurile 7 și 8 creăm un buton și setăm textul acestuia. Pe următoarele două rânduri adăugăm widget-urile noastre la managerul de layout, astfel încât acesta să le aranjeze automat.
Pe linia 11, conectăm semnalul clicked() al butonului la slotul close() al casetei noastre de dialog. Fiecare obiect Qt poate avea propriile semnale și sloturi, care pot fi conectate la semnalele și sloturile altor obiecte și astfel să comunice între elementele programului.
Fișierul main.cpp va arăta astfel:
  1. #include
  2. #include „mydialog.h”
  3. int main(int argc, char * argv) (
  4. Aplicația QApplication (argc, argv);
  5. MyDialog *dialog = nou MyDialog;
  6. dialog->arata();
  7. return app.exec();
* Acest cod sursă a fost evidențiat cu Sursa de evidențiere a codului.

Reconstruirea proiectului în echipă

$ qmake -proiect

Astfel încât fișierele noi să fie adăugate automat la el și le compilăm. Iată cum arată noul nostru program:

Al treilea program

Dacă o casetă de dialog conține o mulțime de elemente grafice, atunci crearea unor astfel de ferestre poate fi destul de plictisitoare. Pentru a simplifica acest proces, există un instrument numit Qt Designer. Hai să-l lansăm

Și alegeți să creați o casetă de dialog fără butoane. Adăugăm o etichetă și un buton și le edităm textul. Folosind instrumentul Signal/Slot Editor, conectăm semnalul apăsat() al butonului la slotul de închidere() al casetei de dialog. Le aranjam vertical folosind managerul de layout. Salvați fișierul rezultat sub numele mydialog.ui. Mai târziu va fi convertit automat într-un fișier antet numit ui_mydialog.h.
Schimbăm fișierul antet al casetei noastre de dialog mydialog.h după cum urmează:

Un programator începător se îndoiește constant ce tehnologii să înceapă să stăpânească, aceasta sau oricare alta. Ar trebui un programator începător să învețe Qt? Neapărat necesar! De exemplu, citiți această postare sau căutați ceva pe Internet. Încă nu sunteți sigur dacă aveți nevoie de Qt? Dacă scrieți în C++, ar trebui să știți Qt, pur și simplu nu aveți altă alternativă.

Deci, hai să mergem...

Să scriem, de exemplu, un calculator simplu - adunând două numere.

Noi creăm proiect nou. După cum putem vedea, există mai multe tipuri de aplicații. Noi, în calitate de începători, selectăm „Aplicația Qt Widgets”:

Indicăm numele proiectului și folderul pentru a plasa fișierele, pentru mine: C:\projects\qt

Qt generează automat următoarele fișiere:

  • lesson1.pro - fișier proiect
  • main.cpp - fișier principal cu funcția main().
  • mainwindow.cpp - codul sursă al ferestrei principale
  • mainwindow.h - fișierul antet al ferestrei principale
  • mainwindow.ui - fișierul formular pentru fereastra principală

Faceți clic pe butonul „Finish” - se deschide editorul pentru dezvoltarea de programe în Qt.

Pentru moment, totul ar trebui să fie clar pentru un dezvoltator Qt începător...

Deschideți formularul principal (pentru a face acest lucru, accesați mainwindow.ui).

În stânga sunt componentele pentru crearea unui formular de ecran, în dreapta este formularul în sine, este gol. Să adăugăm componentele necesare și să nu uităm să numim câmpurile de intrare QLineEdit astfel: edtA, edtB și, respectiv, edtC.

Vedem câmpurile de introducere pe ecran, legendele lor în stânga și butonul „A + B =”. Când facem clic pe acest buton trebuie să adăugăm A și B și să plasăm rezultatul în C.

Ce ar trebuii să fac? Orice dezvoltator Qt începător va presupune că trebuie să atașați butonul de gestionare a clicurilor pe buton și va avea dreptate! În orice alte cadre există așa ceva ca eveniment. Dar Qt a decis să se arate și a venit cu semnale/sloturi. Deși, în esență, acesta este practic același lucru.

Faceți clic dreapta pe butonul „A + B =”, se deschide un meniu pop-up:

Faceți clic pe „Mergeți la slot”

Selectați semnalul clicked() și în editorul de cod care se deschide, scrieți un mic cod în Qt:

Void MainWindow::on_btnOK_clicked() ( int a = ui->edtA->text().toInt(); // Luați textul edtA și convertiți-l într-un număr a int b = ui->edtB->text() .toInt (); // Luați textul edtB și convertiți-l într-un număr b int c = a + b // Adăugați numerele QString s = QString::number(c); ->edtC->setText (s) // Imprimă rezultatul în edtC )

Funcția de gestionare a semnalului clicked() se numește slotul on_btnOK_clicked().

Un exemplu simplu de asamblare manuală

Pentru o mai bună înțelegere a Qt, ar fi corect să construiți manual cel puțin un exemplu simplu în consolă. Tehnica de asamblare este aceeași pentru proiecte de orice dimensiune și este important să înțelegeți cum se face.

Să ne uităm la un exemplu simplu de aplicație GUI care deschide o fereastră goală pe ecran. Selectați un director de pe disc unde vom crea fișierul principal.cpp cu următorul conținut. Numerotarea liniilor a fost adăugată la exemplu pentru a simplifica comentariile suplimentare asupra exemplului. Trebuie înțeles că numerotarea liniilor nu trebuie inclusă în fișierul de lucru, deoarece nu face parte din sintaxa expresiei C++ validă.

01. #include 02. #include 03. 04. int main(int argc, char *argv) 05. ( 06. QApplication app(argc, argv); 07. QWidget wgt; 08. wgt.setWindowTitle(tr(„Bună ziua”)); 09. 10 . wgt.show(); 11. return app.exec();

Liniile 01 și 02 includ fișiere antet QAplicațieŞi QWidget, care, printre altele, conține declarații de clasă QAplicațieŞi QWidget. În exemplul nostru, creăm instanțe ale acestor clase.

Este demn de remarcat tradiția convenabilă de a numi fișierele antet utilizate în Qt. De la versiunea 4 a Qt, dacă aveți nevoie de o clasă numită Qxxx, atunci cel mai probabil definiția sa se află în fișierul antet Qxxx.

În acest exemplu, clasa QAplicație Suntem interesați în calitate de organizator al ciclului de colectare a mesajelor care vor ajunge în fereastra aplicației noastre GUI. Vezi linia 11 (pornirea buclei de mesaje din fereastră). Clasă QAplicație implementat în asemănarea unui Singleton. Pentru cei care nu sunt familiarizați cu acest termen din teoria modelelor de design (sau, modele, din engleză. modele) hai să dăm o mică explicație. Esența unui singleton este că implementarea unei clase împiedică posibilitatea de a crea mai mult de o instanță a unei clase date. Această caracteristică este importantă pentru noi deoarece implică posibilitatea de a defini o variabilă globală în interiorul bibliotecii Qt cu un pointer către o singură instanță a unei clase date. Vezi descrierea simbolului qAppîn ajutorul Qt.

Biblioteca Qt folosește terminologia widget. widget- dispozitiv, dispozitiv) ca element al interfeței GUI. Setul minim comun de proprietăți ale unor astfel de elemente este reprezentat de clasă QWidget.

Liniile 06 și 07 creează obiectele de clasă aplicație și widget. În ambele cazuri, obiectele sunt create static. Durata de viață a unor astfel de obiecte este limitată de blocul de instrucțiuni (...) în care au fost create. De îndată ce execuția codului programului ajunge la paranteza de închidere, ambele aceste obiecte sunt automat distruse. Astfel, în acest exemplu nu ne vom gândi la cum să distrugem obiectele Qt și vom analiza această problemă mai târziu.

La crearea unui obiect QAplicație se folosește un constructor parametrizat căruia îi sunt transmise argumentele de lansare a aplicației (copiate din argumente funcții principale()). În interiorul obiectului clasă, aceste argumente sunt analizate. Clasa de aplicație acceptă o serie de parametri de lansare, care pot fi găsiți în ajutorul pentru constructorul corespunzător QAplicație. Parametrii de lansare care nu sunt incluși în această listă ar trebui analizați independent. La sfârșitul acestei lecții, după ce am plasat mai multe controale în fereastra principală a aplicației, vă vom sugera să experimentați cu parametrul de lansare -style, pentru care, în orice build Qt, sunt posibile următoarele valori: motiv, ferestre, platină.

Linia 08 modifică unul dintre atributele obiectului widget. Folosind metoda setWindowTitle(), setăm propriul text pentru titlul viitoarei ferestre. Rețineți că imaginea șir este înfășurată într-un apel la funcția de traducere tr(). Aceasta este o cerință pentru internaționalizarea aplicației. Pentru toate astfel de wrapper-uri, instrumentele speciale Qt pot fi folosite pentru a crea fișiere speciale cu traduceri în diferite limbi, care pot fi folosite în aplicație pentru a efectua înlocuiri automate. De obicei, astfel de fișiere sunt incluse în compilarea aplicației ca resurse.

Linia 10 deschide fereastra aplicației. Înainte de Qt4, înainte de a deschide o fereastră, widget-ul a fost declarat în mod explicit ca widget principal al aplicației. Acest lucru a fost făcut folosind următorul cod.

App.setMainWidget(&wgt);

Începând de la Qt4, o astfel de comunicare se realizează automat prin accesarea indicatorului global qApp la o instanță a clasei de aplicație.

Linia 11 pornește bucla de procesare a mesajelor sistemului de operare direcționată către fereastra aplicației. Bucla se termină atunci când oricare dintre comenzile de închidere a aplicației este executată. Codul de ieșire al aplicației este returnat de exec() când metoda iese. Acest cod devine codul de returnare al funcției main() prin trecerea acestuia prin operator reveni.

Acum să încercăm să construim aplicația. Cel mai simplu mod de a face acest lucru este pe Linux. Pentru a face acest lucru, trebuie doar să deschideți consola și să rulați câteva comenzi, pe care le vom descrie acum. Pentru Windows, o astfel de muncă poate necesita setarea căilor către directorul în care se află utilitarul qmake din SDK-ul Qt. Acest utilitar implementează regulile sistemului de construire a proiectelor QMake.

Mai întâi trebuie să aflăm ce ne este disponibil din consolă. Dacă ne aflăm în consola bash (*nix), atunci acest lucru este destul de simplu. Tastați comanda qfakeși apăsați Tab de două ori. Ar trebui să vedem o listă cu toate comenzile care încep cu combinația qfake. De exemplu, în cazul meu, văd două comenzi: qfakeŞi qmake-qt4. Aceasta înseamnă că am două versiuni ale bibliotecii instalate din depozit. Echipă qfake corespunde versiunii Qt5 (implicit pentru ultima versiune), și echipa qmake-qt4 corespunde în mod corespunzător Qt4. Acum, în funcție de ce comandă folosesc, voi construi fie folosind versiunea Qt5, fie folosind versiunea Qt4.

Dacă totul în sistemul nostru este configurat normal și versiunile Qt SDK au succes, atunci proiectul ar trebui să fie construit folosind următoarele trei comenzi.

$ qmake -proiect $ qmake $ make

Prima comandă va crea un fișier proiect. Fișierul de proiect are sufixul .pro. În contextul Windows, ar fi corect să spunem „extensie pro„. Concepte sufixul numelui fișieruluiŞi extensia de fișierînseamnă lucruri complet diferite, deși par asemănătoare. Asigurați-vă că îl utilizați corect.

A doua comandă ar trebui să creeze un fișier script de compilare - Makefile. A treia comandă ar trebui să ruleze un script de compilare care ar trebui să producă un fișier de aplicație executabil.

Dacă acest lucru nu se întâmplă, atunci vom încerca să găsim problema.

Deschideți fișierul proiect. Încercați să găsiți următoarea linie acolo.

QT += gui

Dacă o astfel de linie nu există, atunci ar trebui să o adăugați, altfel, în funcție de versiunea SDK, proiectul poate fi perceput ca un proiect de consolă și căile către fișierele antet clasei GUI nu vor fi incluse în el. Acest lucru va duce la erori de compilare care declară că fișierele de antet incluse nu au fost găsite.

Rețineți că, dacă aveți de-a face cu Qt SDK versiunea 5, atunci această definiție trebuie să includă și grupul widget-uri după cum se arată mai jos.

QT += widget-uri gui

Un exemplu de creare a unui șablon de aplicație GUI din QtCreator

Deschideți QtCreator. Pentru a crea un nou proiect, lansați expertul de creare a proiectului din meniul „Fișier->Fișier nou sau Proiect...”. În fereastra care se deschide pe prima pagină a expertului, vi se solicită să selectați un șablon pentru viitorul proiect. Pentru grupul de proiecte „Aplicație”, selectați opțiunea „Aplicație Qt GUI” și faceți clic pe butonul „Alege” pentru a merge la următoarea pagină a expertului.

Pe a doua pagină a expertului de creare a proiectului, vi se cere să selectați un nume de proiect și directorul de locație al acestuia. Folosind numele de proiect specificat, va fi creat un subdirector în care vor fi localizate fișierele de proiect. Subdirectorul va fi creat în directorul de plasare specificat. Astfel, numele proiectului trebuie să fie determinat de regulile pe care trebuie să le respecte numele directorului. Cu toate acestea, pentru a evita problemele, nu folosiți litere și spații rusești. Utilizați litere, numere și litere de subliniere și liniuțe în limba engleză (semnul minus). Să ne numească proiectul aplicația 1. Să-l scriem pe rând nume, iar în linia de selectare a directorului de proiect vom indica directorul în care vom continua să creăm proiecte pe Qt. Căile cu litere și spații rusești ar trebui evitate. Mai devreme sau mai târziu, pot cauza probleme. Dacă trebuie să vă amintiți această cale pentru data viitoare, bifați caseta de selectare „Utilizați ca locație implicită a proiectului”. Faceți clic pe butonul „Următorul” pentru a merge la următoarea pagină a expertului.

Pe a treia pagină a expertului de creare a proiectelor, vi se cere să selectați un SDK Qt din lista celor găsite și înregistrate în QtCreator. Selectați opțiunea Qt4. Pentru versiunea SDK selectată, trebuie să definiți profiluri de construire a proiectului. Opțiunile „Release” și „Debug” sunt oferite. Versiunea „Release” nu conține simboluri de depanare în fișierul executabil și este recomandată pentru a fi transferată în uz real. În toate celelalte cazuri, este mai convenabil să utilizați versiunea „Debug”. În exemplul actual, alegerea ansamblului nu contează. Puteți lăsa ambele profiluri activate. În dreapta numelui profilului există un câmp de introducere în care este scris calea de-a lungul căreia se va efectua asamblarea corespunzătoare. Adesea, aceste căi sunt editate pe baza diferitelor tradiții. În exemplul nostru, putem lăsa aceste căi neschimbate. Faceți clic pe butonul „Următorul” pentru a merge la următoarea pagină a expertului.

Pe a patra pagină a expertului de creare a proiectului, vi se cere să selectați numele clasei pentru forma principală a proiectului, clasa de bază de la care ar trebui să fie moștenită forma principală a proiectului și numele fișierelor în care interfața iar implementarea clasei create a formularului principal va fi localizată. În plus, pagina ar trebui să indice dacă va fi utilizat designul formei vizuale. Deoarece nu vom folosi designul vizual, ar trebui să ne asigurăm că caseta de selectare „Generează formular” este debifată.

Cel mai interesant lucru de pe pagina a patra a Expertului New Project este alegerea unei clase de bază pentru a crea o clasă de formular. Sunt oferite trei variante.

  1. QMainWindow- în majoritatea cazurilor, alegerea cea mai potrivită. Prin moștenirea din această clasă, obținem instrumente gata făcute pentru plasarea unui meniu, bară de stare și câmp central, care pot fi implementate atât în ​​stilul SDI (Single Document Interface), cât și în stilul MDI (Multi Document Interface).
  2. QWidget— această clasă este cel mai simplu widget. În terminologia Qt, acesta este cel mai simplu element cu care este asociată un fel de zonă grafică de pe ecran. Ca clasă de bază pentru fereastra principală, este folosită, de regulă, la crearea unor aplicații simple cu un singur formular și este excelentă pentru scopurile inițiale de „student” datorită faptului că nu conține nimic „de prisos”.
  3. QDialog— clasă de bază pentru crearea casetelor de dialog modale.

Pentru exemplul nostru, vom alege ca clasă de bază, cea mai simplă opțiune este QWidget. Numele pentru clasa principală a formularului și pentru fișierele în care vor fi localizate interfața și implementarea acesteia pot fi lăsate ca valori implicite. Faceți clic pe butonul „Următorul” pentru a merge la următoarea pagină a expertului.

A cincea pagină a expertului de creare a proiectelor vă permite să determinați relația dintre proiectul care este creat și proiectele deja deschise și să specificați alegerea oricărui sistem de control al versiunii de proiect disponibil. În exemplul actual, nu ne interesează aceste funcții, așa că ar trebui să finalizăm expertul făcând clic pe butonul „Finish”.

ÎN versiuni diferite QtCreator Pașii pentru a crea un proiect pot fi ușor diferiți, dar, în esență, iată principalele puncte pe care ar trebui să le înțelegeți atunci când creați un proiect GUI. Descrierea actuală a fost făcută pe baza QtCreator versiunea 2.7.0.

După finalizarea Expertului Proiect nou în QtCreator, se va deschide o vizualizare cu proiectul creat. Rulați proiectul pentru a verifica setările mediului de dezvoltare. Dacă totul este în ordine, atunci șablonul de proiect creat ar trebui să fie compilat și rulat. Pentru a lansa un proiect în mod normal, puteți face clic pe butonul cu un triunghi verde situat pe bara de instrumente. Butonul cu imaginea unui triunghi verde cu un gândac îndeplinește funcțiile de lansare a modului de depanare. În acest caz, dacă punctele de întrerupere sunt setate în proiect, atunci când sunt atinse, execuția rulează aplicația se va opri și linia în care a fost făcută oprirea va fi evidențiată în editorul de depanare.

QtCreator este vizibil diferit în design față de alte medii de dezvoltare. Cu toate acestea, designul său este surprinzător de ergonomic. Printre altele, în era ecranelor late, bara de instrumente situată în stânga în fereastra principală a mediului de dezvoltare arată foarte avantajoasă. În partea de jos a barei de instrumente, așa cum am menționat deja, există butoane pentru lansarea aplicației, iar în partea de sus sunt butoane pentru selectarea filelor pentru fereastra principală a mediului de dezvoltare. Să ne uităm la lista de file principale.

  • „Bine ați venit” — Selectarea proiectului
  • „Editare” — Editarea programului.
  • „Depanare” - Depanare proiect. Include ferestrele necesare pentru monitorizarea obiectelor.
  • „Proiecte” — Setări pentru proiectele încărcate în mediul de dezvoltare. Ar trebui să studiați cu atenție opțiunile prezentate în această filă.
  • „Analiza” - Analiza proiectelor folosind instrumente speciale pentru detectarea blocajelor de performanță și a problemelor de scurgere de memorie.
  • „Ajutor” Fereastra de ajutor integrată. Poate preferați să lucrați cu aplicația de ajutor autonomă, QtAssistant.

Un exemplu simplu de plasare a widget-urilor într-o fereastră de aplicație

Să luăm proiectul pe care l-am creat în secțiunea anterioară folosind expertul pentru mediu de dezvoltare QtCreator. Proiectul creat include următoarele fișiere.

  • app1.pro- dosar proiect
  • principal.cpp widget.h și widget.cpp./li>
  • widget.h— interfața clasei principale de ferestre a aplicației.
  • widget.cpp— implementarea clasei principale a ferestrei aplicației.

Numele fișierelor din proiectul dvs. pot fi ușor diferite. Acest lucru ar putea fi fie pentru că le-ați specificat în mod explicit în altă parte la crearea proiectului, fie pentru că valorile implicite pentru versiunea dvs. de QtCreator sunt diferite.

Să deschidem fișierul de implementare al ferestrei principale a aplicației - widget.cpp. Să o schimbăm în starea prezentată în exemplul următor. Vă rugăm să rețineți că numerele de rând sunt furnizate doar pentru confortul comentariilor.

01. #include 02. #include 03. #include 04. 05. #include "widget.h" 06. 07. Widget::Widget(QWidget *parent) 08. : QWidget(parent) 09. ( 10. setWindowTitle(tr("Bună lume!!!")); 11. setMinimumSize(200, 80); 12. 13. QLabel * plb = new QLabel(tr("Test"), this QLineEdit * ple = new QLineEdit(this 17. ple->setGeometry(110, 20); , 80, 24); 18. 19. QPushButton * ppb = new QPushButton(tr("Ok"), this ->setGeometry(20, 50, 80, 24. ) 22. 23. Widget: ~Widget() 24. ( 25. 26. )

Liniile 01-03 includ fișiere cu interfețe pentru următoarele clase de widget-uri.

  1. QLabel— clasa de etichetă. Adesea folosit pentru a plasa informații de text statice. Înțelege unele etichete HTML pentru formatare. Poate fi folosit pentru a plasa static o imagine. De exemplu, dintr-un fișier cu o imagine. Moștenit de la QFrame, astfel încât poate fi ajustat la diferite forme de margine (borduri).
  2. QLineEdit— o clasă pentru crearea câmpurilor de introducere a informațiilor text pe o linie.
  3. QPushButton— clasa de butoane. Cel mai adesea folosit pentru procesarea semnalului a făcut clic ()- faceți clic pe butonul.

Corpul constructorului clasei Widget conține două linii pentru setarea atributelor ferestrei (liniile 10-11) și 8 linii pentru crearea și plasarea altor trei widget-uri pe câmpul ferestrei (liniile 13-20).

Setarea atributelor ferestrei constă în comanda pentru a seta numele ferestrei aplicației și dimensiunea minimă a ferestrei aplicației. Pentru a seta dimensiunea minimă, se folosește o metodă care ia lățimea și înălțimea ferestrei în pixeli.

Linia 13 conține crearea unei instanțe a clasei QLabel. Un obiect este creat dinamic folosind operatorul nou. Pentru a crea un obiect, se folosește un constructor, al cărui prim parametru specifică șirul pe care trebuie să-l reprezinte obiectul creat. Al doilea parametru al acestui constructor ar trebui să specifice adresa obiectului care va deveni proprietarul obiectului etichetă creat. Adresa proprietarului este setată la acest. Conform regulilor limbajului C++, acest este un pointer către obiectul în care este utilizat. Adică, în acest context, este un pointer către instanța creată a clasei Widget. Astfel, linia 13 creează un obiect de clasă etichetă care ar trebui să reprezinte textul specificat și al cărui proprietar este atribuit obiectului curent.

Acum este timpul să vorbim despre lanțurile de proprietate, care sunt implementate în sistemul de clasă Qt pentru a rezolva problema distrugerii obiectelor pentru a preveni scurgerile accidentale de memorie. Trebuie amintit că obiectele create dinamic, de ex. folosind operatorul nou, sunt situate într-o zonă specială de memorie numită mormanși care trăiesc în grămada până când sunt distruse în mod explicit de către operator şterge. Dacă programatorul nu monitorizează distrugerea obiectelor care au devenit inutile și nu cheamă operatorul pentru a le distruge şterge, atunci aceasta devine cauza unei scurgeri de memorie în aplicație, ceea ce reprezintă o problemă serioasă pentru o serie de limbaje de programare, care includ limbajul C++.

Există mai multe scheme binecunoscute pentru urmărirea automată a distrugerii obiectelor create dinamic. Unul dintre ele este folosirea indicatoarelor inteligente, care ar trebui discutate mai târziu. O altă modalitate este de a crea lanțuri de proprietate, despre care vom discuta acum. A treia modalitate este de a crea un subsistem de colectare a gunoiului, care ar trebui să urmărească obiectele inutile și să le distrugă. Această din urmă metodă, tradițională pentru nucleele multor limbi moderne, practic nu este folosită în C++. Primele două metode sunt mult mai populare în tradițiile limbajului C++.

Deci, lanțurile de proprietate implementează următoarea idee simplă. Este creat un obiect a cărui distrugere ne angajăm să o monitorizăm. Cel mai simplu mod este de a crea un astfel de obiect prin definiție statică și apoi va fi distrus automat când execuția programului ajunge la sfârșitul blocului de instrucțiuni (...) în care a fost definit. În continuare, când creație dinamică Pentru alte obiecte, le vom atribui obiecte proprietar. Responsabilitățile proprietarilor vor include distrugerea obiectelor aflate în proprietate în corpul propriului distrugător. Amintiți-vă că un destructor este o metodă specială care este apelată atunci când un obiect este distrus. Astfel, este posibil să se construiască un astfel de lanț de proprietate în raport cu primul obiect, ale cărui elemente vor fi automat distruse atunci când primul obiect este distrus. Atunci când organizați o astfel de schemă, trebuie doar să luați în considerare corect durata de viață a unui obiect care este desemnat drept proprietar al altui obiect, astfel încât obiectele să nu fie distruse prematur.

Aceasta este exact schema de proprietate implementată în sistemul de clasă Qt. Când creați mai multe clase din această bibliotecă, puteți utiliza un constructor al cărui singur sau ultim parametru ia un pointer către un obiect care este atribuit ca proprietar obiectului creat. Acest parametru este descris ca un parametru cu o valoare implicită, care este definită ca zero. Astfel, dacă adresa proprietarului nu este specificată, atunci parametrul este setat la zero și schema de proprietate pentru un astfel de obiect este dezactivată. În acest caz, ar trebui să vă amintiți să distrugeți în mod explicit un astfel de obiect.

La implementarea diagramelor de circuit de proprietate, unele biblioteci folosesc un parametru numit proprietar, care se traduce din engleză ca proprietar. Cu toate acestea, în biblioteca Qt acest parametru este apelat mamă, care se traduce din engleză ca mamă. Drept urmare, unii începători au o neînțelegere din cauza faptului că conceptul de „părinte” se referă în mod tradițional la lanțurile de moștenire în POO, dar lanțurile de moștenire și lanțurile de proprietate nu au nimic în comun. Fiți atenți și nu fiți victima unor concepții greșite în această chestiune.

Să revenim încă o dată la rândul 13. Acolo am creat un obiect al cărui proprietar este atribuit obiectului curent al ferestrei principale a aplicației. Variabilă plb, care stochează adresa obiectului creat, va fi distrus automat când se ajunge la sfârșitul codului constructorului. Cu toate acestea, obiectul care a fost alocat în memorie va continua să trăiască și va trăi până când obiectul ferestrei principale a aplicației este distrus. Distrugerea obiectului fereastră principală va distruge automat toate obiectele pe care le deține obiectul fereastră.

Pe linia 14, accesăm o metodă de setare a atributelor de geometrie care determină plasarea obiectului personalizat în raport cu proprietarul acestuia. Prima și a doua valoare indică coordonatele orizontale și verticale pentru colțul din stânga sus al obiectului. A treia și a patra valoare indică lățimea și înălțimea obiectului personalizat.

Dacă v-ați asigurat că exemplul creat în secțiunea anterioară a fost asamblat și rulat fără erori, atunci acest exemplu, care este o extensie a celui precedent, ar trebui să ruleze și el.

Folosind această aplicație Puteți experimenta cu opțiunile de lansare a aplicației. Cel mai simplu mod de a face acest lucru este în consolă. Accesați directorul de compilare a aplicației și urmați următoarele opțiuni pentru a rula aplicația cu parametri. În fiecare dintre lansări ar trebui să existe o diferență notabilă în stilul de desenare a widget-urilor.

$ ./app1 -style=motiv $ ./app1 -style=windows $ ./app1 -style=platină

În acest exemplu, fișierul executabil al aplicației este specificat de nume aplicația 1. Este posibil ca în cazul dvs. fișierul executabil să aibă un nume diferit. În sala de operație sistem Windows, fișierele executabile au extensia exe. În plus, în sistemul de operare Windows, puteți lansa un fișier executabil din directorul curent fără a specifica o cale relativă, de exemplu. fara indicatie ( ./ ) - caracterul punct este un sinonim pentru directorul curent, iar caracterul bară oblică este un caracter separator în calea fișierului. De asemenea, rețineți că simbolul dolarului este simbolul prompt standard în consola *nix pentru un utilizator obișnuit și nu trebuie să fie introdus ca parte a comenzii. În consola Windows, caracterul prompt este de obicei caracterul parantezei unghiulare ( > ).

Opțiunile de lansare pot fi specificate și la lansarea unei aplicații din mediul de dezvoltare QtCreator. Pentru a face acest lucru, faceți clic pe pictograma din bara de instrumente din stânga Proiecte. Se va deschide fila de setări corespunzătoare. În partea de sus a ferestrei puteți vedea un sistem ierarhic de file. Filele de nivel superior definesc proiectul, deoarece mai multe proiecte pot fi deschise în mediul de dezvoltare. Următorul nivel de file trebuie, printre altele, să conțină o filă Build&Run, de care avem nevoie. În această filă, selectați apoi versiunea Qt SDK, în cazul în care construiți un proiect pentru mai multe versiuni simultan. Dacă asamblați un proiect pentru o singură versiune, atunci selecția va consta dintr-un element. În interiorul widget-ului de selecție a versiunii Qt SDK, există două butoane stilizate cu margini rotunjite - butoane ConstruiŞi Fugi. Faceți clic pe butonul Fugi pentru a selecta grupul corespunzător de setări. Acolo, în grupul de parametri cu același nume Fugi veți găsi un câmp de intrare cu o singură linie vizavi de etichetă Argumente. Acesta este scopul final al alegerii noastre. Să scriem acolo următorul rând.

Stil=motiv

Lansați aplicația. Apoi încercați alte valori: ferestreŞi platină. Amintiți-vă că obiectul de clasă QAplicație acceptă o listă de câteva zeci de parametri de lansare, despre care pot fi citite în ajutorul pentru constructorii de clasă corespunzători.

Pe măsură ce lucrați cu exemplul, familiarizați-vă cu informații de fundal conform claselor folosite în exemplu. Încercați să adăugați în formular alte instanțe ale etichetei, câmpului de introducere pe o singură linie și claselor de butoane. În plus, încercați să citiți ajutorul și să adăugați obiecte din următoarele clase în formular.

  1. QComboBox— clasa listă derulantă.
  2. QCheckBox— clasa de steag (verificator).
  3. QTextEdit— clasa unui câmp de intrare cu mai multe linii. Folosit atât pentru editare, cât și pentru prezentarea textului. Conține capabilități foarte bogate pentru prezentarea documentelor prin separarea funcțiilor compozitorilor speciali de documente în clase separate.

În încheierea lecției, trebuie menționat că metoda utilizată de plasare „rigidă” a obiectelor pe câmpul formular, prin indicarea explicită a geometriei de plasare, nu este tradițională și recomandată în biblioteca de clase Qt. Următoarele lecții vor analiza managerii de aspect, care sunt mijloace moderne și convenabile de a plasa widget-uri într-un formular.

Programare cu Qt

Partea 1. Introducere. Instrumente pentru dezvoltatori și model de obiect

Seria de conținut:

1. Introducere

Există versiuni de Qt pentru sisteme de operare asemănătoare Unix cu X Window System (de exemplu, X.Org (EN), Mac OS X și Windows OS). Qt Software își portează produsul și pe platforme mobile: Embedded Linux (EN), S60 (EN) și Windows CE. Qt oferă cele mai multe oportunități excelente pentru dezvoltarea multiplatformă diferite programe, nu neapărat cu o interfață grafică. În special, popularul mediu desktop KDE se bazează pe acesta.

Setul de instrumente este împărțit în module, fiecare dintre acestea fiind situat într-o bibliotecă separată. Clasele de bază sunt în QtCore, componentele GUI sunt în QtGui, clasele de rețea sunt în QtNetwork etc. Astfel, este posibil să compilați programe chiar și pentru platforme unde nu există X11 sau alt subsistem grafic compatibil.

2. Instalarea Qt

Va trebui să instalăm mediul de dezvoltare Qt. Software distribuit în conformitate cu termenii licenței gratuite GPL 3.0 sau LGPL 2.1. Poate fi obținut de la http://www.qtsoftware.com/downloads (EN).

2.1. Biblioteci și instrumente de bază

Depozitele distribuțiilor populare GNU/Linux au deja pachete gata făcute cu mediul de dezvoltare Qt (de exemplu, Debian, Fedora, Gentoo, Mandriva, Ubuntu). Cu toate acestea, utilizatorul poate construi și instala setul de instrumente din codul sursă.

Pentru sistemele care utilizează X11, trebuie să descărcați fișierul qt-x11-opensource-src-4.x.y.tar.gz, unde 4.x.y este cea mai recentă versiune stabilă disponibilă. Vom instala versiunea 4.5.0.

În directorul cu fișierul qt-x11-opensource-src-4.5.0.tar.gz, rulați următoarele comenzi:

tar xvfz qt-x11-opensource-src-4.5.0.tar.gz cd qt-x11-opensource-src-4.5.0

Înainte de a construi Qt, rulați scriptul de configurare. Setul complet de opțiuni este afișat folosind comanda ./configure -help , dar de obicei pot fi utilizate setările standard.

Parametrul -prefix specifică directorul de instalare (implicit este /usr/local/Trolltech/Qt-4.5.0). Există, de asemenea, chei pentru instalarea diferitelor componente (fișiere executabile, biblioteci, documentație etc.) în diferite directoare.

Când este lansat, scriptul solicită utilizatorului să fie de acord cu termenii licenței GPL / LGPL. După execuție

./configure

puteți începe construirea și instalarea folosind comenzile:

make & make install

Rețineți că compilarea durează mult timp, iar instalarea Qt poate necesita privilegii de root (fișierele sunt scrise în /usr/local/).

Dacă mai târziu trebuie să reconfigurați și să reconstruiți Qt în același director, eliminați toate urmele configurației anterioare folosind make confclean înainte de a rula din nou ./configure.

Calea către executabilele Qt trebuie adăugată la variabila de mediu PATH. În shell-urile bash, ksh, zsh și sh, acest lucru se poate face prin adăugarea următoarelor linii la fișierul ~/.profile:

PATH=/usr/local/Trolltech/Qt-4.5.0/bin:$PATH export PATH

În csh și tcsh trebuie să adăugați următoarea linie la ~/.login:

setenv PATH /usr/local/Trolltech/Qt-4.5.0/bin:$PATH

Dacă utilizați un alt shell, vă rugăm să consultați secțiunile corespunzătoare ale documentației.

În plus, trebuie să adăugați linia /usr/local/Trolltech/Qt-4.5.0/lib la variabila LD_LIBRARY_PATH dacă compilatorul nu acceptă RPATH. Folosim GNU/Linux și GCC (EN), așa că omitem acest pas.

Apoi utilizați utilitarul qtdemo pentru a rula aplicații demo pentru a testa funcționalitatea instrumentelor instalate.

2.2. SDK

Mediul de dezvoltare multiplatformă Qt Creator a apărut recent. SDK-ul complet, inclusiv IDE-ul (pe lângă bibliotecile și instrumentele de bază pentru dezvoltatori), poate fi găsit pe site-ul web Qt Software. Descărcați binarul qt-sdk-linux-x86-opensource-xxx.bin și rulați vrăjitorul de instalare:

chmod +x ./qt-sdk-linux-x86-opensource-2009.01.bin ./qt-sdk-linux-x86-opensource-2009.01.bin

Dacă nu intenționați să instalați SDK-ul în directorul dvs. de acasă, atunci rulați programul de instalare cu drepturi de superutilizator.


3. Instrumente pentru dezvoltatori

Qt include instrumente de dezvoltare cu o interfață grafică sau de consolă. Printre acestea:

  • assistant este un instrument grafic pentru vizualizarea documentației hipertext pentru instrumentele și bibliotecile Qt.
  • designer – un instrument grafic pentru crearea și asamblarea interfețe cu utilizatorul pe baza componentelor Qt.
  • qmake este un generator de Makefile multiplatformă.
  • moc este un compilator metaobject (manager de extensie Qt pentru C++).
  • uic este un compilator de interfețe utilizator din fișiere .ui create în Qt Designer.
  • rcc – compilator de resurse din fișiere .qrc.
  • qtconfig este un instrument grafic pentru setarea preferințelor utilizatorului pentru aplicațiile Qt.
  • qtdemo – lansează exemple și programe demonstrative.
  • qt3to4 este un instrument pentru migrarea proiectelor de la Qt 3 la Qt 4.
  • lingvist este un instrument de localizare a aplicațiilor.
  • pixeltool – lupă de ecran.


3.1. qfake

Utilitarul qmake este folosit pentru a genera automat Makefile-uri pe diferite platforme.

În general, qmake este orientat către Qt. Dacă sunteți interesat de sisteme de compilare multiplatformă pentru scopuri mai largi, puteți apela la CMake, care acceptă și Qt.

Începătorii ar trebui să rămână cu qmake.

Puteți găsi documentația completă pentru acest utilitar în Qt Assistant. Qt vine și cu pagini de manual, inclusiv qmake(1) (introduceți linie de comandă om qmake). Aici vă vom oferi instrucțiuni de bază pentru a vă ajuta să construiți codul articolului, precum și propriile proiecte simple.

De exemplu, să creăm un director myproject și să adăugăm acolo fișierele hello.h, hello.cpp și main.cpp. În hello.h descriem prototipul funcției hello():

Lista 1.1. Declarații de funcții ale programului „Hello, World!”
// hello.h void hello();

Implementarea hello() va fi plasată în hello.cpp:

Lista 1.2. Implementarea funcțiilor programului „Hello, World!”
// salut.cpp #include #include „hello.h” void hello() ( qDebug()<< "Hello, World!"; }

Aici qDebug() este folosit pentru a scoate informațiile de depanare.

Poate fi eliminat prin declararea simbolului QT_NO_DEBUG_OUTPUT în timpul compilării. Există, de asemenea, o funcție qWarning(), care emite avertismente și qFatal(), care închide aplicația după imprimarea unui mesaj de eroare critic în STDERR (qCritical() face același lucru, dar fără a se termina). În fișierul antet<< . При этом между аргументами (как в случае qDebug() << a << b << c;) автоматически расставляются пробелы, поддерживается вывод многих типов C++ и Qt, а в конце автоматически добавляется перевод строки.

conține declarații care adaugă o sintaxă de operator mai convenabilă pentru qDebug(), qWarning() și qCritical()

Codul principal al aplicației (aici urmează convenția de a pune main() în fișierul main.cpp):
Lista 1.3. Funcția principală () a programului „Hello, World!”

// main.cpp #include "hello.h" int main() ( salut(); return 0; )

Pentru a crea un fișier proiect, rulați

################################## # Generat automat de qmake ######### # ########################## șablon = aplicație ȚINTĂ = DEPENDPATH += . INCLUDEPATH += . # Introduceți HEADERS += hello.h SOURCES += hello.cpp main.cpp

Operatorul = este folosit pentru a atribui valori variabilelor, += adaugă o nouă opțiune unei variabile, -= elimină opțiunea specificată.

Șablon = aplicație înseamnă că construim o aplicație; pentru bibliotecă folosiți ȘABLON = lib .

TARGET – numele fișierului țintă (specificați TARGET = foobar pentru a obține fișierul executabil foobar).

DEPENDPATH – directoare de căutat la rezolvarea dependențelor.

INCLUDEPATH – directoare cu fișiere antet.

După lansare

va fi creat un Makefile obișnuit pe baza myproject.pro în GNU/Linux:

####### Compilați hello.o: hello.cpp hello.h $(CXX) -c $(CXXFLAGS) $ (INCPATH) -o hello.o hello.cpp main.o: main.cpp hello.h $(CXX) -c $(CXXFLAGS) $ (INCPATH) -o main.o main.cpp ####### Instalare instalare: FORCE dezinstalare: FORCE FORCE:

Opțiunile qmake afectează conținutul Makefile. De exemplu, qmake -Wall va adăuga -Wall la steagurile compilatorului – emite toate avertismentele.

Folosind comanda make, vom obține fișierul executabil myproject, care afișează pe ecran șirul „Hello, World!”.

Această schemă poate părea excesiv de complicată, dar în proiectele reale qmake face cea mai mare parte a lucrării de construcție (de exemplu, rulează compilatorul metaobject).

3.2. Qt Creator

Instrumentele descrise mai sus sunt suficiente pentru dezvoltarea aplicațiilor. Puteți folosi editorul de text preferat, cum ar fi GNU Emacs sau Vim. IDE-urile tradiționale precum KDevelop funcționează și cu Qt.

Cu toate acestea, nu cu mult timp în urmă, Qt Software a lansat IDE-ul său multiplatform Qt Creator. Are toate instrumentele de dezvoltator încorporate, un editor cu evidențiere și completare a codului, un depanator (interfață grafică pentru gdb) și suport pentru Perforce, SVN și Git.

Când lucrați în Qt Creator, sunt utilizate mai multe moduri, care corespund filelor din panoul din stânga. Pentru a comuta rapid între moduri, puteți utiliza combinațiile de taste Ctrl+1, Ctrl+2 etc. În principal modul de editare corespunde Ctrl+2 .


Pentru a naviga în editor, utilizați combinația de taste Ctrl+K. După ce faceți clic pe el, trebuie să specificați unul dintre prefixe:

Tabelul 1. Prefixe pentru navigare în Qt Creator

După prefix, apăsați bara de spațiu și introduceți informațiile corespunzătoare.

De exemplu, pentru a merge la linia 93 a fișierului curent trebuie să tastați „l 93” (același lucru se poate face folosind Ctrl+L), pentru a merge la documentația despre subiectul qobject_cast – „qobject_cast”, etc.

În partea de jos a ferestrei, este afișat un câmp cu completare automată.

Figura 5. Câmp de navigare în Qt Creator

Tabelul 2. Comenzi rapide de la tastatură pentru Qt CreatorCtrl+[
Mergeți la începutul bloculuiCtrl+]
Mergeți la sfârșitul bloculuiCtrl+U
Selectați blocCtrl+Shift+U
Deselectați bloculCtrl+I
Aliniați blocul< Ctrl+
Restrânge bloculCtrl+>
Extinde bloculCtrl+/
Comentează bloculCtrl+Shift+
Mută-te în linieCtrl+Shift+↓
Mutați linia în josShift+Del

SDterge linia<Пробел>Editorul încorporat implementează adăugarea de cod „inteligentă”, numită prin combinația de taste Ctrl+

. Baza de simboluri este compilată pe baza fișierelor de antet ale proiectului de la INCLUDEPATH. Există o secțiune separată pentru citirea documentației în IDE. modul ajutor

. Pentru a obține ajutor contextual pentru o clasă sau metodă, pur și simplu mutați cursorul de text pe nume și apăsați F1.

Tasta F2 este, de asemenea, utilă, trecând la definiția din fișierele antet.

Pentru a comuta de la modul Ajutor sau Depanare la modul Editare de bază, apăsați Esc. În modul de editare, Esc mută focalizarea de la ferestre suplimentare (cum ar fi rezultatul compilației sau ajutorul sensibil la context) către editor. Dacă apăsați din nou Esc, ferestrele suplimentare se închid.

La fel ca qmake, Qt Creator folosește fișiere .pro, astfel încât proiectele vechi realizate manual pot fi importate cu ușurință în IDE. Este disponibil și un expert, cu ajutorul căruia puteți crea un șablon pentru un nou proiect.

Qt Creator este în prezent în curs de dezvoltare activă, dar dacă doriți un IDE Qt clasic care funcționează pe o varietate de platforme, aceasta este cea mai bună opțiune. 4. Stilul QtÎn Qt este folosit

CamelCasing

: Numele claselor arată ca MyClassName, iar numele metodelor arată ca myMethodName.

Mai mult, toate numele claselor Qt încep cu Q, de exemplu QObject, QList sau QFont. Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti:

#include

#include Prin urmare, în viitor nu vom stipula separat unde este declarată această sau acea clasă. (Metode de obținere și setare proprietăți getterŞi

T fooBar() const; void setFooBar(T val);

Când dezvoltați propriile aplicații în Qt, ar trebui să respectați acest stil.

5. Model obiect

Pentru a lucra eficient cu clasele în timpul execuției, Qt folosește un model obiect special care extinde modelul C++. În special, sunt adăugate următoarele caracteristici:

  • ierarhii de obiecte de tip arbore;
  • un analog al dynamic_cast pentru o bibliotecă care nu utilizează RTTI;
  • interacţiunea obiectelor prin semnaleŞi sloturi;
  • proprietățile obiectelor.

Multe obiecte sunt definite de valoarea mai multor proprietăți, stări interne și conexiuni cu alte obiecte. Sunt entități individuale, iar pentru ei operațiunea de copiere literală, precum și partajarea datelor în memorie, nu are sens. În Qt, aceste obiecte moștenesc proprietățile QObject.

În cazurile în care un obiect ar trebui tratat nu ca o entitate, ci ca o valoare (de exemplu, atunci când este stocat într-un container), se folosesc pointerii. Uneori, un pointer către un obiect care moștenește de la QObject este pur și simplu numit obiect.

Setul de instrumente este conceput astfel încât pentru QObject și toți descendenții săi, constructorul de copiere și operatorul de atribuire să nu fie disponibile - sunt declarate în secțiunea privată prin macro-ul Q_DISABLE_COPY():

clasa FooBar: QObject public (privat: Q_DISABLE_COPY(FooBar));

Aveți grijă și nu folosiți structura

Foo bar = Foo(baz);

Alte obiecte (cum ar fi containerele și șirurile de caractere) sunt complet definite de datele pe care le reprezintă, astfel încât clasele lor corespunzătoare au un operator de atribuire și un constructor de copiere. În plus, obiectele care reprezintă aceleași date le pot separa în memorie în mod transparent pentru programator.


5.1. Sistem metaobiect

Unele dintre extensii sunt implementate folosind metode standard C++, dar Qt folosește și extensii sintactice mai complexe, așa că folosește generarea automată a codului.

În acest scop, C++ implementează un mecanism șablon, dar nu oferă toate capabilitățile Qt necesare, este slab compatibil cu modelul de obiect dinamic și nu este pe deplin suportat de toate versiunile de compilatoare.

În situații dificile, Qt își folosește compilator metaobiect moc , care convertește codul cu extensii în cod C++ standard. Pentru a indica faptul că o clasă folosește capabilități metaobiect (și, prin urmare, ar trebui să fie gestionată de moc), macro-ul Q_OBJECT trebuie specificat în secțiunea privată.

Dacă întâmpinați erori ciudate de compilare care spun că clasa nu are un constructor definit sau nu are un tabel de funcții virtuale (vtbl), cel mai probabil ați uitat codul generat de moc. Acest lucru se întâmplă de obicei dacă macrocomanda Q_OBJECT nu este specificată.

Pentru a evita erorile, este mai bine să utilizați Q_OBJECT în toate clasele care moștenesc de la QObject (indirect sau direct).

Utilizarea unei abordări dinamice este asociată cu anumite pierderi de performanță față de una statică, dar aceste cheltuieli generale pot fi neglijate dacă se ține cont de beneficiile obținute.

Printre altele, codul meta-obiect adaugă o metodă

const virtual QMetaObject* QObject::metaObject() const;

care returnează un pointer către meta obiectul obiectului.

Semnalele, sloturile și proprietățile se bazează pe sistemul meta-obiect.

Când moșteniți de la QObject, fiți conștienți de restricțiile impuse de moc:

  1. Cu moștenire multiplă, descendentul lui QObject trebuie să fie prima și numai prima clasă moștenită: clasa MyClass: public QObject, public Foo, public Bar ( // ... );
  2. Moștenirea virtuală nu este acceptată cu QObject.

5.2. qobject_cast

Pentru a proiecta dinamic un QObject, utilizați funcția

T qobject_cast(QObject *obiect);

Funcționează ca operația standard dynamic_cast în C++, dar nu necesită suport RTTI.

Să avem o clasă MyClass1, care moștenește de la QObject și MyClass2, care moștenește de la MyClass1:

Mai mult, toate numele claselor Qt încep cu Q, de exemplu QObject, QList sau QFont. clasa MyClass1: public QObject ( Q_OBJECT public: MyClass1(); // ... ); clasa MyClass2: public MyClass1 ( Q_OBJECT public: MyClass2(); // ... );

Turnarea dinamică este ilustrată de următorul cod:

QObject *a = new MyClass2; MyClass1 *b = qobject_cast (o); MyClass2 *c = qobject_cast (b);

Aceste operațiuni vor funcționa corect în timpul execuției.

Ca și în cazul dynamic_cast , rezultatul castării poate fi verificat:

dacă (b = qobject_cast (a)) ( // ... )

Sistemul metaobiect vă permite, de asemenea, să verificați dacă un moștenește clasa MyClass1:

dacă (a->moștenește ("MyClass1")) ( b = static_cast (o); // ... )

Cu toate acestea, este preferată opțiunea anterioară cu qobject_cast.

5.3. Arbori de obiecte

Obiectele claselor care moștenesc de la QObject pot fi organizate într-o structură arborescentă. Când un obiect este șters, Qt îl șterge obiecte copil, care la rândul lor își șterg obiectele copil etc. Cu alte cuvinte, ștergerea unui obiect șterge întregul subarborel al cărui rădăcină este.

Să avem clasele ClassA și ClassB:

Lista 2.1. Declarație MyClass pentru un program care demonstrează cum sunt create și șterse obiectele
// clasa mea.h #include clasa MyClass: QObject public ( public: MyClass (char id, QObject *parent = 0); ~MyClass(); privat: char id_; );
Lista 2.2. Definirea metodelor MyClass pentru un program care demonstrează cum sunt create și șterse obiectele
// clasa mea.cpp #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: #include "myclass.h" MyClass::MyClass (char id, QObject *parent) : QObject(parent), id_(id) ( qDebug()<< "+" >> id_; ) MyClass::~MyClass() ( qDebug()<< "-" >> id_; )

Aici obiectul părinte este setat în constructorul QObject:

QObject::QObject(QObject *parent = 0);

Poate fi setat ulterior folosind metoda setParent() și preluat folosind parent() :

void QObject::setParent(QObject *parent);
QObject* QObject::parent() const;

Dacă creați unul dintre obiectele A și B din stivă, atunci va fi creat mai întâi A, apoi B. În conformitate cu standardul C++, ștergerea are loc în ordine inversă - mai întâi B, apoi A:

Lista 2.3. Crearea instanțelor MyClass pe stivă
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass b ("B"); return 0; )

Dacă creați B pe heap și îl atribuiți ca un copil al lui A, atunci B va fi șters automat împreună cu A:

Lista 2.4. Crearea unei instanțe A a clasei MyClass pe stivă și a unei instanțe B pe heap ca copil al lui A
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass *b = new MyClass ("B", &a); return 0; )

În mod similar, pentru copacii mai complexi:

Figura 8. Exemplu de arbore de obiecte cu mai multe niveluri
Lista 2.5. Arborele cu mai multe niveluri de obiecte înrădăcinate pe stivă
// main.cpp #include "myclass.h" int main() ( MyClass a ("A"); MyClass *b = new MyClass ("B", &a); MyClass *c = new MyClass ("C", &a); MyClass *d = new MyClass("D", c) MyClass *e = new MyClass("E", c);

După ștergerea lui A, întregul arbore va fi șters.

Astfel, programatorul trebuie să creeze obiecte pe heap și să stabilească ierarhiile corespunzătoare, iar Qt se ocupă de gestionarea memoriei.

Dacă un obiect și copiii săi sunt creați pe stivă, atunci această ordine de ștergere poate cauza erori.

int main() ( MyClass b ("B"); MyClass a ("A"); b.setParent(&a); // ... return 0; )

Aici, la părăsirea domeniului de aplicare, obiectul A va fi șters primul, deoarece a fost ultimul creat. În acest caz, Qt va șterge și obiectul său copil B. Dar apoi se va încerca să ștergeți B, ceea ce va duce la o eroare.

În caz contrar, nu va fi nicio problemă, deoarece atunci când destructorul QObject este apelat, obiectul se elimină singur din lista de obiecte copil a obiectului părinte:

int main() ( MyClass a ("A"); MyClass b ("B", &a); // ... return 0; )

În general, obiectele copil ar trebui să fie alocate pe heap.

Fiecare QObject are o proprietate objectName, care este accesată folosind metode

QString objectName() const; void setObjectName(const QString& nume);

În mod implicit, objectName este un șir gol. Prin această proprietate, obiectelor din arbore li se pot atribui nume pentru căutarea ulterioară.

const QList &children() const;

– returnează o listă de obiecte copil.

T findChild(const QString& nume = QString()) const;

– returnează un obiect copil numit nume, care poate fi turnat la tipul T, sau 0 dacă nu este găsit un astfel de obiect. Nici un argument nume returnează toate obiectele copil. Căutarea se efectuează recursiv.

QList QObject::findChildren(const QString& nume = QString()) const;

– returnează toate obiectele copil cu numele nume, care poate fi turnat la tip T, sau o listă goală dacă nu sunt găsite astfel de obiecte. Nici un argument nume returnează toate obiectele copil.

QList Căutarea se efectuează recursiv.

QObject::findChildren (const QRegExp& regExp) const; – asemănător, dar cu căutare prin expresii regulate.

regExp

void dumpObjectTree();

– afișează informații de depanare despre arborele obiectelor cu rădăcina dată.

5.4. Semnale și sloturi Atunci când se creează interfețe grafice cu utilizatorul, interacțiunea cu obiectele se realizează adesea prin apeluri inverse, de ex. transmiterea codului pentru execuția ulterioară (sub formă de pointeri de funcție, functori etc.). De asemenea, concept popularŞi evenimentelor procesoare

, în care handlerul acționează ca un interceptor de evenimente pentru un anumit obiect.

Qt introduce conceptul de semnale și sloturi. Semnal

trimis atunci când este apelată metoda corespunzătoare. Programatorul trebuie doar să specifice prototipul metodei în secțiunea de semnale.

Slot

5.4.1. Anunțând semnale și sloturi, trimitere semnale

Ca exemplu tipic de slot, luați în considerare metoda de obținere a proprietății ( Metode de obținere și setare). Metoda de setare a proprietăților ( getter) în acest caz semnalul va corespunde.

Lista 3.1. Clasa MyClass cu slot void setValue(int x) și signal void valueChanged(int x)
// clasa mea.h #include clasa MyClass: public QObject ( Q_OBJECT public: MyClass(int x, QObject *parent = 0); int value() const; sloturi publice: void setValue (int x); semnale: void valueChanged (int x); privat: int x_ ;

Observați macro-ul Q_OBJECT, care semnalează Qt că sistemul metaobiect este utilizat.

Lista 3.2. Implementarea metodelor clasei MyClass cu slot void setValue(int x) și semnal void valueChanged(int x)
// clasa mea.cpp #include #include "myclass.h" MyClass::MyClass (int x, QObject *parent) : QObject(parent) ( setValue (x); ) int MyClass::value() const ( return x_; ) void MyClass::setValue ( int x) ( dacă (x_ == x) returnează; x_ = x; emit valoareSchimbată (x); )

Cuvântul cheie emit este responsabil pentru trimiterea unui semnal.

Doar un prototip este specificat pentru un semnal, iar semnalul nu poate returna o valoare (adică este specificat void). Compilatorul metaobject este responsabil pentru implementare, de asemenea, convertește sintaxa extinsă cu cuvintele cheie semnale, sloturi, emise în cod C++ standard.

De fapt, cuvintele cheie pot fi înlocuite cu macrocomenzile Q_SIGNALS, Q_SLOTS și Q_EMIT. Acest lucru este util dacă utilizați biblioteci terțe care folosesc deja cuvintele semnale, sloturi sau emit.

Procesarea cuvintelor cheie este dezactivată cu indicatorul no_keywords. În fișierul de proiect qmake (.pro) adăugați

CONFIG += no_keywords

Puteți privi rezultatul compilatorului metaobject în fișierul moc_slots.cpp, care este generat din slots.h și compilat împreună cu rest.cpp.

5.4.2. Conectarea unui semnal la un slot

Lista 3.3. Conectarea semnalului void MyClass::valueChanged (int x) la slotul void MyClass::setValue (int x)
// main.cpp #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: #include "myclass.h" int main() ( MyClass a(1); MyClass b(2); QObject::connect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int))); a.setValue(3);<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 return 0; }

Aici, folosind QObject::connect, semnalul obiectului a este conectat la slotul obiectului b (se trec pointerii către QObject). Macrocomenzile SIGNAL și SLOT formează semnături șir pentru metode. Argumentele lor trebuie să conțină prototipuri fără a specifica nume de variabile, adică.

Semnăturile sunt folosite pentru potrivirea tipului: semnătura semnalului trebuie să se potrivească cu semnătura slotului. În acest caz, semnătura slotului poate fi mai scurtă dacă sunt ignorate argumente suplimentare.

O altă opțiune pentru apelarea QObject::connect:

b.connect(&a, SIGNAL(valueChanged(int)), SLOT(setValue(int)));

Deci, aici, apelarea MyClass::setValue pe a va declanșa MyClass::setValue pe b .

Atenție la linie dacă (x_ == x) returnează; . Este necesar pentru a evita problemele cu conexiunile ciclice. De exemplu, următorul cod va funcționa:

Lista 3.4. Conectarea ciclică a semnalelor void MyClass::valueChanged (int x) cu sloturi void MyClass::setValue (int x)
// main.cpp #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: #include "slots.h" int main() ( MyClass a(0); MyClass b(1); MyClass c(2); QObject::connect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue) (int))); QObject::connect (&b, SIGNAL(valueChanged(int)), &c, SLOT(setValue(int))); (setValue(int)));<< "a:" << a.value(); // 3 qDebug() << "b:" << b.value(); // 3 qDebug() << "c:" << c.value(); // 3 return 0; }

QObject::connect returnează true dacă conexiunea este reușită și false în caz contrar - de exemplu, când semnalul sau slotul nu este găsit sau semnăturile lor sunt incompatibile.

Dacă adăugați conexiuni identice folosind QObject::connect, slotul va fi apelat de mai multe ori.

5.4.3. Închidere

Pentru a deconecta semnalul de la un slot, utilizați QObject::disconnect:

QObject::disconnect (&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));

Dacă oprirea are succes, se returnează true.

Dacă în loc de semnal (SIGNAL(...)) specificați 0, atunci acest receptor de semnal (b) și slotul sunt deconectate de la orice semnal:

QObject::disconnect (&a, 0, &b, SLOT(setValue(int)));

Dacă specificați 0 în loc de receptorul de semnal (b) și slotul (SLOT(...)), atunci tot ceea ce este conectat la acest semnal va fi dezactivat:

QObject::disconnect (&a, SIGNAL(valueChanged(int)), 0, 0);

Dacă specificați 0 în loc de slot (SLOT(...)), atunci tot ceea ce este conectat la acest receptor de semnal (b) va fi dezactivat:

QObject::disconnect (&a, SIGNAL(valueChanged(int)), &b, 0);

Obținem următoarele opțiuni pentru a apela QObject::disconnect:

// Deconectați totul de la semnalele trimise de obiectul a: QObject::disconnect (&a, 0, 0, 0); // Același lucru, dar sub forma unei metode: a.disconnect(); // Deconectați totul de la semnalul SIGNAL(...) trimis de obiectul a: QObject::disconnect (&a, SIGNAL(...), 0, 0); // Același lucru, dar sub forma unei metode: a.deconectare (SEMNAL(...)); // Deconectați acest receptor b: QObject::disconnect (&a, 0, &b, 0); // Același lucru, dar sub forma metodei a: a.deconectare (&b);

Când ștergeți unul dintre obiectele de conexiune, Qt șterge automat conexiunea în sine.

5.4.4. Restricții

Compilatorul metaobiect are o serie de limitări care se aplică și lucrului cu semnale și sloturi.

  1. moc nu se ocupă de șabloane sau macrocomenzi, așa că șabloanele de clasă nu pot defini semnale și sloturi, iar macrocomenzile nu pot fi utilizate atunci când se declară semnale și sloturi (inclusiv atunci când se specifică parametrii).

    Macro-urile nu pot fi utilizate în nicio zonă de cod care trebuie gestionată de moc . În special, nu puteți specifica o clasă de bază prin intermediul acestora.

    Cu toate acestea, unele caracteristici ale preprocesorului pot fi utilizate. Sunt disponibile constructe condiționale simple (cu directivele #if, #ifdef, #ifndef, #else, #elif, #endif, precum și operatorul special definit). Pentru a crea declarații, moc are o opțiune de linie de comandă -D . Utilitarul qmake transmite la moc toate declarațiile enumerate în parametrul de proiect DEFINES.

    De exemplu, moc va procesa corect

    #if 0 // Bloc ignorat #endif
    Blocare #ifdef FOO // ... #endif

    va fi, de asemenea, procesat numai dacă moc -DFOO este apelat, sau dacă există o linie #define FOO înaintea acesteia.

  2. Tipurile trebuie specificate în întregime deoarece QObject::connect() le compară literalmente. În special, dacă enumerarea Bar este definită în interiorul clasei Foo, atunci Foo::Bar trebuie specificat în argumentele semnal:

    class Foo: public QObject ( Q_OBJECT enumerarea Bar ( a, b, c ); semnale: void somethingHappened (Foo::Bar x); );
  3. Indicatorii de funcție nu pot fi utilizați ca parametri de semnal și de slot. De exemplu,

    int (*distractiv)(int)

    nu este un argument valid. Puteți folosi typedef:

    typedef int (*fun)(int);

    De obicei, este mai bine să folosiți moștenirea și funcțiile virtuale în loc de pointeri.

  4. Clasele imbricate nu pot conține semnale și sloturi.
  5. Semnalele și sloturile care returnează referințe sunt tratate ca și cum ar fi returnate nule.
  6. Secțiunile de semnale și sloturi pot declara doar semnale și sloturi.

5.5. Proprietăți

O clasă care moștenește QObject poate conține o declarație de proprietate folosind macrocomanda Q_PROPERTY():

Q_PROPERTY( nume de tip

CITIRE getFunction

Parametri macro necesari:

  • tip– tipul proprietății;
  • nume– numele proprietății;
  • getFunction– metoda const pentru citirea unei valori; tipul de returnare trebuie să fie tip, tip* sau.

tip&

  • Parametri macro opționali: setFunction tipul de returnare trebuie să fie– o metodă de setare a valorii unei proprietăți, trebuie să returneze void și să ia un singur argument de tip sau;
  • , sau resetFunction

– o metodă de setare a valorii implicite a unei proprietăți care depinde de context trebuie să nu aibă argumente și să returneze void ;

Metodele pot fi virtuale sau moștenite din clasa de bază. Pentru moștenirea multiplă, acestea trebuie să aparțină primei clase din listă.

  • Pentru atributele opționale DESIGNABLE, SCRIPTABLE, STORED, USER, valorile booleene sunt permise:
  • DESIGNABLE – dacă se afișează proprietatea în Qt Designer și în programe grafice similare. Valoarea implicită este true , dar puteți specifica și o metodă booleană.
  • SCRIPTABLE – dacă proprietatea ar trebui să fie vizibilă pentru motorul de script. Valoarea implicită este true , dar puteți specifica și o metodă booleană.
  • STORED – dacă proprietatea trebuie salvată atunci când starea obiectului este salvată sau dacă este calculată prin alte proprietăți. Implicit la true .

USER - dacă proprietatea este editabilă de către utilizator. De obicei, clasele corespunzătoare controalelor au o astfel de proprietate. Implicit la false.

De exemplu, QWidget declară, printre altele, următoarele proprietăți:

Q_PROPERTY (QSize minimumSize READ minimumSize WRITE setMinimumSize) Q_PROPERTY(int minimumWidth READ minimumWidth WRITE setMinimumWidth STORED false DESIGNABLE false) Q_PROPERTY(int minimumHeight READ minimumHeight WRITE setMinimumHeight STORED false)

Proprietatea minimumSize este de tip QSize și poate fi obținută folosind QSize minimumSize() const și setată folosind void setMinimumSize (const QSize&). minimumWidth și minimumHeight sunt calculate prin minimumSize , deci sunt setate la STORED false .

Exemplu de proprietate cu atributul USER – text în QLineEdit:

Q_PROPERTY(QString text READ text WRITE setText USER true)

Următoarele metode sunt utilizate pentru a citi și scrie proprietăți:

QVariant QObject::property (const char * nume) const; bool QObject::setProperty (const char * nume, const QVariant& valoare); property() returnează fie valoarea proprietății varianta gresita

setProperty() returnează true dacă obiectul are proprietatea specificată cu un tip compatibil cu valoarea transmisă. În caz contrar, se returnează false.

Dacă clasa nu are proprietatea specificată, atunci este adăugată proprietate dinamică obiect. Lista proprietăților dinamice poate fi obținută folosind

QList QObject::dynamicPropertyNames() const;

Să ne uităm la un exemplu de utilizare a proprietăților. Lăsați clasa MyClass să aibă un text de proprietate șir (de tip QString):

Lista 4.1. Declarația clasei MyClass cu proprietatea text
// clasa mea.h #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: clasa MyClass: public QObject ( Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText) public: MyClass(QString text, QObject *parent = 0); QString text() const; void setText(const QString& text); privat: QString text_; ) ;
Lista 4.2. Definirea metodelor clasei MyClass cu proprietatea text
// clasa mea.cpp #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: #include "myclass.h" MyClass::MyClass(QString text, QObject *parent) : QObject(parent) ( setText(text); ) QString MyClass::text() const ( return text_; ) void MyClass::setText( const QString& text) ( text_ = text; )

Lucrul cu o proprietate:

Lista 4.3. Lucrul cu proprietatea text a obiectului MyClass
// main.cpp #include Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: #include "myclass.h" int main() ( MyClass str("foo"); qDebug()<< "text:" << str.text(); // Через метод: str.setText("bar"); qDebug() << "text:" << str.text(); // Через setProperty() / property(): str.setProperty("text", QVariant("baz")); QVariant prop = str.property("text"); qDebug() << "text:" << prop.toString(); // Добавление динамического свойства: str.setProperty("foo", QVariant("bob")); str.setProperty("bar", QVariant("slack")); QListd_props = str.dynamicPropertyNames(); QListIterator<< "" << d_prop_name << ":" << d_prop.toString(); } return 0; }

iter(d_props);

// (Vom privi separat containerele și iteratoarele) while (iter.hasNext()) ( const char* d_prop_name = iter.next().data(); QVariant d_prop = str.property(d_prop_name); qDebug()

Programul ar trebui să afișeze următoarele:

text: „foo” text: „bar” text: „baz” foo: „bob” bar: „slack”

Desigur, este mai sigur și mai rapid să apelați metode dintr-o anumită clasă pentru a citi și scrie proprietăți. property() și setProperty() sunt necesare atunci când nu se știe nimic despre clasă, în afară de numele și tipurile proprietăților.

Dacă nici măcar nu cunoașteți lista de proprietăți și metode pentru o clasă, puteți utiliza un metaobiect.

5.6. Lucrul cu MetaObjects

Meta obiectul este returnat de metodă

QObject::metaObject()

Poate fi folosit pentru a prelua în mod dinamic informații despre o clasă, cum ar fi în API-ul Java Reflecion.

5.6.1. Informații de bază

Numele clasei revine

const char * QMetaObject::className() const;

Pointer către metaobiectul clasei de bază –

const QMetaObject* superClass() const;

clasa MyClass: public QObject ( Q_OBJECT public: Q_INVOKABLE MyClass(); // vizibil pentru sistemul metaobject Q_INVOKABLE void foo(); // vizibil void foo(); // nu este vizibil);

Pentru a accesa metode (inclusiv semnale și sloturi), utilizați

int QMetaObject::methodCount() const; int QMetaObject::methodOffset() const; QMetaMethod QMetaObject::method (index int) const;

Metodele și proprietățile clasei sunt indexate. O metodă este accesată prin index prin QMetaObject::method() .

Numărul total de metode, inclusiv cele moștenite, este returnat de QMetaObject::methodCount() . Părtinire metodele clasei este returnată de QMetaObject::methodOffset() , arată la ce index încep metodele acestei clase. Offset-ul crește odată cu moștenirea și arată numărul de metode ale clasei de bază.

Exemplu de prezentare a metodei:

const QMetaObject* m_obj = obj.metaObject(); pentru (int i = m_obj->methodOffset(); i< m_obj->methodCount(); i++) ( qDebug()<< m_obj->metoda(i).semnătura(); )

Dacă am începe de la indexul 0, am obține metode din toate clasele de bază, inclusiv QObject:

distrus(QObject*) distrus() deleteLater() _q_reregisterTimers(void*)...

Metodele care încep cu _q_ sunt utilizate intern de Qt și nu fac parte din API.

Constructorii sunt specificați separat:

QMetaMethod QMetaObject::constructor (index int) const; int QMetaObject::constructorCount() const;

De exemplu, obținem o listă de constructori QObject:

Lista 5. Deducerea constructorilor QObject prin sistemul metaobject
Mai mult, toate numele claselor Qt încep cu Q, de exemplu QObject, QList sau QFont. Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: Majoritatea claselor au fișiere antet corespunzătoare cu același nume (fără extensia .h), adică. trebuie sa folosesti: int main() ( QObject obj; const QMetaObject* m_obj = obj.metaObject(); for (int i = 0; i< m_obj->constructorCount(); i++) ( qDebug()<< m_obj->constructor(i).semnătură();

) returnează 0; )

Rezultat:

QObject(QObject*) QObject()

Indicele unei metode, semnal, slot sau constructor poate fi obținut prin semnătura acestuia:

int QMetaObject::indexOfConstructor (const char * constructor) const; int QMetaObject::indexOfMethod (const char * metoda) const; int QMetaObject::indexOfSignal (const char * signal) const; int QMetaObject::indexOfSlot (const char * slot) const; Pentru constructori, sunt de așteptat metode sau semnale semnături normalizate

. Ele pot fi obținute folosind o metodă statică

static QByteArray QMetaObject::normalizedSignature(const char * metoda);

De exemplu,

QMetaObject::normalizedSignature("int * foo(const QString &, QObject *)")

returnează „ int*foo(QString,QObject*)”.

Funcționează la fel

Aceasta este o transformare textuală în formă canonică, utilizată în special atunci când se verifică compatibilitatea semnalului și a slotului.

5.6.3. Proprietăți

Puteți lucra cu proprietăți în același mod.

int QMetaObject::propertyCount() const; int QMetaObject::propertyOffset() const; QMetaProperty QMetaObject::property (index int) const;const QMetaObject* m_obj = obj.metaObject(); pentru (int i = m_obj->propertyOffset(); i< m_obj->>PropertyCount(); i++) ( qDebug()<< m_obj->proprietate(i).nume(); )

(Dacă te uiți la toate proprietățile, inclusiv pe cele moștenite, vei vedea cel puțin objectName din QObject .)

Indicele unei proprietăți poate fi obținut după numele acesteia:

int QMetaObject::indexOfProperty (const char * nume) const;

5.6.4. Transferuri

Enumerările sunt înregistrate într-o clasă folosind macrocomanda Q_ENUMS().

Se numește o enumerare ale cărei valori pot fi combinate folosind un SAU pe biți pavilionși trebuie să fie înregistrat folosind Q_FLAGS() .

QMetaEnum QMetaObject: :enumerator (index int) const; int QMetaObject::enumeratorCount() const; int QMetaObject::enumeratorOffset() const;
Lista 6.1. Clasa MyClass cu enumerare tip și flag Mode
clasa MyClass ( Q_OBJECT Q_ENUMS(Type) Q_FLAGS(Mode) public: enum Type ( A, B, C ); enum Mode ( Citire = 0x1, Scriere = 0x2, Executare = 0x4 ); // ... );

Steagurile sunt folosite după cum urmează:

int mode = MyClass::Read | MyClass::Scrie; // ... dacă (mod & MyClass::Write) // Este setat steagul Write? ( // ... )

Lucru dinamic cu enumerari:

Lista 6.2. Ieșirea enumerărilor și steaguri MyClass prin sistemul metaobiect
MyClassobj; const QMetaObject* m_obj = obj.metaObject(); pentru (int i = m_obj->enumeratorOffset() ; i< m_obj->enumeratorCount(); i++) ( QMetaEnum me = m_obj->enumerator(i); if (me.isValid()) // Există un nume ( if (me.isFlag()) // Flag ( qDebug()<< "" << me.scope() << "::" << me.name(); } else { qDebug() << me.scope() << "::" << me.name(); } } }

Rezultat:

MyClass::Type MyClass::Mode

Indexul unei enumerari poate fi obtinut dupa numele acesteia:

int QMetaObject::indexOfEnumerator (const char * nume) const;

5.6.5. CLASSINFO

Folosind macrocomanda Q_CLASSINFO(), puteți adăuga perechi nume-valoare la un metaobiect. De exemplu,

Lista 7.1. Clasa MyClass cu CLASSINFO
clasa MyClass ( Q_OBJECT Q_CLASSINFO ("autor", "Bob Dobbs") Q_CLASSINFO ("versiunea", "0.23") // ... );

Aceste perechi sunt moștenite și pot fi obținute din metaobiect folosind aceeași schemă:

QMetaClassInfo QMetaObject:: classInfo(int index) const; int QMetaObject::classInfoCount() const; int QMetaObject::classInfoOffset() const;

Pentru exemplul de mai sus:

Lista 7.2. Ieșirea CLASSINFO a clasei MyClass
MyClassobj; const QMetaObject* m_obj = obj.metaObject(); pentru (int i = m_obj->classInfoOffset(); i< m_obj->classInfoCount(); i++) ( QMetaClassInfo mci = m_obj->classInfo(i); qDebug()<< mci.name() << ":" << mci.value(); }

) returnează 0; )

Indexul CLASSINFO poate fi preluat după numele său:

int QMetaObject::indexOfClassInfo (const char * nume) const;

5.6.6. Apelarea constructorilor și a metodelor

Argumentele sunt transmise prin obiectele QGenericArgument și QGenericReturnArgument. Ele sunt create de macrocomenzile Q_ARG și Q_RETURN_ARG.

// referință constantă pentru transmiterea unei valori: Q_ARG (T, const T& valoare) // referință pentru returnarea unei valori: Q_RETURN_ARG (T, T& valoare)

Exemplu de utilizare:

Q_ARG(QString, „foo”) Q_ARG(int, 23) Q_RETURN_ARG(QString, str)

Pentru a crea o nouă instanță a unei clase, utilizați metoda newInstance() a metaobiectului, care poate fi transmisă până la 10 argumente.

QObject* QMetaObject::newInstance (QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument (), QGenericArgument (), QGenericArgument (val) = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) const;

În caz de eroare, se returnează 0.

Pentru a apela o metodă, utilizați invokeMethod():

static bool QMetaObject::invokeMethod (QObject* obj, const char * member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument = QGenericArgument (GenericArgument), QGenericArgument (Generic) 3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument = QGenericArgument (val8 = QGenericArgument(), ));
  • obj– pointer către un obiect;
  • membru– numele metodei;
  • tip– tip de apel:
    • Qt::DirectConnection – imediat,
    • Qt::QueuedConnection – când QCoreApplication::exec() începe să se execute,
    • Qt::AutoConnection - sincron dacă obiectul este în același fir, asincron în caz contrar;
  • ret– valoarea returnată;

Când este apelată asincron, valoarea nu poate fi calculată.

Există supraîncărcări de invokeMethod(). Dacă nu specificați un tip de apel, va fi folosit Qt::AutoConnection. Dacă nu specificați o valoare returnată, aceasta va fi ignorată.

Clasa QMetaMethod oferă aceleași capabilități:

bool QMetaMethod::invoke (obiect QObject*, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument(), QGenericArgument3 = QGenericArgument val0 Argument (), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 =(GenericArgument);

De asemenea, este posibil ca tipul de conexiune și/sau valoarea de returnare să nu fie specificate.

Un apel asincron este utilizat atunci când un calcul durează prea mult pentru a se finaliza, astfel încât rezultatul său nu este așteptat la punctul apelului.

Astfel de calcule sunt de obicei plasate într-un fir separat, astfel încât în ​​mod implicit (Qt::AutoConnection) metodele obiectelor din firele externe sunt numite asincron.

Luați în considerare următoarea clasă:
Lista 8.1. Clasa MyClass cu constructor și metode disponibile pentru sistemul metaobiect

clasa MyClass: public QObject ( Q_OBJECT public: Q_INVOKABLE MyClass (QString text, QObject *parent = 0); Q_INVOKABLE QString text() const; Q_INVOKABLE void setText (const QString& text); privat: QString text_; );

Apelarea constructorului și a metodelor:
Lista 8.2. Apelarea constructorilor și metodelor clasei MyClass prin sistemul metaobiect MyClass foo("foo"); const QMetaObject* m_foo = foo.metaObject(); // Creați o instanță nouă: MyClass *bar = qobject_cast<< "Can"t invoke constructor!"; } else { bar->(m_foo->newInstance(Q_ARG(QString,"bar"))); dacă (!bar) ( qCritical()<< bar->setParent(&foo);<< "Can"t invoke method!"; QString val; // Вызвать метод и получить возвращенное значение: if (!QMetaObject::invokeMethod (&foo, "text", Q_RETURN_ARG(QString, val))) qCritical() << "Can"t invoke method!"; qDebug() << val; // "baz"

qDebug()

text(); // "bar" ) // Apelați metoda: if (!QMetaObject::invokeMethod (&foo, "setText", Q_ARG(QString,"baz"))) qCritical()

text() și setText() sunt numite în acest fel doar ca un exemplu simplu al modului în care funcționează QMetaObject::invokeMethod(). După cum știți deja, aceste două metode trebuie să fie asociate cu o proprietate.

Pentru a lucra eficient cu clasele în timpul execuției, Qt folosește un model de obiect special, în care, folosind moștenirea de la QObject și generarea de cod de către compilatorul metaobject, sunt implementate următoarele:

  • ierarhia obiectelor;
  • un analog special al dynamic_cast , independent de RTTI;
  • sistem de semnale și sloturi;
  • sistem de proprietăți ale obiectului;
  • lucru dinamic cu clasele.

În articolul următor ne vom uita la tipuri, variante, referințe și partiționarea datelor.

După o zi de post-viață, am început să observ o scurgere de karma, așa că îmi cer scuze anticipat pentru stilul de prezentare posibil inacceptabil din articol și subiectivitate

Bună, Habrahabr!

În ultimul timp, nu m-am putut abține să nu fiu atent la popularitatea subiectului Qt de pe Hubrik, dar, cu toate acestea, în comentarii continui să văd oameni care spun lucruri de-a dreptul false și de neînțeles. Cu această postare am vrut să înlătur câteva concepții greșite despre Qt și să vă spun de ce ar trebui să treceți de la Java/Obj-C/.NET la Qt moale și pufos.

Sub tăietură vor fi o mulțime de impresii, subiectivități și părerile mele umile pe această temă cel mai minunat cadru pentru dezvoltarea aplicațiilor. Cu toate acestea, voi încerca să adaug câteva lucruri interesante, astfel încât articolul meu să dobândească măcar un sens util din punct de vedere tehnic. Sper să se dovedească a fi o lectură distractivă și să vă placă.

Ei bine, să mergem?

Veshch nr. 1. C++ API

Nu este un secret pentru nimeni că Qt are un API foarte convenabil și, mai precis, modulul qtbase conține un număr suficient de clase pentru majoritatea sarcinilor de zi cu zi ( Qt este mai mult decât un cadru GUI lol). Am vorbit deja despre ambalajele pentru containere STL în articolul meu de acum trei ani - tytsk. Sunt incluse și clase pentru lucrul cu șiruri de caractere, ieșirea de depanare și multe, multe altele.

QString fructe = „măr, banană, portocală, banană”; QStringList fruitList = fructe.split(", "); qDebug()<< fruitsList; // выведет в консоль [ "apple", "banana", "orange", "banana" ] fruitsList.removeDuplicates(); fruits = fruitsList.join(", "); qDebug() << fruits; // выведет в консоль "apple, banana, orange"
Merită spus că Qt are și module pentru lucrul convenabil cu XML și baze de date ( cu integrarea sistemului delicios-delicios MVC kutesh), OpenGL, lucru audio/video (Phonon), programare în rețea, WebKit2. Pentru sarcinile cu care se confruntă un proiect mediu, această bucătărie este suficientă în 90% din cazuri, iar problemele apar rar cu modulele.

Având în vedere dragostea mea pentru C++, sunt foarte, foarte mulțumit de suportul oferit de Qt pentru diverse lucruri non-triviale la nivel multiplatform. De câteva ori a trebuit să rezolv momente deosebit de de neînțeles, dar așa stau lucrurile.

Veshch nr. 2. Qt Rapid

Qt Quick este o abordare super-slick pentru a crea o interfață grafică cu utilizatorul. Folosind un limbaj declarativ QML (ghici unde a fost inventat lol) similar cu JavaScript, puteți obține o productivitate ridicată la prototiparea interfeței în aplicații orice complexitate. Și lucrul amuzant este că, cu lucrurile care merg așa, Chiar și un designer care cunoaște sintaxa JavaScript se poate ocupa de prototiparea interfeței. Acestea ar fi toate cuvinte goale dacă nu v-aș fi arătat un exemplu de cod funcțional (mai multe puteți găsi pe Proiectul Qt - acolo).

Import QtQuick 2.0 dreptunghi ( id: lățimea paginii: 320; înălțime: 480 culoare: „gri deschis” Text ( id: helloText text: „Hello world!" y: 30 anchors.horizontalCenter: page.horizontalCenter font.pointSize: 24; font. bold: true ) Grilă ( id: colorPicker x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4 rânduri: 2; coloane: 3; spațiere: 3 Celulă ( cellColor: „red”; onClicked: helloText.color = cellColor ) Cell ( cellColor: "verde"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "blue"; onClicked: helloText.color = cellColor ) Cell ( cellColor: "yellow"; onClicked: helloText.color = cellColor ) ) Celulă ( cellColor: „steelblue”; onClicked: helloText.color = cellColor ) Cell ( cellColor: „black”; onClicked: helloText.color = cellColor ) )

Implementarea obiectului Cell este extrem de banală și este definită astfel

import QtQuick 2.0 Item ( id: proprietatea containerului alias cellColor: rectangle.color semnal clicked(color cellColor) lățime: 40; înălțime: 25 Dreptunghi ( id: dreptunghi border.color: „alb” anchors.fill: părinte) MouseArea (ancore. umplere: părinte onClicked: container.clicked(container.cellColor) ) )

Nu există o singură linie de C++ în acest cod și funcționează bine. E bine, nu-i așa? Chiar m-a făcut să mă simt ca un vrăjitor - este mai ușor să merg la magazin să cumpăr pâine decât să pun la punct o astfel de aplicație. Cu toate acestea, în aplicațiile complexe, QML singur nu este suficient și îl combinăm cu C++. Acest lucru a fost discutat în multe articole ale hub-ului Qt Software - de exemplu, acolo.

Veshch nr. 3. Comunitate

Ei bine, am ajuns la un moment plăcut. Dacă vorbim despre mine, am lucrat cu Qt relativ puțin - doar 5 ani. Qt organizează anual evenimente - Qt Developer Days și Qt Contributors „Summit. Am fost o dată la fiecare dintre ele, anul trecut, și mi-a plăcut foarte mult – nivelul de pregătire este ridicat, iar impresiile sunt livrate. Am avut și ocazia să comunicați cu „veteranii” Qt - oameni care au participat la summit-ul de 10 ani. Îmi imaginez cât de tare este să vezi creșterea unui astfel de proiect sub proprii ochi și să fii în epicentrul tuturor dezvoltării - pur și simplu delicios .

Acești oameni sunt foarte toleranți și îi tratează bine pe nou-veniți, mi-a fost foarte ușor și convenabil să stabilesc contacte cu astfel de oameni minunați. Proiectul Qt are forumuri unde oricine poate obține un răspuns la întrebarea sa. E amuzant, dar chiar nasol foarte vioi si acolo într-adevăr răspunde la întrebările care apar în procesul de învățare Qt.

Veshch nr. 4. Sursă deschisă și revizuire a codului

Soretz Cut este dezvoltat în mod deschis, în principal, de către Digia (comm. support +), KDAB, ICS și dezvoltatori entuziaști. Totul este găzduit pe Gitorious - Tadamts. Pentru a contribui la dezvoltarea proiectului, trebuie să treci prin strict verificarea codului - automatizată (respectarea stilului de cod despre care am scris deja mai devreme - ptsss) și uman - codul tău va fi privit de bărbații care nu au încredere în tine și vor căuta uși din spate în codul tău. Acesta este un proces destul de complicat (probleme cu Git/revizuiri de pe Consiliul de revizuire) și probabil că voi scrie un articol despre asta într-o zi.

Apropo, am câteva comiteri în arborele qtbase, așa că mă puteți întreba în PM și voi încerca să vă răspund la întrebări.

Veshch nr. 5. Dinamica dezvoltării proiectelor

Qt a fost în dezvoltare de mulți ani, de la sfârșitul anilor 90. În acest timp, companii precum Trolltech și Nokia au jucat deja destul din versiunea sa comercială, iar acum Digia o face. Dar un lucru este sigur, proiectul este viu și înfloritor. Pentru încă câțiva ani, toată lumea a scris design în widget-uri (clase C++, toate bazate pe QWidget), dar astăzi chiar și un copil mic o poate face. Nu cred că merită să spun că, în paralel cu acesta, cel mai puternic lucru este dezvoltat în mod activ - Qt Creator, care astăzi mulțumește nu numai programatorilor Qt!

^ cool Qt Creator, în care poți crea miracole și nu vei primi nimic pentru asta.

Din păcate, nu am numere stricte, dar ei spun că codul este optimizat în fiecare zi, iar baza de cod este extinsă cu atenție - sunt adăugate noi caracteristici și bug-uri vechi sunt remediate (am văzut deja asta de multe ori). Toate acestea sunt foarte potrivite și nu pot decât să ne bucurăm.

În plus, platformele se dezvoltă acum în mod activ iOS, Android, Windows Phone, acum puteți compila programe pentru ei!

Concluzie

Cred că înțelegi că Qt este foarte tare și după ce ai citit articolul te-ai îndrăgostit de el la fel de mult ca mine.
Vă mulțumim pentru atenție!