PDA

View Full Version : How to solve GUI freezing problem



honestapple
13th March 2013, 08:54
Hi,
I am developping a project about data sampling and realtime display like oscilloscope. The data are transfered to PC with virtual com port which has a high trandfer rate. I am using QT4.8.4 + Qextserialport to accomplish serial port communication on WindowsXP. The following is the link of the reference sourcecode.
http://www.qtcentre.org/threads/21063-QextSerialPort-with-QTimer-approch-for-reading?p=103325&highlight=#post103325

In the sourcecode, the author subclass the QThread and reimplement the run function. The code worked well except when receiving data and update display.
When the application began to receive data continously and tried to update the content of a qtextbrowser through signal and slots, It would got freeze(The GUI has no response to user input).
When I removed the gui display part(The application did nothing except receiving data), the application worked well(The application respond to the pushbutton click).
When I replaced the qtextbrowser widget with qplaintextedit widget, the application could respond to user input slowly.
I think that perhaps the slow gui operation make the gui freeze.

1. Please give me some advice on how to solve this problem.

In addition, I know that there is another method for multithread application which use subclass of QObject and moveToThread method. I have no idea that whether this method could help me get out of the trouble(In fact, I did not think that this method would do some help because the current code could run well without gui operation).

2. I still want to ask a question about the second method. If I want to generate two thread on serial port using this method, one is for reading operation, the other is for writing operation, how to do that?

Added after 10 minutes:

In addition, I search on the google and find the article on the blog "Qt and serial ports | fede.tft's Blog" which perhaps have some relation to my problem. But, I could not access this blog. I will be very appreciate if someone could download this article and send it to me or paste on the board.
Thanks a lot.
The following is the blog link:
Qt and serial ports | fede.tft's Blog
fedetft.wordpress.com/2010/.../qt-and-serial-ports/

Lesiok
13th March 2013, 09:08
Show us some REAL code : how You receive data and how You update GUI.

honestapple
13th March 2013, 09:33
Code removed, see next post

In the ReceiveThread::run(), the dataReceived signal in ReceiveThread class is emitted which will trigger another dataReceived signal in MySerialPort class.
In GUI code, I connect the signal to the slot appendPlainText of the QPlainTextEdit widget.
connect( m_spSerialPort, SIGNAL(dataReceived(const QByteArray &dataReceived)), m_pteReceive, SLOT( appendPlainText(const QByteArray &dataReceived) ), Qt::QueuedConnection );
This would make gui not frozen, just very slowly to user input(such as click pushbutton).
If I changed the widget to QTextBrowser, and create a new slot to insert the received data into textbrowser with insertPlainText method, The GUI would got frozen.(Now, I knew that perhaps this is a time-consuming operation)

wysota
13th March 2013, 10:46
This code is unreadable. Please post it properly as an attachment.

Lesiok
13th March 2013, 10:56
Method SendThread::run() does not make sense. The only thing he does well is eating CPU time. Method ReceiveThread::run() also.

honestapple
13th March 2013, 12:44
Header:MySerialPort.h


#ifndef MYSERIALPORT_H
#define MYSERIALPORT_H
#include <QObject>
#include <QQueue>
#include <QMetaType>
#include <QThread>
#include <QMutex>
#include "qextserialport.h"
Q_DECLARE_METATYPE(BaudRateType);
Q_DECLARE_METATYPE(DataBitsType);
Q_DECLARE_METATYPE(ParityType);
Q_DECLARE_METATYPE(StopBitsType);
Q_DECLARE_METATYPE(FlowType);
class SendThread;
class ReceiveThread;
class MySerialPort : public QObject
{
Q_OBJECT
public:
MySerialPort();
MySerialPort(const QString &name, const BaudRateType baudRate, const DataBitsType dataBits,
const ParityType parity, const StopBitsType stopBits, const FlowType flowControl,
ulong seconds = 0, ulong milliseconds = 10);
~MySerialPort();
bool open();
bool open(const QString &name, const BaudRateType baudRate, const DataBitsType dataBits,
const ParityType parity, const StopBitsType stopBits, const FlowType flowControl,
ulong seconds = 0, ulong milliseconds = 10);
bool isOpen() const;
void close();
// Setter and getter for the basic property of the QextSerialPort
void setPortName(const QString &name);
QString portName() const;
void setBaudRate(const BaudRateType baudRate);
BaudRateType baudRate() const;
void setDataBits(const DataBitsType dataBits);
DataBitsType dataBits() const;
void setParity(const ParityType parity);
ParityType parity() const;
void setStopBits(StopBitsType stopBits);
StopBitsType stopBits() const;
void setFlowControl(const FlowType flowControl);
FlowType flowControl() const;
void setTimeout(const ulong seconds, const ulong milliseconds);
/*
Send
*/
void enableSending(); // enable the SerialPort to send data (init the thread)
void disableSending(); // disable the SerialPort to send data (terminate the thread)
bool isSendingEnable() const;
void stopSending(); // stop the currently sending data operation (don't terminate the thread)
uchar sendData(const QByteArray &data); // send data to the SerialPort (enqueue data to the sendThread queue)
// return 1 OK
// return 2 port not open
// return 3 sending operation disable
/*
Receive
*/
void enableReceiving(); // enable the SerialPort to receive data (init the thread)
void disableReceiving(); // disable the SerialPort to receive data (terminate the thread)
bool isReceivingEnable() const;
void stopReceiving(); // stop the currently receiving data operation (don't terminate the thread)
uchar receiveData(); // Start the receiving thread
// return 1 OK
// return 2 port closed
// return 3 receiving operation disable
signals:
void dataReceived(const QByteArray &dataReceived);
private:
void initPrivateVariable();
private:
QextSerialPort port;
SendThread *sendThread;
ReceiveThread *receiveThread;
bool sendingEnable;
bool receivingEnable;
// Variables to restore the previous state to a reopening of the SerialPort
bool closeCalled;
bool saveStateSendingEnable;
bool saveStateReceivingEnable;
bool saveStateReceiveData;
};
class SendThread : public QThread
{
Q_OBJECT
public:
SendThread(QextSerialPort &adrPort);
~SendThread();
void addDataToSend(const QByteArray &dataToAdd);
void stopSending();
protected:
void run();
private:
QextSerialPort &port;
QQueue<QByteArray> dataToSend;
QMutex mutexSend;
bool stopped;
};
class ReceiveThread : public QThread
{
Q_OBJECT
public:
ReceiveThread(QextSerialPort &adrPort);
~ReceiveThread();
void stopReceiving();
protected:
void run();
signals:
void dataReceived(const QByteArray &dataReceived);
private :
QextSerialPort &port;
QMutex mutexReceive;
bool stopped;
};
#endif // MYSERIALPORT_H


source file:MySerialPort.cpp


#include "myserialport.h"
/*
Private Functions
*/
void MySerialPort::initPrivateVariable()
{
// Init private variable
sendThread = NULL;
receiveThread = NULL;
sendingEnable = false;
receivingEnable = false;
closeCalled = false;
saveStateSendingEnable = false;
saveStateReceivingEnable = false;
saveStateReceiveData = false;
}

MySerialPort::MySerialPort()
{
initPrivateVariable();
}
MySerialPort::MySerialPort(const QString &name, const BaudRateType baudRate, const DataBitsType dataBits,
const ParityType parity, const StopBitsType stopBits, const FlowType flowControl,
ulong seconds, ulong milliseconds)
{
initPrivateVariable();
setPortName(name);
setBaudRate(baudRate);
setDataBits(dataBits);
setParity(parity);
setStopBits(stopBits);
setFlowControl(flowControl);
setTimeout(seconds, milliseconds);
}

MySerialPort::~MySerialPort()
{
if (sendThread)
{
delete sendThread;
sendThread = NULL;
}
if (receiveThread)
{
delete receiveThread;
receiveThread = NULL;
}
if (isOpen())
port.close();
}

