Run 2 pygame windows with 2 different processes [duplicate] - python

I need to to build an application that has multiple windows. In one of these windows, I need to be able to play a simple game and another window has to display questions and get response from a user that influences the game.
(1) I was wanting to use pygame in order to make the game. Is there a simple way to have pygame operate with multiple windows?
(2) If there is no easy way to solve (1), is there a simple way to use some other python GUI structure that would allow for me to run pygame and another window simultaneously?

The short answer is no, creating two pygame windows in the same process is not possible (as of April 2015). If you want to run two windows with one process, you should look into pyglet or cocos2d.
An alternative, if you must use pygame, is to use inter-process communication. You can have two processes, each with a window. They will relay messages to each other using sockets. If you want to go this route, check out the socket tutorial here.

Internally set_mode() probably sets a pointer that represents the memory of a unique display. So if we write:
screenA = pygame.display.set_mode((500,480), 0, 32)
screenB = pygame.display.set_mode((500,480), 0, 32)
For instance we can do something like that later:
screenA.blit(background, (0,0))
screenB.blit(player, (100,100))
both blit() calls will blit on the same surface. screenA and screenB are pointing to the same memory address. Working with 2 windows is quite hard to achieve in pygame.

Yes, that is possible. SDL2 is able to open multiple windows. In the example folder you can take a look at "video.py".
https://github.com/pygame/pygame/blob/main/examples/video.py
"This example requires pygame 2 and SDL2. _sdl2 is experimental and will change."

I've been trying to do this for a few days now, and I'm finally making progress. Does this count as simple? Or even as "being in" pygame. In this exampe I never even call pygame.init() That just seems to get in the way. The event pump is running (for mouse and keyboard) but not all the normal events seem to be coming thru (FOCUSGAINED and LOST in particular). In this example each window renders it status (size, position, etc) to it's self. I also have versions where I mix SDL windows with the pygame display. But those involve encapsulating a Window rather than extending it.
In order to draw on these windows you can draw on a vanilla surface as usually and then use the Renderer associated with the window to create a texture that will update the the window. (texture.draw(), renderer.present). You dont't use display.update() or flip() because you you aren't using the pygame display surface.
The X11 package is just my experimental windowing stuff and has nothing to do with X11. I think all my imports are explicit so it should be easy to figure out what the missing pieces are.
from typing import List
from pygame import Rect, Surface, Color
import pygame.event
from pygame.event import Event
from pygame.freetype import Font
from pygame._sdl2.video import Window, Renderer, Texture
from X11.windows import DEFAULT_PAD, default_font, window_info
from X11.text import prt
class MyWindow(Window):
def __init__(self, font: Font=None):
super().__init__()
self._font = font if font else default_font()
self.resizable = True
self._renderer = None
def destroy(self) -> None:
super().destroy()
def update(self):
r = self.renderer
r.draw_color = Color('grey')
r.clear()
#self.render_line(f"TICKS: {pg.time.get_ticks()}", 5, size=16.0)
txt: List[str] = window_info(self)
self.render_text(txt, lineno=0)
r.present()
#property
def renderer(self):
if self._renderer is None:
try:
self._renderer = Renderer.from_window(self)
except:
self._renderer = Renderer(self)
return self._renderer
def render_text(self, txt: List[str], lineno: int=0):
for line in txt:
self.render_line(line, lineno, size=16.0)
lineno += 1
def render_line(self, txt: str, lineno: int = 0, size: float = 0.0):
font = self._font
line_spacing = font.get_sized_height(size) + DEFAULT_PAD
x = DEFAULT_PAD
y = DEFAULT_PAD + lineno * line_spacing
# compute the size of the message
src_rect = font.get_rect(txt, size=size)
# create a new surface (image) of text
l_surf = Surface((src_rect.width, src_rect.height))
src_rect = font.render_to(l_surf, (0, 0), txt, size=size)
# get ready to draw
texture = Texture.from_surface(self.renderer, l_surf)
dst = Rect(x, y, src_rect.width, src_rect.height)
texture.draw(None, dst)
_running: bool = False
def test():
global _running
win1 = MyWindow()
win2 = MyWindow()
my_windows = {win1.id: win1, win2.id: win2}
win = win1
rnd = win1.renderer
print("pygame.get_init():", pygame.get_init())
print("pygame.display.get_init():", pygame.display.get_init())
print("pygame.mouse.get_pos():", pygame.mouse.get_pos())
clock = pygame.time.Clock()
_running = True
while _running:
events = pygame.event.get()
for event in events:
if event.type != pygame.MOUSEMOTION:
print(event)
if event.type == pygame.QUIT:
_running = False
elif event.type == pygame.WINDOWENTER:
win = my_windows[event.window.id]
print(f"Enter Window ({event.window.id}")
elif event.type == pygame.WINDOWLEAVE:
pass
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_q:
_running = False
if event.key == pygame.K_1:
win = my_windows[1]
rnd = win.renderer
if event.key == pygame.K_2:
win = my_windows[2]
rnd = win.renderer
elif event.key == pygame.K_b:
rnd.draw_color = Color('blue')
rnd.clear()
elif event.key == pygame.K_g:
rnd.draw_color = Color('grey')
rnd.clear()
elif event.key == pygame.K_t:
win.render_line("Hello, world")
elif event.key == pygame.K_s:
surface = pygame.display.get_surface()
print("surface: ", surface)
elif event.key == pygame.K_f:
pygame.display.flip()
# pygame.error: Display mode not set
elif event.key == pygame.K_u:
pygame.display.update()
# pygame.error: Display mode not set
for win in my_windows.values():
win.update()
clock.tick(40)
if __name__ == '__main__':
test()

