Background download Thread Progress Windows problem


#1

Hi,
I prepared simple demo app to download files from URL. As files size can be large i created separate thread to do work. In my UI there are two buttons - one for download, the other one wchich doing something else.
Until i use only Thread class to run the thread i had an access to buttons in my UI during download. After creating progressbar window and changing class Thread with ThreadWithProgressWindow i am completely loosing control on my UI until download is complete. Is it a way to take progressbar thread to the background so i will be able to do the other tasks on UI? Any help appreciated.


#2

It looks like ThreadWithProgressWindow is not suitable for what you want. You will need to figure out some way to show the progress non-modally yourself…


#3

Thanks for your response. So i suppose i need to create custom window using ProgressBar class. I am a bit suprised there is no option for making progress window non-modal or to hide window to use the other dialog in app.


#4

Yes, if you don’t call runThread, but rather launchThread:

bool runThread (int priority=5)
Starts the thread and waits for it to finish.

void launchThread (int priority=5)
Starts the thread and returns.


#5

Nope, that doesn’t work, the progress window blocks the other GUI from working with that one too. (At least on OS-X.)


#6

If you don’t want a blocking window then this isn’t really the right class to use, it doesn’t add very much… Typically if you just have a background task that isn’t blocking, you’d just run it on a thread and show your own UI somewhere to give the user some feedback. Not sure how ThreadWithProgressWindow could help very much with that.


#7

Well, instead of it being a modal dialog window, it could just be a component that could be added to a GUI or to the desktop…


#8

My bad, I overlooked in the docs:

A thread that automatically pops up a modal dialog box with a progress bar

So launchThread will allow the message thread to run, but intentionally blocks user interaction.

The class to look at is probably ThreadPoolJob.

Just adding, have a look at this talk:
https://skillsmatter.com/skillscasts/11632-audio-developers-meet-up-april

…just realised, that the page doesn’t show the name, it was given by Dave Rowland


#9

Thank you all for help. I decided to create a new window with ProgressBar object. Is it a simple way to increment in loop through ProgressBar object to refresh progress variable without creating a new instance each time? I am looking for function similar to setProgress in ThreadProgressWindow.


#10

ProgressBar takes in a reference to the floating point number that is used as the progress amount. Then you can just change the referred to number to get the progress bar to change. (Obviously that number has to be a member variable somewhere so that it sticks around and doesn’t cause a dangling reference in the ProgressBar.)


#11

Hmm… I am incrementing progress value in a loop but it seems it passes only last value of progress. Most likely I am doing something wrong. Sample of code below:

class MainContentComponent : public URL,
							public Component,
							public Thread
{
public:
	MainContentComponent() : Thread("Background Download Thread"), pb(progress)
	{
	
	addAndMakeVisible(&downloadButton);
	downloadButton.setButtonText("Select folder to download file from URL...");
	downloadButton.onClick = [this] { downloadButtonClicked(); };
	
	addAndMakeVisible(pb);

	progress = 0.0;		

	setSize(300, 200);
}

~MainContentComponent()
{
	stopThread(3000);
}

void resized() override
{
	downloadButton.setBounds(10, 10, getWidth() - 20, 20);
	//Other buttons
	pb.setBounds(10, 130, getWidth() - 20, 20);
}
	
private:
	void downloadButtonClicked()
	{
		FileChooser chooser("Select path");
		(File::getCurrentWorkingDirectory());

	if (chooser.browseForDirectory())
	{
		//....			
		startThread();
	}
}

void run() override
{
	while (!threadShouldExit())
	{
		URL fileUrl("http://www.samplewebsite.com/file.mp3");

		String filename = fileUrl.getFileName();
		ScopedPointer<InputStream> fileStream = fileUrl.createInputStream(false);

		// Reading file into buffer...

		FileOutputStream out(localfile);

		double j = mem.getSize();

		for (int i = 0; i < mem.getSize(); ++i)
		{
			out.writeByte(mem[i]);
			progress += i / j; // HERE PROGRESSBAR SHOULD MOVE
		}

		//...

		signalThreadShouldExit();
	}
}

//==========================================================================
TextButton downloadButton;

ProgressBar pb;
double progress;

File chosenPath;

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MainContentComponent)

};


#12

Have you checked your progress calculation ends up producing floating point numbers between 0.0 and 1.0?


#13

I don’t see in your code, how a change in the progress variable would be either forwarded to a progressBar/Slider or a repaint is scheduled…


#14

The ProgressBar has an internal timer that does the repaints. (And it takes the progress variable as a reference.)


#15

I see, got it. Never used ProgressBar. I should read first :wink:


#16

There’s also the really annoying possibility the compiler decides to optimize the progress calculation away, are you testing with a debug or release build?


#17

In use with separate method i.e.

void ButtonClicked() {	progress += 0.1; }

progressBar is moving after each button click. When i try to loop this (from 0.0 to 1.0) it ends with 100%.


#18

Maybe the code is just so fast the progress bar never ends up showing anything else but the 100%? (The repaint timer in the progress bar doesn’t run at super fast intervals if I recall right.)


#19

I tested with Time::waitForMillisecondCounter(400); and same result. It seems that progress value is monitored only after it exits out of the range of function. I am wondering how it can be useful with such limitations.


#20

Thread::sleep seems more appropriate in this situation