2025.2.24
今天对页面进行修改并且加上一些实际功能:具体页面如下:

代码如下:
import os import shutil import sys import cv2 import mediapipe as mp import numpy as np from datetime import datetime from PySide6.QtCore import Qt, QUrl, QTimer, QThread, Signal from PySide6.QtGui import QImage, QPixmap from PySide6.QtWidgets import ( QApplication, QMainWindow, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QComboBox, QLabel, QFileDialog, QFrame, QTextEdit, QInputDialog, QMessageBox ) from PySide6.QtMultimedia import QMediaPlayer from docx import Document # MediaPipe 初始化 mp_pose = mp.solutions.pose mp_drawing = mp.solutions.drawing_utils class VideoProcessor(QThread): frame_processed = Signal(QImage) finished = Signal() def __init__(self, video_path): super().__init__() self.video_path = video_path self._is_running = True self._is_paused = False self.cap = None def run(self): self.cap = cv2.VideoCapture(self.video_path) if not self.cap.isOpened(): print("Error: Could not open video.") self.finished.emit() return pose = mp_pose.Pose( min_detection_confidence=0.5, min_tracking_confidence=0.5 ) while self._is_running and self.cap.isOpened(): if not self._is_paused: ret, frame = self.cap.read() if not ret: break # 关键点检测 results = pose.process(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) if results.pose_landmarks: mp_drawing.draw_landmarks( frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS, mp_drawing.DrawingSpec(color=(245, 117, 66), thickness=2, circle_radius=2), # 关键点样式 mp_drawing.DrawingSpec(color=(245, 66, 230), thickness=2) # 连接线样式 ) # 转换图像格式 rgb_image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch = rgb_image.shape bytes_per_line = ch * w qt_image = QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format.Format_RGB888) self.frame_processed.emit(qt_image) self.cap.release() self.finished.emit() def stop(self): self._is_running = False if self.cap: self.cap.release() def toggle_pause(self): self._is_paused = not self._is_paused class VideoPlayer(QLabel): def __init__(self): super().__init__() self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setStyleSheet("background-color: black;") self.processor = None def openFile(self, filename): if self.processor: self.processor.stop() self.processor.wait() # 确保线程完全停止 self.processor = VideoProcessor(filename) self.processor.frame_processed.connect(self.update_frame) self.processor.finished.connect(self.on_processing_finished) self.processor.start() def update_frame(self, image): self.setPixmap(QPixmap.fromImage(image).scaled( self.width(), self.height(), Qt.AspectRatioMode.KeepAspectRatio )) def playPause(self): if self.processor: self.processor.toggle_pause() def stop(self): if self.processor: self.processor.stop() self.processor.wait() # 确保线程完全停止 def on_processing_finished(self): self.setPixmap(QPixmap()) # 清空播放区域 class MainApp(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("人机交互界面") self.setGeometry(100, 100, 1700, 880) self.video_folder = "videos" self.selected_video = None # 检查并创建视频目录 if not os.path.exists(self.video_folder): os.makedirs(self.video_folder) self.initUI() # 设置背景渐变 self.setStyleSheet(""" QMainWindow { background: linear-gradient(to bottom, #4343FF, #ADADFF); background-repeat: no-repeat; background-position: center; background-size: cover; } """) def initUI(self): main_widget = QWidget() main_layout = QVBoxLayout() # 标题栏 title_label = QLabel("乒乓球运动预测以及指导建议") title_label.setAlignment(Qt.AlignmentFlag.AlignCenter) title_label.setStyleSheet("font-size: 30px; font-weight: bold; margin: 15px; color: black;") # 顶部布局 top_layout = QHBoxLayout() top_layout.addWidget(title_label) top_layout.setContentsMargins(0, 0, 0, 0) # 左侧整体布局 left_layout = QVBoxLayout() left_layout.setContentsMargins(5, 5, 5, 5) left_layout.setSpacing(10) # 中间视频播放区域 self.video_player = VideoPlayer() # 时间显示 self.time_label = QLabel() self.time_label.setAlignment(Qt.AlignmentFlag.AlignCenter) self.update_time() timer = QTimer(self) timer.timeout.connect(self.update_time) timer.start(1000) left_layout.addWidget(self.time_label) # 控制按钮行 control_layout = QHBoxLayout() btn_import = QPushButton("导入视频") btn_import.setStyleSheet(""" QPushButton { background-color: #00d4ff; color: white; border-radius: 10px; padding: 10px 20px; font-size: 16px; } QPushButton:hover { background-color: #0056b3; } QPushButton:pressed { background-color: #003d7a; } """) btn_import.clicked.connect(self.import_perspective) self.btn_pause = QPushButton("暂停/继续") self.btn_pause.setStyleSheet(btn_import.styleSheet()) self.btn_pause.clicked.connect(self.video_player.playPause) btn_detect = QPushButton("检测当前帧") btn_detect.setStyleSheet(btn_import.styleSheet()) control_layout.addWidget(btn_import) control_layout.addWidget(self.btn_pause) control_layout.addWidget(btn_detect) left_layout.addLayout(control_layout) # 视频选择下拉框 self.combo_videos = QComboBox() self.combo_videos.currentIndexChanged.connect(self.load_video) self.populate_video_list() left_layout.addWidget(QLabel("选择视角:")) left_layout.addWidget(self.combo_videos) # 数据展示框 data_frame = QFrame() data_frame.setStyleSheet(""" background-color: rgba(192,191,192,100); border-radius: 10px; padding: 15px; color: black; """) data_layout = QVBoxLayout() data_layout.addWidget(QLabel("检测结果", alignment=Qt.AlignmentFlag.AlignCenter)) data_layout.addWidget(QLabel("X坐标: 0")) data_layout.addWidget(QLabel("Y坐标: 0")) data_layout.addWidget(QLabel("球拍倾斜角度: 0°")) data_layout.addWidget(QLabel("置信度: 0.0")) data_layout.addWidget(QLabel("击打成功几率: 0.0")) data_frame.setLayout(data_layout) left_layout.addWidget(data_frame) # 建议框和报告按钮 self.txt_suggestions = QTextEdit() self.txt_suggestions.setPlaceholderText("分析建议将显示在这里...") self.txt_suggestions.setStyleSheet(""" background-color: rgba(255,255,255,180); border: 1px solid #ccc; padding: 10px; border-radius: 5px; """) left_layout.addWidget(QLabel("分析建议:")) left_layout.addWidget(self.txt_suggestions) btn_report = QPushButton("生成分析报告") btn_report.setStyleSheet(btn_import.styleSheet()) btn_report.clicked.connect(self.generate_report) left_layout.addWidget(btn_report) # 右侧视图布局 right_layout = QVBoxLayout() right_layout.setSpacing(10) side_view = QLabel("轨迹侧视图") side_view.setStyleSheet(""" background-color: rgba(255,255,255,180); border-radius: 10px; border: 1px solid #ccc; min-height: 300px; """) top_view = QLabel("轨迹俯视图") top_view.setStyleSheet(side_view.styleSheet()) right_layout.addWidget(side_view) right_layout.addWidget(top_view) # 主界面布局 main_content = QHBoxLayout() main_content.addLayout(left_layout, 25) main_content.addWidget(self.video_player, 45) main_content.addLayout(right_layout, 30) main_layout.addLayout(top_layout) main_layout.addLayout(main_content) main_widget.setLayout(main_layout) self.setCentralWidget(main_widget) def update_time(self): current_date = datetime.now().strftime("%Y-%m-%d") day_of_week = self.get_day_of_week() self.time_label.setText(f"{current_date}\n星期{day_of_week}") def get_day_of_week(self): day_map = ["一", "二", "三", "四", "五", "六", "日"] return day_map[datetime.now().weekday()] def import_perspective(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择视频文件", "", "视频文件 (*.mp4 *.avi *.mov)") if file_path: name, ok = QInputDialog.getText( self, "输入视角名称", "请为此视角命名:") if ok and name: ext = os.path.splitext(file_path)[1] save_path = os.path.join(self.video_folder, f"{name}{ext}") shutil.copy(file_path, save_path) self.populate_video_list() def populate_video_list(self): self.combo_videos.clear() videos = [f for f in os.listdir(self.video_folder) if f.endswith(('.mp4', '.avi', '.mov'))] self.combo_videos.addItems(videos) def load_video(self): if self.combo_videos.count() > 0: video_name = self.combo_videos.currentText() video_path = os.path.join(self.video_folder, video_name) self.video_player.openFile(video_path) def generate_report(self): doc = Document() doc.add_heading('运动分析报告', 0) doc.add_paragraph(f"生成时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") data = [ ("视频文件", self.combo_videos.currentText()), ("X坐标", "0"), ("Y坐标", "0"), ("球拍角度", "0°"), ("置信度", "0.0"), ("击打成功几率", "0.0"), ("分析建议", self.txt_suggestions.toPlainText()) ] for item in data: doc.add_paragraph(f"{item[0]}:{item[1]}") report_path = os.path.join(os.getcwd(), "analysis_report.docx") doc.save(report_path) QMessageBox.information( self, "报告生成成功", f"报告已保存至:\n{report_path}" ) if __name__ == "__main__": app = QApplication(sys.argv) window = MainApp() window.show() sys.exit(app.exec())
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号