Dynamic Form Widget interaction in python tkinter - python-3.x

I am trying to generate a blank GUI, with 1 menu Item.
I then use a function to generate a label, a button and an entry widget on the same form when the selection is made from the menu item.
However when I try to use the get() method to get the value of the input in the generated textbox, I get an error. I may have missed some core concept here and this may not be possible, but I would like to know. Following is my code,
from tkinter import Tk, Label, Button, Entry, Menu
def btn_clientadd():
print(txt1.get())
def addclient():
lbl1 = Label(window, text="Client Name :")
lbl1.grid(row=1,column=1,padx=7,pady=7,sticky='e')
txt1 = Entry(window)
txt1.grid(row=1, column=2)
txt1.focus()
btn = Button(window, text="Add Client", command=btn_clientadd)
btn.grid(row=2,column=2,padx=7,pady=7)
window = Tk()
window.geometry('400x200')
menu = Menu(window)
new_item1 = Menu(menu)
menu.add_cascade(label='ClientMaster', menu=new_item1)
new_item1.add_command(label='Add New Client', command=addclient)
window.config(menu=menu)
window.mainloop()

The entry txt1 is created inside a function and the reference to it is garbage collected when the function ends. One way you can get around this it to declare a StringVar() in the global scope and then associate it to the entry.
Examine the example below:
from tkinter import Tk, Label, Button, Entry, Menu, StringVar
def btn_clientadd():
print(client_string.get()) # Get contents of StringVar
def addclient():
lbl1 = Label(window, text="Client Name :")
lbl1.grid(row=1,column=1,padx=7,pady=7,sticky='e')
# Create entry and associate it with a textvariable
txt1 = Entry(window, textvariable=client_string)
txt1.grid(row=1, column=2)
txt1.focus()
btn = Button(window, text="Add Client", command=btn_clientadd)
btn.grid(row=2,column=2,padx=7,pady=7)
window = Tk()
window.geometry('400x200')
menu = Menu(window)
new_item1 = Menu(menu)
menu.add_cascade(label='ClientMaster', menu=new_item1)
new_item1.add_command(label='Add New Client', command=addclient)
window.config(menu=menu)
client_string = StringVar() # StringVar to associate with entry
window.mainloop()

Related

How to get input from tkinter Entry widget on second window while the first window continues to run

from tkinter import *
def first():
root1 = Tk()
Button(root1, text = 'get Second', command= second).pack()
root1.mainloop()
def second():
root2 = Tk()
user_input = StringVar()
Entry(root2, text = user_input).pack()
Button(root2, text = 'submit', command = lambda : print(user_input.get(), '\t printed')).pack()
root2.mainloop()
first()
You are making a few basic mistakes in here -
You if want to use a second window, it should be Toplevel not root Tk window. There should be only one root window in the program. This should act as parent to all the windows.
Its a good practice in most of the cases to define the widgets like Button, Entry separately and then pack() them.
Entry should have 'textvariable' not 'text'
Following is the updated code which may help you -
from tkinter import *
root = Tk()
def first():
button = Button(root, text = 'get Second', command= second)
button.pack()
root.mainloop()
def second():
window2 = Toplevel(root)
user_input = StringVar()
entry = Entry(window2, textvariable=user_input)
entry.pack()
button = Button(window2, text = 'submit', command = lambda: print(user_input.get()))
button.pack()
first()

Cannot print array elements in listbox using tkinter

I'm trying to print array elements in console and in listbox (using tKinter), when a button is clicked. The elements are being printed on console, but not in the GUI. Below is the code.
from tkinter import *
from tkinter.ttk import *
from dbprocessor import DbProcessor
window = Tk()
window.title("Welcome To Pin Finder")
lbl = Label(window, text="Search for PCBa_Cards", font=("Arial Bold", 8))
lbl.grid(column=0, row=1)
search = Entry(window, width=20)
search.focus()
search.grid(column=0, row=4)
listbox = Listbox(window)
dp = DbProcessor()
def clicked():
res = "WELCOME " + search.get()
lbl.configure(text=res)
records = dp.connectandread(search.get())
for row in records:
print(str(row))
listbox.insert(END, str(row))
# lbl.configure(text=str(records))
#listbox.pack(fill=BOTH, expand=YES)
lbl.grid(column=1, row=5)
btn = Button(window, text="Search", command=clicked)
btn.grid(column=1, row=4)
window.mainloop()
I can see the array elements in the console, but not in the listbox. How can I fix this?
You never add the listbox to the display. You need to call the grid method of listbox.

Remove previous WIDGETS on selecting options from Menubar in TKINTER

