10

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

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

在事件处理程序中,对话框与 command 一起显示self.myDialog.run(),但这只能工作一次,因为在run()对话框被自动销毁之后。如果我再次单击该按钮,应用程序将崩溃。

我读到有一种方法可以使用show()而不是run()对话框不被破坏,但我觉得这对我来说不是正确的方法,因为我希望对话框以模态方式运行并仅在用户之后将控制权返回给代码已关闭它。

run()有没有一种简单的方法可以使用 gtkbuilder的方法重复显示对话框?我尝试使用 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()
4

3 回答 3

6

实际上,请阅读. Dialog.run()对话框不会自动销毁。如果您hide()run()方法退出时使用它,那么您应该可以根据run()需要多次使用它。

或者,您可以在构建器文件中将对话框设置为模态,然后就show()可以了。这将实现类似但不完全相同的效果run()- 因为run()创建了主 GTK 循环的第二个实例。

编辑

如果您未连接到delete-event信号,则会出现分段错误的原因是您单击了两次关闭按钮。这是发生的事情:

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

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

于 2011-02-16T01:50:33.473 回答
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. 对话框调用 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 回答