Reputation: 511
In Qt I have to wait for 1 second and let event process so I use
QTime lDieTime= QTime::currentTime().addSecs(1);
while (QTime::currentTime() < lDieTime)
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
But after this while, I do operation on the object ( this->mListIP.currentItem();
)
This work fine if the object has not been destroyed during the processEvents. But the object can be destroyed during this loop.
How can I test that the widget/object still exist?
I have tried if(!this->isVisible())
but it crash (read access violation) because this does not exist anymore
My problem is related to this question
So if I can't check if this exist, how can I stop my function if the object is destroyed? Something in the destructor? A try-catch (but I dont like that)
Note : the function is called by a timerEvent from a QBasicTimer every 2 seconds
Edit : structure of the code :
Class A (Widget)
-constructor start a QBasicTimer of 2 seconds
-timerEvent, called by the QBasicTimer every two seconds
Layout containing Class A can destroy Class A object
Inside the timerevent, i start an operation, wait for 1 seconds, then try to update the widget. But I cant update it if it's been destroyed
edit2 : more code
void MaryAnnConnectionWidget::timerEvent( QTimerEvent * /*aEvent*/ )
{
//operation base on a socket and a udp broadcast
...
QTime lDieTime= QTime::currentTime().addSecs(1);
while (QTime::currentTime() < lDieTime)
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
//Operation to handle answers from the broadcast
...
if(this==nullptr) //Exit if we changed window <= CRASH HERE if widget destroyed
return;
//Handle the answers and displays results
...
}
Upvotes: 0
Views: 1513
Reputation: 98425
Your question is a sort of an XY problem. You thought your problem was about how to track object's lifetime. One solution is to use a QPointer
to do that. Another is not to have to explicitly track it and let the framework ensure that your code doesn't run on objects that aren't there anymore. So the solutions are complementary, but it's best to avoid the problem in the first place. Below I'll explain how to do so.
Suppose that you start with the following horrible code. You must factor out doBefore()
and doAfter()
into stand-alone methods:
void Class::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
doBefore();
auto endTime = QTime::currentTime().addSecs(1);
while (QTime::currentTime() < endTime)
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
doAfter(mListIP.currentItem());
}
Then it becomes easy to address your concerns by performing two transformations on the code.
In Qt I have to wait for 1 second and let event process so I use
No, you don't. You have to run your code after one second has elapsed. Here's how:
// Qt 5
void Class::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
doBefore();
QTimer::singleShot(1000, this, [this]{ doAfter(mListIP.currentItem()); });
}
// Qt 4
void Class::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
doBefore();
m_currentItem = mListIP.currentItem()
QTimer::singleShot(1000, this, SLOT(doAfterCurrent()));
}
void Class::doAfterCurrent() { // must be a slot
doAfter(m_currentItem);
}
I do operation on the object ( this->mListIP.currentItem();)
If you run the functor/slot in that object's context, the functor/slot won't execute if the object dies. Thus:
// Qt 5
void Class::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
doBefore();
auto item = mListIP.currentItem();
Q_ASSERT(!item || this.findChildren<QObject*>().contains(item));
// vvvv -- note the changed context!
QTimer::singleShot(1000, item, [this,item]{ doAfter(item); });
}
// Qt 4
void Class::timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
doBefore();
m_currentItem = mListIP.currentItem();
Q_ASSERT(!m_currentItem || this.findChildren<QObject*>().contains(m_currentItem));
// vvvvvvvvvvvv -- note the changed context!
QTimer::singleShot(1000, m_curentItem, SLOT(doAfterCurrent()));
}
void Class::doAfterCurrent() { // must be a slot
doAfter(m_currentItem);
}
Upvotes: 3
Reputation: 2444
If you need a safe pointer to a QObject, use QPointer. It will become null when the object is destroyed. It isn't clear how your code is constructed and where you're trying to do this, but you can also connect to the object's "destroyed" signal, and tie that to a slot that aborts the timer so that it doesn't attempt to reference the object.
Upvotes: 1