The references we are using are William G. Suillivan engineering economy and professor Kashani.H presentations

Introduction

In this project we are going to make a python application program which simplifies calculating the time value of money and also shows deprecations table
We will start with the time value of money.

The Time Value of Money

The term capital refers to wealth in the form of money or property that can be used to produce more wealth. The majority of engineering economy studies involve commitment of capital for extended periods of time, so the effect of time must be considered. In this regard, it is recognized that a dollar today is worth more than a dollar one or more years from now because of the interest (or profit) it can earn. Therefore, money has a time value. It has been said that often the riskiest thing a person can do with money is nothing! Money has value, and if money remains uninvested (like in a large bottle), value is lost Chapter 4 - Engineering Economy

The Way We Are In!

Consider you are going through calculating interest of a bargain. You will need to know some stuff such as which type of interest will be applied to this transaction. So we need understand different type of interest. here we go:

Simple Interest

When the total interest earned or charged is linearly proportional to the initial amount of the loan (principal), the interest rate, and the number of interest periods for which the principal is committed, the interest and interest rate are said to be simple. Simple interest is not used frequently in modern commercial practice.

$$i_t = P * N * {i}$$

Compound Interest

Whenever the interest charge for any interest period (a year, for example) is based on the remaining principal amount plus any accumulated interest charges up to the beginning of that period, the interest is said to be compound. In short, compounding amounts to earning money on your reinvested earnings as well as your original savings.

After this section, we will see how to calculate Present , Future values and Also Annual Rate

Finding F when Given P

$$F = P(1+i)^N$$

Finding P when Given F

$$P = {F({1 \over i+1})^N}$$

Finding the Interest Rate Given P, F, and N

$$i = \sqrt[N]{F \over P} - 1$$

Finding N when Given P, F, and i

$$N = {log({F \over P}) \over log({1+i})}$$

Finding F when Given A

$$F = {A[{(i+1)^N - 1 \over i}]}$$

Finding P when Given A

$$P = {A[{(i+1)^N-1 \over i(i+1)^N}]}$$

Finding A when Given F

$$A = {F[{i \over (i+1)^N - 1}]}$$

Finding A when Given P

$$A = {P[{i(i+1)^N \over (i+1)^N - 1}]}$$

Engineering Into Python

So the journey begins now. we wanna make all the mentioned processes programmatically. we don't want just make a script, wa wanna make an user friendly application (also known as GUI or Graphical User Interface).

Introducing Modules

The very first module we are going to use is Pandas. This module lets us to have dictionaries as dataframes. In order to not to prolong the text, we assume user has base knowledge about dataframes.for more information please feel free to go to Dataframes
Next module is Numpy.One of the uses of this module to apply mathematical operations on arrays and lists.
The very important module that lets us to have graphical interface is PyQt. This module has most of interfaces the let us to get command(known as signal) from user and connect these to a operator(known as slot).
We used many fundamental modules that simplified our script. we leave this part to you to see them in the current script we are going to write.

Starting Coding!

Heretofore, we get familiar to some modules and also the fundamental functions and variables. Now, we start writing the scripts. We write these in Object-Oriented Model. So we need to know class models as well.

Interest Script

