Need some help with a dialog window in Rhino Python (System.Windows.Forms).
The idea is simple: the window should close on Enter and Escape (this part works), and also on right mouse button (RMB) click anywhere on the screen.
The problem is that RMB just doesn’t trigger outside the form.
Current code:
import System.Windows.Forms as Forms
form = Forms.Form()
form.KeyPreview = True
def on_key_down(sender, e):
if e.KeyCode == Forms.Keys.Escape or e.KeyCode == Forms.Keys.Return:
form.Close()
e.Handled = True
form.KeyDown += on_key_down
form.Show()
Tried a few things already: MouseDown on the form → only works inside the window ShowDialog() instead of Show() → no difference attaching handlers to controls → nothing switching to Eto.Forms → same limitation
So clicks outside the form are not detected at all. What’s the correct way to handle RMB globally in this case? Is this something that requires Rhino.UI.MouseCallback, overriding WndProc, or a global mouse hook (SetWindowsHookEx)? Or is this simply not supported with WinForms in Rhino Python?
Running your code, when the form doesn’t have focus, pressing Enter or Esc also does not trigger their event.
How you detect the RMB depends on how global the detection needs to be. For example, Rhino.UI.MouseCallback only works when the pointer is over a Rhino viewport.
I’m trying to close a WinForms dialog in Rhino Python using Enter/Escape (this works) and the right mouse button (RMB). The RMB part currently works only when clicking inside the form. What I need is for it to also work when clicking on the Rhino viewport (not just inside the dialog).
Here is a minimal example:
v1
import System.Windows.Forms as Forms
from System.Drawing import Font, Point
form = Forms.Form()
form.Text = "Test Dialog"
form.Width = 400
form.Height = 200
form.StartPosition = Forms.FormStartPosition.CenterScreen
form.KeyPreview = True
form.ControlBox = True
label = Forms.Label()
label.Text = "Press ENTER, ESCAPE or RIGHT CLICK to close"
label.Font = Font("Arial", 11)
label.AutoSize = True
label.Location = Point(50, 50)
form.Controls.Add(label)
btn = Forms.Button()
btn.Text = "OK"
btn.Location = Point(150, 120)
btn.Click += lambda s, e: form.Close()
form.Controls.Add(btn)
def on_key_down(sender, e):
if e.KeyCode == Forms.Keys.Escape or e.KeyCode == Forms.Keys.Return:
form.Close()
e.Handled = True
def on_mouse_down(sender, e):
if e.Button == Forms.MouseButtons.Right:
form.Close()
form.KeyDown += on_key_down
form.MouseDown += on_mouse_down
form.ShowDialog()
v2
import System.Windows.Forms as Forms
from System.Drawing import Font, Point
import Rhino
form = Forms.Form()
form.Text = "Test Dialog"
form.Width = 400
form.Height = 200
form.StartPosition = Forms.FormStartPosition.CenterScreen
form.KeyPreview = True
form.ControlBox = True
label = Forms.Label()
label.Text = "Press ENTER, ESCAPE or RIGHT CLICK to close"
label.Font = Font("Arial", 11)
label.AutoSize = True
label.Location = Point(50, 50)
form.Controls.Add(label)
btn = Forms.Button()
btn.Text = "OK"
btn.Location = Point(150, 120)
btn.Click += lambda s, e: form.Close()
form.Controls.Add(btn)
def on_key_down(sender, e):
if e.KeyCode == Forms.Keys.Escape or e.KeyCode == Forms.Keys.Return:
form.Close()
e.Handled = True
form.KeyDown += on_key_down
class MyMouseCallback(Rhino.UI.MouseCallback):
def OnMouseDown(self, e):
if e.Button == Rhino.UI.MouseButtons.Right:
form.Close()
self.Enabled = False
mouse_cb = MyMouseCallback()
mouse_cb.Enabled = True
form.ShowDialog()
mouse_cb.Enabled = False
How can RMB be detected when clicking on the Rhino viewport while the dialog is open? Is Rhino.UI.MouseCallback the right approach for this, or is there another recommended way?
here is Gemini 3.1 Pro's solution as a revision to your v2 script with comments on the changes.
import System.Windows.Forms as Forms
from System.Drawing import Font, Point
import Rhino
form = Forms.Form()
form.Text = "Test Dialog"
form.Width = 400
form.Height = 200
form.StartPosition = Forms.FormStartPosition.CenterScreen
form.KeyPreview = True
form.ControlBox = True
form.TopMost = True # Added so the non-modal form doesn't disappear behind Rhino when clicking
label = Forms.Label()
label.Text = "Press ENTER, ESCAPE or RIGHT CLICK to close"
label.Font = Font("Arial", 11)
label.AutoSize = True
label.Location = Point(50, 50)
form.Controls.Add(label)
btn = Forms.Button()
btn.Text = "OK"
btn.Location = Point(150, 120)
btn.Click += lambda s, e: form.Close()
form.Controls.Add(btn)
def on_key_down(sender, e):
if e.KeyCode == Forms.Keys.Escape or e.KeyCode == Forms.Keys.Return:
form.Close()
e.Handled = True
# Added to handle RMB clicks directly over the form background
def on_form_mouse_down(sender, e):
if e.Button == Forms.MouseButtons.Right:
form.Close()
form.KeyDown += on_key_down
form.MouseDown += on_form_mouse_down # Re-added from v1 to detect clicks on the form itself
# Added loop to ensure child controls (Label, Button) also pass the RMB click to close the form
for ctrl in form.Controls:
ctrl.MouseDown += on_form_mouse_down
class MyMouseCallback(Rhino.UI.MouseCallback):
def OnMouseDown(self, e):
# Changed from Rhino.UI.MouseButtons.Right (which doesn't exist and throws an error) to Forms.MouseButtons.Right
if e.Button == Forms.MouseButtons.Right:
e.Cancel = True # Added to swallow the click so it doesn't trigger background actions in the Rhino viewport
if not form.IsDisposed: # Added to prevent crash if callback fires while the form is already in the process of closing
form.Close()
# Removed 'self.Enabled = False' from here to let the FormClosed event handle all cleanup reliably
mouse_cb = MyMouseCallback()
mouse_cb.Enabled = True
# Added event to safely disable the callback only when the form actually closes; required because Show() is non-blocking
def on_form_closed(sender, e):
mouse_cb.Enabled = False
form.FormClosed += on_form_closed
form.Show() # Changed from ShowDialog() to Show() to allow underlying viewport interaction
# Removed 'mouse_cb.Enabled = False' from the end because Show() doesn't block the script, which would instantly kill the callback