python qt eric 中如何加入具有两个滑动块的Slider(range slider)

关键词:

rangeSlider、Two sliders、Qt5 Double range slider

参考:

https://github.com/liuleidong/testQxtSpanSlider.git

https://www.jianshu.com/p/f2468da0a199

 

RangeSlider QML Type

https://doc.qt.io/qt-5/qml-qtquick-controls2-rangeslider.html

 

 

https://stackoverflow.com/questions/47342158/porting-range-slider-widget-to-pyqt5

 

 

如何解决问题

解决方案1

首先要有这个py文件:qrangeslider.py

import sys, os
from PyQt5 import QtCore, QtGui, QtWidgets

__all__ = ['QRangeSlider']

DEFAULT_CSS = """
QRangeSlider * {
    border: 0px;
    padding: 0px;
}
QRangeSlider #Head {
    background: #222;
}
QRangeSlider #Span {
    background: #393;
}
QRangeSlider #Span:active {
    background: #282;
}
QRangeSlider #Tail {
    background: #222;
}
QRangeSlider > QSplitter::handle {
    background: #393;
}
QRangeSlider > QSplitter::handle:vertical {
    height: 4px;
}
QRangeSlider > QSplitter::handle:pressed {
    background: #ca5;
}
"""

def scale(val, src, dst):
    return int(((val - src[0]) / float(src[1]-src[0])) * (dst[1]-dst[0]) + dst[0])


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("QRangeSlider")
        Form.resize(300, 30)
        Form.setStyleSheet(DEFAULT_CSS)
        self.gridLayout = QtWidgets.QGridLayout(Form)
        self.gridLayout.setContentsMargins(0, 0, 0, 0)
        self.gridLayout.setSpacing(0)
        self.gridLayout.setObjectName("gridLayout")
        self._splitter = QtWidgets.QSplitter(Form)
        self._splitter.setMinimumSize(QtCore.QSize(0, 0))
        self._splitter.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self._splitter.setOrientation(QtCore.Qt.Horizontal)
        self._splitter.setObjectName("splitter")
        self._head = QtWidgets.QGroupBox(self._splitter)
        self._head.setTitle("")
        self._head.setObjectName("Head")
        self._handle = QtWidgets.QGroupBox(self._splitter)
        self._handle.setTitle("")
        self._handle.setObjectName("Span")
        self._tail = QtWidgets.QGroupBox(self._splitter)
        self._tail.setTitle("")
        self._tail.setObjectName("Tail")
        self.gridLayout.addWidget(self._splitter, 0, 0, 1, 1)
        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("QRangeSlider", "QRangeSlider"))


class Element(QtWidgets.QGroupBox):
    def __init__(self, parent, main):
        super(Element, self).__init__(parent)
        self.main = main

    def setStyleSheet(self, style):
        self.parent().setStyleSheet(style)

    def textColor(self):
        return getattr(self, '__textColor', QtGui.QColor(125, 125, 125))

    def setTextColor(self, color):
        if type(color) == tuple and len(color) == 3:
            color = QtGui.QColor(color[0], color[1], color[2])
        elif type(color) == int:
            color = QtGui.QColor(color, color, color)
        setattr(self, '__textColor', color)

    def paintEvent(self, event):
        qp = QtGui.QPainter()
        qp.begin(self)
        if self.main.drawValues():
            self.drawText(event, qp)
        qp.end()


class Head(Element):
    def __init__(self, parent, main):
        super(Head, self).__init__(parent, main)

    def drawText(self, event, qp):
        qp.setPen(self.textColor())
        qp.setFont(QtGui.QFont('Arial', 10))
        qp.drawText(event.rect(), QtCore.Qt.AlignLeft, str(self.main.min()))


class Tail(Element):
    def __init__(self, parent, main):
        super(Tail, self).__init__(parent, main)

    def drawText(self, event, qp):
        qp.setPen(self.textColor())
        qp.setFont(QtGui.QFont('Arial', 10))
        qp.drawText(event.rect(), QtCore.Qt.AlignRight, str(self.main.max()))


class Handle(Element):
    def __init__(self, parent, main):
        super(Handle, self).__init__(parent, main)

    def drawText(self, event, qp):
        qp.setPen(self.textColor())
        qp.setFont(QtGui.QFont('Arial', 10))
        qp.drawText(event.rect(), QtCore.Qt.AlignLeft, str(self.main.start()))
        qp.drawText(event.rect(), QtCore.Qt.AlignRight, str(self.main.end()))

    def mouseMoveEvent(self, event):
        event.accept()
        mx = event.globalX()
        _mx = getattr(self, '__mx', None)
        if not _mx:
            setattr(self, '__mx', mx)
            dx = 0
        else:
            dx = mx - _mx
        setattr(self, '__mx', mx)
        if dx == 0:
            event.ignore()
            return
        elif dx > 0:
            dx = 1
        elif dx < 0:
            dx = -1
        s = self.main.start() + dx
        e = self.main.end() + dx
        if s >= self.main.min() and e <= self.main.max():
            self.main.setRange(s, e)


class QRangeSlider(QtWidgets.QWidget, Ui_Form):
    endValueChanged = QtCore.pyqtSignal(int)
    maxValueChanged = QtCore.pyqtSignal(int)
    minValueChanged = QtCore.pyqtSignal(int)
    startValueChanged = QtCore.pyqtSignal(int)
    minValueChanged = QtCore.pyqtSignal(int)
    maxValueChanged = QtCore.pyqtSignal(int)
    startValueChanged = QtCore.pyqtSignal(int)
    endValueChanged = QtCore.pyqtSignal(int)

    _SPLIT_START = 1
    _SPLIT_END = 2

    def __init__(self, parent=None):
        super(QRangeSlider, self).__init__(parent)
        self.setupUi(self)
        self.setMouseTracking(False)
        self._splitter.splitterMoved.connect(self._handleMoveSplitter)
        self._head_layout = QtWidgets.QHBoxLayout()
        self._head_layout.setSpacing(0)
        self._head_layout.setContentsMargins(0, 0, 0, 0)
        self._head.setLayout(self._head_layout)
        self.head = Head(self._head, main=self)
        self._head_layout.addWidget(self.head)
        self._handle_layout = QtWidgets.QHBoxLayout()
        self._handle_layout.setSpacing(0)
        self._handle_layout.setContentsMargins(0, 0, 0, 0)
        self._handle.setLayout(self._handle_layout)
        self.handle = Handle(self._handle, main=self)
        self.handle.setTextColor((150, 255, 150))
        self._handle_layout.addWidget(self.handle)
        self._tail_layout = QtWidgets.QHBoxLayout()
        self._tail_layout.setSpacing(0)
        self._tail_layout.setContentsMargins(0, 0, 0, 0)
        self._tail.setLayout(self._tail_layout)
        self.tail = Tail(self._tail, main=self)
        self._tail_layout.addWidget(self.tail)
        self.setMin(0)
        self.setMax(99)
        self.setStart(0)
        self.setEnd(99)
        self.setDrawValues(True)

    def min(self):
        return getattr(self, '__min', None)

    def max(self):
        return getattr(self, '__max', None)

    def setMin(self, value):
        setattr(self, '__min', value)
        self.minValueChanged.emit(value)

    def setMax(self, value):
        setattr(self, '__max', value)
        self.maxValueChanged.emit(value)

    def start(self):
        return getattr(self, '__start', None)

    def end(self):
        return getattr(self, '__end', None)

    def _setStart(self, value):
        setattr(self, '__start', value)
        self.startValueChanged.emit(value)

    def setStart(self, value):
        v = self._valueToPos(value)
        self._splitter.splitterMoved.disconnect()
        self._splitter.moveSplitter(v, self._SPLIT_START)
        self._splitter.splitterMoved.connect(self._handleMoveSplitter)
        self._setStart(value)

    def _setEnd(self, value):
        setattr(self, '__end', value)
        self.endValueChanged.emit(value)

    def setEnd(self, value):
        v = self._valueToPos(value)
        self._splitter.splitterMoved.disconnect()
        self._splitter.moveSplitter(v, self._SPLIT_END)
        self._splitter.splitterMoved.connect(self._handleMoveSplitter)
        self._setEnd(value)

    def drawValues(self):
        return getattr(self, '__drawValues', None)

    def setDrawValues(self, draw):
        setattr(self, '__drawValues', draw)

    def getRange(self):
        return (self.start(), self.end())

    def setRange(self, start, end):
        self.setStart(start)
        self.setEnd(end)

    def keyPressEvent(self, event):
        key = event.key()
        if key == QtCore.Qt.Key_Left:
            s = self.start()-1
            e = self.end()-1
        elif key == QtCore.Qt.Key_Right:
            s = self.start()+1
            e = self.end()+1
        else:
            event.ignore()
            return
        event.accept()
        if s >= self.min() and e <= self.max():
            self.setRange(s, e)

    def setBackgroundStyle(self, style):
        self._tail.setStyleSheet(style)
        self._head.setStyleSheet(style)

    def setSpanStyle(self, style):
        self._handle.setStyleSheet(style)

    def _valueToPos(self, value):
        return scale(value, (self.min(), self.max()), (0, self.width()))

    def _posToValue(self, xpos):
        return scale(xpos, (0, self.width()), (self.min(), self.max()))

    def _handleMoveSplitter(self, xpos, index):
        hw = self._splitter.handleWidth()
        def _lockWidth(widget):
            width = widget.size().width()
            widget.setMinimumWidth(width)
            widget.setMaximumWidth(width)
        def _unlockWidth(widget):
            widget.setMinimumWidth(0)
            widget.setMaximumWidth(16777215)
        v = self._posToValue(xpos)
        if index == self._SPLIT_START:
            _lockWidth(self._tail)
            if v >= self.end():
                return
            offset = -20
            w = xpos + offset
            self._setStart(v)
        elif index == self._SPLIT_END:
            _lockWidth(self._head)
            if v <= self.start():
                return
            offset = -40
            w = self.width() - xpos + offset
            self._setEnd(v)
        _unlockWidth(self._tail)
        _unlockWidth(self._head)
        _unlockWidth(self._handle)

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    rs = QRangeSlider()
    rs.show()
    rs.setRange(15, 35)
    rs.setBackgroundStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #222, stop:1 #333);')
    rs.handle.setStyleSheet('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #282, stop:1 #393);')
    app.exec_()

