整合營(yíng)銷服務(wù)商

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

          免費(fèi)咨詢熱線:

          大神總結(jié)的Qt開(kāi)發(fā)經(jīng)驗(yàn),滿滿的都是干貨

          先聲明,本文并非原創(chuàng),純屬搬運(yùn),內(nèi)容來(lái)自一位叫做飛揚(yáng)青春的大神的Gitee主頁(yè),主要是為了收藏下面介紹的100多個(gè)Qt開(kāi)發(fā)經(jīng)驗(yàn)。我本身也從事了兩年了Qt開(kāi)發(fā),再轉(zhuǎn)Qt開(kāi)發(fā)以前用的都是MFC,我仔細(xì)的看了一遍下面列出的各條經(jīng)驗(yàn),只恨看到的太晚了,因?yàn)楹芏喽际亲约翰冗^(guò)的坑。比如qss的ANSI編碼、嵌套窗口中主窗口無(wú)法接收鼠標(biāo)移動(dòng)事件等,又比如我用qss設(shè)置窗口樣式,但是項(xiàng)目每次重新構(gòu)建以后,樣式表就會(huì)不生效等問(wèn)題,也花了自己不少時(shí)間去解決,所以在這里轉(zhuǎn)發(fā)大神的經(jīng)驗(yàn),留作以后參考和逐條的研究,也分享給更多正在學(xué)習(xí)Qt或者正在使用Qt進(jìn)行程序開(kāi)發(fā)的朋友們。

          大神主頁(yè):https://gitee.com/feiyangqingyun/qtkaifajingyan?_from=gitee_search

          1. 當(dāng)編譯發(fā)現(xiàn)大量錯(cuò)誤的時(shí)候,從第一個(gè)看起,一個(gè)一個(gè)的解決,不要急著去看下一個(gè)錯(cuò)誤,往往后面的錯(cuò)誤都是由于前面的錯(cuò)誤引起的,第一個(gè)解決后很可能都解決了。

          2. 定時(shí)器是個(gè)好東西,學(xué)會(huì)好使用它,有時(shí)候用QTimer::singleShot可以解決意想不到的問(wèn)題。

          3. 打開(kāi)creator,在構(gòu)建套件的環(huán)境中增加MAKEFLAGS=-j8,可以不用每次設(shè)置多線程編譯。珍愛(ài)時(shí)間和生命。新版的QtCreator已經(jīng)默認(rèn)就是j8。

          4. 如果你想順利用QtCreator部署安卓程序,首先你要在AndroidStudio 里面配置成功,把坑全部趟平。

          5. 很多時(shí)候找到Qt對(duì)應(yīng)封裝的方法后,記得多看看該函數(shù)的重載,多個(gè)參數(shù)的,你會(huì)發(fā)現(xiàn)不一樣的世界,有時(shí)候會(huì)恍然大悟,原來(lái)Qt已經(jīng)幫我們封裝好了。

          6. 可以在pro文件中寫(xiě)上標(biāo)記版本號(hào)+ico圖標(biāo)(Qt5才支持)

          VERSION  = 2020.10.25
          RC_ICONS = main0.ico

          7. 管理員運(yùn)行程序,限定在MSVC編譯器

          QMAKE_LFLAGS += /MANIFESTUAC:"level='requireAdministrator' uiAccess='false'" #以管理員運(yùn)行
          QMAKE_LFLAGS += /SUBSYSTEM:WINDOWS,"5.01" #VS2013 在XP運(yùn)行

          8. 運(yùn)行文件附帶調(diào)試輸出窗口

          CONFIG += console pro

          9. 繪制平鋪背景QPainter::drawTiledPixmap,繪制圓角矩形QPainter::drawRoundedRect(),而不是QPainter::drawRoundRect();

          10. 移除舊的樣式

          //移除原有樣式
          style()->unpolish(ui->btn);
          //重新設(shè)置新的該控件的樣式。
          style()->polish(ui->btn);

          11. 獲取類的屬性

          const QMetaObject *metaobject = object->metaObject();
          int count = metaobject->propertyCount();
          for (int i = 0; i < count; ++i) {
              QMetaProperty metaproperty = metaobject->property(i);
              const char *name = metaproperty.name();
              QVariant value = object->property(name);
              qDebug() << name << value;
          }

          12. Qt內(nèi)置圖標(biāo)封裝在QStyle中,大概七十多個(gè)圖標(biāo),可以直接拿來(lái)用。

          SP_TitleBarMenuButton,
          SP_TitleBarMinButton,
          SP_TitleBarMaxButton,
          SP_TitleBarCloseButton,
          SP_MessageBoxInformation,
          SP_MessageBoxWarning,
          SP_MessageBoxCritical,
          SP_MessageBoxQuestion,
          ...

          13. 根據(jù)操作系統(tǒng)位數(shù)判斷加載

          win32 {
              contains(DEFINES, WIN64) { DESTDIR = $${PWD}/../../bin64
              } else { DESTDIR = $${PWD}/../../bin32 }
          }

          14. Qt5增強(qiáng)了很多安全性驗(yàn)證,如果出現(xiàn)setGeometry: Unable to set geometry,請(qǐng)將該控件的可見(jiàn)移到加入布局之后。

          15. 可以將控件A添加到布局,然后控件B設(shè)置該布局,這種靈活性大大提高了控件的組合度,比如可以在文本框左側(cè)右側(cè)增加一個(gè)搜索按鈕,按鈕設(shè)置圖標(biāo)即可。

          QPushButton *btn = new QPushButton;
          btn->resize(30, ui->lineEdit->height());
          QHBoxLayout *layout = new QHBoxLayout(ui->lineEdit);
          layout->setMargin(0);
          layout->addStretch();
          layout->addWidget(btn);

          16. 對(duì)QLCDNumber控件設(shè)置樣式,需要將QLCDNumber的segmentstyle設(shè)置為flat。

          17. 巧妙的使用findChildren可以查找該控件下的所有子控件。findChild為查找單個(gè)。

          //查找指定類名objectName的控件
          QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>("widgetname");
          //查找所有QPushButton
          QList<QPushButton *> allPButtons = parentWidget.findChildren<QPushButton *>();
          //查找一級(jí)子控件,不然會(huì)一直遍歷所有子控件
          QList<QPushButton *> childButtons = parentWidget.findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);

          18. 巧妙的使用inherits判斷是否屬于某種類。

          QTimer *timer = new QTimer;         // QTimer inherits QObject
          timer->inherits("QTimer");          // returns true
          timer->inherits("QObject");         // returns true
          timer->inherits("QAbstractButton"); // returns false

          19. 使用弱屬性機(jī)制,可以存儲(chǔ)臨時(shí)的值用于傳遞判斷。可以通過(guò)widget->dynamicPropertyNames()列出所有弱屬性名稱,然后通過(guò)widget->property("name")取出對(duì)應(yīng)的弱屬性的值。

          20. 在開(kāi)發(fā)時(shí), 無(wú)論是出于維護(hù)的便捷性, 還是節(jié)省內(nèi)存資源的考慮, 都應(yīng)該有一個(gè) qss 文件來(lái)存放所有的樣式表, 而不應(yīng)該將 setStyleSheet 寫(xiě)的到處都是。如果是初學(xué)階段或者測(cè)試階段可以直接UI上右鍵設(shè)置樣式表,正式項(xiàng)目還是建議統(tǒng)一到一個(gè)qss樣式表文件比較好,統(tǒng)一管理。

          21. 如果出現(xiàn)Z-order assignment: is not a valid widget.錯(cuò)誤提示,用記事本打開(kāi)對(duì)應(yīng)的ui文件,找到<zorder></zorder>為空的地方,刪除即可。

          22. 善于利用QComboBox的addItem的第二個(gè)參數(shù)設(shè)置用戶數(shù)據(jù),可以實(shí)現(xiàn)很多效果,使用itemData取出來(lái)。

          23. 如果用了webengine模塊,發(fā)布程序的時(shí)候帶上QtWebEngineProcess.exe+translations文件夾+resources文件夾。

          24. 默認(rèn)Qt是一個(gè)窗體一個(gè)句柄,如果要讓每個(gè)控件都擁有獨(dú)立的句柄,設(shè)置下 a.setAttribute(Qt::AA_NativeWindows);

          25. Qt+Android防止程序被關(guān)閉。

          #if defined(Q_OS_ANDROID)
          QAndroidService a(argc, argv);
          return a.exec()
          #else
          QApplication a(argc, argv);
          return a.exec();
          #endif

          26. 可以對(duì)整體的指示器設(shè)置樣式,例如 *::down-arrow,*::menu-indicator{} *::up-arrow:disabled,*::up-arrow:off{}。

          27. 可以執(zhí)行位置設(shè)置背景圖片。

          QMainWindow > .QWidget {
              background-color: gainsboro;
              background-image: url(:/images/pagefold.png);
              background-position: top right;
              background-repeat: no-repeat
          }

          28. 嵌入式linux運(yùn)行Qt程序 Qt4寫(xiě)法:./HelloQt -qws & Qt5寫(xiě)法:./HelloQt --platform xcb

          29. Qtcreator軟件的配置文件存放在:C:\Users\Administrator\AppData\Roaming\QtProject,有時(shí)候如果發(fā)現(xiàn)出問(wèn)題了,將這個(gè)文件夾刪除后打開(kāi)creator自動(dòng)重新生成即可。

          30. QMediaPlayer是個(gè)殼,依賴本地解碼器,視頻這塊默認(rèn)基本上就播放個(gè)MP4,如果要支持其他格式需要下載k-lite或者LAV Filters安裝即可(WIN上,其他系統(tǒng)上自行搜索)。如果需要做功能強(qiáng)勁的播放器,初學(xué)者建議用vlc、mpv,終極大法用ffmpeg。

          31. 判斷編譯器類型、編譯器版本、操作系統(tǒng)。

          //GCC編譯器
          #ifdef __GNUC__
          #if __GNUC__ >= 3 // GCC3.0以上
          
          //MSVC編譯器
          #ifdef _MSC_VER
          #if _MSC_VER >=1000 // VC++4.0以上
          #if _MSC_VER >=1100 // VC++5.0以上
          #if _MSC_VER >=1200 // VC++6.0以上
          #if _MSC_VER >=1300 // VC2003以上
          #if _MSC_VER >=1400 // VC2005以上
          #if _MSC_VER >=1500 // VC2008以上
          #if _MSC_VER >=1600 // VC2010以上
          #if _MSC_VER >=1700 // VC2012以上
          #if _MSC_VER >=1800 // VC2013以上
          #if _MSC_VER >=1900 // VC2015以上
          
          //Borland C++
          #ifdef __BORLANDC__
          
          //Cygwin
          
          #ifdef __CGWIN__
          
          #ifdef __CYGWIN32__
          
          //mingw
          
          #ifdef __MINGW32__
          
          //windows
          
          #ifdef _WIN32    //32bit
          
          #ifdef _WIN64    //64bit
          
          #ifdef _WINDOWS     //圖形界面程序
          
          #ifdef _CONSOLE     //控制臺(tái)程序
          
          //Windows(95/98/Me/NT/2000/XP/Vista)和Windows CE都定義了
          
          #if (WINVER >= 0x030a)     // Windows 3.1以上
          
          #if (WINVER >= 0x0400)     // Windows 95/NT4.0以上
          
          #if (WINVER >= 0x0410)     // Windows 98以上
          
          #if (WINVER >= 0x0500)     // Windows Me/2000以上
          
          #if (WINVER >= 0x0501)     // Windows XP以上
          
          #if (WINVER >= 0x0600)     // Windows Vista以上
          
          //_WIN32_WINNT 內(nèi)核版本
          
          #if (_WIN32_WINNT >= 0x0500) // Windows 2000以上
          
          #if (_WIN32_WINNT >= 0x0501) // Windows XP以上
          
          #if (_WIN32_WINNT >= 0x0600) // Windows Vista以上

          32. 在pro中判斷Qt版本及構(gòu)建套件位數(shù)

          #打印版本信息
          message(qt version: $$QT_VERSION)
          
          #判斷當(dāng)前qt版本號(hào)
          QT_VERSION = $$[QT_VERSION]
          QT_VERSION = $$split(QT_VERSION, ".")
          QT_VER_MAJ = $$member(QT_VERSION, 0)
          QT_VER_MIN = $$member(QT_VERSION, 1)
          
          #下面是表示 Qt5.5
          greaterThan(QT_VER_MAJ, 4) {
          greaterThan(QT_VER_MIN, 4) {
          #自己根據(jù)需要做一些處理
          }
          }
          
          #QT_ARCH是Qt5新增的,在Qt4上沒(méi)效果
          #打印當(dāng)前Qt構(gòu)建套件的信息
          message($$QT_ARCH)
          
          #表示arm平臺(tái)構(gòu)建套件
          contains(QT_ARCH, arm) {}
          
          #表示32位的構(gòu)建套件
          contains(QT_ARCH, i386) {}
          
          #表示64位的構(gòu)建套件
          contains(QT_ARCH, x86_64) {}

          33. Qt最小化后恢復(fù)界面假死凍結(jié),加上代碼

          void showEvent(QShowEvent *e)
          {
              setAttribute(Qt::WA_Mapped);
              QWidget::showEvent(e);
          }

          34. 獲取標(biāo)題欄高度:style()->pixelMetric(QStyle::PM_TitleBarHeight); PM_TitleBarHeight點(diǎn)進(jìn)去你會(huì)發(fā)現(xiàn)新大陸。

          35. 設(shè)置高分屏屬性以便支持2K4K等高分辨率,尤其是手機(jī)app。必須寫(xiě)在main函數(shù)的QApplication a(argc, argv);的前面。

          #if (QT_VERSION > QT_VERSION_CHECK(5,6,0))
              QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
          #endif

          36. 如果運(yùn)行程序出現(xiàn) Fault tolerant heap shim applied to current process. This is usually due to previous crashes. 錯(cuò)誤。

          辦法:打開(kāi)注冊(cè)表,找到HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers\,選中Layers鍵值,從右側(cè)列表中刪除自己的那個(gè)程序路徑即可。

          37. Qt內(nèi)置了QFormLayout表單布局用于自動(dòng)生成標(biāo)簽+輸入框的組合的表單界面。

          38. qml播放視頻在linux需要安裝 sudo apt-get install libpulse-dev。

          39. 可以直接繼承QSqlQueryModel實(shí)現(xiàn)自定義的QueryModel,比如某一列字體顏色,占位符,其他樣式等,重寫(xiě)QVariant CustomSqlModel::data(const QModelIndex &index, int role) const。

          40. Qt5以后提供了類QScroller直接將控件滾動(dòng)。

          //禁用橫向滾動(dòng)條
          ui->listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
          
          //禁用縱向滾動(dòng)條
          ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
          
          //設(shè)置橫向按照像素值為單位滾動(dòng)
          ui->listWidget->setHorizontalScrollMode(QListWidget::ScrollPerPixel);
          
          //設(shè)置縱向按照像素值為單位滾動(dòng)
          ui->listWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel);
          
          //設(shè)置滾動(dòng)對(duì)象以及滾動(dòng)方式為鼠標(biāo)左鍵拉動(dòng)滾動(dòng)
          QScroller::grabGesture(ui->listWidget, QScroller::LeftMouseButtonGesture);
          
          //還有個(gè)QScrollerProperties可以設(shè)置滾動(dòng)的一些參數(shù)

          41. 如果使用sqlite數(shù)據(jù)庫(kù)不想產(chǎn)生數(shù)據(jù)庫(kù)文件,可以創(chuàng)建內(nèi)存數(shù)據(jù)庫(kù)。

          QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
          db.setDatabaseName(":memory:");

          42. 清空數(shù)據(jù)表并重置自增ID,sql = truncate table table_name。

          43. Qtchart模塊從Qt5.7開(kāi)始自帶,最低編譯要求Qt5.4。在安裝的時(shí)候記得勾選,默認(rèn)不勾選。使用該模塊需要引入命名空間。

          #include <QChartView>
          QT_CHARTS_USE_NAMESPACE
          class CustomChart : public QChartView

          44. QPushButton左對(duì)齊文字,需要設(shè)置樣式表QPushButton{text-align:left;}

          45. QLabel有三種設(shè)置文本的方法,掌握好Qt的屬性系統(tǒng),舉一反三,可以做出很多效果。

          ui->label->setStyleSheet("qproperty-text:hello;");
          ui->label->setProperty("text", "hello");
          ui->label->setText("hello");

          46. 巧妙的用QEventLoop開(kāi)啟事件循環(huán),可以使得很多同步獲取返回結(jié)果而不阻塞界面。QEventLoop內(nèi)部新建了線程執(zhí)行。

          QEventLoop loop;
          connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
          loop.exec();

          47. 多種預(yù)定義變量 #if (defined webkit) || (defined webengine),去掉生成空的debug和release目錄 CONFIG -= debug_and_release。

          48. 新版的Qtcreator增強(qiáng)了語(yǔ)法檢查,會(huì)彈出很多警告提示等,可以在插件列表中關(guān)閉clang打頭的幾個(gè)即可,Help》About Plugins。也可以設(shè)置代碼檢查級(jí)別,Tools》Options 》C++ 》Code Model。

          49. QSqlTableModel的rowCount方法,默認(rèn)最大返回256,如果超過(guò)256,可以將表格拉到底部,會(huì)自動(dòng)加載剩余的,每次最大加載256條數(shù)據(jù),如果需要打印或者導(dǎo)出數(shù)據(jù),記得最好采用sql語(yǔ)句去查詢,而不是使用QSqlTableModel的rowCount方法。不然永遠(yuǎn)最大只會(huì)導(dǎo)出256條數(shù)據(jù)。

          如果數(shù)據(jù)量很小,也可以采用如下方法:

          //主動(dòng)加載所有數(shù)據(jù),不然獲取到的行數(shù)<=256
          while(model->canFetchMore()) {
              model->fetchMore();
          }

          50. 如果需要指定無(wú)邊框窗體,但是又需要保留操作系統(tǒng)的邊框特性,可以自由拉伸邊框,可以使用

           setWindowFlags(Qt::CustomizeWindowHint);

          51. 在某些http post數(shù)據(jù)的時(shí)候,如果采用的是&字符串連接的數(shù)據(jù)發(fā)送,中文解析亂碼的話,需要將中文進(jìn)行URL轉(zhuǎn)碼。

          QString content = "測(cè)試中文";
          QString note = content.toUtf8().toPercentEncoding();

          52. Qt默認(rèn)不支持大資源文件,比如添加了字體文件,需要pro文件開(kāi)啟。

          CONFIG += resources_big

          53. Qt中繼承QWidget之后,樣式表不起作用,解決辦法有三個(gè)。強(qiáng)烈推薦方法一。

          - 方法一:設(shè)置屬性 this->setAttribute(Qt::WA_StyledBackground, true);

          - 方法二:改成繼承QFrame,因?yàn)镼Frame自帶paintEvent函數(shù)已做了實(shí)現(xiàn),在使用樣式表時(shí)會(huì)進(jìn)行解析和繪制。

          - 方法三:重新實(shí)現(xiàn)QWidget的paintEvent函數(shù)時(shí),使用QStylePainter繪制。

          void Widget::paintEvent(QPaintEvent *)
          {
              QStyleOption option;
              option.initFrom(this);
              QPainter painter(this);
              style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);
          }

          54. 有時(shí)候在界面上加了彈簧,需要?jiǎng)討B(tài)改變彈簧對(duì)應(yīng)的拉伸策略,對(duì)應(yīng)方法為changeSize,很多人會(huì)選擇使用set開(kāi)頭去找,找不到的。

          55. 在使用QFile的過(guò)程中,不建議頻繁的打開(kāi)文件寫(xiě)入然后再關(guān)閉文件,比如間隔5ms輸出日志,IO性能瓶頸很大,這種情況建議先打開(kāi)文件不要關(guān)閉,等待合適的時(shí)機(jī)比如析構(gòu)函數(shù)中或者日期變了需要重新變換日志文件的時(shí)候關(guān)閉文件。不然短時(shí)間內(nèi)大量的打開(kāi)關(guān)閉文件會(huì)很卡,文件越大越卡。

          56. 在很多網(wǎng)絡(luò)應(yīng)用程序,需要自定義心跳包來(lái)保持連接,不然斷電或者非法關(guān)閉程序,對(duì)方識(shí)別不到,需要進(jìn)行超時(shí)檢測(cè),但是有些程序沒(méi)有提供心跳協(xié)議,此時(shí)需要啟用系統(tǒng)層的保活程序,此方法適用于TCP連接。

          int fd = tcpSocket->socketDescriptor();
          int keepAlive = 1;      //開(kāi)啟keepalive屬性,缺省值:0(關(guān)閉)
          int keepIdle = 5;       //如果在5秒內(nèi)沒(méi)有任何數(shù)據(jù)交互,則進(jìn)行探測(cè),缺省值:7200(s)
          int keepInterval = 2;   //探測(cè)時(shí)發(fā)探測(cè)包的時(shí)間間隔為2秒,缺省值:75(s)
          int keepCount = 2;      //探測(cè)重試的次數(shù),全部超時(shí)則認(rèn)定連接失效,缺省值:9(次)
          setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
          setsockopt(fd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));
          setsockopt(fd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
          setsockopt(fd, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

          57. 如果程序打包好以后彈出提示 This application failed to start because it could not find or load the Qt platform plugin 一般都是因?yàn)閜latforms插件目錄未打包或者打包錯(cuò)了的原因?qū)е碌摹?/p>

          58. 非常不建議tr中包含中文,盡管現(xiàn)在的新版Qt支持中文到其他語(yǔ)言的翻譯,但是很不規(guī)范,也不知道TMD是誰(shuí)教的,tr的本意是包含英文,然后翻譯到其他語(yǔ)言比如中文,現(xiàn)在大量的初學(xué)者濫用tr,如果沒(méi)有翻譯的需求,禁用tr,tr需要開(kāi)銷的,Qt默認(rèn)會(huì)認(rèn)為他需要翻譯,會(huì)額外進(jìn)行特殊處理。

          59. 很多人Qt和Qt Creator傻傻分不清楚,經(jīng)常問(wèn)Qt什么版本結(jié)果發(fā)一個(gè)Qt Creator的版本過(guò)來(lái),Qt Creator是使用Qt編寫(xiě)的集成開(kāi)發(fā)環(huán)境IDE,和宇宙第一的Visual Studio一樣,他可以是msvc編譯器的(WIN對(duì)應(yīng)的Qt集成安裝環(huán)境中自帶的Qt Cerator是msvc的),也可以是mingw編譯的,還可以是gcc的。如果是自定義控件插件,需要集成到Qt Creator中,必須保證該插件的動(dòng)態(tài)庫(kù)文件(dll或者so等文件)對(duì)應(yīng)的編譯器和Qt版本以及位數(shù)和Qt Creator的版本完全一致才行,否則基本不大可能集成進(jìn)去。特別注意的是Qt集成環(huán)境安裝包中的Qt版本和Qt Creator版本未必完全一致,必須擦亮眼睛看清楚,有些是完全一致的。

          60. 超過(guò)兩處相同處理的代碼,建議單獨(dú)寫(xiě)成函數(shù)。代碼盡量規(guī)范精簡(jiǎn),比如 if(a == 123) 要寫(xiě)成 if (123 == a),值在前面,再比如 if (ok == true) 要寫(xiě)成 if (ok),if (ok == false) 要寫(xiě)成 if (!ok)等。

          61. 很多人問(wèn)Qt嵌入式平臺(tái)用哪個(gè)好,這里統(tǒng)一回答(當(dāng)前時(shí)間節(jié)點(diǎn)2018年):imx6+335x比較穩(wěn)定,性能高就用RK3288 RK3399,便宜的話就用全志H3,玩一玩可以用樹(shù)莓派香橙派。

          62. 對(duì)于大段的注釋代碼,建議用 #if 0 #endif 將代碼塊包含起來(lái),而不是將該段代碼選中然后全部 // ,下次要打開(kāi)這段代碼的話,又需要重新選中一次取消,如果采用的是 #if 0則只要把0改成1即可,效率大大提升。

          63. Qt打包發(fā)布,有很多辦法,Qt5以后提供了打包工具windeployqt(linux上為linuxdeployqt,mac上為macdeployqt)可以很方便的將應(yīng)用程序打包,使用下來(lái)發(fā)現(xiàn)也不是萬(wàn)能的,有時(shí)候會(huì)多打包一些沒(méi)有依賴的文件,有時(shí)候又會(huì)忘記打包一些插件尤其是用了qml的情況下,而且不能識(shí)別第三方庫(kù),比如程序依賴ffmpeg,則對(duì)應(yīng)的庫(kù)需要自行拷貝,終極大法就是將你的可執(zhí)行文件復(fù)制到Qt安裝目錄下的bin目錄,然后整個(gè)一起打包,挨個(gè)刪除不大可能依賴的組件,直到刪到正常運(yùn)行為止。

          64. Qt中的動(dòng)畫(huà),底層用的是QElapsedTimer定時(shí)器來(lái)完成處理,比如產(chǎn)生一些指定規(guī)則算法的數(shù)據(jù),然后對(duì)屬性進(jìn)行處理。

          65. 在繪制無(wú)背景顏色只有邊框顏色的圓形時(shí)候,可以用繪制360度的圓弧替代,效果完全一致。

          QRect rect(-radius, -radius, radius * 2, radius * 2);
          //以下兩種方法二選一,其實(shí)繪制360度的圓弧=繪制無(wú)背景的圓形
          painter->drawArc(rect, 0, 360 * 16);
          painter->drawEllipse(rect);

          66. 不要把d指針看的很玄乎,其實(shí)就是在類的實(shí)現(xiàn)文件定義了一個(gè)私有類,用來(lái)存放局部變量,個(gè)人建議在做一些小項(xiàng)目時(shí),沒(méi)有太大必要引入這種機(jī)制,會(huì)降低代碼可讀性,增加復(fù)雜性,新手接受項(xiàng)目后會(huì)看的很懵逼。

          67. 很多人在繪制的時(shí)候,設(shè)置畫(huà)筆以為就只可以設(shè)置個(gè)單調(diào)的顏色,其實(shí)QPen還可以設(shè)置brush,這樣靈活性就提高不知道多少倍,比如設(shè)置QPen的brush以后,可以使用各種漸變,比如繪制漸變顏色的進(jìn)度條和文字等,而不再是單調(diào)的一種顏色。

          68. 很多控件都帶有viewport,比如QTextEdit/QTableWidget/QScrollArea,有時(shí)候?qū)@些控件直接處理的時(shí)候發(fā)現(xiàn)不起作用,需要對(duì)其viewport()設(shè)置才行,比如設(shè)置滾動(dòng)條區(qū)域背景透明,需要使用scrollArea->viewport()->setStyleSheet("background-color:transparent;");而不是scrollArea->setStyleSheet("QScrollArea{background-color:transparent;}");

          69. 有時(shí)候設(shè)置了鼠標(biāo)跟蹤setMouseTracking為真,如果該窗體上面還有其他控件,當(dāng)鼠標(biāo)移到其他控件上面的時(shí)候,父類的鼠標(biāo)移動(dòng)事件MouseMove識(shí)別不到了,此時(shí)需要用到HoverMove事件,需要先設(shè)置 setAttribute(Qt::WA_Hover, true);

          70. Qt封裝的QDateTime日期時(shí)間類非常強(qiáng)大,可以字符串和日期時(shí)間相互轉(zhuǎn)換,也可以毫秒數(shù)和日期時(shí)間相互轉(zhuǎn)換,還可以1970經(jīng)過(guò)的秒數(shù)和日期時(shí)間相互轉(zhuǎn)換等。

          QDateTime dateTime;
          QString dateTime_str = dateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
          //從字符串轉(zhuǎn)換為毫秒(需完整的年月日時(shí)分秒)
          datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toMSecsSinceEpoch();
          //從字符串轉(zhuǎn)換為秒(需完整的年月日時(shí)分秒)
          datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toTime_t();
          //從毫秒轉(zhuǎn)換到年月日時(shí)分秒
          datetime.fromMSecsSinceEpoch(1315193829218).toString("yyyy-MM-dd hh:mm:ss:zzz");
          //從秒轉(zhuǎn)換到年月日時(shí)分秒(若有zzz,則為000)
          datetime.fromTime_t(1315193829).toString("yyyy-MM-dd hh:mm:ss[:zzz]");

          71. 在我們使用QList、QStringList、QByteArray等鏈表或者數(shù)組的過(guò)程中,如果只需要取值,而不是賦值,強(qiáng)烈建議使用 at() 取值而不是 [] 操作符,在官方書(shū)籍《C++ GUI Qt 4編程(第二版)》的書(shū)中有特別的強(qiáng)調(diào)說(shuō)明,此教材的原作者據(jù)說(shuō)是Qt開(kāi)發(fā)的核心人員編寫(xiě)的,所以還是比較權(quán)威,至于使用 at() 與使用 [] 操作符速度效率的比較,網(wǎng)上也有網(wǎng)友做過(guò)此類對(duì)比。原文在書(shū)的212頁(yè),這樣描述的:Qt對(duì)所有的容器和許多其他類都使用隱含共享,隱含共享是Qt對(duì)不希望修改的數(shù)據(jù)決不進(jìn)行復(fù)制的保證,為了使隱含共享的作用發(fā)揮得最好,可以采用兩個(gè)新的編程習(xí)慣。第一種習(xí)慣是對(duì)于一個(gè)(非常量的)向量或者列表進(jìn)行只讀存取時(shí),使用 at() 函數(shù)而不用 [] 操作符,因?yàn)镼t的容器類不能辨別 [] 操作符是否將出現(xiàn)在一個(gè)賦值的左邊還是右邊,他假設(shè)最壞的情況出現(xiàn)并且強(qiáng)制執(zhí)行深層賦值,而 at() 函數(shù)則不被允許出現(xiàn)在一個(gè)賦值的左邊。

          72. 如果是dialog窗體,需要在exec以后還能讓其他代碼繼續(xù)執(zhí)行,請(qǐng)?jiān)赿ialog窗體exec前增加一行代碼,否則會(huì)阻塞窗體消息。

          QDialog dialog;
          dialog.setWindowModality(Qt::WindowModal);
          dialog.exec();

          73. 安全的刪除Qt的對(duì)象類,強(qiáng)烈建議使用deleteLater而不是delete,因?yàn)閐eleteLater會(huì)選擇在合適的時(shí)機(jī)進(jìn)行釋放,而delete會(huì)立即釋放,很可能會(huì)出錯(cuò)崩潰。如果要批量刪除對(duì)象集合,可以用qDeleteAll,比如 qDeleteAll(btns);

          74. 在QTableView控件中,如果需要自定義的列按鈕、復(fù)選框、下拉框等其他模式顯示,可以采用自定義委托QItemDelegate來(lái)實(shí)現(xiàn),如果需要禁用某列,則在自定義委托的重載createEditor函數(shù)返回0即可。自定義委托對(duì)應(yīng)的控件在進(jìn)入編輯狀態(tài)的時(shí)候出現(xiàn),如果想一直出現(xiàn),則需要重載paint函數(shù)用drawPrimitive或者drawControl來(lái)繪制。

          75. 將 QApplication::style() 對(duì)應(yīng)的drawPrimitive、drawControl、drawItemText、drawItemPixmap等幾個(gè)方法用熟悉了,再結(jié)合QStyleOption屬性,可以玩轉(zhuǎn)各種自定義委托,還可以直接使用paint函數(shù)中的painter進(jìn)行各種繪制,各種牛逼的表格、樹(shù)狀列表、下拉框等,絕對(duì)屌炸天。QApplication::style()->drawControl 的第4個(gè)參數(shù)如果不設(shè)置,則繪制出來(lái)的控件不會(huì)應(yīng)用樣式表。

          76. 心中有坐標(biāo),萬(wàn)物皆painter,強(qiáng)烈建議在學(xué)習(xí)自定義控件繪制的時(shí)候,將qpainter.h頭文件中的函數(shù)全部看一遍、試一遍、理解一遍,這里邊包含了所有Qt內(nèi)置的繪制的接口,對(duì)應(yīng)的參數(shù)都試一遍,你會(huì)發(fā)現(xiàn)很多新大陸,會(huì)大大激發(fā)你的繪制的興趣,猶如神筆馬良一般,策馬崩騰遨游代碼繪制的世界。

          77. 在使用setItemWidget或者setCellWidget的過(guò)程中,有時(shí)候會(huì)發(fā)現(xiàn)設(shè)置的控件沒(méi)有居中顯示而是默認(rèn)的左對(duì)齊,而且不會(huì)自動(dòng)拉伸填充,對(duì)于追求完美的程序員來(lái)說(shuō),這個(gè)可不大好看,有個(gè)終極通用辦法就是,將這個(gè)控件放到一個(gè)widget的布局中,然后將widget添加到item中,這樣就完美解決了,而且這樣可以組合多個(gè)控件產(chǎn)生復(fù)雜的控件。

          //實(shí)例化進(jìn)度條控件
          QProgressBar *progress = new QProgressBar;
          //增加widget+布局巧妙實(shí)現(xiàn)居中
          QWidget *widget = new QWidget;
          QHBoxLayout *layout = new QHBoxLayout;
          layout->setSpacing(0);
          layout->setMargin(0);
          layout->addWidget(progress);
          widget->setLayout(layout);
          ui->tableWidget->setCellWidget(0, 0, widget);

          78. 很多時(shí)候需要在已知背景色的情況下,能夠清晰的繪制文字,這個(gè)時(shí)候需要計(jì)算對(duì)應(yīng)的文字顏色。

          //根據(jù)背景色自動(dòng)計(jì)算合適的前景色
          double gray = (0.299 * color.red() + 0.587 * color.green() + 0.114 * color.blue()) / 255;
          QColor textColor = gray > 0.5 ? Qt::black : Qt::white;

          79. 對(duì)QTableView或者QTableWidget禁用列拖動(dòng)。

          #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
              ui->tableView->horizontalHeader()->setResizeMode(0, QHeaderView::Fixed);
          #else
              ui->tableView->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Fixed);
          #endif

          80. 從Qt4轉(zhuǎn)到Qt5,有些類的方法已經(jīng)廢棄或者過(guò)時(shí)了,如果想要在Qt5中啟用Qt4的方法,比如QHeadVew的setMovable,可以在你的pro或者pri文件中加上一行即可:DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0

          81. Qt中的QColor對(duì)顏色封裝的很完美,支持各種轉(zhuǎn)換,比如rgb、hsb、cmy、hsl,對(duì)應(yīng)的是toRgb、toHsv、toCmyk、toHsl,還支持透明度設(shè)置,顏色值還能轉(zhuǎn)成16進(jìn)制格式顯示。

          QColor color(255, 0, 0, 100);
          qDebug() << color.name() << color.name(QColor::HexArgb);
          //輸出 #ff0000 #64ff0000

          82. QVariant類型異常的強(qiáng)大,可以說(shuō)是萬(wàn)能的類型,在進(jìn)行配置文件的存儲(chǔ)的時(shí)候,經(jīng)常會(huì)用到QVariant的轉(zhuǎn)換,QVariant默認(rèn)自帶了toString、toFloat等各種轉(zhuǎn)換,但是還是不夠,比如有時(shí)候需要從QVariant轉(zhuǎn)到QColor,而卻沒(méi)有提供toColor的函數(shù),這個(gè)時(shí)候就要用到萬(wàn)能辦法。

          if (variant.typeName() == "QColor") {
              QColor color = variant.value<QColor>();
              QFont font = variant.value<QFont>();
              QString nodeValue = color.name(QColor::HexArgb);
          }

          83. Qt中的QString和const char *之間轉(zhuǎn)換,最好用toStdString().c_str()而不是toLocal8Bit().constData(),比如在setProperty中如果用后者,字符串中文就會(huì)不正確,英文正常。

          84. Qt的信號(hào)槽機(jī)制非常牛逼,也是Qt的獨(dú)特的核心功能之一,有時(shí)候我們?cè)诤芏啻绑w中傳遞信號(hào)來(lái)實(shí)現(xiàn)更新或者處理,如果窗體層級(jí)比較多,比如窗體A的父類是窗體B,窗體B的父類是窗體C,窗體C有個(gè)子窗體D,如果窗體A一個(gè)信號(hào)要傳遞給窗體D,問(wèn)題來(lái)了,必須先經(jīng)過(guò)窗體B中轉(zhuǎn)到窗體C再到窗體D才行,這樣的話各種信號(hào)關(guān)聯(lián)信號(hào)的connect會(huì)非常多而且管理起來(lái)比較亂,可以考慮增加一個(gè)全局的單例類AppEvent,公共的信號(hào)放這里,然后窗體A對(duì)應(yīng)信號(hào)綁定到AppEvent,窗體D綁定AppEvent的信號(hào)到對(duì)應(yīng)的槽函數(shù)即可,干凈清爽整潔。

          85. QTextEdit右鍵菜單默認(rèn)英文的,如果想要中文顯示,加載widgets.qm文件即可,一個(gè)Qt程序中可以安裝多個(gè)翻譯文件,不沖突。

          86. Qt中有個(gè)全局的焦點(diǎn)切換信號(hào)focusChanged,可以用它做自定義的輸入法。Qt4中默認(rèn)會(huì)安裝輸入法上下文,比如在main函數(shù)打印a.inputContext會(huì)顯示值,這個(gè)默認(rèn)安裝的輸入法上下文,會(huì)攔截兩個(gè)牛逼的信號(hào)QEvent::RequestSoftwareInputPanel和QEvent::CloseSoftwareInputPanel,以至于就算你安裝了全局的事件過(guò)濾器依然識(shí)別不到這兩個(gè)信號(hào),你只需要在main函數(shù)執(zhí)行a.setInputContext(0)即可,意思是安裝輸入法上下文為空。

          87. 在Qt5.10以后,表格控件QTableWidget或者QTableView的默認(rèn)最小列寬改成了15,以前的版本是0,所以在新版的qt中,如果設(shè)置表格的列寬過(guò)小,不會(huì)應(yīng)用,取的是最小的列寬。所以如果要設(shè)置更小的列寬需要重新設(shè)置ui->tableView->horizontalHeader()->setMinimumSectionSize(0);

          88. Qt源碼中內(nèi)置了一些未公開(kāi)的不能直接使用的黑科技,都藏在對(duì)應(yīng)模塊的private中,比如gui-private widgets-private等,比如zip文件解壓類QZipReader、壓縮類QZipWriter就在gui-private模塊中,需要在pro中引入QT += gui-private才能使用。

          #include "QtGui/private/qzipreader_p.h"
          #include "QtGui/private/qzipwriter_p.h"
          
          QZipReader reader(dirPath);
          QString path("");
          //解壓文件夾到當(dāng)前目錄
          
          reader.etractAll(path);
          
          //文件夾名稱
          
          QZipReader::FileInfo fileInfo = reader.entryInfoAt(0);
          
          //解壓文件
          
          QFile file(filePath);
          
          file.open(QIODevice::WriteOnly);
          
          file.write(reader.fileData(QString::fromLocal8Bit("%1").arg(filePath)));
          
          file.close();
          
          reader.close();
          
          QZipWriter *writer = new QZipWriter(dirPath);
          
          //添加文件夾
          
          writer->addDirectory(unCompress);
          
          //添加文件
          
          QFile file(filePath);
          
          file.open(QIODevice::ReadOnly);
          
          writer->addFile(data, file.readAll());
          
          file.close();
          
          writer->close();

          89. 理論上串口和網(wǎng)絡(luò)收發(fā)數(shù)據(jù)都是默認(rèn)異步的,操作系統(tǒng)自動(dòng)調(diào)度,完全不會(huì)卡住界面,網(wǎng)上那些說(shuō)收發(fā)數(shù)據(jù)卡住界面主線程的都是扯幾把蛋,真正的耗時(shí)是在運(yùn)算以及運(yùn)算后的處理,而不是收發(fā)數(shù)據(jù),在一些小數(shù)據(jù)量運(yùn)算處理的項(xiàng)目中,一般不建議動(dòng)用線程去處理,線程需要調(diào)度開(kāi)銷的,不要什么東西都往線程里邊扔,線程不是萬(wàn)能的。只有當(dāng)真正需要將一些很耗時(shí)的操作比如編碼解碼等,才需要移到線程處理。

          90. 在構(gòu)造函數(shù)中獲取控件的寬高很可能是不正確的,需要在控件首次顯示以后再獲取才是正確的,控件是在首次顯示以后才會(huì)設(shè)置好正確的寬高值,記住是在首次顯示以后,而不是構(gòu)造函數(shù)或者程序啟動(dòng)好以后,如果程序啟動(dòng)好以后有些容器控件比如QTabWidget中的沒(méi)有顯示的頁(yè)面的控件,你去獲取寬高很可能也是不正確的,萬(wàn)無(wú)一失的辦法就是首次顯示以后去獲取。

          91. 數(shù)據(jù)庫(kù)處理一般建議在主線程,如果非要在其他線程,務(wù)必記得打開(kāi)數(shù)據(jù)庫(kù)也要在那個(gè)線程,即在那個(gè)線程使用數(shù)據(jù)庫(kù)就在那個(gè)線程打開(kāi),不能打開(kāi)數(shù)據(jù)庫(kù)在主線程,執(zhí)行sql在子線程,很可能出問(wèn)題。

          92. 新版的QTcpServer類在64位版本的Qt下很可能不會(huì)進(jìn)入incomingConnection函數(shù),那是因?yàn)镼t5對(duì)應(yīng)的incomingConnection函數(shù)參數(shù)變了,由之前的int改成了qintptr,改成qintptr有個(gè)好處,在32位上自動(dòng)是quint32而在64位上自動(dòng)是quint64,如果在Qt5中繼續(xù)寫(xiě)的參數(shù)是int則在32位上沒(méi)有問(wèn)題在64位上才有問(wèn)題,所以為了兼容Qt4和Qt5,必須按照不一樣的參數(shù)寫(xiě)。

          #if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
              void incomingConnection(qintptr handle);
          #else
              void incomingConnection(int handle);
          #endif

          93. Qt支持所有的界面控件比如QPushButton、QLineEdit自動(dòng)關(guān)聯(lián) on_控件名_信號(hào)(參數(shù)) 信號(hào)槽,比如按鈕的單擊信號(hào) on_pushButton_clicked(),然后直接實(shí)現(xiàn)槽函數(shù)即可。

          94. QWebEngineView控件由于使用了opengl,在某些電腦上可能由于opengl的驅(qū)動(dòng)過(guò)低會(huì)導(dǎo)致花屏或者各種奇奇怪怪的問(wèn)題,比如showfullscreen的情況下鼠標(biāo)右鍵失效,需要在main函數(shù)啟用軟件opengl渲染。

          #if (QT_VERSION > QT_VERSION_CHECK(5,4,0))
              //下面兩種方法都可以,Qt默認(rèn)采用的是AA_UseDesktopOpenGL
              QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
              //QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
          #endif
              QApplication a(argc, argv);

          另外一個(gè)方法解決 全屏+QWebEngineView控件一起會(huì)產(chǎn)生右鍵菜單無(wú)法彈出的bug,需要上移一個(gè)像素

          QRect rect = qApp->desktop()->geometry();
          rect.setY(-1);
          rect.setHeight(rect.height());
          this->setGeometry(rect);

          95. QStyle內(nèi)置了很多方法用處很大,比如精確獲取滑動(dòng)條鼠標(biāo)按下處的值。

          QStyle::sliderValueFromPosition(minimum(), maximum(), event->x(), width());

          96. 用QFile讀寫(xiě)文件的時(shí)候,推薦用QTextStream文件流的方式來(lái)讀寫(xiě)文件,速度快很多,基本上會(huì)有30%的提升,文件越大性能區(qū)別越大。

          //從文件加載英文屬性與中文屬性對(duì)照表
          QFile fle(":/propertyname.txt");
          
          if (file.open(QFile::ReadOnly)) {
          
              //QTextStream方法讀取速度至少快30%
          
          #if 0
          
              while(!file.atEnd()) {
          
                  QString line = file.readLine();
          
                  appendName(line);
          
              }
          
          #else
          
              QTextStream in(&file);
          
              while (!in.atEnd()) {
          
                  QString line = in.readLine();
          
                  appendName(line);
          
              }
          
          #endif
          
              file.close();
          
          }

          97. 用QFile.readAll()讀取QSS文件默認(rèn)是ANSI格式,不支持UTF8,如果在QtCreator中打開(kāi)qss文件來(lái)編輯保存,這樣很可能導(dǎo)致qss加載以后沒(méi)有效果。

          void frmMain::initStyle()
          {
              //加載樣式表
              QString qss;
              //QFile file(":/qss/psblack.css");
              //QFile file(":/qss/flatwhite.css");
              QFile file(":/qss/lightblue.css");
              if (file.open(QFile::ReadOnly)) {
          #if 1
                  //用QTextStream讀取樣式文件不用區(qū)分文件編碼 帶bom也行
                  QStringList list;
                  QTextStream in(&file);
                  //in.stCodec("utf-8");
          
                  while (!in.atEnd()) {
          
                      QString line;
          
                      in >> line;
          
                      list << line;
          
                  }
          
                  qss = list.join("\n");
          
          #else
          
                  //用readAll讀取默認(rèn)支持的是ANSI格式,如果不小心用creator打開(kāi)編輯過(guò)了很可能打不開(kāi)
          
                  qss = QLatin1String(file.readAll());
          
          #endif
          
                  QString paletteColor = qss.mid(20, 7);
          
                  qApp->setPalette(QPalette(QColor(paletteColor)));
          
                  qApp->setStyleSheet(qss);
          
                  file.close();
          
              }
          
          }

          98. QString內(nèi)置了很多轉(zhuǎn)換函數(shù),比如可以調(diào)用toDouble轉(zhuǎn)為double數(shù)據(jù),但是當(dāng)你轉(zhuǎn)完并打印的時(shí)候你會(huì)發(fā)現(xiàn)精確少了,只剩下三位了,其實(shí)原始數(shù)據(jù)還是完整的精確度的,只是打印的時(shí)候優(yōu)化成了三位,如果要保證完整的精確度,可以調(diào)用 qSetRealNumberPrecision 函數(shù)設(shè)置精確度位數(shù)即可。

          QString s1, s2;
          s1 = "666.5567124";
          s2.setNum(888.5632123, 'f', 7);
          qDebug() << qSetRealNumberPrecision(10) << s1.toDouble() << s2.toDouble();

          99. 用QScriptValueIterator解析數(shù)據(jù)的時(shí)候,會(huì)發(fā)現(xiàn)總是會(huì)多一個(gè)節(jié)點(diǎn)內(nèi)容,并且內(nèi)容為空,如果需要跳過(guò)則增加一行代碼。

          while (it.hasNext()) {
              it.next();    
              if (it.flags() & QScriptValue::SkipInEnumeration)      
                 continue;     
              qDebug() << it.name();
          }

          100. setPixmap是最糟糕的貼圖方式,一般只用來(lái)簡(jiǎn)單的不是很頻繁的貼圖,頻繁的建議painter繪制,默認(rèn)雙緩沖,在高級(jí)點(diǎn)用opengl繪制,利用GPU。

          101. 如果需要在尺寸改變的時(shí)候不重繪窗體,則設(shè)置屬性即可 this->setAttribute(Qt::WA_StaticContents, true); 這樣可以避免可以避免對(duì)已經(jīng)顯示區(qū)域的重新繪制。

          102. 默認(rèn)程序中獲取焦點(diǎn)以后會(huì)有虛邊框,如果看著覺(jué)得礙眼不舒服可以去掉,設(shè)置樣式即可:setStyleSheet("*{outline:0px;}");

          103. Qt表格控件一些常用的設(shè)置封裝,QTableWidget繼承自QTableView,所以下面這個(gè)函數(shù)支持傳入QTableWidget。

          void QUIHelper::initTableView(QTableView *tableView, int rowHeight, bool headVisible, bool edit)
          {
              //奇數(shù)偶數(shù)行顏色交替
              tableView->setAlternatingRowColors(false);
              //垂直表頭是否可見(jiàn)
              tableView->verticalHeader()->setVisible(headVisible);
              //選中一行表頭是否加粗
              tableView->horizontalHeader()->setHighlightSections(false);
              //最后一行拉伸填充
              tableView->horizontalHeader()->setStretchLastSection(true);
              //行標(biāo)題最小寬度尺寸
              tableView->horizontalHeader()->setMinimumSectionSize(0);
              //行標(biāo)題最大高度
              tableView->horizontalHeader()->setMaximumHeight(rowHeight);
              //默認(rèn)行高
              tableView->verticalHeader()->setDefaultSectionSize(rowHeight);
              //選中時(shí)一行整體選中
              tableView->setSelectionBehavior(QAbstractItemView::SelectRows);
              //只允許選擇單個(gè)
              tableView->setSelectionMode(QAbstractItemView::SingleSelection);
              //表頭不可單擊
          #if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
              tableView->horizontalHeader()->setSectionsClickable(false);
          #else
              tableView->horizontalHeader()->setClickable(false);
          #endif
              //鼠標(biāo)按下即進(jìn)入編輯模式
              if (edit) {
                  tableView->setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::DoubleClicked);
              } else {
                  tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
              }
          }

          104. 在一些大的項(xiàng)目中,可能嵌套了很多子項(xiàng)目,有時(shí)候會(huì)遇到子項(xiàng)目依賴其他子項(xiàng)目的時(shí)候,比如一部分子項(xiàng)目用來(lái)生成動(dòng)態(tài)庫(kù),一部分子項(xiàng)目依賴這個(gè)動(dòng)態(tài)庫(kù)進(jìn)行編譯,此時(shí)就需要子項(xiàng)目按照順序編譯。

          TEMPLATE = subdirs
          #設(shè)置ordered參數(shù)以后會(huì)依次編譯 demo designer examples
          CONFIG  += ordered
          SUBDIRS += demo
          SUBDIRS += designer
          SUBDIRS += examples

          105. MSVC編譯器的選擇說(shuō)明

          - 如果是32位的Qt則編譯器選擇x86開(kāi)頭的

          - 如果是64位的Qt則編譯器選擇amd64開(kāi)頭的

          - 具體是看安裝的Qt構(gòu)建套件版本以及目標(biāo)運(yùn)行平臺(tái)的系統(tǒng)位數(shù)和架構(gòu)

          - 一般現(xiàn)在的電腦默認(rèn)以64位的居多,選擇amd64即可

          - 如果用戶需要兼容32位的系統(tǒng)則建議選擇32位的Qt,這樣即可在32位也可以在64位系統(tǒng)運(yùn)行

          - 諸葛大佬補(bǔ)充:x86/x64都是編譯環(huán)境和運(yùn)行環(huán)境相同,沒(méi)有或。帶下劃線的就是交叉編譯,前面是編譯環(huán)境,后面是運(yùn)行環(huán)境。

          | 名稱 | 說(shuō)明 |

          | ------ | ------ |

          |x86|32/64位系統(tǒng)上編譯在32/64位系統(tǒng)上運(yùn)行|

          |x86_amd64|32/64位系統(tǒng)上編譯在64位系統(tǒng)上運(yùn)行|

          |x86_arm|32/64位系統(tǒng)上編譯在arm系統(tǒng)上運(yùn)行|

          |amd64|64位系統(tǒng)上編譯在64位系統(tǒng)上運(yùn)行|

          |amd64_x86|64位系統(tǒng)上編譯在32/64位系統(tǒng)上運(yùn)行|

          |amd64_arm|64位系統(tǒng)上編譯在arm系統(tǒng)上運(yùn)行|

          106. 很多時(shí)候用QDialog的時(shí)候會(huì)發(fā)現(xiàn)阻塞了消息,而有的時(shí)候我們希望是后臺(tái)的一些消息繼續(xù)運(yùn)行不要終止,此時(shí)需要做個(gè)設(shè)置。

          QDialog dialog;
          dialog.setWindowModality(Qt::WindowModal);

          107. 很多初學(xué)者甚至幾年工作經(jīng)驗(yàn)的人,對(duì)多線程有很深的誤解和濫用,尤其是在串口和網(wǎng)絡(luò)通信這塊,什么都往多線程里面丟,一旦遇到界面卡,就把數(shù)據(jù)收發(fā)啥的都搞到多線程里面去,殊不知絕大部分時(shí)候那根本沒(méi)啥用,因?yàn)闆](méi)找到出問(wèn)題的根源。

          - 如果你沒(méi)有使用wait***函數(shù)的話,大部分的界面卡都出在數(shù)據(jù)處理和展示中,比如傳過(guò)來(lái)的是一張圖片的數(shù)據(jù),你需要將這些數(shù)據(jù)轉(zhuǎn)成圖片,這個(gè)肯定是耗時(shí)的;

          - 還有就是就收到的數(shù)據(jù)曲線繪制出來(lái),如果過(guò)于頻繁或者間隔過(guò)短,肯定會(huì)給UI造成很大的壓力的,最好的辦法是解決如何不要頻繁繪制UI比如合并數(shù)據(jù)一起繪制等;

          - 如果是因?yàn)槔L制UI造成的卡,那多線程也是沒(méi)啥用的,因?yàn)閁I只能在主線程;

          - 串口和網(wǎng)絡(luò)的數(shù)據(jù)收發(fā)默認(rèn)都是異步的,由操作系統(tǒng)調(diào)度的,如果數(shù)據(jù)處理復(fù)雜而且數(shù)據(jù)量大,你要做的是將數(shù)據(jù)處理放到多線程中;

          - 如果沒(méi)有嚴(yán)格的數(shù)據(jù)同步需求,根本不需要調(diào)用wait***之類的函數(shù)來(lái)立即發(fā)送和接收數(shù)據(jù),實(shí)際需求中大部分的應(yīng)用場(chǎng)景其實(shí)異步收發(fā)數(shù)據(jù)就足夠了;

          - 有嚴(yán)格數(shù)據(jù)同步需求的場(chǎng)景還是放到多線程會(huì)好一些,不然你wait***就卡在那邊了;

          - 多線程是需要占用系統(tǒng)資源的,理論上來(lái)說(shuō),如果線程數(shù)量超過(guò)了CPU的核心數(shù)量,其實(shí)多線程調(diào)度可能花費(fèi)的時(shí)間更多,各位在使用過(guò)程中要權(quán)衡利弊;

          108. 在嵌入式linux上,如果設(shè)置了無(wú)邊框窗體,而該窗體中又有文本框之類的,發(fā)現(xiàn)沒(méi)法產(chǎn)生焦點(diǎn)進(jìn)行輸入,此時(shí)需要主動(dòng)激活窗體才行。

          //這種方式設(shè)置的無(wú)邊框窗體在嵌入式設(shè)備上無(wú)法產(chǎn)生焦點(diǎn)
          setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);
          //需要在show以后主動(dòng)激活窗體
          w->show();
          w->activateWindow();

          109. QString的replace函數(shù)會(huì)改變?cè)址杏洠诜祷靥鎿Q后的新字符串的同時(shí)也會(huì)改變?cè)址业墓怨裕?/p>

          110. QGraphicsEffect類的相關(guān)效果很炫,可以實(shí)現(xiàn)很多效果比如透明、漸變、陰影等,但是該類很耗CPU,如果不是特別需要一般不建議用,就算用也是要用在該部件后期不會(huì)發(fā)生頻繁繪制的場(chǎng)景,不然會(huì)讓你哭暈在廁所。

          111. 在不同的平臺(tái)上文件路徑的斜杠也是不一樣的,比如linux系統(tǒng)一般都是 / 斜杠,而在windows上都是 \ 兩個(gè)反斜杠,Qt本身程序內(nèi)部無(wú)論在win還是linux都支持 / 斜杠的路徑,但是一些第三方庫(kù)的話可能需要轉(zhuǎn)換成對(duì)應(yīng)系統(tǒng)的路徑,這就需要用到斜杠轉(zhuǎn)換,Qt當(dāng)然內(nèi)置類方法。

          QString path = "C:/temp/test.txt";
          path = QDir::toNativeSeparators(path);
          //輸出 C:\\temp\\test.txt
          QString path = "C:\\temp\\test.txt";
          path = QDir::toNativeSeparators(path);
          //輸出 C:/temp/test.txt

          112. 巧用QMetaObject::invokeMethod方法可以實(shí)現(xiàn)很多效果,包括同步和異步執(zhí)行,比如有個(gè)應(yīng)用場(chǎng)景是在回調(diào)中,需要異步調(diào)用一個(gè)public函數(shù),如果直接調(diào)用的話會(huì)發(fā)現(xiàn)不成功,此時(shí)需要使用 QMetaObject::invokeMethod(obj, "fun", Qt::QueuedConnection); 這種方式來(lái)就可以。invokeMethod函數(shù)有很多重載參數(shù),可以傳入返回值和執(zhí)行方法的參數(shù)等。

          113. Qt5中的信號(hào)是public的,可以在需要的地方直接emit即可,而在Qt4中信號(hào)是protected的,不能直接使用,需要定義一個(gè)public函數(shù)來(lái)emit。

          114. Qt5.15版本開(kāi)始官方不再提供安裝包,只提供源碼,可以自行編譯或者在線安裝,估計(jì)每次編譯各種版本太麻煩,更多的是為了統(tǒng)計(jì)收集用戶使用信息比如通過(guò)在線安裝,后期可能會(huì)逐步加大商業(yè)化力度。

          t 面試指南(內(nèi)含面試題)

          QT開(kāi)發(fā)崗位需要掌握哪些技能?

          QT開(kāi)發(fā)崗位需要掌握C++語(yǔ)言、QT框架、GUI編程、多線程編程等技能。此外,對(duì)于不同的崗位還需要掌握不同的領(lǐng)域知識(shí),如網(wǎng)絡(luò)編程、數(shù)據(jù)庫(kù)編程等。

          如何準(zhǔn)備QT面試?

          首先,需要了解公司的招聘要求和崗位職責(zé),了解面試流程和面試形式。其次,需要準(zhǔn)備面試所需的知識(shí)和技能,可以通過(guò)閱讀相關(guān)書(shū)籍、參加培訓(xùn)課程、做練習(xí)題等方式進(jìn)行。最后,需要進(jìn)行模擬面試,找到自己的不足之處并加以改進(jìn)。

          面試題:(需要pdf文檔的可以進(jìn)企鵝937552610裙嗱)

          一、c++基礎(chǔ)知識(shí)

          1、進(jìn)程和線程的同步方式

          進(jìn)程:1)管道,是內(nèi)核里的一串緩存

          2)消息隊(duì)列

          3)共享內(nèi)存

          4)信號(hào)量機(jī)制

          5)信號(hào)

          6)socket

          線程:1)等待通知機(jī)制

          2)共享內(nèi)存

          3)管道

          5)并發(fā)工具

          信號(hào)量、讀寫(xiě)鎖、互斥鎖和條件變量

          線程的死鎖概念 :線程間相互等待臨界資源而造成彼此無(wú)法繼續(xù)執(zhí)行

          方式一

          1)創(chuàng)建一個(gè)線程類的子對(duì)象,繼承QThread:

          2)重寫(xiě)父類的run()方法,在該函數(shù)內(nèi)部編寫(xiě)子線程要處理的具體業(yè)務(wù)流程

          3)在主線程中創(chuàng)建子線程對(duì)象,new一個(gè)

          4)啟動(dòng)子線程,調(diào)用start()方法

          方式二

          1)創(chuàng)建一個(gè)新的類,QObject派生

          2)類中添加一個(gè)公有的自定義成員函數(shù),函數(shù)體就是子線程中執(zhí)行的業(yè)務(wù)邏輯

          3)主線程中創(chuàng)建一個(gè) QThread 對(duì)象,就是子線程對(duì)象

          4)在主線程中創(chuàng)建工作的類對(duì)象,不要給創(chuàng)建的對(duì)象指定父對(duì)象

          5)Mywork對(duì)象移動(dòng)到創(chuàng)建的子線程對(duì)象中,需要調(diào)用QObject類提供的 moveToThread() 方法

          注意事項(xiàng):

          業(yè)務(wù)對(duì)象, 構(gòu)造的時(shí)候不能指定父對(duì)象

          子線程中不能處理ui窗口(ui相關(guān)的類)

          子線程中只能處理一些數(shù)據(jù)相關(guān)的操作, 不能涉及窗口

          2、什么是堆棧

          棧區(qū)(stack)

          堆區(qū)(heap)抽象數(shù)據(jù)結(jié)構(gòu):后進(jìn)先出

          全局區(qū)(靜態(tài)區(qū))(static)

          文字常量區(qū)

          程序代碼區(qū)

          棧是自動(dòng)分配釋放,一級(jí)緩存,類似數(shù)組的結(jié)構(gòu)。

          堆是由程序員分配釋放,二級(jí)緩存,速度慢些,先進(jìn)后出。

          3、常用的排序

          插入排序、希爾排序、選擇排序、冒泡排序、堆排序、快速排序

          冒泡排序:

          時(shí)間復(fù)雜度:最好O(n),最壞O(n的2次方)。

          空間復(fù)雜度:O(1);

          4、數(shù)組和鏈表的區(qū)別

          1)數(shù)組連續(xù)儲(chǔ)存、固定長(zhǎng)度、增刪需要移動(dòng)其他元素。鏈表動(dòng)態(tài)分配,不連續(xù),需要malloc或

          new申請(qǐng)內(nèi)存,不用時(shí)要free或delete.

          2)數(shù)組訪問(wèn)效率高,鏈表刪插效率高

          3)數(shù)組利用下標(biāo)定位,查找的時(shí)間復(fù)雜度是O(1),鏈表通過(guò)遍歷定位元素,查找的時(shí)間復(fù)雜度是O(N)。

          4)數(shù)組插入和刪除需要移動(dòng)其他元素,時(shí)間復(fù)雜度是O(N),鏈表的插入或刪除不需要移動(dòng)其他元素,時(shí)間復(fù)雜度是O(1)。

          5、回調(diào)函數(shù)的三種典型使用場(chǎng)景:

          1)實(shí)現(xiàn)函數(shù)功能重定義

          2)擴(kuò)展函數(shù)功能

          3)實(shí)現(xiàn)程序分層設(shè)計(jì)

          6、區(qū)分static和const

          static :

          (1) 修飾全局變量:變量只在本模塊內(nèi)可見(jiàn),在定義不需要與其他文件共享的全局變量時(shí),加上 static 關(guān)鍵字能夠有效地降低程序模塊之間的耦合,避免不同文件同名變量的沖突,且不會(huì)誤使用。

          (2) 修飾局部變量:變量在全局?jǐn)?shù)據(jù)區(qū)分配內(nèi)存空間,編譯器自動(dòng)對(duì)其初始化,其作用域?yàn)榫植孔饔糜颍?dāng)定義它的函數(shù)結(jié)束時(shí),其作用域隨之結(jié)束。

          (3) 修飾函數(shù):函數(shù)的使用方式與全局變量類似,在函數(shù)的返回類型前加上 static,就是靜態(tài)函數(shù),靜態(tài)函數(shù)只能在聲明它的文件中可見(jiàn),其他文件不能引用該函數(shù),不同的文件可以使用相同名字的靜態(tài)函數(shù),互不影響。

          const :

          被 const 修飾的變量是只讀變量,本質(zhì)還是變量,有人稱其為常變量,和普通變量的區(qū)別在于常變量不能用于左值,其余的用法和普通常量一樣,變量名不可以改變。

          7、工作中有沒(méi)有使用過(guò)動(dòng)態(tài)庫(kù)和靜態(tài)庫(kù)?能不能簡(jiǎn)單說(shuō)下兩者的區(qū)別?

          答:靜態(tài)庫(kù):在鏈接階段將匯編生成的目標(biāo)文件.o與引用庫(kù)一起鏈接打包到可執(zhí)行文件中,可簡(jiǎn)單看成(.o或者.obj文件的集合)。(1)對(duì)函數(shù)庫(kù)的鏈接是放在編譯時(shí)期完成的(2)程序在運(yùn)行時(shí)與函數(shù)庫(kù)沒(méi)有瓜葛,移植方便(3)浪費(fèi)空間和資源

          動(dòng)態(tài)庫(kù):(1)將庫(kù)函數(shù)的鏈接載入推遲到程序運(yùn)行時(shí)期(2)可以實(shí)現(xiàn)進(jìn)程間的資源共享(因此也稱為共享庫(kù))(3)將一些程序升級(jí)變得簡(jiǎn)單(4)可以真正的做到鏈接載入完全由程序員在程序代碼中控制(顯示調(diào)用)

          8、虛函數(shù):

          父類型的指針指向其子類的實(shí)例

          存在虛函數(shù)的類都有一個(gè)一維的虛函數(shù)表叫做虛表,

          類的對(duì)象有一個(gè)指向虛表開(kāi)始的虛指針。虛表是和類對(duì)應(yīng)的,虛表指針是和對(duì)象對(duì)應(yīng)的。

          默認(rèn)構(gòu)造函數(shù)、初始化構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、移動(dòng)構(gòu)造函數(shù)

          構(gòu)造函數(shù)為什么不能被聲明為虛函數(shù)?

          虛函數(shù)對(duì)應(yīng)一個(gè)vtale,這個(gè)表的地址是存儲(chǔ)在對(duì)象的內(nèi)存空間的。如果將構(gòu)造函數(shù)設(shè)置為虛函數(shù),

          就需要到vtable 中調(diào)用,可是對(duì)象還沒(méi)有實(shí)例化,沒(méi)有內(nèi)存空間分配,如何調(diào)用

          9、STL相關(guān)知識(shí)

          STL由6部分組成:容器(Container)、算法(Algorithm)、 迭代器(Iterator)、

          仿函數(shù)(Function object)、適配器(Adaptor)、空間配制器(Allocator)

          容器、算法、迭代器、仿函數(shù)、適配器、空間配置器

          容器:數(shù)組(array) , 鏈表(list), tree(樹(shù)),棧(stack), 隊(duì)列(queue), 集合(set),映射表(map)

          string容器

          Vector容器是單向開(kāi)口的連續(xù)內(nèi)存空間,deque則是一種雙向開(kāi)口的連續(xù)線性空間

          stack是一種先進(jìn)后出

          Queue是一種先進(jìn)先出

          list容器鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu)

          set/multiset二叉樹(shù)

          map/multimap二叉樹(shù)

          交換,查找,遍歷,復(fù)制,修改,反轉(zhuǎn),排序,合并等

          10、智能指針:auto_ptr(17后已遺棄)、unique_ptr、shared_ptr 和 weak_ptr

          智能指針本質(zhì)就是一個(gè)類模板,當(dāng)智能指針對(duì)象使用完后,對(duì)象就會(huì)自動(dòng)調(diào)用析構(gòu)函數(shù)去釋放該指針?biāo)赶虻目臻g。

          shared_ptr采用的是引用計(jì)數(shù)原理來(lái)實(shí)現(xiàn)多個(gè)shared_ptr對(duì)象之間共享資源:

          shared_ptr在內(nèi)部會(huì)維護(hù)著一份引用計(jì)數(shù),用來(lái)記錄該份資源被幾個(gè)對(duì)象共享。

          當(dāng)一個(gè)shared_ptr對(duì)象被銷毀時(shí)(調(diào)用析構(gòu)函數(shù)),析構(gòu)函數(shù)內(nèi)就會(huì)將該計(jì)數(shù)減1。

          如果引用計(jì)數(shù)減為0后,則表示自己是最后一個(gè)使用該資源的shared_ptr對(duì)象,必須釋放資源。

          如果引用計(jì)數(shù)不是0,就說(shuō)明自己還有其他對(duì)象在使用,則不能釋放該資源,否則其他對(duì)象就成為野指針。

          11、指針和引用的區(qū)別

          指針是一個(gè)實(shí)體,而引用僅是個(gè)別名。

          引用只能在定義時(shí)被初始化一次,之后不可變,但指針可以改變。

          指針可以有多級(jí),但引用只有一級(jí)。

          引用不能為空,指針可以為空。

          sizeof 引用”得到的是所指向的變量(對(duì)象)的大小,而“sizeof 指針”得到的是指針本身的大小

          指針和引用的自增(++)運(yùn)算意義不一樣

          引用是類型安全的,而指針不是 ,引用比指針多了類型檢查。

          引用沒(méi)有const,指針有const,const的指針不可變。

          訪問(wèn)實(shí)體方式不同,指針需要顯式解引用,引用編譯器自己處理

          二、網(wǎng)絡(luò)編程

          1、描述QT的TCP通訊流程

          服務(wù)端:(QTcpServer)

          ①創(chuàng)建QTcpServer對(duì)象

          ②監(jiān)聽(tīng)list需要的參數(shù)是地址和端口號(hào)

          ③當(dāng)有新的客戶端連接成功回發(fā)送newConnect信號(hào)

          ④在newConnection信號(hào)槽函數(shù)中,調(diào)用nextPendingConnection函數(shù)獲取新連接QTcpSocket對(duì)象

          ⑤連接QTcpSocket對(duì)象的readRead信號(hào)

          ⑥在readRead信號(hào)的槽函數(shù)使用read接收數(shù)據(jù)

          ⑦調(diào)用write成員函數(shù)發(fā)送數(shù)據(jù)

          客戶端:(QTcpSocket)

          ①創(chuàng)建QTcpSocket對(duì)象

          ②當(dāng)對(duì)象與Server連接成功時(shí)會(huì)發(fā)送connected 信號(hào)

          ③調(diào)用成員函數(shù)connectToHost連接服務(wù)器,需要的參數(shù)是地址和端口號(hào)

          ④connected信號(hào)的槽函數(shù)開(kāi)啟發(fā)送數(shù)據(jù)

          ⑤使用write發(fā)送數(shù)據(jù),read接收數(shù)據(jù)

          1)長(zhǎng)連接的保活問(wèn)題

          標(biāo)準(zhǔn)TCP層協(xié)議里把對(duì)方超時(shí)設(shè)為2小時(shí),若服務(wù)器端超過(guò)了2小時(shí)還沒(méi)收到客戶的信息,它就發(fā)送探測(cè)報(bào)文段,若發(fā)送了10個(gè)探測(cè)報(bào)文段(每一個(gè)相隔75S)還沒(méi)有收到響應(yīng),就假定客戶出了故障,并終止這個(gè)連接。因此應(yīng)對(duì)tcp長(zhǎng)連接進(jìn)行保活。

          2)TCP提供了強(qiáng)制數(shù)據(jù)立即傳送的操作指令push,TCP軟件收到該操作指令后,就立即將本段數(shù)據(jù)發(fā)送出去,而不必等待發(fā)送緩沖區(qū)滿;

          解決方法二:發(fā)送固定長(zhǎng)度的消息

          解決方法三:把消息的尺寸與消息一塊發(fā)送

          解決方法四:雙方約定每次傳送的大小

          解決方法五:雙方約定使用特殊標(biāo)記來(lái)區(qū)分消息間隔

          解決方法六:標(biāo)準(zhǔn)協(xié)議按協(xié)議規(guī)則處理,如Sip協(xié)議

          3)粘包問(wèn)題

          TCP產(chǎn)生粘包問(wèn)題的主要原因是:TCP是面向連接的,所以在TCP看來(lái),并沒(méi)有把消息看成一條條的,而是全部消息在TCP眼里都是字節(jié)流,

          因此A、B消息混在一起后,TCP就分不清了。

          粘包問(wèn)題的最本質(zhì)原因在與接收對(duì)等方無(wú)法分辨消息與消息之間的邊界在哪。我們通過(guò)使用某種方案給出邊界,例如:

          包頭加上包體長(zhǎng)度。包頭是定長(zhǎng)的4個(gè)字節(jié),說(shuō)明了包體的長(zhǎng)度。接收對(duì)先接收包體長(zhǎng)度,依據(jù)包體長(zhǎng)度來(lái)接收包體。

          解決方法一:TCP提供了強(qiáng)制數(shù)據(jù)立即傳送的操作指令push,TCP軟件收到該操作指令后,就立即將本段數(shù)據(jù)發(fā)送出去,而不必等待發(fā)送緩沖區(qū)滿;

          解決方法二:發(fā)送固定長(zhǎng)度的消息

          解決方法三:把消息的尺寸與消息一塊發(fā)送

          解決方法四:雙方約定每次傳送的大小

          解決方法五:雙方約定使用特殊標(biāo)記來(lái)區(qū)分消息間隔

          解決方法六:標(biāo)準(zhǔn)協(xié)議按協(xié)議規(guī)則處理,如Sip協(xié)議

          發(fā)送端給每個(gè)數(shù)據(jù)包添加包首部,首部中應(yīng)該至少包含數(shù)據(jù)包的長(zhǎng)度,這樣接收端在接收到數(shù)據(jù)后,通過(guò)讀取包首部的長(zhǎng)度字段,便知道每一個(gè)數(shù)據(jù)包的實(shí)際長(zhǎng)度了。

          發(fā)送端將每個(gè)數(shù)據(jù)包封裝為固定長(zhǎng)度(不夠的可以通過(guò)補(bǔ)0填充),這樣接收端每次從接收緩沖區(qū)中讀取固定長(zhǎng)度的數(shù)據(jù)就自然而然的把每個(gè)數(shù)據(jù)包拆分開(kāi)來(lái)。

          可以在數(shù)據(jù)包之間設(shè)置邊界,如添加特殊符號(hào),這樣,接收端通過(guò)這個(gè)邊界就可以將不同的數(shù)據(jù)包拆分開(kāi)。

          指定數(shù)據(jù)長(zhǎng)度的解決方法,主要思路:

          我們?cè)跀?shù)據(jù)結(jié)構(gòu)中有個(gè)成員代表了長(zhǎng)度(消息頭),我們準(zhǔn)備一個(gè)足夠大的消息緩沖區(qū)(程序中是1024000個(gè)字節(jié)),循環(huán)使用socket中的recv每次讀取最多102400個(gè)字節(jié),然后把循環(huán)接收的消息拼接到消息緩沖區(qū)中,直到接收到的消息大于消息頭指示的長(zhǎng)度,則接收到了一個(gè)完整的消息(所以我們的消息緩沖區(qū)要比完整的消息還要大才行),進(jìn)行消息處理。

          【文章福利】Qt開(kāi)發(fā)學(xué)習(xí)資料包、大廠面試題、技術(shù)視頻和學(xué)習(xí)路線圖,包括(Qt C++基礎(chǔ),數(shù)據(jù)庫(kù)編程,Qt項(xiàng)目實(shí)戰(zhàn)、Qt框架、QML、Opencv、qt線程等等)有需要的可以進(jìn)企鵝裙937552610領(lǐng)取哦~

          4)TCP與UDP區(qū)別總結(jié):

          1、TCP面向連接(如打電話要先撥號(hào)建立連接);UDP是無(wú)連接的,即發(fā)送數(shù)據(jù)之前不需要建立連接

          2、TCP提供可靠的服務(wù)。也就是說(shuō),通過(guò)TCP連接傳送的數(shù)據(jù),無(wú)差錯(cuò),不丟失,不重復(fù),且按序到達(dá);UDP盡最大努力交付,即不保 證可靠交付

          3、TCP面向字節(jié)流,實(shí)際上是TCP把數(shù)據(jù)看成一連串無(wú)結(jié)構(gòu)的字節(jié)流;UDP是面向報(bào)文的

          UDP沒(méi)有擁塞控制,因此網(wǎng)絡(luò)出現(xiàn)擁塞不會(huì)使源主機(jī)的發(fā)送速率降低(對(duì)實(shí)時(shí)應(yīng)用很有用,如IP電話,實(shí)時(shí)視頻會(huì)議等)

          4、每一條TCP連接只能是點(diǎn)到點(diǎn)的;UDP支持一對(duì)一,一對(duì)多,多對(duì)一和多對(duì)多的交互通信

          5、TCP首部開(kāi)銷20字節(jié);UDP的首部開(kāi)銷小,只有8個(gè)字節(jié)

          6、TCP的邏輯通信信道是全雙工的可靠信道,UDP則是不可靠信道

          7、三次握手和四次揮手具體流程

          SYN:請(qǐng)求建立連接,F(xiàn)IN:請(qǐng)求斷開(kāi)連接,ACK:確認(rèn)是否有效, seq:序列號(hào), ack:確認(rèn)號(hào)

          1)三次握手

          1.客戶端向服務(wù)端發(fā)送?個(gè)SYN=1(請(qǐng)求建立連接),并生成一個(gè)序列號(hào)seq=j。

          2.服務(wù)端接收到SYN=1后,給客戶端發(fā)送?個(gè)SYN=1與ACK=1;并將ack置為j+1;同時(shí)生成一個(gè)序列號(hào)seq=k。

          3.客戶端接收到會(huì)檢查ack是否為j+1與ACK是否為1,如果是,則會(huì)給服務(wù)端發(fā)送一個(gè)ACK=1與ack=k+1,以及自己的序列號(hào)seq=j=1; 服務(wù)端接收到會(huì)檢查ACK是否為1與ack是否為k+1,如果是則代表連接建立成功,兩者間可以傳遞數(shù)據(jù)。

          2)四次揮手

          1.客戶端向服務(wù)端發(fā)送FIN=1(請(qǐng)求關(guān)閉連接),并生成一個(gè)序列號(hào)seq=x。

          2.服務(wù)端接收FIN后,向客戶端發(fā)送ACK=1,ack=x+1,并生成序列號(hào)seq=y(客戶端無(wú)數(shù)據(jù)發(fā)送,但服務(wù)器端需發(fā)送完最后的數(shù)據(jù))。

          3.服務(wù)端處理完所有數(shù)據(jù)后,向客戶端發(fā)送FIN=1與ACK=1,ack=x+1,并生成序列號(hào)z,表示服務(wù)端現(xiàn)在可以斷開(kāi)連接。

          4.客戶端收到服務(wù)端的數(shù)據(jù)包后,會(huì)向服務(wù)端發(fā)送ACK=1,seq=x=1,ack=z+1(需要等待2MSL后才可斷開(kāi)連接)。

          8、指針和引用的區(qū)別

          指針是一個(gè)實(shí)體,而引用僅是個(gè)別名。

          引用只能在定義時(shí)被初始化一次,之后不可變,但指針可以改變。

          指針可以有多級(jí),但引用只有一級(jí)。

          引用不能為空,指針可以為空。

          sizeof 引用”得到的是所指向的變量(對(duì)象)的大小,而“sizeof 指針”得到的是指針本身的大小

          指針和引用的自增(++)運(yùn)算意義不一樣

          引用是類型安全的,而指針不是 ,引用比指針多了類型檢查。

          引用沒(méi)有const,指針有const,const的指針不可變。

          訪問(wèn)實(shí)體方式不同,指針需要顯式解引用,引用編譯器自己處理

          9、HTTP 是一種 超文本傳輸協(xié)議

          因特網(wǎng)的協(xié)議棧由五個(gè)部分組成:物理層、鏈路層、網(wǎng)絡(luò)層、運(yùn)輸層和應(yīng)用層

          URL(即網(wǎng)址)

          瀏覽器會(huì)向DNS(域名服務(wù)器,后面會(huì)說(shuō))提供網(wǎng)址

          HTML 稱為超文本標(biāo)記語(yǔ)言

          http協(xié)議是應(yīng)用層協(xié)議,主要是解決如何包裝數(shù)據(jù)。而tcp協(xié)議是傳輸層協(xié)議,主要解決數(shù)據(jù)如何在網(wǎng)絡(luò)中傳輸。

          通俗點(diǎn)說(shuō),http的任務(wù)是與服務(wù)器交換信息,它不管怎么連到服務(wù)器和保證數(shù)據(jù)正確的事情。而tcp的任務(wù)是保證連接的可靠,它只管連接,它不管連接后要傳什么數(shù)據(jù)。

          http協(xié)議是建立在tcp之上的,http是無(wú)狀態(tài)的短鏈接,而tcp是有狀態(tài)的長(zhǎng)鏈接。

          HTTPS:是以安全為目標(biāo)的 HTTP 通道,是 HTTP 的安全版。HTTPS 的安全基礎(chǔ)是 SSL。

          SSL 協(xié)議位于 TCP/IP 協(xié)議與各種應(yīng)用層協(xié)議之間,為數(shù)據(jù)通訊提供安全支持。

          二、HTTP 與 HTTPS 的區(qū)別

          1、HTTPS 協(xié)議需要到 CA (Certificate Authority,證書(shū)頒發(fā)機(jī)構(gòu))申請(qǐng)證書(shū),一般免費(fèi)證書(shū)較少,因而需要一定費(fèi)用。

          (以前的網(wǎng)易官網(wǎng)是http,而網(wǎng)易郵箱是 https 。)

          2、HTTP 是超文本傳輸協(xié)議,信息是明文傳輸,HTTPS 則是具有安全性的 SSL 加密傳輸協(xié)議。

          3、HTTP 和 HTTPS 使用的是完全不同的連接方式,用的端口也不一樣,前者是80,后者是443。

          4、HTTP 的連接很簡(jiǎn)單,是無(wú)狀態(tài)的。HTTPS 協(xié)議是由 SSL+HTTP 協(xié)議構(gòu)建的可進(jìn)行加密傳輸、身份認(rèn)證的網(wǎng)絡(luò)協(xié)議,

          比 HTTP 協(xié)議安全。(無(wú)狀態(tài)的意思是其數(shù)據(jù)包的發(fā)送、傳輸和接收都是相互獨(dú)立的。

          無(wú)連接的意思是指通信雙方都不長(zhǎng)久的維持對(duì)方的任何信息。)

          三、QT相關(guān)知識(shí)

          1、什么是元對(duì)象系統(tǒng)

          元對(duì)象系統(tǒng)是一個(gè)基于標(biāo)準(zhǔn)C++的擴(kuò)展,為QT提供了信號(hào)與槽機(jī)制、實(shí)時(shí)類型信息、動(dòng)態(tài)屬性系統(tǒng)。

          元對(duì)象系統(tǒng)的三個(gè)基本條件:類必須繼承自QObject、類聲明Q_OBJECT宏(默認(rèn)私有)、元對(duì)象編譯器moc。

          信號(hào)與槽類似觀察者模式;

          回調(diào)函數(shù)的本質(zhì)是基于“你想讓別人的代碼執(zhí)行你的代碼,而別人的代碼你又不能動(dòng)”這種產(chǎn)生的;

          對(duì)象樹(shù);

          信號(hào)與槽的實(shí)現(xiàn)是借助了Qt 的元對(duì)象系統(tǒng),元對(duì)象系統(tǒng)有一個(gè)元對(duì)象編譯器,

          程序編譯之前會(huì)有一個(gè)預(yù)處理過(guò)程,預(yù)處理將一個(gè)類/對(duì)象中的信號(hào),槽的字符串值分別保存在一個(gè)容器中,可能是字符串或者其他的有序容器

          第5個(gè)參數(shù)跟線程相關(guān)Qt::AutoConnection

          1)Qt信號(hào)槽機(jī)制的優(yōu)勢(shì)

          (1)類型安全。需要關(guān)聯(lián)的信號(hào)和槽的簽名必須是等同的,

          (2)松散耦合。

          (3)信號(hào)和槽機(jī)制增強(qiáng)了對(duì)象間通信的靈活性。一個(gè)信號(hào)可以關(guān)聯(lián)多個(gè)槽,也可以多個(gè)信號(hào)關(guān)聯(lián)一個(gè)槽。

          2)Qt信號(hào)槽機(jī)制的不足

          同回調(diào)函數(shù)相比,信號(hào)和槽機(jī)制運(yùn)行速度有些慢。通過(guò)傳遞一個(gè)信號(hào)來(lái)調(diào)用槽函數(shù)將會(huì)比直接調(diào)用非虛函數(shù)運(yùn)行速度慢10倍。原因如下:

          (1)需要定位接收信號(hào)的對(duì)象;

          (2)安全地遍歷所有的關(guān)聯(lián)(如一個(gè)信號(hào)關(guān)聯(lián)多個(gè)槽的情況);

          (3)編組/解組傳遞的參數(shù);

          (4)多線程的時(shí)候,信號(hào)可能需要排隊(duì)等待。

          然而,與創(chuàng)建對(duì)象的new操作及刪除對(duì)象的delete操作相比,信號(hào)和槽的運(yùn)行代價(jià)只是他們很少的一部分。信號(hào)和槽機(jī)制導(dǎo)致的這點(diǎn)性能損耗,對(duì)實(shí)時(shí)應(yīng)用程序是可以忽略的。

          知道QT事件機(jī)制有幾種級(jí)別的事件過(guò)濾嗎?能大致描述下嗎?

          答:根據(jù)對(duì)Qt事件機(jī)制的分析, 我們可以得到5種級(jí)別的事件過(guò)濾,處理辦法. 以功能從弱到強(qiáng), 排列如下:

          1)重載特定事件處理函數(shù).

          最常見(jiàn)的事件處理辦法就是重載象mousePressEvent(), keyPressEvent(), paintEvent() 這樣的特定事件處理函數(shù).

          2)重載event()函數(shù).

          通過(guò)重載event()函數(shù),我們可以在事件被特定的事件處理函數(shù)處理之前(象keyPressEvent())處理它. 比如, 當(dāng)我們想改變tab鍵的默認(rèn)動(dòng)作時(shí),一般要重載這個(gè)函數(shù). 在處理一些不常見(jiàn)的事件(比如:LayoutDirectionChange)時(shí),evnet()也很有用,因?yàn)檫@些函數(shù)沒(méi)有相應(yīng)的特定事件處理函數(shù). 當(dāng)我們重載event()函數(shù)時(shí), 需要調(diào)用父類的event()函數(shù)來(lái)處理我們不需要處理或是不清楚如何處理的事件.

          3) 在Qt對(duì)象上安裝事件過(guò)濾器.

          安裝事件過(guò)濾器有兩個(gè)步驟: (假設(shè)要用A來(lái)監(jiān)視過(guò)濾B的事件)

          首先調(diào)用B的installEventFilter( const QOject *obj ), 以A的指針作為參數(shù). 這樣所有發(fā)往B的事件都將先由A的eventFilter()處理.

          然后, A要重載QObject::eventFilter()函數(shù), 在eventFilter() 中書(shū)寫(xiě)對(duì)事件進(jìn)行處理的代碼.

          4) 給QAppliction對(duì)象安裝事件過(guò)濾器.

          一旦我們給qApp(每個(gè)程序中唯一的QApplication對(duì)象)裝上過(guò)濾器,那么所有的事件在發(fā)往任何其他的過(guò)濾器時(shí),都要先經(jīng)過(guò)當(dāng)前這個(gè) eventFilter(). 在debug的時(shí)候,這個(gè)辦法就非常有用, 也常常被用來(lái)處理失效了的widget的鼠標(biāo)事件,通常這些事件會(huì)被QApplication::notify()丟掉. ( 在QApplication::notify() 中, 是先調(diào)用qApp的過(guò)濾器, 再對(duì)事件進(jìn)行分析, 以決定是否合并或丟棄)

          5) 繼承QApplication類,并重載notify()函數(shù).

          Qt 是用QApplication::notify()函數(shù)來(lái)分發(fā)事件的.想要在任何事件過(guò)濾器查看任何事件之前先得到這些事件,重載這個(gè)函數(shù)是唯一的辦法. 通常來(lái)說(shuō)事件過(guò)濾器更好用一些, 因?yàn)椴恍枰ダ^承QApplication類. 而且可以給QApplication對(duì)象安裝任意個(gè)數(shù)的事件。

          自定義界面

          UI設(shè)計(jì)器集成了Qt樣式表的編輯功能

          前景色color

          背景色background-color

          選中后顏色selection-color

          背景圖片background-image

          選擇器:QPushButton 、QDialog QPushButton、QPushButton#btnOk

          子控件:drop-down、up-button、down-button

          偽狀態(tài):hover(鼠標(biāo)移動(dòng)到條目上方時(shí)),active(處于活動(dòng)的窗體)

          屬性:min_width、padding-top、border-width、

          1、使用Qt Designer

          2、qApp->setStyleSheet(“QLineEdit{ background-color: gray}”);

          自定義QT控件

          1、自定義Widget子類QmyBattery

          paintEvent()事件

          QmyBattery繼承于QWidget

          Q_UNUSED(event)

          QPainter/QRect/QColor

          提升法

          2、自定義Qt Designer插件

          編譯器用MSVC2015 32bit

          Q_INTERFACES聲明了實(shí)現(xiàn)接口

          Q_PLUGIN_METADATA聲明了元數(shù)據(jù)名稱

          要把dll和lib放到插件目錄下

          D:\Qt\Qt5.9.1\Tools\QtCreator\bin\plugins\designer

          D:\Qt\Qt5.9.1.9.1\msvc2015\plugins\designer

          QWT,是一個(gè)基于LGPL版權(quán)協(xié)議的開(kāi)源項(xiàng)目, 可生成各種統(tǒng)計(jì)圖

          QT和MFC消息機(jī)制比較:

          mfc的消息機(jī)制其實(shí)就是消息映射機(jī)制,程序員需要將自定義消息和對(duì)應(yīng)的處理函數(shù)添加到消息映射表中。通過(guò)PostMessage和SendMessage來(lái)實(shí)現(xiàn)異步和同步消息。

          QT的信號(hào)槽機(jī)制是信號(hào)和槽函數(shù)通過(guò)QObject::connect動(dòng)態(tài)鏈接上后存儲(chǔ)到元對(duì)象系統(tǒng)中,通過(guò)emit發(fā)送信號(hào),對(duì)應(yīng)的槽函數(shù)執(zhí)行。

          比較

          Qt的信號(hào)槽是動(dòng)態(tài)鏈接的,而MFC的消息映射是靜態(tài)的

          Qt的信號(hào)支持自定義參數(shù),且類型安全

          在多線程中,MFC需要向已知線程對(duì)象發(fā)布消息,而Qt可以不考慮多線程之間的信號(hào)槽關(guān)系

          總結(jié)

          Qt相比較MFC的消息機(jī)制,使用起來(lái)更方便,最大的優(yōu)勢(shì)是Qt支持動(dòng)態(tài)鏈接信號(hào)槽。

          四、項(xiàng)目相關(guān)

          1、定位問(wèn)題:

          請(qǐng)問(wèn)下,如果軟件除了問(wèn)題(Bug),如何快速定位?主要方法有哪些?

          答:打印輸出/代碼調(diào)試/日志記錄/分析工具/找同事討論。

          1)二分法定位技巧

          無(wú)論是有多復(fù)雜的代碼,利用二分法定位技巧一般都是可以定位到問(wèn)題所在。

          從二分法定位技巧可以延伸出一些具體的處理bug的方法,比如:對(duì)輸入數(shù)據(jù)二分、對(duì)代碼版本二分、注釋掉部分代碼、在不同位置插入試探性代碼、對(duì)運(yùn)行環(huán)境二分。

          2)IDE調(diào)試

          IDE的VS debug的功能簡(jiǎn)直就是立竿見(jiàn)影。它可以加斷點(diǎn),單步調(diào)試。

          單步調(diào)試可以讓我們對(duì)代碼邏輯,執(zhí)行順序,以及各種中間結(jié)果更加清晰。

          至于本身容易出錯(cuò)的BUG,用IDE調(diào)試簡(jiǎn)直是再合適不過(guò)了。

          3)重新讀一遍程序

          相對(duì)新手程序員來(lái)說(shuō),如果代碼出現(xiàn)bug,可以重新讀一遍程序。這種方法是最有效、最快速的 Debug 方式。

          4)必殺,重寫(xiě)一遍

          如果你發(fā)現(xiàn)無(wú)論如何也找不到BUG,而且代碼只是復(fù)雜,本身不是很長(zhǎng),直接重寫(xiě)代碼吧!

          5)小黃鴨調(diào)試法

          小黃鴨調(diào)試法是程序員們經(jīng)常使用的調(diào)試代碼方法之一。

          小黃鴨不懂程序,所以我們可以向他解釋每一行程序的作用,以此來(lái)激發(fā)靈感。

          內(nèi)存泄露及解決辦法:

          什么是內(nèi)存泄露?

          簡(jiǎn)單地說(shuō)就是申請(qǐng)了一塊內(nèi)存空間,使用完畢后沒(méi)有釋放掉。(1)new和malloc申請(qǐng)資源使用后,沒(méi)有用delete和free釋放;(2)子類繼承父類時(shí),父類析構(gòu)函數(shù)不是虛函數(shù)。(3)Windows句柄資源使用后沒(méi)有釋放。

          怎么檢測(cè)?

          第一:良好的編碼習(xí)慣,使用了內(nèi)存分配的函數(shù),一旦使用完畢,要記得使用其相應(yīng)的函數(shù)釋放掉。

          第二:將分配的內(nèi)存的指針以鏈表的形式自行管理,使用完畢之后從鏈表中刪除,程序結(jié)束時(shí)可檢查改鏈表。

          第三:使用智能指針。

          第四:一些常見(jiàn)的工具插件,如ccmalloc、Dmalloc、Leaky、Valgrind等等。

          數(shù)據(jù)庫(kù)相關(guān)

          什么是事務(wù)

          并發(fā)控制

          原子性、一致性、隔離性、持續(xù)性

          valueChanged(int)

          QMutex mutex;

          QMutexLocker locker(&mutex);

          waitForReadyRead()

          常用的設(shè)計(jì)結(jié)構(gòu):

          工廠方法 Factory Method 定義了創(chuàng)建對(duì)象的接口,讓子類決定實(shí)例化哪個(gè)類

          單例 Singleton 確保一個(gè)類只有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)

          原型 Prototype 通過(guò)拷貝原型對(duì)象創(chuàng)建新的對(duì)象。

          適配器 Adapter 將一個(gè)類的接口轉(zhuǎn)換成希望的另外一個(gè)接口,使得原本不兼容的接口可以協(xié)同工作。

          觀察者 Observer 定義對(duì)象間的一種一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),所有依賴于它的對(duì)象都得到通知并被自動(dòng)更新。

          單例:懶漢模式和餓漢模式

          餓漢就是類一旦加載,就把單例初始化完成,保證getInstance的時(shí)候,單例是已經(jīng)存在的了;線程安全。

          而懶漢比較懶,只有當(dāng)調(diào)用getInstance的時(shí)候,才回去初始化這個(gè)單例;

          線程不安全,就只有在實(shí)例化之前調(diào)用的時(shí)候加鎖,后面不加鎖。

          C++新特性主要包括包含語(yǔ)法改進(jìn)和標(biāo)準(zhǔn)庫(kù)擴(kuò)充兩個(gè)方面,主要包括以下11點(diǎn):

          語(yǔ)法的改進(jìn)

          (1)統(tǒng)一的初始化方法,允許構(gòu)造函數(shù)或其他函數(shù)像參數(shù)一樣使用初始化列表

          (2)成員變量默認(rèn)初始化

          (3)類型推導(dǎo) auto關(guān)鍵字 用于定義變量,編譯器可以自動(dòng)判斷的類型(前提:定義一個(gè)變量時(shí)對(duì)其進(jìn)行初始化)

          (4)decltype 求表達(dá)式的類型

          (5)智能指針 shared_ptr

          (6)空指針 nullptr(原來(lái)NULL)

          nullptr表示空指針,是一個(gè)關(guān)鍵詞

          NULL是老版本的,是一個(gè)0的宏

          (7)基于范圍的for循環(huán)

          (8)右值引用和move語(yǔ)義 讓程序員有意識(shí)減少進(jìn)行深拷貝操作

          標(biāo)準(zhǔn)庫(kù)擴(kuò)充(往STL里新加進(jìn)一些模板類,比較好用)

          (9)無(wú)序容器(哈希表) 用法和功能同map一模一樣,區(qū)別在于哈希表的效率更高

          (10)正則表達(dá)式 可以認(rèn)為正則表達(dá)式實(shí)質(zhì)上是一個(gè)字符串,該字符串描述了一種特定模式的字符串

          (11)Lambda表達(dá)式

          息隊(duì)列

          “消息隊(duì)列(MQ)”是在消息的傳輸過(guò)程中保存消息的容器。

          消息隊(duì)列正如同一種先進(jìn)先出的隊(duì)列結(jié)構(gòu),它將發(fā)送方的消息推入隊(duì)列中,并依序推送給接收方。消息隊(duì)列相關(guān)的通信協(xié)議都屬于應(yīng)用層協(xié)議,位于OSI模型第七層,是基于TCP/IP的通信協(xié)議。

          與TCP、UDP或是HTTP協(xié)議不同,MQ相關(guān)協(xié)議沒(méi)有服務(wù)端和客戶端的概念。原本的客戶端和服務(wù)端,現(xiàn)在都通過(guò)一個(gè)中間件服務(wù)器(broker)交互,消息的發(fā)送方稱為生產(chǎn)者,消息的接收方成為消費(fèi)者,生產(chǎn)者和消費(fèi)者都可以視同broker的客戶端。

          通過(guò)這種設(shè)計(jì),所有消息都被存放于一個(gè)中間服務(wù)器中,通信的雙方不再需要?jiǎng)?chuàng)建服務(wù)。這樣做帶來(lái)了幾個(gè)好處:解耦,異步調(diào)用,削峰。

          解耦:通過(guò)中間件,各個(gè)系統(tǒng)之間可以獨(dú)立運(yùn)行,不會(huì)因?yàn)槠渲幸粋€(gè)系統(tǒng)的崩潰影響其他系統(tǒng),且整個(gè)系統(tǒng)的可拓展性也大大加強(qiáng)。

          異步:發(fā)送方的消息推入了中間件,這條消息可以被所有相關(guān)的接收方看到,因此它們可以同時(shí)開(kāi)始處理,這種串聯(lián)的結(jié)構(gòu)的時(shí)間消耗比其他的串行結(jié)構(gòu)小得多。

          削峰:在高并發(fā)環(huán)境下,短時(shí)間的大量請(qǐng)求會(huì)導(dǎo)致系統(tǒng)和數(shù)據(jù)庫(kù)發(fā)生很多問(wèn)題,所以需要對(duì)流量進(jìn)行控制,通過(guò)消息隊(duì)列設(shè)置每秒向消費(fèi)者投遞的消息數(shù)量,可以控制并發(fā)環(huán)境下的系統(tǒng)穩(wěn)定性。

          但是,消息隊(duì)列同樣有它的不足。如降低系統(tǒng)可用性,增加系統(tǒng)的復(fù)雜性和一致性問(wèn)題等。因此,是否使用消息隊(duì)列也必須根據(jù)實(shí)際應(yīng)用來(lái)決定。

          基于消息隊(duì)列的通信協(xié)議有很多,常見(jiàn)的有RabbitMQ,Kafka,還有本文介紹的MQTT。

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

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

          MQTT

          MQTT(Message Queuing Telemetry Transport,消息隊(duì)列遙測(cè)傳輸協(xié)議),是一種基于發(fā)布/訂閱(publish/subscribe)模式的"輕量級(jí)"通訊協(xié)議,該協(xié)議構(gòu)建于TCP/IP協(xié)議上,由IBM在1999年發(fā)布。MQTT最大優(yōu)點(diǎn)在于,可以以極少的代碼和有限的帶寬,為連接遠(yuǎn)程設(shè)備提供實(shí)時(shí)可靠的消息服務(wù)。作為一種低開(kāi)銷、低帶寬占用的即時(shí)通訊協(xié)議,使其在物聯(lián)網(wǎng)、小型設(shè)備、移動(dòng)應(yīng)用等方面有較廣泛的應(yīng)用。

          實(shí)現(xiàn)MQTT協(xié)議需要客戶端和服務(wù)器端通訊完成,在通訊過(guò)程中,MQTT協(xié)議中有三種身份:發(fā)布者(Publish)、代理(Broker)(服務(wù)器)、訂閱者(Subscribe)。其中,消息的發(fā)布者和訂閱者都是客戶端,消息代理是服務(wù)器,消息發(fā)布者可以同時(shí)是訂閱者。

          MQTT會(huì)構(gòu)建底層網(wǎng)絡(luò)傳輸:它將建立客戶端到服務(wù)器的連接,提供兩者之間的一個(gè)有序的、無(wú)損的、基于字節(jié)流的雙向傳輸。當(dāng)應(yīng)用數(shù)據(jù)通過(guò)MQTT網(wǎng)絡(luò)發(fā)送時(shí),MQTT會(huì)把與之相關(guān)的服務(wù)質(zhì)量(QoS)和主題名(Topic)相關(guān)連。

          MQTT中的幾個(gè)重要概念:

          訂閱(Subscription)

          訂閱包含主題篩選器(Topic Filter)和最大服務(wù)質(zhì)量(QoS)。訂閱會(huì)與一個(gè)會(huì)話(Session)關(guān)聯(lián)。一個(gè)會(huì)話可以包含多個(gè)訂閱。每一個(gè)會(huì)話中的每個(gè)訂閱都有一個(gè)不同的主題篩選器。

          會(huì)話(Session)

          每個(gè)客戶端與服務(wù)器建立連接后就是一個(gè)會(huì)話,客戶端和服務(wù)器之間有狀態(tài)交互。會(huì)話存在于一個(gè)網(wǎng)絡(luò)之間,也可能在客戶端和服務(wù)器之間跨越多個(gè)連續(xù)的網(wǎng)絡(luò)連接。

          主題名(Topic Name)

          連接到一個(gè)應(yīng)用程序消息的標(biāo)簽,該標(biāo)簽與服務(wù)器的訂閱相匹配。服務(wù)器會(huì)將消息發(fā)送給訂閱所匹配標(biāo)簽的每個(gè)客戶端。

          主題篩選器(Topic Filter)

          一個(gè)對(duì)主題名通配符篩選器,在訂閱表達(dá)式中使用,表示訂閱所匹配到的多個(gè)主題。

          負(fù)載(Payload)

          消息訂閱者所具體接收的內(nèi)容。

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

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

          配置Qt-MQTT環(huán)境

          默認(rèn)的Qt環(huán)境是不能使用MQTT的,但Qt官方提供了基于MQTT的封裝,需要通過(guò)源碼進(jìn)行編譯。

          在dev分支中可以選擇MQTT版本,選擇最新的下載到本地。

          下載下來(lái)的是一個(gè)Qt項(xiàng)目,在Qt Creator中打開(kāi).pro文件,然后使用Release模式,用你所需要的編譯器(VS,MinGW…),開(kāi)始編譯。

          如果你的系統(tǒng)沒(méi)有安裝過(guò)Perl,需要先安裝Perl,并加入到系統(tǒng)環(huán)境變量中。

          完成編譯后,可以在你的編譯路徑的/bin目錄中得到所需的動(dòng)態(tài)鏈接庫(kù)文件Qt5Mqtt.dll和Qt5Mqttd.dll。前者是release版庫(kù),后者是debug版。

          為了實(shí)現(xiàn)一次配置,所有項(xiàng)目可用的目的,我們可以直接將MQTT配置到系統(tǒng)的Qt環(huán)境中去。C++的編譯機(jī)制是通過(guò)頭文件和靜態(tài)鏈接庫(kù)編譯出動(dòng)態(tài)鏈接庫(kù),再通過(guò)頭文件和動(dòng)態(tài)鏈接庫(kù)運(yùn)行程序。所以這里我們要將前面編譯出的靜態(tài)鏈接庫(kù)和動(dòng)態(tài)鏈接庫(kù)都復(fù)制到Qt環(huán)境中去。

          首先,將qtmqtt源碼目錄下(qtmqtt/src/mqtt)的所有.h頭文件拷貝,在Qt安裝目錄下的include文件夾中創(chuàng)建一個(gè)mqtt目錄,將拷貝的文件粘貼進(jìn)去。

          然后,將源碼編譯生成目錄下的靜態(tài)鏈接庫(kù)相關(guān)文件拷貝到Qt安裝目錄的/lib下,

          依次為Qt5Mqtt.lib(.a) Qt5Mqtt.prl Qt5mqttd.lib(.a) Qt5Mqttd.prl。

          再將編譯生成的兩個(gè)動(dòng)態(tài)鏈接庫(kù)拷貝到Qt安裝目錄的/bin下,

          依次為Qt5Mqtt.dll Qt5Mqttd.dll。

          最后再拷貝模塊配置文件到Qt安裝目錄中。

          這樣MQTT就已經(jīng)配置到我們本地的Qt環(huán)境中了。后續(xù)所有使用此Qt環(huán)境的項(xiàng)目都可以直接使用MQTT了。

          使用MQTT時(shí),首先要在.pro中添加模塊:

          QT += mqtt

          在使用前引入包:

          #include <QtMqtt/qmqttclient.h>

          編寫(xiě)代碼可以參考Qt官方的MQTT說(shuō)明文檔:

          https://doc.qt.io/qt-6/qtmqtt-index.html


          搭建EMQ X服務(wù)器

          為了調(diào)試程序,我們需要一臺(tái)MQTT服務(wù)器。EMQ公司官方提供了測(cè)試的MQTT服務(wù)器,但由于連接數(shù)眾多,不太穩(wěn)定,我們需要自己搭建一臺(tái)MQTT服務(wù)器。

          EMQ X提供了開(kāi)源版的EMQ X服務(wù)器安裝包,支持Windows,Ubuntu等多種使用環(huán)境。

          安裝后,Windows用戶使用管理員權(quán)限命令行進(jìn)入安裝路徑下,進(jìn)入/emqx/bin/,依次執(zhí)行命令

          #先運(yùn)行該命令
          emqx install
          #成功后界面上會(huì)ChangeServiceConfig 成功
          #再運(yùn)行
          emqx console
          #運(yùn)行成功后會(huì)顯示emqx is started!
          #然后會(huì)跳出一個(gè)界面,打開(kāi)emqx運(yùn)行所需要的各個(gè)端口
          #最后運(yùn)行
          emqx start
          #沒(méi)有報(bào)錯(cuò)就執(zhí)行成功了
          

          Linux用戶在安裝路徑下執(zhí)行下述命令即可

          emqx start

          這樣本地就開(kāi)啟了MQTT服務(wù),這里有兩個(gè)重要的端口號(hào)要記住:1883(暴露給外部的MQTT服務(wù)端口),18083(服務(wù)器控制面板端口)。在本地瀏覽器輸入http://127.0.0.1:18083/,打開(kāi)服務(wù)器控制面板。輸入初始用戶名admin和用戶密碼public,即可進(jìn)入控制面板,并進(jìn)行MQTT服務(wù)器相關(guān)配置。

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

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

          調(diào)試軟件MQTT X

          為了調(diào)試程序,我們通常需要一個(gè)調(diào)試軟件來(lái)模擬消息的收發(fā),這里推薦使用MQTT X軟件進(jìn)行調(diào)試。

          MQTT X下載連接:https://mqttx.app/zh

          MQTT X使用文檔:https://mqttx.app/zh/docs

          安裝完成后,點(diǎn)擊+圖標(biāo)可以添加連接。

          這里的Name和Client ID隨意,Host填寫(xiě)我們本地配置的MQTT服務(wù)器地址127.0.0.1,端口號(hào)填1883。點(diǎn)擊Connect即可連接到本地。

          連接后,點(diǎn)擊New Subscription創(chuàng)建topic,然后就可以在該topic下收發(fā)消息。


          Qt-MQTT編程

          這里給出一個(gè)Qt-MQTT的程序樣例,包含了基礎(chǔ)的連接,收,發(fā),斷開(kāi)等功能,讀者可以在此基礎(chǔ)上二次開(kāi)發(fā)。

          .h

          #ifndef MY_MQTT_CLIENT_H
          #define MY_MQTT_CLIENT_H
          
          #include <QObject>
          #include <QDateTime>
          #include <QtMqtt/qmqttclient.h>
          
          namespace Ui {
          class MyMQTTClient;
          }
          
          using namespace std;
          
          class MyMQTTClient : public QObject
          {
              Q_OBJECT
          public:
              explicit MyMQTTClient(QObject *parent = nullptr);
              ~MyMQTTClient(){
              };
          
              QMqttClient *m_client = nullptr;
          
              void MyMQTTSubscribe(QString);
              void MyMQTTSendMessage(const QString, const QString);
          
          signals:
          
          public slots:
              void brokerConnected();
              void updateLogStateChange();
              void brokerDisconnected();
              void receiveMess(const QByteArray &, const QMqttTopicName &);
          
          private:
          
          };
          
          #endif // MY_MQTT_CLIENT_H

          .cpp


          主站蜘蛛池模板: 一区五十路在线中出| 一区二区三区视频在线| 国产av熟女一区二区三区| 国产主播一区二区| 一区二区免费视频| 国产精品久久久久一区二区三区| 日韩免费视频一区二区| 久久无码人妻一区二区三区| 久久精品国内一区二区三区| 一区二区三区亚洲视频| 亚洲AV无码一区二区二三区入口 | 亚洲福利秒拍一区二区| 无码视频免费一区二三区| 国产一区二区三区精品视频| 久久亚洲AV午夜福利精品一区| 一区二区三区高清| 国产A∨国片精品一区二区| 性无码免费一区二区三区在线| 日本一区二区三区不卡视频| 东京热无码av一区二区| 日韩精品一区二区三区老鸭窝| 国产激情一区二区三区四区| 国产精品女同一区二区| 国产精品一区二区综合| 亚洲AV无码一区二区二三区入口| 日韩一区在线视频| 少妇无码一区二区三区| 一本岛一区在线观看不卡| 激情亚洲一区国产精品| 久久精品一区二区影院| 亚洲av乱码一区二区三区按摩| 国产一区二区精品久久凹凸| 无码中文字幕一区二区三区| 国产亚洲无线码一区二区| 欧美日韩综合一区二区三区| 精品亚洲一区二区| 亚洲国产专区一区| 精品国产天堂综合一区在线| 日韩一区二区久久久久久| 三级韩国一区久久二区综合| 国产精品一区二区久久精品|