Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产欧美在线视频免费,国产精彩视频在线,一区二区视频免费观看

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          用Qt進(jìn)行開發(fā)的十個理由

          t的宣傳語:一個框架,一套代碼庫,任意平臺部署。
          Qt開發(fā)平臺下概念太多,新手們可能給一個個名詞搞暈了:Qt, Qt Core, Qt Quick, Qt QML。。。所以整理了一下,給新手們導(dǎo)一下航。

          Qt Developing Platform 開發(fā)平臺

          包括:

          • Qt框架:Qt C++ library, Add-ons, cross-platform適配,代碼庫
          • Qt語言支持:C++, Python, Qt QML, JS
          • 開發(fā)工具:IDE, debug, Profile, Test
          • UI設(shè)計工具: Qt Creator, Qt Design Studio
          • 部署工具:發(fā)布、維護(hù)多個平臺

          支持的平臺

          1. 嵌入式 - 創(chuàng)建高性能的嵌入式系統(tǒng)
          2. 桌面端 - 為桌面應(yīng)用打造令人驚嘆的體驗
          3. 移動端 - 構(gòu)建移動應(yīng)用程序的最強(qiáng)大方式
          4. 微控制器MCU- 極致性能、極小內(nèi)存占用
          5. 更多用例: 汽車、醫(yī)療、物聯(lián)網(wǎng)、消費電子、工業(yè)自動化等

          十大理由

          1. 設(shè)計、開發(fā)出色的用戶體驗

          Qt創(chuàng)造出卓越的用戶體驗將用戶轉(zhuǎn)變?yōu)樗乐曳郏⒊掷m(xù)塑造、提升品牌形象。

          2. Qt能為你節(jié)省開支

          有了Qt, 一個代碼棧和一個開發(fā)團(tuán)隊就能同時支持所有目標(biāo)平臺。

          3. 讓產(chǎn)品更快上市
          Qt為項目簡化每一步工作流程。有了Qt,設(shè)計師和開發(fā)者能無縫協(xié)作,充分利用Qt現(xiàn)成的各類庫在競爭脫穎而出,率先交付產(chǎn)品。

          4. 性能,已達(dá)
          性能也是一種功能。 Qt讓可以您輕松交付。更靈敏的人機(jī)交互、更快的啟動時間、更流暢的動畫和更好的性能,讓您的UX與眾不同。

          5. 快樂的開發(fā)者才是高效的開發(fā)者,無需996
          Qt是開發(fā)者想要的最快、最簡單和最有趣的體驗。

          6. 適用在任何平臺上進(jìn)行開發(fā)
          “一次開發(fā)、任意部署”說的就是Qt。跨平臺開發(fā)不僅能節(jié)省開支,還能推動戰(zhàn)略落地。

          7. 靈活??煽?。就是Qt。
          通過從第三方圖形設(shè)計工具導(dǎo)入UI資產(chǎn)、使用Qt預(yù)制組件、控件或QML來設(shè)計它們。

          8. 一個框架、更少依賴性
          基于不同框架的庫和工具集開發(fā)的應(yīng)用程序不僅很復(fù)雜,而且更不穩(wěn)定 —— 不信,您去問問弗蘭肯斯坦博士。

          9. 支持多種語言
          Qt支持不同的開發(fā)語言。C++高效、強(qiáng)大、通用。Qt的聲明式UI語言QML使得以快速創(chuàng)建UI變得容易。你甚至可以用Python來開發(fā)!

          10. 開源且永不過時
          您是否知道一個開發(fā)框架的生態(tài)系統(tǒng)可以為您項目的未來保駕護(hù)航?Qt擁有超過百萬的專業(yè)用戶群體。因為根植開源,社區(qū)的貢獻(xiàn)讓Qt蓬勃發(fā)展。

          Qt網(wǎng)站上看起來好像吹得天花亂墜,但確實有它的過人之處。
          現(xiàn)在跨平臺開發(fā)里Flutter、Electron火熱,是因為上手簡單,前端HTML5的資源最多,相應(yīng)開發(fā)者也人數(shù)眾多(迭代也非常快)。但離絲滑使用體驗還是有一差距的。
          所以有空學(xué)習(xí)一下Python, C++,結(jié)合Qt,還是可以吃香很久的。

          、Qt元對象系統(tǒng)簡介

          1、元對象系統(tǒng)簡介

          Qt的信號槽和屬性系統(tǒng)基于在運行時進(jìn)行內(nèi)省的能力,所謂內(nèi)省是指面向?qū)ο笳Z言的一種在運行期間查詢對象信息的能力, 比如如果語言具有運行期間檢查對象型別的能力,那么是型別內(nèi)?。╰ype intropection)的,型別內(nèi)省可以用來實施多態(tài)。

          'C++'的內(nèi)省比較有限,僅支持型別內(nèi)省, 'C++'的型別內(nèi)省是通過運行時類型識別(RTTI)(Run-Time Type Information)中的typeid 以及 dynamic_cast關(guān)鍵字來實現(xiàn)的。

          Qt拓展了’C++'的內(nèi)省機(jī)制,但并沒有采用’C++'的RTTI,而是提供了更為強(qiáng)大的元對象(meta object)機(jī)制,來實現(xiàn)內(nèi)省機(jī)制?;趦?nèi)省機(jī)制,可以列出對象的方法和屬性列表,并且能夠獲取有關(guān)對象的所有信息,如參數(shù)類型。如果沒有內(nèi)省機(jī)制,QtScript和 QML是難以實現(xiàn)的。

          Qt中的元對象系統(tǒng)全稱Meta Object System,是一個基于標(biāo)準(zhǔn)’C++'的擴(kuò)展,為Qt提供了信號與槽機(jī)制、實時類型信息、動態(tài)屬性系統(tǒng)。元對象系統(tǒng)基于QObject類、Q_OBJECT宏、元對象編譯器MOC實現(xiàn)。

          A、QObject 類

          作為每一個需要利用元對象系統(tǒng)的類的基類。

          B、Q_OBJECT宏

          定義在每一個類的私有數(shù)據(jù)段,用來啟用元對象功能,比如動態(tài)屬性、信號和槽。

          在一個QObject類或者其派生類中,如果沒有聲明Q_OBJECT宏,那么類的metaobject對象不會被生成,類實例調(diào)用metaObject()返回的就是其父類的metaobject對象,導(dǎo)致的后果是從類的實例獲得的元數(shù)據(jù)其實都是父類的數(shù)據(jù)。因此類所定義和聲明的信號和槽都不能使用,所以,任何從QObject繼承出來的類,無論是否定義聲明了信號、槽和屬性,都應(yīng)該聲明Q_OBJECT 宏。

          C、元對象編譯器MOC (Meta Object Complier),

          MOC分析C++源文件,如果發(fā)現(xiàn)在一個頭文件(header file)中包含Q_OBJECT 宏定義,會動態(tài)的生成一個moc_xxxx命名的C++源文件,源文件包含Q_OBJECT的實現(xiàn)代碼,會被編譯、鏈接到類的二進(jìn)制代碼中,作為類的完整的一部分。

          2、元對象系統(tǒng)的功能

          元對象系統(tǒng)除了提供信號槽機(jī)制在對象間進(jìn)行通訊的功能,還提供了如下功能:

          QObject::metaObject() 方法

          獲得與一個類相關(guān)聯(lián)的 meta-object

          QMetaObject::className() 方法

          在運行期間返回一個對象的類名,不需要本地’C++'編譯器的RTTI(run-time type information)支持

          QObject::inherits() 方法

          用來判斷生成一個對象類是不是從一個特定的類繼承出來,必須是在QObject類的直接或者間接派生類當(dāng)中。

          QObject::tr() and QObject::trUtf8()

          為軟件的國際化翻譯字符串

          QObject::setProperty() and QObject::property()

          根據(jù)屬性名動態(tài)的設(shè)置和獲取屬性值

          使用qobject_cast()方法在QObject類之間提供動態(tài)轉(zhuǎn)換,qobject_cast()方法的功能類似于標(biāo)準(zhǔn)C++的dynamic_cast(),但qobject_cast()不需要RTTI的支持。

          3、Q_PROPERTY()的使用

          #define Q_PROPERTY(text)

          Q_PROPERTY定義在/src/corelib/kernel/Qobjectdefs.h文件中,用于被MOC處理。

          Q_PROPERTY(type name
                      READ getFunction
                      [WRITE setFunction]
                      [RESET resetFunction]
                      [NOTIFY notifySignal]
                      [REVISION int]
                      [DESIGNABLE bool]
                      [SCRIPTABLE bool]
                      [STORED bool]
                      [USER bool]
                      [CONSTANT]
                      [FINAL])

          Type:屬性的類型

          Name:屬性的名稱

          READ getFunction:屬性的訪問函數(shù)

          WRITE setFunction:屬性的設(shè)置函數(shù)

          RESET resetFunction:屬性的復(fù)位函數(shù)

          NOTIFY notifySignal:屬性發(fā)生變化的地方發(fā)射的notifySignal信號

          REVISION int:屬性的版本,屬性暴露到QML中

          DESIGNABLE bool:屬性在GUI設(shè)計器中是否可見,默認(rèn)為true

          SCRIPTABLE bool:屬性是否可以被腳本引擎訪問,默認(rèn)為true

          STORED bool:

          USER bool:

          CONSTANT:標(biāo)識屬性的值是常量,值為常量的屬性沒有WRITE、NOTIFY

          FINAL:標(biāo)識屬性不會被派生類覆寫

          注意:NOTIFY notifySignal聲明了屬性發(fā)生變化時發(fā)射notifySignal信號,但并沒有實現(xiàn),因此程序員需要在屬性發(fā)生變化的地方發(fā)射notifySignal信號。

          Object.h:

          #ifndef OBJECT_H
          #define OBJECT_H
          
          #include <QObject>
          #include <QString>
          #include <QDebug>
          
          class Object : public QObject
          {
              Q_OBJECT
              Q_PROPERTY(int age READ age  WRITE setAge NOTIFY ageChanged)
              Q_PROPERTY(int score READ score  WRITE setScore NOTIFY scoreChanged)
              Q_CLASSINFO("Author", "Scorpio")
              Q_CLASSINFO("Version", "1.0")
              Q_ENUMS(Level)
          protected:
              QString m_name;
              QString m_level;
              int m_age;
              int m_score;
          public:
              enum Level
              {
                  Basic,
                  Middle,
                  Advanced
              };
          public:
              explicit Object(QString name, QObject *parent = 0):QObject(parent)
              {
                  m_name = name;
                  setObjectName(m_name);
                  connect(this, SIGNAL(ageChanged(int)), this, SLOT(onAgeChanged(int)));
                  connect(this, SIGNAL(scoreChanged(int)), this, SLOT(onScoreChanged(int)));
              }
          
              int age()const
              {
                  return m_age;
              }
          
              void setAge(const int& age)
              {
                  m_age = age;
                  emit ageChanged(m_age);
              }
          
              int score()const
              {
                  return m_score;
              }
          
              void setScore(const int& score)
              {
                  m_score = score;
                  emit scoreChanged(m_score);
              }
          signals:
              void ageChanged(int age);
              void scoreChanged(int score);
          public slots:
          
               void onAgeChanged(int age)
               {
                   qDebug() << "age changed:" << age;
               }
               void onScoreChanged(int score)
               {
                   qDebug() << "score changed:" << score;
               }
          };
          
          #endif // OBJECT_H

          Main.cpp:

          #include <QCoreApplication>
          #include "Object.h"
          
          int main(int argc, char *argv[])
          {
              QCoreApplication a(argc, argv);
              Object ob("object");
          
              //設(shè)置屬性age
              ob.setProperty("age", QVariant(30));
              qDebug() << "age: " << ob.age();
              qDebug() << "property age: " << ob.property("age").toInt();
          
              //設(shè)置屬性score
              ob.setProperty("score", QVariant(90));
              qDebug() << "score: " << ob.score();
              qDebug() << "property score: " << ob.property("score").toInt();
          
              //內(nèi)省intropection,運行時查詢對象信息
              qDebug() << "object name: " << ob.objectName();
              qDebug() << "class name: " << ob.metaObject()->className();
              qDebug() << "isWidgetType: " << ob.isWidgetType();
              qDebug() << "inherit: " << ob.inherits("QObject");
          
              return a.exec();
          }

          4、Q_INVOKABLE使用

          #define Q_INVOKABLE

          Q_INVOKABLE定義在/src/corelib/kernel/Qobjectdefs.h文件中,用于被MOC識別。

          Q_INVOKABLE宏用于定義一個成員函數(shù)可以被元對象系統(tǒng)調(diào)用,Q_INVOKABLE宏必須寫在函數(shù)的返回類型之前。如下:

          【領(lǐng)QT開發(fā)教程學(xué)習(xí)資料,點擊下方鏈接莬費領(lǐng)取↓↓,先碼住不迷路~】

          點擊→領(lǐng)取「鏈接」

          Q_INVOKABLE void invokableMethod();

          invokableMethod()函數(shù)使用了Q_INVOKABLE宏聲明,invokableMethod()函數(shù)會被注冊到元對象系統(tǒng)中,可以使用 QMetaObject::invokeMethod()調(diào)用。

          Q_INVOKABLE與QMetaObject::invokeMethod均由元對象系統(tǒng)喚起,在Qt C++/QML混合編程、跨線程編程、Qt Service Framework以及 Qt/ HTML5混合編程以及里廣泛使用。

          A、在跨線程編程中的使用

          如何調(diào)用駐足在其他線程里的QObject方法呢?Qt提供了一種非常友好而且干凈的解決方案:向事件隊列post一個事件,事件的處理將以調(diào)用所感興趣的方法為主(需要線程有一個正在運行的事件循環(huán))。而觸發(fā)機(jī)制的實現(xiàn)是由MOC提供的內(nèi)省方法實現(xiàn)的。因此,只有信號、槽以及被標(biāo)記成Q_INVOKABLE的方法才能夠被其它線程所觸發(fā)調(diào)用。如果不想通過跨線程的信號、槽這一方法來實現(xiàn)調(diào)用駐足在其他線程里的QObject方法。另一選擇就是將方法聲明為Q_INVOKABLE,并且在另一線程中用invokeMethod喚起。

          B、Qt Service Framework

          Qt服務(wù)框架是Qt Mobility 1.0.2版本推出的,一個服務(wù)(service)是一個獨立的組件提供給客戶端(client)定義好的操作。客戶端可以通過服務(wù)的名稱,版本號和服務(wù)的對象提供的接口來查詢服務(wù)。 查找到服務(wù)后,框架啟動服務(wù)并返回一個指針。

          服務(wù)通過插件(plug-ins)來實現(xiàn)。為了避免客戶端依賴某個具體的庫,服務(wù)必須繼承自QObject,保證QMetaObject?系統(tǒng)可以用來提供動態(tài)發(fā)現(xiàn)和喚醒服務(wù)的能力。要使QmetaObject機(jī)制充分的工作,服務(wù)必須滿足,其所有的方法都是通過 signal、slot、property或invokable method和Q_INVOKEBLE來實現(xiàn)。

          QServiceManager manager;
          QObject *storage ;  
          storage = manager.loadInterface("com.nokia.qt.examples.FileStorage"); 
          if(storage)     
              QMetaObject::invokeMethod(storage, "deleteFile", Q_ARG(QString, "/tmp/readme.txt")); 

          上述代碼通過service的元對象提供的invokeMethod方法,調(diào)用文件存儲對象的deleteFile() 方法??蛻舳瞬恍枰缹ο蟮念愋?,因此也沒有鏈接到具體的service庫。 當(dāng)然在服務(wù)端的deleteFile方法,一定要被標(biāo)記為Q_INVOKEBLE,才能夠被元對象系統(tǒng)識別。

          Qt服務(wù)框架的一個亮點是它支持跨進(jìn)程通信,服務(wù)可以接受遠(yuǎn)程進(jìn)程。在服務(wù)管理器上注冊后,進(jìn)程通過signal、slot、invokable method和property來通信,就像本地對象一樣。服務(wù)可以設(shè)定為在客戶端間共享,或針對一個客戶端。 在Qt服務(wù)框架推出之前,信號、槽以及invokable method僅支持跨線程。 下圖是跨進(jìn)程的服務(wù)/客戶段通信示意圖。invokable method和Q_INVOKEBLE 是跨進(jìn)城、跨線程對象之間通信的重要利器。

          二、Qt元對象系統(tǒng)源碼解析

          1、Q_OBJECT宏的定義

          任何從QObject派生的類都包含自己的元數(shù)據(jù)模型,一般通過宏Q_OBJECT定義。

          Q_OBJECT定義在/src/corelib/kernel/Qobjectdefs.h文件中。

          #define Q_OBJECT \
          public: \
              Q_OBJECT_CHECK \
              static const QMetaObject staticMetaObject; \
              Q_OBJECT_GETSTATICMETAOBJECT \
              virtual const QMetaObject *metaObject() const; \
              virtual void *qt_metacast(const char *); \
              QT_TR_FUNCTIONS \
              virtual int qt_metacall(QMetaObject::Call, int, void **); \
          private: \
              Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
              Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);

          QMetaObject類型的靜態(tài)成員變量staticMetaObject是元數(shù)據(jù)的數(shù)據(jù)結(jié)構(gòu)。metaObject,qt_metacast,qt_metacall、qt_static_metacall四個虛函數(shù)由MOC在生成的moc_xxx.cpp文件中實現(xiàn)。metaObject的作用是得到元數(shù)據(jù)表指針;qt_metacast的作用是根據(jù)簽名得到相關(guān)結(jié)構(gòu)的指針,返回void*指針;qt_metacall的作用是查表然后調(diào)用調(diào)用相關(guān)的函數(shù);qt_static_metacall的作用是調(diào)用元方法(信號和槽)。

          #define Q_DECL_HIDDEN __attribute__((visibility("hidden")))

          2、QMetaObject類型

          QMetaObject類定義在/src/corelib/kernel/Qobjectdefs.h文件。

          struct Q_CORE_EXPORT QMetaObject
          {
            ...
          enum Call {
              InvokeMetaMethod,
              ReadProperty,
              WriteProperty,
              ResetProperty,
              QueryPropertyDesignable,
              QueryPropertyScriptable,
              QueryPropertyStored,
              QueryPropertyEditable,
              QueryPropertyUser,
              CreateInstance
          };
          
             int static_metacall(Call, int, void **) const;
             static int metacall(QObject *, Call, int, void **);
            struct { // private data
              const QMetaObject *superdata;
              const char *stringdata;
              const uint *data;
              const void *extradata;
            } d;
          };

          QMetaObject中有一個嵌套結(jié)構(gòu)封裝了所有的數(shù)據(jù):

          const QMetaObject *superdata;//元數(shù)據(jù)代表的類的基類的元數(shù)據(jù)

          const char *stringdata;//元數(shù)據(jù)的簽名標(biāo)記

          const uint *data;//元數(shù)據(jù)的索引數(shù)組的指針

          const QMetaObject **extradata;//擴(kuò)展元數(shù)據(jù)表的指針,指向QMetaObjectExtraData數(shù)據(jù)結(jié)構(gòu)。

          struct QMetaObjectExtraData
          {
          #ifdef Q_NO_DATA_RELOCATION
              const QMetaObjectAccessor *objects;
          #else
              const QMetaObject **objects;
          #endif
          
              typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); //from revision 6
              //typedef int (*StaticMetaCall)(QMetaObject::Call, int, void **); //used from revison 2 until revison 5
              StaticMetacallFunction static_metacall;
          };

          static_metacall是一個指向Object::qt_static_metacall 的函數(shù)指針。

          3、QT_TR_FUNCTIONS宏定義

          宏QT_TR_FUNCTIONS是和翻譯相關(guān)的。

          #define QT_TR_FUNCTIONS \
            static inline QString tr(const char *s, const char *c = 0) \
            { return staticMetaObject.tr(s, c); } \
          #endif

          4、Qt中其它宏的定義

          Qt在/src/corelib/kernel/Qobjectdefs.h文件中定義了大量的宏。

          #ifndef Q_MOC_RUN
          # if defined(QT_NO_KEYWORDS)
          #  define QT_NO_EMIT
          # else
          #   define slots
          #   define signals protected
          # endif
          # define Q_SLOTS
          # define Q_SIGNALS protected
          # define Q_PRIVATE_SLOT(d, signature)
          # define Q_EMIT
          #ifndef QT_NO_EMIT
          # define emit
          #endif
          #define Q_CLASSINFO(name, value)
          #define Q_INTERFACES(x)
          #define Q_PROPERTY(text)
          #define Q_PRIVATE_PROPERTY(d, text)
          #define Q_REVISION(v)
          #define Q_OVERRIDE(text)
          #define Q_ENUMS(x)
          #define Q_FLAGS(x)
          #define Q_SCRIPTABLE
          #define Q_INVOKABLE
          #define Q_SIGNAL
          #define Q_SLOT

          Qt中的大部分宏都無實際的定義,都是提供給MOC識別處理的,MOC工具通過對類中宏的解析處理生成moc_xxx.cpp文件。

          在 Qt4 及之前的版本中,signals被展開成protected。Qt5則變成public,用以支持新的語法。

          三、元對象編譯器MOC

          1、MOC功能

          A、處理Q_OBJECT宏和signals/slots關(guān)鍵字,生成信號和槽的底層代碼

          B、處理Q_PROPERTY()和Q_ENUM()生成property系統(tǒng)代碼

          C、處理Q_FLAGS()和Q_CLASSINFO()生成額外的類meta信息

          D、不需要MOC處理的代碼可以用預(yù)定義的宏括起來,如下:

          #ifndef Q_MOC_RUN
          …
          #endif

          2、MOC限制

          A、模板類不能使用信號/槽機(jī)制

          B、MOC不擴(kuò)展宏,所以信號和槽的定義不能使用宏, 包括connect的時候也不能用宏做信號和槽的名字以及參數(shù)

          C、從多個類派生時,QObject派生類必須放在第一個。 QObject(或其子類)作為多重繼承的父類之一時,需要把它放在第一個。 如果使用多重繼承,moc在處理時假設(shè)首先繼承的類是QObject的一個子類,需要確保首先繼承的類是QObject或其子類。

          D、函數(shù)指針不能作為信號或槽的參數(shù), 因為其格式比較復(fù)雜,MOC不能處理。可以用typedef把它定義成簡單的形式再使用。

          E、用枚舉類型或typedef的類型做信號和槽的參數(shù)時,必須fully qualified。這個詞中文不知道怎么翻譯才合適,簡單的說就是, 如果是在類里定義的, 必須把類的路徑或者命名空間的路徑都加上, 防止出現(xiàn)混淆。如Qt::Alignment之類的,前面的Qt就是Alignment的qualifier, 必須加上,而且有幾級加幾級。

          F、信號和槽不能返回引用類型

          G、signals和slots關(guān)鍵字區(qū)域只能放置信號和槽的定義,不能放其它的如變量、構(gòu)造函數(shù)的定義等,友元聲明不能位于信號或者槽聲明區(qū)內(nèi)。

          H、嵌套類不能含有信號和槽

          MOC無法處理嵌套類中的信號和槽,錯誤的例子:

          class A:public QObject
          {
              Q_OBJECT
          public:
              class B
              {
              public slots://錯誤用法
              };
          };

          I、信號槽不能有缺省參數(shù)

          3、自定義類型的注冊

          Qt線程間傳遞自定義類型數(shù)據(jù)時,自己定義的類型如果直接使用信號槽來傳遞的話會產(chǎn)生下面這種錯誤:

          原因:當(dāng)一個signal被放到隊列中(queued)時,參數(shù)(arguments)也會被一起一起放到隊列中,參數(shù)在被傳送到slot之前需要被拷貝、存儲在隊列中;為了能夠在隊列中存儲參數(shù)(argument),Qt需要去construct、destruct、copy參數(shù)對象,而為了讓Qt知道怎樣去作這些事情,參數(shù)的類型需要使用qRegisterMetaType來注冊。

          步驟:(以自定義XXXXX類型為例)

          A、自定義類型時在類的頂部包含:#include <QMetaType>

          B、在類型定義完成后,加入聲明:Q_DECLARE_METATYPE(XXXXX);

          C、在main()函數(shù)中注冊自定義類類型:qRegisterMetaType<XXXXX>(“XXXXX”);

          如果希望使用類型的引用,同樣要注冊:qRegisterMetaType<XXXXX>(“XXXXX&”);

          4、MOC的使用

          查看工程的Makefile文件可以查找到MOC生成moc_xxx.cpp文件的命令:

          moc_Object.cpp: ../moc/Object.h
          	/usr/local/Trolltech/Qt-4.8.6/bin/moc $(DEFINES) $(INCPATH) ../moc/Object.h -o moc_Object.cpp

          因此命令行可以簡化為:

          moc Object.h -o moc_Object.cpp

          滿足換流站監(jiān)控系統(tǒng)安防要求,許繼電氣股份有限公司的研究人員張浩然、趙冠華、申艷紅、靳瑋瑋、張睿,在2020年第9期《電氣技術(shù)》雜志上撰文,提出了基于Qt框架中的QtWebEngine模塊,利用Web技術(shù)開發(fā)可在Linux上運行的C/S報表組件的技術(shù)方案。文中首先介紹了報表文件的格式定義,然后介紹了設(shè)計器和查看器的設(shè)計思路,并對其中關(guān)鍵的交互流程及實現(xiàn)方法進(jìn)行了說明。

          出于國家安全考慮,現(xiàn)在換流站監(jiān)控系統(tǒng)要求運行在Linux系統(tǒng)上?,F(xiàn)有的客戶端/服務(wù)器(client/server, C/S)類專用報表組件如水晶報表等都是運行在Windows系統(tǒng)上的,在Linux上無法使用。在Linux上雖然有Open Office等辦公套件,但是由于Linux上沒有類似Windows上的對象連接與嵌入技術(shù)(object linking and embedding, OLE)的對象嵌入機(jī)制,所以也無法利用這些辦公套件開發(fā)報表。

          雖然現(xiàn)有的瀏覽器/服務(wù)器(browser/server, B/S)類報表組件可以運行在Linux系統(tǒng)上,但是使用B/S類報表,需要部署Web服務(wù)器。而變電站監(jiān)控系統(tǒng)屬于一區(qū)系統(tǒng),按照二次安防要求,不能采用Web服務(wù)。因此,無法使用B/S類報表組件。故此,開發(fā)可以在Linux系統(tǒng)使用的C/S報表組件,勢在必行。

          本文提供了基于QtWebEngine模塊,利用Web技術(shù)的報表組件開發(fā)方案。此方案既利用了Web頁面的強(qiáng)大展示能力,又通過QtWebEngine與Web頁面的交互能力避開了搭建Web服務(wù)器的需要,從而構(gòu)建了一套強(qiáng)大靈活的C/S報表組件。以下對此技術(shù)方案的技術(shù)環(huán)節(jié)進(jìn)行闡述。

          1 背景技術(shù)介紹

          QtWebEngine是Qt框架中的一個瀏覽器模塊,它提供了易于使用且可擴(kuò)展的應(yīng)用程序接口(appli- cation programming interface, API)。利用QtWebEngine可以很容易地把Web內(nèi)容嵌入到Qt應(yīng)用程序中。QtWebEngine不允許C++/Qt代碼直接操作頁面元素。然而,QtWebEngine提供了RunJavaScript方法??梢酝ㄟ^該方法調(diào)用Web頁面的JavaScript腳本,并獲取執(zhí)行結(jié)果,從而獲取和調(diào)整頁面內(nèi)容。

          QtWebEngine支持Web技術(shù)中最新的超文本標(biāo)記語言(hyper text markup language, HTML)第五版標(biāo)準(zhǔn)。HTML5提供了一套拖放接口,使Web應(yīng)用能夠支持拖放功能。通過這些功能,用戶可以使用鼠標(biāo)選擇可拖動元素,并將元素拖動到可放置容器,通過釋放鼠標(biāo)按鈕來放置這些元素。

          開發(fā)者可以自定義能夠成為可拖拽的元素類型、可拖拽元素產(chǎn)生的反饋,以及可放置的容器元素?;贖TML5的拖放接口,可以實現(xiàn)報表的可視化設(shè)計。此外,Web技術(shù)中的高性能數(shù)據(jù)圖表和數(shù)據(jù)表格也是報表開發(fā)中必需的功能。

          2 模板文件設(shè)計

          報表模板文件的本質(zhì)是一個基于JavaScript對象表示法(Java script object notation, JSON)格式的文本文件,其內(nèi)部保存了數(shù)據(jù)源和界面部件信息兩部分?jǐn)?shù)據(jù)。

          2.1 數(shù)據(jù)源

          數(shù)據(jù)源保存的并非真正在報表中使用的數(shù)據(jù),而是報表要使用的數(shù)據(jù)點的編號和名稱信息。當(dāng)用戶查看報表時,查看器會根據(jù)數(shù)據(jù)源中的數(shù)據(jù)點編號和用戶選擇的時間段,動態(tài)生成一個以數(shù)據(jù)點編號為列,以時間點為行的表格。這才是報表要使用的數(shù)據(jù)。數(shù)據(jù)源在報表模板中以JSON數(shù)組形式存儲,如圖1所示。

          圖1 數(shù)據(jù)源JSON結(jié)構(gòu)

          每個數(shù)組元素是一個JSON對象,對象內(nèi)部由兩個鍵值對組成,id代表數(shù)據(jù)點的編號,name代表數(shù)據(jù)點名稱。

          2.2 界面部件信息

          報表界面部件信息包含了報表界面部件的布局關(guān)系和每個部件自身的屬性設(shè)置。界面部件的布局關(guān)系可以用一個樹形的結(jié)構(gòu)表示,如圖2所示。

          整個報表作為根節(jié)點;在報表頂層的部件為第一級子節(jié)點;其中的容器類型節(jié)點可以擁有自己的子節(jié)點,容器類型節(jié)點可以嵌套。報表組件在展示報表時可以遞歸遍歷這個樹形結(jié)構(gòu)來創(chuàng)建組件對象。

          這個樹形結(jié)構(gòu)在報表模板中用嵌套的JSON對象來存儲,如圖3所示。

          圖2 界面部件布局關(guān)系


          圖3 界面部件信息JSON結(jié)構(gòu)

          界面部件的結(jié)構(gòu)定義如下:1)type,組件類型;2)id,組件編號;3)options,組件屬性;4)list,組件的子節(jié)點列表。

          加載報表模板文件后,設(shè)計器和查看器可以根據(jù)部件的type獲取對應(yīng)的類型,創(chuàng)建部件實例。在設(shè)計階段,設(shè)計器可以通過部件id定位部件對象,以便更新部件屬性設(shè)置和調(diào)整部件位置。

          3 報表組件設(shè)計

          報表組件分為設(shè)計器與查看器兩個部分。設(shè)計器用于創(chuàng)建和編輯報表,由監(jiān)控系統(tǒng)工程開發(fā)人員使用;查看器給運行人員使用,用于在線展示報表內(nèi)容。設(shè)計器與查看器是相互獨立的,但是在底層通過報表模板的格式定義相互影響,共享部分設(shè)計。

          3.1 設(shè)計器

          設(shè)計器內(nèi)部的組件關(guān)系如圖4所示。

          圖4 報表設(shè)計器組件關(guān)系

          由圖4可以看出,Web頁面內(nèi)的主要組件有Widgets-list、Type-list、Report-from、Properties-editor、DataSource-editor和Store。

          Widgets-list是一個提供了包含所有報表部件占位符的列表組件;Type-list是非可視化組件,提供了報表部件的類型定義;創(chuàng)建組件的時候,需要獲取對應(yīng)的組件類型,比如數(shù)據(jù)表格、數(shù)據(jù)圖表,所以需要一個組件的類型映射表,根據(jù)組件的type獲取對應(yīng)的類型,創(chuàng)建實例。

          Report-form代表了報表表單。Widgets-list內(nèi)的部件占位符都被賦予HTML中的draggable屬性,這樣可以將部件占位符從Widgets-list拖到Report- form上。當(dāng)一個部件占位符被從Widgets-list拖拽走時,Widgets-list會創(chuàng)建該占位符的一個復(fù)制品,供下次使用。

          Report-form組件監(jiān)聽了drop事件。當(dāng)部件占位符被拖到Report-form上時,會觸發(fā)監(jiān)測的drop事件。Report-form組件的事件處理器可以從事件參數(shù)中獲取部件占位符,并提取出其攜帶的部件類型信息。然后Widget-builder根據(jù)部件類型信息從Type- lis獲取完整的部件定義,并創(chuàng)建相應(yīng)的部件實例。

          Store組件管理數(shù)據(jù)源定義、報表結(jié)構(gòu)信息、報表組件屬性等內(nèi)容。DataSource-editor組件用于編輯修改數(shù)據(jù)源定義。

          Properties-editor組件用于編輯修改報表組件的屬性,Store組件與Properties-editor組件、Report- form組件都是雙向交互的。在Properties-editor組件中修改了組件屬性,Store中的內(nèi)容會相應(yīng)改變,并且會改變Report-form中報表的顯示。在Report-form中修改了報表的部件或布局時,Store中的信息會相應(yīng)改變,在Properties-editor組件上顯示的內(nèi)容也會跟隨變動。

          報表設(shè)計器的Qt部分加載報表模板或創(chuàng)建一個內(nèi)容為空的報表模板后,會執(zhí)行RunJavaScript方法來調(diào)用Web側(cè)的LoadReportTpl方法,將模板文件傳遞給Web側(cè)。Web側(cè)將報表模板解析后,先存入Store組件,然后由Report-form組件構(gòu)建相應(yīng)的報表部件并顯示。

          用戶隨后可以通過可視化方式對報表模板進(jìn)行編輯,如配置數(shù)據(jù)源、添加修改圖表/表格、設(shè)置圖表/表格的關(guān)聯(lián)數(shù)據(jù)點等。用戶編輯完模板后,可以點擊Qt側(cè)的保存按鈕,然后Qt側(cè)執(zhí)行RunJavaScript方法調(diào)用Web側(cè)的SaveReportTpl方法,從Web側(cè)獲取報表模板。

          3.2 查看器

          查看器內(nèi)部結(jié)構(gòu)的組件關(guān)系如圖5所示。

          圖5 報表查看器組件關(guān)系

          由圖5可以看出,查看器Web頁面內(nèi)的主要組件有Type-list、Report-from和Store。查看器內(nèi)的組件與設(shè)計器內(nèi)同名組件的功能基本相同。不同之處在于,Store組件和Report-from的交互關(guān)系為單向。Web側(cè)將報表模板解析后,先存入Store組件,然后由Report-form組件構(gòu)建相應(yīng)的報表部件并顯示。Report-from不再對Store組件有影響。

          查看器的組件關(guān)系相比設(shè)計器較為簡單。然而,其工作流程卻相對復(fù)雜。查看器的工作流程如圖6所示。

          圖6 報表查看器工作流程

          由圖6可以看出,報表查看器的工作流程如下:Qt部分負(fù)責(zé)加載報表模板;然后Qt側(cè)通過RunJavaScript方法調(diào)用Web側(cè)的LoadReportTpl方法,將報表模板文件傳遞給Web側(cè),由Web側(cè)顯示空的報表框架;同時Qt側(cè)對報表模板進(jìn)行解析,提取出其中的數(shù)據(jù)源設(shè)置;然后Qt側(cè)根據(jù)解析出的數(shù)據(jù)源配置以及用戶設(shè)置的時間段從服務(wù)器獲取相應(yīng)時間端的數(shù)據(jù);Qt側(cè)通過RunJavaScript方法調(diào)用Web側(cè)的LoadDataSet方法,將報表要使用的數(shù)據(jù)集傳遞給Web側(cè);最后Web側(cè)用數(shù)據(jù)集填充報表框架,將報表渲染出來。

          4 結(jié)論

          本文描述的報表組件開發(fā)方案,提供了可視化的報表設(shè)計工具,報表格式能夠靈活定義,而且能夠跨平臺使用,充分滿足變電站監(jiān)控系統(tǒng)的安防需求。并且,報表組件基于QtWebEngine和Web技術(shù)開發(fā),報表模板采用JSON格式定義,不依賴于第三方報表組件,具有良好的兼容性和通用性,易于應(yīng)用到其他C/S架構(gòu)的客戶端系統(tǒng)中。

          本技術(shù)方案也可供使用其他非Qt框架的監(jiān)控系統(tǒng)做技術(shù)參考,只要使用的開發(fā)框架有可用的瀏覽器組件,即可采用類似的技術(shù)路線開發(fā)報表組件。


          主站蜘蛛池模板: 黄桃AV无码免费一区二区三区 | 国产精品福利一区二区| 日韩精品一区二三区中文| 波多野结衣av高清一区二区三区| 少妇人妻精品一区二区三区| 日韩在线视频不卡一区二区三区 | 一区二区三区视频免费观看| 国内自拍视频一区二区三区 | 日韩久久精品一区二区三区| 亚洲视频一区网站| 高清国产精品人妻一区二区| 国产91精品一区| 国内精品一区二区三区最新| 亚洲一区二区三区91| 亚洲一区影音先锋色资源| 国产福利电影一区二区三区,亚洲国模精品一区| 精品一区二区三区无码免费视频 | 另类一区二区三区| 精品一区二区三区免费观看 | 无码人妻精品一区二区三| 国产午夜精品一区二区三区嫩草| 午夜视频久久久久一区 | 熟女少妇丰满一区二区| 国产一区二区三区在线视頻 | 久久久人妻精品无码一区| 日本高清天码一区在线播放| 爱爱帝国亚洲一区二区三区 | 交换国产精品视频一区| 亚洲欧美国产国产综合一区 | 麻豆精品一区二区综合av| 久久久久人妻精品一区三寸| 无码人妻一区二区三区免费手机| 日韩一区二区视频| 午夜在线视频一区二区三区| 国产精品亚洲一区二区三区在线观看 | 亚洲熟女综合色一区二区三区| 精品乱码一区二区三区四区| 亚洲国产AV一区二区三区四区| 精产国品一区二区三产区| 国产在线不卡一区| 亚洲线精品一区二区三区|