pyecs

pyecs.git
git clone git://git.lenczewski.org/pyecs.git
Log | Files | Refs | README | LICENSE

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