Anchor-annotator 0.1.0__py3-none-any.whl → 0.2.1__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/undo.py CHANGED
@@ -6,7 +6,7 @@ import typing
6
6
  import pynini.lib
7
7
  import sqlalchemy
8
8
  from montreal_forced_aligner.data import WordType
9
- from montreal_forced_aligner.db import File, Pronunciation, Speaker, Utterance, Word, bulk_update
9
+ from montreal_forced_aligner.db import File, Pronunciation, Speaker, Utterance, Word
10
10
  from PySide6 import QtCore, QtGui
11
11
  from sqlalchemy.orm import make_transient
12
12
 
@@ -15,6 +15,7 @@ if typing.TYPE_CHECKING:
15
15
  CorpusModel,
16
16
  DiarizationModel,
17
17
  DictionaryTableModel,
18
+ FileUtterancesModel,
18
19
  SpeakerModel,
19
20
  TextFilterQuery,
20
21
  )
@@ -38,9 +39,8 @@ class CorpusCommand(QtGui.QUndoCommand):
38
39
 
39
40
  def redo(self) -> None:
40
41
  with self.corpus_model.edit_lock:
41
- with self.corpus_model.session() as session:
42
- self._redo(session)
43
- session.commit()
42
+ self._redo(self.corpus_model.session)
43
+ self.corpus_model.session.commit()
44
44
  # while True:
45
45
  # try:
46
46
  # with self.corpus_model.session.begin_nested():
@@ -53,9 +53,8 @@ class CorpusCommand(QtGui.QUndoCommand):
53
53
 
54
54
  def undo(self) -> None:
55
55
  with self.corpus_model.edit_lock:
56
- with self.corpus_model.session() as session:
57
- self._undo(session)
58
- session.commit()
56
+ self._undo(self.corpus_model.session)
57
+ self.corpus_model.session.commit()
59
58
  # while True:
60
59
  # try:
61
60
  # with self.corpus_model.session.begin_nested():
@@ -66,6 +65,12 @@ class CorpusCommand(QtGui.QUndoCommand):
66
65
  self.update_data()
67
66
 
68
67
 
68
+ class FileCommand(CorpusCommand):
69
+ def __init__(self, file_model: FileUtterancesModel):
70
+ super().__init__(file_model.corpus_model)
71
+ self.file_model = file_model
72
+
73
+
69
74
  class DictionaryCommand(QtGui.QUndoCommand):
70
75
  def __init__(self, dictionary_model: DictionaryTableModel):
71
76
  super().__init__()
@@ -127,9 +132,9 @@ class SpeakerCommand(QtGui.QUndoCommand):
127
132
  self.update_data()
128
133
 
129
134
 
130
- class DeleteUtteranceCommand(CorpusCommand):
131
- def __init__(self, deleted_utterances: list[Utterance], corpus_model: CorpusModel):
132
- super().__init__(corpus_model)
135
+ class DeleteUtteranceCommand(FileCommand):
136
+ def __init__(self, deleted_utterances: list[Utterance], file_model: FileUtterancesModel):
137
+ super().__init__(file_model)
133
138
  self.deleted_utterances = deleted_utterances
134
139
  self.resets_tier = True
135
140
  self.channels = [
@@ -147,7 +152,6 @@ class DeleteUtteranceCommand(CorpusCommand):
147
152
  for i, utt in enumerate(self.deleted_utterances):
148
153
  make_transient(utt)
149
154
  for x in utt.phone_intervals:
150
- x.duration = None
151
155
  make_transient(x)
152
156
  for x in utt.word_intervals:
153
157
  make_transient(x)
@@ -158,95 +162,80 @@ class DeleteUtteranceCommand(CorpusCommand):
158
162
  def redo(self) -> None:
159
163
  super().redo()
160
164
  self.corpus_model.delete_table_utterances(self.deleted_utterances)
165
+ self.file_model.delete_table_utterances(self.deleted_utterances)
161
166
  self.corpus_model.changeCommandFired.emit()
162
167
 
163
168
  def undo(self) -> None:
164
169
  super().undo()
165
170
  self.corpus_model.add_table_utterances(self.deleted_utterances)
171
+ self.file_model.add_table_utterances(self.deleted_utterances)
166
172
  self.corpus_model.changeCommandFired.emit()
167
173
 
168
174
 
169
- class SplitUtteranceCommand(CorpusCommand):
175
+ class SplitUtteranceCommand(FileCommand):
170
176
  def __init__(
171
177
  self,
172
- split_utterances: list[list[Utterance, ...]],
173
- corpus_model: CorpusModel,
178
+ merged_utterance: Utterance,
179
+ split_utterances: list[Utterance],
180
+ file_model: FileUtterancesModel,
174
181
  update_table: bool = True,
175
182
  ):
176
- super().__init__(corpus_model)
183
+ super().__init__(file_model)
184
+ self.merged_utterance = merged_utterance
177
185
  self.split_utterances = split_utterances
178
186
  self.resets_tier = True
179
187
  self.update_table = update_table
180
- self.channels = [
181
- x[0].channel if x[0].channel is not None else 0 for x in self.split_utterances
182
- ]
188
+ self.channels = [x.channel if x.channel is not None else 0 for x in self.split_utterances]
183
189
  self.setText(
184
190
  QtCore.QCoreApplication.translate("SplitUtteranceCommand", "Split utterances")
185
191
  )
186
192
 
187
193
  def _redo(self, session) -> None:
188
- for i, splits in enumerate(self.split_utterances):
189
- old_utt = splits[0]
190
- split_utts = splits[1:]
191
- session.delete(old_utt)
192
- for u in split_utts:
193
- if u.id is not None:
194
- make_transient(u)
195
- for x in u.phone_intervals:
196
- x.duration = None
197
- make_transient(x)
198
- for x in u.word_intervals:
199
- make_transient(x)
200
- if u.channel is None:
201
- u.channel = self.channels[i]
202
- u.duration = None
203
- u.kaldi_id = None
204
- session.add(u)
205
-
206
- def _undo(self, session) -> None:
207
- for i, splits in enumerate(self.split_utterances):
208
- old_utt = splits[0]
209
- split_utts = splits[1:]
210
- if old_utt.channel is None:
211
- old_utt.channel = self.channels[i]
212
- old_utt.duration = None
213
- old_utt.kaldi_id = None
214
- make_transient(old_utt)
215
- for x in old_utt.phone_intervals:
216
- x.duration = None
194
+ session.delete(self.merged_utterance)
195
+ for u in self.split_utterances:
196
+ if u.id is not None:
197
+ make_transient(u)
198
+ for x in u.phone_intervals:
217
199
  make_transient(x)
218
- for x in old_utt.word_intervals:
200
+ for x in u.word_intervals:
219
201
  make_transient(x)
220
- session.add(old_utt)
221
- for u in split_utts:
222
- session.delete(u)
202
+ if u.channel is None:
203
+ u.channel = self.merged_utterance.channel
204
+ session.add(u)
205
+
206
+ def _undo(self, session) -> None:
207
+ if self.merged_utterance.channel is None:
208
+ self.merged_utterance.channel = self.split_utterances[0].channel
209
+ make_transient(self.merged_utterance)
210
+ for x in self.merged_utterance.phone_intervals:
211
+ make_transient(x)
212
+ for x in self.merged_utterance.word_intervals:
213
+ make_transient(x)
214
+ session.add(self.merged_utterance)
215
+ for u in self.split_utterances:
216
+ session.delete(u)
223
217
 
224
218
  def redo(self) -> None:
225
219
  super().redo()
226
- for splits in self.split_utterances:
227
- old_utt = splits[0]
228
- split_utts = splits[1:]
229
- if self.update_table:
230
- self.corpus_model.split_table_utterances(old_utt, split_utts)
220
+ self.corpus_model.split_table_utterances(self.merged_utterance, self.split_utterances)
221
+ self.file_model.split_table_utterances(self.merged_utterance, self.split_utterances)
231
222
  self.corpus_model.changeCommandFired.emit()
232
223
 
233
224
  def undo(self) -> None:
234
225
  super().undo()
235
- for splits in self.split_utterances:
236
- old_utt = splits[0]
237
- split_utts = splits[1:]
238
- self.corpus_model.merge_table_utterances(old_utt, split_utts)
226
+ self.corpus_model.merge_table_utterances(self.merged_utterance, self.split_utterances)
227
+ self.file_model.merge_table_utterances(self.merged_utterance, self.split_utterances)
239
228
  self.corpus_model.changeCommandFired.emit()
240
229
 
241
230
 
242
- class MergeUtteranceCommand(CorpusCommand):
231
+ class MergeUtteranceCommand(FileCommand):
243
232
  def __init__(
244
233
  self,
245
234
  unmerged_utterances: list[Utterance],
246
235
  merged_utterance: Utterance,
247
- corpus_model: CorpusModel,
236
+ file_model: FileUtterancesModel,
248
237
  ):
249
- super().__init__(corpus_model)
238
+ super().__init__(file_model)
250
239
  self.unmerged_utterances = unmerged_utterances
251
240
  self.merged_utterance = merged_utterance
252
241
  self.resets_tier = True
@@ -263,8 +252,6 @@ class MergeUtteranceCommand(CorpusCommand):
263
252
  make_transient(self.merged_utterance)
264
253
  if self.merged_utterance.channel is None:
265
254
  self.merged_utterance.channel = self.channel
266
- self.merged_utterance.kaldi_id = None
267
- self.merged_utterance.duration = None
268
255
  session.add(self.merged_utterance)
269
256
 
270
257
  def _undo(self, session) -> None:
@@ -273,24 +260,22 @@ class MergeUtteranceCommand(CorpusCommand):
273
260
  if old_utt.channel is None:
274
261
  old_utt.channel = self.channel
275
262
  for x in old_utt.phone_intervals:
276
- x.duration = None
277
263
  make_transient(x)
278
264
  for x in old_utt.word_intervals:
279
265
  make_transient(x)
280
- old_utt.duration = None
281
- old_utt.kaldi_id = None
282
266
  session.add(old_utt)
283
- # session.refresh(self.merged_utterance)
284
267
  session.delete(self.merged_utterance)
285
268
 
286
269
  def redo(self) -> None:
287
270
  super().redo()
288
271
  self.corpus_model.merge_table_utterances(self.merged_utterance, self.unmerged_utterances)
272
+ self.file_model.merge_table_utterances(self.merged_utterance, self.unmerged_utterances)
289
273
  self.corpus_model.changeCommandFired.emit()
290
274
 
291
275
  def undo(self) -> None:
292
276
  super().undo()
293
277
  self.corpus_model.split_table_utterances(self.merged_utterance, self.unmerged_utterances)
278
+ self.file_model.split_table_utterances(self.merged_utterance, self.unmerged_utterances)
294
279
  self.corpus_model.changeCommandFired.emit()
295
280
 
296
281
 
@@ -350,11 +335,10 @@ class MergeSpeakersCommand(CorpusCommand):
350
335
  ).update({Speaker.modified: True})
351
336
 
352
337
 
353
- class CreateUtteranceCommand(CorpusCommand):
354
- def __init__(self, new_utterance: Utterance, corpus_model: CorpusModel):
355
- super().__init__(corpus_model)
338
+ class CreateUtteranceCommand(FileCommand):
339
+ def __init__(self, new_utterance: Utterance, file_model: FileUtterancesModel):
340
+ super().__init__(file_model)
356
341
  self.new_utterance = new_utterance
357
- self.resets_tier = True
358
342
  self.channel = self.new_utterance.channel
359
343
  if self.channel is None:
360
344
  self.channel = 0
@@ -377,18 +361,22 @@ class CreateUtteranceCommand(CorpusCommand):
377
361
  def redo(self) -> None:
378
362
  super().redo()
379
363
  self.corpus_model.add_table_utterances([self.new_utterance])
364
+ self.file_model.add_table_utterances([self.new_utterance])
380
365
  self.corpus_model.changeCommandFired.emit()
381
366
 
382
367
  def undo(self) -> None:
383
368
  super().undo()
384
369
  self.corpus_model.delete_table_utterances([self.new_utterance])
370
+ self.file_model.delete_table_utterances([self.new_utterance])
385
371
  self.corpus_model.changeCommandFired.emit()
386
372
 
387
373
 
388
- class UpdateUtteranceTimesCommand(CorpusCommand):
389
- def __init__(self, utterance: Utterance, begin: float, end: float, corpus_model: CorpusModel):
390
- super().__init__(corpus_model)
391
- self.utterance_id = utterance.id
374
+ class UpdateUtteranceTimesCommand(FileCommand):
375
+ def __init__(
376
+ self, utterance: Utterance, begin: float, end: float, file_model: FileUtterancesModel
377
+ ):
378
+ super().__init__(file_model)
379
+ self.utterance = utterance
392
380
  self.new_begin = begin
393
381
  self.old_begin = utterance.begin
394
382
  self.new_end = end
@@ -400,37 +388,31 @@ class UpdateUtteranceTimesCommand(CorpusCommand):
400
388
  )
401
389
 
402
390
  def _redo(self, session) -> None:
403
- session.query(Utterance).filter(Utterance.id == self.utterance_id).update(
404
- {
405
- Utterance.begin: self.new_begin,
406
- Utterance.end: self.new_end,
407
- Utterance.xvector: None,
408
- Utterance.ivector: None,
409
- Utterance.features: None,
410
- }
411
- )
391
+ self.utterance.begin = self.new_begin
392
+ self.utterance.end = self.new_end
393
+ self.utterance.xvector = None
394
+ self.utterance.ivector = None
395
+ self.utterance.features = None
396
+ session.merge(self.utterance)
412
397
 
413
398
  def _undo(self, session) -> None:
414
- session.query(Utterance).filter(Utterance.id == self.utterance_id).update(
415
- {
416
- Utterance.begin: self.old_begin,
417
- Utterance.end: self.old_end,
418
- Utterance.xvector: None,
419
- Utterance.ivector: None,
420
- Utterance.features: None,
421
- }
422
- )
399
+ self.utterance.begin = self.old_begin
400
+ self.utterance.end = self.old_end
401
+ self.utterance.xvector = None
402
+ self.utterance.ivector = None
403
+ self.utterance.features = None
404
+ session.merge(self.utterance)
423
405
 
424
406
  def update_data(self):
425
407
  super().update_data()
426
408
  self.corpus_model.changeCommandFired.emit()
427
- self.corpus_model.update_utterance_table_row(self.utterance_id)
409
+ self.corpus_model.update_utterance_table_row(self.utterance)
428
410
 
429
411
 
430
- class UpdateUtteranceTextCommand(CorpusCommand):
431
- def __init__(self, utterance: Utterance, new_text: str, corpus_model: CorpusModel):
432
- super().__init__(corpus_model)
433
- self.utterance_id = utterance.id
412
+ class UpdateUtteranceTextCommand(FileCommand):
413
+ def __init__(self, utterance: Utterance, new_text: str, file_model: FileUtterancesModel):
414
+ super().__init__(file_model)
415
+ self.utterance = utterance
434
416
  self.speaker_id = utterance.speaker_id
435
417
  self.old_text = utterance.text
436
418
  self.new_text = new_text
@@ -445,40 +427,28 @@ class UpdateUtteranceTextCommand(CorpusCommand):
445
427
  for w in self.new_text.split():
446
428
  if not self.corpus_model.dictionary_model.check_word(w, self.speaker_id):
447
429
  oovs.add(w)
448
- session.query(Utterance).filter(Utterance.id == self.utterance_id).update(
449
- {
450
- Utterance.text: self.new_text,
451
- Utterance.normalized_text: self.new_text, # FIXME: Update this
452
- Utterance.oovs: " ".join(oovs),
453
- Utterance.ignored: not self.new_text,
454
- }
455
- )
430
+ self.utterance.text = self.new_text
431
+ self.utterance.normalized_text = self.new_text # FIXME: Update this
432
+ self.utterance.oovs = " ".join(oovs)
433
+ self.utterance.ignored = not self.new_text
434
+ session.merge(self.utterance)
456
435
 
457
436
  def _undo(self, session) -> None:
458
437
  oovs = set()
459
438
  for w in self.new_text.split():
460
439
  if not self.corpus_model.dictionary_model.check_word(w, self.speaker_id):
461
440
  oovs.add(w)
462
- session.query(Utterance).filter(Utterance.id == self.utterance_id).update(
463
- {
464
- Utterance.text: self.old_text,
465
- Utterance.oovs: " ".join(oovs),
466
- Utterance.ignored: not self.old_text,
467
- }
468
- )
469
-
470
- def update_data(self):
471
- super().update_data()
472
- try:
473
- self.corpus_model.update_utterance_table_row(self.utterance_id)
474
- except KeyError:
475
- pass
441
+ self.utterance.text = self.old_text
442
+ self.utterance.normalized_text = self.old_text # FIXME: Update this
443
+ self.utterance.oovs = " ".join(oovs)
444
+ self.utterance.ignored = not self.old_text
445
+ session.merge(self.utterance)
476
446
 
477
447
  def id(self) -> int:
478
448
  return 1
479
449
 
480
450
  def mergeWith(self, other: UpdateUtteranceTextCommand) -> bool:
481
- if other.id() != self.id() or other.utterance_id != self.utterance_id:
451
+ if other.id() != self.id() or other.utterance.id != self.utterance.id:
482
452
  return False
483
453
  self.new_text = other.new_text
484
454
  return True
@@ -627,16 +597,17 @@ class UpdateSpeakerCommand(SpeakerCommand):
627
597
  )
628
598
 
629
599
 
630
- class UpdateUtteranceSpeakerCommand(CorpusCommand):
600
+ class UpdateUtteranceSpeakerCommand(FileCommand):
631
601
  def __init__(
632
602
  self,
633
603
  utterances: typing.Union[Utterance, typing.List[Utterance]],
634
604
  new_speaker: typing.Union[Speaker, int],
635
- corpus_model: CorpusModel,
605
+ file_model: FileUtterancesModel,
636
606
  ):
637
- super().__init__(corpus_model)
607
+ super().__init__(file_model)
638
608
  if not isinstance(utterances, list):
639
609
  utterances = [utterances]
610
+ self.utterances = utterances
640
611
  self.utterance_ids = [x.id for x in utterances]
641
612
  self.file_ids = set([x.file_id for x in utterances])
642
613
  self.old_speaker_ids = [x.speaker_id for x in utterances]
@@ -644,7 +615,6 @@ class UpdateUtteranceSpeakerCommand(CorpusCommand):
644
615
  self.new_speaker_id = new_speaker
645
616
  else:
646
617
  self.new_speaker_id = new_speaker.id
647
- self.resets_tier = True
648
618
  self.setText(
649
619
  QtCore.QCoreApplication.translate(
650
620
  "UpdateUtteranceSpeakerCommand", "Update utterance speaker"
@@ -673,18 +643,15 @@ class UpdateUtteranceSpeakerCommand(CorpusCommand):
673
643
  )
674
644
  )
675
645
  session.flush()
676
- session.query(Utterance).filter(Utterance.id.in_(self.utterance_ids)).update(
677
- {Utterance.speaker_id: self.new_speaker_id}
678
- )
646
+ for u in self.utterances:
647
+ u.speaker_id = self.new_speaker_id
679
648
  session.query(Speaker).filter(
680
649
  Speaker.id.in_(self.old_speaker_ids + [self.new_speaker_id])
681
650
  ).update({Speaker.modified: True})
682
651
 
683
652
  def _undo(self, session) -> None:
684
- update_mappings = []
685
- for i, u_id in enumerate(self.utterance_ids):
686
- update_mappings.append({"id": u_id, "speaker_id": self.old_speaker_ids[i]})
687
- bulk_update(self.corpus_model.session, Utterance, update_mappings)
653
+ for i, u in enumerate(self.utterances):
654
+ u.speaker_id = self.old_speaker_ids[i]
688
655
  session.query(Speaker).filter(
689
656
  Speaker.id.in_(self.old_speaker_ids + [self.new_speaker_id])
690
657
  ).update({Speaker.modified: True})
@@ -692,6 +659,8 @@ class UpdateUtteranceSpeakerCommand(CorpusCommand):
692
659
  def update_data(self):
693
660
  super().update_data()
694
661
  self.corpus_model.set_file_modified(self.file_ids)
662
+ self.corpus_model.change_speaker_table_utterances(self.utterances)
663
+ self.file_model.change_speaker_table_utterances(self.utterances)
695
664
  self.corpus_model.changeCommandFired.emit()
696
665
  self.corpus_model.update_data()
697
666
  self.corpus_model.runFunction.emit(
anchor/widgets.py CHANGED
@@ -25,6 +25,8 @@ from anchor.models import (
25
25
  CorpusSelectionModel,
26
26
  DiarizationModel,
27
27
  DictionaryTableModel,
28
+ FileSelectionModel,
29
+ FileUtterancesModel,
28
30
  OovModel,
29
31
  SpeakerModel,
30
32
  TextFilterQuery,
@@ -61,7 +63,6 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
61
63
  self.max_time = None
62
64
  self.start_load_time = None
63
65
  self.min_time = None
64
- self.corpus_model = None
65
66
  self.selection_model = None
66
67
  self.timer = QtCore.QTimer(self)
67
68
  self.timer.setInterval(1)
@@ -78,7 +79,6 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
78
79
  self._audio_output.setDevice(self.devices.defaultAudioOutput())
79
80
  self.setAudioOutput(self._audio_output)
80
81
  self.playbackStateChanged.connect(self.reset_position)
81
- self.mediaStatusChanged.connect(self.update_load)
82
82
  self.fade_in_anim = QtCore.QPropertyAnimation(self._audio_output, b"volume")
83
83
  self.fade_in_anim.setDuration(10)
84
84
  self.fade_in_anim.setStartValue(0.1)
@@ -95,11 +95,6 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
95
95
  self.fade_out_anim.finished.connect(super().pause)
96
96
  self.file_path = None
97
97
 
98
- def update_load(self, state):
99
- if state == self.MediaStatus.LoadedMedia:
100
- self.reset_position()
101
- self.audioReady.emit(True)
102
-
103
98
  def handle_error(self, *args):
104
99
  print("ERROR")
105
100
  print(args)
@@ -118,12 +113,22 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
118
113
  self.fade_in_anim.start()
119
114
 
120
115
  def startTime(self):
121
- if self.selection_model.selected_min_time is not None:
116
+ if (
117
+ self.selection_model.selected_min_time is not None
118
+ and self.selection_model.min_time
119
+ <= self.selection_model.selected_min_time
120
+ <= self.selection_model.max_time
121
+ ):
122
122
  return self.selection_model.selected_min_time
123
123
  return self.selection_model.min_time
124
124
 
125
125
  def maxTime(self):
126
- if self.selection_model.selected_max_time is not None:
126
+ if (
127
+ self.selection_model.selected_max_time is not None
128
+ and self.selection_model.min_time
129
+ <= self.selection_model.selected_max_time
130
+ <= self.selection_model.max_time
131
+ ):
127
132
  return self.selection_model.selected_max_time
128
133
  return self.selection_model.max_time
129
134
 
@@ -149,14 +154,10 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
149
154
  break
150
155
  self._audio_output.setDevice(o)
151
156
 
152
- def set_corpus_models(
153
- self, corpus_model: Optional[CorpusModel], selection_model: Optional[CorpusSelectionModel]
154
- ):
155
- self.corpus_model = corpus_model
156
- self.selection_model = selection_model
157
- if corpus_model is None:
157
+ def set_models(self, selection_model: Optional[FileSelectionModel]):
158
+ if selection_model is None:
158
159
  return
159
- # self.selection_model.fileAboutToChange.connect(self.unload_file)
160
+ self.selection_model = selection_model
160
161
  self.selection_model.fileChanged.connect(self.loadNewFile)
161
162
  self.selection_model.viewChanged.connect(self.update_times)
162
163
  self.selection_model.selectionAudioChanged.connect(self.update_selection_times)
@@ -187,29 +188,27 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
187
188
  self.setCurrentTime(self.startTime())
188
189
 
189
190
  def update_times(self):
190
- if (
191
- self.playbackState() == QtMultimedia.QMediaPlayer.PlaybackState.StoppedState
192
- or self.currentTime() < self.startTime()
193
- or self.currentTime() > self.maxTime()
194
- ):
191
+ if self.currentTime() < self.startTime() or self.currentTime() > self.maxTime():
192
+ self.stop()
193
+ if self.playbackState() != QtMultimedia.QMediaPlayer.PlaybackState.PlayingState:
195
194
  self.setCurrentTime(self.startTime())
196
195
 
197
196
  def loadNewFile(self, *args):
198
197
  self.audioReady.emit(False)
199
198
  self.stop()
200
199
  try:
201
- new_file = self.selection_model.current_file.sound_file.sound_file_path
200
+ new_file = self.selection_model.model().file.sound_file.sound_file_path
202
201
  except Exception:
203
202
  self.setSource(QtCore.QUrl())
204
203
  return
205
204
  if (
206
205
  self.selection_model.max_time is None
207
- or self.selection_model.current_file is None
208
- or self.selection_model.current_file.duration is None
206
+ or self.selection_model.model().file is None
207
+ or self.selection_model.model().file.duration is None
209
208
  ):
210
209
  self.setSource(QtCore.QUrl())
211
210
  return
212
- self.channels = self.selection_model.current_file.num_channels
211
+ self.channels = self.selection_model.model().file.num_channels
213
212
  self.setSource(f"file:///{new_file}")
214
213
  self.setPosition(0)
215
214
  self.audioReady.emit(True)
@@ -218,19 +217,6 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
218
217
  pos = self.position()
219
218
  return pos / 1000
220
219
 
221
- def setMaxTime(self, max_time):
222
- if max_time is None:
223
- return
224
- self.max_time = max_time * 1000
225
-
226
- def setMinTime(
227
- self, min_time
228
- ): # Positions for MediaPlayer are in milliseconds, no SR required
229
- if min_time is None:
230
- min_time = 0
231
- self.min_time = int(min_time * 1000)
232
- self.setCurrentTime(min_time)
233
-
234
220
  def setCurrentTime(self, time):
235
221
  if time is None:
236
222
  time = 0
@@ -245,7 +231,7 @@ class MediaPlayer(QtMultimedia.QMediaPlayer): # pragma: no cover
245
231
  self.stop()
246
232
  self.setSource(
247
233
  QtCore.QUrl.fromLocalFile(
248
- self.selection_model.current_file.sound_file.sound_file_path
234
+ self.selection_model.model().file.sound_file.sound_file_path
249
235
  )
250
236
  )
251
237
  self.play()
@@ -416,7 +402,7 @@ class UtteranceListTable(AnchorTableView):
416
402
  def set_models(self, model: CorpusModel, selection_model: CorpusSelectionModel):
417
403
  self.setModel(model)
418
404
  self.setSelectionModel(selection_model)
419
- self.doubleClicked.connect(self.selectionModel().focusUtterance)
405
+ self.doubleClicked.connect(self.selectionModel().focus_utterance)
420
406
  self.model().utteranceTextUpdated.connect(self.repaint)
421
407
  self.refresh_settings()
422
408
  model.corpusLoaded.connect(self.update_header)
@@ -793,6 +779,7 @@ class UtteranceDetailWidget(QtWidgets.QWidget): # pragma: no cover
793
779
  self.settings = AnchorSettings()
794
780
  self.setAttribute(QtCore.Qt.WidgetAttribute.WA_StyledBackground, True)
795
781
  self.corpus_model = None
782
+ self.file_model = None
796
783
  self.selection_model = None
797
784
  self.dictionary_model = None
798
785
  self.plot_widget = UtteranceView(self)
@@ -829,24 +816,28 @@ class UtteranceDetailWidget(QtWidgets.QWidget): # pragma: no cover
829
816
  def set_models(
830
817
  self,
831
818
  corpus_model: CorpusModel,
832
- selection_model: CorpusSelectionModel,
819
+ file_model: FileUtterancesModel,
820
+ selection_model: FileSelectionModel,
833
821
  dictionary_model: DictionaryTableModel,
834
822
  ):
835
823
  self.corpus_model = corpus_model
824
+ self.file_model = file_model
836
825
  self.selection_model = selection_model
837
826
  self.dictionary_model = dictionary_model
838
827
  self.corpus_model.textFilterChanged.connect(self.plot_widget.set_search_term)
839
828
  self.selection_model.viewChanged.connect(self.update_to_slider)
840
829
  self.selection_model.fileChanged.connect(self.update_to_slider)
841
- self.plot_widget.set_models(corpus_model, selection_model, self.dictionary_model)
830
+ self.plot_widget.set_models(
831
+ corpus_model, file_model, selection_model, self.dictionary_model
832
+ )
842
833
 
843
834
  def update_to_slider(self):
844
835
  with QtCore.QSignalBlocker(self.scroll_bar):
845
- if self.selection_model.current_file is None or self.selection_model.min_time is None:
836
+ if self.selection_model.model().file is None or self.selection_model.min_time is None:
846
837
  return
847
838
  if (
848
839
  self.selection_model.min_time == 0
849
- and self.selection_model.max_time == self.selection_model.current_file.duration
840
+ and self.selection_model.max_time == self.selection_model.model().file.duration
850
841
  ):
851
842
  self.scroll_bar.setPageStep(10)
852
843
  self.scroll_bar.setEnabled(False)
@@ -854,7 +845,7 @@ class UtteranceDetailWidget(QtWidgets.QWidget): # pragma: no cover
854
845
  self.pan_right_button.setEnabled(False)
855
846
  self.scroll_bar.setMaximum(0)
856
847
  return
857
- duration_ms = int(self.selection_model.current_file.duration * 1000)
848
+ duration_ms = int(self.selection_model.model().file.duration * 1000)
858
849
  begin = self.selection_model.min_time * 1000
859
850
  end = self.selection_model.max_time * 1000
860
851
  window_size_ms = int(end - begin)