Can’t access public slots from QtScript - qt

I have this class:
class JavaScript : public QObject {
Q_OBJECT
public:
JavaScript();
bool executeFromFile(QString file);
bool enabled;
public slots:
void setEnabled( bool enabled );
bool isEnabled() const;
private:
QScriptEngine engine;
};
The methods are defined like this:
#include "javascript.h"
JavaScript::JavaScript() {
executeFromFile("test.js");
}
bool JavaScript::executeFromFile(QString file) {
QFile scriptFile(file);
if (!scriptFile.open(QIODevice::ReadOnly)) return false;
QTextStream stream(&scriptFile);
QString contents = stream.readAll();
scriptFile.close();
engine.evaluate(contents, file);
return true;
}
void JavaScript::setEnabled( bool enabled ) {
JavaScript::enabled = enabled;
}
bool JavaScript::isEnabled() const {
return enabled;
}
I’m trying to access the public slots previously defined in the header file like the documentation says:
http://doc.qt.digia.com/qt/scripting.html#making-a-c-object-available-to-scripts-written-in-qtscript
The test.js file looks like this, just like the examples of the docs:
var obj = new JavaScript();
obj.setEnabled( true );
print( "obj is enabled: " + obj.isEnabled() );
But i’m not getting anything. It seems it doesn’t find the JavaScript object. What am I missing?
Doing a simple
print(1+1)
works just fine.
EDIT: An example in the qt4 webpage implements Q_PROPERTY. I tried this, but got the same result:
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
EDIT 1: Tried implementing the initializer like this:
// javascript.h:
JavaScript(QObject *parent = 0);
// javascript.cpp:
JavaScript::JavaScript(QObject *parent) : QObject(parent) {}
Still nothing...
EDIT 2: Some examples inherits from QScriptable too:
class JavaScript : public QObject, public QScriptable {}
But that makes no difference either.

You need to create QScriptClass instead of QObject. Qt contains example of how to extend script capabilites in Qt. Take a look on Custom Script Class Example

What I think you are actually missing is adding it to the script engine.
At some point you will have to declare a script engine
QScriptEngine * engine = new QScriptEngine(this);
Then you are going to want to add your object to the engine
JavaScript* js= new JavaScript();
QScriptValue jsobj = engine->newQObject(js);
engine->globalObject().setProperty("JavaScript", jsobj );
I'm by no means an expert but I think there is something else you need to do to say
var obj = new JavaScript();
at that point you probably need to take Kamil's advice and make JavaScript a subclass of QScriptClass

Related

What is the proper usage of Q_DECLARE_SMART_POINTER_METATYPE?

I tried to define a function that returns std::shared_ptr<OrderModel> as follows:
Q_DECLARE_SMART_POINTER_METATYPE(std::shared_ptr)
namespace tradeclient {
class OrderModel : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString marketId READ marketId CONSTANT)
Q_PROPERTY(quint64 id READ id CONSTANT)
...
};
using OrderPtr = std::shared_ptr<OrderModel>;
class MarketModel : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE tradeclient::OrderPtr createLimitOrder()
{
return m_orders.front();
}
private:
//In my app I initialize OrderModel-s and add them so some container.
std::vector<OrderPtr> m_orders;
};
} //namespace tradeclient
Q_DECLARE_METATYPE(tradeclient::OrderPtr)
qRegisterMetaType<tradeclient::OrderPtr>();
and then call createLimitOrder() from QML:
var order = market.createLimitOrder()
console.log("Limit order type: %1, value: %2, id: %3".arg(typeof(order)).arg(JSON.stringify(order)).arg(order.id))
but with no success, because I can't access order.id and the console output is:
Limit order type: object, value: "", id: undefined
I also tried to define functions like this:
Q_INVOKABLE QVariant createLimitOrder()
{
return QVariant::fromValue(m_orders.front());
}
Q_INVOKABLE QSharedPointer<tradeclient::OrderModel> createLimitOrder()
{
return QSharedPointer<tradeclient::OrderModel>(new OrderModel());
}
but nothing changed.
But at least, QML treats order as a non-empty value and the following QML code:
if (order)
console.log("The order is defined.");
else
console.log("The order is not defined.");
prints:
"The order is defined."
So order is defined, but its properties are not. What did I miss?
Buy the way, the above QML code works correctly if I return naked pointer:
Q_INVOKABLE tradeclient::OrderModel* createLimitOrder()
{
return m_orders.front().get();
}
it prints
Limit order type: object, value: {"objectName":"","marketId":"BTCUSDT","id":0,"side":1,"type":0,"status":0,"price":{"precision":2},"stopPrice":{".....
Does QVariant that wraps std::shared_ptr expose object properties to QML?
EDIT1
As far as I understand, Q_DECLARE_SMART_POINTER_METATYPE is a relatively simple thing, it requires the class template to have operator -> (see ./qtbase/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp):
namespace MyNS {
template<typename T>
class SmartPointer
{
T* pointer;
public:
typedef T element_type;
explicit SmartPointer(T* t = nullptr)
: pointer(t)
{
}
T* operator->() const { return pointer; }
};
}
Q_DECLARE_SMART_POINTER_METATYPE(MyNS::SmartPointer)
so it is reasonable to expect that it behaves like a naked pointer in QML. However, I have concerns that this is one of those cases where they simply did not implement this simple thing well enough.

C++/CLI: wrapping the same unmanaged object in multiple managed objects

I am developing a library which has two layers, unmanaged (C++) and managed (C++/CLI). The unmanaged layer contains the logics and the computation algorithms, while the managed layer provides interface and visualisation to a .NET-based host application. A class in the managed layer wraps its class counterpart in the unmanaged layer, e.g. ManagedA wraps UnmanagedA and ManagedB wraps UnmanagedB.
Classes in the unmanaged layer have query methods, suppose UnmanagedA::B() returns an instance of UnmanagedB. For visualisation, I need to wrap this instance in a ManagedB instance. The problem is, if I repeat this process twice, I am creating two ManagedB instances which points to the same UnmanagedB instance. Because the ManagedB instances are disposed, the same UnmanagedB instance is deleted twice, which should not happen.
So I would like to know the best practice or strategy to wrap an unmanaged object in a managed object.
Here is a code which emulates this behaviour. I understand that you don't need to explicitly delete the managed objects, but I use it here just to emulate the deletion sequence.
Many thanks.
#include "stdafx.h"
using namespace System;
class UnmanagedB
{
public:
UnmanagedB() {}
~UnmanagedB() {}
int i = 0;
};
class UnmanagedA
{
public:
UnmanagedA(UnmanagedB* pUnmanagedB)
: m_pUnmanagedB(pUnmanagedB)
{
}
~UnmanagedA() {}
UnmanagedB* B() { return m_pUnmanagedB; }
protected:
UnmanagedB* m_pUnmanagedB;
};
public ref class ManagedA : IDisposable
{
public:
ManagedA(UnmanagedA* pUnmanagedA)
: m_pUnmanagedA(pUnmanagedA)
{
}
~ManagedA()
{
delete m_pUnmanagedA;
}
private:
UnmanagedA* m_pUnmanagedA;
};
public ref class ManagedB : IDisposable
{
public:
ManagedB(UnmanagedB* pUnmanagedB)
: m_pUnmanagedB(pUnmanagedB)
{
}
~ManagedB()
{
delete m_pUnmanagedB;
}
private:
UnmanagedB * m_pUnmanagedB;
};
int main(array<System::String ^> ^args)
{
UnmanagedB* pUnmanagedB = new UnmanagedB();
UnmanagedA* pUnmanagedA = new UnmanagedA(pUnmanagedB);
ManagedB^ pManagedB1 = gcnew ManagedB(pUnmanagedA->B());
ManagedB^ pManagedB2 = gcnew ManagedB(pUnmanagedA->B());
delete pManagedB1;
delete pManagedB2; // will crash here because the destructor deletes pUnmanagedB, which is already deleted in the previous line
delete pUnmanagedA;
return 0;
}
This is a typical case using a smart pointer.
So don't store UnmanagedA* and UnmanagedB* use shared_ptr and shared_ptr
Becaus ethe managed class can only carry a plain pointer to an unmannged class you have to redirect it again and use:
shared_ptr<UnmanagedA>* pManagedA;
A simple accessor function will help you to use the pointer:
shared_ptr<UnmanagedA> GetPtrA() { return *pManagedA; }
All plain pointer to the unmanaged classes should be shared_ptr instances. In your main use make_shared instead of new. Or direct the pointer created by new into a shared_ptr...
Here is one class rewritten:
public ref class ManagedA : IDisposable
{
public:
ManagedA(shared_ptr<UnmanagedA> pUnmanagedA)
{
m_pUnmanagedA = new shared_ptr<UnmanagedA>();
*m_pUnmanagedA = pUnmanagedA;
}
~ManagedA()
{
delete m_pUnmanagedA;
}
void Doit()
{
GetPtrA()->DoSomething();
}
private:
shared_ptr<UnmanagedA>* m_pUnmanagedA;
shared_ptr<UnmanagedA> GetPtrA() { return *m_pUnmanagedA; }
};

Nor base nor derived virtual function being properly called

I have this base class:
// put the display in a macro on a .h file for less headache.
class Gadget {
protected:
int x, y;
U8GLIB * u8g;
virtual int f_focus() {return 0;};
virtual int f_blur() {return 0;};
virtual void f_draw() {};
virtual void f_select() {};
public:
Gadget(U8GLIB * u8g, int x, int y) :
u8g(u8g),
x(x),
y(y)
{
Serial.println(F("Gadget(U8GLIB * u8g, int x, int y)"));
};
Gadget() {
Serial.println(F("Gadget()"));
};
int focus(){return f_focus();};
int blur(){return f_blur();};
void draw(){f_draw();};
void operator()(){f_select();};
};
And this derived class:
class WakeUp :
public Gadget
{
public:
WakeUp(U8GLIB * u8g) :
Gadget(u8g, 0, 0)
{
Serial.println(F("WakeUp(U8GLIB * u8g)"));
};
};
Then I instantiate the WakeUp class inside an array like this:
Gadget gadgets[1] = {
WakeUp(&u8g)
};
Then I try to access this member like this:
void focus() {
Serial.println(gadgets[0].focus());
}
It is supposed to display 0. However it is displaying -64. Even if I override the f_focus() method on WakeUp class. If I remove the virtual specifier from f_focus() it works fine, displaying 0, but I will not be able to access the derived class implementation of this method.
I wish to understand what is causing this strange behavior and what can I do to avoid it.
EDIT:
The function runs fine if I call it from the Gadget Constructor.
You're slicing your WakeUp object.
You essentially have the following:
Gadget g = WakeUp(...);
What this code does is the following:
Construct a WakeUp object.
Call Gadget(const Gadget& other) with the base from the WakeUp object.
Destroy the temporary WakeUp object, leaving only the copy of the Gadget base.
In order to avoid this, you need to create an array of pointers (this is better if they are smart pointers).
Gadget* gadgets[1] = { new WakeUp(&u8g) }; // If you choose this method, you need to call
// delete gadget[0] or you will leak memory.
Using a pointer will correctly preserve the Gadget and WakeUp instances instead of slicing them away.
With smart pointers:
std::shared_ptr<Gadget> gadgets[1] = { std::make_shared<WakeUp>(&u8g) };

Managed class does not compile

I have the following code in Visual C++:
#using <mscorlib.dll>
using namespace System;
__gc class classEx
{
public:
classEx()
{
data = "abcd";
}
classEx(String *s)
{
data=s;
}
String* getNombre()
{
return data;
}
void setNombre(String *s)
{
data=s;
}
private:
String* data;
};
int main(void)
{
classEx* obj = new classEx();
return 0;
}
I have changed the Configuration Manager to Release and Build is checked. The problem is when I try to compile it appears a bunch of errors, such as:
error C4980: '__gc' : use of this keyword requires /clr:oldSyntax command line option
cannot use this indirection on type 'System::String'
The last error points that I cannot use in the second constructor the String *s. Why is that?
Is there something that I am missing?
If you will set a corresponding compiler option to clr:oldsyntax in the project properties as the first message says, then the following code compiles without errors in Visual Studio 2010:
#include "stdafx.h"
using namespace System;
__gc class A
{
public:
A( String *s ) : data( s ) {}
String * get_data() { return data; }
private:
String *data;
};
int main()
{
A *pa = new A( "Hello World" );
Console::WriteLine( pa->get_data() );
return 0;
}
It seems that the second message is the result of that you did not set the option pointed out in the first message.
You should select menu Project → Properties → General → *Supporting of CLR (or something else because I have the Russian release of Visual Studio 2010 I can not name the option exactly in English) → clr:oldsyntax
You need to use different syntax for managed code (not the * operator):
String ^ data;
You'll also need the /clr command line option.
This article has a wealth of examples:
String (C++/CLI and C++/CX)

QGraphicsScene::itemAt() - how to recognize custom classes

i have a little problem
I am programming Petri Net simulator...
I have two different classes
class PNItem : public QObject, public QGraphicsItem
...
and
class PNEdge : public QGraphicsLineItem
when i call...
QGraphicsItem *QGraphicsScene::ItemAt(//cursor position)
, is it possible somehow to get to know, what item i have clicked on? resp. what item is given item by ItemAt?
GraphicsItem::type() is intended to be used to solve this problem.
So you would do something like this for example:
enum ItemType { TypePNItem = QGraphicsItem::UserType + 1,
TypePNEdge = QGraphicsItem::UserType + 2 }
class PNItem : public QObject, public QGraphicsItem {
public:
int type() { return TypePNItem; }
...
};
Which would then allow you to do this:
QGraphicsItem *item = scene->itemAt( x, y );
switch( item->type() )
{
case PNItem:
...
break;
}
doing this also enables the usage of qgraphicsitem_cast
See also: QGraphicsItem::UserType
Since you've only got two types, you could just use dynamic_casting, and check to see if the cast was successful:
QGraphicsItem *item = scene->ItemAt(pos);
PNEdge *as_pnedge;
PNItem *as_pnitem;
if((as_pnedge = dynamic_cast<PNEdge*>(item))){
// do stuff with as_pnedge
}else if((as_pnitem = dynamic_cast<PNItem*>(item))){
// do stuff with as_pnitem
}

Resources