首页 > 解决方案 > 在 Dialog 的 body 方法中创建 StringVar 时不保持其初始值

问题描述

我看到使用 a 的行为tk.StringVarDiaLog我没想到的。我的问题是为什么 GUI 会以这种方式运行,或者我做了什么让它以这种方式运行。我可以通过使用类属性而不是变量来修复它。

在以下两种情况下,StringVar都以 的body方法开头Dialog

在第一个示例中,class AsExpected将 StringVar 分配给var初始化为“Test”的类属性。在打开对话框时,条目按预期显示“测试”。输入到条目中的任何内容都会回显到标签,链接到相同的 StringVar。

在第二个示例中,class Unexpected将 StringVar 分配给方法中的局部变量。打开对话框时,条目是空白的,链接到同一 StringVar 的标签也是空白。输入到条目中的任何内容仍会回显到标签,因此从小部件到 StringVar 的链接仍然保持不变,但初始化文本会丢失。

从实验来看,行为与变量的范围有关。使其全局化会给出预期的行为,并将其包含在任何形式的闭包中,除了body,会给出预期的行为。我不明白为什么 StringVar 仍然链接到条目和标签,但在过程中的某处被重置为空白。

我也尝试过使用顶级窗口。行为与对话框相同。如果将 StringVar 分配给局部变量,则初始文本将丢失。如果它是全局的或闭包的一部分,则保留初始文本。在创建后使用 set 设置 StringVar 的行为方式相同。

下面的示例旨在使它们保持简单。简单的解决方法是使用属性作为 StringVar 的参考,但我很好奇发生了什么导致这种行为在 Linux、Windows 10 和 Mac OS tk/tcl 版本 8.6 上测试。或者我在做什么来导致这种行为?

这表现得如我所料。

import tkinter as tk
from tkinter import ttk
from tkinter import simpledialog

class AsExpected( simpledialog.Dialog ):
    """ This exhibits the behaviour I'd expect.  The initialisation text in the StringVar 
        shows in the Entry and Label widgets when the dialog opens. 
    """

    def body( self, parent ):
        ttk.Label( parent, text = "The Entry opens with the initial 'Test' text and anything" ).grid()
        ttk.Label( parent, text = "typed into the Entry still echoes to the Label." ).grid()

        self.var = tk.StringVar( parent, value = 'Test' )  # var is an attribute of AsExpected
        ent = ttk.Entry( parent, textvariable = self.var )
        ent.grid( pady = 5 )
        ttk.Label( parent, textvariable = self.var ).grid( pady = 5 )
        return ent

    def apply( self ):
        """ Sets result to a dict after 'Ok' is clicked. """
        self.result = { 'ans': self.var.get() }

# Create an application to trigger the dialog.
root = tk.Tk()
root.title( 'Test' )

choice = 0
def choose():
    global choice
    choice = AsExpected( root, 'As Expected' )
    print( choice.result )

ttk. Label( root, text = "This version keeps the initial value in the StringVar." ).grid( padx = 10, pady = 10 )
ttk.Button( root, text = 'Click to open dialogue.', command = choose ).grid( pady = 5 )

root.mainloop()

这将在对话框打开时将条目和标签留空。

import tkinter as tk
from tkinter import ttk
from tkinter import simpledialog

class Unexpected( simpledialog.Dialog ):
    """ This exhibits the behaviour I didn't expect.  The initialisation text in the StringVar 
        doesn't show in the Entry and Label widgets when the dialog opens. 
    """

    def body( self, parent ):

        def ontrace( *args ):  
            # Used to test using trace and the button command if the Button or trace code is uncommented.
            print( var, var.get() )

        ttk.Label( parent, text = "The Entry with blank not with the initial 'Test' text." ).grid()
        ttk.Label( parent, text = "but anything typed into the Entry still echoes to the Label." ).grid()

        var = tk.StringVar( self, value = 'Test', name = 'body_var' ) # var is a variable in body.
        ent = ttk.Entry( parent, textvariable = var )
        ent.grid( pady = 5 )
        ttk.Label( parent, textvariable = var ).grid( pady = 5 )
        print( var, var.get() )

        # Uncommenting any of the 3 statements below shows the initial value as the dialogue opens.
        # ttk.Button( parent, text = 'Print StringVar', command = ontrace ).grid()
        # var.trace( 'w', ontrace )
        # self.fetch = lambda: var.get()
        return ent

    def fetch( self ): 
        return 'OK Clicked'

    def apply( self ):
        """ Sets result to a dict after 'Ok' is clicked. """
        self.result = { 'ans': self.fetch() }

root = tk.Tk()
root.title( 'Test' )

choice = 0
def choose():
    global choice
    choice = Unexpected( root, 'Unexpected behaviour' )
    print( choice.result )

ttk. Label( root, text = "This version doesn't keep the initial value in the StringVar." ).grid( padx = 10, pady = 10 )
ttk.Button( root, text = 'Click to open dialogue.', command = choose ).grid( pady = 5 )

root.mainloop()

标签: pythontkinter

解决方案


推荐阅读