Tkinter remove/overwrite elements from Frame - python

I created a button that retrieves a list from a DataFrame based on some input from a text field. Everytime the button is pressed, the list will be refreshed. I output the list (as an OptionMenu) in a separate Frame (outputFrame). However, every time I press this button, a new OptionMenu is added to the Frame (instead of overwriting the previous one). How can I make sure that the content of 'ouputFrame' is overwritten each time I press the button?
# start
root = Tkinter.Tk()
# frames
searchBoxClientFrame = Tkinter.Frame(root).pack()
searchButtonFrame = Tkinter.Frame(root).pack()
outputFrame = Tkinter.Frame(root).pack()
# text field
searchBoxClient = Tkinter.Text(searchBoxClientFrame, height=1, width=30).pack()
# function when button is pressed
def getOutput():
outputFrame.pack_forget()
outputFrame.pack()
clientSearch = str(searchBoxClient.get(1.0, Tkinter.END))[:-1]
# retrieve list of clients based on search query
clientsFound = [s for s in df.groupby('clients').count().index.values if clientSearch.lower() in s.lower()]
clientSelected = applicationui.Tkinter.StringVar(root)
if len(clientsFound) > 0:
clientSelected.set(clientsFound[0])
Tkinter.OptionMenu(outputFrame, clientSelected, *clientsFound).pack()
else:
Tkinter.Label(outputFrame, text='Client not found!').pack()
Tkinter.Button(searchButtonFrame, text='Search', command=getOutput).pack()
root.mainloop()

We can actually update the value of the OptionMenu itself rather than destroying it (or it's parent) and then redrawing it. Credit to this answer for the below snippet:
import tkinter as tk
root = tk.Tk()
var = tk.StringVar(root)
choice = [1, 2, 3]
var.set(choice[0])
option = tk.OptionMenu(root, var, *choice)
option.pack()
def command():
option['menu'].delete(0, 'end')
for i in range(len(choice)):
choice[i] += 1
option['menu'].add_command(label=choice[i], command=tk._setit(var, choice[i]))
var.set(choice[0])
button = tk.Button(root, text="Ok", command=command)
button.pack()
root.mainloop()

Related

How to add a new entry box when clicking a button (Python, Tkinter)

Hi i want to add a new entry box when clicking a button. How can i do that ?
What've done is im able to "for loop" a group of entry boxes. But i want the entry boxes to appear one by one by clicking a button.
What've done
My code:
import tkinter as tk
from tkinter import *
root = Tk()
root.title("Entry box")
root.geometry("700x500")
my_entries = []
def something():
entry_list = ''
for entries in my_entries:
entry_list = entry_list + str(entries.get()) + '\n'
my_label.config(text=entry_list)
print(my_entries[0].get())
for x in range(5):
my_entry = Entry(root)
my_entry.grid(row=0, column=x, pady=20, padx=5)
my_entries.append(my_entry)
my_button = Button(root, text="Click Me!", command=something)
my_button.grid(row=1, column=0, pady=20)
There is not much of work here, create a variable to keep track of the columns you are inserting the widget into and then just insert it based on that number, like:
# Rest of your code..
my_entries = []
count = 0 # To keep track of inserted entries
def add():
global count
MAX_NUM = 4 # Maximum number of entries
if count <= MAX_NUM:
my_entries.append(Entry(root)) # Create and append to list
my_entries[-1].grid(row=0,column=count,padx=5) # Place the just created widget
count += 1 # Increase the count by 1
Button(root, text='Add', command=add).grid(row=1, column=1, padx=10) # A button to call the function
# Rest of your code..
Though I am not sure about your other function and its functionality, but it should work after you create entries and then click that button.

How can I store data on Toplevel?

On the toplevel window, if the toplevel is closed after get some input from the user using the entry widget, and then the same toplevel is opened by pressing the same button, is there a way to see the entry we received from the user in the entry widget?
For example user enter his name on toplevel window, and close the toplevel.Then the user open same toplevel,i want it to see his name in the entry widget.
Try this:
import tkinter as tk
last_user_input_entry = ""
last_user_input_button = 0
def on_closing():
global entry, top, last_user_input_entry, last_user_input_button, button_var
text = entry.get()
last_user_input_entry = text
last_user_input_button = button_var.get()
print("The text entered =", last_user_input_entry)
print("Checkbutton state =", last_user_input_button)
top.destroy()
def start_top():
global entry, top, button_var
top = tk.Toplevel(root)
top.protocol("WM_DELETE_WINDOW", on_closing)
entry = tk.Entry(top)
entry.pack()
entry.insert("end", last_user_input_entry)
button_var = tk.IntVar()
button_var.set(last_user_input_button)
button = tk.Checkbutton(top, variable=button_var)
button.pack()
root = tk.Tk()
button = tk.Button(root, text="Open toplevel", command=start_top)
button.pack()
root.mainloop()
Basically we intercept the window closing and handle that our self. We also have a variable that stored the last user input and we put it in the tkinter.Entry after we create it.

Enabling button upon text entry

New to Tkinter and a while since I did Python.
I have a page with multiple entry boxes.
I want the button to be disabled unless all boxes have text in them.
When it is enabled the button will open up a new form.
Here's the code:
from tkinter import *
def only_numeric_input(P):
# checks if entry's value is an integer or empty and returns an appropriate boolean
if P.isdigit() or P == "": # if a digit was entered or nothing was entered
return True
return False
def toggle_state(*_):
if entry1.var.get():
button['state'] = 'disabled'
else:
button['state'] = 'normal'
def raise_frame(frame):
frame.tkraise()
main_win = Tk()
main_win.geometry('500x500')
###################################################
second_frame = Frame(main_win)
second_frame.place(x=0, y=0, width=500, height=250)
Main_frame = Frame(main_win)
Main_frame.place(x=0, y=0, width=500, height=250)
###################################################
#I want it so that the button only allows the user to press it if the entry widget has numbers inside. That
#will then open up the 2nd frame
entry_1 = Entry(Main_frame)
entry_1.place(x=200, y=50)
entry_1_check = Main_frame.register(only_numeric_input)
entry_1.configure(validate="key", validatecommand=(entry_1_check, "%P"))
button1=Button(Main_frame, text='Admin', width=20, bg='brown', fg='white',
command=lambda:[toggle_state, raise_frame(second_frame)])
button1.place(x=0, y=0)
main_win.mainloop()
P.S this may not be preferred method but I am just figuring some stuff out :)
In the description of the question, several entry boxes are mentioned so I made my solution work with an arbitrary number of entry boxes (4 in the example).
What should change the state of the button is when all entries contain numbers, therefore it does not make sense to execute toggle_state() in the button's command. On the contrary, it should be executed when the entries' content is modified.
My solution to toggle the state of the button, is to call the toggle_state() function inside the only_numeric_input() function so that it is executed every time the content of one of the entries is changed. However this means that the function is executed before the entry content is changed, so entry.get() cannot be used. Therefore I use a dictionary to keep track of which entry is empty: not_empty = {<entry name>: bool, ...} which is convenient because we can get the entry name with "%W" in validatecommand. I update this dictionary inside only_numeric_input() before executing toggle_state().
Here is the code:
from tkinter import Entry, Tk, Button, Frame
def only_numeric_input(P, W):
if P.isdigit() or P == "":
not_empty[W] = P != "" # update state of the entry in dictionary
toggle_state() # update button's state
return True
return False
def toggle_state():
none_empty = True
for b in not_empty.values():
none_empty = none_empty and b
if none_empty: # all entries contain numbers
button['state'] = 'normal'
else:
button['state'] = 'disabled'
def raise_frame(frame):
frame.tkraise()
main_win = Tk()
main_win.geometry('500x500')
main_win.grid_columnconfigure(0, weight=1)
main_win.grid_rowconfigure(1, weight=1)
second_frame = Frame(main_win)
second_frame.grid(row=0, column=0, sticky='nsew')
main_frame = Frame(main_win)
main_frame.grid(row=0, column=0, sticky='nsew')
entry_check = main_frame.register(only_numeric_input)
not_empty = {} # keep track of entries' content
# create the entries
for i in range(4):
entry = Entry(main_frame, validate="key", validatecommand=(entry_check, "%P", "%W"))
entry.pack()
not_empty[str(entry)] = False
button = Button(main_frame, text='Admin', width=20, bg='brown', fg='white',
state='disabled',
command=lambda: raise_frame(second_frame))
button.pack()
main_win.mainloop()

Get checkbox to create/delete window when checked/unchecked

I want a checkbox that when check, creates a scrolled text widget and when unchecked, removes the widget.
Currently it creates the widget only once I've checked the box and then unchecked it, and then when checked again, it does nothing and when unchecked again, it creates a second widget below.
I've tried different ways of coding it but I can't figure out what I'm doing wrong.
# Creates Normal Checkbutton
chk_state = BooleanVar()
chk_state.set(False) # set check state
chk = Checkbutton(window, text='Normal Entries', var=chk_state)
chk.place(x=0, y=0)
#Checks Checkbutton State
def chk_checked(event):
txt = scrolledtext.ScrolledText(window, height=15, width=35)
if chk_state.get():
txt.insert(END, 'Paste Normal Entries Here...')
txt.pack(anchor='nw', padx=50, pady=50)
elif txt.winfo_exists():
txt.pack_forget()
else:
pass
#Event when checkbox checked
chk.bind('<Button-1>', chk_checked)
You can try as this
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
def chk_checked():
global txt
if chk_state.get():
txt = ScrolledText(window, height=15, width=35)
txt.insert(tk.END, 'Paste Normal Entries Here...')
txt.pack(anchor='nw', padx=50, pady=50)
else:
txt.pack_forget()
window = tk.Tk()
chk_state = tk.BooleanVar()
chk_state.set(False) # set check state
chk = tk.Checkbutton(window, text='Normal Entries', var=chk_state, command=chk_checked)
chk.place(x=0, y=0)
txt = None
window.mainloop()
This isn't the best way for do that, maybe you can create a class, i think that would be better.
The problem with your code is that each time that you click the CheckButton the function chk_checked(event) creates a new ScrolledText and after works on it instead working on the ScrolledText that was created previously. You have to declare a global variable (instead of a local variable) in wich you store the ScrolledText that you want to use and work only with it

Create Data Entry Window with Tkinter

I'm a complete novice when it comes to using Tkinter. I would like to create a function that pops up a window from the main one and asks a user to input some data. They will then click ok and the data will be returned.
The problem I'm having is that I want the my function to pause until this OK button is pressed so it will actually return values instead of empty strings. So far I have the code below.
def enterData(self, *arg):
top = self.top = Toplevel(self)
top.title("Data Entry")
label = []
self.entry = []
for i in range(0, len(arg)):
label.append(Label(top, text=arg[i]))
label[-1].grid(row=i, column=0)
self.entry.append(Entry(top))
self.entry[-1].grid(row=i, column=1)
ok = Button(top, text ="Ok", command = ??Pause??)
ok.grid(row = len(arg), column =0)

Categories

Resources