網(wǎng)站開發(fā)報價明細(xì)營銷推廣的公司
7.8 圖形界面應(yīng)用案例——關(guān)燈游戲
題目:
[案例]游戲初步——關(guān)燈游戲。
關(guān)燈游戲是很有意思的益智游戲,玩家通過單擊關(guān)掉(或打開)一盞燈。如果關(guān)(掉(或打開)一個電燈,其周圍(上下左右)的電燈也會觸及開關(guān),成功地關(guān)掉所有電燈即可過關(guān)。
圖7-43 關(guān)燈游戲運行效果
分析:游戲中采用二維列表存儲燈的狀態(tài),'you'表示電燈亮(黃色的圓),'wu'表示電燈關(guān)掉(背景色的圓)。在Canvas畫布單擊事件中,獲取鼠標(biāo)單擊位置從而換算成棋盤位(x1,y1),并處理四周燈的狀態(tài)轉(zhuǎn)換。
案例代碼:
from tkinter import *
from tkinter import messagebox
root = Tk()
l= [['wu', 'wu', 'you', 'you', 'you'] ,['wu', 'you', 'wu', 'wu', 'wu'],['wu', 'wu', 'wu', 'wu', 'wu'],['wu', 'wu', 'wu', 'you', 'wu'],['you', 'you', 'you', 'wu', 'wu']]
#繪制燈的狀態(tài)情況圖
def huaqi():for i in range (0,5):for u in range (0,5):if l[i][u]=='you':cv.create_oval(i * 40 + 10,u * 40 + 10,(i+1)* 40+10,(u+1)*40 + 10,outline='white', fill='yellow', width=2)#亮燈else:cv.create_oval(i*40 + 10,u*40 +10,(i+1) *40+10,(u+1)*40 + 10,outline='white', fill='white', width=2) #滅燈
#反轉(zhuǎn)(x1,yl)處燈的狀態(tài)
def reserve(x1,y1):if l[x1][y1] =='wu':l[x1][y1]='you'else:l[x1][y1] = 'wu'
#單擊事件函數(shù)
def luozi(event):x1 = (event.x - 10) // 40y1 = (event.y - 10) // 40print(x1, y1)reserve(x1, y1) # 翻轉(zhuǎn)(x1,y1)處燈的狀態(tài)# 以下翻轉(zhuǎn)(x1,yI)周圍的燈的狀態(tài)#左側(cè)燈的狀態(tài)反轉(zhuǎn)if x1 !=0:reserve(x1 - 1, y1)# 右側(cè)燈的狀態(tài)反轉(zhuǎn)if x1!=4:reserve(x1 + 1, y1)# 上側(cè)燈的狀態(tài)反轉(zhuǎn)if y1!=0:reserve(x1, y1 - 1)# 下側(cè)燈的狀態(tài)反轉(zhuǎn)if y1!=4:reserve(x1, y1 + 1)huaqi()
# 主程序
cv = Canvas(root, bg='white', width=210, height=210)
for i in range(0, 6): # 繪制網(wǎng)格線cv.create_line(10, 10 + 1 * 40,210, 10 + i * 40, arrow = 'none')cv.create_line(10 + i * 40,10,10 + 1 * 40, 210, arrow = 'none' )
huaqi() # 繪制燈的狀態(tài)情況圖
p = 0
for i in range(0, 5):for u in l[i]:if u == 'wu':p= p + 1
if p == 25:messagebox.showinfo('win', '你過關(guān)了') # 顯示贏信息的消息窗口
cv.bind('<Button-1>', luozi)
cv.pack()
root.mainloop ()
這段代碼是一個基于Tkinter庫的燈泡游戲。游戲界面是一個5x5的網(wǎng)格,每個網(wǎng)格代表一個燈泡。初始狀態(tài)下,所有的燈泡都是滅的(白色)。玩家的目標(biāo)是通過點擊燈泡,將所有的燈泡都點亮(黃色)。
代碼中的主要函數(shù)和操作包括:
1. `huaqi()`函數(shù):根據(jù)燈泡的狀態(tài)情況,繪制燈泡的圖形。如果燈泡是亮的,則繪制一個黃色的圓形;如果燈泡是滅的,則繪制一個白色的圓形。
2. `reserve(x1, y1)`函數(shù):根據(jù)給定的坐標(biāo)`(x1, y1)`,反轉(zhuǎn)該位置的燈泡的狀態(tài)。如果燈泡是亮的,則變?yōu)闇绲?;如果燈泡是滅的,則變?yōu)榱恋摹?/p>
3. `luozi(event)`函數(shù):處理鼠標(biāo)點擊事件。根據(jù)點擊的位置,確定對應(yīng)的燈泡,并進(jìn)行狀態(tài)反轉(zhuǎn)。同時,還會反轉(zhuǎn)該燈泡周圍的燈泡狀態(tài)。
4. 主程序部分:創(chuàng)建一個畫布(Canvas)對象,并設(shè)置背景色為白色。然后,使用循環(huán)繪制網(wǎng)格線。接著,調(diào)用`huaqi()`函數(shù)繪制燈泡的初始狀態(tài)。最后,綁定鼠標(biāo)左鍵點擊事件到`luozi()`函數(shù),并將畫布顯示在窗口中。
在游戲過程中,玩家通過點擊燈泡來改變它的狀態(tài),并且反轉(zhuǎn)周圍燈泡的狀態(tài)。當(dāng)所有的燈泡都點亮?xí)r,會彈出一個消息窗口顯示玩家勝利的信息。
注(Tkinter庫):
Tkinter是Python的標(biāo)準(zhǔn)圖形用戶界面(GUI)工具包,它提供了創(chuàng)建和管理GUI應(yīng)用程序所需的組件和功能。Tkinter是基于Tcl/Tk工具包的Python接口,Tcl是一種腳本語言,而Tk是一個用于創(chuàng)建圖形用戶界面的工具包。
Tkinter庫包含了許多常用的GUI組件,比如按鈕、標(biāo)簽、文本框、復(fù)選框、單選按鈕、菜單等,同時也支持布局管理器來幫助開發(fā)者設(shè)計和布局界面。開發(fā)者可以使用Tkinter來創(chuàng)建各種類型的應(yīng)用程序,從簡單的工具到復(fù)雜的桌面應(yīng)用程序都可以使用Tkinter來實現(xiàn)。
Tkinter的優(yōu)點包括:
1. **易于學(xué)習(xí)和使用**:Tkinter是Python的標(biāo)準(zhǔn)庫,因此無需額外安裝即可使用。它的接口簡單直觀,適合初學(xué)者入門。
2. **跨平臺性**:Tkinter可以在多個平臺上運行,包括Windows、Linux和Mac OS等。
3. **豐富的組件**:Tkinter提供了豐富的GUI組件,可以滿足大部分應(yīng)用程序的需求。
4. **靈活性**:Tkinter支持自定義組件和樣式,開發(fā)者可以根據(jù)自己的需求進(jìn)行定制。
總之,Tkinter是一個功能強大且易于使用的GUI工具包,適合用于開發(fā)Python圖形界面應(yīng)用程序。
擴(kuò)展題目:
題目要求:
請完成《7.8關(guān)燈游戲》(課本p.170),并完成以下擴(kuò)展內(nèi)容:
擴(kuò)展內(nèi)容:
1. 請設(shè)計并實現(xiàn)界面功能,允許玩家選擇初始地圖的大小,分別為:小(5x5),中(8x8),大(12x10),玩家選擇之后立即刷新窗口界面并重設(shè)地圖
2. 請設(shè)計并實現(xiàn)界面功能,允許玩家選擇游戲難度,分別為:
容易:一開始有20%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
中等:一開始有40%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
困難:一開始有60%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
3. 游戲開始之前,提示玩家輸入唯一的用戶名,用于保留該玩家的闖關(guān)記錄
4. 按照玩家選擇的地圖大小和難度開始游戲,游戲過程中記錄玩家闖關(guān)的總時間以及翻轉(zhuǎn)次數(shù)(即開關(guān)燈的次數(shù))
5. 玩家通關(guān)成功后,保存相關(guān)信息到數(shù)據(jù)庫,表結(jié)構(gòu)可以自定,但應(yīng)當(dāng)至少包含以下信息:
- 玩家選擇的地圖大小
- 玩家選擇的難度
- 玩家通關(guān)的日期時間
- 玩家通關(guān)所用的總時間
- 玩家通關(guān)所用的總點擊次數(shù)
6. 設(shè)計窗口界面,顯示通關(guān)排行榜,具體說明如下:
- 根據(jù)不同地圖大小以及不同難度顯示各自排名,例如:大地圖中等難度與中地圖困難難度的排名是分開的
- 可以選擇顯示總排名(所有玩家的記錄)與個人排名(玩家自己的所有記錄)
- 可以選擇根據(jù)通關(guān)時間排名,以及根據(jù)點擊次數(shù)排名,兩個排名都是按從小到大排列
擴(kuò)展完第一個功能的代碼
(1. 請設(shè)計并實現(xiàn)界面功能,允許玩家選擇初始地圖的大小,分別為:小(5x5),中(8x8),大(12x10),玩家選擇之后立即刷新窗口界面并重設(shè)地圖):
from tkinter import *
from tkinter import messagebox
import randomclass LightsOutGame:def __init__(self, master=None, size=5):self.master = masterself.size = sizeself.lights = [['wu' for _ in range(size)] for _ in range(size)]self.create_widgets()self.random_open_lights() # 隨機(jī)打開一些燈def create_widgets(self):self.cv = Canvas(self.master, bg='white', width=self.size*40+10, height=self.size*40+10)self.cv.pack()self.draw_grid() # 繪制網(wǎng)格self.huaqi() # 繪制燈的狀態(tài)情況圖self.cv.bind('<Button-1>', self.luozi)def draw_grid(self):self.cv.delete("grid") # 清除之前的網(wǎng)格for i in range(self.size+1): # 繪制網(wǎng)格線self.cv.create_line(10, 10 + i * 40, 10 + self.size * 40, 10 + i * 40, arrow='none', tags="grid")self.cv.create_line(10 + i * 40, 10, 10 + i * 40, 10 + self.size * 40, arrow='none', tags="grid")def huaqi(self):self.cv.delete("lights") # 清除之前的燈for i in range(self.size):for u in range(self.size):if self.lights[i][u] == 'you':self.cv.create_oval(i * 40 + 10, u * 40 + 10, (i + 1) * 40 + 10, (u + 1) * 40 + 10,outline='white', fill='yellow', width=2, tags="lights") # 亮燈else:self.cv.create_oval(i * 40 + 10, u * 40 + 10, (i + 1) * 40 + 10, (u + 1) * 40 + 10,outline='white', fill='white', width=2, tags="lights") # 滅燈def reserve(self, x1, y1):if self.lights[x1][y1] == 'wu':self.lights[x1][y1] = 'you'else:self.lights[x1][y1] = 'wu'def luozi(self, event):x1 = (event.x - 10) // 40y1 = (event.y - 10) // 40print(x1, y1)self.reserve(x1, y1) # 翻轉(zhuǎn)(x1,y1)處燈的狀態(tài)# 以下翻轉(zhuǎn)(x1,yI)周圍的燈的狀態(tài)#左側(cè)燈的狀態(tài)反轉(zhuǎn)if x1 != 0:self.reserve(x1 - 1, y1)# 右側(cè)燈的狀態(tài)反轉(zhuǎn)if x1 != self.size - 1:self.reserve(x1 + 1, y1)# 上側(cè)燈的狀態(tài)反轉(zhuǎn)if y1 != 0:self.reserve(x1, y1 - 1)# 下側(cè)燈的狀態(tài)反轉(zhuǎn)if y1 != self.size - 1:self.reserve(x1, y1 + 1)self.huaqi()def random_open_lights(self):num_of_lights = random.randint(1, self.size*self.size) # 隨機(jī)選擇要打開的燈的數(shù)量positions = random.sample(range(self.size*self.size), num_of_lights) # 隨機(jī)選擇要打開的燈的位置for pos in positions:x = pos // self.sizey = pos % self.sizeself.reserve(x, y)def reset_game(size):game.size = sizegame.lights = [['wu' for _ in range(size)] for _ in range(size)]game.cv.config(width=size*40+10, height=size*40+10)game.draw_grid()game.random_open_lights()game.huaqi()def on_small():reset_game(5)def on_medium():reset_game(8)def on_large():reset_game(12)root = Tk()
game = LightsOutGame(root)menu_frame = Frame(root)
menu_frame.pack()small_button = Button(menu_frame, text="小", command=on_small)
small_button.pack(side=LEFT)medium_button = Button(menu_frame, text="中", command=on_medium)
medium_button.pack(side=LEFT)large_button = Button(menu_frame, text="大", command=on_large)
large_button.pack(side=LEFT)root.mainloop()
擴(kuò)展完功能二的代碼:
請設(shè)計并實現(xiàn)界面功能,允許玩家選擇游戲難度,分別為:
容易:一開始有20%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
中等:一開始有40%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
困難:一開始有60%的格子狀態(tài)為翻轉(zhuǎn)狀態(tài)
from tkinter import *
from tkinter import messagebox
import randomclass LightsOutGame:def __init__(self, master=None, size=5, difficulty='medium'):self.master = masterself.size = sizeself.lights = [['wu' for _ in range(size)] for _ in range(size)]self.create_widgets()self.random_open_lights(difficulty) # 隨機(jī)打開一些燈def create_widgets(self):self.cv = Canvas(self.master, bg='white', width=self.size*40+10, height=self.size*40+10)self.cv.pack()self.draw_grid() # 繪制網(wǎng)格self.huaqi() # 繪制燈的狀態(tài)情況圖self.cv.bind('<Button-1>', self.luozi)def draw_grid(self):self.cv.delete("grid") # 清除之前的網(wǎng)格for i in range(self.size+1): # 繪制網(wǎng)格線self.cv.create_line(10, 10 + i * 40, 10 + self.size * 40, 10 + i * 40, arrow='none', tags="grid")self.cv.create_line(10 + i * 40, 10, 10 + i * 40, 10 + self.size * 40, arrow='none', tags="grid")def huaqi(self):self.cv.delete("lights") # 清除之前的燈for i in range(self.size):for u in range(self.size):if self.lights[i][u] == 'you':self.cv.create_oval(i * 40 + 10, u * 40 + 10, (i + 1) * 40 + 10, (u + 1) * 40 + 10,outline='white', fill='yellow', width=2, tags="lights") # 亮燈else:self.cv.create_oval(i * 40 + 10, u * 40 + 10, (i + 1) * 40 + 10, (u + 1) * 40 + 10,outline='white', fill='white', width=2, tags="lights") # 滅燈def reserve(self, x1, y1):if self.lights[x1][y1] == 'wu':self.lights[x1][y1] = 'you'else:self.lights[x1][y1] = 'wu'def luozi(self, event):x1 = (event.x - 10) // 40y1 = (event.y - 10) // 40print(x1, y1)self.reserve(x1, y1) # 翻轉(zhuǎn)(x1,y1)處燈的狀態(tài)