Search code examples
pythontkinterturtle-graphicshilbert-curve

How to make restart button for Turtle tkinter? (Turtle, Tkinter, hilbert curve)


I am currently working on a small project, in which a user can input on a GUI the nth iteration for a hilbert curve and the curve will be drawn with turtle and is displayed on the same window (not opening a new one). I have a calculate button, which seems to be working fine, and a restart button, which should clear the screen and reset Turtle to its initial state. This currently only works, if the process for the calculation of n has finished and the drawing too. So when you try to reset the function/process while turtle is still drawing it goes crazy. I guess I have to redefine my clear function or I seem to miss something very easy. I think I have to do a conditional statement there like, if the process (main) is unfinished, do this and that. I can't come up with it and hope for help.

Here is the code (I hope the german does not interfer with understanding it):

""" Projektabgabe Python """

import turtle
import tkinter as tk
from tkinter import messagebox


""" Fenster-Deklaration und Umgebung """
window = tk.Tk()
window.title("Hilbert-Kurve")
window.geometry("600x600")

canvas = tk.Canvas(master=window, width=500, height=500)
canvas.pack()
""" RawTurtle um Turtle mit Tkinter zu verbinden """
s = turtle.TurtleScreen(canvas)
t = turtle.RawTurtle(canvas) 

""" Eingabefeld für Benutzereingabe """
eingabe_n = tk.Entry(window)
eingabe_n.pack()

""" Funktion der Hilbert-Kurve """
def hilbert(n, angle, step):
    """ Diese Funktion berechnet rekursiv die Hilbert-Kurve der n-ten Iterationsstufe.
        n: n-te Iterationsstufe der Hilbert-Kurve
        angle: der Winkel der Abzweigung, in main wird diese nach Definition der "Kurve" auf 90 gesetzt.
        step: bezeichnet Schrittlänge (in Pixel) die Turtle nach geg. n hinterlegt.
        Ausgabe: Hilbert-Kurve der n-ten Iterationsstufe. """

    # Wenn Höhe 0, gebe nichts zurück
    if n == 0:
        return None
    # Turtle dreht sich nach links um 90 Grad
    t.left(angle)
    hilbert(n-1, -angle, step) 

    t.forward(step)
    
    t.right(angle)
    
    hilbert(n-1, angle, step) 

    t.forward(step) 
    hilbert(n-1, angle, step) 
    t.right(angle)
    t.forward(step)
    
    hilbert(n-1, -angle, step)
    t.left(angle) 

""" Main-Funktion zur Ausgabe der Benutzereingabe
    In diesem Beispiel wurde die Skalierung der Kurve auf size = 250 gesetzt. """  
def main():
    # Wenn nichts in Eingabebox eingegeben, Fehlermeldung bzw. Infobox

    if len(eingabe_n.get())==0:
        messagebox.showinfo(title=None, message="Geben Sie eine natürliche Zahl größer 0 ein.")     

    n = int(eingabe_n.get())
    
    if n > 3:
        t.speed("fastest")
    # Wenn Eingabe 0, Fehlermeldung bzw. Infobox
    if n == 0:
         messagebox.showinfo(title=None, message="Geben Sie eine natürliche Zahl größer 0 ein.")
         t.reset()
    # size bezeichnet die Skalierung der abzubildenen Hilbertkurve     
    size = 400
    t.penup() # Bewege Turtle ohne zu zeichnen
    # Koordinaten (x,y) je nach size berechnen 
    t.goto(-size / 2, -size / 2) # x = -400/2 und y = -400/2
    # Absetzen von Turtle, da Position gefunden
    t.pendown() 
    """ Wenn man in der Hilbert-Kurve ein Segment |_| hat, dann ist dessen Kantenlänge aller drei Kanten die stepsize und am
        Anfang ist die 1 wenn wir den Kasten auf 1 normieren (oder 400 wenn size=400). Dann unterteilt man das Segment ja
        in ein Raster mit 4x4 Punkten, zwischen denen man die neuen Linien zieht. Im Endeffekt die Anzahl der Punkte in dem
        Gitter auf der gesamten Kantenlänge in jedem Schritt verdoppelt, daher ist die Anzahl der Gitterpunkte auf Level n
        dann 2ⁿ. Wenn man zwei nebeneinander liegende Gitterpunkte verbindet (weil sie verschmelzen), kommt man auf 2^n-1 Verbindungslinien.
        Also teilt man die Gesamtstrecke durch 2^n-1 und skaliert das ganze auf die Seitenlänge des Fensters, also
        gerade: size/ 2**n-1. """
    hilbert(n, 90, size/(2 ** n-1))
    
""" Reset-Funktion """
def clearFunc():
    s.clearscreen()
    u = turtle.RawTurtle(canvas)
    t = u
    
""" Button-Deklarierung
    button_n: Bezeichner für den Berechnen Button
    reset_button: Bezeichner für den Reset (zurücksetzen) Button """
button_n = tk.Button(window, text="Berechnen",command=main)
button_n.pack()

reset_button = tk.Button(window, text="Reset", command=clearFunc)
reset_button.pack()

""" Fenster Funktion """
window.mainloop()


Solution

  • Your problem is that Hilbert is recursive and that there is nothing in it to stop it until it reached the end with n==0. The only way I found to circumvent this, goes as follows: We define a global boolean variable named Flag which will carry the information that we want to stop.

    My suggestion involves 3 modifications and goes like this:

    In the "main()" section:

    def main():
        global Flag; Flag=False
    

    In the "hilbert(...)" section:

    # Wenn Höhe 0, gebe nichts zurück
    if Flag or n == 0:   
        return None
    

    And finally, in the "clearFunc()" section:

    def clearFunc():
        global Flag; Flag=True  
    

    I made these 3 modifications and it solves your problem (as I understood it). Note that the global nature of this auxiliary variable is essential.