PrEditor 0.0.0.dev1__py2.py3-none-any.whl → 0.1.0__py2.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.

Potentially problematic release.


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

Files changed (363) hide show
  1. PrEditor-0.1.0.dist-info/LICENSE +165 -0
  2. PrEditor-0.1.0.dist-info/METADATA +212 -0
  3. PrEditor-0.1.0.dist-info/RECORD +149 -0
  4. {PrEditor-0.0.0.dev1.dist-info → PrEditor-0.1.0.dist-info}/WHEEL +1 -1
  5. PrEditor-0.1.0.dist-info/entry_points.txt +18 -0
  6. PrEditor-0.1.0.dist-info/top_level.txt +1 -0
  7. preditor/__init__.py +301 -0
  8. {blurdev → preditor}/__main__.py +13 -13
  9. preditor/about_module.py +166 -0
  10. preditor/cli.py +192 -0
  11. {blurdev → preditor}/contexts.py +119 -119
  12. preditor/cores/core.py +65 -0
  13. preditor/dccs/maya/PrEditor_maya.mod +2 -0
  14. preditor/dccs/maya/plug-ins/PrEditor_maya.py +108 -0
  15. preditor/debug.py +294 -0
  16. blurdev/scintilla/delayable_engine.py → preditor/delayable_engine/__init__.py +310 -299
  17. blurdev/scintilla/delayables/base.py → preditor/delayable_engine/delayables.py +85 -85
  18. {blurdev → preditor}/enum.py +728 -1003
  19. {blurdev → preditor}/gui/__init__.py +84 -125
  20. preditor/gui/app.py +159 -0
  21. {blurdev → preditor}/gui/codehighlighter.py +209 -219
  22. {blurdev → preditor}/gui/completer.py +226 -236
  23. {blurdev → preditor}/gui/console.py +801 -858
  24. {blurdev → preditor}/gui/dialog.py +200 -216
  25. preditor/gui/drag_tab_bar.py +190 -0
  26. preditor/gui/editor_chooser.py +57 -0
  27. {blurdev → preditor}/gui/errordialog.py +100 -97
  28. preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
  29. preditor/gui/group_tab_widget/__init__.py +319 -0
  30. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  31. preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
  32. preditor/gui/group_tab_widget/grouped_tab_widget.py +75 -0
  33. preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
  34. preditor/gui/level_buttons.py +349 -0
  35. {blurdev → preditor}/gui/logger_window_handler.py +46 -45
  36. {blurdev → preditor}/gui/loggerwindow.py +1205 -1417
  37. {blurdev → preditor}/gui/newtabwidget.py +69 -68
  38. {blurdev → preditor}/gui/redmine_login_dialog.py +63 -61
  39. {blurdev → preditor}/gui/set_text_editor_path_dialog.py +59 -57
  40. preditor/gui/ui/editor_chooser.ui +93 -0
  41. {blurdev → preditor}/gui/ui/errordialog.ui +81 -81
  42. {blurdev → preditor}/gui/ui/loggerwindow.ui +1030 -864
  43. {blurdev → preditor}/gui/ui/redmine_login_dialog.ui +124 -124
  44. {blurdev → preditor}/gui/ui/set_text_editor_path_dialog.ui +149 -149
  45. {blurdev → preditor}/gui/window.py +183 -199
  46. preditor/gui/workbox_mixin.py +357 -0
  47. preditor/gui/workbox_text_edit.py +117 -0
  48. preditor/gui/workboxwidget.py +276 -0
  49. preditor/logging_config.py +52 -0
  50. preditor/osystem.py +401 -0
  51. preditor/plugins.py +65 -0
  52. preditor/prefs.py +74 -0
  53. {blurdev → preditor}/resource/environment_variables.html +26 -38
  54. {blurdev → preditor}/resource/error_mail.html +85 -85
  55. {blurdev → preditor}/resource/error_mail_inline.html +41 -41
  56. preditor/resource/img/README.md +7 -0
  57. preditor/resource/img/arrow_forward.png +0 -0
  58. preditor/resource/img/check-bold.png +0 -0
  59. preditor/resource/img/chevron-down.png +0 -0
  60. preditor/resource/img/chevron-up.png +0 -0
  61. preditor/resource/img/close-thick.png +0 -0
  62. preditor/resource/img/comment-edit.png +0 -0
  63. preditor/resource/img/content-copy.png +0 -0
  64. preditor/resource/img/content-cut.png +0 -0
  65. preditor/resource/img/content-duplicate.png +0 -0
  66. preditor/resource/img/content-paste.png +0 -0
  67. preditor/resource/img/content-save.png +0 -0
  68. preditor/resource/img/debug_disabled.png +0 -0
  69. preditor/resource/img/eye-check.png +0 -0
  70. preditor/resource/img/file-plus.png +0 -0
  71. preditor/resource/img/file-remove.png +0 -0
  72. preditor/resource/img/format-align-left.png +0 -0
  73. preditor/resource/img/format-letter-case-lower.png +0 -0
  74. preditor/resource/img/format-letter-case-upper.png +0 -0
  75. preditor/resource/img/information.png +0 -0
  76. preditor/resource/img/logging_critical.png +0 -0
  77. preditor/resource/img/logging_custom.png +0 -0
  78. preditor/resource/img/logging_debug.png +0 -0
  79. preditor/resource/img/logging_error.png +0 -0
  80. preditor/resource/img/logging_info.png +0 -0
  81. preditor/resource/img/logging_not_set.png +0 -0
  82. preditor/resource/img/logging_warning.png +0 -0
  83. preditor/resource/img/marker.png +0 -0
  84. preditor/resource/img/play.png +0 -0
  85. preditor/resource/img/playlist-play.png +0 -0
  86. preditor/resource/img/plus-minus-variant.png +0 -0
  87. preditor/resource/img/preditor.ico +0 -0
  88. preditor/resource/img/preditor.png +0 -0
  89. preditor/resource/img/preditor.psd +0 -0
  90. preditor/resource/img/preditor.svg +44 -0
  91. preditor/resource/img/restart.svg +1 -0
  92. preditor/resource/img/skip-forward-outline.png +0 -0
  93. preditor/resource/img/skip-next-outline.png +0 -0
  94. preditor/resource/img/skip-next.png +0 -0
  95. preditor/resource/img/skip-previous.png +0 -0
  96. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  97. preditor/resource/img/text-search-variant.png +0 -0
  98. {blurdev → preditor}/resource/lang/python.json +30 -30
  99. preditor/resource/settings.ini +25 -0
  100. {blurdev/resource/stylesheet/logger → preditor/resource/stylesheet}/Bright.css +56 -61
  101. {blurdev → preditor}/resource/stylesheet/Dark.css +190 -132
  102. {blurdev → preditor}/scintilla/__init__.py +22 -28
  103. preditor/scintilla/delayables/__init__.py +11 -0
  104. {blurdev → preditor}/scintilla/delayables/smart_highlight.py +94 -93
  105. {blurdev → preditor}/scintilla/delayables/spell_check.py +173 -172
  106. {blurdev → preditor}/scintilla/documenteditor.py +2039 -2115
  107. {blurdev → preditor}/scintilla/finddialog.py +68 -81
  108. {blurdev → preditor}/scintilla/lang/__init__.py +80 -93
  109. {blurdev → preditor}/scintilla/lang/config/bash.ini +15 -15
  110. {blurdev → preditor}/scintilla/lang/config/batch.ini +14 -14
  111. {blurdev → preditor}/scintilla/lang/config/cpp.ini +19 -19
  112. {blurdev → preditor}/scintilla/lang/config/css.ini +19 -19
  113. {blurdev → preditor}/scintilla/lang/config/eyeonscript.ini +17 -17
  114. {blurdev → preditor}/scintilla/lang/config/html.ini +21 -21
  115. {blurdev → preditor}/scintilla/lang/config/javascript.ini +24 -24
  116. {blurdev → preditor}/scintilla/lang/config/lua.ini +16 -16
  117. {blurdev → preditor}/scintilla/lang/config/maxscript.ini +20 -20
  118. {blurdev → preditor}/scintilla/lang/config/mel.ini +18 -18
  119. {blurdev → preditor}/scintilla/lang/config/mu.ini +22 -22
  120. {blurdev → preditor}/scintilla/lang/config/nsi.ini +5 -5
  121. {blurdev → preditor}/scintilla/lang/config/perl.ini +19 -19
  122. {blurdev → preditor}/scintilla/lang/config/puppet.ini +19 -19
  123. {blurdev → preditor}/scintilla/lang/config/python.ini +28 -28
  124. {blurdev → preditor}/scintilla/lang/config/ruby.ini +19 -19
  125. {blurdev → preditor}/scintilla/lang/config/sql.ini +7 -7
  126. {blurdev → preditor}/scintilla/lang/config/xml.ini +21 -21
  127. {blurdev → preditor}/scintilla/lang/config/yaml.ini +18 -18
  128. {blurdev → preditor}/scintilla/lang/language.py +240 -250
  129. preditor/scintilla/lexers/__init__.py +0 -0
  130. {blurdev → preditor}/scintilla/lexers/cpplexer.py +21 -30
  131. {blurdev → preditor}/scintilla/lexers/javascriptlexer.py +25 -34
  132. {blurdev → preditor}/scintilla/lexers/maxscriptlexer.py +234 -253
  133. {blurdev → preditor}/scintilla/lexers/mellexer.py +368 -376
  134. {blurdev → preditor}/scintilla/lexers/mulexer.py +32 -41
  135. {blurdev → preditor}/scintilla/lexers/pythonlexer.py +41 -50
  136. {blurdev → preditor}/scintilla/ui/finddialog.ui +160 -160
  137. preditor/settings.py +71 -0
  138. preditor/stream/__init__.py +80 -0
  139. preditor/stream/director.py +56 -0
  140. preditor/stream/manager.py +74 -0
  141. preditor/streamhandler_helper.py +46 -0
  142. preditor/utils/__init__.py +0 -0
  143. preditor/utils/cute.py +30 -0
  144. preditor/utils/stylesheets.py +54 -0
  145. {blurdev → preditor}/version.py +5 -5
  146. preditor/weakref.py +363 -0
  147. PrEditor-0.0.0.dev1.dist-info/METADATA +0 -51
  148. PrEditor-0.0.0.dev1.dist-info/RECORD +0 -279
  149. PrEditor-0.0.0.dev1.dist-info/top_level.txt +0 -1
  150. blurdev/__init__.py +0 -356
  151. blurdev/cores/__init__.py +0 -98
  152. blurdev/cores/application.py +0 -26
  153. blurdev/cores/core.py +0 -634
  154. blurdev/debug.py +0 -593
  155. blurdev/external.py +0 -391
  156. blurdev/gui/level_buttons.py +0 -585
  157. blurdev/gui/workboxwidget.py +0 -205
  158. blurdev/logger.py +0 -238
  159. blurdev/osystem.py +0 -813
  160. blurdev/prefs.py +0 -33
  161. blurdev/protocols/__init__.py +0 -71
  162. blurdev/protocols/write_std_output_handler.py +0 -83
  163. blurdev/resource/designer_plugins.xml +0 -9
  164. blurdev/resource/error_email_old.html +0 -41
  165. blurdev/resource/img/add.png +0 -0
  166. blurdev/resource/img/ajax-loader.gif +0 -0
  167. blurdev/resource/img/application.png +0 -0
  168. blurdev/resource/img/applications.png +0 -0
  169. blurdev/resource/img/assburner.png +0 -0
  170. blurdev/resource/img/assfreezer.png +0 -0
  171. blurdev/resource/img/bar.gif +0 -0
  172. blurdev/resource/img/blank.png +0 -0
  173. blurdev/resource/img/blurdev.png +0 -0
  174. blurdev/resource/img/calendar_disabled.png +0 -0
  175. blurdev/resource/img/calendar_enabled.png +0 -0
  176. blurdev/resource/img/cancel.png +0 -0
  177. blurdev/resource/img/custom.png +0 -0
  178. blurdev/resource/img/debug_high.png +0 -0
  179. blurdev/resource/img/debug_low.png +0 -0
  180. blurdev/resource/img/debug_mid.png +0 -0
  181. blurdev/resource/img/debug_off.png +0 -0
  182. blurdev/resource/img/django.png +0 -0
  183. blurdev/resource/img/doc.png +0 -0
  184. blurdev/resource/img/edit.png +0 -0
  185. blurdev/resource/img/elemental.png +0 -0
  186. blurdev/resource/img/explore.png +0 -0
  187. blurdev/resource/img/favorite.png +0 -0
  188. blurdev/resource/img/file.png +0 -0
  189. blurdev/resource/img/folder.png +0 -0
  190. blurdev/resource/img/ide/add.png +0 -0
  191. blurdev/resource/img/ide/add_note.png +0 -0
  192. blurdev/resource/img/ide/arrow_down.png +0 -0
  193. blurdev/resource/img/ide/arrow_up.png +0 -0
  194. blurdev/resource/img/ide/check.png +0 -0
  195. blurdev/resource/img/ide/class.png +0 -0
  196. blurdev/resource/img/ide/clean.png +0 -0
  197. blurdev/resource/img/ide/clearlog.png +0 -0
  198. blurdev/resource/img/ide/close.png +0 -0
  199. blurdev/resource/img/ide/comment_add.png +0 -0
  200. blurdev/resource/img/ide/comment_remove.png +0 -0
  201. blurdev/resource/img/ide/comment_toggle.png +0 -0
  202. blurdev/resource/img/ide/console.png +0 -0
  203. blurdev/resource/img/ide/copy.png +0 -0
  204. blurdev/resource/img/ide/copylstrip.png +0 -0
  205. blurdev/resource/img/ide/cut.png +0 -0
  206. blurdev/resource/img/ide/edit.png +0 -0
  207. blurdev/resource/img/ide/find.png +0 -0
  208. blurdev/resource/img/ide/find_replace.png +0 -0
  209. blurdev/resource/img/ide/findnext.png +0 -0
  210. blurdev/resource/img/ide/findprev.png +0 -0
  211. blurdev/resource/img/ide/folder_find.png +0 -0
  212. blurdev/resource/img/ide/function.png +0 -0
  213. blurdev/resource/img/ide/git-bash.png +0 -0
  214. blurdev/resource/img/ide/git-gui.png +0 -0
  215. blurdev/resource/img/ide/gitk.png +0 -0
  216. blurdev/resource/img/ide/goto.png +0 -0
  217. blurdev/resource/img/ide/goto_def.png +0 -0
  218. blurdev/resource/img/ide/help.png +0 -0
  219. blurdev/resource/img/ide/highlighter.png +0 -0
  220. blurdev/resource/img/ide/lowercase.png +0 -0
  221. blurdev/resource/img/ide/newfile.png +0 -0
  222. blurdev/resource/img/ide/newfolder.png +0 -0
  223. blurdev/resource/img/ide/newproject.png +0 -0
  224. blurdev/resource/img/ide/newwizard.png +0 -0
  225. blurdev/resource/img/ide/open.png +0 -0
  226. blurdev/resource/img/ide/paste.png +0 -0
  227. blurdev/resource/img/ide/pdb_continue.png +0 -0
  228. blurdev/resource/img/ide/pdb_down.png +0 -0
  229. blurdev/resource/img/ide/pdb_next.png +0 -0
  230. blurdev/resource/img/ide/pdb_step.png +0 -0
  231. blurdev/resource/img/ide/pdb_up.png +0 -0
  232. blurdev/resource/img/ide/plus_minus.png +0 -0
  233. blurdev/resource/img/ide/preferences.png +0 -0
  234. blurdev/resource/img/ide/project_find.png +0 -0
  235. blurdev/resource/img/ide/python.png +0 -0
  236. blurdev/resource/img/ide/pyular.png +0 -0
  237. blurdev/resource/img/ide/qt.png +0 -0
  238. blurdev/resource/img/ide/quit.png +0 -0
  239. blurdev/resource/img/ide/redo.png +0 -0
  240. blurdev/resource/img/ide/refresh.png +0 -0
  241. blurdev/resource/img/ide/remove.png +0 -0
  242. blurdev/resource/img/ide/ruler.png +0 -0
  243. blurdev/resource/img/ide/run.png +0 -0
  244. blurdev/resource/img/ide/runall.png +0 -0
  245. blurdev/resource/img/ide/runallclear.png +0 -0
  246. blurdev/resource/img/ide/runselected.png +0 -0
  247. blurdev/resource/img/ide/runselectedclear.png +0 -0
  248. blurdev/resource/img/ide/save.png +0 -0
  249. blurdev/resource/img/ide/saveas.png +0 -0
  250. blurdev/resource/img/ide/sdk.png +0 -0
  251. blurdev/resource/img/ide/separator.png +0 -0
  252. blurdev/resource/img/ide/tabbed.png +0 -0
  253. blurdev/resource/img/ide/tile.png +0 -0
  254. blurdev/resource/img/ide/toolbar.png +0 -0
  255. blurdev/resource/img/ide/undo.png +0 -0
  256. blurdev/resource/img/ide/uppercase.png +0 -0
  257. blurdev/resource/img/ide/view_as.png +0 -0
  258. blurdev/resource/img/ide/windowed.png +0 -0
  259. blurdev/resource/img/ide.ico +0 -0
  260. blurdev/resource/img/ide.png +0 -0
  261. blurdev/resource/img/ide48.png +0 -0
  262. blurdev/resource/img/info.png +0 -0
  263. blurdev/resource/img/legacy tool.png +0 -0
  264. blurdev/resource/img/library.png +0 -0
  265. blurdev/resource/img/logger/about.png +0 -0
  266. blurdev/resource/img/logger/arrow_forward.png +0 -0
  267. blurdev/resource/img/logger/clear.png +0 -0
  268. blurdev/resource/img/logger/close.png +0 -0
  269. blurdev/resource/img/logger/debug_disabled.png +0 -0
  270. blurdev/resource/img/logger/debug_high.png +0 -0
  271. blurdev/resource/img/logger/debug_low.png +0 -0
  272. blurdev/resource/img/logger/debug_mid.png +0 -0
  273. blurdev/resource/img/logger/down.png +0 -0
  274. blurdev/resource/img/logger/find.png +0 -0
  275. blurdev/resource/img/logger/logging_critical.png +0 -0
  276. blurdev/resource/img/logger/logging_debug.png +0 -0
  277. blurdev/resource/img/logger/logging_error.png +0 -0
  278. blurdev/resource/img/logger/logging_info.png +0 -0
  279. blurdev/resource/img/logger/logging_not_set.png +0 -0
  280. blurdev/resource/img/logger/logging_warning.png +0 -0
  281. blurdev/resource/img/logger/next.png +0 -0
  282. blurdev/resource/img/logger/play.png +0 -0
  283. blurdev/resource/img/logger/playlist_play.png +0 -0
  284. blurdev/resource/img/logger/previous.png +0 -0
  285. blurdev/resource/img/logger/return.png +0 -0
  286. blurdev/resource/img/logger/save.png +0 -0
  287. blurdev/resource/img/logger/subdirectory_arrow_right.png +0 -0
  288. blurdev/resource/img/logger/up.png +0 -0
  289. blurdev/resource/img/lovebar.png +0 -0
  290. blurdev/resource/img/new.png +0 -0
  291. blurdev/resource/img/new_selected.png +0 -0
  292. blurdev/resource/img/node.png +0 -0
  293. blurdev/resource/img/ok.png +0 -0
  294. blurdev/resource/img/options.png +0 -0
  295. blurdev/resource/img/packages.png +0 -0
  296. blurdev/resource/img/preview/add.png +0 -0
  297. blurdev/resource/img/preview/brush.png +0 -0
  298. blurdev/resource/img/preview/delete.png +0 -0
  299. blurdev/resource/img/preview/delte.png +0 -0
  300. blurdev/resource/img/preview/fill.png +0 -0
  301. blurdev/resource/img/preview/layers.png +0 -0
  302. blurdev/resource/img/preview/media.png +0 -0
  303. blurdev/resource/img/preview/navigate.png +0 -0
  304. blurdev/resource/img/preview/pencil.png +0 -0
  305. blurdev/resource/img/preview/select.png +0 -0
  306. blurdev/resource/img/preview/type.png +0 -0
  307. blurdev/resource/img/preview/visible.png +0 -0
  308. blurdev/resource/img/project.png +0 -0
  309. blurdev/resource/img/python_logger.ico +0 -0
  310. blurdev/resource/img/python_logger.png +0 -0
  311. blurdev/resource/img/refresh.png +0 -0
  312. blurdev/resource/img/remove.png +0 -0
  313. blurdev/resource/img/reset.png +0 -0
  314. blurdev/resource/img/richtext/font_bold.png +0 -0
  315. blurdev/resource/img/richtext/font_italic.png +0 -0
  316. blurdev/resource/img/richtext/link_image.png +0 -0
  317. blurdev/resource/img/richtext/spell_check.png +0 -0
  318. blurdev/resource/img/richtext/unordered_list.png +0 -0
  319. blurdev/resource/img/save.png +0 -0
  320. blurdev/resource/img/savesettings.png +0 -0
  321. blurdev/resource/img/settings.png +0 -0
  322. blurdev/resource/img/tool.png +0 -0
  323. blurdev/resource/img/toolbarHandleHorizontal.png +0 -0
  324. blurdev/resource/img/toolbarHandleVertical.png +0 -0
  325. blurdev/resource/img/trash.png +0 -0
  326. blurdev/resource/img/trax.png +0 -0
  327. blurdev/resource/img/tree.png +0 -0
  328. blurdev/resource/img/treegrunt.ico +0 -0
  329. blurdev/resource/img/treegrunt.png +0 -0
  330. blurdev/resource/img/treegruntedit.png +0 -0
  331. blurdev/resource/img/user interface.png +0 -0
  332. blurdev/resource/img/warning.png +0 -0
  333. blurdev/resource/img/watermark.png +0 -0
  334. blurdev/resource/sdk/blurdev.sdk +0 -3
  335. blurdev/resource/settings.ini +0 -82
  336. blurdev/resource/softimage/BlurApplication.dll +0 -0
  337. blurdev/resource/softimage/BlurApplication64.dll +0 -0
  338. blurdev/resource/stylesheet/Carbon.css +0 -35
  339. blurdev/resource/stylesheet/logger/Dark.css +0 -62
  340. blurdev/resource/templ/py_comment.templ +0 -1
  341. blurdev/resource/templ/py_debug_raise_error.templ +0 -7
  342. blurdev/resource/templ/py_doc_string.templ +0 -10
  343. blurdev/resource/templ/py_header.templ +0 -9
  344. blurdev/resource/templ/py_line_comment.templ +0 -1
  345. blurdev/resource/templ/py_log_to_file.templ +0 -22
  346. blurdev/resource/templ/py_module_path.templ +0 -1
  347. blurdev/resource/templ/py_pyqt_core.templ +0 -1
  348. blurdev/resource/templ/py_pyqt_gui.templ +0 -1
  349. blurdev/resource/templ/py_splashscreen.templ +0 -6
  350. blurdev/resource/templ/py_testing_note.templ +0 -1
  351. blurdev/resource/templ/py_testing_note_end.templ +0 -1
  352. blurdev/resource/tools_environments.json +0 -72
  353. blurdev/resource/tools_environments.xml +0 -11
  354. blurdev/resource/tools_environments_linux.xml +0 -11
  355. blurdev/resource/tools_environments_offline.xml +0 -7
  356. blurdev/runtimes/__init__.py +0 -2
  357. blurdev/runtimes/logger.py +0 -44
  358. blurdev/scintilla/delayables/__init__.py +0 -9
  359. blurdev/settings.py +0 -312
  360. blurdev/utils/error.py +0 -389
  361. {blurdev/scintilla/lexers → preditor/cores}/__init__.py +0 -0
  362. {blurdev/utils → preditor/gui/fuzzy_search}/__init__.py +0 -0
  363. {blurdev → preditor}/resource/img/warning-big.png +0 -0
@@ -0,0 +1,35 @@
1
+ from __future__ import absolute_import
2
+
3
+ from preditor.gui.level_buttons import LazyMenu
4
+
5
+
6
+ class GroupTabMenu(LazyMenu):
7
+ """A menu listing all tabs of GroupTabWidget and their child GroupedTabWidget
8
+ tabs. When selecting one of the GroupedTabWidget tab, it will make that tab
9
+ the current tab and give it focus.
10
+ """
11
+
12
+ def __init__(self, manager, parent=None):
13
+ super(GroupTabMenu, self).__init__(parent=parent)
14
+ self.manager = manager
15
+ self.triggered.connect(self.focus_tab)
16
+
17
+ def refresh(self):
18
+ self.clear()
19
+ for group in range(self.manager.count()):
20
+ # Create a "header" for the group tabs
21
+ self.addSeparator()
22
+ act = self.addAction(self.manager.tabText(group))
23
+ act.setEnabled(False)
24
+ self.addSeparator()
25
+
26
+ # Add all of this group tab's tabs
27
+ tab_widget = self.manager.widget(group)
28
+ for index in range(tab_widget.count()):
29
+ act = self.addAction(' {}'.format(tab_widget.tabText(index)))
30
+ act.setProperty('info', (group, index))
31
+
32
+ def focus_tab(self, action):
33
+ group, editor = action.property('info')
34
+ widget = self.manager.set_current_groups_from_index(group, editor)
35
+ widget.setFocus()
@@ -0,0 +1,108 @@
1
+ from __future__ import absolute_import
2
+
3
+ import re
4
+
5
+ from Qt.QtCore import QSortFilterProxyModel, Qt
6
+ from Qt.QtGui import QStandardItem, QStandardItemModel
7
+
8
+
9
+ class GroupTabItemModel(QStandardItemModel):
10
+ GroupIndexRole = Qt.UserRole + 1
11
+ TabIndexRole = GroupIndexRole + 1
12
+
13
+ def __init__(self, manager, *args, **kwargs):
14
+ super(GroupTabItemModel, self).__init__(*args, **kwargs)
15
+ self.manager = manager
16
+
17
+ def workbox_indexes_from_model_index(self, index):
18
+ """Returns the group_index and tab_index for the provided QModelIndex"""
19
+ return (
20
+ index.data(self.GroupIndexRole),
21
+ index.data(self.TabIndexRole),
22
+ )
23
+
24
+ def pathFromIndex(self, index):
25
+ parts = [""]
26
+ while index.isValid():
27
+ parts.append(self.data(index, Qt.DisplayRole))
28
+ index = index.parent()
29
+ if len(parts) == 1:
30
+ return ""
31
+ return "/".join([x for x in parts[::-1] if x])
32
+
33
+
34
+ class GroupTabTreeItemModel(GroupTabItemModel):
35
+ def process(self):
36
+ root = self.invisibleRootItem()
37
+ current_group = self.manager.currentIndex()
38
+ current_tab = self.manager.currentWidget().currentIndex()
39
+
40
+ prev_group = -1
41
+ all_widgets = self.manager.all_widgets()
42
+ for _, group_name, tab_name, group_index, tab_index in all_widgets:
43
+ if prev_group != group_index:
44
+ group_item = QStandardItem(group_name)
45
+ group_item.setData(group_index, self.GroupIndexRole)
46
+ root.appendRow(group_item)
47
+ prev_group = group_index
48
+
49
+ tab_item = QStandardItem(tab_name)
50
+ tab_item.setData(group_index, self.GroupIndexRole)
51
+ tab_item.setData(tab_index, self.TabIndexRole)
52
+ group_item.appendRow(tab_item)
53
+ if group_index == current_group and tab_index == current_tab:
54
+ self.original_model_index = self.indexFromItem(tab_item)
55
+
56
+
57
+ class GroupTabListItemModel(GroupTabItemModel):
58
+ def flags(self, index):
59
+ return Qt.ItemIsEnabled | Qt.ItemIsSelectable
60
+
61
+ def process(self):
62
+ root = self.invisibleRootItem()
63
+ current_group = self.manager.currentIndex()
64
+ current_tab = self.manager.currentWidget().currentIndex()
65
+
66
+ all_widgets = self.manager.all_widgets()
67
+ for _, group_name, tab_name, group_index, tab_index in all_widgets:
68
+ tab_item = QStandardItem('/'.join((group_name, tab_name)))
69
+ tab_item.setData(group_index, self.GroupIndexRole)
70
+ tab_item.setData(tab_index, self.TabIndexRole)
71
+ root.appendRow(tab_item)
72
+ if group_index == current_group and tab_index == current_tab:
73
+ self.original_model_index = self.indexFromItem(tab_item)
74
+
75
+
76
+ class GroupTabFuzzyFilterProxyModel(QSortFilterProxyModel):
77
+ """Implements a fuzzy search filter proxy model."""
78
+
79
+ def __init__(self, parent=None):
80
+ super(GroupTabFuzzyFilterProxyModel, self).__init__(parent=parent)
81
+ self._fuzzy_regex = None
82
+
83
+ def setFuzzySearch(self, search):
84
+ search = '.*'.join(search)
85
+ # search = '.*{}.*'.format(search)
86
+ self._fuzzy_regex = re.compile(search, re.I)
87
+ self.invalidateFilter()
88
+
89
+ def filterAcceptsRow(self, sourceRow, sourceParent):
90
+ if self.filterKeyColumn() == 0 and self._fuzzy_regex:
91
+
92
+ index = self.sourceModel().index(sourceRow, 0, sourceParent)
93
+ data = self.sourceModel().data(index)
94
+ ret = bool(self._fuzzy_regex.search(data))
95
+ return ret
96
+
97
+ return super(GroupTabFuzzyFilterProxyModel, self).filterAcceptsRow(
98
+ sourceRow, sourceParent
99
+ )
100
+
101
+ def pathFromIndex(self, index):
102
+ parts = [""]
103
+ while index.isValid():
104
+ parts.append(self.data(index, Qt.DisplayRole))
105
+ index = index.parent()
106
+ if len(parts) == 1:
107
+ return ""
108
+ return "/".join([x for x in parts[::-1] if x])
@@ -0,0 +1,75 @@
1
+ from __future__ import absolute_import
2
+
3
+ from Qt.QtCore import Qt
4
+ from Qt.QtGui import QIcon
5
+ from Qt.QtWidgets import QMessageBox, QToolButton
6
+
7
+ from ... import resourcePath
8
+ from ..drag_tab_bar import DragTabBar
9
+ from ..workbox_text_edit import WorkboxTextEdit
10
+ from .one_tab_widget import OneTabWidget
11
+
12
+
13
+ class GroupedTabWidget(OneTabWidget):
14
+ def __init__(self, editor_kwargs, editor_cls=None, core_name=None, *args, **kwargs):
15
+ super(GroupedTabWidget, self).__init__(*args, **kwargs)
16
+ DragTabBar.install_tab_widget(self, 'grouped_tab_widget')
17
+ self.editor_kwargs = editor_kwargs
18
+ if editor_cls is None:
19
+ editor_cls = WorkboxTextEdit
20
+ self.editor_cls = editor_cls
21
+ self.core_name = core_name
22
+ self.currentChanged.connect(self.tab_shown)
23
+
24
+ self.uiCornerBTN = QToolButton(self)
25
+ self.uiCornerBTN.setText('+')
26
+ self.uiCornerBTN.setIcon(QIcon(resourcePath('img/file-plus.png')))
27
+ self.uiCornerBTN.released.connect(lambda: self.add_new_editor())
28
+ self.setCornerWidget(self.uiCornerBTN, Qt.TopRightCorner)
29
+
30
+ def add_new_editor(self, title="Workbox"):
31
+ editor, title = self.default_tab(title)
32
+ index = self.addTab(editor, title)
33
+ self.setCurrentIndex(index)
34
+ return editor
35
+
36
+ def addTab(self, *args, **kwargs): # noqa: N802
37
+ ret = super(GroupedTabWidget, self).addTab(*args, **kwargs)
38
+ self.update_closable_tabs()
39
+ return ret
40
+
41
+ def close_tab(self, index):
42
+ if self.count() == 1:
43
+ msg = "You have to leave at least one tab open."
44
+ QMessageBox.critical(self, 'Tab can not be closed.', msg, QMessageBox.Ok)
45
+ return
46
+ ret = QMessageBox.question(
47
+ self,
48
+ 'Donate to the cause?',
49
+ "Would you like to donate this tabs contents to the /dev/null fund "
50
+ "for wayward code?",
51
+ QMessageBox.Yes | QMessageBox.Cancel,
52
+ )
53
+ if ret == QMessageBox.Yes:
54
+ # If the tab was saved to a temp file, remove it from disk
55
+ editor = self.widget(index)
56
+ editor.__remove_tempfile__()
57
+
58
+ super(GroupedTabWidget, self).close_tab(index)
59
+
60
+ def default_tab(self, title='Workbox'):
61
+ kwargs = self.editor_kwargs if self.editor_kwargs else {}
62
+ editor = self.editor_cls(parent=self, core_name=self.core_name, **kwargs)
63
+ return editor, title
64
+
65
+ def showEvent(self, event): # noqa: N802
66
+ super(GroupedTabWidget, self).showEvent(event)
67
+ self.tab_shown(self.currentIndex())
68
+
69
+ def tab_shown(self, index):
70
+ editor = self.widget(index)
71
+ if editor and editor.isVisible():
72
+ editor.__show__()
73
+
74
+ def update_closable_tabs(self):
75
+ self.setTabsClosable(self.count() != 1)
@@ -0,0 +1,54 @@
1
+ from __future__ import absolute_import
2
+
3
+ from Qt.QtWidgets import QTabWidget
4
+
5
+
6
+ class OneTabWidget(QTabWidget):
7
+ """A QTabWidget that shows the close button only if there is more than one
8
+ tab. If something removes the last tab, it will add a default tab if the
9
+ default_tab method is implemented on a subclass. This is also used to create
10
+ the first tab on showEvent.
11
+
12
+ Subclasses can implement a `default_tab()` method. This should return the
13
+ widget to add and the title of the tab to create if implemented.
14
+ """
15
+
16
+ def __init__(self, *args, **kwargs):
17
+ super(OneTabWidget, self).__init__(*args, **kwargs)
18
+ self.tabCloseRequested.connect(self.close_tab)
19
+
20
+ def addTab(self, *args, **kwargs): # noqa: N802
21
+ ret = super(OneTabWidget, self).addTab(*args, **kwargs)
22
+ self.update_closable_tabs()
23
+ return ret
24
+
25
+ def close_tab(self, index):
26
+ self.removeTab(index)
27
+ self.update_closable_tabs()
28
+
29
+ def index_for_text(self, text):
30
+ """Return the index of the tab with this text. Returns -1 if not found"""
31
+ for i in range(self.count()):
32
+ if self.tabText(i) == text:
33
+ return i
34
+ return -1
35
+
36
+ def insertTab(self, *args, **kwargs): # noqa: N802
37
+ ret = super(OneTabWidget, self).insertTab(*args, **kwargs)
38
+ self.update_closable_tabs()
39
+ return ret
40
+
41
+ def removeTab(self, index): # noqa: N802
42
+ super(OneTabWidget, self).removeTab(index)
43
+ if hasattr(self, 'default_tab') and not self.count():
44
+ self.addTab(*self.default_tab())
45
+ self.update_closable_tabs()
46
+
47
+ def showEvent(self, event): # noqa: N802
48
+ super(OneTabWidget, self).showEvent(event)
49
+ # Force the creation of a default tab if defined
50
+ if hasattr(self, 'default_tab') and not self.count():
51
+ self.addTab(*self.default_tab())
52
+
53
+ def update_closable_tabs(self):
54
+ self.setTabsClosable(self.count() != 1)
@@ -0,0 +1,349 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+ import types
5
+ from functools import partial
6
+
7
+ import six
8
+ from Qt.QtGui import QIcon
9
+ from Qt.QtWidgets import QAction, QMenu, QToolButton
10
+
11
+ from .. import plugins, resourcePath
12
+ from ..enum import Enum, EnumGroup
13
+
14
+
15
+ class Level(Enum):
16
+
17
+ """
18
+ Custom `Enum` representing an information level.
19
+
20
+ Attributes:
21
+ cached_icon(None): Used to cache the created icon from `get_icon` for
22
+ future use.
23
+ icon_name(str): Name of source icon file to use when creating icon via
24
+ `get_icon`.
25
+ """
26
+
27
+ cached_icon = None
28
+ icon_name = "dot"
29
+
30
+ @property
31
+ def name(self):
32
+ """
33
+ Override of `name` property allowing for the return of a "friendly
34
+ name" to be used in place of the inferred name from the `Enum` instance
35
+ name.
36
+
37
+ Returns:
38
+ str: Name of `Enum` instance.
39
+ """
40
+ return getattr(self, "friendly_name", super(Level, self).name)
41
+
42
+ @property
43
+ def icon(self):
44
+ """
45
+ Icon representing the level. On first access, the icon is created via
46
+ the `get_icon`-method and cached for later use.
47
+
48
+ Returns:
49
+ QIcon:
50
+ """
51
+ if not self.cached_icon:
52
+ self.cached_icon = self.get_icon(self.icon_name, self.level)
53
+ return self.cached_icon
54
+
55
+ def get_icon(self, name, level):
56
+ """
57
+ Retrieves the icon of `name` and level.
58
+
59
+ Args:
60
+ name (str): Icon to retrieve QIcon for.
61
+ level (str): Level name to apply.
62
+
63
+ Returns:
64
+ QIcon: Correct instantiated QIcon.
65
+ """
66
+ return QIcon(
67
+ resourcePath('img/{name}_{level}.png'.format(name=name, level=level))
68
+ )
69
+
70
+
71
+ class LoggerLevel(Level):
72
+ """A Logger level `Enum` using the 'format_align_left' icon."""
73
+
74
+ icon_name = "logging"
75
+
76
+ def is_current(self, logger):
77
+ """Returns if the current logging level matches this label."""
78
+ return logging.getLevelName(logger.level) == self.label
79
+
80
+
81
+ class LoggerLevels(EnumGroup):
82
+ """
83
+ Logger levels with their implementation level name and number & custom
84
+ icon level.
85
+ """
86
+
87
+ Disabled = LoggerLevel(
88
+ friendly_name="Not Set / Inherited", label="NOTSET", number=0, level="not_set"
89
+ )
90
+ Critical = LoggerLevel(label="CRITICAL", number=50, level="critical")
91
+ Error = LoggerLevel(label="ERROR", number=40, level="error")
92
+ Warning = LoggerLevel(label="WARNING", number=30, level="warning")
93
+ Info = LoggerLevel(label="INFO", number=20, level="info")
94
+ Debug = LoggerLevel(label="DEBUG", number=10, level="debug")
95
+
96
+ @classmethod
97
+ def fromLabel(cls, label, default=None, logger=None):
98
+ try:
99
+ return super(LoggerLevels, cls).fromLabel(label, default=default)
100
+ except ValueError:
101
+ # This is not be a standard level, generate a custom level to use
102
+ if logger is None:
103
+ logger = logging.getLogger()
104
+ effective_level = logger.getEffectiveLevel()
105
+ effective_level_name = logging.getLevelName(effective_level)
106
+
107
+ enum = LoggerLevel(
108
+ label=effective_level_name,
109
+ number=effective_level,
110
+ level=effective_level_name,
111
+ )
112
+ # Force the custom icon as this enum's name won't match
113
+ enum.cached_icon = enum.get_icon(enum.icon_name, "custom")
114
+ # Add it to the enum
115
+ LoggerLevels.append(enum)
116
+ return enum
117
+
118
+
119
+ class LoggingLevelButton(QToolButton):
120
+
121
+ """
122
+ A drop down button to set logger levels for all loggers known to Python's
123
+ native logging implementation.
124
+
125
+ The logger menus present in the tool bar button have level-changing actions
126
+ as well a sub-menus for any descendant loggers.
127
+ """
128
+
129
+ def __init__(self, parent=None):
130
+ """
131
+ Creates the root logger menu this button displays when clicked.
132
+ Additionally, any pre-existing loggers and their menus are added.
133
+
134
+ Args:
135
+ parent (QWidget, optional): The parent widget for this button.
136
+ """
137
+ super(LoggingLevelButton, self).__init__(parent=parent)
138
+ self.setPopupMode(QToolButton.InstantPopup)
139
+
140
+ # create root logger menu
141
+ root = logging.getLogger("")
142
+ root_menu = LoggingLevelMenu(name="root", logger=root, parent=self)
143
+ self.setMenu(root_menu)
144
+
145
+ # TODO: Hook refresh up to a root logger signal
146
+ # Monkey patch root.setLogger to emit signal we connect to
147
+ root = self.patched_root_logger().level_changed.connect(self.refresh)
148
+
149
+ @staticmethod
150
+ def patched_root_logger():
151
+ """Returns `logging.getLogger("")`. This will have the level_changed
152
+ signal added if it wasn't already.
153
+
154
+ The level_changed signal is emitted any time something changes the
155
+ root logger level. PrEditor uses this to update the logging level button
156
+ icon any time the root logger's level is changed. The rest of the loggers
157
+ don't need this as the menu is built on demand with the correct icons indicated.
158
+ """
159
+ root = logging.getLogger("")
160
+ if hasattr(root, "level_changed"):
161
+ # Already patched, nothing to do
162
+ return root
163
+
164
+ # Need to patch the root logger
165
+ from signalslot import Signal
166
+
167
+ root.level_changed = Signal(args=["level"], name="level_changed")
168
+
169
+ # Store the current setLevel, so we can call it in our method
170
+ root._setLevel = root.setLevel
171
+
172
+ def setLevel(self, level):
173
+ """
174
+ Sets the threshold for this logger to `level`. Also emits the
175
+ instance's `level_changed`-signal with the level number as its payload.
176
+
177
+ Args:
178
+ level (int): Numeric level value.
179
+ """
180
+ # Call the original setLevel method
181
+ self._setLevel(level)
182
+ # Emit our signal
183
+ self.level_changed.emit(level=level)
184
+
185
+ if six.PY3:
186
+ root.setLevel = types.MethodType(setLevel, root)
187
+ else:
188
+ root.setLevel = types.MethodType(setLevel, root, type(root))
189
+
190
+ return root
191
+
192
+ def refresh(self, **kwargs):
193
+ effective_level = logging.getLogger("").getEffectiveLevel()
194
+ effective_level_name = logging.getLevelName(effective_level)
195
+ level_enum = LoggerLevels.fromLabel(effective_level_name)
196
+
197
+ self.setIcon(level_enum.icon)
198
+ self.setToolTip("Logger 'root' current level: {}".format(level_enum.name))
199
+
200
+
201
+ class LazyMenu(QMenu):
202
+ """A menu class that only calls self.refresh when it is about to be shown."""
203
+
204
+ def __init__(self, *args, **kwargs):
205
+ super(LazyMenu, self).__init__(*args, **kwargs)
206
+ self.aboutToShow.connect(self.refresh)
207
+
208
+
209
+ class HandlerMenu(LazyMenu):
210
+ def __init__(self, logger, parent=None):
211
+ super(HandlerMenu, self).__init__(title="Handlers", parent=parent)
212
+ self.logger = logger
213
+
214
+ def install_handler(self, name):
215
+ for _, cls in plugins.logging_handlers(name):
216
+ handler = cls()
217
+ self.logger.addHandler(handler)
218
+
219
+ def refresh(self):
220
+ self.clear()
221
+ # Add the Install sub menu showing all logging_handler plugins
222
+ handler_install = self.addMenu('Install')
223
+ for name, cls in plugins.logging_handlers():
224
+ act = handler_install.addAction(name)
225
+ act.triggered.connect(partial(self.install_handler, name))
226
+ for h in self.logger.handlers:
227
+ if isinstance(h, cls):
228
+ act.setEnabled(False)
229
+ act.setToolTip('Already installed for this logger.')
230
+ break
231
+
232
+ # Add a visual indication of all of the existing handlers
233
+ # TODO: Add ability to modify the formatters and auto-creation on startup
234
+ self.addSeparator()
235
+ for handler in self.logger.handlers:
236
+ act = self.addAction(repr(handler))
237
+ act.setEnabled(False)
238
+
239
+
240
+ class LoggingLevelMenu(LazyMenu):
241
+
242
+ """
243
+ Custom menu for Python Loggers.
244
+
245
+ Provides an interface for changing logger levels via menu actions. Also
246
+ displays the presently set level by highlighting the relevant menu action
247
+ and via the menu's icon (which displays the logger's effective level,
248
+ potentially inherited from its ancestor).
249
+ """
250
+
251
+ def __init__(self, name, logger, parent=None):
252
+ """
253
+ Creates the default level menu actions for updating the logger's level.
254
+
255
+ Args:
256
+ name (str): Name of Logger this menu will represent.
257
+ logger (logging.Logger): Logger this menu will represent and control
258
+ via actions that modify the logger's set level.
259
+ parent (QToolButton/QMenu): `QMenu` or `QToolButton` this menu will
260
+ be parented to.
261
+
262
+ Note: If the logger is merely a placeholder it will be initialized so
263
+ it can be added to the menu hierarchy. This ensures all ancestors
264
+ exist for appropriate parenting when descendants are added.
265
+ """
266
+ super(LoggingLevelMenu, self).__init__(title=name.split(".")[-1], parent=parent)
267
+
268
+ if isinstance(logger, logging.PlaceHolder):
269
+ logger = logging.getLogger(name)
270
+
271
+ self.logger = logger
272
+ self.name = name
273
+ self.update_ui()
274
+
275
+ def children(self):
276
+ """The direct sub-loggers of this logging object."""
277
+ parent = self.name
278
+ if parent == "root":
279
+ parent = ""
280
+ for name, logger in sorted(
281
+ logging.root.manager.loggerDict.items(), key=lambda x: x[0].lower()
282
+ ):
283
+ if name.startswith(parent):
284
+ remaining = name.lstrip(parent).lstrip(".")
285
+ if remaining and "." not in remaining:
286
+ yield name, logger
287
+
288
+ def level(self):
289
+ """Returns the current effective LoggerLevel for self.logger."""
290
+ effective_level = self.logger.getEffectiveLevel()
291
+ effective_level_name = logging.getLevelName(effective_level)
292
+ return LoggerLevels.fromLabel(effective_level_name, logger=self.logger)
293
+
294
+ def refresh(self):
295
+ self.clear()
296
+
297
+ self.addMenu(HandlerMenu(self.logger, self))
298
+ self.addSeparator()
299
+
300
+ for logger_level in LoggerLevels:
301
+ is_current = logger_level.is_current(self.logger)
302
+
303
+ action = QAction(logger_level.icon, logger_level.name, self)
304
+ action.setCheckable(True)
305
+ action.setChecked(is_current)
306
+
307
+ # tooltip example: "Set 'preditor.debug' to level Warning")
308
+ action.setToolTip(
309
+ "Set '{}' to level {}".format(self.name, logger_level.name)
310
+ )
311
+
312
+ # when clicked/activated set associated loggers level
313
+ action.triggered.connect(partial(self.setLevel, logger_level.number))
314
+ self.addAction(action)
315
+
316
+ self.addSeparator()
317
+
318
+ for name, child in self.children():
319
+ self.addMenu(LoggingLevelMenu(name, child, self))
320
+
321
+ def setLevel(self, level):
322
+ """
323
+ Sets the logger this menu object represents to the level supplied.
324
+
325
+ Args:
326
+ level (str): Logging level to set logger to.
327
+ """
328
+ self.logger.setLevel(level)
329
+ self.update_ui()
330
+
331
+ def update_ui(self):
332
+ """Set the menu icon to this LoggerLevel's icon.
333
+
334
+ If the updated logger is the root logger, the logging level toolbar
335
+ button's icon is updated instead.
336
+
337
+ Args:
338
+ level (LoggerLevel): Logging level to change icon to represent.
339
+ """
340
+
341
+ level_enum = self.level()
342
+ act = self.menuAction()
343
+ act.setIcon(level_enum.icon)
344
+ act.setToolTip(
345
+ "Logger '{}' current level: {}".format(self.logger.name, level_enum.name)
346
+ )
347
+
348
+ if self.name == "root":
349
+ self.parent().refresh()