diff options
Diffstat (limited to 'raphodo/toggleswitch.py')
-rw-r--r-- | raphodo/toggleswitch.py | 184 |
1 files changed, 184 insertions, 0 deletions
diff --git a/raphodo/toggleswitch.py b/raphodo/toggleswitch.py new file mode 100644 index 0000000..0f890f1 --- /dev/null +++ b/raphodo/toggleswitch.py @@ -0,0 +1,184 @@ +# Copyright (C) 2016 Damon Lynch <damonlynch@gmail.com> + +# This file is part of Rapid Photo Downloader. +# +# Rapid Photo Downloader is free software: you can redistribute it and/or +# modify it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Rapid Photo Downloader is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Rapid Photo Downloader. If not, +# see <http://www.gnu.org/licenses/>. + +""" +Toggle Switch reminiscent of Android On/off switches: +https://www.google.com/design/spec/components/selection-controls.html + +Visual style is rounded. However by adjusting the style sheet it can be +made like a rounded square, close to how Gnome handles it, albeit +without the "ON"/"OFF text. + +Inspiration: +http://stackoverflow.com/questions/14780517/toggle-switch-in-qt +http://thesmithfam.org/blog/2010/03/10/fancy-qslider-stylesheet/ +""" + +__author__ = 'Damon Lynch' +__copyright__ = "Copyright 2016, Damon Lynch" + +from typing import Optional + +from PyQt5.QtCore import Qt, pyqtSlot +from PyQt5.QtGui import QPalette, QColor, QFont,QFontMetrics +from PyQt5.QtWidgets import QSlider, QApplication + +class QToggleSwitch(QSlider): + """ + Toggle Switch reminiscent of Android On/off switches. + + Connect to signal valueChanged to react to user setting the switch. + """ + def __init__(self, background: Optional[QColor]=None, parent=None) -> None: + super().__init__(Qt.Horizontal, parent) + + self.base_height = QFontMetrics(QFont()).height() // 2 * 2 + self.radius = self.base_height // 2 + + width = self.base_height * 2 + self.widgetWidth = width + self.handleWidth = width // 2 + self.sliderRange = width + self.sliderMidPoint = width // 2 + self.setRange(0, self.sliderRange) + + self.setMaximumWidth(self.widgetWidth) + self.setFixedHeight(self.base_height + 6) + + self.setStyleSheet(self.stylesheet(background)) + + self.actionTriggered.connect(self.onActionTriggered) + self.sliderReleased.connect(self.onSliderRelease) + + def stylesheet(self, background: Optional[QColor]) -> str: + shading_intensity = 104 + windowColor = (QPalette().color(QPalette().Window)) # type: QColor + + if background is None: + backgroundName = windowColor.name() + else: + backgroundName = QColor(background).name() + + handleLightName = (QPalette().color(QPalette().Light)).name() # type: QColor + handleDarkName = (QPalette().color(QPalette().Dark)).name() # type: QColor + handleHoverLightName = (QPalette().color(QPalette().Light)).lighter(shading_intensity).name() + handleHoverDarkName = (QPalette().color(QPalette().Dark)).darker(shading_intensity).name() + + insetDarkName = windowColor.darker(108).name() + insetLightName = windowColor.darker(102).name() + + highlightColor = (QPalette().color(QPalette().Highlight)) # type: QColor + highlightLightName = highlightColor.lighter(110).name() + highlightDarkName = highlightColor.darker(130).name() + + return """ + QSlider::groove:horizontal { + background-color: %(backgroundName)s; + height: %(height)s px; + } + + QSlider::sub-page:horizontal { + background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 %(highlightDarkName)s, stop: 1 %(highlightLightName)s); + border: 1px solid #777; + border-top-left-radius: %(radius)spx; + border-bottom-left-radius: %(radius)spx; + } + + QSlider::add-page:horizontal { + background: qlineargradient(x1: 0, y1: 0.2, x2: 1, y2: 1, + stop: 0 %(insetDarkName)s, stop: 1 %(insetLightName)s); + border: 1px solid #777; + border-top-right-radius: %(radius)spx; + border-bottom-right-radius: %(radius)spx; + } + + QSlider::handle:horizontal { + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 %(handleLightName)s, stop:1 %(handleDarkName)s); + border: 1px solid #777; + width: %(buttonWidth)s px; + border-radius: %(radius)spx; + } + + QSlider::handle:horizontal:hover { + background: qlineargradient(x1:0, y1:0, x2:1, y2:1, + stop:0 %(handleHoverLightName)s, stop:1 %(handleHoverDarkName)s); + border: 1px solid #444; + border-radius: %(radius)spx; + } + + QSlider::sub-page:horizontal:disabled { + background: #bbb; + border-color: #999; + } + + QSlider::add-page:horizontal:disabled { + background: #eee; + border-color: #999; + } + + QSlider::handle:horizontal:disabled { + background: #eee; + border: 1px solid #aaa; + border-radius: %(radius)spx; + } + """ % dict(buttonWidth=self.handleWidth, + handleLightName=handleLightName, + handleDarkName=handleDarkName, + handleHoverLightName=handleHoverLightName, + handleHoverDarkName=handleHoverDarkName, + backgroundName=backgroundName, + highlightDarkName=highlightDarkName, + highlightLightName=highlightLightName, + height=self.base_height, + insetDarkName=insetDarkName, + insetLightName=insetLightName, + radius=self.radius) + + @pyqtSlot(int) + def onActionTriggered(self, action: int) -> None: + if action != 7: + if action % 2: + self.setValue(self.sliderRange) + else: + self.setValue(0) + + @pyqtSlot() + def onSliderRelease(self) -> None: + if self.sliderPosition() >= self.sliderMidPoint: + self.setValue(self.sliderRange) + else: + self.setValue(0) + + def on(self) -> bool: + return self.value() == self.sliderRange + + def setOn(self, on: bool=True) -> None: + if on: + self.setValue(self.sliderRange) + else: + self.setValue(0) + + +if __name__ == "__main__": + import sys + app = QApplication(sys.argv) + b = QToggleSwitch() + b.show() + sys.exit(app.exec_())
\ No newline at end of file |