vault backup: 2022-09-28 19:13:41
Affected files: .obsidian/workspace 03. Programming/COM/20210726 - COM Interface.md 03. Programming/DB/MySQL.md 03. Programming/DB/sqlite.md 03. Programming/Design Pattern.md 03. Programming/FFMPEG/00. Introduction.md 03. Programming/FFMPEG/01. Setup.md 03. Programming/FFMPEG/FFMpeg.md 03. Programming/Flask.md 03. Programming/Media Foundation/20210604 - Windows media foundation.md 03. Programming/OpenCV.md 03. Programming/OpenGL.md 03. Programming/Python/argparse.ArgumentParser.md 03. Programming/Python/decorator.md 03. Programming/Python/logging.md 03. Programming/Python/opencv.md 03. Programming/Python/subprocess.md 03. Programming/Python/threading.md 03. Programming/Python/tkinter.md 03. Programming/Python/檢測工具.md 03. Programming/QT/Dropdown button.md 03. Programming/QT/QVariant.md 03. Programming/QT/Qt.md 03. Programming/UML.md 03. Programming/演算法.md 04. 資料收集/99. templates/blogHeader.md 04. 資料收集/99. templates/date.md 04. 資料收集/99. templates/front matter.md 04. 資料收集/99. templates/note.md 04. 資料收集/99. templates/table.md 04. 資料收集/99. templates/thisWeek.md 04. 資料收集/99. templates/日記.md 04. 資料收集/99. templates/讀書筆記.md 04. 資料收集/Linux/CLI/cut.md 04. 資料收集/Linux/CLI/scp.md 04. 資料收集/Linux/CLI/timedatectl.md 04. 資料收集/Linux/Programming.md 04. 資料收集/Linux/Ubuntu.md 04. 資料收集/Tool Setup/Hardware/RaspberryPi.md 04. 資料收集/Tool Setup/Software/Chrome.md 04. 資料收集/Tool Setup/Software/Obisidian.md 04. 資料收集/Tool Setup/Software/SublimeText.md 04. 資料收集/Tool Setup/Software/VirtualBox.md 04. 資料收集/Tool Setup/Software/Visual Studio Code.md 04. 資料收集/Tool Setup/Software/Windows Setup.md 04. 資料收集/Tool Setup/Software/Windows Terminal.md 04. 資料收集/Tool Setup/Software/freefilesync.md 04. 資料收集/Tool Setup/Software/vim.md 04. 資料收集/名言佳句.md 04. 資料收集/架站/Gitea.md 04. 資料收集/架站/HTTP Server/Apache.md 04. 資料收集/架站/HTTP Server/Nginx/Reverse Proxy(Layer4).md 04. 資料收集/架站/Pelican blog.md 04. 資料收集/架站/Proxmox VE.md 04. 資料收集/架站/SWAG Reverse proxy.md 04. 資料收集/架站/Storj.md 04. 資料收集/架站/Trojan.md 04. 資料收集/每週外食.md 04. 資料收集/科技/802.11.md 04. 資料收集/科技/HDR Sensor.md 04. 資料收集/科技/量子電腦.md 04. 資料收集/科技/鋰電池.md 04. 資料收集/興趣嗜好/RC/Traxxas Sledge.md 04. 資料收集/興趣嗜好/RC/好盈電變調整中立點.md 04. 資料收集/興趣嗜好/RC/差速器調教教學.md 04. 資料收集/興趣嗜好/模型/舊化作例.md 04. 資料收集/興趣嗜好/軍武/虎式.md 04. 資料收集/讀書筆記/20201201 - 學習如何學習.md 04. 資料收集/讀書筆記/20201218 - Kotlin權威2.0.md 04. 資料收集/讀書筆記/20201224 - 寫作是最好的自我投資.md 04. 資料收集/讀書筆記/20210119 - 中產悲歌.md 04. 資料收集/讀書筆記/20210220 - 最高學習法.md 04. 資料收集/讀書筆記/20210320 - 最高學以致用法.md 04. 資料收集/讀書筆記/20210406 - 精準購買.md 04. 資料收集/讀書筆記/20210723 - 高手學習.md 04. 資料收集/讀書筆記/20220526 - 深入淺出設計模式.md 04. 資料收集/讀書筆記/20220619 - 精確的力量.md 04. 資料收集/軟體工具/IPFS.md 04. 資料收集/軟體工具/MkDocs.md 04. 資料收集/軟體工具/Obsidian.md 04. 資料收集/軟體工具/docker.md 04. 資料收集/軟體工具/git/apply.md 04. 資料收集/軟體工具/git/submodule.md 04. 資料收集/軟體工具/youtube-dl.md 04. 資料收集/面試準備/技术面试最后反问面试官的话.md
This commit is contained in:
56
03. Programming/Python/argparse.ArgumentParser.md
Normal file
56
03. Programming/Python/argparse.ArgumentParser.md
Normal file
@@ -0,0 +1,56 @@
|
||||
一個範例:
|
||||
```python
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-u", "--update_tool", default=WIN_FW_UPDATER_PATH, help="The path of win_fw_updater.exe.")
|
||||
parser.add_argument("-f", "--firmware", required=True, help="The path of ITB file.")
|
||||
parser.add_argument("-w", "--waittime_update_firmware", default=600, type=int, help="Wait time for update the firmware.")
|
||||
parser.add_argument("-g", "--ignore_check_file_path", action='store_true', help="Skip check the existence of file.")
|
||||
args = parser.parse_args()
|
||||
```
|
||||
|
||||
#### 要求user一定要設定的參數
|
||||
使用`required=True`
|
||||
例如:
|
||||
```python
|
||||
parser.add_argument("-f", "--firmware", required=True, help="The path of ITB file.")
|
||||
```
|
||||
如果使用者沒有下`-f`(或者`--firmware=XXX`)就會報錯,如下:
|
||||
```bash
|
||||
FwUpdateCheck.py: error: the following arguments are required: -f/--firmware
|
||||
```
|
||||
|
||||
|
||||
#### 有設定才會產生的參數
|
||||
使用`action='store_true'`或`action='store_false'`
|
||||
例如:
|
||||
```python
|
||||
parser.add_argument("-g", "--ignore_check_file_path", action='store_true', help="Skip check the existence of file.")
|
||||
```
|
||||
當使用者沒有設置`-g`時,`args.ignore_check_file_path`為`False`,當設置時,`args.ignore_check_file_path`為`True`。
|
||||
|
||||
#### 使用預設值
|
||||
例如:
|
||||
```python
|
||||
parser.add_argument("-u", "--update_tool", default="C:\\tool.exe", help="The path of win_fw_updater.exe.")
|
||||
```
|
||||
用`default=<Something>`來設定參數的預設值,上面的例子中,`args.update_tool`的預設值為`C:\tool.exe`。
|
||||
另外可以用`type=<Object type>`來指定預設值的型別。例如:
|
||||
```python
|
||||
parser.add_argument("-n", "--number", default=50, type=int, help="Assign a number")
|
||||
```
|
||||
上例中,`args.number`的預設值是50,型別是`int`,所以可以直接運算,不需要再經過`int(args.number)`這樣的轉換。
|
||||
|
||||
#### 限制使用者的選擇
|
||||
Example:
|
||||
```python
|
||||
parser.add_argument('move', choices=['rock', 'paper', 'scissors'])
|
||||
```
|
||||
使用`choices=<list>`來限定輸入的選項,上例中,使用者只能輸入'rock'、'paper'、'scissors'這三個字串中的其中一個,否則會報錯:
|
||||
```bash
|
||||
error: argument move: invalid choice: 'fire' (choose from 'rock', 'paper', 'scissors')
|
||||
```
|
||||
|
||||
-----
|
||||
- https://docs.python.org/zh-tw/3/library/argparse.html
|
||||
48
03. Programming/Python/decorator.md
Normal file
48
03. Programming/Python/decorator.md
Normal file
@@ -0,0 +1,48 @@
|
||||
## 在decorator內取得function的default argument與class member
|
||||
```python
|
||||
import sys
|
||||
import inspect
|
||||
from functools import wraps
|
||||
|
||||
|
||||
def exampleDecorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
print(f"Decorator: call by {func.__name__}")
|
||||
def get_default_args(func):
|
||||
signature = inspect.signature(func)
|
||||
return {
|
||||
k: v.default for k, v in signature.parameters.items() if v.default is not inspect.Parameter.empty
|
||||
}
|
||||
|
||||
## Get default
|
||||
defaultKwargs = get_default_args(func)
|
||||
defaultKwargs.update(kwargs)
|
||||
print(f"Decorator: args = {args}, kwargs = {kwargs}, defaultKwargs = {defaultKwargs}")
|
||||
|
||||
objectInstance = args[0]
|
||||
if hasattr(objectInstance, 'defaultArg1'):
|
||||
print(f'objectInstance has defaultArg1, a.defaultArg1({type(objectInstance.defaultArg1)}) = {objectInstance.defaultArg1}')
|
||||
if objectInstance.defaultArg1:
|
||||
## Do something here
|
||||
print("Decorator: some message...")
|
||||
else:
|
||||
print('objectInstance does not have defaultArg1')
|
||||
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
|
||||
class ExampleClass():
|
||||
def __init__(self, defaultArg1=True, defaultArg2="SomeString"):
|
||||
self.defaultArg1 = defaultArg1
|
||||
self.defaultArg2 = defaultArg2
|
||||
print(f'self.defaultArg1 = {self.defaultArg1}, self.defaultArg2 = {self.defaultArg2}')
|
||||
|
||||
@exampleDecorator
|
||||
def run(self, arg1=1, arg2=2):
|
||||
print(f"ExampleClass.run(), arg1 = {arg1}, arg2 = {arg2}")
|
||||
|
||||
example = ExampleClass()
|
||||
example.run()
|
||||
```
|
||||
126
03. Programming/Python/logging.md
Normal file
126
03. Programming/Python/logging.md
Normal file
@@ -0,0 +1,126 @@
|
||||
### 準備
|
||||
```
|
||||
import logging
|
||||
```
|
||||
|
||||
### logging level
|
||||
| level | level number | funtion |
|
||||
|:---------|:-------------|:---------------------|
|
||||
| NOTSET | 0 | |
|
||||
| DEBUG | 10 | `logging.debug()` |
|
||||
| INFO | 20 | `logging.info()` |
|
||||
| WARNING | 30 | `logging.warning()` |
|
||||
| ERROR | 40 | `logging.error()` |
|
||||
| CRITICAL | 50 | `logging.critical()` |
|
||||
|
||||
```
|
||||
import logging
|
||||
|
||||
LOG_FORMAT = '%(asctime)s %(levelname)s: %(message)s'
|
||||
LOG_FILENAME = 'C:\\RobotRun\\Output\\RobotRunDocUpdater.log'
|
||||
|
||||
logging.basicConfig(level=logging.INFO, filename=LOG_FILENAME, filemode='a', format=LOG_FORMAT)
|
||||
|
||||
logging.info('logging start')
|
||||
```
|
||||
|
||||
### Print Exception
|
||||
`logging` 模組也提供可以紀錄完整的堆疊追蹤 (stack traces),若在 `logging.error()` 加上 `exc_info` 參數,並將該參數設為 `True`,就可以紀錄 Exception,如下:
|
||||
```python
|
||||
import logging
|
||||
|
||||
try:
|
||||
x = 5 / 0
|
||||
except:
|
||||
logging.error("Catch an exception.", exc_info=True)
|
||||
```
|
||||
也可以使用`logging.exception("Catch an exception.")`,效果跟`logging.error("Catch an exception.", exc_info=True)`一樣。
|
||||
|
||||
### 自訂 logging 輸出格式
|
||||
預設的訊息輸出格式只有 `levelname`、`name`、`message`,下面是其他相關的資訊:
|
||||
|
||||
| 格式化字串 | 說明 |
|
||||
|:------------------|:---------------------------------------------------------------------|
|
||||
| `%(asctime)s` | 日期時間, 格式為 `YYYY-MM-DD HH:mm:SS,ms`,例如:2018-12-13 17:20:30,567 |
|
||||
| `%(filename)s` | 模組檔名 |
|
||||
| `%(funcName)s` | 函數名稱 |
|
||||
| `%(levelname)s` | 日誌的等級名稱 |
|
||||
| `%(levelno)s` | 日誌的等級數值 |
|
||||
| `%(lineno)d` | 呼叫日誌函數所在的行數 |
|
||||
| `%(message)s` | 訊息 |
|
||||
| `%(module)s` | 模組名稱 |
|
||||
| `%(name)s` | logger 的名稱 |
|
||||
| `%(pathname)s` | 檔案的完整路徑 (如果可用) |
|
||||
| `%(process)d` | process ID (如果可用) |
|
||||
| `%(thread)d` | 執行緒 ID (如果可用) |
|
||||
| `%(threradName)s` | 執行緒名稱 |
|
||||
|
||||
例:
|
||||
```python
|
||||
FORMAT = '%(asctime)s %(levelname)s: %(message)s'
|
||||
logging.basicConfig(level=logging.DEBUG, format=FORMAT)
|
||||
logging.debug('debug message') --> 2018-12-13 17:40:34,604 DEBUG: debug message
|
||||
```
|
||||
|
||||
### 儲存log
|
||||
只要在 `logging.basicConfig()` 內的 `filename` 參數設定要儲存的日誌檔名,就可以將 logging 儲存:
|
||||
```python
|
||||
import logging
|
||||
|
||||
FORMAT = '%(asctime)s %(levelname)s: %(message)s'
|
||||
logging.basicConfig(level=logging.DEBUG, filename='myLog.log', filemode='w', format=FORMAT)
|
||||
logging.debug('debug message')
|
||||
```
|
||||
預設 `filemode` 參數是設為 `a`,代表 append (附加) 的意思,每次執行程式時,Logging 會將新的訊息加在舊的訊息後面,不會覆蓋舊的訊息。若要改成新訊息覆蓋就訊息,那可以將 `filemode` 參數設為 `w`,代表 write 的意思。
|
||||
|
||||
### 儲存log也輸出到console
|
||||
`logging`有4個主要module:
|
||||
- Logger:暴露了應用程式程式碼能直接使用的介面。
|
||||
- Handler:將(記錄器產生的)日誌記錄傳送至合適的目的地。
|
||||
- Filter:提供了更好的粒度控制,它可以決定輸出哪些日誌記錄。
|
||||
- Formatter:指明瞭最終輸出中日誌記錄的佈局。
|
||||
|
||||
#### Handler
|
||||
其中`Handlers`有以下幾類:
|
||||
1. `logging.StreamHandler` -> 控制檯輸出
|
||||
使用這個Handler可以向類似與`sys.stdout`或者`sys.stderr`的任何檔案物件(file object)輸出資訊。
|
||||
它的建構函式是: `StreamHandler([strm])` 其中`strm`引數是一個檔案物件。預設是`sys.stderr`。
|
||||
2. `logging.FileHandler` -> 檔案輸出
|
||||
和StreamHandler類似,用於向一個檔案輸出日誌資訊。不過`FileHandler`會幫你開啟這個檔案。
|
||||
它的建構函式是:`FileHandler(filename[,mode])` filename是檔名,必須指定一個檔名。 `mode`是檔案的開啟方式。預設是`'a'`,即新增到檔案末端。
|
||||
3. `logging.handlers.RotatingFileHandler` -> 按照大小自動分割日誌檔案,一旦達到指定的大小重新生成檔案
|
||||
這個Handler類似於上面的`FileHandler`,但是它可以管理檔案大小。當檔案達到一定大小之後,它會自動將當前日誌檔案改名,然後建立一個新的同名日誌檔案繼續輸出。比如日誌檔案是chat.log。當chat.log達到指定的大小之後,`RotatingFileHandler`自動把 檔案改名為chat.log.1。不過,如果chat.log.1已經存在,會先把chat.log.1重新命名為chat.log.2。
|
||||
最後重新建立 chat.log,繼續輸出日誌資訊。它的建構函式是:`RotatingFileHandler(filename[, mode[, maxBytes[, backupCount]]])`,其中`filename`和`mode`兩個引數和FileHandler一樣。`maxBytes`用於指定日誌檔案的最大檔案大小。如果maxBytes為0,意味著日誌檔案可以無限大,這時上面描述的重新命名過程就不會發生。 `backupCount`用於指定保留的備份檔案的個數。比如,如果指定為2,當上面描述的重新命名過程發生時,原有的chat.log.2並不會被更名,而是被刪除。
|
||||
4. `logging.handlers.TimedRotatingFileHandler` -> 按照時間自動分割日誌檔案
|
||||
這個Handler和`RotatingFileHandler`類似,不過,它沒有通過判斷檔案大小來決定何時重新建立日誌檔案,而是間隔一定時間就自動建立新的日誌檔案。重新命名的過程與`RotatingFileHandler`類似,不過新的檔案不是附加數字,而是當前時間。它的建構函式是:`TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])`,其中`filename`引數和`backupCount`引數和`RotatingFileHandler`具有相同的意義。`interval`是時間間隔。 `when`引數是一個字串。表示時間間隔的單位,不區分大小寫。它有以下取值: S 秒 M 分 H 小時 D 天 W 每星期(`interval==0`時代表星期一) midnight 每天凌晨。
|
||||
|
||||
#### Formatters
|
||||
Formatters預設的時間格式為`%Y-%m-%d %H:%M:%S`
|
||||
|
||||
#### Example
|
||||
新增2個handler,一個輸出到螢幕上,一個寫到檔案裡。寫到檔案裡的那個handler必須是`logging.handlers.RotatingFileHandler`,超過1MB時會自動分割。
|
||||
```python
|
||||
import logging
|
||||
import logging.handlers
|
||||
|
||||
logger = logging.getLogger(filename) # filename就是你要存log的檔名
|
||||
|
||||
shell_print = logging.StreamHandler() # 往螢幕上輸出
|
||||
shell_print.setFormatter(format_str) # 設定螢幕上顯示的格式
|
||||
|
||||
file_print = logging.handlers.RotatingFileHandler(
|
||||
filename=filename,
|
||||
mode='a',
|
||||
maxBytes=1024*1024,
|
||||
backupCount=backCount,
|
||||
encoding='utf-8')
|
||||
file_print.setFormatter(format_str) # 設定檔案裡寫入的格式
|
||||
|
||||
logger.addHandler(sh) # 把物件加到logger裡
|
||||
logger.addHandler(th)
|
||||
```
|
||||
|
||||
-----
|
||||
參考:
|
||||
- [Python - 日誌 (logging) 模組](https://titangene.github.io/article/python-logging.html)
|
||||
- [`logging` — Logging facility for Python](https://docs.python.org/3/library/logging.html#module-logging "logging: Flexible event logging system for applications.")
|
||||
61
03. Programming/Python/opencv.md
Normal file
61
03. Programming/Python/opencv.md
Normal file
@@ -0,0 +1,61 @@
|
||||
### 將camera包裝成class
|
||||
```python
|
||||
class CameraCv(object):
|
||||
def __init__(self, videoSource=0):
|
||||
self.videoSource = videoSource
|
||||
self.camera = None
|
||||
self.cameraWidth = 0
|
||||
self.cameraHeight = 0
|
||||
self.cameraPreviewThreadHandle = None
|
||||
self.cameraPreviewThreadStopEvent = threading.Event()
|
||||
self.lastframeRGB = None
|
||||
self.latestFrame = None
|
||||
|
||||
def start(self):
|
||||
print("Open Camera")
|
||||
self.camera = cv2.VideoCapture(self.videoSource, cv2.CAP_DSHOW)
|
||||
if not self.camera.isOpened():
|
||||
raise ValueError("Unable to open video source {}".format(self.videoSource))
|
||||
|
||||
# Get video source width and height
|
||||
self.cameraWidth = self.camera.get(cv2.CAP_PROP_FRAME_WIDTH)
|
||||
self.cameraHeight = self.camera.get(cv2.CAP_PROP_FRAME_HEIGHT)
|
||||
|
||||
self.cameraPreviewThreadStopEvent.clear()
|
||||
self.cameraPreviewThreadHandle = threading.Thread(target=self.collectFrame, daemon=True, args=())
|
||||
self.cameraPreviewThreadHandle.start()
|
||||
|
||||
def stop(self):
|
||||
print("Close Camera")
|
||||
self.cameraPreviewThreadStopEvent.set()
|
||||
if self.camera.isOpened():
|
||||
self.camera.release()
|
||||
cv2.destroyAllWindows()
|
||||
|
||||
def collectFrame(self):
|
||||
while True:
|
||||
ret, frame = self.camera.read()
|
||||
if ret:
|
||||
# Return a boolean success flag and the current frame converted to BGR
|
||||
self.lastframeRGB = frame
|
||||
self.latestFrame = ImageTk.PhotoImage(image=Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)))
|
||||
|
||||
if self.cameraPreviewThreadStopEvent.is_set():
|
||||
break
|
||||
|
||||
time.sleep(0.016)
|
||||
|
||||
def draw(self, container):
|
||||
if self.latestFrame is not None:
|
||||
container.imgtk = self.latestFrame
|
||||
container.configure(image=self.latestFrame)
|
||||
|
||||
def read(self):
|
||||
return self.camera.read()
|
||||
|
||||
def getLastFrameRgb(self):
|
||||
return self.lastframeRGB
|
||||
|
||||
def saveFrame(self, filepath):
|
||||
cv2.imwrite(filepath, self.getLastFrameRgb())
|
||||
```
|
||||
49
03. Programming/Python/subprocess.md
Normal file
49
03. Programming/Python/subprocess.md
Normal file
@@ -0,0 +1,49 @@
|
||||
### subprocess.Popen
|
||||
```python
|
||||
import subprocess
|
||||
|
||||
process = subprocess.Popen(['echo', 'More output'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = process.communicate()
|
||||
stdout, stderr
|
||||
```
|
||||
|
||||
Input arguments is a list.
|
||||
Notice `communicate()` will **block** until process was finished.
|
||||
And the output string `stdout` and `stderr` is of type `byte`. You can convert the output to `string` by:
|
||||
```python
|
||||
new_string = stdout.decode('utf-8')
|
||||
```
|
||||
or use `universal_newlines=True` in `subprocess.Popen()`. Example:
|
||||
```python
|
||||
process = subprocess.Popen(['ping', '-c 4', 'python.org'],
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True)
|
||||
```
|
||||
The `.poll()` will return the exit code of process. If process is still running. `.poll()` will return `None`. Example:
|
||||
```python
|
||||
process = subprocess.Popen(['ping', '-c 4', 'python.org'], stdout=subprocess.PIPE, universal_newlines=True)
|
||||
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
print(output.strip())
|
||||
# Do something else
|
||||
return_code = process.poll()
|
||||
if return_code is not None:
|
||||
print('RETURN CODE', return_code)
|
||||
# Process has finished, read rest of the output
|
||||
for output in process.stdout.readlines():
|
||||
print(output.strip())
|
||||
break
|
||||
```
|
||||
|
||||
-----
|
||||
參考:
|
||||
- [docs.python.org: `subprocess.Popen`](https://docs.python.org/3/library/subprocess.html#subprocess.Popen)
|
||||
|
||||
### subprocess.run
|
||||
`subprocess.run()`跟`subprocess.Popen()`是一樣的行為,差別是`subprocess.run()`會在process執行完畢之後才return,也就是說流程會被block住。
|
||||
`subprocess.run()`會回傳一個型別是`subprocess.CompletedProcess`的object.
|
||||
|
||||
-----
|
||||
參考:
|
||||
- [docs.python.org: _class_ `subprocess.CompletedProcess`](https://docs.python.org/3/library/subprocess.html#subprocess.CompletedProcess)
|
||||
2
03. Programming/Python/threading.md
Normal file
2
03. Programming/Python/threading.md
Normal file
@@ -0,0 +1,2 @@
|
||||
- [Python 多執行緒 threading 模組平行化程式設計教學 - G. T. Wang](https://blog.gtwang.org/programming/python-threading-multithreaded-programming-tutorial/)
|
||||
- [Python — 多線程. 介紹 | by Jease | Jease隨筆 | Medium](https://medium.com/jeasee%E9%9A%A8%E7%AD%86/python-%E5%A4%9A%E7%B7%9A%E7%A8%8B-eb36272e604b)
|
||||
96
03. Programming/Python/tkinter.md
Normal file
96
03. Programming/Python/tkinter.md
Normal file
@@ -0,0 +1,96 @@
|
||||
### 把matplotlib包裝成獨立視窗
|
||||
```python
|
||||
class Plot2D(Frame):
|
||||
def __init__(self, parent, dataCollector, **kwargs):
|
||||
Frame.__init__(self, parent.mainWindow, **kwargs)
|
||||
|
||||
self.parent = parent
|
||||
self.mainWindows = Toplevel(parent.mainWindow)
|
||||
self.mainWindows.title("AF State")
|
||||
self.figure = plt.Figure(figsize=(9,5), dpi=100)
|
||||
self.figure.suptitle('AF value plot', fontsize=16)
|
||||
self.ax = self.figure.add_subplot(111)
|
||||
self.canvas = FigureCanvasTkAgg(self.figure, master=self.mainWindows)
|
||||
self.canvas.get_tk_widget().pack(fill='both')
|
||||
self.axline = None
|
||||
|
||||
self.dataCollector = dataCollector
|
||||
self.dataCollector.start()
|
||||
|
||||
def close(self):
|
||||
print("Plot2D close")
|
||||
self.mainWindows.destroy()
|
||||
self.dataCollector.stop()
|
||||
self.dataCollector = None
|
||||
|
||||
def draw(self):
|
||||
if self.dataCollector:
|
||||
datax, datay = self.dataCollector.getPlotData()
|
||||
self.ax.clear()
|
||||
self.ax.set_xlabel('Last {} datas'.format(self.dataCollector.getDataLength()))
|
||||
self.axline, = self.ax.plot(datax, datay)
|
||||
self.canvas.draw()
|
||||
|
||||
def getWindow(self):
|
||||
return self.mainWindows
|
||||
|
||||
def getLastData(self):
|
||||
return self.dataCollector.getLastData()
|
||||
```
|
||||
|
||||
其中這一行:
|
||||
```python
|
||||
self.mainWindows = Toplevel(parent.mainWindow)
|
||||
```
|
||||
是用來開一個新的視窗,其中的`parent.mainWindow`就是用`tk.TK()`所產生出來的root。
|
||||
|
||||
因為需要一直更新資料,所以需要的一個`DataCollector`來提供資料,`DataCollector`會提供畫圖需要的list,如:
|
||||
```python
|
||||
datax, datay = self.dataCollector.getPlotData()
|
||||
```
|
||||
|
||||
`DataCollector`的定義如下:
|
||||
```python
|
||||
class AfStateCollector(threading.Thread):
|
||||
def __init__(self, dataLength=100, pollingInterval=0.033):
|
||||
threading.Thread.__init__(self)
|
||||
self.dataLength = dataLength
|
||||
self.pollingInterval = pollingInterval
|
||||
self.stopEvent = threading.Event()
|
||||
self.data = []
|
||||
self.xdata = []
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
if self.stopEvent.is_set():
|
||||
break
|
||||
|
||||
afValue = self.readAf()
|
||||
self.data.append(afValue)
|
||||
self.xdata.append(len(self.xdata))
|
||||
if len(self.data) > self.dataLength:
|
||||
self.data = self.data[-self.dataLength:]
|
||||
self.xdata = list(range(self.dataLength))
|
||||
|
||||
# print(f'afValue = {afValue}')
|
||||
time.sleep(self.pollingInterval)
|
||||
|
||||
print("AfStateCollector stopped.")
|
||||
|
||||
def readAf(self):
|
||||
ReadTestXUreg_cmd = "lvreg testxu read 10"
|
||||
ReadTestXUreg_cmd_process = subprocess.Popen(ReadTestXUreg_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
outstring, err = ReadTestXUreg_cmd_process.communicate()
|
||||
outstring = outstring.strip().decode('utf-8')
|
||||
outstring = int(outstring, 16)
|
||||
outstring_H = (outstring & 0xFF00) / 256
|
||||
outstring_L = outstring & 0xFF
|
||||
outAFStat = int(outstring_L * 256 + outstring_H)
|
||||
|
||||
return outAFStat
|
||||
```
|
||||
|
||||
- [Python GUI之tkinter視窗視窗教程大集合(看這篇就夠了) - IT閱讀](https://www.itread01.com/content/1547705544.html)
|
||||
- [【Python】改善 VideoCapture 的影像延遲 | 夏恩的程式筆記 - 點部落](https://dotblogs.com.tw/shaynling/2017/12/28/091936)
|
||||
- [Displaying a video feed with OpenCV and Tkinter - PyImageSearch](https://www.pyimagesearch.com/2016/05/30/displaying-a-video-feed-with-opencv-and-tkinter/)
|
||||
96
03. Programming/Python/檢測工具.md
Normal file
96
03. Programming/Python/檢測工具.md
Normal file
@@ -0,0 +1,96 @@
|
||||
## 單元測試
|
||||
### [pytest](https://docs.pytest.org/en/7.1.x/)
|
||||
Pytest 不僅可以幫助我們運行測試,還可以幫助我們配置如何運行它們、運行哪些文件等等……Pytest 有一個配置文件 `pytest.ini`,您可以在其中描述它的配置,例如哪個版本應該是 Pytest 或者哪些是測試文件,例如下列。
|
||||
```ini
|
||||
# pytet.ini
|
||||
[pytest]
|
||||
minversion = 6.0
|
||||
addopts = -ra -q — cov=src — cov-report=html
|
||||
python_files = test_*.py
|
||||
```
|
||||
|
||||
### [tox](https://tox.wiki/en/latest/)
|
||||
Tox 是一個通用的virtualenv管理和測試命令行工具。
|
||||
使用不同的 Python 版本和解釋器檢查您的包是否正確安裝
|
||||
在每個環境中運行您的測試,配置您選擇的測試工具
|
||||
作為持續集成服務器的前端,大大減少樣板文件並合併 CI 和基於 shell 的測試。
|
||||
Tox 也有它的配置文件。
|
||||
```ini
|
||||
[tox]
|
||||
isolated_build = True
|
||||
envlist = py{38}
|
||||
|
||||
[testenv]
|
||||
usedevelop = true
|
||||
deps = -r src/requirements_dev.txt
|
||||
```
|
||||
|
||||
|
||||
## 程式檢查工具
|
||||
用來檢查程式是否符合coding style、PEP8之類的規範
|
||||
### [pylint](https://github.com/PyCQA/pylint)
|
||||
Pylint config: create `.pylintrc` file
|
||||
```
|
||||
[MESSAGES CONTROL]
|
||||
disable=
|
||||
missing-docstring,
|
||||
too-few-public-methods[REPORTS]
|
||||
output-format=colorized
|
||||
files-output=no
|
||||
reports=no
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
```
|
||||
|
||||
### [flake8](https://github.com/pycqa/flake8)
|
||||
Flake8 config: create `.flake8` file
|
||||
```
|
||||
[flake8]
|
||||
ignore = E203, E266, E501, W503, F403, F401, E402
|
||||
max-line-length = 120
|
||||
max-complexity = 18
|
||||
select = B,C,E,F,W,T4,B9
|
||||
exclude =
|
||||
.git,
|
||||
tests
|
||||
```
|
||||
|
||||
### [mypy](http://www.mypy-lang.org/)
|
||||
|
||||
## Git hook
|
||||
### pre-commit
|
||||
Pre-commit 是一個創建 git hook的framework,以確保您的代碼編寫與您定義的代碼樣式相對應。
|
||||
它會掃描您的原始碼並運行您將在預提交配置文件中定義的所有檢查器:`.pre-commit-config.yaml`
|
||||
```
|
||||
repos:
|
||||
- repo: 'https://gitlab.com/pycqa/flake8'
|
||||
rev: 3.8.2
|
||||
hooks:
|
||||
- id: flake8
|
||||
name: Style Guide Enforcement (flake8)
|
||||
args:
|
||||
- '--max-line-length=120'
|
||||
- repo: 'https://github.com/pre-commit/mirrors-mypy'
|
||||
rev: v0.720
|
||||
hooks:
|
||||
- id: mypy
|
||||
name: Optional Static Typing for Python (mypy)
|
||||
```
|
||||
|
||||
## 漏洞檢查
|
||||
### [SonarQube](https://www.sonarqube.org/)
|
||||
有很多用於漏洞掃描的工具,但我們將看看[Sonarqube](https://www.sonarqube.org/)。Sonarqube 是用於代碼質量和安全掃描的開源強大工具,是該行業的領先工具之一。
|
||||
更多在[官方文檔](https://docs.sonarqube.org/latest/)中。
|
||||
您可以使用 Docker 映像設置本地 Sonarqube 服務器並定義`sonar-project.properties`
|
||||
```
|
||||
# must be unique in a given SonarQube instance
|
||||
sonar.projectKey=python_app_blueprint
|
||||
# --- optional properties ---
|
||||
# defaults to project key
|
||||
#sonar.projectName=My project
|
||||
# defaults to 'not provided'
|
||||
#sonar.projectVersion=1.0
|
||||
# Path is relative to the sonar-project.properties file. Defaults to .
|
||||
#sonar.sources=.
|
||||
# Encoding of the source code. Default is default system encoding
|
||||
#sonar.sourceEncoding=UTF-8
|
||||
```
|
||||
Reference in New Issue
Block a user