Qt in het kort Handleiding Embedded Systems Engineering Groep: ES1, ES1V, ES1D Jos Rouland 7 maart 2012
Voorwoord Om snel op te kunnen starten met de ontwikkelomgeving Qt is deze handleiding gemaakt. Hierbij is er van uitgegaan dat de student Qt onder Windows installeert. Voor andere operating systems zullen de acties vergelijkbaar zijn, maar dat wordt in deze handleiding niet ondersteund. Let wel dat de exacte omschrijvingen gelden op de datum van deze handleiding. Later kunnen er (kleine) veranderingen optreden in de installatieprocedure. De opleiding heeft Qt als ontwikkelomgeving gekozen omdat deze gratis is en erg gebruiksvriendelijk. Bovendien lijkt het vrij sterk op ontwikkelomgevingen als Code- Gear (de opvolger van C+Builder), Visual C++ en wxwidgets. Al deze ontwikkelomgevingen worden veelvuldig in het bedrijfsleven toegepast. Op de website qt.nokia.com vind je veel informatie over het gebruik van dit pakket, met name onder de tabbladen 'LEARN Qt' en 'SUPPORT'. Heel veel plezier met het ontwerpen van mooie user interfaces met behulp van Qt. Jos Rouland 1
Inhoud Voorwoord 1 1 Qt installeren... 3 2 Applicaties ontwikkelen... 4 2.1 Opstarten... 4 2.2 De ontwikkelomgeving... 4 2.3 Widgets en tekst... 6 2.4 Van getal naar string... 9 2.5 Van string naar getal... 9 3 Help functies... 11 4 De debugger gebruiken... 12 2
1 Qt installeren Download vanaf qt.nokia.com via het tabblad 'DOWNLOADS' de Offline installer 1.4 GB Qt SDK voor Windows. (Je kunt ook kiezen voor de Online installer 15MB voor Windows, maar dan kost het installeren meer dan een uur, terwijl de offline installer slechts een minuut kost.) Sla deze installer (Qt_SDK_Win_offline_v1_1_3_en.exe) op je harde schijf op. Voor een ander operating system kies je de betreffende installer. Maak een directory aan voor het opslaan van Qt en start de installer en doorloop de achtereenvolgende schermen. Kies in de Installation folder 'Custom'. ("Default' kan ook, maar dan wordt er veel meer geïnstalleerd dan je nodig hebt voor Informatica 2.) Accepteer de Licence Agreements. Bij Select components vink je Experimental geheel uit. Van Documentation vink je alleen de volgende items aan: - Qt Designer Documentation - Qt SDK Documentation - Qt Simulator Documentation - Qt Creator Documentation - Qt Documentation Bij APIs alleen Notifications API aanvinken. Bij Development Tools alleen Desktop Qt aanvinken en hieronder vervolgens alleen Desktop Qt 4.7.4-1 MinGW. Vink onder Miscellaneous alleen Qt Examples aan. 3
2 Applicaties ontwikkelen 2.1 Opstarten Start Qt Creator op. Wij maken alleen gebruik van Qt Widget Based Application. Onder 'Creating Qt Widget Based Application' vind je een voorbeeld van een te ontwikkelen programma. Dat kun je eventueel nabouwen. Doorloop de volgende stappen: Klik rechtsonder op 'Create Project ' en kies 'Qt Widget Project' (activeer in de linker balk 'Qt Welcome' en vervolgens 'Qt Gui Application'. Kies in het volgende scherm bv. prog1 als name en kies in welke folder je de bestanden wil plaatsen. Aan te bevelen is om 'Use as default project location' aan te vinken. Qt maakt onder de gekozen map telkens een nieuwe map aan (met de opgegeven name) voor een nieuw project. Vink 'Desktop' aan bij Target Setup. Wijzig de Class name in Prog1. Finish. 2.2 De ontwikkelomgeving We zijn nu aangekomen in de ontwikkelomgeving voor ons nieuwe project Prog1. We zien het geopende document prog1.cpp dat twee functies bevat, de zogenaamde constructor Prog1(), gevolgd door de zogenaamde destructor ~Prog1(). De constructor wordt automatisch opgestart als de applicatie wordt gestart. In eerste instantie wordt hierbij de user interface (het hoofdformulier) klaargezet. De destructor wordt automatisch opgestart als de applicatie wordt beëindigd. In eerste instantie wordt hierbij de user interface verwijderd. De user interface kunnen we zien door linksboven 'Forms' open te vouwen en op prog1.ui dubbel te klikken. Er verschijnt nu een leeg formulier met een menubalk. Er verschijnen links ook een vrij groot aantal widgets die we naar het formulier kunnen slepen, maar daarover straks meer. Een widget is een grafisch symbool dat de interactie tussen gebruiker en computer mogelijk maakt. Klik op de groene driehoek linksonder om te compileren, linken en runnen en kies 'Save all'. Het programma wordt na het compileren en linken opgestart (als er geen fouten zijn gedetecteerd) en we zien inderdaad het formulier (zonder de ontwerppuntjes) met de naam Prog1 verschijnen. Ons eerste Windows programma werkt nu. We kunnen het formulier verslepen, minimaliseren, maximaliseren en sluiten. Overigens kunnen we alle files openen door linksboven de diverse onderdelen open te vouwen en vervolgens op de gewenste file dubbel te klikken. Hierna kan een file worden geopend door een keer te klikken op het gewenste document linksonder bij 'Open Documents'. 4
Openen we de file prog1.h dan vinden we hierin de declaratie van de class Prog1. Een class is een uitgebreidere versie van een struct. Een class bevat behalve een aantal variabelen ook functies. We zien hier de prototypes van de constructor en van de destructor (onder public). Onder private is de pointer ui gedeclareerd die wijst naar de user interface Prog1 (het nog vrij lege formulier). De file main.cpp bevat het hoofdprogramma dat een applicatie a declareert en een formulier (window) w dat vervolgens wordt getoond. Tot slot wordt de applicatie opgestart door de functie a.exec() en de returnwaarde van deze functie wordt aan Qt teruggegeven. Dit gebeurt pas als de applicatie a is uitgevoerd, met andere worden als de applicatie is beëindigd. Bij succesvol beëindigen van de applicatie is de returnwaarde 0, anders 1. Openen we de file prog1.ui, dan kunnen we het formulier ontwerpen. Klikken we nu linksboven op 'Edit', dan zien we welke classes er al zijn aangemaakt en welke naam die gekregen hebben. Klikken we op 'Design', dan krijgen we het formulier (de user interface) weer te zien. Een project kan worden gesloten via 'File' in de menubalk. Om een bestaand project opnieuw te openen, klik je linksboven op 'Qt Welcome' en vervolgens rechtsonder op 'Open Project '. Kies dan de gewenste project file (die de extensie.pro heeft). Om het programma wat interessanter te maken, slepen we een Push Button linksboven en een Label rechtsboven op ons formulier. Vergroot de label een beetje door een hoekpunt te verslepen. Klik-rechts op de button en verander zijn naam in "Startknop" (met 'Change object- Name '). Klik-rechts op de button en verander de tekst in "Druk hier" (met 'Change text '). Klik-rechts op de label en verander zijn naam in "Outputlabel" (met 'Change object- Name '). Klik-dubbel op de label en verwijder de tekst op de label. Vink autofillbackgroud aan (rechtsonder bij de attributes van de widget). De puntjes in de label verdwijnen nu. Klik-rechts op de button en kies 'Go to slot ' en kies 'clicked()'. We komen nu automatisch in de editor terecht en wel in de nieuwe functie on_startknop_clicked(). Deze functie wordt opgestart zodra we (tijdens het uitvoeren van de applicatie) op de Startknop klikken. Type als begin van de instructie ui-> en klik-dubbel in het verschijnende menu op 'Outputlabel'. Type hierachter ->sett of scroll naar beneden en dubbelklik 'settext()'. Type tot slot tussen de haakjes van deze functie de tekst "Verrassing!". Run de applicatie opnieuw (met de groene driehoek). Het nieuwe formulier verschijnt en als we op de knop klikken, verschijnt inderdaad de tekst "Verrassing!". We hebben nu een programma met enige interactie tussen gebruiker en computer. Vergeet niet het programma te sluiten door op het kruisje rechtsboven op het formulier te klikken. 5
2.3 Widgets en tekst Breid de applicatie verder uit: Open prog1.h en voeg onderaan 'private' de declaratie "int iteller;" toe. Open prog1.cpp en voeg onderaan in de constructor de instructie "iteller = 0;" toe. Voeg onderaan in de functie on_startknop_clicked() de volgende instructies toe: ui->startknop->settext("nog een keer"); iteller++; if (iteller > 5) exit(0); Run het programma. We zien dat na het klikken op de knop weer de tekst "Verrassing!" verschijnt en dat de tekst op de knop verandert in "Nog een keer". Klikken we nog 5 keer op de knop, dan eindigt de applicatie. Onderaan bij 'Application output' zien we dat de exit code 0 is. Zouden we exit(0) vervangen door bv. exit(10), dan zien we dat de exit code 10 is bij het eindigen van de applicatie. Laten we de applicatie uitbreiden met een tweetal tekstvensters om enerzijds tekst in te voeren en anderzijds resultaattekst zichtbaar te maken. Ook laten we zien hoe een object uit een zogenaamde QString kunnen omzetten in een array van characters. Sleep een Label middenlinks in ons formulier. Geef deze de naam "Inputlabel" en verander de tekst op de label in "Type hier een tekst (begin met k, h, c, o):" Sleep rechts van deze label een Line Edit en noem deze "Inputregel". Sleep een Push Button onder de "Inputregel" en geef deze button de naam "Opdrachtknop". Verander de buttontekst in "Voer opdracht op tekst uit". Sleep een Line Edit onder de button en geef die de naam "Resultaatregel". Plaats links van deze Line Edit een Label met de naam "Resultaatlabel" en de tekst "Resultaat:". 6
De bedoeling van de uitbreiding van de applicatie is dat met het klikken op de Opdrachtknop een actie wordt uitgevoerd op de tekst in de Inputregel, waarbij de soort actie wordt bepaald door de eerste letter in die regel: k: alle letters in de tekst achter de 'k' worden vervangen door kleine letters h: alle letters in de tekst achter de 'h' worden vervangen door hoofdletters c: alle cijfers in de tekst achter de 'c' worden verwijderd o: de tekst achter de 'o' wordt niet gewijzigd Het resultaat wordt weergegeven in de Resultaatregel. Elke andere beginletter geeft de foutmelding "Ongeldig commando!" (in een pop-up venster). Klik-rechts op de Opdrachtknop, kies 'Go to slot ' en kies 'clicked()'. We komen nu automatisch in de editor terecht en wel in de nieuwe functie on_opdrachtknop_clicked(). Plaats hierin de volgende C++ code: QString QTekst; //Qt werkt in Unicode QByteArray ByteTekst, ByteResult; //Tekst in byteformaat char *pctekst, *pcresult; int i = 0; bool bok = true; QTekst = ui->inputregel->text(); //(Unicode)tekst uit Line Edit ByteTekst = QTekst.toLocal8Bit(); //Vertaling in 8-bits code pctekst = ByteTekst.data(); //Beginadres van ASCII-tekst switch (pctekst[0]) { case 'k': ByteResult = ByteTekst.toLower(); break; case 'h': ByteResult = ByteTekst.toUpper(); break; case 'c': do { if (!isdigit(pctekst[i])) { ByteResult.append(pcTekst[i]); } i++; } while (pctekst[i]!= '\0'); break; case 'o': ByteResult = ByteTekst; break; default: ui->resultaatregel->settext(""); bok = false; Foutmelding.showMessage ("Ongeldig commando!"); } if (bok) { pcresult = ByteResult.data(); ui->resultaatregel->settext (pcresult+1); } 7
Voeg aan de file Prog1.h toe: #include <QErrorMessage> en onder 'private': QErrorMessage Foutmelding; We kunnen nu het programma testen door in het invoerveld een tekst te typen met hoofdletters, kleine letters en cijfers, voorafgegaan door een commandoletter (k, h, c of o). Het resultaat verschijnt in de resultaatregel. Bij het beginnen met een andere letter verschijnt in een apart venster de mededeling "Ongeldig commando!". Een alternatieve oplossing voor het omzetten van Unicode in een array van chars is: std::string stekst; //stringobject van C++ QString QTekst; //stringobject van Qt QTekst = ui->lineedit_2->text(); //(Unicode)tekst uit Line Edit const char *pctekst; stekst = QTekst.toStdString(); //Unicode vertalen in C++ string pctekst = stekst.c_str(); //Beginadres van ASCII-tekst enz. Het komt veel voor dat er getallen worden ingevoerd en resultaten van berekeningen moeten worden getoond. Invoer en uitvoer van zowel tekst als getallen vindt plaats als strings van karakters. Bij Qt geschiedt dit via de klasse QString. De strings moeten dus worden omgezet in integers, doubles e.d. Omgekeerd moeten resultaten in de vorm van integers, doubles e.d. worden vertaald in QStrings om op een formulier te kunnen tonen. Het onderstaande project (NrToStr) laat zien hoe deze omzettingen gedaan kunnen worden. Op Scholar vind je de file NrToStr.zip. Pak deze file uit en dubbelklik op NrToStr.pro of open deze projectfile met 'Open File or Project ' als je al in Qt zit. Compileer en run het project. Mocht het compileren niet lukken, klik dan op de linker balk op 'Projects', kies het tabblad 'Build Settings' en zorg ervoor dat de 'Build directory' de juiste is (deze moet gelijk zijn aan directory waarin de project file staat). Het formulier kun je zichtbaar maken door 'Forms' te openen en te dubbelklikken op 'mainwindow.ui': 8
Door op de button "+5" te klikken, wordt het Getal met 5 verhoogd. Door op de button "x2" te klikken, wordt y gelijk aan 2.X gemaakt. Door op de button "Reset" te klikken, worden alle edit vensters gewist. Door op de button "Stop" te klikken, wordt het programma afgesloten. 2.4 Van getal naar string void MainWindow::on_PshBtnPlus5_clicked() { static int in = 0; in = in + 5; QString qsgetal; ui->leresultaat->settext(qsgetal.sprintf("%d", in)); } De integer variabele in wordt telkens met 5 verhoogd als er op de knop "+5" wordt gedrukt. Merk op dat bij de declaratie van in de specificator 'static' gebruikt is. Deze zorgt ervoor dat de variabele (en zijn waarde) bewaard blijft bij het verlaten van de functie. Haal je deze specificator weg, dan zul je zien dat de waarde van in telkens opnieuw 5 wordt bij het opnieuw save, compileren en opstarten van het project (i.p.v. het achtereenvolgens 5, 10, 15, enz. worden). De laatste instructie zorgt ervoor dat de inhoud van de QLineEdit leresultaat gevuld wordt met de tekst.uit de QString qsgetal. Dit object wordt gevuld door de functie sprintf te gebruiken. Deze functie is al bekend als standaard C-functie die gegevens volgens een aan te geven formaat niet op het scherm afdrukt, maar in een string plaatst. Deze functie maakt nu deel uit van het object qsgetal, maar nu moet het adres van de string achterwege blijven, omdat de string in het betreffende object (qsgetal) wordt geplaatst. In dit voorbeeld wordt de integer variabele in m.b.v. de conversiespecificatie %d geconverteerd naar een string en opgeborgen in qsgetal. 2.5 Van string naar getal void MainWindow::on_PshBtnMaal2_clicked() { QString qsx, qsy; qsx = ui->leinvoer->text(); bool bok; double dx = qsx.todouble(&bok); if (bok) { dx = dx * 2; ui->leuitvoer->settext(qsy.sprintf("%.3f", dx)); } else Foutmelding.showMessage ("Ongeldige invoer!"); } 9
De instructie dx = qsx.todouble(&bok); vertaalt de string uit de QString qsx naar een double dx. Op het adres van de boolean variabele bok wordt de waarde true (als de conversie gelukt is) of false (als er verkeerde karakters in de string voorkomen) gezet. In het geval bok = true, wordt de waarde van dx verdubbeld en vervolgens wordt de double variabele dx m.b.v. de conversiespecificatie %.3f geconverteerd naar een string (waarin het getal met 3 cijfers achter de komma wordt afgebeeld) en opgeborgen in de QString qsy. Vervolgens wordt de zo verkregen string uit qsy in de QLineEdit leuitvoer gezet. Als bok false is, wordt de foutboodschap "Ongeldige invoer!" getoond. 10
3 Help functies Qt kent 4 soorten help: 1. Door op de knop 'Help' te klikken, krijg je algemene infomratie over Qt. Door hierna bv. op 'Edit' of 'Design' te klikken, verlaat je deze functie weer. 2. Met 'Help' boven in de menubalk kom je in een keuzemenu. Met 'Contents' kom je in de bovengenoemde situatie terecht. Met 'Context help F1' kom je in de onderstaande situatie terecht. 3. Door de F1-toets in te drukken, krijg je informatie over de geactiveerde component op het formulier of over het item waarop de cursor staat in de editor. Deze methode is meestal de meest handige. 4. Bij het schrijven van het programma hebben we al gezien dat er tijdens het typen soms een pop-up menu verschijnt waaruit je kunt kiezen. Dit bespaart typewerk en laat bovendien zien welke mogelijke items je kunt gebruiken in de betreffende situatie. Dit voorkomt fouten. 11
4 De debugger gebruiken Bij moeilijk vindbare fouten is de debugger een uiterst handig hulpmiddel bij het vinden van de fout. Het gebruik van de debugger van Qt is erg eenvoudig. Je kunt een of meerdere breakpoints plaatsen in je programma door vóór het regelnummer te klikken. Tijdens het uitvoeren stopt het programma op die regel, maar zonder de betreffende instructie te hebben uitgevoerd. Door op een bepaald breakpoint te klikken, verdwijnt dit breakpoint. Plaats een breakpoint op regel 21. Start nu het programma door te klikken op driehoek + kevertje (linksonder). We zien dat het programma inderdaad stopt op regel 21. Als we in het rechter venster 'this' uitvouwen, zien we dat iteller inderdaad nog de waarde 0 heeft. Voeren we één instructie uit door in de menubalk van het debugvenster (onderin) op 'Step Over' te klikken. Het verschil tussen 'Step Over' en 'Step Into' is dat bij de eerste een functie als één instructie wordt uitgevoerd en bij de tweede wordt binnen de functie de eerste instructie uitgevoerd. Dit heeft natuurlijk alleen effect als het programma op een functieaanroep is gestopt. We zien dat iteller nu 1 is geworden. Haal het breakpoint weg en plaats een nieuw breakpoint één regel lager (voor de ifinstructie). Klik op 'Continue' en vervolgens op de knop in de applicatie. Mocht de applicatie niet zichtbaar zijn, klik dan op 'Prog1' onder in de taakbalk. Het programma stopt nu op het volgende breakpoint, in ons geval voor de if-instructie en iteller is 1 geworden. Dit kan enkele malen worden herhaald. Ook kun je het breakpoint verwijderen en vervolgens op 'Continue' klikken. Nu stopt het programma niet meer, maar loopt door tot het einde. 12
In deze onderwijspublicatie is géén auteursrechtelijk beschermd werk opgenomen. 13