PrEditor 1.2.0__tar.gz → 1.4.0__tar.gz

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.

Potentially problematic release.


This version of PrEditor might be problematic. Click here for more details.

Files changed (190) hide show
  1. {preditor-1.2.0 → preditor-1.4.0}/PKG-INFO +1 -1
  2. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/PKG-INFO +1 -1
  3. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/console.py +17 -0
  4. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/drag_tab_bar.py +4 -1
  5. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/group_tab_widget/__init__.py +42 -19
  6. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/group_tab_widget/grouped_tab_widget.py +26 -4
  7. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/group_tab_widget/one_tab_widget.py +42 -0
  8. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/logger_window_plugin.py +3 -0
  9. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/loggerwindow.py +43 -47
  10. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/loggerwindow.ui +11 -1
  11. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/workbox_mixin.py +102 -4
  12. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/workbox_text_edit.py +2 -2
  13. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/workboxwidget.py +9 -4
  14. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/documenteditor.py +64 -20
  15. {preditor-1.2.0 → preditor-1.4.0}/preditor/version.py +3 -3
  16. {preditor-1.2.0 → preditor-1.4.0}/.coveragerc +0 -0
  17. {preditor-1.2.0 → preditor-1.4.0}/.github/ISSUE_TEMPLATE/BUG_REPORT.md +0 -0
  18. {preditor-1.2.0 → preditor-1.4.0}/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +0 -0
  19. {preditor-1.2.0 → preditor-1.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  20. {preditor-1.2.0 → preditor-1.4.0}/.github/workflows/release.yml +0 -0
  21. {preditor-1.2.0 → preditor-1.4.0}/.github/workflows/static-analysis-and-test.yml +0 -0
  22. {preditor-1.2.0 → preditor-1.4.0}/.gitignore +0 -0
  23. {preditor-1.2.0 → preditor-1.4.0}/.pre-commit-config.yaml +0 -0
  24. {preditor-1.2.0 → preditor-1.4.0}/CODE_OF_CONDUCT.md +0 -0
  25. {preditor-1.2.0 → preditor-1.4.0}/CONTRIBUTING.md +0 -0
  26. {preditor-1.2.0 → preditor-1.4.0}/LICENSE +0 -0
  27. {preditor-1.2.0 → preditor-1.4.0}/MANIFEST.in +0 -0
  28. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/SOURCES.txt +0 -0
  29. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/dependency_links.txt +0 -0
  30. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/entry_points.txt +0 -0
  31. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/requires.txt +0 -0
  32. {preditor-1.2.0 → preditor-1.4.0}/PrEditor.egg-info/top_level.txt +0 -0
  33. {preditor-1.2.0 → preditor-1.4.0}/README.md +0 -0
  34. {preditor-1.2.0 → preditor-1.4.0}/examples/add_to_app.py +0 -0
  35. {preditor-1.2.0 → preditor-1.4.0}/examples/output_capture_and_show.py +0 -0
  36. {preditor-1.2.0 → preditor-1.4.0}/preditor/__init__.py +0 -0
  37. {preditor-1.2.0 → preditor-1.4.0}/preditor/__main__.py +0 -0
  38. {preditor-1.2.0 → preditor-1.4.0}/preditor/about_module.py +0 -0
  39. {preditor-1.2.0 → preditor-1.4.0}/preditor/cli.py +0 -0
  40. {preditor-1.2.0 → preditor-1.4.0}/preditor/config.py +0 -0
  41. {preditor-1.2.0 → preditor-1.4.0}/preditor/contexts.py +0 -0
  42. {preditor-1.2.0 → preditor-1.4.0}/preditor/cores/__init__.py +0 -0
  43. {preditor-1.2.0 → preditor-1.4.0}/preditor/cores/core.py +0 -0
  44. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/.hab.json +0 -0
  45. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/maya/PrEditor_maya.mod +0 -0
  46. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/maya/README.md +0 -0
  47. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/maya/plug-ins/PrEditor_maya.py +0 -0
  48. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/studiomax/PackageContents.xml +0 -0
  49. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +0 -0
  50. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/studiomax/README.md +0 -0
  51. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/studiomax/preditor.ms +0 -0
  52. {preditor-1.2.0 → preditor-1.4.0}/preditor/dccs/studiomax/preditor_menu.mnx +0 -0
  53. {preditor-1.2.0 → preditor-1.4.0}/preditor/debug.py +0 -0
  54. {preditor-1.2.0 → preditor-1.4.0}/preditor/delayable_engine/__init__.py +0 -0
  55. {preditor-1.2.0 → preditor-1.4.0}/preditor/delayable_engine/delayables.py +0 -0
  56. {preditor-1.2.0 → preditor-1.4.0}/preditor/enum.py +0 -0
  57. {preditor-1.2.0 → preditor-1.4.0}/preditor/excepthooks.py +0 -0
  58. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/__init__.py +0 -0
  59. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/app.py +0 -0
  60. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/codehighlighter.py +0 -0
  61. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/completer.py +0 -0
  62. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/dialog.py +0 -0
  63. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/editor_chooser.py +0 -0
  64. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/errordialog.py +0 -0
  65. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/find_files.py +0 -0
  66. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/fuzzy_search/__init__.py +0 -0
  67. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/fuzzy_search/fuzzy_search.py +0 -0
  68. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/group_tab_widget/grouped_tab_menu.py +0 -0
  69. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/group_tab_widget/grouped_tab_models.py +0 -0
  70. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/level_buttons.py +0 -0
  71. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/logger_window_handler.py +0 -0
  72. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/newtabwidget.py +0 -0
  73. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/set_text_editor_path_dialog.py +0 -0
  74. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/status_label.py +0 -0
  75. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/suggest_path_quotes_dialog.py +0 -0
  76. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/editor_chooser.ui +0 -0
  77. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/errordialog.ui +0 -0
  78. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/find_files.ui +0 -0
  79. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/set_text_editor_path_dialog.ui +0 -0
  80. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/ui/suggest_path_quotes_dialog.ui +0 -0
  81. {preditor-1.2.0 → preditor-1.4.0}/preditor/gui/window.py +0 -0
  82. {preditor-1.2.0 → preditor-1.4.0}/preditor/logging_config.py +0 -0
  83. {preditor-1.2.0 → preditor-1.4.0}/preditor/osystem.py +0 -0
  84. {preditor-1.2.0 → preditor-1.4.0}/preditor/plugins.py +0 -0
  85. {preditor-1.2.0 → preditor-1.4.0}/preditor/prefs.py +0 -0
  86. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/environment_variables.html +0 -0
  87. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/error_mail.html +0 -0
  88. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/error_mail_inline.html +0 -0
  89. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/README.md +0 -0
  90. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/arrow_forward.png +0 -0
  91. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/check-bold.png +0 -0
  92. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/chevron-down.png +0 -0
  93. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/chevron-up.png +0 -0
  94. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/close-thick.png +0 -0
  95. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/comment-edit.png +0 -0
  96. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/content-copy.png +0 -0
  97. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/content-cut.png +0 -0
  98. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/content-duplicate.png +0 -0
  99. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/content-paste.png +0 -0
  100. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/content-save.png +0 -0
  101. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/debug_disabled.png +0 -0
  102. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/eye-check.png +0 -0
  103. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/file-plus.png +0 -0
  104. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/file-remove.png +0 -0
  105. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/format-align-left.png +0 -0
  106. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/format-letter-case-lower.png +0 -0
  107. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/format-letter-case-upper.png +0 -0
  108. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/format-letter-case.svg +0 -0
  109. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/information.png +0 -0
  110. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_critical.png +0 -0
  111. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_custom.png +0 -0
  112. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_debug.png +0 -0
  113. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_error.png +0 -0
  114. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_info.png +0 -0
  115. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_not_set.png +0 -0
  116. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/logging_warning.png +0 -0
  117. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/marker.png +0 -0
  118. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/play.png +0 -0
  119. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/playlist-play.png +0 -0
  120. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/plus-minus-variant.png +0 -0
  121. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/preditor.ico +0 -0
  122. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/preditor.png +0 -0
  123. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/preditor.psd +0 -0
  124. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/preditor.svg +0 -0
  125. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/regex.svg +0 -0
  126. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/restart.svg +0 -0
  127. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/skip-forward-outline.png +0 -0
  128. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/skip-next-outline.png +0 -0
  129. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/skip-next.png +0 -0
  130. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/skip-previous.png +0 -0
  131. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/subdirectory-arrow-right.png +0 -0
  132. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/text-search-variant.png +0 -0
  133. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/img/warning-big.png +0 -0
  134. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/lang/python.json +0 -0
  135. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/settings.ini +0 -0
  136. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/stylesheet/Bright.css +0 -0
  137. {preditor-1.2.0 → preditor-1.4.0}/preditor/resource/stylesheet/Dark.css +0 -0
  138. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/__init__.py +0 -0
  139. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/delayables/__init__.py +0 -0
  140. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/delayables/smart_highlight.py +0 -0
  141. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/delayables/spell_check.py +0 -0
  142. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/finddialog.py +0 -0
  143. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/__init__.py +0 -0
  144. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/bash.ini +0 -0
  145. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/batch.ini +0 -0
  146. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/cpp.ini +0 -0
  147. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/css.ini +0 -0
  148. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/eyeonscript.ini +0 -0
  149. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/html.ini +0 -0
  150. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/javascript.ini +0 -0
  151. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/lua.ini +0 -0
  152. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/maxscript.ini +0 -0
  153. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/mel.ini +0 -0
  154. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/mu.ini +0 -0
  155. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/nsi.ini +0 -0
  156. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/perl.ini +0 -0
  157. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/puppet.ini +0 -0
  158. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/python.ini +0 -0
  159. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/ruby.ini +0 -0
  160. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/sql.ini +0 -0
  161. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/xml.ini +0 -0
  162. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/config/yaml.ini +0 -0
  163. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lang/language.py +0 -0
  164. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/__init__.py +0 -0
  165. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/cpplexer.py +0 -0
  166. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/javascriptlexer.py +0 -0
  167. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/maxscriptlexer.py +0 -0
  168. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/mellexer.py +0 -0
  169. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/mulexer.py +0 -0
  170. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/lexers/pythonlexer.py +0 -0
  171. {preditor-1.2.0 → preditor-1.4.0}/preditor/scintilla/ui/finddialog.ui +0 -0
  172. {preditor-1.2.0 → preditor-1.4.0}/preditor/settings.py +0 -0
  173. {preditor-1.2.0 → preditor-1.4.0}/preditor/stream/__init__.py +0 -0
  174. {preditor-1.2.0 → preditor-1.4.0}/preditor/stream/director.py +0 -0
  175. {preditor-1.2.0 → preditor-1.4.0}/preditor/stream/manager.py +0 -0
  176. {preditor-1.2.0 → preditor-1.4.0}/preditor/streamhandler_helper.py +0 -0
  177. {preditor-1.2.0 → preditor-1.4.0}/preditor/utils/__init__.py +0 -0
  178. {preditor-1.2.0 → preditor-1.4.0}/preditor/utils/cute.py +0 -0
  179. {preditor-1.2.0 → preditor-1.4.0}/preditor/utils/stylesheets.py +0 -0
  180. {preditor-1.2.0 → preditor-1.4.0}/preditor/utils/text_search.py +0 -0
  181. {preditor-1.2.0 → preditor-1.4.0}/preditor/weakref.py +0 -0
  182. {preditor-1.2.0 → preditor-1.4.0}/pyproject.toml +0 -0
  183. {preditor-1.2.0 → preditor-1.4.0}/requirements-cli.txt +0 -0
  184. {preditor-1.2.0 → preditor-1.4.0}/requirements-dev.txt +0 -0
  185. {preditor-1.2.0 → preditor-1.4.0}/requirements-qsci5.txt +0 -0
  186. {preditor-1.2.0 → preditor-1.4.0}/requirements-qsci6.txt +0 -0
  187. {preditor-1.2.0 → preditor-1.4.0}/requirements-shortcut.txt +0 -0
  188. {preditor-1.2.0 → preditor-1.4.0}/requirements.txt +0 -0
  189. {preditor-1.2.0 → preditor-1.4.0}/setup.cfg +0 -0
  190. {preditor-1.2.0 → preditor-1.4.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PrEditor
3
- Version: 1.2.0
3
+ Version: 1.4.0
4
4
  Summary: A python REPL and Editor and console based on Qt.
5
5
  Author-email: Blur Studio <opensource@blur.com>
6
6
  License: LGPL-3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PrEditor
3
- Version: 1.2.0
3
+ Version: 1.4.0
4
4
  Summary: A python REPL and Editor and console based on Qt.
5
5
  Author-email: Blur Studio <opensource@blur.com>
6
6
  License: LGPL-3.0
@@ -63,6 +63,8 @@ class ConsolePrEdit(QTextEdit):
63
63
 
64
64
  self._firstShow = True
65
65
 
66
+ self.addSepNewline = False
67
+
66
68
  # When executing code, that takes longer than this seconds, flash the window
67
69
  self.flash_time = 1.0
68
70
  self.flash_window = None
@@ -817,6 +819,10 @@ class ConsolePrEdit(QTextEdit):
817
819
  hasattr(window, 'uiErrorHyperlinksACT')
818
820
  and window.uiErrorHyperlinksACT.isChecked()
819
821
  )
822
+ sepPreditorTrace = (
823
+ hasattr(window, 'uiSeparateTracebackACT')
824
+ and window.uiSeparateTracebackACT.isChecked()
825
+ )
820
826
  self.moveCursor(QTextCursor.MoveOperation.End)
821
827
 
822
828
  charFormat = QTextCharFormat()
@@ -855,6 +861,17 @@ class ConsolePrEdit(QTextEdit):
855
861
  filename = info.get("filename", "") if info else ""
856
862
  isConsolePrEdit = '<ConsolePrEdit>' in filename
857
863
 
864
+ # To make it easier to see relevant lines of a traceback, optionally insert
865
+ # a newline separating internal PrEditor code from the code run by user.
866
+ if self.addSepNewline:
867
+ if sepPreditorTrace:
868
+ msg += "\n"
869
+ self.addSepNewline = False
870
+
871
+ preditorCalls = ("cmdresult = e", "exec(compiled,")
872
+ if msg.strip().startswith(preditorCalls):
873
+ self.addSepNewline = True
874
+
858
875
  if info and doHyperlink and not isConsolePrEdit:
859
876
  fileStart = info.get("fileStart")
860
877
  fileEnd = info.get("fileEnd")
@@ -139,8 +139,11 @@ class DragTabBar(QTabBar):
139
139
  """Used by the tab_menu to rename the tab at index `_context_menu_tab`."""
140
140
  if self._context_menu_tab != -1:
141
141
  current = self.tabText(self._context_menu_tab)
142
- msg = 'Rename the {} tab to:'.format(current)
142
+ msg = 'Rename the {} tab to (new name must be unique):'.format(current)
143
+
143
144
  name, success = QInputDialog.getText(self, 'Rename Tab', msg, text=current)
145
+ name = self.parent().get_next_available_tab_name(name)
146
+
144
147
  if success:
145
148
  self.setTabText(self._context_menu_tab, name)
146
149
 
@@ -1,7 +1,6 @@
1
1
  from __future__ import absolute_import
2
2
 
3
3
  import os
4
- import re
5
4
 
6
5
  from Qt.QtCore import Qt
7
6
  from Qt.QtGui import QIcon
@@ -46,6 +45,9 @@ class GroupTabWidget(OneTabWidget):
46
45
  self.editor_cls = WorkboxTextEdit
47
46
  self.core_name = core_name
48
47
  self.setStyleSheet(DEFAULT_STYLE_SHEET)
48
+
49
+ self.default_title = 'Group01'
50
+
49
51
  corner = QWidget(self)
50
52
  lyt = QHBoxLayout(corner)
51
53
  lyt.setSpacing(0)
@@ -69,7 +71,7 @@ class GroupTabWidget(OneTabWidget):
69
71
  self.uiCornerBTN = corner
70
72
  self.setCornerWidget(self.uiCornerBTN, Qt.Corner.TopRightCorner)
71
73
 
72
- def add_new_tab(self, group, title="Workbox", group_fmt=None):
74
+ def add_new_tab(self, group, title=None, prefs=None):
73
75
  """Adds a new tab to the requested group, creating the group if the group
74
76
  doesn't exist.
75
77
 
@@ -80,9 +82,6 @@ class GroupTabWidget(OneTabWidget):
80
82
  If True is passed, then the current group tab is used.
81
83
  title (str, optional): The name to give the newly created tab inside
82
84
  the group.
83
- group_fmt(str, optional): If None is passed to group, this string is
84
- used to search for existing tabs to calculate the last number
85
- and generate the new group tab name.
86
85
 
87
86
  Returns:
88
87
  GroupedTabWidget: The tab group for this group.
@@ -90,21 +89,14 @@ class GroupTabWidget(OneTabWidget):
90
89
  """
91
90
  parent = None
92
91
  if not group:
93
- if group_fmt is None:
94
- group_fmt = r'Group {}'
95
- last = 0
96
- for i in range(self.count()):
97
- match = re.match(group_fmt.format(r'(\d+)'), self.tabText(i))
98
- if match:
99
- last = max(last, int(match.group(1)))
100
- group = group_fmt.format(last + 1)
92
+ group = self.get_next_available_tab_name(self.default_title)
101
93
  elif group is True:
102
94
  group = self.currentIndex()
103
95
  if isinstance(group, int):
104
96
  group_title = self.tabText(group)
105
97
  parent = self.widget(group)
106
98
  elif isinstance(group, str):
107
- group_title = group
99
+ group_title = group.replace(" ", "_")
108
100
  index = self.index_for_text(group)
109
101
  if index != -1:
110
102
  parent = self.widget(index)
@@ -146,7 +138,7 @@ class GroupTabWidget(OneTabWidget):
146
138
  self,
147
139
  'Close all editors under this tab?',
148
140
  'Are you sure you want to close all tabs under the "{}" tab?'.format(
149
- self.tabText(self.currentIndex())
141
+ self.tabText(index)
150
142
  ),
151
143
  QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel,
152
144
  )
@@ -154,11 +146,11 @@ class GroupTabWidget(OneTabWidget):
154
146
  # Clean up all temp files created by this group's editors if they
155
147
  # are not using actual saved files.
156
148
  tab_widget = self.widget(self.currentIndex())
157
- for index in range(tab_widget.count()):
158
- editor = tab_widget.widget(index)
149
+ for editor_index in range(tab_widget.count()):
150
+ editor = tab_widget.widget(editor_index)
159
151
  editor.__remove_tempfile__()
160
152
 
161
- super(GroupTabWidget, self).close_tab(self.currentIndex())
153
+ super(GroupTabWidget, self).close_tab(index)
162
154
 
163
155
  def current_groups_widget(self):
164
156
  """Returns the current widget of the currently selected group or None."""
@@ -166,7 +158,8 @@ class GroupTabWidget(OneTabWidget):
166
158
  if editor_tab:
167
159
  return editor_tab.currentWidget()
168
160
 
169
- def default_tab(self, title='Group 1'):
161
+ def default_tab(self, title=None, prefs=None):
162
+ title = title or self.default_title
170
163
  widget = GroupedTabWidget(
171
164
  parent=self,
172
165
  editor_kwargs=self.editor_kwargs,
@@ -223,6 +216,8 @@ class GroupTabWidget(OneTabWidget):
223
216
  group_name = group['name']
224
217
  tab_widget = None
225
218
 
219
+ group_name = self.get_next_available_tab_name(group_name)
220
+
226
221
  for tab in group.get('tabs', []):
227
222
  # Only add this tab if, there is data on disk to load. The user can
228
223
  # open multiple instances of PrEditor using the same prefs. The
@@ -323,3 +318,31 @@ class GroupTabWidget(OneTabWidget):
323
318
  tab_widget = self.currentWidget()
324
319
  tab_widget.setCurrentIndex(editor)
325
320
  return tab_widget.currentWidget()
321
+
322
+ def set_current_groups_from_workbox(self, workbox):
323
+ """Make the specified workbox the current widget. If the workbox is not
324
+ found, the current widget is not changed.
325
+
326
+ Args:
327
+ workbox (WorkboxMixin): The workbox to make current.
328
+
329
+ Returns:
330
+ success (bool): Whether the workbox was found and made the current
331
+ widget
332
+ """
333
+ workbox_infos = self.all_widgets()
334
+ found_info = None
335
+ for workbox_info in workbox_infos:
336
+ if workbox_info[0] == workbox:
337
+ found_info = workbox_info
338
+ break
339
+ if found_info:
340
+ workbox = workbox_info[0]
341
+ group_idx = workbox_info[-2]
342
+ editor_idx = workbox_info[-1]
343
+
344
+ self.setCurrentIndex(group_idx)
345
+ tab_widget = self.currentWidget()
346
+ tab_widget.setCurrentIndex(editor_idx)
347
+
348
+ return bool(found_info)
@@ -27,7 +27,12 @@ class GroupedTabWidget(OneTabWidget):
27
27
  self.uiCornerBTN.released.connect(lambda: self.add_new_editor())
28
28
  self.setCornerWidget(self.uiCornerBTN, Qt.Corner.TopRightCorner)
29
29
 
30
- def add_new_editor(self, title="Workbox"):
30
+ self.default_title = "Workbox01"
31
+
32
+ def add_new_editor(self, title=None, prefs=None):
33
+ title = title or self.default_title
34
+
35
+ title = self.get_next_available_tab_name(title)
31
36
  editor, title = self.default_tab(title)
32
37
  index = self.addTab(editor, title)
33
38
  self.setCurrentIndex(index)
@@ -45,11 +50,18 @@ class GroupedTabWidget(OneTabWidget):
45
50
  self, 'Tab can not be closed.', msg, QMessageBox.StandardButton.Ok
46
51
  )
