FuzzuTech Advanced Calculator – Python GUI + CLI Project

 Demo :


Click Video πŸ‘‡πŸ‘‡πŸ‘‡





Features:

  • Full video embed from YouTube

  • Short description from above

  • Code snippets (main.py + main_cli.py) with syntax highlighting

  • Download link for code (GitHub/Drive)

  • CTA to subscribe/follow on YT, IG, FB


Code :


main_cli.py


"""

FuzzuTech - Advanced Command Line Calculator

Features:

- Safe expression evaluation (using ast)

- Supports + - * / // % ** parentheses

- Math functions: sqrt, sin, cos, tan, log, ln, exp, factorial, abs

- Memory: M+, M-, MR, MC

- History: 'hist' shows previous calculations

- Commands: help, exit, clear

Author: FuzzuTech

"""


import ast

import math

import operator as op

from collections import deque


# Allowed operators map

ALLOWED_BINOPS = {

    ast.Add: op.add,

    ast.Sub: op.sub,

    ast.Mult: op.mul,

    ast.Div: op.truediv,

    ast.FloorDiv: op.floordiv,

    ast.Mod: op.mod,

    ast.Pow: op.pow,

}


ALLOWED_UNARYOPS = {

    ast.UAdd: lambda x: +x,

    ast.USub: lambda x: -x,

}


# Allowed math functions

MATH_FUNCS = {

    "sqrt": math.sqrt,

    "sin": math.sin,

    "cos": math.cos,

    "tan": math.tan,

    "log": lambda x, base=10: math.log(x, base),

    "ln": math.log,

    "exp": math.exp,

    "abs": abs,

    "fact": math.factorial,

    "factorial": math.factorial,

    "round": round,

}


# Safe eval using AST

def safe_eval(expr: str):

    """

    Evaluate arithmetic expressions safely.

    Supports numbers, parentheses, binops in ALLOWED_BINOPS, unary ops,

    and calls to allowed MATH_FUNCS.

    """

    try:

        node = ast.parse(expr, mode="eval").body

        return _eval_node(node)

    except Exception as e:

        raise ValueError(f"Invalid expression: {e}")


def _eval_node(node):

    if isinstance(node, ast.Num):  # python <3.8

        return node.n

    if hasattr(ast, "Constant") and isinstance(node, ast.Constant):  # py3.8+

        if isinstance(node.value, (int, float)):

            return node.value

        else:

            raise ValueError("Unsupported constant type.")

    if isinstance(node, ast.BinOp):

        left = _eval_node(node.left)

        right = _eval_node(node.right)

        op_type = type(node.op)

        if op_type in ALLOWED_BINOPS:

            return ALLOWED_BINOPS[op_type](left, right)

        else:

            raise ValueError("Operator not allowed.")

    if isinstance(node, ast.UnaryOp):

        operand = _eval_node(node.operand)

        utype = type(node.op)

        if utype in ALLOWED_UNARYOPS:

            return ALLOWED_UNARYOPS[utype](operand)

        raise ValueError("Unary operator not allowed.")

    if isinstance(node, ast.Call):

        if not isinstance(node.func, ast.Name):

            raise ValueError("Only simple function calls allowed.")

        fname = node.func.id

        if fname not in MATH_FUNCS:

            raise ValueError(f"Function '{fname}' not allowed.")

        args = [_eval_node(a) for a in node.args]

        # support keyword defaults? not necessary now

        return MATH_FUNCS[fname](*args)

    raise ValueError(f"Unsupported expression node: {type(node)}")


# CLI calculator main loop

def cli_main():

    print("FuzzuTech - Advanced CLI Calculator")

    print("Type expressions like: 2 + 3*4, sqrt(16), log(100,10), factorial(5)")

    print("Memory commands: M+ (add result), M- (sub result), MR (recall), MC (clear)")

    print("Other: hist (show history), clear (clear history), exit (quit), help")

    memory = 0.0

    last_result = None

    history = deque(maxlen=200)


    while True:

        try:

            inp = input(">>> ").strip()

        except (EOFError, KeyboardInterrupt):

            print("\nExiting.")

            break

        if not inp:

            continue


        cmd = inp.lower()


        if cmd == "exit":

            print("Bye!")

            break

        if cmd == "help":

            print("Examples: 3+4*2, (2+3)**2, sqrt(25), log(100,10)")

            print("Memory: M+, M-, MR, MC")

            continue

        if cmd == "hist":

            if not history:

                print("No history yet.")

            else:

                for i, (expr, res) in enumerate(history, start=1):

                    print(f"{i}. {expr} = {res}")

            continue

        if cmd == "clear":

            history.clear()

            print("History cleared.")

            continue


        # Memory commands

        if cmd.upper() in {"M+", "M-", "MR", "MC"}:

            c = cmd.upper()

            if c == "M+":

                if last_result is None:

                    print("No last result to add to memory.")

                else:

                    memory += float(last_result)

                    print(f"Memory = {memory}")

            elif c == "M-":

                if last_result is None:

                    print("No last result to subtract from memory.")

                else:

                    memory -= float(last_result)

                    print(f"Memory = {memory}")

            elif c == "MR":

                print(f"Memory Recall => {memory}")

                last_result = memory

            elif c == "MC":

                memory = 0.0

                print("Memory cleared.")

            continue


        # Evaluate expression

        try:

            # allow using 'ANS' or 'ans' to refer to last result and 'MEM' to memory

            expr = inp.replace("ANS", str(last_result) if last_result is not None else "0")

            expr = expr.replace("ans", str(last_result) if last_result is not None else "0")

            expr = expr.replace("MEM", str(memory))

            result = safe_eval(expr)

            # floatify small ints to int for clean display

            if isinstance(result, float) and result.is_integer():

                result = int(result)

            print(result)

            history.append((inp, result))

            last_result = result

        except Exception as e:

            print("Error:", e)


if __name__ == "__main__":

    cli_main() 


main.py 


"""

FuzzuTech - Advanced Calculator GUI (CustomTkinter)

Features:

- Modern layout, big buttons, history, memory (M+, M-, MR, MC)

- Safe evaluation (re-uses safe_eval from CLI with limited functions)

- Keyboard support for Enter = evaluate, Backspace, Escape = clear

- Eye-catching styling using customtkinter theme

Author: FuzzuTech

"""


import customtkinter as ctk

from tkinter import messagebox

import math

import ast

import operator as op

from functools import partial


# ------------ Safe eval (same logic as CLI) ------------

ALLOWED_BINOPS = {

    ast.Add: op.add,

    ast.Sub: op.sub,

    ast.Mult: op.mul,

    ast.Div: op.truediv,

    ast.FloorDiv: op.floordiv,

    ast.Mod: op.mod,

    ast.Pow: op.pow,

}


ALLOWED_UNARYOPS = {

    ast.UAdd: lambda x: +x,

    ast.USub: lambda x: -x,

}


MATH_FUNCS = {

    "sqrt": math.sqrt,

    "sin": math.sin,

    "cos": math.cos,

    "tan": math.tan,

    "log": lambda x, base=10: math.log(x, base),

    "ln": math.log,

    "exp": math.exp,

    "abs": abs,

    "fact": math.factorial,

    "factorial": math.factorial,

    "round": round,

}


def _eval_node(node):

    if isinstance(node, ast.Num):

        return node.n

    if hasattr(ast, "Constant") and isinstance(node, ast.Constant):

        if isinstance(node.value, (int, float)):

            return node.value

        raise ValueError("Invalid constant")

    if isinstance(node, ast.BinOp):

        left = _eval_node(node.left)

        right = _eval_node(node.right)

        o = type(node.op)

        if o in ALLOWED_BINOPS:

            return ALLOWED_BINOPS[o](left, right)

        raise ValueError("Operator not allowed")

    if isinstance(node, ast.UnaryOp):

        operand = _eval_node(node.operand)

        u = type(node.op)

        if u in ALLOWED_UNARYOPS:

            return ALLOWED_UNARYOPS[u](operand)

        raise ValueError("Unary op not allowed")

    if isinstance(node, ast.Call):

        if not isinstance(node.func, ast.Name):

            raise ValueError("Bad function call")

        fname = node.func.id

        if fname not in MATH_FUNCS:

            raise ValueError("Function not allowed")

        args = [_eval_node(a) for a in node.args]

        return MATH_FUNCS[fname](*args)

    raise ValueError("Unsupported node")


def safe_eval(expr):

    node = ast.parse(expr, mode="eval").body

    return _eval_node(node)


# ------------ GUI ------------

ctk.set_appearance_mode("Dark")

ctk.set_default_color_theme("dark-blue")


class FancyCalc(ctk.CTk):

    def __init__(self):

        super().__init__()

        self.title("FuzzuTech - Advanced Calculator")

        self.geometry("550x620")

        self.minsize(360, 560)


        # state

        self.memory = 0.0

        self.last_result = None


        # layout: display on top, buttons grid below, right column history

        self.grid_columnconfigure(0, weight=1)

        self.grid_columnconfigure(1, weight=0)

        self.grid_rowconfigure(1, weight=1)


        # display frame

        disp_frame = ctk.CTkFrame(self, corner_radius=12)

        disp_frame.grid(row=0, column=0, columnspan=2, padx=12, pady=12, sticky="nwe")

        self.display = ctk.CTkEntry(disp_frame, font=ctk.CTkFont(size=22, weight="bold"))

        self.display.grid(row=0, column=0, padx=10, pady=14, sticky="we")

        self.display.bind("<Return>", lambda e: self.evaluate())

        self.display.bind("<Escape>", lambda e: self.clear_all())

        self.display.focus()


        # memory buttons

        mem_frame = ctk.CTkFrame(disp_frame, fg_color="transparent")

        mem_frame.grid(row=1, column=0, sticky="we", padx=6)

        self.mem_label = ctk.CTkLabel(mem_frame, text="M: 0", width=120)

        self.mem_label.grid(row=0, column=0, padx=(0,6))

        ctk.CTkButton(mem_frame, text="M+", width=60, command=self.mem_add).grid(row=0, column=1, padx=4)

        ctk.CTkButton(mem_frame, text="M-", width=60, command=self.mem_sub).grid(row=0, column=2, padx=4)

        ctk.CTkButton(mem_frame, text="MR", width=60, command=self.mem_recall).grid(row=0, column=3, padx=4)

        ctk.CTkButton(mem_frame, text="MC", width=60, command=self.mem_clear).grid(row=0, column=4, padx=4)


        # Buttons grid

        btn_frame = ctk.CTkFrame(self, corner_radius=12)

        btn_frame.grid(row=1, column=0, padx=(12,6), pady=6, sticky="nsew")

        for i in range(6):

            btn_frame.grid_rowconfigure(i, weight=1)

        for j in range(4):

            btn_frame.grid_columnconfigure(j, weight=1)


        buttons = [

            ("7","8","9","/"),

            ("4","5","6","*"),

            ("1","2","3","-"),

            ("0",".","(","+"),

            (")","**","%","//"),

            ("sqrt(","fact(","log(","=")

        ]

        for r, row in enumerate(buttons):

            for c, label in enumerate(row):

                action = partial(self.on_button, label)

                if label == "=":

                    btn = ctk.CTkButton(btn_frame, text=label, command=self.evaluate, corner_radius=10)

                else:

                    btn = ctk.CTkButton(btn_frame, text=label, command=action, corner_radius=10)

                btn.grid(row=r, column=c, padx=6, pady=6, sticky="nsew")


        # right panel: history and controls

        right = ctk.CTkFrame(self, width=200, corner_radius=12)

        right.grid(row=1, column=1, padx=(6,12), pady=6, sticky="nsew")

        right.grid_rowconfigure(1, weight=1)

        ctk.CTkLabel(right, text="History", font=ctk.CTkFont(size=16, weight="bold")).grid(row=0, column=0, padx=8, pady=(8,4))

        self.hist_box = ctk.CTkTextbox(right, width=220, wrap="word", state="disabled")

        self.hist_box.grid(row=1, column=0, padx=8, pady=6, sticky="nsew")

        ctk.CTkButton(right, text="Clear History", command=self.clear_history).grid(row=2, column=0, padx=8, pady=(4,10), sticky="we")


        # footer controls

        foot = ctk.CTkFrame(self, fg_color="transparent")

        foot.grid(row=2, column=0, columnspan=2, pady=(0,12))

        ctk.CTkButton(foot, text="Clear", width=100, command=self.clear_entry).grid(row=0, column=0, padx=8)

        ctk.CTkButton(foot, text="All Clear", width=100, command=self.clear_all).grid(row=0, column=1, padx=8)


    # ---------- button handlers ----------

    def on_button(self, text):

        # insert the button text into entry

        cur = self.display.get()

        self.display.delete(0, "end")

        self.display.insert(0, cur + text)


    def evaluate(self):

        expr = self.display.get().strip()

        if not expr:

            return

        # replace ANS and MEM tokens

        expr = expr.replace("ANS", str(self.last_result) if self.last_result is not None else "0")

        expr = expr.replace("MEM", str(self.memory))

        try:

            res = safe_eval(expr)

            # format result

            if isinstance(res, float) and res.is_integer():

                res = int(res)

            self.last_result = res

            self.display.delete(0, "end")

            self.display.insert(0, str(res))

            self.append_history(expr, res)

        except Exception as e:

            messagebox.showerror("Error", f"Could not evaluate: {e}")


    def append_history(self, expr, res):

        self.hist_box.configure(state="normal")

        self.hist_box.insert("end", f"{expr} = {res}\n")

        self.hist_box.see("end")

        self.hist_box.configure(state="disabled")


    def clear_history(self):

        self.hist_box.configure(state="normal")

        self.hist_box.delete("1.0", "end")

        self.hist_box.configure(state="disabled")


    def clear_entry(self):

        self.display.delete(0, "end")


    def clear_all(self):

        self.display.delete(0, "end")

        self.last_result = None


    # ---------- memory ----------

    def mem_add(self):

        if self.last_result is None:

            messagebox.showinfo("Memory", "No last result to add.")

            return

        self.memory += float(self.last_result)

        self.mem_label.configure(text=f"M: {self.memory}")


    def mem_sub(self):

        if self.last_result is None:

            messagebox.showinfo("Memory", "No last result to sub.")

            return

        self.memory -= float(self.last_result)

        self.mem_label.configure(text=f"M: {self.memory}")


    def mem_recall(self):

        self.display.delete(0, "end")

        self.display.insert(0, str(self.memory))

        self.last_result = self.memory


    def mem_clear(self):

        self.memory = 0.0

        self.mem_label.configure(text=f"M: {self.memory}")


if __name__ == "__main__":

    app = FancyCalc()

    app.mainloop() 

Comments

Popular posts from this blog

πŸš€ Simple Login & Registration System in Python Tkinter πŸ“±

πŸ”₯ Advanced MP3 Music Player in Python | CustomTkinter + Pygame | Free Source Code

πŸš€ Create a Python Screen Recorder with Audio (Complete Code)