然后使用(这一点还不是特别清晰)

from qrangeslider import *

Eric生成的如下
self.Slider1 = QtWidgets.QSlider(self.frame_2)
修改成
self.Slider1 = QRangeSlider(self.frame_2)

效果:

方案2

参考文档:https://pypi.org/project/qrangeslider/

但是需要在pyqt4下运行

 

方案3

参考

https://www.e-learn.cn/en/node/2282182

from PyQt5 import QtCore, QtGui, QtWidgets
import sys

MAXVAL = 650000

class RangeSliderClass(QtWidgets.QWidget):

    def __init__(self):
        super().__init__()

        self.minTime = 0
        self.maxTime = 0
        self.minRangeTime = 0
        self.maxRangeTime = 0
        self.middleTime = self.getMiddleTime()
        self.halfTimeInterval = self.middleTime - self.minTime

        self.sliderMin = MAXVAL
        self.sliderMax = MAXVAL

        self.setupUi(self)

    def setupUi(self, RangeSlider):
        RangeSlider.setObjectName("RangeSlider")
        RangeSlider.resize(631, 65)
        RangeSlider.setMaximumSize(QtCore.QSize(16777215, 65))
        self.RangeBarVLayout = QtWidgets.QVBoxLayout(RangeSlider)
        self.RangeBarVLayout.setContentsMargins(5, 0, 5, 0)
        self.RangeBarVLayout.setSpacing(0)
        self.RangeBarVLayout.setObjectName("RangeBarVLayout")
        self.datesFrame = QtWidgets.QFrame(RangeSlider)
        self.datesFrame.setMaximumSize(QtCore.QSize(16777215, 28))
        self.datesFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.datesFrame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.datesFrame.setObjectName("datesFrame")
        self.datesHLayout = QtWidgets.QHBoxLayout(self.datesFrame)
        self.datesHLayout.setContentsMargins(5, 2, 5, 2)
        self.datesHLayout.setObjectName("datesHLayout")

        ## startTime Calendar Widget
        self.startTime = QtWidgets.QDateTimeEdit(self.datesFrame)
        self.startTime.setMinimumSize(QtCore.QSize(183, 0))
        self.startTime.setMaximumSize(QtCore.QSize(185, 24))

        self.startTime.setDate(QtCore.QDate.currentDate().addDays(-1))

        self.startTime.setMaximumDateTime(QtCore.QDateTime(QtCore.QDate(2999, 12, 31), QtCore.QTime(23, 59, 59)))
        self.startTime.setMaximumDate(QtCore.QDate(2999, 12, 31))
        self.startTime.setMinimumDate(QtCore.QDate(2000, 1, 1))
        self.startTime.setCalendarPopup(True)
        self.startTime.setObjectName("startTime")
        self.startTime.dateChanged.connect(self.startDateChangeHandler)
        self.datesHLayout.addWidget(self.startTime)

        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.datesHLayout.addItem(spacerItem)

        ## entTime Calendar Widget
        self.endTime = QtWidgets.QDateTimeEdit(self.datesFrame)
        self.endTime.setMinimumSize(QtCore.QSize(183, 0))
        self.endTime.setMaximumSize(QtCore.QSize(185, 24))
        self.endTime.setDate(QtCore.QDate.currentDate())
        self.endTime.setMaximumDate(QtCore.QDate(2999, 12, 31))
        self.endTime.setMinimumDate(QtCore.QDate(2000, 1, 1))
        self.endTime.setCalendarPopup(True)
        self.endTime.setObjectName("endTime")
        self.endTime.dateChanged.connect(self.endDateChangeHandler)
        self.datesHLayout.addWidget(self.endTime)

        ## Init Time
        self.minTime = self.startTime.dateTime().toTime_t()
        self.maxTime = self.endTime.dateTime().toTime_t()
        self.minRangeTime = self.minTime
        self.maxRangeTime = self.maxTime
        self.middleTime = self.getMiddleTime()
        self.halfTimeInterval = self.middleTime - self.minTime

        self.RangeBarVLayout.addWidget(self.datesFrame)
        self.slidersFrame = QtWidgets.QFrame(RangeSlider)
        self.slidersFrame.setMaximumSize(QtCore.QSize(16777215, 25))
        self.slidersFrame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.slidersFrame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.slidersFrame.setObjectName("slidersFrame")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.slidersFrame)
        self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
        self.horizontalLayout.setContentsMargins(5, 2, 5, 2)
        self.horizontalLayout.setSpacing(0)
        self.horizontalLayout.setObjectName("horizontalLayout")

        ## Start Slider Widget
        self.startSlider = QtWidgets.QSlider(self.slidersFrame)
        self.startSlider.setMaximum(self.sliderMin)
        self.startSlider.setMinimumSize(QtCore.QSize(100, 5))
        self.startSlider.setMaximumSize(QtCore.QSize(16777215, 10))
        font = QtGui.QFont()
        font.setKerning(True)
        self.startSlider.setFont(font)
        self.startSlider.setAcceptDrops(False)
        self.startSlider.setAutoFillBackground(False)
        self.startSlider.setOrientation(QtCore.Qt.Horizontal)
        self.startSlider.setInvertedAppearance(True)
        self.startSlider.setObjectName("startSlider")
        self.startSlider.setValue(MAXVAL)
        self.startSlider.sliderReleased.connect(self.startSliderHandler)
        self.horizontalLayout.addWidget(self.startSlider)

        ## End Slider Widget
        self.endSlider = QtWidgets.QSlider(self.slidersFrame)
        self.endSlider.setMaximum(MAXVAL)
        self.endSlider.setMinimumSize(QtCore.QSize(100, 5))
        self.endSlider.setMaximumSize(QtCore.QSize(16777215, 10))
        self.endSlider.setTracking(True)
        self.endSlider.setOrientation(QtCore.Qt.Horizontal)
        self.endSlider.setObjectName("endSlider")
        self.endSlider.setValue(self.sliderMax)
        self.endSlider.sliderReleased.connect(self.endSliderHandler)
        self.horizontalLayout.addWidget(self.endSlider)

        self.RangeBarVLayout.addWidget(self.slidersFrame)

        self.retranslateUi(RangeSlider)
        QtCore.QMetaObject.connectSlotsByName(RangeSlider)

        self.show()

    def getMiddleTime(self, maxTime = None, minTime = None):
        if minTime == None :
            minTime = self.minRangeTime
        if maxTime == None :
            maxTime = self.maxRangeTime
        return (minTime + maxTime)/2

    def getRangeTime(self):
        return self.minRangeTime, self.maxRangeTime

    def startSliderHandler(self):
        self.sliderMin = self.startSlider.value()

        self.minRangeTime = int(self.middleTime - self.halfTimeInterval * self.sliderMin / MAXVAL)
        #print("\n\nNew Min Time Range : ", self.minRangeTime, " Min : ",  self.minTime, "Minddle : ", self.middleTime)

    def endSliderHandler(self):
        self.sliderMax = self.endSlider.value()

        self.maxRangeTime = int(self.middleTime + self.halfTimeInterval * self.sliderMax / MAXVAL)
        print("\n\nNew Min Time Range : ", self.maxRangeTime, " Max : ",  self.maxTime, "Minddle : ", self.middleTime)

    def startDateChangeHandler(self):
        self.minTime = self.startTime.dateTime().toTime_t()
        #print("MinTime range : ", self.minTime)

    def endDateChangeHandler(self):
        self.maxTime = self.endTime.dateTime().toTime_t()
        #print("MaxTime range : ", self.maxTime)

    def retranslateUi(self, RangeSlider):
        _translate = QtCore.QCoreApplication.translate
        RangeSlider.setWindowTitle(_translate("RangeSlider", "Time interval"))
        self.startTime.setDisplayFormat(_translate("RangeSlider", "dd/MM/yyyy  HH:mm:ss .zz"))
        self.endTime.setDisplayFormat(_translate("RangeSlider", "dd/MM/yyyy  HH:mm:ss .zz"))

app = QtWidgets.QApplication(sys.argv)
awindow = RangeSliderClass()
sys.exit(app.exec_())

 

posted @ 2019-08-05 20:11  bH1pJ  阅读(76)  评论(0编辑  收藏  举报