Commit 5cf7cc82 authored by xuanweiace's avatar xuanweiace

add 新增 撤销与重做 字幕旁白集中管理 状态栏进度条展示 等功能

parent 69305eb1
class Content:
ActivateColumn = 2
# ColumnCount = 3
class Aside:
AsideColumnNumber = 4
......@@ -2,13 +2,14 @@ import time
import os
import cv2
from PyQt5.QtWidgets import QMainWindow, QFileDialog, QTableWidget, QTableWidgetItem
from PyQt5.QtWidgets import QMainWindow, QFileDialog, QTableWidget, QTableWidgetItem, QAbstractItemView, QProgressBar, QLabel
from PyQt5.QtCore import QUrl, Qt, QTimer, QRect
import utils
from utils import validate_and_get_filepath
from management import RunThread, ProjectContext, Element
from detect_dialog import Detect_Dialog
from assemble_dialog import Assemble_Dialog
from prompt_dialog import Prompt_Dialog
from synthesis import SynthesisProcessor
from myVideoWidget import myVideoWidget
from myvideoslider import myVideoSlider
......@@ -16,6 +17,7 @@ from PyQt5 import QtWidgets
from main_window_ui import Ui_MainWindow
from PyQt5.QtMultimedia import *
import time
import constant
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self):
super(MainWindow, self).__init__()
......@@ -27,20 +29,65 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# todo:后续改成QThread的组件
self.synthesis = SynthesisProcessor()
self.synthesis.show_warning_signal.connect(self.show_warning_msg_box)
self.synthesis.synthesis_callback_signal.connect(self.deal_synthesis_callback_slot)
#检测对话框
self.detect_dialog = Detect_Dialog()
self.detect_dialog.start_detect_signal.connect(self.start_detect)
#合成对话框
self.assemble_dialog = Assemble_Dialog()
self.assemble_dialog.start_assemble_signal.connect(self.synthesis.synthesis_slot)
#提示框
self.prompt_dialog = Prompt_Dialog()
#所有QTimer集中管理
self.detect_timer = QTimer()
self.detect_timer.timeout.connect(self.check_if_detect_over_slot)
self.synthesis_timer = QTimer()
self.synthesis_timer.timeout.connect(self.check_if_synthesis_over_slot)
"""
状态栏相关空间
"""
self.statusbarButton = QtWidgets.QPushButton(self)
# 设置按钮对象名称(不是按钮显示内容)
self.statusbarButton.setObjectName("pred")
# 设置按钮位置
self.statusbarButton.setGeometry(QRect(10, 300, 93, 28))
# 设置按钮显示内容
self.statusbarButton.setText("敬请期待")
self.statusbarButton.setEnabled(False)
self.statusbarLabel = QLabel()
self.statusbarLabel.setText(" 休息中")
#定义水平进度条
self.progressBar = QProgressBar()
# 设置进度条的范围,参数1为最小值,参数2为最大值(可以调得更大,比如1000
self.progressBar.setRange(0, 100)
# 设置进度条的初始值
self.progressBar.setValue(0)
# 菜单栏的动作
self.actiona_3.triggered.connect(self.show_detect_dialog)
self.actiona_4.triggered.connect(self.show_assemble_dialog)
self.action_save.triggered.connect(self.save_project)
self.import_movie.triggered.connect(self.import_slot)
self.action_open_project.triggered.connect(self.open_project_slot)
self.action_undo.triggered.connect(self.undo_slot)
self.action_redo.triggered.connect(self.redo_slot)
self.action_view_history.triggered.connect(self.view_history_slot)
# 状态栏的动作
self.statusbar.addPermanentWidget(self.statusbarButton, stretch=0)
self.statusbar.addPermanentWidget(self.statusbarLabel, stretch=2)
self.statusbar.addPermanentWidget(self.progressBar, stretch=10)
#视频时长,全局变量
self.video_duration = None
......@@ -62,6 +109,18 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.sld_video.ClickedValue.connect(self.clickedSlider) # 进度条点击跳转
self.sld_audio.valueChanged.connect(self.volumeChange) # 控制声音播放
# 表格双击和发生change时的处理
self.zm_tableWidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
self.pb_tableWidget.itemDoubleClicked.connect(self.writeHistory)
self.pb_tableWidget.itemChanged.connect(self.rewriteHistory)
self.pb_tableWidget.itemChanged.connect(self.write2ProjectFromAside)
self.zm_tableWidget.itemDoubleClicked.connect(self.change_video_time)
# self.all_tableWidget.itemDoubleClicked.connect(self.writeHistoryFromContent)
# self.all_tableWidget.itemChanged.connect(self.rewriteHistoryFromContent)
# self.all_tableWidget.itemChanged.connect(self.write2ProjectFromContent)
# 重写关闭Mmainwindow窗口
def closeEvent(self, event):
......@@ -81,8 +140,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
QtWidgets.QMessageBox.Yes)
def import_slot(self):
video_path = self.openVideoFile()
self.init_ProjectContext_VideoPath(video_path)
video_path = self.openVideoFile().path()
print("[import_slot] video_path=" + video_path)
self.projectContext.Init(os.path.dirname(video_path), os.path.basename(video_path))
self.statusbar.showMessage("工程路径为:" + video_path)
# todo: 后续这段代码公共的可以抽出来
def open_project_slot(self):
video_path = self.openVideoFile().path()
print("[import_slot] video_path=" + video_path)
print(os.path.dirname(video_path))
print(os.path.basename(video_path))
self.projectContext.Init(os.path.dirname(video_path), os.path.basename(video_path))
self.statusbar.showMessage("工程路径为:" + os.path.dirname(video_path))
self.open_excel_with_project_path()
def start_detect(self, video_path, book_path):
......@@ -157,7 +229,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# 多线程同步进行检测和进度条更新
state = [None]
threads = []
self.state = state
self.threads = []
#todo:状态栏
# t = RunThread(funcName=start_process, args=(
# progressbar_1, progress_1, state, 300000), name="startProgress1")
......@@ -171,46 +244,67 @@ class MainWindow(QMainWindow, Ui_MainWindow):
book_path, state, 1),
name="detect")
t.setDaemon(True)
threads.append(t)
self.threads.append(t)
for t in threads:
for t in self.threads:
t.start()
print("===子线程已经开启 in detect ===")
self.statusbarLabel.setText(" 准备检测:")
self.detect_timer.start(5000)
# 线程完成任务后结束线程,一旦有一个线程结束就判断是否是意外中断
while 1:
# while 1:
# alive = True
# for t in threads:
# alive = alive and t.is_alive()
# if not alive:
# break
# time.sleep(5)
#
# print("===已有线程结束了 in detect ===")
# for t in self.threads:
# if t.exitcode != 0:
# print("Exception in", t.getName())
# self.show_warning_msg_box("运行出错,请联系开发者处理")
# # processState.set("任务中断")
# # progress_state = progressbar_1['value']
# # progressbar_1.stop()
# # progressbar_1['value'] = progress_state
# # stopDetection.config(state=tk.DISABLED)
# # startDetection.config(state=tk.ACTIVE)
# return
def check_if_detect_over_slot(self):
self.check_if_over("检测")
def check_if_synthesis_over_slot(self):
self.check_if_over("合成")
# type = 检测 或 合成
def check_if_over(self, type):
alive = True
for t in threads:
print(self.state)
if self.state != [None]:
self.statusbarLabel.setText(" 正在%s:"%(type))
self.progressBar.setValue(int(self.state[-1]*100))
for t in self.threads:
alive = alive and t.is_alive()
if not alive:
break
time.sleep(5)
self.detect_timer.stop()
print("===已有线程结束了 in detect ===")
for t in threads:
self.statusbarLabel.setText(" %s完成"%(type))
self.progressBar.setValue(0)
for t in self.threads:
if t.exitcode != 0:
print("Exception in", t.getName())
self.show_warning_msg_box("运行出错,请联系开发者处理")
# processState.set("任务中断")
# progress_state = progressbar_1['value']
# progressbar_1.stop()
# progressbar_1['value'] = progress_state
# stopDetection.config(state=tk.DISABLED)
# startDetection.config(state=tk.ACTIVE)
return
# 若不是意外中断,则将进度条的进度拉满到100%,并给出“任务已完成”的提示
# processState.set("任务已完成")
# progressbar_1.stop()
# progressbar_1['value'] = 100.0
# progress_1['text'] = "100.0%"
# # print("end at time: ", datetime.datetime.now())
# progressbar_1.grid_forget()
# progress_1.grid_forget()
# # 检测完成后,将“停止检测”按钮设置为不可点击状态,“开始检测”按钮设置为可点击状态
# stopDetection.config(state=tk.DISABLED)
# startDetection.config(state=tk.ACTIVE)
def deal_synthesis_callback_slot(self, threads, state):
self.statusbarLabel.setText(" 准备合成:")
self.state = state
self.threads = threads
self.detect_timer.start(5000)
"""
......@@ -262,7 +356,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
path = QFileDialog.getOpenFileUrl()[0]
self.player.setMedia(QMediaContent(path)) # 选取视频文件
self.player.play() # 播放视频
print(self.player.availableMetaData())
print("availableMetaData:" , self.player.availableMetaData())
return path
# 在初始化工程时
......@@ -304,11 +398,20 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.projectContext.load_excel_from_path()
self.set_table_to_window()
def open_excel_with_project_path(self):
self.projectContext.load_excel_from_path()
self.set_table_to_window()
def set_table_to_window(self):
header = self.projectContext.header
subtitle_list = self.projectContext.subtitle_list
aside_list = self.projectContext.aside_list
print("sublist", subtitle_list)
print("aside_list", aside_list)
all_elements = self.projectContext.all_elements
contentHeader = self.projectContext.contentHeader
self.zm_tableWidget.clear()
self.pb_tableWidget.clear()
self.zm_tableWidget.setRowCount(len(subtitle_list))
self.zm_tableWidget.setColumnCount(len(header))
self.zm_tableWidget.setHorizontalHeaderLabels(header)
......@@ -322,10 +425,163 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.setElememtToTable(self.pb_tableWidget, aside_list[i], i)
self.all_tableWidget.clear()
self.all_tableWidget.setRowCount(len(all_elements))
self.all_tableWidget.setColumnCount(len(contentHeader))
self.all_tableWidget.setHorizontalHeaderLabels(contentHeader)
for i in range(len(all_elements)):
self.setElememtToTable(self.all_tableWidget, all_elements[i], i)
def setElememtToTable(self, table: QTableWidget, elem: Element, idx: int):
elem_list = elem.to_list()
if table.objectName() == "all_tableWidget":
elem_list = elem.to_short_list()
for j in range(len(elem_list)):
table.setItem(idx, j, QTableWidgetItem(elem_list[j]))
item = QTableWidgetItem(elem_list[j])
# 设置为不可编辑
if table.objectName() == "all_tableWidget" and j != constant.Content.ActivateColumn:
item.setFlags(Qt.ItemIsEnabled)
table.setItem(idx, j, item)
def save_project(self):
self.projectContext.save_project()
err_info = self.projectContext.save_project()
if err_info == None:
self.prompt_dialog.show_dialog_signal.emit("保存工程成功")
else:
self.prompt_dialog.show_dialog_signal.emit("保存工程失败!\n错误为:" + err_info)
def change_video_time(self, item):
if item is None:
print("WARNING!!!")
return
row = item.row() # 获取行数
col = item.column() # 获取列数
print("row, col = %s, %s"%(row, col))
text = item.text() # 获取内容
self.player.setPosition(int(float(text)*1000))
def writeHistory(self, item):
print("writeHistory")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
if col == constant.Aside.AsideColumnNumber:
self.projectContext.history_push(row, text, text)
def writeHistoryFromContent(self, item):
print("writeHistoryFromContent")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
if col == constant.Content.ActivateColumn:
self.projectContext.history_push(row, text, text)
def rewriteHistory(self, item):
print("re writeHistory")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
opt = self.projectContext.history_pop()
if opt == None: # 刚打开表格的时候,会触发这个槽函数,此时opt肯定是None
return
if col != constant.Aside.AsideColumnNumber:
return
# 抛出一个可能的异常
if row != opt.row:
print("[rewriteHistory] Warning!!!row=",row,", old_row=", opt.row, ", [row != opt.row]=", row != opt.row)
else:
self.projectContext.history_push(opt.row, opt.old_str, text)
def rewriteHistoryFromContent(self, item):
print("re rewriteHistoryFromContent")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
opt = self.projectContext.history_pop()
if opt == None: # 刚打开表格的时候,会触发这个槽函数,此时opt肯定是None
return
# 抛出一个可能的异常
if row != opt.row:
print("Warning!!!row=",row,", old_row=", opt.row)
if col == constant.Content.ActivateColumn:
self.projectContext.history_push(opt.row, opt.old_str, text)
def write2ProjectFromAside(self, item):
print("write2ProjectFromAside")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
if col != constant.Aside.AsideColumnNumber:
return
self.projectContext.refresh_aside(row, text)
def write2ProjectFromContent(self, item):
print("write2ProjectFromContent")
if item is None:
print("WRONG!!!! item Is None")
return
else:
row = item.row() # 获取行数
col = item.column() # 获取列数 注意是column而不是col哦
text = item.text() # 获取内容
if col == constant.Content.ActivateColumn:
self.projectContext.refresh_element(row, text)
def undo_slot(self):
record = self.projectContext.history_pop()
print('[undo_slot] record=%s'%(record.to_string()))
item = QTableWidgetItem(record.old_str)
row = int(record.row)
self.projectContext.aside_list[row].aside = record.old_str
self.pb_tableWidget.setItem(row, constant.Aside.AsideColumnNumber, item)
def redo_slot(self):
record = self.projectContext.history_redo()
item = QTableWidgetItem(record.new_str)
row = int(record.row)
self.projectContext.aside_list[row].aside = record.new_str
self.pb_tableWidget.setItem(row, constant.Aside.AsideColumnNumber, item)
def view_history_slot(self):
print("=="*10)
print("records_pos:", self.projectContext.records_pos)
print("history is below:")
for i in range(len(self.projectContext.history_records)):
print("pos:%d, history:%s"%(i, self.projectContext.history_records[i].to_string()))
print("=="*10)
\ No newline at end of file
......@@ -263,6 +263,9 @@ QPushButton:pressed {
</item>
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="contextMenuPolicy">
<enum>Qt::DefaultContextMenu</enum>
</property>
......@@ -276,7 +279,7 @@ QPushButton:pressed {
<enum>QTabWidget::Triangular</enum>
</property>
<property name="currentIndex">
<number>0</number>
<number>2</number>
</property>
<property name="iconSize">
<size>
......@@ -293,7 +296,23 @@ QPushButton:pressed {
<property name="tabBarAutoHide">
<bool>false</bool>
</property>
<widget class="QWidget" name="all_tab">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>字幕旁白</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTableWidget" name="all_tableWidget"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="zm_tab">
<property name="enabled">
<bool>true</bool>
</property>
<attribute name="title">
<string>字幕</string>
</attribute>
......@@ -463,7 +482,7 @@ QPushButton:pressed {
<string>文件</string>
</property>
<addaction name="actionxinjian"/>
<addaction name="actiona"/>
<addaction name="action_open_project"/>
<addaction name="separator"/>
<addaction name="import_movie"/>
<addaction name="actions"/>
......@@ -474,8 +493,9 @@ QPushButton:pressed {
<property name="title">
<string>编辑</string>
</property>
<addaction name="actionchexiao"/>
<addaction name="actionchongzuo"/>
<addaction name="action_undo"/>
<addaction name="action_redo"/>
<addaction name="action_view_history"/>
</widget>
<widget class="QMenu" name="menu_3">
<property name="title">
......@@ -494,7 +514,7 @@ QPushButton:pressed {
<string>新建</string>
</property>
</action>
<action name="actiona">
<action name="action_open_project">
<property name="text">
<string>打开</string>
</property>
......@@ -514,12 +534,12 @@ QPushButton:pressed {
<string>工程保存</string>
</property>
</action>
<action name="actionchexiao">
<action name="action_undo">
<property name="text">
<string>撤销</string>
</property>
</action>
<action name="actionchongzuo">
<action name="action_redo">
<property name="text">
<string>重做</string>
</property>
......@@ -534,6 +554,11 @@ QPushButton:pressed {
<string>旁白音频合成</string>
</property>
</action>
<action name="action_view_history">
<property name="text">
<string>history</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
......
......@@ -143,6 +143,7 @@ class Ui_MainWindow(object):
self.verticalLayout_3.setStretch(0, 8)
self.shuiping.addWidget(self.verticalWidget_3)
self.tabWidget = QtWidgets.QTabWidget(self.centralwidget)
self.tabWidget.setEnabled(True)
self.tabWidget.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.tabWidget.setStyleSheet("")
self.tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
......@@ -152,7 +153,19 @@ class Ui_MainWindow(object):
self.tabWidget.setMovable(False)
self.tabWidget.setTabBarAutoHide(False)
self.tabWidget.setObjectName("tabWidget")
self.all_tab = QtWidgets.QWidget()
self.all_tab.setEnabled(True)
self.all_tab.setObjectName("all_tab")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.all_tab)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.all_tableWidget = QtWidgets.QTableWidget(self.all_tab)
self.all_tableWidget.setObjectName("all_tableWidget")
self.all_tableWidget.setColumnCount(0)
self.all_tableWidget.setRowCount(0)
self.horizontalLayout_4.addWidget(self.all_tableWidget)
self.tabWidget.addTab(self.all_tab, "")
self.zm_tab = QtWidgets.QWidget()
self.zm_tab.setEnabled(True)
self.zm_tab.setObjectName("zm_tab")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.zm_tab)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
......@@ -259,31 +272,34 @@ class Ui_MainWindow(object):
MainWindow.setStatusBar(self.statusbar)
self.actionxinjian = QtWidgets.QAction(MainWindow)
self.actionxinjian.setObjectName("actionxinjian")
self.actiona = QtWidgets.QAction(MainWindow)
self.actiona.setObjectName("actiona")
self.action_open_project = QtWidgets.QAction(MainWindow)
self.action_open_project.setObjectName("action_open_project")
self.import_movie = QtWidgets.QAction(MainWindow)
self.import_movie.setObjectName("import_movie")
self.actions = QtWidgets.QAction(MainWindow)
self.actions.setObjectName("actions")
self.action_save = QtWidgets.QAction(MainWindow)
self.action_save.setObjectName("action_save")
self.actionchexiao = QtWidgets.QAction(MainWindow)
self.actionchexiao.setObjectName("actionchexiao")
self.actionchongzuo = QtWidgets.QAction(MainWindow)
self.actionchongzuo.setObjectName("actionchongzuo")
self.action_undo = QtWidgets.QAction(MainWindow)
self.action_undo.setObjectName("action_undo")
self.action_redo = QtWidgets.QAction(MainWindow)
self.action_redo.setObjectName("action_redo")
self.actiona_3 = QtWidgets.QAction(MainWindow)
self.actiona_3.setObjectName("actiona_3")
self.actiona_4 = QtWidgets.QAction(MainWindow)
self.actiona_4.setObjectName("actiona_4")
self.action_view_history = QtWidgets.QAction(MainWindow)
self.action_view_history.setObjectName("action_view_history")
self.menu.addAction(self.actionxinjian)
self.menu.addAction(self.actiona)
self.menu.addAction(self.action_open_project)
self.menu.addSeparator()
self.menu.addAction(self.import_movie)
self.menu.addAction(self.actions)
self.menu.addSeparator()
self.menu.addAction(self.action_save)
self.menu_2.addAction(self.actionchexiao)
self.menu_2.addAction(self.actionchongzuo)
self.menu_2.addAction(self.action_undo)
self.menu_2.addAction(self.action_redo)
self.menu_2.addAction(self.action_view_history)
self.menu_3.addAction(self.actiona_3)
self.menu_3.addAction(self.actiona_4)
self.menubar.addAction(self.menu.menuAction())
......@@ -291,7 +307,7 @@ class Ui_MainWindow(object):
self.menubar.addAction(self.menu_3.menuAction())
self.retranslateUi(MainWindow)
self.tabWidget.setCurrentIndex(0)
self.tabWidget.setCurrentIndex(2)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
......@@ -302,6 +318,7 @@ class Ui_MainWindow(object):
self.btn_stop.setText(_translate("MainWindow", "暂停"))
self.lab_audio.setText(_translate("MainWindow", "volume:100%"))
self.label_2.setText(_translate("MainWindow", "00:00/12:34"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.all_tab), _translate("MainWindow", "字幕旁白"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.zm_tab), _translate("MainWindow", "字幕"))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.pb_tab), _translate("MainWindow", "旁白"))
self.zm_label.setText(_translate("MainWindow", "字幕时间轴"))
......@@ -311,14 +328,15 @@ class Ui_MainWindow(object):
self.menu_2.setTitle(_translate("MainWindow", "编辑"))
self.menu_3.setTitle(_translate("MainWindow", "功能按键"))
self.actionxinjian.setText(_translate("MainWindow", "新建"))
self.actiona.setText(_translate("MainWindow", "打开"))
self.action_open_project.setText(_translate("MainWindow", "打开"))
self.import_movie.setText(_translate("MainWindow", "视频导入"))
self.actions.setText(_translate("MainWindow", "内容导出"))
self.action_save.setText(_translate("MainWindow", "工程保存"))
self.actionchexiao.setText(_translate("MainWindow", "撤销"))
self.actionchongzuo.setText(_translate("MainWindow", "重做"))
self.action_undo.setText(_translate("MainWindow", "撤销"))
self.action_redo.setText(_translate("MainWindow", "重做"))
self.actiona_3.setText(_translate("MainWindow", "旁白区间检测"))
self.actiona_4.setText(_translate("MainWindow", "旁白音频合成"))
self.action_view_history.setText(_translate("MainWindow", "history"))
from myVideoWidget import myVideoWidget
......
......@@ -3,7 +3,7 @@ import threading
import traceback
import sys
from enum import Enum
import datetime
import openpyxl
from openpyxl.styles import PatternFill, Alignment
......@@ -63,11 +63,17 @@ class Operation(Enum):
# 维护一条历史记录
# 目前仅支持对旁白做修改
class OperateRecord:
def __init__(self, operation: Operation, old: str, new: str):
def __init__(self, row: int, operation: Operation, old: str, new: str):
self.row = row
self.operation = operation
self.old_str = old
self.new_str = new
def to_string(self)->str:
s = "{row=%d, opt=%s, oldStr=%s, newStr=%s}"%(self.row, str(self.operation), self.old_str, self.new_str)
return s
......@@ -79,7 +85,7 @@ class Element:
self.subtitle = subtitle
self.suggest = suggest
self.aside = aside
self.history_records = []
# 判断当前元素是否是字幕
def is_subtitle(self):
......@@ -91,6 +97,14 @@ class Element:
def to_list(self):
return [self.st_time_sec, self.ed_time_sec, self.subtitle, self.suggest, self.aside]
def to_short_list(self):
return [self.st_time_sec, self.subtitle, self.aside]
def print_self(self):
print("st_time_sec:",self.st_time_sec,"ed_time_sec:",self.ed_time_sec,
"subtitle:",self.subtitle,"suggest:",self.suggest, "aside", self.aside)
def equalTo(self, other)->bool:
return abs(self.st_time_sec - other.st_time_sec) < 0.1
class ProjectContext:
def __init__(self):
......@@ -99,30 +113,47 @@ class ProjectContext:
self.excel_path = None
self.subtitle_list = []
self.aside_list = []
self.all_elements = []
# 一些常量
self.header = ["起始时间", "终止时间", "字幕", '建议', '解说脚本']
self.contentHeader = ["起始时间", "字幕", "旁白"]
self.excel_sheet_name = "旁白插入位置建议"
self.history_records = []
self.records_pos = 0
def Init(self, project_dir, video_name):
self.project_base_dir = project_dir
self.video_path = os.path.join(project_dir, video_name)
# self.video_path = os.path.join(project_dir, video_name)
self.video_path = project_dir[1:] + "/" + video_name
self.excel_path = replace_path_suffix(self.video_path, ".xlsx")
def Init(self, project_dir, video_path, excel_path):
self.project_base_dir = project_dir
self.video_path = video_path
self.excel_path = excel_path
# def Init(self, project_dir, video_path, excel_path):
# self.project_base_dir = project_dir
# self.video_path = video_path
# self.excel_path = excel_path
def setVideoPath(self, video_path):
self.video_path = video_path
def setExcelPath(self, excel_path):
self.excel_path = excel_path
# 目前只是把excel保存到文件中
def save_project(self):
all_element = self.subtitle_list + self.aside_list
all_element.sort(lambda x, y: (x.st_time_sec < y.st_time_sec))
for element in all_element:
write_to_sheet(self.excel_path, self.excel_sheet_name, element.to_list)
# 目前只是把excel保存到文件中
# 先备份文件,再覆盖主文件
def save_project(self) -> str:
# all_element = sorted(all_element, key=lambda x: float(x.st_time_sec))
new_excel_path = replace_path_suffix(self.excel_path, datetime.datetime.now().strftime('%Y%m%d%H%M%S')+'.xlsx')
err_info = save_excel_to_to_path(self.all_elements, new_excel_path, self.header, self.excel_sheet_name)
if err_info != None:
return err_info
err_info = save_excel_to_to_path(self.all_elements, self.excel_path, self.header, self.excel_sheet_name)
if err_info != None:
return err_info
return None
def refresh_aside(self, row, aside: str)->None:
self.aside_list[int(row)].aside = aside
def refresh_element(self, row, aside: str):
self.all_elements[int(row)].aside = aside
# 加载整个工程,填充到ProjectContext上下文中
def load_project(self):
......@@ -130,14 +161,74 @@ class ProjectContext:
def load_excel_from_path(self):
d = read_sheet(self.excel_path)
self.all_elements = []
# todo:现在是只用None判断是否是字幕,后续是否也需要用""来?
for i in range(len(d["字幕"])):
st_time_sec, ed_time_sec, subtitle, suggest, aside = [d[x][i] for x in self.header]
# 当前条目是字幕
if d["字幕"][i] != None:
self.subtitle_list.append(Element(st_time_sec, ed_time_sec, subtitle, suggest, aside))
self.all_elements.append(self.subtitle_list[-1])
else:
if i == 0:
st_time_sec = "0.01"
else:
st_time_sec = "%.2f"%(float(d["终止时间"][i-1])+0.01)
if i == len(d["字幕"])-1:
ed_time_sec = "360000" # todo 默认最大时长是100h
else:
ed_time_sec = "%.2f"%(float(d["起始时间"][i + 1]) - 0.01)
self.aside_list.append(Element(st_time_sec, ed_time_sec, subtitle, suggest, aside))
self.all_elements.append(self.aside_list[-1])
print("[load_excel_from_path] ", end='')
self.all_elements[-1].print_self()
# 现在仅支持对修改操作的记录
def history_push(self, row, old, new):
if self.records_pos == len(self.history_records):
self.history_records.append(OperateRecord(row, Operation.Modify, old, new))
else:
self.history_records[self.records_pos] = OperateRecord(row, Operation.Modify, old, new)
self.records_pos += 1
def history_pop(self)-> OperateRecord:
if len(self.history_records) == 0:
return None
self.records_pos -= 1
return self.history_records[self.records_pos]
def history_redo(self)->OperateRecord:
if self.records_pos == len(self.history_records):
return None
res = self.history_records[self.records_pos]
self.records_pos += 1
return res
def aside2contentId(self, aside_element: Element)->int:
for i in range(len(self.all_elements)):
if aside_element.equalTo(self.all_elements[i]):
return i
# 报错
return None
def save_excel_to_to_path(all_element, new_excel_path, header, excel_sheet_name):
if os.path.exists(new_excel_path):
os.remove(new_excel_path)
try:
create_sheet(new_excel_path, "旁白插入位置建议", [header])
for element in all_element:
element.print_self()
write_to_sheet(new_excel_path, excel_sheet_name, element.to_list())
except:
exception_info = ''.join(
traceback.format_exception(*sys.exc_info()))
print("保存表格到路径[%s]失败"%(new_excel_path))
print(exception_info)
return exception_info
print("保存表格到路径[%s]成功"%(new_excel_path))
return None
def write_to_sheet(path: str, sheet_name: str, value: list):
......@@ -149,6 +240,8 @@ def write_to_sheet(path: str, sheet_name: str, value: list):
value (list): 要插入表内的一行数据
"""
index = len(value)
# 把None换成空串
value = ["" if x == None else x for x in value]
workbook = openpyxl.load_workbook(path)
sheet = workbook.get_sheet_by_name(sheet_name)
cur_row = sheet.max_row
......@@ -161,6 +254,27 @@ def write_to_sheet(path: str, sheet_name: str, value: list):
workbook.save(path)
def create_sheet(path: str, sheet_name: str, value: list):
"""根据给定的表头,初始化表格
Args:
path (str): 表格(book)的存储位置
sheet_name (str): 表(sheet)的名字
value (list): 表头内容为['起始时间','终止时间','字幕','建议','旁白解说脚本']
"""
index = len(value)
workbook = openpyxl.Workbook()
sheet = workbook.active
sheet.title = sheet_name
# 将字幕对应的那一列扩宽一些
sheet.column_dimensions['C'].width = 50
sheet.column_dimensions['D'].width = 30
for i in range(0, index):
for j in range(0, len(value[i])):
sheet.cell(row=i + 1, column=j + 1, value=str(value[i][j]))
workbook.save(path)
def read_sheet(book_path: str, sheet_name: str = "") -> dict:
"""读表
......@@ -193,6 +307,15 @@ if __name__ == '__main__':
# print(d["字幕"])
# print(d.keys())
ctx = ProjectContext()
ctx.setExcelPath("d:/123")
print(ctx.excel_path)
\ No newline at end of file
# ctx = ProjectContext()
# ctx.setExcelPath("d:/123")
# print(ctx.excel_path)
e1 = Element(0,1,1,1,1)
# e1.st_time_sec = 0
e2 = Element(1,1,1,1,1)
# e2.st_time_sec = 1
all_element = [e2, e1]
all_element = sorted(all_element, key = lambda x: x.st_time_sec)
print(all_element[0].st_time_sec)
\ No newline at end of file
import sys
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem, QAbstractItemView)
from PyQt5 import QtCore
class myTableWidget(QWidget):
def __init__(self):
......@@ -26,6 +26,7 @@ class myTableWidget(QWidget):
tablewidget.setItem(0,1,ageItem)
jgItem = QTableWidgetItem("北京")
jgItem.setFlags(QtCore.Qt.ItemIsEnabled)
tablewidget.setItem(0,2,jgItem)
......
1
00:00:00,170 --> 00:00:03,919
我是标题
2
00:00:07,919 --> 00:00:09,919
我是测试
3
00:00:13,669 --> 00:00:15,169
你好
4
00:00:22,920 --> 00:00:24,670
Test English
5
00:00:28,670 --> 00:00:31,170
This Is A Movie
6
00:00:33,920 --> 00:00:35,920
结束,再见
import sys
import os
from PyQt5.QtCore import *;
from PyQt5.QtGui import *;
from PyQt5.QtWidgets import *;
from prompt_dialog_ui import Ui_Dialog
from utils import validate_and_get_filepath
"""
通用类
提示型,文本框
想要显示本提示框时,直接发送show_dialog_signal信号,并且把想要提示的内容作为传递过来即可。
"""
class Prompt_Dialog(QDialog, Ui_Dialog):
#开始检测信号,传参分别是movie路径和输出表格路径
show_dialog_signal = pyqtSignal(str)
def __init__(self):
super(Prompt_Dialog, self).__init__()
self.setupUi(self)
self.setWindowTitle("提示框")
self.show_dialog_signal.connect(self.show_with_msg)
def show_with_msg(self, msg):
self.label.setText(msg)
self.show()
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>404</width>
<height>188</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>30</x>
<y>130</y>
<width>341</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>60</x>
<y>30</y>
<width>291</width>
<height>71</height>
</rect>
</property>
<property name="text">
<string>请输入文字</string>
</property>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'prompt_dialog.ui'
#
# Created by: PyQt5 UI code generator 5.12
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName("Dialog")
Dialog.resize(404, 188)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setGeometry(QtCore.QRect(30, 130, 341, 32))
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(Dialog)
self.label.setGeometry(QtCore.QRect(60, 30, 291, 71))
self.label.setObjectName("label")
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept)
self.buttonBox.rejected.connect(Dialog.reject)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label.setText(_translate("Dialog", "请输入文字"))
......@@ -8,9 +8,10 @@ from PyQt5.QtGui import *;
from PyQt5.QtWidgets import *;
from utils import check_sheet_content
from management import RunThread
class SynthesisProcessor(QWidget):
show_warning_signal = pyqtSignal(str)
synthesis_callback_signal = pyqtSignal(list, list)
def __init__(self):
super(SynthesisProcessor, self).__init__()
......@@ -69,7 +70,7 @@ class SynthesisProcessor(QWidget):
# 多线程同时实现语音合成+字幕导出、进度条
state = [None]
threads = []
from management import RunThread
from speech_synthesis import ss_and_export
t = RunThread(funcName=ss_and_export,
args=(video_path, sheet_path, audio_dir, speed,
......@@ -87,6 +88,8 @@ class SynthesisProcessor(QWidget):
t.start()
print("===子线程已经开启 in synthesis===")
self.synthesis_callback_signal.emit(threads)
# 查询线程是否有结束的,一旦一个结束,另一个也结束
while 1:
alive = True
......
1
00:00:00,170 --> 00:00:03,919
我是标题
2
00:00:07,919 --> 00:00:09,919
我是测试
3
00:00:13,669 --> 00:00:15,169
你好
4
00:00:22,920 --> 00:00:24,670
Test English
5
00:00:28,670 --> 00:00:31,170
This Is A Movie
6
00:00:33,920 --> 00:00:35,920
结束,再见
No preview for this file type
......@@ -15,7 +15,7 @@ class TableWidget(QWidget):
tablewidget = QTableWidget()
tablewidget.setRowCount(4)
tablewidget.setColumnCount(6)
tablewidget.clear()
layout.addWidget(tablewidget)
tablewidget.setHorizontalHeaderLabels(['时间','文字'])
......@@ -31,15 +31,16 @@ class TableWidget(QWidget):
jgItem.setFlags(Qt.ItemFlags(32))
# jgItem.checkState()
tablewidget.setItem(0,2,jgItem)
self.tablewidget = tablewidget
# 禁止编辑
# tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 整行选择
tablewidget.setSelectionBehavior(QAbstractItemView.SelectRows)
# tablewidget.setSelectionBehavior(QAbstractItemView.SelectRows)
# 调整列和行
tablewidget.resizeColumnsToContents()
tablewidget.resizeRowsToContents()
# tablewidget.resizeColumnsToContents()
# tablewidget.resizeRowsToContents()
# tablewidget.horizontalHeader().setVisible(False)
# tablewidget.verticalHeader().setVisible(False)
......@@ -51,11 +52,24 @@ class TableWidget(QWidget):
self.setLayout(layout)
tablewidget.itemClicked.connect(self.show_data)
tablewidget.itemDoubleClicked.connect(self.show_data)
# tablewidget.itemActivated.connect(self.show_data2)
tablewidget.itemChanged.connect(self.show_data2)
# tablewidget.itemPressed.connect(self.show_data2)
# tablewidget.itemEntered.connect(self.show_data2)
# tablewidget.cellEntered.connect(self.show_data4)
# tablewidget.cellChanged.connect(self.show_data3)
def show_data4(self, row, col):
print("in show_data4")
print("row=", row, "col=", col)
def show_data3(self, row, col):
print("in show_data3")
print("row=",row,"col=",col)
def show_data(self, Item):
# 如果单元格对象为空
if Item is None:
print("Is None")
return
else:
print(Item.checkState())
......@@ -63,6 +77,23 @@ class TableWidget(QWidget):
col = Item.column() # 获取列数 注意是column而不是col哦
text = Item.text() # 获取内容
# 输出测试
print('row = ', row)
print('col =', col)
print('text = ', text)
def show_data2(self, Item):
print("in show_data2")
self.tablewidget.clear()
# 如果单元格对象为空
if Item is None:
return
else:
row = Item.row() # 获取行数
col = Item.column() # 获取列数 注意是column而不是col哦
text = Item.text() # 获取内容
# 输出测试
print('row = ', row)
print('col =', col)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment