Blog
Want more like this?
Subscribe!

A new way of writing Gtk+ applications

Introducing Pyract - my weekend hack

I love working with Gtk+ - it is a great GUI toolkit with a good developer experience. But React has totally changed how GUI apps are written. Now it is all the rage to use more functional style programming:

Gtk3 counter app

The app very complex, see below

I've always wanted to see a combination of these two things, so this is my weekend hack. It is pyract, a python3 library that merges Gtk+, React and MobX into 1 boiling pot:

from gi.repository import Gtk
from pyract.view import run, Component, Node, load_css
from pyract.model import ObservableModel, ObservableValue, ModelField

# Let's create a model to back our application.  Since it is Observable, it
# will tell pyract to re-render our UI when it changes.
class AppModel(ObservableModel):
    # The ModelField tells the model to create us an ObservableValue and set
    # to 0.  ObservableValues lets us re-render the UI when the value changes.
    counter = ModelField(ObservableValue, 0)

    def increment(self):
        self.counter.value = self.counter.value + 1


# Components are similar to in React.
class AppComponent(Component):
    # Our render method can return a Node or list of Nodes.
    def render(self, model: AppModel):
        # Nodes are a type followed by kwargs.  When a component is
        # re-rendered, then the Node trees get diffed and only the changed
        # Nodes and properties are updated.
        # The type is either a Gtk.Widget or pyract.Component subclass.
        # "signal__" props are the same as connecting a GObject signal
        return Node(Gtk.Window, signal__destroy=Gtk.main_quit,
                    title='My Counter App',
                    children=[
            Node(Gtk.Box, orientation=Gtk.Orientation.VERTICAL,
                 children=[
                # The class_names prop adds the appropriate classes
                Node(Gtk.Label, class_names=['counter-label'],
                     label=str(model.counter.value)),
                Node(Gtk.Button, label='Increment Counter',
                     class_names=['suggested-action', 'bottom-button'],
                     # Hide the button when the counter gets to ten
                     visible=model.counter.value < 10,
                     signal__clicked=self._button_clicked_cb),
                # Add a reset button, but only show it when counter == 10
                Node(Gtk.Button, label='Reset',
                     class_names=['destructive-action', 'bottom-button'],
                     visible=model.counter.value >= 10,
                     signal__clicked=self._reset_clicked_cb)
            ])
        ])

    # Signal handlers are just like in normal Gtk+
    def _button_clicked_cb(self, button):
        # Access the props using self.props
        self.props['model'].increment()

    def _reset_clicked_cb(self, button):
        self.props['model'].counter.value = 0


# Adding CSS is really easy:
load_css('''
.counter-label {
    font-size: 100px;
    padding: 20px;
}
.bottom-button {
    margin: 10px;
}
''')


# The run function works just like constructing a Node, but it enters
# the mainloop and runs the app!
run(AppComponent, model=AppModel())

Be sure to subscribe below so that you get updates on the pyract project. Or check it out on GitHub. And as always, feel free to email me feedback.

Get More Great Content

The intersection of marketing, design and machine learning, delivered straight to your inbox:
😀
Awesome! Please check your inbox and confirm your email.
We'll email you our latest posts plus special past content. Change your settings any time.

Exposing properties with Graphene Django

The other missing guide

Arithmetic with JavaScript Arrays

A Astonishing Adventure

Freeing Disk Space with the PackageKit cache

Automatic updates gone wrong

Keeping Python projects secure on GitLab

Pinning projects to the very latest

Testing GraphQL with Graphene Django

The missing guide

Local Politicians Meet InfoSec - a Wordpress Disaster

The article that I didn't want to have to write

PGP for Every Email

Join us in our PGP journey

SELinux Concepts - but for humans

This is your SELinux dictionary!

Stop Disabling SELinux: A Real-World guide

Be safe from software vulnerabilities AND run your webserver

Plotinus and the quest for searchable menus

The underdog challenges a 30 year old UI convention

DMARC Secured Your Email Identity, But See How it Ruined Mailing Lists

Why people aren't posting on your mailing list

How they track you: Email Service Provider Edition

A summary of how major email marketers track their emails

Blender for Hackers - 3D modeling is just like using VIM

A very brief introduction to Blender

Edge of the World - What Open-World Games Can Teach Us About Design

Spoiler: It's all about the illusions

When fictional worlds are an accurate representations of IoT security

Ok, a little dramatized. But still truthful.

How I Destroyed my Blog's Performance with CSS Background-Blend-Modes

Just because a browser has a feature doesn't mean you should use it

Help Us Answer: The Email Signup Popup - where is it from?

Who is behind the latest wave of popups?

My WATCH runs GNU/Linux And It Is Amazing

Lennart Poettering would love it!

6 Stunning Email SignUp Form Designs with Free HTML

I've spent way to much time on dribbble researching these!
G'day Mate, join us on IRC for good banter:
#Learnt on Freenode
See you there!