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()