Files
Obsidian-Main/05. 資料收集/Programming/QT/Qt.md

20 KiB
Raw Blame History

Prepare

Windows 10 SDK

下載並安裝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 += <lib> <lib2>

導入include path

INCLUDEPATH += <INCLUDE_PATH>

若使用相對路徑必須相對於makefile生成的路徑。 也可以使用$$PWD來生成絕對路徑,例:

INCLUDEPATH += $$PWD/../../include

可以用message()來印出變數:message($$PWD)

導入lib

-L指定library path-l指定libraray name。例

LIBS += -L"../../lib" -lopencv_world320

指定執行目錄

DESTDIR += ../../bin

指定執行檔檔名

TARGET = <NEW_FILENAME>
  • Import QT pro file into Visual Studio.
    • qmake -tp vc <QT_PROJECT_FILE>
    • 使用VS工具import。

基本類別class

QIODevice

An abstract class that wraps input and output for most devices

QDataStream

依序寫入的資料也可以依序被讀出來。資料會被編碼過所以用Text editor會看到亂碼。

QTextStream

可以依序寫入字串也可以依序被讀出來因為寫入的是字串所以用Text editor就可以看到資料。

QLockFile

創造一個在操作時可以獨佔的檔案。避免race condition的情形。

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

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裡面

// logger.h
#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>
#include <QDebug>
#include <QFile>
#include <QDateTime>
#include <QDir>
#include <iostream>
#include <QTextStream>

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
// 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前面加上一個字串以便分類。

#include <QCoreApplication>
#include <QDebug>
#include <QLoggingCategory>

//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

    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
      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

    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

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<QTreeItemWidget*> selectedItems() const
  • Style

    /* 有旁節點,沒有與其他元素相鄰 */
    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

QJson

QFile

Read UTF-8 content from file.

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轉換網站
  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()
    • 例:
      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
  • 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點
  • 更換cursorsetCursor(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種變數類型

  • varyingvertex與shader共用vertex運算完之後傳給shader使用。
  • attributevertex使用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);

檔案操作

開啟檔案對話框

QString selectedFilepath = QFileDialog::getOpenFileName(
    this,
    tr("Select test plan file"),  // 檔案選擇對話框左上角的提示
    tr("."),                      // 檔案選擇對話框一開始的路徑
    "(*.json);;All files(*.*)");  // 可選擇的副檔名

使用者所選擇的檔名會放在selectedFilepath,可以用selectedFilepath.isEmpty()來判斷使用者是不是真的有選擇檔案或是按了取消鍵。

存檔對話框

QString fileName = QFileDialog::getSaveFileName(
    this,
    tr("Save Address Book"), "",  // 檔案選擇對話框左上角的提示
    tr("Address Book (*.abk);;All Files (*)"));  // 存檔的副檔名

MISC

教學文