Teek Tutorial¶
If you have never written any teek code before, you came to the right place. You don’t need to have any tkinter experience in order to learn teek, although learning teek will be easier if you have used tkinter in the past.
Note
If you have any trouble at all following this tutorial, let me know and I’ll try to make this tutorial better. You can contact me for other things as well, but I’m especially interested in making this tutorial beginner-friendly.
Tkinter, _tkinter, Tcl, Tk and teek¶
You need to install tkinter in order to install teek. That’s correct, teek is an alternative to tkinter that needs tkinter. This section explains why.
When we say tkinter, we really mean a combination of two things that cannot be installed separately:
- The
_tkinter
module that lets you run a Tcl interpreter within Python. Tcl is a programming language that can be used without_tkinter
. - The pure-Python
tkinter
module uses_tkinter
. When you do something likeroot = tkinter.Tk()
,tkinter
starts a Tcl interpreter using_tkinter
. Tkinter widgets are just classes written in pure Python that run commands in the Tcl interpreter; to be more precise, they use a Tcl library called Tk.
Teek is an alternative to the pure-Python part; it uses a Tcl interpreter
with _tkinter
, just like tkinter
, but it’s much nicer to use in several
ways as shown in the README.
tl;dr
Tcl is a programming language with a nice GUI library named Tk.
Tkinter and Teek both use Tcl and Tk with _tkinter
.
Installing Teek¶
Install Python 3.4 or newer and tkinter (yes, you need to have tkinter installed, there are more details about this above). Then run this:
python3 -m pip install --user teek
Use py
instead of python3
if you are on Windows.
Hello World!¶
import teek
window = teek.Window("Hello World")
label = teek.Label(window, "Hello World!")
label.pack()
window.on_delete_window.connect(teek.quit)
teek.run()
Run the program. It displays a tiny Hello World greeting.
Let’s go through the program line by line.
import teek
This imports teek. If you get an ImportError
or a
ModuleNotFoundError
from this step, make sure that you installed teek
as explained above.
You can also import teek as tk
and use tk.Label
instead of
teek.Label
, but then people reading your code will see tk.Label
there
and think that it’s probably a tkinter label, because it’s quite common to
import tkinter as tk
. teek.Label
is not much more work to type than
tk.Label
anyway (unlike tkinter.Label
), so I recommend just
import teek
.
Don’t do from teek import *
because that confuses both tools that
process code automatically, and people who read your code.
teek.Label(parent_widget, "hello")
obviously creates a teek label, but
Label(parent_widget, "hello")
creates a label. What is a label? If you have
many star imports…
# this code is BAD!! DONT DO THIS!! NO!!!
from teek import *
from os import *
from sys import *
…and you need to find out where Label
comes from, you have many things
that it might be coming from; if os
or sys
had something called
Label
, it would replace teek’s Label
. Everyone reading this code
need to know that neither os
nor sys
has anything named Label
,
which is bad. Also, if Python developers decide to add something called
Label
to os
or sys
, your code will break.
window = teek.Window("Hello World")
This creates a Window
widget with title "Hello World"
. A widget is
an element of the GUI.
label = teek.Label(window, "Hello World!")
Many widgets need to go into another widget. Label
is a widget that
displays text, and this line of code puts it in our window
. The widget that
the label goes in is called the parent or the master widget of the
label. Similarly, the label is said to be a child or slave of the
window.
label.pack()
If you create a label into the window, it isn’t displayed automatically. This line of code displays it.
Creating a child widget and displaying it in the parent are two separate things because this way you can choose how the widget shows up. There’s more information about this below.
window.on_delete_window.connect(teek.quit)
This line tells teek to run teek.quit()
when the window is closed. By
default, nothing happens when the user tries to close the window. You can
connect it to any other function or method as well, which is useful for things
like “Do you want to save your changes” dialogs.
teek.run()
The code before this runs for just a fraction of a second, but this line of code stays running until we close the window. That’s usually something between a few seconds and a few hours.
Note that instead of this…
label = teek.Label(window, "Hello World")
label.pack()
…we can also do this…
teek.Label(window, "Hello World").pack()
…because we create the variable once, and only use it once. However, this doesn’t work:
label = teek.Label(window, "Hello World").pack() # WRONG! common mistake
Look carefully: this does not set the label
variable to a label; it sets
that variable to what ever the_actual_label_widget.pack()
returns, which is
not same as the label widget itself. If you need to do more than one thing to
a widget, set that widget to a variable and do all the things to that variable.
Options¶
Widget options can be used to change how widgets look and behave. For example,
the text of a label is in an option named text
.
>>> window = teek.Window()
>>> label = teek.Label(window, "blah blah")
>>> label.config['text']
'blah blah'
The only way to check the value of an option is label.config['text']
, but
you can set values of options in several ways:
- You can change the text after creating the label like
label.config['text'] = "new text"
. The label will display the new text automatically. - When creating the label, you can pass options to it like
teek.Label(window, text="blah blah")
. Some common options can also be used without passing the option name explicitly withtext=
, e.g.teek.Label(window, "blah blah")
. This is widget-specific, and it’s documented in teek’s documentation; for example, this label thing is documented inLabel
docs.
Sometimes the name of a widget option happens to be a reserved keyword in
Python. For example, in
is not a valid Python variable name because it’s
used in things like 'hello' in 'hello world'
:
>>> in = 'lol'
Traceback (most recent call last):
...
SyntaxError: invalid syntax
>>> label.pack(in=window)
Traceback (most recent call last):
...
SyntaxError: invalid syntax
To avoid this problem, you can use in_
instead of in
, and teek will
handle it correctly:
>>> in_ = 'lol'
>>> in_
'lol'
>>> label.pack(in_=window)
Teek strips the last _
before it does anything with the option.
Tkinter Instructions¶
Tkinter is very popular, so if you want to know how to do something in tkinter,
you can just google it. For example, if you want to change the text of a label
after creating it, google “tkinter change label text” and you’ll find a
stackoverflow answer that does some_label['text'] = 'new text'
and
some_label.config(text='new text')
. Neither of those works in teek, but
both of them give errors with good messages that tell you what you need
to do instead.
Sometimes teek and tkinter differ a lot more, and teek can’t detect too tkintery ways to do things and give you particularly good errors. In these cases, use teek’s tkinter porting guide.
Manual Pages¶
Note
This section assumes that you know the Tcl stuff explained above.
Sometimes stackoverflow answers don’t contain the best possible solution because they are written by noobs who don’t actually know Tk and tkinter very well. I see this quite often. Some of the people who answer tkinter questions on stackoverflow have 20+ years of Tk experience, but most answerers don’t.
If you don’t want to rely on stackoverflow or you want to do things like experienced Tk programmers do things, you should read Tk’s manual pages. They are written for Tcl users and Tcl’s syntax is quite different from Python syntax, so you will probably be somewhat confused at first. For example, let’s say that you don’t know how to change the text of a label after creating it. Figure it out like this:
- Go to teek’s label documentation by clicking this
Label
link. This tutorial and rest of teek’s documentation are full of these links. Click them. - The
Label
link doesn’t say anything about changing the text afterwards, but it has a link to a manual page. Click it. - In the manual page, press Ctrl+F and search for “text”. You’ll find a widget
option whose “Command-Line Name” is
-text
. The leading-
is common in Tcl syntax, but we won’t need it in teek. So all we really need to do is to change the'text'
widget option as shown above. We found what we were looking for.
BTW
The manual page names are like ttk_label(3tk) or after(3tcl).
GUI things have 3tk
manual pages, and things documented in 3tcl
manual pages can be also used in Tcl programs that don’t have a GUI.
If you use Linux, you can also install the manual pages on your system and read them without a web browser. For example, this command installs them on ubuntu:
sudo apt install tcl8.6-doc tk8.6-doc
The 8.6
makes sure that you get newest manual pages available. After
installing the manual pages, you can read them like this:
man ttk_label
You can close the manual page by pressing q like quit. If you want to search,
Ctrl+F won’t work, but instead you can type /text
followed by enter to
search for text
. All matches will be highlighted, and you can press n like
next to go to the next match.
Buttons and callback functions¶
This code displays a button. Clicking the button runs the on_click()
function.
import teek
def on_click():
print("You clicked me!")
window = teek.Window("Button Example")
button = teek.Button(window, "Click me", command=on_click)
button.pack()
window.on_delete_window.connect(teek.quit)
teek.run()
Most of the code isn’t very different from our label example. Let’s go through the things that are different.
def on_click():
print("You clicked me!")
This defines a function. If you have never defined functions before, you should definitely learn that before continuing with this tutorial. It’ll make everything a lot easier. I have written more about defining functions here.
button = teek.Button(window, "Click me", command=on_click)
Button
takes a parent widget and a text, just like Label
,
but Button
also takes a function that is called when the button is
clicked. Read that sentence carefully: Button
takes a function.
This is a common mistake:
button = teek.Button(..., command=on_click()) # ummm... it doesn't work like this!!
command=on_click()
does not do what was intended here;
command=on_click()
calls the on_click
function because it has ()
after on_click
, and when on_click
has been called, it creates the
button and passes the return value of on_click
to it. Be careful to pass
the function itself without calling it.
BTW
Teek lets you omit the command=
part when creating buttons if you
put the button text before the command, so this…
button = teek.Button(window, "Click me", on_click)
…does the same thing as this:
button = teek.Button(window, "Click me", command=on_click)
Here is another common mistake:
import time
def on_click():
print("Doing something...")
time.sleep(5)
print("Done")
Here time.sleep(5)
waits for 5 seconds. If you click the button now, the
GUI will be frozen for 5 seconds. The button will look like it’s pressed down,
and you can’t even close the window! This is bad, and that’s why button
callbacks must not do anything that takes longer than a tiny fraction of a
second. See concurrency documentation if you need a button
callback that runs for a long time.
Multiple child widgets in same parent¶
It’s possible to put several different widgets into the same parent window with
pack()
, like this:
import teek
window = teek.Window("Pack Example")
teek.Label(window, "One").pack()
teek.Label(window, "Two").pack()
window.on_delete_window.connect(teek.quit)
teek.run()
The “Two” label will show up below the “One” label. If you don’t want that, you can also put the labels next to each other:
teek.Label(window, "One").pack(side='left')
teek.Label(window, "Two").pack(side='left')
That’s correct, both of them have side='left'
. This means that the first
widget goes all the way to the left edge, and the second goes to the right of
that, and so on, so the widgets get stacked to the left edge. The default is
side='top'
, and that’s why the widgets ended up below each other.
If you need more complex layouts, you can create a Frame
and pack it,
and add more widgets inside that Frame
, like this:
import teek
window = teek.Window("Pack Example")
big_frame = teek.Frame(window)
big_frame.pack(fill='both', expand=True)
teek.Label(big_frame, text="Left").pack(side='left', fill='both', expand=True)
teek.Label(big_frame, text="Right").pack(side='left', fill='both', expand=True)
status_bar = teek.Label(window, "This is a status bar")
status_bar.pack(fill='x')
window.geometry(300, 200)
window.on_delete_window.connect(teek.quit)
teek.run()
This example uses plenty of things from pack(3tk), but you know how to read manual pages, so you can figure out how it all works. If that isn’t true at all, keep reading, and I’ll explain how some of the things in the example work.
big_frame.pack(fill='both', expand=True)
This packs the big frame so that it stretches if you resize the window, and it fills as much space as possible. If you don’t want to learn everything about pack, learn at least this “idiom”.
status_bar.pack(fill='x')
This makes the status bar fill all the space it has horizontally.
Mathematicians like to call it the x
direction. Use fill='y'
to fill
vertically, or fill='both'
to fill in both x
and y
directions.
window.geometry(300, 200)
This call to geometry()
resizes the window so that it’s bigger
by default, and you can see how the widgets got laid out without first making
the window bigger.
What now?¶
You are ready for creating a project in teek! All parts of teek’s documentation are listed at left, but here are the things you will most likely need next: