vault backup: 2022-09-26 18:49:43
Affected files: .obsidian/daily-notes.json .obsidian/plugins/periodic-notes/data.json .obsidian/templates.json .obsidian/workspace 01. 個人/02. 專注Study/Android/ADB 取得 APK 的 icon.md 01. 個人/02. 專注Study/Android/ADB.md 01. 個人/02. 專注Study/Android/AOSP.md 01. 個人/02. 專注Study/Android/Android programming.md 01. 個人/02. 專注Study/Android/Ktor.md 01. 個人/02. 專注Study/Android/Service.md 01. 個人/02. 專注Study/Android/Tools.md 01. 個人/02. 專注Study/Android/UI.md 01. 個人/02. 專注Study/C++/C++17.md 01. 個人/02. 專注Study/C++/Class template.md 01. 個人/02. 專注Study/C++/Structured binding declaration.md 01. 個人/02. 專注Study/C++/for_each.md 01. 個人/02. 專注Study/C++/lambda.md 01. 個人/02. 專注Study/C++/lvalue.md 01. 個人/02. 專注Study/C++/move operator.md 01. 個人/02. 專注Study/C++/rvalue.md 01. 個人/02. 專注Study/C++/智慧指標.md 01. 個人/02. 專注Study/RxKotlin/20200207 - Study RxKotlin.md 03. 資料收集/01. Programming/COM/20210726 - COM Interface.md 03. 資料收集/01. Programming/DB/MySQL.md 03. 資料收集/01. Programming/DB/sqlite.md 03. 資料收集/01. Programming/Design Pattern.md 03. 資料收集/01. Programming/FFMPEG/00. Introduction.md 03. 資料收集/01. Programming/FFMPEG/01. Setup.md 03. 資料收集/01. Programming/FFMPEG/FFMpeg.md 03. 資料收集/01. Programming/Flask.md 03. 資料收集/01. Programming/Media Foundation/20210604 - Windows media foundation.md 03. 資料收集/01. Programming/OpenCV.md 03. 資料收集/01. Programming/OpenGL.md 03. 資料收集/01. Programming/Python/argparse.ArgumentParser.md 03. 資料收集/01. Programming/Python/decorator.md 03. 資料收集/01. Programming/Python/logging.md 03. 資料收集/01. Programming/Python/opencv.md 03. 資料收集/01. Programming/Python/subprocess.md 03. 資料收集/01. Programming/Python/threading.md 03. 資料收集/01. Programming/Python/tkinter.md 03. 資料收集/01. Programming/Python/檢測工具.md 03. 資料收集/01. Programming/QT/Dropdown button.md 03. 資料收集/01. Programming/QT/QVariant.md 03. 資料收集/01. Programming/QT/Qt.md 03. 資料收集/01. Programming/UML.md 03. 資料收集/01. Programming/演算法.md 03. 資料收集/99. templates/blogHeader.md 03. 資料收集/99. templates/date.md 03. 資料收集/99. templates/front matter.md 03. 資料收集/99. templates/note.md 03. 資料收集/99. templates/table.md 03. 資料收集/99. templates/thisWeek.md 03. 資料收集/99. templates/日記.md 03. 資料收集/99. templates/讀書筆記.md 03. 資料收集/Hobby/RC.md 03. 資料收集/Hobby/RC/Traxxas Sledge.md 03. 資料收集/Hobby/RC/好盈電變調整中立點.md 03. 資料收集/Hobby/RC/差速器調教教學.md 03. 資料收集/Linux/CLI/cut.md 03. 資料收集/Linux/CLI/scp.md 03. 資料收集/Linux/CLI/timedatectl.md 03. 資料收集/Programming/Qt.md 03. 資料收集/Tool Setup/Hardware/RaspberryPi.md 03. 資料收集/Tool Setup/Software/Chrome.md 03. 資料收集/Tool Setup/Software/Obisidian.md 03. 資料收集/Tool Setup/Software/SublimeText.md 03. 資料收集/Tool Setup/Software/VirtualBox.md 03. 資料收集/Tool Setup/Software/Visual Studio Code.md 03. 資料收集/Tool Setup/Software/Windows Setup.md 03. 資料收集/Tool Setup/Software/Windows Terminal.md 03. 資料收集/Tool Setup/Software/freefilesync.md 03. 資料收集/Tool Setup/Software/vim.md 03. 資料收集/翻牆/V2Ray.md 03. 資料收集/翻牆/Wireguard.md 03. 資料收集/軟體工具/youtube-dl.md
This commit is contained in:
4
.obsidian/daily-notes.json
vendored
4
.obsidian/daily-notes.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"folder": "01. Daily",
|
||||
"folder": "01. 個人/01. Daily",
|
||||
"format": "YYYY-MM-DD(ddd)",
|
||||
"template": "02. PARA/03. Resources(資源)/99. templates/日記"
|
||||
"template": "03. 資料收集/99. templates/日記"
|
||||
}
|
||||
4
.obsidian/plugins/periodic-notes/data.json
vendored
4
.obsidian/plugins/periodic-notes/data.json
vendored
@@ -4,8 +4,8 @@
|
||||
"hasMigratedWeeklyNoteSettings": false,
|
||||
"daily": {
|
||||
"format": "",
|
||||
"template": "02. PARA/03. Resources(資源)/99. templates/日記.md",
|
||||
"folder": "01. Daily",
|
||||
"template": "03. 資料收集/99. templates/日記.md",
|
||||
"folder": "01. 個人/01. Daily",
|
||||
"enabled": true
|
||||
},
|
||||
"weekly": {
|
||||
|
||||
2
.obsidian/templates.json
vendored
2
.obsidian/templates.json
vendored
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"folder": "02. PARA/03. Resources(資源)/99. templates",
|
||||
"folder": "03. 資料收集/99. templates",
|
||||
"dateFormat": "YYYY-MM-DD",
|
||||
"timeFormat": "HH:mm:ss"
|
||||
}
|
||||
33
.obsidian/workspace
vendored
33
.obsidian/workspace
vendored
@@ -7,12 +7,8 @@
|
||||
"id": "828beb43bb437dd1",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "03. 資料收集/RaspberryPi.md",
|
||||
"mode": "source",
|
||||
"source": true
|
||||
}
|
||||
"type": "empty",
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -69,7 +65,6 @@
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "03. 資料收集/RaspberryPi.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
@@ -85,9 +80,7 @@
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "03. 資料收集/RaspberryPi.md"
|
||||
}
|
||||
"state": {}
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -116,15 +109,15 @@
|
||||
},
|
||||
"active": "f69acad103e7f817",
|
||||
"lastOpenFiles": [
|
||||
"03. 資料收集/科技/HDR Sensor.md",
|
||||
"03. 資料收集/軟體工具/IPFS.md",
|
||||
"03. 資料收集/軟體工具/docker.md",
|
||||
"03. 資料收集/架站/HTTP Server/Nginx/Reverse Proxy(Layer4).md",
|
||||
"03. 資料收集/架站/HTTP Server/Nginx.md",
|
||||
"03. 資料收集/架站/SWAG Reverse proxy.md",
|
||||
"03. 資料收集/架站/Gitea.md",
|
||||
"03. 資料收集/架站/Trojan.md",
|
||||
"03. 資料收集/架站/Storj.md",
|
||||
"03. 資料收集/架站/03. Trojan.md"
|
||||
"03. 資料收集/翻牆/V2Ray.md",
|
||||
"02. 工作/01. Logitech/20210412 - TestCam.md",
|
||||
"02. 工作/01. Logitech/20210428 - Sega.md",
|
||||
"02. 工作/01. Logitech/20210512 - Kong.md",
|
||||
"02. 工作/01. Logitech/20210716 - AutoStation.md",
|
||||
"02. 工作/01. Logitech/20220601 - Sentinel.md",
|
||||
"02. 工作/01. Logitech/Logitech.md",
|
||||
"03. 資料收集/名言佳句.md",
|
||||
"03. 資料收集/每週外食.md",
|
||||
"00. Inbox/00. TODO.md"
|
||||
]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
* 好盈電變調整中立點:[【RC】FLYSKY 富斯NB4 遥控器基本功能使用教程_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1pJ411y7yB/)
|
||||
* [(28) 陳春期--HB D812 差速器調教教學 - YouTube](https://www.youtube.com/watch?v=dSYQBz3T6gU)
|
||||
1
03. 資料收集/Hobby/RC/好盈電變調整中立點.md
Normal file
1
03. 資料收集/Hobby/RC/好盈電變調整中立點.md
Normal file
@@ -0,0 +1 @@
|
||||
[【RC】FLYSKY 富斯NB4 遥控器基本功能使用教程_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1pJ411y7yB/)
|
||||
1
03. 資料收集/Hobby/RC/差速器調教教學.md
Normal file
1
03. 資料收集/Hobby/RC/差速器調教教學.md
Normal file
@@ -0,0 +1 @@
|
||||
[(28) 陳春期--HB D812 差速器調教教學 - YouTube](https://www.youtube.com/watch?v=dSYQBz3T6gU)
|
||||
@@ -1,734 +0,0 @@
|
||||
## 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
|
||||
```
|
||||
/* 有旁節點,沒有與其他元素相鄰 */
|
||||
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/)
|
||||
@@ -1,2 +0,0 @@
|
||||
- [GitHub - Qv2ray/Qv2ray: Linux / Windows / macOS 跨平台 V2Ray 客户端 | 支持 VMess / VLESS / SSR / Trojan / Trojan-Go / NaiveProxy / HTTP / HTTPS / SOCKS5 | 使用 C++ / Qt 开发 | 可拓展插件式设计](https://github.com/Qv2ray/Qv2ray)
|
||||
- [Project V · Project V 官方网站](https://www.v2ray.com/)
|
||||
@@ -1,111 +0,0 @@
|
||||
|
||||
## Installation
|
||||
### Install on Synology NAS
|
||||
1. 從[synology-wireguard release](https://github.com/runfalk/synology-wireguard/releases)下載對應的SPK,DS1513+是WireGuard-cedarview-1.0.20200729.spk。若不知道該下載哪一個版本,可以查看[這個對照表](https://www.synology.com/en-global/knowledgebase/DSM/tutorial/Compatibility_Peripherals/What_kind_of_CPU_does_my_NAS_have)。
|
||||
2. 在套件中心裡面手動安裝
|
||||
3. 用SSH登入
|
||||
|
||||
### Install on Ubuntu 20.04
|
||||
安裝: `sudo apt install wireguard resolvconf`
|
||||
|
||||
1. 打開firewall port
|
||||
```
|
||||
sudo ufw allow 50100/udp
|
||||
```
|
||||
2. 打開port forwarding
|
||||
`sudo vim /etc/sysctl.conf`
|
||||
然後加入這一行,存檔離開
|
||||
`net.ipv4.ip_forward=1`
|
||||
套用
|
||||
`sudo sysctl -p`
|
||||
|
||||
## Setup Wireguard
|
||||
1. Make a folder to store key and config
|
||||
```
|
||||
mkdir ~/wireguard ; cd ~/wireguard
|
||||
```
|
||||
2. 生成server的private/public key: `wg genkey | tee server_privateKey | wg pubkey > server_publicKey`
|
||||
3. 在`/etc/wireguard`裡面,建立`wg0.conf`,如下:
|
||||
```
|
||||
[Interface]
|
||||
Address = 10.0.0.1/24
|
||||
ListenPort = 50100
|
||||
PrivateKey = 8EELc7SWYbZswluhP0ZEzSkTAINXLlXqdE8J34eak3g=
|
||||
|
||||
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o enp0s3 -j MASQUERADE
|
||||
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o enp0s3 -j MASQUERADE
|
||||
|
||||
SaveConfig = true
|
||||
DNS = 8.8.8.8
|
||||
|
||||
# Awin
|
||||
[Peer]
|
||||
PublicKey = dB9l0rROSHyp3C6Odykdu69sU1k7XrOEa33ibx10I00=
|
||||
AllowedIPs = 10.0.0.2/32
|
||||
|
||||
# Dean
|
||||
[Peer]
|
||||
PublicKey = N8kOoy3x4rsM1XDekrzLVQJ7Eo9Cb/vcQ07btzEK41Q=
|
||||
AllowedIPs = 10.0.0.3/32
|
||||
```
|
||||
注意文中的`[Interface]`中的`PrivateKey`要替換成你自己生成的key,可以用`cat server_privateKey`與`cat server_publicKey`來取得。
|
||||
4. 生成user private/public key: `wg genkey | tee <user_name>_privateKey | wg pubkey > <user_name>_publicKey`
|
||||
1. 例如要給awin的key: `wg genkey | tee awin_privateKey | wg pubkey > awin_publicKey`
|
||||
5. 建立user的config,例如給awin的config:
|
||||
建立`awin.conf`,內容如下:
|
||||
```
|
||||
[Interface]
|
||||
PrivateKey = OBN3ORMdpaz7pHTSlkyCXHvgLTbXnmB2kxJTCyrr3F4=
|
||||
Address = 10.0.0.2/24
|
||||
DNS = 8.8.8.8
|
||||
|
||||
[Peer]
|
||||
PublicKey = 15Sy2MRW1yKWLzA03MciOkR7qvpxSXfmQtkMj9xOzj0=
|
||||
AllowedIPs = 0.0.0.0/0, ::0/0
|
||||
Endpoint = vpn.awin.one:50100
|
||||
```
|
||||
6. 把user config生成QR code,方便掃描:
|
||||
- `sudo grep -v '^#' /etc/wireguard/<user_name>.conf | qrencode -t ansiutf8`
|
||||
- `qrencode -t ansiutf8 < <user_name>.conf`
|
||||
- 兩個都可以
|
||||
7. 重啟Wireguard
|
||||
```
|
||||
sudo wg-quick up wg0; \
|
||||
sleep 5; \
|
||||
sudo wg-quick down wg0; \
|
||||
sleep 5; \
|
||||
sudo wg-quick up wg0
|
||||
```
|
||||
另一個:
|
||||
`sudo wg-quick down wg0 ; sudo cp ./wg0.conf /etc/wireguard/wg0.conf ; sudo wg-quick up wg0 ; sudo wg show wg0`
|
||||
8. 查看Wireguard狀態: `sudo wg`
|
||||
|
||||
## Troubleshooting
|
||||
That will tell you whether your packets are reaching the remote server, or if they're not getting through the tunnel.
|
||||
- On the remote server: `sudo tcpdump -i wg0`
|
||||
- On local machine: `ping -c1 <server_ip>`
|
||||
|
||||
## Helper
|
||||
寫了一個script來copy config,這樣就可以在Windows直接編輯。
|
||||
```
|
||||
#!/bin/env bash
|
||||
|
||||
sudo cp /volume1/homes/awin/Temp/wg0.conf .
|
||||
sudo cp /volume1/homes/awin/Temp/awin.conf .
|
||||
sudo cp /volume1/homes/awin/Temp/dean.conf .
|
||||
|
||||
sudo wg-quick down wg0
|
||||
sleep 5
|
||||
sudo wg-quick up wg0
|
||||
sleep 5
|
||||
sudo wg-quick down wg0
|
||||
sleep 5
|
||||
sudo wg-quick up wg0
|
||||
```
|
||||
|
||||
----------
|
||||
|
||||
參考資料:
|
||||
- https://github.com/runfalk/synology-wireguard
|
||||
- https://notes.wadeism.net/linux/680/
|
||||
- [『Atrandys』wireguard配置文件讲解 | 配置多用户 - YouTube](https://www.youtube.com/watch?v=X4doKJmjE4o&feature=youtu.be)
|
||||
Reference in New Issue
Block a user