PyQt湯けむりウィジェットツアー(第3弾)

 あー、湯けむりウィジェットツアーも3回目です。 ここまで来たからにはおとなしく帰れると思うなよ(こらこらこら) 今度のウィジェットツアーはQtのたくさんのウィジェットが大集合!(死語)
 サンプルですが、かなり長いです。そこはそれ、体にやさしい当サイトですから、 たまには指の運動もしなきゃ、というわけで、 ずずずとスクロールさせてご覧くださいね。 いや、全部うそです。今思いついた言い訳です。

#! /usr/local/bin/python
import sys
from qt import *

class MyWidget(QWidget):

  def __slider_moved(self, val):
    self.spia.setValue(val)
    self.lcda.display(val)

  def __init__(self, *args):
    apply(QWidget.__init__, (self,) + args)
    self.resize(320, 320)
    self.setCaption(self.tr("ゆけむりん"))

    # タブウィジェットだよ
    tab = QTabWidget(self)
    tab.resize(310, 280)
    cmda = QPushButton(self.tr("終了"), self)
    cmda.setFont(QFont("Helvetica", 16, QFont.Normal))
    cmda.setGeometry(240, 290, 60, 24)
    self.connect(cmda, SIGNAL("clicked()"), self.close)

    # *** PAGE 1 ***
    # タブページだよ
    page1 = QWidget(tab)
    page1.resize(300, 280)
    tab.addTab(page1, "Page 1")
    # グループボックスだよ
    grba = QGroupBox("Group Box 1", page1)
    grba.setGeometry(5, 5, 290, 240)
    # ボタングループだよ
    btga = QButtonGroup("Button Group 1", grba)
    btga.setGeometry(5, 20, 120, 100)
    # ラジオボタンだよ
    self.rads = {"1" : QRadioButton("C1", btga),
                 "2" : QRadioButton("C2", btga),
                 "3" : QRadioButton("C3", btga)}
    self.rads["1"].move(10, 20); self.rads["1"].setChecked(1)
    self.rads["2"].move(10, 40)
    self.rads["3"].move(10, 60)
    # チェックボタンだよ
    self.chka = QCheckBox("Checked?", grba)
    self.chka.setGeometry(205, 215, 80, 20)

    # ラベルだよ
    laba = QLabel(self.tr("むむむ"), grba)
    laba.setFont(QFont("Helvetica", 16, QFont.Normal))
    laba.move(130, 20)
    # ラインエディットだよ
    self.txfa = QLineEdit(grba); self.txfa.setText("(default)")
    self.txfa.setGeometry(130, 50, 60, 20)
    # スライダーだよ
    self.slda = QSlider(0, 100, 5, 50, QSlider.Horizontal, grba)
    self.slda.setGeometry(130, 80, 80, 20)
    # 日本語で言うと説明が長いやつだよ
    self.lcda = QLCDNumber(3, grba)
    self.lcda.setGeometry(130, 110, 80, 20)
    # スピンボックスだよ
    self.spia = QSpinBox(grba); self.spia.setRange(0, 100)
    self.spia.setValue(50)
    self.spia.setGeometry(130, 140, 80, 20)
    self.connect(self.slda, SIGNAL("sliderMoved(int)"), self.__slider_moved)

    # リストボックスだよ
    self.lsta = QListBox(grba)
    self.lsta.setGeometry(5, 130, 110, 105)
    for e in ["item 1", "item 2", "item 3", "item 4"]:
      self.lsta.insertItem(e)
    self.lsta.setCurrentItem(0)
    # コンボボックスだよ
    self.cboa = QComboBox(1, grba)
    self.cboa.setGeometry(130, 170, 80, 24)
    for e in ["item 1", "item 2", "item 3", "item 4"]:
      self.cboa.insertItem(e)
    self.cboa.setCurrentItem(3)

    # *** PAGE 2 ***
    page2 = QWidget(tab)
    tab.addTab(page2, "Page 2")
    # マルチラインエディットだよ
    self.mlea = QMultiLineEdit(page2)
    self.mlea.setFont(QFont("fixed", 14, QFont.Normal))
    self.mlea.setGeometry(5, 20, 240, 80)
    self.mlea.setText(self.tr(
"""これはPyQtでサポートされているウィジェット(GUI部品)のうち、
基本的なものの大部分を一堂に紹介する簡単なサンプルです。
"""))
    # ツールチップだよ
    QToolTip.add(self.mlea, "Help strings")

    # プログレスバーだよ
    self.pbar = QProgressBar(page2)
    self.pbar.setTotalSteps(100)
    self.pbar.setGeometry(5, 110, 180, 30)
    # タイマーだよ
    self.timer = QTimer()
    self.connect(self.timer, SIGNAL("timeout()"), self.__timer_timeout)
    # トグルボタンだよ
    self.cmdt = QPushButton(self.tr("開始"), page2)
    self.cmdt.setFont(QFont("Helvetica", 16, QFont.Normal))
    self.connect(self.cmdt, SIGNAL("stateChanged(int)"), self.__timer_trigger)
    self.cmdt.setToggleButton(1)
    self.cmdt.setGeometry(205, 160, 70, 28)

  def __timer_trigger(self, state):
    if state == 2: # 押された
      self.cmdt.setText(self.tr("ふがふが"))
      # プログレスバーをリセット
      self.pbarpct = 0
      self.pbar.setProgress(0)
      # タイマーを開始
      # 1番目の引数はタイムアウトまでの時間(ミリ秒)、
      # 2番目の引数がTRUE(1)なら一度だけ、FALSE(0)ならstop()メソッドが
      # 呼ばれるまで何度でもタイムアウトイベントを発生させます。
      self.timer.start(500, 0)
    elif state == 0: # 離された
      # タイマーを停止、プログレスバーはその位置で停止
      self.cmdt.setText(self.tr("開始"))
      self.timer.stop()

  def __timer_timeout(self):
    self.pbarpct += 5
    self.pbar.setProgress(self.pbarpct)
    # 最大ステップ数(進度100%)に到達したらタイマーを停止
    if self.pbarpct >= self.pbar.totalSteps():
      self.cmdt.toggle()

  def keyPressEvent(self, e):
    print str(e.key()) + " " + str(e.ascii()) # + " " + str(e.text())