Related

Problem with pymunk while running in Visual Studio

I tried using the code so I can run a simulation of an object hitting on the ground but it just says
draw_polygon ([Vec2d(55.0, -4779353554820.233), Vec2d(55.0, -4779353554810.233), Vec2d(45.0, -4779353554810.233), Vec2d(45.0, -4779353554820.233)], 0.0, SpaceDebugColor(r=44.0, g=62.0, b=80.0, a=255.0), SpaceDebugColor(r=52.0, g=152.0, b=219.0, a=255.0))
import pymunk
#This comands sets the scene for our prosomition
space=pymunk.Space()
space.gravity = 0,-9.80665
body = pymunk.Body()
body.position= 50,100
#This comands create a box that attaches to the body and creates its settings
poly = pymunk.Poly.create_box(body)
poly.mass = 10
space.add(body, poly)
#Creates and prints the scene
print_options = pymunk.SpaceDebugDrawOptions()
speed=int(input("Speed:"))
while True:
space.step(speed)
space.debug_draw(print_options)
Im trying to run this on my visual studio but it's just saying:
draw_polygon ([Vec2d(55.0, -4779353554820.233), Vec2d(55.0, -4779353554810.233), Vec2d(45.0, -4779353554810.233), Vec2d(45.0, -4779353554820.233)], 0.0, SpaceDebugColor(r=44.0, g=62.0, b=80.0, a=255.0), SpaceDebugColor(r=52.0, g=152.0, b=219.0, a=255.0))
Is there any package for an graphical enviroment ?
Yes, by default the debug drawing will just print out the result (its made in this way so that you can use it without installing anything else and even run it from the terminal).
However, it also comes with a module for the two libraries pygame and pyglet that are documented here:
http://www.pymunk.org/en/latest/pymunk.pygame_util.html
and here
http://www.pymunk.org/en/latest/pymunk.pyglet_util.html
Both work in more or less the same way, just use their implementation of the SpaceDebugDrawOptions class instead of the default one.
An example of your code converted to pygame is below. Note that in your example you have speed (the input to space.step) set to an integer. However, when displaying its too big step size to see anything. I have adjusted it to speed/50 so if you enter 1 as speed, then it will progress in the speed matching pygame clock (clock.tick(50)). Without this the box moves too quickly out of the window:
import pygame
import pymunk
import pymunk.pygame_util
speed=int(input("Speed:"))
pygame.init()
screen = pygame.display.set_mode((600, 600))
clock = pygame.time.Clock()
running = True
### Physics stuff
space = pymunk.Space()
space.gravity = 0,-9.80665
body = pymunk.Body()
body.position= 50,100
#This comands create a box that attaches to the body and creates its settings
poly = pymunk.Poly.create_box(body)
poly.mass = 10
space.add(body, poly)
#Creates and prints the scene
print_options = pymunk.pygame_util.DrawOptions(screen)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
running = False
### Draw stuff
screen.fill(pygame.Color("white"))
space.step(float(speed) / 50)
space.debug_draw(print_options)
### Flip screen
pygame.display.flip()
clock.tick(50)
pygame.display.set_caption("fps: " + str(clock.get_fps()))

