Portál AbcLinuxu, 24. října 2025 06:48
V tomto díle si ukážeme, jak vytvořit hlavní okno, jak uložit a načíst nastavení a nakonec, jak by mohl vypadat program pro jednoduché malování.
Spousta programů má ve svém hlavním okně nabídku (menubar), panely nástrojů (toolbary), stavový řádek (status bar) a podobně. Qt pro tyto účely obsahuje třídu QMainWindow, která vytvoření hlavního okna usnadňuje. Následuje jednoduchý demonstrační program – je to pouze widget, takže je třeba vytvořit ještě main.cpp, viz předchozí díl.
mainwindow.h: Zde bych zmínil tzv. dopřednou deklaraci (forward declaration), která může zkrátit dobu potřebnou k sestavení programu. Použité třídy, jejichž API v hlavičkovém souboru nepotřebujeme (třeba členské proměnné), takto dopředu deklarujeme a #include dáme až do implementačního .cpp souboru. Kompilátor díky tomu nebude zbytečně víckrát parsovat hromadu hlavičkových souborů, jen aby se dozvěděl, že taková třída opravdu existuje (protože to je jediná informace, kterou v tomto bodě potřebuje). Přímo vývojáři Qt tvrdí, že to v některých případech může mít poměrně velký vliv na dobu kompilace.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
// dopředná deklarace
class QDir;
class QMessageBox;
class QListWidget;
class QListWidgetItem;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// v konstruktoru nepředáváme rodiče, protože jde o hlavní okno
MainWindow();
private:
QDir* m_dir;
QListWidget* m_list;
QString m_dName;
private slots:
void openDir(QString = QString());
void itemDoubleClicked(QListWidgetItem*);
};
#endif
mainwindow.cpp: Všimněte si slotu itemDoubleClicked, kterému je předán ukazatel na uživatelem dvojkliknutou položku seznamu QListWidgetItem.
#include "mainwindow.h"
#include <QApplication>
#include <QMenuBar>
#include <QToolBar>
#include <QAction>
#include <QFileDialog>
#include <QDir>
#include <QFileInfo>
#include <QMessageBox>
#include <QListWidget>
MainWindow::MainWindow() : m_dir(0)
{
// vytvoříme panel s nabídkou
QMenuBar* menuBar = new QMenuBar(this);
// jednotlivá menu
QMenu* menuFile = new QMenu(tr("&File"));
QMenu* menuHelp = new QMenu(tr("&Help"));
// položky menu
QAction* actionOpen = new QAction(tr("&Open"), this);
QAction* actionQuit = new QAction(tr("&Quit"), this);
QAction* actionAboutQt = new QAction(tr("About Qt"), this);
// nastavení klávesové zkratky
actionOpen->setShortcut(QKeySequence("Ctrl+O"));
// propojení položek menu s funkcemi programu
connect(actionOpen, SIGNAL(triggered()), this, SLOT(openDir()));
connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
connect(actionAboutQt, SIGNAL(triggered()), qApp, SLOT(aboutQt()));
// přidáme položky do patřičných nabídek...
menuFile->addAction(actionOpen);
menuFile->addAction(actionQuit);
menuHelp->addAction(actionAboutQt);
//... a jednotlivá menu do panelu
menuBar->addMenu(menuFile);
menuBar->addMenu(menuHelp);
// řekneme widgetu, aby použil naše menu
setMenuBar(menuBar);
// vytvoříme grafický seznam položek
m_list = new QListWidget(this);
// a nastavíme jej jako hlavní widget hlavního okna
setCentralWidget(m_list);
// dvojklik na seznam spustí náš slot
connect(m_list, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
this, SLOT(itemDoubleClicked(QListWidgetItem*)));
resize(600, 400);
}
void MainWindow::itemDoubleClicked(QListWidgetItem* item)
{
// tato třída poskytuje informace o souborech a adresářích
QFileInfo fileInfo(m_dName + "/" + item->text());
if(fileInfo.isDir())
openDir(fileInfo.canonicalFilePath());
else
{
// dialogy dovedou pracovat s jednoduchým HTML
QString infotext = "<b>Name</b>: " + fileInfo.fileName()
+ "<br/><b>Location</b>: " + fileInfo.canonicalPath()
+ "<br/><b>Owner</b>: " + fileInfo.owner()
+ "<br/><b>Group</b>: " + fileInfo.group();
QMessageBox::information(this, tr("File info"), infotext);
}
}
void MainWindow::openDir(QString dName)
{
if(!dName.isEmpty())
m_dName = dName;
else
do // dialog pro výběr existujícího adresáře
m_dName = QFileDialog::getExistingDirectory(this, tr("Choose a directory"));
while(m_dName.isEmpty());
m_dir = new QDir(m_dName);
// Zde by bylo vhodné kontrolovat přístupová práva,
// a patřičně na ně reagovat. Takto program nedostatečné
// oprávnění ignoruje a seznam se vyprázdní.
// vyprázdníme seznam
m_list->clear();
// přidáme položky z adresáře do seznamu
m_list->addItems(m_dir->entryList(QDir::AllEntries, QDir::Name));
delete m_dir;
}

