Mercurial > parpg-core
annotate src/parpg/bGrease/impl/mode.py @ 66:96af64cf3b81
Renamed grease to bGrease (Basic Grease) to get rid of conflicts with an already installed grease.
author | KarstenBock@gmx.net |
---|---|
date | Mon, 05 Sep 2011 15:00:34 +0200 |
parents | src/parpg/grease/impl/mode.py@09b581087d68 |
children | 0f659c7675f6 |
rev | line source |
---|---|
27 | 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 | |
66
96af64cf3b81
Renamed grease to bGrease (Basic Grease) to get rid of conflicts with an already installed grease.
KarstenBock@gmx.net
parents:
27
diff
changeset
|
39 from bGrease.mode import * |
27 | 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 |