Unfunktionabel?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Sarius
User
Beiträge: 20
Registriert: Dienstag 5. Juni 2018, 18:32

Ich habe ein Buch gekauft und einen Code gefunden und ausprobiert, nur funktioniert er nicht. Wieso?

Code: Alles auswählen

from tkinter import*
HEIGHT = 500
WIDTH = 800
window = Tk()
c = Canvas(window, width=WIDTH, height=HEIGHT, bg="darkblue")
c.pack()
window.attributes("-topmost", 1)

schiff_id = c.create_polygon(5, 5, 5, 25, 30, 15, fill="red")
schiff_id2 = c.create_oval(0, 0, 30, 30, outline="red")
SCHIFF_R = 15
MID_X = WIDTH / 2
MID_Y = HEIGHT / 2
c.move(schiff_id, MID_X, MID_Y)
c.move(schiff_id2, MID_X, MID_Y)

SCHIFF_GESCHW = 10
def schiff_beweg(event):
    if event.keysym == "Up":
        c.move(schiff_id, 0, -SCHIFF_GESCHW)
        c.move(schiff_id2, 0, -SCHIFF_GESCHW)
    elif event.keysym == "Down":
        c.move(schiff_id, 0, SCHIFF_GESCHW)
        c.move(schiff_id2, 0, SCHIFF_GESCHW)
    elif event.keysym == "Left":
        c.move(schiff_id, -SCHIFF_GESCHW, 0)
        c.move(schiff_id2, -SCHIFF_GESCHW, 0)
    elif event.keysym == "Right":
        c.move(schiff_id, SCHIFF_GESCHW, 0)
        c.move(schiff_id2, SCHIFF_GESCHW, 0)
c.bind_all("<Key>", schiff_beweg)


window.mainloop()

from random import randint
bub_id = list()
bub_r = list()
bub_geschw = list()
MIN_BUB_R = 10
MAX_BUB_R = 30
MAX_BUB_GESCHW = 10
GAP = 100
def erstelle_bubble():
    x = WIDTH + GAP
    y = randint(0, HEIGHT)
    r = randint(MIN_BUB_R, MAX_BUB_R)
    id1 = c.create_oval(x - r, y - r, x + r, y + r, outline="white")
    bub_id.append(id1)
    bub_r.append(r)
    bub_geschw.append(randint(1, MAX_BUB_GESCHW))
def bewege_bubbles():
    for i in range(len(bub_id)):
        c.move(bub_id[i], -bub_geschw[i], 0)
BUB_CHANCE = 10
def hole_koord(id_num):
    pos = c.coords(id_num)
    x = (pos[0] + pos[2]) / 2
    y = (pos[1] + pos[3]) / 2
    return x, y
def lösche_bubble(i):
    del bub_r[i]
    del bub_geschw[i]
    c.delete(bub_id[i])
    del bub_id[i]
def entf_bubbles():
    for i in range(len(bub_id)-1, -1, -1):
        x, y = hole_koord(bub_id[i])
        if x < -GAP:
            lösche_bubble(i)
from math import sqrt
def distanz(id1, id2):
    x1, y1 = hole_koord(id1)
    x2, y2 = hole_koord(id2)
    return sqrt((x2 + x1)**2 + (y2 - y1)**2)
def kollision():
    points = 0
    for bub in range(len(bub_id)-1, -1, -1):
        if distanz(schiff_id2, bub_id[bub]) < (SCHIFF_R + bub_r[bub]):
            points += (bub_r[bub] + bub_geschw[bub])
            lösche_bubble(bub)
    return points
c.create_text(50, 30, text="ZEIT", fill="white" )
c.create_text(150, 30, text="PUNKTE", fill="white" )
time_text = c.create_text(50, 50, fill="white" )
score_text = c.create_text(150, 50, fill="white" )
def zeige_punkte(score):
    c.itemconfig(score_text, text=str(score))
def zeige_zeit(time_left):
    c.itemconfig(time_text, text=str(time_left))
from time import sleep, time
BUB_CHANCE = 10
TIME_LIMIT = 30
BONUS_SCORE = 1000
score = 0
bonus = 0
ende = time() + TIME_LIMIT
while time() < ende:
    if randint(1, BUB_CHANCE) == 1:
        erstelle_bubble()
    bewege_bubbles()
    entf_bubbles()
    score += kollision()
    if (int(score / BONUS_SCORE)) > bonus:
        bonus += 1
        ende += TIME_LIMIT
    zeige_punkte(score)
    zeige_zeit(int(ende - time()))
    window.update()
    sleep(0.01)
c.create_text(MID_X, MID_Y, \
    text="GAME OVER", fill="white", font=("Helvetica", 30 ))
c.create_text(MID_X, MID_Y + 30, \
    text="Punkte: "+ str(score), fill="white")
