dev: 初步支持查询过滤

This commit is contained in:
Julian Freeman
2024-07-19 11:07:30 -04:00
parent aca88dd952
commit aa4d88b980
6 changed files with 164 additions and 15 deletions

View File

@@ -11,6 +11,7 @@ columns_d = {
"notes": Column("notes", DataType.BLOB), "notes": Column("notes", DataType.BLOB),
"path": Column("path", DataType.BLOB, nullable=False), "path": Column("path", DataType.BLOB, nullable=False),
} }
all_columns = [ all_columns = [
columns_d["entry_id"], columns_d["entry_id"],
columns_d["title"], columns_d["title"],
@@ -21,3 +22,10 @@ all_columns = [
columns_d["notes"], columns_d["notes"],
columns_d["path"], columns_d["path"],
] ]
query_columns = [
columns_d["entry_id"],
columns_d["title"],
columns_d["username"],
columns_d["url"],
]

4
lib/global_config.py Normal file
View File

@@ -0,0 +1,4 @@
# coding: utf8
table_name = "entries"
button_min_width = 120

View File

@@ -2,6 +2,7 @@
from os import PathLike from os import PathLike
from pykeepass import PyKeePass from pykeepass import PyKeePass
from .Sqlite3Helper import Sqlite3Worker, BlobType, Column from .Sqlite3Helper import Sqlite3Worker, BlobType, Column
from .global_config import table_name
def trim_str(value): def trim_str(value):
@@ -40,8 +41,8 @@ def read_kps_to_db(kps_file: str | PathLike[str], password: str,
extract_otp(entry.otp), extract_otp(entry.otp),
blob_fy(trim_str(entry.url)), blob_fy(trim_str(entry.url)),
blob_fy(entry.notes), blob_fy(entry.notes),
blob_fy("::".join(entry.path[:-1])), blob_fy("::".join([kps_file] + entry.path[:-1])),
]) ])
sqh.insert_into("entries", columns, values) sqh.insert_into(table_name, columns, values)
return kp return kp

View File

@@ -1,14 +1,15 @@
# coding: utf8 # coding: utf8
import os import os
import sys import sys
from datetime import datetime
from pathlib import Path from pathlib import Path
from PySide6 import QtWidgets, QtCore, QtGui from PySide6 import QtWidgets, QtCore, QtGui
from .page_load import PageLoad from .page_load import PageLoad
from .page_query import PageQuery
from .cmbx_styles import StyleComboBox from .cmbx_styles import StyleComboBox
from lib.Sqlite3Helper import Sqlite3Worker from lib.Sqlite3Helper import Sqlite3Worker
from lib.db_columns_def import all_columns from lib.db_columns_def import all_columns
from lib.global_config import table_name
def get_default_db_path() -> str: def get_default_db_path() -> str:
@@ -25,8 +26,7 @@ def get_default_db_path() -> str:
if not app_dir.exists(): if not app_dir.exists():
app_dir.mkdir(parents=True, exist_ok=True) app_dir.mkdir(parents=True, exist_ok=True)
now_s = datetime.now().strftime("%Y%m%d%H%M%S") return str(app_dir / f"default.db")
return str(app_dir / f"{now_s}.db")
class UiKpsUnifier(object): class UiKpsUnifier(object):
@@ -63,11 +63,12 @@ class UiKpsUnifier(object):
self.page_load = PageLoad(sqh, self.cw) self.page_load = PageLoad(sqh, self.cw)
self.sw_m.addWidget(self.page_load) self.sw_m.addWidget(self.page_load)
self.page_query = QtWidgets.QWidget(self.cw) self.page_query = PageQuery(sqh, self.cw)
self.sw_m.addWidget(self.page_query) self.sw_m.addWidget(self.page_query)
def update_sqh(self, sqh: Sqlite3Worker): def update_sqh(self, sqh: Sqlite3Worker):
self.page_load.update_sqh(sqh) self.page_load.update_sqh(sqh)
self.page_query.update_sqh(sqh)
class KpsUnifier(QtWidgets.QMainWindow): class KpsUnifier(QtWidgets.QMainWindow):
@@ -88,7 +89,7 @@ class KpsUnifier(QtWidgets.QMainWindow):
def init_db(self) -> Sqlite3Worker: def init_db(self) -> Sqlite3Worker:
sqh = Sqlite3Worker(self.db_path) sqh = Sqlite3Worker(self.db_path)
sqh.create_table("entries", all_columns, if_not_exists=True) sqh.create_table(table_name, all_columns, if_not_exists=True)
return sqh return sqh
def update_db(self, filename: str): def update_db(self, filename: str):

