690 lines
26 KiB
Python
690 lines
26 KiB
Python
import sys
|
||
import os
|
||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
|
||
QHBoxLayout, QPushButton, QLabel, QFileDialog,
|
||
QSystemTrayIcon, QMenu, QSpinBox, QStyle, QLineEdit,
|
||
QDialog, QTabWidget, QGroupBox, QComboBox, QCheckBox,
|
||
QDialogButtonBox, QMessageBox, QListWidget)
|
||
from PyQt6.QtCore import Qt, QSettings, QTimer
|
||
from PyQt6.QtGui import QIcon, QAction
|
||
from qt_material import apply_stylesheet
|
||
import darkdetect
|
||
from configparser import ConfigParser
|
||
from .category_manager import CategoryManagerDialog
|
||
|
||
class AISettingsDialog(QDialog):
|
||
def __init__(self, parent=None):
|
||
super().__init__(parent)
|
||
self.setWindowTitle("AI模型设置")
|
||
self.resize(500, 300)
|
||
|
||
layout = QVBoxLayout()
|
||
|
||
# 模型类型选择
|
||
model_type_layout = QHBoxLayout()
|
||
model_type_label = QLabel("模型类型:")
|
||
self.model_type_combo = QComboBox()
|
||
self.model_type_combo.addItems(["不使用AI", "OpenAI兼容API", "Ollama本地模型"])
|
||
self.model_type_combo.currentIndexChanged.connect(self.on_model_type_changed)
|
||
model_type_layout.addWidget(model_type_label)
|
||
model_type_layout.addWidget(self.model_type_combo)
|
||
layout.addLayout(model_type_layout)
|
||
|
||
# OpenAI设置
|
||
self.openai_group = QGroupBox("OpenAI设置")
|
||
openai_layout = QVBoxLayout()
|
||
|
||
api_key_layout = QHBoxLayout()
|
||
api_key_label = QLabel("API密钥:")
|
||
self.api_key_edit = QLineEdit()
|
||
self.api_key_edit.setEchoMode(QLineEdit.EchoMode.Password)
|
||
api_key_layout.addWidget(api_key_label)
|
||
api_key_layout.addWidget(self.api_key_edit)
|
||
openai_layout.addLayout(api_key_layout)
|
||
|
||
base_url_layout = QHBoxLayout()
|
||
base_url_label = QLabel("API地址:")
|
||
self.base_url_edit = QLineEdit("https://api.openai.com/v1")
|
||
base_url_layout.addWidget(base_url_label)
|
||
base_url_layout.addWidget(self.base_url_edit)
|
||
openai_layout.addLayout(base_url_layout)
|
||
|
||
model_layout = QHBoxLayout()
|
||
model_label = QLabel("模型:")
|
||
self.model_edit = QLineEdit("gpt-3.5-turbo")
|
||
model_layout.addWidget(model_label)
|
||
model_layout.addWidget(self.model_edit)
|
||
openai_layout.addLayout(model_layout)
|
||
|
||
self.openai_group.setLayout(openai_layout)
|
||
layout.addWidget(self.openai_group)
|
||
|
||
# Ollama设置
|
||
self.ollama_group = QGroupBox("Ollama设置")
|
||
ollama_layout = QVBoxLayout()
|
||
|
||
ollama_url_layout = QHBoxLayout()
|
||
ollama_url_label = QLabel("服务地址:")
|
||
self.ollama_url_edit = QLineEdit("http://localhost:11434/api")
|
||
ollama_url_layout.addWidget(ollama_url_label)
|
||
ollama_url_layout.addWidget(self.ollama_url_edit)
|
||
ollama_layout.addLayout(ollama_url_layout)
|
||
|
||
ollama_model_layout = QHBoxLayout()
|
||
ollama_model_label = QLabel("模型:")
|
||
self.ollama_model_edit = QLineEdit("llama2")
|
||
ollama_model_layout.addWidget(ollama_model_label)
|
||
ollama_model_layout.addWidget(self.ollama_model_edit)
|
||
ollama_layout.addLayout(ollama_model_layout)
|
||
|
||
self.ollama_group.setLayout(ollama_layout)
|
||
layout.addWidget(self.ollama_group)
|
||
|
||
# 测试连接按钮
|
||
self.test_button = QPushButton("测试连接")
|
||
self.test_button.clicked.connect(self.test_connection)
|
||
layout.addWidget(self.test_button)
|
||
|
||
# 按钮
|
||
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Save | QDialogButtonBox.StandardButton.Cancel)
|
||
button_box.accepted.connect(self.accept)
|
||
button_box.rejected.connect(self.reject)
|
||
layout.addWidget(button_box)
|
||
|
||
self.setLayout(layout)
|
||
|
||
# 初始化界面状态
|
||
self.on_model_type_changed(0)
|
||
|
||
def on_model_type_changed(self, index):
|
||
"""模型类型变更处理"""
|
||
if index == 0: # 不使用AI
|
||
self.openai_group.setVisible(False)
|
||
self.ollama_group.setVisible(False)
|
||
self.test_button.setEnabled(False)
|
||
elif index == 1: # OpenAI
|
||
self.openai_group.setVisible(True)
|
||
self.ollama_group.setVisible(False)
|
||
self.test_button.setEnabled(True)
|
||
elif index == 2: # Ollama
|
||
self.openai_group.setVisible(False)
|
||
self.ollama_group.setVisible(True)
|
||
self.test_button.setEnabled(True)
|
||
|
||
def test_connection(self):
|
||
"""测试AI连接"""
|
||
model_type_index = self.model_type_combo.currentIndex()
|
||
|
||
if model_type_index == 0: # 不使用AI
|
||
QMessageBox.information(self, "测试结果", "未启用AI功能")
|
||
return
|
||
|
||
if model_type_index == 1: # OpenAI
|
||
api_key = self.api_key_edit.text()
|
||
base_url = self.base_url_edit.text()
|
||
model = self.model_edit.text()
|
||
|
||
if not api_key:
|
||
QMessageBox.warning(self, "测试失败", "请输入API密钥")
|
||
return
|
||
|
||
try:
|
||
import openai
|
||
openai.api_key = api_key
|
||
if base_url != "https://api.openai.com/v1":
|
||
openai.base_url = base_url
|
||
|
||
client = openai.OpenAI()
|
||
response = client.chat.completions.create(
|
||
model=model,
|
||
messages=[{"role": "user", "content": "Hello"}],
|
||
max_tokens=5
|
||
)
|
||
|
||
QMessageBox.information(self, "测试结果", "连接成功!")
|
||
except ImportError:
|
||
QMessageBox.warning(self, "测试失败", "未安装openai库,请使用pip install openai安装")
|
||
except Exception as e:
|
||
QMessageBox.warning(self, "测试失败", f"连接失败: {str(e)}")
|
||
|
||
elif model_type_index == 2: # Ollama
|
||
base_url = self.ollama_url_edit.text()
|
||
model = self.ollama_model_edit.text()
|
||
|
||
try:
|
||
import requests
|
||
response = requests.get(f"{base_url}/tags")
|
||
|
||
if response.status_code != 200:
|
||
QMessageBox.warning(self, "测试失败", f"连接Ollama服务失败: {response.status_code}")
|
||
return
|
||
|
||
# 检查模型是否存在
|
||
models = response.json().get('models', [])
|
||
model_exists = any(m['name'] == model for m in models)
|
||
|
||
if model_exists:
|
||
QMessageBox.information(self, "测试结果", f"连接成功!模型 {model} 可用")
|
||
else:
|
||
QMessageBox.warning(self, "测试结果", f"连接成功,但模型 {model} 不存在")
|
||
except ImportError:
|
||
QMessageBox.warning(self, "测试失败", "未安装requests库,请使用pip install requests安装")
|
||
except Exception as e:
|
||
QMessageBox.warning(self, "测试失败", f"连接失败: {str(e)}")
|
||
|
||
def get_config(self):
|
||
"""获取AI配置"""
|
||
model_type_index = self.model_type_combo.currentIndex()
|
||
|
||
if model_type_index == 0: # 不使用AI
|
||
return {"model_type": "none"}
|
||
|
||
if model_type_index == 1: # OpenAI
|
||
return {
|
||
"model_type": "openai",
|
||
"api_key": self.api_key_edit.text(),
|
||
"base_url": self.base_url_edit.text(),
|
||
"model": self.model_edit.text()
|
||
}
|
||
|
||
elif model_type_index == 2: # Ollama
|
||
return {
|
||
"model_type": "ollama",
|
||
"base_url": self.ollama_url_edit.text(),
|
||
"model": self.ollama_model_edit.text()
|
||
}
|
||
|
||
return {"model_type": "none"}
|
||
|
||
def set_config(self, config):
|
||
"""设置AI配置"""
|
||
model_type = config.get("model_type", "none")
|
||
|
||
if model_type == "none":
|
||
self.model_type_combo.setCurrentIndex(0)
|
||
elif model_type == "openai":
|
||
self.model_type_combo.setCurrentIndex(1)
|
||
self.api_key_edit.setText(config.get("api_key", ""))
|
||
self.base_url_edit.setText(config.get("base_url", "https://api.openai.com/v1"))
|
||
self.model_edit.setText(config.get("model", "gpt-3.5-turbo"))
|
||
elif model_type == "ollama":
|
||
self.model_type_combo.setCurrentIndex(2)
|
||
self.ollama_url_edit.setText(config.get("base_url", "http://localhost:11434/api"))
|
||
self.ollama_model_edit.setText(config.get("model", "llama2"))
|
||
|
||
class MainWindow(QMainWindow):
|
||
def __init__(self):
|
||
super().__init__()
|
||
self.setWindowTitle("LLMClipboard v0.1.5")
|
||
self.setMinimumWidth(600)
|
||
self.setMinimumHeight(400)
|
||
|
||
# 初始化服务
|
||
from .app import TextCaptureService
|
||
self.service = TextCaptureService()
|
||
|
||
# 加载配置
|
||
self.config = ConfigParser()
|
||
self.config_file = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config.ini')
|
||
self.load_config()
|
||
|
||
# 创建主窗口部件
|
||
main_widget = QWidget()
|
||
self.setCentralWidget(main_widget)
|
||
main_layout = QVBoxLayout(main_widget)
|
||
main_layout.setContentsMargins(20, 20, 20, 20)
|
||
main_layout.setSpacing(15)
|
||
|
||
# 创建标题标签
|
||
title_label = QLabel("LLM智能剪贴板")
|
||
title_label.setStyleSheet("font-size: 24px; font-weight: bold; margin-bottom: 15px;")
|
||
title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||
main_layout.addWidget(title_label)
|
||
|
||
# 创建设置组
|
||
settings_group = QGroupBox("基本设置")
|
||
settings_layout = QVBoxLayout()
|
||
settings_layout.setSpacing(10)
|
||
|
||
# 保存路径设置
|
||
save_path_layout = QHBoxLayout()
|
||
save_path_label = QLabel("保存路径:")
|
||
save_path_label.setMinimumWidth(100)
|
||
self.save_path_edit = QLineEdit(self.config.get('Settings', 'save_location'))
|
||
browse_button = QPushButton("浏览...")
|
||
browse_button.setFixedWidth(80)
|
||
browse_button.clicked.connect(self.browse_save_path)
|
||
save_path_layout.addWidget(save_path_label)
|
||
save_path_layout.addWidget(self.save_path_edit)
|
||
save_path_layout.addWidget(browse_button)
|
||
settings_layout.addLayout(save_path_layout)
|
||
|
||
# 双击阈值设置
|
||
threshold_layout = QHBoxLayout()
|
||
threshold_label = QLabel("双击阈值 (秒):")
|
||
threshold_label.setMinimumWidth(100)
|
||
self.threshold_spin = QSpinBox()
|
||
self.threshold_spin.setRange(1, 1000)
|
||
self.threshold_spin.setValue(int(float(self.config.get('Settings', 'double_click_threshold')) * 1000))
|
||
self.threshold_spin.setSingleStep(100)
|
||
threshold_layout.addWidget(threshold_label)
|
||
threshold_layout.addWidget(self.threshold_spin)
|
||
threshold_layout.addStretch()
|
||
settings_layout.addLayout(threshold_layout)
|
||
|
||
settings_group.setLayout(settings_layout)
|
||
main_layout.addWidget(settings_group)
|
||
|
||
# 创建高级设置组
|
||
advanced_group = QGroupBox("高级设置")
|
||
advanced_layout = QVBoxLayout()
|
||
|
||
# 高级设置按钮
|
||
advanced_buttons_layout = QHBoxLayout()
|
||
|
||
# AI设置按钮
|
||
self.ai_button = QPushButton("AI模型设置")
|
||
self.ai_button.setMinimumHeight(30)
|
||
self.ai_button.clicked.connect(self.show_ai_settings)
|
||
advanced_buttons_layout.addWidget(self.ai_button)
|
||
|
||
# 分类管理按钮
|
||
self.category_button = QPushButton("分类管理")
|
||
self.category_button.setMinimumHeight(30)
|
||
self.category_button.clicked.connect(self.show_category_manager)
|
||
advanced_buttons_layout.addWidget(self.category_button)
|
||
|
||
advanced_layout.addLayout(advanced_buttons_layout)
|
||
|
||
# 最近保存列表
|
||
recent_group = QGroupBox("最近保存")
|
||
recent_layout = QVBoxLayout()
|
||
self.recent_list = QListWidget()
|
||
self.recent_list.itemDoubleClicked.connect(self.open_recent_file)
|
||
recent_layout.addWidget(self.recent_list)
|
||
recent_group.setLayout(recent_layout)
|
||
advanced_layout.addWidget(recent_group)
|
||
|
||
advanced_group.setLayout(advanced_layout)
|
||
main_layout.addWidget(advanced_group)
|
||
|
||
# 状态显示
|
||
status_group = QGroupBox("状态")
|
||
status_layout = QVBoxLayout()
|
||
|
||
self.status_label = QLabel("状态: 已停止")
|
||
self.status_label.setStyleSheet("font-weight: bold;")
|
||
status_layout.addWidget(self.status_label)
|
||
|
||
status_group.setLayout(status_layout)
|
||
main_layout.addWidget(status_group)
|
||
|
||
# 控制按钮
|
||
button_layout = QHBoxLayout()
|
||
button_layout.setSpacing(10)
|
||
|
||
self.start_button = QPushButton("启动监听")
|
||
self.start_button.setMinimumHeight(40)
|
||
self.start_button.setStyleSheet("font-weight: bold;")
|
||
self.start_button.clicked.connect(self.toggle_monitoring)
|
||
|
||
self.save_button = QPushButton("保存设置")
|
||
self.save_button.setMinimumHeight(40)
|
||
self.save_button.clicked.connect(self.save_config)
|
||
|
||
button_layout.addWidget(self.start_button)
|
||
button_layout.addWidget(self.save_button)
|
||
main_layout.addLayout(button_layout)
|
||
|
||
# 系统托盘
|
||
self.setup_system_tray()
|
||
|
||
# 设置样式
|
||
self.setup_style()
|
||
|
||
# 保存的文件路径
|
||
self._last_saved_file = None
|
||
|
||
def load_config(self):
|
||
if os.path.exists(self.config_file):
|
||
self.config.read(self.config_file)
|
||
else:
|
||
self.config['Settings'] = {
|
||
'save_location': os.path.expanduser('~/Documents'),
|
||
'double_click_threshold': '0.3'
|
||
}
|
||
|
||
# 添加AI设置
|
||
if not self.config.has_section('AI'):
|
||
self.config['AI'] = {
|
||
'model_type': 'none',
|
||
'api_key': '',
|
||
'base_url': 'https://api.openai.com/v1',
|
||
'model': 'gpt-3.5-turbo'
|
||
}
|
||
|
||
self.save_config()
|
||
|
||
def save_config(self):
|
||
self.config['Settings']['save_location'] = self.save_path_edit.text()
|
||
self.config['Settings']['double_click_threshold'] = str(self.threshold_spin.value() / 1000)
|
||
|
||
with open(self.config_file, 'w') as f:
|
||
self.config.write(f)
|
||
|
||
# 重新加载服务配置
|
||
if hasattr(self.service, 'load_config'):
|
||
self.service.load_config()
|
||
|
||
self.show_message("设置已保存")
|
||
|
||
def browse_save_path(self):
|
||
folder = QFileDialog.getExistingDirectory(self, "选择保存路径", self.save_path_edit.text())
|
||
if folder:
|
||
self.save_path_edit.setText(folder)
|
||
|
||
def show_ai_settings(self):
|
||
"""显示AI设置对话框"""
|
||
dialog = AISettingsDialog(self)
|
||
|
||
# 设置当前配置
|
||
ai_config = {}
|
||
if self.config.has_section('AI'):
|
||
for key, value in self.config.items('AI'):
|
||
ai_config[key] = value
|
||
|
||
dialog.set_config(ai_config)
|
||
|
||
if dialog.exec():
|
||
# 获取新配置
|
||
new_config = dialog.get_config()
|
||
|
||
# 保存到配置文件
|
||
if not self.config.has_section('AI'):
|
||
self.config.add_section('AI')
|
||
|
||
for key, value in new_config.items():
|
||
self.config.set('AI', key, str(value))
|
||
|
||
self.save_config()
|
||
|
||
# 更新服务的AI配置
|
||
if hasattr(self.service, 'update_ai_config'):
|
||
self.service.update_ai_config(new_config)
|
||
|
||
def show_category_manager(self):
|
||
"""显示分类管理器对话框"""
|
||
# 获取当前分类
|
||
categories = {}
|
||
if hasattr(self.service, 'doc_processor'):
|
||
categories = self.service.doc_processor.categories
|
||
|
||
# 创建分类管理器对话框
|
||
dialog = CategoryManagerDialog(categories, self)
|
||
|
||
# 连接更新信号
|
||
dialog.categories_updated.connect(self.update_categories)
|
||
|
||
# 显示对话框
|
||
dialog.exec()
|
||
|
||
def update_categories(self, new_categories):
|
||
"""更新分类"""
|
||
if hasattr(self.service, 'doc_processor'):
|
||
self.service.doc_processor.update_categories(new_categories)
|
||
self.show_message("分类已更新")
|
||
|
||
def add_recent_file(self, file_path):
|
||
"""添加最近保存的文件到列表"""
|
||
# 检查是否已存在
|
||
for i in range(self.recent_list.count()):
|
||
if self.recent_list.item(i).data(Qt.ItemDataRole.UserRole) == file_path:
|
||
# 已存在,移到顶部
|
||
item = self.recent_list.takeItem(i)
|
||
self.recent_list.insertItem(0, item)
|
||
return
|
||
|
||
# 不存在,添加到顶部
|
||
item = QListWidgetItem(os.path.basename(file_path))
|
||
item.setData(Qt.ItemDataRole.UserRole, file_path)
|
||
self.recent_list.insertItem(0, item)
|
||
|
||
# 限制最大数量
|
||
while self.recent_list.count() > 10:
|
||
self.recent_list.takeItem(self.recent_list.count() - 1)
|
||
|
||
def open_recent_file(self, item):
|
||
"""打开最近保存的文件"""
|
||
file_path = item.data(Qt.ItemDataRole.UserRole)
|
||
if file_path and os.path.exists(file_path):
|
||
self.open_saved_file(file_path)
|
||
|
||
def toggle_monitoring(self):
|
||
if self.start_button.text() == "启动监听":
|
||
self.start_button.setText("停止监听")
|
||
self.status_label.setText("状态: 正在运行")
|
||
self.toggle_action.setText("停止监听")
|
||
self.status_action.setText("状态: 正在运行")
|
||
self.tray_icon.setIcon(self.active_icon)
|
||
self.save_config()
|
||
# 重新加载配置
|
||
self.service.load_config()
|
||
self.service.start()
|
||
else:
|
||
self.start_button.setText("启动监听")
|
||
self.status_label.setText("状态: 已停止")
|
||
self.toggle_action.setText("启动监听")
|
||
self.status_action.setText("状态: 已停止")
|
||
self.tray_icon.setIcon(self.inactive_icon)
|
||
self.service.stop()
|
||
|
||
def setup_system_tray(self):
|
||
self.tray_icon = QSystemTrayIcon(self)
|
||
|
||
# 创建不同状态的图标
|
||
self.active_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton)
|
||
self.inactive_icon = self.style().standardIcon(QStyle.StandardPixmap.SP_ComputerIcon)
|
||
|
||
# 设置初始图标
|
||
self.tray_icon.setIcon(self.inactive_icon)
|
||
|
||
# 创建托盘菜单
|
||
tray_menu = QMenu()
|
||
|
||
# 状态显示
|
||
self.status_action = QAction("状态: 已停止", self)
|
||
self.status_action.setEnabled(False)
|
||
tray_menu.addAction(self.status_action)
|
||
tray_menu.addSeparator()
|
||
|
||
# 主要操作
|
||
self.toggle_action = QAction("启动监听", self)
|
||
self.toggle_action.triggered.connect(self.toggle_monitoring)
|
||
tray_menu.addAction(self.toggle_action)
|
||
|
||
show_action = QAction("显示主窗口", self)
|
||
show_action.triggered.connect(self.show)
|
||
tray_menu.addAction(show_action)
|
||
|
||
# 设置子菜单
|
||
settings_menu = QMenu("设置", self)
|
||
|
||
# 保存路径设置
|
||
change_path_action = QAction("更改保存路径", self)
|
||
change_path_action.triggered.connect(self.browse_save_path)
|
||
settings_menu.addAction(change_path_action)
|
||
|
||
# AI设置
|
||
ai_settings_action = QAction("AI模型设置", self)
|
||
ai_settings_action.triggered.connect(self.show_ai_settings)
|
||
settings_menu.addAction(ai_settings_action)
|
||
|
||
# 自启动设置
|
||
self.autostart_action = QAction("开机自启动", self)
|
||
self.autostart_action.setCheckable(True)
|
||
self.autostart_action.setChecked(self.is_autostart_enabled())
|
||
self.autostart_action.triggered.connect(self.toggle_autostart)
|
||
settings_menu.addAction(self.autostart_action)
|
||
|
||
tray_menu.addMenu(settings_menu)
|
||
tray_menu.addSeparator()
|
||
|
||
# 退出操作
|
||
quit_action = QAction("退出", self)
|
||
quit_action.triggered.connect(self.quit_application)
|
||
tray_menu.addAction(quit_action)
|
||
|
||
self.tray_icon.setContextMenu(tray_menu)
|
||
self.tray_icon.show()
|
||
|
||
# 双击托盘图标显示主窗口
|
||
self.tray_icon.activated.connect(self.tray_icon_activated)
|
||
|
||
def tray_icon_activated(self, reason):
|
||
if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
|
||
self.show()
|
||
|
||
def is_autostart_enabled(self):
|
||
"""检查是否启用了自启动"""
|
||
import winreg
|
||
try:
|
||
key = winreg.OpenKey(
|
||
winreg.HKEY_CURRENT_USER,
|
||
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
||
0, winreg.KEY_READ
|
||
)
|
||
try:
|
||
value, _ = winreg.QueryValueEx(key, "LLMClipboard")
|
||
return True
|
||
except:
|
||
return False
|
||
finally:
|
||
winreg.CloseKey(key)
|
||
except:
|
||
return False
|
||
|
||
def toggle_autostart(self):
|
||
"""切换自启动状态"""
|
||
import winreg
|
||
try:
|
||
key = winreg.OpenKey(
|
||
winreg.HKEY_CURRENT_USER,
|
||
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
||
0, winreg.KEY_WRITE
|
||
)
|
||
|
||
if self.autostart_action.isChecked():
|
||
# 获取应用程序路径
|
||
if getattr(sys, 'frozen', False):
|
||
app_path = sys.executable
|
||
else:
|
||
app_path = sys.argv[0]
|
||
|
||
winreg.SetValueEx(
|
||
key, "LLMClipboard", 0, winreg.REG_SZ, f'"{app_path}"'
|
||
)
|
||
self.show_message("已设置开机自启动")
|
||
else:
|
||
try:
|
||
winreg.DeleteValue(key, "LLMClipboard")
|
||
self.show_message("已取消开机自启动")
|
||
except:
|
||
pass
|
||
|
||
winreg.CloseKey(key)
|
||
except Exception as e:
|
||
self.show_message(f"设置自启动失败: {str(e)}", error=True)
|
||
|
||
def quit_application(self):
|
||
"""完全退出应用程序"""
|
||
if self.service.running:
|
||
self.service.stop()
|
||
QApplication.quit()
|
||
|
||
def setup_style(self):
|
||
# 根据系统主题设置深色/浅色模式
|
||
if darkdetect.isDark():
|
||
apply_stylesheet(self, theme='dark_blue.xml')
|
||
else:
|
||
apply_stylesheet(self, theme='light_blue.xml')
|
||
|
||
def closeEvent(self, event):
|
||
if self.start_button.text() == "停止监听":
|
||
self.service.stop()
|
||
event.ignore()
|
||
self.hide()
|
||
self.tray_icon.showMessage(
|
||
"LLMClipboard",
|
||
"应用程序已最小化到系统托盘",
|
||
QSystemTrayIcon.Icon.Information,
|
||
2000
|
||
)
|
||
|
||
def show_message(self, message, error=False):
|
||
"""显示消息通知"""
|
||
if error:
|
||
self.status_label.setStyleSheet("color: red;")
|
||
else:
|
||
self.status_label.setStyleSheet("color: green;")
|
||
self.status_label.setText(message)
|
||
|
||
def closeEvent(self, event):
|
||
if self.start_button.text() == "停止监听":
|
||
self.service.stop()
|
||
event.ignore()
|
||
self.hide()
|
||
self.tray_icon.showMessage(
|
||
"LLMClipboard",
|
||
"应用程序已最小化到系统托盘",
|
||
QSystemTrayIcon.Icon.Information,
|
||
2000
|
||
)
|
||
|
||
def show_message(self, message, error=False):
|
||
"""显示消息通知"""
|
||
if error:
|
||
self.status_label.setStyleSheet("color: red; font-weight: bold;")
|
||
else:
|
||
self.status_label.setStyleSheet("color: green; font-weight: bold;")
|
||
self.status_label.setText(message)
|
||
|
||
# 5秒后清除消息
|
||
QTimer.singleShot(5000, lambda: self.status_label.setText("就绪"))
|
||
|
||
def show_notification(self, title, message, icon_type="info", actions=None, file_path=None):
|
||
"""显示带操作按钮的通知"""
|
||
icon = QSystemTrayIcon.Icon.Information
|
||
if icon_type == "error":
|
||
icon = QSystemTrayIcon.Icon.Critical
|
||
elif icon_type == "warning":
|
||
icon = QSystemTrayIcon.Icon.Warning
|
||
|
||
# 保存文件路径用于后续操作
|
||
self._last_saved_file = file_path
|
||
|
||
# 显示通知
|
||
self.tray_icon.showMessage(
|
||
title,
|
||
message,
|
||
icon,
|
||
5000 # 显示5秒
|
||
)
|
||
|
||
# 如果有文件路径,添加通知点击操作
|
||
if file_path:
|
||
self.tray_icon.messageClicked.connect(lambda: self.open_saved_file(file_path))
|
||
|
||
# 更新状态标签
|
||
self.show_message(message)
|
||
|
||
# 添加到最近文件列表
|
||
if file_path and os.path.exists(file_path):
|
||
self.add_recent_file(file_path)
|
||
|
||
def open_saved_file(self, file_path):
|
||
"""打开保存的文件"""
|
||
try:
|
||
os.startfile(file_path)
|
||
except Exception as e:
|
||
self.show_message(f"打开文件失败: {str(e)}", error=True)
|