【问题标题】:What's the point of returning an "Undefined Value" when re-defining "print()" function for QScriptEngine?为 QScriptEngine 重新定义“print()”函数时返回“未定义值”有什么意义?
【发布时间】:2014-12-23 02:59:16
【问题描述】:

[背景]

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

自 Qt 4.3 以来,文档"Making Applications Scriptable" 的这一部分保持不变。

部分"Redefining 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();
 }

起初我怀疑return engine-&gt;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
}

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

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

 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);

评估和输出:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

[可编译代码]

(需要Qt版本> 4)

test.pro

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


SOURCES += main.cpp\
        console.cpp

HEADERS  += console.h

ma​​in.cpp

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

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

    Console w;
    w.show();

    return app.exec();
}

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

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()
{

}

[示例]

输入 1:

print(123);

输出(Qt 文档QtPrintFunction()):

123
undefined

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

123

输入 2:

for (i = 0; i < 3; i++)
    print(i);

输出(Qt 文档QtPrintFunction()):

0

1

2

未定义

输出 (myPrintFunction()):

2


输入 3:

print("Stack");
print("Overflow");

输出(Qt 文档QtPrintFunction()):

堆栈

溢出

未定义

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

溢出


[问题]

虽然myPrintFunction 一开始似乎可以正常工作,但是当脚本中调用了两个以上的print 时,它就不起作用了,只有最后一个print 会被执行。

It seems the returning of an "Undefined Value" is NECESSARY for the print function. But why???

【问题讨论】:

  • 作为括号中的旁注,QtScripts 已经“完成(甚至可能已经过时?)一段时间了。这是我在 2013 年毕尔巴鄂 QtCS 之后的想法。
  • 但不是 Javascript。我认为QScriptEngine 可以同时阅读它们,所以基本上我的目标是专注于 Javascirpt。或者您可能建议我删除qtscript 标签?
  • 我只是想写一下,您将来可能想放弃使用QtScript。 (顺便说一句,这是语法错误:QScriptEngin engine;
  • 它是在当时创建的,因为允许简单的脚本选项很有趣,例如我们在游戏开发者的游戏创建者 IDE 中使用,但后来 Qt 项目认为新的 v4 引擎是未来。
  • 如何“输出”使用 myPrintFunction 打印的内容?输出出现在哪里?请在您的问题中说明这一点

标签: javascript c++ qt interpreter read-eval-print-loop


【解决方案1】:

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

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

现在,当您调用 engine-&gt;evaluate() 时,它会返回 last 评估表达式的值。因此,使用 myPrintFunction 您只能获得 last 值。

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

print("Stack");
print("Overflow");
"garbage";

你只会得到garbage(双关语),因为这是最后评估的表达式。

但是,如果你输入这个:

print("Stack") + '\n' +
print("Overflow");

如您所料,您将获得这两个值。

另外,如果你输入:

result = "";
for (i = 0; i < 3; i++)
    result += print(i) + '\n';

你也会得到你所期望的。

希望这能解释为什么您的函数会如此行事。

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

可以做的一件事是定义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);
}

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

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

class QTrace: public QObject
{
    ...
    void clear();
    void append(const QString& value);
    const QString& get();
}

并将该类的一个对象传递给fun.setData(engine-&gt;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();
}

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

trace->clear();

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

或者可能还有其他方法,但希望能帮助你让球朝着正确的方向发展。

【讨论】:

  • 感谢您的详细回答!你的回答和@dbcb 一样好,虽然有点晚了。我希望我可以分割我的赏金,但系统不允许:(所以这是我的决定:我接受你的回答,但我将赏金给予较早回答的 dbcb,希望它足够公平。
【解决方案2】:

快速回答:您不需要返回 undefinedValue。您可以退回任何您想要的东西。但是,engine-&gt;evaluate() 只能返回一个值,我认为这是造成混乱的根源。

看一下评估代码:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

这需要一个脚本字符串并对其进行评估,将结果值分配给result,然后将其附加到QTextEdit 控件。 evaluate() 的返回值将是脚本的最后一个值。例如:

QString command = "var a=1, b=2; a; b;";
QScriptValue result = engine->evaluate(command);
output->append(result.toString());

result 将包含 2,然后将记录到 QTextEdit 控件。

那么,发生了什么?以这个输入为例:

print("Stack");
print("Overflow");

使用QtPrintFunction 时,“堆栈”和“溢出”将作为QtPrintFunction 实现的一部分添加到output 控件。当evaluate() 完成时,最后一条语句是print("Overflow"),它返回未定义。然后,评估代码获取该返回值并将其添加到 output,从而产生:

Stack
Overflow
undefined

使用myPrintFunction 时,该实现不会将任何内容记录到output,而是返回值。结果是evaluate()函数只返回最后一个值(“溢出”),导致这个输出:

Overflow

你看到的是什么。

由于您想将输出重定向到您自己的自定义文本编辑器,您需要更改此代码块:

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

 // Use my revised print function : 
 QScriptValue fun = engine->newFunction(myPrintFunction);
 fun.setData(engine->newQObject(output)); // pass your editor in
 engine->globalObject().setProperty("print", fun);

然后在myPrintFunction 中,您需要以类似于QtPrintFunction 的方式将输出发送到YourCustomEditor。那么你就不再需要从evaluate()输出结果了:

QString command = input->toPlainText();
QScriptValue result = engine->evaluate(command);
// output->append(result.toString()); <- not needed anymore

我之前曾多次使用嵌入式解释器,但事情很快就会变得混乱。希望这足够清楚以提供帮助。

【讨论】:

  • 真的很感激!很清楚,答案是......比我想象的要简单得多,哈哈,事情很快就会变得混乱。
  • 顺便说一句,这两句话:The return value of evaluate() is going to be the last value of the script.When using the myPrintFunction, that implementation doesn't log anything to the output, but returns the value 完全消除了我的困惑。再次感谢,你应得的赏金!
猜你喜欢
  • 2020-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-04
  • 2021-12-05
相关资源
最近更新 更多