Qt 5.5 中Qt Script翻译 (八)

基于Value类型实现原型对象【不是new出来的】

在为基于值的类型(例如,QPointF)实现原型对象时,应用相同的一般技术;您用应该在实例之间共享的功能填充原型对象。然后通过调用QScriptEngine::setDefaultPrototype()将原型对象与类型相关联。这确保了当例如相关类型的值从槽返回到脚本时,脚本值的原型链接将被正确初始化。

当自定义类型的值存储到QVariants( 默认情况下qt脚本确实如此)中时,qscriptvalue_cast()使您能够安全地将脚本值转换为指向C++类型的指针。这使得很容易进行类型检查,而且,对于应该修改底层C++值的原型函数,可以修改脚本值中包含的实际值(而不是它的副本)。

Q_DECLARE_METATYPE(QPointF)
  Q_DECLARE_METATYPE(QPointF*)

  QScriptValue QPointF_prototype_x(QScriptContext *context, QScriptEngine *engine)
  {
    // Since the point is not to be modified, it's OK to cast to a value here
      QPointF point = qscriptvalue_cast<QPointF>(context->thisObject());
      return point.x();
  }

  QScriptValue QPointF_prototype_setX(QScriptContext *context, QScriptEngine *engine)
  {
      // Cast to a pointer to be able to modify the underlying C++ value
      QPointF *point = qscriptvalue_cast<QPointF*>(context->thisObject());
      if (!point)
          return context->throwError(QScriptContext::TypeError, "QPointF.prototype.setX: this object is not a QPointF");
      point->setX(context->argument(0).toNumber());
      return engine->undefinedValue();
  }

实现基于值的类型的构造函数

通过包装本地工厂函数,可以为基于值的类型实现构造函数。例如,以下函数实现QPoint的简单构造函数:

QScriptValue QPoint_ctor(QScriptContext *context, QScriptEngine *engine)
  {
      int x = context->argument(0).toInt32();
      int y = context->argument(1).toInt32();
      return engine->toScriptValue(QPoint(x, y));
  }

  ...

  engine.globalObject().setProperty("QPoint", engine.newFunction(QPoint_ctor));

在上面的代码中,我们简化了一些事情,例如,我们没有检查参数计数来决定使用哪个QPoint C++构造函数。在您自己的构造函数中,您必须自己完成这种类型的解析,即,通过检查传递给本机函数的参数数量,和/或通过检查参数的类型并将参数转换为所需的类型。如果检测到参数有问题,则可能希望通过抛出脚本异常来发出信号;参见QScriptContext::throwError()。

管理基于非QObject的对象

对于基于值的类型(例如QPoint),当Qt脚本对象被垃圾回收时,C++对象将被销毁,因此管理C++对象的内存不是问题。对于QObject,Qt脚本为管理底层C++对象的生存期提供了几种备选方案;请参阅控件QObject所有权部分。但是,对于不从QObject继承的多态类型,当您不能(或不)将该类型包装在QObject中时,您必须自己管理C++对象的生存期。

当Qt脚本对象包装C++对象时,通常是合理的,即当Qt脚本对象是垃圾收集时,C++对象被删除;这通常是对象可以由脚本构建的情况,相反,应用程序为脚本提供了预先生成的“environment”对象。一种使C++对象的生命周期遵循Qt脚本对象的生存期的方法是使用共享指针类(比如QSharedPointer)来保存指向对象的指针;当包含QSharedPointer的Qt脚本对象被垃圾收集时,如果没有其他引用对象,则将删除基础C++对象。

