a13rollcall/plugin_ui_injector.py
2026-10-06 18:38:55 +08:00

863 行
32 KiB
Python

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

"""
A13课堂点名系统 - 插件UI注入工具(增强版)
提供在主程序任何位置添加UI元素、修改布局、添加标签页的完整能力
"""
from PyQt5.QtWidgets import (
QPushButton, QWidget, QHBoxLayout, QVBoxLayout, QLabel, QFrame,
QSizePolicy, QMenu, QAction, QLineEdit, QComboBox, QCheckBox,
QSlider, QProgressBar, QTextEdit, QListWidget, QListWidgetItem,
QTabWidget, QScrollArea, QGroupBox, QFormLayout, QGridLayout,
QSplitter, QToolBar, QStatusBar, QDockWidget, QMessageBox,
QFileDialog, QColorDialog, QFontDialog, QInputDialog, QSpinBox,
QDoubleSpinBox, QDateEdit, QTimeEdit, QDateTimeEdit, QCalendarWidget,
QDial, QLCDNumber, QTableWidget, QTableWidgetItem, QTreeWidget,
QTreeWidgetItem, QHeaderView, QStackedWidget, QToolBox, QButtonGroup,
QRadioButton, QPlainTextEdit, QGraphicsView, QGraphicsScene,
QSystemTrayIcon, QMenuBar, QShortcut
)
from PyQt5.QtCore import Qt, QSize, QTimer, pyqtSignal, QObject, QRect
from PyQt5.QtGui import (
QFont, QIcon, QColor, QPalette, QPixmap, QPainter, QPen, QBrush,
QLinearGradient, QRadialGradient, QFontDatabase, QCursor, QKeySequence
)
import os
import json
class UIInjector:
"""UI注入工具类 - 增强版,支持复杂控件、布局修改、标签页添加"""
def __init__(self, main_window):
self.main_window = main_window
self.injected_elements = []
self._custom_tabs = []
self._custom_panels = []
# ==================== 查找控件 ====================
def find_widget_by_name(self, name):
return self.main_window.findChild(QWidget, name)
def find_all_buttons(self):
return self.main_window.findChildren(QPushButton)
def find_button_by_text(self, text):
for btn in self.find_all_buttons():
if btn.text() == text:
return btn
return None
def find_all_labels(self):
return self.main_window.findChildren(QLabel)
def find_label_by_text(self, text):
for label in self.find_all_labels():
if text in label.text():
return label
return None
def find_layout_by_widget(self, widget):
if widget and widget.parent():
return widget.parent().layout()
return None
def get_all_ui_elements(self, include_layouts=True):
elements = []
for widget in self.main_window.findChildren(QWidget):
elements.append({
"type": type(widget).__name__,
"objectName": widget.objectName(),
"text": getattr(widget, 'text', lambda: '')() if callable(getattr(widget, 'text', None)) else '',
"visible": widget.isVisible(),
"enabled": widget.isEnabled(),
"geometry": widget.geometry().getRect(),
})
return elements
def get_main_window(self):
return self.main_window
def get_central_widget(self):
return self.main_window.centralWidget()
def get_status_bar(self):
return self.main_window.statusBar()
def get_menu_bar(self):
return self.main_window.menuBar()
# ==================== 添加按钮(基础) ====================
def add_button_to_layout(self, layout, text, callback, position=None,
style="primary", icon=None, tooltip=None,
width=None, height=None):
btn = QPushButton(text)
btn.setStyleSheet(self._get_button_style(style))
if icon:
btn.setIcon(icon)
if tooltip:
btn.setToolTip(tooltip)
if width:
btn.setFixedWidth(width)
if height:
btn.setFixedHeight(height)
btn.clicked.connect(callback)
if position is not None:
layout.insertWidget(position, btn)
else:
layout.addWidget(btn)
self.injected_elements.append(btn)
return btn
def add_button_to_top_bar(self, text, callback, position=None, style="primary",
icon=None, tooltip=None, width=100, height=36):
top_bar = self._get_top_bar()
if not top_bar:
return None
layout = top_bar.layout()
if not layout:
layout = QHBoxLayout(top_bar)
return self.add_button_to_layout(layout, text, callback, position, style, icon, tooltip, width, height)
def add_button_to_bottom_bar(self, text, callback, position=None, style="ghost",
icon=None, tooltip=None, width=90, height=32):
bottom_bar = self._get_bottom_bar()
if not bottom_bar:
return None
if isinstance(bottom_bar, QStatusBar):
btn = QPushButton(text)
btn.setStyleSheet(self._get_button_style(style))
if tooltip:
btn.setToolTip(tooltip)
btn.clicked.connect(callback)
bottom_bar.addPermanentWidget(btn)
self.injected_elements.append(btn)
return btn
layout = bottom_bar.layout()
if not layout:
layout = QHBoxLayout(bottom_bar)
return self.add_button_to_layout(layout, text, callback, position, style, icon, tooltip, width, height)
def add_button_to_sidebar(self, text, callback, position=None, style="ghost",
icon=None, tooltip=None, width=120, height=40):
sidebar = self._get_sidebar()
if not sidebar:
return None
layout = sidebar.layout()
if not layout:
layout = QVBoxLayout(sidebar)
return self.add_button_to_layout(layout, text, callback, position, style, icon, tooltip, width, height)
def add_button_to_draw_area(self, text, callback, position=None, style="primary",
icon=None, tooltip=None, width=120, height=48):
draw_area = self._get_draw_area()
if not draw_area:
return None
layout = draw_area.layout()
if not layout:
layout = QVBoxLayout(draw_area)
return self.add_button_to_layout(layout, text, callback, position, style, icon, tooltip, width, height)
def add_button_next_to(self, target_button, text, callback, side="right",
style="success", icon=None, tooltip=None, width=None, height=None):
if isinstance(target_button, str):
target_button = self.find_button_by_text(target_button)
if not target_button:
return None
parent_layout = self.find_layout_by_widget(target_button)
if not parent_layout:
return None
index = parent_layout.indexOf(target_button)
position = index + 1 if side == "right" else index
return self.add_button_to_layout(parent_layout, text, callback, position, style, icon, tooltip, width, height)
def create_floating_button(self, text, callback, x=20, y=100,
size=(100, 40), style="warning", tooltip=None):
btn = DraggableButton(text, self.main_window)
btn.setStyleSheet(self._get_button_style(style))
btn.setGeometry(x, y, size[0], size[1])
if tooltip:
btn.setToolTip(tooltip)
btn.clicked.connect(callback)
btn.show()
self.injected_elements.append(btn)
return btn
# ==================== 添加复杂控件(新增) ====================
def add_input_to_layout(self, layout, placeholder="", callback=None,
position=None, width=200, height=32, echo_normal=True):
input_box = QLineEdit()
input_box.setPlaceholderText(placeholder)
input_box.setFixedHeight(height)
if width:
input_box.setFixedWidth(width)
if not echo_normal:
input_box.setEchoMode(QLineEdit.Password)
if callback:
input_box.textChanged.connect(callback)
if position is not None:
layout.insertWidget(position, input_box)
else:
layout.addWidget(input_box)
self.injected_elements.append(input_box)
return input_box
def add_combo_to_layout(self, layout, items, callback=None, position=None,
width=150, height=32, current_index=0):
combo = QComboBox()
combo.addItems(items)
combo.setFixedHeight(height)
if width:
combo.setFixedWidth(width)
combo.setCurrentIndex(current_index)
if callback:
combo.currentIndexChanged.connect(callback)
if position is not None:
layout.insertWidget(position, combo)
else:
layout.addWidget(combo)
self.injected_elements.append(combo)
return combo
def add_checkbox_to_layout(self, layout, text, callback=None, position=None,
checked=False, width=None):
checkbox = QCheckBox(text)
checkbox.setChecked(checked)
if width:
checkbox.setFixedWidth(width)
if callback:
checkbox.stateChanged.connect(callback)
if position is not None:
layout.insertWidget(position, checkbox)
else:
layout.addWidget(checkbox)
self.injected_elements.append(checkbox)
return checkbox
def add_radio_to_layout(self, layout, text, callback=None, position=None,
checked=False, group=None):
radio = QRadioButton(text)
radio.setChecked(checked)
if group:
group.addButton(radio)
if callback:
radio.toggled.connect(callback)
if position is not None:
layout.insertWidget(position, radio)
else:
layout.addWidget(radio)
self.injected_elements.append(radio)
return radio
def add_slider_to_layout(self, layout, min_val=0, max_val=100, value=50,
callback=None, position=None, orientation=Qt.Horizontal, width=200):
slider = QSlider(orientation)
slider.setRange(min_val, max_val)
slider.setValue(value)
if orientation == Qt.Horizontal and width:
slider.setFixedWidth(width)
if callback:
slider.valueChanged.connect(callback)
if position is not None:
layout.insertWidget(position, slider)
else:
layout.addWidget(slider)
self.injected_elements.append(slider)
return slider
def add_spinbox_to_layout(self, layout, min_val=0, max_val=100, value=0,
callback=None, position=None, width=100, step=1, prefix="", suffix=""):
spinbox = QSpinBox()
spinbox.setRange(min_val, max_val)
spinbox.setValue(value)
spinbox.setSingleStep(step)
spinbox.setFixedWidth(width)
if prefix:
spinbox.setPrefix(prefix)
if suffix:
spinbox.setSuffix(suffix)
if callback:
spinbox.valueChanged.connect(callback)
if position is not None:
layout.insertWidget(position, spinbox)
else:
layout.addWidget(spinbox)
self.injected_elements.append(spinbox)
return spinbox
def add_progress_to_layout(self, layout, value=0, max_val=100, position=None,
width=200, height=20, text_visible=True):
progress = QProgressBar()
progress.setRange(0, max_val)
progress.setValue(value)
progress.setTextVisible(text_visible)
progress.setFixedHeight(height)
if width:
progress.setFixedWidth(width)
if position is not None:
layout.insertWidget(position, progress)
else:
layout.addWidget(progress)
self.injected_elements.append(progress)
return progress
def add_list_to_layout(self, layout, items=None, callback=None, position=None,
width=200, height=150, selection_mode=QListWidget.SingleSelection):
list_widget = QListWidget()
list_widget.setFixedHeight(height)
if width:
list_widget.setFixedWidth(width)
list_widget.setSelectionMode(selection_mode)
if items:
list_widget.addItems(items)
if callback:
list_widget.itemClicked.connect(callback)
if position is not None:
layout.insertWidget(position, list_widget)
else:
layout.addWidget(list_widget)
self.injected_elements.append(list_widget)
return list_widget
def add_table_to_layout(self, layout, rows=0, cols=0, headers=None,
position=None, width=400, height=200):
table = QTableWidget(rows, cols)
if headers:
table.setHorizontalHeaderLabels(headers)
table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
table.setFixedHeight(height)
if width:
table.setFixedWidth(width)
if position is not None:
layout.insertWidget(position, table)
else:
layout.addWidget(table)
self.injected_elements.append(table)
return table
def add_textarea_to_layout(self, layout, placeholder="", callback=None,
position=None, width=300, height=100, read_only=False):
textarea = QTextEdit()
textarea.setPlaceholderText(placeholder)
textarea.setReadOnly(read_only)
textarea.setFixedHeight(height)
if width:
textarea.setFixedWidth(width)
if callback:
textarea.textChanged.connect(callback)
if position is not None:
layout.insertWidget(position, textarea)
else:
layout.addWidget(textarea)
self.injected_elements.append(textarea)
return textarea
def add_label_to_layout(self, layout, text, style="normal", position=None,
width=None, height=None, alignment=None):
label = QLabel(text)
label.setStyleSheet(self._get_label_style(style))
if width:
label.setFixedWidth(width)
if height:
label.setFixedHeight(height)
if alignment:
label.setAlignment(alignment)
if position is not None:
layout.insertWidget(position, label)
else:
layout.addWidget(label)
self.injected_elements.append(label)
return label
def add_separator(self, layout, position=None, orientation=Qt.Horizontal):
line = QFrame()
if orientation == Qt.Horizontal:
line.setFrameShape(QFrame.HLine)
else:
line.setFrameShape(QFrame.VLine)
line.setFrameShadow(QFrame.Sunken)
if position is not None:
layout.insertWidget(position, line)
else:
layout.addWidget(line)
self.injected_elements.append(line)
return line
def add_groupbox(self, layout, title, position=None):
group = QGroupBox(title)
group_layout = QVBoxLayout(group)
if position is not None:
layout.insertWidget(position, group)
else:
layout.addWidget(group)
self.injected_elements.append(group)
return group, group_layout
def add_custom_widget(self, layout, widget, position=None):
if position is not None:
layout.insertWidget(position, widget)
else:
layout.addWidget(widget)
self.injected_elements.append(widget)
return widget
# ==================== 添加标签页/面板(新增) ====================
def add_tab_to_main(self, tab_title, icon=None):
central = self.get_central_widget()
if not central:
return None
tab_widget = central.findChild(QTabWidget)
if not tab_widget:
tab_widget = QTabWidget(central)
layout = central.layout()
if layout:
layout.addWidget(tab_widget)
else:
new_layout = QVBoxLayout(central)
new_layout.addWidget(tab_widget)
new_tab = QWidget()
tab_layout = QVBoxLayout(new_tab)
if icon:
index = tab_widget.addTab(new_tab, icon, tab_title)
else:
index = tab_widget.addTab(new_tab, tab_title)
self._custom_tabs.append((tab_widget, new_tab, index))
self.injected_elements.append(new_tab)
return new_tab, tab_layout, index
def add_panel_to_main(self, title, area=Qt.LeftDockWidgetArea, floating=False):
dock = QDockWidget(title, self.main_window)
dock.setAllowedAreas(Qt.AllDockWidgetAreas)
content = QWidget()
content_layout = QVBoxLayout(content)
dock.setWidget(content)
self.main_window.addDockWidget(area, dock)
if floating:
dock.setFloating(True)
self._custom_panels.append(dock)
self.injected_elements.append(dock)
return dock, content, content_layout
def add_toolbar(self, title, area=Qt.TopToolBarArea):
toolbar = QToolBar(title, self.main_window)
toolbar.setObjectName(f"plugin_toolbar_{title}")
self.main_window.addToolBar(area, toolbar)
self.injected_elements.append(toolbar)
return toolbar
def add_status_widget(self, widget, permanent=True):
status_bar = self.get_status_bar()
if not status_bar:
return None
if permanent:
status_bar.addPermanentWidget(widget)
else:
status_bar.addWidget(widget)
self.injected_elements.append(widget)
return widget
# ==================== 菜单和快捷键(新增) ====================
def add_menu_action(self, menu, text, callback, icon=None, shortcut=None, tooltip=None):
action = QAction(text, self.main_window)
if icon:
action.setIcon(icon)
if shortcut:
action.setShortcut(shortcut)
if tooltip:
action.setToolTip(tooltip)
action.triggered.connect(callback)
menu.addAction(action)
self.injected_elements.append(action)
return action
def add_menu(self, menu_bar, title, icon=None):
menu = menu_bar.addMenu(title)
if icon:
menu.setIcon(icon)
self.injected_elements.append(menu)
return menu
def add_shortcut(self, key_sequence, callback, context=Qt.WindowShortcut):
shortcut = QShortcut(QKeySequence(key_sequence), self.main_window)
shortcut.setContext(context)
shortcut.activated.connect(callback)
self.injected_elements.append(shortcut)
return shortcut
# ==================== 修改现有控件 ====================
def modify_button(self, button, text=None, callback=None, style=None,
icon=None, tooltip=None, enabled=None, visible=None):
if isinstance(button, str):
button = self.find_button_by_text(button)
if not button:
return False
if text is not None:
button.setText(text)
if callback is not None:
try:
button.clicked.disconnect()
except Exception:
pass
button.clicked.connect(callback)
if style is not None:
button.setStyleSheet(self._get_button_style(style))
if icon is not None:
button.setIcon(icon)
if tooltip is not None:
button.setToolTip(tooltip)
if enabled is not None:
button.setEnabled(enabled)
if visible is not None:
button.setVisible(visible)
return True
def modify_label(self, label, text=None, style=None, color=None, font_size=None):
if isinstance(label, str):
label = self.find_label_by_text(label)
if not label:
return False
if text is not None:
label.setText(text)
if style is not None:
label.setStyleSheet(self._get_label_style(style))
if color is not None:
label.setStyleSheet(f"color: {color};")
if font_size is not None:
font = label.font()
font.setPointSize(font_size)
label.setFont(font)
return True
def hide_button(self, button):
return self.modify_button(button, visible=False)
def show_button(self, button):
return self.modify_button(button, visible=True)
def disable_button(self, button):
return self.modify_button(button, enabled=False)
def enable_button(self, button):
return self.modify_button(button, enabled=True)
# ==================== 布局操作(新增) ====================
def get_layout_of_widget(self, widget):
if widget and widget.parent():
return widget.parent().layout()
return None
def remove_widget_from_layout(self, layout, widget):
if layout and widget:
layout.removeWidget(widget)
widget.setParent(None)
return True
return False
def move_widget_in_layout(self, layout, widget, new_position):
if not layout or not widget:
return False
current_index = layout.indexOf(widget)
if current_index < 0:
return False
layout.removeWidget(widget)
layout.insertWidget(new_position, widget)
return True
def swap_widgets_in_layout(self, layout, widget1, widget2):
if not layout or not widget1 or not widget2:
return False
idx1 = layout.indexOf(widget1)
idx2 = layout.indexOf(widget2)
if idx1 < 0 or idx2 < 0:
return False
layout.removeWidget(widget1)
layout.removeWidget(widget2)
min_idx = min(idx1, idx2)
layout.insertWidget(min_idx, widget2)
layout.insertWidget(min_idx + 1, widget1)
return True
def insert_layout(self, parent_layout, position, orientation="vertical"):
if orientation == "vertical":
new_layout = QVBoxLayout()
else:
new_layout = QHBoxLayout()
parent_layout.insertLayout(position, new_layout)
return new_layout
# ==================== 对话框和提示(新增) ====================
def show_message_box(self, title, message, icon=QMessageBox.Information, buttons=QMessageBox.Ok):
msg_box = QMessageBox(self.main_window)
msg_box.setWindowTitle(title)
msg_box.setText(message)
msg_box.setIcon(icon)
msg_box.setStandardButtons(buttons)
return msg_box.exec_()
def show_input_dialog(self, title, label, default_text="", mode=QLineEdit.Normal):
text, ok = QInputDialog.getText(self.main_window, title, label, mode, default_text)
return text if ok else None
def show_file_dialog(self, title, filter="所有文件 (*.*)", directory=""):
file_path, _ = QFileDialog.getOpenFileName(self.main_window, title, directory, filter)
return file_path
def show_save_dialog(self, title, filter="所有文件 (*.*)", directory=""):
file_path, _ = QFileDialog.getSaveFileName(self.main_window, title, directory, filter)
return file_path
def show_color_dialog(self, initial_color=QColor(255, 255, 255)):
color = QColorDialog.getColor(initial_color, self.main_window)
return color if color.isValid() else None
def show_font_dialog(self, initial_font=None):
if initial_font is None:
initial_font = QFont()
font, ok = QFontDialog.getFont(initial_font, self.main_window)
return font if ok else None
# ==================== 样式和主题(新增) ====================
def get_app_style(self):
return self.main_window.styleSheet()
def set_app_style(self, style_sheet):
self.main_window.setStyleSheet(style_sheet)
def append_app_style(self, style_sheet):
current = self.main_window.styleSheet()
self.main_window.setStyleSheet(current + "\n" + style_sheet)
def set_widget_style(self, widget, style_sheet):
if widget:
widget.setStyleSheet(style_sheet)
def get_palette(self):
return self.main_window.palette()
def set_palette_color(self, role, color, group=QPalette.Active):
palette = self.main_window.palette()
palette.setColor(group, role, QColor(color))
self.main_window.setPalette(palette)
# ==================== 窗口操作(新增) ====================
def set_window_title(self, title):
self.main_window.setWindowTitle(title)
def get_window_title(self):
return self.main_window.windowTitle()
def set_window_size(self, width, height):
self.main_window.resize(width, height)
def get_window_size(self):
return self.main_window.size()
def set_window_position(self, x, y):
self.main_window.move(x, y)
def get_window_position(self):
return self.main_window.pos()
def minimize_window(self):
self.main_window.showMinimized()
def maximize_window(self):
self.main_window.showMaximized()
def restore_window(self):
self.main_window.showNormal()
def set_window_opacity(self, opacity):
self.main_window.setWindowOpacity(opacity)
def set_window_on_top(self, on_top=True):
flags = self.main_window.windowFlags()
if on_top:
flags |= Qt.WindowStaysOnTopHint
else:
flags &= ~Qt.WindowStaysOnTopHint
self.main_window.setWindowFlags(flags)
self.main_window.show()
# ==================== 定时器和动画(新增) ====================
def create_timer(self, interval, callback, single_shot=False):
timer = QTimer(self.main_window)
timer.setInterval(interval)
timer.setSingleShot(single_shot)
timer.timeout.connect(callback)
self.injected_elements.append(timer)
return timer
def start_timer(self, timer):
if timer:
timer.start()
def stop_timer(self, timer):
if timer:
timer.stop()
def delayed_call(self, milliseconds, callback):
QTimer.singleShot(milliseconds, callback)
# ==================== 资源管理(新增) ====================
def load_icon(self, path):
if os.path.exists(path):
return QIcon(path)
return None
def load_pixmap(self, path):
if os.path.exists(path):
return QPixmap(path)
return None
def load_font(self, path):
if os.path.exists(path):
font_id = QFontDatabase.addApplicationFont(path)
if font_id >= 0:
return QFontDatabase.applicationFontFamilies(font_id)[0]
return None
def create_color(self, r, g, b, a=255):
return QColor(r, g, b, a)
def create_gradient(self, x1, y1, x2, y2, color1, color2):
gradient = QLinearGradient(x1, y1, x2, y2)
gradient.setColorAt(0, QColor(color1))
gradient.setColorAt(1, QColor(color2))
return gradient
# ==================== 内部辅助方法 ====================
def _get_top_bar(self):
for name in ["top_bar", "title_bar", "header", "top_frame"]:
widget = self.find_widget_by_name(name)
if widget:
return widget
return None
def _get_bottom_bar(self):
from PyQt5.QtWidgets import QStatusBar
for name in ["status_bar", "bottom_bar", "statusBar", "footer"]:
widget = self.find_widget_by_name(name)
if widget:
return widget
status_bar = self.main_window.findChild(QStatusBar)
if status_bar:
return status_bar
return None
def _get_sidebar(self):
for name in ["sidebar", "side_panel", "left_panel", "nav_panel"]:
widget = self.find_widget_by_name(name)
if widget:
return widget
return None
def _get_draw_area(self):
for name in ["draw_area", "draw_widget", "central_area", "main_area"]:
widget = self.find_widget_by_name(name)
if widget:
return widget
return None
def _get_button_style(self, style):
styles = {
"primary": """
QPushButton { background: #0067c0; color: white; border: none;
border-radius: 6px; padding: 8px 16px; font-weight: 600; font-size: 13px; }
QPushButton:hover { background: #005a9e; }
QPushButton:pressed { background: #004578; }
QPushButton:disabled { background: #cccccc; color: #999999; }
""",
"success": """
QPushButton { background: #107c10; color: white; border: none;
border-radius: 6px; padding: 8px 16px; font-weight: 600; font-size: 13px; }
QPushButton:hover { background: #0e6c0e; }
QPushButton:pressed { background: #0a5c0a; }
""",
"warning": """
QPushButton { background: #ca5010; color: white; border: none;
border-radius: 6px; padding: 8px 16px; font-weight: 600; font-size: 13px; }
QPushButton:hover { background: #b04810; }
""",
"danger": """
QPushButton { background: #c42b1c; color: white; border: none;
border-radius: 6px; padding: 8px 16px; font-weight: 600; font-size: 13px; }
QPushButton:hover { background: #a82318; }
""",
"ghost": """
QPushButton { background: transparent; color: #0067c0; border: 1px solid #0067c0;
border-radius: 6px; padding: 8px 16px; font-weight: 600; font-size: 13px; }
QPushButton:hover { background: #e5f1fb; }
""",
"small": """
QPushButton { background: #f0f0f0; color: #333; border: 1px solid #d1d1d1;
border-radius: 4px; padding: 4px 10px; font-size: 11px; }
QPushButton:hover { background: #e0e0e0; }
""",
}
return styles.get(style, styles["primary"])
def _get_label_style(self, style):
styles = {
"normal": "color: #333333; font-size: 13px;",
"title": "color: #1a1a1a; font-size: 18px; font-weight: 700;",
"subtitle": "color: #555555; font-size: 14px; font-weight: 600;",
"hint": "color: #999999; font-size: 12px;",
"success": "color: #107c10; font-size: 13px; font-weight: 600;",
"warning": "color: #ca5010; font-size: 13px; font-weight: 600;",
"danger": "color: #c42b1c; font-size: 13px; font-weight: 600;",
}
return styles.get(style, styles["normal"])
# ==================== 清理 ====================
def cleanup(self):
for element in self.injected_elements:
try:
if hasattr(element, 'deleteLater'):
element.deleteLater()
elif hasattr(element, 'close'):
element.close()
except Exception:
pass
self.injected_elements = []
self._custom_tabs = []
self._custom_panels = []
class DraggableButton(QPushButton):
"""可拖动的按钮类"""
def __init__(self, text, parent=None):
super().__init__(text, parent)
self._drag_position = None
self.setCursor(QCursor(Qt.OpenHandCursor))
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
self._drag_position = event.globalPos() - self.frameGeometry().topLeft()
self.setCursor(QCursor(Qt.ClosedHandCursor))
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() == Qt.LeftButton and self._drag_position:
self.move(event.globalPos() - self._drag_position)
super().mouseMoveEvent(event)
def mouseReleaseEvent(self, event):
self._drag_position = None
self.setCursor(QCursor(Qt.OpenHandCursor))
super().mouseReleaseEvent(event)