[SOLVED]Making item visible taking too long when calling C++ functions from QML



  • Now this is a little bit of a complicated issue, if anyone else can think of a better title after I post my question, please do tell ;)

    Anyways, what's happening is this, I have an custom popover that I created that is invisible (visible: false), and I need to make it visible, right after it becomes visible, I have C++ functions being called from QML, so this is basically what I have:
    @
    MyItem {
    id: myItem
    visible: false
    //Other stuff
    }
    MyOtherItem {
    onSomeButtonPressed: {
    myItem.visible = true;
    myC++RegisteredObject.myMethod();
    }
    }
    @

    So when I have something like that, MyItem does not show until AFTER myMethod() is done being called, if I put if(myItem.visible == true) print("TEST"); just before the method call it prints, and if I comment out the method call, it shows instantly.

    How can I get it to wait for myItem to finish getting painted before the flow of control continues? I know there's no setTimout function, but is there something similar I can use? I tried while(myItem.busy) but that didn't seem to work, it doesn't register itself as busy while it's making itself visible.

    Any help is greately appreciated, thanks.



  • Simple solution is to use a "Timer":http://qt-project.org/doc/qt-4.8/qml-timer.html element:
    @
    MyItem {
    id: myItem
    visible: false
    //Other stuff
    }
    MyOtherItem {
    onSomeButtonPressed: {
    myItem.visible = true;
    myTimer.start();
    }
    Timer {
    id: myTimer
    interval: 0
    onTriggered: {
    myC++RegisteredObject.myMethod();
    }
    }
    @



  • I have the same problem too and the timer can't solve the problem
    even it can, it should not be a solution
    who knows which machine should delay how many msec?



  • Find out a solution which don't need to depend on the timer, but more verbose.Execute your process on another thread, you could use QtConcurrent to make your life easier.

    Don't use QtConccurrent::map under mac os x, because it may have some bug.

    "bug":https://bugreports.qt-project.org/browse/QTBUG-32315



  • That looks pretty promising, I've been trying the Timer for a while with no luck, once I have a chance to implement QtConcurrent I'll post wether it worked for me or not.
    [quote author="stereomatching" date="1373959597"]Find out a solution which don't need to depend on the timer, but more verbose.Execute your process on another thread, you could use QtConcurrent to make your life easier.

    Don't use QtConccurrent::map under mac os x, because it may have some bug.

    "bug":https://bugreports.qt-project.org/browse/QTBUG-32315[/quote]



  • Well, I've been trying to use QtConcurrent for this, and I haven't really been able to figure out how to get it to work for this. What I'm doing throughout my app is this:
    C++: Do some stuff, then load QML using QQuickView
    QML: Do stuff in QML, show popup with progress bar (the thing that's not getting painted because threads getting blocked) then call Q_INVOKABLE methods in C++ (the thing blocking the progress bar from showing)
    C++: in the Q_INVOKABLE methods use QFuture *future = QtConcurrent::run(myCFunction);
    myReturnValue = future->result(); (Now this is blocking the progress bar from showing until the thread returns, so it's having the same problem I was having before)

    That's a quick rundown of what's going on, is there another way I can use QtConcurrent that will fix my issue?

    If I use QtConcurrent run from the getgo to launch my QML, so the gui itself is in another thread, how will I have the Q_INVOKABLE methods called from the original thread? In other words:

    Thread 1 create Thread 2 containing QML gui
    Thread 2 call Q_INVOKABLE methods from within QML and have them run back in Thread 1

    I hope this is making sense, if it doesn't please let me know and I'll try to clarify.



  • Can you use a WorkerScript element to do the C++ API calls? If it's in a loop, it can call back to the main thread at the end of each loop with progress, so that you can update your progress bar.

    That way you avoid using QtConcurrent, which adds complexity to your implementation.

    Cheers,
    Chris.



  • bq. Can you use a WorkerScript element to do the C++ API calls?

    Thanks, I don't know qml provide us WorkerScript, would give it a try next time.

    bq. haven’t really been able to figure out how to get it to work for this

    If you want to upgrade progress bar, you could try QtConccurrent::map(as I said, Don’t use QtConccurrent::map under mac os x because there maybe a bug) or QtConccurrent::mapped.

    The idea is, make your heavy job run in the thread of QtConccurrent::mapped, let your QFutureWatcher
    1 : monitor the progress, emit the signal to the qml ProgressBar
    2 : emit exit signal when all of the progress exit

    Code snippets of QtConccurrent::mapped after simplify

    @
    //constructor
    {
    //progressResults_ is QFutureWatcher<int>
    connect(&progressResults_, SIGNAL(progressValueChanged(int)), this, SLOT(setProgressValue(int)));
    connect(&progressResults_, SIGNAL(finished()), this, SLOT(makeProgressExit()));
    }

    void customImage::setProgressValue(int value)
    {
    if(value != progressValue_){
    progressValue_ = value;
    emit progressValueChanged();
    }
    }

    void customImage::makeProgressExit()
    {
    if(progressExit_ != value){
    progressExit_ = value;
    emit progressExitChanged();
    }
    }

    //the codes which call QtConcurrent::mapped
    void customImage::saveImages( QList<QString> names)
    {
    namespace ph = std::placeholders;

    //The return type of the results  is QFuture<int>
    auto results = QtConcurrent::mapped(names, std::bind(&customImage::saveImagesImplement, this, ph::_1));
    progressResults_.setFuture(results);
    

    }

    @

    Hope this help you, or just try WorkerScript, maybe it could make our life easier



  • Well, I wanted to keep most of my control in QML, so I tried a Workerscript, and still had no luck, I was getting segfaults and gdb was unable to pinpoint where in my file they were, when a workerscript executes Q_INVOKABLE C++ code it can't seem to be backtraced, not to mention it seemed like any variables I edited in the thread created by the workerscript weren't effected outside of the thread (which would make sense). However I decided to give Timer another look, and I think I found a solution using Timer, here is what I did:

    @
    Timer {
    id: myC++Method1Timer
    interval: 1
    signal complete()
    onTriggered: {
    myInvokableC++Class.myC++Method1();
    complete();
    }
    onComplete: {
    loadScreen.init(-1, "Title", "Doing Stuff");
    myC++Method2Timer.start();
    }
    }

    Timer {
    id: myC++Method2Timer
    interval: 1
    signal complete()
    onTriggered: {
    result = myInvokableC++Class.myC++Method2();
    complete();
    }
    onComplete: {
    if(result == 0) {
    loadScreen.init(max /sets max of prog bar/, "Prog Bar Title", "Doing Stuff")
    loadScreen.inc("Doing more stuff"); // Increment prog bar & setting text
    myC++Method3Timer.start();
    }
    else myC++Method3Timer.complete();
    }
    }

    Timer {
    id: myC++Method3Timer
    interval: 1
    signal complete()
    signal continued() // This C++ method needs to be called continuously
    property int run: 0
    onTriggered: {
    myInvokableC++Class.myC++Method3(run);
    run++;
    if(run == max) complete();
    else continued();
    }
    onContinued: {
    loadScreen.inc("Continuing to do more stuff");
    myC++Method3Timer.start();
    }
    onComplete: {
    Finish doing stuff in QML here
    }
    }

    onReady: { // ready is a signal in my root element
    loadScreen.init(-1 /sets prog bar to indeterminate/, "Prog Bar Title", "Doing Something Intensive");
    myC++Method1Timer.start();
    }
    @

    The only downside is when my progress bar is set to indeterminite in loadScreen.init(), it's not animated, but that's not that big of a deal, because it doesn't show that long, as long as the normal progress bar is animated, and it is doing it this way, it's fine.

    And I should probably note that loadScreen is a component I created containing progress bar, a title, and a caption. The progress bar is an "Ubuntu component":http://developer.ubuntu.com/api/ubuntu-12.10/qml/mobile/qml-ubuntu-components0-progressbar.html.



  • To sum it up, the trick to using Timer for this, is having a complete signal in each Timer, in my case I have several methods, each needs the progress bar to animate before it's run, and one of the methods needs to be run multiple times incrementing the progress bar before each run, in that method I also have a continued signal. Look at my example above for more.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.