Dynamic dictionary entry tkinter - python

I'm trying to make a basic GUI in tkinter but I want it to be dynamic.
The end goal is to have a nested dictionary populated by this GUI and that the user can add as many items as he/she want.
I managed to enter a full single dictionary with some simple (and pretty repetetive) code:
for example:
Edit
from Tkinter import *
top = Tk()
L1 = Label(top, text="User Name")
L1.pack( side = LEFT)
E1 = Entry(top, bd =5)
E1.pack(side = RIGHT)
top.mainloop()
Now im looking for a way for the GUI to have a button (like a + sign) that will allow the user to open another single or a set of entries each time he/she presses the button and populate the dictionary accordingly.
Any Ideas?

You basically want to have a Label & Entry object. You can collect these objects in a collection type such as list or dict.
The example below does that using a list:
try:
import tkinter as tk
except ImportError:
import Tkinter as tk
class LabelEntry(tk.Frame):
def __init__(self, master, text="User Name", *args, **kwargs):
tk.Frame.__init__(self, master, *args, **kwargs)
self._label = tk.Label(self, text=text)
self._entry = tk.Entry(self, bd=5)
self._label.pack(side='left', fill='both', expand=True)
self._entry.pack(side='left', fill='both', expand=True)
def add_a_le(master, widgets_list):
widgets_list.append(LabelEntry(master))
widgets_list[-1].pack()
def main():
root = tk.Tk()
my_label_entries = list()
add_btn = tk.Button(root, text="Add")
add_btn['command'] = lambda m=root, ws=my_label_entries: add_a_le(m, ws)
add_btn.pack(side='bottom')
tk.mainloop()
if __name__ == '__main__':
main()

Related

Is there a way to get values of dynamically added text fields in tkinter?

