Customtkinter: 为什么这个 event.widget 丢失了正确的网格信息?

huangapple go评论61阅读模式
英文:

Customtkinter: why does this event.widget lose the proper grid information?

问题

在一个用户界面中,我尝试创建一个交互式矩阵,用户可以输入一些数据。除了数据值的输入,每列和每行都有一个带有占位文本的输入框,用户应该在其中输入列的名称(例如,价格)和行的名称(例如,公司)。为了生成矩阵,我有这个类方法。

import customtkinter as ctk
from tkinter import messagebox

class AK_Matrix_Frame(ctk.CTkFrame):

    def __init__(self, root):
        
        # 初始化框架类
        super().__init__(root)
        
        # 设置框架属性
        self.width = 200
        self.height = 400
        self.grid(row=1,  column=0,  padx=10,  pady=5)

        # 参数
        self.m = 2
        self.n = 3

        # 初始化和设置引用字典
        self.AK_Widget_Matrix_dict = {}
        self.gen_matrix()
        
        # 基本按钮
        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())

矩阵可以正常显示,所有输入框和标签都被正确放置。但是,当调用self.replace_matrix_entry_w_label类方法时,网格信息被错误传递。

对于我输入文本并按回车的任何边缘列或行输入,以下是输出:

{'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

因此,正确读取了写入的文本,但行和列是错误的(无论小部件位于矩阵的哪个位置,它们始终是0,0)。我几乎完全相同的代码在使用 tkinter 而不是 customtkinter 时有效。

为什么grid_info()中的行和列不正确?

我尝试访问绑定的小部件event.widget.grid_info()以获取行和列位置,并使用它来替换输入框为标签。实际发生的是,行和列的值始终为0,0,无论我选择矩阵中的哪个输入框。由于输入框中的文本实际上是正确的,我不明白问题出在哪里。

英文:

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 = &quot;Read&quot;, command = self.read_matrix)
read_button.pack(pady=12, padx = 10)
root.mainloop()
def gen_matrix(self):
matrix_label = ctk.CTkLabel(self, text = &quot;Anbieter-Kategorien Matrix&quot;, font= (&#39;Ariel&#39;, 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 = &quot;A\K&quot;, font= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(0,0)].grid(row = 0,  column = 0,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)
for i in range(self.m):
self.AK_Widget_Matrix_dict[(i+1,0)] = ctk.CTkEntry(self.matrix_frame, placeholder_text = &quot;Anbieter{a}&quot;.format(a = i+1), font= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(i+1,0)].grid(row = i+1,  column = 0,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)
self.AK_Widget_Matrix_dict[(i+1,0)].bind(&quot;&lt;Return&gt;&quot;, 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 = &quot;Kategorie{k}&quot;.format(k = j+1), font= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(0,j+1)].grid(row = 0,  column = j+1,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)
self.AK_Widget_Matrix_dict[(0,j+1)].bind(&quot;&lt;Return&gt;&quot;, self.replace_matrix_entry_w_label)
self.AK_Widget_Matrix_dict[(i+1,j+1)] = ctk.CTkEntry(self.matrix_frame, font= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(i+1,j+1)].grid(row = i+1,  column = j+1,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)
def read_matrix(self):
pass
def replace_matrix_entry_w_label(self, event):
print(event.widget.grid_info())
i = event.widget.grid_info()[&quot;row&quot;]
j = event.widget.grid_info()[&quot;column&quot;]
print(event.widget)
print(&quot;Row, Column:&quot;,i,j)
txt = event.widget.get()
print(&quot;Event widget contains:&quot;,txt)
event.widget.destroy()
self.AK_Widget_Matrix_dict[(i , j)] = ctk.CTkLabel(self.matrix_frame, text = txt, font= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(i , j)].grid(row = i,  column = j,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)
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:

{&#39;in&#39;: &lt;customtkinter.windows.widgets.ctk_entry.CTkEntry object .!ak_matrix_frame.!ctkframe.!ctkentry2&gt;, &#39;column&#39;: 0, &#39;row&#39;: 0, &#39;columnspan&#39;: 1, &#39;rowspan&#39;: 1, &#39;ipadx&#39;: 0, &#39;ipady&#39;: 0, &#39;padx&#39;: 6, &#39;pady&#39;: (2, 3), &#39;sticky&#39;: &#39;nesw&#39;}
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.

答案1

得分: 0

问题出在customtkinter的CTkEntry部件内部创建了一个名为Entry的tkinter部件作为自身的属性。

event.widget不是指代你网格中具有行和列编号的CTkEntry,而是指代了这个CTkEntry内部包含的Entry

replace_matrix_entry_w_label函数中的代码要求获取Entry在其父级中的位置。但父级是一个CTkEntry部件,唯一的位置是(0,0)。如果你请求Entrygrid_info(),那是CTkEntry的主部件,代码会运行正常,如下所示:

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')

出于上述相同的原因,如果你不销毁event.widget.master,你仍然会在你的UI上拥有一个CTkEntry的空外壳,只是内部的Entry被销毁了,但对象仍然存在。

英文:

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()[&quot;row&quot;]
j = event.widget.master.grid_info()[&quot;column&quot;]
print(&quot;Row, Column:&quot;,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= (&#39;Ariel&#39;, 14))
self.AK_Widget_Matrix_dict[(i , j)].grid(row = i,  column = j,  padx = 5,  pady = 5,  sticky=&#39;w&#39;+&#39;e&#39;+&#39;n&#39;+&#39;s&#39;)

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.

huangapple
  • 本文由 发表于 2023年2月6日 21:16:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75361805.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定