0

I am working on a simulation code, and I had separated by classes the simulation and the interface. I was looking for a stop button to stop my long simulation code, but the interface I have created "Is not responding" until:

  • the whole simulation stops, or
  • I get an error, or
  • I stop it using Spyder "stop command"*

The structure is the following:

class interface(tk.Tk):
    def __init__(self):
        super().__init__()
        self.startInterface()

    def startInterface():
        self.title('Simulation')  # Window's title
        self.minsize(450, 180)  # Window's size
        .
        .
        .
        frm_Mediumdown = tk.Frame(self, bd=7, relief='flat')
        frm_Mediumdown.grid(row=3, column=0, padx=0, pady=0)

        BTN_stop = tk.Button(frm_Mediumdown, text='Stop', command = self.stop)
        BTN_stop.grid(row=1, column=2, sticky=tk.W, padx=4)

        BTN_simulate = tk.Button(frm_Mediumdown, text='Simulate', 
                                                    command = self.Simulate)
        BTN_simulate.grid(row=1, column=0, sticky=tk.W, padx=4) 

    def Simulate(self):
        # here this function call another class which start the long simulation code
        Simulation.starts(a, b, etc)
        
    def stop(self):
        # here it should appear the code option to stop the simulation
        


class Simulation():
    # Long code which do the simulation
.
.
.

if __name__ == '__main__':
    print('Start')
    app = interface()
    app.mainloop()

I have tried to put a global option in the stop and Simulate functions def inside the interface class, but it doesn't work, it has the same problem when I launch the code.

And I've also tried the threading option and the daemon thread, but I didn't get any response.

martineau
  • 119,623
  • 25
  • 170
  • 301
Sultry T.
  • 69
  • 10
  • Daemon threads stop when the main thread stops. We can't help you with until you give us at least a bit of the code in the `Simulation` class. Also my guess is that the button stop being responsive when you start the simulation. – TheLizzard Apr 08 '21 at 09:43
  • Could You provide the whole code? (separate it from the MRE tho) especially the `starts()` method – Matiiss Apr 08 '21 at 10:10
  • The interface stops responding because running the simulation interferes with tkinter's `mainloop()`. You might be able to use threading to work around that — see my answer to [Freezing/Hanging tkinter Gui in waiting for the thread to complete](https://stackoverflow.com/a/53697547/355230). – martineau Apr 08 '21 at 10:22
  • Thank you @martineau for the corrections. I saw your answer over there but I didn't understand if I should do a new class apart which call the "simulate" class, or I should add the threading.Thread() and queue.Queue(), etc. inside the "simulate" class.. – Sultry T. Apr 08 '21 at 13:45
  • @TheLizzard I can add the simulation class structure, but is pointless, is just a class which take information and data from an Excel file, and then process it (which is too long). Actually the stop button do not respond, neither the other ones. – Sultry T. Apr 08 '21 at 13:46
  • @Matiiss is too long, and I don't thing the problem is over there. What is the MRE?? Thank you – Sultry T. Apr 08 '21 at 13:46
  • MRE is [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) – Matiiss Apr 08 '21 at 13:53
  • @SultryT. I need to see what is taking so long. There is no way of providing a correct answer without seeing how your code is structured. Does it use a `while` loop? Does it use a `for` loop? Is it just a few statements that are taking a long time? If it is a loop then you might be able to use just add `.update` in the loop to make sure tkinter is responding to button clicks. If it is a few statements that are taking a long time, you might be able to put them in another thread. – TheLizzard Apr 08 '21 at 14:04
  • @SultryT. Please note that button commands aren't run in a new thread. They are called from `.update`/`.mainloop` – TheLizzard Apr 08 '21 at 14:05
  • @SultryT.: I don't think you would need a new class but will need to modify your `Simulation` class. The `ThreadedClient` class would need to be changed to start the simulation running and the `Simulation` class would need to be changed to put messages in the queue about its progress or perhaps just a single one to indicate when it's finished. The idea being that in the meantime the `GuiPart` will keep running and not freeze. – martineau Apr 08 '21 at 17:50

1 Answers1

0

try doing this instead:

def simulate(self):
    simulation = Simulation()
    self.after(1000, simulation.starts, a, b, etc)

however does the starts() method run in loop? also try (not suggested) adding root.update() somewhere in the starts() method (root, is whatever is Your root (like interface))

Matiiss
  • 5,970
  • 2
  • 12
  • 29
  • Actually `self.after(1000, simulation.starts, a, b, etc)` is better. – TheLizzard Apr 08 '21 at 10:33
  • @TheLizzard but if the function ran in loop would it help? also edited the code – Matiiss Apr 08 '21 at 11:53
  • `self.after(1000, lambda: simulation.starts(a, b, etc))` is the same as `self.after(1000, simulation.starts, a, b, etc)` but the latter one looks better. In the `.after` documentation it says that the first argument is the time in ms the second one is the function and the rest are just arguments that are passed in the function that will be called. – TheLizzard Apr 08 '21 at 12:21
  • @TheLizzard ok, thanks for clarifying, also does after execute code in parllel? I am a bit confused about that one. Does it just wait to execute the function passed to it for the amount of time and just execute it without interupting mainloop. So basically if one was to run a while loop using after would the loop still pause rest of the process? – Matiiss Apr 08 '21 at 12:30
  • All functions passed into tkinter run on the tkinter thread (to test that just try `root.after(1000, time.sleep, 30)` - you will see the window stop being responsive). When a function/event binding needs to be called the `.mainloop` or `.update` call that function (as a normal call no threads). Therefore, tkinter calls all user code in the same thread. – TheLizzard Apr 08 '21 at 12:37
  • Thank you, @Matiiss, for the answer, and yes there is a loop. Actually I have tried your proposition, and it worked partially. If I do not add the "lambda" doesn't work, with the "lambda works, BUT, the interface is freeze, during the simulation, and then it checks if it should stop or not, at least it makes the stop button work!! – Sultry T. Apr 08 '21 at 13:51
  • @SultryT. I think You will have to use threads if You don't know anything about them I suggest this [tutorial](https://www.youtube.com/watch?v=IEEhzQoKtQU), since already it was explained that loops can interfere with the `.mainloop()` so at least some issues were solved but You will have to use threads I think – Matiiss Apr 08 '21 at 13:55