IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
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 5 - Jednoduchá kalkulačka v Qt a C++ - Layout

V minulé lekci, Řetězce v Qt - QString a QChar, jsme něco pověděli o řetězcích v Qt a v C++.

Dnes začneme tvořit již nějakou zajímavější aplikaci, velmi jednoduchou kalkulačku. Bude mít dvě vstupní pole a čtyři základní operace: násobení, dělení, sčítání a odečítání.

Dnes nám bude stačit obyčejné okno bez toolbarů, menu, stavového řádku a takových podobných vymožeností :-) Qt je založeno na architektuře MVC, tedy přesněji řečeno na architektuře Model - View, protože pohled zastupuje i funkci controlleru - zpracování požadavků. Pokud jste se s touto architekturou ještě nesetkali, důležité je, že oddělujeme kód logiky aplikace (v našem případě nyní třída s výpočty pro kalkulačku) od prezentace, což je v našem případě kód definující a obsluhující formulář. Nikdy tedy nebudeme provádět nic složitého přímo v souboru s formulářem, ale pouze zde provoláme metody nějaké další třídy, ve které bude další kód umístěný.

Návrh aplikace

Celý návrh můžeme udělat několika způsoby:

  • Od modelu - tedy prvně navrhnout, co bude základ logiky, a to potom zobrazit ve formuláři.
  • Od view - pohledu: tedy prozkoumat, co budeme zobrazovat, a pak si k tomu udělat logickou část.
  • Od každého něco - postupně něco zobrazit. Pak udělat kus logiky, zas to zobrazit, nebo i opačně.

Zde jsem zvolil druhou variantu. Vytvoříme si nejprve kompletní okno a jeho rozvržení, čímž se rozhodneme, co kde má být zobrazeno. Teprve potom, v příští lekci, si vytvoříme třídu, která nám potřebná data poskytne, když o ně požádáme.

Návod k použití aplikace

Kalkulačka bude fungovat takto:

  • Zadáme celé číslo do jednoho pak do druhého vstupního pole
  • Zvolíme operaci mezi čísly
  • Zvolíme číselnou soustavu k zobrazení
  • Stiskneme tlačítko pro výsledek. Ten se ukáže, pokud nenastala chyba. Při chybě zadání, nebo jiné chybě, se zobrazí krátký dialog pomocí QMessageBox.

Založení projektu

Projekt tentokrát založíme jen na QWidget, který můžeme použít i jako základ aplikace. Dřívější lekce měly za úkol ukázat základ komplexní aplikace a z čeho se skládá celé okno, proto jsme v nich použili QMainWindow. V kalkulačce ovšem nebudeme potřebovat menu, toolbar a jiné věci. Právě proto raději použijeme QWidget, který bude fungovat jako plnohodnotné okno i s ikonou, titulkem, frontou událostí a vším potřebným.

Nový projekt již založit umíme. Jen v jedné chvíli to chce maličkou změnu. Předně si pojmenujeme třídu okna Kalkulacka, hlavně žádná diakritická znaménka, a QMainWindow změníme na QWidget:

Založení projektu pro kalkulačku v C++ a Qt frameworku - Qt - Okenní/formulářové aplikace v C++

Též jsem si pro vás připravil ikonu okna:

Ikona okna kalkulačky v C++ - Qt - Okenní/formulářové aplikace v C++

Tu již také umíme nastavit. Přidáme nový soubor Qt - Resource. Klidně si ikonu můžete stáhnout a použít. Nebo si udělat svoji či najít jinou vhodnou. Jinak je postup vlastně stejný jako v dřívějších lekcích.

Návrh uživatelského rozhraní

Jsem ze staré školy a rád používám papír a tužku. Prostě když chci udělat nějakou grafiku v programu, tak si ji nakreslím na papír a potom se rozhodnu jaká technika na to bude nejlepší. Ani zde to nebude jinak:

Návrh uživatelského rozhraní pro Qt aplikaci v C++ - Qt - Okenní/formulářové aplikace v C++

Když jsem se dlouho díval na obrázek výše, došlo mi, že tabulka (mřížka) je zcela ideální pro rozvržení všech prvků. Použijeme tedy QGridLayout. Je to velmi flexibilní rozvržení. Každý prvek má svou vlastní "buňku", ovšem může jich mít i několik - jak v řádku tak i v sloupci. Též lze nastavit i různé mezery mezi buňkami. Výsledný formulář pro kalkulačku bude potom vypadat následovně:

Kalkulačka v Qt a C++ - Qt - Okenní/formulářové aplikace v C++

Nemohl jsem odolat a proto výsledek budeme zobrazovat jako na opravdovém segmentovém displeji kalkulačky. Je pravda, že ovládací prvek QLCDNumber umí zobrazit jen celá čísla, zato v několika soustavách - šestnáctková, decimální, osmičková a binární. Toho přeci musíme také využít, proto přidáme i čtyři přepínače - QRadioButton, které umožní změnit zobrazení pro danou číselnou soustavu.

Abychom nemuseli řešit, jaké číslo nebo text uživatel zadal, použijeme pro každý operand (číslo) zvláštní vstup. Ten bude bohužel pouze textový QLineEdit, ze kterého získáme řetězec QString, tedy i tak si budeme muset zajistit, aby uživatel opravdu vložil celé číslo a ne cosi podivného.

Dlouho jsem rozmýšlel, zda pro volbu operace použít tlačítka, seznam nebo rozbalovací seznam - ComboBox... Výhra padla opět na RadioButton, uživatelsky je to pro tento účel přívětivé. Jenže zde budeme mít dvě sady přepínačů a Qt vše, co se definuje v jednom kontextu, bere, že to k sobě patří. Tedy tyto dvě skupiny musíme od sebe oddělit. K tomu doslova vybízí třída QButtonGroup.

Pro zobrazení výsledku výpočtu bude potom nejlepší obyčejné tlačítko.

kalkulacka.h

Nejprve si modifikujeme hlavičkový soubor kalkulačky, kde doplníme potřebné ukazatele. Jinak si myslím, že v tomto souboru není extra co vysvětlovat. O všech použitých třídách si ještě podrobněji povíme u souboru kalkulacka.cpp. Obsah kalkulacka.h je následující:

#ifndef KALKULACKA_H
#define KALKULACKA_H

#include <QWidget>
#include <QGridLayout>
#include <QLCDNumber>
#include <QRadioButton>
#include <QButtonGroup>
#include <QLineEdit>
#include <QPushButton>

class Kalkulacka : public QWidget
{
    Q_OBJECT

public:
    Kalkulacka(QWidget *parent = 0);
    ~Kalkulacka();

    QGridLayout *mainLayout;

    QLCDNumber *display;

    QRadioButton *btnDec;
    QRadioButton *btnHex;
    QRadioButton *btnOct;
    QRadioButton *btnBin;

    QButtonGroup *numeralButtons;

    QLineEdit *leftOperand;
    QLineEdit *rightOperand;

    QRadioButton *btnPlus;
    QRadioButton *btnMinus;
    QRadioButton *btnTimes;
    QRadioButton *btnDivide;

    QButtonGroup *operationButtons;

    QPushButton *btnEqual;
};

#endif // KALKULACKA_H

kalkulacka.cpp

Rovnou přejdeme i do souboru s implementací a vložíme do něj následující kód, který si záhy vysvětlíme:

#include "kalkulacka.h"

#include <QIcon>

Kalkulacka::Kalkulacka(QWidget *parent)
    : QWidget(parent)
{
    mainLayout = new QGridLayout();

    display = new QLCDNumber();
    mainLayout->addWidget(display, 1, 1, 1, 4);

    btnHex = new QRadioButton("HEX", this);
    btnDec = new QRadioButton("DEC", this);
    btnOct = new QRadioButton("OCT", this);
    btnBin = new QRadioButton("BIN", this);

    numeralButtons = new QButtonGroup();

    mainLayout->addWidget(btnDec, 2, 1);
    mainLayout->addWidget(btnHex, 2, 2);
    mainLayout->addWidget(btnBin, 2, 3);
    mainLayout->addWidget(btnOct, 2, 4);

    numeralButtons->addButton(btnHex);
    numeralButtons->addButton(btnDec);
    numeralButtons->addButton(btnOct);
    numeralButtons->addButton(btnBin);

    leftOperand = new QLineEdit();
    rightOperand = new QLineEdit();

    mainLayout->addWidget(leftOperand, 3, 1, 1, 2);
    mainLayout->addWidget(rightOperand, 3, 3, 1, 2);

    btnPlus = new QRadioButton("+", this);
    btnMinus = new QRadioButton("-", this);
    btnTimes = new QRadioButton("*", this);
    btnDivide = new QRadioButton("/", this);

    operationButtons = new QButtonGroup();

    operationButtons->addButton((btnPlus));
    operationButtons->addButton((btnMinus));
    operationButtons->addButton((btnTimes));
    operationButtons->addButton((btnDivide));

    mainLayout->addWidget(btnPlus, 4, 1);
    mainLayout->addWidget(btnMinus, 4, 2);
    mainLayout->addWidget(btnTimes, 4, 3);
    mainLayout->addWidget(btnDivide, 4, 4);

    btnEqual = new QPushButton(tr("Spočítej"));

    mainLayout->addWidget(btnEqual, 5, 1, 1, 4);

    setLayout(mainLayout);

    btnDec->setChecked(true);
    btnPlus->setChecked(true);
}

