Slide 1

Slide 1 text

Yasushi Itoh (i2y) A Cross-Platform Pure Python Declarative UI Framework

Slide 2

Slide 2 text

● Software Engineer at Mercari, Inc. in Japan ● I like several programming languages. ● I've been using Python regularly for over 20 years and my favorite programming language to use. ● My past python experience: ○ Hobby: i2y/mochi: Dynamically typed functional programming language (github.com) ○ Job: document translation (combination of Natural Language Processing, Image Processing and ML) on AWS Step Functions Self Introduction

Slide 3

Slide 3 text

Background

Slide 4

Slide 4 text

● In the software I have been involved in developing, I have used various languages depending on the requirements. ● In the applications and backend systems at my current employer, various programming languages are used depending on the purpose. Technology Stack | Mercari Engineering ● This is common since different languages and frameworks have different strengths and weaknesses. Use of various programming languages and frameworks

Slide 5

Slide 5 text

The reasons I think: ● Switching Cost 1: If everything can be written in one language, it reduces the burden on the brain when switching a language to another language on programming. ● Switching Cost 2: I may not need to acquire knowledge of multiple languages and frameworks. ● Hiring Cost?: Hiring someone may be easier (if it’s not an obscure language). To avoid incurring these costs, there are two directions to take: write the web- frontend/app parts and backend parts in a single language or framework. Or write all UI code (all frontend/app parts) in less languages and frameworks. I don't want to write code from scratch for each platform, especially in the case of UI code, which can be mostly the same. I think its’ a common desire for other programmers too. Want to write apps/services using less languages or frameworks if I can..

Slide 6

Slide 6 text

So.. several Cross-Platform UI Frameworks exist now and have existed in the past for that..

Slide 7

Slide 7 text

Interest in Flutter/Compose Multiplatform interest has been increasing in recent years Note: Please forgive that the search is based on Jetpack Compose Multiplatform instead of Compose Multiplatform, but it doesn't change most results.

Slide 8

Slide 8 text

● For single programming language ○ Flutter: Dart, Compose Multiplatform: Kotlin ○ These are primarily implemented in that language and have APIs specific to that language. These are not wrappers for existing UI frameworks. So… it’s easy to use in that language and seems to be easy to hack or understand. ● Declarative UI: UI can be described declaratively in the language ○ Other than Flutter and Compose Multiplatform, most of the other UI frameworks that have emerged in the recent past claim to offer Declarative UI (React Native/React, SwiftUI, etc.) Common Features of Flutter and Compose Multiplatform

Slide 9

Slide 9 text

What’s Declarative UI? I've done some research, but there doesn’t seem to be a clear definition. Of course, Wikipedia doesn’t have an entry for it. However, roughly speaking, it seems that the display (view) and state are defined declaratively, the view switches automatically when the state changes. Also, the view can be defined structurally. Non-declarative UI is sometimes called Imperative UI. # Declarative UI state = State(“text”) view = Page(Text(state), Button(“button”) .on_click(lambda: state.set(“text2”))) view.show() click

Slide 10

Slide 10 text

What’s Declarative UI? # Imperative UI p = Page() txt = Text(“text”) p.add_child(txt) btn = Button(“button”) p.add_child(btn) def change_text(): p.remove_child(txt) new_txt = Text(“text2”) p.add_child(new_text) btn.on_click(change_text) p.show() # Declarative UI state = State(“text”) view = Page(Text(state), Button(“button”) .on_click(lambda: state.set(“text2”))) view.show() I've done some research, but there doesn’t seem to be a clear definition. Of course, Wikipedia doesn’t have an entry for it. However, roughly speaking, it seems that the display (view) and state are defined declaratively, the view switches automatically when the state changes. Also, the view can be defined structurally. Non-declarative UI is sometimes called Imperative UI.

Slide 11

Slide 11 text

● I simply want or would like to see it. Especially something that isn’t a wrapper for an existing framework. ● It may be tough to use for product applications, but it could be used for internal tools or for private use. Is there a cross-platform declarative UI framework in the Python World?

Slide 12

Slide 12 text

Famous Cross-Platform UI Frameworks in the Python World (At least two platform supported) Name Declarative UI View can be defined declaratively in Python Not a wrapper Qt for Python (PySide6) ✔ (QML) (Qt) Kivy ✔ (Kv Language) ✔ Tkinter (tcl/tk) wxPython (WxWidgets) Enaml ✔ (Qt) Toga ✔ ✔ (OS Natives/Web) Flexx ✔ ✔ (Web) Edifice ✔ ✔ (Qt)

