Python & ttk Using labelFrames to clean up a frame - python

I'm trying to build a basic GUI using ttk / Tkinter.
I have a plotted out a basic GUI that has the right basic components, but when I try and prettify it / space it out, I'm reach my limit of getting ttk containers to play nicely...
Examples:
from Tkinter import *
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title("Text Comparitor")
## build frame
self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.pack()
## text labels
ttk.Label(self.mainframe, text="Conversion Truth Tester", font=("Helvetica", 32)).grid(column=1, row=1, sticky=E)
self.mainframe.pack(side="bottom", fill=BOTH, expand=True)
self.mainframe.grid()
ttk.Label(self.mainframe, text="Source Filename:").grid(column=1, row=2, sticky=W)
ttk.Label(self.mainframe, text="Source Text:").grid(column=1, row=3, sticky=W)
ttk.Label(self.mainframe, text="Converted Text:").grid(column=1, row=4, sticky=W)
ttk.Label(self.mainframe, text="Cleaned Source:").grid(column=1, row=5, sticky=W)
ttk.Label(self.mainframe, text="Cleaned Converted:").grid(column=1, row=6, sticky=W)
ttk.Label(self.mainframe, text="Details:").grid(column=1, row=7, sticky=W)
## buttons
self.close = ttk.Button(self.mainframe, text="Close",command=self.closeFrame).grid(column=1, row=9, sticky=SE)
self.next = ttk.Button(self.mainframe, text="Next",command=self.nextPara).grid(column=1, row=9, sticky=S)
self.next = ttk.Button(self.mainframe, text="Prev",command=self.prevPara).grid(column=1, row=9, sticky=SW)
def closeFrame(self):
self.root.destroy()
def nextPara(self):
pass
def prevPara(self):
pass
def main():
root = Tk()
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Which results in:
I've been trying to add a 2nd container object, a label frame to hold the text label objects, which results in the buttons moving further up (and so I assume I'm not referencing the labelframe into the grid properly:
from Tkinter import *
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title("Text Comparitor")
## build frame
self.mainframe = ttk.Frame(self.root, padding="3 3 12 12")
self.mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
self.mainframe.columnconfigure(0, weight=1)
self.mainframe.rowconfigure(0, weight=1)
self.mainframe.pack()
## text labels
ttk.Label(self.mainframe, text="Conversion Truth Tester", font=("Helvetica", 32)).grid(column=1, row=1, sticky=E)
self.lfdata = ttk.Labelframe(self.root, labelwidget=self.mainframe, text='Label')#
self.lfdata.grid()
ttk.Label(self.lfdata, text="Source Filename:").grid(column=1, row=2, sticky=W)
ttk.Label(self.lfdata, text="Source Text:").grid(column=1, row=3, sticky=W)
ttk.Label(self.lfdata, text="Converted Text:").grid(column=1, row=4, sticky=W)
ttk.Label(self.lfdata, text="Cleaned Source:").grid(column=1, row=5, sticky=W)
ttk.Label(self.lfdata, text="Cleaned Converted:").grid(column=1, row=6, sticky=W)
ttk.Label(self.lfdata, text="Details:").grid(column=1, row=7, sticky=W)
## buttons
self.close = ttk.Button(self.mainframe, text="Close",command=self.closeFrame).grid(column=1, row=9, sticky=SE)
self.next = ttk.Button(self.mainframe, text="Next",command=self.nextPara).grid(column=1, row=9, sticky=S)
self.next = ttk.Button(self.mainframe, text="Prev",command=self.prevPara).grid(column=1, row=9, sticky=SW)
def closeFrame(self):
self.root.destroy()
def nextPara(self):
pass
def prevPara(self):
pass
def main():
root = Tk()
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Which results in: Note the swap of positions between buttons abd labels, and the just about visible aspects of the labelframe.
I'm trying to get the 2nd version to 'look' like a prettier version of the 1st.
Any pointers - I've been reading around the various resources / docs, and can't find anything that fits my example (most likely - I'm doing something silly...) and nothing I've tried has worked yet - including pack(), grid() and other snippets I've found in other related examples.

There are many places that require adjustments, let us comment on them (I will probably forget about something, so be sure to check the code at bottom).
First of all, applying weights to columns/rows in the frame alone is not going to make it expand as you resize the window. You need to do it in root. After that you might want to do it in the frame, to match your expectations about the layout after a resize. In your case, what makes most sense is making every column have the same weight > 0 and only making the second row have weight > 0. The reasoning for the columns is that you have 3 buttons, and you will want them all to expand in the free space in the same way. For the second part, that is a direct observation considering that you have a Labelframe at the second row. Giving a weight > 0 for any other row is going to give you a very weird layout. Weighting issues done.
Next thing I observed was your top label with a larger font. You certainly want it to span 3 columns (again, this number 3 is related to the row of buttons you will create at a later time). You may also want the text to be centered in these 3 columns (I'm not sure about your preferences here).
Now the Labelframe you create. It is just wrong, the labelwidget option does not mean what you think it does. It specifies a Label widget to serve as the label for this label frame. Thus, specifying your main frame for this parameter makes no sense. Maybe you want to specify some text to be visible at a certain position in the label frame. Also, this label frame must be grided with a columnspan of 3 too.
For the "gridding" in general I recommend specifying the option in_, so you make clear in relation to what widget you are "gridding". With that, it becomes obvious to start at column=0, row=0 each time you deepen your widget parenting level.
Here is how I adjusted your code:
import Tkinter
import ttk
class MakeGUI(object):
def __init__(self,root):
self.root = root
self.root.title(u"Title")
## build frame
self.mainframe = ttk.Frame(self.root, padding=(6, 6, 12, 12))
self.mainframe.grid(sticky='nwse')
for column in range(3):
self.mainframe.columnconfigure(column, weight=1)
self.mainframe.rowconfigure(1, weight=1)
## text labels
ttk.Label(self.mainframe, text=u"Label Title", anchor='center',
font=("Helvetica", 32)).grid(in_=self.mainframe,
column=0, row=0, columnspan=3, sticky="ew")
self.lfdata = ttk.Labelframe(self.mainframe, padding=(6, 6, 12, 12),
text='Labelframe')
self.lfdata.grid(column=0, columnspan=3, row=1, sticky='nsew')
info = (u"Source Filename", u"Source Text", u"Converted Text",
u"Cleaned Source", u"Cleaned Converted", u"Details")
for i, item in enumerate(info):
ttk.Label(self.lfdata, text=u"%s:" % item).grid(in_=self.lfdata,
column=0, row=i, sticky='w')
## buttons
btn = (u"Close", u"Next", u"Prev")
for i, item in enumerate(btn):
ttk.Button(self.mainframe, text=item).grid(in_=self.mainframe,
column=i, row=3)
def main():
root = Tkinter.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
makeGUI = MakeGUI(root)
root.mainloop()
if __name__ == '__main__':
main()
Here is how it looks when the program starts and after some resizing:

Related

How do I align something to the bottom left in Tkinter using either .grid() or .pack()?

I am currently trying to make a game in Tkinter which uses multiple different windows.
However, as I am trying to create the layout of a secondary window, I can't seem to get my Return to Menu button underneath the list box, and aligned to the left. I have tried it using .pack() and .grid() methods, but they don't make sense to me.
I've tried using .pack():
header = Frame(wn).pack()
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).pack(anchor='center')
footer = Frame(wn).pack(side=BOTTOM)
return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).pack(side=BOTTOM,padx=20,pady=250)
# body frame (left side)
bodyL = Frame(wn).pack(side=LEFT)
#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).pack(side=LEFT,padx=15)`
And I've tried using .grid():
header = Frame(wn).grid(sticky=N)
title = Label(header, text='Single-Player',font=('Arial bold',20),bg=bgcolor).grid(sticky=N+E+W,row=0,column=0)
footer = Frame(wn).grid(sticky=S)
return_to_menu = Button(footer, text='Return to Main Menu',font=('Arial',16),bg=bgcolor,command=lambda: menu()).grid(sticky=S,padx=20,row=0,column=0)
# body frame (left side)
bodyL = Frame(wn).grid(sticky=W)
#output box
output = Listbox(bodyL, width=50, font=("Arial", 20)).grid(sticky=W,padx=15, )`
However using .grid() doesn't align my title to the center of the screen anymore.
Is there a way to center it more efficiently - I didn't like using padx=450 to get it centered.
What happens with the button and the list box, is the button appears to the left of the list box, instead of on the bottom. I have attempted several other methods, such as incrementing row numbers, but my script doesn't appear to do what I anticipated.
Here is an example of how you can set up the weight of specific columns and row to get widgets to stick to a specific location on the screen.
With the use of grid() we need to use columnconfigure() and rowconfigure() to make this work.
These 2 methods are used to define at what ratio the column or row will expand in relation to the columns or rows around it as the container grows or shrinks.
See below example and let me know if you have any questions:
import tkinter as tk
root = tk.Tk()
for i in range(3):
root.columnconfigure(i, weight=1)
root.rowconfigure(1, weight=1)
tk.Label(root, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(root, text='Top center').grid(row=0, column=1)
tk.Label(root, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(root, text='center').grid(row=1, column=1)
tk.Label(root, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(root, text='Bottom center').grid(row=2, column=1)
tk.Label(root, text='Bottom right').grid(row=2, column=2, sticky='e')
root.mainloop()
Example:
Here is another example but this time I have a title label outside of a frame so that we can make it easier to manage the title being centered and then working with all the other content of the frame is separate from the title label.
import tkinter as tk
root = tk.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(1, weight=1)
frame = tk.Frame(root)
frame.grid(row=1, column=0, sticky='nsew')
for i in range(3):
frame.columnconfigure(i, weight=1)
frame.rowconfigure(1, weight=1)
tk.Label(root, text='Title centered').grid(row=0, column=0)
tk.Label(frame, text='Top left').grid(row=0, column=0, sticky='w')
tk.Label(frame, text='Top center').grid(row=0, column=1)
tk.Label(frame, text='Top right').grid(row=0, column=2, sticky='e')
tk.Label(frame, text='Center').grid(row=1, column=1)
tk.Label(frame, text='Bottom left').grid(row=2, column=0, sticky='w')
tk.Label(frame, text='Bottom center').grid(row=2, column=1)
tk.Label(frame, text='Bottom right').grid(row=2, column=2, sticky='e')
tk.Label(root, text='Footer centered').grid(row=2, column=0)
root.mainloop()
Example:
To address your problem in the comments you cannot use grid() or any other geometry manager for that matter on the same line you create your container. This will always cause the variable for that frame to return None as the geometry managers return None when called.
See this image as to what happens when you use grid() on the same line you create your container.
Now if you delete the grid() part on the row that your container is created and then write it on the next line as the commented out section of the above images shows it will work as expected. See below image of proper use of grid() for containers.
To address your 2nd question in the comments you can add this line to provide a button at the bottom left.
tk.Button(root, text='Bottom left button').grid(row=3, column=0, sticky='w')
Example:

Tkinter Python Remove Vertical Spaces

import tkinter as tk
root = tk.Tk()
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
B1.grid(row=3, column=0, sticky=tk.E+tk.W)
B2.grid(row=3, column=1, sticky=tk.E+tk.W)
B3.grid(row=3, column=2, sticky=tk.E+tk.W)
B4.grid(row=4, column=0, sticky=tk.E+tk.W)
B5.grid(row=4, column=1, sticky=tk.E+tk.W)
B6.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
label=tk.Text(root,background="yellow")
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky=tk.E+tk.W)
root.mainloop()
I am trying to build a GUI in Python using Tkinter but the space for the inserted text label as "Enter values below\nand click search.\n" occupies about 6 blank rows. Please help me remove it. My current result using the code above is the left one, I want to have the right one image.
When you create the text widget, specify the number of lines you want it to display, for example:
label=tk.Text(root,background="yellow", height=3)
Failing to specify means it will default to 24, hence why it is so large in your program.
Ignoring the grid() mistake in your code.
You could correct the sizing issue by providing a weight and starting geometry size.
UPDATE:
If you provide weights to the proper rows and columns give your Text widget a default height of say 3 and tell the grid() on your Text widget to sticky="nsew" you can have your program start out the size you want and be able to resize evenly if you want to.
Take a look at the below code:
import tkinter as tk
root = tk.Tk()
# we want all 3 columns to resize evenly for the buttons so we provide
# a weight of 1 to each. We also want the first row where the text box is
# to resize so there is not unwanted behavior when resizing, so we set its weight to 1.
# keep in mind a weight of zero (default) will tell tkinter to not resize that row or column.
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
root.columnconfigure(2, weight=1)
root.rowconfigure(0, weight=1)
buttonOK = tk.Button(root, text='B1')
MCC = tk.Button(root, text='B2')
TID = tk.Button(root, text='B3')
CURRENCY = tk.Button(root, text='B4')
COUNTRY = tk.Button(root, text='B5')
RESPONSE = tk.Button(root, text='B6')
# corrected the variables being assigned a grid location
buttonOK.grid(row=3, column=0, sticky=tk.E+tk.W)
MCC.grid(row=3, column=1, sticky=tk.E+tk.W)
TID.grid(row=3, column=2, sticky=tk.E+tk.W)
CURRENCY.grid(row=4, column=0, sticky=tk.E+tk.W)
COUNTRY.grid(row=4, column=1, sticky=tk.E+tk.W)
RESPONSE.grid(row=4, column=2, sticky=tk.E+tk.W)
label1 = tk.Entry(root, bd =8)
label1.grid(row=2, column=0, rowspan=1, columnspan=3, sticky=tk.E+tk.W)
# added a height of 3 to the Text widget. to reduce its starting height
label=tk.Text(root,background="yellow", height=3)
label.insert(index=0.0, chars="Enter values below\nand click search.\n")
# added stick="nsew" so the text box will resize with the available space in the window.
label.grid(row=0, column=0,rowspan=1, columnspan=3, sticky="nsew")
root.mainloop()

Is it possible to make 'dynamically' adjustable widgets in Tkinter/ttk

I'm developing very simple GUI for my DB. It shows record's list/tree in DB on left panel and (if user clicks on some record) shows the record on the right panel.
Here some bit of code which creates GUI
from Tkinter import *
import ttk
master = Tk()
reclist = ttk.Treeview(columns=["TIME STAMP","HASH","MESSAGE"])
ysb = ttk.Scrollbar(orient=VERTICAL, command= reclist.yview)
xsb = ttk.Scrollbar(orient=HORIZONTAL, command= reclist.xview)
reclist['yscroll'] = ysb.set
reclist['xscroll'] = xsb.set
reclist.grid(in_=master, row=0, column=0, sticky=NSEW)
ysb.grid(in_=master, row=0, column=1, sticky=NS)
xsb.grid(in_=master, row=1, column=0, sticky=EW)
Comment = Text(master)
Comment.tag_configure("center", justify='center')
ysc = ttk.Scrollbar(orient=VERTICAL, command= Comment.yview)
xsc = ttk.Scrollbar(orient=HORIZONTAL, command= Comment.xview)
Comment.grid(in_=master,row=0,column=2,sticky=W+E+N+S)#, columnspan=5)
ysc.grid(in_=master, row=0, column=3, sticky=NS)
xsc.grid(in_=master, row=1, column=2, sticky=EW)
master.rowconfigure(0, weight=3)
master.columnconfigure(0, weight=3)
master.columnconfigure(2, weight=3)
master.mainloop()
Everything works pretty well, except that two panels are not adjustable. I cannot move border between them to make list of records or record panel bigger or smaller. I'm pretty sure in is possible (for example in gitk you can move the border between the list of commits and a displaied commit). I've search quite a lot with no luck.
What you are looking for is called a "PanedWindow". Both the tkinter and ttk modules have one, and they work almost identically. The general idea is that you create a PanedWindow instance, and then you add two or more widgets to it. The PanedWindow will add a movable slider between each widget. Typically you would use frames, which you can then fill up with other widgets.
Here is an example using the one in Tkinter:
import Tkinter as tk
root = tk.Tk()
pw = tk.PanedWindow()
pw.pack(fill="both", expand=True)
f1 = tk.Frame(width=200, height=200, background="bisque")
f2 = tk.Frame(width=200, height=200, background="pink")
pw.add(f1)
pw.add(f2)
# adding some widgets to the left...
text = tk.Text(f1, height=20, width=20, wrap="none")
ysb = tk.Scrollbar(f1, orient="vertical", command=text.yview)
xsb = tk.Scrollbar(f1, orient="horizontal", command=text.xview)
text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
f1.grid_rowconfigure(0, weight=1)
f1.grid_columnconfigure(0, weight=1)
xsb.grid(row=1, column=0, sticky="ew")
ysb.grid(row=0, column=1, sticky="ns")
text.grid(row=0, column=0, sticky="nsew")
# and to the right...
b1 = tk.Button(f2, text="Click me!")
s1 = tk.Scale(f2, from_=1, to=20, orient="horizontal")
b1.pack(side="top", fill="x")
s1.pack(side="top", fill="x")
root.mainloop()

How to only select one radio button at a time in Python Tkinter?

I have been going through Tkinter examples and am now working with buttons.
When I select or deselect a button, all other buttons do the same thing. How can I change it where only one button will change at a time? I've tried other things but I'm not having any luck.
Here is my current code:
import tkinter
from tkinter import ttk
class Adder(ttk.Frame):
"""The adders gui and functions."""
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
def init_gui(self):
"""Builds GUI."""
self.root.title('IDL - I.D. Lookup')
self.grid(column=0, row=0, sticky='nsew') # this starts the entire form
self.num1_entry = ttk.Entry(self, width=15) # width of first input box
self.num1_entry.grid(sticky='W', column=1, row = 2) # column and row it is placed on # sticky='w' justifies or aligns to left
self.num2_entry = ttk.Entry(self, width=10) # width of second input box
self.num2_entry.grid(sticky='W', column=1, row=3) # column and row it is placed on
self.calc_button = ttk.Button(self, text='Calculate') # button
self.calc_button.grid(column=0, row=4, columnspan=4) # column and row it is placed on
self.answer_frame = ttk.LabelFrame(self, text='Answer', height=100) # answer box
self.answer_frame.grid(column=0, row=5, columnspan=4, sticky='nesw')
self.rad_button = ttk.Radiobutton(self, text='Town').grid(sticky='W', column=0,row=6, columnspan=1) # sticky W to align everything to left
self.rad_button = ttk.Radiobutton(self, text='Town1').grid(sticky='W', column=0,row=7, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town2').grid(sticky='W', column=0,row=8, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town3').grid(sticky='W', column=0,row=9, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town4').grid(sticky='W', column=0,row=10, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town5').grid(sticky='W', column=0,row=11, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town6').grid(sticky='W', column=0,row=12, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town7').grid(sticky='W', column=0,row=13, columnspan=1)
self.rad_button = ttk.Radiobutton(self, text='Town8').grid(sticky='W', column=0,row=14, columnspan=1)
self.answer_label = ttk.Label(self.answer_frame, text='') # text=' ' holds text in the answer box
self.answer_label.grid(column=0, row=0)
# Labels that remain constant throughout execution.
ttk.Label(self, text='IDL - I.D. Lookup').grid(column=0, row=0,
columnspan=4)
ttk.Label(self, text='Name').grid(column=0, row=2,
sticky='w')
ttk.Label(self, text='I.D.').grid(column=0, row=3,
sticky='w')
ttk.Separator(self, orient='horizontal').grid(column=0, # line under title.
row=1, columnspan=4, sticky='ew')
for child in self.winfo_children():
child.grid_configure(padx=10, pady=4) # padx 10 adds horizontal padding on the out edge of window
if __name__ == '__main__':
root = tkinter.Tk()
Adder(root)
root.mainloop()
Tkinter radiobuttons require two options for proper operation that you're not providing: variable (a Tk variable to store the value of the selected button in the group, typically an IntVar or StringVar) and value (a distinct value for each button in the group, to be stored in the variable when that button is selected). So you'd need:
self.selectedTown = tkinter.StringVar()
self.selectedTown.set('Town')
somewhere at the top, then things like:
ttk.Radiobutton(self, text='Town', value='Town', variable=self.selectedTown)
for each button; then something like
print self.selectedTown.get()
when you want to check which item was selected.
PS: Assigning all of your buttons to the same variable self.rad_button is kind of pointless. You're not assigning the actual button itself, anyway: you're assigning None each time, which is the result of the .grid() call on each line.

frames in frames layout using tkinter

I want to create tkinter grid layout 5 frames. It looked the way I wanted before replacing Frame content.
self.Frame1 = tk.Frame(self.parent, bg="grey")
with
self.Frame1 = LeftUpperWindow(self.parent)
When I maximize the window I see everything in LeftUpperWindow frame in messed up, layout incorrectly. A picture is worth a thousand words so I have this
and would like to have this
import sys
import os
import Tkinter as tk
import ttk as ttk
class LeftUpperWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='#ffffff', borderwidth=1, relief="sunken")
self.__create_layout()
def __create_layout(self):
self.parent.grid()
for r in range(3):
self.parent.rowconfigure(r, weight=1)
for c in range(2):
self.parent.columnconfigure(c, weight=1)
self.editArea = tk.Text(self.parent, wrap=tk.NONE, undo=True,
relief=tk.SUNKEN, borderwidth=5, highlightthickness=0, insertbackground="white")
self.editArea.config(font=('courier', 10, 'normal'))
self.editArea.configure(bg="black", fg="white")
self.editArea.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))
self.scrollbarY = tk.Scrollbar(self.parent, orient=tk.VERTICAL)
self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=2)
self.scrollbarX = tk.Scrollbar(self.parent, orient=tk.HORIZONTAL)
self.scrollbarX.grid(row=1, column=0, sticky=(tk.W, tk.E))
self.status = tk.Label(self.parent, text="Status label", bd=1, relief=tk.SUNKEN, anchor=tk.W, bg='lightgray')
self.status.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), columnspan=2)
def __config_window(self):
pass
def close_quit(self, event=None):
self.parent.destroy()
def dummy_func(self):
print("dummy")
pass
class MainWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='#ffffff', borderwidth=1, relief="sunken")
self.__create_layout()
self.__create_menu()
self.__config_mainWindow()
def __create_layout(self):
self.parent.grid()
for r in range(6):
self.parent.rowconfigure(r, weight=1)
for c in range(10):
self.parent.columnconfigure(c, weight=1)
self.Frame1 = LeftUpperWindow(self.parent) #tk.Frame(self.parent, bg="grey")
self.Frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame2 = tk.Frame(self.parent, bg="blue")
self.Frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame3 = tk.Frame(self.parent, bg="green")
self.Frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame4 = tk.Frame(self.parent, bg="brown")
self.Frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame5 = tk.Frame(self.parent, bg="pink")
self.Frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))
def close_quit(self, event=None):
self.parent.destroy()
def __config_mainWindow(self):
self.parent.config(menu=self.menubar)
def __create_menu(self):
self.menubar = tk.Menu(self.parent)
self.filemenu = tk.Menu(self.menubar, tearoff=0)
self.filemenu.add_command(label="Open", command=self.dummy_func)
self.filemenu.add_separator()
self.filemenu.add_command(label="Exit", command=self.close_quit)
self.menubar.add_cascade(label="File", menu=self.filemenu)
helpmenu = tk.Menu(self.menubar, tearoff=0)
helpmenu.add_command(label="About...", command=self.dummy_func)
self.menubar.add_cascade(label="Help", menu=helpmenu)
def dummy_func(self):
print("dummy")
pass
#
# MAIN
#
def main():
root = tk.Tk()
root.title("Frames")
root.geometry("550x300+525+300")
root.configure(background="#808080")
root.option_add("*font", ("Courier New", 9, "normal"))
window = MainWindow(master=root)
root.protocol("WM_DELETE_WINDOW", window.close_quit)
root.mainloop()
if __name__ == '__main__':
main()
Overview
There is no quick fix for your problem. You are doing several things in your code that stack the odds against you, and which makes it really hard to debug your code. A rewrite is the best solution.
Here are things I've found that make the problem more difficult than they need to be, and which can be solved by a rewrite.
First, don't have a widget be responsible for placing itself in its parent. In your case you're going one step further by having a widget place it's parent into its grandparent. That's simply the wrong way to do it. A containing window should be responsible for layout out it's children, and nothing more
Second, put as much of your layout code together as possible. By "all" I mean "all for a given container". So, for example, instead of this:
x=Label(...)
x.grid(...)
y =Label(...)
y.grid(...)
... do it like this:
x = Label(...)
y = Label(...)
x.grid(...)
y.grid(...)
This makes it much easier to visualize your layout in the code.
Third, don't try to solve multiple layout problems at once. Solve one problem at a time. Since you posted your whole program, I assume you have a problem with the whole program and not just the upper-left widgets. When I ran your code I observed resize behavior that seemed off to me, reinforcing that belief.
Fourth, if you create a class like MainWindow that is designed to contain other widgets, all of these other widgets shild be children of the window, not children of the parent.
Fifth, as a general rule of thumb you want only a single row and single column with a container to have a non-zero weight. This is only a guideline, because there are often times when you want more than one widget to expand or contract. In the case of scrollbars and statuslabels, you typically don't want them to be given extra space. Part of the problem in your code is that you give everything a weight, so extra space is allocated to every widget.
The solution
The solution is work on one section at a time. Get it working, then move on to the next section. In your case I see the effort breaking down into four phases. First, get the main window created and laid out. Next, add the widgets that are immediate children of the main window. After that, create the container for the upper-left window. Finally, add the widgets to the upper left window. After each phase, be sure to run the code and verify that everything looks right.
The main window
The first step is to start with the main window. Get it to look right, and behave properly when you resize the main window. When I ran your code the bottom and right windows would vanish when the window became small, and the bottom frames grew when the window was large. I'm guessing that's not the correct behavior. Regardless, start with what you think is the correct behavior and make it work before adding any more widgets.
Start with the following code. Verify that when you resize the window, everything expands and contracts the way you expect. Notice that I made a couple of simple changes. The main thing to notice is that the function that creates the main window is responsible for making that window visible. I use pack since it's the only widget in the root window, and it means I can do it all in one line without having to worry about row and column weights with grid.
import sys
import os
import Tkinter as tk
import ttk as ttk
class MainWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
def __create_layout(self):
pass
#
# MAIN
#
def main():
root = tk.Tk()
root.title("Frames")
root.geometry("550x300+525+300")
root.configure(background="#808080")
root.option_add("*font", ("Courier New", 9, "normal"))
window = MainWindow(master=root)
window.pack(side="top", fill="both", expand=True)
root.mainloop()
if __name__ == '__main__':
main()
The widgets inside of MainWindow
Once you have the MainWindow shell behaving properly, it's time to add more widgets. It looks like you have five frames, though one of them is a custom class. For now we'll use a regular frame to keep things simple. It's good that you give each one a color, that's a great way to visualize the layout as you build up the window.
Modify __create_layout to look like the following. Note two important changes: every widget is a child of self rather than self.parent, and I've grouped all of the layout code together.
The resize behavior looks off to me, but I don't know exactly what you intend. For me it seems odd to have the green, red and pink frames resize the way they do, though maybe that's what you want. Regardless, now is the time to get it right.
def __create_layout(self):
self.Frame1 = tk.Frame(self, bg="grey")
self.Frame2 = tk.Frame(self, bg="blue")
self.Frame3 = tk.Frame(self, bg="green")
self.Frame4 = tk.Frame(self, bg="brown")
self.Frame5 = tk.Frame(self, bg="pink")
self.Frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
self.Frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))
for r in range(6):
self.rowconfigure(r, weight=1)
for c in range(10):
self.columnconfigure(c, weight=1)
The LeftUpperWindow widget
Next, let's define the LeftUpperWindow class, and make sure we haven't broken anything. Add a stub for the class, give the window a distinct color, and make sure the window still looks ok when it appears and when it is resized.
class LeftUpperWindow(tk.Frame):
def __init__(self, master=None):
self.parent = master
tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
self.__create_layout()
def __create_layout(self):
pass
Remember to modify MainWindow.__create_layout to create an instance of this class rather than frame:
self.frame1 = LeftUpperWindow(self)
The widgets in LeftUpperWindow
Finally, it's time to add the widgets in LeftUpperWindow. Like we did in MainWindow, the widgets are all children of self, and the widget creation and widget layout are done in separate groups. Also, it's usually best to only give a single row and a single column a non-zero weight. This is usually the row and column that a text or canvas widget is in.
You can do what you want, of course. Just be mindful of the fact that because you gave each row and column a weight in your original code, that contributed to all of the space between widgets.
Also, because you're giving the GUI an explicit size (via root.geometry(...)), you want to give the text widget a small size. Otherwise, the default width and height will push out the other widgets since tkinter tries to give each widget its default size. Since you're constraining the overall window size you want the text widget to be as small as possible, and then let grid expand it to fill the area it's been given.
Here's what __create_layout should look like:
def __create_layout(self):
self.editArea = tk.Text(self, wrap=tk.NONE, undo=True,
width=1, height=1,
relief=tk.SUNKEN, borderwidth=5,
highlightthickness=0, insertbackground="white")
self.editArea.config(font=('courier', 10, 'normal'))
self.editArea.configure(bg="black", fg="white")
self.scrollbarY = tk.Scrollbar(self, orient=tk.VERTICAL)
self.scrollbarX = tk.Scrollbar(self, orient=tk.HORIZONTAL)
self.status = tk.Label(self, text="Status label", bd=1, relief=tk.SUNKEN,
anchor=tk.W, bg='lightgray')
self.editArea.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))
self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=2)
self.scrollbarX.grid(row=1, column=0, sticky=(tk.W, tk.E))
self.status.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), columnspan=2)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)

Categories