Yamamoto's Laboratory
 
Version 3
タイマー
 
Version 4
 
 
コンピューター Qt タイマー

QtタイマーVersion 3

Qtとはノルウェーの TROLLTECH社が開発しているGUIを作るためのツールキットです.それを使って,ここではタイマーを作ります.

目次


はじめに

ここでは,簡単な例を示し,Qtの特徴であるシグナルとスロットについて簡単に説明してsする.また,ヘッダーファイルの書き方なども説明する.例としてはカップラーメンタイマーを作成する.

ここでのサンプルプログラムは「Qt GUIプログラミング」を参考にしている.

スピンボックスとスライダー

QtにはGUIのための様々なクラスが用意されている.その例として,QSpinBoxとQSliderを紹介する.これらは,QWidgetを継承している.継承しているということは,QWidgetと同じように扱え,さらに便利な機能が付いている.ということである.

さて,QSpinBoxとQSliderを表示させるようなプログラムを作ろうと思うが,このふたつの親になるメインのウィジェットも必要になる.ここでは,メインウィジェットをQHBoxにする. このQHBoxは水平方向に子となるウィジェットを並べてくれる便利なものである.(ちなみに垂直方向に並べるQVBoxもある) QHBoxもQWidgetを継承している.

それでは,プログラムを実際に作ってみよう.QHBoxを使うためにはqhbox.hをインクルードしなければいけない.同じように,QSpinBoxはqspinbox.hを,QSliderはqslider.hをインクルードしなければ使えない.以下に,プログラムを示す.

#include <qapplication.h>
#include <qhbox.h>
#include <qslider.h>
#include <qspinbox.h>

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);

  QHBox* hbox = new QHBox(0);
  hbox->setMargin(5); //マージンの設定
  hbox->setSpacing(5); //ウィジェット間のスペース

  QSpinBox* spinBox = new QSpinBox(hbox); //hboxを親とするスピンボックス
  QSlider* slider = new QSlider(Qt::Horizontal, hbox);
     //hboxを親とする水平方向のスライダー

  spinBox->setRange(0, 300); //スピンボックスの値の設定 0から300まで
  slider->setRange(0, 300); //スライダーの値の設定 0から300まで

  app.setMainWidget(hbox);
  hbox->show();

  return app.exec();
}

コンパイルは,以下のようにする.

$ qmake -project
$ qmake
$ make

とコンパイルすればアプリケーションができある.

さらに,これを改良してスピンボックスとスライダーの値を結びつけよう.つまり,スピンボックスで値を変えればスライダーの位置が変わり,スライダーの位置を変えればスピンボックスの値が変わるようにする.このためには,シグナルとスロットを使わなければいけない.

QSpinBoxにはvalueChanged(int)というシグナルがある.これはQSpinBoxの値が変わったときに発せられるシグナルであり,QSpinBoxの値も送られる.また,QSliderにはsetValue(int)というスロットがある.これは,QSliderの値を設定するスロットである.これらを結びつけるためには

QObject::connect(spinBox, SIGNAL(valueChanged(int)), slider, SLOT(setValue(int)));

とすればよい.これでspinBoxの値が変わったときにsliderの値が変わるようになる.同じように,sliderの値が変わったときにspinBoxの値が変わるようにするには,

QObject::connect(slider, SIGNAL(valueChanged(int)), spinBox, SLOT(setValue(int)));

とする.

これらを付け足したプログラムを以下に示す.

#include <qapplication.h>
#include <qhbox.h>
#include <qslider.h>
#include <qspinbox.h>

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);

  QHBox* hbox = new QHBox(0);
  hbox->setMargin(5); //マージンの設定
  hbox->setSpacing(5); //ウィジェット間のスペース

  QSpinBox* spinBox = new QSpinBox(hbox); //hboxを親とするスピンボックス
  QSlider* slider = new QSlider(Qt::Horizontal, hbox);
    //hboxを親とする水平方向のスライダー

  spinBox->setRange(0, 300); //スピンボックスの値の設定 0から300まで
  slider->setRange(0, 300); //スライダーの値の設定 0から300まで

    //スピンボックスとスライダーの値を結びつける
  QObject::connect(spinBox, SIGNAL(valueChanged(int)),
		   slider, SLOT(setValue(int)) );
  QObject::connect(slider, SIGNAL(valueChanged(int)),
		   spinBox, SLOT(setValue(int)) );

  app.setMainWidget(hbox);
  hbox->show();

  return app.exec();
}

クラスにまとめる

今作ったプログラムをクラスを使って書き直してみる.作成するクラスはTimerという名前にし,QVBoxを継承させる.するとこのプログラムは次のようになる.

#include <qapplication.h>
#include <qvbox.h>
#include <qhbox.h>
#include <qslider.h>
#include <qspinbox.h>

class Timer : public QVBox{
public:
  Timer(); //コンストラクタ

private:
  QHBox* hbox;
  QSpinBox *spinBox;
  QSlider *slider;
};

Timer::Timer()
{
  setCaption("Timer"); //このクラスにタイトルをつける
  setMargin(5); //このクラス自身のマージンの設定
  setSpacing(5); //このクラス自身のウィジェット間のスペース

  hbox = new QHBox(this); //このクラスを親に持つQHBox
  hbox->setSpacing(5); //ウィジェット間のスペース

  spinBox = new QSpinBox(hbox); //hboxを親とするスピンボックス
  slider = new QSlider(Qt::Horizontal, hbox);  //hboxを親とする水平方向のスライダー

  spinBox->setRange(0, 300); //スピンボックスの値の設定 0から300まで
  slider->setRange(0, 300); //スライダーの値の設定 0から300まで

  //スピンボックスとスライダーの値を結びつける
  QObject::connect(spinBox, SIGNAL(valueChanged(int)),
		   slider, SLOT(setValue(int)) );
  QObject::connect(slider, SIGNAL(valueChanged(int)),
		   spinBox, SLOT(setValue(int)) );

}

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);

  Timer* timer = new Timer();

  app.setMainWidget(timer);
  timer->show();

  return app.exec();

}

このままでもプログラムは動くが,普通はプログラムを一つのファイルに書くのではなく,複数のファイルに分割して書く.一つのクラスを一つのファイルに書き込むのが通例であるようだ.

まず,Timerクラスのヘッダーファイルtimer.hは次のようにする.

#ifndef TIMER_H
#define TIMER_H

#include <qvbox.h>
#include <qhbox.h>
#include <qslider.h>
#include <qspinbox.h>

class Timer : public QVBox{
public:
  Timer(); //コンストラクタ

private:
  QHBox* hbox;
  QSpinBox *spinBox;
  QSlider *slider;
};

#endif //TIMER_H

このヘッダーファイルはほかのファイルでインクルードされる.ヘッダーファイル中の

#ifndef TIMER_H
#define TIMER_H
・
・
・
#endif //TIMER_H

はヘッダーファイルを2回以上インクルードしないための典型的なc言語の手法である.

Timerクラスのメンバー関数を書き込むファイルtimer.cppは次のようになる.

#include <qvbox.h>
#include <qhbox.h>
#include <qslider.h>
#include <qspinbox.h>

#include "timer.h"

Timer::Timer()
{
  setCaption("Timer"); //このクラスにタイトルをつける
  setMargin(5); //このクラス自身のマージンの設定
  setSpacing(5); //このクラス自身のウィジェット間のスペース

  hbox = new QHBox(this); //このクラスを親に持つQHBox
  hbox->setSpacing(5); //ウィジェット間のスペース

  spinBox = new QSpinBox(hbox); //hboxを親とするスピンボックス
  slider = new QSlider(Qt::Horizontal, hbox);  //hboxを親とする水平方向のスライダー

  spinBox->setRange(0, 300); //スピンボックスの値の設定 0から300まで
  slider->setRange(0, 300); //スライダーの値の設定 0から300まで

  //スピンボックスとスライダーの値を結びつける
  QObject::connect(spinBox, SIGNAL(valueChanged(int)),
		   slider, SLOT(setValue(int)) );
  QObject::connect(slider, SIGNAL(valueChanged(int)),
		   spinBox, SLOT(setValue(int)) );

}

今の場合は,ファイルの中身はコンストラクタだけである.

main関数のファイルmain.cppは次のようにする.

#include <qapplication.h>

#include "timer.h"

int main(int argc, char* argv[])
{
  QApplication app(argc, argv);

  Timer* timer = new Timer();

  app.setMainWidget(timer);
  timer->show();

  return app.exec();

}

このように分割してプログラムを作ってもコンパイル方法は変わらず,以下のようにする.

$ qmake -project
$ qmake
$ make

機能を追加する

タイマーとして使うにはもっと機能を追加していかなければいけない.まずはデジタル表示で数字を表示するQLCDNumberを追加する.このためにはTimerクラスのメンバーに

  QLCDNumber *lcd;

を追加して,Timerクラスのコンストラクタに,

  lcd  = new QLCDNumber( 3, this, "lcd" );
  lcd->setSegmentStyle(lcd->Filled);

を追加する.また,QLCDNumberの表示をスライダーやスピンボックスと結びつけるため

  connect( slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int)) );

を追加する.

さらにスタートとストップのプッシュボタンも付けたいので,Timerクラスのメンバーに

  QPushButton *start;
  QPushButton *stop;

を追加し,Timerクラスのコンストラクタに,

  start = new QPushButton("Start", this);
  stop = new QPushButton("Stop", this);

を追加する.これで見た目は少しはタイマーっぽくなった.

シグナルとスロットを設定する

Qtでは自分でシグナルとスロットを設定することができる.ここではスタートボタンを押せばタイマーがスタートして0までカウントするような機能を持たせる.

まずは時間をカウントする仕組みが必要になるがこれにはQTimeクラスを使う.QTimeは時間を設定すると,その時間がくればQtimeout()というシグナルを発してくれる便利なクラスである. 例えば,

  QTime* time(0);
  time->start(n*1000, true);

とすれば,n秒後に一度だけシグナルを発してくれる.二つ目の引数をfalseにするとn秒毎にシグナルを発してくれる.

では独自のスロットを作っていこう.独自のスロットとシグナルを使うためにはクラス宣言の先頭でQ_OBJECTを宣言しなければならない.つまり,

class Timer : public QVBox{
  Q_OBJECT
  …
  …
};

と書く.そしてtimer_start()というスロットを作りたければ,

class Timer : public QVBox{
  Q_OBJECT
  …
  …
private slots:
  void timer_start();

};

とする.そして,timer_start()の実装はtimer.cppに書く.

この完成したプログラムは,以下の通り.

ヘッダーファイル (timer.h).

001   #ifndef TIMER_H
002   #define TIMER_H
003   
004   #include <qvbox.h>
005   #include <qhbox.h>
006   #include <qslider.h>
007   #include <qspinbox.h>
008   #include <qlcdnumber.h>
009   #include <qpushbutton.h>
010   #include <qtimer.h>
011   
012   class Timer : public QVBox{
013     Q_OBJECT
014   
015   public:
016     Timer(); //コンストラクタ
017   
018   private:
019     QLCDNumber *lcd;
020     QHBox* hbox;
021     QSpinBox *spinBox;
022     QSlider *slider;
023     QPushButton *start;
024     QPushButton *stop;
025     QTimer *time;
026   
027     //独自のスロット
028   private slots:
029     void timer_start();
030     void one_sec();
031     void timer_stop();
032   };
033   
034   #endif //TIMER_H
035   

ソースファイル (timer.cpp).

001   #include <qvbox.h>
002   #include <qhbox.h>
003   #include <qslider.h>
004   #include <qspinbox.h>
005   #include <qlcdnumber.h>
006   #include <qpushbutton.h>
007   #include <qtimer.h>
008   
009   #include "timer.h"
010   
011   Timer::Timer()
012   {
013     setCaption("Timer"); //このクラスにタイトルをつける
014     setMargin(5); //このクラス自身のマージンの設定
015     setSpacing(5); //このクラス自身のウィジェット間のスペース
016   
017     lcd  = new QLCDNumber( 3, this, "lcd" ); //3桁の表示
018     lcd->setSegmentStyle(lcd->Filled); //表示方法
019   
020     hbox = new QHBox(this); //このクラスを親に持つQHBox
021     hbox->setSpacing(5); //ウィジェット間のスペース
022   
023     spinBox = new QSpinBox(hbox); //hboxを親とするスピンボックス
024     slider = new QSlider(Qt::Horizontal, hbox);  //hboxを親とする水平方向のスライダー
025   
026     spinBox->setRange(0, 300); //スピンボックスの値の設定 0から300まで
027     slider->setRange(0, 300); //スライダーの値の設定 0から300まで
028   
029     start = new QPushButton("Start", this); //スタートボタン
030     stop = new QPushButton("Stop", this); //ストップボタン
031   
032     time = new QTimer(this); //タイマー
033   
034     //スピンボックスとスライダーの値を結びつける
035     connect(spinBox, SIGNAL(valueChanged(int)),
036         slider, SLOT(setValue(int)) );
037     connect(slider, SIGNAL(valueChanged(int)),
038         spinBox, SLOT(setValue(int)) );
039   
040     //lcdにスピンボックスとスライダーの値を結びつける
041     connect( slider, SIGNAL(valueChanged(int)),
042          lcd, SLOT(display(int)) );
043   
044     //Timerクラス独自のスロットにコネクトする
045     connect( start, SIGNAL(clicked()),
046          this, SLOT(timer_start()) );
047     connect( time, SIGNAL(timeout()),
048          this, SLOT(one_sec()) );
049     connect( stop, SIGNAL(clicked()),
050          this, SLOT(timer_stop()) );
051   }
052   
053   //1秒ごとのカウントをはじめる
054   void Timer::timer_start()
055   {
056     time->start(1000, false);
057   }
058   
059   //1秒たったときの処理
060   void Timer::one_sec()
061   {
062     if( lcd->intValue() > 0)
063       lcd->display( lcd->intValue() -1 ); //デジタルカウンタを1減らす
064     else
065       time->stop(); //デジタルカウンタが0のときタイマーを止める
066   }
067   
068   //タイマーを止める
069   void Timer::timer_stop()
070   {
071     time->stop();
072   }

ソースファイル (main.cpp).

001   #include <qapplication.h>
002   
003   #include "timer.h"
004   
005   int main(int argc, char* argv[])
006   {
007     QApplication app(argc, argv);
008   
009     Timer* timer = new Timer();
010   
011     app.setMainWidget(timer);
012     timer->show();
013   
014     return app.exec();
015   
016   }


no counter