wxThread

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

wxThread

Post by toadybarker »

Dear All,
I have a wxThread which I am attempting to use to show a "Busy" Window. Basically - I am calling an outside WEB Process in my program, and have to wait for the process to end (which can take awhile). I just want the user to know it is loading/Unloading the file. (The process does not give me any progress - so I am just pulsing the bar so it appears to be doing something - but I digress.
This is working fine in my Windows installation of the code, but in ln CentOs version - same code - I never see the busy window at all - never displays.

Anyone have any idea as to what I can do to get this window to show up.

At one point I ran it with gdb, and sometimes I would see the window. Just not sure why this works in one OS, but not the other.

I can certainly include the code if that helps.... but I very simply instantiate a new thread (called MyProgressThread),
and when my web function finishes - I call the delete method by the MyProgress pointer, then call the wait method by that pointer, then delete the pointer to MyProgressThread.

Any ideas that you might have would be greatly appreciated. Thanks.

Todd B.
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

wxWidgets doesn't allow GUI operations from secondary threads. And if the main application is blocked anyway, just use a wxProgressDialog from the main thread.
Use the source, Luke!
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

Re: wxThread

Post by toadybarker »

Hmmm... well I dont completely understand your answer - but maybe I get the second part...

Firstly, in windows it does start a busy window for me....so why does that thread work at all...?

Secondly, I am calling a function which will not return for quite a while. So I guess you are saying put up a progress thread, call my function,
and when I return - take down the progress dialog. Of course I have no progress data to report, so I will have to make it a busy window that appears to be doing somthing. Is that what you are saying?

Todd
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

Technical aspects aside, from a GUI point of view there are only two options:

a) The user can do something else while you're calculating something else. In that case you don't need any modal "busy" indicator, at most a non-intrusive progress indicator.

b) If the user can't do anything else during that time, usually a progress dialog will be displayed. This must be done from the main thread though, not from inside the worker thread
Use the source, Luke!
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

Re: wxThread

Post by toadybarker »

Ok. I think I see what you are saying ... maybe I can rerrite this to use a progress dialog....

Something like create a new progress dialog pointer -
and just set the Gauge to be like
wxGauge * myGauge = new wxGauge(frame, wxID_ANY, 300, wxDefaultPosition, wxDefaultSize, wxGAUGE_EMULATE_INDETERMINATE_MODE);

Update it with a Pulse...
Then call the Web function and when it returns -
delete the pointer to the dialog.....

Something like that?
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

Can you explain i more detail what you're trying to do? I don't want to send you in the wrong direction.

Usually, if you have a longer running task that would normally block the GUI, you'd have a code skeleton like this (at least that's how i do it):

Code: Select all

int status = 0;			// the thread will change this variable with status information
SomeThread *worker_thread = new SomeThread( &status );
if( worker_thread->Create()==wxTHREAD_NO_ERROR )
{
  worker_thread->Run();

  wxProgressDialog dlg("name", "message", 100, NULL);
  dlg.CenterOnScreen();

  while( status!=FINISHED_OK && status!=FINISHED_ERROR )
  {
    dlg.Pulse();      // or Update() if you have information about the progress
    ::wxMilliSleep(100);
  }
}
Use the source, Luke!
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

Re: wxThread

Post by toadybarker »

Hi doublemax,
Thanks for the code snippet. But I don't think that's gonna work.

What I am trying to do isn't something that will be easily done in a thread unfortunately. Way too many moving parts. I guess it's possble - but would be extremely complicated I think.
This is a mixing software application (Audacity to be exact). I am attempting to get audio out of a completely different system - Or - Put audio
into it. To do this I call a C API Library I wrote - which builds a LibCurl call to a Web API in the other system. The Web API returns either the audio file or passes the audio file to the other system via http:. The Web API doesn't tell me it's progress , it just sends/receives the data and a return code of success or failure.
The process of getting an audio file can take forever, from a user's perspective, and I just wanted to let them know it's working, in a visually standardized way. That's why I made a thread to display a progress window, which seemed to work fine in Windows.....

Thanks for any ideas you may have on making this work. Have tried making a new progress dialog window using a class that exists already for doing just that, but it never displays for me, not sure what I did wrong...

Tb
bigpilot
I live to help wx-kind
I live to help wx-kind
Posts: 184
Joined: Tue Sep 19, 2006 8:33 am

Re: wxThread

Post by bigpilot »

The best way to deal with UI and threading is to use custom messages.

I'd open the progress dialog in the UI thread and then start a worker thread. Use the custom messages to signal progress (you can send anything back to the UI thread) to the dialog in the UI thread.
Soon to be world famous ;)
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

That's why I made a thread to display a progress window, which seemed to work fine in Windows.....
So the main thread is blocked and doesn't process any events? Don't you get the "white" faded screen under Windows and the "Not responsing" message? That doesn't sound like a nice solution.
I am attempting to get audio out of a completely different system - Or - Put audio into it. To do this I call a C API Library I wrote - which builds a LibCurl call to a Web API in the other system. The Web API returns either the audio file or passes the audio file to the other system via http:. The Web API doesn't tell me it's progress , it just sends/receives the data and a return code of success or failure.
This sounds like it would be perfect to put it into a worker thread. Does it have too many dependencies on GUI code?

If that's not possible, i can't think of any quick workaround.

Can you post a link to the code part?
Use the source, Luke!
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

Re: wxThread

Post by toadybarker »

That's why I made a thread to display a progress window, which seemed to work fine in Windows.....
So the main thread is blocked and doesn't process any events? Don't you get the "white" faded screen under Windows and the "Not responsing" message? That doesn't sound like a nice solution.
------------------------------------------------------------------------------------------------------------------------------

- No actually in windows - I get a little windows that says - Rivendell Processing File.... and it puts a indeterminate pulsing gauge bar up... and closes it when it returns from the call.

The Process thread code looks like this..(MyProgressThread.cpp)

Code: Select all

/*   This process displays a wait - processing window                 *
 *    The window is killed by the calling process when the caller     *
 *    is finished doing the processing. We do this for Rivendell      *
 *    Imports and Exports so User knows that it's still busy.         */

MyProgressThread::MyProgressThread() : wxThread(wxTHREAD_JOINABLE)
{
	if (wxTHREAD_NO_ERROR == Create()) {
		Run();
	}
}

MyProgressThread::~MyProgressThread()
{
}

wxThread::ExitCode MyProgressThread::Entry()
{
	wxFrame* frame = new wxFrame(NULL, wxID_ANY, _("Rivendell Transfering File - Please Wait"));
	frame->CenterOnParent();
	frame->Show(true);
	wxGauge * myGauge = new wxGauge(frame, wxID_ANY, 300, wxDefaultPosition, wxDefaultSize, wxGAUGE_EMULATE_INDETERMINATE_MODE);
	myGauge->CenterOnParent();
	
	myGauge->Pulse();
		// do something here that takes a long time
		// it's a good idea to periodically check TestDestroy()
	while (!TestDestroy()) {
		wxMilliSleep(10);
		myGauge->Pulse();
	}
	delete myGauge;
	delete frame;

	return static_cast<ExitCode>(NULL);
}
Inside my Main code I do this...

Code: Select all

               // Start Progress Bar In Another Thread
		MyProgressThread* myprogressthread = new MyProgressThread();
		.
		.
		. do function call
		.
		.
		// Kill the progress Thread
		myprogressthread->Delete();
		myprogressthread->Wait();
		delete myprogressthread;
		
This worked fine in Windows....never appears at all in Linux......
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

I was more interesting in the code that you said can't be put into a thread.

Even if this seems to work under Windows, this is far beyond the "righteous" wxWidgets path which strictly says that only the main thread is allowed to do GUI operations.
Use the source, Luke!
toadybarker
Earned a small fee
Earned a small fee
Posts: 13
Joined: Wed Aug 12, 2015 2:29 pm

Re: wxThread

Post by toadybarker »

Well. this is a bigger snippet of the Export code.

Code: Select all

 p->AS_SetRate(samplerate); 
    if (mSaveSelection)
    {
      exportstatus = e->Export(p, channels, fName, true, p->GetSel0(), p->GetSel1(), ms, false,1);
    }
    else
    {
      exportstatus = e->Export(p, channels, fName, false, 0.f, p->GetTracks()->GetEndTime(), ms, false,1);
    }
    p->AS_SetRate(samplerate);
	EndModal(wxID_OK);
	// Need to check exportstatus and if failed turn all days OFF

    if (exportstatus != 1)  {
        strcat(rd_title,"  - EXPORT FAILURE - Length may be Corrupted - ");
		Export_Failure(rd_title,
			cutName.c_str(),
			cartNewNumber,
			(_("The Export appears to have FAILED !!!")));
		return;
	}
	
	delete ms;
	int DbVersion;
	char rivHost[255];     //The Rivendell Host - Web API Call will user
	char rivUser[255]; // The Rivendell User to use for Web Call
	int webResult;

	RivendellCfg->ParseInt("MySQL", "Version", DbVersion);

	if (DbVersion >= 252)  /* This is Rivendell 2.0 */
	{
		
		if (!RivendellCfg->ParseString("RivendellWebHost", "Rivhost", rivHost))
		{
			exportstatus = 0;
		}

		if (exportstatus != 1)  
		{
			strcat(rd_title, "  - EXPORT FAILURE - Length may be Corrupted - ");
			Export_Failure(rd_title,
				cutName.c_str(),
				cartNewNumber,
				(_("The Export FAILED !Incorrect 2.0 Configuration !")));
			return;
		}
	
		// Start Progress Bar In Another Thread
		MyProgressThread* myprogressthread = new MyProgressThread();
		strcpy(rivUser, riv_getuser(mDb).c_str());
		webResult = RD_ImportCart(rivHost,
			rivUser,
			"",
			cartNewNumber,
			cutNewNumber,
			(unsigned)1,
			0,
			0,
			0,
			fName);
		// Kill the progress Thread
		myprogressthread->Delete();
		myprogressthread->Wait();
		delete myprogressthread;

		if (webResult < 0) 
		{
			strcat(rd_title, "  - EXPORT FAILURE - Length may be Corrupted - ");
			Export_Failure(rd_title,
				cutName.c_str(),
				cartNewNumber,
				(_("The Export FAILED ! Contact Help Desk !!")));
			return;
		}

		if ((webResult < 200 || webResult > 299) &&
			(webResult != 0))
		{
			switch (webResult) {
			case 404:
				strcat(rd_title, "  - EXPORT FAILURE - Length may be Corrupted - ");
				Export_Failure(rd_title,
					cutName.c_str(),
					cartNewNumber,
					(_("The Export FAILED ! No Cart/Cut Exists !")));
				return;
			case  401:
				strcat(rd_title, "  - EXPORT FAILURE - Length may be Corrupted - ");
				Export_Failure(rd_title,
					cutName.c_str(),
					cartNewNumber,
					(_("The Export FAILED ! Unauthorized or Cart out of Range")));
				return;
			default:
				strcat(rd_title, "  - EXPORT FAILURE - Length may be Corrupted - ");
				Export_Failure(rd_title,
					cutName.c_str(),
					cartNewNumber,
					(_("The Export FAILED ! Contact Help Desk !")));
				return;
			}
		}
	}
so - The export of the file happen first - which means it copies the audio out to a disk file. This export
call puts up a window showing progress etc...
Then - if I am using a later Rivendell system - I use the web call to actually write the data to the remote Rivendell System.
That's the Riv C Library call that I mentioned before that is linked into the code (RD_ImportCart()). It actually builds the LibCurl calls and
sends it to the Rivendell Web API - which is running on a web server somewhere....not necessarily this machine...
Depending on what I get back from the call I know if I was successful or not....

This is an example of writing a file out - I also could be reading a file in - which is also a web call... Just I get the file first - then call the local import routines to put the actual file into the Audacity engine...
User avatar
doublemax
Moderator
Moderator
Posts: 19160
Joined: Fri Apr 21, 2006 8:03 pm
Location: $FCE2

Re: wxThread

Post by doublemax »

I still don't see why you can't do all that in a thread. I guess i just have to take your word for it.

However, if you want a "clean" solution, i still think that's the way to go.
Use the source, Luke!
Post Reply