Slide 13

Slide 13 text

Why doesn't it exist?

Slide 14

Slide 14 text

My Guess It was common practice to use native look and feel. This has changed recently as web-based applications have become more common. ① Both Flutter and Compose Multiplatform use Skia. This is an important and essential part of the implementation, but it or an equivalent have only recently become available in Python. ③ The demand for writing views declaratively had been met by KV language of Kivy and others, so the motivation to create such an API from scratch was weak in the community and among individuals. ② CPU and GPU performance and the libraries to utilize them were lacking in the past. ④

Slide 15

Slide 15 text

● google/skia: Skia is a complete 2D graphic library for drawing Text, Geometries, and Images. (github.com) ● CanvasKit - Skia + WebAssembly | Skia Skia and CanvasKit

Slide 16

Slide 16 text

The aforementioned reasons may no longer be relevant or exist. So, I think it’s time to trial a cross-platform declarative UI for Python.

Slide 17

Slide 17 text

● i2y/castella: Castella is a cross-platform pure Python UI framework (github.com) Castella

Slide 18

Slide 18 text

● Pure Python! ○ Not a wrapper. It’s made for Python using Python from scratch. ○ Except for dependent window management/rendering libraries ● Declarative UI ● Cross Platform ○ Windows, Mac OS, Linux, Web Browsers ■ Currently, it supports only desktop OS and browsers. Main Features of Castella

Slide 19

Slide 19 text

● Pure Python! ○ Not a wrapper. It’s made for Python using Python from scratch. ○ Except for dependent window management/rendering libraries ● Declarative UI ● Cross Platform ○ Windows, Mac OS, Linux, Web Browsers ■ Currently, it supports only desktop OS and browsers. Main Features of Castella

Slide 20

Slide 20 text

Castella is a Pure Python Framework ● It’s pure python, except for dependent window management and rendering libraries. ● For Desktop App: (GLFW or SDL2) and Skia ● For Web Browser: CanvasKit and PyScript/PyOdide GLFW Castella Desktop OS Skia SDL2 Castella Desktop OS Skia PyScri pt Castella Web Browser Canva sKit ※ It’s important for Castella that there are nice bindings for each native libraries in Python. E.g., skia-python.

Slide 21

Slide 21 text

Castella is a Pure Python Framework ● The Core and each widget module is highly portable because it relies only on the Painter and Frame Protocols, which define the I/F common to each backend. If you add a backend that implements the common I/F for a certain platform, Core and each widget should work correctly. GLFW Castella Desktop OS Skia SDL2 Castella Desktop OS Skia PyScri pt Castella Web Browser Canvas Kit ※ It’s important for Castella that there are nice bindings for each native libraries in Python. E.g., skia-python. Painter Frame Painter Frame Painter Frame

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

Main Features of Castella ● Pure Python! ○ Not a wrapper. It’s made for Python using Python from scratch. ○ Except for dependent window management/rendering libraries ● Declarative UI ● Cross Platform ○ Windows, Mac OS, Linux, Web Browsers ■ Currently, it supports only desktop OS and browsers.

Slide 24

Slide 24 text

Declarative UI class Counter(Component): def __init__(self): super().__init__() self._count = State(0) def view(self): return Column( Text(self._count), Row( Button("Up", font_size=50).on_click(self.up), Button("Down", font_size=50).on_click(self.down), ), ) def up(self, _): self._count += 1 def down(self, _): self._count -= 1 App(Frame("Counter", 800, 600), Counter()).run() View State Event Callbacks

Slide 25

Slide 25 text

Declarative UI class Clock(Component): def __init__(self): super().__init__() self._state = State(datetime.now().time().strftime("%X")) self._thread = threading.Thread(target=self.run, args=()) self._thread.daemon = True self._thread.start() def run(self): while True: time.sleep(1) self._state.set(datetime.now().time().strftime("%X")) def view(self): return Text(self._state) App(Frame("Clock", 600, 200), Clock()).run() Thread updating the state (time) every second

Slide 26

Slide 26 text

● Pure Python! ○ Not a wrapper. It’s made for Python using Python from scratch. ○ Except for dependent window management/rendering libraries ● Declarative UI ● Cross Platform ○ Windows, Mac OS, Linux, Web Browsers ■ Currently, it supports only desktop OS and browsers. Main Features of Castella

Slide 27

Slide 27 text

Cross-Platform Castella Desktop OS Skia GLFW Castella Desktop OS Skia SDL2 Castella Web Browser Canvas Kit PyScript

