9

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?

4 Answers 4

11

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Sockets might be too low-level solution. ZeroMq or other similar library may be enough to push messages back and forth.
You can open multiple windows and control them from a single event loop. The drawing method you use is different and you have to track WINDOWENTER and WINDOWLEAVE events so you know where to send the input events.
4

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."

Comments

1

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.

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.