本文
http://afra55.github.io/2017/12/06/python-second-step/
pip
如果没有 pip 则通过下面的链接下载 get-pip.py 到本地并运行
https://bootstrap.pypa.io/get-pip.py
或者通过如下链接安装
sudo python3 get-pip.py
Pygame
安装 Pygame 依赖的库 brew install hg sdl sdl_image sdl_mixer sdl_ttf
安装 Pygame pip3 install --user hg+http://bitbucket.org/pygame/pygame
或者在 https://bitbucket.org/pygame/pygame/downloads/ 下载Pygame对应文件进行安装
.whl 安装方法 python -m pip install --user xx.whl
创建
import pygame
要使用 pygame 则要先初始化: pygame.init()
接下来需要创建一个显示窗口,并持有这个窗口: screen = pygame.display.set_mode((1200, 800))
pygame.display.set_mode((1200, 800)) 创建显示窗口并返回一个 surface 对象,所有图形绘制都在这个窗口内, 实参是一个元组用于指定尺寸
(0, 0) 原点在屏幕左上角,向下向右增大
surface 代表屏幕的一部分,用于显示游戏元素
设置窗口标题: pygame.display.set_caption("Afra55 game")
开始游戏的主要循环:while True:
不断循环来处理所有发生的事物,比如监听按键,绘制图形等
while True:
# 监视键盘和鼠标事件
for event in pygame.event.get():
if event.type == pygame.QUIT: # 退出, type 指类型
sys.exit()
elif event.type == pygame.KEYDOWN: # 按键
if event.key == pygame.K_RIGHT: # 向右箭头 key 指具体的键
screen.fill(bg_color)
pygame.display.flip() # 让绘制的屏幕显示出来, 即更新屏幕显示
pygame.event.get() 来监听事件,按键或者鼠标移动,可以用 循环来捕捉事件
pygame 的颜色以 RGB(0-255)值指定,红(255, 0, 0), 绿(0, 255, 0), 蓝(0, 0, 255)
surface 的 fill() 方法接受一个颜色实参,来设置背景的填充颜色: screen.fill(bg_color)
def fill(self, color, rect=None, special_flags=0)
如果传入 rect 则在 rect 位置区域上绘制背景 color
surface 的 blit(surface, rect) 方法根据指定的位置 rect 绘制实参 surfcae 到调用这个方法的 surface 上
图片
创建一个图片 surface : pygame.image.load_basic('images/view.bmp')
获取 surface 的 rect 属性: image.get_rect()
, rect 包含 x, y, center, centerx, centery, top, bottom, left, right, height, width
rect.collidepoint(x, y)
返回一个 布尔类型,用于判断 (x, y) 这个点是否在 rect 区域内
显示所有的绘制:pygame.display.flip()
from pygame.sprite import Sprite
Sprite 俗称精灵,可以将相关元素编组,就可以同时操作编组中的所有元素
from pygame.sprite import Group
Group 编组,用于存储 Sprite 类似列表,并提供来额外的功能, 调用 update() 方法,则编组中所有的 Sprite 的 update 方法就被调用
sprite = Sprite() # 创建精灵
group.add(sprite) # 把精灵加入到编组中
for sprite in group.sprites(): # 遍历编组
print(sprite)
group.draw(screen)
注: 当使用 for 循环删除列表时,遍历的应该是列表的副本即 调用列表的 copy() 方法或使用切片来获取副本
pygame.draw.rect(self.screen, self.color, self.rect)
draw.rect() 用于绘制 一个 rect 大小的背景是 color 的方块
group.draw(screen)
对编组使用 draw(surface) 时,pygame 会自动绘制编组的每个元素到实参 surface 中,绘制的位置由元素的 rect 属性决定
for sprite in group.sprites():
便利编组的所有元素
pygame.sprite.groupcollide(groupa, groupb, dokilla, dokillb)
获取 groupa 和 groupb 两个编组中元素位置有重叠的元素,返回字典,key 是 groupa 中的元素,value 是 groupb 中的元素 是个列表, dokilla 和 dokillb 布尔类型, True 指删除编组中的该元素
group.empty()
清空编组中的所有元素
pygame.sprite.spritecollideany(sprite, group)
spritecollideany 方法第一个实参是精灵,第二个是编组,用于检测精灵是否和编组的元素位置有重叠, 如果没有重叠 返回 None,有重叠 则返回 group 中的这个元素
位置重叠,由元素的 rect 判断
from time import sleep
sleep(0.5)
sleep(seconds)
让程序睡眠 seconds 秒
文本
import pygame.ftfont
pygame.ftfont 能够再屏幕上渲染文本
font = pygame.ftfont.SysFont(None, 48)
返回一个 FontType 指用什么字体来渲染文本, 实参None指使用默认字体, 48指文本等字号,详细配置看文档
msg_image = font.render(msg, True, self.text_color, self.button_color)
def render(self, text, antialias, color, background=None)
将 text 文本转换为图像,并返回图像 surface,antialias 布尔类型是否开启反锯齿功能, color 指文本颜色, background 指背景色(默认是透明色)
not Flast == True
not True == False
not
类似 java 中的 !
def round(number, ndigits=None)
round() 让小数精确到小数点后 ndigits 位,如果 ndigits 为负数, round() 将圆整到最近的 10,100,100 等多整数倍
"{:,}".format(rounded_score)
将数值转化为字符串时,在其中插入逗号,例如 1,000,000
小结
Python编程 从入门到实践 里面的打飞机游戏很有意思,做完后有了更深一层的认识
https://github.com/Afra55/alien_invasion
GUI 编程
图形用户界面编程
python 默认的 UI 工具包 tkinter ,它基于 Tk 工具包(最初为工具命令语言 Tcl 设计的)
1. 导入 Tkinter 模块
2. 创建 一个 顶层 窗口 对象, 用于 容纳 整个 GUI 应用
3. 在 顶层 窗口 对象 之上( 或者“ 其中”) 构建 所有 的 GUI 组件( 及其 功能)
4. 通过 底层 的 应用 代码 将 这些 GUI 组件 连接 起来
5. 进入 主事 件 循环
创建顶层窗口,可以创建多个顶层窗口,但是只能有一个根窗口
tkinter.Tk()
顶层 的 根 窗口 对象 包含 组成 GUI 应用 的 所有 小窗 口 对象。 它们 可能 是 文字 标签、 按钮、 列表 框 等。 这些 独立 的 GUI 组件 称为 控 件。 所以 当 我们 说 创建 一个 顶层 窗口 时, 只是 表示 需要 一个 地方 来 摆放 所 有的 控 件
事件 可以 包括 按钮 按下( 及 释放)、 鼠标 移动、 敲击 回车 键 等。 一个 GUI 应用 从 开始 到 结束 就是 通过 整套 事件 体系 来 驱动 的。 这种 方式 称为 事件 驱动 处理
布局管理器有三种:Placer(设置控件的大小和位置来配置摆放控件),Packer(填充控件到父控件中),Grid(网格)
摆放完控件后进入主循环:tkinter.mainloop()
常用控件 | 描述 |
---|---|
Button | 用于包含文本或图像,并提供额外事件,例如 鼠标悬浮、按下、释放、键盘事件 |
Canvas | 提供绘制图像到功能(线段,椭圆,多边形,矩形),可以包含图像或位图 |
CheckButton | 多选框 |
Entry | 单行文本框,用于收集键盘输入 |
Frame | 包含其他控件到纯容器 |
Label | 包含文本或图像 |
LabelFrame | 一个 Frame 可以包含文本或图像 |
Listbox | 选项列表 |
Menu | 按下 Menubutton 后弹出的选项列表,用户可以从中选择 |
Menubutton | 用于包含 Menu |
Message | 消息,多行文本框 |
PanedWindow | 容器控件,可以控制其他控件在其中摆放 |
Radiobutton | 单选框 |
Scale | 线性’滑块’控件,根据起始和结束值,来返回当前值 |
Scrollbar | 为 Text、Canvas、Listbox、Entry 提供滑动功能 |
Spinbox | Entry 和 Button 的组合,可以对值进行调整 |
Text | 多行文本框,用于收集或显示用户输入的文本 |
Toplevel | 于 Frame 类似,并提供一个单独的窗口 |
Label 示例
import tkinter
if __name__ == '__main__':
top = tkinter.Tk()
label = tkinter.Label(top, text='Hello World!')
label.pack()
tkinter.mainloop()
Button 示例
import tkinter
if __name__ == '__main__':
top = tkinter.Tk()
quit_btn = tkinter.Button(top, text='Hello World!', command=top.quit) # command 用于绑定点击的回调函数
quit_btn.pack()
tkinter.mainloop()
Packer 默认垂直排列
import tkinter
if __name__ == '__main__':
top = tkinter.Tk()
hello = tkinter.Label(top, text='Hello Afra55!')
hello.pack()
quit_btn = tkinter.Button(top, text='QUIT!', command=top.quit, bg='red', fg='white')
quit_btn.pack(fill=tkinter.X, expand=1)
tkinter.mainloop()
Scale 示例
import tkinter
def resize(ev=None):
hello.config(font='华康少女字体 -%d bold' % scale.get())
if __name__ == '__main__':
top = tkinter.Tk()
top.geometry('400x400')
hello = tkinter.Label(top, text='床前明月光!', font='华康少女字体 -12 bold')
hello.pack(fill=tkinter.Y, expand=1)
scale = tkinter.Scale(top, from_=10, to=40, orient=tkinter.HORIZONTAL, command=resize)
scale.set(12)
scale.pack(fill=tkinter.X, expand=1)
quit_btn = tkinter.Button(top, text='QUIT!', command=top.quit, activeforeground='white', activebackground='red')
quit_btn.pack()
tkinter.mainloop()
偏函数
使用 偏 函数, 可以 通过 有效地“ 冻结” 那些 预先 确定 的 参数 来 缓存 函数 参数, 然后 在 运行时, 当获 得 需要 的 剩余 参数 后, 可以 将它 们 解冻, 传递 到 最终 的 参数 中, 从而 使用 最终 确定 的 所有 参数 去 调用 函数
from functools import partial
from tkinter import Tk, Button, X
from tkinter.messagebox import showinfo, showwarning, showerror # tk 对话框
warn = 'warn'
error = 'error'
info = 'info'
SIGNS = {
'error one': error,
'warn one': warn,
'info one': info,
'error two': error,
'warn two': warn,
'info two': info,
}
error_cb = lambda: showerror('Error', 'Error Button Pressed!')
warn_cb = lambda: showwarning('Warning', 'Warning Button Pressed!')
info_cb = lambda: showinfo('Info', 'Info Button Pressed!')
top = Tk()
top.geometry('400x400')
top.title('偏函数示例')
Button(top, text='退出', command=top.quit, bg='red', fg='white').pack()
# 这里使用了两阶偏函数
my_button = partial(Button, top) # 第一阶模版化两 Button 和 top,即每次调用 my_button 就会调用 Button类 并把 top 作为第一个参数
ErrorButton = partial(my_button, command=error_cb, bg='white', fg='red') # 第二阶偏函数,使用第一阶并模版化,创建按钮类型
WarnButton = partial(my_button, command=warn_cb, bg='goldenrod1')
InfoButton = partial(my_button, command=info_cb, bg='white')
for each_sign in SIGNS:
sign_type = SIGNS[each_sign]
cmd = '%sButton(text=%r%s).pack(fill=X, expand=True)' % (
sign_type.title(), each_sign,
'.upper()' if sign_type == error else '.title()')
eval(cmd) # 每个按钮通过 eval() 函数实例化
top.mainloop()
文件列表示例
import os
from time import sleep
from tkinter import *
class DirList(object):
def __init__(self, initdir=None):
self.top = Tk()
self.label = Label(self.top, text='文件夹监听器') # 标题
self.label.pack()
self.dirl = Label(self.top, fg='blue', font=('Helvetica', 12, 'bold')) # 显示当前路径
self.dirl.pack()
self.dirfm = Frame(self.top) # 框
self.dirsb = Scrollbar(self.dirfm) # 滑块
self.dirsb.pack(side=RIGHT, fill=Y) # 滑块在右侧
self.dirs = Listbox(self.dirfm, height=15, width=50, yscrollcommand=self.dirsb.set) # 列表,展示当前目录下的文件
self.dirs.bind('<Double-1>', self.setdirandgo) # 双击鼠标左键, 跳转到选择到目标路径
self.dirsb.config(command=self.dirs.yview) # 改变垂直方向的位置
self.dirs.pack(side=LEFT, fill=BOTH) # 列表在左侧,填充 xy
self.dirfm.pack()
self.cwd = StringVar(self.top)
self.dirn = Entry(self.top, width=50, textvariable=self.cwd) # 输入框
self.dirn.bind('<Return>', self.dols) # 绑定回车键, 跳转到输入框内容的目录下
self.dirn.pack()
self.bfm = Frame(self.top) # 最底下的容器
self.clr = Button(self.bfm, text='清空',
command=self.clrdir, # 清空输入框内容
activeforeground='white',
activebackground='blue')
self.ls = Button(self.bfm,
text='跳转到选中目录',
command=self.dols,
activeforeground='white',
activebackground='green')
self.quit = Button(self.bfm, text='退出',
command=self.top.quit,
activeforeground='white',
activebackground='red')
self.clr.pack(side=LEFT)
self.ls.pack(side=LEFT)
self.quit.pack(side=LEFT)
self.bfm.pack()
if initdir:
self.cwd.set(os.curdir)
self.dols()
def clrdir(self, ev=None):
"""
空输入框内容
"""
self.cwd.set('')
def setdirandgo(self, ev=None):
self.last = self.cwd.get()
self.dirs.config(selectbackground='red') # 选择的文件背景变为红色
check = self.dirs.get(self.dirs.curselection())
if not check:
check = os.curdir
self.cwd.set(check) # 设置输入框为选择的item文件名
self.dols() # 跳转到选择到文件目标
def dols(self, ev=None):
"""
跳转到目标目录
"""
error = ''
tdir = self.cwd.get() # 获取输入框的内容
if not tdir: # 入股内容为空,则默认为 '.'
tdir = os.curdir
if not os.path.exists(tdir): # 如果文件不存在
error = tdir + ': 没找到该文件'
elif not os.path.isdir(tdir): # 如果不是文件夹
error = tdir + ': 不是一个文件夹'
if error: # 如果出错
self.cwd.set(error) # 输入框设置出错信息
self.top.update() # 更新绘制
sleep(2) # 睡眠2秒
if not (hasattr(self, 'last') and self.last):
self.last = os.curdir
self.cwd.set(self.last) # 设置输入框为上一个正确的路径
self.dirs.config(selectbackground='LightSkyBlue') # 设置选择的item背景是亮蓝色
self.top.update() # 更新绘制
return
self.cwd.set('抓取文件夹内容...')
self.top.update()
dirlist = os.listdir(tdir) # 获取目标路径下的文件列表
dirlist.sort() # 按文件名 排序
os.chdir(tdir) # 将活动目录设置为目标路径
self.dirl.config(text=os.getcwd()) # 显示目标路径
self.dirs.delete(0, END) # 清空当前展示的文件列表
self.dirs.insert(END, os.curdir) # 插入 '.'
self.dirs.insert(END, os.pardir) # 插入 '..'
for eachFile in dirlist:
self.dirs.insert(END, eachFile) # 插入目标路径下的所有文件
self.cwd.set(os.curdir) # 输入框内容设置为 '.'
self.dirs.config(selectbackground='LightSkyBlue') # # 设置选择的item背景是亮蓝色
def main():
d = DirList(os.curdir)
mainloop()
if __name__ == '__main__':
main()
相关工具
https://wiki.python.org/moin/GuiProgramming