Slide 28

Slide 28 text

A few more details about Castella

Slide 29

Slide 29 text

Layout Column Row Box First Second Third First Second Third Child Column( Text(“First"), Text(“Second"), Text(“Third"), ) Row( Text(“First"), Text(“Second"), Text(“Third"), ) Box(Text(“Child")) Column aligns child elements vertically. Row aligns child elements horizontally. Box takes only one child elements and if the child size is larger than the box, it provides scrollbars automatically.

Slide 30

Slide 30 text

Size Policy Each widget has size policies for width and height. There are three types of policies that can be specified ● Expanding: maximum size that will fit in the parent widget ● Fixed: specified size ● Content: size according to the content (e.g., for text, according to the size of that text) The default policy is different depending on the kind of widget, but it is basically “Expanding” for both width and height.

Slide 31

Slide 31 text

Examples: Expanding Basic Behavior The default setting value of the example on the left without omission Each child element (widgets)’s default size policy is basically Expanding. In this example, the three child elements share their width equally. The height of each child element is the height of the row. First Second Third Row( Text(“First"), Text(“Second"), Text(“Third"), ) Row( Text(“First").WidthPolicy(SizePolicy.Expanding) .HeightPolicy(SizePolicy.Expanding), Text(“Second").WidthPolicy(SizePolicy.Expanding) .HeightPolicy(SizePolicy.Expanding), Text(“Third").WidthPolicy(SizePolicy.Expanding) .HeightPolicy(SizePolicy.Expanding), )

Slide 32

Slide 32 text

Examples: Expanding Flex By specifying "flex" you can specify a percentage of the overall size. In this example, the width of the entire row is divided in the ratio 1:2:1. First Second Third Row( Text(“First").flex(1), Text(“Second").flex(2), Text(“Third").flex(1), ) Mix with Other Policies Of course, you can mix multiple size policies for the children of a single parent widget. In this example, the first element is displayed at the specified fixed size. The remaining two child elements share the remaining width in 2:1 ratio. First Second Third Row( Text(“First").fixed(300, 100), Text(“Second").flex(2), Text(“Third").flex(1), )

Slide 33

Slide 33 text

Two Types of Custom Widget You can combine built-in widgets to create a custom widget. It is defined as a subclass of Component or a subclass of Stateful Component. (You can also define custom widgets without combining existing widgets, but that's another story.) ● Component ○ A component that doesn’t have the state of the component as a whole ● Stateful Component ○ A component that has state as the component

Slide 34

Slide 34 text

Component class Counter(Component): def __init__(self): super().__init__() self._count = State(0) def view(self): c = self._count return Row( Button("Up", font_size=50).on_click(lambda _: c.set(c() + 1)), Text(c()), Button("Down", font_size=50).on_click(lambda _: c.set(c() - 1)), ) App(Frame("NumList", 800, 600), Counter()).run()

Slide 35

Slide 35 text

Component class Counter(Component): def view(self): count = State(0) up = lambda _: count.set(count() + 1) down = lambda _: count.set(count() - 1) return Column( Text(count), Row( Button("+", font_size=50).on_click(up), Button("-", font_size=50).on_click(down), ), ) App(Frame("Counter", 800, 600), Counter()).run()

Slide 36

Slide 36 text

Stateful Component class NumList(StatefulComponent): def __init__(self, n: State[int]): super().__init__(n) self._num: State[int] = n def view(self): return Column( Button(“Add”).fixed_height(40).on_click(lambda _: self.num.set(self._num() + 1)), *(Text(I + 1).fixed_height(40) for I in range(self._num())), scrollable=True, ) App(Frame("NumList", 480, 300), NumList(State(1))).run()

Slide 37

Slide 37 text

And more.. ● Dark Mode ● Hot Reloading/Restarting ● …

Slide 38

Slide 38 text

Summary

Slide 39

Slide 39 text

● Cross Platform Declarative UI Frameworks seem to be gaining momentum. ● In Declarative UI, view and state are defined declaratively, and the view switches automatically when the state changes. Also, the view can be defined structurally. ● There are many good Cross Platform UI Frameworks available in Python. However, there doesn't seem to be a pure python one that allows you to write declarative UI in Python. ● I tried making Castella. If you like, please try using it to create something. I hope this presentation has been of some use to you all. Summary

Slide 40

Slide 40 text

Thank you for listening

Slide 41

Slide 41 text

Taiwan 2022 PyCon APAC