我无法让tkinter更新一对简单的“应用程序全局”布尔变量来匹配相关复选框的变量。布尔值被初始化,更新并存储在一个文件中,并由另一个文件引用(只读)。无论复选框状态如何,布尔值都不会从默认值更改。 (值得注意的一点:False / True的初始值变为0/1,可能是由于tkinter forlelements booleans有问题)。 奇怪的是,对应于tkinter复选框表的类似“应用程序全局”布尔表没有问题。该表是由第三个文件定义的类的实例。该表与简单的布尔值位于同一个文件中(并以相同的方式处理) 所有布尔值都在一个文件(File1.py)中使用(只读),并且只由另一个文件(File2.py)修改。显式返回问题布尔值(从File2.py到File1.py)不起作用。
正在使用的环境是带有Pydev 2.7.32013031601插件的Eclipse Juno(4.2.2,build M20130204-1200)上的Windows 7,Python 3.2.2。该应用程序正在从Eclipse控制台运行。
通过LEGB范围规则,一切似乎都正常(包含和全局语句存在,虚线名称形式用于外部定义的“应用程序级”全局变量)。在我看来,要么所有布尔更新都应该工作,要么都不应该工作。那么为什么布尔表工作正常而不是简单的布尔值 - 以及让简单的布尔值正常工作需要什么呢?
推测,它可能与python实现具有基本值(例如0,1,2,3,True?False?)的变量作为“共享值”而不是单独变量的方式有关 - 这会导致引用时出现问题这些来自其他模块的简单值?
示例应用程序包含3个文件;每个都尽可能减少,同时仍然能够显示问题。在Windows 7上运行时,您应该看到:
File1.py - mainline,包含回调函数“Get_User_Selections”:
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
#==========================================================================
TkinterCheckBoxTableClass.py - 实现布尔表:
'''
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介绍后的部分。 ;>)我也很清楚“应用程序全局”和kludgey“创建一个通用的包含文件并将其导入到任何地方”的方法的优点/缺点;一个我不想使用的,除非绝对没有其他简单的方法来解决我的问题。 (我更喜欢只在需要的地方导入模块。)
答案 0 :(得分:1)
只需定义DoAllReports
和DoNoReports
,就像这样:
DoAllReports = IntVar(False)
然后在您的客户端代码中,使用DoAllReports.get()
代替DoAllReports
,DoNoReports
使用IntVars
。不要创建单独的DoAll
DoNone
和DoAllReports
;它们不是必需的,只需使用DoNoReports
和DoAllReports = DoAll.get()
DoNoReports = DoNone.get()
。
这些行没有做任何事情的原因:
IntVars
...是在构建对话框之前执行,然后用户才有机会点击他们所附加的按钮。因此,您只需返回刚刚初始化DO IT!
的相同值。您不会在任何其他时间更新您的全局变量,例如当用户点击{{1}}时,它们自然不会更新。
答案 1 :(得分:0)
在创建gui时,您似乎只设置了这些全局变量的值。一旦gui出现并允许用户与支票按钮交互,我看不到代码会改变它们的值。仅仅因为使用DoAll.get()
的结果初始化它们并不意味着在检查检查按钮时它们会继续更新。
为什么需要这些变量?当用户点击一个checkbutton时,您已经创建了做更新的IntVar
,为什么要添加另一个抽象级别?只要您需要值,就可以使用DoAll.get()
。
表的工作原因是因为它是IntVar
的表,当checkbutton值发生变化时会不断更新。