734 lines
20 KiB
Markdown
734 lines
20 KiB
Markdown
## 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 += <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的情形。
|
||
```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 <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
|
||
```
|
||
|
||
```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 <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
|
||
```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<QTreeItemWidget*> 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/) |