How do I format my GUI using tkinter by using .grid? - python

I am trying to use .grid to format my GUI, but it is not doing anything. Here below is my code and my desired layout!
Here is the code I am using. I am quite new to Python...
I am not sure of other methods of formatting except for .grid so any other options would be great too!
from tkinter import *
class PayrollSummary:
def __init__(pay):
window = Tk()
window.title("Employee Payroll")
#Add Frame 1
frame1 = Frame(window)
frame1.pack()
#Add ReadFile Button
btReadFile = Button(frame1, text = "Read File")
btReadFile.pack()
#Add ShowPayroll Button
btShowPayroll = Button(frame1, text = "Show Payroll")
btShowPayroll.pack()
#Add FindEmployee by Name Button
btFindEmployee = Button(frame1, text = "Find Employee by Name")
btFindEmployee.pack()
#Add Highest Radio Button
rbHigh = Radiobutton(frame1, text = "Highest")
rbHigh.pack()
#Add Lowest Radio Button
rbLow = Radiobutton(frame1, text ="Lowest")
rbLow.pack()
#Add FindEmployee by Amount Button
btFindEmployee_A = Button(frame1, text = "Find Employee by Amount")
btFindEmployee_A.pack()
#Add WriteOutput Button
btOutput = Button(frame1, text = "Write Output to File")
btOutput.pack()
#Add Cancel Button
btCancel = Button(frame1, text = "Cancel")
btCancel.pack()
btReadFile.grid(row = 1, column = 2)
btShowPayroll.grid(row = 2, column = 2)
btFindEmployee.grid(row = 2, column = 4)
rbHigh.grid(row = 3, column = 2)
rbLow.grid(row = 3, column = 4)
btFindEmployee_A.grid(row = 3, column = 6)
btOutput.grid(row = 4, column = 2)
btCancel.grid(row = 4, column = 4)
window.mainloop()
PayrollSummary()

pack(), grid() and place() are three methods to put widgets in window (or in other widget).
If you use grid() with some widget then don't use pack() or place().
Doc on effbot.org: grid, pack, place
I removed all .pack() except frame.pack() and get almost what you expected.
Now widgets need to be aligned to left ('west') with sticky='w'
And after adding second Frame I got
Code:
from tkinter import *
class PayrollSummary:
def __init__(pay):
window = Tk()
window.title("Employee Payroll")
#Add Frame 1
frame1 = Frame(window)
frame1.pack()
#Add ReadFile Button
btReadFile = Button(frame1, text = "Read File")
#Add ShowPayroll Button
btShowPayroll = Button(frame1, text = "Show Payroll")
#Add FindEmployee by Name Button
btFindEmployee = Button(frame1, text = "Find Employee by Name")
#Add Highest Radio Button
rbHigh = Radiobutton(frame1, text = "Highest")
#Add Lowest Radio Button
rbLow = Radiobutton(frame1, text ="Lowest")
#Add FindEmployee by Amount Button
btFindEmployee_A = Button(frame1, text = "Find Employee by Amount")
#Add WriteOutput Button
btOutput = Button(frame1, text = "Write Output to File")
#Add Cancel Button
btCancel = Button(frame1, text = "Cancel")
btReadFile.grid(row = 1, column = 2, sticky='w')
btShowPayroll.grid(row = 2, column = 2, sticky='w')
btFindEmployee.grid(row = 2, column = 4, sticky='w')
rbHigh.grid(row = 3, column = 2, sticky='w')
rbLow.grid(row = 3, column = 4, sticky='w')
btFindEmployee_A.grid(row = 3, column = 6, sticky='w')
btOutput.grid(row = 4, column = 2, sticky='w')
btCancel.grid(row = 4, column = 4, sticky='w')
#Add Frame 2
frame2 = Frame(window, bg='red')
frame2.pack(fill='both') # try without `fill`
label2 = Label(frame2, text='Label in bottom Frame', bg='green')
label2.pack()
window.mainloop()
PayrollSummary()

Related

Tkinter - Wait for several inputs before calculating the results

I am new to tkinter and python in general. I am trying to create a window that allows the user to input information (based on Entry and dropdown menus) and based on their choices, some new input will show up which will then be used to calculate the results. I've tried making a minimum reproducible snippet of code, as the original code is quite long.
The problem is that in the buttons I don't understand which command to include so that the program waits for all input before executing the rest of the code. Now it seems that no matter what I include it goes directly to the result part of the code.
I've tried having a function that saves the input and calculates the result as the command for the button, but it still does not wait.
The wait_variable as far as I understood only works for one variable?
import tkinter as tk
root = tk.Tk()
root.geometry("1500x800")
Intro_Label = tk.Label(root, text = "Welcome")
Intro_Label.grid(row=0, column=0)
entry_1 = tk.Entry()
entry_1.insert(0, 2.5) #default value
Label_1 = tk.Label(root, text="Input 1")
Label_1.grid(row=2, column=0)
entry_1.grid(row=2, column=1)
Label_2 = tk.Label(root, text="Input 2 ")
#Options for dropdown menu for transport method
I2_clicked = tk.StringVar()
I2_clicked.set("Choose from dropdown menu")
input2_opt = ["a", "b"]
input2 = tk.OptionMenu( root , I2_clicked , *input2_opt )
Label_3 = tk.Label(root, text="Input 2 ")
#Options for dropdown menu for transport method
I3_clicked = tk.StringVar()
I3_clicked.set("Choose from dropdown menu")
input3_opt = ["x", "y"]
input3 = tk.OptionMenu( root , I3_clicked , *input3_opt )
Label_2.grid(row=3, column=0)
input2.grid(row=3, column=1)
Label_3.grid(row=3, column =3)
input3.grid(row=3, column = 4)
def input_calculations():
first_input = entry_1.get()
if I2_clicked.get() == "a":
if I3_clicked.get() == "x":
entry_4 = tk.Entry()
Label_4 = tk.Label(root, text="Input for x|a")
entry_4.insert(0, 5) #dummy default value
entry_4.grid(row = 4, column = 1 )
Label_4.grid(row = 4, column = 0)
entry_5 = tk.Entry()
Label_5 = tk.Label(root, text="Second input for x|a")
entry_5.insert(0, 4) #dummy default value
entry_5.grid(row = 4, column = 4)
Label_5.grid(row = 4, column = 3)
#wait for both inputs before executing the calculations
save_button = tk.Button(root, text ="Calculate results", command= )
save_button.grid(row = 5, column = 6)
#calculate some results
result = float(entry_4.get())* float(entry_5.get())
elif I3_clicked.get() == "y":
entry_6 = tk.Entry()
Label_6 = tk.Label(root, text="Input for y|a")
entry_6.insert(0, 6) #dummy default value
entry_6.grid(row = 4, column = 1 )
Label_6.grid(row = 4, column = 0)
entry_7 = tk.Entry()
Label_7 = tk.Label(root, text="Second input for y|a")
entry_7.insert(0, 7) #dummy default value
entry_7.grid(row = 4, column = 4)
Label_7.grid(row = 4, column = 3)
entry_8 = tk.Entry()
Label_8 = tk.Label(root, text="Third input for y|a")
entry_8.insert(0, 8) #dummy default value
entry_8.grid(row = 4, column = 6)
Label_8.grid(row = 4, column = 5)
save_button = tk.Button(root, text ="Calculate results", command= )
save_button.grid(row = 5, column = 6)
#wait for input before executing the next lines - what to insert here ??
result = float(entry_6.get()) / float(entry_7.get()) * float(entry_8.get())
#continues for all combinations (b and y, b and x) - different inputs, different calculations for each combo
return result
btn = tk.Button(root, text="Confirm", width=15,command=input_calculations)
btn.grid(row= 10, column= 5)
root.mainloop()
You could link the buttons to functions that do the calculations.
Example function:
def button_pressed():
result = float(entry_6.get()) / float(entry_7.get()) * float(entry_8.get())
You could either pass the entries as arguments in a lambda, or make the whole program inside of a class. This way every method can access all of the widgets.
For your save button you would then need to add the function to the command parameter:
save_button = tk.Button(root, text ="Calculate results", command=button_pressed)
You could make multiple functions / methods if you use a class to do the steps you need.

tkinter throws error using a variable for button name to set state

I want to pass a button name to tkinter as a variable. This method works for Text fields, but not for buttons in my code.
I am building a gui app and I want to have a generic clear function that zaps a text entry field and resets a button from NORMAL to DiSABLED.
There are multiple buttons and fields, hence the desire to make this generic.
For the code I have, the buttons are present with the exception of clear all.
I am setting the variable w_button to the specific name of the (existing) button based on what is passed to the function.
def switch_clear(elem_type):
if elem_type == 'scn':
w_button = 'b_clear_scn'
clear_field = 'scn_file_entry'
print ('scenario')
elif elem_type == 'sol2':
w_button = 'b_clear_sol2'
clear_field = 'sol2_file_entry'
print ('sol2')
elif elem_type == 'mdl':
w_button = 'b_clear_mdlList'
clear_field = 'mdlList_file_entry'
print ('mdl')
elif elem_type == 'all':
print ('clear all TBD')
return()
if w_button["state"] == NORMAL:
clear_field.delete(0, END)
w_button["state"] = DISABLED
return()
Here's what happens:
C:\utils>my_frame3.py
scenario
Traceback (most recent call last):
File "C:\utils\my_frame3.py", line 127, in <module>
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
File "C:\utils\my_frame3.py", line 100, in switch_clear
if w_button["state"] == NORMAL:
TypeError: string indices must be integers
C:\utils>
I realize I could duplicate and push the clear operations into the if/elif statements, and I may have to live with that but - is it possible to reference buttons as variables? How can I do this?
Complete code as requested below. The preview is a mess despite using the code widget, but in my file lines are correctly formatted (notepad++). Tx
import os, sys
from tkinter import *
from tkinter import messagebox
from tkinter import filedialog
root2 = Tk()
root2.title('Model Processing')
root2.geometry('{}x{}'.format(512, 400))
# colors
color1 = 'light cyan'
color2 = 'gold'
color3 = 'RosyBrown1'
color4 = 'lavender'
color5 = 'linen'
bg_color = 'azure'
# number of items to process; until >1 OK is disabled; if >1 OK is enabled TBD
l_to_do = [] # items to process; scn, sol2, modelList, installed models
# functions/commands
def callback():
print ('A button was clicked.')
return;
def my_exit():
result = messagebox.askquestion("Cancel Model Processing", "Are you sure?", icon='warning')
if result == 'yes':
root2.quit()
else: # user changed mind
pass
def choose_import_file(str_ftype):
which_field = ''
which_clear_btn = ''
ftype = str_ftype
mdl_opts = {}
if ftype == 'scn':
title_msg = 'Choose a scenario file'
mdl_opts['filetypes'] = [('Supported types', ('.scn')),
('scenario files',('.scn'))]
which_field = scn_file_entry
which_clear_btn = 'b_clear_scn'
elif ftype == 'sol2':
title_msg = 'Choose a SOL2 file'
mdl_opts['filetypes'] = [('Supported types', ('.txt')),
('sol2 files',('.txt'))]
which_field = sol2_file_entry
elif ftype == 'mdllist':
title_msg = 'Choose a ModelList file'
print ('TBD: ModelList file')
file_input = filedialog.askopenfilename(title = title_msg, **mdl_opts)
if file_input == '':
print ('error or cancelled by user')
return
else:
f_inp_file = os.path.basename(file_input)
f_inp_file_base = str(file_input.split('.')[0])
f_inp_file_ext = str.lower(str(file_input.split('.')[1]))
f_inp_d_name = os.path.dirname(file_input)
print('File chosen:', f_inp_file_base)
# populate scenario file field
which_field.insert(INSERT,file_input)
which_clear_btn["state"] = NORMAL
# define appropriate clear button active
# define_clear_btn.configure(state = ACTIVE)
return
def switch_clear(elem_type):
if elem_type == 'scn':
if b_clear_scn["state"] == NORMAL:
scn_file_entry.delete(0, END)
b_clear_scn["state"] = DISABLED
elif elem_type == 'sol2':
f b_clear_sol2["state"] == NORMAL:
clear_field = 'sol2_file_entry'
b_clear_sol2["state"] = DISABLED
elif elem_type == 'mdl':
if b_clear_mdlList["state"] == NORMAL:
clear_field = 'mdlList_file_entry'
b_clear_mdlList["state"] = DISABLED
elif elem_type == 'all':
print ('clear all TBD')
return()
return()
# create all of the main containers
first_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
second_frame = Frame(root2, bg=color5, width = 512, height=90, pady=10)
third_frame = Frame(root2, bg=color5, width=512, height=90, pady=10)
fourth_frame = Frame(root2, bg=color5, width = 512, height = 90, pady=10)
# layout all of the main containers
root2.grid_rowconfigure(3, weight=1)
root2.grid_rowconfigure(2, weight=1)
root2.grid_rowconfigure(1, weight=1)
root2.grid_columnconfigure(0, weight=1)
first_frame.grid(row=0, sticky="ew")
second_frame.grid(row=1, sticky="ew")
third_frame.grid(row=2, sticky="ew")
fourth_frame.grid(row = 3, sticky="e")
# create the widgets for the first frame
#scn_label = Label(first_frame, text = 'Scenario file')
scn_file_entry = Entry(first_frame, background=bg_color, width = 50)
b_choose_scn = Button(first_frame, text = "Choose a scenario..", command = lambda: choose_import_file('scn'), height = 2)
b_clear_scn = Button(first_frame, text = "Clear scenario", command = switch_clear('scn'), height = 2, state=DISABLED)
# layout the widgets in the first frame
#scn_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
scn_file_entry.grid(row = 0, column = 1, padx = (10,10))
b_choose_scn.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_scn.grid(row=2, column=0, padx = (10,10), sticky=W)
# second frame
# sol2_label = Label(second_frame, text = 'Sol2 file')
sol2_file_entry = Entry(second_frame, background=bg_color, width = 50)
b_choose_sol2 = Button(second_frame, text = "Choose SOL2 file..", command = lambda: choose_import_file('sol2'), height = 2)
b_clear_sol2 = Button(second_frame, text = "Clear SOL2", command = switch_clear('sol2'), height = 2, state=DISABLED)
# layout the widgets in the second frame
# sol2_label.grid(row = 0, column = 0, padx = (10,50), pady=5)
sol2_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_sol2.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_sol2.grid(row=2, column=0, padx = (10,10), sticky=W)
# third frame
# mdlList_label = Label(third_frame, text = 'ModelList.txt file')
mdlList_file_entry = Entry(third_frame, background=bg_color, width = 50)
b_choose_mdlList = Button(third_frame, text = "Choose ModelList.txt file..", command = callback, height = 2)
b_clear_mdlList = Button(third_frame, text = "Clear ModelList", command = callback, height = 2, state=DISABLED)
# layout the widgets in the third frame
#mdlList_label.grid(row = 0, column = 0, padx = (10,10), pady=5, sticky = 'ns')
mdlList_file_entry.grid(row = 0, column = 1, padx = (10,10), sticky=EW)
b_choose_mdlList.grid(row=0, column=0, padx = (10,10), sticky=W)
b_clear_mdlList.grid(row=2, column=0, padx = (10,10), sticky=W)
#####################################################################
# create bottom widgets
#####################################################################
clear_all = Button(fourth_frame, text='Clear All', padx = '5', command = callback, height = 2, state=DISABLED)
ok_btn = Button(fourth_frame, text='OK', padx = '5', command = callback, height = 2, state=DISABLED)
cancel_btn = Button(fourth_frame, text='Cancel', height = 2, padx = '12', command = my_exit)
#####################################################################
# layout the bottom widgets
#####################################################################
clear_all.grid(row = 0, column = 2, sticky = 'e')
ok_btn.grid(row = 0, column = 3, sticky = 'e')
cancel_btn.grid(row = 0, column = 4, sticky = 'e')
# commands/bindings
root2.mainloop()
I believe this is what you are trying to do. The below is fully commented with explanations. switch_clear becomes entirely unnecessary with this method. The buttons directly clear their corresponding entry in their command, and Entry validation takes care of the corresponding button state.
Note: I wrote all of this before you posted your full code
import tkinter as tk
root = tk.Tk()
root.geometry('400x400')
#toggle the state of buttons based on entry text length
def toggle_button(name, text):
global btn_switch
btn_switch[name]['state'] = 'normal' if len(text) else 'disabled'
return True
#to hold the buttons so they are easy to position
#just for this example
buttons = tk.Frame(root)
buttons.pack(side='top', anchor='nw')
#create a tcl wrapper for the validate command
vcmd = tk.Widget.register(root, toggle_button)
#mock-up of your entries ~ validate on key press. send widget name and full text to vcmd
scn_file_entry = tk.Entry(root, width=20)
scn_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
scn_file_entry.pack(side='left', anchor='nw')
sol2_file_entry = tk.Entry(root, width=20)
sol2_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
sol2_file_entry.pack(side='left', anchor='nw')
mdlList_file_entry = tk.Entry(root, width=20)
mdlList_file_entry.configure(validate="key", validatecommand=(vcmd, '%W', '%P'))
mdlList_file_entry.pack(side='left', anchor='nw')
#mock-up of your buttons ~ delete the entry text in a lambda and let entry validation handle the button state
b_clear_scn = tk.Button(buttons, text="scn", state='disabled')
b_clear_scn.configure(command=lambda: scn_file_entry.delete(0, 'end'))
b_clear_scn.pack(side='left', anchor='nw')
b_clear_sol2 = tk.Button(buttons, text="sol2", state='disabled')
b_clear_sol2.configure(command=lambda: sol2_file_entry.delete(0, 'end'))
b_clear_sol2.pack(side='left', anchor='nw')
b_clear_mdlList = tk.Button(buttons, text="mdl", state='disabled')
b_clear_mdlList.configure(command=lambda: mdlList_file_entry.delete(0, 'end'))
b_clear_mdlList.pack(side='left', anchor='nw')
#create a dictionary of 'widget name':corresponding button, for toggle_button to reference
btn_switch = {
f'{scn_file_entry}':b_clear_scn,
f'{sol2_file_entry}':b_clear_sol2,
f'{mdlList_file_entry}':b_clear_mdlList,
}
root.mainloop()

