Mercurial > touhou
comparison 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 |
comparison
equal
deleted
inserted
replaced
567:b2269b9c6119 | 568:e7a4731a278b |
---|---|
1 # -*- encoding: utf-8 -*- | |
2 ## | |
3 ## Copyright (C) 2014 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> | |
4 ## | |
5 ## This program is free software; you can redistribute it and/or modify | |
6 ## it under the terms of the GNU General Public License as published | |
7 ## by the Free Software Foundation; version 3 only. | |
8 ## | |
9 ## This program is distributed in the hope that it will be useful, | |
10 ## but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 ## GNU General Public License for more details. | |
13 ## | |
14 | |
15 from pytouhou.utils.helpers import get_logger | |
16 logger = get_logger(__name__) | |
17 | |
18 try: | |
19 from gi.repository import Gtk, Gdk | |
20 except ImportError: | |
21 logger.error('GTK+ unavailable, disabling the GUI menu.') | |
22 raise | |
23 | |
24 import sys | |
25 import re | |
26 | |
27 GL_VERSION_REGEX = re.compile(r'^\d\.\d$') | |
28 | |
29 | |
30 class Handler(object): | |
31 def __init__(self, config, args): | |
32 self.config = config | |
33 self.args = args | |
34 | |
35 def init_gtk(self, builder): | |
36 self.start_window = builder.get_object('start_window') | |
37 self.game_window = builder.get_object('game_window') | |
38 self.options_window = builder.get_object('options_window') | |
39 | |
40 self.replay_filechooserdialog = builder.get_object('replay_filechooserdialog') | |
41 | |
42 # Game widgets | |
43 self.difficulty_box = builder.get_object('difficulty_box') | |
44 self.character_box = builder.get_object('character_box') | |
45 self.stage_box = builder.get_object('stage_box') | |
46 self.boss_rush_checkbutton = builder.get_object('boss_rush_checkbutton') | |
47 | |
48 self.difficulty_comboboxtext = builder.get_object('difficulty_comboboxtext') | |
49 self.character_comboboxtext = builder.get_object('character_comboboxtext') | |
50 self.stage_comboboxtext = builder.get_object('stage_comboboxtext') | |
51 | |
52 # Options widgets | |
53 self.path_filechooserbutton = builder.get_object('path_filechooserbutton') | |
54 self.backend_comboboxtext = builder.get_object('backend_comboboxtext') | |
55 | |
56 # OpenGL backend | |
57 self.opengl_grid = builder.get_object('opengl_grid') | |
58 self.flavor_comboboxtext = builder.get_object('flavor_comboboxtext') | |
59 self.version_entry = builder.get_object('version_entry') | |
60 self.double_buffer_checkbutton = builder.get_object('double_buffer_checkbutton') | |
61 | |
62 # SDL backend | |
63 self.sdl_grid = builder.get_object('sdl_grid') | |
64 | |
65 self.fps_entry = builder.get_object('fps_entry') | |
66 self.no_background_checkbutton = builder.get_object('no_background_checkbutton') | |
67 self.no_particles_checkbutton = builder.get_object('no_particles_checkbutton') | |
68 self.no_sound_checkbutton = builder.get_object('no_sound_checkbutton') | |
69 | |
70 self.difficulty_comboboxtext.set_active_id(str(self.args.rank)) | |
71 self.character_comboboxtext.set_active_id(str(self.args.character)) | |
72 self.stage_comboboxtext.set_active_id(str(self.args.stage)) | |
73 self.boss_rush_checkbutton.set_active(self.args.boss_rush) | |
74 | |
75 self.path_filechooserbutton.set_filename(self.args.path) | |
76 self.backend_comboboxtext.set_active_id(' '.join(self.args.backend)) | |
77 | |
78 self.flavor_comboboxtext.set_active_id(self.args.gl_flavor) | |
79 self.version_entry.set_text(str(self.args.gl_version)) | |
80 if self.args.double_buffer is None: | |
81 self.double_buffer_checkbutton.set_inconsistent(True) | |
82 else: | |
83 self.double_buffer_checkbutton.set_inconsistent(False) | |
84 self.double_buffer_checkbutton.set_active(self.args.double_buffer) | |
85 | |
86 self.fps_entry.set_text(str(self.args.fps_limit)) | |
87 self.no_background_checkbutton.set_active(self.args.no_background) | |
88 self.no_particles_checkbutton.set_active(self.args.no_particles) | |
89 self.no_sound_checkbutton.set_active(self.args.no_sound) | |
90 | |
91 def hide_and_play(self, window): | |
92 window.hide() | |
93 Gtk.main_quit() | |
94 print('Play!') | |
95 | |
96 def on_quit(self, *args): | |
97 Gtk.main_quit(*args) | |
98 sys.exit(0) | |
99 | |
100 | |
101 # Main menu | |
102 | |
103 def on_start_window_key_press_event(self, window, event_key): | |
104 if (event_key.keyval == Gdk.KEY_Escape or | |
105 event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q): | |
106 self.on_quit() | |
107 | |
108 def on_start_button_clicked(self, _): | |
109 self.difficulty_box.show() | |
110 self.character_box.show() | |
111 self.stage_box.hide() | |
112 | |
113 self.stage_comboboxtext.set_active_id(None) | |
114 self.args.stage = None | |
115 | |
116 self.start_window.hide() | |
117 self.game_window.show() | |
118 | |
119 def on_extra_start_button_clicked(self, _): | |
120 self.difficulty_box.hide() | |
121 self.character_box.show() | |
122 self.stage_box.hide() | |
123 | |
124 self.difficulty_comboboxtext.set_active_id('4') | |
125 self.stage_comboboxtext.set_active_id('7') | |
126 | |
127 self.start_window.hide() | |
128 self.game_window.show() | |
129 | |
130 def on_practice_start_button_clicked(self, _): | |
131 self.difficulty_box.show() | |
132 self.character_box.show() | |
133 self.stage_box.show() | |
134 | |
135 self.start_window.hide() | |
136 self.game_window.show() | |
137 | |
138 def on_options_button_clicked(self, _): | |
139 self.start_window.hide() | |
140 self.options_window.show() | |
141 | |
142 def on_replay_button_clicked(self, _): | |
143 self.start_window.hide() | |
144 self.replay_filechooserdialog.show() | |
145 | |
146 def on_inactive_button_clicked(self, _): | |
147 raise NotImplementedError('Menu not implemented') | |
148 | |
149 on_netplay_start_button_clicked = on_inactive_button_clicked | |
150 on_score_button_clicked = on_inactive_button_clicked | |
151 on_music_room_button_clicked = on_inactive_button_clicked | |
152 | |
153 | |
154 # Game menu | |
155 | |
156 def on_game_back_button_clicked(self, _): | |
157 self.game_window.hide() | |
158 self.start_window.show() | |
159 | |
160 def on_play_button_clicked(self, _): | |
161 self.hide_and_play(self.game_window) | |
162 | |
163 def on_game_window_key_press_event(self, window, event_key): | |
164 if event_key.keyval == Gdk.KEY_Escape: | |
165 self.game_window.hide() | |
166 self.start_window.show() | |
167 elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: | |
168 self.on_quit() | |
169 | |
170 def on_difficulty_comboboxtext_changed(self, item): | |
171 active = item.get_active_id() | |
172 difficulty = int(active) | |
173 self.config.set('rank', active if difficulty < 4 else None) | |
174 self.args.rank = difficulty | |
175 | |
176 def on_character_comboboxtext_changed(self, item): | |
177 character = int(item.get_active_id()) | |
178 self.config.set('character', character) | |
179 self.args.character = character | |
180 | |
181 def on_stage_comboboxtext_changed(self, item): | |
182 stage = item.get_active_id() | |
183 self.args.stage = int(stage) if stage is not None else None | |
184 | |
185 def on_boss_rush_checkbutton_toggled(self, boss_rush_checkbutton): | |
186 active = boss_rush_checkbutton.get_active() | |
187 self.config.set('boss-rush', active) | |
188 self.args.boss_rush = active | |
189 | |
190 | |
191 # Replay dialog | |
192 | |
193 def on_replay_filechooserdialog_close(self, _): | |
194 self.replay_filechooserdialog.hide() | |
195 self.start_window.show() | |
196 | |
197 on_replay_cancel_button_clicked = on_replay_filechooserdialog_close | |
198 | |
199 def on_replay_open_button_clicked(self, button): | |
200 try: | |
201 open(self.args.replay, 'rb').close() | |
202 except (IOError, TypeError): | |
203 return | |
204 self.hide_and_play(self.replay_filechooserdialog) | |
205 | |
206 def on_replay_filechooserdialog_selection_changed(self, dialog): | |
207 self.args.replay = dialog.get_filename() | |
208 | |
209 def on_replay_filechooserdialog_file_activated(self, window): | |
210 self.args.replay = window.get_filename() | |
211 self.hide_and_play(self.replay_filechooserdialog) | |
212 | |
213 def on_replay_filechooserdialog_key_press_event(self, window, event_key): | |
214 if event_key.keyval == Gdk.KEY_Escape: | |
215 self.replay_filechooserdialog.hide() | |
216 self.start_window.show() | |
217 elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: | |
218 self.on_quit() | |
219 | |
220 | |
221 # Options menu | |
222 | |
223 def on_options_back_button_clicked(self, _): | |
224 self.options_window.hide() | |
225 self.start_window.show() | |
226 | |
227 def on_options_window_key_press_event(self, window, event_key): | |
228 if event_key.keyval == Gdk.KEY_Escape: | |
229 self.options_window.hide() | |
230 self.start_window.show() | |
231 elif event_key.state == Gdk.ModifierType.CONTROL_MASK and event_key.keyval == Gdk.KEY_q: | |
232 self.on_quit() | |
233 | |
234 def on_path_filechooserbutton_file_set(self, path_filechooserbutton): | |
235 path = path_filechooserbutton.get_filename() | |
236 self.config.set('path', path) | |
237 | |
238 def on_backend_comboboxtext_changed(self, backend_comboboxtext): | |
239 active = backend_comboboxtext.get_active_id() | |
240 self.config.set('backend', active) | |
241 backends = active.split() | |
242 new_grids = [getattr(self, backend + '_grid') for backend in backends] | |
243 for grid in [self.opengl_grid, self.sdl_grid]: | |
244 if grid not in new_grids: | |
245 grid.hide() | |
246 else: | |
247 grid.show() | |
248 | |
249 def on_flavor_comboboxtext_changed(self, flavor_comboboxtext): | |
250 active = flavor_comboboxtext.get_active_id() | |
251 self.config.set('gl-flavor', active) | |
252 | |
253 def on_version_entry_changed(self, version_entry): | |
254 text = version_entry.get_text() | |
255 if not GL_VERSION_REGEX.match(text): | |
256 raise ValueError('ā%sā is not <digit>.<digit>' % text) | |
257 self.config.set('gl-version', text) | |
258 | |
259 def on_double_buffer_checkbutton_clicked(self, double_buffer_checkbutton): | |
260 inconsistent = double_buffer_checkbutton.get_inconsistent() | |
261 active = double_buffer_checkbutton.get_active() | |
262 if inconsistent: | |
263 active = False | |
264 inconsistent = False | |
265 elif active: | |
266 active = True | |
267 else: | |
268 inconsistent = True | |
269 double_buffer_checkbutton.set_active(active) | |
270 double_buffer_checkbutton.set_inconsistent(inconsistent) | |
271 self.config.set('double-buffer', None if inconsistent else active) | |
272 | |
273 def on_fps_entry_changed(self, fps_entry): | |
274 text = fps_entry.get_text() | |
275 try: | |
276 int(text) | |
277 except ValueError: | |
278 raise ValueError('ā%sā is not integer' % text) | |
279 else: | |
280 self.config.set('fps-limit', text) | |
281 | |
282 def on_no_background_checkbutton_toggled(self, checkbutton): | |
283 active = checkbutton.get_active() | |
284 self.config.set('no-background', not active) | |
285 | |
286 def on_no_particles_checkbutton_toggled(self, checkbutton): | |
287 active = checkbutton.get_active() | |
288 self.config.set('no-particles', active) | |
289 | |
290 def on_no_sound_checkbutton_toggled(self, checkbutton): | |
291 active = checkbutton.get_active() | |
292 self.config.set('no-sound', active) | |
293 | |
294 | |
295 def menu(config, args): | |
296 assert Gtk | |
297 handler = Handler(config, args) | |
298 | |
299 builder = Gtk.Builder() | |
300 builder.add_from_file('data/menu.glade') | |
301 builder.connect_signals(handler) | |
302 | |
303 handler.init_gtk(builder) | |
304 | |
305 Gtk.main() |