Remapping Viewer Hotkeys for Roto efficiency

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 isn’t it.

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.

  1. It’ll be in nuke.menu(‘Viewer’) so it’ll only activate when mouse is in viewer area
  2. It’ll use “Q”, “W” as hotkeys, and modifier key enable extra functions.
  3. 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.

frameControl(self, i)
i is an integer indicating viewer frame control 'button' to execute:    

	-6 go to start
	-5 play reverse
	-4 go to previous keyframe
	-3 step back by increment
	-2 go back previous keyframe or increment, whichever is closer
	-1 step back one frame
	0 stop
	+1 step forward one frame
	+2 go to next keyframe or increment, whichever is closer
	+3 step forward by increment
	+4 go to next keyframe
	+5 play forward
	+6 go to end 

Returns: True

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.

def rotoShortcut():
	viewerMenu = nuke.menu('Viewer')
	viewerMenu.addCommand('Next Frame', "nuke.activeViewer().frameControl(+1)", 'w')
	viewerMenu.addCommand('Previous Frame', "nuke.activeViewer().frameControl(-1)", 'q')
	viewerMenu.addCommand('Next Keyframe', "nuke.activeViewer().frameControl(2)", 'alt+w')
	viewerMenu.addCommand('Previous Keyframe', "nuke.activeViewer().frameControl(-2)", 'alt+q')

	if nuke.thisNode().shown():
	    viewerMenu.findItem('Overlay').setEnabled(False)
	    viewerMenu.findItem('Enable Wipe').setEnabled(False)
	    viewerMenu.findItem('Set New ROI').setEnabled(False)
	    viewerMenu.findItem('Next Frame').setEnabled(True)
	    viewerMenu.findItem('Previous Frame').setEnabled(True)
	    viewerMenu.findItem('Next Keyframe').setEnabled(True)
	    viewerMenu.findItem('Previous Keyframe').setEnabled(True)
	else:
	    viewerMenu.findItem('Next Keyframe').setEnabled(False)
	    viewerMenu.findItem('Previous Keyframe').setEnabled(False)
	    viewerMenu.findItem('Next Frame').setEnabled(False)
	    viewerMenu.findItem('Previous Frame').setEnabled(False)
	    viewerMenu.findItem('Overlay').setEnabled(True)
	    viewerMenu.findItem('Enable Wipe').setEnabled(True)
	    viewerMenu.findItem('Set New ROI').setEnabled(True)

nuke.addKnobChanged(rotoShortcut, nodeClass='Roto')

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.

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. All the methods I tried proven to be unstable and crash Nuke occasionally. This is the final piece of code I wrote.

import nuke
viewerMenu = nuke.menu('Viewer')

#setting list of items/commands/hotkeys to add and/or remove
labels = ['Next Frame', 'Previous Frame', 'Next Keyframe or Increment', 'Previous Keyframe or Increment']
commands = ['nuke.activeViewer().frameControl(+1)', 'nuke.activeViewer().frameControl(-1)', 'nuke.activeViewer().frameControl(+2)', 'nuke.activeViewer().frameControl(-2)']
hotkeys = ['w', 'q', 'alt+w', 'alt+q']
conflictingLabels = ['Enable Wipe', 'Overlay', 'Set New ROI'] #original items with conflicting hotkeys

#disable up/down arrow hotkeys in viewer
viewerMenu.findItem('Previous Input (A Side)').setEnabled(False)
viewerMenu.findItem('Next Input (A Side)').setEnabled(False)

#Add commands and functions to viewer menu
index = 0
for new in labels:
    viewerMenu.addCommand(new, commands[index])
    index += 1

def enableRotoHotkeys():
    for old in conflictingLabels:
        viewerMenu.findItem(old).setShortcut('')
        print 'disabled ' + old + ' hotkey...'
    index = 0
    for new in labels:
        viewerMenu.findItem(new).setShortcut(hotkeys[index])
        print 'enabled ' + new + ' hotkey...'
        index += 1

def disableRotoHotkeys():
    for roto in labels:
        viewerMenu.findItem(roto).setShortcut('')
        print 'cleared ' + roto + ' hotkey...'
    index = 0
    for original in conflictingLabels:
        viewerMenu.findItem(original).setShortcut(hotkeys[index])
        print 'enabled ' + original + ' hotkey...'
        index += 1



#Add functions to viewer menu on nuke menu
viewerSubMenu = nuke.menu('Viewer').addMenu('Roto Hotkeys')
viewerSubMenu.addCommand('Enable', "viewerRotoHotkeys.enableRotoHotkeys()", '', icon='Roto.png')
viewerSubMenu.addCommand('Disable', "viewerRotoHotkeys.disableRotoHotkeys()", '')

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.

Leave a Reply

Your email address will not be published. Required fields are marked *