python-tkinter学习实例
在好友的邀请下,尝试用tkinter做一个卡牌的普通界面显示,正好练习下python的写法。
花了两天学习,写了两天代码,做了个最基本的demo。显示如下:
其中需要引入的第三方库主要有,PIL、PILLOW,其它的都是直接引入即可。
1.1、这里贴下下载地址,以WINDOWS系统为例:
PILwin32:https://files.cnblogs.com/files/pcat/PILwin32.zip
PILwin64:https://files.cnblogs.com/files/pcat/PILwin64.zip
1.2、windows安装Pillow
pip install Pillow
或者在http://www.lfd.uci.edu/~gohlke/pythonlibs/#pillow 下载wheel文件使用pip安装
程序中用到的图片主要有:
需要图片资源的小伙伴可以自行上git上下载:
https://github.com/ishikota/PyPokerGUI
pypokergui--server--static
贴上程序的源码,仅供参考:
1 # -*- coding: utf-8 -*- 2 from Tkinter import * 3 from PIL import Image,ImageTk 4 5 import Image,math,random,time,sched 6 import threading as thd 7 class card2: 8 #定义全局变量 9 #总牌数 10 num_list = ['3', '4', '5', '6', '7', '8', '9','T', 'J','Q','K', 'A','2'] 11 # 全局变量纪录玩家当前要打出的牌数字 12 #当前牌面字符串 13 str_num = ''; 14 #当前牌面字符串在num_list中的位置 15 index_num = -1; 16 # 中间桌面上牌的大小 17 mid_num = -2; 18 #判断是否会生成重复的牌 19 check_list = []; 20 #记录当前回合数 21 round_num = 1; 22 #当前玩家手中牌位置 23 cur_position = 0; 24 #定义玩家当前手中牌的集合 25 player_card_list = []; 26 player1_card_list = []; 27 player2_card_list = []; 28 player3_card_list = []; 29 player4_card_list = []; 30 31 #变量判断玩家身份,默认为玩家 32 cur_identify = 1; 33 34 def __init__(self): 35 self.tk = Tk() 36 #tk.geometry('1200x1600') 37 self.tk.title('A Easy Card Game') 38 39 self.img = Image.open('images/poker_pot.png') 40 self.w_box = 120 41 self.h_box = 120 42 self.w, self.h = self.img.size 43 # w,h = img2.size 44 # 缩放图像让它保持比例,同时限制在一个矩形框范围内 45 self.img_resized = self.resize(self.w, self.h, self.w_box, self.h_box, self.img) 46 self.photo = ImageTk.PhotoImage(self.img_resized) 47 # frm_TL = Frame(bg = 'WHITE',width = 300, height = 200) 48 self.frm_T = Frame(bg='white', width=300, height=200) 49 self.frm_L = Frame(bg='white', width=300, height=400) 50 self.frm_M = Frame(bg='white', width=200, height=400) 51 self.frm_R = Frame(bg='white', width=300, height=400) 52 self.frm_B = Frame(bg='white', width=400, height=200) 53 # frm_TL.grid(row=0,column=0) 54 self.frm_T.grid(row=0, column=1) 55 self.frm_L.grid(row=1, column=0) 56 self.frm_M.grid(row=1, column=1) 57 self.frm_R.grid(row=1, column=2) 58 self.frm_B.grid(row=2, column=1) 59 # Top 60 Label(self.frm_T, text="Player3", font=('Arial', 16)).grid(column=1, sticky=N) 61 Button(self.frm_T, text="重新发牌", command=self.replay, width=8, height=1, 62 font=('Arial', 16)).grid(row=0, column=3) 63 self.frm_T_label_L = Label(self.frm_T, image=self.photo) 64 self.frm_T_label_L.image = self.photo 65 self.frm_T_label_L.grid(row=1, column=0, sticky=E) 66 # frm_T_label_L.grid(row=1,column=1,columnspan=2,rowspan=2,sticky=W+E+N+S,padx=5,pady=5) 67 self.frm_T_label_M = Label(self.frm_T, image=self.photo) 68 self.frm_T_label_M.image = self.photo 69 self.frm_T_label_M.grid(row=1, column=1,padx=10, sticky=E) 70 self.frm_T_label_R = Label(self.frm_T, image=self.photo) 71 self.frm_T_label_R.image = self.photo 72 self.frm_T_label_R.grid(row=1, column=2, sticky=E) 73 74 # Left 75 Label(self.frm_L, text="Player4", font=('Arial', 16)).grid(row=1, column=0, sticky=N) 76 self.frm_L_label_T = Label(self.frm_L, image=self.photo) 77 self.frm_L_label_T.image = self.photo 78 self.frm_L_label_T.grid(row=0, column=1, sticky=E) 79 self.frm_L_label_T = Label(self.frm_L, image=self.photo) 80 self.frm_L_label_T.image = self.photo 81 self.frm_L_label_T.grid(row=1, column=1, sticky=E) 82 self.frm_L_label_T = Label(self.frm_L, image=self.photo) 83 self.frm_L_label_T.image = self.photo 84 self.frm_L_label_T.grid(row=2, column=1, sticky=E) 85 86 # Middle 87 self.frm_M_label_T = Label(self.frm_M, image=self.photo) 88 self.frm_M_label_T.image = self.photo 89 self.frm_M_label_T.grid(row=1, column=0, sticky=E) 90 self.frm_M_text = Text(self.frm_M, width=30, height=6, bg='Green', font=('Verdana', 15)) 91 92 #self.frm_M_text.insert('1.0', 'this is '+str(self.round_num)+' round\n') 93 #self.frm_M_text.insert(END, 'last round') 94 self.frm_M_text.grid(row=2, column=0, columnspan=2) 95 96 # Right frm_R 97 self.frm_R_label_T = Label(self.frm_R, image=self.photo) 98 self.frm_R_label_T.image = self.photo 99 self.frm_R_label_T.grid(row=0, column=0, sticky=E) 100 self.frm_R_label_T = Label(self.frm_R, image=self.photo) 101 self.frm_R_label_T.image = self.photo 102 self.frm_R_label_T.grid(row=1, column=0, sticky=E) 103 self.frm_R_label_T = Label(self.frm_R, image=self.photo) 104 self.frm_R_label_T.image = self.photo 105 self.frm_R_label_T.grid(row=2, column=0, sticky=E) 106 Label(self.frm_R, text="Player2", font=('Arial', 16)).grid(row=1, column=1, sticky=N) 107 108 # 初始化玩家手中牌 109 self.load_sys() 110 111 self.main_start() 112 113 self.frm_B_label_T_4 = Label(self.frm_B, image=self.photo) 114 self.frm_B_label_T_4.image = self.photo 115 self.frm_B_label_T_4.grid(row=0, column=3, sticky=E) 116 Label(self.frm_B, text="Player1", font=('Arial', 16)).grid(row=1, column=1, sticky=N) 117 Button(self.frm_B, text="出牌", command=self.discard, width=6, height=1, 118 font=('Arial', 16)).grid(row=1, column=3) 119 Button(self.frm_B, text="PASS", command=self.player_pass, width=6, height=1, 120 font=('Arial', 16)).grid(row=1, column=4) 121 122 def resize(self,w, h, w_box, h_box, pil_image): 123 ''''' 124 resize a pil_image object so it will fit into 125 a box of size w_box times h_box, but retain aspect ratio 126 对一个pil_image对象进行缩放,让它在一个矩形框内,还能保持比例 127 ''' 128 129 f1 = 1.0 * w_box / w # 1.0 forces float division in Python2 130 f2 = 1.0 * h_box / h 131 factor = min([f1, f2]) 132 # print(f1, f2, factor) # test 133 # use best down-sizing filter 134 width = int(w * factor) 135 height = int(h * factor) 136 return pil_image.resize((width, height), Image.ANTIALIAS) 137 def replay(self): 138 self.load_sys() 139 140 #初始化玩家手中牌 141 def load_sys(self): 142 # 初始化当前玩家手中牌集合,先重置 143 self.player1_card_list = [] 144 self.player2_card_list = [] 145 self.check_list = [] 146 self.cur_identify = 1 147 for i in range(3): 148 num = random.randint(0,12) 149 #判断是否生成重复的牌,去重 150 while self.check_list.__contains__(num): 151 num = random.randint(0, 12) 152 cur_str = self.num_list[num] 153 photo_current = ImageTk.PhotoImage(self.resize(self.w, self.h, self.w_box, self.h_box, Image.open('images/card_C' + str(cur_str) + '.png'))) 154 frm_B_label_T_1 = Label(self.frm_B, image=photo_current) 155 frm_B_label_T_1.image = photo_current 156 frm_B_label_T_1.grid(row=0, column=i,sticky=E) 157 frm_B_label_T_1.bind('<ButtonRelease-1>', self.handlerAdaptor(self.choose, cur_str,i)) 158 self.check_list.append(num) 159 self.player1_card_list.append(cur_str) 160 #生成玩家2手中的牌 161 num2 = random.randint(0, 12) 162 # 判断是否生成重复的牌,去重 163 while self.check_list.__contains__(num2): 164 num2 = random.randint(0, 12) 165 cur_str2 = self.num_list[num2] 166 self.check_list.append(num2) 167 self.player2_card_list.append(cur_str2) 168 169 print(self.player1_card_list) 170 print(self.player2_card_list) 171 #记录当前轮数并递增 172 self.frm_M_text.insert('1.0', 'this is ' + str(self.round_num) + ' round\n') 173 self.round_num+=1 174 175 # 清空桌面上的牌 176 frm_M_label_T = Label(self.frm_M, image=self.photo) 177 frm_M_label_T.image = self.photo 178 frm_M_label_T.grid(row=1, column=0, sticky=E) 179 # 重置玩家手中当前牌 180 frm_B_label_T_4 = Label(self.frm_B, image=self.photo) 181 frm_B_label_T_4.image = self.photo 182 frm_B_label_T_4.grid(row=0, column=3, sticky=E) 183 #重置桌面上牌的大小 184 self.mid_num = -2 185 # Bottom 186 def choose(self,event,a,position): 187 self.frm_M_text.insert('1.0','Now Your Card Is C'+str(a)+'\n') 188 photo_cur = ImageTk.PhotoImage(self.resize(self.w, self.h, self.w_box, self.h_box, Image.open('images/card_C' + str(a) + '.png'))) 189 frm_B_label_T_4 = Label(self.frm_B, image=photo_cur) 190 frm_B_label_T_4.image = photo_cur 191 frm_B_label_T_4.grid(row=0, column=3, sticky=E) 192 self.str_num = a 193 self.cur_position = position 194 def handlerAdaptor(self,fun, *kwds): 195 '''''事件处理函数的适配器,相当于中介,此处接收2个参数,那个event是从那里来的呢,我也纳闷,这也许就是python的伟大之处吧''' 196 return lambda event,fun=fun,kwds=kwds: fun(event, *kwds) 197 def discard(self): 198 if self.cur_identify == 1: 199 if self.str_num != '': 200 self.index_num = self.num_list.index(self.str_num) 201 if self.index_num == -1: 202 self.frm_M_text.insert('1.0','请选择一张牌\n') 203 #return '请选择一张牌' 204 #判断要打出的牌与桌面上的牌大小 205 elif self.index_num <= self.mid_num: 206 self.frm_M_text.insert('1.0', '请选择一张大牌\n') 207 # return '请选择一张大牌' 208 else: 209 photo_mid = ImageTk.PhotoImage(self.resize(self.w, self.h, self.w_box, self.h_box, Image.open('images/card_C' + str(self.str_num) + '.png'))) 210 frm_M_label_T = Label(self.frm_M, image=photo_mid) 211 frm_M_label_T.image = photo_mid 212 frm_M_label_T.grid(row=1, column=0, sticky=E) 213 #清空玩家手中当前牌 214 frm_B_label_T_4 = Label(self.frm_B, image=self.photo) 215 frm_B_label_T_4.image = self.photo 216 frm_B_label_T_4.grid(row=0, column=3, sticky=E) 217 #frm_B_label_T_4.grid_forget() 218 #给当前桌面牌赋值 219 self.mid_num = self.index_num 220 #重置玩家手中牌 221 frm_B_label_T_X = Label(self.frm_B, image=self.photo) 222 frm_B_label_T_X.image = self.photo 223 frm_B_label_T_X.grid(row=0, column=self.cur_position, sticky=E) 224 self.frm_M_text.insert('1.0', 'Player1 Play The Card!'+ str(self.str_num) +'\n') 225 self.player1_card_list.remove(self.str_num) 226 if self.player1_card_list.__len__() == 0: 227 self.frm_M_text.insert('1.0', 'Congratulations! Player1 Win The Game!\n') 228 return 229 #当玩家打出牌后,切换到电脑2,调用电脑出牌方法 230 self.cur_identify +=1 231 self.discard_pc(self.cur_identify) 232 else: 233 self.frm_M_text.insert('1.0', '请等待其它玩家出牌\n') 234 235 #没有可出的牌,切换到下一个对手 236 def player_pass(self): 237 # 重置中间牌的大小 238 self.mid_num = -2 239 #切换玩家 240 self.cur_identify += 1 241 self.discard_pc(self.cur_identify) 242 243 # def player_pc(self,iden): 244 # if iden == 2: 245 # self.player_card_list = self.player2_card_list 246 # 247 # if iden ==3: 248 # self.cur_identify = 1 249 250 def discard_pc(self,iden): 251 card_list_pc = [] 252 if iden == 2: 253 for i in range(self.player2_card_list.__len__()): 254 #将当前牌转化成索引号进行比较大小 255 self.index_pc_num = self.num_list.index(self.player2_card_list[i]) 256 257 if self.index_pc_num > self.mid_num: 258 card_list_pc.append(self.player2_card_list[i]) 259 card_list_pc.sort() 260 if len(card_list_pc) > 0: 261 self.index_pc_num_temp = self.num_list.index(card_list_pc[0]) 262 self.mid_num = self.index_pc_num_temp 263 photo_mid = ImageTk.PhotoImage(self.resize(self.w, self.h, self.w_box, self.h_box, 264 Image.open('images/card_C' + str(card_list_pc[0]) + '.png'))) 265 frm_M_label_T = Label(self.frm_M, image=photo_mid) 266 frm_M_label_T.image = photo_mid 267 frm_M_label_T.grid(row=1, column=0, sticky=E) 268 self.frm_M_text.insert('1.0', 'Player2 Play The Card!' + str(card_list_pc[0]) + '\n') 269 self.player2_card_list.remove(card_list_pc[0]) 270 271 if self.player2_card_list.__len__() == 0: 272 self.frm_M_text.insert('1.0', 'Congratulations! Player2 Win The Game!\n') 273 return 274 # 当玩家打出牌后,切换到电脑2,调用电脑出牌方法 275 self.cur_identify += 1 276 if self.cur_identify == 3: 277 self.cur_identify = 1 278 elif len(card_list_pc) == 0: 279 self.frm_M_text.insert('1.0', '没有大过玩家的牌,请玩家出牌\n') 280 #重置中间牌的大小 281 self.mid_num = -2 282 self.cur_identify = 1 283 284 # # 实例化一个sched对象 285 # schedule = sched.scheduler(time.time, time.sleep) 286 def show_time(self): 287 cur_time = time.strftime('%Y-%m-%d %X',time.localtime()) 288 top_time_label = Label(self.frm_T, text=cur_time, font=('Arial', 16)) 289 top_time_label.grid(column=2, sticky=N) 290 while True: 291 cur_time = time.strftime('%Y-%m-%d %X', time.localtime()) 292 top_time_label.configure(text=cur_time) 293 time.sleep(1) 294 def main_start(self): 295 thd.Timer(0, self.show_time).start() 296 def main(): 297 d = card2() 298 mainloop() 299 300 if __name__ == '__main__': 301 main()
注意:调整图片大小需要用到resize()方法,PNG图片需要用到ImageTk.PhotoImage(self.img_resized)转化方可显示。
增加了定时任务显示当前时间,每秒切换一次,注意不要占用主线程。
提出几个方向以待大神们能完善:
2.1、程序中只启用了player2与玩家互动,可以尝试启用4个。
2.2、只选择了一组牌进行显示,可以尝试加入4组牌,用file文件引入数据。
2.3、不同花色的相同数字比较可以用3.1、3.2、3.3、3.4浮点数比较大小。
2.4、图片显示可以增加更多,遗留的问题是图片重叠显示,感觉用tkinter的grid方式无法实现,可能自己水平有限。