joi, 26 martie 2020

Qt - Saptamana a 6 -a - Pagina de blog de sprijin

Astazi am sa va invit sa (refaceti) sau sa faceti (cazul celor cu care nu m-am intalnit la laborator sapatamana trecuta) lucrarea de laborator numarul 4 si lucram apoi exemplu din capitolul al V-lea).

Pe urma incercam sa facem niste Visual Qt.

Va rog sa aveti cartile la indemana.

Voi bloga ca de obicei pe video4linux.blogspot.com, si sper sa fac destul de des update ca sa vedeti paragrafele aparand pe masura ce sunt scrise.
Mai dati eventual cate un refresh.

Exemplul4. (recapitulat)
Intre timp, pregatiti QtCreator-ul... incepeti un empty project... si ...
File -> New File or Project -> Other Project.
Alegeti un empty qmake project.
Si dati-i un nume gen qt-nonvisual-ex4-recap.
Apoi alegeti Kit-ul Desktop.
Click pe Next. Apoi pe Finish.

Ne oprim putin, suntem totusi la cursul teoretic, recapitulez niste elemente de teorie:
Sa (re)citim impreuna primele paragrafe din capitolul al patrulea, sunt despre 
notiuni de teorie a POO, care au (o parte din ele ) legatura cu exemplul care urmeaza. 

Polimorfismul: Era inteles ca fiind capacitatea obiectelor de a raspund ediferit, cu diversi algoritmi, aceluiasi apel de metode.
In exemplele anterioare am vazut cum obiectele Qt raspundeau diferit la apelul metodei show() desenandu-se fiecare dupa rostul si randuiala sa.
Nu discutam aici de polimorfismul parametric. Acesta are legatura cu template-urile de clase si - daca ati programat vreodata in Haskell - cu functiile polimorfice de acolo.

Mostenirea: procesul de transfer a atributelor si metodelor de la o clasa de baza la clsele derivate din ea ati vazut-o deja la lucru in exemplele anterioare. Ca efect al ei, in Qt toate widget-urile au la dispozitie metode cum ar fi resize(), move() sau setGeometry(). Si toate sunt mostenite de la clasa de baza QWidget.

Incapsularea ... duce la acea calitate a obiectelor de a include in interiorul lor tot ce au nevoie ca sa isi faca treaba, atribute si metode, iar artributele pot fi alte obiecte sau pointeri la alte obiecte (deja aceasta e plasarea obiectelor in relatii - vom folosi aceasta tehnica in Qt mai tarziu, cand vom construi ferestre complexe).

Reamintiti-va, atunci cand un obiect contine in interiorul sau pointeri la alte obiecte, putem considera ca aceste obiecte sunt in relatie. Relatiile se implementeaza in general prin pointeri, in Qt. Imaginati-va o fereastra cu butoane, exact ca o gospodina sau un gospodar cu sacose (facand unul din putinele lucruri perimise in aceste vremuri - aprovizionarea).

In carte aici mai sunt niste paragrafe... unul despre proiectarea obiectelor complexe folosind un modelator UML. Sub Linux exista unul disponibil, gratuit :)). Urmatorul paragraf lanseaza ideea de a nu se scrie programe ca pe vremuri, ca in vremea cand se scria Unix-ul, ci in maniera moderna.

Ideea este ca daca de exemplu testezi o anumita functionalitate, scriind chiar si un banal cod in functia main(), mica fereastra care rezulta ruland programul se poate transforma intr-o clasa de obiecte. Exact acest lucru este/era ilustrat in capitolul al patrulea. Si chiar era o problema reala, sa construiesti asemenea componenete reutilizabile. Panourile de control pot beneficia de o multime de astfel de obiect Qt, termometre, voltmetre, butoane si comutatoare de tot felul, o adevarata industrie orizontala s-a dezvoltat pentru a  le produce.

Dati pagina la pagina 49 editia format mic sau la paragraful despre felul cum devine codul din main() -ul anterior o noua clasa de obiecte Qt. In acest fel, pe masura ce realizati subansamble si subansamble ale aplicatiei, ele raman ca niste piese distincte - clase noi de obiecte - care pot fi refolosite.
Iar faptul ca Qt este multiplatforma inseamna ca pot fi refolosite si pe Linux, si pe Windows si pe IOs si pe Android si pe Windows Rt si pe celelalte platforme hardware care ruleaza Qt... fel de fel de placi de circuite programabile.  Da, si pe Raspberry Pi - daca ma intrebati. Raspberry Pi foloseste un Debian pe nume Raspbian sau niste derivate ale acestuia, si da, Debian si derivatele includ si Qt-ul cu toate instrumentele lor.

Daca aveti aplicatia din laboratorul anterior nu vor fi prea multe de facut.
Si in general, cand transformati niste functionalitati testate sub forma programului principal intr-o clasa de obiecte Qt, transformarea este simpla.

Variabilele si pointerii la obiecte din programul principal devin atribute ale noii clase de obiecte.  Stiti probabil de la alte cursuri ca pot fi publice private sau protejate.  Stiti si definitiile acestor notiuni... Cine nu le stie sa le caute sa le recapituleze ;).
Stiti si felul cum s edeosebesc unele de altele, cele publice de cele private si care este rolul lor. Denumirile sunt intuitive, variabilele publice sunt pentru interfata cu exteriorul, cele private pentru operatiile interne ale clasei, cele protejate sunt totusi accesibile in clasele derivate ... revedeti manualul de anul I.

Exista functii in programul principal. Sunt cele mai bune candidate sa devina metode in noua clasa, dar puteti adauga in ea si functii suplimnentare care sa ofere servicii publice, necesare in viitor aplicatiilor care vor folosi clasa. Ah, nu sunteti clarvazator, nu stici de ce va fi nevoie nici ce sa adaugati. Se pot adauga la crearea de clase derivate din clasa dumneavoastra.

Face programul principal o  initializare a intregului set de variabile, o initializare a pointerilor prin crearea de obiecte aflte in relatie cu cel din noua noastra clasa ? Toate acestea sunt numai bune de a deveni parte din constructorul clasei Qt, si regula aceasta functioneaz chiar si la clasele creat cu designerul vizual. Va amintiti ce era constructorul intr-un limpaj de programare cum este acest dialect extins de C++ ? Exact, era functia care purta numele clasei.

Ora a II-a:
Este momentul sa mai si programam un pic:
Editati fiiserul .pro al proiectului nostru de sub Linux (Daca mai folositi inca Qt 4.6 nu editati fisierul, indicatiile sunt doar pentru Qt 5.x si urmatoarele).

TEMPLATE = app
TARGET = QtHelloWorld
QT += core gui
QT += widgets
#INCLUDEPATH += .
# Input
SOURCES += exemplul4.cpp

Acum adaugati in proiect File -> NewFile or Project -> C++ file (de la categoria C++). Pe care numiti-l main.cpp.

Adaugati in fisierul main  codul de mai jos.


Asa ar arata deocamdata micul nostru program demonstrativ in Qt daca l-ar scrie un incepator care nu isi separa clasele in fisiere distincte.
Bineinteles, programul ruleaza si asa, aici ruleaza pe un Ubuntu Linux:


Dati pagina, pina ajungeti la pagina 56:
Acolo este un mic ghid cum sa mutati, daca sunteti incepatori, o astfel de clasa in doua fisiere distincte... va rog sa il cititi.

Pe scurt, aveti de creat o clasa noua, mywidget (sau MyWidget - asa as recomanda) si sa separati declaratia clasei in fisierul .h implementarea in fisierul .cpp si utilizarea ramane in main.
In main (si in implementarea clasei) adaugati #include "MyWidget.h" (sau "mywidget.h") si adaugati si toate includerile claselor de obiecte necesare, aat in main.cpp cat si in fisierul de implementare.
In final obtineti:

Asa se adauga noua clasa ( era mai bine sa o numiti MyWidget):


Fisierul main.cpp:


Header-ul:

O explicatie aici: La Qt 5.9+ veti gasi acest explicit, care interzice sistemului conversia implicita a claselor de obiecte. Daca am un obiect cerut intr-un loc si ii dau ca parametru acolo alt obiect, in lipsa lui explicit, sistemul cauta primul constructor care poate converti din tipul/clasa data in tipul/clasa necesara si il foloseste automat pentru conversie. Asa poti da in locul unui fel de obiecte alte feluri de obiecte si sa mearga treaba. Qt insa nu incurajeaza acest mod de folosire a claselor.

Clasa implementata:

In final obtineti aceeasi aplicatie.

Cheie de control:
La final veti obtine un fisier .pro care arata astfel, mai putin comentariile:

TEMPLATE = app
TARGET = QtHelloWorld
QT += core gui
QT += widgets
#INCLUDEPATH += .
# Input
SOURCES += \
    main.cpp \
    mywidget.cpp

HEADERS += \
    mywidget.h

Revenim la carte, la Capitolul al V-lea.

Pentru inca aproximativ o jumatate de ora - pina la o ora, citim din capitolul al 5-lea, pagina 61 in editia de buzunar (nu in cartea format B5).

Presupun ca cititorii mei cunosc notiunea de callback si cea de eveniment.
Reamintim pe scurt: Callback, ca la OpenGl si programare Windows este acea rutina a carui adresa o dam sistemului iar sistemul o apeleaza cand are nevoie pt indeplinirea unei sarcini (de ex afisarea fiecarui frame in jocurile video) . Iar eveniment este ceva ce se petrece (click de mouse, redimensionare de fereastra , editare de text finalizata cu Enter sau Tab samd) - pe scurt ceva la care sistemul trebuie sa raspunda efectuind ceva:

