在Python中使用Tkinter的费用追踪器应用程序
在下面的教程中,我们将在Tkinter、Tkcalendar和SQLite库的帮助下,开发一个基于图形用户界面(GUI)的费用追踪器应用程序。这是一个中级项目,我们将学到很多关于数据库和GUI的知识以及它们在实际生活中的实现。
所以,让我们开始吧。
什么是费用追踪器
在印度,有很多人靠固定收入生活,他们发现在月底的时候,他们没有足够的钱来满足他们的需求。虽然这个问题可能是因为工资低而产生的,但这是由于理财能力差造成的。支出追踪器 ,也被称为支出管理器和金钱管理器,是一个桌面应用程序或软件,允许用户准确记录他们的资金流入和流出。
人们往往在不了解的情况下过度消费,这可能被证明是可怕的。我们可以在每日支出追踪器的帮助下,跟踪我们的日常支出。到了月底,我们会有一个清晰的画面,显示资金的流动情况。这是管理开支和为财务带来一些秩序的最好方法之一。
关于该项目
本项目旨在建立一个基于GUI的费用追踪器。为了建立这个项目,我们需要对Tkinter库、SQL语言及其命令有中等程度的了解,以及对Tkinter库和tkcalendar库的不同模块有基本了解。
项目的先决条件
在用Python创建GUI助手应用程序时,我们将需要一些库和模块。这些模块简要介绍如下。
1.Tkinter。tkinter模块将帮助我们为应用程序提供图形用户界面(GUI)。
2.Tkcalendar。tkcalendar模块将帮助我们进行下拉式日历的工作。
3.SQLite。sqlite模块将允许我们将Python脚本连接到SQL数据库。
4.日期时间。日期时间模块将允许对日期和时间进行处理。
由于Tkinter、Datetime和SQLite都是Python中预装的模块,所以不需要手动安装它们。然而,我们只需要安装Tkcalendar模块。
tkcalendar模块可以使用PIP安装程序进行安装,在命令提示符或终端输入以下命令。
语法:
# installing the PyGame library
$ pip install tkcalendar
安装完成后,我们可以通过创建一个新的python程序文件并导入tkcalendar模块来验证tkcalendar库是否被正确安装。
以下是说明这一问题的代码片段。
文件:verify.py
import tkcalendar
现在,让我们保存该文件并在命令提示符或终端运行以下命令。
语法:
$ python verify.py
如果程序没有返回任何导入错误,该库就已经成功安装。如果出现任何异常,请尝试重新安装该库并考虑检查其官方文档。
现在让我们开始建设这个项目。
在Python中使用Tkinter构建费用追踪器
为了在Python中建立Expense Tracker,我们将创建一个空的文件夹并命名为 “Expense Tracker”。在这个文件夹中,我们将创建一个名为 “main.py “的Python程序文件,在这里我们将编写项目的全部代码。
为了更好地理解,我们将在Python中创建Expense Tracker的完整项目代码分为几个步骤。这些步骤如下所示。
第1步: 导入必要的模块。
第2步: 创建数据库并定义函数来操作数据。
第3步: 连接到数据库并创建应用程序的主窗口。
第4步: 在窗口中添加必要的小部件,并设置事件触发器。
让我们以更详细的方式来理解上述步骤。
导入必要的模块
首先,我们将开始导入所有需要的模块,其中包括tkinter模块的所有部件和模块,tkcalendar库的DateEntry类,以及datetime和sqlite3模块。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# importing the required modules
from tkinter import * # importing all the modules and classes from tkinter
from tkinter import ttk as ttk # importing the ttk module from tkinter
from tkinter import messagebox as mb # importing the messagebox module from tkinter
import datetime # importing the datetime module
import sqlite3 # importing the sqlite3 module
from tkcalendar import DateEntry # importing the DateEntry class from the tkcalendar module
解释:
在上面的代码片段中,我们从tkinter模块中导入了所有的类和模块来创建GUI窗口并为应用程序添加部件。我们还从tkinter模块中导入了ttk和messagebox模块,以添加ttk部件并显示任何重要信息。然后我们导入了datetime模块来获取当前的日期和时间。我们还导入了sqlite3模块来维护输入的条目的数据库。最后,我们导入了tkcalendar模块的DateEntry类来插入下拉日历中的日期。
创建数据库和定义必要的功能
现在我们已经将所有必要的模块导入到项目中,是时候创建数据库并定义各种函数来实现应用程序中的不同操作。这些函数包括从数据库中检索数据并将其列在表中,从数据表中查看一条记录,重置输入字段,从数据库中删除一条选定的记录,从数据库中删除所有记录,向数据库中添加一条新记录,更新数据库中预先存在的记录的细节,并以文本格式显示记录的细节。
现在让我们详细了解这些功能的实现。
将所有支出从数据库列到表中
我们将首先定义列出所有费用的函数。这个函数将从数据库表中获取记录,并将这些值反复插入我们将使用Tkinter创建的数据表中。
现在让我们考虑以下代码片段来说明这一点。
文件:main.py
# --------------------- defining functions ---------------------
# function to list all the expenses
def listAllExpenses():
'''This function will retrieve the data from the database and insert it to the tkinter data table'''
# using some global variables
global dbconnector, data_table
# clearing the table
data_table.delete(*data_table.get_children())
# executing the SQL SELECT command to retrieve the data from the database table
all_data = dbconnector.execute('SELECT * FROM ExpenseTracker')
# listing the data from the table
data = all_data.fetchall()
# inserting the values iteratively in the tkinter data table
for val in data:
data_table.insert('', END, values = val)
解释:
在上面的代码片段中,我们已经定义了listAllExpenses()这个函数。在这个函数中,我们使用了一些全局变量如dbconnector和data_table。然后我们使用了delete()方法来删除Tkinter表中已有的数据。然后我们执行了SQL SELECT语句,从数据库表中获取数据,并将它们存储在一个变量中。然后我们使用fetchall()方法来列出表中的数据。然后,我们使用for-loop来迭代这些值,并使用insert()方法将它们插入到表中。
查看支出信息
我们现在将定义一个函数来显示所选费用的细节。这个函数将从所选记录的不同列中收集数据,并在数据框中返回这些值。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# function to view an expense information
def viewExpenseInfo():
'''This function will display the expense information in the data frame'''
# using some global variables
global data_table
global dateField, payee, description, amount, modeOfPayment
# return a message box displaying error if no row is selected from the table
if not data_table.selection():
mb.showerror('No expense selected', 'Please select an expense from the table to view its details')
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
val = currentSelectedExpense['values']
# retrieving the date of expenditure from the list
expenditureDate = datetime.date(int(val[1][:4]), int(val[1][5:7]), int(val[1][8:]))
# setting the listed data in their respective entry fields
dateField.set_date(expenditureDate) ; payee.set(val[2]) ; description.set(val[3]) ; amount.set(val[4]) ; modeOfPayment.set(val[5])
解释:
在上面的代码片段中,我们将函数定义为viewExpenseInfo()。我们在这个函数中使用了一些全局变量,并使用tkinter’s messagebox模块的 showerror() 方法返回了一个显示错误的消息框,如果没有从表格中选择任何行。然后我们使用 item() 方法从选定的行中收集字典格式的数据。然后我们定义了另一个变量,将收集到的数据的值存储在一个列表中。最后,我们从列表中获取了日期,并将列表中的数据设置在数据输入框中各自的输入字段。
重设数据输入框中的条目。
现在我们将定义一个函数来清除用户在数据框的输入字段中的条目。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to clear the entries from the entry fields
def clearFields():
'''This function will clear all the entries from the entry fields'''
# using some global variables
global description, payee, amount, modeOfPayment, dateField, data_table
# defining a variable to store today's date
todayDate = datetime.datetime.now().date()
# setting the values in entry fields back to initial
description.set('') ; payee.set('') ; amount.set(0.0) ; modeOfPayment.set('Cash'), dateField.set_date(todayDate)
# removing the specified item from the selection
data_table.selection_remove(*data_table.selection())
解释:
在上面的代码片段中,我们定义了clearFields()这个函数。在这个函数中,我们使用了一些全局变量。然后我们定义了一个变量来存储当前日期。最后,我们将输入字段的值设置为初始值,并将指定的项目从选择中删除。
从表中删除所选记录。
现在我们将定义一个函数,从表中以及数据库中删除选定的记录。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to delete the selected record
def removeExpense():
'''This function will remove the selected record from the table'''
# returning the message box displaying error if no row is selected
if not data_table.selection():
mb.showerror('No record selected!', 'Please select a record to delete!')
return
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
valuesSelected = currentSelectedExpense['values']
# displaying a message box asking for confirmation
confirmation = mb.askyesno('Are you sure?', f'Are you sure that you want to delete the record of {valuesSelected[2]}')
# if the user say YES, executing the SQL DELETE FROM command
if confirmation:
dbconnector.execute('DELETE FROM ExpenseTracker WHERE ID=%d' % valuesSelected[0])
dbconnector.commit()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying the information
mb.showinfo('Record deleted successfully!', 'The record you wanted to delete has been deleted successfully')
解释:
在上面的代码片段中,我们将函数定义为removeExpense()。在这个函数中,我们检查了是否有任何行被选中,并使用tkinter’s messagebox模块的displayror()方法返回一个显示错误的消息框。然后我们以字典的形式收集了所选行的数据,并定义了另一个变量来存储所收集数据的值。然后我们使用了tkinter’s messagebox模块的askyesnow()方法来显示要求确认的消息框。然后我们执行了SQL DELETE语句来删除所选的记录。然后我们调用listAllExpenses()函数并返回显示成功信息的消息框。
从表中删除所有条目。
现在我们将定义一个函数,从表中以及数据库中删除所有条目。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to delete all the entries
def removeAllExpenses():
'''This function will remove all the entries from the table'''
# displaying a message box asking for confirmation
confirmation = mb.askyesno('Are you sure?', 'Are you sure that you want to delete all the expense items from the database?', icon='warning')
# if the user say YES, deleting the entries from the table and executing the SQL DELETE FROM command to delete all the entries
if confirmation:
data_table.delete(*data_table.get_children())
dbconnector.execute('DELETE FROM ExpenseTracker')
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying the information
mb.showinfo('All Expenses deleted', 'All the expenses were successfully deleted')
else:
# returning the message box, if the operation is aborted
mb.showinfo('Ok then', 'The task was aborted and no expense was deleted!')
解释:
在上面的代码片断中,我们将函数定义为removeAllExpenses()。在这个函数中,我们显示了一个消息框,要求用户在askyesno()方法的帮助下进行确认。然后,我们使用delete()方法从表中删除所有的记录。我们还执行了SQL DELETE语句来删除数据库表中的所有条目。我们还调用了clearFields()和listAllExpenses()函数,使用messagebox ‘s showinfo()方法返回显示信息的消息框。
在表中增加一条新的记录。
现在我们将定义一个函数,向表以及数据库添加一条新的记录。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to add an expense
def addAnotherExpense():
'''This function will add an expense to the table and database'''
# using some global variables
global dateField, payee, description, amount, modeOfPayment
global dbconnector
# if any of the field is empty, return the message box displaying error
if not dateField.get() or not payee.get() or not description.get() or not amount.get() or not modeOfPayment.get():
mb.showerror('Fields empty!', "Please fill all the missing fields before pressing the add button!")
else:
# executing the SQL INSERT INTO command
dbconnector.execute(
'INSERT INTO ExpenseTracker (Date, Payee, Description, Amount, ModeOfPayment) VALUES (?, ?, ?, ?, ?)',
(dateField.get_date(), payee.get(), description.get(), amount.get(), modeOfPayment.get())
)
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying information
mb.showinfo('Expense added', 'The expense whose details you just entered has been added to the database')
解释:
在上面的代码片断中,我们将函数定义为addAnotherExpense()。在这个函数中,我们使用了一些全局变量。然后,我们使用messagebox模块的showerror()方法返回了一个消息框,如果其中任何一个字段为空,就会引发错误。然后我们执行了SQL INSERT语句,将记录添加到数据库表中。我们调用了clearFields()和listAllExpenses()函数,并使用messagebox的showinfo()方法返回显示SUCCESS信息的消息框。
编辑所选开支的细节。
我们现在将定义一个函数来编辑所选费用的细节,并在表和数据库中更新这些细节。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to edit the details of an expense
def editExpense():
'''This function will allow user to edit the details of the selected expense'''
# using some global variables
global data_table
# defining a nested to update the details of the selected expense
def editExistingExpense():
'''This function will update the details of the selected expense in the database and table'''
# using some global variables
global dateField, amount, description, payee, modeOfPayment
global dbconnector, data_table
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
content = currentSelectedExpense['values']
# executing the SQL UPDATE command to update the record in database table
dbconnector.execute(
'UPDATE ExpenseTracker SET Date = ?, Payee = ?, Description = ?, Amount = ?, ModeOfPayment = ? WHERE ID = ?',
(dateField.get_date(), payee.get(), description.get(), amount.get(), modeOfPayment.get(), content[0])
)
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning a message box displaying the message
mb.showinfo('Data edited', 'We have updated the data and stored in the database as you wanted')
# destroying the edit button
editSelectedButton.destroy()
# returning a message box displaying error if no record is selected
if not data_table.selection():
mb.showerror('No expense selected!', 'You have not selected any expense in the table for us to edit; please do that!')
return
# calling the viewExpenseInfo() method
viewExpenseInfo()
# adding the Edit button to edit the selected record
editSelectedButton = Button(
frameL3,
text = "Edit Expense",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = editExistingExpense
)
# using the grid() method to set the position of the above button on the main window screen
editSelectedButton.grid(row = 0, column = 0, sticky = W, padx = 50, pady = 10)
解释:
在上面的代码片段中,我们将函数定义为editExpense()。在这个函数中,我们使用了一些全局变量。然后我们定义了一个嵌套函数 editExistingExpense() 。在这个嵌套函数中,我们再次使用了一些全局变量,并使用 item() 方法将数据以所选记录的字典格式存储。然后,我们从收集的数据中分离出数值,将其存储在另一个变量中,并使用SQL UPDATE语句更新数据库表中所选记录的细节。然后我们调用clearFields()和listAllExpenses()函数,并使用messagebox ‘s showinfo()方法返回显示SUCCESS消息的消息框。在嵌套函数之外,我们使用了messagebox的showror()方法来返回消息框,如果没有选择任何行,就会引发错误。然后我们调用了viewExpenseInfo()函数,并使用Button()部件创建了一个按钮,以调用editExistingExpense()函数来编辑数据库中记录的更新值。最后,我们使用grid()方法设置了按钮在主窗口屏幕上的位置。
用文字显示所选记录的细节。
现在我们将定义一个函数来返回显示记录细节的文字信息框。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# function to display the details of selected expense into words
def selectedExpenseToWords():
'''This function will display the details of the selected expense from the table into words'''
# using some global variables
global data_table
# returning a message box displaying error, if no record is selected from the table
if not data_table.selection():
mb.showerror('No expense selected!', 'Please select an expense from the table for us to read')
return
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
val = currentSelectedExpense['values']
# defining the message to be displayed in the message box
msg = f'Your expense can be read like: \n"You paid {val[4]} to {val[2]} for {val[3]} on {val[1]} via {val[5]}"'
# returning the message box displaying the message
mb.showinfo('Here\'s how to read your expense', msg)
解释:
在上面的代码片断中,我们将函数定义为 selectedExpenseToWords() 。在这个函数中,我们使用了一些全局变量,检查该行是否被选中,并返回消息框,使用消息框的 showerror() 方法引发一个错误。然后我们使用item()方法从被选中的行中收集字典格式的数据,并定义另一个变量来存储字典中的值。然后,我们定义了要显示的信息,并借助于messagebox模块的showinfo()方法将其返回到消息框中。
在将其添加到表格之前用文字显示支出细节。
现在我们将定义最后一个函数,在插入表和数据库之前用文字显示支出的细节。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# function to display the expense details into words before adding it to the table
def expenseToWordsBeforeAdding():
'''This function will display the details of the expense into words before adding it to the table'''
# using some global variables
global dateField, description, amount, payee, modeOfPayment
# if any of the field is empty, return the message box displaying error
if not dateField.get() or not payee.get() or not description.get() or not amount.get() or not modeOfPayment.get():
mb.showerror('Incomplete data', 'The data is incomplete, meaning fill all the fields first!')
else:
# defining the message to be displayed in the message box
msg = f'Your expense can be read like: \n"You paid {amount.get()} to {payee.get()} for {description.get()} on {dateField.get_date()} via {modeOfPayment.get()}"'
# displaying a message box asking for confirmation
addQuestion = mb.askyesno('Read your record like: ', f'{msg}\n\nShould I add it to the database?')
# if the user say YES, calling the addAnotherExpense() function
if addQuestion:
addAnotherExpense()
else:
# returning a message box displaying information
mb.showinfo('Ok', 'Please take your time to add this record')
解释:
在上面的代码片断中,我们将函数定义为expenseToWordsBeforeAdding()。在这个函数中,我们使用了一些全局变量。然后,我们返回了一个消息框,如果有任何字段是空的,就会引发一个错误。然后,我们定义了要在消息框中显示的信息。然后这个消息被打印在消息框中,要求使用messagebox模块的askyesno()方法进行确认,以在数据库表中插入详细信息。然后我们调用addAnotherExpense()函数,使用messagebox ‘s showinfo()方法返回一个显示信息的消息框。
连接到数据库并创建应用程序的主窗口
现在我们已经定义了所有必要的功能并创建了数据库,现在是时候让我们把应用程序连接到数据库并创建应用程序的主窗口了。
首先,我们将使用sqlite3模块的不同方法将应用程序连接到数据库。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# main function
if __name__ == "__main__":
# connecting to the Database
dbconnector = sqlite3.connect("Expense_Tracker.db")
dbcursor = dbconnector.cursor()
# specifying the function to execute whenever the application runs
dbconnector.execute(
'CREATE TABLE IF NOT EXISTS ExpenseTracker (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Date DATETIME, Payee TEXT, Description TEXT, Amount FLOAT, ModeOfPayment TEXT)'
)
# committing the above command
dbconnector.commit()
解释:
在上面的代码片段中,我们已经将Python脚本连接到SQLite数据库来存储所有的信息。为了连接到数据库,我们使用 sqlite3.connect(<databaseName>)
方法创建了一个实例作为 dbconnector<databaseName>
。现在数据库已经创建并激活,我们使用execute()和commit()方法来执行数据库中的功能。
因此,我们已经成功地建立了与数据库的连接。现在我们将使用tkinter模块的Tk()类创建一个应用程序的主窗口。我们还将为应用程序设置一个标题以及它在屏幕上的大小和位置。我们还将配置一个背景颜色和一个图标。
文件:main.py
# creating the main window of the application
# creating an instance of the Tk() class
main_win = Tk()
# setting the title of the application
main_win.title("EXPENSE TRACKER - JAVATPOINT")
# setting the size and position of the window
main_win.geometry("1415x650+400+100")
# disabling the resizable option for better UI
main_win.resizable(0, 0)
# configuring the background color to #FFFAF0
main_win.config(bg = "#FFFAF0")
# setting the icon of the application
main_win.iconbitmap("./piggyBank.ico")
解释:
在上面的代码片段中,我们通过实例化Tk()类的main_win来创建主窗口。然后我们使用title()方法来设置窗口的标题。我们还使用geometry()方法设置了窗口的大小和位置,并通过将resizable()方法的参数值设置为0,禁用了可调整大小的选项以获得更好的用户界面。我们用config()方法配置了窗口的背景颜色。最后,我们使用了iconbitmap()方法来设置窗口的图标。
向窗口添加必要的小部件并设置事件触发器
由于应用程序的主窗口已经成功创建,我们将向窗口添加所有必要的部件,并设置事件触发器。这些widget包括框架,用于构造其他widget,标签,用于显示重要信息,输入字段,用于插入数据,以及按钮,用于操作输入的数据和调用函数。
现在让我们详细了解一下这些小工具的添加。
添加框架
我们将首先在主窗口中添加框架。这些框架将有助于结构化其他部件。这些框架可以用tkinter模块的Frame()部件来创建。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# adding frames to the window to provide structure to the other widgets
frameLeft = Frame(main_win, bg = "#FFF8DC")
frameRight = Frame(main_win, bg = "#DEB887")
frameL1 = Frame(frameLeft, bg = "#FFF8DC")
frameL2 = Frame(frameLeft, bg = "#FFF8DC")
frameL3 = Frame(frameLeft, bg = "#FFF8DC")
frameR1 = Frame(frameRight, bg = "#DEB887")
frameR2 = Frame(frameRight, bg = "#DEB887")
# using the pack() method to set the position of the above frames
frameLeft.pack(side=LEFT, fill = "both")
frameRight.pack(side = RIGHT, fill = "both", expand = True)
frameL1.pack(fill = "both")
frameL2.pack(fill = "both")
frameL3.pack(fill = "both")
frameR1.pack(fill = "both")
frameR2.pack(fill = "both", expand = True)
解释:
在上面的代码片段中,我们使用了Frame()部件来添加框架到窗口。我们还将前两个框架的主参数设置为main_win,表示窗口的左边和右边。然后我们把后面三个框架的主参数设置为frameLeft,表示左边框架的小片段。然后我们把剩下的两个帧的主参数设置为frameRight,表示右帧的小帧段。我们将背景颜色设置为bg参数的值,从而配置了背景颜色。最后,我们使用pack()方法来设置这些帧的位置。
现在我们已经将框架添加到了应用程序中,现在是时候将其余的小部件,如标签、输入字段、选项菜单、按钮和表格添加到各自的框架中。
将小部件添加到frameL1框架中。
我们将首先为第一个框架,即frameL1添加一些标签。这些标签将包括应用程序的标题和一个表示数据输入部分的副标题。我们将使用Label() widget来创建所需的标签。
让我们考虑一下下面的代码片断,以证明这一点。
文件:main.py
# ---------------- Adding widgets to the frameL1 frame ----------------
# adding the label to display the heading
headingLabel = Label(
frameL1,
text = "EXPENSE TRACKER",
font = ("Bahnschrift Condensed", "25"),
width = 20,
bg = "#8B4513",
fg = "#FFFAF0"
)
# adding the label to display the subheading
subheadingLabel = Label(
frameL1,
text = "Data Entry Frame",
font = ("Bahnschrift Condensed", "15"),
width = 20,
bg = "#F5DEB3",
fg = "#000000"
)
# using the pack() method to set the position of the above labels
headingLabel.pack(fill = "both")
subheadingLabel.pack(fill = "both")
解释:
在上面的代码片段中,我们使用Label()部件来创建所需的标签。然后我们将这些标签的主参数设置为frameL1框架。我们还指定了要显示的文本、字体风格、背景和前景颜色。最后,我们使用了pack()方法来设置上述标签的位置。
将小部件添加到frameL2框架中。
现在我们将在第二个框架,即frameL2中添加一些小部件。这些部件包括显示一些信息的标签,输入字段,以及一个输入标签数据的选项菜单。我们将使用Label()部件来创建标签,使用Entry()部件来创建输入字段,使用OptionMenu()部件来创建一个下拉菜单。我们还将使用DateEntry()小组件来创建一个下拉日历,让用户输入日期。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# ---------------- Adding widgets to the frameL2 frame ----------------
# creating some labels to ask user to enter the required data
# date label
dateLabel = Label(
frameL2,
text = "Date:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# description label
descriptionLabel = Label(
frameL2,
text = "Description:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# amount label
amountLabel = Label(
frameL2,
text = "Amount:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# payee label
payeeLabel = Label(
frameL2,
text = "Payee:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# mode of payment label
modeLabel = Label(
frameL2,
text = "Mode of \nPayment:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# using the grid() method to set the position of the above labels in the grid format
dateLabel.grid(row = 0, column = 0, sticky = W, padx = 10, pady = 10)
descriptionLabel.grid(row = 1, column = 0, sticky = W, padx = 10, pady = 10)
amountLabel.grid(row = 2, column = 0, sticky = W, padx = 10, pady = 10)
payeeLabel.grid(row = 3, column = 0, sticky = W, padx = 10, pady = 10)
modeLabel.grid(row = 4, column = 0, sticky = W, padx = 10, pady = 10)
# instantiating the StringVar() class to retrieve the data in the string format from the user
description = StringVar()
payee = StringVar()
modeOfPayment = StringVar(value = "Cash")
# instantiating the DoubleVar() class to retrieve the amount detail in double datatype
amount = DoubleVar()
# creating a drop-down calendar for the user to enter the date
dateField = DateEntry(
frameL2,
date = datetime.datetime.now().date(),
font = ("consolas", "11"),
relief = GROOVE
)
# creating entry fields to enter the labelled data
# field to enter description
descriptionField = Entry(
frameL2,
text = description,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# field to enter the amount
amountField = Entry(
frameL2,
text = amount,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# field to enter payee information
payeeField = Entry(
frameL2,
text = payee,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# creating a drop-down menu to enter the mode of payment
modeField = OptionMenu(
frameL2,
modeOfPayment,
*['Cash', 'Cheque', 'Credit Card', 'Debit Card', 'UPI', 'Paytm', 'Google Pay', 'PhonePe', 'Razorpay']
)
# using the config() method to configure the width, font style, and background color of the option menu
modeField.config(
width = 15,
font = ("consolas", "10"),
relief = GROOVE,
bg = "#FFFFFF"
)
# using the grid() method to set the position of the above widgets in the grid format
dateField.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)
descriptionField.grid(row = 1, column = 1, sticky = W, padx = 10, pady = 10)
amountField.grid(row = 2, column = 1, sticky = W, padx = 10, pady = 10)
payeeField.grid(row = 3, column = 1, sticky = W, padx = 10, pady = 10)
modeField.grid(row = 4, column = 1, sticky = W, padx = 10, pady = 10)
解释:
在上面的代码片段中,我们使用了Label()部件来添加一些标签,要求用户在应用程序中输入日期、费用描述、金额、收款人姓名和支付方式。我们将这些标签的主参数设置为frameL2,即我们之前定义的框架之一。然后我们使用grid()方法来设置这些标签在网格格式中的位置。然后我们实例化了StringVar()类来检索字符串格式的数据。我们还实例化了DoubleVar()类来检索双倍数据类型的金额。然后我们使用DateEntry()类来添加一个下拉日历,用户可以从中选择日期。我们使用Entry()部件添加了一些输入字段,供用户输入数据,如支出的描述、金额和收款人的名字,并将其主参数设置为frameL2框架。我们还包括一个下拉菜单,使用OptionMenu()部件选择支付方式,并将其主参数设置为frameL2框架。最后,我们又使用了grid()方法,以便在主窗口屏幕上以网格格式设置上述部件的位置。
将小部件添加到frameL3框架中。
现在我们将在第三个框架,即frameL3中添加一些按钮。这些按钮将允许用户将费用添加到表格中,在添加前将费用转换为文本,以及重置条目。我们将使用Button()部件来创建这些按钮。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# ---------------- Adding widgets to the frameL3 frame ----------------
# creating buttons to manipulate data
# insert button
insertButton = Button(
frameL3,
text = "Add Expense",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = addAnotherExpense
)
# convert button
convertButton = Button(
frameL3,
text = "Convert to Text before Adding",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = expenseToWordsBeforeAdding
)
# reset button
resetButton = Button(
frameL3,
text = "Reset the fields",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#FF0000",
fg = "#FFFFFF",
relief = GROOVE,
activebackground = "#8B0000",
activeforeground = "#FFB4B4",
command = clearFields
)
# using the grid() method to set the position of the above buttons
insertButton.grid(row = 0, column = 0, sticky = W, padx = 50, pady = 10)
convertButton.grid(row = 1, column = 0, sticky = W, padx = 50, pady = 10)
resetButton.grid(row = 2, column = 0, sticky = W, padx = 50, pady = 10)
解释:
在上面的代码片段中,我们使用了Button()部件来添加按钮到应用程序。我们将这些按钮的主参数设置为frameL3,这是我们之前创建的一个框架。我们还将它们的命令参数指定为我们之前定义的不同的函数,以操作数据。最后,我们使用grid()方法来设置这些按钮在网格格式中的位置。
将小部件添加到frameR1框架中。
现在我们将在第四个框架,即frameR1中增加一些按钮。这些按钮将允许用户查看所选费用的细节,编辑所选费用的细节,用文字显示费用细节,从表中删除所选记录,以及从表中删除所有记录。我们将再次使用Button()部件来创建这些按钮。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# ---------------- Adding widgets to the frameR1 frame ----------------
# creating buttons to manipulate data
# view button
viewButton = Button(
frameR1,
text = "View Selected Expense\'s Details",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = viewExpenseInfo
)
# edit button
editButton = Button(
frameR1,
text = "Edit Selected Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = editExpense
)
# convert button
convertSelectedButton = Button(
frameR1,
text = "Convert Selected Expense to a Sentence",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = selectedExpenseToWords
)
# delete button
deleteButton = Button(
frameR1,
text = "Delete Selected Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = removeExpense
)
# delete all button
deleteAllButton = Button(
frameR1,
text = "Delete All Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = removeAllExpenses
)
# using the grid() method to set the position of the above buttons
viewButton.grid(row = 0, column = 0, sticky = W, padx = 10, pady = 10)
editButton.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)
convertSelectedButton.grid(row = 0, column = 2, sticky = W, padx = 10, pady = 10)
deleteButton.grid(row = 1, column = 0, sticky = W, padx = 10, pady = 10)
deleteAllButton.grid(row = 1, column = 1, sticky = W, padx = 10, pady = 10)
解释:
在上面的代码片断中,我们使用了Button()部件来添加按钮到应用程序。我们将这些按钮的主参数设置为frameR1,这是我们之前创建的一个框架。我们还将它们的命令参数指定为我们之前定义的不同的函数,以操作数据。最后,我们使用grid()方法来设置这些按钮在网格格式中的位置。
将小部件添加到frameR2框架中。
现在我们将在第五个框架,即frameR2中添加一个表。这个表将显示数据库中的所有记录。我们将使用ttk模块的Treeview()部件来为数据创建一个表格结构。我们还将在Scrollbar()部件的帮助下在这个结构中加入水平和垂直滚动条,这样用户就可以自由滚动来访问数据。此外,我们还将添加不同的标题和列,使其看起来像一个表格。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# ---------------- Adding widgets to the frameR2 frame ----------------
# creating a table to display all the entries
data_table = ttk.Treeview(
frameR2,
selectmode = BROWSE,
columns = ('ID', 'Date', 'Payee', 'Description', 'Amount', 'Mode of Payment')
)
# creating a horizontal scrollbar to the table
Xaxis_Scrollbar = Scrollbar(
data_table,
orient = HORIZONTAL,
command = data_table.xview
)
# creating a vertical scrollbar to the table
Yaxis_Scrollbar = Scrollbar(
data_table,
orient = VERTICAL,
command = data_table.yview
)
# using the pack() method to set the position of the scrollbars
Xaxis_Scrollbar.pack(side = BOTTOM, fill = X)
Yaxis_Scrollbar.pack(side = RIGHT, fill = Y)
# configuring the horizontal and vertical scrollbars on the table
data_table.config(yscrollcommand = Yaxis_Scrollbar.set, xscrollcommand = Xaxis_Scrollbar.set)
# adding different headings to the table
data_table.heading('ID', text = 'S No.', anchor = CENTER)
data_table.heading('Date', text = 'Date', anchor = CENTER)
data_table.heading('Payee', text = 'Payee', anchor = CENTER)
data_table.heading('Description', text = 'Description', anchor = CENTER)
data_table.heading('Amount', text = 'Amount', anchor = CENTER)
data_table.heading('Mode of Payment', text = 'Mode of Payment', anchor = CENTER)
# adding different columns to the table
data_table.column('#0', width = 0, stretch = NO)
data_table.column('#1', width = 50, stretch = NO)
data_table.column('#2', width = 95, stretch = NO)
data_table.column('#3', width = 150, stretch = NO)
data_table.column('#4', width = 450, stretch = NO)
data_table.column('#5', width = 135, stretch = NO)
data_table.column('#6', width = 140, stretch = NO)
# using the place() method to set the position of the table on the main window screen
data_table.place(relx = 0, y = 0, relheight = 1, relwidth = 1)
解释:
在上面的代码片段中,我们使用ttk模块的Treeview()小组件创建了一个表格结构,并将这个小组件的主参数设置为frameR2,即我们之前定义的框架之一。我们还为这个小组件指定了选择模式和列的名称。后来,我们使用Scrollbar() widget为表格创建了两个滚动条,将这些widget的主参数设置为data_table,即我们先前创建的表格结构。我们还将这些滚动条的方向分别设置为HORIZONTAL和VERTICAL。然后我们使用pack()方法来设置这些滚动条在表中的位置。我们还使用了config()方法来配置表格上的这些滚动条。之后,我们在表格中添加了不同的标题和列。最后,我们使用了place()方法来设置表格在主窗口屏幕上的位置。
运行应用程序
我们将使用mainloop()方法和Tk()类的对象来运行该应用程序。
让我们考虑以下代码片段来说明这一点。
文件:main.py
# using mainloop() method to run the application
main_win.mainloop()
解释:
在上面的代码片段中,我们用main_win,即Tk()类的对象,使用mainloop()方法来运行应用程序。
因此,该项目代码现在已经完成。我们将保存这个Python程序文件,并在命令提示符或终端运行以下命令以查看输出。
语法:
$ python main.py
但在我们看到输出之前,”Expense Tracker Application using Tkinter “的完整项目代码显示如下。
完整的项目代码
以下是 “Expense Tracker Application using Tkinter in Python “的项目代码。
文件:main.py
# importing the required modules
from tkinter import * # importing all the modules and classes from tkinter
from tkinter import ttk as ttk # importing the ttk module from tkinter
from tkinter import messagebox as mb # importing the messagebox module from tkinter
import datetime # importing the datetime module
import sqlite3 # importing the sqlite3 module
from tkcalendar import DateEntry # importing the DateEntry class from the tkcalendar module
# --------------------- defining functions ---------------------
# function to list all the expenses
def listAllExpenses():
'''This function will retrieve the data from the database and insert it to the tkinter data table'''
# using some global variables
global dbconnector, data_table
# clearing the table
data_table.delete(*data_table.get_children())
# executing the SQL SELECT command to retrieve the data from the database table
all_data = dbconnector.execute('SELECT * FROM ExpenseTracker')
# listing the data from the table
data = all_data.fetchall()
# inserting the values iteratively in the tkinter data table
for val in data:
data_table.insert('', END, values = val)
# function to view an expense information
def viewExpenseInfo():
'''This function will display the expense information in the data frame'''
# using some global variables
global data_table
global dateField, payee, description, amount, modeOfPayment
# return a message box displaying error if no row is selected from the table
if not data_table.selection():
mb.showerror('No expense selected', 'Please select an expense from the table to view its details')
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
val = currentSelectedExpense['values']
# retrieving the date of expenditure from the list
expenditureDate = datetime.date(int(val[1][:4]), int(val[1][5:7]), int(val[1][8:]))
# setting the listed data in their respective entry fields
dateField.set_date(expenditureDate) ; payee.set(val[2]) ; description.set(val[3]) ; amount.set(val[4]) ; modeOfPayment.set(val[5])
# function to clear the entries from the entry fields
def clearFields():
'''This function will clear all the entries from the entry fields'''
# using some global variables
global description, payee, amount, modeOfPayment, dateField, data_table
# defining a variable to store today's date
todayDate = datetime.datetime.now().date()
# setting the values in entry fields back to initial
description.set('') ; payee.set('') ; amount.set(0.0) ; modeOfPayment.set('Cash'), dateField.set_date(todayDate)
# removing the specified item from the selection
data_table.selection_remove(*data_table.selection())
# function to delete the selected record
def removeExpense():
'''This function will remove the selected record from the table'''
# returning the message box displaying error if no row is selected
if not data_table.selection():
mb.showerror('No record selected!', 'Please select a record to delete!')
return
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
valuesSelected = currentSelectedExpense['values']
# displaying a message box asking for confirmation
confirmation = mb.askyesno('Are you sure?', f'Are you sure that you want to delete the record of {valuesSelected[2]}')
# if the user say YES, executing the SQL DELETE FROM command
if confirmation:
dbconnector.execute('DELETE FROM ExpenseTracker WHERE ID=%d' % valuesSelected[0])
dbconnector.commit()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying the information
mb.showinfo('Record deleted successfully!', 'The record you wanted to delete has been deleted successfully')
# function to delete all the entries
def removeAllExpenses():
'''This function will remove all the entries from the table'''
# displaying a message box asking for confirmation
confirmation = mb.askyesno('Are you sure?', 'Are you sure that you want to delete all the expense items from the database?', icon='warning')
# if the user say YES, deleting the entries from the table and executing the SQL DELETE FROM command to delete all the entries
if confirmation:
data_table.delete(*data_table.get_children())
dbconnector.execute('DELETE FROM ExpenseTracker')
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying the information
mb.showinfo('All Expenses deleted', 'All the expenses were successfully deleted')
else:
# returning the message box, if the operation is aborted
mb.showinfo('Ok then', 'The task was aborted and no expense was deleted!')
# function to add an expense
def addAnotherExpense():
'''This function will add an expense to the table and database'''
# using some global variables
global dateField, payee, description, amount, modeOfPayment
global dbconnector
# if any of the field is empty, return the message box displaying error
if not dateField.get() or not payee.get() or not description.get() or not amount.get() or not modeOfPayment.get():
mb.showerror('Fields empty!', "Please fill all the missing fields before pressing the add button!")
else:
# executing the SQL INSERT INTO command
dbconnector.execute(
'INSERT INTO ExpenseTracker (Date, Payee, Description, Amount, ModeOfPayment) VALUES (?, ?, ?, ?, ?)',
(dateField.get_date(), payee.get(), description.get(), amount.get(), modeOfPayment.get())
)
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning the message box displaying information
mb.showinfo('Expense added', 'The expense whose details you just entered has been added to the database')
# function to edit the details of an expense
def editExpense():
'''This function will allow user to edit the details of the selected expense'''
# using some global variables
global data_table
# defining a nested to update the details of the selected expense
def editExistingExpense():
'''This function will update the details of the selected expense in the database and table'''
# using some global variables
global dateField, amount, description, payee, modeOfPayment
global dbconnector, data_table
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
content = currentSelectedExpense['values']
# executing the SQL UPDATE command to update the record in database table
dbconnector.execute(
'UPDATE ExpenseTracker SET Date = ?, Payee = ?, Description = ?, Amount = ?, ModeOfPayment = ? WHERE ID = ?',
(dateField.get_date(), payee.get(), description.get(), amount.get(), modeOfPayment.get(), content[0])
)
dbconnector.commit()
# calling the clearFields() function
clearFields()
# calling the listAllExpenses() function
listAllExpenses()
# returning a message box displaying the message
mb.showinfo('Data edited', 'We have updated the data and stored in the database as you wanted')
# destroying the edit button
editSelectedButton.destroy()
# returning a message box displaying error if no record is selected
if not data_table.selection():
mb.showerror('No expense selected!', 'You have not selected any expense in the table for us to edit; please do that!')
return
# calling the viewExpenseInfo() method
viewExpenseInfo()
# adding the Edit button to edit the selected record
editSelectedButton = Button(
frameL3,
text = "Edit Expense",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = editExistingExpense
)
# using the grid() method to set the position of the above button on the main window screen
editSelectedButton.grid(row = 0, column = 0, sticky = W, padx = 50, pady = 10)
# function to display the details of selected expense into words
def selectedExpenseToWords():
'''This function will display the details of the selected expense from the table into words'''
# using some global variables
global data_table
# returning a message box displaying error, if no record is selected from the table
if not data_table.selection():
mb.showerror('No expense selected!', 'Please select an expense from the table for us to read')
return
# collecting the data from the selected row in dictionary format
currentSelectedExpense = data_table.item(data_table.focus())
# defining a variable to store the values from the collected data in list
val = currentSelectedExpense['values']
# defining the message to be displayed in the message box
msg = f'Your expense can be read like: \n"You paid {val[4]} to {val[2]} for {val[3]} on {val[1]} via {val[5]}"'
# returning the message box displaying the message
mb.showinfo('Here\'s how to read your expense', msg)
# function to display the expense details into words before adding it to the table
def expenseToWordsBeforeAdding():
'''This function will display the details of the expense into words before adding it to the table'''
# using some global variables
global dateField, description, amount, payee, modeOfPayment
# if any of the field is empty, return the message box displaying error
if not dateField.get() or not payee.get() or not description.get() or not amount.get() or not modeOfPayment.get():
mb.showerror('Incomplete data', 'The data is incomplete, meaning fill all the fields first!')
else:
# defining the message to be displayed in the message box
msg = f'Your expense can be read like: \n"You paid {amount.get()} to {payee.get()} for {description.get()} on {dateField.get_date()} via {modeOfPayment.get()}"'
# displaying a message box asking for confirmation
addQuestion = mb.askyesno('Read your record like: ', f'{msg}\n\nShould I add it to the database?')
# if the user say YES, calling the addAnotherExpense() function
if addQuestion:
addAnotherExpense()
else:
# returning a message box displaying information
mb.showinfo('Ok', 'Please take your time to add this record')
# main function
if __name__ == "__main__":
# connecting to the Database
dbconnector = sqlite3.connect("Expense_Tracker.db")
dbcursor = dbconnector.cursor()
# specifying the function to execute whenever the application runs
dbconnector.execute(
'CREATE TABLE IF NOT EXISTS ExpenseTracker (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, Date DATETIME, Payee TEXT, Description TEXT, Amount FLOAT, ModeOfPayment TEXT)'
)
# committing the above command
dbconnector.commit()
# creating the main window of the application
# creating an instance of the Tk() class
main_win = Tk()
# setting the title of the application
main_win.title("EXPENSE TRACKER - JAVATPOINT")
# setting the size and position of the window
main_win.geometry("1415x650+400+100")
# disabling the resizable option for better UI
main_win.resizable(0, 0)
# configuring the background color to #FFFAF0
main_win.config(bg = "#FFFAF0")
# setting the icon of the application
main_win.iconbitmap("./piggyBank.ico")
# adding frames to the window to provide structure to the other widgets
frameLeft = Frame(main_win, bg = "#FFF8DC")
frameRight = Frame(main_win, bg = "#DEB887")
frameL1 = Frame(frameLeft, bg = "#FFF8DC")
frameL2 = Frame(frameLeft, bg = "#FFF8DC")
frameL3 = Frame(frameLeft, bg = "#FFF8DC")
frameR1 = Frame(frameRight, bg = "#DEB887")
frameR2 = Frame(frameRight, bg = "#DEB887")
# using the pack() method to set the position of the above frames
frameLeft.pack(side=LEFT, fill = "both")
frameRight.pack(side = RIGHT, fill = "both", expand = True)
frameL1.pack(fill = "both")
frameL2.pack(fill = "both")
frameL3.pack(fill = "both")
frameR1.pack(fill = "both")
frameR2.pack(fill = "both", expand = True)
# ---------------- Adding widgets to the frameL1 frame ----------------
# adding the label to display the heading
headingLabel = Label(
frameL1,
text = "EXPENSE TRACKER",
font = ("Bahnschrift Condensed", "25"),
width = 20,
bg = "#8B4513",
fg = "#FFFAF0"
)
# adding the label to display the subheading
subheadingLabel = Label(
frameL1,
text = "Data Entry Frame",
font = ("Bahnschrift Condensed", "15"),
width = 20,
bg = "#F5DEB3",
fg = "#000000"
)
# using the pack() method to set the position of the above labels
headingLabel.pack(fill = "both")
subheadingLabel.pack(fill = "both")
# ---------------- Adding widgets to the frameL2 frame ----------------
# creating some labels to ask user to enter the required data
# date label
dateLabel = Label(
frameL2,
text = "Date:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# description label
descriptionLabel = Label(
frameL2,
text = "Description:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# amount label
amountLabel = Label(
frameL2,
text = "Amount:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# payee label
payeeLabel = Label(
frameL2,
text = "Payee:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# mode of payment label
modeLabel = Label(
frameL2,
text = "Mode of \nPayment:",
font = ("consolas", "11", "bold"),
bg = "#FFF8DC",
fg = "#000000"
)
# using the grid() method to set the position of the above labels in the grid format
dateLabel.grid(row = 0, column = 0, sticky = W, padx = 10, pady = 10)
descriptionLabel.grid(row = 1, column = 0, sticky = W, padx = 10, pady = 10)
amountLabel.grid(row = 2, column = 0, sticky = W, padx = 10, pady = 10)
payeeLabel.grid(row = 3, column = 0, sticky = W, padx = 10, pady = 10)
modeLabel.grid(row = 4, column = 0, sticky = W, padx = 10, pady = 10)
# instantiating the StringVar() class to retrieve the data in the string format from the user
description = StringVar()
payee = StringVar()
modeOfPayment = StringVar(value = "Cash")
# instantiating the DoubleVar() class to retrieve the amount detail in double datatype
amount = DoubleVar()
# creating a drop-down calendar for the user to enter the date
dateField = DateEntry(
frameL2,
date = datetime.datetime.now().date(),
font = ("consolas", "11"),
relief = GROOVE
)
# creating entry fields to enter the labelled data
# field to enter description
descriptionField = Entry(
frameL2,
text = description,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# field to enter the amount
amountField = Entry(
frameL2,
text = amount,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# field to enter payee information
payeeField = Entry(
frameL2,
text = payee,
width = 20,
font = ("consolas", "11"),
bg = "#FFFFFF",
fg = "#000000",
relief = GROOVE
)
# creating a drop-down menu to enter the mode of payment
modeField = OptionMenu(
frameL2,
modeOfPayment,
*['Cash', 'Cheque', 'Credit Card', 'Debit Card', 'UPI', 'Paytm', 'Google Pay', 'PhonePe', 'Razorpay']
)
# using the config() method to configure the width, font style, and background color of the option menu
modeField.config(
width = 15,
font = ("consolas", "10"),
relief = GROOVE,
bg = "#FFFFFF"
)
# using the grid() method to set the position of the above widgets in the grid format
dateField.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)
descriptionField.grid(row = 1, column = 1, sticky = W, padx = 10, pady = 10)
amountField.grid(row = 2, column = 1, sticky = W, padx = 10, pady = 10)
payeeField.grid(row = 3, column = 1, sticky = W, padx = 10, pady = 10)
modeField.grid(row = 4, column = 1, sticky = W, padx = 10, pady = 10)
# ---------------- Adding widgets to the frameL3 frame ----------------
# creating buttons to manipulate data
# insert button
insertButton = Button(
frameL3,
text = "Add Expense",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = addAnotherExpense
)
# convert button
convertButton = Button(
frameL3,
text = "Convert to Text before Adding",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#90EE90",
fg = "#000000",
relief = GROOVE,
activebackground = "#008000",
activeforeground = "#98FB98",
command = expenseToWordsBeforeAdding
)
# reset button
resetButton = Button(
frameL3,
text = "Reset the fields",
font = ("Bahnschrift Condensed", "13"),
width = 30,
bg = "#FF0000",
fg = "#FFFFFF",
relief = GROOVE,
activebackground = "#8B0000",
activeforeground = "#FFB4B4",
command = clearFields
)
# using the grid() method to set the position of the above buttons
insertButton.grid(row = 0, column = 0, sticky = W, padx = 50, pady = 10)
convertButton.grid(row = 1, column = 0, sticky = W, padx = 50, pady = 10)
resetButton.grid(row = 2, column = 0, sticky = W, padx = 50, pady = 10)
# ---------------- Adding widgets to the frameR1 frame ----------------
# creating buttons to manipulate data
# view button
viewButton = Button(
frameR1,
text = "View Selected Expense\'s Details",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = viewExpenseInfo
)
# edit button
editButton = Button(
frameR1,
text = "Edit Selected Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = editExpense
)
# convert button
convertSelectedButton = Button(
frameR1,
text = "Convert Selected Expense to a Sentence",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = selectedExpenseToWords
)
# delete button
deleteButton = Button(
frameR1,
text = "Delete Selected Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = removeExpense
)
# delete all button
deleteAllButton = Button(
frameR1,
text = "Delete All Expense",
font = ("Bahnschrift Condensed", "13"),
width = 35,
bg = "#FFDEAD",
fg = "#000000",
relief = GROOVE,
activebackground = "#A0522D",
activeforeground = "#FFF8DC",
command = removeAllExpenses
)
# using the grid() method to set the position of the above buttons
viewButton.grid(row = 0, column = 0, sticky = W, padx = 10, pady = 10)
editButton.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)
convertSelectedButton.grid(row = 0, column = 2, sticky = W, padx = 10, pady = 10)
deleteButton.grid(row = 1, column = 0, sticky = W, padx = 10, pady = 10)
deleteAllButton.grid(row = 1, column = 1, sticky = W, padx = 10, pady = 10)
# ---------------- Adding widgets to the frameR2 frame ----------------
# creating a table to display all the entries
data_table = ttk.Treeview(
frameR2,
selectmode = BROWSE,
columns = ('ID', 'Date', 'Payee', 'Description', 'Amount', 'Mode of Payment')
)
# creating a horizontal scrollbar to the table
Xaxis_Scrollbar = Scrollbar(
data_table,
orient = HORIZONTAL,
command = data_table.xview
)
# creating a vertical scrollbar to the table
Yaxis_Scrollbar = Scrollbar(
data_table,
orient = VERTICAL,
command = data_table.yview
)
# using the pack() method to set the position of the scrollbars
Xaxis_Scrollbar.pack(side = BOTTOM, fill = X)
Yaxis_Scrollbar.pack(side = RIGHT, fill = Y)
# configuring the horizontal and vertical scrollbars on the table
data_table.config(yscrollcommand = Yaxis_Scrollbar.set, xscrollcommand = Xaxis_Scrollbar.set)
# adding different headings to the table
data_table.heading('ID', text = 'S No.', anchor = CENTER)
data_table.heading('Date', text = 'Date', anchor = CENTER)
data_table.heading('Payee', text = 'Payee', anchor = CENTER)
data_table.heading('Description', text = 'Description', anchor = CENTER)
data_table.heading('Amount', text = 'Amount', anchor = CENTER)
data_table.heading('Mode of Payment', text = 'Mode of Payment', anchor = CENTER)
# adding different columns to the table
data_table.column('#0', width = 0, stretch = NO)
data_table.column('#1', width = 50, stretch = NO)
data_table.column('#2', width = 95, stretch = NO)
data_table.column('#3', width = 150, stretch = NO)
data_table.column('#4', width = 450, stretch = NO)
data_table.column('#5', width = 135, stretch = NO)
data_table.column('#6', width = 140, stretch = NO)
# using the place() method to set the position of the table on the main window screen
data_table.place(relx = 0, y = 0, relheight = 1, relwidth = 1)
# using mainloop() method to run the application
main_win.mainloop()
输出: