I am using Tkinter to import images with Openslide. I would like to integrate a manual annotation module into my program like this:
class ResizableCanvas(Canvas):
def __init__(self, parent, **kwargs):
Canvas.__init__(self, parent, **kwargs)
self.bind("<Configure>", self.on_resize)
self.height = self.winfo_reqheight()
self.width = self.winfo_reqwidth()
def on_resize(self, event):
wscale = float(event.width) / self.width
hscale = float(event.height) / self.height
self.width = event.width
self.height = event.height
self.config(width=self.width, height=self.height)
class ViewerTab:
def __init__(self, master, model, dim=800):
self.sideFrame = ttk.Frame(self.master, width=100)
self.coords = {"x":0,"y":0,"x2":0,"y2":0}
self.lines = []
def click(self):
self.coords["x"] = self.x
self.coords["y"] = self.y
self.lines.append(self.canvas.create_line(self.coords["x"],self.coords["y"],self.coords["x"],self.coords["y"]))
def drag(self):
# update the coordinates from the event
self.coords["x2"] = self.x
self.coords["y2"] = self.y
self.canvas.coords(self.lines[-1], self.coords["x"],self.coords["y"],self.coords["x2"],self.coords["y2"])
#self.canvas.bind("<Button-1>", self.dirbutton)
#self.canvas.bind("<B1-Motion>", self.move)
self.canvas.bind("<ButtonRelease-1>", self.nomove)
self.canvas.bind("<Button-2>", self.get_position)
self.canvas.bind("<ButtonPress-1>", self.click)
self.canvas.bind("<B1-Motion>", self.drag)
So if I got it right from the comments, the issue is to be able to both pan the slide and draw on it using binding to mouse clicks and motion. There are several way to do that, for instance:
Use radiobuttons so that the user selects the "mode": either pan or annotate. Here is a small example based on https://stackoverflow.com/a/50129744/6415268 for the drawing part. The click()
and drag()
functions do different actions depending on the selected mode (stored a the StringVar
).
import tkinter as tk
coords = {"x": 0, "y": 0, "x2": 0, "y2": 0}
# keep a reference to all lines by keeping them in a list
lines = []
def click(event):
if mode.get() == "pan":
canvas.scan_mark(event.x, event.y)
else:
# define start point for line
coords["x"] = canvas.canvasx(event.x)
coords["y"] = canvas.canvasy(event.y)
# create a line on this point and store it in the list
lines.append(canvas.create_line(coords["x"], coords["y"], coords["x"], coords["y"]))
def drag(event):
if mode.get() == "pan":
canvas.scan_dragto(event.x, event.y, gain=1)
else:
# update the coordinates from the event
coords["x2"] = canvas.canvasx(event.x)
coords["y2"] = canvas.canvasy(event.y)
# Change the coordinates of the last created line to the new coordinates
canvas.coords(lines[-1], coords["x"], coords["y"], coords["x2"], coords["y2"])
root = tk.Tk()
mode = tk.StringVar(root, "pan")
toolbar = tk.Frame(root)
toolbar.pack(fill='x')
tk.Radiobutton(toolbar, text="Pan",
variable=mode, value="pan").pack(side='left')
tk.Radiobutton(toolbar, text="Annotate",
variable=mode, value="annotate").pack(side='left')
canvas = tk.Canvas(root, bg="white")
canvas.create_rectangle(0, 0, 50, 50, fill='red')
canvas.create_rectangle(400, 400, 450, 450, fill='blue')
canvas.pack(fill='both')
canvas.bind("<ButtonPress-1>", click)
canvas.bind("<B1-Motion>", drag)
root.mainloop()
Another possibility is to use different bindings for the two kinds of actions using event modifiers (e.g. pressing the Ctrl, Shift or Alt key). For instance, the panning can be bound to Ctrl + mouse events while the drawing happens on simple mouse clicks and motion.
import tkinter as tk
coords = {"x": 0, "y": 0, "x2": 0, "y2": 0}
# keep a reference to all lines by keeping them in a list
lines = []
def draw_click(event):
# define start point for line
coords["x"] = canvas.canvasx(event.x)
coords["y"] = canvas.canvasy(event.y)
# create a line on this point and store it in the list
lines.append(canvas.create_line(coords["x"], coords["y"], coords["x"], coords["y"]))
def draw_drag(event):
# update the coordinates from the event
coords["x2"] = canvas.canvasx(event.x)
coords["y2"] = canvas.canvasy(event.y)
# Change the coordinates of the last created line to the new coordinates
canvas.coords(lines[-1], coords["x"], coords["y"], coords["x2"], coords["y2"])
root = tk.Tk()
toolbar = tk.Frame(root)
toolbar.pack(fill='x')
canvas = tk.Canvas(root, bg="white")
canvas.create_rectangle(0, 0, 50, 50, fill='red')
canvas.create_rectangle(400, 400, 450, 450, fill='blue')
canvas.pack(fill='both')
canvas.bind("<ButtonPress-1>", draw_click)
canvas.bind("<B1-Motion>", draw_drag)
canvas.bind('<Control-ButtonPress-1>', lambda event: canvas.scan_mark(event.x, event.y))
canvas.bind("<Control-B1-Motion>", lambda event: canvas.scan_dragto(event.x, event.y, gain=1))
root.mainloop()