當前位置:首頁 ? tkinter ? 正文

tkinter的Canvas組件,講解演示動畫知識初步

動畫基本方法:

Canvas組件,我們可以畫線,畫圓,繪圖等,怎樣讓這些線,圓,圖這些畫布的組件動起來呢,目前有3個方法:coords() , itemconfig() , move() ,下面,我就來講解一下這3種方法。

 

move ( 對象ID , X方面移動距離,  Y方向移動距離)        

對象ID:當你在畫布上創建線,圓,圖等對象時,系統會為這些對象分配一個ID號,第一個創建的對象分配的ID號是1,第2個創建的對象分配的ID號是2,以此類推。 

from tkinter import *
import time
 
root=Tk()
 
can1 = Canvas(root,width=300, height=200,background='white') # 白底畫布
can1.pack(fill=BOTH,expand=True)
 
can1.create_line(60,20,200,20) # 畫線
can1.create_oval(60,50,80,70,outline='red',fill='blue') # 畫圓
img1=PhotoImage(file='16-1.png')
can1.create_image(60,120,image=img1) # 繪圖
 
for i in range(50):    # 移動50步
    can1.move(1,2,3)   # ID為1,每步:X方向右移動2像素,Y方向不移動
    can1.move(2,2,3)  # ID為2,每步:X方向左移動2像素,Y方向不移動
    can1.move(3,2,3)   # ID為3,每步:X方向移動2像素,Y像素移動3像素
    # can1.move(1,2,0)    # ID為1,線,向左移動
    # can1.move(2,-2,0)   # ID為2,圓,向右移動
    # can1.move(3,-2,-3)  # ID為3,圖,向右上角移動
    can1.update()     # 刷新畫布
    time.sleep(0.05)  # 時間暫停
 
root.mainloop()

 上面的代碼,在畫布上創建3個對象:線,圓,圖片(它們的ID號,系統會根據創建時間的先后,分配的ID號分別是1,2,3),然后通過循環50次,讓3個對象,在每一次循環時,X方面向左移動2個像素,Y方向向下移動3個像素。

 

運行結果:

   11.PNG

 

在用move() 方法移動畫布上的對象后,一定要加上下面2句代碼:

can1.update() # 不加這條代碼,畫布不刷新,我們看不到對象的移動

time.sleep(0.05) # 不暫停一下,我們的眼睛看不到移動過程,由于移動速度太快,只要看到對象最終的移動位置。

 

移動方向說明:

move ( 對象ID , X方面移動距離,  Y方向移動距離)

 

X方面移動距離:為正值是向右移動,為負值是向左移動,為0不移動

Y方面移動距離:為正值是向下移動,為負值是向上移動,為0不移動

 

 

所以,上面的代碼,也可以讓線向右移動,圓向下移動,QQ圖向右上角方向移動。只要代碼改成:

can1.move(1,2,0)    # ID為1,線,向左移動
can1.move(2,-2,0)   # ID為2,圓,向右移動
can1.move(3,-2,-3)  # ID為3,圖,向右上角移動

我們也可以為對象ID號起個自己滿意的名字,只要在畫面上創建線,圓,圖片時,賦值給一個變量,這個變量就是這個對象的ID號,下面以代碼來說明,下面的代碼,我在畫線,畫圓,繪圖的創建代碼中,我把返回值分別賦值給AAA,BBB,CCC,這樣,在后的move()方法中,我們就可以用AAA代替1這個ID號,BBB代替2這個ID號,CCC代替3這個ID號(當然我們也可以繼續沿用以后的數字ID號)。

from tkinter import *
import time
 
root=Tk()
 
can1 = Canvas(root,width=300, height=200,background='white') # 白底畫布
can1.pack(fill=BOTH,expand=True)
 
AAA=can1.create_line(60,20,200,20) # 畫線
BBB=can1.create_oval(60,50,80,70,outline='red',fill='blue') # 畫圓
img1=PhotoImage(file='16-1.png')
CCC=can1.create_image(60,120,image=img1) # 繪圖
 
for i in range(50):    # 移動50步
    can1.move(AAA,2,3)   # ID為1,每步:X方向右移動2像素,Y方向不移動
    can1.move(BBB,2,3)  # ID為2,每步:X方向左移動2像素,Y方向不移動
    can1.move(CCC,2,3)   # ID為3,每步:X方向移動2像素,Y像素移動3像素
    can1.update()     # 刷新畫布
    time.sleep(0.05)  # 時間暫停
 
root.mainloop()


下面我來講一下coords()方法:

語法:coords(ID號,坐標值)

 

當沒有坐標值這參數時,返回值是畫布對象的坐標值,但是要注意,返回的坐標值對于不同的畫布對象,返回的坐標值是不一樣的。返回的坐標值是一個元組值,但返回的元組的元素不一樣,有的是2個元素,有的是4個元素。

比如:上面的代碼,我在最后加入代碼:

print(can1.coords(AAA)) # 返回線的坐標
print(can1.coords(BBB)) # 返回圓的坐標
print(can1.coords(CCC)) # 返回圖的坐標

運行后,輸出:

[160.0, 170.0, 300.0, 170.0]

[160.0, 200.0, 180.0, 220.0]

[160.0, 270.0]

 

可以看出,圖的坐標是2個元素的元組,而線,圓的坐標是4個元素的元組,這其實好理解,也好記憶,因為在畫布上創建它們時,他們的坐標就分別是4個元素或2個元組。

 

如果在 coords(ID號,坐標值) 2個參數都提供,那么可以移動畫布對象到坐標值的位置,這個坐標值參數提供的個數跟它們在畫布創建時提供的坐標個數是一致的,像繪制線條,繪制圓就要提供 x1,y1,x2,y3 4個坐標參數,而在畫布上插入圖片則只要提供 x1,y1 2個坐標參數就行了。

 

下面的代碼,用 coords()方法實現跟move()方法一樣的效果。

from tkinter import *
import time
 
root=Tk()
 
can1 = Canvas(root,width=300, height=200,background='white') # 白底畫布
can1.pack(fill=BOTH,expand=True)
 
AAA=can1.create_line(60,20,200,20) # 畫線
BBB=can1.create_oval(60,50,80,70,outline='red',fill='blue') # 畫圓
img1=PhotoImage(file='16-1.png')
CCC=can1.create_image(60,120,image=img1) # 繪圖
 
for i in range(50):    # 移動50步
    wz1=can1.coords(AAA) # 獲取線條目前的位置(x1,y1,x2,y2)
    can1.coords(AAA,wz1[0]+3,wz1[1]+3,wz1[2]+3,wz1[3]+3)
    # 在線條坐標的基礎上,分別+3個像素,讓線條向右下角移動
 
    wz2=can1.coords(BBB) # 獲取小球目前的位置(x1,y1,x2,y2)
    can1.coords(BBB,wz2[0]+3,wz2[1]+3,wz2[2]+3,wz2[3]+3)
    # 在小球坐標的基礎上,分別+3個像素,讓小球向右下角移動
 
    wz3=can1.coords(CCC) # 獲取圖片目前的位置(x1,y1)
    can1.coords(CCC,wz3[0]+3,wz3[1]+3)
    # 在圖片坐標的基礎上,分別+3個像素,讓圖片向右下角移動
 
    can1.update()     # 刷新畫布
    time.sleep(0.05)  # 時間暫停
 
root.mainloop()


運行結果是一樣的。運行結果:

202111151542442469369.png

 

上面的代碼是通過循環讓3個畫布對象向右下角方向移動,下面我來講一下如何用鼠標移動這3個畫布對象。

 

首先要遇到的問題是,我們如何指定要操作的畫布對象呢?Canvas組件提供了幾種方法讓我們來指定要操作的畫布對象:

1.  對象ID:如系統自動分配的ID1、2、3 還有我們自定義的IDAAA,BBB,CCC

2.  tags 為畫布對象起的標簽名。(下面我會講解)

3.  ‘all’ :   表示Canvas組件中的所有畫布對象,系統內置標簽

4.  ‘current’ :  表示鼠標指針下的畫布對象,,系統內置標簽

 

其次要遇到問題是,我們如何操作這些畫布對象呢?Canvas組件提供了跟組件事件綁定一樣的方法:tags_bind(),方法里的參數跟組件的事件綁定類似,看下面的代碼,你一看便知。

can1.tag_bind(AAA,"<B1-Motion>",test1)

這條代碼,第1個參數,可以用AAA來指定操作的畫布對象,也可以用ID號:1來指定操作對象,如果在創建畫布對象時,為畫布對象指定了一個標簽tags,我們也可以用標簽來代替AAA,由于是用鼠標點擊拖動這個對象,所以,我們也可以用系統內置標簽‘current’來代替。

 

下面的代碼,我以上面的代碼為基礎,在創建畫布對象時,我們加入參數tags,分別為3個畫布對象起了3個標簽:’t1’ , ‘t2’, ‘t3’

 
from tkinter import *
 
root=Tk()
 
can1 = Canvas(root,width=300, height=200,background='white') # 白底畫布
can1.pack(fill=BOTH,expand=True)
 
AAA=can1.create_line(60,20,200,20,tags='t1') # 畫線 加入tags參數
BBB=can1.create_oval(60,50,80,70,outline='red',fill='blue',tags='t2') # 畫圓
img1=PhotoImage(file='16-1.png')
CCC=can1.create_image(60,120,image=img1,tags='t3') # 繪圖
 
def test1(event):
    can1.coords(AAA,event.x-70,event.y,event.x+70,event.y)  # 用AAA指定對象
 #  can1.coords(1,event.x-70,event.y,event.x+70,event.y)    # 用1指定對象也可以
 #  can1.coords('t1',event.x-70,event.y,event.x+70,event.y) # 用't1' 指定對象也可以
def test2(event):
    can1.coords(BBB,event.x-10,event.y-10,event.x+10,event.y+10) # 用coords方法讓對象跟鼠標坐標一同變化
def test3(event):
    can1.coords(CCC,event.x,event.y) # 圖片坐標只有2個參數,上面的線,小圓有4個參數
 
can1.tag_bind(AAA,"<B1-Motion>",test1) # 線條 鼠標按下移動事件 綁定函數test1
can1.tag_bind(BBB,"<B1-Motion>",test2) # 小圓 鼠標按下移動事件 綁定函數test2
can1.tag_bind(CCC,"<B1-Motion>",test3) # 圖片 鼠標按下移動事件 綁定函數test3
 
root.mainloop()

 上面代碼,3個畫布對象分別綁定“按住鼠標左鍵移動”事件,在綁定的函數里,用coords( 畫布對象ID,對象要移動的目的坐標 ) 來移動畫布對象。

 

運行后,用鼠標按住一個對象,可以把對象拖動到畫布上的任何一個位置。

202111151546159197951.png

 

下面我來講一個畫布反彈小球程序,就是一個小球在畫布不斷移動,到達畫布邊緣,就像撞墻一樣,小球會反彈。下面的代碼沒有新知識,都是move()方法,coords()方法 等知識的運用。

from tkinter import *
import time
 
def ok(): # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text']='  動起來  '
    else:
        yn = True
        but1['text']='  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove(): # 小球在畫布四周不停反彈
    global x, y
    while yn: # 由yn變量來控制小球是否移動
        can1.move(xq, x, y)         # 初始化,小球向右下角移動
        weizhi = can1.coords(xq)    # 獲取小球的位置,一個4個元素的元組 
 
        if weizhi[0] <= 0:       # 偵測球是否超過畫布左方
            x = step
        if weizhi[1] <= 0:       # 偵測球是否超過畫布上方
            y = step
        if weizhi[2] >= can1width:     # 偵測球是否超過畫布右方
            x = -step
        if weizhi[3] >= can1height:    # 偵測球是否超過畫布下方
            y = -step
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
root = Tk()
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 3        # 小于移動的步長,3個像素
x,y=3,3         # 小球移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
 
can1 = Canvas(root, background='lightblue', width=can1width, height=can1height) # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok) # 創建按鈕
but1.pack()
 
xq = can1.create_oval(20, 20, 40, 40, fill='red', outline='green') # 繪制小球
 
def xq_move(event): # 鼠標拖動小球
    can1.coords(xq,event.x-10,event.y-10,event.x+10,event.y+10)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為10)
 
can1.tag_bind(xq,"<B1-Motion>",xq_move) # 小球,鼠標按住移動事件
 
root.mainloop()

  在上面的代碼中,weizhi = can1.coords(xq) 會得到一個元組(x1,y1,x2,y2),x1,y1這是小球外接正方形的左上角坐標,x2,y2是右下角坐標。

  x1,即weizi[0],用于判斷是否撞到畫布的左邊

  y1  weizi[1],用于判斷是否撞到畫布的上邊

  x2  weizi[2],用于判斷是否撞到畫布的右邊

  y2  weizi[3],用于判斷是否撞到畫布的下邊

 

運行上面代碼,點“動起來”可以讓球移動起來,點“暫?!笨梢宰屝∏蜢o止,主要是通過yn這個布爾變量來控制。

你也可以用鼠標拖到小球到任何一個位置,因為代碼中也有對鼠標按住移動事件的綁定。

運行結果:

13.PNG

 

