retireSakiの日記

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

GTKmm (3.0) と glade によるアプリ (1) - paned Widget

 今回は g++ 7.3.0 + GTKmm 3.0 + glade 3.22.1 を使って、gtkmm が提供するUIの1つである paned の表示と設定を行ってみたいと思います。
paned のサンプルが少なかったので、今回このUIにしました。

f:id:retireSaki:20181002150853j:plain

 paned は区切られたパネルの幅または高さを可変させるWidgetです。
ファイルやメーラーなどよく使われているWidgetですが、通常のアプリだと左にツリーテーブル、右に詳細表示などという場合に多く使われています。

 gtkmmGTK+C++インターフェース郡です。使いたいgtkmmバージョンに対応したGTK+,C++のそれぞれのバージョンが必要となります。

・ソースについて
 今回、main関数 と メインウィンドウクラスにソースファイルを分割するので、ヘッダーを含めソースファイルは3ファイルとなります。.glade、makefileも含めると計5ファイルです。

 作成途中のコードが混入してと思いますが、適当に無視してください。

 この記事の下で各種ファイルを圧縮したZIPファイルがダウンロードできます。



(1)アプリの概要

 このアプリはよくあるアプリの分割方法に似せ、最初の画像のように、左右分割、左側を上下に分割しています。
各パネルには、ボタンを配置しています。
左上の quit ボタンをクリックすると確認ダイアログを表示。OKを選択すると、アプリを終了します。キャンセルの場合ダイアログを閉じます。

f:id:retireSaki:20181002151040j:plain


(2)glade による UIデザインの作成 (paned_test.glade)

f:id:retireSaki:20181002151156j:plain


 新規作成のとき、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)

注目記事

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