Anonymous
Как перетаскивать элементы между кадрами в tkinter?
Сообщение
Anonymous » 15 дек 2024, 20:10
У меня есть графический интерфейс tkinter, который отображает некоторые узлы на уровнях.
Уровни разделены на фреймы, и в каждом фрейме узлы размещаются в соответствии со словарем уровней, определенным в классе Viewer.
Я хочу перетаскивать эти узлы с одного уровня на другой, и, как я это делаю, уровни должны корректироваться сами.
Например, если на уровне есть только один узел, и этот узел перемещается вверх тогда этот уровень практически перестанет существовать, а нижние уровни должен двигаться вверх, чтобы заполнить логику иерархии уровней.
Однако перетаскивание не работает должным образом.
Ниже приведен мой код:
Некоторые примечания о коде:
level_dict в Viewer определяет уровни. Ключ DEPS обозначает зависимость одного узла от другого.
Класс DragDrop реализует функцию перетаскивания при нажатии.
Код: Выделить всё
import tkinter as tk
from tkinter import ttk, filedialog
import os
class DragDrop(tk.Label):
def __init__(self, parent, text, app, **kwargs):
super().__init__(parent, text=text, **kwargs)
self.parent = parent
self.app = app
self.text = text
self.bind("", self.on_click)
self.bind("", self.on_drag)
self.bind("", self.on_release)
self._drag_data = {"x": 0, "y": 0, "item": None}
def on_click(self, event):
self._drag_data["item"] = self
self._drag_data["x"] = event.x
self._drag_data["y"] = event.y
def on_drag(self, event):
x = self.winfo_x() - self._drag_data["x"] + event.x
y = self.winfo_y() - self._drag_data["y"] + event.y
self.place(x=x, y=y)
def on_release(self, event):
self._drag_data = {"x": 0, "y": 0, "item": None}
self.app.update_node_position(self)
class Viewer:
def __init__(self, root):
self.root = root
self.level_dict = {'JK': 0, 'pun': 1, 'utp': 1, 'pun utp': 0, 'utk': 1, 'gjr': 2, 'wbk': 3, 'nest': 4, 'mahm': 5, 'ksl': 6, 'krtk': 5}
self.sections = {'JK': {'DEPS': None, 'TYPES': None, 'RATING': '0'}, 'pun': {'DEPS': 'JK', 'TYPES': None, 'RATING': '0'}, 'utp': {'DEPS': 'JK', 'TYPES': None, 'RATING': '0'}, 'utk': {'DEPS': 'pun utp', 'TYPES': None, 'RATING': '0'}, 'gjr': {'DEPS': 'utk', 'TYPES': None, 'RATING': '0'}, 'wbk': {'DEPS': 'gjr', 'TYPES': None, 'RATING': '0'}, 'nest': {'DEPS': 'wbk', 'TYPES': None, 'RATING': '0'}, 'mahm': {'DEPS': 'nest', 'TYPES': None, 'RATING': '0'}, 'ksl': {'DEPS': 'mahm', 'TYPES': None, 'RATING': '0'}, 'krtk': {'DEPS': 'nest', 'TYPES': None, 'RATING': '0'}}
self.canvas = tk.Canvas(root, bg="white")
self.h_scrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=self.canvas.xview)
self.v_scrollbar = tk.Scrollbar(root, orient=tk.VERTICAL, command=self.canvas.yview)
self.scrollable_frame = tk.Frame(self.canvas)
self.scrollable_frame.bind(
"",
lambda e: self.canvas.configure(
scrollregion=self.canvas.bbox("all")
)
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(xscrollcommand=self.h_scrollbar.set, yscrollcommand=self.v_scrollbar.set)
self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
self.event_var = tk.StringVar(value="All")
self.create_widgets()
self.draw_graph()
def create_widgets(self):
control_frame = tk.Frame(self.root)
control_frame.pack(side=tk.TOP, fill=tk.X)
event_label = tk.Label(control_frame, text="Select Types:")
event_label.pack(side=tk.LEFT, padx=5, pady=5)
event_options = ["All", "zones", "states"]
event_menu = ttk.Combobox(control_frame, textvariable=self.event_var, values=event_options)
event_menu.pack(side=tk.LEFT, padx=5, pady=5)
event_menu.bind("", self.on_event_change)
browse_button = tk.Button(control_frame, text="Browse data_file", command=self.browse_file)
browse_button.pack(side=tk.LEFT, padx=5, pady=5)
save_button = tk.Button(control_frame, text="Save", command=self.save_data_file_file)
save_button.pack(side=tk.LEFT, padx=5, pady=5)
def browse_file(self):
data_file_file = filedialog.askopenfilename(filetypes=[("data_file files", "*.data_file")])
if data_file_file:
self.data_file_file = data_file_file
self.main_func()
self.draw_graph()
def save_data_file_file(self):
save_file = filedialog.asksaveasfilename(defaultextension=".data_file", filetypes=[("data_file files", "*.data_file")])
if save_file:
config = configparser.ConfigParser()
for section, attributes in self.sections.items():
config[section] = attributes
config[section]['LEVEL'] = str(self.level_dict[section])
with open(save_file, 'w') as configfile:
config.write(configfile)
def on_event_change(self, event):
self.event_filter = self.event_var.get() if self.event_var.get() != "All" else None
self.main_func()
self.draw_graph()
def draw_graph(self):
for widget in self.scrollable_frame.winfo_children():
widget.destroy()
self.level_frames = {}
levels = {}
for section, level in self.level_dict.items():
if level not in levels:
levels[level] = []
levels[level].append(section)
colors = ["lightblue", "lightgreen", "lightyellow", "lightpink", "lightgray"]
for level, nodes in sorted(levels.items()):
level_frame = tk.Frame(self.scrollable_frame, bg=colors[level % len(colors)], bd=2, relief=tk.SOLID)
level_frame.pack(fill=tk.X, padx=10, pady=5)
self.level_frames[level] = level_frame
level_label = tk.Label(level_frame, text=f"Level {level}", bg=colors[level % len(colors)], font=("Arial", 12, "bold"), anchor="w")
level_label.pack(side=tk.TOP, fill=tk.X)
for node in nodes:
self.draw_node(level_frame, node)
def draw_node(self, parent, node):
level = self.level_dict.get(node, 0)
label = f'{node}({level})'
if node in self.sections:
if self.sections[node]['RATING'] == '1':
color = 'lightblue'
else:
color = 'skyblue'
fg_color = 'darkblue'
node_label = DragDrop(parent, text=label, app=self, bg=color, fg=fg_color, font=("Arial", 10), bd=1, relief=tk.SOLID, padx=5, pady=5)
node_label.pack(side=tk.LEFT, padx=5, pady=5)
def update_node_position(self, node_label):
node_text = node_label.cget("text")
node_name = node_text.split('(')[0]
old_level = self.level_dict[node_name]
for level, frame in self.level_frames.items():
if node_label.winfo_y() >= frame.winfo_y() and node_label.winfo_y() < frame.winfo_y() + frame.winfo_height():
if old_level != level:
self.level_dict[node_name] = level
self.draw_graph()
break
# Remove empty levels and adjust subsequent levels
self.adjust_levels()
def adjust_levels(self):
levels = sorted(self.level_frames.keys())
for i, level in enumerate(levels):
if not any(node in self.level_dict and self.level_dict[node] == level for node in self.sections):
del self.level_frames[level]
for node in self.level_dict:
if self.level_dict[node] > level:
self.level_dict[node] -= 1
self.draw_graph()
if __name__ == '__main__':
root = tk.Tk()
root.title("Order Viewer")
app = Viewer(root)
root.geometry("800x600")
root.mainloop()
Подробнее здесь:
https://stackoverflow.com/questions/792 ... in-tkinter
1734282614
Anonymous
У меня есть графический интерфейс tkinter, который отображает некоторые узлы на уровнях. Уровни разделены на фреймы, и в каждом фрейме узлы размещаются в соответствии со словарем уровней, определенным в классе Viewer. Я хочу перетаскивать эти узлы с одного уровня на другой, и, как я это делаю, уровни должны корректироваться сами. Например, если на уровне есть только один узел, и этот узел перемещается вверх тогда этот уровень практически перестанет существовать, а нижние уровни должен двигаться вверх, чтобы заполнить логику иерархии уровней. Однако перетаскивание не работает должным образом. Ниже приведен мой код: Некоторые примечания о коде: [list] [*]level_dict в Viewer определяет уровни. Ключ DEPS обозначает зависимость одного узла от другого. [*]Класс DragDrop реализует функцию перетаскивания при нажатии. [code] import tkinter as tk from tkinter import ttk, filedialog import os class DragDrop(tk.Label): def __init__(self, parent, text, app, **kwargs): super().__init__(parent, text=text, **kwargs) self.parent = parent self.app = app self.text = text self.bind("", self.on_click) self.bind("", self.on_drag) self.bind("", self.on_release) self._drag_data = {"x": 0, "y": 0, "item": None} def on_click(self, event): self._drag_data["item"] = self self._drag_data["x"] = event.x self._drag_data["y"] = event.y def on_drag(self, event): x = self.winfo_x() - self._drag_data["x"] + event.x y = self.winfo_y() - self._drag_data["y"] + event.y self.place(x=x, y=y) def on_release(self, event): self._drag_data = {"x": 0, "y": 0, "item": None} self.app.update_node_position(self) class Viewer: def __init__(self, root): self.root = root self.level_dict = {'JK': 0, 'pun': 1, 'utp': 1, 'pun utp': 0, 'utk': 1, 'gjr': 2, 'wbk': 3, 'nest': 4, 'mahm': 5, 'ksl': 6, 'krtk': 5} self.sections = {'JK': {'DEPS': None, 'TYPES': None, 'RATING': '0'}, 'pun': {'DEPS': 'JK', 'TYPES': None, 'RATING': '0'}, 'utp': {'DEPS': 'JK', 'TYPES': None, 'RATING': '0'}, 'utk': {'DEPS': 'pun utp', 'TYPES': None, 'RATING': '0'}, 'gjr': {'DEPS': 'utk', 'TYPES': None, 'RATING': '0'}, 'wbk': {'DEPS': 'gjr', 'TYPES': None, 'RATING': '0'}, 'nest': {'DEPS': 'wbk', 'TYPES': None, 'RATING': '0'}, 'mahm': {'DEPS': 'nest', 'TYPES': None, 'RATING': '0'}, 'ksl': {'DEPS': 'mahm', 'TYPES': None, 'RATING': '0'}, 'krtk': {'DEPS': 'nest', 'TYPES': None, 'RATING': '0'}} self.canvas = tk.Canvas(root, bg="white") self.h_scrollbar = tk.Scrollbar(root, orient=tk.HORIZONTAL, command=self.canvas.xview) self.v_scrollbar = tk.Scrollbar(root, orient=tk.VERTICAL, command=self.canvas.yview) self.scrollable_frame = tk.Frame(self.canvas) self.scrollable_frame.bind( "", lambda e: self.canvas.configure( scrollregion=self.canvas.bbox("all") ) ) self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") self.canvas.configure(xscrollcommand=self.h_scrollbar.set, yscrollcommand=self.v_scrollbar.set) self.h_scrollbar.pack(side=tk.BOTTOM, fill=tk.X) self.v_scrollbar.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.event_var = tk.StringVar(value="All") self.create_widgets() self.draw_graph() def create_widgets(self): control_frame = tk.Frame(self.root) control_frame.pack(side=tk.TOP, fill=tk.X) event_label = tk.Label(control_frame, text="Select Types:") event_label.pack(side=tk.LEFT, padx=5, pady=5) event_options = ["All", "zones", "states"] event_menu = ttk.Combobox(control_frame, textvariable=self.event_var, values=event_options) event_menu.pack(side=tk.LEFT, padx=5, pady=5) event_menu.bind("", self.on_event_change) browse_button = tk.Button(control_frame, text="Browse data_file", command=self.browse_file) browse_button.pack(side=tk.LEFT, padx=5, pady=5) save_button = tk.Button(control_frame, text="Save", command=self.save_data_file_file) save_button.pack(side=tk.LEFT, padx=5, pady=5) def browse_file(self): data_file_file = filedialog.askopenfilename(filetypes=[("data_file files", "*.data_file")]) if data_file_file: self.data_file_file = data_file_file self.main_func() self.draw_graph() def save_data_file_file(self): save_file = filedialog.asksaveasfilename(defaultextension=".data_file", filetypes=[("data_file files", "*.data_file")]) if save_file: config = configparser.ConfigParser() for section, attributes in self.sections.items(): config[section] = attributes config[section]['LEVEL'] = str(self.level_dict[section]) with open(save_file, 'w') as configfile: config.write(configfile) def on_event_change(self, event): self.event_filter = self.event_var.get() if self.event_var.get() != "All" else None self.main_func() self.draw_graph() def draw_graph(self): for widget in self.scrollable_frame.winfo_children(): widget.destroy() self.level_frames = {} levels = {} for section, level in self.level_dict.items(): if level not in levels: levels[level] = [] levels[level].append(section) colors = ["lightblue", "lightgreen", "lightyellow", "lightpink", "lightgray"] for level, nodes in sorted(levels.items()): level_frame = tk.Frame(self.scrollable_frame, bg=colors[level % len(colors)], bd=2, relief=tk.SOLID) level_frame.pack(fill=tk.X, padx=10, pady=5) self.level_frames[level] = level_frame level_label = tk.Label(level_frame, text=f"Level {level}", bg=colors[level % len(colors)], font=("Arial", 12, "bold"), anchor="w") level_label.pack(side=tk.TOP, fill=tk.X) for node in nodes: self.draw_node(level_frame, node) def draw_node(self, parent, node): level = self.level_dict.get(node, 0) label = f'{node}({level})' if node in self.sections: if self.sections[node]['RATING'] == '1': color = 'lightblue' else: color = 'skyblue' fg_color = 'darkblue' node_label = DragDrop(parent, text=label, app=self, bg=color, fg=fg_color, font=("Arial", 10), bd=1, relief=tk.SOLID, padx=5, pady=5) node_label.pack(side=tk.LEFT, padx=5, pady=5) def update_node_position(self, node_label): node_text = node_label.cget("text") node_name = node_text.split('(')[0] old_level = self.level_dict[node_name] for level, frame in self.level_frames.items(): if node_label.winfo_y() >= frame.winfo_y() and node_label.winfo_y() < frame.winfo_y() + frame.winfo_height(): if old_level != level: self.level_dict[node_name] = level self.draw_graph() break # Remove empty levels and adjust subsequent levels self.adjust_levels() def adjust_levels(self): levels = sorted(self.level_frames.keys()) for i, level in enumerate(levels): if not any(node in self.level_dict and self.level_dict[node] == level for node in self.sections): del self.level_frames[level] for node in self.level_dict: if self.level_dict[node] > level: self.level_dict[node] -= 1 self.draw_graph() if __name__ == '__main__': root = tk.Tk() root.title("Order Viewer") app = Viewer(root) root.geometry("800x600") root.mainloop() [/code] [/list] Подробнее здесь: [url]https://stackoverflow.com/questions/79282792/how-to-drag-and-drop-items-across-frames-in-tkinter[/url]