章节演示3#

sections_demo_3.py#
1"""
2Section Example 3:
3
4This shows how sections work with a very small example
5
6What's key here is to understand how sections can isolate code that otherwise
7 goes packed together in the view.
8Also, note that events are received on each section only based on the
9 section configuration. This way you don't have to check every time if the mouse
10 position is on top of some area.
11
12Note:
13 - Event dispatching (two sections will receive on_key_press and on_key_release)
14 - Prevent dispatching to allow some events to stop propagating
15 - Event draw, update and event delivering order based on section_manager
16 sections list order
17 - Section "enable" property to show or hide sections
18 - Modal Sections: sections that draw last but capture all events and also stop
19 other sections from updating.
20
21If Python and Arcade are installed, this example can be run from the command line with:
22python -m arcade.examples.sections_demo_3
23"""
24from typing import Optional
25from math import sqrt
26
27import arcade
28from arcade import Section
29from arcade.types import Color
30
31INFO_BAR_HEIGHT = 40
32PANEL_WIDTH = 200
33SPRITE_SPEED = 1
34
35COLOR_LIGHT = Color.from_hex_string('#D9BBA0')
36COLOR_DARK = Color.from_hex_string('#0D0D0D')
37COLOR_1 = Color.from_hex_string('#2A1459')
38COLOR_2 = Color.from_hex_string('#4B89BF')
39COLOR_3 = Color.from_hex_string('#03A688')
40
41
42class Ball(arcade.SpriteCircle):
43 """ The moving ball """
44
45 def __init__(self, radius, color):
46 super().__init__(radius, color)
47
48 self.bounce_count: int = 0 # to count the number of bounces
49
50 @property
51 def speed(self):
52 # return euclidian distance * current fps (60 default)
53 return int(sqrt(pow(self.change_x, 2) + pow(self.change_y, 2)) * 60)
54
55
56class ModalSection(Section):
57 """ A modal section that represents a popup that waits for user input """
58
59 def __init__(self, left: int, bottom: int, width: int, height: int):
60 super().__init__(left, bottom, width, height, modal=True, enabled=False)
61
62 # modal button
63 self.button = arcade.SpriteSolidColor(100, 50, color=arcade.color.RED)
64 pos = self.left + self.width / 2, self.bottom + self.height / 2
65 self.button.position = pos
66
67 def on_draw(self):
68 # draw modal frame and button
69 arcade.draw_lrbt_rectangle_filled(self.left, self.right, self.bottom,
70 self.top, arcade.color.GRAY)
71 arcade.draw_lrbt_rectangle_outline(self.left, self.right, self.bottom,
72 self.top, arcade.color.WHITE)
73 self.draw_button()
74
75 def draw_button(self):
76 # draws the button and button text
77 self.button.draw()
78 arcade.draw_text('Close Modal', self.button.left + 5,
79 self.button.bottom + self.button.height / 2,
80 arcade.color.WHITE)
81
82 def on_resize(self, width: int, height: int):
83 """ set position on screen resize """
84 self.left = width // 3
85 self.bottom = (height // 2) - self.height // 2
86 pos = self.left + self.width / 2, self.bottom + self.height / 2
87 self.button.position = pos
88
89 def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
90 """ Check if the button is pressed """
91 if self.button.collides_with_point((x, y)):
92 self.enabled = False
93
94
95class InfoBar(Section):
96 """ This is the top bar of the screen where info is showed """
97
98 @property
99 def ball(self):
100 return self.view.map.ball
101
102 def on_draw(self):
103 # draw game info
104 arcade.draw_lrbt_rectangle_filled(self.left, self.right, self.bottom,
105 self.top, COLOR_DARK)
106 arcade.draw_lrbt_rectangle_outline(self.left, self.right, self.bottom,
107 self.top, COLOR_LIGHT)
108 arcade.draw_text(f'Ball bounce count: {self.ball.bounce_count}',
109 self.left + 20, self.top - self.height / 1.6,
110 COLOR_LIGHT)
111
112 ball_change_axis = self.ball.change_x, self.ball.change_y
113 arcade.draw_text(f'Ball change in axis: {ball_change_axis}',
114 self.left + 220, self.top - self.height / 1.6,
115 COLOR_LIGHT)
116 arcade.draw_text(f'Ball speed: {self.ball.speed} pixels/second',
117 self.left + 480, self.top - self.height / 1.6,
118 COLOR_LIGHT)
119
120 def on_resize(self, width: int, height: int):
121 # stick to the top
122 self.width = width
123 self.bottom = height - self.view.info_bar.height
124
125
126class Panel(Section):
127 """This is the Panel to the right where buttons and info is showed """
128
129 def __init__(self, left: int, bottom: int, width: int, height: int,
130 **kwargs):
131 super().__init__(left, bottom, width, height, **kwargs)
132
133 # create buttons
134 self.button_stop = self.new_button(arcade.color.ARSENIC)
135 self.button_toggle_info_bar = self.new_button(COLOR_1)
136
137 self.button_show_modal = self.new_button(COLOR_2)
138 # to show the key that's actually pressed
139 self.pressed_key: Optional[int] = None
140
141 @staticmethod
142 def new_button(color):
143 # helper to create new buttons
144 return arcade.SpriteSolidColor(100, 50, color=color)
145
146 def draw_button_stop(self):
147 arcade.draw_text('Press button to stop the ball', self.left + 10,
148 self.top - 40, COLOR_LIGHT, 10)
149 self.button_stop.draw()
150
151 def draw_button_toggle_info_bar(self):
152 arcade.draw_text('Press to toggle info_bar', self.left + 10,
153 self.top - 140, COLOR_LIGHT, 10)
154 self.button_toggle_info_bar.draw()
155
156 def draw_button_show_modal(self):
157 self.button_show_modal.draw()
158 arcade.draw_text('Show Modal', self.left - 37 + self.width / 2,
159 self.bottom + 95, COLOR_DARK, 10)
160
161 def on_draw(self):
162 arcade.draw_lrbt_rectangle_filled(self.left, self.right, self.bottom,
163 self.top, COLOR_DARK)
164 arcade.draw_lrbt_rectangle_outline(self.left, self.right, self.bottom,
165 self.top, COLOR_LIGHT)
166 self.draw_button_stop()
167 self.draw_button_toggle_info_bar()
168
169 if self.pressed_key:
170 arcade.draw_text(f'Pressed key code: {self.pressed_key}',
171 self.left + 10, self.top - 240, COLOR_LIGHT, 9)
172
173 self.draw_button_show_modal()
174
175 def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
176 if self.button_stop.collides_with_point((x, y)):
177 self.view.map.ball.stop()
178 elif self.button_toggle_info_bar.collides_with_point((x, y)):
179 self.view.info_bar.enabled = not self.view.info_bar.enabled
180 elif self.button_show_modal.collides_with_point((x, y)):
181 self.view.modal_section.enabled = True
182
183 def on_resize(self, width: int, height: int):
184 # stick to the right
185 self.left = width - self.width
186 self.height = height - self.view.info_bar.height
187 self.button_stop.position = self.left + self.width / 2, self.top - 80
188
189 pos = self.left + self.width / 2, self.top - 180
190 self.button_toggle_info_bar.position = pos
191
192 pos = self.left + self.width / 2, self.bottom + 100
193 self.button_show_modal.position = pos
194
195 def on_key_press(self, symbol: int, modifiers: int):
196 self.pressed_key = symbol
197
198 def on_key_release(self, _symbol: int, _modifiers: int):
199 self.pressed_key = None
200
201
202class Map(Section):
203 """ This represents the place where the game takes place """
204
205 def __init__(self, left: int, bottom: int, width: int, height: int,
206 **kwargs):
207 super().__init__(left, bottom, width, height, **kwargs)
208
209 self.ball = Ball(20, COLOR_3)
210 self.ball.position = 60, 60
211 self.sprite_list: arcade.SpriteList = arcade.SpriteList()
212 self.sprite_list.append(self.ball)
213
214 self.pressed_key: Optional[int] = None
215
216 def on_update(self, delta_time: float):
217
218 if self.pressed_key:
219 if self.pressed_key == arcade.key.UP:
220 self.ball.change_y += SPRITE_SPEED
221 elif self.pressed_key == arcade.key.RIGHT:
222 self.ball.change_x += SPRITE_SPEED
223 elif self.pressed_key == arcade.key.DOWN:
224 self.ball.change_y -= SPRITE_SPEED
225 elif self.pressed_key == arcade.key.LEFT:
226 self.ball.change_x -= SPRITE_SPEED
227
228 self.sprite_list.update()
229
230 if self.ball.top >= self.top or self.ball.bottom <= self.bottom:
231 self.ball.change_y *= -1
232 self.ball.bounce_count += 1
233 if self.ball.left <= self.left or self.ball.right >= self.right:
234 self.ball.change_x *= -1
235 self.ball.bounce_count += 1
236
237 def on_draw(self):
238 arcade.draw_lrbt_rectangle_filled(self.left, self.right, self.bottom,
239 self.top, COLOR_DARK)
240 arcade.draw_lrbt_rectangle_outline(self.left, self.right, self.bottom,
241 self.top, COLOR_LIGHT)
242 self.sprite_list.draw()
243
244 def on_key_press(self, symbol: int, modifiers: int):
245 self.pressed_key = symbol
246
247 def on_key_release(self, _symbol: int, _modifiers: int):
248 self.pressed_key = None
249
250 def on_resize(self, width: int, height: int):
251 self.width = width - self.view.panel.width
252 self.height = height - self.view.info_bar.height
253
254
255class GameView(arcade.View):
256 """ The game itself """
257
258 def __init__(self):
259 super().__init__()
260
261 # create and store the modal, so we can set
262 # self.modal_section.enabled = True to show it
263 self.modal_section = ModalSection((self.window.width / 2) - 150,
264 (self.window.height / 2) - 100,
265 300, 200)
266
267 # we set accept_keyboard_events to False (default to True)
268 self.info_bar = InfoBar(0, self.window.height - INFO_BAR_HEIGHT, self.window.width, INFO_BAR_HEIGHT,
269 accept_keyboard_keys=False)
270
271 # as prevent_dispatch is on by default, we let pass the events to the
272 # following Section: the map
273 self.panel = Panel(self.window.width - PANEL_WIDTH, 0, PANEL_WIDTH,
274 self.window.height - INFO_BAR_HEIGHT,
275 prevent_dispatch={False})
276 self.map = Map(0, 0, self.window.width - PANEL_WIDTH,
277 self.window.height - INFO_BAR_HEIGHT)
278
279 # add the sections
280 self.section_manager.add_section(self.modal_section)
281 self.section_manager.add_section(self.info_bar)
282 self.section_manager.add_section(self.panel)
283 self.section_manager.add_section(self.map)
284
285 def on_draw(self):
286 arcade.start_render()
287
288
289def main():
290 window = arcade.Window(resizable=True)
291 game = GameView()
292
293 window.show_view(game)
294
295 window.run()
296
297
298if __name__ == '__main__':
299 main()