Python 3: Tkinter and updating frames - python

The problem I'm currently stuck with is that when I add a new shift I can't get the GUI to update properly. Currently the best I can seem to get is getting the updated information but it is displayed on top of the old information.
I did read about the whole mainloop() issue and tried to get use the after() method but I'm not sure if thats what I'm looking for:
class ViewShifts(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
buttonFrame = tk.Frame(self)
buttonFrame.pack(side='bottom', fill='both', expand=True)
#frame to display shifts
table = tk.Frame(self)
table.pack(side='top', fill='both', expand=True)
table.destroy()
#dictionary which will contain widgets that will display shifts (in viewNew method)
self.widgets = {}
button1= tk.Button(self, text="Home",
command=lambda: controller.showFrame(StartPage))
button1.pack(padx=0, pady=10, side='bottom')
button2 = tk.Button(self, text='Update',
command=lambda: deleteShiftFrame(self))
button2.pack(padx=0, pady=10, side='bottom')
text = tk.Label(self, text="All shifts", font=LARGEFONT)
text.pack(padx=10, side='top')
def deleteShiftFrame(self):
table.destroy()
viewNew(self)
def viewNew(self):
#frame to display shifts
table = tk.Frame(self)
table.pack(side='top', fill='both', expand=True)
row = 0
#make dictionary empty
self.widgets = {}
c.execute("SELECT * FROM shifts")
data = c.fetchall()
#create labels for each column
date_label = tk.Label(table, text="Date")
shift_label=tk.Label(table, text="Shift")
shop_label=tk.Label(table, text="Shop")
hours_label=tk.Label(table, text="Hours")
#add labels to grid
date_label.grid(row=0, column=0, sticky="nsw")
shift_label.grid(row=0, column=1, sticky="nsw")
shop_label.grid(row=0, column=2, sticky="nsw")
hours_label.grid(row=0, column=3, sticky="nsw")
#for each column create a tk label for each row within column with corresponding details
for id, date, shift, shop, hours in (data):
row+=1
self.widgets[id] = {
"id":tk.Label(table, text=id),
"date":tk.Label(table, text=date),
"shift":tk.Label(table, text=shift),
"shop":tk.Label(table, text=shop),
"hours":tk.Label(table, text=hours)
}
#add current row of column to grid
self.widgets[id]["date"].grid(row=row, column=0, sticky="nsw")
self.widgets[id]["shift"].grid(row=row, column=1, sticky="nsw")
self.widgets[id]["shop"].grid(row=row, column=2, sticky="nsw")
self.widgets[id]["hours"].grid(row=row, column=3, sticky="nsw")
#add blank column between each field for spacing
table.grid_columnconfigure(0, weight=1)
table.grid_columnconfigure(1, weight=1)
table.grid_columnconfigure(2, weight=1)
table.grid_columnconfigure(3, weight=1)
#add extra row for padding
table.grid_rowconfigure(row+1, weight=1)
viewNew(self)
class AddShifts(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
text = tk.Label(self, text = "Add shifts", font=LARGEFONT)
text.pack(padx=10, side = 'top')
#create frame for entry
AddShiftFrame = tk.Frame(self)
AddShiftFrame.pack(side="top", fill="both", expand=True)
#add column headers
dateLabel = tk.Label(AddShiftFrame, text="Date")
shiftLabel = tk.Label(AddShiftFrame, text="Shift")
shopLabel = tk.Label(AddShiftFrame, text='Shop')
dateLabel.grid(row=1, column=0, sticky="nsw")
shiftLabel.grid(row=1, column=1, sticky="nsw")
shopLabel.grid(row=1, column=2, sticky="nsw")
#create dictionary of widgets
self.widgets = {
"Date":tk.Entry(AddShiftFrame),
"Shift":tk.Entry(AddShiftFrame),
"Shop":tk.Entry(AddShiftFrame)
}
#add widgets to frame
self.widgets["Date"].grid(row=2, column=0, sticky="nsw")
self.widgets["Shift"].grid(row=2, column=1, sticky="nsw")
self.widgets["Shop"].grid(row=2, column=2, sticky="nsw")
#this method will submit the data (callback function)
def submit_data(self):
shift_data = [(self.widgets["Date"].get()), (self.widgets["Shift"].get()), (self.widgets["Shop"].get())]
c.execute("INSERT INTO shifts (date, shift, shop) VALUES(?,?,?)", (shift_data[0], shift_data[1], shift_data[2]))
conn.commit()
print("done")
#submit button
submit = tk.Button(self, text="Submit",
command= lambda: submit_data(self))
submit.pack(padx=10, pady=10, side="left")
#home button
button1 = tk.Button(self, text="Home",
command=lambda: controller.showFrame(StartPage))
button1.pack(padx=10, pady=10, side='left')
app = MainApplication()
app.after(1000, getShifts)
app.mainloop()

Related

How to shift rows up after deleting a row?

I have the code where you can tick a checkbox and delete any row you want but I can't figure out how to get the rows beneath the row deleted to shift up and fill the space from the deleted row. Here is the code I'm running in jupyter. Please help!!
import tkinter as tk
from tkinter import messagebox
class GUI:
def __init__(self, root):
self.root = root
self.root.title("Editable Headings")
self.headings = ['Pad #', 'Location Name', 'Projected Level',
'# 130 bbl Loads', '24hr Rate', 'Rate Per Tank',
'# of Tanks', 'Notes', 'Water weight']
self.entries = []
self.vars = []
self.init_gui()
def init_gui(self):
for i, heading in enumerate(self.headings):
label = tk.Label(self.root, text=heading, font=("TkDefaultFont", 14))
label.grid(row=0, column=i, padx=10, pady=10, sticky="w")
entry = tk.Entry(self.root)
entry.grid(row=1, column=i, padx=10, pady=10, sticky="we")
self.entries.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.root, variable=var)
checkbox.grid(row=1, column=len(self.headings), padx=10, pady=10, sticky="w")
self.vars.append(var)
add_button = tk.Button(self.root, text="+", command=self.add_row)
add_button.grid(row=1, column=len(self.headings)+1, padx=25, pady=10)
delete_button = tk.Button(self.root, text="Delete", command=self.delete_row)
delete_button.grid(row=2, column=len(self.headings)+1, padx=25, pady=10)
def add_row(self):
row = len(self.entries) // len(self.headings) + 1
for i, heading in enumerate(self.headings):
entry = tk.Entry(self.root)
entry.grid(row=row, column=i, padx=10, pady=10, sticky="we")
self.entries.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.root, variable=var)
checkbox.grid(row=row, column=len(self.headings), padx=10, pady=10, sticky="w")
self.vars.append(var)
def delete_row(self):
if not self.entries:
return
if messagebox.askyesno("Delete", "Are you sure you want to delete the selected rows?"):
indices = [i for i, var in enumerate(self.vars) if var.get()]
for i in sorted(indices, reverse=True):
for j in range(len(self.headings)):
entry = self.entries.pop(i * len(self.headings))
entry.destroy()
var = self.vars.pop(i)
var.set(0)
if __name__ == "__main__":
root = tk.Tk()
app = GUI(root)
root.mainloop()
I would just like the rows beneath the row that is selected to be deleted to shift up after the selected is deleted.
For easier control, I would suggest to:
put those entry boxes and checkbuttons in a frame and those action buttons in another frame
use a single dictionary to store those widgets and tk.IntVar using row number as the key instead of separate lists
Then it is easy to delete the widgets in a row by just looking up them in the dictionary. Note that if all widgets in a row are deleted, the height of that row will be zero. So the widgets beneath will be shift up.
import tkinter as tk
from tkinter import messagebox
class GUI:
def __init__(self, root):
self.root = root
self.root.title("Editable Headings")
self.headings = ['Pad #', 'Location Name', 'Projected Level',
'# 130 bbl Loads', '24hr Rate', 'Rate Per Tank',
'# of Tanks', 'Notes', 'Water weight']
self.entries = {}
self.init_gui()
def init_gui(self):
# frame for the entries and checkbuttons
self.frame = tk.Frame(self.root)
self.frame.pack(side="left", fill="y")
# show the headings
for i, heading in enumerate(self.headings):
label = tk.Label(self.frame, text=heading, font=("TkDefaultFont", 14))
label.grid(row=0, column=i, padx=10, pady=10, sticky="w")
# add an initial row
self.add_row()
# frame for the buttons
button_frame = tk.Frame(self.root)
button_frame.pack(side="right", fill="y")
add_button = tk.Button(button_frame, text="+", command=self.add_row)
add_button.grid(row=1, column=len(self.headings)+1, padx=25, pady=10)
delete_button = tk.Button(button_frame, text="Delete", command=self.delete_row)
delete_button.grid(row=2, column=len(self.headings)+1, padx=25, pady=10)
def add_row(self):
# get the insertion row number
row = self.frame.grid_size()[1]
widgets = []
for i, heading in enumerate(self.headings):
entry = tk.Entry(self.frame)
entry.grid(row=row, column=i, padx=10, pady=10, sticky="we")
widgets.append(entry)
var = tk.IntVar()
checkbox = tk.Checkbutton(self.frame, variable=var)
checkbox.grid(row=row, column=len(self.headings), padx=10, pady=10, sticky="w")
widgets.append(checkbox)
# store the widgets in the new row and IntVar into the dictionary
self.entries[row] = (var, widgets)
def delete_row(self):
# any row selected
rows = [row for row, (var, _) in self.entries.items() if var.get()]
if rows and messagebox.askyesno("Delete", "Are you sure you want to delete the selected rows?"):
for row in rows:
_, widgets = self.entries.pop(row)
for w in widgets:
w.destroy()
if __name__ == "__main__":
root = tk.Tk()
app = GUI(root)
root.mainloop()

How to call onto the class that is in the tkinter mainloop?

I've been trying to call the class that is in my mainloop, but whenever I properly call it makes it that the program doesn't even launch anymore. I've been attempting to call it in order to get a method, which would return the current frame. I am aware that my code uses controller and parent to communicate between classes, but I haven't managed to fully grasp an understanding of these. If I call the mainlooped class, with "test = Database_project()", then the program won't run anymore. Can someone explain this to me? I'm trying to get the scrollbar feature to work on specific frames, and I haven't managed to figure out just yet. I'm trying to call the mainlooped class in the "CreatePage" class. Thank you in advance!
from tkinter import *
import tkinter as tk
class Database_Project(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
stack_frame_container = tk.Frame(self)
stack_frame_container.grid_columnconfigure(0, weight=1)
stack_frame_container.grid_rowconfigure(0, weight=1)
stack_frame_container.pack(side="top", fill="both", expand=True)
self.frameslist = {}
for frame in (MainPage, CreatePage):
frame_occurrence = frame.__name__
active_frame = frame(parent=stack_frame_container, controller=self)
self.frameslist[frame_occurrence] = active_frame
active_frame.grid(row=0, column=0, sticky="snew")
#self.frameslist["CreatePage"].dbproject = self.frameslist["Datanase_Project"]
self.current_frame("MainPage")
print(self.frameslist)
def current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
active_frame.tkraise()
def get_current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
return active_frame
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label_create = tk.Label(self, text="Create and insert data").grid(row=0, column=0, padx=50, pady=(50,0))
create_button = tk.Button(self, text="CREATE", command=lambda: controller.current_frame("CreatePage")).grid(row=1, column=0)
label_read = tk.Label(self, text="Query over data").grid(row=0, column=1, padx=50, pady=(50,0))
read_button = tk.Button(self, text="READ").grid(row=1, column=1)
label_update = tk.Label(self, text="Modify existing data").grid(row=2, column=0, padx=50, pady=(50,0))
update_button = tk.Button(self, text="UPDATE").grid(row=3, column=0, pady=(0,50))
label_delete = tk.Label(self, text="Remove data").grid(row=2, column=1, padx=50, pady=(50,0))
delete_button = tk.Button(self, text="DELETE").grid(row=3, column=1, pady=(0,50))
class CreatePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.inputlist = []
self.newinputlist = []
Test = Database_Project()
#active_frame = Test.get_current_frame("CreatePage")
#scrollbar = tk.Scrollbar(active_frame, orient="vertical")
#scrollbar.grid(row=0, column=2, stick="ns", columnspan=10)
#text["yscrollcommand"] =
labels = [tk.Label(self, text="Enter unique field"), tk.Label(self, text="Enter corresponding the value/s")]
self.inputlist.append(labels[:])
for toplabels in range(1):
self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)
first_entries = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.newinputlist.append(first_entries[:])
self.inputlist.append(first_entries[:])
for x in range(0, len(self.newinputlist) + 1):
self.newinputlist[0][x].grid(row=1, column=x, padx=10, pady=5)
button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK", command=lambda: controller.current_frame("MainPage"))]
self.inputlist.append(button_input_1[:])
button_input_2 = [tk.Button(self, text="IMPORT FILE"), tk.Button(self, text="SUBMIT DATA", command=self.submit_data)]
self.inputlist.append(button_input_2[:])
for button in range(len(self.inputlist) - 2, len(self.inputlist)):
self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)
def add_insert(self):
add_input = [tk.Entry(self, borderwidth=5), tk.Text(self, borderwidth=5, height= 5, width=20)]
self.inputlist.insert(-2, add_input)
self.newinputlist.append(add_input)
for widget in self.children.values():
widget.grid_forget()
for index, widgets in enumerate(self.inputlist):
print(widgets)
widget_one = widgets[0]
widget_two = widgets[1]
print(str(index), widget_one, widget_two)
widget_one.grid(row=index, column=0, padx=10, pady=5)
widget_two.grid(row=index, column=1, padx=10)
def submit_data(self):
for index, entries in enumerate(self.newinputlist):
my_label = Label(self, text=str(entries[0].get()) + str(entries[1].get("1.0", END)))
my_label.grid(row=len(self.inputlist) + index, column=0)
if __name__ == "__main__":
NoSQL_Project = Database_Project()
NoSQL_Project.title("NoSQL Database Project")
NoSQL_Project.maxsize(500, 500)
NoSQL_Project.mainloop()

