我正在重新设计(“对象”化)一个 Python 3.2 GUI 应用程序,它自动将 tkinter GUI 对象(按钮标签)适应用户的语言(在启动时确定并且永远不会改变)。作为第一步,我将大型的单一源文件分解为多个模块文件。我能够毫无问题地将一些函数移动到它们自己的模块文件中——但是当从它们自己的模块文件调用时,其他“被证明很好”的函数将不起作用。
尝试使用驻留在一个模块中并由不同(兄弟)模块设置的变量(“Database_to_use”,见下文)时发生“IndexError:字符串索引超出范围”错误。当修改变量的过程位于主线模块中时,不会发生该错误。我怀疑存在范围界定问题和/或名称冲突,但从我所阅读和所做的一切来看,似乎已经消除了这两种可能性。
我正在使用“import”、虚线引用,并将全局声明放在修改函数中。点分符号也被用于“重命名”实体,以一种将名称从其所在位置的实现细节中抽象出来的方式 - 并且还将模块名称更改的范围限制为模块内的一行。我检查了这种方法不会导致名称隐藏问题;在我重命名问题模块和问题函数(下面的“SelectSqlDatabase”模块)后发生了同样的问题。顺便说一句,我也很清楚全局变量的优缺点(最终会消失)。
在下面的摘录中,我对代码进行了严格的编辑(例如,仅显示一个按钮的代码)以删除对理解问题不重要的所有内容。
首先是“SetUpLanguageInUse.py”模块;这是有问题的“Database_to_use”(以及没有问题的“Main_Title”)变量的位置:
English = 'English'
Francais = 'Francais'
Database_to_use = "dummy string forces this variable into the global namespace"
Main_Title = "dummy string forces this variable into the global namespace"
def SetUpLanguageInUse( user_language ):
import __main__ # needed to modify the global value
if ( user_language == English ):
__main__.Main_Title = 'Monthly Summary of Reports'
__main__.Database_to_use = ( '', 'Build', 'Staging', 'Production' )
elif ( user_language == Francais ):
__main__.Main_Title = 'Resume Mensuel de Rapports'
__main__.Database_to_use = ( '', 'Construire', 'Relais', 'Production' )
.....
return
关于上述模块的一些观察:
a) 问题与用户语言无关。b) 使用 [] 而不是 () 并不能解决问题。c) 在函数内部(和/或之前)插入“global Database_to_use”行没有帮助
d) 删除虚拟字符串分配没有帮助
e) 主线的第一行(见下文)调用此函数。当函数包含“全局”行时,变量没有更新。我明白为什么使用“ main ”。有效 - 但是当存在“全局”行时,为什么修改不起作用?
Next, the "SelectSqlDatabase.py" module, it holds the problem function:
from tkinter import *
from tkinter import ttk
import LanguageInUse
Database_to_use = LanguageInUse.Database_to_use
Database_List = ( '', 'server1', 'server2', 'server3' )
SQL_Database = "To be determined"
# ----- the problematic function
def SelectSqlDatabase( SelectDatabaseFrame ) :
global SQL_Database
UserSelection = StringVar( value = "Empty" )
Build_DB = Radiobutton( SelectDatabaseFrame,
text = Database_to_use[ 1 ], # the problematic line
variable = UserSelection,
value = Database_List[ 1 ] )
SQL_Database = UserSelection.get()
return
Finally the mainline. Notice that inthis file the "SelectSqlDatabase" function is a COMMENT and is IDENTICAL to that in "SelectSqlDatabase.py". When this code is run "AS IS" (i.e. using the function in "SelectSqlDatabase.py"), I get the following error:
File "...\SelectSqlDatabase.py", line 65, in SelectSqlDatabase
text = Database_to_use[ 1 ],
IndexError: string index out of range
But when I UN-comment the function (thereby hiding the function in "SelectSqlDatabase.py"), the application runs correctly!
from tkinter import *
from tkinter import ttk
import LanguageInUse
import SelectSqlDatabase
Database_to_use = LanguageInUse.Database_to_use
Database_List = SelectSqlDatabase.Database_List
English = LanguageInUse.English
SetUpLanguageInUse = LanguageInUse.SetUpLanguageInUse
SQL_Database = SelectSqlDatabase.SQL_Database
SelectSqlDatabase = SelectSqlDatabase.SelectSqlDatabase # gets overridden
# ----- the problematic function
'''
def SelectSqlDatabase( SelectDatabaseFrame ) :
global SQL_Database
UserSelection = StringVar( value = "Empty" )
Build_DB = Radiobutton( SelectDatabaseFrame,
text = Database_to_use[ 1 ], # the problematic line
variable = UserSelection,
value = Database_List[ 1 ] )
SQL_Database = UserSelection
return
'''
'''----------'''
def ChooseDataSourceFrame( mainframe ) :
ChooseSourceFrame = ttk.LabelFrame( mainframe, ... )
SelectDatabaseFrame = ttk.LabelFrame( ChooseSourceFrame )
SelectSqlDatabase( SelectDatabaseFrame )
return
'''***** MAINLINE ***** '''
SetUpLanguageInUse( English ) #TODO: make language a startup parameter
mainframe = ttk.Frame( ... )
ChooseDataSourceFrame( mainframe )
To be complete, at one time I was able to eliminate this error (by passing "Database_to_use" as a parameter) but other nasty things happened such as:
a) the GUI displaying the useless dummy titles (effectively ignoring the call to "SetUpLanguageInUse"),
b) getting an error message about "Database_to_use" not having a get() function, or
c) the value returned in "SQL_Database" being either it's default value ("To be determined") or the default value for User_Choice ("Empty") instead of the database chosen by the user.
To summarize, my objective is to set "SQL_Database" to the database server chosen by the user (i.e. extracted from "Database_List") - with tkinter displaying the (language-dependent) strings passed to it inside "Database_to_use".
I've spent a LOT of time experimenting & researching this problem - all to no avail. Everything I've read (including the Python 3.2.3 Tutorial) suggests that what I'm doing is correct, however I feel I'm overlooking something simple. Where am I going wrong?