play_module_window.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. import os
  4. import sys
  5. # 添加项目根目录到 Python 路径
  6. current_dir = os.path.dirname(os.path.abspath(__file__))
  7. project_root = os.path.dirname(current_dir)
  8. sys.path.append(project_root)
  9. import yaml
  10. from PyQt6.QtWidgets import (
  11. QApplication, QWidget, QVBoxLayout, QHBoxLayout,
  12. QPushButton, QLabel, QScrollArea, QGridLayout, QFrame, QGroupBox, QFileDialog,
  13. QComboBox, QLineEdit, QFormLayout
  14. )
  15. from PyQt6.QtCore import Qt, QSize, QTimer
  16. from PyQt6.QtGui import QFont
  17. from kodi_util.kodi_server import KodiServer
  18. from kodi_util.task_registry import get_registered_tasks, get_task_class
  19. from kodi_util.kodi_play.thread import KodiStateMonitorThread
  20. class TaskButton(QPushButton):
  21. """代表一个任务的按钮"""
  22. def __init__(self, task_name, parent=None):
  23. super().__init__(task_name, parent)
  24. self.task_name = task_name
  25. self.setMinimumWidth(200)
  26. self.setMinimumHeight(60)
  27. self.setFont(QFont("Arial", 12, QFont.Weight.Bold))
  28. self.setStyleSheet("""
  29. QPushButton {
  30. background-color: #3498db;
  31. color: white;
  32. border-radius: 5px;
  33. padding: 10px;
  34. }
  35. QPushButton:hover {
  36. background-color: #2980b9;
  37. }
  38. QPushButton:pressed {
  39. background-color: #1c6ea4;
  40. }
  41. """)
  42. class TaskConfigWidget(QWidget):
  43. """任务配置界面"""
  44. def __init__(self, task_name, parent=None):
  45. super().__init__(parent)
  46. self.task_name = task_name
  47. self.task_instance = None
  48. self.state_monitor = None
  49. self.init_ui()
  50. def init_ui(self):
  51. """初始化配置界面"""
  52. layout = QVBoxLayout(self)
  53. # 添加控制按钮
  54. button_layout = QHBoxLayout()
  55. self.start_btn = QPushButton("开始任务")
  56. self.start_btn.clicked.connect(self.start_task)
  57. self.stop_btn = QPushButton("停止任务")
  58. self.stop_btn.clicked.connect(self.stop_task)
  59. self.stop_btn.setEnabled(False)
  60. button_layout.addWidget(self.start_btn)
  61. button_layout.addWidget(self.stop_btn)
  62. layout.addLayout(button_layout)
  63. def start_task(self):
  64. """开始任务"""
  65. try:
  66. # 获取任务类
  67. task_class = get_task_class(self.task_name)
  68. # 创建任务实例
  69. self.task_instance = task_class()
  70. # 创建状态监控线程
  71. if hasattr(self.task_instance, 'kodi_server'):
  72. self.state_monitor = KodiStateMonitorThread(
  73. self.task_instance.kodi_server.clients,
  74. interval=1.0
  75. )
  76. self.state_monitor.start()
  77. # 设置任务实例的状态监控器
  78. self.task_instance.state_monitor = self.state_monitor
  79. # 如果是联动任务,直接开始播放
  80. if self.task_name == "联动":
  81. self.task_instance.play()
  82. # 启动任务
  83. if hasattr(self.task_instance, 'start_monitoring'):
  84. self.task_instance.start_monitoring()
  85. # 更新按钮状态
  86. self.start_btn.setEnabled(False)
  87. self.stop_btn.setEnabled(True)
  88. except Exception as e:
  89. print(f"启动任务失败: {str(e)}")
  90. def stop_task(self):
  91. """停止任务"""
  92. if self.task_instance:
  93. try:
  94. # 停止状态监控线程
  95. if self.state_monitor:
  96. self.state_monitor.stop()
  97. self.state_monitor = None
  98. self.task_instance.interrupt()
  99. self.task_instance = None
  100. # 更新按钮状态
  101. self.start_btn.setEnabled(True)
  102. self.stop_btn.setEnabled(False)
  103. except Exception as e:
  104. print(f"停止任务失败: {str(e)}")
  105. class PlayWindow(QWidget):
  106. """播放效果模块窗口"""
  107. def __init__(self):
  108. super().__init__()
  109. # 设置窗口标题和大小
  110. self.setWindowTitle("任务控制面板")
  111. self.setMinimumSize(QSize(800, 600))
  112. # 创建主布局
  113. self.main_layout = QVBoxLayout(self)
  114. self.main_layout.setContentsMargins(20, 20, 20, 20)
  115. self.main_layout.setSpacing(15)
  116. # 初始化 KodiServer
  117. self.kodi_server = KodiServer()
  118. # 存储正在运行的任务
  119. self.running_tasks = {}
  120. # 存储状态监控线程
  121. self.state_monitors = {}
  122. # 创建状态更新定时器
  123. self.status_update_timer = QTimer()
  124. self.status_update_timer.timeout.connect(self.update_ui)
  125. self.status_update_timer.start(1000) # 每秒更新一次UI
  126. # 初始化UI
  127. self.init_ui()
  128. # 自动启动按钮监听任务
  129. self.execute_task("按钮监听串")
  130. def init_ui(self):
  131. """初始化用户界面"""
  132. # 创建标题
  133. title_label = QLabel("任务控制面板")
  134. title_label.setFont(QFont("Arial", 18, QFont.Weight.Bold))
  135. title_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
  136. title_label.setStyleSheet("color: #2c3e50; margin-bottom: 10px;")
  137. self.main_layout.addWidget(title_label)
  138. # 创建正在执行的任务区域
  139. running_tasks_group = QGroupBox("正在执行的任务")
  140. running_tasks_group.setStyleSheet("""
  141. QGroupBox {
  142. background-color: #f8f9fa;
  143. border-radius: 5px;
  144. padding: 10px;
  145. }
  146. """)
  147. self.running_tasks_layout = QVBoxLayout(running_tasks_group)
  148. self.main_layout.addWidget(running_tasks_group)
  149. # 创建分隔线
  150. line = QFrame()
  151. line.setFrameShape(QFrame.Shape.HLine)
  152. line.setStyleSheet("background-color: #bdc3c7;")
  153. self.main_layout.addWidget(line)
  154. # 创建可用任务区域
  155. available_tasks_group = QGroupBox("可用任务")
  156. available_tasks_group.setStyleSheet("""
  157. QGroupBox {
  158. background-color: #f8f9fa;
  159. border-radius: 5px;
  160. padding: 10px;
  161. }
  162. """)
  163. self.available_tasks_layout = QVBoxLayout(available_tasks_group)
  164. self.main_layout.addWidget(available_tasks_group)
  165. # 添加任务按钮
  166. self.add_task_buttons()
  167. # 添加状态信息区域
  168. self.status_frame = QFrame()
  169. self.status_frame.setFrameShape(QFrame.Shape.StyledPanel)
  170. self.status_frame.setStyleSheet("background-color: #f0f0f0; border-radius: 5px;")
  171. status_layout = QVBoxLayout(self.status_frame)
  172. self.status_label = QLabel("准备就绪")
  173. self.status_label.setFont(QFont("Arial", 12))
  174. self.status_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
  175. status_layout.addWidget(self.status_label)
  176. self.main_layout.addWidget(self.status_frame)
  177. def add_task_buttons(self):
  178. """添加所有任务按钮"""
  179. # 获取所有注册的任务
  180. registered_tasks = get_registered_tasks()
  181. if not registered_tasks:
  182. # 如果没有找到任务,添加提示信息
  183. label = QLabel("未找到注册的任务")
  184. label.setFont(QFont("Arial", 14))
  185. label.setAlignment(Qt.AlignmentFlag.AlignCenter)
  186. label.setStyleSheet("color: #e74c3c;")
  187. self.available_tasks_layout.addWidget(label)
  188. return
  189. # 为每个任务创建一个按钮和配置界面
  190. for task_name in registered_tasks:
  191. # 创建任务容器
  192. task_container = QWidget()
  193. task_layout = QHBoxLayout(task_container)
  194. task_layout.setContentsMargins(5, 5, 5, 5)
  195. # 创建任务标签
  196. task_label = QLabel(task_name)
  197. task_label.setFont(QFont("Arial", 12))
  198. task_label.setMinimumWidth(200)
  199. task_layout.addWidget(task_label)
  200. # 创建执行按钮
  201. execute_btn = QPushButton("执行")
  202. execute_btn.setMinimumWidth(100)
  203. execute_btn.clicked.connect(lambda checked, name=task_name: self.execute_task(name))
  204. # 如果任务正在运行,禁用按钮
  205. if task_name in self.running_tasks:
  206. execute_btn.setEnabled(False)
  207. execute_btn.setText("运行中")
  208. execute_btn.setStyleSheet("""
  209. QPushButton {
  210. background-color: #95a5a6;
  211. color: white;
  212. border-radius: 5px;
  213. }
  214. """)
  215. task_layout.addWidget(execute_btn)
  216. # 添加到布局
  217. self.available_tasks_layout.addWidget(task_container)
  218. def execute_task(self, task_name):
  219. """执行任务"""
  220. try:
  221. # 如果任务已经在运行,直接返回
  222. if task_name in self.running_tasks:
  223. self.update_status(f"任务 {task_name} 已经在运行中")
  224. return
  225. # 先停止所有正在运行的任务
  226. self.stop_all_tasks()
  227. # 获取任务类
  228. task_class = get_task_class(task_name)
  229. # 创建任务实例
  230. task_instance = task_class()
  231. # 确保任务实例使用相同的KodiServer实例
  232. if hasattr(task_instance, 'kodi_server'):
  233. task_instance.kodi_server = self.kodi_server
  234. # 创建状态监控线程
  235. if hasattr(task_instance, 'kodi_server'):
  236. state_monitor = KodiStateMonitorThread(
  237. task_instance.kodi_server.clients,
  238. interval=1.0
  239. )
  240. state_monitor.start()
  241. # 设置任务实例的状态监控器
  242. task_instance.state_monitor = state_monitor
  243. # 存储状态监控线程
  244. self.state_monitors[task_name] = state_monitor
  245. # 添加到正在执行的任务列表
  246. self.add_running_task(task_name, task_instance)
  247. # 如果是联动任务,直接开始播放
  248. if task_name == "联动":
  249. try:
  250. # 使用QTimer延迟执行play方法,避免阻塞UI
  251. QTimer.singleShot(0, lambda: self._start_linkage_task(task_instance))
  252. except Exception as e:
  253. self.update_status(f"联动任务播放失败: {str(e)}")
  254. return
  255. # 启动任务
  256. if hasattr(task_instance, 'start_monitoring'):
  257. try:
  258. task_instance.start_monitoring()
  259. except Exception as e:
  260. self.update_status(f"启动任务监控失败: {str(e)}")
  261. return
  262. # 更新状态
  263. self.update_status(f"任务 {task_name} 已开始执行")
  264. except Exception as e:
  265. self.update_status(f"启动任务失败: {str(e)}")
  266. # 确保清理资源
  267. if task_name in self.state_monitors:
  268. self.state_monitors[task_name].stop()
  269. del self.state_monitors[task_name]
  270. if task_name in self.running_tasks:
  271. del self.running_tasks[task_name]
  272. def _start_linkage_task(self, task_instance):
  273. """启动联动任务"""
  274. try:
  275. print("开始执行联动任务...")
  276. print(f"任务实例类型: {type(task_instance)}")
  277. print(f"任务实例属性: {dir(task_instance)}")
  278. # 检查kodi_server
  279. if hasattr(task_instance, 'kodi_server'):
  280. print(f"KodiServer状态: {task_instance.kodi_server}")
  281. print(f"客户端数量: {len(task_instance.kodi_server.clients)}")
  282. for client in task_instance.kodi_server.clients:
  283. print(f"客户端信息: {client.host}:{client.port}")
  284. # 检查state_monitor
  285. if hasattr(task_instance, 'state_monitor'):
  286. print(f"状态监控器状态: {task_instance.state_monitor}")
  287. # 执行播放
  288. print("调用play方法...")
  289. task_instance.play()
  290. print("play方法调用完成")
  291. except Exception as e:
  292. print(f"联动任务播放失败: {str(e)}")
  293. import traceback
  294. traceback.print_exc()
  295. self.update_status(f"联动任务播放失败: {str(e)}")
  296. def stop_all_tasks(self):
  297. """停止所有正在运行的任务"""
  298. for running_task_name, task_instance in list(self.running_tasks.items()):
  299. try:
  300. # 停止状态监控线程
  301. if running_task_name in self.state_monitors:
  302. self.state_monitors[running_task_name].stop()
  303. del self.state_monitors[running_task_name]
  304. # 停止任务
  305. if task_instance:
  306. task_instance.interrupt()
  307. del self.running_tasks[running_task_name]
  308. # 更新状态
  309. self.update_status(f"已停止任务 {running_task_name}")
  310. except Exception as e:
  311. print(f"停止任务 {running_task_name} 失败: {str(e)}")
  312. # 清空正在执行的任务列表
  313. while self.running_tasks_layout.count():
  314. item = self.running_tasks_layout.takeAt(0)
  315. if item.widget():
  316. item.widget().deleteLater()
  317. def update_ui(self):
  318. """更新UI状态"""
  319. # 更新所有任务按钮状态
  320. self.update_task_buttons()
  321. # 更新状态信息
  322. if self.running_tasks:
  323. self.update_status(f"当前运行任务: {', '.join(self.running_tasks.keys())}")
  324. else:
  325. self.update_status("准备就绪")
  326. def update_task_buttons(self):
  327. """更新所有任务按钮的状态"""
  328. for i in range(self.available_tasks_layout.count()):
  329. widget = self.available_tasks_layout.itemAt(i).widget()
  330. if isinstance(widget, QWidget):
  331. for child in widget.findChildren(QPushButton):
  332. task_name = child.parent().findChild(QLabel).text()
  333. if task_name in self.running_tasks:
  334. child.setEnabled(False)
  335. child.setText("运行中")
  336. child.setStyleSheet("""
  337. QPushButton {
  338. background-color: #95a5a6;
  339. color: white;
  340. border-radius: 5px;
  341. }
  342. """)
  343. else:
  344. child.setEnabled(True)
  345. child.setText("执行")
  346. child.setStyleSheet("")
  347. def add_running_task(self, task_name, task_instance):
  348. """添加正在执行的任务到显示区域"""
  349. # 创建任务容器
  350. task_container = QWidget()
  351. task_layout = QHBoxLayout(task_container)
  352. task_layout.setContentsMargins(5, 5, 5, 5)
  353. # 创建任务标签
  354. task_label = QLabel(task_name)
  355. task_label.setFont(QFont("Arial", 12))
  356. task_label.setMinimumWidth(200)
  357. task_layout.addWidget(task_label)
  358. # 创建停止按钮
  359. stop_btn = QPushButton("停止")
  360. stop_btn.setMinimumWidth(100)
  361. stop_btn.clicked.connect(lambda: self.stop_task(task_name, task_container))
  362. task_layout.addWidget(stop_btn)
  363. # 添加到布局
  364. self.running_tasks_layout.addWidget(task_container)
  365. # 存储任务实例
  366. self.running_tasks[task_name] = task_instance
  367. def stop_task(self, task_name, task_container):
  368. """停止任务"""
  369. if task_name in self.running_tasks:
  370. try:
  371. # 停止状态监控线程
  372. if task_name in self.state_monitors:
  373. self.state_monitors[task_name].stop()
  374. del self.state_monitors[task_name]
  375. task_instance = self.running_tasks[task_name]
  376. task_instance.interrupt()
  377. del self.running_tasks[task_name]
  378. # 从界面中移除任务容器
  379. self.running_tasks_layout.removeWidget(task_container)
  380. task_container.deleteLater()
  381. # 更新可用任务区域的按钮状态
  382. for i in range(self.available_tasks_layout.count()):
  383. widget = self.available_tasks_layout.itemAt(i).widget()
  384. if isinstance(widget, QWidget):
  385. for child in widget.findChildren(QPushButton):
  386. if child.text() == "运行中":
  387. child.setEnabled(True)
  388. child.setText("执行")
  389. child.setStyleSheet("")
  390. break
  391. # 更新状态
  392. self.update_status(f"任务 {task_name} 已停止")
  393. except Exception as e:
  394. self.update_status(f"停止任务失败: {str(e)}")
  395. def update_status(self, message):
  396. """更新状态信息"""
  397. self.status_label.setText(message)
  398. # 根据消息类型设置不同的样式
  399. if "错误" in message or "失败" in message:
  400. color = "#e74c3c" # 红色
  401. elif "成功" in message:
  402. color = "#27ae60" # 绿色
  403. else:
  404. color = "#2980b9" # 蓝色
  405. self.status_label.setStyleSheet(f"color: {color};")
  406. def closeEvent(self, event):
  407. """窗口关闭事件"""
  408. # 停止所有任务
  409. self.stop_all_tasks()
  410. # 停止状态更新定时器
  411. self.status_update_timer.stop()
  412. # 接受关闭事件
  413. event.accept()
  414. if __name__ == "__main__":
  415. app = QApplication(sys.argv)
  416. window = PlayWindow()
  417. window.show()
  418. sys.exit(app.exec())