Velmi často se hodí možnost ukládat (a poté zpětně načítat) nastavení programu, a proto si ukážeme, jak to je jednoduché s třídou QSettings. Konfigurace se ukládá do souboru ~/.config/organizace/název_programu.conf. Pokud budete chtít ukládat veškerou konfiguraci programu do jednoho souboru (což je běžné), pak je vhodné nastavit si název organizace a programu již do instance QApplication ve funkci main(), abyste tyto údaje nemuseli zadávat pokaždé do konstruktoru QSettings:
qApp->setOrganizationName("Organizace");
qApp->setApplicationName("SuperProgram");
// konfigurace bude v ~/.config/Organizace/SuperProgram.conf
Asi nejdůležitější 2 metody třídy QSettings jsou:
void setValue(const QString& key, const QVariant& value) - nastaví položce key hodnotu value.QVariant value(const QString& key, const QVariant& defaultValue = QVariant()) - vrátí hodnotu položky key. Pro případ, že tato položka není v nastavení, můžete nastavit volitelnou výchozí hodnotu.Co je to QVariant? Jde o jakýsi kontejner, který dovede pojmout libovolný datový typ. Chová se jako union pro základní datové typy C++ a Qt. Když chcete z proměnné tohoto typu dostat hodnotu, musíte vědět, jakého je hodnota typu (k tomu může v nejhorším pomoci metoda typeName(), která vrátí název typu jako C řetězec) a zavolat patřičnou metodu toType(), kde Type nahradíte za patřičný datový typ:
QVariant test(true); bool b = test.toBool(); QVariant numero(123); int n = numero.toInt();
A zde konečně přichází ukázkový program. Zkuste si napsat něco do textového pole a potom na spinboxu vybrat nějaké číslo. Nastavení uložte tlačítkem Save, program ukončete a tlačítko Load by vaše nastavení mělo načíst.
settings.h:
#ifndef SETTINGS_H
#define SETTINGS_H
#include <QWidget>
#include <QSettings>
class QLineEdit;
class QSpinBox;
class Settings : public QWidget
{
Q_OBJECT
public:
Settings();
private:
QSettings m_settings;
QLineEdit* m_lineEdit;
QSpinBox* m_spinBox;
public slots:
void loadSettings();
void saveSettings();
};
#endif // SETTINGS_H
settings.cpp:
#include "settings.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QSpinBox>
#include <QPushButton>
Settings::Settings()
{
m_lineEdit = new QLineEdit();
m_spinBox = new QSpinBox();
//tlačítka pro načtení a uložení nastavení
QPushButton* load = new QPushButton(tr("Load"));
QPushButton* save = new QPushButton(tr("Save"));
connect(load, SIGNAL(clicked()), this, SLOT(loadSettings()));
connect(save, SIGNAL(clicked()), this, SLOT(saveSettings()));
QHBoxLayout* wLayout = new QHBoxLayout();
wLayout->addWidget(m_lineEdit, 1);
wLayout->addWidget(m_spinBox);
QHBoxLayout* btnLayout = new QHBoxLayout();
btnLayout->addWidget(load);
btnLayout->addWidget(save);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addLayout(wLayout);
layout->addLayout(btnLayout);
setLayout(layout);
}
// načte nastavení - hodnoty načtené z konfiguračního souboru přiřadí patřičným objektům
void Settings::loadSettings()
{
m_lineEdit->setText( m_settings.value("lineEdit", QString()).toString() );
m_spinBox->setValue( m_settings.value("spinBox", 0).toInt() );
}
// uloží nastavení
void Settings::saveSettings()
{
m_settings.setValue("lineEdit", m_lineEdit->text());
m_settings.setValue("spinBox", m_spinBox->value());
}