Problem with making Circle properly appear on screen in Pygame

I think my understanding of Pygame is a little bit weak. I would appreciate any help in general about the intricacies of the code (since this was given by the teacher) or simply how I can at least make the obstacle visible.
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
Above is where I actually do the drawing and attempt to draw the circle.
The CircularObstacle class is a very simple class that looks like this:
import pygame
class CircularObstacle():
def __init__(self, x, y, radius): #MAYBE ADD A SIZE
self.x = x
self.y = y
self.radius = radius
The problem is that the circle only draws itself when the boids have went over it, which is really weird. I think it has to do with the way the pygame has been setup with and the Surfaces and everything, so below is all the code in main. Of course the obstacle does not work as intended, but I plan to fix that later, first I want to at least get a circle to show.
Below is my full code because I believe it is crucial to solving the issue:
import pygame
from pygame.locals import *
import argparse
import sys
from boid import Boid
from Obstacle import CircularObstacle
def add_boids(boids,num_boids):
for boid in range (num_boids):
boids.add(Boid())
def update(dt, boids):
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit(0)
elif event.type == KEYDOWN:
mods = pygame.key.get_mods()
if event.key == pygame.K_q:
# quit
pygame.quit()
sys.exit(0)
elif event.key == pygame.K_UP:
# add boids
if mods & pygame.KMOD_SHIFT:
add_boids(boids, 100)
else:
add_boids(boids, 10)
elif event.key == pygame.K_DOWN:
# remove boids
if mods & pygame.KMOD_SHIFT:
boids.remove(boids.sprites()[:100])
else:
boids.remove(boids.sprites()[:10])
#ADD STUFF LIKE THE SLIDER AND STUFF
for b in boids:
b.update(dt, boids)
def draw(screen, background, boids, obstaclearray):
#redrawing the whole window
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
pygame.display.update(dirty)
default_boids = 0
default_geometry = "1000x1000"
# Initialise pygame.
pygame.init()
pygame.event.set_allowed([pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP])
# keep a good framerate so the graphics are better
fps = 60.0
fpsClock = pygame.time.Clock()
# Set up pygamme window
window_width, window_height = 800,600
flags = DOUBLEBUF
screen = pygame.display.set_mode((window_width, window_height), flags)
screen.set_alpha(None)
background = pygame.Surface(screen.get_size()).convert()
background.fill(pygame.Color('black'))
boids = pygame.sprite.RenderUpdates()
add_boids(boids, default_boids)
obstaclearray = []
defaultcircleobstacle = CircularObstacle(200,200,13)
obstaclearray.append(defaultcircleobstacle)
#The "game loop"
dt = 1/fps # here dt means the amount of time elapsed since the last frame
#it seems like thie is a forever loop but in reality this is not since in the update method we provide functinality to quit the program
while True:
update(dt, boids)
draw(screen, background, boids, obstaclearray)
dt = fpsClock.tick(fps)
When you call pygame.display.update() you have 2 options. You can call it without any parameter. In this case the complete screen is updated.
pygame.display.update()
Or call it with a list of rectangular regions that need to be updated. In this case, only the rectangular areas will be updated.
pygame.display.update(rect_list)
You do the 2nd option, but the areas where the circles are drawn are not in the dirty list, therefore this regions are not updated.
pygame.display.update(dirty)
Either update the whole screen with pygame.display.update() or add the regions of the circles to the dirty list:
def draw(screen, background, boids, obstaclearray):
boids.clear(screen, background)
dirty = boids.draw(screen)
for element in obstaclearray:
dirty_rect = pygame.draw.circle(screen, (255,255,255), (element.x, element.y), element.radius)
dirty.append(dirty_rect)
pygame.display.update(dirty)