Is it possible to resize an input box (entry) when clicked with tkinter?

Reading through other stackoverflow questions, and other sources I do see that bind can be used to call a function. Currently I'm working on a program that will communicate with a database (most likely mongodb), and so far I've set up a frame that has 2 inputs per row (key-value). I haven't completely decided whether I want one row per document, or one row per field. Right now, if a user has a lot to type then it wouldn't be ideal for them because you can't see everything you write. So what I was thinking is that, if the user clicks on the entry widget, then the box would become bigger and show them everything they have written. My current line of thinking is that maybe I could create another frame for it and somehow pass onto the information to that?
This is what it currently looks like
Then what I'd ideally want it to look like
Here's the code if interested how I made it (Images are from the "CreatePage" section):
from tkinter import *
import tkinter as tk
class Database_Project(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
stack_frame_container = tk.Frame(self)
stack_frame_container.grid_columnconfigure(0, weight=1)
stack_frame_container.grid_rowconfigure(0, weight=1)
stack_frame_container.pack(side="top", fill="both", expand=True)
self.frameslist = {}
for frame in (MainPage, CreatePage):
frame_occurrence = frame.__name__
active_frame = frame(parent=stack_frame_container, controller=self)
self.frameslist[frame_occurrence] = active_frame
active_frame.grid(row=0, column=0, sticky="snew")
self.current_frame("MainPage")
def current_frame(self, frame_occurrence):
active_frame = self.frameslist[frame_occurrence]
active_frame.tkraise()
class MainPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label_create = tk.Label(self, text="Create and insert data").grid(row=0, column=0, padx=50, pady=(50,0))
create_button = tk.Button(self, text="CREATE", command=lambda: controller.current_frame("CreatePage")).grid(row=1, column=0)
label_read = tk.Label(self, text="Query over data").grid(row=0, column=1, padx=50, pady=(50,0))
read_button = tk.Button(self, text="READ").grid(row=1, column=1)
label_update = tk.Label(self, text="Modify existing data").grid(row=2, column=0, padx=50, pady=(50,0))
update_button = tk.Button(self, text="UPDATE").grid(row=3, column=0, pady=(0,50))
label_delete = tk.Label(self, text="Remove data").grid(row=2, column=1, padx=50, pady=(50,0))
delete_button = tk.Button(self, text="DELETE").grid(row=3, column=1, pady=(0,50))
class CreatePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.inputlist = []
self.newinputlist = []
labels = [tk.Label(self, text="Enter unique field"), tk.Label(self, text="Enter corresponding the value/s")]
self.inputlist.append(labels[:])
for toplabels in range(1):
self.inputlist[toplabels][0].grid(row=toplabels, column=0, padx=10, pady=5)
self.inputlist[toplabels][1].grid(row=toplabels, column=1, padx=10, pady=5)
for entries in range(2):
for entrynum in range(0, 1):
print("column:", entries)
print("row", entrynum)
self.newinputlist.append(tk.Entry(self, borderwidth=5))
for x in range(len(self.newinputlist)):
self.newinputlist[x].grid(row=1, column=x, padx=10, pady=5)
self.inputlist.append(self.newinputlist[:])
button_input_1 = [tk.Button(self, text="ADD FIELD/VALUE", command=self.add_insert), tk.Button(self, text="BACK", command=lambda: controller.current_frame("MainPage"))]
self.inputlist.append(button_input_1[:])
button_input_2 = [tk.Button(self, text="IMPORT FILE"), tk.Button(self, text="SUBMIT DATA")]
self.inputlist.append(button_input_2[:])
for button in range(len(self.inputlist) - 2, len(self.inputlist)):
self.inputlist[button][0].grid(row=button, column=0, padx=10, pady=5)
self.inputlist[button][1].grid(row=button, column=1, padx=10, pady=5)
def add_insert(self):
add_input = [tk.Entry(self, borderwidth=5), tk.Entry(self, borderwidth=5)]
self.inputlist.insert(-2, add_input)
self.newinputlist.append(add_input)
for widget in self.children.values():
widget.grid_forget()
for index, widgets in enumerate(self.inputlist):
widget_one = widgets[0]
widget_two = widgets[1]
print(str(index), widget_one, widget_two)
widget_one.grid(row=index, column=0, padx=10, pady=5)
widget_two.grid(row=index, column=1, padx=10)
if __name__ == "__main__":
NoSQL_Project = Database_Project()
NoSQL_Project.title("NoSQL Database Project")
NoSQL_Project.mainloop()
It's pointless to resize an Entry widget since they can only ever hold a single line. I'll give an example using the Text widget instead, though the technique works with any widget.
There's really no trick, just bind to <FocusIn> and <FocusOut>. In the following example I've created two Text widgets that have this resize behavior:
import tkinter as tk
def resizer(event):
if event.widget == event.widget.focus_get():
event.widget.configure(height=8)
else:
event.widget.configure(height=1)
root = tk.Tk()
root.geometry("400x200")
text1 = tk.Text(root, height=1, width=20)
text2 = tk.Text(root, height=1, width=20)
text1.pack(side="left")
text2.pack(side="right")
for widget in (text1, text2):
widget.bind("<FocusIn>", resizer)
widget.bind("<FocusOut>", resizer)
root.mainloop()
The actual behavior depends on how you've laid out your widget. This could cause widgets to jump around or the window resize, but every app will be different so it's hard to give a solution that works everywhere.

Can't get tkinter frame to centre when switching from mutiple frames

I am fairly new to tkinter and I'm using the code from the top comment in this post:Switch between two frames in tkinter with some changes to be able to switch between two frames. The problem is that I can't get my second one to centre or any other frame that isn't the first one when I add it for that matter. Apologises if I'm making any obvious mistakes as I said I'm still getting to know tkinter and python and I don't really understand how the code from this post works. Here is my code:
import tkinter as tk
class MainView(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
self.frames["LoginFrame"] = LoginFrame(parent=container, controller=self)
self.frames["RegisterFrame"] = RegisterFrame(parent=container, controller=self)
self.frames["LoginFrame"].grid(row=0, column=0, sticky="NESW")
self.frames["RegisterFrame"].grid(row=0, column=0, sticky="NESW")
self.ShowFrame("LoginFrame")
def ShowFrame(self, PageName):
frame = self.frames[PageName]
frame.tkraise()
class LoginFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
WelcomeLabel = tk.Label(self, text="Welcome to Detention Organiser!",font=(None,20) ).grid(columnspan=2)
UsernameLabel = tk.Label(self, text="Username",font=(None,15) ).grid(row=1, sticky="E")
PasswordLabel = tk.Label(self, text="Password",font=(None,15) ).grid(row=2, sticky="E")
UsernameEntry = tk.Entry(self).grid(row=1, column=1, sticky="W")
PasswordEntry = tk.Entry(self, show="*").grid(row=2, column=1, sticky="W")
LoginButton = tk.Button(self, text="Login").grid(columnspan=2)
RegisterButton = tk.Button(self, text="Sign Up",command=lambda: controller.ShowFrame("RegisterFrame")).grid(columnspan=2)
class RegisterFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.Variable = tk.StringVar()
self.Variable.set("7A")
RegisterLabel = tk.Label(self, text="Register",font=(None,20)).grid(columnspan=2)
UsernameLabel = tk.Label(self, text="Username",font=(None,15)).grid(row=1, sticky="E")
PasswordLabel = tk.Label(self, text="Password",font=(None,15)).grid(row=2, sticky="E")
FormGroupLabel = tk.Label(self, text="Form Group",font=(None,15) ).grid(row=3, sticky="E")
UsernameEntry = tk.Entry(self).grid(row=1, column=1, sticky="W")
PasswordEntry = tk.Entry(self, show="*").grid(row=2, column=1, sticky="W")
FormGroupDrop = tk.OptionMenu(self,self.Variable,"7A","7B","8A","8B").grid(row=3, column=1, sticky="W")
RegisterButton = tk.Button(self, text="Register",command=lambda: controller.ShowFrame("RegisterFrame"))
RegisterButton.grid(columnspan=2)
BackButton = tk.Button(self, text="Back",command=lambda: controller.ShowFrame("LoginFrame")).grid(columnspan=2)
if __name__ == "__main__":
app = MainView()
app.geometry("640x360")
app.mainloop()
First thing, in general in your program, avoid doing something like:
UsernameEntry = tk.Entry(self).grid(row=1, column=1, sticky="W")
because the variable UsernameEntry is not a tk.Entry as you would expect, and will definitely cause a bug sometimes. Instead, use one of the following:
# If you need the variable later
UsernameEntry = tk.Entry(self)
UsernameEntry.grid(row=1, column=1, sticky="W")
# Otherwise
tk.Entry(self).grid(row=1, column=1, sticky="W")
Now, back to your problem. What is missing is that you don't tell how the program should allocate the remaining space (The space that is not necessary to contain your widgets). By default, the grid will place your widgets as close as possible to the top/left of the container. To change this behaviour, you will need to use grid_columnconfigure and/or grid_rowconfigure. For example,
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
tells the program that the remaining space should be equally split between column 0 and column 1. The weight can be any non-negative integer value:
self.grid_columnconfigure(0, weight=3)
self.grid_columnconfigure(1, weight=0)
self.grid_columnconfigure(2, weight=1)
This tells the program to allocate 3/4 of remaining space to column 0, None to column 1 and 1/4 to column 2. By default, all weights are 0.
Then, your classes LoginFrame and RegisterFrame might look like
class LoginFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.grid_rowconfigure(5, weight=1) # Fills vertical space below the last row
self.grid_columnconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
tk.Label(self, text="Welcome to Detention Organiser!",font=(None,20) ).grid(columnspan=2)
tk.Label(self, text="Username",font=(None,15)).grid(row=1, sticky="E")
tk.Label(self, text="Password",font=(None,15)).grid(row=2, sticky="E")
tk.Entry(self).grid(row=1, column=1, sticky="W")
tk.Entry(self, show="*").grid(row=2, column=1, sticky="W")
tk.Button(self, text="Login").grid(row=3, columnspan=2)
tk.Button(self, text="Sign Up",command=lambda: controller.ShowFrame("RegisterFrame")).grid(row=4, columnspan=2)
class RegisterFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.Variable = tk.StringVar()
self.Variable.set("7A")
self.grid_rowconfigure(5, weight=1) # Fills vertical space below the last row
self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure(1, weight=1)
tk.Label(self, text="Register",font=(None,20)).grid(columnspan=2)
tk.Label(self, text="Username",font=(None,15)).grid(row=1, sticky="E")
tk.Label(self, text="Password",font=(None,15)).grid(row=2, sticky="E")
tk.Label(self, text="Form Group",font=(None,15) ).grid(row=3, sticky="E")
tk.Entry(self).grid(row=1, column=1, sticky="W")
tk.Entry(self, show="*").grid(row=2, column=1, sticky="W")
tk.OptionMenu(self,self.Variable,"7A","7B","8A","8B").grid(row=3, column=1, sticky="W")
RegisterButton = tk.Button(self, text="Register",command=lambda: controller.ShowFrame("RegisterFrame"))
RegisterButton.grid(columnspan=2)
tk.Button(self, text="Back",command=lambda: controller.ShowFrame("LoginFrame")).grid(columnspan=2)
Finally, tkinter has many many different options that you need to test to really understand how they work. As you do some tests, I advise you to extensively use the option bg="COLOR", that will change the background of a widget and tell you precisely its boundaries. For instance,
tk.Frame.__init__(self, parent, bg="RED")

Python/Tkinter: How to display a blank bottom frame when the application is run first?

When the application is run first, the last frame's widgets are displayed on the screen. What i wanted to do is, displaying the related frames when the user clicks their buttons. So, i want to display a blank frame with the top buttons. In order to do that, what should i do? (I removed the button functions, because they are not related to the question.) Thanks in advance.
import tkinter as tk
class TopFrame(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid(row=0, column=0, sticky="nsew")
self.BottomFrame = tk.Frame(master=master)
self.BottomFrame.grid(row=1, column=0, sticky="nsew")
self.f1 = tk.Frame(master=self.BottomFrame)
self.f2 = tk.Frame(master=self.BottomFrame)
self.f3 = tk.Frame(master=self.BottomFrame)
for f in (self.f1, self.f2, self.f3):
f.grid(row=0, column=0, sticky="nsew")
self.b1 = tk.Button(master=self, text="Add Words")
self.b2 = tk.Button(master=self, text="Add From File")
self.b3 = tk.Button(master=self, text="Change Words")
self.add_button = tk.Button(master=self.f1, text="Add")
self.open_button = tk.Button(master=self.f2, text="Open File")
self.change_button = tk.Button(master=self.f3, text="Change")
self.l1 = tk.Label(master=self.f1, text="English")
self.l2 = tk.Label(master=self.f1, text="Turkish")
self.l3 = tk.Label(master=self.f3, text="Old word")
self.l4 = tk.Label(master=self.f3, text="New word")
self.e1 = tk.Entry(master=self.f1)
self.e2 = tk.Entry(master=self.f1)
self.e3 = tk.Entry(master=self.f3)
self.e4 = tk.Entry(master=self.f3)
self.configure_buttons()
self.configure_labels()
self.configure_entries()
def configure_buttons(self):
self.b1.grid(row=0, column=0)
self.b1.configure(command=lambda: self.f1.tkraise())
self.b2.grid(row=0, column=1)
self.b2.configure(command=lambda: self.f2.tkraise())
self.b3.grid(row=0, column=2)
self.b3.configure(command=lambda: self.f3.tkraise())
self.add_button.grid(row=2, columnspan=2)
#self.add_button.configure(command=self.add_word)
self.open_button.pack(side="top")
#self.open_button.configure(command=self.add_from_file)
self.change_button.grid(row=2, columnspan=2)
def configure_labels(self):
self.l1.grid(row=0, column=0)
self.l2.grid(row=0, column=1)
self.l3.grid(row=0, column=0)
self.l4.grid(row=0, column=1)
def configure_entries(self):
self.e1.grid(row=1, column=0)
self.e2.grid(row=1, column=1)
self.e3.grid(row=1, column=0)
self.e4.grid(row=1, column=1)
if __name__ == "__main__":
root = tk.Tk()
example = TopFrame(master=root)
example.mainloop()
Instead of having 3 widgets in the same location, it's better to have only the one you need.
First, get rid of this code:
for f in (self.f1, self.f2, self.f3):
f.grid(row=0, column=0, sticky="nsew")
Now the frame will start in a blank state.
Then, instead of calling .tkraise() on the frames, we will remove the current frame (if any) and add another one in its place. So
self.b1.configure(command=lambda: self.f1.tkraise())
self.b2.configure(command=lambda: self.f2.tkraise())
self.b3.configure(command=lambda: self.f3.tkraise())
becomes:
self.b1.configure(command=lambda: self._activate(self.f1))
self.b2.configure(command=lambda: self._activate(self.f2))
self.b3.configure(command=lambda: self._activate(self.f3))
with
def _activate(self, frame):
# remove the current frame
for child in self.BottomFrame.winfo_children():
child.grid_forget()
# add the new frame in its place
frame.grid(row=0, column=0, sticky='nsew')

Categories