Qt-interest Archive, December 2007
Re: QtScript and qScriptRegisterMetaType ( was testing a data model - using custom data types)
Message 1 in thread
This is a follow-up on a post from Tue, 23 Oct 2007
And specifically (from below):
...
> Q_DECLARE_METATYPE(TPanel*)
> ...
> qScriptRegisterQObjectMetaType<TPanel*>(engine);
>
> in your application.
>
> This issue has popped up from different sources lately, so we're
> seriously contemplating the addition of qScriptRegisterQObjectMetaType()
> into Qt 4.4. The docs will also be updated to state that scripting APIs
> should return QObject*/QWidget* in order to work out of the box.
>
> Regards,
> Kent
I've been working with this "meta" stuff lately and wanted to comment on
any 4.4 changes.
I've had to do some fairly serious hacking to get things to work with
C++ "Interfaces"
/// In this usage, "interface" is equivalent to a java interface.
/// Concretely, it means a class with only pure virtual functions
/// and with zero size (i.e. no member data) and (maybe) a virtual
/// destructor.
///
/// "C++ Programming with design patterns revealed" (Tomasz Muldner)
/// uses the the term "abstract interface" for classes with no data
/// or method implementations.
/// "Design Patterns" (Gamma, et al (aka gang of 4)) uses the
/// term "pure abstract class"
I have this interface:
class FsContainer
{
public:
virtual Q_INVOKABLE FsContIter* getContainers() const = 0;
virtual Q_INVOKABLE FsContainer* getChildContainer(QString childName)
};
Q_DECLARE_METATYPE(FsContainer*)
and:
class FsDir : public FsObj, public FsContainer
{
Q_OBJECT
...
Where FsObj is a QObject.
I want to return FsContainer*s in QtScript but this was "hard" (but doable).
I created this:
/// class to help with using:
/// template <typename T>
/// int qScriptRegisterMetaType(
/// QScriptEngine* engine,
/// QScriptValue(*)(QScriptEngine*,const T& t) toScriptValue,
/// void(*)(const QScriptValue&,T& t) fromScriptValue,
/// const QScriptValue & prototype = QScriptValue() )
template <typename T,bool SCRIPT_OWN = false>
class FsScript
{
public:
typedef T* Tptr;
static QScriptValue toScriptValue(QScriptEngine* engine,
const Tptr& fsObj)
{
Tptr nonConstFsObj = const_cast<Tptr>(fsObj);
if ( SCRIPT_OWN ) {
return engine->newQObject(nonConstFsObj,
QScriptEngine::ScriptOwnership);
} else {
return engine->newQObject(nonConstFsObj);
}
}
static void fromScriptValue(const QScriptValue& scriptValue,
Tptr& fsObj)
{
QObject* qobj = scriptValue.toQObject();
fsObj = static_cast<Tptr>(qobj);
}
// needed these next two versions for FsContainer* (because c++ doesn't
// know that all "real" FsContainer*s are QObject*s (FsObj*s))
static QScriptValue toFsObjToScriptValue(QScriptEngine* engine,
const Tptr& pt)
{
Tptr nonConstPt = const_cast<Tptr>(pt);
FsObj* fsObj = dynamic_cast<FsObj*>(nonConstPt);
ICM_ASSERT(fsObj != NULL,"dynamic_cast<FsObj*> failed","");
if ( SCRIPT_OWN ) {
return engine->newQObject(fsObj,QScriptEngine::ScriptOwnership);
} else {
return engine->newQObject(fsObj);
}
}
static void fromScriptValueToFsObj(const QScriptValue&
scriptValue,
Tptr& fsObj)
{
QObject* qobj = scriptValue.toQObject();
fsObj = dynamic_cast<Tptr>(qobj);
ICM_ASSERT(fsObj != NULL,"dynamic_cast failed",typeid(Tptr).name());
}
};
and then did this:
// FsContIter*
qmetaTy = qScriptRegisterMetaType<FsScript<FsContIter>::Tptr>(
d_scriptEngine,
FsScript<FsContIter,/*scriptOwn*/true>::toScriptValue,
// note -----------------
FsScript<FsContIter>::fromScriptValue);
// FsContainer*
qmetaTy = qScriptRegisterMetaType<FsScript<FsContainer>::Tptr>(
d_scriptEngine,
FsScript<FsContainer>::toFsObjToScriptValue,
// note -------
FsScript<FsContainer>::fromScriptValueToFsObj);
// note -------
I just want to make sure that any changes in 4.4 won't preclude this
type of thing.
Angelo Moriconi wrote:
> Hi all!
> I would like to use QtScript to test a data model for an application.
>
> I have two classes Scene and Panel, a Scene can contains a lot of
> panel (there is some method that can
> add a panel and retrieve a panel in the list (which is private)).
>
> Both classes inherith QObject and have their method declared as public
> slots in order to be used with QtScript.
>
> Then, to create new objects from the script I try to declare the
> classes starting from QMetaObject:
>
> Q_SCRIPT_DECLARE_QMETAOBJECT(Scene, QObject*)
> Q_SCRIPT_DECLARE_QMETAOBJECT(TPanel, QObject*)
>
> ...
> ...
>
> QScriptValue sceneClass = m_engine.scriptValueFromQMetaObject<Scene>();
> m_engine.globalObject().setProperty("Scene", sceneClass);
>
> QScriptValue panelClass = m_engine.scriptValueFromQMetaObject<TPanel>();
> m_engine.globalObject().setProperty("TPanel", panelClass);
>
> In this manner I can create a new Scene and a new Panel with new in
> the script.
>
> When I try to retrieve a panel added to a Scene:
>
> var s = new Scene();
> s.addPanel();
>
> var p = s.getPanel(0);
>
> I receive, instead of a Panel type a variant one:
>
> variant(void*, ...)
>
> and then I cannot use any method.
>
> How can I cast this variant in order to have a Panel type and then
> call its methods ?
>
> Probably I miss something, and I need to declare something else for
> the type Scene and Panel.
>
> Thanks in advance,
>
> Angelo
Hi Angelo,
I assume your getPanel() function returns TPanel*. The problem is that
Qt Script has no conversion functions defined for TPanel*, only for the
built-in types QObject* and QWidget*, so you get the default QVariant
transportation. One way of fixing it is to change the return type of
getPanel() to be QObject*; this is OK if the sole purpose of your API is
for scripting anyway. The alternative way is to register conversion
functions for TPanel* so that QtScript will treat it like a QObject.
Copy the following chunk of code into your project:
---[ begin chunk of code ]---
template <typename Tp>
QScriptValue qScriptValueFromQObject(QScriptEngine *engine, Tp const
&qobject)
{
return engine->newQObject(qobject);
}
template <typename Tp>
void qScriptValueToQObject(const QScriptValue &value, Tp &qobject)
{
qobject = qobject_cast<Tp>(value.toQObject());
}
template <typename Tp>
int qScriptRegisterQObjectMetaType(
QScriptEngine *engine,
const QScriptValue &prototype = QScriptValue()
#ifndef qdoc
, Tp * /* dummy */ = 0
#endif
)
{
return qScriptRegisterMetaType<Tp>(engine, qScriptValueFromQObject,
qScriptValueToQObject, prototype);
}
---[ end chunk of code ]---
Then do
Q_DECLARE_METATYPE(TPanel*)
...
qScriptRegisterQObjectMetaType<TPanel*>(engine);
in your application.
This issue has popped up from different sources lately, so we're
seriously contemplating the addition of qScriptRegisterQObjectMetaType()
into Qt 4.4. The docs will also be updated to state that scripting APIs
should return QObject*/QWidget* in order to work out of the box.
Regards,
Kent
--
[ signature omitted ]