Ожидание выполнения задачи и блокировка формы PyQt5
Как вызвать диалог, который заблокирует основную форму, но при этом чтобы код из основной формы продолжил выполняться?
Я использую PyQt5 и qasync, при нажатии на кнопку у меня выполнятся асинхронный код, который может занять много времени, чтобы избежать бесконечного ожидания я использую asyncio.wait_for(...). При этом на форме есть множество других вкладок и кнопок и если во время выполнения вышеописанной функции пользователь будет нажимать на другие кнопки, то интерфейс сломается и функции, которые выполняются по нажатию на кнопки больше не будут работать. В таком случае я получала ошибку: task was destroyed but it is pending. Для решения этой проблемы я использовала блокировку (setEnabled(False)) всех вкладок приложения, а на выбранной вкладке всех кнопок. Но это достаточно объёмное решение и, я думаю, не совсем верное.
Ко мне пришла идея вызывать "диалог ожидания", который нельзя будет закрыть и который будет блокировать основную форму, пока заданный код не выполнится или пока не пройдёт максимальное время ожидания. Также на такой диалог можно будет выводить процесс выполнения. Но реализовать этот диалог как я задумывала у меня не вышло: после создания диалога я вызывала его с помощью dialog.show(), таким образом диалог показывался и выполнялся дальнейший код, но в таком случае можно переключиться на основную форму, поэтому я использовала dialog.exec_(), но тогда дальнейший код не выполняется. Воспроизводимый пример кода добавила.
Как правильно реализовать этот диалог? Или какой другой метод можно использовать для блокировки всей формы и вывода процесса работы?
import asyncio
import sys
import qasync
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
class Dialog(QDialog):
def __init__(self):
super().__init__()
self.layout = QVBoxLayout()
self.label = QLabel('Дождитесь окончания выполнения!')
self.layout.addWidget(self.label)
self.movie_label = QLabel()
movie = QMovie('wait.gif')
self.movie_label.setMovie(movie)
movie.start()
self.layout.addWidget(self.movie_label)
self.setLayout(self.layout)
class Window(QWidget):
def __init__(self, parent=None):
QWidget.__init__(self, parent)
self.layout = QVBoxLayout()
self.button = QPushButton('Запустить')
self.button.clicked.connect(self.button_clicked)
self.layout.addWidget(self.button)
self.setLayout(self.layout)
@qasync.asyncSlot()
async def button_clicked(self):
dialog = Dialog()
dialog.show()
# dialog.exec_()
# Выполнение какого-то кода
await asyncio.sleep(3)
print('Готово!')
dialog.close()
async def main():
future = asyncio.Future()
main_app = QApplication.instance()
main_app.window = Window()
main_app.window.resize(600, 300)
main_app.window.show()
await future
return True
if __name__ == '__main__':
try:
qasync.run(main())
except asyncio.CancelledError:
sys.exit(0)
Ответы (1 шт):
Вам понадобится сделать диалог модальным и убрать из его заголовка кнопку закрытия
class Dialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.WindowTitleHint | QtCore.Qt.CustomizeWindowHint);
self.setModal(True)
# .....