PDA

View Full Version : mainwindow does not refresh



edb
19th January 2006, 13:39
I have a mainwindow that is derived from QMainwindow and that uses a number of dockwindows and has qworkspace as its central widget (for MDI purposes). Some of the processes I run are very lengthy. If I minimize my application to do something meanwhile, the mainwindow does not refresh on showing. I tryed to force this using the following timer (code in constructor of mainwindow):


QTimer *refreshTimer = new QTimer( this );
connect( refreshTimer, SIGNAL( timeout() ), this, SLOT( SlotRefresh() ));
refreshTimer->start( 100, FALSE );


SlotRefresh(): tryed it with update and repaint:


void mainwindow::SlotRefresh()
{
/*update();
qApp->processEvents();*/
repaint();
qApp->processEvents();
}


I even tryed to call update on one of the dockwindows used, but that doesn't work either :(

Any ideas?

Thanx

high_flyer
19th January 2006, 14:20
it doesn't work since your solution doesn't deal with the problem which is that the legthy task doesn't gives the main event loop a chance to process the timer event, to the same reason your main windwos does not refresh is resposible for the timer slot not to work (actually the timer signal not to be processed).

You need to inser processEvents in your lengthy process, thats all.
Then you can delete the QTimer thing.

edb
23rd January 2006, 11:51
The problem is that the lengthy process is one out of a library we use, which is a non QT library. Is it possible to call the processEvents somehwhere else? Maybe by working with threads (must admit I don't like threads, but if nothing else is possible...).

high_flyer
23rd January 2006, 12:44
Is it possible to call the processEvents somehwhere else?
Sure, but will it help? ;)

Maybe by working with threads (must admit I don't like threads, but if nothing else is possible...).
Yes, it sounds that threads are the solution in this case.
Create a new thread and call your lengthy process in that thread, and send an event when its finished back to the main thread, this will alow your GUI to stay resposive

edb
23rd January 2006, 15:43
I put my lengthy process in a thread and now the refreshing works but the widget from which the thread was started should be disabled during the execution of the thread. With refreshing I only mean the visual aspect, ie the widget shows all its colors and buttons. It may not seem as if it crashed.

How can I do this? Can I disable the widget and if so: how do I check whether the thread has finished. I tryed putting a loop after starting the thread that does nothing and only exits when finished() is true, but that didn't work:


while (!myThread.finished())
{
}


How can I let my main program wait for the thread to finish whithout loosing the refreshing of the main window?

Mike
23rd January 2006, 15:52
You could create also a timer. The timer should fire every second or so, and then check the worker thread. If the thread is done, the time could raise an event or some other means to tell your app that the thread has finished.

jacek
23rd January 2006, 17:49
You could create also a timer. The timer should fire every second or so, and then check the worker thread. If the thread is done, the time could raise an event or some other means to tell your app that the thread has finished.
You don't need timers --- thread can posts such event when it finishes.

wysota
23rd January 2006, 19:18
Or even emit a signal.

jacek
23rd January 2006, 19:23
Or even emit a signal.
In Qt3?

wysota
23rd January 2006, 19:27
In Qt3?
I was speaking in general terms. QThread in Qt3 doesn't provide a signal for that, but you still can emit one (by sending an event, processing it and emitting a signal from the thread using the custom event handler and a wrapper (like void MyThread::emitFinished(){ emit finished(); })

jacek
23rd January 2006, 19:41
QThread in Qt3 doesn't provide a signal for that
Because QThread doesn't inherit QObject in Qt3. You would have to use multiple inheritance.

yop
23rd January 2006, 20:24
Read the docs on QThread, the examples given are enough for your case. You just have to post an event to the main / gui thread to alert it that the operation has finished. The GUI programming with qt3 book also has some very usefull examples.

wysota
23rd January 2006, 20:34
Because QThread doesn't inherit QObject in Qt3. You would have to use multiple inheritance.

Hmm... true, I didn't notice that.

Chicken Blood Machine
24th January 2006, 02:43
I put my lengthy process in a thread and now the refreshing works but the widget from which the thread was started should be disabled during the execution of the thread. With refreshing I only mean the visual aspect, ie the widget shows all its colors and buttons. It may not seem as if it crashed.

How can I do this? Can I disable the widget and if so: how do I check whether the thread has finished. I tryed putting a loop after starting the thread that does nothing and only exits when finished() is true, but that didn't work:


while (!myThread.finished())
{
}


How can I let my main program wait for the thread to finish whithout loosing the refreshing of the main window?


Your while loop is blocking the main thread which is causing the same problem you had before. Here is some partial code that describes what you need to do. Look at the QThread and QWidget docs to see about posting events from a secondary thread to the main thread.




// Main(GUI) thread

// Constructor/destructor of MainWindow, etc.

...
...

MainWindow::startJob()
{
// Disable whole window
setEnabled(false);

// Launch thread
mpWorkerThread = new WorkerThread();

mpWorkerThread.start();
}


MainWindow::stopJob()
{
// Enable whole window
setEnabled(true);

// Do any other cleanup here
}


Mywidget::customEvent(QCustomEvent* e)
{
// Decode e here and check if it is your special worker custom event
if (int(e->type()) == MY_CUSTOM_EVENT_ID)
stopJob();
}


// Worker thread code

#include <qapplication.h>
#include <qevent.h>

// Init static constant
WorkerThread::MY_CUSTOM_EVENT_ID = QEvent::User + 1;

// Implement constructor/destructor, etc.
WorkerThread::WorkerThread(MainWindow* parent) : QThread()
mpParent(parent)
{
}

WorkerThread::run()
{
// Implement worker code here
while ( still_not_finished)
{
...
...
}

// At end of function, notify main thread that we have finished.
qApp->postEvent(mpParent, new QCustomEvent(MY_CUSTOM_EVENT_ID));
}

edb
24th January 2006, 10:45
Thanx for all your tips. I wil try them today and keep you posted!

edb
24th January 2006, 15:21
I tryed this, but I keep getting a strange error:

Xlib: unexpected async reply. I found a website which contains a description of this error:


http://www.faqs.org/faqs/x-faq/part7/section-15.html

It states:


You'll typically get these errors if you are writing a multi-threaded
application and are making X calls from more than one thread -- one of the
more common new ways to introduce memory corruption into Xlib (using bogus
pointers is another, as is mixing up XFree/XtFree/free calls. Even an
operation as simple as XSendEvent can't be called from a second thread.).
Prior to X11R6, X doesn't support multi-threading; check the X11R6
documentation for how to write a threaded application safely with X11R6 and
later versions of X (including being sure to enable Xlib's multi-thread
support).

Does this mean I cannot post events in one thread that can be received in another thread? Using the example above I want to call the qApp->postEvent from my "lengthy process thread" and receive it in my main thread.

Chicken Blood Machine
24th January 2006, 16:58
You should not be having any async problems, since you should not be making iny X11 calls from another thread. Please post your code so we can see where it is going wrong.

edb
24th January 2006, 17:23
my main program:



QApplication a(argc,argv);
MyMDIMainWindowThread* mainthread = new MyMDIMainWindowThread();
mainthread->start();
mainthread->wait();
return a.exec();



MyMDIMainWindowThread run:

void MyMDIMainWindowThread::run()
{
mpMyMainWindow = new MyMDIMainWindow();
mpMyMainWindow->showMaximized();
qApp->setMainWidget(mpMyMainWindow);
std::cout << "mainthread ended" << std::endl << std::flush;
}


run function for thread for the lengthy process:


// ... run lengthy process

//.. mpParent is set in the constructor and is a pointer to the widget that started the thread
qApp->postEvent(mpParent, new QCustomEvent(MyThread::PROCESSTHREADENDED));


customEvents function:

void MyLengthyProcessWidget::customEvent(QEvent* e)
{
switch (int(e->type()))
{
case MyThread::PROCESSTHREADENDED:

{
// the messagebox is never shown!
QMessageBox::critical(0, tr(""),tr("Process Thread Ended!!!"), QMessageBox::Ok,0,0);
setEnabled(true);
break;
}
}

finally: starting the lengthy process thread from a function in MyLengthyProcessWidget::


setEnabled(false);
MyLengthyProcessThread* proc = new MyLengthyProcessThread(this);
proc->start();


Think that's all....

wysota
24th January 2006, 17:31
You can't do GUI operations from a thread which does not own QApplication object.

And looking at your code... why do you need that thread if you put your main application thread to sleep after you spawn it?

I'm talking about this:


QApplication a(argc,argv);
MyMDIMainWindowThread* mainthread = new MyMDIMainWindowThread();
mainthread->start();
mainthread->wait();
return a.exec();

Chicken Blood Machine
24th January 2006, 17:47
@edb - I think you misunderstood my post. (or maybe I wasn't completely clear!)

When I referred to the main thread, I meant the main thread of execution (where the program starts). You do not need to create a 'main thread'. This is why you are getting those async errors.

I've tried to correct your code to put you on the right track:



int main(int argc, char** argv)
{
QApplication a(argc,argv);
MyMDIMainWindow* mainwindow = new MyMDIMainWindow();
mainwindow ->show();
a.setMainWidget(mainwindow);
return a.exec();
}



MyMDIMainWindow startSlot() - called in response to whatever is required to
start your lenthy process, e.g. button press, etc:


void MyMDIMainWindow::startSlot()
{
setEnabled(false);

MyLengthyProcessThread* proc = new MyLengthyProcessThread(this);
proc->start();
}


You can also implement a stopSlot() to terminate the worker thread prematurely if required.

edb
25th January 2006, 09:02
Indeed I was confused about the main thread and whether I had to create that or not, but it didn't work when I did not create a mainthread. I will remove this and start with the code you posted in the previous thread.

I still get these "XLib: unexpected async reply" errors. I have the feeling that the thread is not ended well. The lengthy process is executed well, but if I execute something in MyMDIMainWindow, the program does not respond anymore. I do not start mij lengthy process in MyMDIMainWindow, but from a widget that is placed in one of the dockwindows of the MyMDIMainWindow. Is this a problem?
I also do not delete the lengthyprocess thread. I probably should, but how? I post the ended thread event, but I never get in the customEvent function...

edb
25th January 2006, 17:24
One problem solved! My mistake. I overloaded the customEvent function instead of overriding it! Now the customEvent function is reached!

I used: customEvent(QEvent* e) instead of customEvent(QCustomEvent* e)

Concerning the xlib errors: I still got them. I will now try and remove all GUI related aspects from my thread and see what this will do. I have the feeling that interacting with the GUI from the thread causes this problem....

Chicken Blood Machine
25th January 2006, 17:42
One problem solved! My mistake. I overloaded the customEvent function instead of overriding it! Now the customEvent function is reached!

I used: customEvent(QEvent* e) instead of customEvent(QCustomEvent* e)


Sorry- my mistake, my example was wrong. I'll correct it now.



Concerning the xlib errors: I still got them. I will now try and remove all GUI related aspects from my thread and see what this will do. I have the feeling that interacting with the GUI from the thread causes this problem....

Of course. You cannot interact with the GUI whatsoever from another thread. That is what the customEvent mechanism is for. This is stated in the 'Thread Support in Qt' documentation. Did you read it?

http://doc.trolltech.com/3.3/threads.html