create my toolbox -- first commit
This commit is contained in:
238
toolbox/codes/ui/login_ui.py
Normal file
238
toolbox/codes/ui/login_ui.py
Normal file
@@ -0,0 +1,238 @@
|
||||
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.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
|
||||
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut
|
||||
from codes.common import clibs, db_operation
|
||||
from codes.common.secure_encrypt import PassCipher
|
||||
from codes.ui import main_ui
|
||||
|
||||
class LoginWindow(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.init_ui()
|
||||
self.setup_slot()
|
||||
self.predos()
|
||||
self.le_username.setFocus()
|
||||
|
||||
def init_ui(self):
|
||||
self.setMinimumSize(420, 200)
|
||||
self.setMaximumSize(500, 240)
|
||||
self.resize(480, 200)
|
||||
self.setWindowTitle("登录")
|
||||
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
|
||||
self.setFont(QFont("Consolas", 14))
|
||||
|
||||
self.layout_outter = 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.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_H_username = QHBoxLayout()
|
||||
self.lb_username = QLabel("账号")
|
||||
self.lb_username.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.layout_H_username.addWidget(self.lb_username)
|
||||
self.le_username = QLineEdit()
|
||||
self.le_username.setFocus()
|
||||
self.layout_H_username.addWidget(self.le_username)
|
||||
|
||||
self.layout_H_password = QHBoxLayout()
|
||||
self.lb_password = QLabel("密码")
|
||||
self.lb_password.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.layout_H_password.addWidget(self.lb_password)
|
||||
self.le_password = QLineEdit()
|
||||
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
self.layout_H_password.addWidget(self.le_password)
|
||||
|
||||
self.layout_H_button = QHBoxLayout()
|
||||
self.btn_login = QPushButton("登录")
|
||||
self.btn_login.setAutoDefault(True)
|
||||
self.btn_cancel = QPushButton("取消")
|
||||
self.btn_cancel.setAutoDefault(True)
|
||||
self.layout_H_button.addWidget(self.btn_login)
|
||||
self.layout_H_button.addWidget(self.btn_cancel)
|
||||
|
||||
self.layout_V_user_pass = QVBoxLayout()
|
||||
self.layout_V_user_pass.addLayout(self.layout_H_username)
|
||||
self.layout_V_user_pass.addLayout(self.layout_H_password)
|
||||
self.layout_V_user_pass.addLayout(self.layout_H_button)
|
||||
self.tab_login.setLayout(self.layout_V_user_pass)
|
||||
|
||||
# 注册页面
|
||||
self.layout_H_username_reg = QHBoxLayout()
|
||||
self.lb_username_reg = QLabel("账号设定")
|
||||
self.lb_username_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.layout_H_username_reg.addWidget(self.lb_username_reg)
|
||||
self.le_username_reg = QLineEdit()
|
||||
self.le_username_reg.setFocus()
|
||||
self.layout_H_username_reg.addWidget(self.le_username_reg)
|
||||
|
||||
self.layout_H_password_reg = QHBoxLayout()
|
||||
self.lb_password_reg = QLabel("密码设定")
|
||||
self.lb_password_reg.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.layout_H_password_reg.addWidget(self.lb_password_reg)
|
||||
self.le_password_reg = QLineEdit()
|
||||
self.le_password_reg.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
self.layout_H_password_reg.addWidget(self.le_password_reg)
|
||||
|
||||
self.layout_H_password_reg_confirm = QHBoxLayout()
|
||||
self.lb_password_reg_confirm = QLabel("密码确认")
|
||||
self.lb_password_reg_confirm.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.layout_H_password_reg_confirm.addWidget(self.lb_password_reg_confirm)
|
||||
self.le_password_reg_confirm = QLineEdit()
|
||||
self.le_password_reg_confirm.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
self.layout_H_password_reg_confirm.addWidget(self.le_password_reg_confirm)
|
||||
|
||||
self.layout_H_button_reg = QHBoxLayout()
|
||||
self.btn_login_reg = QPushButton("确认")
|
||||
self.btn_login_reg.setAutoDefault(True)
|
||||
self.btn_cancel_reg = QPushButton("取消")
|
||||
self.btn_cancel_reg.setAutoDefault(True)
|
||||
self.layout_H_button_reg.addWidget(self.btn_login_reg)
|
||||
self.layout_H_button_reg.addWidget(self.btn_cancel_reg)
|
||||
|
||||
self.layout_V_user_pass_reg = QVBoxLayout()
|
||||
self.layout_V_user_pass_reg.addLayout(self.layout_H_username_reg)
|
||||
self.layout_V_user_pass_reg.addLayout(self.layout_H_password_reg)
|
||||
self.layout_V_user_pass_reg.addLayout(self.layout_H_password_reg_confirm)
|
||||
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)
|
||||
|
||||
def setup_slot(self):
|
||||
self.tabW_login.currentChanged.connect(self.onChange_tabW)
|
||||
self.btn_login.clicked.connect(self.login_check)
|
||||
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):
|
||||
db_file = clibs.base_path / "assets/database/toolbox.db"
|
||||
if not db_file.exists():
|
||||
db_operation.db_init(db_file)
|
||||
|
||||
def onChange_tabW(self):
|
||||
text = self.tabW_login.tabText(self.tabW_login.currentIndex())
|
||||
if text == "登录":
|
||||
self.le_username.clear()
|
||||
self.le_password.clear()
|
||||
self.le_username.setFocus()
|
||||
elif text == "注册":
|
||||
self.le_username_reg.clear()
|
||||
self.le_password_reg.clear()
|
||||
self.le_password_reg_confirm.clear()
|
||||
self.le_username_reg.setFocus()
|
||||
else:
|
||||
raise Exception(f"Unknown TabWidget Name: {text}")
|
||||
|
||||
def login_check(self):
|
||||
def login_failed():
|
||||
self.le_username.clear()
|
||||
self.le_password.clear()
|
||||
self.le_username.setFocus()
|
||||
QMessageBox.critical(self, "错误", "账号或密码错误,请重新输入!")
|
||||
|
||||
def validate_login():
|
||||
nonlocal username, password
|
||||
conn, cursor = db_operation.db_conn()
|
||||
cursor.execute(f""" SELECT * FROM users where username = "{username}" """)
|
||||
record = cursor.fetchall()
|
||||
if len(record) == 0:
|
||||
login_failed()
|
||||
elif len(record) == 1:
|
||||
keys = ["id", "timestamp", "username", "password", "salt"]
|
||||
login_info = dict(zip(keys, record[0]))
|
||||
salt = PassCipher.gen_salt("@".join([username, password]))
|
||||
cipher = PassCipher(salt)
|
||||
# password_encrypt = cipher.encrypt("@".join([username, password]))
|
||||
# print(f"password_encrypt = {password_encrypt}")
|
||||
# exit()
|
||||
try:
|
||||
decrypt_password = cipher.decrypt(login_info["password"])
|
||||
if password != decrypt_password:
|
||||
login_failed()
|
||||
return False
|
||||
else:
|
||||
self.mainWindow = main_ui.MainWindow()
|
||||
self.mainWindow.show()
|
||||
db_operation.db_close(conn, cursor)
|
||||
clibs.username = username
|
||||
clibs.password = password
|
||||
self.close()
|
||||
return True
|
||||
except ValueError:
|
||||
login_failed()
|
||||
return False
|
||||
else:
|
||||
raise Exception(f"username duplicated: {username}")
|
||||
|
||||
username = self.le_username.text()
|
||||
password = self.le_password.text()
|
||||
|
||||
validate_login()
|
||||
|
||||
def register_check(self):
|
||||
def register_failed(flag: int = 0):
|
||||
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, "错误", "密码长度不符合要求,请重新输入!")
|
||||
|
||||
def validate_register():
|
||||
nonlocal username, password, password_confirm, record, conn, cursor
|
||||
if password != password_confirm:
|
||||
register_failed()
|
||||
return False
|
||||
|
||||
if len(password) < clibs.account["minimum_password_length"]:
|
||||
register_failed(flag=1)
|
||||
return False
|
||||
|
||||
if len(record) == 0:
|
||||
salt = PassCipher.gen_salt("@".join([username, password]))
|
||||
cipher = PassCipher(salt)
|
||||
password_encrypted = cipher.encrypt(password)
|
||||
cursor.execute("INSERT INTO users (username, password, salt) VALUES (?, ?, ?)", (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:
|
||||
raise Exception(f"username duplicated: {username}")
|
||||
|
||||
username = self.le_username_reg.text()
|
||||
password = self.le_password_reg.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()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
window = LoginWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
|
||||
208
toolbox/codes/ui/main_ui.py
Normal file
208
toolbox/codes/ui/main_ui.py
Normal file
@@ -0,0 +1,208 @@
|
||||
import json
|
||||
from math import lgamma
|
||||
from shutil import copy
|
||||
from random import choice
|
||||
from pathlib import Path
|
||||
import sys
|
||||
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
|
||||
from PySide6.QtCore import Qt, QTime, QSize, QRect,QEvent, QThread
|
||||
from PySide6.QtGui import QCursor, QFont, QIcon, QImage, QPixmap, QShortcut, QAction, QKeySequence, QResizeEvent
|
||||
from codes.common import clibs, db_operation
|
||||
from codes.ui.widget_bg_ui import WidgetWithBg
|
||||
from codes.common.worker import Worker
|
||||
from typing import Callable, Any
|
||||
|
||||
|
||||
class MainWindow(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.init_ui()
|
||||
self.setup_slot()
|
||||
self.predos()
|
||||
|
||||
def init_ui(self):
|
||||
self.setMinimumSize(clibs.win_width, clibs.win_height)
|
||||
self.resize(clibs.win_width, clibs.win_height)
|
||||
self.setWindowTitle("Toolbox")
|
||||
self.setWindowIcon(QIcon(f"{clibs.base_path}/assets/media/icon.ico"))
|
||||
self.setFont(QFont("Consolas", 14))
|
||||
# 任务栏/主窗口/状态栏
|
||||
self.toolBar = QToolBar()
|
||||
self.addToolBar(self.toolBar)
|
||||
self.toolBar.setMovable(False)
|
||||
self.centralW = QWidget()
|
||||
self.setCentralWidget(self.centralW)
|
||||
self.statusBar = QStatusBar()
|
||||
self.setStatusBar(self.statusBar)
|
||||
|
||||
# toolbar
|
||||
self.ac_homepage = QAction()
|
||||
self.ac_homepage.setMenuRole(QAction.MenuRole.NoRole)
|
||||
self.ac_homepage.setStatusTip("Go to homepage")
|
||||
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)
|
||||
|
||||
def setup_slot(self):
|
||||
self.ac_homepage.triggered.connect(self.ac_hp)
|
||||
# QShortcut("Esc", self).activated.connect(self.close)
|
||||
|
||||
def predos(self):
|
||||
self.home_overlay = None
|
||||
db_operation.db_backup()
|
||||
self.conn, self.cursor = db_operation.db_conn()
|
||||
|
||||
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
|
||||
|
||||
def del_repeat_proverb(proverbs: list):
|
||||
_proverbs = []
|
||||
for proverb in proverbs:
|
||||
if proverb not in _proverbs:
|
||||
_proverbs.append(proverb)
|
||||
return _proverbs
|
||||
|
||||
def get_resources():
|
||||
# background image
|
||||
bing = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=8"
|
||||
req = requests.get(bing)
|
||||
res = req.json()
|
||||
_, files = get_files(f"{clibs.base_path}/assets/media/bg")
|
||||
image_names = []
|
||||
for file in files:
|
||||
image_names.append(file.name.removesuffix(".jpg"))
|
||||
for image in res["images"]:
|
||||
startdate = image["startdate"]
|
||||
if startdate 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")
|
||||
try:
|
||||
req = requests.get(image_url, stream=True, timeout=10)
|
||||
with open(file, "wb") as f:
|
||||
for chunk in req.iter_content(chunk_size=8192):
|
||||
f.write(chunk)
|
||||
except Exception as e:
|
||||
pass
|
||||
# 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
|
||||
|
||||
def change_resources():
|
||||
# avatar
|
||||
src, _ = get_files(f"{clibs.base_path}/assets/media/avatar")
|
||||
dst = f"{clibs.base_path}/assets/media/avatar.jpg"
|
||||
copy(src, dst)
|
||||
# proverbs
|
||||
with open(f"{clibs.base_path}/assets/media/hitokoto.json", mode="r", encoding="utf-8") as f:
|
||||
proverbs = json.load(f)
|
||||
res = choice(proverbs)
|
||||
sentence = res["hitokoto"]
|
||||
from_where = res["from"]
|
||||
from_who = res["from_who"]
|
||||
clibs.proverb = f"{sentence}\t\t※⌈{from_where}⌋ | {from_who}※"
|
||||
# bg
|
||||
src, _ = get_files(f"{clibs.base_path}/assets/media/bg")
|
||||
dst = f"{clibs.base_path}/assets/media/bg.jpg"
|
||||
copy(src, dst)
|
||||
|
||||
def gen_page():
|
||||
self.set_shortcuts(False)
|
||||
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()
|
||||
width, height = self.width(), self.height()
|
||||
if width > clibs.win_width:
|
||||
self.resize(self.width()-1, self.height()-1)
|
||||
else:
|
||||
self.resize(clibs.win_width+1, clibs.win_height+1)
|
||||
|
||||
change_resources()
|
||||
self.launch_get_resources(get_resources)
|
||||
gen_page()
|
||||
|
||||
def full_screen(self, flag: bool):
|
||||
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.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 resizeEvent(self, event: QResizeEvent):
|
||||
super().resizeEvent(event)
|
||||
if self.home_overlay:
|
||||
self.home_overlay.setGeometry(self.rect())
|
||||
|
||||
def closeEvent(self, event):
|
||||
if self.isFullScreen():
|
||||
event.ignore()
|
||||
return
|
||||
|
||||
reply = QMessageBox.question(self, "退出", "\n程序可能在运行,确定要退出吗?")
|
||||
if reply == QMessageBox.StandardButton.Yes:
|
||||
db_operation.db_close(self.conn, self.cursor)
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
145
toolbox/codes/ui/untitled.ui
Normal file
145
toolbox/codes/ui/untitled.ui
Normal file
@@ -0,0 +1,145 @@
|
||||
<?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>
|
||||
216
toolbox/codes/ui/widget_bg_ui.py
Normal file
216
toolbox/codes/ui/widget_bg_ui.py
Normal file
@@ -0,0 +1,216 @@
|
||||
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
|
||||
|
||||
|
||||
class LunarClockLabel(QLabel):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setMinimumWidth(350)
|
||||
timer = QTimer(self)
|
||||
timer.timeout.connect(self.update_time)
|
||||
timer.start(1000)
|
||||
self.update_time()
|
||||
|
||||
def update_time(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} {dt.toString('hh:mm:ss')}"
|
||||
self.setText(text)
|
||||
|
||||
|
||||
class DoubleClickLabel(QLabel):
|
||||
doubleClicked = Signal()
|
||||
|
||||
def mouseDoubleClickEvent(self, event):
|
||||
super().mouseDoubleClickEvent(event)
|
||||
self.doubleClicked.emit()
|
||||
|
||||
|
||||
class WidgetWithBg(QWidget):
|
||||
on_closed = Signal()
|
||||
on_full_screen = Signal(int)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.predos()
|
||||
self.init_ui()
|
||||
self.setup_slot()
|
||||
|
||||
def predos(self):
|
||||
font_id = QFontDatabase.addApplicationFont(f"{clibs.base_path}/assets/media/font/OldEnglishTextMT/OldEnglishTextMT.ttf")
|
||||
family = QFontDatabase.applicationFontFamilies(font_id)[0]
|
||||
self.lb_font = QFont(family, 28, QFont.Weight.Medium)
|
||||
self.background_pixmap = QPixmap(clibs.bg)
|
||||
|
||||
def init_ui(self):
|
||||
layout_v = QVBoxLayout()
|
||||
# 最上层的空白区
|
||||
self.lb_empty_up = QLabel(self)
|
||||
layout_v.addWidget(self.lb_empty_up)
|
||||
# 头像区
|
||||
self.lb_avatar = DoubleClickLabel(self)
|
||||
self.lb_avatar.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
avatar = QPixmap(clibs.avatar)
|
||||
avatar = self.circle_pixmap(avatar, 200)
|
||||
self.lb_avatar.setPixmap(avatar)
|
||||
self.lb_avatar.setScaledContents(True)
|
||||
self.lb_avatar.setFixedSize(144, 144)
|
||||
layout_v.addWidget(self.lb_avatar, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
# 艺术字区
|
||||
self.lb_name = QLabel(self)
|
||||
self.lb_name.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
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)
|
||||
layout_v.addWidget(self.lb_name)
|
||||
# 时间区-左横线
|
||||
layout_h = QHBoxLayout()
|
||||
self.line_left = QFrame(self)
|
||||
self.line_left.setFrameShape(QFrame.Shape.HLine)
|
||||
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)
|
||||
self.line_left.setSizePolicy(policy)
|
||||
self.line_left.setLineWidth(1)
|
||||
self.line_left.setFixedWidth(100)
|
||||
# 时间区-右横线
|
||||
self.line_right = QFrame(self)
|
||||
self.line_right.setFrameShape(QFrame.Shape.HLine)
|
||||
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)
|
||||
self.line_right.setSizePolicy(policy)
|
||||
self.line_right.setLineWidth(1)
|
||||
self.line_right.setFixedWidth(100)
|
||||
# 时间区-时间
|
||||
self.lb_time = LunarClockLabel(self)
|
||||
self.lb_time.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.lb_time.setStyleSheet("color: rgba(255,255,255,255);")
|
||||
self.lb_time.setFont(QFont("Consolas", 12, QFont.Weight.Bold))
|
||||
layout_h.addStretch(1)
|
||||
layout_h.addWidget(self.line_left)
|
||||
layout_h.addWidget(self.lb_time)
|
||||
layout_h.addWidget(self.line_right)
|
||||
layout_h.addStretch(1)
|
||||
layout_v.addLayout(layout_h)
|
||||
# layout_v.addWidget(self.line, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.lb_proverb = QLabel(self)
|
||||
self.lb_proverb.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
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)
|
||||
|
||||
self.le_password = QLineEdit(self)
|
||||
self.le_password.setEchoMode(QLineEdit.EchoMode.Password)
|
||||
self.le_password.setFont(QFont("Consolas", 12, QFont.Weight.Normal))
|
||||
self.le_password.setMinimumWidth(300)
|
||||
self.le_password.setFixedHeight(30)
|
||||
self.hide_le_password()
|
||||
layout_v.addWidget(self.le_password, alignment=Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.lb_empty_down = QLabel(self)
|
||||
layout_v.addWidget(self.lb_empty_down)
|
||||
|
||||
layout_v.setStretch(0, 2) # empty up
|
||||
layout_v.setStretch(1, 2) # avatar
|
||||
layout_v.setStretch(2, 1) # name
|
||||
layout_v.setStretch(3, 2) # time
|
||||
layout_v.setStretch(4, 1) # proverb
|
||||
layout_v.setStretch(5, 1) # password
|
||||
layout_v.setStretch(6, 2) # empty down
|
||||
self.setLayout(layout_v)
|
||||
|
||||
def setup_slot(self):
|
||||
self.lb_avatar.doubleClicked.connect(self.auth_show)
|
||||
self.le_password.returnPressed.connect(self.validate_password)
|
||||
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):
|
||||
if self.input_hide:
|
||||
self.show_le_password()
|
||||
else:
|
||||
self.hide_le_password()
|
||||
|
||||
def show_le_password(self):
|
||||
self.input_hide = False
|
||||
self.le_password.clear()
|
||||
self.le_password.setPlaceholderText("Password")
|
||||
self.le_password.setStyleSheet("")
|
||||
self.le_password.setDisabled(False)
|
||||
self.le_password.setFocus()
|
||||
|
||||
def hide_le_password(self):
|
||||
self.input_hide = True
|
||||
self.le_password.clear()
|
||||
self.le_password.setDisabled(True)
|
||||
self.le_password.setPlaceholderText("")
|
||||
self.le_password.setStyleSheet("background:transparent; color:rgba(0,0,0,0); border:none; ")
|
||||
|
||||
def validate_password(self):
|
||||
password = self.le_password.text()
|
||||
if password == clibs.password:
|
||||
self.on_closed.emit()
|
||||
self.close()
|
||||
elif password == "":
|
||||
self.hide_le_password()
|
||||
return
|
||||
else:
|
||||
QMessageBox.critical(self, "错误", "密码不正确,请确认后重新输入!")
|
||||
self.show_le_password()
|
||||
|
||||
@staticmethod
|
||||
def circle_pixmap(src: QPixmap, diameter: int) -> QPixmap:
|
||||
dst = QPixmap(diameter, diameter)
|
||||
dst.fill(Qt.GlobalColor.transparent)
|
||||
painter = QPainter(dst)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.setBrush(QBrush(src))
|
||||
painter.drawEllipse(dst.rect())
|
||||
painter.end()
|
||||
return dst
|
||||
|
||||
def paintEvent(self, event):
|
||||
if not self.background_pixmap.isNull():
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
|
||||
scaled_pixmap = self.background_pixmap.scaled(self.size(), Qt.AspectRatioMode.IgnoreAspectRatio, Qt.TransformationMode.SmoothTransformation)
|
||||
x = (self.width() - scaled_pixmap.width()) // 2
|
||||
y = (self.height() - scaled_pixmap.height()) // 2
|
||||
painter.drawPixmap(QPoint(x, y), scaled_pixmap)
|
||||
|
||||
painter.setBrush(QColor(0, 0, 0, 144)) # 144 ≈ 50% 暗度,越大越暗
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.drawRect(self.rect())
|
||||
painter.end()
|
||||
|
||||
super().paintEvent(event)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
your_widget = WidgetWithBg()
|
||||
your_widget.resize(1000, 450)
|
||||
your_widget.show()
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user