I have created a small basic TKINTER app in python, where the user can insert a dxf file ( AutoCAD file ),and visualize it on a 3D canvas. Also there is a zoom in and zoom out capability. Now I wish to create a 3D animation tool, where by pressing a button e.g. start, the user can navigate himself inside the canvas, troughout all the dxf's entities, with "pause" and "stop" buttons to be enhanced as well. Usually the dxf will be a pure polyline or a road so I wish the navigation to be linear from the beginning to the end of the dxf's entities. Please take a look on my piece of code and give me any suggestions, advice and or any piece of code that could help me achieve that.
import tkinter as tk
from tkinter import filedialog
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import ezdxf
import numpy as np
class DXFViewerApp:
def __init__(self, root):
self.root = root
self.root.title("DXF Viewer")
# Create and configure the main frame
self.main_frame = tk.Frame(self.root, padx=10, pady=10)
self.main_frame.pack()
# Create the canvas for displaying the DXF file with larger dimensions
self.canvas_frame = tk.Frame(self.main_frame, width=1500, height=1400)
self.canvas_frame.pack(side=tk.LEFT)
# Create the canvas for displaying coordinates information
self.coord_frame = tk.Frame(self.main_frame)
self.coord_frame.pack(side=tk.RIGHT)
# Create the menu bar
self.menu_bar = tk.Menu(self.root)
self.root.config(menu=self.menu_bar)
# Create the "File" menu
self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
self.menu_bar.add_cascade(label="File", menu=self.file_menu)
self.file_menu.add_command(label="Open DXF", command=self.open_dxf_file)
# Store the entities for redrawing after zoom
self.entities = None
# Create zoom in and zoom out buttons
self.zoom_in_button = tk.Button(self.canvas_frame, text="Zoom In", command=self.zoom_in)
self.zoom_in_button.pack(side=tk.BOTTOM, padx=5)
self.zoom_out_button = tk.Button(self.canvas_frame, text="Zoom Out", command=self.zoom_out)
self.zoom_out_button.pack(side=tk.BOTTOM, padx=5)
# Variables for mouse movement and zooming
self.prev_x = None
self.prev_y = None
# Initialize 3D plot
self.fig = plt.Figure(figsize=(12, 10))
self.ax = self.fig.add_subplot(111, projection='3d')
# Create Matplotlib canvas
self.canvas = FigureCanvasTkAgg(self.fig, master=self.canvas_frame)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def open_dxf_file(self):
file_path = filedialog.askopenfilename(filetypes=[("DXF Files", "*.dxf")])
if file_path:
self.entities = self.read_dxf(file_path)
self.draw_entities(self.entities)
def read_dxf(self, file_path):
doc = ezdxf.readfile(file_path)
return doc.modelspace().query("*")
def draw_entities(self, entities):
# Clear the 3D plot
self.ax.cla()
# Create a dictionary to store unique colors for each layer
layer_colors = {}
for entity in entities:
layer = entity.dxf.layer
if layer not in layer_colors:
# Generate a random color for the layer
layer_colors[layer] = np.random.rand(3,)
color = layer_colors[layer]
if entity.dxftype() == 'LINE':
start_point = entity.dxf.start
end_point = entity.dxf.end
self.ax.plot([start_point.x, end_point.x], [start_point.y, end_point.y], [start_point.z, end_point.z], color=color)
elif entity.dxftype() == 'LWPOLYLINE':
points = [(vertex[0], vertex[1], vertex[2]) for vertex in entity]
xs, ys, zs = zip(*points)
self.ax.plot(xs, ys, zs, color=color)
elif entity.dxftype() == 'CIRCLE':
center = entity.dxf.center
radius = entity.dxf.radius
phi = np.linspace(0, 2 * np.pi, 100)
x = center.x + radius * np.cos(phi)
y = center.y + radius * np.sin(phi)
z = center.z * np.ones_like(phi)
self.ax.plot(x, y, z, color=color)
# Set axis labels
self.ax.set_xlabel('X')
self.ax.set_ylabel('Y')
self.ax.set_zlabel('Z')
# Set axis limits
self.ax.set_xlim(self.ax.get_xlim())
self.ax.set_ylim(self.ax.get_ylim())
self.ax.set_zlim(self.ax.get_zlim())
# Draw coordinates information
min_x, min_y, max_x, max_y, min_z, max_z = self.ax.get_xlim() + self.ax.get_ylim() + self.ax.get_zlim()
self.draw_coordinates_info(min_x, min_y, max_x, max_y, min_z, max_z)
# Update canvas
self.canvas.draw()
def zoom_in(self):
# Zoom in
self.ax.dist *= 0.9
self.canvas.draw()
def zoom_out(self):
# Zoom out
self.ax.dist /= 0.9
self.canvas.draw()
def draw_coordinates_info(self, min_x, min_y, max_x, max_y, min_z, max_z):
# Clear the coordinates information frame
for widget in self.coord_frame.winfo_children():
widget.destroy()
# Display coordinates information
text = f"Min X: {min_x:.2f}\nMin Y: {min_y:.2f}\nMin Z: {min_z:.2f}\nMax X: {max_x:.2f}\nMax Y: {max_y:.2f}\nMax Z: {max_z:.2f}"
tk.Label(self.coord_frame, text=text, font=("Arial", 10)).pack()
if __name__ == "__main__":
root = tk.Tk()
app = DXFViewerApp(root)
root.mainloop()