Создание окна, информирующего о долгой загрузке GUI
Есть условное приложение:
import sys
from PyQt5 import QtCore, QtWidgets
class StartingWindow(QtWidgets.QMainWindow):
def __init__(self):
super(StartingWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn1.clicked.connect(self.add_row)
def add_row(self):
self.label = QtWidgets.QLabel()
self.label.setText('Ожидайте заавершения операции')
self.label.show()
for i in range(1000000):
self.ui.table.insertRow(i)
item = QtWidgets.QTableWidgetItem()
self.ui.table.setItem(i,0, item)
self.ui.table.item(i,0).setText("example1")
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.table = QtWidgets.QTableWidget(self.centralwidget)
self.table.setRowCount(0)
self.table.setColumnCount(1)
self.horizontalLayout.addWidget(self.table)
self.btn1 = QtWidgets.QPushButton(self.centralwidget)
self.btn1.setText('btn1')
self.horizontalLayout.addWidget(self.btn1)
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
app1 = QtWidgets.QApplication([])
start_win = StartingWindow()
start_win.show()
sys.exit(app1.exec_())
При нажатии на кнопку выполняется долгая функция add_row, изменяющая графический интерфейс. Необходимо выводить дополнительное окно, которое сообщало бы, что программа не зависла, а обрабатывает сигнал.
Реализация с QMessageBox не подходит, т.к. прерывает выполнение и ожидает нажатия кнопки. Была идея вызывать QMessageBox и закрывать его программно, но не нашел как это сделать.
QDialog тоже не подходит, т.к. при вызове его через .show() текст внутри него отображается после завершения функции.
Использовать 2 потока не получилось, т.к. функция изменяет интерфейс и прописывать испускание сигналов - слишком обширная работа.
Есть идеи как реализовать эту задумку? Может быть использовать несколько процессов?(до этого их не использовал и пока не знаю их тонкостей)
Ответы (1 шт):
Долгая функция должна выполняться в дополнительном потоке, из которого вы передаете какие-то данные в основной поток и там их визуализируете.
import sys
from PyQt5 import QtCore, QtWidgets, QtGui
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.table = QtWidgets.QTableWidget(self.centralwidget)
self.table.setRowCount(0)
self.table.setColumnCount(1)
self.horizontalLayout.addWidget(self.table)
self.btn1 = QtWidgets.QPushButton(self.centralwidget)
self.btn1.setText('Start thread')
self.horizontalLayout.addWidget(self.btn1)
MainWindow.setCentralWidget(self.centralwidget)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
class WorkThread(QtCore.QThread):
threadSignal = QtCore.pyqtSignal(int)
def __init__(self, numStart, numEnd):
super().__init__()
self.numStart = numStart
self.numEnd = numEnd
def run(self):
for i in range(self.numStart, self.numEnd):
self.msleep(100) # имитируем длительную загрузку
self.threadSignal.emit(i)
self.finished.emit()
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
class StartingWindow(QtWidgets.QMainWindow):
def __init__(self):
super(StartingWindow, self).__init__()
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.ui.btn1.clicked.connect(self.add_row)
self.thread = None
self.numStart = 0
self.numEnd = 1_000_000
def add_row(self):
self.label = QtWidgets.QLabel()
self.label.setAttribute(QtCore.Qt.WA_TranslucentBackground, True)
self.label.setWindowFlags(
self.windowFlags() |
QtCore.Qt.FramelessWindowHint |
QtCore.Qt.WindowStaysOnTopHint
)
self.label.setText('Ожидайте заавершения операции')
self.movie = QtGui.QMovie('loading.gif')
self.label.setMovie(self.movie)
self.movie.start()
self.label.show()
if self.thread is None:
self.thread = WorkThread(self.numStart, self.numEnd)
self.thread.threadSignal.connect(self.on_threadSignal)
self.thread.finished.connect(self.threadFinished)
self.thread.start()
self.ui.btn1.setText("Stop thread")
else:
self.label.close()
self.thread.terminate()
self.thread = None
self.ui.btn1.setText("Start thread")
self.numStart += 1
def on_threadSignal(self, num):
rows = self.ui.table.rowCount()
self.ui.table.insertRow(rows)
item = QtWidgets.QTableWidgetItem(f"example1 {num}")
self.ui.table.setItem(rows, 0, item)
self.numStart = num
def threadFinished(self):
self.movie.stop()
self.label.close()
self.thread = None
self.ui.btn1.setText("Start thread")
if self.numStart+1 == self.numEnd:
self.numStart = 0
if __name__ == '__main__':
app1 = QtWidgets.QApplication(sys.argv)
start_win = StartingWindow()
start_win.show()
sys.exit(app1.exec_())
