passing a parameter to a method causes Window to freeze
-
Hi! I've been able to (mostly) successfully use pyqt5 to create a benchmarking app that runs a series of algorithms/tests based on the number of trials desired by the user. I have set up 5 different tests that are all variations of the same formula. For example, in the first test, the algorithm is run 10 times, while in the second test, it is run 20 times, etc.
What I find confounding is that if I use DRY principles to simplify this test into a single function with a parameter passed to the function to indicate the number of trials to be run (i.e. n=10, n=20, etc), it causes the pyqt5 app (Window) to freeze/ hang when the function is called. For example, using the following syntax to call my method will result in a freeze.
result = runTest_A(num_shots)
Whereas, if I create 5 separate methods and then hardcode the number of trials (10,20,30, etc) into each method such that no variable is needed to be passed, and then run them, then the app works fine and does not hang while the methods are running (i.e.)
result = runTest_A_no_parameter()
It should be noted that in either case, the underlying methods run just fine in the background. The issue is that the GUI freezes in the first case when a variable is passed. It should be further noted that I am using threads to run the functions when they are called, i.e.:
# Start running functions in a separate thread self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run_function_A) self.thread.start()
Does anyone have any experience with this issue, or know how to solve it? Is using Queue or something similar something I should consider? Is this issue fixed in pyqt6?
Thanks in advance,
Case 1:Window does not hang when functions called do not have passed parameters
Case 2: Window hangs/ freezes when functions called do have passed parameters -
@robliou
Passing parameters or not should not be relevant. Blocking the event loop might cause a "freeze", and if you do anything UI from your threads that would be undefined. Threads are often a cause of problems. Please produce a minimal example of whatever you are doing which shows the issue, and a corresponding one which does not. -
Sure thing. Assume I have a method called 'run_all_tests' which runs 5 sub- tests:
def run_all_tests(num_shots): result_a = runTest_A(num_shots) result_b = runTest_B(num_shots) result_c = runTest_C(num_shots) result_d = runTest_D(num_shots) result_e = runTest_E(num_shots)
if I call run_all_tests(100), then run_all_tests() will correctly pass the parameter 100 to each subtest and they all run fine in the background. However, in my qt Window widget it will hang (i.e I click anywhere on the window while it is running). Whereas, if I edit runTest_A, runTest_B, etc. such that the number of shots is hardcoded, and as such no parameter needs to be passed, as follows:
def run_all_tests(): result_a = runTest_A() result_b = runTest_B() result_c = runTest_C() result_d = runTest_D() result_e = runTest_E()
and then call run_all_tests(), then all 5 sub- functions again run properly in order, but with no qt Window hanging issues, even if I click somewhere on the Window itself.
The way my run_all_tests() function is connected is as follows (note that the function launches on the event of a button click):
... elif selected_item == 'Multi-test': self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.run_all_tests) self.thread.start()
I'm open to any suggestions you may have, thank you.
-
@JonB
Checking out my code again, it's possible the issue has less to do with passing parameters to methods and more to do with the fact that I'm offloading a method to other files and then trying to run it from main.py?After all, my original main.py file has grown to be quite large; it has two classes, Worker() and Main(), with each one having multiple methods. For simplicity's sake, I tried to move some of the methods to other files and then import them into my main.py file. The importing works fine, but it causes the hanging issue with qt Window.
For example, I have a run_multi() method that I offloaded into a separate run_multi.py file and then re-imported into my main.py via:
from functions.run_multi import run_multi_main class Worker(QObject): def main(self): run_multi_imported = run_multi_main(self) return run_multi_imported
and then called later on, after button press, in my Main() class in the same file via:
elif selected_item == 'Multi-test': self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(lambda: self.worker.main()) self.thread.start()
Again, if I never offloaded this run_multi() function into a separate file and kept it in main.py, then everything runs smoothly in qt with no hanging issues. But I noticed when I imported it, the qt hanging issues arose. Any thoughts?
Thanks,
-
@JonB
This is probably the cleanest way for me to explain the issue: seen below, I have a method called 'run_all_functions' in my Worker() class which allows the user to select which suite of algos to run based on the click of a button (attached to a dropdown menu within qt):import suite_1_algo_a, suite_1_algo_b, suite_1_algo_c import suite_2_algo_a, suite_2_algo_b, suite_2_algo_c class Worker(QObject): def run_all_functions(self): if algo == 'suite_1': test_a = suite_1_algo_a test_b = suite_1_algo_b test_c = suite_1_algo_c if algo == 'suite_2': test_a = suite_2_algo_a test_b = suite_2_algo_b test_c = suite_2_algo_c result_1 = test_a.main result_2 = test_b.main result_3 = test_c.main print('result_1:', result_1 ) print('result_2:', result_2 ) print('result_3:', result_3)
After selecting which suite to run, the pyqt Window will hang even as the selected function is running in the background. Whereas, if I simply create two separate functions with the values for test_a, test_b, and test_c hardcoded (i.e. no if/else option at the start for various algo scenarios), and then run either hardcoded function based on the press of a qt button, then the Window widget does not hang. Both the hardcoded and non-hardcoded functions are similarly initiated as threads, as follows:
elif selected_item == 'run_hardcoded_function': self.thread = QThread() self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run_hardcoded_algo) self.thread.start()
-
@robliou said in passing a parameter to a method causes Window to freeze:
The way my run_all_tests() function is connected is as follows (note that the function launches on the event of a button click):
The code you posted decribes how you call
run_all_tests()
. However, what are you doing to callrun_all_tests(num_shots)
when a button is clicked? -
@SimonSchroeder
Good question. For now, I'm just assuming that 'num_shots' = 100 for all tests and have hardcoded this into the run_all_tests() function, so that the variable does not need to be passed. As an example:def run_all_tests(self): num_shots = 100 #allows the cancel button to work while self._is_running: if not self.has_run: self.has_run = True self.result.emit('Running function #1- Simulated Annealing- 5 mins.') self.progress.emit(1) result_x_1, result_y_1 = runTest_100_A.main(num_shots) self.result.emit('result_x_1: ' + result_x_1) self.progress.emit(10) # Emit signal to update progress bar to 10% self.counter += 1 print('this is counter', self.counter) # need this in front of each function in order to allow cancel button to work if not self._is_running: return #run second algorithm result_x_2, result_y_2 = runTest_100_B.main(num_shots) self.result.emit('result_x_2: ' + result_x_2) self.progress.emit(20) # Emit signal to update progress bar to 20% self.counter += 1 print('this is counter', self.counter) if not self._is_running: return
etc. (this repeats for all 10 algorithms until result_x_10, result_y_10 is reached). Note that the qt Window hanging usually begins around the time we reach result_x_5)
-
Hi and Good question. So, at the top of my Worker() class, I have:
result = pyqtSignal(str) # Signal emitted after each function with the result
and then later on in my Main() class I have:
self.worker.result.connect(self.update_text_browser)
this is the update_text_browser function:
def update_text_browser(self, result): self.textBrowser.insertHtml('<p align="left"><font face="Arial" color="black" size=3 ><b>'+ result + '</b></font><br></p>')
I'm assuming that all of this allows the emitted result to be displayed in my text_browser? 'result' is obviously successfully emitting to 'text_browser', but yes and weirdly, around the 4th or 5th emit the Window seems to hang... what could be the cause?
As for using debugger, are you referring to pdb? I tried implementing it, but it didn't seem to work unless I ran it from the Main() class (as opposed to Worker()), but running it from Main() resulted in an infinite loop and not the function itself running. I will try again, unless you are referring to another debugger specific to pyqt?
Thank you,