pygtk / gtkbuilderでダイアログを繰り返し表示する方法は?
-
09-10-2019 - |
質問
ユーザーがボタンを押したときにダイアログを表示する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()
ダイアログは自動的に破壊されます。 2回目のボタンをクリックすると、アプリケーションがクラッシュします。
私は使用する方法があることを読みました show()
それ以外の run()
ダイアログが破壊されていないが、ダイアログをモジュアルに動作させ、ユーザーが閉じた後にのみコードにコントロールを返すことを望んでいるので、これは私にとって正しい方法ではないと感じています。
を使用してダイアログを繰り返し表示する簡単な方法はありますか run()
gtkbuilderを使用した方法? GTKBuilderを使用してダイアログ全体をリロードしようとしましたが、それは実際には機能していなかったようで、ダイアログにはすべての子供の要素が欠落していました(そして、プログラムの開始時にビルダーを1回だけ使用する必要があります)。
解決 (編集)
以下の回答で指摘されているように、使用してください hide()
トリックをします。私は最初に「削除イベント」をキャッチする必要があると思いましたが、実際には必要ありません。動作する簡単な例は次のとおりです。
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()
解決
実際、読んでください ドキュメンテーション の上 Dialog.run()
. 。ダイアログは自動的に破壊されません。もし、あんたが hide()
それは run()
メソッドは終了し、できるはずです run()
それはあなたが望むだけ何度も。
または、ダイアログをビルダーファイルでモーダルに設定してから、 show()
それ。これは似たような効果を達成しますが、まったく同じではありません run()
- なぜなら run()
メインGTKループの2番目のインスタンスを作成します。
編集
あなたが接続しない場合、あなたがセグメンテーション過失を取得している理由 delete-event
信号とは、閉じるボタンを2回クリックしていることです。これが起こることです:
- ダイアログを実行]をクリックすると、これはダイアログを呼び出します
run()
方法。 - モーダルダイアログが表示され、独自のメインループを開始します。
- 閉じる]ボタンをクリックします。ダイアログのメインループは終了しますが、それ以来です
run()
閉じるボタンの通常の動作をオーバーライドすると、ダイアログは閉じられていません。また隠されていないので、ぶらぶらしています。 - ダイアログがまだそこにあるのか、もう一度閉じるボタンをクリックします。以来
run()
もうアクティブではなく、閉じるボタンの通常の動作がトリガーされます。ダイアログが破壊されます。 - 「ダイアログを実行する」をもう一度クリックします。
run()
破壊されたダイアログの方法。クラッシュ!
だからあなたが確実にするなら hide()
ステップ3の後のダイアログ、その後、すべてが機能するはずです。に接続する必要はありません delete-event
信号。
他のヒント
私はちょうどこれを理解するのに少し時間を費やしました。ビルダーから同じオブジェクトを再獲得すると、オブジェクトの新しいインスタンスは作成されませんが、古い(破壊された)オブジェクトへの参照のみを返します。ただし、新しいビルダーインスタンスを作成し、ファイルを新しいビルダーにロードすると、新しいインスタンスが作成されます。
だから私のダイアログ作成機能は次のように見えます:
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()で応答をブロックしていないことに注意してください。
ダイアログは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つは、ユーザーが無効な情報を入力したためであり、ユーザーにそれを修正する機会を与えたいからです。これは、ダイアログの応答信号ハンドラーで対処する必要があります。ダイアログ内のイベントの順序は次のとおりです。
- ユーザーは物理的にOKボタンを押します
- ダイアログは応答を送信します
gtk.RESPONSE_OK
(-5) - ダイアログは、応答信号のハンドラーを呼び出します
- ダイアログは、OKボタンのハンドラーを呼び出します
- ダイアログ
run()
メソッドは応答を返します
ステップ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')