Skip to main content

A thin-ish wrapper around tkinter for specifying GUIs declaratively.

Project description

Table of Contents

  1. Ticklish - Declarative style GUI programming in Python
    1. Getting Started
      1. Next Steps
    2. Features
      1. Widgets
      2. Streams
    3. Future Development
    4. Contributing
    5. License

Ticklish - Declarative style GUI programming in Python

The ticklish_ui package is a wrapper around the tkinter and tkinter.ttk widgets which aims to simplify GUI creation by allowing users to specify the layout declaratively while decoupling GUI creation from event binding.

Ticklish works by wrapping the underlying widgets in factory objects deferring actual widget creation until the entire layout has been specified. Layouts are specified as rows of widgets which are laid out from left to right. Once created, event streams are used to bind actions to specific widgets.

The following simple example creates a window which accepts some input, prints it to the console when the OK button is clicked, and closes the application when the Quit button is clicked.

from ticklish_ui import *

# Define the layout.  An application can have any number of rows and
# rows can contain any number of widgets.
app = Application(
    'ticklish_ui_example',

    # .row1
    [Label('Enter some text below')],

    # .row2
    [Entry().options(name='entry')],

    # .row3
    [Button('OK').options(name='ok'), CloseButton('Quit')]
)

def print_input(event):
    entry = app.nametowidget('.row2.entry')
    print(entry.get())

# click captures all click events anywhere in the application.
click = app.get_event_stream('<ButtonRelease-1>')

# An event stream can then be filtered and bound to some action(s)
(click
 # Here we filter by the name of the widget clicked.
 .by_name('ok') 

 # And then map/bind an action to that event.
 .map(print_input)
)

app.mainloop()

img

The use of event streams is optional. Users can retrieve widgets and bind events and commands in a more traditional way if they prefer. The following would also have worked in the above example.

def print_input():
    entry = app.nametowidget('.row2.entry')
    print(entry.get())

ok_button = app.nametowidget('.row3.ok')
ok_button['command'] = print_input

# Or you can bind events.
#ok_button.bind('<ButtonRelease-1>', do_something)

Most ticklish widgets are just straight wrappers for the underlying widgets but additions have been made for convenience. For instance, CloseButton in the above example is a button which automatically calls the destory() method on the toplevel window that contains it. Similarly, there are RadioGroup and CheckGroup widgets which allow you to lay out whole sets of the corresponding buttons easily.

The goal of ticklish is to simplify the creation and implementation of GUIs without abstracting away any of their power.

Getting Started

To start using ticklish_ui install it from the Python Package Index with pip:

pip3 install ticklish_ui

Check that the install worked by running the following code either from a file or the python interactive interpreter.

import ticklish_ui as tui

tui.Application('MyApp').mainloop()

You should get something that looks like this:

img

Next Steps

  1. Themes

    Ticklish is set up to use the ttk default theme out-of-the-box which probably won't look that great. Once created, you can use the application's style property to change the theme.

    app = Application(
        'MyApp',
        # Rows...
    )
    app.style.theme_use('aqua') # Or whatever theme you're using
    app.mainloop()
    

    The aqua theme is used in the above screenshots but may not be available on all systems.

    You can use the theme viewer example to see the themes available on your system and then set one as above.

    img

  2. Module Documentation

    An attempt has been made to make the ticklish_ui module documentation as comprehensive as possible. It can be viewed in a number of ways.

    With pydoc from the commandline:

    pydoc3 ticklish_ui.widgets.application
    

    With help() from the python interactive interpreter:

    >>> import ticklish_ui
    >>> help(ticklish_ui.events.EventStream)
    

    Or just by browsing the source code on github.

    The github repository also includes a number of examples.

  3. Tutorial

    A more in-depth example implementing a simple application for drawing graphs can be found here.

Features

Widgets

The following widgets are currently implemented. Widgets which are marked as ticklish_ui additions are not part of the standard tkinter/tkinter.ttk widgets sets and may have additional attributes and behaviours in addition to those provided by the base widget.

`ticklish_ui` name Base widget `ticklish_ui` addition
Application tkinter.Tk yes
Button tkinter.ttk.Button no
Canvas tkinter.Canvas no
CheckGroup tkinter.ttk.Frame yes
Checkbutton tkinter.ttk.Checkbutton no
CloseButton tkinter.ttk.Button yes
Combobox tkinter.ttk.Combobox no
Dropdown tkinter.ttk.Combobox yes\*
Entry tkinter.ttk.Entry no
Frame tkinter.ttk.Frame no
Label tkinter.ttk.Label no
LabelFrame tkinter.ttk.LabelFrame no
Listbox tkinter.ttk.Treeview yes\*
Notebook tkinter.ttk.Notebook no
PanedWindow tkinter.ttk.PanedWindow no
Progressbar tkinter.ttk.Progressbar no
RadioGroup tkinter.ttk.Frame yes
Radiobutton tkinter.ttk.Radiobutton no
Scale tkinter.ttk.Scale no
Scrollable tkinter.Canvas yes
Scrollbar tkinter.ttk.Scrollbar no
Separator tkinter.ttk.Separator no
Text tkinter.Text no
Toplevel tkinter.Toplevel no
Treeview tkinter.ttk.Treeview no

*These widgets are additions in the sense that they use specific settings to get a particular default behaviour but are otherwise just wrappers around the base widget.

Eventually ticklish will provide wrappers out-of-the-box for all tkinter and tkinter.ttk widgets. Users can implement or wrap additional widgets by subclassing the WidgetFactory or ContainerFactory classes as needed.

Streams

Ticklish provides a very simple Stream construct. Data can be inserted into a stream and will be acted on automatically before being passed to any child streams if they exist. Child streams are created by filtering and mapping existing streams. Filtering determines what data is allowed into the stream; mapping, how the data is handled and/or transformed.

Here's a quick example:

from ticklish_ui.events import Stream

base = Stream()

odd_stream = base.filter(lambda n: n % 2 == 1).map(lambda n: print(f'odd: {n}'))
even_stream = base.filter(lambda n: n % 2 == 0).map(lambda n: print(f'even: {n}'))

base.insert(1)
base.insert(2)
base.insert(3)
base.insert(4)
base.insert(5)
base.insert(6)
base.insert(7)
base.insert(8)
base.insert(9)
base.insert(10)

RESULTS:

odd: 1
even: 2
odd: 3
even: 4
odd: 5
even: 6
odd: 7
even: 8
odd: 9
even: 10

Note that, although data is being inserted into the base stream, it's the child streams — odd_stream and even_stream — which are doing the actual work. If either of the mapped functions returned a value then further filtering and mapping could be done creating a whole pipeline of actions to be carried out automatically any time a value is inserted into the base stream.

The EventStream class provides default filters for dealing specifically with tkinter events — filtering by the name of the widget involved, for instance — but is otherwise just a regular stream.

Streams allow program authors to handle normal data and user generated events in similar ways but are entirely optional.

Future Development

On the todo list in no particular order:

  • Allow merging streams
  • Implement the rest of the tkinter and tkinter.ttk widgets
  • Add a way to declaratively define grid layouts

Contributing

For detailed information on contributing to ticklish_ui see CONTRIBUTING.org on github.

License

ticklish_ui is free software licensed under the BSD-3-Clause License.

Changes

v0.3.0 (2021-03-13)

Chore

  • Adds CHANGES.md to long_description for pypi
  • Updates README.org with newly added widgets

Docs

  • Adds a more complex application menu example
  • Adds Treeview example
  • Adds an example for the Separator widget

Feat

  • Adds Treeview widget
  • Changes menubar() to work with MenuSpecification objects
  • Adds MenuSpecification class for defining hierarchical menus.
  • Adds the Separator widget

Fix

  • Fixes Application.menubar() so nested menu entries are named properly

v0.2.0 (2021-03-02)

Chore

  • Update to version 0.2.0
  • updates .gitignore

Docs

  • Adds PanedWindow example
  • Adds an example of Notebook
  • Adds two examples using Scrollable widgets
  • Adds tags_example.py
  • Adds an example of the Text widget
  • Adds an example for scale widgets
  • Adds an example demonstrating virtual event user data.

Feat

  • Adds PanedWindow widget
  • Adds Notebook widget
  • Adds Scrollbar widget and Scrollable container
  • Adds tags option to all widgets
  • Adds wrapper class for the Text widget
  • Adds Scale widget
  • Adds progressbar widget
  • Allows user data with virtual events in event streams.

Fix

  • Makes sure tags are applied to containers and not just contents

v0.1.3 (2021-02-21)

v0.1.2 (2021-02-21)

v0.1.1 (2021-02-13)

v0.1.0 (2021-02-13)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ticklish_ui-0.3.0.tar.gz (24.2 kB view hashes)

Uploaded Source

Built Distribution

ticklish_ui-0.3.0-py3-none-any.whl (46.9 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page