plover2cat 3.1.1__py3-none-any.whl → 4.0.0a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/METADATA +5 -2
  2. plover2cat-4.0.0a1.dist-info/RECORD +40 -0
  3. plover_cat/FlowLayout.py +1 -1
  4. plover_cat/PloverCATWindow.py +218 -97
  5. plover_cat/TextEditor.py +43 -24
  6. plover_cat/__init__.py +3 -3
  7. plover_cat/__version__.py +1 -1
  8. plover_cat/affixDialogWindow.py +1 -2
  9. plover_cat/affix_dialog_ui.py +127 -80
  10. plover_cat/captionDialogWindow.py +2 -2
  11. plover_cat/captionWorker.py +4 -4
  12. plover_cat/caption_dialog_ui.py +207 -137
  13. plover_cat/create_dialog_ui.py +97 -72
  14. plover_cat/documentWorker.py +4 -4
  15. plover_cat/export_helpers.py +3 -3
  16. plover_cat/fieldDialogWindow.py +4 -4
  17. plover_cat/field_dialog_ui.py +122 -81
  18. plover_cat/indexDialogWindow.py +5 -5
  19. plover_cat/index_dialog_ui.py +184 -116
  20. plover_cat/plover_cat_ui.py +2783 -1950
  21. plover_cat/qcommands.py +4 -5
  22. plover_cat/recorderDialogWindow.py +35 -9
  23. plover_cat/recorder_dialog_ui.py +165 -107
  24. plover_cat/resources_rc.py +3164 -3230
  25. plover_cat/rtf_parsing.py +2 -2
  26. plover_cat/shortcutDialogWindow.py +4 -4
  27. plover_cat/shortcut_dialog_ui.py +101 -66
  28. plover_cat/steno_objects.py +2 -2
  29. plover_cat/suggestDialogWindow.py +3 -3
  30. plover_cat/suggest_dialog_ui.py +188 -127
  31. plover_cat/testDialogWindow.py +65 -10
  32. plover_cat/test_dialog_ui.py +113 -80
  33. plover2cat-3.1.1.dist-info/RECORD +0 -40
  34. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/WHEEL +0 -0
  35. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/entry_points.txt +0 -0
  36. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/licenses/LICENSE.txt +0 -0
  37. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/top_level.txt +0 -0
  38. {plover2cat-3.1.1.dist-info → plover2cat-4.0.0a1.dist-info}/zip-safe +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plover2cat
3
- Version: 3.1.1
3
+ Version: 4.0.0a1
4
4
  Summary: A CAT software for Plover
5
5
  Home-page: https://github.com/greenwyrt/plover2CAT/
6
6
  Author: plants
@@ -18,7 +18,8 @@ Classifier: Programming Language :: Python :: 3.9
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE.txt
21
- Requires-Dist: plover<5.0.0,>=4.0.0.dev8
21
+ Requires-Dist: plover>=5.0.0.dev2
22
+ Requires-Dist: PySide6-Addons>=6.9.0
22
23
  Requires-Dist: odfpy
23
24
  Requires-Dist: pyparsing>3.0.7
24
25
  Requires-Dist: spylls
@@ -28,6 +29,8 @@ Dynamic: license-file
28
29
 
29
30
  # plover2CAT
30
31
 
32
+ Note: In line with the new Plover 5 release, Plover2CAT is releasing 4.0.0-alpha, which should retain all existing functionalities and is installable on Plover 5 by Github. All issues can be reported here or in Discord.
33
+
31
34
  Plover2CAT is a plugin for Plover, the open-source stenography engine. If the only user requirement is to write steno on the computer, this plugin is not needed as Plover is more than sufficient. Plover2CAT supplements Plover by providing some features of a computer-aided-transcription (CAT) program.
32
35
 
33
36
  ## Features Overview
