I have a GUI displaying a tree architecture as shown here.
Each of those nodes are separate classes that are derived from the node above them. All of them inherit QObject for their implementation.
Now I need to add a few properties to be displayed when the user selects Properties under the Right-Click menu of Implicit. Selecting this opens a window with the properties.
I added these properties in the header file of Implicit like so :
#ifndef FCIMPLICIT_H
#define FCIMPLICIT_H
#include <QObject>
#include "Interface.h"
#include "ResourceItem.h"
#include "MonWindow.h"
#include "FCTab.h"
#include "ResourceItem.h"
#include "FCAbstract.h"
#include "FCInterface.h"
#include "FCConnections.h"
class CFCImplicit: public CResourceItem
{
Q_OBJECT
Q_PROPERTY(int FCPortID READ getPortID )
Q_PROPERTY(QString Type READ getType )
Q_PROPERTY(QString Status READ getStat )
Q_PROPERTY(int WWNodeNumber READ getNodeNo )
Q_PROPERTY(int WWPortNumber READ getPortNo )
Q_PROPERTY(bool AutoActive READ getAuto )
public:
CFCImplicit(QObject*);
~CFCImplicit();
QString getType();
QString getStat();
int getPortID();
int getPortNo();
int getNodeNo();
bool getAuto();
};
FCinterface.h is the header of the FCASM node.
The issue is that only the first property is displayed, as seen in the second picture. Is there a reason why this is happening? Am I supposed to add something to the constructor or a new function?
The constructor for the Implicit class is
CFCImplicit::CFCImplicit(QObject* parent) : CResourceItem(parent)
{
fnSetProperty("objectName", QString("Implicit"));
((CResourceItem*)parent)->fnAddResources(this);
}
EDIT:
This is the code for all the READ functions
QString CFCImplicit::getType()
{
QString a;
a="Implicit";
return a;
}
QString CFCImplicit::getStat()
{QString a;
a="Idle";
return a;}
int CFCImplicit::getPortID()
{int a;
a=1;
return a;}
int CFCImplicit::getPortNo()
{int a;
a=2;
return a;}
int CFCImplicit::getNodeNo()
{int a;
a=2;
return a;}
bool CFCImplicit::getAuto()
{bool a;
a=true;
return a;}
I found out what I was doing wrong. I assumed that since the properties were read only, I only needed a READ accessor function. By adding the WRITE accessor and adding the required WRITE functions, the properties were displayed. I don't exactly understand why this condition is required (maybe having just READ just makes the properties available for introspection), but it worked! So there you go.
Happy coding everyone!
No need to add the READ and the function, you can use the MEMBER to have direct access to the variable.
Related
I want to add some properties (like an ID) to a QPushButton. Therefore, I need to expand or overwrite the class Q_WIDGETS_EXPORT QPushButton : public QAbstractButton
How do I do that?
Thanks for the help.
you dont need to extend the class to just put an id in it ... instead make use of the property system.
as specified in the official doc here:
A property can be read and written using the generic functions QObject::property() and QObject::setProperty(), without knowing anything about the owning class except the property's name.
you just have to do:
ui->myButton->setProperty("Id", 123456);
can also be another object e.g a string (or even your own class if you define it to do that)
ui->myButton->setProperty("_name", "123456");
to read the property is the method property() there for you but read the doc because you get a QVariant as return example:
QVariant(int, 123456)
It really depends on the use case. There is no problem (and often the intended way) in inheriting from Qt-(Widget) Classes (correct me, if I am wrong).
So you could do:
class MyQPushButton : public QPushButton
{
Q_OBJECT
public:
MyQPushButton() : QPushButton(...) {}
private:
int ID = -1;
}
Qt has a very good documentation and you can look at the sources to see what to override.
You could also extend a new class with QPushButton, but than you always have to deal with the QPushButton reference in your class, if you want e.g. connect something. In the inherited class you can connect the slots and so on. But for example you could do this:
class MyQPushButton
{
public:
MyQPushButton() {}
const QPushButton& const GetQPushButton() { return pushButton; }
const QPushButton* const GetQPushButtonPtr() { return &pushButton; }
private:
QPushButton pushButton;
int ID = -1;
}
There is no right and wrong. But I would use the inheritance for Qt-classes.
I wanted to write a pretty simple code which looks for a string and then outputs the variable assigned to it.
I wanted to learn more about the communication between C++ and QML. So I thought of using a hash map in C++ and then use those functions in QML to list out the result.
I had lot of errors so I basically tried everything to make it work and now my code works. But can someone help me in understanding what I have done.
I have few doubts
I was not able to access my functions loadItemList() and findcubby() but as soon as I included the Q_OBJECT Macro it started working why ?
I have seen in some codes online that they often use something like explicit Itemlist(QObject *parent = 0); why and what does this mean?
I have defined my Hash map as public variable which I know is not good, can someone show me how would I go about for a private hash map.
Here is my header file
#ifndef ITEMLIST_H
#define ITEMLIST_H
#include <QObject>
#include <QHash>
class Itemlist : public QObject
{
Q_OBJECT
public:
Itemlist();
Q_INVOKABLE int find_cubby(QString);
QHash<QString, int> my_itemlist;
};
Here is cpp file
#include "itemlist.h"
Itemlist::Itemlist()
{
my_itemlist["aaaa"]=1;
my_itemlist["bb"]=1;
my_itemlist["cc"]=1;
my_itemlist["dd"]=1;
my_itemlist["ee"]=2;
my_itemlist["ff"]=2;
my_itemlist["gg"]=3;
my_itemlist["hh"]=3;
my_itemlist["ii"]=3;
}
int Itemlist::find_cubby(QString Name)
{
if(my_itemlist.contains(Name))
{
return my_itemlist.value(Name);
}
else
{
return 4;
}
}
Q_OBJECT is necessary as it serves as a marker for the moc code generator to create runtime introspection data for the class. Features such as properties or Q_INVOKABLE depend on that mechanism.
explicit is a C++ key word that marks a constructor as not available for implicit type conversions. Generally, constructors that can be called with a single argument can be used by the compiler to convert between the argument type and the class of that constructor.
E.g.
class Foo
{
public:
Foo(int i) {}
};
void doSomething(Foo f) {}
doSomething(5);
The compiler has an integer, 5, and needs a Foo object. There is a constructor for Foo that takes an int, so it can use it for an automatic type conversion.
explicit Foo(int i) {} removes that option, so the compiler will exit with an error, saying that it can't find a doSomething(int) function.
Use a private: section in your class just like you currently do with public:
I would like to have access to constant/variable of desktop width and height in entire program.
This is how I do it - add this code to every .h file of my program and then use it normally.
#include <QDesktopWidget>
QDesktopWidget desktop;
int desktopHeight = desktop.geometry().height();
int desktopWidth = desktop.geometry().width();
I know it is not a good way how to do it. I tried to make one special desktopSize.h and then include to required parts of my program. But I was not successful.
What should be in header file like this one which I need?
You really do not want to use that particular approach and include that code in all translation units. If you did each one would include two variables named desktopWidth and desktopHeight causing duplicate symbol errors during link time. It will also make it difficult to manage updating them if the size of the desktop changes after the application starts. If you really want to provide global variables holding the size of the desktop you should place them in a single .cpp file and place extern declarations in a single header file that is included when needed.
Header file (GlobalDesktopInfo.h)
#ifndef GLOBALDESKTOPINFO_H
#define GLOBALDESKTOPINFO_H
extern int desktopHeight;
extern int desktopWidth;
#endif GLOBALDESKTOPINFO_H
Source file (GlobalDesktopInfo.cpp)
#include "GlobalDesktopInfo.h"
int desktopHeight = 0;
int desktopWidth = 0;
You will also need to initialize it at the earlier point reasonably possible. I suggest doing this in your main() function.
#include "GlobalDesktopInfo.h"
// other includes
int main()
{
QApplication app;
QDesktopWidget desktop;
desktopHeight = desktop.geometry().height();
desktopWidth = desktop.geometry().width();
// other code and initialization
}
I think it's better to have some slots in a class which return the desired values and use Qt's Signal/slot mechanism to access them from other classes.
Just make a signal in the target class, connect it to a slot in the class containing the slots and make a connection between two objects of the classes. This way you can access them in every class you like by connecting a signal to a slot returning that value and just emitting the signal and getting the returned value.
For example you can have a class like :
class DesktopInformation
{
Q_OBJECT
public:
DesktopInformation(QObject *parent = 0);
~DesktopInformation();
QDesktopWidget desktop;
public slots:
int getWidth()
{
return desktop.geometry().width();
}
int getHeight()
{
return desktop.geometry().height();
}
};
And access the desktop information from any other class like :
class SomeClass: public QObject
{
Q_OBJECT
public:
SomeClass(QObject *parent = 0);
~SomeClass();
signals:
int getWidth();
int getHeight();
private:
void someFunction()
{
int width = getWidth();
int heigth = getHeight();
...
}
};
And connect the signal from an object of SomeClass to the slot in an object of DesktopInformation :
connect(someClass, SIGNAL(getWidth()), desktopInformation, SLOT(getWidth()));
In class someClass you can access the value returned from getWidth slot in desktopInformation by just calling the signal and using the returned value.
Note that the two objects should be in the same thread for this to work. If they are in different threads then the connection type should be of type Qt::BlockingQueuedConnection :
connect(someClass, SIGNAL(getWidth()), desktopInformation, SLOT(getWidth()), Qt::BlockingQueuedConnection);
Another way is two use static member functions but it is not recommended unless you have a good reason to do it :
class desktopInformation {
public:
static QDesktopWidget desktop;
static int getWidth()
{
return desktop.geometry().width();
}
static int getHeight()
{
return desktop.geometry().height();
}
};
class someClass {
public:
void do_something();
};
You can access desktopInformation's static member function from someClass like this:
void someClass::do_something()
{
int width = A::getWidth();
...
};
The qml viewer (for 4.8 and 5.0) is implemented like that:
In the .h(eader) we have:
class QtQuick2ApplicationViewer : public QQuickView
{
Q_OBJECT
...
private:
class QtQuick2ApplicationViewerPrivate *d;
};
Then in the .CPP file:
class QtQuick2ApplicationViewerPrivate
{
QString mainQmlFile;
friend class QtQuick2ApplicationViewer;
static QString adjustPath(const QString &path);
};
QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent)
: QQuickView(parent)
, d(new QtQuick2ApplicationViewerPrivate())
{
connect(engine(), SIGNAL(quit()), SLOT(close()));
setResizeMode(QQuickView::SizeRootObjectToView);
#ifdef Q_OS_ANDROID
engine()->setBaseUrl(QUrl::fromLocalFile("/"));
#endif
}
Why is using friend necessary here? I don't see any reason why would anybody use a friend class. Is there any real use for friend classes (except for exotics that anybody could live without)?
.h
#include
class QtQuick2ApplicationViewer : public QQuickView
{
Q_OBJECT
public:
explicit QtQuick2ApplicationViewer(QWindow *parent = 0);
virtual ~QtQuick2ApplicationViewer();
void setMainQmlFile(const QString &file);
void addImportPath(const QString &path);
void showExpanded();
private:
class QtQuick2ApplicationViewerPrivate *d;
};
.cpp
#include "qtquick2applicationviewer.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtQml/QQmlEngine>
class QtQuick2ApplicationViewerPrivate
{
QString mainQmlFile;
friend class QtQuick2ApplicationViewer;
static QString adjustPath(const QString &path);
};
QString QtQuick2ApplicationViewerPrivate::adjustPath(const QString &path)
{
#ifdef Q_OS_UNIX
#ifdef Q_OS_MAC
if (!QDir::isAbsolutePath(path))
return QString::fromLatin1("%1/../Resources/%2")
.arg(QCoreApplication::applicationDirPath(), path);
#elif !defined(Q_OS_ANDROID)
const QString pathInInstallDir =
QString::fromLatin1("%1/../%2").arg(QCoreApplication::applicationDirPath(), path);
if (QFileInfo(pathInInstallDir).exists())
return pathInInstallDir;
#endif
#endif
return path;
}
QtQuick2ApplicationViewer::QtQuick2ApplicationViewer(QWindow *parent)
: QQuickView(parent)
, d(new QtQuick2ApplicationViewerPrivate())
{
connect(engine(), SIGNAL(quit()), SLOT(close()));
setResizeMode(QQuickView::SizeRootObjectToView);
#ifdef Q_OS_ANDROID
engine()->setBaseUrl(QUrl::fromLocalFile("/"));
#endif
}
QtQuick2ApplicationViewer::~QtQuick2ApplicationViewer()
{
delete d;
}
void QtQuick2ApplicationViewer::setMainQmlFile(const QString &file)
{
d->mainQmlFile = QtQuick2ApplicationViewerPrivate::adjustPath(file);
setSource(QUrl::fromLocalFile(d->mainQmlFile));
}
void QtQuick2ApplicationViewer::addImportPath(const QString &path)
{
engine()->addImportPath(QtQuick2ApplicationViewerPrivate::adjustPath(path));
}
void QtQuick2ApplicationViewer::showExpanded()
{
#if defined(Q_WS_SIMULATOR)
showFullScreen();
#else
show();
#endif
}
Friends examine friends' privates. You sure can do without access restrictions at all, but once you use it, being friendly helps in intimate situations.
class Me;
class You {
friend class Me;
private:
Home _home;
Car _car;
public:
void bar(Me my);
};
class Me {
Stuff _stuff;
public:
foo(You you) {
//If you consider me a friend
you._home.enter(); //I can enter your `private _home`
you._car.drive(); //I can drive your `private _car`.
}
};
void You::bar(Me my) {
my.stuff //this is an error because I don't consider you a friend so you can't touch my `private _stuff`.
}
Knowing you can always count on me, for sure. That's what friends are for. http://www.youtube.com/watch?v=xGbnua2kSa8
But I guess you're asking about friend classes in C++.
The whole point of "scope" is to define exactly who can see what in another class. You don't "need friends" any more than you need "protected" or "private", in the sense that you could make everything in all your classes public, and your program would successfullly compile and run. But the idea is to establish -- and document -- exactly what is the public interface of a class, and thus cannot be changed without considering the impact on other classes, and what is an internal implementation, which can be freely re-worked or re-organized without fear of impacting other classes.
So the point of a "friend" is to say: Hey, I have this class X, and this other class Y. And in general other classes don't need to know how X goes about doing it's job. But Y interacts with some low-level thing in X, so it needs to see it. Thus I make Y a friend of X. Like, I have an Investor class that has a function that (presumably among other things) has a function to calculate the total amount of a customer's investments. In general, other classes shouldn't care how I do that calculation: they just want the total. But now I have a TaxReporting class that needs to know how much of that balance is in taxable securities and how much is in non-taxable securities. Maybe I don't want to make these functions public because the information is confidential and I want to limit access for real-world privacy reasons. More often, I don't want to make it public because the calculation is tricky or subject to frequent change, and I want to keep tight control on what classes access it to limit the problems caused when things change. So I make TaxReporting a friend so it can access some functions that make the distinction, without opening these to the world.
In practice, when I was doing C++ I rarely used friends. But "rarely" is not "never". If you find yourself saying, "Oh, I have to make this public just so this one other class can see it", then maybe instead of making it public you should make a friend.
"friend" is super useful and something you want to use all the time.
Typical use cases are:
You have a class that uses subclasses where the subclass is allowed to use private functions of the class that owns the subclasses:
class ManagedObject
{
public:
void doStuff() { mMgr->updateManager(); }
private:
Manager* mMgr;
};
class Manager
{
friend ManagedObject;
public:
ManagedObject* createManagedObject();
private:
void updateManager() { }
};
So in this case you have a class that creates and deals with "managedObject". Whenever this object is manipulated it needs to update the object that created it. You want users of your class to know that they don't ever need to call "updateManager" and in fact wat to generate a compile time error if they do.
Another common case is when you have a function which acts like a class member but cannot for some reason be a class member. An example is operator<<. If you write your own io stream class, or if you want to create a serialization system that users operator<<:
class serializedObject
{
public:
friend Serializer& operator<< ( Serializer& s, const serializedObject& obj );
protected:
u32 mSecretMember;
};
Serializer& operator<<( Serializer& s, serializedObject& obj )
{
serializer << obj.mSecretMember;
return s;
}
In this case the serialization function cannot be a member of serializedObject, but needs to look at the internals of serializedObject to serialize it. You will see similar patterns of you create other operators ( like addition ) where the RHS of the operator is not the same class as the LHS
In Qt, there is something called a 'guarantee of binary compatibility', which means that your app can run against Qt4.8, 4.8.1, and 4.8.2 and so forth without recompiling.
In order to achieve this the vtable for objects cannot change. So, Qt classes are written using the "PIMPL" (pointer to implementation) idiom.
The "Private" class is the PRIVATE implementation of the public class - it is an implementation detail of QtQuick2ApplicationViewer. No one in the whole world knows about the private class except the public class. These two classes are deeply intertwined by design. In fact, they are really different aspects of a single object that has been partitioned c++ wise in order to achieve the binary compatibility guarantee.
It is reasonable in this context that the private class can access the public class.
2) In this context quit is not QApplication::quit(), that is slot of cause, but some internal signal of engine().
I have written a custom class which is available in QtScript through a Prototype. Also another global class is available which should be used to print the custom class generated in QtScript.
This is my custom class (very simple ;) ):
class Message
{
public:
int source;
int target;
};
This is the prototype I am using:
class MessagePrototype : public QObject, public QScriptable
{
Q_OBJECT
Q_PROPERTY(int source READ getSource WRITE setSource)
Q_PROPERTY(int target READ getTarget WRITE setTarget)
public:
void setSource(const int source);
int getSource() const;
void setTarget(const int target);
int getTarget() const;
};
The setter / getter are only changing / printing the corresponding Message object through a qscriptvalue_cast(QScriptable::thisObject());
Now my script looks like this:
var test = new Message;
test.source = 5;
print(test.source);
GlobalObject.sendMessage(test);
So, the script compiles fine and the print() command does what it should, it prints 5. But the problem is the sendMessage function of my GlobalObject:
void MessageAnalysis::sendMessage(Message msg)
{
qDebug() << "[Message]" << msg.source << msg.target;
}
This piece of code always prints: "[Message] 0 0".
MessageAnalysis is registered as "GlobalObject" for QtScript. Also I have registered Message and Message* as Metatypes and the constructor, prototype and everything else. This seems to work.
Does anyone knows why the values have been changed in QtScript but are not accessible from my C++ function? Or what I am doing wrong?
Ok. After several attempts I fixed it.
I changed the sendMessage function to accept a QScriptValue instead of a Message as parameter. Now I can get the properties out of it without problems.
Seems to work fine now :)