Instantly share code, notes, and snippets.
Created
August 12, 2022 08:13
-
Save horstjens/b4be64bd8785b079ed006b6026bcd160 to your computer and use it in GitHub Desktop.
pygame_textmenu
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# generic menu for pygame. see textMenu.py for a simplier version | |
import os | |
import pygame | |
class MenuItem: | |
def __init__(self, name="dummy", choices=[], cindex=0, helptext=None, rect=None): | |
""" | |
Menu Item. For use inside of a Menu.items list. | |
:param name: the name of the Item (Menupoint). | |
:param choices: a list of Strings with the valid values this Menupoint can have (for example font sizes) | |
:param cindex: the index of the currently selected choice | |
:param helptext: optional helptext for this menupoint | |
:param rect: pygame.Rect information, will be automatically written and used by PygameMenu.run() | |
""" | |
self.name = name | |
self.choices = choices | |
self.cindex = cindex | |
self.helptext = helptext | |
self.rect = rect | |
class Menu: | |
# TODO: better nested datastructure, maybe ChainMap? | |
def __init__(self, name="root", items=[], rect=None): | |
""" | |
Menu , a list of Menu Items and Submenus | |
Every Submenu (except the 'root' main menu) get's automatically added an Item('back') | |
:param name: Name of the submenu or 'root' | |
:param items: a list of Item or Menu instances | |
:param rect: pygame.Rect information, will be automatically written and used by PygameMenu.run() | |
""" | |
self.name = name | |
self.items = items | |
self.rect = rect | |
self.add_back_item() # autoexec: add 'back' as first item if necessary | |
def add_back_item(self): | |
if self.name != "root": | |
if "back" not in [i.name for i in self.items]: | |
self.items.insert(0, MenuItem("back")) | |
class Viewer: | |
"""pygame Viewer, initializes pygame screen and has a self.run() method with a main loop""" | |
width: int | |
height: int | |
screenrect: pygame.Rect | |
screen = None | |
background = None | |
font = None | |
menu = None | |
def __init__(self, width=800, height=600): | |
# ---- pygame init | |
pygame.init() | |
Viewer.width = width | |
Viewer.height = height | |
self.setup_screen(width, height) | |
# -------- autoexec ------ | |
#self.create_menu() | |
#self.run() | |
def setup_screen(self, width, height, backgroundcolor=(255,255,255)): | |
Viewer.screenrect = pygame.Rect(0, 0, width, height) | |
Viewer.screen = pygame.display.set_mode( | |
(width, height), pygame.DOUBLEBUF | |
) | |
Viewer.background = pygame.Surface((width, height)) | |
Viewer.background.fill(backgroundcolor) | |
def create_menu(self): | |
# ----------- create menu ---------------------- | |
# start with the submenus, work your way up to the root menu. | |
# ---- audio (submenu of settings)---- | |
audiomenu = Menu(name="audio", items=[ | |
MenuItem("sound effects", choices=["on", "off"], cindex=0), | |
MenuItem("music", choices=["on", "off"], cindex=0), | |
]) | |
# ---- video (submenu of settings) ---- | |
# ----list of possible video resolutions without double entries -> set ---- | |
reslist = list(set(pygame.display.list_modes(flags=pygame.FULLSCREEN))) | |
reslist.sort() # sort the list from smalles resolution to biggest | |
# concert list of tuples( int,int) into list of strings | |
# print("reslist", reslist) | |
reslist = ["x".join((str(x), str(y))) for (x, y) in reslist] | |
# print("reslist", reslist) | |
videomenu = Menu(name="video", items=[ | |
MenuItem("toggle fullscreen"), #, choices=["on", "off"], cindex=0), | |
MenuItem("screen resolution", choices=reslist, cindex=4) | |
]) | |
# --- color (sub-menu of settings)---- | |
# --- prepare lists for acceptable values ----- | |
# --- list of some hexadecimal color tuples: red, green, blue ---- | |
colors = [str(hex(r))[-2:].replace("x", "0") + str(hex(g))[-2:].replace("x", "0") +str(hex(b))[-2:].replace("x", "0") | |
for r in (0,128,255) for g in (0,128,255) for b in (0,128,255)] | |
colormenu = Menu(name="colors", items=[ | |
MenuItem("color_background", choices=colors, cindex=-2, helptext="hexadecimal values for red, green, blue. (00=0, ff=255)"), | |
MenuItem("color_small_font1", choices=colors, cindex=3, helptext="hexadecimal values for red, green, blue. (00=0, ff=255)"), | |
MenuItem("color_small_font2", choices=colors, cindex=7, helptext="hexadecimal values for red, green, blue. (00=0, ff=255)"), | |
MenuItem("color_big_font", choices=colors, cindex=4, helptext="hexadecimal values for red, green, blue. (00=0, ff=255)"), | |
]) | |
# ------ fontsize (submenu of settings) ------ | |
# --- prepare list for acceptable values --- | |
# create a list of fontsizes and convert it into string | |
fontsizes1 = [str(x) for x in range(8, 24, 1)] | |
fontsizes2 = [str(x) for x in range(10, 42, 2)] | |
fontsizemenu = Menu(name="fontsizes", items=[ | |
MenuItem("fontsize_small", choices=fontsizes1, cindex=3), | |
MenuItem("fontsize_big", choices=fontsizes2, cindex=8), | |
]) | |
# ---- settings (submenu of root) --- | |
settingsmenu = Menu(name="settings", items=[ | |
audiomenu, | |
videomenu, | |
fontsizemenu, | |
colormenu | |
]) | |
# ---- merge all submenus into root menu ------ | |
rootmenu = Menu("root", [MenuItem("play"), MenuItem("credits"), settingsmenu, MenuItem("quit")]) | |
# ---- create a PygameMenu and store it into the class variable Viewer.menu1 ---- | |
Viewer.menu1 = PygameMenu(rootmenu) | |
## to set the active menu at start to a submenu, give the whole historylist as argument: | |
#Viewer.menu1 = PygameMenu([rootmenu, settingsmenu, audiomenu]) | |
print("menu1:") | |
print(Viewer.menu1) | |
print("choicesdict:") | |
r = Viewer.menu1.make_choices_dict(rootmenu) | |
print(r) | |
def run(self): | |
# ---- main loop ---- | |
running = True | |
while running: | |
# ---------get a command from the menu --------------- | |
# --------- save the current values of the menu ------------- | |
#menuvalues_old = {} | |
#for k, v in Viewer.menu1.make_choices_dict(Viewer.menu1.rootmenu).items(): | |
# menuvalues_old[k] = v | |
menuvalues_old = Viewer.menu1.make_choices_dict(Viewer.menu1.rootmenu).copy() | |
#print("old:", menuvalues_old) | |
# ------- get new command ------ | |
command, choice = Viewer.menu1.run() | |
menuvalues_new = Viewer.menu1.make_choices_dict(Viewer.menu1.rootmenu) | |
print("command is:", command, "choice is:", choice) | |
# ---- excecute commands ---- | |
#if command == "cancel": | |
# # write old menus back into menu | |
# for k, v in menuvalues_old: | |
# Viewer.menu1.update_choice(k,v) # TODO write choices back into menu | |
if command == "play": | |
## start game code here | |
print("playing a game...") | |
elif command == "cancel": | |
print("cancel new values, restoring previous values...") | |
for k, v in menuvalues_old.items(): | |
rootmenu = Viewer.menu1.history[0] | |
print("restoring", k, "to", v) | |
Viewer.menu1.set_choice(rootmenu, k, v) | |
elif command == "quit": | |
running = False | |
elif command == "toggle fullscreen": | |
print("Fullscreen is now:", pygame.display.toggle_fullscreen()) | |
# --------------update all game settings that have been changed ------------ | |
for k, v in menuvalues_new.items(): | |
#print("comparing:", k,v, menuvalues_old[k]) | |
if menuvalues_old[k] != v: | |
#print("change in ", k, v) | |
# value has changed. update: | |
if k == "color_background": | |
# color is hex value change back into decimal | |
Viewer.menu1.background.fill(pygame.Color(int(v[0:2],16), int(v[2:4], 16), int(v[4:6],16))) | |
if k == "color_small_font1": | |
Viewer.menu1.helptextcolor1 = pygame.Color(int(v[0:2],16), int(v[2:4], 16), int(v[4:6],16)) | |
if k == "color_small_font2": | |
Viewer.menu1.helptextcolor2 = pygame.Color(int(v[0:2],16), int(v[2:4], 16), int(v[4:6],16)) | |
if k == "color_big_font": | |
Viewer.menu1.textcolor = pygame.Color(int(v[0:2],16), int(v[2:4], 16), int(v[4:6],16)) | |
if k == "fontsize_small": | |
Viewer.menu1.helptextfontsize = int(v) | |
if k == "fontsize_big": | |
Viewer.menu1.fontsize = int(v) | |
if k == "screen resolution": | |
# resolution is string like '800x600' -> create integer values for x,y: | |
x, y = int(v.split("x")[0]), int(v.split("x")[1]) | |
#pygame.display.set_mode((x, y)) | |
pygame.display.set_mode( | |
(x,y), pygame.DOUBLEBUF | |
) | |
self.setup_screen(x, y) | |
Viewer.menu1.screen = self.screen | |
Viewer.menu1.background = self.background | |
# ------------------------- | |
print("end of mainloop") | |
pygame.quit() | |
class PygameMenu: | |
def __init__(self, | |
rootmenu_or_historylist, | |
cursortext="-->", | |
startIndex = 0, | |
cycle_up_down=False, | |
cursorTextList = ["→ ", "-→ ", "--→"], | |
cursorAnimTime = 550, | |
menutime=0, | |
textcolor=(0,0,225), | |
background=None, | |
screen=None, | |
fontsize=24, | |
fontname="mono", | |
yspacing=10, | |
helptextheight = 100, | |
helptextcolor1 = (0,0,0), | |
helptextcolor2=(0, 200, 200), | |
helptextfontsize = 15, | |
) -> (str, str): | |
""" | |
A pygame Menu. It returns a list of strings: [selected MenuItem, selected choice (or None)] | |
command starting with _ | |
:rtype: selected Menupoint (string) | |
:param rootmenu_or_historylist: Menu or list of Menus. If list of menu is given, it must be a walkable history like [rootmenu, settingsmenu, audiomenu] complete Menu structure, including all submenus | |
:param cursortext: string to be used as cursor, like '-->' | |
:param startIndex: where to set the cursor | |
:param cycle_up_down: when set to True, the cursor cycles throug Items. When set to False, the cursor stop at first/last item | |
:param cursorTextList: a list of cursor strings, for cursor animation | |
:param cursorAnimTime: how many seconds each Cursor string stays visible. if cursorTextList has 4 strings and AnimTime is set to 1 second, then each string is shown 1/4 = 0.25 seconds | |
:param menutime: age of menu in seconds | |
:param textcolor: color for (big) text of Menu Items | |
:param background: pygame Surface | |
:param screen: pygame Surface | |
:param fontsize: font size for (big) text of Menu Items | |
:param fontname: pygame Font name, default to 'mono' | |
:param yspacing: distance in pixel below each Menu Item | |
:param helptextheight: distance from top screen before Menu Items are blitted ( top border, reserved for helptext) | |
:param helptextcolor1: color1 of (small) help text | |
:param helptextcolor2: color2 of (small) help text | |
:param helptextfontsize: fontsize of small help text | |
""" | |
# --- start-------- | |
# if rootmenu is a list of Menus, fill history with it. Otherwise, only rootmenu is in history | |
if type(rootmenu_or_historylist) == Menu: | |
self.rootmenu = rootmenu_or_historylist | |
self.history = [rootmenu_or_historylist,] # traceback, must be empty list at start | |
elif type(rootmenu_or_historylist) == list: | |
self.rootmenu = rootmenu_or_historylist[0] | |
self.history = rootmenu_or_historylist | |
else: | |
raise ValueError("rootmenu must be a Menu instance or a list of Menuinstances ") | |
self.menu = self.history[-1] # active menu is the last item in history | |
self.i = startIndex | |
self.cursortext = cursortext | |
self.cycle_up_down = cycle_up_down | |
#---- pygame variables | |
self.cursorTextList = cursorTextList | |
self.cursorAnimTime = cursorAnimTime | |
self.menutime = menutime # age of menu in seconds | |
if background is None: | |
self.background = Viewer.background # pygame surface to blit | |
else: | |
self.background = background | |
if screen is None: | |
self.screen = Viewer.screen | |
else: | |
self.screen = screen | |
self.screenrect = self.screen.get_rect() | |
self.textcolor = textcolor # black | |
self.clock = pygame.time.Clock() | |
self.fps = 400 | |
self.fontsize = fontsize | |
self.fontname = fontname | |
self.yspacing = yspacing # pixel vertically between text lines | |
self.helptextheight = helptextheight # pixel distance to top border of window, to display helptext | |
self.helptextcolor1 = helptextcolor1 | |
self.helptextcolor2 = helptextcolor2 | |
self.helptextfontsize = helptextfontsize | |
# ------ | |
@property | |
def font(self): | |
"""read only attribute, influened by fontname and fontsize""" | |
return pygame.font.SysFont(name=self.fontname, size=self.fontsize, bold=True, italic= False) | |
@property | |
def smallfont(self): | |
"""read only attribute, influened by fontname and helptextfontsize""" | |
return pygame.font.SysFont(name=self.fontname, size=self.helptextfontsize, bold=True, italic=False) | |
def cursor_up(self, menupoints): | |
""" move cursor up to previous menupoint """ | |
self.i -= 1 | |
if self.i < 0: | |
if self.cycle_up_down: | |
self.i = len(menupoints) - 1 | |
else: | |
self.i = 0 | |
pygame.mouse.set_pos(self.menu.items[self.i].rect.center) | |
def cursor_down(self, menupoints): | |
""" move cursor down to next menupoint """ | |
self.i += 1 | |
if self.i >= len(menupoints): | |
if self.cycle_up_down: | |
self.i = 0 | |
else: | |
self.i = len(menupoints) - 1 | |
pygame.mouse.set_pos(self.menu.items[self.i].rect.center) | |
def cursor_back(self): | |
"""go back in history to previous menu""" | |
if len(self.history) <= 1: | |
self.menu = self.rootmenu | |
print("you are already at root menu... going back is not possible from here") | |
return | |
self.history.pop() # delete last entry in history list | |
# start from root until the desired menu is found | |
self.menu = self.history[-1] | |
self.i = 0 | |
def cursor_goto_menu(self, targetname, menu): | |
"""recursive serach over ALL menus to go to targetname""" | |
for item in menu.items: | |
print("searching", targetname , " checking item:", item, "in menu:", menu.name) | |
if type(item) == Menu: | |
if item.name == targetname: | |
self.menu = item | |
self.i = 0 | |
return True | |
if self.cursor_goto_menu(targetname, item): | |
return True | |
return False | |
def cursor_goto_submenu(self, name): | |
"""change menu into 'name', witch must be on of the current Menu Items """ | |
if name not in [item.name for item in self.menu.items if type(item) == Menu]: | |
raise ValueError(f"no submenu named {name} in current Menuitems: {self.menu.items}") | |
for item in self.menu.items: | |
if item.name == name and type(item) == Menu: | |
self.menu = item | |
#self.history.append(name) | |
self.history.append(item) | |
self.i = 0 | |
return | |
raise ValueError("no matching menu found...") | |
def next_choice(self): | |
"""select next choice for the active Item""" | |
activeitem = self.menu.items[self.i] | |
if type(activeitem) != MenuItem: | |
return # it's not an Item | |
if len(activeitem.choices) <= 1: | |
return # nothing to change here | |
activeitem.cindex += 1 | |
if activeitem.cindex >= len(activeitem.choices): | |
activeitem.cindex = len(activeitem.choices) - 1 | |
def previous_choice(self): | |
"""select previous choice for the active Item""" | |
activeitem = self.menu.items[self.i] | |
if type(activeitem) != MenuItem: | |
return # it's not an Item | |
if len(activeitem.choices) <= 1: | |
return # nothing to change here | |
activeitem.cindex -= 1 | |
if activeitem.cindex < 0: | |
activeitem.cindex = 0 | |
def make_choices_dict(self, menu, result={}): | |
""" | |
recursive crawl over all items and return a dict with all choices and their currently ativce values | |
asserts that all Items have unique names | |
""" | |
for item in menu.items: | |
if type(item) == MenuItem: | |
if len(item.choices) > 0: | |
result[item.name] = item.choices[item.cindex] | |
else: | |
result = self.make_choices_dict(menu=item, result=result) | |
return result | |
def set_choice(self, menu, itemname, choicevalue ): | |
"""recursive crawl over all items and set the cindex of choices to the corresponding key,value pair of choicesdict.""" | |
for item in menu.items: | |
if type(item) == MenuItem: | |
if item.name == itemname: | |
for i, choice in enumerate(item.choices): | |
if choice == choicevalue: | |
item.cindex = i # found it! | |
return True | |
raise ValueError(f"found {itemname} in menu but not {choicevalue} in choices: {item.choices}") | |
elif type(item) == Menu: | |
return self.set_choice(item, itemname, choicevalue) | |
return False | |
def run(self): | |
""" | |
runs the Menu and returns [selected Item, selected Choice]. | |
if menu.run() is called inside a game loop it stays visible, until Viewer.screen is changed | |
""" | |
cx = 100 # topleft point for menu (cursor is LEFT of this!) | |
cy = 100 | |
helpx = 10 # topleft of helptext (several rows!) | |
helpy = 10 # | |
historyx = 10 # topleft of history text | |
historyy = cy - 30 | |
dy = 25 # y-distance between lines of menuitems | |
choicedistancex = 50 # padding between right screen edge and choiceslist | |
choicedistancey = 5 # between each line | |
running = True | |
# ---------------------- main loop ------------------------- | |
while running: | |
milliseconds = self.clock.tick(self.fps) # | |
seconds = milliseconds / 1000 | |
self.menutime += seconds | |
# ---------- clear all -------------- | |
self.screen.blit(self.background, (0, 0)) | |
# pygame.display.set_icon(self.icon) | |
# ----- get current menupoints and selection ------ | |
menupoints = [p for p in self.menu.items] | |
selection = self.menu.items[self.i] # self.menudict[self.menuname][self.i] | |
# ------- cursor animation -------- | |
maxcursordistance = 20 | |
# how many seconds each Cursor string stays visible | |
anim = int(self.menutime // self.cursorAnimTime) % len(self.cursorTextList) | |
cursortext = self.cursorTextList[anim] | |
cursordistance = 0 | |
cursorcolor = self.textcolor | |
# ----------- writing history on screen ---------- | |
#if len(self.history) == 0: | |
# historytext = "You are here: root" | |
#else: | |
# historytext = "You are here: root>{}".format(">".join(self.history)) | |
historytext = "You are here: {}".format(self.history[0].name if len(self.history)==1 else ">".join([h.name for h in self.history])) | |
write(self.screen, historytext, historyx, historyy, self.textcolor, self.smallfont, origin="topleft" ) | |
# ------- write cursor and entry -------- | |
maxwidth = 0 | |
for i, entry in enumerate(menupoints): | |
if i == self.i: | |
# ----write cursor --- | |
write(self.screen, cursortext, cx - maxcursordistance + cursordistance, cy + dy * i , | |
cursorcolor, self.font, origin="topright") | |
# ----------- write entry --- | |
w,h = write(self.screen, entry.name, cx, cy + dy * i, self.textcolor, self.font, origin="topleft") | |
entry.rect = pygame.Rect(cx, cy + dy*i, w, h) # update rect information | |
#pygame.draw.rect(self.screen, (50,50,50), entry.rect, 1) | |
# ----write indicator to the right if entry is a submenu ---- | |
maxwidth = max(maxwidth, w) | |
if type(entry) == Menu: | |
w2, h2 = write(self.screen, " >", cx + w, cy+dy*i, self.textcolor, self.font, origin="topleft") | |
maxwidth = max(maxwidth, w+w2) | |
elif type(entry) == MenuItem and len(entry.choices) > 0: | |
# ----- write currently selected choice if entry is an Item -------- | |
w2, h2 = write(self.screen, ": "+ entry.choices[entry.cindex], cx+w, cy+dy*i, self.textcolor, self.font, origin="topleft") | |
maxwidth = max(maxwidth, w+w2) | |
# --- maxwitdth is now calculated for all items in this menu --- | |
# ---- write list of choices for active Item ---- | |
activeitem = self.menu.items[self.i] | |
# ---- write general helptext --- | |
t = "press \u2191 \u2193 to navigate, ESC to quit {}".format("\u21D0 for previous menu " if self.menu.name != "root" else "") | |
w, h = write(self.screen,t , helpx, helpy, self.helptextcolor1, self.smallfont, origin="topleft") | |
# ----- write specific helptext ---- | |
if type(activeitem) == Menu: | |
write(self.screen, ", ENTER/Leftclick for submenu", helpx + w, helpy, self.helptextcolor1, self.smallfont, origin="topleft" ) | |
elif type(activeitem) == MenuItem: | |
if len(activeitem.choices) <= 1: | |
# write in same line | |
w, h2= write(self.screen, ", ENTER/Leftclick to activate", helpx + w, helpy, self.helptextcolor1, self.smallfont, origin="topleft") | |
elif len(activeitem.choices) > 1: | |
# write in new line | |
w,h2 = write(self.screen, "press \u2190 \u2192 /Mousewheel/PgUp/PgDn to select, ENTER/Leftclick to accept, c to cancel", helpx, helpy+h , self.helptextcolor1, self.smallfont, origin="topleft") | |
if activeitem.helptext is not None: | |
write(self.screen, activeitem.helptext, helpx, helpy+h+h2, self.helptextcolor2, self.smallfont, origin="topleft") | |
if type(activeitem) == MenuItem and len(activeitem.choices) > 1: | |
# ----------------- write list of choices --------------------- | |
# topleft startpoint for choices: | |
ox, oy = Viewer.width - maxwidth - choicedistancex, 0 | |
choicerects = [] | |
max_w, max_h = 0,0 | |
# ----- calculate y position of choice entries ----- | |
for ctext in activeitem.choices: | |
if activeitem.name[0:9] == "fontsize_": | |
font = pygame.font.SysFont(name=self.fontname, size=int(ctext), bold=True, italic=False) | |
else: | |
font = self.smallfont | |
w, h = font.size(ctext) | |
max_w = max(max_w, w) | |
choicerects.append(pygame.Rect(ox, oy, w, h)) | |
oy += choicedistancey + h | |
max_h = oy + h | |
# make one giant surface with all choicetextes | |
choices_surface = pygame.Surface((max_w, max_h)) | |
choices_surface.fill((255,255,255)) # choices_surface always has a white background | |
# ----- write choice entry into choice surface -------- | |
for i,ctext in enumerate(activeitem.choices): | |
# special colors if it is a hex-value | |
if activeitem.name[0:6] == "color_": | |
color = (int(ctext[0:2], 16), int(ctext[2:4], 16), int(ctext[4:6],16)) # hex -> decimal | |
else: | |
color = self.textcolor | |
# special fontsize for fonsize-choices | |
if activeitem.name[0:9] == "fontsize_": | |
font = pygame.font.SysFont(name=self.fontname, size=int(ctext), bold=True, italic= False) | |
else: | |
font = self.smallfont | |
write(choices_surface, ctext, 0, choicerects[i].y, color, font, origin="topleft" ) | |
##pygame.draw.rect(choices_surface, (5,5,5), (0,0, max_w, max_h), 3) | |
# ----- blit the choice-surface on self.screen , | |
y1 = self.menu.items[self.i].rect.y + self.menu.items[self.i].rect.height // 2 | |
self.screen.blit(choices_surface, (Viewer.width-max_w-50, y1-choicerects[0].height//2-choicerects[activeitem.cindex].y)) | |
# ---- paint line from active item to currently active choice ----- | |
x1 = cx + maxwidth + 5 | |
x3 = Viewer.width - choicedistancex - max_w - 5 | |
y1 = self.menu.items[self.i].rect.y + self.menu.items[self.i].rect.height //2 | |
pygame.draw.line(self.screen, self.textcolor, (x1, y1), (x3,y1), 1) | |
# --------- event handler ----------------- | |
# ------ mouse pointer over menu item ------ | |
for i, item in enumerate(self.menu.items): | |
if item.rect.collidepoint(item.rect.centerx, pygame.mouse.get_pos()[1]): | |
self.i = i | |
# -------- events ------ | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
pygame.quit() | |
# ------- mouse wheel ----- | |
if event.type == pygame.MOUSEBUTTONDOWN: | |
if event.button == 4: # | |
self.next_choice() # mouse wheel up | |
elif event.button == 5: | |
self.previous_choice() # mouse wheel down | |
# ----------- left mouse click ---------- | |
elif event.button == 1: | |
# left mouse click | |
if selection.name == "back": | |
self.cursor_back() # go back to previous menu | |
elif type(selection) == Menu: | |
self.cursor_goto_submenu(selection.name) # jump into submenu | |
else: | |
return selection.name, selection.choices[selection.cindex] if len(selection.choices) > 0 else None | |
# ------- pressed and released key ------ | |
if event.type == pygame.KEYDOWN: | |
if event.key == pygame.K_ESCAPE: | |
return "quit", None | |
if event.key == pygame.K_c: | |
return "cancel", None | |
if event.key == pygame.K_UP or event.key == pygame.K_KP8: | |
self.cursor_up(menupoints) | |
if event.key == pygame.K_DOWN or event.key == pygame.K_KP2: | |
self.cursor_down(menupoints) | |
if event.key == pygame.K_BACKSPACE: | |
self.cursor_back() | |
if event.key in (pygame.K_SPACE, pygame.K_RIGHT, pygame.K_PLUS, pygame.K_KP_PLUS, pygame.K_KP_6): | |
self.next_choice() | |
if event.key in (pygame.K_LEFT, pygame.K_MINUS, pygame.K_KP_MINUS, pygame.K_KP_4): | |
self.previous_choice() | |
if event.key == pygame.K_PAGEUP: | |
for _ in range(15): | |
self.previous_choice() | |
if event.key == pygame.K_PAGEDOWN: | |
for _ in range(15): | |
self.next_choice() | |
if event.key == pygame.K_RETURN or event.key == pygame.K_KP_ENTER: | |
if selection.name == "back": | |
self.cursor_back() # go back to previous menu | |
elif type(selection) == Menu: | |
self.cursor_goto_submenu(selection.name) # jump into submenu | |
else: | |
return selection.name, selection.choices[selection.cindex] if len(selection.choices) > 0 else None | |
# ---------- end of event handler ----- | |
# --- special sprites --- | |
# self.menusprites.update(seconds) | |
# self.menusprites.draw(self.screen) | |
#-------- update screen ------------- | |
#pygame.display.set_caption(f"mouse xy: {pygame.mouse.get_pos()}") | |
pygame.display.flip() | |
# ----- useful functions ------- | |
def write( | |
background, | |
text, | |
x=50, | |
y=150, | |
color=(0, 0, 0), | |
font= None, | |
origin="topleft", | |
): | |
"""blit text on a given pygame surface (given as 'background') | |
:rtype: object | |
:param background: where to blit. pygame.Surface. | |
:param text: text to blit | |
:param x: x position of origin | |
:param y: y position of origin | |
:param color: pygame.Color object | |
:param font: pygame.Font object | |
:param origin: origin can be 'center', 'centercenter', 'topleft', 'topcenter', 'topright', 'centerleft', 'centerright', | |
'bottomleft', 'bottomcenter', 'bottomright' | |
:return: width, height | |
""" | |
if font is None: | |
font=pygame.font.SysFont("mono", 24, True), | |
width, height = font.size(text) | |
surface = font.render(text, True, color) | |
if origin == "center" or origin == "centercenter": | |
background.blit(surface, (x - width // 2, y - height // 2)) | |
elif origin == "topleft": | |
background.blit(surface, (x, y)) | |
elif origin == "topcenter": | |
background.blit(surface, (x - width // 2, y)) | |
elif origin == "topright": | |
background.blit(surface, (x - width, y)) | |
elif origin == "centerleft": | |
background.blit(surface, (x, y - height // 2)) | |
elif origin == "centerright": | |
background.blit(surface, (x - width, y - height // 2)) | |
elif origin == "bottomleft": | |
background.blit(surface, (x, y - height)) | |
elif origin == "bottomcenter": | |
background.blit(surface, (x - width // 2, y)) | |
elif origin == "bottomright": | |
background.blit(surface, (x - width, y - height)) | |
return width, height | |
if __name__ == "__main__": | |
os.environ['SDL_VIDEO_CENTERED'] = '1' #try to center pygame window on screen | |
v = Viewer(800,640) # initialize pygame, create Viewer instance | |
v.create_menu() # create PygameMenu instance as Viewer.menu1 | |
v.run() # call mainloop |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment