1

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:

  1. Default file path appear in second text box only.

  2. 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?

seasonpp
  • 23
  • 1
  • 3
  • if I understand this, you want to name every widget differently in a loop and you are not able to do that, right? – lycuid Oct 01 '16 at 06:23
  • Yes, since I will need to know the location for file ABC and DEF. The file picker also not work, when I press "Get ABC" it update to the text box in DEF file path. – seasonpp Oct 01 '16 at 07:19
  • we use dictionary (or list) for this - ie. `path_loc[obj_name] = ...` – furas Oct 01 '16 at 08:12

1 Answers1

3

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())
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685