Toggle Switch in Qt

后端 未结 10 2223
臣服心动
臣服心动 2020-12-12 17:31

I am trying to use an element which is the equivalent of Android Switches in Qt. I have found a ToggleSwitch in QML, but nothing in the actual C++ Qt libs. Am I just missing

10条回答
  •  余生分开走
    2020-12-12 18:02

    Here is a Python 3 / PyQt5 implementation of @IMAN4K’s answer.

    Improvements over the original implementation:

    • Thumb-size can be larger/smaller than the track size
    • Use current app’s palette for coloring
    • Emit toggled/clicked signals when clicked
    • Various other fixes

    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()
    

提交回复
热议问题