PrEditor 1.0.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.

Potentially problematic release.


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

Files changed (158) hide show
  1. preditor/__init__.py +322 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +161 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +302 -0
  6. preditor/contexts.py +119 -0
  7. preditor/cores/__init__.py +0 -0
  8. preditor/cores/core.py +20 -0
  9. preditor/dccs/maya/PrEditor_maya.mod +2 -0
  10. preditor/dccs/maya/plug-ins/PrEditor_maya.py +110 -0
  11. preditor/debug.py +144 -0
  12. preditor/delayable_engine/__init__.py +302 -0
  13. preditor/delayable_engine/delayables.py +85 -0
  14. preditor/enum.py +728 -0
  15. preditor/excepthooks.py +131 -0
  16. preditor/gui/__init__.py +93 -0
  17. preditor/gui/app.py +160 -0
  18. preditor/gui/codehighlighter.py +209 -0
  19. preditor/gui/completer.py +226 -0
  20. preditor/gui/console.py +867 -0
  21. preditor/gui/dialog.py +178 -0
  22. preditor/gui/drag_tab_bar.py +190 -0
  23. preditor/gui/editor_chooser.py +57 -0
  24. preditor/gui/errordialog.py +68 -0
  25. preditor/gui/find_files.py +125 -0
  26. preditor/gui/fuzzy_search/__init__.py +0 -0
  27. preditor/gui/fuzzy_search/fuzzy_search.py +93 -0
  28. preditor/gui/group_tab_widget/__init__.py +325 -0
  29. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  30. preditor/gui/group_tab_widget/grouped_tab_models.py +108 -0
  31. preditor/gui/group_tab_widget/grouped_tab_widget.py +78 -0
  32. preditor/gui/group_tab_widget/one_tab_widget.py +54 -0
  33. preditor/gui/level_buttons.py +343 -0
  34. preditor/gui/logger_window_handler.py +48 -0
  35. preditor/gui/logger_window_plugin.py +32 -0
  36. preditor/gui/loggerwindow.py +1385 -0
  37. preditor/gui/newtabwidget.py +69 -0
  38. preditor/gui/set_text_editor_path_dialog.py +59 -0
  39. preditor/gui/status_label.py +99 -0
  40. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  41. preditor/gui/ui/editor_chooser.ui +93 -0
  42. preditor/gui/ui/errordialog.ui +74 -0
  43. preditor/gui/ui/find_files.ui +140 -0
  44. preditor/gui/ui/loggerwindow.ui +1105 -0
  45. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  46. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  47. preditor/gui/window.py +161 -0
  48. preditor/gui/workbox_mixin.py +389 -0
  49. preditor/gui/workbox_text_edit.py +137 -0
  50. preditor/gui/workboxwidget.py +298 -0
  51. preditor/logging_config.py +52 -0
  52. preditor/osystem.py +401 -0
  53. preditor/plugins.py +118 -0
  54. preditor/prefs.py +74 -0
  55. preditor/resource/environment_variables.html +26 -0
  56. preditor/resource/error_mail.html +85 -0
  57. preditor/resource/error_mail_inline.html +41 -0
  58. preditor/resource/img/README.md +17 -0
  59. preditor/resource/img/arrow_forward.png +0 -0
  60. preditor/resource/img/check-bold.png +0 -0
  61. preditor/resource/img/chevron-down.png +0 -0
  62. preditor/resource/img/chevron-up.png +0 -0
  63. preditor/resource/img/close-thick.png +0 -0
  64. preditor/resource/img/comment-edit.png +0 -0
  65. preditor/resource/img/content-copy.png +0 -0
  66. preditor/resource/img/content-cut.png +0 -0
  67. preditor/resource/img/content-duplicate.png +0 -0
  68. preditor/resource/img/content-paste.png +0 -0
  69. preditor/resource/img/content-save.png +0 -0
  70. preditor/resource/img/debug_disabled.png +0 -0
  71. preditor/resource/img/eye-check.png +0 -0
  72. preditor/resource/img/file-plus.png +0 -0
  73. preditor/resource/img/file-remove.png +0 -0
  74. preditor/resource/img/format-align-left.png +0 -0
  75. preditor/resource/img/format-letter-case-lower.png +0 -0
  76. preditor/resource/img/format-letter-case-upper.png +0 -0
  77. preditor/resource/img/format-letter-case.svg +1 -0
  78. preditor/resource/img/information.png +0 -0
  79. preditor/resource/img/logging_critical.png +0 -0
  80. preditor/resource/img/logging_custom.png +0 -0
  81. preditor/resource/img/logging_debug.png +0 -0
  82. preditor/resource/img/logging_error.png +0 -0
  83. preditor/resource/img/logging_info.png +0 -0
  84. preditor/resource/img/logging_not_set.png +0 -0
  85. preditor/resource/img/logging_warning.png +0 -0
  86. preditor/resource/img/marker.png +0 -0
  87. preditor/resource/img/play.png +0 -0
  88. preditor/resource/img/playlist-play.png +0 -0
  89. preditor/resource/img/plus-minus-variant.png +0 -0
  90. preditor/resource/img/preditor.ico +0 -0
  91. preditor/resource/img/preditor.png +0 -0
  92. preditor/resource/img/preditor.psd +0 -0
  93. preditor/resource/img/preditor.svg +44 -0
  94. preditor/resource/img/regex.svg +1 -0
  95. preditor/resource/img/restart.svg +1 -0
  96. preditor/resource/img/skip-forward-outline.png +0 -0
  97. preditor/resource/img/skip-next-outline.png +0 -0
  98. preditor/resource/img/skip-next.png +0 -0
  99. preditor/resource/img/skip-previous.png +0 -0
  100. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  101. preditor/resource/img/text-search-variant.png +0 -0
  102. preditor/resource/img/warning-big.png +0 -0
  103. preditor/resource/lang/python.json +30 -0
  104. preditor/resource/settings.ini +25 -0
  105. preditor/resource/stylesheet/Bright.css +65 -0
  106. preditor/resource/stylesheet/Dark.css +199 -0
  107. preditor/scintilla/__init__.py +22 -0
  108. preditor/scintilla/delayables/__init__.py +11 -0
  109. preditor/scintilla/delayables/smart_highlight.py +94 -0
  110. preditor/scintilla/delayables/spell_check.py +173 -0
  111. preditor/scintilla/documenteditor.py +2038 -0
  112. preditor/scintilla/finddialog.py +68 -0
  113. preditor/scintilla/lang/__init__.py +80 -0
  114. preditor/scintilla/lang/config/bash.ini +15 -0
  115. preditor/scintilla/lang/config/batch.ini +14 -0
  116. preditor/scintilla/lang/config/cpp.ini +19 -0
  117. preditor/scintilla/lang/config/css.ini +19 -0
  118. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  119. preditor/scintilla/lang/config/html.ini +21 -0
  120. preditor/scintilla/lang/config/javascript.ini +24 -0
  121. preditor/scintilla/lang/config/lua.ini +16 -0
  122. preditor/scintilla/lang/config/maxscript.ini +20 -0
  123. preditor/scintilla/lang/config/mel.ini +18 -0
  124. preditor/scintilla/lang/config/mu.ini +22 -0
  125. preditor/scintilla/lang/config/nsi.ini +19 -0
  126. preditor/scintilla/lang/config/perl.ini +19 -0
  127. preditor/scintilla/lang/config/puppet.ini +19 -0
  128. preditor/scintilla/lang/config/python.ini +28 -0
  129. preditor/scintilla/lang/config/ruby.ini +19 -0
  130. preditor/scintilla/lang/config/sql.ini +7 -0
  131. preditor/scintilla/lang/config/xml.ini +21 -0
  132. preditor/scintilla/lang/config/yaml.ini +18 -0
  133. preditor/scintilla/lang/language.py +240 -0
  134. preditor/scintilla/lexers/__init__.py +0 -0
  135. preditor/scintilla/lexers/cpplexer.py +21 -0
  136. preditor/scintilla/lexers/javascriptlexer.py +25 -0
  137. preditor/scintilla/lexers/maxscriptlexer.py +234 -0
  138. preditor/scintilla/lexers/mellexer.py +368 -0
  139. preditor/scintilla/lexers/mulexer.py +32 -0
  140. preditor/scintilla/lexers/pythonlexer.py +41 -0
  141. preditor/scintilla/ui/finddialog.ui +160 -0
  142. preditor/settings.py +71 -0
  143. preditor/stream/__init__.py +80 -0
  144. preditor/stream/director.py +73 -0
  145. preditor/stream/manager.py +74 -0
  146. preditor/streamhandler_helper.py +46 -0
  147. preditor/utils/__init__.py +0 -0
  148. preditor/utils/cute.py +30 -0
  149. preditor/utils/stylesheets.py +54 -0
  150. preditor/utils/text_search.py +342 -0
  151. preditor/version.py +21 -0
  152. preditor/weakref.py +363 -0
  153. preditor-1.0.0.dist-info/METADATA +224 -0
  154. preditor-1.0.0.dist-info/RECORD +158 -0
  155. preditor-1.0.0.dist-info/WHEEL +5 -0
  156. preditor-1.0.0.dist-info/entry_points.txt +18 -0
  157. preditor-1.0.0.dist-info/licenses/LICENSE +165 -0
  158. preditor-1.0.0.dist-info/top_level.txt +1 -0
