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,300 @@
1
+ ########################################################################
2
+ ## SPINN DESIGN CODE
3
+ # YOUTUBE: (SPINN TV) https://www.youtube.com/@SpinnTV
4
+ # WEBSITE: customwidgets.org
5
+ ########################################################################
6
+
7
+ ########################################################################
8
+ ## QCustomImageViewer - a modal lightbox for a gallery of images.
9
+ ##
10
+ ## A frameless overlay that covers its host window with a dark scrim and
11
+ ## shows one image centred (aspect-fit), with painted prev / next arrows, a
12
+ ## close button and a "3 / 12" counter. Feed it images with setImages([...])
13
+ ## (QPixmap / path) and open a given one with openAt(index, host); it parents
14
+ ## itself to the host and resizes to cover it. Left / Right / Esc navigate and
15
+ ## close; clicking the scrim closes. Emits indexChanged(int) and closed(). The
16
+ ## scrim is intentionally dark in every theme (a lightbox convention); its
17
+ ## colours are qproperties so a form can still retint them.
18
+ ########################################################################
19
+ from qtpy.QtCore import Qt, Property, Signal, QRectF, QPoint
20
+ from qtpy.QtGui import QColor, QPainter, QBrush, QPen, QPolygonF, QPixmap, QFont
21
+ from qtpy.QtCore import QPointF
22
+ from qtpy.QtWidgets import QWidget, QSizePolicy
23
+
24
+
25
+ class _NavButton(QWidget):
26
+ """A painted circular button: kind = 'prev' | 'next' | 'close'."""
27
+ clicked = Signal()
28
+
29
+ def __init__(self, kind, parent=None):
30
+ super().__init__(parent)
31
+ self._kind = kind
32
+ self._bg = QColor(255, 255, 255, 28)
33
+ self._fg = QColor("#ffffff")
34
+ self._hover = False
35
+ self._d = 46 if kind != "close" else 40
36
+ self.setFixedSize(self._d, self._d)
37
+ self.setCursor(Qt.PointingHandCursor)
38
+ self.setAttribute(Qt.WA_Hover, True)
39
+
40
+ def setColors(self, bg, fg):
41
+ self._bg, self._fg = QColor(bg), QColor(fg)
42
+ self.update()
43
+
44
+ def enterEvent(self, e):
45
+ self._hover = True
46
+ self.update()
47
+
48
+ def leaveEvent(self, e):
49
+ self._hover = False
50
+ self.update()
51
+
52
+ def paintEvent(self, e):
53
+ p = QPainter(self)
54
+ p.setRenderHint(QPainter.Antialiasing, True)
55
+ p.setPen(Qt.NoPen)
56
+ bg = QColor(self._bg)
57
+ if self._hover:
58
+ bg = bg.lighter(140)
59
+ bg.setAlpha(min(255, self._bg.alpha() + 40))
60
+ p.setBrush(QBrush(bg))
61
+ p.drawEllipse(self.rect())
62
+ c = self._d / 2.0
63
+ pen = QPen(self._fg, max(2.0, self._d * 0.055))
64
+ pen.setCapStyle(Qt.RoundCap)
65
+ pen.setJoinStyle(Qt.RoundJoin)
66
+ p.setPen(pen)
67
+ p.setBrush(Qt.NoBrush)
68
+ s = self._d * 0.17
69
+ if self._kind == "close":
70
+ p.drawLine(QPointF(c - s, c - s), QPointF(c + s, c + s))
71
+ p.drawLine(QPointF(c - s, c + s), QPointF(c + s, c - s))
72
+ elif self._kind == "prev":
73
+ p.drawPolyline(QPolygonF([QPointF(c + s * 0.6, c - s),
74
+ QPointF(c - s * 0.6, c),
75
+ QPointF(c + s * 0.6, c + s)]))
76
+ else: # next
77
+ p.drawPolyline(QPolygonF([QPointF(c - s * 0.6, c - s),
78
+ QPointF(c + s * 0.6, c),
79
+ QPointF(c - s * 0.6, c + s)]))
80
+ p.end()
81
+
82
+ def mousePressEvent(self, e):
83
+ if e.button() == Qt.LeftButton:
84
+ self.clicked.emit()
85
+ e.accept()
86
+
87
+
88
+ class QCustomImageViewer(QWidget):
89
+
90
+ indexChanged = Signal(int)
91
+ closed = Signal()
92
+
93
+ WIDGET_ICON = "components/icons/image.png"
94
+ WIDGET_TOOLTIP = "A modal lightbox for a gallery of images"
95
+ WIDGET_MODULE = "Custom_Widgets.QCustomImageViewer"
96
+ WIDGET_DOM_XML = """
97
+ <ui language='c++'>
98
+ <widget class='QCustomImageViewer' name='customImageViewer'>
99
+ <property name='geometry'><rect><x>0</x><y>0</y><width>480</width><height>320</height></rect></property>
100
+ </widget>
101
+ </ui>
102
+ """
103
+ __catalog__ = {
104
+ "name": "QCustomImageViewer",
105
+ "props": {
106
+ "scrimColor": {"type": "color", "default": "#e60a0e18"},
107
+ "buttonColor": {"type": "color", "default": "#1cffffff"},
108
+ "buttonIconColor": {"type": "color", "default": "#ffffff"},
109
+ "textColor": {"type": "color", "default": "#eef1f6"},
110
+ "imageRadius": {"type": "int", "default": 16},
111
+ },
112
+ "signals": ["indexChanged", "closed"],
113
+ "tokens_used": [],
114
+ }
115
+
116
+ def __init__(self, parent=None):
117
+ super().__init__(parent)
118
+ self.setObjectName("QCustomImageViewer")
119
+ self._pixmaps = []
120
+ self._index = 0
121
+ self._scrim = QColor(10, 12, 24, 230)
122
+ self._text = QColor("#eef1f6")
123
+ self._radius = 16
124
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
125
+ self.setAttribute(Qt.WA_StyledBackground, True)
126
+
127
+ self._prev = _NavButton("prev", self)
128
+ self._next = _NavButton("next", self)
129
+ self._close = _NavButton("close", self)
130
+ self._prev.clicked.connect(lambda: self.setIndex(self._index - 1))
131
+ self._next.clicked.connect(lambda: self.setIndex(self._index + 1))
132
+ self._close.clicked.connect(self.close)
133
+ self.hide()
134
+
135
+ # ------------------------------------------------------------------ #
136
+ ## Data API
137
+ # ------------------------------------------------------------------ #
138
+ def setImages(self, images):
139
+ self._pixmaps = [im if isinstance(im, QPixmap) else QPixmap(str(im))
140
+ for im in (images or [])]
141
+ self.update()
142
+
143
+ def setImageAt(self, index, pm):
144
+ """Swap one image in place (e.g. the full-res photo arriving async)."""
145
+ pm = pm if isinstance(pm, QPixmap) else QPixmap(str(pm))
146
+ if 0 <= index < len(self._pixmaps):
147
+ self._pixmaps[index] = pm
148
+ if index == self._index:
149
+ self.update()
150
+
151
+ def count(self):
152
+ return len(self._pixmaps)
153
+
154
+ def index(self):
155
+ return self._index
156
+
157
+ def setIndex(self, i):
158
+ if not self._pixmaps:
159
+ return
160
+ i = max(0, min(len(self._pixmaps) - 1, int(i)))
161
+ if i != self._index:
162
+ self._index = i
163
+ self.indexChanged.emit(i)
164
+ self._syncNav()
165
+ self.update()
166
+
167
+ def openAt(self, index, host=None):
168
+ """Cover `host` (or the current parent) and show image `index`."""
169
+ if host is not None and host is not self.parent():
170
+ self.setParent(host)
171
+ parent = self.parent()
172
+ if parent is not None:
173
+ self.setGeometry(parent.rect())
174
+ self._index = max(0, min(max(0, len(self._pixmaps) - 1), int(index)))
175
+ self._syncNav()
176
+ self.raise_()
177
+ self.show()
178
+ self.setFocus(Qt.OtherFocusReason)
179
+ self.update()
180
+
181
+ # ------------------------------------------------------------------ #
182
+ ## Layout / paint
183
+ # ------------------------------------------------------------------ #
184
+ def _syncNav(self):
185
+ multi = len(self._pixmaps) > 1
186
+ self._prev.setVisible(multi)
187
+ self._next.setVisible(multi)
188
+
189
+ def resizeEvent(self, e):
190
+ w, h = self.width(), self.height()
191
+ m = 22
192
+ self._close.move(w - self._close.width() - m, m)
193
+ self._prev.move(m, (h - self._prev.height()) // 2)
194
+ self._next.move(w - self._next.width() - m, (h - self._next.height()) // 2)
195
+ super().resizeEvent(e)
196
+
197
+ def _imageRect(self):
198
+ w, h = self.width(), self.height()
199
+ avail_w, avail_h = w - 200, h - 150
200
+ pm = self._pixmaps[self._index] if self._pixmaps else None
201
+ if pm is None or pm.isNull() or avail_w <= 0 or avail_h <= 0:
202
+ return QRectF(), None
203
+ scaled = pm.size().scaled(avail_w, avail_h, Qt.KeepAspectRatio)
204
+ x = (w - scaled.width()) / 2.0
205
+ y = (h - scaled.height()) / 2.0
206
+ return QRectF(x, y, scaled.width(), scaled.height()), pm
207
+
208
+ def paintEvent(self, e):
209
+ p = QPainter(self)
210
+ p.setRenderHint(QPainter.Antialiasing, True)
211
+ p.setRenderHint(QPainter.SmoothPixmapTransform, True)
212
+ p.fillRect(self.rect(), self._scrim)
213
+ rect, pm = self._imageRect()
214
+ if pm is not None:
215
+ from qtpy.QtGui import QPainterPath
216
+ path = QPainterPath()
217
+ path.addRoundedRect(rect, self._radius, self._radius)
218
+ p.save()
219
+ p.setClipPath(path)
220
+ p.drawPixmap(rect.toRect(), pm)
221
+ p.restore()
222
+ # counter
223
+ if len(self._pixmaps) > 1:
224
+ f = QFont(self.font())
225
+ f.setPointSizeF(11)
226
+ f.setWeight(QFont.DemiBold)
227
+ p.setFont(f)
228
+ p.setPen(QPen(self._text))
229
+ txt = "%d / %d" % (self._index + 1, len(self._pixmaps))
230
+ p.drawText(QRectF(0, 20, self.width(), 30), Qt.AlignHCenter | Qt.AlignVCenter, txt)
231
+ p.end()
232
+
233
+ def mousePressEvent(self, e):
234
+ # click on the scrim (not the image) closes
235
+ rect, pm = self._imageRect()
236
+ if pm is None or not rect.contains(e.position()):
237
+ self.close()
238
+ e.accept()
239
+
240
+ def keyPressEvent(self, e):
241
+ if e.key() == Qt.Key_Escape:
242
+ self.close()
243
+ elif e.key() in (Qt.Key_Left, Qt.Key_Up):
244
+ self.setIndex(self._index - 1)
245
+ elif e.key() in (Qt.Key_Right, Qt.Key_Down):
246
+ self.setIndex(self._index + 1)
247
+ else:
248
+ super().keyPressEvent(e)
249
+
250
+ def closeEvent(self, e):
251
+ self.closed.emit()
252
+ super().closeEvent(e)
253
+
254
+ # ------------------------------------------------------------------ #
255
+ ## Designer properties
256
+ # ------------------------------------------------------------------ #
257
+ @Property(QColor)
258
+ def scrimColor(self):
259
+ return self._scrim
260
+
261
+ @scrimColor.setter
262
+ def scrimColor(self, c):
263
+ self._scrim = QColor(c)
264
+ self.update()
265
+
266
+ @Property(QColor)
267
+ def buttonColor(self):
268
+ return self._prev._bg
269
+
270
+ @buttonColor.setter
271
+ def buttonColor(self, c):
272
+ for b in (self._prev, self._next, self._close):
273
+ b.setColors(QColor(c), b._fg)
274
+
275
+ @Property(QColor)
276
+ def buttonIconColor(self):
277
+ return self._prev._fg
278
+
279
+ @buttonIconColor.setter
280
+ def buttonIconColor(self, c):
281
+ for b in (self._prev, self._next, self._close):
282
+ b.setColors(b._bg, QColor(c))
283
+
284
+ @Property(QColor)
285
+ def textColor(self):
286
+ return self._text
287
+
288
+ @textColor.setter
289
+ def textColor(self, c):
290
+ self._text = QColor(c)
291
+ self.update()
292
+
293
+ @Property(int)
294
+ def imageRadius(self):
295
+ return self._radius
296
+
297
+ @imageRadius.setter
298
+ def imageRadius(self, v):
299
+ self._radius = max(0, int(v))
300
+ self.update()
@@ -0,0 +1,197 @@
1
+ ########################################################################
2
+ ## SPINN DESIGN CODE
3
+ # YOUTUBE: (SPINN TV) https://www.youtube.com/@SpinnTV
4
+ # WEBSITE: customwidgets.org
5
+ ########################################################################
6
+
7
+ ########################################################################
8
+ ## QCustomMediaGrid - a data-driven thumbnail gallery.
9
+ ##
10
+ ## The "Media, Files & Links" photo grid: a responsive grid of rounded
11
+ ## thumbnails. Feed it images with setImages([...]) (QPixmap / path) or coloured
12
+ ## placeholders with setPlaceholders([(top,bottom), …]); it lays them out in
13
+ ## `columns` with rounded corners and emits `tileClicked(index)` so a manager
14
+ ## can open a viewer. Layout, corner radius and spacing are Designer
15
+ ## qproperties — the manager supplies DATA only, never builds tiles.
16
+ ########################################################################
17
+ from qtpy.QtCore import Qt, Property, Signal, QSize
18
+ from qtpy.QtGui import QColor, QPixmap, QPainter, QPainterPath, QBrush, QLinearGradient
19
+ from qtpy.QtWidgets import QLabel, QWidget, QGridLayout, QSizePolicy
20
+
21
+
22
+ class _Tile(QLabel):
23
+ clicked = Signal(int)
24
+
25
+ def __init__(self, index, radius, parent=None):
26
+ super().__init__(parent)
27
+ self._index = index
28
+ self._radius = radius
29
+ self.setObjectName("mediaTile%d" % index)
30
+ self.setScaledContents(True)
31
+ self.setCursor(Qt.PointingHandCursor)
32
+ self.setMinimumSize(10, 10)
33
+ self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
34
+
35
+ def setRoundedPixmap(self, pm):
36
+ self.setPixmap(_rounded(pm, self._radius))
37
+
38
+ def mousePressEvent(self, e):
39
+ if e.button() == Qt.LeftButton:
40
+ self.clicked.emit(self._index)
41
+ super().mousePressEvent(e)
42
+
43
+
44
+ def _rounded(pm, radius):
45
+ if pm is None or pm.isNull():
46
+ return QPixmap()
47
+ out = QPixmap(pm.size())
48
+ out.setDevicePixelRatio(pm.devicePixelRatio())
49
+ out.fill(QColor(0, 0, 0, 0))
50
+ p = QPainter(out)
51
+ p.setRenderHint(QPainter.Antialiasing, True)
52
+ path = QPainterPath()
53
+ path.addRoundedRect(0, 0, pm.width(), pm.height(), radius, radius)
54
+ p.setClipPath(path)
55
+ p.drawPixmap(0, 0, pm)
56
+ p.end()
57
+ return out
58
+
59
+
60
+ def _gradient(top, bottom, w=220, h=150, radius=22):
61
+ pm = QPixmap(w, h)
62
+ pm.fill(QColor(0, 0, 0, 0))
63
+ p = QPainter(pm)
64
+ p.setRenderHint(QPainter.Antialiasing, True)
65
+ g = QLinearGradient(0, 0, w, h)
66
+ g.setColorAt(0.0, QColor(top))
67
+ g.setColorAt(1.0, QColor(bottom))
68
+ p.setPen(Qt.NoPen)
69
+ p.setBrush(QBrush(g))
70
+ p.drawRoundedRect(0, 0, w, h, radius, radius)
71
+ p.end()
72
+ return pm
73
+
74
+
75
+ class QCustomMediaGrid(QWidget):
76
+
77
+ tileClicked = Signal(int)
78
+
79
+ WIDGET_ICON = "components/icons/collections.png"
80
+ WIDGET_TOOLTIP = "A data-driven thumbnail gallery"
81
+ WIDGET_MODULE = "Custom_Widgets.QCustomMediaGrid"
82
+ WIDGET_DOM_XML = """
83
+ <ui language='c++'>
84
+ <widget class='QCustomMediaGrid' name='customMediaGrid'>
85
+ <property name='geometry'><rect><x>0</x><y>0</y><width>280</width><height>200</height></rect></property>
86
+ </widget>
87
+ </ui>
88
+ """
89
+ __catalog__ = {
90
+ "name": "QCustomMediaGrid",
91
+ "props": {"columns": {"type": "int", "default": 3},
92
+ "spacing": {"type": "int", "default": 8},
93
+ "tileRadius": {"type": "int", "default": 12},
94
+ "tileHeight": {"type": "int", "default": 60}},
95
+ "signals": ["tileClicked"],
96
+ "tokens_used": [],
97
+ }
98
+
99
+ def __init__(self, parent=None):
100
+ super().__init__(parent)
101
+ self.setObjectName("QCustomMediaGrid")
102
+ self._columns = 3
103
+ self._spacing = 8
104
+ self._radius = 12
105
+ self._tile_h = 60
106
+ self._pixmaps = []
107
+ self._grid = QGridLayout(self)
108
+ self._grid.setContentsMargins(0, 0, 0, 0)
109
+ self._grid.setHorizontalSpacing(self._spacing)
110
+ self._grid.setVerticalSpacing(self._spacing)
111
+ self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
112
+
113
+ # ------------------------------------------------------------------ #
114
+ ## Data API — manager supplies DATA, the widget builds the tiles.
115
+ # ------------------------------------------------------------------ #
116
+ def setImages(self, images):
117
+ self._pixmaps = [im if isinstance(im, QPixmap) else QPixmap(str(im)) for im in (images or [])]
118
+ self._rebuild()
119
+
120
+ def setPlaceholders(self, pairs):
121
+ self._pixmaps = [_gradient(a, b) for (a, b) in (pairs or [])]
122
+ self._rebuild()
123
+
124
+ def setImageAt(self, index, pm):
125
+ """Swap one tile's pixmap in place (e.g. a real photo arriving async).
126
+ Also updates the stored pixmap so pixmaps() reflects the real image."""
127
+ pm = pm if isinstance(pm, QPixmap) else QPixmap(str(pm))
128
+ if 0 <= index < len(self._pixmaps):
129
+ self._pixmaps[index] = pm
130
+ if 0 <= index < len(self._tiles):
131
+ self._tiles[index].setRoundedPixmap(pm)
132
+
133
+ def count(self):
134
+ return len(self._pixmaps)
135
+
136
+ def pixmaps(self):
137
+ """The current tile pixmaps (for feeding a lightbox / viewer)."""
138
+ return list(self._pixmaps)
139
+
140
+ _tiles = []
141
+
142
+ def _rebuild(self):
143
+ while self._grid.count():
144
+ it = self._grid.takeAt(0)
145
+ w = it.widget()
146
+ if w is not None:
147
+ w.setParent(None)
148
+ w.deleteLater()
149
+ self._tiles = []
150
+ for i, pm in enumerate(self._pixmaps):
151
+ tile = _Tile(i, self._radius, self)
152
+ tile.setFixedHeight(self._tile_h)
153
+ tile.setRoundedPixmap(pm)
154
+ tile.clicked.connect(self.tileClicked)
155
+ self._grid.addWidget(tile, i // self._columns, i % self._columns)
156
+ self._tiles.append(tile)
157
+
158
+ # ------------------------------------------------------------------ #
159
+ ## Designer properties
160
+ # ------------------------------------------------------------------ #
161
+ @Property(int)
162
+ def columns(self):
163
+ return self._columns
164
+
165
+ @columns.setter
166
+ def columns(self, v):
167
+ self._columns = max(1, int(v))
168
+ self._rebuild()
169
+
170
+ @Property(int)
171
+ def spacing(self):
172
+ return self._spacing
173
+
174
+ @spacing.setter
175
+ def spacing(self, v):
176
+ self._spacing = max(0, int(v))
177
+ self._grid.setHorizontalSpacing(self._spacing)
178
+ self._grid.setVerticalSpacing(self._spacing)
179
+
180
+ @Property(int)
181
+ def tileRadius(self):
182
+ return self._radius
183
+
184
+ @tileRadius.setter
185
+ def tileRadius(self, v):
186
+ self._radius = max(0, int(v))
187
+ self._rebuild()
188
+
189
+ @Property(int)
190
+ def tileHeight(self):
191
+ return self._tile_h
192
+
193
+ @tileHeight.setter
194
+ def tileHeight(self, v):
195
+ self._tile_h = max(20, int(v))
196
+ for t in self._tiles:
197
+ t.setFixedHeight(self._tile_h)