first commit
This commit is contained in:
+546
@@ -0,0 +1,546 @@
|
||||
import os
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QMessageBox, QMenu, QSizePolicy
|
||||
)
|
||||
from PyQt6.QtCore import Qt, pyqtSignal, QSize, QPoint, QMimeData, QByteArray
|
||||
from PyQt6.QtGui import (
|
||||
QDragEnterEvent, QDragMoveEvent, QDropEvent, QCursor, QDrag, QPainter, QColor, QPen,
|
||||
QShortcut, QKeySequence,
|
||||
)
|
||||
from db import database
|
||||
from ui.item import ItemWidget
|
||||
from ui.flow_layout import FlowContainer
|
||||
import ui.theme as theme
|
||||
import qtawesome as qta
|
||||
import shortcut_target
|
||||
import ui.dialog_style as dialog_style
|
||||
|
||||
|
||||
def item_count_for_group(group_id: int) -> int:
|
||||
"""分组内程序/文件总数(文件夹分组数磁盘文件,普通分组数数据库条目)。"""
|
||||
groups = database.get_groups()
|
||||
group_info = next((g for g in groups if g["id"] == group_id), {})
|
||||
folder_path = (group_info.get("folder_path", "") or "").strip()
|
||||
if folder_path and os.path.isdir(folder_path):
|
||||
n = 0
|
||||
try:
|
||||
import stat as _stat
|
||||
for fname in os.listdir(folder_path):
|
||||
fpath = os.path.join(folder_path, fname)
|
||||
try:
|
||||
if _stat.S_ISREG(os.stat(fpath).st_mode):
|
||||
n += 1
|
||||
except OSError:
|
||||
continue
|
||||
except OSError:
|
||||
pass
|
||||
return n
|
||||
return len(database.get_items(group_id))
|
||||
|
||||
|
||||
class DragHandle(QLabel):
|
||||
"""分组拖拽排序手柄"""
|
||||
|
||||
def __init__(self, group_widget, parent=None):
|
||||
super().__init__(parent)
|
||||
self.group_widget = group_widget
|
||||
self.setFixedSize(18, 18)
|
||||
self.setCursor(Qt.CursorShape.SizeVerCursor)
|
||||
self.setToolTip("拖拽排序")
|
||||
self.setPixmap(qta.icon("fa5s.grip-vertical", color="#888").pixmap(14, 14))
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
self._drag_start = event.pos()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if not (event.buttons() & Qt.MouseButton.LeftButton):
|
||||
return
|
||||
if (event.pos() - self._drag_start).manhattanLength() < 6:
|
||||
return
|
||||
drag = QDrag(self)
|
||||
mime = QMimeData()
|
||||
gid = self.group_widget.group_data["id"]
|
||||
mime.setData("application/x-group-id", QByteArray(str(gid).encode()))
|
||||
drag.setMimeData(mime)
|
||||
px = self.group_widget.header.grab()
|
||||
drag.setPixmap(px)
|
||||
drag.setHotSpot(QPoint(px.width() // 2, px.height() // 2))
|
||||
drag.exec(Qt.DropAction.MoveAction)
|
||||
|
||||
|
||||
class FlowWidget(QWidget):
|
||||
item_dropped = pyqtSignal(int)
|
||||
refreshed = pyqtSignal()
|
||||
|
||||
def __init__(self, group_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self.group_id = group_id
|
||||
self.setAcceptDrops(True)
|
||||
self._drop_indicator = -1
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(2, 2, 2, 2)
|
||||
layout.setSpacing(0)
|
||||
|
||||
self._container = FlowContainer(self)
|
||||
layout.addWidget(self._container)
|
||||
layout.addStretch()
|
||||
|
||||
self.setFocusPolicy(Qt.FocusPolicy.ClickFocus)
|
||||
_del_sc = QShortcut(QKeySequence.StandardKey.Delete, self)
|
||||
_del_sc.setContext(Qt.ShortcutContext.WidgetWithChildrenShortcut)
|
||||
_del_sc.activated.connect(self._delete_selected_items)
|
||||
_esc_sc = QShortcut(QKeySequence(Qt.Key.Key_Escape), self)
|
||||
_esc_sc.setContext(Qt.ShortcutContext.WidgetWithChildrenShortcut)
|
||||
_esc_sc.activated.connect(self._container.clear_selection)
|
||||
|
||||
self.refresh()
|
||||
|
||||
def refresh(self, keyword=""):
|
||||
self._container.clear_widgets()
|
||||
|
||||
# 判断是否是文件夹分组
|
||||
groups = database.get_groups()
|
||||
group_info = next((g for g in groups if g["id"] == self.group_id), {})
|
||||
folder_path = (group_info.get("folder_path", "") or "").strip()
|
||||
|
||||
if folder_path and os.path.isdir(folder_path):
|
||||
# 文件夹分组:直接读目录,不走数据库
|
||||
items = []
|
||||
try:
|
||||
for fname in sorted(os.listdir(folder_path)):
|
||||
fpath = os.path.join(folder_path, fname)
|
||||
try:
|
||||
import stat as _stat
|
||||
if _stat.S_ISREG(os.stat(fpath).st_mode):
|
||||
name = os.path.splitext(fname)[0] or fname
|
||||
items.append({"id": None, "name": name,
|
||||
"path": fpath, "group_id": self.group_id})
|
||||
except Exception:
|
||||
continue
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
# 普通分组:走数据库
|
||||
items = database.get_items(self.group_id)
|
||||
|
||||
for data in items:
|
||||
iw = ItemWidget(data, self._container)
|
||||
iw.show()
|
||||
if keyword and not iw.matches(keyword):
|
||||
iw.hide()
|
||||
self._container.add_widget(iw)
|
||||
|
||||
self._container._schedule_relayout()
|
||||
self.updateGeometry()
|
||||
self.refreshed.emit()
|
||||
|
||||
def apply_theme(self):
|
||||
for w in self._container.get_widgets():
|
||||
w._apply_theme()
|
||||
|
||||
def filter(self, keyword: str):
|
||||
# 不重建,只切换可见性,然后重新布局
|
||||
for w in self._container.get_widgets():
|
||||
if keyword:
|
||||
w.setVisible(w.matches(keyword))
|
||||
else:
|
||||
w.show()
|
||||
self._container._relayout()
|
||||
self.updateGeometry()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.MouseButton.LeftButton:
|
||||
cg = self._container.geometry()
|
||||
if not cg.contains(event.pos()):
|
||||
lp = self._container.mapFrom(self, event.pos())
|
||||
w, h = max(1, self._container.width()), max(1, self._container.height())
|
||||
lp.setX(max(0, min(lp.x(), w - 1)))
|
||||
lp.setY(max(0, min(lp.y(), h - 1)))
|
||||
self._container._begin_rubber(lp)
|
||||
return
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def _delete_selected_items(self):
|
||||
widgets = [w for w in self._container.get_widgets() if getattr(w, "_selected", False)]
|
||||
if not widgets:
|
||||
return
|
||||
ret = dialog_style.question(
|
||||
self,
|
||||
"批量删除",
|
||||
f"确定删除选中的 {len(widgets)} 项?",
|
||||
default_button=QMessageBox.StandardButton.No,
|
||||
)
|
||||
if ret != QMessageBox.StandardButton.Yes:
|
||||
return
|
||||
for w in widgets:
|
||||
w._delete_item(skip_confirm=True, skip_refresh=True)
|
||||
self.refresh()
|
||||
|
||||
def dragEnterEvent(self, event: QDragEnterEvent):
|
||||
if event.mimeData().hasFormat("application/x-item-id") or \
|
||||
event.mimeData().hasUrls():
|
||||
self._container.set_drag_highlight(True)
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event: QDragMoveEvent):
|
||||
if event.mimeData().hasFormat("application/x-item-id"):
|
||||
pos = self._container.mapFrom(self, event.position().toPoint())
|
||||
self._drop_indicator = self._container.index_at(pos)
|
||||
self.update()
|
||||
event.acceptProposedAction()
|
||||
|
||||
def dragLeaveEvent(self, event):
|
||||
self._container.set_drag_highlight(False)
|
||||
self._drop_indicator = -1
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, event):
|
||||
super().paintEvent(event)
|
||||
if self._drop_indicator < 0:
|
||||
return
|
||||
from PyQt6.QtGui import QPainter, QColor, QPen
|
||||
all_w = self._container.get_widgets()
|
||||
if not all_w:
|
||||
return
|
||||
pos = self._drop_indicator
|
||||
p = QPainter(self)
|
||||
p.setPen(QPen(QColor("#4a9eff"), 2))
|
||||
offset = self._container.pos()
|
||||
|
||||
def _draw_vline(x_local: int, g):
|
||||
x = offset.x() + x_local
|
||||
y1 = offset.y() + g.top()
|
||||
y2 = offset.y() + g.bottom()
|
||||
p.drawLine(x, y1, x, y2)
|
||||
|
||||
if pos >= len(all_w):
|
||||
for j in range(len(all_w) - 1, -1, -1):
|
||||
if all_w[j].isVisible():
|
||||
g = all_w[j].geometry()
|
||||
_draw_vline(g.right() + 2, g)
|
||||
break
|
||||
p.end()
|
||||
return
|
||||
|
||||
w = all_w[pos]
|
||||
if w.isVisible():
|
||||
g = w.geometry()
|
||||
_draw_vline(g.left() - 2, g)
|
||||
else:
|
||||
placed = False
|
||||
for j in range(pos, len(all_w)):
|
||||
if all_w[j].isVisible():
|
||||
g = all_w[j].geometry()
|
||||
_draw_vline(g.left() - 2, g)
|
||||
placed = True
|
||||
break
|
||||
if not placed:
|
||||
for j in range(pos - 1, -1, -1):
|
||||
if all_w[j].isVisible():
|
||||
g = all_w[j].geometry()
|
||||
_draw_vline(g.right() + 2, g)
|
||||
break
|
||||
p.end()
|
||||
|
||||
def dropEvent(self, event: QDropEvent):
|
||||
self._container.set_drag_highlight(False)
|
||||
self._drop_indicator = -1
|
||||
self.update()
|
||||
mime = event.mimeData()
|
||||
if mime.hasFormat("application/x-item-id"):
|
||||
item_id = int(mime.data("application/x-item-id").data().decode())
|
||||
pos = self._container.mapFrom(self, event.position().toPoint())
|
||||
insert_pos = self._container.index_at(pos)
|
||||
database.move_item(item_id, self.group_id, 999)
|
||||
items = database.get_items(self.group_id)
|
||||
ids = [it["id"] for it in items if it["id"] != item_id]
|
||||
ids.insert(min(insert_pos, len(ids)), item_id)
|
||||
database.reorder_items(self.group_id, ids)
|
||||
self.refresh()
|
||||
self.item_dropped.emit(item_id)
|
||||
event.acceptProposedAction()
|
||||
elif mime.hasUrls():
|
||||
for url in mime.urls():
|
||||
path = url.toLocalFile()
|
||||
if path:
|
||||
store = shortcut_target.path_for_storage(path)
|
||||
name = shortcut_target.item_name_from_sources(path, store)
|
||||
database.add_item(self.group_id, name, store)
|
||||
self.refresh()
|
||||
event.acceptProposedAction()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
t = theme.current()
|
||||
menu_style = f"""
|
||||
QMenu {{ background:{t['menu_bg']}; color:{t['menu_color']};
|
||||
border:1px solid {t['menu_border']}; padding:4px; border-radius:6px; }}
|
||||
QMenu::item {{ padding:5px 16px; border-radius:3px; }}
|
||||
QMenu::item:selected {{ background:{t['menu_selected']}; }}
|
||||
QMenu::item:disabled {{ color:#666; }}
|
||||
"""
|
||||
menu = QMenu(self)
|
||||
menu.setStyleSheet(menu_style)
|
||||
|
||||
refresh_act = menu.addAction("🔄 刷新分组")
|
||||
menu.addSeparator()
|
||||
|
||||
# 检测剪贴板
|
||||
clipboard = QApplication.clipboard()
|
||||
mime = clipboard.mimeData()
|
||||
paste_act = None
|
||||
clipboard_paths = []
|
||||
|
||||
if mime and mime.hasUrls():
|
||||
# 获取本分组信息
|
||||
groups = database.get_groups()
|
||||
group_info = next((g for g in groups if g["id"] == self.group_id), {})
|
||||
is_folder_group = bool(group_info.get("folder_path", ""))
|
||||
|
||||
# 快捷方式扩展名
|
||||
shortcut_exts = {".exe", ".lnk", ".bat", ".cmd", ".url"}
|
||||
|
||||
for url in mime.urls():
|
||||
path = url.toLocalFile()
|
||||
if not path:
|
||||
continue
|
||||
ext = os.path.splitext(path)[1].lower()
|
||||
if is_folder_group:
|
||||
# 文件夹分组:接受所有文件
|
||||
if os.path.isfile(path):
|
||||
clipboard_paths.append(path)
|
||||
else:
|
||||
# 普通分组:只接受快捷方式/程序
|
||||
if ext in shortcut_exts:
|
||||
clipboard_paths.append(path)
|
||||
|
||||
if clipboard_paths:
|
||||
label = f"📋 粘贴 {len(clipboard_paths)} 个文件" if len(clipboard_paths) > 1 \
|
||||
else f"📋 粘贴「{os.path.basename(clipboard_paths[0])}」"
|
||||
paste_act = menu.addAction(label)
|
||||
else:
|
||||
act = menu.addAction("📋 粘贴(无可用文件)")
|
||||
act.setEnabled(False)
|
||||
else:
|
||||
act = menu.addAction("📋 粘贴(剪贴板为空)")
|
||||
act.setEnabled(False)
|
||||
|
||||
action = menu.exec(event.globalPos())
|
||||
if action == refresh_act:
|
||||
self.refresh()
|
||||
elif paste_act and action == paste_act:
|
||||
groups = database.get_groups()
|
||||
group_info = next((g for g in groups if g["id"] == self.group_id), {})
|
||||
folder_path = group_info.get("folder_path", "")
|
||||
is_folder_group = bool(folder_path)
|
||||
|
||||
for path in clipboard_paths:
|
||||
if is_folder_group:
|
||||
# 真正复制文件到文件夹目录
|
||||
import shutil
|
||||
dest_path = os.path.join(folder_path, os.path.basename(path))
|
||||
# 避免覆盖同名文件,自动重命名
|
||||
if os.path.exists(dest_path) and dest_path != path:
|
||||
base, ext = os.path.splitext(os.path.basename(path))
|
||||
i = 1
|
||||
while os.path.exists(dest_path):
|
||||
dest_path = os.path.join(folder_path, f"{base} ({i}){ext}")
|
||||
i += 1
|
||||
try:
|
||||
if path != dest_path:
|
||||
shutil.copy2(path, dest_path)
|
||||
final_path = dest_path
|
||||
except Exception as e:
|
||||
print(f"复制失败: {path} -> {e}")
|
||||
final_path = path # 复制失败就用原路径
|
||||
name = os.path.splitext(os.path.basename(final_path))[0] or os.path.basename(final_path)
|
||||
else:
|
||||
final_path = shortcut_target.path_for_storage(path)
|
||||
name = shortcut_target.item_name_from_sources(path, final_path)
|
||||
|
||||
database.add_item(self.group_id, name, final_path)
|
||||
self.refresh()
|
||||
|
||||
|
||||
class GroupWidget(QWidget):
|
||||
request_refresh_all = pyqtSignal()
|
||||
|
||||
def __init__(self, group_data: dict, parent=None):
|
||||
super().__init__(parent)
|
||||
self.group_data = group_data
|
||||
self.collapsed = False
|
||||
self._build_ui()
|
||||
self._apply_theme()
|
||||
|
||||
def _build_ui(self):
|
||||
self.main_layout = QVBoxLayout(self)
|
||||
self.main_layout.setContentsMargins(0, 0, 0, 4)
|
||||
self.main_layout.setSpacing(0)
|
||||
|
||||
self.header = QWidget()
|
||||
self.header.setFixedHeight(32)
|
||||
self.header.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
self.header.mousePressEvent = lambda e: self._toggle() \
|
||||
if e.button() == Qt.MouseButton.LeftButton else None
|
||||
|
||||
h_layout = QHBoxLayout(self.header)
|
||||
h_layout.setContentsMargins(4, 0, 6, 0)
|
||||
h_layout.setSpacing(4)
|
||||
|
||||
# 拖拽排序手柄
|
||||
self._drag_handle = DragHandle(self)
|
||||
|
||||
self.toggle_icon = QLabel()
|
||||
self.toggle_icon.setFixedSize(14, 14)
|
||||
self.toggle_icon.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||
|
||||
self.title_label = QLabel()
|
||||
self.title_label.setStyleSheet("font-size:12px; font-weight:bold; background:transparent;")
|
||||
self.title_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||
|
||||
folder_path = self.group_data.get("folder_path", "") or ""
|
||||
# 只显示最后两级路径,避免太长
|
||||
if folder_path:
|
||||
parts = folder_path.replace("\\", "/").rstrip("/").split("/")
|
||||
display_path = "/".join(parts[-2:]) if len(parts) >= 2 else folder_path
|
||||
else:
|
||||
display_path = ""
|
||||
self.path_label = QLabel(display_path)
|
||||
self.path_label.setStyleSheet("font-size:9px; color:#888; background:transparent;")
|
||||
self.path_label.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
|
||||
self.path_label.setVisible(bool(folder_path))
|
||||
self.path_label.setMaximumWidth(110)
|
||||
self.path_label.setToolTip(folder_path)
|
||||
self.path_label.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter)
|
||||
|
||||
self._add_btn = QPushButton()
|
||||
self._add_btn.setFixedSize(22, 22)
|
||||
self._add_btn.setToolTip("添加程序")
|
||||
self._add_btn.setStyleSheet("border:none; background:transparent;")
|
||||
self._add_btn.clicked.connect(self._add_item_dialog)
|
||||
self._add_btn.mousePressEvent = lambda e: (
|
||||
self._add_btn.click() if e.button() == Qt.MouseButton.LeftButton else None
|
||||
)
|
||||
|
||||
self._more_btn = QPushButton()
|
||||
self._more_btn.setFixedSize(22, 22)
|
||||
self._more_btn.setStyleSheet("border:none; background:transparent;")
|
||||
self._more_btn.clicked.connect(self._group_menu)
|
||||
|
||||
h_layout.addWidget(self._drag_handle)
|
||||
h_layout.addWidget(self.toggle_icon)
|
||||
h_layout.addWidget(self.title_label)
|
||||
h_layout.addStretch()
|
||||
h_layout.addWidget(self.path_label)
|
||||
h_layout.addWidget(self._add_btn)
|
||||
h_layout.addWidget(self._more_btn)
|
||||
|
||||
self.flow = FlowWidget(self.group_data["id"])
|
||||
self.flow.item_dropped.connect(lambda _: self.request_refresh_all.emit())
|
||||
self.flow.refreshed.connect(self._update_title_count)
|
||||
self._update_title_count()
|
||||
|
||||
self.main_layout.addWidget(self.header)
|
||||
self.main_layout.addWidget(self.flow)
|
||||
|
||||
def _update_title_count(self):
|
||||
n = item_count_for_group(self.group_data["id"])
|
||||
self.title_label.setText(f"{self.group_data['name']} ({n})")
|
||||
|
||||
def _apply_theme(self):
|
||||
t = theme.current()
|
||||
ic = "#cccccc" if theme.name() == "dark" else "#555555"
|
||||
|
||||
self.header.setStyleSheet(f"""
|
||||
QWidget {{ background:{t['header_bg']}; border-radius:5px; }}
|
||||
QWidget:hover {{ background:{t['header_hover']}; }}
|
||||
""")
|
||||
self.title_label.setStyleSheet(
|
||||
f"color:{t['item_name_color']}; font-size:12px; font-weight:bold; background:transparent;"
|
||||
)
|
||||
# 展开/收缩图标
|
||||
arrow = "fa5s.chevron-down" if not self.collapsed else "fa5s.chevron-right"
|
||||
self.toggle_icon.setPixmap(qta.icon(arrow, color=ic).pixmap(14, 14))
|
||||
|
||||
self._add_btn.setIcon(qta.icon("fa5s.plus", color=ic))
|
||||
self._add_btn.setIconSize(QSize(11, 11))
|
||||
self._more_btn.setIcon(qta.icon("fa5s.ellipsis-h", color=ic))
|
||||
self._more_btn.setIconSize(QSize(11, 11))
|
||||
self._drag_handle.setPixmap(qta.icon("fa5s.grip-vertical", color=ic).pixmap(14, 14))
|
||||
path_color = "#777" if theme.name() == "dark" else "#999"
|
||||
self.path_label.setStyleSheet(
|
||||
f"font-size:9px; color:{path_color}; background:transparent;"
|
||||
)
|
||||
|
||||
self.flow.apply_theme()
|
||||
|
||||
def _toggle(self):
|
||||
self.collapsed = not self.collapsed
|
||||
self.flow.setVisible(not self.collapsed)
|
||||
if not self.collapsed:
|
||||
self.flow.updateGeometry()
|
||||
self.updateGeometry()
|
||||
# 更新箭头图标
|
||||
t = theme.current()
|
||||
ic = "#cccccc" if theme.name() == "dark" else "#555555"
|
||||
arrow = "fa5s.chevron-down" if not self.collapsed else "fa5s.chevron-right"
|
||||
self.toggle_icon.setPixmap(qta.icon(arrow, color=ic).pixmap(14, 14))
|
||||
|
||||
def _add_item_dialog(self):
|
||||
path, _ = dialog_style.get_open_file_name(
|
||||
self,
|
||||
"选择程序或快捷方式",
|
||||
"",
|
||||
"程序/快捷方式 (*.exe *.lnk *.bat *.cmd *.url);;所有文件 (*)",
|
||||
)
|
||||
if path:
|
||||
store = shortcut_target.path_for_storage(path)
|
||||
name = shortcut_target.item_name_from_sources(path, store)
|
||||
database.add_item(self.group_data["id"], name, store)
|
||||
self.flow.refresh()
|
||||
|
||||
def _group_menu(self):
|
||||
t = theme.current()
|
||||
menu = QMenu(self)
|
||||
menu.setStyleSheet(f"""
|
||||
QMenu {{ background:{t['menu_bg']}; color:{t['menu_color']};
|
||||
border:1px solid {t['menu_border']}; padding:4px; border-radius:6px; }}
|
||||
QMenu::item {{ padding:5px 16px; border-radius:3px; }}
|
||||
QMenu::item:selected {{ background:{t['menu_selected']}; }}
|
||||
""")
|
||||
rename_act = menu.addAction("重命名")
|
||||
del_act = menu.addAction("删除分组")
|
||||
action = menu.exec(QCursor.pos())
|
||||
if action == rename_act:
|
||||
name, ok = dialog_style.get_text(
|
||||
self, "重命名", "新名称:", text=self.group_data["name"]
|
||||
)
|
||||
if ok and name.strip():
|
||||
database.rename_group(self.group_data["id"], name.strip())
|
||||
self.group_data["name"] = name.strip()
|
||||
self._update_title_count()
|
||||
elif action == del_act:
|
||||
ret = dialog_style.question(
|
||||
self,
|
||||
"确认",
|
||||
f"删除分组「{self.group_data['name']}」及其所有程序?",
|
||||
default_button=QMessageBox.StandardButton.No,
|
||||
)
|
||||
if ret == QMessageBox.StandardButton.Yes:
|
||||
database.delete_group(self.group_data["id"])
|
||||
self.request_refresh_all.emit()
|
||||
|
||||
def refresh(self, keyword=""):
|
||||
self.flow.refresh(keyword)
|
||||
|
||||
def filter(self, keyword: str):
|
||||
self.flow.filter(keyword)
|
||||
if keyword and self.collapsed:
|
||||
self._toggle()
|
||||
Reference in New Issue
Block a user