Как обновлять окно PyQt во время выполнения длительного цикла из другого модуля

rick1177

Новичок
Пользователь
Июн 19, 2022
2
0
1
Windows 11
Python 3.10
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

Привет, я тут первый раз, надеюсь на научение и помощь. Сделал простой демонстрационный пример, где есть приложение, в котором по нажатию кнопки запускается функция, она должна выводить сообщение в окно, она вроде выводит, но проблема в том, что делает это не на каждой итерации, а лишь при полном окончании исполнения. Научите, пожалуйста, обновлять окно после каждого вывода сообщения. Спасибо.

Python:
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"Привет {i}".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()

Python:
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)

Python:
# -*- 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"
"{\n"
"    background-color: palette(base);\n"
"    border-style:solid;\n"
"    border-width: 1px;\n"
"    border-radius: 3px;\n"
"    border-color: #D0D0D0;\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"))
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 650
472
83
можно выделить эту функцию в отдельный поток, и из него через сигнал посылать инфу в главное окно
в главном окне сделать слот, который будет выводить сообщение по этому сигналу
 

rick1177

Новичок
Пользователь
Июн 19, 2022
2
0
1
можно выделить эту функцию в отдельный поток, и из него через сигнал посылать инфу в главное окно
в главном окне сделать слот, который будет выводить сообщение по этому сигналу
А можно попросить пример? Просто для меня это пока магия, без примера. И сразу возник вопрос, а можно ли с той же кнопки принудительно останавливать процесс, не дожидаясь конца?
 

regnor

Модератор
Команда форума
Модератор
Июл 7, 2020
2 650
472
83
А можно попросить пример? Просто для меня это пока магия, без примера
я немного упростил чтобы было поменьше кода
файл ui
Python:
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)
        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"))

файл mainwindow
Python:
from PyQt5 import QtWidgets
from PyQt5.QtCore import QObject, pyqtSignal
from ui import Ui_MainWindow
import threading
import time


class Test(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self):
        super().__init__()
        Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.pushButton_UpdateTitles.clicked.connect(self.test)  # соединяем кнопку с test

    def test(self):
        self.work = Worker()
        self.work.signal.connect(self.slot)  # соединяем сигнал из workera со слотом slot
        test_thread = threading.Thread(target=self.work.Thread_test, daemon=True)  # определяем объект потока
        test_thread.start()  # запускаем поток

    # Слот
    def slot(self, count):
        self.statusBar().showMessage(count)  # пишем в статус бар число, которое передал сигнал из workera


# Класс, из методов которого делаем второй поток, можно вынести в другой модуль
class Worker(QObject):
    signal = pyqtSignal(str)  # определяем сигнал и какой тип он будет передавать

    # В цикле увеличиваем число count, и передаем через сигнал
    def Thread_test(self):
        count = 0
        while 1:
            count += 1
            time.sleep(1)
            self.signal.emit(str(count))

файл main
Python:
import mainwindow
from PyQt5 import QtWidgets
import sys


def main():
    app = QtWidgets.QApplication(sys.argv)  # Новый экземпляр QApplication
    window = mainwindow.Test()  # Создаём объект класса Test
    window.show()  # Показываем окно
    app.exec_()  # и запускаем приложение


if __name__ == '__main__':
    main()  # то запускаем функцию main()

вместо threading можно использовать QThread и moveToThread

И сразу возник вопрос, а можно ли с той же кнопки принудительно останавливать процесс, не дожидаясь конца?
вы имеете ввиду остановить поток? ну как бы можно его убить, но это не рекомендуеться делать, если в потоке идет обработка каких то данных, данные могут быть повреждены
второй вариант, можно сделать глобальную переменную, которая при нажатии меняет свое значение на True/False, и в потоке проверять ее, и если например False, то завершать поток
 
  • Мне нравится
Реакции: Vershitel_sudeb

Форум IT Специалистов