Использование QThread для отображения QProgressDialog в PyQt5
Я использую PyQt5 для написания приложения, которое управляет заказами на продажу. При создании или удалении Заказа я хочу отобразить диалоговое окно прогресса в стиле marqee, чтобы указать, что приложение работает. Я посетил много постов, в которых ответ касался использования QThread. Я пытался реализовать его, но, похоже, мне чего-то не хватает. Это мой класс потоковой передачи.
class Worker(QThread):
finished = Signal()
def run(self):
self.x = QProgressDialog("Please wait..",None,0,0)
self.x.show()
def stop(self):
self.x.close()
В инициализации главного окна я создаю self.worker=Worker()
Теперь код для удаления записи, например:
msg = MsgBox("yn", "Delete Order", "Are you sure you want to delete this order?") # Wrapper for the QMessageBox
if msg == 16384:
self.worker.start() ## start the worker thread, hoping to start the progress dialog
session.delete(order) ##delete order from db
session.commit() ##commit to db
self.change_view("Active", 8) ##func. clean up the table.
self.worker.finished.emit() ##emit the finished signal to close the progress dialog
В результате диалоговое окно прогресса не отображается. Графический интерфейс просто зависает на секунду или две, а затем запись удаляется без отображения диалогового окна прогресса.
Извините, мой код довольно длинный, поэтому я не мог включить его сюда, я просто хотел посмотреть, не понял ли я что-то ужасное.
1 ответ
В вашем коде есть две основные проблемы:
- Элементы графического интерфейса (все, что унаследовано или связано с подклассом QWidget) должны быть созданы и доступны только из основного потока Qt.
- если предположить, что какое-то время уходит на операции удаления / фиксации, это те операции, которые должны выполняться в потоке, показывая диалог выполнения из основного потока, а не наоборот. Также учтите, что
QThread
уже естьfinished()
сигнал, и вы не должны его перезаписывать.
Это пример, основанный на вашем коде:
class Worker(QThread):
def __init__(self, session, order):
super.__init__()
self.session = session
self.order = order
def run(self):
self.session.delete(self.order)
self.session.commit()
class Whatever(QMainWindow):
def __init__(self):
super().__init__()
# ...
self.progressDialog = QProgressDialog("Please wait..", None, 0, 0, self)
def deleteOrder(self, session, order):
msg = MsgBox("yn", "Delete Order",
"Are you sure you want to delete this order?")
if msg == MsgBox.Yes: # you should prefer QMessageBox flags
self.worker = Worker(session, order)
self.worker.started(self.progressDialog.show())
self.worker.finished(self.deleteCompleted)
self.worker.start()
def deleteCompleted(self):
self.progressDialog.hide()
self.change_view("Active", 8)
Поскольку диалоговое окно выполнения должно оставаться открытым во время обработки, вы также должны запретить пользователю закрыть его. Для этого вы можете установить на него фильтр событий и гарантировать, что любое событие закрытия будет принято; кроме того, поскольку QProgressDialog наследуется от QDialog, Esc ключ должен быть отфильтрован, иначе он не закроет диалог, а отклонит и скроет его.
class Whatever(QMainWindow):
def __init__(self):
super().__init__()
# ...
self.progressDialog = QProgressDialog("Please wait..", None, 0, 0, self)
self.progressDialog.installEventFilter(self)
def eventFilter(self, source, event):
if source == self.progressDialog:
# check for both the CloseEvent *and* the escape key press
if event.type() == QEvent.Close or event == QKeySequence.Cancel:
event.accept()
return True
return super().eventFilter(source, event)