How to monitor keydown and keyup in Python?

@dale I am trying to make a script that can read key presses but I have no success yet. Is there a built in function in Python for this? I need this to be able to manipulate objects, similar to Rhino’s arrow key nudge.
I have spent hours trying to figure this out, and the only thing I was able to find (probably due to my lack of search knowledge regarding the python universe) is a pygame module, but I would prefer to avoid third party modules so this can be used on “vanilla” Rhino installations.

I know you gave me some hints on this on the old NG, a year ago or so, but I can not get to that info now.

Tkinter

I have tried to import Tkinter, but Python states that there are “No module named Tkinter”, but this is supposed to be the default GUI module, so what am I doing wrong?

(Probably multiple errors in this script as I have not been able to test it yet, I am cutting and gluing bits and pieces together to try to figure out how stuff works. It’s how I like to learn stuff, and then I read up on the topic if/when it works)

import Tkinter as tk
import rhinoscriptsyntax as rs
import scriptcontext

def handleKeypress(event):
    pressedKey = event.char
    print pressedKey

while True:
    if scriptcontext.escape_test(False):
        break #get out of the loop
    handleKeypress()

Tkinter will not work in the version of python that is embedded in Rhino.

Can you tell us what it is you are trying to do? Monitoring keyboard events (other than escape key) is an extremely rare thing to do in Rhino commands/scripts and there may be a different “technique” to do what you are trying to do.

Ah…Ok.
Will Rhino get another version in the near future?

I want to make a WASD walk-about mode with “mouselook” (Just like any it is in any first person game) for architectural and landscape design evaluation. This will require a separate polysurface for movement constraint and keeping a fixed eye height.

So I need to track if one or more keys are pressed.
And it will terminate at ESC.

Being able to develop such a tool is the main reason I am switching to Python :smile:

We should start publishing release candidates for SR7 in a week or so.

I don’t have a good answer for this yet. I’m experimenting with an idea, and will try to get back to you soon.

Ok, this is a little deep for just getting started with python scripting AND this will only work on windows. Please ask questions about what is going on when they come up.

import Rhino
import System.Drawing
import System.Windows.Forms
import scriptcontext

class Form1(System.Windows.Forms.Form):
    def __init__(self):
        self.InitializeComponent()
    
    def InitializeComponent(self):
        self._button = System.Windows.Forms.Button()
        self.SuspendLayout()
        self._button.Location = System.Drawing.Point(150, 67)
        self._button.Size = System.Drawing.Size(75, 23)
        self._button.TabIndex = 0
        self._button.Text = "Done"
        self._button.Click += self.ButtonClick
        self.ClientSize = System.Drawing.Size(250, 102)
        self.Controls.Add(self._button)
        self.KeyPreview = True
        self.Text = "Press J or K"
        self.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow
        self.KeyDown += self.OnKeyDownEvent
        self.Closing += self.OnClosingEvent
        self.ResumeLayout(False)

    def ButtonClick(self, sender, e):
        self.Close()
        
    def OnKeyDownEvent(self, sender, e):
        left = e.KeyCode == System.Windows.Forms.Keys.J
        right = e.KeyCode == System.Windows.Forms.Keys.K
        if left or right:
            amount = 0.1
            if right: amount = -0.1
            viewport = scriptcontext.doc.Views.ActiveView.MainViewport
            z = viewport.ConstructionPlane().ZAxis
            viewport.Rotate(amount, z, Rhino.Geometry.Point3d.Origin)
            scriptcontext.doc.Views.Redraw()
        finish = e.KeyCode == System.Windows.Forms.Keys.Escape
        finish = finish or e.KeyCode == System.Windows.Forms.Keys.Enter
        if finish:
            self.Close()
        e.Handled = True
    def OnClosingEvent(self, sender, e):
        Rhino.Input.Custom.GetBaseClass.PostCustomMessage("exit")

f = Form1()
f.Show(Rhino.RhinoApp.MainWindow())
gs = Rhino.Input.Custom.GetString()
gs.SetCommandPrompt("press escape to exit")
gs.AcceptCustomMessage(True)
gs.Get()
if not f.IsDisposed:
    f.Close()
2 Likes

You are amazing… WOW!
And there were other nice bits of information there too. THANKS!
It’s absolutely a bit scary, but I’ll hack away on it and we’ll see where I’ll end up :smile:

Hi Steve
Your attached PythonScript is very interesting. I try to add a X , Y , Z key
for rotate around X Y Z axis but i obtain a Rhino Crash.
Can you add a possibilty to change rotate around other axis by click X or Y or Z ?

Ciao Vittorio

Hi @stevebaer I am trying to bend my mind around this.
I am close to getting the results I want, but I frankly don’t understand what makes your script work.
So to keep it simple and a bit abstract:

Let’s say I want to add a counter “i” that I define as i = 0
Then i want to add 1 to “i” for each cycle… if that is the right word…

How would I do that? Does it need a new definition? And where can I go to find some good literature on topics like this? If I could understand how to do this, then I should manage to do what I want to do.

Thanks

This sample is terrible for people getting started with python scripting, but it was the cleanest one I could come up with for what you are trying to do. If I understand your question, you would create a class variable in the constructor.

def __init(self)__:
    self.InitializeComponent()
    self.counter = 0

And then increment that value inside of your keyboard event handler.

def OnKeyDownEvent(self, sender, e):
    self.counter = self.counter + 1
    ....

You should be able to figure out the problem by placing a breakpoint in the OnKeyDownEvent function and stepping through your code. If the people reading this post don’t know how to use the debugger in python, I would highly recommend learning about it. That is one of the most useful tools available to anyone writing code.

Wiping sweat from the brows… drinking coffe… eyes hurting… mind bending…
Note to self: No pain no gain…

Thanks Steve! I’ll get right on it and see if I can understand and reuse this.

smile: There’s a lot to take on here with topics like classes, delegates, and WinForms all on this single sample.