Created
August 21, 2019 14:58
-
-
Save smallsong/1480bdac39ad456dfa1fcaaa8bcf1a72 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from PyQt5.QtCore import QPropertyAnimation, QRectF, QSize, Qt, pyqtProperty | |
from PyQt5.QtGui import QPainter | |
from PyQt5.QtWidgets import ( | |
QAbstractButton, | |
QApplication, | |
QHBoxLayout, | |
QSizePolicy, | |
QWidget, | |
) | |
class Switch(QAbstractButton): | |
def __init__(self, parent=None, track_radius=10, thumb_radius=8): | |
super().__init__(parent=parent) | |
self.setCheckable(True) | |
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) | |
self._track_radius = track_radius | |
self._thumb_radius = thumb_radius | |
self._margin = max(0, self._thumb_radius - self._track_radius) | |
self._base_offset = max(self._thumb_radius, self._track_radius) | |
self._end_offset = { | |
True: lambda: self.width() - self._base_offset, | |
False: lambda: self._base_offset, | |
} | |
self._offset = self._base_offset | |
palette = self.palette() | |
if self._thumb_radius > self._track_radius: | |
self._track_color = { | |
True: palette.highlight(), | |
False: palette.dark(), | |
} | |
self._thumb_color = { | |
True: palette.highlight(), | |
False: palette.light(), | |
} | |
self._text_color = { | |
True: palette.highlightedText().color(), | |
False: palette.dark().color(), | |
} | |
self._thumb_text = { | |
True: '', | |
False: '', | |
} | |
self._track_opacity = 0.5 | |
else: | |
self._thumb_color = { | |
True: palette.highlightedText(), | |
False: palette.light(), | |
} | |
self._track_color = { | |
True: palette.highlight(), | |
False: palette.dark(), | |
} | |
self._text_color = { | |
True: palette.highlight().color(), | |
False: palette.dark().color(), | |
} | |
self._thumb_text = { | |
True: '√', | |
False: '✕', | |
} | |
self._track_opacity = 1 | |
@pyqtProperty(int) | |
def offset(self): | |
return self._offset | |
@offset.setter | |
def offset(self, value): | |
self._offset = value | |
self.update() | |
def sizeHint(self): # pylint: disable=invalid-name | |
return QSize( | |
4 * self._track_radius + 2 * self._margin, | |
2 * self._track_radius + 2 * self._margin, | |
) | |
def setChecked(self, checked): | |
super().setChecked(checked) | |
self.offset = self._end_offset[checked]() | |
def resizeEvent(self, event): | |
super().resizeEvent(event) | |
self.offset = self._end_offset[self.isChecked()]() | |
def paintEvent(self, event): # pylint: disable=invalid-name, unused-argument | |
p = QPainter(self) | |
p.setRenderHint(QPainter.Antialiasing, True) | |
p.setPen(Qt.NoPen) | |
track_opacity = self._track_opacity | |
thumb_opacity = 1.0 | |
text_opacity = 1.0 | |
if self.isEnabled(): | |
track_brush = self._track_color[self.isChecked()] | |
thumb_brush = self._thumb_color[self.isChecked()] | |
text_color = self._text_color[self.isChecked()] | |
else: | |
track_opacity *= 0.8 | |
track_brush = self.palette().shadow() | |
thumb_brush = self.palette().mid() | |
text_color = self.palette().shadow().color() | |
p.setBrush(track_brush) | |
p.setOpacity(track_opacity) | |
p.drawRoundedRect( | |
self._margin, | |
self._margin, | |
self.width() - 2 * self._margin, | |
self.height() - 2 * self._margin, | |
self._track_radius, | |
self._track_radius, | |
) | |
p.setBrush(thumb_brush) | |
p.setOpacity(thumb_opacity) | |
p.drawEllipse( | |
self.offset - self._thumb_radius, | |
self._base_offset - self._thumb_radius, | |
2 * self._thumb_radius, | |
2 * self._thumb_radius, | |
) | |
p.setPen(text_color) | |
p.setOpacity(text_opacity) | |
font = p.font() | |
font.setPixelSize(1.5 * self._thumb_radius) | |
p.setFont(font) | |
p.drawText( | |
QRectF( | |
self.offset - self._thumb_radius, | |
self._base_offset - self._thumb_radius, | |
2 * self._thumb_radius, | |
2 * self._thumb_radius, | |
), | |
Qt.AlignCenter, | |
self._thumb_text[self.isChecked()], | |
) | |
def mouseReleaseEvent(self, event): # pylint: disable=invalid-name | |
super().mouseReleaseEvent(event) | |
if event.button() == Qt.LeftButton: | |
anim = QPropertyAnimation(self, b'offset', self) | |
anim.setDuration(120) | |
anim.setStartValue(self.offset) | |
anim.setEndValue(self._end_offset[self.isChecked()]()) | |
anim.start() | |
def enterEvent(self, event): # pylint: disable=invalid-name | |
self.setCursor(Qt.PointingHandCursor) | |
super().enterEvent(event) | |
def main(): | |
app = QApplication([]) | |
# Thumb size < track size (Gitlab style) | |
s1 = Switch() | |
s1.toggled.connect(lambda c: print('toggled', c)) | |
s1.clicked.connect(lambda c: print('clicked', c)) | |
s1.pressed.connect(lambda: print('pressed')) | |
s1.released.connect(lambda: print('released')) | |
s2 = Switch() | |
s2.setEnabled(False) | |
# Thumb size > track size (Android style) | |
s3 = Switch(thumb_radius=11, track_radius=8) | |
s4 = Switch(thumb_radius=11, track_radius=8) | |
s4.setEnabled(False) | |
l = QHBoxLayout() | |
l.addWidget(s1) | |
l.addWidget(s2) | |
l.addWidget(s3) | |
l.addWidget(s4) | |
w = QWidget() | |
w.setLayout(l) | |
w.show() | |
app.exec() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
hi. This is a really good app. However, I want to display text in the switch background. Could you please suggest the changes required? Thanks
