コード例 #1
0
class BaseSelectOperationUI(wx.BoxSizer, BaseToolUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        BaseToolUI.__init__(self, canvas)
        self._active_operation: Optional[OperationUIType] = None

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._operation_choice = SimpleChoiceAny(self.canvas)
        self._reload_operation = wx.BitmapButton(
            self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16)
        )
        self._reload_operation.SetToolTip("Reload selected operation")

        horizontal_sizer.Add(self._operation_choice)
        horizontal_sizer.Add(self._reload_operation)

        self.Add(horizontal_sizer)

        self._operation_choice.SetItems(
            {key: value.name for key, value in self._operations.items()}
        )
        self._operation_choice.Bind(wx.EVT_CHOICE, self._on_operation_change)

        self._reload_operation.Bind(wx.EVT_BUTTON, self._reload_operation_loader)

        self._operation_sizer = wx.BoxSizer(wx.VERTICAL)
        self.Add(self._operation_sizer, 1, wx.EXPAND)

        # self._operation_change()

    @property
    def name(self) -> str:
        raise NotImplementedError

    def bind_events(self):
        pass

    @property
    def _operations(self) -> OperationStorageType:
        raise NotImplementedError

    @property
    def operation(self) -> str:
        return self._operation_choice.GetCurrentObject()

    def _on_operation_change(self, evt):
        self._operation_change()
        evt.Skip()

    def _unload_active_operation(self):
        if self._active_operation is not None:
            self._active_operation.unload()
            if isinstance(self._active_operation, wx.Window):
                self._active_operation.Destroy()
            elif isinstance(self._active_operation, wx.Sizer):
                self._operation_sizer.GetItem(self._active_operation).DeleteWindows()
            self._active_operation = None

    def _operation_change(self):
        operation_path = self._operation_choice.GetCurrentObject()
        if operation_path:
            operation = self._operations[operation_path]
            self.disable()
            self._active_operation = operation(
                self.canvas, self.canvas, self.canvas.world
            )
            self._operation_sizer.Add(
                self._active_operation, *self._active_operation.wx_add_options
            )
            self.Layout()

    def _reload_operation_loader(self, evt):
        operation_path = self._operation_choice.GetCurrentObject()
        if operation_path:
            operation_loader, success = self._operations[operation_path].reload()
            if success:
                self._operations[operation_path] = operation_loader
            else:
                log.warning(f"Couldn't successfully reload {operation_path}")
                self.reload_operations()
            self._operation_change()

    def enable(self):
        self._operation_change()
        self.canvas.draw_structure = False
        self.canvas.draw_selection = True
        self.canvas.selection_editable = False

    def disable(self):
        super().disable()
        self._unload_active_operation()

    def reload_operations(self):
        self._operation_choice.SetItems(
            {key: value.name for key, value in self._operations.items()}
        )

        if not self.operation:
            return

        items = self._operation_choice.GetItems()
        operation_loader = self._operations[self.operation]

        if operation_loader.name in items and operation_loader.name != items[0]:
            self._operation_choice.SetValue(operation_loader.name)
        else:
            log.warning(
                f"Couldn't successfully reload currently selected operation: {self.operation}"
            )
            self._operation_choice.SetValue(self._operation_choice.GetItems()[0])

        self._operation_change()
コード例 #2
0
class BaseOperationChoiceToolUI(wx.BoxSizer, BaseToolUI):
    OperationGroupName = None
    _active_operation: Optional[OperationUIType]

    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        BaseToolUI.__init__(self, canvas)

        self._active_operation: Optional[OperationUIType] = None
        self._last_active_operation_id: Optional[str] = None

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._operation_choice = SimpleChoiceAny(self.canvas)
        self._reload_operation = wx.BitmapButton(
            self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16)
        )
        self._reload_operation.SetToolTip("Reload Operations")

        horizontal_sizer.Add(self._operation_choice)
        horizontal_sizer.Add(self._reload_operation)

        self.Add(horizontal_sizer)

        assert isinstance(
            self.OperationGroupName, str
        ), "OperationGroupName has not been set or is not a string."

        self._operations = UIOperationManager(self.OperationGroupName)

        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )
        self._operation_choice.Bind(wx.EVT_CHOICE, self._on_operation_change)

        self._reload_operation.Bind(wx.EVT_BUTTON, self._on_reload_operations)

        self._operation_sizer = wx.BoxSizer(wx.VERTICAL)
        self.Add(self._operation_sizer, 1, wx.EXPAND)

    @property
    def name(self) -> str:
        """The name of the group of operations."""
        raise NotImplementedError

    @property
    def active_operation_id(self) -> str:
        """The identifier of the operation selected by the choice input.
        Note if in the process of changing this may be different to self._active_operation."""
        return self._operation_choice.GetCurrentObject()

    def _on_operation_change(self, evt):
        """Run when the operation selection changes."""
        if (
            self.active_operation_id
            and self._last_active_operation_id != self.active_operation_id
        ):
            self._setup_operation()
            self.canvas.reset_bound_events()
        evt.Skip()

    def _setup_operation(self):
        """Remove the old operation and create the UI for the new operation."""
        operation_path = self.active_operation_id
        if operation_path:
            # only reload the operation if the
            operation = self._operations[operation_path]
            if self._active_operation is not None:
                self._active_operation.disable()
            self._operation_sizer.Clear(delete_windows=True)
            try:
                self._active_operation = operation(
                    self.canvas, self.canvas, self.canvas.world
                )
                self._operation_sizer.Add(
                    self._active_operation, *self._active_operation.wx_add_options
                )
                self._active_operation.enable()
            except Exception as e:
                # If something went wrong clear the created UI
                self._active_operation = None
                self._operation_sizer.Clear(delete_windows=True)
                for window in self.canvas.GetChildren():
                    window: wx.Window
                    # remove orphaned windows.
                    # If the Sizer.Add method was not run it will not be in self._operation_sizer
                    if window.GetContainingSizer() is None:
                        window.Destroy()
                log.error("Error loading Operation UI.", exc_info=True)
                dialog = TracebackDialog(
                    self.canvas,
                    "Error loading Operation UI.",
                    str(e),
                    traceback.format_exc(),
                )
                dialog.ShowModal()
                dialog.Destroy()
            finally:
                self._last_active_operation_id = operation.identifier
                self.Layout()

    def bind_events(self):
        if self._active_operation is not None:
            self._active_operation.bind_events()

    def enable(self):
        if self._active_operation is None:
            self._setup_operation()
        else:
            self._active_operation.enable()

    def disable(self):
        if self._active_operation is not None:
            self._active_operation.disable()

    def _on_reload_operations(self, evt):
        """Run when the button is pressed to reload the operations."""
        self.reload_operations()

    def reload_operations(self):
        """Reload all operations and repopulate the UI."""
        # store the id of the old operation
        operation_id = self.active_operation_id

        # reload the operations
        self._operations.reload()

        # repopulate the selection
        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )

        if operation_id:
            identifiers = self._operation_choice.values

            if identifiers:
                if operation_id in identifiers:
                    self._operation_choice.SetSelection(identifiers.index(operation_id))
                else:
                    log.info(f"Operation {operation_id} was not found.")
                    self._operation_choice.SetSelection(0)
            else:
                log.error("No operations found. Something has gone wrong.")

            self._setup_operation()
            self.canvas.reset_bound_events()
コード例 #3
0
class SetBiome(SimpleOperationPanel):
    def __init__(
        self,
        parent: wx.Window,
        canvas: "EditCanvas",
        world: "BaseLevel",
        options_path: str,
    ):
        SimpleOperationPanel.__init__(self, parent, canvas, world,
                                      options_path)
        self.Freeze()
        options = self._load_options({})

        self._mode = SimpleChoiceAny(self, sort=False)
        self._mode.SetItems({mode: lang[mode] for mode in MODES.keys()})
        self._sizer.Add(self._mode, 0, Border, 5)
        self._mode.Bind(wx.EVT_CHOICE, self._on_mode_change)

        self._mode_description = wx.TextCtrl(self,
                                             style=wx.TE_MULTILINE
                                             | wx.TE_READONLY | wx.TE_BESTWRAP)
        self._sizer.Add(self._mode_description, 0, Border, 5)

        self._mode_description.SetLabel(MODES[self._mode.GetCurrentObject()])
        self._mode_description.Fit()

        self._biome_choice = BiomeDefine(
            self,
            world.level_wrapper.translation_manager,
            wx.VERTICAL,
            *(options.get("original_block_options", [])
              or [world.level_wrapper.platform]),
            show_pick_biome=True,
        )
        self._biome_choice.Bind(EVT_PICK, self._on_pick_biome_button)
        self._sizer.Add(self._biome_choice, 1, Border, 5)

        self._add_run_button()

        self.Thaw()

    def _on_mode_change(self, evt):
        self._mode_description.SetLabel(MODES[self._mode.GetCurrentObject()])
        self._mode_description.Fit()
        self.Layout()
        evt.Skip()

    def _on_pick_biome_button(self, evt):
        """Set up listening for the biome click"""
        self._show_pointer = True

    def _on_box_click(self):
        if self._show_pointer:
            self._show_pointer = False
            x, y, z = self._pointer.pointer_base

            # TODO: replace with "get_biome(x, y, z)" if it'll be created
            cx, cz = block_coords_to_chunk_coords(
                x, z, sub_chunk_size=self.world.sub_chunk_size)
            offset_x, offset_z = x - 16 * cx, z - 16 * cz
            chunk = self.world.get_chunk(cx, cz, self.canvas.dimension)

            if chunk.biomes.dimension == BiomesShape.Shape3D:
                biome = chunk.biomes[offset_x // 4, y // 4, offset_z // 4]
            elif chunk.biomes.dimension == BiomesShape.Shape2D:
                biome = chunk.biomes[offset_x, offset_z]
            else:
                return

            self._biome_choice.universal_biome = chunk.biome_palette[biome]

    def _operation(self, world: "BaseLevel", dimension: "Dimension",
                   selection: "SelectionGroup") -> "OperationReturnType":
        mode = self._mode.GetCurrentObject()

        iter_count = len(
            list(world.get_chunk_slice_box(dimension, selection, False)))
        for count, (chunk, slices, _) in enumerate(
                world.get_chunk_slice_box(dimension, selection, False)):
            new_biome = chunk.biome_palette.get_add_biome(
                self._biome_choice.universal_biome)

            if mode == BoxMode:
                if chunk.biomes.dimension == BiomesShape.Shape3D:
                    slices = (
                        slice(slices[0].start // 4,
                              math.ceil(slices[0].stop / 4)),
                        slice(slices[1].start // 4,
                              math.ceil(slices[1].stop / 4)),
                        slice(slices[2].start // 4,
                              math.ceil(slices[2].stop / 4)),
                    )
                elif chunk.biomes.dimension == BiomesShape.Shape2D:
                    slices = (slices[0], slices[2])
                else:
                    continue
            elif mode == ColumnMode:
                if chunk.biomes.dimension == BiomesShape.Shape3D:
                    slices = (
                        slice(slices[0].start // 4,
                              math.ceil(slices[0].stop / 4)),
                        slice(None, None, None),
                        slice(slices[2].start // 4,
                              math.ceil(slices[2].stop / 4)),
                    )
                elif chunk.biomes.dimension == BiomesShape.Shape2D:
                    slices = (slices[0], slices[2])
                else:
                    continue
            else:
                raise ValueError(
                    f"mode {mode} is not a valid mode for the Set Biome operation."
                )

            chunk.biomes[slices] = new_biome

            chunk.changed = True
            yield (count + 1) / iter_count
コード例 #4
0
class FilePanel(wx.BoxSizer, EditCanvasContainer):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        EditCanvasContainer.__init__(self, canvas)

        level = self.canvas.world
        self._version_text = wx.StaticText(
            canvas,
            label=
            f"{level.level_wrapper.platform}, {level.level_wrapper.version}",
        )
        self.Add(self._version_text, 0)
        self.AddStretchSpacer(1)
        self._projection_button = wx.Button(canvas, label="3D")
        self._projection_button.Bind(wx.EVT_BUTTON, self._on_projection_button)
        self.Add(self._projection_button, 0,
                 wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)
        self._location_button = wx.Button(
            canvas,
            label=", ".join([f"{s:.2f}" for s in self.canvas.camera.location]))
        self._location_button.Bind(wx.EVT_BUTTON,
                                   lambda evt: self.canvas.goto())

        self.Add(self._location_button, 0,
                 wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

        self._dim_options = SimpleChoiceAny(canvas)
        self._dim_options.SetItems(level.level_wrapper.dimensions)
        if "overworld" in level.level_wrapper.dimensions:
            self._dim_options.SetValue("overworld")
        else:
            self._dim_options.SetValue(level.level_wrapper.dimensions[0])
        self._dim_options.Bind(wx.EVT_CHOICE, self._on_dimension_change)

        self.Add(self._dim_options, 0,
                 wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

        def create_button(text, operation):
            button = wx.Button(canvas, label=text)
            button.Bind(wx.EVT_BUTTON, operation)
            self.Add(button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
            return button

        self._undo_button: Optional[wx.Button] = create_button(
            "0", lambda evt: self.canvas.undo())
        self._undo_button.SetBitmap(
            image.icon.tablericons.arrow_back_up.bitmap(20, 20))

        self._redo_button: Optional[wx.Button] = create_button(
            "0", lambda evt: self.canvas.redo())
        self._redo_button.SetBitmap(
            image.icon.tablericons.arrow_forward_up.bitmap(20, 20))

        self._save_button: Optional[wx.Button] = create_button(
            "0", lambda evt: self.canvas.save())
        self._save_button.SetBitmap(
            image.icon.tablericons.device_floppy.bitmap(20, 20))

        close_button = wx.BitmapButton(
            canvas, bitmap=image.icon.tablericons.square_x.bitmap(20, 20))
        close_button.Bind(wx.EVT_BUTTON, lambda evt: self.canvas.close())
        self.Add(close_button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)

        self._update_buttons()

        self.Layout()

    def bind_events(self):
        self.canvas.Bind(EVT_CAMERA_MOVED, self._on_camera_move)
        self.canvas.Bind(EVT_UNDO, self._on_update_buttons)
        self.canvas.Bind(EVT_REDO, self._on_update_buttons)
        self.canvas.Bind(EVT_SAVE, self._on_update_buttons)
        self.canvas.Bind(EVT_CREATE_UNDO, self._on_update_buttons)
        self.canvas.Bind(EVT_PROJECTION_CHANGED, self._on_projection_change)
        self.canvas.Bind(EVT_DIMENSION_CHANGE, self._change_dimension)

    def _on_update_buttons(self, evt):
        self._update_buttons()
        evt.Skip()

    def _update_buttons(self):
        self._undo_button.SetLabel(
            f"{self.canvas.world.history_manager.undo_count}")
        self._redo_button.SetLabel(
            f"{self.canvas.world.history_manager.redo_count}")
        self._save_button.SetLabel(
            f"{self.canvas.world.history_manager.unsaved_changes}")

    def _on_dimension_change(self, evt):
        """Run when the dimension selection is changed by the user."""
        dimension = self._dim_options.GetCurrentObject()
        if dimension is not None:
            self.canvas.dimension = dimension
        evt.Skip()

    def _on_projection_change(self, evt):
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self._projection_button.SetLabel("3D")
        elif self.canvas.camera.projection_mode == Projection.TOP_DOWN:
            self._projection_button.SetLabel("2D")
        evt.Skip()

    def _on_projection_button(self, evt):
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.camera.projection_mode = Projection.TOP_DOWN
        else:
            self.canvas.camera.projection_mode = Projection.PERSPECTIVE
        evt.Skip()

    def _change_dimension(self, evt: DimensionChangeEvent):
        """Run when the dimension attribute in the canvas is changed.
        This is run when the user changes the attribute and when it is changed manually in code."""
        dimension = evt.dimension
        index = self._dim_options.FindString(dimension)
        if not (index == wx.NOT_FOUND
                or index == self._dim_options.GetSelection()):
            self._dim_options.SetSelection(index)

    def _on_camera_move(self, evt):
        x, y, z = evt.camera_location
        label = f"{x:.2f}, {y:.2f}, {z:.2f}"
        old_label = self._location_button.GetLabel()
        self._location_button.SetLabel(label)
        if len(label) != len(old_label):
            self.canvas.Layout()
        evt.Skip()
コード例 #5
0
class FilePanel(wx.BoxSizer, BaseUI):
    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)
        BaseUI.__init__(self, canvas)

        self._location_button = wx.Button(
            canvas, label=", ".join([f"{s:.2f}" for s in self.canvas.camera_location])
        )
        self._location_button.Bind(wx.EVT_BUTTON, lambda evt: self.canvas.goto())

        self.Add(self._location_button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

        self._dim_options = SimpleChoiceAny(canvas)
        self._dim_options.SetItems(self.canvas.world.world_wrapper.dimensions)
        self._dim_options.SetValue("overworld")
        self._dim_options.Bind(wx.EVT_CHOICE, self._on_dimension_change)

        self.Add(self._dim_options, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.CENTER, 5)

        def create_button(text, operation):
            button = wx.Button(canvas, label=text)
            button.Bind(wx.EVT_BUTTON, operation)
            self.Add(button, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
            return button

        self._undo_button: Optional[wx.Button] = create_button(
            "Undo", lambda evt: self.canvas.undo()
        )
        self._redo_button: Optional[wx.Button] = create_button(
            "Redo", lambda evt: self.canvas.redo()
        )
        self._save_button: Optional[wx.Button] = create_button(
            "Save", lambda evt: self.canvas.save()
        )
        create_button("Close", lambda evt: self.canvas.close())
        self._update_buttons()

        # self.Fit(self)
        self.Layout()

    def bind_events(self):
        self.canvas.Bind(EVT_CAMERA_MOVE, self._on_camera_move)
        self.canvas.Bind(EVT_UNDO, self._on_update_buttons)
        self.canvas.Bind(EVT_REDO, self._on_update_buttons)
        self.canvas.Bind(EVT_SAVE, self._on_update_buttons)
        self.canvas.Bind(EVT_CREATE_UNDO, self._on_update_buttons)

    def _on_update_buttons(self, evt):
        self._update_buttons()
        evt.Skip()

    def _update_buttons(self):
        self._undo_button.SetLabel(
            f"Undo | {self.canvas.world.history_manager.undo_count}"
        )
        self._redo_button.SetLabel(
            f"Redo | {self.canvas.world.history_manager.redo_count}"
        )
        self._save_button.SetLabel(
            f"Save | {self.canvas.world.history_manager.unsaved_changes}"
        )

    def _on_dimension_change(self, evt):
        """Run when the dimension selection is changed by the user."""
        dimension = self._dim_options.GetCurrentObject()
        if dimension is not None:
            self.canvas.dimension = dimension
        evt.Skip()

    def _change_dimension(self, evt):
        """Run when the dimension attribute in the canvas is changed.
        This is run when the user changes the attribute and when it is changed manually in code."""
        dimension = evt.dimension
        index = self._dim_options.FindString(dimension)
        if not (index == wx.NOT_FOUND or index == self._dim_options.GetSelection()):
            self._dim_options.SetSelection(index)

    def _on_camera_move(self, evt):
        x, y, z = evt.location
        label = f"{x:.2f}, {y:.2f}, {z:.2f}"
        old_label = self._location_button.GetLabel()
        self._location_button.SetLabel(label)
        if len(label) != len(old_label):
            self.canvas.Layout()
コード例 #6
0
class BaseSelectOperationUI(wx.BoxSizer, CameraToolUI):
    OperationGroupName = None

    def __init__(self, canvas: "EditCanvas"):
        wx.BoxSizer.__init__(self, wx.VERTICAL)
        CameraToolUI.__init__(self, canvas)

        self._selection = StaticSelectionBehaviour(self.canvas)

        self._active_operation: Optional[OperationUIType] = None

        horizontal_sizer = wx.BoxSizer(wx.HORIZONTAL)

        self._operation_choice = SimpleChoiceAny(self.canvas)
        self._reload_operation = wx.BitmapButton(
            self.canvas, bitmap=REFRESH_ICON.bitmap(16, 16)
        )
        self._reload_operation.SetToolTip("Reload Operations")

        horizontal_sizer.Add(self._operation_choice)
        horizontal_sizer.Add(self._reload_operation)

        self.Add(horizontal_sizer)

        assert isinstance(
            self.OperationGroupName, str
        ), "OperationGroupName has not been set or is not a string."

        self._operations = UIOperationManager(self.OperationGroupName)

        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )
        self._operation_choice.Bind(wx.EVT_CHOICE, self._on_operation_change)

        self._reload_operation.Bind(wx.EVT_BUTTON, self._on_reload_operations)

        self._operation_sizer = wx.BoxSizer(wx.VERTICAL)
        self.Add(self._operation_sizer, 1, wx.EXPAND)

    @property
    def name(self) -> str:
        raise NotImplementedError

    @property
    def operation(self) -> str:
        return self._operation_choice.GetCurrentObject()

    def _unload_active_operation(self):
        """Unload and destroy the UI for the active operation."""
        if self._active_operation is not None:
            self._active_operation.unload()
            if isinstance(self._active_operation, wx.Window):
                self._active_operation.Destroy()
            elif isinstance(self._active_operation, wx.Sizer):
                self._operation_sizer.GetItem(self._active_operation).DeleteWindows()
            self._active_operation = None

    def _on_operation_change(self, evt):
        """Run when the operation selection changes."""
        self._setup_operation()
        evt.Skip()

    def _setup_operation(self):
        """Create the UI for the new operation."""
        operation_path = self._operation_choice.GetCurrentObject()
        if operation_path:
            operation = self._operations[operation_path]
            self._unload_active_operation()
            self._active_operation = operation(
                self.canvas, self.canvas, self.canvas.world
            )
            self._operation_sizer.Add(
                self._active_operation, *self._active_operation.wx_add_options
            )
            self.Layout()

    def bind_events(self):
        super().bind_events()
        self._selection.bind_events()

    def enable(self):
        super().enable()
        self._selection.update_selection()
        self._setup_operation()

    def disable(self):
        super().disable()
        self._unload_active_operation()

    def _on_reload_operations(self, evt):
        """Run when the button is pressed to reload the operations."""
        self.reload_operations()

    def reload_operations(self):
        """Reload all operations and repopulate the UI."""
        # store the id of the old operation
        operation_id = self.operation

        # reload the operations
        self._operations.reload()

        # repopulate the selection
        self._operation_choice.SetItems(
            {op.identifier: op.name for op in self._operations.operations}
        )

        if operation_id:
            operation_loader = self._operations[operation_id]
            identifiers = self._operation_choice.values

            if identifiers:
                if operation_id in identifiers:
                    self._operation_choice.SetSelection(identifiers.index(operation_id))
                else:
                    log.info(f"Operation {operation_id} was not found.")
                    self._operation_choice.SetSelection(0)
            else:
                log.error("No operations found. Something has gone wrong.")

            self._setup_operation()

    def _on_draw(self, evt):
        self.canvas.renderer.start_draw()
        if self.canvas.camera.projection_mode == Projection.PERSPECTIVE:
            self.canvas.renderer.draw_sky_box()
            glClear(GL_DEPTH_BUFFER_BIT)
        self.canvas.renderer.draw_level()
        self._selection.draw()
        self.canvas.renderer.end_draw()