3

Help run a simple program in Python using Brython.

The basis is taken (it did not work) file from the example http://www.brython.info/gallery/pygame/chimp.html

There are 3 files in the same directory: Eventlist.html,py_VFS.js, brython.js.

py_VFS.js:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL py_VFS.js was not found on this server.</p>
</body></html>

brython.js:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL brython.js was not found on this server.</p>
</body></html>

Eventlist.html fixed path in the lines:

<script type="text/javascript" src="external/brython/brython.js"></script>
<script type="text/javascript" src="external/brython/py_VFS.js"></script>

and a module of Python (the standard unit of pygame \ examples \ eventlist.py) was replaced. As a result, the whole code of Eventlist.html:

<html>

<head>

<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="py_VFS.js"></script>

<script type="text/python">
#!/usr/bin/env python

"""Eventlist is a sloppy style of pygame, but is a handy
tool for learning about pygame events and input. At the
top of the screen are the state of several device values,
and a scrolling list of events are displayed on the bottom.

This is not quality 'ui' code at all, but you can see how
to implement very non-interactive status displays, or even
a crude text output control.
"""

from pygame import *

ImgOnOff = []
Font = None
LastKey = None

def showtext(win, pos, text, color, bgcolor):
    textimg = Font.render(text, 1, color, bgcolor)
    win.blit(textimg, pos)
    return pos[0] + textimg.get_width() + 5, pos[1]


def drawstatus(win):
    bgcolor = 50, 50, 50
    win.fill(bgcolor, (0, 0, 640, 120))
    win.blit(Font.render('Status Area', 1, (155, 155, 155), bgcolor), (2, 2))

    pos = showtext(win, (10, 30), 'Mouse Focus', (255, 255, 255), bgcolor)
    win.blit(ImgOnOff[mouse.get_focused()], pos)

    pos = showtext(win, (330, 30), 'Keyboard Focus', (255, 255, 255), bgcolor)
    win.blit(ImgOnOff[key.get_focused()], pos)

    pos = showtext(win, (10, 60), 'Mouse Position', (255, 255, 255), bgcolor)
    p = '%s, %s' % mouse.get_pos()
    pos = showtext(win, pos, p, bgcolor, (255, 255, 55))

    pos = showtext(win, (330, 60), 'Last Keypress', (255, 255, 255), bgcolor)
    if LastKey:
        p = '%d, %s' % (LastKey, key.name(LastKey))
    else:
        p = 'None'
    pos = showtext(win, pos, p, bgcolor, (255, 255, 55))

    pos = showtext(win, (10, 90), 'Input Grabbed', (255, 255, 255), bgcolor)
    win.blit(ImgOnOff[event.get_grab()], pos)


def drawhistory(win, history):
    win.blit(Font.render('Event History Area', 1, (155, 155, 155), (0,0,0)), (2, 132))
    ypos = 450
    h = list(history)
    h.reverse()
    for line in h:
        r = win.blit(line, (10, ypos))
        win.fill(0, (r.right, r.top, 620, r.height))
        ypos -= Font.get_height()


def main():
    init()

    win = display.set_mode((640, 480), RESIZABLE)
    display.set_caption("Mouse Focus Workout")

    global Font
    Font = font.Font(None, 26)

    global ImgOnOff
    ImgOnOff.append(Font.render("Off", 1, (0, 0, 0), (255, 50, 50)))
    ImgOnOff.append(Font.render("On", 1, (0, 0, 0), (50, 255, 50)))

    history = []

    #let's turn on the joysticks just so we can play with em
    for x in range(joystick.get_count()):
        j = joystick.Joystick(x)
        j.init()
        txt = 'Enabled joystick: ' + j.get_name()
        img = Font.render(txt, 1, (50, 200, 50), (0, 0, 0))
        history.append(img)
    if not joystick.get_count():
        img = Font.render('No Joysticks to Initialize', 1, (50, 200, 50), (0, 0, 0))
        history.append(img)

    going = True
    while going:
        for e in event.get():
            if e.type == QUIT:
                going = False
            if e.type == KEYDOWN:
                if e.key == K_ESCAPE:
                    going = False
                else:
                    global LastKey
                    LastKey = e.key
            if e.type == MOUSEBUTTONDOWN:
                event.set_grab(1)
            elif e.type == MOUSEBUTTONUP:
                event.set_grab(0)
            if e.type == VIDEORESIZE:
                win = display.set_mode(e.size, RESIZABLE)

            if e.type != MOUSEMOTION:
                txt = '%s: %s' % (event.event_name(e.type), e.dict)
                img = Font.render(txt, 1, (50, 200, 50), (0, 0, 0))
                history.append(img)
                history = history[-13:]


        drawstatus(win)
        drawhistory(win, history)

        display.flip()
        time.wait(10)

    quit()