47
52
  return
53
+
54
+ workbox = self.widget(index)
55
+ name = workbox.__workbox_name__()
56
+ msg = (
57
+ f"Would you like to donate the contents of tab\n{name}\nto the "
58
+ "/dev/null fund for wayward code?"
59
+ )
60
+
48
61
  ret = QMessageBox.question(
49
62
  self,
50
63
  'Donate to the cause?',
51
- "Would you like to donate this tabs contents to the /dev/null fund "
52
- "for wayward code?",
64
+ msg,
53
65
  QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.Cancel,
54
66
  )
55
67
  if ret == QMessageBox.StandardButton.Yes:
@@ -59,7 +71,8 @@ class GroupedTabWidget(OneTabWidget):
59
71
 
60
72
  super(GroupedTabWidget, self).close_tab(index)
61
73
 
62
- def default_tab(self, title='Workbox'):
74
+ def default_tab(self, title=None, prefs=None):
75
+ title = title or self.default_title
63
76
  kwargs = self.editor_kwargs if self.editor_kwargs else {}
64
77
  editor = self.editor_cls(parent=self, core_name=self.core_name, **kwargs)
65
78
  return editor, title
@@ -76,5 +89,14 @@ class GroupedTabWidget(OneTabWidget):
76
89
  if hasattr(self.window(), "setWorkboxFontBasedOnConsole"):
