def _restart(self): self.update_car_value(g.start_car_value) self._sprites = [] self._just_simulated = [] self._pcontroller = PigeonController(world=self, keymap=self._keymap) car = Car(self) self._crosshair = Crosshair(self) self.add_sprite(self._crosshair) if '--showcar' in g.argv: self.add_sprite(car) else: self.add_just_simulated_sprite(car) print "keys = %s" % len(self._keymap)
def __init__(self, printer_uuid): template = os.path.split(__file__)[0] template = os.path.join(template, "templates", "printer_control.glade") self.builder = Gtk.Builder() self.builder.add_from_file(template) self.builder.connect_signals(self) window = self.builder.get_object("window1") self.widget = window.get_child() window.remove(self.widget) self.controls = self.builder.get_object("control_widgets_box") self.crosshair = Crosshair(self) self.info = None self.gauges = { "b": None, "t": [], } self.update_controls() self.disable() self.set_printer_name("Unknown Printer") PrinterInterface.__init__(self, printer_uuid)
def __init__(self, printer_uuid): template = os.path.split(__file__)[0] template = os.path.join(template, "templates", "printer_control.glade") self.builder = Gtk.Builder() self.builder.add_from_file(template) self.builder.connect_signals(self) window = self.builder.get_object("window1") self.widget = window.get_child() window.remove(self.widget) self.controls = self.builder.get_object("control_widgets_box") self.crosshair = Crosshair(self) self.info = None self.gauges = { "b" : None, "t" : [], } self.update_controls() self.disable() self.set_printer_name("Unknown Printer") PrinterInterface.__init__(self, printer_uuid)
def main(screen, window_size): """ :param screen: the surface object :param window_size: the window size as a tuple :return: void This is the main game loop """ FPS = 30 FPS_CLOCK = pygame.time.Clock() pygame.mixer.music.load('battle.ogg') pygame.mixer.music.play(-1) whip = pygame.mixer.Sound('whip.ogg') boom = pygame.mixer.Sound('boom.ogg') pond = pygame.image.load('pond.png') # Draw Frogs frog1 = Frog(1, window_size) crosshair1 = Crosshair(window_size, 1) frog2 = Frog(2, window_size) crosshair2 = Crosshair(window_size, 2) # Fly Setup fly_sprites = pygame.sprite.Group() FLY_SPAWN = pygame.USEREVENT + 1 pygame.time.set_timer(FLY_SPAWN, 1000) # Dragonfly Setup dfly_sprites = pygame.sprite.Group() DFLY_SPAWN = pygame.USEREVENT + 2 pygame.time.set_timer(DFLY_SPAWN, 5000) fireballs1 = pygame.sprite.Group() fireballs2 = pygame.sprite.Group() # Text start_ticks = pygame.time.get_ticks() timer = 61 timer_font = pygame.font.SysFont('berlinsansfb', 50) score_font = pygame.font.SysFont('berlinsansfb', 30) p1_move_right = False p1_move_left = False p1_charge = 0 p1_score = 0 p1_fire = 0 p2_move_right = False p2_move_left = False p2_charge = 0 p2_score = 0 p2_fire = 0 while True: # <--- main game loop seconds = (pygame.time.get_ticks() - start_ticks)/1000 # render objects SCREEN.blit(pond, (0, 0)) frog1.draw(SCREEN) frog2.draw(SCREEN) if len(dfly_sprites) > 0: dfly_sprites.update() dfly_sprites.draw(SCREEN) if len(fly_sprites) > 0: fly_sprites.update() fly_sprites.draw(SCREEN) if len(fireballs1) > 0: fireballs1.update() fireballs1.draw(SCREEN) frogs_stunned = pygame.sprite.spritecollide(frog2, fireballs1, True) if len(frogs_stunned) > 0: frog2.stunned = True p2_charge = 0 crosshair2.charging = 0 frog2.time_of_stun = timer - seconds if len(fireballs2) > 0: fireballs2.update() fireballs2.draw(SCREEN) frogs_stunned = pygame.sprite.spritecollide(frog1, fireballs2, True) if len(frogs_stunned) > 0: frog1.stunned = True p1_charge = 0 crosshair1.charging = 0 frog1.time_of_stun = timer - seconds # draw targeting line and charge meter for frog 1 draw_line(screen, frog1.mouth, (crosshair1.x + crosshair1.size[0]/2, crosshair1.y + crosshair1.size[1]/2)) crosshair1.draw(SCREEN, p1_charge * 5) # draw targeting line and charge meter for frog 2 draw_line(screen, frog2.mouth, (crosshair2.x + crosshair2.size[0]/2, crosshair2.y + crosshair2.size[1]/2)) crosshair2.draw(SCREEN, p2_charge * 5) for event in pygame.event.get(): if event.type == QUIT: # QUIT event to exit the game pygame.quit() sys.exit() # Fly event timer if event.type == FLY_SPAWN: if len(fly_sprites) < 10: if random.randrange(0, 2) == 0: fly = Fly(0, random.randrange(175, 501), random.randrange(1, 11)) fly_sprites.add(fly) else: fly = Fly(700, random.randrange(175, 501), random.randrange(1, 11)) fly_sprites.add(fly) # Dragonfly event timer if event.type == DFLY_SPAWN: if len(dfly_sprites) < 1: if random.randrange(0, 2) == 0: dragonfly = Dragonfly(0, random.randrange(175, 450), random.randrange(5, 11), random.randrange(3,11)) dfly_sprites.add(dragonfly) else: dragonfly = Dragonfly(700, random.randrange(175, 450), random.randrange(5, 11), random.randrange(3,11)) dfly_sprites.add(dragonfly) # Input events if event.type == KEYDOWN: if event.key == K_ESCAPE: return 0 # Player 1 if event.key == K_LEFT: p1_move_left = True if event.key == K_RIGHT: p1_move_right = True if event.key == K_DOWN and not frog1.stunned: crosshair1.charging = True # Player 2 if event.key == K_a: p2_move_left = True if event.key == K_d: p2_move_right = True if event.key == K_s and not frog2.stunned: crosshair2.charging = True # Quit if event.key == K_ESCAPE: pygame.quit() sys.exit() if event.type == KEYUP: # Player 1 if event.key == K_LEFT: p1_move_left = False if event.key == K_RIGHT: p1_move_right = False if event.key == K_DOWN and not frog1.stunned: whip.play() crosshair1.charging = False xhair1 = (crosshair1.x + crosshair1.size[0]/2,crosshair1.y + crosshair1.size[1]/2) hit_list, is_dragon = frog1.fire_tongue(SCREEN, p1_charge, xhair1[0], xhair1[1], fly_sprites, dfly_sprites) p1_score += len(hit_list) if len(hit_list) > 0 and p1_charge >= 15: p1_score += 2 if is_dragon: p1_fire += 1 p1_score += 1 p1_charge = 0 if event.key == K_UP and not frog1.stunned: if p1_fire > 0: boom.play() fireball = Fireball(1, frog1.x + 50, frog1.y) fireballs1.add(fireball) p1_fire -= 1 # Player 2 if event.key == K_a: p2_move_left = False if event.key == K_d: p2_move_right = False if event.key == K_s and not frog2.stunned: whip.play() crosshair2.charging = False xhair2 = (crosshair2.x + crosshair2.size[0]/2, crosshair2.y + crosshair2.size[1]/2) hit_list, is_dragon = frog2.fire_tongue(SCREEN, p2_charge, xhair2[0], xhair2[1], fly_sprites, dfly_sprites) p2_score += len(hit_list) if len(hit_list) > 0 and p2_charge >= 15: p2_score += 2 if is_dragon: p2_fire += 1 p2_score += 1 p2_charge = 0 if event.key == K_w and not frog2.stunned: if p2_fire > 0: boom.play() fireball = Fireball(2, frog2.x + 50, frog2.size[1] - 45) fireballs2.add(fireball) p2_fire -= 1 # Player 1 crosshair events if p1_move_left and not crosshair1.x < 0: crosshair1.change_x(-15) if p1_move_right and not crosshair1.x > window_size[0] - crosshair1.size[0]: crosshair1.change_x(15) if crosshair1.charging: if p1_charge < 20: p1_charge += 0.5 # Player 2 crosshair events if p2_move_left and not crosshair2.x < 0: crosshair2.change_x(-15) if p2_move_right and not crosshair2.x > window_size[0] - crosshair2.size[0]: crosshair2.change_x(15) if crosshair2.charging: if p2_charge < 20: p2_charge += 0.5 # Calculate, render timers if timer - seconds > 0: timer_text = str(round(timer - seconds, 2)) else: timer_text = 'GAME OVER' if frog1.stunned: if (timer - seconds) < frog1.time_of_stun - 2: frog1.stunned = False if frog2.stunned: if (timer - seconds) < frog2.time_of_stun - 2: frog2.stunned = False SCREEN.blit(timer_font.render(timer_text, False, (255, 0, 0)), (25, 25)) if (timer - seconds) <= 0: if p1_score > p2_score: return 1 elif p2_score > p1_score: return 2 elif p1_score == p2_score: return 3 # render HUD info p1_score_text = 'Player 1: ' + str(p1_score) p1_fire_text = 'Fireballs: ' + str(p1_fire) p2_score_text = 'Player 2: ' + str(p2_score) p2_fire_text = 'Fireballs: ' + str(p2_fire) SCREEN.blit(score_font.render(p1_score_text, True, (0, 0, 0)), (window_size[0]/2 + frog1.size[0]/2 + 25, window_size[1] - 75)) SCREEN.blit(score_font.render(p1_fire_text, True, (0, 0, 0)), (window_size[0]/2 + frog1.size[0]/2 + 25, window_size[1] - 50)) SCREEN.blit(score_font.render(p2_score_text, True, (0, 0, 0)), (window_size[0]/2 + frog1.size[0]/2 + 25, 25)) SCREEN.blit(score_font.render(p2_fire_text, True, (0, 0, 0)), (window_size[0]/2 + frog1.size[0]/2 + 25, 50)) pygame.display.update() FPS_CLOCK.tick(FPS)
class World(EventHandler): def __init__(self): super(World, self).__init__() self._font = pygame.font.Font(None, 48) self._background_channel = g.sounds['pigeon_background'] self._background_channel.set_volume(0.8) self._restart() def set_car_value(self, v): self._car_value = v def get_car_value(self): return self._car_value car_value = property(get_car_value, set_car_value) def update_car_value(self, car_value): print "car value = %s" % car_value self.car_value = car_value self._car_value_text = self._font.render("Car Value: %s$" % (self._car_value), 1, g.yellow) self._car_value_rect = self._car_value_text.get_rect(centerx=g.width*85/100, centery = g.height*9/10) def _restart(self): self.update_car_value(g.start_car_value) self._sprites = [] self._just_simulated = [] self._pcontroller = PigeonController(world=self, keymap=self._keymap) car = Car(self) self._crosshair = Crosshair(self) self.add_sprite(self._crosshair) if '--showcar' in g.argv: self.add_sprite(car) else: self.add_just_simulated_sprite(car) print "keys = %s" % len(self._keymap) def start_background_sound(self): self._background_channel.play(-1) def stop_background_sound(self): self._background_channel.stop() def on_mouse_down(self): self._crosshair.shoot() def add_sprite(self, sprite): self._sprites.append(sprite) def add_just_simulated_sprite(self, sprite): self._just_simulated.append(sprite) def simulated_pairs(self): all = self._sprites + self._just_simulated n = len(all) for i_src, src in enumerate(all): for i_dest in xrange(i_src + 1, n): dest = all[i_dest] yield src, dest def simulate(self): """ core interactions - simulate everything, collide everything, O(n^2) """ removed = [] for i, s in enumerate(self._sprites): if s.simulate() == 'killme': removed.append(i) # Check for collision, kill collided stuff for src, dest in self.simulated_pairs(): if src._rect.colliderect(dest._rect): src.onhit(dest) # \ dest.onhit(src) # / this makes it easier to implement - just define onhit where it matters # NOTE: it will only die in the next loop - not that bad.. # Delete finished projectiles / pigeons for i in sorted(set(removed), reverse=True): try: del self._sprites[i] except: print "cannot delete sprite %s, have %s sprites left" % (i, len(self._sprites)) def blit(self, screen): for s in self._sprites: if s.visible(): screen.blit(s._sprite, s._rect) # show car_value screen.blit(self._car_value_text, self._car_value_rect) # End game def car_is_dead_long_live_the_pigeons(self): splash(pygame.display.get_surface(), 'pigeon_win_splash.png', min_timeout=1.0) self._restart() def pigeons_dead_long_live_the_car(self): splash(pygame.display.get_surface(), 'car_win_splash.png', min_timeout=1.0) self._restart()
class PrinterController(PrinterInterface): """This class implements the gui functionality for the configuring and operating a specific printer.""" def __init__(self, printer_uuid): template = os.path.split(__file__)[0] template = os.path.join(template, "templates", "printer_control.glade") self.builder = Gtk.Builder() self.builder.add_from_file(template) self.builder.connect_signals(self) window = self.builder.get_object("window1") self.widget = window.get_child() window.remove(self.widget) self.controls = self.builder.get_object("control_widgets_box") self.crosshair = Crosshair(self) self.info = None self.gauges = { "b": None, "t": [], } self.update_controls() self.disable() self.set_printer_name("Unknown Printer") PrinterInterface.__init__(self, printer_uuid) def add_gauges(self): """Adds any necessary gaguges from a given temperature report.""" self.info = self.get_class_info() assert self.info.printer_type == "FFF 3D Printer" dirty = False if self.info.heated_bed: if self.gauges["b"] is None: dirty = True self.gauges["b"] = BedGauge(self) while self.info.tools > len(self.gauges["t"]): tool_num = len(self.gauges["t"]) self.gauges["t"].append(ExtruderGauge(self, tool_num)) dirty = True if dirty: self.update_controls() def get_gauges(self): """Return a list of all gauge widgets without context.""" gauges = [] if self.gauges["b"] is not None: gauges.append(self.gauges["b"]) if self.gauges["t"]: gauges += self.gauges["t"] return gauges def update_controls(self): """Clears out the controls packing box, and then populates it with a crosshair widget and some number of temperature controllers. """ for child in self.controls.get_children(): self.controls.remove(child) self.controls.add(self.crosshair.widget) for gauge in self.get_gauges(): self.controls.add(gauge.widget) gauge.enable() def on_temp_request(self, gauge, target): """Called by a gauge when a user requests a temperature change.""" # first determine if we're talking about a tool or the bed if self.gauges["b"] == gauge: # its the bed self.set_bed_temp(target) else: # which tool? try: tool = self.gauges["t"].index(gauge) self.set_tool_temp(tool, target) except ValueError: # never mind pass def refocus(self): """Called to reset the ui state. Currently, this is only really used by the crosshair when the notebook page changes. """ self.crosshair.refocus() def disable(self): """Disable the controls for this printer.""" self.crosshair.disable() for gauge in self.get_gauges(): gauge.disable() motors_off = self.builder.get_object("motors_off_button") motors_off.set_sensitive(False) def enable(self): """Enable this printer's controls.""" self.crosshair.enable() for gauge in self.get_gauges(): gauge.enable() motors_off = self.builder.get_object("motors_off_button") motors_off.set_sensitive(True) def on_state_change(self, state): """Signal handler for when the printer goes on or offline.""" if state == "ready": self.add_gauges() self.enable() elif state == "offline": self.disable() def on_report(self, blob): packet = json.loads(blob) if packet.has_key("thermistors"): temps = packet['thermistors'] if self.info.heated_bed: self.gauges["b"].set_label(temps["bed"][0]) for gauge, temp in zip(self.gauges["t"], temps["tools"]): gauge.set_label(temp[0]) def set_printer_name(self, name): """Sets the displayed name for this printer.""" label = self.builder.get_object("dashboard_header") label.set_text("Dashboard for %s" % name) def on_focus_in(self, widget, event_info): self.crosshair.on_focus_in(widget, event_info) for gauge in self.get_gauges(): gauge.on_focus_in(widget, event_info) def on_focus_out(self, widget, event_info): self.crosshair.on_focus_out(widget, event_info) for gauge in self.get_gauges(): gauge.on_focus_out(widget, event_info) def on_motors_off(self, *args): self.motors_off()
class PrinterController(PrinterInterface): """This class implements the gui functionality for the configuring and operating a specific printer.""" def __init__(self, printer_uuid): template = os.path.split(__file__)[0] template = os.path.join(template, "templates", "printer_control.glade") self.builder = Gtk.Builder() self.builder.add_from_file(template) self.builder.connect_signals(self) window = self.builder.get_object("window1") self.widget = window.get_child() window.remove(self.widget) self.controls = self.builder.get_object("control_widgets_box") self.crosshair = Crosshair(self) self.info = None self.gauges = { "b" : None, "t" : [], } self.update_controls() self.disable() self.set_printer_name("Unknown Printer") PrinterInterface.__init__(self, printer_uuid) def add_gauges(self): """Adds any necessary gaguges from a given temperature report.""" self.info = self.get_class_info() assert self.info.printer_type == "FFF 3D Printer" dirty = False if self.info.heated_bed: if self.gauges["b"] is None: dirty = True self.gauges["b"] = BedGauge(self) while self.info.tools > len(self.gauges["t"]): tool_num = len(self.gauges["t"]) self.gauges["t"].append(ExtruderGauge(self, tool_num)) dirty = True if dirty: self.update_controls() def get_gauges(self): """Return a list of all gauge widgets without context.""" gauges = [] if self.gauges["b"] is not None: gauges.append(self.gauges["b"]) if self.gauges["t"]: gauges += self.gauges["t"] return gauges def update_controls(self): """Clears out the controls packing box, and then populates it with a crosshair widget and some number of temperature controllers. """ for child in self.controls.get_children(): self.controls.remove(child) self.controls.add(self.crosshair.widget) for gauge in self.get_gauges(): self.controls.add(gauge.widget) gauge.enable() def on_temp_request(self, gauge, target): """Called by a gauge when a user requests a temperature change.""" # first determine if we're talking about a tool or the bed if self.gauges["b"] == gauge: # its the bed self.set_bed_temp(target) else: # which tool? try: tool = self.gauges["t"].index(gauge) self.set_tool_temp(tool, target) except ValueError: # never mind pass def refocus(self): """Called to reset the ui state. Currently, this is only really used by the crosshair when the notebook page changes. """ self.crosshair.refocus() def disable(self): """Disable the controls for this printer.""" self.crosshair.disable() for gauge in self.get_gauges(): gauge.disable() motors_off = self.builder.get_object("motors_off_button") motors_off.set_sensitive(False) def enable(self): """Enable this printer's controls.""" self.crosshair.enable() for gauge in self.get_gauges(): gauge.enable() motors_off = self.builder.get_object("motors_off_button") motors_off.set_sensitive(True) def on_state_change(self, state): """Signal handler for when the printer goes on or offline.""" if state == "ready": self.add_gauges() self.enable() elif state == "offline": self.disable() def on_report(self, blob): packet = json.loads(blob) if packet.has_key("thermistors"): temps = packet['thermistors'] if self.info.heated_bed: self.gauges["b"].set_label(temps["bed"][0]) for gauge, temp in zip(self.gauges["t"], temps["tools"]): gauge.set_label(temp[0]) def set_printer_name(self, name): """Sets the displayed name for this printer.""" label = self.builder.get_object("dashboard_header") label.set_text("Dashboard for %s" % name) def on_focus_in(self, widget, event_info): self.crosshair.on_focus_in(widget, event_info) for gauge in self.get_gauges(): gauge.on_focus_in(widget, event_info) def on_focus_out(self, widget, event_info): self.crosshair.on_focus_out(widget, event_info) for gauge in self.get_gauges(): gauge.on_focus_out(widget, event_info) def on_motors_off(self, *args): self.motors_off()
def __init__(self, world_to_slice, layers=None, annotations=None, interpolation=False, display_coordinates="physical", scalar_bar_visibility=False, orientation_visibility=True, corner_annotations_visibility=False, crosshair="full"): layers = layers or [] annotations = annotations or ObservableList() ############################ # Property-related members # ############################ self._interpolation = None self._display_coordinates = None self._scalar_bar_visibility = True self._orientation_visibility = None self._corner_annotations_visibility = None self._crosshair = None self._world_to_slice = None self._slice_to_world = None self._layers = [] self._annotations = None self._gui_annotations = {} self._image_physical_position = None self._image_index_position = None self._cursor_physical_position = None self._cursor_index_position = None self._zoom = None self._mouse_tools = {} self._keyboard_tools = {} self._renderer = vtkRenderer() ################### # Private members # ################### # World-to-slice matrix, with rows and columns added or removed so that # it is 3x3. self._3d_world_to_slice = None self._3d_slice_to_world = None # Slice extent is the physical extent of all layers, # given as (x_min, x_max, y_min, y_max) self._slice_extent = (-100, 100, -100, 100) # VTK objects self._scalar_bar_actor = vtkScalarBarActor() self._corner_annotation = vtkCornerAnnotation() self._orientation_annotation = vtkOrientationAnnotation() self._crosshair = Crosshair() # Tools and interactions self._observer_tags = [] self._active_source = None ################## # Initialization # ################## super(Slice, self).__init__([ "world_to_slice", "interpolation", "display_coordinates", "scalar_bar_visibility", "orientation_visibility", "corner_annotations_visibility", "crosshair", "zoom" ]) self.add_allowed_event("cursor_position") self.add_allowed_event("image_position") self.add_allowed_event("center") self.add_allowed_event("layer_visibility") # Configure camera camera = self._renderer.GetActiveCamera() camera.ParallelProjectionOn() camera.SetPosition(0, 0, self._actors_altitudes["camera"]) camera.SetFocalPoint(0, 0, 0) # Create cursor self._crosshair.altitude = self._actors_altitudes["cursor"] self._crosshair.hole_size = 5 self._renderer.AddActor(self._crosshair.actor) # Create scalar bar (from vtkInria3D) self._scalar_bar_actor.GetLabelTextProperty().SetColor(1.0, 1.0, 1.0) self._scalar_bar_actor.GetTitleTextProperty().SetColor(1.0, 1.0, 1.0) self._scalar_bar_actor.GetLabelTextProperty().BoldOff() self._scalar_bar_actor.GetLabelTextProperty().ShadowOff() self._scalar_bar_actor.GetLabelTextProperty().ItalicOff() self._scalar_bar_actor.SetNumberOfLabels(3) self._scalar_bar_actor.GetLabelTextProperty().SetFontSize(8) self._scalar_bar_actor.GetPositionCoordinate( ).SetCoordinateSystemToNormalizedViewport() self._scalar_bar_actor.SetWidth(0.1) self._scalar_bar_actor.SetHeight(0.5) self._scalar_bar_actor.SetPosition(0.8, 0.3) self._scalar_bar_actor.PickableOff() self._renderer.AddActor(self._scalar_bar_actor) # Setup text-annotation actors self._corner_annotation.SetNonlinearFontScaleFactor(0.3) self._renderer.AddActor(self._corner_annotation) self._orientation_annotation.SetNonlinearFontScaleFactor(0.25) self._renderer.AddActor(self._orientation_annotation) self._set_interpolation(interpolation) self._set_display_coordinates(display_coordinates) self._set_scalar_bar_visibility(scalar_bar_visibility) self._set_orientation_visibility(orientation_visibility) self._set_corner_annotations_visibility(corner_annotations_visibility) self._set_crosshair(crosshair) self._set_world_to_slice(world_to_slice) for layer in layers: self.append_layer(**layer) if annotations is not None: self._set_annotations(annotations) # Position slice at middle of layer 0 self.reset_view() # Configure default tools self.set_mouse_button_tool("Left", mouse_tools.Select()) self.set_mouse_button_tool("Middle", mouse_tools.Pan()) self.set_mouse_button_tool("Right", mouse_tools.WindowLevel()) self.set_wheel_tool("Forward", mouse_tools.Zoom(1.1)) self.set_wheel_tool("Backward", mouse_tools.Zoom(1. / 1.1)) self.set_keyboard_tool("Left", keyboard_tools.MoveCursor()) self.set_keyboard_tool("Right", keyboard_tools.MoveCursor()) self.set_keyboard_tool("Up", keyboard_tools.MoveCursor()) self.set_keyboard_tool("Down", keyboard_tools.MoveCursor()) self.set_keyboard_tool("Prior", keyboard_tools.MoveCursor()) self.set_keyboard_tool("Next", keyboard_tools.MoveCursor()) self.set_keyboard_tool("PageUp", keyboard_tools.MoveCursor()) self.set_keyboard_tool("PageDown", keyboard_tools.MoveCursor()) self.set_keyboard_tool("+", keyboard_tools.Zoom(1.1)) self.set_keyboard_tool("-", keyboard_tools.Zoom(1. / 1.1)) self.set_keyboard_tool("i", keyboard_tools.ToggleInterpolation()) self.set_keyboard_tool("b", keyboard_tools.ToggleScalarBarVisibility()) self.set_keyboard_tool( "c", keyboard_tools.ToggleCornerAnnotationsVisibility()) self.set_keyboard_tool("o", keyboard_tools.ToggleOrientationVisibility())