When in model methods rowCount() and columnCount() I try to retrieve data from server using QTcpSocket, the model calls these methods few times but not calls data() afterwards. The associated QTableView displays nothing. I checked with debugger and it shows that rowCount() and columnCount() are returning valid data.
The model is derived from QAbstractTableModel. Here's the code of TcpTableModel::rowCount():
//Setup waiting for server response
QTimer timer;
timer.setSingleShot(true);
QEventLoop eventLoop;
connect(&m_tcpSocket, &QTcpSocket::readyRead, &eventLoop, &QEventLoop::quit);
connect(&timer, &QTimer::timeout, &eventLoop, &QEventLoop::quit);
timer.start(TIMEOUT);
//Using QDataStream to send and receive model information
QDataStream dataStream(&m_tcpSocket);
dataStream << MSG_REQUEST_ROWS_COUNT;//Sending request for number of rows
eventLoop.exec();
//If no response received
if(!timer.isActive())
{
connect(&m_tcpSocket, &QTcpSocket::readyRead, this, &TcpTableModel::readyRead);
return 0;
}
timer.stop();
//Receiving information from server
dataStream.startTransaction();
int msgType = -1;
int rowCount = -1;
dataStream >> msgType;
dataStream >> rowCount;
if((msgType != MSG_REQUEST_ROWS_COUNT) || (rowCount == -1))
{
dataStream.commitTransaction();
return 0;
}
dataStream.commitTransaction();
return rowCount;
The server uses same approach to process the request. It receives MSG_REQUEST_ROWS_COUNT and then sends back MSG_REQUEST_ROWS_COUNT and an int containing the number of rows.
What could be the source of such behavior in my program? Maybe QTableView can't work properly if its requests to model are satisfied with delay. Or maybe I should use completely different approach to get model data from server?
Related
I have made new QTimer object in my class's ctor. When the application starts the timer also starts with the specified time. This timer keeps a track of all the process IDs and if finds that a particular process-id is not active(killed from task manager) then it restarts that particular process. Now the issue is my application lags a-bit due to this QTimer object and only after the timeout, the application becomes smooth. But after 5 seconds the timer starts again and the application starts lagging again. What is the best alternative to start a QTimer object apart from ctor of a class?
qTimer = new QTimer(0);
connect(qTimer, SIGNAL(timeout()), this, SLOT(RestartStoppedProcess()), Qt::DirectConnection);
connect(qTimer, SIGNAL(destroyed()), qTimer, SLOT(deleteLater()), Qt::DirectConnection);
qTimer->setTimerType(Qt::VeryCoarseTimer);
qTimer->start(5000);
void ProcessMonitor::RestartStoppedProcess()
{
try
{
QProcess *objMonitorProcess = new QProcess(this);
connect(objMonitorProcess, SIGNAL(finished(int,QProcess::ExitStatus)), objMonitorProcess, SLOT(deleteLater()));
for(int i = 0; i < ui->twShowProcess->rowCount(); i++)
{
#ifdef Q_WS_LIN
// objMonitorProcess->start("pidof", QStringList() << (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
objMonitorProcess->start("pidof "+ (ui->twShowProcess->item(i, 0)->text()), QIODevice::ReadOnly);
#elif Q_WS_WIN
QString cmd("wmic process where ");
QString qszFilters("\"processid=\'");
QString qszSubFilters = qszFilters.append(ui->twShowProcess->item(i, 1)->text().append("\'").append("\"").append(" get name"));
cmd.append(qszSubFilters);
objMonitorProcess->start(cmd.toStdString().c_str(), QIODevice::ReadOnly);
#else
QString qszProcessName = ui->twShowProcess->item(i, 0)->text().split(".").takeFirst();
objMonitorProcess->start("pgrep", QStringList() << qszProcessName, QIODevice::ReadOnly);
#endif
if((objMonitorProcess->waitForStarted(4000) == true) && (objMonitorProcess->state() == QProcess::Running))
{
if(objMonitorProcess->waitForFinished(3000) == false)
continue;
else
{
QString qszProcessStatus = ui->twShowProcess->item(i, 3)->text();
if(qszProcessStatus.compare("Running") == 0)
{
QString qszStandardOutput, qszStandardError;
qszStandardOutput.clear(); qszStandardError.clear();
qszStandardOutput = QString(objMonitorProcess->readAllStandardOutput());
qszStandardError = QString(objMonitorProcess->readAllStandardError());
if(qszStandardError.simplified().compare("No Instance(s) Available.", Qt::CaseInsensitive) == 0 && qszStandardOutput.simplified().isEmpty() == true)
{
ui->twShowProcess->selectRow(i);
if(ui->twShowProcess->selectedItems().at(0)->text().compare("LTtagdb.exe", Qt::CaseInsensitive) == 0 ||
ui->twShowProcess->selectedItems().at(0)->text().compare("LTmessagebroker.exe", Qt::CaseInsensitive) == 0)
this->StopAllApplicationsForcefully();
else
this->on_actionStop_triggered();
if(this->ptrobjContextMenu->isVisible())
this->ptrobjContextMenu->close();
ui->twShowProcess->clearSelection();
if(mobj_ProcessLogMap.mobj_ProcessAndLogMapping.contains(ui->twShowProcess->item(i, 8)->text()) == true)
{
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.remove(ui->twShowProcess->item(i, 8)->text());
mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
else
this->mobj_ProcessLogMap.mobj_ProcessAndLogMapping.insert(ui->twShowProcess->item(i, 8)->text(), QString(""));
}
if(ui->twShowProcess->item(i, 0)->text() == "LThistdb.exe")
{
if(IsServiceRunning(QString("LThistdb.exe")) == false)
this->on_actionStopPostgreSQL_DB_Server_triggered();
}
qszStandardOutput.clear();
}
}
}
else
break;
#ifdef Q_WS_WIN
cmd.clear();
qszFilters.clear();
qszSubFilters.clear();
#endif
}
}
catch(QProcess::ProcessError pError)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Crashed", "Process error returned code: " + QString::number(pError));
}
catch(...)
{
QMessageBox objCrashExit;
objCrashExit.information(this, "Unknown Crash", "Unknown crash exit-child process may not be running correctly!");
}
}
QTimer runs in the UI thread so it blocks your UI if it takes a long time to execute. A simple solution is to just run your "processing" stuff in another background thread, the easiest way to do that in Qt might be using QtConcurrent::run like this from your timeout slot called from the QTimer:
QtConcurrent::run(yourProcessingFunction);
You can obviously create your own Thread and use the build in QTimer from QObject::startTimer(...), but that is more work usually.
The problem is likely due to the amount of work being done by RestartStoppedProcess.
As the documentation for QTimer states, when discussing the called slot:
It should be written in such a way that it always returns quickly (typically after processing one data item) so that Qt can deliver events to the user interface and stop the timer as soon as it has done all its work.
As you've witnessed with your application, a heavy workload with a QTimer on the main thread can cause lag in the Gui. The Qt documentation goes on to state:
This is the traditional way of implementing heavy work in GUI applications, but as multithreading is nowadays becoming available on more and more platforms, we expect that zero-millisecond QTimer objects will gradually be replaced by QThreads.
With this in mind, I suggest refactoring your code so that RestartStoppedProcess functions in a separate thread. There's a great tutorial on "How to Really Truly Use QThread" here.
There are various methods of reading and writing from a QTcpSocket using a QDatastream as seen here
The difference is, I will be sending more than "one packet" or blocks.
A basic implementation on the server (sending) side and client (recieving) is seen below - only the actual sending and receiving snippets are shown
More Info, What I tried:
When writing to a QTcpSocket, I attempted to use the QTcpSocket::canReadLine() however this fails straightup after the QTcpSocket::waitForReadReady() signal fires.
I then tried QDataStream::atEnd() in a while loop which causes a system crash :(
The code below shows my latest attempt of going through the QDataStream docs, and utilzing the commitTransaction where it states
If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive.
Under the heading Using Read Transactions. But ofcourse, this just reads one block that is sent, i.e the first block.
Question:
When writing to a QTcpSocket multiple times, and flushing the socket each time to send that data, how can I read this from a QTcpSocket as it is send, keep the original "send structure"?
The example below only reads the first block and ends. I would like to read the block containing "Response 2" and "Response 3".
Code Implementations:
//server.h
//...
QTcpSocket *clientSocket = nullptr;
QDataStream in;
//...
//server.cpp
//...
in.setDevice(clientSocket);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (in.commitTransaction())
ui->lblOut->setText(nextFortune);
if (clientSocket != nullptr) {
if (!clientSocket->isValid()) {
qDebug() << "tcp socket invalid";
return;
}
if (!clientSocket->isOpen()) {
qDebug() << "tcp socket not open";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString(QString("Response:") + nextFortune);
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 2");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 3 here, and this is the end!");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
clientSocket->disconnectFromHost();
}
//...
And the client side
//client.h
//...
QTcpSocket *tcp_con = nullptr;
QDataStream in;
//...
//client.cpp
//...
if(!tcp_con->waitForReadyRead()){
qDebug(log_lib_netman_err) << "tcp con timeout for reading";
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::SocketError, QString());
}
in.setDevice(tcp_con);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QList<QString> data_rcv = QList<QString>();
QString s;
// while (tcp_con->canReadLine()) {
// in >> s;
// data_rcv.push_back(s);
// }
// while (!in.read) {
in >> s;
data_rcv.push_back(s);
// }
while (!in.commitTransaction()){
qDebug(log_lib_netman_info) << "waiting for more data";
in >> s;
data_rcv.push_back(s);
// qDebug(log_lib_netman_err) << "Unable to send data to server";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::FailedReceiving, QString());
}
// if (s.isEmpty()) {
// qDebug(log_lib_netman_err) << "Empty response recieved";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::NoDataRecieved, QString());
// }
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::ReceivedSuccess, data_rcv);
Help would be greatly appreciated!
I'm trying to log in to a webpage and then download some data that only members can download.
I've performed the post request and then the get request, for some reason the get request was finished first.
Is there any way to wait for the post to be finished or make it done first?
edit:
void downloader::LoginToHost()
{
QNetworkRequest request(QUrl("http://www.example.com/login.php"));
request.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/x-www-form-urlencoded"));
QByteArray data("email=example&password=");
manager->post(request,data);
}
void downloader::Do_Request(QUrl url)
{
manager->get(QNetworkRequest(url));
}
void downloader::finished(QNetworkReply * reply)
{
QMessageBox mess;
if(reply->error() == QNetworkReply::NoError)
{
std::ofstream source("source-code.txt");
QString content = reply->readAll();
source << content.toStdString() << std::endl;
source.close();
mess.setText("Done download!");
}
else mess.setText(reply->errorString());
mess.exec();
}
Send the post and don't send the get until the post returns and triggers the finished signal in QNetworkReply.
Assuming there are two requests, one for the post and one for the get, which you've set up, here's an outline of what you can do: -
QNetworkRequst postRequest;
QNetworkRequest getRequest;
Sending the postRequest returns a QNetworkReply...
QNetworkAccessManager* networkAccessManager = new QNetworkAccessManager;
QNetworkReply postReply = networkAccessManager->post(postRequest, someData);
QNetworkReply getReply = nullptr; // C++ 11 nullptr
You can then connect to the postReply finished signal: -
// using a connection to a lambda function, send the get request when post has finished
connect(postReply, &QNetworkReply::finished, [this, networkAccessManager, getRequest, getReply](){
// handle the reply...
...
// send the getRequest
getReply = networkAccessManager->get(getRequest);
});
Here is a function that get the translation from google translate and return the result:
QString QGoogleTranslate::translate(const QString &keyword, const QString &from, const QString &to)
{
//Locate the translation in the map
QMap<QString, QPair<QString, QString> >::iterator itr = translations.find(keyword);
if(itr != translations.end())
{
if(itr.value().first == to) {
result = itr.value().second;
return result;
}
}
//Translate URL
QString url = QString("http://translate.google.com/translate_a/t?client=t&text=%0&hl=%1&sl=%2&tl=%1&multires=1&prev=enter&oc=2&ssel=0&tsel=0&uptl=%1&sc=1").arg(keyword).arg(to).arg(from);
QNetworkAccessManager manager;
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
//Get reply from Google
do
{
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} while (!reply->isFinished());
//Convert to string
result = reply->readAll();
reply->close();
//Free memory
delete reply;
//Remove [[[" from the beginning
result = result.replace("[[[\"", "");
//Extract final translated string
result = result.mid(0, result.indexOf(",\"") - 1);
//Add the translation to the map so we don't need to make another web request for a translation
translations[keyword] = QPair<QString, QString>(to, result);
return result;
}
But as you see there's a do while loop that stops application until reply->isFinished(), and when I use SIGNAL(finished()) from QNetworkReply instead of do while loop, that's not gonna work!
How can I do that without any interruption?
Move everything after the while to a slot and connect it to reply's finished() signal, you'll need to store reply as a field.
You are going to need a new signal that emits the result.
At some point you either need a processEvents loop or return to all the way back to the thread's event loop.
If you want a "blocking" way, you may use QEventLoop:
Instead of infinite loop in your code with processEvents use following pattern:
QEventLoop loop;
connect( reply, &QNetworkReply::finished, &loop, &QEventLoop::quit );
loop.exec();
For working with responce you may use QJsonObject and other.
I have two QT apps. One app can be considered to hold a big data and it sends about 10 KB of data chunk every second to second application.
Earlier I tried using QUdpSocket to transmit the data but due to MTU limitation of about 2-5K and need to divide and reunite the data myself, I switched to QTcpSocket.
Sometimes data is sent correctly with QTcpSocket (especially if I write data very frequently ~every 100 ms) but sometimes data is not sent at all. Not even after 5 sec. And sometimes several data chunks are internally buffered for a long period (several seconds) and then sent together.
m_socket = new QTcpSocket(this);
m_socket->connectToHost(QHostAddress::LocalHost, 45454);
sendDataEverySec()
{
QByteArray datagram(10000, 'a');
qint64 len = m_socket->write(datagram);
if(len != datagram.size())
qDebug() << "Error"; //this NEVER occurs in MY case.
m_socket->flush();
}
On receiver side, I use readyRead signal to know when data has arrived.
How can I ensure that data is sent immediately? Are there any better alternatives for what I am trying to do?
Edit:: When I am writing after long gaps of 1 second, I receive "QAbstractSocket::SocketTimeoutError" on receiver side everytime sender sends data. This error is not received if sender writes data frequently.
Is it OKAY to use QTcpSocket to stream data the way I am doing????
Edit 2: On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so no need for waitForReadyRead.
I am receiving all data sent but still data does not come immediately. Sometimes two-three chunks are merged. This may be because of delay on receiver side in reading data etc.
On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so there is no need for waitForReadyRead.
It had solved my issue.
my tipical solution for client server app.
on server side :
class Server: public QTcpServer {
public:
Server(QObject *parent = 0);
~Server();
private slots:
void readyRead();
void disconnected();
protected:
void incomingConnection(int);
};
on cpp:
void Server::incomingConnection(int socketfd) {
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescriptor(socketfd);
connect(client, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
}
void Server::disconnected() {
QTcpSocket *client = (QTcpSocket*) sender();
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CLIENT DISCONNECTED " << client->peerAddress().toString();
}
void Server::readyRead() {
QTcpSocket *client = (QTcpSocket*) sender();
while (client->canReadLine()) {
//here i needed a string..
QString line = QString::fromUtf8(client->readLine()).trimmed();
}
}
on client:
class Client: public QTcpSocket {
public:
Client(const QHostAddress&, int, QObject* = 0);
~Client();
void Client::sendMessage(const QString& );
private slots:
void readyRead();
void connected();
public slots:
void doConnect();
};
on cpp:
void Client::readyRead() {
// if you need to read the answer of server..
while (this->canReadLine()) {
}
}
void Client::doConnect() {
this->connectToHost(ip_, port_);
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CONNESSIONE...";
}
void Client::connected() {
qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a "
<< ip_ << " e PORTA " << port_;
//do stuff if you need
}
void Client::sendMessage(const QString& message) {
this->write(message.toUtf8());
this->write("\n"); //every message ends with a new line
}