Na události jsme už jednou narazili v minulém díle, když jsme reimplementovali událost dvojkliku na speciální tlačítko. Tentokrát si ovšem kromě událostí myši zkusíme napsat i paintEvent(), což je funkce, která má za úkol vykreslit a následně překreslovat widget. Překreslit můžeme také pouze danou oblast, takže toho pro efektivitu využijeme a ukážeme si, jak napsat velice jednoduché Malování :-)
painting.h:
#ifndef PAINTING_H
#define PAINTING_H
#include <QWidget>
#include <QPainter>
class Painting : public QWidget
{
public:
Painting(QWidget *parent = 0);
protected:
// hýbání myší
virtual void mouseMoveEvent(QMouseEvent*);
// stlačení tlačítka myši
virtual void mousePressEvent(QMouseEvent*);
// puštění tlačítka myši
virtual void mouseReleaseEvent(QMouseEvent*);
// událost kreslení
virtual void paintEvent(QPaintEvent* e);
private:
bool m_painting;
};
#endif // PAINTING_H
painting.cpp:
#include "painting.h"
#include <QMouseEvent>
Painting::Painting(QWidget *parent)
: QWidget(parent), m_painting(false)
{
// bílé pozadí
setPalette(QPalette(QColor(255, 255, 255)));
setAutoFillBackground(true);
}
// povolí kreslení
void Painting::mousePressEvent(QMouseEvent* e)
{
m_painting = true;
e->ignore();
}
// zakáže kreslení
void Painting::mouseReleaseEvent(QMouseEvent* e)
{
m_painting = false;
e->ignore();
}
// pokud je povolené kreslení, aktualizuje pixel pod kurzorem
void Painting::mouseMoveEvent(QMouseEvent* e)
{
if(!m_painting)
return;
update(e->x(), e->y(), 1, 1);
}
// vykreslíme pixel, který máme aktualizovat
void Painting::paintEvent(QPaintEvent* e)
{
QPainter p(this);
// nastaví barvu "pera"
p.setPen(Qt::black);
// vykreslí tečku na daném bodě
p.drawPoint(e->rect().x(), e->rect().y());
}

