Remapping Viewer Hotkeys for Roto efficiency
This started out as nitpicking but turned into an experiment in changing hotkey on the go while using Nuke.
I don't like where the shortcut key for next/previous frames (arrow keys) in Nuke. It's far away from the left side of the keyboard, and since I'm right handed, my hands are in an awkward position when I'm rotoing. Right hand using tablet on the right side on the table, while left hand sits awkwardly close to the right. What makes it worse is when I want to jump to next keyframe or mid way, it's CTRL or ALT + arrow key. Then both hands need to move to trigger the key, and I like to minimize my movement to maximize my work efficiency.
It's a first world problem.
First, I wouldn't want it to be a hotkey that triggers anywhere on screen, it'll have to be viewer only. Second, what's the new hotkeys? I decided to use "Q" and "W" because I don't use them much often when I'm actively rotoing. But I don't want to replace them permanently, because obviously I still want to be able to toggle overlay or wipe with ease when I stopped rotoing. So here's the thing I'm sure of.
It'll be in nuke.menu('Viewer') so it'll only activate when mouse is in viewer area
It'll use "Q", "W" as hotkeys, and modifier key enable extra functions.
I'll need to be able to return these hotkeys to original when I stopped using them.
The last one was the most difficult for me to solve as I've never swap hotkeys on the go before.
Reading from Nuke Python Reference, I learned the command to navigate frame is as below. So I can replicate the function of arrow keys and jumping between keyframes in python.
First I thought maybe I can enable those hotkeys when a roto properties is shown, and disabled when no roto properties are present. Perhaps knobChanged? Here's the piece of code I tried out.
It did not work out. Since node.shown() isn't a knob, so knobChanged doesn't really work here. And it crashes Nuke everytime I select a roto node. The first bit also replaced the hotkeys permanently even though I tried to use .setEnabled to enable and disable them, it doesn't bring back the original hotkeys for overlay and wipe. Turned out when you use .setShortcut(), it will clear out existing conflicting shortcut keys.
So a different approach.
After more research I realized Nuke actually allows assigning hotkey to menu item on the fly. According to more nuke python reference here. So instead of trying to add command and enable/disable them, I can simply assign hotkeys to them. However I didn't figure out how to trigger this automatically as a roto properties is shown. The methods I tried proven to be unstable and crash Nuke occasionally. This is the piece of code I wrote that requires manual trigger to enable disable the hotkeys change.
So instead of switching automatically, it'll be a manual right click in viewer window, navigating to a custom menu called "Roto Hotkeys" to enable or disable them. While disabled, "Q" and "W" will be return to their normal functions, overlay and wipe. I found this to be the most stable method to changing hotkeys in Nuke. I tried to add a few lines to actually remove the extra items I added in viewer menu, but for reason unknown to me, if I try to remove a menuItem in GUI, Nuke simply crashes without a warning. Perhaps I will report this as a bug.
I also created list and use for loop to change the keys as the previously attempted method seems really messy. And this way I can add more or alter them with ease
You may also notice as highlighted above I also disabled the up/down arrow keys. I often accidentally hit them, and my next viewer item might be a really heavy node. I then have to wait a few seconds frustratingly while Nuke thinks and just switch back to whatever I was doing.
After a few months of using it, I figured I need to be able to trigger it with a hotkey instead of right click to enable/disable every single time. The end result is a bit of combination of everything above.
First I added a line of code that check whether the menuItem has shortcut or not. If it does, it will clear out the hotkey and assign it to the corresponding item I have in mind. However, the line of code below is not working as I expected.
It is suppose to return "Q" when hotkey exists, and "" when it's not. To my surprise, it only returns "" whether menuItem "Overlay" has its hotkey or not.
Thanks to nuke python forum, I learned that I can use menuItem().action().enabled() to check whether a menuItem is enabled or not. So using by utilizing .setEnabled(), I can disable the original menuItem and clear its hotkey, and enable the new one and reassign hotkeys to them. Below is the updated final code.