-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtt100.py
More file actions
346 lines (260 loc) · 14 KB
/
tt100.py
File metadata and controls
346 lines (260 loc) · 14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
"""<tt100.py
In this program, we look at several pack() options for controlling layouts
within a frame:
* side
* fill
* expand
* anchor
This program is unlike the other programs in this series. That is, you don't
need to read the source code in order to understand how to code some feature.
You need to RUN the program.
The purpose of the program is to show you the results of pack options. Running
the program will allow you to set different pack options and to observe the
effects of different combinations of options.
THE CONCEPTS UNDERLYING THE PACK OPTIONS
To understand how we can control the appearance of widgets within a container
(that is, with a frame), we need to remember that the pack geometry manager uses
a "cavity" model of arrangement. That is, each container contains a cavity, and
we pack slaves within the container.
In talking about the positioning and display of components with a container, it
will be useful to have three concepts:
* unclaimed space (that is, the cavity)
* claimed but unused space
* claimed and used space
When you pack a widget, such as a button, it is always packed along one of the
four sides of the cavity. The pack "side" option specifies which side to use.
For example, if we specify "side=LEFT", the the widget will be packed (that is,
positioned) on the left side of the cavity.
When a widget is packed along a side, it claims the entire side, although it may
not actually use all of the space it has claimed. Suppose we pack a small button
called X along the left side of a large cavity, as in the following diagram.
-------------------
claimed but | | |
unused -----> | | cavity |
| | (unclaimed) |
|___| |
claimed and | | |
used -------> | X | |
|___| |
| | |
claimed but | | |
unused -----> | | |
| | |
-------------------
The cavity (the unclaimed area) is now to the right of the widget. Widget X has
claimed the entire left side, in a strip that is wide enough to hold it. But
because widget X is small, it actually uses only a small part of the area that
it has claimed. That is the part that it uses to display itself.
As you can see, widget X has claimed only enough space as it needs to display
itself. If we specify a pack option of "expand=YES", it will claim ALL of the
available area. No part of the cavity would be left unclaimed. Note that
this does not mean that widget X would *use* the whole area. It would still
only use the small part that it needs.
If a widget has claimed more space than it is using, it has two choices:
* it can move around in the unclaimed space, or
* it can grow to fill the unclaimed space.
If we want it to grow to use the the unclaimed space, we can use the "fill"
option, which tells a widget whether or not it can grow to fill the unused
space, and in which direction it can grow.
* "fill=NONE" means that it cannot grow.
* "fill=X" means that it can grow along the X-axis (i.e. horizontally).
* "fill=Y" means that it can grow along the Y-axis (i.e. vertically).
* "fill=BOTH" means that it can grow both horizontally and vertically.
If we want it to grow to move around in the the unclaimed space, then we can use
the "anchor" option, which tells a widget where to position itself in the space
it has claimed. The values of the anchor option are like compass headings. "N"
means "north" (i.e. centered at the top of the claimed area). "NE" means
"northeast" (i.e. in the upper, right corner of the claimed area), "CENTER"
means centered right in the middle of the claimed area. And so on.
RUNNING THE PROGRAM
So now, run the program. You don't need to read the code. Just run the program
and experiment with the various pack options of the three demo buttons.
Button A's frame gives it a horizontal cavity to run around in -- the frame is
no taller than the button.
Button B's cavity gives it a vertical cavity to run around in -- the frame is no
wider than the button.
And button C's frame gives it a big cavity -- much wider and taller than the
button itself -- to play in.
If the appearance of any of the buttons under certain settings surprises
you, try to figure out why the button looks the way it does.
And finally....
A USEFUL DEBUGGING TIP
Note that packing is a complicated business, because the positioning of a widget
with respect to other widgets that were packed earlier depends in part on how
the other widgets were packed. That is, if the other widgets were packed to the
left, then the cavity within which the next widget can be packed will be to
their right. But if they were packed to the top of the cavity, then the cavity
within which the next widget can be packed will be below them. It can all get
very confusing.
Here is a useful debugging tip. If you are developing your layout and hit a
problem -- things aren't acting the way you think they should -- then give each
of your containers (that is, each of your frames) a different background color,
for example:
bg="red"
or
bg="cyan"
or
bg="tan"
... or yellow, or blue, or red, and so on.
This will allow you to see how the frames are actually arranging themselves. Often,
what you see will give you a clue as to what the problem is.
[revised: 2004-04-26]
>"""
from tkinter import *
class MyApp:
def __init__(self, parent):
#------ constants for controlling layout of buttons ------
button_width = 6
button_padx = "2m"
button_pady = "1m"
buttons_frame_padx = "3m"
buttons_frame_pady = "2m"
buttons_frame_ipadx = "3m"
buttons_frame_ipady = "1m"
# -------------- end constants ----------------
# set up Tkinter variables, to be controlled by the radio buttons
self.button_name = StringVar()
self.button_name.set("C")
self.side_option = StringVar()
self.side_option.set(LEFT)
self.fill_option = StringVar()
self.fill_option.set(NONE)
self.expand_option = StringVar()
self.expand_option.set(YES)
self.anchor_option = StringVar()
self.anchor_option.set(CENTER)
# -------------- end constants ----------------
self.myParent = parent
self.myParent.geometry("640x400")
### Our topmost frame is called myContainer1
self.myContainer1 = Frame(parent) ###
self.myContainer1.pack(expand=YES, fill=BOTH)
### We will use HORIZONTAL (left/right) orientation inside myContainer1.
### Inside myContainer1, we create control_frame and demo_frame.
# control frame - basically everything except the demo frame
self.control_frame = Frame(self.myContainer1) ###
self.control_frame.pack(side=LEFT, expand=NO, padx=10, pady=5, ipadx=5, ipady=5)
# inside control_frame we create a header label
# and a buttons_frame at the top,
# and demo_frame at the bottom
myMessage="This window shows the effects of the \nexpand, fill, and anchor packing options.\n"
Label(self.control_frame, text=myMessage, justify=LEFT).pack(side=TOP, anchor=W)
# buttons frame
self.buttons_frame = Frame(self.control_frame) ###
self.buttons_frame.pack(side=TOP, expand=NO, fill=Y, ipadx=5, ipady=5)
# demo frame
self.demo_frame = Frame(self.myContainer1) ###
self.demo_frame.pack(side=RIGHT, expand=YES, fill=BOTH)
### Inside the demo frame, we create top_frame and bottom_frame.
### These will be our demonstration frames.
# top frame
self.top_frame = Frame(self.demo_frame)
self.top_frame.pack(side=TOP, expand=YES, fill=BOTH) ###
# bottom frame
self.bottom_frame = Frame(self.demo_frame,
borderwidth=5, relief=RIDGE,
height=50,
bg="cyan",
) ###
self.bottom_frame.pack(side=TOP, fill=X)
### Now we will put two more frames, left_frame and right_frame,
### inside top_frame. We will use HORIZONTAL (left/right)
### orientation within top_frame.
# left_frame
self.left_frame = Frame(self.top_frame, background="red",
borderwidth=5, relief=RIDGE,
width=50,
) ###
self.left_frame.pack(side=LEFT, expand=NO, fill=Y)
### right_frame
self.right_frame = Frame(self.top_frame, background="tan",
borderwidth=5, relief=RIDGE,
width=250
)
self.right_frame.pack(side=RIGHT, expand=YES, fill=BOTH)
# now put a button in each of the interesting frames
button_names = ["A", "B", "C"]
side_options = [LEFT, TOP, RIGHT, BOTTOM]
fill_options = [X, Y, BOTH, NONE]
expand_options = [YES, NO]
anchor_options = [NW, N, NE, E, SE, S, SW, W, CENTER]
self.buttonA = Button(self.bottom_frame, text="A")
self.buttonA.pack()
self.buttonB = Button(self.left_frame, text="B")
self.buttonB.pack()
self.buttonC = Button(self.right_frame, text="C")
self.buttonC.pack()
self.button_with_name = {"A":self.buttonA, "B":self.buttonB, "C":self.buttonC}
# now we some subframes to the buttons_frame
self.button_names_frame = Frame(self.buttons_frame, borderwidth=5)
self.side_options_frame = Frame(self.buttons_frame, borderwidth=5)
self.fill_options_frame = Frame(self.buttons_frame, borderwidth=5)
self.expand_options_frame = Frame(self.buttons_frame, borderwidth=5)
self.anchor_options_frame = Frame(self.buttons_frame, borderwidth=5)
self.button_names_frame.pack( side=LEFT, expand=YES, fill=Y, anchor=N)
self.side_options_frame.pack( side=LEFT, expand=YES, anchor=N)
self.fill_options_frame.pack( side=LEFT, expand=YES, anchor=N)
self.expand_options_frame.pack(side=LEFT, expand=YES, anchor=N)
self.anchor_options_frame.pack(side=LEFT, expand=YES, anchor=N)
Label(self.button_names_frame, text="\nButton").pack()
Label(self.side_options_frame, text="Side\nOption").pack()
Label(self.fill_options_frame, text="Fill\nOption").pack()
Label(self.expand_options_frame, text="Expand\nOption").pack()
Label(self.anchor_options_frame, text="Anchor\nOption").pack()
for option in button_names:
button = Radiobutton(self.button_names_frame, text=str(option), indicatoron=1,
value=option, command=self.button_refresh, variable=self.button_name)
button["width"] = button_width
button.pack(side=TOP)
for option in side_options:
button = Radiobutton(self.side_options_frame, text=str(option), indicatoron=0,
value=option, command=self.demo_update, variable=self.side_option)
button["width"] = button_width
button.pack(side=TOP)
for option in fill_options:
button = Radiobutton(self.fill_options_frame, text=str(option), indicatoron=0,
value=option, command=self.demo_update, variable=self.fill_option)
button["width"] = button_width
button.pack(side=TOP)
for option in expand_options:
button = Radiobutton(self.expand_options_frame, text=str(option), indicatoron=0,
value=option, command=self.demo_update, variable=self.expand_option)
button["width"] = button_width
button.pack(side=TOP)
for option in anchor_options:
button = Radiobutton(self.anchor_options_frame, text=str(option), indicatoron=0,
value=option, command=self.demo_update, variable=self.anchor_option)
button["width"] = button_width
button.pack(side=TOP)
self.cancelButtonFrame = Frame(self.button_names_frame)
self.cancelButtonFrame.pack(side=BOTTOM, expand=YES, anchor=SW)
self.cancelButton = Button(self.cancelButtonFrame,
text="Cancel", background="red",
width=button_width,
padx=button_padx,
pady=button_pady
)
self.cancelButton.pack(side=BOTTOM, anchor=S)
self.cancelButton.bind("<Button-1>", self.cancelButtonClick)
self.cancelButton.bind("<Return>", self.cancelButtonClick)
# set up the buttons in their initial position
self.demo_update()
def button_refresh(self):
button = self.button_with_name[self.button_name.get()]
properties = button.pack_info()
self.fill_option.set ( properties["fill"] )
self.side_option.set ( properties["side"] )
self.expand_option.set( properties["expand"] )
self.anchor_option.set( properties["anchor"] )
def demo_update(self):
button = self.button_with_name[self.button_name.get()]
button.pack(fill=self.fill_option.get()
, side=self.side_option.get()
, expand=self.expand_option.get()
, anchor=self.anchor_option.get()
)
def cancelButtonClick(self, event):
self.myParent.destroy()
root = Tk()
myapp = MyApp(root)
root.mainloop()