Qt how to capture event indicating that all gui elements are ready - c++

I'm wondering if it is possible to capture an event that is generated when all
Qt objects are initialized and ready ?
It seems that some things can't be done in window's constructor. And they work fine in slot implementation.
For example, when I want to access root window of my application I do it like that
// in *.h
MainWindow* rootWindow
// in *.cpp
rootWindow = qobject_cast<MainWindow *>(this->window());
If it is done in the contructor I can't use rootWindow object - it couses runtime error.
There is no relevant slot to implement. And create event in QMainWindow class is not virtual.
Thanks for help :)

You can use a single-shot timer for this. In your main window class, define a slot function called say, appReady(). In the constructor, create and connect a single shot timer using QTimer::singleShot(0, this, SLOT(appReady())); This timer should fire as soon as the event loop is up and running and there are no more startup events pending.

How can you be sure that root window is in fact MainWindow? Later in the project lifetime you could feed your widget to a different parent (for example a few layers of QFrame decorations for layouting purposes) and this code will fail.
Pass it in as an explicit parameter in the constructor instead.
Unless it is MainWindows all the way down :)

Related

Multiple Threads, how to create qt widgets properly

I have the follow constellation:
A qt gui thread with MainWindow
Another thread which essentially is a CameraManager...everytime a camera is added/removed the MainWindow will be informed.
It roughly looks like this:
Mainwindow derives from ICameraAddedConsumer
MainWindow implements ConsumeCameraAdded and creates widget inside this function. It subscribes itselv as a consumer to the CameraManager
CameraManager calls ConsumeCameraAdded of all it's consumers (MainWindow) when a new camera is added.
The problem is that CameraManager lives in a different thread and Qt will obviously complain about this since a widget is not created in the same thread as the mainwindow was.
Any suggestions how I can solve this?
As per comments, using signals/slots between QObjects in different threads should take care of the issue "automagically."
Barring that, and assuming MainWindow/ICameraAddedConsumer is a QObject, one idea could be to use something like:
QMetaObject::invokeMethod(consumer, "ConsumeCameraAdded", Qt::QueuedConnection, ...)
where consumer is a pointer to the MainWindow/ICameraAddedConsumer instance.
There's QWaitCondition but I'm not sure that makes sense in this case (though it could be adapted I suppose).
Otherwise... don't create the widget in ConsumeCameraAdded() but set some flag there (and return) and then use a QTimer or QObject::timerEvent() to periodically check the flag and create widget if it is set. Unfortunately I'm pretty sure you will not be able to create or start a timer within ConsumeCameraAdded() itself because of threading issues.

Changing the behavior of a widget type in another class

I have a MainWindow class in Qt, in which several checkable QGroupBox widgets are placed.
What I want to do is, to trigger a general onClick(bool checked) slot whenever one (any) of the QGroupBox objects are clicked, identify the sender and trigger some code.
I need to capture the objects' "clicked" signal in order to prevent a disabling action it performs on its children when the control is clicked.
This is the signal I'm trying to handle:
class Q_WIDGETS_EXPORT QGroupBox : public QWidget
{
...
Q_SIGNALS:
void clicked(bool checked = false);
...
};
I tried adding a custom slot like this and tried connecting it with the signal above but since QGroupBox at its own is not an object or pointer, the operation fails.
void MainWindow::onClick(bool clicked)
{
qDebug()<<"Custom slot triggered";
}
Long story short, I need to handle a control type's default behavior within my MainWindow class.
Thanks for any ideas in advance.
I need to capture the objects' "clicked" signal in order to prevent a disabling action it performs on its children when the control is clicked.
Perhaps, but I'm smelling an XY Problem here. You can certainly prevent emission of signals by invoking blockSignals() on the widget. But that's a hack - it will also prevent Qt's internals from acting on the object's destroyed() signal, and you might subtly break other users of the object's signals.
Instead, one could make the UI stateful and have the controller implement the stateful aspect of the button's interaction with the rest of the application. Currently, the button's clicked() signal is connected directly to other users. Instead, connect the button to the controller, and have the controller disable the children only when it's appropriate to do so. One could use the QStateMachine to make this stateful behavior explicit in terms of states.
#Kuba Ober, maybe you're right, I could not state my problem and the facts around it seperately and clearly. Thank you for pointing that out.
After checking out the QGroupBox class' source, I discovered that a "toggle" signal is emitted after every other operation in a "clicked" event.
http://code.qt.io/cgit/qt/qtbase.git/tree/src/widgets/widgets/qgroupbox.cpp
void QGroupBox::setChecked(bool b)
{
Q_D(QGroupBox);
if (d->checkable && b != d->checked) {
update();
d->checked = b;
d->_q_setChildrenEnabled(b);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::State st;
st.checked = true;
QAccessibleStateChangeEvent e(this, st);
QAccessible::updateAccessibility(&e);
#endif
emit toggled(b);
}
}
By combining this information with that of #rbaleksandar 's suggestion, I managed to obtain a solution.
I iterated through the controls of my MainWindow class and connected every QGroupBox's "toggle" signal with a common "MainWindow::onToggle" slot in which I could handle the post-event operations I desire.
As for my previous approach, I could've created a class which inherits QGroupBox and override any methods I wish to. But that would be overkill compared to my current solution. The fix I've came up with did the trick perfectly.
Thanks everyone for their insight and help.

