Turtle in Tkinter creating multiple windows - python

I am attempting to create a quick turtle display using Tkinter, but some odd things are happening.
First two turtle windows are being created, (one blank, one with the turtles), secondly, any attempt of turning the tracer off is not working.
This might be a simple fix but at the moment I cannot find it.
Any help would be appreciated,
Below is the code:
import tkinter as tk
import turtle
window = tk.Tk()
window.title('Top 10\'s')
def loadingscreen():
canvas = tk.Canvas(master = window, width = 500, height = 500)
canvas.pack()
arc1 = turtle.RawTurtle(canvas)
arc2 = turtle.RawTurtle(canvas)
#clean up the turtles and release the window
def cleanup():
turtle.tracer(True)
arc1.ht()
arc2.ht()
turtle.done()
#animate the turtles
def moveTurtles(rangevar,radius,extent,decrease):
for distance in range(rangevar):
arc1.circle(-radius,extent = extent)
arc2.circle(-radius,extent = extent)
radius -= decrease
#Set the turtle
def setTurtle(turt,x,y,heading,pensize,color):
turt.pu()
turt.goto(x,y)
turt.pd()
turt.seth(heading)
turt.pensize(pensize)
turt.pencolor(color)
#draw on the canvas
def draw():
#set variables
rangevar = 200
radius = 200
decrease = 1
extent = 2
#setup and draw the outline
turtle.tracer(False)
setTurtle(arc1,0,200,0,40,'grey')
setTurtle(arc2,14,-165,180,40,'grey')
moveTurtles(rangevar,radius,extent,decrease)
#setup and animate the logo
turtle.tracer(True)
setTurtle(arc1,0,200,0,20,'black')
setTurtle(arc2,14,-165,180,20,'black')
moveTurtles(rangevar,radius,extent,decrease)
#main program
def main():
turtle.tracer(False)
arc1.speed(0)
arc2.speed(0)
draw()
cleanup()
if __name__ == "__main__":
try:
main()
except:
print("An error occurred!!")
loadingscreen()
Essentially I am creating a Tk window, then a canvas, then two turtles, and then animating these turtles

My guess is you're trying to call turtle screen methods without actually having a turtle screen. When turtle is embedded in tkinter like this, you can overlay a Canvas with a TurtleScreen instance which will provide some, but not all, of the screen features of the standalone turtle:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen
def cleanup():
""" hide the turtles """
arc1.hideturtle()
arc2.hideturtle()
def moveTurtles(rangevar, radius, extent, decrease):
""" animate the turtles """
for _ in range(rangevar):
arc1.circle(-radius, extent=extent)
arc2.circle(-radius, extent=extent)
radius -= decrease
def setTurtle(turtle, x, y, heading, pensize, color):
turtle.penup()
turtle.goto(x, y)
turtle.pendown()
turtle.setheading(heading)
turtle.pensize(pensize)
turtle.pencolor(color)
def draw():
# set variables
rangevar = 200
radius = 200
decrease = 1
extent = 2
screen.tracer(False) # turn off animation while drawing outline
# setup and draw the outline
setTurtle(arc1, 0, 200, 0, 40, 'grey')
setTurtle(arc2, 14, -165, 180, 40, 'grey')
moveTurtles(rangevar, radius, extent, decrease)
screen.tracer(True) # turn animation back on for the following
# setup and animate the logo
setTurtle(arc1, 0, 200, 0, 20, 'black')
setTurtle(arc2, 14, -165, 180, 20, 'black')
moveTurtles(rangevar, radius, extent, decrease)
# main program
window = tk.Tk()
window.title("Top 10's")
canvas = tk.Canvas(master=window, width=500, height=500)
canvas.pack()
screen = TurtleScreen(canvas)
arc1 = RawTurtle(screen)
arc1.speed('fastest')
arc2 = RawTurtle(screen)
arc2.speed('fastest')
draw()
cleanup()
Another suggestion: don't mess with tracer() until after everything else is working and then (re)read it's documentation carefully.

I just have the answer for the two windows that are being created: the one with the turtles is obvious, and the blank one is for the main root window that you define (window = tk.Tk()). If you want it not to appear at all, you can add the following line right after its definition:
window.withdraw()
I found this solution here and here.

Related

How to set turtle tracer false using tkinter?

I have to generate two turtle windows and draw in each one, so I'm using tkinter to create and show the windows. My code currently opens the right screen and draws in it, but the turtle is really slow so I want to set the turtle tracer to false to use the update function, but I can't figure out how to.
This is my turtle_interpreter.py file, which has all the functions I use to draw the L-system:
import turtle
from tkinter import *
class Window(Tk):
def __init__(self, title, geometry):
super().__init__()
self.running = True
self.geometry(geometry)
self.title(title)
self.protocol("WM_DELETE_WINDOW", self.destroy_window)
self.canvas = Canvas(self)
self.canvas.pack(side=LEFT, expand=True, fill=BOTH)
self.turtle = turtle.RawTurtle(turtle.TurtleScreen(self.canvas))
def update_window(self):
'''
sets window to update
'''
if self.running:
self.update()
def destroy_window(self):
'''
sets window to close
'''
self.running = False
self.destroy()
def drawString(turt, dstring, distance, angle):
'''Interpret the characters in string dstring as a series
of turtle commands. Distance specifies the distance
to travel for each forward command. Angle specifies the
angle (in degrees) for each right or left command. The list
of turtle supported turtle commands is:
F : forward
- : turn right
+ : turn left
'''
for char in dstring:
if char == 'F':
turt.forward(distance)
elif char == '-':
turt.right(angle)
elif char == '+':
turt.left(angle)
def place(turt, xpos, ypos, angle=None):
'''
places turtle at given coordinates and angle
'''
turt.penup()
turt.goto(xpos, ypos)
if angle != None:
turt.setheading(angle)
turt.pendown()
def goto(turt, xpos, ypos):
'''
moves turtle to given coordinates
'''
turt.penup()
turt.goto(xpos, ypos)
turt.pendown()
def setColor(turt, color):
'''
sets turtle color
'''
turt.color(color)
And this is the file where the functions get called. Running it draws the L-system.
import turtle_interpreter as turt_int
import lsystem_scene_three as lsystem
def turtle_scene_two():
'''
generates scene two
'''
# create window
win_two = turt_int.Window('Turtle Scene 2', '640x480+650+0')
# assign turtle
turt2 = win_two.turtle
# lsystem setup
lsystemFile = lsystem.Lsystem('lsystem_scene_two.txt')
tstr = lsystemFile.buildString(4)
# draw stuff
turt_int.setColor(turt2, (0, 0, 0))
turt_int.place(turt2, 0, -200, 90)
turt_int.drawString(turt2, tstr, 4, 90)
# update window (loop)
while win_two.running:
win_two.update_window()
turtle_scene_two()
Hope this makes sense. Let me know if it doesn't.
Appreciate your help!
Tried a few things but nothing was promising. Calling turtle generates another screen (which I don't want).
Since you didn't provide all your code, I can't test this, so I'm guessing a good start would be changing this:
self.turtle = turtle.RawTurtle(turtle.TurtleScreen(self.canvas))
to something like:
screen = turtle.TurtleScreen(self.canvas)
screen.tracer(False)
self.turtle = turtle.RawTurtle(screen)

Python Turtle Objects in conjuction with tkinter is it possible?

I'm having an issue when I run my main.py, it shows two screens. I know it's because I'm calling an object, just not sure if it's possible to use objects and have it show up on the tkinter window.
main.py
import tkinter as tk
from turtle import TurtleScreen
from createDotTest import Dot
top = tk.Tk()
canvas = tk.Canvas(top, width=600, height=600)
canvas.pack()
screen = TurtleScreen(canvas)
screen.tracer(0)
list_of_points = []
list_of_lines = []
for x in range(100):
dot = Dot()
list_of_points.append(dot)
screen.mainloop()
createDotTest.py
from turtle import Turtle
import random
class Dot(Turtle):
def __init__(self):
super().__init__()
x_pos = random.randint(-280, 280)
y_pos = random.randint(-280, 280)
self.shapesize(.2,.2)
self.penup()
self.shape('circle')
self.goto(x_pos, y_pos)
self.stamp()
I tried also replacing Turtle as TurtleRaw in the createDotTest.py but I get
raise TurtleGraphicsError("bad canvas argument %s" % canvas)
turtle.TurtleGraphicsError: bad canvas argument None
This question looks similar to Combine tkinter and turtle
I am not sure but this may help you

(Tkinter) How run an animation after put the object?

I'm trying to learn tkinter, and I've found an animation of a ball (in this website).
I tried to modificate it to a ball that could be placed with the mouse. Until now I've done that first part, but I don't know how to add the movement. I suposse that the canvas need to be run before the code of the animation to obtain the ball coordinates.
import tkinter
import time
animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01
def animate_ball(window, canvas, xinc, yinc, ball):
while True:
canvas.move(ball,xinc,yinc)
window.update()
time.sleep(animation_refresh_seconds)
ball_pos = canvas.coords(ball)
xl,yl,xr,yr = ball_pos
if xl < abs(xinc) or xr > animation_window_width-abs(xinc):
xinc = -xinc
if yl < abs(yinc) or yr > animation_window_height-abs(yinc):
yinc = -yinc
def position_ball(event):
ball = canvas.create_oval(event.x-animation_ball_radius,
event.y-animation_ball_radius,
event.x+animation_ball_radius,
event.y+animation_ball_radius,
fill="blue", outline="white", width=4)
window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball=canvas.bind("<Button-1>", position_ball)
ball
#animate_ball(window, canvas, animation_ball_min_movement, animation_ball_min_movement, ball)
That is a really bad example. For most GUIs you should never use an infinite loop with sleep() in a GUI thread since it blocks GUI mainloop (pygame is a notable exception to this). Most GUIs, including tkinter, are "event-driven", and you need to use events to do things. The event I think you want is the mouse movement. The event will contain the mouse x, y position, so all that's left is to transfer that to the ball.
import tkinter
animation_window_width=800
animation_window_height=600
animation_ball_radius = 30
animation_ball_min_movement = 5
animation_refresh_seconds = 0.01
def place_ball(event):
canvas.unbind("<Motion>") # stop responding to motion
canvas.xinc = animation_ball_min_movement
canvas.yinc = animation_ball_min_movement
animate_ball()
def locate_ball(event):
canvas.coords(ball,
event.x-animation_ball_radius,
event.y-animation_ball_radius,
event.x+animation_ball_radius,
event.y+animation_ball_radius)
# Create and animate ball in an infinite loop
def animate_ball(event=None):
canvas.move(ball,canvas.xinc,canvas.yinc) # move the ball
xl,yl,xr,yr = canvas.coords(ball) # get current coordinates
if xl < abs(canvas.xinc) or xr > animation_window_width-abs(canvas.xinc):
canvas.xinc = -canvas.xinc
if yl < abs(canvas.yinc) or yr > animation_window_height-abs(canvas.yinc):
canvas.yinc = -canvas.yinc
canvas.coords(ball, xl,yl,xr,yr) # set new coordinates
canvas.after(20, animate_ball) # set the loop event
window = tkinter.Tk()
window.title("Tkinter Animation Demo")
window.geometry(f'{animation_window_width}x{animation_window_height}')
canvas = tkinter.Canvas(window)
canvas.configure(bg="black")
canvas.pack(fill="both", expand=True)
ball = canvas.create_oval(0,0,0,0,fill="blue", outline="white", width=4)
canvas.bind('<Motion>', locate_ball)
canvas.bind('<Button-1>', place_ball)
window.mainloop()

How do I get mouse position with tkinter and turtle?

I am trying to make a program that lets me draw on a tkinter window using turtle. For some reason I cannot get the absolute mouse coordinates.
I have done root.winfo_pointerx() - root.winfo_rootx() (and vrootx).
I have also tried:
def mousePos(event):
x,y = event.x , event.y
return x,y
My code:
import turtle
import tkinter as tk
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500,height=500)
cv.focus_set()
cv.pack(side = tk.LEFT)
pen = turtle.RawTurtle(cv)
window = pen.getscreen()
def main():
window.setworldcoordinates(-500,-500,500,500)
window.bgcolor("white")
frame = tk.Frame(root)
frame.pack(side = tk.RIGHT,fill=tk.BOTH)
pointLabel = tk.Label(frame,text="Width")
pointLabel.pack()
def getPosition(event):
x = root.winfo_pointerx()-root.winfo_vrootx()
y = root.winfo_pointery()-root.winfo_vrooty()
pen.goto(x,y)
cv.bind("<Motion>", getPosition)
cv.pack
tk.mainloop()
pass
I want the cursor to be on top of the arrow, but instead it is always to the right and down. Also, when I move the mouse up, the arrow moves down, and vice versa.
Think hard about how you set the setworldcoordinate(). -500 - 500 means your world has 1,000 in size and window size is 500. Also, the mouse pointer offset from the window root - both absolute coordinates should be used. You mixed up the absolute coordinates - mouse pointer and vrootx which is in different scale so the distance of two makes no sense. Following code is probably closer to what you intended. Note that, I set the world coordinate to match the absolute coordinates of mouse pointer offset from the top/left corner of window.
import turtle
import tkinter as tk
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500,height=500)
cv.focus_set()
cv.pack(side = tk.LEFT)
pen = turtle.RawTurtle(cv)
window = pen.getscreen()
def main():
window.setworldcoordinates(0,500,500,0)
window.bgcolor("white")
frame = tk.Frame(root)
frame.pack(side = tk.RIGHT,fill=tk.BOTH)
pointLabel = tk.Label(frame,text="Width")
pointLabel.pack()
print(dir(root))
def getPosition(event):
x = root.winfo_pointerx()-root.winfo_rootx()
y = root.winfo_pointery()-root.winfo_rooty()
print(x, y)
pen.goto(x,y)
pass
cv.bind("<Motion>", getPosition)
cv.pack
tk.mainloop()
pass
if __name__ == "__main__":
main()
pass
You've got an issue working against you that isn't of your own making. The general rule is when in a turtle canvas, use turtle methods. But turtle doesn't have an inherent 'Motion' event type, so you were trying to use the raw Canvas one as a substitute. Thus the conflict.
An issue of your own making is that when you're inside a fast moving event handler, you need to disable the event hander as the first thing you do, reenabling on exit. Otherwise, events overlap and bad things happen. (Inadvertant recursions and other wierdness.)
I've rewritten your program below to work as I believe you intended. The fix is adding the missing turtle method so we can stay within the turtle domain:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen
from functools import partial
def onscreenmove(self, fun, add=None): # method missing from turtle.py
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)
def getPosition(x, y):
screen.onscreenmove(None) # disable events inside handler
pen.setheading(pen.towards(x, y))
pen.goto(x, y)
screen.onscreenmove(getPosition) # reenable handler on exit
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500, height=500)
cv.focus_set()
cv.pack(side=tk.LEFT)
screen = TurtleScreen(cv)
screen.onscreenmove = partial(onscreenmove, screen) # install missing method
pen = RawTurtle(screen)
frame = tk.Frame(root)
frame.pack(side=tk.RIGHT, fill=tk.BOTH)
tk.Label(frame, text="Width").pack()
screen.onscreenmove(getPosition)
screen.mainloop()
Mouse position for Tkinter:
import Tkinter as tk
root = tk.Tk()
def motion(event):
x, y = event.x, event.y
print('{}, {}'.format(x, y))
root.bind('<Motion>', motion)
root.mainloop()
Mouse position for turtle:
canvas = turtle.getcanvas()
x, y = canvas.winfo_pointerx(), canvas.winfo_pointery()
Hope this helps.

