Main
Main file of Chess game entirely made in python with properly working game engine. Contains a lot of customization option for user. App allows using custom assets, fonts and colors. Comes with easy to use menu to change the properties and readable config file.
Libraries used:
- customtkinter
- threading
- os
- sys
- platform
- configparser
- Pillow [PIL]
- re [regex]
- fontTools
- subprocess
- pywinstyles
- built in libraries
- sounddevice
- soundfile
Link to full documentation: http://chessdocumentation.ct.ws/
1"""Main file of Chess game entirely made in python with properly working game engine. Contains a lot of customization option for user. 2App allows using custom assets, fonts and colors. Comes with easy to use menu to change the properties and readable config file. 3 4Libraries used: 5 - customtkinter 6 - threading 7 - os 8 - sys 9 - platform 10 - configparser 11 - Pillow [PIL] 12 - re [regex] 13 - fontTools 14 - subprocess 15 - pywinstyles 16 - built in libraries 17 - sounddevice 18 - soundfile 19 20Link to full documentation: http://chessdocumentation.ct.ws/ 21""" 22 23import customtkinter as ctk 24import os 25import threading 26from concurrent.futures import ThreadPoolExecutor 27 28from tools import resource_path, get_from_config 29from properties import COLOR, SYSTEM 30from menus import MovesRecord, Options 31from board import Board 32 33class MainWindow(ctk.CTk): 34 """Main class handling the app. Setting size, minimum size, font loading, icon setting, 35 updating font, updating assets and restarting the game all happens here. 36 37 Args: 38 ctk.CTk : Main app window of customtkinter library (master). 39 """ 40 def __init__(self) -> None: 41 """Constructor for the MainWindow class: 42 - sets title 43 - sets geometry 44 - sets minimum size of the window 45 - loads font 46 - Creates instances of the classes: 47 - MoveRecord 48 - Options 49 - Board 50 - loads theme of the app from the config: get_from_config 51 """ 52 super().__init__(fg_color=COLOR.BACKGROUND) 53 self.title('Chess') 54 self.geometry(self.set_window_size()) 55 size: int = int(get_from_config('size')) 56 self.minsize(((size + 2) * 10 + 40)+ 400, ((size + 2) * 10 + 40)) 57 self.load_font() 58 self.moves_record: MovesRecord = MovesRecord(self) 59 self.moves_record.pack(side=ctk.RIGHT, padx=10, pady=10, fill=ctk.Y) 60 self.options: Options = Options(self, self.restart_game, self.update_assets, self.update_font, self.get_board) 61 self.options.pack(side=ctk.LEFT, padx=10, pady=10, fill=ctk.Y) 62 self.board: Board = Board(self, self.moves_record, size) 63 self.theme: str = str(get_from_config('theme')) 64 self.set_icon() 65 66 def load_font(self) -> None: 67 """Function loads font independently on the users operating system. 68 """ 69 font: str = str(get_from_config('font_file_name')) 70 if SYSTEM == 'Windows': 71 ctk.FontManager.windows_load_font(resource_path(os.path.join('fonts', font))) 72 else: 73 ctk.FontManager.load_font(resource_path(os.path.join('fonts', font))) 74 75 def set_icon(self) -> None: 76 """Only for windows machines logo icon will be set due to lack of implementation for linux and mac. 77 """ 78 if SYSTEM == 'Windows': 79 self.iconbitmap(resource_path(os.path.join('assets', 'logo.ico'))) 80 81 def set_window_size(self) -> str: 82 """Calculates the size necessary to display all elements of the app on the screen. 83 84 Returns: 85 str: f'{width}x{height]}' because customtkinter uses f'{width}x{height]}' to set the size of the window. 86 """ 87 size: int = int(get_from_config('size')) 88 size = (size+2) * 10 + 40 89 center_pos: str = f'+{(self.winfo_screenwidth()-(int(size*1.5)))//2}+{(self.winfo_screenheight()-(int(size)*1.1))//2}' 90 return f'{size + 300}x{size}{center_pos}' 91 92 def restart_game(self) -> None: 93 """Helper function for game restart just by calling functions from Board and MoveRecord classes. 94 """ 95 self.board.restart_game() 96 self.moves_record.restart() 97 98 def update_assets(self) -> None: 99 """Updates asset on the Board using thread to avoid window freezing. 100 """ 101 for row in self.board.board: 102 for cell in row: 103 if cell.figure: 104 threading.Thread(target=cell.figure.update_image).start() 105 106 def get_board(self) -> Board: 107 """Getter for board object. 108 109 Returns: 110 Board: Board object. 111 """ 112 return self.board 113 114 def update_font(self, widget=None) -> None: 115 """Helper function updating the font during app runtime without freezing the window. 116 117 Args: 118 widget (Any, optional): Child of the widget. Defaults to None as master widget doesn't have any parents. 119 """ 120 if widget is None: 121 widget = self 122 self.load_font() 123 def thread_task(): 124 widgets = [] 125 queue = [widget] 126 while queue: 127 current_widget = queue.pop() 128 widgets.append(current_widget) 129 queue.extend(current_widget.winfo_children()) 130 with ThreadPoolExecutor() as executor: 131 for child in widgets: 132 if isinstance(child, ctk.CTkLabel | ctk.CTkButton): 133 size = child.cget('font').cget('size') 134 executor.submit(self.__update_font_on_main_thread, child, size) 135 threading.Thread(target=thread_task, daemon=True).start() 136 137 def __update_font_on_main_thread(self, widget, size: int) -> None: 138 self.after(0, lambda: widget.configure(font=ctk.CTkFont(get_from_config('font_name'), size))) 139 140if __name__ == "__main__": 141 ctk.deactivate_automatic_dpi_awareness() 142 app = MainWindow() 143 app.mainloop()
34class MainWindow(ctk.CTk): 35 """Main class handling the app. Setting size, minimum size, font loading, icon setting, 36 updating font, updating assets and restarting the game all happens here. 37 38 Args: 39 ctk.CTk : Main app window of customtkinter library (master). 40 """ 41 def __init__(self) -> None: 42 """Constructor for the MainWindow class: 43 - sets title 44 - sets geometry 45 - sets minimum size of the window 46 - loads font 47 - Creates instances of the classes: 48 - MoveRecord 49 - Options 50 - Board 51 - loads theme of the app from the config: get_from_config 52 """ 53 super().__init__(fg_color=COLOR.BACKGROUND) 54 self.title('Chess') 55 self.geometry(self.set_window_size()) 56 size: int = int(get_from_config('size')) 57 self.minsize(((size + 2) * 10 + 40)+ 400, ((size + 2) * 10 + 40)) 58 self.load_font() 59 self.moves_record: MovesRecord = MovesRecord(self) 60 self.moves_record.pack(side=ctk.RIGHT, padx=10, pady=10, fill=ctk.Y) 61 self.options: Options = Options(self, self.restart_game, self.update_assets, self.update_font, self.get_board) 62 self.options.pack(side=ctk.LEFT, padx=10, pady=10, fill=ctk.Y) 63 self.board: Board = Board(self, self.moves_record, size) 64 self.theme: str = str(get_from_config('theme')) 65 self.set_icon() 66 67 def load_font(self) -> None: 68 """Function loads font independently on the users operating system. 69 """ 70 font: str = str(get_from_config('font_file_name')) 71 if SYSTEM == 'Windows': 72 ctk.FontManager.windows_load_font(resource_path(os.path.join('fonts', font))) 73 else: 74 ctk.FontManager.load_font(resource_path(os.path.join('fonts', font))) 75 76 def set_icon(self) -> None: 77 """Only for windows machines logo icon will be set due to lack of implementation for linux and mac. 78 """ 79 if SYSTEM == 'Windows': 80 self.iconbitmap(resource_path(os.path.join('assets', 'logo.ico'))) 81 82 def set_window_size(self) -> str: 83 """Calculates the size necessary to display all elements of the app on the screen. 84 85 Returns: 86 str: f'{width}x{height]}' because customtkinter uses f'{width}x{height]}' to set the size of the window. 87 """ 88 size: int = int(get_from_config('size')) 89 size = (size+2) * 10 + 40 90 center_pos: str = f'+{(self.winfo_screenwidth()-(int(size*1.5)))//2}+{(self.winfo_screenheight()-(int(size)*1.1))//2}' 91 return f'{size + 300}x{size}{center_pos}' 92 93 def restart_game(self) -> None: 94 """Helper function for game restart just by calling functions from Board and MoveRecord classes. 95 """ 96 self.board.restart_game() 97 self.moves_record.restart() 98 99 def update_assets(self) -> None: 100 """Updates asset on the Board using thread to avoid window freezing. 101 """ 102 for row in self.board.board: 103 for cell in row: 104 if cell.figure: 105 threading.Thread(target=cell.figure.update_image).start() 106 107 def get_board(self) -> Board: 108 """Getter for board object. 109 110 Returns: 111 Board: Board object. 112 """ 113 return self.board 114 115 def update_font(self, widget=None) -> None: 116 """Helper function updating the font during app runtime without freezing the window. 117 118 Args: 119 widget (Any, optional): Child of the widget. Defaults to None as master widget doesn't have any parents. 120 """ 121 if widget is None: 122 widget = self 123 self.load_font() 124 def thread_task(): 125 widgets = [] 126 queue = [widget] 127 while queue: 128 current_widget = queue.pop() 129 widgets.append(current_widget) 130 queue.extend(current_widget.winfo_children()) 131 with ThreadPoolExecutor() as executor: 132 for child in widgets: 133 if isinstance(child, ctk.CTkLabel | ctk.CTkButton): 134 size = child.cget('font').cget('size') 135 executor.submit(self.__update_font_on_main_thread, child, size) 136 threading.Thread(target=thread_task, daemon=True).start() 137 138 def __update_font_on_main_thread(self, widget, size: int) -> None: 139 self.after(0, lambda: widget.configure(font=ctk.CTkFont(get_from_config('font_name'), size)))
Main class handling the app. Setting size, minimum size, font loading, icon setting, updating font, updating assets and restarting the game all happens here.
Arguments:
- ctk.CTk : Main app window of customtkinter library (master).
41 def __init__(self) -> None: 42 """Constructor for the MainWindow class: 43 - sets title 44 - sets geometry 45 - sets minimum size of the window 46 - loads font 47 - Creates instances of the classes: 48 - MoveRecord 49 - Options 50 - Board 51 - loads theme of the app from the config: get_from_config 52 """ 53 super().__init__(fg_color=COLOR.BACKGROUND) 54 self.title('Chess') 55 self.geometry(self.set_window_size()) 56 size: int = int(get_from_config('size')) 57 self.minsize(((size + 2) * 10 + 40)+ 400, ((size + 2) * 10 + 40)) 58 self.load_font() 59 self.moves_record: MovesRecord = MovesRecord(self) 60 self.moves_record.pack(side=ctk.RIGHT, padx=10, pady=10, fill=ctk.Y) 61 self.options: Options = Options(self, self.restart_game, self.update_assets, self.update_font, self.get_board) 62 self.options.pack(side=ctk.LEFT, padx=10, pady=10, fill=ctk.Y) 63 self.board: Board = Board(self, self.moves_record, size) 64 self.theme: str = str(get_from_config('theme')) 65 self.set_icon()
Constructor for the MainWindow class:
- sets title
- sets geometry
- sets minimum size of the window
- loads font
- Creates instances of the classes:
- MoveRecord
- Options
- Board
- loads theme of the app from the config: get_from_config
67 def load_font(self) -> None: 68 """Function loads font independently on the users operating system. 69 """ 70 font: str = str(get_from_config('font_file_name')) 71 if SYSTEM == 'Windows': 72 ctk.FontManager.windows_load_font(resource_path(os.path.join('fonts', font))) 73 else: 74 ctk.FontManager.load_font(resource_path(os.path.join('fonts', font)))
Function loads font independently on the users operating system.
76 def set_icon(self) -> None: 77 """Only for windows machines logo icon will be set due to lack of implementation for linux and mac. 78 """ 79 if SYSTEM == 'Windows': 80 self.iconbitmap(resource_path(os.path.join('assets', 'logo.ico')))
Only for windows machines logo icon will be set due to lack of implementation for linux and mac.
82 def set_window_size(self) -> str: 83 """Calculates the size necessary to display all elements of the app on the screen. 84 85 Returns: 86 str: f'{width}x{height]}' because customtkinter uses f'{width}x{height]}' to set the size of the window. 87 """ 88 size: int = int(get_from_config('size')) 89 size = (size+2) * 10 + 40 90 center_pos: str = f'+{(self.winfo_screenwidth()-(int(size*1.5)))//2}+{(self.winfo_screenheight()-(int(size)*1.1))//2}' 91 return f'{size + 300}x{size}{center_pos}'
Calculates the size necessary to display all elements of the app on the screen.
Returns:
str: f'{width}x{height]}' because customtkinter uses f'{width}x{height]}' to set the size of the window.
93 def restart_game(self) -> None: 94 """Helper function for game restart just by calling functions from Board and MoveRecord classes. 95 """ 96 self.board.restart_game() 97 self.moves_record.restart()
Helper function for game restart just by calling functions from Board and MoveRecord classes.
99 def update_assets(self) -> None: 100 """Updates asset on the Board using thread to avoid window freezing. 101 """ 102 for row in self.board.board: 103 for cell in row: 104 if cell.figure: 105 threading.Thread(target=cell.figure.update_image).start()
Updates asset on the Board using thread to avoid window freezing.
107 def get_board(self) -> Board: 108 """Getter for board object. 109 110 Returns: 111 Board: Board object. 112 """ 113 return self.board
Getter for board object.
Returns:
Board: Board object.
115 def update_font(self, widget=None) -> None: 116 """Helper function updating the font during app runtime without freezing the window. 117 118 Args: 119 widget (Any, optional): Child of the widget. Defaults to None as master widget doesn't have any parents. 120 """ 121 if widget is None: 122 widget = self 123 self.load_font() 124 def thread_task(): 125 widgets = [] 126 queue = [widget] 127 while queue: 128 current_widget = queue.pop() 129 widgets.append(current_widget) 130 queue.extend(current_widget.winfo_children()) 131 with ThreadPoolExecutor() as executor: 132 for child in widgets: 133 if isinstance(child, ctk.CTkLabel | ctk.CTkButton): 134 size = child.cget('font').cget('size') 135 executor.submit(self.__update_font_on_main_thread, child, size) 136 threading.Thread(target=thread_task, daemon=True).start()
Helper function updating the font during app runtime without freezing the window.
Arguments:
- widget (Any, optional): Child of the widget. Defaults to None as master widget doesn't have any parents.