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,317 @@
1
+ ########################################################################
2
+ ## SPINN DESIGN CODE
3
+ # YOUTUBE: (SPINN TV) https://www.youtube.com/@SpinnTV
4
+ # WEBSITE: customwidgets.org
5
+ ########################################################################
6
+
7
+ ########################################################################
8
+ ## QCustomBeeswarm - a column beeswarm / bubble-stack chart.
9
+ ##
10
+ ## Each COLUMN is a thin vertical guide line carrying a vertical stack of
11
+ ## rounded "pill" bubbles. Every bubble shows a VALUE (its number) and a
12
+ ## CATEGORY (a colour from `colorsCsv`); the pill's height scales with the
13
+ ## value between `minSize`..`maxSize`. This is the "check-box product" viz:
14
+ ## numbered capsules in columns, coloured by Resources / Valid / Invalid.
15
+ ## Painted (no QtCharts) so it stays crisp and needs no external axis.
16
+ ##
17
+ ## Give data in code with setData(columns) where `columns` is a list of
18
+ ## columns and each column is a list of (value, category) pairs, or in Qt
19
+ ## Designer with `dataCsv` (columns separated by ';', items by ',', each item
20
+ ## "value:category" — category is the 1-based colour index, default 1).
21
+ ## Colours / the number-text colour rules are qproperties so a theme flips
22
+ ## them on switch. The legend + total are best drawn as sibling labels in the
23
+ ## card (reuse), not by this widget.
24
+ ########################################################################
25
+ from qtpy.QtCore import Qt, Property, QRectF
26
+ from qtpy.QtGui import QColor, QPainter, QBrush, QPen, QFont, QFontMetrics
27
+ from qtpy.QtWidgets import QWidget, QSizePolicy
28
+
29
+
30
+ class QCustomBeeswarm(QWidget):
31
+
32
+ WIDGET_ICON = "components/icons/bar_chart.png"
33
+ WIDGET_TOOLTIP = "A column beeswarm / numbered bubble-stack chart"
34
+ WIDGET_MODULE = "Custom_Widgets.QCustomBeeswarm"
35
+ WIDGET_DOM_XML = """
36
+ <ui language='c++'>
37
+ <widget class='QCustomBeeswarm' name='customBeeswarm'>
38
+ <property name='geometry'><rect><x>0</x><y>0</y><width>560</width><height>300</height></rect></property>
39
+ </widget>
40
+ </ui>
41
+ """
42
+ __catalog__ = {
43
+ "name": "QCustomBeeswarm",
44
+ "props": {"dataCsv": {"type": "string", "default": ""},
45
+ "colorsCsv": {"type": "string", "default": "#ffffff,#8fe36b,#f6912b"},
46
+ "textColorsCsv": {"type": "string", "default": "#1c1c20,#1c1c20,#ffffff"},
47
+ "lineColor": {"type": "color", "default": "#3a3a40"},
48
+ "minSize": {"type": "int", "default": 34},
49
+ "maxSize": {"type": "int", "default": 58},
50
+ "bubbleWidth": {"type": "int", "default": 42},
51
+ "gap": {"type": "int", "default": 8},
52
+ "showValues": {"type": "bool", "default": True},
53
+ "jitter": {"type": "int", "default": 0}},
54
+ "signals": [],
55
+ # Intentionally no tokens_used: this widget resolves no token roles.
56
+ }
57
+
58
+ # Rich editors for the Designer "Custom Properties" dock (see
59
+ # DesignerTools.CustomPropertiesDock).
60
+ DESIGNER_CUSTOM_PROPS = [
61
+ {"name": "dataCsv", "kind": "str", "group": "General"},
62
+ {"name": "colorsCsv", "kind": "str", "group": "Colors"},
63
+ {"name": "textColorsCsv", "kind": "str", "group": "Colors"},
64
+ {"name": "lineColor", "kind": "color", "group": "Colors"},
65
+ {"name": "minSize", "kind": "int", "group": "General"},
66
+ {"name": "maxSize", "kind": "int", "group": "General"},
67
+ {"name": "bubbleWidth", "kind": "int", "group": "General"},
68
+ {"name": "gap", "kind": "int", "group": "General"},
69
+ {"name": "showValues", "kind": "bool", "group": "General"},
70
+ {"name": "jitter", "kind": "int", "group": "General"},
71
+ ]
72
+
73
+ # deterministic demo columns: list of (value, category) per column
74
+ _DEMO = [
75
+ [(52, 0), (81, 2)],
76
+ [(96, 1), (25, 0)],
77
+ [(48, 1), (51, 0)],
78
+ [(80, 1), (49, 2)],
79
+ [(34, 2), (67, 1)],
80
+ [(92, 1), (28, 0)],
81
+ [(58, 1), (20, 2)],
82
+ [(84, 2), (39, 1)],
83
+ [(36, 0), (72, 2)],
84
+ ]
85
+
86
+ def __init__(self, parent=None, columns=None):
87
+ super().__init__(parent)
88
+ self.setObjectName("QCustomBeeswarm")
89
+ self._cols = [[(float(v), int(c)) for v, c in col] for col in columns] if columns else []
90
+ self._colors = [QColor("#ffffff"), QColor("#8fe36b"), QColor("#f6912b")]
91
+ self._text_colors = [QColor("#1c1c20"), QColor("#1c1c20"), QColor("#ffffff")]
92
+ self._line = QColor("#3a3a40")
93
+ self._min_size = 34
94
+ self._max_size = 58
95
+ self._bubble_w = 42
96
+ self._gap = 8
97
+ self._show_values = True
98
+ self._jitter = 0
99
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
100
+ self.setMinimumSize(160, 120)
101
+ if not self._cols:
102
+ self._cols = [[(float(v), int(c)) for v, c in col] for col in self._DEMO]
103
+
104
+ # ------------------------------------------------------------------ #
105
+ ## Data API
106
+ # ------------------------------------------------------------------ #
107
+ def setData(self, columns):
108
+ """`columns` = list of columns; each column = list of (value, category)."""
109
+ self._cols = [[(float(v), int(c)) for v, c in col] for col in (columns or [])]
110
+ self.update()
111
+
112
+ def data(self):
113
+ return [list(col) for col in self._cols]
114
+
115
+ def setColors(self, fills, texts=None):
116
+ self._colors = [QColor(c) for c in fills if c]
117
+ if texts:
118
+ self._text_colors = [QColor(c) for c in texts if c]
119
+ self.update()
120
+
121
+ def _value_range(self):
122
+ vals = [v for col in self._cols for (v, _c) in col]
123
+ if not vals:
124
+ return 0.0, 1.0
125
+ return min(vals), max(vals)
126
+
127
+ def _fill(self, cat):
128
+ return QColor(self._colors[cat % len(self._colors)]) if self._colors else QColor("#ffffff")
129
+
130
+ def _text(self, cat):
131
+ if self._text_colors:
132
+ return QColor(self._text_colors[cat % len(self._text_colors)])
133
+ return QColor("#1c1c20")
134
+
135
+ # ------------------------------------------------------------------ #
136
+ ## Painting
137
+ # ------------------------------------------------------------------ #
138
+ def paintEvent(self, e):
139
+ if not self._cols:
140
+ return
141
+ p = QPainter(self)
142
+ p.setRenderHint(QPainter.Antialiasing, True)
143
+ w, h = self.width(), self.height()
144
+ n = len(self._cols)
145
+ if n == 0:
146
+ p.end(); return
147
+
148
+ lo, hi = self._value_range()
149
+ rng = (hi - lo) or 1.0
150
+ col_w = w / n
151
+ bw = float(min(self._bubble_w, col_w * 0.82))
152
+ gap = float(self._gap)
153
+
154
+ vfont = QFont(self.font())
155
+ vfont.setBold(True)
156
+
157
+ for i, col in enumerate(self._cols):
158
+ cx = col_w * (i + 0.5)
159
+ if self._jitter:
160
+ # deterministic left/right nudge off the guide line
161
+ cx += (self._jitter if i % 2 else -self._jitter)
162
+
163
+ # pill heights for this column
164
+ heights = []
165
+ for (v, _c) in col:
166
+ t = (v - lo) / rng
167
+ heights.append(self._min_size + t * (self._max_size - self._min_size))
168
+ stack_h = sum(heights) + gap * (len(col) - 1)
169
+ # centre the stack vertically with a little breathing room
170
+ top = (h - stack_h) / 2.0
171
+
172
+ # guide line spanning the stack
173
+ if len(col) >= 1:
174
+ pen = QPen(self._line, 2)
175
+ pen.setCapStyle(Qt.RoundCap)
176
+ p.setPen(pen)
177
+ p.drawLine(int(cx), int(max(6, top - 8)),
178
+ int(cx), int(min(h - 6, top + stack_h + 8)))
179
+
180
+ y = top
181
+ for (v, c), bh in zip(col, heights):
182
+ fill = self._fill(c)
183
+ rect = QRectF(cx - bw / 2.0, y, bw, bh)
184
+ r = bw / 2.0
185
+ p.setPen(Qt.NoPen)
186
+ p.setBrush(QBrush(fill))
187
+ p.drawRoundedRect(rect, r, r)
188
+ if self._show_values:
189
+ p.setPen(self._text(c))
190
+ # fit the number: shrink font for short pills
191
+ ps = max(8, min(13, int(bh * 0.34)))
192
+ vfont.setPointSize(ps)
193
+ p.setFont(vfont)
194
+ p.drawText(rect, Qt.AlignCenter, ("%g" % v))
195
+ y += bh + gap
196
+ p.end()
197
+
198
+ # ------------------------------------------------------------------ #
199
+ ## Designer properties
200
+ # ------------------------------------------------------------------ #
201
+ @Property(str)
202
+ def dataCsv(self):
203
+ return ";".join(
204
+ ",".join("%g:%d" % (v, c) for (v, c) in col) for col in self._cols)
205
+
206
+ @dataCsv.setter
207
+ def dataCsv(self, text):
208
+ cols = []
209
+ for chunk in str(text).replace("|", ";").split(";"):
210
+ chunk = chunk.strip()
211
+ if not chunk:
212
+ continue
213
+ col = []
214
+ for tok in chunk.split(","):
215
+ tok = tok.strip()
216
+ if not tok:
217
+ continue
218
+ if ":" in tok:
219
+ a, b = tok.split(":", 1)
220
+ try:
221
+ col.append((float(a), int(float(b))))
222
+ except ValueError:
223
+ pass
224
+ else:
225
+ try:
226
+ col.append((float(tok), 0))
227
+ except ValueError:
228
+ pass
229
+ if col:
230
+ cols.append(col)
231
+ if cols:
232
+ self.setData(cols)
233
+
234
+ @Property(str)
235
+ def colorsCsv(self):
236
+ return ",".join(c.name() for c in self._colors)
237
+
238
+ @colorsCsv.setter
239
+ def colorsCsv(self, text):
240
+ cols = [t.strip() for t in str(text).replace(";", ",").split(",") if t.strip()]
241
+ if cols:
242
+ self._colors = [QColor(c) for c in cols]
243
+ self.update()
244
+
245
+ @Property(str)
246
+ def textColorsCsv(self):
247
+ return ",".join(c.name() for c in self._text_colors)
248
+
249
+ @textColorsCsv.setter
250
+ def textColorsCsv(self, text):
251
+ cols = [t.strip() for t in str(text).replace(";", ",").split(",") if t.strip()]
252
+ if cols:
253
+ self._text_colors = [QColor(c) for c in cols]
254
+ self.update()
255
+
256
+ @Property(QColor)
257
+ def lineColor(self):
258
+ return self._line
259
+
260
+ @lineColor.setter
261
+ def lineColor(self, c):
262
+ self._line = QColor(c)
263
+ self.update()
264
+
265
+ @Property(int)
266
+ def minSize(self):
267
+ return self._min_size
268
+
269
+ @minSize.setter
270
+ def minSize(self, v):
271
+ self._min_size = max(6, int(v))
272
+ self.update()
273
+
274
+ @Property(int)
275
+ def maxSize(self):
276
+ return self._max_size
277
+
278
+ @maxSize.setter
279
+ def maxSize(self, v):
280
+ self._max_size = max(self._min_size, int(v))
281
+ self.update()
282
+
283
+ @Property(int)
284
+ def bubbleWidth(self):
285
+ return self._bubble_w
286
+
287
+ @bubbleWidth.setter
288
+ def bubbleWidth(self, v):
289
+ self._bubble_w = max(8, int(v))
290
+ self.update()
291
+
292
+ @Property(int)
293
+ def gap(self):
294
+ return self._gap
295
+
296
+ @gap.setter
297
+ def gap(self, v):
298
+ self._gap = max(0, int(v))
299
+ self.update()
300
+
301
+ @Property(bool)
302
+ def showValues(self):
303
+ return self._show_values
304
+
305
+ @showValues.setter
306
+ def showValues(self, v):
307
+ self._show_values = bool(v)
308
+ self.update()
309
+
310
+ @Property(int)
311
+ def jitter(self):
312
+ return self._jitter
313
+
314
+ @jitter.setter
315
+ def jitter(self, v):
316
+ self._jitter = max(0, int(v))
317
+ self.update()