Mercurial > touhou
diff pytouhou/menu.py @ 568:e7a4731a278b
Add a GTK+ main menu, mimicking the original EoSD one.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 01 Jul 2014 23:18:15 +0200 |
parents | |
children | f0252cf111d8 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/pytouhou/menu.py @@ -0,0 +1,305 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2014 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from pytouhou.utils.helpers import get_logger +logger = get_logger(__name__) + +try: + from gi.repository import Gtk, Gdk +except ImportError: + logger.error('GTK+ unavailable, disabling the GUI menu.') + raise + +import sys +import re + +GL_VERSION_REGEX = re.compile(r'^\d\.\d$') + + +class Handler(object): + def __init__(self, config, args): + self.config = config + self.args = args + + def init_gtk(self, builder): + self.start_window = builder.get_object('start_window') + self.game_window = builder.get_object('game_window') + self.options_window = builder.get_object('options_window') + + self.replay_filechooserdialog = builder.get_object('replay_filechooserdialog') + + # Game widgets + self.difficulty_box = builder.get_object('difficulty_box') + self.character_box = builder.get_object('character_box') + self.stage_box = builder.get_object('stage_box') + self.boss_rush_checkbutton = builder.get_object('boss_rush_checkbutton') + + self.difficulty_comboboxtext = builder.get_object('difficulty_comboboxtext') + self.character_comboboxtext = builder.get_object('character_comboboxtext') + self.stage_comboboxtext = builder.get_object('stage_comboboxtext') + + # Options widgets + self.path_filechooserbutton = builder.get_object('path_filechooserbutton') + self.backend_comboboxtext = builder.get_object('backend_comboboxtext') + + # OpenGL backend + self.opengl_grid = builder.get_object('opengl_grid') + self.flavor_comboboxtext = builder.get_object('flavor_comboboxtext') + self.version_entry = builder.get_object('version_entry') + self.double_buffer_checkbutton = builder.get_object('double_buffer_checkbutton') + + # SDL backend + self.sdl_grid = builder.get_object('sdl_grid') + + self.fps_entry = builder.get_object('fps_entry') + self.no_background_checkbutton = builder.get_object('no_background_checkbutton') + self.no_particles_checkbutton = builder.get_object('no_particles_checkbutton') + self.no_sound_checkbutton = builder.get_object('no_sound_checkbutton') + + self.difficulty_comboboxtext.set_active_id(str(self.args.rank)) + self.character_comboboxtext.set_active_id(str(self.args.character)) + self.stage_comboboxtext.set_active_id(str(self.args.stage)) + self.boss_rush_checkbutton.set_active(self.args.boss_rush) + + self.path_filechooserbutton.set_filename(self.args.path) + self.backend_comboboxtext.set_active_id(' '.join(self.args.backend)) + + self.flavor_comboboxtext.set_active_id(self.args.gl_flavor) + self.version_entry.set_text(str(self.args.gl_version)) + if self.args.double_buffer is None: + self.double_buffer_checkbutton.set_inconsistent(True) + else: + self.double_buffer_checkbutton.set_inconsistent(False) + self.double_buffer_checkbutton.set_active(self.args.double_buffer) + + self.fps_entry.set_text(str(self.args.fps_limit)) + self.no_background_checkbutton.set_active(self.args.no_background) + self.no_particles_checkbutton.set_active(self.args.no_particles) + self.no_sound_checkbutton.set_active(self.args.no_sound) + + def hide_and_play(self, window): + window.hide() + Gtk.main_quit() + print('Play!') + + def on_quit(self, *args): + Gtk.main_quit(*args) + sys.exit(0) + + + # Main menu + + def on_start_window_key_press_event(self, window, event_key): + if (event_key.keyval == Gdk.KEY_Escape or + event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q): + self.on_quit() + + def on_start_button_clicked(self, _): + self.difficulty_box.show() + self.character_box.show() + self.stage_box.hide() + + self.stage_comboboxtext.set_active_id(None) + self.args.stage = None + + self.start_window.hide() + self.game_window.show() + + def on_extra_start_button_clicked(self, _): + self.difficulty_box.hide() + self.character_box.show() + self.stage_box.hide() + + self.difficulty_comboboxtext.set_active_id('4') + self.stage_comboboxtext.set_active_id('7') + + self.start_window.hide() + self.game_window.show() + + def on_practice_start_button_clicked(self, _): + self.difficulty_box.show() + self.character_box.show() + self.stage_box.show() + + self.start_window.hide() + self.game_window.show() + + def on_options_button_clicked(self, _): + self.start_window.hide() + self.options_window.show() + + def on_replay_button_clicked(self, _): + self.start_window.hide() + self.replay_filechooserdialog.show() + + def on_inactive_button_clicked(self, _): + raise NotImplementedError('Menu not implemented') + + on_netplay_start_button_clicked = on_inactive_button_clicked + on_score_button_clicked = on_inactive_button_clicked + on_music_room_button_clicked = on_inactive_button_clicked + + + # Game menu + + def on_game_back_button_clicked(self, _): + self.game_window.hide() + self.start_window.show() + + def on_play_button_clicked(self, _): + self.hide_and_play(self.game_window) + + def on_game_window_key_press_event(self, window, event_key): + if event_key.keyval == Gdk.KEY_Escape: + self.game_window.hide() + self.start_window.show() + elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: + self.on_quit() + + def on_difficulty_comboboxtext_changed(self, item): + active = item.get_active_id() + difficulty = int(active) + self.config.set('rank', active if difficulty < 4 else None) + self.args.rank = difficulty + + def on_character_comboboxtext_changed(self, item): + character = int(item.get_active_id()) + self.config.set('character', character) + self.args.character = character + + def on_stage_comboboxtext_changed(self, item): + stage = item.get_active_id() + self.args.stage = int(stage) if stage is not None else None + + def on_boss_rush_checkbutton_toggled(self, boss_rush_checkbutton): + active = boss_rush_checkbutton.get_active() + self.config.set('boss-rush', active) + self.args.boss_rush = active + + + # Replay dialog + + def on_replay_filechooserdialog_close(self, _): + self.replay_filechooserdialog.hide() + self.start_window.show() + + on_replay_cancel_button_clicked = on_replay_filechooserdialog_close + + def on_replay_open_button_clicked(self, button): + try: + open(self.args.replay, 'rb').close() + except (IOError, TypeError): + return + self.hide_and_play(self.replay_filechooserdialog) + + def on_replay_filechooserdialog_selection_changed(self, dialog): + self.args.replay = dialog.get_filename() + + def on_replay_filechooserdialog_file_activated(self, window): + self.args.replay = window.get_filename() + self.hide_and_play(self.replay_filechooserdialog) + + def on_replay_filechooserdialog_key_press_event(self, window, event_key): + if event_key.keyval == Gdk.KEY_Escape: + self.replay_filechooserdialog.hide() + self.start_window.show() + elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: + self.on_quit() + + + # Options menu + + def on_options_back_button_clicked(self, _): + self.options_window.hide() + self.start_window.show() + + def on_options_window_key_press_event(self, window, event_key): + if event_key.keyval == Gdk.KEY_Escape: + self.options_window.hide() + self.start_window.show() + elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: + self.on_quit() + + def on_path_filechooserbutton_file_set(self, path_filechooserbutton): + path = path_filechooserbutton.get_filename() + self.config.set('path', path) + + def on_backend_comboboxtext_changed(self, backend_comboboxtext): + active = backend_comboboxtext.get_active_id() + self.config.set('backend', active) + backends = active.split() + new_grids = [getattr(self, backend + '_grid') for backend in backends] + for grid in [self.opengl_grid, self.sdl_grid]: + if grid not in new_grids: + grid.hide() + else: + grid.show() + + def on_flavor_comboboxtext_changed(self, flavor_comboboxtext): + active = flavor_comboboxtext.get_active_id() + self.config.set('gl-flavor', active) + + def on_version_entry_changed(self, version_entry): + text = version_entry.get_text() + if not GL_VERSION_REGEX.match(text): + raise ValueError('ā%sā is not <digit>.<digit>' % text) + self.config.set('gl-version', text) + + def on_double_buffer_checkbutton_clicked(self, double_buffer_checkbutton): + inconsistent = double_buffer_checkbutton.get_inconsistent() + active = double_buffer_checkbutton.get_active() + if inconsistent: + active = False + inconsistent = False + elif active: + active = True + else: + inconsistent = True + double_buffer_checkbutton.set_active(active) + double_buffer_checkbutton.set_inconsistent(inconsistent) + self.config.set('double-buffer', None if inconsistent else active) + + def on_fps_entry_changed(self, fps_entry): + text = fps_entry.get_text() + try: + int(text) + except ValueError: + raise ValueError('ā%sā is not integer' % text) + else: + self.config.set('fps-limit', text) + + def on_no_background_checkbutton_toggled(self, checkbutton): + active = checkbutton.get_active() + self.config.set('no-background', not active) + + def on_no_particles_checkbutton_toggled(self, checkbutton): + active = checkbutton.get_active() + self.config.set('no-particles', active) + + def on_no_sound_checkbutton_toggled(self, checkbutton): + active = checkbutton.get_active() + self.config.set('no-sound', active) + + +def menu(config, args): + assert Gtk + handler = Handler(config, args) + + builder = Gtk.Builder() + builder.add_from_file('data/menu.glade') + builder.connect_signals(handler) + + handler.init_gtk(builder) + + Gtk.main()