作者: 機械伯爵
日時: 2002/6/5(23:49)
 とゆーわけで、まぁ、そこそこのあたりに安定したと思われる
のをアップしときます。

 前回と違い、クラスで作ったので、まぁ前よりはマシかと思われ
ますが、グローバルフィールドで式評価するなど、相変わらず
デンジャラスなことを一部行っております。

 ホントはもう少し機能をつけるつもりだったのですが、現在は
コレが精一杯(特に16進表示に手間取りすぎた・・・)


--^ rpn_v09.py
"""
逆ポーランド式計算機 ver. 0.9
<仕様>
+-*/    四則演算
Enter   スタックプッシュ
Delete  入力行クリア
BS      入力行1文字削除
Home    内部スタック及び入力行全クリア
End     計算機プログラム終了
↑      入力行とスタックトップの交換(swap)
←      整数切り捨て
→      小数点2位
↓      16進法表示(値は捨てられる)
L       常用対数(log10)
P       power(べき乗)
R       平方根
"""
tempValue = 0
posX = 450
posY = 50
numchar="0123456789abcdefxABCDEFX."

class CalcData:         # 保存内部データの殆ど
  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):
    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:
  def setText(self,txt,x,y):
    txtId = self.canvas.create_text(
      x,y,text=txt,
      fill = "green",
      anchor = tk.NE,
      font = "Helvetica 15 bold"
    )
    return txtId
  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)
    if cd.len() > 3:
      self.setText(cd[-4],posX,posY)
    else:
      self.setText("0",posX,posY)
    if cd.len() > 2:
      self.setText(cd[-3],posX,(posY * 2))
    else:
      self.setText("0",posX,(posY * 2))
    if cd.len() > 1:
      self.setText(cd[-2],posX,(posY * 3))
    else:
      self.setText("0",posX,(posY * 3))
    if cd.len() > 0:
      self.setText(cd[-1],posX,(posY * 4))
    else:
      self.setText("0",posX,(posY * 4))
    self.canvas.create_line(
      10,230,470,230,fill="green"
    )
    self.setText(
      cd.entryLineDisplay(),
      posX,
      (posY * 5)
    )
  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:
      e = "tempValue=0.0+" + cd.pop() + opr + 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):
    cd.swap()
    cd.flag(1)
    self.show()
  def math_ex(self,action):# 数学拡張(LISP?)
    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()

  # 数値や四則・論理演算子の処理
  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()
--$

 模範的なコードかどうかは疑問ですが、まぁ、出来る限り
見やすいように書いたつもりです。

 画面書き換えやキーイベントバインドのパターンをいくつも
使ってますので、Tkinterはよーわからん、という方には、
もしかしたら参考になるかも、と考えています。

   機械伯爵