上面是通過繪制一個小球來做為畫布的對象,如果是用一個圖片來做畫布對象,也來做一個撞球程序,因為怎樣做?

要注意:圖片的坐標(x1,y1)只有2個參數,在默認情況下,是圖片正中間的坐標。所以,上面的代碼就要相應做出改變。

我沒有找到合適的小球圖片,我就以一個QQ圖片(大小16X16)來代替小球。

from tkinter import *
import time
 
def ok(): # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text']='  動起來  '
    else:
        yn = True
        but1['text']='  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove(): # 小球在畫布四周不停反彈
    global x, y
    while yn: # 由yn變量業控制小球是否移動
        can1.move(xq, x, y)        # 初始化,小球向右下角移動
        weizhi = can1.coords(xq)   # 獲取小球的位置,注意,是2個元素的元組
 
        if weizhi[0] -8 <= 0:          # 偵測球是否超過畫布左方
            x = step
        if weizhi[1] -8 <= 0:          # 偵測球是否超過畫布上方
            y = step
        if weizhi[0] + 8 >= can1width:  # 偵測球是否超過畫布右方
            x = -step
        if weizhi[1] + 8 >= can1height: # 偵測球是否超過畫布下方
            y = -step
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
root = Tk()
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 3        # 小于移動的步長,3個像素
x,y=3,3         # 小球移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
 
can1 = Canvas(root, background='lightblue', width=can1width, height=can1height) # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok) # 創建按鈕
but1.pack()
 
img1=PhotoImage(file='16-1.png')
xq=can1.create_image(20,20,image=img1) # 繪制小球圖片(現暫用QQ圖片代替)
 
def xq_move(event): # 鼠標拖動小球圖片(現暫用QQ圖片代替)
    can1.coords(xq,event.x,event.y)
 
can1.tag_bind(xq,"<B1-Motion>",xq_move) # 小球(QQ圖片),鼠標按住移動事件
 
root.mainloop()

代碼中:weizhi = can1.coords(xq)  是要得到的圖片位置,這個位置是圖片的正中間,所以要轉化一下,把位置轉為圖片的左上角位置(x1,y1)以及右下角位置(x2,y2)。由于圖片的大小是:16X16,所以應該這樣轉化:

x1 = weizhi[0] - 8      y1 = weizhi[1] - 8

x2 = weizhi[0] + 8      y2 = weizhi[1] + 8

 

運行結果:

14.PNG

圖片的操作跟圓,線條的操作在原理上是一樣的,只是要注意坐標,圖片坐標為2個元素的元組,而圓,線條是4個元素的元組。

 

相撞測試:

 

如果做游戲,比如射擊游戲,子彈發射出去,擊中了目標,在編程角度看來,其實就是子彈這個對象跟目標這個對象相撞擊了,相接觸了,范圍相重合了。

那如何在畫布里測試2個對象的相撞測試呢? 我提供2個方法。在此之前,先把上面的1個小球在畫布上反彈的代碼改一下,變成2個小球不停移動,遇畫布四邊就反彈。

from tkinter import *
import time
 
def ok():  # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text'] = '  動起來  '
    else:
        yn = True
        but1['text'] = '  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove():  # 小球在畫布四周不停反彈
    global x1, y1, x2, y2, yn
 
    while yn:  # 由yn變量來控制小球是否移動
 
        can1.move(xq1, x1, y1)         # 初始化,小球1,紅球,向右下角移動
        can1.move(xq2, x2, y2)         # 初始化,小球2,藍球,向右上角移動
 
        wz1 = can1.coords(xq1)   # 獲取小球1的位置,一個4個元素的元組
 
        if wz1[0] <= 0:          # 偵測球1是否超過畫布左方
            x1 = step
        if wz1[1] <= 0:          # 偵測球1是否超過畫布上方
            y1 = step
        if wz1[2] >= can1width:      # 偵測球1是否超過畫布右方
            x1 = -step
        if wz1[3] >= can1height:     # 偵測球1是否超過畫布下方
            y1 = -step
 
        wz2 = can1.coords(xq2)   # 獲取小球2的位置,一個4個元素的元組
 
        if wz2[0] <= 0:          # 偵測球是否超過畫布左方
            x2 = step2
        if wz2[1] <= 0:          # 偵測球是否超過畫布上方
            y2 = step2
        if wz2[2] >= can1width:        # 偵測球是否超過畫布右方
            x2 = -step2
        if wz2[3] >= can1height:       # 偵測球是否超過畫布下方
            y2 = -step2
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
root = Tk()
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 2        # 小球1移動的步長,2個像素
step2 = 3       # 小球2移動的步長,3個像素
x1, y1 = 2, 2         # 小球1移動的初始步長
x2, y2 = 2, -2      # 小球2移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
r = 10            # 2個小球的半徑
 
