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
myokit/gui/ide.py CHANGED
@@ -5,10 +5,7 @@
5
5
  # This file is part of Myokit.
6
6
  # See http://myokit.org for copyright, sharing, and licensing details.
7
7
  #
8
- from __future__ import absolute_import, division
9
- from __future__ import print_function, unicode_literals
10
-
11
- # Standard library imports
8
+ import configparser
12
9
  import gc
13
10
  import os
14
11
  import sys
@@ -16,17 +13,14 @@ import textwrap
16
13
  import traceback
17
14
  import warnings
18
15
 
19
- # Myokit
20
16
  import myokit
21
17
  import myokit.formats
18
+ import myokit.gui
22
19
  import myokit.lib.deps
23
20
  import myokit.lib.guess
24
21
 
25
- # Qt imports
26
22
  from myokit.gui import QtWidgets, QtGui, QtCore, Qt
27
23
 
28
- # GUI components
29
- import myokit.gui
30
24
  from . import source
31
25
  from . import explorer
32
26
  from . import progress
@@ -37,12 +31,6 @@ from . import vargrapher
37
31
  import matplotlib
38
32
  matplotlib.interactive(True) # Allows plt.show()
39
33
 
40
- # ConfigParser in Python 2 and 3
41
- try:
42
- import ConfigParser as configparser
43
- except ImportError:
44
- import configparser
45
-
46
34
 
47
35
  # Application title
48
36
  TITLE = 'Myokit IDE'
@@ -110,7 +98,11 @@ class MyokitIDE(myokit.gui.MyokitApplication):
110
98
  New GUI for editing ``.mmt`` files.
111
99
  """
112
100
  def __init__(self, filename=None):
113
- super(MyokitIDE, self).__init__()
101
+ super().__init__()
102
+
103
+ # Regular expression for navigator
104
+ self._nav_query = QtCore.QRegularExpression(
105
+ r'^\[[a-zA-Z]{1}[a-zA-Z0-9_]*\]')
114
106
 
115
107
  # Set application icon
116
108
  self.setWindowIcon(icon())
@@ -119,7 +111,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
119
111
  self.resize(950, 720)
120
112
  self.setMinimumSize(600, 440)
121
113
  qr = self.frameGeometry()
122
- cp = QtWidgets.QDesktopWidget().availableGeometry().center()
114
+ cp = QtGui.QGuiApplication.primaryScreen().availableGeometry().center()
123
115
  qr.moveCenter(cp)
124
116
  self.move(qr.topLeft())
125
117
 
@@ -184,7 +176,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
184
176
  self._script_tools.add(self._script_search, 'Find/Replace')
185
177
 
186
178
  # Create editor tabs
187
- self._model_tab = QtWidgets.QSplitter(Qt.Horizontal)
179
+ self._model_tab = QtWidgets.QSplitter(Qt.Orientation.Horizontal)
188
180
  self._model_tab.editor = self._model_editor
189
181
  self._model_tab.search = self._model_search
190
182
  self._model_tab.addWidget(self._model_editor)
@@ -193,7 +185,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
193
185
  self._model_tab.setCollapsible(0, False)
194
186
  self._model_tab.setCollapsible(1, False)
195
187
 
196
- self._protocol_tab = QtWidgets.QSplitter(Qt.Horizontal)
188
+ self._protocol_tab = QtWidgets.QSplitter(Qt.Orientation.Horizontal)
197
189
  self._protocol_tab.editor = self._protocol_editor
198
190
  self._protocol_tab.search = self._protocol_search
199
191
  self._protocol_tab.addWidget(self._protocol_editor)
@@ -202,7 +194,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
202
194
  self._protocol_tab.setCollapsible(0, False)
203
195
  self._protocol_tab.setCollapsible(1, False)
204
196
 
205
- self._script_tab = QtWidgets.QSplitter(Qt.Horizontal)
197
+ self._script_tab = QtWidgets.QSplitter(Qt.Orientation.Horizontal)
206
198
  self._script_tab.editor = self._script_editor
207
199
  self._script_tab.search = self._script_search
208
200
  self._script_tab.addWidget(self._script_editor)
@@ -245,7 +237,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
245
237
  self._console.write('Loading Myokit IDE')
246
238
 
247
239
  # Create central layout: vertical splitter
248
- self._central_splitter = QtWidgets.QSplitter(Qt.Vertical)
240
+ self._central_splitter = QtWidgets.QSplitter(Qt.Orientation.Vertical)
249
241
  self._central_splitter.addWidget(self._editor_tabs)
250
242
  self._central_splitter.addWidget(self._console)
251
243
  self._central_splitter.setSizes([580, 120])
@@ -271,8 +263,9 @@ class MyokitIDE(myokit.gui.MyokitApplication):
271
263
  self._valid_model = None
272
264
  self._valid_protocol = None
273
265
 
274
- # Last-found model error
266
+ # Last-found model and protocol error
275
267
  self._last_model_error = None
268
+ self._last_protocol_error = None
276
269
 
277
270
  # React to changes to model and protocol
278
271
  # (For example devalidate model and protocol upon any changes)
@@ -380,10 +373,10 @@ class MyokitIDE(myokit.gui.MyokitApplication):
380
373
  try:
381
374
  # Ask are you sure?
382
375
  msg = 'Remove all units from expressions in model?'
383
- box = QtWidgets.QMessageBox
384
- options = box.Yes | box.No
385
- reply = box.question(self, TITLE, msg, options)
386
- if reply == box.No:
376
+ sb = QtWidgets.QMessageBox.StandardButton
377
+ reply = QtWidgets.QMessageBox.question(
378
+ self, TITLE, msg, sb.Yes | sb.No)
379
+ if reply == sb.No:
387
380
  return
388
381
  # Strip units
389
382
  # Note: lines are used in error handling!
@@ -465,7 +458,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
465
458
  # Simulation creation method
466
459
  def sim():
467
460
  QtWidgets.QApplication.processEvents(
468
- QtCore.QEventLoop.ExcludeUserInputEvents)
461
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
469
462
  try:
470
463
  # Get model and protocol
471
464
  m = self.model(errors_in_console=True)
@@ -474,12 +467,12 @@ class MyokitIDE(myokit.gui.MyokitApplication):
474
467
  elif m is None:
475
468
  return 'Empty model definition'
476
469
  QtWidgets.QApplication.processEvents(
477
- QtCore.QEventLoop.ExcludeUserInputEvents)
470
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
478
471
  p = self.protocol(errors_in_console=True)
479
472
  if p is False:
480
473
  return 'Errors in protocol'
481
474
  QtWidgets.QApplication.processEvents(
482
- QtCore.QEventLoop.ExcludeUserInputEvents)
475
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
483
476
  # Create and return simulation
484
477
  self.statusBar().showMessage('Creating simulation...')
485
478
  return m, p, myokit.Simulation(m, p)
@@ -815,18 +808,36 @@ class MyokitIDE(myokit.gui.MyokitApplication):
815
808
  Jump to the last error in the model tab.
816
809
  """
817
810
  try:
818
- # Check for error
819
- self.model(console=True)
820
- if self._last_model_error is None:
821
- return
822
- # Switch to model tab if required
823
- self._editor_tabs.setCurrentWidget(self._model_tab)
824
- # Show error
825
- line = self._last_model_error.line
826
- char = self._last_model_error.char
827
- self.statusBar().showMessage(
828
- 'Jumping to (' + str(line) + ',' + str(char) + ').')
829
- self._model_editor.jump_to(line - 1, char)
811
+ t = self._editor_tabs.currentWidget()
812
+ if t is self._model_tab:
813
+
814
+ # Check for error
815
+ self.model(console=True)
816
+ if self._last_model_error is None:
817
+ return
818
+
819
+ # Show error
820
+ line = self._last_model_error.line
821
+ char = self._last_model_error.char
822
+ self.statusBar().showMessage(
823
+ 'Jumping to (' + str(line) + ',' + str(char) + ').')
824
+ self._model_editor.jump_to(line - 1, char)
825
+
826
+ elif t is self._protocol_tab:
827
+
828
+ # Check for error
829
+ self.protocol(console=True)
830
+ if self._last_protocol_error is None:
831
+ return
832
+
833
+ # Show error
834
+ line = self._last_protocol_error.line
835
+ char = self._last_protocol_error.char
836
+ self.statusBar().showMessage(
837
+ 'Jumping to (' + str(line) + ',' + str(char) + ').')
838
+ self._protocol_editor.jump_to(line - 1, char)
839
+
840
+ # Can't be called on script tab -- or does nothing
830
841
  except Exception:
831
842
  self.show_exception()
832
843
 
@@ -987,12 +998,12 @@ class MyokitIDE(myokit.gui.MyokitApplication):
987
998
  #self.setEnabled(False)
988
999
  self._console.write('Running embedded script.')
989
1000
  QtWidgets.QApplication.setOverrideCursor(
990
- QtGui.QCursor(Qt.WaitCursor))
1001
+ QtGui.QCursor(Qt.CursorShape.WaitCursor))
991
1002
  # Create progress bar
992
1003
  pbar = progress.ProgressBar(self, 'Running embedded script')
993
1004
  pbar.show()
994
1005
  QtWidgets.QApplication.processEvents(
995
- QtCore.QEventLoop.ExcludeUserInputEvents)
1006
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
996
1007
  # Get model and protocol
997
1008
  m = self.model(errors_in_console=True)
998
1009
  if m is False:
@@ -1001,16 +1012,16 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1001
1012
  if p is False:
1002
1013
  return
1003
1014
  QtWidgets.QApplication.processEvents(
1004
- QtCore.QEventLoop.ExcludeUserInputEvents)
1015
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
1005
1016
  # Clone model & protocol: the script may modify them!
1006
1017
  if m:
1007
1018
  m = m.clone()
1008
1019
  QtWidgets.QApplication.processEvents(
1009
- QtCore.QEventLoop.ExcludeUserInputEvents)
1020
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
1010
1021
  if p:
1011
1022
  p = p.clone()
1012
1023
  QtWidgets.QApplication.processEvents(
1013
- QtCore.QEventLoop.ExcludeUserInputEvents)
1024
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
1014
1025
  # Get embedded script
1015
1026
  x = self._script_editor.get_text()
1016
1027
  # Run
@@ -1032,7 +1043,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1032
1043
  self._console.write(traceback.format_exc())
1033
1044
  finally:
1034
1045
  QtWidgets.QApplication.processEvents(
1035
- QtCore.QEventLoop.ExcludeUserInputEvents)
1046
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)
1036
1047
  # Hide progress bar
1037
1048
  if pbar is not None:
1038
1049
  pbar.close()
@@ -1143,11 +1154,15 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1143
1154
 
1144
1155
  def action_validate(self):
1145
1156
  """
1146
- Validates the model and, if the model is valid, the protocol.
1157
+ Validates the model or the protocol, depending on the editor tab.
1147
1158
  """
1148
1159
  try:
1149
- self.model(console=True)
1150
- self.protocol(console=True)
1160
+ current = self._editor_tabs.currentWidget()
1161
+ if current is self._model_tab:
1162
+ self.model(console=True)
1163
+ elif current is self._protocol_tab:
1164
+ self.protocol(console=True)
1165
+ # Can't be called on script tab -- or does nothing
1151
1166
  except Exception:
1152
1167
  self.show_exception()
1153
1168
 
@@ -1406,6 +1421,21 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1406
1421
  for widget in self._script_widgets:
1407
1422
  widget.setEnabled(index == 2)
1408
1423
 
1424
+ # Update "validate" and "jump to last error" tools
1425
+ if t is self._model_tab:
1426
+ self._tool_validate.setText('Validate model')
1427
+ self._tool_validate.setToolTip('Validate the model.')
1428
+ self._tool_validate.setEnabled(True)
1429
+ self._tool_jump_to_error.setEnabled(True)
1430
+ elif t is self._protocol_tab:
1431
+ self._tool_validate.setText('Validate protocol')
1432
+ self._tool_validate.setToolTip('Validate the protocol.')
1433
+ self._tool_validate.setEnabled(True)
1434
+ self._tool_jump_to_error.setEnabled(True)
1435
+ else:
1436
+ self._tool_validate.setEnabled(False)
1437
+ self._tool_jump_to_error.setEnabled(False)
1438
+
1409
1439
  def change_model(self):
1410
1440
  """ Qt slot: Called whenever the model is changed. """
1411
1441
  self._valid_model = None
@@ -1508,8 +1538,10 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1508
1538
  if event:
1509
1539
  event.ignore()
1510
1540
  return
1541
+
1511
1542
  # Close all windows, including matplotlib plots
1512
- QtWidgets.qApp.closeAllWindows()
1543
+ QtWidgets.QApplication.instance().closeAllWindows()
1544
+
1513
1545
  # Accept event, closing this window
1514
1546
  if event:
1515
1547
  event.accept()
@@ -1532,14 +1564,14 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1532
1564
  # File menu
