Qt開發經驗小技巧171-175

語言: CN / TW / HK
  1. 在Qt程式設計中經常會遇到編碼的問題,由於跨平臺的考慮相容各種系統,而windows系統預設是gbk或者gb2312編碼,當然後期可能msvc編譯器都支援utf8編碼,所以在部分程式中傳入中文目錄檔名稱的時候會發現失敗,因為可能對應的介面用了早期的fopen函式而不是fopen_s函式,比如fmod中也是這個情況。這個時候就需要轉碼處理。
QString fileName = "c:/測試目錄/1.txt";
//如果應用程式main函式中沒有設定編碼則預設採用系統的編碼,可以直接通過toLocal8Bit轉成正確的資料
const char *name = fileName.toLocal8Bit().constData();

//如果設定過了下面兩句則需要主動轉碼
QTextCodec *codec = QTextCodec::codecForName("utf-8");
QTextCodec::setCodecForLocale(codec);

QTextCodec *code = QTextCodec::codecForName("gbk");
const char *name = code->fromUnicode(fileName).constData();

//推薦方式2以防萬一保證絕對的正確,哪怕是設定過主程式的編碼
//切記一旦設定過QTextCodec::setCodecForLocale會影響toLocal8Bit

//有時候可能還有下面這種情況
#ifdef Q_OS_WIN
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
    QTextCodec *code = QTextCodec::codecForName("utf-8");
#else
    QTextCodec *code = QTextCodec::codecForName("gbk");
#endif
    const char *name = code->fromUnicode(fileName).constData();
#else
    const char *name = fileName.toUtf8().constData();
#endif
  1. 在查閱和學習Qt原始碼的過程中,發現了一些趨勢和改變。
  • 資料型別這塊儘量用Qt內部的資料型別,哪怕是重定義過的比如quint8其實unsigned char,qreal就是double,以前翻看原始碼的時候可能還有些是double,現在慢慢改成了qreal。
  • 迴圈結構用 for(;;) 替代 while(1),因為轉成彙編指令後 for(;;) 只有一條指令而 while(1) 確有4條,指令少不佔用暫存器而且不用跳轉,理論上速度要更快。
  • 其實Qt中就重定義了 forever 關鍵字表示 for(;;) ,我的乖乖,想的真周到。
  • 自動c++11以及後續的標準都支援auto萬能資料型別,發現Qt的原始碼中也慢慢的改成了auto,這樣加快了編寫程式碼的效率,不用自己去指定資料型別而是讓編譯器自己推導資料型別。而且其實也不影響編譯器編譯的速度,因為無論指定和沒有指定資料型別,編譯器都要推導右側的資料型別進行判斷。不過有個缺點就是影響了閱讀程式碼的成本,很多時候需要自己去理解推導。
  1. Qt中設定或者開啟載入本地檔案需要用到QUrl類,本地檔案建議加上 file:/// 字首。
QString url = "file:///c:/1.html";
//瀏覽器控制元件開啟本地網頁檔案
webView->setUrl(QUrl(url));
//開啟本地網頁檔案,下面兩種方法都可以
QDesktopServices::openUrl(QUrl::fromLocalFile(url));
QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
  1. 在網路請求中經常涉及到超時時間的問題,因為預設是30秒鐘,一旦遇到網路故障的時候要等好久才能反應過來,所以需要主動設定下超時時間,超過了就直接中斷結束請求。從Qt5.15開始內建了setTransferTimeout來設定超時時間,非常好用。
//區域性的事件迴圈,不卡主介面
QEventLoop eventLoop;

//設定超時 5.15開始自帶了超時時間函式 預設30秒
#if (QT_VERSION >= QT_VERSION_CHECK(5,15,0))
manager->setTransferTimeout(timeout);
#else
QTimer timer;
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
timer.setSingleShot(true);
timer.start(timeout);
#endif

QNetworkReply *reply = manager->get(QNetworkRequest(QUrl(url)));
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
eventLoop.exec();

if (reply->bytesAvailable() > 0 && reply->error() == QNetworkReply::NoError) {
    //讀取所有資料儲存成檔案
    QByteArray data = reply->readAll();
    QFile file(dirName + fileName);
    if (file.open(QFile::WriteOnly | QFile::Truncate)) {
        file.write(data);
        file.close();
    }
}
  1. Qt中基本上有三大型別的專案,控制檯專案對應QCoreApplication、傳統QWidget介面程式對應QApplication、quick/qml專案程式對應QGuiApplication。有很多屬性的開啟需要在main函式的最前面執行才有效果,比如開啟高分屏支援、設定opengl模式等。不同型別的專案需要對應的QApplication。
//如果是控制檯程式則下面的QApplication換成QCoreApplication
//如果是quick/qml程式則下面的QApplication換成QGuiApplication
int main(int argc, char *argv[])
{
    //可以用下面這行測試Qt自帶的輸入法 qtvirtualkeyboard
    qputenv("QT_IM_MODULE", QByteArray("qtvirtualkeyboard"));
    
    //設定不應用作業系統設定比如字型
    QApplication::setDesktopSettingsAware(false);

#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
    //設定高分屏縮放舍入策略
    QApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::Floor);
#endif
#if (QT_VERSION > QT_VERSION_CHECK(5,6,0))
    //設定啟用高分屏縮放支援
    //要注意開啟後計算到的控制元件或介面寬度高度可能都不對,全部需要用縮放比例運算下
    QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    //設定啟用高分屏圖片支援
    QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
#endif
#if (QT_VERSION > QT_VERSION_CHECK(5,4,0))
    //設定opengl模式 AA_UseDesktopOpenGL(預設) AA_UseSoftwareOpenGL AA_UseOpenGLES
    //QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
    //設定opengl共享上下文
    QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
#endif

    QApplication a(argc, argv);
    QWidget w;
    w.show();
    return a.exec();
}