Waiting for another thread to continue (if needed) before starting the pipeline
-
I am getting a bit confused with QThreads, as I am new to signal-slot concept.
I have the following situation: I have some pre-calculations that can be triggered by user before running the main pipeline. These pre-calculations are optional but, if triggered, they should be computed before running the main pipeline. However, if they are not triggered, main pipeline can be started without them. The main pipeline only starts by click of button, so it can also happen that the pre-calculations finished before user pressed the button.
So, there are 3 cases:
a) Did not select to use pre-calculations -> just run pipeline normally
b) Selected to use pre-calculations & triggered them -> wait till finished -> then run pipeline
c) Selected to use pre-calculations & triggered them & finished before user decided to run pipeline -> just run pipeline normally.I managed to make this MRE, which seems to almost do what I need. I am also not sure this is correct approach in general as it looks a bit over-engineered, but I do not see some simpler way to do it.
Question: is this correct approach in general?
import sys import time from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QPushButton, QCheckBox from PySide2.QtCore import QThread, Signal, Slot class CalculationWorker(QThread): """Simulates additional calculations in a separate thread.""" finished = Signal() def run(self): print("Starting additional calculations...") time.sleep(3) print("Additional calculations completed.") self.finished.emit() class MainUI(QWidget): def __init__(self): super().__init__() self.checkbox = QCheckBox("With Additional Calculations") self.run_button = QPushButton("Run Pipeline") self.start_calc_button = QPushButton("Start Additional Calculations") layout = QVBoxLayout() layout.addWidget(self.checkbox) layout.addWidget(self.start_calc_button) layout.addWidget(self.run_button) self.setLayout(layout) self.calc_done = True self.worker = None self.waiting_for_calculations = False self.run_button.clicked.connect(self.run_pipeline) self.start_calc_button.clicked.connect(self.start_additional_calculations) def run_pipeline(self): """Ensure additional calculations are complete before running.""" if not self.checkbox.isChecked(): print("Running pipeline WITHOUT additional calculations.") self.process_pipeline() return if self.calc_done: print("Running pipeline (calculations were already completed).") self.process_pipeline() return print( "Waiting for additional calculations to finish before running pipeline..." ) self.waiting_for_calculations = True self.worker.finished.connect(self.on_calculations_done_for_pipeline) @Slot() def on_calculations_done_for_pipeline(self): """Called when calculations are done, but only if waiting for pipeline.""" if self.waiting_for_calculations: print("Calculations done. Now running pipeline.") self.process_pipeline() self.waiting_for_calculations = False def start_additional_calculations(self): """Start additional calculations in a worker thread.""" if self.worker and self.worker.isRunning(): print("Calculations are already running...") return # Already running, do nothing print("Starting additional calculations...") self.calc_done = False self.worker = CalculationWorker() self.worker.finished.connect(self.on_calculations_done) self.worker.start() @Slot() def on_calculations_done(self): """Called when calculations are completed.""" self.calc_done = True print("Ready to run pipeline.") def process_pipeline(self): """Placeholder for actual pipeline execution.""" print("*** Pipeline processing completed! ***") if __name__ == "__main__": app = QApplication(sys.argv) window = MainUI() window.show() sys.exit(app.exec_())
-
Why is a thread even involved here?
- Start program
- Enable the calculation and main pipeline buttons
- The user can simply press the main pipeline button to continue (case a).
- If the user opts to do the pre-calculations then disable the main pipeline button, perhaps display a "working" cursor or the like, call the calculation routine (in the main thread) , and enable the button when the computation process completes. (Cases b and c)
-
Hi,
As an alternative to @ChrisW67 suggestion, pipelines are usually graphs or list of tasks so you could simply prepend that preprocess task if your users select the optional preprocess and be done with it. No need for complex threading or change of logic.
-
Why is a thread even involved here?
- Start program
- Enable the calculation and main pipeline buttons
- The user can simply press the main pipeline button to continue (case a).
- If the user opts to do the pre-calculations then disable the main pipeline button, perhaps display a "working" cursor or the like, call the calculation routine (in the main thread) , and enable the button when the computation process completes. (Cases b and c)
@ChrisW67 in the actual app, the use case is that pre-calculation create some objects that are then visualized in the GUI even without running the pipeline. But if pipeline is run, then these objects CAN be passed to pipeline if selected, or pipeline still can be run without them. The thread is mainly there not to freeze the other visualizations while these objects are calculated.
-
Hi,
As an alternative to @ChrisW67 suggestion, pipelines are usually graphs or list of tasks so you could simply prepend that preprocess task if your users select the optional preprocess and be done with it. No need for complex threading or change of logic.
@SGaist as mentioned above in reply to @ChrisW67 (I cannot post several messages immediately as a new user), I am not sure there is a change to do it without a thread.
The goal of the thread is to let user freely explore the other parts of GUI during this calculation. Result of this calculation are also visualized in GUI, so these pre-calculations and the pipeline can exist independently in the app, and only if some parameters are selected, they are combined (case b and c). I am not sure there is some way to achieve this non-blocking behavior of the GUI without making these calculations as a separate QThread. -
If you don't want to freeze your UI, then yes threading is required.
Then, what you could do is that in the slot connected to the button starting the pipeline is to check the status of the pre calculation. If it's running, connect its finished signal to the function that actually start the pipeline, otherwise business as usual. Make it single shot, or disconnect it when calling the slot.