How to create a child-toolwindow for QOpenGLWindow
-
Hi folks,
I'm working on a small project where I have a QOpenGLWindow as primary window, which can be displayed either as window of fullscreen and I would like to have a second window with some control widgets which should be a non-modal child of the first one and always stay on top if. In the ideal case it should be a toolwindow. Currently I'm using QDialog for this, but it is not working as intended although I'm settingsetWindowFlag(Qt::Tool); setWindowFlags(Qt::WindowStaysOnTopHint);
I'm also trying to set
windowHandle()->setTransientParent(MainWindowPtr);
but it forces the dialog always to the center of the MainWindow when shown.I really would appreciate any advice on how I could better approach this use case.
-
Hi and welcome to devnet,
What about using a QOpenGLWidget so that you use the same technology for both ?
-
Hi SGaist,
thank you very much for your response.
Changing the base class to QOpenGLWidget helped to solve some of the issues like the ignored ToolWindow style and using the same technology for both sure is the better fit.
One remaining issue is that when switching to fullscreen mode the second window still is moved to back when clicking somewhere in the mainwindow. In this situation the secondary window can be brought back to front when the user clicks in the first window in the area where the secondary window is located in the background. And it even reacts to this clicks not only by becoming active/focused/visible again. So if the user hits an area where it has e.g. a button it performs what ever action is connected to it. -
Can you show how you are handling these two widgets ?
-
Hi SGaist,
thanks again for your support with this problem.
Below I'm trying to provide all relevant portions of my code regarding the two widgets while excluding everything else for better clarity.
My second best option probably would be just to combine viewport and controls in one widget and thus avoid the always-on-top requirement entirely. Although I would of course prefer to learn what I was missing here.class OpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT friend class ControlDialog; public: OpenGLWidget(); ~OpenGLWidget(); protected: bool event(QEvent *event) override; void keyPressEvent(QKeyEvent *e) override; void toggleFullscreen(); private: ControlDialog* m_ctrlDlg = nullptr; }; OpenGLWidget::OpenGLWidget() : QOpenGLWidget() { setGeometry(300, 300, 800, 800); m_ctrlDlg = new ControlDialog(*this, *m_boids); m_ctrlDlg->show(); m_ctrlDlg->setWindowFlags(m_ctrlDlg->windowFlags() | Qt::WindowStaysOnTopHint); } OpenGLWidget::~OpenGLWidget() { delete m_ctrlDlg; m_ctrlDlg = nullptr; } bool OpenGLWidget::event(QEvent *event) { switch (event->type()) { case QEvent::Close: m_postfx->running = false; m_ctrlDlg->close(); break; } return QOpenGLWidget::event(event); } void OpenGLWidget::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_F: case Qt::Key_F11: toggleFullscreen(); break; } } void OpenGLWidget::toggleFullscreen() { if (windowState() & Qt::WindowFullScreen) showNormal(); else showFullScreen(); // TODO: control dialog is in background, bring it back to front } // ---------------------------------------------------------------------------- class ControlDialog : public QDialog { Q_OBJECT public: ControlDialog(OpenGLWidget& mainWnd, CLBoids& boids); protected: void keyPressEvent(QKeyEvent *e) override; void showEvent(QShowEvent *e) override {} // empty override to suppress re-centering of dialog on Show event private: bool eventFilter(QObject *obj, QEvent *event); OpenGLWidget& m_mainWnd; QTimer m_hideTimer; }; ControlDialog::ControlDialog(OpenGLWidget& mainWnd) : QDialog(&mainWnd, Qt::WindowStaysOnTopHint) , m_mainWnd(mainWnd) { m_ui.setupUi(this); setWindowFlag(Qt::Tool); setWindowFlag(Qt::WindowMaximizeButtonHint, false); setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); qApp->installEventFilter(this); m_hideTimer.setInterval(5000); m_hideTimer.callOnTimeout([&](){ hide(); }); m_hideTimer.start(); } void ControlDialog::keyPressEvent(QKeyEvent* e) { m_mainWnd.keyPressEvent(e); // forward events to main window } bool ControlDialog::eventFilter(QObject* obj, QEvent* event) { switch (event->type()) { case QEvent::Close: m_mainWnd.close(); break; case QEvent::MouseMove: case QEvent::KeyPress: m_hideTimer.start(); // resets timeeout if( !isVisible() ) show(); break; } return false; }
-
That looks a bit very convoluted.
Can you explain what that tool window is responsible for in your application ?
-
OpenGLWidget serves as main window. It is created from main() and does not contain any child widgets and provides the OpenGL viewport.
The ControlDialog tool window contains a couple of sliders to adjust some parameters and shader uniforms of the animation running in the main window. It is created as child of the OpenGLWidget. The goal of splitting viewport and controls in two windows was to show the UI when required and hide it by timeout when there is no user input so it does not distract the view when running in fullscreen mode. -
Ok, then let's simplify things a bit.
You should have slots in your OpenGLWidget that allows to adjust whatever values you want.
Then add signals to your ControlDialog that emits the values of the corresponding controls.
Connect these to together and voilà, no need for passing events around and you can easily implement new controls without worrying about handling events.
This also makes both widgets completely independent and thus easier to maintain.
-
Hi folks,
I'm working on a small project where I have a QOpenGLWindow as primary window, which can be displayed either as window of fullscreen and I would like to have a second window with some control widgets which should be a non-modal child of the first one and always stay on top if. In the ideal case it should be a toolwindow. Currently I'm using QDialog for this, but it is not working as intended although I'm settingsetWindowFlag(Qt::Tool); setWindowFlags(Qt::WindowStaysOnTopHint);
I'm also trying to set
windowHandle()->setTransientParent(MainWindowPtr);
but it forces the dialog always to the center of the MainWindow when shown.I really would appreciate any advice on how I could better approach this use case.
@Joe-Malik so you need two separate windows, right? One for OpenGL and second for control options.
-
Ok, then let's simplify things a bit.
You should have slots in your OpenGLWidget that allows to adjust whatever values you want.
Then add signals to your ControlDialog that emits the values of the corresponding controls.
Connect these to together and voilà, no need for passing events around and you can easily implement new controls without worrying about handling events.
This also makes both widgets completely independent and thus easier to maintain.
Hi @SGaist,
I can surely agree on the general advantages of message passing and decoupling you outlined. And I also see it as a worthy idea to consider a generalized event based shader parameter passing mechanism.
I really won't claim that my choosen way to handle reciprocal dependency of the two widgets is the best possible design neither for the general nor current case. So far I just judged it as good enough and IMHO all possible approaches on this come with their own pros and cons.
But in the end I don't understand how this questions of software design (which is always a good topic to think and talk about) relates to my initial problem of making the second widget stay on top? -
@Joe-Malik so you need two separate windows, right? One for OpenGL and second for control options.
Hi @CroCo,
thanks a lot for your reply.
Yes, I would like to have two widgets. One for OpenGL and the second for some control widgets with the additional requirement that it should stay always on top of the first one even if it is shown in fullscreen mode. -
Hi @SGaist,
I can surely agree on the general advantages of message passing and decoupling you outlined. And I also see it as a worthy idea to consider a generalized event based shader parameter passing mechanism.
I really won't claim that my choosen way to handle reciprocal dependency of the two widgets is the best possible design neither for the general nor current case. So far I just judged it as good enough and IMHO all possible approaches on this come with their own pros and cons.
But in the end I don't understand how this questions of software design (which is always a good topic to think and talk about) relates to my initial problem of making the second widget stay on top?@Joe-Malik said in How to create a child-toolwindow for QOpenGLWindow:
But in the end I don't understand how this questions of software design (which is always a good topic to think and talk about) relates to my initial problem of making the second widget stay on top?
Mostly unrelated because I got sidetracked by the implementation.
What OS are you currently your application on ?
-
@Joe-Malik said in How to create a child-toolwindow for QOpenGLWindow:
But in the end I don't understand how this questions of software design (which is always a good topic to think and talk about) relates to my initial problem of making the second widget stay on top?
Mostly unrelated because I got sidetracked by the implementation.
What OS are you currently your application on ?
-
What if you don't give your dialog any parent ?
-
Setting no parent for the control dialog makes the behaviour even worse.
-
If the control dialog is a child of the OpenGLWidget and I switch it to fullscreen the control dialog is shown on top as intended. When I now click somewhere in the viewport the control dialog is sent to the background. When I then click again in the viewport somewhere in the area where the control dialog was located it gets back in the foreground.
-
If the control dialog has no parent and I switch to fullscreen mode it it is sent to background and stays there until I leave fullscreen mode no matter where I click. It is also not possible to switch to it via Alt+Tab or move it in front of the fullscreen viewport from a second monitor.
In both cases it seems to be the focused state of the OpenGLWidget which keeps the control dialog from getting back to top again.
-