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/gui/dialog.py ADDED
@@ -0,0 +1,178 @@
1
+ from __future__ import absolute_import
2
+
3
+ from Qt.QtCore import Qt
4
+ from Qt.QtWidgets import QDialog
5
+
6
+ from .. import config, relativePath
7
+
8
+
9
+ class Dialog(QDialog):
10
+ _instance = None
11
+
12
+ @classmethod
13
+ def instance(cls, parent=None):
14
+ """If you only want to have one instance of a dialog, use this method instead
15
+ of creating a new dialog. It will only create a new instance of the class if
16
+ the class variable _instance is none.
17
+
18
+ Args:
19
+ parent (QWidget, optional):The parent widget
20
+
21
+ Returns:
22
+ Dialog:
23
+ """
24
+ if not cls._instance:
25
+ cls._instance = cls(parent=parent)
26
+ # protect the memory
27
+ cls._instance.setAttribute(Qt.WA_DeleteOnClose, False)
28
+ return cls._instance
29
+
30
+ def __init__(
31
+ self, parent=None, flags=Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint
32
+ ):
33
+ # if there is no root, create
34
+ if not parent:
35
+ parent = config.root_window()
36
+
37
+ # create a QDialog
38
+ if flags:
39
+ QDialog.__init__(self, parent, flags)
40
+ else:
41
+ QDialog.__init__(self, parent)
42
+
43
+ # INFO
44
+ #
45
+ # As far as we can tell, the purpose for this class is keeping live
46
+ # references to the subclasses so they don't get garbage collected, all while
47
+ # getting around having to actively maintain a list of running dialogs.
48
+ #
49
+ # Generally, setting WA_DeleteOnClose to False, and keeping the _instance
50
+ # variable around will do the trick for pseudo-singleton dialogs. (created with
51
+ # instance=True)
52
+ #
53
+ # However, for non-instanced dialogs where multiples are allowed, deleteOnClose
54
+ # is set to True and no _instance variable is set. Because there are no live
55
+ # references to the dialog, it is closed and garbage collected almost
56
+ # immediately in certain programs (xsi, maya).
57
+ #
58
+ # The current workaround is to manually set WA_DeleteOnClose to False, however
59
+ # this causes any subclasses to stick around in memory even when the
60
+ # window/dialog is closed. So you also have to manually set WA_DeleteOnClose to
61
+ # True in the sub-classed .closeEvent() method before you call super()
62
+ #
63
+ # It is completely possible to write some code that would automatically handle
64
+ # this, and it is CERTAINLY something we can/will be doing in the future, but
65
+ # for now we're not quite sure how that would affect the production tools.
66
+ # Technically this is a problem, but there are currently no consequences from an
67
+ # artist standpoint because we have more than enough memory to hold all those
68
+ # dead dialogs
69
+
70
+ # set the delete attribute to clean up the window once it is closed
71
+ self.setAttribute(Qt.WA_DeleteOnClose, True)
72
+
73
+ # set this property to true to properly handle tracking events to control
74
+ # keyboard overrides
75
+ self.setMouseTracking(True)
76
+
77
+ # If this value is set to False calling setGeometry on this dialog will not
78
+ # adjust the geometry to ensure the dialog is on a valid screen.
79
+ self.checkScreenGeo = True
80
+ # attempt to set the dialog icon
81
+ import os
82
+ import sys
83
+
84
+ from Qt.QtGui import QIcon
85
+
86
+ try:
87
+ path = relativePath(
88
+ os.path.abspath(sys.modules[self.__class__.__module__].__file__),
89
+ 'img/icon.png',
90
+ )
91
+ if os.path.exists(path):
92
+ self.setWindowIcon(QIcon(path))
93
+ except (AttributeError, KeyError):
94
+ pass
95
+
96
+ def _shouldDisableAccelerators(self, old, now):
97
+ """Used to enable typing in DCC's that require it(Max 2018).
98
+
99
+ Args:
100
+ old (QWidget or None): The QWidget that lost focus.
101
+ new (QWidget or None): The QWidget that gained focus.
102
+
103
+ Returns:
104
+ bool: If accelerators should be disabled.
105
+ """
106
+ # By default we always want to disable accelerators.
107
+ return True
108
+
109
+ def closeEvent(self, event):
110
+ # ensure this object gets deleted
111
+ wwidget = None
112
+ if self.testAttribute(Qt.WA_DeleteOnClose):
113
+ # collect the win widget to uncache it
114
+ if self.parent() and self.parent().inherits('QWinWidget'):
115
+ wwidget = self.parent()
116
+
117
+ QDialog.closeEvent(self, event)
118
+
119
+ # uncache the win widget if necessary
120
+ if wwidget:
121
+ from .winwidget import WinWidget
122
+
123
+ WinWidget.uncache(wwidget)
124
+
125
+ def exec_(self):
126
+ # do not use the DeleteOnClose attribute when executing a dialog as often times
127
+ # a user will be accessing information from the dialog instance after it closes.
128
+ # This function properly transfers ownership of the dialog instance back to
129
+ # Python anyway
130
+
131
+ self.setAttribute(Qt.WA_DeleteOnClose, False)
132
+
133
+ # execute the dialog
134
+ return QDialog.exec_(self)
135
+
136
+ def setGeometry(self, *args):
137
+ """
138
+ Sets the dialog's geometry, It will also check if the geometry is visible on any
139
+ monitors. If it is not it will move the dialog so it is visible. This can be
140
+ disabled by setting self.checkScreenGeo to False
141
+ """
142
+ super(Dialog, self).setGeometry(*args)
143
+ if self.checkScreenGeo:
144
+ from ..utils.cute import ensureWindowIsVisible
145
+
146
+ ensureWindowIsVisible(self)
147
+
148
+ def shutdown(self):
149
+ # use a @classmethod to make inheritance magically work
150
+ self._shutdown(self)
151
+
152
+ @classmethod
153
+ def _shutdown(cls, this):
154
+ """
155
+ If this item is the class instance properly close it and remove it from memory
156
+ so it can be recreated.
157
+ """
158
+ # allow the global instance to be cleared
159
+ if this == cls._instance:
160
+ cls._instance = None
161
+ this.setAttribute(Qt.WA_DeleteOnClose, True)
162
+ try:
163
+ this.close()
164
+ except RuntimeError:
165
+ pass
166
+
167
+ @classmethod
168
+ def instance_shutdown(cls):
169
+ """Call shutdown on this class instance only if the class was instantiated.
170
+
171
+ Returns:
172
+ bool: if cls.instance().shutdown() needed to be called.
173
+ """
174
+ instance = cls._instance
175
+ if instance:
176
+ instance.shutdown()
177
+ return True
178
+ return False
@@ -0,0 +1,190 @@
1
+ from __future__ import absolute_import
2
+
3
+ from Qt.QtCore import QByteArray, QMimeData, QPoint, QRect, Qt
4
+ from Qt.QtGui import QCursor, QDrag, QPixmap, QRegion
5
+ from Qt.QtWidgets import QInputDialog, QMenu, QTabBar
6
+
7
+
8
+ class DragTabBar(QTabBar):
9
+ """A QTabBar that allows you to drag and drop its tabs to other DragTabBar's
10
+ while still allowing you to move tabs normally.
11
+
12
+ In most cases you should use `install_tab_widget` to create and add this TabBar
13
+ to a QTabWidget. It takes care of enabling usability features of QTabWidget's.
14
+
15
+ Args:
16
+ mime_type (str, optional): Only accepts dropped tabs that implement this
17
+ Mime Type. Tabs dragged off of this TabBar will have this Mime Type
18
+ implemented.
19
+
20
+ Based on code by ARussel: https://forum.qt.io/post/420469
21
+ """
22
+
23
+ def __init__(self, parent=None, mime_type='DragTabBar'):
24
+ super(DragTabBar, self).__init__(parent=parent)
25
+ self.setAcceptDrops(True)
26
+ self.setMouseTracking(True)
27
+ self._mime_data = None
28
+ self._context_menu_tab = -1
29
+ self.mime_type = mime_type
30
+
31
+ def mouseMoveEvent(self, event): # noqa: N802
32
+
33
+ if not self._mime_data:
34
+ return super(DragTabBar, self).mouseMoveEvent(event)
35
+
36
+ # Check if the mouse has moved outside of the widget, if not, let
37
+ # the QTabBar handle the internal tab movement.
38
+ event_pos = event.pos()
39
+ global_pos = self.mapToGlobal(event_pos)
40
+ bar_geo = QRect(self.mapToGlobal(self.pos()), self.size())
41
+ inside = bar_geo.contains(global_pos)
42
+ if inside:
43
+ return super(DragTabBar, self).mouseMoveEvent(event)
44
+
45
+ # The user has moved the tab outside of the QTabBar, remove the tab from
46
+ # this tab bar and store it in the MimeData, initiating a drag event.
47
+ widget = self._mime_data.property('widget')
48
+ tab_index = self.parentWidget().indexOf(widget)
49
+ self.parentWidget().removeTab(tab_index)
50
+ pos_in_tab = self.mapFromGlobal(global_pos)
51
+ drag = QDrag(self)
52
+ drag.setMimeData(self._mime_data)
53
+ drag.setPixmap(self._mime_data.imageData())
54
+ drag.setHotSpot(event_pos - pos_in_tab)
55
+ cursor = QCursor(Qt.OpenHandCursor)
56
+ drag.setDragCursor(cursor.pixmap(), Qt.MoveAction)
57
+ action = drag.exec_(Qt.MoveAction)
58
+ # If the user didn't successfully add this to a new tab widget, restore
59
+ # the tab to the original location.
60
+ if action == Qt.IgnoreAction:
61
+ original_tab_index = self._mime_data.property('original_tab_index')
62
+ self.parentWidget().insertTab(
63
+ original_tab_index, widget, self._mime_data.text()
64
+ )
65
+
66
+ self._mime_data = None
67
+
68
+ def mousePressEvent(self, event): # noqa: N802
69
+ if event.button() == Qt.LeftButton and not self._mime_data:
70
+ tab_index = self.tabAt(event.pos())
71
+
72
+ # While we don't remove the tab on mouse press, capture its tab image
73
+ # and attach it to the mouse. This also stores info needed to handle
74
+ # moving the tab to a new QTabWidget, and undoing the move if the
75
+ # user cancels the drop.
76
+ tab_rect = self.tabRect(tab_index)
77
+ pixmap = QPixmap(tab_rect.size())
78
+ self.render(pixmap, QPoint(), QRegion(tab_rect))
79
+
80
+ self._mime_data = QMimeData()
81
+ self._mime_data.setData(self.mime_type, QByteArray())
82
+ self._mime_data.setText(self.tabText(tab_index))
83
+ self._mime_data.setProperty('original_tab_index', tab_index)
84
+ self._mime_data.setImageData(pixmap)
85
+ widget = self.parentWidget().widget(tab_index)
86
+ self._mime_data.setProperty('widget', widget)
87
+
88
+ # By default if there are no tabs, the tab bar is hidden. This
89
+ # prevents users from re-adding tabs to the tab bar as only it
90
+ # accepts the tab drops. This preserves the tab bar height
91
+ # after it was drawn with a tab so it should automatically stay
92
+ # the same visual height.
93
+ if not self.minimumHeight():
94
+ self.setMinimumHeight(self.height())
95
+
96
+ super(DragTabBar, self).mousePressEvent(event)
97
+
98
+ def mouseReleaseEvent(self, event): # noqa: N802
99
+ self._mime_data = None
100
+ super(DragTabBar, self).mouseReleaseEvent(event)
101
+
102
+ def dragEnterEvent(self, event): # noqa: N802
103
+ # if event.mimeData().hasFormat(self.mime_type):
104
+ event.accept()
105
+
106
+ def dragLeaveEvent(self, event): # noqa: N802
107
+ event.accept()
108
+
109
+ def dragMoveEvent(self, event): # noqa: N802
110
+ # If this is not a tab of the same mime type, make the tab under the mouse
111
+ # the current tab so users can easily drop inside that tab.
112
+ if not event.mimeData().hasFormat(self.mime_type):
113
+ event.accept()
114
+ tab_index = self.tabAt(event.pos())
115
+ if tab_index == -1:
116
+ tab_index = self.count() - 1
117
+ if self.currentIndex() != tab_index:
118
+ self.setCurrentIndex(tab_index)
119
+
120
+ def dropEvent(self, event): # noqa: N802
121
+ if not event.mimeData().hasFormat(self.mime_type):
122
+ return
123
+ if event.source().parentWidget() == self:
124
+ return
125
+
126
+ event.setDropAction(Qt.MoveAction)
127
+ event.accept()
128
+ counter = self.count()
129
+
130
+ mime_data = event.mimeData()
131
+ if counter == 0:
132
+ self.parent().addTab(mime_data.property('widget'), mime_data.text())
133
+ else:
134
+ self.parent().insertTab(
135
+ counter + 1, mime_data.property('widget'), mime_data.text()
136
+ )
137
+
138
+ def rename_tab(self):
139
+ """Used by the tab_menu to rename the tab at index `_context_menu_tab`."""
140
+ if self._context_menu_tab != -1:
141
+ current = self.tabText(self._context_menu_tab)
142
+ msg = 'Rename the {} tab to:'.format(current)
143
+ name, success = QInputDialog.getText(self, 'Rename Tab', msg, text=current)
144
+ if success:
145
+ self.setTabText(self._context_menu_tab, name)
146
+
147
+ def tab_menu(self, pos, popup=True):
148
+ """Creates the custom context menu for the tab bar. To customize the menu
149
+ call super setting `popup=False`. This will return the menu for
150
+ customization and you will then need to call popup on the menu.
151
+
152
+ This method sets the tab index the user right clicked on in the variable
153
+ `_context_menu_tab`. This can be used in the triggered QAction methods."""
154
+
155
+ self._context_menu_tab = self.tabAt(pos)
156
+ if self._context_menu_tab == -1:
157
+ return
158
+ menu = QMenu(self)
159
+ act = menu.addAction('Rename')
160
+ act.triggered.connect(self.rename_tab)
161
+
162
+ if popup:
163
+ menu.popup(self.mapToGlobal(pos))
164
+
165
+ return menu
166
+
167
+ @classmethod
168
+ def install_tab_widget(cls, tab_widget, mime_type='DragTabBar', menu=True):
169
+ """Creates and returns a instance of DragTabBar and installs it on the
170
+ QTabWidget. This enables movable tabs, and enables document mode.
171
+ Document mode makes the tab bar expand to the size of the QTabWidget so
172
+ drag drop operations are more intuitive.
173
+
174
+ Args:
175
+ tab_widget (QTabWidget): The QTabWidget to install the tab bar on.
176
+ mime_data (str, optional): This TabBar will only accept tab drop
177
+ operations with this mime type.
178
+ menu (bool, optional): Install a custom context menu on the bar bar.
179
+ Override `tab_menu` to customize the menu.
180
+ """
181
+ bar = cls(tab_widget, mime_type=mime_type)
182
+ tab_widget.setTabBar(bar)
183
+ tab_widget.setMovable(True)
184
+ tab_widget.setDocumentMode(True)
185
+
186
+ if menu:
187
+ bar.setContextMenuPolicy(Qt.CustomContextMenu)
188
+ bar.customContextMenuRequested.connect(bar.tab_menu)
189
+
190
+ return bar
@@ -0,0 +1,57 @@
1
+ from __future__ import absolute_import
2
+
3
+ from Qt.QtCore import Slot
4
+ from Qt.QtGui import QIcon
5
+ from Qt.QtWidgets import QWidget
6
+
7
+ from .. import plugins, resourcePath
8
+ from ..gui import loadUi
9
+
10
+
11
+ class EditorChooser(QWidget):
12
+ """A widget that lets the user choose from a a list of available editors."""
13
+
14
+ def __init__(self, parent=None, editor_name=None):
15
+ super(EditorChooser, self).__init__(parent=parent)
16
+ loadUi(__file__, self)
17
+ icon = QIcon(resourcePath('img/warning-big.png'))
18
+ self.uiWarningIconLBL.setPixmap(icon.pixmap(icon.availableSizes()[0]))
19
+ if editor_name:
20
+ self.set_editor_name(editor_name)
21
+
22
+ def editor_name(self):
23
+ return self.uiWorkboxEditorDDL.currentText()
24
+
25
+ def set_editor_name(self, name):
26
+ index = self.uiWorkboxEditorDDL.findText(name)
27
+ if index == -1:
28
+ self.uiWorkboxEditorDDL.addItem(name)
29
+ index = self.uiWorkboxEditorDDL.findText(name)
30
+ self.uiWorkboxEditorDDL.setCurrentIndex(index)
31
+
32
+ @Slot()
33
+ def refresh(self):
34
+ warning = "Choose an editor to enable Workboxs."
35
+ editor_name = self.editor_name()
36
+ if editor_name:
37
+ _, editor = plugins.editor(editor_name)
38
+ warning = editor._warning_text
39
+ self.uiWarningIconLBL.setVisible(bool(warning))
40
+ self.uiWarningTextLBL.setVisible(bool(warning))
41
+ self.uiWarningTextLBL.setText(warning)
42
+
43
+ def refresh_editors(self):
44
+ current = self.editor_name()
45
+ self.uiWorkboxEditorDDL.blockSignals(True)
46
+ self.uiWorkboxEditorDDL.clear()
47
+ for name, _ in sorted(set(plugins.editors())):
48
+ self.uiWorkboxEditorDDL.addItem(name)
49
+
50
+ self.uiWorkboxEditorDDL.setCurrentIndex(
51
+ self.uiWorkboxEditorDDL.findText(current)
52
+ )
53
+ self.uiWorkboxEditorDDL.blockSignals(False)
54
+
55
+ def showEvent(self, event): # noqa: N802
56
+ super(EditorChooser, self).showEvent(event)
57
+ self.refresh_editors()
@@ -0,0 +1,68 @@
1
+ from __future__ import absolute_import
2
+
3
+ import os
4
+ import traceback
5
+
6
+ from Qt.QtCore import Qt
7
+ from Qt.QtGui import QColor, QPixmap
8
+
9
+ from .. import __file__ as pfile
10
+ from . import Dialog, QtPropertyInit, loadUi
11
+
12
+
13
+ class ErrorDialog(Dialog):
14
+ # These Qt Properties can be customized using style sheets.
15
+ errorMessageColor = QtPropertyInit('_errorMessageColor', QColor(Qt.GlobalColor.red))
16
+
17
+ def __init__(self, parent):
18
+ super(ErrorDialog, self).__init__(parent)
19
+
20
+ loadUi(__file__, self)
21
+
22
+ self.parent_ = parent
23
+ self.setWindowTitle('Error Occurred')
24
+ self.uiErrorLBL.setTextFormat(Qt.RichText)
25
+ self.uiIconLBL.setPixmap(
26
+ QPixmap(
27
+ os.path.join(
28
+ os.path.dirname(pfile),
29
+ 'resource',
30
+ 'img',
31
+ 'warning-big.png',
32
+ )
33
+ ).scaledToHeight(64, Qt.SmoothTransformation)
34
+ )
35
+
36
+ self.uiLoggerBTN.clicked.connect(self.show_logger)
37
+ self.uiIgnoreBTN.clicked.connect(self.close)
38
+
39
+ def setText(self, exc_info):
40
+ self.traceback_msg = "".join(traceback.format_exception(*exc_info))
41
+ msg = (
42
+ 'The following error has occurred:<br>'
43
+ '<br><font color=%(color)s>%(text)s</font>'
44
+ )
45
+ self.uiErrorLBL.setText(
46
+ msg
47
+ % {
48
+ 'text': self.traceback_msg.split('\n')[-2],
49
+ 'color': self.errorMessageColor.name(),
50
+ }
51
+ )
52
+
53
+ def show_logger(self):
54
+ """Create/show the main PrEditor instance with the full traceback."""
55
+ from .. import launch
56
+
57
+ launch()
58
+ self.close()
59
+
60
+ @classmethod
61
+ def show_prompt(cls, *exc_info):
62
+ """Return False to this dialog should not be shown on an exception.
63
+
64
+ This is useful for applications like Nuke which uses exceptions to signal
65
+ traditionally non-exception worthy events, such as when a user cancels
66
+ an Open File dialog window.
67
+ """
68
+ return True
@@ -0,0 +1,125 @@
1
+ from __future__ import absolute_import, print_function
2
+
3
+ from Qt.QtCore import Qt, Slot
4
+ from Qt.QtGui import QIcon, QKeySequence
5
+ from Qt.QtWidgets import QApplication, QShortcut, QWidget
6
+
7
+ from .. import resourcePath
8
+ from ..utils.text_search import RegexTextSearch, SimpleTextSearch
9
+ from . import loadUi
10
+
11
+
12
+ class FindFiles(QWidget):
13
+ def __init__(self, parent=None, managers=None, console=None):
14
+ super(FindFiles, self).__init__(parent=parent)
15
+ if managers is None:
16
+ managers = []
17
+ self.managers = managers
18
+ self.console = console
19
+ self.finder = None
20
+ self.match_files_count = 0
21
+
22
+ loadUi(__file__, self)
23
+
24
+ # Set the icons
25
+ self.uiCaseSensitiveBTN.setIcon(
26
+ QIcon(resourcePath("img/format-letter-case.svg"))
27
+ )
28
+ self.uiCloseBTN.setIcon(QIcon(resourcePath('img/close-thick.png')))
29
+ self.uiRegexBTN.setIcon(QIcon(resourcePath("img/regex.svg")))
30
+
31
+ # Create shortcuts
32
+ self.uiCloseSCT = QShortcut(
33
+ QKeySequence(Qt.Key_Escape), self, context=Qt.WidgetWithChildrenShortcut
34
+ )
35
+
36
+ self.uiCloseSCT.activated.connect(self.hide)
37
+
38
+ self.uiCaseSensitiveSCT = QShortcut(
39
+ QKeySequence(Qt.AltModifier | Qt.Key_C),
40
+ self,
41
+ context=Qt.WidgetWithChildrenShortcut,
42
+ )
43
+ self.uiCaseSensitiveSCT.activated.connect(self.uiCaseSensitiveBTN.toggle)
44
+
45
+ self.uiRegexSCT = QShortcut(
46
+ QKeySequence(Qt.AltModifier | Qt.Key_R),
47
+ self,
48
+ context=Qt.WidgetWithChildrenShortcut,
49
+ )
50
+ self.uiRegexSCT.activated.connect(self.uiRegexBTN.toggle)
51
+
52
+ def activate(self):
53
+ """Called to make this widget ready for the user to interact with."""
54
+ self.show()
55
+ self.uiFindTXT.setFocus()
56
+
57
+ @Slot()
58
+ def find(self):
59
+ find_text = self.uiFindTXT.text()
60
+ context = self.uiContextSPN.value()
61
+ # Create an instance of the TextSearch to use for this search
62
+ if self.uiRegexBTN.isChecked():
63
+ TextSearch = RegexTextSearch
64
+ else:
65
+ TextSearch = SimpleTextSearch
66
+ self.finder = TextSearch(
67
+ find_text, self.uiCaseSensitiveBTN.isChecked(), context=context
68
+ )
69
+ self.finder.callback_matching = self.insert_found_text
70
+ self.finder.callback_non_matching = self.insert_text
71
+
72
+ self.insert_text(self.finder.title())
73
+
74
+ self.match_files_count = 0
75
+ for manager in self.managers:
76
+ for (
77
+ editor,
78
+ group_name,
79
+ tab_name,
80
+ group_index,
81
+ tab_index,
82
+ ) in manager.all_widgets():
83
+ path = "/".join((group_name, tab_name))
84
+ workbox_id = '{},{}'.format(group_index, tab_index)
85
+ self.find_in_editor(editor, path, workbox_id)
86
+
87
+ self.insert_text(
88
+ '\n{} matches in {} workboxes\n'.format(
89
+ self.finder.match_count, self.match_files_count
90
+ )
91
+ )
92
+
93
+ def find_in_editor(self, editor, path, workbox_id):
94
+ # Ensure the editor text is loaded and get its raw text
95
+ editor.__show__()
96
+ text = editor.__text__()
97
+
98
+ # Use the finder to check for matches
99
+ found = self.finder.search_text(text, path, workbox_id)
100
+ if found:
101
+ self.match_files_count += 1
102
+
103
+ def insert_found_text(self, text, workbox_id, line_num, tool_tip):
104
+ href = ', {}, {}'.format(workbox_id, line_num)
105
+ cursor = self.console.textCursor()
106
+ # Insert hyperlink
107
+ fmt = cursor.charFormat()
108
+ fmt.setAnchor(True)
109
+ fmt.setAnchorHref(href)
110
+ fmt.setFontUnderline(True)
111
+ fmt.setToolTip(tool_tip)
112
+ cursor.insertText(text, fmt)
113
+ # Show the updated text output
114
+ QApplication.instance().processEvents()
115
+
116
+ def insert_text(self, text):
117
+ cursor = self.console.textCursor()
118
+ fmt = cursor.charFormat()
119
+ fmt.setAnchor(False)
120
+ fmt.setAnchorHref('')
121
+ fmt.setFontUnderline(False)
122
+ fmt.setToolTip('')
123
+ cursor.insertText(text, fmt)
124
+ # Show the updated text output
125
+ QApplication.instance().processEvents()
File without changes