10

ユーザーがボタンを押すとダイアログを表示するPyGTKアプリケーションを作成しました。ダイアログは私の__init__メソッドにロードされます:

builder = gtk.Builder()
builder.add_from_file("filename")
builder.connect_signals(self) 
self.myDialog = builder.get_object("dialog_name")

イベントハンドラーでは、ダイアログはコマンドで表示されますが、ダイアログが自動的に破棄されself.myDialog.run()た後、これは1回だけ機能します。run()ボタンをもう一度クリックすると、アプリケーションがクラッシュします。

show()ダイアログが破棄されない場所の代わりに使用する方法があることを読みましたがrun()、ダイアログをモーダルに動作させ、ユーザーの後にのみ制御をコードに戻したいので、これは私にとって正しい方法ではないと感じていますそれを閉じました。

run()gtkbuilderを使用するメソッドを使用してダイアログを繰り返し表示する簡単な方法はありますか?gtkbuilderを使用してダイアログ全体をリロードしようとしましたが、実際には機能していないようで、ダイアログにはすべての子要素がありませんでした(プログラムの開始時に、ビルダーを1回だけ使用する必要があります)。


[解決策](編集)
以下の回答で指摘されているように、を使用hide()するとトリックが実行されます。「delete-event」をキャッチする必要があると最初は思っていましたが、実際には必要ありません。動作する簡単な例は次のとおりです。


import pygtk
import gtk

class DialogTest:

    def rundialog(self, widget, data=None):
        self.dia.show_all()
        result = self.dia.run() 
        self.dia.hide()


    def destroy(self, widget, data=None):
        gtk.main_quit()

    def __init__(self):
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
        self.window.connect("destroy", self.destroy)

        self.dia = gtk.Dialog('TEST DIALOG', self.window, 
           gtk.DIALOG_MODAL  | gtk.DIALOG_DESTROY_WITH_PARENT)
        self.dia.vbox.pack_start(gtk.Label('This is just a Test'))


        self.button = gtk.Button("Run Dialog")    
        self.button.connect("clicked", self.rundialog, None)
        self.window.add(self.button)
        self.button.show()
        self.window.show()



if __name__ == "__main__":
    testApp = DialogTest()
    gtk.main()
4

3 に答える 3

6

実際には、のドキュメントを読んでくださいDialog.run()。ダイアログは自動的に破棄されません。メソッドが終了hide()したときにそれを実行すると、何度でも実行できるようになります。run()run()

または、ビルダーファイルでダイアログをモーダルに設定してから、それだけを設定することもできますshow()。これにより、メインのGTKループの2番目のインスタンスが作成されるrun()ため、-と似ていますが、まったく同じではない効果が得られます。run()

編集

信号に接続しない場合にセグメンテーション違反が発生する理由はdelete-event、閉じるボタンを2回クリックしているためです。これが何が起こるかです:

  1. [ダイアログの実行]をクリックすると、ダイアログのrun()メソッドが呼び出されます。
  2. モーダルダイアログが表示され、独自のメインループが開始されます。
  3. [閉じる]ボタンをクリックします。ダイアログのメインループは終了しますがrun()、閉じるボタンの通常の動作をオーバーライドするため、ダイアログは閉じられません。また、隠されていないので、ぶらぶらしています。
  4. なぜダイアログがまだそこにあるのか疑問に思い、もう一度閉じるボタンをクリックします。run()アクティブではなくなったため、閉じるボタンの通常の動作がトリガーされます。ダイアログは破棄されます。
  5. run()もう一度[ダイアログの実行]をクリックすると、破棄されたダイアログのメソッドが呼び出されます。クラッシュ!

したがって、hide()手順3の後でダイアログを確認すると、すべてが機能するはずです。delete-event信号に接続する必要はありません。

于 2011-01-11T22:14:03.313 に答える
2

私はこれを理解するのに少し時間を費やしました。ビルダーから同じオブジェクトを再フェッチしても、オブジェクトの新しいインスタンスは作成されませんが、古い(破棄された)オブジェクトへの参照のみが返されます。ただし、新しいビルダーインスタンスを作成し、ファイルを新しいビルダーにロードすると、新しいインスタンスが作成されます。

したがって、私のダイアログ作成関数は次のようになります。

def create():
    builder = gtk.Builder()
    builder.add_from_file('gui/main.ui')

    dlg = builder.get_object('new_dialog')

    def response_function(dialog, response_id):
        ... do stuff ...
        dialog.destroy()

    dlg.connect('response', response_function)
    dlg.show_all()

この場合、twistedを使用しているため、run()で応答をブロックしていないことに注意してください。ただし、同等である必要があります。

于 2011-02-16T01:50:33.473 に答える
2

ダイアログを実行する必要があるのは1回だけです。メニュー項目がダイアログをトリガーすると仮定すると、コードは次のようになります。

def on_menu_item_clicked(self, widget, data=None):
    dialog = FunkyDialog()
    response = dialog.run()

    if response = gtk.RESPONSE_OK:
        // do something with the dialog data

    dialog.destroy()

dialog.run()ダイアログが応答を送信したときに戻るブロッキングメインループです。これは通常、[OK]ボタンと[キャンセル]ボタンを使用して実行されます。これが発生すると、ダイアログは終了し、破棄する必要があります。

ダイアログを繰り返し表示するには、ユーザーは同じワークフローに従う必要があります(上記の例では、メニュー項目をクリックします)。ダイアログは、で__init__、それ自体を設定する責任があります。ダイアログを使用する場合hide()、そのダイアログとの通信に問題があるため、非表示になっている場合でも、アプリケーションの他の部分と最新の状態に保たれます。

「ダイアログを繰り返し実行」したい理由の1つは、ユーザーが無効な情報を入力し、それを修正する機会をユーザーに与えたいためです。これは、ダイアログの応答信号ハンドラーで処理する必要があります。ダイアログ内のイベントの順序は次のとおりです。

  1. ユーザーが物理的に[OK]ボタンを押す
  2. ダイアログは応答を送信しますgtk.RESPONSE_OK(-5)
  3. ダイアログが応答信号のハンドラーを呼び出します
  4. ダイアログが[OK]ボタンのハンドラーを呼び出します
  5. Dialogrun()メソッドは応答を返します

手順4と5が発生しないようにするには、応答ハンドラーが応答信号を抑制する必要があります。これは次のように実現されます。

def on_dialog_response(self, dialog, response, data=None:
    if response == gtk.RESPONSE_OK:
        if data_is_not_valid:
            # Display an error message to the user

            # Suppress the response
            dialog.emit_stop_by_name('response')
于 2011-01-22T10:02:38.653 に答える