> I've explained the problems with referencing the game object in the bug report. :)
That's why I moved player.destroy(); out of the if (state == "invalid") block. But my last comment applies: unless there's a way to track explicitly the objects' lifetimes in QML I don't see how you can fix this. On Thu, Mar 24, 2016 at 7:21 PM, Curtis Mitch <mitch.cur...@theqtcompany.com > wrote: > I've explained the problems with referencing the game object in the bug > report. :) > > > ------------------------------ > *From:* Nye <kshegu...@gmail.com> > *Sent:* Thursday, 24 March 2016 17:45 > > *To:* Curtis Mitch > *Cc:* interest@qt-project.org > *Subject:* Re: [Interest] Ensuring that a queued invocation occurs after > deferred deletion > > > The "loadedComponents" thing is more or less the same as the GameScope > thing (I ended up calling it GameReference), except you need to remember to > increment the count yourself, whereas with GameScope, it does it > automatically. > > Mostly yes, except that this is tied to the loader, not to the loaded item. > > > The problem with the code you posted is as I mentioned earlier: Loader > never immediately deletes its items, so the code would still reference > the game object when it shouldn't. > > Code referencing the game object should be perfectly fine until all the > loaders have finished unloading their respective components (which I assume > is signaled by loader.status == Loader.Null), then you know that you can > destroy() whatever you wish as destroying it will happen (even if loaders > queue their destructions) after all the loaded components have been > destroyed. Isn't this what you wanted in the first place, or am I > misunderstanding? > > On Thu, Mar 24, 2016 at 1:53 PM, Curtis Mitch < > mitch.cur...@theqtcompany.com> wrote: > >> The "loadedComponents" thing is more or less the same as the GameScope >> thing (I ended up calling it GameReference), except you need to remember to >> increment the count yourself, whereas with GameScope, it does it >> automatically. >> >> >> The problem with the code you posted is as I mentioned earlier: Loader >> never immediately deletes its items, so the code would still reference >> the game object when it shouldn't. >> >> >> ------------------------------ >> *From:* Nye <kshegu...@gmail.com> >> *Sent:* Thursday, 24 March 2016 11:25 >> >> *To:* Curtis Mitch >> *Cc:* interest@qt-project.org >> *Subject:* Re: [Interest] Ensuring that a queued invocation occurs after >> deferred deletion >> >> Okay, >> It might sound stupid, but why not notify the game from your loaders? >> Supposedly setting the source of the loader to NULL will unload the >> component (at least that's what the documentation says). >> Something like this (please bear with my tortured QML knowledge): >> >> >> Item { >> id: theGame >> >> property bool isReady: false >> property int loadedComponents: 0 >> >> onStateChanged: { >> if (state == "invalid") { >> print("game.isReady about to change to false...") >> isReady = false; >> print("... game.isReady changed to " + isReady); >> } else if (state == "running") { >> player = Qt.createQmlObject("import QtQuick 2.0; Item { >> property color color: 'black' }", window); >> >> print("game.isReady about to change to true...") >> isReady = true; >> print("... game.isReady changed to true") >> } >> else if (state == "finished") { >> // ... Everything was cleaned now ... supposedly >> } >> } >> >> onLoadedComponentsChanged: { >> if (state == "invalid" && loadedComponents == 0) { >> player.destroy(); >> player = null; >> >> state = "finished"; >> print("... game.state changed to finished; nothing should >> reference properties of game now"); >> } >> } >> >> property Item player >> } >> >> Loader { >> id: loader >> >> Connections { >> target: theGame >> onIsReadyChanged: { >> if (theGame.isReady) { >> loader.setSource("qrc:/LoaderItem.qml", { "game": theGame >> }); >> theGame.loadedComponents++; >> } else { >> loader.source = null; >> } >> } >> } >> >> onStatusChanged: { >> if (loader.status == Loader.Null) { >> theGame.loadedComponents--; >> } >> } >> } >> >> On Thu, Mar 24, 2016 at 10:48 AM, Curtis Mitch < >> mitch.cur...@theqtcompany.com> wrote: >> >>> The problem with doing this is that you'd still need to identify the >>> Loaders that are relevant to (have references to) the game, so you'd end up >>> with more or less the same amount of extra QML code (a line or two). It >>> also ties it to Loaders, but the same problem exists with the destroy() >>> JavaScript function, for example. >>> >>> >>> ------------------------------ >>> *From:* Nye <kshegu...@gmail.com> >>> *Sent:* Thursday, 24 March 2016 01:41 >>> *To:* Curtis Mitch >>> >>> *Cc:* interest@qt-project.org >>> *Subject:* Re: [Interest] Ensuring that a queued invocation occurs >>> after deferred deletion >>> >>> Hello, >>> > At one stage I thought about having a C++ object that could be created >>> in QML and would somehow keep count of references to the game object. For >>> example, each Loader whose source component has access to the game object >>> would somehow register itself with the object, and the game wouldn’t start >>> quitting until all references were gone. As long as the C++ doesn’t know >>> about the UI, I think it could work quite well. >>> >>> Unfortunately my QML knowledge is quite rudimentary, however I believe >>> (correct me if I'm wrong) each component is a `QObject` instance and is >>> parented to the parent component and so on until you reach the root >>> context. So one thing that comes to mind is to "spy" (by installing an >>> event filter) on the root context for when children are added or removed >>> (QEvent::ChildAdded & QEvent::ChildRemoved) and if the children are Loaders >>> then count them up. Respectively when they're destroyed you decrease the >>> count and when it goes to zero you can unload/clean up. This approach would >>> (hopefully) lift the need to do this: >>> >>> GameScope { >>> >>> game: root.game >>> >>> } >>> >>> >>> Kind regards. >>> >>> On Wed, Mar 23, 2016 at 10:41 AM, Curtis Mitch < >>> mitch.cur...@theqtcompany.com> wrote: >>> >>>> Hi. >>>> >>>> >>>> >>>> That does help, thanks. It means that I’d really need to use an >>>> arbitrarily long timer, or find the “proper” solution. >>>> >>>> >>>> >>>> At one stage I thought about having a C++ object that could be created >>>> in QML and would somehow keep count of references to the game object. For >>>> example, each Loader whose source component has access to the game object >>>> would somehow register itself with the object, and the game wouldn’t start >>>> quitting until all references were gone. As long as the C++ doesn’t know >>>> about the UI, I think it could work quite well. >>>> >>>> >>>> >>>> Something like this: >>>> >>>> >>>> >>>> Loader { >>>> >>>> // ... contains GameView >>>> >>>> } >>>> >>>> >>>> >>>> // GameView.qml >>>> >>>> >>>> >>>> Item { >>>> >>>> id: root >>>> >>>> property alias game >>>> >>>> >>>> >>>> GameScope { >>>> >>>> game: root.game >>>> >>>> } >>>> >>>> } >>>> >>>> >>>> >>>> // GameScope.cpp >>>> >>>> >>>> >>>> GameScope::setGame(Game *game) >>>> >>>> { >>>> >>>> if (game == mGame) >>>> >>>> return; >>>> >>>> >>>> >>>> if (game) >>>> >>>> game->increaseReferenceCount(); >>>> >>>> else >>>> >>>> game->decreaseReferenceCount(); >>>> >>>> >>>> >>>> mGame = game; >>>> >>>> } >>>> >>>> >>>> >>>> GameScope::~GameScope() >>>> >>>> { >>>> >>>> if (game) >>>> >>>> game->decreaseReferenceCount(); >>>> >>>> } >>>> >>>> >>>> >>>> >>>> >>>> Each event loop after a quit has been requested, Game could check the >>>> reference count and begin the actual quitting if it’s 0. >>>> >>>> >>>> >>>> It still feels like it shouldn’t be necessary, but at least there’s no >>>> guesswork involved. >>>> >>>> >>>> >>>> *From:* Nye [mailto:kshegu...@gmail.com] >>>> *Sent:* Tuesday, 22 March 2016 10:33 PM >>>> *To:* Curtis Mitch <mitch.cur...@theqtcompany.com> >>>> *Cc:* interest@qt-project.org >>>> *Subject:* Re: [Interest] Ensuring that a queued invocation occurs >>>> after deferred deletion >>>> >>>> >>>> >>>> Hello, >>>> >>>> I don't work with QML, but I'm pretty sure the events are processed in >>>> the order of their appearance in the event queue. So if you have a >>>> `deleteLater` call (i.e. you have a QEvent::DeferredDelete, which is >>>> scheduled through the event loop) any queued call to a slot (i.e. >>>> QEvent::MetaCall) that was made before the deletion request should be >>>> happening before the actual deletion. So, if you're emitting signals from a >>>> single thread, their respective slots would be called in the order in which >>>> the signals had happened. >>>> >>>> Now, with multiple threads it's a bit tricky, since there's the chance >>>> that two threads will be trying to post a deferred function invocation at >>>> the same time (hence the event queue is protected by a mutex). However that >>>> mutex can't guarantee in what order the events will be posted, or rather no >>>> one can. >>>> >>>> > My only thought is to use a zero-second single-shot timer. The >>>> question is: is this guaranteed to happen *after* deferred deletion for a >>>> given iteration of an event loop? >>>> >>>> This posts a timer event on the queue, so you can achieve the same with >>>> QMetaObject::invokeMethod(receiverObject, "method", Qt::QueuedConnection), >>>> and the same "restrictions" apply as mentioned above. >>>> >>>> I hope this is of help. >>>> Kind regards. >>>> >>>> >>>> >>>> On Tue, Mar 22, 2016 at 7:50 PM, Curtis Mitch < >>>> mitch.cur...@theqtcompany.com> wrote: >>>> >>>> >>>> I recently discovered [1] that Loader defers deletion of items via >>>> deleteLater(). Up until that point, I had been treating certain operations >>>> in my program as synchronous (as I haven't introduced threads yet). Now >>>> that I can't safely assume that UI items will be instantly destroyed, I >>>> have to convert these operations into asynchronous ones. >>>> >>>> For example, previously, I had this code: >>>> >>>> game.quitGame(); >>>> >>>> My idea is to turn it into this: >>>> >>>> game.requestQuitGame(); >>>> >>>> Within this function, the Game object would set its "ready" property to >>>> false, emitting its associated property change signal so that Loaders can >>>> set active to false. Then, QMetaObject::invoke would be called with >>>> Qt::QueuedConnection to ensure that the Loader's deleteLater() calls would >>>> have been carried out *before* tearing down the game and its objects. >>>> >>>> In order to confirm that invokeMethod() works the way I thought it did, >>>> I added the following debug statements to QEventLoop: >>>> >>>> diff --git a/src/corelib/kernel/qeventloop.cpp >>>> b/src/corelib/kernel/qeventloop.cpp >>>> index dca25ce..7dae9d0 100644 >>>> --- a/src/corelib/kernel/qeventloop.cpp >>>> +++ b/src/corelib/kernel/qeventloop.cpp >>>> @@ -151,6 +151,7 @@ bool QEventLoop::processEvents(ProcessEventsFlags >>>> flags) >>>> >>>> \sa QCoreApplication::quit(), exit(), processEvents() >>>> */ >>>> +#include <QDebug> >>>> int QEventLoop::exec(ProcessEventsFlags flags) >>>> { >>>> Q_D(QEventLoop); >>>> @@ -200,8 +201,11 @@ int QEventLoop::exec(ProcessEventsFlags flags) >>>> if (app && app->thread() == thread()) >>>> QCoreApplication::removePostedEvents(app, QEvent::Quit); >>>> >>>> - while (!d->exit.loadAcquire()) >>>> + while (!d->exit.loadAcquire()) { >>>> + qDebug() << Q_FUNC_INFO << "--- beginning event loop"; >>>> processEvents(flags | WaitForMoreEvents | EventLoopExec); >>>> + qDebug() << Q_FUNC_INFO << "--- ending event loop"; >>>> + } >>>> >>>> ref.exceptionCaught = false; >>>> return d->returnCode.load(); >>>> >>>> It turns out that I misunderstood the documentation; it only says that >>>> the slot is invoked when control returns to the event loop of the >>>> receiver's thread. So, as I understand it, it's possible that the >>>> invocation could happen *before* the deferred deletion of the Loaders' >>>> items. As the documentation doesn't specify the order between these two >>>> things, I should probably assume that it's not safe to assume anything. >>>> >>>> So, I'm left with the problem of how to ensure that a slot is invoked >>>> after the Loaders' items have been destroyed. My only thought is to use a >>>> zero-second single-shot timer. The question is: is this guaranteed to >>>> happen *after* deferred deletion for a given iteration of an event loop? I >>>> can't see such a guarantee in the documentation. I even checked the source >>>> code of e.g. qeventdispatcher_win.cpp to see if I could find anything, >>>> without success. >>>> >>>> Another question that's in the back of my mind is: is there a better >>>> way to do this? >>>> >>>> [1] https://bugreports.qt.io/browse/QTBUG-51995 >>>> [2] http://doc.qt.io/qt-5/qt.html#ConnectionType-enum >>>> _______________________________________________ >>>> Interest mailing list >>>> Interest@qt-project.org >>>> http://lists.qt-project.org/mailman/listinfo/interest >>>> >>>> >>>> >>> >>> >> >
_______________________________________________ Interest mailing list Interest@qt-project.org http://lists.qt-project.org/mailman/listinfo/interest