总线通信架构整改完成

This commit is contained in:
2025-09-27 19:03:05 +08:00
parent c7c0d15545
commit 1c47497fc2
272 changed files with 592 additions and 304 deletions

View File

@@ -4,8 +4,9 @@ import atexit
base_path = Path(__file__).resolve().parent.parent.parent
db_file = base_path / "assets/database/toolbox.db"
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]
username, password = "", ""
avatar = f"{base_path}/assets/media/avatar.jpg"
@@ -13,7 +14,7 @@ proverb = "佛曰Time will say~"
bg = f"{base_path}/assets/media/bg.jpg"
win_width, win_height = 1100, 500
conn, cursor = None, None
listW_items = ["实用工具", "效率提升", "财务分析"]
listW_items = {"实用工具": "w10_practical", "效率提升": "w20_efficiency", "财务分析": "w30_financial"}
def delete_files_in_directory(directory):

View File

@@ -1,18 +1,23 @@
import sqlite3
import time
from inspect import currentframe
from codes.common import clibs
def db_init(db_file):
conn = sqlite3.connect(db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=10.0)
cursor = conn.cursor()
cursor.execute("PRAGMA journal_mode=wal")
cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
cursor.execute("PRAGMA synchronous=normal")
cursor.execute("PRAGMA temp_store=memory")
cursor.execute("PRAGMA mmap_size=30000000000")
cursor.execute("PRAGMA cache_size=200000")
cursor.execute(
def db_init():
if clibs.db_file.exists():
return
clibs.conn = sqlite3.connect(clibs.db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=10.0)
clibs.cursor = clibs.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")
clibs.cursor.execute(
"""
create table if not exists logs(
id integer primary key autoincrement,
@@ -23,7 +28,7 @@ def db_init(db_file):
)
"""
)
cursor.execute(
clibs.cursor.execute(
"""
create table if not exists users(
id integer primary key autoincrement,
@@ -34,8 +39,8 @@ def db_init(db_file):
)
"""
)
cursor.close()
conn.close()
db_write_logs("数据库初始化成功!", "login_ui")
db_close()
def db_lock(func):
def wrapper(*args, **kwargs):
@@ -49,28 +54,22 @@ def db_lock(func):
def db_backup():
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"
if not (db_file.exists() and db_file.is_file()):
db_init(db_file)
else:
db_file_backup.write_bytes(db_file.read_bytes())
db_dir = clibs.base_path / "assets/database"
db_list = [db for db in db_dir.glob("*.db")]
for db in sorted(db_list)[:-clibs.account["maximum_db_number"]]:
db.unlink()
db_file_backup.write_bytes(clibs.db_file.read_bytes())
db_dir = clibs.base_path / "assets/database"
db_list = [db for db in db_dir.glob("*.db")]
for db in sorted(db_list)[:-clibs.config["maximum_db_number"]]:
db.unlink()
def db_conn():
db_file = clibs.base_path / "assets/database/toolbox.db"
conn = sqlite3.connect(db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=3.0)
cursor = conn.cursor()
cursor.execute("PRAGMA journal_mode=wal")
cursor.execute("PRAGMA wal_checkpoint=TRUNCATE")
cursor.execute("PRAGMA synchronous=normal")
cursor.execute("PRAGMA temp_store=memory")
cursor.execute("PRAGMA mmap_size=30000000000")
cursor.execute("PRAGMA cache_size=200000")
return conn, cursor
clibs.conn = sqlite3.connect(clibs.db_file, isolation_level=None, check_same_thread=False, cached_statements=2048, timeout=3.0)
clibs.cursor = clibs.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
def db_close():
@@ -81,9 +80,16 @@ def db_close():
clibs.conn, clibs.cursor = None, None
@db_lock
def db_write_logs(content, module, level="info"):
if level.lower() not in ["debug", "info", "warning", "error", "exception"]:
def db_write_logs(content, module="", level="info"):
if module == "":
frame = currentframe().f_back
module_name = frame.f_globals["__name__"]
line_no = frame.f_lineno
module = f"{module_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))
@db_lock

View File

@@ -1,14 +1,19 @@
from functools import wraps
from codes.common import db_operation
from inspect import getfile
from pathlib import Path
def handle_exception(module, stop: bool = False):
def handle_exception(stop: bool = False):
def exceptions(func):
module = Path(getfile(func)).stem
func_name = func.__name__
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
db_operation.db_write_logs(e, module, "exception")
db_operation.db_write_logs(str(e), "@".join([func_name, module]), "exception")
if stop:
raise e
return wrapper

View File

@@ -0,0 +1,26 @@
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) # 设置打开侧边栏后的初始页面
qa_stacked_page_switch = Signal(str) # 切换stacked widget页面
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()

View File

View File

View File

@@ -0,0 +1,45 @@
from PySide6.QtGui import QFocusEvent
from PySide6.QtWidgets import QListWidget, QListWidgetItem
from PySide6.QtCore import Qt, QEvent
from codes.common import clibs
from codes.common.signal_bus import signal_bus
class SListWidget(QListWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.predos()
self.init_ui()
self.setup_slot()
def predos(self):
...
def init_ui(self):
for item in clibs.listW_items:
_ = QListWidgetItem(item)
_.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
self.addItem(_)
def setup_slot(self):
self.itemClicked.connect(self.item_clicked)
signal_bus.qa_stacked_page_switch.connect(self.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 focusOutEvent(self, event: QFocusEvent):
self.clearSelection()
super().focusOutEvent(event)

View File

@@ -0,0 +1,62 @@
from pathlib import Path
import importlib.util
from PySide6.QtWidgets import QStackedWidget, QWidget, QLabel
from PySide6.QtCore import Qt
from codes.common import clibs
from codes.common.signal_bus import signal_bus
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 = {}
def init_ui(self):
# stacked widget 1x: 10为一级按钮页其余为二级按钮页2-9同理 | 0x. 日志/设置/关于等页面
self.load_pages()
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.qa_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)
def load_pages(self):
def to_camel(snake: str) -> str:
# w01_setting -> W01Setting
return "".join(word.capitalize() for word in snake.split('_'))
def instantiate(pyFile: Path, className: str) -> QWidget | None:
try:
spec = importlib.util.spec_from_file_location(pyFile.stem, pyFile)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
cls = getattr(module, className)
if issubclass(cls, QWidget):
return cls()
except Exception as e:
print(f"加载 {pyFile} 失败: {e}")
pages_dir = clibs.base_path / "codes/ui/stacked_pages/"
for py_file in pages_dir.glob("w*.py"):
page_id = py_file.stem # w01_setting
class_name = to_camel(page_id) # W01Setting
widget = instantiate(py_file, class_name)
if widget:
widget.setObjectName(page_id) # 用于 findChild / 切换
self.addWidget(widget)
self.page_list[page_id] = widget

View File

@@ -0,0 +1,110 @@
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
class SToolBar(QToolBar):
def __init__(self, parent=None):
super().__init__(parent)
self.predos()
self.init_ui()
self.setup_slot()
def predos(self):
...
def init_ui(self):
# switch
self.on_icon = QIcon(f"{clibs.base_path}/assets/media/switch_on.png") # 开状态图标
self.off_icon = QIcon(f"{clibs.base_path}/assets/media/switch_off.png") # 关状态图标
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.ac_switch.setObjectName("acSwitch")
self.setIconSize(QSize(30, 30)) # ← 一行决定图标像素
self.addAction(self.ac_switch)
btn = self.widgetForAction(self.ac_switch) # 取出实际 QToolButton
btn.setStyleSheet("""
QToolButton {
background: transparent;
border: none;
outline: none;
}
/* 所有伪状态都透明,不让 Qt 画实心矩形 */
QToolButton::hover,
QToolButton::pressed,
QToolButton::checked {
background: transparent;
border: none;
outline: none;
}
""")
# 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 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_stacked_page_switch.connect(self.change2hide)
def ac_sw(self, checked: bool):
self.ac_switch.setIcon(self.on_icon if checked else self.off_icon)
print(f"checked: {checked}")
signal_bus.list_widget_on_off.emit(checked)
def ac_hp(self):
signal_bus.home_overlay_trigger.emit()
def ac_sp(self):
signal_bus.qa_stacked_page_switch.emit("w01_setting")
def ac_lp(self):
signal_bus.qa_stacked_page_switch.emit("w08_log")
def ac_ap(self):
signal_bus.qa_stacked_page_switch.emit("w09_about")
def change2hide(self):
self.ac_switch.setChecked(False)
self.ac_switch.setIcon(self.off_icon)

View File

@@ -1,19 +1,23 @@
import sys
from pathlib import Path
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.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut
from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QLabel, QTabWidget, QLineEdit, QMessageBox, QSizePolicy
from PySide6.QtCore import Qt, QSize
from PySide6.QtGui import QFont, QIcon, QPixmap, QShortcut
from codes.common import clibs, db_operation
from codes.common.secure_encrypt import PassCipher
from codes.ui import main_ui
from codes.common.exception_handler import handle_exception
class LoginWindow(QWidget):
def __init__(self):
super().__init__()
self.predos()
self.init_ui()
self.setup_slot()
self.predos()
self.le_username.setFocus()
def predos(self):
db_operation.db_init()
db_operation.db_conn()
def init_ui(self):
self.setMinimumSize(420, 200)
@@ -23,21 +27,21 @@ class LoginWindow(QWidget):
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
self.setFont(QFont("Consolas", 14))
self.layout_outter = QHBoxLayout()
self.layout_outer = QHBoxLayout()
self.lb_logo = QLabel()
self.lb_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
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.setFixedSize(QSize(120, 120))
self.lb_logo.setScaledContents(True)
self.layout_outter.addWidget(self.lb_logo)
self.layout_outer.addWidget(self.lb_logo)
self.tabW_login = QTabWidget()
self.tab_login = QWidget()
self.tabW_login.addTab(self.tab_login, "登录")
self.tab_register = QWidget()
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()
@@ -110,7 +114,8 @@ class LoginWindow(QWidget):
self.layout_V_user_pass_reg.addLayout(self.layout_H_button_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 setup_slot(self):
self.tabW_login.currentChanged.connect(self.onChange_tabW)
@@ -118,15 +123,9 @@ class LoginWindow(QWidget):
self.btn_cancel.clicked.connect(self.close)
self.btn_login_reg.clicked.connect(self.register_check)
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_reg_confirm.returnPressed.connect(self.register_check)
def predos(self):
self.m = Path(__file__).stem
db_file = clibs.base_path / "assets/database/toolbox.db"
if not db_file.exists():
db_operation.db_init(db_file)
QShortcut("Esc", self).activated.connect(self.close)
def onChange_tabW(self):
text = self.tabW_login.tabText(self.tabW_login.currentIndex())
@@ -139,26 +138,25 @@ class LoginWindow(QWidget):
self.le_password_reg.clear()
self.le_password_reg_confirm.clear()
self.le_username_reg.setFocus()
else:
raise Exception(f"Unknown TabWidget Name: {text}")
@handle_exception()
def login_check(self):
def login_failed(exception: bool = False):
def login_failed(flag: int = 0):
reason = {-1: "用户名或密码为空", 0: "用户名未注册", 1: "解迷成功,密码错误", 2: "解迷失败,密码错误", 3: "数据库中有重复的用户名"}
self.le_username.clear()
self.le_password.clear()
self.le_username.setFocus()
if not exception:
QMessageBox.critical(self, "错误", "账号或密码错误,请重新输入!")
else:
QMessageBox.critical(self, "错误", "账号重复,需检查数据库,更正后再尝试登录!")
QMessageBox.critical(self, "错误", "账号或密码错误,请重新输入!")
raise Exception(f"username:{username} login failed, reason: {reason[flag]}")
def validate_login():
nonlocal username, password
clibs.conn, clibs.cursor = db_operation.db_conn()
record = db_operation.db_query_users(username)
if username == "" or password == "":
login_failed(flag=-1)
if len(record) == 0:
login_failed()
return False
login_failed(flag=0)
elif len(record) == 1:
keys = ["id", "timestamp", "username", "password", "salt"]
login_info = dict(zip(keys, record[0]))
@@ -167,52 +165,46 @@ class LoginWindow(QWidget):
try:
decrypt_password = cipher.decrypt(login_info["password"])
if password != decrypt_password:
login_failed()
return False
login_failed(flag=1)
else:
self.mainWindow = main_ui.MainWindow()
self.mainWindow.show()
db_operation.db_close()
clibs.username = username
clibs.password = password
self.close()
return True
db_operation.db_write_logs(f"username:{username} 登录成功!")
db_operation.db_close()
self.deleteLater()
except ValueError:
login_failed()
return False
login_failed(flag=2)
else:
db_operation.db_write_logs(f"username duplicated: {username}", self.m, "error")
login_failed(exception=True)
return False
login_failed(flag=3)
username = self.le_username.text()
password = self.le_password.text()
validate_login()
@handle_exception()
def register_check(self):
def register_failed(flag: int = 0):
reason = {-1: "用户名或密码为空", 0: "两次密码不一致", 1: "密码长度不合规", 2: "用户名已存在", 3: "数据库中有重复的用户名"}
self.le_username_reg.clear()
self.le_password_reg.clear()
self.le_password_reg_confirm.clear()
self.le_username_reg.setFocus()
if flag == 0:
QMessageBox.critical(self, "错误", "账号已存在,或两次输入密码不一致,请重新输入!")
elif flag == 1:
QMessageBox.critical(self, "错误", "密码长度不符合要求,请重新输入!")
elif flag == 2:
QMessageBox.critical(self, "错误", "账号重复,需检查数据库,更正后再尝试登录!")
QMessageBox.critical(self, "错误", "账号错误,密码长度(>8)不合规,或两次输入密码不一致,请确认后重新输入!")
raise Exception(f"register failed, reason: {reason[flag]}")
def validate_register():
nonlocal username, password, password_confirm
clibs.conn, clibs.cursor = db_operation.db_conn()
record = db_operation.db_query_users(username)
if password != password_confirm:
register_failed()
return False
if username == "" or password == "" or password_confirm == "":
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)
return False
if len(record) == 0:
salt = PassCipher.gen_salt("@".join([username, password]))
@@ -221,20 +213,20 @@ class LoginWindow(QWidget):
db_operation.db_write_users(username, password_encrypted, salt)
QMessageBox.information(self, "成功", "注册成功,切换至登录窗口进行登录!")
self.tabW_login.setCurrentIndex(self.tabW_login.indexOf(self.tab_login))
return True
elif len(record) == 1:
register_failed()
return False
else:
db_operation.db_write_logs(f"username duplicated: {username}", self.m, "error")
register_failed(flag=2)
return False
else:
register_failed(flag=3)
username = self.le_username_reg.text()
password = self.le_password_reg.text()
password_confirm = self.le_password_reg_confirm.text()
validate_register()
def closeEvent(self, event):
db_operation.db_close()
event.accept()
if __name__ == '__main__':
app = QApplication(sys.argv)

View File

@@ -1,17 +1,22 @@
import json
from shutil import copy
from random import choice
from pathlib import Path
import sys
from random import choice
from shutil import copy
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, QListWidgetItem
from PySide6.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut, QAction, QKeySequence, QResizeEvent
import json
import sys
from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout, QListWidget, QStackedWidget, QMessageBox, QToolBar, QMainWindow, QStatusBar
from PySide6.QtGui import QFont, QIcon, QResizeEvent, QShortcut, QKeySequence, QAction
from PySide6.QtCore import Qt
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.common.signal_bus import signal_bus
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):
@@ -26,7 +31,7 @@ class MainWindow(QMainWindow):
self.m = Path(__file__).stem
self.home_overlay = None
db_operation.db_backup()
clibs.conn, clibs.cursor = db_operation.db_conn()
db_operation.db_conn()
def init_ui(self):
self.setMinimumSize(clibs.win_width, clibs.win_height)
@@ -35,7 +40,7 @@ class MainWindow(QMainWindow):
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.toolBar.setMovable(False)
self.centralW = QWidget()
@@ -51,106 +56,65 @@ class MainWindow(QMainWindow):
""")
self.setStatusBar(self.statusBar)
# toolbar
# 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.ac_homepage.setShortcut(QKeySequence("Ctrl+Alt+H"))
self.toolBar.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.ac_setting.setShortcut(QKeySequence("Ctrl+Alt+S"))
self.toolBar.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.ac_log.setShortcut(QKeySequence("Ctrl+Alt+L"))
self.toolBar.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.ac_about.setShortcut(QKeySequence("Ctrl+Alt+A"))
self.toolBar.addAction(self.ac_about)
layout_h = QHBoxLayout()
# list widget
self.listW = QListWidget()
for item in clibs.listW_items:
_ = QListWidgetItem(item)
_.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
self.listW.addItem(_)
layout_h.addWidget(self.listW)
# stacked widget 1x: 10为一级按钮页其余为二级按钮页2-9同理 | 0x. 日志/设置/关于等页面
self.stackedW =QStackedWidget()
self.w00_setting = QWidget()
self.stackedW.addWidget(self.w00_setting)
self.lb_test00 = QLabel("testing text on setting widget", parent=self.w00_setting)
self.w08_log = QWidget()
self.stackedW.addWidget(self.w08_log)
self.lb_test08 = QLabel("testing text on log widget", parent=self.w08_log)
self.w09_about = QWidget()
self.stackedW.addWidget(self.w09_about)
self.lb_test09 = QLabel("testing text on about widget", parent=self.w09_about)
self.w10_practical = QWidget()
self.stackedW.addWidget(self.w10_practical)
self.lb_test10 = QLabel("testing text on practical widget", parent=self.w10_practical)
self.w20_efficiency = QWidget()
self.stackedW.addWidget(self.w20_efficiency)
self.lb_test20 = QLabel("testing text on efficiency widget", parent=self.w20_efficiency)
self.w30_financial = QWidget()
self.stackedW.addWidget(self.w30_financial)
self.lb_test30 = QLabel("testing text on financial widget", parent=self.w30_financial)
layout_h.addWidget(self.stackedW)
layout_h.setStretch(0, 1)
layout_h.setStretch(1, 5)
self.listW = SListWidget()
layout_h.addWidget(self.listW, stretch=1)
self.stackedW =SStackedWidget()
layout_h.addWidget(self.stackedW, stretch=5)
self.centralW.setLayout(layout_h)
def setup_slot(self):
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)
self.f11_sc = QShortcut(QKeySequence("F11"), self)
self.f11_sc.activated.connect(self.sc_F11)
# self.listW.currentItemChanged.connect(self.change_stackedW)
self.listW.itemClicked.connect(self.change_stackedW)
signal_bus.home_overlay_trigger.connect(self.ac_hp)
signal_bus.home_overlay_close.connect(self.exit_overlay)
self.install_sc()
def setup_sc(self, stat: bool = True):
if stat:
self.ac_homepage.setShortcut(QKeySequence("Ctrl+Alt+H"))
self.f11_sc.setEnabled(True)
else:
self.ac_homepage.setShortcut(QKeySequence())
self.f11_sc.setEnabled(False)
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 get_files(dir_path):
folder = Path(dir_path)
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):
_proverbs = []
@@ -159,6 +123,7 @@ class MainWindow(QMainWindow):
_proverbs.append(proverb)
return _proverbs
@handle_exception()
def get_resources():
# background image
bing = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8"
@@ -169,12 +134,12 @@ class MainWindow(QMainWindow):
for file in files:
image_names.append(file.name.removesuffix(".jpg"))
for image in res["images"]:
startdate = image["startdate"]
if startdate in image_names:
image_name = "-".join([image["startdate"], image["title"]])
if image_name in image_names:
continue
else:
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)
with open(file, "wb") as f:
@@ -185,18 +150,22 @@ class MainWindow(QMainWindow):
# proverbs
hitokoto = "https://v1.hitokoto.cn/"
proverbs = []
try:
req = requests.get(hitokoto)
with open(f"{clibs.base_path}/assets/media/hitokoto.json", mode="rt", encoding="utf-8") as f:
proverbs = json.load(f)
proverbs.append(eval(req.text))
proverbs = del_repeat_proverb(proverbs)
with open(f"{clibs.base_path}/assets/media/hitokoto.json", mode="wt", encoding="utf-8") as f:
json.dump(proverbs, f, ensure_ascii=False)
except Exception as e:
pass
proverb_file = Path(f"{clibs.base_path}/assets/media/hitokoto.json")
req = requests.get(hitokoto)
if not proverb_file.exists():
proverb_file.touch()
proverb_file.write_text("[]")
with open(proverb_file, "w") as f:
f.write("[]")
@handle_exception(self.m)
with open(proverb_file, mode="rt", encoding="utf-8") as f:
proverbs = json.load(f)
proverbs.append(eval(req.text))
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():
# avatar
src, _ = get_files(f"{clibs.base_path}/assets/media/avatar")
@@ -216,11 +185,9 @@ class MainWindow(QMainWindow):
copy(src, dst)
def gen_page():
self.setup_sc(False)
self.home_overlay = WidgetWithBg(parent=self)
self.home_overlay.on_closed.connect(self.exit_overlay)
self.home_overlay.on_full_screen.connect(self.toggle_full_screen)
self.home_overlay.show()
self.setup_sc(False)
width, height = self.width(), self.height()
if width > clibs.win_width:
self.resize(self.width()-1, self.height()-1)
@@ -231,14 +198,13 @@ class MainWindow(QMainWindow):
self.launch_get_resources(get_resources)
gen_page()
def ac_sp(self):
self.stackedW.setCurrentWidget(self.w00_setting)
def ac_lp(self):
self.stackedW.setCurrentWidget(self.w08_log)
def ac_ap(self):
self.stackedW.setCurrentWidget(self.w09_about)
def launch_get_resources(self, func, on_anything=None, *args, **kwargs):
self.td_get_resources = Worker(func, *args, **kwargs)
self.td_get_resources.started.connect(lambda: None)
self.td_get_resources.result.connect(lambda: None)
self.td_get_resources.error.connect(lambda: None)
self.td_get_resources.finished.connect(lambda: None)
self.td_get_resources.start()
def toggle_full_screen(self):
if self.isFullScreen():
@@ -250,30 +216,14 @@ class MainWindow(QMainWindow):
self.showFullScreen()
def exit_overlay(self):
self.setup_sc(True)
self.home_overlay = None
self.setup_sc()
if self.isFullScreen():
self.setWindowFlags(self.windowFlags() ^ Qt.WindowType.WindowStaysOnTopHint)
self.show()
self.showMaximized()
def change_stackedW(self, w_item):
if w_item.text() == "实用工具":
self.stackedW.setCurrentWidget(self.w10_practical)
elif w_item.text() == "效率提升":
self.stackedW.setCurrentWidget(self.w20_efficiency)
elif w_item.text() == "财务分析":
self.stackedW.setCurrentWidget(self.w30_financial)
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.started.connect(lambda: None)
self.td_get_resources.result.connect(lambda: None)
self.td_get_resources.error.connect(lambda: None)
self.td_get_resources.finished.connect(lambda: None)
self.td_get_resources.start()
def sc_F11(self):
def shortcut_f11(self):
if not self.home_overlay:
self.ac_hp()
self.toggle_full_screen()
@@ -287,6 +237,7 @@ class MainWindow(QMainWindow):
if self.isFullScreen():
event.ignore()
return
reply = QMessageBox.question(self, "退出", "\n程序可能在运行,确定要退出吗?")
if reply == QMessageBox.StandardButton.Yes:
db_operation.db_close()

View File

@@ -1,9 +1,12 @@
import sys
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.QtCore import Qt, QPoint, QDateTime, Signal, QTimer
from zhdate import ZhDate
from codes.common import clibs
from codes.common.signal_bus import signal_bus
class LunarClockLabel(QLabel):
@@ -33,9 +36,6 @@ class DoubleClickLabel(QLabel):
class WidgetWithBg(QWidget):
on_closed = Signal()
on_full_screen = Signal()
def __init__(self, parent=None):
super().__init__(parent)
self.predos()
@@ -140,10 +140,11 @@ class WidgetWithBg(QWidget):
def setup_slot(self):
self.lb_avatar.doubleClicked.connect(self.toggle_auth_show)
self.le_password.returnPressed.connect(self.validate_password)
self.sc_caL = QShortcut(QKeySequence("Ctrl+Alt+L"), self)
self.sc_caL.activated.connect(self.toggle_auth_show)
self.sc_caS = QShortcut(QKeySequence("Ctrl+Alt+S"), self)
self.sc_caS.activated.connect(lambda: self.on_full_screen.emit())
signal_bus.home_overlay_auth.connect(self.toggle_auth_show)
# self.sc_caL = QShortcut(QKeySequence("Esc"), self)
# self.sc_caL.activated.connect(self.toggle_auth_show)
# self.sc_caS = QShortcut(QKeySequence("Ctrl+Alt+M"), self)
# self.sc_caS.activated.connect(signal_bus.ho_full_screen.emit)
def toggle_auth_show(self):
if self.le_is_visible:
@@ -170,7 +171,7 @@ class WidgetWithBg(QWidget):
password = self.le_password.text()
if password == clibs.password:
self.hide_le_password()
self.on_closed.emit()
signal_bus.home_overlay_close.emit()
self.deleteLater()
return True
elif password == "":

View File

@@ -0,0 +1,24 @@
from PySide6.QtWidgets import QWidget, QLabel
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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -0,0 +1,25 @@
from PySide6.QtWidgets import QWidget, QLabel
class W08Log(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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -0,0 +1,24 @@
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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -0,0 +1,24 @@
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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -0,0 +1,24 @@
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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -0,0 +1,24 @@
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)
def setup_slot(self):
...
def setup_sc(self):
...

View File

@@ -1,41 +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>691</width>
<height>403</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<widget class="QListWidget" name="listWidget">
<property name="geometry">
<rect>
<x>80</x>
<y>60</y>
<width>101</width>
<height>241</height>
</rect>
</property>
</widget>
<widget class="QStackedWidget" name="stackedWidget">
<property name="geometry">
<rect>
<x>320</x>
<y>180</y>
<width>120</width>
<height>80</height>
</rect>
</property>
<widget class="QWidget" name="page_3"/>
<widget class="QWidget" name="page_4"/>
</widget>
</widget>
<resources/>
<connections/>
</ui>