Hallo miteinander,
ich wollte mal schlau sein und habe versucht, eine kleine Hilfsklasse zu schreiben, um das Programmieren mit Tkinter angenehmer zu machen und anschließend im Akkord Grafikspielchen zu fabrizieren. Das hab ich mir so gedacht, dass Objekte dieser Klasse ein Queue als Attribut halten, in das man (ggf. aus einem anderen Thread heraus) Zeichenanweisungen im Gewand eines Dictionaries reinschmeißen kann, und eine parallel laufende Methode kümmert sich ums Zeichnen. Bis jetzt sind die Fähigkeiten sehr rudimentär, aber es funktioniert schon einigermaßen.
canvas_screen.py
Code: Alles auswählen
#!/usr/bin/env python3
import tkinter as tk
from time import sleep
from queue import Queue, Empty
class Screen:
def __init__(self, width, height):
self.window = tk.Tk()
self.canvas = tk.Canvas(self.window, width=width, height=height)
self.name2id = dict()
self.input = Queue()
self.canvas.pack()
def mainloop(self):
self.process_loop()
self.window.mainloop()
def process_loop(self):
while True:
try:
self.process_element(self.input.get_nowait())
except Empty:
break
self.window.after(1, self.process_loop)
def process_element(self, e):
if e['name'] in self.name2id:
# Update
if e['type'] == 'circle':
self.canvas.coords(self.name2id[e['name']], e['M'][0]-e['r'], e['M'][1]-e['r'], e['M'][0]+e['r'], e['M'][1]+e['r'])
elif e['type'] == 'text':
self.canvas.coords(self.name2id[e['name']], e['M'])
self.configure(self.name2id[e['name']], text=e['text'], font=e['font'])
elif e['type'] == 'line':
self.canvas.coords(self.name2id[e['name']], *e['bbox'])
# Delete
if e['type'] == 'delete':
self.canvas.delete(self.name2id[e['name']])
else:
# Create
if e['type'] == 'circle':
self.name2id[e['name']] = self.canvas.create_oval(e['M'][0]-e['r'], e['M'][1]-e['r'], e['M'][0]+e['r'], e['M'][1]+e['r'])
elif e['type'] == 'text':
self.name2id[e['name']] = self.canvas.create_text(e['M'][0], e['M'][1], text=e['text'], font=e['font'])
elif e['type'] == 'line':
self.name2id[e['name']] = self.canvas.create_line(*e['bbox'])
if __name__ == '__main__':
s=Screen(500, 500)
s.process_element({'name':'horst', 'type':'circle', 'M':(100,400), 'r':40})
s.process_element({'name':'hallo', 'type':'text', 'M':(200,200), 'text':'hallo', 'font':('Courier New', 40)})
s.process_element({'name':'lydia', 'type':'line', 'bbox':(100,100,500,300)})
s.window.update()
sleep(1)
s.input.put({'name':'horst', 'type':'circle', 'M':(400,400), 'r':40})
s.input.put({'name':'hallo', 'type':'delete'})
print(s.name2id)
s.mainloop()
Code: Alles auswählen
#!/usr/bin/env python3
from canvas_screen import Screen
from random import randint, choice
from time import time
from threading import Thread, Lock
SPIELFELD_GROESSE = 500
BALKEN_AUF_EINMAL = 5
BALKEN_BREITE = 100
BALL_RADIUS = 20
SCROLL_GESCHWINDIGKEIT = 2 # Pixel pro Schritt
SEITWAERTS_GESCHWINDIGKEIT = 10
FPS = 50
class Rapid_Roll:
def __init__(self):
# linke Ecke der Balken als Koordinatentupel
self.balken = [(randint(0, SPIELFELD_GROESSE-BALKEN_BREITE),
SPIELFELD_GROESSE/BALKEN_AUF_EINMAL*i)
for i in range(1,BALKEN_AUF_EINMAL+1)]
# Mittelpunkt vom Ball
self.ball = choice(self.balken)
self.ball = [self.ball[0]+BALKEN_BREITE/2, self.ball[1]-BALL_RADIUS]
self.gedrueckt = 0 # -1 für links, +1 für rechts
self.gedrueckt_lock = Lock()
self.spiel_laeuft = True
self.screen = Screen(SPIELFELD_GROESSE, SPIELFELD_GROESSE)
self.screen.window.bind('<1>', self.on_L)
self.screen.window.bind('<3>', self.on_R)
self.screen.window.bind('<ButtonRelease-1>', self.on_nichts)
self.screen.window.bind('<ButtonRelease-3>', self.on_nichts)
def on_L(self, e):
self.gedrueckt_lock.acquire()
self.gedrueckt = -1
self.gedrueckt_lock.release()
def on_R(self, e):
self.gedrueckt_lock.acquire()
self.gedrueckt = +1
self.gedrueckt_lock.release()
def on_nichts(self, e):
self.gedrueckt_lock.acquire()
self.gedrueckt = 0
self.gedrueckt_lock.release()
def schritt(self):
# Ball seitwärts
self.gedrueckt_lock.acquire()
self.ball[0] += self.gedrueckt*SEITWAERTS_GESCHWINDIGKEIT
if 0 < self.ball[0] < SPIELFELD_GROESSE:
self.ball[0] -= self.gedrueckt*SEITWAERTS_GESCHWINDIGKEIT
self.gedrueckt_lock.release()
# Herausfinden, ob der Ball auf einem Balken liegt
liegt_auf = any(b[0] <= self.ball[0] <= b[0]+BALKEN_BREITE and b[1]-BALL_RADIUS <= self.ball[1] <= b[1] for b in self.balken)
print("x")
# Ball hochziehen oder fallen lassen
if liegt_auf:
self.ball[1] -= SCROLL_GESCHWINDIGKEIT
else:
self.ball[1] += 2*SCROLL_GESCHWINDIGKEIT
# Balken scrollen
self.balken = [(x, y-SCROLL_GESCHWINDIGKEIT) for (x,y) in self.balken if y>SCROLL_GESCHWINDIGKEIT]
while len(self.balken) <= BALKEN_AUF_EINMAL: # wenn ein Balken oben rausgerutscht ist
self.balken.append((randint(0, SPIELFELD_GROESSE-BALKEN_BREITE),
self.balken[-1][1]+SPIELFELD_GROESSE/BALKEN_AUF_EINMAL))
# prüfen ob der Ball noch im Feld ist
"""if not 0 < self.ball[1]< SPIELFELD_GROESSE:
self.spiel_laeuft = False"""
def ausgabe(self):
for i, b in enumerate(self.balken):
self.screen.input.put({'type':'line', 'name':i, 'bbox':(b[0], b[1], b[0]+BALKEN_BREITE, b[1])}),
self.screen.input.put({'type':'circle', 'name':'ball', 'M':self.ball, 'r':BALL_RADIUS})
def hauptschleife(self):
zeit = time()
while self.spiel_laeuft:
while time()-zeit < 1/FPS:
pass
zeit += 1/FPS
self.schritt()
self.ausgabe()
rr = Rapid_Roll()
Thread(target=rr.hauptschleife).start()
rr.screen.mainloop()
Aber jetzt kommt der Hammer: Wenn ich das print in Zeile 57 auskommentiere, funktioniert gar nichts mehr. Man sieht dann nur ein leeres, hängendes Konsolenfenster und es öffnet sich KEIN Tkinter-Fenster. Habe keine Ahnung, wieso Wahrscheinlich hab ich irgendwas mit den Threads verkackt, aber ich sehe nicht, was genau. Wer kann helfen?
Danke im Voraus und freundliche Grüße!