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.
Files changed (229) hide show
  1. myokit/__init__.py +9 -36
  2. myokit/__main__.py +76 -142
  3. myokit/_aux.py +62 -16
  4. myokit/_bin/example.mmt +1 -2
  5. myokit/_bin/install-win/menu.json +7 -7
  6. myokit/_config.py +22 -31
  7. myokit/_datablock.py +30 -74
  8. myokit/_datalog.py +49 -72
  9. myokit/_err.py +25 -24
  10. myokit/_expressions.py +50 -68
  11. myokit/_io.py +15 -27
  12. myokit/_model_api.py +453 -249
  13. myokit/_myokit_version.py +1 -5
  14. myokit/_parsing.py +38 -44
  15. myokit/_progress.py +5 -8
  16. myokit/_protocol.py +99 -9
  17. myokit/_sim/__init__.py +7 -24
  18. myokit/_sim/cable.c +6 -8
  19. myokit/_sim/cable.py +6 -8
  20. myokit/_sim/cmodel.h +125 -70
  21. myokit/_sim/cmodel.py +12 -14
  22. myokit/_sim/compiler.py +1 -4
  23. myokit/_sim/cvodessim.c +196 -118
  24. myokit/_sim/cvodessim.py +130 -103
  25. myokit/_sim/differential.hpp +4 -4
  26. myokit/_sim/fiber_tissue.c +4 -8
  27. myokit/_sim/fiber_tissue.py +11 -13
  28. myokit/_sim/jacobian.cpp +2 -2
  29. myokit/_sim/jacobian.py +11 -8
  30. myokit/_sim/mcl.h +53 -55
  31. myokit/_sim/opencl.py +21 -27
  32. myokit/_sim/openclsim.c +3 -7
  33. myokit/_sim/openclsim.cl +3 -3
  34. myokit/_sim/openclsim.py +49 -40
  35. myokit/_sim/pacing.h +36 -16
  36. myokit/_sim/rhs.c +6 -13
  37. myokit/_sim/rhs.py +5 -14
  38. myokit/_sim/sundials.py +1 -4
  39. myokit/_system.py +10 -16
  40. myokit/_unit.py +4 -13
  41. myokit/float.py +0 -3
  42. myokit/formats/__init__.py +8 -10
  43. myokit/formats/ansic/__init__.py +0 -3
  44. myokit/formats/ansic/_ewriter.py +2 -4
  45. myokit/formats/ansic/_exporter.py +1 -4
  46. myokit/formats/ansic/template/cable.c +4 -4
  47. myokit/formats/ansic/template/euler.c +5 -5
  48. myokit/formats/ansic/template/sim.c +6 -6
  49. myokit/formats/axon/__init__.py +1 -3
  50. myokit/formats/axon/_abf.py +12 -17
  51. myokit/formats/axon/_atf.py +5 -6
  52. myokit/formats/axon/_importer.py +0 -3
  53. myokit/formats/cellml/__init__.py +0 -3
  54. myokit/formats/cellml/_ewriter.py +3 -6
  55. myokit/formats/cellml/_exporter.py +3 -6
  56. myokit/formats/cellml/_importer.py +1 -4
  57. myokit/formats/cellml/v1/__init__.py +0 -4
  58. myokit/formats/cellml/v1/_api.py +8 -11
  59. myokit/formats/cellml/v1/_parser.py +2 -5
  60. myokit/formats/cellml/v1/_writer.py +2 -11
  61. myokit/formats/cellml/v2/__init__.py +0 -3
  62. myokit/formats/cellml/v2/_api.py +8 -17
  63. myokit/formats/cellml/v2/_parser.py +2 -5
  64. myokit/formats/cellml/v2/_writer.py +1 -4
  65. myokit/formats/channelml/__init__.py +0 -3
  66. myokit/formats/channelml/_importer.py +11 -21
  67. myokit/formats/cpp/__init__.py +1 -3
  68. myokit/formats/cpp/_ewriter.py +0 -3
  69. myokit/formats/cuda/__init__.py +0 -3
  70. myokit/formats/cuda/_ewriter.py +2 -4
  71. myokit/formats/cuda/_exporter.py +0 -3
  72. myokit/formats/cuda/template/kernel.cu +8 -5
  73. myokit/formats/easyml/__init__.py +0 -3
  74. myokit/formats/easyml/_ewriter.py +9 -11
  75. myokit/formats/easyml/_exporter.py +2 -5
  76. myokit/formats/html/__init__.py +0 -3
  77. myokit/formats/html/_exporter.py +0 -3
  78. myokit/formats/html/_flatten.py +5 -21
  79. myokit/formats/latex/__init__.py +0 -3
  80. myokit/formats/latex/_ewriter.py +1 -4
  81. myokit/formats/latex/_exporter.py +4 -6
  82. myokit/formats/mathml/__init__.py +0 -3
  83. myokit/formats/mathml/_ewriter.py +2 -11
  84. myokit/formats/mathml/_parser.py +4 -6
  85. myokit/formats/matlab/__init__.py +0 -3
  86. myokit/formats/matlab/_ewriter.py +1 -4
  87. myokit/formats/matlab/_exporter.py +2 -5
  88. myokit/formats/matlab/template/main.m +3 -2
  89. myokit/formats/opencl/__init__.py +0 -3
  90. myokit/formats/opencl/_ewriter.py +2 -4
  91. myokit/formats/opencl/_exporter.py +2 -5
  92. myokit/formats/opencl/template/cable.c +10 -10
  93. myokit/formats/opencl/template/kernel.cl +1 -1
  94. myokit/formats/opencl/template/minilog.py +1 -1
  95. myokit/formats/python/__init__.py +0 -3
  96. myokit/formats/python/_ewriter.py +2 -5
  97. myokit/formats/python/_exporter.py +0 -3
  98. myokit/formats/python/template/sim.py +14 -14
  99. myokit/formats/sbml/__init__.py +0 -3
  100. myokit/formats/sbml/_api.py +50 -44
  101. myokit/formats/sbml/_importer.py +1 -4
  102. myokit/formats/sbml/_parser.py +2 -5
  103. myokit/formats/stan/__init__.py +0 -3
  104. myokit/formats/stan/_ewriter.py +2 -4
  105. myokit/formats/stan/_exporter.py +2 -5
  106. myokit/formats/stan/template/cell.stan +3 -3
  107. myokit/formats/sympy/__init__.py +0 -3
  108. myokit/formats/sympy/_ereader.py +1 -4
  109. myokit/formats/sympy/_ewriter.py +2 -5
  110. myokit/formats/wcp/__init__.py +0 -3
  111. myokit/formats/wcp/_wcp.py +2 -8
  112. myokit/formats/xml/__init__.py +0 -3
  113. myokit/formats/xml/_exporter.py +0 -3
  114. myokit/formats/xml/_split.py +0 -3
  115. myokit/gui/__init__.py +80 -246
  116. myokit/gui/datablock_viewer.py +103 -86
  117. myokit/gui/datalog_viewer.py +214 -66
  118. myokit/gui/explorer.py +15 -21
  119. myokit/gui/ide.py +171 -144
  120. myokit/gui/progress.py +9 -9
  121. myokit/gui/source.py +406 -375
  122. myokit/gui/vargrapher.py +2 -12
  123. myokit/lib/deps.py +12 -13
  124. myokit/lib/guess.py +3 -4
  125. myokit/lib/hh.py +20 -18
  126. myokit/lib/markov.py +21 -20
  127. myokit/lib/multi.py +1 -3
  128. myokit/lib/plots.py +20 -9
  129. myokit/pacing.py +0 -3
  130. myokit/pype.py +7 -18
  131. myokit/tests/__init__.py +3 -6
  132. myokit/tests/ansic_event_based_pacing.py +1 -4
  133. myokit/tests/ansic_fixed_form_pacing.py +3 -6
  134. myokit/tests/data/beeler-1977-model-compare-b.mmt +2 -2
  135. myokit/tests/data/clancy-1999-fitting.mmt +1 -0
  136. myokit/tests/test_aux.py +13 -28
  137. myokit/tests/test_cellml_v1_api.py +4 -19
  138. myokit/tests/test_cellml_v1_parser.py +0 -15
  139. myokit/tests/test_cellml_v1_writer.py +0 -9
  140. myokit/tests/test_cellml_v2_api.py +4 -19
  141. myokit/tests/test_cellml_v2_parser.py +0 -15
  142. myokit/tests/test_cellml_v2_writer.py +0 -9
  143. myokit/tests/test_cmodel.py +16 -22
  144. myokit/tests/test_compiler_detection.py +1 -11
  145. myokit/tests/test_component.py +108 -56
  146. myokit/tests/test_config.py +34 -67
  147. myokit/tests/test_datablock.py +1 -9
  148. myokit/tests/test_datalog.py +19 -24
  149. myokit/tests/test_dependency_checking.py +8 -23
  150. myokit/tests/test_expressions.py +0 -9
  151. myokit/tests/test_float.py +1 -5
  152. myokit/tests/test_formats.py +0 -9
  153. myokit/tests/test_formats_axon.py +1 -9
  154. myokit/tests/test_formats_cellml.py +0 -15
  155. myokit/tests/test_formats_channelml.py +0 -15
  156. myokit/tests/test_formats_easyml.py +0 -14
  157. myokit/tests/test_formats_exporters.py +1 -16
  158. myokit/tests/test_formats_expression_writers.py +1 -17
  159. myokit/tests/test_formats_html.py +0 -3
  160. myokit/tests/test_formats_importers.py +1 -16
  161. myokit/tests/test_formats_mathml_content.py +0 -9
  162. myokit/tests/test_formats_mathml_presentation.py +0 -9
  163. myokit/tests/test_formats_opencl.py +0 -10
  164. myokit/tests/test_formats_sbml.py +0 -15
  165. myokit/tests/test_formats_sympy.py +0 -9
  166. myokit/tests/test_formats_wcp.py +1 -3
  167. myokit/tests/test_io.py +27 -27
  168. myokit/tests/test_jacobian_calculator.py +6 -14
  169. myokit/tests/test_jacobian_tracer.py +0 -9
  170. myokit/tests/test_lib_deps.py +0 -9
  171. myokit/tests/test_lib_guess.py +0 -9
  172. myokit/tests/test_lib_hh.py +18 -12
  173. myokit/tests/test_lib_markov.py +21 -13
  174. myokit/tests/test_lib_multi.py +0 -9
  175. myokit/tests/test_lib_plots.py +13 -8
  176. myokit/tests/test_meta.py +0 -3
  177. myokit/tests/test_model.py +390 -96
  178. myokit/tests/test_model_building.py +44 -96
  179. myokit/tests/test_opencl_info.py +5 -14
  180. myokit/tests/test_pacing_factory.py +0 -3
  181. myokit/tests/test_pacing_system_c.py +1 -23
  182. myokit/tests/test_pacing_system_py.py +0 -9
  183. myokit/tests/test_parsing.py +139 -56
  184. myokit/tests/test_progress_reporters.py +0 -3
  185. myokit/tests/test_protocol.py +0 -9
  186. myokit/tests/test_protocol_floating_point.py +1 -10
  187. myokit/tests/test_protocol_time_series.py +82 -0
  188. myokit/tests/test_pype.py +0 -9
  189. myokit/tests/test_quantity.py +0 -9
  190. myokit/tests/test_rhs_benchmarker.py +1 -9
  191. myokit/tests/test_sbml_api.py +27 -42
  192. myokit/tests/test_sbml_parser.py +4 -19
  193. myokit/tests/test_simulation_1d.py +45 -25
  194. myokit/tests/test_simulation_cvodes.py +321 -55
  195. myokit/tests/test_simulation_cvodes_from_disk.py +0 -3
  196. myokit/tests/test_simulation_fiber_tissue.py +39 -12
  197. myokit/tests/test_simulation_log_interval.py +1 -431
  198. myokit/tests/test_simulation_opencl.py +69 -48
  199. myokit/tests/test_simulation_opencl_log_interval.py +1 -3
  200. myokit/tests/test_simulation_opencl_vs_cvode.py +1 -10
  201. myokit/tests/test_simulation_opencl_vs_sim1d.py +1 -10
  202. myokit/tests/test_system_info.py +1 -11
  203. myokit/tests/test_tools.py +0 -9
  204. myokit/tests/test_unit.py +1 -10
  205. myokit/tests/test_user_functions.py +0 -10
  206. myokit/tests/test_variable.py +231 -27
  207. myokit/tools.py +5 -21
  208. myokit/units.py +5 -3
  209. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/METADATA +12 -15
  210. myokit-1.35.0.dist-info/RECORD +391 -0
  211. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/WHEEL +1 -1
  212. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/entry_points.txt +0 -1
  213. myokit/_exec_new.py +0 -15
  214. myokit/_exec_old.py +0 -15
  215. myokit/_sim/cvodesim.c +0 -1551
  216. myokit/_sim/cvodesim.py +0 -674
  217. myokit/_sim/icsim.cpp +0 -563
  218. myokit/_sim/icsim.py +0 -363
  219. myokit/_sim/psim.cpp +0 -656
  220. myokit/_sim/psim.py +0 -493
  221. myokit/lib/common.py +0 -1094
  222. myokit/tests/test_lib_common.py +0 -130
  223. myokit/tests/test_simulation_cvode.py +0 -612
  224. myokit/tests/test_simulation_ic.py +0 -108
  225. myokit/tests/test_simulation_p.py +0 -223
  226. myokit-1.33.9.dist-info/RECORD +0 -403
  227. /myokit/formats/opencl/template/{test → test.sh} +0 -0
  228. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/LICENSE.txt +0 -0
  229. {myokit-1.33.9.dist-info → myokit-1.35.0.dist-info}/top_level.txt +0 -0
@@ -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
- from __future__ import absolute_import, division
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
- # ConfigParser in Python 2 and 3
28
+ # SciPy: Only used to load matlab files.
36
29
  try:
37
- import ConfigParser as configparser
30
+ import scipy.io
31
+ has_scipy = True
38
32
  except ImportError:
39
- import configparser
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(DataLogViewer, self).__init__()
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 = QtWidgets.QDesktopWidget().availableGeometry().center()
113
+ cp = QtGui.QGuiApplication.primaryScreen().availableGeometry().center()
118
114
  qr.moveCenter(cp)
119
115
  self.move(qr.topLeft())
120
- # Add widget for Abf file tabs
121
- self._tabs = QtWidgets.QTabWidget()
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
- if self._tabs.count() > tab_count:
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 = QtWidgets.QAction('&Open', self)
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 = QtWidgets.QAction('&Quit', self)
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 = QtWidgets.QAction('&About', self)
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 = QtWidgets.QAction('&License', self)
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(Qt.ToolButtonTextBesideIcon)
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 = actions[ext.lower()]
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
- from scipy.io import loadmat
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(DataLogViewer, self).show()
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
- class AbfTab(QtWidgets.QTabWidget):
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(AbfTab, self).__init__(parent)
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(AbfTab, self).deleteLater()
640
+ super().deleteLater()
515
641
 
516
642
 
517
- class AtfTab(QtWidgets.QTabWidget):
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(AtfTab, self).__init__(parent)
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(AtfTab, self).deleteLater()
711
+ super().deleteLater()
586
712
 
587
713
 
588
- class CsvTab(QtWidgets.QTabWidget):
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(CsvTab, self).__init__(parent)
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
- # Add tab for each column
623
- for k, v in log.items():
624
- if k == time:
748
+ # Overlapping sweeps or neighboring cells?
749
+ keys = []
750
+ groups = {}
751
+ for key in log.keys():
752
+ if key == time:
625
753
  continue
626
- self.addTab(self.create_graph_tab(k, v), k)
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, data):
769
+ def create_graph_tab(self, key, indices=None):
629
770
  """
630
- Creates a widget displaying the ``data`` stored under ``key``.
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
- axes.plot(self._time, data)
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(CsvTab, self).deleteLater()
810
+ super().deleteLater()
663
811
 
664
812
 
665
- class MatTab(QtWidgets.QTabWidget):
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(MatTab, self).__init__(parent)
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(MatTab, self).deleteLater()
898
+ super().deleteLater()
751
899
 
752
900
 
753
- class TxtTab(QtWidgets.QTabWidget):
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(TxtTab, self).__init__(parent)
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(TxtTab, self).deleteLater()
980
+ super().deleteLater()
833
981
 
834
982
 
835
- class WcpTab(QtWidgets.QTabWidget):
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(WcpTab, self).__init__(parent)
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(WcpTab, self).deleteLater()
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(Explorer, self).__init__(parent)
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(QtWidgets.QBoxLayout.LeftToRight)
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(QtWidgets.QBoxLayout.LeftToRight)
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(QtWidgets.QBoxLayout.TopToBottom)
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: