# tkinter - 項目實戰
'''
- 項目分析
- 屏保可以自己啟動,也可以手動啟動.
- 一旦敲擊鍵盤或者移動鼠標,或者引發事件,則停止.
- 如果屏保是一幅畫的話,則沒有畫框.
- 圖像的動作是隨機的,具有隨機性.(包括顏色,大小,運動方向,變形)
- 整個世界的構成是
- ScreenSaver
- 需要一個canvas. 大小等於屏幕大小,沒有邊框.
- Ball
- 包括顏色,大小,運動方向,變形 => 隨機
- 球能動, 可以被調用.
'''
import tkinter as tk
import random
class RandomBall:
'''
定義一個運動球的類
'''
def __init__(self,canvas,scrnWidth,scrnHeight):
'''
canvas:畫布,所有的內容都應該在畫布上呈現出來.此處通過此變量傳入
scrnWidth/scrnheigh:屏幕寬高
'''
self.canvas = canvas # 這個是自己添加的.添加原因是下方代碼並沒有 self.canvas
# 球出現的初始位置要隨機.此位置表示圓心
# xpos表示位置的x坐標
self.xops = random.randint(10,int(scrnWidth)-20)
# ypos表示位置y的坐標
self.yops = random.randint(10,int(scrnHeight)-20)
# 定義球運動的速度 => 模擬運動: 動畫的邏輯就是重繪 =>模擬運動,不斷的擦掉舊事物,重新繪制新事物
self.xvelocity = random.randint(4,20)
self.yvelocity = random.randint(4,20)
# 定義屏幕的寬度和高度
self.scrnWidth = scrnWidth
self.scrnHeight = scrnHeight
# 球的大小隨機.
# 此處球的大小用半徑表示
self.radius = random.randint(20,80)
# 定義顏色
# RGB表示法:三個數字,每個數字的值是0-255之間,表示RGB
c = lambda:random.randint(0,255)
self.color = '#%02x%02x%02x'%(c(),c(),c()) # 這個地方存疑
def create_ball(self):#定義一個函數
'''
用構造函數定義的變量值,在canvas上畫一個球
'''
# tkinter沒有畫圓形的函數
# 只有一個畫橢圓函數,畫橢圓需要定義兩個坐標
# 在一個長方形內畫橢圓,我們只需要定義長方形左上角和右下角就好 => 如果是一個正方形,那么橢圓就是圓
# 求兩個坐標的方法是 (圓心 - 半徑)
x1 = self.xops - self.radius
y1 = self.yops - self.radius # 橢圓的左上角
x2 = self.xops + self.radius
y2 = self.yops + self.radius # 橢圓的右下角
self.item = self.canvas.create_oval(x1,y1,x2,y2,fill=self.color,outline=self.color) # 繪制橢圓
def move_ball(self):
# 移動球的時候,控制球的方向
# 每次移動后,球都有一個新的坐標
self.xops += self.xvelocity
self.yops += self.yvelocity
# 以下判斷是否會撞牆 => 撞牆的算法判斷
if self.xops +self.radius>=self.scrnWidth: #撞到右邊的牆了
self.xvelocity*=-1
elif self.xops+self.radius<=0:
self.xvelocity*=-1
if self.yops + self.radius >=self.scrnHeight:
self.yvelocity*=-1
elif self.yops +self.radius <= 0:
self.yvelocity *=-1
# 在畫布上移動動畫
self.canvas.move(self.item,self.xvelocity,self.yvelocity)
class ScreenSaver:
'''
定義屏保的類
可以被啟動
'''
# 如何裝隨機產生的球
balls = list()
def __init__(self):
self.num_balls = random.randint(6,20)
self.root = tk.Tk()
# 取消邊框
self.root.overrideredirect(1)
# 任何鼠標移動都要取消
self.root.bind('<Motion>',self.myquit)
# 同理,安東任何鍵盤都需要退出屏保
# 得到 屏幕大小規格
w,h = self.root.winfo_screenwidth(),self.root.winfo_screenheight()
self.canvas = tk.Canvas(self.root,width=w,height=h) # 畫布的歸屬,規格
self.canvas.pack() # 布局
# 在畫布上畫球
for i in range(self.num_balls):
ball = RandomBall(self.canvas,scrnWidth=w,scrnHeight=h)
ball.create_ball()
self.balls.append(ball)
self.run_screen_saver()
self.root.mainloop() # 啟動消息循環
def run_screen_saver(self):
for ball in self.balls:
ball.move_ball()
# 系統函數 /after是200毫秒后啟動一個函數,需要啟動的函數是第二個參數
self.canvas.after(20, self.run_screen_saver)
def myquit(self,e):
# 此處只是利用了事件處理機制
# 實際上並不關心事件的類型
# 作業:
# 此屏保程序擴展成,一旦捕獲事件,則判斷屏保不退出
# 顯示一個Button,Button上顯示事件類型,點擊Button后屏保
# 才退出
self.root.destroy()
if __name__ == "__main__":
ScreenSaver()