KEMBAR78
Best Practices in Qt Quick/QML - Part 3 | PDF
© Integrated Computer Solutions, Inc. All Rights Reserved
Best Practices in Qt
Quick/QML Part 3
Justin Noel
Senior Consulting Engineer
ICS, Inc.
© Integrated Computer Solutions, Inc. All Rights Reserved
Agenda
• C++ / QML Integration
• Reusing Existing C++ Code
© Integrated Computer Solutions, Inc. All Rights Reserved
Using C++ and QML
© Integrated Computer Solutions, Inc. All Rights Reserved
Drive QML with C++
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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++?
© Integrated Computer Solutions, Inc. All Rights Reserved
Creating Properties in C++
• Properties are the combination of
• Read function
• Write function
• Notify signal
• Signals/slots is Qt’s object communication
system
© Integrated Computer Solutions, Inc. All Rights Reserved
Creating Properties in C++
• Inherit from QObject
• Use the Q_OBJECT macro
• Use the Q_PROPERTY macro
© Integrated Computer Solutions, Inc. All Rights Reserved
C++ Property Header
class CoffeeMaker : public QObject
{
Q_OBJECT
Q_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged)
public:
int getTemp() const;
void setTemp(int temp);
signals:
void tempChanged(); // Parameter is not required by QtQuick
private:
int m_temp;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Source is as usual
int CoffeeMaker ::getTemp() const
{
return m_temp;
}
void CoffeeMaker ::setTemp(int temp)
{
if(m_temp != temp)
{
m_temp = temp;
emit tempChanged();
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Invokable C++ Methods
• Methods can be called from QML
• Any slot can be called
• Any Q_INVOKABLE can be called
© Integrated Computer Solutions, Inc. All Rights Reserved
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++
© Integrated Computer Solutions, Inc. All Rights Reserved
Invokable C++ Functions
class CoffeeMaker : public QObject
{
Q_OBJECT
Q_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
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Exposing Instances
int 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();
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Basic C++ Integration QML
import QtQuick 2.2
Rectangle {
width: 1024
height: 768
Text {
anchors.centerIn: parent
text: “Coffee Temp” + maker.temp
}
MouseArea {
anchors.fill: parent
onClicked: maker.startBrew();
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Complex Properties
• QObject* can be used as a property
• Used for encapsulation and creating trees of
properties
• Properties can have properties!
© Integrated Computer Solutions, Inc. All Rights Reserved
Complex Properties Header
class CoffeeMaker : public QObject
{
Q_OBJECT
Q_PROPERTY(QObject* options READ getOptions CONSTANT)
public:
QObject* getOptions() const { return &m_options; };
Q_INVOKABLE void startBrew();
private:
Options m_options;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Complex Properties Header
class Options: public QObject
{
Q_OBJECT
Q_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged)
public:
int getTemp() const;
void setTemp(int temp);
signals:
void tempChanged();
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Complex Properties QML
import QtQuick 2.2
Rectangle {
width: 1024
height: 768
Text {
anchors.centerIn: parent
text: “Coffee temp” + maker.options.temp
}
MouseArea {
anchors.fill: parent
onClicked: maker.startBrew();
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
Enum Properties Header
class CoffeeMaker: public QObject
{
Q_OBJECT
Q_PROPERTY(Strength strength READ getStrength
WRITE setStrength
NOTIFY strengthChanged)
public:
CoffeeMaker();
enum Strength { Light, Medium, Dark }; Q_ENUM(Strength)
Strength getStrength() const;
void setStrength(Strength newStrength);
signals:
void strengthChanged();
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Enum Properties Source
CoffeeMaker::CoffeeMaker()
{
qmlRegisterUncreatableType<CoffeeMaker>(“MrCoffee”,
1, 0,
“CoffeeMaker”,
“Do not instance.”);
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Enum Properties QML
import QtQuick 2.2
import MrCoffee 1.0 //Needed to get TypeInfo for CoffeeMaker
Rectangle {
width: 1024
height: 768
Text {
anchors.centerIn: parent
text:textFromStrength(maker.strength)//Evaluated as int
}
function textFromStrength(strength) { … }
MouseArea {
anchors.fill: parent
onClicked: maker.startBrew(CoffeeMaker.Strong); //Used by name
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
Existing Header
class CoffeeMaker : public QObject
{
Q_OBJECT
public:
float targetTemp() const;
void setTargetTemp(float taregetTemp);
float temp() const;
signals:
void targetTempChanged(float targetTemp);
void tempChanged(float temp);
private:
… //Members
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Direct Reuse Header
class CoffeeMaker : public QObject
{
Q_OBJECT
Q_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);
...
};
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Reuse Technique
• Class that provides the QObject interface
• Inheritance
• Composition – Easier to test
• Less chance of “rocking the boat”
• More typing. More code
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrappers fix a lot of issues
• 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
© Integrated Computer Solutions, Inc. All Rights Reserved
Model-View-Presenter Pattern
• Wrappers can be an implementation of MVP
• Also called Supervising Controller Pattern
• Provides flexibility between the Model and UI
• Presentation Layer can “reformat” data
• Create strings from multiple model values
• Convert QList<Foo> into an
QAbstractItemModel
© Integrated Computer Solutions, Inc. All Rights Reserved
Model – View Presenter
View
Model
Presenter
Slots
Properties
State
Changed
Get / Set
© Integrated Computer Solutions, Inc. All Rights Reserved
Existing Header
template<class T> class Option
{
public:
T getSetting() const;
void setSetting(T newSetting);
void registerFooCallback(Callback<T>&);
private:
T m_setting;
CallbackCollection<T> m_settingCallbacks;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Header
class DoubleOptionWrapper : public QObject, public
Option<double>
{
Q_OBJECT
Q_PROPERTY(double setting READ getSetting WRITE setSetting
NOTIFY settingChanged)
public:
DoubleOptionWrapper();
signals:
void settingChanged();
private:
void handleSettingCallback(double newSetting);
Callback<double> m_settingCallback;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Source
DoubleOptionWrapper::DoubleOptionWrapper() :
m_settingCallback(this, DoubleOptionWrapper::handleSettingCallback)
{
registerSettingCallback(m_settingCallback);
}
void DoubleOptionWrapper::handleSettingCallback(double newSetting)
{
Q_UNUSED(newSetting)
emit settingChanged();
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Another Wrapper Example
• Issues
• No storage of values in model
• Does not support get function
• Does use QObject and signals
© Integrated Computer Solutions, Inc. All Rights Reserved
Existing Header
class BusData : public QObject
{
Q_OBJECT
public:
void requestSetTargetTemp(double temp);
signals:
void targetTempChanged(double temp);
void error(const QString& errorMessage);
private:
CanBusComm m_canBus;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
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()
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Header
class BusDataBridge : public QObject
{
Q_OBJECT
Q_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;
};
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Source
BusDataBridge::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();
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Wrapper Source
double BusDataBridge::getTargetTemp() const
{
return m_targetTemp;
}
void BusDataBridge::requestSetTargetTemperature(double temp)
{
m_busData.requestSetTargetTemperature(temp);
}
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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)
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
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()
© Integrated Computer Solutions, Inc. All Rights Reserved
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
© Integrated Computer Solutions, Inc. All Rights Reserved
Implicit Sharing
• When copies are not actually copies
• Most data objects in Qt are implicitly shared
• QString, QList, QMap, QVector, QByteArray, etc
• Implemented w/ thread safe ref counting
• Copy constructor and assignment operator
• Copies an internal pointer and increments the
ref count
© Integrated Computer Solutions, Inc. All Rights Reserved
Exposing C++ Types to QML
• Rather than making 1 CoffeeMaker in main
• Allow QML Programmer to create N CoffeMaker
items
• All of the above applies to exposed types
• Instead of using setContextProperty
• Use qmlRegisterType<>()
© Integrated Computer Solutions, Inc. All Rights Reserved
Expose C++ Types
int 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();
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Expose C++ Types QML
import QtQuick 2.2
import MrCoffee 1.0
Rectangle {
CoffeeMaker { id: maker }
Text {
anchors.centerIn: parent
text: “Coffee Temp” + maker.temp
}
MouseArea {
anchors.fill: parent
onClicked: maker.startBrew(CoffeeMaker.Strong);
}
}
© Integrated Computer Solutions, Inc. All Rights Reserved
Thank You!
Justin Noel
Senior Consulting Engineer
ICS, Inc.

Best Practices in Qt Quick/QML - Part 3

  • 1.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Best Practices in Qt Quick/QML Part 3 Justin Noel Senior Consulting Engineer ICS, Inc.
  • 2.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Agenda • C++ / QML Integration • Reusing Existing C++ Code
  • 3.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Using C++ and QML
  • 4.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Drive QML with C++
  • 5.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 6.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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++?
  • 7.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Creating Properties in C++ • Properties are the combination of • Read function • Write function • Notify signal • Signals/slots is Qt’s object communication system
  • 8.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Creating Properties in C++ • Inherit from QObject • Use the Q_OBJECT macro • Use the Q_PROPERTY macro
  • 9.
    © Integrated ComputerSolutions, Inc. All Rights Reserved C++ Property Header class CoffeeMaker : public QObject { Q_OBJECT Q_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged) public: int getTemp() const; void setTemp(int temp); signals: void tempChanged(); // Parameter is not required by QtQuick private: int m_temp; };
  • 10.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Source is as usual int CoffeeMaker ::getTemp() const { return m_temp; } void CoffeeMaker ::setTemp(int temp) { if(m_temp != temp) { m_temp = temp; emit tempChanged(); } }
  • 11.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Invokable C++ Methods • Methods can be called from QML • Any slot can be called • Any Q_INVOKABLE can be called
  • 12.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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++
  • 13.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Invokable C++ Functions class CoffeeMaker : public QObject { Q_OBJECT Q_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 };
  • 14.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Exposing Instances int 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(); }
  • 15.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Basic C++ Integration QML import QtQuick 2.2 Rectangle { width: 1024 height: 768 Text { anchors.centerIn: parent text: “Coffee Temp” + maker.temp } MouseArea { anchors.fill: parent onClicked: maker.startBrew(); } }
  • 16.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Complex Properties • QObject* can be used as a property • Used for encapsulation and creating trees of properties • Properties can have properties!
  • 17.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Complex Properties Header class CoffeeMaker : public QObject { Q_OBJECT Q_PROPERTY(QObject* options READ getOptions CONSTANT) public: QObject* getOptions() const { return &m_options; }; Q_INVOKABLE void startBrew(); private: Options m_options; };
  • 18.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Complex Properties Header class Options: public QObject { Q_OBJECT Q_PROPERTY(int temp READ getTemp WRITE setTemp NOTIFY tempChanged) public: int getTemp() const; void setTemp(int temp); signals: void tempChanged(); };
  • 19.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Complex Properties QML import QtQuick 2.2 Rectangle { width: 1024 height: 768 Text { anchors.centerIn: parent text: “Coffee temp” + maker.options.temp } MouseArea { anchors.fill: parent onClicked: maker.startBrew(); } }
  • 20.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 21.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Enum Properties Header class CoffeeMaker: public QObject { Q_OBJECT Q_PROPERTY(Strength strength READ getStrength WRITE setStrength NOTIFY strengthChanged) public: CoffeeMaker(); enum Strength { Light, Medium, Dark }; Q_ENUM(Strength) Strength getStrength() const; void setStrength(Strength newStrength); signals: void strengthChanged(); };
  • 22.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Enum Properties Source CoffeeMaker::CoffeeMaker() { qmlRegisterUncreatableType<CoffeeMaker>(“MrCoffee”, 1, 0, “CoffeeMaker”, “Do not instance.”); }
  • 23.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Enum Properties QML import QtQuick 2.2 import MrCoffee 1.0 //Needed to get TypeInfo for CoffeeMaker Rectangle { width: 1024 height: 768 Text { anchors.centerIn: parent text:textFromStrength(maker.strength)//Evaluated as int } function textFromStrength(strength) { … } MouseArea { anchors.fill: parent onClicked: maker.startBrew(CoffeeMaker.Strong); //Used by name } }
  • 24.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 25.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 26.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 27.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Existing Header class CoffeeMaker : public QObject { Q_OBJECT public: float targetTemp() const; void setTargetTemp(float taregetTemp); float temp() const; signals: void targetTempChanged(float targetTemp); void tempChanged(float temp); private: … //Members };
  • 28.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Direct Reuse Header class CoffeeMaker : public QObject { Q_OBJECT Q_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); ... };
  • 29.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 30.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 31.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Reuse Technique • Class that provides the QObject interface • Inheritance • Composition – Easier to test • Less chance of “rocking the boat” • More typing. More code
  • 32.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrappers fix a lot of issues • 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
  • 33.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Model-View-Presenter Pattern • Wrappers can be an implementation of MVP • Also called Supervising Controller Pattern • Provides flexibility between the Model and UI • Presentation Layer can “reformat” data • Create strings from multiple model values • Convert QList<Foo> into an QAbstractItemModel
  • 34.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Model – View Presenter View Model Presenter Slots Properties State Changed Get / Set
  • 35.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Existing Header template<class T> class Option { public: T getSetting() const; void setSetting(T newSetting); void registerFooCallback(Callback<T>&); private: T m_setting; CallbackCollection<T> m_settingCallbacks; };
  • 36.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Header class DoubleOptionWrapper : public QObject, public Option<double> { Q_OBJECT Q_PROPERTY(double setting READ getSetting WRITE setSetting NOTIFY settingChanged) public: DoubleOptionWrapper(); signals: void settingChanged(); private: void handleSettingCallback(double newSetting); Callback<double> m_settingCallback; };
  • 37.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Source DoubleOptionWrapper::DoubleOptionWrapper() : m_settingCallback(this, DoubleOptionWrapper::handleSettingCallback) { registerSettingCallback(m_settingCallback); } void DoubleOptionWrapper::handleSettingCallback(double newSetting) { Q_UNUSED(newSetting) emit settingChanged(); }
  • 38.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Another Wrapper Example • Issues • No storage of values in model • Does not support get function • Does use QObject and signals
  • 39.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Existing Header class BusData : public QObject { Q_OBJECT public: void requestSetTargetTemp(double temp); signals: void targetTempChanged(double temp); void error(const QString& errorMessage); private: CanBusComm m_canBus; };
  • 40.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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()
  • 41.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Header class BusDataBridge : public QObject { Q_OBJECT Q_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; };
  • 42.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Source BusDataBridge::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(); } }
  • 43.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Wrapper Source double BusDataBridge::getTargetTemp() const { return m_targetTemp; } void BusDataBridge::requestSetTargetTemperature(double temp) { m_busData.requestSetTargetTemperature(temp); }
  • 44.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 45.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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)
  • 46.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 47.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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()
  • 48.
    © Integrated ComputerSolutions, Inc. All Rights Reserved 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
  • 49.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Implicit Sharing • When copies are not actually copies • Most data objects in Qt are implicitly shared • QString, QList, QMap, QVector, QByteArray, etc • Implemented w/ thread safe ref counting • Copy constructor and assignment operator • Copies an internal pointer and increments the ref count
  • 50.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Exposing C++ Types to QML • Rather than making 1 CoffeeMaker in main • Allow QML Programmer to create N CoffeMaker items • All of the above applies to exposed types • Instead of using setContextProperty • Use qmlRegisterType<>()
  • 51.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Expose C++ Types int 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(); }
  • 52.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Expose C++ Types QML import QtQuick 2.2 import MrCoffee 1.0 Rectangle { CoffeeMaker { id: maker } Text { anchors.centerIn: parent text: “Coffee Temp” + maker.temp } MouseArea { anchors.fill: parent onClicked: maker.startBrew(CoffeeMaker.Strong); } }
  • 53.
    © Integrated ComputerSolutions, Inc. All Rights Reserved Thank You! Justin Noel Senior Consulting Engineer ICS, Inc.