作者: 機械伯爵
日時: 2002/6/12(21:36)
 ども、機械です。

 欲しかった簡単な統計機能を組み込んで、一応完成です。

 今回は、普及版で教科書もあるPython1.5.2対応版も書きましたが、
現在、私がPython1.5.2環境が無いので、もし使っておられる方が
いましたら、テスト/デバッグお願いいたします。

 まぁ、電卓としてはこんなトコでしょう。

 これ以上となれば、データのセーブ/ロード機能やグラフ描画機能等
ということになるでしょうし、実際、可能だと思いますが、少し時間を
置いてみたいと思います。

 GUIのサンプルとしては、ボタンやメニューといった定番でなく、
キャンバス描画やキー入力といった、どっちかというと往来のDOS
的なイベントを扱ってますので、ゲームなんか作る人には参考になる
んじゃないかしらん、と思います。

--^ rpn_v100.py
"""
逆ポーランド式計算機 ver.1.00
Python 2.2以降
<仕様>
+-*/    四則演算
Enter   スタックプッシュ
Delete  入力行クリア
BS      入力行1文字削除
Home    内部スタック及び入力行全クリア
End     計算機プログラム終了
↑      入力行とスタックトップの交換(swap)
←      整数切り捨て
→      小数点2位
↓      16進法表示(値は捨てられる)
L       常用対数(log10)
P       power(べき乗)
S       合計・平均・標準偏差表示
R       平方根
<用語>
TOS : Top of Stack(スタックの一番上のデータのこと)
<履歴>
1.00    簡易統計機能を組み込んで、ようやく完成版
"""
tempValue = 0
posX = 450
posY = 50
numchar="0123456789abcdefxABCDEFX."
stackLine = 4
BG = "Yes" # 背景表示をするなら"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 total(self): # スタック内の合計値を返す
    t = 0
    for x in self.stack:
      t += float(x)
    return t

  def ave(self): # スタックの平均値Average
    t = self.total()
    l = self.len()
    if l == 0:
      return 0
    else:
      return t/l

  def std(self): # スタックの標準偏差 Standard Diviation
    l = self.len()
    if l == 0:
      return 0
    else:
      ave = self.ave()
      s = 0
      for x in self.stack:
        s += abs(float(x) - ave)
      s = s/l
      return s

  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="gray"
    )

  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)
    if BG == "Yes":
      self.showBG()  # 背景表示
    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)
    )

  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()
        elif cd.flag() == 3: # 統計用特殊表示クリア
          cd.clearStack()
        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)
    elif event.char=="s":
      self.stat()
    else:
      self.alert("BAD KEY!")

  def stat(self): # 統計処理Statistic
    cd.push()
    t = cd.total()
    a = cd.ave()
    s = cd.std()
    cd.clearStack().clearLine()
    cd.flag(3)
    cd.push("Total = " + str(t))
    cd.push("Average = " + str(a))
    cd.push("Standard Diviation = " + str(s))
    self.show()
    cd.flag(3)

  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.title("RPN Calculator ver.1.00")
root.mainloop()
--$

--^ rpn_v101.py
"""
rpn_v101.py
逆ポーランド式計算機 ver.1.01
Python 1.5.2以降対応版
<仕様>
+-*/    四則演算
Enter   スタックプッシュ
Delete  入力行クリア
BS      入力行1文字削除
Home    内部スタック及び入力行全クリア
End     計算機プログラム終了
↑      入力行とスタックトップの交換(swap)
←      整数切り捨て
→      小数点2位
↓      16進法表示(値は捨てられる)
L       常用対数(log10)
P       power(べき乗)
R       平方根
S       簡易統計機能:合計・平均・標準偏差表示
<用語>
TOS : Top of Stack(スタックの一番上のデータのこと)
<履歴>
1.00    簡易統計機能を組み込んで、ようやく完成版
1.01    普及版のPython 1.5.2に対応できるよう、
        一部記述を変更
"""
tempValue = 0
posX = 450
posY = 50
numchar="0123456789abcdefxABCDEFX."
stackLine = 4
BG = "Yes" # 背景表示をするなら"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 len(self.stack):
      k = self.stack[-1]
      self.stack = self.stack[:-1]
      return k
    else:
      return "0"
  
  def len(self): # スタックの長さ取得
    return len(self.stack)
  
  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 total(self): # スタック内の合計値を返す
    t = 0
    for x in self.stack:
      t = t + float(x)
    return t

  def ave(self): # スタックの平均値Average
    t = self.total()
    l = self.len()
    if l == 0:
      return 0
    else:
      return t/l

  def std(self): # スタックの標準偏差 Standard Diviation
    l = self.len()
    if l == 0:
      return 0
    else:
      ave = self.ave()
      s = 0
      for x in self.stack:
        s = s + abs(float(x) - ave)
      s = s / l
      return s

  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
tk = Tkinter
import sys,math

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

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

  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)
    if BG == "Yes":
      self.showBG()  # 背景表示
    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 = sc - 1
    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: # 行が長いので、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()
        elif cd.flag() == 3: # 統計用特殊表示クリア
          cd.clearStack()
        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)
    elif event.char=="s":
      self.stat()
    else:
      self.alert("BAD KEY!")

  def stat(self): # 統計処理Statistic
    cd.push()
    t = cd.total()
    a = cd.ave()
    s = cd.std()
    cd.clearStack().clearLine()
    cd.flag(3)
    cd.push("Total = " + str(t))
    cd.push("Average = " + str(a))
    cd.push("Standard Diviation = " + str(s))
    self.show()
    cd.flag(3)

  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.title("RPN Calculator ver.1.01")
root.mainloop()
--$

 以上のスクリプトは、転載・改造その他自由ですので、勝手に使って
遊んでみてください。

   機械伯爵