77
90
  self.window().setWorkboxFontBasedOnConsole()
78
91
 
92
+ def tab_widget(self):
93
+ """Return the tab widget which contains this group tab
94
+
95
+ Returns:
96
+ self._tab_widget (GroupTabWidget): The tab widget which contains
97
+ this workbox
98
+ """
99
+ return self.parent().parent()
100
+
79
101
  def update_closable_tabs(self):
80
102
  self.setTabsClosable(self.count() != 1)
@@ -1,7 +1,11 @@
1
1
  from __future__ import absolute_import
2
2
 
3
+ import re
4
+
3
5
  from Qt.QtWidgets import QTabWidget
4
6
 
7
+ TAB_ITERATION_PATTERN = re.compile(r"(\d+)(?!.*\d)")
8
+
5
9
 
6
10
  class OneTabWidget(QTabWidget):
7
11
  """A QTabWidget that shows the close button only if there is more than one
@@ -17,6 +21,44 @@ class OneTabWidget(QTabWidget):
17
21
  super(OneTabWidget, self).__init__(*args, **kwargs)
18
22
  self.tabCloseRequested.connect(self.close_tab)
19
23
 
24
+ def get_next_available_tab_name(self, name):
25
+ """Get the next available tab name, incrementing an iteration if needed.
26
+
27
+ Args:
28
+ name (str): The desired name
29
+
30
+ Returns:
31
+ name (str): The name, or updated name if needed
32
+ """
33
+ name = name.replace(" ", "_")
34
+
35
+ existing_names = [self.tabText(i) for i in range(self.count())]
36
+
37
+ # Use regex to find the last set of digits. If found, the base name is
38
+ # a slice of name minus the digits string. Otherwise, the base name is
39
+ # the full name and iteration is zero.
40
+ match = TAB_ITERATION_PATTERN.search(name)
41
+ if match:
42
+ # We found trailing digits, so slice to get base name, and convert
43
+ # iteration to int
44
+ iter_str = match.group()
45
+ base = name[: -len(iter_str)]
46
+ iteration = int(iter_str)
47
+ else:
48
+ # No trailing digits found, so base name is full name and iteration
49
+ # is zero.
50
+ base = name
51
+ iteration = 0
52
+
53
+ if name in existing_names:
54
+ for _ in range(99):
55
+ iteration += 1
56
+ new_iter_str = str(iteration).zfill(2)
57
+ name = base + new_iter_str
58
+ if name not in existing_names:
59
+ break
60
+ return name
61
+
20
62
  def addTab(self, *args, **kwargs): # noqa: N802
21
63
  ret = super(OneTabWidget, self).addTab(*args, **kwargs)
22
64
  self.update_closable_tabs()
@@ -13,6 +13,9 @@ class LoggerWindowPlugin:
13
13
  def __init__(self, parent):
14
14
  self.parent = parent
15
15
 
16
+ def updateWindowTitle(self, title):
17
+ return title
18
+
16
19
  def record_prefs(self, name):
17
20
  """Returns any prefs to save with the PrEditor's preferences.
18
21
 
@@ -48,6 +48,7 @@ from .completer import CompleterMode
48
48
  from .level_buttons import LoggingLevelButton
49
49
  from .set_text_editor_path_dialog import SetTextEditorPathDialog
50
50
  from .status_label import StatusLabel
51
+ from .workbox_mixin import WorkboxName
51
52
 
52
53
  logger = logging.getLogger(__name__)
53
54
 
@@ -59,31 +60,6 @@ class WorkboxPages:
59
60
  Workboxes = 1
60
61
 
61
62
 
62
- class WorkboxName(str):
63
- """The joined name of a workbox `group/workbox` with access to its parts.
64
-
65
- This subclass provides properties for the group and workbox values separately.
66
- """
67
-
68
- def __new__(cls, group, workbox):
69
- txt = "/".join((group, workbox))
70
- ret = super().__new__(cls, txt)
71
- # Preserve the imitable nature of str's by using properties without setters.
72
- ret._group = group
73
- ret._workbox = workbox
74
- return ret
75
-
76
- @property
77
- def group(self):
78
- """The tab name of the group tab that contains the workbox."""
79
- return self._group
80
-
81
- @property
82
- def workbox(self):
83
- """The workbox of the tab for this workbox inside of the group."""
84
- return self._workbox
85
-
86
-
87
63
  class LoggerWindow(Window):
