大神總結(jié)的Qt開發(fā)經(jīng)驗,滿滿的都是干貨
首先聲明,本文并非原創(chuàng),純屬搬運,內(nèi)容來自一位叫做飛揚青春的大神的Gitee主頁,主要是為了收藏下面介紹的100多個Qt開發(fā)經(jīng)驗。我本身也從事了兩年了Qt開發(fā),再轉(zhuǎn)Qt開發(fā)以前用的都是MFC,我仔細的看了一遍下面列出的各條經(jīng)驗,只恨看到的太晚了,因為很多都是自己踩過的坑。比如qss的ANSI編碼、嵌套窗口中主窗口無法接收鼠標(biāo)移動事件等,又比如我用qss設(shè)置窗口樣式,但是項目每次重新構(gòu)建以后,樣式表就會不生效等問題,也花了自己不少時間去解決,所以在這里轉(zhuǎn)發(fā)大神的經(jīng)驗,留作以后參考和逐條的研究,也分享給更多正在學(xué)習(xí)Qt或者正在使用Qt進行程序開發(fā)的朋友們。
大神主頁:https://gitee.com/feiyangqingyun/qtkaifajingyan?_from=gitee_search
1. 當(dāng)編譯發(fā)現(xiàn)大量錯誤的時候,從第一個看起,一個一個的解決,不要急著去看下一個錯誤,往往后面的錯誤都是由于前面的錯誤引起的,第一個解決后很可能都解決了。
2. 定時器是個好東西,學(xué)會好使用它,有時候用QTimer::singleShot可以解決意想不到的問題。
3. 打開creator,在構(gòu)建套件的環(huán)境中增加MAKEFLAGS=-j8,可以不用每次設(shè)置多線程編譯。珍愛時間和生命。新版的QtCreator已經(jīng)默認就是j8。
4. 如果你想順利用QtCreator部署安卓程序,首先你要在AndroidStudio 里面配置成功,把坑全部趟平。
5. 很多時候找到Qt對應(yīng)封裝的方法后,記得多看看該函數(shù)的重載,多個參數(shù)的,你會發(fā)現(xiàn)不一樣的世界,有時候會恍然大悟,原來Qt已經(jīng)幫我們封裝好了。
6. 可以在pro文件中寫上標(biāo)記版本號 ico圖標(biāo)(Qt5才支持)
VERSION = 2020.10.25RC_ICONS = main0.ico
7. 管理員運行程序,限定在MSVC編譯器
QMAKE_LFLAGS = /MANIFESTUAC:"level='requireAdministrator' uiAccess='false'" #以管理員運行QMAKE_LFLAGS = /SUBSYSTEM:WINDOWS,"5.01" #VS2013 在XP運行
8. 運行文件附帶調(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中,大概七十多個圖標(biāo),可以直接拿來用。
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增強了很多安全性驗證,如果出現(xiàn)setGeometry: Unable to set geometry,請將該控件的可見移到加入布局之后。
15. 可以將控件A添加到布局,然后控件B設(shè)置該布局,這種靈活性大大提高了控件的組合度,比如可以在文本框左側(cè)右側(cè)增加一個搜索按鈕,按鈕設(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. 對QLCDNumber控件設(shè)置樣式,需要將QLCDNumber的segmentstyle設(shè)置為flat。
17. 巧妙的使用findChildren可以查找該控件下的所有子控件。findChild為查找單個。
//查找指定類名objectName的控件QList<QWidget *> widgets = parentWidget.findChildren<QWidget *>("widgetname");//查找所有QPushButtonQList<QPushButton *> allPButtons = parentWidget.findChildren<QPushButton *>();//查找一級子控件,不然會一直遍歷所有子控件QList<QPushButton *> childButtons = parentWidget.findChildren<QPushButton *>(QString(), Qt::FindDirectChildrenOnly);
18. 巧妙的使用inherits判斷是否屬于某種類。
QTimer *timer = new QTimer; // QTimer inherits QObjecttimer->inherits("QTimer"); // returns truetimer->inherits("QObject"); // returns truetimer->inherits("QAbstractButton"); // returns false
19. 使用弱屬性機制,可以存儲臨時的值用于傳遞判斷。可以通過widget->dynamicPropertyNames()列出所有弱屬性名稱,然后通過widget->property("name")取出對應(yīng)的弱屬性的值。
20. 在開發(fā)時, 無論是出于維護的便捷性, 還是節(jié)省內(nèi)存資源的考慮, 都應(yīng)該有一個 qss 文件來存放所有的樣式表, 而不應(yīng)該將 setStyleSheet 寫的到處都是。如果是初學(xué)階段或者測試階段可以直接UI上右鍵設(shè)置樣式表,正式項目還是建議統(tǒng)一到一個qss樣式表文件比較好,統(tǒng)一管理。
21. 如果出現(xiàn)Z-order assignment: is not a valid widget.錯誤提示,用記事本打開對應(yīng)的ui文件,找到<zorder></zorder>為空的地方,刪除即可。
22. 善于利用QComboBox的addItem的第二個參數(shù)設(shè)置用戶數(shù)據(jù),可以實現(xiàn)很多效果,使用itemData取出來。
23. 如果用了webengine模塊,發(fā)布程序的時候帶上QtWebEngineProcess.exe translations文件夾 resources文件夾。
24. 默認Qt是一個窗體一個句柄,如果要讓每個控件都擁有獨立的句柄,設(shè)置下 a.setAttribute(Qt::AA_NativeWindows);
25. Qt Android防止程序被關(guān)閉。
#if defined(Q_OS_ANDROID)QAndroidService a(argc, argv);return a.exec()#elseQApplication a(argc, argv);return a.exec();#endif
26. 可以對整體的指示器設(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運行Qt程序 Qt4寫法:./HelloQt -qws & Qt5寫法:./HelloQt –platform xcb
29. Qtcreator軟件的配置文件存放在:C:UsersAdministratorAppDataRoamingQtProject,有時候如果發(fā)現(xiàn)出問題了,將這個文件夾刪除后打開creator自動重新生成即可。
30. QMediaPlayer是個殼,依賴本地解碼器,視頻這塊默認基本上就播放個MP4,如果要支持其他格式需要下載k-lite或者LAV Filters安裝即可(WIN上,其他系統(tǒ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 //控制臺程序//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版本號QT_VERSION = $$[QT_VERSION]QT_VERSION = $$split(QT_VERSION, ".")QT_VER_MAJ = $$member(QT_VERSION, 0)QT_VER_MIN = $$member(QT_VERSION, 1)#下面是表示 Qt5.5greaterThan(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) {}
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點進去你會發(fā)現(xiàn)新大陸。
35. 設(shè)置高分屏屬性以便支持2K4K等高分辨率,尤其是手機app。必須寫在main函數(shù)的QApplication a(argc, argv);的前面。
#if (QT_VERSION > QT_VERSION_CHECK(5,6,0)) QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);#endif
36. 如果運行程序出現(xiàn) Fault tolerant heap shim applied to current process. This is usually due to previous crashes. 錯誤。
辦法:打開注冊表,找到HKEY_LOCAL_MACHINESoftwareMicrosoftWindows NTCurrentVersionAppCompatFlagsLayers,選中Layers鍵值,從右側(cè)列表中刪除自己的那個程序路徑即可。
37. Qt內(nèi)置了QFormLayout表單布局用于自動生成標(biāo)簽 輸入框的組合的表單界面。
38. qml播放視頻在linux需要安裝 sudo apt-get install libpulse-dev。
39. 可以直接繼承QSqlQueryModel實現(xiàn)自定義的QueryModel,比如某一列字體顏色,占位符,其他樣式等,重寫QVariant CustomSqlModel::data(const QModelIndex &index, int role) const。
40. Qt5以后提供了類QScroller直接將控件滾動。
//禁用橫向滾動條ui->listWidget->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//禁用縱向滾動條ui->listWidget->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//設(shè)置橫向按照像素值為單位滾動ui->listWidget->setHorizontalScrollMode(QListWidget::ScrollPerPixel);//設(shè)置縱向按照像素值為單位滾動ui->listWidget->setVerticalScrollMode(QListWidget::ScrollPerPixel);//設(shè)置滾動對象以及滾動方式為鼠標(biāo)左鍵拉動滾動QScroller::grabGesture(ui->listWidget, QScroller::LeftMouseButtonGesture);//還有個QScrollerProperties可以設(shè)置滾動的一些參數(shù)
41. 如果使用sqlite數(shù)據(jù)庫不想產(chǎn)生數(shù)據(jù)庫文件,可以創(chuàng)建內(nèi)存數(shù)據(jù)庫。
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");db.setDatabaseName(":memory:");
42. 清空數(shù)據(jù)表并重置自增ID,sql = truncate table table_name。
43. Qtchart模塊從Qt5.7開始自帶,最低編譯要求Qt5.4。在安裝的時候記得勾選,默認不勾選。使用該模塊需要引入命名空間。
#include <QChartView>QT_CHARTS_USE_NAMESPACEclass CustomChart : public QChartView
44. QPushButton左對齊文字,需要設(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開啟事件循環(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增強了語法檢查,會彈出很多警告提示等,可以在插件列表中關(guān)閉clang打頭的幾個即可,Help》About Plugins。也可以設(shè)置代碼檢查級別,Tools》Options 》C 》Code Model。
49. QSqlTableModel的rowCount方法,默認最大返回256,如果超過256,可以將表格拉到底部,會自動加載剩余的,每次最大加載256條數(shù)據(jù),如果需要打印或者導(dǎo)出數(shù)據(jù),記得最好采用sql語句去查詢,而不是使用QSqlTableModel的rowCount方法。不然永遠最大只會導(dǎo)出256條數(shù)據(jù)。
如果數(shù)據(jù)量很小,也可以采用如下方法:
//主動加載所有數(shù)據(jù),不然獲取到的行數(shù)<=256while(model->canFetchMore()) { model->fetchMore();}
50. 如果需要指定無邊框窗體,但是又需要保留操作系統(tǒng)的邊框特性,可以自由拉伸邊框,可以使用
setWindowFlags(Qt::CustomizeWindowHint);
51. 在某些http post數(shù)據(jù)的時候,如果采用的是&字符串連接的數(shù)據(jù)發(fā)送,中文解析亂碼的話,需要將中文進行URL轉(zhuǎn)碼。
QString content = "測試中文";QString note = content.toUtf8().toPercentEncoding();
52. Qt默認不支持大資源文件,比如添加了字體文件,需要pro文件開啟。
CONFIG = resources_big
53. Qt中繼承QWidget之后,樣式表不起作用,解決辦法有三個。強烈推薦方法一。
– 方法一:設(shè)置屬性 this->setAttribute(Qt::WA_StyledBackground, true);
– 方法二:改成繼承QFrame,因為QFrame自帶paintEvent函數(shù)已做了實現(xiàn),在使用樣式表時會進行解析和繪制。
– 方法三:重新實現(xiàn)QWidget的paintEvent函數(shù)時,使用QStylePainter繪制。
void Widget::paintEvent(QPaintEvent *){ QStyleOption option; option.initFrom(this); QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this);}
54. 有時候在界面上加了彈簧,需要動態(tài)改變彈簧對應(yīng)的拉伸策略,對應(yīng)方法為changeSize,很多人會選擇使用set開頭去找,找不到的。
55. 在使用QFile的過程中,不建議頻繁的打開文件寫入然后再關(guān)閉文件,比如間隔5ms輸出日志,IO性能瓶頸很大,這種情況建議先打開文件不要關(guān)閉,等待合適的時機比如析構(gòu)函數(shù)中或者日期變了需要重新變換日志文件的時候關(guān)閉文件。不然短時間內(nèi)大量的打開關(guān)閉文件會很卡,文件越大越卡。
56. 在很多網(wǎng)絡(luò)應(yīng)用程序,需要自定義心跳包來保持連接,不然斷電或者非法關(guān)閉程序,對方識別不到,需要進行超時檢測,但是有些程序沒有提供心跳協(xié)議,此時需要啟用系統(tǒng)層的?;畛绦?,此方法適用于TCP連接。
int fd = tcpSocket->socketDescriptor();int keepAlive = 1; //開啟keepalive屬性,缺省值:0(關(guān)閉)int keepIdle = 5; //如果在5秒內(nèi)沒有任何數(shù)據(jù)交互,則進行探測,缺省值:7200(s)int keepInterval = 2; //探測時發(fā)探測包的時間間隔為2秒,缺省值:75(s)int keepCount = 2; //探測重試的次數(shù),全部超時則認定連接失效,缺省值: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 一般都是因為platforms插件目錄未打包或者打包錯了的原因?qū)е碌摹?/p>
58. 非常不建議tr中包含中文,盡管現(xiàn)在的新版Qt支持中文到其他語言的翻譯,但是很不規(guī)范,也不知道TMD是誰教的,tr的本意是包含英文,然后翻譯到其他語言比如中文,現(xiàn)在大量的初學(xué)者濫用tr,如果沒有翻譯的需求,禁用tr,tr需要開銷的,Qt默認會認為他需要翻譯,會額外進行特殊處理。
59. 很多人Qt和Qt Creator傻傻分不清楚,經(jīng)常問Qt什么版本結(jié)果發(fā)一個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中,必須保證該插件的動態(tài)庫文件(dll或者so等文件)對應(yīng)的編譯器和Qt版本以及位數(shù)和Qt Creator的版本完全一致才行,否則基本不大可能集成進去。特別注意的是Qt集成環(huán)境安裝包中的Qt版本和Qt Creator版本未必完全一致,必須擦亮眼睛看清楚,有些是完全一致的。
60. 超過兩處相同處理的代碼,建議單獨寫成函數(shù)。代碼盡量規(guī)范精簡,比如 if(a == 123) 要寫成 if (123 == a),值在前面,再比如 if (ok == true) 要寫成 if (ok),if (ok == false) 要寫成 if (!ok)等。
61. 很多人問Qt嵌入式平臺用哪個好,這里統(tǒng)一回答(當(dāng)前時間節(jié)點2018年):imx6 335x比較穩(wěn)定,性能高就用RK3288 RK3399,便宜的話就用全志H3,玩一玩可以用樹莓派香橙派。
62. 對于大段的注釋代碼,建議用 #if 0 #endif 將代碼塊包含起來,而不是將該段代碼選中然后全部 // ,下次要打開這段代碼的話,又需要重新選中一次取消,如果采用的是 #if 0則只要把0改成1即可,效率大大提升。
63. Qt打包發(fā)布,有很多辦法,Qt5以后提供了打包工具windeployqt(linux上為linuxdeployqt,mac上為macdeployqt)可以很方便的將應(yīng)用程序打包,使用下來發(fā)現(xiàn)也不是萬能的,有時候會多打包一些沒有依賴的文件,有時候又會忘記打包一些插件尤其是用了qml的情況下,而且不能識別第三方庫,比如程序依賴ffmpeg,則對應(yīng)的庫需要自行拷貝,終極大法就是將你的可執(zhí)行文件復(fù)制到Qt安裝目錄下的bin目錄,然后整個一起打包,挨個刪除不大可能依賴的組件,直到刪到正常運行為止。
64. Qt中的動畫,底層用的是QElapsedTimer定時器來完成處理,比如產(chǎn)生一些指定規(guī)則算法的數(shù)據(jù),然后對屬性進行處理。
65. 在繪制無背景顏色只有邊框顏色的圓形時候,可以用繪制360度的圓弧替代,效果完全一致。
QRect rect(-radius, -radius, radius * 2, radius * 2);//以下兩種方法二選一,其實繪制360度的圓弧=繪制無背景的圓形painter->drawArc(rect, 0, 360 * 16);painter->drawEllipse(rect);
66. 不要把d指針看的很玄乎,其實就是在類的實現(xiàn)文件定義了一個私有類,用來存放局部變量,個人建議在做一些小項目時,沒有太大必要引入這種機制,會降低代碼可讀性,增加復(fù)雜性,新手接受項目后會看的很懵逼。
67. 很多人在繪制的時候,設(shè)置畫筆以為就只可以設(shè)置個單調(diào)的顏色,其實QPen還可以設(shè)置brush,這樣靈活性就提高不知道多少倍,比如設(shè)置QPen的brush以后,可以使用各種漸變,比如繪制漸變顏色的進度條和文字等,而不再是單調(diào)的一種顏色。
68. 很多控件都帶有viewport,比如QTextEdit/QTableWidget/QScrollArea,有時候?qū)@些控件直接處理的時候發(fā)現(xiàn)不起作用,需要對其viewport()設(shè)置才行,比如設(shè)置滾動條區(qū)域背景透明,需要使用scrollArea->viewport()->setStyleSheet("background-color:transparent;");而不是scrollArea->setStyleSheet("QScrollArea{background-color:transparent;}");
69. 有時候設(shè)置了鼠標(biāo)跟蹤setMouseTracking為真,如果該窗體上面還有其他控件,當(dāng)鼠標(biāo)移到其他控件上面的時候,父類的鼠標(biāo)移動事件MouseMove識別不到了,此時需要用到HoverMove事件,需要先設(shè)置 setAttribute(Qt::WA_Hover, true);
70. Qt封裝的QDateTime日期時間類非常強大,可以字符串和日期時間相互轉(zhuǎn)換,也可以毫秒數(shù)和日期時間相互轉(zhuǎn)換,還可以1970經(jīng)過的秒數(shù)和日期時間相互轉(zhuǎn)換等。
QDateTime dateTime;QString dateTime_str = dateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss");//從字符串轉(zhuǎn)換為毫秒(需完整的年月日時分秒)datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toMSecsSinceEpoch();//從字符串轉(zhuǎn)換為秒(需完整的年月日時分秒)datetime.fromString("2011-09-10 12:07:50:541", "yyyy-MM-dd hh:mm:ss:zzz").toTime_t();//從毫秒轉(zhuǎn)換到年月日時分秒datetime.fromMSecsSinceEpoch(1315193829218).toString("yyyy-MM-dd hh:mm:ss:zzz");//從秒轉(zhuǎn)換到年月日時分秒(若有zzz,則為000)datetime.fromTime_t(1315193829).toString("yyyy-MM-dd hh:mm:ss[:zzz]");
71. 在我們使用QList、QStringList、QByteArray等鏈表或者數(shù)組的過程中,如果只需要取值,而不是賦值,強烈建議使用 at() 取值而不是 [] 操作符,在官方書籍《C GUI Qt 4編程(第二版)》的書中有特別的強調(diào)說明,此教材的原作者據(jù)說是Qt開發(fā)的核心人員編寫的,所以還是比較權(quán)威,至于使用 at() 與使用 [] 操作符速度效率的比較,網(wǎng)上也有網(wǎng)友做過此類對比。原文在書的212頁,這樣描述的:Qt對所有的容器和許多其他類都使用隱含共享,隱含共享是Qt對不希望修改的數(shù)據(jù)決不進行復(fù)制的保證,為了使隱含共享的作用發(fā)揮得最好,可以采用兩個新的編程習(xí)慣。第一種習(xí)慣是對于一個(非常量的)向量或者列表進行只讀存取時,使用 at() 函數(shù)而不用 [] 操作符,因為Qt的容器類不能辨別 [] 操作符是否將出現(xiàn)在一個賦值的左邊還是右邊,他假設(shè)最壞的情況出現(xiàn)并且強制執(zhí)行深層賦值,而 at() 函數(shù)則不被允許出現(xiàn)在一個賦值的左邊。
72. 如果是dialog窗體,需要在exec以后還能讓其他代碼繼續(xù)執(zhí)行,請在dialog窗體exec前增加一行代碼,否則會阻塞窗體消息。
QDialog dialog;dialog.setWindowModality(Qt::WindowModal);dialog.exec();
73. 安全的刪除Qt的對象類,強烈建議使用deleteLater而不是delete,因為deleteLater會選擇在合適的時機進行釋放,而delete會立即釋放,很可能會出錯崩潰。如果要批量刪除對象集合,可以用qDeleteAll,比如 qDeleteAll(btns);
74. 在QTableView控件中,如果需要自定義的列按鈕、復(fù)選框、下拉框等其他模式顯示,可以采用自定義委托QItemDelegate來實現(xiàn),如果需要禁用某列,則在自定義委托的重載createEditor函數(shù)返回0即可。自定義委托對應(yīng)的控件在進入編輯狀態(tài)的時候出現(xiàn),如果想一直出現(xiàn),則需要重載paint函數(shù)用drawPrimitive或者drawControl來繪制。
75. 將 QApplication::style() 對應(yīng)的drawPrimitive、drawControl、drawItemText、drawItemPixmap等幾個方法用熟悉了,再結(jié)合QStyleOption屬性,可以玩轉(zhuǎn)各種自定義委托,還可以直接使用paint函數(shù)中的painter進行各種繪制,各種牛逼的表格、樹狀列表、下拉框等,絕對屌炸天。QApplication::style()->drawControl 的第4個參數(shù)如果不設(shè)置,則繪制出來的控件不會應(yīng)用樣式表。
76. 心中有坐標(biāo),萬物皆painter,強烈建議在學(xué)習(xí)自定義控件繪制的時候,將qpainter.h頭文件中的函數(shù)全部看一遍、試一遍、理解一遍,這里邊包含了所有Qt內(nèi)置的繪制的接口,對應(yīng)的參數(shù)都試一遍,你會發(fā)現(xiàn)很多新大陸,會大大激發(fā)你的繪制的興趣,猶如神筆馬良一般,策馬崩騰遨游代碼繪制的世界。
77. 在使用setItemWidget或者setCellWidget的過程中,有時候會發(fā)現(xiàn)設(shè)置的控件沒有居中顯示而是默認的左對齊,而且不會自動拉伸填充,對于追求完美的程序員來說,這個可不大好看,有個終極通用辦法就是,將這個控件放到一個widget的布局中,然后將widget添加到item中,這樣就完美解決了,而且這樣可以組合多個控件產(chǎn)生復(fù)雜的控件。
//實例化進度條控件QProgressBar *progress = new QProgressBar;//增加widget 布局巧妙實現(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. 很多時候需要在已知背景色的情況下,能夠清晰的繪制文字,這個時候需要計算對應(yī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. 對QTableView或者QTableWidget禁用列拖動。
#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)廢棄或者過時了,如果想要在Qt5中啟用Qt4的方法,比如QHeadVew的setMovable,可以在你的pro或者pri文件中加上一行即可:DEFINES = QT_DISABLE_DEPRECATED_BEFORE=0
81. Qt中的QColor對顏色封裝的很完美,支持各種轉(zhuǎn)換,比如rgb、hsb、cmy、hsl,對應(yīng)的是toRgb、toHsv、toCmyk、toHsl,還支持透明度設(shè)置,顏色值還能轉(zhuǎn)成16進制格式顯示。
QColor color(255, 0, 0, 100);qDebug() << color.name() << color.name(QColor::HexArgb);//輸出 #ff0000 #64ff0000
82. QVariant類型異常的強大,可以說是萬能的類型,在進行配置文件的存儲的時候,經(jīng)常會用到QVariant的轉(zhuǎn)換,QVariant默認自帶了toString、toFloat等各種轉(zhuǎn)換,但是還是不夠,比如有時候需要從QVariant轉(zhuǎn)到QColor,而卻沒有提供toColor的函數(shù),這個時候就要用到萬能辦法。
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中如果用后者,字符串中文就會不正確,英文正常。
84. Qt的信號槽機制非常牛逼,也是Qt的獨特的核心功能之一,有時候我們在很多窗體中傳遞信號來實現(xiàn)更新或者處理,如果窗體層級比較多,比如窗體A的父類是窗體B,窗體B的父類是窗體C,窗體C有個子窗體D,如果窗體A一個信號要傳遞給窗體D,問題來了,必須先經(jīng)過窗體B中轉(zhuǎn)到窗體C再到窗體D才行,這樣的話各種信號關(guān)聯(lián)信號的connect會非常多而且管理起來比較亂,可以考慮增加一個全局的單例類AppEvent,公共的信號放這里,然后窗體A對應(yīng)信號綁定到AppEvent,窗體D綁定AppEvent的信號到對應(yīng)的槽函數(shù)即可,干凈清爽整潔。
85. QTextEdit右鍵菜單默認英文的,如果想要中文顯示,加載widgets.qm文件即可,一個Qt程序中可以安裝多個翻譯文件,不沖突。
86. Qt中有個全局的焦點切換信號focusChanged,可以用它做自定義的輸入法。Qt4中默認會安裝輸入法上下文,比如在main函數(shù)打印a.inputContext會顯示值,這個默認安裝的輸入法上下文,會攔截兩個牛逼的信號QEvent::RequestSoftwareInputPanel和QEvent::CloseSoftwareInputPanel,以至于就算你安裝了全局的事件過濾器依然識別不到這兩個信號,你只需要在main函數(shù)執(zhí)行a.setInputContext(0)即可,意思是安裝輸入法上下文為空。
87. 在Qt5.10以后,表格控件QTableWidget或者QTableView的默認最小列寬改成了15,以前的版本是0,所以在新版的qt中,如果設(shè)置表格的列寬過小,不會應(yīng)用,取的是最小的列寬。所以如果要設(shè)置更小的列寬需要重新設(shè)置ui->tableView->horizontalHeader()->setMinimumSectionSize(0);
88. 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.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ù)都是默認異步的,操作系統(tǒng)自動調(diào)度,完全不會卡住界面,網(wǎng)上那些說收發(fā)數(shù)據(jù)卡住界面主線程的都是扯幾把蛋,真正的耗時是在運算以及運算后的處理,而不是收發(fā)數(shù)據(jù),在一些小數(shù)據(jù)量運算處理的項目中,一般不建議動用線程去處理,線程需要調(diào)度開銷的,不要什么東西都往線程里邊扔,線程不是萬能的。只有當(dāng)真正需要將一些很耗時的操作比如編碼解碼等,才需要移到線程處理。
90. 在構(gòu)造函數(shù)中獲取控件的寬高很可能是不正確的,需要在控件首次顯示以后再獲取才是正確的,控件是在首次顯示以后才會設(shè)置好正確的寬高值,記住是在首次顯示以后,而不是構(gòu)造函數(shù)或者程序啟動好以后,如果程序啟動好以后有些容器控件比如QTabWidget中的沒有顯示的頁面的控件,你去獲取寬高很可能也是不正確的,萬無一失的辦法就是首次顯示以后去獲取。
91. 數(shù)據(jù)庫處理一般建議在主線程,如果非要在其他線程,務(wù)必記得打開數(shù)據(jù)庫也要在那個線程,即在那個線程使用數(shù)據(jù)庫就在那個線程打開,不能打開數(shù)據(jù)庫在主線程,執(zhí)行sql在子線程,很可能出問題。
92. 新版的QTcpServer類在64位版本的Qt下很可能不會進入incomingConnection函數(shù),那是因為Qt5對應(yīng)的incomingConnection函數(shù)參數(shù)變了,由之前的int改成了qintptr,改成qintptr有個好處,在32位上自動是quint32而在64位上自動是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
93. Qt支持所有的界面控件比如QPushButton、QLineEdit自動關(guān)聯(lián) on_控件名_信號(參數(shù)) 信號槽,比如按鈕的單擊信號 on_pushButton_clicked(),然后直接實現(xiàn)槽函數(shù)即可。
94. QWebEngineView控件由于使用了opengl,在某些電腦上可能由于opengl的驅(qū)動過低會導(dǎo)致花屏或者各種奇奇怪怪的問題,比如showfullscreen的情況下鼠標(biāo)右鍵失效,需要在main函數(shù)啟用軟件opengl渲染。
#if (QT_VERSION > QT_VERSION_CHECK(5,4,0)) //下面兩種方法都可以,Qt默認采用的是AA_UseDesktopOpenGL QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); //QCoreApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);#endif QApplication a(argc, argv);
另外一個方法解決 全屏 QWebEngineView控件一起會產(chǎn)生右鍵菜單無法彈出的bug,需要上移一個像素
QRect rect = qApp->desktop()->geometry();rect.setY(-1);rect.setHeight(rect.height());this->setGeometry(rect);
95. QStyle內(nèi)置了很多方法用處很大,比如精確獲取滑動條鼠標(biāo)按下處的值。
QStyle::sliderValueFromPosition(minimum(), maximum(), event->x(), width());
96. 用QFile讀寫文件的時候,推薦用QTextStream文件流的方式來讀寫文件,速度快很多,基本上會有30%的提升,文件越大性能區(qū)別越大。
//從文件加載英文屬性與中文屬性對照表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文件默認是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.stCodec("utf-8"); while (!in.atEnd()) { QString line; in >> line; list << line; } qss = list.join("n");#else //用readAll讀取默認支持的是ANSI格式,如果不小心用creator打開編輯過了很可能打不開 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)完并打印的時候你會發(fā)現(xiàn)精確少了,只剩下三位了,其實原始數(shù)據(jù)還是完整的精確度的,只是打印的時候優(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ù)的時候,會發(fā)現(xiàn)總是會多一個節(jié)點內(nèi)容,并且內(nèi)容為空,如果需要跳過則增加一行代碼。
while (it.hasNext()) { it.next(); if (it.flags() & QScriptValue::SkipInEnumeration) continue; qDebug() << it.name();}
100. setPixmap是最糟糕的貼圖方式,一般只用來簡單的不是很頻繁的貼圖,頻繁的建議painter繪制,默認雙緩沖,在高級點用opengl繪制,利用GPU。
101. 如果需要在尺寸改變的時候不重繪窗體,則設(shè)置屬性即可 this->setAttribute(Qt::WA_StaticContents, true); 這樣可以避免可以避免對已經(jīng)顯示區(qū)域的重新繪制。
102. 默認程序中獲取焦點以后會有虛邊框,如果看著覺得礙眼不舒服可以去掉,設(shè)置樣式即可:setStyleSheet("*{outline:0px;}");
103. Qt表格控件一些常用的設(shè)置封裝,QTableWidget繼承自QTableView,所以下面這個函數(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); //默認行高 tableView->verticalHeader()->setDefaultSectionSize(rowHeight); //選中時一行整體選中 tableView->setSelectionBehavior(QAbstractItemView::SelectRows); //只允許選擇單個 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)按下即進入編輯模式 if (edit) { tableView->setEditTriggers(QAbstractItemView::CurrentChanged | QAbstractItemView::DoubleClicked); } else { tableView->setEditTriggers(QAbstractItemView::NoEditTriggers); }}
104. 在一些大的項目中,可能嵌套了很多子項目,有時候會遇到子項目依賴其他子項目的時候,比如一部分子項目用來生成動態(tài)庫,一部分子項目依賴這個動態(tài)庫進行編譯,此時就需要子項目按照順序編譯。
TEMPLATE = subdirs#設(shè)置ordered參數(shù)以后會依次編譯 demo designer examplesCONFIG = orderedSUBDIRS = demoSUBDIRS = designerSUBDIRS = examples
105. MSVC編譯器的選擇說明
– 如果是32位的Qt則編譯器選擇x86開頭的
– 如果是64位的Qt則編譯器選擇amd64開頭的
– 具體是看安裝的Qt構(gòu)建套件版本以及目標(biāo)運行平臺的系統(tǒng)位數(shù)和架構(gòu)
– 一般現(xiàn)在的電腦默認以64位的居多,選擇amd64即可
– 如果用戶需要兼容32位的系統(tǒng)則建議選擇32位的Qt,這樣即可在32位也可以在64位系統(tǒng)運行
– 諸葛大佬補充:x86/x64都是編譯環(huán)境和運行環(huán)境相同,沒有或。帶下劃線的就是交叉編譯,前面是編譯環(huán)境,后面是運行環(huán)境。
| 名稱 | 說明 |
| —— | —— |
|x86|32/64位系統(tǒng)上編譯在32/64位系統(tǒng)上運行|
|x86_amd64|32/64位系統(tǒng)上編譯在64位系統(tǒng)上運行|
|x86_arm|32/64位系統(tǒng)上編譯在arm系統(tǒng)上運行|
|amd64|64位系統(tǒng)上編譯在64位系統(tǒng)上運行|
|amd64_x86|64位系統(tǒng)上編譯在32/64位系統(tǒng)上運行|
|amd64_arm|64位系統(tǒng)上編譯在arm系統(tǒng)上運行|
106. 很多時候用QDialog的時候會發(fā)現(xiàn)阻塞了消息,而有的時候我們希望是后臺的一些消息繼續(xù)運行不要終止,此時需要做個設(shè)置。
QDialog dialog;dialog.setWindowModality(Qt::WindowModal);
107. 很多初學(xué)者甚至幾年工作經(jīng)驗的人,對多線程有很深的誤解和濫用,尤其是在串口和網(wǎng)絡(luò)通信這塊,什么都往多線程里面丟,一旦遇到界面卡,就把數(shù)據(jù)收發(fā)啥的都搞到多線程里面去,殊不知絕大部分時候那根本沒啥用,因為沒找到出問題的根源。
– 如果你沒有使用wait***函數(shù)的話,大部分的界面卡都出在數(shù)據(jù)處理和展示中,比如傳過來的是一張圖片的數(shù)據(jù),你需要將這些數(shù)據(jù)轉(zhuǎn)成圖片,這個肯定是耗時的;
– 還有就是就收到的數(shù)據(jù)曲線繪制出來,如果過于頻繁或者間隔過短,肯定會給UI造成很大的壓力的,最好的辦法是解決如何不要頻繁繪制UI比如合并數(shù)據(jù)一起繪制等;
– 如果是因為繪制UI造成的卡,那多線程也是沒啥用的,因為UI只能在主線程;
– 串口和網(wǎng)絡(luò)的數(shù)據(jù)收發(fā)默認都是異步的,由操作系統(tǒng)調(diào)度的,如果數(shù)據(jù)處理復(fù)雜而且數(shù)據(jù)量大,你要做的是將數(shù)據(jù)處理放到多線程中;
– 如果沒有嚴格的數(shù)據(jù)同步需求,根本不需要調(diào)用wait***之類的函數(shù)來立即發(fā)送和接收數(shù)據(jù),實際需求中大部分的應(yīng)用場景其實異步收發(fā)數(shù)據(jù)就足夠了;
– 有嚴格數(shù)據(jù)同步需求的場景還是放到多線程會好一些,不然你wait***就卡在那邊了;
– 多線程是需要占用系統(tǒng)資源的,理論上來說,如果線程數(shù)量超過了CPU的核心數(shù)量,其實多線程調(diào)度可能花費的時間更多,各位在使用過程中要權(quán)衡利弊;
108. 在嵌入式linux上,如果設(shè)置了無邊框窗體,而該窗體中又有文本框之類的,發(fā)現(xiàn)沒法產(chǎn)生焦點進行輸入,此時需要主動激活窗體才行。
//這種方式設(shè)置的無邊框窗體在嵌入式設(shè)備上無法產(chǎn)生焦點setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint);//需要在show以后主動激活窗體w->show();w->activateWindow();
109. QString的replace函數(shù)會改變原字符串,切記,他在返回替換后的新字符串的同時也會改變原字符串,我的乖乖!
110. QGraphicsEffect類的相關(guān)效果很炫,可以實現(xiàn)很多效果比如透明、漸變、陰影等,但是該類很耗CPU,如果不是特別需要一般不建議用,就算用也是要用在該部件后期不會發(fā)生頻繁繪制的場景,不然會讓你哭暈在廁所。
111. 在不同的平臺上文件路徑的斜杠也是不一樣的,比如linux系統(tǒng)一般都是 / 斜杠,而在windows上都是 兩個反斜杠,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:temptest.txtQString path = "C:temptest.txt";path = QDir::toNativeSeparators(path);//輸出 C:/temp/test.txt
112. 巧用QMetaObject::invokeMethod方法可以實現(xiàn)很多效果,包括同步和異步執(zhí)行,比如有個應(yīng)用場景是在回調(diào)中,需要異步調(diào)用一個public函數(shù),如果直接調(diào)用的話會發(fā)現(xiàn)不成功,此時需要使用 QMetaObject::invokeMethod(obj, "fun", Qt::QueuedConnection); 這種方式來就可以。invokeMethod函數(shù)有很多重載參數(shù),可以傳入返回值和執(zhí)行方法的參數(shù)等。
113. Qt5中的信號是public的,可以在需要的地方直接emit即可,而在Qt4中信號是protected的,不能直接使用,需要定義一個public函數(shù)來emit。
114. Qt5.15版本開始官方不再提供安裝包,只提供源碼,可以自行編譯或者在線安裝,估計每次編譯各種版本太麻煩,更多的是為了統(tǒng)計收集用戶使用信息比如通過在線安裝,后期可能會逐步加大商業(yè)化力度。