在为QScriptEngine重新定义"print()"函数时返回"未定义值"有什么意义?

Tay*_*510 7 javascript c++ qt interpreter read-eval-print-loop

[背景]

默认print()功能QScriptEngine将结果打印到Qt Creator IDE的终端以进行调试.因此,如果我们要自己制作ECMA脚本解释器,则必须将输出重定向到我们的texteditor.

自Qt 4.3以来,文档" 使应用程序可编写脚本 "的这一部分保持不变.

" 重新定义的print() ":

Qt Script提供了一个内置的print()函数,可用于简单的调试.内置的print()函数写入标准输出.您可以重新定义print()函数(或添加您自己的函数,例如debug()或log()),将文本重定向到其他位置.以下代码显示了一个自定义print(),它将文本添加到QPlainTextEdit.

所以这是建议的重新定义print():

QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine)
 {
     QString result;
     for (int i = 0; i < context->argumentCount(); ++i) {
         if (i > 0)
             result.append(" ");
         result.append(context->argument(i).toString());
     }

     QScriptValue calleeData = context->callee().data();
     QPlainTextEdit *edit = qobject_cast<QPlainTextEdit*>(calleeData.toQObject());
     edit->appendPlainText(result);

     return engine->undefinedValue();
 }
Run Code Online (Sandbox Code Playgroud)

起初,我怀疑是否需要返回"Undefined Value" return engine->undefinedValue();,看起来参数的作用*engine就是返回这个void值.

所以这就是我改变功能的方法:

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;

    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }

    /*
    QScriptValue calleeData = context->callee().data();
    QPlainTextEdit *edit = qobject_cast<QPlainTextEdit*>(calleeData.toQObject());
    edit->appendPlainText(result);

    return engine->undefinedValue();
    */
    return engine->toScriptValue(result); // ---> return the result directly
}
Run Code Online (Sandbox Code Playgroud)

我认为对我来说更合理:QScriptValue从脚本引擎返回一个评估值,稍后可以将该值转换QString为输出值.这绕过了动态类型转换的需要,特别是对于自定义QObject,它可能变得混乱.

对于这两种打印功能,这里是脚本引擎的说明:

 QScriptEngine *engine = new QScriptEngine(this); 
 QTextEdit *input = new QTextEdit(this);
 QTextEdit *output = new QTextEdit(this);

 // Use documented print function : 
 QScriptValue fun = engine->newFunction(QtPrintFunction);
 // Use my revised print function : 
 // QScriptValue fun = engine->newFunction(myPrintFunction);
 fun.setData(engine->newQObject(output));
 engine->globalObject().setProperty("print", fun);
Run Code Online (Sandbox Code Playgroud)

评估和输出:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
output->append(result.toString());
Run Code Online (Sandbox Code Playgroud)

[可编码]

(需要Qt版本> 4)

test.pro

QT += core gui widgets script
TARGET = Test
TEMPLATE = app


SOURCES += main.cpp\
        console.cpp

HEADERS  += console.h
Run Code Online (Sandbox Code Playgroud)

main.cpp中

#include <QApplication>
#include "console.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    Console w;
    w.show();

    return app.exec();
}
Run Code Online (Sandbox Code Playgroud)

console.h

#ifndef CONSOLE_H
#define CONSOLE_H

#include <QWidget>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QScriptEngine>

class Console : public QWidget
{
    Q_OBJECT

public:
    Console();
    ~Console();

public slots:
    void runScript();

private:
    QScriptEngine *engine;
    QVBoxLayout *layout;
    QPushButton *run;
    QTextEdit *input, *output;
};

QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine);
QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine);

#endif // CONSOLE_H
Run Code Online (Sandbox Code Playgroud)

console.cpp

#include "console.h"

Console::Console()
{
    engine = new QScriptEngine(this);
    layout = new QVBoxLayout(this);
    run = new QPushButton("Run",this);
    input = new QTextEdit(this);
    output = new QTextEdit(this);

    layout->addWidget(input);
    layout->addWidget(run);
    layout->addWidget(output);

    //QScriptValue fun = engine->newFunction(QtPrintFunction);
    QScriptValue fun = engine->newFunction(myPrintFunction);
    fun.setData(engine->newQObject(output));
    engine->globalObject().setProperty("print", fun);

    connect(run, SIGNAL(clicked()), this, SLOT(runScript()));
}

void Console::runScript()
{
    QString command = input->toPlainText();
    QScriptValue result = engine->evaluate(command);
    output->append(result.toString());
}

QScriptValue QtPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }

    QScriptValue calleeData = context->callee().data();
    QTextEdit *edit = qobject_cast<QTextEdit*>(calleeData.toQObject());
    edit->append(result);

    return engine->undefinedValue();
}

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }

    return engine->toScriptValue(result);
}

Console::~Console()
{

}
Run Code Online (Sandbox Code Playgroud)


[例]

输入1:

print(123);
Run Code Online (Sandbox Code Playgroud)

输出(Qt文件QtPrintFunction()):

123
undefined
Run Code Online (Sandbox Code Playgroud)

输出(我的版本myPrintFunction()):

123
Run Code Online (Sandbox Code Playgroud)

输入2:

for (i = 0; i < 3; i++)
    print(i);
Run Code Online (Sandbox Code Playgroud)

输出(Qt文件QtPrintFunction()):

0

1

2

未定义

输出(myPrintFunction()):

2


输入3:

print("Stack");
print("Overflow");
Run Code Online (Sandbox Code Playgroud)

输出(Qt文件QtPrintFunction()):

溢出

未定义

输出(我的版本myPrintFunction()):

溢出


[题]

虽然一开始myPrintFunction似乎工作得很好,但是当print脚本中有两个以上调用时,它不起作用,只执行最后一个脚本print.

对于打印功能,似乎返回"未定义值"是必需的.但为什么???

Inn*_*der 2

并不是说 return 是必须的undefinedValue(),但是当你这样做时,就和不返回任何东西是一样的。或者本质上,就好像您将函数声明为void print(...),可以这么说。

这就是它所做的QtPrintFunction——它返回“无”。但它确实有一个副作用,即无论何时调用它,都会将其参数附加到内部数据对象。print这就是为什么您可以获得传递给对象的所有值output

现在,当您调用时,它会返回最后engine->evaluate()计算的表达式的值。所以,你只能得到最后一个值。myPrintFunction

因此,如果您要输入以下内容:

print("Stack");
print("Overflow");
"garbage";
Run Code Online (Sandbox Code Playgroud)

你只会返回garbage(双关语),因为这是最后一个计算的表达式。

但是,如果您要输入以下内容:

print("Stack") + '\n' +
print("Overflow");
Run Code Online (Sandbox Code Playgroud)

正如您所期望的,您将获得这两个值。

此外,如果您输入:

result = "";
for (i = 0; i < 3; i++)
    result += print(i) + '\n';
Run Code Online (Sandbox Code Playgroud)

您也会得到您所期望的。

希望这能解释为什么你的函数会这样运行。

但是,我认为这不是您想要实现的目标。所以...继续前进。

您可以做的一件事是定义myPrintFunction如下:

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    static QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    result.append('\n');

    return engine->toScriptValue(result);
}
Run Code Online (Sandbox Code Playgroud)

这将按照您期望的方式“工作”。唯一的问题是你无法清除 的值result。如果这对你有用,那就这样了。

有一种更好的方法可以做到这一点,这可能是定义一个类,例如:

class QTrace: public QObject
{
    ...
    void clear();
    void append(const QString& value);
    const QString& get();
}
Run Code Online (Sandbox Code Playgroud)

并将该类的对象传递给fun.setData(engine->newQObject(trace))并将您的函数定义为:

QScriptValue myPrintFunction(QScriptContext *context, QScriptEngine *engine)
{
    QString result;
    for (int i = 0; i < context->argumentCount(); ++i) {
        if (i > 0)
            result.append(" ");
        result.append(context->argument(i).toString());
    }
    result.append('\n');

    QScriptValue calleeData = context->callee().data();
    QTrace *trace = qobject_cast<QTrace*>(calleeData.toQObject());
    trace->append(result);

    return engine->undefinedValue();
}
Run Code Online (Sandbox Code Playgroud)

最后,您可以将runScript函数更改为:

trace->clear();

QScriptValue result = engine->evaluate(command);
if(result.isError())
    output->append(result.toString());
else
    output->append(trace->get());
Run Code Online (Sandbox Code Playgroud)

或者可能还有其他方法,但希望能帮助您朝着正确的方向前进。