GTKmm 3: Parse command line with Gtk::Application

2019-07-13 19:13发布

问题:

I'm trying to use GTK's argv-handling, but there seem to be some issues with the main loop.

My goal is to parse the command line after GTK removed its options (like --display), but before opening a window, because I want my app to be usable with a CLI-only interface, too, with both variants making use of Glib etc. This is why I'm trying to open the window in the command_line signal handler.

This works as expected, exits when the window is closed.

#include <gtkmm.h>
int main(int argc, char **argv) {
    auto app = Gtk::Application::create(argc, argv, "my.app");
    Gtk::ApplicationWindow win;
    return app->run(win);
}

But simply adding the HANDLES_COMMAND_LINE flag destroys that: The window is never shown.

#include <gtkmm.h>
int on_cmd(const Glib::RefPtr<Gio::ApplicationCommandLine> &) {
    return 0;
}
int main(int argc, char **argv) {
    auto app = Gtk::Application::create(argc, argv, "my.app",
                 Gio::APPLICATION_HANDLES_COMMAND_LINE);
    app->signal_command_line().connect(sigc::ptr_fun(on_cmd), false);
    Gtk::ApplicationWindow win;
    return app->run(win);
}

So I figured that the command_line handler isn't actually suppposed to return? But the documentation says run starts the main loop. I haven't found a method that simply waits for the main loop to finish, so I crank it manually. The window is being shown again, but of course the loop continues once it's closed, which is the least problem with that code:

#include <gtkmm.h>
int on_cmd(const Glib::RefPtr<Gio::ApplicationCommandLine> &,
           Glib::RefPtr<Gtk::Application> &app) {
    Gtk::ApplicationWindow win(app);
    // app->run(win); --- lands here again -> stack overflow.
    win.show();
    // This looks very wrong but seems to work?!
    while(true)
        Glib::MainContext::get_default()->iteration(true);
    // never reach this
    return 0;
}
int main(int argc, char **argv) {
    auto app = Gtk::Application::create(argc, argv, "my.app",
                 Gio::APPLICATION_HANDLES_COMMAND_LINE);
    app->signal_command_line().connect(
      sigc::bind(sigc::ptr_fun(on_cmd), app), false);
    return app->run();
}

(gtkmm-3.0 version 3.5.13)

回答1:

Turns out, the key is calling activate on the application. The default handler which is executed when HANDLES_COMMAND_LINE is not given does that automatically.

My second example was just missing one line:

#include <gtkmm.h>
int on_cmd(const Glib::RefPtr<Gio::ApplicationCommandLine> &,
  Glib::RefPtr<Gtk::Application> &app) {
    app->activate(); // <----
    return 0;
}
int main(int argc, char **argv) {
    auto app = Gtk::Application::create(argc, argv, "my.app",
                 Gio::APPLICATION_HANDLES_COMMAND_LINE);
    app->signal_command_line().connect(
      sigc::bind(sigc::ptr_fun(on_cmd), app), false);
    Gtk::ApplicationWindow win;
    return app->run(win);
}

Here's a subclassed application which parses the command line using Glib, and if --gui is present, opens a window and only terminates after the window is closed.

Using gtk_get_option_group adds the GTK options (and help) to it, so --help-all really shows all applicable options, and we don't have to rely on gtk_main(argc, argv) to remove the GTK options, i.e. the argments can be deferred to the run(argc, argv) call (but don't have to. If the application constructor is given the arguments, it will remove the GTK options, except --help-gtk, our handler just never sees them but can still display help for it. Doesn't seem to matter either way)

#include <gtkmm.h>
struct MyApp : Gtk::Application {
    MyApp() : Gtk::Application("my.app",
      Gio::APPLICATION_HANDLES_COMMAND_LINE) {}
    int on_command_line(const Glib::RefPtr<Gio::ApplicationCommandLine> &cmd) {
        // parse arguments:
        Glib::OptionContext ctx;
        Glib::OptionGroup group("options", "main options");
        bool show_gui = false;
        Glib::OptionEntry entry;
        entry.set_long_name("gui");
        entry.set_description("show the gui.");
        group.add_entry(entry, show_gui);
        ctx.add_group(group);
        // add GTK options, --help-gtk, etc
        Glib::OptionGroup gtkgroup(gtk_get_option_group(true));
        ctx.add_group(gtkgroup);
        int argc;
        char **argv = cmd->get_arguments(argc);
        ctx.parse(argc, argv);
        // maybe show the gui
        if(show_gui)
            activate();
        return 0;
    }
    Gtk::ApplicationWindow *main;
    void on_activate() {
        // can't use Gtk::manage, so we have to keep
        // the reference or the main loop quits.
        main = new Gtk::ApplicationWindow();
        add_window(*main);
        main->show();
    }
};
int main(int argc, char **argv) {
    return MyApp().run(argc, argv);
}


标签: c++ gtkmm