GUI в Python. Коротко о PyQT5

PyQt – один из самых распространённых инструментариев для создания графического интерфейса пользователя (GUI) на языке Python. В данной статье рассмотрим несколько его возможностей, которых с лихвой хватит для покрытия большинства задач, с которыми мы сталкиваемся каждый день.
Весь код графического интерфейса можно писать самостоятельно с нуля. А можно воспользоваться конструктором Qt Designer, который входит в состав Anaconda (в более старых версия его можно установить через pip install). В этом конструкторе можно собрать все необходимые элементы в одном или нескольких окнах, как того требует функциональность или личные предпочтения.
Итак, предположим, что перед нами стоит задача написать программу с GUI, которая будет разбивать один исходный файл с данными по разным городам на несколько конечных файлов, содержащих данные по каждому городу отдельно. В Qt Designer соберём вот такую конструкцию:

На основной форме (QWidget) мы разместили следующие элементы:
- QLineEdit под именем lineEdit. В нём будет отображено расположение выбранного файла.
- QToolButton под именем toolButton. Эта кнопка (…) будет вызывать окно для выбора файла с исходными данными.
- Три кнопки QPushButton (pushButton, pushButton_2, pushButton_3) для запуска процесса разбиения (Ок), закрытия окна (Закрыть) и предварительного чтения файла (Прочитать файл) соответственно.
- QCheckBox под именем checkBox. Этот элемент (Только выбранные) позволит ограничить итоговый набор файлов по выбранным условиям.
- QListWidget под именем listWidget. Здесь будет отображаться список городов, которые присутствуют в исходном файле. И здесь же можно будет выбрать только те города, по которым мы хотим получить данные. В свойстве selectionMode этого объекта выбираем MultiSelection.
Когда наш макет готов, сохраняем его. На этом этапе у нас есть файл sample.ui, содержащий информацию в формате XML. Конвертируем в привычный python-код командой
pyuic5 sample.ui –o sample.py
в командной строке. Получаем следующий код:
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(464, 230) self.pushButton = QtWidgets.QPushButton(Form) self.pushButton.setGeometry(QtCore.QRect(10, 180, 75, 23)) self.pushButton.setObjectName("pushButton") self.lineEdit = QtWidgets.QLineEdit(Form) self.lineEdit.setGeometry(QtCore.QRect(9, 9, 133, 20)) self.lineEdit.setObjectName("lineEdit") self.toolButton = QtWidgets.QToolButton(Form) self.toolButton.setGeometry(QtCore.QRect(140, 8, 22, 22)) self.toolButton.setObjectName("toolButton") self.pushButton_2 = QtWidgets.QPushButton(Form) self.pushButton_2.setGeometry(QtCore.QRect(90, 180, 75, 23)) self.pushButton_2.setObjectName("pushButton_2") self.checkBox = QtWidgets.QCheckBox(Form) self.checkBox.setGeometry(QtCore.QRect(10, 140, 141, 21)) self.checkBox.setObjectName("checkBox") self.pushButton_3 = QtWidgets.QPushButton(Form) self.pushButton_3.setGeometry(QtCore.QRect(10, 50, 133, 23)) self.pushButton_3.setObjectName("pushButton_3") self.listWidget = QtWidgets.QListWidget(Form) self.listWidget.setGeometry(QtCore.QRect(190, 9, 256, 192)) self.listWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection) self.listWidget.setObjectName("listWidget") self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) self.pushButton.setText(_translate("Form", "Ok")) self.toolButton.setText(_translate("Form", ". ")) self.pushButton_2.setText(_translate("Form", "Закрыть")) self.checkBox.setText(_translate("Form", "Только выбранные")) self.pushButton_3.setText(_translate("Form", "Прочитать файл")) self.listWidget.setSortingEnabled(False)
Приступим к написанию кода основной программы. Нам потребуются дополнительные библиотеки:
import sys import pandas as pd from PyQt5.QtWidgets import QFileDialog
Следующие функции необходимо размещать в классе Ui_Form. Для выбора файла напишем функцию с QFileDialog.getOpenFileName():
def open_dialog_box(self): filename = QFileDialog.getOpenFileName() self.path = filename[0] self.lineEdit.setText(self.path)
Читаем выбранный файл и заполняем наш listWidget:
def readfile(self): self.df = pd.read_excel(self.path) self.uniqueCity = self.df["city"].unique() self.listWidget.clear() for item in self.uniqueCity: self.listWidget.addItem(item)
Не забудем про выход из программы:
def quit(self): sys.exit()
В следующей функции проверяется, отмечена ли опция «Только выбранные». Если да, то будут созданы только файлы по выбранным в окне listWidget городам. Если нет, то будут созданы файлы по всем города, которые имеются в исходном файле.
def job(self): if self.checkBox.checkState() == 2: for city in self.listWidget.selectedItems(): self.df[self.df['city']==city.text()].to_excel(f"excel_.xlsx", index=None) else: for city in self.uniqueCity: self.df[self.df['city']==city].to_excel(f"excel_.xlsx", index=None)
Теперь свяжем написанные функции с элементами на форме, а также создадим два датафрейма, которые используются в функциях выше. Этот код необходимо дописать в функцию setupUi:
self.toolButton.clicked.connect(self.open_dialog_box) self.pushButton_3.clicked.connect(self.readfile) self.pushButton.clicked.connect(self.job) self.pushButton_2.clicked.connect(self.quit) self.uniqueCity = pd.DataFrame() self.df = pd.DataFrame()
Осталось только написать код для запуска нашей программы:
if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) Form = QtWidgets.QMainWindow() ui = Ui_Form() ui.setupUi(Form) Form.show() sys.exit(app.exec_())
Готово. Запускаем программу, выбираем файл, читаем его, ставим при необходимости отметку «Только выбранные», отмечаем нужные нам города, кликаем «Ок» и получаем отчеты в разрезе городов.

