Problem with threads and GUI

I create a Root GUI application:

TApplication app("Application", &argc, argv);
MyClass *mainWindow = new MyClass();
app.Run(kTRUE);

In the constructor of MyClass I create and run a new TThread:

MyClass::MyClass()
{	
	InitWindow(); //this creates different frames, labels etc.

	myThread = new TThread("myThread",
					MyClass::ThreadHandle,(void*)this);
	myThread->Run();
}

Then in ThreadHandle (which is a static method of MyClass) I try to change a text of one of the labels, which is a member of MyClass:

void* MyClass::ThreadHandle(void *arg)
{
	MyClass *classInstance = static_cast<MyClass*>(arg);

	while(1)
	{
		classInstance->ChangeLabelText();
	}
	return nullptr;
}

where ChangeLabelText is a MyClass class method:

void MyClass::ChangeLabelText()
{
		fLabel->SetText("new text");
}

The problem is: the fLabel’s text updates only after I move or resize window, or after I kill myThread. What can I do to update GUI elements from different thread immediately and while it is still running?

Hi,

You can try to call gClient->NeedRedraw(fLabel, kTRUE);If it does’t work, please provide a complete (and running) example reproducing the problem.

Cheers, Bertrand.

Hi Bertrand,

thanks for your reply. Unfortunately, your solution doesn’t work for me. I prepared compilable example showing my problem. The label changes it’s text every five seconds and I see this change in GUI only after resizing or moving the window.

I compiled it with:

g++ main.C -o main `root-config --libs --cflags --glibs`

Best,
Jeremi
main.C (1.68 KB)

[quote=“jniedzie”]Hi Bertrand,

thanks for your reply. Unfortunately, your solution doesn’t work for me. I prepared compilable example showing my problem. The label changes it’s text every five seconds and I see this change in GUI only after resizing or moving the window.
Jeremi[/quote]

It did not help, since main thread is still idle (it is ‘sleeping’ in DispatchOneEvent function) and it’s a main thread who will eventually synchronise with X server
(there are different ways to do it, for example, calling XSync or XFlush).
But even worse - your code is not a correct mt program. Your ‘background’ thread should not touch anything GUI related and should not modify label, which is available from the main GUI thread - just imagine you start resizing a window (thus you have to repaint the label), main thread is trying to access the label’s text and at this point your background thread is trying to update this string. I would recommend you before trying to use threads read some books about them/articles/tutorials, otherwise it’s impossible to write a correct program (in general, mt-programming is way more complex than traditional sequential). Have a look at what your program demonstrates now:
en.wikipedia.org/wiki/Race_conditions.

In your case, the right thing is to (somehow, not sure ROOT has any solution for this) inform the main thread it can now repaint the label and return (somehow) the string value which now must be your label’s text.

I’ll try to make a working example for you later (assuming that ROOT’s components work correctly in MT environment).

Hi,

One easy possible solution would be to add [color=#0000FF]gClient->HandleInput();[/color] at the end of MyClass::ChangeLabelText(), as shown below:

//______________________________________________________________________________ void MyClass::ChangeLabelText() { if (!fChanged) { cout << "1" << endl; fLabel->SetText("new text"); fChanged = true; } else { cout << "2" << endl; fLabel->SetText("old text"); fChanged = false; } gClient->HandleInput(); }
Cheers, Bertrand.

Hi,

thanks for your replies - solution from Bertrand works!

Best,
Jeremi