Python tkinter不更新布尔值,但更新布尔值表

Python tkinter不更新布尔值,但更新布尔值表,python,python-3.x,tkinter,Python,Python 3.x,Tkinter,我无法让tkinter更新一对简单的“应用程序全局”布尔变量以匹配关联复选框的布尔变量。布尔值初始化、更新并存储在一个文件中,并由另一个文件引用(只读)。无论复选框的状态如何,布尔值都不会改变其默认值。(旁注:False/True的初始值变为0/1,可能是由于tkinter推进布尔的方式有问题)。 奇怪的是,与tkinter复选框表对应的类似“应用程序全局”布尔表没有问题。该表是由第三个文件定义的类的实例。该表与简单布尔值位于同一文件中(并以相同的方式处理)。 所有布尔值在一个文件(File1.

我无法让tkinter更新一对简单的“应用程序全局”布尔变量以匹配关联复选框的布尔变量。布尔值初始化、更新并存储在一个文件中,并由另一个文件引用(只读)。无论复选框的状态如何,布尔值都不会改变其默认值。(旁注:False/True的初始值变为0/1,可能是由于tkinter推进布尔的方式有问题)。 奇怪的是,与tkinter复选框表对应的类似“应用程序全局”布尔表没有问题。该表是由第三个文件定义的类的实例。该表与简单布尔值位于同一文件中(并以相同的方式处理)。
所有布尔值在一个文件(File1.py)中使用(只读),并且仅由另一个文件(File2.py)修改。显式返回问题布尔值(从File2.py到File1.py)不起作用

所使用的环境是EclipseJuno上的Windows7、Python 3.2.2(4.2.2,构建M2013024-1200)和Pydev2.7.32013031601插件。应用程序正在从Eclipse控制台运行

根据LEGB范围规则,一切似乎都正常(include和global语句存在,虚线名称形式用于外部定义的“应用程序级别”全局变量)。在我看来,要么所有的布尔更新都能工作,要么一个都不能工作。那么,为什么布尔表可以正常工作,而简单布尔表却不能正常工作?需要什么才能使简单布尔表正常工作?
推测一下,这是否与python将具有基本值(例如0、1、2、3、True?False?)的变量作为“共享值”而不是单独的变量实现的方式有关?这会导致从其他模块引用这些简单值时出现问题

示例应用程序由3个文件组成;每个都已尽可能减少,同时仍能显示问题。在Windows 7上运行时,您应该看到:

File1.py-mainline,包含回调函数“获取用户选择”:

from tkinter  import *
from tkinter  import ttk
import File2 
#==========================================================================
def Get_User_Selections( ):

    print( "\nDoAllReports=", File2.DoAllReports, "DoNoReports=", File2.DoNoReports )

    if ( not File2.DoNoReports ) :  
        for row in range( len( File2.ChosenReports ) ):
            for column in range( len( File2.ChosenReports[ 0 ] ) ) :              
                if ( File2.ChosenReports[ row ][ column ].get() or
                     File2.DoAllReports ) :

                    print( "Do report  (", row, ',', column, ')' )               
    return
#==========================================================================         
def CreateTheReports( *args ):
    Get_User_Selections( )
    return
#==========================================================================
''' *********** MAIN PROCEDURE ***************** '''
root = Tk()
root.title( 'tkinter Boolean problem' )

mainframe = ttk.Frame( root )
mainframe.grid( )

File2.ChooseReports( mainframe )

DoItButton  = Button( mainframe, text = 'DO IT!', command = CreateTheReports )
DoItButton.grid()

root.mainloop()      
#==========================================================================
File2.py-定义GUI,更新所有“应用程序级全局”布尔值:

from tkinter  import *

# import the custom "GUI table" widgit class (which works as expected)

from TkinterCheckBoxTableClass import TkinterCheckBoxTableClass

# Determine the table "shape" (in the original application the strings become  
# the row and column headers, these headers NOT shown by this example code).

RowTitles    = [ "A", "B" ] 
ColumnTitles = [ "1", "2", "3", "4" ]

DefaultReports = [ ]
for i in RowTitles :
    for j in ColumnTitles :
        DefaultReports = DefaultReports + [ False ] 

# Initialize the three "APPLICATION LEVEL" global variables that preserve the
# user choices across invocations of the routine.  Each invocation is caused
# by the user pressing the "DO IT!" button on the GUI.

ChosenReports = DefaultReports     # table of user choices (works properly)

# These two "application" globals override the table (above) of individual 
# choices.  "DoNoReports" overrides "DoAllReports"; both override the table. 

DoAllReports  = False    # does not work, value never changes    
DoNoReports   = False    # does not work, value never changes    

#==========================================================================
def ChooseReports( ParentFrame ):    
    # Purpose : Interface between the "application globals" and what appears
    #           on the GUI.  Called from File1.py whenever user presses 
    #           the "DO IT" button

    global ChosenReports   # "application" global, resides in this file
    global DoAllReports    # "application" global, resides in this file
    global DoNoReports     # "application" global, resides in this file

    GuiTable = [ [ IntVar() for j in range( len( ColumnTitles ) ) ]
                                        for i in range( len( RowTitles ) ) ]

    ThisFrame = LabelFrame( ParentFrame, text = " Select Reports " )    
    ThisFrame.grid( row = 1, column = 0 )

    DoAll  = IntVar( value = DoAllReports )
    DoNone = IntVar( value = DoNoReports )

    SelectAll = Checkbutton( ThisFrame, variable = DoAll, text = "All",
                             onvalue = True, offvalue = False)
    SelectAll.grid( row = 0, column = 1 )

    SelectNone = Checkbutton( ThisFrame, variable = DoNone, text ='None',
                              onvalue = True, offvalue = False )
    SelectNone.grid( row = 0, column = 2 )

    TableFrame = LabelFrame( ThisFrame, background = 'grey', 
                             borderwidth = 1, relief = FLAT )
    TableFrame.grid( row = 1, column = 0, columnspan = 3, rowspan = 2 )

    # Create the custom Checkbox Table, works without any problems.

    ChooseTheReports =  TkinterCheckBoxTableClass( FrameID = TableFrame,
                                              UserSelections = GuiTable )
    # Update the "application level" globals
    DoAllReports  = DoAll.get( )
    DoNoReports   = DoNone.get( )
    ChosenReports = ChooseTheReports.getTable( )
    # Passing back the above variables in the return statement did NOT work.
    # Returning them shouldn't even be needed since a) they have "application" 
    # level scope, b) they reside in THIS file and c) are ONLY modified by THIS 
    # file (with the new values being accessible from other files).   
    return  

#==========================================================================
'''
Implements a 2-D matrix (table) of tkinter checkboxes (for Python 3.x).
'''
from tkinter import *
from tkinter import ttk

class TkinterCheckBoxTableClass( object ):   
    '''
    Python 3.x interface to a matrix (2-D table) of tkinter checkboxes.
    Must pass a tkinter frame and the matrix to the constructor.
    Class constructor parameters:    
        FrameID             - tkinter parent frame that displays the table
        UserSelections      - matrix of True/False values passed to/from to GUI         
    Entire Table Methods:    
        getTable()         - extracts 2-D array of UserSelections from tkinter
    '''
    '''----------------------------------------------------------------------'''
    '''
    Constructor
    '''   
    def __init__( self , FrameID, UserSelections ) :               
        self.frameID             = FrameID      
        self.userSelections      = UserSelections       
        self.rowCount            = max( 1, len( self.userSelections ) )
        self.columnCount         = max( 1, len( self.userSelections[ 0 ] ) )                              
        self.checkBoxTable  = [ [ IntVar() for j in range( self.columnCount ) ]
                                           for i in range( self.rowCount ) ]
        # Construct and display the table of tkinter checkboxes
        for i in range( self.rowCount ) :
            for j in range( self.columnCount ) :
                self.checkBoxTable[i][j] = Checkbutton( self.frameID,
                                    variable = self.userSelections[ i ][ j ],
                                    onvalue  = True, offvalue = False )
                self.checkBoxTable[i][j].grid( row =  i + 1, column =  j + 1, 
                                               sticky = W )
    '''----------------------------------------------------------------------'''
    '''Methods:'''    
    def getTable( self ) :
        for i in range( self.rowCount ) :
            for j in range( self.columnCount ) :
                self.checkBoxTable[i][j] = Checkbutton( self.frameID,
                                     variable = self.userSelections[ i ][ j ],
                                     onvalue  = True,offvalue = False )
                self.checkBoxTable[i][j].grid( row = i + 1, column = j + 1,
                                               sticky = W )
        return self.userSelections   
''' END CLASS '''
TkinterCheckBoxTableClass.py-实现布尔值表:

from tkinter  import *

# import the custom "GUI table" widgit class (which works as expected)

from TkinterCheckBoxTableClass import TkinterCheckBoxTableClass

# Determine the table "shape" (in the original application the strings become  
# the row and column headers, these headers NOT shown by this example code).

RowTitles    = [ "A", "B" ] 
ColumnTitles = [ "1", "2", "3", "4" ]

DefaultReports = [ ]
for i in RowTitles :
    for j in ColumnTitles :
        DefaultReports = DefaultReports + [ False ] 

# Initialize the three "APPLICATION LEVEL" global variables that preserve the
# user choices across invocations of the routine.  Each invocation is caused
# by the user pressing the "DO IT!" button on the GUI.

ChosenReports = DefaultReports     # table of user choices (works properly)

# These two "application" globals override the table (above) of individual 
# choices.  "DoNoReports" overrides "DoAllReports"; both override the table. 

DoAllReports  = False    # does not work, value never changes    
DoNoReports   = False    # does not work, value never changes    

#==========================================================================
def ChooseReports( ParentFrame ):    
    # Purpose : Interface between the "application globals" and what appears
    #           on the GUI.  Called from File1.py whenever user presses 
    #           the "DO IT" button

    global ChosenReports   # "application" global, resides in this file
    global DoAllReports    # "application" global, resides in this file
    global DoNoReports     # "application" global, resides in this file

    GuiTable = [ [ IntVar() for j in range( len( ColumnTitles ) ) ]
                                        for i in range( len( RowTitles ) ) ]

    ThisFrame = LabelFrame( ParentFrame, text = " Select Reports " )    
    ThisFrame.grid( row = 1, column = 0 )

    DoAll  = IntVar( value = DoAllReports )
    DoNone = IntVar( value = DoNoReports )

    SelectAll = Checkbutton( ThisFrame, variable = DoAll, text = "All",
                             onvalue = True, offvalue = False)
    SelectAll.grid( row = 0, column = 1 )

    SelectNone = Checkbutton( ThisFrame, variable = DoNone, text ='None',
                              onvalue = True, offvalue = False )
    SelectNone.grid( row = 0, column = 2 )

    TableFrame = LabelFrame( ThisFrame, background = 'grey', 
                             borderwidth = 1, relief = FLAT )
    TableFrame.grid( row = 1, column = 0, columnspan = 3, rowspan = 2 )

    # Create the custom Checkbox Table, works without any problems.

    ChooseTheReports =  TkinterCheckBoxTableClass( FrameID = TableFrame,
                                              UserSelections = GuiTable )
    # Update the "application level" globals
    DoAllReports  = DoAll.get( )
    DoNoReports   = DoNone.get( )
    ChosenReports = ChooseTheReports.getTable( )
    # Passing back the above variables in the return statement did NOT work.
    # Returning them shouldn't even be needed since a) they have "application" 
    # level scope, b) they reside in THIS file and c) are ONLY modified by THIS 
    # file (with the new values being accessible from other files).   
    return  

#==========================================================================
'''
Implements a 2-D matrix (table) of tkinter checkboxes (for Python 3.x).
'''
from tkinter import *
from tkinter import ttk

class TkinterCheckBoxTableClass( object ):   
    '''
    Python 3.x interface to a matrix (2-D table) of tkinter checkboxes.
    Must pass a tkinter frame and the matrix to the constructor.
    Class constructor parameters:    
        FrameID             - tkinter parent frame that displays the table
        UserSelections      - matrix of True/False values passed to/from to GUI         
    Entire Table Methods:    
        getTable()         - extracts 2-D array of UserSelections from tkinter
    '''
    '''----------------------------------------------------------------------'''
    '''
    Constructor
    '''   
    def __init__( self , FrameID, UserSelections ) :               
        self.frameID             = FrameID      
        self.userSelections      = UserSelections       
        self.rowCount            = max( 1, len( self.userSelections ) )
        self.columnCount         = max( 1, len( self.userSelections[ 0 ] ) )                              
        self.checkBoxTable  = [ [ IntVar() for j in range( self.columnCount ) ]
                                           for i in range( self.rowCount ) ]
        # Construct and display the table of tkinter checkboxes
        for i in range( self.rowCount ) :
            for j in range( self.columnCount ) :
                self.checkBoxTable[i][j] = Checkbutton( self.frameID,
                                    variable = self.userSelections[ i ][ j ],
                                    onvalue  = True, offvalue = False )
                self.checkBoxTable[i][j].grid( row =  i + 1, column =  j + 1, 
                                               sticky = W )
    '''----------------------------------------------------------------------'''
    '''Methods:'''    
    def getTable( self ) :
        for i in range( self.rowCount ) :
            for j in range( self.columnCount ) :
                self.checkBoxTable[i][j] = Checkbutton( self.frameID,
                                     variable = self.userSelections[ i ][ j ],
                                     onvalue  = True,offvalue = False )
                self.checkBoxTable[i][j].grid( row = i + 1, column = j + 1,
                                               sticky = W )
        return self.userSelections   
''' END CLASS '''

顺便说一句,关于PEP8:参见PEP8介绍之后的章节。;>)我也很清楚“应用程序全局”和“创建通用包含文件并到处导入”方法的优缺点;除非绝对没有其他简单的方法来解决我的问题,否则我宁愿不使用它。(我更喜欢只在需要模块的地方导入它们。)

