David Levy
David Levy

Reputation: 511

Qt test current object/widget existence

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

Answers (2)

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.

First Transformation

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);
}

Second Transformation

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

goug
goug

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

Related Questions