def __init__(self, title, message): self._window = None # A Dialog is just a widget, so you make its child a layout just like # a Window. dlg = gui.Dialog(title) # Add the message text em = self.window.theme.font_size dlg_layout = gui.Vert(em, gui.Margins(em, em, em, em)) dlg_layout.add_child(gui.Label(message)) # Add the Ok button. We need to define a callback function to handle # the click. ok_button = gui.Button("Ok") ok_button.set_on_clicked(self._on_ok) # We want the Ok button to be an the right side, so we need to add # a stretch item to the layout, otherwise the button will be the size # of the entire row. A stretch item takes up as much space as it can, # which forces the button to be its minimum size. button_layout = gui.Horiz() button_layout.add_stretch() button_layout.add_child(ok_button) # Add the button layout, dlg_layout.add_child(button_layout) # ... then add the layout as the child of the Dialog dlg.add_child(dlg_layout)
def _on_menu_about(self): # Show a simple dialog. Although the Dialog is actually a widget, you can # treat it similar to a Window for layout and put all the widgets in a # layout which you make the only child of the Dialog. em = self.window.theme.font_size dlg = gui.Dialog("About") # Add the text dlg_layout = gui.Vert(em, gui.Margins(em, em, em, em)) dlg_layout.add_child(gui.Label("Open3D GUI Example")) # Add the Ok button. We need to define a callback function to handle # the click. ok = gui.Button("OK") ok.set_on_clicked(self._on_about_ok) # We want the Ok button to be an the right side, so we need to add # a stretch item to the layout, otherwise the button will be the size # of the entire row. A stretch item takes up as much space as it can, # which forces the button to be its minimum size. h = gui.Horiz() h.add_stretch() h.add_child(ok) h.add_stretch() dlg_layout.add_child(h) dlg.add_child(dlg_layout) self.window.show_dialog(dlg)
def __init__(self, vfov=60, max_pcd_vertices=1 << 20, **callbacks): """Initialize. Args: vfov (float): Vertical field of view for the 3D scene. max_pcd_vertices (int): Maximum point clud verties for which memory is allocated. callbacks (dict of kwargs): Callbacks provided by the controller for various operations. """ self.vfov = vfov self.max_pcd_vertices = max_pcd_vertices gui.Application.instance.initialize() self.window = gui.Application.instance.create_window( "Open3D || Online RGBD Video Processing", 1280, 960) # Called on window layout (eg: resize) self.window.set_on_layout(self.on_layout) self.window.set_on_close(callbacks['on_window_close']) self.pcd_material = o3d.visualization.rendering.MaterialRecord() self.pcd_material.shader = "defaultLit" # Set n_pixels displayed for each 3D point, accounting for HiDPI scaling self.pcd_material.point_size = int(4 * self.window.scaling) # 3D scene self.pcdview = gui.SceneWidget() self.window.add_child(self.pcdview) self.pcdview.enable_scene_caching( True) # makes UI _much_ more responsive self.pcdview.scene = rendering.Open3DScene(self.window.renderer) self.pcdview.scene.set_background([1, 1, 1, 1]) # White background self.pcdview.scene.set_lighting( rendering.Open3DScene.LightingProfile.SOFT_SHADOWS, [0, -6, 0]) # Point cloud bounds, depends on the sensor range self.pcd_bounds = o3d.geometry.AxisAlignedBoundingBox([-3, -3, 0], [3, 3, 6]) self.camera_view() # Initially look from the camera em = self.window.theme.font_size # Options panel self.panel = gui.Vert(em, gui.Margins(em, em, em, em)) self.panel.preferred_width = int(360 * self.window.scaling) self.window.add_child(self.panel) toggles = gui.Horiz(em) self.panel.add_child(toggles) toggle_capture = gui.ToggleSwitch("Capture / Play") toggle_capture.is_on = False toggle_capture.set_on_clicked( callbacks['on_toggle_capture']) # callback toggles.add_child(toggle_capture) self.flag_normals = False self.toggle_normals = gui.ToggleSwitch("Colors / Normals") self.toggle_normals.is_on = False self.toggle_normals.set_on_clicked( callbacks['on_toggle_normals']) # callback toggles.add_child(self.toggle_normals) view_buttons = gui.Horiz(em) self.panel.add_child(view_buttons) view_buttons.add_stretch() # for centering camera_view = gui.Button("Camera view") camera_view.set_on_clicked(self.camera_view) # callback view_buttons.add_child(camera_view) birds_eye_view = gui.Button("Bird's eye view") birds_eye_view.set_on_clicked(self.birds_eye_view) # callback view_buttons.add_child(birds_eye_view) view_buttons.add_stretch() # for centering save_toggle = gui.Horiz(em) self.panel.add_child(save_toggle) save_toggle.add_child(gui.Label("Record / Save")) self.toggle_record = None if callbacks['on_toggle_record'] is not None: save_toggle.add_fixed(1.5 * em) self.toggle_record = gui.ToggleSwitch("Video") self.toggle_record.is_on = False self.toggle_record.set_on_clicked(callbacks['on_toggle_record']) save_toggle.add_child(self.toggle_record) save_buttons = gui.Horiz(em) self.panel.add_child(save_buttons) save_buttons.add_stretch() # for centering save_pcd = gui.Button("Save Point cloud") save_pcd.set_on_clicked(callbacks['on_save_pcd']) save_buttons.add_child(save_pcd) save_rgbd = gui.Button("Save RGBD frame") save_rgbd.set_on_clicked(callbacks['on_save_rgbd']) save_buttons.add_child(save_rgbd) save_buttons.add_stretch() # for centering self.video_size = (int(240 * self.window.scaling), int(320 * self.window.scaling), 3) self.show_color = gui.CollapsableVert("Color image") self.show_color.set_is_open(False) self.panel.add_child(self.show_color) self.color_video = gui.ImageWidget( o3d.geometry.Image(np.zeros(self.video_size, dtype=np.uint8))) self.show_color.add_child(self.color_video) self.show_depth = gui.CollapsableVert("Depth image") self.show_depth.set_is_open(False) self.panel.add_child(self.show_depth) self.depth_video = gui.ImageWidget( o3d.geometry.Image(np.zeros(self.video_size, dtype=np.uint8))) self.show_depth.add_child(self.depth_video) self.status_message = gui.Label("") self.panel.add_child(self.status_message) self.flag_exit = False self.flag_gui_init = False
def __init__(self): self.window = gui.Window("Test") # self.window = gui.Window("Test", 640, 480) # self.window = gui.Window("Test", 640, 480, x=50, y=100) w = self.window # for more concise code # Rather than specifying sizes in pixels, which may vary in size based # on the monitor, especially on macOS which has 220 dpi monitors, use # the em-size. This way sizings will be proportional to the font size, # which will create a more visually consistent size across platforms. em = w.theme.font_size # Widgets are laid out in layouts: gui.Horiz, gui.Vert, # gui.CollapsableVert, and gui.VGrid. By nesting the layouts we can # achieve complex designs. Usually we use a vertical layout as the # topmost widget, since widgets tend to be organized from top to bottom. # Within that, we usually have a series of horizontal layouts for each # row. layout = gui.Vert(0, gui.Margins(0.5 * em, 0.5 * em, 0.5 * em, 0.5 * em)) # Create the menu. The menu is global (because the macOS menu is global), # so only create it once. if gui.Application.instance.menubar is None: menubar = gui.Menu() test_menu = gui.Menu() test_menu.add_item("An option", ExampleWindow.MENU_CHECKABLE) test_menu.set_checked(ExampleWindow.MENU_CHECKABLE, True) test_menu.add_item("Unavailable feature", ExampleWindow.MENU_DISABLED) test_menu.set_enabled(ExampleWindow.MENU_DISABLED, False) test_menu.add_separator() test_menu.add_item("Quit", ExampleWindow.MENU_QUIT) # On macOS the first menu item is the application menu item and will # always be the name of the application (probably "Python"), # regardless of what you pass in here. The application menu is # typically where About..., Preferences..., and Quit go. menubar.add_menu("Test", test_menu) gui.Application.instance.menubar = menubar # Each window needs to know what to do with the menu items, so we need # to tell the window how to handle menu items. w.set_on_menu_item_activated(ExampleWindow.MENU_CHECKABLE, self._on_menu_checkable) w.set_on_menu_item_activated(ExampleWindow.MENU_QUIT, self._on_menu_quit) # Create a file-chooser widget. One part will be a text edit widget for # the filename and clicking on the button will let the user choose using # the file dialog. self._fileedit = gui.TextEdit() filedlgbutton = gui.Button("...") filedlgbutton.horizontal_padding_em = 0.5 filedlgbutton.vertical_padding_em = 0 filedlgbutton.set_on_clicked(self._on_filedlg_button) # (Create the horizontal widget for the row. This will make sure the # text editor takes up as much space as it can.) fileedit_layout = gui.Horiz() fileedit_layout.add_child(gui.Label("Model file")) fileedit_layout.add_child(self._fileedit) fileedit_layout.add_fixed(0.25 * em) fileedit_layout.add_child(filedlgbutton) # add to the top-level (vertical) layout layout.add_child(fileedit_layout) # Create a collapsable vertical widget, which takes up enough vertical # space for all its children when open, but only enough for text when # closed. This is useful for property pages, so the user can hide sets # of properties they rarely use. All layouts take a spacing parameter, # which is the spacinging between items in the widget, and a margins # parameter, which specifies the spacing of the left, top, right, # bottom margins. (This acts like the 'padding' property in CSS.) collapse = gui.CollapsableVert("Widgets", 0.33 * em, gui.Margins(em, 0, 0, 0)) self._label = gui.Label("Lorem ipsum dolor") self._label.text_color = gui.Color(1.0, 0.5, 0.0) collapse.add_child(self._label) # Create a checkbox. Checking or unchecking would usually be used to set # a binary property, but in this case it will show a simple message box, # which illustrates how to create simple dialogs. cb = gui.Checkbox("Enable some really cool effect") cb.set_on_checked(self._on_cb) # set the callback function collapse.add_child(cb) # Create a color editor. We will change the color of the orange label # above when the color changes. color = gui.ColorEdit() color.color_value = self._label.text_color color.set_on_value_changed(self._on_color) collapse.add_child(color) # This is a combobox, nothing fancy here, just set a simple function to # handle the user selecting an item. combo = gui.Combobox() combo.add_item("Show point labels") combo.add_item("Show point velocity") combo.add_item("Show bounding boxes") combo.set_on_selection_changed(self._on_combo) collapse.add_child(combo) # Add a simple image logo = gui.ImageLabel(basedir + "/icon-32.png") collapse.add_child(logo) # Add a list of items lv = gui.ListView() lv.set_items(["Ground", "Trees", "Buildings" "Cars", "People"]) lv.selected_index = lv.selected_index + 2 # initially is -1, so now 1 lv.set_on_selection_changed(self._on_list) collapse.add_child(lv) # Add a tree view tree = gui.TreeView() tree.add_item(tree.get_root_item(), "Camera") geo_id = tree.add_item(tree.get_root_item(), "Geometries") mesh_id = tree.add_item(geo_id, "Mesh") tree.add_item(mesh_id, "Triangles") tree.add_item(mesh_id, "Albedo texture") tree.add_item(mesh_id, "Normal map") points_id = tree.add_item(geo_id, "Points") tree.can_select_items_with_children = True tree.set_on_selection_changed(self._on_tree) # does not call on_selection_changed: user did not change selection tree.selected_item = points_id collapse.add_child(tree) # Add two number editors, one for integers and one for floating point # Number editor can clamp numbers to a range, although this is more # useful for integers than for floating point. intedit = gui.NumberEdit(gui.NumberEdit.INT) intedit.int_value = 0 intedit.set_limits(1, 19) # value coerced to 1 intedit.int_value = intedit.int_value + 2 # value should be 3 doubleedit = gui.NumberEdit(gui.NumberEdit.DOUBLE) numlayout = gui.Horiz() numlayout.add_child(gui.Label("int")) numlayout.add_child(intedit) numlayout.add_fixed( em) # manual spacing (could set it in Horiz() ctor) numlayout.add_child(gui.Label("double")) numlayout.add_child(doubleedit) collapse.add_child(numlayout) # Create a progress bar. It ranges from 0.0 to 1.0. self._progress = gui.ProgressBar() self._progress.value = 0.25 # 25% complete self._progress.value = self._progress.value + 0.08 # 0.25 + 0.08 = 33% prog_layout = gui.Horiz(em) prog_layout.add_child(gui.Label("Progress...")) prog_layout.add_child(self._progress) collapse.add_child(prog_layout) # Create a slider. It acts very similar to NumberEdit except that the # user moves a slider and cannot type the number. slider = gui.Slider(gui.Slider.INT) slider.set_limits(5, 13) slider.set_on_value_changed(self._on_slider) collapse.add_child(slider) # Create a text editor. The placeholder text (if not empty) will be # displayed when there is no text, as concise help, or visible tooltip. tedit = gui.TextEdit() tedit.placeholder_text = "Edit me some text here" # on_text_changed fires whenever the user changes the text (but not if # the text_value property is assigned to). tedit.set_on_text_changed(self._on_text_changed) # on_value_changed fires whenever the user signals that they are finished # editing the text, either by pressing return or by clicking outside of # the text editor, thus losing text focus. tedit.set_on_value_changed(self._on_value_changed) collapse.add_child(tedit) # Create a widget for showing/editing a 3D vector vedit = gui.VectorEdit() vedit.vector_value = [1, 2, 3] vedit.set_on_value_changed(self._on_vedit) collapse.add_child(vedit) # Create a VGrid layout. This layout specifies the number of columns # (two, in this case), and will place the first child in the first # column, the second in the second, the third in the first, the fourth # in the second, etc. # So: # 2 cols 3 cols 4 cols # | 1 | 2 | | 1 | 2 | 3 | | 1 | 2 | 3 | 4 | # | 3 | 4 | | 4 | 5 | 6 | | 5 | 6 | 7 | 8 | # | 5 | 6 | | 7 | 8 | 9 | | 9 | 10 | 11 | 12 | # | ... | | ... | | ... | vgrid = gui.VGrid(2) vgrid.add_child(gui.Label("Trees")) vgrid.add_child(gui.Label("12 items")) vgrid.add_child(gui.Label("People")) vgrid.add_child(gui.Label("2 (93% certainty)")) vgrid.add_child(gui.Label("Cars")) vgrid.add_child(gui.Label("5 (87% certainty)")) collapse.add_child(vgrid) collapse.add_child(vgrid) # Create a tab control. This is really a set of N layouts on top of each # other, but with only one selected. tabs = gui.TabControl() tab1 = gui.Vert() tab1.add_child(gui.Checkbox("Enable option 1")) tab1.add_child(gui.Checkbox("Enable option 2")) tab1.add_child(gui.Checkbox("Enable option 3")) tabs.add_tab("Options", tab1) tab2 = gui.Vert() tab2.add_child(gui.Label("No plugins detected")) tab2.add_stretch() tabs.add_tab("Plugins", tab2) collapse.add_child(tabs) # Quit button. (Typically this is a menu item) button_layout = gui.Horiz() ok_button = gui.Button("Ok") ok_button.set_on_clicked(self._on_ok) button_layout.add_stretch() button_layout.add_child(ok_button) layout.add_child(collapse) layout.add_child(button_layout) # We're done, set the window's layout w.add_child(layout)
def __init__(self, width, height): self.settings = Settings() resource_path = gui.Application.instance.resource_path self.settings.new_ibl_name = resource_path + "/" + AppWindow.DEFAULT_IBL self.window = gui.Application.instance.create_window( "Open3D", width, height) w = self.window # to make the code more concise # 3D widget self._scene = gui.SceneWidget() self._scene.scene = rendering.Open3DScene(w.renderer) self._scene.set_on_sun_direction_changed(self._on_sun_dir) # ---- Settings panel ---- # Rather than specifying sizes in pixels, which may vary in size based # on the monitor, especially on macOS which has 220 dpi monitors, use # the em-size. This way sizings will be proportional to the font size, # which will create a more visually consistent size across platforms. em = w.theme.font_size separation_height = int(round(0.5 * em)) # Widgets are laid out in layouts: gui.Horiz, gui.Vert, # gui.CollapsableVert, and gui.VGrid. By nesting the layouts we can # achieve complex designs. Usually we use a vertical layout as the # topmost widget, since widgets tend to be organized from top to bottom. # Within that, we usually have a series of horizontal layouts for each # row. All layouts take a spacing parameter, which is the spacing # between items in the widget, and a margins parameter, which specifies # the spacing of the left, top, right, bottom margins. (This acts like # the 'padding' property in CSS.) self._settings_panel = gui.Vert( 0, gui.Margins(0.25 * em, 0.25 * em, 0.25 * em, 0.25 * em)) # Create a collapsable vertical widget, which takes up enough vertical # space for all its children when open, but only enough for text when # closed. This is useful for property pages, so the user can hide sets # of properties they rarely use. view_ctrls = gui.CollapsableVert("View controls", 0.25 * em, gui.Margins(em, 0, 0, 0)) self._arcball_button = gui.Button("Arcball") self._arcball_button.horizontal_padding_em = 0.5 self._arcball_button.vertical_padding_em = 0 self._arcball_button.set_on_clicked(self._set_mouse_mode_rotate) self._fly_button = gui.Button("Fly") self._fly_button.horizontal_padding_em = 0.5 self._fly_button.vertical_padding_em = 0 self._fly_button.set_on_clicked(self._set_mouse_mode_fly) self._model_button = gui.Button("Model") self._model_button.horizontal_padding_em = 0.5 self._model_button.vertical_padding_em = 0 self._model_button.set_on_clicked(self._set_mouse_mode_model) self._sun_button = gui.Button("Sun") self._sun_button.horizontal_padding_em = 0.5 self._sun_button.vertical_padding_em = 0 self._sun_button.set_on_clicked(self._set_mouse_mode_sun) self._ibl_button = gui.Button("Environment") self._ibl_button.horizontal_padding_em = 0.5 self._ibl_button.vertical_padding_em = 0 self._ibl_button.set_on_clicked(self._set_mouse_mode_ibl) view_ctrls.add_child(gui.Label("Mouse controls")) # We want two rows of buttons, so make two horizontal layouts. We also # want the buttons centered, which we can do be putting a stretch item # as the first and last item. Stretch items take up as much space as # possible, and since there are two, they will each take half the extra # space, thus centering the buttons. h = gui.Horiz(0.25 * em) # row 1 h.add_stretch() h.add_child(self._arcball_button) h.add_child(self._fly_button) h.add_child(self._model_button) h.add_stretch() view_ctrls.add_child(h) h = gui.Horiz(0.25 * em) # row 2 h.add_stretch() h.add_child(self._sun_button) h.add_child(self._ibl_button) h.add_stretch() view_ctrls.add_child(h) self._show_skybox = gui.Checkbox("Show skymap") self._show_skybox.set_on_checked(self._on_show_skybox) view_ctrls.add_fixed(separation_height) view_ctrls.add_child(self._show_skybox) self._bg_color = gui.ColorEdit() self._bg_color.set_on_value_changed(self._on_bg_color) grid = gui.VGrid(2, 0.25 * em) grid.add_child(gui.Label("BG Color")) grid.add_child(self._bg_color) view_ctrls.add_child(grid) self._show_axes = gui.Checkbox("Show axes") self._show_axes.set_on_checked(self._on_show_axes) view_ctrls.add_fixed(separation_height) view_ctrls.add_child(self._show_axes) self._profiles = gui.Combobox() for name in sorted(Settings.LIGHTING_PROFILES.keys()): self._profiles.add_item(name) self._profiles.add_item(Settings.CUSTOM_PROFILE_NAME) self._profiles.set_on_selection_changed(self._on_lighting_profile) view_ctrls.add_fixed(separation_height) view_ctrls.add_child(gui.Label("Lighting profiles")) view_ctrls.add_child(self._profiles) self._settings_panel.add_fixed(separation_height) self._settings_panel.add_child(view_ctrls) advanced = gui.CollapsableVert("Advanced lighting", 0, gui.Margins(em, 0, 0, 0)) advanced.set_is_open(False) self._use_ibl = gui.Checkbox("HDR map") self._use_ibl.set_on_checked(self._on_use_ibl) self._use_sun = gui.Checkbox("Sun") self._use_sun.set_on_checked(self._on_use_sun) advanced.add_child(gui.Label("Light sources")) h = gui.Horiz(em) h.add_child(self._use_ibl) h.add_child(self._use_sun) advanced.add_child(h) self._ibl_map = gui.Combobox() for ibl in glob.glob(gui.Application.instance.resource_path + "/*_ibl.ktx"): self._ibl_map.add_item(os.path.basename(ibl[:-8])) self._ibl_map.selected_text = AppWindow.DEFAULT_IBL self._ibl_map.set_on_selection_changed(self._on_new_ibl) self._ibl_intensity = gui.Slider(gui.Slider.INT) self._ibl_intensity.set_limits(0, 200000) self._ibl_intensity.set_on_value_changed(self._on_ibl_intensity) grid = gui.VGrid(2, 0.25 * em) grid.add_child(gui.Label("HDR map")) grid.add_child(self._ibl_map) grid.add_child(gui.Label("Intensity")) grid.add_child(self._ibl_intensity) advanced.add_fixed(separation_height) advanced.add_child(gui.Label("Environment")) advanced.add_child(grid) self._sun_intensity = gui.Slider(gui.Slider.INT) self._sun_intensity.set_limits(0, 200000) self._sun_intensity.set_on_value_changed(self._on_sun_intensity) self._sun_dir = gui.VectorEdit() self._sun_dir.set_on_value_changed(self._on_sun_dir) self._sun_color = gui.ColorEdit() self._sun_color.set_on_value_changed(self._on_sun_color) grid = gui.VGrid(2, 0.25 * em) grid.add_child(gui.Label("Intensity")) grid.add_child(self._sun_intensity) grid.add_child(gui.Label("Direction")) grid.add_child(self._sun_dir) grid.add_child(gui.Label("Color")) grid.add_child(self._sun_color) advanced.add_fixed(separation_height) advanced.add_child(gui.Label("Sun (Directional light)")) advanced.add_child(grid) self._settings_panel.add_fixed(separation_height) self._settings_panel.add_child(advanced) material_settings = gui.CollapsableVert("Material settings", 0, gui.Margins(em, 0, 0, 0)) self._shader = gui.Combobox() self._shader.add_item(AppWindow.MATERIAL_NAMES[0]) self._shader.add_item(AppWindow.MATERIAL_NAMES[1]) self._shader.add_item(AppWindow.MATERIAL_NAMES[2]) self._shader.add_item(AppWindow.MATERIAL_NAMES[3]) self._shader.set_on_selection_changed(self._on_shader) self._material_prefab = gui.Combobox() for prefab_name in sorted(Settings.PREFAB.keys()): self._material_prefab.add_item(prefab_name) self._material_prefab.selected_text = Settings.DEFAULT_MATERIAL_NAME self._material_prefab.set_on_selection_changed( self._on_material_prefab) self._material_color = gui.ColorEdit() self._material_color.set_on_value_changed(self._on_material_color) self._point_size = gui.Slider(gui.Slider.INT) self._point_size.set_limits(1, 10) self._point_size.set_on_value_changed(self._on_point_size) grid = gui.VGrid(2, 0.25 * em) grid.add_child(gui.Label("Type")) grid.add_child(self._shader) grid.add_child(gui.Label("Material")) grid.add_child(self._material_prefab) grid.add_child(gui.Label("Color")) grid.add_child(self._material_color) grid.add_child(gui.Label("Point size")) grid.add_child(self._point_size) material_settings.add_child(grid) self._settings_panel.add_fixed(separation_height) self._settings_panel.add_child(material_settings) # ---- # Normally our user interface can be children of all one layout (usually # a vertical layout), which is then the only child of the window. In our # case we want the scene to take up all the space and the settings panel # to go above it. We can do this custom layout by providing an on_layout # callback. The on_layout callback should set the frame # (position + size) of every child correctly. After the callback is # done the window will layout the grandchildren. w.set_on_layout(self._on_layout) w.add_child(self._scene) w.add_child(self._settings_panel) # ---- Menu ---- # The menu is global (because the macOS menu is global), so only create # it once, no matter how many windows are created if gui.Application.instance.menubar is None: if isMacOS: app_menu = gui.Menu() app_menu.add_item("About", AppWindow.MENU_ABOUT) app_menu.add_separator() app_menu.add_item("Quit", AppWindow.MENU_QUIT) file_menu = gui.Menu() file_menu.add_item("Open...", AppWindow.MENU_OPEN) file_menu.add_item("Export Current Image...", AppWindow.MENU_EXPORT) if not isMacOS: file_menu.add_separator() file_menu.add_item("Quit", AppWindow.MENU_QUIT) settings_menu = gui.Menu() settings_menu.add_item("Lighting & Materials", AppWindow.MENU_SHOW_SETTINGS) settings_menu.set_checked(AppWindow.MENU_SHOW_SETTINGS, True) help_menu = gui.Menu() help_menu.add_item("About", AppWindow.MENU_ABOUT) menu = gui.Menu() if isMacOS: # macOS will name the first menu item for the running application # (in our case, probably "Python"), regardless of what we call # it. This is the application menu, and it is where the # About..., Preferences..., and Quit menu items typically go. menu.add_menu("Example", app_menu) menu.add_menu("File", file_menu) menu.add_menu("Settings", settings_menu) # Don't include help menu unless it has something more than # About... else: menu.add_menu("File", file_menu) menu.add_menu("Settings", settings_menu) menu.add_menu("Help", help_menu) gui.Application.instance.menubar = menu # The menubar is global, but we need to connect the menu items to the # window, so that the window can call the appropriate function when the # menu item is activated. w.set_on_menu_item_activated(AppWindow.MENU_OPEN, self._on_menu_open) w.set_on_menu_item_activated(AppWindow.MENU_EXPORT, self._on_menu_export) w.set_on_menu_item_activated(AppWindow.MENU_QUIT, self._on_menu_quit) w.set_on_menu_item_activated(AppWindow.MENU_SHOW_SETTINGS, self._on_menu_toggle_settings_panel) w.set_on_menu_item_activated(AppWindow.MENU_ABOUT, self._on_menu_about) # ---- self._apply_settings()