Help: form keeps updating objects after closing, how to stop it?

Hi guys (@clement and @dale)

I have made a script to manipulate objects with the keys and have extracted the essence of the problem here. Use the arrow keys LEFT and RIGHT to make the object rotate.

My problem is that the task continues and the object continues to rotate after i close the form.
So I can make many objects spin in the file… So how do I terminate this when I close the form?

import Rhino
import rhinoscriptsyntax as rs
import System.Windows.Forms as Forms
import math

# Global variables to store the current state
current_angle = 0  # Angle in degrees, 0 means facing "up" (positive Y direction)
move_forward = False
move_backward = False
turn_left = False
turn_right = False

def rotate_object(obj_id, angle):
    """Rotate the object to the given angle."""
    # Find the object's centroid or a suitable rotation point
    bbox=rs.BoundingBox(obj_id)
    centroid = (bbox[0]+bbox[2])/2
    # Apply the rotation transformation
    rotation = Rhino.Geometry.Transform.Rotation(math.radians(angle), centroid)
    rs.TransformObject(obj_id, rotation)

def update_movement(obj_id):
    """Update the object's movement based on the current key states."""
    global current_angle
    
    if turn_left:
        current_angle += 1  # Turn left
    if turn_right:
        current_angle -= 1  # Turn right
    
    # Rotate the object to face the current direction
    rotate_object(obj_id, current_angle)
    

def on_key_down(sender, e):
    """Handle key press events."""
    global move_forward, move_backward, turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = True
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = True


def on_key_up(sender, e):
    """Handle key release events."""
    global move_forward, move_backward, turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = False
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = False


def main():
    global obj_id
    obj_id = rs.GetObject("Select an object to move")
    if not obj_id:
        return
    
    # Create a form to capture key events
    form = Forms.Form()
    form.KeyPreview = True
    form.KeyDown += on_key_down
    form.KeyUp += on_key_up
    
    # Configure the form to be visible
    form.Text = "Car Simulator"
    form.Width = 300
    form.Height = 100
    
    # Set up a timer to update the object's movement
    timer = Forms.Timer()
    timer.Interval = 10  # Update every 10 ms
    timer.Tick += lambda sender, e: update_movement(obj_id)
    timer.Start()
    
    # Show the form normally (not minimized)
    form.ShowInTaskbar = True
    form.StartPosition = Forms.FormStartPosition.CenterScreen
    
    # Run the form's main loop
    form.ShowDialog()

if __name__ == "__main__":
    main()

After the show dialog call you will want to unsubscribe your event handlers from the form events. (-= instead of +=).

And stop your timer and unregister from that as well.

2 Likes

I tried some of that stuff, but it crashed Rhino or wouldn’t close the form, it just hang, but I will try more!

@Holo

import Rhino
import rhinoscriptsyntax as rs
import System.Windows.Forms as Forms
import math

# Global variables to store the current state
current_angle = 0  # Angle in degrees, 0 means facing "up" (positive Y direction)
move_forward = False
move_backward = False
turn_left = False
turn_right = False
timer = None  # Global timer variable

def rotate_object(obj_id, angle):
    """Rotate the object to the given angle."""
    # Find the object's centroid or a suitable rotation point
    bbox = rs.BoundingBox(obj_id)
    centroid = (bbox[0]+bbox[2])/2
    # Apply the rotation transformation
    rotation = Rhino.Geometry.Transform.Rotation(math.radians(angle), centroid)
    rs.TransformObject(obj_id, rotation)

def update_movement(obj_id):
    """Update the object's movement based on the current key states."""
    global current_angle
    
    if turn_left:
        current_angle += 1  # Turn left
    if turn_right:
        current_angle -= 1  # Turn right
    
    # Rotate the object to face the current direction
    rotate_object(obj_id, current_angle)

def on_key_down(sender, e):
    """Handle key press events."""
    global move_forward, move_backward, turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = True
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = True

def on_key_up(sender, e):
    """Handle key release events."""
    global move_forward, move_backward, turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = False
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = False

def on_form_closing(sender, e):
    """Handle form closing event."""
    global timer
    if timer:
        timer.Stop()
        timer.Dispose()

def main():
    global obj_id, timer
    obj_id = rs.GetObject("Select an object to move")
    if not obj_id:
        return
    
    # Create a form to capture key events
    form = Forms.Form()
    form.KeyPreview = True
    form.KeyDown += on_key_down
    form.KeyUp += on_key_up
    form.FormClosing += on_form_closing
    
    # Configure the form to be visible
    form.Text = "Car Simulator"
    form.Width = 300
    form.Height = 100
    
    # Set up a timer to update the object's movement
    timer = Forms.Timer()
    timer.Interval = 10  # Update every 10 ms
    timer.Tick += lambda sender, e: update_movement(obj_id)
    timer.Start()
    
    # Show the form normally (not minimized)
    form.ShowInTaskbar = True
    form.StartPosition = Forms.FormStartPosition.CenterScreen
    
    # Run the form's main loop
    form.ShowDialog()

if __name__ == "__main__":
    main()

hope this helps,
Farouk

2 Likes

Thanks! So the timer is the only thing I need to stop and dispose? I would not have understood that based on my logic :smile:

I still need some help as I got this reply from McNeel tech support:

You didn’t still unsubscribe from the KeyDown and KeyUp event…
In any case, I would recommend posting this doubt in our official forum Discourse. You will find another power users who will help you solve your problem.

Kind regards,

So how do I unsubscribe KeyDown and KeyUp?
I thought this was linked to the form and closing the form would solve that by it self.

Any help is greatly appreciated.

I tried updating the definition for closing and please give info on what I need to close, dispose or unsubscribe here. Thanks!

Do I need to put “global” and then variable for all variables here too?

def on_form_closed(sender, e):
    """Handle the form closing event to properly dispose of resources."""
    print("closing the form etc")
    global timer
    if timer:
        timer.Stop()
        timer.Dispose()
    #Forms.Application.ExitThread()  # Ensures that the form's thread is terminated
    form.Dispose()
    form = None
    del form
    del current_angle, move_forward, move_backward, turn_left, turn_right, trace_points, polyline_id

Hi @Holo,
here is a possible approach

import Rhino
import rhinoscriptsyntax as rs
import System.Windows.Forms as Forms
import math

# Global variables to store the current state
current_angle = 0  # Angle in degrees, 0 means facing "up" (positive Y direction)
turn_left = False
turn_right = False

def rotate_object(obj_id, angle):
    """Rotate the object to the given angle."""
    # Find the object's centroid or a suitable rotation point
    bbox = rs.BoundingBox(obj_id)
    centroid = (bbox[0] + bbox[2]) / 2
    # Apply the rotation transformation
    rotation = Rhino.Geometry.Transform.Rotation(math.radians(angle), centroid)
    rs.TransformObject(obj_id, rotation)

def update_movement(obj_id):
    """Update the object's movement based on the current key states."""
    global current_angle
    
    if turn_left:
        current_angle += 1  # Turn left
    if turn_right:
        current_angle -= 1  # Turn right
    
    # Rotate the object to face the current direction
    rotate_object(obj_id, current_angle)

def on_key_down(sender, e):
    """Handle key press events."""
    global turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = True
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = True

def on_key_up(sender, e):
    """Handle key release events."""
    global turn_left, turn_right
    
    if e.KeyCode == Forms.Keys.Left:
        turn_left = False
    elif e.KeyCode == Forms.Keys.Right:
        turn_right = False

def on_form_closed(sender, e):
    """Clean up resources when the form is closed."""
    timer.Stop()  # Stop the timer to prevent further updates
    form.KeyDown -= on_key_down  # Unsubscribe from events add all events that you use
    form.KeyUp -= on_key_up

    form.FormClosed -= on_form_closed  # Unsubscribe from the form closed event

def main():
    global obj_id, form, timer
    obj_id = rs.GetObject("Select an object to move")
    if not obj_id:
        return
    
    # Create a form to capture key events
    form = Forms.Form()
    form.KeyPreview = True
    form.KeyDown += on_key_down
    form.KeyUp += on_key_up
    
    # Configure the form to be visible
    form.Text = "Car Simulator"
    form.Width = 300
    form.Height = 100
    
    # Set up a timer to update the object's movement
    timer = Forms.Timer()
    timer.Interval = 10  # Update every 10 ms
    timer.Tick += lambda sender, e: update_movement(obj_id)
    timer.Start()
    
    # Handle form closed event
    form.FormClosed += on_form_closed
    
    # Show the form normally (not minimized)
    form.ShowInTaskbar = True
    form.StartPosition = Forms.FormStartPosition.CenterScreen
    
    # Run the form's main loop
    form.ShowDialog()

if __name__ == "__main__":
    main()

Hope this helps,
Farouk

Oh wow… So there really isn’t a “terminate form” function that releases all of the forms resources when the form is closed? No wonder why bugtracking takes so long and why software tends to become slower over time and needs to be restarted… :smiley:
Thanks for your help, I will test it out.