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.
- preditor/__init__.py +4 -1
- preditor/about_module.py +6 -2
- preditor/dccs/.hab.json +10 -0
- preditor/dccs/maya/PrEditor_maya.mod +0 -1
- preditor/dccs/maya/README.md +22 -0
- preditor/dccs/maya/plug-ins/PrEditor_maya.py +32 -1
- preditor/dccs/studiomax/PackageContents.xml +32 -0
- preditor/dccs/studiomax/PrEditor-PrEditor_Show.mcr +8 -0
- preditor/dccs/studiomax/README.md +17 -0
- preditor/dccs/studiomax/preditor.ms +16 -0
- preditor/dccs/studiomax/preditor_menu.mnx +7 -0
- preditor/debug.py +7 -3
- preditor/excepthooks.py +1 -1
- preditor/gui/app.py +2 -2
- preditor/gui/codehighlighter.py +10 -24
- preditor/gui/completer.py +17 -6
- preditor/gui/console.py +94 -47
- preditor/gui/dialog.py +10 -7
- preditor/gui/drag_tab_bar.py +7 -7
- preditor/gui/errordialog.py +2 -2
- preditor/gui/find_files.py +7 -5
- preditor/gui/fuzzy_search/fuzzy_search.py +8 -4
- preditor/gui/group_tab_widget/__init__.py +32 -4
- preditor/gui/group_tab_widget/grouped_tab_models.py +4 -4
- preditor/gui/group_tab_widget/grouped_tab_widget.py +6 -4
- preditor/gui/level_buttons.py +16 -1
- preditor/gui/loggerwindow.py +48 -27
- preditor/gui/set_text_editor_path_dialog.py +3 -1
- preditor/gui/ui/loggerwindow.ui +11 -1
- preditor/gui/window.py +4 -4
- preditor/gui/workbox_mixin.py +43 -14
- preditor/gui/workbox_text_edit.py +7 -5
- preditor/gui/workboxwidget.py +25 -16
- preditor/logging_config.py +5 -2
- preditor/scintilla/__init__.py +19 -1
- preditor/scintilla/delayables/smart_highlight.py +7 -4
- preditor/scintilla/delayables/spell_check.py +5 -4
- preditor/scintilla/documenteditor.py +228 -135
- preditor/scintilla/finddialog.py +3 -3
- preditor/scintilla/lang/language.py +1 -1
- preditor/scintilla/lexers/cpplexer.py +3 -2
- preditor/scintilla/lexers/javascriptlexer.py +6 -4
- preditor/scintilla/lexers/maxscriptlexer.py +8 -7
- preditor/scintilla/lexers/mellexer.py +3 -2
- preditor/scintilla/lexers/mulexer.py +3 -2
- preditor/scintilla/lexers/pythonlexer.py +7 -6
- preditor/utils/cute.py +9 -8
- preditor/version.py +16 -3
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/METADATA +69 -32
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/RECORD +56 -47
- preditor-1.3.0.dist-info/top_level.txt +3 -0
- tests/find_files/test_find_files.py +74 -0
- tests/ide/test_delayable_engine.py +171 -0
- preditor-1.1.0.dist-info/top_level.txt +0 -1
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/WHEEL +0 -0
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/entry_points.txt +0 -0
- {preditor-1.1.0.dist-info → preditor-1.3.0.dist-info}/licenses/LICENSE +0 -0
preditor/gui/level_buttons.py
CHANGED
|
@@ -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
|
|
preditor/gui/loggerwindow.py
CHANGED
|
@@ -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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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() & (
|
|
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':
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
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(
|
|
1190
|
+
self.uiConsoleTXT.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth)
|
|
1170
1191
|
else:
|
|
1171
|
-
self.uiConsoleTXT.setLineWrapMode(
|
|
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(
|
|
59
|
+
QMessageBox.warning(
|
|
60
|
+
self.window(), label, msg, QMessageBox.StandardButton.Ok
|
|
61
|
+
)
|
preditor/gui/ui/loggerwindow.ui
CHANGED
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<x>0</x>
|
|
93
93
|
<y>0</y>
|
|
94
94
|
<width>796</width>
|
|
95
|
-
<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:
|
preditor/gui/workbox_mixin.py
CHANGED
|
@@ -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
|
-
|
|
86
|
-
ret, was_eval = self.__console__().executeString(txt, 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
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
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 =
|
|
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
|
-
|
|
60
|
-
self.__console__().executeString(txt, 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.
|
|
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
|
preditor/gui/workboxwidget.py
CHANGED
|
@@ -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(
|
|
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() ==
|
|
44
|
+
return self.autoCompletionSource() == QsciScintilla.AutoCompletionSource.AcsAll
|
|
45
45
|
|
|
46
46
|
def __set_auto_complete_enabled__(self, state):
|
|
47
|
-
state =
|
|
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
|
-
|
|
73
|
-
self.__console__().executeString(txt, 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(
|
|
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
|
-
|
|
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
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
)
|
preditor/logging_config.py
CHANGED
|
@@ -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 = {
|
|
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):
|
preditor/scintilla/__init__.py
CHANGED
|
@@ -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:
|
|
40
|
+
from . import delayables # noqa: E402
|