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/__init__.py ADDED
@@ -0,0 +1,322 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import logging
4
+ import os
5
+ import sys
6
+
7
+ from Qt.QtCore import Qt
8
+
9
+ from . import osystem
10
+ from .config import PreditorConfig
11
+ from .plugins import Plugins
12
+ from .version import version as __version__
13
+
14
+ DEFAULT_CORE_NAME = "PrEditor"
15
+ """The default name to use for the core name."""
16
+
17
+ core = None # create a managed Core instance
18
+ """
19
+ The blurdev managed :class:`Core` object from the :mod:`blurdev.cores` module.
20
+ """
21
+
22
+ # Create the root blurdev module logging object.
23
+ _logger = logging.getLogger(__name__)
24
+
25
+ # Add a NullHandler to suppress the "No handlers could be found for _logger"
26
+ # warnings from being printed to stderr. Studiomax and possibly other DCC's
27
+ # tend to treat any text written to stderr as a error when running headless.
28
+ # We also don't want this warning showing up in production anyway.
29
+ _logger.addHandler(logging.NullHandler())
30
+
31
+ plugins = Plugins()
32
+
33
+ config = PreditorConfig()
34
+ """This config is used to setup the instance of PrEditor. It can be edited up
35
+ to the point the instance is created. After that changes will be ignored.
36
+ """
37
+
38
+
39
+ def about_preditor(instance=None):
40
+ """Useful info about installed packages generated by plugins.
41
+
42
+ Args:
43
+ instance (LoggerWindow, optional): Used by the AboutModule plugins
44
+ to access the current instance of a Preditor GUI.
45
+ """
46
+ from .about_module import AboutModule
47
+
48
+ return AboutModule.generate(instance=instance)
49
+
50
+
51
+ def init():
52
+ os.environ['BDEV_EMAILINFO_PREDITOR_VERSION'] = __version__
53
+ pythonw_print_bugfix()
54
+ global core
55
+ # create the core
56
+ if not core:
57
+ from .cores.core import Core
58
+
59
+ objectName = None
60
+ _exe = os.path.basename(sys.executable).lower()
61
+ # Treat designer as a seperate core so it gets its own prefrences.
62
+ if 'designer' in _exe:
63
+ objectName = 'designer'
64
+ elif 'assfreezer' in _exe:
65
+ objectName = 'assfreezer'
66
+ core = Core(objectName=objectName)
67
+
68
+ for plugin in plugins.initialize():
69
+ plugin()
70
+
71
+
72
+ def configure(
73
+ name,
74
+ parent_callback=None,
75
+ excepthook=True,
76
+ logging=True,
77
+ streams=True,
78
+ headless_callback=None,
79
+ ):
80
+ """Global configuration of PrEditor. Safe to re-call until the instance is created.
81
+
82
+ Configures the instance of PrEditor without creating the GUI. It should be run
83
+ as early as possible in the applications initialization to allow PrEditor to
84
+ show as much stdout/err text as possible even the text printed before the
85
+ application's GUI is created.
86
+
87
+ This is a convenience method that sets useful defaults on `preditor.config`.
88
+ You can directly set the values on there instead of calling this function.
89
+ If you use that make sure to enable each feature as it doesn't by default.
90
+
91
+ Once `preditor.instance(create=True)` is called this will return without
92
+ making any changes. Otherwise unless noted with "First call only." each time
93
+ this function is called it will update any previously set values. This allows
94
+ you to minimally configure PrEditor and as more of the application comes
95
+ online enable more advanced features.
96
+
97
+ Args:
98
+ name (str): The core_name to use for the global instance of PrEditor.
99
+ Once this has been set, you can call `launch` without passing name
100
+ to access the main instance. The core_name controls what preferences
101
+ are loaded and used by PrEditor including the workbox tabs.
102
+ parent_callback (callable, optional): Callback that returns a QWidget
103
+ to use as the parent of the LoggerWindow when its first created.
104
+ This can be used by DCC's to set the parent to their main window.
105
+ excepthook (bool, optional): First call only. Replaces `sys.excepthook`
106
+ with a interactive exception handler that prompts the user to show
107
+ PrEditor when an python exception is raised. It is recommended that
108
+ you only add the excepthook once the Qt UI is initialized.
109
+ logging (bool, optional): Restore the python logging configuration settings
110
+ that were recorded the last time PrEditor prefs were saved. If called
111
+ multiple times with different core_name's before the instance is
112
+ created, this will reset the logging configuration from the previous
113
+ core_name if logging prefs exist.
114
+ streams (bool, optional): First call only. Install the stream manager to
115
+ capture any stdout/stderr text written. Later when calling launch, the
116
+ LoggerWindow will show all of the captured text. This lets you only
117
+ create the LoggerWindow IF you need to show it, but when you do it
118
+ will have all of the std stream text written after this call.
119
+ headless_callback (callable, optional): Callback that returns a bool
120
+ indicating if PrEditor should attempt to create GUI elements.
121
+ """
122
+ # The name should always be set first. The logging setting depends on it,
123
+ # and the others may depend on it in the future.
124
+ config.name = name
125
+ config.logging = logging
126
+ # Capture stdout/err streams and install the excepthook so we start capturing
127
+ # output as early as possible if enabled.
128
+ config.streams = streams
129
+ config.excepthook = excepthook
130
+ # These will be used to create the GUI instance if PrEditor needs shown.
131
+ config.parent_callback = parent_callback
132
+ config.headless_callback = headless_callback
133
+
134
+
135
+ def get_core_name():
136
+ """Returns the configured core_name or DEFAULT_CORE_NAME."""
137
+ return config.name
138
+
139
+
140
+ def launch(run_workbox=False, app_id=None, name=None, standalone=False):
141
+ """Launches the preditor gui creating the QApplication instance if not
142
+ already created.
143
+
144
+ Args:
145
+ modal (bool, optional): If True, preditor's gui will be created as a
146
+ modal window (ie. blocks current code execution while its shown).
147
+ run_workbox (bool, optional): After preditor's gui is shown, run its
148
+ current workbox text.
149
+ app_id (str, optional): Set the QApplication's applicationName to this
150
+ value. This is normally only used when launching a standalone
151
+ instance of the PrEditor gui.
152
+ standalone (bool, optional): Launch PrEditor in standalone mode. This
153
+ enables extra options that only make sense when it is running as
154
+ its own app, not inside of another app.
155
+
156
+ Returns:
157
+ preditor.gui.loggerwindow.LoggerWindow: The instance of the PrEditor
158
+ gui that was created.
159
+ """
160
+ if name is None:
161
+ # If the name wasn't passed we will get it from the name stored when
162
+ # configure was called.
163
+ if config.name is None:
164
+ raise RuntimeError(
165
+ "You call configure before calling launch if not passing name"
166
+ )
167
+ name = config.name
168
+ else:
169
+ # A name was provided, call configure to ensure it has been called
170
+ configure(name=name)
171
+
172
+ from .gui.app import App
173
+ from .gui.loggerwindow import LoggerWindow
174
+
175
+ # Check if we can actually run the PrEditor gui and setup Qt if required
176
+ app = App(name=app_id)
177
+ widget = LoggerWindow.instance(
178
+ run_workbox=run_workbox, name=name, standalone=standalone
179
+ )
180
+
181
+ # Show the PrEditor instance and make sure it regains focus and visibility
182
+ widget.show()
183
+ # If the instance was already shown, raise it to the top and make
184
+ # it regain focus.
185
+ widget.activateWindow()
186
+ widget.raise_()
187
+ widget.setWindowState(widget.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
188
+ widget.console().setFocus()
189
+ app.start()
190
+
191
+ return widget
192
+
193
+
194
+ def prefPath(relpath, coreName=''):
195
+ # use the core
196
+ if not coreName and core:
197
+ coreName = core.objectName()
198
+ basepath = os.path.join(
199
+ osystem.expandvars(os.environ['BDEV_PATH_PREFS']), 'app_%s/' % coreName
200
+ )
201
+ return os.path.normpath(os.path.join(basepath, relpath))
202
+
203
+
204
+ def pythonw_print_bugfix():
205
+ """
206
+ When running pythonw print statements and file handles tend to have problems
207
+ so, if its pythonw and stderr and stdout haven't been redirected, redirect them
208
+ to os.devnull.
209
+ """
210
+ if os.path.basename(sys.executable) == 'pythonw.exe':
211
+ if sys.stdout == sys.__stdout__:
212
+ sys.stdout = open(os.devnull, 'w')
213
+ if sys.stderr == sys.__stderr__:
214
+ sys.stderr = open(os.devnull, 'w')
215
+
216
+
217
+ def relativePath(path, additional=''):
218
+ """
219
+ Replaces the last element in the path with the passed in additional path.
220
+ :param path: Source path. Generally a file name.
221
+ :param additional: Additional folder/file path appended to the path.
222
+ :return str: The modified path
223
+ """
224
+ return os.path.join(os.path.dirname(path), additional)
225
+
226
+
227
+ def resourcePath(relpath=''):
228
+ """Returns the full path to the file inside the preditor/resource folder
229
+
230
+ Args:
231
+ relpath (str, optional): The additional path added to the
232
+ preditor/resource folder path.
233
+
234
+ Returns:
235
+ str: The modified path
236
+ """
237
+ return os.path.join(relativePath(__file__), 'resource', relpath)
238
+
239
+
240
+ def parent_callback():
241
+ """Returns the parent_callback or None.
242
+
243
+ This is a callback that returns a QWidget to use as the parent of the
244
+ LoggerWindow when its first created. This can be used by DCC's to set the
245
+ parent to their main window.
246
+ """
247
+ return config.parent_callback
248
+
249
+
250
+ def connect_preditor(
251
+ parent, sequence='F2', text='Show PrEditor', obj_name='uiShowPreditorACT', name=None
252
+ ):
253
+ """Creates a QAction that shows the PrEditor gui with a keyboard shortcut.
254
+ This will automatically call `preditor.configure` if name is provided,
255
+ capturing any `sys.stdout` and `sys.stderr` writes after this call. This does
256
+ not initialize the PrEditor gui instance until the action is actually called.
257
+
258
+ Args:
259
+ parent: The parent widget, normally a window
260
+ sequence (str, optional): A string representing the keyboard shortcut
261
+ associated with the QAction.
262
+ text (str, optional): The display text for the QAction.
263
+ obj_name (str, optional): Set the QAction's objectName to this value.
264
+
265
+ Returns:
266
+ QAction: The created QAction
267
+ """
268
+ from Qt.QtGui import QKeySequence
269
+ from Qt.QtWidgets import QAction
270
+
271
+ if name:
272
+ # Set the core_name if provided
273
+ configure(name)
274
+
275
+ # Create shortcut for launching the PrEditor gui.
276
+ action = QAction(text, parent)
277
+ action.setObjectName(obj_name)
278
+ action.triggered.connect(launch)
279
+ action.setShortcut(QKeySequence(sequence))
280
+ parent.addAction(action)
281
+ return action
282
+
283
+
284
+ def instance(parent=None, run_workbox=False, create=True):
285
+ """Returns the existing instance of the PrEditor gui creating it on first call.
286
+
287
+ Args:
288
+ parent (QWidget, optional): If the instance hasn't been created yet, create
289
+ it and parent it to this object.
290
+ run_workbox (bool, optional): If the instance hasn't been created yet, this
291
+ will execute the active workbox's code once fully initialized.
292
+ create (bool, optional): Returns None if the instance has not been created.
293
+
294
+ Returns:
295
+ Returns a fully initialized instance of the PrEditor gui. If called more
296
+ than once, the same instance will be returned. If create is False, it may
297
+ return None.
298
+ """
299
+ return config.instance(parent=parent, run_workbox=run_workbox, create=create)
300
+
301
+
302
+ def shutdown():
303
+ """Fully close and cleanup the PrEditor gui if it was created.
304
+
305
+ Call this when shutting down your application to ensure any unsaved changes
306
+ to the PrEditor gui are saved and the instance is actually closed instead
307
+ of just hidden.
308
+
309
+ If the PrEditor gui was never created, this does nothing so its safe to call
310
+ even if the user never showed the gui. It also won't add extra time creating
311
+ the gui just so it can "save any changes".
312
+
313
+ Returns:
314
+ bool: If a shutdown was required
315
+ """
316
+ from .gui.loggerwindow import LoggerWindow
317
+
318
+ return LoggerWindow.instance_shutdown()
319
+
320
+
321
+ # initialize the core
322
+ 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,161 @@
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 'PyQt5.QtCore' in sys.modules:
115
+ ret.append('PyQt5: {}'.format(sys.modules['PyQt5.QtCore'].PYQT_VERSION_STR))
116
+ if 'PySide2.QtCore' in sys.modules:
117
+ ret.append('PySide2: {}'.format(sys.modules['PySide2.QtCore'].qVersion()))
118
+
119
+ # Add qt library paths for plugin debugging
120
+ for i, path in enumerate(QtCore.QCoreApplication.libraryPaths()):
121
+ if i == 0:
122
+ ret.append('Library Paths: {}'.format(path))
123
+ else:
124
+ ret.append(' {}'.format(path))
125
+
126
+ return "\n".join(ret)
127
+
128
+ def version(self):
129
+ from Qt import __qt_version__
130
+
131
+ return __qt_version__
132
+
133
+
134
+ class AboutPython(AboutModule):
135
+ """Info about the current instance of python."""
136
+
137
+ def text(self):
138
+ """Return the path PrEditor was loaded from for quick debugging."""
139
+ ret = sys.version
140
+ # Windows doesn't add a newline before the compiler info, and it can end
141
+ # up being a little long for QMessageBox's with short file paths. Add
142
+ # the newline like is present on linux
143
+ ret = ret.replace(") [", ")\n[")
144
+ return ret
145
+
146
+ def version(self):
147
+ return '{}.{}.{}'.format(*sys.version_info[:3])
148
+
149
+
150
+ class AboutExe(AboutModule):
151
+ """The value of sys.executable, disabled if not set."""
152
+
153
+ def enabled(self):
154
+ return bool(sys.executable)
155
+
156
+ def text(self):
157
+ return sys.executable
158
+
159
+ def version(self):
160
+ """No version is returned for this class."""
161
+ 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()