Article Outline
Python pyqt (gui) example 'calculator'
Modules used in program:
import math
calculator
Python pyqt example: calculator
#!/usr/bin/env python
#############################################################################
##
## Copyright (C) 2013 Riverbank Computing Limited.
## Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
## All rights reserved.
##
## This file is part of the examples of PyQt.
##
## $QT_BEGIN_LICENSE:BSD$
## You may use this file under the terms of the BSD license as follows:
##
## "Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions are
## met:
## * Redistributions of source code must retain the above copyright
## notice, this list of conditions and the following disclaimer.
## * Redistributions in binary form must reproduce the above copyright
## notice, this list of conditions and the following disclaimer in
## the documentation and/or other materials provided with the
## distribution.
## * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
## the names of its contributors may be used to endorse or promote
## products derived from this software without specific prior written
## permission.
##
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
## $QT_END_LICENSE$
##
#############################################################################
import math
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QGridLayout, QLayout, QLineEdit,
QSizePolicy, QToolButton, QWidget)
class Button(QToolButton):
def __init__(self, text, parent=None):
super(Button, self).__init__(parent)
self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred)
self.setText(text)
def sizeHint(self):
size = super(Button, self).sizeHint()
size.setHeight(size.height() + 20)
size.setWidth(max(size.width(), size.height()))
return size
class Calculator(QWidget):
NumDigitButtons = 10
def __init__(self, parent=None):
super(Calculator, self).__init__(parent)
self.pendingAdditiveOperator = ''
self.pendingMultiplicativeOperator = ''
self.sumInMemory = 0.0
self.sumSoFar = 0.0
self.factorSoFar = 0.0
self.waitingForOperand = True
self.display = QLineEdit('0')
self.display.setReadOnly(True)
self.display.setAlignment(Qt.AlignRight)
self.display.setMaxLength(15)
font = self.display.font()
font.setPointSize(font.pointSize() + 8)
self.display.setFont(font)
self.digitButtons = []
for i in range(Calculator.NumDigitButtons):
self.digitButtons.append(self.createButton(str(i),
self.digitClicked))
self.pointButton = self.createButton(".", self.pointClicked)
self.changeSignButton = self.createButton(u"\N{PLUS-MINUS SIGN}",
self.changeSignClicked)
self.backspaceButton = self.createButton("Backspace",
self.backspaceClicked)
self.clearButton = self.createButton("Clear", self.clear)
self.clearAllButton = self.createButton("Clear All", self.clearAll)
self.clearMemoryButton = self.createButton("MC", self.clearMemory)
self.readMemoryButton = self.createButton("MR", self.readMemory)
self.setMemoryButton = self.createButton("MS", self.setMemory)
self.addToMemoryButton = self.createButton("M+", self.addToMemory)
self.divisionButton = self.createButton(u"\N{DIVISION SIGN}",
self.multiplicativeOperatorClicked)
self.timesButton = self.createButton(u"\N{MULTIPLICATION SIGN}",
self.multiplicativeOperatorClicked)
self.minusButton = self.createButton("-", self.additiveOperatorClicked)
self.plusButton = self.createButton("+", self.additiveOperatorClicked)
self.squareRootButton = self.createButton("Sqrt",
self.unaryOperatorClicked)
self.powerButton = self.createButton(u"x\N{SUPERSCRIPT TWO}",
self.unaryOperatorClicked)
self.reciprocalButton = self.createButton("1/x",
self.unaryOperatorClicked)
self.equalButton = self.createButton("=", self.equalClicked)
mainLayout = QGridLayout()
mainLayout.setSizeConstraint(QLayout.SetFixedSize)
mainLayout.addWidget(self.display, 0, 0, 1, 6)
mainLayout.addWidget(self.backspaceButton, 1, 0, 1, 2)
mainLayout.addWidget(self.clearButton, 1, 2, 1, 2)
mainLayout.addWidget(self.clearAllButton, 1, 4, 1, 2)
mainLayout.addWidget(self.clearMemoryButton, 2, 0)
mainLayout.addWidget(self.readMemoryButton, 3, 0)
mainLayout.addWidget(self.setMemoryButton, 4, 0)
mainLayout.addWidget(self.addToMemoryButton, 5, 0)
for i in range(1, Calculator.NumDigitButtons):
row = ((9 - i) / 3) + 2
column = ((i - 1) % 3) + 1
mainLayout.addWidget(self.digitButtons[i], row, column)
mainLayout.addWidget(self.digitButtons[0], 5, 1)
mainLayout.addWidget(self.pointButton, 5, 2)
mainLayout.addWidget(self.changeSignButton, 5, 3)
mainLayout.addWidget(self.divisionButton, 2, 4)
mainLayout.addWidget(self.timesButton, 3, 4)
mainLayout.addWidget(self.minusButton, 4, 4)
mainLayout.addWidget(self.plusButton, 5, 4)
mainLayout.addWidget(self.squareRootButton, 2, 5)
mainLayout.addWidget(self.powerButton, 3, 5)
mainLayout.addWidget(self.reciprocalButton, 4, 5)
mainLayout.addWidget(self.equalButton, 5, 5)
self.setLayout(mainLayout)
self.setWindowTitle("Calculator")
def digitClicked(self):
clickedButton = self.sender()
digitValue = int(clickedButton.text())
if self.display.text() == '0' and digitValue == 0.0:
return
if self.waitingForOperand:
self.display.clear()
self.waitingForOperand = False
self.display.setText(self.display.text() + str(digitValue))
def unaryOperatorClicked(self):
clickedButton = self.sender()
clickedOperator = clickedButton.text()
operand = float(self.display.text())
if clickedOperator == "Sqrt":
if operand < 0.0:
self.abortOperation()
return
result = math.sqrt(operand)
elif clickedOperator == u"x\N{SUPERSCRIPT TWO}":
result = math.pow(operand, 2.0)
elif clickedOperator == "1/x":
if operand == 0.0:
self.abortOperation()
return
result = 1.0 / operand
self.display.setText(str(result))
self.waitingForOperand = True
def additiveOperatorClicked(self):
clickedButton = self.sender()
clickedOperator = clickedButton.text()
operand = float(self.display.text())
if self.pendingMultiplicativeOperator:
if not self.calculate(operand, self.pendingMultiplicativeOperator):
self.abortOperation()
return
self.display.setText(str(self.factorSoFar))
operand = self.factorSoFar
self.factorSoFar = 0.0
self.pendingMultiplicativeOperator = ''
if self.pendingAdditiveOperator:
if not self.calculate(operand, self.pendingAdditiveOperator):
self.abortOperation()
return
self.display.setText(str(self.sumSoFar))
else:
self.sumSoFar = operand
self.pendingAdditiveOperator = clickedOperator
self.waitingForOperand = True
def multiplicativeOperatorClicked(self):
clickedButton = self.sender()
clickedOperator = clickedButton.text()
operand = float(self.display.text())
if self.pendingMultiplicativeOperator:
if not self.calculate(operand, self.pendingMultiplicativeOperator):
self.abortOperation()
return
self.display.setText(str(self.factorSoFar))
else:
self.factorSoFar = operand
self.pendingMultiplicativeOperator = clickedOperator
self.waitingForOperand = True
def equalClicked(self):
operand = float(self.display.text())
if self.pendingMultiplicativeOperator:
if not self.calculate(operand, self.pendingMultiplicativeOperator):
self.abortOperation()
return
operand = self.factorSoFar
self.factorSoFar = 0.0
self.pendingMultiplicativeOperator = ''
if self.pendingAdditiveOperator:
if not self.calculate(operand, self.pendingAdditiveOperator):
self.abortOperation()
return
self.pendingAdditiveOperator = ''
else:
self.sumSoFar = operand
self.display.setText(str(self.sumSoFar))
self.sumSoFar = 0.0
self.waitingForOperand = True
def pointClicked(self):
if self.waitingForOperand:
self.display.setText('0')
if "." not in self.display.text():
self.display.setText(self.display.text() + ".")
self.waitingForOperand = False
def changeSignClicked(self):
text = self.display.text()
value = float(text)
if value > 0.0:
text = "-" + text
elif value < 0.0:
text = text[1:]
self.display.setText(text)
def backspaceClicked(self):
if self.waitingForOperand:
return
text = self.display.text()[:-1]
if not text:
text = '0'
self.waitingForOperand = True
self.display.setText(text)
def clear(self):
if self.waitingForOperand:
return
self.display.setText('0')
self.waitingForOperand = True
def clearAll(self):
self.sumSoFar = 0.0
self.factorSoFar = 0.0
self.pendingAdditiveOperator = ''
self.pendingMultiplicativeOperator = ''
self.display.setText('0')
self.waitingForOperand = True
def clearMemory(self):
self.sumInMemory = 0.0
def readMemory(self):
self.display.setText(str(self.sumInMemory))
self.waitingForOperand = True
def setMemory(self):
self.equalClicked()
self.sumInMemory = float(self.display.text())
def addToMemory(self):
self.equalClicked()
self.sumInMemory += float(self.display.text())
def createButton(self, text, member):
button = Button(text)
button.clicked.connect(member)
return button
def abortOperation(self):
self.clearAll()
self.display.setText("####")
def calculate(self, rightOperand, pendingOperator):
if pendingOperator == "+":
self.sumSoFar += rightOperand
elif pendingOperator == "-":
self.sumSoFar -= rightOperand
elif pendingOperator == u"\N{MULTIPLICATION SIGN}":
self.factorSoFar *= rightOperand
elif pendingOperator == u"\N{DIVISION SIGN}":
if rightOperand == 0.0:
return False
self.factorSoFar /= rightOperand
return True
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
calc = Calculator()
calc.show()
sys.exit(app.exec_())
Useful links
- Learn PyQt: https://pythonbasics.org/pyqt-hello-world/
- Install PyQt: https://pythonbasics.org/install-pyqt/