call-with.cc

Skip the build on your toolkit shotgrid apps

For many of us, a shotgrid toolkit app starts with cloning tk-multi-starterapp, therefore much of the workflow is inherited from that approach.

This includes, designing with a .ui file then using pyside-uic and pyside-rcc to build the .ui files into python files and to pack the resources, respectively.

That's not altogether too complex but can have some speedbumps. For example, being on an M2 Mac I can't pip install PySide2. It simply doesn't support the chipset, I can brew install it... but that version doesn't ship with pyside-uic and pyside-rcc. Furthermore it sets:

PYTHON_BASE="/Applications/Shotgun.app/Contents/Resources/Python"

Which, (again, potentially is an M2 chipset thing) doesn't point to any binaries.

Furthermore! the sed command uses non-mac compatible syntax so brew install gsed is necessary along with changing the command itself to gsed.

And lastly, as if that wasn't enough caveats, pyside-uic is often called pyside2-uic, likewise for pyside-rcc.

Wouldn't it be nice if this all just went away?

Let's walkthrough doing just that. First, we need to change our imports, we need to use some stuff that doesn't live in sgtk.platform.qt. The toolkit docs explain how, and when to access PySide2 specific imports. The biggest change is that modules correspond to their official qt locations rather then the shotgrid amalgamation of locations mostly colocated under QtCore and QtGui.

from sgtk.platform.qt5 import (
    QtCore,
    QtUiTools,
    QtWidgets,
)

The following code takes care of the rest.

class AppDialog(QtWidgets.QWidget):
    """
    Main application dialog window
    """

    def __init__(self):
        """
        Constructor
        """
        super().__init__()

        self._app = sgtk.platform.current_bundle()
        self.ui = self._load_ui()

        # We can access and modify UI widgets and their attributes
        # just like normal.
        self.ui.MyLabel.setText("Yo")

    def _load_ui(self):
        # Setup resource dir
        resource_dir = Path(self._app.disk_location) / "resources"

        # Open UI File
        ui_file = QtCore.QFile(str(resource_dir / "dialog.ui"))
        ui_file.open(QtCore.QFile.ReadOnly)

        # Setup Loader
        #
        # by setting the resource dir as the working dir, relative
        # image paths from Creator will work fine.
        loader = QtUiTools.QUiLoader()
        loader.setWorkingDirectory(str(resource_dir))
        
        # Load the UI into a property
        self.ui = loader.load(ui_file)

        # Close the UI File
        ui_file.close()
        
        return self.ui

That's it. No more building!

As always there are downsides to consider:

With Pyside6 support seemingly around the corner, and with identical support for QUiLoader, this may pose one more advantage yet: no need to also update the tooling around the app to Pyside6-uic and Pyside6-rcc. Again, not a huge imposition, but I'm a script-kiddie at heart, get this build step 'outta here.