Tkinter pack Problem

H J van Rooyen mail at microcorp.co.za
Wed Jul 26 12:09:36 EDT 2006


"Eric Brunel" <eric_brunel at despammed.com> wrote:

|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.

I can kind of agree with this - If I learnt one thing it was that I can get them
in a column, or a row, or what looks like a weird kind of horizontal hierarchy,
but not what I was looking for...

|
|[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).

No particular reason other than ignorance - the original example I was following
did something like this, and
as it actually produced something that looked to me like it was working I did
not mess with it :-)

What is the best place to get info on all these wonderful things - I have been
trying to come right by reading the module help - but its kind of cryptic... -
like most things its cool as a reminder if you already know what it does...

|
|>     """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.
|

Ok I will try it, thanks.


|>             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:

The original code actually used the variables as globals ... *ducks*

|
|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.

The thing was giving me all sorts of errors when I changed disp from being a
global - It prolly has to do with the bad move of having a second instance of
k  - I messed around and stopped when I got no more errors - could this be what
is really meant by Heuristic Programming?

|
|>     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.

What is a modal dialog, daddy? (I am not being facetious, I really wanna know)

- I am actually not closing the window - the frame with the two labels and the
entry line disappears when you hit enter, and then gets manufactured again for
the next entry - in the real app there is also a choice widget with scroll bars
that is populated from a file and that also disappears when you make a choice by
double mouse click - that is how coded things like company, document type, and
account codes would be entered in practice - the entry thingy was the simplest
thing to include to show what I was complaining about.

Will the wait_window method work for the Frame, or must it be a window? Or is
there a wait_frame method?

I am not sure I understand the mainloop bit - is this mainloop the same mainloop
as the other mainloop?
What does this mainloop actually do? - I need to pass control to the thingy that
gets the entry somehow, and wait for it to complete, and get the value that was
entered and displayed to use in the real app... - or will this happen
*automagically* because of the binding of the enter key to the entryend
method? - and I need the data before the entry thingy is destroyed so I cant let
it commit seppoku (Hara Kiri for the uninitiated) - or must I do that and then I
would have to go fetch the data out of the other window where its displayed? (if
its still there and not a reference to a destroyed thing - I find assignment in
Python a bit confusing at times...)
I was under the impression that this mainloop is tied to line and not to the
*main* instance...
I did not (and cannot) call mainloop for the second Tk instance - if I do then
app blocks until the second window is closed, and then the whole thing is
broken...

Another small issue - the binding to entryend - on Linux, I see that it binds
only the main keyboard enter key, and not the numeric pad enter key - (I did not
test this on Windows) How does one get both?

|>         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.

Thanks - did not know this and I like it - its also less to type and that is
important cos I am lazy...

|
|>         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)

*nods*

|
|>         Start.pack({'expand': 'yes', 'fill': 'both', "side": "left",
|> 'after':
|> self.descr})
|
|(see above)

*nods*

|
|>         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)

*nods*

|
|>     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)

*nods*

|
|>     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.

Ok - I think it might have to be an instance thinghy really - cos it is
effectively the *return value* of what was typed - and it happens more than
once - OTOH - if I read it out of the class variable at the right time, it might
also work - there *should* only be one of these alive at any one time - or am I
talking BS?

|
|>
|>         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)

*nods*

|
|>         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)

*nods*

|
|>         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)

*nods*

|
|>         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)

*nods*

|
|>         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?

a) they used to be globals *ducks again*
b) when I was changing them from being globals I was getting errors and after I
did this the errors went away... - I just changed the names to make them formal
parameters by appending a "P"...
c) the value of self.dis_string is used above to populate the instance of
disp.Amount_des["text"] in entryend - it is needed there - the other two are
used below in the createWidgets call - but you are right - below here I could
have used them just as they were passed, like I do with master in the __init__
call

|
|>         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

Yes it does - thanks for the effort

In a sense, the answer of "you cant do this with pack, use grid" is the wrong
answer, as I have spent far too much time on this - but hey, that's how you
learn...

Did you actually try to run this thing to see what it did? - from my perspective
it seems to do everything I want except that it messes up the display of the
data in the second window...

|--
|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