a = QApplication(sys.argv)
codec = QTextCodec.codecForName("eucJP")
a.setDefaultCodec(codec)
w = MyWidget()
a.setMainWidget(w)
w.show()
a.exec_loop()
# end.

 では片っ端からいきましょう。ちぎっては投げ、ちぎっては投げ。


タブウィジェット(QTabWidget)

 タブウィジェットとは、MS-Windowsのモニターの設定などでおなじみの、 いくつかの画面をその縁についたタブで切り替えることができるウィジェットで、 限られた領域で集積度の高いアプリケーションを作るのに役立ちます。 PyQtでは、まずQTabWidgetのインスタンスを作ります。 タブの各ページはQWidgetのインスタンスを作り、 これらをaddTabメソッドで載せていきます。 addTabメソッドの第2引数はタブの見出しラベルです。

  tab = QTabWidget(self)
  tab.resize(310, 280)

  page1 = QWidget(self.tab)
  page1.resize(300, 280)
  tab.addTab(self.page1, "Page 1")

  page2 = QWidget(self.tab)
  page2.resize(300, 280)
  tab.addTab(self.page2, "Page 2")


グループボックス(QGroupBox)とボタングループ(QButtonGroup)

 グループボックスとボタングループは、外見は全く同じコンテナウィジェットです。 これらの特徴は、その上に載せるウィジェットを縁どり線で囲み、 ひとつのグループのように見せることです。 グループボックスは本当に「グループのように見せる」 以外はなんの機能もありませんが、 ボタングループはラジオボタンと組み合わせて使う重要な機能を持っています。

  grba = QGroupBox("Group Box 1", self.page1)
  grba.setGeometry(5, 5, 290, 240)


チェックボックス(QCheckBox)

 GUIアプリケーションでよく見掛ける、 つつかれるたびにハネマークがついたり消えたりする小さな四角は、 Qtではチェックボタンではなくチェックボックスと呼ばれます。 メソッドsetCheckedでON(引数=1)とOFF(引数=0) をプログラム中から切り替えることができ、 現在の状態はメソッドcheckedで知ることができます。

  chka = QCheckBox("Checked?", grba)


ラジオボタン(QRadioButton)

 で、ラジオボタンのほうはラジオボタンなのですね。まま、それはいいとして、 Qtのラジオボタンは複数の中から常に1つ以下が選択状態になるようにする制御を自力で行うことができません。 このために出て来るのが先述のボタングループで、 1かたまりのラジオボタンを同じボタングループの上に載せることで、 それらの間で同時に2個のボタンが選択されないような制御が自動的に行われます。

  btga = QButtonGroup("Button Group 1", grba)
  rads = {"1" : QRadioButton("C1", btga),
          "2" : QRadioButton("C2", btga),
          "3" : QRadioButton("C3", btga)}
  rads["1"].move(10, 20); rads["1"].setChecked(1)
  rads["2"].move(10, 40)
  rads["3"].move(10, 60)
ラジオボタンの押されている、押されていないを操作するメソッドは、 チェックボックスと同じくsetCheckedcheckedです。

スクリーンショット


スライダー(QSlider)

 スライダーは、他のGUIライブラリではしばしばスケールと呼ばれる、 マウスでぐにーと引っ張ってあらかじめ設定された値域の中の値を1つ指定するためのウィジェットです。

  slda = QSlider(0, 100, 5, 50, QSlider.Horizontal, grba)
  self.connect(slda, SIGNAL("sliderMoved(int)"), self.__slider_moved)

  def __slider_moved(self, val):
    self.spia.setValue(val)
    self.lcda.display(val)
QSliderのコンストラクタは何種類かありますが、 最も引数の多いそれは6つの引数をとります。 順に最小値、最大値、ページステップ値、初期値、方向、親ウィジェットです。 方向は記号定数HorizontalかVerticalが使えます。 値をプログラムから設定するには、スロットsetValueを使います。 また上でやっているように、 スライダーが引っ張られるとシグナルsliderMoved(int)が飛びます。


スピンボックス(QSpinBox)

 スピンボックスも、 ある範囲の中から整数値をひとつ選択するためのウィジェットです。 スライダーより視覚的でない反面、 値域の範囲が広い場合にはスライダーよりも使いやすいと思います。 使い方はスライダーによく似ています。

  self.spia = QSpinBox(grba); self.spia.setRange(0, 100)
  self.spia.setValue(50)


コンボボックス(QComboBox)

 おや?Qtのコンボボックスは他のGUIライブラリのそれとはちょっと趣が違います。 Qtのコンボボックスは、フロントエンドには常に1つの項目しか表示されません。 マウスでつつかれると選択肢がドロップダウンし、 その中からマウスで選ぶことで選択項目を変えることができるというものです。 つまり、他のGUIライブラリではむしろオプションメニュー(Gtk+) とかチョイス(Java AWT/Swing)とかドロップダウンリストボックス(PowerBuilder) と呼ばれるものに近いわけです。 ていうか、コンストラクタの最初の引数をFALSE(0)にするとまさしくこいつらそのものになってしまいます。 最初の引数を1(TRUE)にすることで、テキスト部分をキー入力で編集できる、 いわゆるコンボボックスになります。

  self.cboa = QComboBox(1, grba)
  for e in ["item 1", "item 2", "item 3", "item 4"]:
    self.cboa.insertItem(e)
  self.cboa.setCurrentItem(3)
 基本的なメソッドはリストボックスとほぼ同じで、 項目の追加はinsertIteminsertStringList、 項目の選択はsetCurrentItem、 項目の削除はremoveItemで行います。 現在選択されている項目をインデックス(最初が0)で得るには currentItem、 コンボボックスだけにキー入力で編集されている場合を鑑みテキストそのものを得るにはcurrentTextを使えばOKです。

スクリーンショット


