QJSEngine: print to console - qt

I'm moving from QScriptEngine (which is deprecated) to QJSEngine, and I see that I'm unable to use print:
QJSEngine engine;
QJSValue val = engine.evaluate(
"print('123');"
);
if (val.isError()){
qDebug() << "error: " << val.toString();
}
qDebug() << "val: " << val.toVariant();
The output is:
error: "ReferenceError: print is not defined"
In QScriptEngine it works.
Then, what is the way to print something to console in QJSEngine? Can't find anything in docs. I tried to use console.log, but console is not defined as well.

From Qt 5.6 on, the solution is even easier: one can install Javascript extensions. One such extension is the console which provides a print function:
QJSEngine myEngine;
myEngine.installExtensions(QJSEngine::ConsoleExtension);
myEngine.eval("print(1 + 2)");

The print function is not implemented in QJSEngine. You will have to implement it yourself. Luckily you can write QObjects and make them available in your script. (See section "QObject Integration" at http://doc.qt.io/qt-5/qjsengine.html)
Here's how i've done it:
Create a class JSConsole inheriting from QObject:
Your own console class
jsconsole.h
#ifndef JSCONSOLE_H
#define JSCONSOLE_H
#include <QObject>
class JSConsole : public QObject
{
Q_OBJECT
public:
explicit JSConsole(QObject *parent = 0);
signals:
public slots:
void log(QString msg);
};
#endif // JSCONSOLE_H
jsconsole.cpp
#include "jsconsole.h"
#include <QDebug>
JSConsole::JSConsole(QObject *parent) :
QObject(parent)
{
}
void JSConsole::log(QString msg)
{
qDebug() << "jsConsole: "<< msg;
}
Using it
Now you create a proxy object in the js engine by using QJSEngine.newQObject.
After that you add it to your global object and use it.
QJSEngine engine;
JSConsole console;
QJSValue consoleObj = engine.newQObject(&console);
engine.globalObject().setProperty("console", consoleObj);
QJSValue result = engine.evaluate("console.log('test');");
Error logging
I've searched a long time for errors in my c++ code, when i just made a spelling error in my js file. The following snippet would have helped avoid that.
if (result.isError())
{
qDebug() << "result: " << result.property("lineNumber").toInt() << ":" << result.toString();
}
PS: First post after years of lurking. I've read tips on writing great answers but if I've made some mistakes/bad things please tell me.
May the code be with you

Related

QProcess with 'cmd' command does not result in command-line window

I am porting code from MinGW to MSVC2013/MSVC2015 and found a problem.
QProcess process;
QString program = "cmd.exe";
QStringList arguments = QStringList() << "/K" << "python.exe";
process.startDetached(program, arguments);
When I use MinGW, this code results in command-line window. But when I use MSVC2013 or MSVC2015, the same code results in cmd-process running in background without any windows. Are there any ways to make command-line window appear?
The problem was connected with msvc2015, not with Qt5.8.0. There is the way to escape it. The idea is to use CREATE_NEW_CONSOLE flag.
#include <QProcess>
#include <QString>
#include <QStringList>
#include "Windows.h"
class QDetachableProcess
: public QProcess {
public:
QDetachableProcess(QObject *parent = 0)
: QProcess(parent) {
}
void detach() {
waitForStarted();
setProcessState(QProcess::NotRunning);
}
};
int main(int argc, char *argv[]) {
QDetachableProcess process;
QString program = "cmd.exe";
QStringList arguments = QStringList() << "/K" << "python.exe";
process.setCreateProcessArgumentsModifier(
[](QProcess::CreateProcessArguments *args) {
args->flags |= CREATE_NEW_CONSOLE;
args->startupInfo->dwFlags &=~ STARTF_USESTDHANDLES;
});
process.start(program, arguments);
process.detach();
return 0;
}
You don't need to use QProcess for this. It's much simpler to just use std::system:
#include <cstdlib>
// then when you want to open a
// detached command prompt:
std::system("cmd");
You can also do things like:
std::system("cd some/path && cmd");
It's standard C++ (from C) so std::system(...) itself will work on any platform, only thing you need to set per platform is the shell command.

Read output of multiple write in QProcess one by one

Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.

How to get human-readable event type from QEvent?

I want to debug event handling code and would like to convert QEvent::Type enum's value to a human-readable string. QEvent has a Q_GADGET macro, so presumably there's a way of pulling that off?
Recent versions of Qt do the right thing when outputting events to the debug stream, so the below isn't neccessary. If you get an error similar to warning C4273: 'operator <<' : inconsistent dll linkage, it means that your version of Qt already supports this without need for the code below.
The Q_GADGET macro adds a QMetaObject staticMetaObject member to the class. The static metaobject's definition is generated by moc, and it - in the case of QEvent - contains the enumeration information.
Below is an example of how to leverage that to give a more reasonable QDebug output of events.
#include <QEvent>
#include <QMetaEnum>
#include <QDebug>
/// Gives human-readable event type information.
QDebug operator<<(QDebug str, const QEvent * ev) {
static int eventEnumIndex = QEvent::staticMetaObject
.indexOfEnumerator("Type");
str << "QEvent";
if (ev) {
QString name = QEvent::staticMetaObject
.enumerator(eventEnumIndex).valueToKey(ev->type());
if (!name.isEmpty()) str << name; else str << ev->type();
} else {
str << (void*)ev;
}
return str.maybeSpace();
}
Use example:
void MyObject::event(QEvent* ev) {
qDebug() << "handling an event" << ev;
}
Q_GADGET and Q_ENUM can be combined to get the following template:
template<typename EnumType>
QString ToString(const EnumType& enumValue)
{
const char* enumName = qt_getEnumName(enumValue);
const QMetaObject* metaObject = qt_getEnumMetaObject(enumValue);
if (metaObject)
{
const int enumIndex = metaObject->indexOfEnumerator(enumName);
return QString("%1::%2::%3").arg(metaObject->className(), enumName, metaObject->enumerator(enumIndex).valueToKey(enumValue));
}
return QString("%1::%2").arg(enumName).arg(static_cast<int>(enumValue));
}
Example:
void MyObject::event(QEvent* ev)
{
qDebug() << ToString(ev->type());
}

QNetworkManager uploading file to FTP crash

I am trying to upload a simple test text file to a FTP server. In order to achieve this I am using QNetworkAccessManager, since QFtp has been deprecated in Qt 5.1.
I created a test.txt file in the programs directory and using QFile I am opening it as QIODevice::ReadWrite | QIODevice::Text.
The problem is when I set the connection and tell the QNetworkAccessManager to upload a file the program crashes ("FTPConnectionTest does not respond"). It happens both when I am trying to use an external FTP server or a local one created with FileZilla.
I connected all signals emitted by the reply (functions: uploadFinish, uploadProgress, uploadError) however no feedback is beeing captured.
Question: Is this problem lying on the side of FTP server or am I doing something wrong in my code?
Code snipped below:
Main.cpp
#include <QCoreApplication>
#include <ftp.h>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Ftp ftp;
return a.exec();
}
ftp.cpp
#include "ftp.h"
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <QFile>
#include <QUrl>
#include <QDebug>
Ftp::Ftp()
{
QFile file("test.txt");
if (file.open(QIODevice::ReadWrite | QIODevice::Text)) {
url = QUrl("ftp://127.0.0.1/test.txt");
url.setUserName("user");
url.setPassword("password");
qDebug() << "URL set" << url;
QNetworkAccessManager* nam = new QNetworkAccessManager();
qDebug() << "nam set";
QNetworkReply *rep = nam->put(QNetworkRequest(url), &file);
qDebug() << "after rep";
connect(rep, SIGNAL(finished()), this, SLOT(uploadFinish()));
connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(uploadError(QNetworkReply::NetworkError)));
connect(rep, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(uploadProgress(qint64,qint64)));
}
else qDebug() << "failed to open";
}
void Ftp::uploadFinish()
{
qDebug() << "finished uploading file";
}
void Ftp::uploadProgress(qint64 a, qint64 b)
{
qDebug() << a << "/" << b;
}
void Ftp::uploadError(QNetworkReply::NetworkError state)
{
qDebug() << "State" << state;
}
See the QNetworkAccessManager::put documentation:
data must be opened for reading when this function is called and must remain valid until the finished() signal is emitted for this reply.
Your file object falls out of scope when the constructor finishes execution, so QNetworkAccessManager probably tries to read from object that is already deleted. You need to make file a class member variable or create it using QFile* file = new QFile().

Qt SIGNAL and SLOT in hierarcy, not threads (yet)

OK, I'm a total beginner but I'm missing something here. Been all over the Qt documentation/examples and everything I can dig up through Google. All similar information is slightly different in context...
I'm just starting out with Qt SIGNALS and SLOTS, I'm successful with gui examples and within a class. Now I want to connect a SIGNAL in a child class with a SLOT in a sibling class with the Connect defined in the parent main. Ultimately my aim is to receive iamges in a class handling a QTcpSocket and emit the data as char* to be handled (saved or displayed) by another class.
For now I've just created the most basic version of the arrangement in a Console app as a learning exercise. I've got a sender class...
sender.h
#ifndef SENDER_H
#define SENDER_H
#include <QObject>
class sender : public QObject
{
Q_OBJECT
public:
sender(QObject *parent = 0);
~sender();
signals:
void output(int data);
public slots:
void test(int data);
private:
};
#endif // SENDER_H
sender.cpp
#include <iostream>
#include "sender.h"
sender::sender(QObject *parent)
: QObject(parent)
{
std::cout << "Created sender" << std::endl;
int stuff = 47;
std::cout << "stuff = " << stuff << std::endl;
connect(this, SIGNAL(output(int)), this, SLOT(test(int)));
emit output(stuff);
}
void sender::test(int data)
{
std::cout << "Got to test, data = " << data << std::endl;
}
sender::~sender()
{
std::cout << "Destroying sender" << std::endl;
}
...and a receiver class...
receiver.h
#ifndef RECEIVER_H
#define RECEIVER_H
#include <QObject>
class receiver : public QObject
{
Q_OBJECT
public:
receiver(QObject *parent = 0);
~receiver();
public slots:
void input (int data);
private:
};
#endif // RECEIVER_H
receiver.cpp
#include <iostream>
#include "receiver.h"
receiver::receiver(QObject *parent)
: QObject(parent)
{
std::cout << "Created receiver" << std::endl;
}
void receiver::input(int data)
{
std::cout << "Got data as = " << data << std::endl;
}
receiver::~receiver()
{
std::cout << "Destroying receiver" << std::endl;
}
My main looks like this...
main.cpp
#include <QtCore/QCoreApplication>
#include <iostream>
#include "sender.h"
#include "receiver.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
receiver myReceiver;
sender mySender;
if (QObject::connect(&mySender, SIGNAL(output(int)),
&myReceiver, SLOT(input(int))))
{
std::cout << "Got here so connect returned true" << std::endl;
}
return a.exec();
}
I've added the cout outputs and the sender::test function to try and figure out what's happening.
For me, this compiles cleanly and runs without any warnings or errors but while the sender::test SLOT gets called the receiver::input SLOT doesn't. The test on the connect in main returns true and neither sender or receiver are destroyed prematurely. Console output is...
Created receiver
Created sender
stuff = 47
Got to test, data = 47
Got here so connect returned true
So the SIGNAL is emitted, the SIGNAL and SLOT parameters match, I've got the Q_OBJECT macros in both sender.h and receiver.h, and both inherit from and #include QObject.
What's wrong???
P.S. I'm running 4.8.3 and IDE is VS2010 with Qt plugin.
The answer is quite simple: you are sending signal before you've connected it to the receiver and after you've connected it to itself. So your output is absolutely correct.
Do all emits only after everything has been connected. Here the instantiation of sender happens before sender and receiver are connected, and that's where the emit was done.

Resources