views:

221

answers:

1

I have an app that has a progress bar & spawns a worker thread to do some work & report back progress. The dialog class overrides the customEvent method so that I can process events that are being passed to the gui thread via the worker thread. Before I was using a QThread derived class as the worker thread and I changed it to use ACE_Thread_Manager->spawn() with a static function for the worker.

The problem shows up when I run the app and press the button so the worker is spawned & starts doing work. When it sends the signal to increment the progress bar I get the following errors logged to std out.

QPixmap: It is not safe to use pixmaps outside the GUI thread

This seems to happen when the progressBar->setValue() is called. So it seems like the setting of the progress bar is happening in a different thread than the main gui thread. I'm unclear as to how that's possible. I'm under the impression that I have a main gui thread which has my gui & the customEvent method is on that same thread and the worker is on it's own thread. Is this assumption wrong? And is there any difference when using the QThread derived class versus the static run_svc method?

Any help would be appreciated. The code snippets for the customEvent handler, run_svc, and button handler code are below and the code is attached.

void MyDlgEx::customEvent(QEvent * e)
{
    if (e->type() == IdNumOperations)
    {
        NumOperations* pEvt = static_cast<NumOperations*>(e); 
        _steps = 0; 
        cout << "Num Operations = " << pEvt->operations() << endl; 
    }
    else if (e->type() == IdStep)
    {
        if (_steps % 10 == 0)
        {
            cout << "Step++ = " << _steps << endl; 
        }

        _steps++;  
        _progressBar->setValue(_steps); 
    }
}

void* MyDlgEx::run_svc(void* args)
{
    auto_ptr<ThreadArgs> thread_args(static_cast<ThreadArgs*>(args)); 
    QApplication::sendEvent((QObject*)thread_args->m_pDlg, new NumOperations(300)); 

    // does some work that takes time -- ommitted for clarity 
    // called in a loop
    QApplication::sendEvent((QObject*)thread_args->m_pDlg, new Step());

    QApplication::sendEvent((QObject*)thread_args->m_pDlg, new Completed()); 

    return 0; 
}

Button Handler Commented out lines where where I used a QT class derived from QThread. Using ACE has to spawn the thread has uncovered this issue.

void MyDlgEx::btnShowProgress_clicked()
{
    //_pProc = new ProcessThread(this); 
    //_pProc->run();

    auto_ptr<ThreadArgs> thread_args(new ThreadArgs(this));
    if (ACE_Thread_Manager::instance()->spawn(
                MyDlgEx::run_svc,
                static_cast<void*>(thread_args.get()), 
                THR_DETACHED | THR_SCOPE_SYSTEM) == -1)
        cout << "Failed to spawn thread." << endl; 

    thread_args.release(); 
}
+2  A: 

Try calling QApplication::postEvent(...) instead of QApplication::sendEvent(). The docs say that sendEvent sends the event directly, meaning that it calls the customEvent() function directly from the other thread. postEvent() adds the event to the event queue where it can later be dispatched to customEvent() by the main GUI event loop.

Just because the customEvent() function is a member of an object created in the main GUI thread doesn't mean another thread cannot call the function. I believe that is what is happening when you call QApplication::sendEvent() from another thread.

Jason
Stephen Burke
I'm not familiar with the WIN32 API, but it sounds the same to me. sendEvent blocks because it is directly calling the event handler, but postEvent returns after pushing the event onto the queue and the event is handled later by another thread or the same thread if postEvent was called from the GUI thread. I would suspect (but do not know) that there are minor differences between the Qt and WIN32 versions, but it sounds like they are similar in the main points.Have you looked at using Qt signals and slots instead of custom events? They might make things a little easier and cleaner.
Jason