画面制御ライブラリの利用

画面とキー入力制御の自由度を高める curses ライブラリを用いると、高機能な対話的プログラムが作れる。

cursesの利用でできること

cursesをプログラム内に導入し、利用宣言すると以下のことが可能になる。

その一方、画面出力・キー入力がすべてcursesのものになるため、 これまでのプログラムでできている以下のことが できなくなる

これらのことに気をつけ、それぞれのメソッドをcurses専用の ものを必ず利用するように気をつけていれば問題ない。

cursesの導入

cursesを利用したプログラムは以下のような流れで作成する。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
require 'curses'
include Curses

init_screen                     # スクリーンを初期化する

begin
  … 必要な処理本体 …
ensure
  close_screen                  # スクリーンを元に戻す
end

画面制御

エスケープシーケンス を用いても画面の任意の位置に文字出力できるが、位置決めを頻繁に行なう ようなプログラムではcursesを用いる方がプログラムを作りやすい。

たとえば、画面上「5行目、10桁目」に「こんにちは」と表示する プログラムは以下のようになる。

cur-hello.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
require 'curses'
include Curses

init_screen                     # スクリーンを初期化する

begin
  setpos(4, 9)                  # 5行目の10桁目(0から数えるため)
  addstr("こんにちは")
  refresh                       # 出力を画面に反映させる
  sleep 3                       # これがないとすぐ消えてしまうため
ensure
  close_screen
end

エスケープシーケンスを用いて、球に見立てた●を動かすプログラム pitch.rbと同じ動きをする ものを、cursesを利用して作ってみると以下のようになる。

cur-pitch.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
# cursesを用いて●を動かす
require 'curses'
include Curses

init_screen			# 画面も消える
ball = "●"
kesu = "  "
wait = 0.05			# タイマー

x = 60				# 60桁目の
y = 10				# 10行目から

begin
  while x > 10			# 右から左へ
    setpos(y-1, x-1)		# カーソルを今の位置へ
    addstr(kesu)		# 前のボールを消す
    x -= 1
    setpos(y-1, x-1)		# カーソルを次の位置へ
    addstr(ball)		# ボールを書く
    setpos(0,0)			# カーソルを邪魔でないところへ
    refresh                     # これをしないと画面に反映されない
    sleep(wait)			# 一定時間休む
  end

  while y > 1			# 下から上へ
    setpos(y-1, x-1)		# カーソルを今の位置へ
    addstr(kesu)		# 前のボールを消す
    y -= 1
    setpos(y-1, x-1)		# カーソルを次の位置へ
    addstr(ball)		# ボールを書く
    setpos(0, 0)		# カーソルを邪魔でないところへ
    refresh                     # 見せたいものが揃ったら必ずrefresh
    sleep(wait*2)		# 一定時間休む
  end
  setpos(y, 0)
  addstr("おしまい\n")
  refresh                       # 最後も忘れずに
  sleep 3
ensure
  close_screen
end

エスケープシーケンス版とほとんどプログラム構成は同じだが、 カーソル位置を決める部分が、setpos メソッドの呼び出し で分かりやすく書けることが分かる。

即時キー入力

これまで作成してきたようなデータの入力を主目的とするプログラムでは、 意味のある文字列を打ち込んで最後にReturnキーを押して 初めてデータがプログラム(getsメソッド)に渡る。 データ入力では間違えずに入れることが大切なのでこれでよいが、たとえば 「準備ができたら何かキーを押してください」、 「yかnを押してください」のような場合はReturn を押さずに進めた方が利用者にしてみれば快適である。

Cursesライブラリに含まれる getch メソッドは入力キー (の文字コード)を1字文だけ読み取るもので、これを呼ぶ前に cbreak メソッドを呼んでおくと、Return を押さなくてもデータが getch メソッドに伝わるようになる。 以下の2つの例を試してみよ。

Returnキーを押すまで進まない:

cur-nocbreak.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
require 'curses'
include Curses

init_screen                     # スクリーンを初期化する
nocbreak                        # Returnでデータをまとめて送る


begin
  addstr("何かキーを押してください: ")
  x = getch                     # 1字読み取る(文字コードが返る)
  addstr("\nさようなら(3秒後に終わります)")
  refresh                       # 出力を画面に反映させる
  sleep 3                       # 結果がしばらく見えるようにする
ensure
  close_screen
end

Returnキーを押さなくても進む:

cur-cbreak.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
require 'curses'
include Curses

init_screen                     # スクリーンを初期化する
cbreak                          # リターンキーなしでも入力させる
noecho                          # 入力文字のエコーバックを無しにする

begin
  addstr("何かキーを押してください(打った文字は見えません): ")
  x = getch                     # 1字読み取る(文字コードが返る)
  addstr("\nさようなら(3秒後に終わります)")
  refresh                       # 出力を画面に反映させる
  sleep 3                       # 結果がしばらく見えるようにする
ensure
  close_screen
end

後者で利用した noecho は、入力した文字を 画面に出す(エコーバックという)ことをやめる。

制限時間つきキー入力

Curses.timeout 変数にミリ秒単位の整数を指定すると gets メソッドで入力を待つ制限時間となる。たとえば、 入力待ちを2.5秒で打ち切るには Curses.timeout = 2500 とする。

cur-timeout.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
require 'curses'
include Curses
cbreak
Curses.timeout = 2500           # 2.5秒でアウト
init_screen

srand
alphabet = "abcdefghijklmnopqrstuvwxyz"
ans = alphabet[rand(alphabet.length)]

begin
  setpos(5,10)
  addstr(sprintf("%cを押せ! : ", ans))
  refresh
  key = getch
  setpos(6,10)
  if key == ans
    addstr("正解! また会おう")
  else
    addstr("出直してこい")
  end
  refresh
  sleep 2
ensure
  close_screen
end

その他必要なメソッド

Cursesライブラリを用いた対話的なプログラムをしっかり作るためには 以下のメソッドや変数を覚えておくよい。

サンプルプログラム

cursesを用いたアクション型プログラムの例を示す。

cur-jump.rb

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-
# cursesを用いて●を動かす
require 'curses'
include Curses
noecho                          # エコーバックなし
cbreak                          # Returnなしで即入力
Curses.timeout = 0		# 入力は待たない

init_screen			# 画面も消える
ball = "●"
kesu = "  "
wait = 0.03			# タイマー

x = 0
y = lines-2			# 下から2行目
j = 0                           # ジャンプの高さ
jmax = 6                        # 2ステップ分高度をあげる
jnow = 0                        # 現在のステップ(0 - 3)

setpos(1, 0)
addstr("SPCでジャンプ!")
setpos(y-5, cols/2+rand(3))     # ランダムに決めた位置に
addstr("★")                    # ★を置く
begin
  h = y-1                       # 高さの初期値をセットしておく
  while x < cols		# 右から左へ
    setpos(h, x-1)		# カーソルを今の位置へ
    addstr(kesu)		# 前のボールを消す
    x += 1
    h = y-1 - ((jmax-jnow)*jnow/2) # ジャンプは2次曲線
    setpos(h, x-1)		# カーソルを次の位置へ
    addstr(ball)		# ボールを書く
    setpos(0,0)			# カーソルを邪魔でないところへ
    refresh                     # これをしないと画面に反映されない
    if jnow > 0 then
      jnow -= 1                 # ジャンプ中の処理
      getch                     # ジャンプ中に押されたキーは捨てる
    else
      key = getch
      if key == " "[0]          # SPCだったら
        jnow = jmax             # ジャンプ開始
      end
    end
    sleep(wait)			# 一定時間休む
  end
  setpos(y-1, 0)
  addstr("おしまい\n")
  refresh                       # 最後も忘れずに
  sleep 3
ensure
  close_screen
end

目次