Search code examples
pythontkinterturtle-graphicstkinter-canvaspython-turtle

how to make turtle draw on click and drag?


i am trying to make paint like app in turtle and tkinter. when i was only using turtle i made a program that lets you draw freely using click and drag. then i tried to make an interface and i switched to raw turtle. now when i click the turtle moves and when i drag the turtle it draws but when i click and drag it only moves. i do not know how to fix it

first code in turtle only :

    import turtle
    turtle.tracer(False)
    turtle.hideturtle()
   
    def draw(x, y):
        t.ondrag(None)
        t.down()
        t.goto(x, y)
        t.up()
        turtle.update()
        t.ondrag(draw)

    def move(x, y):
        turtle.onscreenclick(None)
        t.goto(x, y)
        turtle.onscreenclick(move)
        turtle.update()

    t = turtle.Turtle()
    t.shape("circle")
    t.pencolor('black')
    t.fillcolor("")
    t.up()


    t.pensize(6)
    t.turtlesize(1000000, 1000000)
    ts = turtle.getscreen()
    t.ondrag(draw)
    turtle.onscreenclick(move)
    turtle.update()
    turtle.listen()
    turtle.done()

code with turtle and tkinter :

    import tkinter as tk
    from functools import partial
    from turtle import TurtleScreen, RawTurtle

    def draw(x, y):
        turtle.ondrag(None)
        turtle.pendown()
        turtle.goto(x, y)
        turtle.penup()
        screen.update()
        turtle.ondrag(draw)

    def move(x, y):
        screen.onclick(None)
        turtle.goto(x, y)
        screen.onclick(move)
        screen.update()
    

    def set_color(color):
        global pen_color
        pen_color = color
        turtle.pencolor(color)
        screen.update()
    
    # --- add widgets ---

    root = tk.Tk()

    canvas = tk.Canvas(root, width=500, height=500)
    canvas.pack(side='right', expand=True, fill='both')

    frame = tk.Frame(root)
    frame.pack(side='left', fill='y')

    tk.Label(frame, text='COLORS').grid(column=0, row=0)

    tk.Button(frame, bg='red', width=10, command=partial(set_color, 'red')).grid(column=0, row=1)
    tk.Button(frame, bg='yellow', width=10, command=partial(set_color, 'yellow')).grid(column=0, row=2)
    tk.Button(frame, bg='green', width=10, command=partial(set_color, 'green')).grid(column=0, row=3)
    tk.Button(frame, bg='blue', width=10, command=partial(set_color, 'blue')).grid(column=0, row=4)
    tk.Button(frame, bg='black', width=10, command=partial(set_color, 'black')).grid(column=0, row=5)

    screen = TurtleScreen(canvas)
    screen.tracer(False)

    pen_color = 'black'

    turtle = RawTurtle(screen)
    # turtle.hideturtle()
    turtle.shape("circle")
    turtle.penup()
    turtle.pensize(5)
    turtle.color(pen_color, screen.bgcolor())

    turtle.ondrag(draw)
    screen.onclick(move)
    screen.update()
    screen.listen()
    screen.mainloop()

from what i understand ( i might be wrong i just started learning python ) the listen function should be the one collecting the events. the turtle done function should keep the turtle alert in the first version, but in the second the mainloop keeps the whole app alert and i cant use done anymore. the problem might have happened somewhere in those functions i think. i tried using listen, update and done in various places but nothing seems to change ( or even brake it just works as before )


Solution

  • since the turtle only moves when you click on it, i had to make the turtle as big as the entire screen. then i tried to make it invisible using

    turtle.hideturtle()
    

    that didnt work so instead i made the turtle invisible without changing the pencolor or fillcolor

    new code :

    import tkinter as tk
    from functools import partial
    from turtle import TurtleScreen, RawTurtle, Shape
    
    def draw(x, y):
        turtle.ondrag(None)
        turtle.pendown()
        turtle.goto(x, y)
        turtle.penup()
        screen.update()
        turtle.ondrag(draw)
    
    def move(x, y):
        screen.onclick(None)
        turtle.goto(x, y)
        screen.onclick(move)
        screen.update()
    
    
    def set_color(color):
        global pen_color
        pen_color = color
        turtle.pencolor(color)
        screen.update()
    
    # --- add widgets ---
    
    root = tk.Tk()
    
    canvas = tk.Canvas(root, width=500, height=500)
    canvas.pack(side='right', expand=True, fill='both')
    
    frame = tk.Frame(root)
    frame.pack(side='left', fill='y')
    
    tk.Label(frame, text='COLORS').grid(column=0, row=0)
    
    tk.Button(frame, bg='red', width=10, command=partial(set_color, 'red')).grid(column=0, row=1)
    tk.Button(frame, bg='yellow', width=10, command=partial(set_color, 'yellow')).grid(column=0, row=2)
    tk.Button(frame, bg='green', width=10, command=partial(set_color, 'green')).grid(column=0, row=3)
    tk.Button(frame, bg='blue', width=10, command=partial(set_color, 'blue')).grid(column=0, row=4)
    tk.Button(frame, bg='black', width=10, command=partial(set_color, 'black')).grid(column=0, row=5)
    
    screen = TurtleScreen(canvas)
    screen.tracer(False)
    
    
    turtle = RawTurtle(screen)
    #turtle.hideturtle()
    turtle.shape("circle")
    polygon = turtle.get_shapepoly()
    fixed_color_turtle = Shape("compound")
    fixed_color_turtle.addcomponent(polygon, "", "")
    screen.register_shape('fixed', fixed_color_turtle)
    turtle.shape("fixed")
    turtle.penup()
    turtle.pensize(5)
    turtle.turtlesize(2000,2000)
    turtle.ondrag(draw)
    screen.onscreenclick(move)
    screen.update()
    screen.mainloop()
    

    now it work perfectly, yay :)