I need to run a function with arguments (Installer::install) on a QProcess::finished signal. I've decided to use a lambda as slot, resulting in this:
private:
QProcess* unzip = new QProcess(this);`
void Installer::extract(QString source, QString destination) {
QStringList args;
args.append("x");
args.append(source);
args.append(QString("-o%1").arg(destination));
connect(unzip, &QProcess::finished, this, [=](int code, QProcess::ExitStatus status) {
if (code != 0 || status == QProcess::CrashExit) {
qDebug() << "Installer - Error >> Exit code:" << code;
qDebug() << "Installer - Error >> Status:" << status;
emit error();
}
else
install(destination + "/game.exe");
});
unzip->start("path\to\extractor\program.exe", args);
if (!unzip->waitForStarted()) {
qDebug() << "Installer - Error >> Extractor not started";
qDebug() << "Installer - Error Detail >>" << unzip->error();
emit error();
return;
}
qDebug() << "Sot Installer - Updates - Extractor >> Extracting...";
}
Installer::extract function is called at the end of every extraction. Supposing we have four archives to extract, Installer::extract will be called four times, with four different destination.
For the first archive, all works well. From the second one, the destination variable inside the lambda slot has the value of the first destination while it should have the second destination value. Instead, the destination variable outside the slot (e.g. in the last args.append) has the right value. Why is this happening and how can I solve this?
According to #Igor Tandetnik, connections were stacking up and so were the values of destination. Disconnecting all the connections of unzip before running Installer::extract again solved the problem.
Related
I'm sending a HTTP GET request to a remote server. Using various parameters I define the content I'm interested in. In particular I make sure that output=xml is in the query since it makes the server return a reply as XML.
I have the following connections between my class HttpRetriever and the respective QNetworkReply and QNetworkAccessManager (for the QNetworkRequest see slotStartRequest()):
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
The slots that are of interest here have the following declaration:
slotFinishRequest():
void HttpRetriever::slotFinishRequest()
{
LOG(INFO) << "Finishing HTTP GET request from URL \"" << this->url.toString() << "\"";
this->reply = Q_NULLPTR;
// Reset validity of reply from a previous request
this->validReply = false;
// Skip validation if it's disabled
if (!this->validateReply)
{
LOG(WARNING) << "Validation disabled. In order to enable it see the \"validate\" and \"validationMode\" in \"settings.ini\"";
this->validReply = true;
}
else
{
// Validate reply
this->validReply = validateReply();
}
if (!this->validReply)
{
return;
}
processReply(); // Parsing
this->processingRequest = false;
}
slotReadyReadRequest():
void HttpRetriever::slotReadyReadRequest()
{
LOG(INFO) << "Received data from \"" << this->url.toString() << "\"";
this->bufferReply = this->reply->readAll();
}
Inside the slotFinishRequest() I call the processReply():
void HttpRetriever::processReply()
{
LOG(INFO) << "Processing reply for request \"" << this->url.toString() << "\"";
LOG(DEBUG) << QString(this->bufferReply);
// Process the XML from the reply and extract necessary data
QXmlStreamReader reader;
reader.addData(this->bufferReply);
// Read the XML reply and extract required data
// TODO
while (!reader.atEnd())
{
LOG(DEBUG) << "Reading XML element";
reader.readNextStartElement();
QXmlStreamAttributes attributes = reader.attributes();
foreach (QXmlStreamAttribute attrib, attributes)
{
LOG(DEBUG) << attrib.name();
}
}
if (reader.hasError())
{
LOG(ERROR) << "Encountered error while parsing XML data:" << reader.errorString();
}
LOG(INFO) << "Sending data to data fusion handler";
// TODO
}
I trigger the HTTP get request through the following slot:
void HttpRetriever::slotStartRequest(quint32 id)
{
if (this->processingRequest)
{
this->reply->abort();
}
this->processingRequest = false;
// The first request also instantiates the manager. If the slot is called after the instance of HafasHttpRetriever
// is moved to a new thread this will ensure proper parenting
if (!this->manager)
{
this->manager = new QNetworkAccessManager(this);
}
quint32 requestId = generateRequestId(stopId);
if (!this->url.hasQuery())
{
LOG(WARNING) << "Attempting to start request without parameters";
}
// Part of the filters applied to the request to reduce the data received (for more see slotSetRequestParams())
QUrlQuery query(this->url.query());
query.addQueryItem("input", QString::number(requestId));
// TODO Add more filters; see documentation
this->url.setQuery(query);
LOG(INFO) << "Requesting data from \"" << this->url.toString() << "\" with request ID:" << requestId;
QNetworkRequest request(this->url);
this->reply = this->manager->get(request);
// Establish connections from/to the reply and the network access manager
connect(this->reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
SLOT(slotErrorRequest(QNetworkReply::NetworkError)));
connect(this->reply, &QNetworkReply::finished, this, &HttpRetriever::slotFinishRequest);
connect(this->manager, &QNetworkAccessManager::finished, this->reply, &QNetworkReply::deleteLater);
connect(this->reply, &QIODevice::readyRead, this, &HttpRetriever::slotReadyReadRequest);
}
As you can see so far I have laid down the foundation for the network communication between my class and the server and I am yet to start working on the parsing of the XML reply and extracting the information I need from it.
The problem is that I am getting (very, very often) either
Encountered error while parsing XML data: Start tag expected.
or
Encountered error while parsing XML data: Premature end of document
in my processReply() function. This happens every time I get a large reply (a couple of hundreds up to a couple of thousands of lines). It never happens when I get a small one (30-40 lines give or take).
So the issue is obviously somewhere in the amount of data I am receiving, the way it is put together by the QNetworkAccessManager (or whichever Qt component in all this buffers the received chunks of data) and/or perhaps the way I have setup the instances of the network-related components in my class. I also have to make an important note here namely that in my browser (latest Firefox with the HttpRequester add-on) I am always receiving the complete XML no matter how large it is. So this seems to be a problem exclusive to my application and has nothing to do with the network settings on my system.
Since #Marco didn't write the answer...
The problem was that I was rewriting my buffer all the time by assigning the result from QNetworkReply::readAll(). As suggested using QByteArray::append() solves the problem.
In order to prevent a possible issue from this solution namely that you keep appending with each and every next reply you get, QByteArray::clear() needs to be called at some point for example when the finished() signal is emitted. Of course one needs to first process its contents before flushing it down the drain.
QProcess process_sdcompare;
QString command_sdcompare;
QStringList args_sdcompare;
command_sdcompare = "diff";
args_sdcompare << "Filename" << "Filename";
process_sdcompare.start(command_sdcompare,args_sdcompare,QIODevice::ReadOnly);
process_sdcompare.waitForFinished();
QString StdOut_sdcompare = process_sdcompare.readAllStandardOutput(); //Reads standard output
QString StdError_sdcompare = process_sdcompare.readAllStandardError(); //Reads standard error
if(StdOut_sdcompare.isEmpty()) //the process output is checked here if empty it is a success
return 1;
I am running the above code. When I check for an error condition after comparing not similar text files, isEmpty() returns false.
How do I check if the QProcess execution occurred without errors?
I use QProcess::error() to query the last error (for quick debugging), however a "nicer" way to do it:
// capture any errors (do this before you run the process)
connect(&process_sdcompare, &QProcess::errorOccurred, this, &ThisClass::processError);
Then define slot:
ThisClass::processError(QProcess::ProcessError error)
{
qDebug() << "error enum val = " << error << endl;
}
update
Or with Lambda:
// No need to define a slot function...
connect(&process_sdcompare, &QProcess::errorOccurred, [=](QProcess::ProcessError error)
{
qDebug() << "error enum val = " << error << endl;
});
I think all the status and error functions are pretty useless to figure out if the process execution was actually successful. Example, trying to execute a program which does not exist (under windows):
error(): QProcess::ProcessError(FailedToStart)
exitStatus(): QProcess::ExitStatus(NormalExit)
exitCode(): 0
errorString(): "Process failed to start: Das System kann die angegebene Datei nicht finden."
If the process was actually executed successfully:
error(): QProcess::ProcessError(UnknownError)
exitStatus(): QProcess::ExitStatus(NormalExit)
exitCode(): 0
errorString(): "Unknown error"
So I would consider "Unknown error" not an indicator for success.
The solution I think could look like this:
QProcess *m_proc = new QProcess(this);
bool errorOccured = false;
QProcess::ProcessError procError;
QObject::connect(m_proc, &QProcess::errorOccurred, [&](QProcess::ProcessError error)
{
procError = error;
errorOccured = true;
});
m_proc->start(...);
When waitForFinished returns, it returns a bool that broadly indicates success. As Karsten recommended, you can then check for actual success or failure using QProcess::exitCode.
This is how you use QProcess
QProcess process_sdcompare;
QStringList args_sdcompare;
args_sdcompare << "Filename" << "Filename";
process_sdcompare.setProgram("diff");
process_sdcompare.setArguments(args_sdcompare);
process_sdcompare.start();
if (process_sdcompare.waitForStarted() && process_sdcompare.waitForFinished()) {
QString StdOut_sdcompare = process_sdcompare.readAllStandardOutput();
QString StdError_sdcompare = process_sdcompare.readAllStandardError();
if(StdOut_sdcompare.isEmpty())
return 1;
}
}
I have a QProcess where i would like to output the response in a label. First off, here is what i have tried:
QProcess *proc = new QProcess();
proc->setProcessChannelMode(QProcess::MergedChannels);
proc->start(cmdLineRequest.toUtf8().constData()); // cmdLineRequest is omitted
if (!proc->waitForFinished()) {
qDebug() << "Make failed:" << proc->errorString();
ui->topBarcode->setText(QString(proc->errorString()));
} else {
qDebug() << "Make output:" << proc->readAll();
ui->topBarcode->setText(QString(proc->readAll()) + "asdf");
}
proc->readAll() is a QByteArray and setText accepts a QString. From what i've read, i should be able to cast the QByteArray to a QString, howver it does not work. I have also tried to convert proc->readAll() with the QString class
->setText(QString::fromUtf8(proc->readAll())) // not working
->setText(QString::fromLatin1(proc->readAll())) // not working
->setText(QString::fromLocal8Bit(proc->readAll())) // not working
... etc ...
It seems weird, since i'm adding pictures to labels in nearly the same matter using setPixmap(QPixmap::fromImage(image))
Any help appreciated, thank you.
Update:
If i add a QMessageBox before the end of the method that the above block of code belongs to, i can see the text added to the label. However when i close the QMessageBox, the text dissapears. Am i giving an address position to the label with proc->readAll() or how come this behaviour? Thank you.
The problem here is that you're calling proc->readAll twice; the first for the qDebug output and then again for the string which you set on the label.
{
qDebug() << "Make output:" << proc->readAll();
ui->topBarcode->setText(QString(proc->readAll()) + "asdf");
}
I expect that as QProcess is a QIODevice, it's returning a buffered byte array. When you read it, it removes that from the buffer.
Therefore, create a temporary string and read the buffer once, before calling qDebug and setting the string to the label: -
{
QString output = proc->readAll();
qDebug() << "Make output:" << output;
ui->topBarcode->setText(output + "asdf");
}
You should listen to readyReadStandardOutput() signal and call readAll() when you receive signal.
or you can call
bool waitForReadyRead(int msecs = 30000)
before calling readAll().
Maybe this is stupid question, actually it's appeal, or Qt is just to complicated for me.
Here's the thing:
I'm used to java when writing client-server application, and it's very simple. I would like to do same things in C++ (I'm very familiar with C++ itself), and I choose to learn Qt. I tried to write some applications in qt, but with partial success.
First thing that bothers me is signals and slots. I know how to use them in GUI programming but it confuses me with networking. And there's problem with blocking. When I call BufferedReader's readLine() method in java it blocks until it receives line from socket connection. In Qt I must make sure that there is line available every time, and handle it when there isn't one.
And when I connect QSocket's error signal to some of my custom slots, the signal is emitted when server sends last line and closes the connection, and in client's slot/function that reads I never read that last line. That are some problems I faced so far.
Slots and checking if there is data available makes me confused when I had to implements even the simplest protocols.
Important part:
I tried to find good example on the internet, but problem is that all examples are to complicated an big. Is there anyone how can show me how to write simple client-server application. Server accepts only one client. Client sends textual line containing command. If command is "ADD" or "SUB", server sends "SUP" indicating that command is supported. Otherwise it sends "UNS" and closes the connection. If client receives "SUP" it sends to more lines containing numbers to be subtracted or added. Server responds with result and closes connection.
I know that C++ requires more coding, but in Java this would take only 5 minutes, so it shouldn't take to long to write it in C++ either.
I'm sure this example would be very valuable to anyone who wants to learn networking in Qt.
edit:
This is my try to make the application (described above):
here is the server part:
#ifndef TASK_H
#define TASK_H
#include <QObject>
#include <QTcpServer>
class Task : public QObject
{
Q_OBJECT
public:
Task(QObject *parent = 0) : QObject(parent) {}
public slots:
void run();
void on_newConnection();
void on_error(QAbstractSocket::SocketError);
signals:
void finished();
private:
QTcpServer server;
};
#endif // TASK_H
void Task::run()
{
connect(&server,SIGNAL(newConnection()),this,SLOT(on_newConnection()));
connect(&server,SIGNAL(acceptError(QAbstractSocket::SocketError)),this,SLOT(on_error(QAbstractSocket::SocketError)));
if(server.listen(QHostAddress::LocalHost, 9000)){
qDebug() << "listening";
}else{
qDebug() << "cannot listen";
qDebug() << server.errorString();
}
}
void Task::on_newConnection(){
std::cout << "handeling new connection...\n";
QTcpSocket* socket = server.nextPendingConnection();
QTextStream tstream(socket);
while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}
QString operation = tstream.readLine();
qDebug() << "dbg:" << operation;
if(operation != "ADD" && operation != "SUB"){
tstream << "UNS\n";
tstream.flush();
socket->disconnect();
return;
}
tstream << "SUP\n";
tstream.flush();
double op1,op2;
while(!socket->canReadLine()){
socket->waitForReadyRead((-1));
}
op1 = socket->readLine().trimmed().toDouble();
qDebug() << "op1:" << op1;
while(!socket->canReadLine()){
socket->waitForReadyRead(-1);
}
op2 = socket->readLine().trimmed().toDouble();
qDebug() << "op2:" << op2;
double r;
if(operation == "ADD"){
r = op1 + op2;
}else{
r = op1 - op2;
}
tstream << r << "\n";
tstream.flush();
qDebug() << "result is: " << r;
socket->disconnect();
}
void Task::on_error(QAbstractSocket::SocketError ){
qDebug() << "server error";
server.close();
}
This is client side (header is similar to server's so I wont post it):
void Task::run()
{
QTcpSocket socket;
std::string temp;
socket.connectToHost(QHostAddress::LocalHost,9000);
if(socket.waitForConnected(-1))
qDebug() << "connected";
else {
qDebug() << "cannot connect";
return;
}
QTextStream tstream(&socket);
QString op;
std::cout << "operation: ";
std::cin >> temp;
op = temp.c_str();
tstream << op << "\n";
tstream.flush();
qDebug() << "dbg:" << op << "\n";
while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString response = tstream.readLine();
qDebug() << "dbg:" << response;
if(response == "SUP"){
std::cout << "operand 1: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";
std::cout << "operand 2: ";
std::cin >> temp;
op = temp.c_str();
tstream << op + "\n";
tstream.flush();
while(!socket.canReadLine()){
socket.waitForReadyRead(-1);
}
QString result = tstream.readLine();
std::cout << qPrintable("result is: " + result);
}else if(response == "UNS"){
std::cout << "unsupported operatoion.";
}else{
std::cout << "unknown error.";
}
emit finished();
}
What I could do better?
What are some good practices in similar situations?
When using blocking (not signal/slot mechanism), what is the best way to handle event when other side closes the connection?
Can someone rewrite this to make it look more professional (I just what to see how it supposed to look like, because I think that my solution is far from perfect) ?
Can someone rewrite this using signals and slots?
Thanks you.
Sorry for my English, and probably stupidity :)
Networking with Qt is not that difficult.
Communication between two points is handled by a single class; in the case of TCP/IP, that would be the QTcpSocket class. Both the client and server will communicate with a QTcpSocket object.
The only difference with the server is that you start with a QTcpServer object and call listen() to await a connection...
QTcpServer* m_pTcpServer = new QTcpServer
//create the address that the server will listen on
QHostAddress addr(QHostAddress::LocalHost); // assuming local host (127.0.0.1)
// start listening
bool bListening = m_pServer->listen(addr, _PORT); //_PORT defined as whatever port you want to use
When the server receives a connection from a client QTcpSocket, it will notify you with a newConnection signal, so assuming you've made a connection to a socket in your own class to receive that signal, we can get the server QTcpSocket object to communicate with the client...
QTcpSocket* pServerSocket = m_pServer->nextPendingConnection();
The server will receive a QTcpSocket object for each connection made. The server socket can now be used to send data to a client socket, using the a write method...
pServerSocket->write("Hello!");
When a socket (either client or server) receives data, it emits the readyRead signal. So, assuming you have made a connection to the readyRead signal for the socket, a slot function can retrieve the data...
QString msg = pSocket->readAll();
The other code you'll need is to handle the connect, disconnect and error signals, which you should connect relevant slots for receiving these notifications.
Ensure you only send data when you know the connection has been made. Normally, I would have the server receive a connection and send a 'hello' message back to the client. Once the client receives the message, it knows it can send to the server.
When either side disconnects, the remaining side will receive the disconnect signal and can act appropriately.
As for the client, it will just have one QTcpSocket object and after calling connectToHost, you will either receive a connected signal if the connection was succesfully made, or the error signal.
Finally, you can use QLocalServer and QLocalSocket in the same way, if you're just trying to communicate between processes on the same machine.
See important edit below!
Hi all I'm having trouble figuring out why this segfault is happening. I'm using the Ogre and OIS library. Here is the code that causes it:
bool Troll::Application::keyPressed(const OIS::KeyEvent& event) {
//TODO: Segfault here!
Troll::State* state = mStateManager->peek();
state->key_pressed(event); //This causes the SEGFAULT!!!
return true;
};
And the key_pressed function:
void Troll::RootState::key_pressed(const OIS::KeyEvent& event) {
std::cout << "You got here" << std::endl; //this isnt printed!
std::cout << "Key Pressed: " << event.key << std::endl;
};
Because the segfault is happening on key_pressed but the first line of key_pressed isn't being executed, I can only guess that it is passing the const OIS::KeyEvent& that is causing it.
And the weird thing about this is I have three other functions that are almost identical (but for the mouse) which work perfectly.
bool Troll::Application::mouseMoved(const OIS::MouseEvent& event) {
mStateManager->peek()->mouse_moved(event);
return true;
};
void Troll::RootState::mouse_moved(const OIS::MouseEvent& event) {
std::cout << "Mouse Moved: rel x = " << event.state.X.rel << std::endl;
std::cout << " rel y = " << event.state.Y.rel << std::endl;
std::cout << " abs x = " << event.state.X.abs << std::endl;
std::cout << " abs y = " << event.state.Y.abs << std::endl;
};
I'm creating a basic state system so I can start writing applications for Ogre3D using the OIS library for input. I have an Application class which acts as an input listener for the mouse and keyboard. Here is how its setup...
void Troll::Application::setup_ois() {
//create a parameter list for holding the window handle data
OIS::ParamList pl;
size_t windowHnd = 0;
//we need the window handle to setup OIS
std::ostringstream windowHndStr;
mWindow->getCustomAttribute("WINDOW", &windowHnd);
windowHndStr << windowHnd;
//add the handle data into the parameter list
pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
//create the input system with the parameter list (containing handle data)
mInputManager = OIS::InputManager::createInputSystem(pl);
//true in createInputObject means we want buffered input
mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject( OIS::OISKeyboard, true ));
mMouse = static_cast<OIS::Mouse*>(mInputManager->createInputObject( OIS::OISMouse, true ));
//set this as an event handler
mKeyboard->setEventCallback(this);
mMouse->setEventCallback(this);
};
The application class relays the mouse moves, button pressed and key strokes to the Troll::State (the framework I'm making is called Troll) at the top of the state stack which is inside the Troll:: StateManager (which is merely a wrapper for an std::stack with memory allocation and startup() and shutdown() calls)
Sorry for any confusion the difference of the naming conventions causes for some reason I decided to use_underscores_for_some_reason and I haven't got round to changing it. Thanks in advance, ell. Hope you can solve my problem and please inform me if I haven't given enough detail.
EDIT:
After recently upgrading to Ubuntu Natty Narwhal I cannot get the debugger to work properly, it just crashes the computer. I use Code::Blocks and I don't have a clue how to use a debugger or compiler outside the IDE (sad I know, but I'll get round to learning someday). So sorry, I can't use a debugger.
EDIT:
In response to GMan's comment, even if I check for null, I still get segfaults
bool Troll::Application::keyPressed(const OIS::KeyEvent& event) {
//TODO: Segfault here!
Troll::State* state = mStateManager->peek();
if(state == 0) {
std::cout << "State is null!" << std::endl;
};
state->key_pressed(event);
return true;
};
Although I'm not sure thats the correct way to check for null? Also, other methods using peek() work correctly. Thanks again! :)
Important Edit:
It seems that it is in fact the peek function that is causing trouble, but only when called from the keyPressed function. I discovered this by adding a parameter to peek() so that it would print the address of the state object it return as well as the message. By setting the message parameter to the function from where the peek() function is called, I got these results.
Root state is: 0x8fdd470
Peeking state... 0x8fdd470 from: Application::frameRenderingQueued()
Peeking state... 0x8fdd470 from: Application::mouseMoved
Peeking state... 0x8fdd470 from: Application::frameRenderingQueued()
Peeking state... 0x8fdd470 from: Application::frameRenderingQueued()
Peeking state... 0x8fdd470 from: Application::frameRenderingQueued()
Peeking state... 0x936cf88 from: Application::keyPressed
Segmentation fault
Notice that when the keyPressed function calls the peek method, a different address is shown. I cannot see why a different address is being returned only when the keyPress function calls peek? Somebody please help me with this!
What happens when you check for mStateManager being NULL, and for NULL being returned from mStateManager->peek()?
bool Troll::Application::keyPressed(const OIS::KeyEvent& event) {
if (mStateManager == NULL) {
//! set breakpoint on next line
std::cout << "mStateManager is NULL, returning false" << std::endl;
return false;
}
std::cout << "about to call peek" << std::endl;
if (Troll::State* state = mStateManager->peek())
{
std::cout << "about to call key_pressed" << std::endl;
state->key_pressed(event); //Does this still cause a SEGFAULT?
std::cout << "back from key_pressed" << std::endl;
return true;
}
std::cout << "mStateManager->peek() returned NULL, returning false" << std::endl;
return false;
};
EDIT: I edited the code to print each branch, how it was traced through.