EU-Utility-Plugins-Repo/plugins/calculator/plugin.py

387 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
EU-Utility - Calculator Plugin
Standard calculator with Windows-style layout.
"""
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout,
QLineEdit, QPushButton, QLabel, QGridLayout
)
from PyQt6.QtCore import Qt
from plugins.base_plugin import BasePlugin
class CalculatorPlugin(BasePlugin):
"""Standard calculator with Windows-style layout."""
name = "Calculator"
version = "1.1.0"
author = "ImpulsiveFPS"
description = "Standard calculator"
hotkey = "ctrl+shift+c"
def initialize(self):
"""Setup calculator."""
self.current_value = "0"
self.stored_value = None
self.pending_op = None
self.memory = 0
self.start_new = True
def get_ui(self):
"""Create calculator UI with Windows layout."""
widget = QWidget()
layout = QVBoxLayout(widget)
layout.setSpacing(2)
# Title
title = QLabel("🧮 Calculator")
title.setStyleSheet("color: #4a9eff; font-size: 16px; font-weight: bold;")
layout.addWidget(title)
# Display
self.display = QLineEdit("0")
self.display.setAlignment(Qt.AlignmentFlag.AlignRight)
self.display.setStyleSheet("""
QLineEdit {
background-color: #1a1a1a;
color: white;
font-size: 32px;
font-family: 'Segoe UI', Arial;
padding: 15px;
border: none;
border-radius: 4px;
}
""")
self.display.setReadOnly(True)
layout.addWidget(self.display)
# Button grid - Windows Calculator Layout
grid = QGridLayout()
grid.setSpacing(2)
# Row 1: Memory buttons
mem_buttons = ['MC', 'MR', 'M+', 'M-', 'MS', 'M~']
for i, btn_text in enumerate(mem_buttons):
btn = self._create_button(btn_text, "#3a3a3a")
btn.clicked.connect(lambda checked, t=btn_text: self._on_memory(t))
grid.addWidget(btn, 0, i)
# Row 2: %, CE, C, ⌫
row2 = ['%', 'CE', 'C', '']
for i, btn_text in enumerate(row2):
btn = self._create_button(btn_text, "#3a3a3a")
btn.clicked.connect(lambda checked, t=btn_text: self._on_special(t))
grid.addWidget(btn, 1, i)
# Row 3: ¹/ₓ, x², ²√x, ÷
row3 = [('¹/ₓ', '1/x'), ('', 'sq'), ('²√x', 'sqrt'), '÷']
for i, item in enumerate(row3):
if isinstance(item, tuple):
text, op = item
else:
text = op = item
btn = self._create_button(text, "#3a3a3a")
btn.clicked.connect(lambda checked, o=op: self._on_operator(o))
grid.addWidget(btn, 2, i)
# Row 4: 7, 8, 9, ×
row4 = ['7', '8', '9', '×']
for i, btn_text in enumerate(row4):
btn = self._create_button(btn_text, "#2a2a2a", is_number=btn_text not in ['×'])
if btn_text == '×':
btn.clicked.connect(lambda checked: self._on_operator('*'))
else:
btn.clicked.connect(lambda checked, t=btn_text: self._on_number(t))
grid.addWidget(btn, 3, i)
# Row 5: 4, 5, 6, -
row5 = ['4', '5', '6', '-']
for i, btn_text in enumerate(row5):
btn = self._create_button(btn_text, "#2a2a2a", is_number=btn_text not in ['-'])
if btn_text == '-':
btn.clicked.connect(lambda checked: self._on_operator('-'))
else:
btn.clicked.connect(lambda checked, t=btn_text: self._on_number(t))
grid.addWidget(btn, 4, i)
# Row 6: 1, 2, 3, +
row6 = ['1', '2', '3', '+']
for i, btn_text in enumerate(row6):
btn = self._create_button(btn_text, "#2a2a2a", is_number=btn_text not in ['+'])
if btn_text == '+':
btn.clicked.connect(lambda checked: self._on_operator('+'))
else:
btn.clicked.connect(lambda checked, t=btn_text: self._on_number(t))
grid.addWidget(btn, 5, i)
# Row 7: +/-, 0, ., =
row7 = [('±', '+/-'), '0', '.', '=']
for i, item in enumerate(row7):
if isinstance(item, tuple):
text, val = item
else:
text = val = item
if val == '=':
btn = self._create_button(text, "#0078d4", text_color="white") # Blue equals
else:
btn = self._create_button(text, "#2a2a2a", is_number=True)
if val == '+/-':
btn.clicked.connect(self._on_negate)
elif val == '.':
btn.clicked.connect(self._on_decimal)
elif val == '=':
btn.clicked.connect(self._on_equals)
else:
btn.clicked.connect(lambda checked, t=val: self._on_number(t))
grid.addWidget(btn, 6, i)
# Set column stretch
for i in range(4):
grid.setColumnStretch(i, 1)
# Set row stretch
for i in range(7):
grid.setRowStretch(i, 1)
layout.addLayout(grid)
layout.addStretch()
return widget
def _create_button(self, text, bg_color, is_number=False, text_color="#ffffff"):
"""Create a calculator button."""
btn = QPushButton(text)
btn.setMinimumSize(60, 45)
if is_number:
font_size = "18px"
font_weight = "normal"
else:
font_size = "14px"
font_weight = "normal"
btn.setStyleSheet(f"""
QPushButton {{
background-color: {bg_color};
color: {text_color};
font-size: {font_size};
font-weight: {font_weight};
border: none;
border-radius: 4px;
}}
QPushButton:hover {{
background-color: {self._lighten(bg_color)};
}}
QPushButton:pressed {{
background-color: {self._darken(bg_color)};
}}
""")
return btn
def _lighten(self, color):
"""Lighten a hex color slightly."""
# Simple approximation - increase each component
if color == "#2a2a2a":
return "#3a3a3a"
elif color == "#3a3a3a":
return "#4a4a4a"
elif color == "#0078d4":
return "#1084e0"
return color
def _darken(self, color):
"""Darken a hex color slightly."""
if color == "#2a2a2a":
return "#1a1a1a"
elif color == "#3a3a3a":
return "#2a2a2a"
elif color == "#0078d4":
return "#006cbd"
return color
def _on_number(self, num):
"""Handle number button press."""
if self.start_new:
self.current_value = num
self.start_new = False
else:
if self.current_value == "0":
self.current_value = num
else:
self.current_value += num
self._update_display()
def _on_decimal(self):
"""Handle decimal point."""
if self.start_new:
self.current_value = "0."
self.start_new = False
elif "." not in self.current_value:
self.current_value += "."
self._update_display()
def _on_operator(self, op):
"""Handle operator button."""
try:
current = float(self.current_value)
if op == "1/x":
result = 1 / current
self.current_value = self._format_result(result)
self.start_new = True
elif op == "sq":
result = current ** 2
self.current_value = self._format_result(result)
self.start_new = True
elif op == "sqrt":
import math
result = math.sqrt(current)
self.current_value = self._format_result(result)
self.start_new = True
else:
# Binary operators
if self.pending_op and not self.start_new:
self._calculate()
self.stored_value = float(self.current_value)
self.pending_op = op
self.start_new = True
self._update_display()
except Exception:
self.current_value = "Error"
self._update_display()
self.start_new = True
def _on_special(self, op):
"""Handle special buttons (%, CE, C, backspace)."""
if op == 'C':
# Clear all
self.current_value = "0"
self.stored_value = None
self.pending_op = None
self.start_new = True
elif op == 'CE':
# Clear entry
self.current_value = "0"
self.start_new = True
elif op == '':
# Backspace
if len(self.current_value) > 1:
self.current_value = self.current_value[:-1]
else:
self.current_value = "0"
elif op == '%':
# Percent
try:
result = float(self.current_value) / 100
self.current_value = self._format_result(result)
self.start_new = True
except:
self.current_value = "Error"
self.start_new = True
self._update_display()
def _on_memory(self, op):
"""Handle memory operations."""
try:
current = float(self.current_value)
if op == 'MC':
self.memory = 0
elif op == 'MR':
self.current_value = self._format_result(self.memory)
self.start_new = True
elif op == 'M+':
self.memory += current
elif op == 'M-':
self.memory -= current
elif op == 'MS':
self.memory = current
elif op == 'M~':
# Memory clear (same as MC)
self.memory = 0
self._update_display()
except:
pass
def _on_negate(self):
"""Toggle sign."""
try:
current = float(self.current_value)
result = -current
self.current_value = self._format_result(result)
self._update_display()
except:
pass
def _on_equals(self):
"""Calculate result."""
self._calculate()
self.pending_op = None
self.stored_value = None
self.start_new = True
def _calculate(self):
"""Perform pending calculation."""
if self.pending_op and self.stored_value is not None:
try:
current = float(self.current_value)
if self.pending_op == '+':
result = self.stored_value + current
elif self.pending_op == '-':
result = self.stored_value - current
elif self.pending_op == '*':
result = self.stored_value * current
elif self.pending_op == '÷':
if current != 0:
result = self.stored_value / current
else:
result = "Error"
else:
return
self.current_value = self._format_result(result)
self._update_display()
except:
self.current_value = "Error"
self._update_display()
def _format_result(self, result):
"""Format calculation result."""
if isinstance(result, str):
return result
# Check if it's essentially an integer
if result == int(result):
return str(int(result))
# Format with reasonable precision
formatted = f"{result:.10f}"
# Remove trailing zeros
formatted = formatted.rstrip('0').rstrip('.')
# Limit length
if len(formatted) > 12:
formatted = f"{result:.6e}"
return formatted
def _update_display(self):
"""Update the display."""
self.display.setText(self.current_value)
def on_hotkey(self):
"""Focus calculator when hotkey pressed."""
pass