diff --git a/events_and_signals/click.png b/events_and_signals/click.png new file mode 100644 index 0000000..acfcb2d Binary files /dev/null and b/events_and_signals/click.png differ diff --git a/events_and_signals/events_and_signals.md b/events_and_signals/events_and_signals.md new file mode 100644 index 0000000..fc7b5b5 --- /dev/null +++ b/events_and_signals/events_and_signals.md @@ -0,0 +1,436 @@ +# События и сигналы в Qt5 + +В этой части руководства по программированию Qt5 C++ мы говорим о событиях и сигналах. + +События являются важной частью любой программы с графическим интерфейсом. Все приложения с графическим интерфейсом управляются событиями. Приложение реагирует на различные типы событий, которые генерируются в течение его жизни. События генерируются в основном пользователем приложения. Но они могут быть созданы и другими способами, например. Интернет-соединение, оконный менеджер или таймер. В событийной модели есть три участника: + +- источник события +- объект события +- цель события + +Источником события является объект, состояние которого изменяется. Он генерирует События. Объект события (Event) инкапсулирует изменения состояния в источнике события. Цель события — это объект, который необходимо уведомить. Объект-источник события делегирует задачу обработки события цели события. + +Когда мы вызываем метод exec приложения, приложение входит в основной цикл. Основной цикл извлекает события и отправляет их объектам. Qt имеет уникальный механизм сигналов и слотов. Этот механизм сигналов и слотов является расширением языка программирования C++. + +Сигналы и слоты используются для связи между объектами. Сигнал испускается, когда происходит определенное событие. Слот — это обычный метод C++; он вызывается, когда излучается связанный с ним сигнал. + +## Пример клика Qt5 + +В первом примере показан очень простой пример обработки событий. У нас есть одна кнопка. Нажав на кнопку, мы завершаем работу приложения. + +click.h +``` +pragma once + +#include + +class Click : public QWidget { + + public: + Click(QWidget *parent = nullptr); +}; +``` + +click.cpp +``` +#include +#include +#include +#include "click.h" + +Click::Click(QWidget *parent) + : QWidget(parent) { + + auto *hbox = new QHBoxLayout(this); + hbox->setSpacing(5); + + auto *quitBtn = new QPushButton("Quit", this); + hbox->addWidget(quitBtn, 0, Qt::AlignLeft | Qt::AlignTop); + + connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit); +} + +``` +Мы отображаем QPushButton в окне. +``` +connect(quitBtn, &QPushButton::clicked, qApp, &QApplication::quit); +``` +Метод connect подключает сигнал к слоту. Когда мы нажимаем кнопку Quit, генерируется сигнал clicked. qApp — это глобальный указатель на объект приложения. Он определен в заголовочном файле . Метод quit вызывается, когда испускается сигнал щелчка. + +main.cpp +``` +#include +#include "click.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Click window; + + window.resize(250, 150); + window.setWindowTitle("Click"); + window.show(); + + return app.exec(); +} +``` +Это основной файл. + +![Click](click.png) + +В следующем примере мы реагируем на нажатие клавиши. + +keypress.h +``` +#pragma once + +#include + +class KeyPress : public QWidget { + + public: + KeyPress(QWidget *parent = 0); + + protected: + void keyPressEvent(QKeyEvent * e); +}; +``` + +keypress.cpp +``` +#include +#include +#include "keypress.h" + +KeyPress::KeyPress(QWidget *parent) + : QWidget(parent) +{ } + +void KeyPress::keyPressEvent(QKeyEvent *event) { + + if (event->key() == Qt::Key_Escape) { + qApp->quit(); + } +} +``` +Приложение завршается, если мы нажимаем Esc. +``` +void KeyPress::keyPressEvent(QKeyEvent *eevent) { + + if (e->key() == Qt::Key_Escape) { + qApp->quit(); + } +} +``` +Один из способов работы с событиями в Qt5 — переопределить обработчик событий. QKeyEvent — это объект события, который содержит информацию о том, что произошло. В нашем случае мы используем объект события, чтобы определить, какая клавиша была фактически нажата. + +main.cpp +``` +#include +#include "keypress.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + KeyPress window; + + window.resize(250, 150); + window.setWindowTitle("Key press"); + window.show(); + + return app.exec(); +} +``` + +## QMoveEvent + +Класс QMoveEvent содержит параметры событий для событий перемещения. События перемещения отправляются виджетам, которые были перемещены. + +move.h +``` +#pragma once + +#include + +class Move : public QWidget { + + Q_OBJECT + + public: + Move(QWidget *parent = 0); + + protected: + void moveEvent(QMoveEvent *e); +}; +``` + +move.cpp +``` +#include +#include "move.h" + +Move::Move(QWidget *parent) + : QWidget(parent) +{ } + +void Move::moveEvent(QMoveEvent *e) { + + int x = e->pos().x(); + int y = e->pos().y(); + + QString text = QString::number(x) + "," + QString::number(y); + + setWindowTitle(text); +} +``` +В нашем примере программирования кода мы реагируем на событие перемещения. Мы определяем текущие координаты x, y левого верхнего угла клиентской области окна и устанавливаем эти значения в заголовок окна. +``` +int x = e->pos().x(); +int y = e->pos().y(); +``` +Мы используем объект QMoveEvent для определения значений x, y. +``` +QString text = QString::number(x) + "," + QString::number(y); +``` +Мы конвертируем целочисленные значения в строки. +``` +setWindowTitle(text); +``` +Метод setWindowTitle задает текст заголовка окна. +main.cpp +``` +#include +#include "move.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Move window; + + window.resize(250, 150); + window.setWindowTitle("Move"); + window.show(); + + return app.exec(); +} +``` +![QMoveEvent](moveevent.png) + +## Отключение сигнала + +Сигнал может быть отключен от слота. Следующий пример показывает, как мы можем это сделать. + +disconnect.h +``` +#pragma once + +#include +#include + +class Disconnect : public QWidget { + + Q_OBJECT + + public: + Disconnect(QWidget *parent = 0); + + private slots: + void onClick(); + void onCheck(int); + + private: + QPushButton *clickBtn; +}; +``` +В заголовочном файле мы объявили два слота. Слоты — это не ключевое слово C++, это расширение Qt5. Эти расширения обрабатываются препроцессором до компиляции кода. Когда мы используем сигналы и слоты в наших классах, мы должны предоставить макрос Q_OBJECT в начале определения класса. В противном случае препроцессор будет жаловаться. + +disconnect.cpp +``` +#include +#include +#include +#include "disconnect.h" + +Disconnect::Disconnect(QWidget *parent) + : QWidget(parent) { + + QHBoxLayout *hbox = new QHBoxLayout(this); + hbox->setSpacing(5); + + clickBtn = new QPushButton("Click", this); + hbox->addWidget(clickBtn, 0, Qt::AlignLeft | Qt::AlignTop); + + QCheckBox *cb = new QCheckBox("Connect", this); + cb->setCheckState(Qt::Checked); + hbox->addWidget(cb, 0, Qt::AlignLeft | Qt::AlignTop); + + connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); + connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck); +} + +void Disconnect::onClick() { + + QTextStream out(stdout); + out << "Button clicked" << endl; +} + +void Disconnect::onCheck(int state) { + + if (state == Qt::Checked) { + connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); + } else { + disconnect(clickBtn, &QPushButton::clicked, this, + &Disconnect::onClick); + } +} +``` + +В нашем примере у нас есть кнопка и флажок. Флажок подключает и отключает слот от сигнала нажатия кнопок. Этот пример должен быть выполнен из командной строки. +``` +connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); +connect(cb, &QCheckBox::stateChanged, this, &Disconnect::onCheck); +``` +Здесь мы подключаем сигналы к нашим пользовательским слотам. +``` +void Disconnect::onClick() { + + QTextStream out(stdout); + out << "Button clicked" << endl; +} +``` +Если мы нажмем кнопку Click, мы отправим текст «Кнопка нажата» в окно терминала. +``` +void Disconnect::onCheck(int state) { + + if (state == Qt::Checked) { + connect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); + } else { + disconnect(clickBtn, &QPushButton::clicked, this, &Disconnect::onClick); + } +} +``` +Внутри слота onCheck мы подключаем или отключаем слот onClick от кнопки Click в зависимости от полученного состояния. +main.cpp +``` +#include +#include "disconnect.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Disconnect window; + + window.resize(250, 150); + window.setWindowTitle("Disconnect"); + window.show(); + + return app.exec(); +} + +``` + +## Таймер + +Таймер используется для выполнения одиночных или повторяющихся задач. Хорошим примером использования таймера являются часы; каждую секунду мы должны обновлять нашу метку, отображающую текущее время. +timer.h +``` +#pragma once + +#include +#include + +class Timer : public QWidget { + + public: + Timer(QWidget *parent = 0); + + protected: + void timerEvent(QTimerEvent *e); + + private: + QLabel *label; +}; +``` +timer.cpp +``` +#include "timer.h" +#include +#include + +Timer::Timer(QWidget *parent) + : QWidget(parent) { + + QHBoxLayout *hbox = new QHBoxLayout(this); + hbox->setSpacing(5); + + label = new QLabel("", this); + hbox->addWidget(label, 0, Qt::AlignLeft | Qt::AlignTop); + + QTime qtime = QTime::currentTime(); + QString stime = qtime.toString(); + label->setText(stime); + + startTimer(1000); +} + +void Timer::timerEvent(QTimerEvent *e) { + + Q_UNUSED(e); + + QTime qtime = QTime::currentTime(); + QString stime = qtime.toString(); + label->setText(stime); +} +``` +В нашем примере, мы отобразим на экране местное время. +``` +label = new QLabel("", this); +``` +Для отображения времени мы используем метку виджета. +``` +QTime qtime = QTime::currentTime(); +QString stime = qtime.toString(); +label->setText(stime); +``` +Здесь мы определяем текущее местное время. Мы устанавливаем его в метку виджета. +``` +startTimer(1000); +``` +Мы запускаем таймер. Каждые 1000 мс генерируется событие таймера. +``` +void Timer::timerEvent(QTimerEvent *e) { + + Q_UNUSED(e); + + QTime qtime = QTime::currentTime(); + QString stime = qtime.toString(); + label->setText(stime); +} +``` +To work with timer events, we must reimplement the timerEvent method. +main.cpp +``` +#include +#include "timer.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Timer window; + + window.resize(250, 150); + window.setWindowTitle("Timer"); + window.show(); + + return app.exec(); +} +``` +![Timer](timer.png) + +Эта глава была посвящена событиям и сигналам в Qt5. + diff --git a/events_and_signals/moveevent.png b/events_and_signals/moveevent.png new file mode 100644 index 0000000..1b5eb04 Binary files /dev/null and b/events_and_signals/moveevent.png differ diff --git a/events_and_signals/timer.png b/events_and_signals/timer.png new file mode 100644 index 0000000..183d5c5 Binary files /dev/null and b/events_and_signals/timer.png differ diff --git a/layout_management/absolute1.png b/layout_management/absolute1.png new file mode 100644 index 0000000..9c1a71b Binary files /dev/null and b/layout_management/absolute1.png differ diff --git a/layout_management/buttons.png b/layout_management/buttons.png new file mode 100644 index 0000000..7710506 Binary files /dev/null and b/layout_management/buttons.png differ diff --git a/layout_management/calculator.png b/layout_management/calculator.png new file mode 100644 index 0000000..877583d Binary files /dev/null and b/layout_management/calculator.png differ diff --git a/layout_management/layoutmanagement.md b/layout_management/layoutmanagement.md new file mode 100644 index 0000000..05f054e --- /dev/null +++ b/layout_management/layoutmanagement.md @@ -0,0 +1,633 @@ +# Управление макетом в Qt5 + +В этой части руководства по программированию на Qt5 мы поговорим об управлении компоновкой виджетов. Мы упоминаем менеджеры QHBoxLayout, QVBoxLayout, QFormLayout и QGridLayout. + +Типичное приложение состоит из различных виджетов. Эти виджеты размещаются внутри макетов. Программист должен управлять макетом приложения. В Qt5 у нас есть два варианта: + + абсолютное позиционирование + менеджеры компоновки + +## Абсолютное позиционирование + +Программист указывает положение и размер каждого виджета в пикселях. Когда мы используем абсолютное позиционирование, мы должны понимать несколько вещей. + + Размер и положение виджета не меняются, если мы меняем размер окна. + Приложения выглядят по-разному (часто плохо) на разных платформах. + Изменение шрифтов в нашем приложении может испортить макет. + Если мы решим изменить наш макет, мы должны полностью переделать наш макет, что утомительно и требует много времени. + +Могут быть ситуации, когда мы можем использовать абсолютное позиционирование. Но в основном в реальных программах программисты используют менеджеры компоновки. + +absolute.cpp +``` +#include +#include +#include + +class Absolute : public QWidget { + + public: + Absolute(QWidget *parent = nullptr); +}; + +Absolute::Absolute(QWidget *parent) + : QWidget(parent) { + + auto *ledit = new QTextEdit(this); + ledit->setGeometry(5, 5, 200, 150); +} + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Absolute window; + + window.setWindowTitle("Absolute"); + window.show(); + + return app.exec(); +} +``` +Метод setGeometry используется для позиционирования виджета в окне в абсолютных координатах. +``` +auto *edit = new QTextEdit(this); +ledit->setGeometry(5, 5, 200, 150); +``` +Мы создаем виджет QTextEdit и позиционируем его вручную. Метод setGeometry делает две вещи: позиционирует виджет в абсолютных координатах и изменяет размер виджета. +![pic 1](absolute1.png) + +## Qt5 QVBoxLayout + +Класс QVBoxLayout выстраивает виджеты вертикально. Виджеты добавляются в макет с помощью метода addWidget. + +vertical_box.h +``` +#pragma once + +#include + +class VerticalBox : public QWidget { + + public: + VerticalBox(QWidget *parent = nullptr); +}; +``` + +vertical_box.cpp +``` +#include +#include +#include "vertical_box.h" + +VerticalBox::VerticalBox(QWidget *parent) + : QWidget(parent) { + + auto *vbox = new QVBoxLayout(this); + vbox->setSpacing(1); + + auto *settings = new QPushButton("Settings", this); + settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto *accounts = new QPushButton("Accounts", this); + accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto *loans = new QPushButton("Loans", this); + loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto *cash = new QPushButton("Cash", this); + cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto *debts = new QPushButton("Debts", this); + debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + vbox->addWidget(settings); + vbox->addWidget(accounts); + vbox->addWidget(loans); + vbox->addWidget(cash); + vbox->addWidget(debts); + + setLayout(vbox); +} +``` +В нашем примере у нас есть один менеджер вертикальной компоновки. В него вставляем пять кнопок. Мы делаем все кнопки расширяемыми в обе стороны. +``` +auto *vbox = new QVBoxLayout(this); +vbox->setSpacing(1); +``` +Мы создаем QVBoxLayout и устанавливаем интервал в 1 пиксель между дочерними виджетами. +``` +auto *settings = new QPushButton("Settings", this); +settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +``` +Мы создаем кнопку и устанавливаем для нее политику размера. Дочерние виджеты управляются менеджером компоновки. По умолчанию кнопка расширяется по горизонтали и имеет фиксированный размер по вертикали. Если мы хотим изменить его, мы устанавливаем новую политику размера. В нашем случае кнопка расширяется в обе стороны. +``` +vbox->addWidget(settings); +vbox->addWidget(accounts); +``` +Мы добавляем дочерние виджеты в менеджер компоновки с помощью метода addWidget. +setLayout(vbox); + +Мы устанавливаем менеджер QVBoxLayout для окна. + +main.cpp +``` +#include +#include "vertical_box.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + VerticalBox window; + + window.resize(240, 230); + window.setWindowTitle("VerticalBox"); + window.show(); + + return app.exec(); +} +``` + +![pic 2](verticalbox.png) + +## Кнопки + +В следующем примере мы отображаем две кнопки в клиентской области окна. Они будут расположены в правом нижнем углу окна. + +buttons.h +``` +#pragma once + +#include +#include + +class Buttons : public QWidget { + + public: + Buttons(QWidget *parent = nullptr); + + private: + QPushButton *okBtn; + QPushButton *applyBtn; +}; +``` + +buttons.cpp +``` +#include +#include +#include "buttons.h" + +Buttons::Buttons(QWidget *parent) + : QWidget(parent) { + + auto *vbox = new QVBoxLayout(this); + auto *hbox = new QHBoxLayout(); + + okBtn = new QPushButton("OK", this); + applyBtn = new QPushButton("Apply", this); + + hbox->addWidget(okBtn, 1, Qt::AlignRight); + hbox->addWidget(applyBtn, 0); + + vbox->addStretch(1); + vbox->addLayout(hbox); +} +``` +Скажем, мы хотели иметь две кнопки в правом нижнем углу окна. +``` +auto *vbox = new QVBoxLayout(this); +auto *hbox = new QHBoxLayout(); +``` +Мы создаем два менеджера размещения блоков: один вертикальный и один горизонтальный менеджер размещения блоков. +``` +okBtn = new QPushButton("OK", this); +applyBtn = new QPushButton("Apply", this); +``` +Создаем две кнопки. +``` +hbox->addWidget(okBtn, 1, Qt::AlignRight); +hbox->addWidget(applyBtn, 0); +``` +Кнопки размещены внутри менеджера горизонтальной компоновки. с помощью метода addWidget. Эти кнопки выровнены по правому краю. Первый параметр — это дочерний виджет. Второй параметр — коэффициент растяжения, а последний параметр — выравнивание. Установив коэффициент растяжения на 1 для кнопки OK, мы даем ей пространство от левой до правой стороны окна. Виджет не расширяется на все отведенное ему пространство. Наконец, константа Qt::AlignRight выравнивает виджет справа от выделенного пространства. +``` +vbox->addStretch(1); +vbox->addLayout(hbox); +``` +Мы помещаем пустое расширяемое пространство в вертикальное поле, вызывая метод addStretch. Затем мы добавляем горизонтальный макет блока к вертикальному макету блока. + +main.cpp +``` +#include +#include "buttons.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Buttons window; + + window.resize(290, 170); + window.setWindowTitle("Buttons"); + window.show(); + + return app.exec(); +} +``` +![pic 3](buttons.png) + +## Вложенные макеты Qt5 + +Идея следующего примера состоит в том, чтобы показать, что менеджеры компоновки можно комбинировать. Комбинируя даже простые макеты, мы можем создавать сложные диалоги или окна. Для вложения макетов мы используем метод addLayout. + +nesting.h +``` +#pragma once + +#include + +class Layouts : public QWidget { + + public: + Layouts(QWidget *parent = nullptr); +}; +``` + +nesting.cpp +``` +#include +#include +#include +#include "nesting.h" + +Layouts::Layouts(QWidget *parent) + : QWidget(parent) { + + auto *vbox = new QVBoxLayout(); + auto *hbox = new QHBoxLayout(this); + + auto *lw = new QListWidget(this); + lw->addItem("The Omen"); + lw->addItem("The Exorcist"); + lw->addItem("Notes on a scandal"); + lw->addItem("Fargo"); + lw->addItem("Capote"); + + auto *add = new QPushButton("Add", this); + auto *rename = new QPushButton("Rename", this); + auto *remove = new QPushButton("Remove", this); + auto *removeall = new QPushButton("Remove All", this); + + vbox->setSpacing(3); + vbox->addStretch(1); + vbox->addWidget(add); + vbox->addWidget(rename); + vbox->addWidget(remove); + vbox->addWidget(removeall); + vbox->addStretch(1); + + hbox->addWidget(lw); + hbox->addSpacing(15); + hbox->addLayout(vbox); + + setLayout(hbox); +} +``` + +В примере мы создаем окно, состоящее из четырех кнопок и одного виджета списка. Кнопки сгруппированы в вертикальный столбец и размещены справа от виджета списка. Если мы изменим размер окна, размер виджета списка также изменится. +``` +auto *vbox = new QVBoxLayout(); +``` +QVBoxLayout — это столбец для кнопок. +``` +auto *hbox = new QHBoxLayout(this); +``` +QHBoxLayout — это базовый макет для виджетов. +``` +auto *lw = new QListWidget(this); +lw->addItem("The Omen"); +lw->addItem("The Exorcist"); +lw->addItem("Notes on a scandal"); +lw->addItem("Fargo"); +lw->addItem("Capote"); +``` +QListWidget создан. +``` +auto *add = new QPushButton("Add", this); +auto *rename = new QPushButton("Rename", this); +auto *remove = new QPushButton("Remove", this); +auto *removeall = new QPushButton("Remove All", this); +``` +Здесь мы создаем наши четыре кнопки. +``` +vbox->setSpacing(3); +vbox->addStretch(1); +vbox->addWidget(add); +vbox->addWidget(rename); +vbox->addWidget(remove); +vbox->addWidget(removeall); +vbox->addStretch(1); +``` +Создан вертикальный блок с четырьмя кнопками. Мы оставляем немного места между нашими кнопками. Обратите внимание, что мы добавляем коэффициент растяжения вверху и внизу вертикального прямоугольника. Таким образом, кнопки располагаются вертикально по центру. +``` +hbox->addWidget(lw); +hbox->addSpacing(15); +hbox->addLayout(vbox); +``` +Виджет списка и вертикальный блок кнопок помещаются в макет горизонтального блока. Метод addLayout используется для добавления макета в другой макет. +``` +setLayout(hbox); +``` +Мы устанавливаем базовый макет для родительского окна. + +main.cpp +``` +#include +#include "nesting.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Layouts window; + + window.setWindowTitle("Layouts"); + window.show(); + + return app.exec(); +} +``` + +![pic 4](nesting.png) + +##Qt5 FormLayout + +QFormLayout — это простой менеджер компоновки, который управляет формами виджетов ввода и связанными с ними метками. Он размещает своих дочерних элементов в виде двух столбцов. Левый столбец состоит из меток, а правый столбец состоит из виджетов ввода, таких как QLineEdit или QSpinBox. + +form.h +``` +#pragma once + +#include + +class FormEx : public QWidget { + + public: + FormEx(QWidget *parent = nullptr); +}; +``` + +form.cpp +``` +#include +#include +#include +#include "form.h" + +FormEx::FormEx(QWidget *parent) + : QWidget(parent) { + + auto *nameEdit = new QLineEdit(this); + auto *addrEdit = new QLineEdit(this); + auto *occpEdit = new QLineEdit(this); + + auto *formLayout = new QFormLayout; + formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); + formLayout->addRow("Name:", nameEdit); + formLayout->addRow("Email:", addrEdit); + formLayout->addRow("Age:", occpEdit); + + setLayout(formLayout); +} +``` +В примере создается форма, состоящая из трех меток и трех строк редактирования. +``` +auto *formLayout = new QFormLayout; +``` +Создается экземпляр QFormLayout. +formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); + +С помощью метода setLabelAlignment мы устанавливаем выравнивание виджетов меток. +``` +formLayout->addRow("Name:", nameEdit); +``` +Метод addRow добавляет новую строку в нижнюю часть макета формы с заданной меткой и виджетом ввода. + +main.cpp +``` +#include +#include "form.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + FormEx window; + + window.setWindowTitle("Form example"); + window.show(); + + return app.exec(); +} +``` +![pic 5](simpleform.png) + +## Qt5 QGridLayout + +QGridLayout помещает свои виджеты в сетку. Это мощный менеджер компоновки. + +calculator.h +``` +#pragma once + +#include + +class Calculator : public QWidget { + + public: + Calculator(QWidget *parent = nullptr); +}; +``` + +calculator.cpp +``` +#include +#include +#include "calculator.h" + +Calculator::Calculator(QWidget *parent) + : QWidget(parent) { + + auto *grid = new QGridLayout(this); + grid->setSpacing(2); + + QVector values({ "7", "8", "9", "/", + "4", "5", "6", "*", + "1", "2", "3", "-", + "0", ".", "=", "+" + }); + + int pos = 0; + + for (int i=0; i<4; i++) { + for (int j=0; j<4; j++) { + + auto *btn = new QPushButton(values[pos], this); + btn->setFixedSize(40, 40); + grid->addWidget(btn, i, j); + pos++; + } + } + + setLayout(grid); +} +``` +Создаем скелет калькулятора. +``` +auto *grid = new QGridLayout(this); +grid->setSpacing(2) +``` +Мы создаем макет сетки и устанавливаем интервал 2 пикселя между дочерними виджетами. +``` +QVector values({ "7", "8", "9", "/", + "4", "5", "6", "*", + "1", "2", "3", "-", + "0", ".", "=", "+" +}); +``` +Это символы, которые отображаются на кнопках. +``` +for (int i=0; i<4; i++) { + for (int j=0; j<4; j++) { + + auto *btn = new QPushButton(values[pos], this); + btn->setFixedSize(40, 40); + grid->addWidget(btn, i, j); + pos++; + } +} +``` +Мы размещаем шестнадцать виджетов в макете сетки. Каждая из кнопок имеет фиксированный размер. + +main.cpp +``` +#include +#include "calculator.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Calculator window; + + window.setWindowTitle("Calculator"); + window.show(); + + return app.exec(); +} +``` +![pic 6](calculator.png) + +## Обзор + +В следующем примере этой главы мы создаем более сложное окно с помощью менеджера QGridLayout. + +review.h +``` +#pragma once + +#include + +class Review : public QWidget { + + public: + Review(QWidget *parent = nullptr); +}; +``` + +review.cpp +``` +#include +#include +#include +#include +#include "review.h" + +Review::Review(QWidget *parent) + : QWidget(parent) { + + auto *grid = new QGridLayout(this); + grid->setVerticalSpacing(15); + grid->setHorizontalSpacing(10); + + auto *title = new QLabel("Title:", this); + grid->addWidget(title, 0, 0, 1, 1); + title->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + auto *edt1 = new QLineEdit(this); + grid->addWidget(edt1, 0, 1, 1, 1); + + auto *author = new QLabel("Author:", this); + grid->addWidget(author, 1, 0, 1, 1); + author->setAlignment(Qt::AlignRight | Qt::AlignVCenter); + + auto *edt2 = new QLineEdit(this); + grid->addWidget(edt2, 1, 1, 1, 1); + + auto *review = new QLabel("Review:", this); + grid->addWidget(review, 2, 0, 1, 1); + review->setAlignment(Qt::AlignRight | Qt::AlignTop); + + auto *te = new QTextEdit(this); + grid->addWidget(te, 2, 1, 3, 1); + + setLayout(grid); +} +``` +Код создает окно, которое можно использовать для ввода автора, названия и рецензии на книгу. +``` +auto *grid = new QGridLayout(this); +``` +Менеджер QGridLayout создан. +``` +grid->setVerticalSpacing(15); +grid->setHorizontalSpacing(10); +``` +Мы добавляем вертикальный интервал с помощью метода setVerticalSpacing и горизонтальный интервал с помощью метода setHorizontalSpacing. +``` +auto *title = new QLabel("Title", this); +grid->addWidget(title, 0, 0, 1, 1); +``` +Эти строки кода создают виджет метки и помещают его в макет сетки. Метод addWidget имеет пять параметров. Первый параметр — это дочерний виджет, в нашем случае метка. Следующие два параметра — это строка и столбец в сетке, где мы размещаем метку. Наконец, последними параметрами являются rowspan и colspan. Эти параметры определяют, сколько строк будет охватывать текущий виджет. В нашем случае метка будет охватывать только один столбец и одну строку. +``` +title->setAlignment(Qt::AlignRight | Qt::AlignVCenter); +``` +Метод setAlignment выравнивает метку заголовка в своей ячейке. По горизонтали он выровнен по правому краю. По вертикали он центрирован. +``` +auto *te = new QTextEdit(this); +grid->addWidget(te, 2, 1, 3, 1); +``` +Виджет QTextEdit помещается в третью строку и второй столбец; он охватывает три строки и один столбец. + +main.cpp +``` +#include +#include "review.h" + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + Review window; + + window.setWindowTitle("Review"); + window.show(); + + return app.exec(); +} +``` +![pic 7](review.png) + +Эта часть руководства по Qt5 была посвящена управлению компоновкой. + diff --git a/layout_management/nesting.png b/layout_management/nesting.png new file mode 100644 index 0000000..282069b Binary files /dev/null and b/layout_management/nesting.png differ diff --git a/layout_management/review.png b/layout_management/review.png new file mode 100644 index 0000000..21fd0d9 Binary files /dev/null and b/layout_management/review.png differ diff --git a/layout_management/simpleform.png b/layout_management/simpleform.png new file mode 100644 index 0000000..4f3819b Binary files /dev/null and b/layout_management/simpleform.png differ diff --git a/layout_management/verticalbox.png b/layout_management/verticalbox.png new file mode 100644 index 0000000..1126cff Binary files /dev/null and b/layout_management/verticalbox.png differ