Python和PyQt5写的密码记录工具
- 其他
- 2025-09-11 06:33:02

之前总是注册很多网址 依靠浏览器记住密码 或者随便写一个就忘了然后今天借助AI写了个密码管理器 记住密码 和所对应的网址 希望能帮到各位效果图 防止写入太多 增加了搜索 分页 以及超链接 可以直接点击网址就可以跳到对应的网站 右键 账号或者密码可以弹出复制选项 省的点击编辑再去复制了 样式不好看 但是能用 数据会保存到同级目录下有个 passwords.db 数据库
import sys import sqlite3 from PyQt5.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLineEdit, QPushButton, QTableWidget, QTableWidgetItem, QMessageBox, QDialog, QFormLayout, QHeaderView, QCheckBox, QAbstractItemView, QLabel, QComboBox, QMenu ) from PyQt5.QtCore import Qt, QUrl from PyQt5.QtGui import QDesktopServices, QColor, QClipboard class AddPasswordDialog(QDialog): def __init__(self, parent=None, record=None): super().__init__(parent) self.setWindowTitle('添加/编辑记录') self.setModal(True) # 布局 layout = QFormLayout(self) # 输入框 self.website_input = QLineEdit(self) self.website_input.setPlaceholderText('网址') layout.addRow('网址:', self.website_input) self.username_input = QLineEdit(self) self.username_input.setPlaceholderText('用户名') layout.addRow('用户名:', self.username_input) self.password_input = QLineEdit(self) self.password_input.setPlaceholderText('密码') layout.addRow('密码:', self.password_input) self.notes_input = QLineEdit(self) self.notes_input.setPlaceholderText('备注') layout.addRow('备注:', self.notes_input) # 按钮 buttons_layout = QHBoxLayout() save_button = QPushButton('保存', self) save_button.clicked.connect(self.accept) buttons_layout.addWidget(save_button) cancel_button = QPushButton('取消', self) cancel_button.clicked.connect(self.reject) buttons_layout.addWidget(cancel_button) layout.addRow(buttons_layout) # 如果是编辑模式,填充数据 if record: self.website_input.setText(record[1]) self.username_input.setText(record[2]) self.password_input.setText(record[3]) self.notes_input.setText(record[4]) def get_data(self): return ( self.website_input.text(), self.username_input.text(), self.password_input.text(), self.notes_input.text() ) class PasswordManager(QWidget): def __init__(self): super().__init__() self.current_page = 1 # 当前页 self.page_size = 10 # 每页显示的记录数 self.total_pages = 1 # 总页数 self.initUI() def initUI(self): self.setWindowTitle('密码管理器') self.setGeometry(100, 100, 1000, 800) # 设置窗口大小 # 主布局 main_layout = QVBoxLayout() # 顶部布局(搜索框和按钮) top_layout = QHBoxLayout() # 添加按钮(绿色)放在左上角 add_button = QPushButton('添加', self) add_button.setStyleSheet('background-color: green; color: white; font-weight: bold; font-size: 18px; padding: 10px;') add_button.clicked.connect(self.show_add_dialog) top_layout.addWidget(add_button, alignment=Qt.AlignLeft) # 搜索框 self.search_input = QLineEdit(self) self.search_input.setPlaceholderText('搜索(网址、用户名、备注)') self.search_input.textChanged.connect(self.search_passwords) top_layout.addWidget(self.search_input) # 删除按钮(红色)放在右上角 delete_button = QPushButton('删除', self) delete_button.setStyleSheet('background-color: red; color: white; font-weight: bold; font-size: 18px; padding: 10px;') delete_button.clicked.connect(self.delete_password) top_layout.addWidget(delete_button, alignment=Qt.AlignRight) main_layout.addLayout(top_layout) # 密码表格 self.password_table = QTableWidget(self) self.password_table.setColumnCount(6) # 添加一列用于选择框 self.password_table.setHorizontalHeaderLabels(['选择', '网站', '用户名', '密码', '备注', '操作']) self.password_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) # 自适应列宽 self.password_table.setEditTriggers(QTableWidget.NoEditTriggers) # 禁止编辑 self.password_table.cellDoubleClicked.connect(self.open_website) # 双击打开网址 self.password_table.setSelectionBehavior(QAbstractItemView.SelectRows) # 单击行即可选择 self.password_table.setContextMenuPolicy(Qt.CustomContextMenu) # 启用右键菜单 self.password_table.customContextMenuRequested.connect(self.show_context_menu) # 右键菜单事件 main_layout.addWidget(self.password_table) # 底部布局(全选、分页符号、跳转页码、每页条数) bottom_layout = QHBoxLayout() # 全选框放在最左侧 self.select_all_checkbox = QCheckBox('全选', self) self.select_all_checkbox.stateChanged.connect(self.select_all_checkbox_changed) bottom_layout.addWidget(self.select_all_checkbox, alignment=Qt.AlignLeft) # 分页符号和页码 pagination_layout = QHBoxLayout() # 上一页按钮 prev_button = QPushButton('<', self) prev_button.setStyleSheet('font-size: 14px;') prev_button.clicked.connect(self.prev_page) pagination_layout.addWidget(prev_button) # 当前页码和总页数 self.page_info_label = QLabel(f'第 {self.current_page} 页 / {self.total_pages} 页', self) self.page_info_label.setStyleSheet('font-size: 16px; color: #333;') pagination_layout.addWidget(self.page_info_label) # 下一页按钮 next_button = QPushButton('>', self) next_button.setStyleSheet('font-size: 14px;') next_button.clicked.connect(self.next_page) pagination_layout.addWidget(next_button) # 跳转到第几页 self.jump_to_page_input = QLineEdit(self) self.jump_to_page_input.setPlaceholderText('跳转到第几页') self.jump_to_page_input.setFixedWidth(100) pagination_layout.addWidget(self.jump_to_page_input) jump_button = QPushButton('跳转', self) jump_button.setStyleSheet('font-size: 14px;') jump_button.clicked.connect(self.jump_to_page) pagination_layout.addWidget(jump_button) # 每页显示条数 self.page_size_combo = QComboBox(self) self.page_size_combo.addItems(['10', '20', '50', '100']) self.page_size_combo.setCurrentText(str(self.page_size)) self.page_size_combo.currentTextChanged.connect(self.change_page_size) pagination_layout.addWidget(QLabel('每页显示条数:', self)) pagination_layout.addWidget(self.page_size_combo) bottom_layout.addLayout(pagination_layout) main_layout.addLayout(bottom_layout) # 设置主布局 self.setLayout(main_layout) # 加载密码列表 self.load_passwords() def show_add_dialog(self, record=None): dialog = AddPasswordDialog(self, record) if dialog.exec_() == QDialog.Accepted: website, username, password, notes = dialog.get_data() if website and username and password: conn = sqlite3.connect('passwords.db') c = conn.cursor() if record: # 编辑模式 c.execute("UPDATE passwords SET website = ?, username = ?, password = ?, notes = ? WHERE id = ?", (website, username, password, notes, record[0])) else: # 添加模式 c.execute("SELECT id FROM passwords WHERE website = ?", (website,)) if c.fetchone(): QMessageBox.warning(self, '错误', '该网址已存在!') else: c.execute("INSERT INTO passwords (website, username, password, notes) VALUES (?, ?, ?, ?)", (website, username, password, notes)) conn mit() conn.close() self.load_passwords() else: QMessageBox.warning(self, '错误', '请填写网址、用户名和密码') def load_passwords(self, search_keyword=None): self.password_table.setRowCount(0) # 清空表格 # 计算偏移量 offset = (self.current_page - 1) * self.page_size conn = sqlite3.connect('passwords.db') c = conn.cursor() if search_keyword: # 全局搜索,不限制当前页 c.execute(f"SELECT id, website, username, password, notes FROM passwords WHERE website LIKE ? OR username LIKE ? OR notes LIKE ?", (f"%{search_keyword}%", f"%{search_keyword}%", f"%{search_keyword}%")) else: # 分页查询 c.execute(f"SELECT id, website, username, password, notes FROM passwords LIMIT ? OFFSET ?", (self.page_size, offset)) passwords = c.fetchall() # 获取总记录数,用于计算总页数 if search_keyword: # 如果是搜索模式,总页数基于搜索结果 self.total_pages = (len(passwords) + self.page_size - 1) // self.page_size else: # 如果不是搜索模式,总页数基于全部数据 c.execute("SELECT COUNT(*) FROM passwords") total_count = c.fetchone()[0] self.total_pages = (total_count + self.page_size - 1) // self.page_size conn.close() for row, password in enumerate(passwords): self.password_table.insertRow(row) # 选择框列 checkbox = QCheckBox(self) checkbox.setChecked(False) checkbox.setProperty("id", password[0]) # 使用 setProperty 存储ID self.password_table.setCellWidget(row, 0, checkbox) # 网站列(可点击超链接) website_item = QTableWidgetItem(password[1]) website_item.setData(Qt.UserRole, password[1]) # 存储网址 website_item.setForeground(QColor(0, 0, 255)) # 蓝色字体 website_item.setFlags(website_item.flags() | Qt.ItemIsEnabled) # 启用点击 self.password_table.setItem(row, 1, website_item) self.password_table.setItem(row, 2, QTableWidgetItem(password[2])) self.password_table.setItem(row, 3, QTableWidgetItem(password[3])) self.password_table.setItem(row, 4, QTableWidgetItem(password[4])) # 操作列(编辑按钮) edit_button = QPushButton('编辑', self) edit_button.setStyleSheet('background-color: blue; color: white; font-weight: bold;') edit_button.clicked.connect(lambda _, record=password: self.show_add_dialog(record)) self.password_table.setCellWidget(row, 5, edit_button) # 更新页码显示 self.page_info_label.setText(f'第 {self.current_page} 页 / {self.total_pages} 页') def search_passwords(self): search_keyword = self.search_input.text() self.current_page = 1 # 搜索时重置到第一页 self.load_passwords(search_keyword) def delete_password(self): selected_records = [] for row in range(self.password_table.rowCount()): checkbox = self.password_table.cellWidget(row, 0) if checkbox.isChecked(): # 获取选中行的信息 website = self.password_table.item(row, 1).text() username = self.password_table.item(row, 2).text() notes = self.password_table.item(row, 4).text() selected_records.append(f"网站: {website}, 用户名: {username}, 备注: {notes}") if selected_records: confirmation = QMessageBox.question(self, '确认删除', f"确认删除以下记录:\n\n" + "\n".join(selected_records), QMessageBox.Yes | QMessageBox.No) if confirmation == QMessageBox.Yes: conn = sqlite3.connect('passwords.db') c = conn.cursor() for row in range(self.password_table.rowCount()): checkbox = self.password_table.cellWidget(row, 0) if checkbox.isChecked(): password_id = checkbox.property("id") c.execute("DELETE FROM passwords WHERE id = ?", (password_id,)) conn mit() conn.close() self.load_passwords() else: QMessageBox.warning(self, '错误', '请选择一条或多条记录') def select_all_checkbox_changed(self, state): # 全选框改变时,设置所有行的选择框 for row in range(self.password_table.rowCount()): checkbox = self.password_table.cellWidget(row, 0) checkbox.setChecked(state == Qt.Checked) def open_website(self, row, column): if column == 1: # 只有网站列可以点击 website = self.password_table.item(row, column).data(Qt.UserRole) if website: if not website.startswith("http://") and not website.startswith(" "): website = f" {website}" QDesktopServices.openUrl(QUrl(website)) def prev_page(self): if self.current_page > 1: self.current_page -= 1 self.load_passwords() def next_page(self): if self.current_page < self.total_pages: self.current_page += 1 self.load_passwords() def jump_to_page(self): try: page = int(self.jump_to_page_input.text()) if 1 <= page <= self.total_pages: self.current_page = page self.load_passwords() else: QMessageBox.warning(self, '错误', f'页码必须在 1 到 {self.total_pages} 之间!') except ValueError: QMessageBox.warning(self, '错误', '请输入有效的页码!') def change_page_size(self, size): self.page_size = int(size) self.current_page = 1 # 重置到第一页 self.load_passwords() def show_context_menu(self, pos): # 获取当前选中的行和列 row = self.password_table.rowAt(pos.y()) col = self.password_table.columnAt(pos.x()) # 只有用户名(列2)和密码(列3)支持右键复制 if row >= 0 and col in [2, 3]: menu = QMenu(self) # 复制用户名 if col == 2: copy_username_action = menu.addAction('复制用户名') copy_username_action.triggered.connect(lambda: self.copy_to_clipboard(row, 2)) # 复制密码 if col == 3: copy_password_action = menu.addAction('复制密码') copy_password_action.triggered.connect(lambda: self.copy_to_clipboard(row, 3)) # 显示菜单 menu.exec_(self.password_table.viewport().mapToGlobal(pos)) def copy_to_clipboard(self, row, col): # 获取单元格内容 item = self.password_table.item(row, col) if item: clipboard = QApplication.clipboard() clipboard.setText(item.text()) QMessageBox.information(self, '复制成功', f'已复制到剪贴板:{item.text()}') def create_database(): conn = sqlite3.connect('passwords.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS passwords (id INTEGER PRIMARY KEY AUTOINCREMENT, website TEXT NOT NULL, username TEXT NOT NULL, password TEXT NOT NULL, notes TEXT)''') conn mit() conn.close() if __name__ == '__main__': create_database() app = QApplication(sys.argv) manager = PasswordManager() manager.show() sys.exit(app.exec_())Python和PyQt5写的密码记录工具由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Python和PyQt5写的密码记录工具”