Extras

Big tkinter projects often have their own implementations of some commonly needed things that tkinter doesn’t come with. The teek.extras module contains a collection of them.

To use the extras, import teek is not enough; that doesn’t import any extras. This is good because most teek programs don’t need the extras, and for them, import teek may run a little bit faster if it doesn’t import the extras. Instead, you typically need to do something like this to use the extras:

import teek
from teek.extras import tooltips

# now some code that uses tooltips.set_tooltip

tooltips

This module contains a simple tooltip implementation with teek. There is example code in examples/tooltip.py.

If you have read some of IDLE’s source code (if you haven’t, that’s good; IDLE’s source code is ugly), you might be wondering what this thing has to do with idlelib/tooltip.py. Don’t worry, I didn’t copy/paste any code from idlelib and I didn’t read idlelib while I wrote the tooltip code! Idlelib is awful and I don’t want to use anything from it in my projects.

teek.extras.tooltips.set_tooltip(widget, text)[source]

Create tooltips for a widget.

After calling set_tooltip(some_widget, "hello"), “hello” will be displayed in a small window when the user moves the mouse over the widget and waits for a small period of time. Do set_tooltip(some_widget, None) to get rid of a tooltip.

cross_platform

Most teek things work the same on most platforms, but not everything does. For example, binding <Tab> works everywhere, but binding <Shift-Tab> doesn’t work on Linux and you need a different binding instead. This extra module contains utilities for dealing with things like that.

teek.extras.cross_platform.bind_tab_key(widget, callback, **bind_kwargs)[source]

A cross-platform way to bind Tab and Shift+Tab.

Use this function like this:

from teek.extras import cross_platform

def on_tab(shifted):
    if shifted:
        print("Shift+Tab was pressed")
    else:
        print("Tab was pressed")

cross_platform.bind_tab_key(some_widget, on_tab)

Binding '<Tab>' works on all systems I’ve tried it on, but if you also want to bind tab presses where the shift key is held down, use this function instead.

This function can also take any of the keyword arguments that teek.Widget.bind() takes. If you pass event=True, the callback will be called like callback(shifted, event); that is, the shifted bool is the first argument, and the event object is the second.

more_dialogs

This is useful when teek.dialog is not enough.

All of the functions take these arguments:

  • title will be the title of the dialog.
  • text will be displayed in a label above the text entry or spinbox.
  • initial_value will be added to the entry or spinbox before the user changes it.
  • parent is a window widget that the dialog will be displayed on top of.
teek.extras.more_dialogs.ask_string(title, text, *, validator=<class 'str'>, initial_value='', parent=None)[source]

Displays a dialog that contains a teek.Entry widget.

The validator should be a function that takes a string as an argument, and returns something useful (see below). By default, it returns the string unchanged, which is useful for asking a string from the user. If the validator raises ValueError, the OK button of the dialog is disabled to tell the user that the value they entered is invalid. Then the user needs to enter a valid value or cancel the dialog.

This returns whatever validator returned, or None if the dialog was canceled.

teek.extras.more_dialogs.ask_integer(title, text, allowed_values, *, initial_value=None, parent=None)[source]

Displays a dialog that contains a teek.Spinbox widget.

allowed_values can be a sequence of acceptable integers or a range. If initial_value is given, it must be in allowed_values. If it’s not, allowed_values[0] is used.

This returns an integer in allowed_values, or None if the user cancels.

image_loader

Note

This extra has dependencies that don’t come with teek when you install it with pip install teek or similar, because I want teek to be light-weight and I don’t want to bring in lots of dependencies with it. Run pip install teek[image_loader] to install the things you need for using this extra.

There’s also image_loader_dummy, which is like image_loader except that it just creates teek.Image() objects, and doesn’t support anything that plain teek.Image() doesn’t support. It’s meant to be used like this:

try:
    from teek.extras import image_loader
except ImportError:
    from teek.extras import image_loader_dummy as image_loader

This extra lets you create teek.Image objects of images that Tk itself doesn’t support. It uses other Python libraries like PIL and svglib to do that, and you can just tell it to load an image and let it use whatever libraries are needed.

soup

This extra contains code that views HTML to text widgets. The HTML is taken as BeautifulSoup elements, so you need to have it installed to use this module. Don’t get too excited though – this is not something that’s intended to be used for creating a web browser or something, because this thing doesn’t even support JavaScript or CSS! It’s meant to be used e.g. when you want to display some markup to the user, and you know a library that can convert it to simple HTML.

If the HTML contains images that are not GIF images, make sure to install image_loader. The soup extra will use it if it’s installed.

Only these HTML elements are supported by default (but you can subclass SoupViewer and add support for more elements, see SoupViewer.add_soup()):

  • <h1>, <h2>, <h3>, <h4>, <h5> and <h6>
  • <pre> and <code>
  • <br>
  • <b>, <i>, <em> and <strong>
  • <p>
  • <ul>, <ol> and <li>
  • <a>
  • <img>
class teek.extras.soup.SoupViewer(textwidget, threads=True)[source]

Displays BeautifulSoup HTML elements in a text widget.

Note

If the soup contains <img> tags, the images are read or downloaded automatically by default. Subclass SoupViewer and override download() if you don’t want that.

Images are loaded in threads, so make sure to use teek.init_threads(). Alternatively, you can pass threads=False, and the images won’t be loaded at all.

widget

The teek.Text widget that everything is added to.

add_soup(element)[source]

Render a BeautifulSoup4 HTML element.

The text, images, or whatever the element represents are added to the end of the text widget.

This method looks for methods whose names are handle_ followed by the name of a HTML tag; for example, handle_h1() or handle_p(). Those methods run when an element with the corresponding tag name is added. You can subclass SoupViewer and create more of these methods to handle more different kinds of tags. There are two things that the methods can do:

  1. The method can return None to indicate that add_soup() shouldn’t do anything with the content of the element.

    def handle_pre(self, pre):
        self.widget.insert(self.widget.end, pre.text.rstrip() + '\n\n')
    
  2. The method can be decorated with contextlib.contextmanager(). When it yields, add_soup() will loop over the element and call itself recursively with each subelement.

    @contextlib.contextmanager
    def handle_ul(self, ul):
        for li in ul:
            if li.name == 'li':
                # '\N{bullet} ' creates a Unicode black circle character
                li.insert(0, '\N{bullet} ')
        yield     # the content of the ul is added here
        self.widget.insert(self.widget.end, '\n')
    

In either case, add_soup() adds a textwidget tag as explained in create_tags().

create_tags()[source]

Adds text tags to the widget for displaying the soup elements.

This is not called automatically; you should call this before actually using the SoupViewer.

Each text tag is named with 'soup-' followed by the name of the corresponding HTML tag, such as 'soup-p' or 'soup-pre'. If you are not happy with what this method does, you can change the text tags after calling it.

download(url)[source]

Downloads the content of the URL, and returns it as bytes.

This method is called whenever the soup contains an <img> or something else that has to be read from a file or downloaded. If it raises an exception, the alt of the <img> will be displayed instead of the actual image, if there is an alt. The alt is also displayed while this method is running.

By default, this uses urllib.request.urlopen(). You can override this if urllib is doing something dumb or you want to control which things can be downloaded.

Usually this is called from some other thread than the main thread.

stop_loading(cleanup=True)[source]

Tell currently running threads to do nothing to the widget.

Things like <img> elements are loaded with threads, so they might add something to the text widget several seconds after the add_soup() call.

If cleanup is True, this method also e.g. deletes already loaded images, because then it assumes that they are not needed anymore. This means that if you don’t pass cleanup=False, you should clear the text widget after calling this method.

This is automatically called with cleanup=True when the widget is destroyed.