Skip to content Skip to sidebar Skip to footer

How Can I Launch Pyqt Gui Multiple Times Consequtively In A Process?

How can I architect code to run a pyqt GUI multiple times consecutively in a process? (pyqtgraph specifically, if that is relevant) The context A python script that performs long

Solution 1:

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:

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

Also, you should use ThreadPool to make sure you don't run out of resources.

Here is a complete example:

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


classCaptureDataTaskStatus(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..classCaptureDataTask(QRunnable):
    def__init__(self, num_measurements):
        super().__init__()

        self.num_measurements = num_measurements
        self.status = CaptureDataTaskStatus()

    defrun(self):
        for i inrange(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)


classResultWindow(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 consoleprint('a: {}'.format(result['a']))
        print('b: {}'.format(result['b']))
        print('c: {}'.format(result['c']))


classMainWindow(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()

    defstart_capturing_data(self):
        # Here you start data capture tasks as needed (I just start 3 as an example)for setting inrange(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)


    defcapture_data_progress(self, current, total):
        # Update progress bar, label etc... for this example I will just print them to the consoleprint('Current: {}'.format(current))
        print('Total: {}'.format(total))

    defcapture_data_finished(self, result):
        result_window = ResultWindow(result)
        self.result_windows.append(result_window)
        result_window.show()


classApp(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_())

Solution 2:

If you want your GUI to keep updating in realtime and to not be freezed, you have two main ways to do it:

  1. Refresh the GUI from time to time calling QApplication.processEvents() inside your time consuming function.
  2. Create a separate thread (I mean, QThread) where you run your time consuming function

My personal preference is to go for the latter way. Here is a good tutorial for getting started on how to do multi-threading in Qt.

Having a look at your code:

...
gui.display()
measurement_equipment.capture_data(300) #may take hours
gui.close()
...

it seems you are calling app.exec_ inside gui.display. Its very likely you will have to decouple both functions and call app.exec_ outside of gui.display and after calling capture_data. You will also have to connect the finished signal of the new thread to gui.close. It will be something like this:

...
gui.display() # dont call app.exec_ here
thread = QThread.create(measurement_equipment.capture_data, 300)
thread.finished.connect(gui.close)
app.exec_()
...

I hope this can help you and to not be late!!

Solution 3:

You can have only One graphic GUI thread. This would imply to have some Threads to capture data and sync data with the graphic application when needed. We need to know if the GUI data display is displaying realtime data or only oneshot.

Post a Comment for "How Can I Launch Pyqt Gui Multiple Times Consequtively In A Process?"