Build custom Qt UI within HDK?

   3650   9   3
User Avatar
Member
4 posts
Joined: Nov. 2020
Offline
I'm trying to use Qt to build custom widgets/UI using HDK(C++). There's documentation for doing it in Python with PySide2/PyQt, but those methods seems limited to shelf tools. By using HDK, I have more control over the UI and can activate them with certain user interactions.

Here's an example of what I'm trying to achieve, using the GreedyMouseHook sample code as a base (the relevant part is inside handleMouseEvent):

#include <UT/UT_DSOVersion.h>
#include <DM/DM_EventTable.h>
#include <DM/DM_MouseHook.h>
#include <DM/DM_VPortAgent.h>
#include <UI/UI_Event.h>

#include <QtCore/qcoreapplication.h>
#include <QtWidgets/qmainwindow.h>


class DM_GreedyMouseEventHook : public DM_MouseEventHook {

public:
	DM_GreedyMouseEventHook(DM_VPortAgent &vport) : DM_MouseEventHook(vport, DM_VIEWPORT_ALL_3D) { }
	~DM_GreedyMouseEventHook() override { }

	bool handleMouseEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {

		if ((event->state.values[W] & UI_RIGHT_BUTTON) && (event->state.altFlags & UI_SHIFT_KEY)) {
			if (event->reason == UI_VALUE_START) {
                                // This will not work, the widget is not coupled with Houdini's main thread
				widget = std::make_unique<QWidget>();
				widget.get()->resize(640, 480);
				widget.get()->setWindowTitle("Hello, world!!!");
				widget.get()->show();
				
			}
		}

		return true;
	}

	bool handleMouseWheelEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

	bool handleDoubleClickEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

	bool allowRMBMenu(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

private:
	std::unique_ptr<QWidget> widget; // I'm using unique_prt so I don't need to deal with memory management,
                                         // but I'm open to other suggestions.
};

// DM_GreedyMouseHook is a factory for DM_GreedyMouseEventHook objects.
class DM_GreedyMouseHook : public DM_MouseHook {
	
public:
		
	DM_GreedyMouseHook() : DM_MouseHook("Greedy", 0) { }
	DM_MouseEventHook *newEventHook(DM_VPortAgent &vport) override {
		return new DM_GreedyMouseEventHook(vport);
	}

	void retireEventHook(DM_VPortAgent &vport, DM_MouseEventHook *hook) override {
		// Shared hooks might deference a ref count, but this example just
		// creates one hook per viewport.
		delete hook;
	}
};

void DMnewEventHook(DM_EventTable *table) {
	table->registerMouseHook(new DM_GreedyMouseHook);
}

The problems so far are:
  1. The widget is not tied to Houdini's main thread, which I believe is necessary for the UI to show up.
  2. There's no QApplication involved which usually acts as an entry point for the application.

Is it possible to do what I want to achieve, to build custom UI using Qt and HDK?

Thanks in advance!
User Avatar
Member
460 posts
Joined: July 2005
Offline
I am very interested in this subject as well, trying to do something that requires to be extremely fast and responsive and Python is not cutting it.

thanks
varomix - Founder | Educator @ Mix Training
Technical Artist @ Meta Reality Labs
User Avatar
Member
4 posts
Joined: Nov. 2020
Offline
Looking at this post [www.sidefx.com] from another thread, maybe one of the developers rvinluan or tpetrick can help us? In that post, rvinluan writes:

Also looking in the HDK we don't expose the QT_Utils class which is probably a good thing. You should be able to access the app stylesheet similarly in C++ like so:

QApplication::instance()->styleSheet()

Could you maybe give us a code sample (use my code sample in the first post if you want) as to how to interact with QApplication? I'm trying to use QApplication in my code, but is either crashes or doesn't do anything when I run the plugin in Houdini.

TLDR; How do we create our own custom widgets and attach them to Houdini's QApplication/main thread?
User Avatar
Member
460 posts
Joined: July 2005
Offline
I've been trying in different ways and I always get Houdini to crash anything I want to make a QPushButton and show it, so, is clear I am doing it wrong

Would like to know how can we do pure C++ Qt interface and show it in Houdini, my tool at the moment, runs from a shelf tool and uses PySide to create the GUI, I want to just do all on C++

thanks
varomix - Founder | Educator @ Mix Training
Technical Artist @ Meta Reality Labs
User Avatar
Staff
1255 posts
Joined: July 2005
Offline
Hello,

It is possible to create Qt widgets in C++ and in the HDK. It's fairly straightforward and very similar to how you would create widgets in Python.

However, there are a few things to keep in mind:
1) Never try to create an instance of QApplication nor run the exec()method on QApplication. Houdini already does this on startup and doing the work again can produce unexpected behavior, such as a crash.
2) Avoid using smart pointers to manage Qt objects. Qt already semi-manages its own objects so using smart pointers will likely lead to crashing. Crashing can happen if the smart pointer deletes the referenced Qt object "too early" and Qt then tries to access the object, or at application shutdown when Qt cleans things up. If you need to delete a Qt object before shutdown, then use Qt's .deleteLater()method:
https://doc.qt.io/qt-5/qobject.html#deleteLater [doc.qt.io]

Or if you absolutely need to use smart pointers, then consider setting a custom deleter on the smart pointer that calls .deleteLater()instead of outright deleting the referenced object.

Anyway, I'm guessing that the use of std::unique_ptrin the sample code is leading to crashing in Houdini.

Also, if you need to access the singleton QApplication object, then either call QApplication::instance()or use the global qApppointer. Both are available in the <QtWidgets/QApplication> header and both are mentioned in Qt's documentation:
https://doc.qt.io/qt-5/qapplication.html [doc.qt.io]

And I don't see any indication in the sample code that the widget is created in a separate thread. If that were the case, you would normally see a Qt warning or error message in the terminal about a Qt object created not in the main UI thread.

Cheers,
Rob
User Avatar
Member
4 posts
Joined: Nov. 2020
Offline
Hi Rob, sorry for the late answer.

I finally got it to work. You were right, the unique pointer was causing all the havoc.
Thanks for the help!

varomix, did you get it working? I can post some simple sample code if not.

EDIT: Here's some sample code:

#include <UT/UT_DSOVersion.h>
#include <DM/DM_EventTable.h>
#include <DM/DM_MouseHook.h>
#include <DM/DM_VPortAgent.h>
#include <UI/UI_Event.h>

#include <qwidget.h>


class DM_GreedyMouseEventHook : public DM_MouseEventHook {

public:
	DM_GreedyMouseEventHook(DM_VPortAgent &vport) : DM_MouseEventHook(vport, DM_VIEWPORT_ALL_3D) { }
	~DM_GreedyMouseEventHook() override { }

	bool handleMouseEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {

		if (event->reason == UI_VALUE_CHANGED) {
			// Mouse up. We destroy the widget.
				
			widget->deleteLater();
			return true;
		}
		else if (event->state.values[W] & UI_RIGHT_BUTTON && event->reason == UI_VALUE_START) {
			// Mouse down. We create a standalone widget.

			myWidget = new QWidget(nullptr);
			myWidget->setGeometry(300, 300, 100, 100);
			myWidget->show();

			return true;
		}

		return true;

	}

	bool handleMouseWheelEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

	bool handleDoubleClickEvent(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

	bool allowRMBMenu(const DM_MouseHookData &hook_data, UI_Event *event) override {
		return false;
	}

private:
	QWidget* widget;

};

// DM_GreedyMouseHook is a factory for DM_GreedyMouseEventHook objects.
class DM_GreedyMouseHook : public DM_MouseHook {
	
public:
		
	DM_GreedyMouseHook() : DM_MouseHook("Greedy", 0) { }
	DM_MouseEventHook *newEventHook(DM_VPortAgent &vport) override {
		return new DM_GreedyMouseEventHook(vport);
	}

	void retireEventHook(DM_VPortAgent &vport, DM_MouseEventHook *hook) override {
		// Shared hooks might deference a ref count, but this example just
		// creates one hook per viewport.
		delete hook;
	}
};

void DMnewEventHook(DM_EventTable *table) {
	table->registerMouseHook(new DM_GreedyMouseHook);
}
Edited by testarut - Sept. 27, 2021 17:18:14
User Avatar
Member
460 posts
Joined: July 2005
Offline
testarut
varomix, did you get it working? I can post some simple sample code if not.


I did manage to make it work

thank you
varomix - Founder | Educator @ Mix Training
Technical Artist @ Meta Reality Labs
User Avatar
Member
27 posts
Joined: July 2022
Offline
Can this "HDK Qt UI" be opened by Menu or Shelf or PaneTab

Attachments:
55BO6CX%[C8T%49B4OTW9CD.png (88.2 KB)

User Avatar
Member
1 posts
Joined: May 2022
Offline
forever_kk
Can this "HDK Qt UI" be opened by Menu or Shelf or PaneTab
If you implement it as a New Hscript command, you can call it from a shelf tools, that's what I did

https://www.sidefx.com/docs/hdk/_h_d_k__extend_c_m_d.html [www.sidefx.com]
User Avatar
Member
27 posts
Joined: July 2022
Offline
varomix_meta
forever_kk
Can this "HDK Qt UI" be opened by Menu or Shelf or PaneTab
If you implement it as a New Hscript command, you can call it from a shelf tools, that's what I did

https://www.sidefx.com/docs/hdk/_h_d_k__extend_c_m_d.html [www.sidefx.com]

Thank you
I implement it
  • Quick Links