分享一份十分硬核的QT資料及QT開發(fā)經(jīng)驗(yàn)貼。原文見gitee倉庫:
https://gitee.com/newgolo/qtkaifajingyan
一、QT學(xué)習(xí)資料
1、相關(guān)書籍
(1)C++入門書籍推薦《C++ primer plus》,進(jìn)階書籍推薦《C++ primer》。
(2)Qt入門書籍推薦霍亞飛的《Qt Creator快速入門》,Qt進(jìn)階書籍推薦官方的《C++ GUI Qt4編程》,qml書籍推薦《Qt5編程入門》。
(3)強(qiáng)烈推薦程序員自我修養(yǎng)和規(guī)劃系列書《大話程序員》《程序員的成長課》《解憂程序員》,受益匪淺,受益終生!
二、QT開發(fā)經(jīng)驗(yàn)總結(jié)
當(dāng)編譯發(fā)現(xiàn)大量錯(cuò)誤的時(shí)候,從第一個(gè)看起,一個(gè)一個(gè)的解決,不要急著去看下一個(gè)錯(cuò)誤,往往后面的錯(cuò)誤都是由于前面的錯(cuò)誤引起的,第一個(gè)解決后很可能都解決了。
定時(shí)器是個(gè)好東西,學(xué)會好使用它,有時(shí)候用QTimer::singleShot可以解決意想不到的問題。
打開creator,在構(gòu)建套件的環(huán)境中增加MAKEFLAGS=-j8,可以不用每次設(shè)置多線程編譯。珍愛時(shí)間和生命。新版的QtCreator已經(jīng)默認(rèn)就是j8。
如果你想順利用QtCreator部署安卓程序,首先你要在AndroidStudio 里面配置成功,把坑全部趟平。
很多時(shí)候找到Qt對應(yīng)封裝的方法后,記得多看看該函數(shù)的重載,多個(gè)參數(shù)的,你會發(fā)現(xiàn)不一樣的世界,有時(shí)候會恍然大悟,原來Qt已經(jīng)幫我們封裝好了。
可以在pro文件中寫上標(biāo)記版本號+ico圖標(biāo)(Qt5才支持)
?
VERSION??=?2020.10.25 RC_ICONS?=?main0.ico
?
管理員運(yùn)行程序,限定在MSVC編譯器。
?
QMAKE_LFLAGS?+=?/MANIFESTUAC:"level='requireAdministrator'?uiAccess='false'"?#以管理員運(yùn)行 QMAKE_LFLAGS?+=?/SUBSYSTEM:WINDOWS,"5.01"?#VS2013?在XP運(yùn)行
?
運(yùn)行文件附帶調(diào)試輸出窗口CONFIG += console pro
繪制平鋪背景QPainter::drawTiledPixmap,繪制圓角矩形QPainter::drawRoundedRect(),而不是QPainter::drawRoundRect();
移除舊的樣式
?
//移除原有樣式 style()->unpolish(ui->btn); //重新設(shè)置新的該控件的樣式。 style()->polish(ui->btn);
?
獲取類的屬性
?
const?QMetaObject?*metaobject?=?object->metaObject(); int?count?=?metaobject->propertyCount(); for?(int?i?=?0;?i?property(i); ????const?char?*name?=?metaproperty.name(); ????QVariant?value?=?object->property(name); ????qDebug()?<?
Qt內(nèi)置圖標(biāo)封裝在QStyle中,大概七十多個(gè)圖標(biāo),可以直接拿來用。
?
SP_TitleBarMenuButton, SP_TitleBarMinButton, SP_TitleBarMaxButton, SP_TitleBarCloseButton, SP_MessageBoxInformation, SP_MessageBoxWarning, SP_MessageBoxCritical, SP_MessageBoxQuestion, ...?
根據(jù)操作系統(tǒng)位數(shù)判斷加載
?
win32?{ ????contains(DEFINES,?WIN64)?{?DESTDIR?=?$${PWD}/../../bin64 ????}?else?{?DESTDIR?=?$${PWD}/../../bin32?} }?
Qt5增強(qiáng)了很多安全性驗(yàn)證,如果出現(xiàn)setGeometry: Unable to set geometry,請將該控件的可見移到加入布局之后。
可以將控件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);?
對QLCDNumber控件設(shè)置樣式,需要將QLCDNumber的segmentstyle設(shè)置為flat。
巧妙的使用findChildren可以查找該控件下的所有子控件。findChild為查找單個(gè)。
?
//查找指定類名objectName的控件 QList?widgets?=?parentWidget.findChildren ("widgetname"); //查找所有QPushButton QList ?allPButtons?=?parentWidget.findChildren (); //查找一級子控件,不然會一直遍歷所有子控件 QList ?childButtons?=?parentWidget.findChildren (QString(),?Qt::FindDirectChildrenOnly); ?
巧妙的使用inherits判斷是否屬于某種類。
?
QTimer?*timer?=?new?QTimer;?????????//?QTimer?inherits?QObject timer->inherits("QTimer");??????????//?returns?true timer->inherits("QObject");?????????//?returns?true timer->inherits("QAbstractButton");?//?returns?false?
使用弱屬性機(jī)制,可以存儲臨時(shí)的值用于傳遞判斷。可以通過widget->dynamicPropertyNames()列出所有弱屬性名稱,然后通過widget->property("name")取出對應(yīng)的弱屬性的值。
在開發(fā)時(shí), 無論是出于維護(hù)的便捷性, 還是節(jié)省內(nèi)存資源的考慮, 都應(yīng)該有一個(gè) qss 文件來存放所有的樣式表, 而不應(yīng)該將 setStyleSheet 寫的到處都是。如果是初學(xué)階段或者測試階段可以直接UI上右鍵設(shè)置樣式表,正式項(xiàng)目還是建議統(tǒng)一到一個(gè)qss樣式表文件比較好,統(tǒng)一管理。
如果出現(xiàn)Z-order assignment: is not a valid widget.錯(cuò)誤提示,用記事本打開對應(yīng)的ui文件,找到為空的地方,刪除即可。
善于利用QComboBox的addItem的第二個(gè)參數(shù)設(shè)置用戶數(shù)據(jù),可以實(shí)現(xiàn)很多效果,使用itemData取出來。
如果用了webengine模塊,發(fā)布程序的時(shí)候帶上QtWebEngineProcess.exe+translations文件夾+resources文件夾。
默認(rèn)Qt是一個(gè)窗體一個(gè)句柄,如果要讓每個(gè)控件都擁有獨(dú)立的句柄,設(shè)置下 a.setAttribute(Qt::AA_NativeWindows);
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?
可以對整體的指示器設(shè)置樣式,例如 ::down-arrow,::menu-indicator{} ?:disabled,:off{}。
可以執(zhí)行位置設(shè)置背景圖片。
?
QMainWindow?>?.QWidget?{ ????background-color:?gainsboro; ????background-image:?url(:/images/pagefold.png); ????background-position:?top?right; ????background-repeat:?no-repeat }?
嵌入式linux運(yùn)行Qt程序 ?Qt4寫法:./HelloQt -qws & ? Qt5寫法:./HelloQt --platform xcb
Qtcreator軟件的配置文件存放在:C:UsersAdministratorAppDataRoamingQtProject,有時(shí)候如果發(fā)現(xiàn)出問題了,將這個(gè)文件夾刪除后打開creator自動(dòng)重新生成即可。
QMediaPlayer是個(gè)殼,依賴本地解碼器,視頻這塊默認(rèn)基本上就播放個(gè)MP4,如果要支持其他格式需要下載k-lite或者LAV Filters安裝即可(WIN上,其他系統(tǒng)上自行搜索)。如果需要做功能強(qiáng)勁的播放器,初學(xué)者建議用vlc、mpv,終極大法用ffmpeg。
判斷編譯器類型、編譯器版本、操作系統(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?__CYGWIN__ #ifdef?__CYGWIN32__ //mingw #ifdef?__MINGW32__ //windows #ifdef?_WIN32????//32bit #ifdef?_WIN64????//64bit #ifdef?_WINDOWS?????//圖形界面程序 #ifdef?_CONSOLE?????//控制臺程序 //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以上?
在pro中判斷Qt版本及構(gòu)建套件位數(shù)
?
#打印版本信息 message(qt?version:?$$QT_VERSION) #判斷當(dāng)前qt版本號 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上沒效果 #打印當(dāng)前Qt構(gòu)建套件的信息 message($$QT_ARCH) #表示arm平臺構(gòu)建套件 contains(QT_ARCH,?arm)?{} #表示32位的構(gòu)建套件 contains(QT_ARCH,?i386)?{} #表示64位的構(gòu)建套件 contains(QT_ARCH,?x86_64)?{}?
Qt最小化后恢復(fù)界面假死凍結(jié),加上代碼
?
void?showEvent(QShowEvent?*e) { ????setAttribute(Qt::WA_Mapped); ????QWidget::showEvent(e); }?
獲取標(biāo)題欄高度:style()->pixelMetric(QStyle::PM_TitleBarHeight); PM_TitleBarHeight點(diǎn)進(jìn)去你會發(fā)現(xiàn)新大陸。
設(shè)置高分屏屬性以便支持2K4K等高分辨率,尤其是手機(jī)app。必須寫在main函數(shù)的QApplication a(argc, argv);的前面。
?
#if?(QT_VERSION?>?QT_VERSION_CHECK(5,6,0)) ????QGuiApplication::AA_EnableHighDpiScaling); #endif?
如果運(yùn)行程序出現(xiàn) Fault tolerant heap shim applied to current process. This is usually due to previous crashes. 錯(cuò)誤。辦法:打開注冊表,找到HKEY_LOCAL_MACHINESoftwareMicrosoftWindows ?NTCurrentVersionAppCompatFlagsLayers,選中Layers鍵值,從右側(cè)列表中刪除自己的那個(gè)程序路徑即可。
Qt內(nèi)置了QFormLayout表單布局用于自動(dòng)生成標(biāo)簽+輸入框的組合的表單界面。
qml播放視頻在linux需要安裝 sudo apt-get install libpulse-dev。
可以直接繼承QSqlQueryModel實(shí)現(xiàn)自定義的QueryModel,比如某一列字體顏色,占位符,其他樣式等,重寫QVariant CustomSqlModel::data(const QModelIndex &index, int role) const。
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)對象以及滾動(dòng)方式為鼠標(biāo)左鍵拉動(dòng)滾動(dòng) QScroller::grabGesture(ui->listWidget,?QScroller::LeftMouseButtonGesture); //還有個(gè)QScrollerProperties可以設(shè)置滾動(dòng)的一些參數(shù)?
如果使用sqlite數(shù)據(jù)庫不想產(chǎn)生數(shù)據(jù)庫文件,可以創(chuàng)建內(nèi)存數(shù)據(jù)庫。
?
QSqlDatabase?db?=?QSqlDatabase::addDatabase("QSQLITE"); db.setDatabaseName("");?
清空數(shù)據(jù)表并重置自增ID,sql = truncate table table_name。
Qtchart模塊從Qt5.7開始自帶,最低編譯要求Qt5.4。在安裝的時(shí)候記得勾選,默認(rèn)不勾選。使用該模塊需要引入命名空間。
?
#include?QT_CHARTS_USE_NAMESPACE class?CustomChart?:?public?QChartView ?
QPushButton左對齊文字,需要設(shè)置樣式表QPushButton{text-align:left;}
QLabel有三種設(shè)置文本的方法,掌握好Qt的屬性系統(tǒng),舉一反三,可以做出很多效果。
?
ui->label->setStyleSheet("qproperty-text:hello;"); ui->label->setProperty("text",?"hello"); ui->label->setText("hello");?
巧妙的用QEventLoop開啟事件循環(huán),可以使得很多同步獲取返回結(jié)果而不阻塞界面。QEventLoop內(nèi)部新建了線程執(zhí)行。
?
QEventLoop?loop; connect(reply,?SIGNAL(finished()),?&loop,?SLOT(quit())); loop.exec();?
多種預(yù)定義變量 #if (defined webkit) || (defined webengine),去掉生成空的debug和release目錄 CONFIG -= debug_and_release。
新版的Qtcreator增強(qiáng)了語法檢查,會彈出很多警告提示等,可以在插件列表中關(guān)閉clang打頭的幾個(gè)即可,Help》About Plugins。也可以設(shè)置代碼檢查級別,Tools》Options 》C++ 》Code Model。
QSqlTableModel的rowCount方法,默認(rèn)最大返回256,如果超過256,可以將表格拉到底部,會自動(dòng)加載剩余的,每次最大加載256條數(shù)據(jù),如果需要打印或者導(dǎo)出數(shù)據(jù),記得最好采用sql語句去查詢,而不是使用QSqlTableModel的rowCount方法。不然永遠(yuǎn)最大只會導(dǎo)出256條數(shù)據(jù)。如果數(shù)據(jù)量很小,也可以采用如下方法:
?
//主動(dòng)加載所有數(shù)據(jù),不然獲取到的行數(shù)<=256 while(model->canFetchMore())?{ ????model->fetchMore(); }?
如果需要指定無邊框窗體,但是又需要保留操作系統(tǒng)的邊框特性,可以自由拉伸邊框,可以使用 setWindowFlags(Qt::CustomizeWindowHint);
在某些http post數(shù)據(jù)的時(shí)候,如果采用的是&字符串連接的數(shù)據(jù)發(fā)送,中文解析亂碼的話,需要將中文進(jìn)行URL轉(zhuǎn)碼。
?
QString?content?=?"測試中文"; QString?note?=?content.toUtf8().toPercentEncoding();?
Qt默認(rèn)不支持大資源文件,比如添加了字體文件,需要pro文件開啟。CONFIG += resources_big
Qt中繼承QWidget之后,樣式表不起作用,解決辦法有三個(gè)。強(qiáng)烈推薦方法一。
方法一:設(shè)置屬性 this->setAttribute(Qt::WA_StyledBackground, true);
方法二:改成繼承QFrame,因?yàn)镼Frame自帶paintEvent函數(shù)已做了實(shí)現(xiàn),在使用樣式表時(shí)會進(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); }?
有時(shí)候在界面上加了彈簧,需要?jiǎng)討B(tài)改變彈簧對應(yīng)的拉伸策略,對應(yīng)方法為changeSize,很多人會選擇使用set開頭去找,找不到的。
在使用QFile的過程中,不建議頻繁的打開文件寫入然后再關(guān)閉文件,比如間隔5ms輸出日志,IO性能瓶頸很大,這種情況建議先打開文件不要關(guān)閉,等待合適的時(shí)機(jī)比如析構(gòu)函數(shù)中或者日期變了需要重新變換日志文件的時(shí)候關(guān)閉文件。不然短時(shí)間內(nèi)大量的打開關(guān)閉文件會很卡,文件越大越卡。
在很多網(wǎng)絡(luò)應(yīng)用程序,需要自定義心跳包來保持連接,不然斷電或者非法關(guān)閉程序,對方識別不到,需要進(jìn)行超時(shí)檢測,但是有些程序沒有提供心跳協(xié)議,此時(shí)需要啟用系統(tǒng)層的保活程序,此方法適用于TCP連接。
?
int?fd?=?tcpSocket->socketDescriptor(); int?keepAlive?=?1;??????//開啟keepalive屬性,缺省值:0(關(guān)閉) int?keepIdle?=?5;???????//如果在5秒內(nèi)沒有任何數(shù)據(jù)交互,則進(jìn)行探測,缺省值:7200(s) int?keepInterval?=?2;???//探測時(shí)發(fā)探測包的時(shí)間間隔為2秒,缺省值:75(s) int?keepCount?=?2;??????//探測重試的次數(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));?
如果程序打包好以后彈出提示 This application failed to start because it could not find or load the Qt platform plugin 一般都是因?yàn)閜latforms插件目錄未打包或者打包錯(cuò)了的原因?qū)е碌摹?/p>
非常不建議tr中包含中文,盡管現(xiàn)在的新版Qt支持中文到其他語言的翻譯,但是很不規(guī)范,也不知道TMD是誰教的,tr的本意是包含英文,然后翻譯到其他語言比如中文,現(xiàn)在大量的初學(xué)者濫用tr,如果沒有翻譯的需求,禁用tr,tr需要開銷的,Qt默認(rèn)會認(rèn)為他需要翻譯,會額外進(jìn)行特殊處理。
很多人Qt和Qt Creator傻傻分不清楚,經(jīng)常問Qt什么版本結(jié)果發(fā)一個(gè)Qt Creator的版本過來,Qt Creator是使用Qt編寫的集成開發(fā)環(huán)境IDE,和宇宙第一的Visual Studio一樣,他可以是msvc編譯器的(WIN對應(yīng)的Qt集成安裝環(huán)境中自帶的Qt Cerator是msvc的),也可以是mingw編譯的,還可以是gcc的。如果是自定義控件插件,需要集成到Qt Creator中,必須保證該插件的動(dòng)態(tài)庫文件(dll或者so等文件)對應(yīng)的編譯器和Qt版本以及位數(shù)和Qt Creator的版本完全一致才行,否則基本不大可能集成進(jìn)去。特別注意的是Qt集成環(huán)境安裝包中的Qt版本和Qt Creator版本未必完全一致,必須擦亮眼睛看清楚,有些是完全一致的。
超過兩處相同處理的代碼,建議單獨(dú)寫成函數(shù)。代碼盡量規(guī)范精簡,比如 if(a == 123) 要寫成 if (123 == a),值在前面,再比如 if (ok == true) 要寫成 if (ok),if (ok == false) 要寫成 if (!ok)等。
很多人問Qt嵌入式平臺用哪個(gè)好,這里統(tǒng)一回答(當(dāng)前時(shí)間節(jié)點(diǎn)2018年):imx6+335x比較穩(wěn)定,性能高就用RK3288 RK3399,便宜的話就用全志H3,玩一玩可以用樹莓派香橙派。
對于大段的注釋代碼,建議用 #if 0 #endif 將代碼塊包含起來,而不是將該段代碼選中然后全部 // ,下次要打開這段代碼的話,又需要重新選中一次取消,如果采用的是 #if 0則只要把0改成1即可,效率大大提升。
Qt打包發(fā)布,有很多辦法,Qt5以后提供了打包工具windeployqt(linux上為linuxdeployqt,mac上為macdeployqt)可以很方便的將應(yīng)用程序打包,使用下來發(fā)現(xiàn)也不是萬能的,有時(shí)候會多打包一些沒有依賴的文件,有時(shí)候又會忘記打包一些插件尤其是用了qml的情況下,而且不能識別第三方庫,比如程序依賴ffmpeg,則對應(yīng)的庫需要自行拷貝,終極大法就是將你的可執(zhí)行文件復(fù)制到Qt安裝目錄下的bin目錄,然后整個(gè)一起打包,挨個(gè)刪除不大可能依賴的組件,直到刪到正常運(yùn)行為止。
Qt中的動(dòng)畫,底層用的是QElapsedTimer定時(shí)器來完成處理,比如產(chǎn)生一些指定規(guī)則算法的數(shù)據(jù),然后對屬性進(jìn)行處理。
在繪制無背景顏色只有邊框顏色的圓形時(shí)候,可以用繪制360度的圓弧替代,效果完全一致。
?
QRect?rect(-radius,?-radius,?radius?*?2,?radius?*?2); //以下兩種方法二選一,其實(shí)繪制360度的圓弧=繪制無背景的圓形 painter->drawArc(rect,?0,?360?*?16); painter->drawEllipse(rect);?
不要把d指針看的很玄乎,其實(shí)就是在類的實(shí)現(xiàn)文件定義了一個(gè)私有類,用來存放局部變量,個(gè)人建議在做一些小項(xiàng)目時(shí),沒有太大必要引入這種機(jī)制,會降低代碼可讀性,增加復(fù)雜性,新手接受項(xiàng)目后會看的很懵逼。
很多人在繪制的時(shí)候,設(shè)置畫筆以為就只可以設(shè)置個(gè)單調(diào)的顏色,其實(shí)QPen還可以設(shè)置brush,這樣靈活性就提高不知道多少倍,比如設(shè)置QPen的brush以后,可以使用各種漸變,比如繪制漸變顏色的進(jìn)度條和文字等,而不再是單調(diào)的一種顏色。
很多控件都帶有viewport,比如QTextEdit/QTableWidget/QScrollArea,有時(shí)候?qū)@些控件直接處理的時(shí)候發(fā)現(xiàn)不起作用,需要對其viewport()設(shè)置才行,比如設(shè)置滾動(dòng)條區(qū)域背景透明,需要使用scrollArea->viewport()->setStyleSheet("background-color:transparent;");而不是scrollArea->setStyleSheet("QScrollArea{background-color:transparent;}");
有時(shí)候設(shè)置了鼠標(biāo)跟蹤setMouseTracking為真,如果該窗體上面還有其他控件,當(dāng)鼠標(biāo)移到其他控件上面的時(shí)候,父類的鼠標(biāo)移動(dòng)事件MouseMove識別不到了,此時(shí)需要用到HoverMove事件,需要先設(shè)置 setAttribute(Qt::WA_Hover, true);
Qt封裝的QDateTime日期時(shí)間類非常強(qiáng)大,可以字符串和日期時(shí)間相互轉(zhuǎn)換,也可以毫秒數(shù)和日期時(shí)間相互轉(zhuǎn)換,還可以1970經(jīng)過的秒數(shù)和日期時(shí)間相互轉(zhuǎn)換等。
?
QDateTime?dateTime; QString?dateTime_str?=?dateTime.currentDateTime().toString("yyyy-MM-dd?hhss"); //從字符串轉(zhuǎn)換為毫秒(需完整的年月日時(shí)分秒) datetime.fromString("2011-09-10?1250:541",?"yyyy-MM-dd?hhss:zzz").toMSecsSinceEpoch(); //從字符串轉(zhuǎn)換為秒(需完整的年月日時(shí)分秒) datetime.fromString("2011-09-10?1250:541",?"yyyy-MM-dd?hhss:zzz").toTime_t(); //從毫秒轉(zhuǎn)換到年月日時(shí)分秒 datetime.fromMSecsSinceEpoch(1315193829218).toString("yyyy-MM-dd?hhss:zzz"); //從秒轉(zhuǎn)換到年月日時(shí)分秒(若有zzz,則為000) datetime.fromTime_t(1315193829).toString("yyyy-MM-dd?hhss[:zzz]");?
在我們使用QList、QStringList、QByteArray等鏈表或者數(shù)組的過程中,如果只需要取值,而不是賦值,強(qiáng)烈建議使用 at() 取值而不是 [] 操作符,在官方書籍《C++ GUI Qt 4編程(第二版)》的書中有特別的強(qiáng)調(diào)說明,此教材的原作者據(jù)說是Qt開發(fā)的核心人員編寫的,所以還是比較權(quán)威,至于使用 at() 與使用 [] 操作符速度效率的比較,網(wǎng)上也有網(wǎng)友做過此類對比。原文在書的212頁,這樣描述的:Qt對所有的容器和許多其他類都使用隱含共享,隱含共享是Qt對不希望修改的數(shù)據(jù)決不進(jìn)行復(fù)制的保證,為了使隱含共享的作用發(fā)揮得最好,可以采用兩個(gè)新的編程習(xí)慣。第一種習(xí)慣是對于一個(gè)(非常量的)向量或者列表進(jìn)行只讀存取時(shí),使用 at() 函數(shù)而不用 [] 操作符,因?yàn)镼t的容器類不能辨別 [] 操作符是否將出現(xiàn)在一個(gè)賦值的左邊還是右邊,他假設(shè)最壞的情況出現(xiàn)并且強(qiáng)制執(zhí)行深層賦值,而 at() 函數(shù)則不被允許出現(xiàn)在一個(gè)賦值的左邊。
如果是dialog窗體,需要在exec以后還能讓其他代碼繼續(xù)執(zhí)行,請?jiān)赿ialog窗體exec前增加一行代碼,否則會阻塞窗體消息。
?
QDialog?dialog; dialog.setWindowModality(Qt::WindowModal); dialog.exec();?
安全的刪除Qt的對象類,強(qiáng)烈建議使用deleteLater而不是delete,因?yàn)閐eleteLater會選擇在合適的時(shí)機(jī)進(jìn)行釋放,而delete會立即釋放,很可能會出錯(cuò)崩潰。如果要批量刪除對象集合,可以用qDeleteAll,比如 qDeleteAll(btns);
在QTableView控件中,如果需要自定義的列按鈕、復(fù)選框、下拉框等其他模式顯示,可以采用自定義委托QItemDelegate來實(shí)現(xiàn),如果需要禁用某列,則在自定義委托的重載createEditor函數(shù)返回0即可。自定義委托對應(yīng)的控件在進(jìn)入編輯狀態(tài)的時(shí)候出現(xiàn),如果想一直出現(xiàn),則需要重載paint函數(shù)用drawPrimitive或者drawControl來繪制。
將 QApplication::style() 對應(yīng)的drawPrimitive、drawControl、drawItemText、drawItemPixmap等幾個(gè)方法用熟悉了,再結(jié)合QStyleOption屬性,可以玩轉(zhuǎn)各種自定義委托,還可以直接使用paint函數(shù)中的painter進(jìn)行各種繪制,各種牛逼的表格、樹狀列表、下拉框等,絕對屌炸天。QApplication::style()->drawControl 的第4個(gè)參數(shù)如果不設(shè)置,則繪制出來的控件不會應(yīng)用樣式表。
心中有坐標(biāo),萬物皆painter,強(qiáng)烈建議在學(xué)習(xí)自定義控件繪制的時(shí)候,將qpainter.h頭文件中的函數(shù)全部看一遍、試一遍、理解一遍,這里邊包含了所有Qt內(nèi)置的繪制的接口,對應(yīng)的參數(shù)都試一遍,你會發(fā)現(xiàn)很多新大陸,會大大激發(fā)你的繪制的興趣,猶如神筆馬良一般,策馬崩騰遨游代碼繪制的世界。
在使用setItemWidget或者setCellWidget的過程中,有時(shí)候會發(fā)現(xiàn)設(shè)置的控件沒有居中顯示而是默認(rèn)的左對齊,而且不會自動(dòng)拉伸填充,對于追求完美的程序員來說,這個(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);?
很多時(shí)候需要在已知背景色的情況下,能夠清晰的繪制文字,這個(gè)時(shí)候需要計(jì)算對應(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;?
對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?
從Qt4轉(zhuǎn)到Qt5,有些類的方法已經(jīng)廢棄或者過時(shí)了,如果想要在Qt5中啟用Qt4的方法,比如QHeadVew的setMovable,可以在你的pro或者pri文件中加上一行即可:DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
Qt中的QColor對顏色封裝的很完美,支持各種轉(zhuǎn)換,比如rgb、hsb、cmy、hsl,對應(yīng)的是toRgb、toHsv、toCmyk、toHsl,還支持透明度設(shè)置,顏色值還能轉(zhuǎn)成16進(jìn)制格式顯示。
?
QColor?color(255,?0,?0,?100); qDebug()?<?
QVariant類型異常的強(qiáng)大,可以說是萬能的類型,在進(jìn)行配置文件的存儲的時(shí)候,經(jīng)常會用到QVariant的轉(zhuǎn)換,QVariant默認(rèn)自帶了toString、toFloat等各種轉(zhuǎn)換,但是還是不夠,比如有時(shí)候需要從QVariant轉(zhuǎn)到QColor,而卻沒有提供toColor的函數(shù),這個(gè)時(shí)候就要用到萬能辦法。
?
if?(variant.typeName()?==?"QColor")?{ ????QColor?color?=?variant.value(); ????QFont?font?=?variant.value (); ????QString?nodeValue?=?color.name(QColor::HexArgb); } ?
Qt中的QString和const char *之間轉(zhuǎn)換,最好用toStdString().c_str()而不是toLocal8Bit().constData(),比如在setProperty中如果用后者,字符串中文就會不正確,英文正常。
Qt的信號槽機(jī)制非常牛逼,也是Qt的獨(dú)特的核心功能之一,有時(shí)候我們在很多窗體中傳遞信號來實(shí)現(xiàn)更新或者處理,如果窗體層級比較多,比如窗體A的父類是窗體B,窗體B的父類是窗體C,窗體C有個(gè)子窗體D,如果窗體A一個(gè)信號要傳遞給窗體D,問題來了,必須先經(jīng)過窗體B中轉(zhuǎn)到窗體C再到窗體D才行,這樣的話各種信號關(guān)聯(lián)信號的connect會非常多而且管理起來比較亂,可以考慮增加一個(gè)全局的單例類AppEvent,公共的信號放這里,然后窗體A對應(yīng)信號綁定到AppEvent,窗體D綁定AppEvent的信號到對應(yīng)的槽函數(shù)即可,干凈清爽整潔。
QTextEdit右鍵菜單默認(rèn)英文的,如果想要中文顯示,加載widgets.qm文件即可,一個(gè)Qt程序中可以安裝多個(gè)翻譯文件,不沖突。
Qt中有個(gè)全局的焦點(diǎn)切換信號focusChanged,可以用它做自定義的輸入法。Qt4中默認(rèn)會安裝輸入法上下文,比如在main函數(shù)打印a.inputContext會顯示值,這個(gè)默認(rèn)安裝的輸入法上下文,會攔截兩個(gè)牛逼的信號QEvent::RequestSoftwareInputPanel和QEvent::CloseSoftwareInputPanel,以至于就算你安裝了全局的事件過濾器依然識別不到這兩個(gè)信號,你只需要在main函數(shù)執(zhí)行a.setInputContext(0)即可,意思是安裝輸入法上下文為空。
在Qt5.10以后,表格控件QTableWidget或者QTableView的默認(rèn)最小列寬改成了15,以前的版本是0,所以在新版的qt中,如果設(shè)置表格的列寬過小,不會應(yīng)用,取的是最小的列寬。所以如果要設(shè)置更小的列寬需要重新設(shè)置ui->tableView->horizontalHeader()->setMinimumSectionSize(0);
Qt源碼中內(nèi)置了一些未公開的不能直接使用的黑科技,都藏在對應(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.extractAll(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();?
理論上串口和網(wǎng)絡(luò)收發(fā)數(shù)據(jù)都是默認(rèn)異步的,操作系統(tǒng)自動(dòng)調(diào)度,完全不會卡住界面,網(wǎng)上那些說收發(fā)數(shù)據(jù)卡住界面主線程的都是扯幾把蛋,真正的耗時(shí)是在運(yùn)算以及運(yùn)算后的處理,而不是收發(fā)數(shù)據(jù),在一些小數(shù)據(jù)量運(yùn)算處理的項(xiàng)目中,一般不建議動(dòng)用線程去處理,線程需要調(diào)度開銷的,不要什么東西都往線程里邊扔,線程不是萬能的。只有當(dāng)真正需要將一些很耗時(shí)的操作比如編碼解碼等,才需要移到線程處理。
在構(gòu)造函數(shù)中獲取控件的寬高很可能是不正確的,需要在控件首次顯示以后再獲取才是正確的,控件是在首次顯示以后才會設(shè)置好正確的寬高值,記住是在首次顯示以后,而不是構(gòu)造函數(shù)或者程序啟動(dòng)好以后,如果程序啟動(dòng)好以后有些容器控件比如QTabWidget中的沒有顯示的頁面的控件,你去獲取寬高很可能也是不正確的,萬無一失的辦法就是首次顯示以后去獲取。
數(shù)據(jù)庫處理一般建議在主線程,如果非要在其他線程,務(wù)必記得打開數(shù)據(jù)庫也要在那個(gè)線程,即在那個(gè)線程使用數(shù)據(jù)庫就在那個(gè)線程打開,不能打開數(shù)據(jù)庫在主線程,執(zhí)行sql在子線程,很可能出問題。
新版的QTcpServer類在64位版本的Qt下很可能不會進(jìn)入incomingConnection函數(shù),那是因?yàn)镼t5對應(yīng)的incomingConnection函數(shù)參數(shù)變了,由之前的int改成了qintptr,改成qintptr有個(gè)好處,在32位上自動(dòng)是quint32而在64位上自動(dòng)是quint64,如果在Qt5中繼續(xù)寫的參數(shù)是int則在32位上沒有問題在64位上才有問題,所以為了兼容Qt4和Qt5,必須按照不一樣的參數(shù)寫。
?
#if?(QT_VERSION?>?QT_VERSION_CHECK(5,0,0)) ????void?incomingConnection(qintptr?handle); #else ????void?incomingConnection(int?handle); #endif?
Qt支持所有的界面控件比如QPushButton、QLineEdit自動(dòng)關(guān)聯(lián) on_控件名_信號(參數(shù)) 信號槽,比如按鈕的單擊信號 on_pushButton_clicked(),然后直接實(shí)現(xiàn)槽函數(shù)即可。
QWebEngineView控件由于使用了opengl,在某些電腦上可能由于opengl的驅(qū)動(dòng)過低會導(dǎo)致花屏或者各種奇奇怪怪的問題,比如showfullscreen的情況下鼠標(biāo)右鍵失效,需要在main函數(shù)啟用軟件opengl渲染。
?
#if?(QT_VERSION?>?QT_VERSION_CHECK(5,4,0)) ????//下面兩種方法都可以,Qt默認(rèn)采用的是AA_UseDesktopOpenGL ????QCoreApplication::AA_UseOpenGLES); ????//QCoreApplication::AA_UseSoftwareOpenGL); #endif ????QApplication?a(argc,?argv);?
另外一個(gè)方法解決 全屏+QWebEngineView控件一起會產(chǎn)生右鍵菜單無法彈出的bug,需要上移一個(gè)像素
?
QRect?rect?=?qApp->desktop()->geometry(); rect.setY(-1); rect.setHeight(rect.height()); this->setGeometry(rect);?
QStyle內(nèi)置了很多方法用處很大,比如精確獲取滑動(dòng)條鼠標(biāo)按下處的值。
?
QStyle::sliderValueFromPosition(minimum(),?maximum(),?event->x(),?width());?
用QFile讀寫文件的時(shí)候,推薦用QTextStream文件流的方式來讀寫文件,速度快很多,基本上會有30%的提升,文件越大性能區(qū)別越大。
?
//從文件加載英文屬性與中文屬性對照表 QFile?file(":/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(); }?
用QFile.readAll()讀取QSS文件默認(rèn)是ANSI格式,不支持UTF8,如果在QtCreator中打開qss文件來編輯保存,這樣很可能導(dǎo)致qss加載以后沒有效果。
?
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.setCodec("utf-8"); ????????while?(!in.atEnd())?{ ????????????QString?line; ????????????in?>>?line; ????????????list?<setPalette(QPalette(QColor(paletteColor))); ????????qApp->setStyleSheet(qss); ????????file.close(); ????} }?
QString內(nèi)置了很多轉(zhuǎn)換函數(shù),比如可以調(diào)用toDouble轉(zhuǎn)為double數(shù)據(jù),但是當(dāng)你轉(zhuǎn)完并打印的時(shí)候你會發(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()?<?
用QScriptValueIterator解析數(shù)據(jù)的時(shí)候,會發(fā)現(xiàn)總是會多一個(gè)節(jié)點(diǎn)內(nèi)容,并且內(nèi)容為空,如果需要跳過則增加一行代碼。
?
while?(it.hasNext())?{ ????it.next();???? ????if?(it.flags()?&?QScriptValue::SkipInEnumeration)?????? ???????continue;????? ????qDebug()?<?
setPixmap是最糟糕的貼圖方式,一般只用來簡單的不是很頻繁的貼圖,頻繁的建議painter繪制,默認(rèn)雙緩沖,在高級點(diǎn)用opengl繪制,利用GPU。
如果需要在尺寸改變的時(shí)候不重繪窗體,則設(shè)置屬性即可 this->setAttribute(Qt::WA_StaticContents, true); 這樣可以避免可以避免對已經(jīng)顯示區(qū)域的重新繪制。
默認(rèn)程序中獲取焦點(diǎn)以后會有虛邊框,如果看著覺得礙眼不舒服可以去掉,設(shè)置樣式即可:setStyleSheet("*{outline:0px;}");
Qt表格控件一些常用的設(shè)置封裝,QTableWidget繼承自QTableView,所以下面這個(gè)函數(shù)支持傳入QTableWidget。
?
void?QUIHelper::initTableView(QTableView?*tableView,?int?rowHeight,?bool?headVisible,?bool?edit) { ????//奇數(shù)偶數(shù)行顏色交替 ????tableView->setAlternatingRowColors(false); ????//垂直表頭是否可見 ????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); ????} }?
在一些大的項(xiàng)目中,可能嵌套了很多子項(xiàng)目,有時(shí)候會遇到子項(xiàng)目依賴其他子項(xiàng)目的時(shí)候,比如一部分子項(xiàng)目用來生成動(dòng)態(tài)庫,一部分子項(xiàng)目依賴這個(gè)動(dòng)態(tài)庫進(jìn)行編譯,此時(shí)就需要子項(xiàng)目按照順序編譯。
?
TEMPLATE?=?subdirs #設(shè)置ordered參數(shù)以后會依次編譯?demo?designer?examples CONFIG??+=?ordered SUBDIRS?+=?demo SUBDIRS?+=?designer SUBDIRS?+=?examples?
MSVC編譯器的選擇說明
如果是32位的Qt則編譯器選擇x86開頭的
如果是64位的Qt則編譯器選擇amd64開頭的
具體是看安裝的Qt構(gòu)建套件版本以及目標(biāo)運(yùn)行平臺的系統(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)境相同,沒有或。帶下劃線的就是交叉編譯,前面是編譯環(huán)境,后面是運(yùn)行環(huán)境。
名稱 | 說明 |
---|---|
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)行 |
很多時(shí)候用QDialog的時(shí)候會發(fā)現(xiàn)阻塞了消息,而有的時(shí)候我們希望是后臺的一些消息繼續(xù)運(yùn)行不要終止,此時(shí)需要做個(gè)設(shè)置。
?
QDialog?dialog; dialog.setWindowModality(Qt::WindowModal);
?
很多初學(xué)者甚至幾年工作經(jīng)驗(yàn)的人,對多線程有很深的誤解和濫用,尤其是在串口和網(wǎng)絡(luò)通信這塊,什么都往多線程里面丟,一旦遇到界面卡,就把數(shù)據(jù)收發(fā)啥的都搞到多線程里面去,殊不知絕大部分時(shí)候那根本沒啥用,因?yàn)闆]找到出問題的根源。
如果你沒有使用wait***函數(shù)的話,大部分的界面卡都出在數(shù)據(jù)處理和展示中,比如傳過來的是一張圖片的數(shù)據(jù),你需要將這些數(shù)據(jù)轉(zhuǎn)成圖片,這個(gè)肯定是耗時(shí)的;
還有就是就收到的數(shù)據(jù)曲線繪制出來,如果過于頻繁或者間隔過短,肯定會給UI造成很大的壓力的,最好的辦法是解決如何不要頻繁繪制UI比如合并數(shù)據(jù)一起繪制等;
如果是因?yàn)槔L制UI造成的卡,那多線程也是沒啥用的,因?yàn)閁I只能在主線程;
串口和網(wǎng)絡(luò)的數(shù)據(jù)收發(fā)默認(rèn)都是異步的,由操作系統(tǒng)調(diào)度的,如果數(shù)據(jù)處理復(fù)雜而且數(shù)據(jù)量大,你要做的是將數(shù)據(jù)處理放到多線程中;
如果沒有嚴(yán)格的數(shù)據(jù)同步需求,根本不需要調(diào)用wait***之類的函數(shù)來立即發(fā)送和接收數(shù)據(jù),實(shí)際需求中大部分的應(yīng)用場景其實(shí)異步收發(fā)數(shù)據(jù)就足夠了;
有嚴(yán)格數(shù)據(jù)同步需求的場景還是放到多線程會好一些,不然你wait***就卡在那邊了;
多線程是需要占用系統(tǒng)資源的,理論上來說,如果線程數(shù)量超過了CPU的核心數(shù)量,其實(shí)多線程調(diào)度可能花費(fèi)的時(shí)間更多,各位在使用過程中要權(quán)衡利弊;
在嵌入式linux上,如果設(shè)置了無邊框窗體,而該窗體中又有文本框之類的,發(fā)現(xiàn)沒法產(chǎn)生焦點(diǎn)進(jìn)行輸入,此時(shí)需要主動(dòng)激活窗體才行。
?
//這種方式設(shè)置的無邊框窗體在嵌入式設(shè)備上無法產(chǎn)生焦點(diǎn) setWindowFlags(Qt::WindowStaysOnTopHint?|?Qt::FramelessWindowHint?|?Qt::X11BypassWindowManagerHint); //需要在show以后主動(dòng)激活窗體 w->show(); w->activateWindow();
?
QString的replace函數(shù)會改變原字符串,切記,他在返回替換后的新字符串的同時(shí)也會改變原字符串,我的乖乖!
QGraphicsEffect類的相關(guān)效果很炫,可以實(shí)現(xiàn)很多效果比如透明、漸變、陰影等,但是該類很耗CPU,如果不是特別需要一般不建議用,就算用也是要用在該部件后期不會發(fā)生頻繁繪制的場景,不然會讓你哭暈在廁所。
在不同的平臺上文件路徑的斜杠也是不一樣的,比如linux系統(tǒng)一般都是 / 斜杠,而在windows上都是 兩個(gè)反斜杠,Qt本身程序內(nèi)部無論在win還是linux都支持 / 斜杠的路徑,但是一些第三方庫的話可能需要轉(zhuǎn)換成對應(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
?
巧用QMetaObject::invokeMethod方法可以實(shí)現(xiàn)很多效果,包括同步和異步執(zhí)行,比如有個(gè)應(yīng)用場景是在回調(diào)中,需要異步調(diào)用一個(gè)public函數(shù),如果直接調(diào)用的話會發(fā)現(xiàn)不成功,此時(shí)需要使用 QMetaObject::invokeMethod(obj, "fun", Qt::QueuedConnection); 這種方式來就可以。invokeMethod函數(shù)有很多重載參數(shù),可以傳入返回值和執(zhí)行方法的參數(shù)等。
Qt5中的信號是public的,可以在需要的地方直接emit即可,而在Qt4中信號是protected的,不能直接使用,需要定義一個(gè)public函數(shù)來emit。
Qt5.15版本開始官方不再提供安裝包,只提供源碼,可以自行編譯或者在線安裝,估計(jì)每次編譯各種版本太麻煩,更多的是為了統(tǒng)計(jì)收集用戶使用信息比如通過在線安裝,后期可能會逐步加大商業(yè)化力度。
有時(shí)候我們需要判斷當(dāng)前Qt版本有沒有某個(gè)模塊可以使用qtHaveModule(Qt5新引入的判斷)來判斷,如果要判斷自己的項(xiàng)目中有沒有 QT += 的方式添加的模塊,可以用 contains來判斷。
?
qtHaveModule(webenginewidgets)?{ message("當(dāng)前Qt庫有找到?webenginewidgets?模塊") } !qtHaveModule(webkit)?{ message("當(dāng)前Qt庫沒有找到?webkit?模塊") } contains(QT,?network)?{ message("當(dāng)前項(xiàng)目已經(jīng)引入?network?模塊") } !contains(QT,?widgets)?{ message("當(dāng)前項(xiàng)目沒有引入?widgets?模塊") }
?
c++11新引入了原始字符串格式,用戶避免在字符串中加入轉(zhuǎn)義字符,可以用于表示json字符串等場景。
?
QString?s1?=?R"(test?01.jpg)"; s1.replace("",?"#"); qDebug()<?
安卓上打印信息建議使用 qInfo() 而不是 qDebug() ,qInfo()才有效果。
Qt的默認(rèn)定時(shí)器精度不夠高(比如應(yīng)用場景是1分鐘保存一條記錄或者文件,當(dāng)你用默認(rèn)的定時(shí)器的時(shí)候你會發(fā)現(xiàn)有些時(shí)候是60秒而有些是59秒隨機(jī)的,如果客戶有要求這就需要設(shè)置精度了。當(dāng)然我們所做的絕大部分項(xiàng)目也不需要精度非常高的定時(shí)器,畢竟精度越高,占用的系統(tǒng)資源可能越大),如果需要設(shè)置更高的精度可以設(shè)置 setTimerType(Qt::PreciseTimer)。Qt有兩種定時(shí)器處理,一種是QTimer類,還有一種是QObject類就內(nèi)置的timeevent事件,如果是QObject類的定時(shí)器要設(shè)置的話調(diào)用 startTimer(interval, Qt::PreciseTimer);
Qt::PreciseTimer 精確的定時(shí)器,盡量保持毫秒精度。
Qt::CoarseTimer 粗略的定時(shí)器,盡量保持精度在所需的時(shí)間間隔5%范圍內(nèi)。
Qt::VeryCoarseTimer 很粗略的定時(shí)器,只保留完整的第二精度。
精度再高,也依賴對應(yīng)的操作系統(tǒng)中斷,假設(shè)中斷需要 5ms,則定時(shí)器精度不可能高于5毫秒。
QGraphicsEffect相關(guān)類很耗CPU,甚至在繪制的時(shí)候和某些地方有沖突干擾,基本上不建議使用,情非得已只建議少量使用和非頻繁觸發(fā)繪制的地方使用。
用QSettings設(shè)置注冊表,如果不是管理員身份運(yùn)行會打印 QSettings: failed to set subkey "xxx" (拒絕訪問。),你需要手動(dòng)鼠標(biāo)右鍵管理員身份運(yùn)行就可以。
QLineEdit除了單純的文本框以外,還可以做很多特殊的處理用途。
限制輸入只能輸入IP地址。
限制輸入范圍,強(qiáng)烈推薦使用 QRegExpValidator 正則表達(dá)式來處理。
?
//正在表達(dá)式限制輸入 QString?str?=?"\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"; ui->lineEdit->setValidator(new?QRegExpValidator(QRegExp(str))); //用于占位 ui->lineEdit->setInputMask("000.000.000.000"); #if?0 //下面代碼設(shè)置浮點(diǎn)數(shù)范圍限制失敗 ui->lineEdit->setValidator(new?QDoubleValidator(20,?50,?1)); #else //下面代碼設(shè)置浮點(diǎn)數(shù)范圍限制成功 QDoubleValidator?*validator?=?new?QDoubleValidator(20,?50,?1); validator->setNotation(QDoubleValidator::StandardNotation); ui->lineEdit->setValidator(validator); #endif //下面代碼設(shè)置整數(shù)范圍限制成功 ui->lineEdit->setValidator(new?QIntValidator(10,?120)); //其實(shí)上面的代碼缺陷很多,只能限制只輸入小數(shù),無法設(shè)定數(shù)值范圍,很操蛋 //需要來個(gè)萬能的牛逼的?QRegExpValidator //限制浮點(diǎn)數(shù)輸入范圍為[-180,180] QRegExp?regexp("^-?(180|1?[0-7]?\d(\.\d+)?)$"); //限制浮點(diǎn)數(shù)輸入范圍為[-90,90]并限定為小數(shù)位后4位 QRegExp?regexp("^-?(90|[1-8]?\d(\.\d{1,4})?)$"); QRegExpValidator?*validator?=?new?QRegExpValidator(regexp,?this); ui->lineEdit->setValidator(validator);?
在繼承自QAbstractItemView的控件中,比如QTableView、QTableWidget,如果文本超過對應(yīng)item的寬度,則會自動(dòng)省略號顯示,想要快速顯示完整的文本,可以在該列和下一列分割線中間雙擊即可,會自動(dòng)自適應(yīng)顯示最大寬度,如果是Qt5.14或者更高版本,你會發(fā)現(xiàn)顯示省略號的計(jì)算規(guī)則變了,如果是rtsp、http之類的開頭的英文字符串,同樣的列寬下,會提前就顯示省略號,比如字符串 rtmp://58.200.131.2:1935/livetv/cctv1,會顯示成 rtmp://... ?,而在舊版本的Qt中會顯示成 rtmp://58.200.131... ,很多時(shí)候我們并不想看到煩人的省略號,可以設(shè)置取消。
?
//取消自動(dòng)換行 tableView->setWordWrap(false); //超出文本不顯示省略號 tableView->setTextElideMode(Qt::ElideNone);?
QVideoWidget播放視頻,可能會遇到畫面閃爍的情況,播放視頻的窗體需要設(shè)置個(gè)屬性。
?
QVideoWidget?*videoWidget?=?new?QVideoWidget; videoWidget->setAttribute(Qt::WA_OpaquePaintEvent);?
Qt bug成千上萬,這個(gè)不用大驚小怪,也基本上遇不到,大部分都是特殊極端情況特定應(yīng)用場景出現(xiàn),甚至你會遇到有些是debug可以release報(bào)錯(cuò),有些release可以debug卻報(bào)錯(cuò)的情況,最神奇的還有先是debug報(bào)錯(cuò),然后release正常,再返回去用debug又正常,需要用release激活一下!學(xué)習(xí)編程的路本來就是一條坑坑洼洼的路,不斷填坑,盡量規(guī)避坑!很多時(shí)候很多看起來的坑其實(shí)是自己沒有注意細(xì)節(jié)導(dǎo)致的。
Qt試圖中默認(rèn)排序是按照字符串的ASCII排序的,如果是IP地址的話會出現(xiàn)192.168.1.117排在192.168.1.2前面的情況,如果要規(guī)避這種情況,一種做法是取末尾的地址轉(zhuǎn)成整型再比較大小,缺點(diǎn)是跨網(wǎng)段就歇菜了,又會出現(xiàn)192.168.2.65出現(xiàn)在192.168.1.70前面,終極大法是將IP地址轉(zhuǎn)成整型再比較大小。
?
QString?QUIHelper::ipv4IntToString(quint32?ip) { ????QString?result?=?QString("%1.%2.%3.%4").arg((ip?>>?24)?&?0xFF).arg((ip?>>?16)?&?0xFF).arg((ip?>>?8)?&?0xFF).arg(ip?&?0xFF); ????return?result; } quint32?QUIHelper::ipv4StringToInt(const?QString?&ip) { ????int?result?=?0; ????if?(isIP(ip))?{ ????????QStringList?list?=?ip.split("."); ????????int?ip0?=?list.at(0).toInt(); ????????int?ip1?=?list.at(1).toInt(); ????????int?ip2?=?list.at(2).toInt(); ????????int?ip3?=?list.at(3).toInt(); ????????result?=?ip3?|?ip2?<8?|?ip1?<16?|?ip0?<24; ????} ????return?result; }?
在主QWidget窗體如果直接qss設(shè)置背景圖片的話,預(yù)覽是可見的,運(yùn)行并沒有效果,你需要在這個(gè)主widget上再放個(gè)widget,在新的widget上設(shè)置qss圖片就行,而如果是Dialog或者QMainWindow窗體是支持直接設(shè)置qss背景圖的,預(yù)覽和運(yùn)行效果一致。
Qt提供了qDebug機(jī)制直接輸出打印信息,這個(gè)大大彌補(bǔ)了QtCreator調(diào)試很雞肋的缺點(diǎn),而且無縫對接日志鉤子,使得現(xiàn)場運(yùn)行期間按照預(yù)定的打印信息輸出到日志文件,有時(shí)候在開發(fā)階段,又不想要看到一堆堆的打印信息,最笨的做法是一行行注釋掉qdebug的地方,其實(shí)還可以直接pro中加上一行來禁用整個(gè)項(xiàng)目的qdebug輸出。
?
#禁用qdebug打印輸出 DEFINES?????+=?QT_NO_DEBUG_OUTPUT #自定義define變量?可以在整個(gè)項(xiàng)目中使用 #pro文件可以這樣判斷?contains(DEFINES,?videovlc)?{} #代碼文件可以這樣判斷?#ifdef?videovlc DEFINES?????+=?videovlc1?videoffmpeg #關(guān)閉編譯警告提示?眼不見為凈 CONFIG??????+=?warn_off #指定編譯生成的文件到temp目錄?分門別類存儲 MOC_DIR?????=?temp/moc RCC_DIR?????=?temp/rcc UI_DIR??????=?temp/ui OBJECTS_DIR?=?temp/obj #指定編譯生成的可執(zhí)行文件到bin目錄 DESTDIR?????=?bin?
Qt對操作系統(tǒng)層的消息也做了很多的封裝,可以直接拿到進(jìn)行處理(如果需要攔截處理要用對應(yīng)操作系統(tǒng)的API才行比如鼠標(biāo)鍵盤鉤子),比如系統(tǒng)休眠和喚醒做一些處理。
?
//主窗體頭文件 protected: ????bool?nativeEvent(const?QByteArray?&eventType,?void?*message,?long?*result); #ifdef?Q_OS_WIN ????bool?winEvent(MSG?*message,?long?*result); #endif //主窗體實(shí)現(xiàn)函數(shù) #ifdef?Q_OS_WIN #include?"Windows.h" #endif bool?frmMain::nativeEvent(const?QByteArray?&eventType,?void?*message,?long?*result) { ????if?(eventType?==?"windows_generic_MSG")?{ #ifdef?Q_OS_WIN ????????MSG?*msg?=?static_cast(message); ????????//qDebug()?<message; ????????if?(msg->wParam?==?PBT_APMSUSPEND?&&?msg->message?==?WM_POWERBROADCAST)?{ ????????????//系統(tǒng)休眠的時(shí)候自動(dòng)最小化可以規(guī)避程序可能出現(xiàn)的問題 ????????????this->showMinimized(); ????????}?else?if?(msg->wParam?==?PBT_APMRESUMEAUTOMATIC)?{ ????????????//休眠喚醒后自動(dòng)打開 ????????????this->showNormal(); ????????} #endif ????}?else?if?(eventType?==?"NSEvent")?{ #ifdef?Q_OS_MACOS #endif ????} ????return?false; } #ifdef?Q_OS_WIN bool?frmMain::winEvent(MSG?*message,?long?*result) { ????return?nativeEvent("windows_generic_MSG",?message,?result); } #endif ?
Qt的pro項(xiàng)目管理配置文件中也可添加各種編譯前后的操作及配置,主要通過 QMAKE_POST_LINK和QMAKE_PRE_LINK,他們支持的函數(shù)以及寫法,可以在QtCreator的幫助中搜索 qmake Function Reference 查看詳情說明。
QMAKE_PRE_LINK ? ?表示編譯前執(zhí)行內(nèi)容
QMAKE_POST_LINK ? 表示編譯后執(zhí)行內(nèi)容
?
srcFile1?=?$$PWD/1.txt srcFile2?=?$$PWD/2.txt dstDir?=?$$PWD/../bin #windows上需要轉(zhuǎn)換路徑斜杠?其他系統(tǒng)不需要 srcFile1?=?$$replace(srcFile1,?/,?\); srcFile2?=?$$replace(srcFile2,?/,?\); dstDir?=?$$replace(dstDir,?/,?\); #編譯前執(zhí)行拷貝?多個(gè)拷貝可以通過?&&?符號隔開 QMAKE_PRE_LINK?+=?copy?/Y?$$srcFile1?$$dstDir?&&?copy?/Y?$$srcFile2?$$dstDir #編譯后執(zhí)行拷貝?多個(gè)拷貝可以通過?&&?符號隔開 QMAKE_POST_LINK?+=?copy?/Y?$$srcFile1?$$dstDir?&&?copy?/Y?$$srcFile2?$$dstDir?
三、其他經(jīng)驗(yàn)
Qt界的中文亂碼問題,版本眾多導(dǎo)致的如何選擇安裝包問題,如何打包發(fā)布程序的問題,堪稱Qt界的三座大山!
在Qt的學(xué)習(xí)過程中,學(xué)會查看對應(yīng)類的頭文件是一個(gè)好習(xí)慣,如果在該類的頭文件沒有找到對應(yīng)的函數(shù),可以去他的父類中找找,實(shí)在不行還有爺爺類,肯定能找到的。通過頭文件你會發(fā)現(xiàn)很多函數(shù)接口其實(shí)Qt已經(jīng)幫我們封裝好了,有空還可以閱讀下他的實(shí)現(xiàn)代碼。
Qt安裝目錄下的Examples目錄下的例子,看完學(xué)完,月薪20K起步;Qt常用類的頭文件的函數(shù)看完學(xué)完使用一遍并加以融會貫通,月薪30K起步。
Qt在開發(fā)階段不支持中文目錄,切記,這是無數(shù)人可能犯的錯(cuò)誤,在安裝Qt集成開發(fā)環(huán)境以及編譯器的時(shí)候,務(wù)必記得目錄必須英文,否則很可能不正常,建議盡量用默認(rèn)的安裝位置。
如果出現(xiàn)崩潰和段錯(cuò)誤,80%都是因?yàn)橐丛浇纾次闯跏蓟揽圻@兩點(diǎn),80%的問題解決了。
Qt一共有幾百個(gè)版本,關(guān)于如何選擇Qt版本的問題,我一般保留四個(gè)版本,為了兼容Qt4用4.8.7,最后的支持XP的版本5.7.0,最新的長期支持版本比如5.12,最高的新版本比如5.15.2。強(qiáng)烈不建議使用4.7以前和5.0到5.3之間的版本,太多bug和坑,穩(wěn)定性和兼容性相比于之后的版本相當(dāng)差,能換就換,不能換睡服領(lǐng)導(dǎo)也要換。
Qt和msvc編譯器常見搭配是Qt5.7+VS2013、Qt5.9+VS2015、Qt5.12+VS2017,按照這些搭配來,基本上常用的模塊都會有,比如webengine模塊,如果選用的Qt5.12+msvc2015,則很可能官方?jīng)]有編譯這個(gè)模塊,只是編譯了Qt5.12+msvc2017的。
新版本Qt安裝包安裝的時(shí)候需要填寫注冊信息,如果不想填寫,先禁用網(wǎng)卡,在運(yùn)行安裝包,可以直接跳過這一步進(jìn)行安裝。
終極秘籍:如果遇到問題搜索Qt方面找不到答案,試著將關(guān)鍵字用JAVA C# android打頭,你會發(fā)現(xiàn)別有一番天地,其他人很可能做過!
如果Qt能從下面幾個(gè)方面努力,相信會更有發(fā)展前景。
QWidget支持CSS3,具有諸多的牛逼的效果,目前支持的是CSS2。
QWidget支持GPU繪制,可選切換CPU或者GPU,大大提升繪制效率,利用現(xiàn)在強(qiáng)大的硬件。
Qml無縫支持js,可以利用現(xiàn)在各種js輪子,指數(shù)級提升qml的項(xiàng)目范圍。
支持將程序轉(zhuǎn)成web運(yùn)行,比如轉(zhuǎn)成cgi之類的程序,目前Qt for WebAssembly很雞肋,功能極其有限,首次加載速度超慢,大部分Qt類還不支持。
最后一條:珍愛生命,遠(yuǎn)離編程。祝大家頭發(fā)濃密,睡眠良好,情緒穩(wěn)定,財(cái)富自由!
審核編輯:湯梓紅
評論
查看更多