I'm trying to generate multiple ComboBoxes with values from a "config.ini" file, the config.ini file data is:
priority1 = Normal:farty-blobble-fx.wav:2
priority8 = Reclamacao:buzzy-blop.wav:3
priority3 = Critico:farty-blobble-fx.wav:5
priority2 = Urgente:echo-blip-thing.wav:4
and the goal is turning the sound files names to the select values in the comboboxes.
My code to generate the comboboxes is:
content_data = []
for name, value in parser.items(section_name):
if name=="name":
self.note.add(self.tab2, text = value)
else:
data_prior = value.split(":")
self.PRIOR_LABEL = Label(self.tab2, text=data_prior[0])
self.PRIOR_LABEL.grid(row=data_prior[2],column=0,pady=(10, 2),padx=(40,0))
self.PRIOR_SOUNDS = None
self.PRIOR_SOUNDS = None
self.box_value = StringVar()
self.PRIOR_SOUNDS = Combobox(self.tab2, textvariable=self.box_value,state='readonly',width=35)
self.PRIOR_SOUNDS['values'] = getSoundsName()
self.PRIOR_SOUNDS.current(int(getSoundsName().index(data_prior[1])))
self.PRIOR_SOUNDS.grid(row=data_prior[2],column=1,pady=(10, 2),padx=(30,0))
self.PLAY = Button(self.tab2)
self.PLAY["width"] = 5
self.PLAY["text"] = "Play"
self.PLAY["command"] = lambda:playSound(self.PRIOR_SOUNDS.get())
self.PLAY.grid(row=data_prior[2], column=3,pady=(10,2),padx=(5,0))
And i was unable to show the current values of the "config.ini" file in the comboboxes.
Thank you in advance.
The problem is that you're creating more than one combobox, yet you keep overwriting the variables in each iteration of the loop. At the end of the loop, self.PRIOR_SOUNDS will always point to the last combobox that you created. The same is true for self.box_value, self.PLAY, etc.
The simplest solution is to use an array or dictionary to store all of your variables. A dictionary lets you reference each widget or variable by name; using a list lets you reference them by their ordinal position.
A solution using a dictionary would look something like this:
self.combo_var = {}
self.combo = {}
for name, value in parser.items(section_name):
...
self.combo_var[name] = StringVar()
self.combo[name] = Combobox(..., textvariable = self.combo_var[name])
...
Related
I am trying to create a framework to create a label, text box and button as an object, I can extend it easily.
Idea:
Original
after extend
and it could extend to have 3, 4, 5 or more if it have more file, just declare in dictionary list, it will extend automatically.
The code I have:
def getpath(entry_box, type):
# path or file
if type == 'path':
entry_box.set(filedial.askdirectory())
elif type == 'file':
entry_box.set(filedial.askopenfilename())
MainWin = tk.Tk() # Create main windows
MainWin.title('Get file and path') # App name
# Create root container to hold Frames
mainFrame = ttk.Frame(MainWin)
mainFrame.grid(column=1, row=1)
# define the object to create in dictionary, format:
# {object name:[title name, default path, button name, file or folder path]}
obj2create ={'file1':['ABC Location: ', r'C:/', 'Get ABC','file'],
'file2': ['DEF Location:', r'C:/1/', 'Get DEF', 'file']}
ttl_obj = 0
for key in obj2create:
ttl_obj +=1
vir = obj2create[key]
# Module for get file:
obj_name = key
title = vir[0]
default_path = vir[1]
btn_name = vir[2]
get_type = vir[3]
# Create main container
pa_frame = ttk.Frame(mainFrame)
pa_frame.grid(column=1, row=10*ttl_obj, sticky=tk.W)
pa_frame.config(width = 100)
# Row 1: Label
frame_name = obj_name + '_name'
print(frame_name)
frame_name = ttk.Label(pa_frame, text= title).grid(column=1, row=10*ttl_obj,sticky=tk.W)
# Row 2: path and button
# assign type
path_loc = obj_name + '_path'
path_loc = tk.StringVar()
path_loc.set(default_path)
# put in frame
fileLocPath = obj_name + '_loc_path'
fileLocPath = ttk.Entry(pa_frame, width=70, textvariable=path_loc)
fileLocPath.grid(column=1, row=30*ttl_obj) # Assign position
# Get file button
# define button display text and assign the command / function
bt_get_file_path = obj_name + '_btn'
bt_get_file_path = ttk.Button(pa_frame, text= btn_name,
command=lambda: getpath(path_loc, get_type))
# Position Button in second row, second column (zero-based)
bt_get_file_path.grid(column=2, row=30*ttl_obj)
# Auto popup when open
MainWin.mainloop() # Let the window keep running until close
Issue:
Default file path appear in second text box only.
All the button location point to second box.
I also not sure how could I get the value in different box, the "path_loc = obj_name + '_path'" not able to get the correct object.
How should I make it work? Or the way I use is wrong?
Tkinter widgets are no different than any other python objects with respect to creating them in a loop. Your problem seems to be that you don't know how to create a unique variable for an unknown number of widgets.
The important thing to know is that you don't need a unique variable for each widget. Instead, you can use a list or dictionary, either of which can be easily extended at runtime.
Saving widget references in a dictionary
Here is a solution using a dictionary:
entries = {}
for key in obj2create:
...
entries[key] = ttk.Entry(...)
...
...
print("the value for file1 is", entries["file1"].get()
Creating a custom compound widget
If you're wanting to create sets of widgets that are to be treated as a group, it may be better to create a class. In effect, you're creating your own custom widget. This type of class is often called a "compound widget" or "megawidget".
For example:
class FileWidget(ttk.Frame):
def __init__(self, parent, name):
ttk.Frame.__init__(self, parent)
self.label = ttk.Label(self, text="%s Location" % name)
self.fileLocPath = ttk.Entry(self)
self.bt_get_file_path = ttk.Button(self, text=name, command=self.get_path)
...
def get_path(self):
return self.fileLocPath.get()
You can then create each row in your GUI like this:
widgets = {}
for key in obj2create:
widgets[key] = FileWidget(mainFrame, key)
widgets[key].pack(side="top", fill="x")
Later, you can get back the values like this:
for key in obj2create:
widget = widgets[key]
print("%s: %s" % (key, widget.get_path())
I created 12 Entry boxes using a for loop with a default value of N/A. Any change in the text of entry is detected through .trace method.
I want to use reset button to make all the text on the Entry Boxes back to N/A
from tkinter import *
root = Tk()
t_diesel_price_EnF_variable = ["JanVar", "FebVar", "MarVar", "AprVar","MayVar","JuneVar","JulyVar","AugVar","SeptVar", "OctVar", "NovVar", "DecVar"]
t_diesel_price_EnF_values = ["N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A","N/A", "N/A"]
def EnFChanging(*events):
for EnF in range(0,len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_values[EnF]=t_diesel_price_EnF_variable[EnF].get()
try:
t_diesel_price_EnF_values[EnF] = float(t_diesel_price_EnF_values[EnF])
except ValueError:
pass
print(t_diesel_price_EnF_values)
for EnF in range(0,len(t_diesel_price_EnF_values)):
t_diesel_price_EnF_variable[EnF] = StringVar(root , value = "N/A")
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10).pack()
t_diesel_price_EnF_variable[EnF].trace("w",EnFChanging)
def ChangeText():
for EnF in range(0, len(t_diesel_price_EnF_values)):
t_diesel_price[EnF].delete(0,END)
t_diesel_price[EnF].insert(0,"N/A")
return
b1 = Button(root, text = "Reset" , command = ChangeText).pack()
root.mainloop()
When I press the button it gives an error t_diesel_price[EnF].delete(0,END)
TypeError: 'NoneType' object is not subscriptable
What should I do now, Please ignore the basic errors of programming as I am a Mechanical Engineer with not a programming back ground. And I have to make a lot of other boxes too for my energy calculator.
You trying treat t_diesel_price as an Entry (and as a list) when your variable is None.
First of all I suggest you to install some IDE (e.g. PyCharm) and place break points to see whats wrong with variable!
Your problem occures because you create and pack your widget in one line! So t_diesel_price is None because pack() always returns None (link).
Just split your declaration and packing to:
t_diesel_price = Entry(root , textvariable = t_diesel_price_EnF_variable[EnF], width = 10)
t_diesel_price.pack()
After that it's works for me, except this fact that t_diesel_price is last created entry and the value changes only in it. So I assume that you need another list to iterate over entries:
...
# another fresh list
t_diesel_price_EnF_entries = list()
...
# declare entry
t_diesel_price = Entry(root, textvariable=t_diesel_price_EnF_variable[EnF], width=10)
# pack entry
t_diesel_price.pack()
# append entry to list
t_diesel_price_EnF_entries.append(t_diesel_price)
...
def ChangeText():
# iterate over entries
for diesel_price in t_diesel_price_EnF_entries:
diesel_price.delete(0,END)
diesel_price.insert(0,"N/A")
...
Alternatively you can iterate over StringVar's if you don't wanna store your entries at all:
def ChangeText():
# iterate over stringvars
for EnF in range(len(t_diesel_price_EnF_variable)):
t_diesel_price_EnF_variable[EnF].set('N/A')
And you can make it more readable as in example with entry iterating:
def ChangeText():
# iterate over stringvars
for string_var in t_diesel_price_EnF_variable:
string_var.set('N/A')
Cheers!
I am trying to display a list of users saved in a text file on a line by line basis and have it updated as people launch and close my program respectively. I can't figure out how to update the labels as in create new ones and delete ones no longer present in the text file as I can't .set() or .config() them as far as I know since the actual text on the labels doesn't have to change.
Here is my code so far.
def list_users(self):
with open("usercheck.txt", "r") as ulst:
self.usr_list = []
for line in ulst:
self.usr_list.append(line)
def online(self):
for self.name in self.usr_list:
self.onlbl = tk.Label(self, text = self.name,bg = "#42f480")
self.onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.onlcnt +=1
Running the online function with after just creates duplicates of the same label and does not update the amount of labels. self.onlcnt is 0
The reason might be that you are using the self keyword in your loop variable, which causes the variable to stay constant: don’t.
def online(self):
for name in self.usr_list:
self.onlbl = tk.Label(self, text = name,bg = "#42f480")
self.onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.onlcnt +=1
Also you might want to store the Labels in a list, so you can access them later:
def online(self):
try:
self.labels
except AttributeError:
self.labels = []
self.onlcnt = 0
for name in self.usr_list:
onlbl = tk.Label(self, text = name,bg = "#42f480")
onlbl.grid(row = self.onlcnt,column = 5,padx = 0)
self.labels.append(onlbl)
self.onlcnt +=1
root.after(5000, self.online) #run it again
As the title implies I'm trying to select items from one list box, press a button, and add it to a second list box.
When I click the button to move, the value is printed in command prompt, but the listbox itself isn't updating.
I copied and pasted so I realize that everything should be tabbed over one spot.
class Actions:
def openfile(self): #select a directory to view files
directory = tkFileDialog.askdirectory(initialdir='.')
self.directoryContents(directory)
def filename(self):
Label (text='Please select a directory').pack(side=TOP,padx=10,pady=10)
files = []
fileListSorted = []
#display the contents of the directory
def directoryContents(self, directory): #displays two listBoxes containing items
scrollbar = Scrollbar() #left scrollbar - display contents in directory
scrollbar.pack(side = LEFT, fill = Y)
scrollbarSorted = Scrollbar() #right scrollbar - display sorted files
scrollbarSorted.pack(side = RIGHT, fill = Y, padx = 2, pady=100)
fileList = Listbox(yscrollcommand = scrollbar.set) #files displayed in the left listBox
for filename in os.listdir(directory):
fileList.insert(END, filename)
global files
self.files.append(filename) #insert the values into the files array so we know which element we want to enter in moveFile
fileList.pack(side =LEFT, fill = BOTH)
scrollbar.config(command = fileList.yview)
global fileListSorted #this is for the filelist in the right window. contains the values the user has selected
fileListSorted = Listbox(yscrollcommand = scrollbarSorted.set) #second listbox (button will send selected files to this window)
fileListSorted.pack(side=RIGHT, fill = BOTH)
scrollbarSorted.config(command = fileListSorted.yview)
selection = fileList.curselection() #select the file
b = Button(text="->", command=lambda:self.moveFile(fileList.curselection()))#send the file to moveFile to be added to fileListSorted
b.pack(pady=5, padx =20)
##moveFile addes files to the array fileLIst2, which is the fileList on the right
def moveFile(self,File):
insertValue = int(File[0]) #convert the item to integer
global files
insertName = self.files[insertValue] #get the name of the file to be inserted
global fileListSorted
self.fileListSorted.append(str(insertName)) #append the value to the fileList array
print self.fileListSorted #second listbox list
It's quite difficult to follow that code -- For example, where is self.fileListSorted defined? -- You have a global fileListSorted and an instance variable self.fileListSorted and they're different things. However, you seem to be getting them confused (for example, why is there a line
global fileListSorted
in moveFile when you never use fileListSorted in there?) Also note that to add items into a ListBox, you typically use the insert method, which you haven't used in moveFiles as far as you've shown anyway...
I am trying a piece of python based tkinter code with following objective:
(Please go through the objective, then I will take an example to explain what exactly i require and then in the end will post the script I have written)
Reads from a config file, which is implemented using configparser module.
Based on options read from this file it automatically generates widget.
These widgets are restricted to only labels and entry box as of now.
Every entry box is associated with a variable. It is hence needed to generate a variable
automatically whenever a entry box is declared.
Now when the user enters any value in the entry box, and presses calculate button a list is
generated with combination of values entered by user( in a specific format).
Example:
Let the configparser file has following content:
[widget]
label = ani_label,sham_label
entry = ani,sham
The list generated for this case will be like this:
out_list = ['-ani','< ani >','-sham','< sham >']
< ani > means value stored in ani variable
And below is the code that i have tried.
from Tkinter import *
from Tkinter import Tk
import Tkinter as tk
import ttk
import ConfigParser
import sys
############ Initialize ###############################
parser_read = ConfigParser.ConfigParser()
parser_read.read('option_read.config')
config_list = {}
config_list['label'] = parser_read.get('widget','label').split(',')
config_list['entry'] = parser_read.get('widget','entry').split(',')
######
def calculate():
#Will include the list generation part
pass
#######
root = Tk()
root.title("NRUNTEST GUI VERSION 1")
#
menuframe = ttk.Frame(root)
menuframe.grid(column=0,row=0)
menuframe.columnconfigure(0,weight=1)
menuframe.rowconfigure(0,weight=1)
#
mainframe_label = ttk.Frame(root)
mainframe_label.grid(column=1,row=0)
mainframe_label.columnconfigure(0,weight=1)
mainframe_label.rowconfigure(0,weight=1)
#
mainframe_entry = ttk.Frame(root)
mainframe_entry.grid(column=2,row=0)
mainframe_entry.columnconfigure(0,weight=1)
mainframe_entry.rowconfigure(0,weight=1)
#
general_label= Label(menuframe,text="Please Enter the Values Below").grid(column=1,row=0,sticky=(E))
compiler_label= ttk.Label(menuframe,text="Compiler")
compiler_label.grid(column=1,row=1,sticky=W)
#
calculate_button = ttk.Button(menuframe, text="Calculate", command=calculate).grid(column=1,row=2,sticky=(W,E))
#Automatic Widget declaration ###
for x in config_list['label']:
x = ttk.Label(mainframe_label,text=x).grid()
for x in config_list['entry']:
#print x
var = lambda: sys.stdout.write(x)
x = ttk.Entry(mainframe_entry,textvariable = x).grid()
root.mainloop()
The content of option_read.config is
[widget]
label : animesh_label,sharma
entry : animesh_entry,sharma
STATUS:
I can create the required widgets automatically. But I am not able to create the variables dynamically to store the entry box values.
Once the variable has been calculated, I can write the calculate function on my own.
Please advice how i can proceed.
If you have any better way to meet my requirements, please do suggest.
Also do ping me if you require any more inputs or my query is not clear.
The easiest way to do this, IMO, is to use a dict to store the references to the dynamically created variables. You could use the label as the key. For example:
vars = {}
for x in config_list['entry']:
vars[x] = StringVar()
entry = ttk.Entry(mainframe_entry, textvariable=vars[x])
entry.grid()
By the way... are you aware that if you do something like x=ttk.Entry(...).grid(...), x does not contain a reference to the widget? It contains the result of the call to grid, which is None.