dev: 初步支持查询过滤
This commit is contained in:
@@ -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
4
lib/global_config.py
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# coding: utf8
|
||||||
|
|
||||||
|
table_name = "entries"
|
||||||
|
button_min_width = 120
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
133
src/page_query.py
Normal 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)
|
||||||
Reference in New Issue
Block a user