PyQt5中使用QThread显示QProgressDialog
Using QThread to display QProgressDialog in PyQt5
我正在使用 PyQt5 编写一个管理销售订单的应用程序。创建或删除订单时,我想显示一个字幕样式的进度对话框以指示该应用程序正在运行。我访问了很多帖子,其中涉及使用 QThread.I 的答案试图实现它,但似乎我遗漏了一些东西。这是我的线程 class.
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()
在 Main window 的 init 我创建 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
结果是没有显示进度对话框。 gui 只是冻结了一两秒钟,然后条目被删除,没有显示任何进度对话框。
抱歉,我的代码很长,所以我不能把它全部包含在这里,我只是想看看我是否有什么严重的错误。
您的代码存在两个主要问题:
- 必须从主 Qt 线程仅创建和访问 GUI 元素(所有继承或与 QWidget 子类相关的元素)。
- 假设需要一些时间的是 delete/commit 操作,那么 那些 操作必须在线程中进行,同时从主线程显示进度对话框,而不是相反。
另外,考虑到
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)
我正在使用 PyQt5 编写一个管理销售订单的应用程序。创建或删除订单时,我想显示一个字幕样式的进度对话框以指示该应用程序正在运行。我访问了很多帖子,其中涉及使用 QThread.I 的答案试图实现它,但似乎我遗漏了一些东西。这是我的线程 class.
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()
在 Main window 的 init 我创建 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
结果是没有显示进度对话框。 gui 只是冻结了一两秒钟,然后条目被删除,没有显示任何进度对话框。
抱歉,我的代码很长,所以我不能把它全部包含在这里,我只是想看看我是否有什么严重的错误。
您的代码存在两个主要问题:
- 必须从主 Qt 线程仅创建和访问 GUI 元素(所有继承或与 QWidget 子类相关的元素)。
- 假设需要一些时间的是 delete/commit 操作,那么 那些 操作必须在线程中进行,同时从主线程显示进度对话框,而不是相反。
另外,考虑到
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)