Trolltech Home | Qt-interest Home | Recent Threads | All Threads | Author | Date
All threads index page 4

Qt-interest Archive, May 2008
Does Qt allow a function pointer to connect between a thread and a widget?


Message 1 in thread

My problem is to make a direct call from a separate thread to a GUI widget:

1) I wish a Reader::read() which periodically triggers a call to a 
progress bar.
bool Reader::read(wchar_t &ch)
{
 if (!src) return false;
 ++ m_readCount;
 if (m_readCount % m_interval == 0) {
   display();
 }
 return src->get(ch);
}
void Reader::display()
{
 if (!m_handler) return;
 int pos = src->position();
 int total = src->size();
 (*m_funcptr)(pos, total, m_handler);
}

2) where, m_funcptr is a function pointer, which has been registered in 
main().
int main(int argc, char *argv[])
{
 ProgressCtrl progress;
 Reader reader;
 reader.registerProgress(ProgressCtrl::progressDisplay, &progress);
}

3) The registered function is a static member method of a widget class:
class ProgressCtrl : public QDialog
{
public:
 static bool progressDisplay(int pos, int total, void *);
   QProgressBar *progressBar;
private:
 bool display(int pos, int total);
};

4) and the progress bar gets updated by setValue(pos).
bool ProgressCtrl::progressDisplay(int pos, int total, void *p)
{
 return ((ProgressCtrl *)p)->display(pos, total);
}
bool ProgressCtrl::display(int pos, int total)
{
 progressBar->setRange(0, total);
 progressBar->setValue(pos);
 return true;
}

5) If the read loop is kicked of in main(), everything works well.
int main()
{
...
 wchar_t ch;
 while (reader.read(ch)); // read loop
}

6) But if the read loop is kicked off in a separate thread, then, debug 
mode does not work at all,
while the release mode sometime works, but sometime works with warnings. 
Why???
int main()
{
 QThread *thread = new ThreadRead(&reader);
 thread->start();

 return progress.exec();
}

void ThreadRead::run()
{
 wchar_t ch;
 while (m_reader->read(ch));
}

The output complains:"QWidget::repaint: Recursive repaint detected"
Source code are zipped, and I was trying to attach, but got rejected. So 
it's upon your request for test.
BTW, my test is on WindowXP, VS2005 IDE, and Qt4.3.3

Thanks,
Lingfa

--
 [ signature omitted ] 

Message 2 in thread

No, you can't do that. Qt does not allow any thread except the GUI
thread to update GUI widgets. Cross-thread signals and slots (between
the worker and the GUI) would work.

-- 
 [ signature omitted ] 

Message 3 in thread

Andrew Medico wrote:
> No, you can't do that. Qt does not allow any thread except the GUI
> thread to update GUI widgets. Cross-thread signals and slots (between
> the worker and the GUI) would work.
>   
Thank you for your reply. Single-slot provides communication between 
threads, they are in top level (application level).
Let's say there is library in low level, from where cannot emit signal. 
This function pointer establish a callback, which has no problem in MFC 
for examples, but stopped by Qt sound no good:(

Actually, in release mode the callback between threads in Qt env does 
work. I just don't understand why the progress bar has flick and in 
console report "Recursive repaint".

Does any know why the repaint went into recursive, or is there a way to 
stop the recursion?

Thanks,
Lingfa

--
 [ signature omitted ] 

Message 4 in thread

On Tuesday 20 May 2008 04:40, Lingfa Yang wrote:
> Andrew Medico wrote:
> > No, you can't do that. Qt does not allow any thread except the
> > GUI thread to update GUI widgets. Cross-thread signals and
> > slots (between the worker and the GUI) would work.
>
> Thank you for your reply. Single-slot provides communication
> between threads, they are in top level (application level).
> Let's say there is library in low level, from where cannot emit
> signal. This function pointer establish a callback, which has no
> problem in MFC for examples, but stopped by Qt sound no good:(
>

You can still get low-level callbacks in threads to work fine in Qt.
Here is an example of how you can make it work.

Instead of this:
bool ProgressCtrl::progressDisplay(int pos, int total, void *p)
{
 return ((ProgressCtrl *)p)->display(pos, total);
}

Try this:
bool ProgressCtrl::progressDisplay(int pos, int total, void *p)
{
 bool ret = false;
 QMetaObject::invokeMethod(static_cast<ProgressCtrl*>(p), "display",
    Qt::BlockingQueuedConnection,
    Q_RETURN_ARG(bool,ret), Q_ARG(int,pos), Q_ARG(int,total));
 return ret;
}

Where ProgressCtrl is a QObject and display() is a slot.
display() will then be called in whatever thread ProcessCtrl belongs 
to, which should be the main thread.

-- 
 [ signature omitted ] 

Message 5 in thread

Rohan McGovern wrote:
> On Tuesday 20 May 2008 04:40, Lingfa Yang wrote:
>   
>> Andrew Medico wrote:
>>     
>>> No, you can't do that. Qt does not allow any thread except the
>>> GUI thread to update GUI widgets. Cross-thread signals and
>>> slots (between the worker and the GUI) would work.
>>>       
>> Thank you for your reply. Single-slot provides communication
>> between threads, they are in top level (application level).
>> Let's say there is library in low level, from where cannot emit
>> signal. This function pointer establish a callback, which has no
>> problem in MFC for examples, but stopped by Qt sound no good:(
>>
>>     
>
> You can still get low-level callbacks in threads to work fine in Qt.
> Here is an example of how you can make it work.
>
> Instead of this:
> bool ProgressCtrl::progressDisplay(int pos, int total, void *p)
> {
>  return ((ProgressCtrl *)p)->display(pos, total);
> }
>
> Try this:
> bool ProgressCtrl::progressDisplay(int pos, int total, void *p)
> {
>  bool ret = false;
>  QMetaObject::invokeMethod(static_cast<ProgressCtrl*>(p), "display",
>     Qt::BlockingQueuedConnection,
>     Q_RETURN_ARG(bool,ret), Q_ARG(int,pos), Q_ARG(int,total));
>  return ret;
> }
>
> Where ProgressCtrl is a QObject and display() is a slot.
> display() will then be called in whatever thread ProcessCtrl belongs 
> to, which should be the main thread.
>
>   
Hi Rohan,

This sounds a break though. I tested it. Problem turns to the connection.

Qt can have 4 types of connection in total:
With "Qt::DirectConnection" I got "QCoreApplication::sendEvent: 'Cannot 
send events to object owned by a different thread.'"
With the other three:
  Qt::QueuedConnection
  Qt::BlockingQueuedConnection
  Qt::AutoConnection
I got "QMetaObject::invokeMethod: Unable to invoke methods with return 
values in queued connection"


Any new idea?

Best regards,
Lingfa

--
 [ signature omitted ]