Мы описали лишь небольшую часть инструментов библиотеки PyQt, но большинство остальных программируется аналогичным способом. Теперь вашей программой могут пользоваться другие коллеги без необходимости разбираться в коде для ввода своих данных.
Как обратится к ‘name’ объектов PyQt через цикл c подменой цифры в имени объекта?
Как обратиться к имени объектов, например label_1 , label_2 , label_3 ,
где будет перебирать цикл элементы через id переменную self.label_( id ).setText() ? Есть у данного способа минусы и посоветуете ли вы так делать?
from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(230, 230) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.label_1 = QtWidgets.QLabel(self.centralwidget) self.label_1.setGeometry(QtCore.QRect(30, 20, 47, 13)) self.label_1.setObjectName("label_1") self.label_2 = QtWidgets.QLabel(self.centralwidget) self.label_2.setGeometry(QtCore.QRect(30, 40, 47, 13)) self.label_2.setObjectName("label_2") self.label_3 = QtWidgets.QLabel(self.centralwidget) self.label_3.setGeometry(QtCore.QRect(30, 60, 47, 13)) self.label_3.setObjectName("label_3") self.label_4 = QtWidgets.QLabel(self.centralwidget) self.label_4.setGeometry(QtCore.QRect(30, 80, 47, 13)) self.label_4.setObjectName("label_4") self.label_5 = QtWidgets.QLabel(self.centralwidget) self.label_5.setGeometry(QtCore.QRect(30, 100, 47, 13)) self.label_5.setObjectName("label_5") self.pushButton = QtWidgets.QPushButton(self.centralwidget) self.pushButton.setGeometry(QtCore.QRect(20, 130, 181, 41)) self.pushButton.setObjectName("pushButton") self.lineEdit_1 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_1.setGeometry(QtCore.QRect(70, 20, 113, 20)) self.lineEdit_1.setObjectName("lineEdit_1") self.lineEdit_2 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_2.setGeometry(QtCore.QRect(70, 40, 113, 20)) self.lineEdit_2.setObjectName("lineEdit_2") self.lineEdit_3 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_3.setGeometry(QtCore.QRect(70, 60, 113, 20)) self.lineEdit_3.setObjectName("lineEdit_3") self.lineEdit_4 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_4.setGeometry(QtCore.QRect(70, 80, 113, 20)) self.lineEdit_4.setObjectName("lineEdit_4") self.lineEdit_5 = QtWidgets.QLineEdit(self.centralwidget) self.lineEdit_5.setGeometry(QtCore.QRect(70, 100, 113, 20)) self.lineEdit_5.setObjectName("lineEdit_5") MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 230, 21)) self.menubar.setObjectName("menubar") MainWindow.setMenuBar(self.menubar) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName("statusbar") MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.pushButton.clicked.connect(self.wrt_parametr) # как преобразовать функцию ниже в цикл с обращением к имени объектов? def wrt_parametr(self): self.label_1.setText(self.lineEdit_1.text()) self.label_2.setText(self.lineEdit_2.text()) self.label_3.setText(self.lineEdit_3.text()) self.label_4.setText(self.lineEdit_4.text()) self.label_5.setText(self.lineEdit_5.text()) #for id in range(5): #self.label_(id).setText(self.lineEdit_(id).text()) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) self.label_1.setText(_translate("MainWindow", "1")) self.label_2.setText(_translate("MainWindow", "2")) self.label_3.setText(_translate("MainWindow", "3")) self.label_4.setText(_translate("MainWindow", "4")) self.label_5.setText(_translate("MainWindow", "5")) self.pushButton.setText(_translate("MainWindow", "Присвоить значения")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
Пишем интерфейс на PyQt5, Пример готовой программы
Если вдруг так получилось, что для вашего проекта на Python понадобился интерфейс, а возиться с Tkinter не хочется, очевидным решением станет PyQt5. Из плюсов стоит отметить легкость и быстроту построения интерфейса, из минусов — это очень большой размер исполняемого файла (если такой конечно будет), по сравнению с тем же Tkinter.
Установка
Для работы нам понадобится библиотека для питона pyqt5 и Qt Creator. Как и большинство библиотек в Питоне, проще всего их ставить через имеющийся pip. Открываем командную строку (или PowerShell) с правами администратора и вводим
pip install pyqt5

Теперь переходим к установке Qt Creator, в нем нам нужен только дизайнер.
Заходим на сайт qt.io, жмем «Start for free»

Далее «Desktop & Mobile applications»


Наконец, мы получаем ссылочку на заветный инсталятор

По ходу установки не забываем проверить наличие компонента Qt Creator (он уже отмечен, но проверьте на всякий случай).

Создание простого интерфейса в QtCreator
После запуска создаем новый проект, не вдаваясь в подробности жмем далее, далее.


В списке компонентов проекта ищем файл формы, щелкаем по нему два раза, откроется редактор интерфейса.


Создаем простенький интерфейс и сохраняем.

Переходим в папку с проектом и копируем файл mainwindow.ui в папку Scripts, которая находится в папке с Python’ом, и вводим след. команду
./pyuic5.exe mainwindow.ui -o gui.py

Полученный gui.py перемещаем в папку с питоновским проектом и подключаем соответствующим кодом. (В данном примере главным файлом является main.py, в котором далее мы подключаем gui.py) (Скопировать код)

Пробуем запустить и ..

Работает 🙂 Но пока это просто интерфейс, давайте придадим ему жизни, добавив событие на кнопку.
Привязка события на нажатие кнопки
Идем обратно в main.py и связываем кнопку с функцией, которая передает текст в текстовое окно. Привязка события на кнопку создается через сигналы и слоты.
В моем примере программа условно генерирует ключи, поэтому я добавил еще одну функцию отвечающую за этот процесс. Далее полученный функцией ключ нужно вывести в окно.
self.pushButton.clicked.connect(self.buttonClicked)
В данном случае pushButton — это имя объекта (кнопки), clicked — событие (сигнал), buttonClicked — функция, которая исполнится при этом событии (слот).

Запускаем, и наслаждаемся работой.
Исходный код
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'mainwindow.ui' # # Created by: PyQt5 UI code generator 5.8.2 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(469, 292) self.centralWidget = QtWidgets.QWidget(MainWindow) self.centralWidget.setObjectName("centralWidget") self.pushButton = QtWidgets.QPushButton(self.centralWidget) self.pushButton.setGeometry(QtCore.QRect(30, 90, 111, 51)) self.pushButton.setObjectName("pushButton") self.textEdit = QtWidgets.QTextEdit(self.centralWidget) self.textEdit.setGeometry(QtCore.QRect(200, 40, 221, 181)) self.textEdit.setObjectName("textEdit") MainWindow.setCentralWidget(self.centralWidget) self.menuBar = QtWidgets.QMenuBar(MainWindow) self.menuBar.setGeometry(QtCore.QRect(0, 0, 469, 21)) self.menuBar.setObjectName("menuBar") MainWindow.setMenuBar(self.menuBar) self.mainToolBar = QtWidgets.QToolBar(MainWindow) self.mainToolBar.setObjectName("mainToolBar") MainWindow.addToolBar(QtCore.Qt.TopToolBarArea, self.mainToolBar) self.statusBar = QtWidgets.QStatusBar(MainWindow) self.statusBar.setObjectName("statusBar") MainWindow.setStatusBar(self.statusBar) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Keygen example")) self.pushButton.setText(_translate("MainWindow", "Нажми на меня"))
import random import string import sys from PyQt5.QtGui import * from PyQt5.QtCore import * from PyQt5.QtWidgets import * import gui class Example(QMainWindow, gui.Ui_MainWindow): def __init__(self): super().__init__() self.setupUi(self) self.pushButton.clicked.connect(self.buttonClicked) def generate_pins(self, size=6, chars=string.digits): return ''.join(random.choice(chars) for x in range(size)) def buttonClicked(self): self.textEdit.append(self.generate_pins(10)) if __name__ == '__main__': app = QApplication(sys.argv) form = Example() form.show() app.exec()
Как обновлять окно PyQt во время выполнения длительного цикла из другого модуля
Вы используете устаревший браузер. Этот и другие сайты могут отображаться в нем неправильно.
Необходимо обновить браузер или попробовать использовать другой.
rick1177
Новичок
Пользователь
Июн 19, 2022 2 0 1
Windows 11
Python 3.10
Спойлер: pip list
Package Version
—————— ————
beautifulsoup4 4.11.1
bs4 0.0.1
certifi 2022.5.18.1
charset-normalizer 2.0.12
cycler 0.11.0
fake-useragent 0.1.11
fonttools 4.33.3
idna 3.3
kiwisolver 1.4.3
lxml 4.9.0
matplotlib 3.5.2
numpy 1.22.4
packaging 21.3
pandas 1.4.2
Pillow 9.1.1
pip 22.1.2
psycopg2 2.9.3
pyparsing 3.0.9
pypiwin32 223
PyQt5 5.15.6
PyQt5-Qt5 5.15.2
PyQt5-sip 12.10.1
PyQt5-stubs 5.15.6.0
PyQt5Designer 5.14.1
PySide2 5.15.2.1
python-dateutil 2.8.2
pytz 2022.1
pywin32 304
requests 2.28.0
scipy 1.8.1
setuptools 58.1.0
shiboken2 5.15.2.1
six 1.16.0
soupsieve 2.3.2.post1
urllib3 1.26.9
win10toast 0.9
Привет, я тут первый раз, надеюсь на научение и помощь. Сделал простой демонстрационный пример, где есть приложение, в котором по нажатию кнопки запускается функция, она должна выводить сообщение в окно, она вроде выводит, но проблема в том, что делает это не на каждой итерации, а лишь при полном окончании исполнения. Научите, пожалуйста, обновлять окно после каждого вывода сообщения. Спасибо.
Спойлер: main.py
import sys import FormClasses from PyQt5 import QtWidgets import random import time def send_messages (self): for i in range(5): a=random.randint(1, 3) time.sleep(a) temp_text = f"Привет ".format(str(i)) self.send_logs(text_to_send=temp_text, red=255, green=0, blue=0, text_schema="\033[31m", mes_type="info", self_=self) def main(): app = QtWidgets.QApplication(sys.argv) # Новый экземпляр QApplication window = FormClasses.ExampleApp() # Создаём объект класса ExampleApp window.show() # Показываем окно app.exec_() # и запускаем приложение if __name__ == '__main__': main() # то запускаем функцию main()
Спойлер: FormClasses.py
from PyQt5 import QtWidgets, QtGui import StartForm import random import main import datetime import main class ExampleApp(QtWidgets.QMainWindow, StartForm.Ui_MainWindow): def __init__(self): # Это здесь нужно для доступа к переменным, методам # и т.д. в файле design.py super().__init__() self.count=0 self.setupUi(self) # Это нужно для инициализации нашего дизайна self.pushButton_UpdateTitles.clicked.connect(self.start_update_titels) # назначение на кнопку self.progressBar.setMinimum(1) self.progressBar.setValue(1) def setText_in_plainTextEditLogger_change_color (self, red: int = 0, green: int = 0, blue: int = 0): color = QtGui.QColor(red, green, blue) color_format = self.plainTextEditLogger.currentCharFormat() color_format.setForeground(color) self.plainTextEditLogger.setCurrentCharFormat(color_format) def setText_in_plainTextEditLogger(self, changefontonred=False, text: str = "", red: int = 0, green: int = 0, blue: int = 0): self.plainTextEditLogger.moveCursor(QtGui.QTextCursor.Start) if self.count!=1: text = text+'\n' if changefontonred ==True: self.setText_in_plainTextEditLogger_change_color(red,green,blue) self.plainTextEditLogger.insertPlainText(text) def send_logs(self, text_to_send = "", red=0, green=0, blue=0, text_schema = "", mes_type = "info", self_=None): t = datetime.datetime.now() current_time = t.strftime('%d.%m.%Y %H:%M:%S') print((text_schema+"<>").format(str(current_time) + ": "+text_to_send)) text_to_send = str(current_time+": ") + text_to_send if self_ is not None: self.setText_in_plainTextEditLogger(changefontonred=True, text=text_to_send, red = red, green=green, blue = blue) def start_update_titels(self): main.send_messages(self)
Спойлер: StartForm.py
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'StartForm.ui' # # Created by: PyQt5 UI code generator 5.15.6 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") MainWindow.resize(400, 500) MainWindow.setMinimumSize(QtCore.QSize(400, 500)) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget) self.verticalLayout.setObjectName("verticalLayout") self.pushButton_UpdateTitles = QtWidgets.QPushButton(self.centralwidget) self.pushButton_UpdateTitles.setMinimumSize(QtCore.QSize(0, 30)) self.pushButton_UpdateTitles.setObjectName("pushButton_UpdateTitles") self.verticalLayout.addWidget(self.pushButton_UpdateTitles) self.plainTextEditLogger = QtWidgets.QPlainTextEdit(self.centralwidget) self.plainTextEditLogger.setStyleSheet("QPlainTextEdit\n" "") self.plainTextEditLogger.setObjectName("plainTextEditLogger") self.verticalLayout.addWidget(self.plainTextEditLogger) self.progressBar = QtWidgets.QProgressBar(self.centralwidget) self.progressBar.setProperty("value", 1) self.progressBar.setObjectName("progressBar") self.verticalLayout.addWidget(self.progressBar) MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Парсинг eLibrary")) self.pushButton_UpdateTitles.setText(_translate("MainWindow", "Update Titles"))