【问题标题】:How could I create "markers" for a QSlider on PySide2?如何在 PySide2 上为 QSlider 创建“标记”?
【发布时间】:2020-12-29 08:07:00
【问题描述】:

这张照片很好地代表了我想要模仿的东西。

目标是创建项目或小部件,类似于上面的示例,用户可以通过 MouseDoubleClicked 事件在 QSlider 上创建它们,并且它们将保持在最初创建的 Tick 位置(它将保持不动)。 我已经尝试过使用 QLabels 和 Pixmaps 或 QGraphicsItems 和 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()

【问题讨论】:

  • 您能否提供一个minimal, reproducible example 来说明您目前所获得的信息?可能使用更接近您想要实现的东西的代码(即使它没有按预期工作)。
  • 感谢您的评论,但这实际上是我的问题的重点:我不能。虽然我当然可以把我以前的尝试之一放在这里,但它绝对不会是“最小的”,因此这个问题。最重要的是,即使我这样做了,它也可能违反 Stack Overflow 的规则,我想在这里避免与版主发生问题。
  • 我理解您的担忧,但是 QSlider 不容易映射,而且如果没有最小的 MRE,很难根据“猜测”提供答案。我们可以尝试给出答案,但它可能非常通用,并且可能不是正确的解决方案(和答案)。不要担心版主可能出现的“问题”,尽量先举个例子,如果有问题,他们会收到你的通知。
  • 再次感谢您的评论。我最终冒险了,我只是编辑了我的问题,以添加我认为最接近最小可重现示例的内容。
  • 别担心,你的代码很好,不会太长。据我所见,您几乎可以使用它,除了 QLabel 几何形状的一个小问题以及如果手柄靠近标记它会使滑块几乎无法使用的事实。除此之外(以及某些样式的标记位置略微偏离),我看不到任何其他问题:您的实现有什么问题?

标签: python pyside2 qslider


【解决方案1】:

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

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

  2. 标记像素图未正确对齐(位于其中心的稍微左侧)。

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

【讨论】:

    猜你喜欢
    • 2021-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多