diff --git a/fasterWhisperGUIConfig.json b/fasterWhisperGUIConfig.json index eb55ab3..425efa7 100644 --- a/fasterWhisperGUIConfig.json +++ b/fasterWhisperGUIConfig.json @@ -39,7 +39,7 @@ }, "Transcription_param": { "aggregate_contents": true, - "language": 2, + "language": 1, "task": false, "beam_size": "5", "best_of": "5", @@ -67,11 +67,11 @@ "tabMovable": false, "tabScrollable": false, "tabShadowEnabled": false, - "tabMaxWidth": 205, + "tabMaxWidth": 366, "closeDisplayMode": 0, - "whisperXMinSpeaker": 3, + "whisperXMinSpeaker": 2, "whisperXMaxSpeaker": 3, - "outputFormat": 0, + "outputFormat": 4, "outputEncoding": 1 } } \ No newline at end of file diff --git a/faster_whisper_GUI/mainWindows.py b/faster_whisper_GUI/mainWindows.py index fea68cc..71bc5c1 100644 --- a/faster_whisper_GUI/mainWindows.py +++ b/faster_whisper_GUI/mainWindows.py @@ -54,6 +54,7 @@ from .fasterWhisperGuiIcon import FasterWhisperGUIIcon from .UI_MainWindows import UIMainWin from .tableModel_segments_path_info import TableModel +from .tableViewInterface import CustomTableView from .whisper_x import WhisperXWorker from .de_mucs import DemucsWorker # from .style_sheet import StyleSheet @@ -463,9 +464,11 @@ def transcribeProcess(self): self.log.write(f"\n=========={dateTime_}==========\n") self.log.write(f"==========Cancel==========\n") - messageBoxW = MessageBox(self.tr("取消") - , self.tr("是否取消操作?") - , self) + messageBoxW = MessageBox( + self.tr("取消") + , self.tr("是否取消操作?") + , self + ) if messageBoxW.exec(): self.page_process.button_process.setEnabled(False) @@ -544,7 +547,8 @@ def createResultInTable(self, results): for result in results: segments, file, _ = result file = file.replace("\\", "/") - table_view_widget = TableView() + + table_view_widget = CustomTableView() table_model = TableModel(segments) self.tableModel_list[file] = table_model diff --git a/faster_whisper_GUI/tableModel_segments_path_info.py b/faster_whisper_GUI/tableModel_segments_path_info.py index ddf56dc..089a9c2 100644 --- a/faster_whisper_GUI/tableModel_segments_path_info.py +++ b/faster_whisper_GUI/tableModel_segments_path_info.py @@ -1,9 +1,11 @@ # coding:utf-8 -from PySide6.QtCore import QAbstractTableModel, Qt +from PySide6.QtCore import QAbstractTableModel, QCoreApplication, Qt # from PySide6.QtWidgets import QStyledItemDelegate, QLineEdit from typing import List +from PySide6.QtWidgets import QApplication from faster_whisper import Word +from qfluentwidgets.components.dialog_box.dialog import MessageBox from .seg_ment import segment_Transcribe from .util import HMSToSeconds, secondsToHMS @@ -13,7 +15,10 @@ class TableModel(QAbstractTableModel): def __init__(self, data:List[tuple]): super(TableModel, self).__init__() self._data = data - + + def __tr(self, text): + return QCoreApplication.translate(self.__class__.__name__, text) + def resetData(self, data): self._data = data @@ -56,10 +61,16 @@ def setData(self, index, value, role): retxt = value.split(":") if len(retxt) > 1: if self._data[row].speaker != retxt[0]: - temp_data_speaker = self._data[row].speaker - for data in self._data: - if data.speaker is not None and data.speaker != "" and data.speaker == temp_data_speaker: - data.speaker = retxt[0] + temp_data_speaker = self._data[row].speaker + self._data[row].speaker = retxt[0] + # 批量修改所有相同说话人 + msgb = MessageBox(self.__tr("提示"), self.__tr("是否修改所有相同说话人?"),QApplication.activeWindow()) + # msgb.show() + if msgb.exec(): + for data in self._data: + if data.speaker is not None and data.speaker != "" and data.speaker == temp_data_speaker: + data.speaker = retxt[0] + self._data[row].speaker = retxt[0] self._data[row].text = ":".join(retxt[1:]) else: diff --git a/faster_whisper_GUI/tableViewInterface.py b/faster_whisper_GUI/tableViewInterface.py index 7a9d881..e0a3641 100644 --- a/faster_whisper_GUI/tableViewInterface.py +++ b/faster_whisper_GUI/tableViewInterface.py @@ -1,20 +1,29 @@ # coding:utf-8 -from PySide6.QtCore import QModelIndex, Qt, Signal -from PySide6.QtGui import QPalette +import time +from PySide6.QtCore import (QCoreApplication, QModelIndex, + Qt, + Signal + ) + +from PySide6.QtGui import ( + QPalette, + QAction + ) + from PySide6.QtWidgets import ( + QApplication, QFrame, QHBoxLayout, QSizePolicy, QStackedWidget - ,QStyleOptionViewItem + , QStyleOptionViewItem , QWidget , QVBoxLayout ) from qfluentwidgets import ( - LineEdit, - qrouter + LineEdit , CheckBox , BodyLabel , StrongBodyLabel @@ -27,7 +36,10 @@ , TableItemDelegate , HorizontalSeparator , Router + , RoundMenu ) +from qfluentwidgets.components.dialog_box.message_box_base import MessageBoxBase +from qfluentwidgets.components.widgets.label import SubtitleLabel from .style_sheet import StyleSheet @@ -125,12 +137,9 @@ def __initWidget(self): self.connectSignalToSlot() def connectSignalToSlot(self): - self.movableCheckBox.stateChanged.connect( - lambda: self.tabBar.setMovable(self.movableCheckBox.isChecked())) - self.scrollableCheckBox.stateChanged.connect( - lambda: self.tabBar.setScrollable(self.scrollableCheckBox.isChecked())) - self.shadowEnabledCheckBox.stateChanged.connect( - lambda: self.tabBar.setTabShadowEnabled(self.shadowEnabledCheckBox.isChecked())) + self.movableCheckBox.stateChanged.connect(lambda: self.tabBar.setMovable(self.movableCheckBox.isChecked())) + self.scrollableCheckBox.stateChanged.connect(lambda: self.tabBar.setScrollable(self.scrollableCheckBox.isChecked())) + self.shadowEnabledCheckBox.stateChanged.connect(lambda: self.tabBar.setTabShadowEnabled(self.shadowEnabledCheckBox.isChecked())) self.tabMaxWidthSpinBox.valueChanged.connect(self.tabBar.setTabMaximumWidth) @@ -181,32 +190,19 @@ def addSubInterface(self ): if widget is None: - widget = TableView() + widget = CustomTableView() widget.setObjectName(objectName) - - # 设置缩放策略 - widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) - - # 设置列宽 - widget.setColumnWidth(0,115) - widget.setColumnWidth(1,115) - widget.setColumnWidth(2,400) - # widget.setColumnWidth(4,50) - - # 设置列宽适应内容 - # widget.resizeColumnsToContents() - # widget.resizeColumnToContents(0) - # widget.resizeColumnToContents(1) - widget.resizeColumnToContents(3) - # widget.resizeColumnToContents(2) # 设置填充可用空间 widget.horizontalHeader().setStretchLastSection(True) # 设置单元格代理 重新设置编辑方式、单元格格式等等 widget.setItemDelegate(CustomTableItemDelegate(widget)) - + + # 设置缩放策略 + widget.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) + # 添加子分页 self.stackedWidget.addWidget(widget) @@ -225,6 +221,18 @@ def addSubInterface(self onClick=lambda: self.stackedWidget.setCurrentWidget(widget) ) + # 设置列宽 + widget.setColumnWidth(0,115) + widget.setColumnWidth(1,115) + widget.setColumnWidth(2,400) + widget.setColumnWidth(4,50) + + # 设置列宽适应内容 + # widget.resizeColumnToContents(0) + # widget.resizeColumnToContents(1) + widget.resizeColumnToContents(3) + widget.resizeColumnToContents(2) + def onDisplayModeChanged(self, index): mode = self.closeDisplayModeComboBox.itemData(index) self.tabBar.setCloseButtonDisplayMode(mode) @@ -249,4 +257,202 @@ def removeTab(self, index): widget.deleteLater() +class CustomTableView(TableView): + def __tr(self, text): + return QCoreApplication.translate(self.__class__.__name__, text) + + def __init__(self, parent=None): + super(CustomTableView, self).__init__(parent) + self.setContextMenuPolicy(Qt.CustomContextMenu) + self.customContextMenuRequested.connect(self.show_context_menu) + self.temp_speaker = "" + + def show_context_menu(self, position): + index = self.indexAt(position) + if index.isValid(): + + indexs = self.selectedIndexes() + rows = list(set([index.row() for index in indexs])) + + # rect = self.visualRect(index) + + # 显示单行内容菜单 + menu = RoundMenu(self) + + copy_action = QAction(self.__tr("复制"), self) + copy_action.setShortcut("Ctrl+C") + menu.addAction(copy_action) + copy_action.triggered.connect(self.copy_all_content) + + copy_subtitle_action = QAction(self.__tr("复制字幕内容"), self) + menu.addAction(copy_subtitle_action) + copy_subtitle_action.triggered.connect(self.copy_subtitle_content) + + if len(rows) > 1: + # 显示多行内容菜单 + + # 修改说话人 + set_speaker_action = QAction(self.__tr("设置说话人"), self) + menu.addAction(set_speaker_action) + set_speaker_action.triggered.connect(self.open_set_speaker_dialog) + + # 合并字幕行 + merge_subtitel_line_action = QAction(self.__tr("合并字幕行"), self) + menu.addAction(merge_subtitel_line_action) + merge_subtitel_line_action.triggered.connect(self.merge_subtitles) + + menu.exec(self.mapToGlobal(position)) + + # 批量合并字幕行 + def merge_subtitles(self): + indexs = self.selectedIndexes() + if len(indexs) < 2: + return + + rows = list(set([index.row() for index in indexs])) + rows.sort() + + merge_line = [] + + for i in range(len(rows)-1): + + row1 = rows[i] + row2 = rows[i+1] + + if row2 == row1+1: + self.merge_subtitle_lines(row1, row2) + else: + merge_line.append(row1) + + if i+1 == len(rows)-1: + merge_line.append(row2) + + subtitle_obj = [] + for row in rows: + if row not in merge_line: + subtitle_obj.append(self.model()._data[row]) + + for obj in subtitle_obj: + self.model()._data.remove(obj) + + # self.setCurrentRow(-1) + + def merge_subtitle_lines(self, row1, row2): + # 合并字幕行 + subtitle_line1 = self.model()._data[row1] + subtitle_line2 = self.model()._data[row2] + + subtitle_line2.start = subtitle_line1.start + subtitle_line2.text = subtitle_line1.text + f",{subtitle_line2.text}" + + subtitle_line2.words = subtitle_line1.words + subtitle_line2.words + + subtitle_line2.speaker = None + + # self.model().setData(self.model.index(row2, 0), subtitle_line2) + # self.model.removeRows(row2, 1) + + def set_temp_speaker(self, speaker): + self.temp_speaker = speaker + # print(f"input speaker:{self.temp_speaker}") + + def open_set_speaker_dialog(self): + + indexs = self.selectedIndexes() + print(len(indexs)) + + esmb = EditSpeakerMessageBox(QApplication.activeWindow()) + esmb.accept_speaker_signal.connect(self.set_temp_speaker) + # time.sleep(1000) + if esmb.exec(): + # print(f"temp speaker:{self.temp_speaker}") + self.set_speaker(indexs) + # self.model().refresh() + + def set_speaker(self, indexs): + + # 获取唯一行号为列表 + rows = list(set(index.row() for index in indexs)) + for row in rows: + self.model()._data[row].speaker = self.temp_speaker + + def copy_all_content(self): + indexs = self.selectedIndexes() + content = "" + + rows = [] + for index in indexs: + if index.row() not in rows: + rows.append(index.row()) + + for row in rows: + content += f"{row+1}\n{self.model().index(row, 0).data()} --> {self.model().index(row, 1).data()}\n{self.model().index(row, 2).data()}\n\n" + + # for column in range(self.model().columnCount()-1): + # row_data.append(self.model().index(index.row(), column).data()) + + QApplication.clipboard().setText(content) + + def copy_subtitle_content(self): + + indexs = self.selectedIndexes() + # index = self.currentIndex() + content = "" + + rows = [] + for index in indexs: + if index.row() not in rows: + rows.append(index.row()) + + for row in rows: + content += f"{self.model().index(row, 2).data()}\n" + # content = index.data() + # content = self.model().index(index.row(), 2).data() + QApplication.clipboard().setText(content) + + def custom_action_triggered(self): + index = self.currentIndex() + # 在这里执行自定义操作 + print(f"Custom action triggered on row {index.row()} and column {index.column()}") + +class EditSpeakerMessageBox(MessageBoxBase): + """ Custom message box """ + + accept_speaker_signal = Signal(str) + + def __init__(self, parent=None): + super().__init__(parent) + self.value = "" + + self.titleLabel = SubtitleLabel(self.tr('说话人'), self) + self.speakerLineEdit = LineEdit(self) + + self.speakerLineEdit.setPlaceholderText(self.tr('输入说话人')) + self.speakerLineEdit.setClearButtonEnabled(True) + + # add widget to view layout + self.viewLayout.addWidget(self.titleLabel) + self.viewLayout.addWidget(self.speakerLineEdit) + + # change the text of button + self.yesButton.setText(self.tr('确定')) + self.cancelButton.setText(self.tr('取消')) + + self.widget.setMinimumWidth(350) + self.yesButton.setDisabled(True) + self.speakerLineEdit.textChanged.connect(self._validateUrl) + + # self.hideYesButton() + + def _validateUrl(self, text): + print("text:",text) + self.yesButton.setEnabled(text != "") + self.value=text + self.accept_speaker_signal.emit(self.value) + def exec(self) -> int: + # print("value:",self.value) + # self.value = self.speakerLineEdit.text() + self.accept_speaker_signal.emit(self.value) + return super().exec() + \ No newline at end of file diff --git a/faster_whisper_GUI/version.py b/faster_whisper_GUI/version.py index 73c30b5..aa50090 100644 --- a/faster_whisper_GUI/version.py +++ b/faster_whisper_GUI/version.py @@ -1,6 +1,7 @@ # coding:utf-8 -__version__ = "0.6.0" +__version__ = "0.6.7" __FasterWhisper_version__ = "1.0.1" __WhisperX_version__ = "3.1.1" __Demucs_version__ = "v4.0" +