1533
1565
  self._menu_file = self._menu.addMenu('&File')
1534
1566
  # File > New
1535
- self._tool_new = QtWidgets.QAction('&New', self)
1567
+ self._tool_new = QtGui.QAction('&New', self)
1536
1568
  self._tool_new.setShortcut('Ctrl+N')
1537
1569
  self._tool_new.setStatusTip('Create a new mmt file.')
1538
1570
  self._tool_new.setIcon(myokit.gui.icon('document-new'))
1539
1571
  self._tool_new.triggered.connect(self.action_new)
1540
1572
  self._menu_file.addAction(self._tool_new)
1541
1573
  # File > Open
1542
- self._tool_open = QtWidgets.QAction('&Open', self)
1574
+ self._tool_open = QtGui.QAction('&Open', self)
1543
1575
  self._tool_open.setShortcut('Ctrl+O')
1544
1576
  self._tool_open.setStatusTip('Open an existing mmt file.')
1545
1577
  self._tool_open.setIcon(myokit.gui.icon('document-open'))
@@ -1548,7 +1580,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1548
1580
  # File > ----
1549
1581
  self._menu_file.addSeparator()
1550
1582
  # File > Save
1551
- self._tool_save = QtWidgets.QAction('&Save', self)
1583
+ self._tool_save = QtGui.QAction('&Save', self)
1552
1584
  self._tool_save.setShortcut('Ctrl+S')
1553
1585
  self._tool_save.setStatusTip('Save the current file')
1554
1586
  self._tool_save.setIcon(myokit.gui.icon('document-save'))
@@ -1556,7 +1588,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1556
1588
  self._tool_save.setEnabled(False)
1557
1589
  self._menu_file.addAction(self._tool_save)
1558
1590
  # File > Save as
1559
- self._tool_save_as = QtWidgets.QAction('Save &as', self)
1591
+ self._tool_save_as = QtGui.QAction('Save &as', self)
1560
1592
  self._tool_save_as.setShortcut('Ctrl+Shift+S')
1561
1593
  self._tool_save_as.setStatusTip(
1562
1594
  'Save the current file under a different name.')
@@ -1567,14 +1599,14 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1567
1599
  # File > Recent files
1568
1600
  self._recent_file_tools = []
1569
1601
  for i in range(N_RECENT_FILES):
1570
- tool = QtWidgets.QAction(self, visible=False)
1602
+ tool = QtGui.QAction(self, visible=False)
1571
1603
  tool.triggered.connect(self.action_open_recent)
1572
1604
  self._recent_file_tools.append(tool)
1573
1605
  self._menu_file.addAction(tool)
1574
1606
  # File > ----
1575
1607
  self._menu_file.addSeparator()
1576
1608
  # File > Quit
1577
- self._tool_exit = QtWidgets.QAction('&Quit', self)
1609
+ self._tool_exit = QtGui.QAction('&Quit', self)
1578
1610
  self._tool_exit.setShortcut('Ctrl+Q')
1579
1611
  self._tool_exit.setStatusTip('Exit application.')
1580
1612
  self._tool_exit.triggered.connect(self.close)
@@ -1584,7 +1616,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1584
1616
  #
1585
1617
  self._menu_edit = self._menu.addMenu('&Edit')
1586
1618
  # Edit > Undo
1587
- self._tool_undo = QtWidgets.QAction('&Undo', self)
1619
+ self._tool_undo = QtGui.QAction('&Undo', self)
1588
1620
  self._tool_undo.setShortcut('Ctrl+Z')
1589
1621
  self._tool_undo.setStatusTip('Undo the last edit.')
1590
1622
  self._tool_undo.setIcon(myokit.gui.icon('edit-undo'))
@@ -1592,7 +1624,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1592
1624
  self._tool_undo.setEnabled(False)
1593
1625
  self._menu_edit.addAction(self._tool_undo)
1594
1626
  # Edit > Redo
1595
- self._tool_redo = QtWidgets.QAction('&Redo', self)
1627
+ self._tool_redo = QtGui.QAction('&Redo', self)
1596
1628
  self._tool_redo.setShortcut('Ctrl+Shift+Z')
1597
1629
  self._tool_redo.setStatusTip('Redo the last undone edit.')
1598
1630
  self._tool_redo.setIcon(myokit.gui.icon('edit-redo'))
@@ -1602,7 +1634,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1602
1634
  # Edit > ----
1603
1635
  self._menu_edit.addSeparator()
1604
1636
  # Edit > Cut
1605
- self._tool_cut = QtWidgets.QAction('&Cut', self)
1637
+ self._tool_cut = QtGui.QAction('&Cut', self)
1606
1638
  self._tool_cut.setShortcut('Ctrl+X')
1607
1639
  self._tool_cut.setStatusTip(
1608
1640
  'Cut the selected text and copy it to the clipboard.')
@@ -1611,7 +1643,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1611
1643
  self._tool_cut.setEnabled(False)
1612
1644
  self._menu_edit.addAction(self._tool_cut)
1613
1645
  # Edit > Copy
1614
- self._tool_copy = QtWidgets.QAction('&Copy', self)
1646
+ self._tool_copy = QtGui.QAction('&Copy', self)
1615
1647
  self._tool_copy.setShortcut('Ctrl+C')
1616
1648
  self._tool_copy.setStatusTip(
1617
1649
  'Copy the selected text to the clipboard.')
@@ -1620,7 +1652,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1620
1652
  self._tool_copy.setEnabled(False)
1621
1653
  self._menu_edit.addAction(self._tool_copy)
1622
1654
  # Edit > Paste
1623
- self._tool_paste = QtWidgets.QAction('&Paste', self)
1655
+ self._tool_paste = QtGui.QAction('&Paste', self)
1624
1656
  self._tool_paste.setShortcut('Ctrl+V')
1625
1657
  self._tool_paste.setStatusTip(
1626
1658
  'Paste text from the clipboard into the editor.')
@@ -1630,7 +1662,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1630
1662
  # Edit > ----
1631
1663
  self._menu_edit.addSeparator()
1632
1664
  # Edit > Find and replace
1633
- self._tool_find = QtWidgets.QAction('&Find and replace', self)
1665
+ self._tool_find = QtGui.QAction('&Find and replace', self)
1634
1666
  self._tool_find.setShortcut('Ctrl+F')
1635
1667
  self._tool_find.setStatusTip('Find and/or replace some text.')
1636
1668
  self._tool_find.setIcon(myokit.gui.icon('edit-find'))
@@ -1639,8 +1671,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1639
1671
  # Edit > ----
1640
1672
  self._menu_edit.addSeparator()
1641
1673
  # Edit > Format protocol
1642
- self._tool_format_protocol = QtWidgets.QAction(
1643
- 'Format protocol', self)
1674
+ self._tool_format_protocol = QtGui.QAction('Format protocol', self)
1644
1675
  self._tool_format_protocol.setStatusTip(
1645
1676
  'Standardise the formatting of the protocol section.')
1646
1677
  self._tool_format_protocol.triggered.connect(
@@ -1650,7 +1681,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1650
1681
  # Edit > ----
1651
1682
  self._menu_edit.addSeparator()
1652
1683
  # Edit > Comment or uncomment
1653
- self._tool_comment = QtWidgets.QAction(
1684
+ self._tool_comment = QtGui.QAction(
1654
1685
  '&Comment/uncomment selected lines', self)
1655
1686
  self._tool_comment.setShortcut('Ctrl+;')
1656
1687
  self._tool_comment.setStatusTip(
@@ -1658,7 +1689,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1658
1689
  self._tool_comment.triggered.connect(self.action_comment)
1659
1690
  self._menu_edit.addAction(self._tool_comment)
1660
1691
  # Edit > Remove units from expressions
1661
- self._tool_remove_units = QtWidgets.QAction(
1692
+ self._tool_remove_units = QtGui.QAction(
1662
1693
  'Remove units from &expressions', self)
1663
1694
  self._tool_remove_units.setStatusTip(
1664
1695
  'Remove all units inside expressions.')
@@ -1666,7 +1697,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1666
1697
  self._menu_edit.addAction(self._tool_remove_units)
1667
1698
  self._model_widgets.append(self._tool_remove_units)
1668
1699
  # Edit > Trim whitespace
1669
- self._tool_trim_whitespace = QtWidgets.QAction(
1700
+ self._tool_trim_whitespace = QtGui.QAction(
1670
1701
  'Trim trailing &whitespace', self)
1671
1702
  self._tool_trim_whitespace.setStatusTip(
1672
1703
  'Remove trailing whitespace from each line.')
@@ -1678,14 +1709,13 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1678
1709
  #
1679
1710
  self._menu_view = self._menu.addMenu('&View')
1680
1711
  # View > View model definition
1681
- self._tool_view_model = QtWidgets.QAction(
1682
- 'View &model definition', self)
1712
+ self._tool_view_model = QtGui.QAction('View &model definition', self)
1683
1713
  self._tool_view_model.setShortcut('Alt+1')
1684
1714
  self._tool_view_model.setStatusTip('View the model definition tab')
1685
1715
  self._tool_view_model.triggered.connect(self.action_view_model)
1686
1716
  self._menu_view.addAction(self._tool_view_model)
1687
1717
  # View > View protocol definition
1688
- self._tool_view_protocol = QtWidgets.QAction(
1718
+ self._tool_view_protocol = QtGui.QAction(
1689
1719
  'View &protocol definition', self)
1690
1720
  self._tool_view_protocol.setShortcut('Alt+2')
1691
1721
  self._tool_view_protocol.setStatusTip(
@@ -1693,8 +1723,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1693
1723
  self._tool_view_protocol.triggered.connect(self.action_view_protocol)
1694
1724
  self._menu_view.addAction(self._tool_view_protocol)
1695
1725
  # View > View embedded script
1696
- self._tool_view_script = QtWidgets.QAction(
1697
- 'View embedded &script', self)
1726
+ self._tool_view_script = QtGui.QAction('View embedded &script', self)
1698
1727
  self._tool_view_script.setShortcut('Alt+3')
1699
1728
  self._tool_view_script.setStatusTip('View the embedded script tab')
1700
1729
  self._tool_view_script.triggered.connect(self.action_view_script)
@@ -1702,7 +1731,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1702
1731
  # View > ----
1703
1732
  self._menu_view.addSeparator()
1704
1733
  # View > Show model components (navigator)
1705
- self._tool_view_navigator = QtWidgets.QAction(
1734
+ self._tool_view_navigator = QtGui.QAction(
1706
1735
  'Show model &components', self)
1707
1736
  self._tool_view_navigator.setCheckable(True)
1708
1737
  self._tool_view_navigator.setStatusTip(
@@ -1714,8 +1743,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1714
1743
  # View > ----
1715
1744
  self._menu_view.addSeparator()
1716
1745
  # View > Preview protocol
1717
- self._tool_preview_protocol = QtWidgets.QAction(
1718
- '&Preview protocol', self)
1746
+ self._tool_preview_protocol = QtGui.QAction('&Preview protocol', self)
1719
1747
  self._tool_preview_protocol.setShortcut('Ctrl+P')
1720
1748
  self._tool_preview_protocol.setStatusTip(
1721
1749
  'Show a preview of the current protocol.')
@@ -1729,7 +1757,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1729
1757
  self._menu_convert = self._menu.addMenu('&Convert')
1730
1758
 
1731
1759
  # Convert > Import CellML
1732
- self._tool_import_cellml = QtWidgets.QAction(
1760
+ self._tool_import_cellml = QtGui.QAction(
1733
1761
  'Import model from CellML', self)
1734
1762
  self._tool_import_cellml.setStatusTip(
1735
1763
  'Import a model definition from a CellML file.')
@@ -1737,7 +1765,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1737
1765
  lambda: self.action_import_model('cellml', FILTER_CELLML))
1738
1766
  self._menu_convert.addAction(self._tool_import_cellml)
1739
1767
  # Convert > Export CellML 1
1740
- self._tool_export_cellml1 = QtWidgets.QAction(
1768
+ self._tool_export_cellml1 = QtGui.QAction(
1741
1769
  'Export model to CellML 1.0', self)
1742
1770
  self._tool_export_cellml1.setStatusTip(
1743
1771
  'Export a model definition to a CellML 1.0 document.')
@@ -1746,7 +1774,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1746
1774
  'cellml1', '.cellml', FILTER_CELLML))
1747
1775
  self._menu_convert.addAction(self._tool_export_cellml1)
1748
1776
  # Convert > Export CellML 2
1749
- self._tool_export_cellml2 = QtWidgets.QAction(
1777
+ self._tool_export_cellml2 = QtGui.QAction(
1750
1778
  'Export model to CellML 2.0', self)
1751
1779
  self._tool_export_cellml2.setStatusTip(
1752
1780
  'Export a model definition to a CellML 2.0 document.')
@@ -1758,15 +1786,14 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1758
1786
  # Convert > ----
1759
1787
  self._menu_convert.addSeparator()
1760
1788
  # Convert > Import ABF
1761
- self._tool_import_abf = QtWidgets.QAction(
1762
- 'Import protocol from ABF', self)
1789
+ self._tool_import_abf = QtGui.QAction('Import protocol from ABF', self)
1763
1790
  self._tool_import_abf.setStatusTip(
1764
1791
  'Import a protocol definition from an ABF file.')
1765
1792
  self._tool_import_abf.triggered.connect(
1766
1793
  self.action_import_abf_protocol)
1767
1794
  self._menu_convert.addAction(self._tool_import_abf)
1768
1795
  # Convert > Import ChannelML
1769
- self._tool_import_channelml = QtWidgets.QAction(
1796
+ self._tool_import_channelml = QtGui.QAction(
1770
1797
  'Import model from ChannelML', self)
1771
1798
  self._tool_import_channelml.setStatusTip(
1772
1799
  'Import a channel model from ChannelML.')
@@ -1774,8 +1801,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1774
1801
  lambda: self.action_import_model('channelml', FILTER_CHANNELML))
1775
1802
  self._menu_convert.addAction(self._tool_import_channelml)
1776
1803
  # Convert > Import SBML
1777
- self._tool_import_sbml = QtWidgets.QAction(
1778
- 'Import model from SBML', self)
1804
+ self._tool_import_sbml = QtGui.QAction('Import model from SBML', self)
1779
1805
  self._tool_import_sbml.setStatusTip(
1780
1806
  'Import a model from SBML.')
1781
1807
  self._tool_import_sbml.triggered.connect(
@@ -1785,8 +1811,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1785
1811
  # Convert > ----
1786
1812
  self._menu_convert.addSeparator()
1787
1813
  # Convert > Export HTML
1788
- self._tool_export_html = QtWidgets.QAction(
1789
- 'Export model to HTML', self)
1814
+ self._tool_export_html = QtGui.QAction('Export model to HTML', self)
1790
1815
  self._tool_export_html.setStatusTip(
1791
1816
  'Export a model definition to an HTML document using presentation'
1792
1817
  ' MathML.')
@@ -1794,8 +1819,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1794
1819
  lambda: self.action_export_model('html', '.html', FILTER_HTML))
1795
1820
  self._menu_convert.addAction(self._tool_export_html)
1796
1821
  # Convert > Export Latex
1797
- self._tool_export_latex = QtWidgets.QAction(
1798
- 'Export model to Latex', self)
1822
+ self._tool_export_latex = QtGui.QAction('Export model to Latex', self)
1799
1823
  self._tool_export_latex.setStatusTip(
1800
1824
  'Export a model definition to a Latex document.')
1801
1825
  self._tool_export_latex.triggered.connect(
@@ -1807,7 +1831,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1807
1831
  self._menu_convert.addSeparator()
1808
1832
 
1809
1833
  # Convert > Ansic
1810
- self._tool_export_ansic = QtWidgets.QAction('Export to Ansi C', self)
1834
+ self._tool_export_ansic = QtGui.QAction('Export to Ansi C', self)
1811
1835
  self._tool_export_ansic.setStatusTip(
1812
1836
  'Export to a runnable Ansi C program.')
1813
1837
  self._tool_export_ansic.triggered.connect(
@@ -1815,8 +1839,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1815
1839
  self._menu_convert.addAction(self._tool_export_ansic)
1816
1840
 
1817
1841
  # Convert > CUDA
1818
- self._tool_export_cuda = QtWidgets.QAction(
1819
- 'Export to CUDA kernel', self)
1842
+ self._tool_export_cuda = QtGui.QAction('Export to CUDA kernel', self)
1820
1843
  self._tool_export_cuda.setStatusTip(
1821
1844
  'Export a model definition to a CUDA kernel program.')
1822
1845
  self._tool_export_cuda.triggered.connect(
@@ -1824,7 +1847,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1824
1847
  self._menu_convert.addAction(self._tool_export_cuda)
1825
1848
 
1826
1849
  # Convert > CUDA RL
1827
- self._tool_export_cuda_rl = QtWidgets.QAction(
1850
+ self._tool_export_cuda_rl = QtGui.QAction(
1828
1851
  'Export to CUDA kernel with RL updates', self)
1829
1852
  self._tool_export_cuda_rl.setStatusTip(
1830
1853
  'Export a model definition to a CUDA kernel program using'
@@ -1834,7 +1857,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1834
1857
  self._menu_convert.addAction(self._tool_export_cuda_rl)
1835
1858
 
1836
1859
  # Convert > EasyML
1837
- self._tool_export_easyml = QtWidgets.QAction(
1860
+ self._tool_export_easyml = QtGui.QAction(
1838
1861
  'Export to EasyML (Carp)', self)
1839
1862
  self._tool_export_easyml.setStatusTip(
1840
1863
  'Export to an EasyML script for use with Carp/Carpentry.')
@@ -1843,7 +1866,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1843
1866
  self._menu_convert.addAction(self._tool_export_easyml)
1844
1867
 
1845
1868
  # Convert > Matlab
1846
- self._tool_export_matlab = QtWidgets.QAction(
1869
+ self._tool_export_matlab = QtGui.QAction(
1847
1870
  'Export to Matlab/Octave', self)
1848
1871
  self._tool_export_matlab.setStatusTip(
1849
1872
  'Export to a runnable Matlab/Octave script.')
@@ -1852,7 +1875,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1852
1875
  self._menu_convert.addAction(self._tool_export_matlab)
1853
1876
 
1854
1877
  # Convert > OpenCL
1855
- self._tool_export_opencl = QtWidgets.QAction(
1878
+ self._tool_export_opencl = QtGui.QAction(
1856
1879
  'Export to OpenCL kernel', self)
1857
1880
  self._tool_export_opencl.setStatusTip(
1858
1881
  'Export a model definition to an OpenCL kernel program using')
@@ -1861,7 +1884,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1861
1884
  self._menu_convert.addAction(self._tool_export_opencl)
1862
1885
 
1863
1886
  # Convert > OpenCL RL
1864
- self._tool_export_opencl_rl = QtWidgets.QAction(
1887
+ self._tool_export_opencl_rl = QtGui.QAction(
1865
1888
  'Export to OpenCL kernel with RL updates', self)
1866
1889
  self._tool_export_opencl_rl.setStatusTip(
1867
1890
  'Export a model definition to an OpenCL kernel program using'
@@ -1871,7 +1894,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1871
1894
  self._menu_convert.addAction(self._tool_export_opencl_rl)
1872
1895
 
1873
1896
  # Convert > Python
1874
- self._tool_export_python = QtWidgets.QAction('Export to Python', self)
1897
+ self._tool_export_python = QtGui.QAction('Export to Python', self)
1875
1898
  self._tool_export_python.setStatusTip(
1876
1899
  'Export a model definition to a runnable Python script.')
1877
1900
  self._tool_export_python.triggered.connect(
@@ -1883,7 +1906,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1883
1906
  #
1884
1907
  self._menu_analysis = self._menu.addMenu('&Analysis')
1885
1908
  # Analysis > Model statistics
1886
- self._tool_stats = QtWidgets.QAction('Show model statistics', self)
1909
+ self._tool_stats = QtGui.QAction('Show model statistics', self)
1887
1910
  self._tool_stats.setStatusTip(
1888
1911
  'Displays some basic statistics about the current model.')
1889
1912
  self._tool_stats.triggered.connect(self.action_model_stats)
@@ -1891,8 +1914,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1891
1914
  # Analysis > ----
1892
1915
  self._menu_analysis.addSeparator()
1893
1916
  # Analysis > Check units strict
1894
- self._tool_units_strict = QtWidgets.QAction(
1895
- 'Check units (&strict)', self)
1917
+ self._tool_units_strict = QtGui.QAction('Check units (&strict)', self)
1896
1918
  self._tool_units_strict.setShortcut('F9')
1897
1919
  self._tool_units_strict.setStatusTip(
1898
1920
  'Check this model\'s units in strict mode.')
@@ -1900,7 +1922,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1900
1922
  self.action_check_units_strict)
1901
1923
  self._menu_analysis.addAction(self._tool_units_strict)
1902
1924
  # Analysis > Check units tolerant
1903
- self._tool_units_tolerant = QtWidgets.QAction(
1925
+ self._tool_units_tolerant = QtGui.QAction(
1904
1926
  'Check units (&tolerant)', self)
1905
1927
  self._tool_units_tolerant.setShortcut('F10')
1906
1928
  self._tool_units_tolerant.setStatusTip(
@@ -1911,7 +1933,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1911
1933
  # Analysis > ----
1912
1934
  self._menu_analysis.addSeparator()
1913
1935
  # Analysis > Show variable info
1914
- self._tool_variable_info = QtWidgets.QAction(
1936
+ self._tool_variable_info = QtGui.QAction(
1915
1937
  'Show quick variable info', self)
1916
1938
  self._tool_variable_info.setShortcut('Ctrl+R')
1917
1939
  self._tool_variable_info.setStatusTip(
@@ -1920,7 +1942,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1920
1942
  self._menu_analysis.addAction(self._tool_variable_info)
1921
1943
  self._model_widgets.append(self._tool_variable_info)
1922
1944
  # Analysis > Show variable evaluation
1923
- self._tool_variable_evaluation = QtWidgets.QAction(
1945
+ self._tool_variable_evaluation = QtGui.QAction(
1924
1946
  'Show variable evaluation', self)
1925
1947
  self._tool_variable_evaluation.setShortcut('Ctrl+E')
1926
1948
  self._tool_variable_evaluation.setStatusTip(
@@ -1930,7 +1952,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1930
1952
  self._menu_analysis.addAction(self._tool_variable_evaluation)
1931
1953
  self._model_widgets.append(self._tool_variable_evaluation)
1932
1954
  # Analysis > Show variable dependencies
1933
- self._tool_variable_dependencies = QtWidgets.QAction(
1955
+ self._tool_variable_dependencies = QtGui.QAction(
1934
1956
  'Show variable dependencies', self)
1935
1957
  self._tool_variable_dependencies.setShortcut('Ctrl+D')
1936
1958
  self._tool_variable_dependencies.setStatusTip(
@@ -1940,7 +1962,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1940
1962
  self._menu_analysis.addAction(self._tool_variable_dependencies)
1941
1963
  self._model_widgets.append(self._tool_variable_dependencies)
1942
1964
  # Analysis > Show variable users
1943
- self._tool_variable_users = QtWidgets.QAction(
1965
+ self._tool_variable_users = QtGui.QAction(
1944
1966
  'Show variable users', self)
1945
1967
  self._tool_variable_users.setShortcut('Ctrl+U')
1946
1968
  self._tool_variable_users.setStatusTip(
@@ -1950,7 +1972,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1950
1972
  self._menu_analysis.addAction(self._tool_variable_users)
1951
1973
  self._model_widgets.append(self._tool_variable_users)
1952
1974
  # Analysis > Graph variable
1953
- self._tool_variable_graph = QtWidgets.QAction(
1975
+ self._tool_variable_graph = QtGui.QAction(
1954
1976
  'Graph selected variable', self)
1955
1977
  self._tool_variable_graph.setShortcut('Ctrl+G')
1956
1978
  self._tool_variable_graph.setStatusTip(
@@ -1959,7 +1981,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1959
1981
  self._menu_analysis.addAction(self._tool_variable_graph)
1960
1982
  self._model_widgets.append(self._tool_variable_graph)
1961
1983
  # Analysis > Jump to variable definition
1962
- self._tool_variable_jump = QtWidgets.QAction(
1984
+ self._tool_variable_jump = QtGui.QAction(
1963
1985
  'Jump to variable definition', self)
1964
1986
  self._tool_variable_jump.setShortcut('Ctrl+J')
1965
1987
  self._tool_variable_jump.setStatusTip(
@@ -1972,7 +1994,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1972
1994
  # Analysis > ----
1973
1995
  self._menu_analysis.addSeparator()
1974
1996
  # Analysis > Evaluate state derivatives
1975
- self._tool_state_derivatives = QtWidgets.QAction(
1997
+ self._tool_state_derivatives = QtGui.QAction(
1976
1998
  'Evaluate state derivatives', self)
1977
1999
  self._tool_state_derivatives.setShortcut('F7')
1978
2000
  self._tool_state_derivatives.setStatusTip(
@@ -1981,7 +2003,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1981
2003
  self.action_state_derivatives)
1982
2004
  self._menu_analysis.addAction(self._tool_state_derivatives)
1983
2005
  # Analysis > Evaluate state derivatives without error checking
1984
- self._tool_state_derivatives2 = QtWidgets.QAction(
2006
+ self._tool_state_derivatives2 = QtGui.QAction(
1985
2007
  'Evaluate state derivatives (no error checking)', self)
1986
2008
  self._tool_state_derivatives2.setShortcut('F8')
1987
2009
  self._tool_state_derivatives2.setStatusTip(
@@ -1993,7 +2015,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
1993
2015
  # Analysis > ----
1994
2016
  self._menu_analysis.addSeparator()
1995
2017
  # Analysis > Show component dependency graph
1996
- self._tool_component_dependency_graph = QtWidgets.QAction(
2018
+ self._tool_component_dependency_graph = QtGui.QAction(
1997
2019
  'Show component dependency graph', self)
1998
2020
  self._tool_component_dependency_graph.setStatusTip(
1999
2021
  'Display a graph of the dependencies between components.')
@@ -2001,7 +2023,7 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2001
2023
  self.action_component_dependency_graph)
2002
2024
  self._menu_analysis.addAction(self._tool_component_dependency_graph)
2003
2025
  # Analysis > Show variable dependency graph
2004
- self._tool_variable_dependency_graph = QtWidgets.QAction(
2026
+ self._tool_variable_dependency_graph = QtGui.QAction(
2005
2027
  'Show variable dependency graph', self)
2006
2028
  self._tool_variable_dependency_graph.setStatusTip(
2007
2029
  'Display a graph of the dependencies between variables.')
@@ -2009,14 +2031,14 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2009
2031
  self.action_variable_dependency_graph)
2010
2032
  self._menu_analysis.addAction(self._tool_variable_dependency_graph)
2011
2033
  # Analysis > Show state dependency matrix
2012
- self._tool_state_matrix = QtWidgets.QAction(
2034
+ self._tool_state_matrix = QtGui.QAction(
2013
2035
  'Show state dependency matrix', self)
2014
2036
  self._tool_state_matrix.setStatusTip(
2015
2037
  'Display a matrix graph of the dependencies between states.')
2016
2038
  self._tool_state_matrix.triggered.connect(self.action_state_matrix)
2017
2039
  self._menu_analysis.addAction(self._tool_state_matrix)
2018
2040
  # Analysis > Show component dependency cycles
2019
- self._tool_component_cycles = QtWidgets.QAction(
2041
+ self._tool_component_cycles = QtGui.QAction(
2020
2042
  'Show cyclical component dependencies', self)
2021
2043
  self._tool_component_cycles.setStatusTip(
2022
2044
  'Display a list of cyclical dependencies between components.')
@@ -2028,29 +2050,27 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2028
2050
  #
2029
2051
  self._menu_run = self._menu.addMenu('&Run')
2030
2052
  # Run > Validate
2031
- self._tool_validate = QtWidgets.QAction(
2032
- '&Validate model and protocol', self)
2053
+ self._tool_validate = QtGui.QAction('&Validate model', self)
2033
2054
  self._tool_validate.setShortcut('Ctrl+B')
2034
- self._tool_validate.setStatusTip('Validate the model and protocol')
2055
+ self._tool_validate.setStatusTip('Validate the model.')
2035
2056
  self._tool_validate.triggered.connect(self.action_validate)
2036
2057
  self._menu_run.addAction(self._tool_validate)
2037
2058
  # Run > Jump to error
2038
- self._tool_jump_to_error = QtWidgets.QAction(
2039
- '&Jump to last error', self)
2059
+ self._tool_jump_to_error = QtGui.QAction('&Jump to last error', self)
2040
2060
  self._tool_jump_to_error.setShortcut('Ctrl+Space')
2041
2061
  self._tool_jump_to_error.setStatusTip(
2042
2062
  'Jump to the last model error found.')
2043
2063
  self._tool_jump_to_error.triggered.connect(self.action_jump_to_error)
2044
2064
  self._menu_run.addAction(self._tool_jump_to_error)
2045
2065
  # Run > Run embedded script
2046
- self._tool_run = QtWidgets.QAction('&Run embedded script', self)
2066
+ self._tool_run = QtGui.QAction('&Run embedded script', self)
2047
2067
  self._tool_run.setShortcut('F5')
2048
2068
  self._tool_run.setStatusTip('Run the embedded script.')
2049
2069
  self._tool_run.setIcon(myokit.gui.icon('media-playback-start'))
2050
2070
  self._tool_run.triggered.connect(self.action_run)
2051
2071
  self._menu_run.addAction(self._tool_run)
2052
2072
  # Run > Run explorer
2053
- self._tool_explore = QtWidgets.QAction('&Run explorer', self)
2073
+ self._tool_explore = QtGui.QAction('&Run explorer', self)
2054
2074
  self._tool_explore.setShortcut('F6')
2055
2075
  self._tool_explore.setStatusTip(
2056
2076
  'Run a simulation and display the results in the explorer.')
@@ -2062,12 +2082,12 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2062
2082
  #
2063
2083
  self._menu_help = self._menu.addMenu('&Help')
2064
2084
  # Help > About
2065
- self._tool_about = QtWidgets.QAction('&About', self)
2085
+ self._tool_about = QtGui.QAction('&About', self)
2066
2086
  self._tool_about.setStatusTip('View information about this program.')
2067
2087
  self._tool_about.triggered.connect(self.action_about)
2068
2088
  self._menu_help.addAction(self._tool_about)
2069
2089
  # Help > License
2070
- self._tool_license = QtWidgets.QAction('&License', self)
2090
+ self._tool_license = QtGui.QAction('&License', self)
2071
2091
  self._tool_license.setStatusTip('View this program\'s license info.')
2072
2092
  self._tool_license.triggered.connect(self.action_license)
2073
2093
  self._menu_help.addAction(self._tool_license)
@@ -2238,7 +2258,8 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2238
2258
  # Check for cached valid model
2239
2259
  if self._valid_model is not None and not force:
2240
2260
  if console:
2241
- self._console.write('No changes to model since last build.')
2261
+ self._console.write(
2262
+ 'No changes to model since last build (no errors found).')
2242
2263
  return self._valid_model
2243
2264
 
2244
2265
  # Parse and validate
@@ -2321,15 +2342,15 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2321
2342
  msg = 'Save changes to ' + str(self._file) + '?'
2322
2343
  else:
2323
2344
  msg = 'Save changes to new file?'
2324
- box = QtWidgets.QMessageBox
2325
- options = box.Yes | box.No
2345
+ sb = QtWidgets.QMessageBox.StandardButton
2346
+ options = sb.Yes | sb.No
2326
2347
  if cancel:
2327
- options |= box.Cancel
2328
- reply = box.question(self, TITLE, msg, options)
2329
- if reply == box.Yes:
2348
+ options |= sb.Cancel
2349
+ reply = QtWidgets.QMessageBox.question(self, TITLE, msg, options)
2350
+ if reply == sb.Yes:
2330
2351
  # Only allow quitting if save succesful
2331
2352
  return self.save_file(save_as=False)
2332
- elif reply == box.No:
2353
+ elif reply == sb.No:
2333
2354
  return True
2334
2355
  else:
2335
2356
  return False
@@ -2352,12 +2373,17 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2352
2373
  # Check for cached valid protocol
2353
2374
  if self._valid_protocol and not force:
2354
2375
  if console:
2355
- self._console.write('No changes to protocol since last build.')
2376
+ self._console.write(
2377
+ 'No changes to protocol since last build (no errors'
2378
+ ' found).')
2356
2379
  return self._valid_protocol
2357
2380
 
2358
2381
  # Parse and validate
2359
2382
  protocol = None
2360
2383
 
2384
+ # Reset last protocol error
2385
+ self._last_protocol_error = None
2386
+
2361
2387
  # Check for empty protocol field
2362
2388
  lines = self._protocol_editor.get_text()
2363
2389
  if lines.strip() == '':
@@ -2378,7 +2404,10 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2378
2404
  return protocol
2379
2405
  except myokit.ParseError as e:
2380
2406
  if console or errors_in_console:
2407
+ # Write error to console
2381
2408
  self._console.write(myokit.format_parse_error(e, lines))
2409
+ # Store error
2410
+ self._last_protocol_error = e
2382
2411
  return False
2383
2412
 
2384
2413
  def save_config(self):
@@ -2543,16 +2572,13 @@ class MyokitIDE(myokit.gui.MyokitApplication):
2543
2572
  def update_navigator(self):
2544
2573
  """ Updates the model navigator contents. """
2545
2574
  # Find all components and store their positions in a list (name, line)
2546
- #Sloppy version: query = QtCore.QRegExp(r'^\[') could be faster?
2547
- query = QtCore.QRegExp(r'^\[[a-zA-Z]{1}[a-zA-Z0-9_]*\]')
2548
2575
  pos = 0
2549
- found = self._model_editor.document().find(query, pos)
2576
+ found = self._model_editor.document().find(self._nav_query, pos)
2550
2577
  positions = []
2551
2578
  while not found.isNull():
2552
2579
  pos = found.position()
2553
- block = found.block()
2554
- positions.append((block.text(), pos))
2555
- found = self._model_editor.document().find(query, pos)
2580
+ positions.append((found.selectedText(), pos))
2581
+ found = self._model_editor.document().find(self._nav_query, pos)
2556
2582
  self._model_navigator.set_positions(positions)
2557
2583
 
2558
2584
  def update_recent_files_menu(self):
@@ -2590,7 +2616,7 @@ class TabbedToolBar(QtWidgets.QTabWidget):
2590
2616
  tab_toggled = QtCore.Signal(QtWidgets.QWidget, bool)
2591
2617
 
2592
2618
  def __init__(self, parent=None):
2593
- super(TabbedToolBar, self).__init__(parent)
2619
+ super().__init__(parent)
2594
2620
 
2595
2621
  self.setTabsClosable(True)
2596
2622
  self.tabCloseRequested.connect(self._close_button_hit)
@@ -2615,11 +2641,11 @@ class TabbedToolBar(QtWidgets.QTabWidget):
2615
2641
  def keyPressEvent(self, event):
2616
2642
  """ Qt event: A key-press reaches the widget. """
2617
2643
  key = event.key()
2618
- if key == Qt.Key_Escape:
2644
+ if key == Qt.Key.Key_Escape:
2619
2645
  self.setFocus()
2620
2646
  self.focusPreviousChild()
2621
2647
  else:
2622
- super(TabbedToolBar, self).keyPressEvent(event)
2648
+ super().keyPressEvent(event)
2623
2649
 
2624
2650
  def toggle(self, widget, new_status=None):
2625
2651
  """
@@ -2673,7 +2699,7 @@ class ModelNavigator(QtWidgets.QWidget):
2673
2699
  item_changed = QtCore.Signal(int)
2674
2700
 
2675
2701
  def __init__(self, parent=None):
2676
- super(ModelNavigator, self).__init__(parent)
2702
+ super().__init__(parent)
2677
2703
 
2678
2704
  # List widget
2679
2705
  self._list_widget = QtWidgets.QListWidget()
@@ -2689,7 +2715,7 @@ class ModelNavigator(QtWidgets.QWidget):
2689
2715
  def current_item_changed(self, item, previous_item):
2690
2716
  """ Called if the navigator item is changed. """
2691
2717
  if item is not None:
2692
- line = item.data(Qt.UserRole)
2718
+ line = item.data(Qt.ItemDataRole.UserRole)
2693
2719
  self.item_changed.emit(line)
2694
2720
 
2695
2721
  def set_positions(self, positions):
@@ -2705,7 +2731,7 @@ class ModelNavigator(QtWidgets.QWidget):
2705
2731
  self._list_widget.setSortingEnabled(True)
2706
2732
  for text, pos in self._positions:
2707
2733
  item = QtWidgets.QListWidgetItem(text[1:-1])
2708
- item.setData(Qt.UserRole, pos)
2734
+ item.setData(Qt.ItemDataRole.UserRole, pos)
2709
2735
  self._list_widget.addItem(item)
2710
2736
 
2711
2737
 
@@ -2717,12 +2743,13 @@ class Console(QtWidgets.QPlainTextEdit):
2717
2743
  *Extends*: ``QtWidgets.QPlainTextEdit``
2718
2744
  """
2719
2745
  def __init__(self, parent=None):
2720
- super(Console, self).__init__(parent)
2746
+ super().__init__(parent)
2721
2747
  self.setReadOnly(True)
2722
2748
  font = myokit.gui.qtMonospaceFont()
2723
2749
  font.setPointSize(10)
2724
2750
  self.setFont(font)
2725
- self.setFrameStyle(QtWidgets.QFrame.WinPanel | QtWidgets.QFrame.Sunken)
2751
+ self.setFrameStyle(
2752
+ QtWidgets.QFrame.Shape.WinPanel | QtWidgets.QFrame.Shadow.Sunken)
2726
2753
 
2727
2754
  def clear(self):
2728
2755
  """
@@ -2764,4 +2791,4 @@ class Console(QtWidgets.QPlainTextEdit):
2764
2791
  self.verticalScrollBar().setValue(self.verticalScrollBar().maximum())
2765
2792
  # Autoflush
2766
2793
  QtWidgets.QApplication.processEvents(
2767
- QtCore.QEventLoop.ExcludeUserInputEvents)
2794
+ QtCore.QEventLoop.ProcessEventsFlag.ExcludeUserInputEvents)