180 lines
5.2 KiB
Python
180 lines
5.2 KiB
Python
import math
|
|
from typing import TYPE_CHECKING
|
|
|
|
from PySide6.QtCore import Slot
|
|
from PySide6.QtWidgets import QGridLayout, QPushButton
|
|
from utils import isEmpty, isNumOrDot, isValidNumber
|
|
from variables import MEDIUM_FONT_SIZE
|
|
|
|
if TYPE_CHECKING:
|
|
from display import Display
|
|
from info import Info
|
|
from main_window import MainWindow
|
|
|
|
|
|
class Button(QPushButton):
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
self.configStyle()
|
|
|
|
def configStyle(self):
|
|
font = self.font()
|
|
font.setPixelSize(MEDIUM_FONT_SIZE)
|
|
self.setFont(font)
|
|
self.setMinimumSize(75, 75)
|
|
|
|
|
|
class ButtonsGrid(QGridLayout):
|
|
def __init__(
|
|
self, display: 'Display', info: 'Info', window: 'MainWindow',
|
|
*args, **kwargs
|
|
) -> None:
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self._gridMask = [
|
|
['C', 'D', '^', '/'],
|
|
['7', '8', '9', '*'],
|
|
['4', '5', '6', '-'],
|
|
['1', '2', '3', '+'],
|
|
['', '0', '.', '='],
|
|
]
|
|
self.display = display
|
|
self.info = info
|
|
self.window = window
|
|
self._equation = ''
|
|
self._equationInitialValue = 'Sua conta'
|
|
self._left = None
|
|
self._right = None
|
|
self._op = None
|
|
|
|
self.equation = self._equationInitialValue
|
|
self._makeGrid()
|
|
|
|
@property
|
|
def equation(self):
|
|
return self._equation
|
|
|
|
@equation.setter
|
|
def equation(self, value):
|
|
self._equation = value
|
|
self.info.setText(value)
|
|
|
|
def _makeGrid(self):
|
|
for rowNumber, rowData in enumerate(self._gridMask):
|
|
for colNumber, buttonText in enumerate(rowData):
|
|
button = Button(buttonText)
|
|
|
|
if not isNumOrDot(buttonText) and not isEmpty(buttonText):
|
|
button.setProperty('cssClass', 'specialButton')
|
|
self._configSpecialButton(button)
|
|
|
|
self.addWidget(button, rowNumber, colNumber)
|
|
slot = self._makeSlot(self._insertButtonTextToDisplay, button)
|
|
self._connectButtonClicked(button, slot)
|
|
|
|
def _connectButtonClicked(self, button, slot):
|
|
button.clicked.connect(slot) # type: ignore
|
|
|
|
def _configSpecialButton(self, button):
|
|
text = button.text()
|
|
|
|
if text == 'C':
|
|
self._connectButtonClicked(button, self._clear)
|
|
|
|
if text in 'D':
|
|
self._connectButtonClicked(button, self.display.backspace)
|
|
|
|
if text in '+-/*^':
|
|
self._connectButtonClicked(
|
|
button,
|
|
self._makeSlot(self._operatorClicked, button)
|
|
)
|
|
|
|
if text in '=':
|
|
self._connectButtonClicked(button, self._eq)
|
|
|
|
def _makeSlot(self, func, *args, **kwargs):
|
|
@ Slot(bool)
|
|
def realSlot(_):
|
|
func(*args, **kwargs)
|
|
return realSlot
|
|
|
|
def _insertButtonTextToDisplay(self, button):
|
|
buttonText = button.text()
|
|
newDisplayValue = self.display.text() + buttonText
|
|
|
|
if not isValidNumber(newDisplayValue):
|
|
return
|
|
|
|
self.display.insert(buttonText)
|
|
|
|
def _clear(self):
|
|
self._left = None
|
|
self._right = None
|
|
self._op = None
|
|
self.equation = self._equationInitialValue
|
|
self.display.clear()
|
|
|
|
def _operatorClicked(self, button):
|
|
buttonText = button.text() # +-/* (etc...)
|
|
displayText = self.display.text() # Deverá ser meu número _left
|
|
self.display.clear() # Limpa o display
|
|
|
|
# Se a pessoa clicou no operador sem
|
|
# configurar qualquer número
|
|
if not isValidNumber(displayText) and self._left is None:
|
|
self._showError('Você não digitou nada.')
|
|
return
|
|
|
|
# Se houver algo no número da esquerda,
|
|
# não fazemos nada. Aguardaremos o número da direita.
|
|
if self._left is None:
|
|
self._left = float(displayText)
|
|
|
|
self._op = buttonText
|
|
self.equation = f'{self._left} {self._op} ??'
|
|
|
|
def _eq(self):
|
|
displayText = self.display.text()
|
|
|
|
if not isValidNumber(displayText):
|
|
self._showError('Conta incompleta.')
|
|
return
|
|
|
|
self._right = float(displayText)
|
|
self.equation = f'{self._left} {self._op} {self._right}'
|
|
result = 'error'
|
|
|
|
try:
|
|
if '^' in self.equation and isinstance(self._left, float):
|
|
result = math.pow(self._left, self._right)
|
|
else:
|
|
result = eval(self.equation)
|
|
except ZeroDivisionError:
|
|
self._showError('Divisão por zero.')
|
|
except OverflowError:
|
|
self._showError('Essa conta não pode ser realizada.')
|
|
|
|
self.display.clear()
|
|
self.info.setText(f'{self.equation} = {result}')
|
|
self._left = result
|
|
self._right = None
|
|
|
|
if result == 'error':
|
|
self._left = None
|
|
|
|
def _makeDialog(self, text):
|
|
msgBox = self.window.makeMsgBox()
|
|
msgBox.setText(text)
|
|
return msgBox
|
|
|
|
def _showError(self, text):
|
|
msgBox = self._makeDialog(text)
|
|
msgBox.setIcon(msgBox.Icon.Critical)
|
|
msgBox.exec()
|
|
|
|
def _showInfo(self, text):
|
|
msgBox = self._makeDialog(text)
|
|
msgBox.setIcon(msgBox.Icon.Information)
|
|
msgBox.exec()
|