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

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
}

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.

Qt novice: base class for QLineEdit and QTextEdit

Is there another class besides QWidget which holds all generic functions for both? Something like QEdit...
As an example I'd like to reference cut(), copy() and paste(), but it looks like I have to dynamic cast the QWidget. Is there any other way?
There is no other way besides QWidget. The reason is that QLineEdit is inherited directly from QWidget. You can see the full hierarchy of Qt classes here
You don't have to dynamic-cast anything: this is typically a sign of bad design. Qt generally has very few interface classes - they usually have the word Abstract somewhere in the name, and are not really pure interfaces as they have non-abstract base classes, like e.g. QObject. Thus there was no pattern to follow, and no need for abstracting out the edit operations into an interface.
There are several approaches to overcome this:
Leverage the fact that the methods in question are known by the metaobject system. Note that invokeMethod takes a method name, not signature.
bool cut(QWidget * w) {
return QMetaObject::invokeMethod(w, "cut");
}
bool copy(QWidget * w) {
return QMetaObject::invokeMethod(w, "copy");
}
//...
You can use the free-standing functions such as above on any widget that supports the editing operations.
As above, but cache the method lookup not to pay its costs repeatedly. Note that indexOfMethod takes a method signature, not merely its name.
static QMetaMethod lookup(QMetaObject * o, const char * signature) {
return o->method(o->indexOfMethod(signature));
}
struct Methods {
QMetaMethod cut, copy;
Methods() {}
explicit Methods(QMetaObject * o) :
cut(lookup(o, "cut()")),
copy(lookup(o, "copy()")) {}
Methods(const Methods &) = default;
};
// Meta class names have unique addresses - they are effectively memoized.
// Dynamic metaobjects are an exception we can safely ignore here.
static QMap<const char *, Methods> map;
static const Methods & lookup(QWidget * w) {
auto o = w->metaObject();
auto it = map.find(o->className());
if (it == map.end())
it = map.insert(o->className(), Methods(o));
return *it;
}
bool cut(QWidget * w) {
lookup(w).cut.invoke(w);
}
bool copy(QWidget * w) {
lookup(w).copy.invoke(w);
}
//...
Define an interface and provide implementations specialized for widget types. This approach's only benefit is that it's a bit faster than QMetaMethod::invoke. It makes little sense to use this code for clipboard methods, but it could be useful to minimize overhead for small methods that are called very often. I'd advise not to over-engineer it unless a benchmark shows that it really helps. The previous approach (#2 above) should be quite sufficient.
// Interface
class IClipboard {
public:
virtual cut(QWidget *) = 0;
virtual copy(QWidget *) = 0;
virtual paste(QWidget *) = 0;
};
class Registry {
// all meta class names have unique addresses - they are effectively memoized
static QMap<const char *, IClipboard*> registry;
public:
static void register(const QMetaObject * o, IClipboard * clipboard) {
auto name = o->className();
auto it = registry.find(name);
if (it == registry.end())
registry.insert(name, clipboard);
else
Q_ASSERT(it->value() == clipboard);
}
static IClipboard * for(QWidget * w) {
auto it = registry.find(w->metaObject()->className());
Q_ASSERT(registry.end() != it);
return it->value();
}
static void unregister(const QMetaObject * o) {
registry.remove(o->className());
}
};
template <class W> class ClipboardWidget : public IClipboard {
Q_DISABLE_COPY(ClipboardWidget)
public:
cut(QWidget * w) override { static_cast<W*>(w)->cut(); }
copy(QWidget * w) override { static_cast<W*>(w)->copy(); }
paste(QWidget * w) override { static_cast<W*>(w)->paste(); }
ClipboardWidget() {
Registry::register(&W::staticMetaObject(), this);
}
~ClipboardWidget() {
Registry::unregister(&W::staticMetaObject());
}
};
// Implementation
QMap<const char *, IClipboard*> Registry::registry;
static ClipboardWidget<QTextEdit> w1;
static ClipboardWidget<QLineEdit> w2;
void yourCode() {
//...
Registry::for(widget)->cut(widget);
}

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

Implement "Navigation bar" for custom editor

When registering a custom language service extension, Visual Studio creates a new options entry for the language within the Text Editor node (in the Visual Studio options dialog). Beneath that node two default nodes are created named General and Tabs, whereby the General tab contains statement completion and display settings...
In the Dispay group there are three options; one of them is the Navigation Bar checkbox (which shows/hides the editor´s navigation bar). For my custom language service, this option is disabled. Of course, it´s not implemented yet.
I would like to know, what I have to do, to provide a navigation bar for my custom editor... I guess that there is a certain interface I have to implement in the editor´s factory, or the language service package must export a certain MEF component, or, or, ...
Jon Senchyna´s answer guided me into the right direction. The OnSynchronizeDropdowns method gets never called (the SDK documentation is just wrong in that case). What did the final trick was to override at least GetComboAttributes, GetEntryAttributes and GetEntryText to get text-only items for both combo boxes...
[ComVisible(true)]
public sealed class CustomTypeAndMemberDropdownBars : TypeAndMemberDropdownBars
{
private readonly IList<string> declarations;
private readonly IList<string> members;
public CustomTypeAndMemberDropdownBars(
LanguageService languageService,
IVsTextView view)
: base(languageService)
{
// TODO: initialize declarations and members from the given text view...
this.declarations = ...
this.members = ...
}
private enum ComboIndex
{
Types = 0,
Members = 1
}
public override int GetComboAttributes(
int combo,
out uint entries,
out uint entryType,
out IntPtr imageList)
{
entries = 0;
imageList = IntPtr.Zero;
entryType = (uint)DROPDOWNENTRYTYPE.ENTRY_TEXT;
var comboType = (ComboIndex)combo;
switch (comboType)
{
case ComboIndex.Types:
entries = (uint)this.declarations.Count();
break;
case ComboIndex.Members:
entries = (uint)this.members.Count();
break;
}
return VSConstants.S_OK;
}
public override int GetEntryAttributes(
int combo,
int entry,
out uint fontAttrs)
{
fontAttrs = (uint)DROPDOWNFONTATTR.FONTATTR_PLAIN;
return VSConstants.S_OK;
}
public override int GetEntryText(
int combo,
int entry,
out string text)
{
text = null;
var comboType = (ComboIndex)combo;
switch (comboType)
{
case ComboIndex.Types:
text = this.declarations[entry];
break;
case ComboIndex.Members:
text = this.members[entry];
break;
}
return VSConstants.S_OK;
}
public override bool OnSynchronizeDropdowns(
LanguageService languageService,
IVsTextView textView,
int line,
int col,
ArrayList dropDownTypes,
ArrayList dropDownMembers,
ref int selectedType,
ref int selectedMember)
{
return false;
}
}
I believe the following steps should be what you need:
In your Package class, set the ShowDropDownOptions property to true in the ProvideLanguageService attribute
Create a class that implements TypeAndMemberDropdownBars
In your LanguageService class, implement the CreateDropDownHelper function and have it return an instance of your TypeAndMemberDropdownBars

Can’t access public slots from QtScript

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

Resources