Tkinter, custom widget and label updates

后端 未结 2 1958
闹比i
闹比i 2020-12-12 05:42

Description

I want to make a custom \"Table Widget\" for Tkinter and so far it\'s almost as I want.

The problem I have is that

相关标签:
2条回答
  • 2020-12-12 06:23

    As Bryan said, you don't need to use a StringVar as a Label's textvariable option to change its text. It can be more convenient, though, especially if you want to be able to read the current text string from the Label. FWIW, you can read the options of a Tkinter widget by calling its .config() method with no argument, which returns a dictionary of all current option values. But I must admit that's a bit messy if you only want to inspect a single value.

    If you do want to use StringVars you don't need to keep a separate list or dict of them: you can attach a StringVar as an attribute to the Label object. Just be careful that you don't clobber any of the Label's existing attributes. :)

    I've modified your code a little bit. It was quite good, although you did have a little bug in the init_row method with your cols list. Your code just kept adding entries to that list and then copying it to the row_list. So the one cols list ends up containing every Label from every row, and the row_list ends up with multiple references to that one cols list.

    One thing I need to mention: please avoid doing "star" imports. When you do

    from tkinter import *
    

    it puts 135 Tkinter names into your namespace; in Python 2 you get 175 names. This creates needless clutter in the namespace and it can cause name collisions: if you accidentally name one of your variables with one of the imported names that can lead to mysterious bugs. It's even worse when you do star imports with multiple modules since they can stomp over each others' names. Also, star imports make the code harder to read since you have to remember which names are defined locally and which are imported.

    There are a couple of other things I changed in your code.

    The way you updated row_settings with kwargs is fine, but it's a little more compact and more efficient to do it with a generator expression and the dict.update method.

    I pass the number of columns explicitly, rather than calculating it from the length of the text list. And I save both the number of columns and rows as instance attributes of the class, since they may come in handy.

    I've added a __getitem__ method to your class to make it easier to access the rows of Labels.

    I add a set_text method to each Label as its created to make it simple to change its text. The syntax isn't identical to what you have in the question, but I think you'll like it. :)

    I've added some code to create a main Tkinter window to test your ETable class and call the set_text method; I've also put a Button in that window.The Button callback first prints out the config dictionary of a Label and then it updates its text.

    For future reference, it's a Good Idea to post a MCVE, especially with Tkinter questions, so that people who want to answer can run and test your code without having to write a bunch of stuff first.

    This code has been tested on Python 2.6.6 and 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()
    
    0 讨论(0)
  • 2020-12-12 06:27

    I have only found that you have to use "StringVar()'s" in order to update the labels.

    This is not a true statement. You can update the attributes of any widget with the config / configure method. Starting with your first example, it would look like this:

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

    And, in your final example:

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

    As for the bonus question, yes, it's possible.You can bind any code you want to a label just as you can any other widget, and in addition to bindings on keys and mouse buttons, you can bind on gaining and losing focus.

    0 讨论(0)
提交回复
热议问题