Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Harjoitustehtävät Qt:n ja C++:n oppimiseen

Tutustu kurssin oppimateriaaliin, sieltä löytyy moneen tehtävään miltei valmis ratkaisu. Lisäksi Qt:n omat sivut ovat erittäin hyvä oppimislähde, niihin kannattaa tutustua. Näiden tehtävien yksi tärkeä tehtävä on opettaa opiskelijaa käyttämään Qt:n help-sivustoja, joten kaikki tehtävien yhteydessä olevat linkit kannattaa käydä läpi.

1. Tehtäväsarja

Note

Projektit kannattaa luoda virtuaalikoneessa C-levyn temp-hakemistoon ja kopioida lopuksi projektikansio Z-levylle (tai tikulle) turvaan!
Jos projekti luodaan suoraan Z-levylle, saa tulokseksi vain ongelmia.

...

, tiedostojen tallennus tai avaus saattaa epäonnistua.

Mikäli tehtävänannossa ei toisin mainita, tehtävistä palautetaan niiden lähdekoodi liitetiedostoina joko koko projekti zip-paketissa (klikkaa kakkosnäppäimellä/"oikeaklikkaa" kansiota -> Send to -> Compressed (zipped) folder), tai yksittäisinä tiedostoina.

Ensimmäisissä neljässä tehtävässä teemme vaiheittain QT Creatorin graaffisella editorilla

...

