Guest User

Python UML Diagram Drawer

a guest
Jan 30th, 2025
59
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.54 KB | Source Code | 0 0
  1. import tkinter as tk
  2. from tkinter import simpledialog
  3. import matplotlib.pyplot as plt
  4. import matplotlib.patches as patches
  5.  
  6. class UMLDiagramDrawer:
  7.     def __init__(self, root):
  8.         self.root = root
  9.         self.root.title("UML Diagram Drawer")
  10.        
  11.         self.canvas = tk.Canvas(root, width=800, height=600, bg="white")
  12.         self.canvas.pack()
  13.        
  14.         self.classes = {}  # Stores class names and their properties
  15.         self.relationships = []  # Stores relationships between classes
  16.         self.selected_class = None  # Tracks the currently selected class
  17.         self.drag_data = {"x": 0, "y": 0}  # Tracks mouse drag data
  18.        
  19.         self.create_menu()
  20.         self.create_buttons()
  21.         self.bind_events()
  22.        
  23.     def create_menu(self):
  24.         menubar = tk.Menu(self.root)
  25.         self.root.config(menu=menubar)
  26.        
  27.         diagram_menu = tk.Menu(menubar, tearoff=0)
  28.         diagram_menu.add_command(label="Add Class", command=self.add_class)
  29.         diagram_menu.add_command(label="Add Relationship", command=self.add_relationship)
  30.         menubar.add_cascade(label="Diagram", menu=diagram_menu)
  31.        
  32.     def create_buttons(self):
  33.         # Add a button to render the diagram with matplotlib
  34.         render_button = tk.Button(self.root, text="Render with Matplotlib", command=self.render_with_matplotlib)
  35.         render_button.pack(side=tk.BOTTOM, pady=10)
  36.        
  37.     def bind_events(self):
  38.         # Bind mouse events for selecting and dragging classes
  39.         self.canvas.bind("<ButtonPress-1>", self.on_mouse_press)
  40.         self.canvas.bind("<B1-Motion>", self.on_mouse_drag)
  41.         self.canvas.bind("<ButtonRelease-1>", self.on_mouse_release)
  42.        
  43.     def add_class(self):
  44.         class_name = simpledialog.askstring("Input", "Enter class name:")
  45.         if class_name:
  46.             self.classes[class_name] = {
  47.                 "x": 100,  # Default x position
  48.                 "y": 100,  # Default y position
  49.                 "width": 100,  # Default width
  50.                 "height": 60,  # Default height
  51.                 "rect": None,  # Canvas rectangle object
  52.                 "text": None,  # Canvas text object
  53.             }
  54.             self.draw_class(class_name)
  55.            
  56.     def draw_class(self, class_name):
  57.         props = self.classes[class_name]
  58.         x, y, width, height = props["x"], props["y"], props["width"], props["height"]
  59.        
  60.         # Draw rectangle and text on the canvas
  61.         props["rect"] = self.canvas.create_rectangle(x, y, x + width, y + height, outline="black", fill="lightblue")
  62.         props["text"] = self.canvas.create_text(x + width / 2, y + height / 2, text=class_name, fill="black")
  63.        
  64.     def add_relationship(self):
  65.         class1 = simpledialog.askstring("Input", "Enter first class name:")
  66.         class2 = simpledialog.askstring("Input", "Enter second class name:")
  67.         if class1 in self.classes and class2 in self.classes:
  68.             self.relationships.append((class1, class2))
  69.             self.draw_relationship(class1, class2)
  70.            
  71.     def draw_relationship(self, class1, class2):
  72.         props1 = self.classes[class1]
  73.         props2 = self.classes[class2]
  74.        
  75.         x1 = props1["x"] + props1["width"] / 2
  76.         y1 = props1["y"] + props1["height"]
  77.         x2 = props2["x"] + props2["width"] / 2
  78.         y2 = props2["y"]
  79.        
  80.         self.canvas.create_line(x1, y1, x2, y2, arrow=tk.LAST, fill="black", tags="relationship")
  81.        
  82.     def on_mouse_press(self, event):
  83.         # Check if a class rectangle is clicked
  84.         for class_name, props in self.classes.items():
  85.             x, y, width, height = props["x"], props["y"], props["width"], props["height"]
  86.             if x <= event.x <= x + width and y <= event.y <= y + height:
  87.                 self.selected_class = class_name
  88.                 self.drag_data["x"] = event.x - x
  89.                 self.drag_data["y"] = event.y - y
  90.                 break
  91.        
  92.     def on_mouse_drag(self, event):
  93.         if self.selected_class:
  94.             # Move the selected class
  95.             props = self.classes[self.selected_class]
  96.             new_x = event.x - self.drag_data["x"]
  97.             new_y = event.y - self.drag_data["y"]
  98.            
  99.             # Update class position
  100.             props["x"], props["y"] = new_x, new_y
  101.            
  102.             # Move the rectangle and text on the canvas
  103.             self.canvas.coords(props["rect"], new_x, new_y, new_x + props["width"], new_y + props["height"])
  104.             self.canvas.coords(props["text"], new_x + props["width"] / 2, new_y + props["height"] / 2)
  105.            
  106.             # Redraw relationships involving the moved class
  107.             self.redraw_relationships()
  108.        
  109.     def on_mouse_release(self, event):
  110.         self.selected_class = None  # Deselect the class
  111.        
  112.     def redraw_relationships(self):
  113.         # Clear all relationships and redraw them
  114.         self.canvas.delete("relationship")  # Delete all relationship lines
  115.         for class1, class2 in self.relationships:
  116.             self.draw_relationship(class1, class2)
  117.        
  118.     def render_with_matplotlib(self):
  119.         # Create a matplotlib figure
  120.         fig, ax = plt.subplots(figsize=(8, 6))
  121.        
  122.         # Draw classes as rectangles
  123.         for class_name, props in self.classes.items():
  124.             x, y, width, height = props["x"], props["y"], props["width"], props["height"]
  125.             rect = patches.Rectangle((x, y), width, height, linewidth=1, edgecolor='black', facecolor='lightblue')
  126.             ax.add_patch(rect)
  127.             ax.text(x + width / 2, y + height / 2, class_name, ha='center', va='center', color='black')
  128.        
  129.         # Draw relationships as arrows
  130.         for class1, class2 in self.relationships:
  131.             props1 = self.classes[class1]
  132.             props2 = self.classes[class2]
  133.            
  134.             x1 = props1["x"] + props1["width"] / 2
  135.             y1 = props1["y"] + props1["height"]
  136.             x2 = props2["x"] + props2["width"] / 2
  137.             y2 = props2["y"]
  138.            
  139.             ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
  140.                         arrowprops=dict(arrowstyle="->", color="black"))
  141.        
  142.         # Set plot limits and turn off axes
  143.         ax.set_xlim(0, 800)
  144.         ax.set_ylim(600, 0)  # Invert y-axis to match tkinter coordinates
  145.         ax.set_aspect('equal')
  146.         plt.axis('off')
  147.        
  148.         # Show the plot
  149.         plt.show()
  150.  
  151. if __name__ == "__main__":
  152.     root = tk.Tk()
  153.     app = UMLDiagramDrawer(root)
  154.     root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment