Vydělávej až 160.000 Kč měsíčně! Akreditované rekvalifikační kurzy s garancí práce od 0 Kč. Více informací.
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 7 - Jednoduchá kalkulačka v Qt a C++ - Dokončení

V minulé lekci, Jednoduchá kalkulačka v Qt a C++ - Model, jsme si připravili modelovou vrstvu pro naší formulářovou aplikaci v C++ a frameworku Qt.

Dnes formulář a modelovou vrstvu pospojíme a kalkulačku tak dokončíme, čímž získáme svou první opravdovou formulářovou C++ aplikaci!

kalkulacka.h

Jako vždy začneme od hlavičkového souboru. Nutné je přidat zde hlavičku souboru model.h a jednu členskou proměnnou - odkaz na náš model:

...
#include "model.h
...
private:
    Model *model;

Sloty

Také budeme přijímat mnoho signálů zvenčí a něco je musí zachytávat. Potřebujeme tedy nějaké sloty:

...
public slots:
    void numberTooBig();

    void setPlus();
    void setMinus();
    void setTimes();
    void setDivide();

    void setHex();
    void setDec();
    void setOct();
    void setBin();

    void getFalseOp(int operand);
    void getZeroDivide();

    void calculate();
...

Přidali jsme následující sloty:

  • numberTooBig() - Při výsledku, který se nevejde na display, QLCDNumber pošle signál void numberTooBig(), který je samozřejmě dobré zpracovat.
  • setPlus(), ... - Jako reakce na kliknutí tlačítka výběru operace použijeme sloty void setPlus()...
  • setHex(), ... - Když se přepne číselná soustava, sloty void setHex() a další podobné se postarají o příslušnou akci.
  • getFalseOp() - Model nám může poslat signál o chybě vstupu a my si jej zpracujeme.
  • getZeroDivide() - Možná po kalkulačce bude někdo chtít, aby něco vydělila nulou. Tuto situaci si vezme na starost tento slot.
  • calculate() - A konečně klikneme na tlačítko "spočítej". To bude mít za úkol slot void calculate().

kalkulacka.cpp

V hlavičce máme vše definované a můžeme přejít k implementaci.

QMessageBox

Občas potřebujeme informovat uživatele o nějakých událostech. QMessageBox je k tomu jako dělaný. Vložíme si jeho hlavičku do souboru:

...
#include <QMessageBox>
...

Jak message box ovládat si ukážeme za okamžik.

Konstruktor

Nyní se podíváme na konstruktor a přidáme do něj několik řádků:

...
display->setDigitCount(32);
display->setMaximumHeight(50);

model = new Model(this);
...

Našemu displeji říkáme kolik číslic bude umět zobrazit. Myslím, že 32 je dost i pro binární čísla. setDigitCount(n) nám říká, kolik číslic display bude umět zobrazit. Povolený rozsah je od žádné číslice až do 99 čísel na displeji.

  • Jelikož se mi zdá display nějak moc vysoký a čísla moc velká, tak mu upravíme výšku na 50px.
  • Nutně potřebujeme objekt našeho modelu, abychom s ním mohli komunikovat. Tedy si jej hned vytvoříme. Nezapomeneme jej v destruktoru také smazat, ten následuje níže.
// Destruktor
Kalkulacka::~Kalkulacka()
{
    // Zde zničit vše, co jsme stvořili
    if (model != nullptr) { delete model; }

    if (btnEqual != nullptr) { delete btnEqual; }

    if (operationButtons != nullptr) { delete operationButtons; }

    if (btnPlus != nullptr) { delete btnPlus; }
    if (btnMinus != nullptr) { delete btnMinus; }
    if (btnTimes != nullptr) { delete btnTimes; }
    if (btnDivide != nullptr) { delete btnDivide; }

    if (leftOperand != nullptr) { delete leftOperand; }
    if (rightOperand != nullptr) { delete rightOperand; }

    if (numeralButtons != nullptr) { delete numeralButtons; }

    if (btnHex != nullptr) { delete btnHex; }
    if (btnDec != nullptr) { delete btnDec; }
    if (btnBin != nullptr) { delete btnBin; }
    if (btnOct != nullptr) { delete btnOct; }

    if (mainLayout != nullptr) { delete mainLayout; }
}

