Q_PROPERTY not shown - qt

I'm using Q_PROPERTY with QML. My code is:
using namespace std;
typedef QString lyricsDownloaderString; // this may be either std::string or QString
class lyricsDownloader : public QObject
{
Q_OBJECT
public:
Q_PROPERTY(QString lyrics READ lyrics NOTIFY lyricsChanged)
Q_INVOKABLE virtual short perform() = 0;
inline void setData(const string & a, const string & t); // set artist and track
Q_INVOKABLE inline void setData(const QString & a, const QString & t); // for QStrings
Q_INVOKABLE inline bool anyFieldEmpty(); // check whether everything is filled
inline QString lyrics()
{
return lyrics_qstr;
}
/*some more data*/
signals:
void lyricsChanged(QString);
};
class AZLyricsDownloader : public lyricsDownloader
{
Q_OBJECT
public:
AZLyricsDownloader() : lyricsDownloader("", "") {}
AZLyricsDownloader(const string & a, const string & t) : lyricsDownloader(a, t) {}
Q_INVOKABLE short perform();
//Q_INVOKABLE inline void setData(const string & a, const string & t);// set artist and track
protected:
/*some more data*/
};
and one of the Pages in QML is
import QtQuick 1.1
import com.nokia.meego 1.0
import com.nokia.extras 1.0
Page
{
id: showLyricsPage
tools: showLyricsToolbar
Column
{
TextEdit
{
id: lyricsview
anchors.margins: 10
readOnly: true
text: azdownloader.lyrics
}
}
Component.onCompleted:
{
azdownloader.perform()
busyind.visible = false
}
BusyIndicator
{id: busyind /**/ }
ToolBarLayout
{id: showLyricsToolbar/**/}
// Info about disabling/enabling edit mode
InfoBanner {id: editModeChangedBanner /**/}
}
azdownloader is an AZLyricsDownloader object
The codes runs correctly in C++, the function returns the text which should be in TextEdit.
But unfortunately, the TextEdit is blank. No text is shown there.
there is no body for the signal, but AFAIK signal doesn't need it.
If I use
Q_PROPERTY(QString lyrics READ lyrics CONSTANT)
the result is the same.
What am I doing wrong?

When you change the value of the lyrics property in C++ code, you have to send the NOTIFY signal of the property (here void lyricsChanged();) :
this->setProperty("lyrics", myNewValue);
emit lyricsChanged();
In this case, QML should update the value of the property.

Related

Set character format for a QML TextArea using QTextCursor

I'm trying to create a very simple rich text editor component in QML. The requirements are that the user must be able to quickly format individual words inside a textbox separately. So for example, the user could press "Ctrl+B" in the middle of typing and from that point onward the text becomes bold, when the user press "Ctrl+B" again the subsequently typed text has a normal font weight:
So looking around I found that there is an official example provided by the Qt company to do this. So I extrapolated this to a very basic app:
// DocumentHandler.cpp
#pragma once
#include <QObject>
#include <QQuickTextDocument>
#include <QTextCharFormat>
class DocumentHandler : public QObject {
Q_OBJECT
Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
public:
QQuickTextDocument *document() const;
void setDocument(QQuickTextDocument *newDocument);
int cursorPosition() const;
void setCursorPosition(int newCursorPosition);
int selectionStart() const;
void setSelectionStart(int newSelectionStart);
int selectionEnd() const;
void setSelectionEnd(int newSelectionEnd);
bool modified() const;
void setModified(bool newModified);
bool bold() const;
void setBold(bool newBold);
signals:
void documentChanged();
void cursorPositionChanged();
void selectionStartChanged();
void selectionEndChanged();
void modifiedChanged();
void boldChanged();
private:
void mergeFormatOnWordOrSelection(const QTextCharFormat & format);
void reset();
QTextCursor textCursor() const;
QTextDocument* textDocument() const;
QQuickTextDocument * m_document = nullptr;
int m_cursorPosition = -1;
int m_selectionStart = 0;
int m_selectionEnd = 0;
};
// DocumentHandler.cpp
#include "documenthandler.h"
#include <QTextCursor>
QQuickTextDocument *DocumentHandler::document() const
{
return m_document;
}
void DocumentHandler::setDocument(QQuickTextDocument *newDocument)
{
if (m_document == newDocument)
return;
if(m_document)
m_document->textDocument()->disconnect(this);
m_document = newDocument;
if(m_document){
QObject::connect(m_document->textDocument(), &QTextDocument::modificationChanged,
this, &DocumentHandler::modifiedChanged);
}
emit documentChanged();
}
int DocumentHandler::cursorPosition() const
{
return m_cursorPosition;
}
void DocumentHandler::setCursorPosition(int newCursorPosition)
{
if (m_cursorPosition == newCursorPosition)
return;
m_cursorPosition = newCursorPosition;
reset();
emit cursorPositionChanged();
}
int DocumentHandler::selectionStart() const
{
return m_selectionStart;
}
void DocumentHandler::setSelectionStart(int newSelectionStart)
{
if (m_selectionStart == newSelectionStart)
return;
m_selectionStart = newSelectionStart;
emit selectionStartChanged();
}
int DocumentHandler::selectionEnd() const
{
return m_selectionEnd;
}
void DocumentHandler::setSelectionEnd(int newSelectionEnd)
{
if (m_selectionEnd == newSelectionEnd)
return;
m_selectionEnd = newSelectionEnd;
emit selectionEndChanged();
}
bool DocumentHandler::modified() const
{
return m_document && m_document->textDocument()->isModified();
}
void DocumentHandler::setModified(bool newModified)
{
if (m_document)
m_document->textDocument()->setModified(newModified);
}
void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
{
QTextCursor cursor = textCursor();
if (!cursor.hasSelection())
cursor.select(QTextCursor::WordUnderCursor);
cursor.mergeCharFormat(format);
}
void DocumentHandler::reset()
{
emit boldChanged();
}
QTextCursor DocumentHandler::textCursor() const
{
QTextDocument* doc = textDocument();
if(!doc)
return QTextCursor();
QTextCursor cursor = QTextCursor(doc);
if(m_selectionStart != m_selectionEnd){
cursor.setPosition(m_selectionStart);
cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
}
else{
cursor.setPosition(m_cursorPosition);
}
return cursor;
}
QTextDocument *DocumentHandler::textDocument() const
{
return m_document ? m_document->textDocument() : nullptr;
}
bool DocumentHandler::bold() const
{
auto cursor = textCursor();
if(cursor.isNull())
return false;
return cursor.charFormat().fontWeight() == QFont::Bold;
}
void DocumentHandler::setBold(bool newBold)
{
QTextCharFormat format;
format.setFontWeight(newBold ? QFont::Bold : QFont::Normal);
mergeFormatOnWordOrSelection(format);
emit boldChanged();
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import DocumentHandler 1.0
ApplicationWindow {
width: 640
height: 480
visible: true
title: qsTr("Hello World")
color: "lightpink"
header: ToolBar {
id: appToolbar
ToolButton {
text: "Bold"
checkable: true
checked: documentHandler.bold
onClicked: {
documentHandler.bold = !documentHandler.bold
}
}
}
DocumentHandler {
id: documentHandler
document: textInput.textDocument
cursorPosition: textInput.cursorPosition
selectionStart: textInput.selectionStart
selectionEnd: textInput.selectionEnd
}
TextArea {
id: textInput
anchors.fill: parent
font.pixelSize: 30
wrapMode: TextEdit.WrapAtWordBoundaryOrAnywhere
focus: true
selectByMouse: true
persistentSelection: true
textFormat: Qt.RichText
}
}
If you try to run this app, type something like "hello" and the press the "Bold" button the app correctly makes the "hello" bold. However, there is a problem I'm struggling to get past:
if you click on the button when the textarea is empty, then typing doesn't give you bold text. In fact, it seems that it is not possible to change the font weight of the textarea prior to having some text in it. Also, if you type a word, then press space and then click the button: nothing happens.
Anyone got any clue on what's the root cause of this behaviour?

How to properly link C++ model to QML front end

here is my setup:
First is the model class, this stores a pointer to the data of type CompetitionsList. We can see that it implements the necessary basic functions when deriving from QAbstractListModel, and it uses the QML_ELEMENT macro to expose the model to the QML type system:
class CompetitionsListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(CompetitionsList* list READ list WRITE setList)
QML_ELEMENT
public:
explicit CompetitionsListModel(QObject *parent = nullptr);
enum {
NameRole = Qt::ItemDataRole(),
IdRole
};
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
CompetitionsList *list() const;
void setList(CompetitionsList *list);
virtual QHash<int, QByteArray> roleNames() const;
private:
CompetitionsList* m_list; \\ <--- ptr to data
};
Then we have the data class. This class is also a QML_ELEMENT, and it has a userid property. This is used to populate the QVector<listItem> by requesting data from a RESTful http server.
The two signals preItemAppended() and postItemAppended() are emitted when data is added to the QVector<listItem>. These signals are then connected to the Model as a way of notifying the model it must create a new row in the list. The important function here is populateCompetitions, which I explain below.
struct listItem {
QString competitionName;
QString competitionId;
};
class CompetitionsList : public QObject
{
Q_OBJECT
Q_PROPERTY(QString userId READ getUserId WRITE setUserId)
QML_ELEMENT
public:
explicit CompetitionsList(QObject *parent = nullptr);
void populateCompetitions(const QString& userId);
QVector<listItem> items();
QString getUserId() const;
void setUserId(const QString &value);
signals:
void preItemAppended();
void postItemAppended();
void errorPopulatingData();
private:
QVector<listItem> m_competitions;
QString userId;
};
The populateCompetitions function looks as so:
void CompetitionsList::populateCompetitions(const QString &userId)
{
qDebug() << "initialising data";
HttpClient* client = new HttpClient();
client->getUserCompetitions(userId);
connect(client, &HttpClient::getUserCompetitionsResult, [=](const QByteArray& reply){
QJsonDocument _reply = QJsonDocument::fromJson(reply);
if(_reply["data"].isNull()) {
emit errorPopulatingData();
}
else {
const QJsonArray competitions = _reply["data"]["competitions"].toArray();
for(const QJsonValue& competition : competitions) {
emit preItemAppended();
listItem item{competition["competition-name"].toString(), competition["competition-id"].toString()};
m_competitions.append(std::move(item));
emit postItemAppended();
}
}
});
}
It requests data from the database, and then stores it locally.
Then finally, the QML model which ties it all together:
ListView {
id: view
implicitHeight: 1920
implicitWidth: 1080
clip: true
model: CompetitionsListModel {
list: CompetitionsList {
id: compList
}
Component.onCompleted: { compList.userId = "6033f377257e8630ed13299e" } //<-- calls the populateCompetitions function
}
delegate: RowLayout {
Text {
text: qsTr(model.name + ":::" + model.id)
}
}
}
We can see that it has a ListView element, with a model of type CompetitionsListModel and a list of type CompetitionsList. Once the model component is created, we set the userId of the list, this in turn calls the populateCompetitions function which sets up the data for the model to use as seen above.
Unfortunately this doesnt anything when I run the code. Blank screen. Nada. I was wondering if anyone has an insight as to what might be causing this based on the code provided. Ive been at it for so long and it just inst being nice.
I think you need to call qmlRegisterType or UncreatableType on your CompetitionsListModel :
https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterType

How to show on QML (Qt) from SQLite BLOB data as image?

My code is like below.
Name - as TEXT field,
Photo - as BLOB data
class SqlQueryModel: public QSqlQueryModel
{
Q_OBJECT
QHash<int,QByteArray> *hash;
public:
explicit SqlQueryModel(QObject * parent) : QSqlQueryModel(parent)
{
hash = new QHash<int,QByteArray>;
hash->insert(Qt::UserRole, QByteArray("Name"));
hash->insert(Qt::UserRole + 1, QByteArray("Photo"));
}
inline RoleNameHash roleNames() const { return *hash; }
};
Selecting data
view = new QQuickView();
QSqlQueryModel *someSqlModel = new SqlQueryModel(this);
someSqlModel->setQuery("SELECT Name, Photo FROM some_table");
QQmlContext *context = view->rootContext();
context->setContextProperty("someSqlModel", someSqlModel);
view->setSource(QUrl("qrc:///MainView.qml"));
view->show();
Binding in QML
ListView {
id: someListView
model: SqlContactModel {}
delegate: ItemDelegate {
text: Name
Image {
id: Photo
source: ???
}
}
}
How to show on QML (Qt) from SQLite BLOB data as image?
You have three options:
let the model hand out some ID and use that with a QQuickImageProvider
let the model hand out a QImage and write a custom item that can display that
let the model hand out the image data as a data URI
For (2) the simplest solution is a QQuickPaintedItem derived class, something like this
class QImageItem : public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged)
public:
explicit QImageItem(QQuickItem *parent = Q_NULLPTR) : QQuickPaintedItem(parent) {}
QImage image() const { return m_image; }
void setImage(const QImage &image);
void paint(QPainter *painter) Q_DECL_OVERRIDE;
private:
QImage m_image;
};
void QImageItem::setImage(const QImage &image)
{
m_image = image;
emit imageChanged();
update();
setImplicitWidth(m_image.width());
setImplicitHeight(m_image.height());
}
void QImageItem::paint(QPainter *painter)
{
if (m_image.isNull()) return;
painter.drawImage(m_image.scaled(width(), height()));
}
Register as usual with qmlRegisterType<QImageItem>("SomeModuleName", 1, 0, "SomeTypeName") and in QML import SomeModuleName 1.0 and use SomeTypeName in instead of Image, with the QImage returned by the model bound to the item's image property

How to expose property of QObject pointer to QML

I am giving very brief (and partial) description of my class to show my problem. Basically I have setup two properties.
class Fruit : public QObject
{
Q_OBJECT
....
public:
Q_PROPERTY( int price READ getPrice NOTIFY priceChanged)
Q_PROPERTY(Fruit * fruit READ fruit WRITE setFruit NOTIFY fruitChanged)
}
In my QML if I access the price property it works well and good. But if I access the fruit property which obviously returns Fruit and then try to use its price property, that doesn't work. Is this not supposed to work this way?
Text {
id: myText
anchors.centerIn: parent
text: basket.price // shows correctly
//text: basket.fruit.price // doesn't show
}
The 2nd one returns Fruit which also is a property and it has price property but it doesn't seem to access that property? Should this work?
Updated
I am including my source code. I created a new demo with HardwareComponent, it just makes more sense this way. I tried to make it work based on answer I received but no luck.
HardwareComponent class is the base class for Computer and CPU.
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
class HardwareComponent : public QObject
{
Q_OBJECT
public:
HardwareComponent() : m_price(0) {}
virtual int price() = 0;
virtual Q_INVOKABLE void add(HardwareComponent * item) { m_CPU = item; }
HardwareComponent * getCPU() const { return m_CPU; }
Q_SLOT virtual void setPrice(int arg)
{
if (m_price == arg) return;
m_price = arg;
emit priceChanged(arg);
}
Q_SIGNAL void priceChanged(int arg);
protected:
Q_PROPERTY(HardwareComponent * cpu READ getCPU);
Q_PROPERTY(int price READ price WRITE setPrice NOTIFY priceChanged)
HardwareComponent * m_CPU;
int m_price;
};
class Computer : public HardwareComponent
{
Q_OBJECT
public:
Computer() { m_price = 500; }
int price() { return m_price; }
void setprice(int arg) { m_price = arg; }
};
class CPU : public HardwareComponent
{
Q_OBJECT
public:
CPU() { m_price = 100; }
int price() { return m_price; }
void setprice(int arg) { m_price = arg; }
};
int main(int argc, char *argv[])
{
HardwareComponent * computer = new Computer;
CPU * cpu = new CPU;
computer->add( cpu );
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
engine.rootContext()->setContextProperty("computer", computer);
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.3
import QtQuick.Window 2.2
Window {
visible: true
width: 360
height: 360
Text {
anchors.centerIn: parent
text: computer.price // works
//text: computer.cpu.price // doesn't work, doesn't show any value
}
}
This is the complete source code for all my project files.
When I run it I get this output:
Starting C:\Users\User\Documents\My Qt
Projects\build-hardware-Desktop_Qt_5_4_0_MSVC2010_OpenGL_32bit-Debug\debug\hardware.exe...
QML debugging is enabled. Only use this in a safe environment.
qrc:/MainForm.ui.qml:20: ReferenceError: computer is not defined
Even though it gives a warning at line 20 (computer.price) line, it still works and shows the price of computer (=500). If change it computer.cpu.price, the same warning is reported but the price no longer shows - it doesn't seem to work.
The problem is since price is a virtual property, it works! but if I use this property on another hardware component inside the computer component, it doesn't work! The code/answer posted by Mido gives me hope there could be a solution to this, it looks quite close! I hope I can make this work.
There were just a couple things wrong, related specifically to QML:
The context properties must be set before the engine loads the qml file.
You have not registered the HardwareComponent type. See Defining QML Types from C++ for more information.
Apart from the QML side of things, it appears that you wish the hardware to be a composite with a tree structure. Since a QObject is already a composite, you can leverage this to your advantage. The tree of hardware items can be a tree of objects. The QObject notifies the parents when the children are added or removed: it's thus easy to keep the price current as the children are added and removed.
Each component has a unitPrice: this is the price of the component in isolation. The price is a total of the component's unit price, and the prices of its children.
Instead of traversing the entire tree to obtain the total price, you could cache it and only update it when the unit price changes, or the child prices change. Similarly, the current CPU could be cached, you could enforce a single CPU at any given time, etc. The example is functional, but only a minimal sketch.
I've also shown how to change the CPU types in the Computer. There are notifications for both the price changes and cpu changes. The QML UI reacts appropriately when the cpu property gets changed, as well as when any price property changes.
main.cpp
#include <QGuiApplication>
#include <QtQml>
class HardwareComponent : public QObject {
Q_OBJECT
Q_PROPERTY(QString category MEMBER m_category READ category CONSTANT)
Q_PROPERTY(HardwareComponent * cpu READ cpu WRITE setCpu NOTIFY cpuChanged)
Q_PROPERTY(int price READ price NOTIFY priceChanged)
Q_PROPERTY(int unitPrice MEMBER m_unitPrice READ unitPrice WRITE setUnitPrice NOTIFY unitPriceChanged)
QString m_category;
int m_unitPrice;
bool event(QEvent * ev) Q_DECL_OVERRIDE {
if (ev->type() != QEvent::ChildAdded && ev->type() != QEvent::ChildRemoved)
return QObject::event(ev);
auto childEvent = static_cast<QChildEvent*>(ev);
auto child = qobject_cast<HardwareComponent*>(childEvent->child());
if (! child) return QObject::event(ev);
if (childEvent->added())
connect(child, &HardwareComponent::priceChanged,
this, &HardwareComponent::priceChanged, Qt::UniqueConnection);
else
disconnect(child, &HardwareComponent::priceChanged,
this, &HardwareComponent::priceChanged);
emit priceChanged(price());
if (child->category() == "CPU") emit cpuChanged(cpu());
return QObject::event(ev);
}
public:
HardwareComponent(int price, QString category = QString(), QObject * parent = 0) :
QObject(parent), m_category(category), m_unitPrice(price) {}
HardwareComponent * cpu() const {
for (auto child : findChildren<HardwareComponent*>())
if (child->category() == "CPU") return child;
return 0;
}
Q_INVOKABLE void setCpu(HardwareComponent * newCpu) {
Q_ASSERT(!newCpu || newCpu->category() == "CPU");
auto oldCpu = cpu();
if (oldCpu == newCpu) return;
if (oldCpu) oldCpu->setParent(0);
if (newCpu) newCpu->setParent(this);
emit cpuChanged(newCpu);
}
Q_SIGNAL void cpuChanged(HardwareComponent *);
virtual int price() const {
int total = unitPrice();
for (auto child : findChildren<HardwareComponent*>(QString(), Qt::FindDirectChildrenOnly))
total += child->price();
return total;
}
Q_SIGNAL void priceChanged(int);
int unitPrice() const { return m_unitPrice; }
void setUnitPrice(int unitPrice) {
if (m_unitPrice == unitPrice) return;
m_unitPrice = unitPrice;
emit unitPriceChanged(m_unitPrice);
emit priceChanged(this->price());
}
Q_SIGNAL void unitPriceChanged(int);
QString category() const { return m_category; }
};
struct Computer : public HardwareComponent {
Computer() : HardwareComponent(400) {}
};
class FluctuatingPriceComponent : public HardwareComponent {
QTimer m_timer;
int m_basePrice;
public:
FluctuatingPriceComponent(int basePrice, const QString & category = QString(), QObject * parent = 0) :
HardwareComponent(basePrice, category, parent),
m_basePrice(basePrice) {
m_timer.start(250);
connect(&m_timer, &QTimer::timeout, [this]{
setUnitPrice(m_basePrice + qrand()*20.0/RAND_MAX - 10);
});
}
};
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
Computer computer;
HardwareComponent memoryBay(40, "Memory Bay", &computer);
HardwareComponent memoryStick(60, "Memory Stick", &memoryBay);
FluctuatingPriceComponent cpu1(100, "CPU", &computer);
HardwareComponent cpu2(200, "CPU");
qmlRegisterUncreatableType<HardwareComponent>("bar.foo", 1, 0, "HardwareComponent", "");
engine.rootContext()->setContextProperty("computer", &computer);
engine.rootContext()->setContextProperty("cpu1", &cpu1);
engine.rootContext()->setContextProperty("cpu2", &cpu2);
engine.load(QUrl("qrc:/main.qml"));
return app.exec();
}
#include "main.moc"
main.qml
import QtQuick 2.0
import QtQuick.Controls 1.2
import QtQuick.Window 2.0
Window {
visible: true
Column {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
Text {
text: "Computer price: " + computer.price
}
Text {
text: "CPU price: " + (computer.cpu ? computer.cpu.price : "N/A")
}
Button {
text: "Use CPU 1";
onClicked: { computer.setCpu(cpu1) }
}
Button {
text: "Use CPU 2";
onClicked: { computer.setCpu(cpu2) }
}
Button {
text: "Use no CPU";
onClicked: { computer.setCpu(undefined) }
}
}
}
I created a Fruit class like in your example and it's working fine. I created a new instance of Fruit and I can get the price value. Here's my code:
fruit.h
#ifndef FRUIT_H
#define FRUIT_H
#include <QObject>
#include <QQuickView>
class Fruit : public QObject
{
Q_OBJECT
Q_PROPERTY( int price READ getPrice WRITE setPrice NOTIFY priceChanged)
Q_PROPERTY(Fruit * fruit READ fruit WRITE setFruit NOTIFY fruitChanged)
public:
Fruit();
~Fruit();
Fruit *fruit();
void setFruit(Fruit * value);
int getPrice();
void setPrice(int value);
signals:
void fruitChanged();
void priceChanged(int price);
private:
int _price;
Fruit *_fruit;
};
#endif // FRUIT_H
fruit.cpp
#include "fruit.h"
Fruit::Fruit() :
_price(1),_fruit(this)
{
}
Fruit::~Fruit()
{
if(_fruit)
{
delete _fruit;
_fruit = NULL;
}
}
Fruit *Fruit::fruit()
{
//Uncomment this line to test the set
//_fruit = new Fruit;
return _fruit;
}
void Fruit::setFruit(Fruit *value)
{
_fruit = value;
emit fruitChanged();
}
int Fruit::getPrice()
{
return _price;
}
void Fruit::setPrice(int value)
{
_price = value;
emit priceChanged(_price);
}
main.cpp
#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include "fruit.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQuickView view;
view.rootContext()->setContextProperty("basket", new Fruit);
view.setSource(QStringLiteral("qml/main.qml"));
view.show();
return app.exec();
}
main.qml
import QtQuick 2.2
Rectangle {
width:800
height: 480
property var obj
color: "yellow"
Text{
text: basket.fruit.price
font.pixelSize: 20
}
Component.onCompleted: {
basket.price = 20
console.log(basket.fruit.price)
}
}

