Jack Huang's Blog


  • 首页

  • 标签

  • 归档

  • 搜索

Excel宏编程简易教程

发表于 2019-07-16 | 更新于 2019-07-17

期末了,媳妇计算学生平时成绩好辛苦,于是准备编写Excel宏程序提高媳妇工作的效率,节省时间。于是简单记录Excel宏编程的相关知识。

基本概念

为了编写Excel宏,首先需要了解一些基本概念:

工作簿

工作簿相关概念有:Workbooks、Workbook、ActiveWorkbook、ThisWorkbook。

  • Workbooks集合包含excel中所有当前打开的excel工作簿,亦即所有打开的excel文件;

  • Workbook对应Workbooks中的成员,即其中的excel文件;

  • ActiveWorkbook代表当前处于活动状态的工作簿,即当前显示的excel文件;

  • ThisWorkbook代表其中有Visual Basic代码正在运行的工作簿。

在具体使用中可用Workbooks(index)来引用Workbook对象,其中index为工作簿名称或编号,如Workbooks(1)、 Workbooks(“年度报表.xls”)。而编号按照创建或打开工作簿的顺序来确定,第一个打开的工作簿编号为1,第二个打开的工作簿为2……。

工作表

工作表相关概念有:Worksheets、Worksheet、ActiveSheet。

  • Worksheets集合包含工作簿中所有的工作表,即一个excel文件中的所有数据表页;

  • Worksheet则代表其中的一个工作表;

  • ActiveSheet代表当前处于的活动状态工作表,即当前显示的一个工作表。

图表

图表相关概念有:Chart 、Charts、ChartObject、ChartObjects、ActiveChart。

  • Chart代表工作簿中的图表。该图表既可为嵌入式图表(包含在ChartObject中),也可为一个分开的(单独的)图表工作表。

  • Charts代表指定工作簿或活动工作簿中所有图表工作表的集合,但不包括嵌入式在工作表或对话框编辑表中的图表。使用Charts(index) 可引用单个Chart图表,其中index是该图表工作表的索引号或名称;如Charts(1)、Charts(“销售图表”)。

  • ChartObject代表工作表中的嵌入式图表,其作用是作为Chart对象的容器。利用ChartObject可以控制工作表上嵌入式图表的外观和尺寸。

  • ChartObjects代表指定的图表工作表、对话框编辑表或工作表上所有嵌入式图表的集合。

单元格

单元格相关概念有:Cells、ActiveCell、Range、Areas。

  • Cells(row,column)代表单个单元格,其中row为行号,column为列号。如可以用Cells(1,1)、Cells(10,4)来引用”A1”、”D10” 单元格。

  • ActiveCell代表活动工作表的活动单元格,或指定工作表的活动单元格。

  • Range代表工作表中的某一单元格、某一行、某一列、某一选定区域(该选定区域可包含一个或若干连续单元格区域)或者某一三维区域。可用Range(arg)来引用单元格或单元格区域,其中arg可为单元格号、单元格号范围、单元格区域名称。如Range(“A5”)、 Range(“A1:H8”)、Range(“Criteria”)。虽然可用Range(“A1”)返回单元格A1,但用Cells更方便,因为此时可 用变量指定行和列。

  • Areas 为选定区域内的连续单元格块的集合,其成员是Range对象。

行与列

行与列相关概念有:Rows、Columns、Row、Column。

Rows、Columns分别代表活动工作表、单元格区域范围Range、指定工作表中的所有行数、列数。

基本语法

Visual Basic for Applications(VBA)是一种Visual Basic的一种宏语言,主要能用来扩展Windows的应用程序功能,特别是Microsoft Office软件。也可说是一种应用程序视觉化的Basic Sc​​ript。 1994年发行的Excel 5.0版本中,即具备了VBA的宏功能。Excel宏编程主要使用VBA。

数据类型

基本数据类型

即Primary Type Data,下述列表的括号内为字节数:

  • Byte (1):无符号类型,取值范围0-255
  • Boolean (2)
  • Integer (2)
  • Long (4)
  • Single (4)
  • Double (8)
  • Currency (8)
  • Decimal (14)
  • Date (8)
  • String
  • Object (4)
  • Variant (根据分配确定)

自定义的数据类型

相当于C语言的struct,例如:

1
2
3
4
5
Type 自定义类型名
元素名 As 类型
…
[元素名 As 类型]
End Type

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Option Base 0 '數組索引值從0開始
Option Base 1 '數組索引值從1開始
Dim MyArray(10) '聲明一個數組變量,10是最大的可用的數組索引值
MyArray(5) = 101 '給數組的元素賦值
Dim Data(10,5) '聲明一個二維數組變量
Data(1,1) = "A001" '給數組元素賦值
Dim cArr(-11 To 20, 1 To 3) As String '聲明一個數組,定義數組索引值的上下界
Dim dArr() As String '聲明動態數組
ReDim dArr(0 To 5, 1 To 2) '改變動態數組的尺寸默認把原數據清除。如果保留原來的數據,必須加上參數Preserve。
'使用Preserve參數時只能改變最後一維的大小
If UBound(vTemp) = -1 Then
'判斷數組變量vTemp是否為 空數組
End If
Erase MyArrar, Data 'Eras​​e語句清除數組元素,釋放變量佔用的空間

常量

日期常量由符号“#”将字符括起来,如#2012-1-1#。

系统定义常量有3个:True、False和Null。

固有常量是编程时引用的对象库定义的常量。所有固有常量都可以在宏或VBA代码中使用。通常,固有常量通过前两个字母来指明定义该常量。来自VB库的常量则以“vb”开头。来自Access的常量以“ac”开头。可以使用对象浏览器来查看所有对象库中的固有常量列表。

可以自行定义常量。如:

1
Global Const 符号常量名称 = 常量值

控制结构

if 语句

1
2
3
4
5
6
7
8
9
if 條件1 then
語句1
elseif 條件2 then
語句2
elseif ...
...
else
語句n
end if

