2141 lines
101 KiB
Python
Executable File
2141 lines
101 KiB
Python
Executable File
import datetime as dt
|
||
import json
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
from copy import deepcopy
|
||
from pathlib import Path
|
||
from shutil import rmtree
|
||
|
||
from PyQt5 import uic, QtCore
|
||
from PyQt5.QtCore import Qt, QTime, QUrl, QDate, pyqtSignal
|
||
from PyQt5.QtGui import QIcon, QDesktopServices, QColor
|
||
from PyQt5.QtWidgets import QApplication, QHeaderView, QTableWidgetItem, QLabel, QHBoxLayout, QSizePolicy, \
|
||
QSpacerItem, QFileDialog, QVBoxLayout, QScroller
|
||
from packaging.version import Version
|
||
from loguru import logger
|
||
from qfluentwidgets import (
|
||
Theme, setTheme, FluentWindow, FluentIcon as fIcon, ToolButton, ListWidget, ComboBox, CaptionLabel,
|
||
SpinBox, LineEdit, PrimaryPushButton, TableWidget, Flyout, InfoBarIcon, InfoBar, InfoBarPosition,
|
||
FlyoutAnimationType, NavigationItemPosition, MessageBox, SubtitleLabel, PushButton, SwitchButton,
|
||
CalendarPicker, BodyLabel, ColorDialog, isDarkTheme, TimeEdit, EditableComboBox, MessageBoxBase,
|
||
SearchLineEdit, Slider, PlainTextEdit, ToolTipFilter, ToolTipPosition, RadioButton, HyperlinkLabel,
|
||
PrimaryDropDownPushButton, Action, RoundMenu, CardWidget, ImageLabel, StrongBodyLabel,
|
||
TransparentDropDownToolButton, Dialog, SmoothScrollArea, TransparentToolButton, HyperlinkButton
|
||
)
|
||
|
||
import conf
|
||
import list_ as list_
|
||
import tip_toast
|
||
import utils
|
||
from utils import update_tray_tooltip
|
||
import weather_db
|
||
import weather_db as wd
|
||
from conf import base_directory
|
||
from cses_mgr import CSES_Converter
|
||
from file import config_center, schedule_center
|
||
from network_thread import VersionThread
|
||
from plugin import p_loader
|
||
from plugin_plaza import PluginPlaza
|
||
|
||
# 适配高DPI缩放
|
||
QApplication.setHighDpiScaleFactorRoundingPolicy(
|
||
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
||
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
|
||
QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
|
||
|
||
today = dt.date.today()
|
||
plugin_plaza = None
|
||
|
||
plugin_dict = {} # 插件字典
|
||
enabled_plugins = {} # 启用的插件列表
|
||
|
||
morning_st = 0
|
||
afternoon_st = 0
|
||
|
||
current_week = 0
|
||
|
||
loaded_data = schedule_center.schedule_data
|
||
|
||
schedule_dict = {} # 对应时间线的课程表
|
||
schedule_even_dict = {} # 对应时间线的课程表(双周)
|
||
|
||
timeline_dict = {} # 时间线字典
|
||
|
||
countdown_dict = {}
|
||
|
||
|
||
def open_plaza():
|
||
global plugin_plaza
|
||
if plugin_plaza is None or not plugin_plaza.isVisible():
|
||
plugin_plaza = PluginPlaza()
|
||
plugin_plaza.show()
|
||
plugin_plaza.closed.connect(cleanup_plaza)
|
||
logger.info('打开“插件广场”')
|
||
else:
|
||
plugin_plaza.raise_()
|
||
plugin_plaza.activateWindow()
|
||
|
||
|
||
def cleanup_plaza():
|
||
global plugin_plaza
|
||
logger.info('关闭“插件广场”')
|
||
del plugin_plaza
|
||
plugin_plaza = None
|
||
|
||
|
||
def get_timeline():
|
||
global loaded_data
|
||
loaded_data = schedule_center.schedule_data
|
||
return loaded_data['timeline']
|
||
|
||
|
||
def open_dir(path: str):
|
||
if sys.platform.startswith('win32'):
|
||
os.startfile(path)
|
||
elif sys.platform.startswith('linux'):
|
||
subprocess.run(['xdg-open', path])
|
||
else:
|
||
msg_box = Dialog(
|
||
'无法打开文件夹', f'Class Widgets 在您的系统下不支持自动打开文件夹,请手动打开以下地址:\n{path}'
|
||
)
|
||
msg_box.yesButton.setText('好')
|
||
msg_box.cancelButton.hide()
|
||
msg_box.buttonLayout.insertStretch(0, 1)
|
||
msg_box.setFixedWidth(550)
|
||
msg_box.exec()
|
||
|
||
|
||
def switch_checked(section, key, checked):
|
||
if checked:
|
||
config_center.write_conf(section, key, '1')
|
||
else:
|
||
config_center.write_conf(section, key, '0')
|
||
if key == 'auto_startup':
|
||
if checked:
|
||
conf.add_to_startup()
|
||
else:
|
||
conf.remove_from_startup()
|
||
|
||
|
||
def get_theme_name():
|
||
theme = config_center.read_conf('General', 'theme')
|
||
if os.path.exists(f'{base_directory}/ui/{theme}/theme.json'):
|
||
return theme
|
||
else:
|
||
return 'default'
|
||
|
||
|
||
def load_schedule_dict(schedule, part, part_name):
|
||
"""
|
||
加载课表字典
|
||
"""
|
||
schedule_dict_ = {}
|
||
for week, item in schedule.items():
|
||
all_class = []
|
||
count = [] # 初始化计数器
|
||
for i in range(len(part)):
|
||
count.append(0)
|
||
if str(week) in loaded_data['timeline'] and loaded_data['timeline'][str(week)]:
|
||
timeline = get_timeline()[str(week)]
|
||
else:
|
||
timeline = get_timeline()['default']
|
||
|
||
for item_name, item_time in timeline.items():
|
||
if item_name.startswith('a'):
|
||
try:
|
||
if int(item_name[1]) == 0:
|
||
count_num = 0
|
||
else:
|
||
count_num = sum(count[:int(item_name[1])])
|
||
|
||
prefix = item[int(item_name[2:]) - 1 + count_num]
|
||
period = part_name[str(item_name[1])]
|
||
all_class.append(f'{prefix}-{period}')
|
||
except IndexError or ValueError: # 未设置值
|
||
prefix = '未添加'
|
||
period = part_name[str(item_name[1])]
|
||
all_class.append(f'{prefix}-{period}')
|
||
count[int(item_name[1])] += 1
|
||
schedule_dict_[week] = all_class
|
||
return schedule_dict_
|
||
|
||
|
||
def convert_to_dict(data_dict_):
|
||
data_dict = {}
|
||
for week, item in data_dict_.items():
|
||
cache_list = item
|
||
replace_list = []
|
||
for activity_num in range(len(cache_list)):
|
||
item_info = cache_list[int(activity_num)].split('-')
|
||
replace_list.append(item_info[0])
|
||
data_dict[str(week)] = replace_list
|
||
return data_dict
|
||
|
||
|
||
def se_load_item():
|
||
global schedule_dict
|
||
global schedule_even_dict
|
||
global loaded_data
|
||
loaded_data = schedule_center.schedule_data
|
||
part_name = loaded_data.get('part_name')
|
||
part = loaded_data.get('part')
|
||
schedule = loaded_data.get('schedule')
|
||
schedule_even = loaded_data.get('schedule_even')
|
||
|
||
schedule_dict = load_schedule_dict(schedule, part, part_name)
|
||
schedule_even_dict = load_schedule_dict(schedule_even, part, part_name)
|
||
|
||
|
||
def cd_load_item():
|
||
global countdown_dict
|
||
text = config_center.read_conf('Date', 'cd_text_custom').split(',')
|
||
date = config_center.read_conf('Date', 'countdown_date').split(',')
|
||
if len(text) != len(date):
|
||
countdown_dict = {"Err": f"len(cd_text_custom) (={len(text)}) != len(countdown_date) (={len(date)})"}
|
||
raise Exception(
|
||
f"len(cd_text_custom) (={len(text)}) != len(countdown_date) (={len(date)})"f"len(cd_text_custom) (={len(text)}) != len(countdown_date) (={len(date)}) \n 请检查 config.ini [Date] 项!!")
|
||
countdown_dict = dict(zip(date, text))
|
||
|
||
|
||
class selectCity(MessageBoxBase): # 选择城市
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
title_label = SubtitleLabel()
|
||
subtitle_label = BodyLabel()
|
||
self.search_edit = SearchLineEdit()
|
||
|
||
title_label.setText('搜索城市')
|
||
subtitle_label.setText('请输入当地城市名进行搜索')
|
||
self.yesButton.setText('选择此城市') # 按钮组件汉化
|
||
self.cancelButton.setText('取消')
|
||
|
||
self.search_edit.setPlaceholderText('输入城市名')
|
||
self.search_edit.setClearButtonEnabled(True)
|
||
self.search_edit.textChanged.connect(self.search_city)
|
||
|
||
self.city_list = ListWidget()
|
||
self.city_list.addItems(wd.search_by_name(''))
|
||
self.get_selected_city()
|
||
|
||
# 将组件添加到布局中
|
||
self.viewLayout.addWidget(title_label)
|
||
self.viewLayout.addWidget(subtitle_label)
|
||
self.viewLayout.addWidget(self.search_edit)
|
||
self.viewLayout.addWidget(self.city_list)
|
||
self.widget.setMinimumWidth(500)
|
||
self.widget.setMinimumHeight(600)
|
||
|
||
def search_city(self):
|
||
self.city_list.clear()
|
||
self.city_list.addItems(wd.search_by_name(self.search_edit.text()))
|
||
self.city_list.clearSelection() # 清除选中项
|
||
|
||
def get_selected_city(self):
|
||
selected_city = self.city_list.findItems(
|
||
wd.search_by_num(str(config_center.read_conf('Weather', 'city'))), QtCore.Qt.MatchFlag.MatchExactly
|
||
)
|
||
if selected_city: # 若找到该城市
|
||
item = selected_city[0]
|
||
# 选中该项
|
||
self.city_list.setCurrentItem(item)
|
||
# 聚焦该项
|
||
self.city_list.scrollToItem(item)
|
||
|
||
|
||
class licenseDialog(MessageBoxBase): # 显示软件许可协议
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
title_label = SubtitleLabel()
|
||
subtitle_label = BodyLabel()
|
||
self.license_text = PlainTextEdit()
|
||
|
||
title_label.setText('软件许可协议')
|
||
subtitle_label.setText('此项目 (Class Widgets) 基于 GPL-3.0 许可证授权发布,详情请参阅:')
|
||
self.yesButton.setText('好') # 按钮组件汉化
|
||
self.cancelButton.hide()
|
||
self.buttonLayout.insertStretch(0, 1)
|
||
self.license_text.setPlainText(open('LICENSE', 'r', encoding='utf-8').read())
|
||
self.license_text.setReadOnly(True)
|
||
|
||
# 将组件添加到布局中
|
||
self.viewLayout.addWidget(title_label)
|
||
self.viewLayout.addWidget(subtitle_label)
|
||
self.viewLayout.addWidget(self.license_text)
|
||
self.widget.setMinimumWidth(600)
|
||
self.widget.setMinimumHeight(500)
|
||
|
||
|
||
class PluginSettingsDialog(MessageBoxBase): # 插件设置对话框
|
||
def __init__(self, plugin_dir=None, parent=None):
|
||
super().__init__(parent)
|
||
self.plugin_widget = None
|
||
self.plugin_dir = plugin_dir
|
||
self.parent = parent
|
||
self.init_ui()
|
||
|
||
def init_ui(self):
|
||
# 加载已定义的UI
|
||
self.plugin_widget = p_loader.plugins_settings[self.plugin_dir]
|
||
self.viewLayout.addWidget(self.plugin_widget)
|
||
self.viewLayout.setContentsMargins(0, 0, 0, 0)
|
||
|
||
self.cancelButton.hide()
|
||
self.buttonLayout.insertStretch(0, 1)
|
||
|
||
self.widget.setMinimumWidth(875)
|
||
self.widget.setMinimumHeight(625)
|
||
|
||
|
||
class PluginCard(CardWidget): # 插件卡片
|
||
def __init__(
|
||
self, icon, title='Unknown', content='Unknown', version='1.0.0', plugin_dir='', author=None, parent=None,
|
||
enable_settings=None
|
||
):
|
||
super().__init__(parent)
|
||
icon_radius = 5
|
||
self.plugin_dir = plugin_dir
|
||
self.title = title
|
||
self.parent = parent
|
||
|
||
self.iconWidget = ImageLabel(icon) # 插件图标
|
||
self.titleLabel = StrongBodyLabel(title, self) # 插件名
|
||
self.versionLabel = BodyLabel(version, self) # 插件版本
|
||
self.authorLabel = BodyLabel(author, self) # 插件作者
|
||
self.contentLabel = CaptionLabel(content, self) # 插件描述
|
||
self.enableButton = SwitchButton()
|
||
self.moreButton = TransparentDropDownToolButton()
|
||
self.moreMenu = RoundMenu(parent=self.moreButton)
|
||
self.settingsBtn = TransparentToolButton() # 设置按钮
|
||
self.settingsBtn.hide()
|
||
|
||
self.hBoxLayout = QHBoxLayout(self)
|
||
self.hBoxLayout_Title = QHBoxLayout(self)
|
||
self.vBoxLayout = QVBoxLayout(self)
|
||
|
||
self.moreMenu.addActions([
|
||
Action(
|
||
fIcon.FOLDER, f'打开“{title}”插件文件夹',
|
||
triggered=lambda: open_dir(os.path.join(base_directory, conf.PLUGINS_DIR, self.plugin_dir))
|
||
),
|
||
Action(
|
||
fIcon.DELETE, f'卸载“{title}”插件',
|
||
triggered=self.remove_plugin
|
||
)
|
||
])
|
||
|
||
if plugin_dir in enabled_plugins['enabled_plugins']: # 插件是否启用
|
||
self.enableButton.setChecked(True)
|
||
if enable_settings:
|
||
self.moreMenu.addSeparator()
|
||
self.moreMenu.addAction(Action(fIcon.SETTING, f'“{title}”插件设置', triggered=self.show_settings))
|
||
self.settingsBtn.show()
|
||
|
||
self.setFixedHeight(73)
|
||
self.iconWidget.setFixedSize(48, 48)
|
||
self.moreButton.setFixedSize(34, 34)
|
||
self.iconWidget.setBorderRadius(icon_radius, icon_radius, icon_radius, icon_radius) # 圆角
|
||
self.contentLabel.setTextColor("#606060", "#d2d2d2")
|
||
self.contentLabel.setMaximumWidth(500)
|
||
self.contentLabel.setWordWrap(True) # 自动换行
|
||
self.versionLabel.setTextColor("#999999", "#999999")
|
||
self.authorLabel.setTextColor("#606060", "#d2d2d2")
|
||
self.enableButton.checkedChanged.connect(self.set_enable)
|
||
self.enableButton.setOffText('禁用')
|
||
self.enableButton.setOnText('启用')
|
||
self.moreButton.setMenu(self.moreMenu)
|
||
self.settingsBtn.setIcon(fIcon.SETTING)
|
||
self.settingsBtn.clicked.connect(self.show_settings)
|
||
|
||
self.hBoxLayout.setContentsMargins(20, 11, 11, 11)
|
||
self.hBoxLayout.setSpacing(15)
|
||
self.hBoxLayout.addWidget(self.iconWidget)
|
||
|
||
# 内容
|
||
self.vBoxLayout.setContentsMargins(0, 0, 0, 0)
|
||
self.vBoxLayout.setSpacing(0)
|
||
self.vBoxLayout.addLayout(self.hBoxLayout_Title)
|
||
self.vBoxLayout.addWidget(self.contentLabel, 0, Qt.AlignmentFlag.AlignVCenter)
|
||
self.vBoxLayout.setAlignment(Qt.AlignmentFlag.AlignVCenter)
|
||
self.hBoxLayout.addLayout(self.vBoxLayout, 1) # !!!
|
||
|
||
# 标题栏
|
||
self.hBoxLayout_Title.setSpacing(12)
|
||
self.hBoxLayout_Title.setAlignment(Qt.AlignmentFlag.AlignLeft)
|
||
self.hBoxLayout_Title.addWidget(self.titleLabel, 0, Qt.AlignmentFlag.AlignVCenter)
|
||
self.hBoxLayout_Title.addWidget(self.authorLabel, 0, Qt.AlignmentFlag.AlignVCenter)
|
||
self.hBoxLayout_Title.addWidget(self.versionLabel, 0, Qt.AlignmentFlag.AlignVCenter)
|
||
|
||
self.hBoxLayout.addStretch(1)
|
||
self.hBoxLayout.addWidget(self.settingsBtn, 0, Qt.AlignmentFlag.AlignRight)
|
||
self.hBoxLayout.addWidget(self.enableButton, 0, Qt.AlignmentFlag.AlignRight)
|
||
self.hBoxLayout.addWidget(self.moreButton, 0, Qt.AlignmentFlag.AlignRight)
|
||
|
||
def set_enable(self):
|
||
global enabled_plugins
|
||
if self.enableButton.isChecked():
|
||
enabled_plugins['enabled_plugins'].append(self.plugin_dir)
|
||
conf.save_plugin_config(enabled_plugins)
|
||
else:
|
||
enabled_plugins['enabled_plugins'].remove(self.plugin_dir)
|
||
conf.save_plugin_config(enabled_plugins)
|
||
|
||
def show_settings(self):
|
||
w = PluginSettingsDialog(self.plugin_dir, self.parent)
|
||
w.exec()
|
||
|
||
def remove_plugin(self):
|
||
alert = MessageBox(f"您确定要删除插件“{self.title}”吗?", "删除此插件后,将无法恢复。", self.parent)
|
||
alert.yesButton.setText('永久删除')
|
||
alert.yesButton.setStyleSheet("""
|
||
PushButton{
|
||
border-radius: 5px;
|
||
padding: 5px 12px 6px 12px;
|
||
outline: none;
|
||
}
|
||
PrimaryPushButton{
|
||
color: white;
|
||
background-color: #FF6167;
|
||
border: 1px solid #FF8585;
|
||
border-bottom: 1px solid #943333;
|
||
}
|
||
PrimaryPushButton:hover{
|
||
background-color: #FF7E83;
|
||
border: 1px solid #FF8084;
|
||
border-bottom: 1px solid #B13939;
|
||
}
|
||
PrimaryPushButton:pressed{
|
||
color: rgba(255, 255, 255, 0.63);
|
||
background-color: #DB5359;
|
||
border: 1px solid #DB5359;
|
||
}
|
||
""")
|
||
alert.cancelButton.setText('我再想想……')
|
||
if alert.exec():
|
||
success = p_loader.delete_plugin(self.plugin_dir)
|
||
if success:
|
||
try:
|
||
with open(f'{base_directory}/plugins/plugins_from_pp.json', 'r', encoding='utf-8') as f:
|
||
installed_data = json.load(f)
|
||
installed_plugins = installed_data.get('plugins', [])
|
||
if self.plugin_dir in installed_plugins:
|
||
installed_plugins.remove(self.plugin_dir)
|
||
conf.save_installed_plugin(installed_plugins)
|
||
except Exception as e:
|
||
logger.error(f"更新已安装插件列表失败: {e}")
|
||
|
||
InfoBar.success(
|
||
title='卸载成功',
|
||
content=f'插件 “{self.title}” 已卸载。请重启 Class Widgets 以完全移除。',
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.BOTTOM_RIGHT,
|
||
duration=5000,
|
||
parent=self.window()
|
||
)
|
||
self.deleteLater() # 删除卡片
|
||
else:
|
||
InfoBar.error(
|
||
title='卸载失败',
|
||
content=f'卸载插件 “{self.title}” 时出错,请查看日志获取详细信息。',
|
||
orient=Qt.Horizontal,
|
||
isClosable=True,
|
||
position=InfoBarPosition.BOTTOM_RIGHT,
|
||
duration=5000,
|
||
parent=self.window()
|
||
)
|
||
|
||
|
||
class TextFieldMessageBox(MessageBoxBase):
|
||
""" Custom message box """
|
||
|
||
def __init__(
|
||
self, parent=None, title='标题', text='请输入内容', default_text='', enable_check=False):
|
||
super().__init__(parent)
|
||
self.fail_color = (QColor('#c42b1c'), QColor('#ff99a4'))
|
||
self.success_color = (QColor('#0f7b0f'), QColor('#6ccb5f'))
|
||
self.check_list = enable_check
|
||
|
||
self.titleLabel = SubtitleLabel()
|
||
self.titleLabel.setText(title)
|
||
self.subtitleLabel = BodyLabel()
|
||
self.subtitleLabel.setText(text)
|
||
self.textField = LineEdit()
|
||
self.tipsLabel = CaptionLabel()
|
||
self.tipsLabel.setText('')
|
||
self.yesButton.setText('确定')
|
||
|
||
self.fieldLayout = QVBoxLayout()
|
||
self.textField.setPlaceholderText(default_text)
|
||
self.textField.setClearButtonEnabled(True)
|
||
if enable_check:
|
||
self.textField.textChanged.connect(self.check_text)
|
||
self.yesButton.setEnabled(False)
|
||
|
||
# 将组件添加到布局中
|
||
self.viewLayout.addWidget(self.titleLabel)
|
||
self.viewLayout.addWidget(self.subtitleLabel)
|
||
self.viewLayout.addLayout(self.fieldLayout)
|
||
self.fieldLayout.addWidget(self.textField)
|
||
self.fieldLayout.addWidget(self.tipsLabel)
|
||
|
||
# 设置对话框的最小宽度
|
||
self.widget.setMinimumWidth(350)
|
||
|
||
def check_text(self):
|
||
self.tipsLabel.setTextColor(self.fail_color[0], self.fail_color[1])
|
||
self.yesButton.setEnabled(False)
|
||
if self.textField.text() == '':
|
||
self.tipsLabel.setText('不能为空值啊 ( •̀ ω •́ )✧')
|
||
return
|
||
if f'{self.textField.text()}.json' in self.check_list:
|
||
self.tipsLabel.setText('不可以和之前的课程名重复哦 o(TヘTo)')
|
||
return
|
||
|
||
self.yesButton.setEnabled(True)
|
||
self.tipsLabel.setTextColor(self.success_color[0], self.success_color[1])
|
||
self.tipsLabel.setText('很好!就这样!ヾ(≧▽≦*)o')
|
||
|
||
|
||
class SettingsMenu(FluentWindow):
|
||
closed = pyqtSignal()
|
||
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.button_clear_log = None
|
||
self.version_thread = None
|
||
|
||
# 创建子页面
|
||
self.spInterface = uic.loadUi(f'{base_directory}/view/menu/preview.ui') # 预览
|
||
self.spInterface.setObjectName("spInterface")
|
||
self.teInterface = uic.loadUi(f'{base_directory}/view/menu/timeline_edit.ui') # 时间线编辑
|
||
self.teInterface.setObjectName("teInterface")
|
||
self.seInterface = uic.loadUi(f'{base_directory}/view/menu/schedule_edit.ui') # 课程表编辑
|
||
self.seInterface.setObjectName("seInterface")
|
||
self.cdInterface = uic.loadUi(f'{base_directory}/view/menu/countdown_custom_edit.ui') # 倒计日编辑
|
||
self.cdInterface.setObjectName("cdInterface")
|
||
self.adInterface = uic.loadUi(f'{base_directory}/view/menu/advance.ui') # 高级选项
|
||
self.adInterface.setObjectName("adInterface")
|
||
self.ifInterface = uic.loadUi(f'{base_directory}/view/menu/about.ui') # 关于
|
||
self.ifInterface.setObjectName("ifInterface")
|
||
self.ctInterface = uic.loadUi(f'{base_directory}/view/menu/custom.ui') # 自定义
|
||
self.ctInterface.setObjectName("ctInterface")
|
||
self.cfInterface = uic.loadUi(f'{base_directory}/view/menu/configs.ui') # 配置文件
|
||
self.cfInterface.setObjectName("cfInterface")
|
||
self.sdInterface = uic.loadUi(f'{base_directory}/view/menu/sound.ui') # 通知
|
||
self.sdInterface.setObjectName("sdInterface")
|
||
self.hdInterface = uic.loadUi(f'{base_directory}/view/menu/help.ui') # 帮助
|
||
self.hdInterface.setObjectName("hdInterface")
|
||
self.plInterface = uic.loadUi(f'{base_directory}/view/menu/plugin_mgr.ui') # 插件
|
||
self.plInterface.setObjectName("plInterface")
|
||
|
||
self.init_nav()
|
||
self.init_window()
|
||
|
||
def init_font(self): # 设置字体
|
||
self.setStyleSheet("""QLabel {
|
||
font-family: 'Microsoft YaHei';
|
||
}""")
|
||
|
||
def load_all_item(self):
|
||
self.setup_timeline_edit()
|
||
self.setup_schedule_edit()
|
||
self.setup_schedule_preview()
|
||
self.setup_advance_interface()
|
||
self.setup_about_interface()
|
||
self.setup_customization_interface()
|
||
self.setup_configs_interface()
|
||
self.setup_sound_interface()
|
||
self.setup_help_interface()
|
||
self.setup_plugin_mgr_interface()
|
||
self.setup_countdown_edit()
|
||
|
||
# 初始化界面
|
||
def setup_plugin_mgr_interface(self):
|
||
pm_scroll = self.findChild(SmoothScrollArea, 'pm_scroll')
|
||
QScroller.grabGesture(pm_scroll.viewport(), QScroller.LeftMouseButtonGesture) # 触摸屏适配
|
||
|
||
global plugin_dict, enabled_plugins
|
||
enabled_plugins = conf.load_plugin_config() # 加载启用的插件
|
||
plugin_dict = (conf.load_plugins()) # 加载插件信息
|
||
|
||
open_pp = self.findChild(PushButton, 'open_plugin_plaza')
|
||
open_pp.clicked.connect(open_plaza) # 打开插件广场
|
||
|
||
open_pp2 = self.findChild(PushButton, 'open_plugin_plaza_2')
|
||
open_pp2.clicked.connect(open_plaza) # 打开插件广场
|
||
|
||
auto_delay = self.findChild(SpinBox, 'auto_delay')
|
||
auto_delay.setValue(int(config_center.read_conf('Plugin', 'auto_delay')))
|
||
auto_delay.valueChanged.connect(
|
||
lambda: config_center.write_conf('Plugin', 'auto_delay', str(auto_delay.value())))
|
||
# 设置自动化延迟
|
||
|
||
plugin_card_layout = self.findChild(QVBoxLayout, 'plugin_card_layout')
|
||
open_plugin_folder = self.findChild(PushButton, 'open_plugin_folder')
|
||
open_plugin_folder.clicked.connect(lambda: open_dir(os.path.join(base_directory, conf.PLUGINS_DIR))) # 打开插件目录
|
||
|
||
if not p_loader.plugins_settings: # 若插件设置为空
|
||
p_loader.load_plugins() # 加载插件设置
|
||
|
||
for plugin in plugin_dict:
|
||
if (Path(conf.PLUGINS_DIR) / plugin / 'icon.png').exists(): # 若插件目录存在icon.png
|
||
icon_path = f'{base_directory}/plugins/{plugin}/icon.png'
|
||
else:
|
||
icon_path = f'{base_directory}/img/settings/plugin-icon.png'
|
||
card = PluginCard(
|
||
icon=icon_path,
|
||
title=plugin_dict[plugin]['name'],
|
||
version=plugin_dict[plugin]['version'],
|
||
author=plugin_dict[plugin]['author'],
|
||
plugin_dir=plugin,
|
||
content=plugin_dict[plugin]['description'],
|
||
enable_settings=plugin_dict[plugin]['settings'],
|
||
parent=self
|
||
)
|
||
plugin_card_layout.addWidget(card)
|
||
|
||
tips_plugin_empty = self.findChild(QLabel, 'tips_plugin_empty')
|
||
if plugin_dict:
|
||
tips_plugin_empty.hide()
|
||
|
||
def setup_help_interface(self):
|
||
open_by_browser = self.findChild(PushButton, 'open_by_browser')
|
||
open_by_browser.setIcon(fIcon.LINK)
|
||
open_by_browser.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(
|
||
'https://classwidgets.rinlit.cn/docs-user/'
|
||
)))
|
||
|
||
def setup_sound_interface(self):
|
||
sd_scroll = self.findChild(SmoothScrollArea, 'sd_scroll') # 触摸屏适配
|
||
QScroller.grabGesture(sd_scroll.viewport(), QScroller.LeftMouseButtonGesture)
|
||
|
||
switch_enable_toast = self.findChild(SwitchButton, 'switch_enable_attend')
|
||
switch_enable_toast.setChecked(int(config_center.read_conf('Toast', 'attend_class')))
|
||
switch_enable_toast.checkedChanged.connect(lambda checked: switch_checked('Toast', 'attend_class', checked))
|
||
# 上课提醒开关
|
||
|
||
switch_enable_finish = self.findChild(SwitchButton, 'switch_enable_finish')
|
||
switch_enable_finish.setChecked(int(config_center.read_conf('Toast', 'finish_class')))
|
||
switch_enable_finish.checkedChanged.connect(lambda checked: switch_checked('Toast', 'finish_class', checked))
|
||
# 下课提醒开关
|
||
|
||
switch_enable_finish = self.findChild(SwitchButton, 'switch_enable_schoolout')
|
||
switch_enable_finish.setChecked(int(config_center.read_conf('Toast', 'after_school')))
|
||
switch_enable_finish.checkedChanged.connect(lambda checked: switch_checked('Toast', 'after_school', checked))
|
||
# 放学提醒开关
|
||
|
||
switch_enable_prepare = self.findChild(SwitchButton, 'switch_enable_prepare')
|
||
switch_enable_prepare.setChecked(int(config_center.read_conf('Toast', 'prepare_class')))
|
||
switch_enable_prepare.checkedChanged.connect(lambda checked: switch_checked('Toast', 'prepare_class', checked))
|
||
# 预备铃开关
|
||
|
||
switch_enable_pin_toast = self.findChild(SwitchButton, 'switch_enable_pin_toast')
|
||
switch_enable_pin_toast.setChecked(int(config_center.read_conf('Toast', 'pin_on_top')))
|
||
switch_enable_pin_toast.checkedChanged.connect(lambda checked: switch_checked('Toast', 'pin_on_top', checked))
|
||
# 置顶开关
|
||
|
||
slider_volume = self.findChild(Slider, 'slider_volume')
|
||
slider_volume.setValue(int(config_center.read_conf('Audio', 'volume')))
|
||
slider_volume.valueChanged.connect(self.save_volume) # 音量滑块
|
||
|
||
preview_toast_button = self.findChild(PrimaryDropDownPushButton, 'preview')
|
||
|
||
pre_toast_menu = RoundMenu(parent=preview_toast_button)
|
||
pre_toast_menu.addActions([
|
||
Action(fIcon.EDUCATION, '上课提醒',
|
||
triggered=lambda: tip_toast.push_notification(1, lesson_name='信息技术')),
|
||
Action(fIcon.CAFE, '下课提醒',
|
||
triggered=lambda: tip_toast.push_notification(0, lesson_name='信息技术')),
|
||
Action(fIcon.BOOK_SHELF, '预备提醒',
|
||
triggered=lambda: tip_toast.push_notification(3, lesson_name='信息技术')),
|
||
Action(fIcon.CODE, '其他提醒',
|
||
triggered=lambda: tip_toast.push_notification(4, title='通知', subtitle='测试通知示例',
|
||
content='这是一条测试通知ヾ(≧▽≦*)o'))
|
||
])
|
||
preview_toast_button.setMenu(pre_toast_menu) # 预览通知栏
|
||
|
||
switch_wave_effect = self.findChild(SwitchButton, 'switch_enable_wave')
|
||
switch_wave_effect.setChecked(int(config_center.read_conf('Toast', 'wave')))
|
||
switch_wave_effect.checkedChanged.connect(lambda checked: switch_checked('Toast', 'wave', checked)) # 波纹开关
|
||
|
||
spin_prepare_time = self.findChild(SpinBox, 'spin_prepare_class')
|
||
spin_prepare_time.setValue(int(config_center.read_conf('Toast', 'prepare_minutes')))
|
||
spin_prepare_time.valueChanged.connect(self.save_prepare_time) # 准备时间
|
||
|
||
def setup_configs_interface(self): # 配置界面
|
||
cf_import_schedule = self.findChild(PushButton, 'im_schedule')
|
||
cf_import_schedule.clicked.connect(self.cf_import_schedule) # 导入课程表
|
||
cf_export_schedule = self.findChild(PushButton, 'ex_schedule')
|
||
cf_export_schedule.clicked.connect(self.cf_export_schedule) # 导出课程表
|
||
cf_open_schedule_folder = self.findChild(PushButton, 'open_schedule_folder') # 打开课程表文件夹
|
||
cf_open_schedule_folder.clicked.connect(lambda: open_dir(os.path.join(base_directory, 'config/schedule')))
|
||
|
||
cf_import_schedule_cses = self.findChild(PushButton, 'im_schedule_cses')
|
||
cf_import_schedule_cses.clicked.connect(self.cf_import_schedule_cses) # 导入课程表(CSES)
|
||
cf_export_schedule_cses = self.findChild(PushButton, 'ex_schedule_cses')
|
||
cf_export_schedule_cses.clicked.connect(self.cf_export_schedule_cses) # 导出课程表(CSES)
|
||
cf_what_is_cses = self.findChild(HyperlinkButton, 'what_is')
|
||
cf_what_is_cses.setUrl(QUrl('https://github.com/CSES-org/CSES'))
|
||
|
||
def setup_customization_interface(self):
|
||
ct_scroll = self.findChild(SmoothScrollArea, 'ct_scroll') # 触摸屏适配
|
||
QScroller.grabGesture(ct_scroll.viewport(), QScroller.LeftMouseButtonGesture)
|
||
|
||
self.ct_update_preview()
|
||
|
||
widgets_list_widgets = self.findChild(ListWidget, 'widgets_list')
|
||
widgets_list = []
|
||
for key in list_.get_widget_config():
|
||
try:
|
||
widgets_list.append(list_.widget_name[key])
|
||
except KeyError:
|
||
logger.warning(f'未知的组件:{key}')
|
||
except Exception as e:
|
||
logger.error(f'获取组件名称时发生错误:{sys.exc_info()[0]}/{e}')
|
||
widgets_list_widgets.addItems(widgets_list)
|
||
widgets_list_widgets.sizePolicy().setVerticalPolicy(QSizePolicy.Policy.MinimumExpanding)
|
||
|
||
save_config_button = self.findChild(PrimaryPushButton, 'save_config')
|
||
save_config_button.clicked.connect(self.ct_save_widget_config)
|
||
|
||
set_ac_color = self.findChild(PushButton, 'set_ac_color') # 主题色
|
||
set_ac_color.clicked.connect(self.ct_set_ac_color)
|
||
set_fc_color = self.findChild(PushButton, 'set_fc_color')
|
||
set_fc_color.clicked.connect(self.ct_set_fc_color)
|
||
set_floating_time_color = self.findChild(PushButton, 'set_fc_color_2')
|
||
set_floating_time_color.clicked.connect(self.ct_set_floating_time_color)
|
||
|
||
open_theme_folder = self.findChild(HyperlinkLabel, 'open_theme_folder') # 打开主题文件夹
|
||
open_theme_folder.clicked.connect(lambda: open_dir(os.path.join(base_directory, 'ui')))
|
||
|
||
select_theme_combo = self.findChild(ComboBox, 'combo_theme_select') # 主题选择
|
||
select_theme_combo.addItems(list_.theme_names)
|
||
print(list_.theme_folder, list_.theme_names, get_theme_name())
|
||
select_theme_combo.setCurrentIndex(list_.theme_folder.index(get_theme_name()))
|
||
select_theme_combo.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf('General', 'theme',
|
||
list_.get_theme_ui_path(select_theme_combo.currentText())))
|
||
|
||
color_mode_combo = self.findChild(ComboBox, 'combo_color_mode') # 颜色模式选择
|
||
color_mode_combo.addItems(list_.color_mode)
|
||
color_mode_combo.setCurrentIndex(int(config_center.read_conf('General', 'color_mode')))
|
||
color_mode_combo.currentIndexChanged.connect(self.ct_change_color_mode)
|
||
|
||
widgets_combo = self.findChild(ComboBox, 'widgets_combo') # 组件选择
|
||
widgets_combo.addItems(list_.get_widget_names())
|
||
|
||
search_city_button = self.findChild(PushButton, 'select_city') # 查找城市
|
||
search_city_button.clicked.connect(self.show_search_city)
|
||
|
||
add_widget_button = self.findChild(PrimaryPushButton, 'add_widget')
|
||
add_widget_button.clicked.connect(self.ct_add_widget)
|
||
|
||
remove_widget_button = self.findChild(PushButton, 'remove_widget')
|
||
remove_widget_button.clicked.connect(self.ct_remove_widget)
|
||
|
||
slider_opacity = self.findChild(Slider, 'slider_opacity')
|
||
slider_opacity.setValue(int(config_center.read_conf('General', 'opacity')))
|
||
slider_opacity.valueChanged.connect(
|
||
lambda: config_center.write_conf('General', 'opacity', str(slider_opacity.value()))
|
||
) # 透明度
|
||
|
||
blur_countdown = self.findChild(SwitchButton, 'switch_blur_countdown')
|
||
blur_countdown.setChecked(int(config_center.read_conf('General', 'blur_countdown')))
|
||
blur_countdown.checkedChanged.connect(lambda checked: switch_checked('General', 'blur_countdown', checked))
|
||
# 模糊倒计时
|
||
switch_blur_floating = self.findChild(SwitchButton, 'switch_blur_countdown_2')
|
||
switch_blur_floating.setChecked(int(config_center.read_conf('General', 'blur_floating_countdown')))
|
||
switch_blur_floating.checkedChanged.connect(
|
||
lambda checked: config_center.write_conf('General', 'blur_floating_countdown', int(checked))
|
||
)
|
||
|
||
select_weather_api = self.findChild(ComboBox, 'select_weather_api') # 天气API选择
|
||
select_weather_api.addItems(weather_db.api_config['weather_api_list_zhCN'])
|
||
select_weather_api.setCurrentIndex(weather_db.api_config['weather_api_list'].index(
|
||
config_center.read_conf('Weather', 'api')
|
||
))
|
||
select_weather_api.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf('Weather', 'api',
|
||
weather_db.api_config['weather_api_list'][
|
||
select_weather_api.currentIndex()])
|
||
)
|
||
|
||
api_key_edit = self.findChild(LineEdit, 'api_key_edit') # API密钥
|
||
api_key_edit.setText(config_center.read_conf('Weather', 'api_key'))
|
||
api_key_edit.textChanged.connect(lambda: config_center.write_conf('Weather', 'api_key', api_key_edit.text()))
|
||
|
||
def setup_about_interface(self):
|
||
ab_scroll = self.findChild(SmoothScrollArea, 'ab_scroll') # 触摸屏适配
|
||
QScroller.grabGesture(ab_scroll.viewport(), QScroller.LeftMouseButtonGesture)
|
||
|
||
self.version = self.findChild(BodyLabel, 'version')
|
||
|
||
check_update_btn = self.findChild(PrimaryPushButton, 'check_update')
|
||
check_update_btn.setIcon(fIcon.SYNC)
|
||
check_update_btn.clicked.connect(self.check_update)
|
||
|
||
self.auto_check_update = self.ifInterface.findChild(SwitchButton, 'auto_check_update')
|
||
self.auto_check_update.setChecked(int(config_center.read_conf("Other", "auto_check_update")))
|
||
self.auto_check_update.checkedChanged.connect(
|
||
lambda checked: switch_checked("Other", "auto_check_update", checked)
|
||
) # 自动检查更新
|
||
|
||
self.version_channel = self.findChild(ComboBox, 'version_channel')
|
||
self.version_channel.addItems(list_.version_channel)
|
||
self.version_channel.setCurrentIndex(int(config_center.read_conf("Other", "version_channel")))
|
||
self.version_channel.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf("Other", "version_channel", self.version_channel.currentIndex())
|
||
) # 版本更新通道
|
||
|
||
github_page = self.findChild(PushButton, "button_github")
|
||
github_page.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(
|
||
'https://github.com/RinLit-233-shiroko/Class-Widgets')))
|
||
|
||
bilibili_page = self.findChild(PushButton, 'button_bilibili')
|
||
bilibili_page.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(
|
||
'https://space.bilibili.com/569522843')))
|
||
|
||
license_button = self.findChild(PushButton, 'button_show_license')
|
||
license_button.clicked.connect(self.show_license)
|
||
|
||
thanks_button = self.findChild(PushButton, 'button_thanks')
|
||
thanks_button.clicked.connect(lambda: QDesktopServices.openUrl(QUrl(
|
||
'https://github.com/RinLit-233-shiroko/Class-Widgets?tab=readme-ov-file#致谢')))
|
||
|
||
self.check_update()
|
||
|
||
def setup_advance_interface(self):
|
||
adv_scroll = self.adInterface.findChild(SmoothScrollArea, 'adv_scroll') # 触摸屏适配
|
||
QScroller.grabGesture(adv_scroll.viewport(), QScroller.LeftMouseButtonGesture)
|
||
|
||
margin_spin = self.adInterface.findChild(SpinBox, 'margin_spin')
|
||
margin_spin.setValue(int(config_center.read_conf('General', 'margin')))
|
||
margin_spin.valueChanged.connect(
|
||
lambda: config_center.write_conf('General', 'margin', str(margin_spin.value()))
|
||
) # 保存边距设定
|
||
|
||
self.conf_combo = self.adInterface.findChild(ComboBox, 'conf_combo')
|
||
self.conf_combo.clear()
|
||
self.conf_combo.addItems(list_.get_schedule_config())
|
||
self.conf_combo.setCurrentIndex(
|
||
list_.get_schedule_config().index(config_center.read_conf('General', 'schedule')))
|
||
self.conf_combo.currentIndexChanged.connect(self.ad_change_file) # 切换配置文件
|
||
|
||
conf_name = self.adInterface.findChild(LineEdit, 'conf_name')
|
||
conf_name.setText(config_center.schedule_name[:-5])
|
||
conf_name.textEdited.connect(self.ad_change_file_name)
|
||
|
||
window_status_combo = self.adInterface.findChild(ComboBox, 'window_status_combo')
|
||
window_status_combo.addItems(list_.window_status)
|
||
window_status_combo.setCurrentIndex(int(config_center.read_conf('General', 'pin_on_top')))
|
||
window_status_combo.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf('General', 'pin_on_top', str(window_status_combo.currentIndex()))
|
||
) # 窗口状态
|
||
|
||
switch_startup = self.adInterface.findChild(SwitchButton, 'switch_startup')
|
||
switch_startup.setChecked(int(config_center.read_conf('General', 'auto_startup')))
|
||
switch_startup.checkedChanged.connect(lambda checked: switch_checked('General', 'auto_startup', checked))
|
||
# 开机自启
|
||
if os.name != 'nt':
|
||
switch_startup.setEnabled(False)
|
||
|
||
hide_mode_combo = self.adInterface.findChild(ComboBox, 'hide_mode_combo')
|
||
hide_mode_combo.addItems(list_.hide_mode if os.name == 'nt' else list_.non_nt_hide_mode)
|
||
hide_mode_combo.setCurrentIndex(int(config_center.read_conf('General', 'hide')))
|
||
hide_mode_combo.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf('General', 'hide', str(hide_mode_combo.currentIndex()))
|
||
) # 隐藏模式
|
||
|
||
hide_method_default = self.adInterface.findChild(RadioButton, 'hide_method_default')
|
||
hide_method_default.setChecked(config_center.read_conf('General', 'hide_method') == '0')
|
||
hide_method_default.toggled.connect(lambda: config_center.write_conf('General', 'hide_method', '0'))
|
||
if os.name != 'nt':
|
||
hide_method_default.setEnabled(False)
|
||
# 默认隐藏
|
||
|
||
hide_method_all = self.adInterface.findChild(RadioButton, 'hide_method_all')
|
||
hide_method_all.setChecked(config_center.read_conf('General', 'hide_method') == '1')
|
||
hide_method_all.toggled.connect(lambda: config_center.write_conf('General', 'hide_method', '1'))
|
||
# 单击全部隐藏
|
||
|
||
hide_method_floating = self.adInterface.findChild(RadioButton, 'hide_method_floating')
|
||
hide_method_floating.setChecked(config_center.read_conf('General', 'hide_method') == '2')
|
||
hide_method_floating.toggled.connect(lambda: config_center.write_conf('General', 'hide_method', '2'))
|
||
# 最小化为浮窗
|
||
|
||
switch_enable_exclude = self.adInterface.findChild(SwitchButton, 'switch_exclude_startup')
|
||
switch_enable_exclude.setChecked(int(config_center.read_conf('General', 'excluded_lesson')))
|
||
switch_enable_exclude.checkedChanged.connect(
|
||
lambda checked: switch_checked('General', 'excluded_lesson', checked))
|
||
# 允许排除课程
|
||
|
||
exclude_lesson = self.adInterface.findChild(LineEdit, 'excluded_lessons')
|
||
exclude_lesson.setText(config_center.read_conf('General', 'excluded_lessons'))
|
||
exclude_lesson.textChanged.connect(
|
||
lambda: config_center.write_conf('General', 'excluded_lessons', exclude_lesson.text()))
|
||
# 排除课程
|
||
|
||
switch_enable_click = self.adInterface.findChild(SwitchButton, 'switch_enable_click')
|
||
switch_enable_click.setChecked(int(config_center.read_conf('General', 'enable_click')))
|
||
switch_enable_click.checkedChanged.connect(lambda checked: switch_checked('General', 'enable_click', checked))
|
||
# 允许点击
|
||
|
||
switch_enable_alt_schedule = self.adInterface.findChild(SwitchButton, 'switch_enable_alt_schedule')
|
||
switch_enable_alt_schedule.setChecked(int(config_center.read_conf('General', 'enable_alt_schedule')))
|
||
switch_enable_alt_schedule.checkedChanged.connect(
|
||
lambda checked: switch_checked('General', 'enable_alt_schedule', checked)
|
||
) # 安全模式
|
||
|
||
switch_enable_safe_mode = self.adInterface.findChild(SwitchButton, 'switch_safe_mode')
|
||
switch_enable_safe_mode.setChecked(int(config_center.read_conf('Other', 'safe_mode')))
|
||
switch_enable_safe_mode.checkedChanged.connect(
|
||
lambda checked: switch_checked('Other', 'safe_mode', checked)
|
||
)
|
||
# 安全模式开关
|
||
|
||
switch_enable_multiple_programs = self.adInterface.findChild(SwitchButton, 'switch_multiple_programs')
|
||
switch_enable_multiple_programs.setChecked(int(config_center.read_conf('Other', 'multiple_programs')))
|
||
switch_enable_multiple_programs.checkedChanged.connect(
|
||
lambda checked: switch_checked('Other', 'multiple_programs', checked)
|
||
) # 多开程序
|
||
|
||
switch_disable_log = self.adInterface.findChild(SwitchButton, 'switch_disable_log')
|
||
switch_disable_log.setChecked(int(config_center.read_conf('Other', 'do_not_log')))
|
||
switch_disable_log.checkedChanged.connect(
|
||
lambda checked: switch_checked('Other', 'do_not_log', checked)
|
||
) # 禁用日志
|
||
|
||
button_clear_log = self.adInterface.findChild(PushButton, 'button_clear_log')
|
||
button_clear_log.clicked.connect(self.clear_log) # 清空日志
|
||
|
||
set_start_date = self.adInterface.findChild(CalendarPicker, 'set_start_date') # 日期
|
||
if config_center.read_conf('Date', 'start_date') != '':
|
||
set_start_date.setDate(QDate.fromString(config_center.read_conf('Date', 'start_date'), 'yyyy-M-d'))
|
||
set_start_date.dateChanged.connect(
|
||
lambda: config_center.write_conf('Date', 'start_date', set_start_date.date.toString('yyyy-M-d'))) # 开学日期
|
||
|
||
offset_spin = self.adInterface.findChild(SpinBox, 'offset_spin')
|
||
offset_spin.setValue(int(config_center.read_conf('General', 'time_offset')))
|
||
offset_spin.valueChanged.connect(
|
||
lambda: config_center.write_conf('General', 'time_offset', str(offset_spin.value()))
|
||
) # 保存时差偏移
|
||
|
||
text_scale_factor = self.adInterface.findChild(LineEdit, 'text_scale_factor')
|
||
text_scale_factor.setText(str(float(config_center.read_conf('General', 'scale')) * 100) + '%') # 初始化缩放系数显示
|
||
|
||
slider_scale_factor = self.adInterface.findChild(Slider, 'slider_scale_factor')
|
||
slider_scale_factor.setValue(int(float(config_center.read_conf('General', 'scale')) * 100))
|
||
slider_scale_factor.valueChanged.connect(
|
||
lambda: (config_center.write_conf('General', 'scale', str(slider_scale_factor.value() / 100)),
|
||
text_scale_factor.setText(str(slider_scale_factor.value()) + '%'))
|
||
) # 保存缩放系数
|
||
|
||
what_is_hide_mode_3 = self.adInterface.findChild(HyperlinkLabel, 'what_is_hide_mode_3')
|
||
|
||
def what_is_hide_mode_3_clicked():
|
||
w = MessageBox('灵活模式', '灵活模式为上课时自动隐藏,可手动改变隐藏状态,当前课程状态(上课/课间)改变后会清除手动隐藏状态,重新转为自动隐藏。', self)
|
||
w.cancelButton.hide()
|
||
w.exec()
|
||
what_is_hide_mode_3.clicked.connect(what_is_hide_mode_3_clicked)
|
||
|
||
def setup_schedule_edit(self):
|
||
se_load_item()
|
||
se_set_button = self.findChild(ToolButton, 'set_button')
|
||
se_set_button.setIcon(fIcon.EDIT)
|
||
se_set_button.setToolTip('编辑课程')
|
||
se_set_button.installEventFilter(ToolTipFilter(se_set_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
se_set_button.clicked.connect(self.se_edit_item)
|
||
|
||
se_clear_button = self.findChild(ToolButton, 'clear_button')
|
||
se_clear_button.setIcon(fIcon.DELETE)
|
||
se_clear_button.setToolTip('清空课程')
|
||
se_clear_button.installEventFilter(ToolTipFilter(se_clear_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
se_clear_button.clicked.connect(self.se_delete_item)
|
||
|
||
se_class_kind_combo = self.findChild(ComboBox, 'class_combo') # 课程类型
|
||
se_class_kind_combo.addItems(list_.class_kind)
|
||
|
||
se_week_combo = self.findChild(ComboBox, 'week_combo') # 星期
|
||
se_week_combo.addItems(list_.week)
|
||
se_week_combo.currentIndexChanged.connect(self.se_upload_list)
|
||
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
se_schedule_list.addItems(schedule_dict[str(current_week)])
|
||
se_schedule_list.itemChanged.connect(self.se_upload_item)
|
||
QScroller.grabGesture(se_schedule_list.viewport(), QScroller.LeftMouseButtonGesture) # 触摸屏适配
|
||
|
||
se_save_button = self.findChild(PrimaryPushButton, 'save_schedule')
|
||
se_save_button.clicked.connect(self.se_save_item)
|
||
|
||
se_week_type_combo = self.findChild(ComboBox, 'week_type_combo')
|
||
se_week_type_combo.addItems(list_.week_type)
|
||
se_week_type_combo.currentIndexChanged.connect(self.se_upload_list)
|
||
|
||
se_copy_schedule_button = self.findChild(PushButton, 'copy_schedule')
|
||
se_copy_schedule_button.hide()
|
||
se_copy_schedule_button.clicked.connect(self.se_copy_odd_schedule)
|
||
|
||
quick_set_schedule = self.findChild(ListWidget, 'subject_list')
|
||
quick_set_schedule.addItems(list_.class_kind[1:])
|
||
quick_set_schedule.itemClicked.connect(self.se_quick_set_schedule)
|
||
|
||
quick_select_week_button = self.findChild(PushButton, 'quick_select_week')
|
||
quick_select_week_button.clicked.connect(self.se_quick_select_week)
|
||
|
||
def setup_timeline_edit(self): # 底层大改
|
||
self.te_load_item() # 加载时段
|
||
# teInterface
|
||
te_add_button = self.findChild(ToolButton, 'add_button') # 添加
|
||
te_add_button.setIcon(fIcon.ADD)
|
||
te_add_button.setToolTip('添加时间线') # 增加提示
|
||
te_add_button.installEventFilter(ToolTipFilter(te_add_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
te_add_button.clicked.connect(self.te_add_item)
|
||
te_add_button.clicked.connect(self.te_upload_item)
|
||
|
||
te_add_part_button = self.findChild(ToolButton, 'add_part_button') # 添加节点
|
||
te_add_part_button.setIcon(fIcon.ADD)
|
||
te_add_part_button.setToolTip('添加节点')
|
||
te_add_part_button.installEventFilter(
|
||
ToolTipFilter(te_add_part_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
te_add_part_button.clicked.connect(self.te_add_part)
|
||
|
||
te_part_type_combo = self.findChild(ComboBox, 'part_type') # 节次类型
|
||
te_part_type_combo.clear()
|
||
te_part_type_combo.addItems(list_.part_type)
|
||
|
||
te_name_edit = self.findChild(EditableComboBox, 'name_part_combo') # 名称
|
||
te_name_edit.addItems(list_.time)
|
||
|
||
te_delete_part_button = self.findChild(ToolButton, 'delete_part_button') # 删除节点
|
||
te_delete_part_button.setIcon(fIcon.DELETE)
|
||
te_delete_part_button.setToolTip('删除节点')
|
||
te_delete_part_button.installEventFilter(
|
||
ToolTipFilter(te_delete_part_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
te_delete_part_button.clicked.connect(self.te_delete_part)
|
||
|
||
te_edit_button = self.findChild(ToolButton, 'edit_button') # 编辑
|
||
te_edit_button.setIcon(fIcon.EDIT)
|
||
te_edit_button.setToolTip('编辑时间线')
|
||
te_edit_button.installEventFilter(ToolTipFilter(te_edit_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
te_edit_button.clicked.connect(self.te_edit_item)
|
||
|
||
te_delete_button = self.findChild(ToolButton, 'delete_button') # 删除
|
||
te_delete_button.setIcon(fIcon.DELETE)
|
||
te_delete_button.setToolTip('删除时间线')
|
||
te_delete_button.installEventFilter(
|
||
ToolTipFilter(te_delete_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
te_delete_button.clicked.connect(self.te_delete_item)
|
||
te_delete_button.clicked.connect(self.te_upload_item)
|
||
|
||
te_class_activity_combo = self.findChild(ComboBox, 'class_activity') # 活动类型
|
||
te_class_activity_combo.addItems(list_.class_activity)
|
||
te_class_activity_combo.setToolTip('选择活动类型(“课程”或“课间”)')
|
||
te_class_activity_combo.currentIndexChanged.connect(self.te_sync_time)
|
||
|
||
te_select_timeline = self.findChild(ComboBox, 'select_timeline') # 选择时间线
|
||
te_select_timeline.addItem('默认')
|
||
te_select_timeline.addItems(list_.week)
|
||
te_select_timeline.setToolTip('选择一周内的某一天的时间线')
|
||
te_select_timeline.currentIndexChanged.connect(self.te_upload_list)
|
||
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list') # 所选时间线列表
|
||
te_timeline_list.addItems(timeline_dict['default'])
|
||
te_timeline_list.itemChanged.connect(self.te_upload_item)
|
||
|
||
te_part_time = self.teInterface.findChild(TimeEdit, 'part_time') # 节次时间
|
||
te_part_time.timeChanged.connect(
|
||
lambda: self.show_tip_flyout('重要提示', '请使用 24 小时制', te_part_time)
|
||
)
|
||
|
||
te_save_button = self.findChild(PrimaryPushButton, 'save') # 保存
|
||
te_save_button.clicked.connect(self.te_save_item)
|
||
|
||
part_list = self.findChild(ListWidget, 'part_list')
|
||
QScroller.grabGesture(te_timeline_list.viewport(), QScroller.LeftMouseButtonGesture) # 触摸屏适配
|
||
QScroller.grabGesture(part_list.viewport(), QScroller.LeftMouseButtonGesture) # 触摸屏适配
|
||
self.te_detect_item()
|
||
self.te_update_parts_name() # 修复在启动时无法添加时段到下拉框的问题
|
||
|
||
def setup_schedule_preview(self):
|
||
subtitle = self.findChild(SubtitleLabel, 'subtitle_file')
|
||
subtitle.setText(f'预览 - {config_center.schedule_name[:-5]}')
|
||
|
||
schedule_view = self.findChild(TableWidget, 'schedule_view')
|
||
schedule_view.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) # 使列表自动等宽
|
||
|
||
sp_week_type_combo = self.findChild(ComboBox, 'pre_week_type_combo')
|
||
sp_week_type_combo.addItems(list_.week_type)
|
||
sp_week_type_combo.currentIndexChanged.connect(self.sp_fill_grid_row)
|
||
|
||
# 设置表格
|
||
schedule_view.setColumnCount(7)
|
||
schedule_view.setHorizontalHeaderLabels(list_.week[0:7])
|
||
schedule_view.setBorderVisible(True)
|
||
schedule_view.verticalHeader().hide()
|
||
schedule_view.setBorderRadius(8)
|
||
QScroller.grabGesture(schedule_view.viewport(), QScroller.LeftMouseButtonGesture) # 触摸屏适配
|
||
self.sp_fill_grid_row()
|
||
|
||
def save_volume(self):
|
||
slider_volume = self.findChild(Slider, 'slider_volume')
|
||
config_center.write_conf('Audio', 'volume', str(slider_volume.value()))
|
||
|
||
def show_search_city(self):
|
||
search_city_dialog = selectCity(self)
|
||
if search_city_dialog.exec():
|
||
selected_city = search_city_dialog.city_list.selectedItems()
|
||
if selected_city:
|
||
config_center.write_conf('Weather', 'city', wd.search_code_by_name((selected_city[0].text(),'')))
|
||
|
||
def show_license(self):
|
||
license_dialog = licenseDialog(self)
|
||
license_dialog.exec()
|
||
|
||
def save_prepare_time(self):
|
||
prepare_time_spin = self.findChild(SpinBox, 'spin_prepare_class')
|
||
config_center.write_conf('Toast', 'prepare_minutes', str(prepare_time_spin.value()))
|
||
|
||
def clear_log(self): # 清空日志
|
||
def get_directory_size(path): # 计算目录大小
|
||
total_size = 0
|
||
for dir_path, dir_names, filenames in os.walk(path):
|
||
for file_name in filenames:
|
||
file_path = os.path.join(dir_path, file_name)
|
||
total_size += os.path.getsize(file_path)
|
||
total_size /= 1024
|
||
return round(total_size, 2)
|
||
|
||
self.button_clear_log = self.adInterface.findChild(PushButton, 'button_clear_log')
|
||
size = get_directory_size('log')
|
||
|
||
try:
|
||
if os.path.exists('log'):
|
||
rmtree('log')
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='已清除日志',
|
||
content=f"已清空所有日志文件,约 {size} KB",
|
||
target=self.button_clear_log,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
else:
|
||
Flyout.create(
|
||
icon=InfoBarIcon.INFORMATION,
|
||
title='未找到日志',
|
||
content="日志目录下为空,已清理完成。",
|
||
target=self.button_clear_log,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
except OSError: # 遇到程序正在使用的log,忽略
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='已清除日志',
|
||
content=f"已清空所有日志文件,约 {size} KB",
|
||
target=self.button_clear_log,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
except Exception as e:
|
||
Flyout.create(
|
||
icon=InfoBarIcon.ERROR,
|
||
title='清除日志失败!',
|
||
content=f"清除日志失败:{e}",
|
||
target=self.button_clear_log,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
|
||
def ct_change_color_mode(self):
|
||
color_mode_combo = self.findChild(ComboBox, 'combo_color_mode')
|
||
config_center.write_conf('General', 'color_mode', str(color_mode_combo.currentIndex()))
|
||
if color_mode_combo.currentIndex() == 0:
|
||
tg_theme = Theme.LIGHT
|
||
elif color_mode_combo.currentIndex() == 1:
|
||
tg_theme = Theme.DARK
|
||
else:
|
||
tg_theme = Theme.AUTO
|
||
setTheme(tg_theme)
|
||
self.ct_update_preview()
|
||
|
||
def ct_add_widget(self):
|
||
widgets_list = self.findChild(ListWidget, 'widgets_list')
|
||
widgets_combo = self.findChild(ComboBox, 'widgets_combo')
|
||
if (not widgets_list.findItems(widgets_combo.currentText(), QtCore.Qt.MatchFlag.MatchExactly)) or widgets_combo.currentText() in list_.native_widget_name:
|
||
widgets_list.addItem(widgets_combo.currentText())
|
||
self.ct_update_preview()
|
||
|
||
def ct_remove_widget(self):
|
||
widgets_list = self.findChild(ListWidget, 'widgets_list')
|
||
if widgets_list.count() > 2:
|
||
widgets_list.takeItem(widgets_list.currentRow())
|
||
self.ct_update_preview()
|
||
else:
|
||
w = MessageBox('无法删除', '至少需要保留两个小组件。', self)
|
||
w.cancelButton.hide() # 隐藏取消按钮
|
||
w.buttonLayout.insertStretch(0, 1)
|
||
w.exec()
|
||
|
||
def ct_set_ac_color(self):
|
||
current_color = QColor(f'#{config_center.read_conf("Color", "attend_class")}')
|
||
w = ColorDialog(current_color, "更改上课时主题色", self, enableAlpha=False)
|
||
w.colorChanged.connect(lambda color: config_center.write_conf('Color', 'attend_class', color.name()[1:]))
|
||
w.exec()
|
||
|
||
def ct_set_fc_color(self):
|
||
current_color = QColor(f'#{config_center.read_conf("Color", "finish_class")}')
|
||
w = ColorDialog(current_color, "更改课间时主题色", self, enableAlpha=False)
|
||
w.colorChanged.connect(lambda color: config_center.write_conf('Color', 'finish_class', color.name()[1:]))
|
||
w.exec()
|
||
|
||
def ct_set_floating_time_color(self):
|
||
current_color = QColor(f'#{config_center.read_conf("Color", "floating_time")}')
|
||
w = ColorDialog(current_color, "更改浮窗时间颜色", self, enableAlpha=False)
|
||
w.colorChanged.connect(lambda color: config_center.write_conf('Color', 'floating_time', color.name()[1:]))
|
||
w.exec()
|
||
self.ct_update_preview()
|
||
|
||
def cf_export_schedule(self): # 导出课程表
|
||
file_path, _ = QFileDialog.getSaveFileName(self, "保存文件", config_center.schedule_name,
|
||
"Json 配置文件 (*.json)")
|
||
if file_path:
|
||
if list_.export_schedule(file_path, config_center.schedule_name):
|
||
alert = MessageBox('您已成功导出课程表配置文件',
|
||
f'文件将导出于{file_path}', self)
|
||
alert.cancelButton.hide()
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
if alert.exec():
|
||
return 0
|
||
else:
|
||
print('导出失败!')
|
||
alert = MessageBox('导出失败!',
|
||
'课程表文件导出失败,\n'
|
||
'可能为文件损坏,请将此情况反馈给开发者。', self)
|
||
alert.cancelButton.hide()
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
if alert.exec():
|
||
return 0
|
||
|
||
def check_update(self):
|
||
self.version.setText(f'当前版本:{config_center.read_conf("Other", "version")}\n正在检查最新版本…')
|
||
self.version_thread = VersionThread()
|
||
self.version_thread.version_signal.connect(self.check_version)
|
||
self.version_thread.start()
|
||
|
||
def check_version(self, version): # 检查更新
|
||
if 'error' in version:
|
||
self.version.setText(f'当前版本:{config_center.read_conf("Other", "version")}\n{version["error"]}')
|
||
|
||
if utils.tray_icon:
|
||
utils.tray_icon.push_error_notification(
|
||
"检查更新失败!",
|
||
f"检查更新失败!\n{version['error']}"
|
||
)
|
||
return False
|
||
|
||
channel = int(config_center.read_conf("Other", "version_channel"))
|
||
new_version = version['version_release' if channel == 0 else 'version_beta']
|
||
local_version = config_center.read_conf("Other", "version")
|
||
|
||
logger.debug(f"服务端版本: {Version(new_version)},本地版本: {Version(local_version)}")
|
||
if Version(new_version) <= Version(local_version):
|
||
self.version.setText(f'当前版本:{local_version}\n当前为最新版本')
|
||
else:
|
||
self.version.setText(f'当前版本:{local_version}\n最新版本:{new_version}')
|
||
|
||
if utils.tray_icon:
|
||
utils.tray_icon.push_update_notification(f"新版本速递:{new_version}")
|
||
|
||
def cf_import_schedule_cses(self): # 导入课程表(CSES)
|
||
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "CSES 通用课程表交换文件 (*.yaml)")
|
||
if file_path:
|
||
file_name = file_path.split("/")[-1]
|
||
save_path = f"{base_directory}/config/schedule/{file_name.replace('.yaml', '.json')}"
|
||
|
||
print(save_path)
|
||
importer = CSES_Converter(file_path)
|
||
importer.load_parser()
|
||
cw_data = importer.convert_to_cw()
|
||
if not cw_data:
|
||
alert = MessageBox('转换失败!',
|
||
'课程表文件转换失败!\n'
|
||
'可能为格式错误或文件损坏,请检查此文件是否为正确的 CSES 课程表文件。\n'
|
||
'详情请查看Log日志,日志位于./log/下。', self)
|
||
alert.cancelButton.hide() # 隐藏取消按钮
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
alert.exec()
|
||
try:
|
||
with open(save_path, 'w', encoding='utf-8') as f:
|
||
json.dump(cw_data, f, ensure_ascii=False, indent=4)
|
||
self.conf_combo.addItem(file_name.replace('.yaml', '.json'))
|
||
alert = MessageBox('您已成功导入 CSES 课程表配置文件',
|
||
'请在“高级选项”中手动切换您的配置文件。', self)
|
||
alert.cancelButton.hide()
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
alert.exec()
|
||
except Exception as e:
|
||
logger.error(f'导入课程表时发生错误:{e}')
|
||
alert = MessageBox('导入失败!',
|
||
'课程表文件导入失败!\n'
|
||
'可能为格式错误或文件损坏,请检查此文件是否为正确的 CSES 课程表文件。\n'
|
||
'详情请查看Log日志,日志位于./log/下。', self)
|
||
alert.cancelButton.hide() # 隐藏取消按钮
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
alert.exec()
|
||
|
||
def cf_export_schedule_cses(self): # 导出课程表(CSES)
|
||
file_path, _ = QFileDialog.getSaveFileName(
|
||
self, "保存文件", config_center.schedule_name.replace('.json', '.yaml'), "CSES 通用课程表交换文件 (*.yaml)")
|
||
if file_path:
|
||
exporter = CSES_Converter(file_path)
|
||
exporter.load_generator()
|
||
if exporter.convert_to_cses(cw_path=f'{base_directory}/config/schedule/{config_center.schedule_name}'):
|
||
alert = MessageBox('您已成功导出课程表配置文件',
|
||
f'文件将导出于{file_path}', self)
|
||
alert.cancelButton.hide()
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
if alert.exec():
|
||
return 0
|
||
else:
|
||
print('导出失败!')
|
||
alert = MessageBox('导出失败!',
|
||
'课程表文件导出失败,\n'
|
||
'可能为文件损坏,请将此情况反馈给开发者。', self)
|
||
alert.cancelButton.hide()
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
if alert.exec():
|
||
return 0
|
||
|
||
def cf_import_schedule(self): # 导入课程表
|
||
file_path, _ = QFileDialog.getOpenFileName(self, "选择文件", "", "Json 配置文件 (*.json)")
|
||
if file_path:
|
||
file_name = file_path.split("/")[-1]
|
||
if list_.import_schedule(file_path, file_name):
|
||
self.conf_combo.addItem(file_name)
|
||
alert = MessageBox('您已成功导入课程表配置文件',
|
||
'请在“高级选项”中手动切换您的配置文件。', self)
|
||
alert.cancelButton.hide() # 隐藏取消按钮,必须重启
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
else:
|
||
print('导入失败!')
|
||
alert = MessageBox('导入失败!',
|
||
'课程表文件导入失败!\n'
|
||
'可能为格式错误或文件损坏,请检查此文件是否为 Class Widgets 课程表文件。\n'
|
||
'详情请查看Log日志,日志位于./log/下。', self)
|
||
alert.cancelButton.hide() # 隐藏取消按钮
|
||
alert.buttonLayout.insertStretch(0, 1)
|
||
if alert.exec():
|
||
return 0
|
||
|
||
def ct_save_widget_config(self):
|
||
widgets_list = self.findChild(ListWidget, 'widgets_list')
|
||
widget_config = {'widgets': []}
|
||
for i in range(widgets_list.count()):
|
||
widget_config['widgets'].append(list_.widget_conf[widgets_list.item(i).text()])
|
||
if conf.save_widget_conf_to_json(widget_config):
|
||
self.ct_update_preview()
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='保存成功',
|
||
content=f"已保存至 ./config/widget.json",
|
||
target=self.findChild(PrimaryPushButton, 'save_config'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
|
||
def ct_update_preview(self):
|
||
try:
|
||
widgets_preview = self.findChild(QHBoxLayout, 'widgets_preview')
|
||
# 获取配置列表
|
||
widget_config = list_.get_widget_config()
|
||
while widgets_preview.count() > 0: # 清空预览界面
|
||
item = widgets_preview.itemAt(0)
|
||
if item:
|
||
widget = item.widget()
|
||
if widget:
|
||
widget.deleteLater()
|
||
widgets_preview.removeItem(item)
|
||
|
||
left_spacer = QSpacerItem(20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||
widgets_preview.addItem(left_spacer)
|
||
|
||
theme_folder = config_center.read_conf("General", "theme")
|
||
if not os.path.exists(f'{base_directory}/ui/{theme_folder}/theme.json'):
|
||
theme_folder = 'default' # 主题文件夹不存在,使用默认主题
|
||
logger.warning(f'主题文件夹不存在,使用默认主题:{theme_folder}')
|
||
|
||
for i in range(len(widget_config)):
|
||
widget_name = widget_config[i]
|
||
if isDarkTheme() and conf.load_theme_config(theme_folder)['support_dark_mode']:
|
||
if os.path.exists(f'{base_directory}/ui/{theme_folder}/dark/preview/{widget_name[:-3]}.png'):
|
||
path = f'{base_directory}/ui/{theme_folder}/dark/preview/{widget_name[:-3]}.png'
|
||
else:
|
||
path = f'{base_directory}/ui/{theme_folder}/dark/preview/widget-custom.png'
|
||
else:
|
||
if os.path.exists(f'ui/{theme_folder}/preview/{widget_name[:-3]}.png'):
|
||
path = f'{base_directory}/ui/{theme_folder}/preview/{widget_name[:-3]}.png'
|
||
else:
|
||
path = f'{base_directory}/ui/{theme_folder}/preview/widget-custom.png'
|
||
|
||
label = ImageLabel()
|
||
label.setImage(path)
|
||
widgets_preview.addWidget(label)
|
||
widget_config[i] = label
|
||
right_spacer = QSpacerItem(20, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||
widgets_preview.addItem(right_spacer)
|
||
except Exception as e:
|
||
logger.error(f'更新预览界面时发生错误:{e}')
|
||
|
||
def ad_change_file_name(self):
|
||
try:
|
||
conf_name = self.findChild(LineEdit, 'conf_name')
|
||
old_name = config_center.schedule_name
|
||
new_name = conf_name.text()
|
||
os.rename(f'{base_directory}/config/schedule/{old_name}',
|
||
f'{base_directory}/config/schedule/{new_name}.json') # 重命名
|
||
config_center.write_conf('General', 'schedule', f'{new_name}.json')
|
||
config_center.schedule_name = new_name + '.json'
|
||
conf_combo = self.findChild(ComboBox, 'conf_combo')
|
||
conf_combo.clear()
|
||
conf_combo.addItems(list_.get_schedule_config())
|
||
conf_combo.setCurrentIndex(list_.get_schedule_config().index(f'{new_name}.json'))
|
||
except Exception as e:
|
||
print(f'修改课程文件名称时发生错误:{e}')
|
||
logger.error(f'修改课程文件名称时发生错误:{e}')
|
||
|
||
def ad_change_file(self): # 切换课程文件
|
||
try:
|
||
conf_name = self.findChild(LineEdit, 'conf_name')
|
||
# 添加新课表
|
||
if self.conf_combo.currentText() == '添加新课表':
|
||
self.conf_combo.setCurrentIndex(-1) # 取消
|
||
# new_name = f'新课表 - {list.return_default_schedule_number() + 1}'
|
||
n2_dialog = TextFieldMessageBox(
|
||
self, '请输入新课表名称',
|
||
'请命名您的课程表计划:', '新课表 - 1', list_.get_schedule_config()
|
||
)
|
||
if not n2_dialog.exec():
|
||
return
|
||
|
||
new_name = n2_dialog.textField.text()
|
||
list_.create_new_profile(f'{new_name}.json')
|
||
self.conf_combo.clear()
|
||
self.conf_combo.addItems(list_.get_schedule_config())
|
||
config_center.write_conf('General', 'schedule', f'{new_name}.json')
|
||
self.conf_combo.setCurrentIndex(
|
||
list_.get_schedule_config().index(config_center.read_conf('General', 'schedule')))
|
||
conf_name.setText(new_name)
|
||
update_tray_tooltip()
|
||
|
||
elif self.conf_combo.currentText().endswith('.json'):
|
||
new_name = self.conf_combo.currentText()
|
||
config_center.write_conf('General', 'schedule', new_name)
|
||
conf_name.setText(new_name[:-5])
|
||
update_tray_tooltip()
|
||
|
||
else:
|
||
logger.error(f'切换课程文件时列表选择异常:{self.conf_combo.currentText()}')
|
||
Flyout.create(
|
||
icon=InfoBarIcon.ERROR,
|
||
title='错误!',
|
||
content=f"列表选项异常!{self.conf_combo.currentText()}",
|
||
target=self.conf_combo,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
return
|
||
global loaded_data
|
||
|
||
config_center.schedule_name = config_center.read_conf('General', 'schedule')
|
||
schedule_center.update_schedule()
|
||
loaded_data = schedule_center.schedule_data
|
||
self.te_load_item()
|
||
self.te_upload_list()
|
||
self.te_update_parts_name()
|
||
se_load_item()
|
||
self.se_upload_list()
|
||
self.sp_fill_grid_row()
|
||
except Exception as e:
|
||
print(f'切换配置文件时发生错误:{e}')
|
||
logger.error(f'切换配置文件时发生错误:{e}')
|
||
|
||
def sp_fill_grid_row(self): # 填充预览表格
|
||
subtitle = self.findChild(SubtitleLabel, 'subtitle_file')
|
||
subtitle.setText(f'预览 - {config_center.schedule_name[:-5]}')
|
||
sp_week_type_combo = self.findChild(ComboBox, 'pre_week_type_combo')
|
||
schedule_view = self.findChild(TableWidget, 'schedule_view')
|
||
schedule_view.setRowCount(sp_get_class_num())
|
||
if sp_week_type_combo.currentIndex() == 1:
|
||
schedule_dict_sp = schedule_even_dict
|
||
else:
|
||
schedule_dict_sp = schedule_dict
|
||
for i in range(len(schedule_dict_sp)): # 周数
|
||
for j in range(len(schedule_dict_sp[str(i)])): # 一天内全部课程
|
||
item_text = schedule_dict_sp[str(i)][j].split('-')[0]
|
||
if item_text != '未添加':
|
||
item = QTableWidgetItem(item_text)
|
||
else:
|
||
item = QTableWidgetItem('')
|
||
schedule_view.setItem(j, i, item)
|
||
item.setTextAlignment(Qt.AlignmentFlag.AlignCenter) # 设置单元格文本居中对齐
|
||
|
||
# 加载时间线
|
||
def te_load_item(self):
|
||
global morning_st, afternoon_st, loaded_data, timeline_dict
|
||
loaded_data = schedule_center.schedule_data
|
||
part = loaded_data.get('part')
|
||
part_name = loaded_data.get('part_name')
|
||
timeline = get_timeline()
|
||
# 找控件
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
te_timeline_list.clear()
|
||
part_list = self.findChild(ListWidget, 'part_list')
|
||
part_list.clear()
|
||
|
||
for part_num, part_time in part.items(): # 加载节点
|
||
prefix = part_name[part_num]
|
||
time = QTime(int(part_time[0]), int(part_time[1])).toString('h:mm')
|
||
period = time
|
||
try:
|
||
part_type = part_time[2]
|
||
except IndexError:
|
||
part_type = 'part'
|
||
|
||
part_type = list_.part_type[part_type == 'break']
|
||
text = f'{prefix} - {period} - {part_type}'
|
||
part_list.addItem(text)
|
||
|
||
for week, _ in timeline.items(): # 加载节点
|
||
all_line = []
|
||
for item_name, time in timeline[week].items(): # 加载时间线
|
||
prefix = ''
|
||
item_time = f'{timeline[week][item_name]}分钟'
|
||
# 判断前缀和时段
|
||
if item_name.startswith('a'):
|
||
prefix = '课程'
|
||
elif item_name.startswith('f'):
|
||
prefix = '课间'
|
||
period = part_name[item_name[1]]
|
||
|
||
# 还原 item_text
|
||
item_text = f"{prefix} - {item_time} - {period}"
|
||
all_line.append(item_text)
|
||
timeline_dict[week] = all_line
|
||
|
||
def se_copy_odd_schedule(self):
|
||
logger.info('复制单周课表')
|
||
global schedule_dict, schedule_even_dict
|
||
schedule_even_dict = deepcopy(schedule_dict)
|
||
self.se_upload_list()
|
||
|
||
def te_upload_list(self): # 更新时间线到列表组件
|
||
logger.info('更新列表:时间线编辑')
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
te_select_timeline = self.findChild(ComboBox, 'select_timeline')
|
||
try:
|
||
if te_select_timeline.currentIndex() == 0:
|
||
te_timeline_list.clear()
|
||
te_timeline_list.addItems(timeline_dict['default'])
|
||
else:
|
||
te_timeline_list.clear()
|
||
te_timeline_list.addItems(timeline_dict[str(te_select_timeline.currentIndex() - 1)])
|
||
self.te_detect_item()
|
||
except Exception as e:
|
||
print(f'加载时间线时发生错误:{e}')
|
||
|
||
def show_tip_flyout(self, title, content, target):
|
||
Flyout.create(
|
||
icon=InfoBarIcon.WARNING,
|
||
title=title,
|
||
content=content,
|
||
target=target,
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
|
||
# 上传课表到列表组件
|
||
def se_upload_list(self): # 更新课表到列表组件
|
||
logger.info('更新列表:课程表编辑')
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
se_schedule_list.clearSelection()
|
||
se_week_combo = self.findChild(ComboBox, 'week_combo')
|
||
se_week_type_combo = self.findChild(ComboBox, 'week_type_combo')
|
||
se_copy_schedule_button = self.findChild(PushButton, 'copy_schedule')
|
||
global current_week
|
||
try:
|
||
if se_week_type_combo.currentIndex() == 1:
|
||
se_copy_schedule_button.show()
|
||
current_week = se_week_combo.currentIndex()
|
||
se_schedule_list.clear()
|
||
se_schedule_list.addItems(schedule_even_dict[str(current_week)])
|
||
else:
|
||
se_copy_schedule_button.hide()
|
||
current_week = se_week_combo.currentIndex()
|
||
se_schedule_list.clear()
|
||
se_schedule_list.addItems(schedule_dict[str(current_week)])
|
||
except Exception as e:
|
||
print(f'加载课表时发生错误:{e}')
|
||
|
||
def se_upload_item(self): # 保存列表内容到课表文件
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
se_week_type_combo = self.findChild(ComboBox, 'week_type_combo')
|
||
if se_week_type_combo.currentIndex() == 1:
|
||
global schedule_even_dict
|
||
try:
|
||
cache_list = []
|
||
for i in range(se_schedule_list.count()):
|
||
item_text = se_schedule_list.item(i).text()
|
||
cache_list.append(item_text)
|
||
schedule_even_dict[str(current_week)][:] = cache_list
|
||
except Exception as e:
|
||
print(f'加载双周课表时发生错误:{e}')
|
||
else:
|
||
global schedule_dict
|
||
cache_list = []
|
||
for i in range(se_schedule_list.count()):
|
||
item_text = se_schedule_list.item(i).text()
|
||
cache_list.append(item_text)
|
||
schedule_dict[str(current_week)][:] = cache_list
|
||
|
||
# 保存课程
|
||
def se_save_item(self):
|
||
try:
|
||
data_dict = deepcopy(schedule_dict)
|
||
data_dict_even = deepcopy(schedule_even_dict) # 单双周保存
|
||
|
||
data_dict = convert_to_dict(data_dict)
|
||
data_dict_even = convert_to_dict(data_dict_even)
|
||
|
||
# 写入
|
||
data_dict_even = {"schedule_even": data_dict_even}
|
||
schedule_center.save_data(data_dict_even, config_center.schedule_name)
|
||
data_dict = {"schedule": data_dict}
|
||
schedule_center.save_data(data_dict, config_center.schedule_name)
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='保存成功',
|
||
content=f"已保存至 ./config/schedule/{config_center.schedule_name}",
|
||
target=self.findChild(PrimaryPushButton, 'save_schedule'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
self.sp_fill_grid_row()
|
||
except Exception as e:
|
||
logger.error(f'保存课表时发生错误: {e}')
|
||
|
||
def te_upload_item(self): # 上传时间线到列表组件
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
te_select_timeline = self.findChild(ComboBox, 'select_timeline')
|
||
global timeline_dict
|
||
cache_list = []
|
||
for i in range(te_timeline_list.count()):
|
||
item_text = te_timeline_list.item(i).text()
|
||
cache_list.append(item_text)
|
||
if te_select_timeline.currentIndex() == 0:
|
||
timeline_dict['default'] = cache_list
|
||
else:
|
||
timeline_dict[str(te_select_timeline.currentIndex() - 1)] = cache_list
|
||
|
||
# 保存时间线
|
||
def te_save_item(self):
|
||
te_part_list = self.findChild(ListWidget, 'part_list')
|
||
data_dict = {"part": {}, "part_name": {}, "timeline": {'default': {}, **{str(w): {} for w in range(7)}}}
|
||
data_timeline_dict = deepcopy(timeline_dict)
|
||
# 逐条把列表里的信息整理保存
|
||
for i in range(te_part_list.count()):
|
||
item_text = te_part_list.item(i).text()
|
||
item_info = item_text.split(' - ')
|
||
time_tostring = item_info[1].split(':')
|
||
if len(item_info) == 3:
|
||
part_type = ['part', 'break'][item_info[2] == '休息段']
|
||
else:
|
||
part_type = 'part'
|
||
data_dict['part'][str(i)] = [int(time_tostring[0]), int(time_tostring[1]), part_type]
|
||
data_dict['part_name'][str(i)] = item_info[0]
|
||
|
||
try:
|
||
for week, _ in data_timeline_dict.items():
|
||
counter = [] # 初始化计数器
|
||
for i in range(len(data_dict['part'])):
|
||
counter.append(0)
|
||
counter_key = 0
|
||
lesson_num = 0
|
||
for i in range(len(data_timeline_dict[week])):
|
||
item_text = data_timeline_dict[week][i]
|
||
item_info = item_text.split(' - ')
|
||
item_name = ''
|
||
if item_info[0] == '课程':
|
||
item_name += 'a'
|
||
lesson_num += 1
|
||
if item_info[0] == '课间':
|
||
item_name += 'f'
|
||
|
||
for key, value in data_dict['part_name'].items(): # 节点计数
|
||
if value == item_info[2]:
|
||
item_name += str(key) # +节点序数
|
||
counter_key = int(key) # 记录节点序数
|
||
break
|
||
|
||
if item_name.startswith('a'):
|
||
counter[counter_key] += 1
|
||
|
||
item_name += str(lesson_num - sum(counter[:counter_key])) # 课程序数
|
||
item_time = item_info[1][0:len(item_info[1]) - 2]
|
||
data_dict['timeline'][str(week)][item_name] = item_time
|
||
|
||
schedule_center.save_data(data_dict, config_center.schedule_name)
|
||
self.te_detect_item()
|
||
se_load_item()
|
||
self.se_upload_list()
|
||
self.se_upload_item()
|
||
self.te_upload_item()
|
||
self.sp_fill_grid_row()
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='保存成功',
|
||
content=f"已保存至 ./config/schedule/{config_center.schedule_name}",
|
||
target=self.findChild(PrimaryPushButton, 'save'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
except Exception as e:
|
||
logger.error(f'保存时间线时发生错误: {e}')
|
||
Flyout.create(
|
||
icon=InfoBarIcon.ERROR,
|
||
title='保存失败!',
|
||
content=f"{e}\n保存失败,请将 ./log/ 中的日志提交给开发者以反馈问题。",
|
||
target=self.findChild(PrimaryPushButton, 'save'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
|
||
def te_sync_time(self):
|
||
te_class_activity_combo = self.findChild(ComboBox, 'class_activity')
|
||
spin_time = self.findChild(SpinBox, 'spin_time')
|
||
if te_class_activity_combo.currentIndex() == 0:
|
||
spin_time.setValue(40)
|
||
if te_class_activity_combo.currentIndex() == 1:
|
||
spin_time.setValue(10)
|
||
|
||
def te_detect_item(self):
|
||
timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
part_list = self.findChild(ListWidget, 'part_list')
|
||
tips = self.findChild(CaptionLabel, 'tips_2')
|
||
tips_part = self.findChild(CaptionLabel, 'tips_1')
|
||
if part_list.count() > 0:
|
||
tips_part.hide()
|
||
else:
|
||
tips_part.show()
|
||
if timeline_list.count() > 0:
|
||
tips.hide()
|
||
else:
|
||
tips.show()
|
||
|
||
def te_add_item(self):
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
class_activity = self.findChild(ComboBox, 'class_activity')
|
||
spin_time = self.findChild(SpinBox, 'spin_time')
|
||
time_period = self.findChild(ComboBox, 'time_period')
|
||
if time_period.currentText() == "": # 时间段不能为空 修复 #184
|
||
Flyout.create(
|
||
icon=InfoBarIcon.WARNING,
|
||
title='无法添加时间线 o(TヘTo)',
|
||
content='在添加时间线前,先任意添加一个节点',
|
||
target=self.findChild(ToolButton, 'add_button'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
return # 时间段不能为空
|
||
te_timeline_list.addItem(
|
||
f'{class_activity.currentText()} - {spin_time.value()}分钟 - {time_period.currentText()}'
|
||
)
|
||
self.te_detect_item()
|
||
|
||
def te_add_part(self):
|
||
te_part_list = self.findChild(ListWidget, 'part_list')
|
||
te_name_part = self.findChild(EditableComboBox, 'name_part_combo')
|
||
te_part_time = self.findChild(TimeEdit, 'part_time')
|
||
te_part_type = self.findChild(ComboBox, 'part_type')
|
||
if te_part_list.count() < 10:
|
||
te_part_list.addItem(
|
||
f'{te_name_part.currentText()} - {te_part_time.time().toString("h:mm")} - {te_part_type.currentText()}'
|
||
)
|
||
else: # 最多只能添加9个节点
|
||
Flyout.create(
|
||
icon=InfoBarIcon.WARNING,
|
||
title='没办法继续添加了 o(TヘTo)',
|
||
content='Class Widgets 最多只能添加10个“节点”!',
|
||
target=self.findChild(ToolButton, 'add_part_button'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
self.te_detect_item()
|
||
self.te_update_parts_name()
|
||
|
||
def te_delete_part(self):
|
||
alert = MessageBox("您确定要删除这个时段吗?", "删除该节点后,将一并删除该节点下所有课程安排,且无法恢复。", self)
|
||
alert.yesButton.setText('删除')
|
||
alert.yesButton.setStyleSheet("""
|
||
PushButton{
|
||
border-radius: 5px;
|
||
padding: 5px 12px 6px 12px;
|
||
outline: none;
|
||
}
|
||
PrimaryPushButton{
|
||
color: white;
|
||
background-color: #FF6167;
|
||
border: 1px solid #FF8585;
|
||
border-bottom: 1px solid #943333;
|
||
}
|
||
PrimaryPushButton:hover{
|
||
background-color: #FF7E83;
|
||
border: 1px solid #FF8084;
|
||
border-bottom: 1px solid #B13939;
|
||
}
|
||
PrimaryPushButton:pressed{
|
||
color: rgba(255, 255, 255, 0.63);
|
||
background-color: #DB5359;
|
||
border: 1px solid #DB5359;
|
||
}
|
||
""")
|
||
alert.cancelButton.setText('取消')
|
||
if alert.exec():
|
||
global timeline_dict, schedule_dict
|
||
te_part_list = self.findChild(ListWidget, 'part_list')
|
||
selected_items = te_part_list.selectedItems()
|
||
if not selected_items:
|
||
return
|
||
|
||
deleted_part_name = selected_items[0].text().split(' - ')[0]
|
||
for item in selected_items:
|
||
te_part_list.takeItem(te_part_list.row(item))
|
||
|
||
# 修复了删除时段没能同步删除时间线的Bug #123
|
||
for day in timeline_dict: # 删除时间线
|
||
count = 0
|
||
break_count = 0
|
||
delete_schedule_list = []
|
||
delete_schedule_even_list = []
|
||
delete_part_list = []
|
||
for i in range(len(timeline_dict[day])):
|
||
act = timeline_dict[day][i]
|
||
count += 1
|
||
item_info = act.split(' - ')
|
||
|
||
if item_info[0] == '课间':
|
||
break_count += 1
|
||
|
||
if item_info[2] == deleted_part_name:
|
||
delete_part_list.append(act)
|
||
if item_info[0] != '课间':
|
||
if day != 'default':
|
||
delete_schedule_list.append(schedule_dict[day][count - break_count - 1])
|
||
delete_schedule_even_list.append(schedule_even_dict[day][count - break_count - 1])
|
||
else:
|
||
for j in range(7):
|
||
try:
|
||
for item in schedule_dict[str(j)]:
|
||
if item.split('-')[1] == deleted_part_name:
|
||
delete_schedule_list.append(
|
||
schedule_dict[str(j)][count - break_count - 1])
|
||
for item in schedule_even_dict[str(j)]:
|
||
if item.split('-')[1] == deleted_part_name:
|
||
delete_schedule_even_list.append(
|
||
schedule_dict[str(j)][count - break_count - 1])
|
||
except Exception as e:
|
||
logger.warning(f'删除时段时发生错误:{e}')
|
||
|
||
for item in delete_part_list: # 删除时间线
|
||
timeline_dict[day].remove(item)
|
||
if day != 'default': # 删除课表
|
||
for item in delete_schedule_list:
|
||
schedule_dict[day].remove(item)
|
||
|
||
for day in range(7): # 删除默认课程表
|
||
delete_schedule_list = []
|
||
delete_schedule_even_list = []
|
||
for item in schedule_dict[str(day)]: # 单周
|
||
if item.split('-')[1] == deleted_part_name:
|
||
delete_schedule_list.append(item)
|
||
for item in delete_schedule_list:
|
||
schedule_dict[str(day)].remove(item)
|
||
|
||
for item in schedule_even_dict[str(day)]: # 双周
|
||
if item.split('-')[1] == deleted_part_name:
|
||
delete_schedule_even_list.append(item)
|
||
for item in delete_schedule_even_list:
|
||
schedule_even_dict[str(day)].remove(item)
|
||
|
||
self.te_upload_list()
|
||
self.se_upload_list()
|
||
self.te_update_parts_name()
|
||
else:
|
||
return
|
||
|
||
def te_update_parts_name(self):
|
||
rl = []
|
||
te_time_combo = self.findChild(ComboBox, 'time_period') # 时段
|
||
te_time_combo.clear()
|
||
part_list = self.findChild(ListWidget, 'part_list')
|
||
for i in range(part_list.count()):
|
||
info = part_list.item(i).text().split(' - ')
|
||
rl.append(info[0])
|
||
te_time_combo.addItems(rl)
|
||
|
||
def te_edit_item(self):
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
class_activity = self.findChild(ComboBox, 'class_activity')
|
||
spin_time = self.findChild(SpinBox, 'spin_time')
|
||
time_period = self.findChild(ComboBox, 'time_period')
|
||
selected_items = te_timeline_list.selectedItems()
|
||
|
||
if selected_items:
|
||
selected_item = selected_items[0] # 取第一个选中的项目
|
||
selected_item.setText(
|
||
f'{class_activity.currentText()} - {spin_time.value()}分钟 - {time_period.currentText()}'
|
||
)
|
||
|
||
def se_edit_item(self):
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
se_class_combo = self.findChild(ComboBox, 'class_combo')
|
||
se_custom_class_text = self.findChild(LineEdit, 'custom_class')
|
||
selected_items = se_schedule_list.selectedItems()
|
||
|
||
if selected_items:
|
||
selected_item = selected_items[0]
|
||
name_list = selected_item.text().split('-')
|
||
if se_class_combo.currentIndex() != 0:
|
||
selected_item.setText(
|
||
f'{se_class_combo.currentText()}-{name_list[1]}'
|
||
)
|
||
else:
|
||
if se_custom_class_text.text() != '':
|
||
selected_item.setText(
|
||
f'{se_custom_class_text.text()}-{name_list[1]}'
|
||
)
|
||
se_class_combo.addItem(se_custom_class_text.text())
|
||
|
||
def se_quick_set_schedule(self): # 快速设置课表
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
quick_set_schedule = self.findChild(ListWidget, 'subject_list')
|
||
selected_items = se_schedule_list.selectedItems()
|
||
selected_subject = quick_set_schedule.currentItem().text()
|
||
if se_schedule_list.count() > 0:
|
||
if not selected_items:
|
||
se_schedule_list.setCurrentRow(0)
|
||
|
||
selected_row = se_schedule_list.currentRow()
|
||
selected_item = se_schedule_list.item(selected_row)
|
||
name_list = selected_item.text().split('-')
|
||
selected_item.setText(
|
||
f'{selected_subject}-{name_list[1]}'
|
||
)
|
||
|
||
if se_schedule_list.count() > selected_row + 1: # 选择下一行
|
||
se_schedule_list.setCurrentRow(selected_row + 1)
|
||
|
||
def se_quick_select_week(self): # 快速选择周
|
||
se_week_combo = self.findChild(ComboBox, 'week_combo')
|
||
if se_week_combo.currentIndex() != 6:
|
||
se_week_combo.setCurrentIndex(se_week_combo.currentIndex() + 1)
|
||
|
||
def te_delete_item(self):
|
||
te_timeline_list = self.findChild(ListWidget, 'timeline_list')
|
||
selected_items = te_timeline_list.selectedItems()
|
||
for item in selected_items:
|
||
te_timeline_list.takeItem(te_timeline_list.row(item))
|
||
self.te_detect_item()
|
||
|
||
def se_delete_item(self):
|
||
se_schedule_list = self.findChild(ListWidget, 'schedule_list')
|
||
selected_items = se_schedule_list.selectedItems()
|
||
if selected_items:
|
||
selected_item = selected_items[0]
|
||
name_list = selected_item.text().split('-')
|
||
selected_item.setText(
|
||
f'未添加-{name_list[1]}'
|
||
)
|
||
|
||
def cd_edit_item(self):
|
||
cd_countdown_list = self.findChild(ListWidget, 'countdown_list')
|
||
cd_text_cd = self.findChild(LineEdit, 'text_cd')
|
||
cd_set_countdown_date = self.findChild(CalendarPicker, 'set_countdown_date')
|
||
selected_items = cd_countdown_list.selectedItems()
|
||
if selected_items:
|
||
selected_item = selected_items[0]
|
||
selected_item.setText(
|
||
f"{cd_set_countdown_date.date.toString('yyyy-M-d')} - {cd_text_cd.text()}"
|
||
)
|
||
|
||
def cd_delete_item(self):
|
||
cd_countdown_list = self.findChild(ListWidget, 'countdown_list')
|
||
selected_items = cd_countdown_list.selectedItems()
|
||
if selected_items:
|
||
item = selected_items[0]
|
||
cd_countdown_list.takeItem(cd_countdown_list.row(item))
|
||
|
||
def cd_add_item(self):
|
||
cd_countdown_list = self.findChild(ListWidget, 'countdown_list')
|
||
cd_text_cd = self.findChild(LineEdit, 'text_cd')
|
||
cd_set_countdown_date = self.findChild(CalendarPicker, 'set_countdown_date')
|
||
cd_countdown_list.addItem(
|
||
f"{cd_set_countdown_date.date.toString('yyyy-M-d')} - {cd_text_cd.text()}"
|
||
)
|
||
|
||
def cd_save_item(self):
|
||
cd_countdown_list = self.findChild(ListWidget, 'countdown_list')
|
||
countdown_date = []
|
||
cd_text_custom = []
|
||
|
||
for i in range(cd_countdown_list.count()):
|
||
item = cd_countdown_list.item(i)
|
||
text = item.text().split(' - ')
|
||
countdown_date.append(text[0])
|
||
cd_text_custom.append(text[1])
|
||
|
||
Flyout.create(
|
||
icon=InfoBarIcon.SUCCESS,
|
||
title='保存成功',
|
||
content=f"已保存至 ./config.ini",
|
||
target=self.findChild(PrimaryPushButton, 'save_countdown'),
|
||
parent=self,
|
||
isClosable=True,
|
||
aniType=FlyoutAnimationType.PULL_UP
|
||
)
|
||
|
||
config_center.write_conf('Date', 'countdown_date', ','.join(countdown_date))
|
||
config_center.write_conf('Date', 'cd_text_custom', ','.join(cd_text_custom))
|
||
|
||
def setup_countdown_edit(self):
|
||
cd_load_item()
|
||
logger.debug(f"{countdown_dict}")
|
||
cd_set_button = self.findChild(ToolButton, 'set_button_cd')
|
||
cd_set_button.setIcon(fIcon.EDIT)
|
||
cd_set_button.setToolTip('编辑倒计日')
|
||
cd_set_button.installEventFilter(ToolTipFilter(cd_set_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
cd_set_button.clicked.connect(self.cd_edit_item)
|
||
|
||
cd_clear_button = self.findChild(ToolButton, 'clear_button_cd')
|
||
cd_clear_button.setIcon(fIcon.DELETE)
|
||
cd_clear_button.setToolTip('删除倒计日')
|
||
cd_clear_button.installEventFilter(ToolTipFilter(cd_clear_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
cd_clear_button.clicked.connect(self.cd_delete_item)
|
||
|
||
cd_add_button = self.findChild(ToolButton, 'add_button_cd')
|
||
cd_add_button.setIcon(fIcon.ADD)
|
||
cd_add_button.setToolTip('添加倒计日')
|
||
cd_add_button.installEventFilter(ToolTipFilter(cd_add_button, showDelay=300, position=ToolTipPosition.TOP))
|
||
cd_add_button.clicked.connect(self.cd_add_item)
|
||
|
||
cd_schedule_list = self.findChild(ListWidget, 'countdown_list')
|
||
cd_schedule_list.addItems([f"{date} - {countdown_dict[date]}" for date in countdown_dict])
|
||
|
||
cd_save_button = self.findChild(PrimaryPushButton, 'save_countdown')
|
||
cd_save_button.clicked.connect(self.cd_save_item)
|
||
|
||
cd_mode = self.findChild(ComboBox, 'countdown_mode')
|
||
cd_mode.addItems(list_.countdown_modes)
|
||
cd_mode.setCurrentIndex(int(config_center.read_conf('Date', 'countdown_custom_mode')))
|
||
cd_mode.currentIndexChanged.connect(
|
||
lambda: config_center.write_conf('Date', 'countdown_custom_mode', str(cd_mode.currentIndex())))
|
||
|
||
cd_upd_cd = self.findChild(SpinBox, 'countdown_upd_cd')
|
||
cd_upd_cd.setValue(int(config_center.read_conf('Date', 'countdown_upd_cd')))
|
||
cd_upd_cd.valueChanged.connect(
|
||
lambda: config_center.write_conf('Date', 'countdown_upd_cd', str(cd_upd_cd.value())))
|
||
|
||
def m_start_time_changed(self):
|
||
global morning_st
|
||
te_m_start_time = self.findChild(TimeEdit, 'morningStartTime')
|
||
unformatted_time = te_m_start_time.time()
|
||
h = unformatted_time.hour()
|
||
m = unformatted_time.minute()
|
||
morning_st = (h, m)
|
||
|
||
def a_start_time_changed(self):
|
||
global afternoon_st
|
||
te_m_start_time = self.findChild(TimeEdit, 'afternoonStartTime')
|
||
unformatted_time = te_m_start_time.time()
|
||
h = unformatted_time.hour()
|
||
m = unformatted_time.minute()
|
||
afternoon_st = (h, m)
|
||
|
||
def init_nav(self):
|
||
self.addSubInterface(self.spInterface, fIcon.HOME, '课表预览')
|
||
self.addSubInterface(self.teInterface, fIcon.DATE_TIME, '时间线编辑')
|
||
self.addSubInterface(self.seInterface, fIcon.EDUCATION, '课程表编辑')
|
||
self.addSubInterface(self.cdInterface, fIcon.CALENDAR, '倒计日编辑')
|
||
self.addSubInterface(self.cfInterface, fIcon.FOLDER, '配置文件')
|
||
self.navigationInterface.addSeparator()
|
||
self.addSubInterface(self.hdInterface, fIcon.QUESTION, '帮助')
|
||
self.addSubInterface(self.plInterface, fIcon.APPLICATION, '插件', NavigationItemPosition.BOTTOM)
|
||
self.navigationInterface.addSeparator(NavigationItemPosition.BOTTOM)
|
||
self.addSubInterface(self.ctInterface, fIcon.BRUSH, '自定义', NavigationItemPosition.BOTTOM)
|
||
self.addSubInterface(self.sdInterface, fIcon.RINGER, '提醒', NavigationItemPosition.BOTTOM)
|
||
self.addSubInterface(self.adInterface, fIcon.SETTING, '高级选项', NavigationItemPosition.BOTTOM)
|
||
self.addSubInterface(self.ifInterface, fIcon.INFO, '关于本产品', NavigationItemPosition.BOTTOM)
|
||
|
||
def init_window(self):
|
||
self.stackedWidget.setCurrentIndex(0) # 设置初始页面
|
||
self.load_all_item()
|
||
self.setMinimumWidth(700)
|
||
self.setMinimumHeight(400)
|
||
self.navigationInterface.setExpandWidth(250)
|
||
self.navigationInterface.setCollapsible(False)
|
||
self.setMicaEffectEnabled(True)
|
||
|
||
# 修复设置窗口在各个屏幕分辨率DPI下的窗口大小
|
||
screen_geometry = QApplication.primaryScreen().geometry()
|
||
screen_width = screen_geometry.width()
|
||
screen_height = screen_geometry.height()
|
||
|
||
width = int(screen_width * 0.6)
|
||
height = int(screen_height * 0.7)
|
||
|
||
self.move(int(screen_width / 2 - width / 2), 150)
|
||
self.resize(width, height)
|
||
|
||
self.setWindowTitle('Class Widgets - 设置')
|
||
self.setWindowIcon(QIcon(f'{base_directory}/img/logo/favicon-settings.ico'))
|
||
|
||
self.init_font() # 设置字体
|
||
|
||
def closeEvent(self, event):
|
||
self.closed.emit()
|
||
event.accept()
|
||
|
||
|
||
def sp_get_class_num(): # 获取当前周课程数(未完成)
|
||
highest_count = 0
|
||
for timeline_ in get_timeline().keys():
|
||
timeline = get_timeline()[timeline_]
|
||
count = 0
|
||
for item_name, item_time in timeline.items():
|
||
if item_name.startswith('a'):
|
||
count += 1
|
||
if count > highest_count:
|
||
highest_count = count
|
||
return highest_count
|
||
|
||
|
||
if __name__ == '__main__':
|
||
app = QApplication(sys.argv)
|
||
settings = SettingsMenu()
|
||
settings.show()
|
||
# settings.setMicaEffectEnabled(True)
|
||
sys.exit(app.exec())
|