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.
Files changed (179) hide show
  1. preditor/__init__.py +315 -0
  2. preditor/__main__.py +13 -0
  3. preditor/about_module.py +165 -0
  4. preditor/cli.py +192 -0
  5. preditor/config.py +318 -0
  6. preditor/constants.py +13 -0
  7. preditor/contexts.py +210 -0
  8. preditor/cores/__init__.py +0 -0
  9. preditor/cores/core.py +20 -0
  10. preditor/dccs/.hab.json +10 -0
  11. preditor/dccs/maya/PrEditor_maya.mod +1 -0
  12. preditor/dccs/maya/README.md +22 -0
  13. preditor/dccs/maya/plug-ins/PrEditor_maya.py +141 -0
  14. preditor/dccs/studiomax/PackageContents.xml +32 -0
  15. preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
  16. preditor/dccs/studiomax/README.md +17 -0
  17. preditor/dccs/studiomax/preditor.ms +16 -0
  18. preditor/dccs/studiomax/preditor_menu.mnx +7 -0
  19. preditor/debug.py +149 -0
  20. preditor/delayable_engine/__init__.py +302 -0
  21. preditor/delayable_engine/delayables.py +85 -0
  22. preditor/enum.py +728 -0
  23. preditor/excepthooks.py +165 -0
  24. preditor/gui/__init__.py +56 -0
  25. preditor/gui/app.py +163 -0
  26. preditor/gui/codehighlighter.py +289 -0
  27. preditor/gui/completer.py +237 -0
  28. preditor/gui/console.py +605 -0
  29. preditor/gui/console_base.py +911 -0
  30. preditor/gui/dialog.py +181 -0
  31. preditor/gui/drag_tab_bar.py +625 -0
  32. preditor/gui/editor_chooser.py +57 -0
  33. preditor/gui/errordialog.py +69 -0
  34. preditor/gui/find_files.py +137 -0
  35. preditor/gui/fuzzy_search/__init__.py +0 -0
  36. preditor/gui/fuzzy_search/fuzzy_search.py +97 -0
  37. preditor/gui/group_tab_widget/__init__.py +0 -0
  38. preditor/gui/group_tab_widget/group_tab_widget.py +528 -0
  39. preditor/gui/group_tab_widget/grouped_tab_menu.py +35 -0
  40. preditor/gui/group_tab_widget/grouped_tab_models.py +107 -0
  41. preditor/gui/group_tab_widget/grouped_tab_widget.py +223 -0
  42. preditor/gui/group_tab_widget/one_tab_widget.py +96 -0
  43. preditor/gui/level_buttons.py +358 -0
  44. preditor/gui/logger_window_handler.py +77 -0
  45. preditor/gui/logger_window_plugin.py +35 -0
  46. preditor/gui/loggerwindow.py +2405 -0
  47. preditor/gui/newtabwidget.py +69 -0
  48. preditor/gui/output_console.py +11 -0
  49. preditor/gui/qtdesigner/__init__.py +21 -0
  50. preditor/gui/qtdesigner/_log_plugin.py +29 -0
  51. preditor/gui/qtdesigner/console_base_plugin.py +48 -0
  52. preditor/gui/qtdesigner/console_predit_plugin.py +48 -0
  53. preditor/gui/set_text_editor_path_dialog.py +61 -0
  54. preditor/gui/status_label.py +99 -0
  55. preditor/gui/suggest_path_quotes_dialog.py +50 -0
  56. preditor/gui/ui/editor_chooser.ui +93 -0
  57. preditor/gui/ui/errordialog.ui +74 -0
  58. preditor/gui/ui/find_files.ui +140 -0
  59. preditor/gui/ui/loggerwindow.ui +1909 -0
  60. preditor/gui/ui/set_text_editor_path_dialog.ui +189 -0
  61. preditor/gui/ui/suggest_path_quotes_dialog.ui +225 -0
  62. preditor/gui/window.py +161 -0
  63. preditor/gui/workbox_mixin.py +1139 -0
  64. preditor/gui/workbox_text_edit.py +136 -0
  65. preditor/gui/workboxwidget.py +315 -0
  66. preditor/logging_config.py +55 -0
  67. preditor/osystem.py +401 -0
  68. preditor/plugins.py +118 -0
  69. preditor/prefs.py +381 -0
  70. preditor/resource/environment_variables.html +26 -0
  71. preditor/resource/error_mail.html +85 -0
  72. preditor/resource/error_mail_inline.html +41 -0
  73. preditor/resource/img/README.md +17 -0
  74. preditor/resource/img/arrow_forward.png +0 -0
  75. preditor/resource/img/check-bold.png +0 -0
  76. preditor/resource/img/chevron-down.png +0 -0
  77. preditor/resource/img/chevron-up.png +0 -0
  78. preditor/resource/img/close-thick.png +0 -0
  79. preditor/resource/img/comment-edit.png +0 -0
  80. preditor/resource/img/content-copy.png +0 -0
  81. preditor/resource/img/content-cut.png +0 -0
  82. preditor/resource/img/content-duplicate.png +0 -0
  83. preditor/resource/img/content-paste.png +0 -0
  84. preditor/resource/img/content-save.png +0 -0
  85. preditor/resource/img/debug_disabled.png +0 -0
  86. preditor/resource/img/eye-check.png +0 -0
  87. preditor/resource/img/file-plus.png +0 -0
  88. preditor/resource/img/file-remove.png +0 -0
  89. preditor/resource/img/format-align-left.png +0 -0
  90. preditor/resource/img/format-letter-case-lower.png +0 -0
  91. preditor/resource/img/format-letter-case-upper.png +0 -0
  92. preditor/resource/img/format-letter-case.svg +1 -0
  93. preditor/resource/img/information.png +0 -0
  94. preditor/resource/img/logging_critical.png +0 -0
  95. preditor/resource/img/logging_custom.png +0 -0
  96. preditor/resource/img/logging_debug.png +0 -0
  97. preditor/resource/img/logging_error.png +0 -0
  98. preditor/resource/img/logging_info.png +0 -0
  99. preditor/resource/img/logging_not_set.png +0 -0
  100. preditor/resource/img/logging_warning.png +0 -0
  101. preditor/resource/img/marker.png +0 -0
  102. preditor/resource/img/play.png +0 -0
  103. preditor/resource/img/playlist-play.png +0 -0
  104. preditor/resource/img/plus-minus-variant.png +0 -0
  105. preditor/resource/img/preditor.ico +0 -0
  106. preditor/resource/img/preditor.png +0 -0
  107. preditor/resource/img/preditor.psd +0 -0
  108. preditor/resource/img/preditor.svg +44 -0
  109. preditor/resource/img/regex.svg +1 -0
  110. preditor/resource/img/restart.svg +1 -0
  111. preditor/resource/img/skip-forward-outline.png +0 -0
  112. preditor/resource/img/skip-next-outline.png +0 -0
  113. preditor/resource/img/skip-next.png +0 -0
  114. preditor/resource/img/skip-previous.png +0 -0
  115. preditor/resource/img/subdirectory-arrow-right.png +0 -0
  116. preditor/resource/img/text-search-variant.png +0 -0
  117. preditor/resource/img/warning-big.png +0 -0
  118. preditor/resource/lang/python.json +30 -0
  119. preditor/resource/pref_updates/pref_updates.json +17 -0
  120. preditor/resource/settings.ini +25 -0
  121. preditor/resource/stylesheet/Bright.css +76 -0
  122. preditor/resource/stylesheet/Dark.css +210 -0
  123. preditor/scintilla/__init__.py +40 -0
  124. preditor/scintilla/delayables/__init__.py +11 -0
  125. preditor/scintilla/delayables/smart_highlight.py +97 -0
  126. preditor/scintilla/delayables/spell_check.py +174 -0
  127. preditor/scintilla/documenteditor.py +1924 -0
  128. preditor/scintilla/finddialog.py +68 -0
  129. preditor/scintilla/lang/__init__.py +80 -0
  130. preditor/scintilla/lang/config/bash.ini +15 -0
  131. preditor/scintilla/lang/config/batch.ini +14 -0
  132. preditor/scintilla/lang/config/cpp.ini +19 -0
  133. preditor/scintilla/lang/config/css.ini +19 -0
  134. preditor/scintilla/lang/config/eyeonscript.ini +17 -0
  135. preditor/scintilla/lang/config/html.ini +21 -0
  136. preditor/scintilla/lang/config/javascript.ini +24 -0
  137. preditor/scintilla/lang/config/lua.ini +16 -0
  138. preditor/scintilla/lang/config/maxscript.ini +20 -0
  139. preditor/scintilla/lang/config/mel.ini +18 -0
  140. preditor/scintilla/lang/config/mu.ini +22 -0
  141. preditor/scintilla/lang/config/nsi.ini +19 -0
  142. preditor/scintilla/lang/config/perl.ini +19 -0
  143. preditor/scintilla/lang/config/puppet.ini +19 -0
  144. preditor/scintilla/lang/config/python.ini +28 -0
  145. preditor/scintilla/lang/config/ruby.ini +19 -0
  146. preditor/scintilla/lang/config/sql.ini +7 -0
  147. preditor/scintilla/lang/config/xml.ini +21 -0
  148. preditor/scintilla/lang/config/yaml.ini +18 -0
  149. preditor/scintilla/lang/language.py +240 -0
  150. preditor/scintilla/lexers/__init__.py +0 -0
  151. preditor/scintilla/lexers/cpplexer.py +22 -0
  152. preditor/scintilla/lexers/javascriptlexer.py +27 -0
  153. preditor/scintilla/lexers/maxscriptlexer.py +235 -0
  154. preditor/scintilla/lexers/mellexer.py +369 -0
  155. preditor/scintilla/lexers/mulexer.py +33 -0
  156. preditor/scintilla/lexers/pythonlexer.py +42 -0
  157. preditor/scintilla/ui/finddialog.ui +160 -0
  158. preditor/settings.py +71 -0
  159. preditor/stream/__init__.py +72 -0
  160. preditor/stream/console_handler.py +169 -0
  161. preditor/stream/director.py +144 -0
  162. preditor/stream/manager.py +97 -0
  163. preditor/streamhandler_helper.py +46 -0
  164. preditor/utils/__init__.py +191 -0
  165. preditor/utils/call_stack.py +86 -0
  166. preditor/utils/cute.py +106 -0
  167. preditor/utils/stylesheets.py +54 -0
  168. preditor/utils/text_search.py +338 -0
  169. preditor/version.py +34 -0
  170. preditor/weakref.py +363 -0
  171. preditor-2.1.0.dist-info/METADATA +308 -0
  172. preditor-2.1.0.dist-info/RECORD +179 -0
  173. preditor-2.1.0.dist-info/WHEEL +5 -0
  174. preditor-2.1.0.dist-info/entry_points.txt +19 -0
  175. preditor-2.1.0.dist-info/licenses/LICENSE +165 -0
  176. preditor-2.1.0.dist-info/top_level.txt +3 -0
  177. tests/encodings/test_ecoding.py +33 -0
  178. tests/find_files/test_find_files.py +74 -0
  179. 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"))
@@ -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()