88
64
  _instance = None
89
65
  styleSheetChanged = Signal(str)
@@ -93,9 +69,7 @@ class LoggerWindow(Window):
93
69
  self.name = name if name else get_core_name()
94
70
  self._stylesheet = 'Bright'
95
71
 
96
- # Create timer to autohide status messages
97
- self.statusTimer = QTimer()
98
- self.statusTimer.setSingleShot(True)
72
+ self.setupStatusTimer()
99
73
 
100
74
  # Store the previous time a font-resize wheel event was triggered to prevent
101
75
  # rapid-fire WheelEvents. Initialize to the current time.
@@ -292,10 +266,9 @@ class LoggerWindow(Window):
292
266
 
293
267
  self.dont_ask_again = []
294
268
 
295
- # Load any plugins that modify the LoggerWindow
296
- self.plugins = {}
297
- for name, plugin in plugins.loggerwindow():
298
- self.plugins[name] = plugin(self)
269
+ # Load any plugins, and set window title
270
+ self.loadPlugins()
271
+ self.setWindowTitle(self.defineWindowTitle())
299
272
 
300
273
  self.restorePrefs()
301
274
 
@@ -308,17 +281,6 @@ class LoggerWindow(Window):
308
281
  action.triggered.connect(partial(self.setStyleSheet, style_name))
309
282
 
310
283
  self.uiConsoleTOOLBAR.show()
311
- loggerName = QApplication.instance().translate(
312
- 'PrEditorWindow', DEFAULT_CORE_NAME
313
- )
314
- self.setWindowTitle(
315
- '{} - {} - {} {}-bit'.format(
316
- loggerName,
317
- self.name,
318
- '{}.{}.{}'.format(*sys.version_info[:3]),
319
- osystem.getPointerSize(),
320
- )
321
- )
322
284
 
323
285
  self.setWorkboxFontBasedOnConsole()
324
286
  self.setEditorChooserFontBasedOnConsole()
@@ -359,6 +321,29 @@ class LoggerWindow(Window):
359
321
 
360
322
  self.update_workbox_stack()
361
323
 
324
+ def loadPlugins(self):
325
+ """Load any plugins that modify the LoggerWindow."""
326
+ self.plugins = {}
327
+ for name, plugin in plugins.loggerwindow():
328
+ if name not in self.plugins:
329
+ self.plugins[name] = plugin(self)
330
+
331
+ def defineWindowTitle(self):
332
+ """Define the window title, including and info plugins may add."""
333
+
334
+ # Define the title
335
+ loggerName = QApplication.instance().translate(
336
+ 'PrEditorWindow', DEFAULT_CORE_NAME
337
+ )
338
+ pyVersion = '{}.{}.{}'.format(*sys.version_info[:3])
339
+ size = osystem.getPointerSize()
340
+ title = f"{loggerName} - {self.name} - {pyVersion} {size}-bit"
341
+
342
+ # Add any info plugins may add to title
343
+ for _name, plugin in self.plugins.items():
344
+ title = plugin.updateWindowTitle(title)
345
+ return title
346
+
362
347
  def comment_toggle(self):
