GTKmm (3.0) と glade によるアプリ (9) - 子ウィンドウとインターバルタイマー
今回は、前回の" GTKmm (3.0) と glade によるアプリ (8) - ダイアログのモダールとモードレス表示 "で作成したプログラムを使って、子ウィンドウを作成し、子ウィンドウ内でインターバルタイマーの起動と停止してみたいと思います。
最終的にこんな感じですね。
ソースコードは最後に両方の機能を含めたものを公開。(zipダウンロードできます)
■ 子ウィンドウの作成
基本的に、aboutダイアログで作成した方法と同じです。
1) glade で子ウィンドウをデザインします。
タイマー表示に使うラベル。
その下はボタングループがあります。
ボタングループは、左からタイマーのON/OFFボタン、何もしないボタン、子ウィンドウを閉じるボタン
※ 詳細はgladeファイルを確認してください。
2) 子ウィンドウクラス Child_Window の作成 (Child_Window.cc, Child_Window.h)
glade で作成したウィンドウは GtkWindow をベースにしたので、Gtk::Window から Child_Window クラスを派生。
詳細は、ソースコードを見たほうが早いと思います。
3) メインウィンドウクラスの修正
(1) glade ファイルの修正 (paned_test.glade)
今回は、ボタン4を使いますので、ボタン4のラベルを "child window" に変更。
(2) メインウィンドウ のソースファイルとヘッダーファイルを修正 (MainWindowcc , MainWindow.h)
ボタン4に関するメンバーの取得とイベント処理の追加
● MainWindow.h
… protected: … virtual void on_child_window(); … Gtk::Button *m_btn4 = nullptr; …
● MainWindow.cc
… void MainWindow::setInit_display() { … refBuilder->get_widget("btn4", m_btn4); … m_btn4->signal_clicked().connect( sigc::mem_fun(*this, &MainWindow::on_child_window)); … } … void MainWindow::on_child_window() { Child_Window *child_window = nullptr; refBuilder->get_widget_derived("childWindow", child_window); child_window->set_transient_for(*this); child_window->show(); }
ボタンのクリック処理関連は、今まで通りなので説明は不要でしょう。
今回追加した子ウィンドウの所持関数が、on_child_window() です。
やってることは、aboutダイアログのときと同じです。
■ インターバルタイマーを使う
タイマー関数にはいくつかありますが、今回はインターバルタイマー関数を使います。
sigc::connection Glib::SignalTimeout::connect (
const sigc::slot< bool > & slot,
unsigned int interval,
int priority = PRIORITY_DEFAULT
)
slot は、タイマーイベントを受取る関数のアドレス
interval は、インターバル時間をミリ秒単位で指定
priority は、プライオリティですが、とくに変更するひsつようがなければ未指定で構いません
connect の代わりに connect_once を使うと、ワンショットタイマーになります。
タイマーイベントを受取る関数で false を返すとワンショットタイマーとタイマーと同等の機能を実現できます。
connect の代わりに connect_seconds を使うと、インターバル時間を秒単位で指定することができ、長時間のインターバル処理を実現できます。
connect の代わりに connect_seconds_once を使うと、長時間のワンショットタイマーになります。
注意点としては、
インターバルタイマーのときは、イベント処理の関数は bool で戻します。true なら継続、false なら中止です。
ワンショットrタイマーのときは、イベント処理の関数の戻り値ありません。void です。
● Child_Window.h
… public: … virtual ~Child_Window() { if ( !m_timers.empty() ) m_timers[m_timers.begin()->first].disconnect(); } protected: Gtk::Label *m_labeldisp1 = nullptr; Gtk::Button *m_btntimer = nullptr; … std::map<int, sigc::connection> m_timers; private: unsigned int untimer_interval = 200; // interval (msec) long ntimer_cnt = 0; // loop cnt … void on_btntimer(); bool on_timeout(); …
● Child_Window.cc
… void Child_Window::setInit_display() { … refBuilder->get_widget("btn_timer", m_btntimer); … m_btntimer->signal_clicked().connect( sigc::mem_fun(*this, &Child_Window::on_btntimer)); … m_btntimer->set_label("start timer"); } … void Child_Window::on_btntimer() { if ( m_timers.empty() ){ // start timer ntimer_cnt = 0; sigc::connection conn = Glib::signal_timeout().connect( sigc::mem_fun(*this, &Child_Window::on_timeout), untimer_interval); m_timers[untimer_interval] = conn; m_btntimer->set_label("stop timer"); } else{ // end timer m_timers[m_timers.begin()->first].disconnect(); m_timers.clear(); m_btntimer->set_label("start timer"); } } bool Child_Window::on_timeout() { // display ntimer_cnt++; stringstream sstr; sstr << ntimer_cnt; string str = sstr.str(); m_labeldisp1->set_text( str ); // timer true = continuation , false = stop return true; }
Child_Window.ccは、ほぼ全てのコードがタイマー処理関連ですので、タイマー処理がなければ、ヘッダーファイルにまとめることができます。
btn_timer に関する部分の説明は不要でしょう。
作成したタイマーは、
std::map
に保存しています。
これは、m_timers が empty か確認することで、タイマーが稼働中かどうか判断でき、終了時にタイマーが残っていたら削除します。
void Child_Window::on_btntimer()
はタイマー開始/停止ボタンの処理関数です。
m_timers が empty であれば
Glib::signal_timeout().connect で、on_timeout() をタイマーイベント関数として、インターバルタイマーを起動。
タイマーボタンのラベルを "stop timer" に変更。
empty でなければ
タイマーを停止。
タイマーボタンのラベルを "start timer" に変更。
bool Child_Window::on_timeout()
は、タイマーイベントで呼び出される関数です。
ntimer_cnt をカウントアップして、label_disp1 に表示しています。
戻り値を true にすることで、インターバルタイマは継続し、false で中止します。
以上です。
インターバルタイマは見た目、面倒くさい感じもしますが意外に簡単に実装できます。
最後に、全てのファイルを圧縮したファイル(zip)を載せます。
●ソース一式のダウンロード