2

私はPython3.2GUIアプリケーションをリエンジニアリング(「オブジェクト」化)しており、tkinter GUIオブジェクト(ボタンラベル)をユーザーの言語(起動時に決定され、変更されることはありません)に自動的に適合させます。最初のステップとして、大きな単一のソースファイルを複数のモジュールファイルに分割します。一部の関数を問題なく独自のモジュールファイルに移動できましたが、他の「実績のある」関数は、独自のモジュールファイルから呼び出された場合は機能しません。

「IndexError:string index out of range」エラーは、あるモジュールに存在し、別の(兄弟)モジュールによって設定された変数(「Database_to_use」、以下を参照)を使用しようとすると発生します。変数を変更するプロシージャがメインラインモジュールにある場合、エラーは発生しません。スコーピングの問題や名前の衝突が疑われましたが、私が読んで行ったすべてのことから、両方の可能性が排除されたようです。

「インポート」、点線の参照を使用しており、変更関数にグローバル宣言を配置しています。ドット付き表記は、エンティティの名前を「名前変更」するためにも使用されており、エンティティが存在する場所の実装の詳細から名前を抽象化します。また、モジュール名の変更の範囲をモジュール内の1行に制限します。このアプローチが名前の非表示の問題を引き起こしていないことを確認しました。問題のあるモジュールと問題のある関数(以下の「SelectSqlDatabase」モジュール)の名前を変更した後も、同じ問題が発生しました。ところで、私はグローバルの長所と短所もよく知っています(最終的には消えます)。

次の抜粋では、コードを大幅に編集して(たとえば、1つのボタンのコードのみを表示)、問題を理解するために不可欠ではないものをすべて削除しました。

まず、「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)関数内(および/または前)に「globalDatabase_to_use」行を挿入しても効果がありませんでした
d)ダミーの文字列割り当てを削除しても効果がありませんでした
e)メインラインの最初の行(以下を参照)がこの関数を呼び出します。関数に「グローバル」行が含まれている場合、変数は更新されませんでした。「メイン」を使用する理由がわかります。動作しますが、「グローバル」行が存在する場合に変更が機能しないのはなぜですか。

次に、「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」関数はコメントであり、「SelectSqlDatabase.py」の関数と同じであることに注意してください。このコードを「現状のまま」実行すると(つまり、「SelectSqlDatabase.py」の関数を使用して)、次のエラーが発生します。

ファイル"...\ SelectSqlDatabase.py"、65行目、SelectSqlDatabase text = Database_to_use [1]、
IndexError:文字列インデックスが範囲外です

しかし、関数のコメントを解除すると(これにより、「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のデフォルト値(」ユーザーが選択したデータベースの代わりに「空」)。

要約すると、私の目的は、ユーザーが選択した(つまり、「Database_List」から抽出された)データベースサーバーに「SQL_Database」を設定することです。tkinterは、「Database_to_use」内に渡された(言語に依存する)文字列を表示します。

私はこの問題の実験と研究に多くの時間を費やしてきましたが、まったく役に立ちませんでした。私が読んだすべてのこと(Python 3.2.3チュートリアルを含む)は、私がしていることが正しいことを示唆していますが、私は単純な何かを見落としていると感じています。どこが間違っているのですか?

4

1 に答える 1

2

ここで見逃しているのは、Pythonにはポインターである変数がなく、オブジェクトへの参照であるということです。したがって、変数名を変更すると、その変数名は作成した新しいオブジェクトを指します。他のすべての変数名は、引き続き古い変数を指します。

Database_to_useしたがって、 SelectSqlDatabase にインポートすると、文字列 object がポイントされます"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 = {}あなたの main__init__.pyに a を持っているだけで、それを使用します:

設定:

from mainmodule import configuration

def somefuction():
    configuration['database'] = "mysql:blahblahblah"

使用:

from mainmodule import configuration
configuration['database'] = "mysql:blahblahblah"

def somefuction():
    databaseopener(cofiguration['database'])

また: PEP8

于 2012-12-04T06:29:32.213 に答える