I make an http operation(get,post etc...) by using QNetworkAccessManager. I run a few "get" operation in paralel. For this , I use QtConcurrent::run(this,&RestWebservice::GetHTTPData) to make multi HTTP operations.
My problem is When I close the app before HTTP operation does not complete , App is crashed.Application Output write this line QMutex: destroying locked mutex then write The program has unexpectedly finished.
I guest problem occurs in this line
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
But I am not sure because QtCreater Debugger is not good like VS.By the way , GetHTTPData is in different class.
MY CODE for start network Operation:(MobileOperation.cpp).For exapmle getUserAccount metod start a http operation.
void MobileOperations::getWorkOrderListT(int ekipId) {
MyGlobal::Metods metod=MyGlobal::EkipIsEmriListesi;
QString parameters="{EkipId}";
QMap<QString,QVariant> paramlist;
paramlist["EkipId"]=ekipId;
GetHTTPData(metod,parameters,paramlist);
if(m_workorder.IsSuccess==true)
{
// emit successupdatewo();
if(m_workorder.workorders.count()>0)
{
InsertWo(json.workorder->workorders);
emit processstop("İş Emri Listesi Güncellendi");
// QThread::sleep(2);
}
else
{
emit processstop(json.workorder->ReturnMessage);
}
emit successworkstart();
}
else
{
emit processstop("Bağlantı Başarısız Oldu");
}
}
void MobileOperations::getUserAccount(QString kullaniciAdi, QString sifre,bool isremember)
{
json.user=m_user;
QtConcurrent::run(this,&MobileOperations::getUserAccountT,kullaniciAdi,sifre,isremember);
// getUserAccountT(kullaniciAdi,sifre,isremember);
processstart("Baglaniyor");
}
void MobileOperations::GetHTTPData(MyGlobal::Metods MetodName, QString Parameters, QMap<QString, QVariant> paramlist)
{
try
{
parameter=new HttpRequest();
parameter->url=m_url;
parameter->metodname=MetodName;
parameter->resource=m_path;
parameter->appid=m_appid;
parameter->apppass=m_apppass;
parameter->parametersname=Parameters;
parameter->params=paramlist;
rest= new RestWebservice(parameter->GenerateHTTPQuery(),MetodName);
// json=new JSonParser();
// loop=new QEventLoop();
loop=new QEventLoop();
QObject::connect(rest,SIGNAL(sendhttpdata(QByteArray,MyGlobal::Metods)),&json,SLOT(onGetData(QByteArray,MyGlobal::Metods)));
QObject::connect(&json,SIGNAL(serilazitionCompleted()),loop,SLOT(quit()));
rest->get();
loop->exec();
}
catch(std::string &exp)
{
qDebug()<<"Sonlandırıldı";
}
}
MY CODE of classes For HTTP operatins :
#include "restwebservice.h"
#include <QJsonDocument>
#include<QJsonArray>
#include <QJsonObject>
#include<QJsonValue>
#include<QList>
#include <QThread>
RestWebservice::RestWebservice(QNetworkRequest request,
MyGlobal::Metods metod,
QObject* parent):QObject(parent),m_networkrequest(request),m_metodname(metod)
{
connect(&m_networkManager, SIGNAL(finished(QNetworkReply*)),this, SLOT(onResult(QNetworkReply*)));
// connect(&m_networkManager,SIGNAL())
}
void RestWebservice::get()
{
// mutex.lock();
m_networkManager.get(m_networkrequest);
// mutex.unlock();
}
void RestWebservice::post(QString request)
{
QByteArray requestA= request.toUtf8();
m_networkManager.post(m_networkrequest,requestA);
}
void RestWebservice::onResult(QNetworkReply* reply)
{
try
{
if (reply->error() != QNetworkReply::NoError)
{
qDebug()<<reply->error()<<":"<<reply->errorString();
MyGlobal::NetworkStatus=reply->errorString();
emit sendhttpdata(m_data,m_metodname);
return;
// throw(reply->errorString().toStdString());
}
QByteArray data = reply->readAll();
reply->deleteLater();
m_data=data;
MyGlobal::NetworkStatus="Tablolar Yüklendi";
emit sendhttpdata(m_data,m_metodname);
}
catch(std::string exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp);
}
catch(std::exception &exp)
{
qDebug()<<"Exception:"<<QString::fromStdString(exp.what());
}
}
void RestWebservice::onError()
{
qDebug()<<"Hata VAR";
}
HttpRequest::HttpRequest(QObject *parent) :
QObject(parent)
{
}
QNetworkRequest HttpRequest::GenerateHTTPQuery()
{
// QString path="";
QString path=QString("/%1/%2/%3/%4/%5").arg(resource).arg(MyGlobal::getMetodName(metodname)).arg(appid).arg(apppass).arg(parametersname);
foreach (QString param, params.keys()) {
path.replace("{"+param+"}",params[param].toString());
}
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
QNetworkRequest HttpRequest::GenerateHTTPQueryPost()
{
// QString path="";
QString path=QString("/%1/%2").arg(resource).arg(MyGlobal::getMetodName(metodname));
QUrl m_url(url);
m_url.setPath(path);
m_request.setUrl(m_url);
m_request.setRawHeader("Content-Type","application/json;charset=utf-8");
// m_request.setRawHeader("SOAPAction","http://tempuri.org/IMobileClient/UserAuth");
qDebug()<<m_url.url();
return m_request;
}
Is you mutex a member of your class. In that case the mutex is destructed before it is unlocked (as I presume the containing class goes out of scope), which causes the message you see. The destructor of the mutex is called when the class is destructed, while the lock is held. This is a problem. Typically you will have to find a way to not block indefinitely during your network request.
my problem is I need to show a message ask users wait when I check network availability of other clients.My way is I have a class workerThread to do the business, before start it I create a qMessageBox. But the message only shows the title, not the content. I have no idea why, pls help :(
Here's the worker thread:
#include <QObject>
#include <QString>
#include "clientdataobj.h"
class WorkerThread : public QObject
{
Q_OBJECT
public:
explicit WorkerThread(QObject *parent = 0);
QList<ClientDataObj> listClient() const;
void setListClient(const QList<ClientDataObj> &listClient);
signals:
void finished();
void error(QString err);
void listClientPingChecked( QList <ClientDataObj> list);
public slots:
void testPing();
private:
QList <ClientDataObj> mListClient;
bool pingEachClient(QString ip);
};
implement:
#include "workerthread.h"
#include <QFile>
#include <QMessageBox>
#include <QTextStream>
WorkerThread::WorkerThread(QObject *parent) :
QObject(parent)
{
}
void WorkerThread::testPing()
{
if (mListClient.size()==0) {
emit finished();
return;
}
else{
for(unsigned i=0;i<mListClient.size();i++){
bool result = pingEachClient(mListClient[i].ip());
if(result)
mListClient[i].setStatus(true);
else
mListClient[i].setStatus(false);
}
emit listClientPingChecked(mListClient);
}
emit finished();
}
bool WorkerThread::pingEachClient(QString ip)
{
QString pingCommand = "ping " +ip + " -c 3 | grep loss | awk ' {print $7}' > pingResult.txt";
system(qPrintable(pingCommand));
QString lossPercentTxt = readFileText("pingResult.txt") ;
lossPercentTxt.chop(1);
int lossPercent = lossPercentTxt.toInt();
if(lossPercent<10){
return true;
}
else return false;
}
QList<ClientDataObj> WorkerThread::listClient() const
{
return mListClient;
}
void WorkerThread::setListClient(const QList<ClientDataObj> &listClient)
{
mListClient = listClient;
}
How I call it in MainWindow:
on_pbSendUpdate_clicked()
{
changeModeWaitPing();
getClientOnlineList();
}
getClientOnlineList()
{
if(mListClient.size()==0){
return;
}
mpThreadPing = new QThread;
mpWorkerThread = new WorkerThread;
mpWorkerThread->setListClient(mListClient);
connectThreadPingToGui();
mpThreadPing->start();
}
changeModeWaitPing()
{
ui->pbSendUpdate->setEnabled(false);
callMsgBox("Pinging client... Pls wait!");
// callWaitDialog();
}
callMsgBox( QString text)
{
if (NULL==mMsg) {
return;
}
mMsg->setWindowTitle("INFO");
// mMsg->setAttribute(Qt::WA_DeleteOnClose);
mMsg->setWindowModality(Qt::NonModal);
mMsg->setModal(false);
QString info ="Pinging client... Pls wait!";
mMsg->setText(info);
mMsg->show();
}
connectThreadPingToGui()
{
connect(mpWorkerThread, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(mpThreadPing, SIGNAL(started()), mpWorkerThread, SLOT(testPing()));
connect(mpWorkerThread, SIGNAL(finished()), mpThreadPing, SLOT(quit()));
connect(mpWorkerThread, SIGNAL(finished()), mpWorkerThread, SLOT(deleteLater()));
connect(mpThreadPing, SIGNAL(finished()), mpThreadPing, SLOT(deleteLater()));
connect(mpWorkerThread,SIGNAL(listClientPingChecked(QList<ClientDataObj>)),this,SLOT(updateListClientOnline(QList<ClientDataObj>)));
}
updateListClientOnline(QList<ClientDataObj> list)
{
mListClientOnline = list;
mPingDone = true;
if (NULL==mMsg) {
return;
}
else{
mMsg->hide();
}
if(mpDialogWaitPing==NULL){
return;
}
else{
mpDialogWaitPing->hide();
}
launchClientListTable();
}
You create a new thread, but you don't move any objects to that thread. So your new thread does nothing. I assume you wan't mpWorkerThread to be moved to the new thread. In that case you're missing mpWorkerThread->moveToThread(mpThreadPing);
I need to handle incoming tcp connections on individual QThread's.
After successful client authentication, the according socket should be stored in an QList object.
[simplified main/server-side application]
class Server : public QObject
{
Q_OBJECT
public:
Server();
private:
QList<QTcpSocket*> m_connections;
QTcpServer m_server;
void handleIncomingConnection();
void handleWaiterThread();
private slots:
void treatFinishedWaiterThread();
}
[according function definitions]
handleIncomingConnection() slot is connected with the server object's (m_server) newConnection() signal.
void Server::handleIncomingConnection()
{
QThread *waiter = new QThread();
connect(waiter, SIGNAL(started()), this, SLOT(handleWaiterThread()));
connect(waiter, SIGNAL(finished()), this, SLOT(treatFinishedWaiterThread()));
moveToThread(waiter);
waiter->start();
}
void Server::handleWaiterThread()
{
// fetch requesting socket
QTcpSocket *socket = m_server->nextPendingConnection();
// HANDLE PASSWORD AUTHENTICATION HERE ...
// IF SUCCESSFUL, CONTINUE
connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
// add to list
m_connections.append(socket);
}
void Server::treatFinishedWaiterThread()
{
QThread *caller = qobject_cast<QThread*>(sender());
caller->deleteLater();
}
If I try to run this, the threads get created but no SIGNAL is emitted when they're done, so I can't delete threads afterwards. Additionally I get this message:
QObject::moveToThread: Widgets cannot be moved to a new thread
How to fix this?
[01.06.2016]
According to QTcpServer::nextPendingConnection() it says:
The returned QTcpSocket object cannot be used from another thread. If you want to use an incoming connection from another thread, you need to override incomingConnection().
So in the end I have to create another class that inherits from QTcpServer.
[01.07.2016 #1]
I revised my code and added a custom server and waiter thread class.
[custom server class]
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
WServer(QObject* = nullptr) : QTcpServer(parent) {}
signals:
void connectionRequest(qintptr);
protected:
void incomingConnection(qintptr socketDescriptor)
{
emit connectionRequest(socketDescriptor);
}
};
[custom thread class]
class Waiter : public QThread
{
Q_OBJECT
public:
Waiter(qintptr socketDescriptor, QObject *parent = nullptr)
: QThread(parent)
{
// Create socket
m_socket = new QTcpSocket(this);
m_socket->setSocketDescriptor(socketDescriptor);
}
signals:
void newSocket(QTcpSocket*);
protected:
void run()
{
// DO STUFF HERE
msleep(2500);
emit newSocket(m_socket);
}
private:
QTcpSocket *m_socket;
};
[new main class]
class ServerGUI : public QWidget
{
Q_OBJECT
public:
Server(QObject*);
private:
QList<QTcpSocket*> m_connections;
CustomServer m_server;
private slots:
void handleConnectionRequest(qintptr);
void handleNewSocket(QTcpSocket*);
}
void CustomServer::handleConnectionRequest(qintptr socketDescriptor)
{
Waiter *nextWaiter = new Waiter(socketDescriptor, this);
connect(nextWaiter, SIGNAL(newSocket(QTcpSocket*)), this, SLOT(handleNewSocket(QTcpSocket*)));
connect(nextWaiter, SIGNAL(finished()), this, SLOT(deleteLater()));
nextWaiter->start();
}
void CustomServer::handleNewSocket(QTcpSocket *socket)
{
// DO STUFF HERE ...
connect(socket, SIGNAL(disconnected()), this, SLOT(clientDisconnected()));
// FINALLY ADD TO ACTIVE-CLIENT LIST ...
}
Signal & Slot specific settings:
Since CustomServer is defined as a class member (m_server) within my main widget class (that handles GUI; called ServerGUI),
connectionRequest(qintptr) signal of m_server gets connected with handleConnectionRequest(qintptr) slot of ServerGUI instance.
But now my application is crashing immediately after startup, showing following message in debug window:
HEAP[qtapp.exe]: Invalid address specified to RtlValidateHeap( 000002204F430000, 0000006E0090F4C0 )
What may be the cause of this?
[01.10.2016 #2]
I adapted my code according to user2014561's answer.
for CustomServer class
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
WServer(QHostAddress, quint16, quint16, QObject* = nullptr);
~WServer();
void kickAll();
void kickClient(qintptr);
QHostAddress localAddress() const;
quint16 serverPort() const;
bool isReady() const;
bool alreadyConnected(qintptr) const;
bool clientLimitExhausted() const;
signals:
void clientConnected(qintptr);
void clientDisconnected(qintptr);
private slots:
void destroyedfunc(QObject*);
// JUST FOR TESTING PURPOSES
void waiterFinished();
private:
QList<ServerPeer*> m_connections;
quint16 m_maxAllowedClients;
bool m_readyState;
void incomingConnection(qintptr);
};
for kickAll():
void WServer::kickAll()
{
while (!m_connections.isEmpty())
{
ServerPeer *peer = m_connections.first();
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit())); // ### PROBLEM ENCOUNTERED HERE
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
}
}
for kickClient(qintptr):
void WServer::kickClient(qintptr client_id)
{
foreach (ServerPeer *peer, m_connections)
{
bool peerState;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, peerState), Q_ARG(qintptr, client_id));
if (peerState)
{
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
break;
}
}
}
for destroyedfunc(QObject*):
void CustomServer::destroyedfunc(QObject *obj)
{
ServerPeer *peer = static_cast<ServerPeer*>(obj);
m_connections.removeAll(peer);
}
for incomingConnection(qintptr):
void WServer::incomingConnection(qintptr handle)
{
ServerPeer *peer = new ServerPeer();
QThread *waiter = new QThread();
m_connections.append(peer); // add to list
peer->moveToThread(waiter);
// notify about client connect
connect(peer, SIGNAL(connected(qintptr)), this, SIGNAL(clientConnected(qintptr)));
// stop waiter thread by indirectly raising finished() signal
connect(peer, SIGNAL(finished()), waiter, SLOT(quit()));
// notify about client disconnect
connect(peer, SIGNAL(disconnected(qintptr)), this, SIGNAL(clientDisconnected(qintptr)));
// remove client from list
connect(peer, SIGNAL(destroyed(QObject*)), this, SLOT(destroyedfunc(QObject*)));
// notify about finished waiter thread; only for debug purposes
connect(waiter, SIGNAL(finished()), this, SLOT(waiterFinished()));
// remove waiter thread when finished
connect(waiter, SIGNAL(finished()), waiter, SLOT(deleteLater()));
QMetaObject::invokeMethod(peer, "start", Qt::QueuedConnection,
Q_ARG(qintptr, handle));
waiter->start();
}
for ServerPeer class
class ServerPeer : public QObject
{
Q_OBJECT
public:
ServerPeer(QObject* = nullptr);
~ServerPeer();
bool hasSocket(qintptr) const;
signals:
void connected(qintptr);
void disconnected(qintptr);
void finished();
public slots:
void start(qintptr);
void disconnect();
private slots :
void notifyConnect();
void notifyDisconnect();
private:
QTcpSocket *m_peer;
qintptr m_id;
};
for ServerPeer(QObject*):
ServerPeer::ServerPeer(QObject *parent) : QObject(parent), m_peer(nullptr)
{
}
for ~ServerPeer():
ServerPeer::~ServerPeer()
{
disconnect();
}
for start(qintptr):
void ServerPeer::start(qintptr handle)
{
qDebug() << "New waiter thread has been started.";
m_peer = new QTcpSocket(this);
if (!m_peer->setSocketDescriptor(handle))
{
this->deleteLater();
return;
}
if (true /*verification here*/)
{
connect(m_peer, SIGNAL(disconnected()), this, SLOT(notifyDisconnect()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(deleteLater()));
// manually do connected notification
QTimer::singleShot(0, this, SLOT(notifyConnect()));
}
else
{
this->deleteLater();
}
emit finished();
}
for disconnect():
void ServerPeer::disconnect()
{
if (m_peer != nullptr)
{
if (m_peer->state() != QAbstractSocket::SocketState::ClosingState
&& m_peer->state() != QAbstractSocket::SocketState::UnconnectedState)
m_peer->abort();
delete m_peer;
m_peer = nullptr;
}
}
for notifyConnect():
void ServerPeer::notifyConnect()
{
emit connected(m_peer);
}
for notifyDisconnect():
void ServerPeer::notifyDisconnect()
{
emit disconnected(m_peer);
}
for ServerGUI class
class ServerGUI : public QWidget
{
Q_OBJECT
public:
ServerGUI(QWidget* = nullptr);
private:
Ui::ServerWindow ui;
CustomServer *m_server;
private slots:
// For further handling, e.g. updating client view
void handleNewClient(qintptr);
void handleRemovedClient(qintptr);
}
for ServerGUI(QWidget*):
ServerGUI::ServerGUI(QWidget *parent) : QWidget(parent)
{
// initialize gui elements;
// GENERATED WITH ACCORDING *.ui FILE
ui.setupUi(this);
m_server = new WServer(QHostAddress::LocalHost, 1234, 2, this);
if (!m_server->isReady())
{
qDebug() << "Server could not start!";
delete m_server;
m_server = nullptr;
return;
}
connect(m_server, SIGNAL(clientConnected(qintptr)), this, SLOT(handleNewClient(qintptr)));
connect(m_server, SIGNAL(clientDisconnected(qintptr)), this, SLOT(handleRemovedClient(qintptr)));
}
And here my main function:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
ServerGUI w;
w.show();
return a.exec();
}
With given code following message pops up if I try to kick a (selected) client:
QMetaObject::invokeMethod: No such method ServerPeer::hasSocket(qintptr)
QObject::connect: Cannot connect (null)::destroyed() to QEventLoop::quit()
How to fix this?
If I understood correctly, you want to run each peer of your server on a separate thread, if so, then the following may help you:
create a subclass of QTcpServer
reimplement incomingConnection() method
create an instance (without parent) of QThread and of ServerPeer and start the thread
do the SIGNAL - SLOT connections to remove the peer from the list and delete the thread and the peers instances
add ServerPeer to your QList
once time started, do the credentials verification; if you reject them, abort the connection
Edit, considerations:
You haven't got the connected SIGNAL because when you set a socketDescriptor to the socket was already connected, so you can simple assume after setSocketDescriptor that the socket is connected and do what you want.
About the error when closing, it's happens because you not releasing properly the threads, see my edit how can you solve that.
Finally QTcpSocket must not be accessed by diferent threads, if you need to call ServerPeer from another thread use QMetaObject::invokeMethod with QueuedConnection or BlockingQueuedConnection and SIGNAL SLOT mechanism.
Edit 2:
Now the server and it's peers will be deleted on MainWindow::closeEvent and that way you can see the disconnected function being called. I guess the problem happens depending of the order that the classes will be deleted.
You can interact with the socket including send data through it, but I believe that will be painless use the Qt methods for cross-thread calls already mentioned. In my example you can easily write to a specific peer or to all peers.
customserver.h:
//Step 1
#include <QtCore>
#include <QtNetwork>
#include "serverpeer.h"
class CustomServer : public QTcpServer
{
Q_OBJECT
public:
explicit CustomServer(const QHostAddress &host, quint16 port, quint16 maxconnections, QObject *parent = nullptr);
~CustomServer();
void kickAll();
void kickClient(qintptr id);
void writeData(const QByteArray &data, qintptr id);
void writeData(const QByteArray &data);
QHostAddress localAddress();
quint16 serverPort();
bool isReady();
signals:
void clientConnected(qintptr);
void clientDisconnected(qintptr);
private slots:
void destroyedfunc(QObject *obj);
private:
void incomingConnection(qintptr handle);
QList<ServerPeer*> m_connections;
int m_maxAllowedClients;
};
customserver.cpp:
#include "customserver.h"
CustomServer::CustomServer(const QHostAddress &host, quint16 port, quint16 maxconnections, QObject *parent) :
m_maxAllowedClients(maxconnections), QTcpServer(parent)
{
listen(host, port);
}
CustomServer::~CustomServer()
{
kickAll();
}
//Step 2
void CustomServer::incomingConnection(qintptr handle)
{
// handle client limit
if (m_connections.size() >= m_maxAllowedClients)
{
qDebug() << "Can't allow new connection: client limit reached!";
QTcpSocket *socket = new QTcpSocket();
socket->setSocketDescriptor(handle);
socket->abort();
delete socket;
return;
}
//Step 3
ServerPeer *peer = new ServerPeer();
QThread *waiter = new QThread();
peer->moveToThread(waiter);
//Step 4
connect(peer, SIGNAL(connected(qintptr)), this, SIGNAL(clientConnected(qintptr)));
connect(peer, SIGNAL(disconnected(qintptr)), this, SIGNAL(clientDisconnected(qintptr)));
connect(peer, SIGNAL(destroyed()), waiter, SLOT(quit()));
connect(peer, SIGNAL(destroyed(QObject*)), this, SLOT(destroyedfunc(QObject*)));
connect(waiter, SIGNAL(finished()), waiter, SLOT(deleteLater()));
QMetaObject::invokeMethod(peer, "start", Qt::QueuedConnection, Q_ARG(qintptr, handle));
waiter->start();
//Step 5
m_connections.append(peer);
}
void CustomServer::kickAll()
{
while (!m_connections.isEmpty())
{
ServerPeer *peer = m_connections.first();
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
}
}
void CustomServer::kickClient(qintptr id)
{
foreach (ServerPeer *peer, m_connections)
{
ServerPeer::State hassocket;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ServerPeer::State, hassocket), Q_ARG(qintptr, id));
if (hassocket == ServerPeer::MyTRUE)
{
QEventLoop loop;
connect(peer->thread(), SIGNAL(destroyed()), &loop, SLOT(quit()));
QMetaObject::invokeMethod(peer, "deleteLater", Qt::QueuedConnection);
loop.exec();
break;
}
}
}
void CustomServer::writeData(const QByteArray &data)
{
foreach (ServerPeer *peer, m_connections)
QMetaObject::invokeMethod(peer, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
}
void CustomServer::writeData(const QByteArray &data, qintptr id)
{
foreach (ServerPeer *peer, m_connections)
{
ServerPeer::State hassocket;
QMetaObject::invokeMethod(peer, "hasSocket", Qt::BlockingQueuedConnection, Q_RETURN_ARG(ServerPeer::State, hassocket), Q_ARG(qintptr, id));
if (hassocket == ServerPeer::MyTRUE)
{
QMetaObject::invokeMethod(peer, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, data));
break;
}
}
}
QHostAddress CustomServer::localAddress()
{
return QTcpServer::serverAddress();
}
quint16 CustomServer::serverPort()
{
return QTcpServer::serverPort();
}
bool CustomServer::isReady()
{
return QTcpServer::isListening();
}
void CustomServer::destroyedfunc(QObject *obj)
{
ServerPeer *peer = static_cast<ServerPeer*>(obj);
m_connections.removeAll(peer);
}
serverpeer.h:
#include <QtCore>
#include <QtNetwork>
class ServerPeer : public QObject
{
Q_OBJECT
public:
explicit ServerPeer(QObject *parent = nullptr);
~ServerPeer();
enum State
{
MyTRUE,
MyFALSE
};
signals:
void connected(qintptr id);
void disconnected(qintptr id);
public slots:
ServerPeer::State hasSocket(qintptr id);
void start(qintptr handle);
void writeData(const QByteArray &data);
private slots:
void readyRead();
void notifyConnect();
void notifyDisconnect();
private:
QTcpSocket *m_peer;
qintptr m_id;
};
serverpeer.cpp:
#include "serverpeer.h"
ServerPeer::ServerPeer(QObject *parent) : QObject(parent), m_peer(nullptr)
{
}
ServerPeer::~ServerPeer()
{
if (m_peer)
m_peer->abort();
}
ServerPeer::State ServerPeer::hasSocket(qintptr id)
{
if (m_id == id)
return MyTRUE;
else
return MyFALSE;
}
void ServerPeer::start(qintptr handle)
{
m_peer = new QTcpSocket(this);
m_peer->setSocketDescriptor(handle);
//Step 6
if (true /*verification here*/)
{
m_id = handle;
QTimer::singleShot(0, this, SLOT(notifyConnect()));
connect(m_peer, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(notifyDisconnect()));
connect(m_peer, SIGNAL(disconnected()), this, SLOT(deleteLater()));
}
else
{
m_peer->abort();
this->deleteLater();
}
}
void ServerPeer::readyRead()
{
qDebug() << m_peer->readAll() << QThread::currentThread();
}
void ServerPeer::writeData(const QByteArray &data)
{
m_peer->write(data);
m_peer->flush();
}
void ServerPeer::notifyConnect()
{
emit connected(m_id);
}
void ServerPeer::notifyDisconnect()
{
emit disconnected(m_id);
}
mainwindow.cpp:
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qRegisterMetaType<qintptr>("qintptr");
m_server = new CustomServer(QHostAddress::LocalHost, 1024, 2, this);
if (!m_server->isReady())
{
qDebug() << "Server could not start!";
delete m_server;
m_server = nullptr;
return;
}
connect(m_server, SIGNAL(clientConnected(qintptr)), this, SLOT(handleNewClient(qintptr)));
connect(m_server, SIGNAL(clientDisconnected(qintptr)), this, SLOT(handleRemovedClient(qintptr)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::closeEvent(QCloseEvent *)
{
if (m_server)
{
delete m_server;
m_server = nullptr;
}
}
void MainWindow::handleNewClient(qintptr id)
{
qDebug() << __FUNCTION__ << id;
m_server->writeData(QString("Hello client id: %0\r\n").arg(id).toLatin1(), id);
m_server->writeData(QString("New client id: %0\r\n").arg(id).toLatin1());
}
void MainWindow::handleRemovedClient(qintptr id)
{
qDebug() << __FUNCTION__ << id;
}
I need some help on the usage of Qtimer.
I work with Qt 5.0.2 and here my problem :
I am trying to develop a Timer, and the interface is simple :
There is just 2 button : the button "Start", to launch the timer, and the "Pause" Button, and a QtimeEdit to display the time.
This screenshot shows how it looks like : http://img834.imageshack.us/img834/1046/5ks6.png
The problem is that the pause function doesn't work. I have read all the documentation about Qtimer here : http://harmattan-dev.nokia.com/docs/library/html/qt4/qtimer.html and here : qt.developpez.com/doc/5.0-snapshot/qtimer/ , but no result.
This is the source code I have : (I put only what is needed)
// Creation of the Buttons and the time area
void MainWindow::createBottom()
{
bottom = new QWidget();
play = new QPushButton("Launch",this);
pause = new QPushButton("Pause",this);
play->setDisabled(false);
pause->setDisabled(true);
timeEdit = new QTimeEdit(this);
timeEdit->setDisplayFormat("mm:ss");
layout->addWidget(play);
layout->addWidget(pause);
layout->addWidget(timeEdit );
bottom->setLayout(layout);
connect(play, SIGNAL(clicked()), this, SLOT(startSimulation()));
connect(pause, SIGNAL(clicked()), this, SLOT(pauseSimulation()));
}
// to resume the timer where is was stopped
void MainWindow::resumeSimulation()
{
timer->blockSignals( false );
pause->setText("Pause");
pause->disconnect(SIGNAL(clicked()));
connect(pause, SIGNAL(clicked()), this, SLOT(pauseSimulation()));
paused = false;
timer->start();
int timeOfPause = time->restart();
int timeTotal = timeOfPause + timeElapsed;
time->addMSecs(-timeTotal);
}
// to Start the timer
void MainWindow::pauseSimulation()
{
timer->blockSignals(true);
pause->setText("Resume");
timer->stop();
play->setDisabled(false);
//pause->setDisabled(true);
pause->disconnect(SIGNAL(clicked()));
connect(pause, SIGNAL(clicked()), this, SLOT(resumeSimulation()));
paused = true;
}
// to Start the timer from zero.
void MainWindow::startSimulation()
{
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this , SLOT(updateTime()));
timer->start(500);
play->setDisabled(true);
pause->setDisabled(false);
}
void MainWindow::updateTime()
{
if(time == NULL)
{
time = new QTime(0,0,0,0);
time->start();
}
//timeEdit->setTime(QTime::fromS(time->elapsed()));
//time = &(time->addMSecs(1000));
if(hasRestart)
{
time->restart();
time->addMSecs(-timeElapsed);
hasRestart = false;
}
else
{
timeElapsed =+ time->elapsed();
}
int seconds = 0;
int minutes = 0;
int hours = 0;
if(!paused)
{
seconds = (timeElapsed/1000)%60;
minutes = (timeElapsed/60000)%60;
hours = (timeElapsed/3600000)%24;
std::cout << "Test : " << hours << ":" << minutes << ":" << seconds << std::endl;
timeEdit->setTime(QTime(0,minutes,seconds,0));
timeEdit->update();
}
}
When I push the Start button, the timer starts well, but when I push "Pause" it only pause it on the graphic interface, but when I resume, it shows the present time as if it hadn't paused.
For instance :
I start.
I pause at 00:05. It blocks apparently the timer.
I wait for 10 seconds. I resume the timer, it shows 00:15 instead of 00:06
How could I fix that ?
Thank you !
EDIT : Thanks Kuba Ober, but could you explain me the code you posted please ?
How does the pause work ?
Below is a SSCCE, tested under both Qt 4.8 and 5.1.
//main.cpp
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include <QLabel>
#include <QElapsedTimer>
#include <QTime>
class Window : public QWidget {
Q_OBJECT
int m_timerId;
qint64 m_accumulator;
QLabel *m_label;
QElapsedTimer m_timer;
Q_SLOT void on_restart_clicked() {
m_accumulator = 0;
m_timer.restart();
if (m_timerId == -1) m_timerId = startTimer(50);
}
Q_SLOT void on_pause_clicked() {
if (m_timer.isValid()) {
m_accumulator += m_timer.elapsed();
m_timer.invalidate();
} else {
m_timer.restart();
m_timerId = startTimer(50);
}
}
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timerId) {
QWidget::timerEvent(ev);
return;
}
QTime t(0,0);
t = t.addMSecs(m_accumulator);
if (m_timer.isValid()) {
t = t.addMSecs(m_timer.elapsed());
} else {
killTimer(m_timerId);
m_timerId = -1;
}
m_label->setText(t.toString("h:m:ss.zzz"));
}
public:
explicit Window(QWidget *parent = 0, Qt::WindowFlags f = 0) : QWidget(parent, f), m_timerId(-1) {
QVBoxLayout * l = new QVBoxLayout(this);
QPushButton * restart = new QPushButton("Start");
QPushButton * pause = new QPushButton("Pause/Resume");
restart->setObjectName("restart");
pause->setObjectName("pause");
m_label = new QLabel("--");
l->addWidget(restart);
l->addWidget(pause);
l->addWidget(m_label);
QMetaObject::connectSlotsByName(this);
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "main.moc"
QTime totalTime, sinceStart;
void MainWindow::createBottom()
{
bottom = new QWidget();
play = new QPushButton("Launch",this);
pause = new QPushButton("Pause",this);
play->setDisabled(false);
pause->setDisabled(true);
timeEdit = new QTimeEdit(this);
timeEdit->setDisplayFormat("mm:ss");
layout->addWidget(play);
layout->addWidget(pause);
layout->addWidget(timeEdit);
bottom->setLayout(layout);
connect(play, SIGNAL(clicked()), this, SLOT(startSimulation()));
connect(pause, SIGNAL(clicked()), this, SLOT(pauseSimulation()));
connect(this, SIGNAL(timeChanged(QTime)), timeEdit, SLOT(setTime(QTime)));
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this , SLOT(updateTime()));
}
void MainWindow::updateTime() {
emit timeChanged(totalTime.addMSecs(sinceStart.elpased()));
}
void MainWindow::resumeSimulation() {
sinceStart.restart();
timer->start();
}
void MainWindow::pauseSimulation() {
timer->stop();
totalTime = totalTime.addMSecs(sinceStart.restart());
emit timeChanged(totalTime);
}
I made a Timer class for the start, stop, pause and resume
class MyTimer
{
public:
MyTimer();
QTime m_qtime;
int m_accumulator;
void start();
int stop();
void pause();
void resume();
};
MyTimer::MyTimer()
:m_accumulator(0), m_qtime(QTime())
{
}
void MyTimer::start()
{
m_qtime.start();
m_accumulator = 0;
}
int MyTimer::stop()
{
if(!m_qtime.isNull())
{
int l_elapsedTime = m_qtime.elapsed();
m_accumulator += l_elapsedTime;
}
m_qtime = QTime();
return m_accumulator;
}
void MyTimer::pause()
{
if(!m_qtime.isNull())
{
int l_elapsedTime = m_qtime.elapsed();
m_accumulator += l_elapsedTime;
}
}
void MyTimer::resume()
{
if(!m_qtime.isNull())
{
m_qtime.restart();
}
}
I'm writing a Qt program to get an image from site and insert in a QLabel. When I send my request my screen freezes and nothing more occurs.
Notice I'm new in Qt.
Based on my initial knowledge of Qt it's enough send a signal when download is finished.
...
MapReader::MapReader(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
ui.setupUi(this);
imageLabelMap = ui.imageMap;
getImageButton = ui.getImageButton;
networkManager = new QNetworkAccessManager(this);
setup();
}
MapReader::~MapReader()
{
}
void MapReader::setup()
{
QObject::connect(getImageButton, SIGNAL(clicked()), this, SLOT(triggerDownload()));
QObject::connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(finishedDownload(QNetworkReply*)));
}
void MapReader::setImage(QByteArray imageBytes)
{
QImage map;
...
}
void MapReader::triggerDownload()
{
QUrl url("http://images.tsn.ca/images/stories/2012/09/26/terrydunfield_2035-430x298.jpg");
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
}
void MapReader::finishedDownload(QNetworkReply* reply)
{
reply->deleteLater();
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(reply->error() != QNetworkReply::NoError)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setInformativeText("Error on downloading file: \n"+reply->errorString());
msgBox.exec();
return;
}
QVariant attribute = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (attribute.isValid())
{
QUrl url = attribute.toUrl();
qDebug() << "must go to:" << url;
return;
}
setImage(reply->readAll());
}
I think there is some code missing that might give us a clue. You have
QObject::connect(reply, SIGNAL(readyRead()), &loop, SLOT(quit()));
But I don't see where loop is defined? Sounds like you are running an additional event loop?
Regardless, you don't need that. This should be as simple as:
void MapReader::triggerDownload()
{
QUrl url("http://images.tsn.ca/images/stories/2012/09/26/terrydunfield_2035-430x298.jpg");
QNetworkReply* reply = networkManager->get(QNetworkRequest(url));
QObject::connect(reply, SIGNAL(finished()), this, SLOT(finishedDownload()));
}
void MapReader::finishedDownload()
{
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender()); // sender() allows us to see who triggered this slot - in this case the QNetworkReply
QVariant statusCodeV = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant redirectionTargetUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if(reply->error() != QNetworkReply::NoError)
{
QMessageBox msgBox;
msgBox.setWindowTitle("Error");
msgBox.setInformativeText("Error on downloading file: \n"+reply->errorString());
msgBox.exec();
return;
}
QVariant attribute = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (attribute.isValid())
{
QUrl url = attribute.toUrl();
qDebug() << "must go to:" << url;
return;
}
setImage(reply->readAll());
reply->deleteLater();
}
Make sure you have defined finishedDownload() as a slot in your header file