running 2 python windows simultaneously

Viewed 97

I'm creating a game app and it has a menu window then a separate game window. I'm trying to get a Tkinter window to act as the menu and a pygame window for the game however if the game is running, I cant use the menu (can't even close it). If I try I get a fatal error message:

Fatal Python error: PyEval_RestoreThread: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL)
Python runtime state: initialized

Current thread 0x00002afc (most recent call first):
  File "c:\Users\James\Documents\6th form\comp sci\NEA- A Level\code\gametst.py", line 12 in main
  File "c:\Users\James\Documents\6th form\comp sci\NEA- A Level\code\Menu.py", line 15 in chess
  File "C:\Users\James\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1884 in __call__
  File "C:\Users\James\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1421 in mainloop
  File "c:\Users\James\Documents\6th form\comp sci\NEA- A Level\code\Menu.py", line 22 in <module>

This is the minimum code to reproduce:

Menu:

from tkinter import *
import game

WIN = Tk()
WIN.geometry("200x200")

def f():
    game.main()

button = Button(WIN, text="button", command=f)
button.pack()

WIN.mainloop()

game:

def main():
    import pygame
    pygame.init()

    WIN = pygame.display.set_mode((200, 200))

    run = 1
    while run:
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = 0
                pygame.quit()

Is there a way to do this so that I can use the menu and the game at the same time? thx for any help

also if you know a better way to import the game to the menu that would be great, not overly bothered about that though

2 Answers

You can run the pygame part in a thread:

game.py

import pygame

def main():
    pygame.init()

    WIN = pygame.display.set_mode((200, 200))
    run = 1
    while run:
        pygame.display.update()
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = 0
                pygame.quit()
    print("pygame done")

def terminate():
    if pygame.get_init():
        pygame.event.post(pygame.event.Event(pygame.QUIT))

Note that I have added terminate() function for stopping the pygame loop.

menu.py

from tkinter import *
import game

WIN = Tk()
WIN.geometry("200x200")

def f():
    import threading
    # execute the pygame stuff in a thread
    threading.Thread(target=game.main).start()

button = Button(WIN, text="button", command=f)
button.pack()

WIN.mainloop()
game.terminate()  # terminate pygame loop

Well you can try to start it in another process. That way pygame will have no idea that tkinter has started it. You can try something like this:

# menu.py
from tkinter import *
from subprocess import Popen
from sys import stdin, stdout, stderr

window = Tk()
window.geometry("200x200")

def f():
    proc = Popen("python game.py", stdin=stdin, stdout=stdout, stderr=stderr, shell=True)

button = Button(window, text="button", command=f)
button.pack()

window.mainloop()

and:

# game.py
# Please note this isn't in a function.
import pygame
pygame.init()

WIN = pygame.display.set_mode((200, 200))

run = True
while run:
    pygame.display.update()
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            run = False
            pygame.quit()

The problem is that it isn't going to work if you start it in IDLE. So solve that issue remove the stdin=stdin, stdout=stdout, stderr=stderr and it should work.

With this approach you wouldn't be able to send any data between menu.py and game.py. The only thing you can do is call proc_still_alive = (proc.poll() == None) to see if the process is still alive. For that you will need to make proc a global variable.

Related