Tkinter widgets becomes unresponsive while configuring them inside loop - python

I am trying to make a link monitoring application using python. Following is the code which I am using to monitor the link status.
import tkinter as tk
from tkinter import ttk
import tkinter.scrolledtext as scrolledtext
import telnetlib
import time
import datetime
import schedule
import winsound
duration = 2000 # milliseconds
freq_down = 880 # Hz
freq_up = 440 # Hz
import win32ui
window = tk.Tk()
window.geometry('950x700')
window.option_add('*Font', '19')
window.resizable(False, False)
window.title("Link Monitoring Application")
# Label
ttk.Label(window, text = "Select Link :",
font = ("Times New Roman bold", 16)).grid(column = 0,
row = 0, padx = (10, 5), pady = 15, sticky='EW')
n = tk.StringVar()
linkchoosen = ttk.Combobox(window, width = 47,
textvariable = n)
linkchoosen['values'] = ('LINK-1: 192.168.xx.xx',
'LINK-2: 192.168.xx.xx')
linkchoosen.grid(column = 1, row = 0, sticky='EW', padx = (10,15))
n.set('Select')
def chk_telnet(host_addr):
cb_data = host_addr
host_addr = cb_data.split(": ",1)[1]
host_name = cb_data.split(": ",1)[0]
tn = telnetlib.Telnet(host_addr)
tn.write(b"command\r\n")
time.sleep(5)
tn.write(b"exit\n")
output = tn.read_until(b"exit")
time.sleep(5)
tn.close()
output_str = output.decode('utf-8')
if "ALARMS = NONE" in output_str:
txt.insert(tk.INSERT, str(datetime.datetime.now()) + ' ' + host_name + " UP\n")
if ("LINK-1" in host_name):
btn_status_1.config(background = 'green')
elif ("LINK-2" in host_name):
btn_status_2.config(background = 'green')
# pass
elif ("ALARMS = LOCAL SYNC LOSS" in output_str):
txt.insert(tk.INSERT, str(datetime.datetime.now()) + ' ' + host_name + " DOWN\n")
if ("LINK-1" in host_name):
btn_status_1.config(background = 'red')
elif ("LINK-2" in host_name):
btn_status_2.config(background = 'red')
winsound.Beep(freq_down, duration)
elif ("ALARMS = REMOTE SYNC LOSS" in output_str):
# print("Link is Down")
txt.insert(tk.INSERT, str(datetime.datetime.now()) + ' ' + host_name + " DOWN\n")
if ("LINK-1" in host_name):
btn_status_1.config(background = 'red')
elif ("LINK-2" in host_name):
btn_status_2.config(background = 'red')
winsound.Beep(freq_down, duration)
def check_link_status():
host_addr = linkchoosen.get()
if ('Select' in host_addr):
win32ui.MessageBox("Please select a correct option", "Error")
return
else:
chk_telnet(host_addr)
schedule.run_pending()
window.after(1000, check_link_status)
btn = tk.Button(window, text = 'OK',font = ("Times New Roman bold", 12),
command = check_link_status)
btn.grid(column = 2, row = 0, sticky='EW', padx = (10,15))
btn_frame = tk.Frame(window)
btn_frame.grid(column = 1, row = 1, sticky='EW', padx = (10,15), pady =(10, 10))
btn_status_1 = tk.Button(btn_frame, text = 'LINK-1 STATUS', font = ("Times New Roman bold", 18), command = "", height = 3)
btn_status_1.pack(side = 'left')
btn_status_2 = tk.Button(btn_frame, text = 'LINK-2 STATUS', font = ("Times New Roman bold", 18), command = "", height = 3)
btn_status_2.pack(side = 'right')
txt_frame = tk.Frame(window)
txt_frame.grid(column = 0, row = 2, columnspan = 3, sticky='NSEW', padx
= (10,15), pady = (20, 10))
txt = scrolledtext.ScrolledText(txt_frame, undo=True)
txt['font'] = ('consolas', '12')
txt.grid(column = 0, row = 0, padx = (10,15), pady = (20, 10),
sticky='NSEW')
txt_frame.grid_rowconfigure(0, weight=1)
txt_frame.grid_columnconfigure(0, weight=1)
window.grid_rowconfigure(0, weight=1)
window.grid_columnconfigure(1, weight=1)
window.mainloop()
The above code is running fine and link up/down status are properly updated in the respective buttons and the same is entered as text in scrolled text box.
The issue which I am facing is - while the program is running then "clicking the dropdown button in combobox" or "clicking or selecting the text from textbox" makes GUI unresponsive.
Kindly suggest how to solve this issue so that the GUI becomes responsive.

Related

Checkbutton is not clickable after adding an image to it

I started working with tkinter recently and I have the following problem, I need to make the check box bigger but that is only possible with adding an image. The problem is that whenever I add an image to a button it becomes unclickable and the image is not displayed, here is my source code (part of a bigger project). My goal is to display some information and let the user decide which option he gets to keep using the check button. Any help is appreciated.
import tkinter as tk
import tkcalendar as tkc
LARGE_FONT = ("HELVETICA", 32, 'bold')
NORMAL_FONT = ("calibri", 18)
class ConstituireDosar(tk.Toplevel):
def __init__(self, controller):
tk.Toplevel.__init__(self)
self.update_idletasks()
# self.dosar = dosar
self.controller = controller
self.minsize(651, 569)
# self.maxsize(651, 569)
frame_titlu = tk.Frame(self)
frame_titlu.grid(row = 0, column = 0)
frame_continut = tk.Frame(self)
frame_continut.grid(row = 1, column = 0, sticky = "w")
frame_acte = tk.Frame(self)
frame_acte.grid(row = 2, column = 0)
titlu = tk.Label(frame_titlu, font = LARGE_FONT, text = "Constituire Dosar")
titlu.grid(row = 0 , column = 0, padx = 10, pady = 15)
data_emiterii = tk.Label(frame_continut, font = NORMAL_FONT,text = "Data emiterii documentului:")
data_emiterii.grid(row = 1, column = 0, padx = 10, pady = 5, sticky = "w")
self.cal = tkc.DateEntry(frame_continut, date_pattern = "DD/MM/YYYY", width = 20)
self.cal.grid(row = 2, column = 0, padx = 10, pady = 5, sticky = "w")
debitori_label = tk.Label(frame_continut, font = NORMAL_FONT, text = "Selecteaza debitorii.")
debitori_label.grid(row = 3, column = 0, padx = 10, pady = 5, sticky = "w")
debitori = []
tip_debitori = []
for i in range(2):
debitori.append("Person %s " % str(i))
tip_debitori.append("Person %s type" % str(i))
for i in range(len(debitori)):
print(debitori[i])
row_i = 4
self.vars_debitori = []
on_image = tk.PhotoImage(width=48, height=24)
off_image = tk.PhotoImage(width=48, height=24)
on_image.put(("green",), to=(0, 0, 23,23))
off_image.put(("red",), to=(24, 0, 47, 23))
for i in range(len(debitori)):
var = tk.IntVar(frame_continut, value = 0)
interior = debitori[i] + " - " + tip_debitori[i]
# Checkbutton(ws, image=switch_off, selectimage=switch_on, onvalue=1, offvalue=0, variable=cb1, indicatoron=False, command=switchState)
checkbuton = tk.Checkbutton (frame_continut, bd = 5, image = off_image, selectimage = on_image, indicatoron = False, onvalue = 1, offvalue = 0, variable = var, state = tk.ACTIVE, command = lambda: self.toggle(var))
checkbuton.grid(row = row_i, column = 0, padx = 20, pady = 5, sticky = "nw")
checkbuton.image = off_image
# checkbuton.select()
self.vars_debitori.append(var)
row_i += 1
self.vars_acte = []
acte = ["Acte de Procedura", "Incheiere de Admitere", "Cerere de Incuviintare", "Instiintare Creditor"]
for i in range(4):
v = tk.IntVar()
check = tk.Checkbutton(frame_acte, font = NORMAL_FONT, text = acte[i], variable = v)
check.grid(row = row_i, column = 0, padx = 10, pady = 5)
check.select()
self.vars_acte.append(v)
row_i += 1
emite_acte = tk.Button(frame_acte, font = NORMAL_FONT, text = "Emite acte.", command = self.emite_acte)
emite_acte.grid(row = row_i, column = 1, padx = 15, pady = 30, ipadx = 70, ipady = 10)
emite_acte.configure(bg = '#218838', fg = '#FFFFFF')
buton_cancel = tk.Button(frame_acte, font = NORMAL_FONT, text = "Cancel", command = lambda: self.destroy())
buton_cancel.grid(row = row_i, column = 0, padx = 15, pady = 30, ipadx = 70, ipady = 10)
buton_cancel.configure(bg = "red", fg = '#FFFFFF')
def emite_acte(self):
print(self.cal.get_date().strftime("%d/%m/%y"))
print(self.winfo_height(), self.winfo_width())
if __name__ == "__main__":
root = tk.Tk()
app = ConstituireDosar(root)
app.protocol("WM_DELETE_WINDOW", root.destroy)
root.withdraw()
root.mainloop()
I tried some options that I saw on the forum, in another file they worked fine but when I tried to implement it in the project itself the checkbutton is still unclickable and it doesn't display the images either. tkinter checkbutton different image I tried to replicate Bryan's answer, but no luck there. Also didn't receive any console error message.
As #furas pointed in the comments above, the problem got fixed with keeping the images as member variables of the class, also the button became clickable after removing the self.toggle(var) command from checkbutton

Tkinter app doesn't keep proportions when moved in other screen or run through ssh

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

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

Why does the entry widget not work in this case i put it in a frame, tkinter-python

So this is a basic clock and alarm that i am creating, and in the process i want the user to type in what hour and minute they want to set for the alarm. But the entry widget here is not responding.
import time
import tkinter as tk
current_date, current_time = 0, 0
def current_info(timeinfo): #function to get the current time and date
global current_date, current_time
# current time
current_time = time.strftime('%H:%M:%S')
current_date = time.strftime(r'%m/%d/%Y')
clock.after(200, timeinfo)
#Initialise the window
clock = tk.Tk()
clock.title('Easy CLock')
clock.configure(bg='#121212')
clock.columnconfigure(0, weight = 1)
clock.columnconfigure(1, weight = 1)
clock.columnconfigure(2, weight = 1)
clock.columnconfigure(3, weight = 1)
border_effects = {
"flat": tk.FLAT,
"sunken": tk.SUNKEN,
"raised": tk.RAISED,
"groove": tk.GROOVE,
"ridge": tk.RIDGE,
}
#Logo will be under the main parent
logo = tk.PhotoImage(file = r'C:\Users\User\VSC\Alarm\Logo1.png')
logo_size = logo.subsample(5)
#Time and Date function
def time_date():
current_info(time_date)
#Displays the time
c_time = tk.Label(f_time, text = current_time, fg='white', bg='#121212', font=('Verdana', 30))
c_date = tk.Label(f_time, text = current_date, font=('Verdana', 10), fg='white', bg='#121212')
c_time.grid(column=0, row=0)
c_date.grid(column=0, row=1)
#alarm button command
def alarm_func():
current_info(alarm_func)
c_time = tk.Label(f_alarm, text = current_time, fg='white', bg='#121212', font=('Verdana', 10))
c_date = tk.Label(f_alarm, text = current_date, font=('Verdana', 10), fg='white', bg='#121212')
def pressed_enter(): #Command for the enter button
set_label = tk.Label(f_alarm, text = f'Alarm has been set for {time_set}', fg ='white', bg = '#121212', borderwidth = 1, relief = border_effects['sunken'])
set_label.grid(column = 4, row = 0, sticky = 'W')
# Set the time and date for the alarm
set_time = tk.StringVar()
alarm_entry = tk.Entry(clock, textvariable = set_time)
set_time.set('H : M')
time_set = alarm_entry.get()
#label and entry to set alarm / Enter Button
c_label = tk.Label(f_alarm, text = 'Set Alarm: ', font = ('Verdana', 10), fg= 'white', bg ='#121212' )
alarm_enter = tk.Button(f_alarm, text = 'Enter', font = ('Verdana', 7), width = 5, command = pressed_enter)
#Pack the widgets
c_time.grid(row = 0, column = 0)
c_date.grid(column = 1 , row = 0)
alarm_enter.grid(row = 2, column = 3)
c_label.grid(row = 2, sticky = 'W')
alarm_entry.grid(row = 2, column = 1)
#configure the empty columns
f_alarm.columnconfigure(2, minsize = 10)
def recall_frame(event):
if event == f_alarm:
event.grid_forget()
f_time.grid(column=0, row =1, columnspan = 4, sticky = 'N')
elif event == f_time:
event.grid_forget()
f_alarm.grid(column=0, row=1, columnspan = 4, sticky = 'W')
def back_func():
pass
#Creating Frames
f_time = tk.Frame(clock) #Clock Button
f_alarm = tk.Frame(clock) #Alarm Buttton
#configure the frames
f_time.configure(bg = '#121212')
f_alarm.configure(bg = '#121212')
#Setting label in the frame
f_lbl = tk.Label(clock, text= ' Simplistic Clock', image = logo_size, font=('Verdana', 30), fg='white', bg='#121212', compound = tk.LEFT, padx = 35)
time_but = tk.Button(clock, text='Clock', command= lambda :[time_date(), recall_frame(f_alarm)], bg='#f39c12', relief = border_effects['ridge'], pady = 7)
alarm_but = tk.Button(clock, text = 'Alarm', command = lambda :[alarm_func(), recall_frame(f_time)], bg='#f39c12', relief = border_effects['ridge'], pady = 7)
quit_but = tk.Button(clock, text='Exit', command = clock.quit, bg='#f39c12', relief = border_effects['ridge'], pady = 7)
back_but = tk.Button(clock, text = 'Back ', command = back_func, bg='#f39c12', relief = border_effects['ridge'], pady = 7)
f_lbl.config(borderwidth = 4, relief = border_effects['sunken'])
#Putting it on the frames
f_lbl.grid(column = 0, row = 0, columnspan = 5, sticky = 'EW')
time_but.grid(column = 0, row = 3, sticky = 'EW')
alarm_but.grid(column = 1, row = 3, sticky = 'EW')
quit_but.grid(column = 3, row = 3, sticky = 'EW')
back_but.grid(column = 2, row = 3, sticky = 'EW')
clock.mainloop()
i tried testing an entry widget outside the frame and the entry widget was able to work, is it because the frame f_alarm is not looping constantly in the background?
When someone clicks on your button which activates the pressed_enter() function, it will call that function again every time which will set the time to H:M and it will get that value as the set_time.get() is called immediately after.
You're also creating a new Entry every time the button is being clicked because you put alarm_entry = tk.Entry(clock, textvariable=set_time)
in there as well. You should only put the set_time.get inside of that button so that it gets the value that is currently filled in into the Entry. The other things like
set_time = tk.StringVar()
alarm_entry = tk.Entry(clock, textvariable=set_time)
set_time.set('H : M')
Should be put outside of that function so they don't get called every time someone clicks on the button.

