Pyqt5: первые программы
Содержание:
mainwindow.h
Объявляем дополнительные СЛОТы в заголовочном файле. Это СЛОТы для вызова контекстного меню, и удаления записи. Также необходимо переписать сигнатуру СЛОТа для редактирования записи, поскольку будет использоваться иной способ определения выбранной записи.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QSqlTableModel>
#include <database.h>
#include <dialogadddevice.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_addDeviceButton_clicked();
void slotUpdateModels();
/* К СЛОТу по редактировнию записи
* добавляем СЛОТ по удалению записи.
* Также добавляем СЛОТ для обработки вызова контекстного меню
* */
void slotEditRecord();
void slotRemoveRecord();
void slotCustomMenuRequested(QPoint pos);
private:
Ui::MainWindow *ui;
DataBase *db;
QSqlTableModel *modelDevice;
private:
void setupModel(const QString &tableName, const QStringList &headers);
void createUI();
};
#endif // MAINWINDOW_H
Абсолютное позиционирование
При абсолютном позиционировании программист указывает в пикселях положение и размер каждого виджета. При использовании этого способа размещения виджетов следует знать, что:
размер и положение виджета не изменяются при изменении размера окна;
приложения выглядят по-разному (часто плохо) на разных платформах;
изменение шрифтов в приложении может испортить компоновку;
если мы решим изменить расположение элементов на форме, то должны будем полностью переделать всю компоновку, что, в свою очередь, является довольно утомительным и трудоёмким процессом.
Несомненно, есть примеры, в которых нам никто не запрещает использовать способ абсолютного позиционирования элементов. Но в основном, в реальных проектах, программисты стараются вместо этого использовать менеджеры компоновки.
Перейдём к рассмотрению примера, в котором задействован метод setGeometry() для размещения виджета в окне с использованием абсолютных координат.
Файл реализации — absolute.cpp:
#include <QApplication>
#include <QDesktopWidget>
#include <QTextEdit>
class Absolute : public QWidget {
public:
Absolute(QWidget *parent = 0);
};
Absolute::Absolute(QWidget *parent)
: QWidget(parent) {
QTextEdit *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();
}
|
1 |
#include <QApplication> classAbsolutepublicQWidget{ public Absolute(QWidget*parent=); }; Absolute::Absolute(QWidget*parent) QWidget(parent){ QTextEdit*ledit=newQTextEdit(this); ledit->setGeometry(5,5,200,150); } intmain(intargc,char*argv){ QApplication app(argc,argv); Absolute window; window.setWindowTitle(«Absolute»); window.show(); returnapp.exec(); } |
Здесь мы создаём виджет и вручную размещаем его. Метод setGeometry() выполняет две функции: позиционирует виджет в абсолютных координатах и изменяет его размер.
QTextEdit *edit = new QTextEdit(this);
ledit->setGeometry(5, 5, 200, 150);
|
1 |
QTextEdit*edit=newQTextEdit(this); ledit->setGeometry(5,5,200,150); |
Результат выполнения программы выше:

mainwindow.h
В заголовочном файле требуется добавить только СЛОТы для обработки нажатий управляющих кнопок и СЛОТ для получения номера нажатой динамической кнопки.
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
/* My Includes */
#include <qdynamicbutton.h>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private slots:
void on_addButton_clicked(); // СЛОТ-обработчик нажатия кнопки добавления
void on_deleteButton_clicked(); // СЛОТ-обработчик нажатия кнопки удаления
void slotGetNumber(); // СЛОТ для получения номера нажатой динамической кнопки
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
/* Для удобства работы слои разделены QSplitter
* */
ui->splitter->setStretchFactor(0,1);
ui->splitter->setStretchFactor(1,0);
}
MainWindow::~MainWindow()
{
delete ui;
}
/* Метод для добавления динамической кнопки
* */
void MainWindow::on_addButton_clicked()
{
QDynamicButton *button = new QDynamicButton(this); // Создаем объект динамической кнопки
/* Устанавливаем текст с номером этой кнопки
* */
button->setText("Кнопочка " + QString::number(button->getID()));
/* Добавляем кнопку в слой с вертикальной компоновкой
* */
ui->verticalLayout->addWidget(button);
/* Подключаем сигнал нажатия кнопки к СЛОТ получения номера кнопки
* */
connect(button, SIGNAL(clicked()), this, SLOT(slotGetNumber()));
}
/* Метод для удаления динамической кнопки по её номеру
* */
void MainWindow::on_deleteButton_clicked()
{
/* Выполняем перебор всех элементов слоя, где располагаются динамические кнопки
* */
for(int i = 0; i < ui->verticalLayout->count(); i++){
/* Производим каст элемента слоя в объект динамической кнопки
* */
QDynamicButton *button = qobject_cast<QDynamicButton*>(ui->verticalLayout->itemAt(i)->widget());
/* Если номер кнопки соответствует числу, которое установлено
* в lineEdit, то производим удаление данной кнопки
* */
if(button->getID() == ui->lineEdit->text().toInt()){
button->hide();
delete button;
}
}
}
/* СЛОТ для получения номера кнопки.
* */
void MainWindow::slotGetNumber()
{
/* Определяем объект, который вызвал сигнал
* */
QDynamicButton *button = (QDynamicButton*) sender();
/* После чего устанавливаем номер кнопки в lineEdit,
* который содержится в данной динамической кнопке
* */
ui->lineEdit->setText(QString::number(button->getID()));
/* То есть номер кнопки устанавливается в поле lineEdit только тогда,
* когда мы нажимаем одну из динамических кнопок, и этот номер соответствует
* номеру нажатой кнопки
* */
}
Filling in the Missing Pieces
Begin by designing the user interface and then move on to filling in the missing code. Finally, add the find functionality.
Designing the User Interface

- In the Editor mode, double-click the textfinder.ui file in the Projects view to launch the integrated Qt Designer.
- Drag and drop the following widgets to the form:
- Label (QLabel)
- Line Edit (QLineEdit)
- Push Button (QPushButton)
Note: To easily locate the widgets, use the search box at the top of the Sidebar. For example, to find the Label widget, start typing the word label.
- Double-click the Label widget and enter the text Keyword.
- Double-click the Push Button widget and enter the text Find.
- In the Properties view, change the objectName to findButton.
- Press Ctrl+A (or Cmd+A) to select the widgets and select Lay out Horizontally (or press Ctrl+H on Linux or Windows or Ctrl+Shift+H on ) to apply a horizontal layout (QHBoxLayout).
- Drag and drop a Text Edit widget (QTextEdit) to the form.
- Select the screen area, and then select Lay out Vertically (or press Ctrl+L) to apply a vertical layout (QVBoxLayout).
Applying the horizontal and vertical layouts ensures that the application UI scales to different screen sizes.
- To call a find function when users select the Find button, you use the Qt signals and slots mechanism. A signal is emitted when a particular event occurs and a slot is a function that is called in response to a particular signal. Qt widgets have predefined signals and slots that you can use directly from Qt Designer. To add a slot for the find function:
- Right-click the Find button to open a context-menu.
- Select Go to Slot > clicked(), and then select OK.
A private slot, , is added to the header file, textfinder.h and a private function, , is added to the source file, textfinder.cpp.
- Press Ctrl+S (or Cmd+S) to save your changes.
For more information about designing forms with Qt Designer, see the Qt Designer Manual.
Completing the Header File
The textfinder.h file already has the necessary #includes, a constructor, a destructor, and the object. You need to add a private function, , to read and display the contents of the input text file in the QTextEdit.
- In the Projects view in the Edit view, double-click the file to open it for editing.
- Add a private function to the section, after the pointer, as illustrated by the following code snippet:
private slots: void on_findButton_clicked(); private: Ui::TextFinder *ui; void loadTextFile();
Completing the Source File
Now that the header file is complete, move on to the source file, textfinder.cpp.
- In the Projects view in the Edit view, double-click the textfinder.cpp file to open it for editing.
- Add code to load a text file using QFile, read it with QTextStream, and then display it on with (). This is illustrated by the following code snippet:
void TextFinder::loadTextFile() { QFile inputFile(":/input.txt"); inputFile.open(QIODevice::ReadOnly); QTextStream in(&inputFile); QString line = in.readAll(); inputFile.close(); ui->textEdit->setPlainText(line); QTextCursor cursor = ui->textEdit->textCursor(); cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1); } - To use QFile and QTextStream, add the following #includes to textfinder.cpp:
#include <QFile> #include <QTextStream>
- For the slot, add code to extract the search string and use the () function to look for the search string within the text file. This is illustrated by the following code snippet:
void TextFinder::on_findButton_clicked() { QString searchString = ui->lineEdit->text(); ui->textEdit->find(searchString, QTextDocument::FindWholeWords); } - Once both of these functions are complete, add a line to call in the constructor, as illustrated by the following code snippet:
TextFinder::TextFinder(QWidget *parent) : QWidget(parent), ui(new Ui::TextFinder) { ui->setupUi(this); loadTextFile(); }
The slot is called automatically in the uic generated ui_textfinder.h file by this line of code:
QMetaObject::connectSlotsByName(TextFinder);
Creating a Resource File
You need a resource file (.qrc) within which you embed the input text file. The input file can be any .txt file with a paragraph of text. Create a text file called input.txt and store it in the textfinder folder.
To add a resource file:
- Select File > New File or Project > Qt > Qt Resource File > Choose.
The Choose the Location dialog opens.
- In the Name field, enter textfinder.
- In the Path field, enter , and select Next or Continue.
The Project Management dialog opens.
- In the Add to project field, select TextFinder.pro and select Finish or Done to open the file in the code editor.
- Select Add > Add Prefix.
- In the Prefix field, replace the default prefix with a slash (/).
- Select Add > Add Files, to locate and add input.txt.
Writing a Main Function
Many of the GUI examples provided with Qt follow the pattern of having a file, which contains the standard code to initialize the application, plus any number of other source/header files that contain the application logic and custom GUI components.
A typical function in looks like this:
#include <QtWidgets>
int main(int argc, char *argv)
{
QApplication app(argc, argv);
return app.exec();
}
First, a QApplication object is constructed, which can be configured with arguments passed in from the command line. After the widgets have been created and shown, () is called to start Qt’s event loop. Control passes to Qt until this function returns. Finally, returns the value returned by ().
1 Виджет счетчика
Наш счетчик отображает какое-либо значение в двоичном виде, при этом единице соответствует кружок красного цвета, а нулю — белого. Счетчик обладает разрядностью и периодичностью, с которой будет увеличиваться хранимое значение.
На листинг 1 приведен исходный код файла заголовка счетчика.
#ifndef COUNTER_H
# define COUNTER_H
# include <QWidget>
# include <QVector>
//!< двоичный счетчик
class Counter :public QWidget {
Q_OBJECT
public:
Counter(QWidget* parent);
virtual ~Counter();
void set(int capacity, int delay);
//!< установка разрядности и задержки таймера
protected slots:
void on_tick();
//!< обработка сигнала таймера
void on_start();
//!< обработка сигнала запуска
void on_stop();
//!< обработка сигнала остановки
protected:
virtual void paintEvent(QPaintEvent *);
int m_cap;
//!< количество разрядов
QVector<bool> m_val;
//!< отображаемое значение
QTimer *m_timer;
//!< таймер
};
#endif // COUNTER_H
На листинг 1 все достаточно просто, я отмечу лишь один момент. Мы вынужденно написали метод set, для установки параметров счетчика, хотя, очевидно, что такую настройку можно было внести в конструктор. Дело в том, что конструктор классов, которые мы собираемся использовать в Qt Designer должны принимать лишь указатель на QWidget.
Реализация методов класса Counter дана на листинг 2, при этом, стоит отметить, что при создании таймера, в качестве родительского элемента передается 0, а не this (6 строка) — это, также, связано с тем, что наш счетчик будет использоваться Qt Designer-ом (он не должен содержать дочерних элементов). В связи с этим, деструктор счетчика должен освобождать из под таймера память (автоматическая сборка мусора не сработает).
#include "counter.h"
#include <QPainter>
#include <QTimer>
Counter::Counter(QWidget *parent)
:QWidget(parent), m_cap(0), m_timer(0) {
m_timer = new QTimer(0);
connect(m_timer, SIGNAL(timeout()), this, SLOT(on_tick()));
}
Counter::~Counter() { delete m_timer; }
void Counter::set(int capacity, int delay) {
m_timer->stop();
m_cap = capacity;
m_val.clear();
m_val.fill(false, m_cap);
m_timer->start(delay);
}
void Counter::on_tick() {
for (int i = m_cap - 1; i >= 0; --i) {
if (false == m_val) {
m_val = true;
break;
}
m_val = false;
}
repaint();
}
void Counter::on_start() { m_timer->start(); }
void Counter::on_stop() { m_timer->stop(); }
/*virtual*/ void Counter::paintEvent(QPaintEvent *) {
QPainter painter(this);
if (0 == m_cap) return;
const int size = qMin(height(), width() / m_cap) - 1;
for (int i = 0; i < m_cap; ++i) {
painter.setBrush(QBrush(QColor(255, 0, 0, m_val ? 255 : 0)));
painter.drawEllipse(i * size, 0, size, size);
}
}
Теперь у нас есть счетчик, который мы встроим в форму, созданную Qt Designer.
Building The Examples
If you installed a binary package to get Qt, or if you compiled Qt yourself, the examples described in this tutorial should already be built and ready to run. If you wish to modify and recompile them, follow these steps:
- From a command prompt, enter the directory containing the example you have modified.
- Type and press Return. If this doesn’t work, make sure that the executable is on your path, or enter its full location.
- On Linux/Unix and macOS, type and press Return; on Windows with Visual Studio, type and press Return.
An executable file is created in the current directory. On Windows, this file may be located in a or subdirectory. You can run this executable to see the example code at work.
Виджет QCheckBox
QCheckBox — это виджет чекбокса (англ. «checkbox»), состоящий из ячейки и подписи к ней. имеет 2 состояния: включено или выключено. При включенном состоянии, внутри ячейки отображается флажок (галочка или крестик), при выключенном — ничего.
В следующем примере мы выведем в окне виджет чекбокса. Если у чекбокса установлен флажок, то будет выводиться заголовок окна, в противном случае заголовок окна будет скрыт.
Заголовочный файл — checkbox.h:
#pragma once
#include <QWidget>
class CheckBox : public QWidget {
Q_OBJECT
public:
CheckBox(QWidget *parent = 0);
private slots:
void showTitle(int);
};
|
1 |
#pragma once classCheckBoxpublicQWidget{ Q_OBJECT public CheckBox(QWidget*parent=); privateslots voidshowTitle(int); }; |
Выводим чекбокс в окне и подключаем его к слоту showTitle().
Файл реализации — checkbox.cpp:
#include <QCheckBox>
#include <QHBoxLayout>
#include «checkbox.h»
CheckBox::CheckBox(QWidget *parent)
: QWidget(parent) {
QHBoxLayout *hbox = new QHBoxLayout(this);
QCheckBox *cb = new QCheckBox(«Show Title», this);
cb->setCheckState(Qt::Checked);
hbox->addWidget(cb, 0, Qt::AlignLeft | Qt::AlignTop);
connect(cb, &QCheckBox::stateChanged, this, &CheckBox::showTitle);
}
void CheckBox::showTitle(int state) {
if (state == Qt::Checked) {
setWindowTitle(«QCheckBox»);
} else {
setWindowTitle(» «);
}
}
|
1 |
#include <QCheckBox> CheckBox::CheckBox(QWidget*parent) QWidget(parent){ QHBoxLayout*hbox=newQHBoxLayout(this); QCheckBox*cb=newQCheckBox(«Show Title»,this); cb->setCheckState(Qt::Checked); hbox->addWidget(cb,,Qt::AlignLeft|Qt::AlignTop); connect(cb,&QCheckBox::stateChanged,this,&CheckBox::showTitle); } voidCheckBox::showTitle(intstate){ if(state==Qt::Checked){ setWindowTitle(«QCheckBox»); }else{ setWindowTitle(» «); } } |
Флажок устанавливается при запуске примера:
cb->setCheckState(Qt::Checked);
| 1 | cb->setCheckState(Qt::Checked); |
Определяем состояние флажка и вызываем метод setWindowTitle():
void CheckBox::showTitle(int state) {
if (state == Qt::Checked) {
setWindowTitle(«QCheckBox»);
} else {
setWindowTitle(» «);
}
}
|
1 |
voidCheckBox::showTitle(intstate){ if(state==Qt::Checked){ setWindowTitle(«QCheckBox»); }else{ setWindowTitle(» «); } } |
Главный файл программы — main.cpp:
#include <QApplication>
#include «checkbox.h»
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CheckBox window;
window.resize(250, 150);
window.setWindowTitle(«QCheckBox»);
window.show();
return app.exec();
}
|
1 |
#include <QApplication> intmain(intargc,char*argv){ QApplication app(argc,argv); CheckBox window; window.resize(250,150); window.setWindowTitle(«QCheckBox»); window.show(); returnapp.exec(); } |
Результат:
Detailed Description
Toolbar buttons are added by adding actions, using () or (). Groups of buttons can be separated using () or (). If a toolbar button is not appropriate, a widget can be inserted instead using () or (). Examples of suitable widgets are QSpinBox, QDoubleSpinBox, and QComboBox. When a toolbar button is pressed, it emits the () signal.
A toolbar can be fixed in place in a particular area (e.g., at the top of the window), or it can be movable between toolbar areas; see (), (), () and ().
When a toolbar is resized in such a way that it is too small to show all the items it contains, an extension button will appear as the last item in the toolbar. Pressing the extension button will pop up a menu containing the items that do not currently fit in the toolbar.
Widgets
Widgets are the primary elements for creating user interfaces in Qt. can display data and status information, receive user input, and provide a container for other widgets that should be grouped together. A widget that is not embedded in a parent widget is called a window.

The QWidget class provides the basic capability to render to the screen, and to handle user input events. All UI elements that Qt provides are either subclasses of QWidget, or are used in connection with a QWidget subclass. Creating custom widgets is done by subclassing QWidget or a suitable subclass and reimplementing the virtual event handlers.
- Window and Dialog Widgets
- Application Main Window
- Dialog Windows
- Keyboard Focus in Widgets
Detailed Description
Most actions in an application are represented as items in menus or buttons in toolbars. However sometimes more complex widgets are necessary. For example a zoom action in a word processor may be realized using a QComboBox in a QToolBar, presenting a range of different zoom levels. QToolBar provides () as convenience function for inserting a single widget. However if you want to implement an action that uses custom widgets for visualization in multiple containers then you have to subclass QWidgetAction.
If a QWidgetAction is added for example to a QToolBar then () is called. Reimplementations of that function should create a new custom widget with the specified parent.
If the action is removed from a container widget then () is called with the previously created custom widget as argument. The default implementation hides the widget and deletes it using ().
If you have only one single custom widget then you can set it as default widget using (). That widget will then be used if the action is added to a QToolBar, or in general to an action container that supports QWidgetAction. If a QWidgetAction with only a default widget is added to two toolbars at the same time then the default widget is shown only in the first toolbar the action was added to. QWidgetAction takes over ownership of the default widget.
Note that it is up to the widget to activate the action, for example by reimplementing mouse event handlers and calling ().
macOS: If you add a widget to a menu in the application’s menu bar on macOS, the widget will be added and it will function but with some limitations:
Каркас приложения
Сейчас мы попробуем создать заготовку, которая может стать основой для вашего будущего, уже более сложного, приложения. Пример основан на возможностях виджета .
Заголовочный файл — skeleton.h:
#pragma once
#include <QMainWindow>
#include <QApplication>
class Skeleton : public QMainWindow {
Q_OBJECT
public:
Skeleton(QWidget *parent = 0);
};
|
1 |
#pragma once classSkeletonpublicQMainWindow{ Q_OBJECT public Skeleton(QWidget*parent=); }; |
Создаём элементы меню, панель инструментов и панель состояния.
Файл с реализацией — skeleton.cpp:
#include «skeleton.h»
#include <QToolBar>
#include <QIcon>
#include <QAction>
#include <QMenu>
#include <QMenuBar>
#include <QStatusBar>
#include <QTextEdit>
Skeleton::Skeleton(QWidget *parent)
: QMainWindow(parent) {
QPixmap newpix(«new.png»);
QPixmap openpix(«open.png»);
QPixmap quitpix(«quit.png»);
QAction *quit = new QAction(«&Quit», this);
QMenu *file;
file = menuBar()->addMenu(«&File»);
file->addAction(quit);
connect(quit, &QAction::triggered, qApp, &QApplication::quit);
QToolBar *toolbar = addToolBar(«main toolbar»);
toolbar->addAction(QIcon(newpix), «New File»);
toolbar->addAction(QIcon(openpix), «Open File»);
toolbar->addSeparator();
QAction *quit2 = toolbar->addAction(QIcon(quitpix), «Quit Application»);
connect(quit2, &QAction::triggered, qApp, &QApplication::quit);
QTextEdit *edit = new QTextEdit(this); // создаём виджет QTextEdit
setCentralWidget(edit); // помещаем созданный виджет в центр виджета QMainWindow
statusBar()->showMessage(«Ready»); // показываем в нижней панели приложения сообщение «Ready»
}
|
1 |
#include «skeleton.h» Skeleton::Skeleton(QWidget*parent) QMainWindow(parent){ QPixmap newpix(«new.png»); QPixmap openpix(«open.png»); QPixmap quitpix(«quit.png»); QAction*quit=newQAction(«&Quit»,this); QMenu*file; file=menuBar()->addMenu(«&File»); file->addAction(quit); connect(quit,&QAction::triggered,qApp,&QApplication::quit); QToolBar*toolbar=addToolBar(«main toolbar»); toolbar->addAction(QIcon(newpix),»New File»); toolbar->addAction(QIcon(openpix),»Open File»); toolbar->addSeparator(); QAction*quit2=toolbar->addAction(QIcon(quitpix),»Quit Application»); connect(quit2,&QAction::triggered,qApp,&QApplication::quit); QTextEdit*edit=newQTextEdit(this);// создаём виджет QTextEdit setCentralWidget(edit);// помещаем созданный виджет в центр виджета QMainWindow statusBar()->showMessage(«Ready»);// показываем в нижней панели приложения сообщение «Ready» } |
Главный файл приложения — main.cpp:
#include «skeleton.h»
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
Skeleton window;
window.resize(350, 250);
window.setWindowTitle(«Application skeleton»);
window.show();
return app.exec();
}
|
1 |
#include «skeleton.h» intmain(intargc,char*argv){ QApplication app(argc,argv); Skeleton window; window.resize(350,250); window.setWindowTitle(«Application skeleton»); window.show(); returnapp.exec(); } |
Результат:
Заключение
Мы разобрали несколько примеров с использованием различных менеджеров компоновки. Теперь вы можете самостоятельно попробовать изменить параметры компоновок и понаблюдать за результатом. Посмотрите, как будут перестраиваться виджеты в форме при изменении размеров формы. Вспомните, как задать минимальный размер формы и задайте его так, чтобы не допускать искажения виджетов. Попробуйте сделать собственные проекты с использованием рассмотренных менеджеров компоновки. Используя данную статью, расширяйте своё знакомство с виджетами, самостоятельно устанавливая их в ячейки компоновщика.