c.create_text(MID_X, MID_Y + 45, \
    text="Bonus-Zeit: "+ str(bonus*TIME_LIMIT), fill="white")
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

das ist das Problem wenn man irgendwas findet. Es kann sein, dass es irgendwer weggeworfen hat, weil es Müll ist.

GUI-Programmierung ist bei Deinem jetzigen Wissensstand noch zu viel. Lerne besser noch ein paar Grundlagen, und versuche Dich z.B. an einfacher Textspielen.
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Also ich bin selbst noch ein Anfänger, gebe dir aber den Tipp: kauf dir das Buch "Einstieg in Python" (von Thomas Theis), lese es durch und versuch es zu verstehen, dann liest es nochmal und versuchst es nochmals zu verstehen und versuchst die Aufgaben zu lösen, da in dem Buch Übungsaufgaben mit drin stehen. Ist nicht schlimm, wenn du mal wenige nicht machst oder kappierst.
Ich selbst habe es so gemacht und programmiere eine GUI, ganz cool. Auch wenn der Weg nicht so ist, wie ihn die fortgeschrittenen Programmierer machen würden, aber ich komme zum Ziel und die GUI tut was sie soll! :D

Kleiner Tipp am Rande: nimm die Codebeispiele mit Inhalt, was dich interessiert. Zum Beispiel gibts in meinem Buch ein Spiel, bei den man Zahlen berechnen muss und so, aber sowas interessiert mich nicht, deshalb schaltet das Gehin dabei auch ab und ist nicht so effektiv. Ist einfach meine Meinung! :)

PS. Mit "Copy & Paste" kommst nicht weit. ;) Und falls im Code "Errors" , also Fehler, kommen, schau nach was für welche und wie du sie lösen kannst.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

xXSkyWalkerXx1 hat geschrieben: Freitag 13. Juli 2018, 01:14 Also ich bin selbst noch ein Anfänger, gebe dir aber den Tipp: kauf dir das Buch "Einstieg in Python" (von Thomas Theis)
Den Tipp kann ich nicht teilen. Bei besagtem Author habe ich das Gefühl dass er zu zig Sprachen ein Buch für entsprechenden Verlag auf den Markt bringt ohne sich, bezogen auf Python, in einem ausreichenden Maß mit der Sprache beschäftigt zu haben. Das macht sich in o.g. Buch dadurch bemerkbar, dass PEP8 konsequent ignoriert und somit nicht pythonisch entwickelt wird. Das ist aber für Python wichtig, da man sonst in der Community Gefahr läuft gegen den Strom zu schwimmen und elementare Werkzeuge der Sprache falsch einsetzt / interpretiert und im späteren Programmiererleben mit üblen Effekten zu kämpfen hat.
When we say computer, we mean the electronic computer.
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Ja, dass stimmt, dass er sich auf viele Sprachen bezieht, ob er sich auch intensiv mit dieser Sprache beschäftigt hat, weiß ich allerdings nicht, somit kann und möchte ich dazu auch nichts sagen. ^^
Was ist "PEP8" ?
Das Buch ist nur für die Basis gedacht, ich konnte damit mir ganz gut die Basis aneignen.
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

xXSkyWalkerXx1 hat geschrieben: Freitag 13. Juli 2018, 10:23 Was ist "PEP8" ?
PEP8 ist der offizielle Python-Style-Guide: https://www.python.org/dev/peps/pep-0008/?

Wenn man das verinnerlicht sollte kein Code wie oben herauskommen.

EDIT: wenn der Code tatsächlich aus einem Lehrbuch entnommen wurde, frage ich mich, welcher Author dafür ernsthaft Geld verlangt.
When we say computer, we mean the electronic computer.
xXSkyWalkerXx1
User
Beiträge: 379
Registriert: Mittwoch 27. Juni 2018, 17:39

Achso, okey, jaa - verstehe. :D
Hab "Style" gelesen und iwie nochmal auf dem Code geschaut und ja - dann habe ich auch das Problem gesehen. :D
Aber dann frage ich mich aus welchem Buch / Tutorial der solch ein Code hat? o.O

EDIT: Dann gebe ich dir auch Recht, dass man über PEP8 im Buch von Theis nichts findet.
Hatte gerade mal kurz durchgeblättert und nichts gefunden, doch dieses Thema ist immerhin
wirklich wichtig. Wobei ich sagen muss, ich hatte das auch durch die Beispiele bemerkt, wie
Python aufgebaut wird.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@xXSkyWalkerXx1: Der hat das Buch *geschrieben* und nicht *ab*geschrieben.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Benutzeravatar
sls
User
Beiträge: 480
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Country country = new Zealand();

Da ist ja noch diese andere Sache. Der TE sagt, es funktioniert nicht. Er sagt aber nicht, was nicht funktioniert. Der Einwand von Sirius3 bleibt, GUI ist am Anfang ein harter Brocken.
When we say computer, we mean the electronic computer.
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sarius: Wo hast Du denn den Code gefunden? Im Buch? *So*? Das sieht nämlich sehr danach aus als wurde dort ein Programm in mehreren Schritten entwickelt und anstatt die neuen, erweiterten Teile an der passenden Stelle in das Programm einzubauen, hat da jemand einfach alles aus dem Buch der Reihe nach in eine Datei abgeschrieben ohne zu verstehen was da in welcher Reihenfolge gemacht werden soll.

Aber selbst wenn man die Reihenfolge in Ordnung bringt, ist das kein gutes Programm von dem man sich abschauen sollte wie man programmiert.

Zu der Reihenfolgenänderung gehört unter anderem das die Importe alle oben im Programm stehen, gefolgt von der Definition der Konstanten. Dann kommen die Funktionsdefinitionen. Und der ganze Code zwischen den Funktionsdefinitionen der nichts importiert oder Konstanten definiert, das Hauptprogramm also, gehört ebenfalls in eine Funktion.

Durch dieses ganze Vermischen von Importen, Funktionsdefinitionen, und Hauptprogramm schleichen sich nämlich schnell mal fehler ein. Wenn man aufräumt, stellt man beispielsweise fest, dass die Konstante `BUB_CHANCE` zwei mal definiert ist. Glücklicherweise mit dem gleichen Wert. Aber man Stelle sich mal vor eine *Konstante* hätte in verschiedenen Abschnitten eines Programms unterschiedliche Werte. Viel Spass bei der Fehlersuche.

Die Namen sind teilweise schlecht bis sehr schlecht. Ich hoffe im Originalbuch sind die etwas besser, denn ich habe die Vermutung das ein paar von den Abkürzungen darauf zurück zu führen sind, das die originalen englischen Namen kürzer sind, und die Übersetzer die Vorgabe hatten die Länge beim Übersetzen nicht zu stark zu ändern, damit der Satz der Quelltexte möglichst nicht angepasst werden muss.

Es wurde dann aber auch sehr inkonsequent übersetzt. Beispielsweise die Funktion `zeige_punkte()` bekommt trotzdem noch ein englisches `score`-Argument übergeben.

Bevor man das Hauptprogramm in eine Funktion steckt und damit die ganzen globalen Variablen verschwinden und man die an die anderen Funktionen als Argumente übergeben muss, sollte man noch versuchen die Anzahl der Variablen zu reduzieren. Das beispielsweise die Blasen aus drei parallel geführten Listen bestehen, macht das Programm unnötig komplexer und auch fehleranfälliger.

Wenn man die einzelnen Blasen in Tupel steckt, kommt man mit *einer* Liste für alle Blasen aus. Die beiden Einzelteile des Schiffs kann man auch in ein Tupel stecken und damit zu einem Wert machen. Wenn man die IDs in umgekehrter Reihenfolge in das Tupel steckt, dann ist genau wie bei den Blasen das erste Element die ID, die beispielsweise von der Funktion zur Entfernungsmessung verwendet wird.

Dann kann man sich daran machen aus den Funktionen tatsächliche Funktionen zu machen, die was sie brauchen als Argumente übergeben bekommen und Ergebnisse als Rückgabewerte zurück geben. `erstelle_bubble()` beispielsweise braucht den `canvas` und muss selbst keine Liste(n) manipulieren, sondern könnte das Tupel mit den Daten der erzeugten Blase als Rückgabewert liefern.

Was ziemlich unpythonisch ist, sind die die beiden Funktionen die Blasen entfernen. Statt da rückwärts über einen Laufindex zu iterieren um Blasen aus der/den originalen Liste(n) zu entfernen, würde man in Python einfach neue Listen ohne die Blasen erstellen die entfernt werden sollen.

Eigentlich kommt man bei GUI-Programmierung nicht um objektorientierte Programmierung herum, oder zumindest Closures. In diesem Fall muss man mindestens in einem Fall `functools.partial()` oder einen ``lambda``-Ausdruck verwenden. Nämlich bei der Rückruffunktion für Tastendrücke. Denn die braucht den `Canvas` und das Schiff zusätzlich zum Ereignisobjekt.

Zwischenstand sähe dann so aus:

Code: Alles auswählen

from functools import partial
from math import sqrt
from random import randint
from time import sleep, time
import tkinter as tk

HEIGHT = 500
WIDTH = 800
MID_X = WIDTH / 2
MID_Y = HEIGHT / 2

MIN_BUBBLE_RADIUS = 10
MAX_BUBBLE_RADIUS = 30
MAX_BUBBLE_SPEED = 10
BUBBLE_CHANCE = 10
GAP = 100
assert GAP >= MAX_BUBBLE_RADIUS * 2

SHIP_RADIUS = 15
SHIP_SPEED = 10
TIME_LIMIT = 30
BONUS_SCORE = 1000


def move_ship(canvas, ship, x_delta, y_delta):
    canvas.move(ship[0], x_delta, y_delta)
    canvas.move(ship[1], x_delta, y_delta)


def on_key(canvas, ship, event):
    if event.keysym == 'Up':
        move_ship(canvas, ship, 0, -SHIP_SPEED)
    elif event.keysym == 'Down':
        move_ship(canvas, ship, 0, SHIP_SPEED)
    elif event.keysym == 'Left':
        move_ship(canvas, ship, -SHIP_SPEED, 0)
    elif event.keysym == 'Right':
        move_ship(canvas, ship, SHIP_SPEED, 0)


def create_bubble(canvas):
    x = WIDTH + GAP
    y = randint(0, HEIGHT)
    radius = randint(MIN_BUBBLE_RADIUS, MAX_BUBBLE_RADIUS)
    bubble_id = canvas.create_oval(
        x - radius, y - radius, x + radius, y + radius, outline='white'
    )
    speed = randint(1, MAX_BUBBLE_SPEED)
    return (bubble_id, radius, speed)


def move_bubbles(canvas, bubbles):
    for bubble_id, _, speed in bubbles:
        canvas.move(bubble_id, -speed, 0)


def get_center(canvas, obj):
    x_1, y_1, x_2, y_2 = canvas.coords(obj[0])
    return ((x_1 + x_2) / 2, (y_1 + y_2) / 2)


def remove_bubbles(canvas, bubbles):
    result = list()
    for bubble in bubbles:
        bubble_id = bubble[0]
        x, _ = get_center(canvas, bubble)
        if x < -GAP:
            canvas.delete(bubble_id)
        else:
            result.append(bubble)
    return result


def distance(canvas, obj_a, obj_b):
    x_1, y_1 = get_center(canvas, obj_a)
    x_2, y_2 = get_center(canvas, obj_b)
    return sqrt((x_2 - x_1)**2 + (y_2 - y_1)**2)


def handle_collisions(canvas, ship, bubbles):
    points = 0
    remaining_bubbles = list()
    for bubble in bubbles:
        bubble_id, radius, speed = bubble
        if distance(canvas, ship, bubble) < (SHIP_RADIUS + radius):
            points += radius + speed
            canvas.delete(bubble_id)
        else:
            remaining_bubbles.append(bubble)
    return (points, remaining_bubbles)


def show_score(canvas, score_text, score):
    canvas.itemconfig(score_text, text=str(score))


def show_time(canvas, time_text, time_left):
    canvas.itemconfig(time_text, text=str(time_left))


def main():
    window = tk.Tk()
    canvas = tk.Canvas(window, width=WIDTH, height=HEIGHT, bg='darkblue')
    canvas.pack()
    window.attributes('-topmost', 1)

    ship = (
        canvas.create_oval(0, 0, 30, 30, outline='red'),
        canvas.create_polygon(5, 5, 5, 25, 30, 15, fill='red')
    )
    move_ship(canvas, ship, MID_X, MID_Y)

    canvas.bind_all('<Key>', partial(on_key, canvas, ship))

    bubbles = list()

    canvas.create_text(50, 30, text='ZEIT', fill='white')
    canvas.create_text(150, 30, text='PUNKTE', fill='white')
    time_text = canvas.create_text(50, 50, fill='white')
    score_text = canvas.create_text(150, 50, fill='white')

    score = 0
    bonus = 0
    end_time = time() + TIME_LIMIT
    while time() < end_time:
        if randint(1, BUBBLE_CHANCE) == 1:
            bubbles.append(create_bubble(canvas))
        move_bubbles(canvas, bubbles)
        bubbles = remove_bubbles(canvas, bubbles)
        points, bubbles = handle_collisions(canvas, ship, bubbles)
        score += points
        if int(score / BONUS_SCORE) > bonus:
            bonus += 1
            end_time += TIME_LIMIT
        show_score(canvas, score_text, score)
        show_time(canvas, time_text, int(end_time - time()))
        window.update()
        sleep(0.01)

    canvas.create_text(
        MID_X, MID_Y, text='GAME OVER', fill='white', font=('Helvetica', 30)
    )
    canvas.create_text(
        MID_X, MID_Y + 30, text='Punkte: ' + str(score), fill='white'
    )
    canvas.create_text(
        MID_X, MID_Y + 45,
        text='Bonus-Zeit: ' + str(bonus * TIME_LIMIT),
        fill='white',
    )

    window.mainloop()


if __name__ == '__main__':
    main()
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Antworten