概要:ボタンやメニュー選択項目に多数の機能が組み込まれているGUIベースのプログラムがあります。プロセスの多くは瞬時に実行されますが、5、10、または20秒以上かかるプロセスもあります。
現在の問題:ダイアログボックスが表示されているのに、「プロセスを完了しています。しばらくお待ちください...」などのテキストがボックス内に表示されません。
目標:長いプロセスの実行中に、[お待ちください]ダイアログボックスを表示して、プログラムが停止しておらず、ユーザーが選択した機能に取り組んでいることをユーザーに知らせたいと思います。
背景情報:GUIはPySide2(別名PyQt5)とPython3.6.5で構築されています
現在の試み:
方法1-最初に、threading
モジュールを使用して、実行する主要な機能を開始しました。関数の開始時に、ダイアログボックスを表示するための呼び出しがあります。ダイアログボックスは意図したとおりに表示され、関数の残りの部分はバックグラウンドで処理/完了します。
方法1の問題-プログラムがより複雑になり、ThreadPoolExecutorを使用して処理を高速化する必要が生じるまで、これはうまく機能しました。ThreadPoolExecutorとスレッドモジュールが一緒に機能すると、エラーメッセージなしでクラッシュするため、そのメソッドを放棄する必要がありました。
方法2 -私が使用してみましたsetModal
QDialogボックスの機能をと呼ばれるexec_
機能を
方法2問題-ダイアログボックスに必要なテキストが表示および表示されますが、setModal(False)は効果がなく、ウィンドウが閉じるまでプロセスが停止していました。
方法3(現在使用されている方法)-ProcessRunningダイアログウィンドウクラスの初期化で、文字列(表示するメッセージである文字列)を受け取るPySide2シグナルを作成しました。長いプロセスを呼び出す前の前の行でemit()
、ダイアログボックスを表示する関数であるシグナルの関数を呼び出しました。
方法3問題-ウィンドウが表示され、バックグラウンドプロセスが実行されますが、プロセスのその部分がバックグラウンドプロセスによって保持されているように、ウィンドウにテキストが表示されません。
概要:質問のタイトルは、QDialogボックスを表示する方法がわからないことを示唆していますが、実際の問題は、ユーザーに「待機」するように指示するテキストをウィンドウ内に表示することです。これは私の現在の方法では起こらないので、ボックスを「正しく」表示していないように感じます。したがって、これを達成するための「正しい」方法が私が探しているものです。
これは、私の完全なプログラムにある概念を使用した「短い」例です。:
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
クラスを次のように変更します(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を更新する必要がある場合にのみ絶対に必要です。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加