Rada k novějšímu prostředí QtCreator: Místo NULL používejte nullptr, což je skutečný ukazatel na "nulu" a ne jen makro "nuly". Norma C++11 nechce číslo, ale opravdu skutečný ukazatel. V dalším kódu se této normy budeme držet. Sice funkčně je to shodné, ale modernizace jde vpřed a může se stát, že překladače budou podporovat pouze tuto normu.

Propojení tlačítek ke slotům

V konstruktoru budeme dále připojovat tlačítka ke slotům. Nejprve změny číselných soustav. Každé tlačítko pošle signál clicked() a my k němu připravíme patřičný slot. Ten napojíme na metodu, kterou později vytvoříme. Ovšem propojit sloty s metodami můžeme hned, i když zatím neexistují:

...
connect(btnBin, SIGNAL(clicked()), this, SLOT(setBin()));
connect(btnOct, SIGNAL(clicked()), this, SLOT(setOct()));
connect(btnDec, SIGNAL(clicked()), this, SLOT(setDec()));
connect(btnHex, SIGNAL(clicked()), this, SLOT(setHex()));
...

Následují tlačítka jednotlivých operací:

...
connect(btnPlus, SIGNAL(clicked()), this, SLOT(setPlus()));
connect(btnMinus, SIGNAL(clicked()), this, SLOT(setMinus()));
connect(btnTimes, SIGNAL(clicked()), this, SLOT(setTimes()));
connect(btnDivide, SIGNAL(clicked()), this, SLOT(setDivide()));
...

A zbývá nám poslední tlačítko pro výpočet:

...
connect(btnEqual, SIGNAL(clicked()), this, SLOT(calculate()));
...

Overflow displeje

Komponenta QLCDNumber při přetečení povolené délky čísla posílá signál overflow(). Sice není nutné na něj reagovat, ale uživatel by se mohl divit, že má zobrazenou nulu. Proto si myslím, že je vhodné jej na to upozornit. Tedy tento signál si propojíme se slotem, jenž na tuto skutečnost bude reagovat:

...
connect(display, SIGNAL(overflow()), this, SLOT(numberTooBig()));
...

Další chybové stavy

Nesmíme zapomínat, že uživatel je schopen do vstupu zadat všechno možné a většinou to není správně. Je třeba jej na to upozornit nějakou přívětivou zprávou.

Signály z modelu si tedy propojíme s chybovými sloty. Všimněte si, že operandFalse(int) je pouze s typem argumentu. Jeho název zde neuvádíme:

...
connect(model, SIGNAL(operandFalse(int)), this, SLOT(getFalseOp(int)));
connect(model, SIGNAL(zeroDivide()), this, SLOT(getZeroDivide()));
...

Implementace metod slotů

Tím máme konstruktor hotový. Nastal čas sloty implementovat.

Početní operace

Začneme početními operacemi:

void Kalkulacka::setPlus()
{
    model->setOperator(Model::OP_PLUS);
}

void Kalkulacka::setMinus()
{
    model->setOperator(Model::OP_MINUS);
}

void Kalkulacka::setTimes()
{
    model->setOperator(Model::OP_TIMES);
}

void Kalkulacka::setDivide()
{
    model->setOperator(Model::OP_DIVIDE);
}

Jednoduše jen řekneme modelu, jakou operaci chceme provádět. Použijeme k komu "jeho" konstanty a členské funkce. Myslím, že nic složitého.

Přepínání číselných soustav

Přepínání soustav o nějakém základu je též jednoduché. Pouze využijeme skutečnosti, že slot je standardní funkce, kterou lze zavolat, když je potřeba. Komponenta QLCDNumber obsahuje sloty pro přepínání soustav: setHexMode() atd. nebo setMode(QLCDNumber::Mode), kde Mode může být HEX, DEC, OCT, BIN. V našem případě je příkaz tedy např. display->setHexMode().

Samozřejmě je třeba o těchto změnách informovat i model a to způsobem: model->setNumBasis(Model::SYS_HEX);.

Dodejme tedy metody i pro tyto signály:

void Kalkulacka::setHex()
{
    display->setHexMode();
    model->setNumBasis(Model::SYS_HEX);
}

void Kalkulacka::setDec()
{
    display->setDecMode();
    model->setNumBasis(Model::SYS_DEC);
}

void Kalkulacka::setOct()
{
    display->setOctMode();
    model->setNumBasis(Model::SYS_OCT);
}

void Kalkulacka::setBin()
{
    display->setBinMode();
    model->setNumBasis(Model::SYS_BIN);
}

Chybové hlášky

Pojďme informovat uživatele, že něco spáchal jinak, než je třeba. A nebo, že se stalo něco podivného.

Velké číslo

Začneme tím moc velkým číslem:

void Kalkulacka::numberTooBig()
{
    QMessageBox message(QMessageBox::Information, tr("Příliš dlouhé číslo"), tr("Buhužel číslo se nevejde na display"));
    message.exec();
}

Vidíme, že message box zobrazíme metodou exec(). Mnohem zajímavější jsou ale parametry při jeho vytvoření:

  • Prvním je ikona. QMessageBox disponuje následujícími již přednastavenými ikonami:
    • NoIcon - bez ikony
    • Question - Ikona otázky - Qt - Okenní/formulářové aplikace v C++
    • Information - Ikona informace - Qt - Okenní/formulářové aplikace v C++
    • Warning - Ikona varování - Qt - Okenní/formulářové aplikace v C++
    • Critical - Ikona kritické situace - Qt - Okenní/formulářové aplikace v C++
  • Dalším parametrem je titulek. Ten zde máme opět připravený pro případné překlady aplikace do jiných jazyků pomocí funkce tr().
  • Posledním parametrem je zde text zprávy.

Možností je ještě více, ale o tom si budeme povídat až se dostaneme k dialogům v některé z dalších lekcí.

Chybný vstup

Reakce na chybný vstup bude opět ve formě zprávy. Tady se jen musíme rozhodnout, který to že operand byl špatně if (operand == Model::OP_LEFT)... Pokud bude chyba v obou, model pošle signály dva (postupně) a tedy se zobrazí i dvě zprávy:

void Kalkulacka::getFalseOp(int operand)
{
    if (operand == Model::OP_LEFT) {
        QMessageBox msg(QMessageBox::Critical, tr("Číslo na levé straně"), tr("Někdě se stala chyba. Špatný formát čísla nebo nebylo zadáno vůbec"));
        msg.exec();
    }
    else {
        QMessageBox msg(QMessageBox::Critical, tr("Číslo na pravé straně"), tr("Někde se stala chyba. Špatný formát čísla nebo nebylo zadáno vůbec"));
        msg.exec();
    }
}
Dělení nulou

Dělení nulou je oznámeno též dialogem:

void Kalkulacka::getZeroDivide()
{
    QMessageBox msg(QMessageBox::Critical, tr("Dělení nulou"), tr("Lituji, ale tato operace nemá definovaný výsledek!!!"));
    msg.exec();
}

Výpočet

Zbývá nám už jen samotný výpočet, tedy reakce na vypočítávací tlačítko:

void Kalkulacka::calculate()
{
    model->setLeft(leftOperand->text());

    model->setRight(rightOperand->text());

    int result = model->calculate();
    display->display(result);
}

Prvně nastavíme levý a pravý operand. QLineEdit disponuje funkcí text(), která vrací obsah editačního pole jako QString. Tento text předáme modelu jako model->setLeft(leftOperand->text()). Model následně požádáme o výsledek pomocí int result = model->calculate(). Komponenta QLCDNumber má metodu display(int), která výsledné číslo zobrazí.

Závěrem

Naši kalkulačku máme hotovou a její kompletní zdrojové kódy si můžete stáhnout níže. Na závěr ale ještě několik poznámek:

  • Čísla zadávejte ve tvaru zvolené soustavy:
    • Hex = 0x1af4... nebo 1af4
    • Oct = 017.. 02 ...
    • Bin = 100011100, bohužel tvar 0b010101 není akceptován
Okenní kalkulačka v C++ a Qt - Qt - Okenní/formulářové aplikace v C++

Příště, v lekci QString - Dokončení a souhrn základních řetězcových kolekcí, se podíváme na další zvláštnosti třídy QString. A když zbude místo, tak i na nějaké kolekce jako je QVector, QList a tak podobně, protože se nám vzápětí budou tyto informace hodit.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 92x (13.13 kB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

Předchozí článek
Jednoduchá kalkulačka v Qt a C++ - Model
Všechny články v sekci
Qt - Okenní/formulářové aplikace v C++
Přeskočit článek
(nedoporučujeme)
QString - Dokončení a souhrn základních řetězcových kolekcí
Článek pro vás napsal Virlupus
Avatar
Uživatelské hodnocení:
9 hlasů
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