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 ]