2017-01-26 16 views
1

私はたくさんのソースからのレコードを含む大きなファイルのデータをプロットするPythonアプリケーションを開発しています。私がユーザーに与えようとしているオプションの1つは、必要に応じてこれらのソースのサブセットのみをプロットするオプションです。最初にファイルを読み、ユニークなものが何個あるかを調べ、ソースごとに名前が付けられたQCheckBox()を作成します(それぞれのソースは一意の名前を持ちます)。この特定の場合、データファイルは、キーが固有のソースである巨大な辞書に解析されます。私は各チェックボックスのstateChange()イベントに接続し、ボックスがチェックされていないときにそのソースのプロットを無効にしたいとします。この場合、チェックボックスをオンまたはオフにしたときにソースリストにソースを追加/削除します。私が実行している問題は、すべてのチェックボックスが私のリストの最終的なソースに接続することです。ダイナミックに作成されたQCheckBoxesのstateChangeイベントに接続

最初に作成されたウィンドウは正しく表示され、各ボタンの名前は適切です。ボタンが押されるたびに、btnstate()はそのボタンに関連付けられたテキストを単に出力することになっています。この例のラジオボタンに示すように、各ボタンを明示的に定義できる場合は、このメソッドが機能します。いずれかをクリックすると、ボタンの正しい名前が表示されますが、チェックボックスのチェックを外す/再チェックすると、btnstateは "test4"を表示します。

私は間違っていますか?私はこれが起こっていることの理由は、Pythonが結合して、ループ内の参照をアンバインドする方法に関係していると思います

import sys 
from PyQt4.QtGui import * 

def btnstate(b): 
    print b.text() 

def main(): 
    app = QApplication([]) 
    widget = QWidget() 
    layout = QVBoxLayout() 
    widget.setLayout(layout) 
    radio_layout = QHBoxLayout() 
    checkbox_layout = QHBoxLayout() 

    #setup radio buttons for config pop-up window 
    r1 = QRadioButton("Page Count") 
    r2 = QRadioButton("Date") 
    r1.toggled.connect(lambda:btnstate(r1)) 
    r2.toggled.connect(lambda:btnstate(r2)) 
    radio_layout.addWidget(r1) 
    radio_layout.addWidget(r2) 

    cbs = [] 
    for idx, serial in enumerate(["test1", "test2", "test3", "test4"]): 
     temp = QCheckBox(serial) 
     temp.setText(serial) 
     temp.setChecked(True) 
     checkbox_layout.addWidget(temp) 
     temp.stateChanged.connect(lambda:btnstate(temp)) 
     cbs.append(temp) 

    layout.addLayout(radio_layout) 
    layout.addLayout(checkbox_layout) 
    widget.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 
+0

テキストが多すぎます説明を簡略化してください – eyllanesc

答えて

0

:ここ

コード(ソースはダミーの値に変更)です。 tempが各繰り返しで再定義されているため、スロットは更新されているため、ボタンごとに同じラムダを効果的に呼び出すことができます。 Pythonの参照の詳細についての私の理解は深く深いものではないので、これは手荒なものです。しかし、Qtに対するPythonのバインディングには、Pythonの参照やガベージコレクションに関する多くの問題があることがわかります。ウィジェットを削除すると、Qtオブジェクト階層がPythonで完全に機能しないためです。

とにかく、もっと実際的には、非常に簡単な修正があります。 functools.partialメソッドを使用して、部分関数をラムダではなくスロットとして定義することができます。ボタンを最初のオブジェクトとしてバインドし、ボタンの状態(信号の引数として放出されます)をアンバインドしたままにします。これと同じように:

ループで次に
import functools 

def btnstate(button, state): 
    print button.text() 

:各ボックスにチェック/チェックを外しているときにこれを実行する

for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']): 
    temp = QCheckBox(serial) 
    checkbox_layout.addWidget(temp) 
    temp.stateChanged.connect(functools.partial(btnstate, serial)) 

、私は今、正しいラベルが印刷され得ます。

編集:

はQtのオブジェクト階層との奇妙な方法で対話するPythonの参照カウントの別例えばthis postを参照してください。

+0

それぞれの 'lambda'は異なりますが、すべて同じ' temp'変数を参照しています。これは単にループで作成された最後のチェックボックスにバインドされています。 'partial'と同等の解決策は、現在の変数をデフォルト引数としてキャッシュすることです:' lambda state、temp = temp:btnstate(temp) '。疑問は実際にはガベージコレクションではなくスコープに関するもので、純粋にPythonに関連しています。 – ekhumoro

+0

@ekhumoroああ、右のスコープはGCではない問題です。これを指摘してくれてありがとう、私はこの種の細部を考えてからしばらくしています。 – bnaecker

関連する問題