retireSakiの日記

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

GTKmm (3.0) と glade によるアプリ (5) - アプリケーションのアイコンをセット

 今回は、アプリケーションらしく下のアイコンをセットみます。

f:id:retireSaki:20181019104602g:plain

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

retiresaki.hatenablog.com


f:id:retireSaki:20181019101249p:plain
左 : アイコン未設定
右 : アイコンを設定

アイコンをセットするには、glade で指定する方法とアプリ内で設定する方法があります。

■ glade で指定する方法

 これは一番単純な方法です。

f:id:retireSaki:20181019101510j:plain

画像のように、メインウィンドウのアイコンファイルを指定するだけです。

設定した paned_test.glade

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkApplicationWindow" id="mainWindow">
    <property name="name">mainWindow</property>
    <property name="can_focus">False</property>
    <property name="icon">linux.png</property>
    <child>
      <placeholder/>
    </child>
    <child>
      <object class="GtkBox">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkMenuBar" id="menuBar">
            <property name="name">menuBar</property>
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">ファイル(_F)</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-new</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-open</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-save-as</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkSeparatorMenuItem">
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem" id="menu_quit">
                        <property name="label">gtk-quit</property>
                        <property name="name">menu_quit</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">編集(_E)</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-cut</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-copy</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-paste</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-delete</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">表示(_V)</property>
                <property name="use_underline">True</property>
              </object>
            </child>
            <child>
              <object class="GtkMenuItem">
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="label" translatable="yes">ヘルプ(_H)</property>
                <property name="use_underline">True</property>
                <child type="submenu">
                  <object class="GtkMenu">
                    <property name="visible">True</property>
                    <property name="can_focus">False</property>
                    <child>
                      <object class="GtkImageMenuItem">
                        <property name="label">gtk-about</property>
                        <property name="visible">True</property>
                        <property name="can_focus">False</property>
                        <property name="use_underline">True</property>
                        <property name="use_stock">True</property>
                      </object>
                    </child>
                  </object>
                </child>
              </object>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">0</property>
          </packing>
        </child>
        <child>
          <object class="GtkPaned" id="paned1">
            <property name="name">paned1</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <child>
              <object class="GtkPaned" id="paned2">
                <property name="name">paned2</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="orientation">vertical</property>
                <child>
                  <object class="GtkButton" id="btn1">
                    <property name="label" translatable="yes">quit</property>
                    <property name="name">btn1</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                  </object>
                  <packing>
                    <property name="resize">False</property>
                    <property name="shrink">True</property>
                  </packing>
                </child>
                <child>
                  <object class="GtkButton" id="btn2">
                    <property name="label" translatable="yes">2</property>
                    <property name="name">btn2</property>
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="receives_default">True</property>
                  </object>
                  <packing>
                    <property name="resize">True</property>
                    <property name="shrink">True</property>
                  </packing>
                </child>
              </object>
              <packing>
                <property name="resize">True</property>
                <property name="shrink">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkButton" id="btn3">
                <property name="label" translatable="yes">3</property>
                <property name="name">btn3</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
              </object>
              <packing>
                <property name="resize">True</property>
                <property name="shrink">True</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">True</property>
            <property name="fill">True</property>
            <property name="position">1</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>  

 <property name="icon">linux.png</property>
の部分です。

これだけです。

■ アプリ内で設定する方法

 やることは実行しているカレントディレクト内、もしくは一般的に保存されるアイコンディレクト内にアイコンファイルがあるかどうか調べて、アプリにアイコンをセットします。

 linuxにおける一般的に保存されるアイコンディレクトは、$XDG_DATA_DIRS/icons や $HOME内のicons などを使うなど指します。ひsつ様に応じて 追加したり、他のディレクトリーを追加します。これはインストーラーでアプリケーションアイコンをどこに保存するかによって決定しますが、インストラーを指定しない場合などは直接してしても良いでしょう。

 今回は、実行カレントディレクトリー, /usr/local/share/icons , /usr/share/icons から探してセットすることとしますので、実行カレントディレクトリー, $XDG_DATA_DIRS を使います。