View File

@@ -6,6 +6,7 @@ from .gbx_kps_login import GbxKpsLogin
from lib.Sqlite3Helper import Sqlite3Worker from lib.Sqlite3Helper import Sqlite3Worker
from lib.db_columns_def import all_columns from lib.db_columns_def import all_columns
from lib.kps_operations import read_kps_to_db from lib.kps_operations import read_kps_to_db
from lib.global_config import button_min_width
class WgLoadKps(QtWidgets.QWidget): class WgLoadKps(QtWidgets.QWidget):
@@ -28,6 +29,7 @@ class WgLoadKps(QtWidgets.QWidget):
def on_item_pbn_remove_clicked(self, item: GbxKpsLogin): def on_item_pbn_remove_clicked(self, item: GbxKpsLogin):
self.vly_m.removeWidget(item) self.vly_m.removeWidget(item)
self.kps_wgs.remove(item)
item.deleteLater() item.deleteLater()
def on_item_pbn_load_clicked(self, item: GbxKpsLogin): def on_item_pbn_load_clicked(self, item: GbxKpsLogin):
@@ -58,19 +60,19 @@ class PageLoad(QtWidgets.QWidget):
self.hly_m.addLayout(self.vly_left) self.hly_m.addLayout(self.vly_left)
self.pbn_add = QtWidgets.QPushButton("添加", self) self.pbn_add = QtWidgets.QPushButton("添加", self)
self.pbn_add.setMinimumWidth(button_min_width)
self.vly_left.addWidget(self.pbn_add) self.vly_left.addWidget(self.pbn_add)
self.pbn_load_all = QtWidgets.QPushButton("加载全部", self) self.pbn_load_all = QtWidgets.QPushButton("加载全部", self)
self.pbn_load_all.setMinimumWidth(button_min_width)
self.pbn_load_all.setDisabled(True)
self.vly_left.addWidget(self.pbn_load_all) self.vly_left.addWidget(self.pbn_load_all)
self.vly_left.addStretch(1) self.vly_left.addStretch(1)
self.sa = QtWidgets.QScrollArea(self) self.sa_m = QtWidgets.QScrollArea(self)
self.sa.setWidgetResizable(True) self.sa_m.setWidgetResizable(True)
self.hly_m.addWidget(self.sa) self.hly_m.addWidget(self.sa_m)
self.wg_sa = WgLoadKps(sqh, self.sa) self.wg_sa = WgLoadKps(sqh, self.sa_m)
self.sa.setWidget(self.wg_sa) self.sa_m.setWidget(self.wg_sa)
self.hly_m.setStretchFactor(self.vly_left, 1)
self.hly_m.setStretchFactor(self.sa, 6)
self.pbn_add.clicked.connect(self.on_pbn_add_clicked) self.pbn_add.clicked.connect(self.on_pbn_add_clicked)

133
src/page_query.py Normal file
View File

@@ -0,0 +1,133 @@
# coding: utf8
import json
from PySide6 import QtWidgets, QtCore, QtGui
from lib.Sqlite3Helper import Sqlite3Worker, Operand, Expression
from lib.db_columns_def import query_columns
from lib.global_config import button_min_width, table_name
class QueryTableModel(QtCore.QAbstractTableModel):
def __init__(self, query_results: list[tuple], parent=None):
super().__init__(parent)
self.query_results = query_results
self.headers = ["序号", "标题", "用户名", "URL"]
def rowCount(self, parent: QtCore.QModelIndex = ...):
return len(self.query_results)
def columnCount(self, parent: QtCore.QModelIndex = ...):
return len(self.headers)
def data(self, index: QtCore.QModelIndex, role: int = ...):
if role == QtCore.Qt.ItemDataRole.DisplayRole:
item = self.query_results[index.row()][index.column()]
if isinstance(item, bytes):
return item.decode("utf-8")
return item
def headerData(self, section: int, orientation: QtCore.Qt.Orientation, role: int = ...):
if orientation == QtCore.Qt.Orientation.Horizontal:
if role == QtCore.Qt.ItemDataRole.DisplayRole:
return self.headers[section]
class PageQuery(QtWidgets.QWidget):
def __init__(self, sqh: Sqlite3Worker, parent=None):
super().__init__(parent)
self.sqh = sqh
self.hly_m = QtWidgets.QHBoxLayout()
self.setLayout(self.hly_m)
self.sa_left = QtWidgets.QScrollArea(self)
self.sa_left.setWidgetResizable(True)
self.sa_left.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.sa_left.setSizePolicy(QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Ignored)
self.hly_m.addWidget(self.sa_left)
self.sa_wg = QtWidgets.QWidget(self.sa_left)
self.vly_sa_wg = QtWidgets.QVBoxLayout()
self.sa_wg.setLayout(self.vly_sa_wg)
self.sa_left.setWidget(self.sa_wg)
self.pbn_all = QtWidgets.QPushButton("全部", self.sa_wg)
self.pbn_all.setMinimumWidth(button_min_width)
self.vly_sa_wg.addWidget(self.pbn_all)
self.vly_sa_wg.addStretch(1)
self.pbn_read_filters = QtWidgets.QPushButton("更多过滤", self)
self.pbn_read_filters.setMinimumWidth(button_min_width)
self.vly_sa_wg.addWidget(self.pbn_read_filters)
self.trv_m = QtWidgets.QTreeView(self)
# self.trv_m.setSortingEnabled(True)
self.hly_m.addWidget(self.trv_m)
self.pbn_all.clicked.connect(self.on_pbn_all_clicked)
self.pbn_read_filters.clicked.connect(self.on_pbn_read_filters_clicked)
self.set_default_filters()
def set_default_filters(self):
default_filters = [
{
"name": "Gmail 邮箱",
"where": "username LIKE '%@gmail.com'"
},
{
"name": "Outlook 邮箱",
"where": "username LIKE '%@outlook.com'"
},
{
"name": "谷歌文档",
"where": "url LIKE 'https://docs.google.com/%' or url LIKE 'https://drive.google.com/%'"
},
]
for fil in default_filters:
self.set_filter_button(fil)
def set_filter_button(self, fil: dict):
pbn_fil = PushButtonWithData(fil, self.sa_wg, fil["name"])
pbn_fil.setMinimumWidth(button_min_width)
self.vly_sa_wg.insertWidget(self.vly_sa_wg.count() - 2, pbn_fil)
pbn_fil.clicked_with_data.connect(self.on_custom_filters_clicked_with_data)
def on_pbn_read_filters_clicked(self):
filename, _ = QtWidgets.QFileDialog.getOpenFileName(self, "打开", "../",
filter="JSON 文件 (*.json);;所有文件 (*)")
if len(filename) == 0:
return
with open(filename, "r", encoding="utf8") as f:
filter_ls: list[dict] = json.load(f)
for fil in filter_ls:
self.set_filter_button(fil)
def on_custom_filters_clicked_with_data(self, data: dict):
_, results = self.sqh.select(table_name, query_columns, where=Expression(data["where"]))
model = QueryTableModel(results, self)
self.trv_m.setModel(model)
def update_sqh(self, sqh: Sqlite3Worker):
self.sqh = sqh
def on_pbn_all_clicked(self):
_, results = self.sqh.select(table_name, query_columns)
model = QueryTableModel(results, self)
self.trv_m.setModel(model)
class PushButtonWithData(QtWidgets.QPushButton):
clicked_with_data = QtCore.Signal(dict)
def __init__(self, data: dict, parent: QtWidgets.QWidget = None, title: str = ""):
super().__init__(title, parent)
self.data = data
self.clicked.connect(self.on_self_clicked)
def on_self_clicked(self):
self.clicked_with_data.emit(self.data)