【问题标题】:Tkinter, custom widget and label updatesTkinter,自定义小部件和标签更新
【发布时间】:2016-05-29 10:21:17
【问题描述】:

说明

我想为 Tkinter 制作一个自定义 "Table Widget",到目前为止,这几乎是我想要的。

我的问题是我希望表格包含"Labels",我只发现您必须使用"StringVar()'s" 才能更新标签。

我真的不想为 1000 个标签使用 1000 个变量,所以我已经尝试解决这个问题一周了。我发现的唯一解决方案是使用"Dicts",但我认为这不是一个好方法吗?我可能错了,但感觉它必须是一种方法来做这样的事情:

myLabel = Label(root, text='myText')
myLabel.pack()
myLabel.setText('new text')

当前小部件代码如下所示(This is my first custom widget so if this is horribly wrong or wrong in any way, please tell me)

来源

"""
Description:
        A TABLE WIDGET Extension for Tkinter
"""
try:
    #Python 3.X
    import tkinter #this is quite annoyng, without it (tkinter) wont be available....
    from tkinter import * # even with this
    from tkinter import ttk
    from tkinter.ttk import *

except ImportError:
    #python 2.X
    import tkinter
    from Tkinter import *
    from Tkinter import ttk
    from Tkinter.ttk import *



class ETable(tkinter.Frame):
    def __init__(self, parent, *args, **kwargs):
        tkinter.Frame.__init__(self, parent)
        self.init_table(parent, *args, **kwargs)

    def init_table(self, parent, *args, **kwargs):
        row_settings = {
                           'anchor': CENTER,
                           'background': '#C5FFFF',
                           'bitmap': None,
                           'borderwidth': 5,
                           'compound': None,
                           'cursor': None,
                           'font': None,
                           'foreground': '#000000',
                           'image': None,
                           'relief': FLAT,
                           'rows': 1,
                           'state': NORMAL,
                           'text': None,
                           'textvariable': None,
                           'underline': -1,
                           'width': 20,
                        }

        for kwarg in kwargs:
            if kwarg in row_settings:
                row_settings[kwarg] = kwargs[kwarg]

        self.table_rows = self.init_row(parent, *args, **row_settings)

    def init_row(self, parent, *args, **kwargs):
        text_list = kwargs.pop('text')
        row_list = []

        cols = []
        rows = kwargs.pop('rows')


        for row in range(rows):
            for col in range(len(text_list)):
                tempLabel = Label(parent, text = text_list[col], *args, **kwargs)
                tempLabel.grid(row=row, column=col)
                cols.append(tempLabel)

            row_list.append(cols)
        return row_list

目标

我希望能够做这样的事情

table.getRow[0][0].setText('new text') #This would change col 1 in row 1 to "new text"

奖金问题

(please tell me if I should make a new question for this)

正如我所说,我想为 Tkinter 创建一个 "Table Widget",但我还想向表格添加行为,即当用户单击一行时,我希望该行扩展 "Downwards",就像 @ 987654334@ 但它只会在失去焦点时关闭,这里的目标是添加"Entry or Text boxes" 以便编辑列。

这里的问题是,Tkinter 可以做到这一点吗?

【问题讨论】:

  • 啊,在任何人问之前,是的,我之前已经搜索过这个,有一个类似但不完全的“stackoverflow”问题,这就是我创建自定义小部件的灵感。
  • 我已经添加了一个答案,希望你喜欢。有关您的第二个问题的一些信息,请参阅 Bryan 的回答 herelinked question。要一次操作整行小部件,请考虑将每一行放在自己的 Frame 中。

标签: python tkinter


【解决方案1】:

我只发现你必须使用“StringVar()'s”来更新标签。

这不是一个真实的陈述。您可以使用 config / configure 方法更新任何小部件的属性。从您的第一个示例开始,它看起来像这样:

myLabel = Label(root, text='myText')
myLabel.pack()
myLabel.configure(text='new text')

而且,在你的最后一个例子中:

table.getRow[0][0].configure(text='new text') 

至于附加问题,是的,这是可能的。您可以像绑定任何其他小部件一样将任何您想要的代码绑定到标签,除了键和鼠标按钮的绑定之外,您还可以绑定获得和失去重点。

【讨论】:

  • 啊很好 =D 非常感谢。 configure 方法就像一个魅力 =D。这比dicts等要好得多=D
【解决方案2】:

正如 Bryan 所说,您不需要 使用 StringVar 作为标签的 textvariable 选项来更改其文本。但是,它可能更方便,特别是如果您希望能够从标签中读取当前文本字符串。 FWIW,您可以通过不带参数调用其.config() 方法来读取 Tkinter 小部件的选项,该方法返回所有当前选项值的字典。但我必须承认,如果您只想检查单个值,这有点混乱。

