Tuesday, January 5, 2016

How to write programs that run in Tkinter

There are thousands and thousands of tutorials and discussion threads about Tkinter (and by extension Tcl/Tk), but very few of them seem to address the proper approach to writing a useful program that uses Tkinter. It is easy to think first about the code you want to write to accomplish whatever task your program is supposed to do and work later on whatever GUI you'll build for your user interface. This approach however always results for me in hitting a big wall when I start coding up the GUI. That's because Tkinter (and by extension, etc) is not naitively multi threaded. It needs to either be the only thing running, or be the only thing running in a special sandbox that you make for it by implementing your own threading. It is often unhelpfully pointed out in forum threads that Tkinter is event driven. What this is meant to say is that you can't just write linear code and call routines that do Tkinter stuff; the code that you write can only consist of responses to events in your Tkinter GUI. Essentially, Tkinter is the language of whatever program you are writing; it just happens to have pythonic syntax. It's sort of like how C++ has C-like syntax. This seems terribly unfair at first, but after you decide to do it Tkinter's way it turns out to be not so bad. I've written GUIs using both methods, but these day's I'm finding it easiest to let my programs be Tkinter programs instead of Python programs that use Tkinter.

The breakthrough for me was an example that I found that was structured like an executive loop, although it wasn't presented as such by it's author. What was happening in this example was that the __init__ method was calling a start() method that then called a function that ran some code and then called itself again after a short wait using after(). mainloop() then serviced each of the calls of the executive function as well as the rest of the GUI. Here is that example:


So, the structure of the python __main__ routine for a useful Tkinter program needs to be:

1. define your top-level GUI object, which will call it's __init__. In that __init__ you build all the objects in your control screen and define its appearance.
2. If you don't do it in your __init__ method, you can call your GUI's start() function from your main python routine. But it won't run because mainloop() isn't running yet.
3. call your GUI's mainloop(). Now your start() function will run. It will need to do something that causes itself or another function to be scheduled to be run by mainloop(). The best thing to do here is to have an executive function that functions like a state machine; each time the executive is entered, it checks what the current state is and executes the code for that state, and decides whether to change the state variable before rescheduling itself.
4. Somewhere in your GUI's functions find a place to put a call to your GUI's quit() function, which will exit mainloop().
5. Now you're back in your python main function, where you can either do something else or exit.

Since most programs are basically linear, it is very easy to break them up into state machine pieces. One could even argue that all programs are state machines to begin with.

Here are some of the links that helped me figure this out:

Presently my go-to link that helped me get the idea:

http://stackoverflow.com/questions/29158220/tkinter-understanding-mainloop


From the above link, another example of the ball game with some better defined executive loop examples:

http://stackoverflow.com/questions/25430786/moving-balls-in-tkinter-canvas/25431690#25431690


http://stackoverflow.com/questions/8683217/when-do-i-need-to-call-mainloop-in-a-tkinter-application

https://www.reddit.com/r/learnpython/comments/2rpk0k/how_to_update_your_gui_in_tkinter_after_using/

This one has great discussions about both the threading and executive loop methods:

http://stupidpythonideas.blogspot.com/2013/10/why-your-gui-app-freezes.html


This question and answer sort of addressed the underlying Tkinter philosophy that is part of this topic:

http://programmers.stackexchange.com/questions/213935/why-use-classes-when-programming-a-tkinter-gui-in-python


A google search which brings more good answers of this sort:

https://www.google.com/#q=python+tkinter+define+code+to+be+run+with+mainloop()+is+called


One other tip that is helpful is that it's possible to call update() on your main screen from within the executive loop function to make things appear and move on the GUI while code is executing in the executive loop. In my example, one of my states has a loop of data processing that I want to show progress on; I can set the progress bar value and call update() for each iteration of the loop.



python case statement

The google search that I did because I was sure I'd seen a "case" type statement for python told me that I was wrong, there is no "case" or "switch" for python. Here is a very interesting musing on the question of why this would be:

http://www.pydanny.com/why-doesnt-python-have-switch-case.html

Python Tkinter layout managers

As I am becoming better at using Tkinter, I am gaining a better appreciation for how to use the different layout managers. It turns out that there are three of them, of which I currently most like to use "pack" and "grid" (the third being "absolute", which is both less portable and less elegant than the other two). Here are some bookmarks to the information that I'm making the most use of.

First, some good general tutorial pages:
http://zetcode.com/gui/tkinter/layout/

About pack():

What I have learned is that it's a very quick method of building up display panels using a paradigm of block stacking. You have to define objects, then you place objects within objects in stacks. If you don't use a "side" argument, whatever object you pack gets stacked under the previous objects. A string of pack() commands with "side" arguments get placed next to each other at the same level. Objects can be stretched to touch sides on one or both axes of the object they've been packed into. It's nuts primitive but so easy and quick.

Here is a description of the function with its parameters and a couple of go-to examples:

http://effbot.org/tkinterbook/pack.htm


some of the same examples as in the above link but more nicely presented:

http://www.python-course.eu/tkinter_layout_management.php


grid() is like doing HTML tables, very intuitive and handles resizing well.





Friday, January 1, 2016

Python Tkinter destroy widget

For my status screen, I'd like to have some things come and go depending on what's happening. No problem; in Tkinter each widget has a destroy function. This was the first example I found; although the question in this forum thread is about how to make destroy() work, the actual problem was an interesting reminder of the importance of watching one's scope when assigning the results of a widget init!

http://stackoverflow.com/questions/17673662/tkinter-removing-a-button-from-a-running-program

While working on this feature I also leaned that depending on the layout manager being used and how the frame was defined, using destroy() could result in resizing of the frame. This tutorial covers layout managers excellently:

http://zetcode.com/gui/tkinter/layout/

Python tkinter progress bar

Google keeps bringing me the same four examples for how to do a tkinter progress bar. It turns out that one of them is good in that a commenter showed how to implement a progress bar screen as a class which is how my entire project is constructed. Here's the link:

http://stackoverflow.com/questions/7310511/how-to-create-downloading-progress-bar-in-ttk

I reformatted the commenter's very helpful example to make the window quit when 100% is reached. I had to feel my way around how to add the quit and the destroy; I tried making a root object from the launch of Tk() and using it's quit and destroy, but for some reason that didn't work like it did in my other display classes. So in the end, here is my slightly modified version of the original program:

import Tkinter as tk
import ttk


class SampleApp(tk.Tk):

def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
##root = self.root = tk.Tk()
##root.title('root title')
self.button = ttk.Button(text="start", command=self.start)
self.button.pack()
self.progress = ttk.Progressbar(self, orient="horizontal",
length=200, mode="determinate")
self.progress.pack()

self.bytes = 0
self.maxbytes = 0
def start(self):
self.progress["value"] = 0
self.maxbytes = 50000
self.progress["maximum"] = 50000
self.read_bytes()
def read_bytes(self):
'''simulate reading 500 bytes; update progress bar'''
self.bytes += 500
self.progress["value"] = self.bytes
if self.bytes < self.maxbytes:

# read more bytes after 100 ms
self.after(100, self.read_bytes)
else:
self.quit()
app = SampleApp()
app.mainloop()
app.destroy()

Python Tkinter Label widget set font size and bold without setting font

For user feedback, some kind of text in a frame that can be updated to show status is essential. It turns out that the Label widget is set up to do this naitively. Just define a text variable type and give the name of that variable during the setup of the Label widget and any changes to the text variable automatically show up in the widget. Pretty basic. The second-to-last example in this amazing tutorial demonstrate this with a slider that returns values and a label which is set up to dynamically display whatever the value of the slider is:

http://zetcode.com/gui/tkinter/widgets/

Basically, this entire site's tutorial is incredible and finally something pertinent to what I am doing:

http://zetcode.com/gui/tkinter/

The thing that I found out about which is more interesting to me is how to format the font in the Label widget. I personally didn't care to change the font itself from whatever was the system's default, but I wanted to make it bigger and make it bold. It turns out that this is also no problem; for the "font" specification you just omit specifying the font itself and just specify the size and weight, as in these links:

http://stackoverflow.com/questions/4072150/how-to-change-a-widgets-font-style-without-knowing-the-widgets-font-family-siz

http://effbot.org/tkinterbook/label.htm

So, the code that I made looks like this:

self.var = StringVar()
self.label = Label(self, text=0, textvariable=self.var, font="-size 20 -weight bold")

Tuesday, December 29, 2015

Python split line by tab or multiple spaces

During the process of trying to find the best way to pull out all the tokens from a tab or space delimited line no matter how they are separated, I found an awesome answer but also learned a few things.

It turns out that python has a "regular expression" methods library re. re has a split function and you don't have to give it both tab and space characters, \s+ is all you need The \s apparently covers all white space characters and + includes any combination of them. To wit:

line = 'one\ttwo\t\tthree four \tfive \t\tsix'
re.split('\s+', line)
['one', 'two', 'three', 'four', 'five', 'six']

(source: http://stackoverflow.com/questions/8113782/split-string-on-whitespace-in-python)

Also learned: This link shows that | is used to delimit multiple split options in the same re:
http://stackoverflow.com/questions/4998629/python-split-string-with-multiple-delimiters