diff --git a/__pycache__/shortcut_target.cpython-314.pyc b/__pycache__/shortcut_target.cpython-314.pyc new file mode 100644 index 0000000..e1f461b Binary files /dev/null and b/__pycache__/shortcut_target.cpython-314.pyc differ diff --git a/assets/imgs/weather/雾.png b/assets/imgs/weather/雾.png new file mode 100644 index 0000000..a9ba095 Binary files /dev/null and b/assets/imgs/weather/雾.png differ diff --git a/db/__pycache__/__init__.cpython-314.pyc b/db/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..a96bb38 Binary files /dev/null and b/db/__pycache__/__init__.cpython-314.pyc differ diff --git a/db/__pycache__/database.cpython-314.pyc b/db/__pycache__/database.cpython-314.pyc new file mode 100644 index 0000000..d476d44 Binary files /dev/null and b/db/__pycache__/database.cpython-314.pyc differ diff --git a/db/database.py b/db/database.py index 423ad12..681752e 100644 --- a/db/database.py +++ b/db/database.py @@ -228,3 +228,18 @@ def reorder_items(group_id, id_list): conn.execute("UPDATE items SET position=? WHERE id=?", (pos, iid)) conn.commit() conn.close() + + +def reset_groups_and_items(recreate_default_group: bool = True): + """ + 清空所有分组与程序(items)。 + - 为避免外键级联受 SQLite 外键开关影响,这里显式先删 items 再删 groups。 + - 可选地重新创建默认分组「常用程序」,保证界面至少有一个组可操作。 + """ + conn = get_conn() + conn.execute("DELETE FROM items") + conn.execute("DELETE FROM groups") + if recreate_default_group: + conn.execute("INSERT INTO groups (name, position) VALUES ('常用程序', 0)") + conn.commit() + conn.close() diff --git a/ui/__pycache__/__init__.cpython-314.pyc b/ui/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000..3d14df1 Binary files /dev/null and b/ui/__pycache__/__init__.cpython-314.pyc differ diff --git a/ui/__pycache__/ball.cpython-314.pyc b/ui/__pycache__/ball.cpython-314.pyc new file mode 100644 index 0000000..532c323 Binary files /dev/null and b/ui/__pycache__/ball.cpython-314.pyc differ diff --git a/ui/__pycache__/dialog_style.cpython-314.pyc b/ui/__pycache__/dialog_style.cpython-314.pyc new file mode 100644 index 0000000..e953fae Binary files /dev/null and b/ui/__pycache__/dialog_style.cpython-314.pyc differ diff --git a/ui/__pycache__/dock.cpython-314.pyc b/ui/__pycache__/dock.cpython-314.pyc new file mode 100644 index 0000000..df6709e Binary files /dev/null and b/ui/__pycache__/dock.cpython-314.pyc differ diff --git a/ui/__pycache__/flow_layout.cpython-314.pyc b/ui/__pycache__/flow_layout.cpython-314.pyc new file mode 100644 index 0000000..4271dd3 Binary files /dev/null and b/ui/__pycache__/flow_layout.cpython-314.pyc differ diff --git a/ui/__pycache__/group.cpython-314.pyc b/ui/__pycache__/group.cpython-314.pyc new file mode 100644 index 0000000..70c0541 Binary files /dev/null and b/ui/__pycache__/group.cpython-314.pyc differ diff --git a/ui/__pycache__/item.cpython-314.pyc b/ui/__pycache__/item.cpython-314.pyc new file mode 100644 index 0000000..af74f4e Binary files /dev/null and b/ui/__pycache__/item.cpython-314.pyc differ diff --git a/ui/__pycache__/settings_window.cpython-314.pyc b/ui/__pycache__/settings_window.cpython-314.pyc new file mode 100644 index 0000000..eeea6e5 Binary files /dev/null and b/ui/__pycache__/settings_window.cpython-314.pyc differ diff --git a/ui/__pycache__/theme.cpython-314.pyc b/ui/__pycache__/theme.cpython-314.pyc new file mode 100644 index 0000000..e9d538f Binary files /dev/null and b/ui/__pycache__/theme.cpython-314.pyc differ diff --git a/ui/dock.py b/ui/dock.py index 205af2d..2d740f3 100644 --- a/ui/dock.py +++ b/ui/dock.py @@ -858,6 +858,16 @@ class PanelWindow(QWidget): ) self.groups_layout.insertWidget(self.groups_layout.count() - 1, gw) + def reset_groups_and_items(self): + """清空所有分组与程序,并刷新界面。""" + database.reset_groups_and_items(recreate_default_group=True) + # 清空搜索关键字,确保刷新后可见全部内容 + try: + self.search_box.setText("") + except Exception: + pass + self.refresh_groups() + def _add_group(self): name, ok = dialog_style.get_text(self, "新建分组", "分组名称:") if ok and name.strip(): @@ -909,12 +919,13 @@ class PanelWindow(QWidget): def _collapse_body(self): """收缩:隐藏内容区,窗口缩到只剩标题栏高度""" self._body.hide() + self.weather_bar.hide() # 记录当前高度,展开时恢复 self._expanded_height = self.height() title_h = ( self.container.layout().contentsMargins().top() + self.container.layout().contentsMargins().bottom() - + 46 + + self._title_bar.sizeHint().height() ) self.setFixedHeight(title_h) ic = "#cccccc" if theme.name() == "dark" else "#555555" @@ -923,6 +934,7 @@ class PanelWindow(QWidget): def _expand_body(self): """展开:恢复内容区和窗口高度""" + self.weather_bar.show() self._body.show() h = getattr(self, "_expanded_height", 520) self.setMinimumHeight(MIN_H) diff --git a/ui/settings_window.py b/ui/settings_window.py index 3281165..010d669 100644 --- a/ui/settings_window.py +++ b/ui/settings_window.py @@ -6,10 +6,12 @@ from PyQt6.QtWidgets import ( QCheckBox, QGroupBox, QFormLayout, QSpinBox, QFrame ) from PyQt6.QtCore import Qt, QSize +from PyQt6.QtWidgets import QMessageBox from PyQt6.QtGui import QIcon, QPixmap import qtawesome as qta from db import database import ui.theme as theme +import ui.dialog_style as dialog_style class SettingsWindow(QDialog): @@ -50,7 +52,10 @@ class SettingsWindow(QDialog): ("fa5s.window-maximize", "窗口"), ("fa5s.rocket", "启动"), ("fa5s.heart", "捐赠"), + ("fa5s.eraser", "初始化"), ] + self._nav_ready = False + self._block_nav = False for icon_name, label in pages: try: icon = qta.icon(icon_name, color="#888") @@ -59,6 +64,8 @@ class SettingsWindow(QDialog): # 防止图标名不存在导致设置页直接报错 icon = qta.icon("fa5s.rocket", color="#888") item = QListWidgetItem(icon, label) + if label == "初始化": + item.setData(Qt.ItemDataRole.UserRole, "init") self.nav.addItem(item) self.nav.setCurrentRow(0) self.nav.currentRowChanged.connect(self._on_nav) @@ -69,6 +76,11 @@ class SettingsWindow(QDialog): self.stack.addWidget(self._page_window()) self.stack.addWidget(self._page_startup()) self.stack.addWidget(self._page_donate()) + self.stack.addWidget(self._page_initialization()) + # 此时 stack 已就绪,允许导航回调正常工作 + self._nav_ready = True + self._last_nav_index = self.nav.currentRow() + self.stack.setCurrentIndex(self._last_nav_index) # 分割线 line = QFrame() @@ -312,6 +324,29 @@ class SettingsWindow(QDialog): self._donate_card = card return page + def _page_initialization(self) -> QWidget: + page = QWidget() + layout = QVBoxLayout(page) + layout.setContentsMargins(24, 20, 24, 20) + layout.setSpacing(16) + + layout.addWidget(self._section_title("初始化")) + + desc = QLabel( + "点击左侧「初始化」页签后,将弹出确认提醒。\n" + "确认后会清空所有分组和程序(不可撤销)。" + ) + desc.setWordWrap(True) + desc.setStyleSheet("color:#888; font-size:13px; line-height:1.6;") + layout.addWidget(desc) + + self._init_status_lbl = QLabel("") + self._init_status_lbl.setStyleSheet("font-size:12px; color:#f90;") + layout.addWidget(self._init_status_lbl) + layout.addStretch() + + return page + def _open_donate_image(self, img_path: str): """点击二维码后的弹窗大图查看。""" t = theme.current() @@ -386,7 +421,51 @@ class SettingsWindow(QDialog): return line def _on_nav(self, index: int): - self.stack.setCurrentIndex(index) + if self._block_nav: + return + + if not getattr(self, "_nav_ready", False) or not hasattr(self, "stack"): + return + + item = self.nav.item(index) + is_init_tab = bool(item and item.data(Qt.ItemDataRole.UserRole) == "init") + + if not is_init_tab: + self.stack.setCurrentIndex(index) + self._last_nav_index = index + return + + ret = dialog_style.question( + self, + "初始化", + "提醒请谨慎操作!!!!!\n\n" + "此操作会清空所有分组和程序,且无法撤销。是否继续?", + icon=QMessageBox.Icon.Warning, + ) + if ret == QMessageBox.StandardButton.Yes: + try: + self._panel.reset_groups_and_items() + except Exception as e: + # 初始化失败也要尽量让用户知道原因 + dialog_style.warning(self, "初始化失败", f"清空数据失败:{e}") + # 初始化失败则回退到上一个页签 + self._block_nav = True + self.nav.setCurrentRow(self._last_nav_index) + self._block_nav = False + self.stack.setCurrentIndex(self._last_nav_index) + return + + if hasattr(self, "_init_status_lbl"): + self._init_status_lbl.setText("已完成初始化(分组和程序已清空)。") + + self.stack.setCurrentIndex(index) + self._last_nav_index = index + else: + # 用户取消:回退到上一个页签 + self._block_nav = True + self.nav.setCurrentRow(self._last_nav_index) + self._block_nav = False + self.stack.setCurrentIndex(self._last_nav_index) def _apply_theme(self): t = theme.current()