PDA

View Full Version : Communication between QThread and Yes/No QMessageBox?



pmaktieh.sirhc
22nd January 2007, 23:37
Hi,

is there a way to show a QMessageBox and get the return code (yes/no message box) out of a thread (QThread subclass)?

Thanks!
Chris

jpn
22nd January 2007, 23:40
Unfortunately no, because you may not touch the GUI from any other than the main GUI thread.

pmaktieh.sirhc
23rd January 2007, 18:52
...mmmhhhh, no chance with connections or events?

If the messagebox create process in main thread and call this with connection from second thread and return the result (yes/no) back via connection to second thread?

jpn
23rd January 2007, 19:43
Of course it's possible. The messagebox just has to be exec'd in the main thread. Use queued signals or custom events to deliver the information between threads.

pmaktieh.sirhc
23rd January 2007, 20:54
Ok, but the second thread must be wait for messagebox reply! How?

jpn
23rd January 2007, 21:04
Well, it depends pretty much how the thread is running. Is it running an event loop or is everything done in QThread::run()? You could start with taking a look at the thread examples shipped with Qt.

pmaktieh.sirhc
23rd January 2007, 21:23
It's not implemented yet! I think, in QThread:run()! Is the event loop better?

wysota
23rd January 2007, 22:19
IMO eventually you'll have to use a wait condition to put the waiting thread to sleep. And the message box would have to be shown using show() and not exec(), otherwise it'll block the main thread execution which makes all the situation with using another thread highly doubtfull.

To me it smells a little like a bad design somewhere...

jpn
23rd January 2007, 22:31
Just curious, what is the thread actually processing?

pmaktieh.sirhc
24th January 2007, 10:54
Nothing, it's not implemented yet! It's a consideration so far!

spurious_interrupt
25th January 2007, 01:29
I have an application that uses tape drives. The thread needs to ask if there are any more tapes for the job, SCSI errors, file dialogs...

This method uses QT3, I have not ported this application to QT4 yet.




in your application

void TapeMan::customEvent( QCustomEvent *event )
{
if( event->type() == QEvent::User + 3 ) // display message box
{
MessageBoxEvent *me = ( MessageBoxEvent* )event;
ThreadMessageBox *tm = me->tm;

if( me->kind == TMSG_WARN )
tm->messageResult = QMessageBox::warning( this, me->caption, me->message,
me->button0, me->button1, me->button2 );
if( me->kind == TMSG_INFO )
tm->messageResult = QMessageBox::information( this, me->caption,
me->message, me->button0, me->button1, me->button2 );
if( me->kind == TMSG_STOP )
tm->messageResult = QMessageBox::critical( this, me->caption, me->message,
me->button0, me->button1, me->button2 );
}
}

and then 2 classes

#ifndef THREADMESSAGEBOX_H
#define THREADMESSAGEBOX_H

#include <qstring.h>
#include <qmessagebox.h>

class ThreadMessageBox
{
public:
ThreadMessageBox( THREAD_MESSAGE kind, const QString &caption, const
QString& message, int button0, int button1=QMessageBox::NoButton,
int button2=QMessageBox::NoButton );

~ThreadMessageBox();

int messageResult;
};

#endif

#include "threadmessagebox.h"
#include "messageboxevent.h"

#include <qapplication.h>

extern TapeMan *gApp;

ThreadMessageBox::ThreadMessageBox( THREAD_MESSAGE kind, const QString &caption,
const QString& message, int button0, int button1, int button2 )
{
MessageBoxEvent *mess = new MessageBoxEvent( this, kind, caption, message,
button0, button1, button2 );

messageResult = -99999;

QApplication::postEvent( gApp, mess ); // post event

while( messageResult == -99999 ) // wait
{
sleep( 1 );
}
}

ThreadMessageBox::~ThreadMessageBox()
{
}

#ifndef MESSAGE_BOX_EVENT_H
#define MESSAGE_BOX_EVENT_H

#include <qevent.h>
#include <qmessagebox.h>
#include <qdeepcopy.h>

class ThreadMessageBox;

class MessageBoxEvent : public QCustomEvent
{
public:
MessageBoxEvent( ThreadMessageBox *tm, THREAD_MESSAGE kind, const QString &caption,
const QString& message, int button0, int button1=QMessageBox::NoButton,
int button2=QMessageBox::NoButton );
~MessageBoxEvent();

THREAD_MESSAGE kind; // kind of message box

QDeepCopy<QString> caption; // caption for message box
QDeepCopy<QString> message; // text for message

int button0; // buttons to display
int button1;
int button2;

ThreadMessageBox *tm; // pointer to thread message box
};
#endif

#include "messageboxevent.h"

MessageBoxEvent::MessageBoxEvent( ThreadMessageBox *tm, THREAD_MESSAGE kind,
const QString &caption, const QString& message,
int button0, int button1, int button2 ) : QCustomEvent( QEvent::User+3 )
{
this->kind = kind;
this->caption = caption;
this->message = message;
this->button0 = button0;
this->button1 = button1;
this->button2 = button2;
this->tm = tm;
}

MessageBoxEvent::~MessageBoxEvent()
{
}

to get a message box from your thread

ThreadMessageBox mess = ThreadMessageBox( TMSG_INFO, "Tape Done", "Are there any more tapes",
QMessageBox::Yes, QMessageBox::No ); // will block until message box is closed

if( mess.messageResult == QMessageBox::Yes )
// do action here!



You need to use QDeepCopy's to insure each thread has a unique copy of the strings. This is not needed in QT4. The wait loop is a simple sleep. That is all that was needed for this application.

spud
25th January 2007, 04:45
As long as the tQThread object was created in the Gui thread, you can set up a connection between a private signal and a private slot. For the synchronization you need a QWaitCondition and a QMutex.
e.g.:


class WorkingThread : public QThread
{
Q_OBJECT
public:
WorkingThread(QObject*parent=0);
protected:
virtual void run();
signals:
void askAQuestion( const QString& question, QMessageBox::StandardButton* answer);
private slots:
void questionAsked(const QString& question, QMessageBox::StandardButton* answer);
private:
QWaitCondition waitCondition;
QMutex mutex;
};

WorkingThread::WorkingThread(QObject*parent)
: QThread(parent=0)
{
connect(this, SIGNAL(askAQuestion( const QString&, QMessageBox::StandardButton*)),
this, SLOT(questionAsked( const QString&, QMessageBox::StandardButton*)));
}

void WorkingThread::run()
{
QMessageBox::StandardButton answer = QMessageBox::Yes;
do
{
QMutexLocker locker(&mutex);
emit askAQuestion( "do you want to continue?", &answer);
waitCondition.wait(&mutex);
}
while(answer == QMessageBox::Yes);
}

void WorkingThread::questionAsked(const QString& question, QMessageBox::StandardButton* answer)
{
QMutexLocker locker(&mutex);
*answer = QMessageBox::question(0, "Question:", question, QMessageBox::Yes|QMessageBox::No);
waitCondition.wakeOne();
}


The connection will automatically be queued since the run() function doesn't reside in the same thread as the QThread object, thus it's safe to use a messagebox.

Good luck!