From c997e84d0e21443eaf40bc4a55975347206dd3ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Maury?= Date: Fri, 25 Aug 2023 17:37:21 +0200 Subject: [PATCH 01/22] git guru under hierosme direction --- .../Calculator.app/Resources/CHANGELOGS.md | 14 ++ .../Calculator.app/Resources/calculator.py | 158 +++++++++++++++--- 2 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 Utilities/Calculator.app/Resources/CHANGELOGS.md diff --git a/Utilities/Calculator.app/Resources/CHANGELOGS.md b/Utilities/Calculator.app/Resources/CHANGELOGS.md new file mode 100644 index 000000000..26f2fc75f --- /dev/null +++ b/Utilities/Calculator.app/Resources/CHANGELOGS.md @@ -0,0 +1,14 @@ +# helloSystem Calculator +## v0.2rc1 +* Change size to 210x280 +* Spanning for 0 and = +* Introduse memory line MC, M+, M-, MR +* Introduse ± key +* Fit with layout of OsX +* Use true operator symbol and convert them before the eval +## v0.1mod1 +* Clean up +## v0.1 +* Initial commit + + diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 2aa45bf9a..525aa955b 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -3,6 +3,7 @@ # Calculator Construction Set # for a fun story, see # https://www.folklore.org/StoryView.py?story=Calculator_Construction_Set.txt +# https://doc.qt.io/qtforpython-5/overviews/qtwidgets-widgets-calculator-example.html#calculator-example # Based on PyCalc # https://github.com/realpython/materials/tree/master/pyqt-calculator-tutorial/pycalc @@ -51,11 +52,16 @@ from PyQt5.QtWidgets import qApp from PyQt5.QtGui import QPixmap -__version__ = "0.1-mod1" -__author__ = "Leodanis Pozo Ramos & Contributors" +__version__ = "0.2" +__author__ = [ + "Leodanis Pozo Ramos & Contributors", + "Jérôme ORNECH alias Hierosme" + ] ERROR_MSG = "ERROR" - +TILE_WIDTH = 36 +TILE_HEIGHT = 34 +TILE_SPACING = 3 # Create a subclass of QMainWindow to setup the calculator's GUI class PyCalcUi(QMainWindow): @@ -66,7 +72,11 @@ def __init__(self): super().__init__() # Set some main window's properties self.setWindowTitle("Calculator") - self.setFixedSize(160, 230) + # Strange effect with hellosystem theme + # self.setFixedSize( + # (TILE_WIDTH * 4) + ( TILE_SPACING * 9), + # (TILE_HEIGHT * 7) + (TILE_SPACING * 9) + # ) # Set the central widget and the general layout self.generalLayout = QVBoxLayout() self._centralWidget = QWidget(self) @@ -92,34 +102,52 @@ def _createButtons(self): """Create the buttons.""" self.buttons = {} buttonsLayout = QGridLayout() + buttonsLayout.setSpacing(TILE_SPACING) # Button text | position on the QGridLayout buttons = { - "7": (1, 0), - "8": (1, 1), - "9": (1, 2), - "/": (0, 3), - "C": (0, 0), - "4": (2, 0), - "5": (2, 1), - "6": (2, 2), - "*": (1, 3), - "(": (0, 1), - "1": (3, 0), - "2": (3, 1), - "3": (3, 2), - "-": (2, 3), - ")": (0, 2), - "0": (4, 0), - # "00": (3, 1), - ".": (4, 2), + # First Line + "MC": (0, 0), + "M+": (0, 1), + "M-": (0, 2), + "MR": (0, 3), + # Second line + "C": (1, 0), + "±": (1, 1), + "÷": (1, 2), + "×": (1, 3), + # Third line + "7": (2, 0), + "8": (2, 1), + "9": (2, 2), + "−": (2, 3), + # etc ... + "4": (3, 0), + "5": (3, 1), + "6": (3, 2), "+": (3, 3), + "1": (4, 0), + "2": (4, 1), + "3": (4, 2), "=": (4, 3), + # the last line got only 2 buttons + "0": (5, 0), + ".": (5, 2), } # Create the buttons and add them to the grid layout for btnText, pos in buttons.items(): self.buttons[btnText] = QPushButton(btnText) - self.buttons[btnText].setFixedSize(34, 36) - buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1]) + # Spanning management + self.buttons[btnText].setMinimumWidth(TILE_WIDTH) + self.buttons[btnText].setMinimumHeight(TILE_HEIGHT) + if btnText == "=": + self.buttons[btnText].setMinimumHeight((TILE_HEIGHT * 2) + TILE_SPACING) + # helloSystem can t make vertical padding on a button + buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 2, 1) + elif btnText == "0": + self.buttons[btnText].setMinimumWidth(TILE_WIDTH * 2) + buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 2) + else: + buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 1) # Add buttonsLayout to the general layout self.generalLayout.addLayout(buttonsLayout) @@ -169,6 +197,12 @@ def _showAbout(self): # Create a Model to handle the calculator's operation def evaluateExpression(expression): """Evaluate an expression.""" + if "÷" in expression: + expression = expression.replace("÷", "/") + if "×" in expression: + expression = expression.replace("×", "*") + if "−" in expression: + expression = expression.replace("−", "-") try: result = str(eval(expression, {}, {})) except Exception: @@ -185,14 +219,83 @@ def __init__(self, model, view): """Controller initializer.""" self._evaluate = model self._view = view + self._memory = None + self.memory = None # Connect signals and slots self._connectSignals() + @property + def memory(self): + return self._memory + + @memory.setter + def memory(self, value): + if value is None: + self._memory = None + return + if self.memory != value: + self._memory = value + def _calculateResult(self): """Evaluate expressions.""" result = self._evaluate(expression=self._view.displayText()) self._view.setDisplayText(result) + def _memory_clear(self): + """Clear momory by set value to None""" + self.memory = None + self._view.display.setFocus() + + def _memory_substact(self): + """Add the result of display expression to the memory""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + if self.memory is None: + self.memory = 0 + if "." in result: + if self.memory: + self.memory -= float(result) + else: + self.memory -= int(result) + self._view.display.setFocus() + + def _memory_add(self): + """Substract the result of display expression to the memory""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + if self.memory is None: + self.memory = 0 + if "." in result: + self.memory += float(result) + else: + self.memory += int(result) + self._view.display.setFocus() + + def _memory_print(self): + """If memory value, flush the display with it value""" + if self.memory is not None: + self._view.clearDisplay() + self._view.setDisplayText("%s" % (self.memory)) + else: + self._view.display.setFocus() + + def _neg(self): + """Evaluate expressions value and display the negative value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + if "." in result: + if float(result) > 0: + result = -abs(float(result)) + else: + result = abs(float(result)) + else: + if int(result) > 0: + result = -abs(int(result)) + else: + result = abs(int(result)) + + self._view.setDisplayText(str(result)) + def _buildExpression(self, sub_exp): """Build expression.""" if self._view.displayText() == ERROR_MSG: @@ -204,12 +307,17 @@ def _buildExpression(self, sub_exp): def _connectSignals(self): """Connect signals and slots.""" for btnText, btn in self._view.buttons.items(): - if btnText not in {"=", "C"}: + if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±" }: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.buttons["="].clicked.connect(self._calculateResult) self._view.display.returnPressed.connect(self._calculateResult) self._view.buttons["C"].clicked.connect(self._view.clearDisplay) + self._view.buttons["±"].clicked.connect(self._neg) + self._view.buttons["MC"].clicked.connect(self._memory_clear) + self._view.buttons["M+"].clicked.connect(self._memory_add) + self._view.buttons["M-"].clicked.connect(self._memory_substact) + self._view.buttons["MR"].clicked.connect(self._memory_print) """self._view.display.escapePressed.connect(self._view.clearDisplay)""" From 12add6db4f66a42ed75a08ea3e232f8fa2edbe4b Mon Sep 17 00:00:00 2001 From: probonopd Date: Sat, 26 Aug 2023 09:05:27 +0200 Subject: [PATCH 02/22] Fix spelling and clean up --- Utilities/Calculator.app/Resources/CHANGELOGS.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/Utilities/Calculator.app/Resources/CHANGELOGS.md b/Utilities/Calculator.app/Resources/CHANGELOGS.md index 26f2fc75f..3218a0637 100644 --- a/Utilities/Calculator.app/Resources/CHANGELOGS.md +++ b/Utilities/Calculator.app/Resources/CHANGELOGS.md @@ -1,14 +1,11 @@ # helloSystem Calculator -## v0.2rc1 -* Change size to 210x280 +## v0.2 +* Change size * Spanning for 0 and = -* Introduse memory line MC, M+, M-, MR -* Introduse ± key -* Fit with layout of OsX +* Introduce memory line MC, M+, M-, MR +* Introduce ± key +* Optimize layout * Use true operator symbol and convert them before the eval -## v0.1mod1 * Clean up ## v0.1 * Initial commit - - From 6c2449012d82a18858dedf584bc8a6d65a158ed7 Mon Sep 17 00:00:00 2001 From: probonopd Date: Sat, 26 Aug 2023 09:23:56 +0200 Subject: [PATCH 03/22] Copyright --- Utilities/Calculator.app/Resources/calculator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 525aa955b..b516db9fe 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -12,6 +12,7 @@ # # Copyright (c) 2019, Leodanis Pozo Ramos # Portions Copyright (c) 2020, Simon Peter +# Portions Copyright (c) 2023, Jérôme Ornech alias Hierosme # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal From cbf7af0acb2939d74b007fb00b3ada75f548edb5 Mon Sep 17 00:00:00 2001 From: probonopd Date: Sat, 26 Aug 2023 09:24:12 +0200 Subject: [PATCH 04/22] Copyright --- Utilities/Calculator.app/Resources/LICENSE | 1 + 1 file changed, 1 insertion(+) diff --git a/Utilities/Calculator.app/Resources/LICENSE b/Utilities/Calculator.app/Resources/LICENSE index ab22a4e1f..792f2012e 100644 --- a/Utilities/Calculator.app/Resources/LICENSE +++ b/Utilities/Calculator.app/Resources/LICENSE @@ -5,6 +5,7 @@ The MIT License (MIT) Copyright (c) 2019, Leodanis Pozo Ramos Portions Copyright (c) 2020, Simon Peter +Portions Copyright (c) 2023, Jérôme Ornech alias Hierosme Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From e590f1f56cfc56283cdff7f182b1b03f3fad1c8b Mon Sep 17 00:00:00 2001 From: Tuuux Date: Fri, 1 Sep 2023 19:45:46 +0200 Subject: [PATCH 05/22] Add UI --- .../Calculator.app/Resources/calculator.py | 151 +++--- .../Calculator.app/Resources/main_window.ui | 450 ++++++++++++++++++ .../Resources/main_window_ui.py | 251 ++++++++++ .../Resources/widget_calculator_button.py | 109 +++++ 4 files changed, 905 insertions(+), 56 deletions(-) create mode 100644 Utilities/Calculator.app/Resources/main_window.ui create mode 100644 Utilities/Calculator.app/Resources/main_window_ui.py create mode 100644 Utilities/Calculator.app/Resources/widget_calculator_button.py diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index b516db9fe..12c0d02b0 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -51,26 +51,32 @@ from PyQt5.QtWidgets import QAction from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import qApp -from PyQt5.QtGui import QPixmap +from PyQt5.QtGui import QPixmap, QColor, QIcon + +# The Main Window +from main_window_ui import Ui_MainWindow + +from widget_calculator_button import CalculatorButton __version__ = "0.2" __author__ = [ - "Leodanis Pozo Ramos & Contributors", - "Jérôme ORNECH alias Hierosme" - ] + "Leodanis Pozo Ramos & Contributors", + "Jérôme ORNECH alias Hierosme" +] ERROR_MSG = "ERROR" TILE_WIDTH = 36 TILE_HEIGHT = 34 TILE_SPACING = 3 + # Create a subclass of QMainWindow to setup the calculator's GUI -class PyCalcUi(QMainWindow): +class Window(QMainWindow, Ui_MainWindow): """PyCalc's View (GUI).""" - def __init__(self): - """View initializer.""" - super().__init__() + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self) # Set some main window's properties self.setWindowTitle("Calculator") # Strange effect with hellosystem theme @@ -79,31 +85,33 @@ def __init__(self): # (TILE_HEIGHT * 7) + (TILE_SPACING * 9) # ) # Set the central widget and the general layout - self.generalLayout = QVBoxLayout() - self._centralWidget = QWidget(self) - self.setCentralWidget(self._centralWidget) - self._centralWidget.setLayout(self.generalLayout) + # self.generalLayout = QVBoxLayout() + # self._centralWidget = QWidget(self) + # self.setCentralWidget(self._centralWidget) + # self._centralWidget.setLayout(self.generalLayout) # Create the display and the buttons - self._createDisplay() + self._createButtons() - self._showMenu() - - def _createDisplay(self): - """Create the display.""" - # Create the display widget - self.display = QLineEdit() - # Set some display's properties - # self.display.setFixedHeight(35) + + self.setupInitialState() + self.connectSignalsSlots() + self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "Calculator.png"))) + # self._showMenu() + + def setupInitialState(self): self.display.setAlignment(Qt.AlignRight) - self.display.setReadOnly(False) - # Add the display to the general layout - self.generalLayout.addWidget(self.display) + # self.display.setReadOnly(False) + + def connectSignalsSlots(self): + + # Menu and ToolBar + self.ActionMenuHelpAbout.triggered.connect(self._showAboutDialog) def _createButtons(self): """Create the buttons.""" self.buttons = {} - buttonsLayout = QGridLayout() - buttonsLayout.setSpacing(TILE_SPACING) + # self.basic_buttons_layout = QGridLayout() + self.basic_buttons_layout.setSpacing(TILE_SPACING) # Button text | position on the QGridLayout buttons = { # First Line @@ -136,21 +144,35 @@ def _createButtons(self): } # Create the buttons and add them to the grid layout for btnText, pos in buttons.items(): - self.buttons[btnText] = QPushButton(btnText) + self.buttons[btnText] = CalculatorButton() + self.buttons[btnText].setText(btnText) # Spanning management self.buttons[btnText].setMinimumWidth(TILE_WIDTH) self.buttons[btnText].setMinimumHeight(TILE_HEIGHT) + # Apply Color + if btnText in ["=", "−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: + self.buttons[btnText].setColor(QColor(85, 85, 85)) + self.buttons[btnText].setColorFont(QColor(208, 208, 208)) + elif btnText == "C": + self.buttons[btnText].setColor(QColor(255, 140, 55)) + self.buttons[btnText].setColorFont(QColor(255, 255, 255)) + else: + self.buttons[btnText].setColor(QColor(224, 224, 224)) + self.buttons[btnText].setColorFont(QColor(16, 16, 16)) + if btnText == "=": + + self.buttons[btnText].setMinimumHeight((TILE_HEIGHT * 2) + TILE_SPACING) # helloSystem can t make vertical padding on a button - buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 2, 1) + self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 2, 1) elif btnText == "0": self.buttons[btnText].setMinimumWidth(TILE_WIDTH * 2) - buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 2) + self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 2) else: - buttonsLayout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 1) - # Add buttonsLayout to the general layout - self.generalLayout.addLayout(buttonsLayout) + self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 1) + # # Add buttonsLayout to the general layout + # self.generalLayout.addLayout(self.basic_buttons_layout) def setDisplayText(self, text): """Set display's text.""" @@ -179,22 +201,30 @@ def _showMenu(self): aboutAct.triggered.connect(self._showAbout) helpMenu = menubar.addMenu('&Help') helpMenu.addAction(aboutAct) - - def _showAbout(self): - print("showDialog") + + def _showAboutDialog(self): msg = QMessageBox() msg.setWindowTitle("About") - msg.setIconPixmap(QPixmap(os.path.dirname(__file__) + "/Calculator.png")) - candidates = ["COPYRIGHT", "COPYING", "LICENSE"] - for candidate in candidates: - if os.path.exists(os.path.dirname(__file__) + "/" + candidate): - with open(os.path.dirname(__file__) + "/" + candidate, 'r') as file: + msg.setIconPixmap( + QPixmap( + os.path.join( + os.path.dirname(__file__), + "Calculator.png" + ) + ).scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation) + ) + for candidate in ["COPYRIGHT", "COPYING", "LICENSE"]: + if os.path.exists(os.path.join(os.path.dirname(__file__), candidate)): + with open(os.path.join(os.path.dirname(__file__), candidate), 'r') as file: data = file.read() msg.setDetailedText(data) msg.setText("

Calculator

") - msg.setInformativeText("A simple calculator application written in PyQt5

https://github.com/helloSystem/Utilities") + msg.setInformativeText( + "A simple calculator application written in PyQt5

" + "https://github.com/helloSystem/Utilities") msg.exec() + # Create a Model to handle the calculator's operation def evaluateExpression(expression): """Evaluate an expression.""" @@ -294,7 +324,7 @@ def _neg(self): result = -abs(int(result)) else: result = abs(int(result)) - + self._view.setDisplayText(str(result)) def _buildExpression(self, sub_exp): @@ -308,7 +338,7 @@ def _buildExpression(self, sub_exp): def _connectSignals(self): """Connect signals and slots.""" for btnText, btn in self._view.buttons.items(): - if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±" }: + if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±"}: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.buttons["="].clicked.connect(self._calculateResult) @@ -323,19 +353,28 @@ def _connectSignals(self): # Client code -def main(): - """Main function.""" - # Create an instance of `QApplication` - pycalc = QApplication(sys.argv) - # Show the calculator's GUI - view = PyCalcUi() - view.show() - # Create instances of the model and the controller +# def main(): +# """Main function.""" +# # Create an instance of `QApplication` +# pycalc = QApplication(sys.argv) +# # Show the calculator's GUI +# view = PyCalcUi() +# view.show() +# # Create instances of the model and the controller +# model = evaluateExpression +# PyCalcCtrl(model=model, view=view) +# # Execute calculator's main loop +# sys.exit(pycalc.exec_()) +# +# +# if __name__ == "__main__": +# main() +if __name__ == "__main__": + app = QApplication(sys.argv) + win = Window() + win.show() model = evaluateExpression - PyCalcCtrl(model=model, view=view) + PyCalcCtrl(model=model, view=win) # Execute calculator's main loop - sys.exit(pycalc.exec_()) - -if __name__ == "__main__": - main() + sys.exit(app.exec()) diff --git a/Utilities/Calculator.app/Resources/main_window.ui b/Utilities/Calculator.app/Resources/main_window.ui new file mode 100644 index 000000000..66e206f68 --- /dev/null +++ b/Utilities/Calculator.app/Resources/main_window.ui @@ -0,0 +1,450 @@ + + + MainWindow + + + + 0 + 0 + 282 + 185 + + + + MainWindow + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + background-color: rgb(74, 74, 74); +color: rgb(228, 228, 228); + + + QFrame::WinPanel + + + QFrame::Sunken + + + 2 + + + 2 + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 48 + + + + + 16777215 + 48 + + + + + Nimbus Mono PS + 20 + + + + false + + + false + + + + + + + + + + + + + + 0 + 0 + + + + 1 + + + 0 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 3 + + + 0 + + + 3 + + + 3 + + + 0 + + + + + + + + + + + + + + 0 + 0 + 282 + 28 + + + + + File + + + + + + + + + + View + + + + + + + + + + + + + + Help + + + + + + Convert + + + + + + + + + + + + + + + + + + + + + + + + + Close calculator Window + + + Ctrl+W + + + + + Save Tape As... + + + Ctrl+Shift+S + + + + + Page Setup... + + + Ctrl+Shift+P + + + + + Print Tape... + + + Ctrl+P + + + + + Basic + + + Ctrl+1 + + + + + Scientific + + + Ctrl+2 + + + + + Programmer + + + Ctrl+3 + + + + + Show Paper Tape + + + + + RPN + + + + + Precision + + + + + false + + + Recent Conversions + + + + + false + + + Area... + + + + + false + + + Currency... + + + + + false + + + Energy or Work... + + + + + false + + + Temperature... + + + Temperature + + + + + false + + + Length... + + + + + false + + + Weigths and Masses... + + + + + false + + + Speed... + + + + + false + + + Pressure... + + + + + false + + + Power... + + + + + false + + + Volume... + + + + + false + + + Update Currency Exchanges Rates... + + + + + false + + + Last Update: + + + + + About + + + + + + diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py new file mode 100644 index 000000000..cbe1fc446 --- /dev/null +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -0,0 +1,251 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file './main_window.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(282, 185) + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setSpacing(0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.widget_19 = QtWidgets.QWidget(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.widget_19.sizePolicy().hasHeightForWidth()) + self.widget_19.setSizePolicy(sizePolicy) + self.widget_19.setObjectName("widget_19") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.widget_19) + self.verticalLayout_3.setContentsMargins(3, 3, 3, 3) + self.verticalLayout_3.setSpacing(0) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.frame = QtWidgets.QFrame(self.widget_19) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setStyleSheet("background-color: rgb(74, 74, 74);\n" +"color: rgb(228, 228, 228);") + self.frame.setFrameShape(QtWidgets.QFrame.WinPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) + self.frame.setLineWidth(2) + self.frame.setMidLineWidth(2) + self.frame.setObjectName("frame") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame) + self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_4.setSpacing(0) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.display = QtWidgets.QLineEdit(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.display.sizePolicy().hasHeightForWidth()) + self.display.setSizePolicy(sizePolicy) + self.display.setMinimumSize(QtCore.QSize(0, 48)) + self.display.setMaximumSize(QtCore.QSize(16777215, 48)) + font = QtGui.QFont() + font.setFamily("Nimbus Mono PS") + font.setPointSize(20) + self.display.setFont(font) + self.display.setAutoFillBackground(False) + self.display.setFrame(False) + self.display.setObjectName("display") + self.verticalLayout_4.addWidget(self.display) + self.verticalLayout_3.addWidget(self.frame) + self.verticalLayout_2.addWidget(self.widget_19) + self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth()) + self.stackedWidget.setSizePolicy(sizePolicy) + self.stackedWidget.setLineWidth(1) + self.stackedWidget.setObjectName("stackedWidget") + self.basic = QtWidgets.QWidget() + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.basic.sizePolicy().hasHeightForWidth()) + self.basic.setSizePolicy(sizePolicy) + self.basic.setObjectName("basic") + self.verticalLayout = QtWidgets.QVBoxLayout(self.basic) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setSpacing(0) + self.verticalLayout.setObjectName("verticalLayout") + self.basic_buttons_layout = QtWidgets.QGridLayout() + self.basic_buttons_layout.setContentsMargins(3, 0, 3, 3) + self.basic_buttons_layout.setSpacing(0) + self.basic_buttons_layout.setObjectName("basic_buttons_layout") + self.verticalLayout.addLayout(self.basic_buttons_layout) + self.stackedWidget.addWidget(self.basic) + self.page_2 = QtWidgets.QWidget() + self.page_2.setObjectName("page_2") + self.stackedWidget.addWidget(self.page_2) + self.verticalLayout_2.addWidget(self.stackedWidget) + self.verticalLayout_2.setStretch(1, 1) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 282, 28)) + self.menubar.setObjectName("menubar") + self.menuFile = QtWidgets.QMenu(self.menubar) + self.menuFile.setObjectName("menuFile") + self.menuView = QtWidgets.QMenu(self.menubar) + self.menuView.setObjectName("menuView") + self.menuHelp = QtWidgets.QMenu(self.menubar) + self.menuHelp.setObjectName("menuHelp") + self.menuConvert = QtWidgets.QMenu(self.menubar) + self.menuConvert.setObjectName("menuConvert") + MainWindow.setMenuBar(self.menubar) + self.actionClose_Calculator_Window = QtWidgets.QAction(MainWindow) + self.actionClose_Calculator_Window.setObjectName("actionClose_Calculator_Window") + self.actionSave_Tape_As = QtWidgets.QAction(MainWindow) + self.actionSave_Tape_As.setObjectName("actionSave_Tape_As") + self.actionPage_Setup = QtWidgets.QAction(MainWindow) + self.actionPage_Setup.setObjectName("actionPage_Setup") + self.actionPrint_Tape = QtWidgets.QAction(MainWindow) + self.actionPrint_Tape.setObjectName("actionPrint_Tape") + self.actionView_Basic = QtWidgets.QAction(MainWindow) + self.actionView_Basic.setObjectName("actionView_Basic") + self.actionView_Scientific = QtWidgets.QAction(MainWindow) + self.actionView_Scientific.setObjectName("actionView_Scientific") + self.actionView_Programation = QtWidgets.QAction(MainWindow) + self.actionView_Programation.setObjectName("actionView_Programation") + self.actionView_Tape = QtWidgets.QAction(MainWindow) + self.actionView_Tape.setObjectName("actionView_Tape") + self.actionView_RPN = QtWidgets.QAction(MainWindow) + self.actionView_RPN.setObjectName("actionView_RPN") + self.actionView_Precision = QtWidgets.QAction(MainWindow) + self.actionView_Precision.setObjectName("actionView_Precision") + self.actionConvert_Recent_Convertions = QtWidgets.QAction(MainWindow) + self.actionConvert_Recent_Convertions.setEnabled(False) + self.actionConvert_Recent_Convertions.setObjectName("actionConvert_Recent_Convertions") + self.actionConvert_Area = QtWidgets.QAction(MainWindow) + self.actionConvert_Area.setEnabled(False) + self.actionConvert_Area.setObjectName("actionConvert_Area") + self.actionConvert_Currency = QtWidgets.QAction(MainWindow) + self.actionConvert_Currency.setEnabled(False) + self.actionConvert_Currency.setObjectName("actionConvert_Currency") + self.actionConvert_Energy_or_Work = QtWidgets.QAction(MainWindow) + self.actionConvert_Energy_or_Work.setEnabled(False) + self.actionConvert_Energy_or_Work.setObjectName("actionConvert_Energy_or_Work") + self.actionConvert_Temperature = QtWidgets.QAction(MainWindow) + self.actionConvert_Temperature.setEnabled(False) + self.actionConvert_Temperature.setObjectName("actionConvert_Temperature") + self.actionConvert_Length = QtWidgets.QAction(MainWindow) + self.actionConvert_Length.setEnabled(False) + self.actionConvert_Length.setObjectName("actionConvert_Length") + self.actionConvert_Weigths_and_Masses = QtWidgets.QAction(MainWindow) + self.actionConvert_Weigths_and_Masses.setEnabled(False) + self.actionConvert_Weigths_and_Masses.setObjectName("actionConvert_Weigths_and_Masses") + self.actionConvert_Speed = QtWidgets.QAction(MainWindow) + self.actionConvert_Speed.setEnabled(False) + self.actionConvert_Speed.setObjectName("actionConvert_Speed") + self.actionConvert_Pressure = QtWidgets.QAction(MainWindow) + self.actionConvert_Pressure.setEnabled(False) + self.actionConvert_Pressure.setObjectName("actionConvert_Pressure") + self.actionConvert_Power = QtWidgets.QAction(MainWindow) + self.actionConvert_Power.setEnabled(False) + self.actionConvert_Power.setObjectName("actionConvert_Power") + self.actionConvert_Volume = QtWidgets.QAction(MainWindow) + self.actionConvert_Volume.setEnabled(False) + self.actionConvert_Volume.setObjectName("actionConvert_Volume") + self.actionConvert_Update_currencies_rate = QtWidgets.QAction(MainWindow) + self.actionConvert_Update_currencies_rate.setEnabled(False) + self.actionConvert_Update_currencies_rate.setObjectName("actionConvert_Update_currencies_rate") + self.actionConvert_Last_Update = QtWidgets.QAction(MainWindow) + self.actionConvert_Last_Update.setEnabled(False) + self.actionConvert_Last_Update.setObjectName("actionConvert_Last_Update") + self.ActionMenuHelpAbout = QtWidgets.QAction(MainWindow) + self.ActionMenuHelpAbout.setObjectName("ActionMenuHelpAbout") + self.menuFile.addAction(self.actionClose_Calculator_Window) + self.menuFile.addAction(self.actionSave_Tape_As) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionPage_Setup) + self.menuFile.addAction(self.actionPrint_Tape) + self.menuView.addAction(self.actionView_Basic) + self.menuView.addAction(self.actionView_Scientific) + self.menuView.addAction(self.actionView_Programation) + self.menuView.addSeparator() + self.menuView.addAction(self.actionView_Tape) + self.menuView.addSeparator() + self.menuView.addAction(self.actionView_RPN) + self.menuView.addSeparator() + self.menuView.addAction(self.actionView_Precision) + self.menuHelp.addAction(self.ActionMenuHelpAbout) + self.menuConvert.addAction(self.actionConvert_Recent_Convertions) + self.menuConvert.addSeparator() + self.menuConvert.addAction(self.actionConvert_Area) + self.menuConvert.addAction(self.actionConvert_Currency) + self.menuConvert.addAction(self.actionConvert_Energy_or_Work) + self.menuConvert.addAction(self.actionConvert_Temperature) + self.menuConvert.addAction(self.actionConvert_Length) + self.menuConvert.addAction(self.actionConvert_Weigths_and_Masses) + self.menuConvert.addAction(self.actionConvert_Speed) + self.menuConvert.addAction(self.actionConvert_Pressure) + self.menuConvert.addAction(self.actionConvert_Power) + self.menuConvert.addAction(self.actionConvert_Volume) + self.menuConvert.addSeparator() + self.menuConvert.addAction(self.actionConvert_Update_currencies_rate) + self.menuConvert.addAction(self.actionConvert_Last_Update) + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuView.menuAction()) + self.menubar.addAction(self.menuConvert.menuAction()) + self.menubar.addAction(self.menuHelp.menuAction()) + + self.retranslateUi(MainWindow) + self.stackedWidget.setCurrentIndex(0) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + _translate = QtCore.QCoreApplication.translate + MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + self.menuFile.setTitle(_translate("MainWindow", "File")) + self.menuView.setTitle(_translate("MainWindow", "View")) + self.menuHelp.setTitle(_translate("MainWindow", "Help")) + self.menuConvert.setTitle(_translate("MainWindow", "Convert")) + self.actionClose_Calculator_Window.setText(_translate("MainWindow", "Close calculator Window")) + self.actionClose_Calculator_Window.setShortcut(_translate("MainWindow", "Ctrl+W")) + self.actionSave_Tape_As.setText(_translate("MainWindow", "Save Tape As...")) + self.actionSave_Tape_As.setShortcut(_translate("MainWindow", "Ctrl+Shift+S")) + self.actionPage_Setup.setText(_translate("MainWindow", "Page Setup...")) + self.actionPage_Setup.setShortcut(_translate("MainWindow", "Ctrl+Shift+P")) + self.actionPrint_Tape.setText(_translate("MainWindow", "Print Tape...")) + self.actionPrint_Tape.setShortcut(_translate("MainWindow", "Ctrl+P")) + self.actionView_Basic.setText(_translate("MainWindow", "Basic")) + self.actionView_Basic.setShortcut(_translate("MainWindow", "Ctrl+1")) + self.actionView_Scientific.setText(_translate("MainWindow", "Scientific")) + self.actionView_Scientific.setShortcut(_translate("MainWindow", "Ctrl+2")) + self.actionView_Programation.setText(_translate("MainWindow", "Programmer")) + self.actionView_Programation.setShortcut(_translate("MainWindow", "Ctrl+3")) + self.actionView_Tape.setText(_translate("MainWindow", "Show Paper Tape")) + self.actionView_RPN.setText(_translate("MainWindow", "RPN")) + self.actionView_Precision.setText(_translate("MainWindow", "Precision")) + self.actionConvert_Recent_Convertions.setText(_translate("MainWindow", "Recent Conversions")) + self.actionConvert_Area.setText(_translate("MainWindow", "Area...")) + self.actionConvert_Currency.setText(_translate("MainWindow", "Currency...")) + self.actionConvert_Energy_or_Work.setText(_translate("MainWindow", "Energy or Work...")) + self.actionConvert_Temperature.setText(_translate("MainWindow", "Temperature...")) + self.actionConvert_Temperature.setToolTip(_translate("MainWindow", "Temperature")) + self.actionConvert_Length.setText(_translate("MainWindow", "Length...")) + self.actionConvert_Weigths_and_Masses.setText(_translate("MainWindow", "Weigths and Masses...")) + self.actionConvert_Speed.setText(_translate("MainWindow", "Speed...")) + self.actionConvert_Pressure.setText(_translate("MainWindow", "Pressure...")) + self.actionConvert_Power.setText(_translate("MainWindow", "Power...")) + self.actionConvert_Volume.setText(_translate("MainWindow", "Volume...")) + self.actionConvert_Update_currencies_rate.setText(_translate("MainWindow", "Update Currency Exchanges Rates...")) + self.actionConvert_Last_Update.setText(_translate("MainWindow", "Last Update:")) + self.ActionMenuHelpAbout.setText(_translate("MainWindow", "About")) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py new file mode 100644 index 000000000..b44e35d26 --- /dev/null +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 + +from PyQt5.QtCore import Qt, pyqtSignal +from PyQt5.QtGui import QPaintEvent, QPainter, QPen, QColor, QBrush, QFontMetrics, QFont +from PyQt5.QtWidgets import QColorDialog, QVBoxLayout, QAbstractButton + + +class CalculatorButton(QAbstractButton): + """ + Custom Qt Widget to show a chosen color. + + Left-clicking the button shows the color-chooser, while + right-clicking resets the color to None (no-color). + """ + + colorChanged = pyqtSignal(object) + + def __init__(self, *args, text=None, **kwargs): + super(CalculatorButton, self).__init__(*args, **kwargs) + + self._text = text + self._color = None + self._color_font = None + + self._height = None + # self.pressed.connect(self.onColorPicker) + + self.setupUI() + + def setupUI(self): + self.font = QFont("Nimbus Sans", 13) + self.font_metric = QFontMetrics(self.font) + + # self.setFixedHeight(self._height) + # self.setFixedWidth(self._height) + + # layout = QVBoxLayout() + # self.setLayout(layout) + + def paintEvent(self, e: QPaintEvent) -> None: + qp = QPainter() + qp.begin(self) + self.draw_square(qp) + self.draw_text(qp) + qp.end() + + def draw_text(self, qp): + if self.color_font(): + qp.setPen(QPen(self.color_font(), 1, Qt.SolidLine)) + else: + qp.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + qp.setFont(self.font) + qp.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), + (self.height() / 2) + self.font_metric.height() / 4, + self.text()) + + def draw_square(self, qp): + + pen_size = 1 + pen_size_half = pen_size / 2 + + qp.setPen(QPen(Qt.black, pen_size, Qt.SolidLine, Qt.RoundCap)) + if self.color(): + qp.setBrush(QBrush(self.color(), Qt.SolidPattern)) + qp.drawRect(pen_size_half, pen_size_half, self.width() - pen_size, self.height() - pen_size) + + def setText(self, text): + if text != self._text: + self._text = text + # self.textChanged.emit(self._text) + + def text(self): + return self._text + + def setColor(self, color): + if color != self._color: + self._color = color + # self.textChanged.emit(self._text) + + def color(self): + return self._color + + def setColorFont(self, color_font): + if color_font != self._color_font: + self._color_font = color_font + # self.textChanged.emit(self._text) + + def color_font(self): + return self._color_font + + def onColorPicker(self): + """ + Show color-picker dialog to select color. + + Qt will use the native dialog by default. + + """ + dlg = QColorDialog(self) + if self._color: + dlg.setCurrentColor(QColor(self._color)) + + if dlg.exec_(): + self.setColor(dlg.currentColor().name()) + + def mousePressEvent(self, e): + # if e.button() == Qt.RightButton: + # self.setColor(self._default) + + return super(CalculatorButton, self).mousePressEvent(e) From 78095432006b1bf2c2d1838053b9c86bdbad559b Mon Sep 17 00:00:00 2001 From: Tuuux Date: Sat, 2 Sep 2023 13:55:19 +0200 Subject: [PATCH 06/22] Update UI --- Utilities/Calculator.app/Resources/README.md | 140 ++++++++++++++++++ .../Calculator.app/Resources/calculator.py | 89 +++++------ .../Resources/dialog_paper_tape.py | 22 +++ .../Resources/dialog_paper_tape.ui | 55 +++++++ .../Resources/dialog_paper_tape_ui.py | 38 +++++ .../Calculator.app/Resources/main_window.ui | 35 +++-- .../Resources/main_window_ui.py | 21 +-- 7 files changed, 339 insertions(+), 61 deletions(-) create mode 100644 Utilities/Calculator.app/Resources/README.md create mode 100644 Utilities/Calculator.app/Resources/dialog_paper_tape.py create mode 100644 Utilities/Calculator.app/Resources/dialog_paper_tape.ui create mode 100644 Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py diff --git a/Utilities/Calculator.app/Resources/README.md b/Utilities/Calculator.app/Resources/README.md new file mode 100644 index 000000000..341d6c8a6 --- /dev/null +++ b/Utilities/Calculator.app/Resources/README.md @@ -0,0 +1,140 @@ +# Calculator + + +Calculator.app displays a calculator window ready to perform common operations like addition, subtraction, multiplication, and division, as well as complex calculations and conversions. + +To launch Calculator +Double-click the Calculator icon in the Utilities folder (Figure 1). + +Or + +1. Click the Calculator icon in the Utilities folder (Figure 1) to select it. +2. Choose File > Open, or press . + +The Calculator window appears (Figure 2). + +Figure 2. At startup the Calculator looks like any pocket calculator. + +SCREEN HERE + +## Perform basic calculations +You can use your mouse to click buttons for numbers and operators. + +Or + +Press keyboard keys corresponding to numbers and operators. + +The numbers you enter and the results of your calculations appear at the top of the Calculator window. + + Tip + +You can use the Cut, Copy, and Paste commands to copy the results of calculations into documents. + + +To keep track of your entries +Click View > Show Paper Tape (Figure 3). The Paper Tape dialog appears. It displays entries history (Figure 4). + + +Figure 3. Calculator View menu. +SCREEN HERE + + +Figure 4. By click Show Paper Tape from the View menu, your entries history appear in a separate window dialog. +SCREEN HERE + +To hide the Paper Tape window dialog, click View > Hide Paper Tape or use the Paper Tape window's close button. +You can click the Clear button , To start with a fresh tape. + +You can use commands under the File menu (Figure 41) to save or print the paper tape. + +Figure 41. The File menu includes commands for saving and printing the paper tape. + + + + +## Perform scientific calculations +1. Choose View > Scientific (Figure 39) or press . The window expands to show a variety of functions used for scientific calculations (Figure 42). + +Figure 42. Choosing Scientific from the View menu expands the Calculator to display scientific functions. +SCREEN HERE + + +2. Click buttons for the functions, values, and operators to perform your calculations. + + + Tip + +To hide scientific functions, choose View > Basic (Figure 39) or press . + + +## Perform programmer functions +1. Choose View > Programmer (Figure 39) or press . The window expands to show a variety of programming-related functions (Figure 43). + +Figure 43. Choosing Programmer from the View menu displays programming-related functions. +SCREEN HERE + + +2. Click buttons for the functions, values, and operators to perform your calculations. + + + Tip + +To hide programmer functions, choose View > Basic (Figure 39) or press . + + +To use Reverse Polish Notation +1. + +Choose View > RPN (Figure 39). The letters RPN appear in the calculator's window (Figure 44). + +Figure 44. The letters RPN indicate the calculator is configured for Reverse Polish Notation. + + + +2. + +Click buttons for the functions, values, and operators to perform your calculations. + + + Tips + +Reverse Polish Notation, or RPN, is an alternative format for entering calculations. It is commonly used on Hewlett-Packard brand calculators. + +If you don't know what Reverse Polish Notation is, you probably don't need to use it. + + +To perform conversions +1. + +Enter the value you want to convert. + + +2. + +Choose the conversion you want from the Convert menu (Figure 45). + +Figure 45. The Convert menu lists a variety of common conversions. + + + +3. + +In the dialog that appears, set options for the conversion you want to perform. Figure 46 shows an example that converts speed from miles per hour to knots. + +Figure 46. Set conversion options in a dialog sheet like this. + + + +4. + +Click OK. The original value you entered is converted and appears at the top of the Calculator window. + + + Tips + +The Convert menu's Recent Conversions submenu makes it easy to repeat conversions you have done recently. + +If you have an Internet connection, you should choose Convert > Update Currency Exchange Rates before using the Convert menu to perform currency conversions. + + + diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 12c0d02b0..f1aefcf87 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -57,6 +57,7 @@ from main_window_ui import Ui_MainWindow from widget_calculator_button import CalculatorButton +from dialog_paper_tape import PaperTape __version__ = "0.2" __author__ = [ @@ -67,7 +68,7 @@ ERROR_MSG = "ERROR" TILE_WIDTH = 36 TILE_HEIGHT = 34 -TILE_SPACING = 3 +TILE_SPACING = 0 # Create a subclass of QMainWindow to setup the calculator's GUI @@ -76,27 +77,16 @@ class Window(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super().__init__(parent) + self.paper_tape_dialog = None + self.setupUi(self) - # Set some main window's properties - self.setWindowTitle("Calculator") - # Strange effect with hellosystem theme - # self.setFixedSize( - # (TILE_WIDTH * 4) + ( TILE_SPACING * 9), - # (TILE_HEIGHT * 7) + (TILE_SPACING * 9) - # ) - # Set the central widget and the general layout - # self.generalLayout = QVBoxLayout() - # self._centralWidget = QWidget(self) - # self.setCentralWidget(self._centralWidget) - # self._centralWidget.setLayout(self.generalLayout) - # Create the display and the buttons + self.setupCustomUi() self._createButtons() self.setupInitialState() self.connectSignalsSlots() self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "Calculator.png"))) - # self._showMenu() def setupInitialState(self): self.display.setAlignment(Qt.AlignRight) @@ -106,12 +96,18 @@ def connectSignalsSlots(self): # Menu and ToolBar self.ActionMenuHelpAbout.triggered.connect(self._showAboutDialog) + self.actionView_Show_Paper_Tape.triggered.connect(self._showPaperTape) + + def setupCustomUi(self): + # Paper Tape + self.paper_tape_dialog = PaperTape() + self.paper_tape_dialog.hide() def _createButtons(self): """Create the buttons.""" self.buttons = {} # self.basic_buttons_layout = QGridLayout() - self.basic_buttons_layout.setSpacing(TILE_SPACING) + # self.basic_buttons_layout.setSpacing(TILE_SPACING) # Button text | position on the QGridLayout buttons = { # First Line @@ -147,27 +143,29 @@ def _createButtons(self): self.buttons[btnText] = CalculatorButton() self.buttons[btnText].setText(btnText) # Spanning management - self.buttons[btnText].setMinimumWidth(TILE_WIDTH) + # self.buttons[btnText].setMinimumWidth(TILE_WIDTH) self.buttons[btnText].setMinimumHeight(TILE_HEIGHT) + # Apply Color - if btnText in ["=", "−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: - self.buttons[btnText].setColor(QColor(85, 85, 85)) - self.buttons[btnText].setColorFont(QColor(208, 208, 208)) + if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: + self.buttons[btnText].setColor(QColor("#7a7a7b")) + self.buttons[btnText].setColorFont(QColor("#f7f6f6")) + elif btnText == "=": + self.buttons[btnText].setColor(QColor("#f09648")) + self.buttons[btnText].setColorFont(QColor("#ffffff")) elif btnText == "C": - self.buttons[btnText].setColor(QColor(255, 140, 55)) - self.buttons[btnText].setColorFont(QColor(255, 255, 255)) + self.buttons[btnText].setColor(QColor("#f0003b")) + self.buttons[btnText].setColorFont(QColor("#ffffff")) else: - self.buttons[btnText].setColor(QColor(224, 224, 224)) - self.buttons[btnText].setColorFont(QColor(16, 16, 16)) + self.buttons[btnText].setColor(QColor("#eeeeed")) + self.buttons[btnText].setColorFont(QColor("#3f3f3f")) if btnText == "=": - - - self.buttons[btnText].setMinimumHeight((TILE_HEIGHT * 2) + TILE_SPACING) + self.buttons[btnText].setMinimumHeight((TILE_HEIGHT * 2) + 3) # helloSystem can t make vertical padding on a button self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 2, 1) elif btnText == "0": - self.buttons[btnText].setMinimumWidth(TILE_WIDTH * 2) + # self.buttons[btnText].setMinimumWidth(TILE_WIDTH * 2) self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 2) else: self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 1) @@ -187,20 +185,11 @@ def clearDisplay(self): """Clear the display.""" self.setDisplayText("") - def _showMenu(self): - exitAct = QAction('&Exit', self) - exitAct.setShortcut('Ctrl+Q') - exitAct.setStatusTip('Exit application') - exitAct.triggered.connect(qApp.quit) - menubar = self.menuBar() - fileMenu = menubar.addMenu('&File') - fileMenu.addAction(exitAct) - - aboutAct = QAction('&About', self) - aboutAct.setStatusTip('About this application') - aboutAct.triggered.connect(self._showAbout) - helpMenu = menubar.addMenu('&Help') - helpMenu.addAction(aboutAct) + def closeEvent(self, evnt): + self.paper_tape_dialog.have_to_close = True + self.paper_tape_dialog.close() + + super(Window, self).closeEvent(evnt) def _showAboutDialog(self): msg = QMessageBox() @@ -224,6 +213,14 @@ def _showAboutDialog(self): "https://github.com/helloSystem/Utilities") msg.exec() + def _showPaperTape(self): + if self.paper_tape_dialog.isVisible(): + self.paper_tape_dialog.hide() + else: + self.paper_tape_dialog.show() + self.activateWindow() + self.setFocus() + # Create a Model to handle the calculator's operation def evaluateExpression(expression): @@ -269,7 +266,15 @@ def memory(self, value): def _calculateResult(self): """Evaluate expressions.""" + result = self._evaluate(expression=self._view.displayText()) + if result: + self._view.paper_tape_dialog.plainTextEdit.setPlainText( + "%s\n\n%s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), + self._view.displayText())) + self._view.paper_tape_dialog.plainTextEdit.setPlainText( + "%s\n= %s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), + result)) self._view.setDisplayText(result) def _memory_clear(self): diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape.py b/Utilities/Calculator.app/Resources/dialog_paper_tape.py new file mode 100644 index 000000000..912b18df3 --- /dev/null +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape.py @@ -0,0 +1,22 @@ +from PyQt5.QtWidgets import QWidget +from dialog_paper_tape_ui import Ui_PaperTape + + +class PaperTape(QWidget, Ui_PaperTape): + + def __init__(self, parent=None, process=None): + super(PaperTape, self).__init__(parent) + self.setupUi(self) + self.process = process + + # When you want to destroy the dialog set this to True + self.have_to_close = False + + def closeEvent(self, evnt): + # That widget is call as a window, and should be close with the main app + # Else ---> The widget is hide then continue to store CPU data + if self.have_to_close: + super(PaperTape, self).closeEvent(evnt) + else: + evnt.ignore() + self.hide() diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape.ui b/Utilities/Calculator.app/Resources/dialog_paper_tape.ui new file mode 100644 index 000000000..864aa9308 --- /dev/null +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape.ui @@ -0,0 +1,55 @@ + + + PaperTape + + + + 0 + 0 + 244 + 251 + + + + Form + + + + + + Clear + + + + + + + Recalculate Totals + + + + + + + + + + + + pushButton_clear + clicked() + plainTextEdit + clear() + + + 194 + 226 + + + 121 + 106 + + + + + diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py b/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py new file mode 100644 index 000000000..1d8e53120 --- /dev/null +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file './dialog_paper_tape.ui' +# +# Created by: PyQt5 UI code generator 5.15.9 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_PaperTape(object): + def setupUi(self, PaperTape): + PaperTape.setObjectName("PaperTape") + PaperTape.resize(244, 251) + self.gridLayout = QtWidgets.QGridLayout(PaperTape) + self.gridLayout.setObjectName("gridLayout") + self.pushButton_clear = QtWidgets.QPushButton(PaperTape) + self.pushButton_clear.setObjectName("pushButton_clear") + self.gridLayout.addWidget(self.pushButton_clear, 1, 1, 1, 1) + self.pushButton_recalculate_totals = QtWidgets.QPushButton(PaperTape) + self.pushButton_recalculate_totals.setObjectName("pushButton_recalculate_totals") + self.gridLayout.addWidget(self.pushButton_recalculate_totals, 1, 0, 1, 1) + self.plainTextEdit = QtWidgets.QPlainTextEdit(PaperTape) + self.plainTextEdit.setObjectName("plainTextEdit") + self.gridLayout.addWidget(self.plainTextEdit, 0, 0, 1, 2) + + self.retranslateUi(PaperTape) + self.pushButton_clear.clicked.connect(self.plainTextEdit.clear) # type: ignore + QtCore.QMetaObject.connectSlotsByName(PaperTape) + + def retranslateUi(self, PaperTape): + _translate = QtCore.QCoreApplication.translate + PaperTape.setWindowTitle(_translate("PaperTape", "Form")) + self.pushButton_clear.setText(_translate("PaperTape", "Clear")) + self.pushButton_recalculate_totals.setText(_translate("PaperTape", "Recalculate Totals")) diff --git a/Utilities/Calculator.app/Resources/main_window.ui b/Utilities/Calculator.app/Resources/main_window.ui index 66e206f68..7ddd376c6 100644 --- a/Utilities/Calculator.app/Resources/main_window.ui +++ b/Utilities/Calculator.app/Resources/main_window.ui @@ -6,12 +6,12 @@ 0 0 - 282 + 286 185 - MainWindow + Calculator @@ -63,8 +63,8 @@ - background-color: rgb(74, 74, 74); -color: rgb(228, 228, 228); + background-color: "#cbe5c3"; +color: "#3f3f3f"; QFrame::WinPanel @@ -186,7 +186,7 @@ color: rgb(228, 228, 228); 3 - 0 + 3 @@ -202,7 +202,7 @@ color: rgb(228, 228, 228); 0 0 - 282 + 286 28 @@ -224,7 +224,7 @@ color: rgb(228, 228, 228); - + @@ -317,7 +317,7 @@ color: rgb(228, 228, 228); Ctrl+3 - + Show Paper Tape @@ -446,5 +446,22 @@ color: rgb(228, 228, 228); - + + + actionClose_Calculator_Window + triggered() + MainWindow + close() + + + 140 + 92 + + + 140 + 92 + + + + diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py index cbe1fc446..54376acfe 100644 --- a/Utilities/Calculator.app/Resources/main_window_ui.py +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -14,7 +14,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(282, 185) + MainWindow.resize(286, 185) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) @@ -38,8 +38,8 @@ def setupUi(self, MainWindow): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) self.frame.setSizePolicy(sizePolicy) - self.frame.setStyleSheet("background-color: rgb(74, 74, 74);\n" -"color: rgb(228, 228, 228);") + self.frame.setStyleSheet("background-color: \"#cbe5c3\";\n" +"color: \"#3f3f3f\";") self.frame.setFrameShape(QtWidgets.QFrame.WinPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) self.frame.setLineWidth(2) @@ -88,7 +88,7 @@ def setupUi(self, MainWindow): self.verticalLayout.setObjectName("verticalLayout") self.basic_buttons_layout = QtWidgets.QGridLayout() self.basic_buttons_layout.setContentsMargins(3, 0, 3, 3) - self.basic_buttons_layout.setSpacing(0) + self.basic_buttons_layout.setSpacing(3) self.basic_buttons_layout.setObjectName("basic_buttons_layout") self.verticalLayout.addLayout(self.basic_buttons_layout) self.stackedWidget.addWidget(self.basic) @@ -99,7 +99,7 @@ def setupUi(self, MainWindow): self.verticalLayout_2.setStretch(1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 282, 28)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 286, 28)) self.menubar.setObjectName("menubar") self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") @@ -124,8 +124,8 @@ def setupUi(self, MainWindow): self.actionView_Scientific.setObjectName("actionView_Scientific") self.actionView_Programation = QtWidgets.QAction(MainWindow) self.actionView_Programation.setObjectName("actionView_Programation") - self.actionView_Tape = QtWidgets.QAction(MainWindow) - self.actionView_Tape.setObjectName("actionView_Tape") + self.actionView_Show_Paper_Tape = QtWidgets.QAction(MainWindow) + self.actionView_Show_Paper_Tape.setObjectName("actionView_Show_Paper_Tape") self.actionView_RPN = QtWidgets.QAction(MainWindow) self.actionView_RPN.setObjectName("actionView_RPN") self.actionView_Precision = QtWidgets.QAction(MainWindow) @@ -180,7 +180,7 @@ def setupUi(self, MainWindow): self.menuView.addAction(self.actionView_Scientific) self.menuView.addAction(self.actionView_Programation) self.menuView.addSeparator() - self.menuView.addAction(self.actionView_Tape) + self.menuView.addAction(self.actionView_Show_Paper_Tape) self.menuView.addSeparator() self.menuView.addAction(self.actionView_RPN) self.menuView.addSeparator() @@ -208,11 +208,12 @@ def setupUi(self, MainWindow): self.retranslateUi(MainWindow) self.stackedWidget.setCurrentIndex(0) + self.actionClose_Calculator_Window.triggered.connect(MainWindow.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) + MainWindow.setWindowTitle(_translate("MainWindow", "Calculator")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.menuView.setTitle(_translate("MainWindow", "View")) self.menuHelp.setTitle(_translate("MainWindow", "Help")) @@ -231,7 +232,7 @@ def retranslateUi(self, MainWindow): self.actionView_Scientific.setShortcut(_translate("MainWindow", "Ctrl+2")) self.actionView_Programation.setText(_translate("MainWindow", "Programmer")) self.actionView_Programation.setShortcut(_translate("MainWindow", "Ctrl+3")) - self.actionView_Tape.setText(_translate("MainWindow", "Show Paper Tape")) + self.actionView_Show_Paper_Tape.setText(_translate("MainWindow", "Show Paper Tape")) self.actionView_RPN.setText(_translate("MainWindow", "RPN")) self.actionView_Precision.setText(_translate("MainWindow", "Precision")) self.actionConvert_Recent_Convertions.setText(_translate("MainWindow", "Recent Conversions")) From 857d7f39ee939c7ac67b04bcaea5889e87edb82c Mon Sep 17 00:00:00 2001 From: Tuuux Date: Sat, 2 Sep 2023 13:58:21 +0200 Subject: [PATCH 07/22] Grammatical fix --- Utilities/Calculator.app/Resources/calculator.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index f1aefcf87..2f0e7f532 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -278,11 +278,11 @@ def _calculateResult(self): self._view.setDisplayText(result) def _memory_clear(self): - """Clear momory by set value to None""" + """Clear memory by set value to None""" self.memory = None self._view.display.setFocus() - def _memory_substact(self): + def _memory_subtract(self): """Add the result of display expression to the memory""" result = self._evaluate(expression=self._view.displayText()) if result and "ERROR" not in result: @@ -296,7 +296,7 @@ def _memory_substact(self): self._view.display.setFocus() def _memory_add(self): - """Substract the result of display expression to the memory""" + """Subtract the result of display expression to the memory""" result = self._evaluate(expression=self._view.displayText()) if result and "ERROR" not in result: if self.memory is None: @@ -311,7 +311,7 @@ def _memory_print(self): """If memory value, flush the display with it value""" if self.memory is not None: self._view.clearDisplay() - self._view.setDisplayText("%s" % (self.memory)) + self._view.setDisplayText("%s" % self.memory) else: self._view.display.setFocus() @@ -352,7 +352,7 @@ def _connectSignals(self): self._view.buttons["±"].clicked.connect(self._neg) self._view.buttons["MC"].clicked.connect(self._memory_clear) self._view.buttons["M+"].clicked.connect(self._memory_add) - self._view.buttons["M-"].clicked.connect(self._memory_substact) + self._view.buttons["M-"].clicked.connect(self._memory_subtract) self._view.buttons["MR"].clicked.connect(self._memory_print) """self._view.display.escapePressed.connect(self._view.clearDisplay)""" From 5c85d42403850cfcf424eeaa688fa8012aa11aee Mon Sep 17 00:00:00 2001 From: Tuuux Date: Sun, 3 Sep 2023 22:46:04 +0200 Subject: [PATCH 08/22] Clicked button work --- .../Resources/widget_calculator_button.py | 95 ++++++++++++------- 1 file changed, 62 insertions(+), 33 deletions(-) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index b44e35d26..1e51eecd2 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -1,8 +1,19 @@ #!/usr/bin/env python3 -from PyQt5.QtCore import Qt, pyqtSignal -from PyQt5.QtGui import QPaintEvent, QPainter, QPen, QColor, QBrush, QFontMetrics, QFont -from PyQt5.QtWidgets import QColorDialog, QVBoxLayout, QAbstractButton +import os +from PyQt5.QtCore import Qt, pyqtSignal, QRectF +from PyQt5.QtGui import ( + QPaintEvent, + QPainter, + QPen, + QColor, + QFontMetrics, + QFont, + QImage, + QPainterPath, + QLinearGradient +) +from PyQt5.QtWidgets import QAbstractButton class CalculatorButton(QAbstractButton): @@ -21,26 +32,28 @@ def __init__(self, *args, text=None, **kwargs): self._text = text self._color = None self._color_font = None + self.bordersize = 2 + self.outlineColor = QColor("#1e1e1f") + self.state_pressed = False self._height = None + + self.button_1 = QImage(os.path.join(os.path.dirname(__file__), "button_1.png")) # self.pressed.connect(self.onColorPicker) self.setupUI() + + def setupUI(self): self.font = QFont("Nimbus Sans", 13) self.font_metric = QFontMetrics(self.font) - # self.setFixedHeight(self._height) - # self.setFixedWidth(self._height) - - # layout = QVBoxLayout() - # self.setLayout(layout) - def paintEvent(self, e: QPaintEvent) -> None: qp = QPainter() + qp.begin(self) - self.draw_square(qp) + self.draw_square(qp, event=e) self.draw_text(qp) qp.end() @@ -54,15 +67,39 @@ def draw_text(self, qp): (self.height() / 2) + self.font_metric.height() / 4, self.text()) - def draw_square(self, qp): + def draw_square(self, painter, event): + painter.setRenderHint(QPainter.Antialiasing) + # Create the path + path = QPainterPath() + + if not self.state_pressed: + gradient = QLinearGradient(0, 0, 0, self.height() * 3) + gradient.setColorAt(0.0, Qt.white) + gradient.setColorAt(0.06, self.color()) + gradient.setColorAt(0.94, Qt.black) + else: + gradient = QLinearGradient(0, 0, 0, self.height() * 3) + gradient.setColorAt(0.0, Qt.gray) + gradient.setColorAt(0.06, self.color()) + gradient.setColorAt(0.94, Qt.lightGray) + + # Set painter colors to given values. + pen = QPen(self.outlineColor, self.bordersize) + painter.setPen(pen) + painter.setBrush(gradient) + + rect = QRectF(event.rect()) + # Slighly shrink dimensions to account for bordersize. + rect.adjust(self.bordersize / 2, self.bordersize / 2, -self.bordersize / 2, -self.bordersize / 2) - pen_size = 1 - pen_size_half = pen_size / 2 + # Add the rect to path. + path.addRoundedRect(rect, 10, 10) + painter.setClipPath(path) - qp.setPen(QPen(Qt.black, pen_size, Qt.SolidLine, Qt.RoundCap)) - if self.color(): - qp.setBrush(QBrush(self.color(), Qt.SolidPattern)) - qp.drawRect(pen_size_half, pen_size_half, self.width() - pen_size, self.height() - pen_size) + # Fill shape, draw the border and center the text. + painter.fillPath(path, painter.brush()) + painter.strokePath(path, painter.pen()) + painter.drawText(rect, Qt.AlignCenter, self.text()) def setText(self, text): if text != self._text: @@ -88,22 +125,14 @@ def setColorFont(self, color_font): def color_font(self): return self._color_font - def onColorPicker(self): - """ - Show color-picker dialog to select color. - - Qt will use the native dialog by default. - - """ - dlg = QColorDialog(self) - if self._color: - dlg.setCurrentColor(QColor(self._color)) + def mousePressEvent(self, e): + self.state_pressed = True + self.update() + return super(CalculatorButton, self).mousePressEvent(e) - if dlg.exec_(): - self.setColor(dlg.currentColor().name()) + def mouseReleaseEvent(self, e): + self.state_pressed = False + self.update() + return super(CalculatorButton, self).mouseReleaseEvent(e) - def mousePressEvent(self, e): - # if e.button() == Qt.RightButton: - # self.setColor(self._default) - return super(CalculatorButton, self).mousePressEvent(e) From 2c900574120a0d05c4e6fb47c7c308fe8d562ff7 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Mon, 4 Sep 2023 09:35:38 +0200 Subject: [PATCH 09/22] More light gradient --- .../Resources/widget_calculator_button.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 1e51eecd2..19da1d31a 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -43,8 +43,6 @@ def __init__(self, *args, text=None, **kwargs): self.setupUI() - - def setupUI(self): self.font = QFont("Nimbus Sans", 13) self.font_metric = QFontMetrics(self.font) @@ -73,15 +71,15 @@ def draw_square(self, painter, event): path = QPainterPath() if not self.state_pressed: - gradient = QLinearGradient(0, 0, 0, self.height() * 3) + gradient = QLinearGradient(0, 0, 0, self.height() * 4) gradient.setColorAt(0.0, Qt.white) gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.94, Qt.black) + gradient.setColorAt(0.7, Qt.black) else: - gradient = QLinearGradient(0, 0, 0, self.height() * 3) + gradient = QLinearGradient(0, 0, 0, self.height() * 4) gradient.setColorAt(0.0, Qt.gray) gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.94, Qt.lightGray) + gradient.setColorAt(0.7, Qt.black) # Set painter colors to given values. pen = QPen(self.outlineColor, self.bordersize) @@ -93,7 +91,7 @@ def draw_square(self, painter, event): rect.adjust(self.bordersize / 2, self.bordersize / 2, -self.bordersize / 2, -self.bordersize / 2) # Add the rect to path. - path.addRoundedRect(rect, 10, 10) + path.addRoundedRect(rect, 14, 14) painter.setClipPath(path) # Fill shape, draw the border and center the text. From f77937351c3cf3fc2ddee86cca2b80fe3027ed13 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Mon, 4 Sep 2023 11:00:22 +0200 Subject: [PATCH 10/22] More light gradient --- .../Resources/widget_calculator_button.py | 63 ++++++++++--------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 19da1d31a..76684f13c 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -9,7 +9,6 @@ QColor, QFontMetrics, QFont, - QImage, QPainterPath, QLinearGradient ) @@ -32,59 +31,58 @@ def __init__(self, *args, text=None, **kwargs): self._text = text self._color = None self._color_font = None + self._color_border = None self.bordersize = 2 - self.outlineColor = QColor("#1e1e1f") self.state_pressed = False + self.painter = None self._height = None - self.button_1 = QImage(os.path.join(os.path.dirname(__file__), "button_1.png")) - # self.pressed.connect(self.onColorPicker) - self.setupUI() def setupUI(self): self.font = QFont("Nimbus Sans", 13) self.font_metric = QFontMetrics(self.font) + self.setColorBorder(QColor("#1e1e1f")) + self.painter = QPainter() def paintEvent(self, e: QPaintEvent) -> None: - qp = QPainter() - qp.begin(self) - self.draw_square(qp, event=e) - self.draw_text(qp) - qp.end() + self.painter.begin(self) + self.draw_square(event=e) + self.draw_text() + self.painter.end() - def draw_text(self, qp): + def draw_text(self): if self.color_font(): - qp.setPen(QPen(self.color_font(), 1, Qt.SolidLine)) + self.painter.setPen(QPen(self.color_font(), 1, Qt.SolidLine)) else: - qp.setPen(QPen(Qt.black, 1, Qt.SolidLine)) - qp.setFont(self.font) - qp.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), + self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + self.painter.setFont(self.font) + self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), (self.height() / 2) + self.font_metric.height() / 4, self.text()) - def draw_square(self, painter, event): - painter.setRenderHint(QPainter.Antialiasing) + def draw_square(self, event): + self.painter.setRenderHint(QPainter.Antialiasing) # Create the path path = QPainterPath() if not self.state_pressed: - gradient = QLinearGradient(0, 0, 0, self.height() * 4) + gradient = QLinearGradient(0, 0, 0, self.height() * 5) gradient.setColorAt(0.0, Qt.white) gradient.setColorAt(0.06, self.color()) gradient.setColorAt(0.7, Qt.black) else: - gradient = QLinearGradient(0, 0, 0, self.height() * 4) - gradient.setColorAt(0.0, Qt.gray) + gradient = QLinearGradient(0, 0, 0, self.height() * 5) + gradient.setColorAt(0.0, Qt.darkGray) gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.7, Qt.black) + gradient.setColorAt(0.95, Qt.white) # Set painter colors to given values. - pen = QPen(self.outlineColor, self.bordersize) - painter.setPen(pen) - painter.setBrush(gradient) + pen = QPen(self.color_border(), self.bordersize) + self.painter.setPen(pen) + self.painter.setBrush(gradient) rect = QRectF(event.rect()) # Slighly shrink dimensions to account for bordersize. @@ -92,12 +90,14 @@ def draw_square(self, painter, event): # Add the rect to path. path.addRoundedRect(rect, 14, 14) - painter.setClipPath(path) + self.painter.setClipPath(path) # Fill shape, draw the border and center the text. - painter.fillPath(path, painter.brush()) - painter.strokePath(path, painter.pen()) - painter.drawText(rect, Qt.AlignCenter, self.text()) + self.painter.fillPath(path, self.painter.brush()) + self.painter.strokePath(path, self.painter.pen()) + + # TExt is use a drop shadow + self.painter.drawText(rect, Qt.AlignCenter, self.text()) def setText(self, text): if text != self._text: @@ -123,6 +123,13 @@ def setColorFont(self, color_font): def color_font(self): return self._color_font + def setColorBorder(self, color_border): + if color_border != self._color_border: + self._color_border = color_border + + def color_border(self): + return self._color_border + def mousePressEvent(self, e): self.state_pressed = True self.update() From fcf7a0ceb30c66b8e3aea78029aca90d3916e4c3 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Mon, 4 Sep 2023 20:52:53 +0200 Subject: [PATCH 11/22] Add signals for scientific layout --- .../Calculator.app/Resources/calculator.py | 210 +++++++++++++----- .../Calculator.app/Resources/main_window.ui | 27 ++- .../Resources/main_window_ui.py | 33 +-- .../Resources/widget_calculator_button.py | 157 +++++++++---- 4 files changed, 311 insertions(+), 116 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 2f0e7f532..178e96f5a 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -40,17 +40,10 @@ from functools import partial # Import QApplication and the required widgets from PyQt5.QtWidgets -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt,QSize from PyQt5.QtWidgets import QApplication -from PyQt5.QtWidgets import QGridLayout -from PyQt5.QtWidgets import QLineEdit from PyQt5.QtWidgets import QMainWindow -from PyQt5.QtWidgets import QPushButton -from PyQt5.QtWidgets import QVBoxLayout -from PyQt5.QtWidgets import QWidget -from PyQt5.QtWidgets import QAction from PyQt5.QtWidgets import QMessageBox -from PyQt5.QtWidgets import qApp from PyQt5.QtGui import QPixmap, QColor, QIcon # The Main Window @@ -66,9 +59,6 @@ ] ERROR_MSG = "ERROR" -TILE_WIDTH = 36 -TILE_HEIGHT = 34 -TILE_SPACING = 0 # Create a subclass of QMainWindow to setup the calculator's GUI @@ -78,18 +68,24 @@ class Window(QMainWindow, Ui_MainWindow): def __init__(self, parent=None): super().__init__(parent) self.paper_tape_dialog = None - + self.scientific_buttons = None + self.basic_buttons = None self.setupUi(self) self.setupCustomUi() - self._createButtons() - self.setupInitialState() + self.create_basic_layout() + self.create_scientific_layout() + self.connectSignalsSlots() self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "Calculator.png"))) + self._display_basic() + self.show() def setupInitialState(self): self.display.setAlignment(Qt.AlignRight) + self.scientific_buttons = {} + self.basic_buttons = {} # self.display.setReadOnly(False) def connectSignalsSlots(self): @@ -97,17 +93,18 @@ def connectSignalsSlots(self): # Menu and ToolBar self.ActionMenuHelpAbout.triggered.connect(self._showAboutDialog) self.actionView_Show_Paper_Tape.triggered.connect(self._showPaperTape) + self.actionView_Basic.triggered.connect(self._display_basic) + self.actionView_Scientific.triggered.connect(self._display_scientific) def setupCustomUi(self): + # Paper Tape self.paper_tape_dialog = PaperTape() self.paper_tape_dialog.hide() - def _createButtons(self): - """Create the buttons.""" - self.buttons = {} - # self.basic_buttons_layout = QGridLayout() - # self.basic_buttons_layout.setSpacing(TILE_SPACING) + def create_basic_layout(self): + """Create the basic layout buttons.""" + # Button text | position on the QGridLayout buttons = { # First Line @@ -140,37 +137,114 @@ def _createButtons(self): } # Create the buttons and add them to the grid layout for btnText, pos in buttons.items(): - self.buttons[btnText] = CalculatorButton() - self.buttons[btnText].setText(btnText) - # Spanning management - # self.buttons[btnText].setMinimumWidth(TILE_WIDTH) - self.buttons[btnText].setMinimumHeight(TILE_HEIGHT) + # Create a button + self.basic_buttons[btnText] = CalculatorButton(text=btnText) + # Apply Color + if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: + self.basic_buttons[btnText].setColor(QColor("#7a7a7b")) + self.basic_buttons[btnText].setFontColor(QColor("#f7f6f6")) + elif btnText == "=": + self.basic_buttons[btnText].setColor(QColor("#f09648")) + self.basic_buttons[btnText].setFontColor(QColor("#ffffff")) + elif btnText == "C": + self.basic_buttons[btnText].setColor(QColor("#f0003b")) + self.basic_buttons[btnText].setFontColor(QColor("#ffffff")) + else: + self.basic_buttons[btnText].setColor(QColor("#eeeeed")) + self.basic_buttons[btnText].setFontColor(QColor("#3f3f3f")) + + # Apply location + if btnText == "=": + self.basic_buttons_layout.addWidget(self.basic_buttons[btnText], pos[0], pos[1], 2, 1) + elif btnText == "0": + self.basic_buttons_layout.addWidget(self.basic_buttons[btnText], pos[0], pos[1], 1, 2) + else: + self.basic_buttons_layout.addWidget(self.basic_buttons[btnText], pos[0], pos[1], 1, 1) + + def create_scientific_layout(self): + """Create the basic layout buttons.""" + self.scientific_buttons = {} + # Button text | position on the QGridLayout + buttons = { + # First Line + "2nd": (0, 0), + "⟮": (0, 1), + "⟯": (0, 2), + "%": (0, 3), + "MC": (0, 5), + "M+": (0, 6), + "M-": (0, 7), + "MR": (0, 8), + # Second line + "1/x": (1, 0), + "x²": (1, 1), + "x³": (1, 2), + "yˣ": (1, 3), + "C": (1, 5), + "±": (1, 6), + "÷": (1, 7), + "×": (1, 8), + # Third line + "x!": (2, 0), + "√": (2, 1), + "ˣ√𝑦": (2, 2), + "In": (2, 3), + "7": (2, 5), + "8": (2, 6), + "9": (2, 7), + "−": (2, 8), + # etc ... + "sin": (3, 0), + "cos": (3, 1), + "tan": (3, 2), + "log": (3, 3), + "4": (3, 5), + "5": (3, 6), + "6": (3, 7), + "+": (3, 8), + + "sinh": (4, 0), + "cosh": (4, 1), + "tanh": (4, 2), + "eˣ": (4, 3), + "1": (4, 5), + "2": (4, 6), + "3": (4, 7), + "=": (4, 8), + # the last line got only 2 buttons + "Rad": (5, 0), + "⫪": (5, 1), + "EE": (5, 2), + "RN": (5, 3), + "0": (5, 5), + ".": (5, 7), + } + # Create the buttons and add them to the grid layout + for btnText, pos in buttons.items(): + # Create a button + self.scientific_buttons[btnText] = CalculatorButton(text=btnText) # Apply Color if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: - self.buttons[btnText].setColor(QColor("#7a7a7b")) - self.buttons[btnText].setColorFont(QColor("#f7f6f6")) + self.scientific_buttons[btnText].setColor(QColor("#7a7a7b")) + self.scientific_buttons[btnText].setFontColor(QColor("#f7f6f6")) elif btnText == "=": - self.buttons[btnText].setColor(QColor("#f09648")) - self.buttons[btnText].setColorFont(QColor("#ffffff")) + self.scientific_buttons[btnText].setColor(QColor("#f09648")) + self.scientific_buttons[btnText].setFontColor(QColor("#ffffff")) elif btnText == "C": - self.buttons[btnText].setColor(QColor("#f0003b")) - self.buttons[btnText].setColorFont(QColor("#ffffff")) + self.scientific_buttons[btnText].setColor(QColor("#f0003b")) + self.scientific_buttons[btnText].setFontColor(QColor("#ffffff")) else: - self.buttons[btnText].setColor(QColor("#eeeeed")) - self.buttons[btnText].setColorFont(QColor("#3f3f3f")) + self.scientific_buttons[btnText].setColor(QColor("#eeeeed")) + self.scientific_buttons[btnText].setFontColor(QColor("#3f3f3f")) + # Apply location if btnText == "=": - self.buttons[btnText].setMinimumHeight((TILE_HEIGHT * 2) + 3) - # helloSystem can t make vertical padding on a button - self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 2, 1) + self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 2, 1) elif btnText == "0": - # self.buttons[btnText].setMinimumWidth(TILE_WIDTH * 2) - self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 2) + self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 1, 2) else: - self.basic_buttons_layout.addWidget(self.buttons[btnText], pos[0], pos[1], 1, 1) - # # Add buttonsLayout to the general layout - # self.generalLayout.addLayout(self.basic_buttons_layout) + self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 1, 1) def setDisplayText(self, text): """Set display's text.""" @@ -191,7 +265,8 @@ def closeEvent(self, evnt): super(Window, self).closeEvent(evnt) - def _showAboutDialog(self): + @staticmethod + def _showAboutDialog(): msg = QMessageBox() msg.setWindowTitle("About") msg.setIconPixmap( @@ -221,6 +296,17 @@ def _showPaperTape(self): self.activateWindow() self.setFocus() + def _display_basic(self): + self.setFixedWidth(200) + self.setFixedHeight(250) + self.stacked_widget.setCurrentIndex(0) + + def _display_scientific(self): + self.setFixedWidth(400) + self.setFixedHeight(250) + self.stacked_widget.setCurrentIndex(1) + + # Create a Model to handle the calculator's operation def evaluateExpression(expression): @@ -231,6 +317,10 @@ def evaluateExpression(expression): expression = expression.replace("×", "*") if "−" in expression: expression = expression.replace("−", "-") + if "⟮" in expression: + expression = expression.replace("⟮", "(") + if "⟯" in expression: + expression = expression.replace("⟯", ")") try: result = str(eval(expression, {}, {})) except Exception: @@ -342,18 +432,36 @@ def _buildExpression(self, sub_exp): def _connectSignals(self): """Connect signals and slots.""" - for btnText, btn in self._view.buttons.items(): + # Display signals + self._view.display.returnPressed.connect(self._calculateResult) + + # Connect Basic Layout Button + for btnText, btn in self._view.basic_buttons.items(): if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±"}: btn.clicked.connect(partial(self._buildExpression, btnText)) - self._view.buttons["="].clicked.connect(self._calculateResult) - self._view.display.returnPressed.connect(self._calculateResult) - self._view.buttons["C"].clicked.connect(self._view.clearDisplay) - self._view.buttons["±"].clicked.connect(self._neg) - self._view.buttons["MC"].clicked.connect(self._memory_clear) - self._view.buttons["M+"].clicked.connect(self._memory_add) - self._view.buttons["M-"].clicked.connect(self._memory_subtract) - self._view.buttons["MR"].clicked.connect(self._memory_print) + self._view.basic_buttons["="].clicked.connect(self._calculateResult) + self._view.basic_buttons["C"].clicked.connect(self._view.clearDisplay) + self._view.basic_buttons["±"].clicked.connect(self._neg) + self._view.basic_buttons["MC"].clicked.connect(self._memory_clear) + self._view.basic_buttons["M+"].clicked.connect(self._memory_add) + self._view.basic_buttons["M-"].clicked.connect(self._memory_subtract) + self._view.basic_buttons["MR"].clicked.connect(self._memory_print) + + # Connect Scientific Layout Button + for btnText, btn in self._view.scientific_buttons.items(): + if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±"}: + btn.clicked.connect(partial(self._buildExpression, btnText)) + + self._view.scientific_buttons["="].clicked.connect(self._calculateResult) + + self._view.scientific_buttons["C"].clicked.connect(self._view.clearDisplay) + self._view.scientific_buttons["±"].clicked.connect(self._neg) + self._view.scientific_buttons["MC"].clicked.connect(self._memory_clear) + self._view.scientific_buttons["M+"].clicked.connect(self._memory_add) + self._view.scientific_buttons["M-"].clicked.connect(self._memory_subtract) + self._view.scientific_buttons["MR"].clicked.connect(self._memory_print) + """self._view.display.escapePressed.connect(self._view.clearDisplay)""" @@ -377,7 +485,7 @@ def _connectSignals(self): if __name__ == "__main__": app = QApplication(sys.argv) win = Window() - win.show() + model = evaluateExpression PyCalcCtrl(model=model, view=win) # Execute calculator's main loop diff --git a/Utilities/Calculator.app/Resources/main_window.ui b/Utilities/Calculator.app/Resources/main_window.ui index 7ddd376c6..142da2175 100644 --- a/Utilities/Calculator.app/Resources/main_window.ui +++ b/Utilities/Calculator.app/Resources/main_window.ui @@ -7,7 +7,7 @@ 0 0 286 - 185 + 183 @@ -97,7 +97,7 @@ color: "#3f3f3f"; - + 0 0 @@ -135,7 +135,7 @@ color: "#3f3f3f"; - + 0 @@ -192,7 +192,26 @@ color: "#3f3f3f"; - + + + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py index 54376acfe..faa553d8e 100644 --- a/Utilities/Calculator.app/Resources/main_window_ui.py +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -14,7 +14,7 @@ class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(286, 185) + MainWindow.resize(286, 183) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget) @@ -50,7 +50,7 @@ def setupUi(self, MainWindow): self.verticalLayout_4.setSpacing(0) self.verticalLayout_4.setObjectName("verticalLayout_4") self.display = QtWidgets.QLineEdit(self.frame) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.display.sizePolicy().hasHeightForWidth()) @@ -67,14 +67,14 @@ def setupUi(self, MainWindow): self.verticalLayout_4.addWidget(self.display) self.verticalLayout_3.addWidget(self.frame) self.verticalLayout_2.addWidget(self.widget_19) - self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget) + self.stacked_widget = QtWidgets.QStackedWidget(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth()) - self.stackedWidget.setSizePolicy(sizePolicy) - self.stackedWidget.setLineWidth(1) - self.stackedWidget.setObjectName("stackedWidget") + sizePolicy.setHeightForWidth(self.stacked_widget.sizePolicy().hasHeightForWidth()) + self.stacked_widget.setSizePolicy(sizePolicy) + self.stacked_widget.setLineWidth(1) + self.stacked_widget.setObjectName("stacked_widget") self.basic = QtWidgets.QWidget() sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding) sizePolicy.setHorizontalStretch(0) @@ -91,11 +91,18 @@ def setupUi(self, MainWindow): self.basic_buttons_layout.setSpacing(3) self.basic_buttons_layout.setObjectName("basic_buttons_layout") self.verticalLayout.addLayout(self.basic_buttons_layout) - self.stackedWidget.addWidget(self.basic) - self.page_2 = QtWidgets.QWidget() - self.page_2.setObjectName("page_2") - self.stackedWidget.addWidget(self.page_2) - self.verticalLayout_2.addWidget(self.stackedWidget) + self.stacked_widget.addWidget(self.basic) + self.scientific = QtWidgets.QWidget() + self.scientific.setObjectName("scientific") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.scientific) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.scientific_buttons_layout = QtWidgets.QGridLayout() + self.scientific_buttons_layout.setContentsMargins(3, -1, 3, 3) + self.scientific_buttons_layout.setSpacing(3) + self.scientific_buttons_layout.setObjectName("scientific_buttons_layout") + self.verticalLayout_5.addLayout(self.scientific_buttons_layout) + self.stacked_widget.addWidget(self.scientific) + self.verticalLayout_2.addWidget(self.stacked_widget) self.verticalLayout_2.setStretch(1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) @@ -207,7 +214,7 @@ def setupUi(self, MainWindow): self.menubar.addAction(self.menuHelp.menuAction()) self.retranslateUi(MainWindow) - self.stackedWidget.setCurrentIndex(0) + self.stacked_widget.setCurrentIndex(0) self.actionClose_Calculator_Window.triggered.connect(MainWindow.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(MainWindow) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 76684f13c..46592f811 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import os -from PyQt5.QtCore import Qt, pyqtSignal, QRectF +from PyQt5.QtCore import Qt, pyqtSignal, QRectF, QSize from PyQt5.QtGui import ( QPaintEvent, QPainter, @@ -10,9 +10,10 @@ QFontMetrics, QFont, QPainterPath, - QLinearGradient + QLinearGradient, + ) -from PyQt5.QtWidgets import QAbstractButton +from PyQt5.QtWidgets import QAbstractButton, QSizePolicy class CalculatorButton(QAbstractButton): @@ -24,28 +25,51 @@ class CalculatorButton(QAbstractButton): """ colorChanged = pyqtSignal(object) + clicked = pyqtSignal(bool) - def __init__(self, *args, text=None, **kwargs): + def __init__(self, *args, **kwargs): super(CalculatorButton, self).__init__(*args, **kwargs) - self._text = text + self._text = kwargs.get("text") or None self._color = None - self._color_font = None - self._color_border = None - self.bordersize = 2 - self.state_pressed = False + self._font_color = None + self._font_size = None + self._border_color = None + self._border_size = None + self._border_pen = None + + self.__mouse_checked = False + self.__mouse_over = False + + self.font = None + self.font_metric = None self.painter = None - self._height = None self.setupUI() def setupUI(self): - self.font = QFont("Nimbus Sans", 13) + self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) + self.font = QFont("Nimbus Sans", 11) self.font_metric = QFontMetrics(self.font) - self.setColorBorder(QColor("#1e1e1f")) + self.setBorderColor(QColor("#1e1e1f")) + self.setBorderSize(2) + self.setBorderPen(QPen(self.border_color(), self.border_size())) + self.setMouseTracking(True) self.painter = QPainter() + def minimumSizeHint(self): + return QSize( + self.font_metric.width(self.text()) + (self.border_size() * 2), + self.font_metric.height() + (self.border_size() * 2) + ) + + # def sizeHint(self): + # return QSize( + # self.font_metric.width(self.text()) + (self.border_size() * 2), + # self.font_metric.height() + (self.border_size() * 2) + # ) + def paintEvent(self, e: QPaintEvent) -> None: self.painter.begin(self) @@ -54,49 +78,57 @@ def paintEvent(self, e: QPaintEvent) -> None: self.painter.end() def draw_text(self): - if self.color_font(): - self.painter.setPen(QPen(self.color_font(), 1, Qt.SolidLine)) + if self.font_color(): + self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) else: self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) self.painter.setFont(self.font) self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), - (self.height() / 2) + self.font_metric.height() / 4, - self.text()) + (self.height() / 2) + self.font_metric.height() / 4, + self.text()) def draw_square(self, event): self.painter.setRenderHint(QPainter.Antialiasing) # Create the path path = QPainterPath() - if not self.state_pressed: - gradient = QLinearGradient(0, 0, 0, self.height() * 5) - gradient.setColorAt(0.0, Qt.white) - gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.7, Qt.black) + if not self.__mouse_checked: + if self.__mouse_over: + gradient = QLinearGradient(0, 0, 0, self.height() * 5) + gradient.setColorAt(0.0, Qt.white) + gradient.setColorAt(0.1, self.color()) + gradient.setColorAt(1.0, Qt.black) + else: + gradient = QLinearGradient(0, 0, 0, self.height() * 5) + gradient.setColorAt(0.0, Qt.white) + gradient.setColorAt(0.08, self.color()) + gradient.setColorAt(0.7, Qt.black) else: gradient = QLinearGradient(0, 0, 0, self.height() * 5) - gradient.setColorAt(0.0, Qt.darkGray) + gradient.setColorAt(0.0, Qt.lightGray) gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.95, Qt.white) + gradient.setColorAt(0.95, Qt.lightGray) # Set painter colors to given values. - pen = QPen(self.color_border(), self.bordersize) - self.painter.setPen(pen) + + self.painter.setPen(self.border_pen()) self.painter.setBrush(gradient) rect = QRectF(event.rect()) - # Slighly shrink dimensions to account for bordersize. - rect.adjust(self.bordersize / 2, self.bordersize / 2, -self.bordersize / 2, -self.bordersize / 2) + # Slighly shrink dimensions to account for self.border_size(). + rect.adjust(self.border_size() / 2, self.border_size() / 2, -self.border_size() / 2, -self.border_size() / 2) # Add the rect to path. - path.addRoundedRect(rect, 14, 14) + path.addRoundedRect(rect, 12, 12) self.painter.setClipPath(path) # Fill shape, draw the border and center the text. self.painter.fillPath(path, self.painter.brush()) self.painter.strokePath(path, self.painter.pen()) - # TExt is use a drop shadow + # Text is use a drop shadow + self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + self.painter.setFont(self.font) self.painter.drawText(rect, Qt.AlignCenter, self.text()) def setText(self, text): @@ -115,29 +147,58 @@ def setColor(self, color): def color(self): return self._color - def setColorFont(self, color_font): - if color_font != self._color_font: - self._color_font = color_font + def setFontColor(self, color): + if color is None: + color = Qt.black + if color != self._font_color: + self._font_color = color # self.textChanged.emit(self._text) - def color_font(self): - return self._color_font - - def setColorBorder(self, color_border): - if color_border != self._color_border: - self._color_border = color_border - - def color_border(self): - return self._color_border - - def mousePressEvent(self, e): - self.state_pressed = True + def font_color(self): + return self._font_color + + def setBorderColor(self, color): + if color is None: + color = Qt.gray + if color != self._border_color: + self._border_color = color + + def border_color(self): + return self._border_color + + def setBorderSize(self, size): + if size is None: + size = 2 + if type(size) != int: + raise TypeError("'size' must be int type or None") + if size != self._border_size: + self._border_size = size + + def border_size(self): + return self._border_size + + def setBorderPen(self, pen): + if pen is None: + pen = QPen(self.border_color(), self.border_size()) + if not isinstance(pen, QPen): + raise TypeError("'pen' must be QPen instance or None") + if pen != self._border_pen: + self._border_pen = pen + + def border_pen(self): + return self._border_pen + + def mousePressEvent(self, event): + self.__mouse_checked = True self.update() - return super(CalculatorButton, self).mousePressEvent(e) - def mouseReleaseEvent(self, e): - self.state_pressed = False + def mouseReleaseEvent(self, event): + self.__mouse_checked = False + self.clicked.emit(True) self.update() - return super(CalculatorButton, self).mouseReleaseEvent(e) + def mouseMoveEvent(self, event): + self.__mouse_over = True + def leaveEvent(self, event): + self.__mouse_over = False From 5099c9b653e64ec6c77d1fd0d752bb4ff6170ab4 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Tue, 5 Sep 2023 00:23:32 +0200 Subject: [PATCH 12/22] Scientific calculator start to work --- .../Calculator.app/Resources/calculator.py | 137 +++++++++++++++--- .../Calculator.app/Resources/main_window.ui | 29 +++- .../Resources/main_window_ui.py | 12 +- 3 files changed, 146 insertions(+), 32 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 178e96f5a..896955511 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -36,11 +36,12 @@ """PyCalc is a simple calculator built using Python and PyQt5.""" import os, sys +from math import cos, log, tan, sin, cosh, tanh, sinh from functools import partial # Import QApplication and the required widgets from PyQt5.QtWidgets -from PyQt5.QtCore import Qt,QSize +from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QMainWindow from PyQt5.QtWidgets import QMessageBox @@ -307,7 +308,6 @@ def _display_scientific(self): self.stacked_widget.setCurrentIndex(1) - # Create a Model to handle the calculator's operation def evaluateExpression(expression): """Evaluate an expression.""" @@ -422,6 +422,104 @@ def _neg(self): self._view.setDisplayText(str(result)) + def _cos(self): + """Evaluate expressions value and display the cos of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = cos(float(result)) + else: + result = cos(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _cosh(self): + """Evaluate expressions value and display the cosh of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = cosh(float(result)) + else: + result = cosh(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _sin(self): + """Evaluate expressions value and display the sin of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = sin(float(result)) + else: + result = sin(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _sinh(self): + """Evaluate expressions value and display the sinh of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = sinh(float(result)) + else: + result = sinh(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _tan(self): + """Evaluate expressions value and display the tan of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = tan(float(result)) + else: + result = tan(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _tanh(self): + """Evaluate expressions value and display the tanh of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = tanh(float(result)) + else: + result = tanh(int(result)) + except OverflowError: + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _log(self): + """Evaluate expressions value and display the log of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = log(float(result)) + else: + result = log(int(result)) + except (OverflowError, ValueError): + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + def _buildExpression(self, sub_exp): """Build expression.""" if self._view.displayText() == ERROR_MSG: @@ -434,6 +532,7 @@ def _connectSignals(self): """Connect signals and slots.""" # Display signals self._view.display.returnPressed.connect(self._calculateResult) + """self._view.display.escapePressed.connect(self._view.clearDisplay)""" # Connect Basic Layout Button for btnText, btn in self._view.basic_buttons.items(): @@ -450,7 +549,8 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): - if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±"}: + if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", + "log"}: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) @@ -462,32 +562,21 @@ def _connectSignals(self): self._view.scientific_buttons["M-"].clicked.connect(self._memory_subtract) self._view.scientific_buttons["MR"].clicked.connect(self._memory_print) - """self._view.display.escapePressed.connect(self._view.clearDisplay)""" + self._view.scientific_buttons["cos"].clicked.connect(self._cos) + self._view.scientific_buttons["cosh"].clicked.connect(self._cosh) + + self._view.scientific_buttons["sin"].clicked.connect(self._sin) + self._view.scientific_buttons["sinh"].clicked.connect(self._sinh) + + self._view.scientific_buttons["tan"].clicked.connect(self._tan) + self._view.scientific_buttons["tanh"].clicked.connect(self._tanh) + + self._view.scientific_buttons["log"].clicked.connect(self._log) -# Client code -# def main(): -# """Main function.""" -# # Create an instance of `QApplication` -# pycalc = QApplication(sys.argv) -# # Show the calculator's GUI -# view = PyCalcUi() -# view.show() -# # Create instances of the model and the controller -# model = evaluateExpression -# PyCalcCtrl(model=model, view=view) -# # Execute calculator's main loop -# sys.exit(pycalc.exec_()) -# -# -# if __name__ == "__main__": -# main() if __name__ == "__main__": app = QApplication(sys.argv) win = Window() - model = evaluateExpression PyCalcCtrl(model=model, view=win) - # Execute calculator's main loop - sys.exit(app.exec()) diff --git a/Utilities/Calculator.app/Resources/main_window.ui b/Utilities/Calculator.app/Resources/main_window.ui index 142da2175..0f3a68b00 100644 --- a/Utilities/Calculator.app/Resources/main_window.ui +++ b/Utilities/Calculator.app/Resources/main_window.ui @@ -62,8 +62,11 @@ 0 + + false + - background-color: "#cbe5c3"; + background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(42, 53, 28, 255), stop:0.0299625 rgba(175, 190, 171, 255), stop:0.962547 rgba(156, 187, 151, 255), stop:1 rgba(220, 239, 210, 255)); color: "#3f3f3f"; @@ -73,10 +76,10 @@ color: "#3f3f3f"; QFrame::Sunken - 2 + 6 - 2 + 4 @@ -146,7 +149,7 @@ color: "#3f3f3f"; 1 - 0 + 1 @@ -194,6 +197,21 @@ color: "#3f3f3f"; + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + @@ -329,6 +347,9 @@ color: "#3f3f3f"; + + false + Programmer diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py index faa553d8e..7542652a2 100644 --- a/Utilities/Calculator.app/Resources/main_window_ui.py +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -38,12 +38,13 @@ def setupUi(self, MainWindow): sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) self.frame.setSizePolicy(sizePolicy) - self.frame.setStyleSheet("background-color: \"#cbe5c3\";\n" + self.frame.setAutoFillBackground(False) + self.frame.setStyleSheet("background-color: qlineargradient(spread:pad, x1:0, y1:1, x2:0, y2:0, stop:0 rgba(42, 53, 28, 255), stop:0.0299625 rgba(175, 190, 171, 255), stop:0.962547 rgba(156, 187, 151, 255), stop:1 rgba(220, 239, 210, 255));\n" "color: \"#3f3f3f\";") self.frame.setFrameShape(QtWidgets.QFrame.WinPanel) self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) - self.frame.setLineWidth(2) - self.frame.setMidLineWidth(2) + self.frame.setLineWidth(6) + self.frame.setMidLineWidth(4) self.frame.setObjectName("frame") self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame) self.verticalLayout_4.setContentsMargins(0, 0, 0, 0) @@ -95,6 +96,8 @@ def setupUi(self, MainWindow): self.scientific = QtWidgets.QWidget() self.scientific.setObjectName("scientific") self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.scientific) + self.verticalLayout_5.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_5.setSpacing(0) self.verticalLayout_5.setObjectName("verticalLayout_5") self.scientific_buttons_layout = QtWidgets.QGridLayout() self.scientific_buttons_layout.setContentsMargins(3, -1, 3, 3) @@ -130,6 +133,7 @@ def setupUi(self, MainWindow): self.actionView_Scientific = QtWidgets.QAction(MainWindow) self.actionView_Scientific.setObjectName("actionView_Scientific") self.actionView_Programation = QtWidgets.QAction(MainWindow) + self.actionView_Programation.setEnabled(False) self.actionView_Programation.setObjectName("actionView_Programation") self.actionView_Show_Paper_Tape = QtWidgets.QAction(MainWindow) self.actionView_Show_Paper_Tape.setObjectName("actionView_Show_Paper_Tape") @@ -214,7 +218,7 @@ def setupUi(self, MainWindow): self.menubar.addAction(self.menuHelp.menuAction()) self.retranslateUi(MainWindow) - self.stacked_widget.setCurrentIndex(0) + self.stacked_widget.setCurrentIndex(1) self.actionClose_Calculator_Window.triggered.connect(MainWindow.close) # type: ignore QtCore.QMetaObject.connectSlotsByName(MainWindow) From 8a9d2b04c5765f60a429b23158e21c329409331f Mon Sep 17 00:00:00 2001 From: Tuuux Date: Tue, 5 Sep 2023 00:41:21 +0200 Subject: [PATCH 13/22] Fixe graphic buttons --- .../Resources/widget_calculator_button.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 46592f811..103e34f20 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -78,14 +78,16 @@ def paintEvent(self, e: QPaintEvent) -> None: self.painter.end() def draw_text(self): - if self.font_color(): - self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) - else: - self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) - self.painter.setFont(self.font) - self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), - (self.height() / 2) + self.font_metric.height() / 4, - self.text()) + if self.text() not in ["cos", "cosh", "tan", "tanh", "sin", "sinh", "log", "Rad", "EE", "RN", + "2nd", "ln", "1/x"]: + if self.font_color(): + self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) + else: + self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + self.painter.setFont(self.font) + self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), + (self.height() / 2) + self.font_metric.height() / 4, + self.text()) def draw_square(self, event): self.painter.setRenderHint(QPainter.Antialiasing) From 020d8c36834de59496551231228f59dc215d7b02 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Tue, 5 Sep 2023 23:47:37 +0200 Subject: [PATCH 14/22] Fixe graphic buttons --- .../Calculator.app/Resources/calculator.py | 21 ++++---- .../Resources/widget_calculator_button.py | 48 +++++++++++++------ 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 896955511..de533d439 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -44,7 +44,7 @@ from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QMainWindow -from PyQt5.QtWidgets import QMessageBox +from PyQt5.QtWidgets import QMessageBox, QSpacerItem, QSizePolicy from PyQt5.QtGui import QPixmap, QColor, QIcon # The Main Window @@ -140,6 +140,7 @@ def create_basic_layout(self): for btnText, pos in buttons.items(): # Create a button self.basic_buttons[btnText] = CalculatorButton(text=btnText) + # Apply Color if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: self.basic_buttons[btnText].setColor(QColor("#7a7a7b")) @@ -225,10 +226,11 @@ def create_scientific_layout(self): for btnText, pos in buttons.items(): # Create a button self.scientific_buttons[btnText] = CalculatorButton(text=btnText) + # Apply Color - if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: - self.scientific_buttons[btnText].setColor(QColor("#7a7a7b")) - self.scientific_buttons[btnText].setFontColor(QColor("#f7f6f6")) + if btnText in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "."]: + self.scientific_buttons[btnText].setColor(QColor("#eeeeed")) + self.scientific_buttons[btnText].setFontColor(QColor("#3f3f3f")) elif btnText == "=": self.scientific_buttons[btnText].setColor(QColor("#f09648")) self.scientific_buttons[btnText].setFontColor(QColor("#ffffff")) @@ -236,8 +238,8 @@ def create_scientific_layout(self): self.scientific_buttons[btnText].setColor(QColor("#f0003b")) self.scientific_buttons[btnText].setFontColor(QColor("#ffffff")) else: - self.scientific_buttons[btnText].setColor(QColor("#eeeeed")) - self.scientific_buttons[btnText].setFontColor(QColor("#3f3f3f")) + self.scientific_buttons[btnText].setColor(QColor("#7a7a7b")) + self.scientific_buttons[btnText].setFontColor(QColor("#f7f6f6")) # Apply location if btnText == "=": @@ -247,6 +249,9 @@ def create_scientific_layout(self): else: self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 1, 1) + spacer = QSpacerItem(6, 6, QSizePolicy.Minimum, QSizePolicy.Expanding) + self.scientific_buttons_layout.addItem(spacer, 0, 4, 6, 1) + def setDisplayText(self, text): """Set display's text.""" self.display.setText(text) @@ -549,8 +554,8 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): - if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", - "log"}: + if btnText not in ["=", "C", "MC", "M+", "M-", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", + "log"]: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 103e34f20..45f1b9316 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -50,7 +50,8 @@ def __init__(self, *args, **kwargs): def setupUI(self): self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) - self.font = QFont("Nimbus Sans", 11) + self.font = QFont("Nimbus Sans", 13) + # self.font = QFont("Nimbus Sans", 11) self.font_metric = QFontMetrics(self.font) self.setBorderColor(QColor("#1e1e1f")) self.setBorderSize(2) @@ -78,8 +79,7 @@ def paintEvent(self, e: QPaintEvent) -> None: self.painter.end() def draw_text(self): - if self.text() not in ["cos", "cosh", "tan", "tanh", "sin", "sinh", "log", "Rad", "EE", "RN", - "2nd", "ln", "1/x"]: + if self.text() not in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]: if self.font_color(): self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) else: @@ -96,20 +96,38 @@ def draw_square(self, event): if not self.__mouse_checked: if self.__mouse_over: - gradient = QLinearGradient(0, 0, 0, self.height() * 5) - gradient.setColorAt(0.0, Qt.white) - gradient.setColorAt(0.1, self.color()) - gradient.setColorAt(1.0, Qt.black) + gradient = QLinearGradient(0, 0, 0, self.height()) + gradient.setColorAt(0.0, self.color().darker(110)) + gradient.setColorAt(0.1, self.color().lighter(180)) + gradient.setColorAt(0.15, self.color().lighter(140)) + gradient.setColorAt(0.40, self.color().lighter(130)) + gradient.setColorAt(0.45, self.color()) + gradient.setColorAt(0.51, self.color().darker(110)) + gradient.setColorAt(0.9, self.color().darker(120)) + gradient.setColorAt(0.95, self.color().darker(180)) + gradient.setColorAt(1.0, self.color().darker(160)) else: - gradient = QLinearGradient(0, 0, 0, self.height() * 5) - gradient.setColorAt(0.0, Qt.white) - gradient.setColorAt(0.08, self.color()) - gradient.setColorAt(0.7, Qt.black) + gradient = QLinearGradient(0, 0, 0, self.height()) + gradient.setColorAt(0.0, self.color().darker(110)) + gradient.setColorAt(0.1, self.color().lighter(190)) + gradient.setColorAt(0.15, self.color().lighter(130)) + gradient.setColorAt(0.40, self.color().lighter(120)) + gradient.setColorAt(0.45, self.color()) + gradient.setColorAt(0.51, self.color().darker(120)) + gradient.setColorAt(0.9, self.color().darker(130)) + gradient.setColorAt(0.95, self.color().darker(190)) + gradient.setColorAt(1.0, self.color().darker(160)) else: - gradient = QLinearGradient(0, 0, 0, self.height() * 5) - gradient.setColorAt(0.0, Qt.lightGray) - gradient.setColorAt(0.06, self.color()) - gradient.setColorAt(0.95, Qt.lightGray) + gradient = QLinearGradient(0, 0, 0, self.height()) + gradient.setColorAt(0.0, self.color().darker(110)) + gradient.setColorAt(0.1, self.color().darker(120)) + gradient.setColorAt(0.15, self.color().darker(110)) + gradient.setColorAt(0.40, self.color().darker(105)) + gradient.setColorAt(0.5, self.color()) + gradient.setColorAt(0.51, self.color().lighter(105)) + gradient.setColorAt(0.9, self.color().lighter(110)) + gradient.setColorAt(0.95, self.color().lighter(170)) + gradient.setColorAt(1.0, self.color().lighter(140)) # Set painter colors to given values. From 1c9ae30af5a674dd872e0edde4b3b847a5483e81 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 00:39:42 +0200 Subject: [PATCH 15/22] Fixe M- and 2nd symbols --- Utilities/Calculator.app/Resources/calculator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index de533d439..1e1016e8a 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -111,7 +111,7 @@ def create_basic_layout(self): # First Line "MC": (0, 0), "M+": (0, 1), - "M-": (0, 2), + "M−": (0, 2), "MR": (0, 3), # Second line "C": (1, 0), @@ -142,7 +142,7 @@ def create_basic_layout(self): self.basic_buttons[btnText] = CalculatorButton(text=btnText) # Apply Color - if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M-", "MR"]: + if btnText in ["−", "±", "÷", "×", "+", "MC", "M+", "M−", "MR"]: self.basic_buttons[btnText].setColor(QColor("#7a7a7b")) self.basic_buttons[btnText].setFontColor(QColor("#f7f6f6")) elif btnText == "=": @@ -169,13 +169,13 @@ def create_scientific_layout(self): # Button text | position on the QGridLayout buttons = { # First Line - "2nd": (0, 0), + "2ⁿᵈ": (0, 0), "⟮": (0, 1), "⟯": (0, 2), "%": (0, 3), "MC": (0, 5), "M+": (0, 6), - "M-": (0, 7), + "M−": (0, 7), "MR": (0, 8), # Second line "1/x": (1, 0), @@ -541,7 +541,7 @@ def _connectSignals(self): # Connect Basic Layout Button for btnText, btn in self._view.basic_buttons.items(): - if btnText not in {"=", "C", "MC", "M+", "M-", "MR", "±"}: + if btnText not in {"=", "C", "MC", "M+", "M−", "MR", "±"}: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.basic_buttons["="].clicked.connect(self._calculateResult) @@ -549,12 +549,12 @@ def _connectSignals(self): self._view.basic_buttons["±"].clicked.connect(self._neg) self._view.basic_buttons["MC"].clicked.connect(self._memory_clear) self._view.basic_buttons["M+"].clicked.connect(self._memory_add) - self._view.basic_buttons["M-"].clicked.connect(self._memory_subtract) + self._view.basic_buttons["M−"].clicked.connect(self._memory_subtract) self._view.basic_buttons["MR"].clicked.connect(self._memory_print) # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): - if btnText not in ["=", "C", "MC", "M+", "M-", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", + if btnText not in ["=", "C", "MC", "M+", "M−", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", "log"]: btn.clicked.connect(partial(self._buildExpression, btnText)) @@ -564,7 +564,7 @@ def _connectSignals(self): self._view.scientific_buttons["±"].clicked.connect(self._neg) self._view.scientific_buttons["MC"].clicked.connect(self._memory_clear) self._view.scientific_buttons["M+"].clicked.connect(self._memory_add) - self._view.scientific_buttons["M-"].clicked.connect(self._memory_subtract) + self._view.scientific_buttons["M−"].clicked.connect(self._memory_subtract) self._view.scientific_buttons["MR"].clicked.connect(self._memory_print) self._view.scientific_buttons["cos"].clicked.connect(self._cos) From c3e92812608aa3d1d975d455a43daa424efc3265 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 09:32:32 +0200 Subject: [PATCH 16/22] Optimize speed and add power2 power3 and pi --- .../Calculator.app/Resources/calculator.py | 71 +++++++++++++++---- 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 1e1016e8a..4b01ac5a4 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -35,13 +35,14 @@ """PyCalc is a simple calculator built using Python and PyQt5.""" -import os, sys -from math import cos, log, tan, sin, cosh, tanh, sinh +from os import path +import sys +from math import cos, log, tan, sin, cosh, tanh, sinh, pow, pi from functools import partial # Import QApplication and the required widgets from PyQt5.QtWidgets -from PyQt5.QtCore import Qt, QSize +from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QApplication from PyQt5.QtWidgets import QMainWindow from PyQt5.QtWidgets import QMessageBox, QSpacerItem, QSizePolicy @@ -79,7 +80,7 @@ def __init__(self, parent=None): self.create_scientific_layout() self.connectSignalsSlots() - self.setWindowIcon(QIcon(os.path.join(os.path.dirname(__file__), "Calculator.png"))) + self.setWindowIcon(QIcon(path.join(path.dirname(__file__), "Calculator.png"))) self._display_basic() self.show() @@ -98,7 +99,6 @@ def connectSignalsSlots(self): self.actionView_Scientific.triggered.connect(self._display_scientific) def setupCustomUi(self): - # Paper Tape self.paper_tape_dialog = PaperTape() self.paper_tape_dialog.hide() @@ -214,7 +214,7 @@ def create_scientific_layout(self): "3": (4, 7), "=": (4, 8), - # the last line got only 2 buttons + # the last line "Rad": (5, 0), "⫪": (5, 1), "EE": (5, 2), @@ -277,15 +277,15 @@ def _showAboutDialog(): msg.setWindowTitle("About") msg.setIconPixmap( QPixmap( - os.path.join( - os.path.dirname(__file__), + path.join( + path.dirname(__file__), "Calculator.png" ) ).scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation) ) for candidate in ["COPYRIGHT", "COPYING", "LICENSE"]: - if os.path.exists(os.path.join(os.path.dirname(__file__), candidate)): - with open(os.path.join(os.path.dirname(__file__), candidate), 'r') as file: + if path.exists(path.join(path.dirname(__file__), candidate)): + with open(path.join(path.dirname(__file__), candidate), 'r') as file: data = file.read() msg.setDetailedText(data) msg.setText("

