I have been a programmer for 40 years, but mostly command-line and system programming. I am converting a tcl/tk gui that I wrote to Python because much of the stuff I did in shell scripts can be done effectively in Python, but I'm having a bit of trouble making the wigets line up (using grid) the way I want them. In my example, note the buttons line up perfectly. But the label/entry widget pairs are not lining up the way I want them: Note the workspace/project in row 3. I want the label to be adjacent to the entry widget and center. Also at the bottom I would like to have the scroll text widen to it fills the width of the screen. I just need a couple of pointer, maybe how best to group things in multiple frames. Also, I am assuming that the grid rows and columns are relational to the root windows and not the frame.
"""
PYGUITEST - This is a GUI test to test alignments of fields
Author: Jerry Feldman
"""
import sys
import os
import Tix
import tkMessageBox
import ScrolledText
import getpass
from Tkconstants import *
class pyguitest:
def __init__(self):
self.mainWindow = Tix.Tk()
self.mainWindow.tk.eval('package require Tix')
def __createUI(self, mainWindow):
mainWindow.protocol("WM_DELETE_WINDOW", self.OnExitWindow)
###### major class variables
self.username = getpass.getuser()
self.topFrame = Tix.Frame(self.mainWindow)
self.topRight = Tix.Frame(self.mainWindow)
### define buttons
self.butn01 = Tix.Button(self.topFrame,text=u"Butn01",
command=self.OnButn01ButtonClick)
self.butn02 = Tix.Button(self.topFrame,text=u"Butn02",
command=self.OnButn02ButtonClick)
self.butn03 = Tix.Button(self.topFrame,text=u"Butn03",
command=self.OnButn03ButtonClick)
self.butn04 = Tix.Button(self.topFrame,text=u"Butn04",
command=self.OnButn04ButtonClick)
self.butn05 = Tix.Button(self.topFrame,text=u"Butn05",
command=self.OnButn05ButtonClick)
self.butn06 = Tix.Button(self.topFrame,text=u"Butn06",
command=self.OnButn06ButtonClick)
self.butn07 = Tix.Button(self.topFrame,text=u"Butn07",
command=self.OnButn07ButtonClick)
self.butn08 = Tix.Button(self.topFrame,text=u"Butn08",
command=self.OnButn08ButtonClick)
self.Butn09 = Tix.Button(self.topFrame,text=u"Butn09",
command=self.OnButn09ButtonClick)
self.kwit = Tix.Button(self.topRight,text=u"quit !",
command=lambda:self.mainWindow.destroy())
### Set up the buttons grids for row 0
self.butn01.grid(column=0,row=0, sticky="nw")
self.butn02.grid(column=1,row=0, sticky="nw")
self.butn03.grid(column=2,row=0, sticky="nw")
self.butn04.grid(column=3,row=0, sticky="nw")
self.butn05.grid(column=4,row=0, sticky="nw")
self.butn06.grid(column=5,row=0, sticky="nw")
self.butn07.grid(column=6,row=0, sticky="nw")
self.butn08.grid(column=7,row=0, sticky="nw")
self.Butn09.grid(column=8,row=0, sticky="nw")
self.topFrame.grid(column=0,row=1,columnspan=8,sticky="NW")
self.kwit.grid(column=0,row=0,sticky="NE")
self.topRight.grid(column=10,row=1,columnspan=1,sticky="NE")
Tix.Label(mainWindow,text="This label should be centered").grid(row=2,sticky="nwes",columnspan=8)
wsFrame=Tix.Frame(mainWindow).grid(row=3, columnspan=9)
Tix.Label(wsFrame,text="Workspace:").grid(row=3,column=0, sticky="e")
wsEntry=Tix.Entry(wsFrame)
ws="/home/myuser/workspaces/pyguitest"
wsEntry.config(width=len(ws))
wsEntry.insert(0, ws)
wsEntry.grid(row=3, column=1)
Tix.Label(wsFrame,text="Project: ").grid(row=3,column=2, sticky="e")
self.prEntry=Tix.Entry(wsFrame)
pr=ws+"/project"
self.prEntry.grid(row=3,column=3, sticky="w")
self.prEntry.insert(0, pr)
self.prEntry.config(width=len(pr)+1)
Tix.Label(mainWindow,text="This should also be centered" ).grid(row=4,columnspan=8)
Tix.Label(mainWindow, text="ProductHome: ").grid(row=5)
ProductHome="/pmount/groupname/ProductHome/homeversion"
self.ProductHomeEntry = Tix.Entry(self.mainWindow)
self.ProductHomeEntry.delete(0, END)
self.ProductHomeEntry.insert(0, ProductHome)
self.ProductHomeEntry.configure(width=len(ProductHome)+1)
self.ProductHomeEntry.grid(row=5, column=1)
Tix.Label(mainWindow, text="Starting Port: ").grid(row=5,column=3, sticky="e")
portEntry=Tix.Entry(mainWindow)
portEntry.grid(row=5,column=4, sticky="w")
startingPort="20000"
portEntry.insert(0,startingPort)
portEntry.config(width=len(startingPort)+1)
Tix.Label(mainWindow, text="Product Version: ").grid(row=5,column=5, sticky="e")
pVersion="10.12.8 beta"
self.productVersionEntry=Tix.Entry(mainWindow)
self.productVersionEntry.grid(row=5, column=6, sticky="w")
self.productVersionEntry.insert(0, pVersion)
self.productVersionEntry.config(width=len(pVersion))
Tix.Label(mainWindow,text="Another centered label").grid(row=6, columnspan=8)
Tix.Label(mainWindow, text="Delta:").grid(row=7, column=0)
delta=ProductHome+"/delta"
self.deltaEntry=Tix.Entry(mainWindow)
self.deltaEntry.insert(0, delta)
self.deltaEntry.grid(row=7, column=1, sticky="w")
self.deltaEntry.configure(width=len(delta))
Tix.Label(mainWindow, text="Job to run: ").grid(row=7, column=2, sticky="e")
defaultJob=ProductHome+"/jobs/default.sh"
self.jobToRunEntry=Tix.Entry(mainWindow)
self.jobToRunEntry.grid(row=7, column=3, sticky="w")
self.jobToRunEntry.delete(0,END)
self.jobToRunEntry.insert(0, defaultJob)
self.jobToRunEntry.config(width=len(defaultJob))
defaultLauncher=ProductHome+"/launcher/default.sh"
Tix.Label(mainWindow, text="Launcher: ").grid(row=7, column=4, sticky="e")
self.launcherEntry=Tix.Entry(mainWindow)
self.launcherEntry.grid(row=7, column=5, sticky="w")
self.launcherEntry.delete(0,END)
self.launcherEntry.insert(0, defaultLauncher)
self.launcherEntry.config(width=len(defaultLauncher))
self.logger = ScrolledText.ScrolledText(self.mainWindow,
width = 160,
height = 60,
wrap=Tix.WORD,
relief="sunken")
self.logger.grid(row=10,columnspan=8)
def doLog(self, file):
for line in file.read().splitlines():
self.logger.insert(END, line+"\n")
txt.insert(END, "\nEnd of command\n\n")
def startpyguitest(self):
self.__createUI(self.mainWindow)
self.mainWindow.mainloop()
## Button Actions
def OnExitWindow(self):
self.mainWindow.destroy()
def OnButn01ButtonClick(self):
pass
def OnButn02ButtonClick(self):
pass
def OnButn03ButtonClick(self):
pass
def OnButn04ButtonClick(self):
pass
def OnButn05ButtonClick(self):
pass
def OnButn06ButtonClick(self):
pass
def OnButn07ButtonClick(self):
pass
def OnButn08ButtonClick(self):
pass
def OnButn09ButtonClick(self):
pass
def logIt(self, stdout):
for line in stdout.read().splitlines():
self.logger.insert(END, line+"\n")
self.logger.see(END)
if __name__ == "__main__":
pyguitest = pyguitest()
pyguitest.startpyguitest()
You need to make more use of the sticky option. There are a few widgets -- such as the scrolled text widget -- where they aren't told to stick to the edges of their cell.
You'll also want to use the grid_rowconfigure and grid_columnconfigure methods to get your GUI to resize properly.
You are incorrect about the grid rows and columns. They are relative to the frame. For example, you can have things in column 0 of an inner frame, and have that inner frame at column 2 of the root window.
I would use a .grid
btn.grid(row=0, column=0)
(change the #s as needed)
Related
I've switched from .grid() to .place() in my program, so I decided to remove a frame that contained the grid widgets:
BackButtonR = Button(registerPage, text="Back", command=lambda: show_frame(Menu))
BackButtonR.grid(row=0, column=0, sticky=W)
Button2F3 = Button(registerPage, text="Find")
Button2F3.grid(row=1, column=1)
Button3F3 = Button(registerPage, text="Calculate").grid(row=6, column=1)
LabelTitleF3 = Label(registerPage, text="Calculate Buy Price").grid(row=0, column=3)
label1F3 = Label(registerPage, text="Enter Ticker Symbol:").grid(row=1, column=0)
label2F3 = Label(registerPage, text="Expected CAGR").grid(row=2, column=0)
label3F3 = Label(registerPage, text="Years of Analysis").grid(row=3, column=0)
label4F3 = Label(registerPage, text="Expected PE Ratio").grid(row=4, column=0)
label5F3 = Label(registerPage, text="Desired Annual Return").grid(row=5, column=0)
entry1F3 = Entry(registerPage, width=7).grid(row=1, column=1, padx=0)
entry2F3 = Entry(registerPage).grid(row=2, column=1, pady=10, padx=0)
entry3F3 = Entry(registerPage).grid(row=3, column=1, pady=10, padx=0)
entry4F3 = Entry(registerPage).grid(row=4, column=1, pady=10, padx=0)
entry5F3 = Entry(registerPage).grid(row=, column=1, pady=10, padx=0)
But weirdly, when I rerun my program everything turns blank. This shouldn't happen, since I've removed any reference to .grid(), so the program should be working fine with .place(). Here is my full code:
print(220+135)
from tkinter import *
root = Tk()
root.title("Account Signup")
DarkBlue = "#2460A7"
LightBlue = "#B3C7D6"
root.geometry('350x230')
Menu = Frame(root)
loginPage = Frame(root)
registerPage = Frame(root)
for AllFrames in (Menu, loginPage, registerPage):
AllFrames.grid(row=0, column=0, sticky='nsew')
AllFrames.configure(bg=LightBlue)
def show_frame(frame):
frame.tkraise()
show_frame(Menu)
# ============= Menu Page =========
Menu.grid_columnconfigure(0, weight=1)
menuTitle = Label(Menu, text="Menu", font=("Arial", 25), bg=LightBlue)
menuTitle.place(x=130, y=25)
loginButton1 = Button(Menu, width=25, text="Login", command=lambda: show_frame(loginPage))
loginButton1.place(x=85, y=85)
registerButton1 = Button(Menu, width=25, text="Register", command=lambda: show_frame(registerPage))
registerButton1.place(x=85, y=115)
# ======== Login Page ===========
loginUsernameL = Label(loginPage, text='Username').place(x=30, y=60)
loginUsernameE = Entry(loginPage).place(x=120, y=60)
loginPasswordL = Label(loginPage, text='Password').place(x=30, y=90)
loginPasswordE = Entry(loginPage).place(x=120, y=90)
backButton = Button(loginPage, text='Back', command=lambda: show_frame(Menu)).place(x=0, y=0)
loginButton = Button(loginPage, text='Login', width=20).place(x=100, y=150)
# ======== Register Page ===========
root.mainloop()
Why is my program turning blank?
When you use pack and grid, these functions will normally adjust the size of a widget's parent to fit all of its children. It's one of the most compelling reasons to use these geometry managers.
When you use place this doesn't happen. If you use place to put a widget in a frame, the frame will not grow or shrink to fit the widget.
In your case you're creating Menu, loginPage and registerPage and not giving them a size so they default to 1x1 pixels. When you use place to add a widget to the frame, the frame will remain at 1x1 pixels, rendering it virtually invisible.
The solution is to either give these frames an explicit size, or add the frames to the window with options that cause them to fill the window.
For illustrative purposes I've changed the background color of the window to pink, and set the size of Menu to 200x200. As you can see in the following screenshot, the frame with the widgets is there, and becomes visible when you give it a larger size. Of course, one problem with place is it's up to you to calculate the appropriate size.
The better solution in this specific case would be to use the appropriate grid options to have the frames fill the window. You can do that by giving a weight to the row and column that the frames are in. Unused space in the parent frame will be allocated to the row and column with the widget.
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Generally speaking, grid and pack are superior to place for implementing most layouts because they are able to automatically make all widgets fit into a window with very little work. With place it's up to you to do calculations for position and size, and to make sure that all ancestors are appropriately sized and are visible.
You need to call root.grid_rowconfigure(0, weight=1) and root.grid_columnconfigure(0, weight=1) so that the shown frame use all the space of root window, otherwise the size of those frames are 1x1.
Also Menu.grid_columnconfigure(0, weight=1) is useless because widgets inside Menu are using .place().
So I'm trying to expand my LabelFrame named "Admin Frame" in the X and Y directions somewhat like the pack() system's fill=BOTH argument but it doesn't seem to be working. But I want to be able to do this with the grid()system because I have a complex interface.
This is my code for the LabelFrame:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Election App Mockup")
root.geometry("800x600")
root.resizable(0,0)
frameStyle = ttk.Style().configure("my.TLabelframe.Label")
adminFrame = ttk.LabelFrame(root, text="Admin Panel", style="my.TLabelframe")
adminWinLabel = ttk.Label(adminFrame, text="Welcome Admin")
voterOpBtn = ttk.Button(adminFrame, text="Configure Voters' List", style="my.TButton")
candidateOpBtn = ttk.Button(adminFrame, text="Configure Candidates' List", style="my.TButton")
setVoteSessionBtn = ttk.Button(adminFrame, text="Setup Voting Session", style="my.TButton")
startVoterSessionBtn = ttk.Button(adminFrame, text="Start a Voting Session", style="my.TButton", padding=(25,3,25,3))
adminSettingBtn = ttk.Button(adminFrame, text="Admin Settings", style="my.TButton", )
adminLogoutBtn = ttk.Button(adminFrame, text="Logout", style="my.TButton", padding=(20,3,20,3))
adminWinLabel.grid(row=0, column=0, columnspan=2, pady=(170,5), padx=(150,0))
voterOpBtn.grid(row=1, column=0, padx=(150,5), pady=(10,10), ipadx=15)
candidateOpBtn.grid(row=1, column=1, padx=(10,5), pady=(10,10))
setVoteSessionBtn.grid(row=2, column=0, padx=(150,5), pady=(10,10), ipadx=15)
startVoterSessionBtn.grid(row=2, column=1, padx=(10,5), pady=(10,10))
adminSettingBtn.grid(row=3, column=0, padx=(130,0), pady=(10,10), columnspan=2)
adminLogoutBtn.grid(row=4, column=0, padx=(130,0), pady=(10,10), columnspan=2)
adminFrame.grid(row=0, column=0, sticky=NSEW)
adminFrame.grid_rowconfigure(0, weight=1)
adminFrame.grid_columnconfigure(0, weight=1)
root.mainloop()
I've tried adding extra arguments like ipadx and ipady in but it doesn't work:
adminFrame.grid(row=0, column=0, sticky=NSEW, ipadx=200, ipady=20)
Adding a padding argument in the adminFrame does work but it is very tricky to work with just to expand the frame to the window's full length and breadth.
Thanks in advance! Oh and do note that I did not learn object oriented programming in python, so I won't understand any answers using the class system.
So I'm trying to expand my LabelFrame named "Admin Frame" in the X and Y directions somewhat like the pack() system's fill=BOTH argument but it doesn't seem to be working.
As a rule of thumb, any time you use grid to manage widgets, you should give at least one row and one column a weight greater than zero. You are putting adminFrame in the root window with grid, but you haven't given any rows or columns in the root window a weight.
So, if you want adminFrame to expand to fill the window, you need to give a weight of 1 to row 0 and column 0. The other thing that needs to be done -- that you are already doing -- is to set the sticky attribute so that the frame "sticks" to all four sides of the space allocated to it.
adminFrame.grid(row=0, column=0, sticky=NSEW)
root.grid_rowconfigure(0, weight=1)
root.grid_columnconfigure(0, weight=1)
Look at this:
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Election App Mockup")
root.geometry("800x600")
# root.resizable(False, False)
frameStyle = ttk.Style().configure("my.TLabelframe.Label")
adminFrame = ttk.LabelFrame(root, text="Admin Panel", style="my.TLabelframe")
adminWinLabel = ttk.Label(adminFrame, text="Welcome Admin")
# A frame for the centre 4 buttons
centre_buttons_frame = Frame(adminFrame)
voterOpBtn = ttk.Button(centre_buttons_frame, text="Configure Voters' List", style="my.TButton")
candidateOpBtn = ttk.Button(centre_buttons_frame, text="Configure Candidates' List", style="my.TButton")
setVoteSessionBtn = ttk.Button(centre_buttons_frame, text="Setup Voting Session", style="my.TButton")
startVoterSessionBtn = ttk.Button(centre_buttons_frame, text="Start a Voting Session", style="my.TButton", padding=(25,3,25,3))
adminSettingBtn = ttk.Button(adminFrame, text="Admin Settings", style="my.TButton", )
adminLogoutBtn = ttk.Button(adminFrame, text="Logout", style="my.TButton", padding=(20,3,20,3))
# I think the buttons looks better when they have an equal size so I added
# sticky="ew" to expand them in the horizontal direction.
voterOpBtn.grid(row=0, column=0, sticky="ew", padx=(0,15), pady=10, ipadx=15)
candidateOpBtn.grid(row=0, column=1, sticky="ew", padx=(15,0), pady=10)
setVoteSessionBtn.grid(row=1, column=0, sticky="ew", padx=(0,15), pady=10, ipadx=15)
startVoterSessionBtn.grid(row=1, column=1, sticky="ew", padx=(15,0), pady=10)
adminWinLabel.grid(row=1, column=1, pady=(0,5))
centre_buttons_frame.grid(row=2, column=1)
adminSettingBtn.grid(row=3, column=1, pady=10)
adminLogoutBtn.grid(row=4, column=1, pady=(10,0))
# `adminFrame` is packed in the root so it can expand. There should be a way
# to make it work with .grid, but .pack is easier in this case
adminFrame.pack(fill="both", expand=True)
# Expand the 0th and 6th row - there are no widgets
adminFrame.grid_rowconfigure((0, 6), weight=1)
adminFrame.grid_columnconfigure((0, 3), weight=1)
root.mainloop()
It looks like this on my computer:
I added a frame for the centre 4 buttons so that I can avoid columnspan. Also I used adminFrame.pack(fill="both", expand=True) because it is easier than using .grid_propagate(False) and manually setting the width and height of the frame.
Right now, the GUI should look good with any window size.
I need to take a value from user and then pass this value into function to get a string with this values. My code in first file looks like below (of course it is simplified version):
import tkinter as tk
from tkinter import ttk
import myfunction # this is my module that has another function
class Interface(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_price_minimum = tk.StringVar()
minimum_price = ttk.Label(self, text="Minimum price is: ")
minimum_price.grid(row=0, column=0, padx=5, pady=5)
minimum_price_entry = ttk.Entry(self, width=15, textvariable = self.user_price_minimum)
minimum_price_entry.grid(row=0, column=1)
minimum_price_entry.focus()
#############
button = ttk.Button(self, text="Use price")
button.grid(column=0, rows=2, columnspan=2, padx=5, pady=5)
root = tk.Tk()
root.geometry("450x250")
root.title("Looking for a flat")
root.columnconfigure(0, weight=1)
frame = Interface(root)
frame.pack()
root.mainloop()
My another python file that calls myfunction.py should be able to take this minimum_price from user and add this into string. Code looks like below:
def minimum_price(self):
price_min = self.user_price_minimum.get()
price_min = int(price_min)
print(f'Minimum price is: {price_min}')
So I am not sure how I could use minimum_price values from user into this function.
The simplest is to do this:
button = ttk.Button(self, text="Use price", command=lambda: myfunction.minimum_price(self))
However you could also define it as a method in the class itself:
class Interface(ttk.Frame):
def __init__(self, container):
super().__init__(container)
self.user_price_minimum = tk.StringVar()
minimum_price = ttk.Label(self, text="Minimum price is: ")
minimum_price.grid(row=0, column=0, padx=5, pady=5)
minimum_price_entry = ttk.Entry(self, width=15, textvariable = self.user_price_minimum)
minimum_price_entry.grid(row=0, column=1)
minimum_price_entry.focus()
#############
button = ttk.Button(self, text="Use price", command=self.print_price)
button.grid(column=0, rows=2, columnspan=2, padx=5, pady=5)
def print_price(self):
price = self.user_price_minimum.get()
print(f'Minimum price: {price}')
My personal preference (when using another file in such a case like this one (tho I would probably prefer to define this as above)) would be if:
# in the other file
def minimum_price(value):
print(f'Minimum price: {value}')
# inside the class
button = ttk.Button(self, text="Use price", command=lambda: myfunction.minimum_price(self.user_price_minimum.get()))
Also in this case you don't necessarily need the StringVar, you could also simply get the value by using:
# assignment
self.minimum_price_entry = ttk.Entry(self, width=15)
# get value (probably in some function call, basically the same way as with the `StringVar` except less code)
self.minimum_price_entry.get()
I'm making a DBMS in Tkinter and I want to have a button that will delete the corresponding entry (the entry to the right of it). How would the computer know what button is sending the command or is linked to what entry (or how do I link them)
Take a look at the following code
def edit_db():
for i in root.winfo_children():
i.grid_remove()
back_btn = Button(root, text='<--', command=home)
back_btn.place(height=30, width=30, x=4, y=4)
row_count = 1
for i in cur.execute("SELECT * FROM students").fetchall():
save_btn = Button(root, text='Save', font=("Calbri", 9))
if row_count == 1: save_btn.grid(row=row_count, column=1, pady=(45, 3), padx=(5, 10))
else: save_btn.grid(row=row_count, column=1, pady=3, padx=(5, 10))
del_btn = Button(root, text="X", font=("Calbri", 9), command=del_entry)
if row_count == 1: del_btn.grid(row=row_count, column=0, pady=(45, 3), padx=5)
else: del_btn.grid(row=row_count, column=0, pady=3, padx=(5))
As you can see their are multiple del_btn (and save_btn) and their variable names will no longer correspond, but I want do something like
del_btn = Button(root, text="X", font=("Calbri", 9), command=del_entry(**self/this/me**))
Is there something I can do? Do I have to do this all in a class (I'm not very good a OOP so I don't know what difference it would make)? Or am I missing something and their is way to link each button with the entries in the database.
Here is my picture of my app, if it will help
Here is the solution (includes 3 options):
from tkinter import Tk, Button, Label
label_list = []
def destroy_widget(index):
label_list[index].destroy()
root = Tk()
for i in range(10):
lbl = Label(root, text=f'Label {i}')
lbl.grid(row=i, column=0, sticky='nsew')
label_list.append(lbl)
Button(root, text=f'Destroy {i}',
command=lambda index=i: destroy_widget(index)).grid(row=i, column=1, sticky='nsew')
# option using partial (place import at the top)
# from functools import partial
# Button(root, text=f'Destroy {i}',
# command=lambda: partial(destroy_widget, i).grid(row=i, column=1, sticky='nsew')
# option defining function here (if so then the one at the top isn't needed)
# def destroy_widget(index=i):
# label_list[index].destroy()
# Button(root, text=f'Destroy {i}',
# command=destroy_widget).grid(row=i, column=1, sticky='nsew')
root.mainloop()
As I said, put the widgets in a list, then save index (probably better use a dictionary and save the key so that they can be deleted altogether with their instance, otherwise you can't delete them from the list because that should change other indexes which is gonna mess things up) to the function you are passing to the button so that they "remember" it, I provided 3 different (slightly) ways to do this, one is using anonymous functions, one is using partial, one is using normal functions
I'm pretty new to Tkinter and I build a little window with different widgets.
My Code looks like this:
import tkinter as tk
from tkinter import ttk
class Application(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.master.geometry("800x600")
self.master.title("Tkinter Sandbox")
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(1, weight=1)
self._create_left_frame()
self._create_button_bar()
self._create_label_frame()
def _create_left_frame(self):
frame = tk.Frame(self.master, bg="red")
tree_view = ttk.Treeview(frame)
tree_view.column("#0", stretch=tk.NO)
tree_view.heading("#0", text="Treeview")
tree_view.pack(fill=tk.Y, expand=1)
frame.grid(row=0, column=0, rowspan=2, sticky=tk.N + tk.S)
def _create_button_bar(self):
frame = tk.Frame(self.master, bg="blue")
button_run_single = tk.Button(frame, text="Button 1")
button_run_all = tk.Button(frame, text="Button 2")
button_details = tk.Button(frame, text="Button 3")
button_run_single.grid(row=0, column=0)
button_run_all.grid(row=0, column=1, padx=(35, 35))
button_details.grid(row=0, column=2)
frame.grid(row=0, column=1, sticky=tk.N)
def _create_label_frame(self):
frame = tk.Frame(self.master, bg="blue")
name_label = tk.Label(frame, text="Label 1")
performance_label = tk.Label(frame, text="Label 2")
name_entry = tk.Entry(frame)
performance_entry = tk.Entry(frame)
name_label.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
performance_label.grid(row=1, column=0)
performance_entry.grid(row=1, column=1)
frame.grid(row=1, column=1)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.mainloop()
Between the three buttons and the label + entry frame is a huge space. I want the button and label + entry frame right under each other, without the huge space but the treeview should also expand vertically over the whole application window.
I think the problem might be my row and column configuration but I don't know how to solve this problem.
The way you've structured your code makes it hard to see the problem. As a good general rule of thumb, all calls to grid or pack for widgets within a single parent should be in one place. Otherwise, you create dependencies between functions that are hard to see and understand.
I recommend having each of your helper functions return the frame rather than calling grid on the frame. That way you give control to Application.__init__ for the layout of the main sections of the window.
For example:
left_frame = self._create_left_frame()
button_bar = self._create_button_bar()
label_frame = self._create_label_frame()
left_frame.pack(side="left", fill="y")
button_bar.pack(side="top", fill="x")
label_frame.pack(side="top", fill="both", expand=True)
I used pack here because it requires less code than grid for this type of layout. However, if you choose to switch to grid, or wish to add more widgets to the root window later, you only have to modify this one function rather than modify the grid calls in multiple functions.
Note: this requires that your functions each do return frame to pass the frame back to the __init__ method. You also need to remove frame.grid from each of your helper functions.
With just that simple change you end up with the button bar and label/entry combinations at the top of the section on the right. In the following screenshot I changed the background of the button_bar to green so you can see that it fills the top of the right side of the UI.
You need to change line
self.master.grid_rowconfigure(0, weight=1)
to
self.master.grid_rowconfigure(1, weight=1)
so that the second row takes all the space. Then you need to stick widgets from the label frame to its top by adding sticky parameter to the grid call in _create_label_frame:
frame.grid(row=1, column=1, sticky=tk.N)
I prefer to use the Pack Function since it gives a more open window - its easy to configure. When you use Pack() you can use labels with no text and just spaces to create a spacer, by doing this you won't run into the problem your facing.