マルチラインエディット(QMultiLineEdit)

 マルチラインエディットは和訳してその通り、複数行に渡るテキストを入力・ 表示することができるウィジェットで、 Qtでは嬉しいことに、必要に応じて勝手にスクロールバーが現れます。

  self.mlea = QMultiLineEdit(page2)
  self.mlea.setText(self.tr(
"""これはPyQtでサポートされているウィジェット(GUI部品)のうち、
基本的なものの大部分を一堂に紹介する簡単なサンプルです。
"""))


ツールチップ(QToolTip)

 ツールチップは簡単に使えて芸の細かいアプリケーションづくりに役立つウィジェットです。 オフィスツールのツールバーなどで、アイコンにマウスを当ててしばらくしていると黄色い地にそのボタンが何をするボタンかを説明するテキストがポップアップする、 まさしくあれです。 Qtでは下のようにクラスメソッドaddを使って任意のウィジェット (当然意味のあるものにつけましょう) にこのヘルプテキストをつけることができます。

  QToolTip.add(self.mlea, "Help strings")


トグルボタンは?

 Qtでは、押すたびにへこんだり飛び出たりする変なボタン、 ことトグルボタンは通常のボタンと同じくQPushButtonのインスタンスです。 QPushButtonのメソッドsetToggleButton で引数にTRUE(1)を指定するとトグルボタンになります。 へこんだり飛び出たりするたびにstateChenged(int)シグナルが飛びます。 このときスロットに渡される値は、 マウスでつつかれた結果トグルボタンがへこんだなら2、飛び出たなら0です。 プログラム中からへこみ飛び出しを切り替えるにはスロットtoggleを使います。

  self.cmdt = QPushButton(self.tr("開始"), page2)
  self.connect(self.cmdt, SIGNAL("stateChanged(int)"), self.__timer_trigger)
  self.cmdt.setToggleButton(1)


プログレスバー(QProgressBar)

 プログレスバーとは、ソフトウェアのインストーラなどで出て来る、 処理の進み具合を%で表示する横向きのバーのことです。 QtのプログレスバーはQProgressBarのインスタンスで、 それ単体の使い方はとても簡単です。 setTotalStepsメソッドで全体のステップ数をあらかじめ指定しておき、 setProgressメソッドでそのうちいくらのステップが済んだかを指定すれば、 進み具合を計算して%で表示してくれます。

  self.pbar = QProgressBar(page2)
  self.pbar.setTotalSteps(100)
  self.pbar.setProgress(70)


タイマー(QTimer)

 タイマーは目に見えないウィジェットです。 と言っていいものかどうか? QtはGUIツールキットライブラリですが、 含まれるクラスの全部が全部ウィジェットをおのずから体現したものではなく、 データ構造や単に便利というだけでクラスになっているものもあります。 が、タイマーも実はウィジェットで、目に見えなくても 「親に載せる」という概念がちゃんとあります。 これが暗に意味することはほぼ1つしかないのですけど、 つまり、親ウィジェットが閉じられると、タイマーも一緒に削除されます。

  self.timer = QTimer(self)
  self.connect(self.timer, SIGNAL("timeout()"), self.__timer_timeout)

  self.timer.start(500, 0)

  def __timer_timeout(self):
    self.pbarpct += 5
    if self.pbarpct >= self.pbar.totalSteps():
      self.timer.stop()
 タイマーを開始するにはメソッドstart を使います。第1引数はタイムアウトまでの時間(ミリ秒)です。 その時間が経つとタイマーからシグナルtimeout()が飛びます。 第2引数をTRUE(1)にするとワンショットタイマー、 つまり一度タイムアウトしたらそれっきりタイマーになります。 FALSE(0)にすると、stopメソッドを呼ばない限り、 第1引数で指定した時間間隔を置いて何度もタイムアウトが発生します。

 というわけで、以上、基本的な各種ウィジェットのオンパレード(また死語)でした。 次の第4弾では、少し複雑なあのウィジェットを少しつついてみようと思います。

セクションのサブメニューに戻る
(first uploaded 2000/11/25 last updated 2002/03/21)