beginner11
@beginner11

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

計算ツールを作成したい

分からないことについて

Pythonで計算ツールを作成したいのですが、上手くいきません。
VSCodeで記述しています。
1つ目の数字、記号、2つ目の数字を組み合わせて、適切な計算結果を表示したいです。

エラーの内容について

スクリーンショット 2025-02-09 130827.png

該当するソースコード

brain_calc.py
import tkinter
import math

root=tkinter.Tk()
root.title("偉大なる頭脳")
root.minsize(600,580)
root.option_add("*font",["MS ゴシック",20])

canvas=tkinter.Canvas(bg="#99ff22",width=700,height=480)
canvas.place(x=0,y=0)

def calc():
    global num_one
    global mark_in
    global num_two
    global answer
    num_one=int(one.get())
    mark_in=str(mark.get())
    num_two=int(two.get())
    if mark=='pi' or mark=='log2' or mark=='sqrt' or mark=='sin' or mark=='cos' or mark=='tan':
        ans()
        entry.delete(0,tkinter.END)
        entry.insert(0,answer)
    else:
        num_two=int(two.get())
        ans()
        entry.delete(0,tkinter.END)
        entry.insert(0,answer)

def ans():
    def sum():
        kotae=int(num_one+num_two)
        kotae["text"]=int(answer)

    def minus():
        answer=(num_one-num_two)
        answer["text"]=int(answer)

    def inc():
        answer=(num_one*num_two)
        answer["text"]=int(answer)

    def div():
        answer=(num_one/num_two)
        answer["text"]=int(answer)

    def div2():
        ans=(num_one//num_two)
        ans["text"]=str(ans)

    def joe():
        ans=(num_one**num_two)
        ans["text"]=str(ans)

    def extra():
        ans=(num_one%num_two)
        ans["text"]=str(ans)

    def circle():
        ans=(round(math.pi**num_one,2))
        ans["text"]=str(ans)

    def bottom():
        ans=((math.log2(num_one)))
        ans["text"]=str(ans)

    def sqrt():
        ans=((math.sqrt(num_one)))
        ans["text"]=str(ans)

    def sin():
        ans=(round(math.sin(math.radians(num_one))))
        ans["text"]=str(ans)

    def cos():
        ans=(round(math.cos(math.radians(num_one))))
        ans["text"]=str(ans)

    def tan():
        ans=(math.tan(math.radians(num_one)))
        ans["text"]=str(ans)

        if mark=='+':
            sum()
        elif mark=='-':
            minus()
        elif mark=='*':
            inc()
        elif mark=='/':
            div()
        elif mark=='//':
            div2()
        elif mark=='**':
            joe()
        elif mark=='%':
            extra()
        elif mark=='pi':
            circle()
        elif mark=='log2':
            bottom()
        elif mark=='sqrt':
            sqrt()
        elif mark=='sin':
            sin()
        elif mark=='cos':
            cos()
        elif mark=='tan':
            tan()

guide=tkinter.Label(text="1つ目の数字を入力してね",bg="#99ff22")
guide.place(x=80,y=50)

one=tkinter.Entry(width=12,bd=4)
one.place(x=400,y=50)

guide2=tkinter.Label(text="記号",bg="#99ff22")
guide2.place(x=80,y=150)

mark=tkinter.Entry(width=12,bd=4)
mark.place(x=400,y=150)

guide3=tkinter.Label(text="2つ目の数字を入力してね",bg="#99ff22")
guide3.place(x=80,y=250)

two=tkinter.Entry(width=12,bd=4)
two.place(x=400,y=250)

guide4=tkinter.Label(text="回答",bg="#99ff22")
guide4.place(x=80,y=350)

answer=tkinter.Entry(width=12,bd=4)
answer.pack(pady=10)
answer.place(x=400,y=350)

askbutton=tkinter.Button(master=root,text="計算する",command=calc)
askbutton.place(x=240,y=350)

entry=tkinter.Entry(master=root,width=12,bd=4)
entry.place(x=400,y=350)

root.mainloop()

調べたことなど

tkinterを使って同じような計算ツールの作成例について、色々と調べてみましたが、どうしても分かりません。どなたかアドバイスいただけると助かります。

追記

画像が正常に表示されていなかったので、修正しておきました。
(2025年2月9日 16時57分より)

0

5Answer

「エラーの内容について」の箇所が正しく表示されていません。修正お願いします。

下記、コードについて気になった点です。

  • ans関数内では関数を定義しているのみのようです。
  • 「kotae」という変数がsum関数のみで使われていますがこれは意図したものですか?
  • 変数answerに数値を代入しているようですが良いのでしょうか?
  • 「num_two=int(two.get())」のコードがelse節にもあるのはどういう意図ですか?

tkinterを使って同じような計算ツールの作成例について、色々と調べてみましたが、どうしても分かりません。

tkinterの使い方というよりもPythonの書き方(インデント、変数の使い方)の問題な気がします。
掲載のコードではans関数内のインデントがおかしいと思うのでまずは見直してください。

3Like

Comments

  1. @beginner11

    Questioner

    @megchandesuさんご指摘ありがとうございます!
    エラー内容のスクショ画像の方は表示しておきました。

  • mark : 記号を入力するテキストボックスを表す変数
  • mark_in : markに入力された文字列を格納した変数
    として、使用する想定だと理解しました。
    一方で実際のコードにおいて、calc関数やtan関数内でmarkと文字列を比較しています。
    mark_inと比較すべきではないでしょうか?
1Like

まず持って思うのは、ansの中でsumなどを定義するのは少しナンセンスかなと。
ansの外で定義して、ansは計算処理だけにしてあげた方が良い気がする。
多分この辺が整理できていないので、分岐部のインデントがtanに揃ってしまったのかなと。

そして肝心の分岐は、mark_inと比較するのでは?
何と何を使ってどうしたいのか、そこを整理して考え直すと良いのかもしれません。

個人的にはコード先頭(tk.Tk辺り)から全部クラスに突っ込んだりしますね。

1Like

Comments

  1. 私的回答(一部端折っています)

    import math
    import tkinter as tk
    
    from typing import Self
    
    class Application:
        # インスタンスの初期化
        def __init__ (self: Self) -> None:
            self._build_gui()
        # GUIの構築
        def _build_gui (self: Self) -> None:
            # ルート領域の作成
            root: tk.Tk = ...
            root.title(...)
            ...
            # 背景用?
            canvas: tk.Canvas = ...
            canvas.place(...)
            # ラベルの作成
            label1: tk.Label = ...
            label1.place(...)
            label2: tk.Label = ...
            label2.place(...)
            label3: tk.Label = ...
            label3.place(...)
            label4: tk.Label = ...
            label4.place(...)
            # 入力欄の作成
            entry1: tk.Entry = ...
            entry1.place(...)
            entry2: tk.Entry = ...
            entry2.place(...)
            entry3: tk.Entry = ...
            entry3.place(...)
            entry4: tk.Entry = ...
            entry4.place(...)
            # ボタンの作成
            button1: tk.Button = ...
            button1.place(...)
            # 非Public変数に紐付け(計算処理で参照する)
            self._entry1: tk.Entry = entry1
            self._entry2: tk.Entry = entry2
            self._entry3: tk.Entry = entry3
            self._entry4: tk.Entry = entry4
        # 入力値の計算
        def _calc (self: Self) -> None:
            # 入力値の読み取り
            text1: str = self._entry1.get()
            mark: str = str(self._entry2.get())
            text3: str = self._entry3.get()
            # 数値変換(空白時は0で初期化)
            val1: float = float(text1) if text1 != '' else 0
            val2: float = float(text3) if text3 != '' else 0
            # 回答の定義
            ret: float = 0.0
            # 記号に応じて計算(関数にせず、ここで算出)
            match mark:
                case '+': ret = ...
                case '-': ret = ...
                ...
                case _: return
            # 回答の出力
            self._entry4.delete(0, tk.END)
            self._entry4.insert(0, ret)
        # アプリケーションの表示
        def _show (self: Self) -> None:
            self._root.mainloop()
        # アプリケーションの機動
        @staticmethod
        def launch () -> None:
            app: Application = Application()
            app._show()
    
    if __name__ == '__main__':
        Application.launch()
    
  2. @beginner11

    Questioner

    @refrain_netさんありがとうございます!
    良ければ端折ってある部分も少しだけ教えていただけると助かるのですが……。
    欲張りですみません。

  3. 端折ると言っても、オブジェクトの生成などの既存のコードにある内容のみです。
    三点リーダーにしているだけなので、勉強がてらに入れ替えながら遊んでみると良いのかもです。
    参考までにですが個人的には普段、以下のような流れで考えて作っています。

    1. 何を作りたいかイメージする
    2. ベースになる動作の流れを考える
    3. 想定される異常操作のことを考えて、動作の流れに追加する
      ※今回で言うval1: float = float(text1) if text1 != '' else 0など
    4. コーディング(まずはCLIレベルで作るのが吉かも)
    5. 動作確認と修正
    6. GUIの構成を考える
    7. GUIを構築する
    8. イベントハンドラを紐付ける
    9. 完成

refrain_netさんの回答がほぼ答えだと思いますが、学習という観点での回答。

いきなりguiで実装ではなく、ステップバイステップで小さいものから作ってはいかがでしょうか。

最初はcuiで、足算のみ実装。
次は四則演算を追加で実装。
次は三角関数を追加で実装。
次は・・・
最後にguiに対応。
といった感じで。

それとグローバル変数は極力使わないほうが良いかと思います。
処理のトレースが非常にしづらくなります。
どうしても必要な場合はもちろんありますが、作成しているアプリには全く必要ないかと思います。

1Like

Comments

  1. 最初はCUIで質問してましたよ

  2. @beginner11

    Questioner

    様々なアドバイスをいただけて嬉しいです。ありがとうございます。
    今後の知識の種にしていけるように、頑張っていきたいです。

  3. なるほど。cuiからスタートしていたんですね。
    cuiでのコードの品質をあげるしかないですね。

    小さくスタートすれば全体が単純化されて、グローバル変数を使わなかったり、関数の引数・返却値がいい感じになるかなって思っていたんですけどそうはいかないだな。。。

  4. @beginner11

    Questioner

    @midoribiさん、アドバイスありがとうございます!
    とりあえずCUIに慣れてからまた出直そうと思います。
    @midoribiさんに限らず、皆さんの様々なアドバイスが聞けて良かったです。
    今後は暫くtkinterを使わないCUIのプログラムをメインで使用していきたいです。

変数名の間違いが多いです。

        answer=(num_one-num_two)
        answer["text"]=int(answer)

answer=値でローカル変数answerが作られます。その後にanswer['text']=answerと代入してもグローバル変数answerは使われません。
answer["text"]=値で値を表示したいのであればLabelを使いましょう。
answerとentryが同じ位置で重なっているのも変です。entryは不要でしょう。
初心者のうちは関数の中に関数を定義するのはやめましょう。
globalを使わずに値の取得を関数にしてはいかがでしょうか。

import tkinter
import math

def num_one():
    return int(one.get())

def num_two():
    return int(two.get())

def sum():
    answer["text"]=num_one()+num_two()

def minus():
    answer["text"]=num_one()-num_two()

def inc():
    answer["text"]=num_one()*num_two()

def div():
    answer["text"]=num_one()/num_two()

def div2():
    answer["text"]=num_one()//num_two()

def joe():
    answer["text"]=num_one()**num_two()

def extra():
    answer["text"]=num_one()%num_two()

def circle():
    answer["text"]=round(math.pi**num_one(),2)

def bottom():
    answer["text"]=math.log2(num_one())

def sqrt():
    answer["text"]=math.sqrt(num_one())

def sin():
    answer["text"]=round(math.sin(math.radians(num_one())))

def cos():
    answer["text"]=round(math.cos(math.radians(num_one())))

def tan():
    answer["text"]=math.tan(math.radians(num_one()))

def calc():
    mark_in=mark.get()
    if mark_in=='+':
        sum()
    elif mark_in=='-':
        minus()
    elif mark_in=='*':
        inc()
    elif mark_in=='/':
        div()
    elif mark_in=='//':
        div2()
    elif mark_in=='**':
        joe()
    elif mark_in=='%':
        extra()
    elif mark_in=='pi':
        circle()
    elif mark_in=='log2':
        bottom()
    elif mark_in=='sqrt':
        sqrt()
    elif mark_in=='sin':
        sin()
    elif mark_in=='cos':
        cos()
    elif mark_in=='tan':
        tan()

root=tkinter.Tk()
root.title("偉大なる頭脳")
root.minsize(600,580)
root.option_add("*font",["MS ゴシック",20])

canvas=tkinter.Canvas(bg="#99ff22",width=700,height=480)
canvas.place(x=0,y=0)

guide=tkinter.Label(text="1つ目の数字を入力してね",bg="#99ff22")
guide.place(x=80,y=50)

one=tkinter.Entry(width=12,bd=4)
one.place(x=400,y=50)

guide2=tkinter.Label(text="記号",bg="#99ff22")
guide2.place(x=80,y=150)

mark=tkinter.Entry(width=12,bd=4)
mark.place(x=400,y=150)

guide3=tkinter.Label(text="2つ目の数字を入力してね",bg="#99ff22")
guide3.place(x=80,y=250)

two=tkinter.Entry(width=12,bd=4)
two.place(x=400,y=250)

guide4=tkinter.Label(text="回答",bg="#99ff22")
guide4.place(x=80,y=350)

answer=tkinter.Label(width=12,bd=4)
answer.pack(pady=10)
answer.place(x=400,y=350)

askbutton=tkinter.Button(master=root,text="計算する",command=calc)
askbutton.place(x=240,y=350)

root.mainloop()
0Like

Comments

  1. さらに、演算子と関数の辞書を使うとif文を無くせます。
    計算関数は値を返すようにすれば再利用性が高まります。

    import tkinter
    import math
    
    def num_one():
        return int(one.get())
    
    def num_two():
        return int(two.get())
    
    def sum():
        return num_one()+num_two()
    
    def minus():
        return num_one()-num_two()
    
    def inc():
        return num_one()*num_two()
    
    def div():
        return num_one()/num_two()
    
    def div2():
        return num_one()//num_two()
    
    def joe():
        return num_one()**num_two()
    
    def extra():
        return num_one()%num_two()
    
    def circle():
        return round(math.pi**num_one(),2)
    
    def bottom():
        return math.log2(num_one())
    
    def sqrt():
        return math.sqrt(num_one())
    
    def sin():
        return round(math.sin(math.radians(num_one())))
    
    def cos():
        return round(math.cos(math.radians(num_one())))
    
    def tan():
        return math.tan(math.radians(num_one()))
    
    functions = {
        '+': sum,
        '-': minus,
        '*': inc,
        '/': div,
        '//': div2,
        '**': joe,
        '%': extra,
        'pi': circle,
        'log2': bottom,
        'sqrt': sqrt,
        'sin': sin,
        'cos': cos,
        'tan': tan,
    }
    
    def calc():
        try:
            answer['text']=functions[mark.get()]()
        except (KeyError, ValueError, ZeroDivisionError):
            answer['text']='ERROR'
    
    root=tkinter.Tk()
    root.title("偉大なる頭脳")
    root.minsize(600,580)
    root.option_add("*font",["MS ゴシック",20])
    
    canvas=tkinter.Canvas(bg="#99ff22",width=700,height=480)
    canvas.place(x=0,y=0)
    
    guide=tkinter.Label(text="1つ目の数字を入力してね",bg="#99ff22")
    guide.place(x=80,y=50)
    
    one=tkinter.Entry(width=12,bd=4)
    one.place(x=400,y=50)
    
    guide2=tkinter.Label(text="記号",bg="#99ff22")
    guide2.place(x=80,y=150)
    
    mark=tkinter.Entry(width=12,bd=4)
    mark.place(x=400,y=150)
    
    guide3=tkinter.Label(text="2つ目の数字を入力してね",bg="#99ff22")
    guide3.place(x=80,y=250)
    
    two=tkinter.Entry(width=12,bd=4)
    two.place(x=400,y=250)
    
    guide4=tkinter.Label(text="回答",bg="#99ff22")
    guide4.place(x=80,y=350)
    
    answer=tkinter.Label(width=12,bd=4)
    answer.pack(pady=10)
    answer.place(x=400,y=350)
    
    askbutton=tkinter.Button(master=root,text="計算する",command=calc)
    askbutton.place(x=240,y=350)
    
    root.mainloop()
    

Your answer might help someone💌