C++ QT5 dynamic property - c++

I am using a widget with Q_PROPERTY entries. Now, I do have an internal map, and for each entry in that list has I'd like to add a dynamic property (name e.g. "entry1Color").
I can add a dynamic property via setProperty("entry1Color", Qt::green); successfully, but I don't have a clue where the value (Qt::green) is being transferred to.
How to connect that set value to my map?

When you use setProperty, this value is stored directly in your QObject, and you can use the property getter to retrieve it. The value is returned as a QVariant, so you will have to cast it to the appropriate type. Example for a color:
// The boolean returned indicates if this is a newly created
// or existing properly
bool was_just_create = myObject->setProperty("myColor", QColor(Qt::green));
// [...]
// And read it later on
QColor color1 = myObject->property("myColor").value<QColor>();
Properties declared explicitly with Q_PROPERTY are accessible in the exact same way, with the property getter. This is the same mechanism that is used by the QML engine to access your object properties, with setProperty and property.
Qt consistently use setValue() for setters, and value() (note the absence of get) for getters. Which is probably why you missed the getter in the first place :).

When you are using QObject::setProperty on instance of QObject, it will be saved internally in QObject instance.
As I understand you want to implement it as QMap with value as member variable.
Here how it can be implemented:
testclass.h
#ifndef TESTCLASS_H
#define TESTCLASS_H
#include <QObject>
#include <QMap>
#include <QColor>
class TestClass : public QObject
{
Q_OBJECT
public:
explicit TestClass(QObject *parent = 0);
// mutators
void setColor(const QString& aName, const QColor& aColor);
QColor getColor(const QString &aName) const;
private:
QMap<QString, QColor> mColors;
};
#endif // TESTCLASS_H
testclass.cpp
#include "testclass.h"
TestClass::TestClass(QObject *parent) : QObject(parent)
{
}
void TestClass::setColor(const QString &aName, const QColor &aColor)
{
mColors.insert(aName, aColor);
}
QColor TestClass::getColor(const QString &aName) const
{
return mColors.value(aName);
}
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>
#include "testclass.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TestClass testClass;
testClass.setColor("entry1Color", Qt::green);
qDebug() << testClass.getColor("entry1Color");
return a.exec();
}
But also, it can be useful to check for how QMap works and which limitations for pairs it has.

When you are using QObject::setProperty on instance of QObject, it will be saved internally in QObject instance.
#Dmitriy: Thanks for clarification and the example code.
Now I do can read the value set by setProperty, fine so far.
But this is not all I want. I'd like to have some kind of a set function that will be called by the dynamic property setter, like the WRITE fn declaration for static Q_PROPERTY entries.
In my case, I create a dynamic property by calling setProperty("entry1Color") just in time with mColors.insert call.
The value should be written directly in my map["entry1Color"]. I did not yet stumble upon any idea to achieve this.

Related

Qt access mainWindow in a class without giving the object to it

I need to access the mainWindow object in a different class. The problem is, that I can not give mainWindow to this class (I don't want to do this, it would make everything much more complicated). Question: Is there any way in C++ or Qt to put an object in something like a local "database" or sth where every other class in the project can look into and communicate with the objects.
What I want to have in the end is something like this:
// A.h
#ifndef A_H
#define A_H
class A{
public:
A() { /*here comes the ting*/ myMainWindow->sayHi(); }
};
#endif // A_H
// MainWindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "a.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
A *a = new A;
}
MainWindow::~MainWindow(){delete ui;}
MainWindow::sayHi(){
// just do anything
}
I don't think, this is possible, but I give it a try...
Thanks for answers!
I need to access the mainWindow object in a different class. The
problem is, that I can not give mainWindow to this class (I don't want
to do this, it would make everything much more complicated).
That is doable. The author don't want to expose the "main window" variable holding the reference or pointer to the object. And, obviously the author wants that UI object to be callable from other objects. In Qt that implies either both objects on UI thread or communication is via queued signal-slot connection only. But the direct call wanted, hence on the same thread.
Is there any way in C++ or Qt to put an object in something like a
local "database" or sth where every other class in the project can
look into and communicate with the objects.
Local thread storage is a known pattern to implement things like that. Qt has own implementation of it called QThreadStorage. You can attempt something like that:
// myLts.h
void ltsRegisterObject(const QString &key, QObject *object);
void ltsRemoveObject(const QString &key);
QObject* ltsGetObject(const QString &key);
template <typename T> T* ltsGet(const QString &key) {
return qobject_cast<T*>(ltsGetObject(key));
}
// myLts.cpp
static QThreadStorage<QMap<QString, QObject*> > s_qtObjects;
void ltsRegisterObject(const QString &key, QObject *object)
{
s_qtObjects.localData().insert(key, object);
}
void ltsRemoveObject(const QString &key)
{
if (!s_qtObjects.hasLocalData())
return;
s_qtObjects.localData().remove(key);
}
QObject* ltsGetObject(const QString &key)
{
QObject *object;
auto it = s_qtObjects.localData().find(key);
return it != s_qtObjects.localData().end() ? it.value() : nullptr;
}
Register main window object in LTS:
#include "myLts.h"
// ...
// register main window in LTS
ltsRegisterObject("mainWindow", &mainWindow);
Find and use the object:
#include "myLts.h"
// ...
// use main window from LTS
auto* pMainWnd = ltsGet<QMainWindow>("mainWindow");
if (pMainWnd)
pMainWnd->show();
P.S. I did not compile this. But it is not hard to fix if so.