ikkunan, johon viemme kaksi widgetiä eli graaffista elementtiä.

  1. valitse:
    File Eli tee Qt Creatorilla dialogi-sovellus seuraavasti: valitse -> New file or project
    -> Qt Widget Project
    -> Qt Gui Application
    -> Anna projektin nimi ja tallennuskansio
    -> valitse QMainWindow tilalle QDialog ->aja loput oletusasetuksilla aja loput oletusasetuksilla läpi.

    Valitse vasemmalta alhaalta Debug build. Käännä ja testaa sovellus painikkeella F5, ohjelman pitäisi käynnistyä ja dialogin (tyhjän) ikkunan piirtyä ruudulle. Tutustu koodiin ja etsi sieltä pääohjelma, muodostin = constructor sekä hajotin = destructor.

    Nimeä em. funktiot koodiin, kopioi koodi ja lähetä se tuubin kautta tehtävän 1. vastauksena. Älä käytä liitetiedostoa, vaan maalaa koodi vastausikkunaan.
    ***********
     
  2. Laita

    break point

    breakpoint kaikkiin lähdekoodiin funktioihin aaltosulkujen väliin

    , laita

    ;

    - laita yksi break point pääohjelmaan

    ennen dialogin muodostinta ja yksi show funktion eteen. Break pointin voit asettaa

    (main.cpp) ennen MainWindow-tyyppisen muuttujan luontia

    - yksi muodostimeen setupUi-funktiokutsun eteen (mainwindow.cpp) ja

    - yksi destruktoriin, jälleen aaltosulkeiden sisäpuolelle.

    Breakpointin voit asettaa
    -viemällä hiiren kursorin rivinumeroinnin vasemmalle puolelle (hiiren kursori muuttuu) ja
    -klikkaamalla siellä olevaa tyhjää aluetta. Aja F5:llä kaikki break pointit läpi. Selvitä funktioiden ajojärjestys ja vastaa se tuubiin., jolloin punaisen palloikonin pitäisi ilmestyä

    Paina F5 näppäimistöltä tai hiirellä alempaa nuolta (jonka päällä on ötökän kuva) Qt Creatorin vasemmassa alareunassa. Hetken kuluttua (~10-20s) ensimmäisen asettamasi punaisen pallon kohdalle ilmestyy keltainen nuoli. Aja kaikki breakpointit läpi.

    Selvitä funktioiden ajojärjestys (=missä järjestyksessä asettamasi breakpointit suoritetaan) ja kirjoita tehtävän vastaukseksi (tehtävässä 1 annetut 3 nimeä funktioille, oikeassa järjestyksessä). Tehtävän vastaukseksi siis riittää kolme sanaa oikeassa järjestyksessä, suoraan tehtävän Teksti / Verkkoteksti -vastauskenttään kirjoitettuna.
     
  3. Avaa Qt Creator. Valitse:

    File-> New file or project
    -> Qt Widget Project
    -> Qt Gui Application
    -> Anna projektin nimi ja tallennuskansio
    -> aja loput oletusasetuksilla läpi.

    Avaa juuri luomasi projektin käyttöliittymäsuunnittelunäkymä Forms-kohdan alta, tiedosto mainwindow.ui . Laita ikkunaan liukupalkki (Horizontal tai Vertical Slider) ja "lcd"-näyttö QLCDNumber.

    Johda liukupalkin signaali (valueChanged) "lcd"-näytön slotiin (display) signal-slot tilassa (F4).

    Tee graafisella editorilla kytkennät "widgetien" välille. Testaa sovellus.

    Laita graafisessa editorissa edellisen liukupalkin alarajaksi -100 ja ylärajaksi 100. Jatka edellistä tehtävää seuraavasti. Laita dialogiin liukupalkki (Horizontal tai Vertical Slider) ja "lcd"-näyttö QLCDNumber. Johda liukupalkin signaali (valueChanged) "lcd"-näytön slotiin (display) signal-slot tilassa (F4). Tee graaffisella editorilla kytkennät "widgettien" välille. Testaa sovellus. Laita graafisessa editorissa edellisen liukupalkin alarajaksi -100 ja ylärajaksi 100. Testaa. Ota Snipping Toolilla kuva dialogista ikkunasta siten, että liitynnät näkyvät ja laita palauta se tuubiin.
     
  4. Vie edellisen tehtävän liukupalkin arvo edistymispalkin " QProgressBar " QLCDNumber-elementin sijaan QProgressBar-elementin arvoksi. Ota Snipping Toolilla kuva dialogista ikkunasta siten, että liitynnät näkyvät ja laita palauta se tuubiin.
     
  5. Tee uusi projekti tehtävälle  no 1 ja laite siihen edellä olevat widgetit. Kytke (kuten tehtävässä nro 1) ja lisää ikkunaan tehtävän 3 mukaiset widgetit.

    Kytke widgetit graafisen editorin sijaan toisiinsa connect-funktion avulla eli lisää Dialogin MainWindowin muodostimeen connect....  Alla esimerkki connect-funktion käytöstä:

    Code Block
    languagecppcollapsetrue
    //alla olevasta koodista löydät viisi ensimmäistä riviä valmiina Dialogimainwindow.cpp tiedostosta, lisää connect funktio niiden perään
    //kuten tässäkin on tehty
        DialogiMainWindow::DialogiMainWindow(QWidget *parent) :
          QDialogQMainWindow(parent),
          ui(new Ui::DialogiMainWindow)
        {
          ui->setupUi(this);//tämä on jo tehty puolestasi automaattisesti
          //yhdistetaan signaalit ja slotit, valitse komponentit valikosta ALÄ KOPIOI ettei mene suteen
          //ui-> tarkoittaa sitä, että komponentti on käyttöliittymässä ja ui-> on osoite käyttöliittymään
          connect(ui->horizontalSlider,SIGNAL(valueChanged(int)),ui->lcd,SLOT(display(int)));
          ....
    

    Palauta tehtävän lähdekoodi (mainwindow.cpp).

  6. Tee Dialog sovellus, jossa on Date Time Edit ja Calender Calendar -widgetit. Kytke ne yhteen siten, että muutettaessa kumpaa tahansa, muuttuu myös toisen asetus.
    a.Tee  graafisella editorilla signal - slot -liityntä (eli nuolilla) . (ota kuva liitynnöistä Snipping toolilla ja palauta se)
    b. Ota nuolet pois ja tee liityntä connect-funktiolla. Huom. lisää dialog(palauta lähdekoodi)

    Huom. Jos ohjelma ei muuten toimi, lisää mainwindow.h tiedostoon #include <QDate> muiden include komentojen alle.
     
  7. Tee Dialog sovellus, jossa Dial-valikon arvo menee lcd-näyttöön.
    a.Tee  Tee graafisella editorilla signal - slot -liityntä (eli nuolilla) ja testaa, ota kuva liitynnöistä ja lähetä vastaus tuubillapalauta se
    b Tee liityntä connect-funktiolla. (palauta lähdekoodi)
     
  8. Tässä tehtävässä viet haluamasi tekstin labeliin eli tekstinäyttöön ruudulla, kun painat dialogissa ikkunassa olevaa nappulaa. Eli tee dialogiikkuna, johon luot graafisessa editorissa  painonapin ja labelin. Nimeä nappi ja label siten, että muistat niiden merkityksen.

    Tee mainwindow.h-tiedostoon (Headers-otsikon alla) oma slotin esittely seuraavasti eli avaa Dialog allaolevan esimerkin mukaisesti. Avaa mainwindow.h ja aaltosulkeiden sisälle ennen lopettavaa sulkua lisää alla oleva teksti. Jos ei muistu mieleen slotit ja signaalit, niin katso täältä: ohjeita signaaleista ja sloteista

    (klikkaa 'expand source' jos koodi alla ei näy)

    Code Block
    languagecpp
    collapsetrue
    ...
    public slots:
        void nayta();
    ...
    

    Tee slotin uusi slot toteutustiedostoon Dialogmainwindow.cpp. Lisää tiedostoon funktio, joka näyttää tekstin label ruudulla.

    Code Block
    languagecpp
    collapsetrue
    //tämän lisäät Dialogmainwindow.cpp tiedoston loppuun
    void DialogMainWindow::nayta()
    {
        //laita ui-> jos olet Designer-näkymässä tehnyt labelin dialogiinMainWindowiin
        QString Tekstia;//tämä on muuttuja joka tallettaa merkkijonoja
        Tekstia="juttuja";
        ui->label->setText(Tekstia);//tällä laitat tekstin labeliin
    }
    

    Liitä napin signaali omaan slotiisi. Tee tämä dialogin MainWindowin muodostimessa (nayta-niminen slot on nyt DialogissaMainWindowissa).

    Code Block
    languagecpp
    collapsetrue
    ui->setupUi(this);//tämä on jo tullut automaattisesti
    //yhdistetaan signaalit ja slotit, valitse komponentit valikosta ÄLÄ KOPIOI ettei mene suteen
    //this alla tarkoittaa, että vastaanottaja on tämän tiedoston (luokan) slot
    connect(ui->Nappi,SIGNAL(clicked()),this,SLOT(nayta()));
    
  9. Tässä tehtävässä on tarkoituksena tehdä sekuntikello, jonka voi käynnistää, laittaa tauolle, käynnistää uudelleen ja pysäyttää nappien avulla. Kello toteutetaan QTimer-luokan avulla (tutustu QTimer linkin alta löytyvään tekstiin!!!). Tee dialogiin ikkunaan painikkeet start, pause, jatka, stop ja nollaus. Tee painikkeille slotit (go to slot) sekä vielä lcd-näyttö kellon arvoa varten. Tee QTimer timer; muuttuja Dialogmainwindow.h tiedostoon alla olevan esimerkin mukaisesti.

    Code Block
    languagecpp
    collapsetrue
    #include <QDialog><QMainWindow> // tämä on jo tehty puolestasi eli lisää alla oleva rivi
    #include <QTimer>  //lisää tämä, tähänhän jo tutustuit QTimer helpissä
    
        ... lisää seuraavat muuttujat luokan esittelyyn DialogMainWindow.h tiedostossa
        ~Dialog~MainWindow();// tämä on jo tehty puolestasi automaattisesti, lisää alla olevat muuttujat
        QTimer timer;//ajastin ajan laskentaa varten, tämä rivi sinun täytyy lisätä
        int laskuri;//laskuri joka laskee ajastimen timeout eli kertoja jotka ajastin on käynyt loppuun
        ...
        private slots:
        void on_StopTimer_clicked();//tämä tuli automaattiseti kun teit Stop painikkeen ja sille slotin "go to slot" määrittelyllä
        void on_StartTimer_clicked();//ja tuli kanssa samalla tavoin
        void PaivitaNaytto();//tämän joudut tekemään itse ja vastaavan funktion DialogMainWindow.cpp tiedostoon
    };
    

    Nyt timer on muuttuja, joka sisältää ajastimen. Esimerkiksi timer.start(1000) käynnistää ajastimen yhden sekunnin resoluutiolla. Ajastimen timeout signaalin joudut kiinnittämään itse tekemääsi slotiin PavitaNaytto PaivitaNaytto seuraavasti Dialogin MainWindowin muodostimessa, muistathan että muodostin on se funktio, jossa luokan nimi on sama kuin funktion nimi.

    Code Block
    languagecpp
    ui->setupUi(this);//tämä on jo DialoginMainWindowin muodotimessa
    //tämän liitynnän ajastimen timeout signaalista ja  PaivitaNaytto
    //& merkki tarkoitaa siirtymistä muuttujasta osoitteeseen
    //eli timer on muuttuja &timer on muuttujan osoite
    connect(&timer,SIGNAL(timeout()),this,SLOT(PaivitaNaytto()));
    laskuri=0; 

    Ja sitten vielä funktioiden sisällöt Dialogmainwindow.cpp tiedostoon. Muistathan, että teit funktioiden rungot "go to slot" -mekanismilla.

    Code Block
    collapsetrue
    void DialogMainWindow::on_StartTimer_clicked()//tämän olet tehnyt "go to slot" määrittelyllä
    {
        timer.start(100);
        laskuri=0;
    }
    
    void DialogMainWindow::on_StopTimer_clicked()//tämän olet tehnyt "go to slot" määrittelyllä
    {
        timer.stop();
    
     }
    //ja tähän ilmestyy loppujen painikkeiden slotit, kun teet ne "go to slot" määrittelyllä
    
    //ajetaan läpi timeoutista eli tämän teet itse
    void DialogMainWindow::PaivitaNaytto()
    {
        laskuri++;
        ui->lcdNumber->display(laskuri);
    }
    

    Lisää vielä sisällöt funktioille Jatka ja Pause sekä tee kaikki tarvittavat funktiot ja testaa ohjelma.
     

  10. Muuta edellisen tehtävän kelloa siten, että tarkkuus on 0.01 sekuntia. Eli joudut muuttamaan timerin start-asetusta. kts. QTimer help. Sijoita laskuri double tyypin muuttujaan

    Code Block
    languagecpp
    double apu=laskuri;

    ja jaa apu 100:lla (kokeile lukuja 100 ja 100.0, miten ne eroaa?) sekä sijoita arvo lcd-näyttöön.
     

  11. Muuta edellisen tehtävää siten, että nyt on vain painonapit start/pause, stop ja nollaus. Eli muuta start/pause-napin tekstiä sen mukaan onko ajastin tilassa käynnissä/pysäytettynä. Huom! tarvitset luokkaan boolean-muuttujan (tosi/epätosi), jossa pidät yllä laskurin tilaa.

    Code Block
    languagecpp
    collapsetrue
    void DialogMainWindow::on_StartTimer_clicked()
    {
        QString TekstiPainikkeessa;
        if(FlipFlopstarted==false)//FlipFlopstarted on bool tyypin muuttuja (tosi/epätosi), joka on esiteltävä luokkamäärittelyssä DialogMainWindow.h  bool FlipFlopstarted;
        {
            timer.start(100);
            laskuristarted=0true; //esittele tämä rivimuuttuja kannattaneeMainWindow.h siirtäätiedostossa muodostimeen,bool josstarted; et muista mikä on muodostin niin tarkasta tehtävästä kaksija aseta
            FlipFlop=true; //esittele tämä muuttuja Dialog.h tiedostossa bool FlipFlop; ja aseta
            //FlipFlop asetetaan //runningasetetaan tilaan false DialoginMainWindowin muodostimessa (muodostin kts teht. 2)
            TekstiPainikkeessa="Pause";
            ui->StartTimer->setText(TekstiPainikkeessa);
        }
        else
        {
            timer.stop();
            FlipFlopstarted=false;
            TekstiPainikkeessa="Start";
            ui->StartTimer->setText(TekstiPainikkeessa);
        }
    }
    
  12. Muuta edellistä tehtävää siten, että voit muuttaa kellon tarkkuutta vaikka (desimaalien lukumäärää) spinBox "widgetillä". Muistat tietysti määritellä spinBoxin graafisessa käyttöliittymässä.:
    - Näytetään vain sekunnit (aikaresoluutio: timer laukeaa 1000 ms välein)
    - Näytetään sekunnit ja kymmenesosat (aikaresoluutio: 100 ms)
    - Näytetään sekunnit ja sadasosat (aikaresoluutio: 10 ms)

    Muistat tietysti lisätä spinBoxin graafiseen käyttöliittymään.

    Code Block
    Code Block
    languagecpp
    collapsetrue
    void DialogMainWindow::on_StartTimer_clicked()
    {
        ui->spinBox->setMinimum(0);
        ui->spinBox->setMaximum(2);
        int desimaalit = ui->spinBox->value();
        if(desimaalit==0) aikaResoluutio=1000;//esittele aikaresoluutio .h tiedostossa
        if(desimaalit==1) aikaResoluutio=100;
        if(desimaalit==2) aikaResoluutio=10;
    
        timer.start(aikaResoluutio);
        laskuri=0;
    ....
    

    Lisäksi joudut asettamaan laskurin päivitysfunktioon seuraavanlaisen ehdon, jolla asetat laskurin lisäykseksi 1, jos aikaresoluutio on 1000 ja 0.1 jos aikaresoluutio on 100 jne..

    Jos aikaresoluutio on 1000, lisäät laskurin arvoa jokaisella päivityksellä 1:llä
    Jos aikaresoluutio on 100, lisäät laskurin arvoa jokaisella päivityksellä 0.1:llä
    jne.

     

  13. Muuta edellistä tehtävää: Tässä tehtävässä opetellaan muuttujan ja osoittimen eroa. Joku fiksu jo varmaan ihmetteli, että miksi ajastin esiteltiin connect funktiossa näin connect(&timer....

    Tämä johtuu siitä, connect vaatii lähteen ja kohteen osoitteen sulkujen sisälle. Muuttujan osoite saadaan & merkillä, mutta voimme esitellä (luoda) muuttujan jo valmiiksi osoitteena. Tällöin ohjelman syntaksi muuttuu ja timer muuttujaa ei enää käytetäkään esimerkiksi timer.start(100); vaan timer->start(100). Tässä tapauksessa timer muuttujalle täytyy myös varata muistia ja esittely tehdään toisella tavoin .header-tiedostossa:

    Code Block
    languagecpp
    collapsetrue
    class Dialog MainWindow: public QDialogQMainWindow {
        Q_OBJECT
    public:
        DialogMainWindow(QWidget *parent = 0);
        ~Dialog~MainWindow();
        //kun timer muuttujan eteen laitetaan * se on osoitin eikä muuttuja
        QTimer *timer;
        //laskuri joka laskee ajastimen timeout eli kertoja jotka ajastin on käynyt loppuun
        int laskuri;
    

    Lisätietoja esim. http://edu.phkk.fi/opiskelu/cppohjtekn/14_Osoittimet.htm ja muistinvaraus 

    Lisäksi joudutaan tekemään Dialogmainwindow.cpp tiedostossa muodostimeen lisäys muistinvarausta varten.

    Code Block
    languagecpp
    collapsetrue
    DialogMainWindow::DialogMainWindow(QWidget *parent) :
        QDialogQMainWindow(parent),
        ui(new Ui::DialogMainWindow)
    {
        ui->setupUi(this);
        timer = new QTimer(this);// nyt muisti on varattu ja timer osoitin on käytettävissä
        //eli nyt voidaan & merkki ottaa timer muuttujan edestä pois
        //this on muuten myös  osoitin, kuten huomaat senkään edessä ei ole & merkkiä
        connect(timer,SIGNAL(timeout()),this,SLOT(PaivitaNaytto()));
        FlipFlopstarted=false;
    }
    

    Lisäksi joudut käymään läpi kaikki kohdat, joissa olet käyttänyt timer muuttujaa ja muuttamaan pisteet "nuoliksi" ("->").
     

  14. a.Tee ohjelma, joka sisältää painonapin "PushButton" ja viesti-ikkunan "MessageBox". jonka käyttöliittymään lisäät PushButton-painonapin.
    Kun painetaan nappia, ohjelma näyttää viestiruudulla "MessageBox" MessageBox -viesti-ikkunassa viestin "nappia painettu".

    b. Vie Lisää ikkunaan uusi nappi ja näytä edellisen tehtävän teksti QLabel ikkunaanMessageBoxin sijaan QLabel-widgetissä, jonka myös lisäät.
    c. Vie edellisen tehtävän teksti QLineEdit ikkunaan d.  Lisää ikkunaan uusi nappi ja näytä teksti QLineEdit-widgetissä, jonka myös lisäät. 

    Tutustu Qt:n QDebug luokkaan ja tulosta sen avulla edellisen tehtävän sama teksti vielä komentoikkunaan (Application output). Ks. QDebug-luokan dokumentaatiosta qDebug() -funktion käyttöohje.

    Palauta tehtävän vastauksena ohjelman lähdekoodi.
    Image Added
     
  15. a. Tee ohjelma, joka ilmoittaa millä välillä annettu luku on. Laita annettu arvo lineEdit "ikkunaan" ja nappia "pushButton" painettaessa testaa if-lauseella arvoalue. Aseta "RadioButton"in  (yhden kolmesta) arvo osoittamaan arvoväliä Lisää yksi QPushButton-painonappi (tekstiksi "määritä arvoalue"), yksi QLineEdit ja kolme QRadioButton radiopainiketta, joiden arvot ovat 0...10, 11...100 tai ja >100. b. Tee edellinen tehtävä siten, että painonapilla vaihdat tekstikentän  validaattoria "lennosta" ja laitat samalla vastaavan radioButtonin osoittamaan sallittua lukualuetta.Aseta QRadioButtonien enabled-ominaisuus pois päältä; tällöin käyttäjä ei voi suoraan klikata radiopainikkeita.
    Lue käyttäjän syöttämä arvo lineEdit-widgetistä, pushButton-nappia painettaessa testaa if-lauseella arvoalue, ja valitse sen mukaisesti jokin kolmesta radiopainikkeesta.

    b. Aseta QRadioButtonien enabled-arvo nyt takaisin päälle ja poista QPushButton. Vaihda tekstikentän  validaattoria valitun QRadioButtonin arvon mukaiseksi, kun radiopainiketta painetaan.
     
  16. Lisää kolme Radiobuttonia uuteen ikkunaan/dialogiin. Lisää tekstiruutu (label), jossa kerrot välittömästi radiopainiketta painettaessa, Tee Radio button valikko kolmella painikkeella dialogiin. Lisää teksiruutu, jossa kerrot mitä painikkeista on painettu.
     
  17. Tee laskin, jossa on oikealla plus-, miinus-, kerto- ja jakopainikkeet.
    Vasemmalla on kolme

    teksti-ikkunaa, kahden ylimmän ruudun

    tekstikenttää (QLineEdit).
    Kahden ylimmän kentän perusteella laskettu tulos laitetaan alimpaan ruutuun riippuen painettavasta napista.

    Laskin
    [tekstikenttä]+
    [tekstikenttä]-
     *
    [tekstikenttä tulos]/

    Lisää tekstikenttiin validaattorit, ettei numerokenttiin voi syöttää kirjaimia kts. mallia esimerkistä double validator.

    Katso myös tehtävä 28, joka on jatkoa tälle tehtävälle.

     

  18. Tee säännöllisiä lauseita lausekkeita (regular expression) käyttäen syöttruutusyöttöruutu, johon voi syöttää vain "Excel" solun arvoja, joissa -tyyppisiä solun tunnisteita. Näissä on on yksi kirjain ja numero välillä 1..99 eli esim A1, a1, mutta ei esimerkiksi 1a. QRegExpValidator
     
  19. Tee dialogiohjelma, jossa on progress bar, joka etenee ajastimen tahdissa. Progress barin voi käynnistää, nollata ja pysäyttää painikkeilla. Laita ajastimen jaksoajaksi 0,5 sekuntia. Näytä arvo myös "lcd"-näytöllä.
     
  20. Tee graafisella editorilla dialogi, johon sijoitat kuusi haluamaasi widgettiä (käyttöliittymäelementtiä designerissä), joista ainakin yksi on nappi, ja erottele rinnakkaiset widgetit spacerilla spacer-elementillä toisistaan. Ryhmittele widgetit kolmeen allekkaiseen ryhmään kolmella horisonttaalisella layoutilla.
     
  21. Jatka edellistä tehtävää siten, että teet groupboxin rinnakkaisten widgettien ympärille ja ylimmän ryhmän napilla kätket alemmat widget-ryhmät.
     
  22. Tee edellisen tehtävän dialogi ohjelmallisesti. kts ohjeita
     
  23. Tee dialogi esimerkki, jossa pino on aliluokkanadialogiesimerkki, jossa käytät Qt:n QStack-luokkaa QDialog-olion jäsenmuuttujana. Tee nappula "Laita pinoon" ja "Ota pinosta".
     
  24. Tee mallin 1. tilakone switch-case rakenteella mukainen tilakone ja lisää siihen kaksi askelta. Keksi jokin tapa näyttää askeleet.
     
  25. Tee tehtävä 23 QStack template-luokkaa käyttäen.
     
  26. Tee dialogi, jolla voit laittaa ja katsella lukuja vektorissa/listassa, käytä QVector objektia-luokkaa.
     
  27. Tee dialogi, jolla voit laittaa ja katsella lukuja vektorissa/listassa, käytä QList objektia.

*********2. sarja tehtäviä *************

...

Tee luokkaan Anturi signaali LahetaMitattuArvo(double) ja ota se vastaan käyttöliittymässä.

ui

Dialog

 

Anturi

MittaaTulo->

->Mittaa

 

 

 

MittaaTulonArvo->

 

->PaivitaTulo

 

ArvonNaytto<-

 

<- LahetaMitattuArvo(double)

...

Tässä tehtävässä toteutamme asynkronisen säikeen, joka lukee anturia ja lähettää signaalilla tiedon käyttöliittymälle. Tee Kone luokkaan run-funktio ja  siihen forever toistorakenne. Katso mallia säikeistetystä ohjelmasta.\

Code Block
languagecpp
collapsetrue
//tämä luokan esittelyyn
QTime displayUpdateTimer;
Code Block
collapsetrue
forever
  {
        //Tähän anturin tiedon laskenta
          if (NayttoAjastin.elapsed() > Paivitysaika)
          {
              Nayttoajastin.restart();
              emit LahetaAnturinTiedot(Arvo);
          }
}

...

Kokeile tehdä samainen taulu seuraavalla sql-komennolla:

Code Block
languageSQL
collapsetrue
CREATE TABLE sylinteri
(
    id INTEGER PRIMARY KEY NOT NULL,
    nimi TEXT,
    isku REAL,
    vahvistus REAL
);

...

 Tee Sqlite tietokanta, joka sisältää taulut sensori  kentillä sensor_id, vahvistus ja offset  sekä kalibrointi kentillä kalibrointi_id, sensor_id, Todellinen, Mitattu ja vielä vierasavain tauluun sensori. Vähän mallia

Code Block
languageSQL
collapsetrue
FOREIGN KEY(sensor_id) REFERENCES sensor(sensor_id)

Täytä tauluihin kahden anturin tiedot ja testaa tiesotjen hakua sql-komennoilla, esimeriksi

Code Block
languageSQL
collapsetrue
SELECT * FROM kalibrointi WHERE sensor_id=1;

...

Tee ohjelma, joka lukee sensorien määrän sensoritaulusta ja muodostaa tarvittavan määrän sensoreita.

Code Block
languagecpp
collapsetrue
//luodaan sensori osoittimena Dilogi luokkaan
QVector <Sensori> sensori; //luodaan vektori, joka sisältää sensoreja

// esitellään dialog.h tiedostossa esimerkiksi näin
Sensori *sensori;


luetaan Dialogi.cpp :ssa sensorien määrä tietokannasta

 ja sitten luodaan new komennolla anturit

 sensori=new Sensori[maara];

...

Tässä tehtävässä tehdään säätöpiiri. Säätöpiirin tehtävänä on säätää sylinterille menevää ohjetta siten, että haluttu asema saavutetaan mahdollisimman hyvin. Jotta tehtävä olisi mielekäs, täytyy asema-anturin eli sylinterin oloarvon ja sylinterin halutun aseman eli sylinterin asetusarvon välistä suhdetta simuloida. Toteutetaan simulaattori tässä tehtävässä  mahdollisen yksinkertaisesti seuraavan ajatuksen mukaisesti: jos ohje on suurempi kuin todellinen asema, lisätään todellista asemaa kellolla (QTimer timer muuttujan timeoutilla)

Code Block
languagecpp
collapsetrue
if(ohjesylinterille>olo)olo++;
if(ohjesylinterille<olo)olo--;

Säätöpiiri voidaan tehdä yksinkertaisesti vähennys-ja kertolaskuilla seuraavasti:

Code Block
languagecpp
collapsetrue
ohjesylinterille = (asetus-olo)*vahvistus;

Tehdään tehtävä aluksi ainoastaan hyödyntäen Dialog luokkaa. Eli tehdän Dialog luokkaan slider, jolla annetaan ohjearvoja ja lisäksi edistymispalkki johon laitetaan sylinterin todellinen asema. Ohjelmoi lisäksi tarvittava ajastin, jotta saat oloarvon muuttumaan edellä kuvatulla tavalla. Testaa millä vahvistuksen arvolla saat järjestelmän toimimaan (tee ohjelmaan editline ja sille validator, jolla rajataan vahvistuksen arvot välille 0...1000).

...

Muuta edellisen tehtävän P-säädin PI-säätimeksi. 

Code Block
languagecpp
collapsetrue
//lisätään summaan eroarvo, muista nollata summa muodostimessa
 IntegraattorinSumma+=(asetus-oloarvo);
 //ja sitten säädin
 ohjearvosylinterille=(asetus-oloarvo)*P_vahvistus+ IntegraattorinSumma*I_vahvistus;

Viritä säädin Ziegler Nichols'in säännöillä.

...

  1. -luokkaa.

  2. Refaktoroi (muokkaa) tehtävän 17 ratkaisua siten, että jokaisen laskutoimituksen tekemisen yhteydessä:
    - ohjelma käyttää noudaLuvut-jäsenfunktiota tulosten hakemiseen käyttöliittymästä jäsenmuuttujiin luku1 ja luku2. Varmista että funktio ja jäsenmuuttujat ovat mainwindow.h:ssa esiteltyinä.
    - ohjelma käyttää showTulos-jäsenfunktiota tulosten näyttämiseen käyttöliittymässä

    void MainWindow::noudaLuvut()

    {
        luku1=ui->lineEdit->text().toDouble();
        luku2=ui->lineEdit_2->text().toDouble();
    }
    void MainWindow::showTulos(double tulos)
    {
        QString tulosteksti=QString::number(tulos);
        ui->lineEdit_3->setText(tulosteksti);
    }

     

     

1.5 PC osana mittausjärjestelmää-tehtävät

Tilakonekaaviot (state chart, state machine diagram) voi piirtää esim https://www.draw.io/ tai Astahilla (ilmainen Community tai Professional) http://astah.net/download. Astah Professional löytyy Eclipse-virtuaalikoneesta koulun koneilla.

 

  1.  1. Mallinna liikennevalot UML-tilakonekaaviona esim käyttäen kaavionpiirto-ohjelmaa. Voit olettaa, että tilakoneeseen tulee tapahtuma "30 sekuntia kulunut"  jolloin valoja voi vaihtaa. Voit halutessasi mallintaa myös liikennevalojen välitilat joissa keltainen valo on päällä. http://en.wikipedia.org/wiki/UML_state_machine
  2.  2. Toteuta tilakoneesi tehtävästä 1 switch-case rakenteella tai if-lauseilla. Voit käyttää koodin kirjoittamiseen esimerkiksi http://ideone.com/ -ympäristöä. Kieleksi voi valita esim. C++11
    Esimerkki tällä tavalla toteutetusta tilakoneesta löytyy sivulta Yksinkertainen Caps Lock -tilakonemalli tai osoitteesta http://en.wikipedia.org/wiki/Event-driven_finite-state_machine
  3.  3. Tee Qt Creatorissa uusi käyttöliittymäsovellus. Muunna tehtävän 2 tilakone Qt-sovellukseksi.
    - Kopioi C++-sovelluksesi tapahtumankäsittelijäfunktio mainwindow.cpp:hen MainWindown alaiseksi funktioksi.
    - Kopioi enum-määrittelysi (tilat ja tapahtumat) mainwindow.h -tiedostoon ennen class MainWindow : public QMainWindow riviä eli varsinaisen luokkamäärittelyn ulkopuolelle
    - Lisää käyttöliittymään textbrowser-ikkuna ja tulosta tilakoneen tulosteet seuraavalla addText -funktiolla cout-käskyjen sijaan. (Lisää allaoleva funktio omaan sovellukseesi, lisää funktion esittely .h -tiedostoon,  ja mukauta tarvittaessa textBrowser-elementin nimi, jos olet muuttanut sitä)

    void MainWindow::addText(QString text) {
     QString currentText=ui->textBrowser->toPlainText();
     QString newText=currentText+"\n"+text;
     ui->textBrowser->setText(newText);

    }
    - Kopioi vanhan main-funktiosi sisältö MainWindow::MainWindow(QWidget *parent) -konstruktoriin.

  4.  4. Lisää tehtävän 3 tilakoneeseesi QTimer-ajastin joka kutsuu ("tikkaa") tilakonetta säännöllisin väliajoin, esim. sekunnin välein. ks. QTimerillä toimiva Caps Lock -esimerkki. Tapahtumia on mahdollista käsitellä joka kerralla, kun tapahtumankäsittelijä käynnistyy, mikäli tapahtuma-jäsenmuuttujaan on asetettu tapahtuma.
    QTimerin käyttö edellyttää muutoksia ohjelmaan:

    - QTimer-olio lähettää signaaleja, joilla ei ole parametreja. Tämän takia tilakoneen nykyinen tila pitää välittää tapahtumankäsittelijälle toisella tapaa: Esittele tilakoneen tilaa ilmaiseva muuttuja, entisen main-funktion sijaan, mainwindow.h:ssa luokan jäsenmuuttujana. (Caps Lock -esimerkissä CapsState -tyyppinen muuttuja) Tällöin se on saatavilla kaikille luokan funktioille, ja sen arvo säilyy koko ohjelman suorituksen ajan.

    - QTimer osaa kutsua vain slot-funktioita, joten lisää mainwindow.h:hon private slots: otsikko, ja siirrä tapahtumankäsittelijän esittely sinne.

    - Myös tapahtumat välitetään tässä mallissa tilakoneelle jäsenmuuttujan avulla. (Caps Lock -esimerkissä Event-tyyppinen muuttuja)

    - Lisää enumeraatioon, jossa mahdollisten tapahtumien valikoima on määritelty, erityinen NO_EVENT -arvo, ja tilakoneeseesi tälle tapahtumalle käsittelijä, joka ei tee mitään.

    - Lisää tilakoneesi kaikkien muiden tapahtumien käsittelyihin tapahtuma-jäsenmuuttujan nollaaminen NO_EVENT -arvoon.

    - Lisää #include <QTimer> mainwindow.cpp:n alkuun jotta voit käyttää QTimer-luokkaa

    - Tee MainWindow-luokan konstruktorissa/rakentimessa QTimer-olio, joka kutsuu kasitteleTapatuma()-slotia sekunnin välein

    - Nyt voit muuttaa tilakoneen tilaa muuttamalla tapahtuma-jäsenmuuttujan arvoa ohjelman ollessa käynnissä.
    Lisää jokaista tapahtumaasi kohti yksi tapahtumanlaukaisijafunktio (slot) mainwindow.cpp:hen (ja sen esittely mainwindow.h:hon private slots: otsikon alle). Nimeä funktio kunkin tapahtuman mukaan:

    void MainWindow::keyApressed(){
        currentEvent=KEY_A_PRESSED;
    }

    Voit tämän jälkeen muuttaa tilaa esimerkiksi ajastamalla QTimer::singleShot -funktion kutsumaan ylläolevanlaista funktiota (slotia)
     QTimer::singleShot(3000,this,SLOT(keyApressed()));

    Kutsu kaikkia tapahtumanlaukaisijoitasi ajastetusti MainWindow::MainWindow-konstruktorista singleShot-kutsujen avulla. Huomaa, että singleShot laukeaa millisekunteina sen kutsumahetkestä, eli seuraava laukaisee tapthtumanlaukaisijoita sekunneilla 3,4,5,6 ja 7 koko sovelluksen käynnistyksestä:

     QTimer::singleShot(3000,this,SLOT(keyApressed()));
     QTimer::singleShot(4000,this,SLOT(capsLockPressed()));
     QTimer::singleShot(5000,this,SLOT(keyApressed()));
     QTimer::singleShot(6000,this,SLOT(capsLockPressed()));
     QTimer::singleShot(7000,this,SLOT(keyApressed()));


    Vaihtoehtoisesti voit toki lisätä sovellukseen jokaista tapahtumaa kohti painikkeita, joilla käyttäjä voi vapaasti laukoa tapahtumia silloin, kun haluaa. (Kytke tällöin painikkeiden clicked-signaalit omiin tapahtumanlaukaisijafunktioihisi, singleShotin käyttämisen sijaan.)



  5.  5. Mallinna kerrostalon hissi UML-tilakonekaaviona esim käyttäen kaavionpiirto-ohjelmaa. Sisällytä toiminnallisuuteen ainakin perustoiminnallisuus eli esim. kaikkien kerrosten painikkeet ja näytöt, jotka kertovat hissin tilasta.

 

  1.  6. (Valinnainen lisätehtävä) Lisää hissitilakoneeseen myös ovien sensorit ja ajastin oven sulkeutumiselle, sekä mahdollisuus painaa useamman kerroksen painikkeita siten, että hissi muistaa mennä niihin kaikkiin yksi kerrallaan.

  2.  7. Toteuta hissitilakone switch-case rakenteella.
    Esimerkki tällä tavalla toteutetusta tilakoneesta löytyy sivulta Yksinkertainen Caps Lock -tilakonemalli tai osoitteesta http://en.wikipedia.org/wiki/Event-driven_finite-state_machine
  3.  8. Tee Qt Creatorissa uusi ajastettu käyttöliittymäsovellus, joka simuloi hissin toimintaa. Lisää käyttöliittymään (Qt:ssa Forms:in alla mainwindow.ui tai dialog.ui) painikkeina kaikki tilakoneen tapahtumat, esim. kaikkien kerrosten nappula, ja jos tilakoneessasi voi nappia painaa myös hissin sisällä, lisää myös nämä nappulat. Lisää tämän lisäksi tekstikenttä kuten tehtävässä 4, joka näyttää tilakoneen tilan jokaisella hetkellä.
  4.  9. Toteuta aikaisemman tehtävän liikennevalot Qt:n QStateMachine:na. Esimerkki Qt:n tilakoneen käytöstä löytyy sivulta Erittäin yksinkertaiset liikennevalot Qt-tilakoneena. Kytke esimerkkiin QTimer-ajastin, joka pyörittää liikennevaloja painikkeen sijaan.
  5.  10. Toteuta aikaisemman tehtävän hissi Qt:n QStateMachine:na.
  6.  11. Toteuta Qt:n QStateMachinella liikennevalo-tilakone, jossa on mallinnettuna sekä yksittäisen liikennevalon, että koko risteyksen tilanne. Tarvitset tähän alitilakoneita.

 

2. Tehtäväsarja

  1. Tee Qt:llä ohjelma, jossa lasket yhteen kaksi kahdeksanbittistä etumerkillistä lukua, käytä tietotyyppiä qint8. Tee tarkastukset, etteivät luvut pääse  "vuotamaan yli" ja estä ylivuoto saturoimalla luvut tarvittaessa ala-ja ylärajalle.

    Tee Qt:lla syöttöruudut, laske painikkeella ja tulosta uuteen tekstiruutuun. Käytä validatoria Validator, double int jne. luettavien arvojen hyväksyntään.
     
  2. Tee Qt:llä ohjelma, jossa teet JA-, TAI- Ehdoton TAI,  JA-EI, TAI-EI operaatiot kahden bool-tyyppisen luvunvälillä.
     
  3. Tee edellinen tehtävä Qt:n tyyppillä quint8 .
     
  4. Tee ohjelma, joka nollaa bitit 4 ja 5  antamastasi  8 bittisestä luvusta (kokeile tyyppejä quint8, qint8 ja unsigned char) ja tulostaa arvon.
     
  5. Tee ohjelma, joka asettaa bitit 4 ja 5 arvoon yksi 8 bittisestä luvusta.
     
  6. Tee ohjelma, joka vaihtaa bittien 4 ja 5 arvot vastakkaisiksi  antamastasi 8 bittisestä luvusta. Kutsu funktiota kaksi kertaa ja  tarkasta saitko takaisin alkuperäisen luvun.
     
  7. Kahdessa 8 bittisessä luvussa on merkitsevänä osana molemmissa  neljä vähinten merkitsevää bittiä. Yhdistä luvut yhteen kahdeksan  bittiseen lukuun siten että ensimmäinen luku on neljässä vähiten  merkitsevässä bitissä ja toinen neljässä eniten merkitsevässä bitissä. Loogiset operaatiot kahdeksanbittisillä luvuilla

  8. Vaihda 16 bittisen luvun vähiten merkitsevä ja eniten merkitsevä tavu keskenään.
     
  9. Testaa  sizeof funktion toimintaa ja tulosta näytölle char, short, int, long ja long long tyyppien koko.
     
  10. Lue Checkboxeista arvot 8-bittiseen kokonaislukuun (quint8), näytä luku lcd-ikkunassa  ja pura arvot takaisin toisiin checkboxeihin.
     
  11. CAN-väylässä viesti lähetetään enimmillään 64 bitin ryhmissä. Suunnittele ja toteuta viesti, joka koostuu kahdesta 8-bittisestä etumerkillisestä lämpötilaviestistä, 16-bittisestä etumerkittömästä pyörimisnopeusviestistä, 32-bittisestä asemaviestistä. Tee ohjelma, joka laittaa viestit CAN-viestiin em.järjestyksessä. Humioita! Vaikka lämpötila-arvot luetaan etumerkillisinä, ne kannattaa sijoitaa 64-bittiseen viestiin etumerkittöminä, jos muutat negatiivisen qint8 arvon (kahden komplementin) qint64 arvoksi, niin "etumerkki bitti" menee bittiin numero 64 ja muunnoksesta tulee susi koko viestin kannalta. Kannattaa aetaan break point muunnosten eteen ja asettaa oikealla näkyvä muuttujaikkuna binaarimuotoon, jotta sijoitukset ovat parhaiten nähtävissä ja tämän jälkeen ajaa koodia rivi kerrallaan.    
     
  12. Tee ohjelma, joka purkaa viestit CAN-viestistä takaisin muuttujiin.
     
  13.  Tee ohjelma, joka laskee juoksevaa keskiarvoa perättäin annetusta luvusta. Laske keskiarvo viidestä viimeksi annetusta luvusta ja näytä se Qt:n "lcd-näytössä".
     
  14. Tee tilakoneen switch...case valintarakenne ideaa hyväksi käyttäen jalankulkijoille liikennevalot. a. Tee ensin tehtävälle vaatimusten määrittely ja piirrä sitten jollain piirto-ohjelmalla tilakone, jossa määrittelet jokaiselle tilalle entry, do ja exit ehdot. b. Tee tilakonemalli alo-ohjatulle risteykselle, jossa on myös risteävää ajoneuvoliikennettä.
     
  15. Toteuta suunnittelemallesi ohjelmalle  Qt:llä toteutus.
  16. Tee tilakonemalli manipulaattorille. Manipulaattori nostaa imukupin avulla laatikon liukuhihnalta ja laskee sen toiselle hihnalle. Tarraimena käytetään imukuppia, jossa on painekytkin tunnistmassa kappaletta.
     
  17. Lisää tilakonemalliin ehjän kappaleen tunnistus. Tieto, että kappale on ehjä saadaan, kun kapplae on nostettu ylös. Ehjät kappaleet viedään asemaan A ja virheelliset asemaan B
     
  18. Tee, edellisen tehtävän tilakonemallille toteutus Qt:llä.
     
  19. Testaa alla esitetyn luokan funktiot.

    Code Block
    languagecpp
    collapsetrue
    #ifndef SRMATH_H
    #define SRMATH_H
    #include <QObject>
    
    class Srmath : public QObject
    {
        Q_OBJECT
    public:
        explicit Srmath(QObject *parent = 0);
        double Abs(double in){if(in<0.0)return in*(-1.0); else return in;}
        qint8 Abs(qint8 in){if(in<0)return in*(-1); else return in;}
        qint16 Abs(qint16 in){if(in<0)return in*(-1); else return in;}
        qint32 Abs(qint32 in){if(in<0)return in*(-1); else return in;}
        qint8 isPositive(qint8 in){if(in<0)return -1; else return 1;}
        qint16 isPositive(qint16 in){if(in<0)return (-1); else return 1;}
        qint32 isPositive(qint32 in){if(in<0)return (-1); else return 1;}
        double isPositive(double in){if(in<0.0)return (-1.0); else return 1.0;}
        double Saturate(double in,double limit);
        bool AbsSmaller(double input,double limit);
        bool AbsLarger(double input,double limit);
        bool AbsLargerAndSameSign(double input,double limit);
        double RaiseAbsLimit(double input,double *limit);
        double LowerAbsLimit(double input,double *limit);
        bool InsideOfLimits(double input1,double input2, double limitPercent);
        double CalcAverage(double input,quint8 length);
    private:
        double AvrVector[10];
        quint8 PointInAvrVector;
        double Sum;
        quint8 counter;
    };
    #endif // SRMATH_H
    
    #include "srmath.h"
    
    Srmath::Srmath(QObject *parent) :
        QObject(parent)
    {
        PointInAvrVector=0;
    }
    
    double Srmath::Saturate(double in,double limit)
    {
        if(in>limit){
            return limit;
        }
        else if(in<-limit){
            return -limit;
        }
        else return in;
    }
    
    bool Srmath::AbsSmaller(double input,double limit)
    {
        if(Abs(input)<Abs(limit))return true;
        else return false;
    }
    
    bool Srmath::AbsLarger(double input,double limit)
    {
        if(Abs(input)>Abs(limit))return true;
        else return false;
    }
    
    // when input is abs greater than limit and same sign
    bool Srmath::AbsLargerAndSameSign(double input,double limit)
    {
       if  ((Abs(input) > Abs(limit))&&((input*limit)>=0)) return true;
       else return false;
    }
    
    //! if input is absolute greater than limit, function raises limit
    double Srmath::RaiseAbsLimit(double input,double *limit)
    {
        if(Abs(input)>Abs(*limit)){
            *limit=Abs(input);
        }
        return *limit;
    }
    
    //! if input is absolute smaller than limit, function sets down limit
    double Srmath::LowerAbsLimit(double input,double *limit)
    {
        if(Abs(input)<Abs(*limit)){
            *limit=Abs(input);
        }
        return *limit;
    }
    
    bool Srmath::InsideOfLimits(double input1,double input2, double limitPercent)
    {
        double limit=limitPercent/100.0;
        if(input1<input2*(1.0+limit) &&input1>input2*(1.0-limit)){
            return true;
        }
        else return false;
    }
    
    double Srmath::CalcAverage(double input,quint8 length)
    {
        Sum=0;
        if(length>10)length=10;
        AvrVector[PointInAvrVector]=input;
        PointInAvrVector++;
        if(PointInAvrVector>(length-1))PointInAvrVector=0;
        for(counter=0;counter<length;counter++){
            Sum+=AvrVector[counter];
        }
        return Sum/length;
    }
    
  20. Tee tehtävät 11 ja 12 käyttäen udp-protokollaa ja tee lähettävä ja vastaanottava osa omiin ohjelmiinsa. kts. Tiedonvälitys udp-protokollalla

  21. Tässä tehtävässä testataan Qt:n säikeistystä. Esimerkki säikeistyksestä

  22. Tee moottorinohjaukselle luokka-arkkitehtuuri, jossa koostavana luokkana on luokka Moottori ja aliluokkana PAsuihkutus. Koosta se vielä neljästä Suutin oliosta (käytä QVector muuttujaa). Peri luokka Moottori BeMoottori luokkaan. Koosta Dialog luokka BeMoottorista.
     
  23.  Tee moottorille start painike ja laita dialogiin suuttimia varten radioButtonit, jotika välkkyvät suutinten aukeamisen tahtiin. Käytä QTimer oliota toiminnan tahdistamiseen. Tee kaasupoljin vaikka Silerilla ja kiihdytä ajastimen tahtia Sliderin avulla.
     
  24. Tee BeMoottori luokkaan Sytytys luokka ja keksi sille tekemistä.
     
  25. Tee luokkahierarkia koneenohjaukselle. Ylimpänä rakenteessa on luokka Kone, joka koostaa luokan toimilaite (QVector) ja toimilaite koostaa anturit, joita voi olla n. kappaletta.
     
  26. Tee broadcast server  ja clien ohjelmista versiot, jossa dialogit tehdään graafisella editorille.
     
  27. Lisää edellisen tehtävän  ohjelmiin kieliversiointi. (Suomi ja Englanti)
     
  28. Tee broadcast serveristä versio, jossa palveloin lähettää lämpötilaa. Simuloi lämpötilaa liukupalkilla. Muuta asiakas lukemaan ko. arvoa.
     
  29. Tutustu Fortune Client esimerkkiin. Tee esimerkin pohjalta uusi ohjelma, jossa dialogin ja verkon toiminnallisuudet ovat omissa luokissaan. Eli tee luokat Dialog ja Client. Toteuta esimerkki siten, että funktiot, jotka käsittelevät viestejä  ovat askNewMessage ja readMessage (Dialog luokassa). Tee napit Connect, Ask Message, Read Message ja End.
     
  30. Kopioi Client luokkaan esimerkistä tcp-liitynnän sisältö (ei käyttöliittymää- eli Dialogin sisältöä).
     
  31. Tutustu Kvaserin simplewrite.c ohjelmaan ja liitä se Qt- dialogiin siten, että voit syöttää ohjelman tiedot dialogsta.  
     
  32. Tutustu Kvaserin canmonitor.c ohjelmaan ja liitä se Qt- dialogiin siten, että voit syöttää ohjelman tiedot dialogsta.
     
  33. Tutustu Qwt esimerkkeihin ja tee kolmannen esimerkin pohjalta ohjelma, joka piirtää plot-ikkunaan cosini-käyrää. 
     
  34. Tee sql esimerkin avulla projektitietokanta, jossa on sarakkeet ProjektinNimi, AloitusPvm, Projektipaallikko, Budjetti.
     
  35. Ota paluuarvot talteen edellisen tehtävän sql-kyselyistä ja näytä virheet QMessageBoxilla. Käyttäjätunnus root ja salasana huhtikuu.
     
  36. Asenna tietokooneeseen MySql palvelin ja core.
     
  37. Asenna MySql Administrator, Query Browser ja Worbench
     
  38. Tee tehtävän 18. taulu MySql-tietokantaan.
     
  39. Suunnittele luokkamalli koneelle. Luokka Kone koostuu luokista Toimilaite ja Prosessi. Toimilaite koostaa luokan Liikeanturi ja VoimaAnturi. Nämä luokat taas perivät luokan Anturi. Tee luokista ensin UML-malli  Dia:lla ja sitten Qt:llä.
     
  40. Muokkaa mallia siten, että teet luokan dialogi ja laitat sen jäseneksi luokan Kone. Periytä Kone luokasta QThread. 
     
  41. Korjaa Dialla tekemä malli edellisen tehtävän mukaiseksi.
     
  42. Lisää anturille jäsenet double vahvistus, double offset, double tulo, double tuloSiYksikkona, double taarattuTuloSiYksikkona, double KeskiarvoistusVektori[10], i , int paikkaVektorissa,  double Summa
     
  43. Tee käyttöliittymään nappi MittaaTulo ja  sille Dialog luokkaan slot Mittaa. Laita myös LineEdit tai lcd, josta voit lukea mitatun arvon.
     
  44. Tee Dialog luokkaan signaali MittaaTulonArvo.
     
  45. Tee luokkaan Anturi slot void PaivitaTulo();
     
  46. Yhdistä signaali MittaaTulonArvo slotiin PaivitaTulo.
     
  47. Emitoi (lähetä) signaali MittaaTulonArvo käyttöliittymän slotissa Mittaa.
     
  48. Tee luokkaan Anturi signaali LahetaMitattuArvo(double) ja ota se vastaan käyttöliittymässä.

    ui

    Dialog

     

    Anturi

    MittaaTulo->

    ->Mittaa

     

     

     

    MittaaTulonArvo->

     

    ->PaivitaTulo

     

    ArvonNaytto<-

     

    <- LahetaMitattuArvo(double)

  49. Laita jokin arvo emitoimaasi signaaliin LahetaMitattuArvo(double) ja testaa, että arvo tulee näytölle.
     
  50. Tee käyttöliittymään Horizontal Slider ja välitä se signaali - slot mekanismilla  Anturi- luokan muuttujaan tulo.
     
  51. Tee Anturi-luokkaan funktio Paivita. Kutsu tätä edellisessä tehtävässä tekemästäsi slotista.
     
  52. Laita Paivita - funktion sisällöksi tuloSiYksikkona=tulo*vahvistu+offset;
     
  53. Testaa, että vahvistettu tulo tulee näytölle.
     
  54. Tee  Umbrello -ohjelmalla UML-malli tähän asti tekemästäsi softasta.
     
  55. Tässä tehtävässä toteutamme asynkronisen säikeen, joka lukee anturia ja lähettää signaalilla tiedon käyttöliittymälle. Tee Kone luokkaan run-funktio ja  siihen forever toistorakenne. Katso mallia säikeistetystä ohjelmasta.\

    Code Block
    languagecpp
    collapsetrue
    //tämä luokan esittelyyn
    QTime displayUpdateTimer;
    
    Code Block
    collapsetrue
    forever
      {
            //Tähän anturin tiedon laskenta
              if (NayttoAjastin.elapsed() > Paivitysaika)
              {
                  Nayttoajastin.restart();
                  emit LahetaAnturinTiedot(Arvo);
              }
    }
    
  56. Hae koneen arvot Sqlite tietokannasta esimerkin mukaisesti.
     
  57. Tee  Qwt plot esimerkki.
     
  58. Hyödynnä esimerkkiä ja tulosta anturin arvo qwtplotissa.
     
  59. Päivitä edellisiä tehtäviä kuvaava Umbrello malli.
     
  60. Tee kone esimerkki siten, että teet kaikista luokista dialogeja. Laita luokkiin toimilaite ja anturi liukupalkit, joilla voit säätää niiden antamia arvoja. Älä käytä enää QThread luokkaa kone luokan perinnässä.
     
  61. Tee dialog perustainen ohjelma ja koosta se kahdesta alidialogista. Kokeile dialogien avausta show ja exec funktioilla, mitä huomaat? Ohjeita: sisällytä "include" alidialogit päädialogin otsikkotiedostoon, tee dialogeille muuttujat päädialogiin, tee päädialogiin nappi, jolla avaat alidialogit funktioilla exec tai show.    
     
  62. Lisää edellisen tehtävän dilogeihin viestinvälitystä. Lähetä viesti päädialogista molempiin alidialogeihin. Esimerkiksi niin, että teet dialogiin napin, joka lähettää editline viestin molemmille alidialogeille.
     
  63. Lisää alidialogeihin viestinvälitys toiseen alidialogiin edellisessä tehtävässä kuvatulla tavalla.
     
  64. Lisää vielä alidialogeihin vistinvälitys päädialogiin.
     
  65. Tutustu Kvaserin simplewrite.c ohjelmaan ja liitä se Qt- dialogiin siten, että voit syöttää ohjelman tiedot dialogista.  
     
  66. Tutustu Kvaserin canmonitor.c ohjelmaan ja liitä se Qt- dialogiin siten, että voit syöttää ohjelman tiedot dialogista.
     
  67. Tee ohjaus manipulaattorille, joka nostaa laatikon pöydältä ja laittaa sen toiselle pöydälle.
     
  68. Tee edellinen tehtävä käyttäen switch-case rakennetta ja QTimer ajastinta. Kts. Qt:n wiki.
     
  69. Tee edellinen tehtävä käyttäen tilakoneessa omaa Säiettä QThread.
     
  70.  Asenna koneeseesi Synapticin avulla Sliteman Sqlite tietokanta editori.
     
  71. Tee tietokanta Sliteman ohjelmalla ja lisää siihen taulu Sylinteri, jossa on kentät id, nimi, isku, vahvistus ja tulosta taulun asetukset tekstitiedostoon.
     
  72. Kokeile tehdä samainen taulu seuraavalla sql-komennolla:

    Code Block
    languageSQL
    collapsetrue
    CREATE TABLE sylinteri
    (
        id INTEGER PRIMARY KEY NOT NULL,
        nimi TEXT,
        isku REAL,
        vahvistus REAL
    );
    
  73.  Tee Sqlite tietokanta, joka sisältää taulut sensori  kentillä sensor_id, vahvistus ja offset  sekä kalibrointi kentillä kalibrointi_id, sensor_id, Todellinen, Mitattu ja vielä vierasavain tauluun sensori. Vähän mallia

    Code Block
    languageSQL
    collapsetrue
    FOREIGN KEY(sensor_id) REFERENCES sensor(sensor_id)
    

    Täytä tauluihin kahden anturin tiedot ja testaa tiesotjen hakua sql-komennoilla, esimeriksi

    Code Block
    languageSQL
    collapsetrue
    SELECT * FROM kalibrointi WHERE sensor_id=1;
    
  74. Tee ohjelma, joka lukee sensorien määrän sensoritaulusta ja muodostaa tarvittavan määrän sensoreita.

    Code Block
    languagecpp
    collapsetrue
    //luodaan sensori osoittimena Dilogi luokkaan
    QVector <Sensori> sensori; //luodaan vektori, joka sisältää sensoreja
    
    // esitellään dialog.h tiedostossa esimerkiksi näin
    Sensori *sensori;
    
    
    luetaan Dialogi.cpp :ssa sensorien määrä tietokannasta
    
     ja sitten luodaan new komennolla anturit
    
     sensori=new Sensori[maara];
    
  75. Tee sovellus, joka lukee sylinteritietokannasta sylinterien tiedot ja jokaiseen sylinteriin liittyy asema- ja voima-anturi ja niille kalibrointitaulu. Muista tehdä kalibrointitauluun vierasavain anturitauluun ja anturitauluun vierasavain sylinteritauluun. Näin saat yhdistettyä anturin oikealle sylinterille ja kalibrointitaulun oikealle anturille.
     
  76. Tässä tehtävässä tehdään säätöpiiri. Säätöpiirin tehtävänä on säätää sylinterille menevää ohjetta siten, että haluttu asema saavutetaan mahdollisimman hyvin. Jotta tehtävä olisi mielekäs, täytyy asema-anturin eli sylinterin oloarvon ja sylinterin halutun aseman eli sylinterin asetusarvon välistä suhdetta simuloida. Toteutetaan simulaattori tässä tehtävässä  mahdollisen yksinkertaisesti seuraavan ajatuksen mukaisesti: jos ohje on suurempi kuin todellinen asema, lisätään todellista asemaa kellolla (QTimer timer muuttujan timeoutilla)

    Code Block
    languagecpp
    collapsetrue
    if(ohjesylinterille>olo)olo++;
    if(ohjesylinterille<olo)olo--;
    

    Säätöpiiri voidaan tehdä yksinkertaisesti vähennys-ja kertolaskuilla seuraavasti:

    Code Block
    languagecpp
    collapsetrue
    ohjesylinterille = (asetus-olo)*vahvistus;
    

    Tehdään tehtävä aluksi ainoastaan hyödyntäen Dialog luokkaa. Eli tehdän Dialog luokkaan slider, jolla annetaan ohjearvoja ja lisäksi edistymispalkki johon laitetaan sylinterin todellinen asema. Ohjelmoi lisäksi tarvittava ajastin, jotta saat oloarvon muuttumaan edellä kuvatulla tavalla. Testaa millä vahvistuksen arvolla saat järjestelmän toimimaan (tee ohjelmaan editline ja sille validator, jolla rajataan vahvistuksen arvot välille 0...1000).
     

  77. Lisää tehtävään Qwt:n graafinen näyttö ja piirrä siihen asetus- oloarvot.
     
  78. Muuta edellisen tehtävän P-säädin PI-säätimeksi. 

    Code Block
    languagecpp
    collapsetrue
    //lisätään summaan eroarvo, muista nollata summa muodostimessa
     IntegraattorinSumma+=(asetus-oloarvo);
     //ja sitten säädin
     ohjearvosylinterille=(asetus-oloarvo)*P_vahvistus+ IntegraattorinSumma*I_vahvistus;
    

    Viritä säädin Ziegler Nichols'in säännöillä.

  79. Lisää mittaushaaraan liukuva keskiarvo. Kts. Qt-esimerkit.

  80. Lisää säätimeen D-lohko.

#include <iostream>
using namespace std;

enum CapsStates {
    CAPS_OFF,
    CAPS_ON
    };
enum Events {
    CAPS_LOCK_PRESSED,
    KEY_A_PRESSED
};
        
void kasitteleTapahtuma(Events tapahtuma, CapsStates &kapsLokinTila);

int main() {
    
    CapsStates kapsLokinTila = CAPS_OFF;
    
    Events tapahtuma = CAPS_LOCK_PRESSED;

    kasitteleTapahtuma(KEY_A_PRESSED, kapsLokinTila);
    kasitteleTapahtuma(tapahtuma, kapsLokinTila);
    kasitteleTapahtuma(KEY_A_PRESSED, kapsLokinTila);
    kasitteleTapahtuma(tapahtuma, kapsLokinTila);
    kasitteleTapahtuma(KEY_A_PRESSED, kapsLokinTila);

}

void kasitteleTapahtuma(Events tapahtuma, CapsStates &kapsLokinTila){
    switch(tapahtuma){
        case CAPS_LOCK_PRESSED:
            if(kapsLokinTila==CAPS_OFF){
                kapsLokinTila=CAPS_ON;
                cout << "CAPS LOCK kytketty päälle\n";

            }else if(kapsLokinTila==CAPS_ON){
                kapsLokinTila=CAPS_OFF;
                cout << "CAPS LOCK kytketty pois\n";
            }
            break;
        case KEY_A_PRESSED:
            if(kapsLokinTila==CAPS_OFF){
                cout << "kirjain a painettu\n";
            }else if(kapsLokinTila==CAPS_ON){
                cout << "kirjain A painettu\n";
            }
            break;
        default:
            cout << "tuntematon tapahtuma";
    }
    
}

...