【问题标题】:How do I add a "save" button to the gtk filechooser dialog?如何在 gtk 文件选择器对话框中添加“保存”按钮?
【发布时间】:2019-02-02 06:38:26
【问题描述】:

我有一个需要保存文件的 Gjs 应用程序。我可以从我的菜单中很好地打开文件选择器对话框,并且我添加了一个“保存”和“取消”按钮,但我无法获得“保存”按钮来触发任何内容。

我知道我应该向它传递一个 response_id,但我不确定它应该是什么样子,也不知道之后我应该如何处理它。 我在这里读到了那部分: https://www.roojs.com/seed/gir-1.2-gtk-3.0/gjs/Gtk.FileChooserDialog.html#expand

let actionSaveAs = new Gio.SimpleAction ({ name: 'saveAs' });
    actionSaveAs.connect('activate', () => {
            const saver = new Gtk.FileChooserDialog({title:'Select a destination'});
            saver.set_action(Gtk.FileChooserAction.SAVE);
            saver.add_button('save', 'GTK_RESPONSE_ACCEPT');
            saver.add_button('cancel', 'GTK_RESPONSE_CANCEL');
            const res = saver.run();
            if (res) {
              print(res);
              const filename = saver.get_filename();
              print(filename);
            }
            saver.destroy();
          });
    APP.add_action(actionSaveAs);

当我关闭对话框时,我可以捕捉到res 并触发相关的小日志记录操作,但“保存”和“取消”按钮都只是关闭对话框而不做任何事情或说任何话。

我的问题是,GJS 中的 GTK_RESPONSE_ACCEPT 和 GTK_RESPONSE_CANCEL 应该(看起来像)是什么,我该如何使用它们?

【问题讨论】:

    标签: gtk gjs


    【解决方案1】:

    在 GJS 中,像 GTK_RESPONSE_* 这样的枚举是数字,实际上看起来像这样:

    // imagine this is the Gtk import
    const Gtk = {
        ResponseType: {
            NONE: -1,
            REJECT: -2,
            ACCEPT: -3,
            DELETE_EVENT: -4,
            ...
        }
    };
    
    // access like so
    let response_id = -3;
    
    if (response_id === Gtk.ResponseType.ACCEPT) {
        log(true);
    }
    

    here 有更多关于此的信息。

    let saver = new Gtk.FileChooserDialog({
        title:'Select a destination',
        // you had the enum usage correct here
        action: Gtk.FileChooserAction.SAVE
    });
    
    // Really the response code doesn't matter much, since you're
    // deciding what to do with it. You could pass number literals
    // like 1, 2 or 3. Probably this was not working because you were
    // passing a string as a response id.
    saver.add_button('Cancel', Gtk.ResponseType.CANCEL);
    saver.add_button('Save', Gtk.ResponseType.OK);
    
    // run() is handy, but be aware that it will block the current (only)
    // thread until it returns, so I usually prefer to connect to the
    // GtkDialog::response signal and use GtkWidget.show()
    saver.connect('response', (dialog, response_id) => {
        if (response_id === Gtk.ResponseType.OK) {
            // outputs "-5"
            print(response_id);
    
            // NOTE: we're using @dialog instead of 'saver' in the callback to
            // avoid a possible cyclic reference which could prevent the dialog
            // from being garbage collected.
            let filename = dialog.get_filename();
    
            // here's where you do your stuff with the filename. You might consider
            // wrapping this whole thing in a re-usable Promise. Then you could call
            // `resolve(filename)` or maybe `resolve(null)` if the response_id
            // was not Gtk.ResponseType.OK. You could then `await` the result to get
            // the same functionality as run() but allow other code to execute while
            // you wait for the user.
            print(filename);
    
            // Also note, you actually have to do the writing yourself, such as
            // with a GFile. GtkFileChooserDialog is really just for getting a
            // file path from the user
            let file = Gio.File.new_for_path(filename);
    
            file.replace_contents_bytes_async(
                // of course you actually need bytes to write, since GActions
                // have no way to return a value, unless you're passing all the
                // data through as a parameter, it might not be the best option
                new GLib.Bytes('file contents to write to disk'),
                null,
                false,
                Gio.FileCreateFlags.REPLACE_DESTINATION,
                null,
                // "shadowing" variable with the same name is another way
                // to prevent cyclic references in callbacks.
                (file, res) => {
                    try {
                        file.replace_contents_finish(res);
                    } catch (e) {
                        logError(e);
                    }
                }
            );
        }
    
        // destroy the dialog regardless of the response when we're done.
        dialog.destroy();
    });
    
    // for bonus points, here's how you'd implement a simple preview widget ;)
    saver.preview_widget = new Gtk.Image();
    saver.preview_widget_active = false;
    this.connect('update-preview', (dialog) => {
        try {
            // you'll have to import GdkPixbuf to use this
            let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(
                dialog.get_preview_filename(),
                dialog.get_scale_factor() * 128,
                -1
            );
            dialog.preview_widget.pixbuf = pixbuf;
            dialog.preview_widget.visible = true;
            dialog.preview_widget_active = true;
    
        // if there's some kind of error or the file isn't an image
        // we'll just hide the preview widget
        } catch (e) {
            dialog.preview_widget.visible = false;
            dialog.preview_widget_active = false;
        }
    });
    
    // this is how we'll show the dialog to the user
    saver.show();
    

    【讨论】:

    • 是的,我一开始是想传递一个字符串......我终于想到我需要一些东西,因为文档确实说“gint32 response_id required (not null)”,我注意到任何数字都可以,但这似乎仍然不是一个好方法......感谢您的帮助@andy.holmes
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-17
    • 2011-11-27
    • 2021-08-06
    相关资源
    最近更新 更多