Creating and Getting values from tkinter check boxes using for loop

This code is a part of my project in which I have to manage the attendance of 50 (or more) students.
The thing I want is that all the checkboxes should initially be 'checked' (showing the present state) and when I uncheck random checkboxes (to mark the absent) and click the Submit button (yet to be created at the bottom of the window), I should get a list with 'entered date' as first element and the roll numbers i.e. 2018-MC-XX as other elements.
For example: ['01/08/2020', '2018-MC-7', '2018-MC-11', '2018-MC-23', '2018-MC-44']
Actually my plan is when I will get a list I will easily write it to a text file. Also, if there is another way of creating multiple scrollable checkboxes without packing them inside a canvas then please do tell!
from tkinter import *
from tkcalendar import DateEntry
root = Tk()
root.geometry('920x600+270+50')
root.minsize(920,600)
Attendance_frame = Frame(root) ### Consider it a Main Frame
Attendance_frame.pack()
attendaceBox = LabelFrame(Attendance_frame, text = 'Take Attendance', bd = 4, relief = GROOVE, labelanchor = 'n',font = 'Arial 10 bold', fg = 'navy blue', width = 850, height = 525) # A Label Frame inside the main frame
attendaceBox.pack_propagate(0)
attendaceBox.pack(pady = 15)
dateFrame = Frame(attendaceBox) # A small frame to accommodate date entry label & entry box
dateFrame.pack(anchor = 'w')
font = 'TkDefaultFont 10 bold'
date_label = Label(dateFrame, text = 'Enter Date : ', font = font).grid(row = 0, column = 0, sticky = 'w', padx = 10, pady = 10)
date_entry = DateEntry(dateFrame, date_pattern = 'dd/mm/yyyy', showweeknumbers = FALSE, showothermonthdays = FALSE)
date_entry.grid(row = 0, column = 1, sticky = 'w')
noteLabel = Label(attendaceBox, text = 'Note: Uncheck the boxes for absentees').pack(anchor = 'w', padx = 10, pady = 5)
canvas = Canvas(attendaceBox, borderwidth=0, background="#ffffff")
checkFrame = Frame(canvas, width = 100, height = 50)
vsb = Scrollbar(canvas, orient="vertical", command=canvas.yview)
canvas.configure(yscrollcommand=vsb.set)
vsb.pack(side="right", fill="y")
canvas.pack(side="left", fill="both", expand=True)
canvas.pack_propagate(0)
canvas.create_window((4,4), window=checkFrame, anchor="nw")
def onFrameConfigure(canvas):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
checkFrame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))
for i in range(0,51): # A loop to create Labels of students roll numbers & names
c = Checkbutton(checkFrame, text = f"{'2018-MC-'+str(i+1)} Student {i+1}")
c.grid(row = i, column = 0, padx = 10, sticky = 'w')
mainloop()
First you need StringVar for each Checkbutton in order to get the state of the Checkbuttons later. Then you can use a list to hold the StringVars so that you can access them later. You can set the onvalue of the checkbuttons to the roll number associated to them.
Also you can use Text widget instead of Canvas+Frame. Below is an example:
import tkinter as tk
root = tk.Tk()
frame = tk.Frame(root)
frame.pack()
text = tk.Text(frame, width=40, height=20)
text.pack(side=tk.LEFT, fill=tk.BOTH)
vars = []
for i in range(51):
rollnum = '2018-MC-'+str(i+1)
var = tk.StringVar(value=rollnum)
cb = tk.Checkbutton(text, text=rollnum, variable=var, onvalue=rollnum, offvalue='', bg='white')
text.window_create('end', window=cb)
text.insert('end', '\n')
vars.append(var)
vsb = tk.Scrollbar(frame, orient=tk.VERTICAL, command=text.yview)
vsb.pack(side=tk.RIGHT, fill=tk.Y)
text.config(yscrollcommand=vsb.set)
def submit():
# extract roll numbers for checked checkbuttons
result = [var.get() for var in vars if var.get()]
print(result)
tk.Button(root, text='Submit', command=submit).pack()
root.mainloop()

Python tkinter: access child widgets of a widget

I have a string 'currentMessage' and a Label to display it.
I have a Toplevel widget, which contains a Text widget to provide new value for 'currentMessage':
from tkinter import *
from tkinter import ttk
root = Tk()
mainFrame = ttk.Frame(root)
mainFrame.grid()
currentMessage = 'current Message'
ttk.Label(mainFrame, text = currentMessage).grid(padx = 10, pady = 10)
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5)
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
theButton = ttk.Button(mainFrame, command = changeValues, text = 'Click')
theButton.grid(padx = 10, pady = 10)
mainFrame.mainloop()
I tried to get the content of 'textBox' Text widget of the Toplevel by using this function:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.textBox.get(0.0, END)
But I got an error
'Toplevel' object has no attribute 'textBox'
So how do I access content of the widget 'textBox', which is a child widget of 'popup' (this Toplevel widget is only created when function changeValues() is called)?
I think probably this is what you are looking for -- although I'm just guessing, because you are asking for a solution for a specific problem you think you have, however if I were you I would rethink what exactly do I want to do:
from tkinter import *
from tkinter import ttk
# Create Tk Interface root
root = Tk()
# Initialize mainFrame
mainFrame = ttk.Frame( root )
mainFrame.grid()
# Initialize label of mainframe
theLabel = ttk.Label( mainFrame, text='Current Message' )
theLabel.grid( padx=10, pady=10 )
def createPopup():
# Initialize popup window
popup = Toplevel( mainFrame )
popup.grid()
# Initialize text box of popup window
textBox = Text( popup, width=20, height=5 )
textBox.grid( column = 0, row = 0 )
textBox.insert( END, 'New Message Here' )
# Initialize button of popup window
button = ttk.Button( master = popup,
command = lambda: theLabel.config(text=textBox.get(0.0, END)),
text = 'Update')
button.grid( column=0, row=1, padx=5, pady=5 )
# Initialize button of main frame
theButton = ttk.Button( mainFrame, command=createPopup, text='Click' )
theButton.grid( padx=10, pady=10 )
# Enter event loop
mainFrame.mainloop()
There is a way indeed, like this:
def updateCurrentMessage(popupWindow):
currentMessage = popupWindow.nametowidget('textBox').get(0.0, END)
def changeValues():
popup = Toplevel(mainFrame)
popup.grid()
textBox = Text(popup, width = 20, height = 5, name = 'textBox')
textBox.grid(column = 0, row = 0)
textBox.insert(END, 'new message here')
b = ttk.Button(popup, command = lambda: updateCurrentMessage(popup))
b.grid(column = 0, row = 1, padx = 5, pady = 5)
b['text'] = 'Update'
You can choose whatever you want for the 'name'.

Tkinter / Python grid layout with listboxes

I'm having some trouble getting everything to line up properly using a grid with Tkinter and Python. I want to have the menu bar at the very top, and then two listboxes (one on each side), with a couple buttons between them in the middle of the window.
Currently, both listboxes are appearing on the bottom right side of the window, and the buttons are in the upper left corner, overlapping the menu buttons.
How do I deal with the grid layout of a listbox considering their size. Can they occupy more than one grid cell? For example, if I place a listbox in (row=1, column = 1) and another in row (row = 1, column = 2), would Tkinter automatically widen the cell so that it's the width of the listbox, or would it just overlap the listboxes?
def __init__(self,root):
self.frame = Frame(root, width=500)
self.frame.pack()
self.BuildMainWindow(self.frame)
self.ListboxSet = 0
self.frame.grid()
return
#displays the menu bar and options
def BuildMainWindow(self, frame):
menubar = Frame(frame,relief=RAISED,borderwidth=1)
menubar.pack()
mb_file = Menubutton(menubar,text='file')
mb_file.menu = Menu(mb_file)
mb_file.menu.add_command(label='open', command = self.openfile)
mb_file.grid(row = 1, column = 1)
mb_edit = Menubutton(menubar,text='edit')
mb_edit.menu = Menu(mb_edit)
mb_edit.grid(row=1, column = 3)
mb_file['menu'] = mb_file.menu
mb_edit['menu'] = mb_edit.menu
#upon selecting a directory from the menu and listboxes/buttons are created
def BuildListbox(self, directory):
self.listbox1 = Tkinter.Listbox()
self.listbox2 = Tkinter.Listbox()
self.listbox1.grid(row=2, column=1)
self.listbox2.grid(row=2 ,column=10)
self.listbox2.bind('<<ListboxSelect>>', lambda e:SortActions.GetWindowIndex(self,e))
self.listbox2.bind('<B1-Motion>', lambda e:SortActions.MoveWindowItem(self,e))
i = 0
for filename in sorted(os.listdir(directory)):
self.listbox1.insert(i, filename)
i = i + 1
self.bAddToListTwo = Button(self.frame, text = "->", command = lambda:SortActions.AddToListTwo(self,self.listbox1.curselection()))
self.bAddToListTwo.grid(row=5, column = 5)
self.bAddAll = Button(self.frame, text = "Add All To Playlist", command = lambda:SortActions.AddAllFiles(self))
self.bAddAll.grid(row=6, column = 5)
self.bRemoveFromListTwo = Button(self.frame, text = "Remove From Playlist", command = lambda:SortActions.RemoveFromListTwo(self,self.listbox2.curselection()))
self.bRemoveFromListTwo.grid(row=7, column = 5)
self.bSavePlaylist = Button(self.frame, text = "Save Playlist", command = lambda:SortActions.SaveList(self))
self.bSavePlaylist.grid(row=8, column = 5)
update - I got it to work. As I suspected, I had some weird frame configuration during the initialization.
def __init__(self,root):
self.BuildMainWindow(root)
self.ListboxSet = 0
return
#displays the menu bar and options
def BuildMainWindow(self, root):
menubar = Menu(root)
root.config(menu=menubar)
# Create a menu button labeled "File" that brings up a menu
filemenu = Menu(menubar)
menubar.add_cascade(label='File', menu=filemenu)
# Create entries in the "File" menu
# simulated command functions that we want to invoke from our menus
filemenu.add_command(label='Open', command=self.openfile)
filemenu.add_separator( )
filemenu.add_command(label='Quit', command=sys.exit)
#upon selecting a directory from the menu and listboxes/buttons are created
def BuildListbox(self, directory):
self.listbox1 = Tkinter.Listbox()
self.listbox2 = Tkinter.Listbox()
self.listbox1.grid(row=1, column=1, rowspan = 4, columnspan = 5)
self.listbox2.grid(row=1 ,column=15, rowspan = 4, columnspan = 5)
self.listbox2.bind('<<ListboxSelect>>', lambda e:SortActions.GetWindowIndex(self,e))
self.listbox2.bind('<B1-Motion>', lambda e:SortActions.MoveWindowItem(self,e))
i = 0
for filename in sorted(os.listdir(directory)):
self.listbox1.insert(i, filename)
i = i + 1
self.bAddToListTwo = Button(text = "->", command = lambda:SortActions.AddToListTwo(self,self.listbox1.curselection()))
self.bAddToListTwo.grid(row=1, column = 6)
self.bAddAll = Button(text = "Add All To Playlist", command = lambda:SortActions.AddAllFiles(self))
self.bAddAll.grid(row=2, column = 6)
self.bRemoveFromListTwo = Button(text = "Remove From Playlist", command = lambda:SortActions.RemoveFromListTwo(self,self.listbox2.curselection()))
self.bRemoveFromListTwo.grid(row=3, column = 6)
self.bSavePlaylist = Button(text = "Save Playlist", command = lambda:SortActions.SaveList(self))
self.bSavePlaylist.grid(row=4, column = 6)
Tkinter widgets can occupy more than 1 grid-cell, but you need to manage the layout yourself using the columnspan and rowspan keywords to .grid.
For example, to get the layout:
--------------------------------------------
| Button1 | Button2 | |
-------------------------------- Button3 |
| Label1 | Label2 | Label3 | |
--------------------------------------------
You'd need to do something like:
Button1.grid(row=0,column=0,columnspan=2)
Button2.grid(row=0,column=2)
Button3.grid(row=0,column=4,rowspan=2)
Label1.grid(row=1,column=0)
Label2.grid(row=1,column=1)
Label3.grid(row=1,column=2)
very minimal script
import Tkinter as tk
root = tk.Tk()
Button1 = tk.Button(root,text="Button1")
Button2 = tk.Button(root,text="Button2")
Button3 = tk.Button(root,text="Button3")
Label1 = tk.Label(root,text="Label1")
Label2 = tk.Label(root,text="Label2")
Label3 = tk.Label(root,text="Label3")
Button1.grid(row=0,column=0,columnspan=2)
Button2.grid(row=0,column=2)
Button3.grid(row=0,column=4,rowspan=2)
Label1.grid(row=1,column=0)
Label2.grid(row=1,column=1)
Label3.grid(row=1,column=2)
root.mainloop()

Categories