class VideoMediaView_(MediaView): def __init__(self, media, parent): super(VideoMediaView_, self).__init__(media, parent) self.widget = QVideoWidget(parent) self.player = QMediaPlayer() self.playlist = QMediaPlaylist() self.player.setPlaylist(self.playlist) self.player.setVideoOutput(self.widget) self.widget.setGeometry(media['geometry']) self.set_default_widget_prop() self.widget.setDisabled(True) @Slot() def play(self): self.finished = 0 path = '%s/%s' % (self.save_dir, self.options['uri']) self.playlist.addMedia(QMediaContent(path)) self.player.play() self.widget.show() self.widget.raise_() if float(self.duration) > 0: self.play_timer.setInterval(int(float(self.duration) * 1000)) self.play_timer.start() self.started_signal.emit() @Slot() def stop(self, delete_widget=False): #---- kong ---- if not self.widget: return False self.player.stop() self.playlist = None self.player = None super(VideoMediaView_, self).stop(delete_widget) return True
class EditorWidget(QWidget): """Widget which contain the editor.""" def __init__(self, plugin_manager): super(EditorWidget, self).__init__() os.environ[ 'QT_MULTIMEDIA_PREFERRED_PLUGINS'] = 'windowsmediafoundation' self.plugin_manager = plugin_manager #parent layout self.v_box = QVBoxLayout() self.h_box = QHBoxLayout() # parent splitter for the text and numbers self.text_h_box = QSplitter(Qt.Horizontal) self.text_h_box.splitterMoved.connect(self.on_text_changed) self.settings = QSettings(c.SETTINGS_PATH, QSettings.IniFormat) self.keyboard_settings = QSettings(c.KEYBOARD_SETTINGS_PATH, QSettings.IniFormat) self.theme = self.settings.value(c.THEME, defaultValue=c.THEME_D) # font settings self.font = QFont( self.settings.value(c.FONT, defaultValue="Arial", type=str)) self.font.setPointSize( self.settings.value(c.FONT_SIZE, defaultValue=16, type=int)) # the text widget itself self.text = QPlainTextEdit() self.text.setFont(self.font) self.text.textChanged.connect(self.on_text_changed) self.text.setFocusPolicy(Qt.StrongFocus) # the number text widget to show the row numbers self.numbers = QPlainTextEdit() self.numbers.setFont(self.font) self.numbers.setReadOnly(True) self.numbers.setMinimumWidth(20) self.numbers.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.numbers.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.numbers.setLineWrapMode(QPlainTextEdit.NoWrap) self.numbers.setFocusPolicy(Qt.NoFocus) self.numbers.setFrameStyle(QFrame.NoFrame) self.numbers.setStyleSheet("background-color: rgba(0,0,0,0%)") # sync the text widget and number widget self.text_bar = self.text.verticalScrollBar() self.number_bar = self.numbers.verticalScrollBar() #self.number_bar.valueChanged.connect(self.text_bar.setValue) self.text_bar.valueChanged.connect(self.number_bar.setValue) # add them into their layout self.text_h_box.addWidget(self.numbers) self.text_h_box.addWidget(self.text) self.text_h_box.setSizes([10, 700]) # layout which holds the media controls in the bottom self.media_controls = QHBoxLayout() self.media_controls_settings = QVBoxLayout() self.media_controls_slider_h_box = QHBoxLayout() # direct player controls self.btn_size = 75 self.play_icon = QIcon( os.path.join(c.ICON_PATH, self.theme, "play.png")) self.pause_icon = QIcon( os.path.join(c.ICON_PATH, self.theme, "pause.png")) self.play_btn = QPushButton(icon=self.play_icon) self.play_btn.clicked.connect(self.on_play) self.play_btn.setFixedSize(self.btn_size, self.btn_size) self.play_btn.setIconSize(QSize(self.btn_size, self.btn_size)) self.play_btn.setFlat(True) self.play_btn.setShortcut(QKeySequence().fromString( self.keyboard_settings.value(c.PLAY_PAUSE_KEY, defaultValue=""))) self.forward_btn = QPushButton( icon=QIcon(os.path.join(c.ICON_PATH, self.theme, "forward.png"))) self.forward_btn.clicked.connect(self.on_forward) self.forward_btn.setFixedSize(self.btn_size, self.btn_size) self.forward_btn.setIconSize(QSize(self.btn_size, self.btn_size)) self.forward_btn.setFlat(True) self.forward_btn.setShortcut(QKeySequence().fromString( self.keyboard_settings.value(c.FORWARD_KEY, defaultValue=""))) self.backward_btn = QPushButton( icon=QIcon(os.path.join(c.ICON_PATH, self.theme, "backward.png"))) self.backward_btn.clicked.connect(self.on_backward) self.backward_btn.setFixedSize(self.btn_size, self.btn_size) self.backward_btn.setIconSize(QSize(self.btn_size, self.btn_size)) self.backward_btn.setFlat(True) self.backward_btn.setShortcut(QKeySequence().fromString( self.keyboard_settings.value(c.BACKWARDS_KEY, defaultValue=""))) # add them to the layout self.media_controls.addStretch() self.media_controls.addWidget(self.backward_btn) self.media_controls.addWidget(self.play_btn) self.media_controls.addWidget(self.forward_btn) self.media_controls.addStretch(4) # slider which shows the current time self.time_slider = QSlider(Qt.Horizontal) self.time_slider.sliderMoved.connect(self.on_time_slider_moved) # label on the right of the slider, which shows the current time self.time_label = QLabel("00:00/00:00") self.media_controls_slider_h_box.addWidget(self.time_slider) self.media_controls_slider_h_box.addWidget(self.time_label) # icons for the other sliders self.vol_icon = QIcon( os.path.join(c.ICON_PATH, self.theme, "volume.png")).pixmap(QSize(32, 32)) self.rate_icon = QIcon( os.path.join(c.ICON_PATH, self.theme, "playbackrate.png")).pixmap(QSize(32, 32)) self.rewind_icon = QIcon( os.path.join(c.ICON_PATH, self.theme, "time.png")).pixmap(QSize(32, 32)) # display the icons through labels self.vol_icon_label = QLabel() self.vol_icon_label.setPixmap(self.vol_icon) self.rate_icon_label = QLabel() self.rate_icon_label.setPixmap(self.rate_icon) self.rewind_rewind_label = QLabel() self.rewind_rewind_label.setPixmap(self.rewind_icon) # init of the other sliders self.vol_slider = QSlider(Qt.Horizontal) self.vol_slider.sliderMoved.connect(self.on_vol_slider_moved) self.vol_slider.setFixedWidth(250) self.vol_slider.setRange(1, 100) self.rate_slider = QSlider(Qt.Horizontal) self.rate_slider.sliderMoved.connect(self.on_rate_slider_moved) self.rate_slider.setFixedWidth(250) self.rate_slider.setRange(1, 20) self.rewind_time = 10 self.rewind_slider = QSlider(Qt.Horizontal) self.rewind_slider.sliderMoved.connect(self.on_rewind_slider_moved) self.rewind_slider.setFixedWidth(250) self.rewind_slider.setRange(1, 60) self.rewind_slider.setValue(self.rewind_time) # labels for the values self.vol_label = QLabel() self.rate_label = QLabel() self.rewind_label = QLabel() # create hbox for each of the three sliders self.vol_h_box = QHBoxLayout() self.vol_h_box.addWidget(self.vol_label) self.vol_h_box.addWidget(self.vol_slider) self.vol_h_box.addWidget(self.vol_icon_label) self.rate_h_box = QHBoxLayout() self.rate_h_box.addWidget(self.rate_label) self.rate_h_box.addWidget(self.rate_slider) self.rate_h_box.addWidget(self.rate_icon_label) self.rewind_h_box = QHBoxLayout() self.rewind_h_box.addWidget(self.rewind_label) self.rewind_h_box.addWidget(self.rewind_slider) self.rewind_h_box.addWidget(self.rewind_rewind_label) # group them together in a vlayout self.media_controls_settings.addLayout(self.vol_h_box) self.media_controls_settings.addLayout(self.rewind_h_box) self.media_controls_settings.addLayout(self.rate_h_box) # add this layout to the layout which already contains the buttons self.media_controls.addLayout(self.media_controls_settings) self.word_by_word_actions = QListWidget() self.word_by_word_actions.setMaximumWidth(150) self.h_box.addWidget(self.text_h_box) self.h_box.addWidget(self.word_by_word_actions) # group all ungrouped layouts and widgets to the parent layout self.v_box.addLayout(self.h_box, 10) self.v_box.addLayout(self.media_controls_slider_h_box, 1) self.v_box.addLayout(self.media_controls, 1) # set parent layout self.setLayout(self.v_box) # init media_player self.media_player = QMediaPlayer() self.video_widget = QVideoWidget() self.video_widget.setGeometry(200, 200, 500, 300) self.video_widget.setWindowTitle("Output") self.media_player.setVideoOutput(self.video_widget) self.media_player.positionChanged.connect(self.on_position_change) self.media_player.durationChanged.connect(self.on_duration_change) self.vol_slider.setValue(self.media_player.volume()) self.rate_slider.setValue(int(self.media_player.playbackRate() * 10)) self.on_vol_slider_moved(self.media_player.volume()) self.on_rate_slider_moved(self.media_player.playbackRate() * 10) self.on_rewind_slider_moved(self.rewind_time) self.activate_text_modules = False self.get_text_modules() self.text_option_on = QTextOption() self.text_option_on.setFlags( QTextOption.ShowTabsAndSpaces | QTextOption.ShowLineAndParagraphSeparators) self.text_option_off = QTextOption() self.transcription_meta_data = None self.word_pos = -1 self.word_start_time = None self.word_end_time = None self.tcf_highlight = QTextCharFormat() self.tcf_highlight.setBackground(Qt.red) self.tcf_normal = QTextCharFormat() self.tcf_normal.setBackground(Qt.transparent) self.show_empty_buttons = self.settings.value(c.SHOW_EMPTY_BUTTONS, defaultValue=True, type=bool) def on_position_change(self, position): """Is executed when media is played (position is changed) Args: position: Current position (ms) of the media player. """ self.time_slider.setValue(position) self.time_label.setText( create_time_string(position, self.media_player.duration())) if self.word_end_time is None: return if position > self.word_end_time: self.on_play() self.word_start_time = None self.word_end_time = None def on_duration_change(self, duration): """Is executed when duration of the media changes. Args: duration: duration of the media. """ self.time_slider.setRange(0, duration) self.time_label.setText( create_time_string(0, self.media_player.duration())) def on_time_slider_moved(self, value): """Is executed when the time slider was moved. Args: value: current value of the slider. """ self.media_player.setPosition(value) def on_vol_slider_moved(self, value): """Is executed when the volume slider is moved. Args: value: current value of the slider. """ self.media_player.setVolume(value) self.vol_label.setText(str(value) + "%") def on_rate_slider_moved(self, value): """Is executed when the rate slider is moved. Args: value: current value of the slider. """ self.media_player.setPlaybackRate(value / 10) self.rate_label.setText(str(value / 10) + "x") def on_rewind_slider_moved(self, value): """Is executed when the rewind slider is moved. Args: value: current value of the slider. """ self.rewind_time = value self.rewind_label.setText(str(value) + "s") def on_play(self): """Is executed when the play or pause button is pressed.""" if self.media_player.state() == QMediaPlayer.PlayingState: self.media_player.pause() self.play_btn.setIcon(self.play_icon) else: self.media_player.play() self.play_btn.setIcon(self.pause_icon) def on_forward(self): """Is executed when the forward button is pressed.""" self.media_player.setPosition(self.media_player.position() + self.rewind_time * 1000) def on_backward(self): """Is executed when the backward button is pressed.""" self.media_player.setPosition(self.media_player.position() - self.rewind_time * 1000) def on_text_changed(self): """Is executed when the text changed Calculates the line numbers and sets the text modules if activated. """ lines = int( self.text.document().documentLayout().documentSize().height()) self.numbers.setPlainText("") text = "" for i in range(1, lines + 1): text = text + str(i) + "\n" self.numbers.setPlainText(text) self.number_bar.setSliderPosition(self.text_bar.sliderPosition()) new_text = self.text.toPlainText() if self.activate_text_modules == True: for key in self.text_modules.keys(): to_replace = " " + key + " " to_replace_with = " " + self.text_modules[key] + " " new_text = new_text.replace(to_replace, to_replace_with) if self.text.toPlainText() != new_text: old_pos = self.text.textCursor().position() self.text.setPlainText(new_text) cursor = self.text.textCursor() cursor.setPosition(old_pos, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord) cursor.movePosition(QTextCursor.NextCharacter) self.text.setTextCursor(cursor) def show_video(self): """Shows or hides the video feed.""" if self.video_widget.isVisible(): self.video_widget.hide() else: self.video_widget.show() def open_project(self, project_folder_path): """Opens a project. Args: project_folder_path: folder of the project which should be opened. """ self.project_folder_path = project_folder_path self.media_file = file_util.get_file(self.project_folder_path, c.CON_COPY_POSTFIX) if self.media_file is None: self.hide() return self.media_player.setMedia( QMediaContent(QUrl.fromLocalFile(self.media_file))) self.transcription_path = file_util.get_file(self.project_folder_path, c.TRANSCRIPTION) if self.transcription_path is None: self.hide() return with open(self.transcription_path, 'r') as f: text = f.read() self.text.setPlainText(text) self.transcription_meta_data = file_util.get_value_from_shelve( self.project_folder_path, c.TRANSCRIPTION_META_DATA) print(self.transcription_meta_data) def change_font(self, new_font, new_size): """Changes the font. Args: new_font: Name of the new font. new_size: New font size. """ self.font = QFont(new_font) self.font.setPointSize(int(new_size)) self.text.setFont(self.font) self.numbers.setFont(self.font) self.settings.setValue(c.FONT_SIZE, int(new_size)) self.settings.setValue(c.FONT, new_font) def get_text_modules(self): """Gets the saved text_modules from the settings.""" self.text_modules = self.settings.value(c.TEXT_MODULES, defaultValue={}) def show_special_characters(self, bol): """Displays the special characters. Args: bol: true or false. """ if bol: self.text.document().setDefaultTextOption(self.text_option_on) else: self.text.document().setDefaultTextOption(self.text_option_off) def on_word_by_word(self): """Selects the next or first word in the on word by word editing mode. For that purpose th word_postion is increased and the next word is marked via the textcursor. If everything works correctly the population of the list will be started. """ self.word_pos += 1 #if self.media_player.state() == QMediaPlayer.PlayingState: # return if self.word_pos > len(self.text.toPlainText().split()) - 1: self.reset_word_by_word() return cursor = self.text.textCursor() if self.word_pos == 0: self.show_empty_buttons = self.settings.value(c.SHOW_EMPTY_BUTTONS, defaultValue=True, type=bool) cursor.setPosition(QTextCursor.Start, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfWord, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) self.text.setEnabled(False) else: cursor.movePosition(QTextCursor.NextWord, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) self.text.setTextCursor(cursor) selected_word = cursor.selectedText() if not selected_word: self.word_pos -= 1 self.on_word_by_word() return # change to find all meta data meta_data_with_word = self.find_meta_data(selected_word) self.populate_word_actions(selected_word, meta_data_with_word) def on_word_by_word_prev(self): """Same as word for word but selects to the previous word.""" if self.word_pos < 1: return self.word_pos -= 2 cursor = self.text.textCursor() count = 0 cursor.setPosition(QTextCursor.Start, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfWord, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) while count < self.word_pos: cursor.movePosition(QTextCursor.NextWord, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.KeepAnchor) count += 1 self.text.setTextCursor(cursor) self.on_word_by_word() def reset_word_by_word(self): """Resets the word by word editing mode and goes back to the normal editing.""" self.word_pos = -1 self.play_to = -1 self.text.setEnabled(True) self.word_by_word_actions.clear() cleaned = self.text.textCursor() cleaned.clearSelection() self.text.setTextCursor(cleaned) def populate_word_actions(self, selected, word_meta_data): """Calls the plugin_manager to get alle the word for word buttons and initalize the hear again buttons. Args: selected: The selected word. word_meta_data: The meta_data fr the word. """ self.word_by_word_actions.clear() if self.word_pos == len(self.text.toPlainText().split()): return self.plugin_manager.get_word_by_word_actions(selected, word_meta_data, self.word_pos) btns = [] for meta_data in word_meta_data: media_btn = HearButton(self, meta_data) btns.append(media_btn) self.add_new_word_by_word_action(btns, "Hear again", selected, self.word_pos) def add_new_word_by_word_action(self, btns, name, word, word_pos): """Adds a new word by word action. Args: btns: The buttons to add. name: The (plugin-)name of the buttons. word: The word for which these buttons are. word_pos: The word position. """ if not self.show_empty_buttons and len(btns) == 0: return if self.word_pos != word_pos: print("old item", word, word_pos, self.word_pos) return group_item = QListWidgetItem() group_item.setFlags(Qt.ItemIsSelectable) label = QLabel(name) label.setFixedSize(self.word_by_word_actions.width() - 15, 30) label.setContentsMargins(5, 0, 0, 0) label.setWordWrap(True) group_item.setSizeHint(label.size()) self.word_by_word_actions.addItem(group_item) self.word_by_word_actions.setItemWidget(group_item, label) for btn in btns: btn.setFixedSize(self.word_by_word_actions.width() - 15, 30) item = QListWidgetItem() item.setSizeHint(btn.size()) item.setFlags(Qt.ItemIsSelectable) self.word_by_word_actions.addItem(item) self.word_by_word_actions.setItemWidget(item, btn) def find_meta_data(self, word): """Gets all the meta_data for the given word. Args: word: The word for which the meta_data should be found. Returns: The meta_data """ meta_data_with_word = [] for m_d in self.transcription_meta_data: if m_d.get(c.WORD) == word.lower(): meta_data_with_word.append(m_d) return meta_data_with_word def replace_selection(self, new_word): """Replace the selection with the given word Args: new_word: The replacement. """ cursor = self.text.textCursor() old_cursor_pos = cursor.position() cursor.insertText(new_word) cursor.setPosition(old_cursor_pos, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.EndOfWord, QTextCursor.MoveAnchor) self.text.setTextCursor(cursor) self.word_by_word_actions.clear() def get_selection(self): """Returns the current selection Returns: The current selection. """ return self.text.textCursor().selectedText() def get_text(self): """Returns the current text Returns: The current text. """ return self.text.toPlainText() def set_text(self, new_text, restore_line_breaks=False): """Replace the text with the new text. Args: new_text: The new text. restore_line_breaks: If true, tries to restore the line breaks. (Default value = False) """ cursor = self.text.textCursor() old_cursor_pos = cursor.position() if restore_line_breaks: self.set_text_with_line_breaks(new_text) else: self.text.setPlainText(new_text) cursor.setPosition(old_cursor_pos, QTextCursor.MoveAnchor) self.text.setTextCursor(cursor) def get_word_at(self, pos): """Returns the word at the given position. Args: pos: The position of the word. Returns: The word at the given position. """ text = self.text.toPlainText().strip().split() if pos < 0 or pos > len(text): return None return text[pos % len(text)] def set_word_at(self, word, pos, replace_old): """Sets the word at the given position. Args: word: The replacement. pos: The position. replace_old: If true, the old word at the position will be replaced, otherwise the word will be set before the old word. """ old_word = self.get_word_at(pos) cursor = self.text.textCursor() cursor_pos = cursor.position() if pos < 0: self.text.setPlainText(word + " " + self.text.toPlainText()) cursor.setPosition(cursor_pos, QTextCursor.MoveAnchor) self.text.setTextCursor(cursor) return text = self.text.toPlainText().strip().split() if replace_old and pos < len(text): if word: text[pos] = word else: text.pop(pos) else: text.insert(pos, word) text = " ".join(text) self.set_text_with_line_breaks(text) cursor_pos += len(word) if replace_old: cursor_pos -= len(old_word) if not word: cursor_pos -= 1 else: cursor_pos += 1 words_to_cursor_pos = self.text.toPlainText()[:cursor_pos].split() self.word_pos = len(words_to_cursor_pos) - 1 cursor.setPosition(cursor_pos, QTextCursor.MoveAnchor) cursor.movePosition(QTextCursor.StartOfWord, QTextCursor.MoveAnchor) self.text.setTextCursor(cursor) def find_line_breaks(self): """Returns the lien breaks in the text. Returns: The positions of the linebreaks """ found = [] start = 0 text = self.text.toPlainText() while True: start = text.find("\n", start) if start == -1: return found found.append(start) start += len("\n") def set_text_with_line_breaks(self, text): """Sets the text with linebreaks. Args: text: the new text. """ line_breaks = self.find_line_breaks() for n in line_breaks: text = text[:n + 1] + "\n" + text[n + 1:] text = text.replace(" \n", "\n") text = text.replace("\n ", "\n") self.text.setPlainText(text) def insert_time_stamp(self): """Inserts the current timestamp at the current cursor position.""" cursor = self.text.textCursor() time = "[" + convert_ms(self.media_player.position()) + "]" cursor.insertText(time) def start_hear_again(self, start_time, end_time): """Starts the audio for the specific word from the hear again button. Args: start_time: When to start the audio. end_time: When to end the audio. """ if self.media_player.state() == QMediaPlayer.PlayingState: return self.media_player.pause() self.word_start_time = start_time self.word_end_time = end_time self.media_player.setPosition(self.word_start_time) self.on_play()