QML SectionScroller and QList<QObject*>

I use QList<QObject*> as a model in my app. As there might be a lot of elements, I decided to use SectionScroller. When I try to scroll using the SectionScroller I'm getting a
Error: Unable to assign [undefined] to QString
What am I doing wrong?
My ListView is:
ListView
{
id: irrview
width: parent.width
model: irregulars.db // QList<QObject*>
anchors.top: caption.bottom
anchors.bottom: parent.bottom
clip: true
section.criteria: ViewSection.FirstCharacter
section.property: "form0"
section.delegate: Item {height: 10; width: parent.width; Text { text: section } } // for testing purposes
delegate: Rectangle
{
/**/
}
}
Thanks
EDIT: more code:
the irregulars header
class IrregularListWrapper : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> db READ getdb NOTIFY langChanged)
Q_ENUMS(Language)
public:
enum Language
{
English = 0,
German = 1
};
IrregularListWrapper() : db(0) { setLang(German); }
~IrregularListWrapper() { delete db; }
QList<QObject*> getdb() const { return *db; }
Q_INVOKABLE void changeLang(Language l) { delete db; setLang(l); }
signals:
void langChanged();
protected:
void setLang(Language);
QList<QObject*> * db;
};
and the body of a function
void IrregularListWrapper::setLang(Language l)
{
switch (l)
{
case English:
db = new english;
langName = "English";
break;
case German:
db = new german;
langName = "German";
break;
}
emit langChanged();
}
the classes german, english are like that
class german : public QList<QObject*>
{
public:
german();
};
german::german()
{
append(new IrregularVerb("anfangen", "fing an", "angefangen"));
/*more like that*/
}
and IrregularVerb:
class IrregularVerb : public QObject
{
Q_OBJECT
Q_PROPERTY(QString form0 READ getForm0 NOTIFY formChanged)
Q_PROPERTY(QString form1 READ getForm1 NOTIFY formChanged)
Q_PROPERTY(QString form2 READ getForm2 NOTIFY formChanged)
public:
QString forms[3];
QString getForm0() const { return getForm(0); }
QString getForm1() const { return getForm(1); }
QString getForm2() const { return getForm(2); }
IrregularVerb(QString a, QString b, QString c) { forms[0] = a; forms[1] = b; forms[2] = c; }
protected:
const QString& getForm(const int& ind) const { return forms[ind]; }
signals:
void formChanged();
};
Edit 2: this doesn't work
If I do
QVariantList getdb() const { return QVariant::fromValue(*db); }
IrregularListWrapper.h:24: error: could not convert 'QVariant::fromValue(const T&) [with T = QList<QObject*>; QVariant = QVariant]()' from 'QVariant' to 'QVariantList {aka QList<QVariant>}'
If I remove the star, the error is similar.
EDIT3:
I found out this http://ruedigergad.com/2011/08/22/qml-sectionscroller-vs-qabstractlistmodel/
And found out that irregulars.db.get is undefined
And changed german and english to
class german : public AbstractIrregularList
and
class AbstractIrregularList : public QObject, public QList<QObject*>
{
Q_OBJECT
public:
Q_INVOKABLE QObject* get(int index) {return at(index);}
};
But even now, irregulars.db.get(0) gives error (Result of expression 'irregulars.db.get' [undefined] is not a function.)
Why is happening like that, that the Q_INVOKABLE is not detected? The Q_OBJECT macro is there
/edit5: Even when using QVariant the errors are still there. It can be either treated as QList or as QObject*.
If I am not wrong, you should use QVariantList instead QList<SomeClass> to expose list of elements from C++ to QML.
It should solve problem.
Supported types in QML
Try making code to look like this (in irregulars header):
Q_PROPERTY(QVariantList db READ getdb NOTIFY langChanged)
//...
QVariantList getdb() const {/*convert db to QVariantList*/ return converted_db;}
Or if you want to code look like from links you gave:
Q_PROPERTY(QVariantList db READ getdb NOTIFY langChanged)
//...
QVariant getdb() const {return QVariant::fromValue(db);}

Resources