Skip to content

Commit

Permalink
Fixing menu callbacks, make it more similar to other foundry engines.
Browse files Browse the repository at this point in the history
  • Loading branch information
Gael-Honorez-sb committed Jun 23, 2016
1 parent 56e4175 commit e06ab6b
Showing 1 changed file with 129 additions and 58 deletions.
187 changes: 129 additions & 58 deletions python/tk_katana/menu_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import sys
import unicodedata
import tank

from Katana import QtGui, QtCore

Expand Down Expand Up @@ -53,7 +54,7 @@ def create_menu(self):
# now enumerate all items and create menu objects for them
menu_items = []
for (cmd_name, cmd_details) in self.engine.commands.items():
menu_items.append(AppCommand(cmd_name, cmd_details))
menu_items.append(AppCommand(self.engine, cmd_name, cmd_details))

# sort list of commands in name order
menu_items.sort(key=lambda x: x.name)
Expand All @@ -65,7 +66,7 @@ def create_menu(self):

# scan through all menu items
for cmd in menu_items:
if cmd.get_app_instance_name() == app_instance_name and cmd.name == menu_name:
if cmd.get_app_instance_name == app_instance_name and cmd.name == menu_name:
# found our match!
cmd.add_command_to_menu(menu_handle)
# mark as a favourite item
Expand All @@ -78,13 +79,13 @@ def create_menu(self):
commands_by_app = {}

for cmd in menu_items:
if cmd.get_type() == "context_menu":
if cmd.type == "context_menu":
# context menu!
cmd.add_command_to_menu(self._context_menu)

else:
# normal menu
app_name = cmd.get_app_name()
app_name = cmd.app_name
if app_name is None:
# un-parented app
app_name = "Other Items"
Expand Down Expand Up @@ -225,75 +226,145 @@ class AppCommand(object):
Wraps around a single command that you get from engine.commands
"""

def __init__(self, name, command_dict):
self.name = name
self.properties = command_dict["properties"]
self.callback = command_dict["callback"]
self.favourite = False
def __init__(self, engine, name, command_dict):

def get_app_name(self):
"""
Returns the name of the app that this command belongs to
"""
if "app" in self.properties:
return self.properties["app"].display_name
return None
self._name = name
self._engine = engine
self._properties = command_dict["properties"]
self._callback = command_dict["callback"]
self._favourite = False
self._app = self._properties.get("app")
self._type = self._properties.get("type", "default")
try:
self._app_name = self._app.display_name
except AttributeError:
self._app_name = None
self._app_instance_name = None
if self._app:
for (app_instance_name, app_instance_obj) in engine.apps.items():
if self._app and self._app == app_instance_obj:
self._app_instance_name = app_instance_name

def get_app_instance_name(self):
"""
Returns the name of the app instance, as defined in the environment.
Returns None if not found.
"""
if "app" not in self.properties:
return None
@property
def app(self):
"""The command's parent app."""
return self._app

app_instance = self.properties["app"]
engine = app_instance.engine
@property
def app_instance_name(self):
"""The instance name of the parent app."""
return self._app_instance_name

for (app_instance_name, app_instance_obj) in engine.apps.items():
if app_instance_obj == app_instance:
# found our app!
return app_instance_name
@property
def app_name(self):
"""The name of the parent app."""
return self._app_name

return None
@property
def name(self):
"""The name of the command."""
return self._name

@name.setter
def name(self, name):
self._name = str(name)

@property
def engine(self):
"""The currently-running engine."""
return self._engine

@property
def properties(self):
"""The command's properties dictionary."""
return self._properties

@property
def callback(self):
"""The callback function associated with the command."""
return self._callback

@property
def favourite(self):
"""Whether the command is a favourite."""
return self._favourite

@favourite.setter
def favourite(self, state):
self._favourite = bool(state)

@property
def type(self):
"""The command's type as a string."""
return self._type

def get_documentation_url_str(self):
"""
Returns the documentation as a str.
Returns the documentation URL.
"""
if "app" in self.properties:
app = self.properties["app"]
doc_url = app.documentation_url
# deal with nuke's inability to handle unicode. #fail
if self.app:
doc_url = self.app.documentation_url
# Deal with nuke's inability to handle unicode.
if doc_url.__class__ == unicode:
doc_url = unicodedata.normalize('NFKD', doc_url).encode('ascii', 'ignore')
doc_url = unicodedata.normalize("NFKD", doc_url).encode("ascii", "ignore")
return doc_url

return None

def get_type(self):
"""
Returns the command type (node, custom_pane or default).
def _non_pane_menu_callback_wrapper(self, callback):
"""
return self.properties.get("type", "default")
Callback for all non-pane menu commands.
def do_add_command(self, menu, name, cmd, hot_key=None, icon=None):
# TODO add hot key
if hot_key:
action = QtGui.QAction(name, menu, triggered=cmd, icon=icon)
else:
if icon:
new_icon = QtGui.QIcon(icon)
action = QtGui.QAction(name, menu, triggered=cmd, icon=new_icon)
else:
action = QtGui.QAction(name, menu, triggered=cmd)
menu.addAction(action)

def add_command_to_menu(self, menu):
:param callback: A callable object that is triggered
when the wrapper is invoked.
"""
# This is a wrapped menu callback for whenever an item is clicked
# in a menu which isn't the standard nuke pane menu. This ie because
# the standard pane menu in nuke provides nuke with an implicit state
# so that nuke knows where to put the panel when it is created.
# If the command is called from a non-pane menu however, this implicity
# state does not exist and needs to be explicity defined.
#
# For this purpose, we set a global flag to hint to the panelling
# logic to run its special window logic in this case.
#
# Note that because of nuke not using the import_module()
# system, it's hard to obtain a reference to the engine object
# right here - this is why we set a flag on the main tank
# object like this.
setattr(tank, "_callback_from_non_pane_menu", True)
try:
callback()
finally:
delattr(tank, "_callback_from_non_pane_menu")

def add_command_to_menu(self, menu, enabled=True, icon=None):
"""
Adds an app command to the menu.
Adds a command to the menu.
:param menu: The menu object to add the new item to.
:param enabled: Whether the command will be enabled after it
is added to the menu. Defaults to True.
:param icon: The path to an image file to use as the icon
for the menu command.
"""
# std shotgun menu
icon = self.properties.get("icon")
icon = icon or self.properties.get("icon")
new_icon=None
if icon:
new_icon=QtGui.QIcon(icon)
hotkey = self.properties.get("hotkey")
self.do_add_command(menu,self.name, self.callback, hot_key=hotkey, icon=icon)

# Now wrap the command callback in a wrapper (see above)
# which sets a global state variable. This is detected
# by the show_panel so that it can correctly establish
# the flow for when a pane menu is clicked and you want
# the potential new panel to open in that window.
cb = lambda: self._non_pane_menu_callback_wrapper(self.callback)
if hotkey:
action = QtGui.QAction(self.name, menu,triggered=cb, icon=icon)
#menu.addCommand(self.name, cb, hotkey, icon=icon)
else:
action = QtGui.QAction(self.name, menu,triggered=cb, icon=icon)
#menu.addCommand(self.name, cb, icon=icon)

menu.addAction(action)

0 comments on commit e06ab6b

Please sign in to comment.