基于使用canvas.create_line绘制的简单直线,例如这条image here,基于直线的起点、中点和终点创建三个点,example here.

我想要能够点击并拖动有点的那部分Line,所以我做了以下MRE:

import tkinter as tk

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

line = canvas.create_line(50, 50, 350, 50, width=3)

start_x, start_y, end_x, end_y = canvas.coords(line)

start_dot = canvas.create_oval(start_x - 5, start_y - 5, start_x + 5, start_y + 5, fill='blue', tags=('start_dot',))
end_dot = canvas.create_oval(end_x - 5, end_y - 5, end_x + 5, end_y + 5, fill='red', tags=('end_dot',))

mid_x = (start_x + end_x) / 2
mid_y = start_y

mid_dot = canvas.create_oval(mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5, fill='green', tags=('mid_dot',))


def update_line(event):
    global end_x, end_y, start_y, start_x, mid_y, mid_x, curve
    x, y = event.x, event.y
    item = canvas.find_withtag(tk.CURRENT)[0]
    if item == start_dot:
        canvas.coords(start_dot, x - 5, y - 5, x + 5, y + 5)
        canvas.coords(line, x, y, end_x, end_y)
    elif item == end_dot:
        canvas.coords(end_dot, x - 5, y - 5, x + 5, y + 5)
        canvas.coords(line, start_x, start_y, x, y)
    elif item == mid_dot:
        dx = x - (start_x + end_x) // 2
        dy = y - (start_y + end_y) // 2
        canvas.coords(mid_dot, mid_x + dx - 5, mid_y + dy - 5, mid_x + dx + 5, mid_y + dy + 5)
        canvas.coords(line, start_x, start_y, mid_x + dx, mid_y + dy, end_x, end_y)
    else:
        return

    test = canvas.coords(line)
    if len(test) == 4:
        start_x, start_y, end_x, end_y, = canvas.coords(line)
        mid_x = (start_x + end_x) / 2
        mid_y = (start_y + end_y) / 2
        canvas.coords(mid_dot, mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5)
    else:
        pass


canvas.tag_bind('start_dot', '<B1-Motion>', update_line)
canvas.tag_bind('end_dot', '<B1-Motion>', update_line)
canvas.tag_bind('mid_dot', '<B1-Motion>', update_line)

root.mainloop()

这在某种程度上是可行的,至少对于仅正确移动开始和结束是这样的.尚不起作用的部分是在移动中点后正确更新直线,然后移动直线的起始点或结束点. Here是显示问题的gif,here是预期行为(可以忽略矩形).

我注意到canvas.coords个输出5值,而不是我习惯的4个值.

我怎样才能让上面的内容与中间路径/点一起工作呢?

附注:我使用了术语"折线",但我不确定这是否是我在这里真正在做的事情(在查看谷歌图片时,它看起来确实很像).如果有的话,请随意提及一个更合适的术语.

推荐答案

多亏了jasonharper's条 comments ,我设法找到了解决这个问题的方法:

首先是不直接遵循所需行为的解决方案(适用于所有三个路径/点,但仅使用起始点和结束点时不会移动整行):

import tkinter as tk

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

line = canvas.create_line(50, 50, 350, 50, width=3)

start_x, start_y, end_x, end_y = canvas.coords(line)

start_dot = canvas.create_oval(start_x - 5, start_y - 5, start_x + 5, start_y + 5, fill='blue', tags=('start_dot',))
end_dot = canvas.create_oval(end_x - 5, end_y - 5, end_x + 5, end_y + 5, fill='red', tags=('end_dot',))

mid_x = (start_x + end_x) / 2
mid_y = start_y

mid_dot = canvas.create_oval(mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5, fill='green', tags=('mid_dot',))

def update_line(event):
    global start_x, start_y, end_x, end_y, mid_x, mid_y

    x, y = event.x, event.y
    item = canvas.find_withtag(tk.CURRENT)[0]

    if item == start_dot:
        start_x, start_y = x, y
    elif item == end_dot:
        end_x, end_y = x, y
    elif item == mid_dot:
        mid_x, mid_y = x, y

    canvas.coords(line, start_x, start_y, mid_x, mid_y, end_x, end_y)

    canvas.coords(start_dot, start_x - 5, start_y - 5, start_x + 5, start_y + 5)
    canvas.coords(end_dot, end_x - 5, end_y - 5, end_x + 5, end_y + 5)
    canvas.coords(mid_dot, mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5)

canvas.tag_bind('start_dot', '<B1-Motion>', update_line)
canvas.tag_bind('end_dot', '<B1-Motion>', update_line)
canvas.tag_bind('mid_dot', '<B1-Motion>', update_line)

root.mainloop()

现在,一种解决方案是先移动整条线(仅使用起始点和结束点),然后在中点至少使用一次后再部分移动:

import tkinter as tk

root = tk.Tk()

canvas = tk.Canvas(root, width=400, height=400)
canvas.pack()

line = canvas.create_line(50, 50, 350, 50, width=3)

start_x, start_y, end_x, end_y = canvas.coords(line)

start_dot = canvas.create_oval(start_x - 5, start_y - 5, start_x + 5, start_y + 5, fill='blue', tags=('start_dot',))
end_dot = canvas.create_oval(end_x - 5, end_y - 5, end_x + 5, end_y + 5, fill='red', tags=('end_dot',))

mid_x = (start_x + end_x) / 2
mid_y = start_y

mid_dot = canvas.create_oval(mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5, fill='green', tags=('mid_dot',))
has_middle_dot_moved = False


def update_line(event):
    global end_x, end_y, start_y, start_x, mid_y, mid_x, has_middle_dot_moved
    x, y = event.x, event.y
    item = canvas.find_withtag(tk.CURRENT)[0]
    if not has_middle_dot_moved:
        if item == start_dot:
            canvas.coords(start_dot, x - 5, y - 5, x + 5, y + 5)
            canvas.coords(line, x, y, end_x, end_y)
        elif item == end_dot:
            canvas.coords(end_dot, x - 5, y - 5, x + 5, y + 5)
            canvas.coords(line, start_x, start_y, x, y)
        elif item == mid_dot:
            has_middle_dot_moved = True
        start_x, start_y, end_x, end_y, = canvas.coords(line)
        mid_x = (start_x + end_x) / 2
        mid_y = (start_y + end_y) / 2
        canvas.coords(mid_dot, mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5)
    else:
        if item == start_dot:
            start_x, start_y = x, y
        elif item == end_dot:
            end_x, end_y = x, y
        elif item == mid_dot:
            mid_x, mid_y = x, y

        canvas.coords(line, start_x, start_y, mid_x, mid_y, end_x, end_y)

        canvas.coords(start_dot, start_x - 5, start_y - 5, start_x + 5, start_y + 5)
        canvas.coords(end_dot, end_x - 5, end_y - 5, end_x + 5, end_y + 5)
        canvas.coords(mid_dot, mid_x - 5, mid_y - 5, mid_x + 5, mid_y + 5)


canvas.tag_bind('start_dot', '<B1-Motion>', update_line)
canvas.tag_bind('end_dot', '<B1-Motion>', update_line)
canvas.tag_bind('mid_dot', '<B1-Motion>', update_line)

root.mainloop()

Python相关问答推荐

如何确保Flask应用程序管理面板中的项目具有单击删除功能?

只需使用Python在图像中保留 colored颜色 范围区域

从包含基本数据描述的文本字段中识别和检索特定字符序列

使用itertools出现第n个子串

根据多列和一些条件创建新列

PyTorch卷积自动编码器,输出维度与输入不同

基本链合同的地址是如何计算的?

分组数据并删除重复数据

从webhook中的短代码(而不是电话号码)接收Twilio消息

根据在同一数据框中的查找向数据框添加值

Gekko:Spring-Mass系统的参数识别

带条件计算最小值

ModuleNotFound错误:没有名为flags.State的模块; flags不是包

如何制作10,000年及以后的日期时间对象?

C#使用程序从Python中执行Exec文件

把一个pandas文件夹从juyter笔记本放到堆栈溢出问题中的最快方法?

如何在Python中找到线性依赖mod 2

名为__main__. py的Python模块在导入时不运行'

python中csv. Dictreader. fieldname的类型是什么?'

Python Pandas—时间序列—时间戳缺失时间精确在00:00