-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
171 lines (140 loc) · 5.7 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from __future__ import annotations
import sys
from dataclasses import dataclass, field
from PyQt5 import QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QKeySequence
from PyQt5.QtWidgets import qApp
from cat.GUI import getStyles, SizePolicy
from cat.GUI.components.catWidgetMixins import getGUIColors
from cat.GUI.components.treeBuilders import DataListBuilder
from cat.GUI.icons import iconGetter
from cat.GUI.pythonGUI import PythonGUI, PythonGUIDialog
from cat.utils.utils import runLaterSafe
from cat.examples.calculator.core import *
@dataclass
class OperatorDef:
display: str
shortcut: str | None
operator: Curryable
OPERATOR_ADD = OperatorDef("+", '+', BinaryOperator("+", OperationType.SUM, lambda lhs, rhs: lhs + rhs))
OPERATOR_SUB = OperatorDef("-", '-', BinaryOperator("-", OperationType.SUM, lambda lhs, rhs: lhs - rhs))
OPERATOR_MUL = OperatorDef("×", '*', BinaryOperator("×", OperationType.MULTIPLICATIVE, lambda lhs, rhs: lhs * rhs))
OPERATOR_DIV = OperatorDef("÷", '/', BinaryOperator("÷", OperationType.MULTIPLICATIVE, lambda lhs, rhs: lhs / rhs if rhs != 0.0 else None))
OPERATOR_SQRT = OperatorDef("sqrt", None, UnaryOperator("sqrt", OperationType.FUNCTION, OperationKind.FUNCTION, lambda lhs: lhs**0.5 if lhs >= 0.0 else None))
OPERATOR_SQR = OperatorDef("x²", None, UnaryOperator("²", OperationType.POWER, OperationKind.POSTFIX, lambda lhs: lhs * lhs))
OPERATOR_INV = OperatorDef("1/x", None, UnaryOperator("1 /", OperationType.MULTIPLICATIVE, OperationKind.INFIX, lambda lhs: 1. / lhs if lhs != 0.0 else None))
class Calculator(PythonGUIDialog[PythonGUI]): # must be a dialog for default buttons to work
historyIcon = iconGetter('fa5s.history')
trashIcon = iconGetter('fa5.trash-alt')
appIcon = iconGetter('fa5s.calculator', options=[dict(color=getGUIColors().Highlight)])
def __init__(self):
super().__init__(PythonGUI)
self.isToolbarInTitleBar = True
self._showHistory: bool = False
self.core = CalculatorCore()
def OnToolbarGUI(self, gui: PythonGUI):
gui.elidedLabel(self.windowTitle() or qApp.applicationDisplayName())
gui.addHSpacer(0, SizePolicy.Expanding)
self._showHistory = gui.toggleSwitch(self._showHistory, icon=self.historyIcon, tip="show History")
def OnGUI(self, gui: PythonGUI):
with gui.hLayout():
with gui.vLayout(preventVStretch=True):
self.displayGUI(gui)
self.buttonsGUI(gui)
if self._showHistory:
with gui.vLayout():
self.memoryGUI(gui)
self.historyGUI(gui)
def displayGUI(self, gui: PythonGUI):
with gui.vPanel(seamless=True):
with gui.hLayout(seamless=True):
gui.addHSpacer(0, SizePolicy.Expanding) # right align
gui.label(self.core.lastOperationStr)
gui.textField(self.core.display, fullSize=True, readOnly=True, alignment=Qt.AlignRight, style=getStyles().title)
def buttonsGUI(self, gui: PythonGUI):
with gui.tableLayout(seamless=True) as tableLayout:
tableLayout.setNextItemSpan(2)
if gui.button("Clear All", shortcut="ESC"):
self.core.clearAll()
tableLayout.setNextItemSpan(2)
if gui.button("Clear"):
self.core.clear()
tableLayout.setNextItemSpan(2)
if gui.button("Backspace", shortcut="BACKSPACE"):
self.core.backspace()
tableLayout.advanceRow()
if gui.button("MC"):
self.core.clearMemory()
self._addDigitButton(gui, "7")
self._addDigitButton(gui, "8")
self._addDigitButton(gui, "9")
self._addOperatorButton(gui, OPERATOR_DIV)
self._addOperatorButton(gui, OPERATOR_SQRT)
tableLayout.advanceRow()
if gui.button("MR"):
self.core.readMemory()
self._addDigitButton(gui, "4")
self._addDigitButton(gui, "5")
self._addDigitButton(gui, "6")
self._addOperatorButton(gui, OPERATOR_MUL)
self._addOperatorButton(gui, OPERATOR_SQR)
tableLayout.advanceRow()
if gui.button("MS"):
self.core.setMemory()
self._addDigitButton(gui, "1")
self._addDigitButton(gui, "2")
self._addDigitButton(gui, "3")
self._addOperatorButton(gui, OPERATOR_SUB)
self._addOperatorButton(gui, OPERATOR_INV)
tableLayout.advanceRow()
if gui.button("M+"):
self.core.addToMemory()
if gui.button("±"):
self.core.changeSign()
self._addDigitButton(gui, "0")
if gui.button(".", shortcut="."):
self.core.decimalSeparator()
self._addOperatorButton(gui, OPERATOR_ADD)
if gui.button("=", default=True):
self.core.equals()
def _addDigitButton(self, gui: PythonGUI, digit: str):
if gui.button(digit, shortcut=digit):
self.core.addDigit(digit)
def _addOperatorButton(self, gui: PythonGUI, operator: OperatorDef):
if gui.button(operator.display, shortcut=operator.shortcut or QKeySequence()):
self.core.operator(operator.operator)
def memoryGUI(self, gui: PythonGUI):
with gui.vPanel(seamless=True):
gui.title("Memory")
gui.vSeparator()
gui.label(str(self.core.sumInMemory), selectable=True)
def historyGUI(self, gui: PythonGUI):
with gui.vPanel(seamless=True):
with gui.hLayout(seamless=True):
gui.title("History")
if gui.toolButton(icon=self.trashIcon, tip="Clears the history."):
self.core.history.clear()
gui.tree(
DataListBuilder(
self.core.history,
labelMaker=lambda opRes, c: (opRes.display, f"= {opRes.value}")[c],
iconMaker=None,
toolTipMaker=lambda opRes, c: str(opRes.display),
columnCount=2,
onDoubleClick=lambda opRes: self.core.setValue(opRes.value) or self.redraw(),
onCopy=lambda opRes: str(opRes.value),
getId=id
),
)
def start(argv):
app = QtWidgets.QApplication(argv)
QtWidgets.QApplication.setStyle("Fusion")
window = Calculator()
app.setApplicationName("Cat Calculator")
app.setWindowIcon(window.appIcon)
window.show()
runLaterSafe(0, lambda: window.resize(0, 0)) # reduce window size
app.exec_()
if __name__ == '__main__':
start(argv=sys.argv)