Multiple GUI screens from same python program - python

I've made a pathfinding visualizer using python and pygame. As of now, it can simulate only one algorithm at a time. I want to spawn multiple windows, each simulating different algorithm, side by side so that algorithms can be analyzed against each other. I have a function client.run() that draws the GUI. I'm trying to spawn multiple instances like this:
p=threading.Thread(target = client.run)
q=threading.Thread(target = client.run)
p.start()
q.start()
But by doing so my program hangs! Is there any way to rectify this problem, or any alternative way of running multiple instances/windows?

Pygame is built in a way to have a single window by process, you can't avoid that. The pygame.display module sets you a "display" and that is what you get.
You are in good look, as you have designed you software to work with threads, and have each thread control a display.. Just change the "threading" Python module for the multiprocessing, and use multiprocessing.Process instead of threading.Threads -- as long as you initialize pygame and its display from within each subprocess you should be ok.
I just teste here and teh example bellow works fine:
# -*- coding: utf-8 -*-
import pygame
import multiprocessing
from time import sleep
def init():
pygame.init()
screen = pygame.display.set_mode((320,240))
return screen
def main():
screen = init()
while True:
pygame.event.pump()
event = pygame.event.poll()
if event.type != pygame.NOEVENT:
print event
class Multigame(multiprocessing.Process):
def run(self):
return main()
for i in range(3):
Multigame().start()
while True:
sleep(1)

Related

pygame with multiprocessing

I'm trying to create a pygame program with a 2d drawable grid and methods that run for a long time. I want the main game loop to be able to process while the methods are running so I opted for the threading module and it worked fine but I found out that the multiprocessing module is better suited for CPU-heavy programs so I wanted to switch. The code below is an example or representative of my actual code
# importing the necessary libraries
class Board:
# class representing the actual 2d grid or board on screen
class Graph:
# class for drawing methods that take a long time to run
# Graph's methods call q.get() to get the Board object then
# make appropriate changes to it then call q.put() to put it back in the Queue
def draw_board(surface, rects):
# surface: pygame.display
# rects: list of pygame rectangle objects
# draw every rectangle in rects to the display surface.
def main():
# main game loop
board = Board(*args)
q = multiprocessing.Queue()
q.put(board)
graph = Graph(q)
while True:
draw_board(*args)
for event in pygame.event.get():
# checking some conditions and keypresses here
elif event.type == KEYDOWN:
if event.key == pygame.K_r:
t = multiprocessing.Process(target=graph.draw_sth)
t.start()
pygame.display.update()
# fps clock ticks for 60 FPS here
if __name__ == "__main__":
main()
I use multiprocessing.Queue to transfer resources from the main process to the processes spawned inside main() and back. When I run this and click the key "r", nothing happens and the terminal prints the introduction line when main is first called, namely
pygame 2.0.1 (SDL 2.0.14, Python 3.9.5)
Hello from the pygame community. https://www.pygame.org/contribute.html
This doesn't happen when I use threading so I assume that this is due to my usage of Queue or I might have misunderstood and misused multiprocessing instead. Any help is appreciated on the matter. For simplicity, some lines of code have been omitted.
Try calling this function before your event loop. ( I don't know if this will apply to multiprocessing )
def continue_pygame_loop():
pygame.mainloop(0.1)
yield
This post can do a better job of explaining the mechanics behind it : pyGame in a thread

pygame.error: video system not initialized, when threading is used [duplicate]

This question already has an answer here:
Multithreading with Pygame
(1 answer)
Closed 1 year ago.
Recently i completed learning about threading in python and now trying to implement it in a program. And here is the code:
from threading import Thread
from time import sleep
import pygame as py
py.init()
X,Y = 900, 800
APP = py.display.set_mode((X, Y))
APP = py.display.set_caption("Test")
def exit_app():
"To terminate program."
while True:
for eve in py.event.get():
if eve.type == py.QUIT:
py.quit()
exit()
def update_display():
"Update pygame window"
while True:
sleep(3)
py.display.update()
if __name__ == "__main__":
Thread(target=exit_app).start()
Thread(target=update_display).start()
Now the problem is that after creating thread i ran into a problem.
When i tried to run code it get executed without any problem but when the code exit(i.e., when i close the pygame window) i get to see the error as follows:
$python3 test.py
pygame 2.0.1 (SDL 2.0.14, Python 3.9.4)
Hello from the pygame community. https://www.pygame.org/contribute.html
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python3.9/threading.py", line 954, in _bootstrap_inner
self.run()
File "/usr/lib/python3.9/threading.py", line 892, in run
self._target(*self._args, **self._kwargs)
File "/home/cvam/Documents/test.py", line 23, in update_display
py.display.update()
pygame.error: video system not initialized
Ok, so first of all it seems like you are trying to create separate functions with infinite loops for updating the screen and for event handling, to clear the same, all of it can be done in one loop and is perhaps better way of doing the same.[Thus I have removed the exit_app and the update_display function since they use two other infinite while loops.]
Secondly there is no need to use a separate thread for event handling, since the event handling can be done in the same loop, when you create a separate thread for it with an infinite while loop, then that results in confusion as to which thread pygame shall run on, the main thread or the THREAD1.[Thus the same has been commented out.]
Also placing pygame.init function call and all other statements that were before outside inside the if name == 'main' block seems to ensure that they are executed. Also placing them before the thread starts makes sure that they are executed and that pygame stuff is initialized before the thread executes.
Also some suggestions -:
Instead of the sleep function from the time module, a similar function of the pygame time module namely the pygame.time.delay function can be used, its present within pygame and thus is more suitable to use.
I have experienced many errors while quitting the pygame programs using the inbuilt exit and quit functions, and thus seem to prefer using exit function of the sys module of python since it leads to less errors in most cases.
The code for the same shall become -:
# from threading import Thread
# from time import sleep
import pygame
import sys
if __name__ == "__main__":
pygame.init()
X,Y = 900, 800
APP = pygame.display.set_mode((X, Y))
APP = pygame.display.set_caption("Test")
# # By detecting the events within the main game loop there is no need
# # to call a separate thread, and the same may not be working since the two
# # threads the main one and the THREAD1 are both containing infinite loops and pygame
# # can only take over one at a time.
# THREAD1=Thread(target=exit_app)
# THREAD1.start()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit() # sys module's exit method seems to be better and causes less errors at exit
# # Without a delay the window will still not close incase this script is being
# # used for that purpose by you.
# pygame.time.delay(3) # Pygame alternative to sleep method of time module https://www.pygame.org/docs/ref/time.html#pygame.time.delay
pygame.display.update()
continue
pass

Non-blocking infinite loop

I have a Raspberry pi with a Sense hat. I've made a binary clock that I want to display and keep updated on the Sense hat's display. However, I want the ability to toggle the clock on and off with joystick middle. Everything's working fine, apart from my clock's update-loop blocking any new input once it's started.
from sense_hat import SenseHat
from signal import pause
def show_clock():
# clock-logic
def pushed_middle(event):
while 1:
show_clock()
sense = SenseHat()
sense.stick.direction_middle = pushed_middle
pause
I've been thinking about how to solve this. How to allow the script/clock to keep running and still accept new actions from the joystick. But once the while-loop starts, I'm stuck. I'm not sure what to google for. I've started looking into async/await, but that seem to be a Python 3.5+ feature, and my pi only has 2.7.9/3.4.2(I just sudo apt-get update/upgrade-ed). I've also tried moving the loop around in the program, but it's blocking everything no matter where I place it.
Is it a non-blocking (infinite) loop I'm looking for?
Is this what a game-/event-loop is?
Can I solve this with out using multiple threads(just curious, not a limitation if it's a must)?
Is this a general problem in "designing" infinite loops?
Can I approach this as a (reverse?) race condition? I was thinking about maybe using a semaphore as some kind of tool to not block, but I'm not sure.
I solved it by using a global variable:
from sense_hat import SenseHat
from signal import pause
def show_clock():
global clock_is_on
while clock_is_on: # clock-loop
# clock-logic
# ...
events = sense.stick.get_events()
for event in events:
if event.direction == "middle" and event.action == "pressed":
clock_is_on = False
time.sleep(1) # only need to update clock once every second
def pushed_middle(event):
if not clock_is_on:
clock_is_on = True
show_clock()
sense = SenseHat()
clock_is_on = False
sense.stick.direction_middle = pushed_middle
pause()

Executing multiple Python commands at once

I was wondering what the easiest way to execute two or more commands at the same time in python. For example:
from turtle import *
turtle_one=Turtle()
turtle_two=Turtle()
turtle_two.left(180)
#The lines to be executed at the same time are below.
turtle_one.forward(100)
turtle_two.forward(100)
You can effectively do this using the timer event that comes with the turtle module:
from turtle import Turtle, Screen
turtle_one = Turtle(shape="turtle")
turtle_one.setheading(30)
turtle_two = Turtle(shape="turtle")
turtle_two.setheading(210)
# The lines to be executed at the same time are below.
def move1():
turtle_one.forward(5)
if turtle_one.xcor() < 100:
screen.ontimer(move1, 50)
def move2():
turtle_two.forward(10)
if turtle_two.xcor() > -100:
screen.ontimer(move2, 100)
screen = Screen()
move1()
move2()
screen.exitonclick()
With respect to threads, as suggested by others, read up on the issues discussed in posts like Multi threading in Tkinter GUI as Python's turtle module is built on Tkinter and that recent post notes:
a lot of GUI toolkits are not thread-safe, and tkinter is not an
exception
Try using the threading module.
from turtle import *
from threading import Thread
turtle_one=Turtle()
turtle_two=Turtle()
turtle_two.left(180)
Thread(target=turtle_one.forward, args=[100]).start()
Thread(target=turtle_two.forward, args=[100]).start()
This starts the turtle_one/two.forward function in the background, with 100 as an argument.
To make it easier, make a run_in_background function...
def run_in_background(func, *args):
Thread(target=func, args=args).start()
run_in_background(turtle_one.forward, 100)
run_in_background(turtle_two.forward, 100)

Use pygame to open popup game without closing everything? - Python

I am an amateur programmer. I have a small (and urgent) problem. I am working on a text (console) based adventure game for fun. At a certain point, I want a pygame window to open. The player has to click in the window as fast as possible. The reaction time should be returned to the main program, and the pygame window should close. The main program will then continue running.
I've already written the script for the pygame window and it works fine. My main program also works fine. Now how do I call the pygame window from the main program?
I tried importing the pygame script but that didn't work.
Thanks.
Here's my pygame script:
import pygame, sys, time
from pygame.locals import *
pygame.init()
#Set up window
pygame.event.set_grab(0)
pygame.mouse.set_visible(1)
screen = pygame.display.set_mode((300,200))
shape = screen.convert_alpha()
pygame.display.set_caption("Sniper Alert")
#Colors
WHITE = (255, 255, 255)
BLACK = (0,0,0)
RED = (255, 0, 0)
#Draw on surface object
screen.fill(BLACK)
def alert():
#Create a font
font = pygame.font.Font(None,50)
#Render the text
text = font.render("Sniper Alert", True, RED)
#Create a rectangle
textRect = text.get_rect()
#Center the rectangle
textRect.centerx = screen.get_rect().centerx
textRect.centery = screen.get_rect().centery
#Blit the text
screen.blit(text, textRect)
pygame.display.update()
return press()
def press():
t0 = time.clock()
dt = 0
while time.clock() - t0 < 1.5:
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
dt = time.clock()- t0
return dt
#Exit
pygame.quit()
sys.exit()
#Run the game loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
There are three ways I can think of. Here they are:
Solution Number 1:
This way is probably the worst solution and the hardest to implement, but lets get it out of the way. I wouldn't advise using it but you may want to in some circumstances. You could use the threading module. Threading is designed for multitasking like this, and would do what you want. You can create a new thread like so:
import threading
def DoSomething(a,b,c): #this function will be called in the background, simultaneously to the main program
#do something here
apple = 1
banana = 2
cat = 3
mythread = threading.thread(target=DoSomething,args=(apple,banana,cat)) #defines a thread
mythread.start() #tells the thread to start running
Solution Number 2
A much better way to do this would be just to launch it as a different program. You could do that with the subprocess module, used for running command line commands. This would effectively run the program as if you had executed it in a new tab of your terminal (without the new tab). You can then make the program able to communicate with yours using subprocess.comunicate(). I will connect the input and output of the pygame program to your main program from here. Here is an example of this:
import subprocess
input = <insert input> #when you run the program and it requires something like a raw_input, this will give it input. You could probably avoid needing to send the pygame program input, because all you really want to do is receive an output from it.
process = subprocess.Popen(["python","<popup filename>"],stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=False) #this function is how subprocess runs shell/command prompt commands. On a side note, if you simply want to run a shell command without accessing input or output, just use "subprocess.call(<list of words in command (so ls -a would be ['ls','-a'])>)"
output = process.communicate(input)[0] #this will return any output the program prints, meaning you can communicate with the program and receive information from it
Using subprocess is probably the best way.
Solution Number 3
The final option, if you want it all in one file and do not want to mess around with threading, would be just to alternate the two programs in a while loop. Basically, you would run a loop that executes code from both programs. This is how it would work:
while True: #an infinite loop. it doesn't necessarily have to be infinite.
#call methods or run code to maintain text based game
#call methods or run code to maintain pygame window
this has worked just fine for me in a pygame game i made which also had a text component, but the subprocess way is probably better...

Categories

Resources