363
348
  self.current_workbox().__comment_toggle__()
364
349
 
@@ -815,6 +800,7 @@ class LoggerWindow(Window):
815
800
  'uiStatusLbl_limit': self.uiStatusLBL.limit(),
816
801
  'textEditorPath': self.textEditorPath,
817
802
  'textEditorCmdTempl': self.textEditorCmdTempl,
803
+ 'uiSeparateTracebackACT': self.uiSeparateTracebackACT.isChecked(),
818
804
  'currentStyleSheet': self._stylesheet,
819
805
  'flash_time': self.uiConsoleTXT.flash_time,
820
806
  'find_files_regex': self.uiFindInWorkboxesWGT.uiRegexBTN.isChecked(),
@@ -967,6 +953,8 @@ class LoggerWindow(Window):
967
953
  self.textEditorPath = pref.get('textEditorPath', defaultExePath)
968
954
  self.textEditorCmdTempl = pref.get('textEditorCmdTempl', defaultCmd)
969
955
 
956
+ self.uiSeparateTracebackACT.setChecked(pref.get('uiSeparateTracebackACT', True))
957
+
970
958
  self.uiWordWrapACT.setChecked(pref.get('wordWrap', True))
971
959
  self.setWordWrap(self.uiWordWrapACT.isChecked())
972
960
  self.uiClearBeforeRunningACT.setChecked(pref.get('clearBeforeRunning', False))
@@ -1041,16 +1029,24 @@ class LoggerWindow(Window):
1041
1029
  self.uiStatusLBL.setText(txt)
1042
1030
  self.uiMenuBar.adjustSize()
1043
1031
 
1032
+ def setupStatusTimer(self):
1033
+ # Create timer to autohide status messages
1034
+ self.statusTimer = QTimer()
1035
+ self.statusTimer.setSingleShot(True)
1036
+ self.statusTimer.setInterval(2000)
1037
+ self.statusTimer.timeout.connect(self.clearStatusText)
1038
+
1044
1039
  def clearStatusText(self):
1045
1040
  """Clear any displayed status text"""
1046
1041
  self.uiStatusLBL.clear()
1047
1042
  self.uiMenuBar.adjustSize()
1048
1043
 
1049
1044
  def autoHideStatusText(self):
1050
- """Set timer to automatically clear status text"""
1051
- if self.statusTimer.isActive():
1052
- self.statusTimer.stop()
1053
- self.statusTimer.singleShot(2000, self.clearStatusText)
1045
+ """Set timer to automatically clear status text.
1046
+
1047
+ If timer is already running, it will be automatically stopped first (We can't
1048
+ use static method QTimer.singleShot for this)
1049
+ """
1054
1050
  self.statusTimer.start()
1055
1051
 
1056
1052
  def setStyleSheet(self, stylesheet, recordPrefs=True):
@@ -92,7 +92,7 @@
92
92
  <x>0</x>
93
93
  <y>0</y>
94
94
  <width>796</width>
95
- <height>21</height>
95
+ <height>22</height>
96
96
  </rect>
97
97
  </property>
98
98
  <widget class="QMenu" name="uiDebugMENU">
@@ -193,6 +193,8 @@
193
193
  <addaction name="uiSetFlashWindowIntervalACT"/>
194
194
  <addaction name="separator"/>
195
195
  <addaction name="uiErrorHyperlinksACT"/>
196
+ <addaction name="uiSeparateTracebackACT"/>
197
+ <addaction name="separator"/>
196
198
  <addaction name="uiSetPreferredTextEditorPathACT"/>
197
199
  <addaction name="uiSetWorkboxEditorACT"/>
198
200
  </widget>
@@ -1003,6 +1005,14 @@ at the indicated line in the specified text editor.
1003
1005
  <string>Highlight Exact Completion</string>
1004
1006
  </property>
1005
1007
  </action>
1008
+ <action name="uiSeparateTracebackACT">
1009
+ <property name="checkable">
1010
+ <bool>true</bool>
1011
+ </property>
1012
+ <property name="text">
1013
+ <string>Visually Separate PrEditor Traceback</string>
1014
+ </property>
1015
+ </action>
1006
1016
  </widget>
1007
1017
  <customwidgets>
1008
1018
  <customwidget>
@@ -12,6 +12,44 @@ from Qt.QtWidgets import QStackedWidget
12
12
  from ..prefs import prefs_path
13
13
 
14
14
 