// Open the SerialPort
bool MySerialPort::open()
{
bool res = port.open(QIODevice::ReadWrite);
// If the port is reopened after an earlier closure restores the previous state
if (closeCalled)
{
if (saveStateSendingEnable)
enableSending();
if (saveStateReceivingEnable)
enableReceiving();
if (saveStateReceiveData)
receiveData();
closeCalled = false;
}
return res;
}
bool MySerialPort::open(const QString &name, const BaudRateType baudRate, const DataBitsType dataBits,
const ParityType parity, const StopBitsType stopBits, const FlowType flowControl,
ulong seconds, ulong milliseconds)
{
setPortName(name);
setBaudRate(baudRate);
setDataBits(dataBits);
setParity(parity);
setStopBits(stopBits);
setFlowControl(flowControl);
setTimeout(seconds, milliseconds);
//return open();
return port.open(QIODevice::ReadWrite);
}
// SerialPort is open?
bool MySerialPort::isOpen() const
{
return port.isOpen();
}
// Close the SerialPort
void MySerialPort::close()
{
closeCalled = true;
// Save the state
saveStateSendingEnable = isSendingEnable();
saveStateReceivingEnable = isReceivingEnable();
// Close the port
disableReceiving();
disableSending();
port.close();
// TODO: should I stop send and receive?
}
// Setter and getter for the basic property of the QextSerialPort
void MySerialPort::setPortName(const QString &name)
{
port.setPortName(name);
}
QString MySerialPort::portName() const
{
return port.portName();
}
void MySerialPort::setBaudRate(const BaudRateType baudRate)
{
port.setBaudRate(baudRate);
}
BaudRateType MySerialPort::baudRate() const
{
return port.baudRate();
}
void MySerialPort::setDataBits(const DataBitsType dataBits)
{
port.setDataBits(dataBits);
}
DataBitsType MySerialPort::dataBits() const
{
return port.dataBits();
}
void MySerialPort::setParity(const ParityType parity)
{
port.setParity(parity);
}
ParityType MySerialPort::parity() const
{
return port.parity();
}
void MySerialPort::setStopBits(StopBitsType stopBits)
{
port.setStopBits(stopBits);
}
StopBitsType MySerialPort::stopBits() const
{
return port.stopBits();
}
void MySerialPort::setFlowControl(const FlowType flowControl)
{
port.setFlowControl(flowControl);
}
FlowType MySerialPort::flowControl() const
{
return port.flowControl();
}
void MySerialPort::setTimeout(const ulong seconds, const ulong milliseconds)
{
port.setTimeout(seconds, milliseconds);
}
/*
Send
*/
// Enable the SerialPort to send data (init the thread)
void MySerialPort::enableSending()
{
// If the Sending is not already active AND the sendThead is not initialized
if (!sendingEnable && !sendThread)
{
sendThread = new SendThread(port);
sendingEnable = true;
}
}
// Disable the SerialPort to send data (terminate the thread)
void MySerialPort::disableSending()
{
// If the Sending is already active AND there is a sendThread
if (sendingEnable && sendThread)
{
delete sendThread;
sendThread = NULL;
sendingEnable = false;
}
}
bool MySerialPort::isSendingEnable() const
{
return sendingEnable;
}
// Stop the currently sending data operation (don't terminate the thread)
void MySerialPort::stopSending()
{
// If the Sending is not alread active OR the sendThread is not initialized
if (!sendingEnable || !sendThread)
return;
// If the SerialPort is currently sending data, stop
if (sendThread->isRunning())
{
sendThread->stopSending();
//wait(); ??????????
sendThread->wait();
}
}
// Enqueue data to the sendThread queue
// return 1 OK
// return 2 port closed
// return 3 sending operation disable
uchar MySerialPort::sendData(const QByteArray &data)
{
// check the port if is open
if (!isOpen())
return 2;
// check if sending operation is enable
if (!sendingEnable || !sendThread)
return 3;
sendThread->addDataToSend(data);
return 1;
}
/*
Receive
*/
// Enable the SerialPort to receive data (init the thread)
void MySerialPort::enableReceiving()
{
// If the Receiving is not already active AND the receiveThead is not initialized
if (!receivingEnable && !receiveThread)
{
receiveThread = new ReceiveThread(port);
connect(receiveThread, SIGNAL(dataReceived(const QByteArray &)),
this, SIGNAL(dataReceived(const QByteArray &)));
receivingEnable = true;
}
}
// Disable the SerialPort to receive data (terminate the thread)
void MySerialPort::disableReceiving()
{
// If the Receiving is already active AND there is a receiveThread
if (receivingEnable && receiveThread)
{
delete receiveThread;
receiveThread = NULL;
receivingEnable = false;
}
}
bool MySerialPort::isReceivingEnable() const
{
return receivingEnable;
}
// Stop the currently receiving data operation (don't terminate the thread)
void MySerialPort::stopReceiving()
{
// If the Receiving is not alread active OR the receiveThread is not initialized
if (!receivingEnable || !receiveThread)
return;
// If the SerialPort is currently receiving data, stop
if (receiveThread->isRunning())
{
saveStateReceiveData = false;
receiveThread->stopReceiving();
// wait();
receiveThread->wait();
}
}
// Start the receiving thread
// return 1 OK
// return 2 port closed
// return 3 receiving operation disable
uchar MySerialPort::receiveData()
{
// check the port if is open
if (!isOpen())
return 2;
// check if receiving operation is enable
if (!receivingEnable && !receiveThread)
return 3;
// If the SerialPort receiving thread is not currently active, start it
if (!receiveThread->isRunning())
{
saveStateReceiveData = true;
receiveThread->start();
}
return 1;
}

