Windows应用程序设计作业(Python版实现)
作业3-模仿应用程序界面 - 俄罗斯方块游戏
设计一个俄罗斯方块的游戏界面,要求有菜单,选项设置对话框,操控界面,能控制方块的左右和向下移动。
先来个测试视频:
https://www.bilibili.com/video/BV1Vt4y1e7ej/
代码参考教程:
https://www.bilibili.com/video/BV1eJ411h7ZV?from=search&seid=6903931124819368025
代码封装设计结构如下:
(一)main.py
from tetris_bg_show import Background_show
from tetris_initialize import Tetris_init
import tkinter as tk
win = tk.Tk()
tetris = Tetris_init(win, 20, 12, 30, 400) # R,C,cell_size,FPS
bk_canvas = Background_show(win, tetris)
bk_canvas.BK_canvas()
win.mainloop()
(二)tetris_bg_show.py
import tkinter as tk
from tetris_initialize import Status
from tetris_initialize import Tetris_init
from tetris_shape import Tetris_shape
from tetris_fg_show import Tetrominoe
from PIL import ImageTk,Image
import tkinter.font as tkFont
class Background_show:
def __init__(self, win, tetris):
self.win = win
self.tetris = tetris
self.R, self.C, self.cell_size = tetris.R, tetris.C, tetris.cell_size
self.height, self.width = tetris.height, tetris.width
self.bk_canvas = tk.Canvas(self.win, width = self.width, height = self.height)
def on_click(self):
self.value = self.iv_style.get()
if self.value != 0:
if self.value == 1:
Status.FPS = 800
elif self.value == 2:
Status.FPS = 500
else:
Status.FPS = 200
self.bk_canvas.delete("all")
root = self.win
self.tetris.__init__(root, self.R, self.C, self.cell_size, Status.FPS, True)
self.bk_canvas.pack_forget()
tetrominoe = Tetrominoe(root, self.tetris)
tetrominoe.NW_canvas(root)
def BK_canvas(self):
self.im = ImageTk.PhotoImage(Image.open(r'F:\2020_C#_assignments\background.gif'))
self.bk_canvas.create_image(0, 0, anchor='nw', image = self.im)
self.iv_style = tk.IntVar()
ft1 = tkFont.Font(family='微软雅黑', size=15, weight=tkFont.BOLD)
self.bk_canvas.create_window(180, 260, width = 100, height = 40, window = tk.Radiobutton(self.win, text = '初级难度', font = ft1, foreground = "FireBrick", relief = "raised", value = 1, variable = self.iv_style, indicatoron = 0))
self.bk_canvas.create_window(180, 310, width = 100, height = 40, window = tk.Radiobutton(self.win, text = '中级难度', font = ft1, foreground = "FireBrick", relief = "raised", value = 2, variable = self.iv_style, indicatoron = 0))
self.bk_canvas.create_window(180, 360, width = 100, height = 40, window = tk.Radiobutton(self.win, text = '高级难度', font = ft1, foreground = "FireBrick", relief = "raised", value = 3, variable = self.iv_style, indicatoron = 0))
self.bk_canvas.create_window(180, 450, width=140, height=60, window=tk.Button(self.win, text = "开启游戏之旅", font = ft1, command = self.on_click))
self.bk_canvas.pack()
(三)tetris_fg_show.py
import tkinter as tk
from tetris_initialize import Status
from tetris_initialize import Tetris_init
from tetris_shape import Tetris_shape
from tetris_operation import Tetris_op
class Text:
NEXT = 'NEXT SHAPE'
SCOR = 'SCORES'
ZERO = '0'
FST1 = ('Helvetica', 20, 'bold')
FST2 = ('Helvetica', 80, 'bold')
SCOLOR = {
'D':'DarkCyan',
'P':'pink',
'T':'tan',
'G':'gold',
'R':'RosyBrown'
}
class Tetrominoe:
def __init__(self, win, tetris):
self.win = win
self.tetris = tetris
self.R, self.C, self.cs = tetris.R, tetris.C, tetris.cell_size
self.h, self.w = tetris.height, tetris.width
self.nw_canvas = tk.Canvas(win, width = self.w, height = self.h)
self.tetris_op = Tetris_op(self.tetris, self.nw_canvas)
Status.mouse_click = 1
for i in range(self.R):
i_row = ['' for j in range(self.C)]
Status.block_list.append(i_row)
def NW_canvas(self, win):
self.tetris_op.draw_board(self.nw_canvas)
self.nw_canvas.focus_set()
self.nw_canvas.bind_all('<KeyPress>', self.event_move)
ca, cb = 0, 45
cc, cd, ce = self.cs * 3, self.cs * 9, self.cs * 11
cf, cg, cw, ch = self.cs * 12 + 3, self.cs * 15, self.w, self.h
self.nw_canvas.create_rectangle((cf, ca, cw, cc), width = 4, fill = Text.SCOLOR['P'], outline = Text.SCOLOR['T'], stipple = 'gray25')
self.nw_canvas.create_rectangle((cf, cd, cw, ch), width = 4, fill = Text.SCOLOR['P'], outline = Text.SCOLOR['T'], stipple = 'gray25')
self.nw_canvas.create_rectangle((cf, cc + 4, cw , cd - 4), width = 4, outline = Text.SCOLOR['G'], dash = (1,1))
self.nw_canvas.create_text((cg, cg), text = Text.ZERO, font = Text.FST2, fill = Text.SCOLOR['D'], tag = 'SCORE')
self.nw_canvas.create_text((cg, cb), text = Text.NEXT, font = Text.FST1, fill = Text.SCOLOR['R'])
self.nw_canvas.create_text((cg, ce), text = Text.SCOR, font = Text.FST1, fill = Text.SCOLOR['R'])
win.after(Status.FPS, self.tetris_op.game_loop)
self.nw_canvas.pack()
def event_move(self, event):
direction = [0, 0]
if event.keysym == 'Left':
direction = [-1, 0]
elif event.keysym == 'Right':
direction = [1, 0]
elif event.keysym == 'Up':
self.event_rotata('Up')
elif event.keysym == 'Down':
if Status.current_block is None:
return
cell_list = Status.current_block['cell_list']
cc, cr = Status.current_block['cr']
min_height = self.R
for cell in cell_list:
height = 0
cell_c, cell_r = cell
c, r = cell_c + cc, cell_r + cr
if r >= 0 and Status.block_list[r][c]:
return
for ri in range(r + 1, self.R):
if Status.block_list[ri][c]:
break
else:
height += 1
if height < min_height:
min_height = height
direction = [0, min_height]
if Status.current_block is not None and self.tetris_op.check_move(Status.current_block, direction):
self.tetris_op.draw_block_move(self.nw_canvas, Status.current_block, direction)
def event_rotata(self, event):
if Status.current_block is None:
return
cell_list = Status.current_block['cell_list']
rorate_list = []
for cell in cell_list:
cell_c, cell_r = cell
rorate_cell = [cell_r, -cell_c]
rorate_list.append(rorate_cell)
block_after_rotate = {
'kind': Status.current_block['kind'],
'cell_list': rorate_list,
'cr': Status.current_block['cr']
}
if self.tetris_op.check_move(block_after_rotate):
cc, cr = Status.current_block['cr']
self.tetris_op.draw_cells(self.nw_canvas, cc, cr, Status.current_block['cell_list'])
self.tetris_op.draw_cells(self.nw_canvas, cc, cr, rorate_list, Tetris_shape.SHAPESCOLOR[Status.current_block['kind']])
Status.current_block = block_after_rotate
(四)tetris_initialize.py
from tetris_shape import Tetris_shape
class Status:
current_block = None
next_block = None
next_block_pre = None
block_list = []
mouse_click = 1
flag_clear = False
score = 0
FPS = 0
class Tetris_init:
def __init__(self, win, R, C, cell_size, FPS, show = False):
self.win = win
Status.FPS = FPS
self.R, self.C = R, C
self.cell_size = cell_size
self.height, self.width = self.R * self.cell_size, self.C * self.cell_size
self.screenwidth, self.screenheight = self.win.winfo_screenwidth(), self.win.winfo_screenheight()
if show is True:
self.width = self.width + self.cell_size * 6
self.win.geometry("%dx%d+%d+%d" % (self.width, self.height, (self.screenwidth - self.width) / 2, (self.screenheight - self.height) / 2))
self.win.title("Welcom to Tetris~")
(五)tetris_operation.py
import tkinter as tk
import tkinter.messagebox
from tetris_initialize import Status
from tetris_initialize import Tetris_init
from tetris_shape import Tetris_shape
import pyautogui
import random
class Tetris_op:
def __init__(self, tetris, nw_canvas):
self.tetris = tetris
self.nw_canvas = nw_canvas
self.R, self.C, self.cell_size = tetris.R, tetris.C, tetris.cell_size
self.height, self.width = tetris.R * tetris.cell_size, tetris.C * tetris.cell_size
self.screenwidth, self.screenheight = tetris.win.winfo_screenwidth(), tetris.win.winfo_screenheight()
def draw_cell_by_cr(self, canvas, c, r, color="lightgray"):
x0 = c * self.cell_size
y0 = r * self.cell_size
x1 = c * self.cell_size + self.cell_size
y1 = r * self.cell_size + self.cell_size
canvas.create_rectangle(x0, y0, x1, y1, fill = color, outline = "white", width = 2)
def draw_board(self, canvas):
for ri in range(self.R):
for ci in range(self.C):
cell_type = Status.block_list[ri][ci]
if cell_type:
self.draw_cell_by_cr(canvas, ci, ri, Tetris_shape.SHAPESCOLOR[cell_type])
else:
self.draw_cell_by_cr(canvas, ci, ri)
for ri in range(3, 9):
for ci in range(12, 18):
self.draw_cell_by_cr(canvas, ci, ri, color = "white")
def draw_cells(self, canvas, c, r, cell_list, color = "lightgray", show = False):
for cell in cell_list:
cell_c, cell_r = cell
ci = cell_c + c
ri = cell_r + r
if 0 <= c < self.C and 0 <= r < self.R :
self.draw_cell_by_cr(canvas, ci, ri, color)
if show is True:
self.draw_cell_by_cr(canvas, ci, ri, color)
def draw_block_move(self, canvas, block, direction = [0, 0]):
shape_type = block['kind']
cell_list = block['cell_list']
c, r = block['cr']
self.draw_cells(canvas, c, r, cell_list)
dc, dr = direction
new_c, new_r = c + dc, r + dr
block['cr'] = [new_c, new_r]
self.draw_cells(canvas, new_c, new_r, cell_list, Tetris_shape.SHAPESCOLOR[shape_type])
def generate_new_block(self):
cr = [self.C//2, 0]
new_block = {
'kind': Status.next_block['kind'],
'cell_list': Status.next_block['cell_list'],
'cr': cr
}
if Status.flag_clear is True:
pre_shape_type = Status.next_block['kind']
pre_cell_list = Status.next_block['cell_list']
pre_c, pre_r = Status.next_block['cr']
for cell in pre_cell_list:
cell_c, cell_r = cell
ci = cell_c + pre_c
ri = cell_r + pre_r
x0 = ci * self.cell_size
y0 = ri * self.cell_size
x1 = ci * self.cell_size + self.cell_size
y1 = ri * self.cell_size + self.cell_size
self.nw_canvas.create_rectangle(x0, y0, x1, y1, fill = "white", outline = "white", width = 2)
Status.flag_clear = False
Status.next_block = self.generate_next_block()
self.draw_next_block(self.nw_canvas, Status.next_block, Status.next_block_pre)
return new_block
def generate_next_block(self):
kind = random.choice(list(Tetris_shape.SHAPES.keys()))
next_cr = [15, 6]
next_tetris_block = {
'kind': kind,
'cell_list': Tetris_shape.SHAPES[kind],
'cr': next_cr
}
return next_tetris_block
def draw_next_block(self, canvas, block, block_pre):
shape_type = block['kind']
cell_list = block['cell_list']
c, r = block['cr']
for cell in cell_list:
cell_c, cell_r = cell
ci = cell_c + c
ri = cell_r + r
x0 = ci * self.cell_size
y0 = ri * self.cell_size
x1 = ci * self.cell_size + self.cell_size
y1 = ri * self.cell_size + self.cell_size
canvas.create_rectangle(x0, y0, x1, y1, fill = Tetris_shape.SHAPESCOLOR[shape_type], outline = "white", width = 2)
def check_move(self, block, direction=[0, 0]):
cc, cr = block['cr']
cell_list = block['cell_list']
for cell in cell_list:
cell_c, cell_r = cell
c = cell_c + cc + direction[0]
r = cell_r + cr + direction[1]
if c < 0 or c >= self.C or r >= self.R:
return False
if r >= 0 and Status.block_list[r][c]:
return False
return True
def save_block_to_list(self, block):
shape_type = block['kind']
cell_list = block['cell_list']
cc, cr = block['cr']
for cell in cell_list:
cell_c, cell_r = cell
c = cell_c + cc
r = cell_r + cr
Status.block_list[r][c] = shape_type
def check_row_complete(self, row):
for cell in row:
if cell == '':
return False
return True
def check_and_clear(self):
has_complete_row = False
for ri in range(len(Status.block_list)):
if self.check_row_complete(Status.block_list[ri]):
has_complete_row = True
if ri > 0:
for cur_ri in range(ri, 0, -1):
Status.block_list[cur_ri] = Status.block_list[cur_ri - 1][:]
Status.block_list[0] = ['' for j in range(self.C)]
else:
Status.block_list[ri] = ['' for j in range(self.C)]
Status.score += 10
if has_complete_row:
self.draw_board(self.nw_canvas)
self.nw_canvas.delete('SCORE')
score_str = str(Status.score)
self.nw_canvas.create_text((15 * self.cell_size, 3 * (self.height / 4) ), text = score_str, font = ('Helvetica', 80, 'bold'), fill = 'DarkCyan', tag = 'SCORE')
def game_loop(self):
if Status.mouse_click == 1:
x, y = (self.screenwidth - self.width)/2 + 100 , (self.screenheight - self.height)/2 + 100
pyautogui.moveTo(x, y)
pyautogui.click()
Status.mouse_click += 1
Status.next_block_pre = self.generate_next_block()
Status.next_block = Status.next_block_pre
if Status.current_block is None:
Status.new_block = self.generate_new_block()
self.draw_block_move(self.nw_canvas, Status.new_block)
Status.current_block = Status.new_block
if not self.check_move(Status.current_block, [0, 0]):
tk.messagebox.showinfo("Game Over!","Your Score is %s " % Status.score)
self.tetris.win.destroy()
return
else:
if self.check_move(Status.current_block, [0, 1]):
self.draw_block_move(self.nw_canvas, Status.current_block, [0, 1])
else:
self.save_block_to_list(Status.current_block)
Status.flag_clear = True
Status.current_block = None
self.check_and_clear()
self.tetris.win.after(Status.FPS, self.game_loop)
(六)tetris_shape.py
class Tetris_shape:
SHAPES = {
"I": [(0, 1), (0, 0), (0, -1), (0, -2)],
"J": [(-1, 0), (0, 0), (0, -1), (0, -2)],
"L": [(-1, 0), (0, 0), (-1, -1), (-1, -2)],
"O": [(-1, -1), (0, -1), (-1, 0), (0, 0)],
"S": [(-1, 0), (0, 0), (0, -1), (1, -1)],
"T": [(-1, 0), (0, 0), (0, -1), (1, 0)],
"Z": [(-1, -1), (0, -1), (0, 0), (1, 0)],
}
SHAPESCOLOR = {
"I": "darksalmon",
"J": "sandybrown",
"L": "darkkhaki",
"O": "darkseagreen",
"S": "mediumpurple",
"T": "cornflowerblue",
"Z": "palevioletred",
}
那写看似毫无波澜的日复一日,会在某一天 让你突然发现努力的意义。
无悔昨天 & 感谢今天 & 喜欢明天~
一以贯之的努力,不得懈怠的人生。每天的微小积累,会决定最终的结果,这 就是答案!