是否可以扩展 QSlider 周围的可绘制区域

Is it possible to expand the drawable area around the QSlider

我的目标是在 Python[ 中有一个带有 刻度线 和刻度标签的自定义 QSlider 3 使用 PySide2 模块。为此,我在派生的 class 中编辑 QSlider class 的默认 paintEvent。然而,事实证明可打印区域是有限的,我放置的 top/bottom 标签被裁剪了(见截图)。我用来生成这些滑块的代码如下:

import sys
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *

slider_x = 150
slider_y = 450
slider_step = [0.01, 0.1, 1, 10, 100]  # in microns


class MySlider(QSlider):
    def __init__(self, type, parent=None):
        super(MySlider, self).__init__(parent)
        self.Type = type

    def paintEvent(self, event):
        super(MySlider, self).paintEvent(event)
        qp = QPainter(self)
        pen = QPen()
        pen.setWidth(2)
        pen.setColor(Qt.red)

        qp.setPen(pen)
        font = QFont('Times', 10)
        qp.setFont(font)
        self.setContentsMargins(50, 50, 50, 50)
        # size = self.size()
        # print(size)
        # print("margins", self.getContentsMargins())
        # print(self.getContentsMargins())
        # print(self.contentsRect())
        contents = self.contentsRect()
        self.setFixedSize(QSize(slider_x, slider_y))
        max_slider = self.maximum()
        y_inc = 0
        for i in range(max_slider):
            qp.drawText(contents.x() - font.pointSize(), y_inc + font.pointSize() / 2, '{0:2}'.format(slider_step[i]))
            qp.drawLine(contents.x() + font.pointSize(), y_inc, contents.x() + contents.width(), y_inc)
            y_inc += slider_y/4


class Window(QWidget):
    """ Inherits from QWidget """
    def __init__(self):
        super().__init__()
        self.title = 'Control Stages'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.AxesMapping = [0, 1, 2, 3]
        self.initUI()

    def initUI(self):
        """ Initializes the GUI either using the grid layout or the absolute position layout"""
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        Comp4 = self.createSlider("step_size")
        Comp5 = self.createSlider("speed")
        windowLayout = QGridLayout()
        windowLayout.setContentsMargins(50, 50, 50, 50)
        HGroupBox = QGroupBox()
        layout = QGridLayout()
        layout.addWidget(Comp4, 0, 0)
        layout.addWidget(Comp5, 0, 1)
        HGroupBox.setLayout(layout)
        HGroupBox.setFixedSize(QSize(740, 480))
        windowLayout.addWidget(HGroupBox, 0, 0)
        self.setLayout(windowLayout)
        self.show()

    def createSlider(self, variant):
        Slider = MySlider(Qt.Vertical)
        Slider.Type = variant
        Slider.setMaximum(5)
        Slider.setMinimum(1)
        Slider.setSingleStep(1)
        Slider.setTickInterval(1)
        Slider.valueChanged.connect(lambda: self.sliderChanged(Slider))
        return Slider

    @staticmethod
    def sliderChanged(Slider):
        print("Slider value changed to ", Slider.value(), "slider type is ", Slider.Type)
        if Slider.Type == "step_size":
            print("this is a step size slider")
        elif Slider.Type == "speed":
            print("this is a speed slider")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Window()
    sys.exit(app.exec_())

是否可以扩大 QSlider 周围的可绘制区域?如果可以,我该如何实现这种效果?您可以在屏幕截图中看到第一个和最后一个刻度线旁边的红色标签没有正确显示并且它们被裁剪了(即在第一个刻度标签中,标签 0.01 缺少 1 和 0 的顶部)。

编辑: 在尝试建议的解决方案后,顶部标签的一部分仍然被剪掉了。下面的第二个版本在 Windows 10 64 位 PySide2 5.12.0 和 Python 3.6.6.

上仍然相似

EDIT2 我有一个双引导系统,所以我在 Ubuntu 16.04.3 LTS 和 Python 3.5.2 / PySide 5.12 上尝试了它。 0,开箱即用。这是那里的屏幕截图,但不幸的是它必须在 Windows.

上工作

如果我做对了,您希望刻度线在顶部和底部可见,因为它们似乎是 "cut" 由于页边距。 我认为这是假设小部件边距正确设置为零或类似值的结果,您可以尝试更改数字并查看此效果。 (我尝试移动边距但没有成功)

现在,在 old post 之后,我注意到实现类似的事情似乎很棘手,您可以根据基于 minimummaximum 的公式计算报价QSlider.

由于底部的勾号将在主小部件下方,您只需添加一个特殊条件即可使其可见。

不想粘贴整个代码,但您只需要在循环之前声明 opthandle。计算里面的y位置,用它代替你的y_inc.

def paintEvent(self, event):                                                                                                                                                            
    super(MySlider, self).paintEvent(event)                                                     
    qp = QPainter(self)                                                                         
    pen = QPen()                                                                                
    pen.setWidth(2)                                                                             
    pen.setColor(Qt.red)                                                                        

    qp.setPen(pen)                                                                              
    font = QFont('Times', 10)                                                                   
    qp.setFont(font)                                                                            
    self.setContentsMargins(50, 50, 50, 50)                                                     
    # size = self.size()                                                                        
    # print(size)                                                                               
    # print("margins", self.getContentsMargins())                                               
    # print(self.getContentsMargins())                                                          
    # print(self.contentsRect())                                                                
    contents = self.contentsRect()                                                              
    self.setFixedSize(QSize(slider_x, slider_y))                                                

    # New code                                                                                  
    max_slider = self.maximum()                                                                 
    min_slider = self.minimum()                                                                 
    len_slider = max_slider - min_slider                                                        
    height = self.height()                                                                      

    opt = QStyleOptionSlider()                                                                  
    handle = self.style().subControlRect(QStyle.CC_Slider, opt, QStyle.SC_SliderHandle, self)   
    handle_height = handle.height() # Add +10 on windows (workarount)                                                            
    height_diff = height - handle_height                                                        
    point_size = font.pointSize()                                                               

    for i in range(max_slider):                                                                 
        y = round(((1 - i / len_slider) * height_diff + (handle_height / 2.0))) - 1             

        # Trick for the tick at the bottom                                                      
        if i == 0:                                                                              
            y = self.height() - handle_height/2.0 # To have the tick in the middle              
            #y = height_diff # to shift it a bit                                                

        qp.drawText(contents.x() - point_size, y + point_size / 2, '{0:2}'.format(slider_step[len_slider - i]))
        qp.drawLine(contents.x() + point_size, y, contents.x() + contents.width(), y)  

编辑: windows 上似乎有一个被忽视的大约 10 个单位的顶部边距,恕我直言,这是 Qt 中的一个错误。解决方法是替换:

handle_height = handle.height()

handle_height = handle.height() + 10

paintEvent 方法中。

我的建议是您调查 QSlider 继承的 QAbstractSlider。它包含一个名为 triggerAction (SliderToMaximum value is 6) which can be used to set the Slider Position when the action is triggered 的方法。

触发动作的语法是

 QAbstractSlider.triggerAction (self, SliderAction action)

SliderAction 的值如下:

QAbstractSlider.SliderNoAction 0 QAbstractSlider.SliderSingleStepAdd 1

QAbstractSlider.SliderSingleStepSub 2

QAbstractSlider.SliderPageStepAdd 3

QAbstractSlider.SliderPageStepSub 4

QAbstractSlider.SliderToMinimum 5

QAbstractSlider.SliderToMaximum 6

QAbstractSlider.SliderMove 7

(source)

使用PySide2,您可以使用

调用这些方法
PySide2.QtWidgets.QAbstractSlider.maximum()  //returns the value of the maximum

PySide2.QtWidgets.QAbstractSlider.setMaximum(arg) // pass your own value

折腾多了终于找到了解决办法。它包括样式表,我之前已经尝试过。但是,当时我无法正确实施它。 II 保持小部件大小不变,但减少了凹槽长度,以便能够将标签放入可打印区域内。完整代码如下:

import sys
from PySide2.QtCore import *
from PySide2.QtWidgets import *
from PySide2.QtGui import *

slider_x = 150
slider_y = 450
slider_step = [0.01, 0.1, 1, 10, 100]  # in microns
groove_y = 400
handle_height = 10


class MySlider(QSlider):
    def __init__(self, type, parent=None):
        # super(MySlider, self).__init__(parent)
        super().__init__()
        self.parent = parent
        self.Type = type
        # self.setFixedHeight(115)
        self.setStyleSheet("""QSlider::groove:vertical {
    border: 1px solid black;
    height: """ + str(groove_y) + """ px;
    width: 10px;
    border-radius: 2px;
    }

    QSlider::handle:vertical {
        background: red;
        border: 1px solid red;
        height: """ + str(handle_height) + """ px;
        margin: 2px 0;
        border-radius: 1px;
    }

    QSlider::add-page:vertical {
        background: blue;
    }
    QSlider::sub-page:vertical {
        background: red;
""")

    def paintEvent(self, event):
        super(MySlider, self).paintEvent(event)
        qp = QPainter(self)
        pen = QPen()
        pen.setWidth(2)
        pen.setColor(Qt.black)

        qp.setPen(pen)
        font = QFont('Times', 10)
        qp.setFont(font)
        self.setContentsMargins(50, 50, 50, 50)
        self.setFixedSize(QSize(slider_x, slider_y))
        contents = self.contentsRect()
        max = self.maximum()
        min = self.minimum()

        y_inc = slider_y - (slider_y - groove_y) / 2
        for i in range(len(slider_step)):
            qp.drawText(contents.x() - 2 * font.pointSize(), y_inc + font.pointSize() / 2, '{0:3}'.format(slider_step[i]))
            qp.drawLine(contents.x() + 2 * font.pointSize(), y_inc, contents.x() + contents.width(), y_inc)
            y_inc -= groove_y / (max - min)



class Window(QWidget):
    """ Inherits from QWidget """
    def __init__(self):
        super().__init__()
        self.title = 'Control Stages'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 100
        self.AxesMapping = [0, 1, 2, 3]
        self.initUI()

    def initUI(self):
        """ Initializes the GUI either using the grid layout or the absolute position layout"""
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)
        Comp4 = self.createSlider("step_size")
        Comp5 = self.createSlider("speed")
        windowLayout = QGridLayout()
        windowLayout.setContentsMargins(50, 50, 50, 50)
        HGroupBox = QGroupBox()
        layout = QGridLayout()
        layout.addWidget(Comp4, 0, 0)
        layout.addWidget(Comp5, 0, 1)
        HGroupBox.setLayout(layout)
        HGroupBox.setFixedSize(QSize(740, 480))
        windowLayout.addWidget(HGroupBox, 0, 0)
        self.setLayout(windowLayout)
        self.show()

    def createSlider(self, variant):
        Slider = MySlider(Qt.Vertical)
        Slider.Type = variant
        Slider.setMaximum(len(slider_step))
        Slider.setMinimum(1)
        Slider.setSingleStep(1)
        Slider.setTickInterval(1)
        Slider.valueChanged.connect(lambda: self.sliderChanged(Slider))
        return Slider

    @staticmethod
    def sliderChanged(Slider):
        print("Slider value changed to ", Slider.value(), "slider type is ", Slider.Type)
        if Slider.Type == "step_size":
            print("this is a step size slider")
        elif Slider.Type == "speed":
            print("this is a speed slider")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Window()
    sys.exit(app.exec_())