また本来必要ないのですが、アイコンファイル名は、指定したオリジナルものと、アプリケーション名の2通りのケースを想定し、オリジナルのアイコンがない場合、アプリ名のアイコンを使うこととし、アイコンは PNG 形式とします。

 では、ソースから

設定した 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);

    // set application icon
    set_AppIcon();

}

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

// set application icon
void MainWindow::set_AppIcon()
{
    std::string appname = Glib::get_application_name();

    if ( Glib::file_test( ORG_APPICON, Glib::FILE_TEST_EXISTS )) {
        this->set_icon_from_file( ORG_APPICON );
        return;
    }
    if ( Glib::file_test( appname+".png", Glib::FILE_TEST_EXISTS )) {
        this->set_icon_from_file( appname+".png" );
        return;
    }

    std::vector<std::string> list_dirs = Glib::get_system_data_dirs();
    for (string dir : list_dirs){

        for (int ni=0; ni < 2; ni++){
            string choice_file;
            if (ni==0)  // set original name
                choice_file = Glib::build_filename(	dir, "icons", ORG_APPICON);
            else        // set applcation name
                choice_file = Glib::build_filename(	dir, "icons", appname+".png" );
            if ( Glib::file_test( choice_file, Glib::FILE_TEST_EXISTS )) {
                this->set_icon_from_file( choice_file );
                break;
            }
        }

    }
}

// 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");
    }
}

 オリジナルアイコンファイル名は、mainWindow.h 内で
   #define ORG_APPICON "linux.png"
と定義しています。

 アイコンを探し、セットしている関数は
   void MainWindow::set_AppIcon()
です。

 最初に、実行カレントディレクトリー内からファイルを探し、存在しているときは
  this->set_icon_from_file( const std::string & filename );
でメインウィンドウに見つけたファイルをセットします。
この関数は、指定したファイル名filenameからイメージをロードして生成されたpixbufで Gtk::Window::set_icon ( const Glib::RefPtr< Gdk::Pixbuf >& icon ) を呼び出すのと同様です。

 ここで念の為 PNG 以外のイメージフォーマットに対応しているか確認してみます。
用意したファイルは、GIF, JPG, SVG を用意しました。

f:id:retireSaki:20181019104730p:plain
元画像となったPNGファイル
GIFなどの画像は、下のプログラム一式のZIPに同梱

結果、いずれも問題なく表示できました。

 実行カレントディレクトリー内にアイコンがファイルが存在していない場合、
  (1) $XDG_DATA_DIRS つまり Glib::get_system_data_dirs(); で取得したディレクトリーリスト取得
  (2) Glib::build_filenam() でファイルパスを作成
  (3) 存在チェックで存在していれば this->set_icon_from_file(); でアイコンをセット。
という手順でアイコンをセットします。


 以上です。
このあたりは、Qt のほうが楽ですね。

 サポートされるアイコンのイメージフォマットは、OSやLinuxディストリビューションのバージョンによっても違ってくるので、インストールされるであろうバージョンを想定したイメージフォマットを使うと良いでしょう。


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

ソース一式

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


 GTK+, Qt のちょっと読んでみたい、こんな入門書はいかがでしょうか?

Qtで簡単 GUIプログラミング―Qt入門書を読む前に読む「入門書」

Qtで簡単 GUIプログラミング―Qt入門書を読む前に読む「入門書」

GTK+とGladeで作るLinuxプログラミング超入門―かっこいいアプリを自分で作ろう! (CompuBooks)

GTK+とGladeで作るLinuxプログラミング超入門―かっこいいアプリを自分で作ろう! (CompuBooks)

GTK+入門―基礎からはじめるXプログラミング

GTK+入門―基礎からはじめるXプログラミング

注目記事

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