retireSakiの日記

たぶん引退した?ソフトウエアエンジニアのブログ

GTKmm (3.0) と glade によるアプリ (4) - メニューバーの表示と表示切り替え

 今回は、前回の"GTKmm (3.0) と glade によるアプリ (2) - メニューバーの表示と表示切り替え"で作成したプログラムをベースに、

retiresaki.hatenablog.com

  (1) メニューバーの表示
  (2) メニューバーの終了をフック
  (3) ボタンクリックによるメニューの表示/非表示の切り替え機能
の3つの機能を付けます。

 実際に動作させたときの画像です。

f:id:retireSaki:20181015134212j:plain

 メニューバーの処理に関しては、アプリケーションの終了だけをフックしました。メニューの終了は、quit ボタンのクリック処理する関数をしようしました。

ここで ボタン2 をクリックするとメニューが非表示、さらにクリックするとメニューバーが表示されます。

f:id:retireSaki:20181015134322p:plain
左側がメニュー非表示
右側がメニュー表示

 修正したソースは、MainWindow.cc と MainWindow.h だけです。

 今回は先にソースから

作成した MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <gtkmm.h>
#include <glibmm.h>
#include <giomm.h>
#include <fstream>
#include <iostream>
#include <string>

#include <glib.h>

#define CONF_FILE "application.conf"

class MainWindow : public Gtk::ApplicationWindow {
public:
    MainWindow(BaseObjectType* obj, Glib::RefPtr<Gtk::Builder> const& m_builder_)
        : Gtk::ApplicationWindow(obj)
        , m_builder{m_builder_}
    {
        setInit_display();
    }

    virtual ~MainWindow() = default;

protected:

    virtual void on_btn1_clicked();
    virtual void on_btn2_clicked();

    Gtk::MenuBar *m_menu = nullptr;
    Gtk::MenuItem *m_menuitem_quit = nullptr;

    Gtk::Button *m_btn1 = nullptr;
    Gtk::Button *m_btn2 = nullptr;
    Gtk::Paned  *m_paned1 = nullptr;
    Gtk::Paned  *m_paned2 = nullptr;

private:
    Glib::RefPtr<Gtk::Builder> m_builder;

    gchar *cstrInitPathFile;

    int nConf_MainWindow_posx;
    int nConf_MainWindow_posy;
    int nConf_MainWindow_width;
    int nConf_MainWindow_height;
    int nConf_Paned1_position;
    int nConf_Paned2_position;

    gchar * create_InitPath();
    void load_InitDatas();
    void save_InitDatas();

    void setInit_display();
    void on_hide_window()
    {
        save_InitDatas();
        std::cout << "hide" << std::endl;
    }
};

#endif

作成した MainWindow.cc

#include "MainWindow.h"

using namespace std;

extern Glib::RefPtr<Gtk::Builder> refBuilder;

void MainWindow::setInit_display()
{
    cstrInitPathFile = create_InitPath();
    load_InitDatas();

    // get menu widget
    refBuilder->get_widget("menuBar", m_menu);
    refBuilder->get_widget("menu_quit", m_menuitem_quit);

    // get widget
    refBuilder->get_widget("btn1", m_btn1);
    refBuilder->get_widget("btn2", m_btn2);
    refBuilder->get_widget("paned1", m_paned1);
    refBuilder->get_widget("paned2", m_paned2);

    // connect signals

    // menu signals
    m_menuitem_quit->signal_activate().connect(
        sigc::mem_fun(*this, &MainWindow::on_btn1_clicked));


    // button signals
    m_btn1->signal_clicked().connect(
        sigc::mem_fun(*this, &MainWindow::on_btn1_clicked));
    m_btn2->signal_clicked().connect(
        sigc::mem_fun(*this, &MainWindow::on_btn2_clicked));

    this->signal_hide().connect(
        sigc::mem_fun(*this, &MainWindow::on_hide_window));

    // set window titles & sizes
    set_title("test menu");
    if (nConf_MainWindow_posx != -1*G_MAXINT && nConf_MainWindow_posy != -1*G_MAXINT)
        move(nConf_MainWindow_posx, nConf_MainWindow_posy);;
    set_default_size(nConf_MainWindow_width, nConf_MainWindow_height);
    //resize(nConf_MainWindow_width, nConf_MainWindow_height);

    // set paned position
    m_paned1->set_position(nConf_Paned1_position);
    m_paned2->set_position(nConf_Paned2_position);

}

