From 736cfbc9b5371ce3138ba06d8091a88758590b1c Mon Sep 17 00:00:00 2001
From: QualCoder Developer
Date: Wed, 10 Jun 2026 18:34:29 +1000
Subject: [PATCH 1/3] Added buttons for bookmarks
---
src/GUI_UIs/ui_dialog_code_av.ui | 19 ++++++++++-
src/GUI_UIs/ui_dialog_view_av.ui | 56 +++++++++++++++++++++++++++-----
2 files changed, 65 insertions(+), 10 deletions(-)
diff --git a/src/GUI_UIs/ui_dialog_code_av.ui b/src/GUI_UIs/ui_dialog_code_av.ui
index c59ae89a5..12be1385d 100644
--- a/src/GUI_UIs/ui_dialog_code_av.ui
+++ b/src/GUI_UIs/ui_dialog_code_av.ui
@@ -495,7 +495,7 @@
- 120
+ 150
0
28
28
@@ -508,6 +508,23 @@
+
+
+
+ 120
+ 1
+ 28
+ 28
+
+
+
+ Go to bookmark (Shift B)
+To set a bookmark press B
+
+
+
+
+
diff --git a/src/GUI_UIs/ui_dialog_view_av.ui b/src/GUI_UIs/ui_dialog_view_av.ui
index 32bccff37..73e047ccf 100644
--- a/src/GUI_UIs/ui_dialog_view_av.ui
+++ b/src/GUI_UIs/ui_dialog_view_av.ui
@@ -307,7 +307,7 @@ Forward 5 seconds. Press 5
- 890
+ 940
10
28
28
@@ -382,7 +382,7 @@ red underline = Assigned to code or annotation
- <html><head/><body><p>Search for text.</p></body></html>
+ Search for text
@@ -467,14 +467,17 @@ red underline = Assigned to code or annotation
- 480
+ 470
3
- 161
+ 30
30
+
+ New speaker (Ctrl N)
+
- New speaker
+
@@ -487,7 +490,7 @@ red underline = Assigned to code or annotation
- Insert timestamp
+ Insert timestamp (Ctrl T)
@@ -496,14 +499,17 @@ red underline = Assigned to code or annotation
- 650
+ 500
3
- 161
+ 30
30
+
+ Remove speaker (Ctrl D)
+
- Remove Speaker
+
@@ -550,6 +556,38 @@ red underline = Assigned to code or annotation
+
+
+
+ 650
+ 3
+ 30
+ 30
+
+
+
+ Set bookmark (Ctrl B)
+
+
+
+
+
+
+
+
+ 620
+ 3
+ 30
+ 30
+
+
+
+ Go to bookmark (Ctrl Shift B)
+
+
+
+
+
-
From a380776850134d52c6709279f937d9f5ba96694c Mon Sep 17 00:00:00 2001
From: QualCoder Developer
Date: Wed, 10 Jun 2026 18:35:14 +1000
Subject: [PATCH 2/3] Added buttons for bookmarks
---
src/qualcoder/GUI/ui_dialog_code_av.py | 8 +++++++-
src/qualcoder/GUI/ui_dialog_view_av.py | 26 +++++++++++++++++++-------
2 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/src/qualcoder/GUI/ui_dialog_code_av.py b/src/qualcoder/GUI/ui_dialog_code_av.py
index e015a1c7c..d3b3b7918 100644
--- a/src/qualcoder/GUI/ui_dialog_code_av.py
+++ b/src/qualcoder/GUI/ui_dialog_code_av.py
@@ -146,9 +146,13 @@ def setupUi(self, Dialog_code_av):
self.pushButton_file_attributes.setText("")
self.pushButton_file_attributes.setObjectName("pushButton_file_attributes")
self.pushButton_clear_filter_file = QtWidgets.QPushButton(parent=self.groupBox_file_buttons)
- self.pushButton_clear_filter_file.setGeometry(QtCore.QRect(120, 0, 28, 28))
+ self.pushButton_clear_filter_file.setGeometry(QtCore.QRect(150, 0, 28, 28))
self.pushButton_clear_filter_file.setText("")
self.pushButton_clear_filter_file.setObjectName("pushButton_clear_filter_file")
+ self.pushButton_goto_bookmark = QtWidgets.QPushButton(parent=self.groupBox_file_buttons)
+ self.pushButton_goto_bookmark.setGeometry(QtCore.QRect(120, 1, 28, 28))
+ self.pushButton_goto_bookmark.setText("")
+ self.pushButton_goto_bookmark.setObjectName("pushButton_goto_bookmark")
self.treeWidget = QtWidgets.QTreeWidget(parent=self.splitter)
self.treeWidget.setObjectName("treeWidget")
self.treeWidget.headerItem().setText(0, "Codes")
@@ -235,6 +239,8 @@ def retranslateUi(self, Dialog_code_av):
self.pushButton_document_memo.setToolTip(_translate("Dialog_code_av", "
File memo
"))
self.pushButton_file_attributes.setToolTip(_translate("Dialog_code_av", "Show files with selected file attributes"))
self.pushButton_clear_filter_file.setToolTip(_translate("Dialog_code_av", "Clear file filter"))
+ self.pushButton_goto_bookmark.setToolTip(_translate("Dialog_code_av", "Go to bookmark (Shift B)\n"
+"To set a bookmark press B"))
self.pushButton_clear_filter_code.setToolTip(_translate("Dialog_code_av", "Clear code filter"))
self.lineEdit_code_filter.setToolTip(_translate("Dialog_code_av", "Code name filter"))
self.plainTextEdit.setToolTip(_translate("Dialog_code_av", " Transcript
"))
diff --git a/src/qualcoder/GUI/ui_dialog_view_av.py b/src/qualcoder/GUI/ui_dialog_view_av.py
index 952e362e3..805e981d8 100644
--- a/src/qualcoder/GUI/ui_dialog_view_av.py
+++ b/src/qualcoder/GUI/ui_dialog_view_av.py
@@ -94,7 +94,7 @@ def setupUi(self, Dialog_view_av):
self.pushButton_forward_30.setText("")
self.pushButton_forward_30.setObjectName("pushButton_forward_30")
self.pushButton_help = QtWidgets.QPushButton(parent=self.groupBox_2)
- self.pushButton_help.setGeometry(QtCore.QRect(890, 10, 28, 28))
+ self.pushButton_help.setGeometry(QtCore.QRect(940, 10, 28, 28))
self.pushButton_help.setText("")
self.pushButton_help.setObjectName("pushButton_help")
self.gridLayout.addWidget(self.groupBox_2, 3, 0, 1, 1)
@@ -136,14 +136,16 @@ def setupUi(self, Dialog_view_av):
self.label_search_totals.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
self.label_search_totals.setObjectName("label_search_totals")
self.pushButton_new_speaker = QtWidgets.QPushButton(parent=self.groupBox_search_text)
- self.pushButton_new_speaker.setGeometry(QtCore.QRect(480, 3, 161, 30))
+ self.pushButton_new_speaker.setGeometry(QtCore.QRect(470, 3, 30, 30))
+ self.pushButton_new_speaker.setText("")
self.pushButton_new_speaker.setObjectName("pushButton_new_speaker")
self.pushButton_insert_timestamp = QtWidgets.QPushButton(parent=self.groupBox_search_text)
self.pushButton_insert_timestamp.setGeometry(QtCore.QRect(440, 3, 30, 30))
self.pushButton_insert_timestamp.setText("")
self.pushButton_insert_timestamp.setObjectName("pushButton_insert_timestamp")
self.pushButton_remove_speaker = QtWidgets.QPushButton(parent=self.groupBox_search_text)
- self.pushButton_remove_speaker.setGeometry(QtCore.QRect(650, 3, 161, 30))
+ self.pushButton_remove_speaker.setGeometry(QtCore.QRect(500, 3, 30, 30))
+ self.pushButton_remove_speaker.setText("")
self.pushButton_remove_speaker.setObjectName("pushButton_remove_speaker")
self.checkBox_case_sensitive = QtWidgets.QCheckBox(parent=self.groupBox_search_text)
self.checkBox_case_sensitive.setGeometry(QtCore.QRect(38, 7, 21, 24))
@@ -155,6 +157,14 @@ def setupUi(self, Dialog_view_av):
self.label_case_sensitive.setMaximumSize(QtCore.QSize(28, 28))
self.label_case_sensitive.setText("")
self.label_case_sensitive.setObjectName("label_case_sensitive")
+ self.pushButton_set_bookmark = QtWidgets.QPushButton(parent=self.groupBox_search_text)
+ self.pushButton_set_bookmark.setGeometry(QtCore.QRect(650, 3, 30, 30))
+ self.pushButton_set_bookmark.setText("")
+ self.pushButton_set_bookmark.setObjectName("pushButton_set_bookmark")
+ self.pushButton_goto_bookmark = QtWidgets.QPushButton(parent=self.groupBox_search_text)
+ self.pushButton_goto_bookmark.setGeometry(QtCore.QRect(620, 3, 30, 30))
+ self.pushButton_goto_bookmark.setText("")
+ self.pushButton_goto_bookmark.setObjectName("pushButton_goto_bookmark")
self.gridLayout.addWidget(self.groupBox_search_text, 6, 0, 1, 1)
self.label_note = QtWidgets.QLabel(parent=Dialog_view_av)
self.label_note.setText("")
@@ -208,16 +218,18 @@ def retranslateUi(self, Dialog_view_av):
self.label_transcription.setText(_translate("Dialog_view_av", "Transcription:"))
self.label_speakers.setToolTip(_translate("Dialog_view_av", "Add a speaker name to shortcuts. In the text entry box press ctrl + n
Insert a speaker into transcription. In the text entry box press ctrl + 1 up to ctrl + 8 for the speakers name.
"))
self.label_speakers.setText(_translate("Dialog_view_av", "Speakers:"))
- self.lineEdit_search.setToolTip(_translate("Dialog_view_av", "Search for text.
"))
+ self.lineEdit_search.setToolTip(_translate("Dialog_view_av", "Search for text"))
self.pushButton_next.setToolTip(_translate("Dialog_view_av", "Next"))
self.label_search_regex.setToolTip(_translate("Dialog_view_av", "
Search uses Regex functions.
A dot ‘.’ is used as a wild card, e.g. ‘.ears’ will match ‘bears’ and ‘years’.
A ‘?’ after a character will match one or none times that character, e.g. ‘bears?’ will match ‘bear’ and ‘bears’
A ‘*’ after a character will match zero or more times.
‘\\. will match the dot symbol, ‘\\?’ will match the question mark. ‘\\n’ will match the line ending symbol.
Regex cheatsheet: www.rexegg.com/regex-quickstart.html
"))
self.pushButton_previous.setToolTip(_translate("Dialog_view_av", "Previous"))
self.label_search_totals.setText(_translate("Dialog_view_av", "0 / 0"))
- self.pushButton_new_speaker.setText(_translate("Dialog_view_av", "New speaker"))
- self.pushButton_insert_timestamp.setToolTip(_translate("Dialog_view_av", "Insert timestamp"))
- self.pushButton_remove_speaker.setText(_translate("Dialog_view_av", "Remove Speaker"))
+ self.pushButton_new_speaker.setToolTip(_translate("Dialog_view_av", "New speaker (Ctrl N)"))
+ self.pushButton_insert_timestamp.setToolTip(_translate("Dialog_view_av", "Insert timestamp (Ctrl T)"))
+ self.pushButton_remove_speaker.setToolTip(_translate("Dialog_view_av", "Remove speaker (Ctrl D)"))
self.checkBox_case_sensitive.setToolTip(_translate("Dialog_view_av", "Case sensitive"))
self.label_case_sensitive.setToolTip(_translate("Dialog_view_av", "Case sensitive"))
+ self.pushButton_set_bookmark.setToolTip(_translate("Dialog_view_av", "Set bookmark (Ctrl B)"))
+ self.pushButton_goto_bookmark.setToolTip(_translate("Dialog_view_av", "Go to bookmark (Ctrl Shift B)"))
if __name__ == "__main__":
From b2091d700ebd6a2373bcf539defebb939d7a8734 Mon Sep 17 00:00:00 2001
From: QualCoder Developer
Date: Wed, 10 Jun 2026 18:35:48 +1000
Subject: [PATCH 3/3] Use buttons for bookmarks
---
src/qualcoder/view_av.py | 218 ++++++++++++++++++++++-----------------
1 file changed, 126 insertions(+), 92 deletions(-)
diff --git a/src/qualcoder/view_av.py b/src/qualcoder/view_av.py
index ff1da12bd..df2a58cd4 100644
--- a/src/qualcoder/view_av.py
+++ b/src/qualcoder/view_av.py
@@ -133,6 +133,7 @@ def __init__(self, app, parent_text_edit, tab_reports):
self.ui.splitter_2.setSizes([h0, h1])
except KeyError:
pass
+ # Header section
self.ui.splitter.splitterMoved.connect(self.update_sizes)
self.ui.splitter_2.splitterMoved.connect(self.update_sizes)
self.ui.label_volume.setPixmap(qta.icon('mdi6.volume-high').pixmap(22, 22))
@@ -149,37 +150,43 @@ def __init__(self, app, parent_text_edit, tab_reports):
self.ui.pushButton_rate_up.pressed.connect(self.increase_play_rate)
self.ui.pushButton_help.setIcon(qta.icon('mdi6.help'))
self.ui.pushButton_help.pressed.connect(self.help)
+ self.ui.pushButton_important.setIcon(qta.icon('mdi6.star-outline', options=[{'scale_factor': 1.3}]))
+ self.ui.pushButton_important.pressed.connect(self.show_important_coded)
+ self.ui.pushButton_add_image_to_project.setIcon(
+ qta.icon('mdi6.image-plus-outline', options=[{'scale_factor': 1.3}]))
+ self.ui.pushButton_add_image_to_project.pressed.connect(self.import_screenshot_into_project)
+ self.ui.pushButton_add_image_to_project.setEnabled(False)
+ self.ui.pushButton_screensshot.setIcon(qta.icon('mdi6.image-outline', options=[{'scale_factor': 1.3}]))
+ self.ui.pushButton_screensshot.pressed.connect(self.save_screenshot)
+ self.ui.pushButton_screensshot.setEnabled(False)
self.ui.pushButton_find_code.setIcon(qta.icon('mdi6.card-search-outline', options=[{'scale-factor': 1.2}]))
self.ui.pushButton_find_code.pressed.connect(self.find_code_in_tree)
- # Widgets under codes tree
- self.ui.pushButton_clear_filter_code.setIcon(qta.icon('mdi6.filter-off-outline', options=[{'scale_factor': 1.3}])) # for clear filter code <- L
- self.ui.pushButton_clear_filter_code.pressed.connect(self.clear_code_filter)
- self.ui.pushButton_clear_filter_code.setToolTip(_("Clear code filter"))
- self.ui.pushButton_clear_filter_code.setVisible(False)
- self.ui.lineEdit_code_filter.textChanged.connect(lambda textchanged: self.show_codes_like(self.ui.lineEdit_code_filter.text()))
- # The buttons in the splitter are smaller 24x24 pixels
+ # The buttons under the files list
self.ui.pushButton_latest.setIcon(qta.icon('mdi6.arrow-collapse-right', options=[{'scale_factor': 1.3}]))
self.ui.pushButton_latest.pressed.connect(self.go_to_latest_coded_file)
self.ui.pushButton_next_file.setIcon(qta.icon('mdi6.arrow-right', options=[{'scale_factor': 1.3}]))
self.ui.pushButton_next_file.pressed.connect(self.go_to_next_file)
self.ui.pushButton_document_memo.setIcon(qta.icon('mdi6.text-box-outline', options=[{'scale_factor': 1.3}]))
self.ui.pushButton_document_memo.pressed.connect(self.active_file_memo)
- self.ui.pushButton_important.setIcon(qta.icon('mdi6.star-outline', options=[{'scale_factor': 1.3}]))
- self.ui.pushButton_important.pressed.connect(self.show_important_coded)
self.ui.pushButton_file_attributes.setIcon(qta.icon('mdi6.variable', options=[{'scale_factor': 1.3}]))
self.ui.pushButton_file_attributes.pressed.connect(self.get_files_from_attributes)
self.ui.pushButton_clear_filter_file.setIcon(qta.icon('mdi6.filter-off-outline', options=[{'scale_factor': 1.3}])) # for clear filter file <- L
self.ui.pushButton_clear_filter_file.pressed.connect(self.clear_file_filter)
self.ui.pushButton_clear_filter_file.setToolTip(_("Clear file filter"))
self.ui.pushButton_clear_filter_file.setVisible(False)
- self.ui.pushButton_add_image_to_project.setIcon(qta.icon('mdi6.image-plus-outline', options=[{'scale_factor': 1.3}]))
- self.ui.pushButton_add_image_to_project.pressed.connect(self.import_screenshot_into_project)
- self.ui.pushButton_add_image_to_project.setEnabled(False)
- self.ui.pushButton_screensshot.setIcon(qta.icon('mdi6.image-outline', options=[{'scale_factor': 1.3}]))
- self.ui.pushButton_screensshot.pressed.connect(self.save_screenshot)
- self.ui.pushButton_screensshot.setEnabled(False)
+ self.ui.pushButton_goto_bookmark.setIcon(qta.icon('mdi6.bookmark', options=[{'scale_factor': 1.3}]))
+ self.ui.pushButton_goto_bookmark.pressed.connect(self.go_to_bookmark)
+
+ # Widgets under codes tree
+ self.ui.pushButton_clear_filter_code.setIcon(
+ qta.icon('mdi6.filter-off-outline', options=[{'scale_factor': 1.3}])) # for clear filter code <- L
+ self.ui.pushButton_clear_filter_code.pressed.connect(self.clear_code_filter)
+ self.ui.pushButton_clear_filter_code.setToolTip(_("Clear code filter"))
+ self.ui.pushButton_clear_filter_code.setVisible(False)
+ self.ui.lineEdit_code_filter.textChanged.connect(
+ lambda textchanged: self.show_codes_like(self.ui.lineEdit_code_filter.text()))
# Until any media is selected disable some widgets
self.ui.pushButton_play.setEnabled(False)
@@ -854,7 +861,7 @@ def show_case_files(self):
return
if selection['id'] == -1:
self.get_files()
- self.ui.pushButton_clear_filter_file.setVisible(False) # rreset filter button when showing all <- L
+ self.ui.pushButton_clear_filter_file.setVisible(False) # reset filter button when showing all
self.ui.pushButton_clear_filter_file.setStyleSheet("")
return
cur = self.app.conn.cursor()
@@ -869,7 +876,7 @@ def show_files_like(self):
""" Show files that contain specified filename text.
If blank, show all files. """
- dialog = QtWidgets.QInputDialog(None) #correct: dialog embedded in workspace instead of floating
+ dialog = QtWidgets.QInputDialog(None) # correct: dialog embedded in workspace instead of floating
dialog.setStyleSheet(f"* {{font-size:{self.app.settings['fontsize']}pt}}")
dialog.setWindowTitle(_("Show files like"))
dialog.setWindowFlags(self.windowFlags() & ~QtCore.Qt.WindowType.WindowContextHelpButtonHint)
@@ -1303,15 +1310,6 @@ def set_position(self):
pos = self.ui.horizontalSlider.value()
msecs = self.mediaplayer.get_time()
self.mediaplayer.set_position(pos / 1000.0)
-
- ''' # This code may not be needed - blockSignals seems to fix a problem where msecs returns -1
- counter = 0
- while pos > 0 and msecs == -1 and counter < 10000:
- self.mediaplayer.set_position(pos / 1000.0)
- msecs = self.mediaplayer.get_time()
- counter += 1
- #print("slider pos", pos, pos/1000.0 , "msecs", msecs, "counter", counter)'''
-
self.ui.label_time.setText(msecs_to_hours_mins_secs(msecs) + self.media_duration_text)
self.ui.horizontalSlider.blockSignals(False)
@@ -1702,7 +1700,6 @@ def move_multiple_codes(self):
self.parent_textEdit.append(_("Code moved.") + s['name'].replace(" ← ", "/") + " → " + category['name'])
self.update_dialog_codes_and_categories(["code_name"])
-
def move_code(self, selected):
""" Move code to another category or to no category.
Uses a list selection.
@@ -1968,45 +1965,9 @@ def keyPressEvent(self, event):
if hasattr(self, 'active_handles') and self.active_handles:
self.hide_resize_handles()
return
- '''# Get screenshot and load in project for coding - D
- if key == QtCore.Qt.Key.Key_D and not self.ddialog.isHidden():
- self.import_screenshot_into_project()
- return
- if key == QtCore.Qt.Key.Key_ABC and not (self.ddialog.isHidden() or self.mediaplayer.get_media() is None):
- self.save_screenshot()
- return'''
# Go to bookmark
if key == QtCore.Qt.Key.Key_B and mods & QtCore.Qt.KeyboardModifier.ShiftModifier:
- cur = self.app.conn.cursor()
- cur.execute("select avbookmarkfile, avbookmarkmsec, avbookmarktextpos from project")
- result = cur.fetchone()
- self.file_ = None
- for i, f in enumerate(self.files):
- if f['id'] == result[0]:
- self.file_ = f
- self.ui.listWidget.setCurrentItem(self.ui.listWidget.findItems(self.file_['name'], QtCore.Qt.MatchFlag.MatchExactly)[0])
- self.load_media()
- self.load_segments()
- self.fill_code_counts_in_tree()
- break
- if self.file_ is None:
- print("returning")
- return
- duration_msecs = self.media.get_duration()
- self.mediaplayer.set_time(result[1])
- self.mediaplayer.play()
- # Playback must be active to set_time(). Also add a small sleep to give vlc time to load the media.
- time.sleep(0.2)
- self.mediaplayer.set_time(result[1])
- self.ui.horizontalSlider.setValue(int(result[1] / self.media.get_duration() * 1000))
- self.mediaplayer.pause()
- cursor = self.ui.plainTextEdit.textCursor()
- cursor.setPosition(result[2])
- endpos = result[2] - 1
- if endpos < 0:
- endpos = 0
- cursor.setPosition(endpos, QtGui.QTextCursor.MoveMode.KeepAnchor)
- self.ui.plainTextEdit.setTextCursor(cursor)
+ self.go_to_bookmark()
return
# Set bookmark
if key == QtCore.Qt.Key.Key_B:
@@ -2153,6 +2114,39 @@ def keyPressEvent(self, event):
self.textedit_recent_codes_menu(self.ui.plainTextEdit.cursorRect().topLeft())
return
+ def go_to_bookmark(self):
+ """ B or button. """
+
+ cur = self.app.conn.cursor()
+ cur.execute("select avbookmarkfile, avbookmarkmsec, avbookmarktextpos from project")
+ result = cur.fetchone()
+ self.file_ = None
+ for i, f in enumerate(self.files):
+ if f['id'] == result[0]:
+ self.file_ = f
+ self.ui.listWidget.setCurrentItem(
+ self.ui.listWidget.findItems(self.file_['name'], QtCore.Qt.MatchFlag.MatchExactly)[0])
+ self.load_media()
+ self.load_segments()
+ self.fill_code_counts_in_tree()
+ break
+ if self.file_ is None:
+ return
+ self.mediaplayer.set_time(result[1])
+ self.mediaplayer.play()
+ # Playback must be active to set_time(). Also add a small sleep to give vlc time to load the media.
+ time.sleep(0.2)
+ self.mediaplayer.set_time(result[1])
+ self.ui.horizontalSlider.setValue(int(result[1] / self.media.get_duration() * 1000))
+ self.mediaplayer.pause()
+ cursor = self.ui.plainTextEdit.textCursor()
+ cursor.setPosition(result[2])
+ endpos = result[2] - 1
+ if endpos < 0:
+ endpos = 0
+ cursor.setPosition(endpos, QtGui.QTextCursor.MoveMode.KeepAnchor)
+ self.ui.plainTextEdit.setTextCursor(cursor)
+
def save_screenshot(self):
hms = msecs_to_hours_mins_secs(self.mediaplayer.get_time())
image_name = f"{self.file_['name']}_{hms}.png"
@@ -4496,7 +4490,7 @@ def __init__(self, app, file_, parent=None):
"Positions of the underlying codes / annotations / case-assigned may not correctly adjust if text is typed over or deleted.")
self.ui.label_note.setToolTip(tt)
self.ui.textEdit.installEventFilter(self)
- self.installEventFilter(self) # for rewind, play/stop
+ self.installEventFilter(self) # for rewind, play/stop, etc
if platform.system() in ("Windows", "Darwin"):
self.get_waveform() # Crashes on Fedora 40, segmentation fault with ffmpeg
# Get the transcription text and fill textedit
@@ -4574,14 +4568,23 @@ def __init__(self, app, file_, parent=None):
self.ui.checkBox_case_sensitive.stateChanged.connect(self.search_for_text)
# Transcription buttons
self.ui.pushButton_new_speaker.setIcon(qta.icon('mdi6.account-plus-outline'))
- self.ui.pushButton_new_speaker.setToolTip("Ctrl+N")
self.ui.pushButton_new_speaker.pressed.connect(self.add_speakername)
self.ui.pushButton_remove_speaker.setIcon(qta.icon('mdi6.account-minus-outline'))
- self.ui.pushButton_remove_speaker.setToolTip("Ctrl+D")
self.ui.pushButton_remove_speaker.pressed.connect(self.delete_speakernames)
self.ui.pushButton_insert_timestamp.setIcon(qta.icon('mdi6.clock-outline'))
- self.ui.pushButton_insert_timestamp.setToolTip("Ctrl+T")
self.ui.pushButton_insert_timestamp.pressed.connect(self.insert_timestamp)
+ # Bookmark buttons
+ self.ui.pushButton_goto_bookmark.setIcon(qta.icon('mdi6.bookmark-off'))
+ self.ui.pushButton_goto_bookmark.setEnabled(False)
+ cur = self.app.conn.cursor()
+ cur.execute("select avbookmarkfile from project")
+ result = cur.fetchone()
+ if self.file_['id'] == result[0]:
+ self.ui.pushButton_goto_bookmark.setIcon(qta.icon('mdi6.bookmark-check'))
+ self.ui.pushButton_goto_bookmark.setEnabled(True)
+ self.ui.pushButton_goto_bookmark.pressed.connect(self.go_to_bookmark)
+ self.ui.pushButton_set_bookmark.setIcon(qta.icon('mdi6.bookmark'))
+ self.ui.pushButton_set_bookmark.pressed.connect(self.set_bookmark)
# My solution to getting gui mouse events by putting vlc video in another dialog
self.ddialog = QtWidgets.QDialog()
@@ -4770,17 +4773,6 @@ def get_cases_codings_annotations(self):
if len(self.codetext) > 0 or len(self.annotations) > 0 or len(self.casetext) > 0:
self.no_codes_annotes_cases = False
- ''' Problem with pydub pyaudioop module
- def speech_to_text(self):
- """ Convert speech to text using online service. """
-
- ui = SpeechToText(self.app, self.abs_path)
- ok = ui.exec()
- if not ok:
- return
- txt = ui.text
- self.ui.textEdit.setText(txt)'''
-
def help(self):
""" Open help for transcribe section in browser. """
@@ -4790,7 +4782,7 @@ def ddialog_menu(self, position):
""" Context menu to export a screenshot, to resize dialog """
menu = QtWidgets.QMenu()
- menu.setStyleSheet("QMenu {font-size:" + str(self.app.settings['fontsize']) + "pt} ")
+ menu.setStyleSheet(f"QMenu {{font-size:{self.app.settings['fontsize']}pt}} ")
action_screenshot = menu.addAction(_("Screenshot"))
action_resize = menu.addAction(_("Resize"))
@@ -4829,16 +4821,18 @@ def set_position(self):
def eventFilter(self, object_, event):
""" Add key options to improve manual transcribing.
Options are:
- Alt + minus to rewind 30 seconds.
- Ctrl + R rewind 5 seconds
- Alt + plus forward 30 seconds
- Ctrl + S OR ctrl + P to start/pause On start rewind 1 second
- Ctrl + T to insert timestamp in format [hh.mm.ss]
- Ctrl + N to enter a new speakers name into shortcuts
- Ctrl + D to delete speaker names from shortcuts
- Ctrl + 1 .. 8 to insert speaker in format [speaker name]
- Ctrl + Shift + > to increase play rate
- Ctrl + Shift + < to decrease play rate
+ Crtl B Set Bookmark
+ Ctrl Shift B Go to Bookmart
+ Ctrl D Delete speaker names from shortcuts
+ Ctrl N Enter a new speakers name into shortcuts
+ Ctrl R Rewind 5 seconds
+ Ctrl S OR ctrl + P Start/pause On start rewind slightly
+ Ctrl T Insert timestamp in format [hh.mm.ss]
+ Ctrl +1 .. 8 Insert speaker in format [speaker name]
+ Ctrl Shift > Increase play rate
+ Ctrl Shift < Decrease play rate
+ Alt plus Forward 30 seconds
+ Alt minus Rewind 30 seconds.
"""
if event.type() != 7: # QtGui.QKeyEvent
@@ -4876,16 +4870,56 @@ def eventFilter(self, object_, event):
if key == QtCore.Qt.Key.Key_D and mods == QtCore.Qt.KeyboardModifier.ControlModifier:
self.pause()
self.delete_speakernames()
- # Increase play rate Ctrl + Shift + >
+ # Increase play rate Ctrl Shift >
if key == QtCore.Qt.Key.Key_Greater and (mods and QtCore.Qt.KeyboardModifier.ShiftModifier) and \
(mods and QtCore.Qt.KeyboardModifier.ControlModifier):
self.increase_play_rate()
- # Decrease play rate Ctrl + Shift + <
+ # Decrease play rate Ctrl Shift <
if key == QtCore.Qt.Key.Key_Less and (mods and QtCore.Qt.KeyboardModifier.ShiftModifier) and \
(mods and QtCore.Qt.KeyboardModifier.ControlModifier):
self.decrease_play_rate()
+ # Go to bookmark, if this is the correct a/v file
+ if key == QtCore.Qt.Key.Key_B and mods & QtCore.Qt.KeyboardModifier.ShiftModifier and \
+ mods & QtCore.Qt.KeyboardModifier.ControlModifier:
+ self.go_to_bookmark()
+ # Set bookmark
+ if key == QtCore.Qt.Key.Key_B and mods & QtCore.Qt.KeyboardModifier.ControlModifier:
+ self.set_bookmark()
return True
+ def go_to_bookmark(self):
+ """ Only if this file is bookmarked. Ctrl Shift B or button. """
+
+ cur = self.app.conn.cursor()
+ cur.execute("select avbookmarkfile, avbookmarkmsec, avbookmarktextpos from project")
+ result = cur.fetchone()
+ if self.file_['id'] != result[0]:
+ return True
+ self.mediaplayer.play()
+ # Playback must be active to set_time().
+ time.sleep(0.1)
+ self.mediaplayer.set_time(result[1])
+ self.ui.horizontalSlider.setValue(int(result[1] / self.media.get_duration() * 1000))
+ self.mediaplayer.pause()
+ cursor = self.ui.textEdit.textCursor()
+ cursor.setPosition(result[2])
+ endpos = result[2] - 1
+ if endpos < 0:
+ endpos = 0
+ cursor.setPosition(endpos, QtGui.QTextCursor.MoveMode.KeepAnchor)
+ self.ui.textEdit.setTextCursor(cursor)
+
+ def set_bookmark(self):
+ """ Ctrl B or button. """
+
+ cur = self.app.conn.cursor()
+ cursor_pos = self.ui.textEdit.textCursor().position()
+ cur.execute("update project set avbookmarkfile=?, avbookmarkmsec=?, avbookmarktextpos=?",
+ [self.file_['id'], self.mediaplayer.get_time(), cursor_pos])
+ self.app.conn.commit()
+ self.ui.pushButton_goto_bookmark.setIcon(qta.icon('mdi6.bookmark-check'))
+ self.ui.pushButton_goto_bookmark.setEnabled(True)
+
def rewind_30_seconds(self):
""" Rewind 30 seconds. Alt + R """