Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Dan Yeaw - 5 Steps to Build Python Native GUI Widgets for BeeWare

Dan Yeaw - 5 Steps to Build Python Native GUI Widgets for BeeWare

Have you ever wanted to write a GUI application in Python that you can run on both your laptop and your phone? Have you been looking to contribute to an open source project, but you don't know where to start?

BeeWare is a set of software libraries for cross-platform native app development from a single Python codebase and tools to simplify app deployment. The project aims to build, deploy, and run apps for Windows, Linux, macOS, Android, iPhone, and the web. It is native because it is actually using your platform's native GUI widgets, not a theme, icon pack, or webpage wrapper.

This talk will teach you how Toga, the BeeWare GUI toolkit, is architected and then show you how you can contribute to Toga by creating your own GUI widget in five easy steps.

https://us.pycon.org/2019/schedule/presentation/235/

53b37e14a09c5a718a39fda61fe1b8e5?s=128

PyCon 2019

May 05, 2019
Tweet

Transcript

  1. 5 STEPS TO BUILD PYTHON 5 STEPS TO BUILD PYTHON

    NATIVE GUI WIDGETS FOR NATIVE GUI WIDGETS FOR
  2. BeeWare is CROSS-PLATFORM CROSS-PLATFORM NATIVE NATIVE APP DEVELOPMENT APP DEVELOPMENT

    WITH PYTHON WITH PYTHON and SIMPLE APP DEPLOYMENT SIMPLE APP DEPLOYMENT
  3. TOGA TOGA BEEWARE'S GUI TOOLKIT BEEWARE'S GUI TOOLKIT

  4. HELLO WORLD HELLO WORLD

  5. import toga class HelloWorld(toga.App): def startup(self): self.main_window = toga.MainWindow(title=self.name) main_box

    = toga.Box() self.main_window.content = main_box self.main_window.show() def main(): return HelloWorld('Hello World', 'org.pybee.helloworld')
  6. BACKGROUND BACKGROUND A widget is the controls and logic that

    a user interacts with when using a GUI A Canvas widget will be used as an example
  7. TOGA BLACKBOX TOGA BLACKBOX

  8. BRIDGE OR BRIDGE OR TRANSPILER TRANSPILER

  9. TOGA WHITEBOX TOGA WHITEBOX

  10. MORE TERMS MORE TERMS

  11. TOGA_IMPL FACTORY TOGA_IMPL FACTORY PATTERN PATTERN

  12. STEP 0 STEP 0 DEVELOPMENT PLATFORM DEVELOPMENT PLATFORM Normally pick

    the platform that you are most familiar with macOS and GTK are the most developed � Is this a mobile only widget (camera, GPS, etc)?
  13. STEP 1 STEP 1 RESEARCH YOUR WIDGET RESEARCH YOUR WIDGET

    Abstraction requires knowledge of specific examples Create use cases or user stories Get feedback
  14. RESEARCH YOUR WIDGET RESEARCH YOUR WIDGET Tkinter canvas = tk.Canvas()

    canvas.create_rectangle(10, 10, 100, 100, fill="red") canvas.pack()
  15. RESEARCH YOUR WIDGET RESEARCH YOUR WIDGET GTK drawingarea = Gtk.DrawingArea()

    drawingarea.connect("draw", draw) def draw(da, ctx): ctx.set_source_rgb(200, 0, 0) ctx.rectangle(10, 10, 100, 100) ctx.fill()
  16. RESEARCH YOUR WIDGET RESEARCH YOUR WIDGET Use Cases

  17. STEP 2 STEP 2 WRITE DOCS WRITE DOCS Write your

    API documentation first The API provides the set of clearly defined methods of communication (layers) between the so�ware components Documentation Driven Development This is iterative with Step 1
  18. WRITE DOCS WRITE DOCS The canvas is used for creating

    a blank widget that you can draw on. ## Usage An example of simple usage is to draw a colored rectangle on the screen using the `rect` drawing object: import toga canvas = toga.Canvas(style=Pack(flex=1)) with canvas.fill(color=rgb(200, 0, 0)) as fill: fill.rect(10, 10, 100, 100)
  19. WRITE CODE OUTLINE / WRITE CODE OUTLINE / DOCSTRINGS DOCSTRINGS

    class Canvas(Widget): """Create new canvas. Args: id (str): An identifier for this widget. style (:obj:`Style`): An optional style object. factory (:obj:`module`): A python module that is capable to return a implementation of this class.
  20. STEP 3 STEP 3 IMPLEMENT TOGA_CORE IMPLEMENT TOGA_CORE (WITH TDD)

    (WITH TDD) First write tests for Toga_core Then code the outline created in Step 2
  21. WRITE TESTS FOR TOGA_CORE WRITE TESTS FOR TOGA_CORE def test_widget_created():

    assertEqual(canvas._impl.interface, canvas) self.assertActionPerformed(canvas, "create Canvas")
  22. WRITE TESTS FOR TOGA_CORE WRITE TESTS FOR TOGA_CORE def test_rect_modify():

    rect = canvas.rect(-5, 5, 10, 15) rect.x = 5 rect.y = -5 rect.width = 0.5 rect.height = -0.5 canvas.redraw() self.assertActionPerformedWith( canvas, "rect", x=5, y=-5, width=0.5, height=-0.5 )
  23. CODE TOGA_CORE CODE TOGA_CORE class Canvas(Widget): def __init__(self, id=None, style=None,

    factory=None): super().__init__(id=id, style=style, factory=factory) self._impl = self.factory.Canvas(interface=self) def rect(self, x, y, width, height): self.impl.rect( self.x, self.y, self.width, self.height )
  24. STEP 4 STEP 4 IMPLEMENT TOGA_IMPL IMPLEMENT TOGA_IMPL DUMMY BACKEND

    DUMMY BACKEND Dummy is for automatic testing without a native platform Code the implementation layer API endpoint, create a method for each call of the API Check that all tests now pass
  25. IMPLEMENT TOGA_IMPL IMPLEMENT TOGA_IMPL DUMMY BACKEND DUMMY BACKEND class Canvas(Widget):

    def create(self): self._action("create Canvas") def rect(self, x, y, width, height): self._action( "rect", x=x, y=y, width=width, height=height )
  26. STEP 5 STEP 5 IMPLEMENT TOGA_IMPL IMPLEMENT TOGA_IMPL YOUR PLATFORM

    YOUR PLATFORM Copy toga_dummy and create a new endpoint for the platform you chose in Step 1 Make use of the native interface API for this widget on your platform
  27. IMPLEMENT TOGA_IMPL IMPLEMENT TOGA_IMPL YOUR PLATFORM YOUR PLATFORM

  28. class Canvas(Widget): def create(self): self.native = Gtk.DrawingArea() self.native.connect("draw", self.gtk_draw_callback) def

    gtk_draw_callback(self, canvas, gtk_context): self.interface._draw(self, draw_context=gtk_context) def rect(self, x, y, width, height, draw_context): draw_context.rectangle(x, y, width, height)
  29. IMPLEMENT TOGA_IMPL IMPLEMENT TOGA_IMPL OTHER PLATFORMS OTHER PLATFORMS

  30. class TogaCanvas(NSView): @objc_method def drawRect_(self, rect: NSRect) -> None: context

    = NSGraphicsContext.currentContext.graphicsPort() class Canvas(Widget): def create(self): self.native = TogaCanvas.alloc().init() def rect(self, x, y, width, height, draw_context, *args, **kwargs): rectangle = CGRectMake(x, y, width, height) core_graphics.CGContextAddRect(draw_context, rectangle
  31. ITERATE ITERATE ITERATE THROUGH STEPS 1-5 TO ITERATE THROUGH STEPS

    1-5 TO COMPLETE YOUR WIDGET COMPLETE YOUR WIDGET IMPLEMENTATION IMPLEMENTATION
  32. SUBMIT A PULL REQUEST! SUBMIT A PULL REQUEST! ��� ���

  33. SUMMARY SUMMARY 1. Research Your Widget 2. Write Docs 3.

    Toga_core 4. Toga_impl - Dummy Backend 5. Toga_impl - Your Platform
  34. @danyeaw github.com/danyeaw dan.yeaw.me linkedin.com/in/danyeaw dan@yeaw.me