/*************************************************
    ini functions
*************************************************/

// create ini-file pathfilename
gchar * MainWindow::create_InitPath()
{
    cstrInitPathFile = NULL;
    //g_autoptr(GError) error = NULL;
    GError *error = nullptr;

    // get application name
    const gchar *app_name;
    app_name = g_get_application_name();
    if (!app_name)
        app_name = g_get_prgname();

    // get config directory
    const gchar *user_config_dir = g_get_user_config_dir();
    gchar *pathfile = g_build_filename(user_config_dir, app_name, CONF_FILE, NULL);
    if (g_file_test(pathfile, G_FILE_TEST_EXISTS)) {
        return pathfile;
    }

    // check exists derectory
    if (!g_file_test(g_build_filename(user_config_dir, app_name, NULL), G_FILE_TEST_EXISTS)) {
        // create directory
        GFile *file = g_file_new_build_filename(user_config_dir, app_name,NULL);
        if (!g_file_make_directory(file, NULL, &error)){
            std::cout << "Error make application config directory :"
                      << error->message << std::endl;            
            g_clear_error(&error);
            return NULL;
        }
    }

    return pathfile;
}

// load ini-file
void MainWindow::load_InitDatas()
{
    // defalts
    nConf_MainWindow_posx=-1*G_MAXINT;
    nConf_MainWindow_posy=-1*G_MAXINT;
    nConf_MainWindow_width = 400;
    nConf_MainWindow_height = 300;
    nConf_Paned1_position = 150;
    nConf_Paned2_position = 50;

    if (cstrInitPathFile == NULL)
        return;

    // exists check key file
    if (!g_file_test(cstrInitPathFile, G_FILE_TEST_EXISTS))
        return;

    //g_autoptr(GError) error = NULL;
    GError *error = nullptr;
    g_autoptr(GKeyFile) key_file = g_key_file_new ();

    if (!g_key_file_load_from_file (key_file,
            cstrInitPathFile,
            G_KEY_FILE_KEEP_COMMENTS,
            &error)) {
        if (!g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
            std::cout << "Error loading key file :"
                      << error->message << std::endl;            
        else
            std::cout << "Error loading key file :"
                      << error->code << std::endl;            
        g_clear_error(&error);
        g_key_file_free(key_file);
        return;
    }

    int nvalue;

    // main window
    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "window_posx", &error);
    if (error == NULL)
        nConf_MainWindow_posx = nvalue;
    else
        g_clear_error(&error);

    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "window_posy", &error);
    if (error == NULL)
        nConf_MainWindow_posy = nvalue;
    else
        g_clear_error(&error);

    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "window_width", &error);
    if (error == NULL)
        nConf_MainWindow_width = nvalue;
    else
        g_clear_error(&error);

    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "window_height", &error);
    if (error == NULL)
        nConf_MainWindow_height = nvalue;
    else
        g_clear_error(&error);

    // paned1
    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "paned1_divider_position", &error);
    if (error == NULL)
        nConf_Paned1_position = nvalue;
    else
        g_clear_error(&error);

    // paned2
    error = NULL;
    nvalue = g_key_file_get_integer(key_file, "MAINWINDOW", "paned2_divider_position", &error);
    if (error == NULL)
        nConf_Paned2_position = nvalue;
    else
        g_clear_error(&error);

    g_key_file_free(key_file);
}

void MainWindow::save_InitDatas()
{
    if (cstrInitPathFile == NULL)
        return;

    // get application position & size
    Gdk::Rectangle rect;
    get_window()->get_frame_extents(rect);
    

    int int_val_mw_posx = rect.get_x();
    int int_val_mw_posy = rect.get_y();
    int int_val_mw_width = 0;
    int int_val_mw_height = 0;
    get_size(int_val_mw_width, int_val_mw_height);

    // get paneds position
    gint int_val_paned1 = m_paned1->get_position();
    gint int_val_paned2 = m_paned2->get_position();

    // set key values
    GError *error = nullptr;
    //g_autoptr(GError) error = NULL;
    g_autoptr(GKeyFile) key_file = g_key_file_new ();

    g_key_file_set_integer(key_file, "MAINWINDOW", "window_posx", int_val_mw_posx);
    g_key_file_set_integer(key_file, "MAINWINDOW", "window_posy", int_val_mw_posy);

    g_key_file_set_integer(key_file, "MAINWINDOW", "window_width", int_val_mw_width);
    g_key_file_set_integer(key_file, "MAINWINDOW", "window_height", int_val_mw_height);

    g_key_file_set_integer(key_file, "MAINWINDOW", "paned1_divider_position", int_val_paned1);
    g_key_file_set_integer(key_file, "MAINWINDOW", "paned2_divider_position", int_val_paned2);

    // write key file
    if (!g_key_file_save_to_file(key_file, cstrInitPathFile, &error)){
        std::cout << "Error saving key file :"
                  << error->message << std::endl;            
        g_clear_error(&error);
        return;
    }

    g_key_file_free(key_file);
}

/*************************************************
    events
*************************************************/

// btn1(close) event
void MainWindow::on_btn1_clicked()
{
    // confirm close
    Gtk::MessageDialog dialog(*this, "close this window ?", false,
                           Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_OK_CANCEL);
    dialog.set_title( "close this window" );
    dialog.set_default_response(Gtk::RESPONSE_OK);
    if (dialog.run()==Gtk::RESPONSE_CANCEL)
        return;

    hide();
}

void MainWindow::on_btn2_clicked()
{
    if (m_menu->is_visible()){
        m_menu->hide();
        m_btn2->set_label("click to show menu-bar");
    }
    else{
        m_menu->show();
        m_btn2->set_label("click to hide menu-bar");
    }
}

1) メニューバーの表示

 メニューバーは glade で追加します。
コンテナーの GtkMenuBar を追加しますが、GtkBoxを先に設置してから GtkMenuBar を貼り付けます。
今回は、menuBar という名称にしました。

 さらに メニューバーのファイルから終了を選択し、menu_quit と名称に。

 単にメニューバーを表示するだけなら glade の編集だけですみますが、これだけでは意味がありません。
そこで、終了のイベントをフックします。

2) メニューバーの終了のイベントをフック

 まず、
   refBuilder->get_widget()
を使って、menuBar および menu_quit を Gtk::Builder から取得。

 メニューのイベントは
   signal_activate()
を使います。

 menu_quitメンバーから signal_activate を使い、btn1 のクリック処理関数を呼び出します。
   m_menuitem_quit->signal_activate().connect(
     sigc::mem_fun(*this, &MainWindow::on_btn1_clicked));
これで終了のイベントが呼び出せるようになります。

3) メニューバーの表示・非表示できるようにする

 btn2 のクリックイベントの処理を定義します。
   m_btn2->signal_clicked().connect(
     sigc::mem_fun(*this, &MainWindow::on_btn2_clicked));

 次に on_btn2_clicked 内の処理を作成。

 (3.1) メニューバーが visible なら

   menuBar を hide() 関数を使って非表示にし、
  判りやすいように、btn2 のラベルを "click to show menu-bar" に。

 (3.2) メニューバーが visible でないなら

   menuBar を show() 関数を使って表示し、
  判りやすいように、btn2 のラベルを "click to hide menu-bar" に。


以上で、今回の機能追加はできました。


最後に、全てのファイルを圧縮したファイル(zip)を載せます。

ソース一式

すべてのファイル(zip)をダウンロードする
www.mediafire.com


 簡単でしたね!
基本的に Qt とさほど変わりません。
glade の作業で気づかれた方も多いと思いますが、デザイン上Qtとはメニューバーの取り扱い方が若干違っていますので、気をつけてください。

Gtk+3入門

Gtk+3入門

注目記事

「Amazon.co.jpアソシエイト」または「[乙の名称を挿入]は、Amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイトプログラムである、Amazonアソシエイト・プログラムの参加者です。