QT-PyQt-PySide-Custom-Widgets-Pro 1.1.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.
Files changed (45) hide show
  1. custom_widgets_pro/__init__.py +73 -0
  2. custom_widgets_pro/_catalog.py +25 -0
  3. custom_widgets_pro/_cli.py +132 -0
  4. custom_widgets_pro/_license.py +419 -0
  5. custom_widgets_pro/_version.py +14 -0
  6. custom_widgets_pro/datatable/__init__.py +17 -0
  7. custom_widgets_pro/datatable/datatable_pro.py +319 -0
  8. custom_widgets_pro/datatable/export.py +143 -0
  9. custom_widgets_pro/datatable/frozen_view.py +112 -0
  10. custom_widgets_pro/datatable/grouping.py +309 -0
  11. custom_widgets_pro/datatable/pivot.py +100 -0
  12. custom_widgets_pro/datatable/provider.py +98 -0
  13. custom_widgets_pro/datatable/virtual_model.py +451 -0
  14. custom_widgets_pro/widgets/__init__.py +7 -0
  15. custom_widgets_pro/widgets/charts/QCustomBeeswarm.py +317 -0
  16. custom_widgets_pro/widgets/charts/QCustomBubbleChart.py +655 -0
  17. custom_widgets_pro/widgets/charts/QCustomCandlestickChart.py +522 -0
  18. custom_widgets_pro/widgets/charts/QCustomDivergingBarChart.py +430 -0
  19. custom_widgets_pro/widgets/charts/QCustomDotMatrix.py +266 -0
  20. custom_widgets_pro/widgets/charts/QCustomFunnelChart.py +474 -0
  21. custom_widgets_pro/widgets/charts/QCustomGanttChart.py +376 -0
  22. custom_widgets_pro/widgets/charts/QCustomHeatmap.py +555 -0
  23. custom_widgets_pro/widgets/charts/QCustomRadarChart.py +600 -0
  24. custom_widgets_pro/widgets/charts/QCustomRadialBars.py +441 -0
  25. custom_widgets_pro/widgets/charts/QCustomRadialLines.py +554 -0
  26. custom_widgets_pro/widgets/charts/QCustomRangeBarChart.py +504 -0
  27. custom_widgets_pro/widgets/charts/QCustomSankey.py +539 -0
  28. custom_widgets_pro/widgets/charts/QCustomScatterChart.py +623 -0
  29. custom_widgets_pro/widgets/charts/__init__.py +1 -0
  30. custom_widgets_pro/widgets/data/QCustomCodeEditor.py +391 -0
  31. custom_widgets_pro/widgets/data/QCustomNodeGraph.py +1030 -0
  32. custom_widgets_pro/widgets/data/QCustomRichTextEditor.py +189 -0
  33. custom_widgets_pro/widgets/data/QCustomTableToolbar.py +566 -0
  34. custom_widgets_pro/widgets/data/__init__.py +1 -0
  35. custom_widgets_pro/widgets/media/QCustomImageViewer.py +300 -0
  36. custom_widgets_pro/widgets/media/QCustomMediaGrid.py +197 -0
  37. custom_widgets_pro/widgets/media/QCustomMediaTimeline.py +607 -0
  38. custom_widgets_pro/widgets/media/QCustomVideoPlayer.py +326 -0
  39. custom_widgets_pro/widgets/media/__init__.py +1 -0
  40. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/METADATA +112 -0
  41. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/RECORD +45 -0
  42. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/WHEEL +5 -0
  43. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/entry_points.txt +2 -0
  44. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/licenses/LICENSE +91 -0
  45. qt_pyqt_pyside_custom_widgets_pro-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,391 @@
1
+ # qtpy Custom Widgets #
2
+ # GPL 3.0 - Kadir Aksoy #
3
+ # https://github.com/kadir014/pyqt5-custom-widgets #
4
+ # DISCLAIMER: This class uses the JSON files in the #
5
+ # syntax and themes folders which has #
6
+ # pre-defined syntax rules and themes. #
7
+ # Don't forget to include them in your #
8
+ # package's folder if you're installing #
9
+ # this project manually.
10
+
11
+ import json
12
+ import pathlib
13
+
14
+ from qtpy.QtCore import QRegularExpression, Qt, Property
15
+ from qtpy.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QLabel, QPlainTextEdit
16
+ from qtpy.QtGui import QColor, QTextCharFormat, QFont, QSyntaxHighlighter, QPainter, QPen, QBrush
17
+ from Custom_Widgets._resources import packageDir
18
+
19
+ class QCustomCodeEditor(QWidget):
20
+ # Qt Designer contract. WIDGET_MODULE is the FLAT public path --
21
+ # Custom_Widgets.QCustomCodeEditor is what .ui files carry in <header>, not the
22
+ # subpackage this file now lives in.
23
+ __catalog__ = {
24
+ "name": "QCustomCodeEditor",
25
+ "props": {
26
+ "lang": {},
27
+ "theme": {},
28
+ },
29
+ }
30
+ WIDGET_MODULE = "Custom_Widgets.QCustomCodeEditor"
31
+ WIDGET_TOOLTIP = "A syntax-highlighted code editor"
32
+ WIDGET_DOM_XML = """
33
+ <ui language='c++'>
34
+ <widget class='QCustomCodeEditor' name='qCustomCodeEditor'>
35
+ <property name='geometry'><rect><x>0</x><y>0</y><width>420</width><height>300</height></rect></property>
36
+ </widget>
37
+ </ui>
38
+ """
39
+
40
+ LANG_DISPLAY = {
41
+ "plain" : "Plain text",
42
+ "python" : "Python",
43
+ "py" : "Python",
44
+ "cpp" : "C++",
45
+ "c++" : "C++",
46
+ "js" : "JavaScript",
47
+ "jsx" : "JavaScript",
48
+ "javascript" : "JavaScript",
49
+ "ts" : "TypeScript",
50
+ "tsx" : "TypeScript",
51
+ "typescript" : "TypeScript"
52
+ }
53
+
54
+ # Rich editors for the Designer "Custom Properties" dock (see
55
+ # DesignerTools.CustomPropertiesDock).
56
+ DESIGNER_CUSTOM_PROPS = [
57
+ {"name": "lang", "kind": "str", "group": "General"},
58
+ {"name": "theme", "kind": "str", "group": "General"},
59
+ ]
60
+
61
+ # -- designer-facing properties (editable in the Custom Properties dock) --
62
+ @Property(str)
63
+ def lang(self):
64
+ return getattr(self, "_lang", "python")
65
+
66
+ @lang.setter
67
+ def lang(self, value):
68
+ self._lang = str(value)
69
+ if value in QCustomCodeEditor.LANG_DISPLAY:
70
+ self.setLang(value)
71
+
72
+ @Property(str)
73
+ def theme(self):
74
+ return getattr(self, "_theme", "default")
75
+
76
+ @theme.setter
77
+ def theme(self, value):
78
+ self._theme = str(value)
79
+ try:
80
+ self.setTheme(value)
81
+ except Exception:
82
+ pass
83
+
84
+ def __init__(self, parent=None):
85
+ super().__init__(parent)
86
+
87
+ self.layout = QHBoxLayout()
88
+ self.layout.setContentsMargins(0, 0, 0, 0)
89
+ self.setLayout(self.layout)
90
+
91
+ self.layout.addSpacing(37)
92
+
93
+ self.editorlyt = QVBoxLayout()
94
+ self.editorlyt.setSpacing(0)
95
+ self.editorlyt.setContentsMargins(0, 0, 0, 0)
96
+ self.layout.addLayout(self.editorlyt)
97
+
98
+ self.editor = QPlainTextEdit()
99
+ self.editor.setLineWrapMode(QPlainTextEdit.NoWrap)
100
+ self.editorlyt.addWidget(self.editor)
101
+
102
+ self.setStyleSheet("QPlainTextEdit {font-family:Consolas; font-size:14px; color:#222222;}")
103
+
104
+ self.highlighter = QCustomSyntaxHighlighter(self.editor.document())
105
+
106
+ self.sliderVal = 0
107
+ self.lastdigit = 2
108
+ vs = self.editor.verticalScrollBar()
109
+
110
+ @vs.rangeChanged.connect
111
+ def slot(v):
112
+ self.sliderVal = self.editor.verticalScrollBar().value()
113
+
114
+ if len(str(vs.maximum()+15)) > self.lastdigit:
115
+ self.lastdigit = len(str(vs.maximum()+15))
116
+ self.layout.insertSpacing(0, 10)
117
+
118
+ self.update()
119
+
120
+ @vs.valueChanged.connect
121
+ def slot(v):
122
+ self.sliderVal = v
123
+
124
+ self.update()
125
+
126
+ self.statusbar = QWidget()
127
+ self.statusbar.setFixedHeight(26)
128
+ self.statusbarlyt = QHBoxLayout()
129
+ self.statusbarlyt.setContentsMargins(10, 0, 30, 0)
130
+ self.statusbar.setLayout(self.statusbarlyt)
131
+ self.editorlyt.addWidget(self.statusbar)
132
+
133
+ self.cursor_lbl = QLabel("0:0")
134
+ self.cursor_lbl.setStyleSheet("font-size:16px;")
135
+ self.statusbarlyt.addWidget(self.cursor_lbl, alignment=Qt.AlignLeft|Qt.AlignVCenter)
136
+
137
+ self.lang_lbl = QLabel("Plain text")
138
+ self.lang_lbl.setStyleSheet("font-size:16px;")
139
+ self.statusbarlyt.addWidget(self.lang_lbl, alignment=Qt.AlignRight|Qt.AlignVCenter)
140
+
141
+ @self.editor.cursorPositionChanged.connect
142
+ def slot():
143
+ self.cursor_lbl.setText(f"{self.editor.textCursor().blockNumber()}:{self.editor.textCursor().positionInBlock()}")
144
+
145
+ def __repr__(self):
146
+ return f"<Custom.CodeTextEdit({QCustomCodeEditor.LANG_DISPLAY(self.lang)})>"
147
+
148
+ def setTheme(self, theme):
149
+ self.highlighter.setTheme(theme)
150
+ self.highlighter.setRules()
151
+
152
+ c = self.highlighter.theme["background"]
153
+ rgb = f"rgb({c.red()}, {c.green()}, {c.blue()})"
154
+
155
+ cc = self.highlighter.theme["identifier"]
156
+ crgb = f"rgb({cc.red()}, {cc.green()}, {cc.blue()})"
157
+
158
+ self.editor.setStyleSheet(f"QPlainTextEdit {{background-color: {rgb}; color: {crgb};}}")
159
+
160
+ self.cursor_lbl.setStyleSheet(f"color: {crgb}; font-size:16px;")
161
+ self.lang_lbl.setStyleSheet(f"color: {crgb}; font-size:16px;")
162
+
163
+ self.highlighter.rehighlight()
164
+ self.update()
165
+
166
+ def setLang(self, lang):
167
+ self.lang_lbl.setText(QCustomCodeEditor.LANG_DISPLAY[lang])
168
+
169
+ self.highlighter.setLang(lang)
170
+ self.highlighter.setRules()
171
+ self.highlighter.rehighlight()
172
+ self.update()
173
+
174
+ def loadFile(self, filepath, encoding="utf-8"):
175
+
176
+ if filepath.endswith(".py"): lang = "python"
177
+ elif filepath.endswith(".cpp"): lang = "cpp"
178
+ else: lang = "plain"
179
+
180
+ with open(filepath, "r", encoding=encoding) as f:
181
+ self.editor.setPlainText(f.read())
182
+
183
+ self.setLang(lang)
184
+
185
+ def paintEvent(self, event):
186
+ pt = QPainter()
187
+ pt.begin(self)
188
+ pt.setRenderHint(QPainter.Antialiasing)
189
+
190
+ pt.setBrush(QBrush(self.highlighter.theme["lines-background"]))
191
+
192
+ pt.drawRect(0, 0, self.width(), self.height())
193
+
194
+ font = self.editor.font()
195
+ pt.setFont(font)
196
+
197
+ gap = font.pixelSize() + 3
198
+
199
+ for i in range((self.height()//gap)):
200
+ font = pt.font()
201
+ pt.setFont(font)
202
+ pt.setPen(QPen(self.highlighter.theme["lines"]))
203
+
204
+ pt.drawText(13, i*gap, str(i+self.sliderVal))
205
+
206
+ pt.setBrush(QBrush(self.highlighter.theme["background"]))
207
+ pt.setPen(QPen(QColor(0, 0, 0, 0)))
208
+ pt.drawRect(3, self.height()-self.statusbar.height(), 40, self.statusbar.height()-6)
209
+
210
+ pt.end()
211
+
212
+ class QCustomSyntaxHighlighter(QSyntaxHighlighter):
213
+
214
+ AVAILABLE_LANGS = {
215
+ "plain" : "plain",
216
+ "py" : "python",
217
+ "python" : "python",
218
+ "cpp" : "cpp",
219
+ "c++" : "cpp",
220
+ "js" : "javascript",
221
+ "jsx" : "javascript",
222
+ "javascript" : "javascript",
223
+ "ts" : "javascript",
224
+ "tsx" : "javascript",
225
+ "typescript" : "javascript"
226
+ }
227
+
228
+ def __init__(self, document, lang="python", theme="default"):
229
+ super().__init__(document)
230
+
231
+ self.setLang(lang)
232
+ self.setTheme(theme)
233
+ self.setRules()
234
+
235
+ def formatThemeKey(self, color, style="", returnColor=False):
236
+ _color = QColor()
237
+ if type(color) is not str:
238
+ _color.setRgb(color[0], color[1], color[2])
239
+
240
+ elif color.startswith("#"):
241
+ hexc = color.lstrip('#')
242
+ hexc = tuple(int(hexc[i:i+2], 16) for i in (0, 2, 4))
243
+ _color.setRgb(*hexc)
244
+
245
+ else:
246
+ _color = QColor(color)
247
+
248
+ if returnColor: return _color
249
+
250
+ _format = QTextCharFormat()
251
+ _format.setForeground(_color)
252
+ if "bold" in style: _format.setFontWeight(QFont.Bold)
253
+ if "italic" in style: _format.setFontItalic(True)
254
+
255
+ return _format
256
+
257
+ def setTheme(self, theme):
258
+ if isinstance(theme, dict):
259
+ pass
260
+
261
+ else:
262
+ path = pathlib.Path(packageDir()) / "CodeEditorThemes" / f"{theme}.json"
263
+ with open(path, "r", encoding="utf-8") as f:
264
+ s = json.loads(f.read())
265
+
266
+ self.theme = dict()
267
+
268
+ for k in s:
269
+ p = s[k].split("-")
270
+ if len(p) == 2: self.theme[k] = self.formatThemeKey(s[k], p[1])
271
+ else: self.theme[k] = self.formatThemeKey(s[k])
272
+
273
+ self.theme["background"] = self.formatThemeKey(s["background"], returnColor=True)
274
+ self.theme["lines-background"] = self.formatThemeKey(s["lines-background"], returnColor=True)
275
+ self.theme["lines"] = self.formatThemeKey(s["lines"], returnColor=True)
276
+ self.theme["identifier"] = self.formatThemeKey(s["identifier"], returnColor=True)
277
+
278
+ def setLang(self, lang):
279
+ lang = lang.lower()
280
+
281
+ if lang in QCustomSyntaxHighlighter.AVAILABLE_LANGS:
282
+ self.lang = QCustomSyntaxHighlighter.AVAILABLE_LANGS[lang]
283
+
284
+ if self.lang == "plain": return
285
+
286
+ path = pathlib.Path(packageDir()) / "CodeEditorSyntax" / f"{self.lang}.json"
287
+ with open(path, "r", encoding="utf-8") as f:
288
+ rr = json.loads(f.read())
289
+
290
+ self.keywords = rr["keywords"]
291
+ self.this = rr["this"]
292
+ self.comment = rr["comment"]
293
+
294
+ self.operators = operators = (
295
+ "=",
296
+ "==", "!=", "<", "<=", ">", ">=",
297
+ r"\+", "-", r"\*", "/", "//", r"\%", r"\*\*",
298
+ r"\+=", "-=", r"\*=", "/=", r"\%=",
299
+ r"\^", r"\|", r"\&", r"\~", ">>", "<<",
300
+ r"\+\+", "--", r"\&\&", r"\|\|"
301
+ )
302
+
303
+ self.braces = (
304
+ r"\{", r"\}", r"\(", r"\)", r"\[", r"\]",
305
+ )
306
+
307
+ else:
308
+ raise ValueError(f"Language '{lang}' is not supported.")
309
+
310
+ def setRules(self):
311
+ self.tri_single = (QRegularExpression("'''"), 1, self.theme["string"])
312
+ self.tri_double = (QRegularExpression('"""'), 2, self.theme["string"])
313
+
314
+ if self.lang == "plain":
315
+ self.rules = list()
316
+ return
317
+
318
+ rules = list()
319
+
320
+ rules += [(r'\b%s\b' % w, 0, self.theme["keyword"]) for w in self.keywords]
321
+
322
+ rules += [(r'%s' % o, 0, self.theme["operator"]) for o in self.operators]
323
+
324
+ rules += [(r'%s' % b, 0, self.theme["brace"]) for b in self.braces]
325
+
326
+ if self.lang in ("cpp", "c"):
327
+ rules.append( (r'\#[^\n]*', 0, self.theme["preprocessor"]) )
328
+
329
+ rules += [
330
+ (r"\b[A-Za-z0-9_]+(?=\()", 0, self.theme["function"]),
331
+
332
+ (r'\b%s\b' % self.this, 0, self.theme["this"]),
333
+
334
+ (r'"[^"\\]*(\\.[^"\\]*)*"', 0, self.theme["string"]),
335
+
336
+ (r"'[^'\\]*(\\.[^'\\]*)*'", 0, self.theme["string"]),
337
+
338
+ (r'%s[^\n]*' % self.comment, 0, self.theme["comment"]),
339
+
340
+ (r'\b[+-]?[0-9]+[lL]?\b', 0, self.theme["numeric"]),
341
+ (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, self.theme["numeric"]),
342
+ (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, self.theme["numeric"])
343
+ ]
344
+
345
+ self.rules = [(QRegularExpression(pat), index, fmt) for (pat, index, fmt) in rules]
346
+
347
+ def highlightBlock(self, text):
348
+ for expression, nth, format in self.rules:
349
+ match = expression.match(text)
350
+ index = match.capturedStart(nth)
351
+
352
+ while index >= 0:
353
+ length = match.capturedLength(nth)
354
+ self.setFormat(index, length, format)
355
+ index = expression.match(text, index + length).capturedStart(nth)
356
+
357
+ self.setCurrentBlockState(0)
358
+
359
+ in_multiline = self.match_multiline(text, *self.tri_single)
360
+ if not in_multiline:
361
+ in_multiline = self.match_multiline(text, *self.tri_double)
362
+
363
+
364
+
365
+ def match_multiline(self, text, delimiter, in_state, style):
366
+ if self.previousBlockState() == in_state:
367
+ start = 0
368
+ add = 0
369
+
370
+ else:
371
+ match = delimiter.match(text)
372
+ start = match.capturedStart()
373
+ add = match.capturedLength()
374
+
375
+ while start >= 0:
376
+ end = delimiter.indexIn(text, start + add)
377
+
378
+ if end >= add:
379
+ length = end - start + add + delimiter.matchedLength()
380
+ self.setCurrentBlockState(0)
381
+
382
+ else:
383
+ self.setCurrentBlockState(in_state)
384
+ length = len(text) - start + add
385
+
386
+ self.setFormat(start, length, style)
387
+ start = delimiter.indexIn(text, start + length)
388
+
389
+
390
+ if self.currentBlockState() == in_state: return True
391
+ else: return False