## Prepare ### Windows 10 SDK 下載並安裝[Windows 10 SDK](https://developer.microsoft.com/zh-tw/windows/downloads/windows-10-sdk/),安裝 **Debugging Tools for Windows**就可以了。 ![[chrome_20210309_110223_934x685.png]] 然後在QtCreator裡面設置Debugger。 ![[NVIDIA_Share_20210309_110738_1920x1080.png]] ### Visual Studio 2017 - ![[Pasted image 20210312144218.png]] - ![[Pasted image 20210312144242.png]] - ![[Pasted image 20210312144405.png]] - ![[Pasted image 20210312144523.png]] ## PRO file 語法 ### 導入lib ``` QT += ``` ### 導入include path ``` INCLUDEPATH += ``` 若使用相對路徑,必須相對於makefile生成的路徑。 也可以使用`$$PWD`來生成絕對路徑,例: ``` INCLUDEPATH += $$PWD/../../include ``` > 可以用`message()`來印出變數:`message($$PWD)` ### 導入lib 用`-L`指定library path,用`-l`指定libraray name。例: ``` LIBS += -L"../../lib" -lopencv_world320 ``` ### 指定執行目錄 ``` DESTDIR += ../../bin ``` ### 指定執行檔檔名 ``` TARGET = ``` - Import QT pro file into Visual Studio. - `qmake -tp vc ` - 使用VS工具import。 ## 基本類別(class) ### QIODevice An abstract class that wraps input and output for most devices ### QDataStream 依序寫入的資料,也可以依序被讀出來。資料會被編碼過,所以用Text editor會看到亂碼。 ### QTextStream 可以依序寫入字串,也可以依序被讀出來,因為寫入的是字串,所以用Text editor就可以看到資料。 ### QLockFile 創造一個在操作時可以獨佔的檔案。避免race condition的情形。 ```cpp QLockFile lock(fileName() +".lock"); lock.setStaleLockTime(30000); if(lock.tryLock()) { ... lock.unlock(); } else { qInfo() << "Could not lock the file!"; qint64 pid; QString host; QString application; if(lock.getLockInfo(&pid,&host,&application)) { qInfo() << "The file is locked by:"; qInfo() << "Pid: " << pid; qInfo() << "Host: " << host; qInfo() << "Application: " << application; } else { qInfo() << "File is locked, but we can't get the info!"; } } ``` ### QtMessageHandler 可以用來重新導向或攔截`qInfo()`、`qDebug()`、`qWarning()`、`qCritical()`、`qFatal()`這些function ```cpp const QtMessageHandler QT_DEFAULT_MESSAGE_HANDLER = qInstallMessageHandler(nullptr); void lhandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QString txt; switch (type) { case QtInfoMsg: txt = QString("Info: %1 in %2").arg(msg); break; case QtDebugMsg: txt = QString("Debug: %1").arg(msg); break; case QtWarningMsg: txt = QString("Warning: %1").arg(msg); break; case QtCriticalMsg: txt = QString("Critical: %1").arg(msg); break; case QtFatalMsg: txt = QString("Fatal: %1").arg(msg); break; } ... (*QT_DEFAULT_MESSAGE_HANDLER)(type, context, msg); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qInstallMessageHandler(lhandler); qInfo() << "This is a info message"; qDebug() << "This is a debug message"; qWarning() << "This is a warning message"; qCritical() << "This is a critical message"; qFatal("THIS IS SPARTA!!!"); return a.exec(); } ``` 也可以把這機制封裝在class裡面 ```cpp // logger.h #ifndef LOGGER_H #define LOGGER_H #include #include #include #include #include #include #include class logger : public QObject { Q_OBJECT public: explicit logger(QObject *parent = nullptr); static bool logging; static QString filename; static void attach(); static void handler(QtMsgType type, const QMessageLogContext &context, const QString & msg); signals: public slots: }; #endif // LOGGER_H ``` ```cpp // logger.cpp #include "logger.h" QString logger::filename = QDir::currentPath() + QDir::separator() + "log.txt"; bool logger::logging = false; static const QtMessageHandler QT_DEFAULT_MESSAGE_HANDLER = qInstallMessageHandler(nullptr); logger::logger(QObject *parent) : QObject(parent) { } void logger::attach() { logger::logging = true; qInstallMessageHandler(logger::handler); } void logger::handler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { // Here, write log message to file (*QT_DEFAULT_MESSAGE_HANDLER)(type, context, msg); } ``` ### Logging category Logging category可以用來把輸出的log前面加上一個字串以便分類。 ```cpp #include #include #include //Declare a logging category Q_DECLARE_LOGGING_CATEGORY(network); Q_LOGGING_CATEGORY(network, "network"); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qInfo() << "test"; // stdout: "test" qInfo(network) << "test"; // stdout: "network: test" //turn it off QLoggingCategory::setFilterRules("network.debug=false"); //Will not log qDebug(network) << This message will not shown; if(!network().isDebugEnabled()) { QLoggingCategory::setFilterRules("network.debug=true"); qDebug(network) << "We turned it back on"; } qDebug(network) << "You see this because you turn the message on"; return a.exec(); } ``` ## QThread 1. 繼承自QThread。 2. 重載`run()`。 3. 呼叫`start()`來開始thread。 ## QWidget ### 設定signal與對應的slot 1. 在class header裡面加入`Q_OBJECT`。 2. 在constructor裡,呼叫`connect()`來建立對應關係:`connect(SOURCE_OBJECT, SIGNAL(clicked()), DEST_OBJECT, SLOT(close()))`。 ### Postion 1. `QRect geometry();` 取得x, y, width, height 2. `setGeometry();` 設定x, y, width, height 3. `x()`, `y()`, `width()`, `height()` 單獨取得x, y, width, height 4. `move(x, y);` 只設定x, y 5. `resize(width, height);` 只設定width, height ### Window state 1. `setWindowState(Qt::WindowMaximized);` 1. `Qt::WindowMaximized` -> `showMaximized()` 2. `Qt::WindowMinimized` -> `showMinimized()` 3. `Qt::WindowNoState` -> `showNormal()` 4. `Qt::WindowFullScreen` -> `showFullScreen()` ### Window Style 1. `CustomizeWindowHint()` 2. `setWindowFlags(Qt::WindowCloseButtonHint | Qt::WindowMinimizeButtonHint)` 1. Qt::WindowCloseButtonHint 2. Qt::WindowMinimizeButtonHint 3. `setWindowFlag(Qt::WindowCloseButtonHint, false)` // 取消close按鈕 1. Qt::WindowCloseButtonHint 2. Qt::WindowMinimizeButtonHint 4. 沒有邊框的視窗:`setWindowFlags(Qt::FramelessWindowHint)` 5. 透明背景: `setAttribute(Qt::WA_TranslucentBackground, true)` 6. Style sheet: https://doc.qt.io/qt-5/stylesheet-syntax.html#style-rules ### Layout - QVBoxLayout ```cpp QVBoxLayout* pVlayout = new QVBoxLayout(); widget->setLayout(pVlayout); // Add button to layout QPushButton* button = new QPushButton("My Button"); pVlayout->addWidget(button); ``` - `setContentsMargin()`:設定元與layout的距離 - `setSpacing()`:設定元件與元件之間的距離 - QFormLayout - Iterate all items ```cpp QFormLayout* layout = (QFormLayout*)this->layout(); for (int i = 0; i < layout->rowCount(); i++) { QLayoutItem* item = layout->itemAt(i, QFormLayout::FieldRole); QWidget* widget = (QWidget*)item->widget(); if (widget) { QString className = widget->metaObject()->className(); if (className == "QLineEdit") { QLineEdit* edit = (QLineEdit*)widget; edit->setText("Text changed"); } } else { continue; } } ``` #### QSizePolicly - QSizePolicly::GrowFlag:必要時可以超過 - QSizePolicly::ExpandFlag:放到最大 - QSizePolicly::ShrinkFlag:必要時可以小於建議尺寸 - QSizePolicly::IgnoreFlag:忽略 ## QLabel - `QLabel.setTextIneractionFlags()`: ## QSlider ### Style #### groove ``` QSlider::groove { border: 1px solid #999999; height: 28px; background: rgba(155, 155, 155, 200); border-radius: 10px; } ``` #### handle ``` QSlider::handle { border: 1px solid #FF0000; width: 20px; margin: -10px 0; background: rgba(255, 0, 0, 200); border-radius: 10px; } ``` #### add-page ``` QSlider::add-page { background: rgba(255, 0, 0, 200); } ``` #### sub-page ``` QSlider::add-page { background: rgba(0, 255, 0, 200); } ``` ![[Pasted image 20210318115725.png]] ## QListWidget - Insert a QLineEdit ```cpp QLineEdit* lineEdit = new QLineEdit("A line edit here"); ui->listWidget->setItemWidget(new QListWidgetItem("", ui->listWidget), lineEdit); ``` - Insert a QSpinBox ``` QSpinBox* spinBox = new QSpinBox(); ui->listWidget->setItemWidget(new QListWidgetItem("Test item 5", ui->listWidget), spinBox); ``` ### Stylesheet example ``` QTableView { selection-background-color: qlineargradient(x1: 0, y1: 0, x2: 0.5, y2: 0.5, stop: 0 #FF92BB, stop: 1 white); } ``` ``` QTableView QTableCornerButton::section { background: red; border: 2px outset red; } ``` ``` QTableView::indicator:unchecked { background-color: #d7d6d5 } ``` ## QTableWidget - setColumnCount() - https://doc.qt.io/qt-5/qtablewidget.html#setColumnCount - setHorizontalHeaderItem(column_index, new QTableWidgetItem("Header1")) - https://doc.qt.io/qt-5/qtablewidget.html#setHorizontalHeaderItem - setColumnWidth(column_index, column_width) - https://doc.qt.io/qt-5/qtableview.html#setColumnWidth - Bind Header's event - `connect(ui.tableWidget->horizontalHeader(), SIGNAL(sectionClicked(int)), this, SLOT(SectionClicked(int)));` - Style ``` QHeaderView::section:checked{ /* Focus */ background-color: rgba(55, 55, 55,255 ); } QHeaderView::section:hover{ /* 滑鼠懸停 */ background-color: rgba(85, 10, 10,255 ); } QTableWidget{ background-color: rgb(27, 122, 239); alternate-background-color: rgb(26, 81, 232); color:white; } ``` ## QTreeWidget - Data create & insert - `insertTopLevelItem(int index, QTreeWidgetItem*)` - `addTopLevelItem(QTreeWidgetItem*)` - `new QTreeWidgetItem(QTreeWidget*)` - Insert widget - `void setItemWidget(QTreeWidgetItem* item, int column, QWidget* widget)` - Iterate items - `QTreeWidgetItem* topLevelItem(int index) const` - `int topLevelItemCount() const` - Get item - `QList selectedItems() const` - Style ```cpp /* 有旁節點,沒有與其他元素相鄰 */ QTreeView::branch:has-siblings:!adjoins-item { border-image: url(vline.png) 0; } /* 有旁節點,與其他元素相鄰 */ QTreeView::branch:has-siblings:adjoins-item { border-image: url(branch-more.png) 0; } /* 沒有子節點,沒有旁節點,與其他元素相鄰 */ QTreeView::branch:!has-children:!has-siblings:adjoins-item { border-image: url(branch-end.png) 0; } /* 收合狀態,有子節點,沒有旁節點 */ QTreeView::branch:closed:has-children:!has-siblings, /* 收合狀態,有子節點,有旁節點 */ QTreeView::branch:closed:has-children:has-siblings { border-image: none; image: url(branch-closed.png); } /* 打開狀態,有子節點,沒有旁節點 */ QTreeView::branch:open:has-children:!has-siblings, /* 打開狀態,有子節點,有旁節點 */ QTreeView::branch:open:has-children:has-siblings { border-image: none; image: url(branch-open.png); } ``` ## QStatusBar - `showMessage(QString, int miniSeconds)`: 顯示文字,一段時間後消失 ## QToolBar - `addAction(QIcon, QString)` - `addAction(QString)` - `setToolButtonStyle(int)` ## QDockWidget - https://blog.csdn.net/czyt1988/article/details/51209619 ## QJson - [C++(Qt5)入門範例:各種Json範例](https://lolikitty.pixnet.net/blog/post/206254516) ## QFile Read UTF-8 content from file. ```cpp QString filename = QString::fromLocal8Bit("testplan中文檔名也可以.json"); QFile file(filename); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream fileRead(&file); fileRead.setCodec("UTF-8"); QString fileContent = fileRead.readAll(); printf("fileContent = %s\n", fileContent.toLocal8Bit().toStdString().c_str()); file.close(); parseJson(fileContent); } else { printf("Open failed. %s\n", filename.toLocal8Bit().toStdString().c_str()); } ``` ## String ### UTF-8 1. 對於n bytes的字元,第一個byte的前n個bits是1,第n+1個bit是0,第2個byte以後,都以`10`開頭。 1. 例: 2. 1 byte character: 0x0XXXXXXX。 3. 2 bytes character: 0x110XXXXXX 0x10XXXXXX。 4. 3 bytes character: 0x1110XXXXX 0x10XXXXXX 0x10XXXXXX。 5. 4 bytes character: 0x11110XXXX 0x10XXXXXX 0x10XXXXXX 0x10XXXXXX。 2. 例: 1. 「A」是一個1 byte字元,編碼為`0x01000001`。 2. 「Á」是一個2 bytes字元,編碼為`0x11000011 0x10000001`。 3. 「好」是一個3 bytes字元,編碼為`0x11100101 0x10100101 0x10111101`。 3. [UTF-8 to binary轉換網站](https://onlineutf8tools.com/convert-utf8-to-binary) 2. BOM 1. Big endian: `FE FF` 2. Little endian: `FF FE` ### QString - 16 bits QChar(unsigned short), UNIDODE 4.0 - `==`: compare string - `isNull()`: If has a value - `isEmpty()`: If contained value == "" - `QString::number()`: 接收一個數字,把數字轉為字串。 - `QString::toInt()`、`QString::toDouble()`:把字串轉為Int或Double。 - 讀取UTF-8的字串: ``` codec = QTextCodec::codecFromName("UTF-8"); QTextCodec::setCodecForLocale(codec); ``` 然後用`QString::fromLocal8Bit()`來讀取字串,該字串就會被當作UTF-8來處理。 - `QTextCodec::availableCodecs()`可以列出QT所支援的格式。 - 在Visual Studio中,強制文件用UTF-8來存檔:`#pragma execution_character_set("UTF-8")` - 在Visual Studio中,預設是以CP950存檔,所以字串**都不是**unicode,而是**multi-character**,所以要從Windows讀進來的字串,一律需要使用`QString::fromLocal8Bit("")`,而一旦轉為QString之後,就是UTF-8了,若需要再輸出回Windows,則需要`QString.toLocal8Bit().toStdString()`。 - 例: ```cpp QString filename = QString::fromLocal8Bit("testplan中文檔名也可以.json"); QFile file(filename); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream fileRead(&file); fileRead.setCodec("UTF-8"); QString fileContent = fileRead.readAll(); printf("fileContent = %s\n", fileContent.toLocal8Bit().toStdString().c_str()); file.close(); parseJson(fileContent); } else { printf("Open failed. %s\n", filename.toLocal8Bit().toStdString().c_str()); } ``` - Visual Studio 2017 debug QString: [调试时直接显示QString的字符值(包含windows和linux)](https://blog.csdn.net/iamdbl/article/details/78343642) #### Search - `QString.indexOf()` - `QString.lastIndexOf()` #### Trim - `QString.chop(5)`:去掉結尾的5個字元,inplace轉換,沒有回傳新字串。 - `QString.left(5)`:取最左邊5個字元 - `QString.right(5)`:取最右邊5個字元 #### Replace - `QString.replace("original", "new")`:將"original"替換為"new"。 - 也支援regular expression #### Split - `QString.split(",")`:用","把字串分割,並回傳一個`QStringList`。 #### Regular Expression - `.`:匹配任意1個字元 - `+`:此類型的匹配字元可以1個以上 - `*`:匹配任意多個字元 - `^`:表示開始位置 - `$`:表示結束位置 - `?`:表示前一個匹配條件不一定要成立 - `[]`:用括號來限制要匹配的字元 ## Event - 如果有特殊需求,可以重載`QWidget::event(QEvent*)`來達成。 ### MouseEvent - Get coordinate of component: `QMouseEvent::x()` & `QMouseEvent::y()` - Get coordinate of window: `QMouseEvent::windowPos().x()` & `QMouseEvent::windowPos().y()` - Get coordinate of screen: `QMouseEvent::screenPos().x()` & `QMouseEvent::screenPos().y()` - `QPoint mapToGlobal(QPoint)`: Translates the widget coordinate pos to global screen coordinates. - `QCursor::pos().x()` & `QCursor::pos().x()`: 也可以獲得screen coordinate。 - Get click ``` if (mouseEvent->buttons() & Qt::LeftButton) // or Qt::RightButton, Qt::MiddleButton { } ``` ### MouseCursor - 設定cursor click的位置:`QCursor cursor = QCursor(pixmap, -1, -1)` - (-1, -1) 表示用cursor中心點為click點 - ( 0, 0) 表示用cursor左上角為click點 - 更換cursor:`setCursor(Qt::ArrowCursor)` ### ResizeEvent - `void resizeEvent(QResizeEvent*)` ## QOpenGLWidget - `void paintGL()` - `void initialGL()` - `void resizeGL(int w, int h)` ## QGLShaderProgram - `addShaderFromSourceCode` - `bindAttributeLocation`:設定傳入的變數 - `uniformLocation`:取得變數 ## GL概念 ### Vertex #### Coordinate ``` float *vertexData = new float[12] { 1.0f, -1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, }; ``` ### Texture #### Coordinate 材質座標永遠都在第一象限 ``` float* textureVertexData = new float[8] { 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, }; ``` #### 建立材質 - `glGenTextures(1, t);` - `glBindTexture(GL_TEXTURE_2D, *t);` - `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);` - `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);` #### 寫入材質 - `glActiveTexture(GL_TEXTURE0);` - `glBindTexture(GL_TEXTURE_2D, idY);` - `glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, pixelW, pixelH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, plane[0]);` ``` glTexImage2D( GL_TEXTURE_2D, 0, // 細節,預設0 GL_LUMINANCE, // GPU內部格式 pixelW, pixelH, 0, GL_LUMINANCE, // Pixel format GL_UNSIGNED_BYTE, // Data format plane[0]); ``` - `glTexSubImage2D()`:修改texture - `glUniform1i(textureUniformY, 0);` - `glDrawArray(GL_TRIANGLE_STRIP, 0, 4);` ## GLSL ### 內建變數 - `gl_FragColor` ### 3種變數類型 - `varying`:vertex與shader共用,vertex運算完之後傳給shader使用。 - `attribute`:vertex使用,由`bindAttributeLocation`傳入 - `uniform`:由user傳入的資料,由`uniformLocation`得到pointer address,再由`glUniform1i(textureUniformY, 0)`來設定。 ### Vertex Shader ``` attribute vec4 vertexIn; attribute vec2 textureIn; varying vec2 textureOut; void main() { gl_position = vertexIn; textureOut = textureIn; } ``` ### Fragment Shader ``` varying vec2 textureOut; uniform sampler2D texY; uniform sampler2D texU; uniform sampler2D texV; void main() { vec3 yuv; vec3 rgb; yuv.x = textue2D(texY, textureOut).r; yuv.y = textue2D(texU, textureOut).r - 0.5; yuv.z = textue2D(texV, textureOut).r - 0.5; rgb = mat3( 1, 1, 1, 0, -0.39465, 2.03211, 1.13983, -0.5806, 0) * yuv; gl_FragColor = vec4(rgb, 1); } ``` ### APIs 畫出矩形與材質 - `glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);` - `glEnableVertexAttribArray(ATTRIB_VERTEX);` - `glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);` - `glEnableVertexAttribArray(ATTRIB_TEXTURE);` # 檔案操作 ## 開啟檔案對話框 ```c++ QString selectedFilepath = QFileDialog::getOpenFileName( this, tr("Select test plan file"), // 檔案選擇對話框左上角的提示 tr("."), // 檔案選擇對話框一開始的路徑 "(*.json);;All files(*.*)"); // 可選擇的副檔名 ``` 使用者所選擇的檔名會放在`selectedFilepath`,可以用`selectedFilepath.isEmpty()`來判斷使用者是不是真的有選擇檔案或是按了取消鍵。 ## 存檔對話框 ```c++ QString fileName = QFileDialog::getSaveFileName( this, tr("Save Address Book"), "", // 檔案選擇對話框左上角的提示 tr("Address Book (*.abk);;All Files (*)")); // 存檔的副檔名 ``` # MISC ## 教學文 - [Qt教程,Qt5编程入门教程(非常详细)](http://c.biancheng.net/qt/)