Oct-07-2021, 06:44 PM
(This post was last modified: Oct-07-2021, 09:48 PM by deanhystad.)
This is pretty old, but just in case you haven't found answers.
The reason you are getting .!frame2.!entry in your Entry widgets is because you do this:
You do need the get, but you can hide it. I would make a class that hides the details of interacting with the "dictionary".
The reason you are getting .!frame2.!entry in your Entry widgets is because you do this:
val = Entry(frame, textvariable=item, width=15) val.grid(row=ind, column=i + 1, sticky="ns", padx=5, pady=5) fields_dict[key][i].set(val)What is val? val is an entry. Remove the fields_dict[key][i].set(val) to fix the problem.
You do need the get, but you can hide it. I would make a class that hides the details of interacting with the "dictionary".
from tkinter import * class DoubleEntry(Entry): '''A special Entry for GeometryMatrix. Verifies that input text is a number. Can set a command to call when the value changes. Set and get numeric value using "value" property. ''' def __init__(self, *args, command=None, justify='right', **kwargs): super().__init__(*args, **kwargs) self.var = StringVar(self, '') self.config(textvariable=self.var, justify=justify) self.bind('<Return>', self._accept) self.bind('<FocusOut>', self._accept) self.value = 0.0 self.command = command def __str__(self): '''Return the text value''' return self.var.get() def _accept(self, event): '''Accept value change when return is pressed of losing focus''' try: value = float(self.var.get()) except ValueError: value = self._value changed = value != self._value self.value = value if changed and self.command: self.command(self) @property def value(self): '''Return the numeric value''' return self._value @value.setter def value(self, value): '''Set the numeric value''' try: self._value = float(value) except ValueError: pass self.var.set(str(self._value)) class GeometryMatrix(Frame): '''A matrix like thing of Entry controls''' def __init__(self, parent, rows, columns, ewidth=10, padding=1, command=None, **kwargs): super().__init__(**kwargs) self.rows = len(rows) self.columns = len(columns) self.command = command self.column_headers = [Label(self, text=text) for text in columns] for column in range(self.columns): self.column_headers[column].grid(row=0, column=column+1, padx=padding, pady=padding) self.entries = [] self.row_headers = [Label(self, text=text) for text in rows] for row in range(self.rows): self.row_headers[row].grid(row=row+1, column=0, padx=padding, pady=padding) for column in range(self.columns): entry = DoubleEntry(self, width=ewidth, command=self._value_changed) entry.grid(row=row+1, column=column+1, padx=padding, pady=padding) self.entries.append(entry) def __len__(self): '''Return how many cells are in my matrix''' return self.rows * self.columns def _index(self, row, column=0): '''Private function. Compute index for [row, column]''' return row * self.columns + column def _value_changed(self, cell): '''Private function called when a cell value is edited''' if (self.command is not None): self.command(self) def get(self, row, column): '''Return value at [row, column]''' return self.entries[self._index(row, column)].value def set(self, row, column, value): '''Set value at [row, column] = value''' self.variables[self._index(row, column)].value = value @property def values(self): '''Get list with all values''' return [entry.value for entry in self.entries] @values.setter def values(self, values): '''Set all values from list''' for entry, value in zip(self.entries, values): entry.value = value def __str__(self): '''Return a pretty string showing my values''' w1 = max((len(x['text']) for x in self.row_headers)) w2 = max(self.entries[0]['width'], max((len(x['text']) for x in self.column_headers))) retval = '[' + ' '*w1 + ', '.join((f'{x["text"]:^{w2}}' for x in self.column_headers)) for row in range(self.rows): vals = [f'{str(entry):>{w2}}' for entry in self.entries[self._index(row):self._index(row+1)]] retval += '\n ' + f'{self.row_headers[row]["text"]:>{w1}}' + ', '.join(vals) return retval + ']' if __name__ == '__main__': root = Tk() input_geometry = GeometryMatrix( root, rows = [f'LABEL{i+1}' for i in range(5)], columns = ['Min', 'Step', 'Max'], command=print) input_geometry.values = [x**2 for x in range(3*5)] input_geometry.pack() root.mainloop()With everything wrapped up in a class it is really easy to add things like converting the input to floats, binding a command to call with the values change, etc. It improved the value dictionary idea so much that I decided to add some new features to the Entry widget so it works better with numbers.