tkinter Frame Layout with grid and pack - python

I have a layout with Frame and pack some widwets inside. This works so fare. But for the treeview widget it doesn't work and gives a strange error:
Display Names in the Treeview doesn't work with pack tkinter.TclError:
cannot use geometry manager pack inside . which already has slaves
managed by grid
Need help from experienced tkinter user.
Here my layout:
here, if I try the same with a treeview instead of a label widget:
here is my program:
import tkinter as tk
from tkinter import ttk
# Main App
win = tk.Tk()
win.title('Layout Test')
win.geometry('1200x720+300+300')
win.resizable(True, True)
# Frame Design
top_frame = tk.Frame(win, background="#FFF0C1", bd=1, relief="sunken")
left_frame = tk.Frame(win, background="#D2E2FB", bd=1, relief="sunken")
center_frame = tk.Frame(win, background="#CCE4CA", bd=1, relief="sunken")
right_frame = tk.Frame(win, background ='lightblue', bd=1, relief='sunken')
bottom_frame = tk.Frame(win, background="#F5C2C1", bd=1, relief="sunken")
top_frame.grid(row=0, column=0, columnspan=3, sticky="nsew", padx=2, pady=2)
left_frame.grid(row=1, column=0, sticky="nsew", padx=2, pady=2)
center_frame.grid(row=1, column=1, sticky="nsew", padx=2, pady=2)
right_frame.grid(row=1, column=2, sticky="nsew", padx=2, pady=2)
bottom_frame.grid(row=3, column=0, columnspan=3, sticky="nsew", padx=2, pady=2)
win.grid_rowconfigure(0, weight=6)
win.grid_rowconfigure(1, weight=40)
win.grid_rowconfigure(3, weight=1)
win.grid_columnconfigure(0, weight=1)
win.grid_columnconfigure(1, weight=2)
win.grid_columnconfigure(2, weight=4)
# Display Names in the Treeview doesn't work with pack
# tkinter.TclError: cannot use geometry manager pack inside .
# which already has slaves managed by grid
treeview = ttk.Treeview(win)
treeview.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
treeview.insert('center_frame','0','item1', text = 'First item',tag='T' )
treeview.insert('center_frame','1','item2', text = 'Second item',tag='T' )
treeview.insert('center_frame','2','item3', text = 'Third item',tag='T' )
treeview.insert('center_frame','3','item4', text = 'Forth item',tag='T' )
treeview.insert('center_frame','end','item5', text = 'Five item',tag='T' )
treeview.insert('item1','end','item6', text = 'Sechster Text',tag='T' )
treeview.tag_configure('T', font=('Calibre', 15))
"""
# Test works well with pack
MyLabel = tk.Label(center_frame,text="Label inside Frame1")
MyLabel.pack(side=tk.LEFT, fill=tk.BOTH, expand=1)
"""
# Program here
# Status Bar at bottom_frame works well with pack
statustext = tk.StringVar()
statustext.set(' ... choose your avm_xml file') # will be changed from file dialoge
status = ttk.Label(bottom_frame, textvariable=statustext, borderwidth='25', relief=tk.SUNKEN, anchor=tk.W)
status.pack(side=tk.BOTTOM, fill=tk.X) #fill='both', expand=False, padx=8, pady=8
win.mainloop()

treeview has win as master, so when you try to pack it you get an error since you used grid for the other widgets in win. This is because the layout managers pack and grid cannot be used simultaneously in the same master widget.
On the other hand, your test label's master is center_frame, so you can pack it inside. If you change your treeview's master to center_frame, you will be able to pack it like the label.
import tkinter as tk
from tkinter import ttk
# Main App
win = tk.Tk()
win.title('Layout Test')
win.geometry('1200x720+300+300')
win.resizable(True, True)
# Frame Design
top_frame = tk.Frame(win, background="#FFF0C1", bd=1, relief="sunken")
left_frame = tk.Frame(win, background="#D2E2FB", bd=1, relief="sunken")
center_frame = tk.Frame(win, background="#CCE4CA", bd=1, relief="sunken")
right_frame = tk.Frame(win, background ='lightblue', bd=1, relief='sunken')
bottom_frame = tk.Frame(win, background="#F5C2C1", bd=1, relief="sunken")
top_frame.grid(row=0, column=0, columnspan=3, sticky="nsew", padx=2, pady=2)
left_frame.grid(row=1, column=0, sticky="nsew", padx=2, pady=2)
center_frame.grid(row=1, column=1, sticky="nsew", padx=2, pady=2)
right_frame.grid(row=1, column=2, sticky="nsew", padx=2, pady=2)
bottom_frame.grid(row=3, column=0, columnspan=3, sticky="nsew", padx=2, pady=2)
win.grid_rowconfigure(0, weight=6)
win.grid_rowconfigure(1, weight=40)
win.grid_rowconfigure(3, weight=1)
win.grid_columnconfigure(0, weight=1)
win.grid_columnconfigure(1, weight=2)
win.grid_columnconfigure(2, weight=4)
treeview = ttk.Treeview(center_frame) # <-- changed master from win to center_frame
treeview.pack(side=tk.LEFT, fill=tk.BOTH, expand=1) # <-- pack works now
# Program here
# Status Bar at bottom_frame works well with pack
statustext = tk.StringVar()
statustext.set(' ... choose your avm_xml file') # will be changed from file dialoge
status = ttk.Label(bottom_frame, textvariable=statustext, borderwidth='25', relief=tk.SUNKEN, anchor=tk.W)
status.pack(side=tk.BOTTOM, fill=tk.X) #fill='both', expand=False, padx=8, pady=8
win.mainloop()

As far as I am concerned, if you change treeview.pack() to treeview.grid(sticky=W)etc it might work. You can't have both pack and grid in the same widget.

Related

incorrect button width using grid in tkinter

Somehow the buttons placed using grid method will be much bigger than the cell width defined by columnconfigure. I tried to place some test labels in the same way, and it worked as expected. Is there any way to get correct button width like these labels did?
Below is the simplified code to the question:
import tkinter as tk
from tkinter.ttk import *
root = tk.Tk()
style = Style()
style.theme_use('clam')
style.configure('TestLabel1.TLabel', background='red')
style.configure('TestLabel2.TLabel', background='green')
style.configure('TestLabel3.TLabel', background='blue')
style.configure('Buttons.TButton')
output_path = tk.StringVar()
output_path.set('test')
settings = Frame(root, width=500, height=200)
settings.pack_propagate(0)
settings.pack(side='top')
chart_preset_frame = LabelFrame(settings, text='Chart Parameter Preset', padding=(5, 5, 5, 5))
chart_preset_frame.pack(expand=True, fill='x', pady=10, side='top')
chart_preset_frame.columnconfigure(0, weight=6)
chart_preset_frame.columnconfigure(1, weight=2)
chart_preset_frame.columnconfigure(2, weight=2)
output_path_input = Entry(chart_preset_frame, textvariable=output_path, width=20)
output_path_input.grid(row=0, column=0, columnspan=3, sticky='EW', ipady=5)
output_load_btn = Button(chart_preset_frame, text='Load...', style='Buttons.TButton', command=None)
output_load_btn.grid(row=1, column=1, sticky='EW', padx=0, pady=5)
output_save_btn = Button(chart_preset_frame, text='Save As...', style='Buttons.TButton', command=None)
output_save_btn.grid(row=1, column=2, sticky='EW', padx=0, pady=5)
# for label test
# label_test_1 = Label(chart_preset_frame, text='test', style='TestLabel1.TLabel')
# label_test_1.grid(row=1, column=0, sticky='EW')
# label_test_2 = Label(chart_preset_frame, text='test', style='TestLabel2.TLabel')
# label_test_2.grid(row=1, column=1, sticky='EW')
# label_test_3 = Label(chart_preset_frame, text='test', style='TestLabel3.TLabel')
# label_test_3.grid(row=1, column=2, sticky='EW')
root.mainloop()
weight is just a guideline to the layout manager how to distribute available spaces. Since there is no widget in cell (row 1, column 0), it may not get the required space.
It can be enforced by adding uniform=1 to those columnconfigur(...):
chart_preset_frame.columnconfigure(0, weight=6, uniform=1)
chart_preset_frame.columnconfigure(1, weight=2, uniform=1)
chart_preset_frame.columnconfigure(2, weight=2, uniform=1)
Result:

Why is columnspan not recognized? (Tiknter) The size of the button does not change

