Pythonのtkinterで電卓作成の学習をしてみたまとめ【Python電卓アプリ】

Pythonで研究に役立つツールを自作したい。そんな思いから、何か簡単なアプリを作って見ようということで、電卓の作成練習を通してツールの作成方法を学んだことをアウトプットします。

参考ページ丸写しでは勉強にならないため、自分で+αの機能の搭載を試みて、改良を行いました。

電卓の作成練習をする目的

研究のデータ解析などで、やはり「目的に合わせた自作の補助ツールが欲しい」と思うので最終的にはそれを作るスキルを身に着けるということが最終目標です。
それに当たって、まずツールといえば、アプリを立ち上げて画面が出てきて、ボタンをクリックしたり、何か入力したり、読み込んだりというものをイメージしますね。
この形を作ってみたいということで、電卓アプリの作成練習を行いました。

今回の学習で学べる要素としては次の通りだと思います。

  • ツールのウィンドウのレイアウト設定
  • ボタンの配置
  • ウィンドウのサイズ変更に合わせてボタンのサイズが変わる
  • ボタンをクリックして応答
  • 計算する

参考にさせていただいたページ

今回の学習は下記のページを参考にさせていただきました。

このページは、Tkinterを用いた電卓の作成を手取り足取りとても詳しく解説されていて、非常にわかりやすく学習させてもらうことができました。

興味のある方は、ぜひ参考にさせていただいてはいかがでしょうか。

学習環境

OS:Mac
使用環境:Python3.7、VS Code
使用ライブラリ:tkinter

実際に書いたコード

赤色部分が本学習で自分でいじった部分です。

from tkinter import ttk
from tkinter import Tk
from tkinter import StringVar

#レイアウトを作成するために下記のようにすると楽だそうです
LAYOUT = [
    ['7', '8', '9', '/',],
    ['4', '5', '6', '*',],
    ['1', '2', '3', '-', 'C'],
    ['0', '.', '=', '+', 'AC'],
]

#記号をまとめた定数、if char in CAlC_SYMBOLS:…のように使うために定義しておく
CAlC_SYMBOLS = ('+', '-', '', '/', '*', '//')

class CalcApp(ttk.Frame):
    """ 電卓アプリ """
 def __init__(self, master=None):
     super().__init__(master)
     self.exp_list =['0']
     self.create_style()
     self.create_widgets()

  def create_style(self):
     """ボタン、ラベルのスタイルを変更"""
     style = ttk.Style()
     style.theme_use('default')

     #ラベルのスタイルを上書き
     style.configure('TLabel', font=('Helvetica', 20), background='black', foreground='white')

     #ボタンのスタイルを上書き
     style.configure('TButton', font=('Helvetica', 20))
  def create_widgets(self):  #ウィジェットの作成と配置

     #計算結果の表示ラベル
     self.display_var = StringVar()
     self.display_var.set('0') #初期値を0にする
     dispay_label = ttk.Label(self, textvariable=self.display_var)
     dispay_label.grid(column=0, row=0, columnspan=5, sticky='N, S, E, W')

     #レイアウトの作成
     for y, row in enumerate(LAYOUT, 1):
         for x, char in enumerate(row):
             button = ttk.Button(self, text=char)
             button.grid(column=x, row=y, sticky='N, S, E, W')
             button.bind('<Button-1>', self.calc)
     self.grid(column=0, row=0, sticky='N, S, E, W')  #これを忘れると表示されないので注意

     #各列の引き伸ばし設定
     self.columnconfigure(0, weight=1)
     self.columnconfigure(1, weight=1)
     self.columnconfigure(2, weight=1)
     self.columnconfigure(3, weight=1)

     #各行の引き伸ばし設定
     self.rowconfigure(0, weight=0)
     self.rowconfigure(1, weight=1)
     self.rowconfigure(2, weight=1)
     self.rowconfigure(3, weight=1)
     self.rowconfigure(4, weight=1)

     #トップレベルのウィジェットも引き伸ばしに対応させる
     self.master.columnconfigure(0, weight=1)
     self.master.rowconfigure(0, weight=1) def calc(self, event):

     #押されたボタンのテキストを取得
     char = event.widget['text']

     #最後に押したボタンの内容
     last = self.exp_list[-1]

     #=を押した時
     if char == '=':
         if last in CAlC_SYMBOLS:
             self.exp_list.pop()
         exp = eval(''.join(self.exp_list))
         self.exp_list = [str(exp)]

     #ACを押した場合
     elif char == 'AC':
         self.exp_list = ['0']

     #Cを押した場合
     elif char == 'C':
         if len(self.exp_list) == 1:
             self.exp_list = ['0']
         else:
             self.exp_list = self.exp_list[:-1]

     #各演算記号を押した場合
     elif char in CAlC_SYMBOLS:
         if last == char == '/':
             self.exp_list[-1] += '/'
         elif last == char == '*':
             self.exp_list[-1] += '*'
         elif last in CAlC_SYMBOLS:
             self.exp_list[-1] = char
         else:
             self.exp_list.append(char)

     #数値のボタンを押した場合
     else:
         if last == '0':
             self.exp_list[-1] = char
         elif last in CAlC_SYMBOLS:
             self.exp_list.append(char)
         else:
             self.exp_list[-1] += char
     self.display_var.set(''.join(self.exp_list))