if __name__ == '__main__':
    main()

</script>
</head>
<body onload="brython({debug:1})">
  <div id="pydiv"></div>
</body>

</html>

When you run Eventlist.html a blank page shows in a browser. The code from the file eventlist.py works and the result is the following: enter image description here

As a result, I want to get a similar window in the browser.

kchomski
  • 2,872
  • 20
  • 31
EmptyMan
  • 297
  • 1
  • 4
  • 25

1 Answers1

2

I spent some time these past few days trying to get Brython-PyGame working in a browser and concluded that:

  1. Brython-PyGame was never fully implemented.
  2. It is all but impossible to implement a fully working version of PyGame in Brython, due to fundamental differences between PyGame and browsers in how user interaction is modeled.

Here are the details:

Incomplete implementation

Brython-PyGame has many unimplemented functions. Some of these could be considered non-critical (for example, the module transform.py is entirely missing). But others go to the heart of any PyGame program. For example, all nontrivial functions in the event.py module, such as event.get() and event.wait(), rely on calls to unimplemented functions in SDL.py, such as SDL_WaitEventAndReturn() and SDL_PeepEvents(). No nontrivial PyGame program would be able to work without one of these functions as the basis for its event loop.

Fundamental design differences

The fact that event.wait() was never fully implemented is not coincidental. This function goes to the heart of the conceptual design difference between PyGame and client-side web programming:

  • PyGame assumes control of the application running your game, meaning that it can and often does block the main thread until an event of interest occurs.
  • By contrast, JavaScript programming is not allowed to block the main JS thread, as this thread is used for UI rendering of the page. If you attempt to block this thread, the browser will display a message saying the page has become unresponsive, and suggest that the user close the tab. Instead, JS relies heavily on promises and callbacks to be run when an event occurs.

Thus, a call to event.wait(), which blocks until an event such as a mouse move, is impossible to implement in the client side of a browser.

There are also other cases where PyGame uses blocking calls in ways that are not allowed in JavaScript. For example, consider the following innocent-looking PyGame snippet:

import pygame as pg
img = pg.image.load('flag.png')
screen.blit(img, (0,0))

This loads an image file and renders it on screen. Behind the scenes, the pg.image.load command blocks while the file is read into memory. In Brython-PyGame, the implementation attempts to read the file from a given URL. However, reading a URL is a nonblocking operation in JavaScript, as the required network operation might take a while and we do not want the UI thread to block until it is complete. In JavaScript, one would implement a similar operation as something like

var img = new Image();
ctx = getContextForSomeCanvas();
img.onload = function() {
  ctx.drawImage(img, 0, 0);
}
img.src = 'flag.png';

where the onload callback ensures that ctx.drawImage will be executed only after the image has been loaded. Converting the former Python code into something resembling the above JavaScript is nontrivial: It would require some behind-the-scenes handling of the asynchronous load operation, which would delay the call to blit() until the image was loaded. Even if this could work, it would be very unintuitive to a naive PyGame programmer, and could easily lead to bugs later in the code (for example, if the code assumes that mouse clicks are on the image even though it hasn't been loaded yet). In any case, the Brython-PyGame implementation does not implement this behind-the-scenes processing, leading the above code to fail because the image will be blitted before it was loaded.

Zvika
  • 1,236
  • 12
  • 16