8 """ |
8 """ |
9 |
9 |
10 import contextlib |
10 import contextlib |
11 import os |
11 import os |
12 |
12 |
13 from PyQt5.QtCore import QObject, QTranslator |
13 from PyQt6.QtCore import QObject, QTranslator |
14 from PyQt5.QtGui import QColor |
14 from PyQt6.QtGui import QColor |
15 from PyQt5.QtWidgets import QColorDialog, QMenu, QDialog |
15 from PyQt6.QtWidgets import QColorDialog, QMenu, QDialog |
16 |
16 |
17 from E5Gui.E5Application import e5App |
17 from EricWidgets.EricApplication import ericApp |
18 from E5Gui import E5MessageBox |
18 from EricWidgets import EricMessageBox |
19 |
19 |
20 # Start-Of-Header |
20 # Start-Of-Header |
21 name = "Color String Plug-in" |
21 name = "Color String Plug-in" |
22 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
22 author = "Detlev Offenbach <detlev@die-offenbachs.de>" |
23 autoactivate = True |
23 autoactivate = True |
24 deactivateable = True |
24 deactivateable = True |
25 version = "3.2.0" |
25 version = "1.0.0" |
26 className = "ColorStringPlugin" |
26 className = "ColorStringPlugin" |
27 packageName = "ColorString" |
27 packageName = "ColorString" |
28 shortDescription = "Insert color as string" |
28 shortDescription = "Insert color as string" |
29 longDescription = ( |
29 longDescription = ( |
30 """This plug-in implements a tool to select a color via a""" |
30 """This plug-in implements a tool to select a color via a""" |
77 act = menu.addSeparator() |
79 act = menu.addSeparator() |
78 self.__mainActions.append(act) |
80 self.__mainActions.append(act) |
79 act = menu.addMenu(self.__menu) |
81 act = menu.addMenu(self.__menu) |
80 self.__mainActions.append(act) |
82 self.__mainActions.append(act) |
81 |
83 |
82 e5App().getObject("ViewManager").editorOpenedEd.connect( |
84 ericApp().getObject("ViewManager").editorOpenedEd.connect( |
83 self.__editorOpened) |
85 self.__editorOpened) |
84 e5App().getObject("ViewManager").editorClosedEd.connect( |
86 ericApp().getObject("ViewManager").editorClosedEd.connect( |
85 self.__editorClosed) |
87 self.__editorClosed) |
86 |
88 |
87 for editor in e5App().getObject("ViewManager").getOpenEditors(): |
89 for editor in ericApp().getObject("ViewManager").getOpenEditors(): |
88 self.__editorOpened(editor) |
90 self.__editorOpened(editor) |
89 |
91 |
90 return None, True |
92 return None, True |
91 |
93 |
92 def deactivate(self): |
94 def deactivate(self): |
99 if menu is not None: |
101 if menu is not None: |
100 for act in self.__mainActions: |
102 for act in self.__mainActions: |
101 menu.removeAction(act) |
103 menu.removeAction(act) |
102 self.__mainActions = [] |
104 self.__mainActions = [] |
103 |
105 |
104 e5App().getObject("ViewManager").editorOpenedEd.disconnect( |
106 ericApp().getObject("ViewManager").editorOpenedEd.disconnect( |
105 self.__editorOpened) |
107 self.__editorOpened) |
106 e5App().getObject("ViewManager").editorClosedEd.disconnect( |
108 ericApp().getObject("ViewManager").editorClosedEd.disconnect( |
107 self.__editorClosed) |
109 self.__editorClosed) |
108 |
110 |
109 for editor, acts in self.__editors.items(): |
111 for editor, acts in self.__editors.items(): |
110 editor.showMenu.disconnect(self.__editorShowMenu) |
112 editor.showMenu.disconnect(self.__editorShowMenu) |
111 menu = editor.getMenu("Tools") |
113 menu = editor.getMenu("Tools") |
126 translation = "colorstring_{0}".format(loc) |
128 translation = "colorstring_{0}".format(loc) |
127 translator = QTranslator(None) |
129 translator = QTranslator(None) |
128 loaded = translator.load(translation, locale_dir) |
130 loaded = translator.load(translation, locale_dir) |
129 if loaded: |
131 if loaded: |
130 self.__translator = translator |
132 self.__translator = translator |
131 e5App().installTranslator(self.__translator) |
133 ericApp().installTranslator(self.__translator) |
132 else: |
134 else: |
133 print("Warning: translation file '{0}' could not be" |
135 print("Warning: translation file '{0}' could not be" |
134 " loaded.".format(translation)) |
136 " loaded.".format(translation)) |
135 print("Using default.") |
137 print("Using default.") |
136 |
138 |
146 |
148 |
147 def __populateMenu(self, name, menu): |
149 def __populateMenu(self, name, menu): |
148 """ |
150 """ |
149 Private slot to populate the tools menu with our entry. |
151 Private slot to populate the tools menu with our entry. |
150 |
152 |
151 @param name name of the menu (string) |
153 @param name name of the menu |
152 @param menu reference to the menu to be populated (QMenu) |
154 @type str |
|
155 @param menu reference to the menu to be populated |
|
156 @type QMenu |
153 """ |
157 """ |
154 if name not in ["Tools", "PluginTools"]: |
158 if name not in ["Tools", "PluginTools"]: |
155 return |
159 return |
156 |
160 |
157 editor = e5App().getObject("ViewManager").activeWindow() |
161 editor = ericApp().getObject("ViewManager").activeWindow() |
158 |
162 |
159 if name == "Tools": |
163 if name == "Tools": |
160 if not menu.isEmpty(): |
164 if not menu.isEmpty(): |
161 menu.addSeparator() |
165 menu.addSeparator() |
162 act = menu.addMenu(self.__menu) |
166 act = menu.addMenu(self.__menu) |
166 |
170 |
167 def __editorOpened(self, editor): |
171 def __editorOpened(self, editor): |
168 """ |
172 """ |
169 Private slot called, when a new editor was opened. |
173 Private slot called, when a new editor was opened. |
170 |
174 |
171 @param editor reference to the new editor (QScintilla.Editor) |
175 @param editor reference to the new editor |
|
176 @type Editor |
172 """ |
177 """ |
173 menu = editor.getMenu("Tools") |
178 menu = editor.getMenu("Tools") |
174 if menu is not None: |
179 if menu is not None: |
175 self.__editors[editor] = [] |
180 self.__editors[editor] = [] |
176 if not menu.isEmpty(): |
181 if not menu.isEmpty(): |
183 |
188 |
184 def __editorClosed(self, editor): |
189 def __editorClosed(self, editor): |
185 """ |
190 """ |
186 Private slot called, when an editor was closed. |
191 Private slot called, when an editor was closed. |
187 |
192 |
188 @param editor reference to the editor (QScintilla.Editor) |
193 @param editor reference to the editor |
|
194 @type Editor |
189 """ |
195 """ |
190 with contextlib.suppress(KeyError): |
196 with contextlib.suppress(KeyError): |
191 del self.__editors[editor] |
197 del self.__editors[editor] |
192 if not self.__editors: |
198 if not self.__editors: |
193 self.__menu.setEnabled(False) |
199 self.__menu.setEnabled(False) |
195 def __editorShowMenu(self, menuName, menu, editor): |
201 def __editorShowMenu(self, menuName, menu, editor): |
196 """ |
202 """ |
197 Private slot called, when the the editor context menu or a submenu is |
203 Private slot called, when the the editor context menu or a submenu is |
198 about to be shown. |
204 about to be shown. |
199 |
205 |
200 @param menuName name of the menu to be shown (string) |
206 @param menuName name of the menu to be shown |
201 @param menu reference to the menu (QMenu) |
207 @type str |
|
208 @param menu reference to the menu |
|
209 @type QMenu |
202 @param editor reference to the editor |
210 @param editor reference to the editor |
|
211 @type Editor |
203 """ |
212 """ |
204 if ( |
213 if ( |
205 menuName == "Tools" and |
214 menuName == "Tools" and |
206 self.__menu.menuAction() not in menu.actions() |
215 self.__menu.menuAction() not in menu.actions() |
207 ): |
216 ): |
215 |
224 |
216 def __isHexString(self, text): |
225 def __isHexString(self, text): |
217 """ |
226 """ |
218 Private method to check, if a given text is a hex string. |
227 Private method to check, if a given text is a hex string. |
219 |
228 |
220 @param text text to check (string) |
229 @param text text to check |
221 @return flag indicating a hex string (boolean) |
230 @type str |
222 """ |
231 @return flag indicating a hex string |
223 return all(map(lambda c: c in "0123456789abcdefABCDEF", text)) |
232 @rtype bool |
|
233 """ |
|
234 return all(c in "0123456789abcdefABCDEF" for c in text) |
224 |
235 |
225 def __isValidColor(self, name): |
236 def __isValidColor(self, name): |
226 """ |
237 """ |
227 Private method to check for a valid color name. |
238 Private method to check for a valid color name. |
228 |
239 |
229 @param name color name to check (string) |
240 @param name color name to check |
230 @return flag indicating a valid color name (boolean) |
241 @type str |
231 """ |
242 @return flag indicating a valid color name |
232 try: |
243 @rtype bool |
233 if self.__isHexString(name) and len(name) in [3, 6, 9, 12]: |
244 """ |
234 return True |
245 if self.__isHexString(name) and len(name) in (3, 6, 8, 9, 12): |
235 return QColor.isValidColor(name) |
246 return True |
236 except AttributeError: |
247 return QColor.isValidColor(name) |
237 if name.startswith("#"): |
|
238 if len(name) not in [4, 7, 10, 13]: |
|
239 return False |
|
240 hexCheckStr = name[1:] |
|
241 return self.__isHexString(hexCheckStr) |
|
242 else: |
|
243 if self.__isHexString(name) and len(name) in [3, 6, 9, 12]: |
|
244 return True |
|
245 return name in QColor.colorNames() |
|
246 |
248 |
247 def __selectHexColor(self): |
249 def __selectHexColor(self): |
248 """ |
250 """ |
249 Private slot implementing the hex color string selection. |
251 Private slot implementing the hex color string selection. |
250 """ |
252 """ |
251 editor = e5App().getObject("ViewManager").activeWindow() |
253 editor = ericApp().getObject("ViewManager").activeWindow() |
252 if editor is None: |
254 if editor is None: |
253 return |
255 return |
254 |
256 |
255 if editor.hasSelectedText(): |
257 if editor.hasSelectedText(): |
256 currColor = editor.selectedText() |
258 currColor = editor.selectedText() |
257 if not self.__isValidColor(currColor): |
259 if not self.__isValidColor(currColor): |
258 E5MessageBox.critical( |
260 EricMessageBox.critical( |
259 self.__ui, |
261 self.__ui, |
260 self.tr("Color String"), |
262 self.tr("Color String"), |
261 self.tr( |
263 self.tr( |
262 """<p>The selected string <b>{0}</b> is not a""" |
264 """<p>The selected string <b>{0}</b> is not a""" |
263 """ valid color string. Aborting!</p>""") |
265 """ valid color string. Aborting!</p>""") |
276 withHash = True |
278 withHash = True |
277 currColor = "" |
279 currColor = "" |
278 initColor = QColor() |
280 initColor = QColor() |
279 |
281 |
280 color = QColorDialog.getColor( |
282 color = QColorDialog.getColor( |
281 initColor, self.__ui, self.tr("Color String")) |
283 initColor, self.__ui, self.tr("Color String"), |
|
284 QColorDialog.ColorDialogOption.ShowAlphaChannel) |
282 if color.isValid(): |
285 if color.isValid(): |
283 colorStr = color.name() |
286 if color.alpha() == 255: |
|
287 nameFormat = QColor.NameFormat.HexRgb |
|
288 else: |
|
289 nameFormat = QColor.NameFormat.HexArgb |
|
290 colorStr = color.name(nameFormat) |
284 if not withHash: |
291 if not withHash: |
285 colorStr = colorStr[1:] |
292 colorStr = colorStr[1:] |
286 editor.beginUndoAction() |
293 editor.beginUndoAction() |
287 if editor.hasSelectedText(): |
294 if editor.hasSelectedText(): |
288 editor.replaceSelectedText(colorStr) |
295 editor.replaceSelectedText(colorStr) |
294 |
301 |
295 def __selectColorName(self): |
302 def __selectColorName(self): |
296 """ |
303 """ |
297 Private slot implementing the named color string selection. |
304 Private slot implementing the named color string selection. |
298 """ |
305 """ |
299 editor = e5App().getObject("ViewManager").activeWindow() |
306 editor = ericApp().getObject("ViewManager").activeWindow() |
300 if editor is None: |
307 if editor is None: |
301 return |
308 return |
302 |
309 |
303 if editor.hasSelectedText(): |
310 if editor.hasSelectedText(): |
304 currColor = editor.selectedText() |
311 currColor = editor.selectedText() |
305 if currColor not in QColor.colorNames(): |
312 if currColor not in QColor.colorNames(): |
306 E5MessageBox.critical( |
313 EricMessageBox.critical( |
307 self.__ui, |
314 self.__ui, |
308 self.tr("Color String"), |
315 self.tr("Color String"), |
309 self.tr( |
316 self.tr( |
310 """<p>The selected string <b>{0}</b> is not a""" |
317 """<p>The selected string <b>{0}</b> is not a""" |
311 """ valid color name. Aborting!</p>""") |
318 """ valid color name. Aborting!</p>""") |
314 else: |
321 else: |
315 currColor = "" |
322 currColor = "" |
316 |
323 |
317 from ColorString.ColorSelectionDialog import ColorSelectionDialog |
324 from ColorString.ColorSelectionDialog import ColorSelectionDialog |
318 dlg = ColorSelectionDialog(currColor, self.__ui) |
325 dlg = ColorSelectionDialog(currColor, self.__ui) |
319 if dlg.exec() == QDialog.Accepted: |
326 if dlg.exec() == QDialog.DialogCode.Accepted: |
320 colorStr = dlg.getColor() |
327 colorStr = dlg.getColor() |
321 editor.beginUndoAction() |
328 editor.beginUndoAction() |
322 if editor.hasSelectedText(): |
329 if editor.hasSelectedText(): |
323 editor.replaceSelectedText(colorStr) |
330 editor.replaceSelectedText(colorStr) |
324 else: |
331 else: |
329 |
336 |
330 def __selectRgbaColor(self): |
337 def __selectRgbaColor(self): |
331 """ |
338 """ |
332 Private slot implementing the RGBA color string selection. |
339 Private slot implementing the RGBA color string selection. |
333 """ |
340 """ |
334 editor = e5App().getObject("ViewManager").activeWindow() |
341 editor = ericApp().getObject("ViewManager").activeWindow() |
335 if editor is None: |
342 if editor is None: |
336 return |
343 return |
337 |
344 |
338 if editor.hasSelectedText(): |
345 if editor.hasSelectedText(): |
339 currColor = editor.selectedText() |
346 currColor = editor.selectedText() |
340 valid, rgba = self.__isValidRgbaColor(currColor) |
347 valid, rgba = self.__isValidRgbaColor(currColor) |
341 if not valid: |
348 if not valid: |
342 E5MessageBox.critical( |
349 EricMessageBox.critical( |
343 self.__ui, |
350 self.__ui, |
344 self.tr("Color String"), |
351 self.tr("Color String"), |
345 self.tr( |
352 self.tr( |
346 """<p>The selected string <b>{0}</b> is not a""" |
353 """<p>The selected string <b>{0}</b> is not a""" |
347 """ valid color string. Aborting!</p>""") |
354 """ valid RGBA color string. Aborting!</p>""") |
348 .format(currColor)) |
355 .format(currColor)) |
349 return |
356 return |
350 initColor = QColor(*rgba) |
357 initColor = QColor(*rgba) |
351 else: |
358 else: |
352 initColor = QColor() |
359 initColor = QColor() |
353 |
360 |
354 color = QColorDialog.getColor( |
361 color = QColorDialog.getColor( |
355 initColor, self.__ui, self.tr("Color String"), |
362 initColor, self.__ui, self.tr("Color String"), |
356 QColorDialog.ShowAlphaChannel) |
363 QColorDialog.ColorDialogOption.ShowAlphaChannel) |
357 if color.isValid(): |
364 if color.isValid(): |
358 rgba = color.getRgb() |
365 rgba = color.getRgb() |
359 if rgba[-1] == 255: |
366 if rgba[-1] == 255: |
360 colorStr = "{0}, {1}, {2}".format(*rgba[:-1]) |
367 colorStr = "{0}, {1}, {2}".format(*rgba[:-1]) |
361 else: |
368 else: |
371 |
378 |
372 def __isValidRgbaColor(self, color): |
379 def __isValidRgbaColor(self, color): |
373 """ |
380 """ |
374 Private method to check for a valid RGBA color. |
381 Private method to check for a valid RGBA color. |
375 |
382 |
376 @param color color string to check (string) |
383 @param color color string to check |
|
384 @type str |
377 @return flag indicating a valid RGBA color (boolean) and a list with |
385 @return flag indicating a valid RGBA color (boolean) and a list with |
378 the RGBA components of the color (three or four integers) |
386 the RGBA components of the color (three or four integers) |
|
387 @rtype tuple of (bool, [int, int, int]) or (bool, [int, int, int, int]) |
379 """ |
388 """ |
380 rgba = [] |
389 rgba = [] |
381 |
390 |
382 parts = color.split(",") |
391 parts = color.split(",") |
383 if len(parts) not in [3, 4]: |
392 if len(parts) not in [3, 4]: |