@@ -0,0 +1,40 @@
1
+ plover2cat-4.0.0a1.dist-info/licenses/LICENSE.txt,sha256=nmGAW0AJG4ke0VT1kvbz6m_jBMcnRRcJQnBtJmKAKU0,1084
2
+ plover_cat/FlowLayout.py,sha256=3sCpVJCNJNwIimyFFlSOaNt-TNb-6kFwvlxwRI3QtNg,3707
3
+ plover_cat/PloverCATWindow.py,sha256=CGgJ381usOx9kC8zekVK70892hC8thi8uAmqla8vkqA,156212
4
+ plover_cat/TextEditor.py,sha256=EdC3D60cLypoHg80jizljEkfJt7Ja-j5Ri9foiN_sV4,63383
5
+ plover_cat/__init__.py,sha256=vg2Hh4rB32Uasptr9ZZPhBpAaI5lUV0m4G6WiLt3nOU,820
6
+ plover_cat/__version__.py,sha256=_fsxWOZfbNI4mXLh4P-xhdbPcH5o8IXuGvbnQj1c3N0,33
7
+ plover_cat/affixDialogWindow.py,sha256=p-SlQR7rDYeTWJeSwjT0RqqLUtvxgeWQUzA9HBLwSME,2729
8
+ plover_cat/affix_dialog_ui.py,sha256=8dqlCZCEOA29fzOFpSa66lehjYt7QOJcwSUesKIZOPc,5665
9
+ plover_cat/captionDialogWindow.py,sha256=Zc3JqwaHasJF7FsmLH7hAEYDvbvbuKNihsXDIBqlZN0,2817
10
+ plover_cat/captionWorker.py,sha256=MFjoYnJ2pEeU9KalUlN0PmygVoOvKAdlc1LM8SPQRlw,8210
11
+ plover_cat/caption_dialog_ui.py,sha256=hPLVru4II6CtHDMjr7qMZvG-1NXzEvIZES2xcHIyyo4,9725
12
+ plover_cat/constants.py,sha256=wDKCJPJuasBpHu6DsiOPW2dlRTWR1ayUYCjEEutQvKs,4933
13
+ plover_cat/create_dialog_ui.py,sha256=q3467-b9u4EVYcSf_a1n44Ob3kzhDHJSK1jWEFoZNzY,4387
14
+ plover_cat/documentWorker.py,sha256=VmgqscHSEjxIEOpdHSydTkOm9mg1zUc5Djkgkc4S1uA,33512
15
+ plover_cat/export_helpers.py,sha256=Tw2r8JroK3lbFpinmBuZKEpt7M1N9-USehwT-EHbXMQ,20116
16
+ plover_cat/fieldDialogWindow.py,sha256=DhVxHb5r1Hp_JUvEX9zicH695pG2RUQXol6PTOKsJjA,3134
17
+ plover_cat/field_dialog_ui.py,sha256=Mp6LEkjtsWC9rF6_ityZidv4vm2NRNIwJEJH7W3DLPg,5585
18
+ plover_cat/helpers.py,sha256=xImF2Rgj246TGotVfvW8jOJEdbo3IfqzwGnSb5mw87U,5582
19
+ plover_cat/indexDialogWindow.py,sha256=tx5dIAVKuEAMBsmudjb8G4le22ySEQ1blXe-WT_VyUA,5640
20
+ plover_cat/index_dialog_ui.py,sha256=nX18ThtZi8uM0a-6W5Y7dxnahfm6LciKaJhmh6Cf9VU,8062
21
+ plover_cat/plover_cat_ui.py,sha256=hwQeyvwIlfvBRGTX0977trX303AvwhyU9LaBZm4O3uI,158388
22
+ plover_cat/qcommands.py,sha256=5HGazgGS_KgOjowzEMpBiP4O5dQGYvKhc3GhlFjGjaM,39342
23
+ plover_cat/recorderDialogWindow.py,sha256=PqcM4kmg1ipNiNG3g97MWC5Wfaeu7wQqLxmZ9-6gf1I,2917
24
+ plover_cat/recorder_dialog_ui.py,sha256=2Bn8RW3alXV29bU46R0YrM7rKTROrFGkGyopkZHWLXE,7781
25
+ plover_cat/resources_rc.py,sha256=YMhA4nqYfp7vItP3zJL7qlItKGALOsMuPXB2ba8c7Mw,126110
26
+ plover_cat/rtf_parsing.py,sha256=6p-9oH73vep4dG53tl9TDfNfoX0Ggv8KPeDXolXH5nY,21299
27
+ plover_cat/shortcutDialogWindow.py,sha256=8Tlk6v2-z6lyRdDR4Y7rquKxoqGL1UkCnYiKVHipgoQ,2453
28
+ plover_cat/shortcut_dialog_ui.py,sha256=_TayFrjTBdTj_y9dAV_Iz7GPLe0BEv9tbCOSQaq_Gtc,4503
29
+ plover_cat/spellcheck.py,sha256=r0dO34PGLpMbPypkFHE08jTrClGWen8C89BdmMidKVU,6695
30
+ plover_cat/steno_objects.py,sha256=RfMUofAC2TZvi4zhe8qdzY-2iHjn2ePxogLCH9aGVAQ,42843
31
+ plover_cat/suggestDialogWindow.py,sha256=Oa2HSh9b8rsxjtcxgMFSqpNnwmPuYoLywIkYUL_19z0,6404
32
+ plover_cat/suggest_dialog_ui.py,sha256=6Ds50LoSE7N2AqzYnvbh4uzyDqv4HMTvZHizFm_dtSA,8782
33
+ plover_cat/testDialogWindow.py,sha256=wqkk_6mGRcKlNHdVc3P_yTkt4x70MOtUZlLYU3RN0bM,21821
34
+ plover_cat/test_dialog_ui.py,sha256=BdJYfN7Ua7SGIUR-iOvs7MzbHlRj8cIyUu-HCMHpTfM,4735
35
+ plover2cat-4.0.0a1.dist-info/METADATA,sha256=DQeqs9XoZ3i-tCzSizCapI07-y3ahv_-3w8nANukA6A,5073
36
+ plover2cat-4.0.0a1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ plover2cat-4.0.0a1.dist-info/entry_points.txt,sha256=bZekpaOB8tv7DjkjuOBYrIdcMgCNuOYDrXKmV6Mgd1o,55
38
+ plover2cat-4.0.0a1.dist-info/top_level.txt,sha256=q7Rw-aE6ep8anwrqhiZOlCig80KhoV4bmE8yWiIlL4E,11
39
+ plover2cat-4.0.0a1.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
40
+ plover2cat-4.0.0a1.dist-info/RECORD,,
plover_cat/FlowLayout.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # copied from https://stackoverflow.com/questions/41621354/
2
2
  import sys
3
- from PyQt5 import QtCore, QtGui, QtWidgets
3
+ from PySide6 import QtCore, QtGui, QtWidgets
4
4
 
5
5
  class FlowLayout(QtWidgets.QLayout):
6
6
  def __init__(self, parent=None, margin=-1, hspacing=-1, vspacing=-1):
@@ -11,18 +11,18 @@ from sys import platform
11
11
  from tempfile import gettempdir, TemporaryDirectory
12
12
 
13
13
 
14
- from PyQt5 import QtCore, QtGui, QtWidgets
15
- from PyQt5.QtGui import (QBrush, QColor, QTextCursor, QFont, QFontMetrics, QTextDocument,
14
+ from PySide6 import QtCore, QtGui, QtWidgets
15
+ from PySide6.QtGui import (QBrush, QColor, QTextCursor, QFont, QFontMetrics, QTextDocument,
16
16
  QCursor, QStandardItem, QStandardItemModel, QPageSize, QTextBlock, QTextFormat, QTextBlockFormat,
17
- QTextOption, QTextCharFormat, QKeySequence, QPalette, QDesktopServices, QPixmap, QIcon)
18
- from PyQt5.QtWidgets import (QMainWindow, QFileDialog, QInputDialog, QListWidgetItem, QTableWidgetItem,
19
- QStyle, QMessageBox, QDialog, QFontDialog, QColorDialog, QLabel, QMenu,
20
- QCompleter, QApplication, QTextEdit, QPlainTextEdit, QProgressBar, QAction, QToolButton, QDockWidget)
21
- from PyQt5.QtMultimedia import (QMediaPlayer, QMediaRecorder,
22
- QMultimedia, QVideoEncoderSettings, QAudioEncoderSettings)
23
- from PyQt5.QtMultimediaWidgets import QVideoWidget
24
- from PyQt5.QtCore import Qt, QFile, QTextStream, QUrl, QTime, QDateTime, QSettings, QRegExp, QSize, QStringListModel, QSizeF, QTimer, QThread
17
+ QTextOption, QTextCharFormat, QKeySequence, QPalette, QDesktopServices, QPixmap, QAction, QIcon)
18
+ from PySide6.QtWidgets import (QMainWindow, QFileDialog, QInputDialog, QListWidgetItem, QTableWidgetItem,
19
+ QStyle, QStyleFactory, QMessageBox, QDialog, QFontDialog, QColorDialog, QLabel, QMenu,
20
+ QCompleter, QApplication, QTextEdit, QPlainTextEdit, QProgressBar, QToolButton, QDockWidget)
21
+ from PySide6.QtMultimedia import (QMediaPlayer, QMediaRecorder, QMediaFormat, QMediaDevices, QAudioInput)
22
+ from PySide6.QtMultimediaWidgets import QVideoWidget
23
+ from PySide6.QtCore import Qt, QFile, QTextStream, QUrl, QTime, QDateTime, QSettings, QRegularExpression, QSize, QStringListModel, QSizeF, QTimer, QThread
25
24
  _ = lambda txt: QtCore.QCoreApplication.translate("Plover2CAT", txt)
25
+ from PySide6.QtTextToSpeech import QTextToSpeech
26
26
 
27
27
  import plover
28
28
 
@@ -123,7 +123,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
123
123
  self.tabifyDockWidget(self.dockHistoryStack, self.dockPaper)
124
124
  self.tabifyDockWidget(self.dockPaper, self.dockNavigation)
125
125
  self.tabifyDockWidget(self.dockAudio, self.dockStenoData)
126
- settings = QSettings("Plover2CAT", "OpenCAT")
126
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
127
127
  if settings.contains("geometry"):
128
128
  self.restoreGeometry(settings.value("geometry"))
129
129
  if settings.contains("windowstate"):
@@ -160,6 +160,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
160
160
  self.caption_cursor_pos = 0
161
161
  self.actionUndo = None
162
162
  self.actionRedo = None
163
+ self.tts_instance = None
163
164
  self.menu_enabling()
164
165
  self.audio_menu_enabling(False)
165
166
  self.set_shortcuts()
@@ -185,12 +186,16 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
185
186
  self.actionOpenTranscriptFolder.triggered.connect(lambda: self.open_root())
186
187
  self.actionImportRTF.triggered.connect(lambda: self.import_rtf())
187
188
  ## audio connections
189
+ for output in QMediaDevices.audioOutputs():
190
+ self.audio_output.addItem(output.description(), output)
188
191
  self.actionOpenAudio.triggered.connect(lambda: self.open_audio())
189
192
  self.actionRecordPause.triggered.connect(lambda: self.record_or_pause())
190
193
  self.actionStopRecording.triggered.connect(lambda: self.stop_record())
191
194
  self.actionShowVideo.triggered.connect(lambda: self.show_hide_video())
192
195
  self.actionCaptioning.triggered.connect(self.setup_captions)
193
196
  self.actionFlushCaption.triggered.connect(self.flush_caption)
197
+ ## tts connections
198
+ self.tts_setup()
194
199
  # self.actionAddChangeAudioTimestamps.triggered.connect(self.modify_audiotime)
195
200
  ## editor related connections
196
201
  self.actionClearParagraph.triggered.connect(lambda: self.reset_paragraph())
@@ -284,6 +289,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
284
289
  self.cursor_status.setObjectName("cursor_status")
285
290
  self.statusBar.addPermanentWidget(self.cursor_status)
286
291
  self.display_message("Create New Transcript or Open Existing...")
292
+ # self.setStyle(QStyleFactory.create("fusion"))
287
293
 
288
294
  def set_shortcuts(self):
289
295
  """Set shortcuts for menu items using keysequence strings.
@@ -346,7 +352,8 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
346
352
  QMessageBox.about(self, "Plover2CAT",
347
353
  "Plover2CAT is built on top of Plover, the open source stenotype engine. "
348
354
  "It owes its development to the members of the Plover discord group who provided suggestions and bug finding. "
349
- "PyQt5 and Plover are both licensed under the GPL. Fugue icons are by Yusuke Kamiyamane, under the Creative Commons Attribution 3.0 License.")
355
+ "PySide6 is licensed under the LGPL and Plover is licensed under the GPL. "
356
+ "Fugue icons are by Yusuke Kamiyamane, under the Creative Commons Attribution 3.0 License.")
350
357
 
351
358
  def open_help(self):
352
359
  """Link to Readthedocs help pages.
@@ -599,6 +606,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
599
606
  """
600
607
  self.actionOpenAudio.setEnabled(not value)
601
608
  self.actionPlayPause.setEnabled(value)
609
+ self.audio_output.setEnabled(value)
602
610
  self.actionStopAudio.setEnabled(value)
603
611
  self.actionSkipForward.setEnabled(value)
604
612
  self.actionSkipBack.setEnabled(value)
@@ -624,6 +632,8 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
624
632
  self.playRate.valueChanged.connect(self.textEdit.player.setPlaybackRate)
625
633
  self.audioDelay.setValue(self.textEdit.audio_delay)
626
634
  self.audioDelay.valueChanged.connect(self.set_audio_delay)
635
+ self.set_audio_output()
636
+ self.audio_output.currentIndexChanged.connect(self.set_audio_output)
627
637
  else:
628
638
  self.audio_label.setText("Select file to play audio")
629
639
  self.actionPlayPause.triggered.disconnect()
@@ -637,6 +647,10 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
637
647
  self.update_seeker_track(0)
638
648
  self.audioDelay.valueChanged.disconnect()
639
649
  self.audioDelay.setValue(0)
650
+ self.audio_output.currentIndexChanged.disconnect()
651
+
652
+ def set_audio_output(self):
653
+ self.textEdit.player.audioOutput().setDevice(self.audio_output.currentData())
640
654
 
641
655
  def update_duration(self, duration):
642
656
  """Update duration label in GUI with duration of media.
@@ -757,7 +771,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
757
771
  log.debug("Updating recent files menu.")
758
772
  self.menuRecentFiles.clear()
759
773
  self.clear_layout(self.recentfileflow)
760
- settings = QSettings("Plover2CAT", "OpenCAT")
774
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
761
775
  recent_file_paths = settings.value("recentfiles", [])
762
776
  for dir_path in recent_file_paths:
763
777
  transcript_path = pathlib.Path(dir_path)
@@ -770,7 +784,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
770
784
  self.menuRecentFiles.addAction(action)
771
785
  tb = QToolButton()
772
786
  icon = QtGui.QIcon()
773
- icon.addFile(":/document-text-large.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
787
+ icon.addFile(":/resources/document-text-large.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
774
788
  tb.setDefaultAction(action)
775
789
  tb.setIcon(icon)
776
790
  tb.setIconSize(QSize(32, 32))
@@ -821,17 +835,17 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
821
835
 
822
836
  def change_highlight_colors(self, action):
823
837
  key = action.data()
824
- el_color = QSettings("Plover2CAT", "OpenCAT").value(key, "black")
838
+ el_color = QSettings("Plover2CAT-4", "OpenCAT").value(key, "black")
825
839
  new_color = QColorDialog.getColor(QColor(el_color))
826
840
  if new_color.isValid():
827
- QSettings("Plover2CAT", "OpenCAT").setValue(key, new_color)
841
+ QSettings("Plover2CAT-4", "OpenCAT").setValue(key, new_color)
828
842
  self.update_highlight_color()
829
843
  if self.textEdit:
830
844
  self.textEdit.get_highlight_colors()
831
845
  self.refresh_editor_styles()
832
846
 
833
847
  def update_highlight_color(self):
834
- settings = QSettings("Plover2CAT", "OpenCAT")
848
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
835
849
  el_names = ["stroke", "text", "automatic", "field", "index"]
836
850
  self.menuHighlightColors.clear()
837
851
  for el in el_names:
@@ -1152,7 +1166,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1152
1166
  """Set autosave time interval that applies to all transcripts.
1153
1167
  """
1154
1168
  log.debug("User set autosave time.")
1155
- settings = QSettings("Plover2CAT", "OpenCAT")
1169
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
1156
1170
  if settings.contains("autosaveinterval"):
1157
1171
  min_save = settings.value("autosaveinterval")
1158
1172
  else:
@@ -1167,7 +1181,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1167
1181
  def autosave_setup(self, checked):
1168
1182
  """Set up/stop timer for autosave.
1169
1183
  """
1170
- settings = QSettings("Plover2CAT", "OpenCAT")
1184
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
1171
1185
  if settings.contains("autosaveinterval"):
1172
1186
  min_save = settings.value("autosaveinterval")
1173
1187
  else:
@@ -1264,7 +1278,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1264
1278
  editorLayout.setObjectName(f"editorLayout_{time.time()}")
1265
1279
  if self.textEdit:
1266
1280
  self.textEdit.restore_dictionary_from_backup(self.engine)
1267
- self.textEdit.disconnect()
1281
+ # self.textEdit.disconnect()
1268
1282
  self.breakdown_connections()
1269
1283
  if self.mainTabs.currentChanged.connect(self.switch_restore):
1270
1284
  self.mainTabs.currentChanged.disconnect()
@@ -1291,7 +1305,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1291
1305
  def recentfile_store(self, path):
1292
1306
  """Store transcript path in settings to open.
1293
1307
  """
1294
- settings = QSettings("Plover2CAT", "OpenCAT")
1308
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
1295
1309
  recent_file_paths = settings.value("recentfiles", [])
1296
1310
  try:
1297
1311
  recent_file_paths.remove(path)
@@ -1323,12 +1337,13 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1323
1337
  if self.textEdit:
1324
1338
  self.textEdit.restore_dictionary_from_backup(self.engine)
1325
1339
  self.breakdown_connections()
1326
- self.textEdit.disconnect()
1340
+ # self.textEdit.disconnect()
1327
1341
  focal_transcript = self.mainTabs.widget(index)
1328
1342
  textEdit = focal_transcript.findChild(QTextEdit)
1329
1343
  if textEdit:
1330
1344
  self.textEdit = textEdit
1331
- self.textEdit.load(self.textEdit.file_name, self.engine, load_transcript = False)
1345
+ # print(self.textEdit.file_name)
1346
+ # self.textEdit.load(self.textEdit.file_name, self.engine, load_transcript = False)
1332
1347
  self.setup_connections()
1333
1348
  else:
1334
1349
  self.textEdit = None
@@ -1338,7 +1353,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1338
1353
  """
1339
1354
  self.actionUndo = self.textEdit.undo_stack.createUndoAction(self.menuEdit)
1340
1355
  undo_icon = QtGui.QIcon()
1341
- undo_icon.addFile(":/arrow-curve-180.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
1356
+ undo_icon.addFile(":/resources/arrow-curve-180.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
1342
1357
  self.actionUndo.setIcon(undo_icon)
1343
1358
  self.actionUndo.setShortcutContext(QtCore.Qt.WindowShortcut)
1344
1359
  self.actionUndo.setToolTip("Undo writing or other action")
@@ -1346,7 +1361,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1346
1361
  self.actionUndo.setObjectName("actionUndo")
1347
1362
  self.actionRedo = self.textEdit.undo_stack.createRedoAction(self.menuEdit)
1348
1363
  redo_icon = QtGui.QIcon()
1349
- redo_icon.addFile(":/arrow-curve.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
1364
+ redo_icon.addFile(":/resources/arrow-curve.png", QtCore.QSize(), QtGui.QIcon.Normal, QtGui.QIcon.Off)
1350
1365
  self.actionRedo.setIcon(redo_icon)
1351
1366
  self.actionRedo.setShortcutContext(QtCore.Qt.WindowShortcut)
1352
1367
  self.actionRedo.setToolTip("Redo writing or other action")
@@ -1373,8 +1388,8 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1373
1388
  self.update_spell_gui()
1374
1389
  self.spell_search.clicked.connect(lambda: self.spellcheck())
1375
1390
  self.spell_skip.clicked.connect(lambda: self.spellcheck())
1376
- self.spell_ignore_all.clicked.connect(lambda: self.sp_ignore_all())
1377
1391
  self.spellcheck_suggestions.itemDoubleClicked.connect(self.sp_insert_suggest)
1392
+ self.spell_ignore_all.clicked.connect(lambda: self.sp_ignore_all())
1378
1393
  self.dict_selection.activated.connect(self.set_sp_dict)
1379
1394
  self.update_config_gui()
1380
1395
  self.page_width.valueChanged.connect(lambda value, key = "page_width": self.textEdit.set_config_value(key, value))
@@ -1399,6 +1414,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1399
1414
  self.style_selector.activated.connect(self.update_paragraph_style)
1400
1415
  self.submitEdited.setEnabled(True)
1401
1416
  self.submitEdited.clicked.connect(self.edit_paragraph_properties)
1417
+ self.update_gui()
1402
1418
  self.search_forward.clicked.connect(lambda: self.search())
1403
1419
  self.search_backward.clicked.connect(lambda: self.search(-1))
1404
1420
  self.find_all.clicked.connect(lambda: self.search_all())
@@ -1407,15 +1423,19 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1407
1423
  self.textEdit.undo_stack.indexChanged.connect(self.check_undo_stack)
1408
1424
  self.textEdit.undo_stack.cleanChanged.connect(self.display_unsave)
1409
1425
  self.textEdit.customContextMenuRequested.connect(self.context_menu)
1426
+ self.textEdit.document().blockCountChanged.connect(lambda: self.get_suggestions())
1410
1427
  self.textEdit.send_message.connect(self.display_message)
1411
1428
  self.textEdit.send_tape.connect(self.update_tape)
1412
- self.textEdit.document().blockCountChanged.connect(lambda: self.get_suggestions())
1413
1429
  self.textEdit.cursorPositionChanged.connect(self.update_gui)
1414
- self.textEdit.player.videoAvailableChanged.connect(self.set_up_video)
1415
- if self.textEdit.player.isAudioAvailable():
1430
+ self.textEdit.player.hasVideoChanged.connect(self.set_up_video)
1431
+
1432
+ # Qt 6 no longer provides isAudioAvailable()/isVideoAvailable().
1433
+ # Use presence of an attached audio file and hasVideo() instead.
1434
+ if getattr(self.textEdit, "audio_file", None):
1416
1435
  self.audio_menu_enabling()
1417
- if self.textEdit.player.isVideoAvailable():
1418
- self.set_up_video()
1436
+
1437
+ if self.textEdit.player.hasVideo():
1438
+ self.set_up_video(True)
1419
1439
 
1420
1440
  def breakdown_connections(self):
1421
1441
  """Disconnect GUI and transcript.
@@ -1433,6 +1453,14 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1433
1453
  self.setup_captions(False)
1434
1454
  self.actionCaptioning.setChecked(False)
1435
1455
  self.menu_enabling()
1456
+ self.menuParagraphStyle.clear()
1457
+ self.menuParagraphStyle.triggered.disconnect()
1458
+ self.style_file_path.setText("")
1459
+ self.style_controls.setEnabled(False)
1460
+ self.actionCreateNewStyle.setEnabled(False)
1461
+ self.menuField.clear() # clear field submenu
1462
+ self.menuIndexEntry.clear() # clear index submenu
1463
+ self.parSteno.clear()
1436
1464
  self.strokeList.clear()
1437
1465
  self.undoView.setStack(None)
1438
1466
  self.dict_selection.clear()
@@ -1461,24 +1489,21 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1461
1489
  self.footer_right.editingFinished.disconnect()
1462
1490
  self.style_selector.clear()
1463
1491
  self.style_selector.activated.disconnect()
1464
- self.textEdit.undo_stack.indexChanged.disconnect(self.check_undo_stack)
1465
- self.textEdit.undo_stack.cleanChanged.disconnect()
1466
- self.textEdit.document().blockCountChanged.disconnect()
1467
1492
  self.submitEdited.clicked.disconnect()
1468
1493
  self.submitEdited.setEnabled(False)
1469
1494
  self.search_forward.clicked.disconnect()
1495
+ self.search_backward.clicked.disconnect()
1470
1496
  self.find_all.clicked.disconnect()
1471
1497
  self.replace_selected.clicked.disconnect()
1472
1498
  self.replace_all.clicked.disconnect()
1473
- self.menuParagraphStyle.clear()
1474
- self.menuParagraphStyle.triggered.disconnect()
1475
- self.style_file_path.setText("")
1476
- self.style_controls.setEnabled(False)
1477
- self.actionCreateNewStyle.setEnabled(False)
1478
- self.menuField.clear() # clear field submenu
1479
- self.menuIndexEntry.clear() # clear index submenu
1480
- self.parSteno.clear()
1481
- if self.textEdit.player.isAudioAvailable(): # clear audio connections if transcript has them
1499
+ self.textEdit.undo_stack.indexChanged.disconnect(self.check_undo_stack)
1500
+ self.textEdit.undo_stack.cleanChanged.disconnect()
1501
+ self.textEdit.customContextMenuRequested.disconnect()
1502
+ self.textEdit.document().blockCountChanged.disconnect()
1503
+ self.textEdit.send_message.disconnect()
1504
+ self.textEdit.send_tape.disconnect()
1505
+ self.textEdit.cursorPositionChanged.disconnect()
1506
+ if getattr(self.textEdit, "audio_file", None):
1482
1507
  self.textEdit.player.stop()
1483
1508
  self.audio_menu_enabling(False)
1484
1509
  if self.video:
@@ -1489,7 +1514,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1489
1514
  """Close window after saving settings and checking for multiple transcripts.
1490
1515
  """
1491
1516
  self.display_message("User selected quit.")
1492
- settings = QSettings("Plover2CAT", "OpenCAT")
1517
+ settings = QSettings("Plover2CAT-4", "OpenCAT")
1493
1518
  settings.setValue("geometry", self.saveGeometry())
1494
1519
  settings.setValue("windowstate", self.saveState())
1495
1520
  settings.setValue("windowfont", self.font().toString())
@@ -1520,7 +1545,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1520
1545
  if tab_page.objectName().startswith("editorTab"):
1521
1546
  choice = self.textEdit.close_transcript()
1522
1547
  if choice and self.textEdit:
1523
- self.textEdit.disconnect()
1548
+ # self.textEdit.disconnect()
1524
1549
  self.breakdown_connections()
1525
1550
  self.textEdit = None
1526
1551
  elif not choice:
@@ -1726,8 +1751,9 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
1726
1751
  if self.blockParentStyle.currentText() != style_name:
1727
1752
  new_style_dict["parentstylename"] = self.blockParentStyle.currentText()
1728
1753
  else:
1729
- QMessageBox.warning(self, "Plover2CAT", "Style cannot be parent of itself.")
1730
- return
1754
+ new_style_dict["parentstylename"] = ""
1755
+ # QMessageBox.warning(self, "Plover2CAT", "Style cannot be parent of itself.")
1756
+ # return
1731
1757
  if self.blockNextStyle.currentIndex() != -1:
1732
1758
  new_style_dict["nextstylename"] = self.blockNextStyle.currentText()
1733
1759
  if self.blockHeadingLevel.currentText() != "":
@@ -2476,7 +2502,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
2476
2502
  """Search for untranslated steno.
2477
2503
  """
2478
2504
  flags = QTextDocument.FindFlags()
2479
- untrans_reg = QRegExp("(\\b|\\*)(?=[STKPWHRAO*EUFBLGDZ]{3,})S?T?K?P?W?H?R?A?O?\\*?E?U?F?R?P?B?L?G?T?S?D?Z?\\b")
2505
+ untrans_reg = QRegularExpression("(\\b|\\*)(?=[STKPWHRAO*EUFBLGDZ]{3,})S?T?K?P?W?H?R?A?O?\\*?E?U?F?R?P?B?L?G?T?S?D?Z?\\b")
2480
2506
  if direction == -1:
2481
2507
  flags |= QTextDocument.FindBackward
2482
2508
  cursor = self.textEdit.textCursor()
@@ -2589,7 +2615,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
2589
2615
  def open_audio(self):
2590
2616
  """Select media file for playing.
2591
2617
  """
2592
- if self.textEdit.recorder.state() == QMediaRecorder.StoppedState:
2618
+ if self.textEdit.recorder.recorderState() == QMediaRecorder.StoppedState:
2593
2619
  pass
2594
2620
  else:
2595
2621
  QMessageBox.information(self, "Plover2CAT", "Recording in progress. Stop recording before loading media file.")
@@ -2628,57 +2654,111 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
2628
2654
  self.video.show()
2629
2655
 
2630
2656
  def record_or_pause(self):
2631
- """Toggle between play and pause for recorder.
2632
- """
2633
- if self.textEdit.player.state() != QMediaPlayer.StoppedState:
2634
- self.display_message("Playing in progress. Stop media first.")
2657
+ """Toggle between record / pause for the audio recorder."""
2658
+ # 1) refuse to record while playback is running
2659
+ if self.textEdit.player.playbackState() != QMediaPlayer.StoppedState:
2660
+ self.display_message("Playback is in progress. Stop the media first.")
2635
2661
  return
2636
- else:
2637
- pass
2638
- if self.textEdit.recorder.state() == QMediaRecorder.StoppedState:
2662
+
2663
+ # ------------------------------------------------------------------
2664
+ # RECORD: recorder currently stopped
2665
+ # ------------------------------------------------------------------
2666
+ if self.textEdit.recorder.recorderState() == QMediaRecorder.StoppedState:
2667
+ # show settings dialog
2639
2668
  self.recorder_settings = recorderDialogWindow(self.textEdit.recorder)
2640
- res = self.recorder_settings.exec_()
2641
- if res == QDialog.Accepted:
2642
- self.actionStopRecording.setEnabled(True)
2643
- settings = QAudioEncoderSettings()
2644
- audio_input = self.recorder_settings.audio_device.itemText(self.recorder_settings.audio_device.currentIndex())
2645
- audio_codec = self.recorder_settings.audio_device.itemText(self.recorder_settings.audio_codec.currentIndex())
2646
- audio_container = self.recorder_settings.audio_container.itemText(self.recorder_settings.audio_container.currentIndex())
2647
- audio_sample_rate = int(self.recorder_settings.audio_sample_rate.itemText(self.recorder_settings.audio_sample_rate.currentIndex()))
2648
- audio_channels = self.recorder_settings.audio_channels.itemData(self.recorder_settings.audio_channels.currentIndex())
2649
- audio_bitrate = self.recorder_settings.audio_bitrate.itemData(self.recorder_settings.audio_bitrate.currentIndex())
2650
- audio_encoding = QMultimedia.ConstantQualityEncoding if self.recorder_settings.constant_quality.isChecked() else QMultimedia.ConstantBitRateEncoding
2651
- self.textEdit.recorder.setAudioInput(audio_input)
2652
- settings.setCodec(audio_codec)
2653
- settings.setSampleRate(audio_sample_rate)
2654
- settings.setBitRate(audio_bitrate)
2655
- settings.setChannelCount(audio_channels)
2656
- settings.setQuality(QMultimedia.EncodingQuality(self.recorder_settings.quality_slider.value()))
2657
- settings.setEncodingMode(audio_encoding)
2658
- self.textEdit.recorder.setEncodingSettings(settings, QVideoEncoderSettings(), audio_container)
2659
- self.display_message(f"Audio settings:\nAudio Input: {audio_input}\nCodec: {audio_codec}\nMIME Type: {audio_container}\nSample Rate: {audio_sample_rate}\nChannels: {audio_channels}\nQuality: {audio_channels}\nBitrate: {str(QMultimedia.EncodingQuality(self.recorder_settings.quality_slider.value()))}\nEncoding:{audio_encoding}")
2660
- common_file_formats = ["aac", "amr", "flac", "gsm", "m4a", "mp3", "mpc", "ogg", "opus", "raw", "wav"]
2661
- guessed_format = [ext for ext in common_file_formats if ext in audio_container]
2662
- if len(guessed_format) == 0:
2663
- audio_file_path = self.textEdit.file_name / "audio" / self.textEdit.file_name.stem
2664
- else:
2665
- audio_file_path = self.textEdit.file_name / "audio" / (self.textEdit.file_name.stem + "." + guessed_format[0])
2666
- self.textEdit.file_name.joinpath("audio").mkdir(exist_ok = True)
2667
- if audio_file_path.exists():
2668
- user_choice = QMessageBox.question(self, "Record", "Are you sure you want to replace existing audio?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
2669
- if user_choice == QMessageBox.Yes:
2670
- self.display_message("Overwriting existing audio file.")
2671
- pass
2672
- else:
2673
- self.display_message("Abort recording attempt due to pre-existing audio file.")
2674
- return
2675
- self.textEdit.recorder.setOutputLocation(QUrl.fromLocalFile(str(audio_file_path)))
2676
- self.textEdit.recorder.record()
2677
- self.display_message("Recording started.")
2678
- self.textEdit.recorder.durationChanged.connect(self.update_record_time)
2679
- elif self.textEdit.recorder.state() == QMediaRecorder.RecordingState:
2669
+ if self.recorder_settings.exec_() != QDialog.Accepted:
2670
+ return # user cancelled
2671
+
2672
+ self.actionStopRecording.setEnabled(True)
2673
+
2674
+ # --- collect the user’s choices --------------------------------
2675
+ audio_device = self.recorder_settings.audio_device.currentData()
2676
+ audio_codec = self.recorder_settings.audio_codec.currentData()
2677
+ container = self.recorder_settings.audio_container.currentData()
2678
+ sample_rate = int(self.recorder_settings.audio_sample_rate.currentData())
2679
+ channels = self.recorder_settings.audio_channels.currentData()
2680
+ bitrate = self.recorder_settings.audio_bitrate.currentData()
2681
+ encoding_mode = self.recorder_settings.audio_encoding.currentData()
2682
+ quality_enum = QMediaRecorder.Quality(self.recorder_settings.quality_slider.value())
2683
+
2684
+ # --- build QMediaFormat (Qt 6 replacement for *EncoderSettings) -
2685
+ fmt = QMediaFormat()
2686
+
2687
+ # codec ----------------------------------------------------------
2688
+ # QMediaFormat expects an enum; map the user string if possible,
2689
+ # otherwise leave codec unset and rely on container/extension.
2690
+ # codec_map = {
2691
+ # "audio/mpeg": QMediaFormat.AudioCodec.MP3,
2692
+ # "audio/aac": QMediaFormat.AudioCodec.AAC,
2693
+ # "audio/flac": QMediaFormat.AudioCodec.FLAC,
2694
+ # "audio/opus": QMediaFormat.AudioCodec.Opus,
2695
+ # "audio/wav": QMediaFormat.AudioCodec.Wave
2696
+ # }
2697
+ # if audio_codec in codec_map:
2698
+ # fmt.setAudioCodec(codec_map[audio_codec])
2699
+ fmt.setAudioCodec(audio_codec)
2700
+
2701
+ # container ------------------------------------------------------
2702
+ # container_map = {
2703
+ # "audio/mpeg": QMediaFormat.Mpeg,
2704
+ # "audio/aac": QMediaFormat.Mpeg4Audio,
2705
+ # "audio/flac": QMediaFormat.Flac,
2706
+ # "audio/ogg": QMediaFormat.Ogg,
2707
+ # "audio/wav": QMediaFormat.Wave
2708
+ # }
2709
+ # if container in container_map:
2710
+ # fmt.setFileFormat(container_map[container])
2711
+ fmt.setFileFormat(container)
2712
+
2713
+ # ----------------------------------------------------------------
2714
+ sel_input = QAudioInput(self.textEdit.media_recorder)
2715
+ sel_input.setDevice(audio_device)
2716
+ self.textEdit.media_recorder.setAudioInput(sel_input)
2717
+ self.textEdit.recorder.setMediaFormat(fmt)
2718
+ self.textEdit.recorder.setAudioSampleRate(sample_rate)
2719
+ self.textEdit.recorder.setAudioChannelCount(channels)
2720
+ self.textEdit.recorder.setAudioBitRate(bitrate)
2721
+ self.textEdit.recorder.setEncodingMode(encoding_mode)
2722
+ self.textEdit.recorder.setQuality(quality_enum)
2723
+
2724
+ # choose an output file name -------------------------------------
2725
+ # common_ext = ["aac", "amr", "flac", "gsm", "m4a", "mp3", "ogg",
2726
+ # "opus", "raw", "wav"]
2727
+ # guessed = [ext for ext in common_ext if ext in container]
2728
+ # out_path = (
2729
+ # self.textEdit.file_name / "audio" /
2730
+ # (self.textEdit.file_name.stem + (("." + guessed[0]) if guessed else ""))
2731
+ # )
2732
+ out_path = (self.textEdit.file_name / "audio" /
2733
+ (self.textEdit.file_name.stem + f".{fmt.mimeType().preferredSuffix()}"))
2734
+ out_path.parent.mkdir(exist_ok=True)
2735
+ self.textEdit.recorder.setOutputLocation(QUrl.fromLocalFile(str(out_path)))
2736
+ if out_path.exists():
2737
+ if QMessageBox.question(
2738
+ self, "Record",
2739
+ "File exists. Overwrite?",
2740
+ QMessageBox.Yes | QMessageBox.No,
2741
+ QMessageBox.No) != QMessageBox.Yes:
2742
+ self.display_message("Recording cancelled by user.")
2743
+ return
2744
+
2745
+ # self.textEdit.recorder.setOutputLocation(QUrl.fromLocalFile(str(out_path)))
2746
+
2747
+ # start recording ------------------------------------------------
2748
+ self.textEdit.recorder.record()
2749
+ self.textEdit.recorder.durationChanged.connect(self.update_record_time)
2750
+ self.display_message("Recording started.")
2751
+
2752
+ # ------------------------------------------------------------------
2753
+ # PAUSE
2754
+ # ------------------------------------------------------------------
2755
+ elif self.textEdit.recorder.recorderState() == QMediaRecorder.RecordingState:
2680
2756
  self.textEdit.recorder.pause()
2681
2757
  self.display_message("Recording paused.")
2758
+
2759
+ # ------------------------------------------------------------------
2760
+ # CONTINUE
2761
+ # ------------------------------------------------------------------
2682
2762
  else:
2683
2763
  self.textEdit.recorder.record()
2684
2764
  self.display_message("Recording continued.")
@@ -2691,6 +2771,47 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
2691
2771
  self.display_message("Recording stopped.")
2692
2772
  self.textEdit.recorder.durationChanged.disconnect()
2693
2773
 
2774
+ def tts_synthesize(self):
2775
+ pass
2776
+
2777
+ def tts_setup(self):
2778
+ self.tts_instance = QTextToSpeech(self)
2779
+ for engine in QTextToSpeech.availableEngines():
2780
+ if engine != "mock":
2781
+ self.tts_engine.addItem(engine)
2782
+ self.tts_engine.setCurrentIndex(0)
2783
+ self.tts_instance.setEngine(self.tts_engine.currentText())
2784
+ self.tts_pitch.valueChanged.connect(self.tts_set_pitch)
2785
+ self.tts_volume.valueChanged.connect(self.tts_set_volume)
2786
+ self.tts_rate.valueChanged.connect(self.tts_set_rate)
2787
+ self.tts_locale.currentIndexChanged.connect(self.tts_set_locale)
2788
+ self.tts_voice.currentIndexChanged.connect(self.tts_set_voice)
2789
+ self.tts_engine.currentIndexChanged.connect(self.tts_set_engine)
2790
+
2791
+ def tts_set_engine(self, index):
2792
+ pass
2793
+
2794
+ def tts_set_locale(self, index):
2795
+ pass
2796
+
2797
+ def tts_set_voice(self, index):
2798
+ pass
2799
+
2800
+ def tts_set_pitch(self, pitch):
2801
+ pitch = pitch / 10
2802
+ if self.tts_instance:
2803
+ self.tts_instance.setPitch(pitch)
2804
+
2805
+ def tts_set_volume(self, volume):
2806
+ volume = volume / 10
2807
+ if self.tts_instance:
2808
+ self.tts_instance.setVolume(volume)
2809
+
2810
+ def tts_set_rate(self, rate):
2811
+ rate = rate / 10
2812
+ if self.tts_instance:
2813
+ self.tts_instance.setRate(rate)
2814
+
2694
2815
  def setup_caption_window(self, display_font, max_blocks):
2695
2816
  """Set a new window for displaying captions.
2696
2817
 
@@ -2785,7 +2906,7 @@ class PloverCATWindow(QMainWindow, Ui_PloverCAT):
2785
2906
  stroke_data = current_block.userData()["strokes"]
2786
2907
  track_pos = current_block.position()
2787
2908
  current_cursor.setPosition(track_pos, QTextCursor.MoveAnchor)
2788
- # current_cursor.movePosition(QTextCursor.PreviousWord, QTextCursor.MoveAnchor)
2909
+ # current_cursor.movePosition(QTextCursor.PreviousWord, QTextCursor.MoveAnchor, 2)
2789
2910
  else:
2790
2911
  current_cursor.movePosition(QTextCursor.PreviousWord, QTextCursor.MoveAnchor, self.caption_dialog.charOffset.value())
2791
2912
  new_pos = current_cursor.position()