Pygame through SSH does not register keystrokes (Raspberry Pi 3)

So I got raspi 3 and simple 8x8 LED matrix. After some playing with it I decided to make a simple snake game (displaying on that matrix) with pygame's events, I have no prior experience with pygame. There is no screen/display connected besides the led matrix.
So the problem at first was "pygame.error: video system not initialized", though I think i got it fixed by setting an env variable:
os.putenv('DISPLAY', ':0.0')
Now that I got it working I run it...and nothing happens, like no keystrokes are registered. Just this "junk", I don't know how to call it The dot on LED matrix is not moving. If i alter the snake's x or y position somewhere in the loop it moves as intended.
My code:
#!/usr/bin/python2
import pygame
import max7219.led as led
from max7219.font import proportional, SINCLAIR_FONT, TINY_FONT, CP437_FONT
import numpy as nqp
import os
SIZE = (8, 8)
class Board:
def __init__(self, size, snake):
"Board object for snake game"
self.matrix = np.zeros(size, dtype=np.int8)
self.device = led.matrix()
self.snake = snake
def draw(self):
#add snake
self.matrix = np.zeros(SIZE, dtype=np.int8)
self.matrix[self.snake.x][self.snake.y] = 1
for x in range(8):
for y in range(8):
self.device.pixel(x, y, self.matrix[x][y], redraw=False)
self.device.flush()
def light(self, x, y):
"light specified pixel"
self.matrix[x][y] = 1
def dim(self, x, y):
"off specified pixel"
self.matrix[x][y] = 0
class Snake:
def __init__(self):
"Object representing an ingame snake"
self.length = 1
self.x = 3
self.y = 3
if __name__=="__main__":
os.putenv('DISPLAY', ':0.0')
pygame.init()
snake = Snake()
board = Board(SIZE, snake)
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
snake.y -= 1
elif event.key == pygame.K_DOWN:
snake.y += 1
elif event.key == pygame.K_LEFT:
snake.x -= 1
elif event.key == pygame.K_RIGHT:
snake.x += 1
board.draw()
I'm using pygame because I don't know anything else (Well I can't use pygame either but I just don't know of any alternatives). If it can be done simpler I will be happy to do it. Thank You in advance!
You should be able to use curses. Here's a simple example:
import curses
def main(screen):
key = ''
while key != 'q':
key = screen.getkey()
screen.addstr(0, 0, 'key: {:<10}'.format(key))
if __name__ == '__main__':
curses.wrapper(main)
You'll see that your key presses are registered - they're just strings.
However, this runs in blocking mode. Assuming that your code needs to do other things, you can turn nodelay on:
def main(screen):
screen.nodelay(True)
key = ''
while key != 'q':
try:
key = screen.getkey()
except curses.error:
pass # no keypress was ready
else:
screen.addstr(0, 0, 'key: {:<10}'.format(key))
In your scenario you probably would put this inside your game loop that's drawing out to your 8x8 display, so it would look something like this:
game = SnakeGame()
while game.not_done:
try:
key = screen.getkey()
except curses.error:
key = None
if key == 'KEY_UP':
game.turn_up()
elif key == 'KEY_DOWN':
game.turn_down()
elif key == 'KEY_LEFT':
game.turn_left()
elif key == 'KEY_RIGHT':
game.turn_right()
game.tick()
One thing to note - this approach will take 100% of your CPU, so if you don't have some other way to limit what your app is doing it can cause you some problems. You could extend this approach using threading/multiprocessing, if you find that to be something that you need.

