1

For my project, I have a Tkinter window as the main menu, from which, a pygame window can be launched. In the pygame window, a simulation runs with a conditional infinite main loop. I want to have a stop functionality so that the user can close the pygame window and be able to start a new one, however, if I call pygame.quit() (or sys.exit() or pygame.display.quit()) in the pygame program, it closes the Tkinter window as well, which I'd want to stay open.

I have tried to embed the pygame program into a Toplevel window, but after I stop the pygame main loop and call destroy() on the Toplevel window it closes, but the main Tk window freezes and will not respond to any user input, but code execution continues after calling destroy().

Pygame

Main loop

def run(self):
        while not self.exit_flag:

            self.display.fill(GREY)
            self.draw_text()
            self.draw_obstacles()

            self.pygame_event_check()  # a method for checking if event.type == pygame.QUIT

            if self.running:               #self.running is for pausing the window
                if self.counter >= self.lifespan:   # create new generation for simulation
                    self.counter = 0
                    new_generation, finished = self.rocket_pop.selection()
                    self.rocket_pop = Population(new_generation)
                    time.sleep(1)
                    self.gen_count += 1

                    if finished > 0 and self.reached_goal == False:     # tracking the first generation to reach the goal
                        print(f"Reached the goal in {self.gen_count} generations")
                        self.reached_goal = True


                for rocket in self.rocket_pop.population:    # runs the update method on all individuals
                    rocket.update(self.counter)

                self.counter += 1                            # keeping track of the lifetime of the rockets

                self.clock.tick(self.fps)
                pygame.draw.circle(self.display, PINK, self.target, 20)     # draw the goal
                pygame.display.update()

        print("exit")

Simulation start procedure

def run():     
    main = Main()
    mThread = threading.Thread(target=main.run)  # I get a "PyEval_RestoreThread: NULL tstate" error if I just call main.run()
    mThread.start()
    mThread.join()

Tkinter

Setup

import tkinter as tk
from genetic_stuff import *


class Application(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master=master)
        self.master = master
        self.pack()
        self.setup()

    def setup(self):
        start = tk.Button(self.master, text="Start", command = self.run_simulation)
        stop = tk.Button(self.master, text="Stop", command = self.stop_simulation)

        start.place(x=10, y=10, w=100, h=45)
        stop.place(x=240, y=10, w=100, h=45)

        ...

root = tk.Tk()
app = Application(master=root)
app.mainloop()

Start/ Stop simulation

    def run_simulation(self):
        self.sim_window = tk.Toplevel(self.master)

        embed = tk.Frame(self.sim_window, width = Main.window_width, height = Main.window_height) 
        embed.pack()
        os.environ['SDL_WINDOWID'] = str(embed.winfo_id())
        os.environ['SDL_VIDEODRIVER'] = 'windib'
        run()                                         
        self.sim_window.update()

    def stop_simulation(self):
        Main.exit_flag = True
        self.sim_window.destroy()
        self.sim_window.update()

links to pastebins of full code for replicating:

pics for reference:

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Sizzle
  • 11
  • 1
  • I suspect `while` is your problem. – Mike - SMT Feb 13 '20 at 20:14
  • You have 2 different functions called `run()` that may also be a problem. `Main.window_width` does not make sense. Main is a class. I think you mean `main.window_width` with a lower case. That said `main` is only a local variable to `run()` I am not sure it will work as expected. Also this line `self.sim_window.update()` can only ever error. – Mike - SMT Feb 13 '20 at 20:18
  • Move `self.sim_window.destroy()` inside `def run(self):` right after `print("exit")`. Also remove both `self.sim_window.update()` – stovfl Feb 13 '20 at 20:20
  • I have a similar app that combines a GTK window that controls things and displays numeric data, with a pygame window that displays the animations and graphics. I did not embed the pygame window in the GTK, window, I just let pygame manage it's own window. That way when my simulation stops and destroys the pygame window, GTK is not affected. – rcriii Feb 13 '20 at 20:24

0 Answers0