import math import toga from toga.colors import WHITE, rgb from toga.constants import Baseline from toga.fonts import SANS_SERIF from toga.style import Pack class StartApp(toga.App): def startup(self): self.main_window = toga.MainWindow(size=(150, 250)) # Create empty canvas self.canvas = toga.Canvas( style=Pack(flex=1), on_resize=self.on_resize, on_press=self.on_press, ) box = toga.Box(children=[self.canvas]) # Add the content on the main window self.main_window.content = box # Draw tiberius on the canvas self.draw_tiberius() # Show the main window self.main_window.show() def fill_head(self): with self.canvas.Fill(color=rgb(149, 119, 73)) as head_filler: head_filler.move_to(112, 103) head_filler.line_to(112, 113) head_filler.ellipse(73, 114, 39, 47, 0, 0, math.pi) head_filler.line_to(35, 84) head_filler.arc(65, 84, 30, math.pi, 3 * math.pi / 2) head_filler.arc(82, 84, 30, 3 * math.pi / 2, 2 * math.pi) def stroke_head(self): with self.canvas.Stroke(line_width=4.0) as head_stroker: with head_stroker.ClosedPath(112, 103) as closed_head: closed_head.line_to(112, 113) closed_head.ellipse(73, 114, 39, 47, 0, 0, math.pi) closed_head.line_to(35, 84) closed_head.arc(65, 84, 30, math.pi, 3 * math.pi / 2) closed_head.arc(82, 84, 30, 3 * math.pi / 2, 2 * math.pi) def draw_eyes(self): with self.canvas.Fill(color=WHITE) as eye_whites: eye_whites.arc(58, 92, 15) eye_whites.arc(88, 92, 15, math.pi, 3 * math.pi) # Draw eyes separately to avoid miter join with self.canvas.Stroke(line_width=4.0) as eye_outline: eye_outline.arc(58, 92, 15) with self.canvas.Stroke(line_width=4.0) as eye_outline: eye_outline.arc(88, 92, 15, math.pi, 3 * math.pi) with self.canvas.Fill() as eye_pupils: eye_pupils.arc(58, 97, 3) eye_pupils.arc(88, 97, 3) def draw_horns(self): with self.canvas.Context() as r_horn: with r_horn.Fill(color=rgb(212, 212, 212)) as r_horn_filler: r_horn_filler.move_to(112, 99) r_horn_filler.quadratic_curve_to(145, 65, 139, 36) r_horn_filler.quadratic_curve_to(130, 60, 109, 75) with r_horn.Stroke(line_width=4.0) as r_horn_stroker: r_horn_stroker.move_to(112, 99) r_horn_stroker.quadratic_curve_to(145, 65, 139, 36) r_horn_stroker.quadratic_curve_to(130, 60, 109, 75) with self.canvas.Context() as l_horn: with l_horn.Fill(color=rgb(212, 212, 212)) as l_horn_filler: l_horn_filler.move_to(35, 99) l_horn_filler.quadratic_curve_to(2, 65, 6, 36) l_horn_filler.quadratic_curve_to(17, 60, 37, 75) with l_horn.Stroke(line_width=4.0) as l_horn_stroker: l_horn_stroker.move_to(35, 99) l_horn_stroker.quadratic_curve_to(2, 65, 6, 36) l_horn_stroker.quadratic_curve_to(17, 60, 37, 75) def draw_nostrils(self): with self.canvas.Fill(color=rgb(212, 212, 212)) as nose_filler: nose_filler.move_to(45, 145) nose_filler.bezier_curve_to(51, 123, 96, 123, 102, 145) nose_filler.ellipse(73, 114, 39, 47, 0, math.pi / 4, 3 * math.pi / 4) with self.canvas.Fill() as nostril_filler: nostril_filler.arc(63, 140, 3) nostril_filler.arc(83, 140, 3) with self.canvas.Stroke(line_width=4.0) as nose_stroker: nose_stroker.move_to(45, 145) nose_stroker.bezier_curve_to(51, 123, 96, 123, 102, 145) def draw_text(self): font = toga.Font(family=SANS_SERIF, size=20) self.text_width, text_height = self.canvas.measure_text("Tiberius", font) x = (150 - self.text_width) // 2 y = 175 with self.canvas.Stroke(color="REBECCAPURPLE", line_width=4.0) as rect_stroker: self.text_border = rect_stroker.rect( x - 5, y - 5, self.text_width + 10, text_height + 10, ) with self.canvas.Fill(color=rgb(149, 119, 73)) as text_filler: self.text = text_filler.write_text("Tiberius", x, y, font, Baseline.TOP) def draw_tiberius(self): self.fill_head() self.draw_eyes() self.draw_horns() self.draw_nostrils() self.stroke_head() self.draw_text() def on_resize(self, widget, width, height, **kwargs): # On resize, center the text horizontally on the canvas. on_resize will be # called when the canvas is initially created, when the drawing objects won't # exist yet. Only attempt to reposition the text if there's context objects on # the canvas. if widget.context: left_pad = (width - self.text_width) // 2 self.text.x = left_pad self.text_border.x = left_pad - 5 widget.redraw() def on_press(self, widget, x, y, **kwargs): self.main_window.info_dialog("Hey!", f"You poked the yak at ({x}, {y})") def main(): return StartApp("Tutorial 4", "org.beeware.toga.tutorial") if __name__ == "__main__": main().main_loop()