How do I get a output into a tkinter Entry field

Im making my own cypher encryption and I want to put the result into the entry field called output. Now im just using print() so I could test if I got a result. But that was only for testing. This is one of the first times I used Python so if there are some other things I could have done better please let me know :)
this is what I have so far.
from tkinter import *
#Make frame
root = Tk()
root.geometry("500x300")
root.title("Encryption Tool")
top_frame = Frame(root)
bottom_frame = Frame(root)
top_frame.pack()
bottom_frame.pack()
#Text
headline = Label(top_frame, text="Encryption Tool", fg='black')
headline.config(font=('Courier', 27))
headline.grid(padx=10, pady=10)
Key = Label(bottom_frame, text="Key:", fg='black')
Key.config(font=('Courier', 20))
Key.grid(row=1)
Text_entry = Label(bottom_frame, text="Text:", fg='black')
Text_entry.config(font=('Courier', 20))
Text_entry.grid(row=2)
Output_text = Label(bottom_frame, text="Output:", fg='black')
Output_text.config(font=('Courier', 20))
Output_text.grid(row=3)
Key_entry = Entry(bottom_frame)
Key_entry.grid(row=1, column=1)
Text_entry = Entry(bottom_frame)
Text_entry.grid(row=2, column=1)
Output_text = Entry(bottom_frame)
Output_text.grid(row=3, column=1)
#Encryption_button
def encrypt():
result = ''
text = ''
key = Key_entry.get()
text = Text_entry.get()
formule = int(key)
for i in range(0, len(text)):
result = result + chr(ord(text[i]) + formule + i * i)
result = ''
Encryption_button = Button(bottom_frame, text="Encrypt", fg='black')
Encryption_button.config(height = 2, width = 15)
Encryption_button.grid(row = 4, column = 0, sticky = S)
Encryption_button['command'] = encrypt
#Decryption_button
def decrypt():
result = ''
text = ''
key = Key_entry.get()
text = Text_entry.get()
formule = int(key)
for i in range(0, len(text)):
result = result + chr(ord(text[i]) - formule - i * i)
print(result)
result = ''
Decryption_button = Button(bottom_frame, text="Decrypt", fg="black")
Decryption_button.config(height = 2, width = 15)
Decryption_button.grid(row = 5, column = 0, sticky = S)
Decryption_button['command'] = decrypt
#Quit_button
def end():
exit()
Quit_button = Button(bottom_frame, text="Quit", fg='black')
Quit_button.config(height = 2, width = 15)
Quit_button.grid(row = 6, column = 0, sticky = S)
Quit_button['command'] = end
root.mainloop()
The most common way to do this with tkinter would be with a StringVar() object you can connect to the Entry object (Some documentation here).
output_entry_value = StringVar()
Output_text = Entry(bottom_frame, textvariable=output_entry_value)
Output_text.grid(row=3, column=1)
then you can .set() the result in the stringvar, and it will update in the entries you connected it to:
output_entry_value.set(result)

Categories