Inside my QDialog code I've got the following:
QTimer::singleShot(2 * 1000, [&] {
if (theme_download_label) {
theme_download_label->hide();
theme_download_label->setText("text");
}
});
The issue is that if I close the dialog before the timer fires, theme_download_label->setText crashes. How to deal with this?
I've already tried setting theme_download_label to Q_NULLPR before the close() method to no effect.
It doesn't matter where the widget originates from, or whether it's a widget. The solution is the same for any QObject.
Provide a QObject context to the connection. When the context object dies, it gets disconnected, and thus the functor will not be invoked.
Pass the pointer to the label by value, not by reference, in case the timer outlives this. That was the likely cause of the crash.
There's no need to check whether theme_download_label is null: it can't be. That's the invariant of a connection with a context object: it is guaranteed by design that the context object is alive.
QTimer::singleShot(2 * 1000, theme_download_label, [label = theme_download_label] {
label->hide();
label->setText("text");
});
Related
I found an interesting article on how to impement QObject with dynamic properties (see C++ class DynamicObject). The code from the article works fine, the properties of DynamicObject are get and set successfully from both C++ and QML, but the only thing I cannot figure out is how to fire dynamic signals.
I tried to fire "nameChanged()" signal with the following code:
bool DynamicObject::emitDynamicSignal(char *signal, void **arguments)
{
QByteArray theSignal = QMetaObject::normalizedSignature(signal);
int signalId = metaObject()->indexOfSignal(theSignal);
if (signalId >= 0)
{
QMetaObject::activate(this, metaObject(), signalId, arguments);
return true;
}
return false;
}
myDynamicObject->emitDynamicSignal("nameChanged()", nullptr);
the index of the signal is found and signalId is assigned to 5, but the signal is not fired. But if I do, for example,
myDynamicObject->setProperty("name", "Botanik");
the property is changed and the signal is fired successfully.
What is wrong in my code? What should I pass as 'arguments' parameter of QMetaObject::activate ?
EDIT1:
The full source code is temporarily available here.
A signal is also a method. You can invoke it from the meta object.
So, replace your line QMetaObject::activate(...) by:
metaObject()->method(signalId).invoke(this);
And let Qt handles the call to activate().
There is also an issue in DynamicObject::qt_metacall(): you are handling only QMetaObject::ReadProperty and QMetaObject::WriteProperty calls.
You have to add QMetaObject::InvokeMetaMethod if you want to emit your signal.
given this code:
void FooBar::ProcessExitHandler(QProcess* someProcess, QString logsPath)
{
if (clientProcess->exitCode() != 0)
{
QMessageBox* dialog = new QMessageBox();
dialog->setText("bye bye");
dialog->setStandardButtons(0);
QObject::connect(dialog, &QMessageBox::finished, [this](int) {
if (mMainWindow->AutoCloseCheckBoxChecked())
{
delete dialog; //TODO: need to confirm what is the correct way
this->quit();
}
});
dialog->show();
dialog->activateWindow();
}
else
{
if (mMainWindow->AutoCloseCheckBoxChecked())
{
delete dialog; //TODO: need to confirm what is the correct way
this->quit();
}
}
}
Is calling delete dialog like that correct? Is there a more QT idiomatic way of doing this?
Also, something that has caused me confusion is the idea (from the docs) that I should be passing a parent to the constructor of the message box. Then I would get automatic memory management, right? Is that the QT style I should shoot for?
I'm aware that since the app is exiting anyway, the leak "doesn't matter", but I want to do the right thing.
The right way is to use setAttribute
QMessageBox* dialog = new QMessageBox();
dialog->setAttribute(Qt::WA_DeleteOnClose);
By setting the attribute WA_DeleteOnClose, the destructor will be called at the right moment.
When you manually call delete, the pointer will keep its value (the address) although it isn't valid anymore. If for some reason you were to reuse that pointer again, the app would crash.
\warning Deleting a QObject while pending events are waiting to be
delivered can cause a crash. You must not delete the QObject directly
if it exists in a different thread than the one currently executing.
Use deleteLater() instead, which will cause the event loop to delete
the object after all pending events have been delivered to it.
https://code.woboq.org/qt5/qtbase/src/corelib/kernel/qobject.cpp.html#881
I see the accept() somewhat similar to a return, so I've been putting it a the end of my slots with no code afterwards. That is, the accept() "finishes" the execution of the dialog.
Nevertheless, I came across the need to close a dialog and open a new one from a slot in the first one. Therefore, what I thought was moving the accept() to the beginning of the slot and initializing the second dialog after it. Something like the following:
void FirstDialog:slotFirstDialog()
{
accept();
// Setup second dialog arguments
// ...
SecondDialog *sd = new SecondDialog();
sd->exec();
}
Is this use of accept() valid? Is it good practice?
I'd avoid it. Calling accept() can trigger a delayed deletion of FirstDialog (say, if it has the Qt::WA_DeleteOnClose flag set)1; in that case, it would be deleted in one of the first events dispatched by the nested event loop (sd->exec()), which would lead to go on executing code in a method of an instance that has been deleted. This is just a sample problem on the top of my head, I'm sure others can be found.
I'd probably just hide the dialog before calling exec() on the other, and call accept() after the end of the nested event loop.
void FirstDialog:slotFirstDialog()
{
// Setup second dialog arguments
// ...
SecondDialog *sd = new SecondDialog();
hide();
sd->exec();
accept();
// NB are we leaking sd?
}
By the way:
SecondDialog *sd = new SecondDialog();
sd->exec();
here you are allocating on the heap a dialog without a parent, so either you set the Qt::WA_DeleteOnClose or explicitly call this->deleteLater() inside its code, or you are leaking the dialog instance.
Notes:
and it is explicitly remarked in the documentation
As with QWidget::close(), done() deletes the dialog if the Qt::WA_DeleteOnClose flag is set.
QDialog::accept calls QDialog::done with a dialog code Accepted. Here is how QDialog::done looks like:
void QDialog::done(int r)
{
Q_D(QDialog);
setResult(r);
hide();
d->close_helper(QWidgetPrivate::CloseNoEvent);
d->resetModalitySetByOpen();
emit finished(r);
if (r == Accepted)
emit accepted();
else if (r == Rejected)
emit rejected();
}
which, according to the documentation:
Hides the modal dialog and sets the result code to Accepted.
With this in mind, I think this is not a question of a good practice, but of what your application logic requires.
Although I'm using Qt from Python via PyQt, this question is equally applicable to pure Qt, just the syntax is a bit different, the issue is the same:
When we want to dispose of a QGraphicsItem object in our scene, we call scene.removeItem(item). When we want to dispose of a QGraphicsObject object in our scene, we call scene.removeItem(item) because it derives from QGraphicsItem, but we ALSO call item.deleteLater() because it derives from QObject and that is the recommended way of disposing of QObjects (so that pending signals to and from the item are properly handled).
PROBLEM is that slots in the object item may can get called AFTER the item has been removed from the scene, due to how deleteLater() functions. This requires that we test for self.scene() being None in slots. But this is error prone as it is easy to forget to do that, and forgetting this leads to exception if slot is called.
Another approach is to not call deleteLater() before removing the item from the scene, but this requires manually disconnecting the item from other objects. This has similar disadvantage to testing for self.scene() being None in slots, and its easy to forgot to disconnect a slot.
A better way of mitigating this source of error (if there are no hidden gotchas) would be to NOT call scene.removeItem(item) when item is a QGraphicsObject, and JUST call its deleteLater(): it seems, based on some simple tests, that the scene automatically removes item from its list when it eventually gets destroyed. HOWEVER, I can't find any Qt documentation that states this, and I might have just been lucky; perhaps in a more realistic scenario I would get a memory leak or a crash.
So I'm leaning towards calling deleteLater() without calling removeItem() when item is a QGraphicsObject, do you think this is safe?
Below is the source code for the QGraphicsItem destructor (taken from qt-5.7/qtbase/src/widgets/graphicsview/qgraphicsitem.cpp). As you can see, it does a whole load of cleanup, as well as calling the scene's internal removeItemHelper function (which is also called by removeItem). Thus, it seems well designed to handle removal via deletion.
QGraphicsItem::~QGraphicsItem()
{
if (d_ptr->isObject) {
QGraphicsObject *o = static_cast<QGraphicsObject *>(this);
QObjectPrivate *p = QObjectPrivate::get(o);
p->wasDeleted = true;
if (p->declarativeData) {
if (static_cast<QAbstractDeclarativeDataImpl*>(p->declarativeData)->ownedByQml1) {
if (QAbstractDeclarativeData::destroyed_qml1)
QAbstractDeclarativeData::destroyed_qml1(p->declarativeData, o);
} else {
if (QAbstractDeclarativeData::destroyed)
QAbstractDeclarativeData::destroyed(p->declarativeData, o);
}
p->declarativeData = 0;
}
}
d_ptr->inDestructor = 1;
d_ptr->removeExtraItemCache();
#ifndef QT_NO_GESTURES
if (d_ptr->isObject && !d_ptr->gestureContext.isEmpty()) {
QGraphicsObject *o = static_cast<QGraphicsObject *>(this);
if (QGestureManager *manager = QGestureManager::instance()) {
const auto types = d_ptr->gestureContext.keys(); // FIXME: iterate over the map directly?
for (Qt::GestureType type : types)
manager->cleanupCachedGestures(o, type);
}
}
#endif
clearFocus();
setFocusProxy(0);
// Update focus scope item ptr.
QGraphicsItem *p = d_ptr->parent;
while (p) {
if (p->flags() & ItemIsFocusScope) {
if (p->d_ptr->focusScopeItem == this)
p->d_ptr->focusScopeItem = 0;
break;
}
p = p->d_ptr->parent;
}
if (!d_ptr->children.isEmpty()) {
while (!d_ptr->children.isEmpty())
delete d_ptr->children.first();
Q_ASSERT(d_ptr->children.isEmpty());
}
if (d_ptr->scene) {
d_ptr->scene->d_func()->removeItemHelper(this);
} else {
d_ptr->resetFocusProxy();
setParentItem(0);
}
#ifndef QT_NO_GRAPHICSEFFECT
delete d_ptr->graphicsEffect;
#endif //QT_NO_GRAPHICSEFFECT
if (d_ptr->transformData) {
for(int i = 0; i < d_ptr->transformData->graphicsTransforms.size(); ++i) {
QGraphicsTransform *t = d_ptr->transformData->graphicsTransforms.at(i);
static_cast<QGraphicsTransformPrivate *>(t->d_ptr.data())->item = 0;
delete t;
}
}
delete d_ptr->transformData;
if (QGraphicsItemCustomDataStore *dataStore = qt_dataStore())
dataStore->data.remove(this);
}
Another approach is to not call deleteLater() before removing the item from the scene, but this requires manually disconnecting the item from other objects. This has similar disadvantage to testing for self.scene() being None in slots, and its easy to forgot to disconnect a slot.
First of all, there's no reason to manually remove an item from the scene if your goal is to destroy the item. The scene tracks the item lifetime. So all you need to do is to destroy the item by appropriate means.
If none of the item's methods are on the call stack, simply delete item.
If the item's methods may be on the call stack, use the QObject::deleteLater method.
Qt's classes are mostly well designed and thus follow the Liskov Substitution Principle. The QGraphicsObject is-substitutable-for-a QObject and you can treat it as if it was, indeed, a QObject, without worrying that it happens to be something a QGraphicsItem too.
That's all there's to it. It will solve all your problems in one go.
You almost never have to call scene.removeItem directly: manage the lifetime of the items, and the scene will follow it for you. It's just like the interaction between QWidget and QLayout: widgets that are managed by layouts are still destructible and the layout will forget about the widget when the widget gets destroyed.
From what I understand to make dialog Modeless you have to allocate it on the heap. By Doing something like this:
MyDialog* dlg = new MyDialog(this);
dlg->show();
dlg->raise();
Since exec() ignores Modal Property. However now there is a memory leak since nothing deallocates memory pointed to by dlg pointer until the application is closed. I found one solution here http://tinf2.vub.ac.be/~dvermeir/manuals/KDE20Development-html/ch08lev1sec3.html#ch08list09 at the end of the page and was wondering whether there was a less cumbersome way having Modeless dialog.
You can use the attribute Qt::WA_DeleteOnClose to destroy the window when it is closed/hidden, and QWeakPointer (or QPointer) with a static variable to track the existence of the window inside the slot/function which opens it:
void MyWindow::openDialog() {
static QWeakPointer<MyDialog> dlg_;
if (!dlg_)
dlg_ = new MyDialog(this);
MyDialog *dlg = dlg_.data();
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
dlg->raise();
dlg->activateWindow();
}
I'd schedule it for deletion at the time it's work is finished by using deleteLater:
void MyDialog::MyDialog(QWidget *parent) {
// ...
connect(this, SIGNAL(finished(int)), SLOT(deleteLater)));
}
This approach will preclude you from examining it after the finished signal has been emitted (unless you can guarantee that any accesses happen before everything gets back to the event loop when the deletion is actually performed).
Personally, I would choose between either using
dlg->setAttribute(Qt::WA_DeleteOnClose);
or making the dialog a -dynamically allocated- member i.e. creating it only once:
// constructor
: dialog_(0)
// member function
{
if (! dialog_)
dialog_ = new MyDialog(this);
dialog_->show();
dialog_->raise();
}
This way the dialog is deleted when the parent dies, and only needs to be constructed once.