Cannot derive from `QSortFilterProxyModel`

I am trying to use a custom QSortFilterProxyModel.
Here is my header:
#include <QSortFilterProxyModel>
class QSortFilterProxyModel_NumbersLast : public QSortFilterProxyModel
{
Q_OBJECT
public:
QSortFilterProxyModel_NumbersLast(QObject * parent = nullptr);
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
};
Here is the constructor from the source file:
QSortFilterProxyModel_NumbersLast::QSortFilterProxyModel_NumbersLast(QObject * parent)
: QSortFilterProxyModel(parent)
{
}
(Also, I have properly - I think - called Q_DECLARE_METATYPE(QSortFilterProxyModel_NumbersLast)
and qRegisterMetaType<QSortFilterProxyModel_NumbersLast>("QSortFilterProxyModel_NumbersLast");
.)
Unfortunately, I receive the following error from the compiler:
error C2248: 'QSortFilterProxyModel::QSortFilterProxyModel' : cannot
access private member declared in class 'QSortFilterProxyModel'
... I do notice that the constructor for the base class, QSortFilterProxyModel::QSortFilterProxyModel, is declared public:
// (From *qsortfilterproxymodel.h*, in the Qt core)
public:
explicit QSortFilterProxyModel(QObject *parent = 0);
My question: Why am I receiving the error 'QSortFilterProxyModel::QSortFilterProxyModel' : cannot access private member? What can I do to fix this?
Note:
From other questions, such as this, this, and this, I see that I might by copying somewhere - which is not allowed for QObjects.
However, the only use of my QSortFilterProxyModel_NumbersLast is the following:
QStandardItemModel * model = new QStandardItemModel(ui->listView_dmu_members);
QSortFilterProxyModel_NumbersLast *proxyModel = new QSortFilterProxyModel_NumbersLast(ui->listView_dmu_members);
proxyModel->setSourceModel(model);
ui->listView_dmu_members->setModel(model);
... which, I'd think, doesn't trigger a copy.
Note 2:
Per #KubaOber's comments:
I have removed the Q_DECLARE_METATYPE(QSortFilterProxyModel_NumbersLast)
and qRegisterMetaType<QSortFilterProxyModel_NumbersLast>("QSortFilterProxyModel_NumbersLast");
registration. The error I now receive is:
Type is not registered, please use the Q_DECLARE_METATYPE macro to
make it known to Qt's meta-object system
I do not believe I am copying the QSortFilterProxyModel_NumbersLast instance. Therefore, I do not understand why this error would be triggered.
(Note that initially, I had not registered the QSortFilterProxyModel_NumbersLast class. It was only due to the above error that I registered the class. I am clear, however, in retrospect, that a QObject-derived class cannot be registered, because it cannot be copied.)
Q_DECLARE_METATYPE requires access to the copy constructor, and that one is deleted (C++11) or inaccessible (C++98) in all QObject-deriving classes. That's the constructor that the compiler complains about. The fix is not to declare the proxy filter model metatype.
The below works for me under both Qt 4.5.8 and 5.2.1, across major desktop platforms.
#include <QApplication>
#include <QSortFilterProxyModel>
#include <QListView>
#include <QStandardItemModel>
class QSortFilterProxyModel_NumbersLast : public QSortFilterProxyModel
{
Q_OBJECT
public:
QSortFilterProxyModel_NumbersLast(QObject * parent = nullptr) :
QSortFilterProxyModel(parent) {}
bool lessThan(const QModelIndex &, const QModelIndex &) const {
return false;
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QStandardItemModel * model = new QStandardItemModel(&app);
QSortFilterProxyModel_NumbersLast *proxyModel = new QSortFilterProxyModel_NumbersLast(&app);
proxyModel->setSourceModel(model);
QListView view;
view.setModel(model);
view.show();
model->appendRow(new QStandardItem("Foo"));
model->appendRow(new QStandardItem("Bar"));
model->appendRow(new QStandardItem("Baz"));
return app.exec();
}
#include "main.moc"
In Qt 5, the error message you see comes from the qMetaTypeId method. It is a static assert, so presumably you're seeing a compile time error. This method is called from qRegisterMetaType and from a few other places, mostly to do with templated connection methods in QObject, and with QVariant.

Qt C++ cannot call member function ' ' without object

I keep getting this error:
cannot call member function 'QString Load::loadRoundsPlayed()'without object
Now im pretty new to c++ and qt so im not sure what this means. I am trying to call a function from another class to set the number on some lcdNumbers. Here is the Load.cpp which holds the function:
#include "load.h"
#include <QtCore>
#include <QFile>
#include <QDebug>
Load::Load() //here and down
{}
QString Load::loadRoundsPlayed()
{
QFile roundsFile(":/StartupFiles/average_rounds.dat");
if(!roundsFile.open(QFile::ReadOnly | QFile::Text))
{
qDebug("Could not open average_rounds for reading");
}
Load::roundsPlayed = roundsFile.readAll();
roundsFile.close();
return Load::roundsPlayed;
}
And here is the Load.h:
#ifndef LOAD_H
#define LOAD_H
#include <QtCore>
class Load
{
private:
QString roundsPlayed; //and here
public:
Load();
QString loadRoundsPlayed(); //and here
};
#endif // LOAD_H
And finally the place where i call the function:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "load.h"
#include <QLCDNumber>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
MainWindow::startupLoad();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::startupLoad()
{
ui->roundPlayer_lcdNumber->display(Load::loadRoundsPlayed()); //right here
}
When i run this i get that error. Im not sure what it means so if anyone could help i would be thankfull. Thanks.
The error description is pretty clear
cannot call member function 'QString Load::loadRoundsPlayed()'without object
You cannot call member functions, that are not static, without creating instance of the class.
Looking at you code, you probably need to do this:
Load load;
ui->roundPlayer_lcdNumber->display(load.loadRoundsPlayed()); //right here
There are two other options:
make loadRoundsPlayed static and roundsPlayed static, if you don't want them to be associated with the concrete instances OR
make loadRoundsPlayed static and return QString by copy, that will be locally created inside the function. Something like
:
QString Load::loadRoundsPlayed()
{
QFile roundsFile(":/StartupFiles/average_rounds.dat");
if(!roundsFile.open(QFile::ReadOnly | QFile::Text))
{
qDebug("Could not open average_rounds for reading");
}
QString lRoundsPlayed = roundsFile.readAll();
roundsFile.close();
return lRoundsPlayed;
}
Because the method and member are not associated with class instances, make it static:
class Load
{
private:
static QString roundsPlayed;
public:
Load();
static QString loadRoundsPlayed();
};
If you want them to be associated with instances, you'll need to create an object and call the method on it (it doesn't have to be static in this case).
In Load::loadRoundsPlayed(), you should change
Load::roundsPlayed = roundsFile.readAll();
to
this->roundsPlayed = roundsFile.readAll();
or simply
roundsPlayed = roundsFile.readAll();
This particular example won't fix the compiler error, but it illustrates where you are having some confusion with the syntax. When you prefix a function or variable name with "Load::", you are saying that you want the field that belongs to this class. However, every object of a class will have its own copy of the variables that you have declared in it. This means that you need to create an object before you can use them. Similarly, functions are bound to objects, so you again need an object in order to call a member function.
The other option is to make your functions static so that you don't need an object to call it. I strongly encourage you to learn about the difference between instance functions and static functions of a class so that you can use these two tools appropriately when the situation calls for it.

