import os import json from PySide2 import QtWidgets from hotbox_designer.dialog import warning from hotbox_designer.languages import ( MEL, PYTHON, NUKE_TCL, NUKE_EXPRESSION, HSCRIPT) HOTBOXES_FILENAME = 'hotboxes.json' SHARED_HOTBOXES_FILENAME = 'shared_hotboxes.json' SETMODE_PRESS_RELEASE = 'open on press | close on release' SETMODE_SWITCH_ON_PRESS = 'switch on press' def execute(command): exec(command) class AbstractApplication(object): def __init__(self): self.name = type(self).__name__ folder = self.get_data_folder() self.local_file = os.path.join(folder, HOTBOXES_FILENAME) self.shared_file = os.path.join(folder, SHARED_HOTBOXES_FILENAME) self.main_window = self.get_main_window() self.reader_parent = self.get_reader_parent() self.available_languages = self.get_available_languages() self.available_set_hotkey_modes = self.get_available_set_hotkey_modes() @staticmethod def get_data_folder(): raise NotImplementedError @staticmethod def get_reader_parent(): raise NotImplementedError @staticmethod def get_main_window(): raise NotImplementedError @staticmethod def get_available_languages(): raise NotImplementedError @staticmethod def get_available_set_hotkey_modes(): raise NotImplementedError def set_hotkey(self, mode, sequence, open_cmd, close_cmd, switch_cmd): raise NotImplementedError class Maya(AbstractApplication): @staticmethod def get_data_folder(): from maya import cmds return cmds.internalVar(userPrefDir=True) @staticmethod def get_main_window(): import maya.OpenMayaUI as omui import shiboken2 main_window = omui.MQtUtil.mainWindow() if main_window is not None: return shiboken2.wrapInstance(long(main_window), QtWidgets.QWidget) @staticmethod def get_reader_parent(): return None @staticmethod def get_available_languages(): return [MEL, PYTHON] @staticmethod def get_available_set_hotkey_modes(): return [SETMODE_PRESS_RELEASE, SETMODE_SWITCH_ON_PRESS] def set_hotkey( self, name, mode, sequence, open_cmd, close_cmd, switch_cmd): from maya import cmds, mel current_hotkey_set = cmds.hotkeySet(current=True, query=True) if current_hotkey_set == 'Maya_Default': msg = ( 'The current hotkey set is locked,' 'change in the hotkey editor') warning('Hotbox designer', msg) return mel.eval("hotkeyEditorWindow;") use_alt = 'Alt' in sequence use_ctrl = 'Ctrl' in sequence use_shift = 'Shift' in sequence touch = sequence.split("+")[-1] show_name = 'showHotbox_' + name hide_name = 'hideHotbox_' + name switch_name = 'switchHotbox_' + name if mode == SETMODE_PRESS_RELEASE: cmds.nameCommand( show_name, annotation='show ' + name + ' hotbox', command=format_command_for_mel(open_cmd), sourceType="python") cmds.nameCommand( hide_name, annotation='hide ' + name + ' hotbox', command=format_command_for_mel(close_cmd), sourceType="python") cmds.hotkey( keyShortcut=touch, altModifier=use_alt, ctrlModifier=use_ctrl, shiftModifier=use_shift, name=show_name, releaseName=hide_name) else: cmds.nameCommand( switch_name, annotation='switch ' + name + ' hotbox', command=format_command_for_mel(switch_cmd), sourceType="python") cmds.hotkey( keyShortcut=touch, altModifier=use_alt, ctrlModifier=use_ctrl, shiftModifier=use_shift, name=switch_name) def format_command_for_mel(command): ''' cause cmds.nameCommand fail to set python command, this method embed the given command to a mel command callin "python" function. It put everylines in a single one cause mel is not supporting multi-lines strings. Hopefully Autodesk gonna fixe this soon. ''' command = command.replace("\n", ";") command = 'python("{}")'.format(command) return command class Nuke(AbstractApplication): @staticmethod def get_data_folder(): return os.path.expanduser('~/.nuke') @staticmethod def get_main_window(): for widget in QtWidgets.QApplication.instance().topLevelWidgets(): if widget.inherits('QMainWindow'): return widget @staticmethod def get_reader_parent(): return None @staticmethod def get_available_languages(): return PYTHON, NUKE_TCL, NUKE_EXPRESSION @staticmethod def get_available_set_hotkey_modes(): return [SETMODE_SWITCH_ON_PRESS] def set_hotkey( self, name, mode, sequence, open_cmd, close_cmd, switch_cmd): self.save_hotkey(name, sequence, switch_cmd) self.create_menus() def get_hotkey_file(self): hotkey_file = os.path.join( self.get_data_folder(), 'hotbox_hotkey.json') return hotkey_file def load_hotkey(self): hotkey_file = self.get_hotkey_file() if not os.path.exists(hotkey_file): return {} with open(hotkey_file, 'r') as f: return json.load(f) def save_hotkey(self, name, sequence, command): data = self.load_hotkey() data[name] = { 'sequence': sequence, 'command': command} with open(str(self.get_hotkey_file()), 'w+') as f: json.dump(data, f, indent=2) def create_menus(self): import nuke nuke_menu = nuke.menu('Nuke') menu = nuke_menu.addMenu('Hotbox Designer') hotkey_data = self.load_hotkey() for name, value in hotkey_data.items(): menu.addCommand( name='Hotboxes/{name}'.format(name=name), command=str(value['command']), shortcut=value['sequence']) class Houdini(AbstractApplication): @staticmethod def get_data_folder(): return os.path.expanduser('~/houdini17.0') @staticmethod def get_main_window(): import hou return hou.qt.mainWindow() @staticmethod def get_reader_parent(): return None @staticmethod def get_available_languages(): return [PYTHON, HSCRIPT] @staticmethod def get_available_set_hotkey_modes(): return [SETMODE_SWITCH_ON_PRESS] def set_hotkey( self, name, mode, sequence, open_cmd, close_cmd, switch_cmd): from hotbox_designer.qtutils import set_shortcut from functools import partial set_shortcut(sequence, self.main_window, partial(execute, switch_cmd))