Turtle freehand drawing

I'm working on a simple drawing program that combines Tkinter and Turtle modules.
I would like to add an option that the user can draw anything by just using mouse similar to pen widget on Paint. I tried many things, I could not figure out how l can do it.How can l make the turtle draw anything (like pen widget on Paint ) on canvas by using mouse
from tkinter import *
import turtle
sc=Tk()
sc.geometry("1000x1000+100+100")
fr4=Frame(sc,height=500,width=600,bd=4,bg="light green",takefocus="",relief=SUNKEN)
fr4.grid(row=2,column=2,sticky=(N,E,W,S))
#Canvas
canvas = Canvas(fr4,width=750, height=750)
canvas.pack()
#Turtle
turtle1=turtle.RawTurtle(canvas)
turtle1.color("blue")
turtle1.shape("turtle")
points=[]
spline=0
tag1="theline"
def point(event):
canvas.create_oval(event.x, event.y, event.x+1, event.y+1, fill="red")
points.append(event.x)
points.append(event.y)
return points
def canxy(event):
print (event.x, event.y)
def graph(event):
global theline
canvas.create_line(points, tags="theline")
def toggle(event):
global spline
if spline == 0:
canvas.itemconfigure(tag1, smooth=1)
spline = 1
elif spline == 1:
canvas.itemconfigure(tag1, smooth=0)
spline = 0
return spline
canvas.bind("<Button-1>", point)
canvas.bind("<Button-3>", graph)
canvas.bind("<Button-2>", toggle)
sc.mainloop()
The following code will let you freehand draw with the turtle. You'll need to integrate with the rest of your 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=750, height=750)
canvas.pack()
# Turtle
turtle1 = turtle.RawTurtle(canvas)
turtle1.color("blue")
turtle1.shape("turtle")
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()

Categories

Resources