comparison src/parpg/grease/impl/mode.py @ 27:09b581087d68

Added base files for grease
author KarstenBock@gmx.net
date Tue, 12 Jul 2011 10:16:48 +0200
parents
children
comparison
equal deleted inserted replaced
26:5529dd5644b8 27:09b581087d68
1 #############################################################################
2 #
3 # Copyright (c) 2010 by Casey Duncan and contributors
4 # All Rights Reserved.
5 #
6 # This software is subject to the provisions of the MIT License
7 # A copy of the license should accompany this distribution.
8 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
9 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
10 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
11 #
12 #############################################################################
13 """
14 Modes manage the state and transition between different application modes.
15 Typically such modes are presented as different screens that the user can
16 navigate between, similar to the way a browser navigates web pages. Individual
17 modes may be things like:
18
19 - Title screen
20 - Options dialog
21 - About screen
22 - In-progress game
23 - Inventory interface
24
25 The modal framework provides a simple mechanism to ensure that modes are
26 activated and deactivated properly. An activated mode is running and receives
27 events. A deactivated mode is paused and does not receive events.
28
29 Modes may be managed as a *last-in-first-out* stack, or as a list, or ring
30 of modes in sequence, or some combination of all.
31
32 For example usage see: :ref:`the mode section of the tutorial <tut-mode-section>`.
33 """
34
35 __version__ = '$Id$'
36
37 import abc
38 import pyglet
39 from grease.mode import *
40
41 class PygletManager(BaseManager):
42 """Mode manager abstract base class using pyglet.
43
44 The mode manager keeps a stack of modes where a single mode
45 is active at one time. As modes are pushed on and popped from
46 the stack, the mode at the top is always active. The current
47 active mode receives events from the manager's event dispatcher.
48 """
49
50 event_dispatcher = None
51 """:class:`pyglet.event.EventDispatcher` object that the
52 active mode receive events from.
53 """
54
55 def activate_mode(self, mode):
56 """Perform actions to activate a node
57
58 :param mode: The :class: 'Mode' object to activate
59 """
60 BaseManager.activate_mode(self, mode)
61 self.event_dispatcher.push_handlers(mode)
62
63 def deactivate_mode(self, mode):
64 """Perform actions to deactivate a node
65
66 :param mode: The :class: 'Mode' object to deactivate
67 """
68 BaseManager.deactivate_mode(self, mode)
69 self.event_dispatcher.remove_handlers(mode)
70
71 class Manager(PygletManager):
72 """A basic mode manager that wraps a single
73 :class:`pyglet.event.EventDispatcher` object for use by its modes.
74 """
75
76 def __init__(self, event_dispatcher):
77 self.modes = []
78 self.event_dispatcher = event_dispatcher
79
80
81 class ManagerWindow(PygletManager, pyglet.window.Window):
82 """An integrated mode manager and pyglet window for convenience.
83 The window is the event dispatcher used by modes pushed to
84 this manager.
85
86 Constructor arguments are identical to :class:`pyglet.window.Window`
87 """
88
89 def __init__(self, *args, **kw):
90 super(ManagerWindow, self).__init__(*args, **kw)
91 self.modes = []
92 self.event_dispatcher = self
93
94 def on_key_press(self, symbol, modifiers):
95 """Default :meth:`on_key_press handler`, pops the current mode on ``ESC``"""
96 if symbol == pyglet.window.key.ESCAPE:
97 self.pop_mode()
98
99 def on_last_mode_pop(self, mode):
100 """Hook executed when the last mode is popped from the manager.
101 When the last mode is popped from a window, an :meth:`on_close` event
102 is dispatched.
103
104 :param mode: The :class:`Mode` object just popped from the manager
105 """
106 self.dispatch_event('on_close')
107
108
109 class Mode(BaseMode):
110 """Application mode abstract base class using pyglet
111
112 Subclasses must implement the :meth:`step` method
113
114 :param step_rate: The rate of :meth:`step()` calls per second.
115
116 :param master_clock: The :class:`pyglet.clock.Clock` interface used
117 as the master clock that ticks the world's clock. This
118 defaults to the main pyglet clock.
119 """
120 clock = None
121 """The :class:`pyglet.clock.Clock` instance used as this mode's clock.
122 You should use this clock to schedule tasks for this mode, so they
123 properly respect when the mode is active or inactive
124
125 Example::
126
127 my_mode.clock.schedule_once(my_cool_function, 4)
128 """
129
130 def __init__(self, step_rate=60, master_clock=pyglet.clock,
131 clock_factory=pyglet.clock.Clock):
132 BaseMode.__init__(self)
133 self.step_rate = step_rate
134 self.time = 0.0
135 self.master_clock = master_clock
136 self.clock = clock_factory(time_function=lambda: self.time)
137 self.clock.schedule_interval(self.step, 1.0 / step_rate)
138
139 def on_activate(self):
140 """Being called when the Mode is activated"""
141 self.master_clock.schedule(self.tick)
142
143 def on_deactivate(self):
144 """Being called when the Mode is deactivated"""
145 self.master_clock.unschedule(self.tick)
146
147 def tick(self, dt):
148 """Tick the mode's clock.
149
150 :param dt: The time delta since the last tick
151 :type dt: float
152 """
153 self.time += dt
154 self.clock.tick(poll=False)
155
156 @abc.abstractmethod
157 def step(self, dt):
158 """Execute a timestep for this mode. Must be defined by subclasses.
159
160 :param dt: The time delta since the last time step
161 :type dt: float
162 """
163
164 class Multi(BaseMulti, Mode):
165 """A mode with multiple submodes. One submode is active at one time.
166 Submodes can be switched to directly or switched in sequence. If
167 the Multi is active, then one submode is always active.
168
169 Multis are useful when modes can switch in an order other than
170 a LIFO stack, such as in "hotseat" multiplayer games, a
171 "wizard" style ui, or a sequence of slides.
172
173 Note unlike a normal :class:`Mode`, a :class:`Multi` doesn't have it's own
174 :attr:`clock` and :attr:`step_rate`. The active submode's are used
175 instead.
176 """
177
178 def __init__(self, submodes):
179 BaseMulti.__init__(self, submodes)
180 self.time = 0.0
181
182
183 def _set_active_submode(self, submode):
184 BaseMulti._set_active_submode(self, submode)
185 self.master_clock = submode.master_clock
186 self.clock = submode.clock
187
188 def clear_subnode(self):
189 """Clear any subnmode data"""
190 BaseMulti.clear_subnode(self)
191 self.master_clock = None
192 self.clock = None
193
194 def tick(self, dt):
195 """Tick the active submode's clock.
196
197 :param dt: The time delta since the last tick
198 :type dt: float
199 """
200 self.time += dt
201 if self.active_submode is not None:
202 self.active_submode.clock.tick(poll=False)
203