Select Case 语句

1
2
3
4
5
6
7
8
9
Select Case 表達式
Case 表達式列表1
語句1
Case 表達式列表2
語句2
...
Case 表達式列表n
語句n
End Select

Do…Loop 语句

1
2
3
4
5
Do While或Until 條件
語句塊1
Exit Do
語句塊2
Loop
1
2
3
4
5
Do
語句塊1
Exit Do
語句塊2
Loop While或Until 條件

For…Next语句

1
2
3
For 循環控制變量=初值To 終值Step 步長
語句塊 ‘Exit For語句可以跳出循環體
Next

For Each … Next语句

1
2
3
4
For Each 循環控制變量 In 集合變量
語句塊
Exit For語句可以跳出循環體
Next 循環控制變量

跳出本次循环的continue语句

VBA没有类似C语言的continue语句。通常可如此写程序:

1
2
3
4
5
6
7
For 循環控制變量=初值 To 終值 Step 步長
Do '用于模拟continue
語句塊
If 条件 Then Exit Do '用于模拟continue
語句塊
Loop While False '用于模拟continue
Next

With语句

1
2
3
With 對象引用
語句塊
End With

On Error语句

1
On Error Goto 出錯處理語句的label '跳轉到出錯處理語句

或者

1
On Error Resume Next '遇到錯誤,不管錯誤,繼續往下執行

过程与函数

1
2
3
4
5
Sub 過程名(參數表)
語句塊
Exit Sub
語句塊
End Sub
1
2
3
4
5
Function 函數名(參數表) As Type
語句塊
函數名=表達式
Exit Function
End Function

常用内置函数

VBA的常用内置函数列表:

  • MsgBox
  • InputBox
  • 舍入函数:Fix 向0取整,Int向下取整, Round四舍五入
  • Rnd 返回0-1内的单精度随机数
  • 字符串函数:
    • Filter:对字符串的一维数组的过滤
    • InStr([Start, ],[, Compare])与InStrRev: 查找子串
    • Len 字符串长度
    • Join:连接一维数组中的所有子字符串
    • Left,Right,Mid 截取子字符串
    • Space(数值) 生成空格字符串
    • Ucase,Lcase 大小写转换函数
    • Ltrim, Rtrim,Trim 删除首尾空格
    • Replace
    • Split:分割一个字符串成为一维数组
    • StrComp:字符串比较
    • StrConv:字符串转换
    • String(number, character):制定字符重复若干次
    • StrReverse
  • 日期/时间有关函数:
    • Year, Month, Day, WeekDay,Hour,Minute,Second 截取日期时间分量
    • DateAdd 日期/时间增量函数
    • DateDiff(<间隔类型>,<日期1>,<日期2>[,W1][,W2])日期/时间的距离函数
    • DatePart(<分割类型>,<日期>[,w1][,w2])时间分割函数
    • DateSerial(<表达式1>,<表达式2>,<表达式3>) 合成日期;DateValue(“字符串表达式”)返回日期;
    • Date,Time,Now,Timer 返回日期时间
    • DateValue
    • TimeSerial:由时间序列得到时间对象
    • TimeValue:由时间字符串得到时间对象
    • Weekday:获得日期的周几
    • WeekdayName
  • 转换函数:CBool、CByte、CCur、 CDate、 CDbl、CDec、CInt、 CLng、CLngLng、CLngPtr、CSng、CStr、CVar、CVErr、Asc(<字符串表达式>)返回第一个字符的Ascii编码值、Chr(ASCII码)返回字符、Hex、Oct、Str(<数值表达式>)返回字符串、Val(string)、Format、FormatCurrency、FormatDateTime、FormatNumber、FormatPercent、MonthName
  • Nz(表达式或字段属性值[,规定值])如果是空,则返回0或者””或者函数的第二个参数值
  • 验证函数:isNumeric、isDate、isNull、isEmpty IsArray、IsError、IsMissing、IsObject
  • 数学函数:Abs、Sqr、Tan、Atn(即atan)、Sin、Cos、Exp(e为基的指数)、Log自然对数
  • Array:构造一个Array对象
  • CallByName: get or set a property, or invoke a method at run time using a string name.
  • 控制流:Choose:类似于C语言的select语句、IIf相当于IF-ELSE语句、Switch
  • Command:获取命令行参数
  • CreateObject:创建ActiveX对象
  • CurDir:返回指定驱动器的当前工作路径
  • 由基本数学函数导出的函数:Sec、Cosec、Cotangent、Cotan、Arcsin、Arccos、Arcsec、Arccosec、Arccotan、HSin、HCos、HTan、HSec、HCosec、HCotan、HArcsin、HArccos、HArctan、HArcsec、HArccosec、HArccotan、LogN
  • DoEvents:暂时把CPU控制权交回给系统。
  • Environ:返回环境变量的值
  • 文件操作:
    • Dir:返回满足条件的所有文件、目录的名字
    • EOF
    • FileAttr
    • FileDateTime
    • FileLen
    • FreeFile Function
    • GetAttr:返回文件、目录的属性值
    • Input:读取文件
    • Loc:文件指针位置
    • LOF:文件打开时的指针位置
    • Seek:文件指针定位
    • Spc:使用Print做position output
    • Tab:用于Print函数
    • Error:错误号对应的错误消息
    • Windows Registry中的数据:GetAllSettings、SaveSetting、DeleteSetting、GetSetting
    • GetObject:ActiveX组建的引用
    • IMEStatus:返回当前Input Method Editor (IME)。
    • Macintosh平台:MacID、MacScript
  • 金融函数:
    • DDB:使用double-declining balance计算贬值
    • FV:计算固定利率的年金
    • IPmt:计算利率
    • IRR:计算利率
    • MIRR:计算利率
    • NPer:计算周期数
    • NPV:计算net present value
    • Pmt:计算支付数
    • PPmt:计算本金支付数
    • PV:计算present value
    • Rate:利息率
    • SLN:straight-line depreciation
    • SYD:计算sum-of-years’ digits depreciation
  • Partition:返回字符串,表示一个数值名字落在各个range内。常用于SQL select语句
  • QBColor:颜色值
  • RGB:颜色值
  • TypeName:得到变量的类型名
  • VarType:得到变量的类型数

表达式

比较特殊的运算符有指数运算^,浮点除法/,整数除法\,取模运算Mod,不等逻辑比较运算<>

简单示例

在Excel开发工具中点击录制宏,生成一个VBA过程,创建如下代码计算学生平均成绩:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Sub 计算平均成绩()
'
' 计算平均成绩 宏
' 计算平均成绩
'
' 快捷键: Ctrl+l
'
Application.Goto Reference:="计算平均成绩"

Dim count As Integer ' 统计学生总数
count = Range("A1").CurrentRegion.Rows.count ' 统计学生总数

ActiveSheet.Cells(1, "E").Value = "平均成绩"
Dim workSocre As Double ' 课程分
Dim videoScore As Double ' 视频分
Dim discussScore As Double ' 讨论分

For i = 2 To count
' 计算学生平均成绩
workSocre = ActiveSheet.Cells(i, "A").Value
videoScore = ActiveSheet.Cells(i, "B").Value
discussScore = Discuss(ActiveSheet.Cells(i, "C").Value)
ActiveSheet.Cells(i, "E").Value = workSocre * 0.6 + videoScore * 100 * 0.2 + discussScore * 0.2
Next
MsgBox ("平均成绩计算完成!")
End Sub


Function Discuss(discussCount As Integer) As Integer
Dim score As Integer
Select Case discussCount
Case Is >= 20
score = 100
Case Is >= 15
score = 80
Case Is >= 10
score = 60
Case Is >= 5
score = 40
Case Is > 0
score = 20
Case Is = 0
score = 0
End Select
Discuss = score
End Function

参考链接

  1. Excel宏教程 (宏的介绍与基本使用),by 远洪.
  2. Visual Basic for Applications,by wikipedia.
  3. VBA——Range操作,by 风之工程师.

小波变换学习笔记

发表于 2019-07-14 | 更新于 2019-10-04

小波分析(英语:wavelet analysis)或小波变换(英语:wavelet transform)是指用有限长或快速衰减的“母小波”(mother wavelet)的振荡波形来表示信号。该波形被缩放和平移以匹配输入的信号。

小波变化的发展,承袭Gabor transform的局部化思想,并且克服了傅里叶和Gabor transform的部分缺陷,小波变换提供了一个可以调变的时频窗口,窗口的宽度(width)随着频率变化,频率增高时,时间窗口的宽度就会变窄,以提高分辨率.小波在整个时间范围内的振幅平均值为0,具有有限的持续时间和突变的频率与震幅,可以是不规则,或不对称的信号。

小波变换分成两个大类:离散小波变换(DWT) 和连续小波变换(CWT)。两者的主要区别在于,连续变换在所有可能的缩放和平移上操作,而离散变换采用所有缩放和平移值的特定子集。

参考链接

  1. 能不能通俗的讲解下傅立叶分析和小波分析之间的关系?,by zhihu.
  2. 什么是线性平稳信号和非线性非平稳信号?,by zhihu.
  3. 形象易懂讲解算法I——小波变换,by 咚懂咚懂咚.
  4. 从傅里叶变换进阶到小波变换(一),by 1335.
  5. 信号频域分析方法的理解(频谱、能量谱、功率谱、倒频谱、小波分析),by Mr.括号.
  6. 小波分析,by wikipedia.
  7. 傅里叶变换交互式入门,by Jez Swanson.

信号与线性系统学习笔记

发表于 2019-07-05 | 更新于 2019-07-08

信号与线性系统是学习自动控制理论的前置专业基础课,可惜当年就学得似懂非懂,过了这么多年,知识早就忘了,现在捡起来温习一下,算是温故而知新吧。

信号与系统基础

连续的或离散的动态系统,按其基本特性可分为线性的与非线性的;时变的与时不变的;因果的与非因果的;稳定与非稳定的。

连续系统的时域分析

对单输入与单输出的线性时不变连续系统,通常适用n阶常系数线性微分方程表示:

$$y^{(n)}(t)+a_{n-1}y^{(n-1)}(t)+…+a_1y^{(1)}(t)+a_0y(t) \
=b_mf^{(m)}(t)+b_{m-1}f^{(m-1)}(t)+…+b_1f^{(1)}(t)+b_0f(t) \tag{1}$$

或缩写为

$$\sum_{i=0}^{n}a_iy^{(i)}(t)=\sum_{j=0}^{m}b_jy^{(j)}(t)$$

其中$f(t)$为激励,$y(t)$为响应,$a_i(i=0,1,…,n)$和$b_j(j=0,1,…,n)$均为常数,$a_n=1$。

该微分方程的全解由齐次解$y_h(t)$和特解$y_p(t)$组成,即

$$y(t)=y_h(t)+y_p(t)$$

参考链接

  1. 1. 信号与线性系统—究竟在学啥?,by Dean Rossi.
  2. 2. 信号与线性系统——信号的分解,by Dean Rossi.
  3. 3. 信号与线性系统——啥?卷积?,by Dean Rossi.
  4. 4. 信号与线性系统——你好,傅里叶,by Dean Rossi.
  5. 5. 信号与线性系统——F.T&L.T,by Dean Rossi.
  6. 7. 信号与线性系统——系统的分析方法,by Dean Rossi.

傅里叶级数和变换学习笔记

发表于 2019-07-01 | 更新于 2019-10-04

在数学中,傅里叶级数(Fourier series)能将任何周期函数或周期信号分解成一个(可能由无穷个元素组成的)简单振荡函数的集合,即正弦函数和余弦函数(或者,等价地使用复指数)。傅里叶变换(Fourier transform)是一种线性积分变换,用于信号在时域(或空域)和频域之间的变换,在物理学和工程学中有许多应用。傅里叶变换就像化学分析,确定物质的基本成分;信号来自自然界,也可对其进行分析,确定其基本成分。

傅里叶级数公式

傅里叶级数的公式:

$$
f(t) =\frac{a_{0}}{2}+a_{1}cos(\omega t)+b_{1}sin(\omega t) \
+a_{2}cos(2\omega t)+b_{2}sin(2\omega t) +…\
=\frac{a_{0}}{2}+\sum_{n=1}^{\infty}{[a_{n}cos(n\omega t)+b_{n}sin(n\omega t)]} \tag{1}
$$

其中:
$$
a_{n}=\frac{2}{T}\int_{t_{0}}^{t_{0}+T}f(t)cos(n\omega t)dt \tag{2}
$$
$$
b_{n}=\frac{2}{T}\int_{t_{0}}^{t_{0}+T}f(t)sin(n\omega t)dt \tag{3}
$$

傅里叶级数的收敛性

若傅里叶级数不收敛于$f(t)$,则不能在两者之间画等号。关于傅里叶级数的收敛性,最常用的为狄利克雷条件:

对于一个周期为$2\pi$的函数$f(x)$,如果它满足:

(1)在一个周期内连续或只有有限个第一类间断点;

(2)在一个周期内只有有限个极值点。

那么$f(x)$的傅里叶级数收敛于$\frac{f(x+0)+f(x-0)}{2}$。

狄利克雷条件只是傅里叶级数收敛的充分条件,而非必要条件,级数收敛不代表该条件成立。

下面给出一个周期函数的傅里叶级数的计算示例。

周期为$2\pi$的函数$f(x)$,在$(-\pi,\pi)$上$f(x)=x$,求$f(x)$的傅里叶级数。

$$a_n=\frac{1}{\pi}\int_{-\pi}^{\pi}x\mathrm{cos}nx\mathrm{d}x=0$$

$$b_n=\frac{1}{\pi}\int_{-\pi}^{\pi}x\mathrm{sin}nx\mathrm{d}x=(-1)^{n+1}\frac{2}{n}$$

狄利克雷条件显然成立,所以

$$f(x)=\sum^{\infty}_{n=1}(-1)^{n+1}\frac{2}{n}\mathrm{sin}nx$$

傅里叶级数的指数形式

令$i$表示虚数单位,傅里叶级数的指数形式为:

$$f(t)=\sum^{\infty}_{n=-\infty}c_ne^{in\omega t}$$

其中,

$$c_n=\frac{1}{T}\int^{T}_{0}f(t)e^{-in\omega t}\mathrm{d}t$$

指数形式与三角形式是相等的,推导如下:

$$\begin{aligned}
&\quad\sum^{\infty}{n=-\infty}c_ne^{in\omega t}\
&=c_0+\sum^{\infty}
{n=1}(c_ne^{in\omega t}+c_{-n}e^{-in\omega t}) \
&=c_0+\sum^{\infty}{n=1}[(c_n+c{-n})\mathrm{cos}n\omega t+i(c_n-c_{-n})\mathrm{sin}n\omega t] \
&=\frac{a_0}{2}+\sum_{n=1}^{\infty}(a_n \mathrm{cos}n \omega t+b_n \mathrm{sin}n \omega t)
\end{aligned}
$$

傅里叶变换

傅里叶变换是傅里叶级数在非周期函数上的推广。对非周期函数$f(x)$,其周期$T\rightarrow\infty$。因为$\omega_0=\frac{2\pi}{T}$,则$\omega_0\rightarrow0$。

观察傅里叶级数的指数形式

$$f(t)=\sum^{\infty}_{n=-\infty}c_ne^{in\omega t} \tag{1}$$

其中,

$$c_n=\frac{1}{T}\int^{T}_{0}f(t)e^{-in\omega t}\mathrm{d}t \tag{2}$$

当 $\omega_0\rightarrow0$ 时, $n\omega_0$ 从原本的离散变化变成了连续变化, $c_n$ 也就可以表示为关于 $n\omega_0$ 的函数 $F(n\omega_0)$ 。

傅里叶级数中公式(2)的积分的上下限不一定是$0$到$T$,只需要$f(t)$的一个周期就可以了。即对于任意的$x_0$, 公式(2)可表示为:

$$c_n=\frac{1}{T}\int^{x_0+T}_{x_0}f(t)e^{-in\omega_0 t}\mathrm{d}t \tag{3}$$

这个积分需要积一整个周期,而此时的周期为无穷大,也就是整个定义域上都需要积,所以要从$-\infty$积分到$\infty$。

只需要让上式中的 $T\rightarrow\infty$ , $\omega_0\rightarrow0$ ,便可以得到 $F(n\omega_0)$ 的表达式。不妨令 $\omega=n\omega_0$ ,就得到了

$$F(\omega)=\frac{1}{T}\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t \tag{4}$$

由于$\frac{1}{T}\rightarrow0$,我们先丢弃$\frac{1}{T}$,之后用到$F(\omega)$在乘回来,于是令:

$$F(\omega)=\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t \tag{5}$$

将公式(5)代入公式(1),并代入$\frac{1}{T}=\frac{\omega_0}{2\pi}$,则有:

$$f(t)=\frac{1}{2\pi}\sum^{\infty}_{n=-\infty}F(n\omega_0)e^{in\omega_0 t}\omega_0 \tag{6}$$

因为 $\omega=n\omega_0$ ,每次 $\omega$ 的增量 $d\omega$ 都是由于 $n$ 变为 $n+1$ 造成的,所以

$$\mathrm{d}\omega=(n+1)\omega_0-n\omega_0=\omega_0$$

同时 $n\omega_0$ 连续变化,原本的离散意义下的求和就该变为连续意义下的积分,于是公式(6)变形为:

