First Tkinter script: requesting comments

B

Bart Kastermans

I wrote a first script using Tkinter. As I am new to its
use, I am only just feeling my way around. I would very
much like comments on the design of the script (and in fact
any other comments on my code would also be very welcome).

I have it posted (with syntax coloring) at:

http://kasterma.wordpress.com/2010/05/21/first-experiments-with-tkinter/

But will also include it here for convenience.

Thanks for any help,

Best,
Bart

***********************

#!/usr/bin/env python
#
# Getting a list of students and grades displayed so that grades can
# be updated, and we poll these changes (so that in the future we can
# act on it).
#
# Bart Kastermans, www.bartk.nl

"""
Design of the window

+-------------------------------------------------------------+
| root |
| +------------------------------------------------------+ |
| | title_frame | |
| | +------------------------------+ | |
| | | Label(title) | | |
| | | | | |
| | +------------------------------+ | |
| +------------------------------------------------------+ |
| +------------------------------------------------------+ |
| | exam_grades_frames | |
| | +-------------------------------------------------+ | |
| | | Frame(ex) | | |
| | | +--------------------+ +---------------------+ | | |
| | | | Entry(name) | | Entry(grade) | | | |
| | | | | | | | | |
| | | +--------------------+ +---------------------+ | | |
| | +-------------------------------------------------+ | |
| | | |
| +------------------------------------------------------+ |
| |
| +---------------------+ |
| | quit_button | |
| | | |
| +---------------------+ |
+-------------------------------------------------------------+

"""



from Tkinter import *

# global info for this specific example

# four students
no_stud = 4
exam_grades = [1,2,3,4]
names = ["Ben", "Jim", "James", "Mel"]
# upper bound for name length
max_name_len = max (map (len, names))

# set up root window
root = Tk()
root.geometry ("400x400")

exam_grades_string = map (lambda x: StringVar (root,str (x)), exam_grades)

names_string = map (lambda x: StringVar (root, x), names)

def setup ():
""" setup the window with the list of students.

This is test-code to figure out what the app finally should look
like.
"""

# title frame, with title Grade Correction in it
title_frame = Frame(root)
title_frame.pack (fill=X)

w = Label (title_frame, text = "Grade Correction", font = ("Helvetica", 25))
w.pack (side=LEFT)

# from to hold the list of grades
exam_grades_frame = Frame (root)
exam_grades_frame.pack (fill=BOTH)

exam_label = Label (exam_grades_frame, text="EXAMS")
exam_label.pack ()

# set up the list of grades
for i in range (0,no_stud):
# a frame per student
ex = Frame (exam_grades_frame)
ex.pack ()
# name on the left
name = Entry (ex, textvariable=names_string, width=max_name_len+2)
name.config (state=DISABLED)
name.pack (side=LEFT)
# grade next to it
grade = Entry (ex, textvariable=exam_grades_string , width=4)
grade.pack (side=LEFT)

# button to quit the application
qb = Button (root)
qb ['text'] = "quit"
qb ['command'] = root.quit
qb.pack ()


def to_int (st):
""" helper function to convert strings to integers.

Empty string represents 0.
"""
if len (st) == 0:
return 0
else:
return int (st)

def get_curr_grades ():
""" extract the grades from exam_grades_string.

exam_grades_string consists of StringVar that get updated when the
fields are updated in the GUI.
"""
grades = []
for i in range (0, no_stud):
grades.append (exam_grades_string .get())
return grades

# get the current grades
curr_grades = map (to_int, get_curr_grades ())

def poll_exams ():
""" function that keeps polling the current grades, looking for changes"""
global curr_grades
new_grades = map (to_int, get_curr_grades ())
if new_grades != curr_grades:
print new_grades
curr_grades = new_grades
root.after( 250, poll_exams)

# window setup
setup ()

# start the polling
poll_exams ()

# start the eventloop
root.mainloop ()

# do cleanup
root.destroy ()
sys.exit ()
 
F

Francesco Bochicchio

I wrote a first script using Tkinter.  As I am new to its
use, I am only just feeling my way around.  I would very
much like comments on the design of the script (and in fact
any other comments on my code would also be very welcome).

I have it posted (with syntax coloring) at:

http://kasterma.wordpress.com/2010/05/21/first-experiments-with-tkinter/

But will also include it here for convenience.

Thanks for any help,

Best,
Bart

***********************

#!/usr/bin/env python
#
# Getting a list of students and grades displayed so that grades can
# be updated, and we poll these changes (so that in the future we can
# act on it).
#
# Bart Kastermans,www.bartk.nl

"""
Design of the window

      +-------------------------------------------------------------+
      |  root                                                       |
      |  +------------------------------------------------------+   |
      |  | title_frame                                          |   |
      |  |  +------------------------------+                    |   |
      |  |  | Label(title)                 |                    |   |
      |  |  |                              |                    |   |
      |  |  +------------------------------+                    |   |
      |  +------------------------------------------------------+   |
      |  +------------------------------------------------------+   |
      |  | exam_grades_frames                                   |   |
      |  |  +-------------------------------------------------+ |   |
      |  |  | Frame(ex)                                       | |   |
      |  |  | +--------------------+  +---------------------+ | |   |
      |  |  | | Entry(name)        |  | Entry(grade)        | | |   |
      |  |  | |                    |  |                     | | |   |
      |  |  | +--------------------+  +---------------------+ | |   |
      |  |  +-------------------------------------------------+ |   |
      |  |                                                      |   |
      |  +------------------------------------------------------+   |
      |                                                             |
      |                 +---------------------+                     |
      |                 | quit_button         |                     |
      |                 |                     |                     |
      |                 +---------------------+                     |
      +-------------------------------------------------------------+

"""

from Tkinter import *

# global info for this specific example

# four students
no_stud = 4
exam_grades = [1,2,3,4]
names = ["Ben", "Jim", "James", "Mel"]
# upper bound for name length
max_name_len = max (map (len, names))

# set up root window
root = Tk()
root.geometry ("400x400")

exam_grades_string = map (lambda x: StringVar (root,str (x)), exam_grades)

names_string = map (lambda x: StringVar (root, x), names)

def setup ():
    """ setup the window with the list of students.

    This is test-code to figure out what the app finally should look
    like.
    """

    # title frame, with title Grade Correction in it
    title_frame = Frame(root)
    title_frame.pack (fill=X)

    w = Label (title_frame, text = "Grade Correction", font = ("Helvetica", 25))
    w.pack (side=LEFT)

    # from to hold the list of grades
    exam_grades_frame = Frame (root)
    exam_grades_frame.pack (fill=BOTH)

    exam_label = Label (exam_grades_frame, text="EXAMS")
    exam_label.pack ()

    # set up the list of grades
    for i in range (0,no_stud):
        # a frame per student
        ex = Frame (exam_grades_frame)
        ex.pack ()
        # name on the left
        name = Entry (ex, textvariable=names_string, width=max_name_len+2)
        name.config (state=DISABLED)
        name.pack (side=LEFT)
        # grade next to it
        grade = Entry (ex, textvariable=exam_grades_string , width=4)
        grade.pack (side=LEFT)

    # button to quit the application
    qb = Button (root)
    qb ['text'] = "quit"
    qb ['command'] = root.quit
    qb.pack ()

def to_int (st):
    """ helper function to convert strings to integers.

    Empty string represents 0.
    """
    if len (st) == 0:
        return 0
    else:
        return int (st)

def get_curr_grades ():
    """ extract the grades from exam_grades_string.

    exam_grades_string consists of StringVar that get updated when the
    fields are updated in the GUI.
    """
    grades = []
    for i in range (0, no_stud):
        grades.append (exam_grades_string .get())
    return grades

# get the current grades
curr_grades = map (to_int, get_curr_grades ())

def poll_exams ():
    """ function that keeps polling the current grades, looking for changes"""
    global curr_grades
    new_grades = map (to_int, get_curr_grades ())
    if new_grades != curr_grades:
        print new_grades
        curr_grades = new_grades
    root.after( 250, poll_exams)

# window setup
setup ()

# start the polling
poll_exams ()

# start the eventloop
root.mainloop ()

# do cleanup
root.destroy ()
sys.exit ()


It looks fine to me, except that I would do it a bit more OOP-style
(can't stand the globals very much).

Also, I would use labels instead of disabled text fields for student
names, since they don't need to be edited.
Add maybe tray to align the name and grade widgets in a 2x4 matrix
using .grid() instead of .pack()
( see http://effbot.org/tkinterbook/grid.htm )

One thing I don't understand, is why you need to 'poll' continuously
for changes, except for demo purpose.
If this is supposed to be a window for user to enter data into the
program, the standard practice is to have a
separate window (not the main one), with apply and cancel buttons,
where both buttons close the window but only
the apply button register the change. So you need to do the stuff you
do in the poll_exams function only in the
callback of the apply button.

On the python side, you don't need no_stud variable since you can do
len(names_string). And maybe have single list
of (name, grade) pairs, so youl loop would become something like:
for i, (name, grade) in enumerate( names_and_grades_list ) :
# use name instead of names_string
# use grade instead of exam_grades_string

Although this could slightly complicate the upgrade of grades. The
usage of a small class StudentData could be in order, too.




Ciao
 
P

Peter Otten

Francesco said:
One thing I don't understand, is why you need to 'poll' continuously
for changes, except for demo purpose.

You can give the user immediate feedback on changes he makes. I'd argue that
e. g. an "auto-apply" dialog with a revert button is much more usable and am
surprised that this style hasn't caught on.
If this is supposed to be a window for user to enter data into the
program, the standard practice is to have a
separate window (not the main one), with apply and cancel buttons,
where both buttons close the window but only
the apply button register the change. So you need to do the stuff you
do in the poll_exams function only in the
callback of the apply button.

If you want live updates you can also use StringVar.trace() instead of
polling:

from functools import partial

def grade_changed(name, index, var, *args):
new_grade = to_int(var.get())
if new_grade != exam_grades[index]:
exam_grades[index] = new_grade
print name, "-->", new_grade

pairs = zip(names, exam_grades_string)
for index, (name, var) in enumerate(pairs):
var.trace("w", partial(grade_changed, name, index, var))

See also http://effbot.org/tkinterbook/variable.htm

Peter
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,766
Messages
2,569,569
Members
45,043
Latest member
CannalabsCBDReview

Latest Threads

Top