Mein Ziel ist es, einen benutzerdefinierten QSlider mit Häkchen und Häkchenbezeichnungen in Python 3 mit dem Modul PySide2 zu haben. Dazu bearbeite ich das Standard-paintEvent der QSlider-Klasse in einer abgeleiteten Klasse. Es stellt sich jedoch heraus, dass der druckbare Bereich begrenzt ist und die von mir platzierten oberen / unteren Etiketten beschnitten sind (siehe Screenshot). Der Code, den ich zum Generieren dieser Schieberegler verwende, lautet wie folgt:

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

Ist es möglich, den Zeichenbereich um den QSlider zu erweitern, und wenn ja, wie kann ich diesen Effekt erzielen? Sie können auf dem Screenshot sehen, dass die roten Beschriftungen neben dem ersten und dem letzten Häkchen nicht richtig angezeigt werden und zugeschnitten sind (d. H. Im ersten Häkchen fehlt die Beschriftung von 1 und 0 für das Etikett 0,01).

enter image description here

BEARBEITEN: Nach dem Versuch der vorgeschlagenen Lösung wird immer noch ein Teil des oberen Etiketts abgeschnitten. Die zweite Version unten ist unter Windows 10 64-Bit mit PySide2 5.12.0 und Python 3.6.6 immer noch ähnlich.

EDIT2 Ich habe ein Dual-Boot-System, also habe ich es unter Ubuntu 16.04.3 LTS mit Python 3.5.2 / PySide 5.12.0 ausprobiert und es hat sofort funktioniert. Hier ist ein Screenshot von dort, aber leider muss es unter Windows funktionieren.

enter image description here

5
Vesnog 18 Jän. 2019 im 01:09

3 Antworten

Beste Antwort

Nachdem ich mehr herumgespielt hatte, fand ich endlich eine Lösung. Es enthält Styling-Blätter und ich hatte es schon einmal versucht. Damals konnte ich es jedoch nicht richtig umsetzen. II hielt die Widgetgröße konstant, verringerte jedoch die Rillenlänge, um die Etiketten in den druckbaren Bereich einpassen zu können. Der vollständige Code ist unten:

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_())
2
Vesnog 4 Feb. 2019 im 00:24

Meine Empfehlung wäre, dass Sie QAbstractSlider untersuchen, das von QSlider geerbt wird. Es enthält eine Methode namens triggerAction (SliderToMaximum value is 6), die dies kann verwendet werden, um die Slider-Position festzulegen, wenn Aktion wird ausgelöst.

Die Syntax zum Auslösen einer Aktion lautet

 QAbstractSlider.triggerAction (self, SliderAction action)

Die Werte für SliderAction lauten wie folgt:

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)

Verwenden von PySide2 können Sie diese Methoden mit aufrufen

PySide2.QtWidgets.QAbstractSlider.maximum()  //returns the value of the maximum

Oder

PySide2.QtWidgets.QAbstractSlider.setMaximum(arg) // pass your own value
4
Rachel Gallen 31 Jän. 2019 im 10:30

Wenn ich es richtig verstanden habe, möchten Sie, dass die Zecken oben und unten sichtbar sind, da sie aufgrund der Ränder "geschnitten" zu sein schienen. Ich denke, das ist das Ergebnis der Annahme, dass die Widgets-Ränder richtig auf Null oder ähnliches gesetzt sind. Sie können mit sich ändernden Zahlen herumspielen und diese Effekte sehen. (Ich habe versucht, Ränder zu verschieben und war nicht erfolgreich)

Nach einem alten Beitrag bemerkte ich nun, dass es ähnlich zu sein scheint, etwas Ähnliches zu erreichen schwierig, und Sie können Ihre Zecken auf einer Formel basieren, die auf minimum und maximum von QSlider basiert.

Da sich das Häkchen unten unter dem Haupt-Widget befindet, können Sie einfach eine Sonderbedingung hinzufügen, damit es sichtbar wird.

Ich wollte nicht den gesamten Code einfügen, sondern muss nur opt und handle vor Ihrer Schleife deklarieren. Berechnen Sie die Position y im Inneren und verwenden Sie sie anstelle Ihrer 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)  

enter image description here

Bearbeiten: Unter Windows scheint es einen übersehenen oberen Rand von etwa 10 Einheiten zu geben, und das ist meiner Meinung nach ein Fehler in Qt. Eine Problemumgehung besteht darin, Folgendes zu ersetzen:

handle_height = handle.height()

Mit

handle_height = handle.height() + 10

Innerhalb der paintEvent Methode.

3
cmaureir 31 Jän. 2019 im 08:07