python GUI编程(3)2048
2019-04-17 本文已影响4人
Lykit01
这次我们用tkinter函数式编程来实现小游戏2048,下面是我的源码,还可以再优化:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tkinter as tk
import tkinter.messagebox as mb
import random,copy
root=tk.Tk()
root.title('2048')
root.geometry('360x330')
color_dict={0:'white'}
score_dict={}
scorevar=tk.StringVar()
board=[[0,0,0,0] for i in range(4)]
col=['grey','pink','yellow','brown','red','violet','crimson','fuchsia']*100
for num in range(1,100):
color_dict[2**num]=col[num-1]
score_dict[2**num]=num-1
#gui
def display(board):
for i in range(4):
for j in range(4):
lb=tk.Label(root,text='' if board[i][j]==0 else board[i][j],bg=color_dict[board[i][j]],width=7,height=3)
lb.place(x=(55+5)*(j+1),y=(55+5)*(i+1),anchor='nw')
#移动与计算
def available_dot(arr):
res=[]
for i in range(len(arr)):
for j in range(len(arr[0])):
if arr[i][j]==0:
res.append([i,j])
return res
def spown_dot(board,num):
sample=random.sample(available_dot(board),num)
for i in range(num):
x, y = sample[i]
board[x][y] = 2
def to_text(board):
text = ''
for i in range(len(board)):
for j in range(len(board[0])):
text += str(board[i][j]) + ' '
text += '\n'
return text
def can_move(board):
for i in range(1,len(board)-1):
for j in range(1,len(board[0])-1):
if board[i][j]==board[i-1][j] or board[i][j]==board[i+1][j] or board[i][j]==board[i][j-1] or board[i][j]==board[i][j+1]:
return True
if board[0][0]==board[0][1] or board[0][0]==board[1][0]:
return True
if board[3][0]==board[3][1] or board[3][0]==board[2][0]:
return True
if board[0][3]==board[1][3] or board[0][3]==board[0][2]:
return True
if board[3][3]==board[3][2] or board[3][3]==board[2][3]:
return True
if board[1][0]==board[2][0] or board[0][1]==board[0][2] or board[3][1]==board[3][2] or board[3][1]==board[3][2]:
return True
return False
def left_arrow(event):
last_board=copy.deepcopy(board)
for i in range(4):
tmp=[]
for j in range(4):
if board[i][j]!=0:
tmp.append(board[i][j])
for k in range(len(tmp)-1):
if tmp[k]!=0 and tmp[k]==tmp[k+1]:
tmp[k]*=2
get_score(tmp[k])
tmp=tmp[:k+1]+tmp[k+2:]+[0]
tmp+=[0]*(4-len(tmp))
board[i]=tmp
if last_board!=board:
spown_dot(board,1)
#var.set(to_text(board))
display(board)
elif not available_dot(board):#动不了时游戏结束
if not can_move(board):
mb.showinfo(title='游戏结束',message='得分:%s'%scorevar.get())
restart()
def right_arrow(event):
last_board=copy.deepcopy(board)
for i in range(4):
tmp=[]
for j in range(4):
if board[i][j]!=0:
tmp.append(board[i][j])
for k in range(len(tmp)-1,0,-1):
if tmp[k]!=0 and tmp[k]==tmp[k-1]:
tmp[k]*=2
get_score(tmp[k])
tmp=[0]+tmp[:k-1]+tmp[k:]
tmp=[0]*(4-len(tmp))+tmp
board[i]=tmp
if last_board!=board:
spown_dot(board,1)
#var.set(to_text(board))
display(board)
elif not available_dot(board):#动不了时游戏结束
if not can_move(board):
mb.showinfo(title='游戏结束',message='得分:%s'%scorevar.get())
restart()
def up_arrow(event):
last_board=copy.deepcopy(board)
for j in range(4):
tmp=[]
for i in range(4):
if board[i][j]!=0:
tmp.append(board[i][j])
for k in range(len(tmp)-1):
if tmp[k]!=0 and tmp[k]==tmp[k+1]:
tmp[k]*=2
get_score(tmp[k])
tmp=tmp[:k+1]+tmp[k+2:]+[0]
tmp += [0] * (4 - len(tmp))
for i in range(4):
board[i][j]=tmp[i]
if last_board!=board:
spown_dot(board,1)
#var.set(to_text(board))
display(board)
elif not available_dot(board):#动不了时游戏结束
if not can_move(board):
mb.showinfo(title='游戏结束',message='得分:%s'%scorevar.get())
restart()
def down_arrow(event):
last_board=copy.deepcopy(board)
for j in range(4):
tmp=[]
for i in range(4):
if board[i][j]!=0:
tmp.append(board[i][j])
for k in range(len(tmp)-1,0,-1):
if tmp[k]!=0 and tmp[k]==tmp[k-1]:
tmp[k]*=2
get_score(tmp[k])
tmp=[0]+tmp[:k-1]+tmp[k:]
tmp = [0] * (4 - len(tmp))+tmp
for i in range(4):
board[i][j]=tmp[i]
if last_board!=board:
spown_dot(board,1)
#var.set(to_text(board))
display(board)
elif not available_dot(board):#动不了时游戏结束
if not can_move(board):
mb.showinfo(title='游戏结束',message='得分:%s'%scorevar.get())
restart()
def get_score(num):
now_score=int(scorevar.get())
now_score+=score_dict[num]
scorevar.set(str(now_score))
def restart():#重新开始
global board
board=[[0,0,0,0] for i in range(4)]
spown_dot(board,2)
display(board)
scorevar.set(0)
#initiate
board=[[0,0,0,0] for i in range(4)]
spown_dot(board,2)
display(board)
scorevar.set(0)
#debug
#var=tk.StringVar()
#var.set(to_text(board))
#board_lb=tk.Label(root,textvariable=var,bg='darkcyan',fg='white',width=8,height=5)
board_lb=tk.Label(root)
board_lb.place(x=300,y=200,anchor='nw')
board_lb.focus_set()#只有当组件获得焦点的时候才能接收键盘事件
board_lb.bind('<Left>',left_arrow)
board_lb.bind('<Right>',right_arrow)
board_lb.bind('<Up>',up_arrow)
board_lb.bind('<Down>',down_arrow)
score_nt=tk.Label(root,text='得分',width=5,height=2)
score_nt.place(x=100,y=20,anchor='nw')
score_lb=tk.Label(root,textvariable=scorevar,width=5,height=2)
score_lb.place(x=140,y=20,anchor='nw')
maker_lb=tk.Label(root,text='@Hue Zhang',width=10,height=2)
maker_lb.place(x=180,y=320,anchor='center')
restartbtn=tk.Button(root,text='重新开始',bg='lightgrey',width=6,height=1,command=restart)
restartbtn.place(x=220,y=20,anchor='nw')
root.mainloop()
这种函数式编程中的global变量很难管理,很容易出错,我们换用一个高级的ui包PyQt5,下面用pyqt改写上面的程序,也增添了一些小功能。下面是pyqt5的教程:pyqt5的中文教程
我的源码:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import sys
from PyQt5.QtWidgets import (QMainWindow,QWidget,QPushButton,
QApplication,QDesktopWidget,qApp,QAction,QLabel,QMessageBox,QToolTip)
from PyQt5.QtGui import QIcon,QFont
from PyQt5.QtCore import Qt
import random,copy,math
class Game(QMainWindow):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
#other property
self.color_dict={}
self.score_dict={}
self.color_dict[0]='white'
col = ['lightgrey','grey', 'pink', 'yellow', 'brown', 'red', 'violet', 'crimson', 'fuchsia'] * 100
for num in range(1, 100):
self.color_dict[2 ** num] = col[num - 1]
self.score_dict[2 ** num] = num - 1
self.first2048=False#第一次2048
#main feature
self.font = QFont('SansSerif', 10)
QToolTip.setFont(self.font)
self.setGeometry(300, 300, 500, 550)
self.setWindowTitle("2048")
self.setWindowIcon(QIcon('./2p.ico'))
self.center()
self.setToolTip('<b>Made by @Hue Zhang</b>')
self.statusbar = self.statusBar()
self.statusbar.showMessage('按WASD来移动')
self.scorent=QLabel(self)
self.scorent.resize(50, 50)
self.scorent.move(80,10)
self.scorent.setFont(self.font)
self.scorent.setText('得分')
self.scoreboard = QLabel(self)
self.scoreboard.resize(100, 50)
self.scoreboard.move(180, 10)
self.scoreboard.setFont(self.font)
restartbtn = QPushButton('重新开始', self)
restartbtn.setToolTip('重新开始一局游戏')
restartbtn.resize(restartbtn.sizeHint())
restartbtn.move(280, 10)
restartbtn.clicked.connect(self.restart)
#self.wholetextboard=QLabel(self)#for debug
#self.wholetextboard.resize(120,120)
#self.wholetextboard.move(10000,10000)
#self.wholetextboard.setFont(self.font)
#初始化界面
self.lbs_init()#label不能覆盖
self.restart()
self.show()
def initBoard(self):
self.wholeboard=[[0,0,0,0] for i in range(4)]
self.spown_dot(2)
#self.wholetextboard.setText(self.board_to_text(self.wholeboard))
self.score=0
self.scoreboard.setText('0')
def restart(self):
self.initBoard()
self.lbs_update()
def lbs_init(self):
self.lbs = [[0, 0, 0, 0] for i in range(4)]
for i in range(4):
for j in range(4):
self.lbs[i][j] = QLabel(self)
self.lbs[i][j].setFont(self.font)
self.lbs[i][j].setAlignment(Qt.AlignCenter)#设置文字居中
self.lbs[i][j].resize(100, 100)
self.lbs[i][j].move(110 * j + 30, 110 *i+70)
def lbs_update(self):
for i in range(4):
for j in range(4):
if not self.first2048 and self.wholeboard[i][j]==2048:
self.first2048=True
congrats = QMessageBox.about(self, '恭喜!', '你已经达成2048成就!')
col=self.color_dict[self.wholeboard[i][j]]
if self.wholeboard[i][j] == 0:
txt=''
else:
txt=str(self.wholeboard[i][j])
self.lbs[i][j].setText(txt)
self.lbs[i][j].setStyleSheet('background-color:%s'%col)
def keyPressEvent(self,e):
self.statusbar.showMessage('按WASD来移动')
if e.key()==Qt.Key_Escape:
self.close()
elif e.key()==Qt.Key_W:
self.toUp()
elif e.key() == Qt.Key_S:
self.toDown()
elif e.key()==Qt.Key_A:#方向键暂时没找到方法获取焦点
self.toLeft()
elif e.key()==Qt.Key_D:
self.toRight()
def toLeft(self):
self.last_board=copy.deepcopy(self.wholeboard)
for i in range(4):
tmp = []
for j in range(4):
if self.wholeboard[i][j] != 0:
tmp.append(self.wholeboard[i][j])
for k in range(len(tmp) - 1):
if tmp[k] != 0 and tmp[k] == tmp[k + 1]:
tmp[k] *= 2
self.get_score(tmp[k])
tmp = tmp[:k + 1] + tmp[k + 2:] + [0]
tmp += [0] * (4 - len(tmp))
self.wholeboard[i] = tmp
self.check_board()
def toRight(self):
self.last_board=copy.deepcopy(self.wholeboard)
for i in range(4):
tmp = []
for j in range(4):
if self.wholeboard[i][j] != 0:
tmp.append(self.wholeboard[i][j])
for k in range(len(tmp) - 1, 0, -1):
if tmp[k] != 0 and tmp[k] == tmp[k - 1]:
tmp[k] *= 2
self.get_score(tmp[k])
tmp = [0] + tmp[:k - 1] + tmp[k:]
tmp = [0] * (4 - len(tmp)) + tmp
self.wholeboard[i] = tmp
self.check_board()
def toUp(self):
self.last_board = copy.deepcopy(self.wholeboard)
for j in range(4):
tmp = []
for i in range(4):
if self.wholeboard[i][j] != 0:
tmp.append(self.wholeboard[i][j])
for k in range(len(tmp) - 1):
if tmp[k] != 0 and tmp[k] == tmp[k + 1]:
tmp[k] *= 2
self.get_score(tmp[k])
tmp = tmp[:k + 1] + tmp[k + 2:] + [0]
tmp += [0] * (4 - len(tmp))
for i in range(4):
self.wholeboard[i][j] = tmp[i]
self.check_board()
def toDown(self):
self.last_board = copy.deepcopy(self.wholeboard)
for j in range(len(self.wholeboard)):
tmp = []
for i in range(len(self.wholeboard[0])):
if self.wholeboard[i][j] != 0:
tmp.append(self.wholeboard[i][j])
for k in range(len(tmp) - 1, 0, -1):
if tmp[k] != 0 and tmp[k] == tmp[k - 1]:
tmp[k] *= 2
self.get_score(tmp[k])
tmp = [0] + tmp[:k - 1] + tmp[k:]
tmp = [0] * (4 - len(tmp)) + tmp
for i in range(4):
self.wholeboard[i][j] = tmp[i]
self.check_board()
def check_board(self):
if self.last_board != self.wholeboard:
self.spown_dot(1)
#self.wholetextboard.setText(self.board_to_text(self.wholeboard))
self.lbs_update()
elif not self.available_dot(self.wholeboard): # 动不了时游戏结束
if not self.can_move(self.wholeboard):
ggabout = QMessageBox.about(self, 'Game Over', '你已经无法移动了,得分:%s,点击OK重新开始。'%self.score)
self.restart()
else:#无法向四个方向移动
self.statusbar.showMessage('无法合并,请尝试其他方向')
def board_to_text(self,board):
text = ''
for i in range(len(board)):
for j in range(len(board[0])):
text += str(board[i][j]) + ' '
text += '\n'
return text
def spown_dot(self,num):
sample = random.sample(self.available_dot(self.wholeboard), num)
for i in range(num):
x, y = sample[i]
self.wholeboard[x][y] = 2
def available_dot(self,arr):
res = []
for i in range(len(arr)):
for j in range(len(arr[0])):
if arr[i][j] == 0:
res.append([i, j])
return res
def can_move(self,board):
for i in range(1, len(board) - 1):
for j in range(1, len(board[0]) - 1):
if board[i][j] == board[i - 1][j] or board[i][j] == board[i + 1][j] or board[i][j] == board[i][j - 1] or \
board[i][j] == board[i][j + 1]:
return True
if board[0][0] == board[0][1] or board[0][0] == board[1][0]:
return True
if board[3][0] == board[3][1] or board[3][0] == board[2][0]:
return True
if board[0][3] == board[1][3] or board[0][3] == board[0][2]:
return True
if board[3][3] == board[3][2] or board[3][3] == board[2][3]:
return True
if board[1][0] == board[2][0] or board[0][1] == board[0][2] or board[3][1] == board[3][2] or board[3][1] == \
board[3][2]:
return True
return False
def get_score(self,score):
self.score+=self.score_dict[score]
self.scoreboard.setText(str(self.score))
def center(self):
qr=self.frameGeometry()
cp=QDesktopWidget().availableGeometry().center()
qr.moveCenter(cp)
self.move(qr.topLeft())
def closeEvent(self,event):
reply=QMessageBox.question(self,'提示','确认要退出吗?',
QMessageBox.Yes | QMessageBox.No,QMessageBox.No)
if reply==QMessageBox.Yes:
event.accept()
else:
event.ignore()
if __name__ == '__main__':
app=QApplication(sys.argv)
ex=Game()
sys.exit(app.exec_())