IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

6. diel - Jednoduchá kalkulačka v Qt a C ++ - Model

V minulej lekcii, Jednoduchá kalkulačka v Qt a C ++ - Layout , sme si sľúbili, že si k našej kalkulačke vytvoríme business logiku - model a pridáme nejakú funkčnosť, aby sa z návrhu formulára kalkulačky naozaj stala funkčné kalkulačkou.

Model

V modeli sa bude nachádzať veľmi "jednoduchá" logika pre základné funkcie kalkulačky. V podstate potrebujeme len odovzdať nejaké dáta - v tomto prípade dva textové reťazce, typ operácie a druh aktuálnej číselnej sústavy. Reťazca je potrebné ďalej previesť na celé čísla a testovať, či používateľ nezadal nejaký nezmysel - teda urobiť program "relatívne blbuvzdorný". Aj keď podľa zákonov pána Murphyho to je asi nemožné:) Nakoniec to chce nejakú metódu, ktorá nám vráti výsledok danej operácie.

Naše okno, teda "controller", len odovzdá dáta a model sa postará o všetky kontroly a výsledok odovzdá späť, ak o to bude požiadaný.

Do projektu si pridáme novú triedu, napr. Model, odvodenú od QObject. Zdedením QObject získame možnosť používať sloty a signály. Tvorcovia Qt tento spôsob komunikácie odporúčajú a používajú vo svojich modeloch, zvyčajne odvodených od triedy QAbstractItemModem. Myslím, že to už by bol kanón na vrabce a ako povedal A. Einstein - Urob to tak jednoducho, ako to len ide. Nie viac ani menej. (Parafráza)

Teda pre odovzdanie dát použijeme Setter, pri chybách budeme posielať signály a výsledok odovzdáme späť "Getter".

Pridanie triedy do projektu

Pravým tlačidlom myši klikneme do Qt prieskumníkovi súborov projektu az kontextového menu zvolíme pridať nový. Vyberieme súbor C++ a typ C++ Class:

Pridanie novej C ++ triedy do Qt projektu - Qt - Okenné / formulárové aplikácie v C ++

Súbor pomenujeme Model, nastavíme Base class na QObject a zaškrtneme možnosť Include QObject:

Vytvorenie modelu v Qt aplikácii v C ++ - Qt - Okenné / formulárové aplikácie v C ++

Model.h

Hlavičkový súbor nového modelu bude mať nasledujúci kód, ktorý si hneď popíšeme:

#ifndef MODEL_H
#define MODEL_H

#include <QObject>
#include <QString>

class Model : public QObject
{
    Q_OBJECT
public:
    explicit Model(QObject *parent = nullptr);

    static const int OP_PLUS    = 0x00;
    static const int OP_MINUS   = 0x01;
    static const int OP_TIMES   = 0x02;
    static const int OP_DIVIDE  = 0x03;

    static const int SYS_HEX    = 0x04;
    static const int SYS_DEC    = 0x05;
    static const int SYS_OCT    = 0x06;
    static const int SYS_BIN    = 0x07;

    static const int OP_LEFT    = 0x08;
    static const int OP_RIGHT   = 0x09;


private:
    int opLeft;
    int opRight;

    int operation;
    int numBasis;

    bool isLeftOk;
    bool isRightOk;

public:
    void setLeft(QString value);
    void setRight(QString value);
    void setOperator(int operation);
    void setNumBasis(int numBasis);

    int calculate();

private:
    int numberTest(QString number, bool *isOk);

signals:
    void operandFalse(int operand);
    void zeroDivide();

public slots:
};

#endif // MODEL_H

Čo sme to teda vlastne stvorili v hlavičkovom súbore?

  • Úplne na začiatok bolo treba vložiť hlavičkový súbor knižnice QString
  • Pridali sme niekoľko statických konštánt, ktoré zastupujú jednotlivé operácie. Alternatívne by šlo tiež použiť vymenovaného typu. Keby sme použili konštanty (vlastne makrá) druhu #define C_NAME (VALUE), tak tie potom nemajú priamu kontrolu nad typom.
  • Ďalšie sada konštánt určuje číselnú sústavu.
  • Následne sme deklarovali niekoľko privátnych atribútov, ktoré zastupujú operandmi (vstupné čísla) a operáciu: int leftOperand.... Tiež sme definovali aktuálne používanú sústavu. Veľmi dôležité sú logické atribúty bool isLeftOk..., ktoré nám budú detekovať chyby pri zadaní. Síce vyvolajú len neurčitú chybu, ale budeme vedieť, že niečo bolo zle.
  • Potom bolo treba uverejniť niekoľko sto / get metód k nastavenie atribútov tried: void setLeft(QString value)..., int getLeft()...
  • Deklarujeme metódu, ktorá dokáže vrátiť výsledok operácie: int calculate().
  • Aby sme zbytočne nepísali rovnaký kód na test správnosti vstupe, napíšeme si jednu funkciu navyše: int numberTest(QString number, bool *isOk);, Kde prvý parameter odovzdá užívateľský vstup a druhý mám umožní vrátiť prípadnú chybu zadania.

Užívateľské signály

Napriek tomu, že táto časť článku patrí k hlavičke modelu, je jej dôležitosť natoľko významná, že si zasluhuje svoj nadpis.

Zatiaľ som používali len signály, ktoré už niekto nanovo vymedzil. Ovšem je dosť dobré vedieť, že si ich môžeme napísať sami a dokonca v nich odovzdávať argumenty rôznych typov.

