简体   繁体   中英

Customizing the QScrollbar in PyQt

Usually the default QScrollBar's from PyQt are too small for high dpi displays. That's why it can be necessary to adjust them. And once you're doing it anyway, why not take benefit from the aesthetic improvements you can make?

This is how you can tweak the 'look and feel' of your QScrollBar:

########################################
#     My custom QScrollArea            #
#     widget                           #
########################################

class MyScrollArea(QScrollArea):

    def __init__(self, parent):
        super(MyScrollArea, self).__init__(parent)
        ...

        self.setStyleSheet("""QScrollBar:vertical {
                    width: 45px;
                    margin: 45px 0 45px 0;
                    background: #32CC99;
                  }

                  QScrollBar::handle:vertical {
                    border: 10px solid grey;
                    background: white;
                    min-height: 10px;
                  }

                  QScrollBar::add-line:vertical {
                    border: 2px solid grey;
                    background: none;
                    height: 45px;
                    subcontrol-position: bottom;
                    subcontrol-origin: margin;
                  }

                  QScrollBar::sub-line:vertical {
                    border: 2px solid grey;
                    background: none;
                    height: 45px;
                    subcontrol-position: top;
                    subcontrol-origin: margin;
                  }

                  QScrollBar::up-arrow:vertical {
                    border: 5px solid grey;
                    height: 40px; 
                    width: 40px 
                  }

                  QScrollBar::down-arrow:vertical {
                    border: 5px solid grey;
                    height: 40px; 
                    width: 40px                              
                  }""")
        ...

    ### End init ###

### End Class ###

I found the following documentation on how to setup the style sheet:

http://doc.qt.io/qt-4.8/stylesheet-examples.html#customizing-qscrollbar d

THE PROBLEM :

After customizing the QScrollBars, they work perfectly. But the user doesn't get any visual feedback when clicking either the handle or the arrows. Clicking the arrow for example, will not result in the visual feedback that the arrow has been pressed.

Here is an example of how it should be:

在此处输入图片说明

I don't know how to use style sheets achieve it.

I created three qss files.

a.qss

    QScrollBar:vertical{
    background: black;
    width: 10px;
}

b.qss

    QScrollBar:vertical{
    background: black;
    width: 10px;
}


QScrollBar::add-page:vertical{
    background: red;
}

c.qss

    QScrollBar:vertical{
    background: black;
    width: 10px;
}


QScrollBar::sub-page:vertical{
    background: red;
}

and the code:

class Main(QScrollArea):

    def __init__(self):

        super(Main, self).__init__()

        self.resize(300, 200)

        self.index = QWidget()
        self.index.setMinimumHeight(1000)
        self.index.setMinimumWidth(500)


        self.setWidget(self.index)
        self.setWidgetResizable(True)

        with open('a.qss', 'r') as f:
            self.a_text = f.read()
            self.setStyleSheet(self.a_text)
        with open('b.qss', 'r') as f:
            self.b_text = f.read()
        with open('c.qss', 'r') as f:
            self.c_text = f.read()

        # save values.
        self.value = 0
        self.pre_value = 0

        # save pause condition.
        self.pauseCond = True
        self.timer = QTimer()

        self.timer.timeout.connect(self.timerout)
        self.verticalScrollBar().actionTriggered.connect(self.change)
        self.timer.start(300)

    def change(self):
        # if sliding the slider(click and Mouse pulley).

        self.value = self.verticalScrollBar().sliderPosition()

        # if sliding down/right.
        if self.pre_value < self.value:
            self.setStyleSheet(self.b_text)
        # if sliding up/left.
        elif self.pre_value > self.value:
            self.setStyleSheet(self.c_text)

        self.pre_value = self.verticalScrollBar().sliderPosition()
        self.pauseCond = True

    def timerout(self):
        if not self.pauseCond:
            return 1

        # if click or pulley stop.
        if self.verticalScrollBar().sliderPosition() == self.value:
            self.setStyleSheet(self.a_text)
            self.pauseCond = False

I am learning English, I hope you don't mind this.

You can actually use QStyle.hitTestComplexControl() , but it requires you to subclass the scrollbars too. In the following example I fully implemented the vertical scrollbar, you just have to fill in the basic css parts of the horizontal one.

class CustomScrollBar(QtWidgets.QScrollBar):
    def __init__(self, *args, **kwargs):
        QtWidgets.QScrollBar.__init__(self, *args, **kwargs)
        self.baseSheet = '''
            QScrollBar {{
                width: 45px;
                margin: 45px 0 45px 0;
                background: #32CC99;
            }}

            QScrollBar::handle {{
                border: 10px solid grey;
                background: white;
                min-height: 10px;
            }}

            QScrollBar::add-line:vertical {{
                border: 2px solid grey;
                background: none;
                height: 45px;
                subcontrol-position: bottom;
                subcontrol-origin: margin;
            }}

            QScrollBar::sub-line:vertical {{
                border: 2px solid grey;
                background: none;
                height: 45px;
                subcontrol-position: top;
                subcontrol-origin: margin;
            }}

            QScrollBar::up-arrow:vertical {{
                subcontrol-position: bottom;
                subcontrol-origin: margin;
                {upArrow}
            }}

            QScrollBar::down-arrow:vertical {{
                subcontrol-position: bottom;
                subcontrol-origin: margin;
                {downArrow}
            }}

            QScrollBar::left-arrow:vertical {{
                subcontrol-position: bottom;
                subcontrol-origin: margin;
                {leftArrow}
            }}

            QScrollBar::right-arrow:vertical {{
                subcontrol-position: bottom;
                subcontrol-origin: margin;
                {rightArrow}
            }}
            '''
        self.arrowNormal = '''
                border-top: 5px solid lightgray;
                border-left: 5px solid lightgray;
                border-right: 5px solid gray;
                border-bottom: 5px solid gray;
            '''
        self.arrowPressed = '''
                border: 5px solid darkgray;
            '''
        self.setStyleSheet(self.baseSheet.format(
            upArrow=self.arrowNormal, 
            downArrow=self.arrowNormal, 
            leftArrow=self.arrowNormal, 
            rightArrow=self.arrowNormal))

    def mousePressEvent(self, event):
        QtWidgets.QScrollBar.mousePressEvent(self, event)
        opt = QtWidgets.QStyleOptionSlider()
        opt.initFrom(self)

        subControl = self.style().hitTestComplexControl(self.style().CC_ScrollBar, opt, event.pos(), self)
        if subControl == self.style().SC_ScrollBarAddLine:
            if self.orientation() == QtCore.Qt.Vertical:
                downArrow = self.arrowPressed
                upArrow = leftArrow = rightArrow = self.arrowNormal
            else:
                rightArrow = self.arrowPressed
                upArrow = downArrow = leftArrow = self.arrowNormal
        elif subControl == self.style().SC_ScrollBarSubLine:
            if self.orientation() == QtCore.Qt.Vertical:
                upArrow = self.arrowPressed
                downArrow = leftArrow = rightArrow = self.arrowNormal
            else:
                leftArrow = self.arrowPressed
                rightArrow = upArrow = downArrow = self.arrowNormal
        self.setStyleSheet(self.baseSheet.format(upArrow=upArrow, downArrow=downArrow, leftArrow=leftArrow, rightArrow=rightArrow))

    def mouseReleaseEvent(self, event):
        QtWidgets.QScrollBar.mouseReleaseEvent(self, event)
        self.setStyleSheet(self.baseSheet.format(
            upArrow=self.arrowNormal, 
            downArrow=self.arrowNormal, 
            leftArrow=self.arrowNormal, 
            rightArrow=self.arrowNormal))


class MyScrollArea(QtWidgets.QScrollArea):
    def __init__(self, parent=None):
        super(MyScrollArea, self).__init__(parent)
        w = QtWidgets.QWidget()
        w.setFixedSize(640, 480)
        self.setWidget(w)
        vScrollBar = CustomScrollBar(QtCore.Qt.Vertical)
        self.setVerticalScrollBar(vScrollBar)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM