Skip to content

Commit e79ad1f

Browse files
authored
Merge pull request #374 from ppizarror/improve-maze-example
Improve maze example
2 parents 41d7b8e + 0dc67a3 commit e79ad1f

File tree

10 files changed

+256
-43
lines changed

10 files changed

+256
-43
lines changed

pygame_menu/_scrollarea.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,10 @@ class ScrollArea(Base):
122122
_menubar: 'pygame_menu.widgets.MenuBar'
123123
_parent_scrollarea: 'ScrollArea'
124124
_rect: 'pygame.Rect'
125-
_scrollbars_props: Tuple[Any, ...]
126125
_scrollbar_positions: Tuple[str, ...]
127126
_scrollbar_thick: int
128127
_scrollbars: List['ScrollBar']
128+
_scrollbars_props: Tuple[Any, ...]
129129
_translate: Tuple2IntType
130130
_view_rect: 'pygame.Rect'
131131
_world: 'pygame.Surface'
@@ -1072,7 +1072,7 @@ def update(self, events: EventVectorType) -> bool:
10721072
:param events: List of pygame events
10731073
:return: ``True`` if updated
10741074
"""
1075-
updated = [0, 0]
1075+
updated = [False, False]
10761076
for sbar in self._scrollbars:
10771077
if not sbar.is_visible():
10781078
continue

pygame_menu/events.py

+50
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,31 @@
2121
'NONE',
2222
'RESET',
2323

24+
# Last menu events
25+
'MENU_LAST_DISABLE_UPDATE',
26+
'MENU_LAST_FRAMES',
27+
'MENU_LAST_JOY_REPEAT',
28+
'MENU_LAST_MENU_BACK',
29+
'MENU_LAST_MENU_CLOSE',
30+
'MENU_LAST_MENUBAR',
31+
'MENU_LAST_MOUSE_ENTER_MENU',
32+
'MENU_LAST_MOUSE_ENTER_WINDOW',
33+
'MENU_LAST_MOUSE_LEAVE_MENU',
34+
'MENU_LAST_MOUSE_LEAVE_WINDOW',
35+
'MENU_LAST_MOVE_DOWN',
36+
'MENU_LAST_MOVE_LEFT',
37+
'MENU_LAST_MOVE_RIGHT',
38+
'MENU_LAST_MOVE_UP',
39+
'MENU_LAST_NONE',
40+
'MENU_LAST_QUIT',
41+
'MENU_LAST_SCROLL_AREA',
42+
'MENU_LAST_SELECTED_WIDGET_BUTTON_UP',
43+
'MENU_LAST_SELECTED_WIDGET_EVENT',
44+
'MENU_LAST_SELECTED_WIDGET_FINGER_UP',
45+
'MENU_LAST_WIDGET_DISABLE_ACTIVE_STATE',
46+
'MENU_LAST_WIDGET_SELECT',
47+
'MENU_LAST_WIDGET_SELECT_MOTION',
48+
2449
# Pygame events
2550
'PYGAME_QUIT',
2651
'PYGAME_WINDOWCLOSE'
@@ -74,3 +99,28 @@ def is_event(event: Any) -> bool:
7499
PYGAME_WINDOWCLOSE = __locals.WINDOWCLOSE
75100
elif hasattr(__locals, 'WINDOWEVENT_CLOSE'):
76101
PYGAME_WINDOWCLOSE = __locals.WINDOWEVENT_CLOSE
102+
103+
# Menu last event types. Returned by menu.get_last_update_mode()
104+
MENU_LAST_DISABLE_UPDATE = 'DISABLE_UPDATE'
105+
MENU_LAST_FRAMES = 'FRAMES'
106+
MENU_LAST_JOY_REPEAT = 'JOY_REPEAT'
107+
MENU_LAST_MENU_BACK = 'MENU_BACK'
108+
MENU_LAST_MENU_CLOSE = 'MENU_CLOSE'
109+
MENU_LAST_MENUBAR = 'MENUBAR'
110+
MENU_LAST_MOUSE_ENTER_MENU = 'MOUSE_ENTER_MENU'
111+
MENU_LAST_MOUSE_ENTER_WINDOW = 'MOUSE_ENTER_WINDOW'
112+
MENU_LAST_MOUSE_LEAVE_MENU = 'MOUSE_LEAVE_MENU'
113+
MENU_LAST_MOUSE_LEAVE_WINDOW = 'MOUSE_LEAVE_WINDOW'
114+
MENU_LAST_MOVE_DOWN = 'MOVE_DOWN'
115+
MENU_LAST_MOVE_LEFT = 'MOVE_LEFT'
116+
MENU_LAST_MOVE_RIGHT = 'MOVE_RIGHT'
117+
MENU_LAST_MOVE_UP = 'MOVE_UP'
118+
MENU_LAST_NONE = 'NONE'
119+
MENU_LAST_QUIT = 'QUIT'
120+
MENU_LAST_SCROLL_AREA = 'SCROLL_AREA'
121+
MENU_LAST_SELECTED_WIDGET_BUTTON_UP = 'SELECTED_WIDGET_BUTTON_UP'
122+
MENU_LAST_SELECTED_WIDGET_EVENT = 'SELECTED_WIDGET_EVENT'
123+
MENU_LAST_SELECTED_WIDGET_FINGER_UP = 'SELECTED_WIDGET_FINGER_UP'
124+
MENU_LAST_WIDGET_DISABLE_ACTIVE_STATE = 'WIDGET_DISABLE_ACTIVE_STATE'
125+
MENU_LAST_WIDGET_SELECT = 'WIDGET_SELECT'
126+
MENU_LAST_WIDGET_SELECT_MOTION = 'WIDGET_SELECT_MOTION'

pygame_menu/examples/other/maze.py

+66-29
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ class MazeApp(object):
175175
Maze class.
176176
"""
177177

178+
_algorithms: dict
178179
_diagonals: bool
179180
_end_point: _Point2
180181
_grid: _MazeType
@@ -190,8 +191,8 @@ class MazeApp(object):
190191

191192
def __init__(
192193
self,
193-
width: int = 7,
194-
rows: int = 85,
194+
width: int = 8,
195+
rows: int = 75,
195196
margin: int = 0
196197
) -> None:
197198
"""
@@ -231,8 +232,9 @@ def __init__(
231232
self._drag_end_point = False
232233

233234
# Used for deciding what to do in different situations
234-
self._path_found = False
235235
self._algorithm_run = False
236+
self._algorithms = {0: 'dijkstra', 1: 'astar', 2: 'dfs', 3: 'bfs'}
237+
self._path_found = False
236238

237239
# Create the window
238240
self._clock = pygame.time.Clock()
@@ -297,6 +299,8 @@ def _diagonals(value: bool) -> None:
297299
Changes diagonals
298300
"""
299301
self._diagonals = value
302+
if self._algorithm_run:
303+
self._path_found = self._update_path()
300304

301305
theme = pygame_menu.Theme(
302306
background_color=pygame_menu.themes.TRANSPARENT_COLOR,
@@ -309,7 +313,7 @@ def _diagonals(value: bool) -> None:
309313
self._menu = pygame_menu.Menu(
310314
height=self._screen_width,
311315
mouse_motion_selection=True,
312-
position=(640, 25, False),
316+
position=(645, 25, False),
313317
theme=theme,
314318
title='',
315319
width=240
@@ -349,7 +353,7 @@ def _diagonals(value: bool) -> None:
349353
items=[('Prim', 0),
350354
('Alt Prim', 1),
351355
('Recursive', 2),
352-
('Terrain', 3)],
356+
('(+) Terrain', 3)],
353357
dropselect_id='generator',
354358
font_size=16,
355359
onchange=onchange_dropselect,
@@ -428,15 +432,16 @@ def _diagonals(value: bool) -> None:
428432

429433
# Create about menu
430434
menu_about = pygame_menu.Menu(
431-
height=self._screen_width,
435+
height=self._screen_width + 20,
432436
mouse_motion_selection=True,
433-
position=(640, 25, False),
437+
position=(645, 8, False),
434438
theme=theme,
435439
title='',
436440
width=240
437441
)
438442
menu_about.add.label('pygame-menu\nMaze', font_name=pygame_menu.font.FONT_FIRACODE_BOLD, font_size=25,
439443
margin=(0, 5))
444+
menu_about.add.vertical_margin(10)
440445
text = 'Left click to create a wall or move the start and end points.\n' \
441446
'Hold left CTRL and left click to create a sticky mud patch (whi' \
442447
'ch reduces movement speed to 1/3).\n'
@@ -478,7 +483,8 @@ def _diagonals(value: bool) -> None:
478483
for btn in self._menu.get_widgets(['run_generator', 'run_solver', 'about', 'about_back']):
479484
btn.set_onmouseover(button_onmouseover)
480485
btn.set_onmouseleave(button_onmouseleave)
481-
btn.set_cursor(pygame_menu.locals.CURSOR_HAND)
486+
if not btn.readonly:
487+
btn.set_cursor(pygame_menu.locals.CURSOR_HAND)
482488
btn.set_background_color((75, 79, 81))
483489

484490
def _clear_maze(self) -> None:
@@ -491,6 +497,7 @@ def _clear_maze(self) -> None:
491497
for column in range(self._rows):
492498
if (row, column) != self._start_point and (row, column) != self._end_point:
493499
self._grid[row][column].update(nodetype='blank', is_visited=False, is_path=False)
500+
self._clear_visited()
494501

495502
def _run_generator(self) -> None:
496503
"""
@@ -499,30 +506,37 @@ def _run_generator(self) -> None:
499506
if self._visualize:
500507
ut.set_pygame_cursor(pygame_menu.locals.CURSOR_NO)
501508
o_visualize = self._visualize
502-
self._clear_maze()
503509
gen_type = self._menu.get_widget('generator').get_value()[1]
510+
if gen_type != 3:
511+
self._clear_maze()
504512
if gen_type == 0:
505-
self._grid = self._prim()
513+
self._grid = self._prim(start_point=self._start_point)
506514
elif gen_type == 1:
507-
self._grid = self._better_prim()
515+
self._grid = self._better_prim(start_point=self._start_point)
508516
elif gen_type == 2:
517+
pygame.display.flip()
509518
self._recursive_division()
510519
elif gen_type == 3:
511520
self._random_terrain()
512521
self._visualize = o_visualize
522+
if self._visualize:
523+
ut.set_pygame_cursor(pygame_menu.locals.CURSOR_ARROW)
513524

514525
def _run_solver(self) -> None:
515526
"""
516527
Run the solver.
517528
"""
518-
if self._visualize:
519-
ut.set_pygame_cursor(pygame_menu.locals.CURSOR_NO)
520529
o_visualize = self._visualize
521530
solver_type = self._menu.get_widget('solver').get_value()[1]
522-
self._clear_visited()
523-
self._update_gui()
524531
if self._visualize:
525532
pygame.display.flip()
533+
if self._path_found and self._algorithms[solver_type] == self._algorithm_run:
534+
self._visualize = False
535+
else:
536+
self._clear_visited()
537+
pygame.display.flip()
538+
if self._visualize:
539+
ut.set_pygame_cursor(pygame_menu.locals.CURSOR_NO)
526540
if solver_type == 0:
527541
self._path_found = self._dijkstra(self._grid, self._start_point, self._end_point)
528542
self._algorithm_run = 'dijkstra'
@@ -538,6 +552,8 @@ def _run_solver(self) -> None:
538552
self._visualize = o_visualize
539553
self._grid[self._start_point[0]][self._start_point[1]].update(nodetype='start')
540554
self._update_path()
555+
if self._visualize:
556+
ut.set_pygame_cursor(pygame_menu.locals.CURSOR_ARROW)
541557

542558
def _check_esc(self) -> None:
543559
"""
@@ -546,7 +562,7 @@ def _check_esc(self) -> None:
546562
if self._visualize:
547563
for event in pygame.event.get():
548564
if event.type == pygame.QUIT:
549-
exit()
565+
self._quit()
550566
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
551567
self._visualize = False
552568

@@ -559,7 +575,7 @@ def _sleep(ms: float) -> None:
559575
"""
560576
time.sleep(ms)
561577

562-
def _better_prim(self, mazearray: Optional[_MazeType] = None, start_point: bool = False) -> _MazeType:
578+
def _better_prim(self, mazearray: Optional[_MazeType] = None, start_point: Optional[_Point2] = None) -> _MazeType:
563579
"""
564580
Randomized Prim's algorithm for creating random mazes.
565581
This version maintains the traditional "maze" look, where a route cannot
@@ -646,12 +662,18 @@ def _better_prim(self, mazearray: Optional[_MazeType] = None, start_point: bool
646662

647663
walls.remove(wall)
648664

665+
# Iterate through each dormant, if so, change to blank
666+
for row in range(self._rows):
667+
for column in range(self._rows):
668+
if mazearray[row][column].nodetype == 'dormant':
669+
mazearray[row][column].update(nodetype='blank')
670+
649671
mazearray[self._end_point[0]][self._end_point[1]].update(nodetype='end')
650672
mazearray[self._start_point[0]][self._start_point[1]].update(nodetype='start')
651673

652674
return mazearray
653675

654-
def _prim(self, mazearray: Optional[_MazeType] = None, start_point: bool = False) -> _MazeType:
676+
def _prim(self, mazearray: Optional[_MazeType] = None, start_point: Optional[_Point2] = None) -> _MazeType:
655677
"""
656678
Randomized Prim's algorithm for creating random mazes.
657679
@@ -828,7 +850,7 @@ def _gaps_to_offset() -> List[int]:
828850
y += 1
829851
if y >= self._rows:
830852
y = self._rows - 1
831-
self._grid[x][y].update(nodetype="blank")
853+
self._grid[x][y].update(nodetype='blank')
832854
self._draw_square(self._grid, x, y)
833855
if self._visualize:
834856
self._update_square(x, y)
@@ -925,10 +947,8 @@ def _update_path(self) -> Union[bool, _MazeType]:
925947
Updates the path.
926948
"""
927949
self._clear_visited()
928-
valid_algorithms = ['dijkstra', 'astar', 'dfs', 'bfs']
929-
930-
assert self._algorithm_run in valid_algorithms, \
931-
f'last algorithm used ({self._algorithm_run}) is not in valid algorithms: {valid_algorithms}'
950+
assert self._algorithm_run in self._algorithms.values(), \
951+
f'last algorithm used ({self._algorithm_run}) is not in valid algorithms: {self._algorithms.values()}'
932952

933953
visualize = self._visualize
934954
self._visualize = False
@@ -1116,8 +1136,8 @@ def _dijkstra(
11161136
time_taken = end - start
11171137

11181138
# Print timings
1119-
print(f'Program finished in {time_taken:.4f} seconds after checking {num_visited}'
1120-
f' nodes. That is {time_taken / num_visited:.8f} seconds per node.')
1139+
print(f'Program finished in {time_taken:.4f}s after checking {num_visited}'
1140+
f' nodes ({time_taken / num_visited:.8f} s/node)')
11211141

11221142
return False if v_distances[goal_node] == float('inf') else True
11231143

@@ -1275,19 +1295,39 @@ def _pos_in_grid(self, pos: _Point2) -> bool:
12751295
y = pos[1] - self._offset[1]
12761296
return 1 <= x <= self._screen_width and 1 <= y <= self._screen_width
12771297

1298+
@staticmethod
1299+
def _quit() -> None:
1300+
"""
1301+
Quit app.
1302+
"""
1303+
pygame.quit()
1304+
exit()
1305+
12781306
def mainloop(self, test: bool) -> None:
12791307
"""
12801308
Executes the main loop of the app.
12811309
12821310
:param test: If True, runs only 1 frame
12831311
"""
1312+
print('Press [ESC] to skip process if Visualize is On')
12841313
while True:
1314+
12851315
# Application events
12861316
events = pygame.event.get()
1317+
1318+
# Update the menu
1319+
self._menu.update(events)
1320+
1321+
# If a menu widget disable its active state, disable the events, this is due to
1322+
# user can click outside a dropselection box, and that triggers the disable active
1323+
# state. If so, the event is destroyed, thus avoiding clicking the canvas
1324+
if pygame_menu.events.MENU_LAST_WIDGET_DISABLE_ACTIVE_STATE in self._menu.get_last_update_mode()[0]:
1325+
events = []
1326+
12871327
for event in events:
12881328
# User closes
12891329
if event.type == pygame.QUIT:
1290-
exit()
1330+
self._quit()
12911331

12921332
# Write in the board
12931333
elif event.type == pygame.MOUSEBUTTONDOWN:
@@ -1394,9 +1434,6 @@ def mainloop(self, test: bool) -> None:
13941434

13951435
pygame.display.flip()
13961436

1397-
# Update the menu events
1398-
self._menu.update(events)
1399-
14001437
# Update the app
14011438
self._update_gui()
14021439

pygame_menu/font.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
FONT_FIRACODE = __fonts_path__.format('FiraCode-Regular.ttf')
5555
FONT_FIRACODE_BOLD = __fonts_path__.format('FiraCode-Bold.ttf')
5656
FONT_FIRACODE_BOLD_ITALIC = __fonts_path__.format('FiraMono-BoldItalic.ttf')
57-
FONT_FIRACODE_ITALIC = __fonts_path__.format('FiraMono-RegularItalic.ttf')
57+
FONT_FIRACODE_ITALIC = __fonts_path__.format('FiraMono-Italic.ttf')
5858
FONT_FRANCHISE = __fonts_path__.format('franchise.ttf')
5959
FONT_HELVETICA = __fonts_path__.format('helvetica.ttf')
6060
FONT_MUNRO = __fonts_path__.format('munro.ttf')

0 commit comments

Comments
 (0)