| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- import os
- import sys
- # 添加项目根目录到 Python 路径
- current_dir = os.path.dirname(os.path.abspath(__file__))
- project_root = os.path.dirname(current_dir)
- sys.path.append(project_root)
- import yaml
- from PyQt6.QtWidgets import (
- QApplication, QWidget, QVBoxLayout, QHBoxLayout,
- QPushButton, QLabel, QScrollArea, QGridLayout, QFrame, QGroupBox, QFileDialog,
- QComboBox, QLineEdit, QFormLayout
- )
- from PyQt6.QtCore import Qt, QSize, QTimer
- from PyQt6.QtGui import QFont
- from kodi_util.kodi_server import KodiServer
- from kodi_util.task_registry import get_registered_tasks, get_task_class
- from kodi_util.kodi_play.thread import KodiStateMonitorThread
- class TaskButton(QPushButton):
- """代表一个任务的按钮"""
-
- def __init__(self, task_name, parent=None):
- super().__init__(task_name, parent)
- self.task_name = task_name
- self.setMinimumWidth(200)
- self.setMinimumHeight(60)
- self.setFont(QFont("Arial", 12, QFont.Weight.Bold))
- self.setStyleSheet("""
- QPushButton {
- background-color: #3498db;
- color: white;
- border-radius: 5px;
- padding: 10px;
- }
- QPushButton:hover {
- background-color: #2980b9;
- }
- QPushButton:pressed {
- background-color: #1c6ea4;
- }
- """)
- class TaskConfigWidget(QWidget):
- """任务配置界面"""
-
- def __init__(self, task_name, parent=None):
- super().__init__(parent)
- self.task_name = task_name
- self.task_instance = None
- self.state_monitor = None
- self.init_ui()
-
- def init_ui(self):
- """初始化配置界面"""
- layout = QVBoxLayout(self)
-
- # 添加控制按钮
- button_layout = QHBoxLayout()
-
- self.start_btn = QPushButton("开始任务")
- self.start_btn.clicked.connect(self.start_task)
- self.stop_btn = QPushButton("停止任务")
- self.stop_btn.clicked.connect(self.stop_task)
- self.stop_btn.setEnabled(False)
-
- button_layout.addWidget(self.start_btn)
- button_layout.addWidget(self.stop_btn)
-
- layout.addLayout(button_layout)
-
- def start_task(self):
- """开始任务"""
- try:
- # 获取任务类
- task_class = get_task_class(self.task_name)
-
- # 创建任务实例
- self.task_instance = task_class()
-
- # 创建状态监控线程
- if hasattr(self.task_instance, 'kodi_server'):
- self.state_monitor = KodiStateMonitorThread(
- self.task_instance.kodi_server.clients,
- interval=1.0
- )
- self.state_monitor.start()
-
- # 设置任务实例的状态监控器
- self.task_instance.state_monitor = self.state_monitor
-
- # 如果是联动任务,直接开始播放
- if self.task_name == "联动":
- self.task_instance.play()
-
- # 启动任务
- if hasattr(self.task_instance, 'start_monitoring'):
- self.task_instance.start_monitoring()
-
- # 更新按钮状态
- self.start_btn.setEnabled(False)
- self.stop_btn.setEnabled(True)
-
- except Exception as e:
- print(f"启动任务失败: {str(e)}")
-
- def stop_task(self):
- """停止任务"""
- if self.task_instance:
- try:
- # 停止状态监控线程
- if self.state_monitor:
- self.state_monitor.stop()
- self.state_monitor = None
-
- self.task_instance.interrupt()
- self.task_instance = None
-
- # 更新按钮状态
- self.start_btn.setEnabled(True)
- self.stop_btn.setEnabled(False)
-
- except Exception as e:
- print(f"停止任务失败: {str(e)}")
- class PlayWindow(QWidget):
- """播放效果模块窗口"""
-
- def __init__(self):
- super().__init__()
-
- # 设置窗口标题和大小
- self.setWindowTitle("任务控制面板")
- self.setMinimumSize(QSize(800, 600))
-
- # 创建主布局
- self.main_layout = QVBoxLayout(self)
- self.main_layout.setContentsMargins(20, 20, 20, 20)
- self.main_layout.setSpacing(15)
-
- # 初始化 KodiServer
- self.kodi_server = KodiServer()
-
- # 存储正在运行的任务
- self.running_tasks = {}
-
- # 存储状态监控线程
- self.state_monitors = {}
-
- # 创建状态更新定时器
- self.status_update_timer = QTimer()
- self.status_update_timer.timeout.connect(self.update_ui)
- self.status_update_timer.start(1000) # 每秒更新一次UI
-
- # 初始化UI
- self.init_ui()
-
- # 自动启动按钮监听任务
- self.execute_task("按钮监听串")
-
- def init_ui(self):
- """初始化用户界面"""
- # 创建标题
- title_label = QLabel("任务控制面板")
- title_label.setFont(QFont("Arial", 18, QFont.Weight.Bold))
- title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
- self.main_layout.addWidget(title_label)
-
- # 创建正在执行的任务区域
- running_tasks_group = QGroupBox("正在执行的任务")
- running_tasks_group.setStyleSheet("""
- QGroupBox {
- background-color: #f8f9fa;
- border-radius: 5px;
- padding: 10px;
- }
- """)
- self.running_tasks_layout = QVBoxLayout(running_tasks_group)
- self.main_layout.addWidget(running_tasks_group)
-
- # 创建分隔线
- line = QFrame()
- line.setFrameShape(QFrame.Shape.HLine)
- line.setStyleSheet("background-color: #bdc3c7;")
- self.main_layout.addWidget(line)
-
- # 创建可用任务区域
- available_tasks_group = QGroupBox("可用任务")
- available_tasks_group.setStyleSheet("""
- QGroupBox {
- background-color: #f8f9fa;
- border-radius: 5px;
- padding: 10px;
- }
- """)
- self.available_tasks_layout = QVBoxLayout(available_tasks_group)
- self.main_layout.addWidget(available_tasks_group)
-
- # 添加任务按钮
- self.add_task_buttons()
-
- # 添加状态信息区域
- self.status_frame = QFrame()
- self.status_frame.setFrameShape(QFrame.Shape.StyledPanel)
- self.status_frame.setStyleSheet("background-color: #f0f0f0; border-radius: 5px;")
- status_layout = QVBoxLayout(self.status_frame)
-
- self.status_label = QLabel("准备就绪")
- self.status_label.setFont(QFont("Arial", 12))
- self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- status_layout.addWidget(self.status_label)
-
- self.main_layout.addWidget(self.status_frame)
-
- def add_task_buttons(self):
- """添加所有任务按钮"""
- # 获取所有注册的任务
- registered_tasks = get_registered_tasks()
-
- if not registered_tasks:
- # 如果没有找到任务,添加提示信息
- label = QLabel("未找到注册的任务")
- label.setFont(QFont("Arial", 14))
- label.setAlignment(Qt.AlignmentFlag.AlignCenter)
- label.setStyleSheet("color: #e74c3c;")
- self.available_tasks_layout.addWidget(label)
- return
-
- # 为每个任务创建一个按钮和配置界面
- for task_name in registered_tasks:
- # 创建任务容器
- task_container = QWidget()
- task_layout = QHBoxLayout(task_container)
- task_layout.setContentsMargins(5, 5, 5, 5)
-
- # 创建任务标签
- task_label = QLabel(task_name)
- task_label.setFont(QFont("Arial", 12))
- task_label.setMinimumWidth(200)
- task_layout.addWidget(task_label)
-
- # 创建执行按钮
- execute_btn = QPushButton("执行")
- execute_btn.setMinimumWidth(100)
- execute_btn.clicked.connect(lambda checked, name=task_name: self.execute_task(name))
-
- # 如果任务正在运行,禁用按钮
- if task_name in self.running_tasks:
- execute_btn.setEnabled(False)
- execute_btn.setText("运行中")
- execute_btn.setStyleSheet("""
- QPushButton {
- background-color: #95a5a6;
- color: white;
- border-radius: 5px;
- }
- """)
-
- task_layout.addWidget(execute_btn)
-
- # 添加到布局
- self.available_tasks_layout.addWidget(task_container)
-
- def execute_task(self, task_name):
- """执行任务"""
- try:
- # 如果任务已经在运行,直接返回
- if task_name in self.running_tasks:
- self.update_status(f"任务 {task_name} 已经在运行中")
- return
-
- # 先停止所有正在运行的任务
- self.stop_all_tasks()
-
- # 获取任务类
- task_class = get_task_class(task_name)
-
- # 创建任务实例
- task_instance = task_class()
-
- # 确保任务实例使用相同的KodiServer实例
- if hasattr(task_instance, 'kodi_server'):
- task_instance.kodi_server = self.kodi_server
-
- # 创建状态监控线程
- if hasattr(task_instance, 'kodi_server'):
- state_monitor = KodiStateMonitorThread(
- task_instance.kodi_server.clients,
- interval=1.0
- )
- state_monitor.start()
-
- # 设置任务实例的状态监控器
- task_instance.state_monitor = state_monitor
-
- # 存储状态监控线程
- self.state_monitors[task_name] = state_monitor
-
- # 添加到正在执行的任务列表
- self.add_running_task(task_name, task_instance)
-
- # 如果是联动任务,直接开始播放
- if task_name == "联动":
- try:
- # 使用QTimer延迟执行play方法,避免阻塞UI
- QTimer.singleShot(0, lambda: self._start_linkage_task(task_instance))
- except Exception as e:
- self.update_status(f"联动任务播放失败: {str(e)}")
- return
-
- # 启动任务
- if hasattr(task_instance, 'start_monitoring'):
- try:
- task_instance.start_monitoring()
- except Exception as e:
- self.update_status(f"启动任务监控失败: {str(e)}")
- return
-
- # 更新状态
- self.update_status(f"任务 {task_name} 已开始执行")
-
- except Exception as e:
- self.update_status(f"启动任务失败: {str(e)}")
- # 确保清理资源
- if task_name in self.state_monitors:
- self.state_monitors[task_name].stop()
- del self.state_monitors[task_name]
- if task_name in self.running_tasks:
- del self.running_tasks[task_name]
- def _start_linkage_task(self, task_instance):
- """启动联动任务"""
- try:
- print("开始执行联动任务...")
- print(f"任务实例类型: {type(task_instance)}")
- print(f"任务实例属性: {dir(task_instance)}")
-
- # 检查kodi_server
- if hasattr(task_instance, 'kodi_server'):
- print(f"KodiServer状态: {task_instance.kodi_server}")
- print(f"客户端数量: {len(task_instance.kodi_server.clients)}")
- for client in task_instance.kodi_server.clients:
- print(f"客户端信息: {client.host}:{client.port}")
-
- # 检查state_monitor
- if hasattr(task_instance, 'state_monitor'):
- print(f"状态监控器状态: {task_instance.state_monitor}")
-
- # 执行播放
- print("调用play方法...")
- task_instance.play()
- print("play方法调用完成")
-
- except Exception as e:
- print(f"联动任务播放失败: {str(e)}")
- import traceback
- traceback.print_exc()
- self.update_status(f"联动任务播放失败: {str(e)}")
- def stop_all_tasks(self):
- """停止所有正在运行的任务"""
- for running_task_name, task_instance in list(self.running_tasks.items()):
- try:
- # 停止状态监控线程
- if running_task_name in self.state_monitors:
- self.state_monitors[running_task_name].stop()
- del self.state_monitors[running_task_name]
-
- # 停止任务
- if task_instance:
- task_instance.interrupt()
-
- del self.running_tasks[running_task_name]
-
- # 更新状态
- self.update_status(f"已停止任务 {running_task_name}")
-
- except Exception as e:
- print(f"停止任务 {running_task_name} 失败: {str(e)}")
-
- # 清空正在执行的任务列表
- while self.running_tasks_layout.count():
- item = self.running_tasks_layout.takeAt(0)
- if item.widget():
- item.widget().deleteLater()
-
- def update_ui(self):
- """更新UI状态"""
- # 更新所有任务按钮状态
- self.update_task_buttons()
-
- # 更新状态信息
- if self.running_tasks:
- self.update_status(f"当前运行任务: {', '.join(self.running_tasks.keys())}")
- else:
- self.update_status("准备就绪")
-
- def update_task_buttons(self):
- """更新所有任务按钮的状态"""
- for i in range(self.available_tasks_layout.count()):
- widget = self.available_tasks_layout.itemAt(i).widget()
- if isinstance(widget, QWidget):
- for child in widget.findChildren(QPushButton):
- task_name = child.parent().findChild(QLabel).text()
- if task_name in self.running_tasks:
- child.setEnabled(False)
- child.setText("运行中")
- child.setStyleSheet("""
- QPushButton {
- background-color: #95a5a6;
- color: white;
- border-radius: 5px;
- }
- """)
- else:
- child.setEnabled(True)
- child.setText("执行")
- child.setStyleSheet("")
-
- def add_running_task(self, task_name, task_instance):
- """添加正在执行的任务到显示区域"""
- # 创建任务容器
- task_container = QWidget()
- task_layout = QHBoxLayout(task_container)
- task_layout.setContentsMargins(5, 5, 5, 5)
-
- # 创建任务标签
- task_label = QLabel(task_name)
- task_label.setFont(QFont("Arial", 12))
- task_label.setMinimumWidth(200)
- task_layout.addWidget(task_label)
-
- # 创建停止按钮
- stop_btn = QPushButton("停止")
- stop_btn.setMinimumWidth(100)
- stop_btn.clicked.connect(lambda: self.stop_task(task_name, task_container))
- task_layout.addWidget(stop_btn)
-
- # 添加到布局
- self.running_tasks_layout.addWidget(task_container)
-
- # 存储任务实例
- self.running_tasks[task_name] = task_instance
-
- def stop_task(self, task_name, task_container):
- """停止任务"""
- if task_name in self.running_tasks:
- try:
- # 停止状态监控线程
- if task_name in self.state_monitors:
- self.state_monitors[task_name].stop()
- del self.state_monitors[task_name]
-
- task_instance = self.running_tasks[task_name]
- task_instance.interrupt()
- del self.running_tasks[task_name]
-
- # 从界面中移除任务容器
- self.running_tasks_layout.removeWidget(task_container)
- task_container.deleteLater()
-
- # 更新可用任务区域的按钮状态
- for i in range(self.available_tasks_layout.count()):
- widget = self.available_tasks_layout.itemAt(i).widget()
- if isinstance(widget, QWidget):
- for child in widget.findChildren(QPushButton):
- if child.text() == "运行中":
- child.setEnabled(True)
- child.setText("执行")
- child.setStyleSheet("")
- break
-
- # 更新状态
- self.update_status(f"任务 {task_name} 已停止")
-
- except Exception as e:
- self.update_status(f"停止任务失败: {str(e)}")
-
- def update_status(self, message):
- """更新状态信息"""
- self.status_label.setText(message)
- # 根据消息类型设置不同的样式
- if "错误" in message or "失败" in message:
- color = "#e74c3c" # 红色
- elif "成功" in message:
- color = "#27ae60" # 绿色
- else:
- color = "#2980b9" # 蓝色
- self.status_label.setStyleSheet(f"color: {color};")
- def closeEvent(self, event):
- """窗口关闭事件"""
- # 停止所有任务
- self.stop_all_tasks()
-
- # 停止状态更新定时器
- self.status_update_timer.stop()
-
- # 接受关闭事件
- event.accept()
- if __name__ == "__main__":
- app = QApplication(sys.argv)
- window = PlayWindow()
- window.show()
- sys.exit(app.exec())
-
|