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:
self.ui
won't have IDE introspection. I find I target few enough UI elements, or can glean the name quickly by having the point-and-click QT Creator file open that it's not a huge deal. (Or you can usepyside2-uic
to build some stubs once the UI has been more or less locked-down.)- Performance. While I don't have any metrics to share, I'm sure dynamically spinning up the UI and not packing the resources into their binary format has an effect. Is it noticeable? Not in the types of scripts I'm running.
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.