如何使用PyGTK/Gtkbuilder重复显示对话框?

11

我创建了一个PyGTK应用程序,当用户按下按钮时,它会显示一个对话框。 该对话框在我的__init__方法中加载:

builder = gtk.Builder()
builder.add_from_file("filename")
builder.connect_signals(self) 
self.myDialog = builder.get_object("dialog_name")
在事件处理程序中,使用命令self.myDialog.run()显示对话框,但这只能有效执行一次,因为在run()之后,对话框会自动销毁。如果我第二次点击按钮,应用程序就会崩溃。
我了解到可以使用show()而不是run()的方法,在这种情况下对话框不会被销毁,但我觉得这对我来说不是正确的方法,因为我希望对话框表现为模态,并且仅在用户关闭它后才将控制返回给代码。
使用gtkbuilder是否有一种简单的方法重复使用run()方法显示对话框?我尝试使用gtkbuilder重新加载整个对话框,但似乎并没有真正起作用,对话框缺少所有子元素(而且我更愿意在程序开始时仅使用构建器一次)。 [解决方案](已编辑)
如下面的答案指出,使用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()
3个回答

7
实际上,阅读有关Dialog.run()文档。对话框不会自动销毁。如果在run()方法退出时使用hide()隐藏它,则应该能够多次运行它。
或者,您可以在构建器文件中将对话框设置为模态,然后只需调用show()即可。这将实现类似但不完全相同于run()的效果-因为run()会创建主GTK循环的第二个实例。
编辑
如果您不连接到delete-event信号,则会出现分段错误的原因是您两次单击关闭按钮。以下是发生的情况:
1. 您单击“运行对话框”,这将调用对话框的run()方法。 2. 模态对话框出现并启动其自己的主循环。 3. 您单击关闭按钮。对话框的主循环退出,但由于run()覆盖了关闭按钮的正常行为,因此对话框未关闭。它也没有隐藏,所以它仍然存在。 4. 您想知道为什么对话框仍然存在并再次单击关闭按钮。由于run()不再处于活动状态,因此触发关闭按钮的正常行为:对话框被销毁。 5. 您再次单击“运行对话框”,尝试调用已销毁对话框的run()方法。崩溃!
因此,如果在第3步后确保hide()对话框,则一切都应该正常工作。没有必要连接到delete-event信号。

嗯,这里的文档并不是很清楚。看起来,“run”方法本身并不会销毁对话框,但如果您通过窗口管理器提供的“关闭”控件(通常位于窗口右上角的小“X”)关闭它,则如果您没有捕获“delete-event”,对话框将被销毁。 - Julian
@Julian,正好相反-请看文档的第二段。即使您关闭窗口,对话框也不会被销毁。 - ptomato
是的,我看过了,但我有点困惑:如果在我上面发布的示例中注释掉self.dia.connect("delete-event", self.closedialog)这一行,第二次单击按钮时会出现分段错误。因此,某些东西已经被销毁了,而我并没有这样做,对吧?顺便说一下,谢谢您的澄清。 - Julian
@Julian,直接删除closedialog函数,并在run()调用后放置self.dia.hide()即可实现你想要的效果。问题在于当你遇到段错误时,你会点击两次关闭按钮。请查看我编辑后的答案以了解原因。 - ptomato

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()

请注意,我在这种情况下不会使用run()来阻止响应,因为我正在使用twisted,但它应该是等效的。

2

您的对话框只需要运行一次。假设菜单项触发对话框,则代码应如下所示:

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() 是一个阻塞的主循环,会在对话框发出响应时返回。通常通过“确定”和“取消”按钮进行操作。此时,对话框已完成并需要被销毁。

如果要重复显示对话框,则用户应按照相同的工作流程(例如,单击菜单项)。在__init__中,对话框负责设置自身。如果你使用hide()隐藏对话框,那么就需要解决如何与该对话框进行通信,以便它即使在隐藏时也能保持与应用程序的其余部分更新同步。

有些人想“重复运行对话框”的原因是用户输入了无效信息,而你希望给用户纠正的机会。这必须在对话框的响应信号处理器中处理。对话框中事件的顺序如下:

  1. 用户物理上点击了“确定”按钮
  2. 对话框发送响应gtk.RESPONSE_OK(-5)
  3. 对话框调用响应信号的处理器
  4. 对话框调用“确定”按钮的处理器
  5. 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')

好的,但是我使用GtkBuilder来获取对话框,一旦对话框被销毁,它就会保持销毁状态 - 我找不到重新设置它的方法。 - Julian
destroy() 正是如其名,它会销毁对象。如果你想再次显示同样的对话框,你需要创建另一个实例。阅读 http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#method-gtkwidget--destroy 以获取更多关于 destroy() 的信息,尤其是:“如果小部件是顶层窗口(派生自 gtk.Window),它将从顶层窗口列表中移除,并且 PyGTK 对它的引用也将被移除。” - Jon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接