作者: 機械伯爵
日時: 2002/6/9(11:55)
 ども、機械です。

 計算機プログラムを作る、というのは、色々勉強になります。

 後々の拡張などを考えると、お題目に見えた(笑)オブジェクトによる
カプセル化などが、現実味を帯びて見えてきます。

 結構コレ、初心者→中級者へのステップアップの課題として面白い
かもしれませんね。

※電卓プログラムはプログラミングの教科書の定番だけど・・・


--^ rpn_v092.py
"""
逆ポーランド式計算機 ver. 0.9.2
Python 2.2以降
<仕様>
+-*/    四則演算
Enter   スタックプッシュ
Delete  入力行クリア
BS      入力行1文字削除
Home    内部スタック及び入力行全クリア
End     計算機プログラム終了
↑      入力行とスタックトップの交換(swap)
←      整数切り捨て
→      小数点2位
↓      16進法表示(値は捨てられる)
L       常用対数(log10)
P       power(べき乗)
R       平方根
<用語>
TOS : Top of Stack(スタックの一番上のデータのこと)
"""
tempValue = 0
posX = 450
posY = 50
numchar="0123456789abcdefxABCDEFX."
stackLine = 4
BG = "No" # 背景表示をするなら"Yes"
# 背景画はXBM形式のモノクロでファイル名はbg.xbm
# 大きさは480x360以内

class CalcData: # 保存内部データの殆ど
  """Class of calculate data"""
  def __init__(self):
    self.stack = []
    self.inputStr = ""
    self.tempValue = 0
    self.newLine = 0
  # スタック関連のメソッド


  def push(self,str=None): # スタックプッシュ
    if str==None:
      if self.inputStr == "":
        self.stack.append("0")
      else:
        self.stack.append(self.entryLineDisplay())
    else:
      self.stack.append(str)
    return self

  def pop(self): # スタックポップ
    if self.stack.__len__():
      return self.stack.pop()
    else:
      return "0"

  def len(self): # スタックの長さ取得
    return self.stack.__len__()

  def __getitem__(self,key): # スタックアイテムの参照
    return self.stack[key]

  def __setitem__(self,key,value): # スタックアイテムの代入
    self.stack[key] = value

  def swap(self): # 入力行とTOSの入れ替え(内部処理)
    if self.len():
      self[-1] , self.inputStr = self.inputStr , self[-1]

  def clearStack(self): # 全スタッククリア(内部処理)
    self.stack = []
    return self

  def str(self,txt=None): # 入力行整形用メソッド
    if txt==None:
      return self.inputStr
    else:
      self.inputStr = txt
      return self

  def flag(self,n=None): # 入力行取り扱い指示フラグ
    if n == None:
      return self.newLine
    else:
      self.newLine = n

  def str_empty(self): # 入力行の内容の有無を取得
    if self.str() == "" or self.str() =="0":
      return 1
    else:
      return 0

  def backDelete(self): # 一文字削除(内部処理)
    if self.inputStr:
      self.inputStr = self.inputStr[:-1]

  def clearLine(self): # 入力行削除(内部処理)
    self.inputStr = ""
    return self

  def addChar(self,c): # 一文字追加(内部処理)
    self.inputStr = self.inputStr + c
    return self

  def entryLineDisplay(self): # 入力行表示指示
    if self.str() == "":
      return "0"
    else:
      return cd.str()

cd = CalcData() # 計算データインスタンス作成

import Tkinter as tk
import sys,math

class DisplayView:
  """Calculator Display Class"""

  def showBG(self): # 背景画表示
    self.canvas.create_bitmap(
      0,0,anchor=tk.NW,
      bitmap="@bg.xbm",foreground="green"
    )

  def setText(self,txt,x,y): # 文字表示
    self.canvas.create_text(
      x,y,text=txt,
      fill = "green",
      anchor = tk.NE,
      font = "Helvetica 15 bold"
    )

  def alert(self,txt): # 警告表示
    self.canvas.create_text(
      10,10,text=txt,
      fill = "red",
      anchor = tk.NW,
      font = "Helvetica 15 bold"
    )

  def show(self): # 画面書き換えメソッド
    self.canvas.delete(tk.ALL)
    sc = stackLine
    while sc:
      pY = posY * (5 - sc)
      if cd.len() >= sc:
        self.setText(cd[-sc],posX,pY)
      else:
        self.setText("0.0",posX,pY)
      sc -= 1
    self.canvas.create_line(
      10,230,470,230,fill="green"
    )
    self.setText(
      cd.entryLineDisplay(),
      posX,
      (posY * 5)
    )
    if BG == "Yes":
      self.showBG()  # 背景表示

  def allClear(self,event): # 全スタッククリア
    cd.clearStack().clearLine()
    self.show()

  def enterValue(self,event): # 入力数プッシュ
    cd.push()
    cd.flag(2)
    self.show()

  def calc(self,opr): # 演算メソッド
    if cd.str_empty() and opr=="/":
      self.alert("ZERO DIVIDE!")
    else: # 行が長いので、2行に(笑)
      e = "tempValue=0.0+" + cd.pop() + opr
      e = e + cd.entryLineDisplay()
      exec e
      cd.str(str(tempValue))
      cd.flag(1)
      self.show()

  def backSpace(self,event): # 一文字削除
    cd.backDelete()
    self.show()

  def deleteKey(self,event): # 入力行削除
    cd.clearLine()
    self.show()

  def swap(self,event): # 入力用とTOSの入れ替え
    cd.swap()
    cd.flag(1)
    self.show()

  def math_ex(self,action):# 数学拡張
    cd.str(
      str(
        action(
          float(
            cd.entryLineDisplay()
          )
        )
      )
    )
    cd.flag(1)
    self.show()

  def math_ex2(self,action):# 数学拡張(2項)
    cd.str(
      str(
        action(
          float(
            cd.pop()
          ),
          float(
            cd.entryLineDisplay()
          )
        )
      )
    )
    cd.flag(1)
    self.show()

  def format_ex(self,format): # 書式変換(本体)
    cd.str(
      str(
        format % float(
          cd.entryLineDisplay()
        )
      )
    )
    cd.flag(1)
    self.show()

  # 特殊キーによる書式変換メソッド(ラッパー)

  def intform(self,event):
    self.format_ex("%i")

  def f2form(self,event):
    self.format_ex("%.2f")

  def hexform(self,event):
    self.format_ex("%x")
    cd.clearLine()

  # 数値や四則・論理演算子の処理
  # フラグが0なら、直接入力続行
  # フラグが1で入力行が空行でないなら、自動プッシュ
  # フラグが2なら、入力行を削除
  def inputCharacter(self,event):
    if event.char in numchar:
      if cd.flag():
        if cd.flag()==1:
          if not cd.str_empty():
            cd.push()
        cd.clearLine()
        cd.flag(0)
      cd.addChar(event.char)
      self.show()
    elif event.char in "+-*/":
      self.calc(event.char)
    elif event.char=="l":
      self.mathex(math.log10)
    elif event.char=="r":
      self.mathex(math.sqrt)
    elif event.char=="p":
      self.mathex2(pow)
    else:
      self.alert("BAD KEY!")

  def __init__(self,master): # キー割り当て
    self.canvas = tk.Canvas(
      master,width = 480,height=360,bg="black"
    )
    self.canvas.bind("<Home>",self.allClear)
    self.canvas.bind("<Return>",self.enterValue)
    self.canvas.bind("<BackSpace>",self.backSpace)
    self.canvas.bind("<Delete>",self.deleteKey)
    self.canvas.bind("<Up>",self.swap)
    self.canvas.bind("<Left>",self.intform)
    self.canvas.bind("<Right>",self.f2form)
    self.canvas.bind("<Down>",self.hexform)
    self.canvas.bind("<Key>",self.inputCharacter)
    self.canvas.bind("<End>",sys.exit)
    self.canvas.pack()
    self.show()
    self.canvas.focus_set()

# プログラム開始
root = tk.Tk()
dv = DisplayView(root)
root.mainloop()
--$

 今回は、一部ルーチンの整理と、私用版には実はあった背景表示
メソッドを加えています。

 XBM画像は、UNIXユーザには簡単だけど、Winユーザの方は、
BMPのモノクロ画像をGIMPで変換するのが一番簡単だと思われ
ます。

   「次は統計機能かな、やっぱ」機械伯爵