import os
import pygame
from pygame.locals import *
from PIL import Image, ImageTk
import numpy as np
import tkinter
WIDTH = 640
HEIGHT = 480
SCRECT = Rect(0, 0, WIDTH, HEIGHT)
FPS = 300
IFPS = 1000 // FPS
IMG = './wx_64x64.png'
XBM = '''
#define image_width 16
#define image_height 16
static char image_bits[] = {
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff};
'''
class Player(pygame.sprite.Sprite):
def __init__(self, img, pos, vxy, angle):
super().__init__()
self.im = pygame.image.load(img).convert_alpha()
if angle != 0: self.im = pygame.transform.rotate(self.im, angle)
self.rct = self.im.get_rect(topleft=pos)
self.vx, self.vy = vxy
self.angle = angle
def update(self):
self.rct.move_ip(self.vx, self.vy)
if self.rct.left < 0 or self.rct.right > SCRECT.width: self.vx = -self.vx
if self.rct.top < 0 or self.rct.bottom > SCRECT.height: self.vy = -self.vy
self.rct = self.rct.clamp(SCRECT)
def draw(self, screen):
screen.blit(self.im, self.rct)
class Game(object):
def __init__(self):
os.environ['SDL_VIDEO_CENTERED'] = ''
os.environ['SDL_VIDEO_WINDOW_POS'] = f'{WIDTH+10},{30}'
pygame.init()
self.screen = pygame.display.set_mode(SCRECT.size, flags=pygame.DOUBLEBUF)
pygame.display.set_caption('title')
self.players = [
Player(IMG, (0, 0), (2, 1), 0),
Player(IMG, (0, 0), (1, 2), 90),
Player(IMG, (0, 0), (2, 1), -45),
Player(IMG, (0, 0), (1, 2), 45)]
self.running = True
def loop_tick(self, w, h):
self.screen.fill((0, 0, 0))
for p in self.players:
p.update()
p.draw(self.screen)
s = pygame.display.get_surface()
b = s.get_buffer()
a = np.ndarray((HEIGHT, WIDTH, 4), np.uint8, b.raw) # ARGB
im = Image.fromarray(a[:,:,2::-1]) # as BGR (skip alpha)
pygame.display.flip()
for ev in pygame.event.get():
if ev.type == QUIT:
self.running = False
break
if ev.type == KEYDOWN:
if ev.key == K_ESCAPE:
self.running = False
break
return ImageTk.PhotoImage(im.resize((h, h)))
def __del__(self):
pygame.quit()
class Rt(object):
def __init__(self, g):
self.g = g
self.rt = tkinter.Tk()
self.rt.title('title')
self.rt.geometry(f'{WIDTH}x{HEIGHT}+0+0')
self.canvas = tkinter.Canvas(self.rt, width=WIDTH, height=HEIGHT)
self.canvas.place(x=0, y=0)
w, h = [v // 2 for v in SCRECT.size]
self.canvas.create_rectangle(0, 0, w, h, fill='green')
self.canvas.xbm = tkinter.BitmapImage(data=XBM,
foreground='red', background='lime')
self.canvas.create_image(w, h, image=self.canvas.xbm, anchor='sw')
self.canvas.ph = tkinter.PhotoImage(file=IMG)
self.canvas.create_image(w, h, image=self.canvas.ph, anchor='ne')
self.canvas.im = ImageTk.PhotoImage(Image.open(IMG).resize((h, h)))
self.canvas.create_image(w, h, image=self.canvas.im, anchor='nw')
def after_callback(self):
w, h = [v // 2 for v in SCRECT.size]
self.canvas.im = self.g.loop_tick(w, h)
self.canvas.create_image(w, h, image=self.canvas.im, anchor='nw')
if self.g.running: self.rt.after(IFPS, self.after_callback)
else: self.rt.quit()
def mainloop(self):
self.rt.after(IFPS, self.after_callback)
self.rt.mainloop()
self.g.running = False
del self.g
if __name__ == '__main__':
rt = Rt(Game())
rt.mainloop()
aW1wb3J0IG9zCmltcG9ydCBweWdhbWUKZnJvbSBweWdhbWUubG9jYWxzIGltcG9ydCAqCmZyb20gUElMIGltcG9ydCBJbWFnZSwgSW1hZ2VUawppbXBvcnQgbnVtcHkgYXMgbnAKaW1wb3J0IHRraW50ZXIKCldJRFRIID0gNjQwCkhFSUdIVCA9IDQ4MApTQ1JFQ1QgPSBSZWN0KDAsIDAsIFdJRFRILCBIRUlHSFQpCkZQUyA9IDMwMApJRlBTID0gMTAwMCAvLyBGUFMKSU1HID0gJy4vd3hfNjR4NjQucG5nJwpYQk0gPSAnJycKI2RlZmluZSBpbWFnZV93aWR0aCAxNgojZGVmaW5lIGltYWdlX2hlaWdodCAxNgpzdGF0aWMgY2hhciBpbWFnZV9iaXRzW10gPSB7CjB4ZmYsIDB4ZmYsIDB4ZmYsIDB4ZmYsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsCjB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4ZmYsIDB4ZmYsIDB4ZmYsIDB4ZmYsCjB4ZmYsIDB4ZmYsIDB4ZmYsIDB4ZmYsIDB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsCjB4MDAsIDB4MDAsIDB4MDAsIDB4MDAsIDB4ZmYsIDB4ZmYsIDB4ZmYsIDB4ZmZ9OwonJycKCmNsYXNzIFBsYXllcihweWdhbWUuc3ByaXRlLlNwcml0ZSk6CiAgZGVmIF9faW5pdF9fKHNlbGYsIGltZywgcG9zLCB2eHksIGFuZ2xlKToKICAgIHN1cGVyKCkuX19pbml0X18oKQogICAgc2VsZi5pbSA9IHB5Z2FtZS5pbWFnZS5sb2FkKGltZykuY29udmVydF9hbHBoYSgpCiAgICBpZiBhbmdsZSAhPSAwOiBzZWxmLmltID0gcHlnYW1lLnRyYW5zZm9ybS5yb3RhdGUoc2VsZi5pbSwgYW5nbGUpCiAgICBzZWxmLnJjdCA9IHNlbGYuaW0uZ2V0X3JlY3QodG9wbGVmdD1wb3MpCiAgICBzZWxmLnZ4LCBzZWxmLnZ5ID0gdnh5CiAgICBzZWxmLmFuZ2xlID0gYW5nbGUKCiAgZGVmIHVwZGF0ZShzZWxmKToKICAgIHNlbGYucmN0Lm1vdmVfaXAoc2VsZi52eCwgc2VsZi52eSkKICAgIGlmIHNlbGYucmN0LmxlZnQgPCAwIG9yIHNlbGYucmN0LnJpZ2h0ID4gU0NSRUNULndpZHRoOiBzZWxmLnZ4ID0gLXNlbGYudngKICAgIGlmIHNlbGYucmN0LnRvcCA8IDAgb3Igc2VsZi5yY3QuYm90dG9tID4gU0NSRUNULmhlaWdodDogc2VsZi52eSA9IC1zZWxmLnZ5CiAgICBzZWxmLnJjdCA9IHNlbGYucmN0LmNsYW1wKFNDUkVDVCkKCiAgZGVmIGRyYXcoc2VsZiwgc2NyZWVuKToKICAgIHNjcmVlbi5ibGl0KHNlbGYuaW0sIHNlbGYucmN0KQoKY2xhc3MgR2FtZShvYmplY3QpOgogIGRlZiBfX2luaXRfXyhzZWxmKToKICAgIG9zLmVudmlyb25bJ1NETF9WSURFT19DRU5URVJFRCddID0gJycKICAgIG9zLmVudmlyb25bJ1NETF9WSURFT19XSU5ET1dfUE9TJ10gPSBmJ3tXSURUSCsxMH0sezMwfScKICAgIHB5Z2FtZS5pbml0KCkKICAgIHNlbGYuc2NyZWVuID0gcHlnYW1lLmRpc3BsYXkuc2V0X21vZGUoU0NSRUNULnNpemUsIGZsYWdzPXB5Z2FtZS5ET1VCTEVCVUYpCiAgICBweWdhbWUuZGlzcGxheS5zZXRfY2FwdGlvbigndGl0bGUnKQogICAgc2VsZi5wbGF5ZXJzID0gWwogICAgICBQbGF5ZXIoSU1HLCAoMCwgMCksICgyLCAxKSwgMCksCiAgICAgIFBsYXllcihJTUcsICgwLCAwKSwgKDEsIDIpLCA5MCksCiAgICAgIFBsYXllcihJTUcsICgwLCAwKSwgKDIsIDEpLCAtNDUpLAogICAgICBQbGF5ZXIoSU1HLCAoMCwgMCksICgxLCAyKSwgNDUpXQogICAgc2VsZi5ydW5uaW5nID0gVHJ1ZQoKICBkZWYgbG9vcF90aWNrKHNlbGYsIHcsIGgpOgogICAgc2VsZi5zY3JlZW4uZmlsbCgoMCwgMCwgMCkpCiAgICBmb3IgcCBpbiBzZWxmLnBsYXllcnM6CiAgICAgIHAudXBkYXRlKCkKICAgICAgcC5kcmF3KHNlbGYuc2NyZWVuKQogICAgcyA9IHB5Z2FtZS5kaXNwbGF5LmdldF9zdXJmYWNlKCkKICAgIGIgPSBzLmdldF9idWZmZXIoKQogICAgYSA9IG5wLm5kYXJyYXkoKEhFSUdIVCwgV0lEVEgsIDQpLCBucC51aW50OCwgYi5yYXcpICMgQVJHQgogICAgaW0gPSBJbWFnZS5mcm9tYXJyYXkoYVs6LDosMjo6LTFdKSAjIGFzIEJHUiAoc2tpcCBhbHBoYSkKICAgIHB5Z2FtZS5kaXNwbGF5LmZsaXAoKQoKICAgIGZvciBldiBpbiBweWdhbWUuZXZlbnQuZ2V0KCk6CiAgICAgIGlmIGV2LnR5cGUgPT0gUVVJVDoKICAgICAgICBzZWxmLnJ1bm5pbmcgPSBGYWxzZQogICAgICAgIGJyZWFrCiAgICAgIGlmIGV2LnR5cGUgPT0gS0VZRE9XTjoKICAgICAgICBpZiBldi5rZXkgPT0gS19FU0NBUEU6CiAgICAgICAgICBzZWxmLnJ1bm5pbmcgPSBGYWxzZQogICAgICAgICAgYnJlYWsKCiAgICByZXR1cm4gSW1hZ2VUay5QaG90b0ltYWdlKGltLnJlc2l6ZSgoaCwgaCkpKQoKICBkZWYgX19kZWxfXyhzZWxmKToKICAgIHB5Z2FtZS5xdWl0KCkKCmNsYXNzIFJ0KG9iamVjdCk6CiAgZGVmIF9faW5pdF9fKHNlbGYsIGcpOgogICAgc2VsZi5nID0gZwogICAgc2VsZi5ydCA9IHRraW50ZXIuVGsoKQogICAgc2VsZi5ydC50aXRsZSgndGl0bGUnKQogICAgc2VsZi5ydC5nZW9tZXRyeShmJ3tXSURUSH14e0hFSUdIVH0rMCswJykKICAgIHNlbGYuY2FudmFzID0gdGtpbnRlci5DYW52YXMoc2VsZi5ydCwgd2lkdGg9V0lEVEgsIGhlaWdodD1IRUlHSFQpCiAgICBzZWxmLmNhbnZhcy5wbGFjZSh4PTAsIHk9MCkKICAgIHcsIGggPSBbdiAvLyAyIGZvciB2IGluIFNDUkVDVC5zaXplXQogICAgc2VsZi5jYW52YXMuY3JlYXRlX3JlY3RhbmdsZSgwLCAwLCB3LCBoLCBmaWxsPSdncmVlbicpCiAgICBzZWxmLmNhbnZhcy54Ym0gPSB0a2ludGVyLkJpdG1hcEltYWdlKGRhdGE9WEJNLAogICAgICBmb3JlZ3JvdW5kPSdyZWQnLCBiYWNrZ3JvdW5kPSdsaW1lJykKICAgIHNlbGYuY2FudmFzLmNyZWF0ZV9pbWFnZSh3LCBoLCBpbWFnZT1zZWxmLmNhbnZhcy54Ym0sIGFuY2hvcj0nc3cnKQogICAgc2VsZi5jYW52YXMucGggPSB0a2ludGVyLlBob3RvSW1hZ2UoZmlsZT1JTUcpCiAgICBzZWxmLmNhbnZhcy5jcmVhdGVfaW1hZ2UodywgaCwgaW1hZ2U9c2VsZi5jYW52YXMucGgsIGFuY2hvcj0nbmUnKQogICAgc2VsZi5jYW52YXMuaW0gPSBJbWFnZVRrLlBob3RvSW1hZ2UoSW1hZ2Uub3BlbihJTUcpLnJlc2l6ZSgoaCwgaCkpKQogICAgc2VsZi5jYW52YXMuY3JlYXRlX2ltYWdlKHcsIGgsIGltYWdlPXNlbGYuY2FudmFzLmltLCBhbmNob3I9J253JykKCiAgZGVmIGFmdGVyX2NhbGxiYWNrKHNlbGYpOgogICAgdywgaCA9IFt2IC8vIDIgZm9yIHYgaW4gU0NSRUNULnNpemVdCiAgICBzZWxmLmNhbnZhcy5pbSA9IHNlbGYuZy5sb29wX3RpY2sodywgaCkKICAgIHNlbGYuY2FudmFzLmNyZWF0ZV9pbWFnZSh3LCBoLCBpbWFnZT1zZWxmLmNhbnZhcy5pbSwgYW5jaG9yPSdudycpCiAgICBpZiBzZWxmLmcucnVubmluZzogc2VsZi5ydC5hZnRlcihJRlBTLCBzZWxmLmFmdGVyX2NhbGxiYWNrKQogICAgZWxzZTogc2VsZi5ydC5xdWl0KCkKCiAgZGVmIG1haW5sb29wKHNlbGYpOgogICAgc2VsZi5ydC5hZnRlcihJRlBTLCBzZWxmLmFmdGVyX2NhbGxiYWNrKQogICAgc2VsZi5ydC5tYWlubG9vcCgpCiAgICBzZWxmLmcucnVubmluZyA9IEZhbHNlCiAgICBkZWwgc2VsZi5nCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgogIHJ0ID0gUnQoR2FtZSgpKQogIHJ0Lm1haW5sb29wKCk=