from tkinter import Canvas, Tk
from turtle import TurtleScreen, RawTurtle
from functools import partial
import ctypes
from PIL import ImageGrab
# Utility methods for working with turtle.
# The first method is the only one that is important.
# Feel free to ignore the rest.
# CAC, 2024
def getTurtleAndScreen(title, width, height, filename, moveWorld=True):
'''
Create a screen and a turtle to draw on that screen
:param title: The title you want on the window.
:param width: The width of the window/canvas
:param height: The height of the window/canvas
:param filename: the name of the file that called this. Used to name the image file
if a screenshot is taken.
:param moveWorld: If set to true, the lower left corner will be (0,0) and
the upper right will be (width,height)
Otherwise, the lower left corner will be (-width/2, -height/2), the
center of the screen will be (0,0), and the upper right will be (width/2,height/2)
:return: a turtle and a screen
'''
#----------------------------------------------
# From https://stackoverflow.com/questions/44398075/can-dpi-scaling-be-enabled-disabled-programmatically-on-a-per-session-basis
# to make it not scale if scaling is turned on.
# Set DPI Awareness (Windows 10 and 8)
errorCode = ctypes.windll.shcore.SetProcessDpiAwareness(2)
# Set DPI Awareness (Windows 7 and Vista)
success = ctypes.windll.user32.SetProcessDPIAware()
#----------------------------------------------
root = Tk()
ws = root.winfo_screenwidth()
hs = root.winfo_screenheight()
x = (ws / 2) - (width / 2)
y = (hs / 2) - (height / 2)
root.geometry('%dx%d+%d+%d' % (width, height, x, y))
root.lift()
root.wm_title(title)
canvas = ResizingCanvas(root, desired_width=width, desired_height=height, width=width,
height=height, highlightthickness=0)
canvas.pack(fill="both", expand=True)
screen = TurtleScreen(canvas)
if moveWorld:
screen.setworldcoordinates(0, -1, width, height - 1)
# So you can specify colors using rgb integer values.
screen.colormode(255)
# Do not show the turtle drawing.
screen.tracer(0)
# Get an invisible turtle.
turtle = RawTurtle(screen, visible=False)
params = {}
params['root']=root
params['width']=width
params['height']=height
params['swidth']=ws
params['sheight']=hs
params['title']=title
params['filename']=filename
screen.onscreenclick(partial(myClickHandler, params))
return turtle, screen
def myClickHandler(params,x,y):
params['x']=x
params['y']=y
image = screenshot(params)
filename = params['filename'] if 'filename' in params else "unknown"
filename += ".png"
image.save(filename)
def screenshot(params):
width = int(params['width'])
height = int(params['height'])
root = params['root']
# To take screenshot properly when scaling is happening, I need to use
# this. I have instead added code above that "turns off" scaling.
# so I set the scale_factor to 1. Leaving this in case some machines
# do not do the above correctly.
# sf = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100
sf=1
x0 = root.winfo_rootx()
y0 = root.winfo_rooty()
x1 = x0 + width
y1 = y0 + height
image = ImageGrab.grab().crop((x0*sf, y0*sf, x1*sf, y1*sf))
image = image.resize((width,height))
return image
class ResizingCanvas(Canvas):
def __init__(self, parent, desired_width, desired_height, **kwargs):
Canvas.__init__(self, parent, **kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
self.origHeight = self.height # save original height
self.origWidth = self.width # save original height
# Need to negate this one. Not sure why.
self.x_shift = -(self.width - desired_width) // 2
# Don't need to negate this one. Not sure why.
self.y_shift = (self.height - desired_height) // 2
def on_resize(self, event):
# determine the ratio of old width/height to new width/height
wscale = float(event.width) / self.width
hscale = float(event.height) / self.height
self.width = event.width
self.height = event.height
# resize the canvas
self.config(width=self.width, height=self.height)
# rescale all the objects tagged with the "all" tag
self.scale("all", self.x_shift, self.y_shift - self.origHeight, wscale, hscale)