I am trying to place an OptionMenu widget inside of a frame, which itself is inside of a notebook. From what I've found online, the code for doing this is roughly:
# add a drop down menu
hops = range(0,6)
self.selectedHop = StringVar(frame2)
self.selectedHop.set(hops[0])
self.hopOptions = OptionMenu(frame2, self.selectedHop, *hops)
self.hopOptions.grid(row=0, column=2, sticky=EW)
However, when I place this in my code in the code below (the chunk above is placed towards the bottom of it, and is labeled "PROBLEMATIC CODE..."), my app just freezes and I have to force quit it, and I have no error message to debug with. Any help would be appreciated.
#!/usr/bin/python3
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
import subprocess
import sys
class ReportGUI:
def __init__(self, master):
self.master = master
master.title('Reporting')
master.resizable(True, True)
master.configure(background = '#b3e6cc')
master.minsize(width=800,height=700)
textcolor = "#003399"
self.style = ttk.Style()
self.style.configure('TFrame', background = '#e1d8b9')
self.style.configure('TButton', background = '#e1d8b9')
self.style.configure('TLabel', background = '#e1d8b9', font = ('Arial', 40))
self.style.configure('Header.TLabel', font = ('Arial', 30, 'bold'))
# step 1 - create notebook
self.notebook = ttk.Notebook(master)
self.notebook.pack()
# step 2 - create first frame to add to notebook
self.frame_logon = ttk.Frame(self.notebook)
# step 3 - add first frame to notebook and style it
self.notebook.add(self.frame_logon, text = 'Login')
self.frame_logon.config(padding = (20, 20, 20))
self.frame_logon.config(relief = RIDGE)
######### --- (1) LOGIN TAB ---- #########
label = ttk.Label(self.frame_logon, text = 'Administrative Reporting',
foreground=textcolor, style = 'Header.TLabel')
label.grid(row = 1, columnspan = 2)
# widget: username and password Label()
label2 = ttk.Label(self.frame_logon, text = 'Username:',
font = ('Arial', 17),foreground=textcolor)
label2.grid(row = 3, column = 0, padx = 5, sticky = 'sw')
label3 = ttk.Label(self.frame_logon, text = 'Password:',
font = ('Arial', 17),foreground=textcolor)
label3.grid(row = 3, column = 1, padx = 5, sticky = 'sw')
# widget: entry boxes Entry()
self.entry_name = ttk.Entry(self.frame_logon, width = 20, font = ('Arial', 15))
self.entry_pw = ttk.Entry(self.frame_logon, width = 20, font = ('Arial', 15))
# place the widgets
self.entry_name.grid(row = 4, column = 0, padx = 5)
self.entry_pw.grid(row = 4, column = 1, padx = 5)
self.entry_pw.config(show = '*') # make password not show
# widget: connect button Button()
self.loginButton = ttk.Button(self.frame_logon, text = 'Connect',
command = self.login)
self.loginButton.grid(row = 5, column = 1, columnspan = 2, padx = 1, pady = 1, sticky = 'e')
### COMMAND FUNCTIONS
def login(self):
# Make connections (TBA)
# 1) log into app
# 2) ssh into server port
# 3) connect to database
# if successful login and connection, launch reports tab
self.reportTab()
self.notebook.select(1) # switch tabs to reports tab
self.loginButton.state(['disabled']) # disable login button
# TAB 2: reporting tab
def reportTab(self):
# create report frame and add to notebook
self.frame_report = ttk.Frame(self.notebook)
self.notebook.add(self.frame_report, text = 'Report Options')
######### --- REPORT TAB ---- #########
#--------- FILTER 1: -----------
frame = ttk.Frame(self.frame_report)
frame.grid(row=1,column=0)
frame.config(height = 100, width = 200)
frame.config(relief = RIDGE)
ttk.LabelFrame(frame, height=100,width = 200,text = 'FILTER 1').pack()
#--------- FILTER 2: -----------
frame2 = ttk.Frame(self.frame_report)
frame2.grid(row=1,column=1)
frame2.config(height = 100, width = 200)
frame2.config(relief = RIDGE)
ttk.LabelFrame(frame2, height=100,width = 200,text = 'FILTER 2').pack()
#---------- PROBLEMATIC CODE: trying to add a drop down menu ----
hops = range(0,6)
self.selectedHop = StringVar(frame2)
self.selectedHop.set(hops[0])
self.hopOptions = OptionMenu(frame2, self.selectedHop, *hops)
self.hopOptions.grid(row=0, column=2, sticky=EW)
#----------------------------------------------------------------
#--------- FILTER 3: -----------
frame3 = ttk.Frame(self.frame_report)
frame3.grid(row=2,column=0)
frame3.config(height = 100, width = 200)
frame3.config(relief = RIDGE)
lbf3 = ttk.LabelFrame(frame3, height=100,width = 200,text = 'FILTER 3')
lbf3.pack()
#--------- FILTER 4: -----------
frame4 = ttk.Frame(self.frame_report)
frame4.grid(row=2,column=1)
frame4.config(height = 100, width = 200)
frame4.config(relief = RIDGE)
ttk.LabelFrame(frame4, height=100,width = 200,text = 'FILTER 4').pack()
# code for calling queries TBA
# launch results tab if queries successful
# self.resultsTab()
def func(self,value):
print(value)
# TAB 3: results tab
def resultsTab(self):
# create results frame and add to notebook
self.frame_results = ttk.Frame(self.notebook)
self.notebook.add(self.frame_results, text = 'Results')
def clear(self):
self.entry_name.delete(0, 'end')
self.entry_pw.delete(0, 'end')
self.text_comments.delete(1.0, 'end')
def main():
root = Tk()
hccwgui = ReportGUI(root)
root.mainloop()
if __name__ == "__main__": main()
Thanks in advance!
# ...
ttk.LabelFrame(frame2, height=100,width = 200,text = 'FILTER 2').pack()
# ...
self.hopOptions = OptionMenu(frame2, self.selectedHop, *hops)
self.hopOptions.grid(row=0, column=2, sticky=EW)
This is your problem. You're packing a LabelFrame in frame2, and then trying to grid something in frame2. A given container can only use one of grid() or pack()- using both won't cause an error, but the two managers will negotiate on how to place things for the rest of your lifetime.
The solution is to make sure that a given container's children are either packed or gridded, but never both in the same container (though you could have a structure parent->child->grandchild, where child is packed in parent and grandchild is gridded in child).
Related
I'm trying to incorporate a 'back button' in my Tkinter app in my 'second_home' to allow the user to return to the previous page - from second_home to home. I've incorporated a hide_frames() function to allow all the frames to disappear when called. I have this done on the second_home but I've commented out the function on the home function - the app won't run with the function called. I also have the home function called when launching the app.
When clicking on the back button titled "Previous Page" I receive the error 'TclError: bad window path name ".!labelframe.!canvas'
from tkinter import *
from tkinter import ttk
from tkinter.font import BOLD
win = Tk()
# Creating Frames
wrapper1 = LabelFrame(win, width = 900, height = 950)
wrapper2 = LabelFrame(win, width = 900, height = 950)
wrapper3 = LabelFrame(win, width = 900, height = 950)
wrapper4 = LabelFrame(win, width = 900, height = 950)
mycanvas = Canvas(wrapper1)
mycanvas.pack(side = LEFT, fill = "both", expand ="yes")
# Creating a Hide Frame Function
def hide_frames():
# Destroying the widgets in each frame
for widget in wrapper1.winfo_children():
widget.destroy()
for widget in wrapper2.winfo_children():
widget.destroy()
for widget in wrapper3.winfo_children():
widget.destroy()
for widget in wrapper4.winfo_children():
widget.destroy()
# Hiding all frames
wrapper1.pack_forget()
wrapper2.pack_forget()
wrapper3.pack_forget()
wrapper4.pack_forget()
def home():
#hide_menu_frames()
global myframe
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window = myframe, anchor = "nw")
wrapper1.pack(fill = "both", expand = "yes", padx = 10, pady = 10)
myframe.pack(fill = "both", expand = 1)
start_label = Label(myframe, text = "Choose Length of Loan Model", font = ("Helvetica", 19)).pack(pady = 100)
# Creating buttons
home_button1 = Button(myframe, text = "12 Month Model", bg='#ffffff', activeforeground='#4444ff', font = ("Helvetica", 16, BOLD), command = second_home).pack(pady = 10)
def second_home():
hide_frames()
mycanvas = Canvas(wrapper2)
mycanvas.pack(side = LEFT, fill = "both", expand ="yes")
yscrollbar = ttk.Scrollbar(wrapper2, orient = VERTICAL, command = mycanvas.yview)
yscrollbar.pack(side = RIGHT, fill = "y")
mycanvas.configure(yscrollcommand = yscrollbar.set)
mycanvas.bind('<Configure>',lambda e: mycanvas.configure(scrollregion = mycanvas.bbox('all')))
wrapper2.pack(fill = "both", expand = "yes", padx = 10, pady = 10)
myframe = Frame(mycanvas)
mycanvas.create_window((0,0), window = myframe, anchor = "nw")
second_label = Label(myframe, text = "Please Input Loan Parameters", font = ("Helvetica", 18), pady = 10, padx = 325).pack()
# Creating a Back Button
back_button = Button(myframe, text = "Previous Page", command = home)
back_button.pack(pady = 10)
# Defining a Main Menu
my_menu = Menu(win)
win.config(menu = my_menu)
# Creating Menu Items
app_menu = Menu(my_menu)
my_menu.add_cascade(label = "Options", menu = app_menu)
app_menu.add_command(label = "Home", command = home)
app_menu.add_separator()
app_menu.add_command(label = "Exit", command = win.quit)
win.geometry("900x950")
#win.resizable(False, False)
win.title("Loan Cash Flow Model Creator")
home()
win.mainloop()
I've built a GUI using Tkinter. On my dual monitor local machine, the appearance of the gui is as it was designed for.
I have the following issues though:
If I move the window to the second monitor, then the frames inside the main window don't resize according to the dimensions of the new monitor, so the gui looks like so I guess it has to do with the fact that I'm using the size of the monitor that the app is open to columnconfigure my frames.
screen_width = int(window.winfo_geometry().split('x', 1)[0])
tab_Digitizer.columnconfigure(0, minsize=int(0.75*screen_width), weight=80)
The second issue is that, if I connect from a remote machine (MacOS with XQuartz) using ssh, by default a small window opens and my gui looks like so
Oddly enough if I maximize the window, the layout gets completely messed up
Since I just started in Python and Tkinter probably I'm doing something wrong but can't figure out what.
Is there a way to automatically resize/rescale/shrink everything depending on the dimensions of the root window?
Also is it possible to do the same for the children of every parent i.e. widgets inside a frame
The code I have is super big to paste here, so I'm giving a link to it
https://pastebin.com/hHEYJ1bR but I have a snippet that might help
import tkinter as tk
import tkinter.font
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from PIL import ImageTk, Image
import screeninfo
from screeninfo import get_monitors
import os
# Method to make Label(Widget) invisible
def hide_frame(frame):
# This will remove the widget
frame.grid_remove()
# Method to make Label(widget) visible
def show_frame(frame, c, r, S, pdx):
# This will recover the widget
frame.grid(column=c, row=r, sticky=S, ipadx=(pdx,0))
# Method to make Label(widget) visible
def show_frame_padx(frame, c, r, S, pdx):
# This will recover the widget
frame.grid(column=c, row=r, sticky=S, padx=(pdx,0))
def populate(frame, rows):
'''Put in some fake data'''
for row in range(rows):
tk.Label(frame,
text = "%s" % row,
).grid(row=row, column=0)
t="Blah blah blah blah blah blah blah blah %s" %row
tk.Label(frame, text=t).grid(row = row,
column = 1,
sticky = EW
)
#____________________________________________________________________________________________
#This will be the main window
window = tk.Tk()
window.resizable(True, True)
window.attributes('-zoomed', True)
window.title("DICER daq")
default_font = tkinter.font.Font(font='TkDefaultFont')
screen_height = window.winfo_screenheight()
window.update()
screen_width = int(window.winfo_geometry().split('x', 1)[0])
screen_height = int(window.winfo_geometry().split('x', 1)[1].split('+', 1)[0])
#____________________________________________________________________________________________
#Upper frame
frame_logo = Frame(window, bd=10)
frame_logo.place(rely=0.0, relx=0.0, relwidth=1.0)
logo_label = tk.Label(frame_logo, text="daq", fg="red", font='Helvetica 18 bold')
logo_label.pack(anchor=W)
label_version = tk.Label(frame_logo, text="version test.0", fg="blue")
label_version.pack(side=LEFT)
#____________________________________________________________________________________________
#Lower frame
frame_main = Frame(window)
frame_main.place(rely=0.10, relx=0.0, relwidth=1.0)
#Create a tabcontrol
tabControl = ttk.Notebook(frame_main)
tabControl.grid(column=0, row=1)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Digitizer tab
tab_Digitizer = ttk.Frame(tabControl)
tabControl.add(tab_Digitizer, text=' Digitizer ')
tab_Digitizer.columnconfigure(0, minsize=int(0.75*screen_width), weight=80)
tab_Digitizer.columnconfigure(1, minsize=75, weight=10)
tab_Digitizer.columnconfigure(2, minsize=75, weight=10)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#This is to select available digitizer channels
def Digitizer_channel_selection(event):
print ("Digitizer channel selected:", var_digitizer_channel.get() )
pass
lbl_general_board_settings = Label(tab_Digitizer, text="Digitizer channel number", font='-weight bold')
lbl_general_board_settings.grid(column=0, row=0, sticky=W)
opt_digitizer_channel = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
var_digitizer_channel = IntVar(tab_Digitizer)
var_digitizer_channel.set(opt_digitizer_channel[0]) # default value
digitizer_channel = OptionMenu(tab_Digitizer, var_digitizer_channel, *opt_digitizer_channel, command = Digitizer_channel_selection)
digitizer_channel.grid(column=1, row=0, sticky=NSEW)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#General board settings
lbl_general_board_registers = Label(tab_Digitizer,
text = "General board registers",
font = '-weight bold',
)
lbl_general_board_registers.grid(column = 0,
row = 1,
sticky = W
)
btn_hide_general_board_registers = Button(tab_Digitizer,
text = "Hide",
font = '-weight bold',
command = lambda: hide_frame(frame_general_board_registers)
)
btn_hide_general_board_registers.grid(column = 1,
row = 1,
sticky = EW
)
btn_show_general_board_registers = Button(tab_Digitizer,
text = "Show",
font = '-weight bold',
command = lambda: show_frame_padx(frame_general_board_registers, 0, 2, EW, 0)
)
btn_show_general_board_registers.grid(column = 2,
row = 1,
sticky = EW
)
frame_general_board_registers = Frame(tab_Digitizer
)
frame_general_board_registers.columnconfigure(0, minsize=int(0.49*0.75*screen_width), weight=50)
frame_general_board_registers.columnconfigure(1, minsize=int(0.25*0.75*screen_width), weight=25)
frame_general_board_registers.columnconfigure(2, minsize=int(0.25*0.75*screen_width), weight=25)
frame_general_board_registers.config(highlightbackground="black", highlightthickness=3)
frame_general_board_registers.grid(column = 0,
row = 2,
sticky = EW,
ipadx = 0
)
populate(frame_general_board_registers, 35)
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#Channel settings
lbl_channel_registers = Label(tab_Digitizer,
text = "Channel registers",
font = '-weight bold'
)
lbl_channel_registers.grid(column = 0,
row = 3,
sticky = W
)
btn_hide_channel_registers = Button(tab_Digitizer,
text="Hide",
font="-weight bold",
command=lambda: hide_frame(frame_channel_registers)
)
btn_hide_channel_registers.grid(column = 1,
row = 3,
sticky = EW
)
btn_show_channel_registers = Button(tab_Digitizer,
text = "Show",
font = '-weight bold',
command = lambda: show_frame_padx(frame_channel_registers, 0, 4, W, 0)
)
btn_show_channel_registers.grid(column = 2,
row = 3,
sticky = EW
)
frame_channel_registers = Frame(tab_Digitizer
)
frame_channel_registers.grid(column = 0,
row = 4,
sticky = W
)
frame_channel_registers.columnconfigure(0, minsize=int(0.840*0.75*screen_width), weight=84)
frame_channel_registers.columnconfigure(1, minsize=int(0.075*0.75*screen_width), weight=8)
frame_channel_registers.columnconfigure(2, minsize=int(0.075*0.75*screen_width), weight=8)
frame_channel_registers.config(highlightbackground="black", highlightthickness=3)
##Channel settings - PSD
def onFrameConfigure(event):
'''Reset the scroll region to encompass the inner frame'''
canvas.configure(scrollregion=canvas.bbox("all"))
def handle_user_scrolling_up(event):
canvas.yview_scroll(-5, "units")
def handle_user_scrolling_down(event):
canvas.yview_scroll(5, "units")
def handle_scrollbar_scrolling(event):
canvas.configure(scrollregion=canvas.bbox("all"))
lbl_PSD_channel_registers = Label(frame_channel_registers,
text = "DPP-PSD registers",
font = (default_font.actual(), "10", "bold")
)
lbl_PSD_channel_registers.grid(column = 0,
row = 3,
ipadx = 20,
sticky = W)
btn_hide_PSD_channel_registers = Button(frame_channel_registers,
text = "Hide",
font = (default_font.actual(), "10", "bold"),
command = lambda: [hide_frame(canvas), hide_frame(myscrollbar)]
)
btn_hide_PSD_channel_registers.grid(column = 1,
row = 3,
sticky = EW
)
btn_show_PSD_channel_registers = Button(frame_channel_registers,
text = "Show",
font = (default_font.actual(), "10", "bold"),
command = lambda: [show_frame_padx(canvas, 0, 4, EW, 50), show_frame_padx(myscrollbar, 1, 4, NS, 0)])
btn_show_PSD_channel_registers.grid(column = 2,
row = 3,
sticky = EW
)
canvas = tk.Canvas(frame_channel_registers,
height = int(0.20*screen_height)
)
frame_PSD_channel_registers = Frame(canvas
)
frame_PSD_channel_registers.grid(column = 0,
row = 0,
sticky = EW
)
frame_PSD_channel_registers.bind("<Button-4>" , handle_user_scrolling_up)
frame_PSD_channel_registers.bind("<Button-5>" , handle_user_scrolling_down)
frame_PSD_channel_registers.bind("<Configure>", handle_scrollbar_scrolling)
myscrollbar=Scrollbar(frame_channel_registers ,orient="vertical", command=canvas.yview)
canvas.configure(scrollregion=canvas.bbox("all"))
myscrollbar.grid(column = 1,
row = 4,
sticky = NS,
ipadx = 0)
canvas.grid(column = 0,
row = 4,
sticky = EW,
padx = (50,0))
canvas.create_window((0, 0), window=frame_PSD_channel_registers, anchor="nw")
canvas.configure(yscrollcommand=myscrollbar.set)
canvas.columnconfigure(0, minsize=int(0.90*0.84*0.75*screen_width), weight=99)
canvas.columnconfigure(1, minsize=int(0.10*0.84*0.75*screen_width), weight=1)
frame_PSD_channel_registers.columnconfigure(0, minsize=int(0.20*0.84*0.75*screen_width), weight=20)
frame_PSD_channel_registers.columnconfigure(1, minsize=int(0.10*0.84*0.75*screen_width), weight=10)
frame_PSD_channel_registers.columnconfigure(2, minsize=int(0.60*0.84*0.75*screen_width), weight=50)
frame_PSD_channel_registers.config(highlightbackground="black", highlightthickness=1)
populate(frame_PSD_channel_registers, 35)
#This keeps the window open - has to be at the end
window.mainloop()
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()
I cannot for the life in me work out why this does not work. I am using TKinter to display an image in Python. The code I have written so far is shown here:
from tkinter import *
from tkinter import messagebox
def unlock():
root.withdraw()
def logout():
inside.destroy()
root.deiconify()
#######################
### ###
### unlock Form ###
### ###
#######################
inside = Tk()
inside.geometry("576x576")
inside.title("SAFE CRACKER")
# LABELS, Textboxes and Buttons
imageInside = PhotoImage(file = "images/inside.gif")
imageLabel = Label(inside,image = imageInside).grid(row =1, columnspan = 2)
label = Label(inside, text="Safe Cracker", font = ("Arial",16)).grid(row = 0, columnspan = 2)
exitButton = Button(inside, text = "Exit", width = 15, command = logout )
exitButton.grid(row = 3, column = 0,padx = 10, pady = 10)
#######################
### ###
### Main Form ###
### ###
#######################
root = Tk()
root.geometry("430x450")
root.title("SAFE CRACKER")
# LABELS, Textboxes and Buttons
label = Label(root, text="Safe Cracker", font = ("Arial",16)).grid(row = 0, columnspan = 2)
imageSafe = PhotoImage(file = "images/safe.gif")
imageLabel = Label(root,image = imageSafe).grid(row =1, columnspan = 2)
label = Label(root, text = "Enter the code", font = ("Arial",12)).grid(row = 2, column = 0)
unlockCode = Entry(root, width = 30)
unlockCode.grid(row = 2, column = 1,padx = 10, pady = 10)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 3, column = 0,padx = 10, pady = 10)
enterButton = Button(root, text = "Enter the VAULT", width = 15, command = unlock).grid(row = 3, column = 1,padx = 10, pady = 10)
The code doesn't do much at present, however it's something i'm working on. When I run the program it will display a picture of a safe (Great), and when I click the button it moves over to the next form.
image of safe working
On the new form the image, labels and buttons do not display, however, when the image code is removed it all works swimmingly.
Initially I thought about putting the root form within a function, however, whenever I place this code within a function it fails to load the image (ahhhh). Can these images not be placed within functions?
You need to keep a reference to the photo.
imageInside = PhotoImage(file = "images/inside.gif")
imageLabel = Label(inside,image = imageInside)
imageLabel.grid(row =1, columnspan = 2)
imageLabel.photo_ref = imageInside # keep a reference!
Thank you for your help.
I can confirm that this works, here's the code.
from tkinter import *
from tkinter import messagebox
def unlock():
root.withdraw()
def logout():
inside.destroy()
root.deiconify()
#######################
### ###
### unlock Form ###
### ###
#######################
inside = Toplevel()
inside.geometry("576x576")
inside.title("SAFE CRACKER")
# LABELS, Textboxes and Buttons
imageInside = PhotoImage(file = "images/inside.gif")
imageLabel = Label(inside,image = imageInside)
imageLabel.grid(row =1, columnspan = 2)
imageLabel.photo_ref = imageInside
label = Label(inside, text="Safe Cracker", font = ("Arial",16)).grid(row = 0, columnspan = 2)
exitButton = Button(inside, text = "Exit", width = 15, command = logout )
exitButton.grid(row = 3, column = 0,padx = 10, pady = 10)
#######################
### ###
### Main Form ###
### ###
#######################
root = Tk()
root.geometry("430x450")
root.title("SAFE CRACKER")
# LABELS, Textboxes and Buttons
label = Label(root, text="Safe Cracker", font = ("Arial",16)).grid(row = 0, columnspan = 2)
imageSafe = PhotoImage(file = "images/safe.gif")
imageLabel = Label(root,image = imageSafe).grid(row =1, columnspan = 2)
label = Label(root, text = "Enter the code", font = ("Arial",12)).grid(row = 2, column = 0)
unlockCode = Entry(root, width = 30)
unlockCode.grid(row = 2, column = 1,padx = 10, pady = 10)
exitButton = Button(root, text = "Exit", width = 15, command = exit).grid(row = 3, column = 0,padx = 10, pady = 10)
enterButton = Button(root, text = "Enter the VAULT", width = 15, command = unlock).grid(row = 3, column = 1,padx = 10, pady = 10)
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'.