Nu uitati ca aplicatiile Qt raspund si la semnale care vin prin conexiunile semnal-slot !

In acest capitol vedem cum se realizeaza o mica aplicatie de dimensiune programata impreuna cu intregul mecanism (facut de noi , "custom" as zice) de redimensionare.

Cateva considerente geometrice despre plasarea widget-urilor in fereastra gasiti in capitolul acesta, in paragraful "Calculul pozitiilor in fereastra". Puteti deduce aceste considerente urmarind indicatiile de pozitionare ale widget-urilor in fereastra date in cod. Ideea este ca avem un widget in stanga sus, un altul plasat pe bara de jos a ferestrei (el se va deplasa la marirea acesteia) si un al treilea la mijloc care se va redimensiona "umflandu-se" cand fereastra creste in dimensiuni. Pe margini si intre widget-uri se lasa spatiu.

Atentie: Acest exemplu este calculat incoordonate absolute, pixeli. Unii programatori de Qt pentru Android prefera sa lucreze ca la aplicatiile Android: calculeaza totul in fractiuni de latime si de lungime de ecran iar la initializarea aplicatiei afla sau cer dimensiunea ecranului si o stocheaza in niste variabile si/sau mai bine intr-un fisier (de unde o pot recupera la alta rulare).

Nota: QResizeEvent -ul , obiectul adus in momentul cand redimensionam fereastra tragand-o de colt poarta cu el cele doua dimensiuni, le aflati cu metodele height() si width().

Programul care demonstreaza cum raspunde o fereastra Qt la evenimente este acesta, scris intr-un singur fisier:

Asa arata declaratia de clasei:


Cam asa arata declaratia de clasa, mai putin erorile: despre ele vreau sa va spun ca apar cand pastrati fragmenete de cod in documente office si MSOffice sau OpenOffice schimba automat anumite coduri de caractere. Acele caractere trebuiesc scrise iar sau inlocuite cu cele initiale.

Asa arata implementarea clasei. Mare atentie la felul cum lucreaza ResizeEvent!


Si asa arata programul principal:


Iar asa arata micul nostru program, in executie, cu doua dimensiuni diferite ale ferestrei:


Si asa:

Exercitiul 1: Separati clasa MyWidget in doua fisere .cpp si .h ca in capitolul al patrulea.
Exercitiul 2*: Creati un algoritm custom de modificare a pozitiei widget-urilor in fereastra: Ce-ati zice daca widget-uri similare s-ar invarti in jurul centrului ferestrei ca raspuns la eveniment.
Exercitiul 3*: Cititi din Help-ul mediului despre procesarea evenimentelor de mouse ale Qt-ului.
Scrieti un program care prelucreaza astfel de evenimente.

Programare cu Designerul: Qt Visual Checkbox (v0.1)

De data aceasta faceti proiectul ca la programarea vizuala: File -> New Project -> Application Qt Widgets Application.
Urmeaza sa proiectati in mod vizual interfata programului.

Interfata va contine:
- O eticheta : Muzica ta preferata
- Patru checkboxuri
- Un buton pentru apasat pe el
- Un camp TextEdit in care apare rezultatul.

Interfata va arata asa, (exemplul este o problema clasica de programare vizuala si multumesc fostului meu coleg B.P. pt varianta in V.B5).


Cum va functiona: Textul de jos reflecta checkbox-urile bifate sus.

Indicatie: Puteti folosi un <QString> numit sa zicem result si un <QTextStream> creat ad hoc pe baza referintei la result, &result.
Apoi in acest in acest QTextStream trimiteti cu operatorul de IO clasic (aici redefinit sa lucreze cu QTextStream-uri)  << stringurile necesare, conditionat de valorile din checkbox-uri care se pot testa cu functia isChecked().
Apoi cu metoda setText(result), aplicam QString-ul rezultat in interiorul TextEdit-ului.

E suficienta indicatia ? Daca nu, priviti  imaginea:


Exercitiu: Corectati codul de mai sus ca sa apara si muzica pupulara pe lista atunci cand este bifat check-box-ul corespunzator. Unde este greseala in imaginea de mai sus ?

Exercitiu: In cazul cand utilizatorul nu bifeaza nimic raspunsul sa fie altul, ingrijorat: "Vai, dar tu nu asculti muzica de loc ?"

Ati uitat ceva: In declaratia clasei declarati si slotul privat corespunzator butonului:


O explicatie este necesara aici: Ca sa putem lansa in executie metoda noastra va trebui sa declaram un slot privat cu un nume special on_pushButton_clicked(), nume format din trei parti: on , numele widget-ului si numele semnalului.

Pentru a verifica proiectul dvs in cazul in care anumite nume de widget-uri nu sunt corecte, observati cum numeste Qt-ul widget-urile implicite.




Niciun comentariu:

Trimiteți un comentariu