I'm not entirely sure what I'm doing incorrectly here. I have a class which contains a constant pointer to another class object. However I'm getting an error about not being able to convert the const (class object). What am I doing wrong? Is my code setup incorrect in the what I'm trying to do?
Error message: cannot convert 'const AppProfile' to 'AppProfile*' in return
I initially had this in my header file class AppProfile and i changed it to #include "appprofile.h" which helped remove another error.
I later will call run() which executes run on my AppProfile object.
header file
#ifndef APPITEM_H
#define APPITEM_H
#include <QObject>
#include <QUrl>
#include <QDir>
#include "appprofile.h"
class AppItem : public QObject
{
Q_OBJECT
public:
explicit AppItem(QObject *parent = nullptr);
explicit AppItem(const AppProfile &profile,
QObject *parent);
/// App Profile
AppProfile *profile() const;
signals:
public slots:
void run();
private:
const AppProfile m_profile;
};
#endif // APPITEM_H
cpp file
#include "appitem.h"
#include "appprofile.h"
AppItem::AppItem(QObject *parent) :
QObject(parent)
{
}
AppItem::AppItem(const AppProfile &profile,
QObject *parent) :
QObject(parent),
m_profile(profile)
{
}
QString AppItem::name() const
{
return m_name;
}
void AppItem::run()
{
AppProfile *profile = profile();
profile->run();
}
AppProfile *AppItem::profile() const
{
return m_profile;
}
UPDATE:
Follow up question reguarding the answer givens given...
To simply explain my intentions, I'm parsing a json file that contains data used to create the parent object AppItem. When this item is constructed, it takes in it's construct an AppProfile object. This object is only ever created once, at the time in which AppItem is created.
Knowing that, how would you suggest i move forward editing the original questions code relating to AppProfile. Assuming that's enough information. I appreciate you help. This is what the code looks like that I would use to create an AppItem
AppProfile *profile = new AppProfile();
AppItem *appItem = new AppItem(profile);
For starters either there is a typo in your code or the function is defined incorrectly
AppProfile *AppItem::profile() const
{
return m_profile;
}
Within the class the data member m_profile is not a pointer.
//...
private:
const AppProfile m_profile;
};
So if the declaration of the data member is valid then the function should look like
const AppProfile *AppItem::profile() const
{
return &m_profile;
}
Or if the data member declaration should look like
//...
private:
const AppProfile *m_profile;
};
then in any case the function shall return a pointer to constant data.
const AppProfile *AppItem::profile() const
{
return m_profile;
}
That is the error message implicitly says that there is a typo in your code
cannot convert 'const AppProfile' to 'AppProfile*' in return
And if you will update the typo in any case you may not discard the qualifier const for the pointer.
Related
I know something about C++ but I'm pretty new to QML.
I want to pass multiple custom C++ objects in a container to QML but I am having trouble doing so.
The code provided is narrowed down to the basics.
I can communicate from and to QML in a single object which I register with setContextProperty, this works fine. But if I try to do so with a QHash, then I get an error:
‘QVariant::QVariant(void*)’ is private within this context‘
Maybe you can help me out or give me a direction?
Thanks a bunch.
Update:
Thanks derM, here is my try at it:
I have added: Q_DECLARE_METATYPE(MyData); at the end of the header file. I have changed the container to a QVariantMap.
If I try: QVariant qvtest1(test1);
I get the error:
no matching function for call to ‘QVariant::QVariant(MyData&)’
However this works:
QVariant qvtest1, qvtest2;
qvtest1.setValue(test1);
qvtest2.setValue(test2);
But again I get an error with: setContextProperty("mymap", &mymap);
error:
calling a private constructor of class 'QVariant'
Code is adjusted accordingly.
Update 2
Thanks eyllanesc, your approach is working!
However I now get confronted with related issues in QML.
It seems that I can not acces all the QMap functions from QML.
For example:
var test_data = mymap["three"] // works fine
var test_data2 = mymap.find("two").value() // results in: Property 'find' of object [object Object] is not a function
Same problem:
var tmp1 = mydata_qml_object // object was created before
mymap["four"] = tmp1 // works fine
mymap.insert("four", tmp1) // Property 'insert' of object [object Object] is not a function
I am using Qt 5.11.1
Is this a bug or am I missing something?
C++ code
mydata.hpp:
#ifndef MYDATA_HPP
#define MYDATA_HPP
#include <QObject>
#include <QString>
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)
public:
explicit MyData(QObject *parent = nullptr);
MyData(QString name);
MyData(const MyData &other);
MyData(MyData &&other) = delete;
MyData &operator=(const MyData &other);
MyData operator=(MyData &&other) = delete;
~MyData() override = default;
signals:
void nameChanged();
public slots:
void set_name(const QString &name);
QString get_name();
private:
QString _name;
};
Q_DECLARE_METATYPE(MyData);
#endif // MYDATA_HPP
mydata.cpp:
#include "mydata.hpp"
MyData::MyData(QObject *parent)
: QObject(parent)
{
}
MyData::MyData(QString name)
: _name(name)
{
}
MyData::MyData(const MyData &other)
{
_name = other._name;
}
MyData &MyData::operator=(const MyData &other)
{
if (this != &other)
{
_name = other._name;
return *this;
}
}
void MyData::set_name(const QString &name)
{
_name = name;
}
QString MyData::get_name()
{
return _name;
}
main.cpp:
#include <mydata.hpp>
#include <QGuiApplication>
#include <QMap>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQmlContext>
#include <QQuickView>
#include <iostream>
int main(int argc, char *argv[])
{
MyData test1("Hi");
MyData test2("Hello");
QMap<QString, QVariant> mymap; // QVariantMap
QVariant qvtest1(test1); // error: no matching function for call to ‘QVariant::QVariant(MyData&)’
//working:
QVariant qvtest1, qvtest2;
qvtest1.setValue(test1);
qvtest2.setValue(test2);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
auto *engine = new QQmlEngine;
QQuickView view;
QQmlContext *ctxt = view.rootContext();
// this is working:
qmlRegisterType<MyData>("MyData", 1, 0, "MyData");
engine->rootContext()->setContextProperty("test1", &test1);
// this produces an error: calling a private constructor of class 'QVariant'
engine->rootContext()->setContextProperty("mymap", &mymap);
QQmlComponent component(engine, QUrl("qrc:/main.qml"));
QQmlEngine::setObjectOwnership(engine, QQmlEngine::CppOwnership);
QObject *object = component.create();
return app.exec();
}
According to the docs, the classes that inherit from QObject can not have a copy constructor or assignment operator:
No Copy Constructor or Assignment Operator
QObject has neither a copy constructor nor an assignment operator.
This is by design. Actually, they are declared, but in a private
section with the macro Q_DISABLE_COPY(). In fact, all Qt classes
derived from QObject (direct or indirect) use this macro to declare
their copy constructor and assignment operator to be private. The
reasoning is found in the discussion on Identity vs Value on the Qt
Object Model page.
The main consequence is that you should use pointers to QObject (or to
your QObject subclass) where you might otherwise be tempted to use
your QObject subclass as a value. For example, without a copy
constructor, you can't use a subclass of QObject as the value to be
stored in one of the container classes. You must store pointers.
On the other hand, if you want a class that inherits from QObject to support the QMetaType to use it as QVariant, you must pass it to the pointer because, as noted, the QObjects do not have a copy constructor, but the pointers are copyable.
//mydata.h
#ifndef MYDATA_H
#define MYDATA_H
#include <QObject>
class MyData : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ get_name WRITE set_name NOTIFY nameChanged)
public:
explicit MyData(QObject *parent = nullptr);
MyData(const QString & name);
~MyData() override = default;
signals:
void nameChanged();
public slots:
void set_name(const QString &name);
QString get_name();
private:
QString _name;
};
Q_DECLARE_METATYPE(MyData*)
#endif // MYDATA_H
//mydata.cpp
#include "mydata.h"
MyData::MyData(QObject *parent) : QObject(parent)
{}
MyData::MyData(const QString & name)
: _name(name){}
void MyData::set_name(const QString &name)
{
if(_name == name) return;
_name = name;
emit nameChanged();
}
QString MyData::get_name()
{return _name;}
You point out that setValue works, have you bought that it works ?, besides this method is used for the types that do not support QVariant so they will probably accept any type of data, what must be done is to pass the pointer:
MyData test1("Hi");
MyData test2("Hello");
QMap<QString, QVariant> mymap;
QVariant qvtest1 = QVariant::fromValue(&test1);
QVariant qvtest_1, qvtest_2;
qvtest_1.setValue(&test1);
qvtest_2.setValue(&test2);
On the other hand is setContextProperty, this accepts a QVariant or a pointer to QObject, and in your first case you pass QObject, the correct thing is to pass the pointer, in the second case you pass a QVariant so there are no problems as it can be copied.
engine->rootContext()->setContextProperty("test1", &test1);
engine->rootContext()->setContextProperty("mymap", mymap);
In conclusion you must pass an object copyable to setContextProperty.
qmlRegisterType only records that a type can be transmitted through the signals, but this does not guarantee that it works, this is a necessary condition but not enough since it is not copyable, so you must use MyData*.
In the midst of making a test program I've come across a problem that has me boggled. I have googled and looked on here and haven't found an answer to the problem so thought I might just ask.
The issue: I get compiler errors on a getter and a setter function I made in QT. Basically I need to be able to set and get a QList object.
What am I overlooking? I think I might be doing something wrong with QList but I can't see what.
SliderArray.h
#ifndef SLIDERARRAY_H
#define SLIDERARRAY_H
#include <QWidget>
#include <QList>
#include <QSlider>
class SliderArray : public QWidget
{
Q_OBJECT
public:
explicit SliderArray(int sliders, QWidget *parent = 0);
~SliderArray();
//getter function for slider data
QList<int> GetSliderData();
//setter function for slider data
void SetSliderData(QList<int>);
private:
QList<int> integerList1; //Qlist Object to hold data
SliderArray.cpp
SliderArray::GetSliderData()
{
return integerList1;
}
SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
these are the errors I get when I try to compile:
...\sliderarray.cpp:24: error: prototype for 'intSliderArray::GetSliderData()' does not match any in class 'SliderArray'
SliderArray::GetSliderData()
^
...\sliderarray.h:16: error: candidate is: QList<int> SliderArray::GetSliderData()
QList<int> GetSliderData();
^
...\sliderarray.cpp:29: error: prototype for 'int SliderArray::SetSliderData(QList<int>)' does not match any in class 'SliderArray'
SliderArray::SetSliderData(QList<int> datalist)
^
...\sliderarray.h:19: error: candidate is: void SliderArray::SetSliderData(QList<int>)
void SetSliderData(QList<int>);
^
In your cpp file you should have:
QList<int> SliderArray::GetSliderData()
{
return integerList1;
}
void SliderArray::SetSliderData(QList<int> datalist)
{
integerList1 = datalist;
}
This has nothing to do with Qt, you're just missing the return types.
I have two classes: AbstractClass and SubClass.
This is basically my code (well, just some example code):
abstractclass.h
class AbstractClass
{
public:
AbstractClass();
void doSomething();
protected:
virtual void implementMe() = 0;
int a;
};
abstractclass.cpp
#include "abstractclass.h"
AbstractClass::AbstractClass(){}
void AbstractClass::doSomething()
{
implementMe(); // compiler error: "implementMe() was not declared in this scope"
a = 0; // same compiler error here...
}
subclass.h
#include "abstractclass.h"
class SubClass : public AbstractClass
{
public:
SubClass();
protected:
void implementMe();
};
subclass.cpp
#include "subclass.h"
SubClass::SubClass() {}
void SubClass::implementMe()
{
// do some stuff
}
In the AbstractClass, however, I keep getting a compiler error (for the virtual function as well as for the class variable):
implementMe() was not declared in this scope
The only way I found to get rid of this was to use forward-declaration:
void implementMe();
AbstractClass::doSomething()
{
implementMe();
}
I cannot believe that this is the correct way, though?
Thanks!
EDIT:
Ok, as my conceptual understanding of subclassing in C++ doesn't seem to be totally wrong (see the comments), I'm gonna share some of my original source code. Hopefully this will help to indentify the error.
This is my abstract / base class:
abstractenvironment.h
#ifndef ABSTRACTENVIRONMENT_H
#define ABSTRACTENVIRONMENT_H
#include <QObject>
class AbstractEnvironment : public QObject
{
Q_OBJECT
public:
AbstractEnvironment(QObject *parent = 0);
protected:
virtual void process() = 0;
quint32 counter;
private slots:
void handleTimeout();
};
#endif // ABSTRACTENVIRONMENT_H
abstractenvironment.cpp
#include "abstractenvironment.h"
#include <QTimer>
QTimer *myTimer;
AbstractEnvironment::AbstractEnvironment(QObject *parent) :
QObject(parent)
{
myTimer = new QTimer(this);
connect(myTimer, &QTimer::timeout, this, &AbstractEnvironment::handleTimeout);
myTimer->start(1);
counter = 0;
}
void handleTimeout()
{
process();
counter++;
}
And this is my subclass:
environment.h
#ifndef ENVIRONMENT_H
#define ENVIRONMENT_H
#include "abstractenvironment.h"
class Environment : public AbstractEnvironment
{
Q_OBJECT
public:
Environment(Controller *controller, QObject *parent = 0);
protected:
void process();
};
#endif // ENVIRONMENT_H
environment.cpp
#include "environment.h"
Environment::Environment(Controller *controller, QObject *parent) :
AbstractEnvironment(controller, parent) {}
void Environment::process()
{
// do something
}
PS: I've learned from the first part of this question and tried to compile the source code above inside Qt with MinGW. I get exactly two error messages (as expected):
..\untitled\abstractenvironment.cpp: In function 'void handleTimeout()':
..\untitled\abstractenvironment.cpp:17:13: error: 'process' was not declared in this scope
..\untitled\abstractenvironment.cpp:18:5: error: 'counter' was not declared in this scope
In case you want to try it yourself, I've zipped the Qt project and uploaded it to my Dropbox (of course I will remove this file at some point but the code is exactly the same as in the post above --> it's just for the sake of convenience, so you don't have to copy-paste it yourself)
EDIT: You just changed your question. So I can't tell if your original text was your actual source code or not. Good rule of thumb, paste your actual code rather than paraphrase it (then de-identify or reduce it if needed).
ORIGINAL ANSWER:
implementMe(); // compiler error: "implementMe() was not declared in this scope"
That is because doSomething() isn't declared properly in AbstractClass. You "declared" it in the base class with:
doSomething();
The compiler doesn't recognize AbstractClass::doSomething() out of line definition so nothing inside the implementation is resolved to the class scope.
Change that to:
void doSomething();
just like in your derived class.
and
AbstractClass::doSomething()
{
implementMe();
}
to
void AbstractClass::doSomething()
{
implementMe();
}
UPDATE:
void handleTimeout()
{
process();
counter++;
}
is a global function. That isn't the class implementation. It should be:
void AbstractClass::handleTimeout()
{
process();
counter++;
}
In abstractenvironment.cpp you define void handleTimeout(), which is non-member function and does not relate to AbstractEnvironment class. Thus, it doesn't look for AbstractEnvironment::process() and AbstractEnvironment::counter, but for ::process() and ::counter instead (which are not declared, hence the error).
Change it to void AbstractEnvironment::handleTimeout() and it should compile.
I'm trying to pass QList of integer from QML to C++ code, but somehow my approach is not working. With below approach am getting following error:
left of '->setParentItem' must point to class/struct/union/generic type
type is 'int *'
Any inputs to trouble shoot the issue is highly appreciated
Below is my code snippet
Header file
Q_PROPERTY(QDeclarativeListProperty<int> enableKey READ enableKey)
QDeclarativeListProperty<int> enableKey(); //function declaration
QList<int> m_enableKeys;
cpp file
QDeclarativeListProperty<int> KeyboardContainer::enableKey()
{
return QDeclarativeListProperty<int>(this, 0, &KeyboardContainer::append_list);
}
void KeyboardContainer::append_list(QDeclarativeListProperty<int> *list, int *key)
{
int *ptrKey = qobject_cast<int *>(list->object);
if (ptrKey) {
key->setParentItem(ptrKey);
ptrKey->m_enableKeys.append(key);
}
}
You CAN'T use QDeclarativeListProperty (or QQmlListProperty in Qt5) with any other type than QObject derived ones. So int or QString will NEVER work.
If you need to exchange a QStringList or a QList or anything that is an array of one of the basic types supported by QML, the easiest way to do it is to use QVariant on the C++ side, like this :
#include <QObject>
#include <QList>
#include <QVariant>
class KeyboardContainer : public QObject {
Q_OBJECT
Q_PROPERTY(QVariant enableKey READ enableKey
WRITE setEnableKey
NOTIFY enableKeyChanged)
public:
// Your getter method must match the same return type :
QVariant enableKey() const {
return QVariant::fromValue(m_enableKey);
}
public slots:
// Your setter must put back the data from the QVariant to the QList<int>
void setEnableKey (QVariant arg) {
m_enableKey.clear();
foreach (QVariant item, arg.toList()) {
bool ok = false;
int key = item.toInt(&ok);
if (ok) {
m_enableKey.append(key);
}
}
emit enableKeyChanged ();
}
signals:
// you must have a signal named <property>Changed
void enableKeyChanged();
private:
// the private member can be QList<int> for convenience
QList<int> m_enableKey;
};
On the QML side, simply affect a JS array of Number, the QML engine will automatically convert it to QVariant to make it comprehensible to Qt :
KeyboardContainer.enableKeys = [12,48,26,49,10,3];
That's all !
consider this scenario:
I need to create a ui for some settings. As, data and ui should be separated in theory, I defined a separate class which takes care of the configuration data. The question I have is how to instantiate the data class inside the settings class.
One way is to create the data class in the caller object, I mean the object that calls the settings menu class.
The other way, which my question is involved with, is to create a DATA class variable inside the settings class. I doubt what happens when the settings class gets destroyed then! would the data class object inside settings class also get destroyed? what about if it is defined as a static member of settings class?
#ifndef SETTINGSWINDOW_H
#define SETTINGSWINDOW_H
#include <QMainWindow>
#include <QModelIndex>
#include <QSignalMapper>
#include <QRadioButton>
#include <QSpinBox>
#include <QTimer>
#include "cameracommands.h"
struct Config
{
/* General Options */
QString general_key_lock;
QString general_back_light;
};
//class IConfigSource
//{
//public:
// virtual Config config() const;
// virtual void setConfig(const Config& cfg);
//};
class ConfigSource /* : public IConfigSource*/
{
public:
ConfigSource() {
config_.general_back_light = "OFF";
config_.general_key_lock = "OFF";
}
Config config() const {return config_;}
void setConfig(const Config& cfg) {config_ = cfg;}
private:
Config config_;
};
class ConfigUpdater : public QObject
{
Q_OBJECT
public:
ConfigUpdater(ConfigSource& src) : src_(src) {}
public slots:
void apply () {src_.setConfig(tempConfig_);}
void cancel() {tempConfig_ = src_.config();}
public:
void updateGeneralBackLight(QString s) {tempConfig_.general_back_light = s; qDebug() << "BackLight updated :)";}
void updateGeneralKeyLock(QString s) {tempConfig_.general_key_lock = s; qDebug() << "KeyLock updated :)";}
Config tempConfig_;
ConfigSource& src_;
};
//----------------------------
namespace Ui {
class SettingsWindow;
}
class SettingsWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SettingsWindow(QWidget *parent = 0);
~SettingsWindow();
signals:
void clicked(const QString &text);
void sendToPLC(QByteArray );
public slots:
void updateGeneralBackLight();
void updateGeneralKeyLock();
void getRow(QModelIndex);
void MySlot(QString);
private slots:
void on_pushButton_5_clicked();
void on_pushButton_3_clicked();
private:
void set_mappings();
Ui::SettingsWindow *ui;
ConfigUpdater *config_updater;
QSignalMapper *mapper;
};
#endif // SETTINGSWINDOW_H
and this is the source:
QMainWindow(parent),
ui(new Ui::SettingsWindow)
{
/* initializations */
ui->setupUi(this);
ConfigSource src;
config_updater = new ConfigUpdater(src);
That depends on how do you need to use it.
Scenario 1. The settings need to be held in memory when the program is working.
Scenario 2. The settings need to be saved to the disc immediately, and then will be read on-demand.
In scenario 1, you need to be able to always access the data in the memory. So it is better to separate the settingsUI class and the settingsData class, so you will have access to the latter.
class settingsUI
{
<...>
private:
settingsData * data;//pointer to the data object
}
class settingsData
{
}
In scenario 2, you can aggregate settingsData into the settingsUI, and save the data to a file when the UI is destroyed.
class settingsUI
{
public:
<...>
~settingsUI();
private:
class settingsData
{
<..>
}data;
<...>
}
class settingsUI::~settingsUI()
{
data.saveToFile();
}
And yes, uf you aggregate settings into the UI, it will be destroyed when the UI is destroyed. Holding data as a static member is not the best idea, it is better to separate the data from the visual representation (which is in your case the UI class).
UPD:
If you want to hold it just until the program exits, I would suggest you to hold a static pointer to the data in the UI class. Here is an example with raw pointers, but you can use smart pointers too, ofc.
class data
{
}
class UI
{
private:
static data * data_;
}
data* UI::data_;
When your program starts, allocate memory for the data: UI::data_ = new data(), and when your program ends (or if you don't need the data anymore), free the memory: delete UI::data_. Once again, it is better to use smart pointers, so this is just an example.
If the settings class is used by only the UI then it makes sense to keep it within the UI class:
class Settings {
int a;
int b;
};
class Ui {
private:
Settings settings;
};
settings will be destroyed during the destruction of Ui.
If you are using the Settings object in many places, it makes more sense to keep a shared pointer to it:
class Ui {
public:
Ui(std::shared_ptr<Settings> someSettings)
: settings(someSettings)
{}
private:
std::shared_ptr<Settings> settings;
};
This Settings object will be destroyed when the last owner of the shared_ptr is destroyed.
Yes the data object would get destroyed when the settings object is destroyed. If you make it a static member then it would not. But that's probably not such a good idea. A better way would be to persist the data object to a file (say). You can read the file in the settings object constructor and write the file in the settings object destructor.
EDIT
class SettingsWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SettingsWindow(ConfigSource& src , QWidget *parent = 0);
...
}
SettingsWindow::SettingsWindow(ConfigSource& src , QWidget *parent)
QMainWindow(parent),
ui(new Ui::SettingsWindow)
{
ui->setupUi(this);
config_updater = new ConfigUpdater(src);
...
}
thanks, it is correct. The program terminates when I attempt to pass the below variable to the user-defined function:
(inside header)
void print_config(Config cfg);
Ui::SettingsWindow *ui;
ConfigUpdater *config_updater;
inside cpp:
void SettingsWindow::print_config(Config config)
{
qDebug() << config.general_back_light;
qDebug() << config.general_key_lock;
}
void SettingsWindow::on_sendToMainButton_clicked() /* cancel */
{
print_config(config_updater->tempConfig_);
print_config(config_updater->src_.config());
}
the first, print_config instruciton works fine, as for tempConfig_, but the when I pass src_ in the second statement, it jump outta program.
//------------
I know where the problem comes from, but I cannot solve it, i hope this can help:
class ConfigUpdater : public QObject
{
Q_OBJECT
public:
ConfigUpdater(ConfigSource& src) : src_(src) {}
public slots:
void apply () {src_.setConfig(tempConfig_);}
void cancel() {tempConfig_ = src_.config();}
public:
Config tempConfig_;
ConfigSource& src_;
};
Here, src_ is passed by reference, everywhere, even in the constructor of Settings window.
the program fails when I attemp to access it inside memory, for example:
config_updater->cancel();
which does:
void cancel() {tempConfig_ = src_.config();}