避免关闭 GTK 对话框

Posted

技术标签:

【中文标题】避免关闭 GTK 对话框【英文标题】:Avoiding Closing a GTK Dialog 【发布时间】:2018-03-29 08:10:18 【问题描述】:

有什么方法可以在按下 OK 按钮之后但在 GTK 对话框中关闭对话框之前运行代码?我希望能够在按下 OK 按钮后对输入到对话框中的一些代码进行语法检查,如果代码无法编译,可以选择保持对话框打开。经过一番谷歌搜索后,我找到了How to avoid closing of Gtk.Dialog in Python?,但令人遗憾的是,答案缺乏细节,所以我不知道如何实现这一点。如何做到这一点?

编辑:虽然链接的问题专门询问了 Python,但我实际上并不关心任何特定的语言。我正在使用 Haskell 绑定,但我可以通过 GTK+ 绑定以任何语言回答。

编辑:如果您发现这个问题试图弄清楚如何进行验证,但没有我的复杂要求,我强烈建议您查看下面的@AlexanderDmitriev's answer。

【问题讨论】:

您是否点击了答案中的链接? 是的,我做到了。但是,我已经知道如何构建自定义对话框 - 它是自定义对话框,其内容需要在关闭对话框之前进行验证,我不知道如何制作。 【参考方案1】:

我正在添加另一个答案,以使之前的答案有效。

我看到了两种实现期望行为的方法。

    使用已弃用的gtk_dialog_get_action_area 并在此处打包一个按钮 停止信号发射以防止 GtkDialog “看到”响应按钮被按下。

这两种方法都可以在下面的代码中找到。查找deprecated 用于第一种方法,查找awesome 用于第二种方法

    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk

    class DialogExample(Gtk.Dialog):

        button_state = True

        def awesome_cb (button, de):
            if de.button_state:
                print("Awesome ok")

            else:
                print("Awesome  Not allowed")
                button.stop_emission_by_name ("clicked")

        def deprecated_cb (button, de):
            if de.button_state:
                print("Deprecated ok")
                de.response(11)
            else:
                print("Deprecated Not allowed");

        def switch_state(button, de):
            de.button_state = not de.button_state
            de.dialog_ok_btn.set_sensitive (de.button_state)


        def __init__(self, parent):
            Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
                (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                 Gtk.STOCK_OK, Gtk.ResponseType.OK))

            self.set_default_size(150, 100)

            label = Gtk.Label("This is a dialog to display additional information")

            box = self.get_content_area()
            state_switcher_btn = Gtk.Button ("Switch")
            state_switcher_btn.connect ("clicked", DialogExample.switch_state, self)

            box.add(label)
            box.add(state_switcher_btn)

            hard_work_button = Gtk.Button ("deprec")
            hard_work_button.connect ("clicked", DialogExample.deprecated_cb, self)

            carea = self.get_action_area()
            carea.add (hard_work_button)

            tfb = Gtk.Button ("awesome");
            tfb.connect("clicked", DialogExample.awesome_cb, self)
            self.add_action_widget(tfb, 12)

            self.dialog_ok_btn = self.get_widget_for_response (Gtk.ResponseType.OK)
            self.show_all()

        def do_response (self, response_id):
            print ("Response! ID is ", response_id)

    class DialogWindow(Gtk.Window):

        def __init__(self):
            Gtk.Window.__init__(self, title="Dialog Example")

            self.set_border_width(6)

            button = Gtk.Button("Open dialog")
            button.connect("clicked", self.on_button_clicked)

            self.add(button)

        def on_button_clicked(self, widget):
            dialog = DialogExample(self)
            response = dialog.run()

            if response == Gtk.ResponseType.OK:
                print("The OK button was clicked")
            elif response == Gtk.ResponseType.CANCEL:
                print("The Cancel button was clicked")

            dialog.destroy()

    win = DialogWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    Gtk.main()

【讨论】:

【参考方案2】:

看起来 GtkDialog 本身不允许取消按钮按下(从用户的角度来看这是可以的)。但是,每次用户更改某些内容时,您都可以检查它并使按钮敏感与否。我已经扩展了code from answer to mentioned question

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

