platform.py (7317B)
1 import tkinter as tk 2 3 from base64 import b64encode 4 from typing import List, Optional 5 6 from .common import * 7 from .ecs.components import Lives, Score 8 9 10 __all__ = ['Screen'] 11 12 13 def load_image(fpath, **kwargs) -> Optional[tk.PhotoImage]: 14 try: 15 with open(fpath, 'rb') as f: 16 data = f.read() 17 return tk.PhotoImage(data=b64encode(data), **kwargs) 18 except Exception as err: 19 critical(err) 20 return None 21 22 23 class Screen(): 24 """ 25 Abstraction over the raw tkinter canvas. 26 """ 27 def __init__(self, root, **kwargs): 28 self.root = root 29 self.canvas = tk.Canvas(root, 30 width=WIDTH, 31 height=HEIGHT, 32 bg=BLACK, 33 **kwargs) 34 self.canvas.pack() 35 36 self.fps = self.canvas.create_text( 37 5, 5, anchor='nw', fill=WHITE) 38 self.objects = self.canvas.create_text( 39 5, 20, anchor='nw', fill=WHITE) 40 self.score = self.canvas.create_text( 41 WIDTH - 5, 5, anchor='ne', fill=WHITE) 42 self.lives = self.canvas.create_text( 43 WIDTH - 5, 20, anchor='ne', fill=WHITE) 44 45 self._tracked_entity = -1 46 self._tracked_score = 0 47 self._tracked_lives = 0 48 self.player_name = 'John Doe' 49 50 self.boss_image = self.canvas.create_image(1, 1, anchor='nw', 51 state='hidden', tags='boss-key-img') 52 self.boss_image_shown = False 53 54 self.boss_image_data = load_image(BOSS_KEY_IMAGE_FPATH) 55 if self.boss_image_data is not None: 56 debug('Loaded boss-key image') 57 self.update(self.boss_image, image=self.boss_image_data) 58 59 self.menu_shown = False 60 61 self.menu_bg = self.canvas.create_rectangle( 62 WIDTH * 0.25, HEIGHT * 0.25, 63 WIDTH * 0.75, HEIGHT * 0.75, 64 fill=BLACK, state='hidden', tags='menu') 65 66 menu_title_fontsize = 13 67 self.menu_title = self.canvas.create_text( 68 WIDTH * 0.5, HEIGHT * 0.25 + menu_title_fontsize, 69 text='Paused', font=menu_title_fontsize, anchor='center', 70 fill=WHITE, state='hidden', tags='menu') 71 72 self.menu_quit_btn_callback = lambda: None 73 self.menu_quit_btn = self.canvas.create_window( 74 WIDTH * 0.3, HEIGHT * 0.3, 75 width=WIDTH * 0.4, height=20, 76 window=tk.Button(self.canvas, text='Quit', bg=RED, 77 command=lambda: self.menu_quit_btn_callback()), 78 anchor='nw', state='hidden', tag='menu') 79 80 self.menu_save_btn_callback = lambda: self.save_state() 81 self.menu_save_btn = self.canvas.create_window( 82 WIDTH * 0.3, HEIGHT * 0.3 + 30, 83 width=WIDTH * 0.4, height=20, 84 window=tk.Button(self.canvas, text='Save', bg=RED, 85 command=lambda: self.menu_save_btn_callback()), 86 anchor='nw', state='hidden', tag='menu') 87 88 self.controls_text = self.canvas.create_text( 89 WIDTH * 0.3, HEIGHT * 0.4, 90 width=WIDTH * 0.4, text=HELP_CONTENTS, fill=WHITE, 91 anchor='nw', state='hidden', tag='menu') 92 93 94 def tick(self, dt: float, manager): 95 if self._tracked_entity != -1: 96 self._tracked_lives = manager.fetch_component( 97 self._tracked_entity, Lives.cid).count 98 self._tracked_score = manager.fetch_component( 99 self._tracked_entity, Score.cid).count 100 101 self.canvas.itemconfig(self.score, 102 text=f'SCORE: {self._tracked_score:9}') 103 self.canvas.itemconfig(self.lives, 104 text=f'LIVES: {self._tracked_lives:9}') 105 106 if dt != 0: 107 self.canvas.itemconfig(self.fps, text=f'FPS: {1/dt:9.3f}') 108 109 self.canvas.itemconfig(self.objects, 110 text=f'OBJ: {len(manager.entities):9}') 111 112 self.root.update() 113 114 115 def set_tracked_entity(self, entity): 116 self._tracked_entity = entity 117 118 119 def get_score(self): 120 return self._tracked_score 121 122 123 def get_lives(self): 124 return self._tracked_lives 125 126 127 def get_name(self): 128 return self.player_name 129 130 131 def draw_text(self, x, y, content, **kwargs): 132 return self.canvas.create_text(x, y, text=content, **kwargs) 133 134 135 def draw_image(self, x, y, **kwargs): 136 return self.canvas.create_image(x, y, **kwargs) 137 138 139 def draw_poly(self, vertices: List[int], **kwargs): 140 return self.canvas.create_polygon(*vertices, **kwargs) 141 142 143 def rect_vertices(self, sx, sy): 144 x0, y0 = -(sx // 2), -(sy // 2) 145 x1, y1 = sx // 2, sy // 2 146 147 return [ 148 x0, y0, 149 x1, y0, 150 x1, y1, 151 x0, y1 152 ] 153 154 155 def raise_tag(self, tag): 156 self.canvas.tag_raise(tag) 157 158 159 def lower_tag(self, tag): 160 self.canvas.tag_lower(tag) 161 162 163 def update(self, handle, *args, **kwargs): 164 self.canvas.itemconfig(handle, *args, **kwargs) 165 166 167 def get_coords(self, handle, **kwargs): 168 return self.canvas.coords(handle) 169 170 171 def set_coords(self, handle, coords: List[int], **kwargs): 172 self.canvas.coords(handle, *coords, **kwargs) 173 174 175 def remove(self, handle): 176 self.canvas.delete(handle) 177 178 179 def remove_all(self): 180 self.canvas.delete(tk.ALL) 181 182 183 def set_event_handler(self, event, handler): 184 self.root.bind(event, handler) 185 186 187 def set_proto_handler(self, protocol, handler): 188 self.root.protocol(protocol, handler) 189 190 191 def set_quit_handler(self, callback): 192 self.menu_quit_btn_callback = callback 193 194 195 def do_after(self, seconds, callback): 196 self.canvas.after(int(seconds * 1000), callback) 197 198 199 def save_state(self): 200 save_gamestate(STATE_FPATH, self._tracked_score, self._tracked_lives) 201 202 203 def toggle_boss_image(self): 204 self.raise_tag('boss-key-img') 205 if self.boss_image_shown: 206 self.update(self.boss_image, state='hidden') 207 self.boss_image_shown = False 208 else: 209 self.update(self.boss_image, state='normal') 210 self.boss_image_shown = True 211 212 213 def toggle_menu(self): 214 self.raise_tag('menu') 215 216 menu_elements = self.canvas.find_withtag('menu') 217 218 if self.menu_shown: 219 for element in menu_elements: 220 self.update(element, state='hidden') 221 222 self.menu_shown = False 223 else: 224 for element in menu_elements: 225 self.update(element, state='normal') 226 227 self.menu_shown = True 228 229 230 def toggle_gameover(self, finished_callback): 231 self.draw_text(WIDTH / 2, HEIGHT / 2, 'GAME OVER', font='20', fill=RED, 232 anchor='center') 233 234 highscore_window = tk.Toplevel(self.root) 235 highscore_window.title('Enter your name') 236 237 name_variable = tk.StringVar() 238 name_variable.set(self.player_name) 239 240 name_entry = tk.Entry(highscore_window, textvariable=name_variable) 241 name_entry.pack() 242 243 def submit(): 244 self.player_name = name_variable.get() 245 finished_callback() 246 247 finish_btn = tk.Button(highscore_window, text='Submit', command=submit) 248 finish_btn.pack() 249 250 251 def destroy(self): 252 self.root.destroy() 253