系列文章目录
用python尝试做游戏(一)
用python尝试做游戏(二)
文章目录
目标
之前记录了极度基础的界面怎么写,以及基础的键盘鼠标交互如何可以进行,接下来将围绕以下内容调查学习并记录:鼠标在画布(界面)的定位、画布中组件的定位(自身边界——泛)、简单的碰撞引擎、物理引擎、画布上网格显示、画布上或者界面中的数据显示(时间) 、数据的自动更新(时间)、
一、鼠标在界面中的定位
我找到三个 pyautogui,pynput,普通的tkinter。
pyautogui
pip install pyautogui
import pyautogui as pag
try:
while True:
print("Press Ctrl-C to end")
screenWidth, screenHeight = pag.size() # 获取屏幕的尺寸
x, y = pag.position() # 返回鼠标的坐标
print("Screen size: (%s %s), Position : (%s, %s)\n" % (screenWidth, screenHeight, x, y)) # 打印坐标
time.sleep(1) # 每个1s中打印一次 , 并执行清屏
except KeyboardInterrupt:
print('end')
pyautogui的文档 https://pyautogui.readthedocs.io/en/latest/
问题移动非常快会缺失,原因未知,待研究。
猜测,可能是因为移动速度大于运算处理速度???
不过问题应该不大,控制一下刷新频率就行,反正人眼频率分辨率很低,两三百撑死了。
游戏的话,也还好。
(截图数据都是我刻意找到的,平常很正常)
pynput
官方文档地址
https://pynput.readthedocs.io/en/latest/mouse.html#pynput.mouse.Controller.move
tkinter
居然还是bind!!!!!!!!!!
这个不错的样子
而且不局限于画布,只要界面大小没锁死,拉伸以后还可以,而且不能越界。
(不过刷新频率不确定)
- 如图所示,移动非常快会缺失,原因未知,待研究。可能和window.mainloop()循环有关。
#读取当前鼠标位置 界面里的位置 像素点
def b(event):
print(event.x,event.y)
window.bind('<Motion>',b)
关于pyautogui和tkinter 鼠标位置刷新速度的的实验(续 )注:不完整
一开始生成,点击左键后,开始绘画鼠标在画布上的轨迹,像素点,单位1。
如图绘画情况,移速满则密集,快则疏松。
示意代码,但是不知道为啥,一次点击后,b就一直运行?加了一次if条件没啥用。
是不是得再设个全局变量,就是点击一次,bb里记个数,加一。b里面这个数 if == 1就运行画画,然后画完 这个变量减一。
#读取当前鼠标位置 相对位置
def b(ev):
line = canvas.create_line(ev.x, ev.y, ev.x+1, ev.y+1,fill='yellow')
def bb_(evv):
if evv.num == 1:
window.bind('<Motion>',b)
window.bind("<Button-1>",bb_)
确实,按照上面的说法试了一下,ok就是点一下画一个点。还能再改吧,或者查查有没有其他方式,不过我记得脚本的一些库中,有判断是否鼠标按下,有没有抬起等等的功能,用到再查一下。
pyautogui里好像就有。
ma = 0
#读取当前鼠标位置 相对位置
def b(ev):
global ma
if ma == 1:
ma = ma -1
line = canvas.create_line(ev.x, ev.y, ev.x+1, ev.y+1,fill='yellow')
def bb_(evv):
global ma
window.bind('<Motion>',b)
ma = ma +1
window.bind("<Button-1>",bb_)
反正也写了,顺便看看pyautogui
好吧,似乎差不多。
不过如果是画布,得设置一下比例相对位置什么的,因为这个返回的坐标是电脑屏幕的。
代码差不多。
#读取当前鼠标位置 据对位置
def b(ev):
e_x, e_y = pag.position() # 返回鼠标的坐标
line = canvas.create_line(e_x, e_y, e_x+1, e_y+1,fill='yellow')
def bb_(evv):
window.bind('<Motion>',b)
window.bind("<Button-1>",bb_)
感觉 似乎 对吧 电脑自带的画图,再加加改改,搞俩按钮菜单,做个数据保存打开什么的,也能做?!!! 就做出来了?
二、画布中组件的定位
思路
想法 一种是笨办法,建个三维数组,反正是画网格,差不多。然后二维是对应显示器坐标嘛。再有一维就是标记。各种标记。
或者字典??字典好像可以干这个,我等等查查吧。
那么ok,现在得想着,按标记画图,这样才好确定啥是啥,画图的时候就记下标记,感觉省事。
place 绝对位置——参数:横坐标x,纵坐标y
关于画图 有绘图库Turtle。
#画一个像素块 给起始位置,向正方向画图,(目前多数是屏幕右下)画布的相对位置,左上角是0,0 默认黄色
def paint_point(x,y,size):
line = canvas.create_line(x, y, x+size, y+size,fill='yellow')
#这个是测试了一下,返回参数的情况,想看看如果line画完,外部能不能记录。
def aaa():
return 8
x = aaa()
print (x)
然后 我记得好像可以标记,就是本身有,查了查 欧克,get。
canvas(...)
image = canvas.create_image(250, 0, anchor='n',image=image_file)
line = canvas.create_line(2, 2, 3,3,fill='yellow',tag = 'g1')
canvas.pack()
#全部的列出来 1,2。。。。 注意 这个的运行速度很慢很慢!!!!!!!
print(canvas.find_all())
# 访问图形项的id,也就是编号、生成的顺序
print(line) # 2
# 根据指定tag该tag对应的所有图形项 即编号第几个
print(canvas.find_withtag('g1')) # (2)
canvas.dtag(1, 't1') # 删除id为1的图形项上名为t1的tag
canvas.dtag(line, 'g1') # 删除id为line的图形项上名为g1的tag
canvas.delete(line) # 删除对应的图形,可以是前面的变量名、标签、id等等
再回顾一下思路,先标记识别(后端,不是鼠标指,可以但是目前好像不需要)区分图形,可以选择图形,删除图形。然后要可以查找图形,进一步要判断图形的位置。
.clear()
clear方法清除字典中所有的项。这个是原地操作(类似于list.sort),所以无返回值(或返回None)
deque队列
try except 语句的执行流程如下:
首先执行 try 中的代码块,如果执行过程中出现异常,系统会自动生成一个异常类型,并将该异常提交给 Python 解释器,此过程称为捕获异常。
当 Python 解释器收到异常对象时,会寻找能处理该异常对象的 except 块,如果找到合适的 except 块,则把该异常对象交给该 except 块处理,这个过程被称为处理异常。如果 Python 解释器找不到处理异常的 except 块,则程序运行终止,Python 解释器也将退出。
try:
可能产生异常的代码块
except [ (Error1, Error2, ... ) [as e] ]:
处理异常的代码块1
except [ (Error3, Error4, ... ) [as e] ]:
处理异常的代码块2
except [Exception]:
处理其它异常
该格式中,[] 括起来的部分可以使用,也可以省略。其中各部分的含义如下:
(Error1, Error2,...) 、(Error3, Error4,...):其中,Error1、Error2、Error3 和 Error4 都是具体的异常类型。显然,一个 except 块可以同时处理多种异常。
[as e]:作为可选参数,表示给异常类型起一个别名 e,这样做的好处是方便在 except 块中调用异常类型。
[Exception]:作为可选参数,可以代指程序可能发生的所有异常情况,其通常用在最后一个 except 块。
三、附录
知识再来一遍
background(bg) 背景色;
foreground(fg) 前景色;
borderwidth 组件边框宽度;
width 组件宽度;
height 高度;
bitmap 位图;
image 图片;
绘图:
create_arc 圆弧;
create_bitmap 绘制位图,支持XBM;
create_image 绘制图片,支持GIF(x,y,image,anchor);
create_line 绘制支线;
create_oval; 绘制椭圆;
create_polygon 绘制多边形(坐标依次罗列,不用加括号,还有参数,fill,outline);
create_rectangle 绘制矩形((a,b,c,d),值为左上角和右下角的坐标);
create_text 绘制文字(字体参数font,);
create_window 绘制窗口;
delete 删除绘制的图形;
itemconfig 修改图形属性,第一个参数为图形的ID,后边为想修改的参数;
move 移动图像(1,4,0),1为图像对象,4为横移4像素,0为纵移像素,然后用
root.update()刷新即可看到图像的移动,为了使多次移动变得可视,最好加上time.sleep()函数;
只要用create_方法画了一个图形,就会自动返回一个ID,创建一个图形时将它赋值给一个变量,需要ID
时就可以使用这个变量名。
coords(ID) 返回对象的位置的两个坐标(4个数字元组);
对于按钮组件、菜单组件等可以在创建组件时通过command参数指定其事件处理函数。方法为bind;或者
用bind_class方法进行类绑定,bind_all方法将所有组件事件绑定到事件响应函数上。
复制代码
10、菜单Menu
复制代码
参数:
tearoff 分窗,0为在原窗,1为点击分为两个窗口
bg,fg 背景,前景
borderwidth 边框宽度
font 字体
activebackgound 点击时背景,同样有activeforeground,activeborderwidth,disabledforeground
cursor
postcommand
selectcolor 选中时背景
takefocus
title
type
relief
方法:
menu.add_cascade 添加子选项
menu.add_command 添加命令(label参数为显示内容)
menu.add_separator 添加分隔线
menu.add_checkbutton 添加确认按钮
delete 删除
复制代码
11、事件关联
复制代码
bind(sequence,func,add)——
bind_class(className,sequence,func,add)
bind_all(sequence,func,add)
事件参数:
sequence 所绑定的事件;
func 所绑定的事件处理函数;
add 可选参数,为空字符或‘+’;
className 所绑定的类;
鼠标键盘事件
<Button-1> 鼠标左键按下,2表示中键,3表示右键;
<ButtonPress-1> 同上;
<ButtonRelease-1> 鼠标左键释放;
<B1-Motion> 按住鼠标左键移动;
<Double-Button-1> 双击左键;
<Enter> 鼠标指针进入某一组件区域;
<Leave> 鼠标指针离开某一组件区域;
<MouseWheel> 滚动滚轮;
<KeyPress-A> 按下A键,A可用其他键替代;
<Alt-KeyPress-A> 同时按下alt和A;alt可用ctrl和shift替代;
<Double-KeyPress-A> 快速按两下A;
<Lock-KeyPress-A> 大写状态下按A;
窗口事件
Activate 当组件由不可用转为可用时触发;
Configure 当组件大小改变时触发;
Deactivate 当组件由可用转变为不可用时触发;
Destroy 当组件被销毁时触发;
Expose 当组件从被遮挡状态中暴露出来时触发;
Unmap 当组件由显示状态变为隐藏状态时触发;
Map 当组件由隐藏状态变为显示状态时触发;
FocusIn 当组件获得焦点时触发;
FocusOut 当组件失去焦点时触发;
Property 当窗体的属性被删除或改变时触发;
Visibility 当组件变为可视状态时触发;
响应事件
event对象(def function(event)):
char 按键字符,仅对键盘事件有效;
keycode 按键名,仅对键盘事件有效;
keysym 按键编码,仅对键盘事件有效;
num 鼠标按键,仅对鼠标事件有效;
type 所触发的事件类型;
widget 引起事件的组件;
width,heigh 组件改变后的大小,仅Configure有效;
x,y 鼠标当前位置,相对于窗口;
x_root,y_root 鼠标当前位置,相对于整个屏幕
复制代码
12、弹窗
复制代码
messagebox._show函数的控制参数:
default 指定消息框按钮;
icon 指定消息框图标;
message 指定消息框所显示的消息;
parent 指定消息框的父组件;
title 标题;
type 类型;
simpledialog模块参数:
title 指定对话框的标题;
prompt 显示的文字;
initialvalue 指定输入框的初始值;
filedialog 模块参数:
filetype 指定文件类型;
initialdir 指定默认目录;
initialfile 指定默认文件;
title 指定对话框标题
colorchooser模块参数:
initialcolor 指定初始化颜色;
title 指定对话框标题;
复制代码
13、字体(font)
一般格式:
('Times -10 bold')
('Times',10,'bold','italic') 依次表示字体、字号、加粗、倾斜
补充:
config 重新配置
label.config(font='Arial -%d bold' % scale.get())
依次为字体,大小(大小可为字号大小),加粗
tkinter.StringVar 能自动刷新的字符串变量,可用set和get方法进行传值和取值,类似的
还有IntVar,DoubleVar...
sys.stdout.flush() 刷新输出
然后 (这个的画布是很全的http://www.uml.org.cn/python/201912161.asp)
超全的画布标签操作!!!
# 绘制一个矩形框
rt = canvas.create_rectangle(40, 40, 300, 220,
outline='blue', width=2,
tag = ('t1', 't2', 't3', 'tag4')) # 为该图形项指定标签
# 访问图形项的id,也就是编号
print(rt) # 1
# 绘制一个椭圆
oval = canvas.create_oval(350, 50, 580, 200,
fill='yellow', width=0,
tag = ('g1', 'g2', 'g3', 'tag4')) # 为该图形项指定标签
# 访问图形项的id,也就是编号
print(oval) # 2
# 根据指定tag该tag对应的所有图形项
print(canvas.find_withtag('tag4')) # (1, 2)
# 获取指定图形项的所有tag
print(canvas.gettags(rt)) # ('t1', 't2', 't3', 'tag4')
print(canvas.gettags(2)) # ('g1', 'g2', 'g3', 'tag4')
canvas.dtag(1, 't1') # 删除id为1的图形项上名为t1的tag
canvas.dtag(oval, 'g1') # 删除id为oval的图形项上名为g1的tag
# 获取指定图形项的所有tag
print(canvas.gettags(rt)) # ('tag4', 't2', 't3')
print(canvas.gettags(2)) # ('tag4', 'g2', 'g3')
# 为所有图形项添加tag
canvas.addtag_all('t5')
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5')
# 为指定图形项添加tag
canvas.addtag_withtag('t6', 'g2')
# 获取指定图形项的所有tag
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6')
# 为指定图形项上面的图形项添加tag, t2上面的就是oval图形项
canvas.addtag_above('t7', 't2')
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7')
# 为指定图形项下面的图形项添加tag, g2下面的就是rt图形项
canvas.addtag_below('t8', 'g2')
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7')
# 为最接近指定点的图形项添加tag,最接近360、90的图形项是oval
canvas.addtag_closest('t9', 360, 90)
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9')
# 为位于指定区域内(几乎覆盖整个图形区)的最上面的图形项添加tag
canvas.addtag_closest('t10', 30, 30, 600, 240)
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9', 't10')
# 为与指定区域内重合的最上面的图形项添加tag
canvas.addtag_closest('t11', 250, 30, 400, 240)
print(canvas.gettags(1)) # ('tag4', 't2', 't3', 't5', 't8')
print(canvas.gettags(oval)) # ('tag4', 'g2', 'g3', 't5', 't6', 't7', 't9', 't10', 't11')
图型操作!!!
from tkinter import *
from tkinter import colorchooser
import threading
# 创建窗口
window = Tk()
window.title('操作图形项')
# 创建并添加Canvas
canvas = Canvas(window, background='white', width=400, height=350)
canvas.pack(fill=BOTH, expand=YES)
# 该变量用于保存当前选中的图形项
current = None
# 该变量用于保存当前选中的图形项的边框颜色
current_outline = None
# 该变量用于保存当前选中的图形项的边框宽度
current_width = None
# 该函数用于高亮显示选中图形项(边框颜色会red、yellow之间切换)
def show_current():
# 如果当前选中项不为None
if current is not None:
# 如果当前选中图形项的边框色为red,将它改为yellow
if canvas.itemcget(current, 'outline') == 'red':
canvas.itemconfig(current, width=2,
outline='yellow')
# 否则,将颜色改为red
else:
canvas.itemconfig(current, width=2,
outline='red')
global t
# 通过定时器指定0.2秒之后执行show_current函数
t = threading.Timer(0.2, show_current)
t.start()
# 通过定时器指定0.2秒之后执行show_current函数
t = threading.Timer(0.2, show_current)
t.start()
# 分别创建矩形、椭圆、和圆
rect = canvas.create_rectangle(30, 30, 250, 200,
fill='magenta', width='0')
oval = canvas.create_oval(180, 50, 380, 180,
fill='yellow', width='0')
circle = canvas.create_oval(120, 150, 300, 330,
fill='pink', width='0')
bottomF = Frame(window)
bottomF.pack(fill=X,expand=True)
liftbn = Button(bottomF, text='向上',
# 将椭圆移动到它上面的item之上
command=lambda : canvas.tag_raise(oval, canvas.find_above(oval)))
liftbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
lowerbn = Button(bottomF, text='向下',
# 将椭圆移动到它下面的item之下
command=lambda : canvas.tag_lower(oval, canvas.find_below(oval)))
lowerbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
def change_fill():
# 弹出颜色选择框,让用户选择颜色
fill_color = colorchooser.askcolor(parent=window,
title='选择填充颜色',
# 初始颜色设置为椭圆当前的填充色(fill选项值)
color = canvas.itemcget(oval, 'fill'))
if fill_color is not None:
canvas.itemconfig(oval, fill=fill_color[1])
fillbn = Button(bottomF, text='改变填充色',
# 该按钮触发change_fill函数
command=change_fill)
fillbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
def change_outline():
# 弹出颜色选择框,让用户选择颜色
outline_color = colorchooser.askcolor(parent=window,
title='选择边框颜色',
# 初始颜色设置为椭圆当前的边框色(outline选项值)
color = canvas.itemcget(oval, 'outline'))
if outline_color is not None:
canvas.itemconfig(oval, outline=outline_color[1],
width=4)
outlinebn = Button(bottomF, text='改变边框色',
# 该按钮触发change_outline函数
command=change_outline)
outlinebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
movebn = Button(bottomF, text='右下移动',
# 调用move方法移动图形项
command=lambda : canvas.move(oval, 15, 10))
movebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
coordsbn = Button(bottomF, text='位置复位',
# 调用coords方法重设图形项的大小和位置
command=lambda : canvas.coords(oval, 180, 50, 380, 180))
coordsbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
# 再次添加Frame容器
bottomF = Frame(window)
bottomF.pack(fill=X,expand=True)
zoomoutbn = Button(bottomF, text='缩小',
# 调用scale方法对图形项进行缩放
# 前面两个坐标指定缩放中心,后面两个参数指定横向、纵向的缩放比
command=lambda : canvas.scale(oval, 180, 50, 0.8, 0.8))
zoomoutbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
zoominbn = Button(bottomF, text='放大',
# 调用scale方法对图形项进行缩放
# 前面两个坐标指定缩放中心,后面两个参数指定横向、纵向的缩放比
command=lambda : canvas.scale(oval, 180, 50, 1.2, 1.2))
zoominbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
def select_handler(ct):
global current, current_outline, current_width
# 如果ct元组包含了选中项
if ct is not None and len(ct) > 0:
ct = ct[0]
# 如果current对应的图形项不为空
if current is not None:
# 恢复current对应的图形项的边框
canvas.itemconfig(current, outline=current_outline,
width = current_width)
# 获取当前选中图形项的边框信息
current_outline = canvas.itemcget(ct, 'outline')
current_width = canvas.itemcget(ct, 'width')
# 使用current保存当前选中项
current = ct
def click_handler(event):
# 获取当前选中的图形项
ct = canvas.find_closest(event.x, event.y)
# 调用select _handler处理选中图形项
select_handler(ct)
def click_select():
# 取消为“框选”绑定的两个事件处理函数
canvas.unbind('<B1-Motion>')
canvas.unbind('<ButtonRelease-1>')
# 为“点选”绑定鼠标点击的事件处理函数
canvas.bind('<Button-1>', click_handler)
clickbn = Button(bottomF, text='点选图形项',
# 该按钮触发click_select函数
command=click_select)
clickbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
# 记录鼠标拖动的第一个点的x、y坐标
firstx = firsty = None
# 记录前一次绘制的、代表选择区的虚线框
prev_select = None
def drag_handler(event):
global firstx, firsty, prev_select
# 刚开始拖动时,用鼠标位置为firstx、firsty赋值
if firstx is None and firsty is None:
firstx, firsty = event.x, event.y
leftx, lefty = min(firstx, event.x), min(firsty, event.y)
rightx, righty = max(firstx, event.x), max(firsty, event.y)
# 删除上一次绘制的虚线选择框
if prev_select is not None:
canvas.delete(prev_select)
# 重新绘制虚线选择框
prev_select = canvas.create_rectangle(leftx, lefty, rightx, righty,
dash=2)
def release_handler(event):
global firstx, firsty
if prev_select is not None:
canvas.delete(prev_select)
if firstx is not None and firsty is not None:
leftx, lefty = min(firstx, event.x), min(firsty, event.y)
rightx, righty = max(firstx, event.x), max(firsty, event.y)
firstx = firsty = None
# 获取当前选中的图形项
ct = canvas.find_enclosed(leftx, lefty, rightx, righty)
# 调用select _handler处理选中图形项
select_handler(ct)
def rect_select():
# 取消为“点选”绑定的事件处理函数
canvas.unbind('<Button-1>')
# 为“框选”绑定鼠标拖动、鼠标释放的事件处理函数
canvas.bind('<B1-Motion>', drag_handler)
canvas.bind('<ButtonRelease-1>', release_handler)
rectbn = Button(bottomF, text='框选图形项',
# 该按钮触发rect_select函数
command=rect_select)
rectbn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
deletebn = Button(bottomF, text='删除',
# 删除图形项
command=lambda : canvas.delete(oval))
deletebn.pack(side=LEFT, ipadx=10, ipady=5, padx=3)
window.mainloop()