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_helperdef runFontDialogInThread():# This function is called on the thread dedicated to PyQt. It's only# safe to import PyQt4 from this thread.from PyQt4 import QtCorefrom PyQt4 import QtGuiclass 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.
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 queueCommandto 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 aPython 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 = Nonedef 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_threadif __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 QtCorefrom PyQt4 import QtGuiwhile 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 = Nonedef getApplication():"""Return the QtGui.QApplication. Use this function instead of creatingPyQt threads manually. Do not call this function from anything otherthan the PyQt thread."""# This function may only be called from the PyQt thread.from PyQt4 import QtCorefrom PyQt4 import QtGui# We're careful not to create more than one QApplication.global __pyqt_appif __pyqt_app is None:__pyqt_app = QtGui.QApplication(['houdini'])return __pyqt_app
