Как перетаскивать элементы между кадрами в tkinter?Python

Программы на Python
Ответить
Anonymous
 Как перетаскивать элементы между кадрами в tkinter?

Сообщение Anonymous »

У меня есть графический интерфейс 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
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»