无法从其他模块修改Python字符串/列表变量(由tkinter使用)

时间:2012-12-03 21:18:59

标签: python python-3.x tkinter

我重新设计("对象" -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教程)都表明我所做的是正确的,但我觉得我忽略了一些简单的事情。我哪里错了?

1 个答案:

答案 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