用Tkinter打造GUI开发工具(46)在Tkinter中实现控件拖拽功能
Python已经升级到Python 3.8版本了,Tkinter库仍然是自带GUI开发库。
原生tkinter.dnd模块就提供了控件拖拽的演示,这个是不同tk窗口中拖拽控件的演示。
在C:\Python\Lib\tkinter目录中可以找到dnd.py这个文件,原始文件读者自己运行尝试。
如果在一个窗口中的不同区域怎么移动控件呢?
因此我在tkinter.dnd模块进一步封装,可以实现不同区域或不同窗口中的控件拖动。
我们使用HP_tk2的View控件,把窗口划分为四个区域,在这四个区域中,做演示。
下面我直接给出源代码。
import tkinter as tk #导入Tkinter
import tkinter.dnd as dnd
import HP_tk2 as htk
#独狼荷蒲qq:2886002
#通通小白python量化群:524949939
#微信公众号:独狼股票分析
class dndIcon:
def __init__(self, idname = None):
self.idname = idname
self.canvas = None
self.label = None
self.id = None
def attach(self, canvas, x=10, y=10):
if canvas is self.canvas:
self.canvas.coords(self.id, x, y)
return
if self.canvas:
self.detach()
if not canvas:
return
id = canvas.create_window(x, y, window=self.idname, anchor="nw")
self.canvas = canvas
self.id = id
self.idname.bind("" , self.press)
def detach(self):
canvas = self.canvas
if not canvas:
return
id = self.id
label = self.label
self.canvas = self.label = self.id = None
canvas.delete(id)
self.idname.place_forget()
def press(self, event):
if dnd.dnd_start(self, event):
# where the pointer is relative to the label widget:
self.x_off = event.x
self.y_off = event.y
# where the widget is relative to the canvas:
self.x_orig, self.y_orig = self.canvas.coords(self.id)
def move(self, event):
x, y = self.where(self.canvas, event)
self.canvas.coords(self.id, x, y)
def putback(self):
self.canvas.coords(self.id, self.x_orig, self.y_orig)
def where(self, canvas, event):
# where the corner of the canvas is relative to the screen:
x_org = canvas.winfo_rootx()
y_org = canvas.winfo_rooty()
# where the pointer is relative to the canvas widget:
x = event.x_root - x_org
y = event.y_root - y_org
# compensate for initial pointer offset
return x - self.x_off, y - self.y_off
def dnd_end(self, target, event):
pass
#可拖拽控件的控件
class dndwidget(tk.Frame):
def __init__(self, master,**kw):
tk.Frame.__init__(self, master,**kw)
self.top = master
self.canvas = tk.Canvas(self.top, width=100, height=100,**kw)
self.canvas.pack(fill="both", expand=1)
self.canvas.dnd_accept = self.dnd_accept
def dnd_accept(self, source, event):
return self
def dnd_enter(self, source, event):
self.canvas.focus_set() # Show highlight border
x, y = source.where(self.canvas, event)
x1, y1, x2, y2 = source.canvas.bbox(source.id)
dx, dy = x2-x1, y2-y1
self.dndid = self.canvas.create_rectangle(x, y, x+dx, y+dy)
self.dnd_motion(source, event)
def dnd_motion(self, source, event):
x, y = source.where(self.canvas, event)
x1, y1, x2, y2 = self.canvas.bbox(self.dndid)
self.canvas.move(self.dndid, x-x1, y-y1)
def dnd_leave(self, source, event):
self.top.focus_set() # Hide highlight border
self.canvas.delete(self.dndid)
self.dndid = None
def dnd_commit(self, source, event):
self.dnd_leave(source, event)
x, y = source.where(self.canvas, event)
source.attach(self.canvas, x, y)
def test2():
root = tk.Tk()
root.geometry('{}x{}+{}+{}'.format(800,600, 250, 350)) #改变窗口位置和大小
root.title('dnd拖拽演示')
v=htk.View(root,kind='田')
v.pack()
t1 = dndwidget(v.v[0])
t2 = dndwidget(v.v[1],bg='green')
t3 = dndwidget(v.v[2],bg='yellow')
t4 = dndwidget(v.v[3],bg='blue')
i1 = dnd.Icon("ICON1")
i2 = dnd.Icon("ICON2")
i3 = dnd.Icon("ICON3")
bt=tk.Button(v.v[3], text="Quit")
i4 = dndIcon(bt)
i1.attach(t1.canvas)
i2.attach(t2.canvas)
i3.attach(t3.canvas)
i4.attach(t4.canvas)
root.mainloop()
if __name__ == '__main__':
test2()