class RunExperiment(SceneBase): """ Scene that shows the progress of an experiment with some summary stats """ def __init__(self, experiment): SceneBase.__init__(self) # data to keep track of the experiment's progress self.experiment = experiment self.experiment_running = False self.total = experiment.iterations # constants defining where stuff can be drawn self.MARGIN = 96 self.TITLE_SIZE = 56 self.LABEL_SIZE = 48 self.INFO_BOX = pygame.Rect(self.MARGIN, self.MARGIN * 1.5, 1920 - self.MARGIN * 2, 1080 * 0.66 - 2 * self.MARGIN) self.PROGRESS_BAR_WIDTH = 1920 - 2 * self.MARGIN self.PROGRESS_BAR_HEIGHT = 100 self.PROGRESS_BAR_X = self.MARGIN self.PROGRESS_BAR_Y = self.INFO_BOX.y + self.INFO_BOX.height + 0.5 * self.PROGRESS_BAR_HEIGHT self.PROGRESS_BORDER = pygame.Rect(self.PROGRESS_BAR_X, self.PROGRESS_BAR_Y, self.PROGRESS_BAR_WIDTH, self.PROGRESS_BAR_HEIGHT) self.PROGRESS_BG = pygame.Rect(self.PROGRESS_BAR_X + 4, self.PROGRESS_BAR_Y + 4, self.PROGRESS_BAR_WIDTH - 8, self.PROGRESS_BAR_HEIGHT - 8) self.title_font = FontService.get_regular_font(self.TITLE_SIZE) self.label_font = FontService.get_regular_font(self.LABEL_SIZE) font_color = Settings.theme['font'] self.heading_surface = self.title_font.render( "%s vs %s" % (self.experiment.p1.name, self.experiment.p2.name), False, font_color) self.heading_size = self.title_font.size( "%s vs %s" % (self.experiment.p1.name, self.experiment.p2.name)) self.heading_location = (self.INFO_BOX.centerx - 0.5 * self.heading_size[0], self.INFO_BOX.top - 1.5 * self.heading_size[1]) self.insertion_msg_surface = self.label_font.render( "Saving results... (This may take some time)", False, font_color) self.insertion_msg_size = self.title_font.size( "Saving results... (This may take some time)") self.insertion_msg_location = (self.INFO_BOX.centerx - 0.5 * self.insertion_msg_size[0], self.PROGRESS_BG.bottom + 12) # "Done" button def back_to_mm(): SceneManager.go_to_main_menu(self) self.done_btn = Button(1920 * 0.5 - 150, self.PROGRESS_BG.bottom + 24, 300, 100, "Done", back_to_mm) # unset SQLite objects from the main thread DB.close() # kick off the experiment in a separate thread def run_experiment(): # Initialize SQLite objects in this thread DB.init() self.experiment.run() DB.close() experiment_thread = threading.Thread(target=run_experiment) experiment_thread.start() def run_experiment(): # Initialize SQLite objects in this thread DB.init() self.experiment.run() DB.close() experiment_thread = threading.Thread(target=run_experiment) experiment_thread.start() def process_input(self, events, pressed_keys): for widget in self.widgets: widget.process_input(events, pressed_keys) if self.experiment.finished: self.done_btn.process_input(events, pressed_keys) def update(self): pass def render(self, screen): bg = ImageService.get_game_bg() screen.blit(bg, (0, 0)) aa_border_rounded_rect(screen, self.INFO_BOX, Settings.theme['widget_background'], Settings.theme['widget_highlight'], radius=0.1) screen.blit(self.heading_surface, self.heading_location) # draw progress bar progress = self.experiment.completed_iterations / self.experiment.iterations pygame.draw.rect(screen, Settings.theme['widget_highlight'], self.PROGRESS_BORDER) pygame.draw.rect(screen, Settings.theme['widget_background'], self.PROGRESS_BG) progress_bar = self.PROGRESS_BG.copy() progress_bar.width = floor(self.PROGRESS_BG.width * progress) pygame.draw.rect(screen, Settings.theme['primary'], progress_bar) # draw labels for num wins/losses num_wins_surface = self.label_font.render( "Wins for X : %s Games" % self.experiment.p1_wins, False, Settings.theme['font']) num_wins_size = self.label_font.size("X has won : %s Games" % self.experiment.p1_wins) num_wins_location = (self.INFO_BOX.x + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.25 - num_wins_size[1] * 0.5) screen.blit(num_wins_surface, num_wins_location) num_ties_surface = self.label_font.render( "Number of Ties : %s Games" % self.experiment.ties, False, Settings.theme['font']) num_ties_size = self.label_font.size("Number of Ties : %s Games" % self.experiment.ties) num_ties_location = (self.INFO_BOX.x + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.50 - num_ties_size[1] * 0.5) screen.blit(num_ties_surface, num_ties_location) num_losses_surface = self.label_font.render( "Wins for O : %s Games" % self.experiment.p2_wins, False, Settings.theme['font']) num_losses_size = self.label_font.size("Wins for O : %s Games" % self.experiment.p2_wins) num_losses_location = (self.INFO_BOX.x + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.75 - num_losses_size[1] * 0.5) screen.blit(num_losses_surface, num_losses_location) # draw labels for win/tie/loss rates win_rate = round(self.experiment.get_p1_win_rate() * 100) loss_rate = round(self.experiment.get_p2_win_rate() * 100) tie_rate = round(self.experiment.get_tie_rate() * 100) num_wins_surface = self.label_font.render( "X Win Percentage : %s %%" % win_rate, False, Settings.theme['font']) num_wins_size = self.label_font.size("X Win Percentage: %s %%" % win_rate) num_wins_location = (self.INFO_BOX.centerx + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.25 - num_wins_size[1] * 0.5) screen.blit(num_wins_surface, num_wins_location) num_ties_surface = self.label_font.render( "Tied Percentage : %s %%" % tie_rate, False, Settings.theme['font']) num_ties_size = self.label_font.size("Tied Percentage: %s %%" % tie_rate) num_ties_location = (self.INFO_BOX.centerx + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.50 - num_ties_size[1] * 0.5) screen.blit(num_ties_surface, num_ties_location) num_losses_surface = self.label_font.render( "X Loss Percentage : %s %%" % loss_rate, False, Settings.theme['font']) num_losses_size = self.label_font.size("X Loss Percentage: %s %%" % loss_rate) num_losses_location = (self.INFO_BOX.centerx + 48, self.INFO_BOX.y + self.INFO_BOX.height * 0.75 - num_losses_size[1] * 0.5) screen.blit(num_losses_surface, num_losses_location) progress_text = "%s %%" % round(progress * 100) progress_text_surface = self.label_font.render(progress_text, False, Settings.theme['font']) progress_text_size = self.label_font.size(progress_text) progress_text_location = (progress_bar.centerx - 0.5 * progress_text_size[0], progress_bar.centery - 0.5 * progress_text_size[1]) screen.blit(progress_text_surface, progress_text_location) if self.experiment.completed_iterations == self.experiment.iterations and not self.experiment.finished: screen.blit(self.insertion_msg_surface, self.insertion_msg_location) if self.experiment.finished: self.done_btn.render(screen) for widget in self.widgets: widget.render(screen)
class SettingsScreen(Screen): def __init__(self, render_surface, surface_size): Screen.__init__(self, render_surface, surface_size) self.checkboxes = { "randomize_kana": { "checkbox": Checkbox( self.render_surface, (600, 450, 720, 100), "Randomize Kana", ).set_themed(), "setting": "randomize_kana", }, } self.kana_widgets = { "hiragana": { "checkbox": Checkbox( self.render_surface, (200, 600, 700, 100), "Learn Hiragana", ).set_themed(), "button": Button( self.render_surface, (1000, 600, 700, 100), "Select which kana", ).set_themed(), "setting": "learn_hiragana", }, "katakana": { "checkbox": Checkbox( self.render_surface, (200, 750, 700, 100), "Learn Katakana", ).set_themed(), "button": Button( self.render_surface, (1000, 750, 700, 100), "Select which kana", ).set_themed(), "setting": "learn_katakana", }, "kanji": { "checkbox": Checkbox( self.render_surface, (200, 900, 700, 100), "Learn Kanji", ).set_themed(), "button": Button( self.render_surface, (1000, 900, 700, 100), "Kanji options", ).set_themed(), "setting": "learn_kanji", }, } # set the 'selected' property of the checkboxes and kana_widgets for checkbox_widget_id, checkbox_widget in self.checkboxes.items(): checkbox = checkbox_widget["checkbox"] checkbox.selected = Settings.get(checkbox_widget["setting"]) for kana_widget_id, kana_widget in self.kana_widgets.items(): checkbox = kana_widget["checkbox"] checkbox.selected = Settings.get(kana_widget["setting"]) theme = Settings.get("theme") themes = Settings.get("themes") self.widgets = { "heading_settings": Heading( self.render_surface, (0, 0, 1920, 100), "Settings" ).set_themed(), "button_menu": Button( self.render_surface, (10, 10, 230, 80), "Menu" ).set_themed(), "theme_text": Text( self.render_surface, (500, 150, 920, 100), f"Current theme: '{theme}'", ).set_themed(), } # get the theme index of the current theme if theme in themes: self.theme_index = list(themes.keys()).index(theme) else: # somehow the theme in the settings file isn't available, reset print(f"theme {theme} is invalid! resetting to default") self.theme_index = 0 Settings.set("theme", next(iter(themes))) Collections.reapply_theme() # theme related widgets theme_name = list(themes.keys())[self.theme_index] apply_theme_text = f"Apply theme: {theme_name}" self.theme_left = Button( self.render_surface, (300, 275, 100, 100), "<" ).set_themed() self.theme_apply = Button( self.render_surface, (500, 275, 920, 100), apply_theme_text ).set_themed() self.theme_right = Button( self.render_surface, (1520, 275, 100, 100), ">" ).set_themed() def update(self, delta_time): Screen.update(self, delta_time) def key_press(self, event): Screen.key_press(self, event) def mouse_event(self, event): Screen.mouse_event(self, event) if event.type == pygame.MOUSEBUTTONUP: if self.widgets["button_menu"].rect_hit(event.pos): return {"screen_id": "menuscreen"} for kana_widget_id, kana_widget in self.kana_widgets.items(): checkbox = kana_widget["checkbox"] # check whether the kana checkboxes are hit checkbox.on_mouse_release(event.pos) if checkbox.rect_hit(event.pos): # checkbox is hit, save the new setting Settings.set(kana_widget["setting"], checkbox.selected) # check whether the buttons are hit if kana_widget["button"].rect_hit(event.pos): return {"screen_id": f"optionsfor{kana_widget_id}"} for checkbox_widget_id, checkbox_widget in self.checkboxes.items(): checkbox = checkbox_widget["checkbox"] checkbox.on_mouse_release(event.pos) if checkbox.rect_hit(event.pos): # checkbox is hit, save the new setting Settings.set(checkbox_widget["setting"], checkbox.selected) # theme switcher related buttons if self.theme_left.rect_hit(event.pos): self.update_theme_index(-1) if self.theme_right.rect_hit(event.pos): self.update_theme_index(1) if self.theme_apply.rect_hit(event.pos): # get the theme from the index and save the new theme theme = list(Settings.get("themes").keys())[self.theme_index] Settings.set("theme", theme) # update the text on the current theme widget self.widgets["theme_text"].set_text( f"Current theme: '{theme}'" ) # reapply the (new) theme to the GUI Collections.reapply_theme() def draw(self): Screen.draw(self) for widget_id, widget in self.widgets.items(): widget.render() for kana_widget_id, kana_widget in self.kana_widgets.items(): kana_widget["checkbox"].render() if kana_widget["checkbox"].selected: kana_widget["button"].render() for checkbox_widget_id, checkbox_widget in self.checkboxes.items(): checkbox_widget["checkbox"].render() self.theme_left.render() self.theme_apply.render() self.theme_right.render() def update_theme_index(self, change): themes = Settings.get("themes") last_theme_index = len(themes) - 1 # update and bounds check the theme index index = self.theme_index + change index = last_theme_index if index < 0 else index index = 0 if index > last_theme_index else index # update the class' theme index and the text self.theme_index = index apply_theme_text = f"Apply theme: {list(themes.keys())[index]}" self.theme_apply.set_text(apply_theme_text)