diff --git a/src/__init__.py b/src/__init__.py index 068ca3d..f75a0c8 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -5,7 +5,7 @@ from src.core.util import resource_path # logger.remove() # logger.add(sys.stderr, level="INFO") -logger.add("{time:YYYY-MM}/{time:YYYY-MM-DD}.log", rotation="00:00", level="DEBUG", retention='1 day', +logger.add("/{time:YYYY-MM}/{time:YYYY-MM-DD}.log", rotation="00:00", level="DEBUG", retention='1 day', encoding='utf-8') QtCore.QDir.addSearchPath('icons', resource_path('ui/icon/')) diff --git a/src/app.py b/src/app.py index 5feb6ad..a9dc898 100644 --- a/src/app.py +++ b/src/app.py @@ -1,4 +1,4 @@ -import qdarkstyle +import qdarkstyle, sys from PyQt6.QtWidgets import QApplication from src.ui.app import Application @@ -9,4 +9,4 @@ if __name__ == '__main__': app.setStyleSheet(qdarkstyle.load_stylesheet(qt_api='pyqt6')) main_win = Application() main_win.show() - app.exec() + sys.exit(app.exec()) diff --git a/src/core/定时任务.py b/src/core/定时任务.py index 0830393..c6ab099 100644 --- a/src/core/定时任务.py +++ b/src/core/定时任务.py @@ -1,3 +1,4 @@ +import os import time import schedule from loguru import logger @@ -9,45 +10,49 @@ from src.entity.pay_record import get_pay_failed_by_user from src.entity.user import get_user_by_username_and_password from src.entity.visual_list import text_count_by_user +# 日志配置 +logger.add("debug.log", format="{time} {level} {message}", level="DEBUG", rotation="1 week", compression="zip") -def job_count(username, password): + +def execute_task(task_func, username, password): try: - logger.info(f'Running job_count for username: {username}') + logger.info(f'Running {task_func.__name__} for username: {username}') user = get_user_by_username_and_password(username, password) - send_message(user.bot_token, user.count_group_id, text_count_by_user(user, get_curr_day())) - logger.info(f'Finished job_count for username: {username}') - except Exception as e: - logger.error(f'Error running job_count for username: {username}, Error: {e}') + message = task_func(user, get_curr_day()) # Assuming all tasks take a 'user' and 'date' + send_message(user.bot_token, user.count_group_id, message) + logger.info(f'Finished {task_func.__name__} for username: {username}') + except Exception as e: # Consider catching more specific exceptions if possible + logger.error(f'Error running {task_func.__name__} for username: {username}, Error: {e}') + logger.exception(e) # This will log the full stack trace -def query_failed_deposit(username, password): - try: - logger.info(f'Running query_failed_deposit for username: {username}') - user = get_user_by_username_and_password(username, password) - send_message(user.bot_token, user.count_group_id, get_pay_failed_by_user(user, get_curr_day())) - logger.info(f'Finished query_failed_deposit for username: {username}') - except Exception as e: - logger.error(f'Error running query_failed_deposit for username: {username}, Error: {e}') +def job_count(user, date): + return text_count_by_user(user, date) -def query_net_win(username, password): - try: - logger.info(f'Running query_net_win for username: {username}') - user = get_user_by_username_and_password(username, password) - send_message(user.bot_token, user.count_group_id, get_net_win_by_user(user, date=get_curr_day())) - logger.info(f'Finished query_net_win for username: {username}') - except Exception as e: - logger.error(f'Error running query_net_win for username: {username}, Error: {e}') +def query_failed_deposit(user, date): + return get_pay_failed_by_user(user, date) + + +def query_net_win(user, date): + return get_net_win_by_user(user, date) if __name__ == '__main__': logger.info('Starting scheduled tasks') + + # Get credentials from environment variables + username = os.getenv('MY_APP_USERNAME', 'default_username') + password = os.getenv('MY_APP_PASSWORD', 'default_password') + times = ['10:50', '14:40', '17:40', '20:40', '21:40', '23:59'] + for time_str in times: - schedule.every().day.at(time_str).do(job_count, 'zayac', '123456') - schedule.every().day.at(time_str).do(query_net_win, 'zayac', '123456') - schedule.every().day.at('23:59').do(query_failed_deposit, 'zayac', '123456') + schedule.every().day.at(time_str).do(execute_task, job_count, username, password) + schedule.every().day.at(time_str).do(execute_task, query_net_win, username, password) + + schedule.every().day.at('23:59').do(execute_task, query_failed_deposit, username, password) + while True: schedule.run_pending() - time.sleep(1) - # logger.info('Running scheduled tasks') # This line is commented out to reduce log verbosity + time.sleep(10) \ No newline at end of file diff --git a/src/entity/banner_info.py b/src/entity/banner_info.py index bb95d7e..4c1a57c 100644 --- a/src/entity/banner_info.py +++ b/src/entity/banner_info.py @@ -46,7 +46,6 @@ def query_banner_info(account: Account): last_banner_info = get_banner_info(account) while True: try: - date = get_curr_day() banner_info = get_banner_info(account) logger.debug(f'{account.name}请求成功:{banner_info}') logger.info( diff --git a/src/entity/pay_record.py b/src/entity/pay_record.py index 657c12b..4555765 100644 --- a/src/entity/pay_record.py +++ b/src/entity/pay_record.py @@ -1,7 +1,5 @@ import asyncio -from concurrent.futures import ThreadPoolExecutor from dataclasses import dataclass -from datetime import datetime from typing import Dict, List, Optional from src import logger diff --git a/src/ui/app.py b/src/ui/app.py index 2f79683..8375843 100644 --- a/src/ui/app.py +++ b/src/ui/app.py @@ -1,6 +1,5 @@ -import configparser -import os import time +from enum import Enum from loguru import logger from PyQt6.QtCore import QDate, QDateTime, Qt, QThreadPool, QTime, QTimer @@ -11,6 +10,7 @@ from PyQt6.QtWidgets import (QApplication, QCheckBox, QDateEdit, QHBoxLayout, QTableWidget, QTableWidgetItem, QTabWidget, QTextEdit, QVBoxLayout, QWidget) +from src.ui.config import ConfigManager from src.core.message_client import send_message from src.core.util import convert_data, resource_path from src.entity.member import get_today_new_member_list @@ -24,6 +24,15 @@ from src.ui.title_bar import CustomTitleBar class Application(QMainWindow): def __init__(self): super().__init__() + self.config_manager = ConfigManager() + self.thread_pool = QThreadPool() + self.report_timer = None + self.date_update_timer = None + self.username = None + self.password = None + self.minimum = False + self.customTitleBar = None + self.tray_icon = None self.tables = {} self.is_dragging = False self.drag_position = None @@ -37,10 +46,7 @@ class Application(QMainWindow): # 1. 加载配置文件 self.load_config() - # 2. 设置线程池 - self.thread_pool = QThreadPool() - - # 3. 设置 UI + # 2. 设置 UI self.setup_ui() # 4. 初始化系统托盘图标 @@ -49,13 +55,9 @@ class Application(QMainWindow): # 5. 初始化表格数据 self.init_table_data() - # 6. 设置日期更新和报告定时器 - self.setup_date_update_timer() - self.setup_report_timer() - # 7. 全局信号处理,更新用户信息 - global_signals.user_data_updated.connect(self.refresh_user_data) - # 8. 消息通知对象 - self.chat_id = self.user.group_id if self.user.group_id else self.user.chat_id + self.setup_timers() + + self.connect_signals() def setup_ui(self): self.apply_stylesheet() @@ -63,6 +65,14 @@ class Application(QMainWindow): self.create_central_widget() self.setup_layouts() + def setup_timers(self): + self.setup_date_update_timer() + self.setup_report_timer() + + def connect_signals(self): + self.tray_icon.activated.connect(self.tray_icon_clicked) + global_signals.user_data_updated.connect(self.refresh_user_data) + def set_window_properties(self): self.resize(600, 400) self.setWindowTitle("zayac的小工具") @@ -82,40 +92,20 @@ class Application(QMainWindow): self.setMenuWidget(self.customTitleBar) def init_tray_icon(self): - self.tray_icon = QSystemTrayIcon(QIcon("icons:icon.png"), self) - tray_menu = QMenu() - exit_action = QAction("退出", self) - exit_action.triggered.connect(self.exit_application) - tray_menu.addAction(exit_action) - self.tray_icon.setContextMenu(tray_menu) - self.tray_icon.activated.connect(self.tray_icon_clicked) + self.tray_icon = SystemTrayIcon(QIcon("icons:icon.png"), self) # 替换为正确的图标路径 self.tray_icon.show() - def init_data_and_timers(self): - # 初始化数据 - self.init_table_data() - - # 设置定时器 - self.setup_date_update_timer() - self.setup_report_timer() - def load_config(self): - config = configparser.ConfigParser() - config_file = 'config.ini' - - if not os.path.exists(config_file): - QMessageBox.warning(None, "警告", "用户信息获取失败!") + # 使用 + try: + self.username = self.config_manager.get('Credentials', 'username') + self.password = self.config_manager.get('Credentials', 'password') + self.minimum = self.config_manager.get('Minimum', 'minimum') + self.user = get_user_by_username_and_password(self.username, self.password) + except FileNotFoundError as e: + QMessageBox.warning(None, "警告", "配置文件信息加载失败!请检查文件是否存在") return None, None - config.read(config_file) - username = config.get('Credentials', 'username') - password = config.get('Credentials', 'password') - minimum = config.get('Minimum', 'minimum') - self.username = username - self.password = password - self.user = get_user_by_username_and_password(username, password) - self.minimum = minimum - def init_table_data(self): # 初始化表格数据 data = self.query_initial_data(self.user) @@ -124,14 +114,13 @@ class Application(QMainWindow): self.update_table(row.agentCode, [time_str, row.registerMembers, row.firstDepositNum, row.netWinLose, row.effectiveNew, row.activeMembers]) - def query_initial_data(self, account): - # 实际实现应该根据您的业务逻辑来定义 - return ReportTask.query_data_for_user(account) + def query_initial_data(self, user): + return ReportTask.get_banner_info_by_user(user) def setup_report_timer(self): self.report_timer = QTimer(self) self.report_timer.timeout.connect(self.update_reports) - self.report_timer.start(60000) # 每60秒触发一次 + self.report_timer.start(60000) def update_reports(self): for account in self.user.accounts: @@ -194,13 +183,12 @@ class Application(QMainWindow): def start_date_update_timer(self): now = QDateTime.currentDateTime() - next_midnight = QDateTime(now.date().addDays(1), QTime(0, 0)) + next_midnight = QDateTime(now.date().addDays(1), QTime(0, 0)) # 设置为次日的午夜 00:00 interval = now.msecsTo(next_midnight) self.date_update_timer.start(interval if interval > 0 else 86400000) # 86400000ms = 24小时 def update_date_edit(self): - self.dateEdit.setDate(QDate.currentDate()) - self.dateEdit.update() + self.dateEdit.setDate(QDate.currentDate()) # 设置 QDateEdit 控件的日期为当前日期 self.update_date_range() self.start_date_update_timer() @@ -287,7 +275,7 @@ class Application(QMainWindow): if self.toaster_notify_enabled: self.tray_icon.showMessage(f"{title}通知", msg, QSystemTrayIcon.MessageIcon.Information, 2000) if self.telegram_notify_enabled: - send_message(self.user.bot_token, self.chat_id, msg) + send_message(self.user.bot_token, self.user.group_id, msg) def on_report_clicked(self): try: @@ -323,35 +311,42 @@ class Application(QMainWindow): # 获取当前的行数 row_count = table.rowCount() table.insertRow(row_count) - notifications = [] + notifications = [] for col, cell_data in enumerate(data): - cell = self.create_table_cell(cell_data, table, col) - # 使用 row_count 而不是 0l - table.setItem(row_count, col, cell) - self.handle_data_change(table, cell_data, col, account_username, notifications) + self.set_table_item(table, row_count, col, cell_data) + self.check_data_change_for_notifications(table, row_count, col, cell_data, account_username, notifications) self.send_all_notifications(notifications) - def create_table_cell(self, cell_data, table, col): + def send_all_notifications(self, notifications): + """发送所有通知""" + for notification in notifications: + self.send_notification(*notification) + + def set_table_item(self, table, row, col, cell_data): + """在表格指定行列设置单元格数据""" cell = QTableWidgetItem(str(cell_data)) cell.setTextAlignment(Qt.AlignmentFlag.AlignCenter) - return cell + table.setItem(row, col, cell) - def handle_data_change(self, table, cell_data, col, account_username, notifications): - if table.rowCount() > 1 and col != 0: - old_data_str = table.item(table.rowCount() - 2, col).text() + def check_data_change_for_notifications(self, table, row, col, new_data, account_username, notifications): + """检查数据变更并准备通知""" + if row > 0 and col != 0: # 忽略第一列,通常是时间戳 + old_data_str = table.item(row - 1, col).text() old_data = convert_data(old_data_str) - new_data = convert_data(cell_data) - + new_data = convert_data(new_data) if old_data != new_data: - count_change = new_data - old_data - self.update_cell_color(table, col, count_change) - self.generate_notifications(account_username, col, cell_data, count_change, notifications) + self.handle_data_change(table, col, new_data - old_data, account_username, notifications) - def update_cell_color(self, table, col, count_change): - # 更新单元格颜色 - cell = table.item(table.rowCount() - 1, col) + def handle_data_change(self, table, col, count_change, account_username, notifications): + """处理表格数据变更,更新单元格颜色,并生成通知""" + self.update_cell_color(table, table.rowCount() - 1, col, count_change) + self.generate_notifications(account_username, col, col, count_change, notifications) + + def update_cell_color(self, table, row, col, count_change): + """更新单元格颜色""" + cell = table.item(row, col) if count_change > 0: cell.setForeground(QColor(Qt.GlobalColor.green)) elif count_change < 0: @@ -373,10 +368,6 @@ class Application(QMainWindow): notifications.append( ('🎉', account.name, '首存', count_change, deposit_results, str(cell_data))) - def send_all_notifications(self, notifications): - for notification in notifications: - self.send_notification(*notification) - def addToggleButton(self, text, style): toggleButton = QPushButton(text) toggleButton.setCheckable(True) @@ -399,39 +390,29 @@ class Application(QMainWindow): self.main_layout.addLayout(self.middle_panel) def add_tabs(self, notebook): - column_headers = ["时间", "注册", "首存", "负盈利", "有效", "活跃"] + column_headers = ColumnHeaders.list() for account in self.user.accounts: - # 创建 Tab 和布局 tab = QWidget() tab_layout = QVBoxLayout(tab) notebook.addTab(tab, account.name) - # 创建表格并设置列数和列标题 - table = QTableWidget() - table.setColumnCount(len(column_headers)) - table.setHorizontalHeaderLabels(column_headers) - - # 禁用表格的编辑功能 - table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) - - # 设置列宽 - header = table.horizontalHeader() - - # 设置所有列为自适应宽度 - for i in range(len(column_headers)): - header.setSectionResizeMode(i, QHeaderView.ResizeMode.Stretch) - - header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents) - - # 将表格的大小调整策略设置为填充整个 Tab - table.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)) - - # 将表格添加到布局中 + table = self.create_table_for_tab(column_headers) tab_layout.addWidget(table) - - # 保存表格引用以便稍后更新 self.tables[account.username] = table + def create_table_for_tab(self, column_headers): + table = QTableWidget() + table.setColumnCount(len(column_headers)) + table.setHorizontalHeaderLabels(column_headers) + table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers) + + header = table.horizontalHeader() + header.setSectionResizeMode(QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents) + + table.setSizePolicy(QSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)) + return table + def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: self.drag_position = event.globalPosition().toPoint() @@ -540,13 +521,40 @@ class Application(QMainWindow): def tray_icon_clicked(self, reason): if reason == QSystemTrayIcon.ActivationReason.Trigger: - if self.isVisible(): - self.hide() + if self.isMinimized() or not self.isVisible(): + self.showNormal() # 恢复窗口大小 + self.activateWindow() # 激活窗口 else: - self.showNormal() + self.hide() # 点击托盘图标再次隐藏窗口 def copy_to_clipboard(text): clipboard = QApplication.clipboard() clipboard.setText(text) + +class SystemTrayIcon(QSystemTrayIcon): + def __init__(self, icon, parent=None): + super().__init__(icon, parent) + self.menu = QMenu(parent) + self.setContextMenu(self.menu) + self.create_actions(parent) + + def create_actions(self, parent): + exit_action = QAction("Exit", parent) + exit_action.triggered.connect(parent.exit_application) + self.menu.addAction(exit_action) + + +class ColumnHeaders(Enum): + TIME = "时间" + REGISTER = "注册" + FIRST_DEPOSIT = "首存" + NET_PROFIT = "负盈利" + EFFECTIVE = "有效" + ACTIVE = "活跃" + + @classmethod + def list(cls): + return [cls.TIME.value, cls.REGISTER.value, cls.FIRST_DEPOSIT.value, + cls.NET_PROFIT.value, cls.EFFECTIVE.value, cls.ACTIVE.value] diff --git a/src/ui/config.py b/src/ui/config.py new file mode 100644 index 0000000..494d1b4 --- /dev/null +++ b/src/ui/config.py @@ -0,0 +1,14 @@ +import configparser +import os +from loguru import logger + + +class ConfigManager: + def __init__(self, config_path='config.ini'): + self.config = configparser.ConfigParser() + if not os.path.exists(config_path): + raise FileNotFoundError(f"配置文件 {config_path} 不存在。") + self.config.read(config_path, encoding='utf-8') + + def get(self, section, option, fallback=None): + return self.config.get(section, option, fallback=fallback) diff --git a/src/ui/data_query.py b/src/ui/data_query.py index 26390b1..6e56b4f 100644 --- a/src/ui/data_query.py +++ b/src/ui/data_query.py @@ -5,7 +5,7 @@ from PyQt6.QtCore import QObject, QRunnable, pyqtSignal from src.entity.banner_info import get_banner_info, get_banner_info_by_user from src.entity.finance import get_adjusted_salary, get_net_win_by_user from src.entity.pay_record import get_pay_failed_by_user -from src.entity.visual_list import text_count_by_user +from src.entity.visual_list import text_count_by_user, get_statics class TaskSignals(QObject): @@ -31,8 +31,9 @@ class ReportTask(QRunnable): return [time_str, banner_info.registerMembers, banner_info.firstDepositNum, banner_info.netWinLose, banner_info.effectiveNew, banner_info.activeMembers] + @staticmethod - def query_data_for_user(user): + def get_banner_info_by_user(user): banner_info_res = get_banner_info_by_user(user) return banner_info_res diff --git a/src/ui/system_tray_icon.py b/src/ui/system_tray_icon.py new file mode 100644 index 0000000..99f3288 --- /dev/null +++ b/src/ui/system_tray_icon.py @@ -0,0 +1,15 @@ +from PyQt6.QtWidgets import QSystemTrayIcon, QMenu +from PyQt6.QtGui import QIcon, QAction + + +class SystemTrayIcon(QSystemTrayIcon): + def __init__(self, icon, parent=None): + super().__init__(icon, parent) + self.menu = QMenu(parent) + self.setContextMenu(self.menu) + self.create_actions(parent) + + def create_actions(self, parent): + exit_action = QAction("Exit", parent) + exit_action.triggered.connect(parent.exit_application) + self.menu.addAction(exit_action) diff --git a/src/ui/title_bar.py b/src/ui/title_bar.py index 454e349..8bef181 100644 --- a/src/ui/title_bar.py +++ b/src/ui/title_bar.py @@ -2,69 +2,58 @@ from PyQt6.QtCore import Qt from PyQt6.QtGui import QFont, QIcon, QPixmap from PyQt6.QtWidgets import QHBoxLayout, QLabel, QToolButton, QWidget - class CustomTitleBar(QWidget): def __init__(self, parent): super().__init__(parent) self.parent = parent - self.setup_ui() self.mousePressed = False + self.setup_ui() def setup_ui(self): self.layout = QHBoxLayout() - self.layout.setContentsMargins(0, 0, 0, 0) # 设置布局的边距为0 - self.layout.setSpacing(0) # 设置按钮之间的间距为0 + self.layout.setContentsMargins(0, 0, 0, 0) + self.layout.setSpacing(0) - # 创建图标 QLabel + self.setup_icon_label() + self.setup_title_label() + self.setup_buttons() + + self.setLayout(self.layout) + + def setup_icon_label(self): self.iconLabel = QLabel(self) - self.iconLabel.setFixedSize(32, 32) # 设置图标大小 + self.iconLabel.setFixedSize(32, 32) iconPixmap = QPixmap('icons:icon.png').scaled(24, 24, Qt.AspectRatioMode.KeepAspectRatio) self.iconLabel.setPixmap(iconPixmap) - self.layout.addWidget(self.iconLabel) # 添加图标到布局 + self.layout.addWidget(self.iconLabel) - # 创建标题 QLabel + def setup_title_label(self): font = QFont() font.setPointSize(11) - self.titleLabel = QLabel("zayac的小工具") + self.titleLabel = QLabel("zayac’s Toolkit") self.titleLabel.setFont(font) self.titleLabel.setAlignment(Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignVCenter) self.layout.addWidget(self.titleLabel, 1) - # 使用图标按钮并应用样式 - self.stayOnTopButton = QToolButton() - self.stayOnTopButton.setIcon(QIcon('icons:top.svg')) - self.stayOnTopButton.setFixedSize(32, 32) # 设置按钮的固定大小 - self.stayOnTopButton.setCheckable(True) - self.stayOnTopButton.clicked.connect(self.toggle_stay_on_top) - self.layout.addWidget(self.stayOnTopButton) - - # 设置其他按钮 - self.minimizeButton = QToolButton() - self.minimizeButton.setIcon(QIcon('icons:min.svg')) - self.minimizeButton.setFixedSize(32, 32) - self.minimizeButton.clicked.connect(self.parent.showMinimized) - self.layout.addWidget(self.minimizeButton) - - self.maximizeButton = QToolButton() - self.maximizeButton.setIcon(QIcon('icons:max.svg')) - self.maximizeButton.setFixedSize(32, 32) - self.maximizeButton.clicked.connect(self.toggle_maximize) - self.layout.addWidget(self.maximizeButton) - - self.closeButton = QToolButton() - self.closeButton.setIcon(QIcon('icons:close.svg')) - self.closeButton.setFixedSize(32, 32) - self.closeButton.clicked.connect(self.parent.close) + def setup_buttons(self): + self.stayOnTopButton = self.create_tool_button('icons:top.svg', self.toggle_stay_on_top) + self.minimizeButton = self.create_tool_button('icons:min.svg', self.parent.showMinimized) + self.maximizeButton = self.create_tool_button('icons:max.svg', self.toggle_maximize) + self.closeButton = self.create_tool_button('icons:close.svg', self.parent.close) self.closeButton.setObjectName("closeButton") - self.layout.addWidget(self.closeButton) - # 在创建按钮后立即设置样式表 - - self.setLayout(self.layout) + def create_tool_button(self, icon_path, callback): + button = QToolButton() + button.setIcon(QIcon(icon_path)) + button.setFixedSize(32, 32) + button.clicked.connect(callback) + self.layout.addWidget(button) + return button def mousePressEvent(self, event): - self.mousePressed = True - self.mousePos = event.globalPosition().toPoint() + if event.buttons() == Qt.MouseButton.LeftButton: + self.mousePressed = True + self.mousePos = event.globalPosition().toPoint() def mouseMoveEvent(self, event): if self.mousePressed: @@ -75,20 +64,17 @@ class CustomTitleBar(QWidget): self.mousePressed = False def toggle_stay_on_top(self): + current_flags = self.parent.windowFlags() if self.stayOnTopButton.isChecked(): - self.parent.setWindowFlags(self.parent.windowFlags() | Qt.WindowType.WindowStaysOnTopHint) + self.parent.setWindowFlags(current_flags | Qt.WindowType.WindowStaysOnTopHint) else: - self.parent.setWindowFlags(self.parent.windowFlags() & ~Qt.WindowType.WindowStaysOnTopHint) - self.parent.show() # 重新显示窗口以应用新的窗口标志 - - def enterEvent(self, event): - self.parent.reset_cursor_style() # 通知主窗口重置鼠标样式 - - def leaveEvent(self, event): - self.parent.reset_cursor_style() # 通知主窗口重置鼠标样式 + self.parent.setWindowFlags(current_flags & ~Qt.WindowType.WindowStaysOnTopHint) + self.parent.show() # Re-show the window to apply new window flags def toggle_maximize(self): if self.parent.isMaximized(): self.parent.showNormal() + self.maximizeButton.setIcon(QIcon('icons:max.svg')) # Update icon if needed else: self.parent.showMaximized() + self.maximizeButton.setIcon(QIcon('icons:restore.svg')) # Update icon if needed \ No newline at end of file