Setting common parent Qt widget for new widget in separate thread

I need to create a widget in a separate thread and to set MainWindow for it as a parent widget. Creation of a thread cannot be avoided.
In the constructor of a new widget I am specifying a pointer to MainWindow, but give
QObject::setParent: Cannot set parent, new parent is in a different thread
How to solve this?
P.S. Child widgets may be numerous.
You cannot create UI widgets outside of main thread
This is not possible. See the following code reference for details why not:
QObject source code
In particular, you would need to pay attention to this warning:
"qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");"
which you got on the command line based on your question, so this is all expected.
As the warning says, you need to make sure that the parenting happens in the same thread between the parent and child.
Creation of a thread cannot be avoided. How to solve this?
I am afraid you will need to refactor the code by either moveing this out of your thread into the same where the parent is or/and not have the separate thread at all.
Based on the information in your question, currently, it is not possible to say more since we do not yet completely know the functionality of your other thread.
Hope this helps with explaining it.

Forcing QDialog to stay open

How should I best go about forcing a QDialog to stay open when the dialog's accept() slot is called? I was thinking of reimplementing that function to get the dialog's exec to return without hiding the dialog, but was wondering if there were any better ways to accomplish this.
Rather than use a QDialog, I would accomplish the effect with a QDockWidget.
Remove the feature that allows the dock to be moved (QDockWidget::DockWidgetMovable)
Set the dock widget to floating (setFloating(true))
Connect items on the dock widget to the appropriate signals and slots on the main window
References
Dock Widgets Example
QDockWidget Documentation
You need to make your QDialog modeless, by calling show instead of exec, and using a custom signal instead of accept, because accept closes the window. And you connect that signal to a slot in the main window with the code you had after the exec call.
And in case that wasn't already the case, you should keep a reference/pointer to your QDialog somewhere (as a member in your main window class, or a static variable within the function that opens it) to be able to avoid creating multiple instances of the dialog, and you need to make sure you only connect the signals/slots once.

Is there a way to determine if a top level Qt window has been moved?

I am trying to determine when the main window of my application has been moved. The main window is a standard QMainWindow and we've installed an eventFilter on the QApplication to look for moveEvents for the QMainWindow, but none are being triggered. For a variety of reasons, subclassing the QMainWindow isn't really an option.
Any thoughts on this, aside from starting a QTimer tto constantly check the position, would greatly be appreciated.
I guess it's better to install the event filter at the top-level window, instead of the application. However, if you still do not get QMoveEvents and you're working on Windows, you probably can override winEventFilter() and wait for WM_MOVE. Similar functionality might be available for Linux and Mac.
I usually do not recommend to break the platform-independence, but sometimes it might make sense.
Subclassing is really the best solution :-/
In the class that implements your top level windows you just overload this function:
virtual void moveEvent ( QMoveEvent * event )
From the documentation:
This event handler can be
reimplemented in a subclass to receive
widget move events which are passed in
the event parameter. When the widget
receives this event, it is already at
the new position.
The old position is accessible through
QMoveEvent::oldPos().
This should allow you to detect if your main window has moved. Why can't you subclass? Are you using an instance of QMainWindow directly? The usual use case is to subclass it anyway.