今天我学会了什么 2024-1

66 阅读5分钟

2024-1-14

Python

datetime(所有平台/所有语言/所有领域)

from datetime import datetime, timedelta, timezone

datetime 可以从很多格式转换为标准的时间格式。时区是独立设置的,这样转换为时间戳就很方便。

def name_to_datetime(name: str) -> datetime:
    dt = datetime.strptime(name, "%Y-%m-%d-%H-%M-%S")
    tz = timezone(timedelta(hours=8))
    dt = dt.replace(tzinfo=tz)
    return dt

arg(所有平台/所有语言/所有领域)

import sys

Python 中的参数可以用 sys 模块获取。

if len(sys.argv) > 1:
    working_dir = Path(sys.argv[1])

PySide(所有平台/Python+CPP)

from PySide6.QtCore import Qt, QUrl, Slot
from PySide6.QtGui import QFont
from PySide6.QtMultimedia import QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget
from PySide6.QtWidgets import (
    QApplication,
    QFrame,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QPushButton,
    QSizePolicy,
    QSlider,
    QSpacerItem,
    QVBoxLayout,
    QWidget,
)

VSCode 的 Python 插件可以整理 import。

布局、控件、项目(Qt)

一般用的是控件(widget),控件用布局(layout)联系起来,自动调整大小。项目(item)还没怎么用过,但 spacer 是项目而不是控件。

布局的动态修改(Qt)

可以作为最外层的控件可以设置一个布局(setLayout)。在新建布局时,构造函数直接传入最外层控件,就不用手动调用 setLayout 设置。

布局中可以添加控件(addWidget)、布局(addLayout)、项目(addSpacerItem)。

stretch(Qt)

参见 stackoverflow.com/a/31328541。… stretch 参数均为 0(默认),则等分。否则非 0 的按比例,0 的按内容。

self.hboxLayout1.addLayout(self.vboxLayout1, stretch=1)  # 其他都是 0,vboxLayout1 把剩余空间用完。

QFrame(Qt)

QFrame 可以在内部关联布局。外面就是个框。

QFrame 设置边框颜色(CSS)

参见 www.qtcentre.org/threads/444…

self.frame.setObjectName("frame")
self.frame.setStyleSheet(f"#frame {{ border: 10px solid {color}; }}")

Qt 播放视频(Qt)

from PySide6.QtMultimedia import QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget

显示、多媒体是分开的模块。

self.videoWidget = QVideoWidget(self)
self.vboxLayout1.addWidget(self.videoWidget, 1)
self.mediaPlayer = QMediaPlayer(self)
self.mediaPlayer.setVideoOutput(self.videoWidget)
self.mediaPlayer.setSource(QUrl.fromLocalFile(self._video_file_path))

2024-1-15

Python

bisect(所有平台/Python)

from bisect import bisect

二分查找。

timedelta(所有平台/Python)

from datetime import timedelta

timedelta 表示两个 datetime 的差。

if et is None or t < et:
    self._set_frame_color("red")
elif t - et < timedelta(seconds=3):
    self._set_frame_color("green")

如果一个时间有时区,一个没有,就不能比较或者相减,转换成时间戳后再操作。

if et is None or t.timestamp() < et.timestamp():
    self._set_frame_color("red")
elif t.timestamp() - et.timestamp() < 3.0:
    self._set_frame_color("green")

PySide(所有平台/Python+CPP)

AudioOutput(Qt)

from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget

要播放声音,必须使用 QAudioOutput。必须指定父结点,不能是临时的。

self.mediaPlayer = QMediaPlayer(self)
self.mediaPlayer.setVideoOutput(self.videoWidget)
self.mediaPlayer.setAudioOutput(QAudioOutput(self))

2024-1-16

Python

PySide(所有平台/Python+CPP)

blockSignals(Qt)

使用 blockSignals 方法,可以阻止发出信号。可以用在强制 set value 而不触发信号的场景。

pre = self.slider.blockSignals(True)
self.slider.setValue(position)
self.slider.blockSignals(pre)

2024-1-17

Kotlin

Bitmap, Canvas, Matrix(Android/Kotlin+Java)

