如何在PySide2上为QSlider创建“标记”?

2024-06-30 15:08:43 发布

您现在位置:Python中文网/ 问答频道 /正文

这张图片很好地展示了我要模仿的东西

目标是创建项目或小部件,如上面的示例所示,用户可以通过MouseDoubleClicked事件在QSlider上创建这些项目或小部件,这些项目或小部件将保持在最初创建的勾号位置(它将保持不动)。 我已经尝试过使用QLabels和Pixmaps或者QGraphicsSitems和QGraphicsView的组合,但都没有成功

尽管如此,我还是觉得我很可能把事情复杂化了,而且可能有一种更简单的方法来实现这一点

你将如何制作这些“标记”

编辑:我已经尽力编辑了我以前的一次尝试,以使其成为一个最小的可复制示例。虽然可能还是太长了,但还是这样

import random

from PySide2 import QtCore, QtGui, QtWidgets


class Marker(QtWidgets.QLabel):
    def __init__(self, parent=None):
        super(Marker, self).__init__(parent)
        self._slider = None
        self.setAcceptDrops(True)
        pix = QtGui.QPixmap(30, 30)
        pix.fill(QtGui.QColor("transparent"))
        paint = QtGui.QPainter(pix)
        slider_color = QtGui.QColor(random.randint(130, 180), random.randint(130, 180), random.randint(130, 180))
        handle_pen = QtGui.QPen(QtGui.QColor(slider_color.darker(200)))
        handle_pen.setWidth(3)
        paint.setPen(handle_pen)
        paint.setBrush(QtGui.QBrush(slider_color, QtCore.Qt.SolidPattern))
        points = QtGui.QPolygon([
            QtCore.QPoint(5, 5),
            QtCore.QPoint(5, 19),
            QtCore.QPoint(13, 27),
            QtCore.QPoint(21, 19),
            QtCore.QPoint(21, 5),

        ])
        paint.drawPolygon(points)
        del paint
        self.setPixmap(pix)


class myTimeline(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(myTimeline, self).__init__(parent)
        layout = QtWidgets.QGridLayout(self)
        self.slider = QtWidgets.QSlider(QtCore.Qt.Horizontal)
        self.slider.setMinimum(0)
        self.slider.setMaximum(50)
        self.slider.setTickPosition(QtWidgets.QSlider.TicksAbove)
        self.slider.setTickInterval(1)
        self.slider.setSingleStep(1)
        self.slider.setAcceptDrops(True)
        self.resize(self.width(), 50)
        layout.addWidget(self.slider)

    def create_marker(self):
        bookmark = Marker(self)
        opt = QtWidgets.QStyleOptionSlider()
        self.slider.initStyleOption(opt)
        rect = self.slider.style().subControlRect(
            QtWidgets.QStyle.CC_Slider,
            opt,
            QtWidgets.QStyle.SC_SliderHandle,
            self.slider
        )
        bookmark.move(rect.center().x(), 0)
        bookmark.show()

    def mouseDoubleClickEvent(self, event):
        self.create_marker()


Tags: 项目selfinit部件defrandomparentslider
1条回答
网友
1楼 · 发布于 2024-06-30 15:08:43

你的方法确实是正确的,它只有几个问题:

  1. 应更新标记的几何图形以反映其内容。这是必需的,因为QLabel是一种非常特殊的小部件类型,通常仅在添加到布局或是顶级窗口时才调整其大小

  2. 标记pixmap未正确对齐(它略位于其中心的左侧)

  3. 标记定位不仅应使用rect中心,还应使用标记宽度滑块位置(因为您要将滑块添加到父级,并且存在布局,因此滑块实际上根据布局内容边距的大小关闭)

  4. 每次调整小部件的大小时,标记都应该重新定位,因此它们应该保留对其值的引用

  5. 标记对于鼠标事件应该是透明的,否则它们会阻止滑块上的鼠标控制

鉴于上述情况,我建议您:

class Marker(QtWidgets.QLabel):
    def __init__(self, value, parent=None):
        super(Marker, self).__init__(parent)
        self.value = value
        # ...
        # correctly centered polygon
        points = QtGui.QPolygon([
            QtCore.QPoint(7, 5),
            QtCore.QPoint(7, 19),
            QtCore.QPoint(15, 27),
            QtCore.QPoint(22, 19),
            QtCore.QPoint(22, 5),

        ])
        paint.drawPolygon(points)
        del paint
        self.setPixmap(pix)

        # this is important!
        self.adjustSize()


class myTimeline(QtWidgets.QWidget):
    def __init__(self, parent=None):
        # ...
        self.markers = []

    def mouseDoubleClickEvent(self, event):
        self.create_marker()

    def create_marker(self):
        bookmark = Marker(self.slider.value(), self)
        bookmark.show()
        bookmark.installEventFilter(self)
        self.markers.append(bookmark)
        self.updateMarkers()

    def updateMarkers(self):
        opt = QtWidgets.QStyleOptionSlider()
        self.slider.initStyleOption(opt)
        for marker in self.markers:
            opt.sliderValue = opt.sliderPosition = marker.value
            rect = self.slider.style().subControlRect(
                QtWidgets.QStyle.CC_Slider,
                opt,
                QtWidgets.QStyle.SC_SliderHandle,
            )
            marker.move(rect.center().x() - marker.width() / 2 + self.slider.x(), 0)

    def eventFilter(self, source, event):
        if event.type() == QtCore.QEvent.MouseButtonPress:
            return True
        return super().eventFilter(source, event)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.updateMarkers()

相关问题 更多 >