我开始使用 Tkinter 查看拖放,并根据答案,我在此处生成了上一个问题的更新 2。我还参考了这个答案。
基于这些,我制作了这段显然未完成的代码:
import tkinter as tk
import customtkinter as ctk
import tkinter
from tkinterdnd2 import TkinterDnD, DND_ALL
class CTk(ctk.CTk, TkinterDnD.DnDWrapper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.TkdndVersion = TkinterDnD._require(self)
class DnD():
"""Drag and Drop management class, used to drag one widget over another, target widget.
The target widget then takes on the colours of the dragged
widget."""
payload = ['', '']
def __init__(self, widget, enable_drag=True, enable_drop=True):
self.widget = widget
root.update_idletasks()
self.widget_x_restore = widget.winfo_x()
self.widget_y_restore = widget.winfo_y()
if enable_drag:
self.widget.drag_source_register(1)
self.widget.bind("<Button-1>", self.drag_start)
self.widget.bind("<B1-Motion>", self.drag_motion)
self.widget.bind("<ButtonRelease-1>", self.restore_position)
DnD.payload = [self.widget.cget('fg_color'), self.widget. cget('hover_color')]
if enable_drop:
self.widget.bind("<<Drop>>", self.drop)
def drag_start(self, event):
"""The drag_start method is a callback function bound to a mouse action (Button-1 click)."""
widget = event.widget
widget.startX = event.x
widget.startY = event.y
print(f'Dragging...')
# widget.lift()
def drag_motion(self, event):
"""Callback method, used in binding to mouse pointer motion, causing the dragged widget motion."""
widget = event.widget
x = widget.winfo_x() - event.x
y = widget.winfo_y() - event.y
widget.place(x=x, y=y)
def drop(self, event):
"""Register a widget as a drop target."""
target_button = event.widget # Access the target button
if isinstance(target_button, ctk.CTkButton):
fg_colour, hover_colour = DnD.payload
target_button.configure(fg_color=fg_colour, hover_colour=hover_colour) # Change target button color
def restore_position(self, event):
self.widget.place(x=self.widget_x_restore, y=self.widget_y_restore)
root.update_idletasks() # move top widget (source widget), to access widget below (target widget)
x, y = event.widget.winfo_pointerxy()
target = event.widget.winfo_containing(x, y)
target.event_generate("<<Drop>>")
class App(CTk):
def __init__(self):
super().__init__()
pass
if __name__ == "__main__":
root = App()
root.geometry("600x400+200+100")
button1 = ctk.CTkButton(root,
text="button1",
fg_color="red",
hover_color="red",
width=100,
height=50)
button1.place(x=10, y=100)
button2 = ctk.CTkButton(root,
text="button2",
fg_color="blue",
hover_color="blue",
width=100,
height=50)
button2.place(x=200, y=200)
# Register the buttons for drag and drop.
button1_dnd = DnD(button1)
button2_dnd = DnD(button2)
root.mainloop()
问题是,当我点击并尝试拖动按钮时,按钮不是移动,而是缩小或消失。
有人经历过并解决过这个问题吗?
谢谢。
经过一番摸索和重构后,我想出了一个不利用 tkinterdnd2 的解决方案,但可以使用 CustomTkinter 按钮,这是我的主要要求:
import customtkinter as ctk
class CTkButtonDnD():
"""Facilitate drag and drop of CustomTkinter CTkButton.
Drag a CTkButton over another CTkButton and update the drop target colours,to
those of the dragged widget. When the operation is completed, the dragged
widget is returned to its original position."""
source_fg_color = ''
source_hover_color = ''
def __init__(self, widget, enable_drag=True, enable_drop=True):
self.widget = widget
self.enable_drag = enable_drag
self.enable_drop = enable_drop
print(f'Type of widget: {self.widget}')
self.initial_position = None
if enable_drag:
widget.bind("<Button-1>", self.on_click)
widget.bind("<ButtonRelease-1>", lambda event: self.restore_position(widget))
# self.widget_x_restore, self.widget_y_restore = 0, 0
root.update_idletasks()
self.widget_x_restore = widget.winfo_x()
self.widget_y_restore = widget.winfo_y()
self.pointer_start_x, self.pointer_start_y = 0, 0
if enable_drop:
self.widget.bind("<<Drop>>", self.drop)
self.widget.bind("<Destroy>", self.cleanup)
def cleanup(self, event=None):
"""Cleanup method to be called when the associated CTkButton widget is destroyed."""
self.widget.unbind_all()
def on_click(self, event):
self.pointer_start_x, self.pointer_start_y = event.x, event.y
widget = event.widget.master
print(f'Type of clicked widget: {event.widget}')
print(f'Type of clicked widget parent: {event.widget.master}')
if self.enable_drag:
CTkButtonDnD.source_fg_color = widget.cget('fg_color')
CTkButtonDnD.source_hover_color = widget.cget('hover_color')
self.widget.bind("<B1-Motion>", self.drag_motion)
if self.enable_drop:
self.widget.bind("<ButtonRelease-1>", self.drop)
def drag_motion(self, event):
x = self.widget.winfo_x() + event.x - self.pointer_start_x
y = self.widget.winfo_y() + event.y - self.pointer_start_y
self.widget.lift()
self.widget.place(x=x, y=y)
def drop(self, event):
self.widget.lower()
widget = event.widget
widget.master.configure(fg_color=CTkButtonDnD.source_fg_color, hover_color=CTkButtonDnD.source_hover_color)
def restore_position(self, widget):
widget.lower()
x, y = root.winfo_pointerxy()
target_widget = root.winfo_containing(x, y)
widget.place(x=self.widget_x_restore, y=self.widget_y_restore)
root.update_idletasks()
target_widget.event_generate('<<Drop>>')
def reset_colors():
button1.configure(fg_color="red", hover_color="green")
button2.configure(fg_color="blue", hover_color="black")
root = ctk.CTk()
root.geometry("310x200+200+200")
button1 = ctk.CTkButton(root, fg_color="red", hover_color="green", width=100, height=50)
button1.place(x=10, y=50)
button1_dnd = CTkButtonDnD(button1)
button2 = ctk.CTkButton(root, fg_color="blue", hover_color="black", width=100, height=50)
button2.place(x=200, y=50)
button2_dnd = CTkButtonDnD(button2, True, True)
button_reset = ctk.CTkButton(root, text='Reset', width=100, command=reset_colors)
button_reset.place(x=200, y=160)
root.mainloop()