Anchor-annotator 0.3.3__py3-none-any.whl → 0.5.0__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.
anchor/plot.py CHANGED
@@ -13,6 +13,11 @@ import sqlalchemy
13
13
  from Bio import pairwise2
14
14
  from montreal_forced_aligner.data import CtmInterval
15
15
  from montreal_forced_aligner.db import Speaker, Utterance
16
+ from montreal_forced_aligner.dictionary.mixins import (
17
+ DEFAULT_PUNCTUATION,
18
+ DEFAULT_WORD_BREAK_MARKERS,
19
+ )
20
+ from montreal_forced_aligner.tokenization.simple import SimpleTokenizer
16
21
  from PySide6 import QtCore, QtGui, QtWidgets
17
22
 
18
23
  from anchor import workers
@@ -157,11 +162,11 @@ class UtteranceClusterView(pg.PlotWidget):
157
162
  def __init__(self, *args):
158
163
  super().__init__(*args)
159
164
  self.settings = AnchorSettings()
160
-
161
- self.setBackground(self.settings.value(self.settings.PRIMARY_VERY_DARK_COLOR))
165
+ plot_theme = self.settings.plot_theme
166
+ self.setBackground(plot_theme.background_color)
162
167
  self.corpus_model = None
163
168
  self.speaker_model: SpeakerModel = None
164
- self.selection_model: CorpusSelectionModel = None
169
+ self.selection_model: FileSelectionModel = None
165
170
  self.updated_indices = set()
166
171
  self.brushes = {-1: pg.mkBrush(0.5)}
167
172
  self.scatter_item = ScatterPlot()
@@ -226,7 +231,7 @@ class UtteranceClusterView(pg.PlotWidget):
226
231
  def set_models(
227
232
  self,
228
233
  corpus_model: CorpusModel,
229
- selection_model: CorpusSelectionModel,
234
+ selection_model: FileSelectionModel,
230
235
  speaker_model: SpeakerModel,
231
236
  ):
232
237
  self.corpus_model = corpus_model
@@ -248,13 +253,12 @@ class UtteranceClusterView(pg.PlotWidget):
248
253
  if ev.button() == QtCore.Qt.MouseButton.LeftButton:
249
254
  utterance_id = int(self.speaker_model.utterance_ids[index])
250
255
  utterance = self.corpus_model.session.query(Utterance).get(utterance_id)
251
- self.selection_model.set_current_utterance(utterance_id)
252
- self.selection_model.current_utterance_id = utterance_id
253
256
  self.selection_model.set_current_file(
254
257
  utterance.file_id,
255
258
  utterance.begin,
256
259
  utterance.end,
257
- utterance.channel,
260
+ utterance.id,
261
+ utterance.speaker_id,
258
262
  force_update=True,
259
263
  )
260
264
  else:
@@ -374,8 +378,8 @@ class AudioPlotItem(pg.PlotItem):
374
378
  self.setDefaultPadding(0)
375
379
  self.setClipToView(True)
376
380
 
377
- self.getAxis("bottom").setPen(self.settings.value(self.settings.ACCENT_LIGHT_COLOR))
378
- self.getAxis("bottom").setTextPen(self.settings.value(self.settings.ACCENT_LIGHT_COLOR))
381
+ self.getAxis("bottom").setPen(self.settings.plot_theme.break_line_color)
382
+ self.getAxis("bottom").setTextPen(self.settings.plot_theme.break_line_color)
379
383
  self.getAxis("bottom").setTickFont(self.settings.small_font)
380
384
  rect = QtCore.QRectF()
381
385
  rect.setTop(top_point)
@@ -433,6 +437,7 @@ class SpeakerTierItem(pg.PlotItem):
433
437
  a.setText("Create utterance")
434
438
  a.triggered.connect(functools.partial(item.create_utterance, begin=begin, end=end))
435
439
  menu.addAction(a)
440
+
436
441
  menu.setStyleSheet(item.settings.menu_style_sheet)
437
442
  menu.exec_(event.screenPos())
438
443
 
@@ -457,9 +462,13 @@ class UtteranceView(QtWidgets.QWidget):
457
462
 
458
463
  # self.break_line.setZValue(30)
459
464
  self.audio_layout = pg.GraphicsLayoutWidget()
465
+ self.audio_layout.viewport().setAttribute(
466
+ QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, False
467
+ )
460
468
  self.audio_layout.centralWidget.layout.setContentsMargins(0, 0, 0, 0)
461
469
  self.audio_layout.centralWidget.layout.setSpacing(0)
462
- self.audio_layout.setBackground(self.settings.value(self.settings.PRIMARY_VERY_DARK_COLOR))
470
+ plot_theme = self.settings.plot_theme
471
+ self.audio_layout.setBackground(plot_theme.background_color)
463
472
  self.audio_plot = AudioPlots(2, 1, 0)
464
473
  self.audio_plot_item = AudioPlotItem(2, 0)
465
474
  self.audio_plot_item.addItem(self.audio_plot)
@@ -470,9 +479,13 @@ class UtteranceView(QtWidgets.QWidget):
470
479
  self.show_transcription = True
471
480
  self.show_alignment = True
472
481
  self.speaker_tier_layout = pg.GraphicsLayoutWidget()
482
+ self.speaker_tier_layout.viewport().setAttribute(
483
+ QtCore.Qt.WidgetAttribute.WA_AcceptTouchEvents, False
484
+ )
473
485
  self.speaker_tier_layout.setAspectLocked(False)
474
486
  self.speaker_tier_layout.centralWidget.layout.setContentsMargins(0, 0, 0, 0)
475
487
  self.speaker_tier_layout.centralWidget.layout.setSpacing(0)
488
+ self.speaker_tier_layout.setBackground(plot_theme.background_color)
476
489
  self.speaker_tiers: dict[SpeakerTier] = {}
477
490
  self.speaker_tier_items = {}
478
491
  self.search_term = None
@@ -522,6 +535,25 @@ class UtteranceView(QtWidgets.QWidget):
522
535
  self.selection_model.waveformReady.connect(self.finalize_loading_auto_wave_form)
523
536
  self.selection_model.speakerRequested.connect(self.set_default_speaker)
524
537
  self.file_model.selectionRequested.connect(self.finalize_loading_utterances)
538
+ self.file_model.speakersChanged.connect(self.finalize_loading_utterances)
539
+ self.corpus_model.refreshTiers.connect(self.finalize_loading_utterances)
540
+
541
+ def refresh_theme(self):
542
+ self.audio_layout.setBackground(self.settings.plot_theme.background_color)
543
+ self.speaker_tier_layout.setBackground(self.settings.plot_theme.background_color)
544
+ self.audio_plot.wave_form.update_theme()
545
+ self.audio_plot.spectrogram.update_theme()
546
+ self.audio_plot.pitch_track.update_theme()
547
+ self.audio_plot_item.getAxis("bottom").setPen(self.settings.plot_theme.break_line_color)
548
+ self.audio_plot_item.getAxis("bottom").setTextPen(
549
+ self.settings.plot_theme.break_line_color
550
+ )
551
+
552
+ def refresh(self):
553
+ self.finalize_loading_utterances()
554
+ self.finalize_loading_auto_wave_form()
555
+ self.finalize_loading_pitch_track()
556
+ self.finalize_loading_spectrogram()
525
557
 
526
558
  def finalize_loading_utterances(self):
527
559
  if self.file_model.file is None:
@@ -532,7 +564,10 @@ class UtteranceView(QtWidgets.QWidget):
532
564
  self.speaker_tier_items = {}
533
565
  self.speaker_tier_layout.clear()
534
566
  available_speakers = {}
535
- speaker_tier_height = self.separator_point - self.bottom_point
567
+ num_visible_speakers = min(
568
+ len(self.file_model.speakers), self.settings.value(self.settings.TIER_MAX_SPEAKERS)
569
+ )
570
+ speaker_tier_height = (self.separator_point - self.bottom_point) / num_visible_speakers
536
571
  for i, speaker_id in enumerate(self.file_model.speakers):
537
572
  speaker_name = self.corpus_model.get_speaker_name(speaker_id)
538
573
  top_point = i * speaker_tier_height
@@ -570,8 +605,10 @@ class UtteranceView(QtWidgets.QWidget):
570
605
  if tier.speaker_id == self.default_speaker_id:
571
606
  scroll_to = i
572
607
  row_height = self.tier_scroll_area.height()
573
- self.speaker_tier_layout.setFixedHeight(len(self.speaker_tiers) * row_height)
574
- if len(self.speaker_tiers) > 1:
608
+ self.speaker_tier_layout.setFixedHeight(
609
+ len(self.speaker_tiers) * row_height / num_visible_speakers
610
+ )
611
+ if len(self.file_model.speakers) > num_visible_speakers:
575
612
  self.tier_scroll_area.verticalScrollBar().setSingleStep(row_height)
576
613
  self.tier_scroll_area.verticalScrollBar().setPageStep(row_height)
577
614
  self.tier_scroll_area.verticalScrollBar().setMinimum(0)
@@ -585,7 +622,6 @@ class UtteranceView(QtWidgets.QWidget):
585
622
  0, 0, self.settings.scroll_bar_height, 0
586
623
  )
587
624
  if scroll_to is not None:
588
- # self.tier_scroll_area.scrollContentsBy(0, scroll_to * tier_height)
589
625
  self.tier_scroll_area.verticalScrollBar().setValue(
590
626
  scroll_to * self.tier_scroll_area.height()
591
627
  )
@@ -1057,8 +1093,9 @@ class TranscriberErrorHighlighter(QtGui.QSyntaxHighlighter):
1057
1093
  super().__init__(*args)
1058
1094
  self.alignment = None
1059
1095
  self.settings = AnchorSettings()
1060
- self.keyword_color = self.settings.error_color
1061
- self.keyword_text_color = self.settings.primary_very_dark_color
1096
+
1097
+ self.keyword_color = self.settings.plot_theme.error_color
1098
+ self.keyword_text_color = self.settings.plot_theme.error_text_color
1062
1099
  self.highlight_format = QtGui.QTextCharFormat()
1063
1100
  self.highlight_format.setBackground(self.keyword_color)
1064
1101
  self.highlight_format.setForeground(self.keyword_text_color)
@@ -1202,6 +1239,15 @@ class IntervalTextRegion(pg.GraphicsObject):
1202
1239
  self.rect.setBottom(self.top_point - self.height)
1203
1240
  self._generate_picture()
1204
1241
 
1242
+ def setSelected(self, selected):
1243
+ if selected:
1244
+ new_brush = self.selected_brush
1245
+ else:
1246
+ new_brush = self.background_brush
1247
+ if new_brush != self.currentBrush:
1248
+ self.currentBrush = new_brush
1249
+ self._generate_picture()
1250
+
1205
1251
  def _generate_picture(self):
1206
1252
  painter = QtGui.QPainter(self.picture)
1207
1253
  painter.setPen(self.border)
@@ -1282,7 +1328,9 @@ class TextAttributeRegion(pg.GraphicsObject):
1282
1328
  self.picture = QtGui.QPicture()
1283
1329
  self.mouseHovering = False
1284
1330
  self.selected = False
1285
- self.currentBrush = self.parentItem().background_brush
1331
+ self.background_brush = self.parentItem().background_brush
1332
+ self.selected_brush = self.parentItem().selected_brush
1333
+ self.currentBrush = self.parentItem().currentBrush
1286
1334
  self.text.setPos((self.begin + self.end) / 2, self.top_point - (self.height / 2))
1287
1335
  self.begin_line = pg.InfiniteLine()
1288
1336
  self.rect = QtCore.QRectF(
@@ -1292,9 +1340,18 @@ class TextAttributeRegion(pg.GraphicsObject):
1292
1340
  height=self.height,
1293
1341
  )
1294
1342
  self.rect.setTop(self.top_point)
1295
- self.rect.setBottom(self.top_point - self.height)
1343
+ self.rect.setBottom(self.bottom_point)
1296
1344
  self._generate_picture()
1297
1345
 
1346
+ def setSelected(self, selected):
1347
+ if selected:
1348
+ new_brush = self.selected_brush
1349
+ else:
1350
+ new_brush = self.background_brush
1351
+ if new_brush != self.currentBrush:
1352
+ self.currentBrush = new_brush
1353
+ self._generate_picture()
1354
+
1298
1355
  def _generate_picture(self):
1299
1356
  painter = QtGui.QPainter(self.picture)
1300
1357
  painter.setPen(self.border)
@@ -1395,7 +1452,7 @@ class NormalizedTextRegion(TextAttributeRegion):
1395
1452
 
1396
1453
 
1397
1454
  class Highlighter(QtGui.QSyntaxHighlighter):
1398
- WORDS = r"\S+"
1455
+ WORDS = rf"[^\s{''.join(DEFAULT_WORD_BREAK_MARKERS)+''.join(DEFAULT_PUNCTUATION)}]+"
1399
1456
 
1400
1457
  def __init__(self, *args):
1401
1458
  super(Highlighter, self).__init__(*args)
@@ -1424,8 +1481,25 @@ class Highlighter(QtGui.QSyntaxHighlighter):
1424
1481
  def highlightBlock(self, text):
1425
1482
  self.settings.sync()
1426
1483
  self.spellcheck_format.setUnderlineColor(self.settings.error_color)
1484
+ words = self.WORDS
1485
+ try:
1486
+ tokenizers = self.dictionary_model.corpus_model.corpus.get_tokenizers()
1487
+ dictionary_id = self.dictionary_model.corpus_model.corpus.get_dict_id_for_speaker(
1488
+ self.speaker_id
1489
+ )
1490
+ if isinstance(tokenizers, dict) and dictionary_id is not None:
1491
+ tokenizer = self.dictionary_model.corpus_model.corpus.get_tokenizer(dictionary_id)
1492
+ else:
1493
+ tokenizer = tokenizers
1494
+ if isinstance(tokenizer, SimpleTokenizer):
1495
+ extra_symbols = "".join(tokenizer.punctuation) + "".join(
1496
+ tokenizer.word_break_markers
1497
+ )
1498
+ words = rf"[^\s{extra_symbols}]+"
1499
+ except Exception:
1500
+ pass
1427
1501
  if self.dictionary_model is not None and self.dictionary_model.word_sets:
1428
- for word_object in re.finditer(self.WORDS, text):
1502
+ for word_object in re.finditer(words, text):
1429
1503
  if not self.dictionary_model.check_word(word_object.group(), self.speaker_id):
1430
1504
  self.setFormat(
1431
1505
  word_object.start(),
@@ -1454,8 +1528,6 @@ class MfaRegion(pg.LinearRegionItem):
1454
1528
  audioSelected = QtCore.Signal(object, object)
1455
1529
  viewRequested = QtCore.Signal(object, object)
1456
1530
 
1457
- settings = AnchorSettings()
1458
-
1459
1531
  def __init__(
1460
1532
  self,
1461
1533
  item: CtmInterval,
@@ -1469,6 +1541,8 @@ class MfaRegion(pg.LinearRegionItem):
1469
1541
  pg.GraphicsObject.__init__(self)
1470
1542
  self.item = item
1471
1543
 
1544
+ self.settings = AnchorSettings()
1545
+
1472
1546
  self.item_min = self.item.begin
1473
1547
  self.item_max = self.item.end
1474
1548
  if selection_model.settings.right_to_left:
@@ -1481,15 +1555,15 @@ class MfaRegion(pg.LinearRegionItem):
1481
1555
  self.top_point = top_point
1482
1556
  self.span = (self.bottom_point, self.top_point)
1483
1557
  self.text_margin_pixels = 2
1558
+ self.height = abs(self.top_point - self.bottom_point)
1484
1559
 
1485
- self.selected_range_color = self.settings.value(self.settings.PRIMARY_BASE_COLOR).lighter()
1486
- self.interval_background_color = self.settings.value(self.settings.PRIMARY_DARK_COLOR)
1487
- self.hover_line_color = self.settings.value(self.settings.ERROR_COLOR)
1488
- self.moving_line_color = self.settings.value(self.settings.ERROR_COLOR)
1560
+ self.interval_background_color = self.settings.plot_theme.interval_background_color
1561
+ self.hover_line_color = self.settings.plot_theme.hover_line_color
1562
+ self.moving_line_color = self.settings.plot_theme.moving_line_color
1489
1563
 
1490
- self.break_line_color = self.settings.value(self.settings.ACCENT_LIGHT_COLOR)
1491
- self.text_color = self.settings.value(self.settings.MAIN_TEXT_COLOR)
1492
- self.selected_interval_color = self.settings.value(self.settings.PRIMARY_BASE_COLOR)
1564
+ self.break_line_color = self.settings.plot_theme.break_line_color
1565
+ self.text_color = self.settings.plot_theme.text_color
1566
+ self.selected_interval_color = self.settings.plot_theme.selected_interval_color
1493
1567
  self.plot_text_font = self.settings.big_font
1494
1568
  self.setCursor(QtCore.Qt.CursorShape.SizeAllCursor)
1495
1569
  self.pen = pg.mkPen(self.break_line_color, width=3)
@@ -1515,15 +1589,42 @@ class MfaRegion(pg.LinearRegionItem):
1515
1589
  self.swapMode = "sort"
1516
1590
  self.clipItem = None
1517
1591
  self._boundingRectCache = None
1518
- self.setBrush(self.background_brush)
1519
1592
  self.movable = False
1520
1593
  self.cached_visible_duration = None
1521
1594
  self.cached_view = None
1595
+ self.currentBrush = self.background_brush
1596
+ self.picture = QtGui.QPicture()
1597
+ self.rect = QtCore.QRectF(
1598
+ left=self.item_min,
1599
+ top=self.top_point,
1600
+ width=self.item_max - self.item_min,
1601
+ height=self.height,
1602
+ )
1603
+ self.rect.setTop(self.top_point)
1604
+ self.rect.setLeft(self.item_min)
1605
+ self.rect.setRight(self.item_max)
1606
+ self.rect.setBottom(self.bottom_point)
1607
+ self._generate_picture()
1608
+ self.sigRegionChanged.connect(self.update_bounds)
1609
+ self.sigRegionChangeFinished.connect(self.update_bounds)
1522
1610
 
1523
- def paint(self, p, *args):
1524
- p.setBrush(self.currentBrush)
1525
- p.setPen(self.border_pen)
1526
- p.drawRect(self.boundingRect())
1611
+ def update_bounds(self):
1612
+ beg, end = self.getRegion()
1613
+ self.rect.setLeft(beg)
1614
+ self.rect.setRight(end)
1615
+ self._generate_picture()
1616
+
1617
+ def _generate_picture(self):
1618
+ if self.selection_model is None:
1619
+ return
1620
+ painter = QtGui.QPainter(self.picture)
1621
+ painter.setPen(self.border_pen)
1622
+ painter.setBrush(self.currentBrush)
1623
+ painter.drawRect(self.rect)
1624
+ painter.end()
1625
+
1626
+ def paint(self, painter, *args):
1627
+ painter.drawPicture(0, 0, self.picture)
1527
1628
 
1528
1629
  def mouseClickEvent(self, ev: QtGui.QMouseEvent):
1529
1630
  if ev.button() != QtCore.Qt.MouseButton.LeftButton:
@@ -1543,10 +1644,12 @@ class MfaRegion(pg.LinearRegionItem):
1543
1644
 
1544
1645
  def setSelected(self, selected: bool):
1545
1646
  if selected:
1546
- self.setBrush(pg.mkBrush(self.selected_interval_color))
1647
+ new_brush = pg.mkBrush(self.selected_interval_color)
1547
1648
  else:
1548
- # self.interval_background_color.setAlpha(0)
1549
- self.setBrush(pg.mkBrush(self.interval_background_color))
1649
+ new_brush = pg.mkBrush(self.interval_background_color)
1650
+ if new_brush != self.currentBrush:
1651
+ self.currentBrush = new_brush
1652
+ self._generate_picture()
1550
1653
  self.update()
1551
1654
 
1552
1655
  def setMouseHover(self, hover: bool):
@@ -1562,6 +1665,26 @@ class MfaRegion(pg.LinearRegionItem):
1562
1665
  if self.selected and not deselect and not reset:
1563
1666
  return
1564
1667
 
1668
+ def boundingRect(self):
1669
+ try:
1670
+ visible_begin = max(self.item_min, self.selection_model.plot_min)
1671
+ visible_end = min(self.item_max, self.selection_model.plot_max)
1672
+ except TypeError:
1673
+ visible_begin = self.item_min
1674
+ visible_end = self.item_max
1675
+ br = QtCore.QRectF(self.picture.boundingRect())
1676
+ br.setLeft(visible_begin)
1677
+ br.setRight(visible_end)
1678
+
1679
+ br.setTop(self.top_point)
1680
+ br.setBottom(self.bottom_point + 0.01)
1681
+ br = br.normalized()
1682
+
1683
+ if self._boundingRectCache != br:
1684
+ self._boundingRectCache = br
1685
+ self.prepareGeometryChange()
1686
+ return br
1687
+
1565
1688
 
1566
1689
  class AlignmentRegion(MfaRegion):
1567
1690
  def __init__(
@@ -1595,49 +1718,43 @@ class AlignmentRegion(MfaRegion):
1595
1718
  self.text.textItem.document().setDefaultTextOption(options)
1596
1719
  self.text.setParentItem(self)
1597
1720
  self.per_tier_range = self.top_point - self.bottom_point
1721
+ self.currentBrush = pg.mkBrush((0, 0, 0, 0))
1722
+ self.selection_model.viewChanged.connect(self.check_visibility)
1723
+ self.check_visibility()
1598
1724
 
1599
- def viewRangeChanged(self):
1725
+ def setSelected(self, selected: bool):
1726
+ if selected:
1727
+ self.text.setColor(self.settings.plot_theme.selected_text_color)
1728
+ else:
1729
+ self.text.setColor(self.settings.plot_theme.text_color)
1730
+
1731
+ def check_visibility(self):
1600
1732
  if (self.item_max - self.item_min) / (
1601
1733
  self.selection_model.max_time - self.selection_model.min_time
1602
1734
  ) < 0.001:
1603
1735
  self.hide()
1604
1736
  else:
1605
- self.show()
1606
- super().viewRangeChanged()
1607
-
1608
- def boundingRect(self):
1609
- br = QtCore.QRectF(self.viewRect()) # bounds of containing ViewBox mapped to local coords.
1610
- vb = self.getViewBox()
1611
-
1612
- pixel_size = vb.viewPixelSize()
1613
-
1614
- br.setLeft(self.item_min)
1615
- br.setRight(self.item_max)
1616
-
1617
- br.setTop(self.top_point)
1618
- # br.setBottom(self.top_point-self.per_tier_range)
1619
- br.setBottom(self.bottom_point + 0.01)
1620
- try:
1737
+ vb = self.getViewBox()
1621
1738
  visible_begin = max(self.item_min, self.selection_model.plot_min)
1622
1739
  visible_end = min(self.item_max, self.selection_model.plot_max)
1623
- except TypeError:
1624
- return br
1625
- visible_duration = visible_end - visible_begin
1626
- x_margin_px = 8
1627
- available_text_width = visible_duration / pixel_size[0] - (2 * x_margin_px)
1628
- self.text.setVisible(available_text_width > 10)
1629
- if visible_duration != self.cached_visible_duration:
1630
- self.cached_visible_duration = visible_duration
1631
-
1740
+ visible_duration = visible_end - visible_begin
1632
1741
  self.text.setPos(
1633
1742
  visible_begin + (visible_duration / 2), self.top_point - (self.per_tier_range / 2)
1634
1743
  )
1635
- br = br.normalized()
1744
+ if vb is None:
1745
+ self.text.setVisible(
1746
+ (visible_end - visible_begin)
1747
+ / (self.selection_model.max_time - self.selection_model.min_time)
1748
+ > 0.005
1749
+ )
1750
+ else:
1751
+ pixel_size = vb.viewPixelSize()[0]
1752
+ if pixel_size != 0:
1753
+ x_margin_px = 8
1754
+ available_text_width = visible_duration / pixel_size - (2 * x_margin_px)
1636
1755
 
1637
- if self._boundingRectCache != br:
1638
- self._boundingRectCache = br
1639
- self.prepareGeometryChange()
1640
- return br
1756
+ self.text.setVisible(available_text_width > 10)
1757
+ self.show()
1641
1758
 
1642
1759
 
1643
1760
  class PhoneRegion(AlignmentRegion):
@@ -1649,10 +1766,14 @@ class PhoneRegion(AlignmentRegion):
1649
1766
  selection_model: CorpusSelectionModel,
1650
1767
  bottom_point: float = 0,
1651
1768
  top_point: float = 1,
1769
+ color=None,
1652
1770
  ):
1653
1771
  super().__init__(
1654
1772
  phone_interval, corpus_model, file_model, selection_model, bottom_point, top_point
1655
1773
  )
1774
+ if color is not None:
1775
+ self.currentBrush = pg.mkBrush(color)
1776
+ self._generate_picture()
1656
1777
 
1657
1778
 
1658
1779
  class WordRegion(AlignmentRegion):
@@ -1670,6 +1791,7 @@ class WordRegion(AlignmentRegion):
1670
1791
  super().__init__(
1671
1792
  word_interval, corpus_model, file_model, selection_model, bottom_point, top_point
1672
1793
  )
1794
+ self._generate_picture()
1673
1795
 
1674
1796
  def mouseClickEvent(self, ev: QtGui.QMouseEvent):
1675
1797
  search_term = TextFilterQuery(self.item.label, word=True)
@@ -1763,7 +1885,7 @@ class UtteranceRegion(MfaRegion):
1763
1885
  per_tier_range=self.per_tier_range,
1764
1886
  dictionary_model=self.dictionary_model,
1765
1887
  speaker_id=self.item.speaker_id,
1766
- border=pg.mkPen(self.settings.accent_light_color),
1888
+ border=pg.mkPen(self.settings.plot_theme.break_line_color),
1767
1889
  )
1768
1890
  self.text.setParentItem(self)
1769
1891
 
@@ -1807,7 +1929,7 @@ class UtteranceRegion(MfaRegion):
1807
1929
  tier_top_point,
1808
1930
  self.per_tier_range,
1809
1931
  self.selection_model,
1810
- border=pg.mkPen(self.settings.accent_light_color),
1932
+ border=pg.mkPen(self.settings.plot_theme.break_line_color),
1811
1933
  dictionary_model=dictionary_model,
1812
1934
  search_term=search_term,
1813
1935
  speaker_id=utterance.speaker_id,
@@ -1836,9 +1958,25 @@ class UtteranceRegion(MfaRegion):
1836
1958
 
1837
1959
  if intervals is None:
1838
1960
  continue
1961
+ min_confidence = None
1962
+ max_confidence = None
1963
+ cmap = pg.ColorMap(
1964
+ None,
1965
+ [
1966
+ self.settings.plot_theme.error_color,
1967
+ self.settings.plot_theme.interval_background_color,
1968
+ ],
1969
+ )
1970
+ cmap.linearize()
1971
+ if "phone_intervals" in lookup:
1972
+ for interval in intervals:
1973
+ if interval.confidence is None:
1974
+ continue
1975
+ if min_confidence is None or interval.confidence < min_confidence:
1976
+ min_confidence = interval.confidence
1977
+ if max_confidence is None or interval.confidence > max_confidence:
1978
+ max_confidence = interval.confidence
1839
1979
  for interval in intervals:
1840
- # if (interval.end - interval.begin) /(self.selection_model.max_time -self.selection_model.min_time) < 0.01:
1841
- # continue
1842
1980
  if lookup == "transcription_text":
1843
1981
  interval_reg = TranscriberTextRegion(
1844
1982
  self,
@@ -1848,13 +1986,21 @@ class UtteranceRegion(MfaRegion):
1848
1986
  tier_top_point,
1849
1987
  self.per_tier_range,
1850
1988
  self.selection_model,
1851
- border=pg.mkPen(self.settings.accent_light_color),
1989
+ border=pg.mkPen(self.settings.plot_theme.break_line_color),
1852
1990
  alignment=alignment,
1853
1991
  dictionary_model=dictionary_model,
1854
1992
  search_term=search_term,
1855
1993
  speaker_id=utterance.speaker_id,
1856
1994
  )
1995
+ interval_reg.setParentItem(self)
1996
+ interval_reg.audioSelected.connect(self.setSelected)
1857
1997
  elif "phone_intervals" in lookup:
1998
+ color = None
1999
+ if interval.confidence is not None:
2000
+ normalized_confidence = (interval.confidence - min_confidence) / (
2001
+ max_confidence - min_confidence
2002
+ )
2003
+ color = cmap.map(normalized_confidence)
1858
2004
  interval_reg = PhoneRegion(
1859
2005
  interval,
1860
2006
  self.corpus_model,
@@ -1862,8 +2008,10 @@ class UtteranceRegion(MfaRegion):
1862
2008
  selection_model=selection_model,
1863
2009
  top_point=tier_top_point,
1864
2010
  bottom_point=tier_bottom_point,
2011
+ color=color,
1865
2012
  )
1866
2013
  interval_reg.setParentItem(self)
2014
+ interval_reg.audioSelected.connect(self.setSelected)
1867
2015
  elif "word_intervals" in lookup:
1868
2016
  interval_reg = WordRegion(
1869
2017
  interval,
@@ -1875,18 +2023,19 @@ class UtteranceRegion(MfaRegion):
1875
2023
  )
1876
2024
  interval_reg.setParentItem(self)
1877
2025
  interval_reg.highlightRequested.connect(self.highlighter.setSearchTerm)
2026
+ interval_reg.audioSelected.connect(self.setSelected)
1878
2027
 
1879
2028
  else:
1880
2029
  interval_reg = IntervalTextRegion(
1881
2030
  interval,
1882
2031
  self.text_color,
1883
- border=pg.mkPen(self.settings.accent_light_color, width=3),
2032
+ border=pg.mkPen(self.settings.plot_theme.break_line_color, width=3),
1884
2033
  top_point=tier_top_point,
1885
2034
  height=self.per_tier_range,
1886
- # font=self.settings.font,
1887
2035
  background_brush=self.background_brush,
1888
- selected_brush=pg.mkBrush(self.selected_range_color),
2036
+ selected_brush=pg.mkBrush(self.selected_interval_color),
1889
2037
  )
2038
+ interval_reg.audioSelected.connect(self.setSelected)
1890
2039
  interval_reg.setParentItem(self)
1891
2040
 
1892
2041
  interval_reg.audioSelected.connect(self.audioSelected.emit)
@@ -1896,6 +2045,27 @@ class UtteranceRegion(MfaRegion):
1896
2045
  self.show()
1897
2046
  self.available_speakers = available_speakers
1898
2047
 
2048
+ def show(self):
2049
+ for intervals in self.extra_tier_intervals.values():
2050
+ for interval in intervals:
2051
+ if (
2052
+ self.selection_model.min_time
2053
+ < interval.item.end
2054
+ <= self.selection_model.max_time
2055
+ or self.selection_model.min_time
2056
+ <= interval.item.begin
2057
+ < self.selection_model.max_time
2058
+ ):
2059
+ interval.show()
2060
+ else:
2061
+ interval.hide()
2062
+ super().show()
2063
+
2064
+ def setSelected(self, selected: bool):
2065
+ if selected:
2066
+ self.text_edit.setFocus()
2067
+ super().setSelected(selected)
2068
+
1899
2069
  def change_editing(self, editable: bool):
1900
2070
  self.lines[0].movable = editable
1901
2071
  self.lines[1].movable = editable
@@ -1963,7 +2133,10 @@ class UtteranceRegion(MfaRegion):
1963
2133
  menu.exec_(ev.screenPos())
1964
2134
 
1965
2135
  def update_tier_visibility(self, checked):
1966
- tier_name = self.sender().text().split(maxsplit=1)[-1]
2136
+ sender = self.sender()
2137
+ if not isinstance(sender, QtGui.QAction):
2138
+ return
2139
+ tier_name = sender.text().split(maxsplit=1)[-1]
1967
2140
  self.settings.setValue(self.settings.tier_visibility_mapping[tier_name], checked)
1968
2141
  self.settings.sync()
1969
2142
  self.corpus_model.refreshTiers.emit()
@@ -1978,7 +2151,10 @@ class UtteranceRegion(MfaRegion):
1978
2151
  self.file_model.update_utterance_speaker(self.item, speaker_id)
1979
2152
 
1980
2153
  def update_speaker(self):
1981
- speaker_name = self.sender().text()
2154
+ sender = self.sender()
2155
+ if not isinstance(sender, QtGui.QAction):
2156
+ return
2157
+ speaker_name = sender.text()
1982
2158
  if speaker_name == "New speaker":
1983
2159
  speaker_id = 0
1984
2160
  else:
@@ -2134,7 +2310,7 @@ class WaveForm(pg.PlotCurveItem):
2134
2310
  self.top_point = top_point
2135
2311
  self.bottom_point = bottom_point
2136
2312
  self.mid_point = (self.top_point + self.bottom_point) / 2
2137
- pen = pg.mkPen(self.settings.value(self.settings.MAIN_TEXT_COLOR), width=1)
2313
+ pen = pg.mkPen(self.settings.plot_theme.wave_line_color, width=1)
2138
2314
  super(WaveForm, self).__init__()
2139
2315
  self.setPen(pen)
2140
2316
  self.channel = 0
@@ -2142,6 +2318,10 @@ class WaveForm(pg.PlotCurveItem):
2142
2318
  self.selection_model = None
2143
2319
  self.setAcceptHoverEvents(False)
2144
2320
 
2321
+ def update_theme(self):
2322
+ pen = pg.mkPen(self.settings.plot_theme.wave_line_color, width=1)
2323
+ self.setPen(pen)
2324
+
2145
2325
  def hoverEvent(self, ev):
2146
2326
  return
2147
2327
 
@@ -2155,7 +2335,7 @@ class PitchTrack(pg.PlotCurveItem):
2155
2335
  self.top_point = top_point
2156
2336
  self.bottom_point = bottom_point
2157
2337
  self.mid_point = (self.top_point + self.bottom_point) / 2
2158
- pen = pg.mkPen(self.settings.value(self.settings.PRIMARY_LIGHT_COLOR), width=3)
2338
+ pen = pg.mkPen(self.settings.plot_theme.pitch_color, width=3)
2159
2339
  super().__init__()
2160
2340
  self.setPen(pen)
2161
2341
  self.channel = 0
@@ -2164,19 +2344,25 @@ class PitchTrack(pg.PlotCurveItem):
2164
2344
  self.setAcceptHoverEvents(False)
2165
2345
  self.min_label = pg.TextItem(
2166
2346
  str(self.settings.PITCH_MIN_F0),
2167
- self.settings.value(self.settings.PRIMARY_VERY_LIGHT_COLOR),
2347
+ self.settings.plot_theme.pitch_color,
2168
2348
  anchor=(1, 1),
2169
2349
  )
2170
2350
  self.min_label.setFont(self.settings.font)
2171
2351
  self.min_label.setParentItem(self)
2172
2352
  self.max_label = pg.TextItem(
2173
2353
  str(self.settings.PITCH_MAX_F0),
2174
- self.settings.value(self.settings.PRIMARY_VERY_LIGHT_COLOR),
2354
+ self.settings.plot_theme.pitch_color,
2175
2355
  anchor=(1, 0),
2176
2356
  )
2177
2357
  self.max_label.setFont(self.settings.font)
2178
2358
  self.max_label.setParentItem(self)
2179
2359
 
2360
+ def update_theme(self):
2361
+ pen = pg.mkPen(self.settings.plot_theme.pitch_color, width=3)
2362
+ self.setPen(pen)
2363
+ self.min_label.setColor(self.settings.plot_theme.pitch_color)
2364
+ self.max_label.setColor(self.settings.plot_theme.pitch_color)
2365
+
2180
2366
  def hoverEvent(self, ev):
2181
2367
  return
2182
2368
 
@@ -2199,7 +2385,11 @@ class Spectrogram(pg.ImageItem):
2199
2385
  self.channel = 0
2200
2386
  super(Spectrogram, self).__init__()
2201
2387
  self.cmap = pg.ColorMap(
2202
- None, [self.settings.primary_very_dark_color, self.settings.accent_light_color]
2388
+ None,
2389
+ [
2390
+ self.settings.plot_theme.background_color,
2391
+ self.settings.plot_theme.spectrogram_color,
2392
+ ],
2203
2393
  )
2204
2394
  self.cmap.linearize()
2205
2395
  self.color_bar = pg.ColorBarItem(colorMap=self.cmap)
@@ -2210,6 +2400,19 @@ class Spectrogram(pg.ImageItem):
2210
2400
  self.cached_channel = None
2211
2401
  self.stft = None
2212
2402
 
2403
+ def update_theme(self):
2404
+ self.cmap = pg.ColorMap(
2405
+ None,
2406
+ [
2407
+ self.settings.plot_theme.background_color,
2408
+ self.settings.plot_theme.spectrogram_color,
2409
+ ],
2410
+ )
2411
+ self.cmap.linearize()
2412
+ self.color_bar.setColorMap(self.cmap)
2413
+ self.color_bar.setImageItem(self)
2414
+ self.update()
2415
+
2213
2416
  def set_models(self, selection_model: CorpusSelectionModel):
2214
2417
  self.selection_model = selection_model
2215
2418
 
@@ -2271,8 +2474,12 @@ class SelectionArea(pg.LinearRegionItem):
2271
2474
  self.setVisible(False)
2272
2475
  else:
2273
2476
  self.setRegion([begin, end])
2274
- self.lines[0].label.setText(f"{begin:.3f}", self.settings.error_color)
2275
- self.lines[1].label.setText(f"{end:.3f}", self.settings.error_color)
2477
+ self.lines[0].label.setText(
2478
+ f"{begin:.3f}", self.settings.plot_theme.selected_range_color
2479
+ )
2480
+ self.lines[1].label.setText(
2481
+ f"{end:.3f}", self.settings.plot_theme.selected_range_color
2482
+ )
2276
2483
  self.setVisible(True)
2277
2484
 
2278
2485
 
@@ -2290,17 +2497,17 @@ class AudioPlots(pg.GraphicsObject):
2290
2497
  self.wave_form.setParentItem(self)
2291
2498
  self.spectrogram.setParentItem(self)
2292
2499
  self.pitch_track.setParentItem(self)
2293
- color = self.settings.error_color
2500
+ color = self.settings.plot_theme.selected_range_color
2294
2501
  color.setAlphaF(0.25)
2295
2502
  self.selection_brush = pg.mkBrush(color)
2296
- self.background_pen = pg.mkPen(self.settings.accent_light_color)
2297
- self.background_brush = pg.mkBrush(self.settings.primary_very_dark_color)
2503
+ self.background_pen = pg.mkPen(self.settings.plot_theme.break_line_color)
2504
+ self.background_brush = pg.mkBrush(self.settings.plot_theme.background_color)
2298
2505
  self.selection_area = SelectionArea(
2299
2506
  top_point=self.top_point,
2300
2507
  bottom_point=self.bottom_point,
2301
2508
  brush=self.selection_brush,
2302
2509
  clipItem=self,
2303
- pen=pg.mkPen(self.settings.error_color),
2510
+ pen=pg.mkPen(self.settings.plot_theme.selected_interval_color),
2304
2511
  )
2305
2512
  self.selection_area.setParentItem(self)
2306
2513
 
@@ -2315,7 +2522,11 @@ class AudioPlots(pg.GraphicsObject):
2315
2522
  self.update_line = pg.InfiniteLine(
2316
2523
  pos=-20,
2317
2524
  span=(0, 1),
2318
- pen=pg.mkPen(self.settings.error_color, width=3, style=QtCore.Qt.PenStyle.DashLine),
2525
+ pen=pg.mkPen(
2526
+ self.settings.plot_theme.selected_interval_color,
2527
+ width=3,
2528
+ style=QtCore.Qt.PenStyle.DashLine,
2529
+ ),
2319
2530
  movable=False, # We have our own code to handle dragless moving.
2320
2531
  )
2321
2532
  self.update_line.setParentItem(self)
@@ -2403,7 +2614,7 @@ class AudioPlots(pg.GraphicsObject):
2403
2614
  self.selection_area.setVisible(True)
2404
2615
  self.selection_model.select_audio(min_time, max_time)
2405
2616
  else:
2406
- self.selection_model.request_start_time(ev.pos().x())
2617
+ self.selection_model.request_start_time(ev.pos().x(), update=True)
2407
2618
  ev.accept()
2408
2619
 
2409
2620
  def hoverEvent(self, ev):
@@ -2486,7 +2697,9 @@ class SpeakerTier(pg.GraphicsObject):
2486
2697
  self.speaker_name = speaker_name
2487
2698
  self.speaker_index = 0
2488
2699
  self.top_point = top_point
2489
- self.speaker_label = pg.TextItem(self.speaker_name, color=self.settings.accent_base_color)
2700
+ self.speaker_label = pg.TextItem(
2701
+ self.speaker_name, color=self.settings.plot_theme.break_line_color
2702
+ )
2490
2703
  self.speaker_label.setFont(self.settings.font)
2491
2704
  self.speaker_label.setParentItem(self)
2492
2705
  self.speaker_label.setZValue(40)
@@ -2494,8 +2707,8 @@ class SpeakerTier(pg.GraphicsObject):
2494
2707
  self.annotation_range = self.top_point - self.bottom_point
2495
2708
  self.extra_tiers = {}
2496
2709
  self.visible_utterances: dict[str, UtteranceRegion] = {}
2497
- self.background_brush = pg.mkBrush(self.settings.primary_very_dark_color)
2498
- self.border = pg.mkPen(self.settings.accent_light_color)
2710
+ self.background_brush = pg.mkBrush(self.settings.plot_theme.background_color)
2711
+ self.border = pg.mkPen(self.settings.plot_theme.break_line_color)
2499
2712
  self.picture = QtGui.QPicture()
2500
2713
  self.has_visible_utterances = False
2501
2714
  self.has_selected_utterances = False
@@ -2510,28 +2723,14 @@ class SpeakerTier(pg.GraphicsObject):
2510
2723
  self.corpus_model.refreshUtteranceText.connect(self.refreshTexts)
2511
2724
  self.selection_model.selectionChanged.connect(self.update_select)
2512
2725
  self.selection_model.model().utterancesReady.connect(self.refresh)
2513
- self.file_model.speakersChanged.connect(self.update_available_speakers)
2514
2726
  self.available_speakers = {}
2515
2727
 
2516
- def update_available_speakers(self):
2517
- self.available_speakers = {}
2518
- for speaker_id in self.file_model.speakers:
2519
- speaker_name = self.corpus_model.get_speaker_name(speaker_id)
2520
- self.available_speakers[speaker_name] = speaker_id
2521
- for reg in self.visible_utterances.values():
2522
- reg.available_speakers = self.available_speakers
2523
-
2524
2728
  def wheelEvent(self, ev):
2525
2729
  self.receivedWheelEvent.emit(ev)
2526
2730
 
2527
2731
  def create_utterance(self, begin, end):
2528
2732
  self.file_model.create_utterance(self.speaker_id, begin, end)
2529
2733
 
2530
- def setSearchterm(self, term):
2531
- self.search_term = term
2532
- for reg in self.visible_utterances.values():
2533
- reg.highlighter.setSearchTerm(term)
2534
-
2535
2734
  def boundingRect(self):
2536
2735
  return QtCore.QRectF(self.picture.boundingRect())
2537
2736
 
@@ -2594,14 +2793,16 @@ class SpeakerTier(pg.GraphicsObject):
2594
2793
  visible_ids = [x.id for x in model_visible_utterances]
2595
2794
  for reg in self.visible_utterances.values():
2596
2795
  reg.hide()
2796
+
2797
+ item_min, item_max = reg.getRegion()
2597
2798
  if (
2598
- self.selection_model.min_time - reg.item.end > 15
2599
- or reg.item.begin - self.selection_model.max_time > 15
2799
+ self.selection_model.min_time - item_max > 15
2800
+ or item_min - self.selection_model.max_time > 15
2600
2801
  or (
2601
2802
  reg.item.id not in visible_ids
2602
2803
  and (
2603
- reg.item.begin < self.selection_model.max_time
2604
- or reg.item.end > self.selection_model.min_time
2804
+ item_min < self.selection_model.max_time
2805
+ or item_max > self.selection_model.min_time
2605
2806
  )
2606
2807
  )
2607
2808
  ):
@@ -2690,10 +2891,10 @@ class SpeakerTier(pg.GraphicsObject):
2690
2891
  if r == reg:
2691
2892
  continue
2692
2893
  other_begin, other_end = r.getRegion()
2693
- if other_begin <= beg < other_end:
2894
+ if other_begin <= beg < other_end or beg <= other_begin < other_end < end:
2694
2895
  reg.setRegion([other_end, end])
2695
2896
  break
2696
- if other_begin < end <= other_end:
2897
+ if other_begin < end <= other_end or end > other_begin > other_end > beg:
2697
2898
  reg.setRegion([beg, other_begin])
2698
2899
  break
2699
2900
  reg.text.begin, reg.text.end = reg.getRegion()
@@ -2716,7 +2917,7 @@ class SpeakerTier(pg.GraphicsObject):
2716
2917
  if new_begin == utt.begin and new_end == utt.end:
2717
2918
  return
2718
2919
  self.selection_model.model().update_utterance_times(utt, begin=new_begin, end=new_end)
2719
- self.selection_model.select_audio(new_begin, None)
2920
+ self.selection_model.request_start_time(new_begin)
2720
2921
  reg.text.begin = new_begin
2721
2922
  reg.text.end = new_end
2722
2923
  reg.update()