GTK+ で電卓(Ruby)

20180619021620
GUI で電卓なんて簡単にできるだろうと思ったら大間違いでした。

Ruby コード。
dentaku.rb

require 'gtk2'

class Calculator
  def initialize(entry)
    @entry = entry
    clear
  end
  
  def input(n)
    a = ["AC", "C" , "", "**", 7, 8, 9, "+", 4, 5, 6,
         "-", 1, 2, 3, "*", 0, ".", "=", "/"][n]
    parse(a)
  end
  
  def show(st)
    @entry.set_text(st)
  end
  
  def clear
    @buffer = []
    @left = ""
    @operator = nil
    show("")
  end
  
  def parse(c)
    case c
    when "AC"
      clear
    when "C"
      return if @buffer.empty?
      @buffer = []
      show("")
    when 0..9, "."
      if /^0/.match(number) and !/^0\./.match(number)
        @buffer = @buffer.drop_while {|x| x == 0}
      end
      if @buffer.empty? and c == "."
        @buffer = [0, "."]
      elsif @buffer.count(".").zero? or c != "."
        @buffer << c
      end
      show(number)
    when "*", "/", "+", "-", "**", "="
      right = eval(number).to_s
      return if right.empty? and !@operator and @left.empty?    #最初であるとき
      @buffer = []
      if @operator and !right.empty?
        begin
          right = eval(@left + @operator + right).to_s
          raise ZeroDivisionError if right == "Infinity"
          show(right)
        rescue ZeroDivisionError
          clear
          show("ZeroDivisionError")
          return
        end
      end
      @left = right unless right.empty?
      @operator = (c == "=") ? nil : c
    when ""
      st = eval(number).to_s
      @buffer = []
      if !st.empty?
        @left = eval("Math.sqrt(#{st})").to_s
      elsif !@left.empty?
        @left = eval("Math.sqrt(#{@left})").to_s
      else
        return
      end
      show(@left)
    else
      raise "error"
    end
  end
  
  def number
    @buffer.join
  end
end

class MainWindow < Gtk::Window
  def initialize
    super("電卓")
    set_resizable(false)
    
    entry  = Gtk::Entry.new
    entry.set_editable(false)
    entry.set_xalign(1)    #右詰め
    entry.set_height_request(40)
    entry.modify_font(Pango::FontDescription.new("12"))    #フォントのサイズ
    
    @calc = Calculator.new(entry)
    
    box = Gtk::VBox.new
    add(box)
    box.pack_start(entry, false, true, 0)
    box.pack_start(buttons_area, true, true, 0)
    
    signal_connect("destroy") {Gtk.main_quit}
    show_all
  end
  
  def buttons_area
    characters = %W(AC C √ ^ 7 8 9 + 4 5 6 - 1 2 3 × 0 . = ÷)
    hboxes = Array.new(5) {Gtk::HBox.new}
    i = 0
    area = Gtk::VBox.new
    hboxes.each do |box|
      4.times do
        b = Gtk::Button.new
        b.set_size_request(50, 40)
        l = Gtk::Label.new(characters[i])
        l.modify_font(Pango::FontDescription.new("12"))
        b.add(l)
        n = i
        b.signal_connect("clicked") {@calc.input(n)}
        box.pack_start(b, true, true, 0)
        i += 1
      end
      area.pack_start(box, true, true, 0)
    end
    area
  end
end

MainWindow.new
Gtk.main

GUI の部分は簡単でしたが、電卓のボタン処理をする部分(parse()メソッド)がぐちゃぐちゃになってしまいました。明らかにクソコードですね(^^;


ぐぐってみると普通の電卓を GUI でエミュレートしたような実装例はなかなかないですね。少なくとも Ruby では見つけられませんでした。Ruby でいうと簡易 irb みたいなのを実装する話が多いです。