Script Python de orçamento pessoal
Publicado por Pedro Fernandes (última atualização em 30/01/2023)
[ Hits: 3.944 ]
Homepage: https://github.com/PedroF37
Download Orcamento-Pessoal.tar.gz
Projeto de Aplicativo em Python de Controle de Receitas e Despesas
Aplicativo permite entrar com as receitas e categorias de despesas, depois mostra a receita total, os gastos e valor restante de toda a receita. Permite editar valores e categorias e deletar a tabela para iniciar outra nova (se isso faz algum sentido..). Projeto feito para praticar e para aprender um pouco mais de matplotlib, pandas e sqlite. Não está 100 por cento perfeito mas está perfeitamente funcional.
Usa o tkinter, matplotlib, pandas, sqlite e o pillow
Arquivos e icones e imagens estão em:
https://github.com/PedroF37/Orcamento-Pessoal
Arquivo - main.py
# --------------------------------------------------------------------------- #
# IMPORTAÇÕES
# tkinter
from tkinter import Tk, Frame, Label, Button, Entry, messagebox
from tkinter.ttk import Style, Progressbar, Treeview, Scrollbar, Combobox
# Pillow
from PIL import Image, ImageTk
# tkcalendar
from tkcalendar import DateEntry
# matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
# view
from view import insert_category, insert_revenue, insert_expenses
from view import show_category_records, drop_tables
from view import table, bar_graph_values, update_instruction
from view import pie_graph_values, percentage_bar_values
# bd_create
from bd_create import create_category, create_revenue, create_expenses
# re
import re
# --------------------------------------------------------------------------- #
# CONSTANTES E GLOBAIS
COLOR0 = "#2e2d2b"
COLOR1 = "#feffff"
COLOR2 = "#4fa882"
COLOR3 = "#38576b"
COLOR4 = "#403d3d"
COLOR5 = "#e06636"
COLOR6 = "#038cfc"
COLOR7 = "#3fbfb9"
COLOR8 = "#263238"
COLOR9 = "#e9edf5"
COLOR10 = '#545454'
COLOR11 = '#83a9e6'
COLORS = [
'#5588bb', '#66bbbb',
'#99bb55', '#ee9944',
'#444466', '#bb5555'
]
# Padrões para validações das Entries
string_pattern = r'^[A-Za-z]+( [A-Za-z]+)*$'
number_pattern = r'^\d+$'
global tree
# --------------------------------------------------------------------------- #
# FUNÇÕES
def manipulate_tables():
"""Função que cuida de deletar e criar as tabelas."""
drop_tables()
refresh_data()
messagebox.showinfo(
'Sucesso', f'Dados deletados com sucesso. A visualização' \
f' pode ficar estranha enquanto não reiniciar o aplicativo.'
)
def percentage():
"""Função para mostrar a percentagem."""
percent_message_label = Label(
middle_frame, text='Porcentagem da receita restante',
height=1, anchor='nw', font=('Verdana 12'),
bg=COLOR1, fg=COLOR4
)
percent_message_label.place(x=7, y=5)
style = Style()
style.theme_use('default')
style.configure(
'black.Horizontal.TProgressbar',
background='#daed6b'
)
style.configure('TProgressbar', thickness=25)
progressbar = Progressbar(
middle_frame, length=180,
style='black.Horizontal.TProgressbar'
)
progressbar.place(x=10, y=35)
progressbar['value'] = percentage_bar_values()
percent_value = percentage_bar_values()
percent_number_label = Label(
middle_frame, text=f'{percent_value:,.2f}%',
anchor='nw', font=('Verdana 12'),
bg=COLOR1, fg=COLOR4
)
percent_number_label.place(x=200, y=35)
def bar_graph():
"""Função que cuida do gráfico de barra."""
category_list = ['Renda', 'Despesas', 'Saldo']
values_list = bar_graph_values()
# O gráfico e os eixos. Esta parte dos gráficos
# foi muito copy/paste. matplotlib ainda é meio
# "chinês" para mim kk.
graph = plt.Figure(figsize=(4, 3.45), dpi=60)
ax = graph.add_subplot(111)
# ax.autoscale(enable=True, axis='both', tight=None)
ax.bar(category_list, values_list, color=COLORS, width=0.9)
c = 0
for item in ax.patches:
ax.text(
item.get_x() - .001, item.get_height() + .5,
f'{values_list[c]:,.0f}', fontsize=17,
fontstyle='italic', verticalalignment='bottom',
color='dimgrey'
)
c += 1
ax.set_xticklabels(category_list, fontsize=16)
# Personalizando o gráfico
ax.patch.set_facecolor('#ffffff')
ax.spines['bottom'].set_color('#CCCCCC')
ax.spines['bottom'].set_linewidth(1)
ax.spines['right'].set_linewidth(0)
ax.spines['top'].set_linewidth(0)
ax.spines['left'].set_color('#CCCCCC')
ax.spines['left'].set_linewidth(1)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.tick_params(bottom=False, left=False)
ax.set_axisbelow(True)
ax.yaxis.grid(True, color='#EEEEEE')
ax.xaxis.grid(False)
canvas = FigureCanvasTkAgg(graph, middle_frame)
canvas.get_tk_widget().place(x=10, y=70)
def summary():
"""Função que cuida do sumário da renda e despesas."""
values = bar_graph_values()
# Renda Mensal
line_label = Label(
middle_frame, text='',
width=215, height=1,
anchor='nw', font=('Arial 1'),
bg=COLOR10
)
# line_label.place(x=349, y=52)
line_label.place(x=500, y=52)
summary_label = Label(
middle_frame, text='TOTAL RENDA MENSAL ',
anchor='nw', font=('Verdana 12'),
bg=COLOR1, fg=COLOR11
)
# summary_label.place(x=349, y=33)
summary_label.place(x=500, y=33)
value_label = Label(
middle_frame, text=f'R$ {values[0]:,.2f}',
anchor='nw', font=('Arial 17'),
bg=COLOR1, fg=COLOR10
)
# value_label.place(x=349, y=70)
value_label.place(x=500, y=70)
# Despesas Mensais
line_label = Label(
middle_frame, text='',
width=215, height=1,
anchor='nw', font=('Arial 1'),
bg=COLOR10
)
# line_label.place(x=349, y=132)
line_label.place(x=500, y=132)
summary_label = Label(
middle_frame, text='TOTAL DESPESAS MENSAIS',
anchor='nw', font=('Verdana 12'),
bg=COLOR1, fg=COLOR11
)
# summary_label.place(x=349, y=113)
summary_label.place(x=500, y=113)
value_label = Label(
middle_frame, text=f'R$ {values[1]:,.2f}',
anchor='nw', font=('Arial 17'),
bg=COLOR1, fg=COLOR10
)
# value_label.place(x=349, y=150)
value_label.place(x=500, y=150)
# Saldo Total
line_label = Label(
middle_frame, text='',
width=215, height=1,
anchor='nw', font=('Arial 1'),
bg=COLOR10
)
# line_label.place(x=349, y=212)
line_label.place(x=500, y=212)
summary_label = Label(
middle_frame, text='SALDO TOTAL DA CAIXA ',
anchor='nw', font=('Verdana 12'),
bg=COLOR1, fg=COLOR11
)
# summary_label.place(x=349, y=193)
summary_label.place(x=500, y=193)
value_label = Label(
middle_frame, text=f'R$ {values[2]:,.2f}',
anchor='nw', font=('Arial 17'),
bg=COLOR1, fg=COLOR10
)
# value_label.place(x=349, y=230)
value_label.place(x=500, y=230)
def pie_graph():
"""Função que cuida do gráfico circular (pie graph)."""
graph = plt.Figure(figsize=(5, 3), dpi=90)
ax = graph.add_subplot(111)
category_list = pie_graph_values()[0] # Categoria
values_list = pie_graph_values()[1] # Valores
explode = []
for item in category_list:
explode.append(0.05)
ax.pie(
values_list, explode=explode,
wedgeprops=dict(width=0.2),
autopct='%1.1f%%', colors=COLORS,
shadow=True, startangle=90
)
ax.legend(
category_list,
loc="center right",
bbox_to_anchor=(1.55, 0.50)
)
category_canvas = FigureCanvasTkAgg(graph, pie_graph_frame)
category_canvas.get_tk_widget().place(x=130, y=10)
def show_table():
"""Função que cuida de mostrar a tabela dos dados."""
global tree
table_label = Label(
middle_frame, text='Tabela Receitas e Despesas',
anchor='nw', font=('Verdana 12'), bg=COLOR1, fg=COLOR4
)
table_label.place(x=5, y=309)
table_header = ['#id', 'Categoria', 'Data', 'Quantia']
records = table()
tree = Treeview(
table_frame, selectmode='extended',
columns=table_header, show='headings'
)
# Barra de rolagem vertical
vsb = Scrollbar(table_frame, orient='vertical', command=tree.yview)
# Barra de rolagem horizontal
hsb = Scrollbar(table_frame, orient='horizontal', command=tree.xview)
tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
tree.grid(row=0, column=0, sticky='nsew')
vsb.grid(row=0, column=1, sticky='ns')
hsb.grid(row=1, column=0, sticky='ew')
# Posicionamento
hd = ['center', 'center', 'center', 'center']
h = [30, 100, 100, 100]
n = 0
for item in table_header:
tree.heading(item, text=item.title(), anchor='center')
tree.column(item, width=h[n], anchor=hd[n])
n += 1
for item in records:
tree.insert('', 'end', values=item)
def insert_new_category():
"""Função que cuida da inserção de novas categorias."""
category_name = [new_category_entry.get()]
# Validação
for item in category_name:
if item == '':
messagebox.showerror('Erro', 'Preencha todos os campos')
return
# Só aceita alfabeto
category_string = ' '.join(category_name)
if re.fullmatch(string_pattern, category_string) is None:
messagebox.showerror('Erro', 'Categoria aceita apenas letras')
return
else:
insert_category(category_name)
messagebox.showinfo('Sucesso', 'Dados inseridos com sucesso')
reset_widgets(new_category_entry)
# Pegando os valores da categoria
categories = show_category_records()
category = []
for item in categories:
# item[1] -> categoria/ item[0] -> id
category.append(item[1])
# atualizando dados
expense_category_combo['values'] = (category)
def insert_new_renevue():
"""Função que cuida da inserção de novas receitas."""
revenue_name = 'Receita'
revenue_date = revenue_calendar_entry.get()
revenue_value = total_value_revenue_entry.get()
insert_revenue_list = [revenue_name, revenue_date, revenue_value]
# Validação
for item in insert_revenue_list:
if item == '':
messagebox.showerror('Erro', 'Preencha todos os campos')
return
# Só aceita numérico em revenue_value
if re.fullmatch(number_pattern, revenue_value) is None:
messagebox.showerror(
'Erro', 'Quantia aceita apenas digitos [0-9]'
)
return
else:
insert_revenue(insert_revenue_list)
messagebox.showinfo('Sucesso', 'Dados inseridos com sucesso')
reset_widgets(revenue_calendar_entry, total_value_revenue_entry)
# atualizando dados
refresh_data()
def insert_new_expenses():
"""Função que cuida da inserção de novas despesas."""
# Não pode inserir nada se ainda não tiver receita nunhuma.
# bar_graph_values()[0] == valor total da receita.
if bar_graph_values()[0] == 0:
messagebox.showerror(
'Erro',
'Não pode inserir despesas sem ter uma receita antes'
)
return
else:
expense_name = expense_category_combo.get()
expense_date = expense_calendar_entry.get()
expense_value = total_value_expense_entry.get()
insert_expense_list = [expense_name, expense_date, expense_value]
# Validação
for item in insert_expense_list:
if item == '':
messagebox.showerror('Erro', 'Preencha todos os campos')
return
# Só aceita numérico em expense_value
if re.fullmatch(number_pattern, expense_value) is None:
messagebox.showerror(
'Erro', 'Quantia aceita apenas digitos [0-9]'
)
return
else:
insert_expenses(insert_expense_list)
messagebox.showinfo('Sucesso', 'Dados inseridos com sucesso')
reset_widgets(
expense_category_combo,
expense_calendar_entry,
total_value_expense_entry
)
# atualizando dados
refresh_data()
def reset_widgets(*widgets):
"""Função que reseta os entries e combos depois de inserções."""
for widget in widgets:
widget.delete(0, 'end')
return
def refresh_data():
"""Função que cuida de atualizar
os dados depois de inserções/remoções."""
percentage()
summary()
bar_graph()
pie_graph()
show_table()
return
def edit_data():
"""Função que cuida de editar/alterar dados da tabela."""
replaced_category = alter_category_entry.get()
replaced_amount = alter_value_entry.get()
if tree.focus() == '':
messagebox.showerror(
'Erro',
'Tem que selecionar um registro na TABELA da esquerda para alterar'
)
return
else:
treeview_data = tree.focus()
treeview_dict = tree.item(treeview_data)
treeview_list = treeview_dict['values']
# Aqui, obrigo a preencher apenas um dos campos e não os dois,
# porque podemos querer mudar apenas o valor do aluguél por exemplo,
# ou pudemos queres mudar apenas o nome da despesa. Ou os dois kk
if replaced_category == '' and replaced_amount == '':
messagebox.showerror(
'Erro', 'Tem que preencher pelo menos um dos campos'
)
return
else:
# Agora temos que validar o ou os inputs
if replaced_category != '':
# Só alfabeto
if re.fullmatch(string_pattern, replaced_category) is None:
messagebox.showerror(
'Erro', 'Categoria aceita apenas letras'
)
return
if replaced_amount != '':
# Só digito
if re.fullmatch(number_pattern, replaced_amount) is None:
messagebox.showerror(
'Erro', 'Quantia aceita apenas digitos [0-9]'
)
return
update_instruction(
treeview_list, replaced_category, replaced_amount
)
messagebox.showinfo('Sucesso', 'Dados atualizados com sucesso')
refresh_data()
reset_widgets(alter_category_entry, alter_value_entry)
# --------------------------------------------------------------------------- #
# CRIA TABELAS CASO NECESSÁRIO
# Instrução sql em bd_create é: create if not exists
# logo se já existir não faz nada ok?!
create_category()
create_revenue()
create_expenses()
# --------------------------------------------------------------------------- #
# JANELA
window = Tk()
window.title('')
window.geometry('1320x648')
window.resizable(width=False, height=False)
# window.configure(background=COLOR9)
style = Style(window)
style.theme_use('clam')
# --------------------------------------------------------------------------- #
# FRAMES, TÍTULO E LOGO
# Frames principais
upper_frame = Frame(
window, width=1320,
height=50, bg=COLOR1,
relief='flat'
)
upper_frame.grid(row=0, column=0, padx=0)
middle_frame = Frame(
window, width=1320, height=361,
bg=COLOR1, pady=20, relief='raised'
)
middle_frame.grid(
row=1, column=0,
pady=1, padx=0,
sticky='nsew'
)
lower_frame = Frame(
window, width=1320, height=237,
bg=COLOR1, relief='flat'
)
lower_frame.grid(
row=2, column=0,
pady=0, padx=0,
sticky='nsew'
)
# Frames dentro de Frames
pie_graph_frame = Frame(
middle_frame, width=580,
height=250, bg=COLOR1
)
# pie_graph_frame.place(x=415, y=5)
pie_graph_frame.place(x=720, y=5)
table_frame = Frame(
lower_frame, width=330,
height=237, bg=COLOR1
)
table_frame.grid(row=0, column=0)
expenses_insert_frame = Frame(
lower_frame, width=330,
height=237, bg=COLOR1
)
expenses_insert_frame.grid(row=0, column=1, padx=5)
revenue_insert_frame = Frame(
lower_frame, width=330,
height=237, bg=COLOR1
)
revenue_insert_frame.grid(row=0, column=2)
alter_table_frame = Frame(
lower_frame, width=330,
height=237, bg=COLOR1
)
alter_table_frame.grid(row=0, column=3)
# Título e logo do app
img = Image.open('Icones/money-bag.png')
img = img.resize((45, 45))
img = ImageTk.PhotoImage(img)
title_label = Label(
upper_frame, image=img,
text=' Controle de Receitas e Despesas',
width=1400, compound='left',
padx=5, relief='raised',
anchor='nw', bg=COLOR1,
fg=COLOR4, font=('Verdana 20 bold')
)
title_label.grid(row=0, column=0)
# --------------------------------------------------------------------------- #
# DESPESAS
expenses_label = Label(
expenses_insert_frame,
text='Insira novas Despesas',
height=1, anchor='nw',
font=('Verdana 10 bold'),
bg=COLOR1, fg=COLOR4
)
expenses_label.place(x=10, y=5)
# Categoria das despesas
category_label = Label(
expenses_insert_frame,
text='Categoria',
height=1, anchor='nw',
font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
category_label.place(x=10, y=40)
# Pegandos categorias
categories_list = show_category_records()
categories = []
for category in categories_list:
categories.append(category[1])
expense_category_combo = Combobox(
expenses_insert_frame,
width=10, font=('Roboto 10')
)
expense_category_combo['values'] = (categories)
expense_category_combo.place(x=110, y=41)
# Data das despesas
expense_date_label = Label(
expenses_insert_frame,
text='Data', height=1,
anchor='nw', font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
expense_date_label.place(x=10, y=70)
expense_calendar_entry = DateEntry(
expenses_insert_frame,
width=9, background='darkgrey',
foreground='white', year=2023,
borderwidth=2
)
expense_calendar_entry.place(x=110, y=71)
# Quantia total das despesas
total_value_expense_label = Label(
expenses_insert_frame,
text='Quantia Total',
height=1, anchor='nw',
font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
total_value_expense_label.place(x=10, y=102)
total_value_expense_entry = Entry(
expenses_insert_frame,
width=10, justify='left',
relief='solid'
)
total_value_expense_entry.place(x=110, y=101)
# Botão adicionar
add_expenses_img = Image.open('Icones/add.png')
add_expenses_img = add_expenses_img.resize((17, 17))
add_expenses_img = ImageTk.PhotoImage(add_expenses_img)
insert_expenses_button = Button(
expenses_insert_frame,
image=add_expenses_img,
text='ADICIONAR', width=68,
compound='left', anchor='se',
font=('Roboto 7 bold'), bg=COLOR1,
fg=COLOR4, overrelief='ridge',
command=insert_new_expenses
)
insert_expenses_button.place(x=110, y=141)
# --------------------------------------------------------------------------- #
# RECEITAS
revenues_label = Label(
revenue_insert_frame,
text='Insira novas Receitas/Categorias',
height=1, anchor='nw', font=('Verdana 10 bold'),
bg=COLOR1, fg=COLOR4
)
revenues_label.place(x=10, y=5)
# Data das receitas
revenue_date_label = Label(
revenue_insert_frame, text='Data',
height=1, anchor='nw', font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
revenue_date_label.place(x=10, y=40)
revenue_calendar_entry = DateEntry(
revenue_insert_frame, width=9,
background='darkgrey', foreground='white',
borderwidth=2, year=2023
)
revenue_calendar_entry.place(x=110, y=41)
# Quantia total das receitas
total_value_revenue_label = Label(
revenue_insert_frame, text='Quantia Total',
height=1, anchor='nw', font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
total_value_revenue_label.place(x=10, y=74)
total_value_revenue_entry = Entry(
revenue_insert_frame, width=10,
justify='left', relief='solid'
)
total_value_revenue_entry.place(x=110, y=71)
add_revenue_img = Image.open('Icones/add.png')
add_revenue_img = add_revenue_img.resize((17, 17))
add_revenue_img = ImageTk.PhotoImage(add_revenue_img)
insert_revenue_button = Button(
revenue_insert_frame,
image=add_revenue_img,
text='ADICIONAR', width=68,
compound='left', anchor='se',
font=('Roboto 7 bold'),
bg=COLOR1, fg=COLOR4,
overrelief='ridge',
command=insert_new_renevue
)
insert_revenue_button.place(x=110, y=111)
# --------------------------------------------------------------------------- #
# NOVA CATEGRIA
new_category_label = Label(
revenue_insert_frame,
text='Nova Categoria',
height=1, anchor='nw',
font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
new_category_label.place(x=10, y=163)
new_category_entry = Entry(
revenue_insert_frame, width=10,
justify='left', relief='solid'
)
new_category_entry.place(x=110, y=161)
add_category_img = Image.open('Icones/add.png')
add_category_img = add_category_img.resize((17, 17))
add_category_img = ImageTk.PhotoImage(add_category_img)
insert_new_category_button = Button(
revenue_insert_frame,
image=add_category_img,
text='ADICIONAR', width=68,
compound='left', anchor='se',
font=('Roboto 7 bold'),
bg=COLOR1, fg=COLOR4,
overrelief='ridge',
command=insert_new_category
)
insert_new_category_button.place(x=110, y=195)
# --------------------------------------------------------------------------- #
# ALTERAR DADOS DA TABELA
alter_data_label = Label(
alter_table_frame,
text='Alterar/Deletar Tabela',
height=1, anchor='nw',
font=('Verdana 10 bold'),
bg=COLOR1, fg=COLOR4
)
alter_data_label.place(x=70, y=5)
# Categoria das despesas
alter_category_label = Label(
alter_table_frame,
text='Categoria',
height=1, anchor='nw',
font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
alter_category_label.place(x=70, y=40)
alter_category_entry = Entry(
alter_table_frame, width=10,
justify='left', relief='solid'
)
alter_category_entry.place(x=170, y=41)
alter_value_label = Label(
alter_table_frame, text='Quantia Total',
height=1, anchor='nw', font=('Roboto 10'),
bg=COLOR1, fg=COLOR4
)
alter_value_label.place(x=70, y=108)
alter_value_entry = Entry(
alter_table_frame, width=10,
justify='left', relief='solid'
)
alter_value_entry.place(x=170, y=101)
alter_img = Image.open('Icones/replace.png')
alter_img = alter_img.resize((17, 17))
alter_img = ImageTk.PhotoImage(alter_img)
alter_button = Button(
alter_table_frame,
image=alter_img,
text='ALTERAR ', width=68,
compound='left', anchor='se',
font=('Roboto 7 bold'),
bg=COLOR1, fg=COLOR4,
overrelief='ridge',
command=edit_data
)
alter_button.place(x=170, y=131)
# --------------------------------------------------------------------------- #
# DELETAR TODA A TABELA
destroy_label = Label(
alter_table_frame,
text='Apagar tabela',
height=1, anchor='nw',
font=('Roboto 10 bold'),
bg=COLOR1, fg=COLOR4
)
destroy_label.place(x=70, y=197)
destroy_img = Image.open('Icones/delete.png')
destroy_img = destroy_img.resize((17, 17))
destroy_img = ImageTk.PhotoImage(destroy_img)
# Os espaços são propositais. Para os icones
# adicionar e deletar ficarem alinhados.
destroy_button = Button(
alter_table_frame,
image=destroy_img,
text='DELETAR ',
width=68, compound='left',
anchor='se', font=('Roboto 7 bold'),
bg=COLOR1, fg=COLOR4, overrelief='ridge',
command=manipulate_tables
)
destroy_button.place(x=170, y=191)
# --------------------------------------------------------------------------- #
# LOOP
percentage()
summary()
bar_graph()
pie_graph()
show_table()
window.mainloop()
# -------------------------------------------------------------------------------------------------------------- #
Arquivo - view.py
# --------------------------------------------------------------------------- #
# IMPORTAÇÕES
import sqlite3
import pandas as pd
# --------------------------------------------------------------------------- #
# CONEXÃO
connection = sqlite3.connect('data.db')
# --------------------------------------------------------------------------- #
# INSERÇÕES
def insert_category(category):
"""Função para insterir categoria em tabela"""
with connection:
cursor = connection.cursor()
insertion = 'insert into Categoria (nome) values (?)'
cursor.execute(insertion, category)
def insert_revenue(revenue):
"""Função para insterir receita em tabela"""
with connection:
cursor = connection.cursor()
insertion = '''
insert into Receitas(
categoria,
adicionado_em, valor
)
values (?, ?, ?)
'''
cursor.execute(insertion, revenue)
def insert_expenses(expenses):
"""Função para insterir gastos em tabela"""
with connection:
cursor = connection.cursor()
insertion = '''
insert into Gastos(
categoria,
retirado_em, valor
)
values (?, ?, ?)
'''
cursor.execute(insertion, expenses)
# --------------------------------------------------------------------------- #
# EDIÇÃO/ATUALIZAÇÃO
def update_instruction(record, category, amount):
"""Função que cuida da atualização dos dados da tabela."""
# Monta a instrução de update, de acordo com os campos preenchidos.
if record[1] == 'Receita':
with connection:
cursor = connection.cursor()
update = 'update Receitas set valor = (?) where id = (?)'
cursor.execute(update, [amount, record[0]])
else:
# Aqui, não tem perigo de fazer deste jeito, pois
# no arquivo main já impeço de ter dois campos vazios
# antes de chamar esta função.
if category == '':
new_category = record[1]
new_amount = amount
elif amount == '':
new_category = category
new_amount = record[3]
else:
new_category = category
new_amount = amount
with connection:
cursor = connection.cursor()
update_expense = '''
update Gastos
set categoria = (?),
valor = (?)
where id = (?)
limit 1
'''
update_category = '''
update Categoria
set nome = (?)
where id = (?)
'''
cursor.execute(
update_expense,
[new_category, new_amount, record[0]]
)
cursor.execute(update_category, [new_category, record[0]])
# --------------------------------------------------------------------------- #
# REMOÇÃO DAS TABELAS
def drop_tables():
"""Função que cuida de apagar a tabela toda."""
with connection:
cursor = connection.cursor()
for item in ('Categoria', 'Receitas', 'Gastos'):
cursor.execute(f'delete from {item}')
# --------------------------------------------------------------------------- #
# VISUALIZAÇÕES
def show_category_records():
"""Função para mostrar todos os registros da tabela categorias."""
datalist = []
with connection:
cursor = connection.cursor()
cursor.execute('select * from Categoria')
records = cursor.fetchall()
for record in records:
datalist.append(record)
return datalist
def show_revenue_records():
"""Função para mostrar todos os registros da tabela receitas."""
datalist = []
with connection:
cursor = connection.cursor()
cursor.execute('select * from Receitas')
records = cursor.fetchall()
for record in records:
datalist.append(record)
return datalist
def show_expenses_records():
"""Função para mostrar todos os registros da tabela gastos."""
datalist = []
with connection:
cursor = connection.cursor()
cursor.execute('select * from Gastos')
records = cursor.fetchall()
for record in records:
datalist.append(record)
return datalist
# --------------------------------------------------------------------------- #
# ATUALIZAÇÃO DOS GRÁFICOS, SUMÁRIO E TABELA
def table():
"""Função que cuida dos dados da tabela."""
expenses = show_expenses_records()
revenues = show_revenue_records()
table_list = []
for item in expenses:
table_list.append(item)
for item in revenues:
table_list.append(item)
return table_list
def bar_graph_values():
"""Função que cuida dos dados do gráfico de barra e do sumário."""
# Receita total
revenues = show_revenue_records()
revenues_list = []
for item in revenues:
revenues_list.append(item[3])
total_revenue = sum(revenues_list)
# Despesas totais
expenses = show_expenses_records()
expenses_list = []
for item in expenses:
expenses_list.append(item[3])
total_expenses = sum(expenses_list)
# Saldo total
total_credit = total_revenue - total_expenses
return (total_revenue, total_expenses, total_credit)
def pie_graph_values():
"""Cuida dos dados do gráfico circular (pie)"""
expenses = show_expenses_records()
table_list = []
for item in expenses:
table_list.append(item)
dataframe = pd.DataFrame(
table_list,
columns=[
'id', 'categoria',
'Data', 'valor'
]
)
dataframe = dataframe.groupby('categoria')['valor'].sum()
amount_list = dataframe.values.tolist()
category_list = []
for item in dataframe.index:
category_list.append(item)
return ([category_list, amount_list])
def percentage_bar_values():
"""Cuida dos valores da barra de porcentagem."""
# Aqui, se não tiver dados, exemplo, se for a primeira vez
# que executa o script ou se tiver deletado a tabela, dá erro
# por causa da divisão por zero (ZeroDivision). Logo, se
# bar_graph_values()[1] == gastos_totais for igual a zero
# retorna apenas a receita.
if bar_graph_values()[1] == 0:
return bar_graph_values()[0]
else:
return (
(
bar_graph_values()[0] - bar_graph_values()[1]
) / bar_graph_values()[0]) * 100
# ----------------------------------------------------------------------------------------------------------------- #
Arquivo - bd_create.py (main.py executa este arquivo, não precisa rodar manualmente)
import sqlite3
# Conexão
connection = sqlite3.connect('data.db')
'''
Segundo o que li na documentação do sqlite3, não é recomendado
usar o atributo autoincrement. Até porque pelo vi, o sqlite3
incrementa automaticamente o id (rowid), quando especificamos
id integer primary key.
'''
# tabela categoria
def create_category():
with connection:
cursor = connection.cursor()
cursor.execute(
'''create table if not exists Categoria(
id integer primary key,
nome text
)'''
)
# tabela receita
def create_revenue():
with connection:
cursor = connection.cursor()
cursor.execute(
'''create table if not exists Receitas(
id integer primary key,
categoria text,
adicionado_em date,
valor decimal
)'''
)
# tabela de gastos
def create_expenses():
with connection:
cursor = connection.cursor()
cursor.execute(
'''create table if not exists Gastos(
id integer primary key,
categoria text,
retirado_em date,
valor decimal
)'''
)
Simples script para atrasar/adiantar legendas
Sugestão aleatória de filmes e séries para assistir por streaming
Monitorando o Preço do Bitcoin ou sua Cripto Favorita em Tempo Real com um Widget Flutuante
IA Turbina o Desktop Linux enquanto distros renovam forças
Como extrair chaves TOTP 2FA a partir de QRCODE (Google Authenticator)
Como realizar um ataque de força bruta para desobrir senhas?
Como usar Gpaste no ambiente Cinnamon
Atualizando o Fedora 42 para 43
Pergunta: Meu teclado não está respondendo direito como e consertar? (1)
SQLITE não quer funcionar no LINUX LMDE6 64 com Lazaruz 4.2 64bit (n... (0)
Secure boot, artigo interessante, nada técnico. (5)









