ござるのブログ

覚え書きいろいろ

Ruby/Tkで少しだけインタラクティブなグラフを表示する

はじめに

以前書いたように、Ruby/TkPlotchartでグラフを表示できるのですが、それだけではつまらないので、ちょっとだけマウスと連動させてみます。

コード

#!/usr/bin/env ruby
# -*- coding: utf-8 -*-

require 'csv'
require 'tk'
require 'tkextlib/tcllib/plotchart'

# plotchart.rb を修正するモンキーパッチ
module Tk::Tcllib::Plotchart
  class XYPlot
    def pixel_to_coords(x, y)
      list(tk_call_without_enc('::Plotchart::pixelToCoords', @path, x, y))
    end
  end
end

# データを読み込み
en, cn = CSV.parse(DATA).map { |r| [r[0].to_f, r[1].to_i] }.transpose

# データをプロット
pl = Tk::Tcllib::Plotchart::XLogYPlot
     .new([0, en.size, 10**Math.log10(en.size).floor],
          [1, 10**Math.log10(cn.max).ceil],
          width: 600, height: 400) do
  title 'Spectrum'
  xtext 'Channel'
  ytext 'Counts/Channel'
  dataconfig('series1', color: :red)
  cn.each_with_index { |c, i| plot('series1', i, c) }
  pack(fill: :both)
end

# マウスカーソルの位置の値を表示
label = TkLabel.new.pack
pl.bind(:Motion, '%x %y') do |x, y|
  ch = pl.pixel_to_coords(x, y)[0].to_i
  if 0 <= ch && ch < en.size
    label.text(sprintf('%10d ch, %10.1f keV, %10d counts',
                       ch, en[ch], cn[ch]))
  end
end

# イベントループ
Tk.mainloop

__END__
32.78,1074
62.72,12131
92.70,13944
122.72,13110
152.79,11859
182.90,13204
213.05,11647
243.24,8780
273.47,7412
303.75,6632
334.07,6460
364.43,6369
394.83,6503
425.28,6566
455.76,5250
486.29,2766
516.86,1574
547.48,1062
578.13,914
608.83,3532
639.57,15018
670.35,18992
701.18,6495
732.04,660
762.95,177
793.90,157
824.89,134
855.93,154
887.00,131
918.12,124
949.28,129
980.48,113
1011.73,96
1043.01,103
1074.34,112
1105.72,92
1137.13,95
1168.58,86
1200.08,85
1231.62,66
1263.20,50
1294.83,29
1326.49,40
1358.20,33
1389.95,47
1421.74,93
1453.57,111
1485.45,67
1517.37,41
1549.33,19
1581.33,17
1613.38,11
1645.46,13
1677.59,18
1709.76,15
1741.98,10
1774.23,6
1806.53,13
1838.87,15
1871.25,11
1903.67,6
1936.14,8
1968.64,6
2001.20,8

実行結果

OSX Yosemiteで実行した結果です。下記のように、マウスカーソルの位置の値がグラフの下に表示されます。

f:id:gzalt:20141219145331g:plain

Ruby 2.2.0同梱のplotchart.rbのバグ

グラフ内におけるマウスカーソルの座標を取得するためにTk::Tcllib::Plotchart::XLogYPlot#pixel_to_coordsを使ったのですが、Ruby 2.2.0 同梱のplotchart.rbにはバグがあるので、下記のようにモンキーパッチを当てています。

module Tk::Tcllib::Plotchart
  class XYPlot
    def pixel_to_coords(x, y)
      list(tk_call_without_enc('::Plotchart::pixelToCoords', @path, x, y))
    end
  end
end

既に修正が本家に取り込まれたようなので、そのうち必要なくなると思います。

最後に

Ruby/TkPlotchartさえ入っていればWindowsでも動きます。しかし、WindowsPlotchartを使えるようにするまでがちょっと面倒でした(こちらに書きました)。