Check out Kite (free AI Coding Assistant) → Link



Buy Me a Coffee? Your support is much appreciated!

PayPal Me: https://www.paypal.me/jiejenn/5
Venmo: @Jie-Jenn

Source Code:   

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView,  \
                            QHBoxLayout, QVBoxLayout, QFrame, QDesktopWidget, QAbstractItemView
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QIcon

COLOR1 = '#962D2E'

class TableWidgetItem(QTableWidgetItem):
    def __init__(self, text):
        super().__init__(text)
        self.setTextAlignment(Qt.AlignCenter)


class Tip_Calculator(QWidget):
    def __init__(self):
        super().__init__()
        self.window_width, self.window_height = 1700, 1150
        self.setFixedSize(self.window_width, self.window_height)
        self.setWindowTitle('Tip Calculator by Jay')
        self.setWindowIcon(QIcon(r'C:\Users\Jie\Google Drive\(YouTube Folder)\_To Upload\(PyQt5) Tip Calculator\Calculator.ico'))

        self.setStyleSheet('''
            QLineEdit {
                height: 45px;
            }
        ''')

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.subLayouts = {}

        self.inputFields()
        self.outputFields()
        self.insertSummaryTable()

    def insertSummaryTable(self):
        self.subLayouts['summaryTable'] = QVBoxLayout()
        self.layout.addLayout(self.subLayouts['summaryTable'])

        tiers = (
            (0.05, 'Very Poor'), 
            (0.1, 'Very Poor'),  
            (0.13, 'Poor'), 
            (0.15, 'Average'), 
            (0.18, 'Average'), 
            (0.2, 'Above Average'), 
            (0.23, 'Good'), 
            (0.25, 'Very Good'), 
            (0.28, 'Excellent'),
            (0.3, 'Excellent'), 
            (0.32, 'Amazing'), 
            (0.35, 'Amazing'), 
            (0.38, 'Amazing'),
            (0.4, 'Cousins'), 
            (0.5, 'Family')
        )

        self.summaryTable = QTableWidget(len(tiers), 6)
        self.summaryTable.setShowGrid(False)
        self.summaryTable.verticalHeader().setVisible(False)
        self.summaryTable.horizontalHeader().setSectionResizeMode(QHeaderView.Fixed)
        self.summaryTable.setAlternatingRowColors(True)
        self.summaryTable.setHorizontalHeaderLabels(
            ('Tip %', 'Tip Amount', 'Bill Amount', 'Bill Per Person', 'Tip Per Person', 'Service Scale')
        )
        self.summaryTable.setEditTriggers(QAbstractItemView.NoEditTriggers) # allow selection, but no editing

        self.summaryTable.setColumnWidth(0, 180)
        self.summaryTable.setColumnWidth(1, 270)
        self.summaryTable.setColumnWidth(2, 280)
        self.summaryTable.setColumnWidth(3, 280)
        self.summaryTable.setColumnWidth(4, 270)
        self.summaryTable.setColumnWidth(5, 380)

        self.summaryTable.setStyleSheet(f'''
            ::section {{
                background-color: {COLOR1};
                color: white;
                border: none;
            }}

        ''')

        for indx, tier in enumerate(tiers):
            item = TableWidgetItem(str('{:.0f}%'.format(tier[0] * 100)))
            self.summaryTable.setItem(indx, 0, item)

            self.summaryTable.setItem(indx, 1, TableWidgetItem('${:.2f}'.format(0)))
            self.summaryTable.setItem(indx, 2, TableWidgetItem('${:.2f}'.format(0)))
            self.summaryTable.setItem(indx, 3, TableWidgetItem('${:.2f}'.format(0)))
            self.summaryTable.setItem(indx, 4, TableWidgetItem('${:.2f}'.format(0)))

            item = TableWidgetItem(tier[1])
            self.summaryTable.setItem(indx, 5, item)

        self.subLayouts['summaryTable'].addWidget(self.summaryTable)

    def inputFields(self):
        self.subLayouts['inputLabels'] = QHBoxLayout()
        self.subLayouts['inputLabels'].setSpacing(15)
        self.layout.addLayout(self.subLayouts['inputLabels'])

        self.subLayouts['inputFields'] = QHBoxLayout()
        self.subLayouts['inputFields'].setSpacing(15)
        self.layout.addLayout(self.subLayouts['inputFields'])

        labels = ('Bill Amount ($)', 'Tip %', 'Number of People')
        for label in labels:
            self.subLayouts['inputLabels'].addWidget(QLabel(label + ' :'))

        hLine = QFrame(self)
        hLine.setFrameShadow(QFrame.Sunken)
        hLine.setFrameShape(QFrame.VLine)
        hLine.setGeometry(0, 135, QDesktopWidget().screenGeometry().width() + 300, 4)
        hLine.setStyleSheet(f'background-color: {COLOR1};')

        self.inputs = {}
        self.inputs['Bill Amount'] = QLineEdit()
        self.inputs['Tip Percentage'] = QLineEdit()
        self.inputs['Party Number'] = QLineEdit()

        for key, widget in self.inputs.items():
            self.subLayouts['inputFields'].addWidget(widget)
            widget.setPlaceholderText(f'Enter {key}')
            widget.setObjectName(key.replace(' ', ''))
            #TODO (Conigure Signals)
            # widget.editingFinished.connect()
            widget.textChanged.connect(self.calculateTips)

    def outputFields(self):
        self.layout.addSpacing(40)

        self.subLayouts['outputLabels'] = QHBoxLayout()
        self.subLayouts['outputLabels'].setSpacing(15)
        self.layout.addLayout(self.subLayouts['outputLabels'])

        self.subLayouts['outputFields'] = QHBoxLayout()
        self.subLayouts['outputFields'].setSpacing(15)
        self.layout.addLayout(self.subLayouts['outputFields'])

        labels = ('Tip Amount', 'Total Bill Amount', 'Tip Per Person', 'Total Bill Per Person')
        for label in labels:
            self.subLayouts['outputLabels'].addWidget(QLabel(label))
        
        self.outputs = {}
        self.outputs['Tip Amount'] = QLineEdit()
        self.outputs['Total Bill Amount'] = QLineEdit()
        self.outputs['Tip Per Person'] = QLineEdit()
        self.outputs['Total Bill Per Person'] = QLineEdit()

        for widget in self.outputs.values():
            widget.setReadOnly(True)
            self.subLayouts['outputFields'].addWidget(widget)

        self.layout.addSpacing(15)

    def calculateTips(self):
        try:
            tipAmount = float(self.inputs['Bill Amount'].text()) * float(self.inputs['Tip Percentage'].text()) / 100
            totalBillAmount = tipAmount + float(self.inputs['Bill Amount'].text())
            tipPerPerson = '{:.2f}'.format(tipAmount / int(self.inputs['Party Number'].text()))
            billPerPerson = '{:.2f}'.format(totalBillAmount / int(self.inputs['Party Number'].text()))
            tipAmount = '{:.2f}'.format(tipAmount)
            totalBillAmount = '{:.2f}'.format(totalBillAmount)

            self.outputs['Tip Amount'].setText('$' + str(tipAmount))
            self.outputs['Total Bill Amount'].setText('$' + str(totalBillAmount))
            self.outputs['Tip Per Person'].setText('$' + str(tipPerPerson))
            self.outputs['Total Bill Per Person'].setText('$' + str(billPerPerson))

            self.updateSummaryTable()

        except ValueError:
            self.outputs['Tip Amount'].setText('')
            self.outputs['Total Bill Amount'].setText('')
            self.outputs['Tip Per Person'].setText('')
            self.outputs['Total Bill Per Person'].setText('')

    def updateSummaryTable(self):
        for row in range(self.summaryTable.rowCount()):
            tip_percentage = float(self.summaryTable.item(row, 0).text().replace('%', '')) / 100
            tipAmount = float(self.inputs['Bill Amount'].text()) * tip_percentage

            totalBillAmount = tipAmount + float(self.inputs['Bill Amount'].text())
            tipPerPerson = '{:.2f}'.format(tipAmount / int(self.inputs['Party Number'].text()))

            billPerPerson = '{:.2f}'.format(totalBillAmount / int(self.inputs['Party Number'].text()))
            tipAmount = '{:.2f}'.format(tipAmount)
            totalBillAmount = '{:.2f}'.format(totalBillAmount)

            self.summaryTable.item(row, 1).setText('$' + str(tipAmount))
            self.summaryTable.item(row, 2).setText('$' + str(totalBillAmount))
            self.summaryTable.item(row, 3).setText('$' + str(tipPerPerson))
            self.summaryTable.item(row, 4).setText('$' + str(billPerPerson))

if __name__ == '__main__':
    # don't auto scale when drag app to a different monitor.
    # QApplication.setAttribute(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    
    app = QApplication(sys.argv)
    app.setStyleSheet('''
        QWidget {
            font-size: 30px;
        }
    ''')
    
    myApp = Tip_Calculator()
    myApp.show()

    try:
        sys.exit(app.exec_())
    except SystemExit:
        print('Closing Window...')