def main():
     root = Tk()
     root.title('簡易電卓')
     CalcApp(root)
     root.mainloop()

if __name__ == '__main__':
     main()
動作させてみるとこんな感じです
サイズを変更するとボタンのサイズもちゃんと変わる

改造したところ

エラーが出た部分

「sticky=(N, S, E, W)」で「name ‘N’ is not defined」のエラーが出た

ttk.Button(self, text='横0, 縦0').grid(column=0, row=0, sticky=(N, S, E, W))

いう文があるがN、S、E、Wが定義されていないとエラーが出てしまいました。

上記のページを参考にさせていただくと、下のように””で囲まれていることがわかりました。

frame.grid(row=0, column=0, sticky='nwse')

試しに

ttk.Button(self, text='横0, 縦0').grid(column=0, row=0, sticky='N, S, E, W')

と入力するとちゃんと進みました。

StringVarでエラーが出る

こんな感じのエラーでした。

name 'stringvar' is not defined tkinter

上記のページを参考にさせていただくと、

from tkinter import StringVar

とあるので、インポートしてみると、見事に動きました!

おそらく見本にさせていただいているものは

from tkinter import *

*で全てを読み込んでいるからこれをしなくても問題が出ていないものと思います。
私の環境だと上記のインポートの仕方はなぜかエラーが出てしまうので、分けてインポートしている状態になっています。

計算部分のbackgroundが黒くならない

表記通りにするとなぜか図のように計算部分が黒くなりません。

これについては、試してみると、どうもスタイルが「aqua」だと反映されないそうです。
私の環境は何も表記しないデフォルトの状態が「aqua」だったため反映されなかったっぽい。
というわけで、

style.theme_use('default')

と記載するとしっかり黒くなりました!

追加機能をつけた部分

「AC」と「C」の二つのクリアボタンの搭載

今回参考にさせていただいた電卓では「C」を押すと計算部分が全消去になるボタンが一つついているだけととてもシンプルなものでした。
普段の電卓では、「AC」で全消去、「C」で一つ消去となっていると思いますが、今回それがついていないということは、「自分で勉強してカスタマイズしてみてね」ということか!と解釈して、トライしてみました。

ものすごく試行錯誤した結果、下記のように

#ACを押した場合
  elif char == 'AC':
      self.exp_list = ['0']
#Cを押した場合
  elif char == 'C':
      if len(self.exp_list) == 1:
          self.exp_list = ['0']
      else:
          self.exp_list = self.exp_list[:-1]

記載すると、「AC」を押すと全て消去して0になり、「C」を押すと一文字消去という感じで実装できました。

Cを入力前
C入力後、「+」だけが消えた

原理として、self.exp_listというものが計算部分に表記されているもので、これは文字列という認識をしているようです。
そこで、if len(self.exp_list) == 1:のように「文字列の数が1の場合のみ0を表記する」
と、self.exp_list = self.exp_list[:-1]のように「それ以外の時は1文字消去」
という感じで記載すると、狙い通りに「AC」と「C」についてそれぞれ機能を追加することができました。

反省点

エラーが出た部分について、
参考にさせていただいたページ通りだとエラーが出てしまう事が「なぜそれだとエラーがでてしまうのか」というところの大半が理解できていないところが今回の反省点です。

この「なぜ?」を説明できるようになれば大きく成長できると思うので、これからも精進します!

コメント

タイトルとURLをコピーしました