frames in frames layout using tkinter - python

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)

Related

How can I create this layout with Python3 using Tkinter with the grid geometry manager?

I am trying to create the following layout:
This is my code:
from . import FlowPane,JsonPane,PropertiesPane,ToolbarPane
import tkinter as tk
class ConflixEditor(tk.Tk):
def __init__(self, args):
super().__init__()
self.__dict__.update({k: v for k, v in locals().items() if k != 'self'})
self.minsize(width=1024, height=768)
self.title('Conflix Editor')
# Widget Creation
self.frame = tk.Frame(self)
self.toolbarPane = ToolbarPane.ToolbarPane(self.frame, bg='black')
self.flowPane = FlowPane.FlowPane(self.frame, bg='red')
self.propertiesPane = PropertiesPane.PropertiesPane(self.frame, bg='blue')
self.jsonPane = JsonPane.JsonPane(self.frame, bg='green')
# Widget Layout
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(2, weight=1)
self.frame.grid(row=0, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
self.toolbarPane.grid(row=0, column=0, columnspan=3, rowspan=2, sticky=tk.N+tk.E+tk.W)
self.flowPane.grid(row=2, column=0, columnspan=2, rowspan=5, sticky=tk.N+tk.S+tk.W)
self.propertiesPane.grid(row=2, column=2, columnspan=1, rowspan=5, sticky=tk.N+tk.E+tk.S)
self.jsonPane.grid(row=7, column=0, columnspan=3, rowspan=3, sticky=tk.E+tk.S+tk.W)
The constructors for FlowPane, JsonPane, PropertiesPane, ToolbarPane all take two parameters: the parent widget and the background color.
Instead of getting the desired result above, I am getting the following result:
What am I doing wrong? How can I create the desired layout? Note that the background colors are just temporary to confirm that each widget is using the correct amount of space. This is eventually going to be an application for designing and building Netflix Conductor workflows. I want to have a toolbar with menus and buttons in the black area, a Canvas in the red area for displaying the flow-chart elements that represent tasks in the workflows, a Treeview for viewing properties in the blue area, and a Text Editor in the green area at the bottom for viewing/editing the raw JSON.
You need to:
specify height option for toolbarPane and jsonPane;
specify width option for propertiesPane;
add tk.E to sticky option for flowPane;
use grid_rowconfigure() and grid_columnconfigure() for self and self.frame as well
Below is an updated code:
class ConflixEditor(tk.Tk):
def __init__(self, *args):
super().__init__()
#self.__dict__.update({k: v for k, v in locals().items() if k != 'self'})
self.minsize(width=1024, height=768)
self.title('Conflix Editor')
# make self.frame use all the space of root window
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
# Widget Creation
self.frame = tk.Frame(self)
self.toolbarPane = ToolbarPane.ToolbarPane(self.frame, bg='black', height=100) # added height option
self.flowPane = FlowPane.FlowPane(self.frame, bg='red')
self.propertiesPane = PropertiesPane.PropertiesPane(self.frame, bg='blue', width=250) # added width option
self.jsonPane = JsonPane.JsonPane(self.frame, bg='green', height=200) # added height option
# Widget Layout
self.frame.grid_columnconfigure(0, weight=1) # used self.frame instead of self
self.frame.grid_rowconfigure(2, weight=1) # used self.frame instead of self
self.frame.grid(row=0, column=0, sticky=tk.N+tk.E+tk.S+tk.W)
self.toolbarPane.grid(row=0, column=0, columnspan=3, rowspan=2, sticky=tk.N+tk.E+tk.W)
self.flowPane.grid(row=2, column=0, columnspan=2, rowspan=5, sticky=tk.N+tk.E+tk.S+tk.W) # added tk.E
self.propertiesPane.grid(row=2, column=2, columnspan=1, rowspan=5, sticky=tk.N+tk.E+tk.S)
self.jsonPane.grid(row=7, column=0, columnspan=3, rowspan=3, sticky=tk.E+tk.S+tk.W)

*.grid() layout manager does not work as I expect it to behave

So I try to create my from Tk.LabelFrame derived class and use the grid() layout manager.
But somehow it doesn't work as expected. It really annoys me, is there anything I dont see?
Every other class I used and created with this method works as expected, but not this one...
This is my minimal working example (which doesnt "work", lol):
# ==[ Import ]============================================================
import tkinter as Tk
from tkinter import ttk
# ==[ Class definition ]==================================================
class FileHandlingFrame(Tk.LabelFrame):
# --< Initialization >------------------------------------------------
# Constructor
def __init__(self, container, *args, **kwargs):
Tk.LabelFrame.__init__(self, container, *args, **kwargs)
self.configure_self()
self.create_widgets()
pass
def configure_self(self):
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
self.grid_columnconfigure(2, weight=1)
self.grid_columnconfigure(3, weight=1)
self.grid_columnconfigure(4, weight=1)
self.grid_columnconfigure(5, weight=1)
self.grid_columnconfigure(6, weight=1)
self.grid_columnconfigure(7, weight=1)
self.pack()
pass
def create_widgets(self):
# Create ui components
self.importButton = ttk.Button(master=self, text="Import")
self.exportButton = ttk.Button(master=self, text="Export")
self.currentLabel = ttk.Label(master=self, text="Current")
self.createButton = ttk.Button(master=self, text="Create")
self.configurationButton = ttk.Button(master=self, text="s")
# Grid components
self.importButton.grid(row=0, column=0, columnspan=4, sticky="w")
self.exportButton.grid(row=0, column=4, columnspan=4, sticky="e")
self.currentLabel.grid(row=1, column=0, columnspan=8, sticky="w")
self.createButton.grid(row=2, column=0, columnspan=7)
self.configurationButton.grid(row=2, column=7, columnspan=1)
pass
# ==[ Start of main application ]=========================================
if __name__ == "__main__":
root = Tk.Tk()
root.resizable(width=False, height=False)
root.title("MWE")
mainframe = Tk.Frame(master=root)
mainframe.pack()
application = FileHandlingFrame(container=mainframe)
application.mainloop()
This is what I expect:
But this is what I get:
Anyone has any idea? I really am fighting this for hours already...
P.S.: I know I should do:
self.importButton.grid(row=0, column=0, columnspan=4, sticky="w")
self.exportButton.grid(row=0, column=4, columnspan=4, sticky="e")
Even though you've given a weight to every column, columns without anything in them will be empty and thus have no size. weight only affects how extra space is allocated, and doesn't affect the minimum or default size.
If you want all of the columns to be the same size, you can set the uniform option of every column to the same value. It doesn't matter what the value is as long as it is the same for every column.
The other problem is that you aren't using sticky properly to get the result from your drawing.
By the way, you can pass more than one column to columnconfigure:
self.grid_columnconfigure((0,1,2,3,4,5,6,7), weight=1, uniform="x")
...
self.importButton.grid(row=0, column=0, columnspan=4, sticky="ew")
self.exportButton.grid(row=0, column=4, columnspan=4, sticky="ew")
self.currentFileLabel.grid(row=1, column=0, columnspan=8, sticky="ew")
self.createHeatmapButton.grid(row=2, column=0, columnspan=7, sticky="ew")
self.heatmapConfigurationButton.grid(row=2, column=7, columnspan=1, sticky="ew")

Is their a grid_remember()? Reversible grid_forget()?

I am confused on the documentation surrounding the tkinter "grid_forget()"
I know that this function does not permanently delete the widget ascribed to it, however I do not know how to call it again. Further, if the widget is forgotten in a frame, can it be called back to the same the frame?
You can call grid() with no parameters to reverse the effects of grid_remove().
In the following example there is a label that is placed at the top of the window with grid. There is a toggle button that will alternate between calling grid and grid_remove to show that calling grid with no parameters will restore the message exactly as it was.
Notice, for example, that both the row, column, and columnspan attributes are remembered when the message reappears.
import tkinter as tk
class Example():
def __init__(self):
self.root = tk.Tk()
self.root.grid_rowconfigure(2, weight=1)
self.root.grid_columnconfigure(1, weight=1)
self.toolbar = tk.Frame(self.root)
self.toggle = tk.Button(self.toolbar, text="Toggle the message",
command=self.toggle_message)
self.toggle.pack(side="left")
# simulate a typical app with a navigation area on the left and a main
# working area on the right
self.navpanel = tk.Frame(self.root, background="bisque", width=100, height=200)
self.main = tk.Frame(self.root, background="white", width=300, height=200, bd=1, relief='sunken')
self.message = tk.Label(self.root, text="Hello, world!")
self.toolbar.grid(row=0, column=0, columnspan=2)
self.message.grid(row=1, column=0, columnspan=2)
self.navpanel.grid(row=2, column=0, sticky="nsew")
self.main.grid(row=2, column=1, sticky="nsew")
def start(self):
self.root.mainloop()
def toggle_message(self):
if self.message.winfo_viewable():
self.message.grid_remove()
else:
self.message.grid()
if __name__ == "__main__":
Example().start()
If you change the code from using grid_remove to using grid_forget, restoring the label will not put it back in the same place or with the same options. That is the main distinction between grid_remove and grid_forget -- grid_forget literally forgets the grid options whereas grid_remove removes the widget but remembers the settings.
Here is a simple example to illustrate what is happening when you remove a widget from the grid then re-grid it. You simply need to re apply the grid the same way you would have done in the first place. You can even chose a different grid location if you like. Though I am not sure if you can change the container it was originally assigned to. If not then it will only be able to be re-added to the original container the widget was assigned to.
import tkinter as tk
root = tk.Tk()
some_label = tk.Label(root, text="IM HERE!")
some_label.grid(row=0, column=0, columnspan=2)
def forget_label():
some_label.grid_forget()
def return_label():
some_label.grid(row=0, column=0, columnspan=2)
tk.Button(root, text="Forget Label", command=forget_label).grid(row=1, column=0)
tk.Button(root, text="Return Label", command=return_label).grid(row=1, column=1)
root.mainloop()

Python & ttk Using labelFrames to clean up a frame

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:

Encountered error; Tkinter GUI using python

I'm creating a simple Tkinter gui. However, something appears to be going haywire. Nothing is actually being 'pack'ed to the frame. Can anyone spot what I've done wrong? (Other than the issues caused by using 'from Tkinter import *', and the apparently un-useful 'do_nothing()' function.
#/usr/bin/python
from Tkinter import *
class gui:
def __init__(self, parent):
f = Frame(parent, width=300, height=500)
f.pack(padx=30, pady=15)
self.label = Label(f, text="Japanese Trainer")
self.label.pack(side=TOP, padx=10, pady=12)
self.txtlbl = Entry(f, justify=CENTER, text="", font=("Calibri", 15, "bold"), width=37)
self.txtlbl.pack()
self.txtlbl.grid(row=1, rowspan=2, sticky=E, pady=10, padx=40)
self.button0 = Button(f, text="Kana Trainer", width=20, command=self.do_nothing)
self.button0.pack()
self.button0.grid(row=3, rowspan=2, sticky=W, pady=10, padx=40)
self.button1 = Button(f, text="Vocab Trainer", width=20, command=self.do_nothing)
self.button1.pack()
self.button1.grid(row=3, rowspan=2, sticky=E, pady=10, padx=40)
def do_nothing(self):
self.txtlbl.delete(0, END)
self.txtlbl.insert(END, "Command did nothing...")
root = Tk()
root.title('Eg.')
app = gui(root)
root.mainloop()
You are mixing grid and pack in the same master window. You can't do that. Each one will potentially resize the widgets they manage, and each will respond to resizes in the widgets they manage. So, pack will resize the widgets to fit, grid will recognize the change and try to resize widgets to fit, pack will recognize the change and try to resize widgets to fit, ... resulting in an endless loop.
You can use pack and grid together in the same program, but you cannot use them to manage the same container.

Categories