PrEditor 1.1.0__py3-none-any.whl → 1.3.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 (57) hide show
  1. preditor/__init__.py +4 -1
  2. preditor/about_module.py +6 -2
  3. preditor/dccs/.hab.json +10 -0
  4. preditor/dccs/maya/PrEditor_maya.mod +0 -1
  5. preditor/dccs/maya/README.md +22 -0
  6. preditor/dccs/maya/plug-ins/PrEditor_maya.py +32 -1
  7. preditor/dccs/studiomax/PackageContents.xml +32 -0
  8. preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
  9. preditor/dccs/studiomax/README.md +17 -0
  10. preditor/dccs/studiomax/preditor.ms +16 -0
  11. preditor/dccs/studiomax/preditor_menu.mnx +7 -0
  12. preditor/debug.py +7 -3
  13. preditor/excepthooks.py +1 -1
  14. preditor/gui/app.py +2 -2
  15. preditor/gui/codehighlighter.py +10 -24
  16. preditor/gui/completer.py +17 -6
  17. preditor/gui/console.py +94 -47
  18. preditor/gui/dialog.py +10 -7
  19. preditor/gui/drag_tab_bar.py +7 -7
  20. preditor/gui/errordialog.py +2 -2
  21. preditor/gui/find_files.py +7 -5
  22. preditor/gui/fuzzy_search/fuzzy_search.py +8 -4
  23. preditor/gui/group_tab_widget/__init__.py +32 -4
  24. preditor/gui/group_tab_widget/grouped_tab_models.py +4 -4
  25. preditor/gui/group_tab_widget/grouped_tab_widget.py +6 -4
  26. preditor/gui/level_buttons.py +16 -1
  27. preditor/gui/loggerwindow.py +48 -27
  28. preditor/gui/set_text_editor_path_dialog.py +3 -1
  29. preditor/gui/ui/loggerwindow.ui +11 -1
  30. preditor/gui/window.py +4 -4
  31. preditor/gui/workbox_mixin.py +43 -14
  32. preditor/gui/workbox_text_edit.py +7 -5
  33. preditor/gui/workboxwidget.py +25 -16
  34. preditor/logging_config.py +5 -2
  35. preditor/scintilla/__init__.py +19 -1
  36. preditor/scintilla/delayables/smart_highlight.py +7 -4
  37. preditor/scintilla/delayables/spell_check.py +5 -4
  38. preditor/scintilla/documenteditor.py +228 -135
  39. preditor/scintilla/finddialog.py +3 -3
  40. preditor/scintilla/lang/language.py +1 -1
  41. preditor/scintilla/lexers/cpplexer.py +3 -2
  42. preditor/scintilla/lexers/javascriptlexer.py +6 -4
  43. preditor/scintilla/lexers/maxscriptlexer.py +8 -7
  44. preditor/scintilla/lexers/mellexer.py +3 -2
  45. preditor/scintilla/lexers/mulexer.py +3 -2
  46. preditor/scintilla/lexers/pythonlexer.py +7 -6
  47. preditor/utils/cute.py +9 -8
  48. preditor/version.py +16 -3
  49. {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/METADATA +69 -32
  50. {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/RECORD +56 -47
  51. preditor-1.3.0.dist-info/top_level.txt +3 -0
  52. tests/find_files/test_find_files.py +74 -0
  53. tests/ide/test_delayable_engine.py +171 -0
  54. preditor-1.1.0.dist-info/top_level.txt +0 -1
  55. {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/WHEEL +0 -0
  56. {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/entry_points.txt +0 -0
  57. {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -134,7 +134,7 @@ class LoggingLevelButton(QToolButton):
134
134
  parent (QWidget, optional): The parent widget for this button.
135
135
  """
136
136
  super(LoggingLevelButton, self).__init__(parent=parent)
137
- self.setPopupMode(QToolButton.InstantPopup)
137
+ self.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup)
138
138
 
139
139
  # create root logger menu
140
140
  root = logging.getLogger("")
@@ -291,6 +291,21 @@ class LoggingLevelMenu(LazyMenu):
291
291
  self.addMenu(HandlerMenu(self.logger, self))
292
292
  self.addSeparator()
293
293
 
294
+ if self.logger.disabled:
295
+ # Warn the user that this logger has been disabled. The documentation
296
+ # says to consider this property as read only so this is just info
297
+ # and does not implement a way to re-enable this logger. It's not
298
+ # disabled because that would prevent the tooltip from showing.
299
+ action = self.addAction("Logger is disabled")
300
+ action.setToolTip(
301
+ 'This <b>logger has been disabled</b> and does not handle events '
302
+ "so you likely won't see events from it or its children. "
303
+ 'This normally happens when using logging.config with '
304
+ '"disable_existing_loggers" defaulted to True.'
305
+ )
306
+ action.setIcon(QIcon(resourcePath('img/warning-big.png')))
307
+ self.addSeparator()
308
+
294
309
  for logger_level in LoggerLevels:
295
310
  is_current = logger_level.is_current(self.logger)
296
311
 
@@ -12,15 +12,17 @@ from datetime import datetime, timedelta
12
12
  from functools import partial
13
13
 
14
14
  import __main__
15
+ import Qt as Qt_py
15
16
  from Qt import QtCompat, QtCore, QtWidgets
16
17
  from Qt.QtCore import QByteArray, Qt, QTimer, Signal, Slot
17
- from Qt.QtGui import QCursor, QFont, QIcon, QTextCursor
18
+ from Qt.QtGui import QCursor, QFont, QIcon, QKeySequence, QTextCursor
18
19
  from Qt.QtWidgets import (
19
20
  QApplication,
20
21
  QFontDialog,
21
22
  QInputDialog,
22
23
  QMessageBox,
23
24
  QTextBrowser,
25
+ QTextEdit,
24
26
  QToolTip,
25
27
  QVBoxLayout,
26
28
  )
@@ -91,9 +93,7 @@ class LoggerWindow(Window):
91
93
  self.name = name if name else get_core_name()
92
94
  self._stylesheet = 'Bright'
93
95
 
94
- # Create timer to autohide status messages
95
- self.statusTimer = QTimer()
96
- self.statusTimer.setSingleShot(True)
96
+ self.setupStatusTimer()
97
97
 
98
98
  # Store the previous time a font-resize wheel event was triggered to prevent
99
99
  # rapid-fire WheelEvents. Initialize to the current time.
@@ -198,7 +198,7 @@ class LoggerWindow(Window):
198
198
  self.uiCompleterModeMENU.addSeparator()
199
199
  action = self.uiCompleterModeMENU.addAction('Cycle mode')
200
200
  action.setObjectName('uiCycleModeACT')
201
- action.setShortcut(Qt.CTRL | Qt.Key_M)
201
+ action.setShortcut(QKeySequence(Qt.Modifier.CTRL | Qt.Key.Key_M))
202
202
  action.triggered.connect(self.cycleCompleterMode)
203
203
  self.uiCompleterModeMENU.hovered.connect(self.handleMenuHovered)
204
204
 
@@ -490,7 +490,7 @@ class LoggerWindow(Window):
490
490
 
491
491
  def openSetPreferredTextEditorDialog(self):
492
492
  dlg = SetTextEditorPathDialog(parent=self)
493
- dlg.exec_()
493
+ dlg.exec()
494
494
 
495
495
  def focusToConsole(self):
496
496
  """Move focus to the console"""
@@ -529,7 +529,7 @@ class LoggerWindow(Window):
529
529
 
530
530
  cursor = console.textCursor()
531
531
  if not cursor.hasSelection():
532
- cursor.select(QTextCursor.LineUnderCursor)
532
+ cursor.select(QTextCursor.SelectionType.LineUnderCursor)
533
533
  text = cursor.selectedText()
534
534
  prompt = console.prompt()
535
535
  if text.startswith(prompt):
@@ -565,7 +565,7 @@ class LoggerWindow(Window):
565
565
 
566
566
  def wheelEvent(self, event):
567
567
  """adjust font size on ctrl+scrollWheel"""
568
- if event.modifiers() == Qt.ControlModifier:
568
+ if event.modifiers() == Qt.KeyboardModifier.ControlModifier:
569
569
  # WheelEvents can be emitted in a cluster, but we only want one at a time
570
570
  # (ie to change font size by 1, rather than 2 or 3). Let's bail if previous
571
571
  # font-resize wheel event was within a certain threshhold.
@@ -605,7 +605,10 @@ class LoggerWindow(Window):
605
605
  else:
606
606
  text = action.toolTip()
607
607
 
608
- menu = action.parentWidget()
608
+ if Qt_py.IsPyQt4:
609
+ menu = action.parentWidget()
610
+ else:
611
+ menu = action.parent()
609
612
  QToolTip.showText(QCursor.pos(), text, menu)
610
613
 
611
614
  def selectFont(self, monospace=False, proportional=False):
@@ -620,13 +623,16 @@ class LoggerWindow(Window):
620
623
  curFontFamily = origFont.family()
621
624
 
622
625
  if monospace and proportional:
623
- options = QFontDialog.MonospacedFonts | QFontDialog.ProportionalFonts
626
+ options = (
627
+ QFontDialog.FontDialogOption.MonospacedFonts
628
+ | QFontDialog.FontDialogOption.ProportionalFonts
629
+ )
624
630
  kind = "monospace or proportional "
625
631
  elif monospace:
626
- options = QFontDialog.MonospacedFonts
632
+ options = QFontDialog.FontDialogOption.MonospacedFonts
627
633
  kind = "monospace "
628
634
  elif proportional:
629
- options = QFontDialog.ProportionalFonts
635
+ options = QFontDialog.FontDialogOption.ProportionalFonts
630
636
  kind = "proportional "
631
637
 
632
638
  # Present a QFontDialog for user to choose a font
@@ -686,9 +692,9 @@ class LoggerWindow(Window):
686
692
 
687
693
  def adjustWorkboxOrientation(self, state):
688
694
  if state:
689
- self.uiSplitterSPLIT.setOrientation(Qt.Horizontal)
695
+ self.uiSplitterSPLIT.setOrientation(Qt.Orientation.Horizontal)
690
696
  else:
691
- self.uiSplitterSPLIT.setOrientation(Qt.Vertical)
697
+ self.uiSplitterSPLIT.setOrientation(Qt.Orientation.Vertical)
692
698
 
693
699
  def backupPreferences(self):
694
700
  """Saves a copy of the current preferences to a zip archive."""
@@ -757,7 +763,11 @@ class LoggerWindow(Window):
757
763
 
758
764
  def keyPressEvent(self, event):
759
765
  # Fix 'Maya : Qt tools lose focus' https://redmine.blur.com/issues/34430
760
- if event.modifiers() & (Qt.AltModifier | Qt.ControlModifier | Qt.ShiftModifier):
766
+ if event.modifiers() & (
767
+ Qt.KeyboardModifier.AltModifier
768
+ | Qt.KeyboardModifier.ControlModifier
769
+ | Qt.KeyboardModifier.ShiftModifier
770
+ ):
761
771
  pass
762
772
  else:
763
773
  super(LoggerWindow, self).keyPressEvent(event)
@@ -781,7 +791,7 @@ class LoggerWindow(Window):
781
791
  pref.update(
782
792
  {
783
793
  'loggergeom': [geo.x(), geo.y(), geo.width(), geo.height()],
784
- 'windowState': int(self.windowState()),
794
+ 'windowState': QtCompat.enumValue(self.windowState()),
785
795
  'SplitterVertical': self.uiEditorVerticalACT.isChecked(),
786
796
  'SplitterSize': self.uiSplitterSPLIT.sizes(),
787
797
  'tabIndent': self.uiIndentationsTabsACT.isChecked(),
@@ -803,6 +813,7 @@ class LoggerWindow(Window):
803
813
  'uiStatusLbl_limit': self.uiStatusLBL.limit(),
804
814
  'textEditorPath': self.textEditorPath,
805
815
  'textEditorCmdTempl': self.textEditorCmdTempl,
816
+ 'uiSeparateTracebackACT': self.uiSeparateTracebackACT.isChecked(),
806
817
  'currentStyleSheet': self._stylesheet,
807
818
  'flash_time': self.uiConsoleTXT.flash_time,
808
819
  'find_files_regex': self.uiFindInWorkboxesWGT.uiRegexBTN.isChecked(),
@@ -863,7 +874,7 @@ class LoggerWindow(Window):
863
874
  if dialog.objectName() in self.dont_ask_again:
864
875
  return
865
876
 
866
- dialog.exec_()
877
+ dialog.exec()
867
878
 
868
879
  def restartLogger(self):
869
880
  """Closes this PrEditor instance and starts a new process with the same
@@ -911,7 +922,7 @@ class LoggerWindow(Window):
911
922
  sizes = pref.get('SplitterSize')
912
923
  if sizes:
913
924
  self.uiSplitterSPLIT.setSizes(sizes)
914
- self.setWindowState(Qt.WindowStates(pref.get('windowState', 0)))
925
+ self.setWindowState(Qt.WindowState(pref.get('windowState', 0)))
915
926
  self.uiIndentationsTabsACT.setChecked(pref.get('tabIndent', True))
916
927
  self.uiCopyTabsToSpacesACT.setChecked(pref.get('copyIndentsAsSpaces', False))
917
928
 
@@ -955,6 +966,8 @@ class LoggerWindow(Window):
955
966
  self.textEditorPath = pref.get('textEditorPath', defaultExePath)
956
967
  self.textEditorCmdTempl = pref.get('textEditorCmdTempl', defaultCmd)
957
968
 
969
+ self.uiSeparateTracebackACT.setChecked(pref.get('uiSeparateTracebackACT', True))
970
+
958
971
  self.uiWordWrapACT.setChecked(pref.get('wordWrap', True))
959
972
  self.setWordWrap(self.uiWordWrapACT.isChecked())
960
973
  self.uiClearBeforeRunningACT.setChecked(pref.get('clearBeforeRunning', False))
@@ -983,7 +996,7 @@ class LoggerWindow(Window):
983
996
  _font = pref.get('consoleFont', None)
984
997
  if _font:
985
998
  font = QFont()
986
- if font.fromString(_font):
999
+ if QtCompat.QFont.fromString(font, _font):
987
1000
  self.console().setConsoleFont(font)
988
1001
 
989
1002
  self.dont_ask_again = pref.get('dont_ask_again', [])
@@ -1029,16 +1042,24 @@ class LoggerWindow(Window):
1029
1042
  self.uiStatusLBL.setText(txt)
1030
1043
  self.uiMenuBar.adjustSize()
1031
1044
 
1045
+ def setupStatusTimer(self):
1046
+ # Create timer to autohide status messages
1047
+ self.statusTimer = QTimer()
1048
+ self.statusTimer.setSingleShot(True)
1049
+ self.statusTimer.setInterval(2000)
1050
+ self.statusTimer.timeout.connect(self.clearStatusText)
1051
+
1032
1052
  def clearStatusText(self):
1033
1053
  """Clear any displayed status text"""
1034
1054
  self.uiStatusLBL.clear()
1035
1055
  self.uiMenuBar.adjustSize()
1036
1056
 
1037
1057
  def autoHideStatusText(self):
1038
- """Set timer to automatically clear status text"""
1039
- if self.statusTimer.isActive():
1040
- self.statusTimer.stop()
1041
- self.statusTimer.singleShot(2000, self.clearStatusText)
1058
+ """Set timer to automatically clear status text.
1059
+
1060
+ If timer is already running, it will be automatically stopped first (We can't
1061
+ use static method QTimer.singleShot for this)
1062
+ """
1042
1063
  self.statusTimer.start()
1043
1064
 
1044
1065
  def setStyleSheet(self, stylesheet, recordPrefs=True):
@@ -1166,9 +1187,9 @@ class LoggerWindow(Window):
1166
1187
 
1167
1188
  def setWordWrap(self, state):
1168
1189
  if state:
1169
- self.uiConsoleTXT.setLineWrapMode(self.uiConsoleTXT.WidgetWidth)
1190
+ self.uiConsoleTXT.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth)
1170
1191
  else:
1171
- self.uiConsoleTXT.setLineWrapMode(self.uiConsoleTXT.NoWrap)
1192
+ self.uiConsoleTXT.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
1172
1193
 
1173
1194
  def show_about(self):
1174
1195
  """Shows `preditor.about_preditor()`'s output in a message box."""
@@ -1248,7 +1269,7 @@ class LoggerWindow(Window):
1248
1269
 
1249
1270
  # if this is the global instance, then allow it to be deleted on close
1250
1271
  if self == LoggerWindow._instance:
1251
- self.setAttribute(Qt.WA_DeleteOnClose, True)
1272
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
1252
1273
  LoggerWindow._instance = None
1253
1274
 
1254
1275
  # clear out the system
@@ -1339,7 +1360,7 @@ class LoggerWindow(Window):
1339
1360
  )
1340
1361
 
1341
1362
  # protect the memory
1342
- inst.setAttribute(Qt.WA_DeleteOnClose, False)
1363
+ inst.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
1343
1364
 
1344
1365
  # cache the instance
1345
1366
  LoggerWindow._instance = inst
@@ -56,4 +56,6 @@ class SetTextEditorPathDialog(QDialog):
56
56
  else:
57
57
  msg = "That path doesn't exists or isn't an executable file."
58
58
  label = 'Incorrect Path'
59
- QMessageBox.warning(self.window(), label, msg, QMessageBox.Ok)
59
+ QMessageBox.warning(
60
+ self.window(), label, msg, QMessageBox.StandardButton.Ok
61
+ )
@@ -92,7 +92,7 @@
92
92
  <x>0</x>
93
93
  <y>0</y>
94
94
  <width>796</width>
95
- <height>21</height>
95
+ <height>22</height>
96
96
  </rect>
97
97
  </property>
98
98
  <widget class="QMenu" name="uiDebugMENU">
@@ -193,6 +193,8 @@
193
193
  <addaction name="uiSetFlashWindowIntervalACT"/>
194
194
  <addaction name="separator"/>
195
195
  <addaction name="uiErrorHyperlinksACT"/>
196
+ <addaction name="uiSeparateTracebackACT"/>
197
+ <addaction name="separator"/>
196
198
  <addaction name="uiSetPreferredTextEditorPathACT"/>
197
199
  <addaction name="uiSetWorkboxEditorACT"/>
198
200
  </widget>
@@ -1003,6 +1005,14 @@ at the indicated line in the specified text editor.
1003
1005
  <string>Highlight Exact Completion</string>
1004
1006
  </property>
1005
1007
  </action>
1008
+ <action name="uiSeparateTracebackACT">
1009
+ <property name="checkable">
1010
+ <bool>true</bool>
1011
+ </property>
1012
+ <property name="text">
1013
+ <string>Visually Separate PrEditor Traceback</string>
1014
+ </property>
1015
+ </action>
1006
1016
  </widget>
1007
1017
  <customwidgets>
1008
1018
  <customwidget>
preditor/gui/window.py CHANGED
@@ -25,7 +25,7 @@ class Window(QMainWindow):
25
25
  if not cls._instance:
26
26
  cls._instance = cls(parent=parent)
27
27
  # protect the memory
28
- cls._instance.setAttribute(Qt.WA_DeleteOnClose, False)
28
+ cls._instance.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
29
29
  return cls._instance
30
30
 
31
31
  def __init__(self, parent=None, flags=0):
@@ -67,7 +67,7 @@ class Window(QMainWindow):
67
67
  # dead dialogs
68
68
 
69
69
  # set the delete attribute to clean up the window once it is closed
70
- self.setAttribute(Qt.WA_DeleteOnClose, True)
70
+ self.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
71
71
  # If this value is set to False calling setGeometry on this window will not
72
72
  # adjust the geometry to ensure the window is on a valid screen.
73
73
  self.checkScreenGeo = True
@@ -103,7 +103,7 @@ class Window(QMainWindow):
103
103
  def closeEvent(self, event):
104
104
  # ensure this object gets deleted
105
105
  wwidget = None
106
- if self.testAttribute(Qt.WA_DeleteOnClose):
106
+ if self.testAttribute(Qt.WidgetAttribute.WA_DeleteOnClose):
107
107
  # collect the win widget to uncache it
108
108
  if self.parent() and self.parent().inherits('QWinWidget'):
109
109
  wwidget = self.parent()
@@ -141,7 +141,7 @@ class Window(QMainWindow):
141
141
  # allow the global instance to be cleared
142
142
  if this == cls._instance:
143
143
  cls._instance = None
144
- this.setAttribute(Qt.WA_DeleteOnClose, True)
144
+ this.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, True)
145
145
  try:
146
146
  this.close()
147
147
  except RuntimeError:
@@ -1,9 +1,11 @@
1
1
  from __future__ import absolute_import, print_function
2
2
 
3
+ import io
3
4
  import os
4
5
  import tempfile
5
6
  import textwrap
6
7
 
8
+ import chardet
7
9
  from Qt.QtCore import Qt
8
10
  from Qt.QtWidgets import QStackedWidget
9
11
 
@@ -82,8 +84,8 @@ class WorkboxMixin(object):
82
84
  txt = '\n' * line + txt
83
85
 
84
86
  # execute the code
85
- filename = self.__workbox_filename__(selection=True)
86
- ret, was_eval = self.__console__().executeString(txt, filename=filename)
87
+ title = self.__workbox_trace_title__(selection=True)
88
+ ret, was_eval = self.__console__().executeString(txt, filename=title)
87
89
  if was_eval:
88
90
  # If the selected code was a statement print the result of the statement.
89
91
  ret = repr(ret)
@@ -143,7 +145,7 @@ class WorkboxMixin(object):
143
145
 
144
146
  return group, editor
145
147
 
146
- def __workbox_filename__(self, selection=False):
148
+ def __workbox_trace_title__(self, selection=False):
147
149
  title = "WorkboxSelection" if selection else "Workbox"
148
150
  group, editor = self.__group_tab_index__()
149
151
  if group == -1 or editor == -1:
@@ -317,14 +319,37 @@ class WorkboxMixin(object):
317
319
  os.remove(tempfile)
318
320
 
319
321
  @classmethod
320
- def __open_file__(cls, filename):
321
- with open(filename) as fle:
322
- return fle.read()
323
- return ""
322
+ def __open_file__(cls, filename, strict=True):
323
+ """Open a file and try to detect the text encoding it was saved as.
324
+
325
+ Returns:
326
+ encoding(str): The detected encoding, Defaults to "utf-8" if unable
327
+ to detect encoding.
328
+ text(str): The contents of the file decoded to a str.
329
+ """
330
+ with open(filename, "rb") as f:
331
+ text_bytes = f.read()
332
+
333
+ # Open file, detect source encoding and convert to utf-8
334
+ encoding = chardet.detect(text_bytes)['encoding'] or 'utf-8'
335
+ try:
336
+ text = text_bytes.decode(encoding)
337
+ except UnicodeDecodeError as e:
338
+ if strict:
339
+ raise UnicodeDecodeError( # noqa: B904
340
+ e.encoding,
341
+ e.object,
342
+ e.start,
343
+ e.end,
344
+ f"{e.reason}, Filename: {filename}",
345
+ )
346
+ encoding = 'utf-8'
347
+ text = text_bytes.decode(encoding, errors="ignore")
348
+ return encoding, text
324
349
 
325
350
  @classmethod
326
- def __write_file__(cls, filename, txt):
327
- with open(filename, 'w') as fle:
351
+ def __write_file__(cls, filename, txt, encoding=None):
352
+ with io.open(filename, 'w', newline='\n', encoding=encoding) as fle:
328
353
  fle.write(txt)
329
354
 
330
355
  def __show__(self):
@@ -335,7 +360,7 @@ class WorkboxMixin(object):
335
360
  if self._filename_pref:
336
361
  self.__load__(self._filename_pref)
337
362
  elif self._tempfile:
338
- txt = self.__open_file__(self.__tempfile__())
363
+ _, txt = self.__open_file__(self.__tempfile__(), strict=False)
339
364
  self.__set_text__(txt)
340
365
 
341
366
  def process_shortcut(self, event, run=True):
@@ -365,10 +390,14 @@ class WorkboxMixin(object):
365
390
  modifiers = event.modifiers()
366
391
 
367
392
  # 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
393
+ ret = key == Qt.Key.Key_Return
394
+ enter = key == Qt.Key.Key_Enter
395
+ shift = modifiers == Qt.KeyboardModifier.ShiftModifier
396
+ ctrlShift = (
397
+ modifiers
398
+ == Qt.KeyboardModifier.ControlModifier
399
+ | Qt.KeyboardModifier.ShiftModifier
400
+ )
372
401
 
373
402
  # Determine which actions to take
374
403
  evalTrunc = enter or (ret and shift)
@@ -27,6 +27,7 @@ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
27
27
  ):
28
28
  super(WorkboxTextEdit, self).__init__(parent=parent, core_name=core_name)
29
29
  self._filename = None
30
+ self._encoding = None
30
31
  self.__set_console__(console)
31
32
  highlight = CodeHighlighter(self)
32
33
  highlight.setLanguage('Python')
@@ -56,15 +57,15 @@ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
56
57
 
57
58
  def __exec_all__(self):
58
59
  txt = self.__text__().rstrip()
59
- filename = self.__workbox_filename__()
60
- self.__console__().executeString(txt, filename=filename)
60
+ title = self.__workbox_trace_title__()
61
+ self.__console__().executeString(txt, filename=title)
61
62
 
62
63
  def __font__(self):
63
64
  return self.font()
64
65
 
65
66
  def __set_font__(self, font):
66
67
  metrics = QFontMetrics(font)
67
- self.setTabStopDistance(metrics.width(" ") * 4)
68
+ self.setTabStopDistance(metrics.horizontalAdvance(" ") * 4)
68
69
  super(WorkboxTextEdit, self).setFont(font)
69
70
 
70
71
  def __goto_line__(self, line):
@@ -79,7 +80,8 @@ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
79
80
 
80
81
  def __load__(self, filename):
81
82
  self._filename = filename
82
- txt = self.__open_file__(self._filename)
83
+ enc, txt = self.__open_file__(self._filename)
84
+ self._encoding = enc
83
85
  self.__set_text__(txt)
84
86
 
85
87
  def __margins_font__(self):
@@ -114,7 +116,7 @@ class WorkboxTextEdit(WorkboxMixin, QTextEdit):
114
116
 
115
117
  selectText = self.window().uiSelectTextACT.isChecked() or selectText
116
118
  if selectText:
117
- cursor.select(QTextCursor.LineUnderCursor)
119
+ cursor.select(QTextCursor.SelectionType.LineUnderCursor)
118
120
  self.setTextCursor(cursor)
119
121
 
120
122
  return text, line
@@ -1,6 +1,5 @@
1
1
  from __future__ import absolute_import, print_function
2
2
 
3
- import io
4
3
  import re
5
4
  import time
6
5
 
@@ -10,6 +9,7 @@ from Qt.QtWidgets import QAction
10
9
 
11
10
  from .. import core, resourcePath
12
11
  from ..gui.workbox_mixin import WorkboxMixin
12
+ from ..scintilla import QsciScintilla
13
13
  from ..scintilla.documenteditor import DocumentEditor, SearchOptions
14
14
  from ..scintilla.finddialog import FindDialog
15
15
 
@@ -36,15 +36,19 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
36
36
  self.initShortcuts()
37
37
  self.setLanguage('Python')
38
38
  # Default to unix newlines
39
- self.setEolMode(self.EolUnix)
39
+ self.setEolMode(QsciScintilla.EolMode.EolUnix)
40
40
  if hasattr(self.window(), "setWorkboxFontBasedOnConsole"):
41
41
  self.window().setWorkboxFontBasedOnConsole()
42
42
 
43
43
  def __auto_complete_enabled__(self):
44
- return self.autoCompletionSource() == self.AcsAll
44
+ return self.autoCompletionSource() == QsciScintilla.AutoCompletionSource.AcsAll
45
45
 
46
46
  def __set_auto_complete_enabled__(self, state):
47
- state = self.AcsAll if state else self.AcsNone
47
+ state = (
48
+ QsciScintilla.AutoCompletionSource.AcsAll
49
+ if state
50
+ else QsciScintilla.AutoCompletionSource.AcsNone
51
+ )
48
52
  self.setAutoCompletionSource(state)
49
53
 
50
54
  def __clear__(self):
@@ -69,8 +73,8 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
69
73
 
70
74
  def __exec_all__(self):
71
75
  txt = self.__unix_end_lines__(self.text()).rstrip()
72
- filename = self.__workbox_filename__()
73
- self.__console__().executeString(txt, filename=filename)
76
+ title = self.__workbox_trace_title__()
77
+ self.__console__().executeString(txt, filename=title)
74
78
 
75
79
  def __file_monitoring_enabled__(self):
76
80
  return self._fileMonitoringActive
@@ -120,7 +124,7 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
120
124
  try:
121
125
  marker = self._marker
122
126
  except AttributeError:
123
- self._marker = self.markerDefine(self.Circle)
127
+ self._marker = self.markerDefine(QsciScintilla.MarkerSymbol.Circle)
124
128
  marker = self._marker
125
129
  self.markerAdd(line, marker)
126
130
 
@@ -146,7 +150,12 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
146
150
 
147
151
  def __selected_text__(self, start_of_line=False, selectText=False):
148
152
  line, s, end, e = self.getSelection()
149
- if line == -1:
153
+
154
+ # Sometime self.getSelection returns values that equate to a non-existent
155
+ # selection, ie start and end are the same, so let's process it as if there is
156
+ # no selection
157
+ selectionIsEmpty = line == end and s == e
158
+ if line == -1 or selectionIsEmpty:
150
159
  # Nothing is selected, return the current line of text
151
160
  line, index = self.getCursorPosition()
152
161
  txt = self.text(line)
@@ -184,7 +193,7 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
184
193
  Returns:
185
194
  str: The requested text.
186
195
  """
187
- if line:
196
+ if line is not None:
188
197
  return self.text(line)
189
198
  elif (start is None) != (end is None):
190
199
  raise ValueError('You must pass start and end if you pass either.')
@@ -197,10 +206,10 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
197
206
  self.setText(txt)
198
207
 
199
208
  @classmethod
200
- def __write_file__(cls, filename, txt):
201
- with io.open(filename, 'w', newline='\n') as fle:
202
- # Save unix newlines for simplicity
203
- fle.write(cls.__unix_end_lines__(txt))
209
+ def __write_file__(cls, filename, txt, encoding=None):
210
+ # Save unix newlines for simplicity
211
+ txt = cls.__unix_end_lines__(txt)
212
+ super(WorkboxWidget, cls).__write_file__(filename, txt, encoding=encoding)
204
213
 
205
214
  def keyPressEvent(self, event):
206
215
  """Check for certain keyboard shortcuts, and handle them as needed,
@@ -216,13 +225,13 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
216
225
  when Return is pressed), so this combination is not detectable.
217
226
  """
218
227
  if self._software == 'softimage':
219
- DocumentEditor.keyPressEvent(self, event)
228
+ super(WorkboxWidget, self).keyPressEvent(event)
220
229
  else:
221
230
  if self.process_shortcut(event):
222
231
  return
223
232
  else:
224
233
  # Send regular keystroke
225
- DocumentEditor.keyPressEvent(self, event)
234
+ super(WorkboxWidget, self).keyPressEvent(event)
226
235
 
227
236
  def initShortcuts(self):
228
237
  """Use this to set up shortcuts when the DocumentEditor"""
@@ -248,7 +257,7 @@ class WorkboxWidget(WorkboxMixin, DocumentEditor):
248
257
 
249
258
  # create the search dialog and connect actions
250
259
  self._searchDialog = FindDialog(self)
251
- self._searchDialog.setAttribute(Qt.WA_DeleteOnClose, False)
260
+ self._searchDialog.setAttribute(Qt.WidgetAttribute.WA_DeleteOnClose, False)
252
261
  self.uiFindACT.triggered.connect(
253
262
  lambda: self._searchDialog.search(self.searchText())
254
263
  )
@@ -9,9 +9,12 @@ from .prefs import prefs_path
9
9
 
10
10
 
11
11
  class LoggingConfig(object):
12
- def __init__(self, core_name, version=1):
12
+ def __init__(self, core_name, version=1, disable_existing_loggers=False):
13
13
  self._filename = None
14
- self.cfg = {'version': version}
14
+ self.cfg = {
15
+ 'version': version,
16
+ 'disable_existing_loggers': disable_existing_loggers,
17
+ }
15
18
  self.core_name = core_name
16
19
 
17
20
  def add_logger(self, name, logger):
@@ -1,5 +1,23 @@
1
1
  from __future__ import absolute_import
2
2
 
3
+ __all__ = ["delayables", "FindState", "Qsci", "QsciScintilla"]
4
+
5
+ import Qt
6
+
7
+ if Qt.IsPyQt6:
8
+ from PyQt6 import Qsci
9
+ from PyQt6.Qsci import QsciScintilla
10
+ elif Qt.IsPyQt5:
11
+ from PyQt5 import Qsci
12
+ from PyQt5.Qsci import QsciScintilla
13
+ elif Qt.IsPyQt4:
14
+ from PyQt4 import Qsci
15
+ from PyQt4.Qsci import QsciScintilla
16
+ else:
17
+ raise ImportError(
18
+ "QScintilla library is not supported by {}".format(Qt.__binding__)
19
+ )
20
+
3
21
 
4
22
  class FindState(object):
5
23
  """
@@ -19,4 +37,4 @@ class FindState(object):
19
37
  self.end_pos = None
20
38
 
21
39
 
22
- from . import delayables # noqa: F401, E402
40
+ from . import delayables # noqa: E402