Android 中,Bitmap 创建后需要用 Canvas 进行绘图,与 Windows 类似。利用 Matrix 可以进行仿射变换。

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Matrix
directBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
rotatedBitmap = Bitmap.createBitmap(height, width, Bitmap.Config.ARGB_8888)
val canvas = Canvas(rb)
val matrix = Matrix().apply {
    postRotate(270f)
    postTranslate(0f, rb.height.toFloat())
canvas.drawBitmap(db, matrix, null)

Bitmap 可以从 Buffer 中拷贝图像,也可以把图像拷贝到 Buffer 中。

val byteBuffer = ByteBuffer.wrap(data)
byteBuffer.int // Skip the first 4 bytes.
db.copyPixelsFromBuffer(byteBuffer)
rb.copyPixelsToBuffer(ByteBuffer.wrap(frd))

2024-1-18

Python

Matplotlib(所有平台/Python)

Matplotlib 可以使用 Qt 作为后端。如果项目中已经存在 Qt,要先导入 Qt,再导入 Matplotlib。

from PySide6.QtCore import QPoint, QRect, Qt, QUrl, Slot
from PySide6.QtGui import QFont, QPainter, QPen
from PySide6.QtMultimedia import QAudioOutput, QMediaPlayer
from PySide6.QtMultimediaWidgets import QVideoWidget
from PySide6.QtWidgets import (
    QApplication,
    QFrame,
    QHBoxLayout,
    QLabel,
    QMainWindow,
    QPushButton,
    QSizePolicy,
    QSlider,
    QSpacerItem,
    QVBoxLayout,
    QWidget,
)

if True:
    from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg
    from matplotlib.figure import Figure

使用 Qt 作为后端时,Matplotlib 有一个显示控件,名为 FigureCanvasQTAgg,它是一个 Matplotlib 的 Canvas,也是一个 Qt 的 QWidget。初始化时,需要将它要绘制的 Figure 作为参数传入。FigureCanvas 的关系基本上类似于昨天写的 BitmapCanvas 的关系。

self.figure = Figure()
self.ax = self.figure.add_subplot(111)
self.canvas = FigureCanvasQTAgg(self.figure)
layout.addWidget(self.canvas)

如果修改 Figure 后没有窗口大小改变这样的事件提醒 FigureCanvasQTAgg 重绘,则需要手动调用 Canvasdraw 方法。

self.ax.clear()
self.ax.plot(self._x[left_idx:right_idx], self._y[left_idx:right_idx])
self.canvas.draw()

2024-1-19

Kotlin

Context(Android/Kotlin+Java)

参见 www.jianshu.com/p/fb0681f5f…

Context 提供了关于环境全局信息的接口,Application、Activity、Service 均是 Context。

ContextapplicationContext 属性,可以获取当前进程的全局单例 Application Context。如果某个东西的生命周期与进程相关,就用它。

不同的 Context 能力不同,例如 Application 就不允许弹窗。

Swift

简介(Apple/Swift)

import 语句导入。用 var 关键字定义变量。注释类似于 C++,但多行注释可嵌套。

import Cocoa
var myString = "Hello, World!"
print(myString)

let 是常量,等效于 Rust 的 let,Kotlin 的 val,Go 的 const

var 是变量,等效于 Rust 的 let mut,Kotlin 的 var,Go 的 var

声明语法和 Rust、Kotlin 一样。

可选类型的原理是枚举,和 Rust 一样(NoneSome(T))。写法和 Kotlin 一样,空值的名字和 Go 一样(nil)。

var myString: String? = nil

断言有值时,和 Kotlin 一样,但是只使用一个 !

和 Rust 一样,可以作模式匹配。

if let constantName = someOptional {
    statements
}

和 C++ 一样,用 Exp1 ? Exp2 : Exp3 表示三目运算符。

范围循环和 Rust 一样,但是省略号多一个点。

import Cocoa

for index in 1...5 {
    print("\(index) 乘于 5 为:\(index * 5)")
}

Swift 的内存模型应该类似于 Rust,因为还有 Arc 啥的。

和 Go 一样,用 func 定义函数。

和大家都不一样,类里面,在 func 前加 mutating 表示可以修改成员。类方法隐含 self 这个名字。

2024-1-20

硬件

I2C

I2C 的地址位的最低位表示读(1)写(0)。当别人明确告诉你一个地址是 7-bit 时,你需要左移一位使用,库的 readwrite 方法可能会自动帮你修改读写位。

参见 www.i2c-bus.org/repeated-st… 的时序中有一个 start 条件和 stop 条件。在 stop 前,允许有多个 start,使得多个批次的操作不会被打断。这在多个主机的系统中有用。Mbed Studio 中的 I2C 类的 repeat 参数可以实现不发送 stop 条件。