15
+ class WorkboxName(str):
16
+ """The joined name of a workbox `group/workbox` with access to its parts.
17
+
18
+ You may pass the group, workbox, or the fully formed workbox name:
19
+ examples:
20
+ workboxName = WorkboxName("Group01", "Workbox05")
21
+ workboxName = WorkboxName("Group01/Workbox05")
22
+ This subclass provides properties for the group and workbox values separately.
23
+ """
24
+
25
+ def __new__(cls, name, sub_name=None):
26
+ if sub_name is not None:
27
+ txt = "/".join((name, sub_name))
28
+ else:
29
+ txt = name
30
+ try:
31
+ name, sub_name = txt.split("/")
32
+ except ValueError:
33
+ msg = "A fully formed name, or a group and name, must be passed in."
34
+ raise ValueError(msg) from None
35
+
36
+ ret = super().__new__(cls, txt)
37
+ # Preserve the imitable nature of str's by using properties without setters.
38
+ ret._group = name
39
+ ret._workbox = sub_name
40
+ return ret
41
+
42
+ @property
43
+ def group(self):
44
+ """The tab name of the group tab that contains the workbox."""
45
+ return self._group
46
+
47
+ @property
48
+ def workbox(self):
49
+ """The workbox of the tab for this workbox inside of the group."""
50
+ return self._workbox
51
+
52
+
15
53
  class WorkboxMixin(object):
16
54
  _warning_text = None
17
55
  """When a user is picking this Workbox class, show a warning with this text."""
@@ -26,6 +64,16 @@ class WorkboxMixin(object):
26
64
  self._tempfile = tempfile
27
65
  self.core_name = core_name
28
66
 
67
+ self._tab_widget = parent
68
+
69
+ def __tab_widget__(self):
70
+ """Return the tab widget which contains this workbox
71
+
72
+ Returns:
73
+ GroupedTabWidget: The tab widget which contains this workbox
74
+ """
75
+ return self._tab_widget
76
+
29
77
  def __auto_complete_enabled__(self):
30
78
  raise NotImplementedError("Mixin method not overridden.")
31
79
 
@@ -84,8 +132,8 @@ class WorkboxMixin(object):
84
132
  txt = '\n' * line + txt
85
133
 
86
134
  # execute the code
87
- filename = self.__workbox_filename__(selection=True)
88
- ret, was_eval = self.__console__().executeString(txt, filename=filename)
135
+ title = self.__workbox_trace_title__(selection=True)
136
+ ret, was_eval = self.__console__().executeString(txt, filename=title)
89
137
  if was_eval:
90
138
  # If the selected code was a statement print the result of the statement.
91
139
  ret = repr(ret)
@@ -145,13 +193,63 @@ class WorkboxMixin(object):
145
193
 
146
194
  return group, editor
147
195
 
148
- def __workbox_filename__(self, selection=False):
196
+ def __workbox_trace_title__(self, selection=False):
149
197
  title = "WorkboxSelection" if selection else "Workbox"
150
198
  group, editor = self.__group_tab_index__()
151
199
  if group == -1 or editor == -1:
152
200
  return '<{}>'.format(title)
153
201
  else:
154
- return '<{}>:{},{}'.format(title, group, editor)
202
+ name = self.__workbox_name__()
203
+ return '<{}>:{}'.format(title, name)
204
+
205
+ def __workbox_name__(self, workbox=None):
206
+ """Returns the name for this workbox or a given workbox.
207
+ The name is the group tab text and the workbox tab text joined by a `/`"""
208
+ workboxTAB = self.window().uiWorkboxTAB
209
+ group_name = None
210
+ workbox_name = None
211
+
212
+ if workbox:
213
+ grouped_tab_widget = workbox.__tab_widget__()
214
+ for group_idx in range(workboxTAB.count()):
215
+ # If a previous iteration determine workbox_name, bust out
216
+ if workbox_name:
217
+ break
218
+ # Check if current group is the workboxes parent group
219
+ cur_group_widget = workboxTAB.widget(group_idx)
220
+ if cur_group_widget == grouped_tab_widget:
221
+ group_name = workboxTAB.tabText(group_idx)
222
+
223
+ # Found the group, now find workbox
224
+ for workbox_idx in range(cur_group_widget.count()):
225
+ cur_workbox_widget = cur_group_widget.widget(workbox_idx)
226
+ if cur_workbox_widget == workbox:
227
+ workbox_name = cur_group_widget.tabText(workbox_idx)
228
+ break
229
+ else:
230
+ grouped = self.__tab_widget__()
231
+ groupedTabBar = grouped.tabBar()
232
+
233
+ idx = -1
234
+ for idx in range(grouped.count()):
235
+ if grouped.widget(idx) == self:
236
+ break
237
+ workbox_name = groupedTabBar.tabText(idx)
238
+
239
+ group = grouped.tab_widget()
240
+ groupTabBar = group.tabBar()
241
+ idx = -1
242
+ for idx in range(group.count()):
243
+ if group.widget(idx) == grouped:
244
+ break
245
+ group_name = groupTabBar.tabText(idx)
246
+
247
+ # If both found, construct workbox name
248
+ if group_name and workbox_name:
249
+ name = WorkboxName(group_name, workbox_name)
250
+ else:
251
+ name = WorkboxName("", "")
252
+ return name
155
253
 
156
254
  def __goto_line__(self, line):
157
255
  raise NotImplementedError("Mixin method not overridden.")
@@ -57,8 +57,8 @@ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
57
57
 
58
58
  def __exec_all__(self):
59
59
  txt = self.__text__().rstrip()
60
- filename = self.__workbox_filename__()
61
- self.__console__().executeString(txt, filename=filename)
60
+ title = self.__workbox_trace_title__()
61
+ self.__console__().executeString(txt, filename=title)
62
62
 
63
63
  def __font__(self):
64
64
  return self.font()