For homework, I have to create an application that creates a text field everytime a user clicks a button, and then get values from the fields when "submit" button is pressed.
The trace method shows up repeatedly, but I do not know how to use it. I know it requires a callback function, but what should that callback function be?
from tkinter import *
from tkinter import ttk
import sqlite3
import getpass
import wipComingIn
class Application(object):
def __init__(self,master):
self.master=master
self.ScanWIPIn = Button(master, text="Scan WIP In", width=25,
font='Calibri 12
bold',background='snow',command=self.scanWIPIn).grid(row=0, column=0,
padx=10)
def scanWIPIn(self):
incomingInventory=wipComingIn.scanIn()
def main():
root = Tk()
app=Application(root)
root.title("Main Menu")
root.configure(background="light cyan")
root.resizable(0, 0)
root.geometry('230x230+300+80')
root.mainloop()
if __name__=='__main__':
main()
class scanIn(Toplevel):
def __init__(self):
Toplevel.__init__(self)
self.geometry('300x100+350+100')
self.title('Scan In')
self.resizable(0,0)
self.num_rows=1
self.LocationLb = Label(self,text='Scan Location:',font='Arial
12').grid(row=1,column=1)
self.LocationBCText = Entry(self).grid(row=1,column=2)
self.AddLotBtn= Button(self,text="Scan
Lot",command=self.addField).grid(row=2,column=1)
self.CompleteTransaction =
Button(self,text="Complete",command=self.AddEntry).grid(row=2,column=4)
global listOfLots
listOfLots=[]
listOfLocation=[]
global rowNum
rowNum=2
def addField(self):
height =Toplevel.winfo_height(self)
height=height+25
global rowNum
rowNum=rowNum+1
listOfLots.append(StringVar())
newLot = Entry(self, textvariable=listOfLots[rowNum - 2])
newLot.grid(row=rowNum,column=2, pady=1)
listOfLots.append(StringVar())
geometryText='300'+str(height)+'350+100'
print(geometryText)
self.geometry('300x'+str(height)+'+350+100')
newLot.focus_set()
You could try just making a class that does it, for example:
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry('200x200')
class EntryListWidget(ttk.Frame):
"""Widget that creates a column of entry boxes."""
def __init__(self, master):
super().__init__(master)
self.entries = []
def add_entry(self):
"""Creates a new entry box and keeps reference to respective variable."""
entry_var = tk.StringVar()
self.entries.append(entry_var)
ttk.Entry(self, textvariable=entry_var).pack()
def get_entries(self):
"""Gets each entrybox text and returns as list."""
return [entry.get() for entry in self.entries]
entry_widget = EntryListWidget(root)
entry_widget.pack()
# Buttons to control adding new entry and getting their values
ttk.Button(root, text='Add Entry', command=entry_widget.add_entry).pack()
ttk.Button(root, text='Get Entries', command=entry_widget.get_entries).pack()
root.mainloop()
Just using the variable classes and not trace; I actually wouldn't use trace in this situation because I believe trace uses the callback every time the variable changes and here you have a one time "submit" button that collects all the values. You could extend this class idea to get what you're looking to do I bet.

Getting variable out of Tkinter

I would like to ask if anyone knows how to get out a variable from an Entry in Tkinter to be used in future calculation.
Let us assume that I want to create a prompt where the user needs to place two numbers in the two different Entry widgets.
These numbers are to be used in another script for calculation. How can I retrieve the values from the prompt created in Tkinter?
In my opinion, I would need to create a function with the code bellow and make it return the value from the Tkinter prompt. However, I cannot return the numbers because I'm destroying the root window. How can I get pass this, preferably without global variables.
Best Regards
from tkinter import *
from tkinter import ttk
#Start of window
root=Tk()
#title of the window
root.title('Title of the window')
def get_values():
values=[(),(value2.get())]
return values
# Creates a main frame on the window with the master being the root window
mainframe=ttk.Frame(root, width=500, height=300,borderwidth=5, relief="sunken")
mainframe.grid(sticky=(N, S, E, W))
###############################################################################
#
#
# Label of the first value
label1=ttk.Label(master=mainframe, text='First Value')
label1.grid(column=0,row=0)
# Label of the second value
label2=ttk.Label(master=mainframe, text='Second Value')
label2.grid(column=0,row=1)
###############################################################################
#
#
# Entry of the first value
strvar1 = StringVar()
value1 = ttk.Entry(mainframe, textvariable=strvar1)
value1.grid(column=1,row=0)
# Entry of the second value
strvar2 = StringVar()
value2 = ttk.Entry(mainframe, textvariable=strvar2)
value2.grid(column=1,row=1)
# Creates a simplle button widget on the mainframe
button1 = ttk.Button(mainframe, text='Collect', command=get_values)
button1.grid(column=2,row=1)
# Creates a simplle button widget on the mainframe
button2 = ttk.Button(mainframe, text='Exit', command=root.destroy)
button2.grid(column=2,row=2)
root.mainloop()
You use a class because the class instance and it's variables remain after tkinter exits.https://www.tutorialspoint.com/python/python_classes_objects.htm And you may want to reexamine some of your documentation requirements, i.e. when the statement is
"root.title('Title of the window')", adding the explanation "#title of the window" is just a waste of your time..
""" A simplified example
"""
import sys
if 3 == sys.version_info[0]: ## 3.X is default if dual system
import tkinter as tk ## Python 3.x
else:
import Tkinter as tk ## Python 2.x
class GetEntry():
def __init__(self, master):
self.master=master
self.entry_contents=None
self.e = tk.Entry(master)
self.e.grid(row=0, column=0)
self.e.focus_set()
tk.Button(master, text="get", width=10, bg="yellow",
command=self.callback).grid(row=10, column=0)
def callback(self):
""" get the contents of the Entry and exit
"""
self.entry_contents=self.e.get()
self.master.quit()
master = tk.Tk()
GE=GetEntry(master)
master.mainloop()
print("\n***** after tkinter exits, entered =", GE.entry_contents)
So, I have taken Curly Joe's example and made a function with the his sketch
The final result, for anyone wanting to use this as a template for a input dialog box:
def input_dlg():
import tkinter as tk
from tkinter import ttk
class GetEntry():
def __init__(self, master):
self.master=master
self.master.title('Input Dialog Box')
self.entry_contents=None
## Set point entries
# First point
self.point1 = ttk.Entry(master)
self.point1.grid(row=0, column=1)
self.point1.focus_set()
# Second point
self.point2 = ttk.Entry(master)
self.point2.grid(row=1, column=1)
self.point2.focus_set()
# labels
ttk.Label(text='First Point').grid(row=0, column=0)
ttk.Label(text='Second Point').grid(row=1, column=0)
ttk.Button(master, text="Done", width=10,command=self.callback).grid(row=5, column=2)
def callback(self):
""" get the contents of the Entries and exit the prompt"""
self.entry_contents=[self.point1.get(),self.point2.get()]
self.master.destroy()
master = tk.Tk()
GetPoints=GetEntry(master)
master.mainloop()
Points=GetPoints.entry_contents
return list(Points)
In python, functions are objects, as in get_values is an object.
Objects can have attributes.
Using these two, and the knowledge that we can't really return from a button command, we can instead attach an attribute to an already global object and simply use that as the return value.
Example with button
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
def on_button_press(entry):
on_button_press.value = entry.get()
entry.quit()
def main():
root = tk.Tk()
entry = tk.Entry(root)
tk.Button(root, text="Get Value!", command=lambda e = entry : on_button_press(e)).pack()
entry.pack()
tk.mainloop()
return on_button_press.value
if __name__ == '__main__':
val = main()
print(val)
Minimalistic example
Similarly modules are also objects, if you want to avoid occupying global namespace extremely, you can attach a new attribute to the module you're using
See:
try: # In order to be able to import tkinter for
import tkinter as tk # either in python 2 or in python 3
except ImportError:
import Tkinter as tk
if __name__ == '__main__':
tk.my_value = lambda: [setattr(tk, 'my_value', entry.get()), root.destroy()]
root = tk.Tk()
entry = tk.Entry(root)
root.protocol('WM_DELETE_WINDOW', tk.my_value)
entry.pack()
tk.mainloop()
print(tk.my_value)

Get search terms from input box

I am trying to create a GUI for an auto-complete prototype and am new to tkinter. I want to get the entire input when Space is pressed but I am unable to do so. The idea is to get all the entries in the text box so that I can do some analysis inside a function call.
This is the code:
def kp(event):
app.create_widgets(1)
import random
def getFromScript(text):
#########THIS IS A PLACE HOLDER FOR ACTUAL IMPLEMENTATION
i= random.randint(1,100)
return ['hello'+str(i),'helou'+text]
from tkinter import *
class Application(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.create_widgets(0)
# Create main GUI window
def create_widgets(self,i):
self.search_var = StringVar()
self.search_var.trace("w", lambda name, index, mode: self.update_list(i))
self.lbox = Listbox(self, width=45, height=15)
if i==0:
self.entry = Entry(self, textvariable=self.search_var, width=13)
self.entry.grid(row=0, column=0, padx=10, pady=3)
self.lbox.grid(row=1, column=0, padx=10, pady=3)
# Function for updating the list/doing the search.
# It needs to be called here to populate the listbox.
self.update_list(i)
def update_list(self,i):
search_term = self.search_var.get()#### THIS LINE SHOULD READ THE TEXT
# Just a generic list to populate the listbox
if(i==0):
lbox_list = ['Excellent','Very Good','Shabby', 'Unpolite']
if(i==1):
lbox_list = getFromScript(search_term)####### PASS TEXT HERE
self.lbox.delete(0, END)
for item in lbox_list:
if search_term.lower() in item.lower():
self.lbox.insert(END, item)
root = Tk()
root.title('Filter Listbox Test')
root.bind_all('<space>', kp)
app = Application(master=root)
app.mainloop()
Any kind of help is highly appreciable. Thanks in advance
Problem is you are creating a new StringVar on each create_widgets call.
Create StringVar in your __init__.
class Application(Frame):
def __init__(self, master=None):
...
self.search_var = StringVar()
...

import user input from TKinter button to a different .py module

On ticket.py module i have a Tkinter frame; value = Entry(frame), and on the same module I have a button where command=exoutput;
def exoutput():
print value.get()
I would like to import the value to othermodule.py on the button command/ when I hit the button.
Currently when I import, the print is generated from the exoutput() function, rather than from the othermodule.py file.
Suggestions on how to print the value on othermodule.py?
# ticket.py
from Tkinter import*
window = Tk()
window.title("Entry")
frame = Frame(window)
value = Entry(frame)
def exoutput():
print value.get()
btnStage = Button(frame, text='ACTION', command=exoutput)
btnStage.pack(side=RIGHT, padx=2)
value.pack(side=LEFT)
frame.pack(padx=10, pady=10)
window.resizable(0, 0)
window.mainloop()
The other file, I've tried something like this;
# othermodule.py
import ticket
usersinput = ticket.value.get()
print usersinput
I think you either need multi-threading, or swap your file contents. Nothing runs after mainloop until the Tk instance is destroyed.
Alternatively, you could structure your ticket.py in OOP, and fetch GUI object from it by your othermodule to use it as you please. Below is an example:
ticket.py:
#import tkinter as tk
import Tkinter as tk
class Window(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Entry")
self.resizable(False, False)
# create widgets
self.frame = tk.Frame(self)
self.value = tk.Entry(self.frame)
self.btn_stage = tk.Button(self.frame, text="ACTION")
# widgets layout
self.frame.pack(padx=10, pady=10)
self.btn_stage.pack(side='right', padx=2)
self.value.pack(side='left')
if __name__ == "__main__":
root = Window()
root.mainloop()
and othermodule.py:
import ticket
def put():
global my_var_in_othermodule
my_var_in_othermodule = ticket_GUI.value.get()
ticket_GUI.destroy()
my_var_in_othermodule = ""
ticket_GUI = ticket.Window()
ticket_GUI.btn_stage['command'] = put
ticket_GUI.mainloop()
print(my_var_in_othermodule)
input()

Cant get listbox to show for tkinter

So, what I am trying to do is open a file when pressing a button and displaying the contents in a listbox. This is what I have so far, but I am not getting the listbox to display, let alone get the info to be in the listbox:
#!/usr/bin/perl -w
import time
from Tkinter import *
import tkFileDialog
def listbox(listbox):
def open_file():
file = tkFileDialog.askopenfilename()
openFile = open(file)
for line in openFile:
listbox.insert(END, line)
open_file()
class App:
def __init__(self, parent):
frame = Frame(parent.title("Buttons"))
frame.pack()
root.pack_propagate(0)
self.exit = Button(frame, text="QUIT", fg="red", command=frame.quit)
self.exit.pack(side=LEFT)
self.open = Button(frame, text="Open...", command=self.call_listbox)
self.open.pack(side=LEFT)
frame.listbox = Frame()
scrollme = Scrollbar(frame.listbox)
self.listbox = Listbox(frame.listbox, yscrollcommand = scrollme.set)
scrollme.config(command = self.listbox.yview)
scrollme.pack(side = RIGHT, fill = Y)
self.listbox.pack()
self.listbox.insert(END, "Code:")
def call_listbox(self):
listbox(self.listbox)
root = Tk()
app = App(root)
root.mainloop()
any suggestions? thanks
You are forgetting to pack the frame that contains the listbox.
FWIW, your overloading of the name "listbox" makes your code very confusing - you have def listbox(listbox), self.listbox and frame.listbox. And you also have call_listbox and the Listbox class to add to the confusion.

Categories