Basic

Un conjunto de herramientas GUI nativo de Python y del sistema operativo.

Introducció

Toga iss available on macOS, Windows, Linux (GTK), Android, iOS, for single-page web apps, and console apps.

Toga uses native system widgets, not themes. When you see a Toga app running, it doesn’t just look like a native app - it is a native app. Applying an operating system-inspired theme over the top of a generic widget set is an easy way for a developer to achieve a cross-platform goal, but it leaves the end user with the mess.

Native widgets are always faster than a themed generic widget. After all, you’re using native system capability that has been tuned and optimized, not a drawing engine that’s been layered on top of a generic widget. They also inherit all of the native platform’s accessibility affordances, such as support for screen readers and adaptive font sizes.

Toga has been designed from the ground up to be a Python native widget toolkit. This means the API is able to exploit language level features like generators and context managers

Your first Toga app

Crea un projecte toga-basic amb uv:

uv init toga-basic
cd toga-basic
uv add toga

Write the app

Modifica el fitxer main.py:

import toga


def button_handler(widget):
    print("hello")


def build(app):
    box = toga.Box()

    button = toga.Button("Hello world", on_press=button_handler)
    button.style.margin = 50
    button.style.flex = 1
    box.add(button)

    return box


def main():
    app = toga.App("Basic App", "dev.xtec.toga.basic", startup=build)
    app.main_loop()

if __name__ == "__main__":
    main()

Let’s walk through this one line at a time.

The code starts with imports. First, we import toga:

import toga

Then we set up a handler, which is a wrapper around behavior that we want to activate when the button is pressed. A handler is just a function. The function takes the widget that was activated as the first argument; depending on the type of event that is being handled, other arguments may also be provided. In the case of a simple button press, however, there are no extra arguments:

def button_handler(widget):
    print("hello")

When the app gets instantiated (in main()), Toga will create a window with a menu. We need to provide a method that tells Toga what content to display in the window. The method can be named anything, it just needs to accept an app instance:

def build(app):

We want to put a button in the window. However, unless we want the button to fill the entire app window, we can’t just put the button into the app window. Instead, we need create a box, and put the button in the box.

A box is an object that can be used to hold multiple widgets, and to define a margin around widgets. So, we define a box:

box = toga.Box()

We can then define a button. When we create the button, we can set the button text, and we also set the behavior that we want to invoke when the button is pressed, referencing the handler that we defined earlier:

button = toga.Button("Hello world", on_press=button_handler)

Now we have to define how the button will appear in the window. By default, Toga uses a style algorithm called Pack, which is a bit like “CSS-lite”. We can set style properties of the button:

button.style.margin = 50

What we’ve done here is say that the button will have a margin of 50 pixels on all sides. If we wanted to define a margin of 20 pixels on top of the button, we could have defined margin_top = 20, or we could have specified the margin = (20, 50, 50, 50).

Now we will make the button take up all the available width:

button.style.flex = 1

The flex attribute specifies how a widget is sized with respect to other widgets along its direction. The default direction is row (horizontal) and since the button is the only widget here, it will take up the whole width. Check out style docs for more information on how to use the flex attribute.

The next step is to add the button to the box:

box.add(button)

The button has a default height, defined by the way that the underlying platform draws buttons. As a result, this means we’ll see a single button in the app window that stretches to the width of the screen, but has a 50 pixel space surrounding it.

Now we’ve set up the box, we return the outer box that holds all the UI content. This box will be the content of the app’s main window:

return box

Lastly, we instantiate the app itself. The app is a high level container representing the executable. The app has a name and a unique identifier. The identifier is used when registering any app-specific system resources. By convention, the identifier is a “reversed domain name”.

The app also accepts our method defining the main window contents.

We wrap this creation process into a method called `main().

The call to main_loop() is a blocking call; it won’t return until you quit the main app.

def main():
    app = toga.App("Basic App", "dev.xtec.toga.basic", startup=build)
    app.main_loop()

And that’s it!

This should pop up a window with a button:

If you click on the button, you should see messages appear in the console. Even though we didn’t define anything about menus, the app will have default menu entries to quit the app, and an About page. The keyboard bindings to quit the app, plus the “close” button on the window will also work as expected. The app will have a default Toga icon (a picture of Tiberius the yak).

A slightly less toy example

Most applications require a little more than a button on a page. Let’s build a slightly more complex example - a Fahrenheit to Celsius converter:

Toga - Tutorial 1

Documentation

Toga - Documentation