This is a simple UI template for loading Qt / PyQt tool windows within Unreal using Python. This template will parent the window to Unreal, remove old instances of the window, and automatically resize the widget. It also includes sample methods, how to identify child widgets, and how to connect a signal to a method. I recommend Qt Designer to generate tool .ui files.
Please feel free to post questions, comments, or suggestions for improvements.
This template worked great in 5.3 and below. However, in 5.4 as the Python version is now 3.11 Pyside2 is no longer supported.
I tried PySide6 however the engine just hangs and crashes when running the script.
Any ideas of what changes that need to be made? This currently blocks some tools we’ve made from working in 5.4.
EDIT:
Further investigation suggests its the QUiLoader, still testing things out to see if there is a fix.
EDIT:
import unreal
import sys
from functools import partial # if you want to include args with UI method calls
from PySide6.QtWidgets import QApplication, QWidget, QPushButton
from PySide6.QtUiTools import QUiLoader
from PySide6.QtCore import QFile
"""
Be sure to add your default python modules directory path to Unreal:
Project Settings -> Python -> Additional Paths
Default location of installed modules:
C:\\Users\\[USER]]\\AppData\Local\\Programs\\Python\\[PYTHON VERSION]\\Lib\site-packages
This code required PySide6 module which may need to be installed.
To install required modules open windows command prompt and enter:
pip install [MODULENAME]
"""
class UnrealUITemplate(QWidget):
"""
Create a default tool window.
"""
# store ref to window to prevent garbage collection
window = None
def __init__(self, parent = None, loader = None):
"""
Import UI and connect components
"""
super(UnrealUITemplate, self).__init__(parent)
#load the created UI widget
self.widgetPath = 'C:\\'
self.widget = loader.load(self.widgetPath + 'mainWidget.ui') #path to PyQt .ui file
#attach the widget to the instance of this class (aka self)
self.widget.setParent(self)
#find interactive elements of UI
self.btn_close = self.widget.findChild(QPushButton, 'btn_close')
#assign clicked handler to buttons
self.btn_close.clicked.connect(self.closewindow)
"""
Your code goes here.
"""
def resizeEvent(self, event):
"""
Called on automatically generated resize event
"""
self.widget.resize(self.width(), self.height())
def closewindow(self):
"""
Close the window.
"""
self.destroy()
def openWindow():
"""
Create tool window.
"""
ui_loader = QUiLoader()
if QApplication.instance():
# Id any current instances of tool and destroy
for win in (QApplication.allWindows()):
if 'toolWindow' in win.objectName(): # update this name to match name below
win.destroy()
else:
QApplication(sys.argv)
# load UI into QApp instance
UnrealUITemplate.window = UnrealUITemplate(loader = ui_loader)
UnrealUITemplate.window.show()
UnrealUITemplate.window.setObjectName('toolWindow') # update this with something unique to your tool
UnrealUITemplate.window.setWindowTitle('Sample Tool')
unreal.parent_external_window_to_slate(UnrealUITemplate.window.winId(),unreal.SlateParentWindowSearchMethod.MAIN_WINDOW)
openWindow()