简体   繁体   English

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

[英]What's the point of returning an “Undefined Value” when re-defining “print()” function for QScriptEngine?

[Background] [背景]

The default print() function of QScriptEngine prints the result to the terminal of Qt Creator IDE for debugging purpose. QScriptEngine的默认print()函数将结果打印到Qt Creator IDE的终端以进行调试。 As a result, the output must be redirected to our texteditor if we are going to make a ECMA script interpreter ourselves. 因此,如果我们要自己制作ECMA脚本解释器,则必须将输出重定向到我们的texteditor。

This part of the document " Making Applications Scriptable " remains untouched since Qt 4.3. 自Qt 4.3以来,文档使应用程序可编写脚本 ”的这一部分保持不变。

Section " Redefining print() " : 重新定义打印() ”部分

Qt Script provides a built-in print() function that can be useful for simple debugging purposes. Qt Script提供了一个内置的print()函数,可用于简单的调试。 The built-in print() function writes to standard output. 内置的print()函数写入标准输出。 You can redefine the print() function (or add your own function, eg debug() or log()) that redirects the text to somewhere else. 您可以重新定义print()函数(或添加您自己的函数,例如debug()或log()),将文本重定向到其他位置。 The following code shows a custom print() that adds text to a QPlainTextEdit. 以下代码显示了一个自定义print(),它将文本添加到QPlainTextEdit。

So here is the suggested re-definition of print() : 所以这是建议的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();
 }

At first, I doubted the need of returning an "Undefined Value" by return engine->undefinedValue(); 起初,我怀疑是否需要通过return engine->undefinedValue();返回“Undefined Value” return engine->undefinedValue(); , and it looks like the role of the argument *engine is just to return this void value. ,看起来像参数*engine的作用就是返回这个空值。

So here is what I've done to change the function: 所以这就是我改变功能的方法:

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
}

which I think is more reasonable to me: returning an evaluated QScriptValue from script engine, and the value can later be translated to QString for output. 我认为对我来说更合理:从脚本引擎返回一个评估的QScriptValue ,稍后可以将该值转换为QString以进行输出。 This bypass the need of dynamic type cast, which could become messy especially for customized QObjects. 这绕过了动态类型转换的需要,特别是对于自定义QObject,它可能变得混乱。

For both kinds of print function, here is the exposition to the script engine: 对于这两种打印功能,这里是脚本引擎的说明:

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

Evaluation and output: 评估和输出:

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

[Compilable Code] [可编码]

(Qt version > 4 is needed) (需要Qt版本> 4)

test.pro test.pro

 QT += core gui widgets script TARGET = Test TEMPLATE = app SOURCES += main.cpp\\ console.cpp HEADERS += console.h 

main.cpp main.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 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 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() { } 


[Example] [例]

Input 1: 输入1:

print(123);

Output (Qt Document QtPrintFunction() ): 输出(Qt文档QtPrintFunction() ):

123
undefined

Output (My version myPrintFunction() ): 输出(我的版本myPrintFunction() ):

123

Input 2: 输入2:

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

Output (Qt Document QtPrintFunction() ): 输出(Qt文档QtPrintFunction() ):

0 0

1 1

2 2

undefined 未定义

Output ( myPrintFunction() ): 输出( myPrintFunction() ):

2 2


Input 3: 输入3:

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

Output (Qt Document QtPrintFunction() ): 输出(Qt文档QtPrintFunction() ):

Stack

Overflow 溢出

undefined 未定义

Output (My version myPrintFunction() ): 输出(我的版本myPrintFunction() ):

Overflow 溢出


[Question] [题]

Although myPrintFunction seems to work fine at first, it didn't work when there are more than two print called in a script, where only the last print will be executed. 尽管myPrintFunction看起来工作得很好,但是当脚本中调用了两个以上的print时,它才会起作用,其中只会执行最后一次print

It seems the returning of an "Undefined Value" is NECESSARY for the print function. 对于打印功能,似乎返回“未定义值”是必需的。 But why??? 但为什么???

It's not that it is NECESSARY to return undefinedValue() , but when you do, it's the same as not returning anything. 返回undefinedValue()并不是必须的,但是当你这样做时,它就像没有返回任何内容一样。 Or essentially, as if you declared the function as void print(...) , so to speak. 或者基本上,好像您将函数声明为void print(...) ,可以这么说。

That's what the QtPrintFunction does -- it returns "nothing". 这就是QtPrintFunction作用 - 它返回“无”。 But it does have a side effect of appending its argument to the internal data object, whenever you call it. 但是无论何时调用它,它都会产生将其参数附加到内部数据对象的副作用。 That's why you get all of the values passed to print in the output object. 这就是为什么你得到所有传递给值printoutput对象。

Now, when you call engine->evaluate() it returns the value of the last evaluated expression. 现在,当您调用engine->evaluate()它将返回上次计算的表达式的值。 So, with myPrintFunction you get the last value only. 因此,使用myPrintFunction您只能获得最后一个值。

So, if you were to enter the following: 所以,如果您要输入以下内容:

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

you will only get garbage back (pun intended), as this was the last evaluated expression. 你只会得到garbage回来(双关语),因为这是最后一次评估的表达式。

But, if you were to enter this: 但是,如果你输入这个:

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

you will get both values, as you expected. 你会得到两个值,如你所料。

Additionally, if you enter: 此外,如果您输入:

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

you will also get what you expected. 你也会得到你所期望的。

Hopefully this explains why you functions behave the way they are. 希望这能解释为什么你的功能按照它们的方式运行。

However, I don't think this is what you are trying to achieve. 但是,我不认为这是你想要实现的目标。 So... moving right along. 所以...向前移动。

One thing you can do is to define the myPrintFunction as follows: 可以做的一件事是定义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);
}

This will "work" the way you expected it to work. 这将按照您期望的方式“工作”。 The only problem is that you can't clear the value of result . 唯一的问题是你无法清除result的值。 If that works for you, then that will be that. 如果这对你有用,那就是那个。

There is a more betterer way to do this, which is probably to define a class, eg: 有一种更好的方法可以做到这一点,这可能是为了定义一个类,例如:

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

and pass an object of that class to fun.setData(engine->newQObject(trace)) and define your function as: 并将该类的对象传递给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();
}

Lastly, you would change your runScript function to something like: 最后,您可以将runScript函数更改为:

trace->clear();

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

Or there are probably other ways, but hopefully will help you get the ball rolling in the right direction. 或者可能有其他方式,但希望能帮助你让球在正确的方向上滚动。

The quick answer: you don't need to return undefinedValue. 快速回答:您不需要返回undefinedValue。 You can return anything you want. 你可以退回任何你想要的东西。 However, engine->evaluate() can only return a single value, which I think is the source of the confusion. 但是, engine->evaluate()只能返回一个值,我认为这是混乱的根源。

Take a look at the evaluation code: 看看评估代码:

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

This takes a script string and evaluates it, assigning the resulting value into result , where it is then appended to the QTextEdit control. 这将获取一个脚本字符串并对其进行评估,将结果值分配到result ,然后将其附加到QTextEdit控件。 The return value of evaluate() is going to be the last value of the script. evaluate()的返回值将是脚本的最后一个值。 For example: 例如:

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

result would contain 2, which would then get logged to the QTextEdit control. result将包含2,然后将记录到QTextEdit控件。

So, what's happening? 那么,发生了什么? Take this input for example: 以此输入为例:

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

When using the QtPrintFunction , "Stack" and "Overflow" are added to the output control as part of the QtPrintFunction implementation. 使用QtPrintFunction ,作为QtPrintFunction实现的一部分,“Stack”和“Overflow”被添加到output控件中。 When the evaluate() finishes, the last statement was print("Overflow") which returns undefined. evaluate()完成时,最后一个语句是print("Overflow") ,它返回undefined。 The evaluation code then takes that return value and adds it to the output , resulting in: 然后,评估代码获取该返回值并将其添加到output ,从而导致:

Stack
Overflow
undefined

When using the myPrintFunction , that implementation doesn't log anything to the output , but returns the value. 使用myPrintFunction ,该实现不会将任何内容记录到output ,而是返回值。 The result is that the evaluate() function only returns the last value ("Overflow"), resulting in this output: 结果是evaluate()函数只返回最后一个值(“溢出”),从而产生以下输出:

Overflow

Which is what you are seeing. 这是你所看到的。

Since you want to redirect output to your own custom text editor, you need to change this block of code: 由于您要将输出重定向到自己的自定义文本编辑器,因此需要更改此代码块:

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

Then in myPrintFunction you need to send the output to YourCustomEditor in a way that's similar to the QtPrintFunction . 然后在myPrintFunction您需要以与YourCustomEditor类似的方式将输出发送到QtPrintFunction Then you no longer need to output the result from evaluate() : 然后您不再需要从evaluate()输出结果:

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

I've worked with embedded interpreters many times before and things can get confusing quickly. 我以前曾多次使用嵌入式解释器,事情很快就会混乱。 Hopefully this is clear enough to help. 希望这很明显可以提供帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM