Tkinter pack Problem
Eric Brunel
eric_brunel at despammed.com
Wed Jul 26 07:58:30 EDT 2006
On Wed, 26 Jul 2006 12:46:39 +0200, H J van Rooyen <mail at microcorp.co.za>
wrote:
> Hi,
>
> I am struggling to get the pack method to do what I intend.
> I am trying to display user input in a seperate window, along with
> a little description of the field, something like this:
>
> Current entry
> Company : entered co. name
> First entry : entered stuff
> The second entry: more entered stuff
> Entry number three : Last entered stuff
You won't be able to do that kind of layout with pack. This is - quite
obviously IMHO - a job for grid, since your widgets are arranged in a grid.
According to my experience, pack should only be used for *very* simple
layouts, such as all widgets in a single row or in a single column. Trying
to do anything else will very soon end up being a nightmare, with unneeded
frames everywhere. Learn to use grid and life will be far easier for you.
[snip]
A few comments on your code:
> #!/usr/bin/python
> # The original of this module is supposed to do an accounting entry -
>
> # we get the gui stuff
>
> from Tkinter import *
>
> class Entryscreen(Frame):
Why do you inherit from Frame? Apparently, you're trying to do a window. A
Frame is not a window; it's a general-purpose container for widgets. If
you want to do a window, inherit from Tk (for a main window) or from
Toplevel (for any other).
> """This screen is used to construct a new Entry."""
>
> # we define the variables we use
>
> des_string = '' # Description of what we want to do
> req_string = '' # Prompt string
> dis_string = '' # Description of field for display
>
> Company = "New Entry Screen" # we dont have a company yet
> Entry_1 = '' # or any entries
> Entry_2 = ''
> Entry_3 = ''
>
> def start_new(self):
> """This is the routine that assembles a new record"""
>
> if self.Company == "New Entry Screen": # if its the first time,
>
> # we make a new window
>
> show = Tk()
No, we don't. We actually create a whole new tcl/tk interpreter
environment, completely independent from the one we already have, which
happens to create a new main window. Doing so is bound to cause problems
later. If you want to create a new window, instantiate Toplevel.
> show.title('Accounting entry display window')
>
> # we get an instance to display results in
>
> self.disp = Showscreen("Current entry",master=show)
>
> # then we ask for the company:
>
> des_string = "Getting the Company "
> req_string = "Enter the Company for this session"
> dis_string = 'Company:'
> error = self.GetEntry(des_string, req_string, dis_string,
> self.disp)
> self.Company = self.Amount
>
> # Then or else we ask for entry details
>
> des_string = "Getting first entry"
> req_string = "Enter first field"
> dis_string = "First entry:"
> error = self.GetEntry(des_string, req_string, dis_string,
> self.disp)
>
> des_string = "Getting second entry"
> req_string = "Enter second field"
> dis_string = "The second entry:"
> error = self.GetEntry(des_string, req_string, dis_string,
> self.disp)
>
> des_string = "Getting third entry"
> req_string = "Enter third field"
> dis_string = "Entry number three:"
> error = self.GetEntry(des_string, req_string, dis_string,
> self.disp)
This is not important, but why do you always create variables for your
strings instead of passing them directly? For example, the previous 4
lines can be written:
error = self.GetEntry("Getting third entry", "Enter third field", "Entry
number three:", self.disp)
This would shorten your code a lot. And why do you always pass self.disp
as the last argument? This is an attribute, so using it directly inside
the GetEntry method is not a problem.
> def GetEntry(self, des_string, req_string, dis_string, disp):
> """Entry routine for one field"""
>
> line = Entryline(des_string, req_string, dis_string, disp,
> master=root)
> error = line.mainloop()
*Never* call mainloop twice in an application! This is not the way to go.
If you want to do a modal dialog, you have to use the wait_window method,
which waits for a given window to be closed.
> self.Amount = line.retstring
> line.destroy()
>
> def say_bye(self):
> print 'Have a nice day, Bye!'
> self.quit()
>
> def createWidgets(self):
>
> self.descr = Label(self)
> self.descr["text"] = self.Company
> self.descr["fg"] = 'purple'
> self.descr['bg'] = 'yellow'
> self.descr.pack({'expand': 'yes', 'fill': 'both', "side": "top"})
This can be written:
self.descr.pack(side=TOP, fill=BOTH, expand=1)
which is shorter and IMHO clearer. TOP and BOTH are constants defined in
the Tkinter module, and passing named arguments is exactly the same as
passing a dictionary.
> Start = Button(self)
> Start["text"] = "Start a new entry"
> Start["fg"] = "blue"
> Start['bg'] = 'green'
> Start["command"] = self.start_new
Again, replace the 5 last lines with:
Start = Button(self, text="Start a new entry", fg='blue', bg='green',
command=self.start_new)
> Start.pack({'expand': 'yes', 'fill': 'both', "side": "left",
> 'after':
> self.descr})
(see above)
> QUIT = Button(self)
> QUIT["text"] = "QUIT"
> QUIT["fg"] = "black"
> QUIT['bg'] = 'red'
> QUIT["command"] = self.say_bye
> QUIT.pack({'expand': 'yes', 'fill': 'both', "side": "left",
> 'after':
> Start})
(see above)
> def __init__(self, master=None):
> Frame.__init__(self, master)
> self.pack()
> self.createWidgets()
>
> class Showscreen(Frame):
> """This is supposed to show the entries as they occur."""
>
> def CreateWidgets(self, Description):
>
> self.descr = Label(self)
> self.descr["text"] = Description
> self.descr["fg"] = 'purple'
> self.descr['bg'] = 'yellow'
> self.descr.pack({'expand': 'yes', 'fill': 'x', "side": "top",
> "anchor":
> "n"})
(see above)
> def __init__(self, Description, master=None):
> Frame.__init__(self,master)
> self.pack()
> self.CreateWidgets(Description)
>
> class Entryline(Frame):
> """This asks for an entry from the user and displays the result"""
>
> retstring = ''
>
> def entryend(self,S):
> """This gets done on carriage return and is where the hassles
> originate"""
>
> self.retstring = self.estring.get() # get the entered string
You're using retstring both as a class attribute (in "retstring = ''"
above) and here as an instance attribute. This is not really wrong, but a
bit weird. If you want retstring to be a class attribute, you should
access it using Entryline.retstring (and not self.retstring); If you want
it to be an instance attribute, you should remove the line "retstring =
''" above and add a line:
self.retstring = ''
in the constructor below.
>
> self.disp.Amount_des = Label(self.disp) # and put it into
> the
> display window
> self.disp.Amount_des["text"] = self.dis_string
> self.disp.Amount_des["fg"] = 'purple'
> self.disp.Amount_des['bg'] = 'yellow'
> self.disp.Amount_des.pack({'expand': 'yes', 'fill': 'x', "side":
> "left"})
(see above)
> self.disp.Amount = Label(self.disp)
> self.disp.Amount["text"] = self.retstring
> self.disp.Amount["fg"] = 'blue'
> self.disp.Amount['bg'] = 'green'
> self.disp.Amount.pack({'expand': 'yes', 'fill': 'x', "after":
> self.disp.Amount_des})
(see above)
> self.quit() # That's it, folks
>
> def createWidgets(self, des_string, req_string):
> """makes the stuff to ask the question"""
>
> descr = Label(self)
> descr["text"] = des_string
> descr["fg"] = 'purple'
> descr['bg'] = 'yellow'
> descr.pack({'expand': 'yes', 'fill': 'both', "side": "top"})
(see above)
> req = Label(self)
> req ["text"] = req_string
> req ["fg"] = 'yellow'
> req ['bg'] = 'blue'
> req.pack({'expand': 'yes', 'fill': 'both', "side" :"top",'after':
> descr})
(see above)
> self.estring = Entry(self)
> self.estring['fg'] = 'black'
> self.estring['bg'] = 'white'
> self.estring.pack({'expand':'yes','fill': 'both', 'side': 'top',
> 'after': req})
> self.estring.bind('<Key-Return>', self.entryend)
> self.estring.focus_set()
>
> def __init__(self, des_stringP, req_stringP, dis_stringP, dispP,
> master):
>
> self.disp = dispP # keep the passed params in instance vars
> self.des_string = des_stringP
> self.req_string = req_stringP
> self.dis_string = dis_stringP
AFAICS, these attributes are never used. Why did you create them?
> Frame.__init__(self, master)
> self.pack()
> self.createWidgets(self.des_string, self.req_string)
>
> root = Tk()
> root.title('Accounting Entry Window')
> app = Entryscreen(master=root)
> app.mainloop()
> root.destroy()
HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"
More information about the Python-list
mailing list