$$f(t)=\frac{1}{2\pi}\int_{-\infty}^{\infty}F(\omega)e^{i\omega t}\mathrm{d}\omega \tag{7}$$

至此得到傅里叶变换的两个公式:

$$F(\omega)=ℱ[f(t)]=\int^{\infty}_{-\infty}f(t)e^{-i\omega t}\mathrm{d}t$$

$$f(t)=ℱ^{-1}[F(\omega)]=\frac{1}{2\pi}\int_{-\infty}^{\infty}F(\omega)e^{i\omega t}\mathrm{d}\omega$$

傅里叶变换条件

由于傅里叶变换是从傅里叶级数推导得来的,所以还是狄利克雷条件,不过此时还要加上第三条, $f(t)$ 在一个周期内绝对可积。

这一个条件在 $f(t)$ 为周期函数时,可以由前两个条件推出来,因为周期和函数值均为有限值,所以在一个周期内一定绝对可积。但是推广到傅里叶变换后,这个推导就不成立了,需要单独判定第三个条件。

参考链接

  1. 傅里叶分析之掐死教程,by Heinrich.
  2. 傅里叶级数和傅里叶变换,by 木不shi丁.
  3. 傅里叶级数和傅里叶变换是什么关系?,by 马同学.
  4. 傅里叶级数与傅里叶变换推导,by 令狐哦打.
  5. 傅里叶级数,by wikipedia.
  6. 傅里叶变换,by wikipedia.
  7. 傅里叶系列(一)傅里叶级数的推导,by ElPsyCongree.
  8. 傅里叶变换(一) 傅里叶级数,by misaka.
  9. 傅里叶变换交互式入门,by Jez Swanson.

使用python3+PyQt5+PyCharm桌面GUI开发

发表于 2019-06-29 | 更新于 2021-03-07

下面简单介绍基于Python3、PyQt5和PyCharm进行桌面GUI开发。

环境配置

安装python3

PyQt5所支持的python版本是从3.5开始的,因此安装的Python3版本必须大于3.5。

安装PyQt5

1
2
pip3 install PyQt5
pip3 install PyQt5-tools

安装PyCharm

安装完PyCharm后,需要配置Qt Designer和PyUIC,前者用于设计UI,后者用于将UI转成Py文件。

具体步骤请参考程序员之路:python3+PyQt5+pycharm桌面GUI开发。

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'clearWaterPrintGui.ui'
#
# Created by: PyQt5 UI code generator 5.11.3
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(400, 300)
self.label_DirPath = QtWidgets.QLabel(Form)
self.label_DirPath.setGeometry(QtCore.QRect(16, 52, 72, 15))
self.label_DirPath.setObjectName("label_DirPath")
self.label_ExtName = QtWidgets.QLabel(Form)
self.label_ExtName.setGeometry(QtCore.QRect(31, 112, 72, 15))
self.label_ExtName.setObjectName("label_ExtName")
self.buttonBox = QtWidgets.QDialogButtonBox(Form)
self.buttonBox.setGeometry(QtCore.QRect(190, 260, 193, 28))
self.buttonBox.setInputMethodHints(QtCore.Qt.ImhNone)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.lineEdit_DirPath = QtWidgets.QLineEdit(Form)
self.lineEdit_DirPath.setGeometry(QtCore.QRect(100, 50, 281, 21))
self.lineEdit_DirPath.setObjectName("lineEdit_DirPath")
self.lineEdit_ExtName = QtWidgets.QLineEdit(Form)
self.lineEdit_ExtName.setGeometry(QtCore.QRect(100, 112, 281, 21))
self.lineEdit_ExtName.setObjectName("lineEdit_ExtName")

self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)

def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label_DirPath.setText(_translate("Form", "文件路径:"))
self.label_ExtName.setText(_translate("Form", "扩展名:"))

# 下面代码是手动添加的
if __name__=="__main__":
import sys
from PyQt5.QtGui import QIcon
app=QtWidgets.QApplication(sys.argv)
widget=QtWidgets.QWidget()
ui=Ui_Form()
ui.setupUi(widget)
# widget.setWindowIcon(QIcon('web.png'))#增加icon图标,如果没有图片可以没有这句
widget.show()
sys.exit(app.exec_())

参考链接

  1. 程序员之路:python3+PyQt5+pycharm桌面GUI开发,by 莫水千流.
  2. 使用PyQt快速开发GUI应用,by Prayer.
  3. PyQt,by wikipedia.
  4. Qt Widgets、QML、Qt Quick的区别,by 云水.
  5. Q_OBJECT宏的作用,by 沈子恒.

Python程序打包成exe

发表于 2019-06-29

大部分人是普通人,不知道如何运行Python脚本程序,因此有必要将Python脚本程序打包成可执行文件,免去安装Python环境,提高Python脚本程序的可用性。

打包方法

Python脚本程序的发布有三种方法:

  • .py文件:对于开源项目或者源码没那么重要的,直接提供源码,需要使用者自行安装Python并且安装依赖的各种库。
  • .pyc文件:有些公司或个人因为机密或者各种原因,不愿意源码被运行者看到,可以使用pyc文件发布,pyc文件是Python解释器可以识别的二进制码,故发布后也是跨平台的,需要使用者安装相应版本的Python和依赖库。
  • 可执行文件:对于非码农用户,最简单的方式就是提供一个可执行文件,只需要把用法告诉他即可。比较麻烦的是需要针对不同平台需要打包不同的可执行文件(Windows, Linux, Mac,…)。

下面介绍将Python脚本程序打包成可执行程序的各种工具。

表1 各种打包工具的对比
Solution Windows Linux OS X Python 3 One file mode Zipfile import Eggs pkg_resources support
bbFreeze yes yes yes no no yes yes yes
py2exe yes no no yes yes yes no no
pyInstaller yes yes yes yes yes no yes no
cx_Freeze yes yes yes yes no yes yes no
py2app no no yes yes no yes yes yes

从上述对Python脚本程序打包工具对比可知,推荐使用pyInstaller。

PyInstaller打包示例

安装PyInstaller

1
pip install pyinstaller

打包Python脚本

打包Python脚本成单独的 .exe 文件。

1
pyinstaller -F yourprogram.py

参考链接

  1. 如何将 Python 程序打包成 .exe 文件?,by 刘哈哈.
  2. Python程序打包成exe可执行文件,by 知行流浪.

Python命令行参数解析示例

发表于 2019-06-29

最新需要一个小程序实现解析命令行参数、遍历指定文件夹,处理指定扩展名的文件。于是简单用python实现一下。

库选择

参数解析

sys.argv

解析Python中命令行参数的最传统的方法是通过sys.argv。但这种方法比较古老,灵活性很差,同时解析出来的参数都是str类型。但在编写简单脚本,参数较少且固定时比较方便。

getopt模块

getopt模块是专门处理命令行参数的模块,用于获取命令行选项和参数,也就是sys.argv。命令行选项使得程序的参数更加灵活。支持短选项模式(-)和长选项模式(–)。

optparse模块

optparse,功能强大,易于使用,可以方便地生成标准的、符合Unix/Posix 规范的命令行说明。但在Python2.7后就已经弃用不再维护。

argparse模块

argparse模块是Python内置的参数解析模块,使用起来比较简单且功能强大。

ArgumentParser类创建时的参数如下:

  • prog - 程序的名字(默认:sys.argv[0])
  • usage - 描述程序用法的字符串(默认:从解析器的参数生成)
  • description - 参数帮助信息之前的文本(默认:空)
  • epilog - 参数帮助信息之后的文本(默认:空)
  • parents - ArgumentParser 对象的一个列表,这些对象的参数应该包括进去
  • formatter_class - 定制化帮助信息的类
  • prefix_chars - 可选参数的前缀字符集(默认:‘-‘)
  • fromfile_prefix_chars - 额外的参数应该读取的文件的前缀字符集(默认:None)
  • argument_default - 参数的全局默认值(默认:None)
  • conflict_handler - 解决冲突的可选参数的策略(通常没有必要)
  • add_help - 给解析器添加-h/–help 选项(默认:True)

add_argument函数的参数如下:

  • name or flags - 选项字符串的名字或者列表,例如foo 或者-f, –foo。
  • action - 在命令行遇到该参数时采取的基本动作类型。
  • nargs - 应该读取的命令行参数数目。
  • const - 某些action和nargs选项要求的常数值。
  • default - 如果命令行中没有出现该参数时的默认值。
  • type - 命令行参数应该被转换成的类型。
  • choices - 参数可允许的值的一个容器。
  • required - 该命令行选项是否可以省略(只针对可选参数)。
  • help - 参数的简短描述。
  • metavar - 参数在帮助信息中的名字。
  • dest - 给parse_args()返回的对象要添加的属性名称。

参数解析模块比较

  • getopt,只能简单的处理命令行参数,无法解析一个参数多个值的情况,如 –file file1 file2 file3。
  • optparse,功能强大,易于使用,可以方便地生成标准的、符合Unix/Posix 规范的命令行说明。但在Python2.7后就已经弃用不再维护。
  • argparse,使其更加容易的编写用户友好的命令行接口。它所需的程序进程了参数定义,argparse将更好的解析sys.argv。同时argparse模块还能自动生成帮助及用户输入错误参数时的提示信息。

文件夹遍历

文件夹遍历有两种方法:

  • 使用os.walk
1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*- 
import os
def Test1(rootDir):
list_dirs = os.walk(rootDir)
for root, dirs, files in list_dirs:
for d in dirs:
print os.path.join(root, d)
for f in files:
print os.path.join(root, f)
  • 使用os.listdir
1
2
3
4
5
6
7
8
# -*- coding: utf-8 -*- 
import os
def Test2(rootDir):
for lists in os.listdir(rootDir):
path = os.path.join(rootDir, lists)
print path
if os.path.isdir(path):
Test2(path)

文件处理

Python内置了读写文件的函数,用法和C是兼容的。本节介绍内容大致有:文件的打开/关闭、文件对象、文件的读写等。

代码示例

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# -*- coding: utf-8 -*- 

import os
import argparse

# 遍历文件夹
def traverse(pathName,extName):
extNameList=extName.split(' ')
print(extNameList)
list_dirs = os.walk(pathName)
for root, dirs, files in list_dirs:
for f in files:
targetExt=os.path.splitext(f)[-1]
if targetExt.lower() in extNameList:
clearWaterPrint(os.path.join(root, f))

def clearWaterPrint(fileName):
print(fileName)
fileOrgin=open(fileName,'rb')
fileTarget=open(fileName+'.exe','wb')
fileTarget.write(fileOrgin.read())
fileOrgin.close()
os.remove(fileName)
fileTarget.close()
os.rename(fileName+'.exe',fileName)

# Driver Code
if __name__ == '__main__':
# 首先创建一个ArgumentParser对象
parser = argparse.ArgumentParser(description='Process the water print!')
# 添加--path设置文件目录
parser.add_argument('-p','--path', type = str,dest='pathName', help='give the path of directory',
default = './')
# 添加--ext设置文件目录
parser.add_argument('-e','--ext', type = str,dest='extName', help='give the extension name of file',
default = '.doc .docx .wav .txt .xml .dot .html .jpg .png',nargs = '*')
#返回一个命名空间,如果想要使用变量,可用args.attr
args = parser.parse_args()

# Calling traverse() function
traverse(args.pathName,args.extName)

使用方法如下:

1
2
python3 clearWaterPrint.py -h
python3 clearWaterPrint.py -p /home/test -e .doc .ppt

参考链接

  1. Python的命令行参数解析,by Tyan.
  2. Python中最好用的命令行参数解析工具,by Mingle Wong.
  3. Python遍历文件夹的两种方法比较,by likecao.
  4. 读写字节数据,by python3-cookbook.
  5. 读写二进制文件,by funhacks.

C++标准演化简介

发表于 2019-06-26

最近阅读采用C++编写的MAVROS源码,遇到很多C++语言的新特性,理解起来很费劲,因此,特地分析一下C++标准演变过程,学习其进化过程中引入的新特性,提高C++源码阅读效率。

C++标准演变

2017年12月05日,ISO C++ 委员会正式发布了 C++ 17 标准,官方名称为 ISO/IEC 14882:2017。之前发布的C++标准有C++14、C++11、C++03、C++98。

C++98

C++98是第一个C++标准。它分为两个部分:核心语言和C++标准程序库;后者包含了大部分标准模板库和C标准程序库的稍加修改版本。存在许多不属于标准部分的C++程序库,且使用外部链接,程序库甚至可以用C撰写。

C++标准程序库充分吸收了C标准程序库,并佐以少许的修改,使其与C++良好的运作。另一个大型的程序库部分,是以标准模板库(STL)为基础,STL于1994年2月正式成为ANSI/ISO C++。它提供了实用的工具,如容器类(如:Array和Vector),迭代器(广义指针)提供容器以类似数组的访问方式,以及泛型算法进行搜索和排序的运算。此外还提供了(multi)map和(multi)set,它们都共享相似的成员函数。因此,以下成为可能,使用模板撰写泛型算法,它可以和任何容器或在任何以迭代器定义的序列上运作。如同C,使用#include指令包含标准表头,即可访问程序库里的功能。C++提供69个标准表头,其中19个不再赞成使用。

使用标准模板库(例如:使用std::vector或std::string来取代C风格的数组或字符数组)有助于导向更安全和更灵活的软件。

在STL在纳入C++标准以前,是来自HP和后来的SGI的第三方程式库,标准中并未称之为“STL”,它只是标准库中的一部分,但仍有许多人使用这个名称,以别于其它的标准库(输入/输出流、国际化、诊断、C程序库子集,等等)。 另外,如std::basic_string此类标准委员会添加的接口,有时也被误认为STL;实际上它们并不存在于原始的SGI STL中,在标准化后SGI STL才从标准库吸收加入其中。

C++03

C++03 是 C++ 语言国际标准的一个版本,正式名称是 ISO/IEC 14882:2003。该标准由国际标准化组织(ISO)和国际电工委员会(IEC)共同制定。

C++03 取代了 C++ 标准的前一个版本 C++98,后被 C++11 所取代。C++03 主要是在前一个版本的基础上针对实现方的一些问题进行了修复,从而在各个实现间达到一致、保持了可移植性。该版本共涉及 92 项核心语言缺陷报告、125 项库缺陷报告,所提供的新特性只有一项:值初始化(英语:value initialization)。

C++03 的第 69 号库缺陷报告非常值得一提,为了解决该问题,标准中加入了“std::vector 中的元素必须连续存储”的要求。

C++11

C++11,先前被称作C++0x,即ISO/IEC 14882:2011,是C++编程语言的一个标准。它取代第二版标准ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公开于1998年,第二版于2003年更新,分别通称C++98以及C++03,两者差异很小),且已被C++14取代。相比于C++03,C++11标准包含核心语言的新机能,而且扩展C++标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数除外)。 ISO/IEC JTC1/SC22/WG21 C++标准委员会计划在2010年8月之前完成对最终委员会草案的投票,以及于2011年3月召开的标准会议完成国际标准的最终草案。然而,WG21预期ISO将要花费六个月到一年的时间才能正式发布新的C++标准。为了能够如期完成,委员会决定致力于直至2006年为止的提案,忽略新的提案。最终于2011年8月12日公布,并于2011年9月出版。

2012年2月28日的国际标准草案是最接近于C++11标准的草案,差异仅有编辑上的修正。

像C++这样的编程语言,透过一种演化的的过程来发展其定义。这个过程不可避免地将引发与现有代码的兼容问题,在C++的发展过程中偶尔会发生。不过根据比雅尼·斯特劳斯特鲁普(C++的创始人并且是委员会的一员)表示,新的标准将几乎100%兼容于现有标准。

C++14

C++14是C++的现行标准的非正式名称,正式名称为”International Standard ISO/IEC 14882:2014(E) Programming Language C++”。C++14旨在作为C++11的一个小扩展,主要提供漏洞修复和小的改进。C++14标准的委员会草案(Committee Draft)N3690于2013年5月15日发表。工作草案(Working Draft)N3936已于2014年3月2日完成。最终的投票期结束于2014年8月15日,结果(一致通过)已于8月18日公布。

C++17

C++17又称C++1z,是C++的现行标准的非正式名称,正式名称为”International Standard ISO/IEC Programming Language C++”。C++17旨在作为大型扩展,最终的投票期将于2017年结束。

当前不少著名C++编译器已支持C++17仍未定案的草案(draft),例如最新的GCC6已支持C++ concept的C++事务型内存(Transactional Memory),Visual Studio与Clang当前都提供了modules。

C++新语言特性

Lambda函数与表示式

在标准C++,特别是当使用C++标准程序库算法函数诸如sort和find,用户经常希望能够在算法函数调用的附近定义一个临时的述部函数(又称谓词函数,predicate function)。由于语言本身允许在函数内部定义类别,可以考虑使用函数对象,然而这通常既麻烦又冗赘,也阻碍了代码的流程。此外,标准C++不允许定义于函数内部的类别被用于模板,所以前述的作法是不可行的。

C++11对lambda(即匿名函数)的支持可以解决上述问题。

一个lambda函数可以用如下的方式定义:

1
[](int x, int y) { return x + y; }

这个不具名函数的回返类型是decltype(x+y)。只有在lambda函数匹配”return expression”的形式下,它的回返类型才能被忽略。在前述的情况下,lambda函数仅能为一个述句。

