Esimerkki työsäikeen käytöstä sekä funktion lukitsemisesta atomiseksi. Käyttöliittymää on järkevää päivittää n. 30...500 ms:n välein, kun taas ohjattavaa prosessia joudutaan tapauksesta riippuen mittaamaan ja ohjaamaan tarvittaessa paljon useammin. Mekatronisilla laitteilla päivitysväli on tyypillisesti 1...20 ms. Käyttöliittymän päivittäminen saattaa joissain tapauksissa olla hyvinkin raskasta, jos päivitettävänä on paljon grafiikkaa. Tyypillisesti käyttöliittymään päivitetään tapahtumaperustaisesti, kun taas prosessia ohjataan kelloon perustuen. Jos prosessin ohjaus sisältää säätimiä esimerkiksi aikaan perustuvia integraattoreita, niin vakio jaksoaikaan perustuva ohjaus on monesti helpoin ratkaisu.
Tee alla oleva esimerkki seuraavasti (älä kopioi kaikkea alla olevaa, vaan tee ohjeen mukaan muuten menee suteen! ):
- Tee uusi Dialog perustainen projekti
- Tee luokka Saie tai Thread periyttämällä se QThread luokasta ja type information arvoksi QObject
- Tee dialogiin napit, jolla käynnistät ja pysäytät säikeen sekä tekstiruudun ja napin tiedon välittämiseksi säikeelle.
- Tee tekstiruutu, johon voi kirjoittaa säikeen laskurin arvoa.
- Tee dialogiin SLOT VastaanotaArvoSaikeelta(int)
- Tee saikeeseen SLOT jolla vastaanotat asetusarvon käyttöliittymästä.
- Tee run-funktio säikeeseen
- Lisää tarvittavat ajastimet Saie luokkaan.
Seuraavassa on esitetty käyttöliittymän yksinkertaisen toteutuksen lähdekoodi.
#include "dialogi.h" #include "ui_dialogi.h" #include <QMessageBox> Dialogi::Dialogi(QWidget *parent) : QDialog(parent), ui(new Ui::Dialogi) { ui->setupUi(this); //yhdistetaan signaalit ja slotit connect(this,SIGNAL(AsetaTestiarvo(int)),&saie,SLOT(AsetaTestiarvo(int))); connect(&saie,SIGNAL(PaivitaUi(int)),this,SLOT(VastaanotaArvoSaikeelta(int))); } Dialogi::~Dialogi() { //sammutetaan säie, jottei tule segmentation faultia if(saie.isRunning())//onko asynkrooninen säie käynnissä { saie.pysayta();//pysäytetään säie } delete ui; } void Dialogi::changeEvent(QEvent *e) { QDialog::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } //haetaan arvo ruudulta void Dialogi::on_SiirraTietoaTyosaikeelle_clicked() { QString apu=ui->tietosaikeelle->text(); int apu2=apu.toInt(); emit AsetaTestiarvo(apu2); } void Dialogi::on_StartSaie_clicked() { QMessageBox msgBox; if(saie.isRunning())//onko asynkrooninen säie käynnissä { msgBox.setText("Säie on jo käynnissä"); msgBox.exec(); } else { saie.start();//käynnistetään säie } } void Dialogi::on_StopSaie_clicked() { if(saie.isRunning())//onko asynkrooninen säie käynnissä { saie.pysayta();//pysäytetään säie } } void Dialogi::VastaanotaArvoSaikeelta(int arvo) { QString apu; apu.setNum(arvo); ui->lineEdit->setText(apu); }
Ja alla käyttöliittymän otsikkotiedosto.
#ifndef DIALOGI_H #define DIALOGI_H #include <QDialog> #include "saie.h" namespace Ui { class Dialogi; } class Dialogi : public QDialog { Q_OBJECT public: Dialogi(QWidget *parent = 0); ~Dialogi(); Saie saie; //bool started; protected: void changeEvent(QEvent *e); private: Ui::Dialogi *ui; private slots: void on_StopSaie_clicked(); void on_StartSaie_clicked(); void on_pushButton_clicked(); void VastaanotaArvoSaikeelta(int); signals: void AsetaTestiarvo(int); }; #endif // DIALOGI_H
Säikeistetyn ohjelman toteutus vaatii QThread luokasta periytetyn luokan. Tähän luokkaan täytyy tehdä jäsenfunktiona run-funktio, jonka abstrakti toteutus on QThread luokassa. run-funktio muodostaa oman säikeen (thread), jota "ajetaan" samanaikaisesti käyttöliittymäsäikeen kanssa. run--funktio emitoi signaalien välityksellä käyttöliittymälle infoa reaalimaailman tapahtumista.
#include "saie.h" #include <QDebug> Saie::Saie(QObject *parent) : QThread(parent) { Testiarvo=0; runTimer.start(); uiTimer.start(); SaieStop=false; jaksoaika=1; ui_paivitysaika=10; } // tällä saadaan saie pysaytettya void Saie::pysayta() { SaieStop=true; } void Saie::AsetaTestiarvo(int arvo) { //suojataan mutexilla sijoitus atomiseksi operaatioksi eli jos funktiossa on useimpia //rivejä suoritattavaa ohjelmaa ne kaikki toteutetaan ilman, että //toinen säie pääsisi väliin QMutexLocker locer(&mutex); Testiarvo=arvo; } void Saie::run() { //tässä syntyy toinen säie int arvo; QString msg; forever { if (runTimer.elapsed() > jaksoaika)//talla maarataan saikeen paivitysvali { runTimer.restart(); Testiarvo++; Testiarvo=Testiarvo+3; if(uiTimer.elapsed()>ui_paivitysaika)//talla maarataan kayttoliittyman paivitysvali { uiTimer.restart(); emit PaivitaUi(Testiarvo); msg.setNum(Testiarvo); qDebug()<< msg;//debug tuottaa informaatiota application output ikkunaan } } if(SaieStop==true){break;} } SaieStop=false; }
#ifndef SAIE_H #define SAIE_H #include <QThread> #include <QTime> #include <QMutex> class Saie : public QThread { Q_OBJECT public: explicit Saie(QObject *parent = 0); int Testiarvo; bool SaieStop; QTime runTimer; QTime uiTimer; int jaksoaika; int ui_paivitysaika; void pysayta(); QMutex mutex; protected: //! run function for own real-time thread void run(); signals: void PaivitaUi(int); public slots: void AsetaTestiarvo(int arvo); }; #endif // SAIE_H