import vtk class ButtonCallback(object): def __init__(self, renderer, actor1): self.renderer = renderer self.actor1 = actor1 def execute(self, obj, event): print("Button was clicked!") # Move actor1 if self.actor1: current_position = self.actor1.GetPosition() self.actor1.SetPosition(current_position[0] + 0.1, current_position[1], current_position[2]) self.renderer.GetRenderWindow().Render() class MyInteractorStyle(vtk.vtkInteractorStyleTrackballCamera): def __init__(self, parent=None): super().__init__() self.AddObserver("LeftButtonPressEvent", self.left_button_press) self.actor1 = None self.actor2 = None self.label1 = None self.label2 = None def left_button_press(self, obj, event): try: click_pos = self.GetInteractor().GetEventPosition() print(f'left_button_press({click_pos[0]}, {click_pos[1]})') # Get the default renderer renderer = self.GetDefaultRenderer() if renderer is None: print("Error: Default renderer is None") return # Create a picker and perform picking picker = vtk.vtkPropPicker() picker.Pick(click_pos[0], click_pos[1], 0, renderer) # Get the picked actor picked_actor = picker.GetActor() if picked_actor is None: print("No actor was picked") elif picked_actor == self.actor1: print("Actor 1 was picked") # Define the movement of actor2 self.actor2.SetPosition(self.actor2.GetPosition()[0] + 0.1, self.actor2.GetPosition()[1], self.actor2.GetPosition()[2]) elif picked_actor == self.actor2: print("Actor 2 was picked") # Define the movement of actor1 self.actor1.SetPosition(self.actor1.GetPosition()[0] + 0.1, self.actor1.GetPosition()[1], self.actor1.GetPosition()[2]) else: print("Some other actor was picked") self.GetInteractor().Render() except Exception as e: print(f"An error occurred: {e}") def create_button(renderer, callback, position, size, label): # Create a TextActor to display the button text text_actor = vtk.vtkTextActor() text_property = vtk.vtkTextProperty() text_property.SetFontFamilyToArial() text_property.SetFontSize(12) text_property.BoldOn() text_property.ItalicOff() text_property.ShadowOn() text_property.SetJustificationToCentered() text_property.SetColor(0, 0, 0) # Text color text_actor.SetInput(label) text_actor.SetTextProperty(text_property) # Calculate text position, center it text_actor.SetDisplayPosition(position[0] + size[0] // 2, position[1] + size[1] // 2 - 10) # Create an ImageData to create the background image width, height = size image_data = vtk.vtkImageData() image_data.SetDimensions(width, height, 1) image_data.SetSpacing(1, 1, 1) image_data.SetOrigin(0, 0, 0) image_data.AllocateScalars(vtk.VTK_UNSIGNED_CHAR, 4) colors = vtk.vtkNamedColors() background_color = colors.GetColor4ub("LightGray") highlight_color = colors.GetColor4ub("LightBlue") # Set the default background color scalars = image_data.GetPointData().GetScalars() for y in range(height): for x in range(width): offset = y * width + x scalars.SetTuple4(offset, background_color[0], background_color[1], background_color[2], background_color[3]) # Create an ImageMapper to create the background image image_mapper = vtk.vtkImageMapper() image_mapper.SetInputData(image_data) # Create an ImageActor to display the background image background_actor = vtk.vtkImageActor() background_actor.SetMapper(image_mapper) # Set background position background_actor.SetDisplayPosition(position[0], position[1]) # Set interaction representation button_rep = vtk.vtkButtonRepresentation2D() button_rep.SetPlaceFactor(1.0) button_rep.PlaceWidget(position, [size[0], size[1]]) # Create a ButtonWidget to manage button interaction button = vtk.vtkButtonWidget() button.SetInteractor(renderer.GetRenderWindow().GetInteractor()) button.SetRepresentation(button_rep) button.AddObserver("StateChangedEvent", callback) # Enable the button button.On() # Update background color to reflect button state def update_button_state(obj, event): state = button_rep.GetState() if state == 0: # Normal state color = background_color else: # Highlight state color = highlight_color scalars = image_data.GetPointData().GetScalars() for y in range(height): for x in range(width): offset = y * width + x scalars.SetTuple4(offset, color[0], color[1], color[2], color[3]) image_data.Modified() renderer.GetRenderWindow().Render() button.AddObserver("StateChangedEvent", update_button_state) # Add text and background to renderer renderer.AddActor2D(text_actor) renderer.AddActor2D(background_actor) return button def read_and_display_stl(file_path1, file_path2): # Read the first STL model reader1 = vtk.vtkSTLReader() reader1.SetFileName(file_path1) mapper1 = vtk.vtkPolyDataMapper() mapper1.SetInputConnection(reader1.GetOutputPort()) actor1 = vtk.vtkActor() actor1.SetMapper(mapper1) # Create label text_property1 = vtk.vtkTextProperty() text_property1.SetFontFamilyToArial() text_property1.SetFontSize(16) text_property1.BoldOn() text_property1.ItalicOff() text_property1.ShadowOn() label1 = vtk.vtkTextActor() label1.SetInput("Model 1") label1.SetTextProperty(text_property1) label1.SetDisplayPosition(50, 50) # Set position # Read the second STL model reader2 = vtk.vtkSTLReader() reader2.SetFileName(file_path2) mapper2 = vtk.vtkPolyDataMapper() mapper2.SetInputConnection(reader2.GetOutputPort()) actor2 = vtk.vtkActor() actor2.SetMapper(mapper2) # Create label text_property2 = vtk.vtkTextProperty() text_property2.SetFontFamilyToArial() text_property2.SetFontSize(16) text_property2.BoldOn() text_property2.ItalicOff() text_property2.ShadowOn() label2 = vtk.vtkTextActor() label2.SetInput("Model 2") label2.SetTextProperty(text_property2) label2.SetDisplayPosition(50, 100) # Set position renderer = vtk.vtkRenderer() renderer.AddActor(actor1) renderer.AddActor(actor2) renderer.AddActor2D(label1) # Add label to renderer renderer.AddActor2D(label2) # Add label to renderer render_window = vtk.vtkRenderWindow() render_window.AddRenderer(renderer) render_window.SetSize(800, 600) # Set window size to 800x600 interactor = vtk.vtkRenderWindowInteractor() interactor.SetRenderWindow(render_window) # Set custom interaction style style = MyInteractorStyle() style.actor1 = actor1 style.actor2 = actor2 style.label1 = label1 style.label2 = label2 interactor.SetInteractorStyle(style) # Set default renderer style.SetDefaultRenderer(renderer) # Create button callback = ButtonCallback(renderer, actor1) button1 = create_button(renderer, callback.execute, (100, 550), (100, 30), "Move Actor 1") interactor.Initialize() interactor.Start() if __name__ == "__main__": # Replace with your two STL file paths stl_file_path1 = "tetrahedron.stl" stl_file_path2 = "tetrahedron.stl" read_and_display_stl(stl_file_path1, stl_file_path2)