Script Python de orçamento pessoal
Publicado por Pedro Fernandes (última atualização em 30/01/2023)
[ Hits: 3.062 ]
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 )''' )
Versão das principais distribuições.
Enviar mensagem ao usuário trabalhando com as opções do php.ini
Meu Fork do Plugin de Integração do CVS para o KDevelop
Compartilhando a tela do Computador no Celular via Deskreen
Como Configurar um Túnel SSH Reverso para Acessar Sua Máquina Local a Partir de uma Máquina Remota
Configuração para desligamento automatizado de Computadores em um Ambiente Comercial
Compartilhamento de Rede com samba em modo Público/Anônimo de forma simples, rápido e fácil
Cups: Mapear/listar todas as impressoras de outro Servidor CUPS de forma rápida e fácil
Criando uma VPC na AWS via CLI
Tem como instalar o gerenciador AMD Adrenalin no Ubuntu 24.04? (8)