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,555 @@
1
+ ########################################################################
2
+ ## SPINN DESIGN CODE
3
+ # YOUTUBE: (SPINN TV) https://www.youtube.com/@SpinnTV
4
+ # WEBSITE: customwidgets.org
5
+ ########################################################################
6
+
7
+ ########################################################################
8
+ ## QCustomHeatmap - a painted colour-intensity grid.
9
+ ##
10
+ ## Two modes (`mode`):
11
+ ## "grid" (default) - a rows x cols matrix (e.g. hours x weekdays, the
12
+ ## "Activity by time" heatmap): each cell's colour is its value mapped on a
13
+ ## low->high ramp, with row/col labels and an optional Less->More legend.
14
+ ## "calendar" - a GitHub-style contributions calendar: a flat list of daily
15
+ ## values wrapped into 7 rows (weekdays) x N columns (weeks).
16
+ ##
17
+ ## Values come in via setValues(list[list[float]]) / a flat list (calendar), or
18
+ ## the `valuesCsv` Designer property (rows separated by ';', cells by ','). Cells
19
+ ## auto-normalise across the data (min->max) unless you setRange(...). Painted
20
+ ## with QPainter so it stays crisp at any size; the grid FLEX-fits the box left
21
+ ## after labels + legend, so nothing clips when the widget grows or shrinks.
22
+ ##
23
+ ## Signals: cellClicked(row, col, value); a per-cell tooltip on hover.
24
+ ########################################################################
25
+ import math
26
+
27
+ from qtpy.QtCore import Qt, Property, Signal, QRectF, QPointF
28
+ from qtpy.QtGui import QColor, QPainter, QPen, QBrush, QFont, QFontMetrics
29
+ from qtpy.QtWidgets import QWidget, QSizePolicy
30
+
31
+
32
+ class QCustomHeatmap(QWidget):
33
+
34
+ cellClicked = Signal(int, int, float)
35
+
36
+ WIDGET_ICON = "components/icons/grid_on.png"
37
+ WIDGET_TOOLTIP = "A painted colour-intensity grid (activity / calendar heatmap)"
38
+ WIDGET_MODULE = "Custom_Widgets.QCustomHeatmap"
39
+ WIDGET_DOM_XML = """
40
+ <ui language='c++'>
41
+ <widget class='QCustomHeatmap' name='customHeatmap'>
42
+ <property name='geometry'><rect><x>0</x><y>0</y><width>340</width><height>230</height></rect></property>
43
+ </widget>
44
+ </ui>
45
+ """
46
+ __catalog__ = {
47
+ "name": "QCustomHeatmap",
48
+ "props": {
49
+ "mode": {"type": "enum", "values": ["grid", "calendar"], "default": "grid"},
50
+ "valuesCsv": {"type": "string", "default": ""},
51
+ "lowColor": {"type": "color", "default": "#1e1b3a"},
52
+ "highColor": {"type": "color", "default": "#b3a4ff"},
53
+ "emptyColor": {"type": "color", "default": "#17152b"},
54
+ "rowLabelsCsv": {"type": "string", "default": ""},
55
+ "colLabelsCsv": {"type": "string", "default": ""},
56
+ "cellSize": {"type": "int", "default": 0},
57
+ "cellGap": {"type": "int", "default": 4},
58
+ "cornerRadius": {"type": "int", "default": 4},
59
+ "showLabels": {"type": "bool", "default": True},
60
+ "showLegend": {"type": "bool", "default": True},
61
+ "labelColor": {"type": "color", "default": "#8b90a0"},
62
+ "autoNormalize": {"type": "bool", "default": True},
63
+ "minValue": {"type": "float", "default": 0.0},
64
+ "maxValue": {"type": "float", "default": 1.0},
65
+ },
66
+ "signals": ["cellClicked"],
67
+ # Intentionally no tokens_used: this widget resolves no token roles.
68
+ }
69
+
70
+ # Rich editors for the Designer "Custom Properties" dock (see
71
+ # DesignerTools.CustomPropertiesDock).
72
+ DESIGNER_CUSTOM_PROPS = [
73
+ {"name": "mode", "kind": "choice", "group": "General"},
74
+ {"name": "valuesCsv", "kind": "str", "group": "General"},
75
+ {"name": "lowColor", "kind": "color", "group": "Colors"},
76
+ {"name": "highColor", "kind": "color", "group": "Colors"},
77
+ {"name": "emptyColor", "kind": "color", "group": "Colors"},
78
+ {"name": "rowLabelsCsv", "kind": "str", "group": "General"},
79
+ {"name": "colLabelsCsv", "kind": "str", "group": "General"},
80
+ {"name": "cellSize", "kind": "int", "group": "General"},
81
+ {"name": "cellGap", "kind": "int", "group": "General"},
82
+ {"name": "cornerRadius", "kind": "int", "group": "General"},
83
+ {"name": "showLabels", "kind": "bool", "group": "General"},
84
+ {"name": "showLegend", "kind": "bool", "group": "General"},
85
+ {"name": "labelColor", "kind": "color", "group": "Colors"},
86
+ {"name": "autoNormalize", "kind": "bool", "group": "General"},
87
+ {"name": "minValue", "kind": "float", "group": "General"},
88
+ {"name": "maxValue", "kind": "float", "group": "General"},
89
+ ]
90
+
91
+ def __init__(self, parent=None, values=None, mode="grid"):
92
+ super().__init__(parent)
93
+ self.setObjectName("QCustomHeatmap")
94
+ self._mode = "calendar" if str(mode) == "calendar" else "grid"
95
+ self._values = self._coerce(values) if values is not None else self._seed()
96
+ self._low = QColor("#1e1b3a")
97
+ self._high = QColor("#b3a4ff")
98
+ self._empty = QColor("#17152b")
99
+ self._row_labels = ["1pm", "2pm", "3pm", "4pm", "5pm", "6pm"]
100
+ self._col_labels = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
101
+ self._cell_size = 0 # 0 -> auto flex-fit
102
+ self._cell_gap = 4
103
+ self._radius = 4
104
+ self._show_labels = True
105
+ self._show_legend = True
106
+ self._label_color = QColor("#8b90a0")
107
+ self._auto = True
108
+ self._min = 0.0
109
+ self._max = 1.0
110
+ self._hover = (-1, -1)
111
+ self._rects = {} # (r,c) -> QRectF (last layout, for hit-test)
112
+ self.setMouseTracking(True)
113
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
114
+ self.setMinimumSize(120, 90)
115
+
116
+ # ------------------------------------------------------------------ #
117
+ ## Data
118
+ # ------------------------------------------------------------------ #
119
+ @staticmethod
120
+ def _seed():
121
+ # a small activity-by-time grid so it previews in Designer / render_widget
122
+ import itertools
123
+ rng = [3, 8, 2, 6, 9, 1, 4, 7, 5, 2, 8, 6, 3, 9, 4, 1, 7, 5, 6, 2, 8]
124
+ it = itertools.cycle(rng)
125
+ return [[float(next(it)) for _ in range(7)] for _ in range(6)]
126
+
127
+ @staticmethod
128
+ def _coerce(values):
129
+ """Accept list[list] or a flat list (wrapped later for calendar)."""
130
+ if not values:
131
+ return []
132
+ if isinstance(values[0], (list, tuple)):
133
+ return [[(None if v is None else float(v)) for v in row] for row in values]
134
+ return [float(v) for v in values] # flat -> arranged per mode
135
+
136
+ def setValues(self, values):
137
+ self._values = self._coerce(values)
138
+ self.update()
139
+
140
+ def setData(self, values):
141
+ """Backward-compatible alias for setValues().
142
+
143
+ Early releases shipped setData(); keep it so existing demos and user
144
+ code keep working after the rename.
145
+ """
146
+ self.setValues(values)
147
+
148
+ def setRange(self, minimum, maximum):
149
+ self._auto = False
150
+ self._min, self._max = float(minimum), float(maximum)
151
+ self.update()
152
+
153
+ def setColors(self, low, high, empty=None):
154
+ self._low, self._high = QColor(low), QColor(high)
155
+ if empty is not None:
156
+ self._empty = QColor(empty)
157
+ self.update()
158
+
159
+ def setLabels(self, row_labels=None, col_labels=None):
160
+ if row_labels is not None:
161
+ self._row_labels = [str(s) for s in row_labels]
162
+ if col_labels is not None:
163
+ self._col_labels = [str(s) for s in col_labels]
164
+ self.update()
165
+
166
+ def setMode(self, mode):
167
+ self._mode = "calendar" if str(mode) == "calendar" else "grid"
168
+ self.update()
169
+
170
+ def values(self):
171
+ return self._values
172
+
173
+ # ------------------------------------------------------------------ #
174
+ ## Grid shape (mode-aware): returns (rows, cols, get(r,c)->value|None)
175
+ # ------------------------------------------------------------------ #
176
+ def _matrix(self):
177
+ vals = self._values
178
+ if not vals:
179
+ return 0, 0, (lambda r, c: None)
180
+ if self._mode == "calendar":
181
+ flat = vals
182
+ if flat and isinstance(flat[0], (list, tuple)):
183
+ flat = [v for row in flat for v in row]
184
+ weeks = max(1, math.ceil(len(flat) / 7.0))
185
+
186
+ def get(r, c):
187
+ i = c * 7 + r
188
+ return flat[i] if 0 <= i < len(flat) else None
189
+ return 7, weeks, get
190
+ # grid
191
+ rows = len(vals)
192
+ cols = max((len(row) for row in vals), default=0)
193
+
194
+ def get(r, c):
195
+ row = vals[r]
196
+ return row[c] if c < len(row) else None
197
+ return rows, cols, get
198
+
199
+ def _norm_range(self):
200
+ if not self._auto:
201
+ return self._min, self._max
202
+ lo, hi = None, None
203
+ for row in self._values:
204
+ row = row if isinstance(row, (list, tuple)) else [row]
205
+ for v in row:
206
+ if v is None:
207
+ continue
208
+ lo = v if lo is None else min(lo, v)
209
+ hi = v if hi is None else max(hi, v)
210
+ if lo is None:
211
+ return 0.0, 1.0
212
+ return lo, hi
213
+
214
+ def _cell_color(self, v, lo, hi):
215
+ if v is None:
216
+ return QColor(self._empty)
217
+ t = 0.5 if hi <= lo else (v - lo) / (hi - lo)
218
+ t = max(0.0, min(1.0, t))
219
+ return self._lerp(self._low, self._high, t)
220
+
221
+ @staticmethod
222
+ def _lerp(c0, c1, t):
223
+ return QColor(
224
+ int(c0.red() + (c1.red() - c0.red()) * t),
225
+ int(c0.green() + (c1.green() - c0.green()) * t),
226
+ int(c0.blue() + (c1.blue() - c0.blue()) * t),
227
+ )
228
+
229
+ # ------------------------------------------------------------------ #
230
+ ## Flex layout — size the cells to the box left after labels + legend
231
+ # ------------------------------------------------------------------ #
232
+ def _label_font(self):
233
+ """One label font used for BOTH measuring the gutters and painting the
234
+ labels — a size mismatch here clips the row labels."""
235
+ f = QFont(self.font())
236
+ side = min(self.width(), self.height())
237
+ f.setPointSizeF(max(7.5, min(11.0, side * 0.034)))
238
+ return f
239
+
240
+ def _layout(self, rows, cols):
241
+ w, h = self.width(), self.height()
242
+ m = 8.0
243
+ fm = QFontMetrics(self._label_font())
244
+ show_lab = self._show_labels
245
+ # left gutter for row labels, top strip for column labels
246
+ left = 0.0
247
+ if show_lab and self._row_labels and self._mode == "grid":
248
+ left = max((fm.horizontalAdvance(s) for s in self._row_labels), default=0) + 12.0
249
+ top = 0.0
250
+ if show_lab and self._col_labels and self._mode == "grid":
251
+ top = fm.height() + 4.0
252
+ legend_h = (fm.height() + 10.0) if self._show_legend else 0.0
253
+
254
+ avail_w = max(1.0, w - 2 * m - left)
255
+ avail_h = max(1.0, h - 2 * m - top - legend_h)
256
+ gap = float(self._cell_gap)
257
+ if self._cell_size > 0:
258
+ cell = float(self._cell_size)
259
+ else:
260
+ cell = min((avail_w - (cols - 1) * gap) / max(cols, 1),
261
+ (avail_h - (rows - 1) * gap) / max(rows, 1))
262
+ cell = max(cell, 2.0)
263
+ grid_w = cols * cell + (cols - 1) * gap
264
+ grid_h = rows * cell + (rows - 1) * gap
265
+ x0 = m + left + max(0.0, (avail_w - grid_w) / 2.0)
266
+ y0 = m + top + max(0.0, (avail_h - grid_h) / 2.0)
267
+ return x0, y0, cell, gap, left, top, legend_h
268
+
269
+ # ------------------------------------------------------------------ #
270
+ ## Painting
271
+ # ------------------------------------------------------------------ #
272
+ def paintEvent(self, e):
273
+ rows, cols, get = self._matrix()
274
+ if rows == 0 or cols == 0:
275
+ return
276
+ p = QPainter(self)
277
+ p.setRenderHint(QPainter.Antialiasing, True)
278
+ lo, hi = self._norm_range()
279
+ x0, y0, cell, gap, left, top, legend_h = self._layout(rows, cols)
280
+ self._rects = {}
281
+
282
+ # labels (grid mode) — same font used to measure the gutters
283
+ if self._show_labels and self._mode == "grid":
284
+ p.setFont(self._label_font())
285
+ p.setPen(QPen(self._label_color))
286
+ if top:
287
+ for c in range(cols):
288
+ if c < len(self._col_labels):
289
+ cx = x0 + c * (cell + gap)
290
+ p.drawText(QRectF(cx, y0 - top, cell, top - 2),
291
+ Qt.AlignCenter, self._col_labels[c])
292
+ if left:
293
+ for r in range(rows):
294
+ if r < len(self._row_labels):
295
+ ry = y0 + r * (cell + gap)
296
+ p.drawText(QRectF(4, ry, left - 8, cell),
297
+ Qt.AlignVCenter | Qt.AlignRight, self._row_labels[r])
298
+
299
+ # cells
300
+ for r in range(rows):
301
+ for c in range(cols):
302
+ v = get(r, c)
303
+ rect = QRectF(x0 + c * (cell + gap), y0 + r * (cell + gap), cell, cell)
304
+ self._rects[(r, c)] = rect
305
+ p.setPen(Qt.NoPen)
306
+ p.setBrush(QBrush(self._cell_color(v, lo, hi)))
307
+ p.drawRoundedRect(rect, self._radius, self._radius)
308
+ if (r, c) == self._hover and v is not None:
309
+ pen = QPen(QColor(255, 255, 255, 150), max(1.0, cell * 0.06))
310
+ p.setPen(pen)
311
+ p.setBrush(Qt.NoBrush)
312
+ p.drawRoundedRect(rect, self._radius, self._radius)
313
+
314
+ if self._show_legend:
315
+ self._paint_legend(p, x0, self.height() - 8.0 - (legend_h - 10.0),
316
+ x0 + cols * (cell + gap) - gap)
317
+ p.end()
318
+
319
+ def _paint_legend(self, p, x_left, y, x_right):
320
+ fm = p.fontMetrics()
321
+ f = QFont(self.font())
322
+ f.setPointSizeF(max(7.0, self.font().pointSizeF()))
323
+ p.setFont(f)
324
+ sw = max(9.0, fm.height() - 2) # swatch size
325
+ gap = 3.0
326
+ steps = 4
327
+ block = steps * sw + (steps - 1) * gap
328
+ more_w = fm.horizontalAdvance("More") + 3
329
+ less_w = fm.horizontalAdvance("Less") + 3
330
+ total = less_w + 6 + block + 6 + more_w
331
+ x = max(x_left, x_right - total)
332
+ p.setPen(QPen(self._label_color))
333
+ p.drawText(QRectF(x, y - fm.height() / 2.0 + sw / 2.0, less_w, fm.height()),
334
+ Qt.AlignVCenter | Qt.AlignLeft, "Less")
335
+ x += less_w + 6
336
+ for i in range(steps):
337
+ t = i / (steps - 1)
338
+ p.setPen(Qt.NoPen)
339
+ p.setBrush(QBrush(self._lerp(self._low, self._high, t)))
340
+ p.drawRoundedRect(QRectF(x, y, sw, sw), 2, 2)
341
+ x += sw + gap
342
+ x += 6 - gap
343
+ p.setPen(QPen(self._label_color))
344
+ p.drawText(QRectF(x, y - fm.height() / 2.0 + sw / 2.0, more_w, fm.height()),
345
+ Qt.AlignVCenter | Qt.AlignLeft, "More")
346
+
347
+ # ------------------------------------------------------------------ #
348
+ ## Interaction
349
+ # ------------------------------------------------------------------ #
350
+ def _cell_at(self, pos):
351
+ for (r, c), rect in self._rects.items():
352
+ if rect.contains(pos):
353
+ return r, c
354
+ return -1, -1
355
+
356
+ def mouseMoveEvent(self, e):
357
+ pos = e.position() if hasattr(e, "position") else QPointF(e.pos())
358
+ rc = self._cell_at(pos)
359
+ if rc != self._hover:
360
+ self._hover = rc
361
+ r, c = rc
362
+ if r >= 0:
363
+ _, _, get = self._matrix()
364
+ v = get(r, c)
365
+ if v is not None:
366
+ rl = self._row_labels[r] if r < len(self._row_labels) else "%d" % r
367
+ cl = self._col_labels[c] if c < len(self._col_labels) else "%d" % c
368
+ self.setToolTip("%s · %s: %g" % (cl, rl, v))
369
+ else:
370
+ self.setToolTip("")
371
+ else:
372
+ self.setToolTip("")
373
+ self.update()
374
+ super().mouseMoveEvent(e)
375
+
376
+ def leaveEvent(self, e):
377
+ if self._hover != (-1, -1):
378
+ self._hover = (-1, -1)
379
+ self.update()
380
+ super().leaveEvent(e)
381
+
382
+ def mousePressEvent(self, e):
383
+ pos = e.position() if hasattr(e, "position") else QPointF(e.pos())
384
+ r, c = self._cell_at(pos)
385
+ if r >= 0:
386
+ _, _, get = self._matrix()
387
+ v = get(r, c)
388
+ self.cellClicked.emit(r, c, float(v) if v is not None else float("nan"))
389
+ super().mousePressEvent(e)
390
+
391
+ # ------------------------------------------------------------------ #
392
+ ## Designer properties
393
+ # ------------------------------------------------------------------ #
394
+ @Property(str)
395
+ def mode(self):
396
+ return self._mode
397
+
398
+ @mode.setter
399
+ def mode(self, m):
400
+ self.setMode(m)
401
+
402
+ @Property(str)
403
+ def valuesCsv(self):
404
+ rows = self._values if self._values and isinstance(self._values[0], (list, tuple)) \
405
+ else [self._values]
406
+ return ";".join(",".join("" if v is None else "%g" % v for v in row) for row in rows)
407
+
408
+ @valuesCsv.setter
409
+ def valuesCsv(self, text):
410
+ out = []
411
+ for rowtok in str(text).split(";"):
412
+ rowtok = rowtok.strip()
413
+ if not rowtok:
414
+ continue
415
+ row = []
416
+ for tok in rowtok.split(","):
417
+ tok = tok.strip()
418
+ row.append(None if tok == "" else float(tok) if _isnum(tok) else None)
419
+ out.append(row)
420
+ if out:
421
+ self.setValues(out)
422
+
423
+ @Property(str)
424
+ def rowLabelsCsv(self):
425
+ return ",".join(self._row_labels)
426
+
427
+ @rowLabelsCsv.setter
428
+ def rowLabelsCsv(self, text):
429
+ self.setLabels(row_labels=[s.strip() for s in str(text).split(",") if s.strip()])
430
+
431
+ @Property(str)
432
+ def colLabelsCsv(self):
433
+ return ",".join(self._col_labels)
434
+
435
+ @colLabelsCsv.setter
436
+ def colLabelsCsv(self, text):
437
+ self.setLabels(col_labels=[s.strip() for s in str(text).split(",") if s.strip()])
438
+
439
+ @Property(QColor)
440
+ def lowColor(self):
441
+ return self._low
442
+
443
+ @lowColor.setter
444
+ def lowColor(self, c):
445
+ self._low = QColor(c)
446
+ self.update()
447
+
448
+ @Property(QColor)
449
+ def highColor(self):
450
+ return self._high
451
+
452
+ @highColor.setter
453
+ def highColor(self, c):
454
+ self._high = QColor(c)
455
+ self.update()
456
+
457
+ @Property(QColor)
458
+ def emptyColor(self):
459
+ return self._empty
460
+
461
+ @emptyColor.setter
462
+ def emptyColor(self, c):
463
+ self._empty = QColor(c)
464
+ self.update()
465
+
466
+ @Property(int)
467
+ def cellSize(self):
468
+ return self._cell_size
469
+
470
+ @cellSize.setter
471
+ def cellSize(self, v):
472
+ self._cell_size = max(0, int(v))
473
+ self.update()
474
+
475
+ @Property(int)
476
+ def cellGap(self):
477
+ return self._cell_gap
478
+
479
+ @cellGap.setter
480
+ def cellGap(self, v):
481
+ self._cell_gap = max(0, int(v))
482
+ self.update()
483
+
484
+ @Property(int)
485
+ def cornerRadius(self):
486
+ return self._radius
487
+
488
+ @cornerRadius.setter
489
+ def cornerRadius(self, v):
490
+ self._radius = max(0, int(v))
491
+ self.update()
492
+
493
+ @Property(bool)
494
+ def showLabels(self):
495
+ return self._show_labels
496
+
497
+ @showLabels.setter
498
+ def showLabels(self, v):
499
+ self._show_labels = bool(v)
500
+ self.update()
501
+
502
+ @Property(bool)
503
+ def showLegend(self):
504
+ return self._show_legend
505
+
506
+ @showLegend.setter
507
+ def showLegend(self, v):
508
+ self._show_legend = bool(v)
509
+ self.update()
510
+
511
+ @Property(QColor)
512
+ def labelColor(self):
513
+ return self._label_color
514
+
515
+ @labelColor.setter
516
+ def labelColor(self, c):
517
+ self._label_color = QColor(c)
518
+ self.update()
519
+
520
+ @Property(bool)
521
+ def autoNormalize(self):
522
+ return self._auto
523
+
524
+ @autoNormalize.setter
525
+ def autoNormalize(self, v):
526
+ self._auto = bool(v)
527
+ self.update()
528
+
529
+ @Property(float)
530
+ def minValue(self):
531
+ return self._min
532
+
533
+ @minValue.setter
534
+ def minValue(self, v):
535
+ self._min = float(v)
536
+ self._auto = False
537
+ self.update()
538
+
539
+ @Property(float)
540
+ def maxValue(self):
541
+ return self._max
542
+
543
+ @maxValue.setter
544
+ def maxValue(self, v):
545
+ self._max = float(v)
546
+ self._auto = False
547
+ self.update()
548
+
549
+
550
+ def _isnum(s):
551
+ try:
552
+ float(s)
553
+ return True
554
+ except ValueError:
555
+ return False