如何在进程 运行 时显示带有说明文本的 QDialog 框
How to display a QDialog box with instructional text, while a process is running
概述:我有一个 GUI-based 程序,它具有内置于按钮和菜单选项中的多种功能。许多过程是瞬时的,但其中几个过程很容易花费超过 5、10 甚至 20 秒。
当前问题:当我有一个对话框显示时,它不会显示框内的文本,例如'Completing Process, please wait...'
目标:在长时间处理 运行 时,我想显示一个 'please wait' 对话框,以便用户知道程序没有停止并且正在处理用户选择的功能.
背景信息:GUI 内置于 PySide2(又名 PyQt5)和 Python 3.6.5
当前尝试:
方法 1 - 最初,我使用 threading
模块来启动主要功能 运行。在函数开始时,我会调用显示对话框,该对话框将按预期显示,函数的其余部分将在后台 process/complete。
方法 1 问题 - 在程序变得更加复杂并且我需要使用 ThreadPoolExecutor 来加快速度之前,它一直很有效。 ThreadPoolExecutor 和线程模块不能很好地工作,如果一起使用会导致崩溃而不会出现错误消息,因此我不得不放弃该方法。
方法 2 - 我尝试使用 QDialog 框的 setModal
函数并调用 exec_
函数
方法 2 问题 - 对话框会根据需要显示和显示文本,但 setModal(False) 没有效果,进程将停止,直到 window 关闭。
方法 3(当前使用的方法)- 在我的 ProcessRunning 对话框 window class 的初始化中,我创建了一个 PySide2 信号,它接受一个字符串(该字符串是我要显示)。在调用长进程之前的前一行,我调用了信号的 emit()
函数,连接到它的是显示对话框的函数。
方法 3 问题 - window 显示和后台进程 运行s 但 window 不显示文本,几乎就像进程的那一部分被阻止通过后台进程。
总结:虽然问题标题表明我不知道如何显示 QDialog 框,但真正的问题是在 window 中显示指示用户“等待”的文本。我觉得由于我当前的方法没有发生这种情况,所以我没有“正确”显示该框。所以,实现这一点的“正确”方法就是我正在寻找的方法。
这是一个“简短”示例,使用了我在完整程序中的概念。 :
import sys
import math
import PySide2
from PySide2 import QtCore, QtGui, QtWidgets
import pandas
class Ui_functionRunning(object):
def setupUi(self, functionRunning):
functionRunning.setObjectName("functionRunning")
functionRunning.resize(234, 89)
self.labelProcessStatus = QtWidgets.QLabel(functionRunning)
self.labelProcessStatus.setGeometry(QtCore.QRect(10, 0, 221, 51))
self.labelProcessStatus.setAlignment(QtCore.Qt.AlignCenter)
self.labelProcessStatus.setWordWrap(True)
self.labelProcessStatus.setObjectName("labelProcessStatus")
self.buttonProcessCompleted = QtWidgets.QPushButton(functionRunning)
self.buttonProcessCompleted.setEnabled(False)
self.buttonProcessCompleted.setGeometry(QtCore.QRect(60, 60, 111, 23))
self.buttonProcessCompleted.setObjectName("buttonProcessCompleted")
class FunctionRunning(PySide2.QtWidgets.QDialog, Ui_functionRunning):
display_box = PySide2.QtCore.Signal(str)
def __init__(self):
super(FunctionRunning, self).__init__()
self.setupUi(self)
def showDialog(self, displayText):
MasterClass.process_running.labelProcessStatus.setText(displayText)
MasterClass.process_running.show()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(335, 255, 126, 23))
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
MainWindow.setStatusBar(self.statusbar)
self.pushButton.setText("PUSH TO TEST ")
class MainWindowUI(PySide2.QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindowUI, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.test_function_running)
def test_function_running(self):
MasterClass.process_running.display_box.emit('testing running and displaying a message')
df = pandas.DataFrame({'id': [0,1,2,3,4,5,6,7,8,9],'lon':[-121.28473, -121.29511, -121.32834, -121.29569, -121.29251, -121.25374, -121.28417, -121.29854, -121.21188, -121.25812], 'lat':
[37.986450, 37.911396, 37.969345, 37.923443, 37.990696, 37.975395, 37.942062, 37.993350, 37.979430, 37.975790]})
`If your system processes all this too quickly to notice the problem, increase the value in
`this first loop, from 10 to something like 20 or 30
for x in range(10):
for i in df.index:
for r in df.index:
if df.loc[i, 'lat'] != '':
df.loc[i, 'DIST_to_%s' % df.loc[i, 'id']] = self.dist(float(df.loc[i, 'lat']), float(df.loc[i, 'lon']), float(df.loc[r, 'lat']), float(df.loc[r, 'lon']))
print('%s pass completed - %s times through' % (i, x))
print('finished calculating distances')
MasterClass.process_running.labelProcessStatus.setText('All Done!')
def dist(self, lat_1, lon_1, lat_2, lon_2):
if lat_1 != lat_2 and lon_1 != lon_2:
val_1 = math.radians(90 - float(lat_1))
val_2 = math.cos(val_1)
val_3 = math.radians(90 - float(lat_2))
val_4 = math.cos(val_3)
val_5 = math.radians(90 - float(lat_1))
val_6 = math.sin(val_5)
val_7 = math.radians(90 - float(lat_2))
val_8 = math.sin(val_7)
val_9 = math.radians(float(lon_1) - float(lon_2))
val_10 = math.cos(val_9)
distance = round(math.acos(val_2 * val_4 + val_6 * val_8 * val_10) * 3958.756, 1)
return distance
else:
return 0
class MasterClass:
def __init__(self):
super(MasterClass, self).__init__()
MasterClass.app = PySide2.QtWidgets.QApplication(sys.argv)
MasterClass.process_running = FunctionRunning()
MasterClass.process_running.display_box.connect(MasterClass.process_running.showDialog)
MasterClass.main_ui = MainWindowUI()
MasterClass.main_ui.show()
MasterClass.app.exec_()
if __name__ == '__main__':
MasterClass()
您参考的方法 1 可能有效,但有一些注意事项;任何更新您的 UI 的调用都应使用信号通过 PySide2.QtCore.Signal()
完成
将 MainWindowUI
class 更改为如下所示(保留 dist
功能,无需更改):
class MainWindowUI(PySide2.QtWidgets.QMainWindow, Ui_MainWindow):
# Create your signal here
initiate_function = PySide2.QtCore.Signal()
def __init__(self):
super(MainWindowUI, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.test_function_running)
def test_function_running(self):
MasterClass.process_running.display_box.emit('testing running and displaying a message')
def test():
df = pandas.DataFrame({'id': [0,1,2,3,4,5,6,7,8,9],'lon':[-121.28473, -121.29511, -121.32834, -121.29569, -121.29251, -121.25374, -121.28417, -121.29854, -121.21188, -121.25812], 'lat':
[37.986450, 37.911396, 37.969345, 37.923443, 37.990696, 37.975395, 37.942062, 37.993350, 37.979430, 37.975790]})
for x in range(10):
for i in df.index:
for r in df.index:
if df.loc[i, 'lat'] != '':
df.loc[i, 'DIST_to_%s' % df.loc[i, 'id']] = self.dist(float(df.loc[i, 'lat']), float(df.loc[i, 'lon']), float(df.loc[r, 'lat']), float(df.loc[r, 'lon']))
print('%s pass completed - %s times through' % (i, x))
# You could even update you dialog box mid-process
MasterClass.process_running.labelProcessStatus.setText('%s pass completed - %s times through' % (i, x))
print('finished calculating distances')
# Below, the signal is emitted and simultaneously runs the function assigned below, in the MasterClass __init__
MasterClass.main_ui.initiate_function.emit()
MasterClass.process_running.labelProcessStatus.setText('All Done!')
t = threading.Thread(target=test)
t.daemon = True
t.start()
def update_ui(self):
print('Updating UI here')
self.pushButton.setText('New Button Text')
现在将您的 MasterClass
更新为如下所示:
class MasterClass:
def __init__(self):
super(MasterClass, self).__init__()
MasterClass.app = PySide2.QtWidgets.QApplication(sys.argv)
MasterClass.process_running = FunctionRunning()
MasterClass.process_running.display_box.connect(MasterClass.process_running.showDialog)
MasterClass.main_ui = MainWindowUI()
# After the MasterClass object is initialized, you can connect a function to the signal that was added to the MainWindowUI class
MasterClass.main_ui.initiate_function.connect(MasterClass.main_ui.update_ui)
# update_ui is the new function added above, all it does is change the text on the button, but it could be more complex if needed
MasterClass.main_ui.show()
MasterClass.app.exec_()
这应该有助于在 PySide2 中使用线程以及 threading
和 ThreadPoolExecutor
运行 问题。所以这里的关键是,当你需要一个进程在主线程中 运行 时使用信号,例如对 UI 的更新。它可以用于其他事情,但只有在 UI 需要刷新时才绝对必要。
概述:我有一个 GUI-based 程序,它具有内置于按钮和菜单选项中的多种功能。许多过程是瞬时的,但其中几个过程很容易花费超过 5、10 甚至 20 秒。
当前问题:当我有一个对话框显示时,它不会显示框内的文本,例如'Completing Process, please wait...'
目标:在长时间处理 运行 时,我想显示一个 'please wait' 对话框,以便用户知道程序没有停止并且正在处理用户选择的功能.
背景信息:GUI 内置于 PySide2(又名 PyQt5)和 Python 3.6.5
当前尝试:
方法 1 - 最初,我使用 threading
模块来启动主要功能 运行。在函数开始时,我会调用显示对话框,该对话框将按预期显示,函数的其余部分将在后台 process/complete。
方法 1 问题 - 在程序变得更加复杂并且我需要使用 ThreadPoolExecutor 来加快速度之前,它一直很有效。 ThreadPoolExecutor 和线程模块不能很好地工作,如果一起使用会导致崩溃而不会出现错误消息,因此我不得不放弃该方法。
方法 2 - 我尝试使用 QDialog 框的 setModal
函数并调用 exec_
函数
方法 2 问题 - 对话框会根据需要显示和显示文本,但 setModal(False) 没有效果,进程将停止,直到 window 关闭。
方法 3(当前使用的方法)- 在我的 ProcessRunning 对话框 window class 的初始化中,我创建了一个 PySide2 信号,它接受一个字符串(该字符串是我要显示)。在调用长进程之前的前一行,我调用了信号的 emit()
函数,连接到它的是显示对话框的函数。
方法 3 问题 - window 显示和后台进程 运行s 但 window 不显示文本,几乎就像进程的那一部分被阻止通过后台进程。
总结:虽然问题标题表明我不知道如何显示 QDialog 框,但真正的问题是在 window 中显示指示用户“等待”的文本。我觉得由于我当前的方法没有发生这种情况,所以我没有“正确”显示该框。所以,实现这一点的“正确”方法就是我正在寻找的方法。
这是一个“简短”示例,使用了我在完整程序中的概念。 :
import sys
import math
import PySide2
from PySide2 import QtCore, QtGui, QtWidgets
import pandas
class Ui_functionRunning(object):
def setupUi(self, functionRunning):
functionRunning.setObjectName("functionRunning")
functionRunning.resize(234, 89)
self.labelProcessStatus = QtWidgets.QLabel(functionRunning)
self.labelProcessStatus.setGeometry(QtCore.QRect(10, 0, 221, 51))
self.labelProcessStatus.setAlignment(QtCore.Qt.AlignCenter)
self.labelProcessStatus.setWordWrap(True)
self.labelProcessStatus.setObjectName("labelProcessStatus")
self.buttonProcessCompleted = QtWidgets.QPushButton(functionRunning)
self.buttonProcessCompleted.setEnabled(False)
self.buttonProcessCompleted.setGeometry(QtCore.QRect(60, 60, 111, 23))
self.buttonProcessCompleted.setObjectName("buttonProcessCompleted")
class FunctionRunning(PySide2.QtWidgets.QDialog, Ui_functionRunning):
display_box = PySide2.QtCore.Signal(str)
def __init__(self):
super(FunctionRunning, self).__init__()
self.setupUi(self)
def showDialog(self, displayText):
MasterClass.process_running.labelProcessStatus.setText(displayText)
MasterClass.process_running.show()
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.pushButton = QtWidgets.QPushButton(self.centralwidget)
self.pushButton.setGeometry(QtCore.QRect(335, 255, 126, 23))
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
MainWindow.setStatusBar(self.statusbar)
self.pushButton.setText("PUSH TO TEST ")
class MainWindowUI(PySide2.QtWidgets.QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindowUI, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.test_function_running)
def test_function_running(self):
MasterClass.process_running.display_box.emit('testing running and displaying a message')
df = pandas.DataFrame({'id': [0,1,2,3,4,5,6,7,8,9],'lon':[-121.28473, -121.29511, -121.32834, -121.29569, -121.29251, -121.25374, -121.28417, -121.29854, -121.21188, -121.25812], 'lat':
[37.986450, 37.911396, 37.969345, 37.923443, 37.990696, 37.975395, 37.942062, 37.993350, 37.979430, 37.975790]})
`If your system processes all this too quickly to notice the problem, increase the value in
`this first loop, from 10 to something like 20 or 30
for x in range(10):
for i in df.index:
for r in df.index:
if df.loc[i, 'lat'] != '':
df.loc[i, 'DIST_to_%s' % df.loc[i, 'id']] = self.dist(float(df.loc[i, 'lat']), float(df.loc[i, 'lon']), float(df.loc[r, 'lat']), float(df.loc[r, 'lon']))
print('%s pass completed - %s times through' % (i, x))
print('finished calculating distances')
MasterClass.process_running.labelProcessStatus.setText('All Done!')
def dist(self, lat_1, lon_1, lat_2, lon_2):
if lat_1 != lat_2 and lon_1 != lon_2:
val_1 = math.radians(90 - float(lat_1))
val_2 = math.cos(val_1)
val_3 = math.radians(90 - float(lat_2))
val_4 = math.cos(val_3)
val_5 = math.radians(90 - float(lat_1))
val_6 = math.sin(val_5)
val_7 = math.radians(90 - float(lat_2))
val_8 = math.sin(val_7)
val_9 = math.radians(float(lon_1) - float(lon_2))
val_10 = math.cos(val_9)
distance = round(math.acos(val_2 * val_4 + val_6 * val_8 * val_10) * 3958.756, 1)
return distance
else:
return 0
class MasterClass:
def __init__(self):
super(MasterClass, self).__init__()
MasterClass.app = PySide2.QtWidgets.QApplication(sys.argv)
MasterClass.process_running = FunctionRunning()
MasterClass.process_running.display_box.connect(MasterClass.process_running.showDialog)
MasterClass.main_ui = MainWindowUI()
MasterClass.main_ui.show()
MasterClass.app.exec_()
if __name__ == '__main__':
MasterClass()
您参考的方法 1 可能有效,但有一些注意事项;任何更新您的 UI 的调用都应使用信号通过 PySide2.QtCore.Signal()
将 MainWindowUI
class 更改为如下所示(保留 dist
功能,无需更改):
class MainWindowUI(PySide2.QtWidgets.QMainWindow, Ui_MainWindow):
# Create your signal here
initiate_function = PySide2.QtCore.Signal()
def __init__(self):
super(MainWindowUI, self).__init__()
self.setupUi(self)
self.pushButton.clicked.connect(self.test_function_running)
def test_function_running(self):
MasterClass.process_running.display_box.emit('testing running and displaying a message')
def test():
df = pandas.DataFrame({'id': [0,1,2,3,4,5,6,7,8,9],'lon':[-121.28473, -121.29511, -121.32834, -121.29569, -121.29251, -121.25374, -121.28417, -121.29854, -121.21188, -121.25812], 'lat':
[37.986450, 37.911396, 37.969345, 37.923443, 37.990696, 37.975395, 37.942062, 37.993350, 37.979430, 37.975790]})
for x in range(10):
for i in df.index:
for r in df.index:
if df.loc[i, 'lat'] != '':
df.loc[i, 'DIST_to_%s' % df.loc[i, 'id']] = self.dist(float(df.loc[i, 'lat']), float(df.loc[i, 'lon']), float(df.loc[r, 'lat']), float(df.loc[r, 'lon']))
print('%s pass completed - %s times through' % (i, x))
# You could even update you dialog box mid-process
MasterClass.process_running.labelProcessStatus.setText('%s pass completed - %s times through' % (i, x))
print('finished calculating distances')
# Below, the signal is emitted and simultaneously runs the function assigned below, in the MasterClass __init__
MasterClass.main_ui.initiate_function.emit()
MasterClass.process_running.labelProcessStatus.setText('All Done!')
t = threading.Thread(target=test)
t.daemon = True
t.start()
def update_ui(self):
print('Updating UI here')
self.pushButton.setText('New Button Text')
现在将您的 MasterClass
更新为如下所示:
class MasterClass:
def __init__(self):
super(MasterClass, self).__init__()
MasterClass.app = PySide2.QtWidgets.QApplication(sys.argv)
MasterClass.process_running = FunctionRunning()
MasterClass.process_running.display_box.connect(MasterClass.process_running.showDialog)
MasterClass.main_ui = MainWindowUI()
# After the MasterClass object is initialized, you can connect a function to the signal that was added to the MainWindowUI class
MasterClass.main_ui.initiate_function.connect(MasterClass.main_ui.update_ui)
# update_ui is the new function added above, all it does is change the text on the button, but it could be more complex if needed
MasterClass.main_ui.show()
MasterClass.app.exec_()
这应该有助于在 PySide2 中使用线程以及 threading
和 ThreadPoolExecutor
运行 问题。所以这里的关键是,当你需要一个进程在主线程中 运行 时使用信号,例如对 UI 的更新。它可以用于其他事情,但只有在 UI 需要刷新时才绝对必要。