0

I have read a lot on threads, but I really need help with this one: I have a PyQt Main GUI that runs an optimization with scipy.minimize... As I cannot not make an example of this I use a "placeholder" process to show what my problem is.

I want to let the Main GUI remain stoppable by the User, if the Process takes too long to give a result. My working example is this one, where I use an integration with sympy

import sympy as sp
import numpy as np
import matplotlib.pyplot as plt
import time, sys
from PyQt4.QtCore  import *
from PyQt4.QtGui import *

class IntegrationRunner(QObject):
    'Object managing the integration'

    def __init__(self):
        super(IntegrationRunner, self).__init__() 
        self._isRunning = True

    def longRunning(self):      
        # reset
        if not self._isRunning:
            self._isRunning = True  

        print("preparing Integration")
        #this is known to be time consuming and returning not the right integral
        #this is a placeholder for a "time consuming" operation that sould 
        #be handled by a thread outside the main GUI
        #imagine an optimization process or any other time consuming operation
        #that would freeze the Main GUI

        t=sp.symbols('t')
        exp=sp.sqrt((3*t+1)/t)
        sol=sp.integrate(exp,t)
        print(sol)

        print('finished...') 

    def stop(self):
        self._isRunning = False
        #this is displayed when the "stop button" is clicked 
        #but actually the integration process won´t stop
        print("Integration too long - User terminated")

class SimulationUi(QDialog):
    'PyQt interface'

    def __init__(self):
        super(SimulationUi, self).__init__()

        self.goButton = QPushButton('Run Integration')
        self.stopButton = QPushButton('Stop if too long')


        self.layout = QHBoxLayout()
        self.layout.addWidget(self.goButton)
        self.layout.addWidget(self.stopButton)

        self.setLayout(self.layout)

        self.simulThread = QThread()
        self.simulThread.start()

        self.simulIntegration = IntegrationRunner()
        self.simulIntegration.moveToThread(self.simulThread)
        #self.simulIntegration.stepIncreased.connect(self.currentStep.setValue)

        # call stop on Integr.Runner from this (main) thread on click
        self.stopButton.clicked.connect(lambda: self.simulIntegration.stop())
        self.goButton.clicked.connect(self.simulIntegration.longRunning)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    simul = SimulationUi()
    simul.show()
    sys.exit(app.exec_()) 

After clicking the "start" button and the "stop" button before the integration stops automatically i get this output:

>>preparing Integration
>>Integration too long - User terminated
>>Integral(sqrt((3*t + 1)/t), t)
>>finished...

This isn´t exactly my problem but I want to understand how I can use a thread where I can put time consuming calculations and stop them in order to try other "parameters" or "initial guesses" when using something like Scipy Minimize Is it even possible to stop an iterative function from "outside" and restart it without getting into "non responding"? Any help to improve here is appreciated. I took these example here as a guideline how-to-signal-from-a-running-qthread-back-to-... and pastebin

Community
  • 1
  • 1
www.pieronigro.de
  • 840
  • 2
  • 12
  • 30
  • 1
    You should design your thread workload so it can be fragmented into "loops" instead of having it executed in a synchronous and blocking manner, this way you can check if the worker has received a "stop" message before each "loop" is attempted, and if so, terminate the workload in a safe manner. Otherwise you will only be able to `terminate()` the ugly way. This will involve keeping track of the state and progress of the workload using the worker object's members instead of "locals" so those can persist across the workload loops. – dtech Dec 15 '14 at 22:26
  • but if the task can not be fragmented into loops? is there a workaround? – www.pieronigro.de Dec 16 '14 at 08:08
  • @Hiatus Fragmenting the task into loops is the preferred way, and even with scipy.optimize.minimize you can (specify maxIter). And you can restart too (by using the output of the previous minimization as start for the next round. But killing a QThread that is still running (terminate) is really a bad idea since you do not know what side effects you will cause. Don't do that. – NoDataDumpNoContribution Dec 16 '14 at 09:08
  • see http://stackoverflow.com/questions/1898636/how-can-i-terminate-a-qthread for why terminating is bad and http://stackoverflow.com/questions/9243835/how-to-stop-a-qthread-that-runs-a-blocking-forever-loop for how to do it right – NoDataDumpNoContribution Dec 16 '14 at 09:09
  • @Trilarion when using Scipy-minimize, I don´t have access to the iteration as a loop, as I do not have it with the imported integration from sympy. I want to import a module and be able to stop any interative process from "outside" but within the thread – www.pieronigro.de Dec 16 '14 at 11:10
  • Doesn't seem this might be possible to do in a pretty way without `integrate()` being designed to be interruptible and running in chunks. If the thread has locked executing synchronous code you can only `terminate()` it forcefully and uncontrollably, and without the possibility to return a notification that it was interrupted. – dtech Dec 16 '14 at 11:27
  • In short, in order to be able to interrupt something it has to be designed to be interruptible in the first place. Sounds like you need to write your own integration or rewrite the one you are using to run in chunks. – dtech Dec 16 '14 at 11:36

0 Answers0