Kalkulacka::~Kalkulacka()
{
    if (btnEqual != NULL) { delete btnEqual; }

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

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

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

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

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

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

    setWindowIcon(QIcon(":/img/mainIcon"));
    resize(640, 480);
}

Na úplném začátku jsme vytvořili objekt tabulkového layoutu a následně objekt LCD a umístili jej do layoutu pomocí mainLayout->addWidget(display, 1, 1, 1, 4);. Zde stojí za všimnutí parametry, který mi jsou:

  • ukazatel na widget
  • řádek v tabulce - počítáno od 1 do n
  • sloupek v tabulce - opět od 1 do n
  • počet sloučených řádků, přes které má ovládací prvek přesahovat (pro hodnotu 1 se nic nestane)
  • počet sloučených sloupců, zde tedy prvek zabere 4 sloupce

Poté vyrobíme první skupinu přepínačů pro přechody z jedné číselné soustavy do druhé a umístíme ji do layoutu pomocí mainLayout->addWidget(btnDec, 2, 1);... Tak přidáme i skupiny tlačítek: operationButtons->addButton((btnPlus));... Zde si lze všimnout, že dva parametry jsme vynechali, protože nejsou potřebné a Qt nám to umožňuje. Jednoduše jen volíme řádek a sloupec.

Pro jistotu znovu pro zapamatování: QGridLayout si své pozice čísluje od jedničky, nikoli od nuly jako pole v C.

leftOperand = new QLineEdit(); a rightOperand = new QLineEdit(); jsou dvě důležitá editační políčka k vložení dvou vstupních čísel. Samozřejmě je hned umístíme do "tabulky" rozložení prvků jako mainLayout->addWidget(leftOperand, 3, 1, 1, 2);. Každé z nich bude zabírat polovinu řádku, tedy 2 sloupce.

Následují "přepínače" pro početní operace. Zde asi není také není moc co vysvětlovat, protože je to stejný postup, který byl použit u *přepínačů" číselných soustav.

Celý layout nakonec vložíme do widgetu pomocí setLayout(mainLayout); a pro jistotu, aby bylo jasné, že počítáme v dekadické soustavě a základní operace je sčítání, nastavíme dané přepínače jako aktivní/zapnuté: btnDec->setChecked(true); a btnPlus->setChecked(true);

A zcela na konec si nastavíme ikonu a velikost okna: setWindowIcon(QIcon(":/img/mainIcon")); resize(640, 480);

Jako poslední máme tlačítko pro výpočet. Takové již jsme dělali skoro na počátku kurzu. Ovšem zde je drobná změna, která asi bude později dost důležitá a myslím, že se z ní stane i dost dobrý zvyk.

Mezinárodní rozhraní

btnEqual = new QPushButton(tr("Spočítej"));

Na kódu výše je samozřejmě zajímavé použití funkce tr(QString). Ta se dá vyložit jako: přelož řetězec. Tedy pokud všechny řetězce budeme uzavírat do této funkce, máme aplikaci připravenou k případným vícejazyčným mutacím. Během kurzu si tyto možnosti ještě představíme.

Závěr

Tím máme hotové rozvržení aplikace. Klidně si ji přeložte a spusťte. Sice nic zatím nedělá, ale vypadá hezky :-)

Příště, v lekci Jednoduchá kalkulačka v Qt a C++ - Model, si vytvoříme jednoduchý model. Sice je kalkulačka jednoduchá, ale pracujeme v MVC (MV) frameworku, tak by bylo dobré dodržet tuto architekturu. Opravdu se bude hodit později, když si osvojíme dobré návyky co nejdříve. Příště také ošetříme uživatelské vstupy a zprovozníme přepínače.


 

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

 

Předchozí článek
Řetězce v Qt - QString a QChar
Všechny články v sekci
Qt - Okenní/formulářové aplikace v C++
Přeskočit článek
(nedoporučujeme)
Jednoduchá kalkulačka v Qt a C++ - Model
Č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