SendThread::SendThread(QextSerialPort &adrPort): port(adrPort)
{
dataToSend.clear();
stopped = false;
}

SendThread::~SendThread()
{
if (isRunning())
{
stopSending();
wait();
}
}

// Add the data to the Send Queue
void SendThread::addDataToSend(const QByteArray &dataToAdd)
{
QMutexLocker locker(&mutexSend);
for (int i=0; i<dataToAdd.size(); i++)
dataToSend.enqueue(QByteArray(1,dataToAdd.at(i)));
if (!isRunning())
start();
}
// Stop the sending operation
void SendThread::stopSending()
{
stopped = true;
}
// Thread Send Loop
void SendThread::run()
{
QByteArray byteArray;
forever
{
mutexSend.lock();
if (dataToSend.isEmpty() || stopped)
{
mutexSend.unlock();
stopped = false;
break;
}
byteArray = dataToSend.dequeue();
mutexSend.unlock();
port.write(byteArray, 1);
}
}

ReceiveThread::ReceiveThread(QextSerialPort &adrPort) : port(adrPort)
{
stopped = false;
}

ReceiveThread::~ReceiveThread()
{
if (isRunning())
{
stopReceiving();
wait();
}
}

// Stop the sending operation
void ReceiveThread::stopReceiving()
{
stopped = true;
}
// Thread Receive Loop
void ReceiveThread::run()
{
int numBytes = 0;
QByteArray data;
forever
{
if (stopped)
{
stopped = false;
break;
}
numBytes = port.bytesAvailable();
if (numBytes>0)
{
mutexReceive.lock();
data = port.read(numBytes);
mutexReceive.unlock();
emit dataReceived(data);
}
}
}


Added after 5 minutes:

OK! Post sourcecode again. I downloaded them from the following link.
http://www.qtcentre.org/threads/2106...ht=#post103325

Hi, Lesiok
Would you like to give some details about why you think these two function make no sense. Thank you.

wysota
13th March 2013, 13:00
Why do you think they do make sense? For instance what do mutexSend and mutexReceive protect?

Lesiok
13th March 2013, 14:09
1. You don't need extra run method.
2. QExtSerialPort is QIODevice so it have signal readyRead().
3. Why You send only one byte and not all together ?

Your problem is meaningless program running in circles in both run methods.

honestapple
13th March 2013, 14:21
Thank you. It seems that there are something wrong with these two QMutex. The mutexSend is used to protect QQueue dataToSend. For example, if the application need to send some command to the device from the GUI thread using sendData method, the new entered command would disrupt the previous command which was sending. But, there are no need to add mutexSend.lock()/unlock() in run() due to there already has a QMutexLocker in addDataToSend().
The mutexReceive seems to protect QextSerialPort port to prevent writing the port when reading.
I still do not understand why these two run() function make no sense. I have test the device using com assistant. It could receive data at a high speed but has no response to user input when receiving. So I tried to seperate the reading and writing operation in two threads as this example did.
I am a newbie to QT. If I make wrong, please point it out. Thanks a lot.

In addition, Could anybody access the following blog.
Qt and serial ports | fede.tft's Blog
fedetft.wordpress.com/2010/.../qt-and-serial-ports/
Please paste the content to the board.

wysota
13th March 2013, 14:29
The mutexReceive seems to protect QextSerialPort port to prevent writing the port when reading.
And how do you do that if you don't lock the mutex while sending?


I still do not understand why these two run() function make no sense.
Because you are burning cpu cycles like crazy. You don't need those two threads at all. Your program can happily run in one (main) thread. You just need to make sure you try to read something when there is anything to be read.

