In an UI I'm trying to make I want to make an interactive matrix where some data should be entered by the user. Apart from the data value Entries, each column and row has an Entry with a placeholder text where the user should enter the name of the column (e.g. price) and row (e.g. a company). In order to generate the matrix I have this class method
import customtkinter as ctk
from tkinter import messagebox
class AK_Matrix_Frame(ctk.CTkFrame):
def __init__(self, root):
#initialize the frame class-------------------
super().__init__(root)
#Set frame properties-------------------------
self.width = 200
self.height = 400
self.grid(row=1, column=0, padx=10, pady=5)
#Parameters-----------------------------------
self.m = 2
self.n = 3
#initialize and set up reference dict----------
self.AK_Widget_Matrix_dict ={}
self.gen_matrix()
#Base Button-----------------------------------
read_button = ctk.CTkButton(self, text = "Read", command = self.read_matrix)
read_button.pack(pady=12, padx = 10)
root.mainloop()
def gen_matrix(self):
matrix_label = ctk.CTkLabel(self, text = "Anbieter-Kategorien Matrix", font= ('Ariel', 18))
matrix_label.pack(pady=12, padx = 10)
self.matrix_frame = ctk.CTkFrame(self, width=200, height=200)
self.matrix_frame.pack( padx=10, pady=5, expand=True)
self.AK_Widget_Matrix_dict[(0,0)] = ctk.CTkLabel(self.matrix_frame, text = "A\K", font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(0,0)].grid(row = 0, column = 0, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
for i in range(self.m):
self.AK_Widget_Matrix_dict[(i+1,0)] = ctk.CTkEntry(self.matrix_frame, placeholder_text = "Anbieter{a}".format(a = i+1), font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(i+1,0)].grid(row = i+1, column = 0, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
self.AK_Widget_Matrix_dict[(i+1,0)].bind("<Return>", self.replace_matrix_entry_w_label)
for j in range(self.n):
if i == 0:
self.AK_Widget_Matrix_dict[(0,j+1)] = ctk.CTkEntry(self.matrix_frame, placeholder_text = "Kategorie{k}".format(k = j+1), font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(0,j+1)].grid(row = 0, column = j+1, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
self.AK_Widget_Matrix_dict[(0,j+1)].bind("<Return>", self.replace_matrix_entry_w_label)
self.AK_Widget_Matrix_dict[(i+1,j+1)] = ctk.CTkEntry(self.matrix_frame, font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(i+1,j+1)].grid(row = i+1, column = j+1, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
def read_matrix(self):
pass
def replace_matrix_entry_w_label(self, event):
print(event.widget.grid_info())
i = event.widget.grid_info()["row"]
j = event.widget.grid_info()["column"]
print(event.widget)
print("Row, Column:",i,j)
txt = event.widget.get()
print("Event widget contains:",txt)
event.widget.destroy()
self.AK_Widget_Matrix_dict[(i , j)] = ctk.CTkLabel(self.matrix_frame, text = txt, font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(i , j)].grid(row = i, column = j, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
AK_Matrix_Frame(ctk.CTk())
The matrix displays without problem and all entries and labels are placed in the correct location. But when the class method self.replace_matrix_entry_w_label is called, the grid information is transmitted falsely.
And this is the output for any fringe column or row entry I enter text and press return:
{'in': <customtkinter.windows.widgets.ctk_entry.CTkEntry object .!ak_matrix_frame.!ctkframe.!ctkentry2>, 'column': 0, 'row': 0, 'columnspan': 1, 'rowspan': 1, 'ipadx': 0, 'ipady': 0, 'padx': 6, 'pady': (2, 3), 'sticky': 'nesw'}
Row, Column: 0 0
Event widget contains: 23def
So the text one writes in is read correctly, but the row and column are wrong (always 0,0 no matter where the widget is located). I had the code almost identically with tkinter instead off customtkinter, and then it worked.
Why is the row and column in grid_info() not correct?
I tried accessing the bound widgets event.widget.grid_info()
in order to get row and column position and use that to replace the Entry with a Label. What actually happens is that the row and column values are always 0,0, no matter which entry in the matrix I select. Since the text written in the Entry is actually correct I don't understand where the problem is.
The problem arises because customtkinter's CTkEntry
widget internally creates a tkinter Entry
as an attribute of itself.
event.widget
does not refer to the CTkEntry
that has a row and column number in your grid, but instead refers to the Entry
that this CTkEntry
contains.
Your code in the function replace_matrix_entry_w_label
asks for the position of Entry
in its parent. But the parent is a CTkEntry
widget and the only location is (0,0)
. The code works if you ask for the grid_info()
of the master of Entry
, that is of CTkEntry
. Like this:
def replace_matrix_entry_w_label(self, event):
i = event.widget.master.grid_info()["row"]
j = event.widget.master.grid_info()["column"]
print("Row, Column:",i,j)
txt = event.widget.get()
event.widget.master.destroy()
self.AK_Widget_Matrix_dict[(i , j)] = ctk.CTkLabel(self.matrix_frame, text = txt, font= ('Ariel', 14))
self.AK_Widget_Matrix_dict[(i , j)].grid(row = i, column = j, padx = 5, pady = 5, sticky='w'+'e'+'n'+'s')
For the same reason as described above, if you don't destroy event.widget.master
you will still have an empty shell of CTkEntry
where just the internal Entry
is destroyed, but the object still lives on your UI.