
Wrapping C++ Objects For Property Exposure In QML Charley Bay Beckman Coulter, Inc. Review: C++ From Within QML Integrating QML and C++: (1) Call (Invoke) C++ from QML – Invoke (existing / internal) C++ API for Qt QML or Qt Quick – Apply Q_INVOKABLE to funcs in QObject-derived (2) Implement C++ Types for QML – Derive from QObject – Define “properties” (Q_PROPERTY()) – Register with QML qmlRegisterType() qmlRegisterUncreatableType() qmlRegisterInterface() qmlRegisterSingletonType() What's The Problem? The Issue: "Deep / Rich" Types Are typically: ● Domain-specific ● Have business logic ● Often found in legacy systems ● Expected to be "reusable" within that domain ● Are often "key abstractions" (but not always) ● May be "lightweight" or "heavyweight" Deep / Rich Types: Ligthweight Lightweight: "Popcorn Types" ● Frequently come-and-go (are created-and-destroyed) ● Have non-trivial business logic ● Often imply rules needed throughout the system (e.g., point-of- entry data validation, back-end system adaptation across components/revisions of hardware, etc.) ● Often used to bridge sub-systems ● Often "serialized" to span sessions, setups, configurations ● Often comprise significant portions of the domain-specific APIs for "interface-state" (e.g., are "ubiquitous") These are typically your desired QML "properties"! Deep / Rich Types: Lightweight Examples Example "Lightweight-Deep/Rich" types – class Wavelength – class Voltage – class Parameter – class Detector – class SpectraFilter Deep / Rich Types: Heavyweight Heavyweight: Typically "Key Abstractions" ● Often created at "system-start" with known configuration (e.g., "devices") ● Typically expressed through class hierarchies (e.g., across device revisions, devices specific to a product line evolution) ● Typically consistent across products / product-lines (e.g., "one software to control all devices in a family-of-product-lines) ● May be "plug-&-play" (e.g., subsystems may come-and-go as units during system-run) ● Is often what needs to be monitored / controlled ● Is part of higher-level system (coordination with other key abstractions is typical for data validation, denial of access/operations [e.g., "baton-passing"], etc.) You typically want to "hand-wrap" these for QML! Deep / Rich Types: Heavyweight Examples Example "Heavyweight-Deep/Rich" types – class FluidicsSubsystem – class OpticsSubsystem – class CytometerInstrument – class ExperimentArchive QObject Implies Alternative Control Flow QObject and QMetaObject: ● signals/slots introduce coupling to imply "alternative- control-flow" (only relevant to designs intended for that control flow) ● QObject-derived types should typically be used as "identity" instances (not with value semantics, copy CTOR and operator=() not available) Signals/slots are “side-effect” triggers used to assemble, communicate among, and “stitch-together” across sub-systems. While they can be used within a sub- system, they are often not needed where deterministic behavior is required within (even large) key-abstractions that represent functional business-logic state. Wrapping Deep / Rich Types: QObject C++ types sometimes cannot derive from QObject: ● If cross-platform embedded (to "very-small-footprint-deployment") ● If application-specific performance requirements ● If application-specific design may establish class hierarchies (which disallow QObject) ● If used where Qt is not used ● If "existing/legacy" code rd ● If 3 Party code that cannot be modified ● If developed / tested at unit / functional level with as little coupling as possible An Example: class Wavelength class Wavelength ● Lightweight class { (only data member is double value_nm_; //IS ONLY DATA MEMBER! “double”) public: Wavelength(double value_nm); Wavelength& operator=(double value_nm); ● Has domain-rules Wavelength& operator=(Wavelength& other); – Range-bounds QString getAsLabel(void) const; – Label formatting bool isBlue(void) const; bool isGreen(void) const; – Interpreted as “color” bool isOrange(void) const; bool isRed(void) const; ● Is ubiquitous bool isViolet(void) const; bool isYellow(void) const; throughout domain- specific APIs bool isVisible(void) const; }; const bool switch ( band ) { const bool Wavelength case 0: Wavelength const SdString ::setFromStringParseable( // wavelength_in_nm <= 380 (Invisible, but we need *some* color) ::operator<(const Wavelength& other) const Wavelength const SdString& string, r_0_to_1 = 0.35; { ::getAsString(void) const long& start_index) g_0_to_1 = 0; return isLessThan(other); { const bool { b_0_to_1 = 1; } //FILE: SdString string; Wavelength //------------------------------------------------------------------------ intensity_0_to_1 = 0.3; // appendToString(string); ::hasStateWithWholeNm(void) const // ------------------------------------------------------------------------ break; const bool const bool return string; { //------------------------------------------------------------------------ Wavelength Wavelength #include <limits> // For std::numeric_limits<double> } return hasState() && (!hasStateWithFractionalNm static const SdString()); STRING_NM_ (STR_NM_); case 1: ::IsWavelengthVisible(double wavelength_nanometers) ::operator<=(const Wavelength& other) const #include <algorithm> // For max() } //------------------------------------------------------------------------ // 380 < wavelength_in_nm <={ 420, violet { const bool //------------------------------------------------------------------------ r_0_to_1 = std::max(0.35, - (wavelength_in_nm return (wavelength_nanometers-440)/(440-380)); >= WAVELENGTH_NM_VISIBLE_MIN) return isLessThanOrEqual(other); #include "Wavelength.hpp" Wavelength const bool //------------------------------------------------------------------------ g_0_to_1 = 0; && } ::getAsStringParseable(SdString& string) const Wavelength b_0_to_1 = 1; (wavelength_nanometers <= WAVELENGTH_NM_VISIBLE_MAX); #include "SdString.hpp" { ::hasWavelength(double wavelength_value clear();) const intensity_0_to_1 = 0.3 + 0.7*(wavelength_in_nm} -380)/(420-380); const bool #include "TypeUtil.hpp" string.clear(); { break; Wavelength return appendToStringParseable(string); return TypeUtil::IsValuesEqual long(wavelength_value start_index_temp, wavelength_nanometers = string.findIndexFirstCharNonWhitespace_); (start_index); const bool ::operator!=(const Wavelength& other) const } } if(start_index_temp >= 0) case 2: Wavelength { { // We have something in this string at-or-after the index // 420specified. < wavelength_in_nm <=:: IsWavelengthVisibleBlue440, blue (double wavelength_nanometers) return isNotEqual(other); //------------------------------------------------------------------------ const SdString const bool // r_0_to_1 = -(wavelength_in_nm{ -440)/(440-380); } //------------------------------------------------------------------------ Wavelength Wavelength if(string.isCharDigit(start_index_temp)) g_0_to_1 = 0; return (wavelength_nanometers >= WAVELENGTH_NM_BLUE_MIN) //------------------------------------------------------------------------ ::getAsStringParseable(void) const ::isEqual(const Wavelength& other) { const b_0_to_1 = 1; && const double { { double value; break; (wavelength_nanometers <= WAVELENGTH_NM_BLUE_MAX); Wavelength static const char* STR_NM_ = "nm"; SdString string; // if(this != &other) if(string.parseFloatingStartingAt(value, start_index_temp )) } ::operator+(const Wavelength& other) const appendToStringParseable(string); // { { case 3: { //------------------------------------------------------------------------class return string; returnWavelength wavelength_nanometers _ == other.wavelength_nanometersif(IsOkWavelengthInNanometers_; ((double)value) || (value // 440 == <WAVELENGTH_NM_NONE)) wavelength_in_nm <=const 490, bool blue green As It Really Exists: return operator+(other.wavelength_nanometers _); //------------------------------------------------------------------------ } // } { // We read an acceptable value, or successfully parsedr_0_to_1 a zero.= 0; Wavelength } //------------------------------------------------------------------------ const bool // return true; // RECALL: We MAY have an optional "nm", so if we g_0_to_1 see it, = (wavelength_in_nm::-IsWavelengthVisibleGreen440)/(490-440); (double wavelength_nanometers) Wavelength } // we should skip it. b_0_to_1 = 1; { const double ::getAsStringUserInterface(SdString& string) const if(start_index_temp >= 0) break; return (wavelength_nanometers >= WAVELENGTH_NM_GREEN_MIN) Wavelength { const bool { && ::operator+(double nm_value) const Wavelength::Wavelength(void) string.clear(); Wavelength if((start_index_temp = string.findIndexFirstCharNonWhitespace case 4: (start_index_temp )) >= (0)wavelength_nanometers <= WAVELENGTH_NM_GREEN_MAX); { :wavelength_nanometers_(WAVELENGTH_NM_NONE) return appendToStringUserInterface(string); ::isGreaterThan(const Wavelength& other) const{ // 490 < wavelength_in_nm <=} 510, deep blue green return ((INT32)wavelength_nanometers_ + (INT32)nm_value); { } { if(string.isMatchSubThis(start_index_temp , STRING_NM_, r_0_to_1 = false/*0; case_sensitive */)) } // clear(); // if(this != &other) { g_0_to_1 = 1; const bool } const SdString // { start_index_temp += STRING_NM_.getLength (); b_0_to_1 = -(wavelength_in_nmWavelength-510)/(510 -490); const double Wavelength return wavelength_nanometers _ > other.wavelength_nanometers } _; break; ::IsWavelengthVisibleOrange(double wavelength_nanometers) Wavelength ::getAsStringUserInterface(void) const //FILE: Wavelength.hpp Wavelength::Wavelength(double wavelength_nanometers) // } } { ::operator-(const
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages45 Page
-
File Size-