← Learn library

Houdini & Procedural Workflows / Maya / Houdini / Python

Day 10 - Classes and Windows in Houdini with Python | Houdini 10 Day Introduction to Python

Channel: CGI Nerd ·

Written guide

Original CGI Nerd video thumbnail
Day 10 - Classes and Windows in Houdini with Python | Houdini 10 Day Introduction to PythonCGI Nerd · Video hosted on YouTube

Playing loads YouTube content. YouTube processes data after activation.

Watch on YouTube

If the player is blocked or unavailable, use Watch on YouTube.

Software: Maya, Houdini, Python

Wrap the previous lesson’s texture-assignment function in a small Houdini window. A directory field and browse button provide the path; an Assign Textures button runs the existing operation on the selected shader. The lesson uses this practical tool to introduce classes, instance data, methods and Qt signal connections.

Read Day 9: Functions first. Its texture naming rules and shader assumptions still apply. The exact Houdini/PySide build and complete pasted code are not recoverable from the captions. This is a structural guide to the demonstrated interface, not a tested drop-in script for every current Houdini build.

Give each object its own data

At 01:00, define a class with an initializer and another method. __init__ initializes a newly created instance; self conventionally names that instance within its methods. Assigning self.name = name stores the supplied name on that particular object.

The introductory example can be expressed with clearly illustrative values:

class Artist:
    def __init__(self, name, role, studio):
        self.name = name
        self.role = role
        self.studio = studio

    def introduce(self):
        print(f"{self.name} is a {self.role} at {self.studio}")

artist = Artist("Alex", "FX artist", "Example Studio")
artist.introduce()

Creating artist runs its initializer even though that initializer prints nothing. Calling artist.introduce() then uses the data stored on that instance. A second Artist can hold different values without changing the first. Python supplies the instance to an ordinary bound method call, so the caller does not manually pass self again.

This small example was checked in standalone Python. It illustrates the recording’s instance/method relationship; it does not execute Qt or Houdini. The Python class tutorial provides the language reference behind that distinction.

Reuse the assignment function and parent the dialog to Houdini

At 07:00, import Houdini’s module, directory utilities and the available PySide binding, then bring in the previous lesson’s apply-textures function. Keeping the assignment operation separate means the new window only needs to collect its two inputs: a material and a directory.

At 08:01, obtain Houdini’s main Qt window and use it as the dialog’s parent. The spoken explanation later reverses this relationship briefly: the new dialog is the child of Houdini’s main window, not its parent. SideFX’s hou.qt.mainWindow() documentation confirms this parenting pattern. It helps with window ownership and lifetime; it is not a guarantee that arbitrary UI code cannot crash.

Define a class derived from QDialog, then initialize the base dialog with the intended parent. This supplies dialog behavior such as setting a title, setting a minimum size and showing the window. The demonstrated minimum is 300 × 80. A window-flag adjustment removes the help/question-mark control, but its complete expression and binding version are not preserved here. Do not mix PySide imports or enum spellings from different Qt versions by guesswork.

Build widgets before laying them out or connecting them

At 11:00, the class organizes its custom work into three methods: create widgets, create layouts and create connections. That order encodes dependencies. Layouts need existing widgets; signal connections need both the widgets and the callback methods.

The widget method creates:

  • A QLineEdit to display and edit the texture-directory path.
  • Houdini’s file-chooser button beside the field.
  • An ordinary button labeled Assign Textures for the actual operation.

The browse button and assignment button do different jobs. Selecting a directory should populate the field, while applying textures should happen when the assignment action is invoked.

At 14:00, configure the chooser’s title, read mode and directory file type. The tool needs a folder containing maps, not a single selected image. SideFX’s FileChooserButton reference documents this widget separately from a general Qt push button. Consult the installed Houdini binding for its exact methods.

Arrange the fields without inheriting unrelated tool behavior

At 15:01, use a horizontal layout for the line edit and browse button. Put the assignment button into the next layout row, then add the rows through a form/main layout attached to the dialog.

The instructor reuses layout code from a crowd tool and points out a stale variable name. That name is incidental; this interface does not create agents. Trace the widget that is actually added rather than inferring behavior from a copied identifier.

The visible result should be one labeled directory row and one clear assignment action. Creating widgets alone is not sufficient: if a control is absent from the window, inspect whether it was added to a layout and whether that layout reaches the dialog’s main layout.

Connect signals to small callbacks

At 16:00, connect the chooser’s file-selection signal to a method that sets the line edit’s text. Connect the assignment button to a second method that obtains the selected node and reads the field’s current text, then passes both to the existing texture-assignment function.

The sequence is important: opening the window does not assign materials. The assignment callback runs on the button action, using the selection and path available then. If the user changes material selection after opening the dialog, that later selection is the relevant one in the demonstrated approach.

The callbacks should remain understandable as adapters: selected directory → field text, then selected material + field text → assignment function. The underlying function, not the dialog constructor, performs the shader-parameter changes. The captions occasionally conflate those operations; they are separate responsibilities.

The earlier limitations remain: selecting no node, selecting an incompatible node, entering an unreadable folder or supplying unexpected map names is not handled robustly in the shown tool. A visible window is not evidence that those cases are supported.

Manage repeated launches and inspect the actual result

At 18:01, the launch code attempts to close and remove an existing dialog reference before constructing a new instance and calling show(). The source uses a broad try/except/pass pattern so the first launch can proceed when no old reference exists. That also means a cleanup error could be hidden; the recording does not develop a complete exception or Qt-lifetime policy.

Retain the constructed dialog instance for subsequent use and check a second launch, not only the first. Closing a window and releasing a Python reference are related lifecycle steps, but are not a demonstrated guarantee that every Qt object has been destroyed immediately.

At 20:01, create and select a Principled Shader, browse to a texture folder and press Assign Textures. Inspect the texture fields, including normal/bump and displacement settings. The recording reports successful assignment without an error, but the shader preview still has a display problem. It does not render a new scene to establish the cause or prove the material’s final appearance.

The completed learning outcome is the class-to-window-to-callback structure. Keep interface behavior, texture-field assignment and final shading quality as separate verification points. Broader error handling, binding-version compatibility and shader/render validation remain follow-up work rather than solved features of this example.