PDA

View Full Version : QMessageBox in slot signaled by QThread



jkv
15th December 2009, 20:53
I have subclassed worker QThread that is sending results of it's work in signal to a slot.

Within the slot I call QMessageBox::question(..) to let user confirm action based on thread's result. However if there comes more than one signal at once msgbox is shown for each of them before previous ones have been answered. So I end up with multiple boxes on screen.

If I use Qt::BlockingQueuedConnection instead there is no problem, but worker thread won't do it's work in background while the box is shown.

I don't quite understand how Qt event processing works but it seems like it blocks for the QMessageBox::qeustion(..) but still by some black magic (or not) it gets to resume event processing and finally calls the same slot function again...

How could I prevent the slot from being called until it has been returned while letting worker thread keep doing it's job? Or is Qt::BlockingQueuedConnection the only way?

Thanks for any ideas

nish
16th December 2009, 15:50
in your slot use QMutex..


QMutex mutex; //make sure it is global or a static member of a class
void myslot()
{
if(mutex.tryLock()) //u can use mutex.lock() but it will block..
{
QMessageBox::information();
mutex.unlock();
}

}

jkv
16th December 2009, 20:41
Here is the original code I had in the slot


void FileDatabase::taskFinished(WorkerTask* worktask)
{
switch (worktask->id())
{
...
case DBTASK_DELETE:
{

// Double Check for accidental fallthroughs
if (worktask->id() != DBTASK_DELETE) break;

if (d->deleteConfirmation == QMessageBox::Cancel) break;

FileModifiedTask* task = static_cast<FileModifiedTask*>(worktask);
FileNode* fn = task->file();

if (task->isModified()) {
Log(LOG_WARN) << tr("File has been modified; won't delete (%1)")
.arg(fileRelativePath(fn)) << Log::ENDL;
break;
}

if (d->deleteConfirmation != QMessageBox::YesToAll)
{
d->deleteConfirmation = QMessageBox::question(
app, tr("File About to be Deleted"),
tr("Are you sure you want to delete %1 file '%2' ?\n\n"
"WARNING: File will be removed from filesystem and it "
"can't be restored!\n")
.arg(FileAttributeString(fn), fileRelativePath(fn)),
QMessageBox::Yes|QMessageBox::No|
QMessageBox::YesToAll|QMessageBox::Cancel,
QMessageBox::No);
}

if (d->deleteConfirmation&
(QMessageBox::Yes|QMessageBox::YesAll))
{
QFile file(fn->path());
if (file.remove())
removeFile(fn);
else {
Log(LOG_ERROR) <<
tr("Can't delete file (%1)")
.arg(fileRelativePath(fn)) << Log::ENDL;
}
}
}
break;
}

delete worktask;
}

Worker thread computes hash of file content and the result is stored in FileModifiedTask and then emitted to the slot. In the slot I check if the file has been modified and confirm the file delete, unless it's been canceled or YesToAlled.

So I thought about your suggestion to use mutex. In this case wouldn't simple bool variable be enough? I would assume this happens in single thread ? But then the problem becomes what to do with those tasks that can't get the lock ?

So I thought it a bit more and then realised I need to store those finished tasks and process them later



QObject::connect(taskTimer, SIGNAL(timeout()),
db, SLOT(processQueuedTasks()));

void FileDatabase::taskFinished(WorkerTask* task)
{
d->taskQueue.enqueue(task);
if (!(d->taskTimer->isActive() || d->processingTasks))
d->taskTimer->start();
}

void FileDatabase::processQueuedTasks()
{
d->processingTasks = true;

QTime time;
time.start();
int n = d->taskQueue.size();
while (d->taskQueue.size() && time.elapsed() < 10) {
processTask(d->taskQueue.dequeue()); // same code as in original taskFinished(..)
}
emit progressAdvanced(n - d->taskQueue.size());
if (d->taskQueue.size())
d->taskTimer->start();

if (d->batchFinished && d->taskQueue.isEmpty())
unlock();

d->processingTasks = false;
}

Not very pretty but but at least it seems to work...

Btw am I correct when I think that each time QMessageBox::question() is called a new EventLoop is pushed on stack that now handles the gui events and all the other event that the previous "main" EventLoop did ? That in a sense there are then multiple EventLoops on stack, but only top one is "active"? I hope I'm not too off the mark here :confused: