myokit 1.33.9__py3-none-any.whl → 1.35.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.
- myokit/__init__.py +9 -36
- myokit/__main__.py +76 -142
- myokit/_aux.py +62 -16
- myokit/_bin/example.mmt +1 -2
- myokit/_bin/install-win/menu.json +7 -7
- myokit/_config.py +22 -31
- myokit/_datablock.py +30 -74
- myokit/_datalog.py +49 -72
- myokit/_err.py +25 -24
- myokit/_expressions.py +50 -68
- myokit/_io.py +15 -27
- myokit/_model_api.py +453 -249
- myokit/_myokit_version.py +1 -5
- myokit/_parsing.py +38 -44
- myokit/_progress.py +5 -8
- myokit/_protocol.py +99 -9
- myokit/_sim/__init__.py +7 -24
- myokit/_sim/cable.c +6 -8
- myokit/_sim/cable.py +6 -8
- myokit/_sim/cmodel.h +125 -70
- myokit/_sim/cmodel.py +12 -14
- myokit/_sim/compiler.py +1 -4
- myokit/_sim/cvodessim.c +196 -118
- myokit/_sim/cvodessim.py +130 -103
- myokit/_sim/differential.hpp +4 -4
- myokit/_sim/fiber_tissue.c +4 -8
- myokit/_sim/fiber_tissue.py +11 -13
- myokit/_sim/jacobian.cpp +2 -2
- myokit/_sim/jacobian.py +11 -8
- myokit/_sim/mcl.h +53 -55
- myokit/_sim/opencl.py +21 -27
- myokit/_sim/openclsim.c +3 -7
- myokit/_sim/openclsim.cl +3 -3
- myokit/_sim/openclsim.py +49 -40
- myokit/_sim/pacing.h +36 -16
- myokit/_sim/rhs.c +6 -13
- myokit/_sim/rhs.py +5 -14
- myokit/_sim/sundials.py +1 -4
- myokit/_system.py +10 -16
- myokit/_unit.py +4 -13
- myokit/float.py +0 -3
- myokit/formats/__init__.py +8 -10
- myokit/formats/ansic/__init__.py +0 -3
- myokit/formats/ansic/_ewriter.py +2 -4
- myokit/formats/ansic/_exporter.py +1 -4
- myokit/formats/ansic/template/cable.c +4 -4
- myokit/formats/ansic/template/euler.c +5 -5
- myokit/formats/ansic/template/sim.c +6 -6
- myokit/formats/axon/__init__.py +1 -3
- myokit/formats/axon/_abf.py +12 -17
- myokit/formats/axon/_atf.py +5 -6
- myokit/formats/axon/_importer.py +0 -3
- myokit/formats/cellml/__init__.py +0 -3
- myokit/formats/cellml/_ewriter.py +3 -6
- myokit/formats/cellml/_exporter.py +3 -6
- myokit/formats/cellml/_importer.py +1 -4
- myokit/formats/cellml/v1/__init__.py +0 -4
- myokit/formats/cellml/v1/_api.py +8 -11
- myokit/formats/cellml/v1/_parser.py +2 -5
- myokit/formats/cellml/v1/_writer.py +2 -11
- myokit/formats/cellml/v2/__init__.py +0 -3
- myokit/formats/cellml/v2/_api.py +8 -17
- myokit/formats/cellml/v2/_parser.py +2 -5
- myokit/formats/cellml/v2/_writer.py +1 -4
- myokit/formats/channelml/__init__.py +0 -3
- myokit/formats/channelml/_importer.py +11 -21
- myokit/formats/cpp/__init__.py +1 -3
- myokit/formats/cpp/_ewriter.py +0 -3
- myokit/formats/cuda/__init__.py +0 -3
- myokit/formats/cuda/_ewriter.py +2 -4
- myokit/formats/cuda/_exporter.py +0 -3
- myokit/formats/cuda/template/kernel.cu +8 -5
- myokit/formats/easyml/__init__.py +0 -3
- myokit/formats/easyml/_ewriter.py +9 -11
- myokit/formats/easyml/_exporter.py +2 -5
- myokit/formats/html/__init__.py +0 -3
- myokit/formats/html/_exporter.py +0 -3
- myokit/formats/html/_flatten.py +5 -21
- myokit/formats/latex/__init__.py +0 -3
- myokit/formats/latex/_ewriter.py +1 -4
- myokit/formats/latex/_exporter.py +4 -6
- myokit/formats/mathml/__init__.py +0 -3
- myokit/formats/mathml/_ewriter.py +2 -11
- myokit/formats/mathml/_parser.py +4 -6
- myokit/formats/matlab/__init__.py +0 -3
- myokit/formats/matlab/_ewriter.py +1 -4
- myokit/formats/matlab/_exporter.py +2 -5
- myokit/formats/matlab/template/main.m +3 -2
- myokit/formats/opencl/__init__.py +0 -3
- myokit/formats/opencl/_ewriter.py +2 -4
- myokit/formats/opencl/_exporter.py +2 -5
- myokit/formats/opencl/template/cable.c +10 -10
- myokit/formats/opencl/template/kernel.cl +1 -1
- myokit/formats/opencl/template/minilog.py +1 -1
- myokit/formats/python/__init__.py +0 -3
- myokit/formats/python/_ewriter.py +2 -5
- myokit/formats/python/_exporter.py +0 -3
- myokit/formats/python/template/sim.py +14 -14
- myokit/formats/sbml/__init__.py +0 -3
- myokit/formats/sbml/_api.py +50 -44
- myokit/formats/sbml/_importer.py +1 -4
- myokit/formats/sbml/_parser.py +2 -5
- myokit/formats/stan/__init__.py +0 -3
- myokit/formats/stan/_ewriter.py +2 -4
- myokit/formats/stan/_exporter.py +2 -5
- myokit/formats/stan/template/cell.stan +3 -3
- myokit/formats/sympy/__init__.py +0 -3
- myokit/formats/sympy/_ereader.py +1 -4
- myokit/formats/sympy/_ewriter.py +2 -5
- myokit/formats/wcp/__init__.py +0 -3
- myokit/formats/wcp/_wcp.py +2 -8
- myokit/formats/xml/__init__.py +0 -3
- myokit/formats/xml/_exporter.py +0 -3
- myokit/formats/xml/_split.py +0 -3
- myokit/gui/__init__.py +80 -246
- myokit/gui/datablock_viewer.py +103 -86
- myokit/gui/datalog_viewer.py +214 -66
- myokit/gui/explorer.py +15 -21
- myokit/gui/ide.py +171 -144
- myokit/gui/progress.py +9 -9
- myokit/gui/source.py +406 -375
- myokit/gui/vargrapher.py +2 -12
- myokit/lib/deps.py +12 -13
- myokit/lib/guess.py +3 -4
- myokit/lib/hh.py +20 -18
- myokit/lib/markov.py +21 -20
- myokit/lib/multi.py +1 -3
- myokit/lib/plots.py +20 -9
- myokit/pacing.py +0 -3
- myokit/pype.py +7 -18
- myokit/tests/__init__.py +3 -6
- myokit/tests/ansic_event_based_pacing.py +1 -4
- myokit/tests/ansic_fixed_form_pacing.py +3 -6
- myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
- myokit/tests/data/clancy-1999-fitting.mmt +1 -0
- myokit/tests/test_aux.py +13 -28
- myokit/tests/test_cellml_v1_api.py +4 -19
- myokit/tests/test_cellml_v1_parser.py +0 -15
- myokit/tests/test_cellml_v1_writer.py +0 -9
- myokit/tests/test_cellml_v2_api.py +4 -19
- myokit/tests/test_cellml_v2_parser.py +0 -15
- myokit/tests/test_cellml_v2_writer.py +0 -9
- myokit/tests/test_cmodel.py +16 -22
- myokit/tests/test_compiler_detection.py +1 -11
- myokit/tests/test_component.py +108 -56
- myokit/tests/test_config.py +34 -67
- myokit/tests/test_datablock.py +1 -9
- myokit/tests/test_datalog.py +19 -24
- myokit/tests/test_dependency_checking.py +8 -23
- myokit/tests/test_expressions.py +0 -9
- myokit/tests/test_float.py +1 -5
- myokit/tests/test_formats.py +0 -9
- myokit/tests/test_formats_axon.py +1 -9
- myokit/tests/test_formats_cellml.py +0 -15
- myokit/tests/test_formats_channelml.py +0 -15
- myokit/tests/test_formats_easyml.py +0 -14
- myokit/tests/test_formats_exporters.py +1 -16
- myokit/tests/test_formats_expression_writers.py +1 -17
- myokit/tests/test_formats_html.py +0 -3
- myokit/tests/test_formats_importers.py +1 -16
- myokit/tests/test_formats_mathml_content.py +0 -9
- myokit/tests/test_formats_mathml_presentation.py +0 -9
- myokit/tests/test_formats_opencl.py +0 -10
- myokit/tests/test_formats_sbml.py +0 -15
- myokit/tests/test_formats_sympy.py +0 -9
- myokit/tests/test_formats_wcp.py +1 -3
- myokit/tests/test_io.py +27 -27
- myokit/tests/test_jacobian_calculator.py +6 -14
- myokit/tests/test_jacobian_tracer.py +0 -9
- myokit/tests/test_lib_deps.py +0 -9
- myokit/tests/test_lib_guess.py +0 -9
- myokit/tests/test_lib_hh.py +18 -12
- myokit/tests/test_lib_markov.py +21 -13
- myokit/tests/test_lib_multi.py +0 -9
- myokit/tests/test_lib_plots.py +13 -8
- myokit/tests/test_meta.py +0 -3
- myokit/tests/test_model.py +390 -96
- myokit/tests/test_model_building.py +44 -96
- myokit/tests/test_opencl_info.py +5 -14
- myokit/tests/test_pacing_factory.py +0 -3
- myokit/tests/test_pacing_system_c.py +1 -23
- myokit/tests/test_pacing_system_py.py +0 -9
- myokit/tests/test_parsing.py +139 -56
- myokit/tests/test_progress_reporters.py +0 -3
- myokit/tests/test_protocol.py +0 -9
- myokit/tests/test_protocol_floating_point.py +1 -10
- myokit/tests/test_protocol_time_series.py +82 -0
- myokit/tests/test_pype.py +0 -9
- myokit/tests/test_quantity.py +0 -9
- myokit/tests/test_rhs_benchmarker.py +1 -9
- myokit/tests/test_sbml_api.py +27 -42
- myokit/tests/test_sbml_parser.py +4 -19
- myokit/tests/test_simulation_1d.py +45 -25
- myokit/tests/test_simulation_cvodes.py +321 -55
- myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
- myokit/tests/test_simulation_fiber_tissue.py +39 -12
- myokit/tests/test_simulation_log_interval.py +1 -431
- myokit/tests/test_simulation_opencl.py +69 -48
- myokit/tests/test_simulation_opencl_log_interval.py +1 -3
- myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
- myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
- myokit/tests/test_system_info.py +1 -11
- myokit/tests/test_tools.py +0 -9
- myokit/tests/test_unit.py +1 -10
- myokit/tests/test_user_functions.py +0 -10
- myokit/tests/test_variable.py +231 -27
- myokit/tools.py +5 -21
- myokit/units.py +5 -3
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
- myokit-1.35.0.dist-info/RECORD +391 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
- myokit/_exec_new.py +0 -15
- myokit/_exec_old.py +0 -15
- myokit/_sim/cvodesim.c +0 -1551
- myokit/_sim/cvodesim.py +0 -674
- myokit/_sim/icsim.cpp +0 -563
- myokit/_sim/icsim.py +0 -363
- myokit/_sim/psim.cpp +0 -656
- myokit/_sim/psim.py +0 -493
- myokit/lib/common.py +0 -1094
- myokit/tests/test_lib_common.py +0 -130
- myokit/tests/test_simulation_cvode.py +0 -612
- myokit/tests/test_simulation_ic.py +0 -108
- myokit/tests/test_simulation_p.py +0 -223
- myokit-1.33.9.dist-info/RECORD +0 -403
- /myokit/formats/opencl/template/{test → test.sh} +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
- {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
myokit/gui/datalog_viewer.py
CHANGED
|
@@ -4,23 +4,16 @@
|
|
|
4
4
|
# This file is part of Myokit.
|
|
5
5
|
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
6
|
#
|
|
7
|
-
|
|
8
|
-
from __future__ import print_function, unicode_literals
|
|
9
|
-
|
|
10
|
-
# Standard library imports
|
|
7
|
+
import configparser
|
|
11
8
|
import gc
|
|
12
9
|
import os
|
|
13
10
|
import sys
|
|
14
11
|
import traceback
|
|
15
12
|
|
|
16
|
-
# Qt imports
|
|
17
13
|
from myokit.gui import QtWidgets, QtGui, QtCore, Qt
|
|
18
14
|
|
|
19
|
-
# Myokit
|
|
20
15
|
import myokit
|
|
21
16
|
import myokit.gui
|
|
22
|
-
|
|
23
|
-
# Myokit components
|
|
24
17
|
import myokit.formats.axon
|
|
25
18
|
import myokit.formats.wcp
|
|
26
19
|
|
|
@@ -32,11 +25,12 @@ from myokit.gui import matplotlib_backend as backend
|
|
|
32
25
|
# NumPy
|
|
33
26
|
import numpy as np
|
|
34
27
|
|
|
35
|
-
#
|
|
28
|
+
# SciPy: Only used to load matlab files.
|
|
36
29
|
try:
|
|
37
|
-
import
|
|
30
|
+
import scipy.io
|
|
31
|
+
has_scipy = True
|
|
38
32
|
except ImportError:
|
|
39
|
-
|
|
33
|
+
has_scipy = False
|
|
40
34
|
|
|
41
35
|
|
|
42
36
|
# Application title
|
|
@@ -108,33 +102,62 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
108
102
|
Graphical interface for viewing DataLog data.
|
|
109
103
|
"""
|
|
110
104
|
def __init__(self, *filenames):
|
|
111
|
-
super(
|
|
105
|
+
super().__init__()
|
|
106
|
+
|
|
112
107
|
# Set Title, icon
|
|
113
108
|
self.setWindowTitle(TITLE + ' ' + myokit.__version__)
|
|
109
|
+
|
|
114
110
|
# Set size, center
|
|
115
111
|
self.resize(800, 600)
|
|
116
112
|
qr = self.frameGeometry()
|
|
117
|
-
cp =
|
|
113
|
+
cp = QtGui.QGuiApplication.primaryScreen().availableGeometry().center()
|
|
118
114
|
qr.moveCenter(cp)
|
|
119
115
|
self.move(qr.topLeft())
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
|
|
117
|
+
# Add widget for file tabs
|
|
118
|
+
self._tabs = TabWidget()
|
|
122
119
|
self._tabs.setTabsClosable(True)
|
|
123
120
|
self._tabs.tabCloseRequested.connect(self.action_close)
|
|
121
|
+
self._tabs.currentChanged.connect(self.fileTabChangeEvent)
|
|
124
122
|
self.setCentralWidget(self._tabs)
|
|
123
|
+
|
|
125
124
|
# Menu bar
|
|
126
125
|
self.create_menu()
|
|
126
|
+
|
|
127
127
|
# Tool bar
|
|
128
128
|
self.create_toolbar()
|
|
129
|
+
|
|
129
130
|
# Status bar
|
|
130
131
|
self.statusBar().showMessage('Ready')
|
|
132
|
+
|
|
131
133
|
# Current path
|
|
132
134
|
self._path = QtCore.QDir.currentPath()
|
|
135
|
+
|
|
133
136
|
# Load settings from ini file
|
|
134
137
|
self.load_config()
|
|
138
|
+
|
|
139
|
+
# File-loading methods
|
|
140
|
+
self._load_actions = {
|
|
141
|
+
'.abf': self.load_abf_file,
|
|
142
|
+
'.atf': self.load_atf_file,
|
|
143
|
+
'.csv': self.load_datalog,
|
|
144
|
+
'.pro': self.load_abf_file,
|
|
145
|
+
'.txt': self.load_txt_file,
|
|
146
|
+
'.wcp': self.load_wcp_file,
|
|
147
|
+
'.zip': self.load_datalog,
|
|
148
|
+
}
|
|
149
|
+
if has_scipy:
|
|
150
|
+
self._load_actions['.mat'] = self.load_mat_file
|
|
151
|
+
|
|
135
152
|
# Load any selected files
|
|
136
153
|
for filename in filenames:
|
|
137
154
|
self.load_file(filename)
|
|
155
|
+
tc = self._tabs.count()
|
|
156
|
+
if tc > 0:
|
|
157
|
+
if tc > 1:
|
|
158
|
+
self._tool_next_file.setEnabled(True)
|
|
159
|
+
self._tool_prev_file.setEnabled(True)
|
|
160
|
+
self._tabs.setCurrentIndex(0)
|
|
138
161
|
|
|
139
162
|
def action_about(self):
|
|
140
163
|
"""
|
|
@@ -146,8 +169,16 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
146
169
|
"""
|
|
147
170
|
Called when a tab should be closed
|
|
148
171
|
"""
|
|
172
|
+
# Remove tab
|
|
149
173
|
tab = self._tabs.widget(index)
|
|
150
174
|
self._tabs.removeTab(index)
|
|
175
|
+
|
|
176
|
+
# Update buttons
|
|
177
|
+
if self._tabs.count() < 2:
|
|
178
|
+
self._tool_next_file.setEnabled(False)
|
|
179
|
+
self._tool_prev_file.setEnabled(False)
|
|
180
|
+
|
|
181
|
+
# Delete tab
|
|
151
182
|
if tab is not None:
|
|
152
183
|
tab.deleteLater()
|
|
153
184
|
gc.collect()
|
|
@@ -168,13 +199,49 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
168
199
|
if filenames:
|
|
169
200
|
# Save current number of tabs
|
|
170
201
|
tab_count = self._tabs.count()
|
|
202
|
+
|
|
171
203
|
# Load files
|
|
172
204
|
for filename in filenames:
|
|
173
205
|
self.load_file(str(filename))
|
|
206
|
+
|
|
174
207
|
# If loading went ok, show first of newly loaded files
|
|
175
|
-
|
|
208
|
+
tab_count_new = self._tabs.count()
|
|
209
|
+
if tab_count_new > tab_count:
|
|
176
210
|
self._tabs.setCurrentIndex(tab_count)
|
|
177
211
|
|
|
212
|
+
# Enable next/previous file menu items
|
|
213
|
+
if tab_count_new > 1:
|
|
214
|
+
self._tool_next_file.setEnabled(True)
|
|
215
|
+
self._tool_prev_file.setEnabled(True)
|
|
216
|
+
|
|
217
|
+
def action_next_file(self):
|
|
218
|
+
"""
|
|
219
|
+
Select the next open file.
|
|
220
|
+
"""
|
|
221
|
+
self._tabs.next()
|
|
222
|
+
|
|
223
|
+
def action_next_var(self):
|
|
224
|
+
"""
|
|
225
|
+
Select the next variable in the selected file.
|
|
226
|
+
"""
|
|
227
|
+
tab = self._tabs.currentWidget()
|
|
228
|
+
if tab:
|
|
229
|
+
tab.next()
|
|
230
|
+
|
|
231
|
+
def action_prev_file(self):
|
|
232
|
+
"""
|
|
233
|
+
Select the previous open file.
|
|
234
|
+
"""
|
|
235
|
+
self._tabs.previous()
|
|
236
|
+
|
|
237
|
+
def action_prev_var(self):
|
|
238
|
+
"""
|
|
239
|
+
Select the previous variable in the selected file
|
|
240
|
+
"""
|
|
241
|
+
tab = self._tabs.currentWidget()
|
|
242
|
+
if tab:
|
|
243
|
+
tab.previous()
|
|
244
|
+
|
|
178
245
|
def closeEvent(self, event=None):
|
|
179
246
|
"""
|
|
180
247
|
Called when window is closed. To force a close (and trigger this
|
|
@@ -196,7 +263,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
196
263
|
# File menu
|
|
197
264
|
self._menu_file = self._menu.addMenu('&File')
|
|
198
265
|
# File > Open
|
|
199
|
-
self._tool_open =
|
|
266
|
+
self._tool_open = QtGui.QAction('&Open', self)
|
|
200
267
|
self._tool_open.setShortcut('Ctrl+O')
|
|
201
268
|
self._tool_open.setStatusTip('Open a file')
|
|
202
269
|
self._tool_open.setIcon(QtGui.QIcon.fromTheme('document-open'))
|
|
@@ -205,21 +272,52 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
205
272
|
# File > ----
|
|
206
273
|
self._menu_file.addSeparator()
|
|
207
274
|
# File > Quit
|
|
208
|
-
self._tool_exit =
|
|
275
|
+
self._tool_exit = QtGui.QAction('&Quit', self)
|
|
209
276
|
self._tool_exit.setShortcut('Ctrl+Q')
|
|
210
277
|
self._tool_exit.setStatusTip('Exit application.')
|
|
211
278
|
self._tool_exit.setIcon(QtGui.QIcon.fromTheme('application-exit'))
|
|
212
279
|
self._tool_exit.triggered.connect(self.close)
|
|
213
280
|
self._menu_file.addAction(self._tool_exit)
|
|
281
|
+
# View menu
|
|
282
|
+
self._menu_view = self._menu.addMenu('&View')
|
|
283
|
+
# View > Next file
|
|
284
|
+
self._tool_next_file = QtGui.QAction('Next file', self)
|
|
285
|
+
self._tool_next_file.setShortcut('Ctrl+PgDown')
|
|
286
|
+
self._tool_next_file.setStatusTip('Select the next open file')
|
|
287
|
+
self._tool_next_file.triggered.connect(self.action_next_file)
|
|
288
|
+
self._tool_next_file.setEnabled(False)
|
|
289
|
+
self._menu_view.addAction(self._tool_next_file)
|
|
290
|
+
# View > Previous file
|
|
291
|
+
self._menu_view.addAction(self._tool_next_file)
|
|
292
|
+
self._tool_prev_file = QtGui.QAction('Previous file', self)
|
|
293
|
+
self._tool_prev_file.setShortcut('Ctrl+PgUp')
|
|
294
|
+
self._tool_prev_file.setStatusTip('Select the previous open file')
|
|
295
|
+
self._tool_prev_file.triggered.connect(self.action_prev_file)
|
|
296
|
+
self._tool_prev_file.setEnabled(False)
|
|
297
|
+
self._menu_view.addAction(self._tool_prev_file)
|
|
298
|
+
# View > Next variable
|
|
299
|
+
self._tool_next_var = QtGui.QAction('Next variable', self)
|
|
300
|
+
self._tool_next_var.setShortcut('PgDown')
|
|
301
|
+
self._tool_next_var.setStatusTip('Show the next variable')
|
|
302
|
+
self._tool_next_var.triggered.connect(self.action_next_var)
|
|
303
|
+
self._tool_next_var.setEnabled(False)
|
|
304
|
+
self._menu_view.addAction(self._tool_next_var)
|
|
305
|
+
# View > Previous var
|
|
306
|
+
self._tool_prev_var = QtGui.QAction('Previous variable', self)
|
|
307
|
+
self._tool_prev_var.setShortcut('PgUp')
|
|
308
|
+
self._tool_prev_var.setStatusTip('Show the previous variable')
|
|
309
|
+
self._tool_prev_var.triggered.connect(self.action_prev_var)
|
|
310
|
+
self._tool_prev_var.setEnabled(False)
|
|
311
|
+
self._menu_view.addAction(self._tool_prev_var)
|
|
214
312
|
# Help menu
|
|
215
313
|
self._menu_help = self._menu.addMenu('&Help')
|
|
216
314
|
# Help > About
|
|
217
|
-
self._tool_about =
|
|
315
|
+
self._tool_about = QtGui.QAction('&About', self)
|
|
218
316
|
self._tool_about.setStatusTip('View information about this program.')
|
|
219
317
|
self._tool_about.triggered.connect(self.action_about)
|
|
220
318
|
self._menu_help.addAction(self._tool_about)
|
|
221
319
|
# Help > License
|
|
222
|
-
self._tool_license =
|
|
320
|
+
self._tool_license = QtGui.QAction('&License', self)
|
|
223
321
|
self._tool_license.setStatusTip('View this program\'s license info.')
|
|
224
322
|
self._tool_license.triggered.connect(self.action_license)
|
|
225
323
|
self._menu_help.addAction(self._tool_license)
|
|
@@ -231,7 +329,8 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
231
329
|
self._toolbar = self.addToolBar('tools')
|
|
232
330
|
self._toolbar.setFloatable(False)
|
|
233
331
|
self._toolbar.setMovable(False)
|
|
234
|
-
self._toolbar.setToolButtonStyle(
|
|
332
|
+
self._toolbar.setToolButtonStyle(
|
|
333
|
+
Qt.ToolButtonStyle.ToolButtonTextBesideIcon)
|
|
235
334
|
self._toolbar.addAction(self._tool_open)
|
|
236
335
|
#self._toolbar.addSeparator()
|
|
237
336
|
|
|
@@ -274,18 +373,8 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
274
373
|
Loads a data file.
|
|
275
374
|
"""
|
|
276
375
|
root, ext = os.path.splitext(os.path.basename(filename))
|
|
277
|
-
actions = {
|
|
278
|
-
'.abf': self.load_abf_file,
|
|
279
|
-
'.atf': self.load_atf_file,
|
|
280
|
-
'.csv': self.load_datalog,
|
|
281
|
-
'.mat': self.load_mat_file,
|
|
282
|
-
'.pro': self.load_abf_file,
|
|
283
|
-
'.txt': self.load_txt_file,
|
|
284
|
-
'.wcp': self.load_wcp_file,
|
|
285
|
-
'.zip': self.load_datalog,
|
|
286
|
-
}
|
|
287
376
|
try:
|
|
288
|
-
action =
|
|
377
|
+
action = self._load_actions[ext.lower()]
|
|
289
378
|
except KeyError:
|
|
290
379
|
QtWidgets.QMessageBox.critical(
|
|
291
380
|
self, TITLE, 'File format not recognized: ' + ext)
|
|
@@ -344,8 +433,7 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
344
433
|
This method requires ``SciPy`` to be installed.
|
|
345
434
|
"""
|
|
346
435
|
try:
|
|
347
|
-
|
|
348
|
-
mat = loadmat(filename)
|
|
436
|
+
mat = scipy.io.loadmat(filename)
|
|
349
437
|
except Exception:
|
|
350
438
|
e = traceback.format_exc()
|
|
351
439
|
QtWidgets.QMessageBox.critical(self, TITLE, e)
|
|
@@ -408,18 +496,56 @@ class DataLogViewer(myokit.gui.MyokitApplication):
|
|
|
408
496
|
"""
|
|
409
497
|
Shows this viewer.
|
|
410
498
|
"""
|
|
411
|
-
super(
|
|
499
|
+
super().show()
|
|
412
500
|
QtWidgets.QApplication.processEvents()
|
|
413
501
|
|
|
502
|
+
def fileTabChangeEvent(self, index):
|
|
503
|
+
"""
|
|
504
|
+
Different file tab selected.
|
|
505
|
+
"""
|
|
506
|
+
if index >= 0:
|
|
507
|
+
tab = self._tabs.widget(index)
|
|
508
|
+
if tab.count() > 1:
|
|
509
|
+
self._tool_prev_var.setEnabled(True)
|
|
510
|
+
self._tool_next_var.setEnabled(True)
|
|
511
|
+
return
|
|
512
|
+
self._tool_prev_var.setEnabled(False)
|
|
513
|
+
self._tool_next_var.setEnabled(False)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
class TabWidget(QtWidgets.QTabWidget):
|
|
517
|
+
"""
|
|
518
|
+
Tab widget that can move up and down when asked.
|
|
519
|
+
"""
|
|
520
|
+
def next(self):
|
|
521
|
+
"""
|
|
522
|
+
Select the next widget.
|
|
523
|
+
"""
|
|
524
|
+
n = self.count()
|
|
525
|
+
if n < 2:
|
|
526
|
+
return
|
|
527
|
+
i = self.currentIndex() + 1
|
|
528
|
+
self.setCurrentIndex(0 if i >= n else i)
|
|
529
|
+
|
|
530
|
+
def previous(self):
|
|
531
|
+
"""
|
|
532
|
+
Select the previous widget.
|
|
533
|
+
"""
|
|
534
|
+
n = self.count()
|
|
535
|
+
if n < 2:
|
|
536
|
+
return
|
|
537
|
+
i = self.currentIndex() - 1
|
|
538
|
+
self.setCurrentIndex(n - 1 if i < 0 else i)
|
|
414
539
|
|
|
415
|
-
|
|
540
|
+
|
|
541
|
+
class AbfTab(TabWidget):
|
|
416
542
|
"""
|
|
417
543
|
A widget displaying an ABF file.
|
|
418
544
|
"""
|
|
419
545
|
def __init__(self, parent, abf):
|
|
420
|
-
super(
|
|
546
|
+
super().__init__(parent)
|
|
421
547
|
self.setTabsClosable(False)
|
|
422
|
-
self.setTabPosition(self.East)
|
|
548
|
+
self.setTabPosition(self.TabPosition.East)
|
|
423
549
|
self._abf = abf
|
|
424
550
|
self._figures = []
|
|
425
551
|
self._axes = []
|
|
@@ -511,19 +637,19 @@ class AbfTab(QtWidgets.QTabWidget):
|
|
|
511
637
|
axes.cla()
|
|
512
638
|
del self._figures, self._axes
|
|
513
639
|
gc.collect()
|
|
514
|
-
super(
|
|
640
|
+
super().deleteLater()
|
|
515
641
|
|
|
516
642
|
|
|
517
|
-
class AtfTab(
|
|
643
|
+
class AtfTab(TabWidget):
|
|
518
644
|
"""
|
|
519
645
|
A widget displaying an AGF file.
|
|
520
646
|
"""
|
|
521
647
|
def __init__(self, parent, atf):
|
|
522
|
-
super(
|
|
648
|
+
super().__init__(parent)
|
|
523
649
|
self._atf = atf
|
|
524
650
|
|
|
525
651
|
self.setTabsClosable(False)
|
|
526
|
-
self.setTabPosition(self.East)
|
|
652
|
+
self.setTabPosition(self.TabPosition.East)
|
|
527
653
|
|
|
528
654
|
self._figures = []
|
|
529
655
|
self._axes = []
|
|
@@ -582,19 +708,19 @@ class AtfTab(QtWidgets.QTabWidget):
|
|
|
582
708
|
axes.cla()
|
|
583
709
|
del self._figures, self._axes
|
|
584
710
|
gc.collect()
|
|
585
|
-
super(
|
|
711
|
+
super().deleteLater()
|
|
586
712
|
|
|
587
713
|
|
|
588
|
-
class CsvTab(
|
|
714
|
+
class CsvTab(TabWidget):
|
|
589
715
|
"""
|
|
590
716
|
A widget displaying a CSV file.
|
|
591
717
|
|
|
592
718
|
The given log must have a time variable set.
|
|
593
719
|
"""
|
|
594
720
|
def __init__(self, parent, log, filename):
|
|
595
|
-
super(
|
|
721
|
+
super().__init__(parent)
|
|
596
722
|
self.setTabsClosable(False)
|
|
597
|
-
self.setTabPosition(self.East)
|
|
723
|
+
self.setTabPosition(self.TabPosition.East)
|
|
598
724
|
self._log = log.npview()
|
|
599
725
|
self._filename = filename
|
|
600
726
|
self._figures = []
|
|
@@ -619,17 +745,33 @@ class CsvTab(QtWidgets.QTabWidget):
|
|
|
619
745
|
self, TITLE, 'Unable to load file: no data found.')
|
|
620
746
|
return
|
|
621
747
|
|
|
622
|
-
#
|
|
623
|
-
|
|
624
|
-
|
|
748
|
+
# Overlapping sweeps or neighboring cells?
|
|
749
|
+
keys = []
|
|
750
|
+
groups = {}
|
|
751
|
+
for key in log.keys():
|
|
752
|
+
if key == time:
|
|
625
753
|
continue
|
|
626
|
-
|
|
754
|
+
index, var = myokit.split_key(key)
|
|
755
|
+
if index:
|
|
756
|
+
group = groups.get(var, None)
|
|
757
|
+
if group is None:
|
|
758
|
+
groups[var] = [index]
|
|
759
|
+
keys.append(var)
|
|
760
|
+
else:
|
|
761
|
+
group.append(index)
|
|
762
|
+
else:
|
|
763
|
+
keys.append(var)
|
|
764
|
+
|
|
765
|
+
# Add tab for each column
|
|
766
|
+
for k in keys:
|
|
767
|
+
self.addTab(self.create_graph_tab(k, groups.get(k)), k)
|
|
627
768
|
|
|
628
|
-
def create_graph_tab(self, key,
|
|
769
|
+
def create_graph_tab(self, key, indices=None):
|
|
629
770
|
"""
|
|
630
|
-
Creates a widget displaying the
|
|
771
|
+
Creates a widget displaying the data stored under ``key``.
|
|
631
772
|
"""
|
|
632
773
|
widget = QtWidgets.QWidget(self)
|
|
774
|
+
|
|
633
775
|
# Create figure
|
|
634
776
|
figure = matplotlib.figure.Figure()
|
|
635
777
|
figure.suptitle(self._filename)
|
|
@@ -638,8 +780,14 @@ class CsvTab(QtWidgets.QTabWidget):
|
|
|
638
780
|
axes = figure.add_subplot(1, 1, 1)
|
|
639
781
|
axes.set_title(key)
|
|
640
782
|
toolbar = backend.NavigationToolbar2QT(canvas, widget)
|
|
783
|
+
|
|
641
784
|
# Draw lines
|
|
642
|
-
|
|
785
|
+
if indices is None:
|
|
786
|
+
axes.plot(self._time, self._log[key])
|
|
787
|
+
else:
|
|
788
|
+
for i in indices:
|
|
789
|
+
axes.plot(self._time, self._log[i + key])
|
|
790
|
+
|
|
643
791
|
# Create a layout
|
|
644
792
|
vbox = QtWidgets.QVBoxLayout()
|
|
645
793
|
vbox.addWidget(canvas)
|
|
@@ -659,17 +807,17 @@ class CsvTab(QtWidgets.QTabWidget):
|
|
|
659
807
|
axes.cla()
|
|
660
808
|
del self._figures, self._axes
|
|
661
809
|
gc.collect()
|
|
662
|
-
super(
|
|
810
|
+
super().deleteLater()
|
|
663
811
|
|
|
664
812
|
|
|
665
|
-
class MatTab(
|
|
813
|
+
class MatTab(TabWidget):
|
|
666
814
|
"""
|
|
667
815
|
A widget displaying a MAT file.
|
|
668
816
|
"""
|
|
669
817
|
def __init__(self, parent, mat, filename):
|
|
670
|
-
super(
|
|
818
|
+
super().__init__(parent)
|
|
671
819
|
self.setTabsClosable(False)
|
|
672
|
-
self.setTabPosition(self.East)
|
|
820
|
+
self.setTabPosition(self.TabPosition.East)
|
|
673
821
|
self._figures = []
|
|
674
822
|
self._filename = filename
|
|
675
823
|
self._axes = []
|
|
@@ -747,17 +895,17 @@ class MatTab(QtWidgets.QTabWidget):
|
|
|
747
895
|
axes.cla()
|
|
748
896
|
del self._figures, self._axes
|
|
749
897
|
gc.collect()
|
|
750
|
-
super(
|
|
898
|
+
super().deleteLater()
|
|
751
899
|
|
|
752
900
|
|
|
753
|
-
class TxtTab(
|
|
901
|
+
class TxtTab(TabWidget):
|
|
754
902
|
"""
|
|
755
903
|
A widget displaying a TXT file (with lots of heuristics!).
|
|
756
904
|
"""
|
|
757
905
|
def __init__(self, parent, data, filename):
|
|
758
|
-
super(
|
|
906
|
+
super().__init__(parent)
|
|
759
907
|
self.setTabsClosable(False)
|
|
760
|
-
self.setTabPosition(self.East)
|
|
908
|
+
self.setTabPosition(self.TabPosition.East)
|
|
761
909
|
self._figures = []
|
|
762
910
|
self._filename = filename
|
|
763
911
|
self._axes = []
|
|
@@ -829,17 +977,17 @@ class TxtTab(QtWidgets.QTabWidget):
|
|
|
829
977
|
axes.cla()
|
|
830
978
|
del self._figures, self._axes
|
|
831
979
|
gc.collect()
|
|
832
|
-
super(
|
|
980
|
+
super().deleteLater()
|
|
833
981
|
|
|
834
982
|
|
|
835
|
-
class WcpTab(
|
|
983
|
+
class WcpTab(TabWidget):
|
|
836
984
|
"""
|
|
837
985
|
A widget displaying a WCP file.
|
|
838
986
|
"""
|
|
839
987
|
def __init__(self, parent, wcp):
|
|
840
|
-
super(
|
|
988
|
+
super().__init__(parent)
|
|
841
989
|
self.setTabsClosable(False)
|
|
842
|
-
self.setTabPosition(self.East)
|
|
990
|
+
self.setTabPosition(self.TabPosition.East)
|
|
843
991
|
self._wcp = wcp
|
|
844
992
|
self._figures = []
|
|
845
993
|
self._axes = []
|
|
@@ -884,5 +1032,5 @@ class WcpTab(QtWidgets.QTabWidget):
|
|
|
884
1032
|
axes.cla()
|
|
885
1033
|
del self._figures, self._axes
|
|
886
1034
|
gc.collect()
|
|
887
|
-
super(
|
|
1035
|
+
super().deleteLater()
|
|
888
1036
|
|
myokit/gui/explorer.py
CHANGED
|
@@ -4,25 +4,16 @@
|
|
|
4
4
|
# This file is part of Myokit.
|
|
5
5
|
# See http://myokit.org for copyright, sharing, and licensing details.
|
|
6
6
|
#
|
|
7
|
-
from __future__ import absolute_import, division
|
|
8
|
-
from __future__ import print_function, unicode_literals
|
|
9
|
-
|
|
10
|
-
# Standard library imports
|
|
11
|
-
|
|
12
|
-
# Myokit
|
|
13
7
|
import myokit
|
|
8
|
+
import myokit.gui
|
|
14
9
|
|
|
15
|
-
# Qt imports
|
|
16
10
|
from myokit.gui import QtCore, QtGui, QtWidgets
|
|
17
|
-
|
|
18
|
-
# GUI components
|
|
19
|
-
import myokit.gui
|
|
11
|
+
from myokit.gui import matplotlib_backend as backend
|
|
20
12
|
from . import progress
|
|
21
13
|
|
|
22
14
|
# Matplotlib (must be imported _after_ gui has had chance to set backend)
|
|
23
15
|
import matplotlib
|
|
24
16
|
import matplotlib.figure
|
|
25
|
-
from myokit.gui import matplotlib_backend as backend
|
|
26
17
|
|
|
27
18
|
|
|
28
19
|
# Constants
|
|
@@ -44,7 +35,7 @@ class Explorer(QtWidgets.QDialog):
|
|
|
44
35
|
*Extends:* ``QtWidgets.QDialog``
|
|
45
36
|
"""
|
|
46
37
|
def __init__(self, parent, sim_method, output_stream, duration=1000):
|
|
47
|
-
super(
|
|
38
|
+
super().__init__(parent)
|
|
48
39
|
self.setWindowTitle('Myokit Explorer')
|
|
49
40
|
self._sim_method = sim_method
|
|
50
41
|
self._stream = output_stream
|
|
@@ -84,7 +75,8 @@ class Explorer(QtWidgets.QDialog):
|
|
|
84
75
|
self._close_button = QtWidgets.QPushButton('Close')
|
|
85
76
|
self._close_button.clicked.connect(self.action_close)
|
|
86
77
|
# Create button layout
|
|
87
|
-
button_layout = QtWidgets.QBoxLayout(
|
|
78
|
+
button_layout = QtWidgets.QBoxLayout(
|
|
79
|
+
QtWidgets.QBoxLayout.Direction.LeftToRight)
|
|
88
80
|
button_layout.addWidget(label1)
|
|
89
81
|
button_layout.addWidget(self._pre_field)
|
|
90
82
|
button_layout.addWidget(label2)
|
|
@@ -93,14 +85,16 @@ class Explorer(QtWidgets.QDialog):
|
|
|
93
85
|
button_layout.addWidget(self._run_button)
|
|
94
86
|
# Create graph options layout
|
|
95
87
|
graph_option_layout = QtWidgets.QBoxLayout(
|
|
96
|
-
QtWidgets.QBoxLayout.LeftToRight)
|
|
88
|
+
QtWidgets.QBoxLayout.Direction.LeftToRight)
|
|
97
89
|
graph_option_layout.addWidget(self._select_x)
|
|
98
90
|
graph_option_layout.addWidget(self._select_y)
|
|
99
91
|
# Create bottom layout
|
|
100
|
-
bottom_layout = QtWidgets.QBoxLayout(
|
|
92
|
+
bottom_layout = QtWidgets.QBoxLayout(
|
|
93
|
+
QtWidgets.QBoxLayout.Direction.LeftToRight)
|
|
101
94
|
bottom_layout.addWidget(self._close_button)
|
|
102
95
|
# Create central layout
|
|
103
|
-
layout = QtWidgets.QBoxLayout(
|
|
96
|
+
layout = QtWidgets.QBoxLayout(
|
|
97
|
+
QtWidgets.QBoxLayout.Direction.TopToBottom)
|
|
104
98
|
layout.addLayout(button_layout)
|
|
105
99
|
layout.addLayout(graph_option_layout)
|
|
106
100
|
layout.addWidget(self._canvas)
|
|
@@ -155,11 +149,11 @@ class Explorer(QtWidgets.QDialog):
|
|
|
155
149
|
"""
|
|
156
150
|
pbar = progress.ProgressBar(self, 'Creating simulation')
|
|
157
151
|
QtWidgets.QApplication.processEvents(
|
|
158
|
-
QtCore.QEventLoop.ExcludeUserInputEvents)
|
|
152
|
+
QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
|
|
159
153
|
try:
|
|
160
154
|
pbar.show()
|
|
161
155
|
QtWidgets.QApplication.processEvents(
|
|
162
|
-
QtCore.QEventLoop.ExcludeUserInputEvents)
|
|
156
|
+
QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
|
|
163
157
|
# Create and run simulation
|
|
164
158
|
out = self._sim_method()
|
|
165
159
|
if type(out) == str:
|
|
@@ -168,7 +162,7 @@ class Explorer(QtWidgets.QDialog):
|
|
|
168
162
|
else:
|
|
169
163
|
m, p, s = out
|
|
170
164
|
QtWidgets.QApplication.processEvents(
|
|
171
|
-
QtCore.QEventLoop.ExcludeUserInputEvents)
|
|
165
|
+
QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
|
|
172
166
|
pre = float(self._pre_field.text())
|
|
173
167
|
run = float(self._run_field.text())
|
|
174
168
|
if pre:
|
|
@@ -176,7 +170,7 @@ class Explorer(QtWidgets.QDialog):
|
|
|
176
170
|
d = s.run(run, progress=pbar.reporter()).npview()
|
|
177
171
|
self._stream.write('Final state: \n' + m.format_state(s.state()))
|
|
178
172
|
QtWidgets.QApplication.processEvents(
|
|
179
|
-
QtCore.QEventLoop.ExcludeUserInputEvents)
|
|
173
|
+
QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
|
|
180
174
|
except myokit.SimulationCancelledError:
|
|
181
175
|
return
|
|
182
176
|
except myokit.MyokitError as e:
|
|
@@ -189,7 +183,7 @@ class Explorer(QtWidgets.QDialog):
|
|
|
189
183
|
pbar.close()
|
|
190
184
|
pbar.deleteLater()
|
|
191
185
|
QtWidgets.QApplication.processEvents(
|
|
192
|
-
QtCore.QEventLoop.ExcludeUserInputEvents)
|
|
186
|
+
QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
|
|
193
187
|
# Reset combo-box keys?
|
|
194
188
|
reset_keys = True
|
|
195
189
|
if self._keys:
|