我重新设计("对象" -ifying)一个Python 3.2 GUI应用程序,它自动将tkinter GUI对象(按钮标签)调整为用户的语言(在启动时确定,从不变化)。作为第一步,我将大型单个源文件分解为多个模块文件。我能够将一些功能移动到他们自己的模块文件中而没有任何问题 - 但其他"证明是好的"从他们自己的模块文件调用时,函数将不起作用。
" IndexError:字符串索引超出范围"尝试使用驻留在一个模块中并由不同(兄弟)模块设置的变量(" Database_to_use",见下文)时发生错误。修改变量的过程在主线模块中时不会发生错误。我怀疑是一个范围问题和/或名字冲突,但是从我所阅读和完成的所有内容中,似乎已经消除了这两种可能性。
我正在使用" 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)插入"全局Database_to_use"功能内部(和/或之前)的行没有帮助
d)删除虚拟字符串分配没有帮助
e)主线的第一行(见下文)称此功能。当函数包含" global"变量未更新的行。我理解为什么要使用" main 。"工作 - 但为什么在" global"有线?
接下来," SelectSqlDatabase.py"模块,它具有问题功能:
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
最后是主线。注意,在这个文件中" SelectSqlDatabase"函数是一个注释,并且对于该函数是IDENTICAL " SelectSqlDatabase.py&#34 ;.运行此代码" AS IS" (即使用" SelectSqlDatabase.py"中的函数),我收到以下错误:
File" ... \ SelectSqlDatabase.py",第65行,在SelectSqlDatabase中
text = Database_to_use [1],
IndexError:字符串索引超出范围
但是当我对函数进行UN注释(从而将函数隐藏在" SelectSqlDatabase.py")时,应用程序正确运行!
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 )
要完成,有一次我能够消除这个错误(通过" Database_to_use"作为参数)但是发生了其他令人讨厌的事情,例如:
a)显示无用虚拟标题的GUI(实际上忽略了对" SetUpLanguageInUse"的调用),
b)收到关于" Database_to_use"的错误消息。没有get()函数,或者
c)" SQL_Database"中返回的值是它的默认值("待确定")或User_Choice的默认值("空")而不是用户选择的数据库。
总结一下,我的目标是设置" SQL_Database"到用户选择的数据库服务器(即从中提取) " Database_List") - tkinter显示传递给它的(语言相关的)字符串" Database_to_use"。
我花了很多时间试验&研究这个问题 - 一切都无济于事。我读过的所有内容(包括Python 3.2.3教程)都表明我所做的是正确的,但我觉得我忽略了一些简单的事情。我哪里错了?
答案 0 :(得分:2)
你在这里缺少的是Python 不有变量指针,它们是对象的引用。因此,当您更改变量名称时,该变量名称将指向您创建的新对象。所有其他变量名称将继续指向旧变量。
因此,当您将Database_to_use
导入SelectSqlDatabase时,它将指向字符串对象"dummy string forces this variable into the global namespace"
。当您的函数稍后将第一个文件中的Database_to_use
名称更改为指向另一个字符串时,这不会修改SelectSqlDatabase.Database_to_use
引用的内容。它将继续引用原始字符串。
那么,你应该怎么做?您应该将运行时配置保存在某种对象中,并始终从该对象中查找变量,这样就不会将配置变量保留在不会更改的局部变量中。
所以在这种情况下,要解决问题,请删除行
Database_to_use = LanguageInUse.Database_to_use
而只是一直引用LanguageInUse.Database_to_use
,它可能会解决问题。
但它仍然有点难看。我可能只在主configuraion = {}
中有一个__init__.py
,然后使用:
环境:
from mainmodule import configuration
def somefuction():
configuration['database'] = "mysql:blahblahblah"
使用:
from mainmodule import configuration
configuration['database'] = "mysql:blahblahblah"
def somefuction():
databaseopener(cofiguration['database'])
另外:PEP8