下面的代码片段显示了构造函数,该函数构造使用QSharedPointer存储的QXmlStreamReader对象:

 typedef QSharedPointer<QXmlStreamReader> XmlStreamReaderPointer;

  Q_DECLARE_METATYPE(XmlStreamReaderPointer)

  QScriptValue constructXmlStreamReader(QScriptContext *context, QScriptEngine *engine)
  {
      if (!context->isCalledAsConstructor())
          return context->throwError(QScriptContext::SyntaxError, "please use the 'new' operator");

      QIODevice *device = qobject_cast<QIODevice*>(context->argument(0).toQObject());
      if (!device)
          return context->throwError(QScriptContext::TypeError, "please supply a QIODevice as first argument");

      // Create the C++ object
      QXmlStreamReader *reader = new QXmlStreamReader(device);

      XmlStreamReaderPointer pointer(reader);

      // store the shared pointer in the script object that we are constructing
      return engine->newVariant(context->thisObject(), QVariant::fromValue(pointer));
  }

原型函数可以使用qscriptvalue_cast() 来将这个对象转换成适当的类型:

QScriptValue xmlStreamReader_atEnd(QScriptContext *context, QScriptEngine *)
  {
      XmlStreamReaderPointer reader = qscriptvalue_cast<XmlStreamReaderPointer>(context->thisObject());
      if (!reader)
          return context->throwError(QScriptContext::TypeError, "this object is not an XmlStreamReader");
      return reader->atEnd();
  }

原型和构造函数对象以通常的方式设置:

QScriptEngine engine;
      QScriptValue xmlStreamReaderProto = engine.newObject();
      xmlStreamReaderProto.setProperty("atEnd", engine.newFunction(xmlStreamReader_atEnd));

      QScriptValue xmlStreamReaderCtor = engine.newFunction(constructXmlStreamReader, xmlStreamReaderProto);
      engine.globalObject().setProperty("XmlStreamReader", xmlStreamReaderCtor);

脚本现在可以通过调用XmlStreamReader构造函数来构造QXmlStreamReader对象,并且当垃圾收集Qt Script对象(或者脚本引擎被销毁)时,QXmlStreamReader对象也被销毁。

用QScriptClass定义自定义脚本类

在某些情况下,QScriptEngine::newQObject()提供的动态QObject绑定或QScriptEngine::newFunction()提供的手动绑定都不够。例如,您可能想要对底层对象实现动态脚本代理;或者您可能希望实现一个类似于数组的类(即,对作为有效数组索引的属性和长度属性进行特殊处理)。在这种情况下,可以对QScriptClass进行子类实现所需的行为。

QScriptClass允许您通过虚拟get/set属性函数处理(类)脚本对象的所有属性访问。定制属性的迭代也通过QScriptClassPropertyIterator类得到支持;这意味着您可以通知要由for-in脚本语句和QScriptValueIterator报告的属性。

错误处理和调试设备

脚本中的语法错误将在评估脚本后立即报告;QScriptEngine::evaluate()将返回一个SyntaxError对象,您可以将该对象转换为字符串以获得错误的描述。

函数QScriptEngine::uncaughtExceptionBacktrace() 为您提供了上一个未捕获异常的人类可读回溯。为了在回溯中获得有用的文件名信息,在评估脚本时,应该将适当的文件名传递给QScriptEngine::evaluate()。

通常,在评估脚本时不会发生异常,但是在稍后实际执行脚本定义的函数时,会发生异常。对于C++信号处理程序来说,这是棘手的;考虑按钮的clicked()信号连接到脚本函数的情况,并且脚本函数在处理该信号时导致脚本异常。脚本异常传播到哪里?

解决方案是连接到QScriptEngine::signalHandlerException()信号;当信号处理器导致异常时,这将向您提供通知,以便您可以了解发生了什么和/或从中恢复。

在Qt 4.4中引入了QScriptEngineAgent类。QScriptEngineAgent提供了用于报告脚本引擎中的低级“事件”的接口,例如当一个新的函数调用或达到新的脚本语句时。通过子类化QScriptEngineAgent,可以通知您这些事件并执行一些操作(如果需要的话)。QScriptEngineAgent本身不提供任何特定于调试的功能(例如,设置断点),但它是这样做的工具的基础。

Qt脚本工具模块提供了一个可以嵌入到应用程序中的QT脚本调试器。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值