Canvas-Bug (?)

Fragen zu Tkinter.
Antworten
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Moin,

Ich hatte angefangen ein kleiner Fußballspiel zu programmieren, wo man mit Pfeiltasten bzw. WASD seine Spieler bewegen kann. mit einem Spieler funktioniert das auch soweit, aber wennn ich ihn nach links, linksoben oder linksunten bewege zieht er (warum auch immer) graue Linien hinter sich.
Hier der Code:

Code: Alles auswählen

import Tkinter as tk
from player import *

pressLeft,pressRight,pressUp,pressDown = False,False,False,False
posX,posY = 100,100
running = False
ball = False

main = tk.Tk()


# Bilder
img_pitchBG = tk.PhotoImage(file='img/pitch.gif')

# GUI
main.state('zoomed')

pitch = tk.Canvas(main, width=1000, height=600)
pitch.create_image(500, 300, image=img_pitchBG)
pitch.place(x=100, y=50, width=1000, height=600)




player1 = Player(pitch, '#ff0000', True, 100, 100)

def up(e):
    global pressUp
    pressUp = True
def down(e):
    global pressDown
    pressDown = True
def left(e):
    global pressLeft
    pressLeft = True
def right(e):
    global pressRight
    pressRight = True
    
def r_up(e):
    global pressUp
    pressUp = False
def r_down(e):
    global pressDown
    pressDown = False
def r_left(e):
    global pressLeft
    pressLeft = False
def r_right(e):
    global pressRight
    pressRight = False


def update():
    global posX,posY
    if pressLeft:
        posX -= 1
    if pressRight:
        posX += 1
    if pressUp:
        posY -= 1
    if pressDown:
        posY += 1
    p.setCoords(posX, posY)
    main.after(1, update)

def start():
    global running,ball
    ball = not(ball)
    player1.setBall(ball)
    
    if running:
        return
    running = True
    update()


main.bind('<KeyPress-w>', up)
main.bind('<KeyPress-s>', down)
main.bind('<KeyPress-a>', left)
main.bind('<KeyPress-d>', right)

main.bind('<KeyRelease-w>', r_up)
main.bind('<KeyRelease-s>', r_down)
main.bind('<KeyRelease-a>', r_left)
main.bind('<KeyRelease-d>', r_right)
button_start = tk.Button(main, text='Starten', command=start)
button_start.pack()

main.mainloop()
und die Datei player.py:

Code: Alles auswählen



class Player:
    def __init__(self, pitch, color, team, x, y):
        self.pitch = pitch
        r = 8
        self.id_circle = self.pitch.create_oval(x-r, y-r, x+r, y+r, fill=color, outline='#000000', width=1)
        self.x = x
        self.y = y
    def setCoords(self, x, y):
        self.pitch.coords(self.id_circle, x-r, y-r, x+r, y+r)
        self.x = x
        self.y = y
    def setBall(self, ball):
        if ball:
            self.pitch.itemconfig(self.id_circle, outline='#eeeeee', width=4)
            r = 12
            self.pitch.coords(self.id_circle, self.x-r, self.y-r, self.x+r, self.y+r)
        else:
            self.pitch.itemconfig(self.id_circle, outline='#000000', width=1)
            r = 8
            self.pitch.coords(self.id_circle, self.x-r, self.y-r, self.x+r, self.y+r)

Der Spieler wird auf dem Feld als roter Kreis mit schwarzem Rahmen angezeigt.
Die Methode setBall ist dazu da das Aussehen des Players zu ändern, wenn er den Ball bekommt [setBall(True)]. dann sollte er einen grauen rand bekommen und größer werden. Bei setBall(False) wird er wieder kleiner und bekommt den schewarzen Rahmen zurück. Das Klappt auch soweit.
Wenn er aber gerade den grauen Rahmen hat und ich ihn nach links bewege zieht er diesen grauen Strich hinter sich.
Woher kommt das und wie kann ich das wieder beheben?

Danke, Grüße raiminator
Zuletzt geändert von raiminator am Sonntag 17. Juni 2012, 15:01, insgesamt 2-mal geändert.
BlackJack

@raiminator: Was auch immer das Problem ist, bei den Sternchen-Importen, den vielen ``global``\s, und Namen wie `p` oder `i` hast Du Grössere. ;-)
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

@BlackJack: ich hoffe mal mit dem Code oben hast du jetzt keine Probleme mehr ;)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

raiminator hat geschrieben:@BlackJack: ich hoffe mal mit dem Code oben hast du jetzt keine Probleme mehr ;)
Äh... was hat sich denn da essenziell geändert? Ich sehe immer noch einen *-Import und lauter `globals`... :P

Zudem wäre es ja trivial aus dem Player-Modul das einzig vorhandene Objekt explizit zu importieren ;-)

Des Weiteren solltest Du Dich mehr an PEP8 halten; Funktionen werden klein und mit Unterstrichen benannt, nicht "mixedCase". Außerdem würde ich statt Gettern und Settern eher mit Properties arbeiten; das ist pythonischer :-)

Zudem hast Du viel zu viel Copy&Paste-Code in Deinem Programm. Ein einfaches Beispiel ist dieses hier:

Code: Alles auswählen

main.bind('<KeyPress-w>', up)
main.bind('<KeyPress-s>', down)
main.bind('<KeyPress-a>', left)
main.bind('<KeyPress-d>', right)

main.bind('<KeyRelease-w>', r_up)
main.bind('<KeyRelease-s>', r_down)
main.bind('<KeyRelease-a>', r_left)
main.bind('<KeyRelease-d>', r_right)
Wieso drückst Du das nicht mittels einer geeigneten Datenstruktur und einer Schleife aus?

Code: Alles auswählen

bindings = (
    ('<KeyPress-w>', up),
    ('<KeyPress-s>', down),
    # usw.
)
for name, func in bindings:
    main.bind(name, func)
Das gezeigt ist aber an sich eher unnötig komplex. Wenn Du doch bei einem Tastendruck eine Funktion aufrufen kannst, wieso muss es denn immer eine separate sein, die dann dazu noch den Wert einer globalen Variablen ändert? Viele einfacher und imho offensichtlicher wäre es doch, immer die gleiche Funktion aufzurufen und dieser per Parameter mitzuteilen, *welche* Taste gedrückt wurde!
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Toll. ich verstehe ja dass diese Community Probleme mit Leuten hat, die noch an andere Programmiersprachen gewöhnt sind, aber meine eigentliche Frage war ja, ob jemand weiß woher dieser Bug (?) beim Canvas kommt.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.
raiminator hat geschrieben:Toll. ich verstehe ja dass diese Community Probleme mit Leuten hat, die noch an andere Programmiersprachen gewöhnt sind, aber meine eigentliche Frage war ja, ob jemand weiß woher dieser Bug (?) beim Canvas kommt.
Damit hat hier sicher keiner Probleme, da die meisten hier mehrere andere Programmiersprachen beherrschen. Ganz unabhängig davon sie die Anmerkungen jedoch sprachunabhängig. Auch in anderen Sprachen müllt man sich nicht den Namensraum zu, verwendet globale Variablen und kopiert Code.

Sieh es doch aus einem anderen Blickwinkel: du möchtest Hilfe zu einen Problem und solltest daher die Frage für die Helfenden so einfach wie möglich stellen. Nur wenige haben so viel Zeit und Lust sich durch schlechten Code zu arbeiten. Neben vielen Verbesserungen an deinem Programm erhöht guter Code also auch noch die Chancen auf eine Antwort.

Sebastian
Das Leben ist wie ein Tennisball.
deets

Dazu gesellt sich auch noch, dass der gezeigte Code nicht laeuft - mal heisst es player1, mal p, und das benutzte Bild sollte man vielleicht auch zur Verfuegung stellen - schliesslich macht es einen Unterschied, ob man da was zuerst in den Hintergrund malt oder nicht.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi raiminator

Wie schon deets erwähnte enthält dein erstes Skript einige Bremsen. Habe es soweit angepasst damit es für mich startfähig ist.

Main:

Code: Alles auswählen

import Tkinter as tk
from player import *

pressLeft,pressRight,pressUp,pressDown = False,False,False,False
posX,posY = 100,100
running = False
ball = False

main = tk.Tk()


# Bilder
img_pitchBG = tk.PhotoImage() #file='img/pitch.gif')

# GUI
#main.state('zoomed')

pitch = tk.Canvas(main, width=1000, height=600, bg='steelblue')
pitch.pack()

pitch.create_image(500, 300, image=img_pitchBG)



player1 = Player(pitch, '#ff0000', True, 100, 100)

def up(e):
    global pressUp
    pressUp = True
def down(e):
    global pressDown
    pressDown = True
def left(e):
    global pressLeft
    pressLeft = True
def right(e):
    global pressRight
    pressRight = True
   
def r_up(e):
    global pressUp
    pressUp = False
def r_down(e):
    global pressDown
    pressDown = False
def r_left(e):
    global pressLeft
    pressLeft = False
def r_right(e):
    global pressRight
    pressRight = False


def update():
    global posX,posY
    if pressLeft:
        posX -= 1
    if pressRight:
        posX += 1
    if pressUp:
        posY -= 1
    if pressDown:
        posY += 1
    player1.setCoords(posX, posY)
    main.after(1, update)

def start():
    global running,ball
    ball = not(ball)
    player1.setBall(ball)
   
    if running:
        return
    running = True
    update()


main.bind('<KeyPress-w>', up)
main.bind('<KeyPress-s>', down)
main.bind('<KeyPress-a>', left)
main.bind('<KeyPress-d>', right)

main.bind('<KeyRelease-w>', r_up)
main.bind('<KeyRelease-s>', r_down)
main.bind('<KeyRelease-a>', r_left)
main.bind('<KeyRelease-d>', r_right)
button_start = tk.Button(main, text='Starten', command=start)
button_start.pack()

main.mainloop()
Klasse Player:

Code: Alles auswählen

class Player:
    def __init__(self, pitch, color, team, x, y):
        self.pitch = pitch
        self.r = 8
        self.id_circle = self.pitch.create_oval(x-self.r, y-self.r, x+self.r,
            y+self.r, fill=color, outline='#000000', width=1)
        self.x = x
        self.y = y
    def setCoords(self, x, y):
        self.pitch.coords(self.id_circle, x-self.r, y-self.r, x+self.r,
            y+self.r)
        self.x = x
        self.y = y
    def setBall(self, ball):
        if ball:
            self.pitch.itemconfig(self.id_circle, outline='#eeeeee', width=4)
            r = 12
            self.pitch.coords(self.id_circle, self.x-r, self.y-r, self.x+r, self.y+r)
        else:
            self.pitch.itemconfig(self.id_circle, outline='#000000', width=1)
            r = 8
            self.pitch.coords(self.id_circle, self.x-r, self.y-r, self.x+r, self.y+r)
Habe noch eine Frage betreffs Bedienung über die Tastatur. So wie ich sehe bewegst du den Spieler mit den Tasten 'a', 'w', 's', 'd'. Wie gedenkst du einen der 11 bzw. insgesamt 22 Spieler über die Tastatur zu selektieren?

Gruß wuf :wink:
Take it easy Mates!
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Hatte ich mir so vorgestellt dass man es zu zweit spielt und jeder außer ASDW bzw. Pfeiltasten eine Taste zum Spieler wechseln hat, eine zum Schießen und zum Passen (vllt. q, e, Leertaste und linke Maustaste, rechte Maustaste, Enter)
Naja, das Programm bis jetzt war ja nur der Anfang.

Zu den Globals:
ich habe gerade mal versucht, das Programm so zu ändern dass es keine Globals mehr gibt. ball und running sind mehr oder weniger unnötig für das weitere Programm, die wären sowieso noch weggekommen.
Aber ohne pressUp, pressDown... und posX, posY komme ich einfach nicht aus.. muss man wirklich alle Globals vermeiden oder hat jemand einen Vorschlag wie man das umschreiben könnte?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

raiminator hat geschrieben: ich habe gerade mal versucht, das Programm so zu ändern dass es keine Globals mehr gibt. ball und running sind mehr oder weniger unnötig für das weitere Programm, die wären sowieso noch weggekommen.
Man entwirft Programme eigentlich so, dass man erst gar keine `globals` benötigt ;-)
raiminator hat geschrieben: Aber ohne pressUp, pressDown... und posX, posY komme ich einfach nicht aus.. muss man wirklich alle Globals vermeiden oder hat jemand einen Vorschlag wie man das umschreiben könnte?
Ja, indem Du diese einfach an die Funktion übergibst, bei der sie gebraucht werden; wenn sie einen Zustand besitzen, kann man diese in eine Klasse oder auch eine simple Datenstruktur wie ein Dict packen und dieses entsprechend in einer Funktion auslesen. Es gibt da viele Möglichkeiten; mir war es jetzt zu mühsam, mir Deinen speziellen Code anzugucken und zu überlegen, wie man das da am besten macht.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Hyperion hat geschrieben: Man entwirft Programme eigentlich so, dass man erst gar keine `globals` benötigt ;-)
'Man' vielleicht, wenn du damit erfahrenere Python-Programmier meinst :D
An die FUnktion übrgeben hatte ich ja versucht, aber wie soll ich eine Funktion schreiben, die sich selbst updatet, und gleichzeitig auf Knopfdruck reagiert bzw. Parameter übergeben bekommt? Das ist ja mein Problem..
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi raiminator

Danke für deine Antwort. Ich finde deine Idee interessant und mutig ein Fussball-Spiel in Python & Tkinter zu programmieren. Das Projekt könnte ein grösseres Ausmass annehmen. Sollte das Projekt auch nicht fertig werden kannst du dabei sicher viel Erfahrung sammeln. Learning by doing ist auch ein Weg sich Stoff anzueignen. Bin zwar ein völliger Laie was Fussball anbelangt aber es gibt hierzu ja viele Informationen auf dem Internet wo man sich schlau machen kann. Da mich die Spiel-Programmierung auch ein wenig interessiert habe ich einmal versucht deine Idee auf MVC-Basis als Prototyp zu realisieren. Vielleicht wird dir der Anblick meines Skriptes ein wenig Kopfweh verursachen. Speziell der Teil unter 'move_key_bindings'. Das MVC setzt sich ja aus drei Hauptklassen zusammen. Das sind die Model-View-Controller Klassen. Da dein Projekt ja etwas Großes werden könnte wäre es sinnvoll auf der MVC-Basis aufzubauen um den Überblick nicht zu verlieren. Das Projekt könnte noch besser strukturiert werden indem es auf mehrere Dateien aufgeteilt würde z.B. football_model.py, football_view.py, football_control.py, football_config.py, football_images.py usw. Diese Dateien könnten untereinander wo sie nötig sind importiert werden. Das Skript muss natürlich noch wesentlich erweitert werden um alle Spieler zu integrieren! Hier wäre mein Prototyp verfügbar:

MVC-Football Prototype

Das Skript ist ein erster Wurf und kann sicher noch wesentlich verbessert werden! Noch viel Spass beim Programmieren.

Gruß wuf :wink:
Take it easy Mates!
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Schönes Skript, aber viele Sachen verstehe ich noch nicht so ganz :oops:
Braucht man da wirklich soo viele Klassen? bzw. wozu MVC oder wo ist der Sinn davon?
----------
Ich habe jetzt einfach mal von vorne angefangen, und versuche mal alles mit Klassen zu realisieren.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hier vorab etwas über MVC:

MVC-Theorie

Gruß wuf :wink:
Take it easy Mates!
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

@wuf:
Ok... ich denke mal dass das eher was für erfahrenere Programmierer ist, deshalb lasse ich erstmal die Finger davon :D
ich bin jetzt fast so weit wie du, ich poste den Code dann bald nochmal ;)
Gruß raiminator
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi raiminator
raiminator hat geschrieben:Ok... ich denke mal dass das eher was für erfahrenere Programmierer ist, deshalb lasse ich erstmal die Finger davon :D
OK raiminator verstehe ich voll. Jeder sollte sein Projekt so lösen wie es seine momentanen Kenntnisse ermöglichen.

Betreffs MVC:
Der oder mein Hauptgrund hierfür ist sollte ich den grafischen Teil jetzt auf Tkinter basieren einmal durch ein anderes GUI-Toolkit wie Pygame, wxPython oder PyQt ersetzen muss ich nur die Klassen erneuern die das GUI-Handling erledigen. Die Model-, Control-Klasse und Konfiguration kann fast 1:1 ungeändert übernommen werden.
raiminator hat geschrieben:ich bin jetzt fast so weit wie du, ich poste den Code dann bald nochmal ;)
Super! Viel Spass.

Gruß wuf :wink:
Take it easy Mates!
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Gut, dann habe ich das verstanden. Da ich aber bis jetzt nur Tkinter "beherrsche" reicht mir das halt erstmal so aus ;)
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

Soo, hier ist wie versprochen meine neue Version.
http://www.python-forum.de/pastebin.php?mode=view&s=289
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi raiminator

Wie ich sehe ist dein Fussballspiel schon mächtig gewachsen. Wo sind die Globals geblieben? Du hast es geschafft sie zu eliminieren. Super! Habe eine Frage. Ist die Spieleraufstellung wie du sie gewählt hast eine Standardaufstellung für Fussballspiele? Interessiert mich da ich selber etwas in diese Richtung programmiere. Ich nehme an, dass du deinen Spielern auf dem Felde noch einen sichtbaren Tag anheftest (Nr., Buchstabe oder Name) damit du weisst wo sich welcher Spieler befindet? Wie ist die Bedienung?

Gruß wuf :wink:
Take it easy Mates!
raiminator
User
Beiträge: 31
Registriert: Dienstag 1. Mai 2012, 08:06

ich als Werder-Fan nehme wohl immer [4, 1, 2, 1, 2], am häufigsten wird aber
[4, 2, 3, 1] oder [4, 2, 2, 2] verwendet ;) muss man dann halt oben in der Konfiguration ändern.
Das mit dem Tag... werde ich noch machen, jetzt aber erstmal Spieler wechseln- und passen-Funktion :)
Antworten