GTKmm (3.0) と glade によるアプリ (1) - paned Widget
今回は g++ 7.3.0 + GTKmm 3.0 + glade 3.22.1 を使って、gtkmm が提供するUIの1つである paned の表示と設定を行ってみたいと思います。
paned のサンプルが少なかったので、今回このUIにしました。
paned は区切られたパネルの幅または高さを可変させるWidgetです。
ファイルやメーラーなどよく使われているWidgetですが、通常のアプリだと左にツリーテーブル、右に詳細表示などという場合に多く使われています。
gtkmm はGTK+のC++インターフェース郡です。使いたいgtkmmバージョンに対応したGTK+,C++のそれぞれのバージョンが必要となります。
・ソースについて
今回、main関数 と メインウィンドウクラスにソースファイルを分割するので、ヘッダーを含めソースファイルは3ファイルとなります。.glade、makefileも含めると計5ファイルです。
作成途中のコードが混入してと思いますが、適当に無視してください。
この記事の下で各種ファイルを圧縮したZIPファイルがダウンロードできます。
(1)アプリの概要
このアプリはよくあるアプリの分割方法に似せ、最初の画像のように、左右分割、左側を上下に分割しています。
各パネルには、ボタンを配置しています。
左上の quit ボタンをクリックすると確認ダイアログを表示。OKを選択すると、アプリを終了します。キャンセルの場合ダイアログを閉じます。
(2)glade による UIデザインの作成 (paned_test.glade)
新規作成のとき、gtkmmのバージョンを問われますので、3.0の最新バージョンを選択。
最初に、ベースとなるWidgetとして GtkApplicationWindowを配置。
次に各panedを配置し、各panedにボタンを配置。
最後に各widgetの id, name を設定します。
<paned_test.glade に移動>
(3)mainファイルの作成 (main.cc)
最初にcallされる main関数のソースファイルです。
主に、以下の3つを行っています。
1) 指定したgladeファイルの解析。
2) mainWindowの派生クラスによって実装されているwidgetへのポインタを取得。
3) widgetへのポインタで提供される派生クラスを実行します。
今回は、main.ccのような方法で実現していますが他にも方法があります。クラスの有無やglade有無などで変わるので、それぞれにあったやり方で実現します。たぶん、一番最初にハマる部分だと思います。
<main.cc に移動>
(4)MainWindowのヘッダーファイルの作成 (MainWindow.h)
MainWindowクラスを定義したヘッダーファイルです。
Gtk::ApplicationWindow からMainWindowクラスを派生するようにします。
・gladeで定義したwidgetで実際に利用するwidgetを定義。
Gtk::Button *m_btn1;
Gtk::Paned *m_paned1;
Gtk::Paned *m_paned2;
・btn1のクリックシグナルに呼応する関数を定義。
virtual void on_btn1_clicked();
・MeinWindow終了時に呼び出される関数を定義。
void on_hide_window();
<MainWindow.h に移動>
(5)MainWindowのC++ファイルの作成 (MainWindow.cc)
MainWindowクラスの関数内の処理を記述したソースファイルです。
・setInit_display 関数 setInit_display()
メインウィンドウ生成時に呼び出される関数。
以下の4つを行っています。
gladeで定義したwidgetで実際に利用するwidgetを抽出。
使用するシグナルの設定。
ウィンドウ全体の設定。
各panedの分割サイズを設定。
・MainWindow終了時に呼び出される関数 on_hide_window()
hide()イベントに呼応する関数です。
・quitクリック時に呼び出される関数 on_btn1_clicked()
終了確認時、CANCELボタン以外(OKボタン)をクリックしたとき、MainWindowをhide (終了) する。
<MainWindow.cc に移動>
こんな本を読み合わせて、作ってみるのもありかも。
・Gtk+3入門
・GTK+とGladeで作るLinuxプログラミング超入門―かっこいいアプリを自分で作ろう! (CompuBooks)
ソース一式
すべてのファイル(zip)をダウンロードする
www.mediafire.com
作成された 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> <child> <placeholder/> </child> <child> <object class="GtkPaned" id="paned1"> <property name="name">paned1</property> <property name="visible">True</property> <property name="can_focus">True</property> <property name="position">10</property> <property name="position_set">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> <property name="position">10</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> <signal name="clicked" handler="on_btn1_clicked" swapped="no"/> </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">False</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> </child> </object> </interface>
作成した main.cc
#include "MainWindow.h" Glib::RefPtr<Gtk::Builder> refBuilder; /******************************************************************************* main *******************************************************************************/ int main(int argc, char* argv[]) { Gtk::Main kit(&argc, &argv); try { refBuilder=Gtk::Builder::create_from_file("paned_test.glade"); } catch(const Glib::FileError& ex) { std::cerr << "FileError: " << ex.what() << std::endl; return 1; } catch(const Glib::MarkupError& ex) { std::cerr << "MarkupError: " << ex.what() << std::endl; return 1; } catch(const Gtk::BuilderError& ex) { std::cerr << "BuilderError: " << ex.what() << std::endl; return 1; } MainWindow* m_main = nullptr; refBuilder->get_widget_derived("mainWindow", m_main); if (m_main){ kit.run(*m_main); delete m_main; return 0; } std::cerr << "mainWindow load failed\n" << std::endl; delete m_main; return 1; }
作成した MainWindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <gtkmm.h> #include <glibmm.h> #include <giomm.h> #include <fstream> #include <iostream> #include <string> 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(); Gtk::Button *m_btn1 = nullptr; Gtk::Paned *m_paned1 = nullptr; Gtk::Paned *m_paned2 = nullptr; private: Glib::RefPtr<Gtk::Builder> builder; void setInit_display(); void on_hide_window(); }; #endif
作成した MainWindow.cc
#include "MainWindow.h" extern Glib::RefPtr<Gtk::Builder> refBuilder; void MainWindow::setInit_display() { // 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"); set_default_size(400, 300); // set paned positio m_paned1->set_position(150); m_paned2->set_position(50); } /************************************************* events *************************************************/ // close event void MainWindow::on_hide_window() { std::cout << "hide" <<std::endl; } // 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(); }
作成した makefile
CXXFLAGS = -o -Wall -Werror `pkg-config gtkmm-3.0 --cflags` LDLIBS = -lpthread `pkg-config gtkmm-3.0 --libs` INCLUDE = . PROG = paned_test SRCS = main.cc MainWindow.cc OBJS = main.o MainWindow.o $(PROG): $(OBJS) $(CXX) -o $(PROG) $(OBJS) $(LDLIBS) -include deps.mak deps: $(CXX) -MM $(SRCS) > deps.mak clean: $(RM) $(OBJS) $(PROG)