""" 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'), ('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