I have a menu bar which has 2 options
[1]TO ADD DATA
[2]TO UPDATE THE DATA
When I select an option the widgets load in the window. When I select the other option after that , the previous widgets stay & new widgets overlapped. How do I keep the window & destroy the existing widgets over it.
I have not attached the original code with other menu options due to its size, however the code snippet replicates the actual scenario.
I have tried destroy() exit() & calling the function again
from tkinter import *
root = None
data = None
def _add(dt):
global data
data = dt
print(data, 'ADDED')
def _edit(new_dt):
global data
data = new_dt
print('UPDATED')
print("DATA =", data)
def cust_add():
global root
global data
lbl = Label(root,text="ADD NAME:")
ent = Entry(root)
btn = Button(root, text='ADD', command=lambda: _add(ent.get()))
lbl.pack()
ent.pack()
btn.pack()
def cust_edit():
global root
global data
lbl = Label(root, text="UPDATE NAME:")
ent = Entry(root)
btn = Button(root, text="UPDATE", command=lambda: _edit(ent.get()))
lbl.pack()
ent.pack()
btn.pack()
def homepage():
global root
root = Tk()
root.geometry('300x400')
root.title('CAR FLEET MANAGEMENT SYSTEM')
back_image = PhotoImage(file='icons/background3.png')
back_label = Label(root, image=back_image)
back_label.place(x=0, y=0, relwidth=1, relheight=1)
menus = Menu()
cst = Menu(menus, tearoff=FALSE)
cst.add_command(label="Add New Customer", command=cust_add)
cst.add_command(label="Change Customer Details", command=cust_edit)
menus.add_cascade(label='CUSTOMERS', menu=cst)
root.config(menu=menus)
root.mainloop()
homepage()
If I understand what you want correctly. It's probably better to create two frames for Add Customer and Update Customer. Then show and hide them as required. Like this the widgets are created once and reused as required. I've tried to change your code as little as possible.
I've used grid and grid_remove to hide and show. I couldn't find a pack_remove.
Edit: Below changed to use pack_forget() which in this simple case seems to work OK.
import tkinter as tk
root = None
data = None
def _add(dt):
global data
data = dt
print(data, 'ADDED')
def _edit(new_dt):
global data
data = new_dt
print('UPDATED')
print("DATA =", data)
def homepage():
global root
def cust_edit():
# c_add.grid_remove() Old grid version replaced by pack version
# c_upd.grid()
c_add.pack_forget() # Hide Add Customer
c_upd.pack() # Show Update Customer
def cust_add():
# c_upd.grid_remove() # Grid version removed.
# c_add.grid()
c_upd.pack_forget() # Hide Update Customer
c_add.pack() # Show Add Customer
root = tk.Tk()
root.geometry('300x400')
root.title('CAR FLEET MANAGEMENT SYSTEM')
c_add = tk.Frame(root)
tk.Label(c_add,text="ADD NAME:").pack()
a_ent = tk.Entry(c_add)
a_btn = tk.Button(c_add, text='ADD', command=lambda: _add(a_ent.get()))
a_ent.pack()
a_btn.pack()
c_upd = tk.Frame(root)
tk.Label(c_upd,text="UPDATE NAME:").pack()
u_ent = tk.Entry(c_upd)
u_btn = tk.Button(c_upd, text='UPDATE', command=lambda: _edit(u_ent.get()))
u_ent.pack()
u_btn.pack()
menus = tk.Menu()
cst = tk.Menu(menus, tearoff=tk.FALSE)
cst.add_command(label="Add New Customer", command=cust_add)
cst.add_command(label="Change Customer Details", command=cust_edit)
menus.add_cascade(label='CUSTOMERS', menu=cst)
root.config(menu=menus)
root.mainloop()
homepage()

How to store button name, from a loop, when clicked on?

Trying to store the name of the buttons that are clicked on, in a list (orderList). These buttons were also generated from a list (menuList) by a FOR loop. Whenever any button is clicked, it stores the last value only.
menuList = ['egg', 'bacon', 'bread']
orderList = []
def makeOrder(arg):
orderList.append(arg)
print (orderList)
for btn in mealList:
ttk.Button(mainframe, text=btn, command=lambda: makeOrder(btn)).grid(column=1, row=5)
I just want to get each button to store it's name inside the blank list (orderList)
This is due to late binding of python which you can read more about it here. In short, you can work it out by modifying your lambda function:
from tkinter import ttk
import tkinter as tk
menuList = ['egg', 'bacon', 'bread']
mealList = ["breakfast","lunch","dinner"]
orderList = []
root = tk.Tk()
mainframe = tk.Frame(root)
mainframe.pack()
for num,btn in enumerate(mealList):
ttk.Button(mainframe, text=btn,command=lambda x=btn: orderList.append(x)).grid(column=num, row=5)
tk.Button(mainframe,text="result",command=lambda: print (orderList)).grid(row=6,column=0)
root.mainloop()

Python Tkinter Check if Frame exists

I am trying to do the following:
Create a Tkinter App with a 'File' menu.
The File Menu has 2 options, Add and View.
The Add option adds a Frame and then adds a Label widget (Label 1) in the frame.
If I then select the View option form the file menu, it should print out whether or not a Frame widget already exists.
Following is my attempt at it, but I receive the error
AttributeError: 'Test' object has no attribute 'tk'
when I select the View option, can someone please help point out what I am missing here?
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
f=Frame()
f.pack()
lbl1 = Label(f, text="Label 1").grid(row=0, column=0)
def viewlbl(self):
print(Frame.winfo_exists(self))
T=Test()
I replicated your problem. I got the code below to work using Python3.4 on Linux. f needs to become self.f. I named it self.frame. This enables the frame to be accessed outside of the method it is created in.
from tkinter import Tk, Menu, Label, Frame
class Test():
def __init__(self):
self.gui = Tk()
self.gui.geometry("600x400")
menu = Menu(self.gui)
new_item1 = Menu(menu)
menu.add_cascade(label='File', menu=new_item1)
new_item1.add_command(label='Add', command=self.addlbl)
new_item1.add_command(label='View', command=self.viewlbl)
self.gui.config(menu=menu)
self.gui.mainloop()
def addlbl(self):
self.frame = Frame(self.gui)
self.frame.pack()
lbl1 = Label(self.frame, text="Label 1")
lbl1.grid(row=0, column=0)
def viewlbl(self):
print('frame exists {}'.format(self.frame.winfo_exists()))
T=Test()

Resources