honestapple
13th March 2013, 14:46
Thank you.
I knew that the signal readyRead(). It is useful for reading data. I have used this signal on the previous version in which the application has only one thread which process reading and writing operation.When I was receiving data, I could not enter command to order the device to stop uploading data.

Your last advice remind me perhaps I should buffer the data before process them.

Added after 15 minutes:

Yes, If there is only one reading operation, There is no need to use thread. But my trouble is when I am reading, I can not write to it(Perhaps closing it is the only choice). So, I tried to use thread to solve this problem.

Lesiok
13th March 2013, 16:45
What do you mean by when I am reading, I can not write to it ?

wysota
13th March 2013, 18:45
Yes, If there is only one reading operation, There is no need to use thread.
No, there is no need to use threads at all here.


But my trouble is when I am reading, I can not write to it(Perhaps closing it is the only choice).
No, closing or not closing has nothing to do with it. If you can't write to a port you have been reading from, it means you are doing it wrong (I mean writing).

graciano
13th March 2013, 22:33
Hi

I'm making some research about serial communication and i came across this post.

The QextSerialPort (http://sourceforge.net/projects/qextserialport/?source=recommended) is labeled as inactive. Is it a good idea to use it?

In my case, i ended up installing the QtSerialPort(http://qt-project.org/wiki/QtSerialPort) module and now i'm starting to read somethings about it.

I want to do some basic serial communication with the Arduino board.

As i have never done anything like this before, is this a good starting option?

wysota
13th March 2013, 23:05
Hi

I'm making some research about serial communication and i came across this post.

The QextSerialPort (http://sourceforge.net/projects/qextserialport/?source=recommended) is labeled as inactive. Is it a good idea to use it?

In my case, i ended up installing the QtSerialPort(http://qt-project.org/wiki/QtSerialPort) module and now i'm starting to read somethings about it.

I want to do some basic serial communication with the Arduino board.

As i have never done anything like this before, is this a good starting option?

How about starting your own thread instead of hijacking this one? This thread is about a specific problem which is not "whether I should choose QExtSerialPort or QtSerialPort".

Lesiok
14th March 2013, 06:17
Hi
I'm making some research about serial communication and i came across this post.
The QextSerialPort (http://sourceforge.net/projects/qextserialport/?source=recommended) is labeled as inactive. Is it a good idea to use it?
In my case, i ended up installing the QtSerialPort(http://qt-project.org/wiki/QtSerialPort) module and now i'm starting to read somethings about it.
I want to do some basic serial communication with the Arduino board.
As i have never done anything like this before, is this a good starting option?

Your research is not very accurate. Here is active QextSerialPort (http://code.google.com/p/qextserialport/). Last version is 1.2 RC.

kuzulis
14th March 2013, 07:21
2 Admins,

Please ban for Taubssmealm or do something else with spam.


2 graciano,

Please, make another thread for your question. Then we can discuss it.

honestapple
15th March 2013, 02:08
Hi,
Thanks for paticipating this discussion. I think I should explain the problems which I have encountered in detail.
First, I think that the device transfer data to PC at hight transfer rate is one key point. I have test the device using many com assistants. No one can works well except accessport from SUDT. It can display the received data in hight frame rate, others show me no display, no response, nothing but freezing. I know that some com assistant use a timer to poll the port.This is a effective method at the low data transfer rate. If the data transfer rate is high, I think polling will make PC application blocking or freezing. The better choice will be multithread(I guess the accessport use multithread). The problem with accessport is when it is receiving data, it can't process input commands which are send to port to changing something, sampling rate, data format, data source, restarting data uploading, etc. Even when I try to close the port, the accessport will choke. The correct step to send commands to the device using accessport is:
1. Stop the device in debug mode, then the accessport will restore responsive.
2. Send commands to device.
3. Then, the device will receive the command and do some change.
I wonder that perhaps it is because that the accessport only have one thread which process reading and writing port simultaneously.
That why I try to use multithread.
The above is only my own opinion. If anybody think I am wrong, please point it out. Thank you.
Then the problem which I encountered in the previous version of the application is that I could not send command when the device is uploading data to the PC. In fact, before the device start to uploading data, the PC should reset the device and shakehand with it. These process all needed reading and writing port, and they works well. But, When the device began to transfer data to PC at high rate, it would be no responsive to click the pushbutton which used to send command to device(I don't want to close the port, I just want to tranfer some command to change the sampling rate or stop transfering, etc).
I have note that this problems only occur when I tried to display the tranfered data(That is only display them in a textbrowser). If I do nothing including gui operation when receiving data, the GUI will be reponsive to my input(That is I can writing some command to port when it is reading data).
But, the aim of the application is receiving data at high speed and display them in realtime!!!!
I have searched on the website, I think the problems stated on the following link are similar to my questions.
http://stackoverflow.com/questions/11426335/qthread-execution-freezes-my-gui
http://stackoverflow.com/questions/8123404/qt-gui-sometimes-freezing-when-using-threads-and-signals-slots
Perhaps it is the contradiction of high data transfer rate and slow gui operations which make the GUI has no reponse.

I still don't understand why wysota said "there is no need to use threads at all here". Does it mean that the Qextserialport has alweady include threads for reading and writing?

If anybody think I am wrong, Please point it out and give me a little detail. Thank you very much.

ChrisW67
15th March 2013, 03:03
I have no idea what bearing any of the stuff about some 3rd party software package has to do with your code, threading, Qt GUI responsiveness etc.

"Very high speed" for a serial port is a mere trickle of data. Compare 115000 bits per second from a "high speed" serial port to 80,000,000+ bits-per-second from a mediocre Ethernet LAN. Qt can saturate the LAN connection over multiple connections without threads... I don't know why you are having difficulty believing that handling data from a few serial ports is hardly an issue.

Sending a command is simply calling QIODevice::write() on the device. Receiving is merely responding to every time the QIODevice emits readyRead(). Sequencing a handshake is a job for a state machine of some sort. Handling multiple ports is just keeping a list of open QIODevices and reading/writing/tracking state for each. All of the activity is non-blocking, asynchronous, single-threaded, and nobody is suggesting you implement a busy poll of the serial ports.

If you are trying to get a UI to update itself entirely after each and every sample/byte received, lets say 10000+ times per second, then of course you are going to have issues. This is simply the result of it taking actual time to draw stuff. While the GUI thread is actually drawing it will not respond to user input and threads will not change that; you need to ensure drawing leaves time to process other events. The user cannot possibly follow updates at high rates anyway so you be better off buffering received data and updating the display perhaps 10-20 times a second. Once again this does not require threads.

kuzulis
15th March 2013, 07:42
@honestapple,

You have the wrong notion about using streaming I/O. And you're doing it all wrong, and complicates the decision.

Ideally, in your case there is no need for multi-threading, because the I/O is asynchronous and non-blocking, the engine of which is the Qt event-loop.

But another thing is that the implementation QExtSerialPort uses "fake" asynchrony.
For example, when reading data read function can be blocked by PENDING I/O (this is true for Windows implementation),
so, it can be lead to suspends Qt event-loop in these moments - which, of course, leads to the some GUI freeze.

To solve problems with asynchronous I/O, I recommend using a different library QtSerialPort (http://qt-project.org/wiki/QtSerialPort)
which does not have these disadvantages.

In any case, regardless of which library you will be using, you have to do this:

1. Use one GUI thread, by default
2. Use async signal/slot approach
3. Use caching for received data, accumulate it for some time or some size and display or re-draw their by chunk-per-chunk.
4. Use queue


You can use the additional thread for:

1. Processing and converting the data (in case of long-term conversion, etc.)
2. Move object of serial port to, like:


class MySerialPort public BaseAnySerialPortLibraryClass
{
...
}

MyThread::run
{
MySerialPort port;
...
...
exec();
}


because the I/O can stop when you click the mouse on the title bar of window or move the window.
But in any case, same in a separate thread, use async approach!

honestapple
20th March 2013, 11:18
Hi, kuzulis
Thank you for your advice. I have tried QtSerialPort. It is an excellent library. But, I still got a question. That is no matter how much data the device send, the QtSerialPort seems that combine them with 512 bytes before it process them.

I have changed the data block size with 128 bytes and 16K and printed the bytesAvailable() in the slot of readyRead() signal. I found it show 512 bytes every time. The data transfered to PC were correct. I just wonder whether this is correct.

Added after 1 1:

Another question
2. Use async signal/slot approach
Do you mean connect the signal/slot with Qt::QueuedConnection?
I tried that and found that it seemed to block the slot so that the data could not be read out(The recieve buffer were full and could not receive any more).

kuzulis
20th March 2013, 11:38
That is no matter how much data the device send, the QtSerialPort seems that combine them with 512 bytes before it process them


When each Rx event that received from driver, then QtSerialPort do try to read from the port 512 bytes,
regardless of how many bytes are actually located in the receive buffer of driver. You can change this
value in the code to search for optimal performance, but the main feature is that between the two signals
readyRead(), really come of ~8-10 bytes, depending on the BaudRate, device driver type and etc.




I found it show 512 bytes every time. The data transfered to PC were correct. I just wonder whether this is correct.


Most likely this is a bug. But this bug was previously fixed for Windows, please try update repo.
Or this bug was reproduced on Linux, MacOSX?

Added after 4 minutes:


Do you mean connect the signal/slot with Qt::QueuedConnection?
Yes, I mean connection by default.



I tried that and found that it seemed to block the slot so that the data could not be read out(The recieve buffer were full and could not receive any more).

Please provide here your code with receiving data.

honestapple
20th March 2013, 12:10
CSerialPortWidget::CSerialPortWidget( QWidget *parent )
: QWidget( parent )
{
/* Initialize widgets and layout */
...

/* Initialize serial port object */
InitDevice();// Reset and handshake using blocking IO

connect( m_spSerialPort, SIGNAL( readyRead() ), this, SLOT( slt_DataReceived() ), Qt::QueuedConnection );
connect( m_pbStartSerialPort, SIGNAL( clicked() ), this, SLOT( slt_pbStartSerialPort_Clicked() ) );
}

When I clicked the start pushbutton, the application send a command to the device. So, the device began to sample data. There are a buffer with 16384 bytes in the device. When the buffer was full, the device upload them through USB virtual COM port.


void CSerialPortWidget::slt_DataReceived()
{
qDebug() << m_spSerialPort->bytesAvailable();
QString retSeq = m_spSerialPort->readAll();
// Indeed the QtPlainTextEdit could not display the content. It just displayed a part and frozen. //When I stop transfer, It would display the rest.
m_pteReceive->appendPlainText(retSeq);
QScrollBar *sbScroll = m_pteReceive->verticalScrollBar();
sbScroll->setValue( sbScroll->maximum() );
}

If I use the Qt::DirectConnection, the output block size by qDebug() is 512 bytes(No matter how much data the device transfered actually). If I use the Qt::QueuedConnection, the output of the qDebug is the following:
512
3584
0
0
0
0
0
0
512
3584
0
0
0
0
0
0
512
3584
0
0
0
0
0
0
512
3584
0
0
0
0
0
0
...// Same as above
512
8192 // I have set the size of receive buffer to 8192 bytes. From now on, It did not change any more
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0

kuzulis
20th March 2013, 12:23
1. You use last QtSerialPort repo?
2. You try on Windows or another OS?
3. Do not use setReadBufferSize() if there are no reasons.

UPD: Seems, all rigth, your device send to you packet of data with 4096 bytes each.. What are the problems?

honestapple
20th March 2013, 13:11
I have downloaded the stable version of sourcecode(Wednesday March 20 2013) from the link "http://qt.gitorious.org/qt/qtserialport".

The platform: visual studio 2008 + QT4.8.6 on Windows XP

I have removed the setReadBufferSize().

Now, If I use the Qt :: DirectConnection, the result is the same as above. If I use the Qt::QueuedConnection, the output of the qDebug is the following:
512
3584
4096
8192
8192
12288
16384
16384
24576
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
16384
0
0
0
... Always zeros.

The data block size is correct. But, It seems that these zeros means that there is readyRead signal but the avalible bytes are zero. It seems contradict. I met this condition before. When I used the waitForReadyRead function(I used this function to receive data in device initialization stage), I must close the port after receive some data successfully. Otherwise, It would got a readyRead signal with zero avaliable byte. If I closed the port and reopen it for next receiving, everything is OK.

kuzulis
20th March 2013, 14:16
... Always zeros.

Zeros - this is a consequence QueuedConnection.

The fact that readyRead() signal is emitted each time when successfully added a portion of the data in the internal buffer.
But because slot is called NOT immediatelly - but in queue, this lead to this behavior.

For more information:
~~~~~~~~~~~~~~~~

For example, you receive 100 bytes, but really this data stream can received as chunk per chunk by 10 byte each.
So, then you should get the following behavior:




Automatically processing from class internally:

01. RX_EVENT occur -> Read 10 bytes -> emit ReadyRead() ( really bytes available == 10 )
02. RX_EVENT occur -> Read 10 bytes -> emit ReadyRead() ( really bytes available == 20 )
...
...
09. RX_EVENT occur -> Read 10 bytes -> emit ReadyRead() ( really bytes available == 90 )
10. RX_EVENT occur -> Read 10 bytes -> emit ReadyRead() ( really bytes available == 100 )

Read slot sequence :

01. Called Slot: -> port->bytesAvailable() == 10, port->readAll() ( after read, really bytes available == 90)
...
<some long time> // It was some time between calls to the two slots, for example, at this moment Qt event-loop was a bit loaded. So, we can get next port->bytesAvailable() > 10 byte!
...
02. Called Slot: -> port->bytesAvailable() == 90, port->readAll() ( after read, really bytes available == 0)
03. Called Slot: -> port->bytesAvailable() == 0, port->readAll() ( after read, really bytes available == 0)
...
...
09. Called Slot: -> port->bytesAvailable() == 0, port->readAll() ( after read, really bytes available == 0)
10. Called Slot: -> port->bytesAvailable() == 0, port->readAll() ( after read, really bytes available == 0)



It's all because of QueuedConnection! :)

So, I do not know if this is a bug or a feature... But I will think about how can reduce the amount
of emit readyRead() in order to relieve the Qt event-loop.

Thanks for the interesting catch! :)

honestapple
3rd April 2013, 10:13
When each Rx event that received from driver, then QtSerialPort do try to read from the port 512 bytes,
regardless of how many bytes are actually located in the receive buffer of driver. You can change this value in the code to search for optimal performance.

Hi kuzulis,
1. How to change the default value of the bytes read from the port by QtSerialPort?
2. If I transfer a data block which have bytes less than 512, Does QtSerialPort still read 512 bytes or read actual bytes in the buffer?

kuzulis
3rd April 2013, 18:45
1. How to change the default value of the bytes read from the port by QtSerialPort?
Need modify source code in "qserialport_p.h" for ReadChunkSize enum.

But this "optimization" makes sense only if port speed very big (for example 4Mbod), i.e. if really between implicity
internal read operations comes to the buffer of driver more than 512 bytes.


2. If I transfer a data block which have bytes less than 512, Does QtSerialPort still read 512 bytes or read actual bytes in the buffer?

QtSerialPort always trying to read by chunks by 512 bytes!
But this internal implementation of a class shan't excite by no means you as the user. It shall be hidden from the user.

PS:
Most likely your problems with application performance are related to something another, for example to your design of application.
Therefore all my responses don't make sense, from the practical point of view of the user, i.e. for your problem.

honestapple
6th April 2013, 12:35
Hi,
Thank you for your detailed explaination and patience:D!I nearly have got the application to run as what I wanted------receiving data and plotting them in real time. If you does not mind, I still want to have a discussion with you about the phenomenon in the experiments.
The device communicated the PC through USB to USART pattern. The maximum of the data packet is 64 bytes. In the experiments, I found that when I transfered data with amount less than 64 bytes, etc 32 bytes, The readyRead signal would be generated after every chunks holding 32 bytes were received, even if I used queueconnection to connect the signal with its slot. If the size of chunks was increased to 64 bytes, The readyRead signal would be generated every chunks of 512 bytes received. Indeed, when I print the output of bytesAvailable(), the result was following:
...
512
3584
0
0
0
0
0
0
512
3584
0
0
0
0
0
0
512
3584
...
When the size of chunks was larger than 64 bytes, the output was similar to above except that ended with 4096(This probably related with the USB CDC driver I used).
Perhaps the large amount would lead to a long time GUI operation, so it seem to transfer chunks with smaller size(less than 64 bytes).
In my experiments, in order to let the GUI have time to do some operation, I sent command to MCU to fetch data every 10 ms.

I am working with QT4.8.6+QWT6.0.2+QtSerialPort(latest) on Windows XP.