diff --git a/README.md b/README.md index dc9aac6..b84ba96 100644 --- a/README.md +++ b/README.md @@ -124,27 +124,30 @@ then you can also target it directly in your style sheet. /* Because QRangeSlider inherits QSlider, it will also inherit styles */ QSlider::groove:horizontal { border: 0px; - background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #FDE282, stop:1 #EB9A5D); - height: 16px; - border-radius: 2px; + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #777, stop:1 #aaa); + height: 20px; + border-radius: 10px; } -QSlider::handle:horizontal { - background: #271848; - border: 1px solid #583856; - width: 18px; - margin: -2px 0; - border-radius: 3px; +QSlider::handle { + background: qradialgradient(cx:0, cy:0, radius: 1.2, fx:0.5, + fy:0.5, stop:0 #eef, stop:1 #000); + height: 20px; + width: 20px; + border-radius: 10px; } -QSlider::handle:hover { - background-color: #2F4F4F; -} - -/* "QSlider::sub-page" will style the "bar" area between the QRangeSlider handles */ +/* "QSlider::sub-page" (which styles the area to the left of the +QSlider handle) is the one exception ... */ QSlider::sub-page:horizontal { - background: #AF5A50; - border-radius: 2px; + background: #447; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; +} + +/* for QRangeSlider, use "qproperty-barColor" */ +QRangeSlider { + qproperty-barColor: #447; } ``` diff --git a/examples/demo_widget.py b/examples/demo_widget.py index 7fd1018..5706ae2 100644 --- a/examples/demo_widget.py +++ b/examples/demo_widget.py @@ -3,31 +3,30 @@ from qtrangeslider.qtcompat import QtCore from qtrangeslider.qtcompat import QtWidgets as QtW QSS = """ - QSlider::groove:horizontal { border: 0px; - background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #FDE282, stop:1 #EB9A5D); - height: 16px; - border-radius: 2px; + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #777, stop:1 #aaa); + height: 20px; + border-radius: 10px; } -QSlider::handle:horizontal { - background: #271848; - border: 1px solid #583856; - width: 18px; - margin: -2px 0; - border-radius: 3px; -} - -QSlider::handle:hover { - background-color: #2F4F4F; +QSlider::handle { + background: qradialgradient(cx:0, cy:0, radius: 1.2, fx:0.5, + fy:0.5, stop:0 #eef, stop:1 #000); + height: 20px; + width: 20px; + border-radius: 10px; } QSlider::sub-page:horizontal { - background: #AF5A50; - border-radius: 2px; + background: #447; + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; } +QRangeSlider { + qproperty-barColor: #447; +} """ diff --git a/qtrangeslider/_qrangeslider.py b/qtrangeslider/_qrangeslider.py index 88a6b7b..381a0f2 100644 --- a/qtrangeslider/_qrangeslider.py +++ b/qtrangeslider/_qrangeslider.py @@ -4,7 +4,16 @@ from typing import List, Sequence, Tuple from ._style import RangeSliderStyle, update_styles_from_stylesheet from .qtcompat import QtGui -from .qtcompat.QtCore import QEvent, QPoint, QPointF, QRect, QRectF, Qt, Signal +from .qtcompat.QtCore import ( + Property, + QEvent, + QPoint, + QPointF, + QRect, + QRectF, + Qt, + Signal, +) from .qtcompat.QtWidgets import ( QApplication, QSlider, @@ -62,9 +71,18 @@ class QRangeSlider(QSlider): # color self._style = RangeSliderStyle() + self.setStyleSheet("") # ############### Public API ####################### + def setStyleSheet(self, styleSheet: str) -> None: + # sub-page styles render on top of the lower sliders and don't work here. + override = f""" + \n{type(self).__name__}::sub-page:horizontal {{background: none}} + \n{type(self).__name__}::sub-page:vertical {{background: none}} + """ + return super().setStyleSheet(styleSheet + override) + def value(self) -> Tuple[int, ...]: """Get current value of the widget as a tuple of integers.""" return tuple(self._value) @@ -180,6 +198,14 @@ class QRangeSlider(QSlider): opt.sliderPosition = 0 return opt + def _getBarColor(self): + return self._style.brush_active or QtGui.QColor() + + def _setBarColor(self, color): + self._style.brush_active = color + + barColor = Property(QtGui.QColor, _getBarColor, _setBarColor) + def _drawBar(self, painter: QStylePainter, opt: QStyleOptionSlider): brush = self._style.brush(opt) diff --git a/qtrangeslider/_style.py b/qtrangeslider/_style.py index 8996ba5..c7fb4dc 100644 --- a/qtrangeslider/_style.py +++ b/qtrangeslider/_style.py @@ -191,14 +191,29 @@ qradial_pattern = re.compile( re.X, ) +rgba_pattern = re.compile( + r""" + rgba?\( + (?P\d+),\s* + (?P\d+),\s* + (?P\d+),?\s*(?P\d+)?\) + """, + re.X, +) + def parse_color(color: str) -> Union[str, QGradient]: qc = QColor(color) if qc.isValid(): return qc + match = rgba_pattern.search(color) + if match: + rgba = [int(x) if x else 255 for x in match.groups()] + return QColor(*rgba) + # try linear gradient: - match = qlineargrad_pattern.match(color) + match = qlineargrad_pattern.search(color) if match: grad = QLinearGradient(*[float(i) for i in match.groups()[:4]]) grad.setColorAt(0, QColor(match.groupdict()["stop0"])) @@ -206,8 +221,7 @@ def parse_color(color: str) -> Union[str, QGradient]: return grad # try linear gradient: - match = qradial_pattern.match(color) - print("match", match.groupdict()) + match = qradial_pattern.search(color) if match: grad = QRadialGradient(*[float(i) for i in match.groups()[:5]]) grad.setColorAt(0, QColor(match.groupdict()["stop0"])) @@ -220,33 +234,31 @@ def parse_color(color: str) -> Union[str, QGradient]: def update_styles_from_stylesheet(obj: "QRangeSlider"): qss = obj.styleSheet() - p = obj - while p.parent(): - qss = p.styleSheet() + qss - p = p.parent() + + parent = obj.parent() + while parent is not None: + qss = parent.styleSheet() + qss + parent = parent.parent() qss = QApplication.instance().styleSheet() + qss - obj._style.has_stylesheet = False + # obj._style.has_stylesheet = False - # Find bar color - # TODO: optional horizontal or vertical - match = re.search(r"Slider::sub-page:?([^{\s]*)?\s*{\s*([^}]+)}", qss, re.S) - if match: - orientation, content = match.groups() - for line in reversed(content.splitlines()): - bgrd = re.search(r"background(-color)?:\s*([^;]+)", line) - if bgrd: - color = parse_color(bgrd.groups()[-1]) - obj._style.brush_active = color - # TODO: parse for inactive and disabled - obj._style.brush_inactive = color - obj._style.brush_disabled = color - obj._style.has_stylesheet = True - class_name = type(obj).__name__ - _ss = f"\n{class_name}::sub-page:{orientation}{{background: none}}" - # TODO: block double event - obj.setStyleSheet(qss + _ss) - break + # # Find bar color + # # TODO: optional horizontal or vertical + # match = re.search(r"Slider::sub-page:?([^{\s]*)?\s*{\s*([^}]+)}", qss, re.S) + # if match: + # orientation, content = match.groups() + # for line in reversed(content.splitlines()): + # bgrd = re.search(r"background(-color)?:\s*([^;]+)", line) + # if bgrd: + # color = parse_color(bgrd.groups()[-1]) + # obj._style.brush_active = color + # # TODO: parse for inactive and disabled + # obj._style.brush_inactive = color + # obj._style.brush_disabled = color + # obj._style.has_stylesheet = True + # obj.update() + # break # Find bar height/width for orient, dim in (("horizontal", "height"), ("vertical", "width")):