如何在gtk filechooser对话框中添加“保存”按钮?

时间:2019-02-01 20:56:33

标签: gtk gjs

我有一个需要保存文件的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应该是(看起来)如何使用?

1 个答案:

答案 0 :(得分: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();