text not showing up dynamically, pygame

The code below is supposed to create a green button that makes a score text appear. unfortunately the button does nothing, and the only way I've managed to get it to work is by putting the function call for makeText in the while loop instead of in the clickButton function, but if I do that it's no longer dynamic. Can someone explain why the text isn't showing up when I press the button and fix my code so it does show up?
import pygame
import sys
#game stuff
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
#functions
def makeText(title,text,posx,posy):
font=pygame.font.Font(None,30)
scoretext=font.render(str(title)+ ": " +str(text), 1,(0,0,0))
screen.blit(scoretext, (posx, posy))
def clickButton(name,x,y,width,height):
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#update display
pygame.display.update()
clock.tick(60)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
The problem is that once you created the text, your main loop keeps going and calls screen.fill, overdrawing the text even before pygame.display.update() is called.
You could change it to:
...
def clickButton(name,x,y,width,height):
print x + width > cur[0] > x and y + height > cur[1] > y
if x + width > cur[0] > x and y + height > cur[1] > y:
if click == (1,0,0):
makeText("score",300,100,10)
#objects
button1 = pygame.Rect((0,0), (32,32))
while True:
screen.fill((255,255,255))
screen.fill((55,155,0), button1)
#event handling
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.MOUSEBUTTONDOWN:
cur = event.pos
click = pygame.mouse.get_pressed()
clickButton("button1",button1.left,button1.top,button1.width,button1.height)
...
so the text is created after filling the screen with the background color and before pygame.display.update() is called, but that does not solve the problem of the screen being filled again the next iteration of the while loop.
So the solution is to keep track of the fact that the button was pressed, a.k.a. keeping track of a state.
Here's an example of a different approach, using classes for the buttons and a dict for the global state (so you don't need global variables, which should you avoid most of the time, because it can get very confusing fast if your game starts becoming more complex).
Click the first button to show or hide the score, and click the second button to change the background color and earn 100 points.
See how easy it becomes to create new buttons; it's just adding a simple function.
import pygame
import sys
import random
pygame.init()
screen = pygame.display.set_mode((640, 480),0,32)
clock = pygame.time.Clock()
# create font only once
font = pygame.font.Font(None,30)
# it's always a good idea to cache all text surfaces, since calling 'Font.render' is
# an expensive function. You'll start to notice once your game becomes more complex
# and uses more text. Also, use python naming conventions
text_cache = {}
def make_text(title, text):
key = "{title}: {text}".format(title=title, text=text)
if not key in text_cache:
text = font.render(key, 1,(0,0,0))
text_cache[key] = text
return text
else:
return text_cache[key]
# we use the 'Sprite' class because that makes drawing easy
class Button(pygame.sprite.Sprite):
def __init__(self, rect, color, on_click):
pygame.sprite.Sprite.__init__(self)
self.rect = rect
self.image = pygame.Surface((rect.w, rect.h))
self.image.fill(color)
self.on_click = on_click
# this happens when the first button is pressed
def toggle_score_handler(state):
state['show_score'] = not state['show_score']
# this happens when the second button is pressed
def toggle_backcolor_handler(state):
state['backcolor'] = random.choice(pygame.color.THECOLORS.values())
state['score'] += 100
# here we create the buttons and keep them in a 'Group'
buttons = pygame.sprite.Group(Button(pygame.Rect(30, 30, 32, 32), (55, 155 ,0), toggle_score_handler),
Button(pygame.Rect(250, 250, 32, 32), (155, 0, 55), toggle_backcolor_handler))
# here's our game state. In a real
# game you probably have a custom class
state = {'show_score': False,
'score': 0,
'backcolor': pygame.color.Color('White')}
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
# you can check for the first mouse button with 'event.button == 1'
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
# to check if the mouse is inside the button, you
# can simple use the 'Rect.collidepoint' function
for button in (b for b in buttons if b.rect.collidepoint(event.pos)):
button.on_click(state)
screen.fill(state['backcolor'])
# draw all buttons by simple calling 'Group.draw'
buttons.draw(screen)
if state['show_score']:
screen.blit(make_text("score", state['score']), (100, 30))
pygame.display.update()
clock.tick(60)
You are checking the value of "click" in the clickButton function, but I don't see click defined anywhere that clickButton would have access to it.
Perhaps you should pass click as an argument in the clickButton function, which would then possibly make the if condition true?

pygame code organization

I've started to learn making games with python/pygame and as though it is easy to make a working game quickly in pygame, there's no real tutorial on how to organize the code in a sensible way.
On the pygame tutorials page, I've found 3 ways to do it.
1- No use of classes, for small projects
2- MVC ruby-on-rails kind of structure but without the rails framework which results in something overly complicated and obscure (even with OO programming and rails knowledge)
3- C++-like structure as follows: (clean and intuitive but maybe not very much python-like?)
import pygame
from pygame.locals import *
class MyGame:
def __init__(self):
self._running = True
self._surf_display = None
self.size = self.width, self.height = 150, 150
def on_init(self):
pygame.init()
self._display_surf = pygame.display.set_mode(self.size)
pygame.display.set_caption('MyGame')
#some more actions
pygame.display.flip()
self._running = True
def on_event(self, event):
if event.type == pygame.QUIT:
self._running = False
elif event.type == KEYDOWN:
if event.key == K_ESCAPE:
self._running = False
elif event.type == MOUSEBUTTONDOWN:
if event.button == 1:
print event.pos
def on_loop(self):
pass
def on_render(self):
pass
def on_cleanup(self):
pygame.quit()
def on_execute(self):
if self.on_init() == False:
self._running = False
while( self._running ):
for event in pygame.event.get():
self.on_event(event)
self.on_loop()
self.on_render()
self.on_cleanup()
if __name__ == "__main__" :
mygame = MyGame()
mygame.on_execute()
I'm used to make SDL games in C++ and I use this exact structure but I'm wondering if it's usable for both small and large projects or if there's a cleaner way in pygame.
For example I found a game organized like this:
imports
def functionx
def functiony
class MyGameObject:
class AnotherObject:
class Game: #pygame init, event handler, win, lose...etc
while True: #event loop
display update
It also looks very well organized and easy to follow.
Which structure should I use consistently in all my projects so as to have a clean code usable in small and large games?
I'd also suggest maybe using comments (as dreary as it seems as first) for dividing your work. As an example:
import pygame, random, math
## CLASSES ----------------------------------------------
class Ball():
def __init__(self, (x,y), size):
"""Setting up the new instance"""
self.x = x
self.y = y
self.size = size
## FUNCTIONS --------------------------------------------
def addVectors((angle1, length1), (angle2, length2)):
"""Take two vectors and find the resultant"""
x = math.sin(angle1) * length1 + math.sin(angle2) * length2
y = math.cos(angle1) * length1 + math.cos(angle2) * length2
## INIT -------------------------------------------------
width = 600
height = 400
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("S-kuru")
And so on.
As another option to consider, have you thought about using sub-modules? They're just other Python files (.py) where you place commonly-used functions.
def showText(passedVariable):
print passedVariable
return
This new file is the imported, just as math or random would be and the function used as such:
import mySubModule
mySubModule.showText("Hello!")
But that's just how I work. Absolutely follow what you can understand, not just now but next week or year also.
Do what you can follow. If you can make sense of the code you posted, then that's what you should use. If a different structure feels more natural, use that instead.

Categories