Making a Menu with Arcade’s GUI

This tutorial shows how to use most of Arcade’s gui’s widgets.
Step 1: Open a Window

First, let’s start a blank window with a view.
1"""
2Menu.
3
4Shows the usage of almost every gui widget, switching views and making a modal.
5"""
6import arcade
7
8# Screen title and size
9SCREEN_WIDTH = 800
10SCREEN_HEIGHT = 600
11SCREEN_TITLE = "Making a Menu"
12
13
14class MainView(arcade.View):
15 """ Main application class."""
16
17 def __init__(self):
18 super().__init__()
19
20 def on_show_view(self):
21 """ This is run once when we switch to this view """
22 arcade.set_background_color(arcade.color.DARK_BLUE_GRAY)
23
24 def on_draw(self):
25 """ Render the screen. """
26 # Clear the screen
27 self.clear()
28
29
30def main():
31 window = arcade.Window(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE, resizable=True)
32 main_view = MainView()
33 window.show_view(main_view)
34 arcade.run()
35
36
37if __name__ == "__main__":
38 main()
Step 2: Switching to Menu View

For this section we will switch the current view of the window to the menu view.
Imports
First we will import the Arcade gui:
import arcade
import arcade.gui
Modify the MainView
We are going to add a button to change the view. For drawing a button we would
need a UIManager
.
def __init__(self):
super().__init__()
self.manager = arcade.gui.UIManager()
After initialising the manager we need to enable it when the view is shown and disable it when the view is hidden.
def on_show_view(self):
"""This is run once when we switch to this view"""
arcade.set_background_color(arcade.color.DARK_BLUE_GRAY)
# Enable the UIManager when the view is showm.
self.manager.enable()
def on_hide_view(self):
# Disable the UIManager when the view is hidden.
self.manager.disable()
We also need to draw the children of the menu in on_draw
.
Drawing UI on screendef on_draw(self): """Render the screen.""" # Clear the screen self.clear() # Draw the manager. self.manager.draw()
Now we have successfully setup the manager, we can now add a button to the view.
We are using UIAnchorLayout
to position the button. We also setup a function
which is called when the button is clicked.
Initialising the Buttondef __init__(self): super().__init__() self.manager = arcade.gui.UIManager() switch_menu_button = arcade.gui.UIFlatButton(text="Pause", width=250) # Initialise the button with an on_click event. @switch_menu_button.event("on_click") def on_click_switch_button(event): # Passing the main view into menu view as an argument. menu_view = MenuView(self) self.window.show_view(menu_view) # Use the anchor to position the button on the screen. self.anchor = self.manager.add(arcade.gui.UIAnchorLayout()) self.anchor.add( anchor_x="center_x", anchor_y="center_y", child=switch_menu_button, )
Initialise the Menu View
We make a boiler plate view just like we did in Step-1 for switiching the view when the pause button is clicked.
class MenuView(arcade.View):
"""Main menu view class."""
def __init__(self, main_view):
super().__init__()
self.manager = arcade.gui.UIManager()
self.main_view = main_view
def on_hide_view(self):
# Disable the UIManager when the view is hidden.
self.manager.disable()
def on_show_view(self):
"""This is run once when we switch to this view"""
# Makes the background darker
arcade.set_background_color([rgb - 50 for rgb in arcade.color.DARK_BLUE_GRAY])
self.manager.enable()
def on_draw(self):
"""Render the screen."""
# Clear the screen
self.clear()
self.manager.draw()
Program Listings
menu_02.py Full Listing ← Where we are right now
menu_02.py Diff ← What we changed to get here
Step 3: Setting Up the Menu View

In this step we will setup the display buttons of the actual menu. The code
written in this section is written for MenuView
Initialising the Buttons
First we setup buttons for resume, starting a new game, volume, options and exit.
def __init__(self, main_view):
super().__init__()
self.manager = arcade.gui.UIManager()
resume = arcade.gui.UIFlatButton(text="Resume", width=150)
start_new_game = arcade.gui.UIFlatButton(text="Start New Game", width=150)
volume = arcade.gui.UIFlatButton(text="Volume", width=150)
options = arcade.gui.UIFlatButton(text="Options", width=150)
exit = arcade.gui.UIFlatButton(text="Exit", width=320)
Displaying the Buttons in a Grid
After setting up the buttons we add them to UIGridLayout
, so that they can
displayed in a grid like manner.
def __init__(self, main_view):
super().__init__()
self.manager = arcade.gui.UIManager()
resume = arcade.gui.UIFlatButton(text="Resume", width=150)
start_new_game = arcade.gui.UIFlatButton(text="Start New Game", width=150)
volume = arcade.gui.UIFlatButton(text="Volume", width=150)
options = arcade.gui.UIFlatButton(text="Options", width=150)
exit = arcade.gui.UIFlatButton(text="Exit", width=320)
# Initialise a grid in which widgets can be arranged.
self.grid = arcade.gui.UIGridLayout(
column_count=2, row_count=3, horizontal_spacing=20, vertical_spacing=20
)
# Adding the buttons to the layout.
self.grid.add(resume, column=0, row=0)
self.grid.add(start_new_game, column=1, row=0)
self.grid.add(volume, column=0, row=1)
self.grid.add(options, column=1, row=1)
self.grid.add(exit, column=0, row=2, column_span=2)
Final code for the __init__
method after these.
def __init__(self, main_view):
super().__init__()
self.manager = arcade.gui.UIManager()
resume = arcade.gui.UIFlatButton(text="Resume", width=150)
start_new_game = arcade.gui.UIFlatButton(text="Start New Game", width=150)
volume = arcade.gui.UIFlatButton(text="Volume", width=150)
options = arcade.gui.UIFlatButton(text="Options", width=150)
exit = arcade.gui.UIFlatButton(text="Exit", width=320)
# Initialise a grid in which widgets can be arranged.
self.grid = arcade.gui.UIGridLayout(
column_count=2, row_count=3, horizontal_spacing=20, vertical_spacing=20
)
# Adding the buttons to the layout.
self.grid.add(resume, column=0, row=0)
self.grid.add(start_new_game, column=1, row=0)
self.grid.add(volume, column=0, row=1)
self.grid.add(options, column=1, row=1)
self.grid.add(exit, column=0, row=2, column_span=2)
self.anchor = self.manager.add(arcade.gui.UIAnchorLayout())
self.anchor.add(
anchor_x="center_x",
anchor_y="center_y",
child=self.grid,
)
self.main_view = main_view
Program Listings
menu_03.py Full Listing ← Where we are right now
menu_03.py Diff ← What we changed to get here
Step 4: Configuring the Menu Buttons

We basically add event listener for on_click
for buttons.
Adding on_click
Callback for Resume, Start New Game and Exit
First we will add the event listener to resume, start_new_game and exit button as they don’t have much to explain.
@resume_button.event("on_click")
def on_click_resume_button(event):
# Pass already created view because we are resuming.
self.window.show_view(self.main_view)
@start_new_game_button.event("on_click")
def on_click_start_new_game_button(event):
# Create a new view because we are starting a new game.
main_view = MainView()
self.window.show_view(main_view)
@exit_button.event("on_click")
def on_click_exit_button(event):
arcade.exit()
Adding on_click
Callback for Volume and Options
Now we need to implement an actual menu for volume and options, for that we have
to make a class that acts like a window. Using UIMouseFilterMixin
we catch
all the events happening for the parent and respond nothing to them. Thus
making it act like a window/view.
class SubMenu(arcade.gui.UIMouseFilterMixin, arcade.gui.UIAnchorLayout):
"""Acts like a fake view/window."""
def __init__(
self,
):
super().__init__(size_hint=(1, 1))
# Setup frame which will act like the window.
frame = self.add(arcade.gui.UIAnchorLayout(width=300, height=400, size_hint=None))
frame.with_padding(all=20)
# Add a background to the window.
frame.with_background(
texture=arcade.gui.NinePatchTexture(
left=7,
right=7,
bottom=7,
top=7,
texture=arcade.load_texture(
":resources:gui_basic_assets/window/dark_blue_gray_panel.png"
),
)
)
back_button = arcade.gui.UIFlatButton(text="Back", width=250)
# The type of event listener we used earlier for the button will not work here.
back_button.on_click = self.on_click_back_button
# Internal widget layout to handle widgets in this class.
widget_layout = arcade.gui.UIBoxLayout(align="left", space_between=10)
widget_layout.add(back_button)
frame.add(child=widget_layout, anchor_x="center_x", anchor_y="top")
def on_click_back_button(self, event):
# Removes the widget from the manager.
# After this the manager will respond to its events like it previously did.
self.parent.remove(self)
We have got ourselves a fake window currently. We now, pair it up with the volume and options button to trigger it when they are clicked.
@volume_button.event("on_click")
def on_click_volume_button(event):
volume_menu = SubMenu()
self.manager.add(volume_menu, layer=1)
@options_button.event("on_click")
def on_click_options_button(event):
options_menu = SubMenu()
self.manager.add(options_menu, layer=1)
Program Listings
menu_04.py Full Listing ← Where we are right now
menu_04.py Diff ← What we changed to get here
Step 5: Finalising the Fake Window aka the Sub Menu

We finalise the menu or you can call it the last step!
Editing the Parameters for the Sub Menu
We will edit the parameters for the sub menu to suit our needs. Will explain later why are those parameters needed.
def __init__(
self,
title: str,
input_text: str,
toggle_label: str,
dropdown_options: List[str],
slider_label: str,
):
We also need to change accordingly the places where we have used this class i.e
options and volume on_click
event listener. The layer parameter being set
1, means that this layer is always drawn on top i.e.its the first layer.
@volume_button.event("on_click")
def on_click_volume_button(event):
volume_menu = SubMenu(
"Volume Menu",
"How do you like your volume?",
"Enable Sound",
["Play: Rock", "Play: Punk", "Play: Pop"],
"Adjust Volume",
)
self.manager.add(volume_menu, layer=1)
@options_button.event("on_click")
def on_click_options_button(event):
options_menu = SubMenu(
"Funny Menu",
"Too much fun here",
"Fun?",
["Make Fun", "Enjoy Fun", "Like Fun"],
"Adjust Fun",
)
self.manager.add(options_menu, layer=1)
- Now you might be getting a little idea why we have edited the parameters but
follow on to actually know the reason.
Adding a Title label
We will be adding a UILabel
that explains the menu. UISpace
is a widget
that can be used to add space around some widget, you can set its color to the
background color so it appears invisible.
title_label = arcade.gui.UILabel(text=title, align="center", font_size=20, multiline=False)
# Adding some extra space around the title.
title_label_space = arcade.gui.UISpace(height=30, color=arcade.color.DARK_BLUE_GRAY)
Adding it to the widget layout.
widget_layout.add(title_label)
widget_layout.add(title_label_space)
Adding a Input Field
We will use UIInputText
to add an input field. The with_border()
function creates a border around the widget with color(default argument is
black) black and thickness(default argument is 2px) 2px. Add this just below
the title label.
input_text_widget = arcade.gui.UIInputText(text=input_text, width=250).with_border()
Adding it to the widget layout.
widget_layout.add(input_text_widget)
If you paid attention when we defined the input_text
variable we passed the
text
parameter with our input_text_default
argument. We basically added
those parameters in our sub menu so that it can be used by both volume and
options button, with texts respecting their names. We will repeat this again
in the last also for those of you who are skipping through this section :P.
Adding a Toggle Button
Don’t go on the section title much, in Arcade the UITextureToggle
is not
really a button it switches between two textures when clicked. Yes, it
functions like a button but by “is not really a button” we meant that it
doesn’t inherits the button class. We also pair it up horizontally with the
toggle label.
# Load the on-off textures.
on_texture = arcade.load_texture(
":resources:gui_basic_assets/simple_checkbox/circle_on.png"
)
off_texture = arcade.load_texture(
":resources:gui_basic_assets/simple_checkbox/circle_off.png"
)
# Create the on-off toggle and a label
toggle_label = arcade.gui.UILabel(text=toggle_label)
toggle = arcade.gui.UITextureToggle(
on_texture=on_texture, off_texture=off_texture, width=20, height=20
)
# Align toggle and label horizontally next to each other
toggle_group = arcade.gui.UIBoxLayout(vertical=False, space_between=5)
toggle_group.add(toggle)
toggle_group.add(toggle_label)
Adding it to the widget layout. Add this line after you have added the input field.
widget_layout.add(toggle_group)
Adding a Dropdown
We add a dropdown by using UIDropdown
.
dropdown = arcade.gui.UIDropdown(
default=dropdown_options[0], options=dropdown_options, height=20, width=250
)
Adding it to the widget layout.
widget_layout.add(dropdown)
Adding a Slider
The final widget. In Arcade you can use UISlider
to implement a slider.
Theres a functionality to style the slider, this is also present for
UIFlatButton
and UITextureButton
.
slider_label = arcade.gui.UILabel(text=slider_label)
pressed_style = arcade.gui.UISlider.UIStyle(
filled_track=arcade.color.GREEN, unfilled_track=arcade.color.RED
)
default_style = arcade.gui.UISlider.UIStyle()
style_dict = {
"press": pressed_style,
"normal": default_style,
"hover": default_style,
"disabled": default_style,
}
# Configuring the styles is optional.
slider = arcade.gui.UISlider(value=50, width=250, style=style_dict)
Adding it to the widget layout.
widget_layout.add(slider_label)
widget_layout.add(slider)
Finishing touches
As we mentioned earlier, to explain the use of those parameters to the class. We basically used them so it can be used by both options and volume as we wanted to have different text for both. For those who have read the full tutorial line-by-line; ‘They will never know’. :D. We also recommend to see the full code for this section.
Program Listings
menu_05.py Full Listing ← Where we are right now
menu_05.py Diff ← What we changed to get here