PDA

View Full Version : Q_PROPERTY compile problem



Wasabi
7th January 2011, 14:22
I'm starting to want to use the Q_PROPERTY system for a specific case. I have a class with a member which contains the information used in various modeless dialogs. However, since these dialogs are modeless, the information might be altered while they are running. So I thought of transforming this member into a Q_PROPERTY with a NOTIFY signal that I could then connect to the dialogs so they'd refresh themselves should this data be altered. However, the member is now no longer detected as a class data member and thus the class fails to compile (giving hundreds of "undeclared identifier" errors).

The class declaration is the following:

class Canvas : public QSFMLCanvas
{
Q_OBJECT
Q_PROPERTY(std::vector<Fault> faults READ GetFaults WRITE SetFaults NOTIFY FaultsEdited)
Q_PROPERTY(std::vector<Horizon> horizons READ GetHorizons NOTIFY HorizonsEdited)
/*...*/
//std::vector<Fault> faults;
//std::vector<Horizon> horizons;
/*...*/
public:
std::vector<Fault> GetFaults() const;
void SetFaults(std::vector<Fault> v);
std::vector<Horizon> GetHorizons() const;
/*...*/
signals:
void FaultsEdited();
void HorizonsEdited();
};


As well, should I uncomment the original data members, it compiles just fine, but the NOTIFY signal is not emitted.

tbscope
7th January 2011, 15:53
Create your obvious private members.

In the rest of the code, use the property functions like getFaults and setFaults.

Wasabi
7th January 2011, 16:07
They are created. The compiling problem isn't with the functions, but with the data members, faults and horizons.

1>.\Canvas.cpp(74) : error C2065: 'faults' : undeclared identifier
1>.\Canvas.cpp(78) : error C2065: 'horizons' : undeclared identifier

high_flyer
7th January 2011, 16:10
They are created.
Not in the header you posted.
You need a member that can store the values you set and get.

Wasabi
7th January 2011, 16:30
Wait, in the examples in the doc (http://doc.trolltech.com/latest/properties.html#a-simple-example), the member is given in the Q_PROPERTY line and never again.

Q_PROPERTY(Priority priority READ priority WRITE setPriority)
It later defines the enum type Priority, but doesn't create a member priority.
So, as I understand, the macro already creates the data member.

As well, should I uncomment the original data members (which are commented in the given header), it compiles just fine, as stated in the OP. However, the signal is not emitted. Even if the value is altered via the WRITE function.

I mean, I'm not supposed to manually emit() the signal from within the WRITE function, right? That's precisely what the NOTIFY keyword is supposed to do for me, right?

EDIT: Just read this (http://www.qtcentre.org/threads/1252-custom-plugin-designer-property-with-out-a-variable), which cleared my doubt regarding the variable declaration, but there's still the issue of the signal not being emitted.

high_flyer
7th January 2011, 17:14
but there's still the issue of the signal not being emitted.
This is an implementation issue, with out the code responsible for it, its hard to tell what you are doing wrong.

Wasabi
7th January 2011, 17:32
As a pure test, I created a bogus slot in Canvas (::asd()).
I then did the following:

class Canvas : public QSFMLCanvas
{
Q_OBJECT
Q_PROPERTY(std::vector<Fault> faults READ GetFaults WRITE SetFaults NOTIFY FaultsEdited)
Q_PROPERTY(std::vector<Horizon> horizons READ GetHorizons NOTIFY HorizonsEdited)
std::vector<Fault> faults; //contains all faults in project
std::vector<Horizon> horizons; //contains all horizons in project
public:
Canvas(const QPoint& Position, const QSize& Size, QWidget* Parent=0);
Canvas();
std::vector<Fault> GetFaults() const;
void SetFaults(std::vector<Fault> v);
std::vector<Horizon> GetHorizons() const;
public slots:
void asd();
signals:
void FaultsEdited();
void HorizonsEdited();
};

Canvas::Canvas()
{
//...
connect(this,SIGNAL(FaultsEdited()),this,SLOT(asd( )));
std::vector<Fault> v;
v.push_back(Fault());
SetFaults(v);
//...
}
void Canvas::SetFaults(std::vector<Fault> v)
{
faults=v;
}
void Canvas::asd()
{
int u=1;
}

I run this in Debug Mode with a breakpoint inside asd() so that, should the signal be emitted and received, the program simply stops at the breakpoint. This is a quick and easy method I use often.

But this time, nothing happens, telling me the signal wasn't emitted. Again, I don't have to emit the NOTIFY signal myself, do I? The macro should do that for me, right?

wysota
7th January 2011, 18:03
There is no magic there. Qt won't do your work for you. Q_PROPERTY only marks certain things as available for the property system but you have to implement all the getters and setters yourself. Including all signal emissions. Only you know what should happen inside a setter and if you just add "emit FaultsEdited();" in your setter then this will not be a proper solution. You can only emit the signal if the contents of the property really changed.


void Canvas::SetFaults(std::vector<Fault> &v) {
if(faults==v) return;
faults = v;
emit FaultsEdited();
// better yet: emit FaultsEdited(faults);
}

Wasabi
7th January 2011, 18:08
Ah, okay. I thought that Q_PROPERTY was just as magic-imbued as SIGNALS and SLOTS. I also thought that there might be uses for it outside of the Meta-Object System. But that answers all my doubts. Merci.

wysota
7th January 2011, 18:41
Ah, okay. I thought that Q_PROPERTY was just as magic-imbued as SIGNALS and SLOTS.
Not that much. There are properties that are not mapped to fields in the class so if Qt did that automatically, it would bring needless memory overhead. Just look at QWidget - there are a couple of properties related to geometry of the widget (geometry, pos, rect, size, width, height, x, y) but they all work on one single rectangle.