· 10 months ago · Nov 15, 2024, 03:40 AM
1import tkinter as tk
2from tkinter import ttk, messagebox, filedialog
3import sqlite3
4import pyperclip
5import webbrowser
6from reportlab.lib.pagesizes import letter, landscape
7from reportlab.pdfgen import canvas
8import shutil
9
10class Application(tk.Tk):
11 def __init__(self):
12 super().__init__()
13 self.title("Gerenciador de Senhas de Sites - Versão 5.6")
14 self.geometry("1000x600")
15 self.sort_order_site = "asc"
16 self.sort_order_login = "asc"
17 self.init_db()
18 self.create_widgets()
19 self.create_menu()
20 self.date_label = ttk.Label(self, text="Mizuno - 02/11/2024")
21 self.date_label.place(x=10, y=self.winfo_height() - 30)
22 self.center_window()
23
24 def create_widgets(self):
25 self.search_var = tk.StringVar()
26 self.search_entry = ttk.Entry(self, textvariable=self.search_var, width=50)
27 self.search_entry.pack(pady=(0, 10))
28
29 # Vincula o evento KeyRelease para busca em tempo real
30 self.search_entry.bind("<KeyRelease>", lambda event: self.search_records())
31
32 self.search_button = ttk.Button(self, text="Pesquisar", command=self.search_records)
33 self.search_button.pack(pady=(0, 10))
34
35 # Botão Atualizar com o mesmo comportamento do código 1
36 self.refresh_button = ttk.Button(self, text="Atualizar", command=self.refresh_tree)
37 self.refresh_button.pack(pady=(0, 10))
38
39 self.tree = ttk.Treeview(self, columns=("Site", "URL", "Login", "Senha"), show="headings")
40 self.tree.heading("Site", text="Site", command=self.sort_sites)
41 self.tree.heading("URL", text="URL")
42 self.tree.heading("Login", text="Login", command=self.sort_login)
43 self.tree.heading("Senha", text="Senha")
44 self.tree.pack(pady=10, side="left", fill="both", expand=True)
45
46 scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.tree.yview)
47 scrollbar.pack(side="right", fill="y")
48 self.tree.configure(yscrollcommand=scrollbar.set)
49
50 self.add_button = ttk.Button(self, text="Adicionar", command=self.add_entry)
51 self.add_button.pack()
52 self.edit_button = ttk.Button(self, text="Editar", command=self.edit_entry)
53 self.edit_button.pack()
54 self.delete_button = ttk.Button(self, text="Excluir", command=self.confirm_delete)
55 self.delete_button.pack()
56 self.pdf_button = ttk.Button(self, text="Exportar PDF", command=self.export_to_pdf)
57 self.pdf_button.pack()
58
59 # Botões de Backup e Restauração
60 self.backup_button = ttk.Button(self, text="Fazer Backup", command=self.backup_data)
61 self.backup_button.pack()
62 self.restore_button = ttk.Button(self, text="Restaurar Backup", command=self.restore_data)
63 self.restore_button.pack()
64
65 self.load_data()
66 self.tree.bind("<Button-3>", self.popup_menu)
67 self.tree.bind("<Double-1>", self.open_url)
68
69 def create_menu(self):
70 self.menu_bar = tk.Menu(self)
71 self.file_menu = tk.Menu(self.menu_bar, tearoff=0)
72 self.file_menu.add_command(label="Sobre", command=self.show_about_dialog)
73 self.file_menu.add_separator()
74 self.file_menu.add_command(label="Sair", command=self.quit_app)
75 self.menu_bar.add_cascade(label="Arquivo", menu=self.file_menu)
76 self.config(menu=self.menu_bar)
77
78 def open_url(self, event):
79 if self.tree.selection():
80 item = self.tree.selection()[0]
81 url = self.tree.item(item, "values")[1]
82 webbrowser.open(url)
83
84 def center_window(self):
85 self.update_idletasks()
86 width = self.winfo_width()
87 height = self.winfo_height()
88 x = (self.winfo_screenwidth() - width) // 2
89 y = (self.winfo_screenheight() - height) // 2
90 self.geometry(f'{width}x{height}+{x}+{y}')
91
92 def init_db(self):
93 self.conn = sqlite3.connect("dados.db")
94 self.cursor = self.conn.cursor()
95 self.cursor.execute("""
96 CREATE TABLE IF NOT EXISTS senhas (
97 id INTEGER PRIMARY KEY AUTOINCREMENT,
98 site TEXT,
99 url TEXT,
100 login TEXT,
101 senha TEXT
102 )
103 """)
104 self.conn.commit()
105
106 def load_data(self):
107 self.cursor.execute("SELECT site, url, login, senha FROM senhas")
108 for row in self.cursor.fetchall():
109 self.tree.insert("", "end", values=row)
110
111 def save_data(self):
112 self.cursor.execute("DELETE FROM senhas")
113 for item in self.tree.get_children():
114 values = self.tree.item(item, "values")
115 self.cursor.execute("INSERT INTO senhas (site, url, login, senha) VALUES (?, ?, ?, ?)", values)
116 self.conn.commit()
117
118 def refresh_tree(self):
119 for item in self.tree.get_children():
120 self.tree.delete(item)
121 self.load_data()
122
123 def add_entry(self):
124 AddEntryWindow(self)
125
126 def edit_entry(self):
127 try:
128 item = self.tree.selection()[0]
129 values = self.tree.item(item, "values")
130 EditEntryWindow(self, values)
131 except IndexError:
132 messagebox.showerror("Erro", "Selecione um registro para editar.")
133
134 def delete_entry(self):
135 selected_item = self.tree.selection()[0]
136 site = self.tree.item(selected_item, "values")[0]
137 self.cursor.execute("DELETE FROM senhas WHERE site=?", (site,))
138 self.conn.commit()
139 self.tree.delete(selected_item)
140
141 def confirm_delete(self):
142 try:
143 if messagebox.askyesno("Confirmar", "Tem certeza que deseja excluir este registro?"):
144 self.delete_entry()
145 except IndexError:
146 messagebox.showerror("Erro", "Selecione um registro para excluir.")
147
148 def popup_menu(self, event):
149 popup_menu = tk.Menu(self, tearoff=0)
150 popup_menu.add_command(label="Copiar URL", command=lambda: self.copy_to_clipboard(1))
151 popup_menu.add_command(label="Copiar Login", command=lambda: self.copy_to_clipboard(2))
152 popup_menu.add_command(label="Copiar Senha", command=lambda: self.copy_to_clipboard(3))
153 popup_menu.post(event.x_root, event.y_root)
154
155 def copy_to_clipboard(self, column_index):
156 item = self.tree.selection()[0]
157 value = self.tree.item(item, "values")[column_index]
158 pyperclip.copy(value)
159
160 def sort_sites(self):
161 items = self.tree.get_children("")
162 if self.sort_order_site == "asc":
163 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[0])
164 self.sort_order_site = "desc"
165 else:
166 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[0], reverse=True)
167 self.sort_order_site = "asc"
168 for item in sorted_items:
169 self.tree.move(item, "", "end")
170
171 def sort_login(self):
172 items = self.tree.get_children("")
173 if self.sort_order_login == "asc":
174 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[2])
175 self.sort_order_login = "desc"
176 else:
177 sorted_items = sorted(items, key=lambda x: self.tree.item(x, "values")[2], reverse=True)
178 self.sort_order_login = "asc"
179 for item in sorted_items:
180 self.tree.move(item, "", "end")
181
182 def show_about_dialog(self):
183 messagebox.showinfo("Sobre", "Filtre os dados por Site ou por Login ou pesquise na caixa de texto.\n\nVersão 5.6 - Última atualização: 15/11/2024, Mizuno.")
184
185 def quit_app(self):
186 self.conn.close()
187 self.destroy()
188
189 def search_records(self):
190 search_term = self.search_var.get().strip().lower()
191
192 # Limpa a TreeView antes de preencher com os resultados da pesquisa
193 for item in self.tree.get_children():
194 self.tree.delete(item)
195
196 # Se não houver termo de pesquisa, exibe todos os registros
197 if not search_term:
198 self.load_data()
199 return
200
201 # Realiza a consulta de acordo com o termo de pesquisa
202 self.cursor.execute("""
203 SELECT site, url, login, senha FROM senhas
204 WHERE lower(site) LIKE ? OR lower(url) LIKE ? OR lower(login) LIKE ?
205 """, ('%' + search_term + '%', '%' + search_term + '%', '%' + search_term + '%'))
206
207 for row in self.cursor.fetchall():
208 self.tree.insert("", "end", values=row)
209
210 def export_to_pdf(self):
211 file_name = "informacoes_exportadas.pdf"
212 c = canvas.Canvas(file_name, pagesize=landscape(letter))
213 c.setFont("Helvetica", 10)
214
215 x_position = 40
216 y_position = 550
217
218 headers = ["Site", "URL", "Login", "Senha"]
219 for header in headers:
220 c.drawString(x_position, y_position, header)
221 x_position += 200
222
223 y_position -= 20
224 x_position = 40
225
226 for item in self.tree.get_children():
227 if y_position < 40:
228 c.showPage()
229 c.setFont("Helvetica", 10)
230 y_position = 550
231 x_position = 40
232 for header in headers:
233 c.drawString(x_position, y_position, header)
234 x_position += 200
235 y_position -= 20
236 x_position = 40
237
238 values = self.tree.item(item, "values")
239 for value in values:
240 c.drawString(x_position, y_position, str(value))
241 x_position += 200
242 y_position -= 20
243 x_position = 40
244
245 c.save()
246 messagebox.showinfo("Exportação de dados", f"Arquivo {file_name} foi gerado com sucesso.")
247
248 def backup_data(self):
249 backup_file = filedialog.asksaveasfilename(defaultextension=".db", filetypes=[("Database files", "*.db")])
250 if backup_file:
251 try:
252 shutil.copy("dados.db", backup_file)
253 messagebox.showinfo("Backup", "Backup realizado com sucesso.")
254 except Exception as e:
255 messagebox.showerror("Erro", f"Erro ao fazer backup: {e}")
256
257 def restore_data(self):
258 restore_file = filedialog.askopenfilename(filetypes=[("Database files", "*.db")])
259 if restore_file:
260 try:
261 shutil.copy(restore_file, "dados.db")
262 self.conn.close() # Fecha a conexão para recarregar os dados restaurados
263 self.conn = sqlite3.connect("dados.db")
264 self.cursor = self.conn.cursor()
265 self.refresh_tree()
266 messagebox.showinfo("Restauração", "Restauração realizada com sucesso.")
267 except Exception as e:
268 messagebox.showerror("Erro", f"Erro ao restaurar backup: {e}")
269
270class AddEntryWindow(tk.Toplevel):
271 def __init__(self, parent):
272 super().__init__(parent)
273 self.title("Adicionar Entrada")
274 self.geometry("400x300")
275 self.create_widgets()
276 self.center_window()
277
278 def create_widgets(self):
279 self.site_label = ttk.Label(self, text="Site:")
280 self.site_label.pack(pady=5)
281 self.site_entry = ttk.Entry(self, width=50)
282 self.site_entry.pack(pady=5)
283
284 self.url_label = ttk.Label(self, text="URL:")
285 self.url_label.pack(pady=5)
286 self.url_entry = ttk.Entry(self, width=50)
287 self.url_entry.pack(pady=5)
288
289 self.login_label = ttk.Label(self, text="Login:")
290 self.login_label.pack(pady=5)
291 self.login_entry = ttk.Entry(self, width=50)
292 self.login_entry.pack(pady=5)
293
294 self.senha_label = ttk.Label(self, text="Senha:")
295 self.senha_label.pack(pady=5)
296 self.senha_entry = ttk.Entry(self, width=50)
297 self.senha_entry.pack(pady=5)
298
299 self.save_button = ttk.Button(self, text="Salvar", command=self.save_entry)
300 self.save_button.pack(pady=20)
301
302 def center_window(self):
303 self.update_idletasks()
304 width = self.winfo_width()
305 height = self.winfo_height()
306 x = (self.winfo_screenwidth() - width) // 2
307 y = (self.winfo_screenheight() - height) // 2
308 self.geometry(f'{width}x{height}+{x}+{y}')
309
310 def save_entry(self):
311 site = self.site_entry.get().strip()
312 url = self.url_entry.get().strip()
313 login = self.login_entry.get().strip()
314 senha = self.senha_entry.get().strip()
315 if site and url and login and senha:
316 self.master.cursor.execute("INSERT INTO senhas (site, url, login, senha) VALUES (?, ?, ?, ?)",
317 (site, url, login, senha))
318 self.master.conn.commit()
319 self.master.refresh_tree()
320 self.destroy()
321 else:
322 messagebox.showerror("Erro", "Todos os campos devem ser preenchidos.")
323
324class EditEntryWindow(tk.Toplevel):
325 def __init__(self, parent, values):
326 super().__init__(parent)
327 self.title("Editar Entrada")
328 self.geometry("400x300")
329 self.values = values
330 self.create_widgets()
331 self.center_window()
332
333 def create_widgets(self):
334 self.site_label = ttk.Label(self, text="Site:")
335 self.site_label.pack(pady=5)
336 self.site_entry = ttk.Entry(self, width=50)
337 self.site_entry.pack(pady=5)
338 self.site_entry.insert(0, self.values[0])
339
340 self.url_label = ttk.Label(self, text="URL:")
341 self.url_label.pack(pady=5)
342 self.url_entry = ttk.Entry(self, width=50)
343 self.url_entry.pack(pady=5)
344 self.url_entry.insert(0, self.values[1])
345
346 self.login_label = ttk.Label(self, text="Login:")
347 self.login_label.pack(pady=5)
348 self.login_entry = ttk.Entry(self, width=50)
349 self.login_entry.pack(pady=5)
350 self.login_entry.insert(0, self.values[2])
351
352 self.senha_label = ttk.Label(self, text="Senha:")
353 self.senha_label.pack(pady=5)
354 self.senha_entry = ttk.Entry(self, width=50)
355 self.senha_entry.pack(pady=5)
356 self.senha_entry.insert(0, self.values[3])
357
358 self.save_button = ttk.Button(self, text="Salvar", command=self.save_entry)
359 self.save_button.pack(pady=20)
360
361 def center_window(self):
362 self.update_idletasks()
363 width = self.winfo_width()
364 height = self.winfo_height()
365 x = (self.winfo_screenwidth() - width) // 2
366 y = (self.winfo_screenheight() - height) // 2
367 self.geometry(f'{width}x{height}+{x}+{y}')
368
369 def save_entry(self):
370 site = self.site_entry.get().strip()
371 url = self.url_entry.get().strip()
372 login = self.login_entry.get().strip()
373 senha = self.senha_entry.get().strip()
374 if site and url and login and senha:
375 selected_item = self.master.tree.selection()[0]
376 self.master.cursor.execute("""
377 UPDATE senhas
378 SET site = ?, url = ?, login = ?, senha = ?
379 WHERE site = ?
380 """, (site, url, login, senha, self.values[0]))
381 self.master.conn.commit()
382 self.master.refresh_tree()
383 self.destroy()
384 else:
385 messagebox.showerror("Erro", "Todos os campos devem ser preenchidos.")
386
387def main():
388 app = Application()
389 app.mainloop()
390
391if __name__ == "__main__":
392 main()
393