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.
- PrEditor-0.1.0.dist-info/LICENSE +165 -0
- PrEditor-0.1.0.dist-info/METADATA +212 -0
- PrEditor-0.1.0.dist-info/RECORD +149 -0
- {PrEditor-0.0.0.dev1.dist-info → PrEditor-0.1.0.dist-info}/WHEEL +1 -1
- PrEditor-0.1.0.dist-info/entry_points.txt +18 -0
- PrEditor-0.1.0.dist-info/top_level.txt +1 -0
- preditor/__init__.py +301 -0
- {blurdev → preditor}/__main__.py +13 -13
- preditor/about_module.py +166 -0
- preditor/cli.py +192 -0
- {blurdev → preditor}/contexts.py +119 -119
- preditor/cores/core.py +65 -0
- preditor/dccs/maya/PrEditor_maya.mod +2 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +108 -0
- preditor/debug.py +294 -0
- blurdev/scintilla/delayable_engine.py → preditor/delayable_engine/__init__.py +310 -299
- blurdev/scintilla/delayables/base.py → preditor/delayable_engine/delayables.py +85 -85
- {blurdev → preditor}/enum.py +728 -1003
- {blurdev → preditor}/gui/__init__.py +84 -125
- preditor/gui/app.py +159 -0
- {blurdev → preditor}/gui/codehighlighter.py +209 -219
- {blurdev → preditor}/gui/completer.py +226 -236
- {blurdev → preditor}/gui/console.py +801 -858
- {blurdev → preditor}/gui/dialog.py +200 -216
- preditor/gui/drag_tab_bar.py +190 -0
- preditor/gui/editor_chooser.py +57 -0
- {blurdev → preditor}/gui/errordialog.py +100 -97
- preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
- preditor/gui/group_tab_widget/__init__.py +319 -0
- preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
- preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
- preditor/gui/group_tab_widget/grouped_tab_widget.py +75 -0
- preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
- preditor/gui/level_buttons.py +349 -0
- {blurdev → preditor}/gui/logger_window_handler.py +46 -45
- {blurdev → preditor}/gui/loggerwindow.py +1205 -1417
- {blurdev → preditor}/gui/newtabwidget.py +69 -68
- {blurdev → preditor}/gui/redmine_login_dialog.py +63 -61
- {blurdev → preditor}/gui/set_text_editor_path_dialog.py +59 -57
- preditor/gui/ui/editor_chooser.ui +93 -0
- {blurdev → preditor}/gui/ui/errordialog.ui +81 -81
- {blurdev → preditor}/gui/ui/loggerwindow.ui +1030 -864
- {blurdev → preditor}/gui/ui/redmine_login_dialog.ui +124 -124
- {blurdev → preditor}/gui/ui/set_text_editor_path_dialog.ui +149 -149
- {blurdev → preditor}/gui/window.py +183 -199
- preditor/gui/workbox_mixin.py +357 -0
- preditor/gui/workbox_text_edit.py +117 -0
- preditor/gui/workboxwidget.py +276 -0
- preditor/logging_config.py +52 -0
- preditor/osystem.py +401 -0
- preditor/plugins.py +65 -0
- preditor/prefs.py +74 -0
- {blurdev → preditor}/resource/environment_variables.html +26 -38
- {blurdev → preditor}/resource/error_mail.html +85 -85
- {blurdev → preditor}/resource/error_mail_inline.html +41 -41
- preditor/resource/img/README.md +7 -0
- preditor/resource/img/arrow_forward.png +0 -0
- preditor/resource/img/check-bold.png +0 -0
- preditor/resource/img/chevron-down.png +0 -0
- preditor/resource/img/chevron-up.png +0 -0
- preditor/resource/img/close-thick.png +0 -0
- preditor/resource/img/comment-edit.png +0 -0
- preditor/resource/img/content-copy.png +0 -0
- preditor/resource/img/content-cut.png +0 -0
- preditor/resource/img/content-duplicate.png +0 -0
- preditor/resource/img/content-paste.png +0 -0
- preditor/resource/img/content-save.png +0 -0
- preditor/resource/img/debug_disabled.png +0 -0
- preditor/resource/img/eye-check.png +0 -0
- preditor/resource/img/file-plus.png +0 -0
- preditor/resource/img/file-remove.png +0 -0
- preditor/resource/img/format-align-left.png +0 -0
- preditor/resource/img/format-letter-case-lower.png +0 -0
- preditor/resource/img/format-letter-case-upper.png +0 -0
- preditor/resource/img/information.png +0 -0
- preditor/resource/img/logging_critical.png +0 -0
- preditor/resource/img/logging_custom.png +0 -0
- preditor/resource/img/logging_debug.png +0 -0
- preditor/resource/img/logging_error.png +0 -0
- preditor/resource/img/logging_info.png +0 -0
- preditor/resource/img/logging_not_set.png +0 -0
- preditor/resource/img/logging_warning.png +0 -0
- preditor/resource/img/marker.png +0 -0
- preditor/resource/img/play.png +0 -0
- preditor/resource/img/playlist-play.png +0 -0
- preditor/resource/img/plus-minus-variant.png +0 -0
- preditor/resource/img/preditor.ico +0 -0
- preditor/resource/img/preditor.png +0 -0
- preditor/resource/img/preditor.psd +0 -0
- preditor/resource/img/preditor.svg +44 -0
- preditor/resource/img/restart.svg +1 -0
- preditor/resource/img/skip-forward-outline.png +0 -0
- preditor/resource/img/skip-next-outline.png +0 -0
- preditor/resource/img/skip-next.png +0 -0
- preditor/resource/img/skip-previous.png +0 -0
- preditor/resource/img/subdirectory-arrow-right.png +0 -0
- preditor/resource/img/text-search-variant.png +0 -0
- {blurdev → preditor}/resource/lang/python.json +30 -30
- preditor/resource/settings.ini +25 -0
- {blurdev/resource/stylesheet/logger → preditor/resource/stylesheet}/Bright.css +56 -61
- {blurdev → preditor}/resource/stylesheet/Dark.css +190 -132
- {blurdev → preditor}/scintilla/__init__.py +22 -28
- preditor/scintilla/delayables/__init__.py +11 -0
- {blurdev → preditor}/scintilla/delayables/smart_highlight.py +94 -93
- {blurdev → preditor}/scintilla/delayables/spell_check.py +173 -172
- {blurdev → preditor}/scintilla/documenteditor.py +2039 -2115
- {blurdev → preditor}/scintilla/finddialog.py +68 -81
- {blurdev → preditor}/scintilla/lang/__init__.py +80 -93
- {blurdev → preditor}/scintilla/lang/config/bash.ini +15 -15
- {blurdev → preditor}/scintilla/lang/config/batch.ini +14 -14
- {blurdev → preditor}/scintilla/lang/config/cpp.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/css.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/eyeonscript.ini +17 -17
- {blurdev → preditor}/scintilla/lang/config/html.ini +21 -21
- {blurdev → preditor}/scintilla/lang/config/javascript.ini +24 -24
- {blurdev → preditor}/scintilla/lang/config/lua.ini +16 -16
- {blurdev → preditor}/scintilla/lang/config/maxscript.ini +20 -20
- {blurdev → preditor}/scintilla/lang/config/mel.ini +18 -18
- {blurdev → preditor}/scintilla/lang/config/mu.ini +22 -22
- {blurdev → preditor}/scintilla/lang/config/nsi.ini +5 -5
- {blurdev → preditor}/scintilla/lang/config/perl.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/puppet.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/python.ini +28 -28
- {blurdev → preditor}/scintilla/lang/config/ruby.ini +19 -19
- {blurdev → preditor}/scintilla/lang/config/sql.ini +7 -7
- {blurdev → preditor}/scintilla/lang/config/xml.ini +21 -21
- {blurdev → preditor}/scintilla/lang/config/yaml.ini +18 -18
- {blurdev → preditor}/scintilla/lang/language.py +240 -250
- preditor/scintilla/lexers/__init__.py +0 -0
- {blurdev → preditor}/scintilla/lexers/cpplexer.py +21 -30
- {blurdev → preditor}/scintilla/lexers/javascriptlexer.py +25 -34
- {blurdev → preditor}/scintilla/lexers/maxscriptlexer.py +234 -253
- {blurdev → preditor}/scintilla/lexers/mellexer.py +368 -376
- {blurdev → preditor}/scintilla/lexers/mulexer.py +32 -41
- {blurdev → preditor}/scintilla/lexers/pythonlexer.py +41 -50
- {blurdev → preditor}/scintilla/ui/finddialog.ui +160 -160
- preditor/settings.py +71 -0
- preditor/stream/__init__.py +80 -0
- preditor/stream/director.py +56 -0
- preditor/stream/manager.py +74 -0
- preditor/streamhandler_helper.py +46 -0
- preditor/utils/__init__.py +0 -0
- preditor/utils/cute.py +30 -0
- preditor/utils/stylesheets.py +54 -0
- {blurdev → preditor}/version.py +5 -5
- preditor/weakref.py +363 -0
- PrEditor-0.0.0.dev1.dist-info/METADATA +0 -51
- PrEditor-0.0.0.dev1.dist-info/RECORD +0 -279
- PrEditor-0.0.0.dev1.dist-info/top_level.txt +0 -1
- blurdev/__init__.py +0 -356
- blurdev/cores/__init__.py +0 -98
- blurdev/cores/application.py +0 -26
- blurdev/cores/core.py +0 -634
- blurdev/debug.py +0 -593
- blurdev/external.py +0 -391
- blurdev/gui/level_buttons.py +0 -585
- blurdev/gui/workboxwidget.py +0 -205
- blurdev/logger.py +0 -238
- blurdev/osystem.py +0 -813
- blurdev/prefs.py +0 -33
- blurdev/protocols/__init__.py +0 -71
- blurdev/protocols/write_std_output_handler.py +0 -83
- blurdev/resource/designer_plugins.xml +0 -9
- blurdev/resource/error_email_old.html +0 -41
- blurdev/resource/img/add.png +0 -0
- blurdev/resource/img/ajax-loader.gif +0 -0
- blurdev/resource/img/application.png +0 -0
- blurdev/resource/img/applications.png +0 -0
- blurdev/resource/img/assburner.png +0 -0
- blurdev/resource/img/assfreezer.png +0 -0
- blurdev/resource/img/bar.gif +0 -0
- blurdev/resource/img/blank.png +0 -0
- blurdev/resource/img/blurdev.png +0 -0
- blurdev/resource/img/calendar_disabled.png +0 -0
- blurdev/resource/img/calendar_enabled.png +0 -0
- blurdev/resource/img/cancel.png +0 -0
- blurdev/resource/img/custom.png +0 -0
- blurdev/resource/img/debug_high.png +0 -0
- blurdev/resource/img/debug_low.png +0 -0
- blurdev/resource/img/debug_mid.png +0 -0
- blurdev/resource/img/debug_off.png +0 -0
- blurdev/resource/img/django.png +0 -0
- blurdev/resource/img/doc.png +0 -0
- blurdev/resource/img/edit.png +0 -0
- blurdev/resource/img/elemental.png +0 -0
- blurdev/resource/img/explore.png +0 -0
- blurdev/resource/img/favorite.png +0 -0
- blurdev/resource/img/file.png +0 -0
- blurdev/resource/img/folder.png +0 -0
- blurdev/resource/img/ide/add.png +0 -0
- blurdev/resource/img/ide/add_note.png +0 -0
- blurdev/resource/img/ide/arrow_down.png +0 -0
- blurdev/resource/img/ide/arrow_up.png +0 -0
- blurdev/resource/img/ide/check.png +0 -0
- blurdev/resource/img/ide/class.png +0 -0
- blurdev/resource/img/ide/clean.png +0 -0
- blurdev/resource/img/ide/clearlog.png +0 -0
- blurdev/resource/img/ide/close.png +0 -0
- blurdev/resource/img/ide/comment_add.png +0 -0
- blurdev/resource/img/ide/comment_remove.png +0 -0
- blurdev/resource/img/ide/comment_toggle.png +0 -0
- blurdev/resource/img/ide/console.png +0 -0
- blurdev/resource/img/ide/copy.png +0 -0
- blurdev/resource/img/ide/copylstrip.png +0 -0
- blurdev/resource/img/ide/cut.png +0 -0
- blurdev/resource/img/ide/edit.png +0 -0
- blurdev/resource/img/ide/find.png +0 -0
- blurdev/resource/img/ide/find_replace.png +0 -0
- blurdev/resource/img/ide/findnext.png +0 -0
- blurdev/resource/img/ide/findprev.png +0 -0
- blurdev/resource/img/ide/folder_find.png +0 -0
- blurdev/resource/img/ide/function.png +0 -0
- blurdev/resource/img/ide/git-bash.png +0 -0
- blurdev/resource/img/ide/git-gui.png +0 -0
- blurdev/resource/img/ide/gitk.png +0 -0
- blurdev/resource/img/ide/goto.png +0 -0
- blurdev/resource/img/ide/goto_def.png +0 -0
- blurdev/resource/img/ide/help.png +0 -0
- blurdev/resource/img/ide/highlighter.png +0 -0
- blurdev/resource/img/ide/lowercase.png +0 -0
- blurdev/resource/img/ide/newfile.png +0 -0
- blurdev/resource/img/ide/newfolder.png +0 -0
- blurdev/resource/img/ide/newproject.png +0 -0
- blurdev/resource/img/ide/newwizard.png +0 -0
- blurdev/resource/img/ide/open.png +0 -0
- blurdev/resource/img/ide/paste.png +0 -0
- blurdev/resource/img/ide/pdb_continue.png +0 -0
- blurdev/resource/img/ide/pdb_down.png +0 -0
- blurdev/resource/img/ide/pdb_next.png +0 -0
- blurdev/resource/img/ide/pdb_step.png +0 -0
- blurdev/resource/img/ide/pdb_up.png +0 -0
- blurdev/resource/img/ide/plus_minus.png +0 -0
- blurdev/resource/img/ide/preferences.png +0 -0
- blurdev/resource/img/ide/project_find.png +0 -0
- blurdev/resource/img/ide/python.png +0 -0
- blurdev/resource/img/ide/pyular.png +0 -0
- blurdev/resource/img/ide/qt.png +0 -0
- blurdev/resource/img/ide/quit.png +0 -0
- blurdev/resource/img/ide/redo.png +0 -0
- blurdev/resource/img/ide/refresh.png +0 -0
- blurdev/resource/img/ide/remove.png +0 -0
- blurdev/resource/img/ide/ruler.png +0 -0
- blurdev/resource/img/ide/run.png +0 -0
- blurdev/resource/img/ide/runall.png +0 -0
- blurdev/resource/img/ide/runallclear.png +0 -0
- blurdev/resource/img/ide/runselected.png +0 -0
- blurdev/resource/img/ide/runselectedclear.png +0 -0
- blurdev/resource/img/ide/save.png +0 -0
- blurdev/resource/img/ide/saveas.png +0 -0
- blurdev/resource/img/ide/sdk.png +0 -0
- blurdev/resource/img/ide/separator.png +0 -0
- blurdev/resource/img/ide/tabbed.png +0 -0
- blurdev/resource/img/ide/tile.png +0 -0
- blurdev/resource/img/ide/toolbar.png +0 -0
- blurdev/resource/img/ide/undo.png +0 -0
- blurdev/resource/img/ide/uppercase.png +0 -0
- blurdev/resource/img/ide/view_as.png +0 -0
- blurdev/resource/img/ide/windowed.png +0 -0
- blurdev/resource/img/ide.ico +0 -0
- blurdev/resource/img/ide.png +0 -0
- blurdev/resource/img/ide48.png +0 -0
- blurdev/resource/img/info.png +0 -0
- blurdev/resource/img/legacy tool.png +0 -0
- blurdev/resource/img/library.png +0 -0
- blurdev/resource/img/logger/about.png +0 -0
- blurdev/resource/img/logger/arrow_forward.png +0 -0
- blurdev/resource/img/logger/clear.png +0 -0
- blurdev/resource/img/logger/close.png +0 -0
- blurdev/resource/img/logger/debug_disabled.png +0 -0
- blurdev/resource/img/logger/debug_high.png +0 -0
- blurdev/resource/img/logger/debug_low.png +0 -0
- blurdev/resource/img/logger/debug_mid.png +0 -0
- blurdev/resource/img/logger/down.png +0 -0
- blurdev/resource/img/logger/find.png +0 -0
- blurdev/resource/img/logger/logging_critical.png +0 -0
- blurdev/resource/img/logger/logging_debug.png +0 -0
- blurdev/resource/img/logger/logging_error.png +0 -0
- blurdev/resource/img/logger/logging_info.png +0 -0
- blurdev/resource/img/logger/logging_not_set.png +0 -0
- blurdev/resource/img/logger/logging_warning.png +0 -0
- blurdev/resource/img/logger/next.png +0 -0
- blurdev/resource/img/logger/play.png +0 -0
- blurdev/resource/img/logger/playlist_play.png +0 -0
- blurdev/resource/img/logger/previous.png +0 -0
- blurdev/resource/img/logger/return.png +0 -0
- blurdev/resource/img/logger/save.png +0 -0
- blurdev/resource/img/logger/subdirectory_arrow_right.png +0 -0
- blurdev/resource/img/logger/up.png +0 -0
- blurdev/resource/img/lovebar.png +0 -0
- blurdev/resource/img/new.png +0 -0
- blurdev/resource/img/new_selected.png +0 -0
- blurdev/resource/img/node.png +0 -0
- blurdev/resource/img/ok.png +0 -0
- blurdev/resource/img/options.png +0 -0
- blurdev/resource/img/packages.png +0 -0
- blurdev/resource/img/preview/add.png +0 -0
- blurdev/resource/img/preview/brush.png +0 -0
- blurdev/resource/img/preview/delete.png +0 -0
- blurdev/resource/img/preview/delte.png +0 -0
- blurdev/resource/img/preview/fill.png +0 -0
- blurdev/resource/img/preview/layers.png +0 -0
- blurdev/resource/img/preview/media.png +0 -0
- blurdev/resource/img/preview/navigate.png +0 -0
- blurdev/resource/img/preview/pencil.png +0 -0
- blurdev/resource/img/preview/select.png +0 -0
- blurdev/resource/img/preview/type.png +0 -0
- blurdev/resource/img/preview/visible.png +0 -0
- blurdev/resource/img/project.png +0 -0
- blurdev/resource/img/python_logger.ico +0 -0
- blurdev/resource/img/python_logger.png +0 -0
- blurdev/resource/img/refresh.png +0 -0
- blurdev/resource/img/remove.png +0 -0
- blurdev/resource/img/reset.png +0 -0
- blurdev/resource/img/richtext/font_bold.png +0 -0
- blurdev/resource/img/richtext/font_italic.png +0 -0
- blurdev/resource/img/richtext/link_image.png +0 -0
- blurdev/resource/img/richtext/spell_check.png +0 -0
- blurdev/resource/img/richtext/unordered_list.png +0 -0
- blurdev/resource/img/save.png +0 -0
- blurdev/resource/img/savesettings.png +0 -0
- blurdev/resource/img/settings.png +0 -0
- blurdev/resource/img/tool.png +0 -0
- blurdev/resource/img/toolbarHandleHorizontal.png +0 -0
- blurdev/resource/img/toolbarHandleVertical.png +0 -0
- blurdev/resource/img/trash.png +0 -0
- blurdev/resource/img/trax.png +0 -0
- blurdev/resource/img/tree.png +0 -0
- blurdev/resource/img/treegrunt.ico +0 -0
- blurdev/resource/img/treegrunt.png +0 -0
- blurdev/resource/img/treegruntedit.png +0 -0
- blurdev/resource/img/user interface.png +0 -0
- blurdev/resource/img/warning.png +0 -0
- blurdev/resource/img/watermark.png +0 -0
- blurdev/resource/sdk/blurdev.sdk +0 -3
- blurdev/resource/settings.ini +0 -82
- blurdev/resource/softimage/BlurApplication.dll +0 -0
- blurdev/resource/softimage/BlurApplication64.dll +0 -0
- blurdev/resource/stylesheet/Carbon.css +0 -35
- blurdev/resource/stylesheet/logger/Dark.css +0 -62
- blurdev/resource/templ/py_comment.templ +0 -1
- blurdev/resource/templ/py_debug_raise_error.templ +0 -7
- blurdev/resource/templ/py_doc_string.templ +0 -10
- blurdev/resource/templ/py_header.templ +0 -9
- blurdev/resource/templ/py_line_comment.templ +0 -1
- blurdev/resource/templ/py_log_to_file.templ +0 -22
- blurdev/resource/templ/py_module_path.templ +0 -1
- blurdev/resource/templ/py_pyqt_core.templ +0 -1
- blurdev/resource/templ/py_pyqt_gui.templ +0 -1
- blurdev/resource/templ/py_splashscreen.templ +0 -6
- blurdev/resource/templ/py_testing_note.templ +0 -1
- blurdev/resource/templ/py_testing_note_end.templ +0 -1
- blurdev/resource/tools_environments.json +0 -72
- blurdev/resource/tools_environments.xml +0 -11
- blurdev/resource/tools_environments_linux.xml +0 -11
- blurdev/resource/tools_environments_offline.xml +0 -7
- blurdev/runtimes/__init__.py +0 -2
- blurdev/runtimes/logger.py +0 -44
- blurdev/scintilla/delayables/__init__.py +0 -9
- blurdev/settings.py +0 -312
- blurdev/utils/error.py +0 -389
- {blurdev/scintilla/lexers → preditor/cores}/__init__.py +0 -0
- {blurdev/utils → preditor/gui/fuzzy_search}/__init__.py +0 -0
- {blurdev → preditor}/resource/img/warning-big.png +0 -0
|
@@ -1,1417 +1,1205 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
from
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
from
|
|
45
|
-
|
|
46
|
-
from
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
self.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
self.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
self.uiConsoleTXT.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
self.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
self.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
self.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
self.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
self.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
self.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
self.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
self.
|
|
139
|
-
self.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
self.
|
|
143
|
-
self.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
self.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
self.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
self.
|
|
153
|
-
self.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
self.
|
|
157
|
-
|
|
158
|
-
self.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
self.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
self.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
self.
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
self.
|
|
186
|
-
|
|
187
|
-
self.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
)
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
self.
|
|
194
|
-
self.
|
|
195
|
-
self.
|
|
196
|
-
self.
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
self.
|
|
201
|
-
self.
|
|
202
|
-
self.
|
|
203
|
-
self.
|
|
204
|
-
self.
|
|
205
|
-
self.
|
|
206
|
-
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
self.
|
|
220
|
-
self.
|
|
221
|
-
self.
|
|
222
|
-
self.
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
self.
|
|
227
|
-
self.
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
self.
|
|
231
|
-
|
|
232
|
-
self.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
self.
|
|
241
|
-
self.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
self.
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
self.
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
for
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
)
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
"""
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
def
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
if
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
#
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
def
|
|
538
|
-
"""
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
def
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
workbox
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
self.
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
self.
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
self.
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
self.
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
)
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
"""
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
self.
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
""
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
self.
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
def
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
for
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
self.
|
|
1073
|
-
|
|
1074
|
-
def
|
|
1075
|
-
"""
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
if
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
def
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
if
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
def renameTab(self):
|
|
1207
|
-
if self._currentTab != -1:
|
|
1208
|
-
current = self.uiWorkboxTAB.tabBar().tabText(self._currentTab)
|
|
1209
|
-
msg = 'Rename the {} tab to:'.format(current)
|
|
1210
|
-
name, success = QInputDialog.getText(None, 'Rename Tab', msg, text=current)
|
|
1211
|
-
if success:
|
|
1212
|
-
self.uiWorkboxTAB.tabBar().setTabText(self._currentTab, name)
|
|
1213
|
-
|
|
1214
|
-
def linkCurrentTab(self):
|
|
1215
|
-
if self._currentTab != -1:
|
|
1216
|
-
# get the previous path
|
|
1217
|
-
pref = self.load_prefs()
|
|
1218
|
-
prevPath = pref.get('linkFolder', os.path.join(os.path.expanduser('~')))
|
|
1219
|
-
|
|
1220
|
-
# Handle the file dialog
|
|
1221
|
-
filters = "Python Files (*.py);;All Files (*.*)"
|
|
1222
|
-
path, _ = QtCompat.QFileDialog.getOpenFileName(
|
|
1223
|
-
self, "Link File", prevPath, filters
|
|
1224
|
-
)
|
|
1225
|
-
if not path:
|
|
1226
|
-
return
|
|
1227
|
-
|
|
1228
|
-
pref['linkFolder'] = os.path.dirname(path)
|
|
1229
|
-
self.save_prefs(pref)
|
|
1230
|
-
|
|
1231
|
-
self.linkTab(self._currentTab, path)
|
|
1232
|
-
|
|
1233
|
-
def linkTab(self, tabIdx, path):
|
|
1234
|
-
wid = self.uiWorkboxTAB.widget(tabIdx)
|
|
1235
|
-
tab = self.uiWorkboxTAB.tabBar()
|
|
1236
|
-
|
|
1237
|
-
wid.load(path)
|
|
1238
|
-
wid.setAutoReloadOnChange(True)
|
|
1239
|
-
tab.setTabText(tabIdx, os.path.basename(path))
|
|
1240
|
-
tab.setTabToolTip(tabIdx, path)
|
|
1241
|
-
iconprovider = QFileIconProvider()
|
|
1242
|
-
tab.setTabIcon(tabIdx, iconprovider.icon(QFileInfo(path)))
|
|
1243
|
-
|
|
1244
|
-
font = self.console().font()
|
|
1245
|
-
wid.setWorkboxFont(font)
|
|
1246
|
-
|
|
1247
|
-
def unlinkCurrentTab(self):
|
|
1248
|
-
if self._currentTab != -1:
|
|
1249
|
-
self.unlinkTab(self._currentTab)
|
|
1250
|
-
|
|
1251
|
-
def unlinkTab(self, tabIdx):
|
|
1252
|
-
wid = self.uiWorkboxTAB.currentWidget()
|
|
1253
|
-
tab = self.uiWorkboxTAB.tabBar()
|
|
1254
|
-
|
|
1255
|
-
wid.enableFileWatching(False)
|
|
1256
|
-
wid.setAutoReloadOnChange(False)
|
|
1257
|
-
tab.setTabToolTip(tabIdx, '')
|
|
1258
|
-
tab.setTabIcon(tabIdx, QIcon())
|
|
1259
|
-
|
|
1260
|
-
def linkedFileChanged(self, filename):
|
|
1261
|
-
font = self.console().font()
|
|
1262
|
-
for tabIndex in range(self.uiWorkboxTAB.count()):
|
|
1263
|
-
workbox = self.uiWorkboxTAB.widget(tabIndex)
|
|
1264
|
-
if workbox.filename() == filename:
|
|
1265
|
-
self._reloadRequested.add(tabIndex)
|
|
1266
|
-
self.uiWorkboxTAB.currentWidget().setFont(font)
|
|
1267
|
-
|
|
1268
|
-
newIdx = self.uiWorkboxTAB.currentIndex()
|
|
1269
|
-
self.updateLink(newIdx)
|
|
1270
|
-
|
|
1271
|
-
def currentChanged(self):
|
|
1272
|
-
newIdx = self.uiWorkboxTAB.currentIndex()
|
|
1273
|
-
self.updateLink(newIdx)
|
|
1274
|
-
|
|
1275
|
-
def updateLink(self, tabIdx):
|
|
1276
|
-
if tabIdx in self._reloadRequested:
|
|
1277
|
-
fn = self.uiWorkboxTAB.currentWidget().filename()
|
|
1278
|
-
if not os.path.isfile(fn):
|
|
1279
|
-
self.unlinkTab(tabIdx)
|
|
1280
|
-
else:
|
|
1281
|
-
# Only reload the current widget if requested
|
|
1282
|
-
time.sleep(0.1) # loading the file too quickly misses any changes
|
|
1283
|
-
self.uiWorkboxTAB.currentWidget().reloadChange()
|
|
1284
|
-
font = self.console().font()
|
|
1285
|
-
self.uiWorkboxTAB.currentWidget().setFont(font)
|
|
1286
|
-
self._reloadRequested.remove(tabIdx)
|
|
1287
|
-
|
|
1288
|
-
def openFileMonitor(self):
|
|
1289
|
-
return self._openFileMonitor
|
|
1290
|
-
|
|
1291
|
-
@staticmethod
|
|
1292
|
-
def instance(parent=None, runWorkbox=False, create=True):
|
|
1293
|
-
"""Returns the existing instance of the python logger creating it on first call.
|
|
1294
|
-
|
|
1295
|
-
Args:
|
|
1296
|
-
parent (QWidget, optional): If the instance hasn't been created yet, create
|
|
1297
|
-
it and parent it to this object.
|
|
1298
|
-
runWorkbox (bool, optional): If the instance hasn't been created yet, this
|
|
1299
|
-
will execute the active workbox's code once fully initialized.
|
|
1300
|
-
create (bool, optional): Returns None if the instance has not been created.
|
|
1301
|
-
|
|
1302
|
-
Returns:
|
|
1303
|
-
Returns a fully initialized instance of the Python Logger. If called more
|
|
1304
|
-
than once, the same instance will be returned. If create is False, it may
|
|
1305
|
-
return None.
|
|
1306
|
-
"""
|
|
1307
|
-
# create the instance for the logger
|
|
1308
|
-
if not LoggerWindow._instance:
|
|
1309
|
-
if not create:
|
|
1310
|
-
return None
|
|
1311
|
-
|
|
1312
|
-
# create the logger instance
|
|
1313
|
-
inst = LoggerWindow(parent, runWorkbox=runWorkbox)
|
|
1314
|
-
|
|
1315
|
-
# RV has a Unique window structure. It makes more sense to not parent a
|
|
1316
|
-
# singleton window than to parent it to a specific top level window.
|
|
1317
|
-
if blurdev.core.objectName() == 'rv':
|
|
1318
|
-
inst.setParent(None)
|
|
1319
|
-
inst.setAttribute(Qt.WA_QuitOnClose, False)
|
|
1320
|
-
|
|
1321
|
-
# protect the memory
|
|
1322
|
-
inst.setAttribute(Qt.WA_DeleteOnClose, False)
|
|
1323
|
-
|
|
1324
|
-
# cache the instance
|
|
1325
|
-
LoggerWindow._instance = inst
|
|
1326
|
-
|
|
1327
|
-
return LoggerWindow._instance
|
|
1328
|
-
|
|
1329
|
-
def installLogToFile(self):
|
|
1330
|
-
"""All stdout/stderr output is also appended to this file.
|
|
1331
|
-
|
|
1332
|
-
This uses blurdev.debug.logToFile(path, useOldStd=True).
|
|
1333
|
-
"""
|
|
1334
|
-
if self._logToFilePath is None:
|
|
1335
|
-
path = blurdev.osystem.defaultLogFile()
|
|
1336
|
-
path, _ = QtCompat.QFileDialog.getOpenFileName(
|
|
1337
|
-
self, "Log Output to File", path
|
|
1338
|
-
)
|
|
1339
|
-
if not path:
|
|
1340
|
-
return
|
|
1341
|
-
path = os.path.normpath(path)
|
|
1342
|
-
print('Output logged to: "{}"'.format(path))
|
|
1343
|
-
blurdev.debug.logToFile(path, useOldStd=True)
|
|
1344
|
-
# Store the std's so we can clear them later
|
|
1345
|
-
self._stds = (sys.stdout, sys.stderr)
|
|
1346
|
-
self.uiLogToFileACT.setText('Output Logged to File')
|
|
1347
|
-
self.uiLogToFileClearACT.setVisible(True)
|
|
1348
|
-
self._logToFilePath = path
|
|
1349
|
-
else:
|
|
1350
|
-
print('Output logged to: "{}"'.format(self._logToFilePath))
|
|
1351
|
-
|
|
1352
|
-
@classmethod
|
|
1353
|
-
def instanceSetPdbMode(cls, mode, msg=''):
|
|
1354
|
-
"""Sets the instance of LoggerWindow to pdb mode if the logger instance has
|
|
1355
|
-
been created.
|
|
1356
|
-
|
|
1357
|
-
Args:
|
|
1358
|
-
mode (bool): The mode to set it to
|
|
1359
|
-
"""
|
|
1360
|
-
if cls._instance:
|
|
1361
|
-
inst = cls._instance
|
|
1362
|
-
if inst.uiConsoleTXT.pdbMode() != mode:
|
|
1363
|
-
inst.uiConsoleTXT.setPdbMode(mode)
|
|
1364
|
-
import blurdev.external
|
|
1365
|
-
|
|
1366
|
-
blurdev.external.External(
|
|
1367
|
-
['pdb', '', {'msg': 'blurdev.debug.getPdb().currentLine()'}]
|
|
1368
|
-
)
|
|
1369
|
-
# Pdb returns its prompt automatically. If we detect the pdb prompt and
|
|
1370
|
-
# _pdbContinue is set re-run the command until it's count reaches zero.
|
|
1371
|
-
if inst._pdbContinue and msg == '(Pdb) ':
|
|
1372
|
-
if inst._pdbContinue[0]:
|
|
1373
|
-
count = inst._pdbContinue[0] - 1
|
|
1374
|
-
if count > 0:
|
|
1375
|
-
# Decrease the count.
|
|
1376
|
-
inst._pdbContinue = (count, inst._pdbContinue[1])
|
|
1377
|
-
# Resend the requested message
|
|
1378
|
-
inst.uiConsoleTXT.pdbSendCommand(inst._pdbContinue[1])
|
|
1379
|
-
else:
|
|
1380
|
-
# We are done refreshing so nothing to do.
|
|
1381
|
-
inst._pdbContinue = None
|
|
1382
|
-
|
|
1383
|
-
@classmethod
|
|
1384
|
-
def instancePdbResult(cls, data):
|
|
1385
|
-
if cls._instance:
|
|
1386
|
-
if data.get('msg') == 'pdb_currentLine':
|
|
1387
|
-
filename = data.get('filename')
|
|
1388
|
-
lineNo = data.get('lineNo')
|
|
1389
|
-
doc = cls._instance.uiPdbTAB.currentWidget()
|
|
1390
|
-
if not isinstance(doc, WorkboxWidget):
|
|
1391
|
-
doc = cls._instance.addWorkbox(
|
|
1392
|
-
cls._instance.uiPdbTAB, closable=False
|
|
1393
|
-
)
|
|
1394
|
-
cls._instance._pdb_marker = doc.markerDefine(doc.Circle)
|
|
1395
|
-
cls._instance.uiPdbTAB.setTabText(
|
|
1396
|
-
cls._instance.uiPdbTAB.currentIndex(), filename
|
|
1397
|
-
)
|
|
1398
|
-
doc.markerDeleteAll(cls._instance._pdb_marker)
|
|
1399
|
-
if os.path.exists(filename):
|
|
1400
|
-
doc.load(filename)
|
|
1401
|
-
doc.goToLine(lineNo)
|
|
1402
|
-
doc.markerAdd(lineNo, cls._instance._pdb_marker)
|
|
1403
|
-
else:
|
|
1404
|
-
doc.clear()
|
|
1405
|
-
doc._filename = ''
|
|
1406
|
-
|
|
1407
|
-
@classmethod
|
|
1408
|
-
def instanceShutdown(cls):
|
|
1409
|
-
"""Faster way to shutdown the instance of LoggerWindow if it possibly was not used.
|
|
1410
|
-
|
|
1411
|
-
Returns:
|
|
1412
|
-
bool: If a shutdown was required
|
|
1413
|
-
"""
|
|
1414
|
-
if cls._instance:
|
|
1415
|
-
cls._instance.shutdown()
|
|
1416
|
-
return True
|
|
1417
|
-
return False
|
|
1
|
+
from __future__ import absolute_import, print_function
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
import warnings
|
|
9
|
+
from builtins import bytes
|
|
10
|
+
from datetime import datetime, timedelta
|
|
11
|
+
from functools import partial
|
|
12
|
+
|
|
13
|
+
import __main__
|
|
14
|
+
import six
|
|
15
|
+
from Qt import QtCompat, QtCore, QtWidgets
|
|
16
|
+
from Qt.QtCore import QByteArray, Qt, QTimer, Signal, Slot
|
|
17
|
+
from Qt.QtGui import QCursor, QFont, QFontDatabase, QIcon, QTextCursor
|
|
18
|
+
from Qt.QtWidgets import (
|
|
19
|
+
QApplication,
|
|
20
|
+
QInputDialog,
|
|
21
|
+
QLabel,
|
|
22
|
+
QMessageBox,
|
|
23
|
+
QTextBrowser,
|
|
24
|
+
QToolTip,
|
|
25
|
+
QVBoxLayout,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from .. import (
|
|
29
|
+
DEFAULT_CORE_NAME,
|
|
30
|
+
about_preditor,
|
|
31
|
+
core,
|
|
32
|
+
debug,
|
|
33
|
+
osystem,
|
|
34
|
+
plugins,
|
|
35
|
+
prefs,
|
|
36
|
+
resourcePath,
|
|
37
|
+
)
|
|
38
|
+
from ..delayable_engine import DelayableEngine
|
|
39
|
+
from ..gui import Dialog, Window, loadUi
|
|
40
|
+
from ..gui.fuzzy_search.fuzzy_search import FuzzySearch
|
|
41
|
+
from ..gui.group_tab_widget.grouped_tab_models import GroupTabListItemModel
|
|
42
|
+
from ..logging_config import LoggingConfig
|
|
43
|
+
from ..utils import stylesheets
|
|
44
|
+
from .completer import CompleterMode
|
|
45
|
+
from .level_buttons import LoggingLevelButton
|
|
46
|
+
from .set_text_editor_path_dialog import SetTextEditorPathDialog
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class WorkboxPages:
|
|
50
|
+
"""Nice names for the uiWorkboxSTACK indexes."""
|
|
51
|
+
|
|
52
|
+
Options = 0
|
|
53
|
+
Workboxes = 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class LoggerWindow(Window):
|
|
57
|
+
_instance = None
|
|
58
|
+
styleSheetChanged = Signal(str)
|
|
59
|
+
|
|
60
|
+
def __init__(self, parent, name=None, run_workbox=False, standalone=False):
|
|
61
|
+
super(LoggerWindow, self).__init__(parent=parent)
|
|
62
|
+
self.name = name if name else DEFAULT_CORE_NAME
|
|
63
|
+
self.aboutToClearPathsEnabled = False
|
|
64
|
+
self._stylesheet = 'Bright'
|
|
65
|
+
|
|
66
|
+
# Create timer to autohide status messages
|
|
67
|
+
self.statusTimer = QTimer()
|
|
68
|
+
self.statusTimer.setSingleShot(True)
|
|
69
|
+
|
|
70
|
+
# Store the previous time a font-resize wheel event was triggered to prevent
|
|
71
|
+
# rapid-fire WheelEvents. Initialize to the current time.
|
|
72
|
+
self.previousFontResizeTime = datetime.now()
|
|
73
|
+
|
|
74
|
+
self.setWindowIcon(QIcon(resourcePath('img/preditor.png')))
|
|
75
|
+
loadUi(__file__, self)
|
|
76
|
+
|
|
77
|
+
self.uiConsoleTXT.flash_window = self
|
|
78
|
+
self.uiConsoleTXT.reportExecutionTime = self.reportExecutionTime
|
|
79
|
+
self.uiClearToLastPromptACT.triggered.connect(
|
|
80
|
+
self.uiConsoleTXT.clearToLastPrompt
|
|
81
|
+
)
|
|
82
|
+
# If we don't disable this shortcut Qt won't respond to this classes or
|
|
83
|
+
# the ConsolePrEdit's
|
|
84
|
+
self.uiConsoleTXT.uiClearToLastPromptACT.setShortcut('')
|
|
85
|
+
|
|
86
|
+
# create the status reporting label
|
|
87
|
+
self.uiStatusLBL = QLabel(self)
|
|
88
|
+
self.uiMenuBar.setCornerWidget(self.uiStatusLBL)
|
|
89
|
+
|
|
90
|
+
# create the workbox tabs
|
|
91
|
+
self._currentTab = -1
|
|
92
|
+
self._reloadRequested = set()
|
|
93
|
+
# Setup delayable system
|
|
94
|
+
self.delayable_engine = DelayableEngine.instance('logger', self)
|
|
95
|
+
|
|
96
|
+
self.uiWorkboxTAB.editor_kwargs = dict(
|
|
97
|
+
console=self.uiConsoleTXT, delayable_engine=self.delayable_engine.name
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Create additional buttons in toolbar.
|
|
101
|
+
self.uiLoggingLevelBTN = LoggingLevelButton(self)
|
|
102
|
+
self.uiConsoleTOOLBAR.insertWidget(
|
|
103
|
+
self.uiRunSelectedACT,
|
|
104
|
+
self.uiLoggingLevelBTN,
|
|
105
|
+
)
|
|
106
|
+
self.uiConsoleTOOLBAR.insertSeparator(self.uiRunSelectedACT)
|
|
107
|
+
|
|
108
|
+
# Initial configuration of the logToFile feature
|
|
109
|
+
self._logToFilePath = None
|
|
110
|
+
self._stds = None
|
|
111
|
+
self.uiLogToFileClearACT.setVisible(False)
|
|
112
|
+
|
|
113
|
+
self.uiRestartACT.triggered.connect(self.restartLogger)
|
|
114
|
+
self.uiCloseLoggerACT.triggered.connect(self.closeLogger)
|
|
115
|
+
|
|
116
|
+
self.uiRunAllACT.triggered.connect(self.execAll)
|
|
117
|
+
self.uiRunSelectedACT.triggered.connect(self.execSelected)
|
|
118
|
+
|
|
119
|
+
self.uiAutoCompleteEnabledACT.toggled.connect(self.setAutoCompleteEnabled)
|
|
120
|
+
|
|
121
|
+
self.uiAutoCompleteCaseSensitiveACT.toggled.connect(self.setCaseSensitive)
|
|
122
|
+
|
|
123
|
+
# Setup ability to cycle completer mode, and create action for each mode
|
|
124
|
+
self.completerModeCycle = itertools.cycle(CompleterMode)
|
|
125
|
+
# create CompleterMode submenu
|
|
126
|
+
defaultMode = next(self.completerModeCycle)
|
|
127
|
+
for mode in CompleterMode:
|
|
128
|
+
modeName = mode.displayName()
|
|
129
|
+
action = self.uiCompleterModeMENU.addAction(modeName)
|
|
130
|
+
action.setObjectName('ui{}ModeACT'.format(modeName))
|
|
131
|
+
action.setData(mode)
|
|
132
|
+
action.setCheckable(True)
|
|
133
|
+
action.setChecked(mode == defaultMode)
|
|
134
|
+
completerMode = CompleterMode(mode)
|
|
135
|
+
action.setToolTip(completerMode.toolTip())
|
|
136
|
+
action.triggered.connect(partial(self.selectCompleterMode, action))
|
|
137
|
+
|
|
138
|
+
self.uiCompleterModeMENU.addSeparator()
|
|
139
|
+
action = self.uiCompleterModeMENU.addAction('Cycle mode')
|
|
140
|
+
action.setObjectName('uiCycleModeACT')
|
|
141
|
+
action.setShortcut(Qt.CTRL | Qt.Key_M)
|
|
142
|
+
action.triggered.connect(self.cycleCompleterMode)
|
|
143
|
+
self.uiCompleterModeMENU.hovered.connect(self.handleMenuHovered)
|
|
144
|
+
|
|
145
|
+
# Workbox add/remove
|
|
146
|
+
self.uiNewWorkboxACT.triggered.connect(
|
|
147
|
+
lambda: self.uiWorkboxTAB.add_new_tab(group=True)
|
|
148
|
+
)
|
|
149
|
+
self.uiCloseWorkboxACT.triggered.connect(self.uiWorkboxTAB.close_current_tab)
|
|
150
|
+
|
|
151
|
+
# Browse previous commands
|
|
152
|
+
self.uiGetPrevCmdACT.triggered.connect(self.getPrevCommand)
|
|
153
|
+
self.uiGetNextCmdACT.triggered.connect(self.getNextCommand)
|
|
154
|
+
|
|
155
|
+
# Focus to console or to workbox, optionally copy seleciton or line
|
|
156
|
+
self.uiFocusToConsoleACT.triggered.connect(self.focusToConsole)
|
|
157
|
+
self.uiCopyToConsoleACT.triggered.connect(self.copyToConsole)
|
|
158
|
+
self.uiFocusToWorkboxACT.triggered.connect(self.focusToWorkbox)
|
|
159
|
+
self.uiCopyToWorkboxACT.triggered.connect(self.copyToWorkbox)
|
|
160
|
+
|
|
161
|
+
# Navigate workbox tabs
|
|
162
|
+
self.uiNextTabACT.triggered.connect(self.nextTab)
|
|
163
|
+
self.uiPrevTabACT.triggered.connect(self.prevTab)
|
|
164
|
+
|
|
165
|
+
self.uiTab1ACT.triggered.connect(partial(self.gotoTabByIndex, 1))
|
|
166
|
+
self.uiTab2ACT.triggered.connect(partial(self.gotoTabByIndex, 2))
|
|
167
|
+
self.uiTab3ACT.triggered.connect(partial(self.gotoTabByIndex, 3))
|
|
168
|
+
self.uiTab4ACT.triggered.connect(partial(self.gotoTabByIndex, 4))
|
|
169
|
+
self.uiTab5ACT.triggered.connect(partial(self.gotoTabByIndex, 5))
|
|
170
|
+
self.uiTab6ACT.triggered.connect(partial(self.gotoTabByIndex, 6))
|
|
171
|
+
self.uiTab7ACT.triggered.connect(partial(self.gotoTabByIndex, 7))
|
|
172
|
+
self.uiTab8ACT.triggered.connect(partial(self.gotoTabByIndex, 8))
|
|
173
|
+
self.uiTabLastACT.triggered.connect(partial(self.gotoTabByIndex, -1))
|
|
174
|
+
|
|
175
|
+
self.uiGroup1ACT.triggered.connect(partial(self.gotoGroupByIndex, 1))
|
|
176
|
+
self.uiGroup2ACT.triggered.connect(partial(self.gotoGroupByIndex, 2))
|
|
177
|
+
self.uiGroup3ACT.triggered.connect(partial(self.gotoGroupByIndex, 3))
|
|
178
|
+
self.uiGroup4ACT.triggered.connect(partial(self.gotoGroupByIndex, 4))
|
|
179
|
+
self.uiGroup5ACT.triggered.connect(partial(self.gotoGroupByIndex, 5))
|
|
180
|
+
self.uiGroup6ACT.triggered.connect(partial(self.gotoGroupByIndex, 6))
|
|
181
|
+
self.uiGroup7ACT.triggered.connect(partial(self.gotoGroupByIndex, 7))
|
|
182
|
+
self.uiGroup8ACT.triggered.connect(partial(self.gotoGroupByIndex, 8))
|
|
183
|
+
self.uiGroupLastACT.triggered.connect(partial(self.gotoGroupByIndex, -1))
|
|
184
|
+
|
|
185
|
+
self.uiFocusNameACT.triggered.connect(self.show_focus_name)
|
|
186
|
+
|
|
187
|
+
self.uiCommentToggleACT.triggered.connect(self.comment_toggle)
|
|
188
|
+
|
|
189
|
+
self.uiSpellCheckEnabledACT.toggled.connect(self.setSpellCheckEnabled)
|
|
190
|
+
self.uiIndentationsTabsACT.toggled.connect(self.updateIndentationsUseTabs)
|
|
191
|
+
self.uiCopyTabsToSpacesACT.toggled.connect(self.updateCopyIndentsAsSpaces)
|
|
192
|
+
self.uiWordWrapACT.toggled.connect(self.setWordWrap)
|
|
193
|
+
self.uiResetWarningFiltersACT.triggered.connect(warnings.resetwarnings)
|
|
194
|
+
self.uiLogToFileACT.triggered.connect(self.installLogToFile)
|
|
195
|
+
self.uiLogToFileClearACT.triggered.connect(self.clearLogToFile)
|
|
196
|
+
self.uiClearLogACT.triggered.connect(self.clearLog)
|
|
197
|
+
self.uiSaveConsoleSettingsACT.triggered.connect(
|
|
198
|
+
lambda: self.recordPrefs(manual=True)
|
|
199
|
+
)
|
|
200
|
+
self.uiClearBeforeRunningACT.triggered.connect(self.setClearBeforeRunning)
|
|
201
|
+
self.uiEditorVerticalACT.toggled.connect(self.adjustWorkboxOrientation)
|
|
202
|
+
self.uiEnvironmentVarsACT.triggered.connect(self.showEnvironmentVars)
|
|
203
|
+
self.uiBackupPreferencesACT.triggered.connect(self.backupPreferences)
|
|
204
|
+
self.uiBrowsePreferencesACT.triggered.connect(self.browsePreferences)
|
|
205
|
+
self.uiAboutPreditorACT.triggered.connect(self.show_about)
|
|
206
|
+
core.aboutToClearPaths.connect(self.pathsAboutToBeCleared)
|
|
207
|
+
self.uiSetFlashWindowIntervalACT.triggered.connect(self.setFlashWindowInterval)
|
|
208
|
+
|
|
209
|
+
self.uiSetPreferredTextEditorPathACT.triggered.connect(
|
|
210
|
+
self.openSetPreferredTextEditorDialog
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Tooltips - Qt4 doesn't have a ToolTipsVisible method, so we fake it
|
|
214
|
+
regEx = ".*"
|
|
215
|
+
menus = self.findChildren(QtWidgets.QMenu, QtCore.QRegExp(regEx))
|
|
216
|
+
for menu in menus:
|
|
217
|
+
menu.hovered.connect(self.handleMenuHovered)
|
|
218
|
+
|
|
219
|
+
self.uiClearLogACT.setIcon(QIcon(resourcePath('img/close-thick.png')))
|
|
220
|
+
self.uiNewWorkboxACT.setIcon(QIcon(resourcePath('img/file-plus.png')))
|
|
221
|
+
self.uiCloseWorkboxACT.setIcon(QIcon(resourcePath('img/file-remove.png')))
|
|
222
|
+
self.uiSaveConsoleSettingsACT.setIcon(
|
|
223
|
+
QIcon(resourcePath('img/content-save.png'))
|
|
224
|
+
)
|
|
225
|
+
self.uiAboutPreditorACT.setIcon(QIcon(resourcePath('img/information.png')))
|
|
226
|
+
self.uiRestartACT.setIcon(QIcon(resourcePath('img/restart.svg')))
|
|
227
|
+
self.uiCloseLoggerACT.setIcon(QIcon(resourcePath('img/close-thick.png')))
|
|
228
|
+
|
|
229
|
+
# Make action shortcuts available anywhere in the Logger
|
|
230
|
+
self.addAction(self.uiClearLogACT)
|
|
231
|
+
|
|
232
|
+
self.restorePrefs()
|
|
233
|
+
|
|
234
|
+
# add font menu list
|
|
235
|
+
curFamily = self.console().font().family()
|
|
236
|
+
fontDB = QFontDatabase()
|
|
237
|
+
fontFamilies = fontDB.families(QFontDatabase.Latin)
|
|
238
|
+
monospaceFonts = [fam for fam in fontFamilies if fontDB.isFixedPitch(fam)]
|
|
239
|
+
|
|
240
|
+
self.uiMonospaceFontMENU.clear()
|
|
241
|
+
self.uiProportionalFontMENU.clear()
|
|
242
|
+
|
|
243
|
+
for family in fontFamilies:
|
|
244
|
+
if family in monospaceFonts:
|
|
245
|
+
action = self.uiMonospaceFontMENU.addAction(family)
|
|
246
|
+
else:
|
|
247
|
+
action = self.uiProportionalFontMENU.addAction(family)
|
|
248
|
+
action.setObjectName(u'ui{}FontACT'.format(family))
|
|
249
|
+
action.setCheckable(True)
|
|
250
|
+
action.setChecked(family == curFamily)
|
|
251
|
+
action.triggered.connect(partial(self.selectFont, action))
|
|
252
|
+
|
|
253
|
+
# add stylesheet menu options.
|
|
254
|
+
for style_name in stylesheets.stylesheets():
|
|
255
|
+
action = self.uiStyleMENU.addAction(style_name)
|
|
256
|
+
action.setObjectName('ui{}ACT'.format(style_name))
|
|
257
|
+
action.setCheckable(True)
|
|
258
|
+
action.setChecked(self._stylesheet == style_name)
|
|
259
|
+
action.triggered.connect(partial(self.setStyleSheet, style_name))
|
|
260
|
+
|
|
261
|
+
self.uiConsoleTOOLBAR.show()
|
|
262
|
+
loggerName = QApplication.instance().translate(
|
|
263
|
+
'PrEditorWindow', DEFAULT_CORE_NAME
|
|
264
|
+
)
|
|
265
|
+
self.setWindowTitle(
|
|
266
|
+
'{} - {} - {} {}-bit'.format(
|
|
267
|
+
loggerName,
|
|
268
|
+
self.name,
|
|
269
|
+
'{}.{}.{}'.format(*sys.version_info[:3]),
|
|
270
|
+
osystem.getPointerSize(),
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
self.setup_run_workbox()
|
|
275
|
+
|
|
276
|
+
if not standalone:
|
|
277
|
+
# This action only is valid when running in standalone mode
|
|
278
|
+
self.uiRestartACT.setVisible(False)
|
|
279
|
+
|
|
280
|
+
# Run the current workbox after the LoggerWindow is shown.
|
|
281
|
+
if run_workbox:
|
|
282
|
+
# By using two singleShot timers, we can show and draw the LoggerWindow,
|
|
283
|
+
# then call execAll. This makes it easier to see what code you are running
|
|
284
|
+
# before it has finished running completely.
|
|
285
|
+
# QTimer.singleShot(0, lambda: QTimer.singleShot(0, self.execAll))
|
|
286
|
+
QTimer.singleShot(
|
|
287
|
+
0, lambda: QTimer.singleShot(0, lambda: self.run_workbox(run_workbox))
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
@Slot()
|
|
291
|
+
def apply_options(self):
|
|
292
|
+
"""Apply editor options the user chose on the WorkboxPage.Options page."""
|
|
293
|
+
editor_cls_name, editor_cls = plugins.editor(
|
|
294
|
+
self.uiEditorChooserWGT.editor_name()
|
|
295
|
+
)
|
|
296
|
+
if editor_cls_name is None:
|
|
297
|
+
return
|
|
298
|
+
if editor_cls_name != self.editor_cls_name:
|
|
299
|
+
self.editor_cls_name = editor_cls_name
|
|
300
|
+
self.uiWorkboxTAB.editor_cls = editor_cls
|
|
301
|
+
# We need to change the editor, save all prefs
|
|
302
|
+
self.recordPrefs()
|
|
303
|
+
# Clear the uiWorkboxTAB
|
|
304
|
+
self.uiWorkboxTAB.clear()
|
|
305
|
+
# Restore prefs to populate the tabs
|
|
306
|
+
self.restorePrefs()
|
|
307
|
+
|
|
308
|
+
self.update_workbox_stack()
|
|
309
|
+
|
|
310
|
+
def comment_toggle(self):
|
|
311
|
+
self.current_workbox().__comment_toggle__()
|
|
312
|
+
|
|
313
|
+
def current_workbox(self):
|
|
314
|
+
"""Returns the current workbox for the current tab group."""
|
|
315
|
+
return self.uiWorkboxTAB.current_groups_widget()
|
|
316
|
+
|
|
317
|
+
@classmethod
|
|
318
|
+
def name_for_workbox(cls, workbox):
|
|
319
|
+
"""Returns the name for a given workbox.
|
|
320
|
+
The name is the group tab text and the workbox tab text joined by a `/`"""
|
|
321
|
+
ret = []
|
|
322
|
+
logger = cls.instance()
|
|
323
|
+
index = logger.uiWorkboxTAB.currentIndex()
|
|
324
|
+
ret.append(logger.uiWorkboxTAB.tabText(index))
|
|
325
|
+
group_widget = logger.uiWorkboxTAB.currentWidget()
|
|
326
|
+
index = group_widget.currentIndex()
|
|
327
|
+
ret.append(group_widget.tabText(index))
|
|
328
|
+
return "/".join(ret)
|
|
329
|
+
|
|
330
|
+
@classmethod
|
|
331
|
+
def workbox_for_name(cls, name, show=False, visible=False):
|
|
332
|
+
"""Used to find a workbox for a given name. It accepts a string matching
|
|
333
|
+
the "{group}/{workbox}" format, or if True, the current workbox.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
name(str, boolean): Used to define which workbox to run.
|
|
337
|
+
show (bool, optional): If a workbox is found, call `__show__` on it
|
|
338
|
+
to ensure that it is initialized and its text is loaded.
|
|
339
|
+
visible (bool, optional): Make the this workbox visible if found.
|
|
340
|
+
"""
|
|
341
|
+
logger = cls.instance()
|
|
342
|
+
|
|
343
|
+
workbox = None
|
|
344
|
+
|
|
345
|
+
# If name is True, run the current workbox
|
|
346
|
+
if isinstance(name, bool):
|
|
347
|
+
if name:
|
|
348
|
+
workbox = logger.current_workbox()
|
|
349
|
+
|
|
350
|
+
# If name is a string, find first tab with that name
|
|
351
|
+
elif isinstance(name, six.string_types):
|
|
352
|
+
split = name.split('/', 1)
|
|
353
|
+
if len(split) < 2:
|
|
354
|
+
return None
|
|
355
|
+
group, editor = split
|
|
356
|
+
group_index = logger.uiWorkboxTAB.index_for_text(group)
|
|
357
|
+
if group_index != -1:
|
|
358
|
+
tab_widget = logger.uiWorkboxTAB.widget(group_index)
|
|
359
|
+
index = tab_widget.index_for_text(editor)
|
|
360
|
+
if index != -1:
|
|
361
|
+
workbox = tab_widget.widget(index)
|
|
362
|
+
if visible:
|
|
363
|
+
tab_widget.setCurrentIndex(index)
|
|
364
|
+
logger.uiWorkboxTAB.setCurrentIndex(group_index)
|
|
365
|
+
|
|
366
|
+
if show and workbox:
|
|
367
|
+
workbox.__show__()
|
|
368
|
+
|
|
369
|
+
return workbox
|
|
370
|
+
|
|
371
|
+
@classmethod
|
|
372
|
+
def run_workbox(cls, name):
|
|
373
|
+
"""This is a function which will be added to __main__, and therefore
|
|
374
|
+
available to PythonLogger users. It will accept a string matching the
|
|
375
|
+
"{group}/{workbox}" format, or a boolean that will run the current tab
|
|
376
|
+
to support the command line launching functionality which auto-runs the
|
|
377
|
+
current workbox on launch.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
name(str, boolean): Used to define which workbox to run.
|
|
381
|
+
|
|
382
|
+
Raises:
|
|
383
|
+
Exception: "Cannot call current workbox."
|
|
384
|
+
|
|
385
|
+
Example Usages:
|
|
386
|
+
run_workbox('group_a/test')
|
|
387
|
+
run_workbox('some/stuff.py')
|
|
388
|
+
(from command line): blurdev launch Python_Logger --run_workbox
|
|
389
|
+
"""
|
|
390
|
+
workbox = cls.workbox_for_name(name)
|
|
391
|
+
|
|
392
|
+
if workbox is not None:
|
|
393
|
+
# if name is True, its ok to run the workbox, this option
|
|
394
|
+
# is passed by the cli to run the current tab
|
|
395
|
+
if workbox.hasFocus() and name is not True:
|
|
396
|
+
raise Exception("Cannot call current workbox.")
|
|
397
|
+
else:
|
|
398
|
+
# Make sure the workbox text is loaded as it likely has not
|
|
399
|
+
# been shown yet and each tab is now loaded only on demand.
|
|
400
|
+
workbox.__show__()
|
|
401
|
+
workbox.__exec_all__()
|
|
402
|
+
|
|
403
|
+
def setup_run_workbox(self):
|
|
404
|
+
"""We will bind the runWordbox function on __main__, which makes is available to
|
|
405
|
+
code running within PythonLogger.
|
|
406
|
+
"""
|
|
407
|
+
__main__.run_workbox = self.run_workbox
|
|
408
|
+
|
|
409
|
+
def openSetPreferredTextEditorDialog(self):
|
|
410
|
+
dlg = SetTextEditorPathDialog(parent=self)
|
|
411
|
+
dlg.exec_()
|
|
412
|
+
|
|
413
|
+
def focusToConsole(self):
|
|
414
|
+
"""Move focus to the console"""
|
|
415
|
+
self.console().setFocus()
|
|
416
|
+
|
|
417
|
+
def focusToWorkbox(self):
|
|
418
|
+
"""Move focus to the current workbox"""
|
|
419
|
+
self.current_workbox().setFocus()
|
|
420
|
+
|
|
421
|
+
def copyToConsole(self):
|
|
422
|
+
"""Copy current selection or line from workbox to console"""
|
|
423
|
+
workbox = self.current_workbox()
|
|
424
|
+
if not workbox.hasFocus():
|
|
425
|
+
return
|
|
426
|
+
|
|
427
|
+
text = workbox.__selected_text__()
|
|
428
|
+
if not text:
|
|
429
|
+
line, index = workbox.__cursor_position__()
|
|
430
|
+
text = workbox.__text__(line)
|
|
431
|
+
text = text.rstrip('\r\n')
|
|
432
|
+
if not text:
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
cursor = self.console().textCursor()
|
|
436
|
+
if cursor.hasSelection():
|
|
437
|
+
cursor.removeSelectedText()
|
|
438
|
+
|
|
439
|
+
self.console().insertPlainText(text)
|
|
440
|
+
self.focusToConsole()
|
|
441
|
+
|
|
442
|
+
def copyToWorkbox(self):
|
|
443
|
+
"""Copy current selection or line from console to workbox"""
|
|
444
|
+
console = self.console()
|
|
445
|
+
if not console.hasFocus():
|
|
446
|
+
return
|
|
447
|
+
|
|
448
|
+
cursor = console.textCursor()
|
|
449
|
+
if not cursor.hasSelection():
|
|
450
|
+
cursor.select(QTextCursor.LineUnderCursor)
|
|
451
|
+
text = cursor.selectedText()
|
|
452
|
+
prompt = console.prompt()
|
|
453
|
+
if text.startswith(prompt):
|
|
454
|
+
text = text[len(prompt) :]
|
|
455
|
+
text = text.lstrip()
|
|
456
|
+
|
|
457
|
+
outputPrompt = console.outputPrompt()
|
|
458
|
+
outputPrompt = outputPrompt.rstrip()
|
|
459
|
+
if text.startswith(outputPrompt):
|
|
460
|
+
text = text[len(outputPrompt) :]
|
|
461
|
+
text = text.lstrip()
|
|
462
|
+
|
|
463
|
+
if not text:
|
|
464
|
+
return
|
|
465
|
+
|
|
466
|
+
workbox = self.current_workbox()
|
|
467
|
+
workbox.__remove_selected_text__()
|
|
468
|
+
workbox.__insert_text__(text)
|
|
469
|
+
|
|
470
|
+
line, index = workbox.__cursor_position__()
|
|
471
|
+
index += len(text)
|
|
472
|
+
workbox.__set_cursor_position__(line, index)
|
|
473
|
+
|
|
474
|
+
self.focusToWorkbox()
|
|
475
|
+
|
|
476
|
+
def getNextCommand(self):
|
|
477
|
+
if hasattr(self.console(), 'getNextCommand'):
|
|
478
|
+
self.console().getNextCommand()
|
|
479
|
+
|
|
480
|
+
def getPrevCommand(self):
|
|
481
|
+
if hasattr(self.console(), 'getPrevCommand'):
|
|
482
|
+
self.console().getPrevCommand()
|
|
483
|
+
|
|
484
|
+
def wheelEvent(self, event):
|
|
485
|
+
"""adjust font size on ctrl+scrollWheel"""
|
|
486
|
+
if event.modifiers() == Qt.ControlModifier:
|
|
487
|
+
# WheelEvents can be emitted in a cluster, but we only want one at a time
|
|
488
|
+
# (ie to change font size by 1, rather than 2 or 3). Let's bail if previous
|
|
489
|
+
# font-resize wheel event was within a certain threshhold.
|
|
490
|
+
now = datetime.now()
|
|
491
|
+
elapsed = now - self.previousFontResizeTime
|
|
492
|
+
tolerance = timedelta(microseconds=100000)
|
|
493
|
+
if elapsed < tolerance:
|
|
494
|
+
return
|
|
495
|
+
self.previousFontResizeTime = now
|
|
496
|
+
|
|
497
|
+
# QT4 presents QWheelEvent.delta(), QT5 has QWheelEvent.angleDelta().y()
|
|
498
|
+
if hasattr(event, 'delta'): # Qt4
|
|
499
|
+
delta = event.delta()
|
|
500
|
+
else: # QT5
|
|
501
|
+
delta = event.angleDelta().y()
|
|
502
|
+
|
|
503
|
+
# convert delta to +1 or -1, depending
|
|
504
|
+
delta = delta / abs(delta)
|
|
505
|
+
minSize = 5
|
|
506
|
+
maxSize = 50
|
|
507
|
+
font = self.console().font()
|
|
508
|
+
newSize = font.pointSize() + delta
|
|
509
|
+
newSize = max(min(newSize, maxSize), minSize)
|
|
510
|
+
|
|
511
|
+
font.setPointSize(newSize)
|
|
512
|
+
self.console().setConsoleFont(font)
|
|
513
|
+
|
|
514
|
+
for workbox in self.uiWorkboxTAB.all_widgets():
|
|
515
|
+
marginsFont = workbox.__margins_font__()
|
|
516
|
+
marginsFont.setPointSize(newSize)
|
|
517
|
+
workbox.__set_margins_font__(marginsFont)
|
|
518
|
+
|
|
519
|
+
workbox.__set_font__(font)
|
|
520
|
+
else:
|
|
521
|
+
Window.wheelEvent(self, event)
|
|
522
|
+
|
|
523
|
+
def handleMenuHovered(self, action):
|
|
524
|
+
"""Qt4 doesn't have a ToolTipsVisible method, so we fake it"""
|
|
525
|
+
# Don't show if it's just the text of the action
|
|
526
|
+
text = re.sub(r"(?<!&)&(?!&)", "", action.text())
|
|
527
|
+
text = text.replace('...', '')
|
|
528
|
+
|
|
529
|
+
if text == action.toolTip():
|
|
530
|
+
text = ''
|
|
531
|
+
else:
|
|
532
|
+
text = action.toolTip()
|
|
533
|
+
|
|
534
|
+
menu = action.parentWidget()
|
|
535
|
+
QToolTip.showText(QCursor.pos(), text, menu)
|
|
536
|
+
|
|
537
|
+
def findCurrentFontAction(self):
|
|
538
|
+
"""Find and return current font's action"""
|
|
539
|
+
actions = self.uiMonospaceFontMENU.actions()
|
|
540
|
+
actions.extend(self.uiProportionalFontMENU.actions())
|
|
541
|
+
|
|
542
|
+
action = None
|
|
543
|
+
for act in actions:
|
|
544
|
+
if act.isChecked():
|
|
545
|
+
action = act
|
|
546
|
+
break
|
|
547
|
+
|
|
548
|
+
return action
|
|
549
|
+
|
|
550
|
+
def selectFont(self, action):
|
|
551
|
+
"""Set console and workbox font to current font
|
|
552
|
+
|
|
553
|
+
Args:
|
|
554
|
+
action (QAction): menu action associated with chosen font
|
|
555
|
+
"""
|
|
556
|
+
|
|
557
|
+
actions = self.uiMonospaceFontMENU.actions()
|
|
558
|
+
actions.extend(self.uiProportionalFontMENU.actions())
|
|
559
|
+
|
|
560
|
+
for act in actions:
|
|
561
|
+
act.setChecked(act == action)
|
|
562
|
+
|
|
563
|
+
family = action.text()
|
|
564
|
+
font = self.console().font()
|
|
565
|
+
font.setFamily(family)
|
|
566
|
+
self.console().setConsoleFont(font)
|
|
567
|
+
|
|
568
|
+
for workbox in self.uiWorkboxTAB.all_widgets():
|
|
569
|
+
workbox.__set_margins_font__(font)
|
|
570
|
+
workbox.__set_font__(font)
|
|
571
|
+
|
|
572
|
+
@classmethod
|
|
573
|
+
def _genPrefName(cls, baseName, index):
|
|
574
|
+
if index:
|
|
575
|
+
baseName = '{name}{index}'.format(name=baseName, index=index)
|
|
576
|
+
return baseName
|
|
577
|
+
|
|
578
|
+
def adjustWorkboxOrientation(self, state):
|
|
579
|
+
if state:
|
|
580
|
+
self.uiSplitterSPLIT.setOrientation(Qt.Horizontal)
|
|
581
|
+
else:
|
|
582
|
+
self.uiSplitterSPLIT.setOrientation(Qt.Vertical)
|
|
583
|
+
|
|
584
|
+
def backupPreferences(self):
|
|
585
|
+
"""Saves a copy of the current preferences to a zip archive."""
|
|
586
|
+
zip_path = prefs.backup()
|
|
587
|
+
print('PrEditor Preferences backed up to "{}"'.format(zip_path))
|
|
588
|
+
return zip_path
|
|
589
|
+
|
|
590
|
+
def browsePreferences(self):
|
|
591
|
+
prefs.browse(core_name=self.name)
|
|
592
|
+
|
|
593
|
+
def console(self):
|
|
594
|
+
return self.uiConsoleTXT
|
|
595
|
+
|
|
596
|
+
def clearLog(self):
|
|
597
|
+
self.uiConsoleTXT.clear()
|
|
598
|
+
|
|
599
|
+
def clearLogToFile(self):
|
|
600
|
+
"""If installLogToFile has been called, clear the stdout."""
|
|
601
|
+
if self._stds:
|
|
602
|
+
self._stds[0].clear(stamp=True)
|
|
603
|
+
|
|
604
|
+
def closeEvent(self, event):
|
|
605
|
+
self.recordPrefs()
|
|
606
|
+
# Save the logger configuration
|
|
607
|
+
lcfg = LoggingConfig(core_name=self.name)
|
|
608
|
+
lcfg.build()
|
|
609
|
+
lcfg.save()
|
|
610
|
+
|
|
611
|
+
super(LoggerWindow, self).closeEvent(event)
|
|
612
|
+
if self.uiConsoleTOOLBAR.isFloating():
|
|
613
|
+
self.uiConsoleTOOLBAR.hide()
|
|
614
|
+
|
|
615
|
+
# Handle any cleanup each workbox tab may need to do before closing
|
|
616
|
+
for editor, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
|
|
617
|
+
editor.__close__()
|
|
618
|
+
|
|
619
|
+
def closeLogger(self):
|
|
620
|
+
self.close()
|
|
621
|
+
|
|
622
|
+
def execAll(self):
|
|
623
|
+
"""Clears the console before executing all workbox code"""
|
|
624
|
+
if self.uiClearBeforeRunningACT.isChecked():
|
|
625
|
+
self.clearLog()
|
|
626
|
+
self.current_workbox().__exec_all__()
|
|
627
|
+
|
|
628
|
+
if self.uiAutoPromptACT.isChecked():
|
|
629
|
+
console = self.console()
|
|
630
|
+
prompt = console.prompt()
|
|
631
|
+
console.startPrompt(prompt)
|
|
632
|
+
|
|
633
|
+
def execSelected(self):
|
|
634
|
+
"""Clears the console before executing selected workbox code"""
|
|
635
|
+
if self.uiClearBeforeRunningACT.isChecked():
|
|
636
|
+
self.clearLog()
|
|
637
|
+
self.current_workbox().__exec_selected__()
|
|
638
|
+
|
|
639
|
+
def keyPressEvent(self, event):
|
|
640
|
+
# Fix 'Maya : Qt tools lose focus' https://redmine.blur.com/issues/34430
|
|
641
|
+
if event.modifiers() & (Qt.AltModifier | Qt.ControlModifier | Qt.ShiftModifier):
|
|
642
|
+
pass
|
|
643
|
+
else:
|
|
644
|
+
super(LoggerWindow, self).keyPressEvent(event)
|
|
645
|
+
|
|
646
|
+
def pathsAboutToBeCleared(self):
|
|
647
|
+
if self.uiClearLogOnRefreshACT.isChecked():
|
|
648
|
+
self.clearLog()
|
|
649
|
+
|
|
650
|
+
def reportExecutionTime(self, seconds):
|
|
651
|
+
"""Update status text with seconds passed in."""
|
|
652
|
+
self.setStatusText('Exec: {:0.04f} Seconds'.format(seconds))
|
|
653
|
+
|
|
654
|
+
def recordPrefs(self, manual=False):
|
|
655
|
+
if not manual and not self.uiAutoSaveSettingssACT.isChecked():
|
|
656
|
+
return
|
|
657
|
+
|
|
658
|
+
pref = self.load_prefs()
|
|
659
|
+
geo = self.geometry()
|
|
660
|
+
pref.update(
|
|
661
|
+
{
|
|
662
|
+
'loggergeom': [geo.x(), geo.y(), geo.width(), geo.height()],
|
|
663
|
+
'windowState': int(self.windowState()),
|
|
664
|
+
'SplitterVertical': self.uiEditorVerticalACT.isChecked(),
|
|
665
|
+
'SplitterSize': self.uiSplitterSPLIT.sizes(),
|
|
666
|
+
'tabIndent': self.uiIndentationsTabsACT.isChecked(),
|
|
667
|
+
'copyIndentsAsSpaces': self.uiCopyTabsToSpacesACT.isChecked(),
|
|
668
|
+
'hintingEnabled': self.uiAutoCompleteEnabledACT.isChecked(),
|
|
669
|
+
'spellCheckEnabled': self.uiSpellCheckEnabledACT.isChecked(),
|
|
670
|
+
'wordWrap': self.uiWordWrapACT.isChecked(),
|
|
671
|
+
'clearBeforeRunning': self.uiClearBeforeRunningACT.isChecked(),
|
|
672
|
+
'clearBeforeEnvRefresh': self.uiClearLogOnRefreshACT.isChecked(),
|
|
673
|
+
'toolbarStates': six.text_type(self.saveState().toHex(), 'utf-8'),
|
|
674
|
+
'consoleFont': self.console().font().toString(),
|
|
675
|
+
'uiAutoSaveSettingssACT': self.uiAutoSaveSettingssACT.isChecked(),
|
|
676
|
+
'uiAutoPromptACT': self.uiAutoPromptACT.isChecked(),
|
|
677
|
+
'uiLinesInNewWorkboxACT': self.uiLinesInNewWorkboxACT.isChecked(),
|
|
678
|
+
'uiErrorHyperlinksACT': self.uiErrorHyperlinksACT.isChecked(),
|
|
679
|
+
'textEditorPath': self.textEditorPath,
|
|
680
|
+
'textEditorCmdTempl': self.textEditorCmdTempl,
|
|
681
|
+
'currentStyleSheet': self._stylesheet,
|
|
682
|
+
'flash_time': self.uiConsoleTXT.flash_time,
|
|
683
|
+
}
|
|
684
|
+
)
|
|
685
|
+
|
|
686
|
+
# completer settings
|
|
687
|
+
completer = self.console().completer()
|
|
688
|
+
pref["caseSensitive"] = completer.caseSensitive()
|
|
689
|
+
pref["completerMode"] = completer.completerMode().value
|
|
690
|
+
|
|
691
|
+
if self._stylesheet == 'Custom':
|
|
692
|
+
pref['styleSheet'] = self.styleSheet()
|
|
693
|
+
|
|
694
|
+
workbox_prefs = self.uiWorkboxTAB.save_prefs()
|
|
695
|
+
pref['workbox_prefs'] = workbox_prefs
|
|
696
|
+
|
|
697
|
+
pref['editor_cls'] = self.editor_cls_name
|
|
698
|
+
|
|
699
|
+
self.save_prefs(pref)
|
|
700
|
+
|
|
701
|
+
def load_prefs(self):
|
|
702
|
+
filename = prefs.prefs_path('preditor_pref.json', core_name=self.name)
|
|
703
|
+
if os.path.exists(filename):
|
|
704
|
+
with open(filename) as fp:
|
|
705
|
+
return json.load(fp)
|
|
706
|
+
return {}
|
|
707
|
+
|
|
708
|
+
def save_prefs(self, pref):
|
|
709
|
+
# Save preferences to disk
|
|
710
|
+
filename = prefs.prefs_path('preditor_pref.json', core_name=self.name)
|
|
711
|
+
dirname = os.path.dirname(filename)
|
|
712
|
+
if not os.path.exists(dirname):
|
|
713
|
+
os.makedirs(dirname)
|
|
714
|
+
with open(filename, 'w') as fp:
|
|
715
|
+
json.dump(pref, fp, indent=4)
|
|
716
|
+
|
|
717
|
+
def restartLogger(self):
|
|
718
|
+
"""Closes this PrEditor instance and starts a new process with the same
|
|
719
|
+
cli arguments.
|
|
720
|
+
|
|
721
|
+
Note: This only works if PrEditor is running in standalone mode. It doesn't
|
|
722
|
+
quit the QApplication or other host process. It simply closes this instance
|
|
723
|
+
of PrEditor, saving its preferences, which should allow Qt to exit if no
|
|
724
|
+
other windows are open.
|
|
725
|
+
"""
|
|
726
|
+
self.close()
|
|
727
|
+
|
|
728
|
+
# Get the current command and launch it as a new process. This handles
|
|
729
|
+
# use of the preditor/preditor executable launchers.
|
|
730
|
+
cmd = sys.argv[0]
|
|
731
|
+
args = sys.argv[1:]
|
|
732
|
+
|
|
733
|
+
if os.path.basename(cmd) == "__main__.py":
|
|
734
|
+
# Handles using `python -m preditor` style launch.
|
|
735
|
+
cmd = sys.executable
|
|
736
|
+
args = ["-m", "preditor"] + args
|
|
737
|
+
QtCore.QProcess.startDetached(cmd, args)
|
|
738
|
+
|
|
739
|
+
def restorePrefs(self):
|
|
740
|
+
pref = self.load_prefs()
|
|
741
|
+
|
|
742
|
+
# Editor selection
|
|
743
|
+
self.editor_cls_name = pref.get('editor_cls')
|
|
744
|
+
if self.editor_cls_name:
|
|
745
|
+
self.editor_cls_name, editor_cls = plugins.editor(self.editor_cls_name)
|
|
746
|
+
self.uiWorkboxTAB.editor_cls = editor_cls
|
|
747
|
+
else:
|
|
748
|
+
self.uiWorkboxTAB.editor_cls = None
|
|
749
|
+
# Set the workbox core_name so it reads/writes its tabs content into the
|
|
750
|
+
# same core_name preference folder.
|
|
751
|
+
self.uiWorkboxTAB.core_name = self.name
|
|
752
|
+
self.uiEditorChooserWGT.set_editor_name(self.editor_cls_name)
|
|
753
|
+
|
|
754
|
+
# Geometry
|
|
755
|
+
if 'loggergeom' in pref:
|
|
756
|
+
self.setGeometry(*pref['loggergeom'])
|
|
757
|
+
self.uiEditorVerticalACT.setChecked(pref.get('SplitterVertical', False))
|
|
758
|
+
self.adjustWorkboxOrientation(self.uiEditorVerticalACT.isChecked())
|
|
759
|
+
|
|
760
|
+
sizes = pref.get('SplitterSize')
|
|
761
|
+
if sizes:
|
|
762
|
+
self.uiSplitterSPLIT.setSizes(sizes)
|
|
763
|
+
self.setWindowState(Qt.WindowStates(pref.get('windowState', 0)))
|
|
764
|
+
self.uiIndentationsTabsACT.setChecked(pref.get('tabIndent', True))
|
|
765
|
+
self.uiCopyTabsToSpacesACT.setChecked(pref.get('copyIndentsAsSpaces', False))
|
|
766
|
+
self.uiAutoCompleteEnabledACT.setChecked(pref.get('hintingEnabled', True))
|
|
767
|
+
|
|
768
|
+
# completer settings
|
|
769
|
+
self.setCaseSensitive(pref.get('caseSensitive', True))
|
|
770
|
+
completerMode = CompleterMode(pref.get('completerMode', 0))
|
|
771
|
+
self.cycleToCompleterMode(completerMode)
|
|
772
|
+
self.setCompleterMode(completerMode)
|
|
773
|
+
|
|
774
|
+
self.setSpellCheckEnabled(self.uiSpellCheckEnabledACT.isChecked())
|
|
775
|
+
self.uiSpellCheckEnabledACT.setChecked(pref.get('spellCheckEnabled', False))
|
|
776
|
+
self.uiSpellCheckEnabledACT.setDisabled(False)
|
|
777
|
+
|
|
778
|
+
self.uiConsoleTXT.completer().setEnabled(
|
|
779
|
+
self.uiAutoCompleteEnabledACT.isChecked()
|
|
780
|
+
)
|
|
781
|
+
self.uiAutoSaveSettingssACT.setChecked(pref.get('uiAutoSaveSettingssACT', True))
|
|
782
|
+
|
|
783
|
+
self.uiAutoPromptACT.setChecked(pref.get('uiAutoPromptACT', False))
|
|
784
|
+
self.uiLinesInNewWorkboxACT.setChecked(
|
|
785
|
+
pref.get('uiLinesInNewWorkboxACT', False)
|
|
786
|
+
)
|
|
787
|
+
self.uiErrorHyperlinksACT.setChecked(pref.get('uiErrorHyperlinksACT', True))
|
|
788
|
+
|
|
789
|
+
# External text editor filepath and command template
|
|
790
|
+
defaultExePath = r"C:\Program Files\Sublime Text 3\sublime_text.exe"
|
|
791
|
+
defaultCmd = r"{exePath} {modulePath}:{lineNum}"
|
|
792
|
+
self.textEditorPath = pref.get('textEditorPath', defaultExePath)
|
|
793
|
+
self.textEditorCmdTempl = pref.get('textEditorCmdTempl', defaultCmd)
|
|
794
|
+
|
|
795
|
+
self.uiWordWrapACT.setChecked(pref.get('wordWrap', True))
|
|
796
|
+
self.setWordWrap(self.uiWordWrapACT.isChecked())
|
|
797
|
+
self.uiClearBeforeRunningACT.setChecked(pref.get('clearBeforeRunning', False))
|
|
798
|
+
self.uiClearLogOnRefreshACT.setChecked(pref.get('clearBeforeEnvRefresh', False))
|
|
799
|
+
self.setClearBeforeRunning(self.uiClearBeforeRunningACT.isChecked())
|
|
800
|
+
|
|
801
|
+
self._stylesheet = pref.get('currentStyleSheet', 'Bright')
|
|
802
|
+
if self._stylesheet == 'Custom':
|
|
803
|
+
self.setStyleSheet(pref.get('styleSheet', ''))
|
|
804
|
+
else:
|
|
805
|
+
self.setStyleSheet(self._stylesheet)
|
|
806
|
+
self.uiConsoleTXT.flash_time = pref.get('flash_time', 1.0)
|
|
807
|
+
|
|
808
|
+
self.uiWorkboxTAB.restore_prefs(pref.get('workbox_prefs', {}))
|
|
809
|
+
|
|
810
|
+
# Ensure the correct workbox stack page is shown
|
|
811
|
+
self.update_workbox_stack()
|
|
812
|
+
|
|
813
|
+
_font = pref.get('consoleFont', None)
|
|
814
|
+
if _font:
|
|
815
|
+
font = QFont()
|
|
816
|
+
if font.fromString(_font):
|
|
817
|
+
self.console().setConsoleFont(font)
|
|
818
|
+
|
|
819
|
+
def restoreToolbars(self, pref=None):
|
|
820
|
+
if pref is None:
|
|
821
|
+
pref = self.load_prefs()
|
|
822
|
+
|
|
823
|
+
state = pref.get('toolbarStates', None)
|
|
824
|
+
if state:
|
|
825
|
+
state = QByteArray.fromHex(bytes(state, 'utf-8'))
|
|
826
|
+
self.restoreState(state)
|
|
827
|
+
|
|
828
|
+
def setAutoCompleteEnabled(self, state):
|
|
829
|
+
self.uiConsoleTXT.completer().setEnabled(state)
|
|
830
|
+
for workbox, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
|
|
831
|
+
workbox.__set_auto_complete_enabled__(state)
|
|
832
|
+
|
|
833
|
+
def setSpellCheckEnabled(self, state):
|
|
834
|
+
try:
|
|
835
|
+
self.delayable_engine.set_delayable_enabled('spell_check', state)
|
|
836
|
+
except KeyError:
|
|
837
|
+
# Spell check can not be enabled
|
|
838
|
+
if self.isVisible():
|
|
839
|
+
# Only show warning if Logger is visible and also disable the action
|
|
840
|
+
self.uiSpellCheckEnabledACT.setDisabled(True)
|
|
841
|
+
QMessageBox.warning(
|
|
842
|
+
self, "Spell-Check", 'Unable to activate spell check.'
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
def setStatusText(self, txt):
|
|
846
|
+
"""Set the text shown in the menu corner of the menu bar.
|
|
847
|
+
|
|
848
|
+
Args:
|
|
849
|
+
txt (str): The text to show in the status text label.
|
|
850
|
+
"""
|
|
851
|
+
self.uiStatusLBL.setText(txt)
|
|
852
|
+
self.uiMenuBar.adjustSize()
|
|
853
|
+
|
|
854
|
+
def clearStatusText(self):
|
|
855
|
+
"""Clear any displayed status text"""
|
|
856
|
+
self.uiStatusLBL.setText('')
|
|
857
|
+
self.uiMenuBar.adjustSize()
|
|
858
|
+
|
|
859
|
+
def autoHideStatusText(self):
|
|
860
|
+
"""Set timer to automatically clear status text"""
|
|
861
|
+
if self.statusTimer.isActive():
|
|
862
|
+
self.statusTimer.stop()
|
|
863
|
+
self.statusTimer.singleShot(2000, self.clearStatusText)
|
|
864
|
+
self.statusTimer.start()
|
|
865
|
+
|
|
866
|
+
def setStyleSheet(self, stylesheet, recordPrefs=True):
|
|
867
|
+
"""Accepts the name of a stylesheet included with blurdev, or a full
|
|
868
|
+
path to any stylesheet. If given None, it will default to Bright.
|
|
869
|
+
"""
|
|
870
|
+
sheet = None
|
|
871
|
+
if stylesheet is None:
|
|
872
|
+
stylesheet = 'Bright'
|
|
873
|
+
if os.path.isfile(stylesheet):
|
|
874
|
+
# A path to a stylesheet was passed in
|
|
875
|
+
with open(stylesheet) as f:
|
|
876
|
+
sheet = f.read()
|
|
877
|
+
self._stylesheet = stylesheet
|
|
878
|
+
else:
|
|
879
|
+
# Try to find an installed stylesheet with the given name
|
|
880
|
+
sheet, valid = stylesheets.read_stylesheet(stylesheet)
|
|
881
|
+
if valid:
|
|
882
|
+
self._stylesheet = stylesheet
|
|
883
|
+
else:
|
|
884
|
+
# Assume the user passed the text of the stylesheet directly
|
|
885
|
+
sheet = stylesheet
|
|
886
|
+
self._stylesheet = 'Custom'
|
|
887
|
+
|
|
888
|
+
# Load the stylesheet
|
|
889
|
+
if sheet is not None:
|
|
890
|
+
super(LoggerWindow, self).setStyleSheet(sheet)
|
|
891
|
+
|
|
892
|
+
# Update the style menu
|
|
893
|
+
for act in self.uiStyleMENU.actions():
|
|
894
|
+
name = act.objectName()
|
|
895
|
+
isCurrent = name == 'ui{}ACT'.format(self._stylesheet)
|
|
896
|
+
act.setChecked(isCurrent)
|
|
897
|
+
|
|
898
|
+
# Notify widgets that the styleSheet has changed
|
|
899
|
+
self.styleSheetChanged.emit(stylesheet)
|
|
900
|
+
|
|
901
|
+
def setCaseSensitive(self, state):
|
|
902
|
+
"""Set completer case-sensivity"""
|
|
903
|
+
completer = self.console().completer()
|
|
904
|
+
completer.setCaseSensitive(state)
|
|
905
|
+
self.uiAutoCompleteCaseSensitiveACT.setChecked(state)
|
|
906
|
+
self.reportCaseChange(state)
|
|
907
|
+
completer.refreshList()
|
|
908
|
+
|
|
909
|
+
def toggleCaseSensitive(self):
|
|
910
|
+
"""Toggle completer case-sensitivity"""
|
|
911
|
+
state = self.console().completer().caseSensitive()
|
|
912
|
+
self.reportCaseChange(state)
|
|
913
|
+
self.setCaseSensitive(not state)
|
|
914
|
+
|
|
915
|
+
# Completer Modes
|
|
916
|
+
def cycleCompleterMode(self):
|
|
917
|
+
"""Cycle comleter mode"""
|
|
918
|
+
completerMode = next(self.completerModeCycle)
|
|
919
|
+
self.setCompleterMode(completerMode)
|
|
920
|
+
self.reportCompleterModeChange(completerMode)
|
|
921
|
+
|
|
922
|
+
def cycleToCompleterMode(self, completerMode):
|
|
923
|
+
"""
|
|
924
|
+
Syncs the completerModeCycle iterator to currently chosen completerMode
|
|
925
|
+
Args:
|
|
926
|
+
completerMode: Chosen CompleterMode ENUM member
|
|
927
|
+
"""
|
|
928
|
+
for _ in range(len(CompleterMode)):
|
|
929
|
+
tempMode = next(self.completerModeCycle)
|
|
930
|
+
if tempMode == completerMode:
|
|
931
|
+
break
|
|
932
|
+
|
|
933
|
+
def setCompleterMode(self, completerMode):
|
|
934
|
+
"""
|
|
935
|
+
Set the completer mode to chosen mode
|
|
936
|
+
Args:
|
|
937
|
+
completerMode: Chosen CompleterMode ENUM member
|
|
938
|
+
"""
|
|
939
|
+
completer = self.console().completer()
|
|
940
|
+
|
|
941
|
+
completer.setCompleterMode(completerMode)
|
|
942
|
+
completer.buildCompleter()
|
|
943
|
+
|
|
944
|
+
for action in self.uiCompleterModeMENU.actions():
|
|
945
|
+
action.setChecked(action.data() == completerMode)
|
|
946
|
+
|
|
947
|
+
def selectCompleterMode(self, action):
|
|
948
|
+
if not action.isChecked():
|
|
949
|
+
action.setChecked(True)
|
|
950
|
+
return
|
|
951
|
+
"""
|
|
952
|
+
Handle when completer mode is chosen via menu
|
|
953
|
+
Will sync mode iterator and set the completion mode
|
|
954
|
+
Args:
|
|
955
|
+
action: the menu action associated with the chosen mode
|
|
956
|
+
"""
|
|
957
|
+
|
|
958
|
+
# update cycleToCompleterMode to current Mode
|
|
959
|
+
mode = action.data()
|
|
960
|
+
self.cycleToCompleterMode(mode)
|
|
961
|
+
self.setCompleterMode(mode)
|
|
962
|
+
|
|
963
|
+
def reportCaseChange(self, state):
|
|
964
|
+
"""Update status text with current Case Sensitivity Mode"""
|
|
965
|
+
text = "Case Sensitive " if state else "Case Insensitive "
|
|
966
|
+
self.setStatusText(text)
|
|
967
|
+
self.autoHideStatusText()
|
|
968
|
+
|
|
969
|
+
def reportCompleterModeChange(self, mode):
|
|
970
|
+
"""Update status text with current Completer Mode"""
|
|
971
|
+
self.setStatusText('Completer Mode: {} '.format(mode.displayName()))
|
|
972
|
+
self.autoHideStatusText()
|
|
973
|
+
|
|
974
|
+
def setClearBeforeRunning(self, state):
|
|
975
|
+
self.uiRunSelectedACT.setIcon(QIcon(resourcePath('img/playlist-play.png')))
|
|
976
|
+
self.uiRunAllACT.setIcon(QIcon(resourcePath('img/play.png')))
|
|
977
|
+
|
|
978
|
+
def setFlashWindowInterval(self):
|
|
979
|
+
value = self.uiConsoleTXT.flash_time
|
|
980
|
+
msg = (
|
|
981
|
+
'If running code in the logger takes X seconds or longer,\n'
|
|
982
|
+
'the window will flash if it is not in focus.\n'
|
|
983
|
+
'Setting the value to zero will disable flashing.'
|
|
984
|
+
)
|
|
985
|
+
value, success = QInputDialog.getDouble(self, 'Set flash window', msg, value)
|
|
986
|
+
if success:
|
|
987
|
+
self.uiConsoleTXT.flash_time = value
|
|
988
|
+
|
|
989
|
+
def setWordWrap(self, state):
|
|
990
|
+
if state:
|
|
991
|
+
self.uiConsoleTXT.setLineWrapMode(self.uiConsoleTXT.WidgetWidth)
|
|
992
|
+
else:
|
|
993
|
+
self.uiConsoleTXT.setLineWrapMode(self.uiConsoleTXT.NoWrap)
|
|
994
|
+
|
|
995
|
+
def show_about(self):
|
|
996
|
+
"""Shows `preditor.about_preditor()`'s output in a message box."""
|
|
997
|
+
msg = about_preditor(instance=self)
|
|
998
|
+
QMessageBox.information(self, 'About PrEditor', '<pre>{}</pre>'.format(msg))
|
|
999
|
+
|
|
1000
|
+
def showEnvironmentVars(self):
|
|
1001
|
+
dlg = Dialog(self)
|
|
1002
|
+
lyt = QVBoxLayout(dlg)
|
|
1003
|
+
lbl = QTextBrowser(dlg)
|
|
1004
|
+
lyt.addWidget(lbl)
|
|
1005
|
+
dlg.setWindowTitle('Blurdev Environment Variable Help')
|
|
1006
|
+
with open(resourcePath('environment_variables.html')) as f:
|
|
1007
|
+
lbl.setText(f.read().replace('\n', ''))
|
|
1008
|
+
dlg.setMinimumSize(600, 400)
|
|
1009
|
+
dlg.show()
|
|
1010
|
+
|
|
1011
|
+
def showEvent(self, event):
|
|
1012
|
+
super(LoggerWindow, self).showEvent(event)
|
|
1013
|
+
self.restoreToolbars()
|
|
1014
|
+
self.updateIndentationsUseTabs()
|
|
1015
|
+
self.updateCopyIndentsAsSpaces()
|
|
1016
|
+
|
|
1017
|
+
# Adjust the minimum height of the label so it's text is the same as
|
|
1018
|
+
# the action menu text
|
|
1019
|
+
height = self.uiMenuBar.actionGeometry(self.uiFileMENU.menuAction()).height()
|
|
1020
|
+
self.uiStatusLBL.setMinimumHeight(height)
|
|
1021
|
+
|
|
1022
|
+
@Slot()
|
|
1023
|
+
def show_workbox_options(self):
|
|
1024
|
+
self.uiWorkboxSTACK.setCurrentIndex(WorkboxPages.Options)
|
|
1025
|
+
|
|
1026
|
+
@Slot()
|
|
1027
|
+
def show_focus_name(self):
|
|
1028
|
+
model = GroupTabListItemModel(manager=self.uiWorkboxTAB)
|
|
1029
|
+
model.process()
|
|
1030
|
+
|
|
1031
|
+
def update_tab(index):
|
|
1032
|
+
group, tab = model.workbox_indexes_from_model_index(index)
|
|
1033
|
+
if group is not None:
|
|
1034
|
+
self.uiWorkboxTAB.set_current_groups_from_index(group, tab)
|
|
1035
|
+
|
|
1036
|
+
w = FuzzySearch(model, parent=self)
|
|
1037
|
+
w.selected.connect(update_tab)
|
|
1038
|
+
w.canceled.connect(update_tab)
|
|
1039
|
+
w.highlighted.connect(update_tab)
|
|
1040
|
+
w.popup()
|
|
1041
|
+
|
|
1042
|
+
def updateCopyIndentsAsSpaces(self):
|
|
1043
|
+
for workbox, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
|
|
1044
|
+
workbox.__set_copy_indents_as_spaces__(
|
|
1045
|
+
self.uiCopyTabsToSpacesACT.isChecked()
|
|
1046
|
+
)
|
|
1047
|
+
|
|
1048
|
+
def updateIndentationsUseTabs(self):
|
|
1049
|
+
for workbox, _, _, _, _ in self.uiWorkboxTAB.all_widgets():
|
|
1050
|
+
workbox.__set_indentations_use_tabs__(
|
|
1051
|
+
self.uiIndentationsTabsACT.isChecked()
|
|
1052
|
+
)
|
|
1053
|
+
|
|
1054
|
+
@Slot()
|
|
1055
|
+
def update_workbox_stack(self):
|
|
1056
|
+
if self.uiWorkboxTAB.editor_cls:
|
|
1057
|
+
index = WorkboxPages.Workboxes
|
|
1058
|
+
else:
|
|
1059
|
+
index = WorkboxPages.Options
|
|
1060
|
+
|
|
1061
|
+
self.uiWorkboxSTACK.setCurrentIndex(index)
|
|
1062
|
+
|
|
1063
|
+
def shutdown(self):
|
|
1064
|
+
# close out of the ide system
|
|
1065
|
+
|
|
1066
|
+
# if this is the global instance, then allow it to be deleted on close
|
|
1067
|
+
if self == LoggerWindow._instance:
|
|
1068
|
+
self.setAttribute(Qt.WA_DeleteOnClose, True)
|
|
1069
|
+
LoggerWindow._instance = None
|
|
1070
|
+
|
|
1071
|
+
# clear out the system
|
|
1072
|
+
self.close()
|
|
1073
|
+
|
|
1074
|
+
def nextTab(self):
|
|
1075
|
+
"""Move focus to next workbox tab"""
|
|
1076
|
+
tabWidget = self.uiWorkboxTAB.currentWidget()
|
|
1077
|
+
if not tabWidget.currentWidget().hasFocus():
|
|
1078
|
+
tabWidget.currentWidget().setFocus()
|
|
1079
|
+
|
|
1080
|
+
index = tabWidget.currentIndex()
|
|
1081
|
+
if index == tabWidget.count() - 1:
|
|
1082
|
+
tabWidget.setCurrentIndex(0)
|
|
1083
|
+
else:
|
|
1084
|
+
tabWidget.setCurrentIndex(index + 1)
|
|
1085
|
+
|
|
1086
|
+
def prevTab(self):
|
|
1087
|
+
"""Move focus to previous workbox tab"""
|
|
1088
|
+
tabWidget = self.uiWorkboxTAB.currentWidget()
|
|
1089
|
+
if not tabWidget.currentWidget().hasFocus():
|
|
1090
|
+
tabWidget.currentWidget().setFocus()
|
|
1091
|
+
|
|
1092
|
+
index = tabWidget.currentIndex()
|
|
1093
|
+
if index == 0:
|
|
1094
|
+
tabWidget.setCurrentIndex(tabWidget.count() - 1)
|
|
1095
|
+
else:
|
|
1096
|
+
tabWidget.setCurrentIndex(index - 1)
|
|
1097
|
+
|
|
1098
|
+
def gotoGroupByIndex(self, index):
|
|
1099
|
+
"""Generally to be used in conjunction with the Ctrl+Alt+<num> keyboard
|
|
1100
|
+
shortcuts, which allow user to jump directly to another tab, mimicking
|
|
1101
|
+
web browser functionality.
|
|
1102
|
+
"""
|
|
1103
|
+
if index == -1:
|
|
1104
|
+
index = self.uiWorkboxTAB.count() - 1
|
|
1105
|
+
else:
|
|
1106
|
+
count = self.uiWorkboxTAB.count()
|
|
1107
|
+
index = min(index, count)
|
|
1108
|
+
index -= 1
|
|
1109
|
+
|
|
1110
|
+
self.uiWorkboxTAB.setCurrentIndex(index)
|
|
1111
|
+
|
|
1112
|
+
def gotoTabByIndex(self, index):
|
|
1113
|
+
"""Generally to be used in conjunction with the Ctrl+<num> keyboard
|
|
1114
|
+
shortcuts, which allow user to jump directly to another tab, mimicking
|
|
1115
|
+
web browser functionality.
|
|
1116
|
+
"""
|
|
1117
|
+
group_tab = self.uiWorkboxTAB.currentWidget()
|
|
1118
|
+
if index == -1:
|
|
1119
|
+
index = group_tab.count() - 1
|
|
1120
|
+
else:
|
|
1121
|
+
count = group_tab.count()
|
|
1122
|
+
index = min(index, count)
|
|
1123
|
+
index -= 1
|
|
1124
|
+
|
|
1125
|
+
group_tab.setCurrentIndex(index)
|
|
1126
|
+
|
|
1127
|
+
@staticmethod
|
|
1128
|
+
def instance(
|
|
1129
|
+
parent=None, name=None, run_workbox=False, create=True, standalone=False
|
|
1130
|
+
):
|
|
1131
|
+
"""Returns the existing instance of the PrEditor gui creating it on first call.
|
|
1132
|
+
|
|
1133
|
+
Args:
|
|
1134
|
+
parent (QWidget, optional): If the instance hasn't been created yet, create
|
|
1135
|
+
it and parent it to this object.
|
|
1136
|
+
run_workbox (bool, optional): If the instance hasn't been created yet, this
|
|
1137
|
+
will execute the active workbox's code once fully initialized.
|
|
1138
|
+
create (bool, optional): Returns None if the instance has not been created.
|
|
1139
|
+
standalone (bool, optional): Launch PrEditor in standalone mode. This
|
|
1140
|
+
enables extra options that only make sense when it is running as
|
|
1141
|
+
its own app, not inside of another app.
|
|
1142
|
+
|
|
1143
|
+
Returns:
|
|
1144
|
+
Returns a fully initialized instance of the PrEditor gui. If called more
|
|
1145
|
+
than once, the same instance will be returned. If create is False, it may
|
|
1146
|
+
return None.
|
|
1147
|
+
"""
|
|
1148
|
+
# create the instance for the logger
|
|
1149
|
+
if not LoggerWindow._instance:
|
|
1150
|
+
if not create:
|
|
1151
|
+
return None
|
|
1152
|
+
|
|
1153
|
+
# create the logger instance
|
|
1154
|
+
inst = LoggerWindow(
|
|
1155
|
+
parent, name=name, run_workbox=run_workbox, standalone=standalone
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
# RV has a Unique window structure. It makes more sense to not parent a
|
|
1159
|
+
# singleton window than to parent it to a specific top level window.
|
|
1160
|
+
if core.objectName() == 'rv':
|
|
1161
|
+
inst.setParent(None)
|
|
1162
|
+
inst.setAttribute(Qt.WA_QuitOnClose, False)
|
|
1163
|
+
|
|
1164
|
+
# protect the memory
|
|
1165
|
+
inst.setAttribute(Qt.WA_DeleteOnClose, False)
|
|
1166
|
+
|
|
1167
|
+
# cache the instance
|
|
1168
|
+
LoggerWindow._instance = inst
|
|
1169
|
+
|
|
1170
|
+
return LoggerWindow._instance
|
|
1171
|
+
|
|
1172
|
+
def installLogToFile(self):
|
|
1173
|
+
"""All stdout/stderr output is also appended to this file.
|
|
1174
|
+
|
|
1175
|
+
This uses preditor.debug.logToFile(path, useOldStd=True).
|
|
1176
|
+
"""
|
|
1177
|
+
if self._logToFilePath is None:
|
|
1178
|
+
path = osystem.defaultLogFile()
|
|
1179
|
+
path, _ = QtCompat.QFileDialog.getSaveFileName(
|
|
1180
|
+
self, "Log Output to File", path
|
|
1181
|
+
)
|
|
1182
|
+
if not path:
|
|
1183
|
+
return
|
|
1184
|
+
path = os.path.normpath(path)
|
|
1185
|
+
print('Output logged to: "{}"'.format(path))
|
|
1186
|
+
debug.logToFile(path, useOldStd=True)
|
|
1187
|
+
# Store the std's so we can clear them later
|
|
1188
|
+
self._stds = (sys.stdout, sys.stderr)
|
|
1189
|
+
self.uiLogToFileACT.setText('Output Logged to File')
|
|
1190
|
+
self.uiLogToFileClearACT.setVisible(True)
|
|
1191
|
+
self._logToFilePath = path
|
|
1192
|
+
else:
|
|
1193
|
+
print('Output logged to: "{}"'.format(self._logToFilePath))
|
|
1194
|
+
|
|
1195
|
+
@classmethod
|
|
1196
|
+
def instance_shutdown(cls):
|
|
1197
|
+
"""Call shutdown the LoggerWindow instance only if it was instantiated.
|
|
1198
|
+
|
|
1199
|
+
Returns:
|
|
1200
|
+
bool: If a shutdown was required
|
|
1201
|
+
"""
|
|
1202
|
+
if cls._instance:
|
|
1203
|
+
cls._instance.shutdown()
|
|
1204
|
+
return True
|
|
1205
|
+
return False
|