Pygame with Multiple Windows

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?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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.

Method 2

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.

Method 3

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.

Method 4

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()


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x