How can I use signal and slots to get an id from a qt form, then search for it in a linked list and display the result using another form?

Class with the information that should trigger the the search then display of the other classes after connecting using signal and slots:
#include "recruitsearch.h"
#include "ui_recruitsearch.h"
#include <cctype>
#include <QtGui>
#include <string>
#include <QtCore>
using namespace std;
RecruitSearch::RecruitSearch(QWidget *parent) :
QDialog(parent),
ui(new Ui::RecruitSearch)
{
ui->setupUi(this);
}
RecruitSearch::~RecruitSearch()
{
delete ui;
}
void RecruitSearch::on_pushButton_clicked()
{
//if(EmployerSearch::ui->buttonBox->clicked();
if(ui->rfrId->text().isEmpty() || ui->rfrId->text().isNull() || (is_Digit(ui->rfrId->text().toStdString())==false) ){
QMessageBox::warning(this,"Error", "Please enter a valid RFR id(digits only)");
}
else{
accepted();
this->close();
}
}
int RecruitSearch:: getRfrId(){
return ui->rfrId->text().toInt();
}
bool RecruitSearch::is_Digit( string input){
for (int i = 0; i < input.length(); i++) {
if (!std::isdigit(input[i]))
return false;
}
return true;
}
Class with the display. How would I connect the two slots and use the id from the a first form to search a linkedlist then display results using another form:
#include "rfrform.h"
#include "ui_rfrform.h"
#include <cctype>
#include <string>
#include <QString>
#include <QtGui>
#include <QtCore>
#include <iostream>
using namespace std;
RfrForm::RfrForm(QWidget *parent) :
QDialog(parent),
ui(new Ui::RfrForm)
{
ui->setupUi(this);
}
RfrForm::~RfrForm()
{
delete ui;
}
void RfrForm::setEmpName(string name){
QString qstr=QString::fromStdString(name);
ui->EmployerName->setText(qstr);
}
void RfrForm::setAOE(string aoe){
QString qstr=QString::fromStdString(aoe);
ui->AOE->setText(qstr);
}
void RfrForm::setEmpId(int id){
QString qstr=QString::number(id);
ui->EmpId->setText(qstr);
}// end of setId
void RfrForm::setNumOfPos(int num){
QString qstr=QString::number(num);
ui->numOfPos->setText(qstr);
}
void RfrForm::setGender(string gen){
QString qstr=QString::fromStdString(gen);
ui->gender->setText(qstr);
}
void RfrForm::setMaxRecruits(int max){
QString qstr=QString::number(max);
ui->MaxRecruits->setText(qstr);
}
void RfrForm::display(RFR *temp){
this->show();
}
Think of signals and slots in Qt as simply callbacks. For instance, a standard signal/slot may look as follows:
....
QAction* act = new QAction(this);
connect(act, SIGNAL(triggered()), this, SLOT(handleAct()));
....
Note that the first parameter is a pointer to the object which will emit the signal (thus, the signal method, triggered in this case, must be associated with our sender, in this case QAction) and the second parameter is the signal function signature. Similarly, the last two arguments are the pointer for the slot (i.e. the event handler, in this case, the current class) and the function signature for some handler function.
This is for some class which looks like this (note the Q_OBJECT macro)
class TestMe
{
Q_OBJECT
public:
...
protected slots:
void handleAct();
};
Where the handleAct() method is some arbitrary function you design. Now, to answer your question more specifically, we will need more detail on exactly which type of QWidget you are using (i.e. so we know which signals it emits). In any case, the basic idea is to catch a particular type of symbol from a particular element, and then handle the information accordingly.
Sometimes, to do a type of information transfer you are looking for, you may need to hold particular pointers to objects as class members so you can easily get a the information in the corresponding QWidget (generally a text() method or similar).
It is also important to notice, however, that you can override QWidget types and created custom signals and slots. So if you have a custom signal which returns an id and a signal which takes an id value as input, you can connect these signals and slots (look into the emit functionality)
If you provide some more specific information about the QWidget you are using, we may be able to provide you a more specific solution.