import math
class interest():
    # Simple Interest
    class simple_interest():
        def total_interest( P , N , i):
            return P * N * (i/100)
    # Compound Interest
    class compound_interest():
        def total_interest(P,N,i):
            got_money = P
            for iterate in range(N):
                got_money *= 1+(i)/100
            return got_money
    # Present and Future
    class present_and_future():
        def find_f(P,N,i):
            return P*(((i/100)+1)**N)
        def find_p(F,N,i):
            return F/(((i/100)+1)**N)
        def find_interest(F,P,N):
            return 1-(F/P)**(1/N) 
        def find_n(P,F,i):
            return (math.log(F/P))/(math.log((i/100)+1))
    # Annual Investment

    class annual_investment():
        def find_f(A, N, i):
            sum_to_return = 0
            P_F = 1 / (1+(i/100))**N
            present_future = interest().get_present_and_future()
            for x in range(1 , N+1):
                sum_to_return += present_future.find_f(1,N-x,i)
            return A*sum_to_return
            return sum_to_return
        def find_p(A, N, i):
            return A*(((i/100)+1)**N - 1)/((i/100)*((1+(i/100))**N))
        
        def find_from_f(F,N,i):
            return F*((i/100)/(((i/100)+1)**N-1))
        
        def find_from_p(P,N,i):
            return P*((i/100)*(1+(i/100))**N/(((i/100)+1)**N-1))
        
        def find_n_from_p(A,P,i):
            to_achieve = P/A
            N = 1
            while(True):
                P_over_A = ((i+1)**N - 1)/(i*((1+i)**N))
                if P_over_A > to_achieve:
                    return N
                N += 1
        def find_interest_from_f(A,F,N):
            to_achieve = F/A
            i = 0.01
            while(True):
                F_over_A = (i/(((i+1)**N) - 1))**(-1)
                print
                if F_over_A > to_achieve:
                    return i
                i += 0.01
            
    
            
    # Getters
    
    # Simple Interest Getter
    def get_simple_interest(self):
        return self.simple_interest
    
    # Compound Interest Getter
    def get_compound_interest(self):
        return self.compound_interest
    
    # Present and Future Getter
    def get_present_and_future(self):
        return self.present_and_future
    
    def get_annual_investment(self):
        return self.annual_investment
    
# Example   
# interest = interest()
# annual = interest.annual_investment
# print(annual.find_interest_from_f(50.45,2143.60,8))

As you see, we made this part in class mode (Object-Oriented). all the remain codes that you will see, have the same shape.

Deprecations

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def SLMethod(N,present_value,salvage):
    dataframe_dict = {"m":[],"Rm":[],"Dm":[],"BVm":[]}
    Rm = 1/N
    Dm = (present_value-salvage)/N 
    temp_variable = present_value
    for year in range(N+1):
        if(year != 0):
            dataframe_dict["m"].append((year))
            dataframe_dict["Rm"].append(round(Rm,3))
            dataframe_dict["Dm"].append(round(Dm,3))
            temp_variable -= Dm
        else:
            dataframe_dict["m"].append("--")
            dataframe_dict["Rm"].append("--")
            dataframe_dict["Dm"].append("--")
        dataframe_dict["BVm"].append(temp_variable)
    return pd.DataFrame(data=dataframe_dict)

def SoYMethod(N,present_value,salvage , decimals = 2):
    dataframe_dict = {"m":["--"],"Rm":["--"],"Dm":["--"],"BVm":[present_value]}
    SoY = N*(N+1)/2
    BVm = present_value
    for year in range(1,N+1):
        Rm = (N-year+1)/SoY
        Dm = (present_value-salvage)*Rm
        BVm -= Dm
        dataframe_dict["m"].append(year)
        dataframe_dict["Rm"].append(round(Rm,decimals))
        dataframe_dict["Dm"].append(round(Dm,decimals))
        dataframe_dict["BVm"].append(round(BVm,decimals))
    return pd.DataFrame(data=dataframe_dict)



def DBMethod(N,decline_balance, present_value, salvage , decimals = 2):
    dataframe_dict = {"m":["--"],"Rm":["--"],"Dm":["--"],"DmSL":["--"],"DmDB":["--"],"BVm":[present_value]}
    SoY = N*(N+1)/2
    BVm = present_value
    Rm = (decline_balance/100)/N
    switched_to_SM = False
    for year in range(1,N+1):
        if not switched_to_SM:
            to_divide = (N-year+1)
            DmDB = (BVm)*Rm
            DmSL = (BVm-salvage)/to_divide
            if DmDB >= DmSL:
                Dm = DmDB
            else:
                Dm = DmSL
                switched_to_SM = True
        BVm -= Dm
        dataframe_dict["m"].append(year)
        dataframe_dict["Rm"].append(round(Rm,decimals))
        dataframe_dict["DmSL"].append(round(DmSL,decimals))
        dataframe_dict["DmDB"].append(round(DmDB,decimals))
        dataframe_dict["Dm"].append(round(Dm,decimals))

        dataframe_dict["BVm"].append(round(BVm,decimals))
        
    return pd.DataFrame(data=dataframe_dict)
                            

GUI scripts

GUI script is for showing the simplifing the interfaces for user. So we just show the main window scripts. You should know this main script is connected to major files. All the files are downloadable in end of this page.

""" 
@author Amirhossein Kiani
@License MIT-license.
@Copy-right Feburary 2021 - https://AKiani.ir

"""
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QApplication, QTableWidgetItem
from PyQt5.QtGui import QCursor, QIcon, QPixmap
import sys,os
from PyQt5.QtCore import Qt
import matplotlib.pyplot as plt 
import collections

import mainWindow
from interest import *
from TableModel import TableModel
from table import Ui_Dialog as Table
from figureUi import Ui_Dialog as FigureUI
import deprication
from matplotlib.lines import Line2D

tempDict = {1:{"P":100},
            2:{"F":200},
            3:{"A":-500},
            4:{"A":-400},
            5:{"A":-400},
            6:{"A":-400},
            7:{"A":-400},
            8:{"A":-400},
           }
MY_FIGURE_SIZE = (500,500)
my_dpi = 140
class CEMApplication(QtWidgets.QMainWindow, mainWindow.Ui_MainWindow):
    path = os.path.dirname(os.path.abspath(__file__))
    interest = interest()
    def __init__(self, parent=None):
        
        
        super(CEMApplication, self).__init__(parent)
        # Set stylesheet
        self.defaultStyleSheet = open(os.path.join(self.path,"qss/stylesheet.qss")).read()
        self.setStyleSheet(self.defaultStyleSheet)
        self.setupUi(self)
        # Undecorated Window 
        self.setWindowFlags(self.windowFlags() | QtCore.Qt.FramelessWindowHint)

        # Change size of main window as the background size
        self.resize(self.background.width() ,self.background.height())
        # Set window unresizable
        self.setFixedSize(self.size())
        # Initialize icons
        self.initializeIcons()
        # Initialize event handlers
        self.initializeEventHandlers()
        # Initialize styles
        self.result = tempDict

        self.initializeStyles()
        # self.makeFigure(self.equivalent(tempDict,20))
        # self.makeFigure(self.equivalent(tempDict,20))
        # self.showFigure()
        # Handle and initialize table
        self.initializeTable(1)
        # self.deleteColumn("P",1)
        # self.resetTableSlot()
        # self.addToTable("F",1,2000)

    def closeEventHandler(self):
        QtCore.QCoreApplication.quit()   
        
    """ Interests """


    
    """ Simple Interest Slot"""
    def simpleInterestHandler(self):
        simple_interest_instance = self.interest.get_simple_interest()
        
        if(self.checkIsNull(self.presentValueText,self.interestValueText,self.periodsValueText)):
            presentValue = self.presentValueText.toPlainText()
            interest = self.interestValueText.toPlainText()
            periods = self.periodsValueText.toPlainText()
            self.firstOutputLabel.setText(str(simple_interest_instance.total_interest(int(presentValue),int(periods),int(interest))))
    """ Compoun Interest Slot"""  
    def compounInterest(self):
        compound_instance = self.interest.get_compound_interest()
        
        if(self.checkIsNull(self.presentValueText,self.interestValueText,self.periodsValueText)):
            presentValue = self.presentValueText.toPlainText()
            interest = self.interestValueText.toPlainText()
            periods = self.periodsValueText.toPlainText()
            self.firstOutputLabel.setText(str(round(compound_instance.total_interest(int(presentValue),int(periods),int(interest)) , 2)))
    """ Find Future Value Slot """
    def findFutureValueSlot(self):
        present_and_future_instance = self.interest.get_present_and_future()
        annual_instance = self.interest.get_annual_investment()
        """ If Present value is presented """
        if(self.checkIsNull(self.presentValueText,self.interestValueText,self.periodsValueText)):
            present = float(self.presentValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            interest = float(self.interestValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(present_and_future_instance.find_f(present,periods,interest) , 2)))
            return
        self.backToDefault(self.presentValueText , self.interestValueText, self.periodsValueText)
        """ if Annual Value is presented """
        if(self.checkIsNull(self.annualValueText,self.interestValueText,self.periodsValueText)):
            annul = float(self.annualValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            interest = float(self.interestValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(annual_instance.find_f(annul,periods,interest) , 2)))
            return
        self.backToDefault(self.annualValueText , self.interestValueText, self.periodsValueText)
        
    """ Find Present Value Slot """
    def findPresentValueSlot(self):
        present_and_future_instance = self.interest.get_present_and_future()
        annual_instance = self.interest.get_annual_investment()
        """ If Present value is presented """
        if(self.checkIsNull(self.futureValueText,self.interestValueText,self.periodsValueText)):
            future = float(self.futureValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            interest = float(self.interestValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(present_and_future_instance.find_p(future,periods,interest) , 2)))
            return
        self.backToDefault(self.futureValueText , self.interestValueText, self.periodsValueText)
        """ if Annual Value is presented """
        if(self.checkIsNull(self.annualValueText,self.interestValueText,self.periodsValueText)):
            annul = float(self.annualValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            interest = float(self.interestValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(annual_instance.find_p(annul,periods,interest) , 2)))
            return

        
        self.backToDefault(self.annualValueText , self.interestValueText, self.periodsValueText)
    def findInterestSlot(self):
        present_and_future_instance = self.interest.get_present_and_future()
        annual_instance = self.interest.get_annual_investment()

        """ If Annual value is not presented """
        if(self.checkIsNull(self.presentValueText,self.futureValueText,self.periodsValueText)):
            present = float(self.presentValueText.toPlainText())
            future = float(self.futureValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(present_and_future_instance.find_interest(present,future,periods),3)))
            return
        
        self.backToDefault(self.futureValueText , self.presentValueText, self.periodsValueText)

        """ If Present Value is not presented """
        if(self.checkIsNull(self.annualValueText,self.futureValueText,self.periodsValueText)):
            annual = float(self.annualValueText.toPlainText())
            future = float(self.futureValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(annual_instance.find_interest_from_f(annual,future,periods) , 3)))
            return
        self.backToDefault(self.annualValueText , self.presentValueText, self.periodsValueText)
    def findAnnualSlot(self):
        annual_instance = self.interest.get_annual_investment()
        """ If Annual value is not presented """
        if(self.checkIsNull(self.presentValueText,self.interestValueText,self.periodsValueText)):
            present = float(self.presentValueText.toPlainText())
            interest = float(self.interestValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(annual_instance.find_from_p(present,periods,interest),3)))
            return
        
        self.backToDefault(self.interestValueText , self.presentValueText, self.periodsValueText)

        """ If Present Value is not presented """
        if(self.checkIsNull(self.interestValueText,self.futureValueText,self.periodsValueText)):
            interest = float(self.interestValueText.toPlainText())
            future = float(self.futureValueText.toPlainText())
            periods = float(self.periodsValueText.toPlainText())
            self.firstOutputLabel.setText(str(round(annual_instance.find_from_f(future,periods,interest) , 3)))
            return
        self.backToDefault(self.interestValueText , self.futureValueText, self.periodsValueText)




    """ Deprication """
    def SLMethodSlot(self):

        if self.checkIsNull(self.periodsValueTextDeprication,self.presentValueTextDeprication,self.salvageValueTextDeprication):
            periods = int(self.periodsValueTextDeprication.toPlainText())
            present = float(self.presentValueTextDeprication.toPlainText())
            salvage = float(self.salvageValueTextDeprication.toPlainText())
            returnData = deprication.SLMethod(periods,present,salvage)
            self.showTables(returnData)
    def soyMethodSlot(self):
        if self.checkIsNull(self.periodsValueTextDeprication,self.presentValueTextDeprication,self.salvageValueTextDeprication):
            periods = int(self.periodsValueTextDeprication.toPlainText())
            present = float(self.presentValueTextDeprication.toPlainText())
            salvage = float(self.salvageValueTextDeprication.toPlainText())
            returnData = deprication.SoYMethod(periods,present,salvage)
            self.showTables(returnData)
    def dbMethodSlot(self):
        if self.checkIsNull(self.periodsValueTextDeprication,self.presentValueTextDeprication,self.salvageValueTextDeprication):
            periods = int(self.periodsValueTextDeprication.toPlainText())
            present = float(self.presentValueTextDeprication.toPlainText())
            salvage = float(self.salvageValueTextDeprication.toPlainText())
            if(self.checkBox1.checkState() == 0 and self.checkBox2.checkState() == 2):
                returnData = deprication.DBMethod(periods,200,present,salvage)
                self.showTables(returnData)
            elif(self.checkBox1.checkState() == 2 and self.checkBox2.checkState() == 0):
                returnData = deprication.DBMethod(periods,100,present,salvage)
                self.showTables(returnData)

    """ Making Table Dialog """
    def showTables(self , data):
        dialog = QtWidgets.QDialog()
        dialog.setStyleSheet(self.defaultStyleSheet)
        dialog.ui = Table()
        dialog.ui.setupUi(dialog)
        dialog.resize(dialog.ui.viewTable.width() ,dialog.ui.viewTable.height())
        dialog.setFixedSize(dialog.ui.viewTable.size())

        self.addDataToTable(data,dialog.ui.viewTable)
        dialog.exec_()
        dialog.show()
    """ Show Figure """
    def showFigure(self):
        dialog = QtWidgets.QDialog()
        dialog.setStyleSheet(self.defaultStyleSheet)
        dialog.ui = FigureUI()
        dialog.ui.setupUi(dialog)
        dialog.exec_()
        dialog.show()
    """ Delete From Table """
    def deleteItemSlot(self):
        column = self.valuesTable.currentColumn() 
        if column != -1:
            year = self.valuesTable.item(0,column).text()
            value = self.valuesTable.item(1,column).text()
            valueType = self.valuesTable.item(2,column).text()
            if year != "Year":
                self.deleteColumn(valueType,year)
    """ Add to Table """
    def addItemSlot(self):
        valueType = self.typeOfValue.currentText()
        value = self.insertValueTextEdit.toPlainText()
        year = self.yearValue.toPlainText()
        if value != "" and year !="":
            value = int(value)
            year = int(year)
            self.addToTable(valueType,year,value)
    """ Reset Table """
    def resetTableSlot(self):
        self.resetTable()
    """ Evaluate Results """
    def evaluateSlot(self):
        if(self.interestEvaluate.toPlainText() != ""):
            self.makeFigure(self.equivalent(self.result,float(self.interestEvaluate.toPlainText())))
            self.showFigure()

    def initializeEventHandlers(self):
        """ Interest's Signals """
        """ Simple Interest Signal"""
        self.simpleInterest.clicked.connect(self.simpleInterestHandler)
        """ Compound Interest Signal"""
        self.compundInterest.clicked.connect(self.compounInterest)
        """ Find Future Value Signal"""
        self.findFutureValueText.clicked.connect(self.findFutureValueSlot)
        """ Find Present Value Signal """
        self.findPresentValueText.clicked.connect(self.findPresentValueSlot)
        """ Find Interest Signal """
        self.findInterestText.clicked.connect(self.findInterestSlot)
        """ Find Annual Signal """
        self.findAnnualValueText.clicked.connect(self.findAnnualSlot)

        """ Deprication's Signals """
        """ SL Method Signal"""
        self.SLMethod.clicked.connect(self.SLMethodSlot)
        """ SoY Method Signal """
        self.SoYMethod.clicked.connect(self.soyMethodSlot)
        """ DB Method Signal """
        self.DBMethod.clicked.connect(self.dbMethodSlot)




        """ Close and Minimize """
        self.closeButton.clicked.connect(self.closeEventHandler)
        self.minimizeButton.clicked.connect(lambda: self.showMinimized())

        """ Table Slots """
        self.deleteItem.clicked.connect(self.deleteItemSlot)
        self.insertItem.clicked.connect(self.addItemSlot)
        self.reset.clicked.connect(self.resetTableSlot)
        self.evaluate.clicked.connect(self.evaluateSlot)


    def initializeIcons(self):
        # Labels
        # self.navbarBackground.setPixmap(QPixmap(os.path.join(self.path,"images/navbar.png")))
        # self.background.setPixmap(QPixmap(os.path.join(self.path, 'images/background.jpg')))

        # Button
        self.closeButton.setIcon(QIcon(os.path.join(self.path,'images/close.png')))
        self.minimizeButton.setIcon(QIcon(os.path.join(self.path,'images/minimize.png')))
        
    
        

    def addDataToTable(self,dataDict , table):
        tableModelInstance = TableModel(dataDict)
        table.setModel(tableModelInstance)
        tableWidth = float(table.width())
        tableHeight = float(table.height()) 

        columnWidth = tableWidth/(len(list(dataDict.keys())))
        rowHeight = tableHeight/len(list(dataDict.to_dict().values())[0])

        for i in range(len(list(dataDict.keys()))):
            table.setColumnWidth(i , columnWidth)
        for i in range(len(list(dataDict.to_dict().values())[0])):
            table.setRowHeight(i , rowHeight)

    
    """ Plotting """
    def makeFigure(self,dataDict):
        f, ax = plt.subplots(1,figsize=self.pixelToInches(MY_FIGURE_SIZE))
        ax.axhline(ls=":" , color="black")
        ax.axvline(ls=":",c="black")
        colorData = {"P":"r" , "A":"g" , "F":"c"}
        maxValue = 0
        custom_lines = [Line2D([1], [0], color="red", lw=4),
                    Line2D([1], [0], color="cyan", lw=4),
                    Line2D([0], [0], color="green", lw=4)]
        for year , info in dataDict.items():
            for valueType in list(info.keys()):
                value = info[valueType]
                if abs(value) > maxValue:
                    maxValue = abs(value)
                typeColor = colorData[valueType.upper()]
                if(valueType.upper() == "A"):
                    space = 0.5
                elif(valueType.upper() == "P"):
                    space = 1
                else:
                    space = 1.5
                plt.annotate(str(round(value))+"$", xy=(0, 0), xytext=(year,space) , fontsize=5)
                ax.arrow(year,0,0,-1*abs(value/1000),color=typeColor,width=len(list(dataDict.keys()))/100 )
        ax.set_ylim(-1*(maxValue/1000)*1.25,(maxValue*1.25)/1000)    
        ax.legend(custom_lines,['Present value', 'Future Value', 'Annual Value'])
        ax.figure.savefig("images/myFig.png",dpi=my_dpi)
    
    """ Find Equivalents """
    def equivalent(self,dataDict,interestValue):
        maxYear = max(list(dataDict.keys()))
        myDict = {}
        
        for i in range(0, maxYear+1):
            myDict[i] = {}
            
        present_future_getter = self.interest.get_present_and_future()
        zero_value = 0
        
        for year in dataDict:
            innerDict = dataDict[year]
            future_value = list(innerDict.values())[0]
            zero_value += present_future_getter.find_p(abs(future_value),year,interestValue)
            
        myDict[0] = {"P":zero_value}
        whole_future_value = present_future_getter.find_f(abs(zero_value) , maxYear , interestValue)
        
        annual_getter = self.interest.get_annual_investment()
        annual_value = annual_getter.find_from_p(zero_value,maxYear,interestValue)
        myDict[maxYear] = {"F":whole_future_value , "A":annual_value}
        for i in range(1 , maxYear):
            myDict[i] = {"A":annual_value}
        return myDict


    def pixelToInches(self,tupleOfPixels):
        width = tupleOfPixels[0]
        height = tupleOfPixels[1]
        return (width/my_dpi , height/my_dpi)
    def initializeStyles(self):
        # Set Cursor
        self.logoLabel.setCursor(QCursor(Qt.PointingHandCursor))

        # Add Fonts
        QtGui.QFontDatabase.addApplicationFont(os.path.join(self.path,"fonts/Arizonia-Regular.ttf"))
        QtGui.QFontDatabase.addApplicationFont(os.path.join(self.path,"fonts/Anton-Regular.ttf"))
        QtGui.QFontDatabase.addApplicationFont(os.path.join(self.path,"fonts/Roboto-Bold.ttf"))

        # Add Dynamic Style to fields
        self.typeOfValue.setEditable(True)
        self.typeOfValue.lineEdit().setAlignment(QtCore.Qt.AlignCenter)
        self.typeOfValue.lineEdit().setReadOnly(True)
        
    def initializeTable(self,first):
        self.valuesTable.setRowCount(3)
        self.valuesTable.setColumnCount(1)
        for row in range(self.valuesTable.rowCount()):
            self.valuesTable.setRowHeight(row,self.valuesTable.height()/(self.valuesTable.rowCount()+1))
        column_index = self.valuesTable.columnCount()
        self.valuesTable.setItem(0,0 , QTableWidgetItem('Year'))
        self.valuesTable.setItem(1,0 , QTableWidgetItem('Value'))
        self.valuesTable.setItem(2,0 , QTableWidgetItem('Type'))
        # stylesheet = "background:blue"
        # self.valuesTable.horizontalHeader().setStyleSheet(stylesheet)
        if first == 1:
            self.updateTable()
    def cellPressedSlot(self,row,col):
        print(row , col )
        print(self.valuesTable.currentRow())
        
    def updateTable(self):
        keys = list(self.result.keys())
        items = list(self.result.values())
        if len(self.result) != 0:
            maxYear = max(keys)
            column = 1
            self.valuesTable.setColumnCount(1)
            for i in range(maxYear+1):
                if i in keys:
                    print
                    innerDict = self.result[i]
                    for valueType , value in innerDict.items():
                        self.valuesTable.setColumnCount(column+1)
                        self.valuesTable.setItem(0,column,QTableWidgetItem(str(i)))
                        self.valuesTable.setItem(1,column,QTableWidgetItem(str(abs(value))))
                        self.valuesTable.setItem(2,column,QTableWidgetItem(valueType))
                        column += 1
        else:
            self.initializeTable(0)

    def deleteColumn(self,valueType,year):
        year = int(year)
        if year in list(self.result.keys()):
            innerDict = self.result[year]
            if valueType in list(innerDict.keys()):
                del self.result[year][valueType]
        self.updateTable()
    def addToTable(self,valueType,year,value):
        if year in list(self.result.keys()):
            innerDict = self.result[year]
            if valueType in list(innerDict.keys()):
                self.result[year][valueType] += value
            else:
                self.result[year][valueType] = value
        else:
            self.result[year] = {}
            self.result[year][valueType] = value
        self.updateTable()
                
    def resetTable(self):
        self.result = {}
        self.updateTable()


    def checkIsNull(self,*args):
        findNull = True
        self.backToDefault()
        for arg in args:
            if arg.toPlainText() == None or arg.toPlainText() == "":
                findNull = False
                arg.setStyleSheet("background-color:red")
            else:
                arg.setStyleSheet("")
        return findNull 
    """
        Events
    """
    def backToDefault(self,*args):
        for arg in args:
            arg.setStyleSheet("")
    

def main():
    app = QApplication(sys.argv)
    form = CEMApplication()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()
                            

**Attention**

please pay attention, you will need to run bellow code due to install required modules(if you already have these modules, please feel free to go to next step)

pip install PyQt5
                        
pip install matplotlib
                        
pip install pandas
                        
pip install numpy
                        

you should run this commands through cmd (windows) or shell(linux) before running the main file.

After all, the main window will be:

Explanataions

As the picture shows, the interest part can calculate Total, Compound, Future, Present and also interest from compound values of Persent, Future and Annaul with specific periods

The next part is Deprication. You can get a table which shows value of Rm and BV of every year. The shape of this part is like:


The last part, and also the most challangable part, is to calculate The Equivalence Calculations Involving Multiple Interest Formulas
The default values, are the values in Example 4-16 in William G. Suillivan Engineering Economy. As you see, you are able to reset or add more values. After clicking Evaluating, a new window will be appear to show the plot of this equivalence

At Last!

As the last words, we show the way we went through. we two object-oriented script named as deprication.py and interest.py. these two files, handle the functions which used in main window due to change the values into another.
Then, we started to writing the main GUI window script which used PyQt5 module as well.
This main window splitted into three parts and every part does unique job as explained.
We designed two minor windows called Figure and Tables which shows us result of processes which has been done in main window.
Most of the style of window and designing has been done in stylesheet.qss which has a very similiar syntax to css (Cascading Style Sheets) files that made my work simpler.
Finally, you can download the zip file which you can run the main and see the processes in your local machine by clicking the download link. (DO NOT FORGET TO INSTALL THE REQUIRED MODULES AS SAID).