3

I have been trying to save images capturing handwritten text for my handwritten text recognition project. For this purpose I am using the python turtle. I want to change the coordinates of the turtle (in pen up position) on the canvas by moving my mouse, and make it write (in pen down position) by moving the mouse while holding on the left mouse button. I am not able to implement this. Here is my code.

import tkinter
import turtle

sc = tkinter.Tk()
sc.geometry("1000x1000+100+100")

fr4 = tkinter.Frame(sc, height=500, width=600, bd=4, bg="light green", takefocus="", relief=tkinter.SUNKEN)

fr4.grid(row=2, column=2, sticky=(tkinter.N, tkinter.E, tkinter.W, tkinter.S))

# Canvas
canvas = tkinter.Canvas(fr4, width=1920, height=1080)
canvas.pack()

# Turtle
turtle1 = turtle.RawTurtle(canvas)
turtle1.color("black")
turtle1.shape("turtle")
turtle1.speed(100000)

def drag_handler(x, y):
    turtle1.ondrag(None)  # disable event inside event handler
    turtle1.goto(x, y)
    turtle1.ondrag(drag_handler)  # reenable event on event handler exit

turtle1.ondrag(drag_handler)

sc.mainloop()
4
  • 1
    Please be more clear than "I am not able to implement this" as to the nature of the problem. Exactly what is not happening as you want? If there is an error, what is the full traceback? See How to create a Minimal, Complete, and Verifiable example. Commented Jun 16, 2017 at 23:56
  • Your code works if you want to drag the turtle with the mouse
    – eyllanesc
    Commented Jun 17, 2017 at 0:35
  • 1
    @eyllanesc, reread his description. He wants the tutle to always track the mouse, only leaving a trail when the left mouse button is down. The code provided does drag-n-draw, but doesn't track when the mouse button isn't pressed.
    – cdlane
    Commented Jun 17, 2017 at 6:35
  • Related: turtle - How to get mouse cursor position in window?
    – ggorlen
    Commented Nov 3, 2023 at 19:41

1 Answer 1

3

Below's my implementation of what you describe. I've moved it out of Tk and squarely into turtle. However, I introduce low level Tk calls to implement the missing turtle onmove() event handler. Once that's in place, it becomes a matter of managing motion, clicks, releases and drags. Make sure to click first on the window's title bar to make it active:

from turtle import Turtle, Screen

MOVING, DRAGGING = range(2)  # states

def move_handler(x, y):
    if state != MOVING:  # ignore stray events
        return

    onmove(screen, None)  # avoid overlapping events
    yertle.penup()
    yertle.setheading(yertle.towards(x, y))
    yertle.goto(x, y)
    onmove(screen, move_handler)

def click_handler(x, y):
    global state

    yertle.onclick(None)  # disable until release
    onmove(screen, None)  # disable competing handler

    yertle.onrelease(release_handler)  # watch for release event
    yertle.ondrag(drag_handler)  # motion is now dragging until release

    state = DRAGGING

def release_handler(x, y):
    global state

    yertle.onrelease(None)  # disable until click
    yertle.ondrag(None)  # disable competing handler

    yertle.onclick(click_handler)  # watch for click event
    onmove(screen, move_handler)  # dragging is now motion until click

    state = MOVING

def drag_handler(x, y):
    if state != DRAGGING:  # ignore stray events
        return

    yertle.ondrag(None)  # disable event inside event handler
    yertle.pendown()
    yertle.setheading(yertle.towards(x, y))
    yertle.goto(x, y)
    yertle.ondrag(drag_handler)  # reenable event on event handler exit

def onmove(self, fun, add=None):
    """
    Bind fun to mouse-motion event on screen.

    Arguments:
    self -- the singular screen instance
    fun  -- a function with two arguments, the coordinates
        of the mouse cursor on the canvas.

    Example:

    >>> onmove(turtle.Screen(), lambda x, y: print(x, y))
    >>> # Subsequently moving the cursor on the screen will
    >>> # print the cursor position to the console
    >>> screen.onmove(None)
    """

    if fun is None:
        self.cv.unbind('<Motion>')
    else:
        def eventfun(event):
            fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
        self.cv.bind('<Motion>', eventfun, add)

screen = Screen()
screen.setup(500, 600)
screen.screensize(1920, 1080)

yertle = Turtle('turtle')
yertle.speed('fastest')

state = MOVING

# Initially we track the turtle's motion and left button clicks
onmove(screen, move_handler)  # a la screen.onmove(move_handler)
yertle.onclick(click_handler)  # a click will turn motion into drag

screen.mainloop()

The onmove() event implementation is from my answer to Find the cursor's current position in Python turtle, feel free to give it an upvote when you visit. (Just as your drag_handler() is from my answer to Turtle freehand drawing, feel free to give that one an upvote if you haven't already.)

7
  • This implementation works perfectly when my mouse movement is slow in pen down position, but when it is more haphazard ( like someone's writing), there are bugs. Sometimes, the turtle doesn't leave a trail, and sometimes it draws only a straight line between the initial and final positions of the drag. Commented Jun 17, 2017 at 9:33
  • 1
    @TheBeginner, I instrumented the code and found it generated unexpected motion events so I've added an explicit state variable to filter out those strays. It draws a lot cleaner -- if you need it to be perfect, you might look into semaphores and locking.
    – cdlane
    Commented Jun 17, 2017 at 15:47
  • Very nice solution! Thank you.
    – J-L
    Commented Aug 27, 2019 at 16:27
  • Can't figure what # a la screen.onmove(move_handler) should be.
    – Red
    Commented Jul 4, 2020 at 1:16
  • @AnnZen, can't figure out your comment. The onmove() code is written in the form of a method so that although we call it as a function via onmove(screen, move_handler) it can be installed as a method of screen and invoked screen.onmove(move_handler) either in a subclass orvia some other trickery.
    – cdlane
    Commented Jul 4, 2020 at 1:24

Not the answer you're looking for? Browse other questions tagged or ask your own question.