I was wondering why when I use the columnspan parameter in the .grid() function the button does not change size. Should it not span all columns within the frame? Below I have my code and the output. Thanks all.
# Import Modules
from tkinter import ttk
import tkinter as tk
import os
# Basic Structure
root = tk.Tk()
root.minsize(600, 700)
root.maxsize(600, 700)
root.title("Training HUB")
root.config(bg="slategray")
# Create Frames - Title, Left and Right Frames
title_frame = tk.Frame(root, width=580, height=50, bg='white', highlightbackground="black", highlightthickness=1)
title_frame.grid(row=0, columnspan=2, padx=10, pady=5)
left_frame = tk.Frame(root, width=280, height=550, bg='white', highlightbackground="black", highlightthickness=1)
left_frame.grid(row=1, column=0, padx=10, pady=5)
left_frame.grid_propagate(0)
right_frame = tk.Frame(root, width=280, height=550, bg='white', highlightbackground="black", highlightthickness=1)
right_frame.grid(row=1, column=1, padx=10, pady=5)
right_frame.grid_propagate(0)
# Buttons
button1 = ttk.Button(right_frame, text="Run Processing").grid(
row=0, column=0, columnspan = 2, padx=5, pady=5, sticky = 'ew')
# Run Application
root.mainloop()
Tkinter Output:

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

Python Tkinter grid spacing of widgets and LablelFrames not right

I am designing a simple GUI in Python 2.7 Tkinter, but I can't get things to spread out as I want them. I have managed to get my various widgets roughly where I want them, however I can't seem to force spacing out and things are a little bunched up.
I have also tried to draw 3 LabelFrames to separate the window out, but widgets seem to fall over the LabelFrames. I am wondering how I can space this out a little better. The grid system seems to allow things to bunch up and ignores blank rows and columns as far as I can see.
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog, tkMessageBox
class FileZap():
def __init__(self, root):
root.title("TestGUI")
root.geometry("860x450")
self.topFrame = LabelFrame(root, text="Top Area")
self.topFrame.grid(row=1, column=1, rowspan=6, columnspan=7, padx=5, pady = 5, sticky="NSEW")
self.listbox1 = Listbox(root, width=50, selectmode="multiple")
self.listbox1.grid(row=3, column=2)
self.scrollbar = Scrollbar(orient=VERTICAL, command=self.listbox1.yview)
self.listbox1.config(yscrollcommand=self.scrollbar.set)
self.scrollbar.grid(row=3, column=3, sticky="ns")
self.listbox2 = Listbox(root, width=50)
self.listbox2.grid(row=3, column=4)
self.selectLabel = Label(root, text="Select a folder: ")
self.selectLabel.grid(row=3, column=1)
self.user1 = Entry(root, width="50")
self.user1.grid(row=2, column=2)
self.browse = Button(root, text="Browse")
self.browse.grid(row=2, column=3)
self.addItems = Button(root, text="Add to Selection")
self.addItems.grid(row=4, column=2)
self.clearItems = Button(root, text="Clear Selection")
self.clearItems.grid(row=4, column=4)
self.leftFrame = LabelFrame(root, text="Left Area")
self.leftFrame.grid(row=5, column=1, rowspan=6, columnspan=3, padx=5, pady = 5, sticky="NSEW")
self.replaceInLable = Label(root, text="String to replace: ")
self.replaceOutLable = Label(root, text="New string: ")
self.replaceInLable.grid(row=7, column=1)
self.replaceOutLable.grid(row=7, column=2)
self.replaceIn = Entry(root, width="20")
self.replaceOut = Entry(root, width="20")
self.replaceIn.grid(row=8, column=1)
self.replaceOut.grid(row=8, column=2)
self.replace = Button(root, text="Replace")
self.replace.grid(row=8,column=3)
self.rightFrame = LabelFrame(root, text="Right Area")
self.rightFrame.grid(row=5, column=4, rowspan=6, columnspan=3, padx=5, pady = 5, sticky="NSEW")
self.quit = Button(root, text="Exit", command=root.quit)
self.quit.grid(row=9, column=6)
root = Tkinter.Tk()
file_zap = FileZap(root)
root.mainloop()
I have tried various alterations but can't nail it! Any help would be much appreciated.
First, the columns / row adapt to there content so an empty one as a zero height/width. If you want to put space between your widgets use the padx and pady options in the .grid method. They can take either one number which will give the padding on both sides or a couple of numbers giving the padding on each side.
Secondly, if you want your widgets to be inside a LabelFrame, you need to create them with this LabelFrame as master instead of the main window.
from Tkinter import LabelFrame, Tk, Button, Label
root = Tk()
# make row 0 resize with the window
root.rowconfigure(0, weight=1)
# make column 0 and 1 resize with the window
root.columnconfigure(0, weight=1)
root.columnconfigure(1, weight=1)
# create LabelFrames
top_frame = LabelFrame(root, text="top")
left_frame = LabelFrame(root, text="left")
right_frame = LabelFrame(root, text="right")
top_frame.grid(row=0, column=0, columnspan=2, padx=10, pady=(10,4), sticky="nsew")
left_frame.grid(row=1, column=0, padx=(10,4), pady=4, sticky="nsew")
right_frame.grid(row=1, column=1, padx=(4,10), pady=4, sticky="nsew")
#create widgets inside top_frame
Label(top_frame, text="I'm inside top_frame").pack()
Button(top_frame, text="Top").pack()
#create widgets inside left_frame
Label(left_frame, text="I'm inside left_frame").pack()
Button(left_frame, text="Left").pack()
#create widgets inside top_frame
Label(right_frame, text="I'm inside right_frame").pack()
Button(right_frame, text="Right").pack()
Button(root, text="Quit", command=root.destroy).grid(row=2, column=0,
columnspan=2, pady=10)
root.mainloop()

More than one text box on a gui

I am newbie in python tkinter,I want to have some 3 textbox of each having some 4 lines to print text on a single GUI.
I tried a code which creates just only one text box that wraps to the entire screen.i also wanted to number the 3 textboxes as 1,2,3 on its left side as label.Please help me to complete my code!
import tkinter as tki # Tkinter -> tkinter in Python3
class App(object):
def __init__(self):
self.root = tki.Tk()
# create a Frame for the Text and Scrollbar
txt_frm = tki.Frame(self.root, width=600, height=600)
txt_frm.pack(fill="both", expand=True)
# ensure a consistent GUI size
txt_frm.grid_propagate(False)
# implement stretchability
txt_frm.grid_rowconfigure(0, weight=1)
txt_frm.grid_columnconfigure(0, weight=1)
# create a Text widget
self.txt = tki.Text(txt_frm, borderwidth=3, relief="sunken")
self.txt.config(font=("consolas", 12), undo=True, wrap='word')
self.txt.grid(row=0, column=0, sticky="nsew", padx=2, pady=2)
# create a Scrollbar and associate it with txt
scrollb = tki.Scrollbar(txt_frm, command=self.txt.yview)
scrollb.grid(row=0, column=1, sticky='nsew')
self.txt['yscrollcommand'] = scrollb.set
app = App()
app.root.mainloop()
Try this
import tkinter as tki # Tkinter -> tkinter in Python3
class App(object):
def __init__(self,root):
self.root = root
# create a Frame for the Text and Scrollbar
txt_frm = tki.Frame(self.root, width=600, height=400)
txt_frm.pack(fill="both", expand=True)
# ensure a consistent GUI size
txt_frm.grid_propagate(False)
# create first Text label, widget and scrollbar
self.lbl1 = tki.Label(txt_frm, text="1")
self.lbl1.grid(row=0,column=0,padx=2,pady=2)
self.txt1 = tki.Text(txt_frm, borderwidth=3, relief="sunken", height=4,width=55)
self.txt1.config(font=("consolas", 12), undo=True, wrap='word')
self.txt1.grid(row=0, column=1, sticky="nsew", padx=2, pady=2)
scrollb1 = tki.Scrollbar(txt_frm, command=self.txt1.yview)
scrollb1.grid(row=0, column=2, sticky='nsew')
self.txt1['yscrollcommand'] = scrollb1.set
# create second Text label, widget and scrollbar
self.lbl2 = tki.Label(txt_frm, text="2")
self.lbl2.grid(row=1,column=0,padx=2,pady=2)
self.txt2 = tki.Text(txt_frm, borderwidth=3, relief="sunken",height=4,width=55)
self.txt2.config(font=("consolas", 12), undo=True, wrap='word')
self.txt2.grid(row=1, column=1, sticky="nsew", padx=2, pady=2)
scrollb2 = tki.Scrollbar(txt_frm, command=self.txt2.yview)
scrollb2.grid(row=1, column=2, sticky='nsew')
self.txt2['yscrollcommand'] = scrollb2.set
# create third Text label, widget and scrollbar
self.lbl3 = tki.Label(txt_frm, text="3")
self.lbl3.grid(row=2,column=0,padx=2,pady=2)
self.txt3 = tki.Text(txt_frm, borderwidth=3, relief="sunken",height=4,width=55)
self.txt3.config(font=("consolas", 12), undo=True, wrap='word')
self.txt3.grid(row=2, column=1, sticky="nsew", padx=2, pady=2)
scrollb3 = tki.Scrollbar(txt_frm, command=self.txt3.yview)
scrollb3.grid(row=2, column=2, sticky='nsew')
self.txt3['yscrollcommand'] = scrollb3.set
root = tki.Tk()
app = App(root)
root.mainloop()

Categories