Random Schleife

Fragen zu Tkinter.
Antworten
RoflKartoffel379
User
Beiträge: 8
Registriert: Dienstag 7. Juni 2016, 14:03

Dienstag 7. Juni 2016, 14:08

Guten Tag Community,
Ich habe ein Problem. Ich möchte ein spiel mit tkinter machen, aber ich weiß nicht wie ich eine schleife schreiben kann wo sich
ein Objekt Random bewegt und nach z.B. 1 sek sich irgendwo anders hinbewegt. Die Objekt und so hab ich schon. möchte nur
die Schleife wissen. Habe es selber mit jeder erdänkliche Schleife ausprobiert aber keine Lösung gefunden.
Bitte um Hilfe.
Wenns geht mit Quellcode
Sirius3
User
Beiträge: 7777
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 7. Juni 2016, 14:12

@RoflKartoffel379: was hast Du denn probiert, und was passiert, was Du nicht möchtest? Wenn's geht mit Quellcode.
BlackJack

Dienstag 7. Juni 2016, 14:18

@RoflKartoffel379: So eine Schleife kannst Du höchstwahrscheinlich gar nicht schreiben, denn der Programmfluss wird von der Tk-Hauptschleife angetrieben, was nicht mehr funktioniert wenn Du selber Schleifen schreibst die viel Zeit beanspruchen. Denn während Deine Schleife läuft, kann ja die Tk-Hauptschleife nicht laufen, also wird auch die GUI nicht aktualisiert und reagiert nicht auf den Benutzer.

Du musst den Schleifenkörper in eine Methode auslagern die dann regelmässig über die `after()`-Methode auf Widgets aufgerufen wird.
RoflKartoffel379
User
Beiträge: 8
Registriert: Dienstag 7. Juni 2016, 14:03

Dienstag 7. Juni 2016, 14:23

Code: Alles auswählen

from import tkinter *
import time
import random

r = random.randint( 1, 30)
a = random.randint( 1, 30)


def Start():
    i = 0
    while i != 5:
        leinwand.move(Kreis, r, a)
        time.sleep(1)
        i = i + 1
Das ist ein Teil des codes. Ich möchte ,dass das Objekt Kreis sich random bewegt dann 1 sek wartet und dann an eine andere
Random Position geht. Hier ist jetzt z.B. eine While Schleife
Zuletzt geändert von Anonymous am Dienstag 7. Juni 2016, 14:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 7777
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 7. Juni 2016, 14:34

@RoflKartoffel379: Was ist Kreis? Was ist leinwand? Das ist kein Programm, das irgendwer testen könnte, um Fehler zu suchen. Mach es den Forumsteilnehmern möglichst einfach, indem Du läuffähige Programme postest, dann ist die Chance am größten, dass Du kompetente Antworten bekommst.
Deine while-Schleife unterbricht die Ereignisschleife von Tk, so dass Deine GUI einfriert, bis die Schleife fertig ist. r und a werden nie geändert, der Kreis kann sich also nicht bewegen.
RoflKartoffel379
User
Beiträge: 8
Registriert: Dienstag 7. Juni 2016, 14:03

Dienstag 7. Juni 2016, 14:41

Hier das ganze program:

Code: Alles auswählen

#This Program is Copyright by Moritz Winnekens
from tkinter import *
import random
import time

#Fenster

root = Tk()
root.title("Centipede")
root.geometry("800x900")

#Variablen

leinwand = Canvas(root, bg="black")
Kreis = leinwand.create_oval(10, 10, 20, 20, fill="green")
Rechteck = leinwand.create_rectangle(200, 200, 210, 210, fill="red")
Bewegen = leinwand.move(Kreis, 5, 0)
i = 0
r = random.randint( 1, 30)
a = random.randint( 1, 30)

#Oberfläche

def Controls():
    lab1 = Label(root, text="|W| ist nach oben")
    lab2 = Label(root, text="|A| ist nach links")
    lab3 = Label(root, text="|D| ist nach rechts")
    lab4 = Label(root, text="|S| ist nach unten")
    lab5 = Label(root, text="|Space| ist schießen")
    lab1.pack()
    lab2.pack()
    lab3.pack()
    lab4.pack()
    lab5.pack()
   
def Start():
    i = 0
    while i != 5:
        leinwand.move(Kreis, r, a)
        i = i + 1
    
        
leinwand = Canvas(root, bg="black")

#Tastendruck

def keydown(e):
    move_down(e)
    move_up(e)
    move_right(e)
    move_left(e)

frame = Frame(root, width=100, height=100)
frame.bind("<KeyPress>", keydown)
frame.pack()
frame.focus_set()

#Objekte

Kreis = leinwand.create_oval(10, 10, 20, 20, fill="green")
Rechteck = leinwand.create_rectangle(200, 200, 210, 210, fill="red")

#Button

but1 = Button(root, text="Start", command=Start)
but1.pack()

but2 = Button(root, text="Controls", command=Controls)
but2.pack()

leinwand.pack()

leinwand.move(Kreis, 5, 5)

#Movement

def move_up(e):
    if(e.char == "w"):
        leinwand.move(Rechteck, 0, -5)

def move_down(e):
    if(e.char == "s"):
        leinwand.move(Rechteck, 0, 5)

def move_right(e):
    if(e.char == "d"):
        leinwand.move(Rechteck, 5, 0)

def move_left(e):
    if (e.char == "a"):
        leinwand.move(Rechteck, -5, 0)


        

root.mainloop
Hilft dir das weiter?
Zuletzt geändert von Anonymous am Dienstag 7. Juni 2016, 14:47, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

Dienstag 7. Juni 2016, 15:07

@RoflKartoffel379: Auf Modulebene gehören nur Definitionen von Konstanten, Funktionen, und Klassen. Keine Variablen. Noch unübersichtlicher wird es wenn zwischen Variablen und Hauptprogramm auf Modulebene auch noch Funktionsdefinitionen stehen. Das ist furchtbar unübersichtlich. So kommen dann so Sachen zustande, dass Namen definiert, aber nirgends verwendet werden, oder Namen fälschlicherweise mehr als einmal definiert werden. `leinwand` zum Beispiel wird zweimal an ein `Canvas`-Objekt gebunden, und auch `Kreis` und `Rechteck` werden zweimal definiert (also einmal definiert und einmal neu gebunden), obwohl das ziemlich sicher nur einmal passieren sollte.

Die Grösse des Hauptfensterts solltest Du nicht angeben. Gib die Grösse des Canvas an, dann brauchst Du Dir nicht merken was dort die Defaultwerte sind, und dann wird das Fenster schon so gross dargestellt wie der Inhalt Platz benötigt.

Der `Frame` als Tastatur-Listener ist nicht wirklich nötig. Das kann man auch an das `Canvas`-Objekt oder an das Fenster binden.

Die Namen `r` und `a` sind schlecht. Da weiss der Leser nicht was die Werte dahinter bedeuten sollen. Genau dafür sind Namen aber da.

Das Problem mit Schleifen wurde ja schon genannt, zusätzlich wäre Deine ``while``-Schleife eigentlich eine ``for``-Schleife. Wenn man weiss wie oft eine Schleife durchlaufen werden wird, nimmt man ``for`` und `range()` und nicht ``while`` mit manuellem hochzählen.

In `Controls()` hätte man sich Tipparbeit mit einer Schleife über die Texte sparen können. Oder man hätte einfach *ein* Label und Text mit Zeilenumbrüchen verwenden können.

``if`` ist keine Funktion — die unnötigen Klammern um die Bedingung gehören da nicht hin, und auf jeden Fall sollte man ein Leerzeichen nach dem Schlüsselwort setzen.

Die Namen entsprechen in der Schreibweise nicht dem Style Guide for Python Code. Zudem werden Funktionen und Methoden üblicherweise nach Tätigkeiten benannt, weil sie etwas *tun*, und um sie besser/leichter von eher passiven Werten unterscheiden zu können.

Letztendlich kommt man bei jedem nicht-trivialen GUI-Programm in Python nicht um objektorientierte Programmierung (OOP) herum. Wie sieht es denn da bei Dir aus? Klassen sollte man IMHO am besten schon drauf haben bevor man mit GUI-Programmierung anfängt, denn die bringt auch nochmal neuen Lernstoff mit, wie ereignisorientierte Programmierung. Also keine Schleifen wie Du Dir das vorgestellt hast.

Es würde sich hier zum Beispiel anbieten die IDs von den Formen auf dem Canvas in Objekte zu kapseln.
RoflKartoffel379
User
Beiträge: 8
Registriert: Dienstag 7. Juni 2016, 14:03

Dienstag 7. Juni 2016, 15:13

Ich habe Python gerade in der Schule und wir sollten als Projektarbeit ein eigenes großes Projekt aussuchen.
Wir haben das noch nicht solange und ich habe es so geschrieben wie wir es gelernt haben.
Entschuldigung das ich dann so geschrieben habe, ich wollte eigentlich nur die Problem lösung dazu, da ich da nicht
mehr weiter komme
BlackJack

Dienstag 7. Juni 2016, 15:44

@RoflKartoffel379: Du brauchst Dich für den Code nicht zu entschuldigen. Gerade weil Du noch nicht lange Programmierst sind Hinweise wo man etwas besser machen kann, sinnvoll. Insbesondere die Variablen auf Modulebene und das mischen von deren Definitionen, dem Hauptprogramm, und Funktionsdefinitionen, stehen Dir im Weg, weil das *jetzt* schon schwer zu durchschauen ist, und das mit mehr Code dieser Art nicht besser werden wird.

Die Problemlösung hat etwas mit objektorientierter Programmierung zu tun (IMHO), darum nützt das jetzt nicht ein paar Zeilen Quelltext hier zu schreiben, die Du dann sehr wahrscheinlich nicht nachvollziehen kannst, wenn Du Dich mit dem Thema noch nicht beschäftigt hast. Ganz konkret wurde die `after()`-Methode auf Widgets und herausziehen des Schleifeninhalts in eine Methode die von der Methode dann aufgerufen wird, schon erwähnt.

Vielleicht nochmal etwas inhaltliches: Mit der `Canvas.move()`-Methode wirst Du nicht weit kommen, weil die nicht verhindert, dass Objekte den sichtbaren Bereich des `Canvas` verlassen. Da kann sich der Spieler raus manövrieren, aber gerade auch bei dem sich zufällig bewegenden Grafikobjekt besteht die Gefahr, dass das ”abhaut”. Man muss also die Möglichkeit haben zu prüfen wo sich das Objekt befindet und die Bewegung gegebenenfalls verhindern oder umdrehen.
BlackJack

Mittwoch 8. Juni 2016, 11:24

Das ganze mal ein bisschen aufgeräumter mit echten Funktionen die nicht auf globale Variablen zurückgreifen:

Code: Alles auswählen

import tkinter as tk
import random
from functools import partial

STEP_DELTA = 5


def show_control_info(parent):
    tk.Label(
        parent,
        text=
            '|W| ist nach oben\n'
            '|A| ist nach links\n'
            '|D| ist nach rechts\n'
            '|S| ist nach unten\n'
            '|Space| ist schießen'
    ).pack()


def move_circle(canvas, circle_id, x_delta, y_delta, step_count):
    if step_count > 0:
        canvas.move(circle_id, x_delta, y_delta)
        canvas.after(
            1000,
            move_circle,
            canvas,
            circle_id,
            x_delta,
            y_delta,
            step_count - 1,
        )

 
def move_up(canvas, rectangle_id, event):
    if event.char == 'w':
        canvas.move(rectangle_id, 0, -STEP_DELTA)
 

def move_down(canvas, rectangle_id, event):
    if event.char == 's':
        canvas.move(rectangle_id, 0, STEP_DELTA)
 

def move_right(canvas, rectangle_id, event):
    if event.char == 'd':
        canvas.move(rectangle_id, STEP_DELTA, 0)


def move_left(canvas, rectangle_id, event):
    if event.char == 'a':
        canvas.move(rectangle_id, -STEP_DELTA, 0)
 

def on_keypress(canvas, rectangle_id, event):
    for move_func in [move_down, move_up, move_right, move_left]:
        move_func(canvas, rectangle_id, event)
 

def main():   
    root = tk.Tk()
    root.title('Centipede')
     
    canvas = tk.Canvas(root, width=800, height=600, background='black')
    canvas.pack()
    # 
    # TODO Rename those to reflect the meaning of the shape, not the
    #   form of the shape.
    # 
    circle_id = canvas.create_oval(10, 10, 20, 20, fill='green')
    rectangle_id = canvas.create_rectangle(200, 200, 210, 210, fill='red')

    root.bind('<KeyPress>', partial(on_keypress, canvas, rectangle_id))
    # 
    # FIXME Both buttons can be pressed multiple times which leads to
    #   undesired behaviour.
    # 
    x_delta = random.randint(1, 30)
    y_delta = random.randint(1, 30)
    tk.Button(
        root,
        text='Start',
        command=partial(move_circle, canvas, circle_id, x_delta, y_delta, 5),
    ).pack()
    tk.Button(
        root, text='Controls', command=partial(show_control_info, root)
    ).pack()
     
    root.mainloop()


if __name__ == '__main__':
    main()
RoflKartoffel379
User
Beiträge: 8
Registriert: Dienstag 7. Juni 2016, 14:03

Mittwoch 8. Juni 2016, 18:57

Cool, Danke das du das gemacht hast.
Als Frage gibst du auch sozusagen Unterricht in sowas? Möchte besser werden, da ich sowas später Beruflich machen möchte?
Antworten