GTKmm (3.0) と glade によるアプリ (3) - config データの取得/保存 と 最終位置の保存/表示
今回は、前回の"GTKmm (3.0) と glade によるアプリ (2) - アプリケーションの多重起動の防止"で作成したプログラム
retiresaki.hatenablog.com
をベースに、
(1) config(ini)データの取得と保存機能
(2) アプリの最終ポジションの保存と起動時にその位置で表示
の2つの機能を付けます。
まずは、実際に動作させたときの画像から
・左側がconfigデータが無いとき
・右側がconfigデータが存在するとき
のそれぞれの画像です。
アプリの位置だけでなく、各panedもポジションも保存しています。
アプリの位置に関しては、(現時点のGTKバージョンで) メインウィンドウではなくタイトルバー、フレームサイズも含めたアプリケーションの位置を使っています。
修正したソースは、MainWindow.cc と MainWindow.h だけです。
今回は先にソースから
作成した MainWindow.cc
#include "MainWindow.h" using namespace std; extern Glib::RefPtr<Gtk::Builder> refBuilder; void MainWindow::setInit_display() { cstrInitPathFile = create_InitPath(); load_InitDatas(); // get widget refBuilder->get_widget("btn1", m_btn1); refBuilder->get_widget("paned1", m_paned1); refBuilder->get_widget("paned2", m_paned2); // connect signals m_btn1->signal_clicked().connect( sigc::mem_fun(*this, &MainWindow::on_btn1_clicked)); this->signal_hide().connect( sigc::mem_fun(*this, &MainWindow::on_hide_window)); // set window titles & sizes set_title("test paned"); 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); // 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(); }
1) config(ini) ファイル
1.1) config(ini) ファイルについて
config(ini) ファイルを扱う前に、保存先のディレクトリーを確認し、存在していないときは作る必要があります。
これは、
gchar * MainWindow::create_InitPath()
内で処理しています。この関数はメインウィンドウ生成時で、config(ini) ファイルを読み込む前に行う必要があります。
g_get_user_config_dir() を使ってconfigデータディレクトリーを取得します。
( ubuntu であれば、"/home/<user name>/.config" )
g_get_application_name() で得たアプリ名を使い、g_build_filename で各パスを1つのパスとして合成します。
結果、
"/home/<user name>/.config/<application name>"
というパス文字列ができました。
これを使って
(1) g_file_test() で存在チェック。あれば以下の処理を継続
(2) g_file_new_build_filename() で先のパスの GFile* を作成
(3) g_file_make_directory() でディレクトリーを作成
という手順で行っています。
1.2) メインウィンドウ生成時に config(ini) データを読み込む
今回、config(ini)ファイル名は MainWindow.h 内で、以下のように定義しました。
#define CONF_FILE "application.conf"
config(ini)の読み込み処理は、
void MainWindow::load_InitDatas()
で行っています。
基本的に通常のファイル処理と同様に
(1) config(ini) ファイルの存在チェック
(2) g_key_file_load_from_file config(ini) ファイルの読み込み
(3) g_key_file_get_integer() などを使って、指定グループ、キーから該当する値を取得
(4) config(ini) ファイルの開放
です。
ソースコードと言語リファレンスを見れば、すぐに分かると思いますので詳細は省略します。
1.3) on_hide_window イベント時に config(ini) データを保存
config(ini)の保存処理は、
void MainWindow::save_InitDatas()
で行っています。
アプリケーション終了(メインウィンドウ開放)時に保存したいので、on_hide_window イベント時に save_InitDatas() をcallします。
保存処理は
(1) g_key_file_new() で GKeyFile を作成
(2) g_key_file_set_integer() などを使って、指定グループ、キーと該当する値をセット
(3) g_key_file_save_to_file() で config(ini) データを保存
(4) config(ini) ファイルの開放
です。
これも、ソースコードと言語リファレンスを見れば、すぐに分かると思いますので詳細は省略します。
2) アプリケーション位置の保存と表示
2.1) アプリケーション位置の保存
アプリケーション位置の保存は、save_InitDatas() で行っています。
Qtでもそうですが、普通に position() 関数を使うと、アプリケーションの位置ではなく、メインウィンドウの位置を取得してしまいます。
必要なのはアプリケーション(ルートウィンドウ)の位置で、メインウィンドウから見ると、タイトルバー、フレームサイズなどを差し引いた左の値となります。
そこで、
get_frame_extents();
を使います。
引数は、Gdk::Rectangle を渡すことで、アプリケーションの左、上、幅、高さを取得できます。
この関数は、タイトルバー/ボーダー(存在する場合)を含むウィンドウのバウンディングボックスのサイズを取得します。なので、
get_size();
でサイズを取得します。
あとは、その値を保存するだけです。
2.2) アプリケーションを最終保存位置で表示
アプリケーション位置の取得は、load_InitDatas() で行い、その後
move()
で最終位置にアプリケーションを移動させます。
最後に、全てのファイルを圧縮したファイル(zip)を載せます。
ソース一式
すべてのファイル(zip)をダウンロードするwww.mediafire.com
簡単でしたね!
何が大変かというと、使用する関数を調べることです。ある程度日本語されているといっても、情報量が多いのは英語のサイトです。
当ブログのように、最新版でいろいろな情報を発信することで、最終的に日本語の情報量も増えていきます。
皆さん、頑張ってくださいね。
- 作者: ToshioCP
- 発売日: 2017/06/06
- メディア: Kindle版
- この商品を含むブログを見る