Home Reference Houdini Object Model HOM Cookbook 

PyQt Example: Writing Custom GUIs inside Houdini

Overview

This example illustrates how to use the PyQt user interface widget toolkit to create a custom user interface inside Houdini. If you don’t have PyQt4 installed, you will need to install it to your Python distribution. On windows, install to $HFS/python.

Note if you use PyQt for commercial purposes, you must buy a commercial license. See http://www.riverbankcomputing.co.uk/pyqt/ for more information.

Location

Supporting files for this example are in $HFS/mozilla/documents/hom/cookbook/pyqt, also found in the cookbook/pyqt directory of cookbook_files.tar.gz.

Running the Example

Start Houdini from the cookbook/pyqt directory so Houdini can import the pyqt_helper module. Then create a shelf tool containing the following, and launch it. It will pop up a window with a button and some text. Clicking on the button will display a font chooser dialog that controls the font for the text.

import pyqt_helper
def runFontDialogInThread():
# This function is called on the thread dedicated to PyQt. It's only
# safe to import PyQt4 from this thread.
from PyQt4 import QtCore
from PyQt4 import QtGui
class FontDialog(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
hbox = QtGui.QHBoxLayout()
self.setGeometry(500, 300, 250, 110)
self.setWindowTitle('FontDialog')
button = QtGui.QPushButton('Change Font...', self)
button.setFocusPolicy(QtCore.Qt.NoFocus)
button.move(20, 20)
hbox.addWidget(button)
self.connect(button, QtCore.SIGNAL('clicked()'), self.showDialog)
self.label = QtGui.QLabel('This is some Sample Text', self)
self.label.move(130, 20)
hbox.addWidget(self.label, 1)
self.setLayout(hbox)
def showDialog(self):
font, ok = QtGui.QFontDialog.getFont()
if ok:
self.label.setFont(font)
app = pyqt_helper.getApplication()
cd = FontDialog()
cd.show()
app.exec_()
pyqt_helper.queueCommand(runFontDialogInThread)

Notes about Using PyQt

This example uses a module named pyqt_helper to help launch PyQt in a separate thread. See the notes inside this module.

pyqt_helper.py:

import threading
"""
This module helps you use PyQt in Houdini's GUI.
Rules when using multiple threads:
- Don't directly run code that accesses PyQt. Instead, call queueCommand
to queue a command to run on the thread dedicated to PyQt apps.
- Don't import PyQt4 from any code that isn't running on the PyQt thread.
- Don't create QtGui.QApplication's. Instead, call getApplication.
- Don't write/print anything to stdout/stderr (at least not when there's a
Python shell open in Houdini), or Houdini will hang.
"""
# Set this variable to False to run PyQt in the main thread. If it runs in
# the main thread, the rest of Houdini will be blocked until the application
# ends.
use_separate_thread = True
__command_queue = []
__command_queue_lock = threading.Lock()
__command_queue_event = threading.Event()
__pyqt_thread = None
def queueCommand(callable, arguments=()):
"""Queue up a command to run on the PyQt thread."""
if use_separate_thread == False:
callable(*arguments)
return
# Start up the PyQt thread if it's not already running.
global __pyqt_thread
if __pyqt_thread is None:
__pyqt_thread = threading.Thread(target=__pyQtThreadMain)
__pyqt_thread.start()
__command_queue_lock.acquire()
__command_queue.append((callable, arguments))
__command_queue_lock.release()
# Signal the PyQt thread to run the task.
__command_queue_event.set()
def __pyQtThreadMain():
"""This function is the starting point for the PyQt thread."""
# It's important that we import PyQt4 only from the PyQt thread, and not
# from another thread.
from PyQt4 import QtCore
from PyQt4 import QtGui
while True:
# Wait for the main thread to signal us.
__command_queue_event.wait()
# Remove the command from the stack and reset the event.
__command_queue_lock.acquire()
command = __command_queue.pop()
__command_queue_event.clear()
__command_queue_lock.release()
# Run the command.
command[0].__call__(*command[1])
__pyqt_app = None
def getApplication():
"""Return the QtGui.QApplication. Use this function instead of creating
PyQt threads manually. Do not call this function from anything other
than the PyQt thread."""
# This function may only be called from the PyQt thread.
from PyQt4 import QtCore
from PyQt4 import QtGui
# We're careful not to create more than one QApplication.
global __pyqt_app
if __pyqt_app is None:
__pyqt_app = QtGui.QApplication(['houdini'])
return __pyqt_app