#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Settings panel - wkhtmltoimage path, screenshot quality, theme """ import os from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QGroupBox, QFormLayout, QSpinBox, QComboBox, QMessageBox, QFileDialog, QScrollArea, QFrame ) from PyQt6.QtCore import Qt, pyqtSignal from config import get_config, save_config from .constants import ( PAGE_PADDING, SECTION_SPACING, GROUP_PADDING_TOP, GROUP_PADDING_SIDE, GROUP_PADDING_BOTTOM, FORM_ROW_SPACING, FORM_H_SPACING, INPUT_HEIGHT, INPUT_MIN_WIDTH, BUTTON_HEIGHT, BUTTON_HEIGHT_NORMAL, BUTTON_MIN_WIDTH, BUTTON_MIN_WIDTH_NORMAL, BUTTON_SPACING, get_title_style ) class SettingsWidget(QWidget): """Settings panel""" log_signal = pyqtSignal(str) theme_changed = pyqtSignal(str) def __init__(self, parent=None): super().__init__(parent) self._setup_ui() self._load_settings() def _setup_ui(self): main_layout = QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) # Scroll area scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setFrameShape(QFrame.Shape.NoFrame) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) content = QWidget() layout = QVBoxLayout(content) layout.setContentsMargins(PAGE_PADDING, PAGE_PADDING, PAGE_PADDING, PAGE_PADDING) layout.setSpacing(SECTION_SPACING) # Title title = QLabel("⚙️ 系统设置") title.setStyleSheet(get_title_style()) layout.addWidget(title) # ==================== Screenshot settings ==================== screenshot_group = QGroupBox("截图设置") screenshot_layout = QFormLayout(screenshot_group) screenshot_layout.setContentsMargins(GROUP_PADDING_SIDE, GROUP_PADDING_TOP, GROUP_PADDING_SIDE, GROUP_PADDING_BOTTOM) screenshot_layout.setSpacing(FORM_ROW_SPACING) screenshot_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) # wkhtmltoimage path wkhtml_layout = QHBoxLayout() wkhtml_layout.setSpacing(FORM_H_SPACING) self.wkhtml_edit = QLineEdit() self.wkhtml_edit.setPlaceholderText("留空则自动检测") self.wkhtml_edit.setMinimumHeight(INPUT_HEIGHT) wkhtml_layout.addWidget(self.wkhtml_edit) browse_btn = QPushButton("浏览...") browse_btn.setMinimumHeight(BUTTON_HEIGHT_NORMAL) browse_btn.clicked.connect(self._browse_wkhtml) wkhtml_layout.addWidget(browse_btn) detect_btn = QPushButton("检测") detect_btn.setMinimumHeight(BUTTON_HEIGHT_NORMAL) detect_btn.clicked.connect(self._detect_wkhtml) wkhtml_layout.addWidget(detect_btn) screenshot_layout.addRow("wkhtmltoimage:", wkhtml_layout) # Screenshot width self.width_spin = QSpinBox() self.width_spin.setRange(800, 3840) self.width_spin.setSingleStep(100) self.width_spin.setMinimumHeight(INPUT_HEIGHT) self.width_spin.setMinimumWidth(130) screenshot_layout.addRow("截图宽度:", self.width_spin) # Screenshot height self.height_spin = QSpinBox() self.height_spin.setRange(600, 2160) self.height_spin.setSingleStep(100) self.height_spin.setMinimumHeight(INPUT_HEIGHT) self.height_spin.setMinimumWidth(130) screenshot_layout.addRow("截图高度:", self.height_spin) # Quality self.quality_spin = QSpinBox() self.quality_spin.setRange(50, 100) self.quality_spin.setMinimumHeight(INPUT_HEIGHT) self.quality_spin.setMinimumWidth(130) self.quality_spin.setSuffix(" %") screenshot_layout.addRow("JPEG质量:", self.quality_spin) # JS delay self.js_delay_spin = QSpinBox() self.js_delay_spin.setRange(1000, 10000) self.js_delay_spin.setSingleStep(500) self.js_delay_spin.setSuffix(" ms") self.js_delay_spin.setMinimumHeight(INPUT_HEIGHT) self.js_delay_spin.setMinimumWidth(130) screenshot_layout.addRow("JS等待时间:", self.js_delay_spin) layout.addWidget(screenshot_group) # ==================== Theme settings ==================== theme_group = QGroupBox("主题设置") theme_layout = QFormLayout(theme_group) theme_layout.setContentsMargins(GROUP_PADDING_SIDE, GROUP_PADDING_TOP, GROUP_PADDING_SIDE, GROUP_PADDING_BOTTOM) theme_layout.setSpacing(FORM_ROW_SPACING) theme_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) self.theme_combo = QComboBox() self.theme_combo.addItem("浅色主题", "light") self.theme_combo.addItem("深色主题", "dark") self.theme_combo.setMinimumHeight(INPUT_HEIGHT) self.theme_combo.setMinimumWidth(INPUT_MIN_WIDTH) self.theme_combo.currentIndexChanged.connect(self._on_theme_change) theme_layout.addRow("界面主题:", self.theme_combo) layout.addWidget(theme_group) # ==================== Platform API settings ==================== api_group = QGroupBox("平台设置") api_layout = QFormLayout(api_group) api_layout.setContentsMargins(GROUP_PADDING_SIDE, GROUP_PADDING_TOP, GROUP_PADDING_SIDE, GROUP_PADDING_BOTTOM) api_layout.setSpacing(FORM_ROW_SPACING) api_layout.setLabelAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter) self.base_url_edit = QLineEdit() self.base_url_edit.setMinimumHeight(INPUT_HEIGHT) api_layout.addRow("平台地址:", self.base_url_edit) self.login_url_edit = QLineEdit() self.login_url_edit.setMinimumHeight(INPUT_HEIGHT) api_layout.addRow("登录地址:", self.login_url_edit) layout.addWidget(api_group) # ==================== Action buttons ==================== btn_layout = QHBoxLayout() btn_layout.setSpacing(BUTTON_SPACING) btn_layout.addStretch() reset_btn = QPushButton("恢复默认") reset_btn.setMinimumWidth(BUTTON_MIN_WIDTH_NORMAL) reset_btn.setMinimumHeight(BUTTON_HEIGHT) reset_btn.clicked.connect(self._reset_defaults) btn_layout.addWidget(reset_btn) save_btn = QPushButton("💾 保存设置") save_btn.setObjectName("primary") save_btn.setMinimumWidth(BUTTON_MIN_WIDTH) save_btn.setMinimumHeight(BUTTON_HEIGHT) save_btn.clicked.connect(self._save_settings) btn_layout.addWidget(save_btn) layout.addLayout(btn_layout) layout.addStretch() scroll.setWidget(content) main_layout.addWidget(scroll) def _load_settings(self): """Load settings from config""" config = get_config() # Screenshot settings self.wkhtml_edit.setText(config.screenshot.wkhtmltoimage_path) self.width_spin.setValue(config.screenshot.width) self.height_spin.setValue(config.screenshot.height) self.quality_spin.setValue(config.screenshot.quality) self.js_delay_spin.setValue(config.screenshot.js_delay_ms) # Theme idx = self.theme_combo.findData(config.theme) if idx >= 0: self.theme_combo.setCurrentIndex(idx) # API settings self.base_url_edit.setText(config.zsgl.base_url) self.login_url_edit.setText(config.zsgl.login_url) def _save_settings(self): """Save settings to config""" config = get_config() # Screenshot settings config.screenshot.wkhtmltoimage_path = self.wkhtml_edit.text().strip() config.screenshot.width = self.width_spin.value() config.screenshot.height = self.height_spin.value() config.screenshot.quality = self.quality_spin.value() config.screenshot.js_delay_ms = self.js_delay_spin.value() # Theme config.theme = self.theme_combo.currentData() # API settings config.zsgl.base_url = self.base_url_edit.text().strip() config.zsgl.login_url = self.login_url_edit.text().strip() save_config(config) self.log_signal.emit("设置已保存") QMessageBox.information(self, "提示", "设置已保存") def _browse_wkhtml(self): """Browse for wkhtmltoimage executable""" filepath, _ = QFileDialog.getOpenFileName( self, "选择 wkhtmltoimage", "", "可执行文件 (*.exe);;所有文件 (*)" ) if filepath: self.wkhtml_edit.setText(filepath) def _detect_wkhtml(self): """Detect wkhtmltoimage path""" from core.screenshot import _resolve_wkhtmltoimage_path path = _resolve_wkhtmltoimage_path() if path: self.wkhtml_edit.setText(path) self.log_signal.emit(f"检测到 wkhtmltoimage: {path}") QMessageBox.information(self, "检测成功", f"找到 wkhtmltoimage:\n{path}") else: self.log_signal.emit("未检测到 wkhtmltoimage") QMessageBox.warning(self, "检测失败", "未找到 wkhtmltoimage,请手动指定路径或安装该工具。") def _on_theme_change(self, index): """Handle theme change""" theme = self.theme_combo.currentData() self.theme_changed.emit(theme) def _reset_defaults(self): """Reset to default settings""" reply = QMessageBox.question( self, "确认", "确定要恢复默认设置吗?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No ) if reply == QMessageBox.StandardButton.Yes: self.wkhtml_edit.clear() self.width_spin.setValue(1920) self.height_spin.setValue(1080) self.quality_spin.setValue(95) self.js_delay_spin.setValue(3000) self.theme_combo.setCurrentIndex(0) # 默认浅色主题 self.base_url_edit.setText("https://postoa.aidunsoft.com") self.login_url_edit.setText("https://postoa.aidunsoft.com/admin/login.aspx") self.log_signal.emit("已恢复默认设置")