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.
- myokit/__init__.py +11 -14
- myokit/__main__.py +0 -3
- myokit/_config.py +1 -3
- myokit/_datablock.py +914 -12
- myokit/_model_api.py +1 -3
- myokit/_myokit_version.py +1 -1
- myokit/_protocol.py +14 -28
- myokit/_sim/cable.c +1 -1
- myokit/_sim/cable.py +3 -2
- myokit/_sim/cmodel.h +1 -0
- myokit/_sim/cvodessim.c +79 -42
- myokit/_sim/cvodessim.py +20 -8
- myokit/_sim/fiber_tissue.c +1 -1
- myokit/_sim/fiber_tissue.py +3 -2
- myokit/_sim/openclsim.c +1 -1
- myokit/_sim/openclsim.py +8 -11
- myokit/_sim/pacing.h +121 -106
- myokit/_unit.py +1 -1
- myokit/formats/__init__.py +178 -0
- myokit/formats/axon/_abf.py +911 -841
- myokit/formats/axon/_atf.py +62 -59
- myokit/formats/axon/_importer.py +2 -2
- myokit/formats/heka/__init__.py +38 -0
- myokit/formats/heka/_importer.py +39 -0
- myokit/formats/heka/_patchmaster.py +2512 -0
- myokit/formats/wcp/_wcp.py +318 -133
- myokit/gui/datablock_viewer.py +144 -77
- myokit/gui/datalog_viewer.py +212 -231
- myokit/tests/ansic_event_based_pacing.py +3 -3
- myokit/tests/{ansic_fixed_form_pacing.py → ansic_time_series_pacing.py} +6 -6
- myokit/tests/data/formats/abf-v2.abf +0 -0
- myokit/tests/test_datablock.py +84 -0
- myokit/tests/test_datalog.py +2 -1
- myokit/tests/test_formats_axon.py +589 -136
- myokit/tests/test_formats_wcp.py +191 -22
- myokit/tests/test_pacing_system_c.py +51 -23
- myokit/tests/test_pacing_system_py.py +18 -0
- myokit/tests/test_simulation_1d.py +62 -22
- myokit/tests/test_simulation_cvodes.py +52 -3
- myokit/tests/test_simulation_fiber_tissue.py +35 -4
- myokit/tests/test_simulation_opencl.py +28 -4
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/LICENSE.txt +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/METADATA +1 -1
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/RECORD +47 -44
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/WHEEL +0 -0
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/entry_points.txt +0 -0
- {myokit-1.35.0.dist-info → myokit-1.35.2.dist-info}/top_level.txt +0 -0
myokit/gui/datalog_viewer.py
CHANGED
|
@@ -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 = '
|
|
83
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
542
|
-
"""
|
|
543
|
-
|
|
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
|
-
|
|
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
|
|
562
|
-
"""
|
|
563
|
-
|
|
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
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
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
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
-
|
|
605
|
-
times =
|
|
606
|
-
for
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
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
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
|
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
|
-
|
|
644
|
-
|
|
645
|
-
A widget displaying an
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|