PrEditor 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- preditor/__init__.py +315 -0
- preditor/__main__.py +13 -0
- preditor/about_module.py +165 -0
- preditor/cli.py +192 -0
- preditor/config.py +318 -0
- preditor/constants.py +13 -0
- preditor/contexts.py +210 -0
- preditor/cores/__init__.py +0 -0
- preditor/cores/core.py +20 -0
- preditor/dccs/.hab.json +10 -0
- preditor/dccs/maya/PrEditor_maya.mod +1 -0
- preditor/dccs/maya/README.md +22 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +141 -0
- preditor/dccs/studiomax/PackageContents.xml +32 -0
- preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
- preditor/dccs/studiomax/README.md +17 -0
- preditor/dccs/studiomax/preditor.ms +16 -0
- preditor/dccs/studiomax/preditor_menu.mnx +7 -0
- preditor/debug.py +149 -0
- preditor/delayable_engine/__init__.py +302 -0
- preditor/delayable_engine/delayables.py +85 -0
- preditor/enum.py +728 -0
- preditor/excepthooks.py +165 -0
- preditor/gui/__init__.py +56 -0
- preditor/gui/app.py +163 -0
- preditor/gui/codehighlighter.py +289 -0
- preditor/gui/completer.py +237 -0
- preditor/gui/console.py +605 -0
- preditor/gui/console_base.py +911 -0
- preditor/gui/dialog.py +181 -0
- preditor/gui/drag_tab_bar.py +625 -0
- preditor/gui/editor_chooser.py +57 -0
- preditor/gui/errordialog.py +69 -0
- preditor/gui/find_files.py +137 -0
- preditor/gui/fuzzy_search/__init__.py +0 -0
- preditor/gui/fuzzy_search/fuzzy_search.py +97 -0
- preditor/gui/group_tab_widget/__init__.py +0 -0
- preditor/gui/group_tab_widget/group_tab_widget.py +528 -0
- preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
- preditor/gui/group_tab_widget/grouped_tab_models.py +107 -0
- preditor/gui/group_tab_widget/grouped_tab_widget.py +223 -0
- preditor/gui/group_tab_widget/one_tab_widget.py +96 -0
- preditor/gui/level_buttons.py +358 -0
- preditor/gui/logger_window_handler.py +77 -0
- preditor/gui/logger_window_plugin.py +35 -0
- preditor/gui/loggerwindow.py +2405 -0
- preditor/gui/newtabwidget.py +69 -0
- preditor/gui/output_console.py +11 -0
- preditor/gui/qtdesigner/__init__.py +21 -0
- preditor/gui/qtdesigner/_log_plugin.py +29 -0
- preditor/gui/qtdesigner/console_base_plugin.py +48 -0
- preditor/gui/qtdesigner/console_predit_plugin.py +48 -0
- preditor/gui/set_text_editor_path_dialog.py +61 -0
- preditor/gui/status_label.py +99 -0
- preditor/gui/suggest_path_quotes_dialog.py +50 -0
- preditor/gui/ui/editor_chooser.ui +93 -0
- preditor/gui/ui/errordialog.ui +74 -0
- preditor/gui/ui/find_files.ui +140 -0
- preditor/gui/ui/loggerwindow.ui +1909 -0
- preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
- preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
- preditor/gui/window.py +161 -0
- preditor/gui/workbox_mixin.py +1139 -0
- preditor/gui/workbox_text_edit.py +136 -0
- preditor/gui/workboxwidget.py +315 -0
- preditor/logging_config.py +55 -0
- preditor/osystem.py +401 -0
- preditor/plugins.py +118 -0
- preditor/prefs.py +381 -0
- preditor/resource/environment_variables.html +26 -0
- preditor/resource/error_mail.html +85 -0
- preditor/resource/error_mail_inline.html +41 -0
- preditor/resource/img/README.md +17 -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/format-letter-case.svg +1 -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/regex.svg +1 -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
- preditor/resource/img/warning-big.png +0 -0
- preditor/resource/lang/python.json +30 -0
- preditor/resource/pref_updates/pref_updates.json +17 -0
- preditor/resource/settings.ini +25 -0
- preditor/resource/stylesheet/Bright.css +76 -0
- preditor/resource/stylesheet/Dark.css +210 -0
- preditor/scintilla/__init__.py +40 -0
- preditor/scintilla/delayables/__init__.py +11 -0
- preditor/scintilla/delayables/smart_highlight.py +97 -0
- preditor/scintilla/delayables/spell_check.py +174 -0
- preditor/scintilla/documenteditor.py +1924 -0
- preditor/scintilla/finddialog.py +68 -0
- preditor/scintilla/lang/__init__.py +80 -0
- preditor/scintilla/lang/config/bash.ini +15 -0
- preditor/scintilla/lang/config/batch.ini +14 -0
- preditor/scintilla/lang/config/cpp.ini +19 -0
- preditor/scintilla/lang/config/css.ini +19 -0
- preditor/scintilla/lang/config/eyeonscript.ini +17 -0
- preditor/scintilla/lang/config/html.ini +21 -0
- preditor/scintilla/lang/config/javascript.ini +24 -0
- preditor/scintilla/lang/config/lua.ini +16 -0
- preditor/scintilla/lang/config/maxscript.ini +20 -0
- preditor/scintilla/lang/config/mel.ini +18 -0
- preditor/scintilla/lang/config/mu.ini +22 -0
- preditor/scintilla/lang/config/nsi.ini +19 -0
- preditor/scintilla/lang/config/perl.ini +19 -0
- preditor/scintilla/lang/config/puppet.ini +19 -0
- preditor/scintilla/lang/config/python.ini +28 -0
- preditor/scintilla/lang/config/ruby.ini +19 -0
- preditor/scintilla/lang/config/sql.ini +7 -0
- preditor/scintilla/lang/config/xml.ini +21 -0
- preditor/scintilla/lang/config/yaml.ini +18 -0
- preditor/scintilla/lang/language.py +240 -0
- preditor/scintilla/lexers/__init__.py +0 -0
- preditor/scintilla/lexers/cpplexer.py +22 -0
- preditor/scintilla/lexers/javascriptlexer.py +27 -0
- preditor/scintilla/lexers/maxscriptlexer.py +235 -0
- preditor/scintilla/lexers/mellexer.py +369 -0
- preditor/scintilla/lexers/mulexer.py +33 -0
- preditor/scintilla/lexers/pythonlexer.py +42 -0
- preditor/scintilla/ui/finddialog.ui +160 -0
- preditor/settings.py +71 -0
- preditor/stream/__init__.py +72 -0
- preditor/stream/console_handler.py +169 -0
- preditor/stream/director.py +144 -0
- preditor/stream/manager.py +97 -0
- preditor/streamhandler_helper.py +46 -0
- preditor/utils/__init__.py +191 -0
- preditor/utils/call_stack.py +86 -0
- preditor/utils/cute.py +106 -0
- preditor/utils/stylesheets.py +54 -0
- preditor/utils/text_search.py +338 -0
- preditor/version.py +34 -0
- preditor/weakref.py +363 -0
- preditor-2.1.0.dist-info/METADATA +308 -0
- preditor-2.1.0.dist-info/RECORD +179 -0
- preditor-2.1.0.dist-info/WHEEL +5 -0
- preditor-2.1.0.dist-info/entry_points.txt +19 -0
- preditor-2.1.0.dist-info/licenses/LICENSE +165 -0
- preditor-2.1.0.dist-info/top_level.txt +3 -0
- tests/encodings/test_ecoding.py +33 -0
- tests/find_files/test_find_files.py +74 -0
- tests/ide/test_delayable_engine.py +171 -0
preditor/__init__.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
from __future__ import absolute_import, print_function
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from . import osystem
|
|
8
|
+
from .config import PreditorConfig
|
|
9
|
+
from .plugins import Plugins
|
|
10
|
+
from .version import version as __version__
|
|
11
|
+
|
|
12
|
+
DEFAULT_CORE_NAME = "PrEditor"
|
|
13
|
+
"""The default name to use for the core name."""
|
|
14
|
+
|
|
15
|
+
core = None # create a managed Core instance
|
|
16
|
+
"""
|
|
17
|
+
The blurdev managed :class:`Core` object from the :mod:`blurdev.cores` module.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
# Create the root blurdev module logging object.
|
|
21
|
+
_logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
# Add a NullHandler to suppress the "No handlers could be found for _logger"
|
|
24
|
+
# warnings from being printed to stderr. Studiomax and possibly other DCC's
|
|
25
|
+
# tend to treat any text written to stderr as a error when running headless.
|
|
26
|
+
# We also don't want this warning showing up in production anyway.
|
|
27
|
+
_logger.addHandler(logging.NullHandler())
|
|
28
|
+
|
|
29
|
+
plugins = Plugins()
|
|
30
|
+
|
|
31
|
+
config = PreditorConfig()
|
|
32
|
+
"""This config is used to setup the instance of PrEditor. It can be edited up
|
|
33
|
+
to the point the instance is created. After that changes will be ignored.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def about_preditor(instance=None):
|
|
38
|
+
"""Useful info about installed packages generated by plugins.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
instance (LoggerWindow, optional): Used by the AboutModule plugins
|
|
42
|
+
to access the current instance of a Preditor GUI.
|
|
43
|
+
"""
|
|
44
|
+
from .about_module import AboutModule
|
|
45
|
+
|
|
46
|
+
return AboutModule.generate(instance=instance)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def init():
|
|
50
|
+
os.environ['BDEV_EMAILINFO_PREDITOR_VERSION'] = __version__
|
|
51
|
+
pythonw_print_bugfix()
|
|
52
|
+
global core
|
|
53
|
+
# create the core
|
|
54
|
+
if not core:
|
|
55
|
+
from .cores.core import Core
|
|
56
|
+
|
|
57
|
+
objectName = None
|
|
58
|
+
_exe = os.path.basename(sys.executable).lower()
|
|
59
|
+
# Treat designer as a seperate core so it gets its own prefrences.
|
|
60
|
+
if 'designer' in _exe:
|
|
61
|
+
objectName = 'designer'
|
|
62
|
+
elif 'assfreezer' in _exe:
|
|
63
|
+
objectName = 'assfreezer'
|
|
64
|
+
core = Core(objectName=objectName)
|
|
65
|
+
|
|
66
|
+
for plugin in plugins.initialize():
|
|
67
|
+
plugin()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def configure(
|
|
71
|
+
name,
|
|
72
|
+
parent_callback=None,
|
|
73
|
+
excepthook=True,
|
|
74
|
+
logging=True,
|
|
75
|
+
streams=True,
|
|
76
|
+
headless_callback=None,
|
|
77
|
+
):
|
|
78
|
+
"""Global configuration of PrEditor. Safe to re-call until the instance is created.
|
|
79
|
+
|
|
80
|
+
Configures the instance of PrEditor without creating the GUI. It should be run
|
|
81
|
+
as early as possible in the applications initialization to allow PrEditor to
|
|
82
|
+
show as much stdout/err text as possible even the text printed before the
|
|
83
|
+
application's GUI is created.
|
|
84
|
+
|
|
85
|
+
This is a convenience method that sets useful defaults on `preditor.config`.
|
|
86
|
+
You can directly set the values on there instead of calling this function.
|
|
87
|
+
If you use that make sure to enable each feature as it doesn't by default.
|
|
88
|
+
|
|
89
|
+
Once `preditor.instance(create=True)` is called this will return without
|
|
90
|
+
making any changes. Otherwise unless noted with "First call only." each time
|
|
91
|
+
this function is called it will update any previously set values. This allows
|
|
92
|
+
you to minimally configure PrEditor and as more of the application comes
|
|
93
|
+
online enable more advanced features.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
name (str): The core_name to use for the global instance of PrEditor.
|
|
97
|
+
Once this has been set, you can call `launch` without passing name
|
|
98
|
+
to access the main instance. The core_name controls what preferences
|
|
99
|
+
are loaded and used by PrEditor including the workbox tabs.
|
|
100
|
+
parent_callback (callable, optional): Callback that returns a QWidget
|
|
101
|
+
to use as the parent of the LoggerWindow when its first created.
|
|
102
|
+
This can be used by DCC's to set the parent to their main window.
|
|
103
|
+
excepthook (bool, optional): First call only. Replaces `sys.excepthook`
|
|
104
|
+
with a interactive exception handler that prompts the user to show
|
|
105
|
+
PrEditor when an python exception is raised. It is recommended that
|
|
106
|
+
you only add the excepthook once the Qt UI is initialized.
|
|
107
|
+
logging (bool, optional): Restore the python logging configuration settings
|
|
108
|
+
that were recorded the last time PrEditor prefs were saved. If called
|
|
109
|
+
multiple times with different core_name's before the instance is
|
|
110
|
+
created, this will reset the logging configuration from the previous
|
|
111
|
+
core_name if logging prefs exist.
|
|
112
|
+
streams (bool, optional): First call only. Install the stream manager to
|
|
113
|
+
capture any stdout/stderr text written. Later when calling launch, the
|
|
114
|
+
LoggerWindow will show all of the captured text. This lets you only
|
|
115
|
+
create the LoggerWindow IF you need to show it, but when you do it
|
|
116
|
+
will have all of the std stream text written after this call.
|
|
117
|
+
headless_callback (callable, optional): Callback that returns a bool
|
|
118
|
+
indicating if PrEditor should attempt to create GUI elements.
|
|
119
|
+
"""
|
|
120
|
+
# The name should always be set first. The logging setting depends on it,
|
|
121
|
+
# and the others may depend on it in the future.
|
|
122
|
+
config.name = name
|
|
123
|
+
config.logging = logging
|
|
124
|
+
# Capture stdout/err streams and install the excepthook so we start capturing
|
|
125
|
+
# output as early as possible if enabled.
|
|
126
|
+
config.streams = streams
|
|
127
|
+
config.excepthook = excepthook
|
|
128
|
+
# These will be used to create the GUI instance if PrEditor needs shown.
|
|
129
|
+
config.parent_callback = parent_callback
|
|
130
|
+
config.headless_callback = headless_callback
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def get_core_name():
|
|
134
|
+
"""Returns the configured core_name or DEFAULT_CORE_NAME."""
|
|
135
|
+
return config.name
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def launch(run_workbox=False, app_id=None, name=None, standalone=False):
|
|
139
|
+
"""Launches the preditor gui creating the QApplication instance if not
|
|
140
|
+
already created.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
modal (bool, optional): If True, preditor's gui will be created as a
|
|
144
|
+
modal window (ie. blocks current code execution while its shown).
|
|
145
|
+
run_workbox (bool, optional): After preditor's gui is shown, run its
|
|
146
|
+
current workbox text.
|
|
147
|
+
app_id (str, optional): Set the QApplication's applicationName to this
|
|
148
|
+
value. This is normally only used when launching a standalone
|
|
149
|
+
instance of the PrEditor gui.
|
|
150
|
+
standalone (bool, optional): Launch PrEditor in standalone mode. This
|
|
151
|
+
enables extra options that only make sense when it is running as
|
|
152
|
+
its own app, not inside of another app.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
preditor.gui.loggerwindow.LoggerWindow: The instance of the PrEditor
|
|
156
|
+
gui that was created.
|
|
157
|
+
"""
|
|
158
|
+
if name is None:
|
|
159
|
+
# If the name wasn't passed we will get it from the name stored when
|
|
160
|
+
# configure was called.
|
|
161
|
+
if config.name is None:
|
|
162
|
+
raise RuntimeError(
|
|
163
|
+
"You call configure before calling launch if not passing name"
|
|
164
|
+
)
|
|
165
|
+
name = config.name
|
|
166
|
+
else:
|
|
167
|
+
# A name was provided, call configure to ensure it has been called
|
|
168
|
+
configure(name=name)
|
|
169
|
+
|
|
170
|
+
from .gui.app import App
|
|
171
|
+
from .gui.loggerwindow import LoggerWindow
|
|
172
|
+
|
|
173
|
+
# Check if we can actually run the PrEditor gui and setup Qt if required
|
|
174
|
+
app = App(name=app_id)
|
|
175
|
+
widget = LoggerWindow.instance(
|
|
176
|
+
run_workbox=run_workbox, name=name, standalone=standalone
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# Show the PrEditor instance, make sure it regains focus, visibility and is
|
|
180
|
+
# raised to the top.
|
|
181
|
+
widget.launch(focus=True)
|
|
182
|
+
app.start()
|
|
183
|
+
|
|
184
|
+
return widget
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def prefPath(relpath, coreName=''):
|
|
188
|
+
# use the core
|
|
189
|
+
if not coreName and core:
|
|
190
|
+
coreName = core.objectName()
|
|
191
|
+
basepath = os.path.join(
|
|
192
|
+
osystem.expandvars(os.environ['BDEV_PATH_PREFS']), 'app_%s/' % coreName
|
|
193
|
+
)
|
|
194
|
+
return os.path.normpath(os.path.join(basepath, relpath))
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def pythonw_print_bugfix():
|
|
198
|
+
"""
|
|
199
|
+
When running pythonw print statements and file handles tend to have problems
|
|
200
|
+
so, if its pythonw and stderr and stdout haven't been redirected, redirect them
|
|
201
|
+
to os.devnull.
|
|
202
|
+
"""
|
|
203
|
+
if os.path.basename(sys.executable) == 'pythonw.exe':
|
|
204
|
+
if sys.stdout == sys.__stdout__:
|
|
205
|
+
sys.stdout = open(os.devnull, 'w')
|
|
206
|
+
if sys.stderr == sys.__stderr__:
|
|
207
|
+
sys.stderr = open(os.devnull, 'w')
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def relativePath(path, additional=''):
|
|
211
|
+
"""
|
|
212
|
+
Replaces the last element in the path with the passed in additional path.
|
|
213
|
+
:param path: Source path. Generally a file name.
|
|
214
|
+
:param additional: Additional folder/file path appended to the path.
|
|
215
|
+
:return str: The modified path
|
|
216
|
+
"""
|
|
217
|
+
return os.path.join(os.path.dirname(path), additional)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def resourcePath(relpath=''):
|
|
221
|
+
"""Returns the full path to the file inside the preditor/resource folder
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
relpath (str, optional): The additional path added to the
|
|
225
|
+
preditor/resource folder path.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
str: The modified path
|
|
229
|
+
"""
|
|
230
|
+
return os.path.join(relativePath(__file__), 'resource', relpath)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def parent_callback():
|
|
234
|
+
"""Returns the parent_callback or None.
|
|
235
|
+
|
|
236
|
+
This is a callback that returns a QWidget to use as the parent of the
|
|
237
|
+
LoggerWindow when its first created. This can be used by DCC's to set the
|
|
238
|
+
parent to their main window.
|
|
239
|
+
"""
|
|
240
|
+
return config.parent_callback
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def connect_preditor(
|
|
244
|
+
parent, sequence='F2', text='Show PrEditor', obj_name='uiShowPreditorACT', name=None
|
|
245
|
+
):
|
|
246
|
+
"""Creates a QAction that shows the PrEditor gui with a keyboard shortcut.
|
|
247
|
+
This will automatically call `preditor.configure` if name is provided,
|
|
248
|
+
capturing any `sys.stdout` and `sys.stderr` writes after this call. This does
|
|
249
|
+
not initialize the PrEditor gui instance until the action is actually called.
|
|
250
|
+
|
|
251
|
+
Args:
|
|
252
|
+
parent: The parent widget, normally a window
|
|
253
|
+
sequence (str, optional): A string representing the keyboard shortcut
|
|
254
|
+
associated with the QAction.
|
|
255
|
+
text (str, optional): The display text for the QAction.
|
|
256
|
+
obj_name (str, optional): Set the QAction's objectName to this value.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
QAction: The created QAction
|
|
260
|
+
"""
|
|
261
|
+
from Qt.QtGui import QKeySequence
|
|
262
|
+
from Qt.QtWidgets import QAction
|
|
263
|
+
|
|
264
|
+
if name:
|
|
265
|
+
# Set the core_name if provided
|
|
266
|
+
configure(name)
|
|
267
|
+
|
|
268
|
+
# Create shortcut for launching the PrEditor gui.
|
|
269
|
+
action = QAction(text, parent)
|
|
270
|
+
action.setObjectName(obj_name)
|
|
271
|
+
action.triggered.connect(launch)
|
|
272
|
+
action.setShortcut(QKeySequence(sequence))
|
|
273
|
+
parent.addAction(action)
|
|
274
|
+
return action
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def instance(parent=None, run_workbox=False, create=True):
|
|
278
|
+
"""Returns the existing instance of the PrEditor gui creating it on first call.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
parent (QWidget, optional): If the instance hasn't been created yet, create
|
|
282
|
+
it and parent it to this object.
|
|
283
|
+
run_workbox (bool, optional): If the instance hasn't been created yet, this
|
|
284
|
+
will execute the active workbox's code once fully initialized.
|
|
285
|
+
create (bool, optional): Returns None if the instance has not been created.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Returns a fully initialized instance of the PrEditor gui. If called more
|
|
289
|
+
than once, the same instance will be returned. If create is False, it may
|
|
290
|
+
return None.
|
|
291
|
+
"""
|
|
292
|
+
return config.instance(parent=parent, run_workbox=run_workbox, create=create)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def shutdown():
|
|
296
|
+
"""Fully close and cleanup the PrEditor gui if it was created.
|
|
297
|
+
|
|
298
|
+
Call this when shutting down your application to ensure any unsaved changes
|
|
299
|
+
to the PrEditor gui are saved and the instance is actually closed instead
|
|
300
|
+
of just hidden.
|
|
301
|
+
|
|
302
|
+
If the PrEditor gui was never created, this does nothing so its safe to call
|
|
303
|
+
even if the user never showed the gui. It also won't add extra time creating
|
|
304
|
+
the gui just so it can "save any changes".
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
bool: If a shutdown was required
|
|
308
|
+
"""
|
|
309
|
+
from .gui.loggerwindow import LoggerWindow
|
|
310
|
+
|
|
311
|
+
return LoggerWindow.instance_shutdown()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
# initialize the core
|
|
315
|
+
init()
|
preditor/__main__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
""" Enables support for calling the preditor cli using `python -m preditor`
|
|
2
|
+
"""
|
|
3
|
+
from __future__ import absolute_import
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
import preditor.cli
|
|
8
|
+
|
|
9
|
+
if __name__ == '__main__':
|
|
10
|
+
# prog_name prevents __main__.py from being shown as the command name in the help
|
|
11
|
+
# text. We don't know the exact command the user passed so we provide a generic
|
|
12
|
+
# `python -m preditor` command.
|
|
13
|
+
sys.exit(preditor.cli.cli(prog_name="python -m preditor"))
|
preditor/about_module.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import textwrap
|
|
7
|
+
|
|
8
|
+
from future.utils import with_metaclass
|
|
9
|
+
|
|
10
|
+
import preditor
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AboutModule(with_metaclass(abc.ABCMeta, object)):
|
|
14
|
+
"""Base class for the `preditor.plug.about_module` entry point. Create a
|
|
15
|
+
subclass of this method and expose the class object to the entry point.
|
|
16
|
+
|
|
17
|
+
Properties:
|
|
18
|
+
instance: If provided, the instance of PrEditor to generate text for.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
indent = " "
|
|
22
|
+
"""Use this to indent new lines for text"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, instance=None):
|
|
25
|
+
self.instance = instance
|
|
26
|
+
|
|
27
|
+
@classmethod
|
|
28
|
+
def generate(cls, instance=None):
|
|
29
|
+
"""Generates the output text for all plugins.
|
|
30
|
+
|
|
31
|
+
Outputs this for each plugin:
|
|
32
|
+
|
|
33
|
+
{name}: {version}
|
|
34
|
+
{indent}{text line 1}
|
|
35
|
+
{indent}{text line ...}
|
|
36
|
+
|
|
37
|
+
Name is the name of each entry point. If duplicates are found, a logging
|
|
38
|
+
warning is generated. Each line of `self.text()` is indented
|
|
39
|
+
"""
|
|
40
|
+
ret = []
|
|
41
|
+
for name, plugin in preditor.plugins.about_module():
|
|
42
|
+
version = "Unknown"
|
|
43
|
+
if isinstance(plugin, str):
|
|
44
|
+
text = plugin
|
|
45
|
+
else:
|
|
46
|
+
try:
|
|
47
|
+
plug = plugin(instance=instance)
|
|
48
|
+
if not plug.enabled():
|
|
49
|
+
continue
|
|
50
|
+
version = plug.version()
|
|
51
|
+
text = plug.text()
|
|
52
|
+
except Exception as error:
|
|
53
|
+
text = "Error processing: {}".format(error)
|
|
54
|
+
text = textwrap.indent(text, cls.indent)
|
|
55
|
+
|
|
56
|
+
# Build the output string including the version information if provided.
|
|
57
|
+
if version is not None:
|
|
58
|
+
ret.append("{}: {}\n{}".format(name, version, text))
|
|
59
|
+
else:
|
|
60
|
+
ret.append("{}:\n{}".format(name, text))
|
|
61
|
+
|
|
62
|
+
return '\n'.join(ret)
|
|
63
|
+
|
|
64
|
+
def enabled(self):
|
|
65
|
+
"""The plugin can use this to disable reporting by generate."""
|
|
66
|
+
return True
|
|
67
|
+
|
|
68
|
+
@abc.abstractmethod
|
|
69
|
+
def text(self):
|
|
70
|
+
"""Returns info about this plugin. This can have multiple lines, and
|
|
71
|
+
each line will be indented to provide visual distinction between plugins.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
@abc.abstractmethod
|
|
75
|
+
def version(self):
|
|
76
|
+
"""Returns The version as a string to show next to name."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class AboutPreditor(AboutModule):
|
|
80
|
+
"""About module used to show info about PrEditor."""
|
|
81
|
+
|
|
82
|
+
def text(self):
|
|
83
|
+
"""Return the path PrEditor was loaded from for quick debugging."""
|
|
84
|
+
ret = []
|
|
85
|
+
# Include the core_name of the current PrEditor gui instance if possible
|
|
86
|
+
if self.instance:
|
|
87
|
+
ret.append("Core Name: {}".format(self.instance.name))
|
|
88
|
+
# THe path to the PrEditor package
|
|
89
|
+
ret.append("Path: {}".format(os.path.dirname(preditor.__file__)))
|
|
90
|
+
return "\n".join(ret)
|
|
91
|
+
|
|
92
|
+
def version(self):
|
|
93
|
+
return preditor.__version__
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AboutQt(AboutModule):
|
|
97
|
+
"""Info about Qt modules being used."""
|
|
98
|
+
|
|
99
|
+
def text(self):
|
|
100
|
+
"""Return the path PrEditor was loaded from for quick debugging."""
|
|
101
|
+
from Qt import QtCore, __binding__, __version__
|
|
102
|
+
|
|
103
|
+
ret = ['Qt.py: {}, binding: {}'.format(__version__, __binding__)]
|
|
104
|
+
|
|
105
|
+
# Attempt to get a version for QtSiteConfig if defined
|
|
106
|
+
try:
|
|
107
|
+
import QtSiteConfig
|
|
108
|
+
|
|
109
|
+
ret.append('QtSiteConfig: {}'.format(QtSiteConfig.__version__))
|
|
110
|
+
except (ImportError, AttributeError):
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
# Add info for all Qt5 bindings that have been imported
|
|
114
|
+
if 'PySide6.QtCore' in sys.modules:
|
|
115
|
+
ret.append('PySide6: {}'.format(sys.modules['PySide6.QtCore'].qVersion()))
|
|
116
|
+
if 'PyQt6.QtCore' in sys.modules:
|
|
117
|
+
ret.append('PyQt6: {}'.format(sys.modules['PyQt6.QtCore'].PYQT_VERSION_STR))
|
|
118
|
+
if 'PySide2.QtCore' in sys.modules:
|
|
119
|
+
ret.append('PySide2: {}'.format(sys.modules['PySide2.QtCore'].qVersion()))
|
|
120
|
+
if 'PyQt5.QtCore' in sys.modules:
|
|
121
|
+
ret.append('PyQt5: {}'.format(sys.modules['PyQt5.QtCore'].PYQT_VERSION_STR))
|
|
122
|
+
|
|
123
|
+
# Add qt library paths for plugin debugging
|
|
124
|
+
for i, path in enumerate(QtCore.QCoreApplication.libraryPaths()):
|
|
125
|
+
if i == 0:
|
|
126
|
+
ret.append('Library Paths: {}'.format(path))
|
|
127
|
+
else:
|
|
128
|
+
ret.append(' {}'.format(path))
|
|
129
|
+
|
|
130
|
+
return "\n".join(ret)
|
|
131
|
+
|
|
132
|
+
def version(self):
|
|
133
|
+
from Qt import __qt_version__
|
|
134
|
+
|
|
135
|
+
return __qt_version__
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class AboutPython(AboutModule):
|
|
139
|
+
"""Info about the current instance of python."""
|
|
140
|
+
|
|
141
|
+
def text(self):
|
|
142
|
+
"""Return the path PrEditor was loaded from for quick debugging."""
|
|
143
|
+
ret = sys.version
|
|
144
|
+
# Windows doesn't add a newline before the compiler info, and it can end
|
|
145
|
+
# up being a little long for QMessageBox's with short file paths. Add
|
|
146
|
+
# the newline like is present on linux
|
|
147
|
+
ret = ret.replace(") [", ")\n[")
|
|
148
|
+
return ret
|
|
149
|
+
|
|
150
|
+
def version(self):
|
|
151
|
+
return '{}.{}.{}'.format(*sys.version_info[:3])
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class AboutExe(AboutModule):
|
|
155
|
+
"""The value of sys.executable, disabled if not set."""
|
|
156
|
+
|
|
157
|
+
def enabled(self):
|
|
158
|
+
return bool(sys.executable)
|
|
159
|
+
|
|
160
|
+
def text(self):
|
|
161
|
+
return sys.executable
|
|
162
|
+
|
|
163
|
+
def version(self):
|
|
164
|
+
"""No version is returned for this class."""
|
|
165
|
+
return None
|
preditor/cli.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from distutils.spawn import find_executable
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from click.core import ParameterSource
|
|
9
|
+
from click_default_group import DefaultGroup
|
|
10
|
+
|
|
11
|
+
import preditor
|
|
12
|
+
import preditor.prefs
|
|
13
|
+
from preditor.settings import OS_TYPE
|
|
14
|
+
|
|
15
|
+
# No one wants to actually type `--help`
|
|
16
|
+
CONTEXT_SETTINGS = dict(help_option_names=["-h", "--help"])
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_app_id(name, is_default):
|
|
22
|
+
"""Returns the name to use for the app_id of windows shortcuts.
|
|
23
|
+
This allows for taskbar/start menu pinning.
|
|
24
|
+
"""
|
|
25
|
+
if is_default:
|
|
26
|
+
# If not using a custom name, just use it for the app_id
|
|
27
|
+
return name
|
|
28
|
+
# Otherwise use a prefix of the name provided by the user
|
|
29
|
+
return "PrEditor - {}".format(name)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@click.group(
|
|
33
|
+
cls=DefaultGroup,
|
|
34
|
+
default='launch',
|
|
35
|
+
default_if_no_args=True,
|
|
36
|
+
context_settings=CONTEXT_SETTINGS,
|
|
37
|
+
)
|
|
38
|
+
def cli():
|
|
39
|
+
"""PrEditor is a Qt based python console and code editor. It runs in many
|
|
40
|
+
DCC's like Maya, Houdini, Nuke, and 3ds Max.
|
|
41
|
+
|
|
42
|
+
To see help for launching the PrEditor gui, use `preditor launch -h`.
|
|
43
|
+
"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# launch
|
|
48
|
+
@cli.command()
|
|
49
|
+
@click.option(
|
|
50
|
+
"-n",
|
|
51
|
+
"--name",
|
|
52
|
+
default=preditor.DEFAULT_CORE_NAME,
|
|
53
|
+
envvar="PREDITOR_NAME",
|
|
54
|
+
help="Name to save preferences with. This allows you to open multiple "
|
|
55
|
+
"instances with their own code and settings.",
|
|
56
|
+
)
|
|
57
|
+
@click.option(
|
|
58
|
+
'-r',
|
|
59
|
+
'--run-workbox',
|
|
60
|
+
is_flag=True,
|
|
61
|
+
help='After the logger is shown, run the current workbox text.',
|
|
62
|
+
)
|
|
63
|
+
def launch(name, run_workbox):
|
|
64
|
+
"""Run the PrEditor console's gui."""
|
|
65
|
+
# Check if the user passed the name or it was the default
|
|
66
|
+
parameter_source = click.get_current_context().get_parameter_source('name')
|
|
67
|
+
app_id = get_app_id(name, parameter_source == ParameterSource.DEFAULT)
|
|
68
|
+
|
|
69
|
+
preditor.launch(run_workbox=run_workbox, app_id=app_id, name=name, standalone=True)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# shortcut
|
|
73
|
+
@cli.command()
|
|
74
|
+
@click.argument("path")
|
|
75
|
+
@click.option(
|
|
76
|
+
"-p",
|
|
77
|
+
"--public",
|
|
78
|
+
is_flag=True,
|
|
79
|
+
help='If using "start-menu" or "desktop" for path, create the shortcut '
|
|
80
|
+
'in the public location not the user location. This may require '
|
|
81
|
+
'administrative privileges.',
|
|
82
|
+
)
|
|
83
|
+
@click.option(
|
|
84
|
+
"-n",
|
|
85
|
+
"--name",
|
|
86
|
+
default=preditor.DEFAULT_CORE_NAME,
|
|
87
|
+
envvar="PREDITOR_NAME",
|
|
88
|
+
help="Name to save preferences with. This allows you to open multiple "
|
|
89
|
+
"instances with their own code and settings.",
|
|
90
|
+
)
|
|
91
|
+
@click.option(
|
|
92
|
+
"--target",
|
|
93
|
+
default="preditorw",
|
|
94
|
+
help='The command the shortcut will run. "preditor" or "preditorw" are '
|
|
95
|
+
'converted to the full path to the exe.',
|
|
96
|
+
)
|
|
97
|
+
@click.option(
|
|
98
|
+
'--args',
|
|
99
|
+
default="",
|
|
100
|
+
help="The command the shortcut will run.",
|
|
101
|
+
)
|
|
102
|
+
@click.option(
|
|
103
|
+
'--description',
|
|
104
|
+
default='Opens PrEditor',
|
|
105
|
+
help="The description to give the shortcut.",
|
|
106
|
+
)
|
|
107
|
+
def shortcut(path, public, name, target, args, description):
|
|
108
|
+
"""Create a shortcut to launch PrEditor.
|
|
109
|
+
|
|
110
|
+
Path is a the full path of the shortcut to create. On windows you can
|
|
111
|
+
pass "start-menu" to create the shortcut in the start menu.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
if OS_TYPE != "Windows":
|
|
115
|
+
click.echo("Creating a shortcut is currently only supported on Windows.")
|
|
116
|
+
sys.exit(1)
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
from casement.shortcut import Shortcut
|
|
120
|
+
except ImportError:
|
|
121
|
+
click.echo(
|
|
122
|
+
"Unable to import casement use `pip install casement` to enable "
|
|
123
|
+
"creating shortcuts on windows."
|
|
124
|
+
)
|
|
125
|
+
sys.exit(1)
|
|
126
|
+
|
|
127
|
+
if path in ("desktop", "start-menu"):
|
|
128
|
+
path = [path]
|
|
129
|
+
|
|
130
|
+
# Resolve the full path to the preditor exe.
|
|
131
|
+
if target in ("preditor", "preditorw"):
|
|
132
|
+
target = find_executable(target)
|
|
133
|
+
|
|
134
|
+
parameter_source = click.get_current_context().get_parameter_source('name')
|
|
135
|
+
if parameter_source == ParameterSource.DEFAULT:
|
|
136
|
+
app_id = name
|
|
137
|
+
else:
|
|
138
|
+
# Strip off the leading "launch " command argument if it was passed
|
|
139
|
+
if args.startswith('launch '):
|
|
140
|
+
args = args[7:]
|
|
141
|
+
# Pass the name to the launched PrEditor instance
|
|
142
|
+
args = 'launch --name "{}" {}'.format(name, args)
|
|
143
|
+
app_id = name = get_app_id(name, parameter_source == ParameterSource.DEFAULT)
|
|
144
|
+
|
|
145
|
+
Shortcut.create(
|
|
146
|
+
name,
|
|
147
|
+
args,
|
|
148
|
+
target=target,
|
|
149
|
+
# icon=None,
|
|
150
|
+
icon_source=preditor.resourcePath('img/preditor.ico'),
|
|
151
|
+
icon_filename="preditor",
|
|
152
|
+
path=path,
|
|
153
|
+
description=description,
|
|
154
|
+
common=int(public),
|
|
155
|
+
app_id=app_id,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
# prefs
|
|
160
|
+
@cli.group()
|
|
161
|
+
def prefs():
|
|
162
|
+
"""PrEditor preference management."""
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@prefs.command()
|
|
167
|
+
def backup():
|
|
168
|
+
"""Backup all of preferences inside a zip file."""
|
|
169
|
+
zip_path = preditor.prefs.backup()
|
|
170
|
+
click.echo('PrEditor Preferences backed up to "{}"'.format(zip_path))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@prefs.command()
|
|
174
|
+
@click.argument("name", default="", required=False)
|
|
175
|
+
def browse(name):
|
|
176
|
+
"""Open a file explorer to the preference directory. Optionally pass the
|
|
177
|
+
name of a specific preference.
|
|
178
|
+
"""
|
|
179
|
+
preditor.prefs.browse(name)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@prefs.command()
|
|
183
|
+
def list():
|
|
184
|
+
"""List the core_names for existing preferences."""
|
|
185
|
+
click.echo("Existing pref names:")
|
|
186
|
+
click.echo("--------------------")
|
|
187
|
+
for pref in preditor.prefs.existing():
|
|
188
|
+
click.echo(pref)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
if __name__ == '__main__':
|
|
192
|
+
cli()
|