Calculator

") @@ -525,12 +525,54 @@ def _log(self): self._view.setDisplayText(str(result)) + def _power2(self): + """Evaluate expressions value and display the power2 of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = pow(float(result), 2) + else: + result = pow(int(result), 2) + except (OverflowError, ValueError): + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _power3(self): + """Evaluate expressions value and display the power3 of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = pow(float(result), 3) + else: + result = pow(int(result), 3) + except (OverflowError, ValueError): + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _pi(self): + """Evaluate expressions value and display the power3 of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + try: + if "." in result: + result = pow(float(result), 3) + else: + result = pow(int(result), 3) + except (OverflowError, ValueError): + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + def _buildExpression(self, sub_exp): """Build expression.""" if self._view.displayText() == ERROR_MSG: self._view.clearDisplay() - expression = self._view.displayText() + sub_exp + expression = "".join([self._view.displayText(), str(sub_exp)]) self._view.setDisplayText(expression) def _connectSignals(self): @@ -555,7 +597,7 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): if btnText not in ["=", "C", "MC", "M+", "M−", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", - "log"]: + "log", "x²", "x³", "⫪"]: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) @@ -578,6 +620,11 @@ def _connectSignals(self): self._view.scientific_buttons["log"].clicked.connect(self._log) + self._view.scientific_buttons["x²"].clicked.connect(self._power2) + self._view.scientific_buttons["x³"].clicked.connect(self._power3) + + self._view.scientific_buttons["⫪"].clicked.connect(partial(self._buildExpression, pi)) + if __name__ == "__main__": app = QApplication(sys.argv) From 26c5a4534800924f16063116f08151496de4e06d Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 09:58:19 +0200 Subject: [PATCH 17/22] add inverse --- Utilities/Calculator.app/Resources/calculator.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 4b01ac5a4..35a288f0a 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -63,7 +63,7 @@ ERROR_MSG = "ERROR" -# Create a subclass of QMainWindow to setup the calculator's GUI +# Create a subclass of QMainWindow to set up the calculator's GUI class Window(QMainWindow, Ui_MainWindow): """PyCalc's View (GUI).""" @@ -88,10 +88,8 @@ def setupInitialState(self): self.display.setAlignment(Qt.AlignRight) self.scientific_buttons = {} self.basic_buttons = {} - # self.display.setReadOnly(False) def connectSignalsSlots(self): - # Menu and ToolBar self.ActionMenuHelpAbout.triggered.connect(self._showAboutDialog) self.actionView_Show_Paper_Tape.triggered.connect(self._showPaperTape) @@ -553,15 +551,15 @@ def _power3(self): self._view.setDisplayText(str(result)) - def _pi(self): - """Evaluate expressions value and display the power3 of the value""" + def _inverse(self): + """Evaluate expressions value and display the 1/x of the value""" result = self._evaluate(expression=self._view.displayText()) if result and "ERROR" not in result: try: if "." in result: - result = pow(float(result), 3) + result = 1/float(result) else: - result = pow(int(result), 3) + result = 1/int(result) except (OverflowError, ValueError): result = ERROR_MSG @@ -597,7 +595,7 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): if btnText not in ["=", "C", "MC", "M+", "M−", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", - "log", "x²", "x³", "⫪"]: + "log", "x²", "x³", "⫪", "1/x"]: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) @@ -625,6 +623,8 @@ def _connectSignals(self): self._view.scientific_buttons["⫪"].clicked.connect(partial(self._buildExpression, pi)) + self._view.scientific_buttons["1/x"].clicked.connect(self._inverse) + if __name__ == "__main__": app = QApplication(sys.argv) From e026455964bd074256d3cc90670b7b9a0c406937 Mon Sep 17 00:00:00 2001 From: probonopd Date: Wed, 6 Sep 2023 18:53:09 +0200 Subject: [PATCH 18/22] Call it "Close" --- Utilities/Calculator.app/Resources/main_window_ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py index 7542652a2..dafd63457 100644 --- a/Utilities/Calculator.app/Resources/main_window_ui.py +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -229,7 +229,7 @@ def retranslateUi(self, MainWindow): self.menuView.setTitle(_translate("MainWindow", "View")) self.menuHelp.setTitle(_translate("MainWindow", "Help")) self.menuConvert.setTitle(_translate("MainWindow", "Convert")) - self.actionClose_Calculator_Window.setText(_translate("MainWindow", "Close calculator Window")) + self.actionClose_Calculator_Window.setText(_translate("MainWindow", "Close")) self.actionClose_Calculator_Window.setShortcut(_translate("MainWindow", "Ctrl+W")) self.actionSave_Tape_As.setText(_translate("MainWindow", "Save Tape As...")) self.actionSave_Tape_As.setShortcut(_translate("MainWindow", "Ctrl+Shift+S")) From 9a7c8c2a650d9d24a1224cc6aa7a9e0e8009d47c Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 22:38:41 +0200 Subject: [PATCH 19/22] Permit a disable button, disable every unimplemented function --- .../Calculator.app/Resources/calculator.py | 54 ++++++++++--- .../Resources/dialog_paper_tape.py | 14 ++++ .../Resources/dialog_paper_tape.ui | 5 +- .../Resources/dialog_paper_tape_ui.py | 3 +- .../Calculator.app/Resources/main_window.ui | 17 +++- .../Resources/main_window_ui.py | 5 ++ .../Resources/widget_calculator_button.py | 80 ++++++++++++------- 7 files changed, 136 insertions(+), 42 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 35a288f0a..16bd9520c 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -72,6 +72,7 @@ def __init__(self, parent=None): self.paper_tape_dialog = None self.scientific_buttons = None self.basic_buttons = None + self.asking_question = None self.setupUi(self) self.setupCustomUi() @@ -85,9 +86,11 @@ def __init__(self, parent=None): self.show() def setupInitialState(self): + self.asking_question = False self.display.setAlignment(Qt.AlignRight) self.scientific_buttons = {} self.basic_buttons = {} + self.paper_tape_dialog.hide() def connectSignalsSlots(self): # Menu and ToolBar @@ -99,7 +102,6 @@ def connectSignalsSlots(self): def setupCustomUi(self): # Paper Tape self.paper_tape_dialog = PaperTape() - self.paper_tape_dialog.hide() def create_basic_layout(self): """Create the basic layout buttons.""" @@ -247,6 +249,9 @@ def create_scientific_layout(self): else: self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 1, 1) + if btnText in ["Rad", "EE", "RN", "eˣ", "2ⁿᵈ", "n", "yˣ", "In", "x!", "ˣ√𝑦", "√"]: + self.scientific_buttons[btnText].setEnabled(False) + spacer = QSpacerItem(6, 6, QSizePolicy.Minimum, QSizePolicy.Expanding) self.scientific_buttons_layout.addItem(spacer, 0, 4, 6, 1) @@ -296,9 +301,12 @@ def _showPaperTape(self): if self.paper_tape_dialog.isVisible(): self.paper_tape_dialog.hide() else: + self.paper_tape_dialog.move(self.pos().x() - self.paper_tape_dialog.width(), self.pos().y()) self.paper_tape_dialog.show() + self.activateWindow() self.setFocus() + self.raise_() def _display_basic(self): self.setFixedWidth(200) @@ -359,16 +367,19 @@ def memory(self, value): def _calculateResult(self): """Evaluate expressions.""" + if not self._view.asking_question: + + result = self._evaluate(expression=self._view.displayText()) + if result: + self._view.paper_tape_dialog.plainTextEdit.setPlainText( + "%s\n\n%s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), + self._view.displayText())) + self._view.paper_tape_dialog.plainTextEdit.setPlainText( + "%s\n= %s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), + result)) + self._view.setDisplayText(result) + - result = self._evaluate(expression=self._view.displayText()) - if result: - self._view.paper_tape_dialog.plainTextEdit.setPlainText( - "%s\n\n%s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), - self._view.displayText())) - self._view.paper_tape_dialog.plainTextEdit.setPlainText( - "%s\n= %s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), - result)) - self._view.setDisplayText(result) def _memory_clear(self): """Clear memory by set value to None""" @@ -551,6 +562,26 @@ def _power3(self): self._view.setDisplayText(str(result)) + def _powerx(self): + """Evaluate expressions value and display the powerX of the value""" + result = self._evaluate(expression=self._view.displayText()) + if result and "ERROR" not in result: + self._view.asking_question = True + self._view.setDisplayText("Expose ") + + + self._view.asking_question = False + try: + if "." in result: + result = pow(float(result), 3) + else: + result = pow(int(result), 3) + except (OverflowError, ValueError): + result = ERROR_MSG + + self._view.setDisplayText(str(result)) + + def _inverse(self): """Evaluate expressions value and display the 1/x of the value""" result = self._evaluate(expression=self._view.displayText()) @@ -595,7 +626,7 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): if btnText not in ["=", "C", "MC", "M+", "M−", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", - "log", "x²", "x³", "⫪", "1/x"]: + "log", "x²", "x³", "yˣ", "⫪", "1/x"]: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) @@ -620,6 +651,7 @@ def _connectSignals(self): self._view.scientific_buttons["x²"].clicked.connect(self._power2) self._view.scientific_buttons["x³"].clicked.connect(self._power3) + # self._view.scientific_buttons["yˣ"].clicked.connect(self._powerx) self._view.scientific_buttons["⫪"].clicked.connect(partial(self._buildExpression, pi)) diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape.py b/Utilities/Calculator.app/Resources/dialog_paper_tape.py index 912b18df3..c6182ee4d 100644 --- a/Utilities/Calculator.app/Resources/dialog_paper_tape.py +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape.py @@ -1,4 +1,6 @@ from PyQt5.QtWidgets import QWidget +from PyQt5.QtCore import Qt + from dialog_paper_tape_ui import Ui_PaperTape @@ -11,6 +13,9 @@ def __init__(self, parent=None, process=None): # When you want to destroy the dialog set this to True self.have_to_close = False + self.setFocusPolicy(Qt.ClickFocus) + + def closeEvent(self, evnt): # That widget is call as a window, and should be close with the main app @@ -20,3 +25,12 @@ def closeEvent(self, evnt): else: evnt.ignore() self.hide() + + + + + # def focusOutEvent(self, event): + # self.setFocus() + # self.activateWindow() + # self.raise_() + # self.show() diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape.ui b/Utilities/Calculator.app/Resources/dialog_paper_tape.ui index 864aa9308..1a16ddb90 100644 --- a/Utilities/Calculator.app/Resources/dialog_paper_tape.ui +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape.ui @@ -11,7 +11,7 @@ - Form + Paper Tape @@ -23,6 +23,9 @@ + + false + Recalculate Totals diff --git a/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py b/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py index 1d8e53120..99d419b7c 100644 --- a/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py +++ b/Utilities/Calculator.app/Resources/dialog_paper_tape_ui.py @@ -21,6 +21,7 @@ def setupUi(self, PaperTape): self.pushButton_clear.setObjectName("pushButton_clear") self.gridLayout.addWidget(self.pushButton_clear, 1, 1, 1, 1) self.pushButton_recalculate_totals = QtWidgets.QPushButton(PaperTape) + self.pushButton_recalculate_totals.setEnabled(False) self.pushButton_recalculate_totals.setObjectName("pushButton_recalculate_totals") self.gridLayout.addWidget(self.pushButton_recalculate_totals, 1, 0, 1, 1) self.plainTextEdit = QtWidgets.QPlainTextEdit(PaperTape) @@ -33,6 +34,6 @@ def setupUi(self, PaperTape): def retranslateUi(self, PaperTape): _translate = QtCore.QCoreApplication.translate - PaperTape.setWindowTitle(_translate("PaperTape", "Form")) + PaperTape.setWindowTitle(_translate("PaperTape", "Paper Tape")) self.pushButton_clear.setText(_translate("PaperTape", "Clear")) self.pushButton_recalculate_totals.setText(_translate("PaperTape", "Recalculate Totals")) diff --git a/Utilities/Calculator.app/Resources/main_window.ui b/Utilities/Calculator.app/Resources/main_window.ui index 0f3a68b00..5ba6b995b 100644 --- a/Utilities/Calculator.app/Resources/main_window.ui +++ b/Utilities/Calculator.app/Resources/main_window.ui @@ -300,13 +300,16 @@ color: "#3f3f3f"; - Close calculator Window + Close Ctrl+W + + false + Save Tape As... @@ -315,6 +318,9 @@ color: "#3f3f3f"; + + false + Page Setup... @@ -323,6 +329,9 @@ color: "#3f3f3f"; + + false + Print Tape... @@ -363,11 +372,17 @@ color: "#3f3f3f"; + + false + RPN + + false + Precision diff --git a/Utilities/Calculator.app/Resources/main_window_ui.py b/Utilities/Calculator.app/Resources/main_window_ui.py index dafd63457..615b2aa6d 100644 --- a/Utilities/Calculator.app/Resources/main_window_ui.py +++ b/Utilities/Calculator.app/Resources/main_window_ui.py @@ -123,10 +123,13 @@ def setupUi(self, MainWindow): self.actionClose_Calculator_Window = QtWidgets.QAction(MainWindow) self.actionClose_Calculator_Window.setObjectName("actionClose_Calculator_Window") self.actionSave_Tape_As = QtWidgets.QAction(MainWindow) + self.actionSave_Tape_As.setEnabled(False) self.actionSave_Tape_As.setObjectName("actionSave_Tape_As") self.actionPage_Setup = QtWidgets.QAction(MainWindow) + self.actionPage_Setup.setEnabled(False) self.actionPage_Setup.setObjectName("actionPage_Setup") self.actionPrint_Tape = QtWidgets.QAction(MainWindow) + self.actionPrint_Tape.setEnabled(False) self.actionPrint_Tape.setObjectName("actionPrint_Tape") self.actionView_Basic = QtWidgets.QAction(MainWindow) self.actionView_Basic.setObjectName("actionView_Basic") @@ -138,8 +141,10 @@ def setupUi(self, MainWindow): self.actionView_Show_Paper_Tape = QtWidgets.QAction(MainWindow) self.actionView_Show_Paper_Tape.setObjectName("actionView_Show_Paper_Tape") self.actionView_RPN = QtWidgets.QAction(MainWindow) + self.actionView_RPN.setEnabled(False) self.actionView_RPN.setObjectName("actionView_RPN") self.actionView_Precision = QtWidgets.QAction(MainWindow) + self.actionView_Precision.setEnabled(False) self.actionView_Precision.setObjectName("actionView_Precision") self.actionConvert_Recent_Convertions = QtWidgets.QAction(MainWindow) self.actionConvert_Recent_Convertions.setEnabled(False) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 45f1b9316..762247d7a 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -80,10 +80,14 @@ def paintEvent(self, e: QPaintEvent) -> None: def draw_text(self): if self.text() not in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]: - if self.font_color(): - self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) + if self.isEnabled(): + if self.font_color(): + self.painter.setPen(QPen(self.font_color(), 1, Qt.SolidLine)) + else: + self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) else: - self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + self.painter.setPen(QPen(Qt.lightGray, 1, Qt.SolidLine)) + self.painter.setFont(self.font) self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), (self.height() / 2) + self.font_metric.height() / 4, @@ -93,37 +97,48 @@ def draw_square(self, event): self.painter.setRenderHint(QPainter.Antialiasing) # Create the path path = QPainterPath() - - if not self.__mouse_checked: - if self.__mouse_over: - gradient = QLinearGradient(0, 0, 0, self.height()) - gradient.setColorAt(0.0, self.color().darker(110)) - gradient.setColorAt(0.1, self.color().lighter(180)) - gradient.setColorAt(0.15, self.color().lighter(140)) - gradient.setColorAt(0.40, self.color().lighter(130)) - gradient.setColorAt(0.45, self.color()) - gradient.setColorAt(0.51, self.color().darker(110)) - gradient.setColorAt(0.9, self.color().darker(120)) - gradient.setColorAt(0.95, self.color().darker(180)) - gradient.setColorAt(1.0, self.color().darker(160)) + if self.isEnabled(): + if not self.__mouse_checked: + if self.__mouse_over: + gradient = QLinearGradient(0, 0, 0, self.height()) + gradient.setColorAt(0.0, self.color().darker(110)) + gradient.setColorAt(0.1, self.color().lighter(180)) + gradient.setColorAt(0.15, self.color().lighter(140)) + gradient.setColorAt(0.40, self.color().lighter(130)) + gradient.setColorAt(0.45, self.color()) + gradient.setColorAt(0.51, self.color().darker(110)) + gradient.setColorAt(0.9, self.color().darker(120)) + gradient.setColorAt(0.95, self.color().darker(180)) + gradient.setColorAt(1.0, self.color().darker(160)) + else: + gradient = QLinearGradient(0, 0, 0, self.height()) + gradient.setColorAt(0.0, self.color().darker(110)) + gradient.setColorAt(0.1, self.color().lighter(190)) + gradient.setColorAt(0.15, self.color().lighter(130)) + gradient.setColorAt(0.40, self.color().lighter(120)) + gradient.setColorAt(0.45, self.color()) + gradient.setColorAt(0.51, self.color().darker(120)) + gradient.setColorAt(0.9, self.color().darker(130)) + gradient.setColorAt(0.95, self.color().darker(190)) + gradient.setColorAt(1.0, self.color().darker(160)) else: gradient = QLinearGradient(0, 0, 0, self.height()) gradient.setColorAt(0.0, self.color().darker(110)) - gradient.setColorAt(0.1, self.color().lighter(190)) - gradient.setColorAt(0.15, self.color().lighter(130)) - gradient.setColorAt(0.40, self.color().lighter(120)) - gradient.setColorAt(0.45, self.color()) - gradient.setColorAt(0.51, self.color().darker(120)) - gradient.setColorAt(0.9, self.color().darker(130)) - gradient.setColorAt(0.95, self.color().darker(190)) - gradient.setColorAt(1.0, self.color().darker(160)) + gradient.setColorAt(0.1, self.color().darker(120)) + gradient.setColorAt(0.15, self.color().darker(110)) + gradient.setColorAt(0.40, self.color().darker(105)) + gradient.setColorAt(0.5, self.color()) + gradient.setColorAt(0.51, self.color().lighter(105)) + gradient.setColorAt(0.9, self.color().lighter(110)) + gradient.setColorAt(0.95, self.color().lighter(170)) + gradient.setColorAt(1.0, self.color().lighter(140)) else: gradient = QLinearGradient(0, 0, 0, self.height()) gradient.setColorAt(0.0, self.color().darker(110)) gradient.setColorAt(0.1, self.color().darker(120)) gradient.setColorAt(0.15, self.color().darker(110)) gradient.setColorAt(0.40, self.color().darker(105)) - gradient.setColorAt(0.5, self.color()) + gradient.setColorAt(0.5,self.color()) gradient.setColorAt(0.51, self.color().lighter(105)) gradient.setColorAt(0.9, self.color().lighter(110)) gradient.setColorAt(0.95, self.color().lighter(170)) @@ -147,7 +162,10 @@ def draw_square(self, event): self.painter.strokePath(path, self.painter.pen()) # Text is use a drop shadow - self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + if self.isEnabled(): + self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) + else: + self.painter.setPen(QPen(Qt.darkGray, 1, Qt.SolidLine)) self.painter.setFont(self.font) self.painter.drawText(rect, Qt.AlignCenter, self.text()) @@ -165,7 +183,10 @@ def setColor(self, color): # self.textChanged.emit(self._text) def color(self): - return self._color + if self.isEnabled(): + return self._color + else: + return QColor(Qt.lightGray) def setFontColor(self, color): if color is None: @@ -184,7 +205,10 @@ def setBorderColor(self, color): self._border_color = color def border_color(self): - return self._border_color + if self.isEnabled(): + return self._border_color + else: + return QColor(Qt.lightGray) def setBorderSize(self, size): if size is None: From a9da41021cd5e39e27719af17a17ce85b4153e2d Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 23:14:34 +0200 Subject: [PATCH 20/22] Limit memory usage of disable colors --- .../Resources/widget_calculator_button.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 762247d7a..4b73b2131 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -45,6 +45,10 @@ def __init__(self, *args, **kwargs): self.font_metric = None self.painter = None + self.color_disable = QColor(Qt.lightGray) + self.font_color_disable = QColor(Qt.lightGray) + self.font_shadow_color_disable = QColor(Qt.darkGray) + self.border_color_disable = QColor(Qt.lightGray) self.setupUI() @@ -53,7 +57,7 @@ def setupUI(self): self.font = QFont("Nimbus Sans", 13) # self.font = QFont("Nimbus Sans", 11) self.font_metric = QFontMetrics(self.font) - self.setBorderColor(QColor("#1e1e1f")) + self.setBorderColor() self.setBorderSize(2) self.setBorderPen(QPen(self.border_color(), self.border_size())) self.setMouseTracking(True) @@ -65,12 +69,6 @@ def minimumSizeHint(self): self.font_metric.height() + (self.border_size() * 2) ) - # def sizeHint(self): - # return QSize( - # self.font_metric.width(self.text()) + (self.border_size() * 2), - # self.font_metric.height() + (self.border_size() * 2) - # ) - def paintEvent(self, e: QPaintEvent) -> None: self.painter.begin(self) @@ -165,7 +163,7 @@ def draw_square(self, event): if self.isEnabled(): self.painter.setPen(QPen(Qt.black, 1, Qt.SolidLine)) else: - self.painter.setPen(QPen(Qt.darkGray, 1, Qt.SolidLine)) + self.painter.setPen(QPen(self.font_shadow_color_disable, 1, Qt.SolidLine)) self.painter.setFont(self.font) self.painter.drawText(rect, Qt.AlignCenter, self.text()) @@ -186,7 +184,7 @@ def color(self): if self.isEnabled(): return self._color else: - return QColor(Qt.lightGray) + return self.color_disable def setFontColor(self, color): if color is None: @@ -196,11 +194,14 @@ def setFontColor(self, color): # self.textChanged.emit(self._text) def font_color(self): - return self._font_color + if self.isEnabled(): + return self._font_color + else: + return self.font_color_disable - def setBorderColor(self, color): + def setBorderColor(self, color=None): if color is None: - color = Qt.gray + color = QColor("#1e1e1f") if color != self._border_color: self._border_color = color @@ -208,7 +209,7 @@ def border_color(self): if self.isEnabled(): return self._border_color else: - return QColor(Qt.lightGray) + return self.border_color_disable def setBorderSize(self, size): if size is None: From 897d89eb95074cdc0d9de2f9a7597edd2acb5b44 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 23:25:45 +0200 Subject: [PATCH 21/22] Reformat files --- .../Calculator.app/Resources/calculator.py | 69 ++++++++++--------- .../Resources/widget_calculator_button.py | 27 +++----- 2 files changed, 49 insertions(+), 47 deletions(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 16bd9520c..90973b4f1 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -55,10 +55,7 @@ from dialog_paper_tape import PaperTape __version__ = "0.2" -__author__ = [ - "Leodanis Pozo Ramos & Contributors", - "Jérôme ORNECH alias Hierosme" -] +__author__ = ["Leodanis Pozo Ramos & Contributors", "Jérôme ORNECH alias Hierosme"] ERROR_MSG = "ERROR" @@ -204,7 +201,6 @@ def create_scientific_layout(self): "5": (3, 6), "6": (3, 7), "+": (3, 8), - "sinh": (4, 0), "cosh": (4, 1), "tanh": (4, 2), @@ -213,7 +209,6 @@ def create_scientific_layout(self): "2": (4, 6), "3": (4, 7), "=": (4, 8), - # the last line "Rad": (5, 0), "⫪": (5, 1), @@ -268,33 +263,31 @@ def clearDisplay(self): """Clear the display.""" self.setDisplayText("") - def closeEvent(self, evnt): + def closeEvent(self, event): self.paper_tape_dialog.have_to_close = True self.paper_tape_dialog.close() - super(Window, self).closeEvent(evnt) + super(Window, self).closeEvent(event) @staticmethod def _showAboutDialog(): msg = QMessageBox() msg.setWindowTitle("About") msg.setIconPixmap( - QPixmap( - path.join( - path.dirname(__file__), - "Calculator.png" - ) - ).scaled(64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation) + QPixmap(path.join(path.dirname(__file__), "Calculator.png")).scaled( + 64, 64, Qt.KeepAspectRatio, Qt.SmoothTransformation + ) ) for candidate in ["COPYRIGHT", "COPYING", "LICENSE"]: if path.exists(path.join(path.dirname(__file__), candidate)): - with open(path.join(path.dirname(__file__), candidate), 'r') as file: + with open(path.join(path.dirname(__file__), candidate), "r") as file: data = file.read() msg.setDetailedText(data) msg.setText("

Calculator

") msg.setInformativeText( "A simple calculator application written in PyQt5

" - "https://github.com/helloSystem/Utilities") + "https://github.com/helloSystem/Utilities" + ) msg.exec() def _showPaperTape(self): @@ -334,7 +327,7 @@ def evaluateExpression(expression): expression = expression.replace("⟯", ")") try: result = str(eval(expression, {}, {})) - except Exception: + except (Exception, BaseException): result = ERROR_MSG return result @@ -368,19 +361,16 @@ def memory(self, value): def _calculateResult(self): """Evaluate expressions.""" if not self._view.asking_question: - result = self._evaluate(expression=self._view.displayText()) if result: self._view.paper_tape_dialog.plainTextEdit.setPlainText( - "%s\n\n%s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), - self._view.displayText())) + "%s\n\n%s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), self._view.displayText()) + ) self._view.paper_tape_dialog.plainTextEdit.setPlainText( - "%s\n= %s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), - result)) + "%s\n= %s" % (self._view.paper_tape_dialog.plainTextEdit.toPlainText(), result) + ) self._view.setDisplayText(result) - - def _memory_clear(self): """Clear memory by set value to None""" self.memory = None @@ -569,7 +559,6 @@ def _powerx(self): self._view.asking_question = True self._view.setDisplayText("Expose ") - self._view.asking_question = False try: if "." in result: @@ -581,16 +570,15 @@ def _powerx(self): self._view.setDisplayText(str(result)) - def _inverse(self): """Evaluate expressions value and display the 1/x of the value""" result = self._evaluate(expression=self._view.displayText()) if result and "ERROR" not in result: try: if "." in result: - result = 1/float(result) + result = 1 / float(result) else: - result = 1/int(result) + result = 1 / int(result) except (OverflowError, ValueError): result = ERROR_MSG @@ -608,7 +596,7 @@ def _connectSignals(self): """Connect signals and slots.""" # Display signals self._view.display.returnPressed.connect(self._calculateResult) - """self._view.display.escapePressed.connect(self._view.clearDisplay)""" + # self._view.display.escapePressed.connect(self._view.clearDisplay) # Connect Basic Layout Button for btnText, btn in self._view.basic_buttons.items(): @@ -625,8 +613,27 @@ def _connectSignals(self): # Connect Scientific Layout Button for btnText, btn in self._view.scientific_buttons.items(): - if btnText not in ["=", "C", "MC", "M+", "M−", "MR", "±", "cos", "sin", "tan", "cosh", "sinh", "tanh", - "log", "x²", "x³", "yˣ", "⫪", "1/x"]: + if btnText not in [ + "=", + "C", + "MC", + "M+", + "M−", + "MR", + "±", + "cos", + "sin", + "tan", + "cosh", + "sinh", + "tanh", + "log", + "x²", + "x³", + "yˣ", + "⫪", + "1/x", + ]: btn.clicked.connect(partial(self._buildExpression, btnText)) self._view.scientific_buttons["="].clicked.connect(self._calculateResult) diff --git a/Utilities/Calculator.app/Resources/widget_calculator_button.py b/Utilities/Calculator.app/Resources/widget_calculator_button.py index 4b73b2131..ff13e7c5a 100644 --- a/Utilities/Calculator.app/Resources/widget_calculator_button.py +++ b/Utilities/Calculator.app/Resources/widget_calculator_button.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 -import os from PyQt5.QtCore import Qt, pyqtSignal, QRectF, QSize from PyQt5.QtGui import ( QPaintEvent, @@ -11,17 +10,13 @@ QFont, QPainterPath, QLinearGradient, - ) from PyQt5.QtWidgets import QAbstractButton, QSizePolicy class CalculatorButton(QAbstractButton): """ - Custom Qt Widget to show a chosen color. - - Left-clicking the button shows the color-chooser, while - right-clicking resets the color to None (no-color). + Custom Qt Widget to show a colored button color. """ colorChanged = pyqtSignal(object) @@ -55,7 +50,6 @@ def __init__(self, *args, **kwargs): def setupUI(self): self.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) self.font = QFont("Nimbus Sans", 13) - # self.font = QFont("Nimbus Sans", 11) self.font_metric = QFontMetrics(self.font) self.setBorderColor() self.setBorderSize(2) @@ -66,11 +60,10 @@ def setupUI(self): def minimumSizeHint(self): return QSize( self.font_metric.width(self.text()) + (self.border_size() * 2), - self.font_metric.height() + (self.border_size() * 2) + self.font_metric.height() + (self.border_size() * 2), ) def paintEvent(self, e: QPaintEvent) -> None: - self.painter.begin(self) self.draw_square(event=e) self.draw_text() @@ -87,9 +80,11 @@ def draw_text(self): self.painter.setPen(QPen(Qt.lightGray, 1, Qt.SolidLine)) self.painter.setFont(self.font) - self.painter.drawText((self.width() / 2) - (self.font_metric.width(self.text()) / 2), - (self.height() / 2) + self.font_metric.height() / 4, - self.text()) + self.painter.drawText( + (self.width() / 2) - (self.font_metric.width(self.text()) / 2), + (self.height() / 2) + self.font_metric.height() / 4, + self.text(), + ) def draw_square(self, event): self.painter.setRenderHint(QPainter.Antialiasing) @@ -136,14 +131,13 @@ def draw_square(self, event): gradient.setColorAt(0.1, self.color().darker(120)) gradient.setColorAt(0.15, self.color().darker(110)) gradient.setColorAt(0.40, self.color().darker(105)) - gradient.setColorAt(0.5,self.color()) + gradient.setColorAt(0.5, self.color()) gradient.setColorAt(0.51, self.color().lighter(105)) gradient.setColorAt(0.9, self.color().lighter(110)) gradient.setColorAt(0.95, self.color().lighter(170)) gradient.setColorAt(1.0, self.color().lighter(140)) # Set painter colors to given values. - self.painter.setPen(self.border_pen()) self.painter.setBrush(gradient) @@ -178,7 +172,7 @@ def text(self): def setColor(self, color): if color != self._color: self._color = color - # self.textChanged.emit(self._text) + # self.colorChanged.emit(self._text) def color(self): if self.isEnabled(): @@ -191,7 +185,7 @@ def setFontColor(self, color): color = Qt.black if color != self._font_color: self._font_color = color - # self.textChanged.emit(self._text) + # self.fontChanged.emit(self._text) def font_color(self): if self.isEnabled(): @@ -239,6 +233,7 @@ def mousePressEvent(self, event): def mouseReleaseEvent(self, event): self.__mouse_checked = False + # noinspection PyUnresolvedReferences self.clicked.emit(True) self.update() From 8f3091a95a915b5be415303ab17262f4ad05fbe2 Mon Sep 17 00:00:00 2001 From: Tuuux Date: Wed, 6 Sep 2023 23:28:15 +0200 Subject: [PATCH 22/22] Disable "%" --- Utilities/Calculator.app/Resources/calculator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Calculator.app/Resources/calculator.py b/Utilities/Calculator.app/Resources/calculator.py index 90973b4f1..4ddd3eb58 100755 --- a/Utilities/Calculator.app/Resources/calculator.py +++ b/Utilities/Calculator.app/Resources/calculator.py @@ -244,7 +244,7 @@ def create_scientific_layout(self): else: self.scientific_buttons_layout.addWidget(self.scientific_buttons[btnText], pos[0], pos[1], 1, 1) - if btnText in ["Rad", "EE", "RN", "eˣ", "2ⁿᵈ", "n", "yˣ", "In", "x!", "ˣ√𝑦", "√"]: + if btnText in ["Rad", "EE", "RN", "eˣ", "2ⁿᵈ", "n", "yˣ", "In", "x!", "ˣ√𝑦", "√", "%"]: self.scientific_buttons[btnText].setEnabled(False) spacer = QSpacerItem(6, 6, QSizePolicy.Minimum, QSizePolicy.Expanding)