Python两个案例练习

项目介绍

Python官方LOGO

项目是一个python入门的案例代码,可以帮助你通过实践来更好的学习python

项目地址:https://gitlab.com/lxqxsyu/PythonDemos

Tkinter是什么

Tkinter是python自带的一个图形开发介面库
python提供了多个图形开发界面的库,几个常用Python GUI库如下:

  • Tkinter: Tkinter模块(“Tk 接口”)是Python的标准Tk GUI工具包的接口.Tk和Tkinter可以在大多数的Unix平台下使用,同样可以应用在Windows和Macintosh系统里.,Tk8.0的后续版本可以实现本地窗口风格,并良好地运行在绝大多数平台中。

  • wxPython:wxPython 是一款开源软件,是 Python 语言的一套优秀的 GUI 图形库,允许 Python 程序员很方便的创建完整的、功能键全的 GUI 用户界面。

  • Jython:Jython程序可以和Java无缝集成。除了一些标准模块,Jython使用Java的模块。Jython几乎拥有标准的Python中不依赖于C语言的全部模块。比如,Jython的用户界面将使用Swing,AWT或者SWT。Jython可以被动态或静态地编译成Java字节码。

最简单 的Tkinter实例

tkinter显示一个窗体

我们从导入Tkinter模块开始。它包含了与Tk工具包一起工作所需的所有类,函数和其他东西。在大多数情况下,你可以简单地将所有东西从Tkinter导入你的模块名字空间:

1
from Tkinter import *

要初始化Tkinter,我们必须创建一个Tk根部件。这是一个普通的窗口,窗口管理器提供标题栏和其他装饰。您应该只为每个程序创建一个根窗口小部件,并且必须在任何其他窗口部件之前创建它。

1
root = Tk()

程序将保持在事件循环中,直到关闭窗口。事件循环不仅处理来自用户的事件(如鼠标点击和按键)还处理窗口系统(如重绘事件和窗口配置消息)事件。

1
root.mainloop()

相关文档地址:http://effbot.org/tkinterbook/tkinter-whats-tkinter.htm

python的模块

模块可以让你有逻辑的组织代码,可以把相关的代码分配到一个模块里。简单的说模块就是一个python文件,模块可以定义函数,类和变量.

import 语句

想使用Python源文件,只需在另一个源文件里执行import语句,语法如下:

1
import module1[, module2[,... moduleN]

当解释器遇到import语句,如果模块在当前的搜索路径就会被导入。
搜索路径是一个解释器会先进行搜索的所有目录的列表。如想要导入模块hello.py,需要把命令放在脚本的顶端:

1
2
3
4
5
6
7
8
#coding=utf-8
#!/usr/bin/python

# 导入模块
import support

# 现在可以调用模块里包含的函数了
support.print_func("Zara")

From…import 语句

Python的from语句让你从模块中导入一个指定的部分到当前命名空间中。语法如下:

1
from modname import name1[, name2[, ... nameN]]

例如,要导入模块fib的fibonacci函数,使用如下语句:

1
from fib import fibonacci

From…import* 语句

把一个模块的所有内容全都导入到当前的命名空间也是可行的,只需使用如下声明:

1
from modname import *

这提供了一个简单的方法来导入一个模块中的所有项目。然而这种声明不该被过多地使用。

定位模块

当你导入一个模块,Python解析器对模块位置的搜索顺序是:

  1. 当前目录
  2. 如果不在当前目录,Python则搜索在shell变量PYTHONPATH下的每个目录。
  3. 如果都找不到,Python会察看默认路径。UNIX下,默认路径一般为/usr/local/lib/python/

模块搜索路径存储在system模块的sys.path变量中。变量里包含当前目录,PYTHONPATH和由安装过程决定的默认目录。

python内置了很多常用的模块

dir()函数

dir()函数一个排好序的字符串列表,内容是一个模块里定义过的名字。

返回的列表容纳了在一个模块里定义的所有模块,变量和函数。如下一个简单的实例:

1
2
3
4
5
6
7
8
9
#coding=utf-8
#!/usr/bin/python

# 导入内置math模块
import math

content = dir(math)

print(content)

dir()函数执行结果

python面向对象

创建类

1
2
3
4
5
6
7
8
9
10
class Person:

#特殊方法,构造函数,创建这个类的时候会首先调用
def __init__(self, name, age):
self.name = name
self.age = age

#定义一个方法,显示人的信息
def displyPersonInfor(self):
print("name:", self.name, ", age:", self.age)

创建对象(对象就是类的具体实例)

1
person = Person("xiaoming", 22)

访问属性及方法

1
2
3
4
person.displyPersonInfor()  #访问类的成员方法

print(person.name) #访问类的属性
print(person.age)

python对象销毁(垃圾回收)

析构函数 __del____del__在对象销毁的时候被调用,当对象不再被使用时,__del__方法运行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person:

#特殊方法,构造函数,创建这个类的时候会首先调用
def __init__(self, name, age):
self.name = name
self.age = age

#定义一个方法,显示人的信息
def displyPersonInfor(self):
print("name:", self.name, ", age:", self.age)

#析构函数,对象销毁的时候调用
def __del__(self):
print(self.__class__.__name__ + "销毁")


xiaoming = Person("xiaoming", 22)
xiaoming.displyPersonInfor() #访问类的成员方法

xiaoqiang = Person("xiaoqiang", 25)
xiaoqiang.displyPersonInfor()

del xiaoming
del xiaoqiang

类的继承

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。

继承语法 class 派生类名(基类名):基类名写作括号里,基本类是在类定义的时候,在元组之中指明的

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
class Person:

#特殊方法,构造函数,创建这个类的时候会首先调用
def __init__(self, name, age):
self.name = name
self.age = age

#定义一个方法,显示人的信息
def displyPersonInfor(self):
print("name:", self.name, ", age:", self.age)

#析构函数,对象销毁的时候调用
def __del__(self):
print(self.__class__.__name__ + "销毁")


class Student(Person):

def __init__(self, name, age, grade):
Person.__init__(self, name, age)
self.grade = grade

def displyPersonInfor(self):
print("i am a student, my name is " + self.name)


def __del__(self):
print("student析构函数调用")


xiaoming = Person("xiaoming", 22)
xiaoming.displyPersonInfor() #访问类的成员方法

xiaoqiang = Student("xiaoqiang", 25, 3)
xiaoqiang.displyPersonInfor()

del xiaoming
del xiaoqiang

Tkinter面向对象写法

创建一个Frame窗体,然后给里面添加了一个关闭按钮

Tkinter面向对象写法

相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python3.6.3
#-*-coding:utf-8-*-
import tkinter

class App:
def __init__(self, master):
frame = tkinter.Frame(master)
frame.pack()

self.button = tkinter.Button(
frame, text="关闭", fg="red", command=frame.quit)
self.button.pack()

root = tkinter.Tk(None, None)
app = App(root)
root.mainloop()

细心的朋友会发现上面每次创建一个窗体对象就会调用一次pack()方法,这个pack()方法是用来确定窗体大小然后重绘窗体的。

案例一:计算器界面

我们先写出计算器界面的最上面一行和输入框

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
#!/usr/bin/python3
#-*-coding:utf-8-*-
import tkinter
import tkinter.font

class Calculate():
def __init__(self, master):
master.title("Calculate")
master.resizable(0, 0)

self.showfont = tkinter.font.Font(master, size=26)
self.sysfont = tkinter.font.Font(master, size=16)

self.entry = tkinter.Entry(master, width=20, font=self.showfont, background="#ffffff")
self.entry.grid(row=0, column=0, columnspan=4, pady=10)

self.btn1 = tkinter.Button(master, text="1", font=self.sysfont)
self.btn1.grid(row=1, column=0, sticky = tkinter.N + tkinter.S + tkinter.W+tkinter.E)

self.btn2 = tkinter.Button(master, text="2", font=self.sysfont)
self.btn2.grid(row=1, column=1, sticky = tkinter.N + tkinter.S + tkinter.W+tkinter.E)

self.btn3 = tkinter.Button(master, text="3", font = self.sysfont)
self.btn3.grid(row=1, column=2, sticky = tkinter.N + tkinter.S + tkinter.W + tkinter.E)

self.btn_divide = tkinter.Button(master, text = "÷", font=self.sysfont)
self.btn_divide.grid(row=1, column = 3, sticky = tkinter.N + tkinter.S + tkinter.W + tkinter.E)

root = tkinter.Tk(None, None)
cal=Calculate(root)
root.mainloop()

tkinter实现计算器按钮过程示例

修改框体的名字,也可在创建时使用className参数来命名;

root.title(‘标题名’)

框体大小可调性,分别表示x,y方向的可变性;

root.resizable(0,0)

tkinter有那些UI控件呢?

Tkinter supports 15 core widgets

Button、Cavans、Checkbutton、Entry、Frame、Label、Listbox、Menu、Menubutton、Message、Radiobutton、Scale、Scrollbar、Text、Toplevel

计算器全部代码

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#!/usr/bin/python3
#-*-coding:utf-8-*-
import tkinter
import tkinter.font


class Calculate():
def __init__(self, master):
master.title("Calculate")
master.resizable(0, 0)
self.showfont = tkinter.font.Font(master, size=26)
self.sysfont = tkinter.font.Font(master, size=16)
self.entry = tkinter.Entry(
master, width=20, font=self.showfont, background="#ffffff")
self.entry.grid(row=0, column=0, columnspan=4, pady=10)
self.btn1 = tkinter.Button(master, text="1", font=self.sysfont)
self.btn1.grid(row=1, column=0, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn2 = tkinter.Button(master, text="2", font=self.sysfont)
self.btn2.grid(row=1, column=1, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn3 = tkinter.Button(master, text="3", font=self.sysfont)
self.btn3.grid(row=1, column=2, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_divide = tkinter.Button(master, text="÷", font=self.sysfont)
self.btn_divide.grid(
row=1, column=3, sticky=tkinter.N + tkinter.S + tkinter.W + tkinter.E)
self.btn4 = tkinter.Button(master, text="4", font=self.sysfont)
self.btn4.grid(row=2, column=0, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn5 = tkinter.Button(master, text="5", font=self.sysfont)
self.btn5.grid(row=2, column=1, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn6 = tkinter.Button(master, text="6", font=self.sysfont)
self.btn6.grid(row=2, column=2, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_mult = tkinter.Button(master, text="×", font=self.sysfont)
self.btn_mult.grid(row=2, column=3, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn7 = tkinter.Button(master, text="7", font=self.sysfont)
self.btn7.grid(row=3, column=0, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn8 = tkinter.Button(master, text="8", font=self.sysfont)
self.btn8.grid(row=3, column=1, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn9 = tkinter.Button(master, text="9", font=self.sysfont)
self.btn9.grid(row=3, column=2, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_minus = tkinter.Button(master, text="-", font=self.sysfont)
self.btn_minus.grid(row=3, column=3, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn0 = tkinter.Button(master, text="0", font=self.sysfont)
self.btn0.grid(row=4, column=0, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_point = tkinter.Button(master, text=".", font=self.sysfont)
self.btn_point.grid(row=4, column=1, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_es = tkinter.Button(master, text="=", font=self.sysfont)
self.btn_es.grid(row=4, column=2, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)
self.btn_add = tkinter.Button(master, text="+", font=self.sysfont)
self.btn_add.grid(row=4, column=3, sticky=tkinter.N +
tkinter.S + tkinter.W + tkinter.E)


root = tkinter.Tk(None, None)
cal = Calculate(root)
root.mainloop()

tkinter实现的计算器界面

Python中的包

包是一个分层次的文件目录结构,它定义了一个由模块及子包,和子包下的子包等组成的Python的应用环境。
考虑一个在Phone目录下的pots.py文件。这个文件有如下源代码:

1
2
3
4
5
#coding=utf-8
#!/usr/bin/python

def Pots():
print "I'm Pots Phone"

同样地,我们有另外两个保存了不同函数的文件:

  • Phone/Isdn.py 含有函数Isdn()
  • Phone/G3.py 含有函数G3()

现在,在Phone目录下创建file __init__.py

  • Phone/__init__.py

当你导入Phone时,为了能够使用所有函数,你需要在__init__.py里使用显式的导入语句,如下:

1
2
3
from Pots import Pots
from Isdn import Isdn
from G3 import G3

当你把这些代码添加到__init__.py之后,导入Phone包的时候这些类就全都是可用的了。

packagetest.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#coding=utf-8
#!/usr/bin/python

# 导入内置math模块
#import math

#导入我们创建的Phone包
import Phone

#content = dir(math)
#print(content)

Phone.Pots()
Phone.Isdn()
Phone.G3()

python工程目录结构

案例二(android多渠道打包)

1
2
3
4
5
6
7
8
#!/usr/bin/python
# coding=utf-8

# 空文件 便于写入此空文件到apk包中作为channel文件
src_empty_file = 'info/czt.txt'
# 创建一个空文件(不存在则创建)
f = open(src_empty_file, 'w')
f.close()

open函数

python中必须先用Python内置的open()函数打开一个文件,创建一个file对象,相关的辅助方法才可以调用它进行读写。

1
file object = open(file_name [, access_mode][, buffering])

各个参数的细节如下:

  • file_name:file_name变量是一个包含了你要访问的文件名称的字符串值。
  • access_mode:access_mode决定了打开文件的模式:只读,写入,追加等。所有可取值见如下的完全列表。这个参数是非强制的,默认文件访问模式为只读(r)。
  • buffering:如果buffering的值被设为0,就不会有寄存。如果buffering的值取1,访问文件时会寄存行。如果将buffering的值设为大于1的整数,表明了这就是的寄存区的缓冲大小。如果取负值,寄存区的缓冲大小则为系统默认。

不同模式打开文件的完全列表:

不同模式打开文件的完全列表

File对象的属性

一个文件被打开后,你有一个file对象,你可以得到有关该文件的各种信息。
以下是和file对象相关的所有属性的列表:

File对象的属性

1
2
3
4
5
6
7
8
9
#!/usr/bin/python
# -*- coding: UTF-8 -*-

# 打开一个文件
fo = open("foo.txt", "wb")
print "文件名: ", fo.name
print "是否已关闭 : ", fo.closed
print "访问模式 : ", fo.mode
print "末尾是否强制加空格 : ", fo.softspace

close()方法

File对象的close()方法刷新缓冲区里任何还没写入的信息,并关闭该文件,这之后便不能再进行写入。
当一个文件对象的引用被重新指定给另一个文件时,Python会关闭之前的文件。用close()方法关闭文件是一个很好的习惯。

1
fileObject.close();

更多关于读写的方法请参考:https://www.w3cschool.cn/python/python-files-io.html

筛选apk文件

1
2
3
4
5
6
7
8
9
10
11
12
13
# 获取当前目录中所有的apk源包
src_apks = []
# python3 : os.listdir()即可,这里使用兼容Python2的os.listdir('.')
apk_path = 'E:/AndroidStudio/work/xxx/app/build/outputs/apk/'
for file in os.listdir(apk_path):
fulldirfile = os.path.join(apk_path, file)
if os.path.isfile(fulldirfile):
extension = os.path.splitext(file)[1][1:]
if extension in 'apk':
if file.find('unaligned') == -1:
src_apks.append(fulldirfile)

print(src_apks)

Python中有很多标准库,我们来看看这个操作系统接口库os

注意:确保使用import os而不是from os import *。这样可以防止函数os.open()覆盖内建函数open(),两者之间的操作是很不同的。

我们可以通过dir()和help()函数来帮助我们了解这些库是怎么使用的

1
2
3
4
5
6
7
import os

dir = dir(os)

print(dir)

help(os)

dir()和help()函数的输出结果

os.listdir(path=’.’)

返回一个list,包含给定path 目录下所有条目的名字。该list是任意顺序,不包括特殊条目’.’以及’..’,即使它们存在于目录中。

path可以是str类型或bytes类型。如果path的类型为bytes,则返回的文件名也将为bytes类型;否则,它们的类型为str。

os.path.join(path, *paths)
将一个或多个路径正确地连接起来。返回值是路径和*路径的任何成员与每个非空的后面紧跟一个目录分隔符(os.sep)的连接部分

os.path.isfile(path)
如果路径是现有的常规文件,则返回True。

os.path.splitext(path)
将从文件路径的后缀名分割字符串

关于更多module-os的函数请参考:http://python.usyiyi.cn/translate/python_352/library/os.html#module-os

os.path相关文档:http://python.usyiyi.cn/translate/python_352/library/os.path.html#module-os.path

完整代码

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/python
# coding=utf-8

import os
import zipfile
import shutil

# 空文件 便于写入此空文件到apk包中作为channel文件
src_empty_file = 'info/czt.txt'
# 创建一个空文件(不存在则创建)
f = open(src_empty_file, 'w')
f.close()

# 获取当前目录中所有的apk源包
src_apks = []
# python3 : os.listdir()即可,这里使用兼容Python2的os.listdir('.')
apk_path = 'E:/AndroidStudio/work/xxx/app/build/outputs/apk/'
for file in os.listdir(apk_path):
fulldirfile = os.path.join(apk_path, file)
if os.path.isfile(fulldirfile):
extension = os.path.splitext(file)[1][1:]
if extension in 'apk':
if file.find('unaligned') == -1:
src_apks.append(fulldirfile)

print(src_apks)


# 获取渠道列表
channel_file = 'info/channel.txt'
f = open(channel_file)
lines = f.readlines()
f.close()

for src_apk in src_apks:
# file name (with extension)
src_apk_file_name = os.path.basename(src_apk)
# 分割文件名与后缀
temp_list = os.path.splitext(src_apk_file_name)
# name without extension
src_apk_name = temp_list[0]
# 后缀名,包含. 例如: ".apk "
src_apk_extension = temp_list[1]

# 创建生成目录,与文件名相关
package_name = src_apk_name[0:src_apk_name.index('-')]
output_dir = 'G:/XXX打包/' + package_name +'/output_' + src_apk_name + '/'
# 目录不存在则创建
if not os.path.exists(output_dir):
os.mkdir(output_dir)

# 遍历渠道号并创建对应渠道号的apk文件
for line in lines:
# 获取当前渠道号,因为从渠道文件中获得带有\n,所有strip一下
target_channel = line.strip()
# 拼接对应渠道号的apk
target_apk = output_dir + src_apk_name + "-" + target_channel + src_apk_extension
# 拷贝建立新apk
shutil.copy(src_apk, target_apk)
# zip获取新建立的apk文件
zipped = zipfile.ZipFile(target_apk, 'a', zipfile.ZIP_DEFLATED)
# 初始化渠道信息
empty_channel_file = "META-INF/cztchannel_{channel}".format(channel = target_channel)
# 写入渠道信息
zipped.write(src_empty_file, empty_channel_file)
# 关闭zip流
zipped.close()

打包思路参考请参考:https://segmentfault.com/a/1190000003763833