Results 1 to 5 of 5

Thread: Doing work after QML item is destroyed

  1. #1
    Join Date
    Aug 2006
    Posts
    250
    Thanks
    19
    Thanked 49 Times in 36 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Doing work after QML item is destroyed

    I've got a QML mobile app based around a StackView. Anytime I push a new screen onto the stack I notify a C++ function that the screen is about to load and load the appropriate controller (C++ class which manages the data and other tasks related to this view).

    So to load the controllers I have the following on the QML side:

    Qt Code:
    1. function createScreenObject(path, props) {
    2. // notify the backend that we are about to create a screen.
    3. shellinterface.initScreen(path)
    4. var component, obj
    5. component = Qt.createComponent(Qt.resolvedUrl(path))
    6. if (component.status === Component.Ready) {
    7. obj = component.createObject()
    8. obj.screenName = path
    9. }
    10. else if (component.status === Component.Error)
    11. shellinterface.logError("Screen::createScreenObject(" + path + ")" + " - Error loading component: " + component.errorString())
    12. return {item:obj, properties:props, destroyOnPop:true}
    13. }
    14.  
    15. stack.push(createScreenObject("To-Do Module/TasksToDoView.qml"))
    To copy to clipboard, switch view to plain text mode 

    Then in the C++ backend I have the initScreen function which is similar to:

    Qt Code:
    1. void ShellInterface::initScreen(const QString &identifier) {
    2. if (identifier.endsWith("ContextView.qml")) {
    3. loadController<ContextController>(identifier);
    4.  
    5. }
    6. else if (identifier.endsWith("EmployersView.qml") || identifier.endsWith("AddEditEmployerView.qml"))
    7. loadController<EmployersController>(identifier);
    8. }
    To copy to clipboard, switch view to plain text mode 

    So for each view I can load any number of controllers. This works great, because the controllers are loaded before the QML, so the QML can safely use the controller functions when it loads.

    The problem comes when unloading. Currently all the screens that get loaded onto the StackView are derived from a Screen object. To unload the controller, I have something like:

    Qt Code:
    1. Screen.qml
    2. Rectangle {
    3. property string screenName
    4. Component.onDestruction: shellinterface.deinitScreen(screenName)
    5. }
    To copy to clipboard, switch view to plain text mode 

    This works, except that onDestruction is called before the component is fully done being destroyed. So when I unload the controller I get errors from QML bindings that are still trying to read values from the controller.

    How would I change it from onDestruction to something like onDestroyed? I want to run code after the QML item is totally gone, but I can't see any events I could hook up to.
    Thoughts?

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Doing work after QML item is destroyed

    Two ideas:

    1) in deinitScreen() don't unload but schedule unloading, e.g. using QMetaObject::invokeMethod with Qt::QueuedConnection.

    2) Register the controllers as QML instantiable types and let each screen just create its controller objects. Like any other children they will then be deleted with the screen

    Cheers,
    _

  3. #3
    Join Date
    Aug 2006
    Posts
    250
    Thanks
    19
    Thanked 49 Times in 36 Posts
    Qt products
    Qt4
    Platforms
    Windows

    Default Re: Doing work after QML item is destroyed

    Quote Originally Posted by anda_skoa View Post
    Two ideas:

    1) in deinitScreen() don't unload but schedule unloading, e.g. using QMetaObject::invokeMethod with Qt::QueuedConnection.
    I've been experimenting with this. I think it is working, but hard to tell if this is actually a guarantee that the controller will be deleted after the object. Since destroy() schedules deletion of the QML and then I schedule deletion of the controller I think the order is undefined.

    2) Register the controllers as QML instantiable types and let each screen just create its controller objects. Like any other children they will then be deleted with the screen

    Cheers,
    _
    This might be the way to go. It would also be cleaner because right now the controllers do a setContextProperty("someController", this) in their constructors, and since there is no way to remove context properties I just set them to null when the controller is destroyed.
    Only thing is that sometimes different QML screens have the same controller. Right now I manage this with QSharedPointers so they are created and then destroyed when the last reference to them is popped off the stack. I wonder can you have a C++ singleton object that can be instantiated from QML?

  4. #4
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,416
    Thanks
    37
    Thanked 1,544 Times in 1,494 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Doing work after QML item is destroyed

    Quote Originally Posted by pherthyl View Post
    I've been experimenting with this. I think it is working, but hard to tell if this is actually a guarantee that the controller will be deleted after the object. Since destroy() schedules deletion of the QML and then I schedule deletion of the controller I think the order is undefined.
    Delayed invocation is implemented using events and those are queued (FIFO), so if the delayed invocation for destroy is scheduled first it should come first.

    Anyway

    Quote Originally Posted by pherthyl View Post
    Only thing is that sometimes different QML screens have the same controller. Right now I manage this with QSharedPointers so they are created and then destroyed when the last reference to them is popped off the stack. I wonder can you have a C++ singleton object that can be instantiated from QML?
    QML2 (Qt5) has singleton objects.
    However you can also easily achieve that by an indirection:
    Qt Code:
    1. class ControllerQmlPart : public QObject
    2. {
    3. Q_OBJECT
    4. Q_PROPERTY(QString someProperty READ someProperty NOTIFY somePropertyChanged);
    5.  
    6. public:
    7. explicit ControllerQmlPart(QObject *parent = 0);
    8.  
    9. QString someProperty() const;
    10.  
    11. Q_SIGNALS:
    12. void somePropertyChanged();
    13. };
    To copy to clipboard, switch view to plain text mode 
    Qt Code:
    1. ControllerQmlPart(QObject *parent) : QObject(parent)
    2. {
    3. connect(ControllerSingletonPart::instance(), SIGNAL(somePropertyChanged()), this, SIGNAL(somePropertyChanged()));
    4. }
    5.  
    6. QString ControllerQmlPart::someProperty() const
    7. {
    8. return ControllerSingletonPart::instance()->someProperty();
    9. }
    To copy to clipboard, switch view to plain text mode 

    Cheers,
    _

  5. The following user says thank you to anda_skoa for this useful post:

    pherthyl (21st February 2014)

  6. #5
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Doing work after QML item is destroyed

    An alternative:

    Qt Code:
    1. class ControllerProxy : public QObject {
    2. Q_OBJECT
    3. Q_PROPERTY(QObject* controllerType1 READ controllerType1 CONSTANT)
    4. Q_PROPERTY(QObject* controllerType2 READ controllerType2 CONSTANT)
    5. public:
    6. ControllerProxy(QObject *parent = 0) : QObject(parent) {}
    7. QObject* controllerType1() const { return ControllerType1::instance(); }
    8. QObject* controllerType2() const { return ControllerType2::instance(); }
    9. };
    To copy to clipboard, switch view to plain text mode 

    javascript Code:
    1. ControllerProxy {
    2. id: controller
    3. }
    4.  
    5. Connections {
    6. target: controller.controllerType1
    7. onSomeSignal: ...
    8. }
    To copy to clipboard, switch view to plain text mode 

    First use of instance() would of course instantiate a specific controller type.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  7. The following user says thank you to wysota for this useful post:

    pherthyl (21st February 2014)

Similar Threads

  1. Replies: 19
    Last Post: 25th November 2010, 08:52
  2. Destroyed while process is still running
    By qtzcute in forum Qt Programming
    Replies: 5
    Last Post: 23rd July 2009, 08:26
  3. Object destroyed, what about connections?
    By Caius Aérobus in forum Qt Programming
    Replies: 1
    Last Post: 7th July 2008, 17:41
  4. remove item for QGirdLayout doesn't work.
    By klnusbaum in forum Qt Programming
    Replies: 4
    Last Post: 23rd May 2008, 23:04
  5. [Qt 4.1.3] Child widget not destroyed
    By Dusdan in forum Qt Programming
    Replies: 13
    Last Post: 24th May 2006, 08:17

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.