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. Doset_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 passevent=True
, the callback will be called likecallback(shifted, event)
; that is, theshifted
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 raisesValueError
, 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, orNone
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 arange
. Ifinitial_value
is given, it must be inallowed_values
. If it’s not,allowed_values[0]
is used.This returns an integer in
allowed_values
, orNone
if the user cancels.
links¶
With this extra, you can insert web browser style links to
Text
widgets. This is based on
this tutorial that is
almost as old as I am, but it’s still usable.
See examples/links.py for example code.
-
teek.extras.links.
add_url_link
(textwidget, url, start, end)[source]¶ Make some of the text in the textwidget to be clickable so that clicking it will open
url
.The text between the text indexes
start
andend
becomes clickable, and rest of the text is not touched.Do this if you want to insert some text and make it a link immediately:
from teek.extras import links ... old_end = textwidget.end # adding text to end changes textwidget.end textwidget.insert(textwidget.end, 'Click me') links.add_url_link(textwidget, 'https://example.com/', old_end, textwidget.end)
This function uses
webbrowser.open()
for openingurl
.
-
teek.extras.links.
add_function_link
(textwidget, function, start, end)[source]¶ Like
add_url_link()
, but calls a function instead of opening a URL in a web browser.
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. SubclassSoupViewer
and overridedownload()
if you don’t want that.Images are loaded in threads, so make sure to use
teek.init_threads()
. Alternatively, you can passthreads=False
, and the images won’t be loaded at all.-
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()
orhandle_p()
. Those methods run when an element with the corresponding tag name is added. You can subclassSoupViewer
and create more of these methods to handle more different kinds of tags. There are two things that the methods can do: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')
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 increate_tags()
.
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, thealt
of the<img>
will be displayed instead of the actual image, if there is analt
. Thealt
is also displayed while this method is running.By default, this uses
urllib.request.urlopen()
. You can override this ifurllib
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 theadd_soup()
call.If
cleanup
isTrue
, 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 passcleanup=False
, you should clear the text widget after calling this method.This is automatically called with
cleanup=True
when thewidget
is destroyed.
-