首页 > 解决方案 > 如何通过 python Tkinter 绘制智能拖放折线?

问题描述

我做了一个类来创建一条由线条组成的多段线,可以拖放。但是有以下两个问题:

  1. 如果有一种聪明的方法可以让折线在我移动时自动绕过对象(例如矩形)?

  2. 如果有任何聪明的方法来选择一条线并改变它的位置但保持所有线连接?

'''

from tkinter import *

class polyline:
    def __init__(self,coords,canvas):
        self.lines_list = []
        self.canvas = canvas
        self.selected = False
        self.x_list,self.y_list = [],[]
        for i in range(len(coords)-1):
            self.lines_list.append(canvas.create_line(coords[i][0],coords[i][1], coords[i+1][0],coords[i+1][1]))
        self.get_diagonal_coords()
    def get_diagonal_coords(self):
        self.x_list,self.y_list = [],[]
        for i in self.lines_list:
            coord = self.canvas.coords(i)
            self.x_list.append(coord[0])
            self.x_list.append(coord[2])
            self.y_list.append(coord[1])
            self.y_list.append(coord[3])
        self.x_min = min(self.x_list)
        self.y_min = min(self.y_list)
        self.x_max = max(self.x_list)
        self.y_max = max(self.y_list)
        # print self.x_min,self.y_min
    def move(self,move_dis):
        self.get_diagonal_coords()
        if self.x_min+move_dis[0] > 10 and self.y_min+move_dis[1] > 10 and self.x_max +move_dis[0]< canvas.winfo_width()-10 and self.y_max +move_dis[1]<canvas.winfo_height()-10:
            for l in self.lines_list:
                self.canvas.move(l,move_dis[0],move_dis[1])
                # print self.x_list
    def goto(self,destination):
        self.get_diagonal_coords()
        move_dis = (destination[0]-self.x_min,destination[1]-self.y_min)
        self.move(move_dis)
        self.get_diagonal_coords()

    def select(self,coord):
        self.get_diagonal_coords()
        if coord[0]>self.x_min and coord[1]>self.y_min and coord[0]<self.x_max and coord[1]<self.y_max:
            self.selected = True
        return False


master = Tk()
canvas = Canvas(master, width=500, height=500)
canvas.pack()
# canvas.create_line(20,200,1,200)
global p
p = polyline([(10,200),(50,200),(50,300),(150,300),(150,360)],canvas)

def onclick_handler(event):
    global p,start
    start = (event.x-p.x_min, event.y-p.y_min)
    pass
    p.select((event.x, event.y))
   
def on_drag_motion(event):
    global p,start
    # print p.selected
    if p.selected:
        p.goto((event.x-start[0], event.y-start[1]))


def onrelease_handler(event):
    global p
    p.selected = False

canvas.bind("<Button-1>", onclick_handler)
canvas.bind("<B1-Motion>", on_drag_motion)
canvas.bind("<ButtonRelease-1>", onrelease_handler)

master.mainloop()

'''

效果演示

标签: pythontkintercanvasdrag-and-drop

解决方案


from tkinter import *

class draw_polyline:
    def __init__(self,canvas,vertical):
        self.lines_list = []
        self.canvas = canvas
        self.vertical = vertical
        self.click_num = 0
        pass

    def start_draw_line(self,event):
        x1,y1,x,y = event.x,event.y,event.x,event.y
        if self.click_num  == 0:
            if self.vertical:
                self.lines_list.append(self.canvas.create_line(x1,y1,x1,y))
            else:
                self.lines_list.append(self.canvas.create_line(x1,y1,x,y1))

        else:
            l = self.lines_list[self.click_num-1]
            coords = self.canvas.coords(l)
            if self.vertical:
                self.lines_list.append(self.canvas.create_line(coords[2],coords[3],coords[2],y+5))
            else:
                self.lines_list.append(self.canvas.create_line(coords[2],coords[3],x+5,coords[3]))

        canvas.bind("<B1-Motion>", self.move_and_draw)
        canvas.bind("<ButtonRelease-1>", self.onrelease_handler)

    def onrelease_handler(self,event):
        self.vertical = not self.vertical
        self.click_num+=1

    def in_the_range(self,l,coord):
        coords = self.canvas.coords(l)
        x_min,x_max = min(coords[0],coords[2]),max(coords[0],coords[2])
        y_min,y_max = min(coords[1],coords[3]),max(coords[1],coords[3])
        x_in_range = coord[0]>x_min-3 and coord[0]<x_max+3
        y_in_range = coord[1]>y_min-3 and coord[1]<y_max+3
        return x_in_range and y_in_range

    def move_and_draw(self,event):
        if len(self.lines_list)>0:
            l = self.lines_list[self.click_num]
            coords = self.canvas.coords(l)
            x1,y1,x,y = coords[0],coords[1],event.x,event.y
            if self.vertical:
                if y-y1<5:
                    y = int(y+5*abs(y-y1)/((y-y1)+1e-9))
                self.canvas.coords(l,x1,y1,x1,y)
            else:
                if x-x1<5:
                    x = int(x+5*abs(x-x1)/((x-x1)+1e-9))
        
                self.canvas.coords(l,x1,y1,x,y1)
            
    def onclick_handler(self,event):
        self.canvas.bind("<Button-1>", self.start_draw_line)
    def start_draw(self):
        self.canvas.bind("<Button-1>", self.start_draw_line)



master = Tk()
canvas = Canvas(master, width=500, height=500)
canvas.pack()
p =  draw_polyline(canvas = canvas,vertical=True)
p.start_draw()
master.mainloop()

推荐阅读