Esempio n. 1
0
    def __init__(self):
        self.mouse_model = gui.SceneWidget.Controls.ROTATE_CAMERA
        self.bg_color = gui.Color(1, 1, 1)
        self.show_skybox = False
        self.show_axes = False
        self.use_ibl = True
        self.use_sun = True
        self.new_ibl_name = None  # clear to None after loading
        self.ibl_intensity = 45000
        self.sun_intensity = 45000
        self.sun_dir = [0.577, -0.577, -0.577]
        self.sun_color = gui.Color(1, 1, 1)

        self.apply_material = True  # clear to False after processing
        self._materials = {
            Settings.LIT: rendering.Material(),
            Settings.UNLIT: rendering.Material(),
            Settings.NORMALS: rendering.Material(),
            Settings.DEPTH: rendering.Material()
        }
        self._materials[Settings.LIT].base_color = [0.9, 0.9, 0.9, 1.0]
        self._materials[Settings.LIT].shader = Settings.LIT
        self._materials[Settings.UNLIT].base_color = [0.9, 0.9, 0.9, 1.0]
        self._materials[Settings.UNLIT].shader = Settings.UNLIT
        self._materials[Settings.NORMALS].shader = Settings.NORMALS
        self._materials[Settings.DEPTH].shader = Settings.DEPTH

        # Conveniently, assigning from self._materials[...] assigns a reference,
        # not a copy, so if we change the property of a material, then switch
        # to another one, then come back, the old setting will still be there.
        self.material = self._materials[Settings.LIT]
Esempio n. 2
0
        def _visualize(self, title, width, height):
            gui.Application.instance.initialize()
            self._init_user_interface(title, width, height)

            # override just to set background color to back :)
            bgcolor = gui.ColorEdit()
            bgcolor.color_value = gui.Color(0, 0, 0)
            self._on_bgcolor_changed(bgcolor.color_value)

            self._3d.scene.downsample_threshold = 400000

            # Turn all the objects off except the first one
            for name, node in self._name2treenode.items():
                node.checkbox.checked = False
                self._3d.scene.show_geometry(name, False)
            for name in [self._objects.data_names[0]]:
                self._name2treenode[name].checkbox.checked = True
                self._3d.scene.show_geometry(name, True)

            def on_done_ui():
                # Add bounding boxes here: bounding boxes belonging to the dataset
                # will not be loaded until now.
                self._update_bounding_boxes()

                self._update_datasource_combobox()
                self._update_shaders_combobox()

                # Display "colors" by default if available, "points" if not
                available_attrs = self._get_available_attrs()
                self._set_shader(self.SOLID_NAME, force_update=True)
                if "colors" in available_attrs:
                    self._datasource_combobox.selected_text = "colors"
                elif "points" in available_attrs:
                    self._datasource_combobox.selected_text = "points"

                self._dont_update_geometry = True
                self._on_datasource_changed(
                    self._datasource_combobox.selected_text, self._datasource_combobox.selected_index
                )
                self._update_geometry_colors()
                self._dont_update_geometry = False
                # _datasource_combobox was empty, now isn't, re-layout.
                self.window.set_needs_layout()

                self._update_geometry()
                self.setup_camera()

            self._load_geometries(self._objects.data_names, on_done_ui)
            gui.Application.instance.run()
Esempio n. 3
0
    def _apply_settings(self):
        bg_color = [
            self.settings.bg_color.red, self.settings.bg_color.green,
            self.settings.bg_color.blue, self.settings.bg_color.alpha
        ]
        self._scene.scene.set_background(bg_color)
        self._scene.scene.show_skybox(self.settings.show_skybox)
        self._scene.scene.show_axes(self.settings.show_axes)
        if self.settings.new_ibl_name is not None:
            self._scene.scene.scene.set_indirect_light(
                self.settings.new_ibl_name)
            # Clear new_ibl_name, so we don't keep reloading this image every
            # time the settings are applied.
            self.settings.new_ibl_name = None
        self._scene.scene.scene.enable_indirect_light(self.settings.use_ibl)
        self._scene.scene.scene.set_indirect_light_intensity(
            self.settings.ibl_intensity)
        sun_color = [
            self.settings.sun_color.red, self.settings.sun_color.green,
            self.settings.sun_color.blue
        ]
        self._scene.scene.scene.set_sun_light(self.settings.sun_dir, sun_color,
                                              self.settings.sun_intensity)
        self._scene.scene.scene.enable_sun_light(self.settings.use_sun)

        if self.settings.apply_material:
            self._scene.scene.update_material(self.settings.material)
            self.settings.apply_material = False

        self._bg_color.color_value = self.settings.bg_color
        self._show_skybox.checked = self.settings.show_skybox
        self._show_axes.checked = self.settings.show_axes
        self._use_ibl.checked = self.settings.use_ibl
        self._use_sun.checked = self.settings.use_sun
        self._ibl_intensity.int_value = self.settings.ibl_intensity
        self._sun_intensity.int_value = self.settings.sun_intensity
        self._sun_dir.vector_value = self.settings.sun_dir
        self._sun_color.color_value = self.settings.sun_color
        self._material_prefab.enabled = (
            self.settings.material.shader == Settings.LIT)
        c = gui.Color(self.settings.material.base_color[0],
                      self.settings.material.base_color[1],
                      self.settings.material.base_color[2],
                      self.settings.material.base_color[3])
        self._material_color.color_value = c
        self._point_size.double_value = self.settings.material.point_size
Esempio n. 4
0
def low_level():
    app = gui.Application.instance
    app.initialize()

    points = make_point_cloud(100, (0, 0, 0), 1.0)

    w = app.create_window("Open3D - 3D Text", 1024, 768)
    widget3d = gui.SceneWidget()
    widget3d.scene = rendering.Open3DScene(w.renderer)
    mat = rendering.MaterialRecord()
    mat.shader = "defaultUnlit"
    mat.point_size = 5 * w.scaling
    widget3d.scene.add_geometry("Points", points, mat)
    for idx in range(0, len(points.points)):
        l = widget3d.add_3d_label(points.points[idx], "{}".format(idx))
        l.color = gui.Color(points.colors[idx][0], points.colors[idx][1],
                            points.colors[idx][2])
        l.scale = np.random.uniform(0.5, 3.0)
    bbox = widget3d.scene.bounding_box
    widget3d.setup_camera(60.0, bbox, bbox.get_center())
    w.add_child(widget3d)

    app.run()
Esempio n. 5
0
    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)
Esempio n. 6
0
    def __init__(self, width, height):
        self.window = gui.Window("Open3D", width, height)
        w = self.window  # to make the code more concise

        # 3D widget
        self._scene = gui.SceneWidget(w)

        # ---- 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._fly_button = gui.Button("Fly")
        self._model_button = gui.Button("Model")
        self._sun_button = gui.Button("Sun")
        self._ibl_button = gui.Button("Environment")
        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_skymap = gui.Checkbox("Show skymap")
        view_ctrls.add_fixed(separation_height)
        view_ctrls.add_child(self._show_skymap)

        bg_color = gui.ColorEdit()
        bg_color.color_value = gui.Color(1, 1, 1, 1)
        self._on_bg_color(bg_color.color_value)
        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(bg_color)
        view_ctrls.add_child(grid)

        self._show_axes = gui.Checkbox("Show axes")
        view_ctrls.add_fixed(separation_height)
        view_ctrls.add_child(self._show_axes)

        profiles = gui.Combobox()
        profiles.add_item("Bright day with sun at +Y [default]")
        profiles.add_item("Bright day with sun at -Y")
        profiles.add_item("Bright day with sun at +Z")
        profiles.add_item("Less bright day with sun at +Y")
        profiles.add_item("Less bright day with sun at -Y")
        profiles.add_item("Less bright day with sun at +Z")
        profiles.add_item("Cloudy day (no direct sun)")
        profiles.add_item("Custom")
        view_ctrls.add_fixed(separation_height)
        view_ctrls.add_child(gui.Label("Lighting profiles"))
        view_ctrls.add_child(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))

        self._show_skybox = gui.Checkbox("HDR map")
        self._show_skybox.checked = True
        self._show_sunlight = gui.Checkbox("Sun")
        self._show_sunlight.checked = True
        advanced.add_child(gui.Label("Light sources"))
        h = gui.Horiz(em)
        h.add_child(self._show_skybox)
        h.add_child(self._show_sunlight)
        advanced.add_child(h)

        self._ibl_map = gui.Combobox()
        self._ibl_map.add_item("default")
        self._ibl_intensity = gui.Slider(gui.Slider.INT)
        self._ibl_intensity.set_limits(0, 200000)
        self._ibl_intensity.int_value = 45000
        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.int_value = 45000
        self._sun_dir = gui.VectorEdit()
        self._sun_dir.vector_value = [0.577, -0.577, -0.577]
        self._sun_color = gui.ColorEdit()
        self._sun_color.color_value = gui.Color(1, 1, 1, 1)
        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("Lit")
        self._shader.add_item("Unlit")
        self._shader.add_item("Normals")
        self._shader.add_item("Depth")
        self._material_profile = gui.Combobox()
        self._material_profile.add_item("Clay")
        self._material_profile.add_item("Glazed ceramic")
        self._material_profile.add_item("Metal (rougher)")
        self._material_profile.add_item("Metal (smoother)")
        self._material_profile.add_item("Plastic")
        self._material_profile.add_item("Polished ceramic [default]")
        self._material_color = gui.ColorEdit()
        self._material_color.color_value = gui.Color(1, 1, 1, 1)
        self._point_size = gui.Slider(gui.Slider.INT)
        self._point_size.set_limits(1, 10)
        self._point_size.int_value = 3

        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_profile)
        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)
            file_menu.set_enabled(AppWindow.MENU_EXPORT, False)
            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_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)