似乎只有在创建gui时才设置这些全局变量的值。我看不到任何代码在gui出现并且允许用户与复选按钮交互后更改其值。仅仅因为您使用
DoAll.get()
的结果对它们进行了初始化,并不意味着在选中复选按钮时它们会继续得到更新

为什么需要这些变量?你已经创建了
IntVar
s,当用户点击一个复选按钮时会更新,为什么还要添加另一个抽象级别呢?只要在需要值时使用
DoAll.get()


该表工作的原因是因为它是一个包含
IntVar
s的表,当checkbutton值更改时,这些值会不断更新。

只需定义
DoAllReports
donoreport
,如下所示:

DoAllReports = IntVar(False)
然后在客户端代码中,使用
DoAllReports.get()
,而不仅仅是
DoAllReports
,这与
donoreport
相同。不要创建单独的
IntVars
DoAll
DoNone
;它们不是必需的,只需使用
DoAllReports
donoreport

这些行没有任何作用的原因:

DoAllReports  = DoAll.get()
DoNoReports   = DoNone.get()

。。。它们是在用户单击它们所连接的按钮之前,在构建对话框的过程中执行的。因此,您只需返回刚才初始化
IntVars
时使用的相同值。您不会在任何其他时间更新全局变量,例如当用户单击
DO IT,因此它们自然永远不会更新。

抽象正是关键所在-它的接口(将价值与获得价值的方式隔离开来)。立即将tkinter值转换为标准类型(例如,从IntVar到int),将转换本地化,并且还消除了在使用该值的所有其他例程中插入.get()的需要(例如,get_用户选择中的“if”测试)。假设例程是您无法更改的预先存在的代码。对于要使用GUI值的例程,您需要将值传回python布尔值,而不是tkinter“booleanVar”。@user1459519:我想您可能误解了基于事件的编程的工作原理。每当用户单击相关的复选按钮时,
IntVar
就会改变。除非添加代码将值复制到全局变量,否则该全局变量将过期。您必须创建代码,以便在每次值更改时更新变量,或者仅在需要时获取值。我个人认为后者更干净,更易于维护。我还认为,您可能不理解调用
get()
的真正价值:它会立即告诉您该值与小部件绑定,这有助于维护。使用全局变量,回答“此变量的值来自何处”的问题变得更加困难。但是,当然,最终你需要使用你最舒服的东西。我只是想确保您理解全局变量与另一个对象保持同步的后果,这正是
IntV