I'm wanting to click two spots on an image/window, draw a horizontal line for each, both of which are movable independently, and then use the two points/lines to get their Y position.
The way I'm thinking about this is currently to just draw two lines that are draggable during creation. Once both lines are drawn no more lines should be drawable. It should also be possible to click one line or the other to drag and modify its location. Then I simply get the y coordinate off each one using their tag name once a confirm button is pressed.
So far I can get lines draggable on creation, but not afterward.
lines = []
def click(e):
lines.append(canvas.create_line(0, e.y, width, e.y))
def drag(e):
canvas.coords(lines[-1], 0, e.y, width, e.y,)
canvas.bind("<ButtonPress-1>", click)
canvas.bind("<B1-Motion>", drag)
This allows you to create and drag as many lines as you want and none are moveable after creation. I was playing around with keeping track of the index and only using index 0, 1, but couldn't get that to work out.
I can also make it where premade lines with tags are draggable with this
canvas.create_line(0, 150, width, 150, tag="first_line")
canvas.create_line(0, 300, width, 300, tag="second_line")
def find_shape(e):
item = canvas.find_closest(e.x, e.y)
tags = canvas.gettags(item)
print(tags[0])
canvas.bind("<B1-Motion>", lambda event, arg=tags[0]: drag(event, arg))
def drag(e, tag):
canvas.coords(tag, 0, e.y, width, e.y)
canvas.bind("<ButtonPress-1>", find_shape)
def print_y():
print(f"line1 y: {canvas.coords(canvas.find_withtag('first_line'))[1]}")
button = tk.Button(text="print tag y coord", width=30, height=3,
command=print_y)
button.pack()
My big problem comes with trying to combine them. I'm not sure how to keep track of if the first line has been created or not, and how to avoid the user from creating more than 2 lines. Also dealing with the overlapping <ButtonPress-1> prompts is confusing me a bit.
CodePudding user response:
This code uses canvas.move for nice object movement control.
The select function enables line drawing via key-d and limits total number of objects.
The work function performs line drawing and object movement.
In draw mode, mouse key 1 will draw line segments while mouse key 3 will finish drawing.
In movement mode, mouse key 1 will cut canvas object (pick up) and mouse key 3 will paste object.
I've inserted remarks into the code that help explain the various actions.
import tkinter as tk
root = tk.Tk()
labelframe = tk.LabelFrame(root, labelanchor = tk.S, text = '0|0')
labelframe.grid(sticky = tk.NSEW)
canvas = tk.Canvas(labelframe, width = 640, height = 480)
canvas.grid(sticky = tk.NSEW)
# Variables
item = xd = yd = 0
# Preset to draw
flag = True
def select(event):
global flag
# limit number of canvas objects or allow object editing
flag = (len(canvas.find_all()) < 2 or item)
def work(event):
global item, xd, yd, flag
# capture mouse pointer position in canvas
xc, yc = canvas.canvasx(event.x), canvas.canvasx(event.y)
if not flag:
# 5 = mouse button release
if int(event.type) == 5:
# cut and move object
if event.num == 1:
item = canvas.find_closest(xc, yc)
if item:
item = item[0]
else:
item = 0
# paste object
elif event.num == 3:
item = 0
if item:
# Use previous and current mouse positions to move item
canvas.move(item, xc - xd, yc - yd)
elif item:
# draw line
bbox = canvas.coords(item)
if int(event.type) == 5:
# mouse button 1 release
if event.num == 1:
bbox.extend([xc, yc])
canvas.coords(item, *bbox)
# line drawing complete
elif event.num == 3:
flag = False
else:
# mouse movement controls line display
canvas.coords(item, bbox[:~1] [xc, yc])
# Make new line object
elif int(event.type) == 5:
if event.num == 1:
item = canvas.create_line(
xd, yd, xc, yc, fill = 'black', tag = 'piece')
labelframe['text'] = f'{xc} | {yc}'
# keep track of previous and current mouse positions
xd, yd = xc, yc
# Press 'd' key to Draw
canvas.bind('<KeyRelease-d>', select)
# handle many inputs via event_add
canvas.event_add('<<PICK>>', '<Motion>', '<ButtonPress>', '<ButtonRelease>')
canvas.bind('<<PICK>>', work)
# make sure canvas has the focus
canvas.focus_force()
root.mainloop()
CodePudding user response:
You can check the size of lines to determine whether new line should be created. Also use a global variable selected to refer to the current selected line:
lines = []
selected = None # item ID of selected line
def click(e):
global selected
if len(lines) < 2:
# create new line
selected = canvas.create_line(0, e.y, width, e.y)
lines.append(selected)
else:
# check whether a line is under current mouse position
selected = canvas.find_withtag(tk.CURRENT)
def drag(e):
if selected:
canvas.coords(selected, 0, e.y, width, e.y)
