前言
- 最近初步学习了一下Python的tkinter库,花了一整天的时间写了一个我以前最喜欢的一个小游戏2048的电脑版,使用numpy简单地构建了2048的算法。在不断的debug中掌握tkinter的一些基本操作,最后效果还算满意,可以拿来无聊的时候消遣一下。但是用pyinstaller打包的时候打包了200多M(泪奔),还望哪位路过的大神不吝赐教,也希望对tkinter的初学者有所帮助,喜欢玩2048的小伙伴也可以自己运行一下玩一玩。由于是初学者,也是第一次使用class,如有错误或可以改进的地方还望指正,谢谢!
- 整个程序包括两个类,分别是游戏窗口和设置窗口,设置窗口通过松耦合来更改主窗口的参数,然而我不太清楚如何直接通过更改参数来使游戏窗口更新界面,所以就直接把原来的窗口给关闭掉然后重新打开一个窗口。程序中也参考了一些网上的教程,再次一并感谢了!
遇到的问题
- 程序打包大小问题
- label的大小不知道怎么控制,它会随字数的增加变大,最后只能放网上找的图片了,有图片就可以控制大小了
- tkinter各种部件的大小设定的度量单位真是捉摸不透
- tkinter的各种变量如String等的使用还不是很会,不知道是否可以通过改这些参数直接更改部件的参数
图片文件
- 程序运行的时候需要数字图标,我将其放在我的百度云里面,有需要的可以拿去放在程序的同级目录下程序就可以正常运行了。
- 网盘地址链接:https://pan.baidu.com/s/1ws5WSPIIoR9QEnFpaPeM4A 密码:yf24
程序
from tkinter import Toplevel, Tk, HORIZONTAL, RIGHT, Menu, IntVar, Button, Frame, Scale, Label, messagebox
from numpy import zeros, repeat, append, argwhere, concatenate, sum
from numpy.random import randint, choice
from PIL import Image, ImageTk
from random import sample
class Game2048():
def __init__(self):
self.root = Tk()
self.length = 5
self.size_label = 100
self.number_start = 4
self.step = 1
self.labels = globals()
self.frames = globals()
self.images = globals()
self.numbers = [0,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072]
self.new_game(distroy=False)
def resize(self,w_box, h_box, image_file):
pil_image = Image.open(image_file)
w, h = pil_image.size
f1 = 1.0 * w_box / w
f2 = 1.0 * h_box / h
factor = min([f1, f2])
width = int(w * factor)
height = int(h * factor)
pil_image_resized = pil_image.resize((width, height), Image.ANTIALIAS)
return ImageTk.PhotoImage(pil_image_resized)
def help(self):
messagebox.showinfo(title='Hi,这是你需要的帮助(^-^)',
message='''基本操作:使用键盘的上、下、左、右分别控制游戏的方向。\n回退操作:点击键盘的b(英文哦!)回到上一步,只有4次机会哦!\n设置操作:你可以在Edit下的Setting通过设置定制你的游戏哦!\n注意:如果重新开始一局游戏需要点击一下游戏框哦!''')
def success(self):
anser = messagebox.askokcancel('通关成功(^-^)', '是否重新开始?')
if anser:
self.new_game()
def fail(self):
anser = messagebox.askokcancel('游戏失败(-……-)', '是否重新开始?')
if anser:
self.new_game()
def new_game(self, distroy = True):
if distroy:
self.root.destroy()
self.root = Tk()
self.root.resizable(0,0)
self.root.title('2048游戏')
for num in self.numbers:
file = '.\\images\\' + str(num) + '.GIF'
self.images['img%d'%num] = self.resize(self.size_label, self.size_label, file)
self.root.bind("", self.sum_by_direction)
self.root.focus_set()
self.menubar = Menu(self.root)
self.filemenu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='Edit', menu=self.filemenu)
self.filemenu.add_command(label='Restart', command=self.new_game)
self.filemenu.add_command(label='Setting', command=self.setup_config)
self.filemenu.add_separator()
self.filemenu.add_command(label='Exit', command=self.root.quit)
self.helpmenu = Menu(self.menubar, tearoff=0)
self.menubar.add_cascade(label='Help', menu=self.helpmenu)
self.helpmenu.add_command(label='Show Help', command=self.help)
self.root.config(menu=self.menubar)
self.history_data = zeros((5, self.length, self.length))
arr = repeat(0,self.length**2)
arr[randint(0,self.length**2,self.number_start)] = choice([2,4],self.number_start)
self.data = arr.reshape(self.length,self.length)
self.history_data[0] = self.data
ord = 0
for i in range(self.length):
self.frames['fra%d'%i] = Frame(self.root)
self.frames['fra%d'%i].pack(side='top',expand='yes',fill='both')
for j in range(self.length):
number = self.data[i,j]
self.labels['lab%d'%ord] = Label(self.frames['fra%d'%i], text=number if number!=0 else '',
font = ("Helvetica 16 bold italic",20),
relief='ridge',
image = self.images['img%d'%number],
width = self.size_label, height=self.size_label)
self.labels['lab%d'%ord].pack(side='left',expand='YES',fill='both')
ord += 1
def print_data(self):
ord = 0
for i in range(self.length):
for j in range(self.length):
number = self.data[i,j]
self.labels['lab%d' % ord]['image'] = self.images['img%d'%number]
ord += 1
def sum_by_direction(self,event):
direction = event.keysym
data_old = self.data.copy()
if 131072 in self.data:
self.success()
return
if direction in ['Left', 'Up', 'Right', 'Down']:
if direction in ['Left','Up']:
self.data = self.data.T if direction=='Up' else self.data
for i in range(self.length):
set_data = [n for n in self.data[i,:] if n != 0]
set_len = len(set_data)
position = 0
while position1:
two = set_data[position:position+2]
if len(set(two))==1:
set_data[position:position+2] = [sum(two),0]
position += 1
not0 = [n for n in set_data if n != 0]
self.data[i,:] = append(not0, repeat(0, self.length-len(not0)))
self.data = self.data.T if direction=='Up' else self.data
elif direction in ['Right', 'Down']:
self.data = self.data.T if direction=='Down' else self.data
for i in range(self.length):
set_data = [n for n in self.data[i,:] if n != 0]
set_len = len(set_data)
position = set_len
while position>1:
two = set_data[position-2:position]
if len(set(two))==1:
set_data[position-2:position] = [0,sum(two)]
position -= 1
not0 = [n for n in set_data if n != 0]
self.data[i,:] = append(repeat(0, self.length-len(not0)), not0)
self.data = self.data.T if direction=='Down' else self.data
if (self.data != data_old).any():
position = argwhere(self.data == 0)
number_0 = position.shape[0]
number_new = self.step if number_0>self.step else 1
p = position[sample(range(position.shape[0]),number_new),:]
self.data[p[:,0],p[:,1]] = choice([2,4], number_new)
self.history_data = concatenate((self.data.reshape((1,self.length,self.length)),self.history_data[:4]))
elif 0 not in self.data:
self.fail()
return
if direction == 'b' and sum(self.history_data[1:])!=0:
self.data = self.history_data[1]
self.history_data = concatenate((self.history_data[1:], zeros((1,self.length,self.length))))
self.print_data()
def setup_config(self):
res = self.ask_userinfo()
if res is None: return
self.length, self.size_label,self.number_start, self.step = res
self.new_game(distroy=True)
def ask_userinfo(self):
current_parameter = [self.length, self.size_label,self.number_start, self.step]
setting_info = Setting(data=current_parameter)
self.root.wait_window(setting_info)
return setting_info.userinfo
class Setting(Toplevel):
def __init__(self, data):
super().__init__()
self.title('设置游戏参数')
self.length = IntVar()
self.length.set(data[0])
self.size = IntVar()
self.size.set(data[1])
self.start = IntVar()
self.start.set(data[2])
self.step = IntVar()
self.step.set(data[3])
self.setup_UI()
def setup_UI(self):
row1 = Frame(self)
row1.pack(fill="x")
scale_length = Scale(row1, label='每行每列的格子数', from_=2, to=20,
orient=HORIZONTAL, length=200, showvalue=1,
tickinterval=3, resolution=1, variable=self.length)
scale_length.set(self.length.get())
scale_length.pack(side='top',expand='YES',fill='both')
scale_size = Scale(row1, label='每个格子的大小', from_=10, to=200,
orient=HORIZONTAL, length=200, showvalue=1,
tickinterval=30, resolution=10, variable=self.size)
scale_size.set(self.size.get())
scale_size.pack(side='top',expand='YES',fill='both')
scale_start = Scale(row1, label='初始化数字格个数', from_=1, to=10,
orient=HORIZONTAL, length=200, showvalue=1,
tickinterval=2, resolution=1, variable=self.start)
scale_start.set(self.start.get())
scale_start.pack(side='top',expand='YES',fill='both')
scale_step = Scale(row1, label='每步添加的数字个数', from_=1, to=10,
orient=HORIZONTAL, length=200, showvalue=1,
tickinterval=2,resolution=1, variable=self.step)
scale_step.set(self.step.get())
scale_step.pack(side='top',expand='YES',fill='both')
row3 = Frame(self)
row3.pack(fill="x")
Button(row3, text="取消", command=self.cancel).pack(side=RIGHT)
Button(row3, text="确定", command=self.ok).pack(side=RIGHT)
def ok(self):
self.userinfo = [self.length.get(), self.size.get(),
self.start.get(), self.step.get()]
self.destroy()
def cancel(self):
self.userinfo = None
self.destroy()
if __name__ == '__main__':
game = Game2048()
game.root.mainloop()
运行界面效果图