完成日志界面的制作

This commit is contained in:
2025-09-28 18:21:48 +08:00
parent 1c47497fc2
commit 943130b875
12 changed files with 295 additions and 72 deletions

View File

@@ -1,6 +1,6 @@
from PySide6.QtGui import QFocusEvent
from PySide6.QtWidgets import QListWidget, QListWidgetItem
from PySide6.QtCore import Qt, QEvent
from PySide6.QtCore import Qt
from codes.common import clibs
from codes.common.signal_bus import signal_bus
@@ -18,6 +18,7 @@ class SListWidget(QListWidget):
...
def init_ui(self):
self.setMinimumWidth(150)
for item in clibs.listW_items:
_ = QListWidgetItem(item)
_.setTextAlignment(Qt.AlignmentFlag.AlignCenter)
@@ -25,7 +26,9 @@ class SListWidget(QListWidget):
def setup_slot(self):
self.itemClicked.connect(self.item_clicked)
signal_bus.qa_stacked_page_switch.connect(self.hide)
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):
@@ -40,6 +43,10 @@ class SListWidget(QListWidget):
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)

View File

@@ -1,11 +1,12 @@
from pathlib import Path
import importlib.util
from PySide6.QtWidgets import QStackedWidget
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
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):
@@ -17,46 +18,23 @@ class SStackedWidget(QStackedWidget):
self.setup_slot()
def predos(self):
self.page_list = {}
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):
# stacked widget 1x: 10为一级按钮页其余为二级按钮页2-9同理 | 0x. 日志/设置/关于等页面
self.load_pages()
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.qa_stacked_page_switch.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)
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

@@ -6,7 +6,6 @@ from codes.common import clibs
from codes.common.signal_bus import signal_bus
class SToolBar(QToolBar):
def __init__(self, parent=None):
super().__init__(parent)
@@ -85,24 +84,26 @@ class SToolBar(QToolBar):
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)
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)
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")
signal_bus.stacked_page_switch.emit("w01_setting")
signal_bus.stacked_page_switch_setting.emit()
def ac_lp(self):
signal_bus.qa_stacked_page_switch.emit("w08_log")
signal_bus.stacked_page_switch.emit("w08_log")
signal_bus.stacked_page_switch_log.emit()
def ac_ap(self):
signal_bus.qa_stacked_page_switch.emit("w09_about")
signal_bus.stacked_page_switch.emit("w09_about")
signal_bus.stacked_page_switch_about.emit()
def change2hide(self):
self.ac_switch.setChecked(False)

View File

@@ -126,6 +126,8 @@ class LoginWindow(QWidget):
self.le_password.returnPressed.connect(self.login_check)
self.le_password_reg_confirm.returnPressed.connect(self.register_check)
QShortcut("Esc", self).activated.connect(self.close)
QShortcut("Alt+1", self).activated.connect(lambda: self.tabW_login.setCurrentIndex(0))
QShortcut("Alt+2", self).activated.connect(lambda: self.tabW_login.setCurrentIndex(1))
def onChange_tabW(self):
text = self.tabW_login.tabText(self.tabW_login.currentIndex())
@@ -142,7 +144,7 @@ class LoginWindow(QWidget):
@handle_exception()
def login_check(self):
def login_failed(flag: int = 0):
reason = {-1: "用户名或密码为空", 0: "用户名未注册", 1: "成功,密码错误", 2: "失败,密码错误", 3: "数据库中有重复的用户名"}
reason = {-1: "用户名或密码为空", 0: "用户名未注册", 1: "成功,密码错误", 2: "失败,密码错误", 3: "数据库中有重复的用户名"}
self.le_username.clear()
self.le_password.clear()
self.le_username.setFocus()
@@ -172,7 +174,6 @@ class LoginWindow(QWidget):
clibs.username = username
clibs.password = password
db_operation.db_write_logs(f"username:{username} 登录成功!")
db_operation.db_close()
self.deleteLater()
except ValueError:
login_failed(flag=2)
@@ -224,7 +225,6 @@ class LoginWindow(QWidget):
validate_register()
def closeEvent(self, event):
db_operation.db_close()
event.accept()

View File

@@ -5,7 +5,7 @@ import requests
import json
import sys
from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout, QListWidget, QStackedWidget, QMessageBox, QToolBar, QMainWindow, QStatusBar
from PySide6.QtWidgets import QApplication, QWidget, QHBoxLayout, QMessageBox, QMainWindow, QStatusBar
from PySide6.QtGui import QFont, QIcon, QResizeEvent, QShortcut, QKeySequence, QAction
from PySide6.QtCore import Qt

View File

@@ -1,7 +1,7 @@
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.QtGui import QPixmap, QPainter, QFontDatabase, QFont, QBrush, QColor
from PySide6.QtCore import Qt, QPoint, QDateTime, Signal, QTimer
from zhdate import ZhDate

View File

@@ -1,25 +1,224 @@
from PySide6.QtWidgets import QWidget, QLabel
from PySide6.QtWidgets import QWidget, QLabel, QMessageBox, QVBoxLayout, QTreeWidget, QHBoxLayout, QPushButton, QFrame, QLineEdit, QCheckBox, QTreeWidgetItem, QDialog
from PySide6.QtCore import Qt, Signal
from PySide6.QtGui import QColor, QIcon, QFont, QKeySequence, QIntValidator, QShortcut
from codes.common.signal_bus import signal_bus
from codes.common import db_operation
from codes.common import clibs
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.setFont(QFont('Consolas', 14))
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.predos()
self.ui_init()
self.setup_slot()
self.setup_sc()
def predos(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.lb_test = QLabel(f"testing text on widget: \n{__file__}", parent=self)
layout_v = QVBoxLayout(self)
self.treeW = QTreeWidget()
self.treeW.setHeaderLabels(["ID", "时间戳", "告警级别", "模块信息", "告警内容"])
layout_v.addWidget(self.treeW, stretch=9)
layout_h = QHBoxLayout()
self.pb_previous = QPushButton("上一页")
layout_h.addWidget(self.pb_previous, stretch=1)
self.lb_page = ClickableLabel("999999/999999")
self.lb_page.setAlignment(Qt.AlignmentFlag.AlignCenter)
self.lb_page.setMinimumWidth(144)
layout_h.addWidget(self.lb_page, stretch=2)
self.pb_next = QPushButton("下一页")
layout_h.addWidget(self.pb_next, stretch=1)
layout_h.addStretch(9)
self.frame_checkbox = QFrame()
layout_h_checkbox = QHBoxLayout()
self.box_info = QCheckBox("通知", parent=self.frame_checkbox)
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.setChecked(True)
layout_h_checkbox.addWidget(self.box_warning, stretch=1)
self.box_error = QCheckBox("错误", parent=self.frame_checkbox)
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.setChecked(True)
layout_h_checkbox.addWidget(self.box_exception, stretch=1)
self.box_unknown = QCheckBox("未知", parent=self.frame_checkbox)
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.setPlaceholderText("告警内容")
self.le_search.setMinimumWidth(300)
layout_h.addWidget(self.le_search, stretch=5)
self.pb_search = QPushButton("查找")
layout_h.addWidget(self.pb_search, stretch=1)
layout_v.addLayout(layout_h, stretch=1)
self.setLayout(layout_v)
def setup_slot(self):
...
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(True)
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(True)
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=levels)
if search_text:
# ids = [_[0] for _ in self.records]
# placeholder = ",".join(ids)
# clibs.cursor.execute(f"SELECT * FROM logs WHERE id IN ({placeholder}) and content like ?", (ids + [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(True)
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(True)
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()
for record in records[idx_start:idx_end]:
record = [str(_) for _ in record]
item = QTreeWidgetItem(self.treeW, record)
self.treeW.addTopLevelItem(item)
colors = {"info": QColor(144, 238, 144), "warning": QColor(255, 240, 210), "error": QColor(255, 220, 220), "exception": QColor(255, 220, 220), "unknown": QColor(255, 220, 220)}
color = colors[record[2]]
for col in range(5):
item.setBackground(col, color)
self.treeW.scrollToBottom()