can1 = Canvas(root, background='lightblue',
              width=can1width, height=can1height)  # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok)  # 創建按鈕
but1.pack()
 
xq1 = can1.create_oval(20, 20, 20+2*r, 20+2*r, fill='red',
                       outline='green')  # 繪制小球1,紅球,半徑為r個像素
xq2 = can1.create_oval(60, 60, 60+2*r, 60+2*r, fill='blue',
                       outline='green')  # 繪制小球2,藍球,半徑為r個像素
 
def xq1_move(event):  # 鼠標拖動小球
    can1.coords(xq1, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
def xq2_move(event):  # 鼠標拖動小球
    can1.coords(xq2, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
can1.tag_bind(xq1, "<B1-Motion>", xq1_move)  # 小球1,鼠標按住移動事件
can1.tag_bind(xq2, "<B1-Motion>", xq2_move)  # 小球2,鼠標按住移動事件
 
root.mainloop()

上面的代碼,我在前面的小球反彈代碼基礎上,增加了小球2(藍球)代碼,這個簡單,復制粘貼一下,小球1xq1)坐標改x,yx1,y1 增加的小球xq2,坐標為x2,y2 等等,這個簡單,不再多言,為了講解簡單方便,我把小球的半徑10獨立出來,多設置一個變量r,半徑初始化為10

運行上面的代碼,點擊“動起來”2個小于就自由地在畫布上不停移動,反彈了。

15.PNG

 

但上面代碼還沒有加入相撞測試代碼,下面我先用第1種方法來判斷2球相撞,這個方法是:find_overlapping()

語法:find_overlapping(x1, y1, x2, y2)  # 4個坐標參數組成一個矩形范圍

返回值是在這個限定矩形內的畫布對象的 ID組成的元組,畫面對象部分或全部在這個限定矩形都算。

比如: IDS=can1.find_overlapping(wz1[0], wz1[1], wz1[2], wz1[3])

上面4個參數分別是小球1,即紅球的外接方形坐標,當小球1和小球2還沒有相撞時,IDS這個返回值是 (1,) 即在這個限定范圍內只有ID1的小球1,當相撞時,返回值 IDS=(1,2)  len(IDS)=2 這說明限定矩形內有2個畫面對象,其實,只要 len(IDS)>=2 就說明有2個及以上的畫布對象相撞了。

好了,我把加入的相撞判斷代碼加入,大家測試一下,相撞判斷代碼加入到小球移動循環中,即小球每移動一步,就判斷一下。

from tkinter import *
import time
 
def ok():  # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text'] = '  動起來  '
    else:
        yn = True
        but1['text'] = '  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove():  # 小球在畫布四周不停反彈
    global x1, y1, x2, y2, yn
 
    while yn:  # 由yn變量來控制小球是否移動
 
        can1.move(xq1, x1, y1)         # 初始化,小球1,紅球,向右下角移動
        can1.move(xq2, x2, y2)         # 初始化,小球2,藍球,向右上角移動
 
        wz1 = can1.coords(xq1)   # 獲取小球1的位置,一個4個元素的元組
 
        if wz1[0] <= 0:          # 偵測球1是否超過畫布左方
            x1 = step
        if wz1[1] <= 0:          # 偵測球1是否超過畫布上方
            y1 = step
        if wz1[2] >= can1width:        # 偵測球1是否超過畫布右方
            x1 = -step
        if wz1[3] >= can1height:       # 偵測球1是否超過畫布下方
            y1 = -step
 
        wz2 = can1.coords(xq2)   # 獲取小球2的位置,一個4個元素的元組
 
        if wz2[0] <= 0:          # 偵測球是否超過畫布左方
            x2 = step2
        if wz2[1] <= 0:          # 偵測球是否超過畫布上方
            y2 = step2
        if wz2[2] >= can1width:        # 偵測球是否超過畫布右方
            x2 = -step2
        if wz2[3] >= can1height:       # 偵測球是否超過畫布下方
            y2 = -step2
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
        # 相撞判斷:
        IDS=can1.find_overlapping(wz1[0], wz1[1], wz1[2], wz1[3]) # 小球1的位置坐標參數
        # find_overlapping(外接四方形左上角坐標,外接四方形右下角坐標),返回的坐標范圍內的畫面對象元組。
        if len(IDS) >= 2: # 在同一個四方框時有2個以上畫布對象,即為相撞
            print('相撞了')
            yn = False
            but1['text']='  動起來  '
            break       # 退出循環,讓小球暫停移動
 
root = Tk()
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 2        # 小球1移動的步長,2個像素
step2 = 3       # 小球2移動的步長,3個像素
x1, y1 = 2, 2         # 小球1移動的初始步長
x2, y2 = 2, -2      # 小球2移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
r = 10            # 2個小球的半徑
# xz=False # 相撞的狀態
 
can1 = Canvas(root, background='lightblue',
              width=can1width, height=can1height)  # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok)  # 創建按鈕
but1.pack()
 
xq1 = can1.create_oval(20, 20, 20+2*r, 20+2*r, fill='red',
                       outline='green')  # 繪制小球1,紅球,半徑為r個像素
xq2 = can1.create_oval(60, 60, 60+2*r, 60+2*r, fill='blue',
                       outline='green')  # 繪制小球2,藍球,半徑為r個像素
 
def xq1_move(event):  # 鼠標拖動小球
    can1.coords(xq1, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
def xq2_move(event):  # 鼠標拖動小球
    can1.coords(xq2, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
can1.tag_bind(xq1, "<B1-Motion>", xq1_move)  # 小球1,鼠標按住移動事件
can1.tag_bind(xq2, "<B1-Motion>", xq2_move)  # 小球2,鼠標按住移動事件
 
root.mainloop()

 

運行后,第一次相撞的位置: 丶丌皛

16.PNG

經過測試,小球相撞,都可以成功判斷出。

這種方法判斷畫布對象相撞,代碼也比較容易寫,只要把相撞的一個對象的坐標加入find_overlapping()方法中,只要返回值的元組有2個及以上元素,就說明有2個或2個以上的對象相撞了。

但這種方法,有時,(如上圖所示)當小球相撞了,即小球的外接方框相撞了,但看上去,小球還沒有撞到,小球越大,這個情況越明顯。但小球半徑小的時候,這個情況大大好轉,你可以把上面的代碼的r=10 改成 r=3  再測試一下,要加快小球的運行,把 speed=0.03 改成 speed=0.01

我看用這種方法來判斷子彈射中目標比較好,因為子彈相對于目標都是很小的對象。

 

下面我來講第2種方法來判斷小球相撞,我們知道,當2個小球相撞時,剛一接觸,這2個小球是相切的,這2個小球的球心之間的距離正好是2個小球半徑相加,目前這2個球半徑是相同的,所以,判斷2個小球相撞的條件,就是2球心之間的距離<=2*r  當大于2*r距離,都說明2球的距離還沒有接觸。

下面的代碼,我把第2種判斷小球相撞的代碼取代第1種方法:

from tkinter import *
import time
 
def ok():  # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text'] = '  動起來  '
    else:
        yn = True
        but1['text'] = '  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove():  # 小球在畫布四周不停反彈
    global x1, y1, x2, y2, yn
 
    while yn:  # 由yn變量來控制小球是否移動
 
        can1.move(xq1, x1, y1)         # 初始化,小球1,紅球,向右下角移動
        can1.move(xq2, x2, y2)         # 初始化,小球2,藍球,向右上角移動
 
        wz1 = can1.coords(xq1)   # 獲取小球1的位置,一個4個元素的元組
 
        if wz1[0] <= 0:          # 偵測球1是否超過畫布左方
            x1 = step
        if wz1[1] <= 0:          # 偵測球1是否超過畫布上方
            y1 = step
        if wz1[2] >= can1width:        # 偵測球1是否超過畫布右方
            x1 = -step
        if wz1[3] >= can1height:       # 偵測球1是否超過畫布下方
            y1 = -step
 
        wz2 = can1.coords(xq2)   # 獲取小球2的位置,一個4個元素的元組
 
        if wz2[0] <= 0:          # 偵測球是否超過畫布左方
            x2 = step2
        if wz2[1] <= 0:          # 偵測球是否超過畫布上方
            y2 = step2
        if wz2[2] >= can1width:        # 偵測球是否超過畫布右方
            x2 = -step2
        if wz2[3] >= can1height:       # 偵測球是否超過畫布下方
            y2 = -step2
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
        # 相撞判斷:
        SS=pow( (((wz2[0]+10)-(wz1[0]+10))**2 + ((wz2[1]+10)-(wz1[1]+10))**2),1/2 ) # 小球每移動一次,即時求出2球的圓心之間的間距
 
        print('ss=',SS) # 輸出2球心之間的間距讓大家看看
 
        if SS<=20 :  # 20就是2個球的間距,等于或小于2球心之間的間距,就說明2球相撞了。
            xz=True
            print('相撞了')
            yn = False
            but1['text']='  動起來  '
            break
 
root = Tk() # 源碼來自wb98.com
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 2        # 小球1移動的步長,2個像素
step2 = 3       # 小球2移動的步長,3個像素
x1, y1 = 2, 2         # 小球1移動的初始步長
x2, y2 = 2, -2      # 小球2移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
r = 10            # 2個小球的半徑
# xz=False # 相撞的狀態
 
can1 = Canvas(root, background='lightblue',
              width=can1width, height=can1height)  # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok)  # 創建按鈕
but1.pack()
 
xq1 = can1.create_oval(20, 20, 20+2*r, 20+2*r, fill='red',
                       outline='green')  # 繪制小球1,紅球,半徑為r個像素
xq2 = can1.create_oval(60, 60, 60+2*r, 60+2*r, fill='blue',
                       outline='green')  # 繪制小球2,藍球,半徑為r個像素
 
def xq1_move(event):  # 鼠標拖動小球
    can1.coords(xq1, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
def xq2_move(event):  # 鼠標拖動小球
    can1.coords(xq2, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
can1.tag_bind(xq1, "<B1-Motion>", xq1_move)  # 小球1,鼠標按住移動事件
can1.tag_bind(xq2, "<B1-Motion>", xq2_move)  # 小球2,鼠標按住移動事件
 
root.mainloop()

運行后,第1次相撞:

17.PNG

 

經測試,這種判斷2球相撞(相切)效果不錯,無論球的半徑大小都還不錯,但是要不斷計算2球心之間的距離SS。

SS=pow( (((wz2[0]+r)-(wz1[0]+r))**2 + ((wz2[1]+r)-(wz1[1]+r))**2),1/2 )
# 小球每移動一次,即時求出2球的圓心之間的間距

說明一下:pow() 計算平方根:

pow(A,1/2)  # 計算A2次方根

pow(A,1/3)  # 計算A3次方根

 

2球心之間的距離是一個數字問題,我稍講解一下:

18.PNG

 

如上圖所示:

小球1的外接正方方形坐標為 wz1[0], wz1[1]

小球2的外接正方方形坐標為 wz2[0], wz2[1]

 

小球1的球心坐標為 wz1[0] + r , wz1[1] + r

小球2的球心坐標為 wz2[0] + r , wz2[1] + r

 

B = 2的橫坐標 - 1的橫坐標 =  (wz2[0] + r) - (wz1[0] + r)

A = 2的縱坐標 - 1的縱坐標 =  (wz2[1] + r) - (wz1[1] + r)

 

C的平方=A的平方 + B的平方

C**2 = A**2 + B**2

 

2球心的距離為 C = 2次方根 [ A2次方 + B2次方)]

            C= pow( (A**2 + B**2), 1/2 )

 

2球心之間的距離這個數字問題,我就講解到這,不明白的,多看幾遍吧。

 

 

上面的代碼,我想再改進一下,因為2個球相撞后,再點“動起來”按鈕好幾次,2球還是處于相撞狀態,我希望相撞后,在2球分開之前,只算1次相撞的情況。

我新加入一個相撞的布爾變量:xz ,當相撞前,球心之間的距離只要>2r ,xz=False 當相撞后,xz=True,在暫停的條件加上 xz=True 這樣,相撞后到分開前,只執行一次暫停小球移動。

改動的代碼,如下,不解釋了,對我寫的文章有興趣,可以關注我,收藏我的文章。我的網站 wb98.com

from tkinter import *
import time
 
def ok():  # 點擊按鈕,由yn變量決定是暫停還是繼續動起來
    global yn
    if yn == True:
        yn = False
        but1['text'] = '  動起來  '
    else:
        yn = True
        but1['text'] = '  暫 停  '
        ballmove()  # 小球繼續動起來
 
def ballmove():  # 小球在畫布四周不停反彈
    global x1, y1, x2, y2, yn, xz
 
    while yn:  # 由yn變量來控制小球是否移動
 
        can1.move(xq1, x1, y1)         # 初始化,小球1,紅球,向右下角移動
        can1.move(xq2, x2, y2)         # 初始化,小球2,藍球,向右上角移動
 
        wz1 = can1.coords(xq1)   # 獲取小球1的位置,一個4個元素的元組
 
        if wz1[0] <= 0:          # 偵測球1是否超過畫布左方
            x1 = step
        if wz1[1] <= 0:          # 偵測球1是否超過畫布上方
            y1 = step
        if wz1[2] >= can1width:        # 偵測球1是否超過畫布右方
            x1 = -step
        if wz1[3] >= can1height:       # 偵測球1是否超過畫布下方
            y1 = -step
 
        wz2 = can1.coords(xq2)   # 獲取小球2的位置,一個4個元素的元組
 
        if wz2[0] <= 0:          # 偵測球是否超過畫布左方
            x2 = step2
        if wz2[1] <= 0:          # 偵測球是否超過畫布上方
            y2 = step2
        if wz2[2] >= can1width:        # 偵測球是否超過畫布右方
            x2 = -step2
        if wz2[3] >= can1height:       # 偵測球是否超過畫布下方
            y2 = -step2
 
        can1.update()       # 刷新畫布
        time.sleep(speed)   # 可以控制移動速度
 
        # 相撞判斷:
        # 小球每移動一次,即時求出2球的圓心之間的間距
        SS = pow((((wz2[0]+r)-(wz1[0]+r))**2 +
                 ((wz2[1]+r)-(wz1[1]+r))**2), 1/2)
 
        print('ss=', SS)  # 輸出2球心之間的間距讓大家看看
 
        if SS > 2*r:
            xz = False  # 相距大于20個像素,肯定相撞狀態為假
 
        if SS <= 20 and xz == False:  # 20就是2個球的間距,等于或小于2球心之間的間距,就說明2球相撞了。
            xz = True
            print('相撞了')
            yn = False
            but1['text'] = '  動起來  '
            break
 
root = Tk() # 源碼來自www.chinaengraver.com
 
can1width = 200       # 定義畫布寬度
can1height = 150      # 定義畫布高度
step = 2        # 小球1移動的步長,2個像素
step2 = 3       # 小球2移動的步長,3個像素
x1, y1 = 2, 2         # 小球1移動的初始步長
x2, y2 = 2, -2      # 小球2移動的初始步長
speed = 0.03    # 設置移動速度
yn = False      # yn的值來控制球是否移動
r = 10            # 2個小球的半徑
xz = False  # 相撞的狀態
 
can1 = Canvas(root, background='lightblue',
              width=can1width, height=can1height)  # 創建畫布
can1.pack()
 
but1 = Button(root, text="  動起來  ", command=ok)  # 創建按鈕
but1.pack()
 
xq1 = can1.create_oval(20, 20, 20+2*r, 20+2*r, fill='red',
                       outline='green')  # 繪制小球1,紅球,半徑為r個像素
xq2 = can1.create_oval(60, 60, 60+2*r, 60+2*r, fill='blue',
                       outline='green')  # 繪制小球2,藍球,半徑為r個像素
 
def xq1_move(event):  # 鼠標拖動小球
    can1.coords(xq1, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
def xq2_move(event):  # 鼠標拖動小球
    can1.coords(xq2, event.x-r, event.y-r, event.x+r, event.y+r)
    # 將鼠標當前位置轉為小球的外接正方形的左上角和右下角坐標(小球的半徑為r)
 
can1.tag_bind(xq1, "<B1-Motion>", xq1_move)  # 小球1,鼠標按住移動事件
can1.tag_bind(xq2, "<B1-Motion>", xq2_move)  # 小球2,鼠標按住移動事件
 
root.mainloop()


Scavas組件要學的知識還有很多,本人也只是學了一點丁基礎知識,以后學了很多的Scavas知識,再來講解Scavas組件更深入的知識吧。

此文章來自:wb98.com  網站還有相關的系列課程文章,感興趣的可以前往。

 

 

下一篇文章講解一下導入模塊的方法,以及不同導入模塊方法有什么區別,如 import tkinter.ttk form tkinter.ttk import * 有什么不同。


來源:濟亨網

本文鏈接:http://www.chinaengraver.com/post/333.html

    << 上一篇 下一篇 >>

    湘公網安備 43011102000514號 - 湘ICP備08100508號

    2019年秋霞鲁丝片瓜皮_导航亚洲AV日韩AV永久无码_有没有哪些可以看片的免费的_国产色妞妞在线视频免费播放