tkinter和pymunk的Sprite类练习参考代码

tkinter和pymunk的Sprite类练习参考代码

```"""本项目暂停2019/4/17,项目的初衷是编制一个受物理引擎pymunk的Sprite类,由于种种原因,本项目可能不会再重启,代码供人参考——李兴球"""

import time
import math
import tkinter

class Vec2D(tuple):
"""2D向量类，继承于元组，提供以下的向量操作,a,b是向量，k是数字

a+b 向量加法
a-b 向量减法
a*b 向量内积
k*a and a*k 缩放向量
|a| 向量的绝对值
a.rotate(angle) 旋转向量
"""
def __new__(cls, x, y):
return tuple.__new__(cls, (x, y))
return Vec2D(self[0]+other[0], self[1]+other[1])
def __mul__(self, other):
if isinstance(other, Vec2D):
return self[0]*other[0]+self[1]*other[1]
return Vec2D(self[0]*other, self[1]*other)
def __rmul__(self, other):
if isinstance(other, int) or isinstance(other, float):
return Vec2D(self[0]*other, self[1]*other)
def __sub__(self, other):
return Vec2D(self[0]-other[0], self[1]-other[1])
def __neg__(self):
return Vec2D(-self[0], -self[1])
def __abs__(self):
return (self[0]**2 + self[1]**2)**0.5
def rotate(self, angle):
"""旋转向量
"""
perp = Vec2D(-self[1], self[0])
angle = angle * math.pi / 180.0
c, s = math.cos(angle), math.sin(angle)
return Vec2D(self[0]*c+perp[0]*s, self[1]*c+perp[1]*s)
def __getnewargs__(self):
return (self[0], self[1])
def __repr__(self):
return "(%.2f,%.2f)" % self

def negative_y_cors(points):
"""把每个点的y坐标取反"""
amounts = len(points)
points = [ (2*(1-(index%2)) -1)  * points[index] for index in range(amounts) ]
return points

class _Window(tkinter.Tk):

_screen = None
def __init__(self,width,height,title="窗口标题",bgcolor='white',background=None):
tkinter.Tk.__init__(self)

self.__bgimages = {}         # 背景字典
self.raw_title = title       # 仅记录原始标题
self.resizable(False,False)  # noresize不可变窗口大小
self.title(title)
self.width = width
self.height = height
self.canvas = tkinter.Canvas(self,width=self.width,height=self.height,bg=bgcolor,border=0 )
self.canvas.config(scrollregion=(-width//2, -height//2, width//2, height//2 ))
self.canvas.pack()
self.__blankimage = tkinter.PhotoImage(width=1, height=1) # 新建空白图形

self.__background = self.canvas.create_image(0,0,image=self.__blankimage) # 这是背景item,是个id号

self.set_background(background)

def set_background(self,image):

if image:                   # 如果image有数据并且字典中有，则直接返回，否则添加到背景字典里。
self.__background_image = self.__bgimages.setdefault(image,tkinter.PhotoImage(file=image))
else:
self.__background_image = self.__blankimage
self.canvas.itemconfig(self.__background,image=self.__background_image)

def set_bgcolor(self,color):

self.canvas.config(bg=color)

def set_width_and_height(self,width,height):

"""设置窗口画布的宽度和高度，别名是size"""

self.canvas.config(width=width,height=height)
self.canvas.config(scrollregion=(-width//2, -height//2, width//2, height//2 ))

def ontimer(self,fun,millisecond=0):
"""定时器，在一定时间后再次调用fun,时间单位为毫秒"""
if millisecond == 0:
self.canvas.after_idle(fun)
else:
self.canvas.after(millisecond, fun)

# 定义别名
size = set_width_and_height
background = set_background
bgcolor = set_bgcolor

class Sprite():
def __init__(self):
if _Window._screen is None:
_Window._screen = create_window(480,360)
self.canvas = _Window._screen.canvas
self.dx = 0           # 代表水平速度
self.dy = 0           # 代表垂直速度
self.angle = 0        # 角度
self.itemid = 0       # 角色id号,具体在子类中的canvas分配

def move(self):

self.canvas.move(self.itemid,self.dx,-self.dy)

def bbox(self):

x1,y1,x2,y2 =  self.canvas.bbox(self.itemid)
return x1,-y1,x2,-y2

def coords(self,position=None):
"""返回形状的两点坐标或设置坐标"""
if position == None:
points =  self.canvas.coords(self.itemid)
return negative_y_cors(points)
else:
self.canvas.coords(self.itemid,negative_y_cors(position))

def show(self):
"""显示出来"""
self.canvas.itemconfig(self.itemid,state='normal')

def hide(self):
"""隐藏"""
self.canvas.itemconfig(self.itemid,state='hidden')

def point_list(self):
"""返回得到的每个顶点列表，旋转形状需要获取形状的每个顶点坐标。"""

def get_center(self):
points = self.coords()
amounts = len(points)
all_x = [points[i] for i in range(0,amounts,2)]
all_y = [points[i] for i in range(1,amounts,2)]
self._left = min(all_x)
self._right = max(all_x)
self._top = max(all_y)
self._bottom = min(all_y)
self._centerx = (self._left + self._right)/2.0
self._centery = (self._bottom + self._top)/2.0
return points

def rotate(self,angle):

points = self.get_center()   # 获取多边形的每一个顶点和得到中心点坐标
print("点们：",points)       # 获取到的是这样的列表： [-50.0, 25.0, 50.0, -25.0]
amounts = len(points)
points = [ (points[i],points[i+1]) for i in range(0,amounts,2) ]
x = self._centerx
y = self._centery
print("中心点：",x,y)
points = [ Vec2D(point[0]-x,point[1]-y) for point in points]           # 转变成向量
points = [ point.rotate(angle) for point in points]    # 旋转
points = [ (point[0]+x,point[1]+y) for point in points]    # 转换回元组
points_y = [ negative_y_cors(point) for point in points] # 把每个点的y坐标取负

##        if self.canvas.type(self.itemid) in ["arc","oval","rectangle"]:
##            self.canvas.coords(self.itemid, *points)
##
##        elif self.canvas.type(self.itemid) in ["polygon"]:
p = []
for point in points_y:
p.append(point[0])
p.append(point[1])
print(p)
self.canvas.coords(self.itemid,p)

self.angle = angle

self.angle = self.angle + delta_angle
self.angle = self.angle % 360
print("角度=",self.angle)
self.rotate(self.angle)

class Line(Sprite):

def __init__(self,pointlist,color="blue",width=1,capstyle='round',joinstyle='round'):
"""画直线，pointlist：坐标点列表，color：颜色，width：笔迹宽度，
capstyle：末端风格，值为'round'或'butt'或'projecting'
joinstyle：连接风格，值为'round'、'bevel'、'miter' 。
"""
Sprite.__init__(self)
pointlist = negative_y_cors(pointlist)
self.itemid = self.canvas.create_line(pointlist,fill=color,width=width,capstyle=capstyle,joinstyle=joinstyle)

class Polygon(Sprite):
def __init__(self,pointlist,color="blue"):
Sprite.__init__(self)
pointlist = negative_y_cors(pointlist)
self.itemid = self.canvas.create_polygon(pointlist,fill=color,outline=color,width=0)

class Arc(Sprite):
def __init__(self,bbox,color="blue",start=0.0,end=90.0,style='pieslice'):
"""画扇形,弦形,和弧形,bbox:绑定盒,color:颜色,start:起始角度,end:结束角度,style:样式(pieslice、chord、arc)
tkinter.PIESLICE -> 'pieslice'扇, tkinter.CHORD ->'chord'弦, tkinter.ARC ->'arc'弧
"""
Sprite.__init__(self)
bbox = negative_y_cors(bbox)
self.itemid = self.canvas.create_arc(bbox ,fill=color,outline=color,width=0,start=start,extent=end,style=style)

class Rectangle(Sprite):
def __init__(self,bbox,color="blue"):
Sprite.__init__(self)
bbox = negative_y_cors(bbox)
self.itemid = self.canvas.create_rectangle(bbox ,fill=color,outline=color,width=0)

class Circle(Sprite):
def __init__(self,bbox,color="blue"):
Sprite.__init__(self)
bbox = negative_y_cors(bbox)
self.itemid = self.canvas.create_oval(bbox,fill=color,outline=color)

class Text(Sprite):
def __init__(self,position,string,color="blue",font=("黑体",24,"normal")):
Sprite.__init__(self)
self.itemid = self.canvas.create_text(position,text=string,fill=color,font=font)

def create_window(width,height,title="窗口标题",bgcolor='white',background=None):

if _Window._screen is None:
_Window._screen = _Window(width,height,title=title,bgcolor=bgcolor,background=background)

return _Window._screen

if __name__ == "__main__":

def display_cursor(event):
"""在标题栏里显示标题"""
x = window.canvas.canvasx(event.x)
y = -window.canvas.canvasy(event.y)
window.title("(" + str(x) + "," + str(y) + ")_" + window.raw_title)

背景表 = ["images/" + str(i) + ".png"  for i in range(40)]
amounts = len(背景表)

window = create_window(480,360,title="窗口标题",bgcolor='cyan',background='background.png')

window.canvas.bind("<Motion>",display_cursor)
window.size(800,600)
index = 0
def alt_background():
global index
window.background(背景表[index])
index = index + 1
index = index % amounts
window.ontimer(alt_background,10)

alt_background()
window.canvas.bind("<Button-1>",lambda event:window.destroy())

```