class DialogExample(Gtk.Dialog):

    #this variable controls, whether OK is sensitive
    button_state = True 

    def switch_state(button, de):
        print ("switcher")
        de.button_state = not de.button_state
        de.set_response_sensitive (Gtk.ResponseType.OK, de.button_state)


    def __init__(self, parent):
        Gtk.Dialog.__init__(self, "My Dialog", parent, 0,
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
             Gtk.STOCK_OK, Gtk.ResponseType.OK))

        self.set_default_size(150, 100)

        label = Gtk.Label("This is a dialog to display additional information")

        box = self.get_content_area()
        # a button to switch OK's sensitivity
        state_switcher_btn = Gtk.Button ("Switch")
        state_switcher_btn.connect ("clicked", DialogExample.switch_state, self)

        box.add(label)
        box.add(state_switcher_btn)

        self.show_all()

    def do_response (self, response_id):
        print ("Override! ID is ", response_id)

class DialogWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="Dialog Example")

        self.set_border_width(6)

        button = Gtk.Button("Open dialog")
        button.connect("clicked", self.on_button_clicked)

        self.add(button)

    def on_button_clicked(self, widget):
        dialog = DialogExample(self)
        response = dialog.run()

        if response == Gtk.ResponseType.OK:
            print("The OK button was clicked")
        elif response == Gtk.ResponseType.CANCEL:
            print("The Cancel button was clicked")

        dialog.destroy()

win = DialogWindow()
win.connect("delete-event", Gtk.main_quit)
win.show_all()
Gtk.main()

【讨论】:

聪明的解决方案!不幸的是,我想做的特定类型的验证 - 语法检查 - 会产生一个长而详细的错误消息,我想在消息对话框中显示它,但否则我会使用这个解决方案。我正在编辑我的问题以反映这一点。 我不明白,有什么问题。在我的switch_state 函数中,您仍然可以get_content_area 并在那里打包错误消息。甚至在GtkRevealer 中打包一个标签,并在用户输入正确或错误时分别显示和隐藏文本 好点 - 我没有想到这一点。但是,我一直在考虑您的解决方案,并意识到不幸的是它无论如何都行不通。您的答案需要一些地方放置代码来交换按钮的灵敏度。在您的代码中,将此代码放在switch_state 方法中。我的问题与 TextView 有关,因此为了正确执行此操作,我需要在每次更改文本时检查输入的代码。但是,这意味着每次用户输入另一个字符时,我都需要重新解析输入的代码,... ... 花费很长时间并且使应用程序不太适合使用。我已经编辑了我的问题以更好地反映我的问题。【参考方案3】:

我曾经也有过这个。我决定捕捉response 信号。我有一个可以处理验证的函数。但是,处理response 信号的函数总是返回True 以向GTK 表明信号已经被处理并且对话框没有关闭。如果对话框需要关闭,我手动完成。

myDialogWindow.connect("response", validate_response)

def validate_response(dialog, response_id):
     # validate
     if correct:
        dialog.destroy()
     else:
        print("Something went wrong")
     return True

虽然这可以完成工作,但我不确定这是最类似于 GTK 的解决方案。

【讨论】:

【参考方案4】:

根据 Alexander Dmitriev 使用 button.stop_emission_by_name 的提示,我想出了这个解决方案,这可能就是您所要求的:

import gi
gi.require_version('Gtk', '3.0')

from gi.repository import Gtk

class MyDialog(Gtk.Dialog):
    def __init__(self, *args, **kwargs):
        super(MyDialog, self).__init__(*args, **kwargs)
        self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
                         Gtk.STOCK_OK, Gtk.ResponseType.OK)
        self.connect("response", self._cb_response)

    def _cb_response(self, widget, response_id):
        if response_id == Gtk.ResponseType.OK and self._check_invalid():
            msg = Gtk.MessageDialog(
                parent=self,
                text="There are errors in what you entered.\n\n"
                "Are you sure you want to continue?",
                message_type=Gtk.MessageType.QUESTION,
                buttons=Gtk.ButtonsType.YES_NO,
            )
            response = msg.run()
            msg.destroy()
            if response == Gtk.ResponseType.NO:
                widget.stop_emission_by_name("response")
                return True
        return False

    def _check_invalid(self):
        """Placeholder for checking for problems"""
        return True

dialog = MyDialog()
dialog.run()

【讨论】:

以上是关于避免关闭 GTK 对话框的主要内容,如果未能解决你的问题,请参考以下文章

GTK 退出对话框?

对于Gtk #Windows,是否有Form.Showdialog等价物?

避免关闭模态对话框bootstrap和jsf

如何关闭带有异步任务的进度对话框并避免“您的活动是不是运行错误?”

有时 GTK 模态对话框不是模态的 --- 错误或功能?

GTK3 主题对话框在 Audacity 中呈现纯黑色背景