diff --git a/source/gui/app.py b/source/gui/app.py index 488c7bd..2fe97ac 100644 --- a/source/gui/app.py +++ b/source/gui/app.py @@ -2,11 +2,11 @@ import tkinter as tk import os from functools import partial -from gui.base import AppGuiBase -from gui.welcome import Welcome -from gui.gameboard import Gameboard -from gui.leaderboard import Leaderboard -from gui.help import Help +from gui.base import ContextBase +from gui.welcome import WelcomeContext +from gui.gameboard import GameboardContext +from gui.leaderboard import LeaderboardContext +from gui.help import HelpContext from localization import localizer THEME_FILE = f"{os.getcwd()}\gui\Sun-Valley-ttk-theme\sun-valley.tcl" @@ -15,40 +15,45 @@ class Application(tk.Tk): """ - Wrapper around the main Tk instance. Handles context switching, window setup, etc. + Wrapper around the main Tk instance. Handles context switching, window setup, + theme management, etc. + + Holds an instance of all contexts. Contexts do not know about other contexts, + and so data should flow from a context <-> Application <-> other context if + required. """ def __init__(self) -> None: tk.Tk.__init__(self) # Set instance variables - self.current_context: AppGuiBase = None - self.previous_context: AppGuiBase = None + self.current_context: ContextBase = None + self.previous_context: ContextBase = None self.using_theme: str = DEFAULT_THEME if self.__has_themes_installed() else None - # Init all of the GUIs this app will use - self.welcome_screen = Welcome( + # Init all of the context GUIs this app will use + self.welcome_context = WelcomeContext( master=self, theme=self.using_theme, rows=3, columns=3, name=localizer.get("WELCOME_SCREEN"), ) - self.gameboard_screen = Gameboard( + self.gameboard_context = GameboardContext( master=self, theme=self.using_theme, rows=3, columns=3, name=localizer.get("GAMEBOARD_SCREEN"), ) - self.leaderboard_screen = Leaderboard( + self.leaderboard_context = LeaderboardContext( master=self, theme=self.using_theme, rows=3, columns=3, name=localizer.get("LEADERBOARD_SCREEN"), ) - self.help_screen = Help( + self.help_context = HelpContext( master=self, theme=self.using_theme, rows=3, @@ -57,61 +62,61 @@ def __init__(self) -> None: ) # Setup the GUIs commands - self.welcome_screen.set_theme_cmd(partial(self.__change_theme)) + self.welcome_context.set_theme_cmd(partial(self.__change_theme)) - self.welcome_screen.set_play_cmd( - partial(self.__switch_context, screen=self.gameboard_screen) + self.welcome_context.set_play_cmd( + partial(self.__switch_context, context=self.gameboard_context) ) - self.welcome_screen.set_highscores_cmd( + self.welcome_context.set_highscores_cmd( partial( self.__switch_context, - screen=self.leaderboard_screen, - # When switching to leaderboard from welcome screen: refresh scores, disable back btn, enable main menu btn + context=self.leaderboard_context, + # When switching to leaderboard from welcome: refresh scores, disable back btn, enable main menu btn funcs=( - self.leaderboard_screen.refresh_scores, - partial(self.leaderboard_screen.set_back_btn_state, enabled=False), + self.leaderboard_context.refresh_scores, + partial(self.leaderboard_context.set_back_btn_state, enabled=False), partial( - self.leaderboard_screen.set_mainmenu_btn_state, enabled=True + self.leaderboard_context.set_mainmenu_btn_state, enabled=True ), ), ) ) - self.leaderboard_screen.set_mainmenu_cmd( - partial(self.__switch_context, screen=self.welcome_screen) + self.leaderboard_context.set_mainmenu_cmd( + partial(self.__switch_context, context=self.welcome_context) ) - self.leaderboard_screen.set_back_cmd( + self.leaderboard_context.set_back_cmd( partial(self.__switch_context, previous=True) ) - self.gameboard_screen.set_mainmenu_cmd( - partial(self.__switch_context, screen=self.welcome_screen) + self.gameboard_context.set_mainmenu_cmd( + partial(self.__switch_context, context=self.welcome_context) ) - self.gameboard_screen.set_highscores_cmd( + self.gameboard_context.set_highscores_cmd( partial( self.__switch_context, - screen=self.leaderboard_screen, - # When switching to leaderboard from welcome screen: refresh scores, enable back btn, disable main menu btn + context=self.leaderboard_context, + # When switching to leaderboard from gameboard: refresh scores, enable back btn, disable main menu btn funcs=( - self.leaderboard_screen.refresh_scores, - partial(self.leaderboard_screen.set_back_btn_state, enabled=True), + self.leaderboard_context.refresh_scores, + partial(self.leaderboard_context.set_back_btn_state, enabled=True), partial( - self.leaderboard_screen.set_mainmenu_btn_state, enabled=False + self.leaderboard_context.set_mainmenu_btn_state, enabled=False ), ), ) ) - self.gameboard_screen.set_help_cmd( - partial(self.__switch_context, screen=self.help_screen) + self.gameboard_context.set_help_cmd( + partial(self.__switch_context, context=self.help_context) ) - self.help_screen.set_back_cmd(partial(self.__switch_context, previous=True)) + self.help_context.set_back_cmd(partial(self.__switch_context, previous=True)) # Set welcome screen as current context and try to import/use theme - self.__switch_context(self.welcome_screen) + self.__switch_context(self.welcome_context) self.__import_theme() self.update() @@ -139,13 +144,13 @@ def __change_theme(self): self.tk.call("set_theme", "dark") def __switch_context( - self, screen: AppGuiBase = None, previous: bool = False, funcs: tuple = None + self, context: ContextBase = None, previous: bool = False, funcs: tuple = None ): """ - Handles switching the context from one screen to another. + Handles switching the context from one context to another. - screen: the screen you wish to switch to - previous: pass true if you wish to return to previous screen. This will ignore screen param. + context: the context you wish to switch to + previous: pass true if you wish to return to previous context. This will ignore context param. funcs: optionally pass a tuple of functions to execute immediately after switching """ # Remove current context from the screen @@ -153,12 +158,12 @@ def __switch_context( self.current_context.pack_forget() # use previous context - if previous or not screen: - screen = self.previous_context + if previous or not context: + context = self.previous_context # Set previous context to current context and current context to requested screen self.previous_context = self.current_context - self.current_context = screen + self.current_context = context self.current_context.pack(fill="both", expand=True) # Execute functions from passed funcs if exists diff --git a/source/gui/base.py b/source/gui/base.py index d777362..4834d1b 100644 --- a/source/gui/base.py +++ b/source/gui/base.py @@ -2,30 +2,46 @@ import tkinter as tk -class AppGuiBase(ttk.Frame): +class ContextBase(ttk.Frame): """ - The base layout of the game window. + The base layout of a context. Each context represents an entirely different screen of + the app. Contexts do not know about other contexts. If a context needs to know some data + in a different context, it is preferred to have the main application use a getter on the + context with the needed data, and then to use a setter on the context which needs the data. + + This class should be inherited by all contexts. """ def __init__( - self, master, rows: int, columns: int, theme: str, name: str, *args, **kwargs + self, + master: tk.Tk, + rows: int, + columns: int, + theme: str, + name: str, + *args, + **kwargs ) -> None: ttk.Frame.__init__(self, master, *args, **kwargs) + # Instance variables self.name: str = name self.using_theme: str = theme self.num_rows: int = rows + self.num_columns: int = columns + self.app: tk.Tk = master # Reference back to application + + # Setup rows/columns for row in range(rows): self.rowconfigure(index=row, weight=1, minsize=100) - self.num_columns: int = columns for column in range(columns): self.columnconfigure(index=column, weight=1, minsize=150) if __name__ == "__main__": root = tk.Tk() - app = AppGuiBase(master=root, rows=3, columns=3) + app = ContextBase(master=root, rows=3, columns=3) app.pack(fill="both", expand=True) root.update() diff --git a/source/gui/gameboard.py b/source/gui/gameboard.py index f6e96e1..c848f0e 100644 --- a/source/gui/gameboard.py +++ b/source/gui/gameboard.py @@ -1,16 +1,16 @@ from tkinter import ttk, Tk, messagebox from functools import partial -from gui.base import AppGuiBase +from gui.base import ContextBase from localization import localizer -class Gameboard(AppGuiBase): +class GameboardContext(ContextBase): """ The main game window. Has the gameboard, all game buttons, etc. """ - def __init__(self, master, *args, **kwargs) -> None: - AppGuiBase.__init__(self, master, *args, **kwargs) + def __init__(self, *args, **kwargs) -> None: + ContextBase.__init__(self, *args, **kwargs) self.rowconfigure(index=0, weight=5, minsize=100) self.rowconfigure(index=1, weight=90, minsize=100) @@ -82,7 +82,7 @@ def set_help_cmd(self, command) -> None: if __name__ == "__main__": root = Tk() - app = Gameboard(master=root, rows=3, columns=3, theme=None, name="") + app = GameboardContext(master=root, rows=3, columns=3, theme=None, name="") app.pack(fill="both", expand=True) root.update() diff --git a/source/gui/help.py b/source/gui/help.py index 044caf9..4c7846a 100644 --- a/source/gui/help.py +++ b/source/gui/help.py @@ -1,15 +1,15 @@ from tkinter import ttk -from gui.base import AppGuiBase +from gui.base import ContextBase from functools import partial -class Help(AppGuiBase): +class HelpContext(ContextBase): """ Shows information about how this game works. """ - def __init__(self, master, *args, **kwargs) -> None: - AppGuiBase.__init__(self, master, *args, **kwargs) + def __init__(self, *args, **kwargs) -> None: + ContextBase.__init__(self, *args, **kwargs) self.label = ttk.Label( self, @@ -30,5 +30,5 @@ def set_back_cmd(self, command) -> None: if __name__ == "__main__": - app = Help(master=None) + app = HelpContext(master=None) app.mainloop() diff --git a/source/gui/leaderboard.py b/source/gui/leaderboard.py index cbd5935..6eee7c5 100644 --- a/source/gui/leaderboard.py +++ b/source/gui/leaderboard.py @@ -2,17 +2,17 @@ import tkinter as tk from functools import partial from turtle import st -from gui.base import AppGuiBase +from gui.base import ContextBase from localization import localizer -class Leaderboard(AppGuiBase): +class LeaderboardContext(ContextBase): """ Shows the top 10 scores. """ - def __init__(self, master, *args, **kwargs) -> None: - AppGuiBase.__init__(self, master, *args, **kwargs) + def __init__(self, *args, **kwargs) -> None: + ContextBase.__init__(self, *args, **kwargs) # Title self.title = ttk.Label( @@ -114,7 +114,7 @@ def refresh_scores(self) -> None: if __name__ == "__main__": root = tk.Tk() - app = Leaderboard(master=root, theme=None, rows=3, columns=3) + app = LeaderboardContext(master=root, theme=None, rows=3, columns=3) app.pack(fill="both", expand=True) root.update() diff --git a/source/gui/welcome.py b/source/gui/welcome.py index 47742fd..4936679 100644 --- a/source/gui/welcome.py +++ b/source/gui/welcome.py @@ -1,17 +1,17 @@ from tkinter import ttk import tkinter as tk from functools import partial -from gui.base import AppGuiBase +from gui.base import ContextBase from localization import localizer -class Welcome(AppGuiBase): +class WelcomeContext(ContextBase): """ Initial screen upon starting the game. """ - def __init__(self, master, *args, **kwargs) -> None: - AppGuiBase.__init__(self, master, *args, **kwargs) + def __init__(self, *args, **kwargs) -> None: + ContextBase.__init__(self, *args, **kwargs) self.use_dark_theme = tk.BooleanVar( value=True if self.using_theme == "dark" else False @@ -116,7 +116,7 @@ def set_highscores_cmd(self, command) -> None: if __name__ == "__main__": root = tk.Tk() - app = Welcome(master=root, rows=3, columns=3) + app = WelcomeContext(master=root, rows=3, columns=3) app.pack(fill="both", expand=True) root.update()