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
@@ -0,0 +1,389 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ import os
4
+ import tempfile
5
+ import textwrap
6
+
7
+ from Qt.QtCore import Qt
8
+ from Qt.QtWidgets import QStackedWidget
9
+
10
+ from ..prefs import prefs_path
11
+
12
+
13
+ class WorkboxMixin(object):
14
+ _warning_text = None
15
+ """When a user is picking this Workbox class, show a warning with this text."""
16
+
17
+ def __init__(
18
+ self, parent=None, tempfile=None, filename=None, core_name=None, **kwargs
19
+ ):
20
+ super(WorkboxMixin, self).__init__(parent=parent, **kwargs)
21
+ self._filename_pref = filename
22
+ self._is_loaded = False
23
+ self._tempdir = None
24
+ self._tempfile = tempfile
25
+ self.core_name = core_name
26
+
27
+ def __auto_complete_enabled__(self):
28
+ raise NotImplementedError("Mixin method not overridden.")
29
+
30
+ def __set_auto_complete_enabled__(self, state):
31
+ raise NotImplementedError("Mixin method not overridden.")
32
+
33
+ def __clear__(self):
34
+ raise NotImplementedError("Mixin method not overridden.")
35
+
36
+ def __close__(self):
37
+ """Called just before the LoggerWindow is closed to allow for workbox cleanup"""
38
+
39
+ def __comment_toggle__(self):
40
+ raise NotImplementedError("Mixin method not overridden.")
41
+
42
+ def __console__(self):
43
+ """Returns the PrEditor console to code is executed in if set."""
44
+ try:
45
+ return self._console
46
+ except AttributeError:
47
+ self._console = None
48
+
49
+ def __set_console__(self, console):
50
+ self._console = console
51
+
52
+ def __copy_indents_as_spaces__(self):
53
+ """When copying code, should it convert leading tabs to spaces?"""
54
+ raise NotImplementedError("Mixin method not overridden.")
55
+
56
+ def __set_copy_indents_as_spaces__(self, state):
57
+ raise NotImplementedError("Mixin method not overridden.")
58
+
59
+ def __cursor_position__(self):
60
+ """Returns the line and index of the cursor."""
61
+ raise NotImplementedError("Mixin method not overridden.")
62
+
63
+ def __set_cursor_position__(self, line, index):
64
+ """Set the cursor to this line number and index"""
65
+ raise NotImplementedError("Mixin method not overridden.")
66
+
67
+ def __exec_all__(self):
68
+ raise NotImplementedError("Mixin method not overridden.")
69
+
70
+ def __exec_selected__(self, truncate=True):
71
+ txt, line = self.__selected_text__()
72
+
73
+ # Remove any leading white space shared across all lines
74
+ txt = textwrap.dedent(txt)
75
+
76
+ # Get rid of pesky \r's
77
+ txt = self.__unix_end_lines__(txt)
78
+
79
+ # Make workbox line numbers match the workbox line numbers, by adding
80
+ # the appropriate number of newlines to mimic it's original position in
81
+ # the workbox.
82
+ txt = '\n' * line + txt
83
+
84
+ # execute the code
85
+ filename = self.__workbox_filename__(selection=True)
86
+ ret, was_eval = self.__console__().executeString(txt, filename=filename)
87
+ if was_eval:
88
+ # If the selected code was a statement print the result of the statement.
89
+ ret = repr(ret)
90
+ self.__console__().startOutputLine()
91
+ if truncate:
92
+ print(self.truncate_middle(ret, 100))
93
+ else:
94
+ print(ret)
95
+
96
+ def __file_monitoring_enabled__(self):
97
+ """Returns True if this workbox supports file monitoring.
98
+ This allows the editor to update its text if the linked
99
+ file is changed on disk."""
100
+ return False
101
+
102
+ def __set_file_monitoring_enabled__(self, state):
103
+ pass
104
+
105
+ def __filename__(self):
106
+ raise NotImplementedError("Mixin method not overridden.")
107
+
108
+ def __font__(self):
109
+ raise NotImplementedError("Mixin method not overridden.")
110
+
111
+ def __set_font__(self, font):
112
+ raise NotImplementedError("Mixin method not overridden.")
113
+
114
+ def __group_tab_index__(self):
115
+ """Returns the group and editor indexes if this editor is being used in
116
+ a GroupTabWidget.
117
+
118
+ Returns:
119
+ group, editor: The index of the group tab and the index of the
120
+ editor's tab under the group tab. -1 is returned for both if
121
+ this isn't parent to a GroupTabWidget.
122
+ """
123
+ group = editor = -1
124
+
125
+ # This widget's parent should be a stacked widget and we can get the
126
+ # editors index from that
127
+ stack = self.parent()
128
+ if stack and isinstance(stack, QStackedWidget):
129
+ editor = stack.indexOf(self)
130
+ else:
131
+ return -1, -1
132
+
133
+ # The parent of the stacked widget should be a tab widget, get its parent
134
+ editor_tab = stack.parent()
135
+ if not editor_tab:
136
+ return -1, -1
137
+
138
+ # This should be a stacked widget under a tab widget, we can get group
139
+ # from it without needing to get its parent.
140
+ stack = editor_tab.parent()
141
+ if stack and isinstance(stack, QStackedWidget):
142
+ group = stack.indexOf(editor_tab)
143
+
144
+ return group, editor
145
+
146
+ def __workbox_filename__(self, selection=False):
147
+ title = "WorkboxSelection" if selection else "Workbox"
148
+ group, editor = self.__group_tab_index__()
149
+ if group == -1 or editor == -1:
150
+ return '<{}>'.format(title)
151
+ else:
152
+ return '<{}>:{},{}'.format(title, group, editor)
153
+
154
+ def __goto_line__(self, line):
155
+ raise NotImplementedError("Mixin method not overridden.")
156
+
157
+ def __indentations_use_tabs__(self):
158
+ raise NotImplementedError("Mixin method not overridden.")
159
+
160
+ def __set_indentations_use_tabs__(self, state):
161
+ raise NotImplementedError("Mixin method not overridden.")
162
+
163
+ def __insert_text__(self, txt):
164
+ raise NotImplementedError("Mixin method not overridden.")
165
+
166
+ def __load__(self, filename):
167
+ raise NotImplementedError("Mixin method not overridden.")
168
+
169
+ def __margins_font__(self):
170
+ raise NotImplementedError("Mixin method not overridden.")
171
+
172
+ def __set_margins_font__(self, font):
173
+ raise NotImplementedError("Mixin method not overridden.")
174
+
175
+ def __marker_add__(self, line):
176
+ raise NotImplementedError("Mixin method not overridden.")
177
+
178
+ def __marker_clear_all__(self):
179
+ raise NotImplementedError("Mixin method not overridden.")
180
+
181
+ def __reload_file__(self):
182
+ raise NotImplementedError("Mixin method not overridden.")
183
+
184
+ def __remove_selected_text__(self):
185
+ raise NotImplementedError("Mixin method not overridden.")
186
+
187
+ def __save__(self):
188
+ raise NotImplementedError("Mixin method not overridden.")
189
+
190
+ def __selected_text__(self, start_of_line=False, selectText=False):
191
+ """Returns selected text or the current line of text, plus the line
192
+ number of the begining of selection / cursor position.
193
+
194
+ If text is selected, it is returned. If nothing is selected, returns the
195
+ entire line of text the cursor is currently on.
196
+
197
+ Args:
198
+ start_of_line (bool, optional): If text is selected, include any
199
+ leading text from the first line of the selection.
200
+ selectText (bool): If expanding to the entire line from the cursor,
201
+ indicates whether to select that line of text
202
+
203
+ Returns:
204
+ str: The requested text
205
+ line (int): plus the line number of the beginning of selection / cursor
206
+ position.
207
+ """
208
+ raise NotImplementedError("Mixin method not overridden.")
209
+
210
+ def __tab_width__(self):
211
+ raise NotImplementedError("Mixin method not overridden.")
212
+
213
+ def __set_tab_width__(self, width):
214
+ raise NotImplementedError("Mixin method not overridden.")
215
+
216
+ def __text__(self, line=None, start=None, end=None):
217
+ """Returns the text in this widget, possibly limited in scope.
218
+
219
+ Note: Only pass line, or (start and end) to this method.
220
+
221
+ Args:
222
+ line (int, optional): Limit the returned scope to just this line number.
223
+ start (int, optional): Limit the scope to text between this and end.
224
+ end (int, optional): Limit the scope to text between start and this.
225
+
226
+ Returns:
227
+ str: The requested text.
228
+ """
229
+ raise NotImplementedError("Mixin method not overridden.")
230
+
231
+ def __set_text__(self, txt):
232
+ """Replace all of the current text with txt. This method should be overridden
233
+ by sub-classes, and call super to mark the widget as having been loaded.
234
+ If text is being set on the widget, it most likely should be marked as
235
+ having been loaded.
236
+ """
237
+ self._is_loaded = True
238
+
239
+ def truncate_middle(self, s, n, sep=' ... '):
240
+ """Truncates the provided text to a fixed length, putting the sep in the middle.
241
+ https://www.xormedia.com/string-truncate-middle-with-ellipsis/
242
+ """
243
+ if len(s) <= n:
244
+ # string is already short-enough
245
+ return s
246
+ # half of the size, minus the seperator
247
+ n_2 = int(n) // 2 - len(sep)
248
+ # whatever's left
249
+ n_1 = n - n_2 - len(sep)
250
+ return '{0}{1}{2}'.format(s[:n_1], sep, s[-n_2:])
251
+
252
+ @classmethod
253
+ def __unix_end_lines__(cls, txt):
254
+ """Replaces all windows and then mac line endings with unix line endings."""
255
+ return txt.replace('\r\n', '\n').replace('\r', '\n')
256
+
257
+ def __restore_prefs__(self, prefs):
258
+ self._filename_pref = prefs.get('filename')
259
+ self._tempfile = prefs.get('tempfile')
260
+
261
+ def __save_prefs__(self, name, current=None):
262
+ ret = {}
263
+ # Hopefully the alphabetical sorting of this dict is preserved in py3
264
+ # to make it easy to diff the json pref file if ever required.
265
+ if current is not None:
266
+ ret['current'] = current
267
+ ret['filename'] = self._filename_pref
268
+ ret['name'] = name
269
+ ret['tempfile'] = self._tempfile
270
+
271
+ if not self._is_loaded:
272
+ return ret
273
+
274
+ if self._filename_pref:
275
+ self.__save__()
276
+ else:
277
+ if not self._tempfile:
278
+ self._tempfile = self.__create_tempfile__()
279
+ ret['tempfile'] = self._tempfile
280
+ self.__write_file__(
281
+ self.__tempfile__(create=True),
282
+ self.__text__(),
283
+ )
284
+
285
+ return ret
286
+
287
+ def __tempdir__(self, create=False):
288
+ if self._tempdir is None:
289
+ self._tempdir = prefs_path('workboxes', core_name=self.core_name)
290
+
291
+ if create and not os.path.exists(self._tempdir):
292
+ os.makedirs(self._tempdir)
293
+
294
+ return self._tempdir
295
+
296
+ def __tempfile__(self, create=False):
297
+ if self._tempfile:
298
+ return os.path.join(self.__tempdir__(create=create), self._tempfile)
299
+
300
+ def __create_tempfile__(self):
301
+ """Creates a temporary file to be used by `__tempfile__` to store this
302
+ editors text contents stored in `__tempdir__`."""
303
+ with tempfile.NamedTemporaryFile(
304
+ prefix='workbox_',
305
+ suffix='.py',
306
+ dir=self.__tempdir__(create=True),
307
+ delete=False,
308
+ ) as fle:
309
+ name = fle.name
310
+
311
+ return os.path.basename(name)
312
+
313
+ def __remove_tempfile__(self):
314
+ """Removes `__tempfile__` if it is being used."""
315
+ tempfile = self.__tempfile__()
316
+ if tempfile and os.path.exists(tempfile):
317
+ os.remove(tempfile)
318
+
319
+ @classmethod
320
+ def __open_file__(cls, filename):
321
+ with open(filename) as fle:
322
+ return fle.read()
323
+ return ""
324
+
325
+ @classmethod
326
+ def __write_file__(cls, filename, txt):
327
+ with open(filename, 'w') as fle:
328
+ fle.write(txt)
329
+
330
+ def __show__(self):
331
+ if self._is_loaded:
332
+ return
333
+
334
+ self._is_loaded = True
335
+ if self._filename_pref:
336
+ self.__load__(self._filename_pref)
337
+ elif self._tempfile:
338
+ txt = self.__open_file__(self.__tempfile__())
339
+ self.__set_text__(txt)
340
+
341
+ def process_shortcut(self, event, run=True):
342
+ """Check for workbox shortcuts and optionally call them.
343
+
344
+ Args:
345
+ event (QEvent): The keyPressEvent to process.
346
+ run (bool, optional): Run the expected action if possible.
347
+
348
+ Returns:
349
+ str or False: Returns False if the key press was not handled, indicating
350
+ that the subclass needs to handle it(or call super). If a known
351
+ shortcut was detected, a string indicating the action is returned
352
+ after running the action if enabled and supported.
353
+
354
+ Known actions:
355
+ __exec_selected__: If the user pressed Shift + Return or pressed the
356
+ number pad enter key calling `__exec_selected__`.
357
+ """
358
+
359
+ # Number pad enter, or Shift + Return pressed, execute selected
360
+ # Ctrl+ Shift+Return pressed, execute selected without truncating output
361
+ if run:
362
+ # self.__exec_selected__()
363
+ # Collect what was pressed
364
+ key = event.key()
365
+ modifiers = event.modifiers()
366
+
367
+ # Determine which relevant combos are pressed
368
+ ret = key == Qt.Key_Return
369
+ enter = key == Qt.Key_Enter
370
+ shift = modifiers == Qt.ShiftModifier
371
+ ctrlShift = modifiers == Qt.ControlModifier | Qt.ShiftModifier
372
+
373
+ # Determine which actions to take
374
+ evalTrunc = enter or (ret and shift)
375
+ evalNoTrunc = ret and ctrlShift
376
+
377
+ if evalTrunc:
378
+ # Execute with truncation
379
+ self.window().execSelected()
380
+ elif evalNoTrunc:
381
+ # Execute without truncation
382
+ self.window().execSelected(truncate=False)
383
+
384
+ if evalTrunc or evalNoTrunc:
385
+ if self.window().uiAutoPromptACT.isChecked():
386
+ self.__console__().startInputLine()
387
+ return '__exec_selected__'
388
+ else:
389
+ return False
@@ -0,0 +1,137 @@
1
+ from __future__ import absolute_import
2
+
3
+ import logging
4
+
5
+ from Qt.QtGui import QFont, QFontMetrics, QTextCursor
6
+ from Qt.QtWidgets import QTextEdit
7
+
8
+ from .codehighlighter import CodeHighlighter
9
+ from .workbox_mixin import WorkboxMixin
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
15
+ """A very simple multi-line text editor without any bells and whistles.
16
+
17
+ It's better than nothing, but not by much.
18
+ """
19
+
20
+ _warning_text = (
21
+ "This is a bare bones workbox, if you have another option, it's probably"
22
+ "a better option."
23
+ )
24
+
25
+ def __init__(
26
+ self, parent=None, console=None, delayable_engine='default', core_name=None
27
+ ):
28
+ super(WorkboxTextEdit, self).__init__(parent=parent, core_name=core_name)
29
+ self._filename = None
30
+ self.__set_console__(console)
31
+ highlight = CodeHighlighter(self)
32
+ highlight.setLanguage('Python')
33
+ self.uiCodeHighlighter = highlight
34
+
35
+ def __auto_complete_enabled__(self):
36
+ pass
37
+
38
+ def __set_auto_complete_enabled__(self, state):
39
+ pass
40
+
41
+ def __copy_indents_as_spaces__(self):
42
+ """When copying code, should it convert leading tabs to spaces?"""
43
+ return False
44
+
45
+ def __set_copy_indents_as_spaces__(self, state):
46
+ logger.info(
47
+ "WorkboxTextEdit does not support converting indents to spaces on copy."
48
+ )
49
+
50
+ def __cursor_position__(self):
51
+ """Returns the line and index of the cursor."""
52
+ cursor = self.textCursor()
53
+ sc = QTextCursor(self.document())
54
+ sc.setPosition(cursor.selectionStart())
55
+ return sc.blockNumber(), sc.positionInBlock()
56
+
57
+ def __exec_all__(self):
58
+ txt = self.__text__().rstrip()
59
+ filename = self.__workbox_filename__()
60
+ self.__console__().executeString(txt, filename=filename)
61
+
62
+ def __font__(self):
63
+ return self.font()
64
+
65
+ def __set_font__(self, font):
66
+ metrics = QFontMetrics(font)
67
+ self.setTabStopDistance(metrics.width(" ") * 4)
68
+ super(WorkboxTextEdit, self).setFont(font)
69
+
70
+ def __goto_line__(self, line):
71
+ cursor = QTextCursor(self.document().findBlockByLineNumber(line - 1))
72
+ self.setTextCursor(cursor)
73
+
74
+ def __indentations_use_tabs__(self):
75
+ return True
76
+
77
+ def __set_indentations_use_tabs__(self, state):
78
+ logger.info("WorkboxTextEdit does not support using spaces for tabs.")
79
+
80
+ def __load__(self, filename):
81
+ self._filename = filename
82
+ txt = self.__open_file__(self._filename)
83
+ self.__set_text__(txt)
84
+
85
+ def __margins_font__(self):
86
+ return QFont()
87
+
88
+ def __set_margins_font__(self, font):
89
+ pass
90
+
91
+ def __tab_width__(self):
92
+ # TODO: Implement custom tab widths
93
+ return 4
94
+
95
+ def __text__(self, line=None, start=None, end=None):
96
+ return self.toPlainText()
97
+
98
+ def __set_text__(self, text):
99
+ super(WorkboxTextEdit, self).__set_text__(text)
100
+ self.setPlainText(text)
101
+
102
+ def __selected_text__(self, start_of_line=False, selectText=False):
103
+ cursor = self.textCursor()
104
+
105
+ # Get starting line number. Must set the cursor's position to the start of the
106
+ # selection, otherwise we may instead get the ending line number.
107
+ tempCursor = self.textCursor()
108
+ tempCursor.setPosition(tempCursor.selectionStart())
109
+ line = tempCursor.block().firstLineNumber()
110
+
111
+ # If no selection, return the current line
112
+ if cursor.selection().isEmpty():
113
+ text = cursor.block().text()
114
+
115
+ selectText = self.window().uiSelectTextACT.isChecked() or selectText
116
+ if selectText:
117
+ cursor.select(QTextCursor.LineUnderCursor)
118
+ self.setTextCursor(cursor)
119
+
120
+ return text, line
121
+
122
+ # Otherwise return the selected text
123
+ if start_of_line:
124
+ sc = QTextCursor(self.document())
125
+ sc.setPosition(cursor.selectionStart())
126
+ sc.movePosition(cursor.StartOfLine, sc.MoveAnchor)
127
+ sc.setPosition(cursor.selectionEnd(), sc.KeepAnchor)
128
+
129
+ return sc.selection().toPlainText(), line
130
+
131
+ return self.textCursor().selection().toPlainText(), line
132
+
133
+ def keyPressEvent(self, event):
134
+ if self.process_shortcut(event):
135
+ return
136
+ else:
137
+ super(WorkboxTextEdit, self).keyPressEvent(event)