Download - Best Practices in Qt Quick/QML - Part III
![Page 1: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/1.jpg)
Qt Quick Best PracticesPart 3
Justin NoelSenior Consulting Engineer
ICS, Inc.
![Page 2: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/2.jpg)
Agenda• C++ / QML Integration• Reusing Existing C++ Code
![Page 3: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/3.jpg)
Using C++ and QML
![Page 4: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/4.jpg)
Drive QML with C++
![Page 5: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/5.jpg)
Model – View Pattern• C++ code can know nothing about the UI
• Properties, Slots and Signals are the interface in QML• QML Items connect or bind to C++ Objects
• Good Design is Enforced• C++ cannot depend on UI
• Avoids “accidental” storage of data inside UI components
• C++ is more portable to other UI frameworks
![Page 6: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/6.jpg)
C++ Integration Techniques• Expose object instances from C++ to QML
• Objects appear as global variables to QML• Effectively singletons
• Expose C++ types to QML• New types are available for QML programmers
to use• Remember how Rectangle and Text are actually
C++?
![Page 7: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/7.jpg)
Creating Properties in C++• Properties are the combination of
• Read function• Write function• Notify signal
• Signals/slots is Qt’s object communication system
![Page 8: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/8.jpg)
Creating Properties in C++• Inherit from QObject• Use the Q_OBJECT macro• Use the Q_PROPERTY macro
![Page 9: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/9.jpg)
C++ Property Headerclass CoffeeMaker : public QObject{
Q_OBJECTQ_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged)
public:int getTemp() const;void setTemp(int temp);
signals:void tempChanged(); //Using a parameter is not required by QtQuick
};
![Page 10: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/10.jpg)
Source is as usualint CoffeeMaker ::getTemp() const{
return m_temp;}
void CoffeeMaker ::setTemp(int temp){
if(m_temp != temp){
m_temp = temp;emit tempChanged();
}}
![Page 11: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/11.jpg)
Invokable C++ Methods• Methods can be called from QML
• Any slot can be called• Any Q_INVOKABLE can be called
![Page 12: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/12.jpg)
Invokable C++ Return Types• Any basic Qt or C++ type
• int, double, QString, etc• Any returned QObject* belongs to QML
• Will be deleted by QML during GC• NOTE: QObject* returned from a
Q_PROPERTY• Belongs to C++
![Page 13: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/13.jpg)
Invokable C++ Functionsclass CoffeeMaker : public QObject{
Q_OBJECTQ_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged)
public:int getTemp() const;void setTemp(int temp);Q_INVOKABLE void startBrew();
public slots:void stopBrew();
signals:void tempChanged(); //Using a parameter is not required by QtQuick
};
![Page 14: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/14.jpg)
Exposing Instancesint main(int argc, char** argv){
QGuiApplication app(argc, argv);
CoffeeMaker maker;
QQuickView view;view.rootContext()->setContextProperty(“maker”, &maker);view.setSource(Qurl(“qrc:/main.qml”));view.show();
return app.exec();}
![Page 15: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/15.jpg)
Basic C++ Integration QMLimport QtQuick 2.2
Rectangle {width: 1024height: 768
Text {anchors.centerIn: parenttext: “Coffee Temp” + maker.temp
}
MouseArea {anchors.fill: parentonClicked: maker.startBrew();
}}
![Page 16: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/16.jpg)
Complex Proeprties• QObject* can be used as a property
• Used for encapsulation and creating trees of properties• Properties can have properties!
![Page 17: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/17.jpg)
Complex Properties Headerclass CoffeeMaker : public QObject{
Q_OBJECTQ_PROPERTY(QObject* options READ getOptions CONSTANT)
public:QObject* getOptions() const { return &m_options; };Q_INVOKABLE void startBrew();
private:Options m_options;
};
![Page 18: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/18.jpg)
Complex Properties Headerclass Options: public QObject{
Q_OBJECTQ_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged)
public:int getTemp() const;void setTemp(int temp);
signals:void tempChanged();
};
![Page 19: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/19.jpg)
Complex Properties QMLimport QtQuick 2.2
Rectangle {width: 1024height: 768
Text {anchors.centerIn: parenttext: “Coffee temp” + maker.options.temp
}
MouseArea {anchors.fill: parentonClicked: maker.startBrew();
}}
![Page 20: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/20.jpg)
Enum Properties• C++ enums can be used as QML types
• Use Q_ENUMS macro• Use qmlRegisterUncreatableType<>(…)
• Package Name• Major Version• Minor Version• Type Name• Error Message
![Page 21: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/21.jpg)
Enum Properties Headerclass CoffeeMaker: public QObject{
Q_OBJECTQ_ENUMS(Strength)Q_PROPERTY(Strength strength READ getStrength
WRITE setStrengthNOTIFY strengthChanged)
public:CoffeeMaker();enum Strength { Light, Medium, Dark };Strength getStrength() const;void setStrength(Strength newStrength);
signals:void strengthChanged();
};
![Page 22: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/22.jpg)
Enum Properties SourceCoffeeMaker::CoffeeMaker(){
qmlRegisterUncreatableType<CoffeeMaker>(“MrCoffee”,1, 0, “CoffeeMaker”,“Do not instance.”);
}
![Page 23: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/23.jpg)
Enum Properties QMLimport QtQuick 2.2import MrCoffee 1.0 //Needed to get TypeInfo for CoffeeMakerRectangle {
width: 1024height: 768
Text {anchors.centerIn: parenttext:textFromStrength(maker.strength)//Evaluated as int
}
function textFromStrength(strength) { … }
MouseArea {anchors.fill: parentonClicked: maker.startBrew(CoffeeMaker.Strong); //Used by name
}}
![Page 24: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/24.jpg)
Reusing Existing Code• QML requires that properties
• READ functions returns correct value• Could be called anytime
• NOTIFY signal emitted when prop changes• Immediate call to READ returns new changed value
![Page 25: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/25.jpg)
Reuse Techniques• Direct – Add Q_PROPERTY
• Model is already QObject based• Stores its own data
• Wrapper – Write a new QObject class• Model is not (or cannot be) QObject based• Model does not store data
![Page 26: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/26.jpg)
Direct Reuse Technique• Use Q_PROPERTY with existing
• READ function• WRITE function (optional)• NOTIFY signal
• Use Q_INVOKABLE on existing methods• Functions you want callable from the UI
![Page 27: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/27.jpg)
Existing Headerclass CoffeeMaker : public QObject{
Q_OBJECTpublic:
float targetTemp() const;void setTargetTemp(float taregetTemp);
float temp() const;
signals:void targetTempChanged(float targetTemp);void tempChanged(float temp);
private:… //Members
};
![Page 28: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/28.jpg)
Direct Reuse Headerclass CoffeeMaker : public QObject{
Q_OBJECTQ_PROPERTY(float targetTemp READ targetTemp NOTIFY targetTempChanged)Q_PROPERTY(float temp READ temp NOTIFY tempChanged
public:float targetTemp() const;Q_INVOKABLE void setTargetTemp(float taregetTemp);
float temp() const;
signals:void targetTempChanged(float targetTemp);void tempChanged(float temp);...
};
![Page 29: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/29.jpg)
Read Only Properties• Properties can be read-only
• Slots or Q_INVOKABLE functions• Can change state and emit signals
• Sometimes it’s cleaner to have• Read only properties• Q_INVOKABLE setter functions
![Page 30: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/30.jpg)
Direct Reuse Issues• Inherited NOTIFY signals compile error
• NOTIFY signal needs be in the same class as Q_PROPERTY declaration
• Workaround:• Specify new signal in subclass• Use SIGNAL – SIGNAL connection in ctor
![Page 31: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/31.jpg)
Wrapper Reuse Technique• Class that provides the QObject interface
• Inheritance• Composition – Easier to test
• Less chance of “rocking the boat”• More typing. More code
![Page 32: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/32.jpg)
Wrappers fix a lot of issues• Wrappers can work around limitations
• Class is template based• Can’t directly inherit QObject
• Class does not use signals• Uses some other callback mechanism
• Class does not follow get/set/notify pattern• Wrapper can implement local data cache
![Page 33: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/33.jpg)
Model-View-Presenter Pattern• Wrappers can be an implementation of MVP
• Also called Supervising Controller Pattern• Provides flexibility between the Model and U
• Presentation Layer can “reformat” data• Create strings from multiple model values• Convert QList<Foo> into an QAbstractItemModel
![Page 34: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/34.jpg)
Model – View Presenter
View
Model
Presenter Slots
Properties
StateChanged
Get / Set
![Page 35: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/35.jpg)
Existing Headertemplate<class T> class Option{public:
T getSetting() const;void setSetting(T newSetting);
void registerFooCallback(Callback<T>&);
private:T m_setting;CallbackCollection<T> m_settingCallbacks;
};
![Page 36: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/36.jpg)
Wrapper Headerclass DoubleOptionWrapper : public QObject, public Option<double>{
Q_OBJECTQ_PROPERTY(double setting READ getSetting WRITE setSetting
NOTIFY settingChanged)public:
DoubleOptionWrapper();
signals:void settingChanged();
private:void handleSettingCallback(double newSetting);Callback<double> m_settingCallback;
};
![Page 37: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/37.jpg)
Wrapper SourceDoubleOptionWrapper::DoubleOptionWrapper() :
m_settingCallback(this, DoubleOptionWrapper::handleSettingCallback){
registerSettingCallback(m_settingCallback);}
void DoubleOptionWrapper::handleSettingCallback(double newSetting){
Q_UNUSED(newSetting)emit settingChanged();
}
![Page 38: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/38.jpg)
Another Wrapper Example• Issues
• No storage of values in model• Does not support get function
• Does use QObject and signals
![Page 39: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/39.jpg)
Existing Headerclass BusData : public QObject{
Q_OBJECTpublic:
void requestSetTargetTemp(double temp);
signals:void targetTempChanged(double temp);void error(const QString& errorMessage);
private:CanBusComm m_canBus;
};
![Page 40: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/40.jpg)
Existing Code Structure
BusData TempPanel
void BusData::handleCan(){…emit tempChanged(temp);
}
void setTemp(double temp){m_TempLabel->setText(temp);
}
// UI Label is used for storage!// Works, but not good design!
Connect()
![Page 41: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/41.jpg)
Wrapper Headerclass BusDataBridge : public QObject{
Q_OBJECTQ_PROPERTY(double targetTemp READ getTargetTemp NOTIFY targetTempChanged)
public:BusDataBridge(BusData& busData);double getTargetTemp() const;Q_INVOKABLE void requestSetTargetTemp(double temp);
signals:void targetTempChanged();
private slots:void handleTempTargetChanged(double temp);
private:BusData& m_busData;double m_targetTemp;
};
![Page 42: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/42.jpg)
Wrapper SourceBusDataBridge::BusDataBridge(BusData& busData) :
m_busData(busData){
connect(m_busData, SIGNAL(targetTempChanged(double),this, SLOT(handleTargetTempChanged(double));
connect(m_busData, SIGNAL(error(QString)),this, SIGNAL(error(QString)));
}
void BusDataBridge::handleTargetTemperatureChanged(double temp){
if(m_temp != temp){
m_temp = temp;emit targetTemperatureChanged();
}}
![Page 43: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/43.jpg)
Wrapper Sourcedouble BusDataBridge::getTargetTemp() const{
return m_targetTemp;}
void BusDataBridge::requestSetTargetTemperature(double temp){
m_busData.requestSetTargetTemperature(temp);}
![Page 44: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/44.jpg)
Threading Considerations• BusData example can be useful pattern
• If BusData reads data on another thread• Sig/Slot connections work across threads
• Qt will dispatch an async event automatically• Automatic Copy/Lock of data across threads
• Storing data in Bridge object (on GUI thread).• Good! Avoids locking on read
![Page 45: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/45.jpg)
QObject Thread Affinity• QObjects “belong” to a thread
• Default is the thread that created the QObject• QObjects can be assigned another thread via• obj->moveToThread(otherThread)
![Page 46: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/46.jpg)
Cross Thread Signals and Slots• At emit time Qt compares thread ids
• The id of the current thread calling emit signal• The id the receiver belongs to via obj->thread()
• If the threads are the same slots are called• If different an event is packaged/posted
![Page 47: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/47.jpg)
Cross Thread Signal and Slot
BusBridge
void handleCanData(data){
…emit tempChanged(temp);
} void handleTempChanged(temp){
if(m_temp != temp) {m_temp = temp;emit tempChanged();
}}
BusData
Worker Thread Main Thread
Event Loop
postEvent()
![Page 48: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/48.jpg)
Passing Data Across Threads• Signal parameters across threads
• Need to be QVariant Compatible• Default constructor• Assignment Operator• Copy Constructor• Q_DECLARE_METATYPE(Type) at end of Header• qRegisterMetaType<Type>(“Type”); in Source
![Page 49: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/49.jpg)
Implicit Sharing• When copies are not actually copies• Most data objects in Qt are implicitly shared
• QString, QList, QMap, QVector, etc• Implemented w/ thread safe ref counting
• Copy constructor and assignment operator• Copies an internal pointer and increments the ref
count
![Page 50: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/50.jpg)
Exposing C++ Types to QML• Rather than making 1 CoffeeMaker in main
• Allow QML Programmer to create N CoffeMakeritems
• All of the above applies to exposed types• Instead of using setContextProperty• Use qmlRegisterType<>()
![Page 51: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/51.jpg)
Expose C++ Typesint main(int argc, char** argv){
QGuiApplication app(argc, argv);
qmlRegisterType<CoffeeMaker>(“MrCoffee”, 1, 0, “CoffeeMaker”);
QQuickView view;view.setSource(Qurl(“qrc:/main.qml”));view.show();
return app.exec();}
![Page 52: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/52.jpg)
Expose C++ Types QMLimport QtQuick 2.2import MrCoffee 1.0
Rectangle {
CoffeeMaker { id: maker }
Text {anchors.centerIn: parenttext: “Coffee Temp” + maker.temp
}
MouseArea {anchors.fill: parentonClicked: maker.startBrew();
}}
![Page 53: Best Practices in Qt Quick/QML - Part III](https://reader034.vdocuments.us/reader034/viewer/2022050802/55cae65fbb61eb4c788b4864/html5/thumbnails/53.jpg)
Thank You!
Justin NoelSenior Consulting Engineer
ICS, Inc.