preditor/config.py ADDED
@@ -0,0 +1,302 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+ from functools import wraps
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ def set_if_unlocked(track_state=False):
10
+ def _set_if_unlocked(func):
11
+ """Decorate property setter functions to prevent editing after locking."""
12
+
13
+ @wraps(func)
14
+ def wrapper(self, value):
15
+ if not self.is_locked(func.__name__, track_state=track_state):
16
+ # logger.debug(f'"{func.__name__}" is unlocked and can be set: {value}')
17
+ return func(self, value)
18
+ # logger.info(
19
+ # f'"{func.__name__}" is locked and can no-longer be set: {value}'
20
+ # )
21
+
22
+ return wrapper
23
+
24
+ return _set_if_unlocked
25
+
26
+
27
+ class PreditorConfig:
28
+ """Global configuration of PrEditor's instance.
29
+
30
+ When creating the main PrEditor instance it will use `preditor.config` to
31
+ control how its initialized. That stores an instance of this class.
32
+
33
+ Once `preditor.instance(create=True)` the properties on this class will then
34
+ silently ignore any attempts to set most of its properties. Where noted in
35
+ the property doc-strings, some properties are used to install a setting that
36
+ can't be automatically undone, so they will stop responding after the first
37
+ setting.
38
+
39
+ While this class does not force a singleton pattern, it is expected in normal
40
+ operation that there will only ever be a single instance made and it is stored
41
+ on `preditor.config` when first imported. Things like excepthook and streams
42
+ modify python's state which could lead to duplicate outputs, multiple error
43
+ prompts or other undesirable behavior.
44
+ """
45
+
46
+ def __init__(self):
47
+ self._locks = {}
48
+ self._name = None
49
+ self._logging = False
50
+ self._headless_callback = None
51
+ self._parent_callback = None
52
+ self._error_dialog_class = True
53
+ self._excepthooks = []
54
+
55
+ def dump(self, indent=0):
56
+ """A convenient way to inspect the current configuration."""
57
+ ret = [
58
+ f"{' ' * indent}name: {self.name}",
59
+ f"{' ' * indent}streams: {self.streams}",
60
+ f"{' ' * indent}excepthook: {self.excepthook!r}",
61
+ f"{' ' * indent}excepthooks: {self.excepthooks!r}",
62
+ f"{' ' * indent}error_dialog_class: {self.error_dialog_class!r}",
63
+ f"{' ' * indent}logging: {self.logging}",
64
+ f"{' ' * indent}headless_callback: {self.headless_callback!r}",
65
+ f"{' ' * indent}parent_callback: {self.parent_callback!r}",
66
+ ]
67
+ return '\n'.join(ret)
68
+
69
+ def is_locked(self, value, track_state=False):
70
+ """Returns if value is locked and can not be updated.
71
+
72
+ Once `preditor.instance` returns a value this will always return False.
73
+ The config settings should not be modified once the instance is created.
74
+
75
+ Args:
76
+ value (str): The name of the value to check. Should match a property
77
+ name on this class.
78
+ track_state (bool, optional): If True then also check if value has
79
+ been flagged as locked. This indicates that it was already set
80
+ by a previous call and can no longer be modified even if instance
81
+ is False.
82
+ """
83
+ if self.instance(create=False):
84
+ # if the instance is created all items are locked
85
+ return True
86
+ if track_state:
87
+ return self._locks.get(value, False)
88
+ return False
89
+
90
+ @property
91
+ def error_dialog_class(self):
92
+ """Dialog class shown if PrEditor isn't visible and a error happens.
93
+
94
+ This dialog is used to prompt the user to show PrEditor and can be
95
+ sub-classed to add extra functionality. This is called by
96
+ `PreditorExceptHook.ask_to_show_logger` method when added to
97
+ `preditor.config.excepthooks`.
98
+
99
+ If set to `True` then `blurdev.gui.errordialog.ErrorDialog` is used. When
100
+ replacing this, it should be a sub-class of that class or re-implement
101
+ its api.
102
+
103
+ You can use an EntryPoint string like `preditor.gui.errordialog:ErrorDialog`
104
+ instead of passing the actual class object. This lets you delay the import
105
+ until actually needed.
106
+ """
107
+ return self._error_dialog_class
108
+
109
+ @error_dialog_class.setter
110
+ def error_dialog_class(self, cls):
111
+ self._error_dialog_class = cls
112
+
113
+ @property
114
+ def excepthook(self):
115
+ """Installs a `sys.excepthook` handler when first set to True.
116
+
117
+ Replaces `sys.excepthook` with a interactive exception handler that
118
+ prompts the user to show PrEditor when an python exception is raised.
119
+ It is recommended that you only add the excepthook once the Qt UI is
120
+ initialized. See: :py:class:`preditor.excepthooks.PreditorExceptHook`.
121
+ """
122
+ return self._locks.get("excepthook", False)
123
+
124
+ @excepthook.setter
125
+ @set_if_unlocked(track_state=True)
126
+ def excepthook(self, value):
127
+ if not value:
128
+ return
129
+
130
+ # Install the excepthook:
131
+ import preditor.excepthooks
132
+
133
+ # Note: install checks if the current excepthook is a instance of this
134
+ # class and prevents installing a second time.
135
+ preditor.excepthooks.PreditorExceptHook.install()
136
+
137
+ # Disable future setting via `set_if_unlocked`, the `PreditorExceptHook`
138
+ # is a chaining except hook that calls the previous excepthook when called.
139
+ # We don't want to install multiple automatically, the user can define that
140
+ # logic if required.
141
+ self._locks["excepthook"] = True
142
+
143
+ @property
144
+ def excepthooks(self):
145
+ """A list of callables that are called when an exception is handled.
146
+
147
+ If `excepthook` is enabled installing `PreditorExceptHook` then it will
148
+ call each item in this list. The signature of the function should be
149
+ `callable(*args)`. If not configured it will automatically install default
150
+ callables. You can add `None` to disable this.
151
+ """
152
+ return self._excepthooks
153
+
154
+ @property
155
+ def headless_callback(self):
156
+ """A pointer to a method that is called by `is_headless`.
157
+
158
+ This callback returns a bool indicating if PrEditor should attempt to
159
+ create GUI elements. Application integrations can set this callback to
160
+ give them control over what PrEditor gets parent to.
161
+ """
162
+ return self._headless_callback
163
+
164
+ @headless_callback.setter
165
+ @set_if_unlocked()
166
+ def headless_callback(self, cb):
167
+ self._headless_callback = cb
168
+
169
+ @classmethod
170
+ def instance(cls, parent=None, run_workbox=False, create=True):
171
+ """Returns the existing instance of the PrEditor gui creating it on first call.
172
+
173
+ Args:
174
+ parent (QWidget, optional): If the instance hasn't been created yet, create
175
+ it and parent it to this object.
176
+ run_workbox (bool, optional): If the instance hasn't been created yet, this
177
+ will execute the active workbox's code once fully initialized.
178
+ create (bool, optional): Returns None if the instance has not been created.
179
+
180
+ Returns:
181
+ Returns a fully initialized instance of the PrEditor gui. If called more
182
+ than once, the same instance will be returned. If create is False, it may
183
+ return None.
184
+ """
185
+ from .gui.loggerwindow import LoggerWindow
186
+
187
+ return LoggerWindow.instance(
188
+ parent=parent, run_workbox=run_workbox, create=create
189
+ )
190
+
191
+ def is_headless(self):
192
+ """Returns True if PrEditor should not create GUI items.
193
+
194
+ Returns None if the `headless_callback` has not been configured. Otherwise
195
+ returns the result of calling `headless_callback()` which should return a bool.
196
+ """
197
+ if not self.headless_callback:
198
+ return None
199
+ return self.headless_callback()
200
+
201
+ @property
202
+ def logging(self):
203
+ """Restore the python logging configuration settings that were recorded
204
+ the last time PrEditor prefs were saved.
205
+
206
+ If called multiple times with different name's before the instance is
207
+ created, this will reset the logging configuration from the previous
208
+ name if logging prefs exist.
209
+ """
210
+ return self._logging
211
+
212
+ @logging.setter
213
+ def logging(self, state):
214
+ self._logging = state
215
+ self.update_logging()
216
+
217
+ @property
218
+ def name(self):
219
+ """The name to use for the global instance of PrEditor.
220
+
221
+ Once this has been set, you can call `preditor.launch` without passing
222
+ name to access the main instance. The name controls what preferences
223
+ are loaded and used by PrEditor including the workbox tabs."""
224
+ return self._name
225
+
226
+ @name.setter
227
+ @set_if_unlocked()
228
+ def name(self, name):
229
+ changed = self.name != name
230
+ self._name = name
231
+
232
+ if changed:
233
+ # If the core name was changed attempt to update the logging config.
234
+ self.update_logging()
235
+
236
+ @property
237
+ def parent_callback(self):
238
+ """A pointer to a method that is called by `self.root_window`.
239
+
240
+ This callback returns a QWidget to use as the parent of the LoggerWindow
241
+ when the instance is first created. This can be used by DCC's to set the
242
+ parent to their main window."""
243
+ return self._parent_callback
244
+
245
+ @parent_callback.setter
246
+ @set_if_unlocked()
247
+ def parent_callback(self, cb):
248
+ self._parent_callback = cb
249
+
250
+ def root_window(self):
251
+ """The QWidget to use as the parent of PrEditor widgets."""
252
+ # If a parent widget callback was configured, use it
253
+ if self.parent_callback is not None:
254
+ return self.parent_callback()
255
+
256
+ # Otherwise, attempt to find the top level widget from Qt
257
+ from .gui.app import App
258
+
259
+ return App.root_window()
260
+
261
+ @property
262
+ def streams(self):
263
+ """Installs the stream managers when first set to True.
264
+
265
+ Install the stream manager to capture any stdout/stderr text written.
266
+ Later when calling launch, the LoggerWindow will show all of the captured
267
+ text. This lets you only create the LoggerWindow IF you need to show it,
268
+ but when you do it will have all of the std stream text written after
269
+ this is enabled."""
270
+ return self._locks.get("streams", False)
271
+
272
+ @streams.setter
273
+ @set_if_unlocked(track_state=True)
274
+ def streams(self, value):
275
+ if not value:
276
+ return
277
+
278
+ # Install the stream manager to capture output
279
+ from .stream import install_to_std
280
+
281
+ install_to_std()
282
+
283
+ # Disable re-installing streams.
284
+ self._locks["streams"] = True
285
+
286
+ def update_logging(self):
287
+ """Install/replace the logging config.
288
+
289
+ This does nothing unless `logging` is set to True and `name` is set.
290
+
291
+ Note: When called repeatedly, this won't remove old logger's added by
292
+ previous calls so you may see some loggers added that were never
293
+ actually added by code other than the LoggingConfig.
294
+ """
295
+
296
+ if not (self.logging and self.name):
297
+ return
298
+
299
+ from .logging_config import LoggingConfig
300
+
301
+ cfg = LoggingConfig(core_name=self.name)
302
+ cfg.load()
preditor/contexts.py ADDED
@@ -0,0 +1,119 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import logging
4
+
5
+ _LOGGER = logging.getLogger(__name__)
6
+
7
+
8
+ class ErrorReport(object):
9
+ """Allows you to provide additional debug info if a error happens in this context.
10
+
11
+ PrEditor can send a error email when any python error is raised.
12
+ Sometimes just a traceback does not provide enough information to debug the
13
+ traceback. This class allows you to provide additional information to the error
14
+ report only if it is generated. For example if your treegrunt environment does not
15
+ have a email setup, or the current debug level is not set to Disabled.
16
+
17
+ ErrorReport can be used as a with context, or as a function decorator.
18
+
19
+ Examples:
20
+ This example shows a class using both the with context and a decorated method.
21
+
22
+ from preditor.contexts import ErrorReport
23
+ class Test(object):
24
+ def __init__(self):
25
+ self.value = None
26
+ def errorInfo(self):
27
+ # The text returned by this function will be included in the error email
28
+ return 'Info about the Test class: {}'.format(self.value)
29
+ def doStuff(self):
30
+ with ErrorReport(self.errorInfo, 'Test.doStuff'):
31
+ self.value = 'doStuff'
32
+ raise RuntimeError("BILL")
33
+ @ErrorReport(errorInfo, 'Test.doMoreStuff')
34
+ def doMoreStuff(self):
35
+ self.value = 'doMoreStuff'
36
+ raise RuntimeError("BOB")
37
+
38
+ Using this class does not initialize the Python Logger, so you don't need to worry
39
+ if your class is running headless and not use this class. However unless you set up
40
+ your own error reporting system the callbacks will not be called and nothing will be
41
+ reported.
42
+
43
+ If you want to set up your own error reporting system you need to set
44
+ `ErrorReport.enabled = True`. Then you will need to call ErrorReport.clearReports()
45
+ any time excepthook is called. This prevents a buildup of all error reports any time
46
+ a exception occurs. It should always be in place when you set enabled == True to
47
+ prevent wasting memory. Calling ErrorReport.generateReport() will return the info
48
+ you should include in your report. Calling generateReport is optional, but must be
49
+ called before calling clearReports.
50
+
51
+ Args:
52
+ callback (function): If a exception happens this function is called and its
53
+ returned value is added to the error email if sent. No arguments are passed
54
+ to this function and it is expected to only return a string.
55
+ title (str, optional): This short string is added to the title of the
56
+ ErrorReport.
57
+ Attributes:
58
+ enabled (bool): If False(the default), then all callbacks are cleared even if
59
+ there is a exception. This is used to prevent these functions from leaking
60
+ memory if there isn't a excepthook calling clearReports.
61
+ """
62
+
63
+ __reports__ = []
64
+ enabled = False
65
+
66
+ def __init__(self, callback, title=''):
67
+ self._callback = callback
68
+ self._title = title
69
+
70
+ def __call__(self, funct):
71
+ def wrapper(wrappedSelf, *args, **kwargs):
72
+ unbound = self._callback
73
+ self._callback = self._callback.__get__(wrappedSelf)
74
+ try:
75
+ with self:
76
+ return funct(wrappedSelf, *args, **kwargs)
77
+ finally:
78
+ self._callback = unbound
79
+
80
+ return wrapper
81
+
82
+ def __enter__(self):
83
+ type(self).__reports__.append((self._title, self._callback))
84
+
85
+ def __exit__(self, exc_type, exc_val, exc_tb):
86
+ # If exc_type is None, then no exception was raised, so we should remove the
87
+ # callback. If cls.enabled is False, then nothing has set itself up to call
88
+ # clearReports. We need to remove the callback so it doesn't stay in memory.
89
+ if exc_type is None or not type(self).enabled:
90
+ type(self).__reports__.remove((self._title, self._callback))
91
+
92
+ @classmethod
93
+ def clearReports(cls):
94
+ """Removes all of the currently stored callbacks.
95
+
96
+ This should be called after all error reporting is finished, or if a error
97
+ happened and there is nothing to report it. If you set cls.enabled to True,
98
+ something in excepthook should call this to prevent keeping refrences to
99
+ functions from staying in memory.
100
+ """
101
+ cls.__reports__ = []
102
+
103
+ @classmethod
104
+ def generateReport(cls, fmt='{result}'):
105
+ """Executes and returns all of the currently stored callbacks.
106
+ Args:
107
+
108
+ ftm (str, Optional): The results of the callbacks will be inserted into this
109
+ string using str.format into {results}.
110
+ Returns:
111
+ list: A list of tuples for all active ErrorReport classes. The tuples
112
+ contain two strings; the title string, and result of the passed in
113
+ callback function.
114
+ """
115
+ ret = []
116
+ for title, callback in cls.__reports__:
117
+ result = callback()
118
+ ret.append((title, fmt.format(result=result)))
119
+ return ret
File without changes
preditor/cores/core.py ADDED
@@ -0,0 +1,20 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ from Qt.QtCore import QObject
4
+
5
+
6
+ class Core(QObject):
7
+ """
8
+ The Core class provides all the main shared functionality and signals that need to
9
+ be distributed between different pacakges.
10
+ """
11
+
12
+ def __init__(self, objectName=None):
13
+ super(Core, self).__init__()
14
+ if objectName is None:
15
+ objectName = 'PrEditor'
16
+ self.setObjectName(objectName)
17
+
18
+ # Paths in this variable will be removed in
19
+ # preditor.osystem.subprocessEnvironment
20
+ self._removeFromPATHEnv = set()
@@ -0,0 +1,2 @@
1
+ + PrEditor DEVELOPMENT .
2
+ PYTHONPATH +:= ../..
@@ -0,0 +1,110 @@
1
+ from __future__ import absolute_import
2
+
3
+ import maya.mel
4
+ from maya import OpenMayaUI, cmds
5
+
6
+ preditor_menu = None
7
+
8
+
9
+ def headless():
10
+ """If true, no Qt gui elements should be used because python is running a
11
+ QCoreApplication."""
12
+ return bool(cmds.about(batch=True))
13
+
14
+ # TODO: This is the old method for detecting batch mode. Remove this once
15
+ # the above about command is vetted as working.
16
+ # basename = os.path.splitext(os.path.basename(sys.executable).lower())[0]
17
+ # return basename in ('mayabatch', 'mayapy')
18
+
19
+
20
+ def root_window():
21
+ """Returns the main window of Maya as a Qt object to be used for parenting."""
22
+ from Qt import QtCompat
23
+
24
+ ptr = OpenMayaUI.MQtUtil.mainWindow()
25
+ if ptr is not None:
26
+ pointer = int(ptr)
27
+ return QtCompat.wrapInstance(pointer)
28
+
29
+
30
+ def launch(ignored):
31
+ """Show the PrEditor GUI and bring it into focus if it was minimized."""
32
+ import preditor
33
+
34
+ widget = preditor.launch()
35
+ return widget
36
+
37
+
38
+ def initializePlugin(mobject): # noqa: N802
39
+ """Initialize the script plug-in"""
40
+ global preditor_menu
41
+
42
+ # If running headless, there is no need to build a gui and create the python logger
43
+ if not headless():
44
+ from Qt.QtWidgets import QApplication
45
+
46
+ import preditor
47
+
48
+ maya_ver = cmds.about(version=True).split(" ")[0]
49
+
50
+ # Capture all stderr/out after the plugin is loaded. This makes it so
51
+ # if the PrEditor GUI is shown, it will include all of the output. Also
52
+ # tells PrEditor how to parent itself to the main window and save prefs.
53
+ preditor.configure(
54
+ # Set the core_name so preferences are saved per-maya version.
55
+ "Maya-{}".format(maya_ver),
56
+ # Tell PrEditor how to find the maya root window for parenting
57
+ parent_callback=root_window,
58
+ # Tell it how to check if running in batch mode
59
+ headless_callback=headless,
60
+ )
61
+
62
+ # Detect Maya shutting down and ensure PrEditor's prefs are saved
63
+ if QApplication.instance():
64
+ QApplication.instance().aboutToQuit.connect(preditor.shutdown)
65
+
66
+ # Add a new PrEditor menu with an item that launches PrEditor
67
+ gmainwindow = maya.mel.eval("$temp1=$gMainWindow")
68
+ preditor_menu = cmds.menu(label="PrEditor", parent=gmainwindow, tearOff=True)
69
+ cmds.menuItem(
70
+ label="Show",
71
+ command=launch,
72
+ sourceType="python",
73
+ image=preditor.resourcePath('img/preditor.png'),
74
+ parent=preditor_menu,
75
+ )
76
+
77
+ # TODO: Alternatively figure out how to add the launcher menuItem to a
78
+ # pre-existing maya menu like next to the "Script Editor" in
79
+ # "Windows -> General Editors"
80
+ # https://github.com/chadmv/cvwrap/blob/master/scripts/cvwrap/menu.py#L18
81
+
82
+ # menu = 'mainWindowMenu'
83
+ # # Make sure the menu widgets exist first.
84
+ # maya.mel.eval('ChaDeformationsMenu MayaWindow|{0};'.format(menu))
85
+ # items = cmds.menu(menu, q=True, ia=True)
86
+ # # print(items)
87
+ # for item in items:
88
+ # menu_label = cmds.menuItem(item, q=True, label=True)
89
+ # # print(menu_label)
90
+ # if menu_label == "General Editors":
91
+ # # cmds.menuItem(parent=item, divider=True, dividerLabel='PrEditor' )
92
+ # cmds.menuItem(
93
+ # label="PrEditor",
94
+ # command=launch,
95
+ # sourceType='python',
96
+ # image=preditor.resourcePath('img/preditor.png'),
97
+ # parent=item,
98
+ # )
99
+
100
+
101
+ def uninitializePlugin(mobject): # noqa: N802
102
+ """Uninitialize the script plug-in"""
103
+ import preditor
104
+
105
+ # Remove the PrEditor Menu if it exists
106
+ if preditor_menu and cmds.menu(preditor_menu, exists=True):
107
+ cmds.deleteUI(preditor_menu, menu=True)
108
+
109
+ # Close PrEditor making sure to save prefs
110
+ preditor.core.shutdown()