Commit f8dc8db5 authored by 翟艳秋(20软)'s avatar 翟艳秋(20软)

add doc and fix the bugs

parent f90ee73d
......@@ -7,3 +7,4 @@ chineseocr_usage.py
easyOCR_usage.py
dist
build
log
from PyQt5.QtCore import Qt, QRect, QPointF
from PyQt5.QtGui import QPainter, QColor
from PyQt5.QtWidgets import QSlider, QWidget, QVBoxLayout, QProxyStyle, QStyle, QStyleOptionSlider
class SliderStyle(QProxyStyle):
def subControlRect(self, control, option, subControl, widget=None):
rect = super(SliderStyle, self).subControlRect(
control, option, subControl, widget)
if subControl == QStyle.SC_SliderHandle:
if option.orientation == Qt.Horizontal:
# 高度1/3
radius = int(widget.height() / 3)
offset = int(radius / 3)
if option.state & QStyle.State_MouseOver:
x = min(rect.x() - offset, widget.width() - radius)
x = x if x >= 0 else 0
else:
radius = offset
x = min(rect.x(), widget.width() - radius)
rect = QRect(x, int((rect.height() - radius) / 2),
radius, radius)
else:
# 宽度1/3
radius = int(widget.width() / 3)
offset = int(radius / 3)
if option.state & QStyle.State_MouseOver:
y = min(rect.y() - offset, widget.height() - radius)
y = y if y >= 0 else 0
else:
radius = offset
y = min(rect.y(), widget.height() - radius)
rect = QRect(int((rect.width() - radius) / 2),
y, radius, radius)
return rect
return rect
class PaintQSlider(QSlider):
def __init__(self, *args, **kwargs):
super(PaintQSlider, self).__init__(*args, **kwargs)
# 设置代理样式,主要用于计算和解决鼠标点击区域
self.setStyle(SliderStyle())
def paintEvent(self, _):
option = QStyleOptionSlider()
self.initStyleOption(option)
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing)
# 中间圆圈的位置
rect = self.style().subControlRect(
QStyle.CC_Slider, option, QStyle.SC_SliderHandle, self)
# 画中间白色线条
painter.setPen(Qt.white)
painter.setBrush(Qt.white)
if self.orientation() == Qt.Horizontal:
y = self.height() / 2
painter.drawLine(QPointF(0, y), QPointF(self.width(), y))
else:
x = self.width() / 2
painter.drawLine(QPointF(x, 0), QPointF(x, self.height()))
# 画圆
painter.setPen(Qt.NoPen)
if option.state & QStyle.State_MouseOver: # 双重圆
# 半透明大圆
r = rect.height() / 2
painter.setBrush(QColor(255, 255, 255, 100))
painter.drawRoundedRect(rect, r, r)
# 实心小圆(上下左右偏移4)
rect = rect.adjusted(4, 4, -4, -4)
r = rect.height() / 2
painter.setBrush(QColor(255, 255, 255, 255))
painter.drawRoundedRect(rect, r, r)
# 绘制文字
painter.setPen(Qt.white)
if self.orientation() == Qt.Horizontal: # 在上方绘制文字
x, y = rect.x(), rect.y() - rect.height() - 2
else: # 在左侧绘制文字
x, y = rect.x() - rect.width() - 2, rect.y()
painter.drawText(
x, y, rect.width(), rect.height(),
Qt.AlignCenter, str(self.value())
)
else: # 实心圆
r = rect.height() / 2
painter.setBrush(Qt.white)
painter.drawRoundedRect(rect, r, r)
# class Window(QWidget):
# def __init__(self, *args, **kwargs):
# super(Window, self).__init__(*args, **kwargs)
# self.setAttribute(Qt.WA_StyledBackground, True)
# layout = QVBoxLayout(self)
# layout.addWidget(PaintQSlider(Qt.Vertical, self, minimumWidth=90))
# layout.addWidget(PaintQSlider(Qt.Horizontal, self, minimumHeight=90))
# if __name__ == '__main__':
# import sys
# from PyQt5.QtWidgets import QApplication
# app = QApplication(sys.argv)
# w = Window()
# w.setStyleSheet('QWidget {background: gray;}')
# w.show()
# sys.exit(app.exec_())
\ No newline at end of file
# accessibility_movie_2
二期的无障碍电影制作工具,主要包含旁白区间检测和旁白及字幕导出两个功能; 二期主要提升用户体验、产品功能与易用性,设计成熟的操作界面进行人机交互,另外为了方便用户使用,使用pyinstaller进行打包。
注意要自行下载mp4的解码器,不然无法播放视频
\ No newline at end of file
import sys
import os
from PyQt5.QtCore import *;
from PyQt5.QtGui import *;
from PyQt5.QtWidgets import *;
from assemble_dialog_ui import Ui_Dialog
from utils import validate_and_get_filepath, replace_path_suffix
"""
视频合成 技术实现步骤:
1、dialog发送点击信号(把所有信息都带上,放到一个list里,不然太长了)
2、synthesis绑定该信号,并触发自身槽函数
3、开启多线程,进行视频合成
4、注意,dialog组件和synthesis组件都绑定到mainwindow上,信号就是方便他们交互的。
"""
class Assemble_Dialog(QDialog, Ui_Dialog):
# 开始合成信号,传参分别是video_path,audio_dir, sheet_path,speed_info, caption_path, speaker_info
# 注意这里打包成一个list,在worker端进行解参数,避免传参过多
start_assemble_signal = pyqtSignal(list)
def __init__(self, projectContext):
super(Assemble_Dialog, self).__init__()
self.projectContext = projectContext
self.setupUi(self)
self.setWindowTitle("合成")
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("开始合成")
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
self.pushButton.clicked.connect(self.openFile)
self.pushButton_2.clicked.connect(self.openTableFile)
# 注意,不能直接绑定到buttonBox上。
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).clicked.connect(self.start_assemble)
self.lineEdit_3.setText(projectContext.speaker_info)
self.lineEdit_4.setText(projectContext.speaker_speed)
# self.show.connect(self.init_self_slot)
def init_self(self):
# print("self.projectContext.speaker_info", self.projectContext.speaker_info)
self.lineEdit.setText(self.projectContext.video_path)
self.lineEdit_2.setText(self.projectContext.excel_path)
self.lineEdit_3.setText(self.projectContext.speaker_info)
self.lineEdit_4.setText(self.projectContext.speaker_speed)
def openFile(self):
file_info = QFileDialog.getOpenFileNames(self, '选择视频', os.getcwd(), "All Files(*);;Text Files(*.txt)")
file_name, ok = validate_and_get_filepath(file_info)
if ok and file_name != "":
self.lineEdit.setText(file_name)
def openTableFile(self):
now_path = os.path.join(os.getcwd(), self.lineEdit.text())
#todo: 方法提取出来放到utils里
now_path = now_path.replace(os.path.splitext(now_path)[-1], ".xlsx")
print("path:", now_path)
file_info = QFileDialog.getOpenFileNames(self, '选择文件路径', now_path, "'xlsx(*.xlsx)'")
print(file_info)
file_name, ok = validate_and_get_filepath(file_info)
print(file_name, ok)
if ok and file_name != "":
self.lineEdit_2.setText(file_name)
def start_assemble(self):
print("start_assemble")
video_path = self.lineEdit.text()
# 默认 输出的音频是工程目录+/output
audio_dir = os.path.join(self.projectContext.project_base_dir, "output")
sheet_path = self.lineEdit_2.text()
speaker_info = self.lineEdit_3.text()
speed_info = self.lineEdit_4.text()
#todo 后续变成常量存起来,或者做成配置
# caption_path = replace_path_suffix(self.lineEdit.text(), ".srt")
caption_path = os.path.join(audio_dir, os.path.basename(video_path).split('.')[0] + ".srt")
print("video_path: ",video_path)
print("audio_dir: ",audio_dir)
print("sheet_path: ",sheet_path)
print("speed_info: ",speed_info)
print("caption_path: ",caption_path)
print("speaker_info: ",speaker_info)
self.start_assemble_signal.emit([video_path, audio_dir, sheet_path,speed_info, caption_path, speaker_info])
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("./res/images/eagle_2.ico"))
dialog = Assemble_Dialog()
dialog.show()
"""旁白音频合成对话框
技术实现步骤:
1. dialog发送点击信号(把所有信息都带上,放到一个list里,不然太长了)
2. synthesis绑定该信号,并触发自身槽函数
3. 开启多线程,进行视频合成
4. 注意,dialog组件和synthesis组件都绑定到mainwindow上,信号就是方便他们交互的。
"""
import sys
import os
from PyQt5.QtCore import *;
from PyQt5.QtGui import *;
from PyQt5.QtWidgets import *;
from assemble_dialog_ui import Ui_Dialog
from utils import validate_and_get_filepath, replace_path_suffix
class Assemble_Dialog(QDialog, Ui_Dialog):
# 开始合成信号,传参分别是video_path,audio_dir, sheet_path,speed_info, caption_path, speaker_info
# 注意这里打包成一个list,在worker端进行解参数,避免传参过多
start_assemble_signal = pyqtSignal(list)
def __init__(self, projectContext):
super(Assemble_Dialog, self).__init__()
self.projectContext = projectContext
self.setupUi(self)
self.setWindowTitle("合成")
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("开始合成")
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
self.pushButton.clicked.connect(self.open_file)
self.pushButton_2.clicked.connect(self.open_table_file)
# 注意,不能直接绑定到buttonBox上。
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).clicked.connect(self.start_assemble)
self.lineEdit_3.setText(projectContext.speaker_info)
self.lineEdit_4.setText(projectContext.speaker_speed)
# self.show.connect(self.init_self_slot)
def init_self(self):
"""使用工程配置信息初始化语音合成界面
"""
# print("self.projectContext.speaker_info", self.projectContext.speaker_info)
self.lineEdit.setText(self.projectContext.video_path)
self.lineEdit_2.setText(self.projectContext.excel_path)
self.lineEdit_3.setText(self.projectContext.speaker_info)
self.lineEdit_4.setText(self.projectContext.speaker_speed)
def open_file(self):
"""选择视频文件
"""
file_info = QFileDialog.getOpenFileNames(self, '选择视频', os.getcwd(), "All Files(*);;Text Files(*.txt)")
file_name, ok = validate_and_get_filepath(file_info)
if ok and file_name != "":
self.lineEdit.setText(file_name)
def open_table_file(self):
"""选择旁白表格文件
"""
now_path = os.path.join(os.getcwd(), self.lineEdit.text())
#todo: 方法提取出来放到utils里
now_path = now_path.replace(os.path.splitext(now_path)[-1], ".xlsx")
print("path:", now_path)
file_info = QFileDialog.getOpenFileNames(self, '选择文件路径', now_path, "'xlsx(*.xlsx)'")
print(file_info)
file_name, ok = validate_and_get_filepath(file_info)
print(file_name, ok)
if ok and file_name != "":
self.lineEdit_2.setText(file_name)
def start_assemble(self):
"""开始语音合成
读取当前界面中的用户输入,并通过信号发送到主界面调用旁白音频合成算法
"""
print("start_assemble")
video_path = self.lineEdit.text()
# 默认 输出的音频是工程目录+/output
audio_dir = os.path.join(self.projectContext.project_base_dir, "output")
sheet_path = self.lineEdit_2.text()
speaker_info = self.lineEdit_3.text()
speed_info = self.lineEdit_4.text()
#todo 后续变成常量存起来,或者做成配置
# caption_path = replace_path_suffix(self.lineEdit.text(), ".srt")
caption_path = os.path.join(audio_dir, os.path.basename(video_path).split('.')[0] + ".srt")
print("video_path: ",video_path)
print("audio_dir: ",audio_dir)
print("sheet_path: ",sheet_path)
print("speed_info: ",speed_info)
print("caption_path: ",caption_path)
print("speaker_info: ",speaker_info)
self.start_assemble_signal.emit([video_path, audio_dir, sheet_path,speed_info, caption_path, speaker_info])
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setWindowIcon(QIcon("./res/images/eagle_2.ico"))
dialog = Assemble_Dialog()
dialog.show()
sys.exit(app.exec_())
\ No newline at end of file
"""常用的一些常量
类型如下:
- Content类是界面中字幕+旁白表格,标注常用列的编号及表格对应名称
- Aside类是界面中的旁白表格,标注常用列的编号、表格对应名称和语速选项
- Subtitle类是界面中的字幕表格,标注时间戳对应列编号和表格对应名称
- Pathes类提供常用资源的路径,如说话人信息文件
"""
import os
......@@ -29,8 +37,6 @@ class Subtitle:
TimeFormatColumns = [0, 1]
dir_path = os.path.dirname(os.path.abspath(__file__))
class Pathes:
dir_path = os.path.dirname(os.path.abspath(__file__))
speaker_conf_path = os.path.join(dir_path, "res/speakers.json")
"""新建工程界面相关响应
新建工程界面相关响应如下:
- chooese_root: 选择存放工程的文件夹的函数
- create_project: 点击“确认”时调用,用于确认输入信息正确性,从而正确地新建工程
- close_dialog: 点击“取消”时调用,用于关闭对话框
"""
import os
from PyQt5.QtCore import *;
from PyQt5.QtGui import *;
......@@ -17,12 +26,19 @@ class Create_Dialog(QDialog, Ui_Dialog):
self.cancel.clicked.connect(self.close_dialog)
def choose_root(self):
"""选择存放新工程的文件夹
"""
root_info = QFileDialog.getExistingDirectory(self, "选择工程文件夹", os.getcwd())
if len(self.root_input.text()) != 0 and len(root_info) == 0:
return
self.root_input.setText(root_info)
def create_project(self):
"""创建新的工程
读取用户输入,进行合理性判断,存在错误时弹窗提示,否则在该路径下创建以工程名命名的文件夹,并通过信号传递工程路径,对主界面进行更新,然后自动关闭该界面。
"""
self.project_name = self.name_input.text()
self.dir_path = self.root_input.text()
if os.path.exists(self.dir_path) and len(self.project_name) > 0:
......@@ -40,4 +56,7 @@ class Create_Dialog(QDialog, Ui_Dialog):
self.prompt_dialog.show_with_msg("请输入合法文件夹路径")
def close_dialog(self):
"""关闭窗口
"""
self.close()
\ No newline at end of file
import cv2
if __name__ == '__main__':
video_path = 'D:/mystudy/Eagle/accessibility_movie_1/test37second.mp4'
video = cv2.VideoCapture(video_path)
fps = video.get(cv2.CAP_PROP_FPS)
frame_cnt = video.get(cv2.CAP_PROP_FRAME_COUNT)
duration = frame_cnt / fps
print("fps:", fps, type(fps))
print("frame_cnt:", frame_cnt, type(frame_cnt))
print("duration:", duration, type(duration))
\ No newline at end of file
"""旁白区间检测界面相关响应
旁白区间检测界面相关响应如下:
- init_self: 初始化当前界面
- open_file: 选择待处理的视频
- open_table_file: 选择字幕和旁白输出的表格在本地的存放位置
- start_detect: 确认是否开始旁白区间检测
"""
import sys
import os
from PyQt5.QtCore import *;
......@@ -19,23 +29,34 @@ class Detect_Dialog(QDialog, Ui_Dialog):
self.setWindowTitle("检测")
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("开始检测")
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
self.pushButton.clicked.connect(self.openFile)
self.pushButton_2.clicked.connect(self.openTableFile)
self.pushButton.clicked.connect(self.open_file)
self.pushButton_2.clicked.connect(self.open_table_file)
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).clicked.connect(self.start_detect)
self.prompt_dialog = Prompt_Dialog()
def init_self(self):
"""根据工程配置信息初始化界面
"""
self.lineEdit.setText(self.projectContext.video_path)
self.lineEdit_2.setText(self.projectContext.excel_path)
def openFile(self):
def open_file(self):
"""选择待处理的视频
根据工程当前选定的视频为起始点打开一个窗口供用户进行视频选择,在该窗口中只显示mp4、rmvb、mkv和avi格式的视频文件,避免用户选择到非视频文件。
"""
root_dir = os.getcwd() if self.projectContext.video_path is None else os.path.dirname(self.projectContext.video_path)
file_info = QFileDialog.getOpenFileNames(self, '选择视频', root_dir, "Video Files(*.mp4 *.rmvb *mkv *avi)")
file_name, ok = validate_and_get_filepath(file_info)
if ok and file_name != "":
self.lineEdit.setText(file_name)
def openTableFile(self):
def open_table_file(self):
"""选择旁白和字幕输出表格的路径
以视频路径为基础构造表格路径,以此为基点打开一个选择文件路径的窗口,在该窗口中默认保存文件后缀为xlsx,在确认文件路径正确后,在表格中更新对应文本框中的内容。
"""
now_path = os.path.join(os.getcwd(), self.lineEdit.text())
now_path = now_path.replace(os.path.splitext(now_path)[-1], ".xlsx")
print("path:", now_path)
......@@ -47,12 +68,12 @@ class Detect_Dialog(QDialog, Ui_Dialog):
self.lineEdit_2.setText(file_name)
def start_detect(self):
# 发出一个信号,开始检测了
# 版本1.0:(当前版本)
# 让主窗口接受,新起进程检测并和主线程交互
# 版本2.0:
# 在主窗口启动时,就启动一个QThread,专门接收该信号并进行检测,
# 发出该信号后,由QThread和主窗口同时接收,然后让他俩通过信号交互即可。
"""开始检测
对视频和表格路径判空后,将视频和表格路径通过信号传递给主界面,从而在主界面中新起线程启动旁白区间检测
[todo] 方法2: 在主窗口启动时,就启动一个QThread,专门接收该信号并进行检测,发出该信号后,由QThread和主窗口同时接收,然后让他俩通过信号交互即可。
"""
if self.lineEdit.text()!="" and self.lineEdit_2.text()!="":
self.start_detect_signal.emit(self.lineEdit.text(), self.lineEdit_2.text())
else:
......
......@@ -34,9 +34,11 @@ from main_window import MainWindow, Element
up_b, down_b = 0, 0
# 初始化ocr工具
ocr = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False, use_gpu=False)
# ocr = EasyOCR()
# ocr = ChineseOCR()
paddle_dir = "res/.paddleocr/2.3.0.1/ocr/"
cur_cls_model_dir = paddle_dir + "cls/ch_ppocr_mobile_v2.0_cls_infer"
cur_det_model_dir = paddle_dir + "det/ch/ch_PP-OCRv2_det_infer"
cur_rec_model_dir = paddle_dir + "rec/ch/ch_PP-OCRv2_rec_infer"
ocr = PaddleOCR(use_angle_cls=True, lang="ch", show_log=False, use_gpu=False, cls_model_dir=cur_cls_model_dir, det_model_dir=cur_det_model_dir, rec_model_dir=cur_rec_model_dir)
# 正常语速为4字/秒
normal_speed = 4
......@@ -181,14 +183,14 @@ def normalize(text: str) -> str:
return text
def detect_subtitle(img: np.ndarray) -> Union[str, None]:
def detect_subtitle(img: np.ndarray) -> Tuple[Union[str, None], float]:
"""检测当前画面得到字幕信息
Args:
img (np.ndarray): 当前画面
Returns:
Union[str, None]: 字幕信息(没有字幕时返回None)
Tuple[Union[str, None]]: 字幕信息(没有字幕时返回None)和置信度
"""
subTitle = ''
height = down_b - up_b
......@@ -214,16 +216,18 @@ def detect_subtitle(img: np.ndarray) -> Union[str, None]:
gradient = np.arctan(abs((rect[1][1] - rect[0][1]) / (rect[1][0] - rect[0][0])))
# log.append("文本:{},置信度:{},中心点:{},斜率:{},字体大小:{}".format(txt, confidence, mid / img.shape[1], gradient,
# font_size)) 置信度>0.7 & 斜率<0.1 & 字幕偏移量<=25 & 字幕中心在画面宽的0.4-0.6之间
# print("文本:{},置信度:{},中心点:{},斜率:{},字体大小:{}".format(txt, confidence, mid / img.shape[1], gradient, font_size))
# print("差距:{}".format(abs(rect[0][1] - 30) + abs(img.shape[0] - rect[2][1] - 30)))
print("文本:{},置信度:{},中心点:{},斜率:{},字体大小:{}".format(txt, confidence, mid / img.shape[1], gradient, font_size))
print("字体大小差距: {}", format(height - font_size))
print("高度中心:{}".format((rect[0][1] + rect[1][1])/2/img.shape[0]))
conf_thred1 = 0.7
conf_thred2 = 0.85
# conf_thred1 = 0.1
# conf_thred2 = 0.4
# conf_thred1 = 0.5
# conf_thred2 = 0.7
if confidence > conf_thred1 and gradient < 0.1 and 0.4 < mid / img.shape[1] < 0.6 and \
abs(rect[0][1] - 30) + abs(img.shape[0] - rect[2][1] - 30) <= font_size - 10:
if (rect[0][1] + rect[1][1])/2/img.shape[0] > 0.5 or (rect[0][1] + rect[1][1])/2/img.shape[0] <= 0.1:
continue
if confidence > conf_thred1 and gradient < 0.1 and 0.4 < mid / img.shape[1] < 0.6:
subTitle += txt
conf = max(conf,confidence)
# possible_txt.append([txt, mid/img.shape[1]])
......
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
assemble\_dialog module
=======================
.. automodule:: assemble_dialog
:members:
:undoc-members:
:show-inheritance:
assemble\_dialog\_ui module
===========================
.. automodule:: assemble_dialog_ui
:members:
:undoc-members:
:show-inheritance:
import sys
from os.path import abspath, dirname
sys.path.insert(0, dirname(dirname(dirname(abspath(__file__)))))
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = '无障碍电影制作系统'
copyright = '2023, 翟艳秋'
author = '翟艳秋'
# The full version, including alpha/beta/rc tags
release = 'v2.1.0'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.apidoc',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.coverage',
'sphinx.ext.mathjax',
'rst2pdf.pdfbuilder',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'zh_CN'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'sphinx_rtd_theme'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
htmlhelp_basename = 'PYTHON doc'
# -- Options for LATEX output -------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
'preamble': '''
\\hypersetup{unicode=true}
\\usepackage{CJKutf8}
\\AtBeginDocument{\\begin{CJK}{UTF8}{gbsn}}
\\AtEndDocument{\\end{CJK}}
''',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'Accessibility.tex', 'PYTHON Documentation',
'xxxx', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'PYTHON', 'PYTHON ',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'PYTHON', 'PYTHON',
author, 'PYTHON', 'One line description of project.',
'Miscellaneous'),
]
# -- Extension configuration -------------------------------------------------
# -- Options for todo extension ----------------------------------------------
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
\ No newline at end of file
constant module
===============
.. automodule:: constant
:members:
:undoc-members:
:show-inheritance:
create\_dialog module
=====================
.. automodule:: create_dialog
:members:
:undoc-members:
:show-inheritance:
create\_dialog\_ui module
=========================
.. automodule:: create_dialog_ui
:members:
:undoc-members:
:show-inheritance:
detect\_dialog module
=====================
.. automodule:: detect_dialog
:members:
:undoc-members:
:show-inheritance:
detect\_dialog\_ui module
=========================
.. automodule:: detect_dialog_ui
:members:
:undoc-members:
:show-inheritance:
detect\_with\_asr module
========================
.. automodule:: detect_with_asr
:members:
:undoc-members:
:show-inheritance:
detect\_with\_ocr module
========================
.. automodule:: detect_with_ocr
:members:
:undoc-members:
:show-inheritance:
.. Assistant Production System of Narration For Barrier-Free Films documentation master file, created by
sphinx-quickstart on Sun Jun 18 22:25:10 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Assistant Production System of Narration For Barrier-Free Films's documentation!
===========================================================================================
.. toctree::
:maxdepth: 1
:caption: Get Started
:hidden:
:glob:
.. toctree::
:maxdepth: 2
:caption: UI
:glob:
main_window_ui
assemble_dialog_ui
create_dialog_ui
detect_dialog_ui
operation_dialog_ui
prompt_dialog_ui
setting_dialog_ui
start
.. toctree::
:maxdepth: 2
:caption: funcs_for_ui
:glob:
main_window
assemble_dialog
create_dialog
detect_dialog
operation_dialog
prompt_dialog
setting_dialog
start
.. toctree::
:maxdepth: 2
:caption: API
:glob:
constant
detect_with_asr
detect_with_ocr
judge_subtitle
management
myvideoslider
myVideoWidget
mywidgetcontents
narratage_detection
render
speech_synthesis
split_wav
synthesis
utils
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
judge\_subtitle module
======================
.. automodule:: judge_subtitle
:members:
:undoc-members:
:show-inheritance:
main\_window module
===================
.. automodule:: main_window
:members:
:undoc-members:
:show-inheritance:
main\_window\_ui module
=======================
.. automodule:: main_window_ui
:members:
:undoc-members:
:show-inheritance:
management module
=================
.. automodule:: management
:members:
:undoc-members:
:show-inheritance:
accessibility_movie_2
=====================
.. toctree::
:maxdepth: 4
assemble_dialog
assemble_dialog_ui
constant
create_dialog
create_dialog_ui
detect_dialog
detect_dialog_ui
detect_with_asr
detect_with_ocr
judge_subtitle
main_window
main_window_ui
management
myVideoWidget
myvideoslider
mywidgetcontents
narratage_detection
operation_dialog
operation_dialog_ui
prompt_dialog
prompt_dialog_ui
render
setting_dialog
setting_dialog_ui
speech_synthesis
split_wav
start
synthesis
utils
myVideoWidget module
====================
.. automodule:: myVideoWidget
:members:
:undoc-members:
:show-inheritance:
myvideoslider module
====================
.. automodule:: myvideoslider
:members:
:undoc-members:
:show-inheritance:
mywidgetcontents module
=======================
.. automodule:: mywidgetcontents
:members:
:undoc-members:
:show-inheritance:
narratage\_detection module
===========================
.. automodule:: narratage_detection
:members:
:undoc-members:
:show-inheritance:
operation\_dialog module
========================
.. automodule:: operation_dialog
:members:
:undoc-members:
:show-inheritance:
operation\_dialog\_ui module
============================
.. automodule:: operation_dialog_ui
:members:
:undoc-members:
:show-inheritance:
prompt\_dialog module
=====================
.. automodule:: prompt_dialog
:members:
:undoc-members:
:show-inheritance:
prompt\_dialog\_ui module
=========================
.. automodule:: prompt_dialog_ui
:members:
:undoc-members:
:show-inheritance:
render module
=============
.. automodule:: render
:members:
:undoc-members:
:show-inheritance:
setting\_dialog module
======================
.. automodule:: setting_dialog
:members:
:undoc-members:
:show-inheritance:
setting\_dialog\_ui module
==========================
.. automodule:: setting_dialog_ui
:members:
:undoc-members:
:show-inheritance:
speech\_synthesis module
========================
.. automodule:: speech_synthesis
:members:
:undoc-members:
:show-inheritance:
split\_wav module
=================
.. automodule:: split_wav
:members:
:undoc-members:
:show-inheritance:
start module
============
.. automodule:: start
:members:
:undoc-members:
:show-inheritance:
synthesis module
================
.. automodule:: synthesis
:members:
:undoc-members:
:show-inheritance:
utils module
============
.. automodule:: utils
:members:
:undoc-members:
:show-inheritance:
This diff is collapsed.
......@@ -273,6 +273,7 @@ class Ui_MainWindow(object):
self.sld_video.setTickInterval(1)
self.sld_video.setObjectName("sld_video")
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.scrollArea.setGeometry(QtCore.QRect(0, 0, 820, 42))
self.zm_slider_layout.addWidget(self.scrollArea)
self.lab_video = QtWidgets.QLabel(self.verticalWidget_2)
self.lab_video.setMaximumSize(QtCore.QSize(16777215, 50))
......@@ -358,8 +359,6 @@ class Ui_MainWindow(object):
self.action_3.setObjectName("action_3")
self.action_4 = QtWidgets.QAction(MainWindow)
self.action_4.setObjectName("action_4")
self.action_refresh_tab = QtWidgets.QAction(MainWindow)
self.action_refresh_tab.setObjectName("action_refresh_tab")
self.action_operate = QtWidgets.QAction(MainWindow)
self.action_operate.setObjectName("action_operate")
self.action_export = QtWidgets.QAction(MainWindow)
......@@ -386,7 +385,6 @@ class Ui_MainWindow(object):
self.menu_3.addAction(self.action_3)
self.menu_3.addAction(self.action_4)
self.menu_3.addSeparator()
self.menu_3.addAction(self.action_refresh_tab)
self.menubar.addAction(self.menu.menuAction())
self.menubar.addAction(self.menu_2.menuAction())
self.menubar.addAction(self.menu_3.menuAction())
......@@ -419,7 +417,6 @@ class Ui_MainWindow(object):
self.action_redo.setText(_translate("MainWindow", "重做"))
self.action_3.setText(_translate("MainWindow", "旁白区间检测"))
self.action_4.setText(_translate("MainWindow", "旁白音频合成"))
self.action_refresh_tab.setText(_translate("MainWindow", "刷新且保存表格"))
self.action_operate.setText(_translate("MainWindow", "操作表格"))
self.action_export.setText(_translate("MainWindow", "导出"))
self.action_insert_aside_from_now.setText(_translate("MainWindow", "当前位置插入旁白"))
......
This diff is collapsed.
import sys
from PyQt5.QtWidgets import (QWidget, QTableWidget, QHBoxLayout, QApplication, QTableWidgetItem, QAbstractItemView)
from PyQt5 import QtCore
class myTableWidget(QWidget):
def __init__(self):
super(myTableWidget,self).__init__()
self.initUI()
def initUI(self):
self.setWindowTitle("QTableWidget演示")
self.resize(430, 230);
layout = QHBoxLayout()
tablewidget = QTableWidget()
tablewidget.setRowCount(4)
tablewidget.setColumnCount(6)
layout.addWidget(tablewidget)
tablewidget.setHorizontalHeaderLabels(['时间','文字'])
nameItem = QTableWidgetItem("0:00")
tablewidget.setItem(0,0,nameItem)
ageItem = QTableWidgetItem("24")
tablewidget.setItem(0,1,ageItem)
jgItem = QTableWidgetItem("北京")
jgItem.setFlags(QtCore.Qt.ItemIsEnabled)
tablewidget.setItem(0,2,jgItem)
# 禁止编辑
# tablewidget.setEditTriggers(QAbstractItemView.NoEditTriggers)
# 整行选择
tablewidget.setSelectionBehavior(QAbstractItemView.SelectRows)
# 调整列和行
tablewidget.resizeColumnsToContents()
tablewidget.resizeRowsToContents()
# tablewidget.horizontalHeader().setVisible(False)
# tablewidget.verticalHeader().setVisible(False)
tablewidget.setVerticalHeaderLabels(["a","b"])
# 隐藏表格线
tablewidget.setShowGrid(False)
self.setLayout(layout)
tablewidget.itemClicked.connect(self.show_data)
def show_data(self, Item):
# 如果单元格对象为空
if Item is None:
return
else:
row = Item.row() # 获取行数
col = Item.column() # 获取列数 注意是column而不是col哦
text = Item.text() # 获取内容
# 输出测试
print('row = ', row)
print('col =', col)
print('text = ', text)
if __name__ == '__main__':
app = QApplication(sys.argv)
example = myTableWidget()
example.show()
sys.exit(app.exec_())
This diff is collapsed.
def f(a,b,c,d):
print(a,b,c,d)
# f([1,2,3))
data = [ 'ACME', 50, 91.1, (2012, 12, 21) ]
name, shares, price, date = data
print(name)
from enum import Enum
class Operation(Enum):
Modify = 0
Delete = 1
# print(Operation.Delete)
print(Operation(('修改', "xiugai")))
\ No newline at end of file
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, ok_func = None):
super(Prompt_Dialog, self).__init__()
self.setupUi(self)
self.setWindowTitle("提示框")
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("确认")
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
self.show_dialog_signal.connect(self.show_with_msg)
if ok_func != None:
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).clicked.connect(ok_func)
def show_with_msg(self, msg):
self.label.setText(msg)
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, ok_func = None):
super(Prompt_Dialog, self).__init__()
self.setupUi(self)
self.setWindowTitle("提示框")
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).setText("确认")
self.buttonBox.button(QDialogButtonBox.StandardButton.Cancel).setText("取消")
self.show_dialog_signal.connect(self.show_with_msg)
if ok_func != None:
self.buttonBox.button(QDialogButtonBox.StandardButton.Ok).clicked.connect(ok_func)
def show_with_msg(self, msg: str):
"""在弹窗中展示提示语句
Args:
msg (str): 提示语句
"""
self.label.setText(msg)
self.show()
\ No newline at end of file
......@@ -32,15 +32,33 @@
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>60</x>
<y>30</y>
<width>291</width>
<x>50</x>
<y>40</y>
<width>300</width>
<height>71</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>300</width>
<height>0</height>
</size>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>请输入文字</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</widget>
<resources/>
......
......@@ -20,6 +20,7 @@ class Ui_Dialog(object):
self.buttonBox.setObjectName("buttonBox")
self.label = QtWidgets.QLabel(Dialog)
self.label.setGeometry(QtCore.QRect(60, 30, 291, 71))
self.label.setWordWrap(True)
self.label.setObjectName("label")
self.retranslateUi(Dialog)
......
paddlepaddle==2.2.0
paddleocr==2.3.0.1
qt_material
soundfile
azure-cognitiveservices-speech==1.19.0
openpyxl
opencv-python
PyQt5
\ No newline at end of file
cn2an==0.5.16
Flask==2.0.2
flask_cors==3.0.10
ijson==3.2.1
LAC==2.1.2
librosa==0.8.1
numpy==1.19.3
onnxruntime==1.14.0
opencv_python==4.4.0.46
openpyxl==3.0.9
paddle==1.0.2
paddleocr==2.3.0.1
paddlepaddle==2.2.0
paddlespeech==0.1.0
parakeet==0.24
Pillow==9.5.0
pyaudio==0.2.13
pyclipper==1.3.0
pydub==0.25.1
PyQt5==5.15.9
PyYAML==6.0
qt_material==2.12
resampy==0.2.2
scipy==1.3.0
Shapely==1.8.0
SoundFile==0.10.2
tqdm==4.62.3
visualdl==2.2.1
webrtcvad==2.0.10
yacs==0.1.8
zhconv==1.4.3
......@@ -41,6 +41,11 @@ class Setting_Dialog(QDialog, Ui_Dialog):
self.pushButton.clicked.connect(self.play_audio_slot)
def content_fresh(self):
"""刷新界面中的内容
将工程信息中的说话人信息、说话人语速更新到界面中,如果未选择则初始化为第一个选项
"""
if self.projectContext.speaker_info is None:
self.comboBox.setCurrentIndex(0)
else:
......@@ -51,16 +56,26 @@ class Setting_Dialog(QDialog, Ui_Dialog):
self.comboBox_2.setCurrentIndex(self.speed_li_2.index(self.projectContext.speaker_speed))
def speaker_change_slot(self):
"""切换说话人
将当前的说话人设置为工程的说话人,并保存到配置文件中
"""
self.projectContext.speaker_info = self.comboBox.currentText()
self.projectContext.save_conf()
# print("self.projectContext.speaker_info:", self.projectContext.speaker_info)
def speed_change_slot(self):
"""切换语速
将当前的语速设置为工程的语速,并保存到配置文件中
"""
self.projectContext.speaker_speed = self.comboBox_2.currentText()
self.projectContext.save_conf()
def play_audio_slot(self):
"""播放说话人的样例音频
"""新起一个线程来播放说话人的样例音频
根据用户选择的说话人更新对应样例音频
停止上一个说话人的音频
......@@ -75,13 +90,21 @@ class Setting_Dialog(QDialog, Ui_Dialog):
audioPlayed = winsound.PlaySound(chosenSpeaker.voice_example, winsound.SND_ASYNC)
thread_it(f, name="playAudio")
# 重写关闭窗口事件,使得可以停止播放样例视频
def closeEvent(self, event):
"""重写关闭窗口事件,使得可以停止播放样例视频
Args:
event: 关闭窗口事件
"""
global audioPlayed
winsound.PlaySound(audioPlayed, winsound.SND_PURGE)
event.accept()
def showDialog(self):
"""刷新信息并展示窗口
"""
self.content_fresh()
self.show()
......
......@@ -14,6 +14,7 @@
from speech_synthesis import ss_and_export
ss_and_export(video_path, sheet_path, audio_dir, speed, caption_path, speaker_name, state)
"""
import json
import os
......@@ -25,6 +26,7 @@ import numpy as np
from azure.cognitiveservices.speech import SpeechConfig, SpeechSynthesizer, ResultReason, AudioDataStream
from azure.cognitiveservices.speech.audio import AudioOutputConfig
import openpyxl
import shutil
tmp_file = 'tmp.wav'
adjusted_wav_path = "adjusted.wav"
......@@ -58,7 +60,7 @@ def init_speakers():
相关配置文件为"speaker.json",如果有信息需要修改,请在speaker.json中修改
"""
f = open("speakers.json", encoding="utf-8")
f = open("./res/speakers.json", encoding="utf-8")
content = json.load(f)
global speakers
for speaker_info in content["speaker_details"]:
......@@ -82,12 +84,15 @@ def choose_speaker(speaker_name: str) -> Speaker:
def speech_synthesis(text: str, output_file: str, speaker: Speaker, speed: float = 1.0):
"""用于合成讲解音频并输出
分为两步走:第一步在项目文件夹生成tmp.wav;第二步在output_file路径下生成调整音量和语速后的音频
分为两步走:第一步在项目文件夹生成tmp.wav;第二步在output_file路径下生成调整音量和语速后的音频
Args:
text (str): 解说文本
output_file (str): 输出文件路径
speaker (Speaker): 说话人
speed (float, optional): 指定的音频语速. Defaults to 1.0.
"""
speech_config = SpeechConfig(
subscription="db34d38d2d3447d482e0f977c66bd624",
......@@ -143,13 +148,15 @@ def change_speed_and_volume(wav_path: str, speed: float = 1.0):
def read_sheet(book_path: str, sheet_name: str = "") -> dict:
"""读表
从表格中读出所有的内容,用dict保存(表格的格式固定,第一行为表头(起始时间|终止时间|字幕|建议|解说脚本))
从表格中读出所有的内容,用dict保存(表格的格式固定,第一行为表头(起始时间|终止时间|字幕|建议|解说脚本))
Args:
book_path (str): 表格的存储路径
sheet_name (str, optional): 想要读取的表在excel表格中的名字. Defaults to "".
Returns:
dict: 表格中的所有内容,key为该列表头,value为列中的数据
"""
workbook = openpyxl.load_workbook(book_path)
sheet = workbook.active
......@@ -324,7 +331,9 @@ def ss_and_export(video_path: str, sheet_path: str, output_dir: str, speed: floa
# 音频输出位置路径
root_path = output_dir
# 如果文件夹不存在,则新建文件夹
# 如果文件夹已存在,就删除已有的文件夹,避免用户之前已有的一些中间结果
if os.path.exists(root_path):
shutil.rmtree(root_path)
if not os.path.exists(root_path):
os.mkdir(root_path)
......@@ -358,8 +367,6 @@ def ss_and_export(video_path: str, sheet_path: str, output_dir: str, speed: floa
if os.path.exists(tmp_file):
time.sleep(1)
os.remove(tmp_file)
# os.remove(origin_wav_path)
# os.remove(adjusted_wav_path)
state[0] = 1.00
......@@ -371,11 +378,4 @@ if __name__ == '__main__':
speed = 1.25
caption_file = os.path.join(output_dir, os.path.basename(video_path) + ".srt")
speaker_name = '晓秋'
ss_and_export(video_path, sheet_path, output_dir, speed, caption_file, speaker_name)
# import pprint
# d = read_sheet("./test37second.xlsx")
# pprint.pprint(d)
# init_speakers()
# speaker_name = "晓秋"
# speaker = choose_speaker(speaker_name)
# speech_synthesis("今天我们讲解的电影是何以笙箫默,它讲述了", r"D:\AddCaption\cur_version\accessibility_movie_2\test.wav", speaker, 0.5)
\ No newline at end of file
ss_and_export(video_path, sheet_path, output_dir, speed, caption_file, speaker_name)
\ No newline at end of file
......@@ -12,6 +12,11 @@ os.environ['PYQTGRAPH_QT_LIB'] = 'PyQt5'
project_path = None
def change_project_path(path):
"""用于切换当前工程路径
Args:
path (str): 用户选择或新建的工程路径
"""
global project_path
project_path = path
......
"""各种工具函数
工具函数描述如下:
- check_sheet_content: 检查表头是否符合预期
- get_progress_with_cmd: 获取ffmpeg处理视频的进度
- get_sheetHead: 获取表头
- replace_path_suffix: 替换路径后缀
- stop_thread: 停止线程
- trans_to_seconds: 将"hh:mm:ss.xxx"格式的时间转换为秒
- transfer_second_to_time: 将秒转换为"hh:mm:ss.xxx"格式的时间
- validate_and_get_filepath: 确认传入参数正确性,并返回文件路径
"""
import os, sys
import openpyxl
import subprocess
......@@ -6,8 +20,17 @@ import threading
import time
import inspect
import ctypes
from typing import Tuple
def validate_and_get_filepath(file_info) -> Tuple[str, bool]:
"""确认传入参数正确性,并返回文件路径
Args:
file_info: 文件选择窗口中返回的文件信息
def validate_and_get_filepath(file_info):
Returns:
Tuple[str, bool]: 文件路径和传入信息是否包含路径
"""
if type(file_info[0]) == str:
return file_info[0], True
# 兼容 空
......@@ -19,7 +42,7 @@ def trans_to_seconds(timePoint: str) -> float:
"""将用户输入的时间字符串转换为秒数
Args:
timePoint (str): 时间字符串
timePoint (str): "hh:mm:ss.xxx"格式的时间字符串
Returns:
float: 时间字符串对应秒数
......@@ -33,8 +56,13 @@ def trans_to_seconds(timePoint: str) -> float:
return time_in_seconds
def transfer_second_to_time(sec: str) -> str:
"""
输入xxxx.xxx秒,输出xx:xx:xx.xxx的格式
"""将秒数转换为"hh:mm:ss.xxx"格式的时间字符串
Args:
sec (str): 待转换的描述
Returns:
str: "hh:mm:ss.xxx"格式的时间字符串
"""
duration = int(float(sec))
hour = int(duration/3600)
......@@ -44,7 +72,16 @@ def transfer_second_to_time(sec: str) -> str:
time = "%02d:%02d:%02d.%03d" % (hour, minutes, seconds, msec)
return time
def replace_path_suffix(path, new_suffix):
def replace_path_suffix(path: str, new_suffix: str) -> str:
"""替换文件路径后缀
Args:
path (str): 原文件路径
new_suffix (str): 新的后缀
Returns:
str: 新的文件路径
"""
return path.replace(os.path.splitext(path)[-1], new_suffix)
def check_sheet_content(book_path: str) -> bool:
......@@ -86,11 +123,21 @@ def get_sheetHead(book_path: str) -> list:
return sheet_head
def get_progress_with_cmd(cmd: str, state=None):
"""获取ffmpeg命令行的执行进度
Args:
cmd (str): 用于后期处理的ffmpeg命令行
state (optional): 用于同步主界面中进度条的信号量. Defaults to None.
"""
if state is None:
state = [0]
pre = state[0]
process = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, encoding='utf-8', text=True)
startup = subprocess.STARTUPINFO()
startup.dwFlags = subprocess.STARTF_USESHOWWINDOW
startup.wShowWindow = subprocess.SW_HIDE
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, encoding='utf-8', text=True,
startupinfo=startup)
for line in process.stdout:
print(line)
duration_res = re.search(r'\sDuration: (?P<duration>\S+)', line)
......@@ -126,46 +173,12 @@ def _async_raise(tid, exctype):
def stop_thread(thread):
"""杀死线程
Args:
thread: 待杀死的线程
"""
_async_raise(thread.ident, SystemExit)
if __name__ == '__main__':
x = transfer_second_to_time("12000.923")
print(x)
x = transfer_second_to_time("79.925")
print(x)
y = trans_to_seconds("1:00:00.92")
print(y)
z = transfer_second_to_time("1200.923")
print(z)
import time
strtime = "1:00:00.92"
# time.strptime(strtime, "%H:%M:%S")
import re
print("------------"*10)
# tests = ["00:12:34a", "01:12:34", "10:12:34.567", "12:12:34.89", "24:12:34.8", "2:34.2", "12:34.", "12:34.0", "02:34.0", "00:34.0"]
tests = ["01:12:34", "10:12:34.567", "12:12:34.89", "24:12:34.8", "2:34.2", "12:12:34.", "01:12:34.0", "02:02:34.0", "00:00:34.0"]
for s in tests:
x = re.match("^(([0-1]\d)|(2[0-4])):[0-5]\d:[0-5]\d(.\d{1,2})?$", s)
print("当前=", s)
print(x)
if x:
print(x.group())
print(x.group(1))
print("---------------------------------")
# print(re.match("^(([0-1]\d)|(2[0-4])):[0-5]\d$", "a12:34"))
# print(re.match("^(([0-1]\d)|(2[0-4])):[0-5]\d$", "a"))
# print(re.match("[^abc]", "plain"))
# print("------------")
# q = re.match("p|(pl)ain", "plain")
# print(q)
# print(q.group())
# print("------------")
# qq = re.match("ya(msen|nsen|nsem)", "yansen’s blog")
# print(qq)
# print(qq.group())
# print(qq.group(1))
\ No newline at end of file
pass
\ No newline at end of file
......@@ -46,7 +46,7 @@ exe = EXE(
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=True,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
......
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