宽松许可
- MIT
- Apache
- BSD
鼓励开源和代码共享,允许修改、再发布,尊重原作者权利,在发布或代码引用处声明BSD协议
弱化的著作权
- LGPL
著作权
- GPL
宽松许可
弱化的著作权
著作权
使用 jenkins ant 自动化脚本调用build.xml任务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<?xml version="1.0" ?>
<project basedir="." default="build_all" name="TestProj">
<description>
TestProj Build File.
</description>
<tstamp>
<format locale="en" pattern="yyMMdd" property="TODAY_CN"/>
</tstamp>
<property name="build_ver" Value="1.0.5.0" />
<target description="Create publish folder" name="create_folder">
<delete dir="${build_ver}" quiet="true"/>
<mkdir dir="${build_ver}\publish"/>
</target>
<target description="Pack TestProj" name="sign_and_copy">
<exec dir="." executable="cmd.exe">
<arg line="/c"/>
<arg line=".\uac_cert\signtool.exe sign /f ".\uac_cert\test.pfx" /p passwordxxx /fd SHA256 /t "http://timestamp.digicert.com" "..\TestProj\bin\Release\TestProj.exe""/>
</exec>
<copy file="..\TestProj\bin\Release\TestProj.exe" overwrite="true" todir="${build_ver}\publish"/>
<copy file="..\TestProj\bin\Release\Microsoft.Identity.Client.dll" overwrite="true" todir="${build_ver}\publish"/>
<copy file="..\TestProj\bin\Release\Newtonsoft.Json.dll" overwrite="true" todir="${build_ver}\publish"/>
<zip destfile="${build_ver}\publish.zip" basedir="${build_ver}\publish" />
<delete dir="${build_ver}\publish" quiet="true"/>
</target>
<!-- Build All -->
<target description="Build solution" name="build_all">
<antcall target="create_folder"/>
<antcall target="pack_pubclient"/>
</target>
</project>
其中使用signtool.exe给构建生成的应用添加数字签名,签名密钥保存在pfx文件中 需使用password访问
issue: capicom.dll没有正确安装或者是没有注册
加密API组件对象模型(Cryptographic API Component Object Model,capicom)微软Windows系统组件,用于以数字方式签署数据代码、验证数字签章、加密解密等,见CryptoAPI
注册capicom1
Regsvr32 c:/windows/system32/capicom.dll
提供允许你与系统进程、事件日志和性能计数器进行交互的类。
1 | using System.Diagnostics |
argparse模块是python用于解析命令行参数和选项的标准模块 对于封装好的py函数文件 可实现在命令行输出—help的效果:
定义函数文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#-*- coding: UTF-8 -*-
import argparse
def parse_args():
"""
:return:进行参数的解析
"""
description = "you should add those parameter"
parser = argparse.ArgumentParser(description=description) # 这些参数都有默认值,当调用parser.print_help()或者运行程序时由于参数不正确(此时python解释器其实也是调用了pring_help()方法)时,
# 会打印这些描述信息,一般只需要传递description参数,如上。
mode_desc = "action mode"
image_desc = "the path of image"
parser.add_argument('--mode', help=mode_desc)
parser.add_argument('--image', help=image_desc)
args = parser.parse_args()
return args
if __name__ == '__main__':
args = parse_args()
if()
命令行输入1
python arg.py -h
输出提示为1
2
3
4
5
6
7
8
9
10Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
you should add those parameter
optional arguments:
-h, --help show this help message and exit
--addresses ADDRESSES
The path of address
ArgumentParser.add_argument(name or flags…[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
action— 命令行遇到参数时的动作,默认值是 store; store_const—表示赋值为const;append—将遇到的值存储成列表,也就是如果参数重复则会保存多个值; append_const—将参数规范中定义的一个值保存到一个列表;count—存储遇到的次数;此外,也可以继承 argparse.Action 自定义参数解析动作;
nargs — 应该读取的命令行参数个数,可以是数字,或者通配符如?表示0个或1个;*表示 0 或多个;+表示 1 或多个。
const - action 和 nargs 所需要的常量值。
1 | parser.add_argument('--name', type=str, required=True, default='', help='名') |
是否输入了某参数1
parser.add_argument('--properties', action='store_true', help=properties_desc)
如上调用python arg.py时,若带参数—properties 则args.properties=True 否则为False
WebView2使用 Microsoft Edge(要知道Edge也是fork自Chromium的分发版本) 作为绘制引擎, 展示web内容的混合应用控件
可以认为Electron是WebView2的前辈 二者的原理是一样的
WebView2 runtime is available on most Windows 10 and Windows 11 machines by default. But it may not be available on older platforms.
若缺需要查看WebView2运行时是否安装 Microsoft Docs:检测是否已安装合适的 WebView2 运行时
PyQt 是 Qt 的Python版本,Qt是基于C++的GUI,在Python的UI组件库中,PyQt功能强大,提供QT Designer设计UI。PyQt 可与 C++ Qt无缝整合,另有QtWebEngine结合web前端实现Electron的功能。
题外话:Tkinter 是Python解决UI的原生库,优点在于没有其他依赖。但内容基础功能简单 比其他UI库都有不及
PyQt5类分为很多模块
QtCore 包含了核心的非GUI的功能。主要和时间、文件与文件夹、各种数据、流、URLs、mime类文件、进程与线程一起使用。
QtGui 包含了窗口系统、事件处理、2D图像、基本绘画、字体和文字类。QtWidgets类包含了一系列创建桌面应用的UI元素。 QtMultimedia包含了处理多媒体的内容和调用摄像头API的类。 QtNetwork包含了网络编程的类,这些工具能让TCP/IP和UDP开发变得更加方便和可靠。QtWebSockets包含了WebSocket协议的类。 QtWebKit包含了一个基WebKit2的web浏览器。
1 | pip install pyqt5 |
使用国内pip源1
pip install -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple pyqt5
添加pyqt5-tools路径到环境变量Path
配置PyCharm外部工具
PyCharm -> 文件 -> 设置 -> 工具 -> 外部工具
填入QT designer路径
另添加 Pyuic 用于将ui文件转为py文件
须填入参数$FileName$ -o $FileNameWithoutExtension$.py
类似的 可添加PyRCC
1 | import sys |
完成如上配置 可在PyCharm的外部工具打开 QT designer,
使用QT designer生成UI文件
1 | pyuic demo.ui -o demo.py |
生成的py文件是UI的类定义文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(800, 600)
self.centralWidget = QtWidgets.QWidget(MainWindow)
self.centralWidget.setObjectName("centralWidget")
self.btn_import = QtWidgets.QToolButton(self.centralWidget)
self.btn_import.setGeometry(QtCore, QRect(610, 20, 71, 22))
self.btn_import.setObjectName("btn_import")
self.btn_import.clicked.connect(self.on_clicked_btn_import)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.btn_import.setText(_translate("MainWindow", "import"))
def on_clicked_btn_import(self):
print("btn_import clicked")
.....
运行该UI需要在程序入口(__main__)实例化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
26import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
from ui.mainWindow import Ui_MainWindow # 引入UI的类定义
class Startup(QMainWindow, Ui_MainWindow):
def __init__(self):
super(Startup, self).__init__()
self.setupUi(self)
def importImg(self):
fname = QFileDialog.getOpenFileName(self, 'import Image', './')
if fname[0]:
QMessageBox.information(self, 'import success', fname[0], QMessageBox.Yes)
print("import img is here")
if __name__ == '__main__':
app = QApplication(sys.argv)
startup = Startup()
startup.show()
sys.exit(app.exec_())
可见这里的Startup类即 Ui_MainWindow 实例化参数object 两个类是相互引用的;Startup类函数即主窗体的槽函数
在控件层面 事件如点击按钮(clicked)选择菜单(triggered),事件可以直接connect到响应函数(槽函数)1
2
3
4btn = QPushButton('test', self)
btn.resize(btn.sizeHint())
btn.move(50, 50)
btn.clicked.connect(self.showDlg)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# MenuBar -> Menu -> Action -> Slot
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 842, 26))
self.menubar.setObjectName("menubar")
self.menu = QtWidgets.QMenu(self.menubar)
self.menu.setObjectName("menu")
MainWindow.setMenuBar(self.menubar)
self.actionimport = QtWidgets.QAction(MainWindow)
self.actionimport.setObjectName("actionimport")
self.menu.addAction(self.actionimport)
self.menubar.addAction(self.menu.menuAction())
self.retranslateUi(MainWindow)
self.actionimport.triggered.connect(MainWindow.importImg)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
或者,不同事件发送某信号,而某个模块监听到该信号而触发响应1
2
3
4
5
6
7
8
9
10
11# 创建一个信号closeApp。当触发鼠标点击事件时信号会被发射。信号连接到QMainWindow的close()方法。
class Communicate(QObject):
closeApp = pyqtSignal()
# 信号使用了pyqtSignal()方法创建,并且成为外部类Communicate类的属性。
self.c = Communicate()
self.c.closeApp.connect(self.close)
# 把自定义的closeApp信号连接到QMainWindow的close()槽上。
def mousePressEvent(self, event):
self.c.closeApp.emit()
# 当我们在窗口上点击一下鼠标,closeApp信号会被发射。应用中断。
pyinstaller vs Nuitka
Nuitka(音妞卡)是将python程序转换成C语言的可执行elf文件。这样在运行时就可以享受到C语言处理过程中的优化,提高速度。
1 | pip install -U nuitka |
编译依赖Python和C compiler,c compiler需支持C11或C++03 这意味着需安装MinGW64 C11编译器 基于gcc11.2或更高版本,或visual studio
用户刷新/访问资源行为的不同方式,会采用不同的缓存策略
对比缓存: 浏览器第一次请求数据时,服务器会将缓存标识与数据一起返回给浏览器,浏览器将二者备份至缓存数据库中。
当浏览器再次请求数据时,浏览器将备份的缓存标识发送给服务器,服务器根据缓存标识进行判断,判断成功后,返回304状态码,通知客户端比较成功,可以使用缓存数据。
对于对比缓存,响应 header 中会有两个字段来标明规则:Last-Modified / If-Modified-Since
服务器响应请求时,会告诉浏览器一个告诉浏览器资源的最后修改时间:Last-Modified,浏览器之后再请求的时候,会带上一个头:If-Modified-Since,这个值就是服务器上一次给的 Last-Modified 的时间,服务器会比对资源当前最后的修改时间,如果大于If-Modified-Since,则说明资源修改过了,浏览器不能再使用缓存,否则浏览器可以继续使用缓存,并返回304状态码。Etag / If-None-Match(优先级高于Last-Modified / If-Modified-Since)
返回304结果的资源调用了协商缓存
HTTP 缓存主要是通过请求和响应报文头中的对应 Header 信息,来控制缓存的策略。
Cache-Control 是最重要的规则。常见的取值有 private、public、no-cache、max-age,no-store,默认为 private。
私有缓存通常存在浏览器端,只存在本地,不会与其他客户端共享,因此可以保存个性化设置,部分资料中区分“浏览器缓存”和私有Http缓存的概念,“浏览器缓存”指localstorage sessionstorage cookies等1
Cache-Control: private
共享缓存可细分为代理缓存和托管缓存,代理缓存是网络中间代理提供的缓存,在Https普及的现状下应用收到局限,托管缓存由服务开发人员明确部署,以降低源服务器负载并有效地交付内容。如反向代理、CDN 和 service worker 与缓存 API 的组合。
对前端来说,缓存是缓存在前端,但实际上代码是后端的同学来写的。如果你需要实现前端缓存的话啊,通知后端的同学加响应头就好了。
以node.js为例 对于需要强缓存的资源1
res.setHeader('Cache-Control', 'public, max-age=xxx');
协商缓存1
2
3res.setHeader('Cache-Control', 'public, max-age=0');
res.setHeader('Last-Modified', xxx);
res.setHeader('ETag', xxx);
npmjs: fresh