· last year · Aug 24, 2024, 10:55 PM
1import tkinter as tk
2from tkinter import ttk, messagebox
3import sqlite3
4import pyperclip
5import webbrowser
6from reportlab.lib.pagesizes import letter, landscape
7from reportlab.pdfgen import canvas
8
9class Application(tk.Tk):
10 def __init__(self):
11 super().__init__()
12 self.title("Gerenciador de Senhas de Sites - Versão 5.3")
13 self.geometry("1000x600")
14 self.sort_order_site = "asc"
15 self.sort_order_login = "asc"
16 self.init_db() # Inicializa o banco de dados primeiro
17 self.create_widgets()
18 self.create_menu()
19 self.date_label = ttk.Label(self, text="Mizuno - 24/08/2024")
20 self.date_label.place(x=10, y=self.winfo_height() - 30)
21 self.center_window()
22
23 def create_widgets(self):
24 self.search_var = tk.StringVar()
25 self.search_entry = ttk.Entry(self, textvariable=self.search_var)
26 self.search_entry.pack(pady=(0, 10))
27 self.search_button = ttk.Button(self, text="Pesquisar", command=self.search_records)
28 self.search_button.pack(pady=(0, 10))
29 self.tree = ttk.Treeview(self, columns=("Site", "URL", "Login", "Senha"), show="headings")
30 self.tree.heading("Site", text="Site", command=self.sort_sites)
31 self.tree.heading("URL", text="URL")
32 self.tree.heading("Login", text="Login", command=self.sort_login)
33 self.tree.heading("Senha", text="Senha")
34 self.tree.pack(pady=10, side="left", fill="both", expand=True)
35 scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)
36 scrollbar.pack(side="right", fill="y")
37 self.tree.configure(yscrollcommand=scrollbar.set)
38 self.refresh_button = ttk.Button(self, text="Atualizar", command=self.refresh_tree)
39 self.refresh_button.pack()
40 self.add_button = ttk.Button(self, text="Adicionar", command=self.add_entry)
41 self.add_button.pack()
42 self.edit_button = ttk.Button(self, text="Editar", command=self.edit_entry)
43 self.edit_button.pack()
44 self.delete_button = ttk.Button(self, text="Excluir", command=self.confirm_delete)
45 self.delete_button.pack()
46
47 # Novo botão para exportar para PDF
48 self.pdf_button = ttk.Button(self, text="Exportar PDF", command=self.export_to_pdf)
49 self.pdf_button.pack()
50
51 self.load_data()
52 self.tree.bind("<Button-3>", self.popup_menu)
53 self.tree.bind("<Double-1>", self.open_url)
54
55 def create_menu(self):
56 self.menu_bar = tk.Menu(self)
57 self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
58 self.file_menu.add_command(label="Sobre", command=self.show_about_dialog)
59 self.file_menu.add_separator()
60 self.file_menu.add_command(label="Sair", command=self.quit_app)
61 self.menu_bar.add_cascade(label="Arquivo", menu=self.file_menu)
62 self.config(menu=self.menu_bar)
63
64 def open_url(self, event):
65 if self.tree.selection():
66 item = self.tree.selection()[0]
67 url = self.tree.item(item, "values")[1]
68 webbrowser.open(url)
69
70 def center_window(self):
71 self.update_idletasks()
72 width = self.winfo_width()
73 height = self.winfo_height()
74 x = (self.winfo_screenwidth() - width) // 2
75 y = (self.winfo_screenheight() - height) // 2
76 self.geometry('{}x{}+{}+{}'.format(width, height, x, y))
77
78 def init_db(self):
79 self.conn = sqlite3.connect("dados.db")
80 self.cursor = self.conn.cursor()
81 self.cursor.execute("""
82 CREATE TABLE IF NOT EXISTS senhas (
83 id INTEGER PRIMARY KEY AUTOINCREMENT,
84 site TEXT,
85 url TEXT,
86 login TEXT,
87 senha TEXT
88 )
89 """)
90 self.conn.commit()
91
92 def load_data(self):
93 self.cursor.execute("SELECT site, url, login, senha FROM senhas")
94 for row in self.cursor.fetchall():
95 self.tree.insert("", "end", values=row)
96
97 def save_data(self):
98 self.cursor.execute("DELETE FROM senhas")
99 for item in self.tree.get_children():
100 values = self.tree.item(item, "values")
101 self.cursor.execute("INSERT INTO senhas (site, url, login, senha) VALUES (?, ?, ?, ?)", values)
102 self.conn.commit()
103
104 def refresh_tree(self):
105 for item in self.tree.get_children():
106 self.tree.delete(item)
107 self.load_data()
108
109 def add_entry(self):
110 AddEntryWindow(self)
111
112 def edit_entry(self):
113 try:
114 item = self.tree.selection()[0]
115 values = self.tree.item(item, "values")
116 EditEntryWindow(self, values)
117 except IndexError:
118 messagebox.showerror("Erro", "Selecione um registro para editar.")
119
120 def delete_entry(self):
121 selected_item = self.tree.selection()[0]
122 site = self.tree.item(selected_item, "values")[0]
123 self.cursor.execute("DELETE FROM senhas WHERE site=?", (site,))
124 self.conn.commit()
125 self.tree.delete(selected_item)
126
127 def confirm_delete(self):
128 try:
129 if messagebox.askyesno("Confirmar", "Tem certeza que deseja excluir este registro?"):
130 self.delete_entry()
131 except IndexError:
132 messagebox.showerror("Erro", "Selecione um registro para excluir.")
133
134 def popup_menu(self, event):
135 popup_menu = tk.Menu(self, tearoff=0)
136 popup_menu.add_command(label="Copiar URL", command=lambda: self.copy_to_clipboard(1))
137 popup_menu.add_command(label="Copiar Login", command=lambda: self.copy_to_clipboard(2))
138 popup_menu.add_command(label="Copiar Senha", command=lambda: self.copy_to_clipboard(3))
139 popup_menu.post(event.x_root, event.y_root)
140
141 def copy_to_clipboard(self, column_index):
142 item = self.tree.selection()[0]
143 value = self.tree.item(item, "values")[column_index]
144 pyperclip.copy(value)
145
146 def sort_sites(self):
147 items = self.tree.get_children("")
148 if self.sort_order_site == "asc":
149 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[0])
150 self.sort_order_site = "desc"
151 else:
152 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[0], reverse=True)
153 self.sort_order_site = "asc"
154 for item in sorted_items:
155 self.tree.move(item, "", "end")
156
157 def sort_login(self):
158 items = self.tree.get_children("")
159 if self.sort_order_login == "asc":
160 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[2])
161 self.sort_order_login = "desc"
162 else:
163 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[2], reverse=True)
164 self.sort_order_login = "asc"
165 for item in sorted_items:
166 self.tree.move(item, "", "end")
167
168 def show_about_dialog(self):
169 messagebox.showinfo("Sobre", "Filtre os dados por Site ou por Login ou pesquise na caixa de texto.\n\nVersão 5.3 - Última atualização: 24/08/2024, by Mizuno.")
170
171 def quit_app(self):
172 self.conn.close()
173 self.destroy()
174
175 def search_records(self):
176 search_term = self.search_var.get().strip().lower()
177 if not search_term:
178 messagebox.showinfo("Pesquisa", "Digite um termo de pesquisa.")
179 return
180
181 # Limpa a TreeView antes de preencher com os resultados da pesquisa
182 for item in self.tree.get_children():
183 self.tree.delete(item)
184
185 self.cursor.execute("""
186 SELECT site, url, login, senha FROM senhas
187 WHERE lower(site) LIKE ? OR lower(url) LIKE ? OR lower(login) LIKE ?
188 """, ('%' + search_term + '%', '%' + search_term + '%', '%' + search_term + '%'))
189
190 found = False
191 for row in self.cursor.fetchall():
192 self.tree.insert("", "end", values=row)
193 found = True
194
195 if not found:
196 messagebox.showinfo("Pesquisa", "Nenhum registro correspondente encontrado.")
197 self.refresh_tree() # Chama a função de atualização da árvore diretamente após fechar o alerta
198
199 def export_to_pdf(self):
200 file_name = "informacoes_exportadas.pdf"
201 c = canvas.Canvas(file_name, pagesize=landscape(letter)) # Ajuste para paisagem
202 c.setFont("Helvetica", 10)
203
204 # Posição inicial
205 x_position = 40
206 y_position = 550 # Ajustado para a altura da página em paisagem
207
208 # Cabeçalhos das colunas
209 headers = ["Site", "URL", "Login", "Senha"]
210 for header in headers:
211 c.drawString(x_position, y_position, header)
212 x_position += 200 # Ajuste conforme necessário para espaçar as colunas
213
214 y_position -= 20 # Move para a próxima linha
215 x_position = 40
216
217 # Dados dos registros
218 for item in self.tree.get_children():
219 if y_position < 40: # Verifica se a posição Y está próxima do fim da página
220 c.showPage() # Cria uma nova página
221 c.setFont("Helvetica", 10)
222 y_position = 550 # Reinicia a posição Y para a nova página
223 # Redesenha os cabeçalhos na nova página
224 x_position = 40
225 for header in headers:
226 c.drawString(x_position, y_position, header)
227 x_position += 200
228 y_position -= 20
229 x_position = 40
230
231 values = self.tree.item(item, "values")
232 for value in values:
233 c.drawString(x_position, y_position, str(value))
234 x_position += 200 # Ajuste conforme necessário para espaçar as colunas
235 y_position -= 20 # Move para a próxima linha
236 x_position = 40
237
238 c.save()
239 messagebox.showinfo("Exportação de dados", f"Arquivo {file_name} foi gerado com sucesso.")
240
241class AddEntryWindow(tk.Toplevel):
242 def __init__(self, parent):
243 super().__init__(parent)
244 self.title("Adicionar Entrada")
245 self.geometry("400x300")
246 self.create_widgets()
247
248 def create_widgets(self):
249 self.site_label = ttk.Label(self, text="Site:")
250 self.site_label.pack(pady=5)
251 self.site_entry = ttk.Entry(self)
252 self.site_entry.pack(pady=5)
253
254 self.url_label = ttk.Label(self, text="URL:")
255 self.url_label.pack(pady=5)
256 self.url_entry = ttk.Entry(self)
257 self.url_entry.pack(pady=5)
258
259 self.login_label = ttk.Label(self, text="Login:")
260 self.login_label.pack(pady=5)
261 self.login_entry = ttk.Entry(self)
262 self.login_entry.pack(pady=5)
263
264 self.senha_label = ttk.Label(self, text="Senha:")
265 self.senha_label.pack(pady=5)
266 self.senha_entry = ttk.Entry(self)
267 self.senha_entry.pack(pady=5)
268
269 self.save_button = ttk.Button(self, text="Salvar", command=self.save_entry)
270 self.save_button.pack(pady=20)
271
272 def save_entry(self):
273 site = self.site_entry.get().strip()
274 url = self.url_entry.get().strip()
275 login = self.login_entry.get().strip()
276 senha = self.senha_entry.get().strip()
277 if site and url and login and senha:
278 self.master.cursor.execute("INSERT INTO senhas (site, url, login, senha) VALUES (?, ?, ?, ?)",
279 (site, url, login, senha))
280 self.master.conn.commit()
281 self.master.refresh_tree()
282 self.destroy()
283 else:
284 messagebox.showerror("Erro", "Todos os campos devem ser preenchidos.")
285
286class EditEntryWindow(tk.Toplevel):
287 def __init__(self, parent, values):
288 super().__init__(parent)
289 self.title("Editar Entrada")
290 self.geometry("400x300")
291 self.values = values
292 self.create_widgets()
293
294 def create_widgets(self):
295 self.site_label = ttk.Label(self, text="Site:")
296 self.site_label.pack(pady=5)
297 self.site_entry = ttk.Entry(self)
298 self.site_entry.pack(pady=5)
299 self.site_entry.insert(0, self.values[0]) # Inserir valor depois de criar o widget
300
301 self.url_label = ttk.Label(self, text="URL:")
302 self.url_label.pack(pady=5)
303 self.url_entry = ttk.Entry(self)
304 self.url_entry.pack(pady=5)
305 self.url_entry.insert(0, self.values[1]) # Inserir valor depois de criar o widget
306
307 self.login_label = ttk.Label(self, text="Login:")
308 self.login_label.pack(pady=5)
309 self.login_entry = ttk.Entry(self)
310 self.login_entry.pack(pady=5)
311 self.login_entry.insert(0, self.values[2]) # Inserir valor depois de criar o widget
312
313 self.senha_label = ttk.Label(self, text="Senha:")
314 self.senha_label.pack(pady=5)
315 self.senha_entry = ttk.Entry(self)
316 self.senha_entry.pack(pady=5)
317 self.senha_entry.insert(0, self.values[3]) # Inserir valor depois de criar o widget
318
319 self.save_button = ttk.Button(self, text="Salvar", command=self.save_entry)
320 self.save_button.pack(pady=20)
321
322 def save_entry(self):
323 site = self.site_entry.get().strip()
324 url = self.url_entry.get().strip()
325 login = self.login_entry.get().strip()
326 senha = self.senha_entry.get().strip()
327 if site and url and login and senha:
328 selected_item = self.master.tree.selection()[0]
329 self.master.cursor.execute("""
330 UPDATE senhas
331 SET site = ?, url = ?, login = ?, senha = ?
332 WHERE site = ?
333 """, (site, url, login, senha, self.values[0]))
334 self.master.conn.commit()
335 self.master.refresh_tree()
336 self.destroy()
337 else:
338 messagebox.showerror("Erro", "Todos os campos devem ser preenchidos.")
339
340def main():
341 app = Application()
342 app.mainloop()
343
344if __name__ == "__main__":
345 main()
346