問題描述
如何構建代碼以在一個進程中連續(xù)多次運行 pyqt GUI?
How can I architect code to run a pyqt GUI multiple times consecutively in a process?
(特別是 pyqtgraph,如果相關的話)
(pyqtgraph specifically, if that is relevant)
在測量設備上執(zhí)行長時間運行的數(shù)據(jù)捕獲的 python 腳本(一個大的 for 循環(huán)).在每次捕獲迭代期間,都會出現(xiàn)一個新的 GUI,并向用戶顯示來自測量設備的實時數(shù)據(jù),同時主捕獲代碼正在運行.
A python script that performs long running data capture on measurement equipment (a big for loop). During each capture iteration a new GUI appear and displays live data from the measurement equipment to the user, while the main capture code is running.
我想做這樣的事情:
for setting in settings:
measurement_equipment.start(setting)
gui = LiveDataStreamGUI(measurement_equipment)
gui.display()
measurement_equipment.capture_data(300) #may take hours
gui.close()
主要問題
我希望數(shù)據(jù)捕獲代碼成為主線程.然而 pyqt 似乎不允許這種架構,因為它的 app.exec_()
是一個阻塞調用,允許每個進程只創(chuàng)建一次 GUI(例如,在 gui.display()
以上).
The main issue
I'd like the data capture code to be the main thread. However pyqt doesn't seems to allow this architecture, as its app.exec_()
is a blocking call, allowing a GUI to be created only once per process (e.g., in gui.display()
above).
推薦答案
應用程序是一個可執(zhí)行進程,它運行在一個或多個前臺線程上,每個前臺線程也可以啟動后臺線程來執(zhí)行并行操作或操作而不阻塞調用線程.應用程序將在所有前臺線程結束后終止,因此,您至少需要一個前臺線程,在您的情況下,該線程是在您調用 app.exec_()
語句時創(chuàng)建的.在 GUI 應用程序中,這是您應該創(chuàng)建和顯示主窗口和任何其他 UI 小部件的 UI 線程.當所有小部件都關閉時,Qt 將自動終止您的應用程序進程.
An application is an executable process that runs on one or more foreground threads each of which can also start background threads to perform parallel operations or operations without blocking the calling thread. An application will terminate after all foreground threads have ended, therefore, you need at least one foreground thread which in your case is created when you call the app.exec_()
statement. In a GUI application, this is the UI thread where you should create and display the main window and any other UI widget. Qt will automatically terminate your application process when all widgets are closed.
恕我直言,您應該盡量遵循上述正常流程,工作流程可能如下:
IMHO, you should try to follow the normal flow described above as much as possible, the workflow could be as follows:
啟動應用程序 > 創(chuàng)建主窗口 > 為每個計算啟動一個后臺線程 > 將進度發(fā)送到 UI 線程 > 每次計算完成后在窗口中顯示結果 > 關閉所有窗口 > 結束應用程序
Start Application > Create main window > Start a background thread for each calculation > Send progress to UI thread > Show results in a window after each calculation is finished > Close all windows > End application
另外,您應該使用 ThreadPool
來確保不會耗盡資源.
Also, you should use ThreadPool
to make sure you don't run out of resources.
這是一個完整的例子:
import sys
import time
import PyQt5
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtCore import QRunnable, pyqtSignal, QObject
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QDialog
class CaptureDataTaskStatus(QObject):
progress = pyqtSignal(int, int) # This signal is used to report progress to the UI thread.
captureDataFinished = pyqtSignal(dict) # Assuming your result is a dict, this can be a class, a number, etc..
class CaptureDataTask(QRunnable):
def __init__(self, num_measurements):
super().__init__()
self.num_measurements = num_measurements
self.status = CaptureDataTaskStatus()
def run(self):
for i in range(0, self.num_measurements):
# Report progress
self.status.progress.emit(i + 1, self.num_measurements)
# Make your equipment measurement here
time.sleep(0.1) # Wait for some time to mimic a long action
# At the end you will have a result, for example
result = {'a': 1, 'b': 2, 'c': 3}
# Send it to the UI thread
self.status.captureDataFinished.emit(result)
class ResultWindow(QWidget):
def __init__(self, result):
super().__init__()
# Display your result using widgets...
self.result = result
# For this example I will just print the dict values to the console
print('a: {}'.format(result['a']))
print('b: {}'.format(result['b']))
print('c: {}'.format(result['c']))
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.result_windows = []
self.thread_pool = QtCore.QThreadPool().globalInstance()
# Change the following to suit your needs (I just put 1 here so you can see each task opening a window while the others are still running)
self.thread_pool.setMaxThreadCount(1)
# You could also start by clicking a button, menu, etc..
self.start_capturing_data()
def start_capturing_data(self):
# Here you start data capture tasks as needed (I just start 3 as an example)
for setting in range(0, 3):
capture_data_task = CaptureDataTask(300)
capture_data_task.status.progress.connect(self.capture_data_progress)
capture_data_task.status.captureDataFinished.connect(self.capture_data_finished)
self.thread_pool.globalInstance().start(capture_data_task)
def capture_data_progress(self, current, total):
# Update progress bar, label etc... for this example I will just print them to the console
print('Current: {}'.format(current))
print('Total: {}'.format(total))
def capture_data_finished(self, result):
result_window = ResultWindow(result)
self.result_windows.append(result_window)
result_window.show()
class App(QApplication):
"""Main application wrapper, loads and shows the main window"""
def __init__(self, sys_argv):
super().__init__(sys_argv)
self.main_window = MainWindow()
self.main_window.show()
if __name__ == '__main__':
app = App(sys.argv)
sys.exit(app.exec_())
這篇關于如何在一個進程中多次啟動 pyqt GUI?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網(wǎng)!