4
\$\begingroup\$

Recently I started to learn Python and PyQT5.

So here is my code for a calculator. Personally I don't like how the doMath() function is looking but I don't know how to make it more readable and pythonic.

import operator
import sys
from functools import partial
from PyQt5.QtWidgets import (QApplication, QShortcut, QLabel, QPushButton,
 QGridLayout, QWidget, QMainWindow, QLineEdit,
 QVBoxLayout, QLineEdit, QShortcut, QAction)
from PyQt5.QtGui import QKeySequence, QDoubleValidator
from PyQt5.QtCore import QLocale, Qt
class MyWindow(QMainWindow):
 def __init__(self, parent=None):
 super(MyWindow, self).__init__(parent)
 self.setGeometry(0, 0, 200, 300)
 self.setWindowTitle('PyQt5 calculator')
 self.buttonsText = [
 '<-', '=', '+',
 '/', '*', '-',
 '7', '8', '9',
 '4', '5', '6',
 '1', '2', '3',
 'c', '0', '.'
 ]
 self.col_num = 3
 self.operators = {'+': operator.add, '-': operator.sub,
 '/': operator.truediv, '*': operator.mul}
 self.operand = ''
 self.operator = ''
 self.initUI()
 self.setShortcuts()
 def initUI(self):
 # initialize widgets and layouts
 cenWidget = QWidget(self)
 vertLayout = QVBoxLayout(cenWidget)
 gridWidget = QWidget()
 gridLayout = QGridLayout(gridWidget)
 gridWidget.setLayout(gridLayout)
 self.hintField = QLabel(parent=cenWidget)
 # configure text filed and validator
 self.textField = QLineEdit()
 f = self.textField.font()
 f.setPointSize(18)
 self.textField.setFont(f)
 validator = QDoubleValidator()
 validator.setLocale(QLocale('English'))
 self.textField.setValidator(validator)
 # add widgets to top level layout
 vertLayout.addWidget(self.hintField)
 vertLayout.addWidget(self.textField)
 vertLayout.addWidget(gridWidget)
 # add buttons to grid layout
 self.buttonInstances = [QPushButton(item) for item in self.buttonsText]
 for i, button in enumerate(self.buttonInstances):
 button.clicked.connect(partial(self.doMath, button.text()))
 gridLayout.addWidget(button, i//self.col_num, i % self.col_num)
 self.setCentralWidget(cenWidget)
 def doMath(self, buttonValue):
 currentValue = self.textField.text()
 convertedValue = self.convertValue(currentValue)
 if buttonValue == 'c':
 self.clearFields()
 elif buttonValue == '<-':
 self.textField.setText(currentValue[:-1])
 elif buttonValue == '.':
 if '.' not in currentValue:
 self.textField.setText(currentValue+buttonValue)
 elif (not self.operand and buttonValue in self.operators
 and convertedValue != None):
 self.operator = buttonValue
 self.operand = convertedValue
 self.textField.setText('')
 elif (buttonValue == '=' or buttonValue in self.operators 
 and self.operator and convertedValue):
 answer = self.operators[self.operator](
 self.operand, convertedValue)
 self.clearFields()
 if buttonValue == '=':
 self.textField.setText(f'{answer}')
 else:
 self.operand = answer
 self.operator = buttonValue
 elif self.convertValue(buttonValue) != None:
 self.textField.setText(currentValue+buttonValue)
 self.hintField.setText(f'<h1><font color=#a0a0a0>{self.operand}' 
 f'{self.operator}</font></h1>')
 def setShortcuts(self):
 eqShort= QShortcut(QKeySequence('Enter'), self)
 eqShort.activated.connect(partial(self.doMath, '='))
 for sign in self.buttonsText[1:5]:
 short= QShortcut(QKeySequence(sign), self)
 short.activated.connect(partial(self.doMath, sign))
 def convertValue(self, value):
 try:
 return int(value)
 except ValueError:
 try:
 return float(value)
 except:
 return None
 def clearFields(self):
 self.operand = ''
 self.operator = ''
 self.textField.setText('')
app = QApplication(sys.argv)
window = MyWindow()
window.show()
sys.exit(app.exec_())
Toby Speight
87.9k14 gold badges104 silver badges325 bronze badges
asked Feb 5, 2020 at 7:16
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Make use of this small project to separate core logic from UI. You can have a class Calculator which retains current state, and your UI is just a way to display this class.

To do this, just think your design as if you were to replace UI by CLI. You have to be able to unplug your UI, plug CLI, and your calculator has to work the same. This is also, I think, an easier way to write unit tests or run batch functional tests.

It mainly concerns doMath and convertValue, which has nothing to do in UI code. Actually convertValue might disappear even in a Calculator class, because you will have the real value stored.

answered Feb 5, 2020 at 8:03
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.