PDA

View Full Version : QtScript Bindings Generator ScriptShell Event Binding



JohannesMunk
16th March 2010, 14:44
Hi there!

How can I call the native event handler when using the scriptshell binding of the qt script binding generator?

I'm using the splendid qt script binding generator lab-project (http://labs.trolltech.com/page/Projects/QtScript/Generator) to make all qt classes fully scriptable. Through the scriptshells it allows binding a script function to otherwise inaccessible protected event handlers. In non script you would derive from the class to access these..



scriptcode:

var a = new QWidget();
a.setGeometry(50,50,200,100);
a.show();
a.event = function (event) {
print(event.type().toString());
};



This works perfectly, but it hides the native implementation. If you look into the generated scriptshell files of the generator:



from: ..\com_trolltech_qt_gui\qtscriptshell_QWidget.cpp

bool QtScriptShell_QWidget::event(QEvent* arg__1)
{
QScriptValue _q_function = __qtscript_self.property("event");
if (!_q_function.isFunction() || QTSCRIPT_IS_GENERATED_FUNCTION(_q_function)
|| (__qtscript_self.propertyFlags("event") & QScriptValue::QObjectMember)) {
return QWidget::event(arg__1);
} else {
QScriptEngine *_q_engine = __qtscript_self.engine();
return qscriptvalue_cast<bool >(_q_function.call(__qtscript_self,
QScriptValueList()
<< qScriptValueFromValue(_q_engine, arg__1)));
}
}
So either the native or the script event handler is called. Now my question is, if I missed an obvious way to call the native event handler from script.

If thats not possible I'm thinking of changing the scriptshell-generator-code (..\generator\shellimplgenerator.cpp ) to call the native handler for all events that have not been accepted inside the script-method. That's easy enough, but I want to know, if there is an out-of-the-box way.

Thx!

Johannes

JohannesMunk
10th April 2010, 13:09
Hey there!

Here is what I came up with:

I changed the scriptshellgenerator code so that the generated code looks like this:



bool QtScriptShell_QWidget::event(QEvent* arg__1)
{
QScriptValue _q_function = __qtscript_self.property("event");
if (!_q_function.isFunction() || QTSCRIPT_IS_GENERATED_FUNCTION(_q_function)
|| (__qtscript_self.propertyFlags("event") & QScriptValue::QObjectMember)) {
return QWidget::event(arg__1);
} else {
QScriptEngine *_q_engine = __qtscript_self.engine();
// callmode. first bit: 1:callcpp|0:don't call cpp
// 2nd-bit: 1:returncpp|0:returnscript
int _callmode = 3;
QScriptValue _q_callmode = __qtscript_self.property("event_callmode");
if (_q_callmode.isValid()) _callmode = _q_callmode.toInt32();
if ((_callmode & 1) > 0) {
bool resultcpp = QWidget::event(arg__1);
bool resultscript = qscriptvalue_cast<bool >(_q_function.call(__qtscript_self,
QScriptValueList()
<< qScriptValueFromValue(_q_engine, arg__1)));
if ((_callmode & 2) > 0) {
return resultcpp;
} else {
return resultscript;
}
} else {
return qscriptvalue_cast<bool >(_q_function.call(__qtscript_self,
QScriptValueList()
<< qScriptValueFromValue(_q_engine, arg__1)));
}
}
}

instead of the above.. So the default behaviour is to call both. And to return the value of the c++ implementation. If you want another behaviour, you need to specifiy a value for <eventname>_callmode.

JohannesMunk
10th April 2010, 13:10
This is the ShellImplGenerator::write method in generator/shellimplgenerator.cpp which you need to change in the bindings generator.


void ShellImplGenerator::write(QTextStream &s, const AbstractMetaClass *meta_class)
{
if (FileOut::license)
writeQtScriptQtBindingsLicense(s);

QString packName = meta_class->package().replace(".", "_");

priGenerator->addSource(packName, fileNameForClass(meta_class));

s << "#include \"qtscriptshell_" << meta_class->name() << ".h\"" << endl << endl;

if (!meta_class->generateShellClass())
return;

s << "#include <QtScript/QScriptEngine>" << endl;

IncludeList list = meta_class->typeEntry()->extraIncludes();
qSort(list.begin(), list.end(), include_less_than);
foreach (const Include &inc, list) {
if (inc.type == Include::TargetLangImport)
continue;

s << "#include ";
if (inc.type == Include::LocalPath)
s << "\"";
else
s << "<";

s << inc.name;

if (inc.type == Include::LocalPath)
s << "\"";
else
s << ">";

s << endl;
}
s << endl;

writeHelperCode(s, meta_class);

// find constructors
AbstractMetaFunctionList ctors;
ctors = meta_class->queryFunctions(AbstractMetaClass::Constructors
| AbstractMetaClass::WasVisible
| AbstractMetaClass::NotRemovedFromTargetLang);
// find member functions
AbstractMetaFunctionList functions = meta_class->queryFunctions(
AbstractMetaClass:: VirtualFunctions | AbstractMetaClass::WasVisible
| AbstractMetaClass::NotRemovedFromTargetLang
);

// write metatype declarations
{
QSet<QString> registeredTypeNames = m_qmetatype_declared_typenames;
declareFunctionMetaTypes(s, functions, registeredTypeNames);
s << endl;
}

// write constructors
foreach (const AbstractMetaFunction *ctor, ctors) {
s << "QtScriptShell_" << meta_class->name() << "::";
writeFunctionSignature(s, ctor, 0, QString(), Option(OriginalName | ShowStatic));
s << endl;
s << " : " << meta_class->qualifiedCppName() << "(";
AbstractMetaArgumentList args = ctor->arguments();
for (int i = 0; i < args.size(); ++i) {
if (i > 0)
s << ", ";
s << args.at(i)->argumentName();
}
s << ")" << " {}" << endl << endl;
}

// write destructor
s << "QtScriptShell_" << meta_class->name() << "::"
<< "~QtScriptShell_" << meta_class->name() << "()";
if (!meta_class->destructorException().isEmpty())
s << " " << meta_class->destructorException();
s << " {}" << endl << endl;

// write member functions
for (int i = 0; i < functions.size(); ++i) {
AbstractMetaFunction *fun = functions.at(i);
writeFunctionSignature(s, fun, meta_class, QString(),
Option(OriginalName | ShowStatic | UnderscoreSpaces),
"QtScriptShell_");
s << endl << "{" << endl;
QString scriptFunctionName = fun->name();
{
QPropertySpec *read = 0;
for (const AbstractMetaClass *cls = meta_class; !read && cls; cls = cls->baseClass())
read = cls->propertySpecForRead(fun->name());
if (read && (read->name() == fun->name())) {
// use different name to avoid infinite recursion
// ### not sure if this is the best solution though...
scriptFunctionName.prepend("_qs_");
}
}

s << " QScriptValue _q_function = __qtscript_self.property(\""
<< scriptFunctionName << "\");" << endl;
s << " if (!_q_function.isFunction() || QTSCRIPT_IS_GENERATED_FUNCTION(_q_function)" << endl
<< " || (__qtscript_self.propertyFlags(\"" << scriptFunctionName << "\") & QScriptValue::QObjectMember)) {" << endl;

AbstractMetaArgumentList args = fun->arguments();
s << " ";
if (fun->isAbstract()) {
s << "qFatal(\"" << meta_class->name() << "::" << fun->name()
<< "() is abstract!\");" << endl;
} else {
// call the C++ implementation
if (fun->type()) {
s << "return ";
}
s << meta_class->qualifiedCppName() << "::" << fun->originalName() << "(";
for (int i = 0; i < args.size(); ++i) {
if (i > 0)
s << ", ";
s << args.at(i)->argumentName();
}
s << ");" << endl;
}

s << " } else {" << endl;

// call the script function
if (args.size() > 0)
s << " QScriptEngine *_q_engine = __qtscript_self.engine();" << endl;

// NTS Addon
if (!fun->isAbstract()) {
s << " // callmode. first bit: 1:callcpp|0:don't call cpp" << endl;
s << " // 2nd-bit: 1:returncpp|0:returnscript" << endl;
s << " int _callmode = 3;" << endl;
s << " QScriptValue _q_callmode = __qtscript_self.property(\""
<< scriptFunctionName << "_callmode\");" << endl;
s << " if (_q_callmode.isValid()) _callmode = _q_callmode.toInt32();" << endl;
s << " if ((_callmode & 1) > 0) {" << endl;

// First call the c++ implementation
s << " ";
if (fun->type()) {
writeTypeInfo(s, fun->type());
s << "resultcpp = ";
}
s << meta_class->qualifiedCppName() << "::" << fun->originalName() << "(";
for (int i = 0; i < args.size(); ++i) {
if (i > 0)
s << ", ";
s << args.at(i)->argumentName();
}
s << ");" << endl;

// Then call the script implementation
s << " ";
if (fun->type()) {
writeTypeInfo(s, fun->type());
s << "resultscript = qscriptvalue_cast<";
writeTypeInfo(s, fun->type());
s << ">(";
}
s << "_q_function.call(__qtscript_self";
if (args.size() > 0) {
s << "," << endl;
s << " QScriptValueList()";
int i = 0;
for (int j = 0; j < args.size(); ++j) {
if (fun->argumentRemoved(j+1))
continue;
s << endl << " << ";
s << "qScriptValueFromValue(_q_engine, ";
AbstractMetaType *atype = args.at(j)->type();
QString asig = atype->cppSignature();
bool constCastArg = asig.endsWith('*') && asig.startsWith("const ");
if (constCastArg)
s << "const_cast<" << asig.mid(6) << ">(";
s << args.at(i)->argumentName() << ")";
if (constCastArg)
s << ")";
++i;
}
}
s << ")";
if (fun->type())
s << ")";
s << ";" << endl;

if (fun->type()) {
// Decide which value to return
s << " if ((_callmode & 2) > 0) {" << endl;
s << " return resultcpp;" << endl;
s << " } else {" << endl;
s << " return resultscript;" << endl;
s << " }"<< endl;
}

s << " } else {" << endl;
}

// only call the script implementation
s << " ";
if (fun->type()) {
s << "return qscriptvalue_cast<";
writeTypeInfo(s, fun->type());
s << ">(";
}
s << "_q_function.call(__qtscript_self";
if (args.size() > 0) {
s << "," << endl;
s << " QScriptValueList()";
int i = 0;
for (int j = 0; j < args.size(); ++j) {
if (fun->argumentRemoved(j+1))
continue;
s << endl << " << ";
s << "qScriptValueFromValue(_q_engine, ";
AbstractMetaType *atype = args.at(j)->type();
QString asig = atype->cppSignature();
bool constCastArg = asig.endsWith('*') && asig.startsWith("const ");
if (constCastArg)
s << "const_cast<" << asig.mid(6) << ">(";
s << args.at(i)->argumentName() << ")";
if (constCastArg)
s << ")";
++i;
}
}
s << ")";
if (fun->type())
s << ")";
s << ";" << endl;

if (!fun->isAbstract()) {
s << " }" << endl;
}

s << " }" << endl;

s << "}" << endl << endl;
}
}


Cheers,

Johannes