myokit 1.35.0__py3-none-any.whl → 1.35.2__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 (47) hide show
  1. myokit/__init__.py +11 -14
  2. myokit/__main__.py +0 -3
  3. myokit/_config.py +1 -3
  4. myokit/_datablock.py +914 -12
  5. myokit/_model_api.py +1 -3
  6. myokit/_myokit_version.py +1 -1
  7. myokit/_protocol.py +14 -28
  8. myokit/_sim/cable.c +1 -1
  9. myokit/_sim/cable.py +3 -2
  10. myokit/_sim/cmodel.h +1 -0
  11. myokit/_sim/cvodessim.c +79 -42
  12. myokit/_sim/cvodessim.py +20 -8
  13. myokit/_sim/fiber_tissue.c +1 -1
  14. myokit/_sim/fiber_tissue.py +3 -2
  15. myokit/_sim/openclsim.c +1 -1
  16. myokit/_sim/openclsim.py +8 -11
  17. myokit/_sim/pacing.h +121 -106
  18. myokit/_unit.py +1 -1
  19. myokit/formats/__init__.py +178 -0
  20. myokit/formats/axon/_abf.py +911 -841
  21. myokit/formats/axon/_atf.py +62 -59
  22. myokit/formats/axon/_importer.py +2 -2
  23. myokit/formats/heka/__init__.py +38 -0
  24. myokit/formats/heka/_importer.py +39 -0
  25. myokit/formats/heka/_patchmaster.py +2512 -0
  26. myokit/formats/wcp/_wcp.py +318 -133
  27. myokit/gui/datablock_viewer.py +144 -77
  28. myokit/gui/datalog_viewer.py +212 -231
  29. myokit/tests/ansic_event_based_pacing.py +3 -3
  30. myokit/tests/{ansic_fixed_form_pacing.py → ansic_time_series_pacing.py} +6 -6
  31. myokit/tests/data/formats/abf-v2.abf +0 -0
  32. myokit/tests/test_datablock.py +84 -0
  33. myokit/tests/test_datalog.py +2 -1
  34. myokit/tests/test_formats_axon.py +589 -136
  35. myokit/tests/test_formats_wcp.py +191 -22
  36. myokit/tests/test_pacing_system_c.py +51 -23
  37. myokit/tests/test_pacing_system_py.py +18 -0
  38. myokit/tests/test_simulation_1d.py +62 -22
  39. myokit/tests/test_simulation_cvodes.py +52 -3
  40. myokit/tests/test_simulation_fiber_tissue.py +35 -4
  41. myokit/tests/test_simulation_opencl.py +28 -4
  42. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/LICENSE.txt +1 -1
  43. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/METADATA +1 -1
  44. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/RECORD +47 -44
  45. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/WHEEL +0 -0
  46. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/entry_points.txt +0 -0
  47. {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/top_level.txt +0 -0
@@ -14,6 +14,7 @@ from myokit.gui import QtWidgets, QtGui, QtCore, Qt
14
14
 
15
15
  import myokit
16
16
  import myokit.gui
17
+ import myokit.gui.progress
17
18
  import myokit.formats.axon
18
19
  import myokit.formats.wcp
19
20
 
@@ -79,16 +80,18 @@ LICENSE = myokit.LICENSE_HTML
79
80
  FILTER_ABF = 'ABF files (*.abf *.pro)'
80
81
  FILTER_ATF = 'ATF files (*.atf)'
81
82
  FILTER_CSV = 'CSV files (*.csv)'
82
- FILTER_MAT = 'MAT files (*.mat)'
83
- FILTER_TXT = 'TXT files (*.txt)'
83
+ FILTER_MAT = 'Matlab files (*.mat)'
84
+ FILTER_DAT = 'PatchMaster files (*.dat)'
85
+ FILTER_TXT = 'Text files (*.txt)'
84
86
  FILTER_WCP = 'WCP files (*.wcp)'
85
87
  FILTER_ZIP = 'Zipped DataLog files (*.zip)'
86
88
  FILTER_ANY = 'All files (*.*)'
87
- FILTER_ALL = 'Data files (*.abf *.csv *.mat *.pro *.txt *.wcp *.zip)'
89
+ FILTER_ALL = 'Data files (*.abf *.csv *.dat *.mat *.pro *.txt *.wcp *.zip)'
88
90
  FILTER_LIST = ';;'.join([
89
91
  FILTER_ALL,
90
92
  FILTER_ABF,
91
93
  FILTER_CSV,
94
+ FILTER_DAT,
92
95
  FILTER_MAT,
93
96
  FILTER_TXT,
94
97
  FILTER_WCP,
@@ -141,6 +144,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
141
144
  '.abf': self.load_abf_file,
142
145
  '.atf': self.load_atf_file,
143
146
  '.csv': self.load_datalog,
147
+ '.dat': self.load_dat_file,
144
148
  '.pro': self.load_abf_file,
145
149
  '.txt': self.load_txt_file,
146
150
  '.wcp': self.load_wcp_file,
@@ -184,6 +188,18 @@ class DataLogViewer(myokit.gui.MyokitApplication):
184
188
  gc.collect()
185
189
  del tab
186
190
 
191
+ def action_first_var(self):
192
+ """ Select the first variable in the current file. """
193
+ tab = self._tabs.currentWidget()
194
+ if tab:
195
+ tab.first()
196
+
197
+ def action_last_var(self):
198
+ """ Select the last variable in the current file. """
199
+ tab = self._tabs.currentWidget()
200
+ if tab:
201
+ tab.last()
202
+
187
203
  def action_license(self):
188
204
  """
189
205
  Displays this program's licensing information.
@@ -295,6 +311,8 @@ class DataLogViewer(myokit.gui.MyokitApplication):
295
311
  self._tool_prev_file.triggered.connect(self.action_prev_file)
296
312
  self._tool_prev_file.setEnabled(False)
297
313
  self._menu_view.addAction(self._tool_prev_file)
314
+ # View > ----
315
+ self._menu_view.addSeparator()
298
316
  # View > Next variable
299
317
  self._tool_next_var = QtGui.QAction('Next variable', self)
300
318
  self._tool_next_var.setShortcut('PgDown')
@@ -309,6 +327,20 @@ class DataLogViewer(myokit.gui.MyokitApplication):
309
327
  self._tool_prev_var.triggered.connect(self.action_prev_var)
310
328
  self._tool_prev_var.setEnabled(False)
311
329
  self._menu_view.addAction(self._tool_prev_var)
330
+ # View > First variable
331
+ self._tool_first_var = QtGui.QAction('First variable', self)
332
+ self._tool_first_var.setShortcut('Home')
333
+ self._tool_first_var.setStatusTip('Show the first variable')
334
+ self._tool_first_var.triggered.connect(self.action_first_var)
335
+ self._tool_first_var.setEnabled(False)
336
+ self._menu_view.addAction(self._tool_first_var)
337
+ # View > Last var
338
+ self._tool_last_var = QtGui.QAction('Last variable', self)
339
+ self._tool_last_var.setShortcut('End')
340
+ self._tool_last_var.setStatusTip('Show the last variable')
341
+ self._tool_last_var.triggered.connect(self.action_last_var)
342
+ self._tool_last_var.setEnabled(False)
343
+ self._menu_view.addAction(self._tool_last_var)
312
344
  # Help menu
313
345
  self._menu_help = self._menu.addMenu('&Help')
314
346
  # Help > About
@@ -382,9 +414,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
382
414
  action(filename)
383
415
 
384
416
  def load_abf_file(self, filename):
385
- """
386
- Loads an abf file.
387
- """
417
+ """ Loads an ABF file. """
388
418
  try:
389
419
  abf = myokit.formats.axon.AbfFile(filename)
390
420
  except Exception:
@@ -395,9 +425,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
395
425
  self._tabs.addTab(AbfTab(self, abf), os.path.basename(filename))
396
426
 
397
427
  def load_atf_file(self, filename):
398
- """
399
- Loads an ATF file.
400
- """
428
+ """ Loads an ATF file. """
401
429
  try:
402
430
  atf = myokit.formats.axon.AtfFile(filename)
403
431
  except Exception:
@@ -407,10 +435,47 @@ class DataLogViewer(myokit.gui.MyokitApplication):
407
435
  self._path = os.path.dirname(filename)
408
436
  self._tabs.addTab(AtfTab(self, atf), os.path.basename(filename))
409
437
 
438
+ def load_dat_file(self, filename):
439
+ """ Loads a PatchMaster dat file. """
440
+
441
+ pbar = myokit.gui.progress.ProgressBar(
442
+ self, 'Loading groups and series')
443
+ pbar.show()
444
+ reporter = pbar.reporter()
445
+ reporter.enter()
446
+
447
+ flag = QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents
448
+ QtWidgets.QApplication.processEvents(flag)
449
+
450
+ try:
451
+ with myokit.formats.heka.PatchMasterFile(filename) as f:
452
+ n = sum([len(list(g.complete_series())) for g in f])
453
+ i = 0
454
+ stop = False
455
+ for group in f:
456
+ for series in group.complete_series():
457
+ self._tabs.addTab(
458
+ PatchMasterTab(self, series),
459
+ f'{group.label()} {series.label()}')
460
+ i += 1
461
+ if not reporter.update(i / n):
462
+ stop = True
463
+ break
464
+ QtWidgets.QApplication.processEvents(flag)
465
+ if stop:
466
+ break
467
+ self._path = os.path.dirname(filename)
468
+ except Exception:
469
+ e = traceback.format_exc()
470
+ QtWidgets.QMessageBox.critical(self, TITLE, e)
471
+ return
472
+ finally:
473
+ reporter.exit()
474
+ pbar.close()
475
+ pbar.deleteLater()
476
+
410
477
  def load_datalog(self, filename):
411
- """
412
- Loads a DataLog from csv or zip file.
413
- """
478
+ """ Loads a DataLog from csv or zip file. """
414
479
  try:
415
480
  if filename[-4:].lower() == '.csv':
416
481
  log = myokit.DataLog.load_csv(filename)
@@ -443,9 +508,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
443
508
  self._tabs.addTab(MatTab(self, mat, name), name)
444
509
 
445
510
  def load_txt_file(self, filename):
446
- """
447
- Loads a csv file.
448
- """
511
+ """ Loads a text file. """
449
512
  try:
450
513
  data = np.loadtxt(filename)
451
514
  except Exception:
@@ -457,9 +520,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
457
520
  self._tabs.addTab(TxtTab(self, data, name), name)
458
521
 
459
522
  def load_wcp_file(self, filename):
460
- """
461
- Loads a wcp file.
462
- """
523
+ """ Loads a WinWCP file. """
463
524
  try:
464
525
  wcp = myokit.formats.wcp.WcpFile(filename)
465
526
  except Exception:
@@ -506,21 +567,30 @@ class DataLogViewer(myokit.gui.MyokitApplication):
506
567
  if index >= 0:
507
568
  tab = self._tabs.widget(index)
508
569
  if tab.count() > 1:
509
- self._tool_prev_var.setEnabled(True)
570
+ self._tool_first_var.setEnabled(True)
571
+ self._tool_last_var.setEnabled(True)
510
572
  self._tool_next_var.setEnabled(True)
573
+ self._tool_prev_var.setEnabled(True)
511
574
  return
575
+ self._tool_first_var.setEnabled(False)
576
+ self._tool_last_var.setEnabled(False)
512
577
  self._tool_prev_var.setEnabled(False)
513
578
  self._tool_next_var.setEnabled(False)
514
579
 
515
580
 
516
581
  class TabWidget(QtWidgets.QTabWidget):
517
- """
518
- Tab widget that can move up and down when asked.
519
- """
582
+ """ Generic tab widget with first/last next/previous methods. """
583
+
584
+ def first(self):
585
+ """ Select the first widget. """
586
+ self.setCurrentIndex(0)
587
+
588
+ def last(self):
589
+ """ Select the last widget. """
590
+ self.setCurrentIndex(self.count() - 1)
591
+
520
592
  def next(self):
521
- """
522
- Select the next widget.
523
- """
593
+ """ Select the next widget. """
524
594
  n = self.count()
525
595
  if n < 2:
526
596
  return
@@ -528,9 +598,7 @@ class TabWidget(QtWidgets.QTabWidget):
528
598
  self.setCurrentIndex(0 if i >= n else i)
529
599
 
530
600
  def previous(self):
531
- """
532
- Select the previous widget.
533
- """
601
+ """ Select the previous widget. """
534
602
  n = self.count()
535
603
  if n < 2:
536
604
  return
@@ -538,121 +606,144 @@ class TabWidget(QtWidgets.QTabWidget):
538
606
  self.setCurrentIndex(n - 1 if i < 0 else i)
539
607
 
540
608
 
541
- class AbfTab(TabWidget):
542
- """
543
- A widget displaying an ABF file.
544
- """
545
- def __init__(self, parent, abf):
609
+ class GraphTabWidget(TabWidget):
610
+ """ Tab widget to graph a data source. """
611
+
612
+ def __init__(self, parent):
546
613
  super().__init__(parent)
614
+
547
615
  self.setTabsClosable(False)
548
616
  self.setTabPosition(self.TabPosition.East)
549
- self._abf = abf
617
+
550
618
  self._figures = []
551
619
  self._axes = []
552
- for i in range(self._abf.data_channels()):
553
- tab, name = self.create_graph_tab(i)
554
- self.addTab(tab, name)
555
- for i in range(self._abf.protocol_channels()):
556
- tab, name = self.create_protocol_tab(i)
557
- self.addTab(tab, name)
558
- self.addTab(self.create_info_tab(), 'Info')
559
- del self._abf
560
620
 
561
- def create_graph_tab(self, channel):
562
- """
563
- Creates a widget displaying the main data.
564
- """
621
+ def deleteLater(self):
622
+ """ Deletes this tab (later). """
623
+ for figure in self._figures:
624
+ figure.clear()
625
+ for axes in self._axes:
626
+ axes.cla()
627
+ del self._figures, self._axes
628
+ gc.collect()
629
+ super().deleteLater()
630
+
631
+
632
+ class SweepSourceTab(GraphTabWidget):
633
+ """ A tab widget for sources implementing the SweepSource interface. """
634
+
635
+ def __init__(self, parent, source):
636
+ super().__init__(parent)
637
+
638
+ # Add A/D
639
+ for i in range(source.channel_count()):
640
+ self._add_graph_tab(source, i)
641
+
642
+ # Add D/A
643
+ for i in range(source.da_count()):
644
+ self._add_graph_tab(source, i, True)
645
+
646
+ # Add meta data
647
+ self._add_meta_tab(source)
648
+
649
+ def _add_graph_tab(self, source, index, da=False):
650
+ """ Adds a tab for a graph. """
651
+
652
+ # Create widget
565
653
  widget = QtWidgets.QWidget(self)
654
+
566
655
  # Create figure
567
656
  figure = matplotlib.figure.Figure()
568
- figure.suptitle(self._abf.filename())
569
657
  canvas = backend.FigureCanvasQTAgg(figure)
570
658
  canvas.setParent(widget)
571
- axes = figure.add_subplot(1, 1, 1)
572
659
  toolbar = backend.NavigationToolbar2QT(canvas, widget)
573
- # Draw lines
574
- name = 'AD(' + str(channel) + ')' # Default if no data is present
575
- times = None
576
- for i, sweep in enumerate(self._abf):
577
- if times is None:
578
- name = 'AD' + str(sweep[channel].number()) + ': ' \
579
- + sweep[channel].name()
580
- times = sweep[channel].times()
581
- axes.plot(times, sweep[channel].values())
660
+
661
+ # Draw signal
662
+ join_sweeps = not source.equal_length_sweeps()
663
+ if da:
664
+ name = source.da_names(index)
665
+ units = source.da_units(index)
666
+ times, values = source.da(index, join_sweeps)
667
+ else:
668
+ name = source.channel_names(index)
669
+ units = source.channel_units(index)
670
+ times, values = source.channel(index, join_sweeps)
671
+
672
+ axes = figure.add_subplot(1, 1, 1)
673
+ axes.set_xlabel(f'Time {source.time_unit()}')
674
+ axes.set_ylabel(f'{name} {units}')
675
+ if join_sweeps:
676
+ axes.plot(times, values)
677
+ else:
678
+ for v in values:
679
+ axes.plot(times[0], v)
680
+
681
+ # Store for later deletion
682
+ self._figures.append(figure)
683
+ self._axes.append(axes)
684
+
582
685
  # Create a layout
583
686
  vbox = QtWidgets.QVBoxLayout()
584
687
  vbox.addWidget(canvas)
585
688
  vbox.addWidget(toolbar)
586
689
  widget.setLayout(vbox)
587
- self._figures.append(figure)
588
- self._axes.append(axes)
589
- return widget, name
590
690
 
591
- def create_protocol_tab(self, channel):
592
- """
593
- Creates a widget displaying a stored D/A signal.
594
- """
691
+ # Add tab
692
+ self.addTab(widget, name)
693
+
694
+ '''
695
+ def debug_tab(self, channel_index, da=True):
696
+ """ Add a tab graphing data using the DataLog method. """
595
697
  widget = QtWidgets.QWidget(self)
698
+
699
+ # Create widget
700
+ widget = QtWidgets.QWidget(self)
701
+
596
702
  # Create figure
597
703
  figure = matplotlib.figure.Figure()
598
704
  figure.suptitle(self._abf.filename())
599
705
  canvas = backend.FigureCanvasQTAgg(figure)
600
706
  canvas.setParent(widget)
601
- axes = figure.add_subplot(1, 1, 1)
602
707
  toolbar = backend.NavigationToolbar2QT(canvas, widget)
708
+
603
709
  # Draw lines
604
- name = 'DA(' + str(channel) + ')' # Default if no data is present
605
- times = None
606
- for i, sweep in enumerate(self._abf.protocol()):
607
- if times is None:
608
- name = 'DA' + str(sweep[channel].number()) + ': ' \
609
- + sweep[channel].name()
610
- times = sweep[channel].times()
611
- axes.plot(times, sweep[channel].values())
710
+ p = self._abf.da_protocol(channel_index, tu='s', vu='V')
711
+ times, _ = self._abf.da(channel_index)
712
+ for t in times:
713
+ d = p.log_for_interval(t[0], t[-1], for_drawing=True).npview()
714
+ axes.plot(d['time'] - t[0], d['pace'])
715
+
612
716
  # Create a layout
613
717
  vbox = QtWidgets.QVBoxLayout()
614
718
  vbox.addWidget(canvas)
615
719
  vbox.addWidget(toolbar)
616
720
  widget.setLayout(vbox)
617
- self._figures.append(figure)
618
- self._axes.append(axes)
619
- return widget, name
620
721
 
621
- def create_info_tab(self):
622
- """
623
- Creates a tab displaying information about the file.
624
- """
625
- widget = QtWidgets.QTextEdit(self)
626
- widget.setText(self._abf.info(show_header=True))
627
- widget.setReadOnly(True)
628
- return widget
722
+ # Add tab
723
+ self.addTab(widget, name)
724
+ '''
629
725
 
630
- def deleteLater(self):
631
- """
632
- Deletes this tab (later).
633
- """
634
- for figure in self._figures:
635
- figure.clear()
636
- for axes in self._axes:
637
- axes.cla()
638
- del self._figures, self._axes
639
- gc.collect()
640
- super().deleteLater()
726
+ def _add_meta_tab(self, source):
641
727
 
728
+ meta = source.meta_str(True)
729
+ if meta:
730
+ widget = QtWidgets.QTextEdit(self)
731
+ widget.setText(meta)
732
+ widget.setReadOnly(True)
733
+ self.addTab(widget, 'info')
642
734
 
643
- class AtfTab(TabWidget):
644
- """
645
- A widget displaying an AGF file.
646
- """
735
+
736
+ class AbfTab(SweepSourceTab):
737
+ """ A widget displaying an ABF file. """
738
+ pass
739
+
740
+
741
+ class AtfTab(GraphTabWidget):
742
+ """ A widget displaying an ATF file. """
647
743
  def __init__(self, parent, atf):
648
744
  super().__init__(parent)
649
745
  self._atf = atf
650
746
 
651
- self.setTabsClosable(False)
652
- self.setTabPosition(self.TabPosition.East)
653
-
654
- self._figures = []
655
- self._axes = []
656
747
  keys = list(self._atf.keys())
657
748
  if len(keys) > 1:
658
749
  time = keys[0] # Time is always first (and regularly sampled)
@@ -690,28 +781,14 @@ class AtfTab(TabWidget):
690
781
  return widget
691
782
 
692
783
  def create_info_tab(self):
693
- """
694
- Creates a tab displaying information about the file.
695
- """
784
+ """ Creates a tab displaying information about the file. """
696
785
  widget = QtWidgets.QTextEdit(self)
697
786
  widget.setText(self._atf.info())
698
787
  widget.setReadOnly(True)
699
788
  return widget
700
789
 
701
- def deleteLater(self):
702
- """
703
- Deletes this tab (later).
704
- """
705
- for figure in self._figures:
706
- figure.clear()
707
- for axes in self._axes:
708
- axes.cla()
709
- del self._figures, self._axes
710
- gc.collect()
711
- super().deleteLater()
712
-
713
790
 
714
- class CsvTab(TabWidget):
791
+ class CsvTab(GraphTabWidget):
715
792
  """
716
793
  A widget displaying a CSV file.
717
794
 
@@ -719,12 +796,9 @@ class CsvTab(TabWidget):
719
796
  """
720
797
  def __init__(self, parent, log, filename):
721
798
  super().__init__(parent)
722
- self.setTabsClosable(False)
723
- self.setTabPosition(self.TabPosition.East)
799
+
724
800
  self._log = log.npview()
725
801
  self._filename = filename
726
- self._figures = []
727
- self._axes = []
728
802
 
729
803
  # Check time key was found
730
804
  time = log.time_key()
@@ -767,9 +841,7 @@ class CsvTab(TabWidget):
767
841
  self.addTab(self.create_graph_tab(k, groups.get(k)), k)
768
842
 
769
843
  def create_graph_tab(self, key, indices=None):
770
- """
771
- Creates a widget displaying the data stored under ``key``.
772
- """
844
+ """ Creates a widget displaying the data stored under ``key``. """
773
845
  widget = QtWidgets.QWidget(self)
774
846
 
775
847
  # Create figure
@@ -797,30 +869,13 @@ class CsvTab(TabWidget):
797
869
  self._axes.append(axes)
798
870
  return widget
799
871
 
800
- def deleteLater(self):
801
- """
802
- Deletes this tab (later).
803
- """
804
- for figure in self._figures:
805
- figure.clear()
806
- for axes in self._axes:
807
- axes.cla()
808
- del self._figures, self._axes
809
- gc.collect()
810
- super().deleteLater()
811
-
812
872
 
813
- class MatTab(TabWidget):
814
- """
815
- A widget displaying a MAT file.
816
- """
873
+ class MatTab(GraphTabWidget):
874
+ """ A widget displaying a .mat file. """
817
875
  def __init__(self, parent, mat, filename):
818
876
  super().__init__(parent)
819
- self.setTabsClosable(False)
820
- self.setTabPosition(self.TabPosition.East)
821
- self._figures = []
877
+
822
878
  self._filename = filename
823
- self._axes = []
824
879
 
825
880
  # Find usable data
826
881
  for key in mat.keys():
@@ -885,30 +940,19 @@ class MatTab(TabWidget):
885
940
  self._axes.append(axes)
886
941
  return widget
887
942
 
888
- def deleteLater(self):
889
- """
890
- Deletes this tab (later).
891
- """
892
- for figure in self._figures:
893
- figure.clear()
894
- for axes in self._axes:
895
- axes.cla()
896
- del self._figures, self._axes
897
- gc.collect()
898
- super().deleteLater()
899
943
 
944
+ class PatchMasterTab(SweepSourceTab):
945
+ """ A widget displaying a PatchMaster series. """
946
+ pass
947
+
948
+
949
+ class TxtTab(GraphTabWidget):
950
+ """ A widget displaying a .txt file (with lots of heuristics!). """
900
951
 
901
- class TxtTab(TabWidget):
902
- """
903
- A widget displaying a TXT file (with lots of heuristics!).
904
- """
905
952
  def __init__(self, parent, data, filename):
906
953
  super().__init__(parent)
907
- self.setTabsClosable(False)
908
- self.setTabPosition(self.TabPosition.East)
909
- self._figures = []
954
+
910
955
  self._filename = filename
911
- self._axes = []
912
956
 
913
957
  # Find usable data
914
958
  if np.prod(data.shape) == np.max(data.shape):
@@ -967,70 +1011,7 @@ class TxtTab(TabWidget):
967
1011
  self._axes.append(axes)
968
1012
  return widget
969
1013
 
970
- def deleteLater(self):
971
- """
972
- Deletes this tab (later).
973
- """
974
- for figure in self._figures:
975
- figure.clear()
976
- for axes in self._axes:
977
- axes.cla()
978
- del self._figures, self._axes
979
- gc.collect()
980
- super().deleteLater()
981
-
982
-
983
- class WcpTab(TabWidget):
984
- """
985
- A widget displaying a WCP file.
986
- """
987
- def __init__(self, parent, wcp):
988
- super().__init__(parent)
989
- self.setTabsClosable(False)
990
- self.setTabPosition(self.TabPosition.East)
991
- self._wcp = wcp
992
- self._figures = []
993
- self._axes = []
994
- for i in range(self._wcp.records()):
995
- self.addTab(self.create_graph_tab(i), 'Record ' + str(i))
996
- del self._wcp
997
-
998
- def create_graph_tab(self, record):
999
- """
1000
- Creates a widget displaying the data in record i
1001
- """
1002
- widget = QtWidgets.QWidget(self)
1003
- # Create figure
1004
- figure = matplotlib.figure.Figure()
1005
- figure.suptitle(self._wcp.filename())
1006
- canvas = backend.FigureCanvasQTAgg(figure)
1007
- canvas.setParent(widget)
1008
- axes = figure.add_subplot(1, 1, 1)
1009
- toolbar = backend.NavigationToolbar2QT(canvas, widget)
1010
- # Draw lines
1011
- for i in range(self._wcp.channels()):
1012
- axes.plot(
1013
- np.array(self._wcp.times(), copy=True),
1014
- np.array(self._wcp.values(record, i), copy=True),
1015
- )
1016
- # Create a layout
1017
- vbox = QtWidgets.QVBoxLayout()
1018
- vbox.addWidget(canvas)
1019
- vbox.addWidget(toolbar)
1020
- widget.setLayout(vbox)
1021
- self._figures.append(figure)
1022
- self._axes.append(axes)
1023
- return widget
1024
1014
 
1025
- def deleteLater(self):
1026
- """
1027
- Deletes this tab (later).
1028
- """
1029
- for figure in self._figures:
1030
- figure.clear()
1031
- for axes in self._axes:
1032
- axes.cla()
1033
- del self._figures, self._axes
1034
- gc.collect()
1035
- super().deleteLater()
1015
+ class WcpTab(SweepSourceTab):
1016
+ pass
1036
1017
 
@@ -22,7 +22,7 @@ class AnsicEventBasedPacing(myokit.CModule):
22
22
  """
23
23
  _index = 0
24
24
 
25
- def __init__(self, protocol):
25
+ def __init__(self, protocol, initial_time=0):
26
26
  super().__init__()
27
27
 
28
28
  # Unique id
@@ -42,8 +42,8 @@ class AnsicEventBasedPacing(myokit.CModule):
42
42
  self._sys = self._compile(module_name, fname, args, libs, libd, incd)
43
43
 
44
44
  # Initialize
45
- self._sys.init(protocol.clone())
46
- self.advance(0)
45
+ self._sys.init(protocol.clone(), initial_time)
46
+ self.advance(initial_time)
47
47
 
48
48
  def __del__(self):
49
49
  # Free the memory used by the pacing system