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álvoid numberTooBig()
, který je samozřejmě dobré zpracovat.setPlus()
, ... - Jako reakce na kliknutí tlačítka výběru operace použijeme slotyvoid setPlus()
...setHex()
, ... - Když se přepne číselná soustava, slotyvoid 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 slotvoid 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
50
px. - 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 ikonyQuestion
-Information
-Warning
-Critical
-
- 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
... nebo1af4
- Oct =
017
..02
... - Bin =
100011100
, bohužel tvar0b010101
není akceptován
- Hex =

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 101x (13.13 kB)
Aplikace je včetně zdrojových kódů v jazyce C++