V hlavičkovom súbore nájdeme signály v bloku začínajúce kľúčovým slovom signals, čo je teraz synonymum pre public (predtým bolo definované ako #define signals protected).

Inak ich deklarácia má rovnaký tvar ako bežná členská funkcia, ale nemusí byť v súbore *.cpp definovaná.

My budeme posielať signál pri chybnom vstupe void operandFalse(int operand), kde argument určí pomocou konštanty, ktoré číslo bolo zle. Signál void zeroDivide() bude varovať pri delení nulou, čo je v matematike nedefinovaný výsledok. Proste sa to nerobí:-)

Model.cpp

Prejdime do implementačného súboru. Tu máme teda výkonnú časť modelu našej kalkulačky.

Konštruktor

Konštruktor by nám mal preventívne nastaviť atribúty na predvolené hodnoty, aby nedochádzalo k nečakaným chybám. Východisková situácia po vytvorení modelu bude:

  • Žiadny operand (číslo) nebol zadaný.
  • Operácia je defaultne sčítanie.
  • Číselná sústava je dekadické.
  • Nedošlo k žiadnej chybe.

Kód konstruktoru je nasledujúci:

Model::Model(QObject *parent) : QObject(parent)
{
    opLeft = 0;
    opRight = 0;
    operation = Model::OP_PLUS;
    numBasis = Model::SYS_DEC;
    isLeftOk = false;
    isRightOk = false;
}

Setter

Setter operandov využijú členských funkcií triedy QString a prevedú reťazec na celé číslo. Zároveň prebehne test správnosti vstupu metódou numberTest(), ktorú dodáme neskôr. Výsledok testu si uložíme na neskoršie použitie:

void Model::setLeft(QString value)
{
    opLeft = numberTest(value, &isLeftOk);
}

void Model::setRight(QString value)
{
    opRight = numberTest(value, &isRightOk);
}

Následne si nastavíme typ operácie a základ číselnej sústavy. Argumentom bude jedna z konštánt modelu. Metódy sú veľmi triviálne a podtržnítko u názvu argumentu je len jednoduchá finta, ako používať rovnaké názvy a vyhnúť sa ukazovateľom (pointerům):

void Model::setOperator(int _operation)
{
    operation = _operation;
}

void Model::setNumBasis(int _numBasis)
{
    numBasis = _numBasis;
}

Test vstupov

Pozrime sa na funkciu pre test vstupu. Odovzdáme jej reťazec, ktorý by mal predstavovať číslo, a referenciu na atribút výsledku testu. Podľa aktuálnej číselnej sústavy zabezpečíme prevod textu na číslo. Napr. pre šestnástkovom sústavu riadok retVal = number.toInt(isOk, 16) znamená: text preveď na celé číslo o základe 16 a ak všetko je v poriadku, do isOk ulož true, inak false.

Hodnotu potom metóda vráti a to aj v prípade, že došlo k chybe (vtedy vracia 0):

int Model::numberTest(QString number, bool *isOk)
{
    int retVal = 0;
    switch (numBasis) {
    case SYS_HEX:
        retVal = number.toInt(isOk, 16);
        break;
    case SYS_DEC:
        retVal = number.toInt(isOk, 10);
        break;
    case SYS_OCT:
        retVal = number.toInt(isOk, 8);
        break;
    case SYS_BIN:
        retVal = number.toInt(isOk, 2);
        break;
    }
    return retVal;
}

Výpočet

Pre samotný výpočet použijeme jednoduchú funkciu obsahujúce jeden prepínač switch, ktorý podľa druhu operácie vykoná požadovanú akciu:

int Model::calculate()
{
   if (!isRightOk) {
        emit operandFalse(OP_RIGHT);
        return 0;
    }

    if (!isLeftOk) {
        emit operandFalse(OP_LEFT);
        return 0;
    }

    if (opRight == 0 && operation == OP_DIVIDE) {
        emit zeroDivide();
        return 0;
    }

    switch (operation) {
    case OP_PLUS:
        return opLeft + opRight;
        break;
    case OP_MINUS:
        return opLeft - opRight;
        break;
    case OP_TIMES:
        return opLeft * opRight;
        break;
    case OP_DIVIDE:
        return opLeft / opRight;
        break;
    default:
        return 0;
    }
}

Najskôr vykonáme niekoľko testov. Predovšetkým ak je chybný niektorý z operandov, odošleme signál pomocou príkazu emit, ktorý z nich to bol. Pre prípad, že dôjde k pokusu delenie nulou, opäť pošleme signál. Pri chybe automaticky vraciame výsledok 0:

V budúcej lekcii, Jednoduchá kalkulačka v Qt a C ++ - Dokončenie , vykonáme úpravy hlavného okna a tým kalkulačku sprevádzkujeme.


 

Predchádzajúci článok
Jednoduchá kalkulačka v Qt a C ++ - Layout
Všetky články v sekcii
Qt - Okenné / formulárové aplikácie v C ++
Preskočiť článok
(neodporúčame)
Jednoduchá kalkulačka v Qt a C ++ - Dokončenie
Článok pre vás napísal Virlupus
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje webovým aplikacím, skladově-účetnímu softwaru, 3D grafice, lexiální analýze a parserování. Studuje fyziku na MFF UK. Učil IT na střední škole.
Aktivity