[PYTHON] Display the script shortcut menu in the network editor

This article is the 25th day article of Houdini Advent Calender 2019. Last day! !!

at first

Display your own menu by pressing Shift + Tab (changeable) on the network editor. houdini_network_editor.gif Character images are just a hobby. You don't have to put it out.

Houdini itself has a lot of HDA, shelves, presets and useful tools so you don't really need to do this, but if you like it, Houdini is a fun and messy software. I will.

If you want to mention the points that you feel good to make

Well, the tools registered on the shelf can also be displayed on the normal TAB menu ... The disadvantage is that it is difficult to create. If you make a mistake in your code, Houdini will continue to output error messages endlessly.

If you think it's fun, please do. I will write the creation method below. Houdini I'm using is 18.0.287, which is a Windows environment.

The official documentation is here. The script of the network editor that is actually running is in (Houdini installation destination) /houdini/python2.7libs/nodegraph.py, so if you have any, it is recommended to refer to this.

Hook key events in the network editor

First, add the script file. For documents $HOUDINI_USER_PREF_DIR/python2.7libs/nodegraphhooks.py Example: C: \ Users \ (user name) \ Documents \ houdini18.0 \ python2.7libs \ nodegraphhooks.py Is written to add. Personally, I created another directory to make it easier to manage the script and specified the path in pythonrc.py.

pythonrc.py


import sys
sys.path.insert(0,'Script storage path')

I think it's okay if you like either one. The operation check code of nodegraphhooks.py is as follows.

nodegraphhooks.py


#coding:utf-8 
from canvaseventtypes import *

def createEventHandler(uievent, pending_actions):
    """Hook events from the network editor"""
    if isinstance(uievent, KeyboardEvent):
        if uievent.eventtype == 'keyhit' and uievent.key=='Shift+Tab':
            uievent.editor.flashMessage(None, 'nodegraphhook!!', 2.0)
    return None, False

Now Shift + Tab will display the message in the network editor. nodegraphhook.gif Simply find the unused hotkeys and write the corresponding code to extend the editor. If you have scripting that you use often, you may find it useful to add it here.

Display menu

Here we use hou.qt.Menu to display the shortcut menu. To write a process that will continue for a while, it seems to be a good idea to create a class that inherits nodegraphbase.EventHandler and process it with a function called handleEvent.

The original QMenu of hou.qt.Menu is often displayed using a function called exec_, but if you use this function, a warning may be displayed, so display it with show.

Adding the custom_tabmenu_handler.py file. Place it somewhere in your python path. If you are not particular about it, you can use the same directory as nodegraphhooks.py.

nodegraphhooks.py


#coding:utf-8 
from canvaseventtypes import *
from custom_tabmenu_handler import CustomTabMenuHandler

def createEventHandler(uievent, pending_actions):
    """Hook events from the network editor"""
    if isinstance(uievent, KeyboardEvent):
        if uievent.eventtype == 'keyhit' and uievent.key=='Shift+Tab':
            #Returns an event handler for a custom tab menu
            return CustomTabMenuHandler(uievent), True
    
    return None, False

custom_tabmenu_handler.py


#coding:utf-8 
import hou
import nodegraphbase as base
from hutil.Qt import QtGui
from hutil.Qt import QtWidgets

class CustomTabMenuHandler(base.EventHandler):
    """ Shift+Custom tab menu processing to open with Tab"""
    def __init__(self, uievent):
        """Initialization process"""
        base.EventHandler.__init__(self, uievent)
        #Creating a Menu
        self.menu = hou.qt.Menu()
        #Action registration
        self.addAction(self.menu, 'test', self.testFunc1)
        self.addAction(self.menu, 'test2', self.testFunc2)
        self.menu.addSeparator()
        self.addAction(self.menu, 'test3', self.testFunc3)
        #Display of Menu
        cursor_pos = QtGui.QCursor.pos()
        self.menu.move(cursor_pos)
        self.menu.show()

    def handleEvent(self, uievent, pending_actions):
        """Event processing"""
        #Returns an event handler to continue event processing
        if self.menu.isVisible():
            return self
        #Event processing ends when the menu disappears
        return None

    def addAction(self, menu, name, func):
        """Helper function for action registration"""
        act = QtWidgets.QAction(name, menu)
        act.triggered.connect(func)
        menu.addAction(act)

    def testFunc1(self):
        """Display message in network editor"""
        #The uievent passed in the initialization is self.start_Can be obtained with uievent
        self.start_uievent.editor.flashMessage(None, 'test1', 2.0)

    def testFunc2(self):
        self.start_uievent.editor.flashMessage(None, 'test2', 2.0)

    def testFunc3(self):
        self.start_uievent.editor.flashMessage(None, 'test3', 2.0)

To add an action, add a member function and associate it with addAction. nodegraphhook3.gif

Display image with menu

I just created a widget that displays only the image and displayed it with hou.qt.Menu. I am using such a widget.

class PopupImage(QtWidgets.QWidget):
    """Existence that only displays images for liveliness"""

    image = None

    def __init__(self, parent=None):
        super(PopupImage, self).__init__(parent)
        self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.NoDropShadowWindowHint)
        self.setAttribute(QtCore.Qt.WA_NoSystemBackground)
        self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
        
        if PopupImage.image is None:
            #Please adjust the image path to a good feeling.
            imagepath = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'images', 'popup_image.png'))
            PopupImage.image = QtGui.QImage()
            PopupImage.image.load(imagepath)

    def paintEvent(self, e):
        #Drawing event
        painter = QtGui.QPainter(self)
        painter.drawImage(self.rect(), PopupImage.image, PopupImage.image.rect())

A little more practical sample

The code is listed on GitHub, so check here for details. https://github.com/towazumi/HouNetworkEditorExtendSample

Add a Null node named OUT to make it black

The original story is Python starting with Houdini. Originally we changed the name and color of the selected node, but here we will add Null nodes directly. One thing to keep in mind is to make sure the network editor is at the SOP level and check if it appears in the menu.

nodegraphhook5.gif

Add a Python text frame and run button to the selected node

The original story is __2: Method using String of Read Wavefront.mtl file by callback. Make this setting from Python.

nodegraphhook6.gif

Save / restore the selected node as Python code

The original story is [Houdini Snippet] Save Node as Python Code. I am making it possible to use that information when adding processing to the script generated by asCode and restoring it.

nodegraphhook7.gif

Summary

It may be a dangerous time to get out of hand because Python 3 is also coming up, but it is fun to modify it to your liking. I hope this article will be useful to those who are also interested. Until the end Thank you for reading.

Recommended Posts

Display the script shortcut menu in the network editor
Get the script path in Python
Debug the script with Sakura Editor
Django ~ Let's display it in the browser ~
Display Python 3 in the browser with MAMP
Run the Python interpreter in a script
Load the network modeled with Rhinoceros in Python ③
[Python] Display the Altair legend in the plot area
Load the network modeled with Rhinoceros in Python ②
Load the network modeled with Rhinoceros in Python ①
I want to display the progress in Python!
Access the variables defined in the script from the REPL
Display the edge
Read the csv file and display it in the browser
Display line numbers in vim editor (with default settings)
Python OpenCV tried to display the image in text.