ASSISTANT
Sure! Below is the complete code with adjustments to handle string input for the `capitalize()` function, ensuring that the argument is correctly quoted when evaluated.
```python
import ctypes
import math
import re
from typing import Any, Dict, Union
import tkinter as tk
from tkinter import font as tkFont, ttk
# Revised Function for Capitalization
def capitalize_sentence(sentence: str) -> str:
"""Capitalize the first letter of each word in the sentence."""
return ' '.join(word[0].upper() + word[1:].lower() for word in sentence.split())
# Conversion Functions
def kg_to_lb(kg: float) -> float:
"""Convert kilograms to pounds."""
return kg / 0.45359237
def lb_to_kg(lb: float) -> float:
"""Convert pounds to kilograms."""
return lb * 0.45359237
# Evaluation Function
def evaluate_expression(expression: str, use_degrees: bool = False) -> Union[float, str]:
"""Safely evaluate a mathematical expression considering degree mode."""
def convert_angle(func: Any, x: float) -> float:
return func(math.radians(x)) if use_degrees else func(x)
def inverse_convert_angle(func: Any, x: float) -> float:
result = func(x)
return math.degrees(result) if use_degrees else result
safe_functions: Dict[str, Any] = {
"sqrt": math.sqrt,
"sin": lambda x: convert_angle(math.sin, x),
"cos": lambda x: convert_angle(math.cos, x),
"tan": lambda x: convert_angle(math.tan, x),
"asin": lambda x: inverse_convert_angle(math.asin, x),
"acos": lambda x: inverse_convert_angle(math.acos, x),
"atan": lambda x: inverse_convert_angle(math.atan, x),
"log": math.log10,
"ln": math.log,
"pi": math.pi,
"e": math.e,
"lb": kg_to_lb,
"kg": lb_to_kg,
"capitalize": capitalize_sentence # Ensure this works with string inputs
}
expression = expression.replace("^", "**")
# Adjust to handle capitalize with string argument
expression = re.sub(r'\bcapitalize\(([^\)]+)\)',
lambda match: f'capitalize("{match.group(1)}")',
expression)
func_calls = re.findall(r"(\b\w+\b)\(([^)]+)\)", expression)
for func, arg in func_calls:
if func in ["asin", "acos"]:
try:
value = eval(arg, {"__builtins__": None}, safe_functions)
if not -1 <= value <= 1:
raise ValueError(f"{func}(x) is undefined for |x| > 1")
except Exception as e:
return f"Error: {e}"
try:
result = eval(expression, {"__builtins__": None}, safe_functions)
if isinstance(result, float) and abs(result) < 1e-10:
result = 0.0
return round(result, 2) if isinstance(result, (int, float)) else result
except Exception as e:
return f"Error: {e}"
# Calculator GUI Class
class CalculatorGUI:
"""Tkinter-based Calculator with Degrees/Radians toggle and history."""
def __init__(self, master: tk.Tk) -> None:
self.master = master
self.use_degrees: tk.BooleanVar = tk.BooleanVar(value=False)
self.set_dpi_awareness()
self.setup_window()
self.create_widgets()
self.layout_widgets()
self.configure_grid()
def set_dpi_awareness(self) -> None:
"""Enable high-DPI support on Windows."""
try:
ctypes.windll.shcore.SetProcessDpiAwareness(1)
except Exception:
pass
def setup_window(self) -> None:
"""Configure the main application window."""
self.master.title("Calculator")
self.master.geometry("1200x720")
def create_widgets(self) -> None:
"""Initialize all GUI components."""
self.font: tkFont.Font = tkFont.Font(family="Roboto Flex", size=16)
self.button_font: tkFont.Font = tkFont.Font(family="Roboto Flex", size=14)
self.expression_var: tk.StringVar = tk.StringVar()
self.entry: ttk.Entry = ttk.Entry(
self.master, textvariable=self.expression_var, font=self.font, width=50
)
self.entry.bind("<Return>", self.calculate)
self.history_text: tk.Text = tk.Text(
self.master,
width=70,
height=10,
font=self.font,
wrap="none",
state=tk.DISABLED,
)
self.scrollbar: ttk.Scrollbar = ttk.Scrollbar(
self.master, orient="vertical", command=self.history_text.yview
)
self.history_text.configure(yscrollcommand=self.scrollbar.set)
# Create buttons for functions
self.function_buttons = {
"Functions": ["sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "log", "ln"],
"Conversions": ["lb", "kg"],
"Unique": ["capitalize"],
"Constants": ["pi", "e"]
}
self.function_frame = ttk.Frame(self.master)
self.create_function_buttons()
def create_function_buttons(self):
"""Create buttons for inserting functions into the entry."""
# Functions category with three columns
ttk.Label(self.function_frame, text="Functions", font=self.button_font).grid(
row=0, column=0, columnspan=3, padx=5, pady=5, sticky="n"
)
funcs = self.function_buttons["Functions"]
for i, func in enumerate(funcs):
button = ttk.Button(
self.function_frame,
text=func,
command=lambda f=func: self.insert_function(f),
width=10,
style='Function.TButton'
)
button.grid(row=(i // 3) + 1, column=(i % 3), padx=5, pady=5)
# Other categories with one column each
col = 0
for category in ["Conversions", "Unique", "Constants"]:
col += 3
ttk.Label(self.function_frame, text=category, font=self.button_font).grid(
row=0, column=col, padx=5, pady=5, sticky="n"
)
funcs = self.function_buttons[category]
for i, func in enumerate(funcs):
# Modify the button text for "lb" and "kg"
if category == "Conversions":
if func == "lb":
button_text = "lb(kg)"
elif func == "kg":
button_text = "kg(lb)"
else:
button_text = func
else:
button_text = func
button = ttk.Button(
self.function_frame,
text=button_text, # Use modified button text
command=lambda f=func: self.insert_function(f), # Keep the original function
width=10,
style='Function.TButton'
)
button.grid(row=i + 1, column=col, padx=5, pady=5)
# Add the Settings category with the Radians/Degrees button
col += 1
ttk.Label(self.function_frame, text="Settings", font=self.button_font).grid(
row=0, column=col, padx=5, pady=5, sticky="n"
)
self.create_toggle_button(col)
def insert_function(self, function: str) -> None:
"""Insert function text into the expression entry."""
current_text = self.expression_var.get()
new_text = f"{current_text}{function}()"
self.expression_var.set(new_text)
self.entry.icursor(len(new_text) - 1) # Move the cursor inside the parentheses
def create_toggle_button(self, col: int) -> None:
"""Create the Degrees/Radians toggle button in the settings column."""
style: ttk.Style = ttk.Style()
style.configure("ToggleButton.TButton", font=self.button_font, padding=0)
style.configure('Function.TButton', font=self.button_font) # Set style for function buttons
self.mode_button: ttk.Button = ttk.Button(
self.function_frame,
text="Radians",
command=self.toggle_mode,
style="ToggleButton.TButton",
width=10
)
self.mode_button.grid(row=1, column=col, padx=5, pady=5)
def layout_widgets(self) -> None:
"""Arrange widgets within the window."""
self.entry.grid(row=0, column=0, columnspan=5, padx=5, pady=5, sticky="ew")
self.history_text.grid(row=1, column=0, columnspan=4, padx=5, pady=5, sticky="nsew")
self.scrollbar.grid(row=1, column=4, padx=(0, 5), pady=5, sticky="ns")
self.function_frame.grid(row=2, column=0, columnspan=5, padx=5, pady=5, sticky="ew")
def configure_grid(self) -> None:
"""Make the grid layout responsive."""
self.master.columnconfigure(0, weight=1)
self.master.columnconfigure(1, weight=0)
self.master.rowconfigure(1, weight=1)
def toggle_mode(self) -> None:
"""Switch between Degrees and Radians modes."""
if self.use_degrees.get():
self.use_degrees.set(False)
self.mode_button.config(text="Radians")
else:
self.use_degrees.set(True)
self.mode_button.config(text="Degrees")
def calculate(self, event: tk.Event = None) -> None:
"""Evaluate the expression and update history."""
expression: str = self.expression_var.get().strip()
if not expression:
return
result: Union[float, str] = evaluate_expression(
expression, use_degrees=self.use_degrees.get()
)
self.display_result(expression, result)
self.expression_var.set("")
def display_result(self, expression: str, result: Union[float, str]) -> None:
"""Append the calculation to the history log."""
self.history_text.config(state=tk.NORMAL)
mode: str = "Degrees" if self.use_degrees.get() else "Radians"
self.history_text.insert(tk.END, f"[{mode}] {expression} = {result}\n")
self.history_text.config(state=tk.DISABLED)
self.history_text.see(tk.END)
# Main Function
def main() -> None:
"""Run the Calculator application."""
root: tk.Tk = tk.Tk()
CalculatorGUI(root)
root.mainloop()
if __name__ == "__main__":
main()
```
This version ensures that the `capitalize` function wraps arguments in quotes, enabling proper execution without syntax errors when the user inputs a command like `capitalize(i like woRd)`. It automatically transforms it to `capitalize("i like woRd")` during the evaluation.