QT/C++ - Accessing MainWindow UI from a different class

I'm a beginner to both C++ and Qt, so perhaps this is trivial. It certainly feels like it should be simple, but I've been searching for an answer for a few hours now and can't find the solution. I'm making a simple board game where the MainWindow's ui (made in QtDesigner) contains a canvas for the game board (a QGraphicsView). Now, the main.cpp is as simple as can be:
MainWindow Game;
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Game.show();
return a.exec();
}
Since I need to access and edit the MainWindow Widgets from another totally unrelated class, I thought the easiest way would be to just make MainWindow a global variable. It seems that this approach was very wrong, though. Upon trying to run the project in QtDesigner I get a Microsoft Visual C++ runtime library error: the application has requested runtime to terminate it in an unusual way.
So what is the correct way to do what I need?
Aside from the MainWindow I have a dialog for a new game (QDialog, generated from QtDesigner) that is displayed after clicking a menu item in MainWindow. When the user inputs all parameters for the game and clicks OK in the dialog, I instantiate a custom non-Qt class called GameState. This class is meant to operate the game itself, draw the board, prompt the user, etc. However, as this class is created in the QDialog, it does not know of the existence of a MainWindow and so I cannot do anything with the MainWindow from this class. How can I modify the MainWindow from an unrelated class, then?
Also, jsut how does the setEnabled() function work? It never seems to do anything. Any widget I set as disabled in the QtDesigner and then try to enable through this function still stays disabled in the GUI...
First off it's a bad idea to create MainGame before you create your QApplication object.
If you want to have your MainGame object globally available like this it should be a pointer:
MainWindow *Game;
int main (int argc, char **argv)
{
QApplication a (argc, argv);
Game = new MainWindow();
Game->show();
int result = a.exec();
delete Game;
Game = NULL;
return result;
}
This approach is however not the most elegant. There are two much better choices.
The QApplication object actually stores all top level windows like your MainGame which means you can allways aquire it through QApplication::topLevelWidgets() which is a static function and returns a list with all top level widgets. Since you only have one, the first one is your MainGame. The drawback is you'll have to cast it, but using Qts qobject_cast<MainGame*>(...) is fairly safe. You'll have to check the result though to make sure it isn't a NULL pointer.
Use the singelton design pattern. You should store the global Game pointer in the source (cpp) file of the Game class itself (subclass QMainWindow) and your Game class should implement a static public method which returns this global pointer. So if any other class needs the Game pointer, it simply calls:
MyGame *theGame = MyGame::getInstance();
for example.
Regarding your setEnabled() problem. Please post the relevant code. If it's too much feel free to send me the *.ui file and the piece of code via mail.
Best regardsD
I am doing it this way:
QMainWindow* getMainWindow()
{
foreach (QWidget *w, qApp->topLevelWidgets())
if (QMainWindow* mainWin = qobject_cast<QMainWindow*>(w))
return mainWin;
return nullptr;
}
If your application has only one window you can simply use:
MainWindow * win = (MainWindow *) qApp::activeWindow();
The easiest way to do this is to first set up a signal in the header file of your other class to say perform a function to manipulate an object in the main class like this
signals:
void disableLoadButtton();
Then create a slot under private slots in the header file of the main window like this
private slots:
void disableLoadButtton();
Then create the function as a members function in the main window to manipulate the object
void MainWindow::disableLoadButton()
{
ui->loadButton->setenabled(false);
}
Then add the following line in another member function of the main window which say sets up the page. My other class is called searchWidget
void MainWindow::setUpPage()
{
connect(searchWidget, SIGNAL(disableLoadButton()), this, SLOT(disableLoadButton()));
}
Then all you have to do to disable the loadButton (which is a object in MainWindow) is to add the following line in any members function of my other class searchWidget
void searchWidget::performSomething()
{
emit disableLoadButton();
}
This will then manipulate the object loadButton in the mainwindow from within a member function of the other class searchWidget.
In the past I used approach described in this answer (found in Qtractor project).
Now I use QObject 'name' property and discover it anywhere as described here.
main.c
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.cpp
#include <QString>
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "c.h"
MainWindow * MainWindow::pMainWindow = nullptr;
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
pMainWindow = this;
setCentralWidget(&m_pb);
connect(&m_pb, SIGNAL(clicked()), this, SLOT(on_pb_clicked()));
}
MainWindow::~MainWindow() {delete ui;}
// kind of singleton reference.
MainWindow *MainWindow::getMainWinPtr()
{
return pMainWindow;
}
void MainWindow::pbSetText()
{
m_pb.setText(QString{"Call from c."});
}
void MainWindow::on_pb_clicked()
{
c C; // call of MainWindow from class c ctor
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QString>
#include <QPushButton>
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
static MainWindow * getMainWinPtr();
void pbSetText();
public slots:
void on_pb_clicked();
private:
static MainWindow * pMainWindow;
Ui::MainWindow *ui;
QPushButton m_pb{QString{"Press me."}, this};
};
#endif // MAINWINDOW_H
c.cpp
#include "c.h"
#include "mainwindow.h"
c::c()
{
MainWindow * mw = MainWindow::getMainWinPtr();
mw->pbSetText();
}
c.h
#ifndef C_H
#define C_H
class c
{
public:
explicit c();
};
#endif // C_H
If you have to access your MainWindow from another window, you are probably doing it wrong. Using another class to pass information with signals/slots is probably a much better approach