如果您确实想使用 StringVars,则无需为它们保留单独的列表或字典:您可以将 StringVar 作为属性附加到 Label 对象。请注意不要破坏标签的任何现有属性。 :)

我稍微修改了您的代码。非常好,尽管您的 cols 列表中的 init_row 方法确实有一个小错误。您的代码只是不断向该列表添加条目,然后将其复制到row_list。因此,cols 列表最终包含每一行中的每个标签,row_list 最终包含对该 cols 列表的多个引用。

我需要提一提的一点:请避免进行“明星”导入。当你这样做时

from tkinter import *

它将 135 个 Tkinter 名称放入您的命名空间;在 Python 2 中你会得到 175 个名字。这会在命名空间中造成不必要的混乱,并可能导致名称冲突:如果您不小心将其中一个变量命名为可能导致神秘错误的导入名称之一。当您使用多个模块进行星号导入时,情况会更糟,因为它们可能会踩到彼此的名字。此外,星号导入使代码更难阅读,因为您必须记住哪些名称是本地定义的,哪些是导入的。

我在您的代码中更改了一些其他内容。

使用kwargs 更新row_settings 的方式很好,但使用生成器表达式和dict.update 方法会更紧凑、更高效。

我明确传递列数,而不是根据文本列表的长度计算它。我将列数和行数都保存为类的实例属性,因为它们可能会派上用场。

我在您的类中添加了一个__getitem__ 方法,以便更轻松地访问标签行。

我在每个标签创建时添加了一个set_text 方法,以便更轻松地更改其文本。语法与您在问题中的语法不同,但我认为您会喜欢它。 :)

我添加了一些代码来创建一个主 Tkinter 窗口来测试您的 ETable 类并调用 set_text 方法;我还在那个窗口中放了一个 Button。Button 回调首先打印出标签的 config 字典,然后更新其文本。

为了将来参考,最好发布MCVE,尤其是 Tkinter 问题,以便想要回答的人可以运行和测试您的代码,而无需先编写一堆东西。

此代码已在 Python 2.6.6 和 Python 3.6.0a0 上测试。

"""
Description:
        A TABLE WIDGET Extension for Tkinter

        Written by Estrobeda, with modifications by PM 2Ring
        2016.05.30
"""

from __future__ import print_function

try:
    # Python2
    import Tkinter as tk
except ImportError:
    # Python3
    import tkinter as tk

class ETable(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent)
        self.init_table(parent, *args, **kwargs)

    def init_table(self, parent, *args, **kwargs):
        row_settings = {
            'anchor': 'center',
            'background': '#C5FFFF',
            'bitmap': None,
            'borderwidth': 5,
            'cols': 1,
            'compound': None,
            'cursor': None,
            'font': None,
            'foreground': '#000000',
            'image': None,
            'relief': 'flat',
            'rows': 1,
            'state': 'normal',
            'texts': None,
            'textvariable': None,
            'underline': -1,
            'width': 20,
        }

        # Update row_settings with valid entries in kwargs
        row_settings.update((k, v) for k, v in kwargs.items() if k in row_settings)
        self.rows = row_settings.pop('rows')
        self.cols = row_settings.pop('cols')
        self.table_rows = self.init_rows(parent, *args, **row_settings)

    def init_rows(self, parent, *args, **kwargs):
        texts = iter(kwargs.pop('texts'))

        row_list = []
        for row in range(self.rows):
            col_list = []
            for col in range(self.cols):
                tempLabel = tk.Label(parent, text=next(texts), *args, **kwargs)
                #Bind a function to the label to change its text
                tempLabel.set_text = lambda s, w=tempLabel: w.config(text=s)
                tempLabel.grid(row=row, column=col)
                col_list.append(tempLabel)

            row_list.append(col_list)
        return row_list

    def __getitem__(self, row):
        return self.table_rows[row]

# Test

root = tk.Tk()
root.title("LabelTable Demo")
texts = list('abcdefghijkl')
table = ETable(root, rows=3, cols=4, texts=texts, relief='ridge', width=10)

# Change the text of a label
table[0][1].set_text('new text')

def change_text():
    widget = table[2][3]
    # Display the widget's current configuration
    cfg = widget.config()
    for k in sorted(cfg):
        print(k, '=', cfg[k])
    # Change the text
    widget.set_text('hello')

b = tk.Button(root, text='Change', command=change_text)
b.grid(row=4, column=0)

root.mainloop()

【讨论】:

  • 哇,谢谢,您对上一个答案的解释更加清晰,并且还对代码以及在提出此类问题时应该考虑的问题提供了非常感谢的反馈。代码更改看起来很棒,非常感谢这种深入的解释和代码改进。
猜你喜欢
  • 2017-11-12
  • 2010-12-27
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-06
相关资源
最近更新 更多