QPainter toho dokáže mnohem víc, než jen kreslit tečky, ale podrobněji to v tomto seriálu nebudeme rozebírat, takže zájemce odkazuji na dokumentaci.
V příštím díle si mj. ukážeme, jak navrhnout GUI pomocí Qt Designeru a jak s takovým návrhem potom dále pracovat.
. Jinak mám celkově dojem že pokud bych prostudoval kód Doliho programů, tak se dozvím to samé
. Celkově mi jde o to, že by bylo možná lepší, se zaměřit na lidi co chtějí Qt začít.
. No by mě zajímalo jestli sem v tu dobu tomu kodu rozuměl
Musím souhlasit, že seriál není cílen pro úplné začátečníky a cílová skupina tak bude chudší než asi bylo zamýšleno. Ovšem pokud si je čtenář jistý svými C++ znalostmi, měl by se poměrně rychle problematikou prokousat.
Jediné, co by bylo pro úplné začátečníky přínosnější, by byla lokalizace celé Qt dokumentace do češtiny. K tomu ale není důvod, protože pokud není člověk schopný zvládnout (alespoň pasivně) technický anglický text tak, aby pochopil myšlenku (není potřeba překládat každé slovo a chápat gramatiku), nikdy z něj nebude programátor. Alespoň ne takový, který svede více než jen nahradu "Hello world!" za "Ahoj svete" (I když taky jsem měl radost jak decko, když se mi před mnoha lety podařilo poprvé přeložil změny v jednom demu podobného rozsahu
)
Musím souhlasit, že seriál není cílen pro úplné začátečníky a cílová skupina tak bude chudší než asi bylo zamýšleno.Nesouhlas
Ovšem pokud si je čtenář jistý svými C++ znalostmi, měl by se poměrně rychle problematikou prokousat.Jak je řečeno jinde, článek je pro začátečníky v Qt, ne v C++
Já tenhle seriál vítám, alespoň se konečně dozvím, proč to je právě takhle 
Jsem programátor v C, shell, PL/SQL. C++ jsem se ještě stále nenaučil, mám jen jakési povědomí o objektech a jak to zhruba funguje, ale to mi nezabránilo spáchat jeden plugin do kate (Konečně jsem jej začal přepisovat pro KDE 4) a podílet se na vývoji jednoho plasmoidu (yaWP - jestli ho někdo zná). Něco si najdu v tutoriálech, něco najdu v jiných programech a inspiruji se, zbytek metodou pokus-omyl. Nejhorší to bývá při překladu. Některé hlášky jsou docela zajímavé a co je vlastně špatně člověk většinou zjistí až s pomocí Googlu.
.
Bez QSettings by byl svět Qt aplikací docela smutnej
Není zač.
MainWindow::MainWindow() : m_dir(0)). Podle me jsou dost dobre voleny na to, aby z nich bylo pekne jasne k cemu jednotlive Qt objekty slouzi a jak se s nimi delaji zakladni veci. Podle me je to pekny start pro programatory do Qt.
MainWindow::MainWindow()
{
m_dir = 0;
...
m_dir jsem inicializoval, abych pak nemusel podmiňovat delete a mohl ho prostě spustit ať už se m_dir použije nebo ne. Je to bezpečnější.
NULLou
Ale taky bych řekl, že minimálně u tohoto seriálu by bylo dobrý to trochu (nebo klidně hodně) roztáhnout už v defaultu.
Vetsina takovych IDE se da ovladat pouze intuici, neni treba nic studovat ani nic slozite nastavovat (mozna par cest). A ze jich neni malo, Qt Creator, KDevelop, QDevelop, Monkey Studio, HaiQ, Vim a urcite jsem jich dost zapomel. Zakladni schopnosti jako doplnovani kodu, editaci *.ui, kompilaci, debugovani, atd. podporuji svym zpusobem vsechny. Dobre IDE neznamena, ze se musi chovat a vypadat jako MSVS. Je treba vyzkouset vsechny a jit cestou nejmensiho odporu. Napr. u me je urcite neschudny vi, prestoze si jini bez nej nedokazi pocitac ani predstavit...
To já jsem se to teda nejdřív musel naučit, celkem závidím
Když pochopíš princip, tak je i vim intuitivní.
dw - delete word
Nicméně netvrdím, že bych vim nějak extra uměl. Většinou se totiž pohybuji na různých AIX serverech, kde je jen vi, tak jsem se naučil jen pár základních příkazů, které potřebuji.
Ja a Vim nejsme kamaradi
Ale pokud clovek ovlada Vim, verim, ze neni problem ho pouzivat i jako IDE pro Qt. (uz jsem tu par screenshotu videl)
Právě jsem na jeden narazil...
Možná mi někdo poradí. Tvrdě jsem narazil s naprostou trivialitou 
Mám tři proměnné QColor, potřebuji je zazálohovat, změnit na default , něco vykreslit a pak obnovit ze zálohy. Bohužel to obnovení mi nějak nefunguje
Zkoušel jsem různé varianty, studoval dokumentaci ke QColor, ale zatím nic nezabralo. Jak už jsem psal, v C++ celkem dost plavu 
if ( m_theme != "default" && m_theme != "naked" ) {
// set default colors when custom colors and not default or naked theme
QColor fontColorBck = m_fontColor;
QColor fontLowerColorBck = m_fontLowerColor;
QColor fontShadowColorBck = m_fontShadowColor;
setDefaultColors();
}
.....
if ( m_theme != "default" && m_theme != "naked" ) {
// restore font colors
// tady mi to nějak drhne
m_fontColor = fontColorBck;
m_fontLowerColor = fontLowerColorBck;
m_fontShadowColor = fontShadowColorBck;
}
/usr/local/src/yawp/yawp/0.1/yawp.cpp: In member function ‘void YaWP::paintPanel(QPainter*, const QStyleOptionGraphicsItem*, const QRect&)’: /usr/local/src/yawp/yawp/0.1/yawp.cpp:781: error: ‘fontColorBck’ was not declared in this scope /usr/local/src/yawp/yawp/0.1/yawp.cpp:782: error: ‘fontLowerColorBck’ was not declared in this scope /usr/local/src/yawp/yawp/0.1/yawp.cpp:783: error: ‘fontShadowColorBck’ was not declared in this scope make[2]: *** [CMakeFiles/plasma_yawp.dir/yawp.o] Error 1 make[1]: *** [CMakeFiles/plasma_yawp.dir/all] Error 2 make: *** [all] Error 2
Proměnné fontColorBck, fontLowerColorBck a fontShadowColorBck jsou platné jenom v tom bloku if ( m_theme != "default" && m_theme != "naked" ) { ... }. Nejpřímočařejší postup (netvrdím, že nejlepší) by byl asi takový:
QColor fontColorBck;
QColor fontLowerColorBck;
QColor fontShadowColorBck;
if ( m_theme != "default" && m_theme != "naked" ) {
// set default colors when custom colors and not default or naked theme
fontColorBck = m_fontColor;
fontLowerColorBck = m_fontLowerColor;
fontShadowColorBck = m_fontShadowColor;
setDefaultColors();
}
.....
if ( m_theme != "default" && m_theme != "naked" ) {
// restore font colors
m_fontColor = fontColorBck;
m_fontLowerColor = fontLowerColorBck;
m_fontShadowColor = fontShadowColorBck;
}
Nejsa Céčkař, nejsem si jistý, jestli překladač nebude prudit, že ty proměnné might not be initialized, ale to je jen takový detail
Díky moc, na tohle jsem úplně zapomněl, už to funguje. Taková blbost a já se s tím od včerejška trápím.
To je tak, když člověk v něčem delší dobu nedělá, pak všechno zapomene 
Constructs an invalid color with the RGB value (0, 0, 0). An invalid color is a color that is not properly set up for the underlying window system.
The alpha value of an invalid color is unspecified.
QStettings jsem neznal, zajímavé. Dá se nastavit nějaká úplně jiná cesta k .conf?
QSettings ( const QString & fileName, Format format, QObject * parent = 0 )
Mrkni na pretizene konstruktory, napr.:
QSettings::QSettings(const QString& fileName, Format format, QObject* parent = 0)
Na jednom tydennim oficialnim skoleni, na ktere jsem byl vyslan, se nam skolitel snazil mimo jineho vysvetlit, proc nepouzivat QtDesigner a proc radeji kreslit gui primo z kodu. Je pravda, ze designer nepodporuje spoustu veci, jako vlozeni na toolbar neco jineho nez toolbutton nebo akci z menu, obdobne se statusbarem, moznost rozdelit lokalizaci textu pro jednotne a mnozne cislo (pripadne vice tvaru mnozneho cisla (1 zprava, 2 zpravy, 5 zprav, ...)) nebo napr. pouziti tzv. stretch factoru...
Ale je pravda, ze stale pouzivam spise designer, protoze vyjma ruznych widgetu na tool/status-baru jsem zatim byl schopny vyjmenovane nevyhody elegantne obejit 
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.