pyvideotrans / videotrans /component /onlyone_set_recogn2.py
jameson512
Upload files via LFS
1f235c3
Raw
History Blame Contribute Delete
11.7 kB
from pathlib import Path
from PySide6.QtCore import Qt, QTimer, QSize, QUrl
from PySide6.QtGui import QIcon, QDesktopServices
from PySide6.QtWidgets import (
QDialog, QVBoxLayout, QHBoxLayout, QLineEdit, QLabel,
QPushButton, QWidget, QTableWidget, QTableWidgetItem, QHeaderView, QAbstractItemView
)
from videotrans.configure.config import ROOT_DIR, tr, settings, logger
from videotrans.util import tools
class EditRecognResultDialog2(QDialog):
def __init__(
self,
parent=None,
target_sub: str = None
):
super().__init__()
self.parent = parent
self.target_sub = target_sub
self.srt_list_dict = tools.get_subtitle_from_srt(self.target_sub)
self.setWindowTitle( tr("Modify secondary recognition results") )
self.setWindowIcon(QIcon(f"{ROOT_DIR}/videotrans/styles/icon.ico"))
self.setMinimumWidth(int(parent.width*0.95))
self.setMinimumHeight(int(parent.height*0.95))
self.setWindowFlags(Qt.Window |
Qt.WindowStaysOnTopHint | # 2. 始终在最顶层
Qt.WindowTitleHint | # 3. 显示标题栏
Qt.CustomizeWindowHint | # 4. 允许自定义标题栏按钮(否则OS会强制加关闭按钮)
Qt.WindowMaximizeButtonHint # 5. 只加最大化按钮,不加关闭按钮
)
self.count_down = int(float(settings.get('countdown_sec', 1)))
main_layout = QVBoxLayout(self)
# Top Bar
hstop = QHBoxLayout()
self.prompt_label = QLabel(tr("jimiaohoufanyi"))
self.prompt_label.setStyleSheet('font-size:14px;color:#aaaaaa')
hstop.addWidget(self.prompt_label)
self.stop_button = QPushButton(f"{tr('Click here to stop the countdown')}({self.count_down})")
self.stop_button.setStyleSheet("font-size: 16px;color:#ffff00")
self.stop_button.setCursor(Qt.PointingHandCursor)
self.stop_button.clicked.connect(self.stop_countdown)
hstop.addWidget(self.stop_button)
main_layout.addLayout(hstop)
prompt_label2 = QLabel(tr("If you need to delete a line of subtitles, just clear the text in that line"))
prompt_label2.setAlignment(Qt.AlignCenter)
main_layout.addWidget(prompt_label2)
# Search Bar
search_replace_layout = QHBoxLayout()
search_replace_layout.addStretch()
self.search_input = QLineEdit()
self.search_input.setPlaceholderText(tr("Original text"))
self.replace_input = QLineEdit()
self.replace_input.setPlaceholderText(tr("Replace"))
replace_button = QPushButton(tr("Replace"))
replace_button.clicked.connect(self.replace_text)
search_replace_layout.addWidget(self.search_input)
search_replace_layout.addWidget(self.replace_input)
search_replace_layout.addWidget(replace_button)
search_replace_layout.addStretch()
main_layout.addLayout(search_replace_layout)
# Loading
self.loading_widget = QWidget()
self.loading_label = QLabel(tr('Loading...'), self)
self.loading_label.setAlignment(Qt.AlignCenter)
load_layout = QVBoxLayout(self.loading_widget)
load_layout.addWidget(self.loading_label)
main_layout.addWidget(self.loading_widget)
# Table Widget (初始隐藏)
self.table = QTableWidget()
self.table.setVisible(False)
main_layout.addWidget(self.table)
# Bottom Bar
self.save_button = QPushButton(tr("nextstep"))
self.save_button.clicked.connect(self.save_and_close)
self.save_button.setCursor(Qt.PointingHandCursor)
self.save_button.setMinimumSize(QSize(300, 35))
self.save_button2 = QPushButton(tr("nosaveandstep"))
self.save_button2.clicked.connect(self.save_and_close2)
self.save_button2.setCursor(Qt.PointingHandCursor)
self.save_button2.setMinimumSize(QSize(200, 35))
cancel_button = QPushButton(tr("Terminate this mission"))
cancel_button.clicked.connect(self.cancel_and_close)
cancel_button.setCursor(Qt.PointingHandCursor)
cancel_button.setMaximumSize(QSize(150, 30))
cancel_button.setStyleSheet("background-color:transparent;color:#ff0")
bottom_layout = QHBoxLayout()
bottom_layout.addStretch()
bottom_layout.addWidget(self.save_button)
bottom_layout.addWidget(self.save_button2)
bottom_layout.addWidget(cancel_button)
bottom_layout.addStretch()
main_layout.addLayout(bottom_layout)
# 延迟加载表格
QTimer.singleShot(10, self.load_table)
def load_table(self):
"""极致性能加载表格"""
if not self.isVisible():
return
try:
# 1. 创建 QTableWidget
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels([tr("Line"), tr("Time Axis"), tr("Subtitle Text")])
# 2. 禁用所有非必要功能
self.table.setShowGrid(False)
self.table.setAlternatingRowColors(False)
self.table.setWordWrap(False)
self.table.setMouseTracking(False)
self.table.setFocusPolicy(Qt.NoFocus)
self.table.setSelectionMode(QAbstractItemView.NoSelection)
# 垂直表头
v_header = self.table.verticalHeader()
v_header.setVisible(False)
v_header.setSectionResizeMode(QHeaderView.Fixed)
v_header.setDefaultSectionSize(22) # 最小行高
# 水平表头
h_header = self.table.horizontalHeader()
h_header.setStretchLastSection(True)
h_header.setSectionResizeMode(0, QHeaderView.Fixed)
h_header.setSectionResizeMode(1, QHeaderView.Fixed)
h_header.setSectionResizeMode(2, QHeaderView.Fixed)
self.table.setColumnWidth(0, 50)
self.table.setColumnWidth(1, 230)
self.table.setStyleSheet("""
QTableWidget {
color: #cccccc;
border: none;
}
QTableWidget::item {
padding: 2px;
border: none;
}
QHeaderView::section {
background-color: #2b2b2b;
color: #dddddd;
border: none;
border-right: 1px solid #3e3e3e;
padding: 3px;
height: 24px;
}
""")
# 3. 预计算所有显示数据
self.display_data = []
for item in self.srt_list_dict:
duration = (item['end_time'] - item['start_time']) / 1000.0
time_str = f"{item['startraw']} --> {item['endraw']} ({duration:.1f}s)"
self.display_data.append({
'line': item['line'],
'time_str': time_str,
'text': item['text'],
'startraw': item['startraw'],
'endraw': item['endraw']
})
# 4. 设置行数
total_rows = len(self.display_data)
self.table.setRowCount(total_rows)
# 5. 【分批填充】先填充前100行,避免界面冻结
self._batch_fill(0, min(total_rows, 100))
# 6. 显示表格
self.loading_widget.setVisible(False)
self.table.setVisible(True)
# 7. 延迟加载剩余数据
if total_rows > 100:
QTimer.singleShot(0, lambda: self._load_remaining(100))
# 8. 启动倒计时
self.timer = QTimer(self)
self.timer.timeout.connect(self.update_countdown)
self.timer.start(1000)
if self.parent:
self.parent.activateWindow()
except Exception as e:
import traceback
traceback.print_exc()
self.loading_label.setText(f"Error: {e}")
def _batch_fill(self, start_row, end_row):
"""批量填充数据"""
for row in range(start_row, end_row):
data = self.display_data[row]
# Line (只读)
item0 = QTableWidgetItem(str(data['line']))
item0.setFlags(Qt.ItemIsEnabled)
self.table.setItem(row, 0, item0)
# Time (只读)
item1 = QTableWidgetItem(data['time_str'])
item1.setFlags(Qt.ItemIsEnabled)
self.table.setItem(row, 1, item1)
# Text (可编辑)
item2 = QTableWidgetItem(data['text'])
item2.setFlags(Qt.ItemIsEnabled | Qt.ItemIsEditable | Qt.ItemIsSelectable)
self.table.setItem(row, 2, item2)
def _load_remaining(self, start_row):
"""延迟加载剩余行"""
total = len(self.display_data)
batch_size = 300 # 每批300行
end_row = min(start_row + batch_size, total)
self._batch_fill(start_row, end_row)
if end_row < total:
QTimer.singleShot(0, lambda: self._load_remaining(end_row))
def cancel_and_close(self):
if hasattr(self, 'timer') and self.timer:
self.timer.stop()
self.reject()
def update_countdown(self):
self.count_down -= 1
if self.stop_button:
self.stop_button.setText(f"{tr('Click here to stop the countdown')}({self.count_down})")
if self.count_down <= 0:
self.timer.stop()
self.save_and_close()
def stop_countdown(self):
if hasattr(self, 'timer'):
self.timer.stop()
self.stop_button.deleteLater()
self.prompt_label.deleteLater()
def replace_text(self):
"""替换文本"""
search_text = self.search_input.text()
replace_text = self.replace_input.text()
if not search_text:
return
# 禁用更新提升性能
self.table.setUpdatesEnabled(False)
first = -1
last = -1
for i, data in enumerate(self.display_data):
if search_text in data['text']:
new_text = data['text'].replace(search_text, replace_text)
data['text'] = new_text
item = self.table.item(i, 2)
if item:
item.setText(new_text)
if first == -1:
first = i
last = i
self.table.setUpdatesEnabled(True)
def save_and_close2(self):
self.accept()
def closeEvent(self, event):
event.ignore() # 忽略关闭请求,窗口保持不动
def save_and_close(self):
self.save_button.setDisabled(True)
srt_str_list = []
# 从表格获取最新文本
for i, data in enumerate(self.display_data):
item = self.table.item(i, 2)
text = item.text().strip() if item else data['text'].strip()
if text:
srt_str_list.append(f'{data["line"]}\n{data["startraw"]} --> {data["endraw"]}\n{text}')
try:
Path(self.target_sub).write_text("\n\n".join(srt_str_list), encoding="utf-8")
except Exception as e:
logger.exception(f"Save error: {e}",exc_info=True)
self.accept()