pyqt5 最终使用的动作分割软件




# coding=UTF-8
"""
theme: pyqt5实现动作起始帧和结束帧的定位,将定位到的帧数保存json文件
time:  2024-6-27
author: cong
"""
import json
import os
import re
import sys
from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent, QMediaPlaylist
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from functools import partial


# 使用 QMediaPlayer 可以进行视频文件解码,视频播放必须将视频帧在某个界面组件上显示,
# 有 QVideoWidget 和 QGraphicsVideoItem 两种视频显示组件,也可以从这两个类继承,自定义视频显示组件。
# QMediaPlayer 也可以结合 QMediaPlaylist 实现视频文件列表播放。


class VideoWin(QWidget):
    save_flag = 0
    save_start_count = 1
    save_end_count = 2
    len_load_json = 0


    def __init__(self):
        super(VideoWin, self).__init__()
        self.num_end = None
        self.setWindowTitle("MediaPlayer")
        # 播放画面
        self.player = QMediaPlayer()
        self.video_widget = QVideoWidget(self)  # 定义视频显示的widget,界面组件
        self.video_widget.setFixedSize(1280, 720)
        self.player.setVideoOutput(self.video_widget)  # 视频播放输出的widget,就是上面定义的
        self.video_path = ''
        # 当前播放的进度,显示调整视频进度条
        self.label_time = QLabel()
        self.timeSlider = QSlider()
        self.timeSlider.setOrientation(Qt.Horizontal)
        self.timeSlider.setValue(0)
        self.timeSlider.setMinimum(0)
        self.player.positionChanged.connect(self.get_time)
        self.timeSlider.sliderPressed.connect(self.player.pause)
        self.timeSlider.sliderMoved.connect(self.change_time)
        self.timeSlider.sliderReleased.connect(self.player.play)

        # 打开视频
        self.open_button = QPushButton('打开视频')
        self.open_button.clicked.connect(self.open_file)
        # 快进
        self.right_button = QPushButton('快进')
        self.right_button.clicked.connect(self.up_time)
        # play
        self.play_button = QPushButton('播放')
        self.play_button.clicked.connect(self.player.play)
        # pause
        self.mid_button = QPushButton('暂停')
        self.mid_button.clicked.connect(self.player.pause)
        # 快退
        self.left_button = QPushButton('快退')
        self.left_button.clicked.connect(self.down_time)
        # 保存开始时间
        self.start_button = QPushButton('保存动作开始时间')
        self.start_button.clicked.connect(self.save_start_time)
        # 保存结束时间
        self.end_button = QPushButton('保存动作结束时间')
        self.end_button.clicked.connect(self.save_end_time)

        # # 进度条跳转
        jump_layout = QVBoxLayout()
        for i in range(30):
            button = QPushButton(f'跳转 {i + 1}')
            button.setFixedSize(50, 21)
            jump_layout.addWidget(button)
            # 给每个按钮连接一个槽函数(示例中没有实际的槽函数)
            button.clicked.connect(partial(self.jump, i))

        # 加载json
        self.load_json_button = QPushButton('加载json')
        self.load_json_button.setFixedSize(80, 40)
        self.load_json_button.clicked.connect(self.load_json)

        # 所有时间选定,最终保存按钮
        self.done_button = QPushButton('完成并保存')
        self.done_button.setFixedSize(80, 40)
        self.done_button.clicked.connect(self.save_json)

        # label_names布局
        self.labels_names_entry = QLineEdit()
        labels_names =['一道', '七道', '三道', '九道', '二道', '五道', '停车信号', '八道', '六道', '减速信号',
                       '十一道', '十三道', '十二道', '十五道', '四道', '指挥机车向显示人反方向去的信号', '指挥机车向显示人方向来的信号',
                       '指挥机车向显示人方向稍行移动的信号', '道岔开通信号']

        self.labels_names_entry.setText(str(labels_names))
        # 视频路径 entry 布局

        self.path_entry = QLineEdit()

        # 创建一个网格布局
        grid_layout = QGridLayout()
        self.grid_num = 90
        self.entry_names = [f'entry_{i + 1}' for i in range(self.grid_num)]
        for i in range(self.grid_num):
            self.entry_names[i] = QLineEdit(self)
            self.entry_names[i].setFixedWidth(60)
            if i % 3 == 0:
                label = QLabel(f"start_time_d{int((i + 3) // 3)}:")
                grid_layout.addWidget(label, i // 3, (i % 3) * 3)
                grid_layout.addWidget(self.entry_names[i], i // 3, (i % 3) * 3 + 1)

            elif i % 3 == 1:
                label = QLabel(f"end_time_d{int((i + 2) // 3)}:")
                grid_layout.addWidget(label, i // 3, (i % 3) + 1)
                grid_layout.addWidget(self.entry_names[i], i // 3, (i % 3) + 2)
            else:
                label = QLabel(f"label_name_d{int((i + 1) // 3)}:")
                grid_layout.addWidget(label, i // 3, (i % 3) + 2)
                grid_layout.addWidget(self.entry_names[i], i // 3, (i % 3) + 3)
                # label.setFixedSize(60,20)
            # self.entry_names[i].setFixedWidth(50)
            # grid_layout.addWidget(label, i // 3, (i % 3)*3)
            # grid_layout.addWidget(self.entry_names[i], i // 3,(i % 3)*3+1)
        # 上述按钮布局
        button_layout = QHBoxLayout()
        button_layout.addWidget(self.open_button)
        button_layout.addWidget(self.right_button)
        button_layout.addWidget(self.play_button)
        button_layout.addWidget(self.mid_button)
        button_layout.addWidget(self.left_button)
        button_layout.addWidget(self.start_button)
        button_layout.addWidget(self.end_button)

        # 左侧布局
        left_layout = QVBoxLayout()
        left_layout.addWidget(self.video_widget)
        left_layout.addWidget(self.label_time, alignment=Qt.AlignRight)

        left_layout.addWidget(self.timeSlider)

        left_layout.addLayout(button_layout)

        # left_layout.addSpacing(100)
        left_layout.addWidget(QLabel("动作标签:"))
        left_layout.addWidget(self.labels_names_entry)
        left_layout.addWidget(QLabel("视频路径:"))
        left_layout.addWidget(self.path_entry)
        json_layout = QHBoxLayout()
        json_layout.addWidget(self.load_json_button)

        json_layout.addWidget(QLabel("路径:"))
        self.json_path_entry = QLineEdit()
        json_layout.addWidget(self.json_path_entry)

        left_layout.addLayout(json_layout)
        # 中间布局
        middle_layout = QHBoxLayout()
        middle_layout.addLayout(grid_layout)
        middle_layout.addLayout(jump_layout)
        # 最右侧布局
        right_layout = QVBoxLayout()


        right_layout.addWidget(self.done_button)

        # 总布局
        all_layout = QHBoxLayout()
        all_layout.addLayout(left_layout)
        all_layout.addLayout(middle_layout)
        all_layout.addLayout(right_layout)
        self.setLayout(all_layout)
        self.showMaximized()

        # 设置播放进度检查定时器
        self.position_check_timer = QTimer(self)
        self.position_check_timer.setInterval(10)  # 设置定时器间隔为100毫秒





    # 打开视频
    def open_file(self):
        video_extensions = ['.mp4', '.avi', '.mov', '.mkv', '.wmv', '.flv', '.webm']
        a = QFileDialog.getOpenFileUrl()[0]
        if a.toString() == '' or not any(a.toString().endswith(ext) for ext in video_extensions):
            QMessageBox.information(self, '提示', "请打开符合格式的视频文件")

            return
        self.video_path = re.split('file:///', a.toString())[1]
        self.player.setMedia(QMediaContent(a))  # 选取视频文件
        msg = QMessageBox.information(self, '提示', "已经打开视频文件")
        self.path_entry.setText(self.video_path)
        # 自动调用json文件
        file_path = re.split('/', self.video_path)[-1]
        json_path_name = file_path.replace(file_path.split('.')[-1], 'json')
        json_names = os.listdir('json_output')
        if json_path_name in json_names:
            json_path = os.path.join('json_output', json_path_name)
            self.json_path_entry.setText(json_path)
            with open(json_path, encoding='utf-8') as f:
                data = json.load(f)
                # print('data', data)
                # self.path_entry.setText(data['video_path'])
                split_result = data['split_result']
                for i in range(len(split_result)):
                    self.entry_names[3 * i].setText(str(split_result[i]['beginTime']))
                    self.entry_names[3 * i + 1].setText(str(split_result[i]['endTime']))
                    self.entry_names[3 * i + 2].setText(str(split_result[i]['label']))
            self.len_load_json = len(split_result)
            self.save_start_count_load = self.len_load_json
            self.save_end_count_load = self.len_load_json
            QMessageBox.information(self, '提示', "已经自动加载json文件")
        else:
            QMessageBox.information(self, '提示', "未找到对应json文件")




    # 调节播放进度
    def change_time(self, num):
        self.player.setPosition(num)

    # 快进
    def up_time(self):
        # print(self.player.duration())
        # num = self.player.position() + int(self.player.duration() / 20)
        num = self.player.position() + 200
        self.player.setPosition(num)

    # 快退
    def down_time(self):
        # num = self.player.position() - int(self.player.duration() / 20)
        num = self.player.position() - 200
        self.player.setPosition(num)

    # 获取进度条进度
    def get_time(self, num):
        self.timeSlider.setMaximum(self.player.duration())
        self.timeSlider.setValue(num)
        # frame_count = int(num / 1000 * 30)
        # d = QDateTime.fromMSecsSinceEpoch(num).toString("mm:ss")
        # print(d)
        all = self.player.duration()
        # total_count = int(all / 1000 * 30)
        # all_d = QDateTime.fromMSecsSinceEpoch(all).toString("mm:ss")
        self.label_time.setText(str(self.player.position()) + '/' + str(all))

    def jump(self, jump_button_index):
        # print(jump_button_index)
        num_start = int(self.entry_names[jump_button_index * 3].text())
        self.num_end = int(self.entry_names[jump_button_index * 3 + 1].text())
        if num_start == '' or self.num_end == '':
            QMessageBox.information(self, "提示", f"请先保存动作时间")
            return

        self.position_check_timer.stop()

        self.position_check_timer.timeout.connect(self.check_position)
        # 停止播放进度检查定时器(如果之前有启动的话)

        # 设置播放起始位置
        self.player.setPosition(num_start)  # 转换为毫秒
        # 启动播放进度检查定时器
        self.position_check_timer.start()

        # 开始播放
        self.player.play()



    def check_position(self):
        # 获取当前播放的位置(毫秒)
        current_position = self.player.position()
        print('current_position', current_position)
        # 如果达到设置的结束时间,停止播放
        if current_position >= self.num_end:
            self.player.pause()
            self.position_check_timer.stop()

    def closeEvent(self, event):  # 关闭前需要self.player.pause()操作,否则报错
        self.player.pause()
        reply = QMessageBox.question(self, '提示',
                                     "是否退出",
                                     QMessageBox.Yes | QMessageBox.No,
                                     QMessageBox.No)
        if reply == QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

    def save_start_time(self):
        if self.save_flag == 0:
            self.save_flag = 1
            start_time = self.player.position()
            # print('start time:', start_time)
            # start_frame = int(start_time / 1000 * 30)
            if self.len_load_json != 0:
                self.entry_names[self.save_start_count_load * 3].setText(str(start_time))
                self.save_start_count_load += 1
            else:
                self.entry_names[self.save_start_count - 1].setText(str(start_time))
                self.save_start_count += 3
            # QMessageBox.information(self, "保存成功", f"已保存当前时间点:第{start_frame}帧 ")

        else:
            QMessageBox.information(self, "保存失败", f"请先保存动作结束时间 ")

    def save_end_time(self):
        if self.save_flag == 1:
            self.save_flag = 0
            end_time = self.player.position()
            # end_frame = int(end_time / 1000 * 30)
            if self.len_load_json != 0:
                self.entry_names[self.save_end_count_load * 3 + 1].setText(str(end_time))
                self.save_end_count_load += 1

            else:
                self.entry_names[self.save_end_count - 1].setText(str(end_time))
                self.save_end_count += 3
            # QMessageBox.information(self, "保存成功", f"已保存当前时间点:第{end_frame}帧 ")
        else:
            QMessageBox.information(self, "保存失败", f"请先保存动作开始时间 ")

    def load_json(self):

        os.makedirs('json_output', exist_ok=True)
        # 默认打开'json_output'
        json_path = QFileDialog.getOpenFileName(self, '打开json文件', 'json_output', 'json文件(*.json)')
        if json_path[0] == '':
            return
        print(json_path)
        self.json_path = os.path.basename(json_path[0])
        self.json_path = os.path.join('json_output', self.json_path)
        if not self.json_path.endswith('.json'):
            QMessageBox.information(self, "加载失败", f"请先打开json文件")
            return
        self.json_path_entry.setText(self.json_path)
        with open(self.json_path, encoding='utf-8') as f:
            data = json.load(f)
            print('data', data)
            # self.path_entry.setText(data['video_path'])
            split_result = data['split_result']
            for i in range(len(split_result)):
                self.entry_names[3*i].setText(str(split_result[i]['beginTime']))
                self.entry_names[3*i+1].setText(str(split_result[i]['endTime']))
                self.entry_names[3*i+2].setText(str(split_result[i]['label']))
            # self.len_load_json = len(split_result)
            # print('len_load_json', self.len_load_json)



    def save_json(self):
        result = {}
        single_part = {}
        if self.video_path == '':
            QMessageBox.information(self, "保存失败", f"请先打开视频文件")
            return
        video_path = self.video_path
        print('当前保存结果来源于视频文件', video_path)
        result['video_path'] = video_path
        result['split_result'] = []
        file_path = re.split('/', video_path)[-1]
        # print('file_path', file_path)
        # json_path_name = file_path.split('.mkv')[0] + '.json'
        json_path_name = file_path.replace(file_path.split('.')[-1], 'json')
        json_path_name = os.path.join('json_output', os.path.basename(json_path_name))
        os.makedirs('json_output', exist_ok=True)
        for i in range(len(self.entry_names)):
            if self.entry_names[i].text() == '':
                continue
            if i % 3 == 0:
                label_key = f"beginTime"
                single_part[label_key] = int(self.entry_names[i].text())
                # print('ssssssssssssssssss')

            elif i % 3 == 1:
                label_key = f"endTime"
                single_part[label_key] = int(self.entry_names[i].text())
                # print('########################')
            else:
                # print('labels_names:',self.labels_names_entry.text())
                if self.entry_names[i].text() not in self.labels_names_entry.text():
                    QMessageBox.information(self, "保存失败", f"标签名字不在标签列表中")
                    break
                else:
                    label_key = f"label"
                    single_part[label_key] = self.entry_names[i].text()
                    result['split_result'].append(single_part)
                    single_part = {}
            # print('single_part:', single_part)

        if len(single_part) % 3 != 0:
            QMessageBox.information(self, "保存失败", f"请检查是否有漏缺,数据长度不对")

        with open(json_path_name, 'w', encoding='utf-8') as f:
            json.dump(result, f, ensure_ascii=False, indent=4)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.aboutToQuit.connect(app.deleteLater)
    win = VideoWin()
    win.show()
    sys.exit(app.exec())














 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值