BQT: custom UI for add-ons & tool in Blender with PyQt or PySide

BQT let’s you create completely custom UI for your tools and addons with the QT library.
sample_blender_2way

fully custom UI

Instead of feeling limited by N-Panel only UI. Do whatever you want.

cross app

Qt widgets run nativaly in Krita, 3ds Max, Maya, … and are great to use in cross dcc pipelines.
There are already hundreds of QT widgets out on GitHub. Which you can just reuse.

Themed

BQT ships with a basic blender theme, so qt widgets will by default look similar to Blender.

Tech talk

  • The blender window is wrapped in a QWindow, so you can parent your widgets to it. To prevent them dissapearing in the background when using Blender.
  • The parenting also prevents your widget from being garbage collected.

Note that using operators from a QT window often is quite buggy. Try to not use any operators. If you do, use context override to set the window.

Installation

  • You can get the latest add-on from the repo: https://github.com/techartorg/bqt
  • You also need to pip install PySide2
  • since bqt is now an add-on, you need to enable it in Preferences

It has undergone major updates recently, so if you tried it in the past and had issues. These might now be resolved. Feel free to report issues here https://github.com/techartorg/bqt/issues


check out my other add-ons on my GitHub profile

11 Likes

theres 2 problems ,

A) the preferences window also gets wrapped and when i close it, the whole blender program closes.
B) the preferences window doesnt stay on top of the blender main window anymore. All other subwindows do behave correctly though, so it should be fixable relatively easy

Yes that’s an anoying issue.
It happens when on startup the wrong window is wrapped.
The wrapped window, is the one with title Blender qt.
image

yesterday I submitted an attempted fix for this.
Since there’s AFAIK no way to know which window is the main blender window, bqt wraps the largest window. If your preferences window on startup is larger than the blender window, it will wrap the preferences.

The wrapped qt window becomes the main window. so if you close that one, it closes Blender.

You can disable the wrapping with the env var BQT_DISABLE_WRAP set to 1. But then widgets wont stay in foreground.

the problem seems to be that blender exposes no window class for its windows … they are all just ghostclass (no class), there is no way to know which window is which … i think when bqt is merged into blender this could be fixed in one go ?

i changed my startup file so the preferences arent open on startup … now it seems to work correctly

tbh i don’t see that happen soon.

  • the easiest would be if the devs added a bpy method to get the window-handle of the main window.
  • or if blender could parent any window to blender like unreal’s parent_external_window_to_slate

FYI, GHOST is the underlying UI framework of Blender. So GHOST class refers to a class from that UI framework, not to a no-class class (which you call ghost-class)

3 Likes

ahhh ok i misunderstood that then … all other programs on my pc have a custom window class/name (often with a comprehensive naming) for all of their windows and subwindows though.
it would make things a bit easier, also for external tools, to interact with blender.

think i found the solution :smiley: , will merge it in later. tested and seems to work.
if there’s no parent handle, (0) it’s the main window

parent_hwnd = ctypes.windll.user32.GetParent(win.hwnd)
if parent_hwnd == 0:
    blender_windows = [win]
    break
1 Like

merged the fix for wrapping the main window in the bqt repo. now having preferences open should be possible without causing wrap issues.

various updates

  • widgets can stay in front if blender is not wrapped in qt (more stable)
  • widgets can be auto discovered and added (parented) to bqt
  • widgets can be enforced unique using the objectname, to avoid opening the same tool twice
  • fixed various bugs
    • notable bug: settings are not saved on closing blender
  • after years of outdated pip packages, bqt released a new version to PyPI (for pip install)
1 Like

Awesome Job.

I use the latest version of bqt. It works well currently.
Thanks a lot.

@H4nnes
By the way, have you ever used the PySide2.QtWebEngineWidgets in blender? It will always crash within any version… Do you have any suggestions?

You’re the Best!

Since I haven’t used it, I sadly don’t have a suggestion to fix PySide2.QtWebEngineWidgets
I created a bug ticket to track the issue https://github.com/techartorg/bqt/issues/110

Bqt now auto wraps all widgets in a dockable widget.
This feature can be disabled with env var BQT_DOCKABLE_WRAP set to 0, see wiki

dockablebqt

5 Likes

Hello!

Thanks for everyone’s work on this, it’s been really useful for me evaluating Blender!

I have an issue, and I’m not sure if it’s expected. When using a QScrollArea in my widget, I call takeWidget(), my widget is removed from the scrollArea, but instead of just not being visible, it becomes a separate widget.

Is this expected behaviour? In max, we do not have the same behaviour.

I’ve tried to make a simple version of what I mean below. Thanks for any help you can give!

from PySide2 import QtWidgets, QtUiTools, QtCore
from PySide2.QtCore import Qt, QTimer

class Window(QtWidgets.QWidget):
	def __init__(self, parent=None):
		super().__init__(parent)
		self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint)

		self.button1 = QtWidgets.QPushButton("Test")
		self.button1.pressed.connect(self.button_pushed)
		self.scrollArea = QtWidgets.QScrollArea()

		self.setLayout(QtWidgets.QVBoxLayout())
		self.layout().addWidget(self.button1)
		self.layout().addWidget(self.scrollArea)

		loader = QtUiTools.QUiLoader()
		ui_file = QtCore.QFile(path_to_local_ui_file)
		ui_file.open(QtCore.QFile.ReadOnly)
		self.parameters_ui = loader.load(ui_file)
		self.scrollArea.setWidget(self.parameters_ui)
        

	def button_pushed(self):
		self.scrollArea.takeWidget()

main_window = QtWidgets.QApplication.instance().blender_widget
w = Window(main_window)
w.show()

AH, figured it out, it’s because there is an option to auto parent orphan widgets.

Setting BQT_AUTO_ADD to 0 fixed this for me.

Thanks!

Great to hear you figured it out.
Another solution could be to add a parent when you create the widget
since only widgets without parent are auto collected by bqt.

# replace with this
self.scrollArea = QtWidgets.QScrollArea(parent=self)

The windows title bar is now dark (instead of white) when wrapping Blender in MainWidget.
release
image

note that title bars from non qt sub windows are still white, e.g. preferences.

I believe Blender itself fixed this in latest versions of Blender. Which now also ship with dark title bars by default. but e.g. 3.2 wont

1 Like
  • bqt’s UI framework has been updated to pyside6
    this means some nicer things like a black titlebar, and no more error in the log when closing blender.
  • logging support was added for developers.
1 Like

Hey
Awesome lib!!
Trying to test it now, I need to move lots of QT studio tools to blender ^^
One question, by default it installed pyside2, where do I configure which pyside gets installed?
Is there discord-support maybe?

Thanks!

it depends from where and how you installed it.
it might be set in the requirements.txt , e.g. when installed with plugget.
or if installed with pip it will be set in setup.py

i noticed the requirements was still set to pyside 2 so i ll update that

1 Like

I hope one day blender devs switch to QT. It’s a more configurable and elegant framework for UI. it would remove the need for nPanel. You could have pop-up, pop-out and dockable panels like all other 3D apps. Thanks for working through this.

1 Like