在一个更为复杂的例子中,回返类型可以被明确的指定如下:

1
[](int x, int y) -> int { int z = x + y; return z + x; }

本例中,一个临时的参数z被创建用来存储中间结果。如同一般的函数,z的值不会保留到下一次该不具名函数再次被调用时。

如果lambda函数没有传回值(例如void),其回返类型可被完全忽略。

定义在与lambda函数相同作用域的参数引用也可以被使用。这种的参数集合一般被称作closure(闭包)。

1
2
3
4
5
6
[]      // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。

参考链接

  1. C++,by wikipedia.
  2. C++ 的历史,by cppreference.
  3. C++03,by wikipedia.
  4. C++11,by wikipedia.
  5. C++14,by wikipedia.
  6. C++17,by wikipedia.

有趣的数学游戏

发表于 2019-06-23 | 更新于 2019-06-24

记录一些有趣的数学游戏,与小伙伴分享。

三门问题

问题描述

三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let’s Make a Deal。

问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机率?

问题解答

漫画:反直觉的 “三门问题”

海盗分金币问题

问题描述

经济学上有个“海盗分金”模型:是说5个海盗抢得100枚金币,他们按抽签的顺序依次提方案:首先由1号提出分配方案,然后5人表决,投票要超过半数同意方案才被通过,否则他将被扔入大海喂鲨鱼,依此类推。

“海盗分金”其实是一个高度简化和抽象的模型,体现了博弈的思想。在“海盗分金”模型中,任何“分配者”想让自己的方案获得通过的关键是事先考虑清楚“挑战者”的分配方案是什么,并用最小的代价获取最大收益,拉拢“挑战者”分配方案中最不得意的人们。

问题解答

漫画:有趣的海盗问题

参考链接

  1. 漫画:反直觉的 “三门问题”,by 小灰.
  2. 漫画:有趣的海盗问题,by 小灰.

gdb调试入门

发表于 2019-06-23

GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c++程序员,gdb是必不可少的工具。

启动gdb

对C/C++程序的调试,需要在编译前就加上-g选项。对大型项目,一般选择Debug选项进行编译以 方便gdb调试。

1
$ g++ -g hello.cpp -o hello

调试可执行文件:

1
$gdb <program>

program也就是你的执行文件,一般在当前目录下。

gdb交互命令

启动gdb后,进入到交互模式,通过以下命令完成对程序的调试;注意高频使用的命令一般都会有缩写,熟练使用这些缩写命令能提高调试的效率;

运行

  • run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。
  • continue (简写c ):继续执行,到下一个断点处(或运行结束)
  • next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。
  • step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的
  • until:当你厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体。
  • until+行号: 运行至某行,不仅仅用来跳出循环
  • finish: 运行程序,直到当前函数完成返回,并打印函数返回时的堆栈地址和返回值及参数值等信息。
  • call 函数(参数):调用程序中可见的函数,并传递“参数”,如:call gdb_test(55)
  • quit:简记为 q ,退出gdb

设置断点

  • break n (简写b n):在第n行处设置断点
    (可以带上代码路径和代码名称: b OAGUPDATE.cpp:578)
  • b fn1 if a>b:条件断点设置
  • break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button
  • delete 断点号n:删除第n个断点
  • disable 断点号n:暂停第n个断点
  • enable 断点号n:开启第n个断点
  • clear 行号n:清除第n行的断点
  • info b (info breakpoints) :显示当前程序的断点设置情况
  • delete breakpoints:清除所有断点

查看源代码

  • list :简记为 l ,其作用就是列出程序的源代码,默认每次显示10行。
  • list 行号:将显示当前文件以“行号”为中心的前后10行代码,如:list 12
  • list 函数名:将显示“函数名”所在函数的源代码,如:list main
  • list :不带参数,将接着上一次 list 命令的,输出下边的内容。

打印表达式

  • print 表达式:简记为 p ,其中“表达式”可以是任何当前正在被测试程序的有效表达式,比如当前正在调试C语言的程序,那么“表达式”可以是任何C语言的有效表达式,包括数字,变量甚至是函数调用。
  • print a:将显示整数 a 的值
  • print ++a:将把 a 中的值加1,并显示出来
  • print name:将显示字符串 name 的值
  • print gdb_test(22):将以整数22作为参数调用 gdb_test() 函数
  • print gdb_test(a):将以变量 a 作为参数调用 gdb_test() 函数
  • display 表达式:在单步运行时将非常有用,使用display命令设置一个表达式后,它将在每次单步进行指令后,紧接着输出被设置的表达式及值。如: display a
  • watch 表达式:设置一个监视点,一旦被监视的“表达式”的值改变,gdb将强行终止正在被调试的程序。如: watch a
  • whatis :查询变量或函数
  • info function: 查询函数
  • 扩展info locals: 显示当前堆栈页的所有变量

查询运行信息

  • where/bt :当前运行的堆栈列表;
  • bt backtrace 显示当前调用堆栈
  • up/down 改变堆栈显示的深度
  • set args 参数:指定运行时的参数
1
2
set args -l a -C abc
set args -l=a -C=abc
  • show args:查看设置好的参数
  • info program: 来查看程序的是否在运行,进程号,被暂停的原因。

分割窗口

  • layout:用于分割窗口,可以一边查看代码,一边测试:
  • layout src:显示源代码窗口
  • layout asm:显示反汇编窗口
  • layout regs:显示源代码/反汇编和CPU寄存器窗口
  • layout split:显示源代码和反汇编窗口
  • Ctrl + L:刷新窗口

参考链接

  1. gdb 调试利器,by Linux Tools Quick Tutorial.
  2. GDB调试带参数的程序(转载+整理+实践),by blacet.
上一页1…383940…53下一页

Jack Huang

523 日志
67 标签
© 2025 Jack Huang
由 Hexo 强力驱动
|
主题 — NexT.Muse