Compare commits
5 Commits
bb3ae1e65a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 062b1e24e9 | |||
| ed947743fc | |||
| 943130b875 | |||
| 1c47497fc2 | |||
| c7c0d15545 |
3
.gitignore
vendored
@@ -3,4 +3,5 @@
|
|||||||
rokae/testbench.py
|
rokae/testbench.py
|
||||||
**/__pycache__/
|
**/__pycache__/
|
||||||
NOTRACK/
|
NOTRACK/
|
||||||
gui/assets/database/
|
toolbox/assets/database/
|
||||||
|
toolbox/output/
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"ip_addr": "192.168.0.160",
|
"ip_addr": "192.168.2.160",
|
||||||
"ssh_port": "22",
|
"ssh_port": "22",
|
||||||
"socket_port": 5050,
|
"socket_port": 5050,
|
||||||
"xService_port": 6666,
|
"xService_port": 6666,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"minimum_password_length": 8,
|
"minimum_password_length": 8,
|
||||||
"maximum_db_number": 10,
|
"maximum_db_number": 10,
|
||||||
|
"log_number_per_page": 30,
|
||||||
"database_path": "",
|
"database_path": "",
|
||||||
"api_ali_pay": "",
|
"api_ali_pay": "",
|
||||||
"api_wechat_pay": ""
|
"api_wechat_pay": ""
|
||||||
|
|||||||
27
toolbox/assets/conf/qss/home_overlay.qss
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
LunarClockLabel#lb_time {
|
||||||
|
color: rgba(255,255,255,255);
|
||||||
|
font: 96px "Arial Black";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
LunarClockLabel#lb_date {
|
||||||
|
color: rgba(255,255,255,255);
|
||||||
|
font: 18px "Consolas";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel#lb_name {
|
||||||
|
color: rgba(255,255,255,255);
|
||||||
|
}
|
||||||
|
|
||||||
|
QFrame#line_left,
|
||||||
|
QFrame#line_right {
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(255, 255, 255, 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel#lb_proverb {
|
||||||
|
color: rgba(255,255,255,255);
|
||||||
|
font: 20px "Consolas";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
92
toolbox/assets/conf/qss/list_widget.qss
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/* ------ 整体画布 ------ */
|
||||||
|
QListWidget#SListWidget {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #d0d7de;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
color: #24292f; /* 默认文字色 */
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 18px;
|
||||||
|
gridline-color: transparent; /* 网格线 */
|
||||||
|
/* 交替行颜色(需代码 setAlternatingRowColors(true)) */
|
||||||
|
alternate-background-color: #f6f8fa;
|
||||||
|
/* 滚动条圆角背景 */
|
||||||
|
background-clip: padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 单项(行) ------ */
|
||||||
|
QListWidget#SListWidget::item {
|
||||||
|
height: 36px; /* 固定行高 */
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 8px;
|
||||||
|
border: none;
|
||||||
|
/* 行内文字对齐方式 */
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中态 */
|
||||||
|
QListWidget#SListWidget::item:selected {
|
||||||
|
background: #0969da;
|
||||||
|
color: #ffffff;
|
||||||
|
border-left: 0px solid #0550ae; /* 左侧高亮条 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停态(未选中) */
|
||||||
|
QListWidget#SListWidget::item:hover:!selected {
|
||||||
|
background: rgba(9, 105, 218, 12%);
|
||||||
|
border-left: 4px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 禁用态 */
|
||||||
|
QListWidget#SListWidget::item:disabled {
|
||||||
|
color: #8c959f;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 图标区域 ------ */
|
||||||
|
QListWidget#SListWidget::icon {
|
||||||
|
padding-right: 8px; /* 图标与文字间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 滚动条(仅影响 #SListWidget) ------ */
|
||||||
|
QListWidget#SListWidget QScrollBar:vertical {
|
||||||
|
width: 10px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::handle:vertical {
|
||||||
|
background: #c0c6cc;
|
||||||
|
border-radius: 5px;
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::handle:vertical:hover {
|
||||||
|
background: #a0a6ac;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::add-line:vertical,
|
||||||
|
QListWidget#SListWidget QScrollBar::sub-line:vertical {
|
||||||
|
height: 0; /* 隐藏上下箭头 */
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget#SListWidget QScrollBar:horizontal {
|
||||||
|
height: 10px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::handle:horizontal {
|
||||||
|
background: #c0c6cc;
|
||||||
|
border-radius: 5px;
|
||||||
|
min-width: 30px;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::handle:horizontal:hover {
|
||||||
|
background: #a0a6ac;
|
||||||
|
}
|
||||||
|
QListWidget#SListWidget QScrollBar::add-line:horizontal,
|
||||||
|
QListWidget#SListWidget QScrollBar::sub-line:horizontal {
|
||||||
|
width: 0; /* 隐藏左右箭头 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 拖拽插入指示器(可选) ------ */
|
||||||
|
QListWidget#SListWidget::drop-indicator {
|
||||||
|
background: #0969da;
|
||||||
|
width: 2px;
|
||||||
|
}
|
||||||
6
toolbox/assets/conf/qss/statusbar.qss
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
QStatusBar#SStatusBar {
|
||||||
|
background: #8B8989; /* 背景色 */
|
||||||
|
color: #000000; /* 文字色 */
|
||||||
|
border: none;
|
||||||
|
font: 16px "Consolas";
|
||||||
|
}
|
||||||
49
toolbox/assets/conf/qss/toolbar.qss
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
QToolBar#SToolBar {
|
||||||
|
background: #eef1f5; /* 背景色 */
|
||||||
|
border: none; /* 去掉默认边框 */
|
||||||
|
border-bottom: 2px solid #d0d7de;/* 底部分隔线 */
|
||||||
|
spacing: 2px; /* 图标/按钮间距 */
|
||||||
|
padding: 2px 4px; /* 内边距:上下/左右 */
|
||||||
|
min-height: 20px; /* 最小高度 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 工具栏里所有 QToolButton(图标按钮) */
|
||||||
|
QToolBar#SToolBar QToolButton {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #24292f;
|
||||||
|
font: 18px "Consolas";
|
||||||
|
padding: 0px 0px;
|
||||||
|
margin: 0px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 悬停 */
|
||||||
|
QToolBar#SToolBar QToolButton:hover {
|
||||||
|
background: rgba(9, 105, 218, 12%);
|
||||||
|
border-color: #0969da;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按下/选中 */
|
||||||
|
QToolBar#SToolBar QToolButton:pressed,
|
||||||
|
QToolBar#SToolBar QToolButton:checked {
|
||||||
|
background: #0969da;
|
||||||
|
color: #ffffff;
|
||||||
|
border-color: #0550ae;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 仅工具栏里 objectName = ac_switch 的按钮 */
|
||||||
|
QToolBar#SToolBar QToolButton#ac_switch {
|
||||||
|
background: #eef1f5;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: #fff;
|
||||||
|
font: 14px "Microsoft YaHei";
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
QToolBar#SToolBar QToolButton#ac_switch:hover {
|
||||||
|
background: #eef1f5;
|
||||||
|
}
|
||||||
|
QToolBar#toolBar QToolButton#ac_switch:pressed {
|
||||||
|
background: #0550ae;
|
||||||
|
}
|
||||||
121
toolbox/assets/conf/qss/w08_log.qss
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
/* ------ 整体画布 ------ */
|
||||||
|
QTreeWidget#treeW {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #d0d7de;
|
||||||
|
border-radius: 8px;
|
||||||
|
outline: none;
|
||||||
|
color: #24292f; /* 默认文字色 */
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 16px;
|
||||||
|
gridline-color: #e1e4e8; /* 网格线 */
|
||||||
|
show-decoration-selected: 1; /* 整行选中 */
|
||||||
|
background-clip: padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 表头 ------ */
|
||||||
|
QHeaderView#header {
|
||||||
|
background: #eef1f5;
|
||||||
|
border-left: 1px solid #d0d7de;
|
||||||
|
border-bottom: 1px solid #d0d7de;
|
||||||
|
border-right: none; /* 最右列去线 */
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #57606a;
|
||||||
|
}
|
||||||
|
QHeaderView#header:section {
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
QHeaderView#header:section:hover {
|
||||||
|
background: rgba(9, 105, 218, 12%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 行(item) ------ */
|
||||||
|
QTreeWidget#treeW::item {
|
||||||
|
height: 25px; /* 固定行高 */
|
||||||
|
padding-left: 8px;
|
||||||
|
/*border-right: 1px solid #e1e4e8;
|
||||||
|
border-bottom: 1px solid #e1e4e8;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidget#treeW::item:selected {
|
||||||
|
background: #0969da;
|
||||||
|
color: #ffffff;
|
||||||
|
border-left: 0px solid #0550ae; /* 左侧高亮条 */
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW::item:hover:!selected {
|
||||||
|
background: rgba(9, 105, 218, 12%);
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW::item:disabled {
|
||||||
|
color: #8c959f;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 分支指示器(三角) ------ */
|
||||||
|
QTreeWidget#treeW::branch {
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
image: none; /* 也不画三角 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------ 滚动条(仅影响 #treeW) ------ */
|
||||||
|
QTreeWidget#treeW QScrollBar:vertical {
|
||||||
|
width: 10px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::handle:vertical {
|
||||||
|
background: #c0c6cc;
|
||||||
|
border-radius: 3px;
|
||||||
|
min-height: 20px;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::handle:vertical:hover {
|
||||||
|
background: #a0a6ac;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::add-line:vertical,
|
||||||
|
QTreeWidget#treeW QScrollBar::sub-line:vertical {
|
||||||
|
height: 0; /* 隐藏箭头 */
|
||||||
|
}
|
||||||
|
|
||||||
|
QTreeWidget#treeW QScrollBar:horizontal {
|
||||||
|
height: 10px;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::handle:horizontal {
|
||||||
|
background: #c0c6cc;
|
||||||
|
border-radius: 5px;
|
||||||
|
min-width: 20px;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::handle:horizontal:hover {
|
||||||
|
background: #a0a6ac;
|
||||||
|
}
|
||||||
|
QTreeWidget#treeW QScrollBar::add-line:horizontal,
|
||||||
|
QTreeWidget#treeW QScrollBar::sub-line:horizontal {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton#pb_search,
|
||||||
|
QPushButton#pb_previous,
|
||||||
|
QPushButton#pb_next {
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLabel#lb_page {
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCheckBox#box_info,
|
||||||
|
QCheckBox#box_warning,
|
||||||
|
QCheckBox#box_error,
|
||||||
|
QCheckBox#box_exception,
|
||||||
|
QCheckBox#box_unknown {
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineEdit#le_search {
|
||||||
|
font-family: "Consolas";
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 327 KiB After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 329 KiB |
|
Before Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 327 KiB |
|
Before Width: | Height: | Size: 328 KiB |
|
Before Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 322 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 322 KiB |
|
After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 313 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 313 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 315 KiB |
|
After Width: | Height: | Size: 314 KiB |
|
After Width: | Height: | Size: 319 KiB |
|
After Width: | Height: | Size: 319 KiB |
|
After Width: | Height: | Size: 327 KiB |
|
After Width: | Height: | Size: 321 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 330 KiB |
|
After Width: | Height: | Size: 329 KiB |
|
After Width: | Height: | Size: 325 KiB |
|
After Width: | Height: | Size: 322 KiB |
BIN
toolbox/assets/media/buttons/RGB.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
toolbox/assets/media/buttons/颜色库.png
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
toolbox/assets/media/switch_off.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
toolbox/assets/media/switch_on.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
@@ -1,15 +1,33 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
|
||||||
base_path = Path(__file__).resolve().parent.parent.parent
|
|
||||||
lock = Lock()
|
lock = Lock()
|
||||||
account = None
|
config = None
|
||||||
code_dict = [4, 11, 4, 31, 22, 12, 19, 23, 7, 16, 7, 23, 1, 8, 7, 18, 27, 32, 28, 25, 7, 32, 9, 15, 2, 32, 0, 12, 26, 15, 14, 17]
|
code_dict = [4, 11, 4, 31, 22, 12, 19, 23, 7, 16, 7, 23, 1, 8, 7, 18, 27, 32, 28, 25, 7, 32, 9, 15, 2, 32, 0, 12, 26, 15, 14, 17]
|
||||||
username, password = "", ""
|
username, password = "", ""
|
||||||
avatar = f"{base_path}/assets/media/avatar.jpg"
|
|
||||||
proverb = "佛曰:Time will say~"
|
proverb = "佛曰:Time will say~"
|
||||||
bg = f"{base_path}/assets/media/bg.jpg"
|
|
||||||
win_width, win_height = 1100, 500
|
win_width, win_height = 1100, 500
|
||||||
|
conn, cursor = None, None
|
||||||
|
listW_items = {"实用工具": "w10_practical", "效率提升": "w20_efficiency", "财务分析": "w30_financial"}
|
||||||
|
caller_frame = None
|
||||||
|
|
||||||
|
base_path = Path(__file__).resolve().parent.parent.parent
|
||||||
|
db_file = base_path / "assets/database/toolbox.db"
|
||||||
|
bg = f"{base_path}/assets/media/bg.jpg"
|
||||||
|
avatar = f"{base_path}/assets/media/avatar.jpg"
|
||||||
|
icon = f"{base_path}/assets/media/icon.ico"
|
||||||
|
logo = f"{base_path}/assets/media/logo.png"
|
||||||
|
on_icon = f"{base_path}/assets/media/switch_on.png"
|
||||||
|
off_icon = f"{base_path}/assets/media/switch_off.png"
|
||||||
|
|
||||||
|
qss_home_overlay = f"{base_path}/assets/conf/qss/home_overlay.qss"
|
||||||
|
qss_list_widget = f"{base_path}/assets/conf/qss/list_widget.qss"
|
||||||
|
qss_toolbar = f"{base_path}/assets/conf/qss/toolbar.qss"
|
||||||
|
qss_statusbar = f"{base_path}/assets/conf/qss/statusbar.qss"
|
||||||
|
qss_w08_log = f"{base_path}/assets/conf/qss/w08_log.qss"
|
||||||
|
|
||||||
|
|
||||||
def delete_files_in_directory(directory):
|
def delete_files_in_directory(directory):
|
||||||
path = Path(directory)
|
path = Path(directory)
|
||||||
if path.exists() and path.is_dir():
|
if path.exists() and path.is_dir():
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import time
|
import time
|
||||||
|
from inspect import currentframe
|
||||||
|
from functools import singledispatch, wraps
|
||||||
|
|
||||||
from codes.common import clibs
|
from codes.common import clibs
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
def db_init(db_file):
|
def db_init():
|
||||||
conn = sqlite3.connect(db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=10.0)
|
if clibs.db_file.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
conn = sqlite3.connect(clibs.db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=10.0)
|
||||||
cursor = conn.cursor()
|
cursor = conn.cursor()
|
||||||
cursor.execute("PRAGMA journal_mode=wal")
|
cursor.execute("PRAGMA journal_mode=wal")
|
||||||
cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
|
cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
|
||||||
@@ -35,14 +40,20 @@ def db_init(db_file):
|
|||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
cursor.execute(f"INSERT INTO logs (level, module, content) VALUES (?, ?, ?)", ("info", "login_ui", "数据库初始化成功!"))
|
||||||
cursor.close()
|
cursor.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
def db_lock(func):
|
def db_lock(func):
|
||||||
|
@wraps(func)
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
|
clibs.caller_frame = currentframe().f_back
|
||||||
try:
|
try:
|
||||||
clibs.lock.acquire(True)
|
clibs.lock.acquire(True)
|
||||||
ret = func(*args, **kwargs)
|
ret = func(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"db operation error: {e}")
|
||||||
|
ret = None
|
||||||
finally:
|
finally:
|
||||||
clibs.lock.release()
|
clibs.lock.release()
|
||||||
return ret
|
return ret
|
||||||
@@ -50,31 +61,87 @@ def db_lock(func):
|
|||||||
|
|
||||||
def db_backup():
|
def db_backup():
|
||||||
t = time.strftime("%Y%m%d%H%M%S", time.localtime())
|
t = time.strftime("%Y%m%d%H%M%S", time.localtime())
|
||||||
db_file = clibs.base_path / "assets/database/toolbox.db"
|
|
||||||
db_file_backup = clibs.base_path / f"assets/database/toolbox.{t}.db"
|
db_file_backup = clibs.base_path / f"assets/database/toolbox.{t}.db"
|
||||||
if not (db_file.exists() and db_file.is_file()):
|
db_file_backup.write_bytes(clibs.db_file.read_bytes())
|
||||||
db_init(db_file)
|
db_dir = clibs.base_path / "assets/database"
|
||||||
else:
|
db_list = [db for db in db_dir.glob("*.db")]
|
||||||
db_file_backup.write_bytes(db_file.read_bytes())
|
for db in sorted(db_list)[:-clibs.config["maximum_db_number"]]:
|
||||||
db_dir = clibs.base_path / "assets/database"
|
db.unlink()
|
||||||
db_list = [db for db in db_dir.glob("*.db")]
|
|
||||||
for db in sorted(db_list)[:-clibs.account["maximum_db_number"]]:
|
|
||||||
db.unlink()
|
|
||||||
|
|
||||||
def db_conn():
|
def db_conn():
|
||||||
db_file = clibs.base_path / "assets/database/toolbox.db"
|
# import traceback, inspect
|
||||||
conn = sqlite3.connect(db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=3.0)
|
# print("[Conn] 被调用", traceback.format_stack()[-2])
|
||||||
cursor = conn.cursor()
|
# print("[Conn] conn=", clibs.conn, "cursor=", clibs.cursor)
|
||||||
cursor.execute("PRAGMA journal_mode=wal")
|
|
||||||
cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
|
if clibs.conn is not None:
|
||||||
cursor.execute("PRAGMA synchronous=normal")
|
return
|
||||||
cursor.execute("PRAGMA temp_store=memory")
|
|
||||||
cursor.execute("PRAGMA mmap_size=30000000000")
|
clibs.conn = sqlite3.connect(clibs.db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=3.0)
|
||||||
cursor.execute("PRAGMA cache_size=200000")
|
clibs.cursor = clibs.conn.cursor()
|
||||||
return conn, cursor
|
clibs.cursor.execute("PRAGMA journal_mode=wal")
|
||||||
|
clibs.cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
|
||||||
|
clibs.cursor.execute("PRAGMA synchronous=normal")
|
||||||
|
clibs.cursor.execute("PRAGMA temp_store=memory")
|
||||||
|
clibs.cursor.execute("PRAGMA mmap_size=30000000000")
|
||||||
|
clibs.cursor.execute("PRAGMA cache_size=200000")
|
||||||
|
|
||||||
@db_lock
|
@db_lock
|
||||||
def db_close(conn, cursor):
|
def db_close():
|
||||||
cursor.close()
|
if clibs.cursor is not None:
|
||||||
conn.close()
|
clibs.cursor.close()
|
||||||
|
if clibs.conn is not None:
|
||||||
|
clibs.conn.close()
|
||||||
|
clibs.conn, clibs.cursor = None, None
|
||||||
|
|
||||||
|
@db_lock
|
||||||
|
def db_write_logs(content, module="", level="info"):
|
||||||
|
if module == "" and clibs.caller_frame is not None:
|
||||||
|
module_name = clibs.caller_frame.f_globals["__name__"].split(".")[-1] #
|
||||||
|
func_name = clibs.caller_frame.f_code.co_name
|
||||||
|
line_no = clibs.caller_frame.f_lineno
|
||||||
|
module = f"{module_name}-{func_name}:{line_no}"
|
||||||
|
|
||||||
|
if level.lower() not in ["info", "warning", "error", "exception"]:
|
||||||
|
level = "unknown"
|
||||||
|
|
||||||
|
clibs.cursor.execute(f"INSERT INTO logs (level, module, content) VALUES (?, ?, ?)", (level, module, content))
|
||||||
|
|
||||||
|
@singledispatch
|
||||||
|
@db_lock
|
||||||
|
def db_query_logs(dummy: object = None):
|
||||||
|
clibs.cursor.execute(f"SELECT * FROM logs")
|
||||||
|
records = clibs.cursor.fetchall()
|
||||||
|
len_records = len(records)
|
||||||
|
return records, len_records
|
||||||
|
|
||||||
|
@db_query_logs.register
|
||||||
|
def _(levels: list):
|
||||||
|
placeholders = ",".join("?" * len(levels))
|
||||||
|
clibs.cursor.execute(f"SELECT * FROM logs WHERE level IN ({placeholders})", (*levels, ))
|
||||||
|
records = clibs.cursor.fetchall()
|
||||||
|
len_records = len(records)
|
||||||
|
return records, len_records
|
||||||
|
|
||||||
|
@db_query_logs.register
|
||||||
|
def _(search_text: str, records: list):
|
||||||
|
ids = [_[0] for _ in records]
|
||||||
|
placeholder = ",".join("?" * len(ids))
|
||||||
|
clibs.cursor.execute(f"SELECT * FROM logs WHERE id IN ({placeholder}) and content like ?", (ids + [f"%{search_text}%", ]))
|
||||||
|
records = clibs.cursor.fetchall()
|
||||||
|
len_records = len(records)
|
||||||
|
return records, len_records
|
||||||
|
|
||||||
|
@db_lock
|
||||||
|
def db_write_users(username, password_encrypted, salt):
|
||||||
|
clibs.cursor.execute("INSERT INTO users (username, password, salt) VALUES (?, ?, ?)", (username, password_encrypted, salt))
|
||||||
|
|
||||||
|
@db_lock
|
||||||
|
def db_delete_users(username):
|
||||||
|
# clibs.cursor.execute("INSERT INTO users (username, password, salt) VALUES (?, ?, ?)", (username, password_encrypted, salt))
|
||||||
|
...
|
||||||
|
|
||||||
|
@db_lock
|
||||||
|
def db_query_users(username):
|
||||||
|
clibs.cursor.execute(f""" SELECT * FROM users where username = "{username}" """)
|
||||||
|
record = clibs.cursor.fetchall()
|
||||||
|
return record
|
||||||
|
|||||||
29
toolbox/codes/common/exception_handler.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
from functools import wraps
|
||||||
|
from codes.common import db_operation
|
||||||
|
from inspect import currentframe
|
||||||
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
|
def handle_exception(stop: bool = False):
|
||||||
|
def exceptions(func):
|
||||||
|
@wraps(func)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
real_frame = currentframe().f_back
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except Exception as e:
|
||||||
|
for frame, lineno in traceback.walk_tb(e.__traceback__):
|
||||||
|
if frame.f_code.co_name == func.__name__:
|
||||||
|
real_frame = frame
|
||||||
|
break
|
||||||
|
|
||||||
|
tb = traceback.format_exc() # 完整堆栈
|
||||||
|
module_name = real_frame.f_globals["__name__"].split(".")[-1]
|
||||||
|
func_name = real_frame.f_code.co_name
|
||||||
|
line_no = real_frame.f_lineno
|
||||||
|
module = f"{module_name}-{func_name}:{line_no}"
|
||||||
|
db_operation.db_write_logs(tb, module, "exception")
|
||||||
|
if stop:
|
||||||
|
raise e
|
||||||
|
return wrapper
|
||||||
|
return exceptions
|
||||||
41
toolbox/codes/common/qss_reloader.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PySide6.QtCore import QObject, QFileSystemWatcher
|
||||||
|
|
||||||
|
|
||||||
|
class QssReloader(QObject):
|
||||||
|
instance = None
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls.instance is None:
|
||||||
|
cls.instance = super().__new__(cls)
|
||||||
|
return cls.instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.path2objs = {}
|
||||||
|
self.watcher = QFileSystemWatcher(self)
|
||||||
|
self.watcher.fileChanged.connect(self.on_file_changed)
|
||||||
|
|
||||||
|
def register(self, qss_path: str, ui_obj):
|
||||||
|
path = str(Path(qss_path).resolve())
|
||||||
|
obj_list = self.path2objs.setdefault(path, [])
|
||||||
|
if ui_obj not in obj_list:
|
||||||
|
obj_list.append(ui_obj)
|
||||||
|
if path not in self.watcher.files():
|
||||||
|
self.watcher.addPath(path)
|
||||||
|
self.apply_one(path, ui_obj)
|
||||||
|
|
||||||
|
def on_file_changed(self, path: str):
|
||||||
|
for obj in self.path2objs.get(path, []):
|
||||||
|
self.apply_one(path, obj)
|
||||||
|
if path not in self.watcher.files():
|
||||||
|
self.watcher.addPath(path)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def apply_one(path: str, obj):
|
||||||
|
qss = Path(path).read_text(encoding="utf-8")
|
||||||
|
obj.setStyleSheet(qss)
|
||||||
|
|
||||||
|
|
||||||
|
qss_reloader = QssReloader()
|
||||||
30
toolbox/codes/common/signal_bus.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
from PySide6.QtCore import QObject, Signal
|
||||||
|
|
||||||
|
|
||||||
|
class SignalBus(QObject):
|
||||||
|
instance = None
|
||||||
|
|
||||||
|
def __new__(cls):
|
||||||
|
if cls.instance is None:
|
||||||
|
cls.instance = super(SignalBus, cls).__new__(cls)
|
||||||
|
return cls.instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
|
current_stacked_page = Signal(str) # 获取当前页面的page_id
|
||||||
|
init_stacked_page = Signal(str) # 设置打开侧边栏后的初始页面
|
||||||
|
stacked_page_switch = Signal(str) # 切换stacked widget的页面
|
||||||
|
stacked_page_switch_setting = Signal() # 切换stacked widget的设置页面后的触发信号
|
||||||
|
stacked_page_switch_log = Signal() # 切换stacked widget的日志页面后的触发信号
|
||||||
|
stacked_page_switch_about = Signal() # 切换stacked widget的关于页面后的触发信号
|
||||||
|
qa_switch_change = Signal(bool) # 切换折叠侧边栏的状态
|
||||||
|
home_overlay_trigger = Signal() # 触发软件锁屏
|
||||||
|
home_overlay_auth = Signal() # 触发密码框的显示与隐藏
|
||||||
|
home_overlay_close = Signal() # 退出锁屏后的收尾信号
|
||||||
|
list_widget_click = Signal(str) # 触发点击list widget信号
|
||||||
|
list_widget_on_off = Signal(bool) # 主动控制是否显示list widget组件
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
signal_bus = SignalBus()
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
import sys
|
|
||||||
from codes.common import clibs
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
UIC_CMD = "pyside6-uic"
|
|
||||||
|
|
||||||
|
|
||||||
def single_uic(ui_path: str, py_path: str):
|
|
||||||
for file in Path(ui_path).rglob("*.ui"):
|
|
||||||
file_name = file.stem
|
|
||||||
ui_file = file
|
|
||||||
py_file = Path(py_path) / f"{file_name}.py"
|
|
||||||
cmd = [UIC_CMD, "-o", py_file, ui_file]
|
|
||||||
|
|
||||||
print(f"Processing {ui_file} -> {py_file}")
|
|
||||||
try:
|
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
print(f"转换失败: {ui_file}\n{e.stderr}", file=sys.stderr)
|
|
||||||
|
|
||||||
def main():
|
|
||||||
ui_path = clibs.base_path / "assets" / "ui"
|
|
||||||
py_path = clibs.base_path / "codes" / "ui"
|
|
||||||
single_uic(str(ui_path), str(py_path))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
0
toolbox/codes/ui/__init__.py
Normal file
0
toolbox/codes/ui/components/__init__.py
Normal file
58
toolbox/codes/ui/components/list_widget_ui.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
from PySide6.QtGui import QFocusEvent
|
||||||
|
from PySide6.QtWidgets import QListWidget, QListWidgetItem
|
||||||
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
|
from codes.common import clibs
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.common.qss_reloader import qss_reloader
|
||||||
|
|
||||||
|
|
||||||
|
class SListWidget(QListWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.pre_do()
|
||||||
|
self.init_ui()
|
||||||
|
self.post_do()
|
||||||
|
|
||||||
|
def pre_do(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
self.setObjectName("SListWidget")
|
||||||
|
self.setMinimumWidth(150)
|
||||||
|
for item in clibs.listW_items:
|
||||||
|
_ = QListWidgetItem(item)
|
||||||
|
_.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.addItem(_)
|
||||||
|
|
||||||
|
def post_do(self):
|
||||||
|
qss_reloader.register(clibs.qss_list_widget, self)
|
||||||
|
self.setup_slot()
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
self.itemClicked.connect(self.item_clicked)
|
||||||
|
signal_bus.stacked_page_switch_setting.connect(self.qa_hide)
|
||||||
|
signal_bus.stacked_page_switch_log.connect(self.qa_hide)
|
||||||
|
signal_bus.stacked_page_switch_about.connect(self.qa_hide)
|
||||||
|
signal_bus.list_widget_on_off.connect(self.lw_show_hide)
|
||||||
|
|
||||||
|
def item_clicked(self, item):
|
||||||
|
page_id = clibs.listW_items[item.text()]
|
||||||
|
signal_bus.list_widget_click.emit(page_id)
|
||||||
|
|
||||||
|
def lw_show_hide(self, enabled: bool):
|
||||||
|
if enabled:
|
||||||
|
self.clearSelection()
|
||||||
|
self.show()
|
||||||
|
signal_bus.init_stacked_page.emit("w10_practical")
|
||||||
|
else:
|
||||||
|
self.hide()
|
||||||
|
|
||||||
|
def qa_hide(self):
|
||||||
|
self.hide()
|
||||||
|
signal_bus.qa_switch_change.emit(False)
|
||||||
|
|
||||||
|
def focusOutEvent(self, event: QFocusEvent):
|
||||||
|
self.clearSelection()
|
||||||
|
super().focusOutEvent(event)
|
||||||
41
toolbox/codes/ui/components/stacked_widget_ui.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
from PySide6.QtWidgets import QStackedWidget
|
||||||
|
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.ui.stacked_pages.w01_setting import W01Setting
|
||||||
|
from codes.ui.stacked_pages.w08_log import W08Log
|
||||||
|
from codes.ui.stacked_pages.w09_about import W09About
|
||||||
|
from codes.ui.stacked_pages.w10_practical import W10Practical
|
||||||
|
from codes.ui.stacked_pages.w20_efficiency import W20Efficiency
|
||||||
|
from codes.ui.stacked_pages.w30_financial import W30Financial
|
||||||
|
|
||||||
|
|
||||||
|
class SStackedWidget(QStackedWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.init_ui()
|
||||||
|
self.setup_slot()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
self.page_list = {"w01_setting": W01Setting(), "w08_log": W08Log(), "w09_about": W09About(), "w10_practical": W10Practical(), "w20_efficiency": W20Efficiency(), "w30_financial": W30Financial()}
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
self.setObjectName("SStackedWidget")
|
||||||
|
# stacked widget 1x: 10为一级按钮页,其余为二级按钮页,2-9同理 | 0x. 日志/设置/关于等页面
|
||||||
|
for page_id, widget in self.page_list.items():
|
||||||
|
widget.setObjectName(page_id)
|
||||||
|
self.addWidget(widget)
|
||||||
|
|
||||||
|
w = self.page_list.get("w01_setting")
|
||||||
|
self.setCurrentWidget(w)
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
signal_bus.init_stacked_page.connect(self.set_current_page)
|
||||||
|
signal_bus.stacked_page_switch.connect(self.set_current_page)
|
||||||
|
signal_bus.list_widget_click.connect(self.set_current_page)
|
||||||
|
|
||||||
|
def set_current_page(self, page_id: str):
|
||||||
|
w = self.page_list.get(page_id)
|
||||||
|
self.setCurrentWidget(w)
|
||||||
|
signal_bus.current_stacked_page.emit(page_id)
|
||||||
25
toolbox/codes/ui/components/statusbar_ui.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PySide6.QtWidgets import QStatusBar
|
||||||
|
from PySide6.QtGui import QAction, QIcon
|
||||||
|
from PySide6.QtCore import QSize
|
||||||
|
|
||||||
|
from codes.common import clibs
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.common.qss_reloader import qss_reloader
|
||||||
|
|
||||||
|
|
||||||
|
class SStatusBar(QStatusBar):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.pre_do()
|
||||||
|
self.init_ui()
|
||||||
|
self.post_do()
|
||||||
|
|
||||||
|
def pre_do(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
self.setObjectName("SStatusBar")
|
||||||
|
...
|
||||||
|
|
||||||
|
def post_do(self):
|
||||||
|
qss_reloader.register(clibs.qss_statusbar, self)
|
||||||
102
toolbox/codes/ui/components/toolbar_ui.py
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
from PySide6.QtWidgets import QToolBar
|
||||||
|
from PySide6.QtGui import QAction, QIcon
|
||||||
|
from PySide6.QtCore import QSize
|
||||||
|
|
||||||
|
from codes.common import clibs
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.common.qss_reloader import qss_reloader
|
||||||
|
|
||||||
|
|
||||||
|
class SToolBar(QToolBar):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.pre_do()
|
||||||
|
self.init_ui()
|
||||||
|
self.post_do()
|
||||||
|
|
||||||
|
def pre_do(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
self.setObjectName("SToolBar")
|
||||||
|
# switch
|
||||||
|
self.on_icon = QIcon(clibs.on_icon) # 开状态图标
|
||||||
|
self.off_icon = QIcon(clibs.off_icon) # 关状态图标
|
||||||
|
self.ac_switch = QAction(self.on_icon, "", self)
|
||||||
|
self.ac_switch.setMenuRole(QAction.MenuRole.NoRole)
|
||||||
|
self.ac_switch.setStatusTip("Switch side bar")
|
||||||
|
self.ac_switch.setToolTip("Ctrl+Alt+T")
|
||||||
|
self.ac_switch.setCheckable(True)
|
||||||
|
self.ac_switch.setChecked(True)
|
||||||
|
self.setIconSize(QSize(30, 30))
|
||||||
|
self.addAction(self.ac_switch)
|
||||||
|
btn = self.widgetForAction(self.ac_switch) # 取出实际 QToolButton
|
||||||
|
btn.setObjectName("ac_switch")
|
||||||
|
|
||||||
|
# homepage
|
||||||
|
self.ac_homepage = QAction()
|
||||||
|
self.ac_homepage.setMenuRole(QAction.MenuRole.NoRole)
|
||||||
|
self.ac_homepage.setStatusTip("Go to home page")
|
||||||
|
self.ac_homepage.setToolTip("Ctrl+Alt+H")
|
||||||
|
self.ac_homepage.setText("主页")
|
||||||
|
self.addAction(self.ac_homepage)
|
||||||
|
|
||||||
|
# setting
|
||||||
|
self.ac_setting = QAction()
|
||||||
|
self.ac_setting.setMenuRole(QAction.MenuRole.NoRole)
|
||||||
|
self.ac_setting.setStatusTip("Go to setting page")
|
||||||
|
self.ac_setting.setToolTip("Ctrl+Alt+S")
|
||||||
|
self.ac_setting.setText("设置")
|
||||||
|
self.addAction(self.ac_setting)
|
||||||
|
|
||||||
|
# log
|
||||||
|
self.ac_log = QAction()
|
||||||
|
self.ac_log.setMenuRole(QAction.MenuRole.NoRole)
|
||||||
|
self.ac_log.setStatusTip("Go to log page")
|
||||||
|
self.ac_log.setToolTip("Ctrl+Alt+L")
|
||||||
|
self.ac_log.setText("日志")
|
||||||
|
self.addAction(self.ac_log)
|
||||||
|
|
||||||
|
# about
|
||||||
|
self.ac_about = QAction()
|
||||||
|
self.ac_about.setMenuRole(QAction.MenuRole.NoRole)
|
||||||
|
self.ac_about.setStatusTip("Go to about page")
|
||||||
|
self.ac_about.setToolTip("Ctrl+Alt+A")
|
||||||
|
self.ac_about.setText("关于")
|
||||||
|
self.addAction(self.ac_about)
|
||||||
|
|
||||||
|
def post_do(self):
|
||||||
|
qss_reloader.register(clibs.qss_toolbar, self)
|
||||||
|
self.setup_slot()
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
self.ac_switch.toggled.connect(self.ac_sw)
|
||||||
|
self.ac_homepage.triggered.connect(self.ac_hp)
|
||||||
|
self.ac_setting.triggered.connect(self.ac_sp)
|
||||||
|
self.ac_log.triggered.connect(self.ac_lp)
|
||||||
|
self.ac_about.triggered.connect(self.ac_ap)
|
||||||
|
signal_bus.qa_switch_change.connect(self.change2hide)
|
||||||
|
|
||||||
|
def ac_sw(self, checked: bool):
|
||||||
|
self.ac_switch.setIcon(self.on_icon if checked else self.off_icon)
|
||||||
|
signal_bus.list_widget_on_off.emit(checked)
|
||||||
|
|
||||||
|
def ac_hp(self):
|
||||||
|
signal_bus.home_overlay_trigger.emit()
|
||||||
|
|
||||||
|
def ac_sp(self):
|
||||||
|
signal_bus.stacked_page_switch.emit("w01_setting")
|
||||||
|
signal_bus.stacked_page_switch_setting.emit()
|
||||||
|
|
||||||
|
def ac_lp(self):
|
||||||
|
signal_bus.stacked_page_switch.emit("w08_log")
|
||||||
|
signal_bus.stacked_page_switch_log.emit()
|
||||||
|
|
||||||
|
def ac_ap(self):
|
||||||
|
signal_bus.stacked_page_switch.emit("w09_about")
|
||||||
|
signal_bus.stacked_page_switch_about.emit()
|
||||||
|
|
||||||
|
def change2hide(self):
|
||||||
|
self.ac_switch.setChecked(False)
|
||||||
|
self.ac_switch.setIcon(self.off_icon)
|
||||||
|
|
||||||
@@ -1,64 +1,80 @@
|
|||||||
import sys
|
import sys
|
||||||
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QListWidget, QStackedWidget, QCheckBox, QSpinBox, QToolBox, QLineEdit, QTableWidget, QTreeWidget, QCalendarWidget, QMessageBox, QToolBar, QSizePolicy
|
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QLineEdit, QMessageBox, QSizePolicy
|
||||||
from PySide6.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
|
from PySide6.QtCore import Qt, QSize
|
||||||
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut
|
from PySide6.QtGui import QFont, QIcon, QPixmap, QShortcut
|
||||||
from codes.common import clibs, db_operation
|
from codes.common import clibs, db_operation
|
||||||
from codes.common.secure_encrypt import PassCipher
|
from codes.common.secure_encrypt import PassCipher
|
||||||
from codes.ui import main_ui
|
from codes.ui import main_ui
|
||||||
|
from codes.common.exception_handler import handle_exception
|
||||||
|
|
||||||
|
|
||||||
class LoginWindow(QWidget):
|
class LoginWindow(QWidget):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.pre_do()
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.setup_slot()
|
self.post_do()
|
||||||
self.predos()
|
|
||||||
self.le_username.setFocus()
|
def pre_do(self):
|
||||||
|
db_operation.db_init()
|
||||||
|
db_operation.db_conn()
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
|
self.setObjectName("LoginWindow")
|
||||||
self.setMinimumSize(420, 200)
|
self.setMinimumSize(420, 200)
|
||||||
self.setMaximumSize(500, 240)
|
self.setMaximumSize(500, 240)
|
||||||
self.resize(480, 200)
|
self.resize(480, 200)
|
||||||
self.setWindowTitle("登录")
|
self.setWindowTitle("登录")
|
||||||
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
|
self.setWindowIcon(QIcon(clibs.icon))
|
||||||
self.setFont(QFont("Consolas", 14))
|
self.setFont(QFont("Consolas", 14))
|
||||||
|
|
||||||
self.layout_outter = QHBoxLayout()
|
self.layout_outer = QHBoxLayout()
|
||||||
self.lb_logo = QLabel()
|
self.lb_logo = QLabel()
|
||||||
|
self.lb_logo.setObjectName("lb_logo")
|
||||||
self.lb_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lb_logo.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
|
self.lb_logo.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred)
|
||||||
self.lb_logo.setPixmap(QPixmap(f"{clibs.base_path}/assets/media/logo.png"))
|
self.lb_logo.setPixmap(QPixmap(clibs.logo))
|
||||||
self.lb_logo.setFixedSize(QSize(120, 120))
|
self.lb_logo.setFixedSize(QSize(120, 120))
|
||||||
self.lb_logo.setScaledContents(True)
|
self.lb_logo.setScaledContents(True)
|
||||||
self.layout_outter.addWidget(self.lb_logo)
|
self.layout_outer.addWidget(self.lb_logo)
|
||||||
|
|
||||||
self.tabW_login = QTabWidget()
|
self.tabW_login = QTabWidget()
|
||||||
|
self.tabW_login.setObjectName("tabW_login")
|
||||||
self.tab_login = QWidget()
|
self.tab_login = QWidget()
|
||||||
|
self.tab_login.setObjectName("tab_login")
|
||||||
self.tabW_login.addTab(self.tab_login, "登录")
|
self.tabW_login.addTab(self.tab_login, "登录")
|
||||||
self.tab_register = QWidget()
|
self.tab_register = QWidget()
|
||||||
|
self.tab_register.setObjectName("tab_register")
|
||||||
self.tabW_login.addTab(self.tab_register, "注册")
|
self.tabW_login.addTab(self.tab_register, "注册")
|
||||||
self.layout_outter.addWidget(self.tabW_login)
|
self.layout_outer.addWidget(self.tabW_login)
|
||||||
|
|
||||||
# 登陆页面
|
# 登陆页面
|
||||||
self.layout_H_username = QHBoxLayout()
|
self.layout_H_username = QHBoxLayout()
|
||||||
self.lb_username = QLabel("账号")
|
self.lb_username = QLabel("账号")
|
||||||
|
self.lb_username.setObjectName("lb_username")
|
||||||
self.lb_username.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_username.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layout_H_username.addWidget(self.lb_username)
|
self.layout_H_username.addWidget(self.lb_username)
|
||||||
self.le_username = QLineEdit()
|
self.le_username = QLineEdit()
|
||||||
|
self.le_username.setObjectName("le_username")
|
||||||
self.le_username.setFocus()
|
self.le_username.setFocus()
|
||||||
self.layout_H_username.addWidget(self.le_username)
|
self.layout_H_username.addWidget(self.le_username)
|
||||||
|
|
||||||
self.layout_H_password = QHBoxLayout()
|
self.layout_H_password = QHBoxLayout()
|
||||||
self.lb_password = QLabel("密码")
|
self.lb_password = QLabel("密码")
|
||||||
|
self.lb_password.setObjectName("lb_password")
|
||||||
self.lb_password.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_password.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layout_H_password.addWidget(self.lb_password)
|
self.layout_H_password.addWidget(self.lb_password)
|
||||||
self.le_password = QLineEdit()
|
self.le_password = QLineEdit()
|
||||||
|
self.le_password.setObjectName("le_password")
|
||||||
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
self.layout_H_password.addWidget(self.le_password)
|
self.layout_H_password.addWidget(self.le_password)
|
||||||
|
|
||||||
self.layout_H_button = QHBoxLayout()
|
self.layout_H_button = QHBoxLayout()
|
||||||
self.btn_login = QPushButton("登录")
|
self.btn_login = QPushButton("登录")
|
||||||
|
self.btn_login.setObjectName("btn_login")
|
||||||
self.btn_login.setAutoDefault(True)
|
self.btn_login.setAutoDefault(True)
|
||||||
self.btn_cancel = QPushButton("取消")
|
self.btn_cancel = QPushButton("取消")
|
||||||
|
self.btn_cancel.setObjectName("btn_cancel")
|
||||||
self.btn_cancel.setAutoDefault(True)
|
self.btn_cancel.setAutoDefault(True)
|
||||||
self.layout_H_button.addWidget(self.btn_login)
|
self.layout_H_button.addWidget(self.btn_login)
|
||||||
self.layout_H_button.addWidget(self.btn_cancel)
|
self.layout_H_button.addWidget(self.btn_cancel)
|
||||||
@@ -72,32 +88,40 @@ class LoginWindow(QWidget):
|
|||||||
# 注册页面
|
# 注册页面
|
||||||
self.layout_H_username_reg = QHBoxLayout()
|
self.layout_H_username_reg = QHBoxLayout()
|
||||||
self.lb_username_reg = QLabel("账号设定")
|
self.lb_username_reg = QLabel("账号设定")
|
||||||
|
self.lb_username_reg.setObjectName("lb_username_reg")
|
||||||
self.lb_username_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_username_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layout_H_username_reg.addWidget(self.lb_username_reg)
|
self.layout_H_username_reg.addWidget(self.lb_username_reg)
|
||||||
self.le_username_reg = QLineEdit()
|
self.le_username_reg = QLineEdit()
|
||||||
|
self.le_username_reg.setObjectName("le_username_reg")
|
||||||
self.le_username_reg.setFocus()
|
self.le_username_reg.setFocus()
|
||||||
self.layout_H_username_reg.addWidget(self.le_username_reg)
|
self.layout_H_username_reg.addWidget(self.le_username_reg)
|
||||||
|
|
||||||
self.layout_H_password_reg = QHBoxLayout()
|
self.layout_H_password_reg = QHBoxLayout()
|
||||||
self.lb_password_reg = QLabel("密码设定")
|
self.lb_password_reg = QLabel("密码设定")
|
||||||
|
self.lb_password_reg.setObjectName("lb_password_reg")
|
||||||
self.lb_password_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_password_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layout_H_password_reg.addWidget(self.lb_password_reg)
|
self.layout_H_password_reg.addWidget(self.lb_password_reg)
|
||||||
self.le_password_reg = QLineEdit()
|
self.le_password_reg = QLineEdit()
|
||||||
|
self.le_password_reg.setObjectName("le_password_reg")
|
||||||
self.le_password_reg.setEchoMode(QLineEdit.EchoMode.Password)
|
self.le_password_reg.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
self.layout_H_password_reg.addWidget(self.le_password_reg)
|
self.layout_H_password_reg.addWidget(self.le_password_reg)
|
||||||
|
|
||||||
self.layout_H_password_reg_confirm = QHBoxLayout()
|
self.layout_H_password_reg_confirm = QHBoxLayout()
|
||||||
self.lb_password_reg_confirm = QLabel("密码确认")
|
self.lb_password_reg_confirm = QLabel("密码确认")
|
||||||
|
self.lb_password_reg_confirm.setObjectName("lb_password_reg_confirm")
|
||||||
self.lb_password_reg_confirm.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_password_reg_confirm.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.layout_H_password_reg_confirm.addWidget(self.lb_password_reg_confirm)
|
self.layout_H_password_reg_confirm.addWidget(self.lb_password_reg_confirm)
|
||||||
self.le_password_reg_confirm = QLineEdit()
|
self.le_password_reg_confirm = QLineEdit()
|
||||||
|
self.le_password_reg.setObjectName("le_password_reg")
|
||||||
self.le_password_reg_confirm.setEchoMode(QLineEdit.EchoMode.Password)
|
self.le_password_reg_confirm.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
self.layout_H_password_reg_confirm.addWidget(self.le_password_reg_confirm)
|
self.layout_H_password_reg_confirm.addWidget(self.le_password_reg_confirm)
|
||||||
|
|
||||||
self.layout_H_button_reg = QHBoxLayout()
|
self.layout_H_button_reg = QHBoxLayout()
|
||||||
self.btn_login_reg = QPushButton("确认")
|
self.btn_login_reg = QPushButton("确认")
|
||||||
|
self.btn_login_reg.setObjectName("btn_login_reg")
|
||||||
self.btn_login_reg.setAutoDefault(True)
|
self.btn_login_reg.setAutoDefault(True)
|
||||||
self.btn_cancel_reg = QPushButton("取消")
|
self.btn_cancel_reg = QPushButton("取消")
|
||||||
|
self.btn_cancel_reg.setObjectName("btn_cancel_reg")
|
||||||
self.btn_cancel_reg.setAutoDefault(True)
|
self.btn_cancel_reg.setAutoDefault(True)
|
||||||
self.layout_H_button_reg.addWidget(self.btn_login_reg)
|
self.layout_H_button_reg.addWidget(self.btn_login_reg)
|
||||||
self.layout_H_button_reg.addWidget(self.btn_cancel_reg)
|
self.layout_H_button_reg.addWidget(self.btn_cancel_reg)
|
||||||
@@ -109,7 +133,11 @@ class LoginWindow(QWidget):
|
|||||||
self.layout_V_user_pass_reg.addLayout(self.layout_H_button_reg)
|
self.layout_V_user_pass_reg.addLayout(self.layout_H_button_reg)
|
||||||
self.tab_register.setLayout(self.layout_V_user_pass_reg)
|
self.tab_register.setLayout(self.layout_V_user_pass_reg)
|
||||||
|
|
||||||
self.setLayout(self.layout_outter)
|
self.setLayout(self.layout_outer)
|
||||||
|
self.le_username.setFocus()
|
||||||
|
|
||||||
|
def post_do(self):
|
||||||
|
self.setup_slot()
|
||||||
|
|
||||||
def setup_slot(self):
|
def setup_slot(self):
|
||||||
self.tabW_login.currentChanged.connect(self.onChange_tabW)
|
self.tabW_login.currentChanged.connect(self.onChange_tabW)
|
||||||
@@ -117,14 +145,11 @@ class LoginWindow(QWidget):
|
|||||||
self.btn_cancel.clicked.connect(self.close)
|
self.btn_cancel.clicked.connect(self.close)
|
||||||
self.btn_login_reg.clicked.connect(self.register_check)
|
self.btn_login_reg.clicked.connect(self.register_check)
|
||||||
self.btn_cancel_reg.clicked.connect(self.close)
|
self.btn_cancel_reg.clicked.connect(self.close)
|
||||||
QShortcut("Esc", self).activated.connect(self.close)
|
|
||||||
self.le_password.returnPressed.connect(self.login_check)
|
self.le_password.returnPressed.connect(self.login_check)
|
||||||
self.le_password_reg_confirm.returnPressed.connect(self.register_check)
|
self.le_password_reg_confirm.returnPressed.connect(self.register_check)
|
||||||
|
QShortcut("Esc", self).activated.connect(self.close)
|
||||||
def predos(self):
|
QShortcut("Alt+1", self).activated.connect(lambda: self.tabW_login.setCurrentIndex(0))
|
||||||
db_file = clibs.base_path / "assets/database/toolbox.db"
|
QShortcut("Alt+2", self).activated.connect(lambda: self.tabW_login.setCurrentIndex(1))
|
||||||
if not db_file.exists():
|
|
||||||
db_operation.db_init(db_file)
|
|
||||||
|
|
||||||
def onChange_tabW(self):
|
def onChange_tabW(self):
|
||||||
text = self.tabW_login.tabText(self.tabW_login.currentIndex())
|
text = self.tabW_login.tabText(self.tabW_login.currentIndex())
|
||||||
@@ -137,98 +162,93 @@ class LoginWindow(QWidget):
|
|||||||
self.le_password_reg.clear()
|
self.le_password_reg.clear()
|
||||||
self.le_password_reg_confirm.clear()
|
self.le_password_reg_confirm.clear()
|
||||||
self.le_username_reg.setFocus()
|
self.le_username_reg.setFocus()
|
||||||
else:
|
|
||||||
raise Exception(f"Unknown TabWidget Name: {text}")
|
|
||||||
|
|
||||||
|
@handle_exception()
|
||||||
def login_check(self):
|
def login_check(self):
|
||||||
def login_failed():
|
def login_failed(flag: int = 0):
|
||||||
|
reason = {-1: "用户名或密码为空", 0: "用户名未注册", 1: "解密成功,密码错误", 2: "解密失败,密码错误", 3: "数据库中有重复的用户名"}
|
||||||
self.le_username.clear()
|
self.le_username.clear()
|
||||||
self.le_password.clear()
|
self.le_password.clear()
|
||||||
self.le_username.setFocus()
|
self.le_username.setFocus()
|
||||||
QMessageBox.critical(self, "错误", "账号或密码错误,请重新输入!")
|
QMessageBox.critical(self, "错误", "账号或密码错误,请重新输入!")
|
||||||
|
raise Exception(f"username:{username} login failed, reason: {reason[flag]}")
|
||||||
|
|
||||||
def validate_login():
|
def validate_login():
|
||||||
nonlocal username, password
|
nonlocal username, password
|
||||||
conn, cursor = db_operation.db_conn()
|
record = db_operation.db_query_users(username)
|
||||||
cursor.execute(f""" SELECT * FROM users where username = "{username}" """)
|
if username == "" or password == "":
|
||||||
record = cursor.fetchall()
|
login_failed(flag=-1)
|
||||||
|
|
||||||
if len(record) == 0:
|
if len(record) == 0:
|
||||||
login_failed()
|
login_failed(flag=0)
|
||||||
elif len(record) == 1:
|
elif len(record) == 1:
|
||||||
keys = ["id", "timestamp", "username", "password", "salt"]
|
keys = ["id", "timestamp", "username", "password", "salt"]
|
||||||
login_info = dict(zip(keys, record[0]))
|
login_info = dict(zip(keys, record[0]))
|
||||||
salt = PassCipher.gen_salt("@".join([username, password]))
|
salt = PassCipher.gen_salt("@".join([username, password]))
|
||||||
cipher = PassCipher(salt)
|
cipher = PassCipher(salt)
|
||||||
# password_encrypt = cipher.encrypt("@".join([username, password]))
|
|
||||||
# print(f"password_encrypt = {password_encrypt}")
|
|
||||||
# exit()
|
|
||||||
try:
|
try:
|
||||||
decrypt_password = cipher.decrypt(login_info["password"])
|
decrypt_password = cipher.decrypt(login_info["password"])
|
||||||
if password != decrypt_password:
|
if password != decrypt_password:
|
||||||
login_failed()
|
login_failed(flag=1)
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
self.mainWindow = main_ui.MainWindow()
|
self.mainWindow = main_ui.MainWindow()
|
||||||
self.mainWindow.show()
|
self.mainWindow.show()
|
||||||
db_operation.db_close(conn, cursor)
|
|
||||||
clibs.username = username
|
clibs.username = username
|
||||||
clibs.password = password
|
clibs.password = password
|
||||||
self.close()
|
db_operation.db_write_logs(f"username:{username} 登录成功!")
|
||||||
return True
|
self.deleteLater()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
login_failed()
|
login_failed(flag=2)
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
raise Exception(f"username duplicated: {username}")
|
login_failed(flag=3)
|
||||||
|
|
||||||
username = self.le_username.text()
|
username = self.le_username.text()
|
||||||
password = self.le_password.text()
|
password = self.le_password.text()
|
||||||
|
|
||||||
validate_login()
|
validate_login()
|
||||||
|
|
||||||
|
@handle_exception()
|
||||||
def register_check(self):
|
def register_check(self):
|
||||||
def register_failed(flag: int = 0):
|
def register_failed(flag: int = 0):
|
||||||
|
reason = {-1: "用户名或密码为空", 0: "两次密码不一致", 1: "密码长度不合规", 2: "用户名已存在", 3: "数据库中有重复的用户名"}
|
||||||
self.le_username_reg.clear()
|
self.le_username_reg.clear()
|
||||||
self.le_password_reg.clear()
|
self.le_password_reg.clear()
|
||||||
self.le_password_reg_confirm.clear()
|
self.le_password_reg_confirm.clear()
|
||||||
self.le_username_reg.setFocus()
|
self.le_username_reg.setFocus()
|
||||||
if flag == 0:
|
QMessageBox.critical(self, "错误", "账号错误,密码长度(>8)不合规,或两次输入密码不一致,请确认后重新输入!")
|
||||||
QMessageBox.critical(self, "错误", "账号已存在,或两次输入密码不一致,请重新输入!")
|
raise Exception(f"register failed, reason: {reason[flag]}")
|
||||||
elif flag == 1:
|
|
||||||
QMessageBox.critical(self, "错误", "密码长度不符合要求,请重新输入!")
|
|
||||||
|
|
||||||
def validate_register():
|
def validate_register():
|
||||||
nonlocal username, password, password_confirm, record, conn, cursor
|
nonlocal username, password, password_confirm
|
||||||
if password != password_confirm:
|
record = db_operation.db_query_users(username)
|
||||||
register_failed()
|
if username == "" or password == "" or password_confirm == "":
|
||||||
return False
|
register_failed(flag=-1)
|
||||||
|
|
||||||
if len(password) < clibs.account["minimum_password_length"]:
|
if password != password_confirm:
|
||||||
|
register_failed(flag=0)
|
||||||
|
|
||||||
|
if len(password) < clibs.config["minimum_password_length"]:
|
||||||
register_failed(flag=1)
|
register_failed(flag=1)
|
||||||
return False
|
|
||||||
|
|
||||||
if len(record) == 0:
|
if len(record) == 0:
|
||||||
salt = PassCipher.gen_salt("@".join([username, password]))
|
salt = PassCipher.gen_salt("@".join([username, password]))
|
||||||
cipher = PassCipher(salt)
|
cipher = PassCipher(salt)
|
||||||
password_encrypted = cipher.encrypt(password)
|
password_encrypted = cipher.encrypt(password)
|
||||||
cursor.execute("INSERT INTO users (username, password, salt) VALUES (?, ?, ?)", (username, password_encrypted, salt))
|
db_operation.db_write_users(username, password_encrypted, salt)
|
||||||
QMessageBox.information(self, "成功", "注册成功,切换至登录窗口进行登录!")
|
QMessageBox.information(self, "成功", "注册成功,切换至登录窗口进行登录!")
|
||||||
self.tabW_login.setCurrentIndex(self.tabW_login.indexOf(self.tab_login))
|
self.tabW_login.setCurrentIndex(self.tabW_login.indexOf(self.tab_login))
|
||||||
return True
|
|
||||||
elif len(record) == 1:
|
elif len(record) == 1:
|
||||||
register_failed()
|
register_failed(flag=2)
|
||||||
return False
|
|
||||||
else:
|
else:
|
||||||
raise Exception(f"username duplicated: {username}")
|
register_failed(flag=3)
|
||||||
|
|
||||||
username = self.le_username_reg.text()
|
username = self.le_username_reg.text()
|
||||||
password = self.le_password_reg.text()
|
password = self.le_password_reg.text()
|
||||||
password_confirm = self.le_password_reg_confirm.text()
|
password_confirm = self.le_password_reg_confirm.text()
|
||||||
conn, cursor = db_operation.db_conn()
|
|
||||||
cursor.execute(f""" SELECT * FROM users where username = "{username}" """)
|
|
||||||
record = cursor.fetchall()
|
|
||||||
validate_register()
|
validate_register()
|
||||||
|
|
||||||
|
def closeEvent(self, event):
|
||||||
|
event.accept()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|||||||
@@ -1,64 +1,116 @@
|
|||||||
import json
|
|
||||||
from math import lgamma
|
|
||||||
from shutil import copy
|
|
||||||
from random import choice
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
from random import choice
|
||||||
|
from shutil import copy
|
||||||
import requests
|
import requests
|
||||||
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QListWidget, QStackedWidget, QCheckBox, QSpinBox, QToolBox, QLineEdit, QTableWidget, QTreeWidget, QCalendarWidget, QMessageBox, QToolBar, QSizePolicy, QMainWindow, QStatusBar
|
import json
|
||||||
from PySide6.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
|
import sys
|
||||||
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut, QAction, QKeySequence, QResizeEvent
|
|
||||||
|
from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout, QMessageBox, QMainWindow, QStatusBar
|
||||||
|
from PySide6.QtGui import QIcon, QResizeEvent, QShortcut, QKeySequence, QAction
|
||||||
|
from PySide6.QtCore import Qt, QFileSystemWatcher
|
||||||
|
|
||||||
from codes.common import clibs, db_operation
|
from codes.common import clibs, db_operation
|
||||||
from codes.ui.widget_bg_ui import WidgetWithBg
|
from codes.ui.components.toolbar_ui import SToolBar
|
||||||
|
from codes.ui.components.list_widget_ui import SListWidget
|
||||||
|
from codes.ui.components.stacked_widget_ui import SStackedWidget
|
||||||
|
from codes.ui.components.statusbar_ui import SStatusBar
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
from codes.common.worker import Worker
|
from codes.common.worker import Worker
|
||||||
from typing import Callable, Any
|
from codes.common.exception_handler import handle_exception
|
||||||
|
from codes.ui.overlay_page.overlay_ui import WidgetWithBg
|
||||||
|
|
||||||
|
|
||||||
class MainWindow(QMainWindow):
|
class MainWindow(QMainWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
self.pre_do()
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.setup_slot()
|
self.post_do()
|
||||||
self.predos()
|
|
||||||
|
def pre_do(self):
|
||||||
|
self.m = Path(__file__).stem
|
||||||
|
self.home_overlay = None
|
||||||
|
db_operation.db_backup()
|
||||||
|
db_operation.db_conn()
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
|
self.setObjectName("MainWindow")
|
||||||
self.setMinimumSize(clibs.win_width, clibs.win_height)
|
self.setMinimumSize(clibs.win_width, clibs.win_height)
|
||||||
self.resize(clibs.win_width, clibs.win_height)
|
self.resize(clibs.win_width, clibs.win_height)
|
||||||
self.setWindowTitle("Toolbox")
|
self.setWindowTitle("Toolbox")
|
||||||
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
|
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
|
||||||
self.setFont(QFont("Consolas", 14))
|
|
||||||
# 任务栏/主窗口/状态栏
|
# 任务栏/主窗口/状态栏
|
||||||
self.toolBar = QToolBar()
|
self.toolBar = SToolBar()
|
||||||
self.addToolBar(self.toolBar)
|
self.addToolBar(self.toolBar)
|
||||||
self.toolBar.setMovable(False)
|
self.toolBar.setMovable(False)
|
||||||
self.centralW = QWidget()
|
self.centralW = QWidget()
|
||||||
self.setCentralWidget(self.centralW)
|
self.setCentralWidget(self.centralW)
|
||||||
self.statusBar = QStatusBar()
|
self.statusBar = SStatusBar()
|
||||||
self.setStatusBar(self.statusBar)
|
self.setStatusBar(self.statusBar)
|
||||||
|
|
||||||
# toolbar
|
layout_h = QHBoxLayout()
|
||||||
self.ac_homepage = QAction()
|
# list widget
|
||||||
self.ac_homepage.setMenuRole(QAction.MenuRole.NoRole)
|
self.listW = SListWidget()
|
||||||
self.ac_homepage.setStatusTip("Go to homepage")
|
layout_h.addWidget(self.listW, stretch=1)
|
||||||
self.ac_homepage.setToolTip("Ctrl+Alt+H")
|
self.stackedW =SStackedWidget()
|
||||||
self.ac_homepage.setText("主页")
|
layout_h.addWidget(self.stackedW, stretch=5)
|
||||||
self.ac_homepage.setShortcut(QKeySequence("Ctrl+Alt+H"))
|
self.centralW.setLayout(layout_h)
|
||||||
self.toolBar.addAction(self.ac_homepage)
|
|
||||||
|
|
||||||
def setup_slot(self):
|
def setup_slot(self):
|
||||||
self.ac_homepage.triggered.connect(self.ac_hp)
|
signal_bus.home_overlay_trigger.connect(self.ac_hp)
|
||||||
# QShortcut("Esc", self).activated.connect(self.close)
|
signal_bus.home_overlay_close.connect(self.exit_overlay)
|
||||||
|
self.install_sc()
|
||||||
|
|
||||||
def predos(self):
|
def post_do(self):
|
||||||
self.home_overlay = None
|
self.setup_slot()
|
||||||
db_operation.db_backup()
|
self.setup_sc()
|
||||||
self.conn, self.cursor = db_operation.db_conn()
|
|
||||||
|
def setup_sc(self, enable: bool = True):
|
||||||
|
for sc_id, obj in self.sc_pool.items():
|
||||||
|
if isinstance(obj, QAction):
|
||||||
|
obj.setEnabled(enable)
|
||||||
|
elif isinstance(obj, QShortcut):
|
||||||
|
obj.setEnabled(enable)
|
||||||
|
|
||||||
|
if enable == False and (sc_id == "esc" or sc_id == "ctrl_alt_m") and self.home_overlay:
|
||||||
|
obj.setEnabled(True)
|
||||||
|
if (sc_id == "esc" or sc_id == "ctrl_alt_m") and self.home_overlay is None:
|
||||||
|
obj.setEnabled(False)
|
||||||
|
|
||||||
|
def install_sc(self):
|
||||||
|
self.sc_pool = {}
|
||||||
|
# QAction
|
||||||
|
self.toolBar.ac_switch.setShortcut(QKeySequence("Ctrl+Alt+T"))
|
||||||
|
self.sc_pool["ctrl_alt_t"] = self.toolBar.ac_switch
|
||||||
|
self.toolBar.ac_homepage.setShortcut(QKeySequence("Ctrl+Alt+H"))
|
||||||
|
self.sc_pool["ctrl_alt_h"] = self.toolBar.ac_homepage
|
||||||
|
self.toolBar.ac_setting.setShortcut(QKeySequence("Ctrl+Alt+S"))
|
||||||
|
self.sc_pool["ctrl_alt_s"] = self.toolBar.ac_setting
|
||||||
|
self.toolBar.ac_log.setShortcut(QKeySequence("Ctrl+Alt+L"))
|
||||||
|
self.sc_pool["ctrl_alt_l"] = self.toolBar.ac_log
|
||||||
|
self.toolBar.ac_about.setShortcut(QKeySequence("Ctrl+Alt+A"))
|
||||||
|
self.sc_pool["ctrl_alt_a"] = self.toolBar.ac_about
|
||||||
|
# QShortcut
|
||||||
|
self.sc_f11 = QShortcut(QKeySequence("F11"), self)
|
||||||
|
self.sc_f11.activated.connect(self.shortcut_f11)
|
||||||
|
self.sc_pool["f11"] = self.sc_f11
|
||||||
|
# Subpage
|
||||||
|
self.sc_Esc = QShortcut(QKeySequence("Esc"), self)
|
||||||
|
self.sc_Esc.activated.connect(signal_bus.home_overlay_auth.emit)
|
||||||
|
self.sc_pool["esc"] = self.sc_Esc
|
||||||
|
self.sc_caM = QShortcut(QKeySequence("Ctrl+Alt+M"), self)
|
||||||
|
self.sc_caM.activated.connect(self.toggle_full_screen)
|
||||||
|
self.sc_pool["ctrl_alt_m"] = self.sc_caM
|
||||||
|
|
||||||
def ac_hp(self):
|
def ac_hp(self):
|
||||||
def get_files(dir_path):
|
def get_files(dir_path):
|
||||||
folder = Path(dir_path)
|
folder = Path(dir_path)
|
||||||
files = [p for p in folder.rglob("*") if p.is_file()]
|
files = [p for p in folder.rglob("*") if p.is_file()]
|
||||||
return choice(files), files
|
if files:
|
||||||
|
rand_choice = choice(files)
|
||||||
|
else:
|
||||||
|
rand_choice = ""
|
||||||
|
return rand_choice, files
|
||||||
|
|
||||||
def del_repeat_proverb(proverbs: list):
|
def del_repeat_proverb(proverbs: list):
|
||||||
_proverbs = []
|
_proverbs = []
|
||||||
@@ -67,6 +119,7 @@ class MainWindow(QMainWindow):
|
|||||||
_proverbs.append(proverb)
|
_proverbs.append(proverb)
|
||||||
return _proverbs
|
return _proverbs
|
||||||
|
|
||||||
|
@handle_exception()
|
||||||
def get_resources():
|
def get_resources():
|
||||||
# background image
|
# background image
|
||||||
bing = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8"
|
bing = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8"
|
||||||
@@ -77,33 +130,46 @@ class MainWindow(QMainWindow):
|
|||||||
for file in files:
|
for file in files:
|
||||||
image_names.append(file.name.removesuffix(".jpg"))
|
image_names.append(file.name.removesuffix(".jpg"))
|
||||||
for image in res["images"]:
|
for image in res["images"]:
|
||||||
startdate = image["startdate"]
|
image_name = "-".join([image["startdate"], image["hsh"]])
|
||||||
if startdate in image_names:
|
if image_name in image_names:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
image_url = f"""https://www.bing.com{image["url"]}"""
|
image_url = f"""https://www.bing.com{image["url"]}"""
|
||||||
file = Path(f"{clibs.base_path}/assets/media/bg/{startdate}.jpg")
|
file = Path(f"{clibs.base_path}/assets/media/bg/{image_name}.jpg")
|
||||||
try:
|
req = requests.get(image_url, stream=True, timeout=10)
|
||||||
req = requests.get(image_url, stream=True, timeout=10)
|
with open(file, "wb") as f:
|
||||||
with open(file, "wb") as f:
|
for chunk in req.iter_content(chunk_size=8192):
|
||||||
for chunk in req.iter_content(chunk_size=8192):
|
f.write(chunk)
|
||||||
f.write(chunk)
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
# proverbs
|
# proverbs
|
||||||
hitokoto = "https://v1.hitokoto.cn/"
|
hitokoto = "https://v1.hitokoto.cn/"
|
||||||
proverbs = []
|
proverbs = []
|
||||||
try:
|
proverb_file = Path(f"{clibs.base_path}/assets/media/hitokoto.json")
|
||||||
req = requests.get(hitokoto)
|
req = requests.get(hitokoto)
|
||||||
with open(f"{clibs.base_path}/assets/media/hitokoto.json", mode="rt", encoding="utf-8") as f:
|
if not proverb_file.exists():
|
||||||
proverbs = json.load(f)
|
proverb_file.touch()
|
||||||
proverbs.append(eval(req.text))
|
proverb_file.write_text("[]")
|
||||||
proverbs = del_repeat_proverb(proverbs)
|
with open(proverb_file, "w") as f:
|
||||||
with open(f"{clibs.base_path}/assets/media/hitokoto.json", mode="wt", encoding="utf-8") as f:
|
f.write("[]")
|
||||||
json.dump(proverbs, f, ensure_ascii=False)
|
|
||||||
except Exception as e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
with open(proverb_file, mode="rt", encoding="utf-8") as f:
|
||||||
|
proverbs = json.load(f)
|
||||||
|
proverb = json.loads(req.text)
|
||||||
|
if None in proverb.values():
|
||||||
|
for k, v in proverb.items():
|
||||||
|
if v is None:
|
||||||
|
if v == "from_who":
|
||||||
|
proverb.update({k: "佚名"})
|
||||||
|
elif v == "from":
|
||||||
|
proverb.update({k: "不知道"})
|
||||||
|
else:
|
||||||
|
proverb.update({k: "-"})
|
||||||
|
|
||||||
|
proverbs.append(proverb)
|
||||||
|
proverbs = del_repeat_proverb(proverbs)
|
||||||
|
with open(proverb_file, mode="wt", encoding="utf-8") as f:
|
||||||
|
json.dump(proverbs, f, ensure_ascii=False)
|
||||||
|
|
||||||
|
@handle_exception()
|
||||||
def change_resources():
|
def change_resources():
|
||||||
# avatar
|
# avatar
|
||||||
src, _ = get_files(f"{clibs.base_path}/assets/media/avatar")
|
src, _ = get_files(f"{clibs.base_path}/assets/media/avatar")
|
||||||
@@ -123,11 +189,9 @@ class MainWindow(QMainWindow):
|
|||||||
copy(src, dst)
|
copy(src, dst)
|
||||||
|
|
||||||
def gen_page():
|
def gen_page():
|
||||||
self.set_shortcuts(False)
|
|
||||||
self.home_overlay = WidgetWithBg(parent=self)
|
self.home_overlay = WidgetWithBg(parent=self)
|
||||||
self.home_overlay.on_closed.connect(self.exit_overlay)
|
|
||||||
self.home_overlay.on_full_screen.connect(self.full_screen)
|
|
||||||
self.home_overlay.show()
|
self.home_overlay.show()
|
||||||
|
self.setup_sc(False)
|
||||||
width, height = self.width(), self.height()
|
width, height = self.width(), self.height()
|
||||||
if width > clibs.win_width:
|
if width > clibs.win_width:
|
||||||
self.resize(self.width()-1, self.height()-1)
|
self.resize(self.width()-1, self.height()-1)
|
||||||
@@ -138,44 +202,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.launch_get_resources(get_resources)
|
self.launch_get_resources(get_resources)
|
||||||
gen_page()
|
gen_page()
|
||||||
|
|
||||||
def full_screen(self, flag: bool):
|
def launch_get_resources(self, func, on_anything=None, *args, **kwargs):
|
||||||
if flag == 0:
|
|
||||||
if self.isFullScreen():
|
|
||||||
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
|
|
||||||
self.show()
|
|
||||||
self.showMaximized()
|
|
||||||
else:
|
|
||||||
self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
|
|
||||||
self.showFullScreen()
|
|
||||||
elif flag == 1:
|
|
||||||
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
|
|
||||||
self.show()
|
|
||||||
self.showMaximized()
|
|
||||||
|
|
||||||
def exit_overlay(self):
|
|
||||||
self.set_shortcuts(True)
|
|
||||||
if self.isFullScreen():
|
|
||||||
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
|
|
||||||
self.show()
|
|
||||||
self.showMaximized()
|
|
||||||
|
|
||||||
def set_shortcuts(self, stat: bool = True):
|
|
||||||
if stat:
|
|
||||||
self.ac_homepage.setShortcut(QKeySequence("Ctrl+Alt+H"))
|
|
||||||
# self.ac_settings.setShortcut(QKeySequence("Ctrl+Alt+S"))
|
|
||||||
# self.ac_logs.setShortcut(QKeySequence("Ctrl+Alt+L"))
|
|
||||||
# self.ac_about.setShortcut(QKeySequence("Ctrl+Alt+A"))
|
|
||||||
# self.ac_caging.setShortcut(QKeySequence("Ctrl+Alt+C"))
|
|
||||||
# self.ac_quit.setShortcut(QKeySequence("Ctrl+Alt+Q"))
|
|
||||||
else:
|
|
||||||
self.ac_homepage.setShortcut(QKeySequence())
|
|
||||||
# self.ac_settings.setShortcut(QKeySequence())
|
|
||||||
# self.ac_logs.setShortcut(QKeySequence())
|
|
||||||
# self.ac_about.setShortcut(QKeySequence())
|
|
||||||
# self.ac_caging.setShortcut(QKeySequence())
|
|
||||||
# self.ac_quit.setShortcut(QKeySequence())
|
|
||||||
|
|
||||||
def launch_get_resources(self, func, on_anything: Callable[..., Any] = None, *args, **kwargs):
|
|
||||||
self.td_get_resources = Worker(func, *args, **kwargs)
|
self.td_get_resources = Worker(func, *args, **kwargs)
|
||||||
self.td_get_resources.started.connect(lambda: None)
|
self.td_get_resources.started.connect(lambda: None)
|
||||||
self.td_get_resources.result.connect(lambda: None)
|
self.td_get_resources.result.connect(lambda: None)
|
||||||
@@ -183,6 +210,28 @@ class MainWindow(QMainWindow):
|
|||||||
self.td_get_resources.finished.connect(lambda: None)
|
self.td_get_resources.finished.connect(lambda: None)
|
||||||
self.td_get_resources.start()
|
self.td_get_resources.start()
|
||||||
|
|
||||||
|
def toggle_full_screen(self):
|
||||||
|
if self.isFullScreen():
|
||||||
|
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
|
||||||
|
self.show()
|
||||||
|
self.showMaximized()
|
||||||
|
else:
|
||||||
|
self.setWindowFlags(Qt.WindowType.WindowStaysOnTopHint)
|
||||||
|
self.showFullScreen()
|
||||||
|
|
||||||
|
def exit_overlay(self):
|
||||||
|
self.home_overlay = None
|
||||||
|
self.setup_sc()
|
||||||
|
if self.isFullScreen():
|
||||||
|
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
|
||||||
|
self.show()
|
||||||
|
self.showMaximized()
|
||||||
|
|
||||||
|
def shortcut_f11(self):
|
||||||
|
if not self.home_overlay:
|
||||||
|
self.ac_hp()
|
||||||
|
self.toggle_full_screen()
|
||||||
|
|
||||||
def resizeEvent(self, event: QResizeEvent):
|
def resizeEvent(self, event: QResizeEvent):
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
if self.home_overlay:
|
if self.home_overlay:
|
||||||
@@ -195,7 +244,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
reply = QMessageBox.question(self, "退出", "\n程序可能在运行,确定要退出吗?")
|
reply = QMessageBox.question(self, "退出", "\n程序可能在运行,确定要退出吗?")
|
||||||
if reply == QMessageBox.StandardButton.Yes:
|
if reply == QMessageBox.StandardButton.Yes:
|
||||||
db_operation.db_close(self.conn, self.cursor)
|
db_operation.db_close()
|
||||||
event.accept()
|
event.accept()
|
||||||
else:
|
else:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
|
|||||||
0
toolbox/codes/ui/overlay_page/__init__.py
Normal file
@@ -1,26 +1,42 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from PySide6.QtWidgets import QWidget, QApplication, QSizePolicy, QVBoxLayout, QLabel, QFrame, QHBoxLayout, QLineEdit, QMessageBox
|
from PySide6.QtWidgets import QWidget, QApplication, QSizePolicy, QVBoxLayout, QLabel, QFrame, QHBoxLayout, QLineEdit, QMessageBox
|
||||||
from PySide6.QtGui import QPixmap, QPainter, QFontDatabase, QFont, QBrush, QShortcut, QKeySequence, QColor
|
from PySide6.QtGui import QPixmap, QPainter, QFontDatabase, QFont, QBrush, QColor
|
||||||
from PySide6.QtCore import Qt, QPoint, QDateTime, Signal, QTimer
|
from PySide6.QtCore import Qt, QPoint, QDateTime, Signal, QTimer
|
||||||
from zhdate import ZhDate
|
from zhdate import ZhDate
|
||||||
|
|
||||||
from codes.common import clibs
|
from codes.common import clibs
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.common.qss_reloader import qss_reloader
|
||||||
|
|
||||||
|
|
||||||
class LunarClockLabel(QLabel):
|
class LunarClockLabel(QLabel):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None, flag=0):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setMinimumWidth(350)
|
self.setMinimumWidth(350)
|
||||||
timer = QTimer(self)
|
timer = QTimer(self)
|
||||||
timer.timeout.connect(self.update_time)
|
|
||||||
timer.start(1000)
|
timer.start(1000)
|
||||||
self.update_time()
|
if flag == 0:
|
||||||
|
self.update_date()
|
||||||
|
timer.timeout.connect(self.update_date)
|
||||||
|
else:
|
||||||
|
self.update_time()
|
||||||
|
timer.timeout.connect(self.update_time)
|
||||||
|
|
||||||
|
def update_date(self):
|
||||||
|
dt = QDateTime.currentDateTime()
|
||||||
|
g = dt.date()
|
||||||
|
z = ZhDate.today()
|
||||||
|
week = "一二三四五六日"[g.dayOfWeek() - 1]
|
||||||
|
text = f"{g.year()}年{g.month()}月{g.day()}日 {z.chinese()[5:]} 星期{week}"
|
||||||
|
self.setText(text)
|
||||||
|
|
||||||
def update_time(self):
|
def update_time(self):
|
||||||
dt = QDateTime.currentDateTime()
|
dt = QDateTime.currentDateTime()
|
||||||
g = dt.date()
|
g = dt.date()
|
||||||
z = ZhDate.today()
|
z = ZhDate.today()
|
||||||
week = "一二三四五六日"[g.dayOfWeek() - 1]
|
week = "一二三四五六日"[g.dayOfWeek() - 1]
|
||||||
text = f"{g.year()}年{g.month()}月{g.day()}日 {z.chinese()[5:]} 星期{week} {dt.toString('hh:mm:ss')}"
|
text = f"{dt.toString('hh:mm')}"
|
||||||
self.setText(text)
|
self.setText(text)
|
||||||
|
|
||||||
|
|
||||||
@@ -33,100 +49,97 @@ class DoubleClickLabel(QLabel):
|
|||||||
|
|
||||||
|
|
||||||
class WidgetWithBg(QWidget):
|
class WidgetWithBg(QWidget):
|
||||||
on_closed = Signal()
|
|
||||||
on_full_screen = Signal(int)
|
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.predos()
|
self.pre_do()
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.setup_slot()
|
self.post_do()
|
||||||
|
|
||||||
def predos(self):
|
def pre_do(self):
|
||||||
font_id = QFontDatabase.addApplicationFont(f"{clibs.base_path}/assets/media/font/OldEnglishTextMT/OldEnglishTextMT.ttf")
|
font_id = QFontDatabase.addApplicationFont(f"{clibs.base_path}/assets/media/font/OldEnglishTextMT/OldEnglishTextMT.ttf")
|
||||||
family = QFontDatabase.applicationFontFamilies(font_id)[0]
|
family = QFontDatabase.applicationFontFamilies(font_id)[0]
|
||||||
self.lb_font = QFont(family, 28, QFont.Weight.Medium)
|
self.lb_font = QFont(family, 32, QFont.Weight.Medium)
|
||||||
self.background_pixmap = QPixmap(clibs.bg)
|
self.background_pixmap = QPixmap(clibs.bg)
|
||||||
|
|
||||||
def init_ui(self):
|
def init_ui(self):
|
||||||
|
self.setObjectName("WidgetWithBg")
|
||||||
layout_v = QVBoxLayout()
|
layout_v = QVBoxLayout()
|
||||||
# 最上层的空白区
|
# 最上层的空白区
|
||||||
self.lb_empty_up = QLabel(self)
|
self.lb_empty_up = QLabel(self)
|
||||||
|
self.lb_empty_up.setObjectName("lb_empty_up")
|
||||||
layout_v.addWidget(self.lb_empty_up)
|
layout_v.addWidget(self.lb_empty_up)
|
||||||
# 头像区
|
# 头像区
|
||||||
|
layout_h_1 = QHBoxLayout()
|
||||||
|
layout_h_1.addStretch(1)
|
||||||
self.lb_avatar = DoubleClickLabel(self)
|
self.lb_avatar = DoubleClickLabel(self)
|
||||||
|
self.lb_avatar.setObjectName("lb_avatar")
|
||||||
self.lb_avatar.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_avatar.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
avatar = QPixmap(clibs.avatar)
|
avatar = QPixmap(clibs.avatar)
|
||||||
avatar = self.circle_pixmap(avatar, 200)
|
avatar = self.circle_pixmap(avatar, 200)
|
||||||
self.lb_avatar.setPixmap(avatar)
|
self.lb_avatar.setPixmap(avatar)
|
||||||
self.lb_avatar.setScaledContents(True)
|
self.lb_avatar.setScaledContents(True)
|
||||||
self.lb_avatar.setFixedSize(144, 144)
|
self.lb_avatar.setFixedSize(144, 144)
|
||||||
layout_v.addWidget(self.lb_avatar, alignment=Qt.AlignmentFlag.AlignCenter)
|
layout_h_1.addWidget(self.lb_avatar)
|
||||||
|
self.lb_time = LunarClockLabel(self, flag=1)
|
||||||
|
self.lb_time.setObjectName("lb_time")
|
||||||
|
self.lb_time.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
layout_h_1.addWidget(self.lb_time)
|
||||||
|
layout_h_1.addStretch(1)
|
||||||
|
layout_v.addLayout(layout_h_1)
|
||||||
# 艺术字区
|
# 艺术字区
|
||||||
self.lb_name = QLabel(self)
|
self.lb_name = QLabel(self)
|
||||||
|
self.lb_name.setObjectName("lb_name")
|
||||||
self.lb_name.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_name.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lb_name.setText("Manford Fan · Code Create Life")
|
self.lb_name.setText("Manford Fan · Code Create Life")
|
||||||
self.lb_name.setStyleSheet("color: rgba(255,255,255,255);")
|
|
||||||
self.lb_name.setFont(self.lb_font)
|
self.lb_name.setFont(self.lb_font)
|
||||||
layout_v.addWidget(self.lb_name)
|
layout_v.addWidget(self.lb_name)
|
||||||
# 时间区-左横线
|
# 时间区-左横线
|
||||||
layout_h = QHBoxLayout()
|
layout_h = QHBoxLayout()
|
||||||
self.line_left = QFrame(self)
|
self.line_left = QFrame(self)
|
||||||
|
self.line_left.setObjectName("line_left")
|
||||||
self.line_left.setFrameShape(QFrame.Shape.HLine)
|
self.line_left.setFrameShape(QFrame.Shape.HLine)
|
||||||
self.line_left.setFrameShadow(QFrame.Shadow.Plain)
|
self.line_left.setFrameShadow(QFrame.Shadow.Plain)
|
||||||
self.line_left.setStyleSheet("""
|
|
||||||
QFrame {
|
|
||||||
border: none;
|
|
||||||
background-color: rgba(255, 255, 255, 40);
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||||
self.line_left.setSizePolicy(policy)
|
self.line_left.setSizePolicy(policy)
|
||||||
self.line_left.setLineWidth(1)
|
self.line_left.setLineWidth(1)
|
||||||
self.line_left.setFixedWidth(100)
|
self.line_left.setFixedWidth(100)
|
||||||
# 时间区-右横线
|
# 时间区-右横线
|
||||||
self.line_right = QFrame(self)
|
self.line_right = QFrame(self)
|
||||||
|
self.line_right.setObjectName("line_right")
|
||||||
self.line_right.setFrameShape(QFrame.Shape.HLine)
|
self.line_right.setFrameShape(QFrame.Shape.HLine)
|
||||||
self.line_right.setFrameShadow(QFrame.Shadow.Plain)
|
self.line_right.setFrameShadow(QFrame.Shadow.Plain)
|
||||||
self.line_right.setStyleSheet("""
|
|
||||||
QFrame {
|
|
||||||
border: none;
|
|
||||||
background-color: rgba(255, 255, 255, 40);
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
policy = QSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Fixed)
|
||||||
self.line_right.setSizePolicy(policy)
|
self.line_right.setSizePolicy(policy)
|
||||||
self.line_right.setLineWidth(1)
|
self.line_right.setLineWidth(1)
|
||||||
self.line_right.setFixedWidth(100)
|
self.line_right.setFixedWidth(100)
|
||||||
# 时间区-时间
|
# 时间区-时间
|
||||||
self.lb_time = LunarClockLabel(self)
|
self.lb_date = LunarClockLabel(self)
|
||||||
self.lb_time.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_date.setObjectName("lb_date")
|
||||||
self.lb_time.setStyleSheet("color: rgba(255,255,255,255);")
|
self.lb_date.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lb_time.setFont(QFont("Consolas", 12, QFont.Weight.Bold))
|
|
||||||
layout_h.addStretch(1)
|
layout_h.addStretch(1)
|
||||||
layout_h.addWidget(self.line_left)
|
layout_h.addWidget(self.line_left)
|
||||||
layout_h.addWidget(self.lb_time)
|
layout_h.addWidget(self.lb_date)
|
||||||
layout_h.addWidget(self.line_right)
|
layout_h.addWidget(self.line_right)
|
||||||
layout_h.addStretch(1)
|
layout_h.addStretch(1)
|
||||||
layout_v.addLayout(layout_h)
|
layout_v.addLayout(layout_h)
|
||||||
# layout_v.addWidget(self.line, alignment=Qt.AlignmentFlag.AlignCenter)
|
|
||||||
|
|
||||||
self.lb_proverb = QLabel(self)
|
self.lb_proverb = QLabel(self)
|
||||||
|
self.lb_proverb.setObjectName("lb_proverb")
|
||||||
self.lb_proverb.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
self.lb_proverb.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
self.lb_proverb.setText(clibs.proverb)
|
self.lb_proverb.setText(clibs.proverb)
|
||||||
self.lb_proverb.setStyleSheet("color: rgba(255,255,255,255);")
|
|
||||||
self.lb_proverb.setFont(QFont("Consolas", 14, QFont.Weight.Bold))
|
|
||||||
layout_v.addWidget(self.lb_proverb)
|
layout_v.addWidget(self.lb_proverb)
|
||||||
|
|
||||||
self.le_password = QLineEdit(self)
|
self.le_password = QLineEdit(self)
|
||||||
|
self.le_password.setObjectName("le_password")
|
||||||
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
self.le_password.setFont(QFont("Consolas", 12, QFont.Weight.Normal))
|
# self.le_password.setFont(QFont("Consolas", 12, QFont.Weight.Normal))
|
||||||
self.le_password.setMinimumWidth(300)
|
self.le_password.setMinimumWidth(300)
|
||||||
self.le_password.setFixedHeight(30)
|
self.le_password.setFixedHeight(30)
|
||||||
self.hide_le_password()
|
|
||||||
layout_v.addWidget(self.le_password, alignment=Qt.AlignmentFlag.AlignCenter)
|
layout_v.addWidget(self.le_password, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.hide_le_password()
|
||||||
|
|
||||||
self.lb_empty_down = QLabel(self)
|
self.lb_empty_down = QLabel(self)
|
||||||
|
self.lb_empty_down.setObjectName("lb_empty_down")
|
||||||
layout_v.addWidget(self.lb_empty_down)
|
layout_v.addWidget(self.lb_empty_down)
|
||||||
|
|
||||||
layout_v.setStretch(0, 2) # empty up
|
layout_v.setStretch(0, 2) # empty up
|
||||||
@@ -138,29 +151,31 @@ class WidgetWithBg(QWidget):
|
|||||||
layout_v.setStretch(6, 2) # empty down
|
layout_v.setStretch(6, 2) # empty down
|
||||||
self.setLayout(layout_v)
|
self.setLayout(layout_v)
|
||||||
|
|
||||||
def setup_slot(self):
|
def post_do(self):
|
||||||
self.lb_avatar.doubleClicked.connect(self.auth_show)
|
qss_reloader.register(clibs.qss_home_overlay, self)
|
||||||
self.le_password.returnPressed.connect(self.validate_password)
|
self.setup_slot()
|
||||||
QShortcut(QKeySequence("Ctrl+Alt+L"), self, self.auth_show)
|
|
||||||
QShortcut(QKeySequence("Ctrl+Alt+S"), self, lambda: self.on_full_screen.emit(0))
|
|
||||||
# QShortcut(QKeySequence("Esc"), self).activated.connect(lambda: self.on_full_screen.emit(1))
|
|
||||||
|
|
||||||
def auth_show(self):
|
def setup_slot(self):
|
||||||
if self.input_hide:
|
self.lb_avatar.doubleClicked.connect(self.toggle_auth_show)
|
||||||
self.show_le_password()
|
self.le_password.returnPressed.connect(self.validate_password)
|
||||||
else:
|
signal_bus.home_overlay_auth.connect(self.toggle_auth_show)
|
||||||
|
|
||||||
|
def toggle_auth_show(self):
|
||||||
|
if self.le_is_visible:
|
||||||
self.hide_le_password()
|
self.hide_le_password()
|
||||||
|
else:
|
||||||
|
self.show_le_password()
|
||||||
|
|
||||||
def show_le_password(self):
|
def show_le_password(self):
|
||||||
self.input_hide = False
|
self.le_is_visible = True
|
||||||
self.le_password.clear()
|
self.le_password.clear()
|
||||||
self.le_password.setPlaceholderText("Password")
|
self.le_password.setPlaceholderText("Password")
|
||||||
self.le_password.setStyleSheet("")
|
self.le_password.setStyleSheet("border:none; border-radius: 5px;")
|
||||||
self.le_password.setDisabled(False)
|
self.le_password.setDisabled(False)
|
||||||
self.le_password.setFocus()
|
self.le_password.setFocus()
|
||||||
|
|
||||||
def hide_le_password(self):
|
def hide_le_password(self):
|
||||||
self.input_hide = True
|
self.le_is_visible = False
|
||||||
self.le_password.clear()
|
self.le_password.clear()
|
||||||
self.le_password.setDisabled(True)
|
self.le_password.setDisabled(True)
|
||||||
self.le_password.setPlaceholderText("")
|
self.le_password.setPlaceholderText("")
|
||||||
@@ -169,14 +184,17 @@ class WidgetWithBg(QWidget):
|
|||||||
def validate_password(self):
|
def validate_password(self):
|
||||||
password = self.le_password.text()
|
password = self.le_password.text()
|
||||||
if password == clibs.password:
|
if password == clibs.password:
|
||||||
self.on_closed.emit()
|
self.hide_le_password()
|
||||||
self.close()
|
signal_bus.home_overlay_close.emit()
|
||||||
|
self.deleteLater()
|
||||||
|
return True
|
||||||
elif password == "":
|
elif password == "":
|
||||||
self.hide_le_password()
|
self.hide_le_password()
|
||||||
return
|
return False
|
||||||
else:
|
else:
|
||||||
QMessageBox.critical(self, "错误", "密码不正确,请确认后重新输入!")
|
# QMessageBox.critical(self, "错误", "密码不正确,请确认后重新输入!")
|
||||||
self.show_le_password()
|
self.show_le_password()
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def circle_pixmap(src: QPixmap, diameter: int) -> QPixmap:
|
def circle_pixmap(src: QPixmap, diameter: int) -> QPixmap:
|
||||||
0
toolbox/codes/ui/stacked_pages/__init__.py
Normal file
27
toolbox/codes/ui/stacked_pages/w01_setting.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel
|
||||||
|
|
||||||
|
from codes.common import clibs
|
||||||
|
|
||||||
|
|
||||||
|
class W01Setting(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.ui_init()
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
|
||||||
|
self.lb_test.setObjectName("lb_test")
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
286
toolbox/codes/ui/stacked_pages/w08_log.py
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel, QMessageBox, QVBoxLayout, QTreeWidget, QHeaderView, QHBoxLayout, QPushButton, QFrame, QLineEdit, QCheckBox, QTreeWidgetItem, QDialog, QPlainTextEdit, QApplication
|
||||||
|
from PySide6.QtCore import Qt, Signal, QSize
|
||||||
|
from PySide6.QtGui import QColor, QIcon, QKeySequence, QIntValidator, QShortcut
|
||||||
|
|
||||||
|
from codes.common.signal_bus import signal_bus
|
||||||
|
from codes.common import db_operation
|
||||||
|
from codes.common import clibs
|
||||||
|
from codes.common.qss_reloader import qss_reloader
|
||||||
|
|
||||||
|
|
||||||
|
class LogDialog(QDialog):
|
||||||
|
def __init__(self, content, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("日志详情")
|
||||||
|
self.setGeometry(100, 100, 700, 300)
|
||||||
|
self.center_on_screen()
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
self.plain_text_edit = QPlainTextEdit()
|
||||||
|
self.plain_text_edit.setReadOnly(True)
|
||||||
|
self.plain_text_edit.setPlainText(content)
|
||||||
|
layout.addWidget(self.plain_text_edit)
|
||||||
|
|
||||||
|
def center_on_screen(self):
|
||||||
|
screen_geometry = QApplication.primaryScreen().geometry()
|
||||||
|
screen_width = screen_geometry.width()
|
||||||
|
screen_height = screen_geometry.height()
|
||||||
|
dialog_width = self.width()
|
||||||
|
dialog_height = self.height()
|
||||||
|
x = (screen_width - dialog_width) // 2
|
||||||
|
y = (screen_height - dialog_height) // 2
|
||||||
|
self.move(x, y)
|
||||||
|
|
||||||
|
|
||||||
|
class PageNumberInput(QDialog):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.init_ui()
|
||||||
|
self.setup_slot()
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
QShortcut(QKeySequence("Esc"), self).activated.connect(self.reject)
|
||||||
|
self.le_page_number.returnPressed.connect(self.accept)
|
||||||
|
self.le_page_number.returnPressed.connect(self.get_page_number)
|
||||||
|
|
||||||
|
def init_ui(self):
|
||||||
|
self.setMinimumSize(300,100)
|
||||||
|
self.setMaximumSize(400,120)
|
||||||
|
self.resize(300, 100)
|
||||||
|
self.setWindowIcon(QIcon(clibs.icon))
|
||||||
|
self.setWindowTitle("输入页码")
|
||||||
|
layout_h = QHBoxLayout()
|
||||||
|
layout_h.addStretch(1)
|
||||||
|
self.le_page_number = QLineEdit(self)
|
||||||
|
self.le_page_number.setText("1")
|
||||||
|
self.le_page_number.selectAll()
|
||||||
|
self.le_page_number.setValidator(QIntValidator(0, 9999999, self))
|
||||||
|
layout_h.addWidget(self.le_page_number, stretch=4)
|
||||||
|
layout_h.addStretch(1)
|
||||||
|
self.setLayout(layout_h)
|
||||||
|
|
||||||
|
def get_page_number(self):
|
||||||
|
text = self.le_page_number.text()
|
||||||
|
return 1 if text == 0 else int(text)
|
||||||
|
|
||||||
|
|
||||||
|
class ClickableLabel(QLabel):
|
||||||
|
clicked = Signal()
|
||||||
|
|
||||||
|
def mousePressEvent(self, event):
|
||||||
|
if event.button() == Qt.MouseButton.LeftButton:
|
||||||
|
self.clicked.emit()
|
||||||
|
super().mousePressEvent(event)
|
||||||
|
|
||||||
|
|
||||||
|
class W08Log(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.pre_do()
|
||||||
|
self.ui_init()
|
||||||
|
self.post_do()
|
||||||
|
|
||||||
|
def pre_do(self):
|
||||||
|
self.records, self.len_records = "", ""
|
||||||
|
self.is_searching = False
|
||||||
|
self.max_item_number = clibs.config["log_number_per_page"]
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.setObjectName("W08Log")
|
||||||
|
layout_v = QVBoxLayout(self)
|
||||||
|
self.treeW = QTreeWidget()
|
||||||
|
self.treeW.setObjectName("treeW")
|
||||||
|
self.treeW.setHeaderLabels(["ID", "时间戳", "告警级别", "模块信息", "告警内容"])
|
||||||
|
self.treeW.headerItem().setTextAlignment(0, Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||||
|
self.header = self.treeW.header()
|
||||||
|
self.header.setObjectName("header")
|
||||||
|
for i in range(self.treeW.columnCount()):
|
||||||
|
self.header.setSectionResizeMode(i, QHeaderView.ResizeMode.ResizeToContents)
|
||||||
|
layout_v.addWidget(self.treeW, stretch=9)
|
||||||
|
|
||||||
|
layout_h = QHBoxLayout()
|
||||||
|
self.pb_previous = QPushButton("上一页")
|
||||||
|
self.pb_previous.setObjectName("pb_previous")
|
||||||
|
layout_h.addWidget(self.pb_previous, stretch=1)
|
||||||
|
|
||||||
|
self.lb_page = ClickableLabel("999999/999999")
|
||||||
|
self.lb_page.setObjectName("lb_page")
|
||||||
|
self.lb_page.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||||
|
self.lb_page.setMinimumWidth(144)
|
||||||
|
layout_h.addWidget(self.lb_page, stretch=2)
|
||||||
|
|
||||||
|
self.pb_next = QPushButton("下一页")
|
||||||
|
self.pb_next.setObjectName("pb_next")
|
||||||
|
layout_h.addWidget(self.pb_next, stretch=1)
|
||||||
|
|
||||||
|
layout_h.addStretch(9)
|
||||||
|
|
||||||
|
self.frame_checkbox = QFrame()
|
||||||
|
self.frame_checkbox.setObjectName("frame_checkbox")
|
||||||
|
layout_h_checkbox = QHBoxLayout()
|
||||||
|
self.box_info = QCheckBox("通知", parent=self.frame_checkbox)
|
||||||
|
self.box_info.setObjectName("box_info")
|
||||||
|
self.box_info.setChecked(True)
|
||||||
|
layout_h_checkbox.addWidget(self.box_info, stretch=1)
|
||||||
|
self.box_warning = QCheckBox("告警", parent=self.frame_checkbox)
|
||||||
|
self.box_warning.setObjectName("box_warning")
|
||||||
|
self.box_warning.setChecked(True)
|
||||||
|
layout_h_checkbox.addWidget(self.box_warning, stretch=1)
|
||||||
|
self.box_error = QCheckBox("错误", parent=self.frame_checkbox)
|
||||||
|
self.box_error.setObjectName("box_error")
|
||||||
|
self.box_error.setChecked(True)
|
||||||
|
layout_h_checkbox.addWidget(self.box_error, stretch=1)
|
||||||
|
self.box_exception = QCheckBox("异常", parent=self.frame_checkbox)
|
||||||
|
self.box_exception.setObjectName("box_exception")
|
||||||
|
self.box_exception.setChecked(True)
|
||||||
|
layout_h_checkbox.addWidget(self.box_exception, stretch=1)
|
||||||
|
self.box_unknown = QCheckBox("未知", parent=self.frame_checkbox)
|
||||||
|
self.box_unknown.setObjectName("box_unknown")
|
||||||
|
self.box_unknown.setChecked(True)
|
||||||
|
layout_h_checkbox.addWidget(self.box_unknown, stretch=1)
|
||||||
|
layout_h.addLayout(layout_h_checkbox, stretch=4)
|
||||||
|
|
||||||
|
self.le_search = QLineEdit()
|
||||||
|
self.le_search.setObjectName("le_search")
|
||||||
|
self.le_search.setPlaceholderText("告警内容")
|
||||||
|
self.le_search.setMinimumWidth(300)
|
||||||
|
layout_h.addWidget(self.le_search, stretch=5)
|
||||||
|
|
||||||
|
self.pb_search = QPushButton("查找")
|
||||||
|
self.pb_search.setObjectName("pb_search")
|
||||||
|
layout_h.addWidget(self.pb_search, stretch=1)
|
||||||
|
|
||||||
|
layout_v.addLayout(layout_h, stretch=1)
|
||||||
|
|
||||||
|
self.setLayout(layout_v)
|
||||||
|
|
||||||
|
def post_do(self):
|
||||||
|
qss_reloader.register(clibs.qss_w08_log, self)
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
self.treeW.itemDoubleClicked.connect(self.show_single_log)
|
||||||
|
self.pb_previous.clicked.connect(self.previous_page)
|
||||||
|
self.pb_next.clicked.connect(self.next_page)
|
||||||
|
self.pb_search.clicked.connect(self.search_page)
|
||||||
|
self.le_search.returnPressed.connect(self.search_page)
|
||||||
|
self.lb_page.clicked.connect(self.goto_page)
|
||||||
|
signal_bus.stacked_page_switch_log.connect(self.show_latest_page)
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def previous_page(self):
|
||||||
|
if not self.is_searching:
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(None)
|
||||||
|
current, total = self.lb_page.text().split("/")
|
||||||
|
page_number = int(current) - 1
|
||||||
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
||||||
|
|
||||||
|
def next_page(self):
|
||||||
|
if not self.is_searching:
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(None)
|
||||||
|
current, total = self.lb_page.text().split("/")
|
||||||
|
page_number = int(current) + 1
|
||||||
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
||||||
|
|
||||||
|
def search_page(self):
|
||||||
|
filters = {"info": self.box_info.isChecked(), "warning": self.box_warning.isChecked(), "error": self.box_error.isChecked(), "exception": self.box_exception.isChecked(), "unknown": self.box_unknown.isChecked()}
|
||||||
|
search_text = self.le_search.text().strip()
|
||||||
|
flag, levels = False, []
|
||||||
|
for level, enable in filters.items():
|
||||||
|
if not enable:
|
||||||
|
continue
|
||||||
|
flag = True
|
||||||
|
levels.append(level)
|
||||||
|
|
||||||
|
if not flag:
|
||||||
|
QMessageBox().warning(None, "警告", "至少选择一个过滤器!")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(levels)
|
||||||
|
if search_text:
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(search_text, self.records)
|
||||||
|
|
||||||
|
self.is_searching = True
|
||||||
|
self.show_page(self.records, self.len_records, page_number=None)
|
||||||
|
|
||||||
|
def goto_page(self):
|
||||||
|
dlg = PageNumberInput()
|
||||||
|
if dlg.exec() != 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
page_number = dlg.get_page_number()
|
||||||
|
if not self.is_searching:
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(None)
|
||||||
|
self.show_page(self.records, self.len_records, page_number=page_number)
|
||||||
|
|
||||||
|
def show_latest_page(self):
|
||||||
|
self.records, self.len_records = db_operation.db_query_logs(None)
|
||||||
|
self.is_searching = False
|
||||||
|
self.box_info.setChecked(True)
|
||||||
|
self.box_warning.setChecked(True)
|
||||||
|
self.box_error.setChecked(True)
|
||||||
|
self.box_exception.setChecked(True)
|
||||||
|
self.box_unknown.setChecked(True)
|
||||||
|
self.le_search.clear()
|
||||||
|
self.show_page(self.records, self.len_records, page_number=None)
|
||||||
|
|
||||||
|
def show_page(self, records, len_records, page_number: int | None):
|
||||||
|
if len_records == 0:
|
||||||
|
self.treeW.clear()
|
||||||
|
return
|
||||||
|
|
||||||
|
remainder = len_records % self.max_item_number
|
||||||
|
total = len_records // self.max_item_number + 1 if remainder else len_records // self.max_item_number
|
||||||
|
if page_number is None:
|
||||||
|
current = total
|
||||||
|
elif page_number <= 0:
|
||||||
|
current = 1
|
||||||
|
elif page_number < total:
|
||||||
|
current = page_number
|
||||||
|
else:
|
||||||
|
current = total
|
||||||
|
self.lb_page.setText(f"{current}/{total}")
|
||||||
|
|
||||||
|
if current == 1:
|
||||||
|
idx_start = 0
|
||||||
|
idx_end = self.max_item_number if len_records >= self.max_item_number else len_records
|
||||||
|
elif current == total:
|
||||||
|
remainder = len_records % self.max_item_number
|
||||||
|
idx_start = len_records - remainder if remainder else len_records - self.max_item_number
|
||||||
|
idx_end = len_records
|
||||||
|
else:
|
||||||
|
idx_start = self.max_item_number * (current-1)
|
||||||
|
idx_end = self.max_item_number * current
|
||||||
|
|
||||||
|
self.treeW.clear()
|
||||||
|
self.treeW.setUniformRowHeights(True)
|
||||||
|
for record in records[idx_start:idx_end]:
|
||||||
|
record = [str(_) for _ in record]
|
||||||
|
item = QTreeWidgetItem(self.treeW, record)
|
||||||
|
colors = {
|
||||||
|
"info": QColor(255, 255, 255), # 白色
|
||||||
|
"warning": QColor(244, 164, 96), # 棕橙色
|
||||||
|
"error": QColor(205, 92, 92), # 印度红
|
||||||
|
"exception": QColor(70, 130, 180), # 钢蓝色
|
||||||
|
"unknown": QColor(190, 190, 190) # 灰色
|
||||||
|
}
|
||||||
|
# colors = {"info": "#FFFFFF", "warning": "#F4A460", "error": "#CD5C5C", "exception": "#4682B4", "unknown": "#BEBEBE"}
|
||||||
|
level = record[2]
|
||||||
|
color = colors.get(level, QColor(255, 255, 255))
|
||||||
|
for col in range(self.treeW.columnCount()):
|
||||||
|
item.setBackground(col, color)
|
||||||
|
self.treeW.addTopLevelItem(item)
|
||||||
|
self.treeW.scrollToBottom()
|
||||||
|
|
||||||
|
def show_single_log(self, item, column):
|
||||||
|
log_id = f"id = {item.text(0)}"
|
||||||
|
log_ts = f"ts = {item.text(1)}"
|
||||||
|
log_level = f"level = {item.text(2)}"
|
||||||
|
log_module = f"module = {item.text(3)}\n"
|
||||||
|
deco_line = "=" * 40
|
||||||
|
log_msg = item.text(4)
|
||||||
|
content = "\n".join([log_id, log_ts, log_level, log_module, deco_line, log_msg])
|
||||||
|
dialog = LogDialog(content, self)
|
||||||
|
dialog.exec()
|
||||||
25
toolbox/codes/ui/stacked_pages/w09_about.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel
|
||||||
|
|
||||||
|
|
||||||
|
class W09About(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.ui_init()
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
|
||||||
|
self.lb_test.setObjectName("lb_test")
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
25
toolbox/codes/ui/stacked_pages/w10_practical.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel
|
||||||
|
|
||||||
|
|
||||||
|
class W10Practical(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.ui_init()
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
|
||||||
|
self.lb_test.setObjectName("lb_test")
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
25
toolbox/codes/ui/stacked_pages/w20_efficiency.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel
|
||||||
|
|
||||||
|
|
||||||
|
class W20Efficiency(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.ui_init()
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
|
||||||
|
self.lb_test.setObjectName("lb_test")
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
25
toolbox/codes/ui/stacked_pages/w30_financial.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from PySide6.QtWidgets import QWidget, QLabel
|
||||||
|
|
||||||
|
|
||||||
|
class W30Financial(QWidget):
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.predos()
|
||||||
|
self.ui_init()
|
||||||
|
self.setup_slot()
|
||||||
|
self.setup_sc()
|
||||||
|
|
||||||
|
def predos(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def ui_init(self):
|
||||||
|
self.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
|
||||||
|
self.lb_test.setObjectName("lb_test")
|
||||||
|
|
||||||
|
def setup_slot(self):
|
||||||
|
...
|
||||||
|
|
||||||
|
def setup_sc(self):
|
||||||
|
...
|
||||||
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>Form</class>
|
|
||||||
<widget class="QWidget" name="Form">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>602</width>
|
|
||||||
<height>376</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Form</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>125</width>
|
|
||||||
<height>125</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>125</width>
|
|
||||||
<height>125</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="pixmap">
|
|
||||||
<pixmap>../../assets/media/avatar.png</pixmap>
|
|
||||||
</property>
|
|
||||||
<property name="scaledContents">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Manford Fan · Code Create Life</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="minimumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>400</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
<property name="layoutDirection">
|
|
||||||
<enum>Qt::LayoutDirection::LeftToRight</enum>
|
|
||||||
</property>
|
|
||||||
<property name="autoFillBackground">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Shadow::Sunken</enum>
|
|
||||||
</property>
|
|
||||||
<property name="midLineWidth">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Orientation::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="frameShadow">
|
|
||||||
<enum>QFrame::Shadow::Sunken</enum>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>memo</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,9">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="lineEdit">
|
|
||||||
<property name="maximumSize">
|
|
||||||
<size>
|
|
||||||
<width>200</width>
|
|
||||||
<height>16777215</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string>TextLabel</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections/>
|
|
||||||
</ui>
|
|
||||||
@@ -1,26 +1,18 @@
|
|||||||
import sys
|
import sys
|
||||||
import json
|
from pathlib import Path
|
||||||
from PySide6.QtWidgets import QMainWindow, QApplication
|
|
||||||
|
|
||||||
from codes.ui import login_ui, main_ui
|
from PySide6.QtWidgets import QApplication
|
||||||
|
|
||||||
|
from codes.ui.main_ui import MainWindow
|
||||||
|
from codes.ui.login_ui import LoginWindow
|
||||||
from codes.common import clibs
|
from codes.common import clibs
|
||||||
|
|
||||||
|
|
||||||
class LoginWindow(login_ui.LoginWindow):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
class MainWindow(main_ui.MainWindow):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
with open(f"{clibs.base_path}/assets/conf/config.json", mode="rt", encoding="utf-8") as f:
|
clibs.config = eval(Path(f"{clibs.base_path}/assets/conf/config.json").read_text(encoding="utf-8"))
|
||||||
clibs.account = json.load(f)
|
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
window = LoginWindow()
|
# window = LoginWindow()
|
||||||
# window = MainWindow()
|
window = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
sys.exit(app.exec())
|
sys.exit(app.exec())
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# 工具箱
|
||||||
|
|
||||||
|
## 功能
|
||||||
|
|
||||||
|
### 计算器
|
||||||
|
|
||||||
|
### 颜色板
|
||||||
|
|
||||||
|
### 时间转换
|
||||||
|
|
||||||
|
### ASCII码速查
|
||||||
|
|
||||||
|
### 账本
|
||||||
|
|
||||||
|
### 待办提醒
|
||||||
|
|
||||||
|
### 密码
|
||||||
|
|
||||||
|
## TODOs
|
||||||
|
|
||||||
|
### 自定义快捷键
|
||||||
|
|
||||||
|
### 修改密码
|
||||||
|
|
||||||
|
### 自定义图标
|
||||||
|
|
||||||
|
|
||||||
|
```commandline
|
||||||
|
pyinstaller --noconfirm --onedir --windowed --icon "D:\Syncthing\company\S-Program\Projects\toolbox\assets\media\icon.ico" --name "toolbox" --contents-directory "resource" --clean --log-level "WARN" --optimize "2" --add-data "D:\Syncthing\company\S-Program\Projects\toolbox\assets;assets/" "D:\Syncthing\company\S-Program\Projects\toolbox\main.py"
|
||||||
|
```
|
||||||