Components not showing right away


#1

I'm trying to show a progress bar (but the same thing happens when I tried with buttons, sliders, any component) after clicking a MenuBar menu item:

 

(simplified code)

void MenuBar::menuItemSelected(int menuItemID, int topLevelMenuIndex){
    switch (menuItemID) {   
        case ID_Open: {
                ProgressBar* pr(new ProgressBar(mapping_editor->getPatchProgress()));
                parent->addAndMakeVisible(pr);
                pr->setBounds(300, 300, 200, 40);
                
                mapping_editor->graph->loadPatch(instrument);
                //the progress bar shows up only after this function is over, not before,
                //so I cannot see it update
                break;
            }
        }
    }
}

 

Why are components only becoming visible when the function ends? The whole purpose of this is to add a progress bar before loading a patch to display the progress while the patch is loading. I tried all sorts of things to get this to work, including calling the loadPatch function from another thread, calling the parent and progress bar's repaint() method after adding it, adding the progress bar as a member before hand and only calling its setVisible() method in the menu callback, etc.


 

 


#2

This would pretty much be the expected behavior under Windows, OS-X, etc. writing natively. The exact details vary, but user input generates a system of events (mouse move, click, menu activation, etc.) which the OS dispatches to an application to process. You are in the middle of processing a menu event, so even dispatching is stopped until you return. Drawing a progress bar requires events to flow...

I'd have to know more about what you are doing to advise on how you might structure your code, but the ThreadWithProgressWindow class would be a good way to execute a chunk of code with a progress bar while letting messages dispatch.

 

Good Luck


#3

I tried subclassing ThreadWithProgressWindow, and it shows itself and runs, but it does not ever "repaint" itself (put in quotes because it isn't a Component)--it is just a box that sits there, frozen, and then when the function returns it catches up to itself and quickly sets the progress from 0 to 100 in a flash.

ProgressWindow* p = new ProgressWindow(mapping_editor->graph->getPatchProgress(), parent, this);
p->launchThread();
mapping_editor->graph->loadPatch(instrument);

right after calling launchThread(), the ProgressWindow shows itself, but the image it displays in the box is nonsense, it doesn't show a progress bar, just random garbage, then execution continues to the loadPatch() function. Once loadPatch() returns, the progress bar immediately catches up to itself, shows 100% for an instant and then immediately closes since the thread detects that the progress is 100%.

I'm not sure what's going on, but I checked that the progress window thread is correctly reading the percentage value (and calling setProgress() with that value) while loadPatch() is being executed.


 


#4

No no no.. you're failing to grasp the way event-driven UIs work!

Everything happens on a callback - mouse events, repaints, timer callbacks, other messages, etc. In an event handler, you NEVER write code that does a long task - it must quickly update whatever components you need, and then immediately return and wait for the next event, because no other events else can possibly be delivered until your callback has finished.


#5

Couldn't I execute a long task on a different thread from the Message thread, though?


#6

Yes, of course - that's the correct way to do it! You do need to be careful with thread-safety issues when modifying your components from another thread though.


#7

Okay, not sure if you read all of the post but I have already tried these things. And yes, I'm aware of MessageManagerLock for when you modify components from another thread. Like I said, I tried (seemingly) everything. Maybe I will just delete it all and rewrite it, or use std::thread instead lol.

 


#8

No.. you said you'd used ThreadWithProgressWindow, but clearly weren't using it correctly if it didn't update (presumably because you were still blocking the event thread). Did you look at the examples of it in the juce demo?

(And it makes little difference whether you use juce::Thread or std::thread - it's basically doing the same thing)


#9

I did also mention that I tried calling loadPatch() on another Thread. Thanks for your help, though. I at least know I'm on the right track.


#10

I fixed it by not executing any code that either creates or alters a Component in the separate thread. Not sure why it was necessary and why the MessageManagerLock didn't do the trick, but I moved all that code back to the Message thread and only did the part that takes a long time (loading the samples) on the other thread.