Qt Framework on monialustainen sovelluskehys, jolla voidaan tehdä työpöytä-, mobiili- ja sulautettujen järjestelmien sovelluksia. Qt Creator puolestaan on kehitysympäristö, jolla Qt-sovelluksia voidaan kirjoittaa ja rakentaa. Tässä oppaassa nimellä Qt tarkoitetaan Qt Frameworkia, ellei erikseen puhuta esimerkiksi Qt Creatorista. Qt Framework sisältää käyttöliittymäkomponenttien lisäksi kirjastoja esimerkiksi verkkoliikenteeseen, tietokantoihin ja säikeistykseen. Qt:n pääasiallinen ohjelmointikieli on C++.
Qt on saatavilla sekä kaupallisella että avoimen lähdekoodin lisenssillä. Tässä oppaassa perehdytään HTTP-protokollan käyttöön Qt-sovelluksissa.
Qt Widgets ja Qt Quick ovat Qt:n kaksi keskeistä käyttöliittymätekniikkaa. Niiden avulla voidaan luoda sovelluksen käyttäjälle näkyvä käyttöliittymä, kuten ikkunat, painikkeet, valikot ja muut käyttöliittymäelementit.
Tämän oppaan esimerkit toteutetaan Qt Widgets -tekniikalla.
Qt Creator on ns. IDE (Integrated Development Environment) eli integroitu kehitysympäristö. Koodin kirjoittamista nopeuttaa, kun opettelee käyttämään editorin pikanäppäimia (quick keys). Alla muutamia hyödyllisimpiä:
Qt:n asennusohjeet löytyvät sivulta https://peatutor.com/c_kieli/qt_asennus.php
Jotta gitin käyttö ryhmässä sujuisi helpommin, kannattaa määrittää build-kansio ohjeessa https://peatutor.com/c_kieli/qt_asennus.php#build annetun mallin mukaisesti.
Kun build kansio määritellään edellisen ohjeen mukaan, Qt-Creator luo projektikansion alle kansion build, joka on kannattaa kirjoittaa .gitignore tiedostoon.
Huom! QtCreator 18.0.0 ja uudemmissa versioissa projektiasetukset tallennetaan .qtcreator kansioon (aiemmin käytettiin CMakeLists.txt.user tiedostoa). Myös tämä kansio kannattaa lisätä .gitignore tiedostoon, koska se sisältää käyttäjäkohtaisia asetuksia.
Q_OBJECT on Qt-kirjaston olennainen makro, joka tarjoaa tärkeitä ominaisuuksia C++-luokille. Se on keskeisessä roolissa, kun luodaan luokkia, jotka hyödyntävät Qt:n signal- ja slot-mekanismia, dynaamista ominaisuusjärjestelmää sekä RTTI:tä (Run-Time Type Information).
Kun luokkaan lisätään Q_OBJECT-makro, se mahdollistaa seuraavat Qt:n tarjoamat ominaisuudet:
Q_OBJECT mahdollistaa signaalien ja slottien käytön, jotka ovat Qt:n oma tapa käsitellä tapahtumia ja kommunikointia objektien välillä.Q_OBJECT-makron avulla luokasta tulee metaobjekti, mikä mahdollistaa mm. luokan tyyppitietojen (kuten metodien ja ominaisuuksien) saamisen ajonaikana.
Alla on esimerkki Qt-luokasta, joka hyödyntää Q_OBJECT-makroa:
#include <QObject>
class MyClass : public QObject {
Q_OBJECT
public:
MyClass(QObject* parent = nullptr) : QObject(parent) {}
signals:
void mySignal();
public slots:
void mySlot();
};
Tässä esimerkissä Q_OBJECT-makro mahdollistaa MyClass-luokan käytön signaalien ja slottien kanssa.
On tärkeää muistaa, että jos luokka sisältää Q_OBJECT-makron, luokan tulee olla peritty QObject-luokasta, ja sen tulee myös olla erillisenä tiedostona (esim. .cpp tai .h), jotta moc (Meta-Object Compiler) voi käsitellä sen oikein.
qDebug() on Qt:n tarjoama makro, jolla voi tulostaa debug-viestejä konsoliin ohjelman kehityksen ja testauksen aikana. Se toimii samankaltaisesti kuin C++:n std::cout, mutta on integroitu Qt:n järjestelmään ja tukee Qt-tyyppejä (kuten QString, QByteArray jne.) suoraan.
Esimerkki qDebug():n käytöstä:
#include <QDebug>
int arvo = 42;
QString teksti = "Testi";
qDebug() << "Muuttujan arvo:" << arvo << "Teksti:" << teksti;
// Tulostaa: Muuttujan arvo: 42 Teksti: "Testi"
Huom! qDebug-viestit on syytä poistaa tai estää Release-versiosta, sillä ne:
CMakeLists.txt-tiedostossa voidaan määrittää, että Release-versiossa kaikki qDebug-viestit poistetaan automaattisesti käännösaikana. Tämä tapahtuu lisäämällä QT_NO_DEBUG_OUTPUT-määritelmä Release-buildia varten:
# CMakeLists.txt
# Debug-viestit pois Release-buildeista
target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<CONFIG:Release>:QT_NO_DEBUG_OUTPUT>
)
Kun QT_NO_DEBUG_OUTPUT on määritelty, kaikki qDebug()-kutsut eivät tuota mitään koodia, mikä tekee niistä täysin "ilmaisia" suoritusajan kannalta Release-versiossa.
Signaali–slot-mekanismi muistuttaa JavaScriptistä tuttuja callback-funktioita:
tietyn tapahtuman jälkeen suoritetaan siihen yhdistetty funktio. Qt:ssa tapahtumasta
ilmoittava signaali ja sen käsittelevä slot yhdistetään connect()-funktiolla.
Samaan signaaliin voidaan yhdistää useita slotteja.
Signaalit ja slotit ovat Qt:n tapa välittää tietoa olioiden välillä.
Olio lähettää signaalin, kun jokin tapahtuma tapahtuu tai sen tila muuttuu.
Signaali voidaan yhdistää connect()-funktiolla vastaanottavaan
funktioon eli slottiin.
Esimerkiksi QPushButton-painike lähettää
clicked()-signaalin, kun käyttäjä painaa sitä. Signaaliin
yhdistetty funktio suoritetaan tämän jälkeen.
connect(button, &QPushButton::clicked,
this, &MainWindow::handleButtonClick);
Signaalin lähettävän olion ei tarvitse tietää, mikä olio vastaanottaa signaalin. Tämä vähentää olioiden välisiä riippuvuuksia ja muistuttaa Observer-suunnittelumallia.
Qt-frameworkin oliomallin parent-ominaisuus tarjoaa tehokkaan ja helpon tavan hallita olioiden elinkaarta. Jokainen QObject-pohjainen olio voi saada toisen QObject-olion parentiksi. Systeemi toimii niin, että kun parent olio tuhotaan, myös sen lapsi oliot tuhotaan automaattisesti.
Parent-lapsi suhde muodostaa puumaisuuden:
Sovelluksessa on ikkunat Mainwindow ja LoginForm. MainWindow:sa on painike, jolla avataan LoginForm. Painikkeen clicked tapahtumaan on kirjoitettu koodi:
LoginForm *objectLoginForm = new LoginForm(this); objectLoginForm->show();Edellä this-osoittimella asetetaan LoginFormin parentiksi MainWindow-olio, jolloin objectLoginForm olion tuhoamisesta ei tarvitse itse huolehtia.
Parent-parametri on valinnainen (QWidget *parent = nullptr), mutta se kannattaa asettaa aina kun:
KRIITTINEN VIRHE: Älä koskaan aseta parenttia pino-oliolle (stack-allocated object)!
// VÄÄRIN - Aiheuttaa crash:n!
void MainWindow::someFunction() {
LoginForm objectLoginForm(this); // Pino-olio
objectLoginForm.show();
}
// Kun funktio päättyy, pino-olio tuhotaan automaattisesti
// Parent (this) yrittää tuhota saman olion myöhemmin -> crash!
// OIKEIN - Käytä aina new-operaattoria kun asetat parentin:
void MainWindow::someFunction() {
LoginForm *objectLoginForm = new LoginForm(this); // Heap-olio
objectLoginForm->show();
}
// Parent huolehtii tuhoamisesta
Huom!
Qt:ssa QObject-luokan perivistä luokista ei yleisesti suositella luotavan olioita älykkäillä pointtereilla (kuten std::unique_ptr tai std::shared_ptr), koska parent-lapsi systeemi hoitaa muistinhallinnan.
Qt:n MOC (Meta-Object Compiler) tiedosto on automaattisesti luotu tiedosto, joka syntyy Qt:n metaobjektijärjestelmän osana. MOC tiedosto käsittelee C++-luokkia, joissa on Qt:n tarjoamia laajennuksia, kuten signaalit, slotit ja parent-olio.
MOC:n päätehtävänä on tuottaa koodi, joka mahdollistaa Qt:n signals and slots -mekanismin, RTTI:n (Run-Time Type Information) ja dynaamiset propertyt. MOC analysoi C++-lähdekoodin ja tuottaa siitä lähdekooditiedoston, joka lisätään osaksi projektin käännösprosessia.
Tämä mekanismi on keskeinen osa Qt:n dynaamista toiminnallisuutta, ja sen ansiosta Qt voi tarjota kehittyneitä ominaisuuksia, kuten tapahtumakäsittelyä ja GUI-komponenttien kommunikointia.
Ohjelmistokehityksen maailmassa rakennusjärjestelmillä on keskeinen rooli prosessissa, jossa lähdekoodi muunnetaan suoritettaviksi ohjelmiksi. Kaksi suosittua C++-kehityksessä käytettyä rakennusjärjestelmää ovat QMAKE ja CMAKE.
QMAKE on rakennusjärjestelmätyökalu, joka on osa Qt-kehystä, ja sitä käytetään pääasiassa Qt-sovellusten rakentamiseen. Se yksinkertaistaa rakennusprosessia tuottamalla Makefile-tiedostoja projektitiedostojen (.pro-tiedostojen) perusteella. QMAKE automatisoi lähdetiedostojen, otsikkotiedostojen ja muiden resurssien käsittelyn, mikä helpottaa kehittäjiä projektien kääntämisessä eri alustoilla.
Yksi QMAKE:n vahvuuksista on sen tiivis integrointi Qt-kehykseen. Se tarjoaa ennalta määriteltyjä muuttujia ja funktioita, jotka on erityisesti suunniteltu Qt-kehitykseen, mikä mahdollistaa kehittäjille Qt:n ominaisuuksien, kuten käyttöliittymämuotojen ja käännösten, hallinnan helposti.
CMAKE on tehokas, avoimen lähdekoodin rakennusjärjestelmä, joka ei ole sidottu mihinkään tiettyyn kehykseen. Se on suunniteltu erittäin joustavaksi ja alustariippumattomaksi, ja se kykenee tuottamaan alkuperäisiä rakennustiedostoja laajalle valikoimalle käyttöjärjestelmiä ja kääntäjiä. CMAKE käyttää CMakeLists.txt-tiedostoja määritelläkseen rakennusmääritykset ja riippuvuudet.
CMAKE tunnetaan monipuolisuudestaan ja laajasta tuestaan erilaisille työkaluketjuille. Sitä voidaan käyttää projektien rakentamiseen, jotka tukeutuvat useisiin kirjastoihin ja kehyksiin, mikä tekee siitä suositun valinnan monimutkaisille projekteille, jotka vaativat monialustatukea. Lisäksi CMAKE tukee laajaa valikoimaa kolmannen osapuolen moduuleja ja laajennuksia, mikä parantaa sen ominaisuuksia entisestään.
Sekä QMAKE:lla että CMAKE:lla on omat vahvuutensa, ja ne sopivat erilaisiin käyttötapauksiin. QMAKE on ihanteellinen projekteille, jotka nojaavat vahvasti Qt-kehykseen, sillä se tarjoaa sujuvan rakennusprosessin Qt-spesifisten ominaisuuksien avulla. Toisaalta CMAKE on monipuolisempi ja sitä voidaan käyttää laajempaan valikoimaan projekteja, sillä se tarjoaa suurempaa joustavuutta ja tukea eri alustoille ja työkaluketjuille.
Kun valitaan QMAKE:n ja CMAKE:n välillä, kehittäjien tulisi harkita projektinsa erityisvaatimuksia, käyttämiään kehyksiä ja tarvetta monialustatukeen.
Nykyään CMakea suositellaan Qt-ohjelmoinnissa ensisijaiseksi rakennustyökaluksi qmaken sijaan. Tämä suuntaus on tullut selväksi Qt 6:n myötä, jossa CMake on virallinen ja ensisijaisesti tuettu rakennusjärjestelmä.