【问题标题】:Update slider from PyQt when zooming in/out on a PyQtGraph在 PyQtGraph 上放大/缩小时从 PyQt 更新滑块
【发布时间】:2020-03-19 11:19:45
【问题描述】:

我想将 PyQtGraph 鼠标滚轮缩放功能连接到 QSlider 小部件。所以当我放大/缩小图表时,我会得到视图框的范围,滑块窗口应该在滑动范围内。在这个 PyQtGraph 示例中可以找到最接近的示例:http://www.pyqtgraph.org/downloads/0.10.0/pyqtgraph-0.10.0-deb/pyqtgraph-0.10.0/examples/crosshair.py

所以我想以某种方式连接以下定义。

def update_plot(self):
    self.axX = self.p6.getAxis('bottom')
    self.xmin = self.axX.range[0]
    self.xmax = self.axX.range[0]
    print(self.axX.range)
    return xmin, xmax

def update_slider(self, xmin, xmax):
    self.size = self.w1.slider.value()
    self.p6.setXRange(self.xmin+self.size,self.xmax+self.size)
    print(self.size)

但是,我似乎无法让它工作。我在下面附上了我的示例的完整代码。你能帮我什么忙吗?

import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \
    QVBoxLayout, QWidget

import pyqtgraph as pg
import numpy as np


class Slider(QWidget):
    def __init__(self, minimum, maximum, parent=None):
        super(Slider, self).__init__(parent=parent)
        self.verticalLayout = QVBoxLayout(self)
        self.label = QLabel(self)
        self.verticalLayout.addWidget(self.label)
        self.horizontalLayout = QHBoxLayout()
        spacerItem = QSpacerItem(0, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.slider = QSlider(self)
        self.slider.setOrientation(Qt.Vertical)
        self.horizontalLayout.addWidget(self.slider)
        spacerItem1 = QSpacerItem(0, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem1)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.resize(self.sizeHint())

        self.minimum = minimum
        self.maximum = maximum
        self.slider.valueChanged.connect(self.setLabelValue)
        self.x = None
        self.setLabelValue(self.slider.value())

    def setLabelValue(self, value):
        self.x = self.minimum + (float(value) / (self.slider.maximum() - self.slider.minimum())) * (
        self.maximum - self.minimum)
        self.label.setText("{0:.4g}".format(self.x))

class Widget(QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent=parent)
        self.horizontalLayout = QHBoxLayout(self)

        # plot (p6)
        self.win = pg.GraphicsWindow(title="Basic plotting examples")
        self.horizontalLayout.addWidget(self.win)
        self.p6 = self.win.addPlot(title="My Plot")
        x = np.arange(1e5)
        self.y1 = np.random.randn(x.size)
        self.p6.plot(self.y1, pen="r")
        self.p6.setMouseEnabled(x=True, y=False)
        self.p6.setXRange(0,300)
        self.p6.setLimits(xMin=0, xMax=len(self.y1))

        self.p6.sigRangeChanged.connect(self.update_plot)

        # slider (w1)
        self.w1 = Slider(0, len(self.y1))
        self.horizontalLayout.addWidget(self.w1)        
        self.w1.slider.setMinimum(0)
        self.w1.slider.setMaximum(len(self.y1))

        self.w1.slider.valueChanged.connect(self.update_slider)

    def update_plot(self):
        self.axX = self.p6.getAxis('bottom')
        self.xmin = self.axX.range[0]
        self.xmax = self.axX.range[0]
        print(self.axX.range)
        return self.xmin, self.xmax


    def update_slider(self, xmin, xmax):
        self.size = self.w1.slider.value()
        self.p6.setXRange(self.xmin+self.size,self.xmax+self.size)
        print(self.size)



if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt pyqt5 pyqtgraph


    【解决方案1】:

    首先,您首先提出的问题实际上对 StackOverflow 的“行为”更有用。编辑后,您只是从 pyqtgraph 示例中留下了 useless 纯副本/粘贴。

    不要那样做

    人们很少进行编辑,并且看到您问题的当前状态,他们会认为您根本没有为解决您的问题付出任何努力(导致他们只是忽略你的问题)。我建议您撤消您的编辑,或者至少恢复原始问题示例代码。


    您的问题的主要问题是缩放不是线性。从普通用户的角度来看,缩放滑块的每一步都应该增加或减少一个缩放因子(实际上,pyqtgraph 就是这样做的)。

    滑块通常是一个线性界面,这意味着它的每一步都应该与其上一步或下一步成比例。
    简而言之,如果滑块的“中间步骤”等于图像的原始尺寸,并且“下一步”等于双倍尺寸,则用户期望接下来的步骤将导致再次将尺寸翻倍。很明显,同样的情况也适用。

    将它想象成一个旋转框,甚至是几个+/- 按钮。

        size = 2
        print(size)
    Out[1]: 2
    
        def doZoom(size, step):
            zoomFactor = 2
            if step > 0:
                size *= zoomFactor
            else:
                size /= zoomFactor
            return size
    
        size = doZoom(size, +1)
        print(size)
    Out[2]: 4
    
        size = doZoom(size, +1)
        print(size)
    Out[3]: 8
    
        size = doZoom(size, -1)
        print(size)
    Out[4]: 4
    

    一个可能的解决方案是实现一个考虑所有这些的滑块。

    import sys
    from math import log
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import (QApplication, QLabel, QSlider, QWidget, 
        QHBoxLayout, QVBoxLayout)
    
    import pyqtgraph as pg
    import numpy as np
    
    class LabelSlider(QWidget):
        def __init__(self, *args, **kwargs):
            super(LabelSlider, self).__init__(*args, **kwargs)
            layout = QVBoxLayout(self)
            # IMPORTANT! QStyles sometimes create *huge* margins (6-10 pixels) around
            # layout contents; we don't really need those in here
            layout.setContentsMargins(0, 0, 0, 0)
    
            self.label = QLabel()
            layout.addWidget(self.label, alignment=Qt.AlignHCenter)
            # use an arbitrary text for the minimum width to minimize size flickering
            # on value changes
            self.label.setMinimumWidth(self.fontMetrics().width("8.8888e+88"))
            self.label.setAlignment(Qt.AlignCenter)
    
            layout.addSpacing(10)
    
            self.slider = QSlider()
            # when adding a QSlider to a QLayout and specifying an alignment, the
            # opposite of the orientation *has* to be omitted to ensure that it's
            # centered in the other direction
            layout.addWidget(self.slider, alignment=Qt.AlignHCenter)
            # set the slider "transparent" for mouse events, so that the user will
            # still see it as enabled but won't be able to interact with it
            self.slider.setAttribute(Qt.WA_TransparentForMouseEvents)
    
            #set a range high enough to limit rounding errors
            self.slider.setMaximum(100000)
    
            # expose the basic slider signal/methods for transparency, so that
            # this LabelSlider will have a similar interface to that of a QSlider
            self.value = self.slider.value
            self.valueChanged = self.slider.valueChanged
    
        def setValue(self, value, xLimit):
            sliderValue = self.slider.maximum() - value * self.slider.maximum()
            self.slider.setValue(sliderValue)
    
            xLimitMin, xLimitMax = xLimit
            limitRange = xLimitMax - xLimitMin
            floatValue = xLimitMin + (value * limitRange / (limitRange)) * (
                limitRange)
            self.label.setText("{0:.4g}".format(floatValue))
            # ensure that the widget is resized to fit the label contents too;
            # sizeHint will be called afterwards
            self.updateGeometry()
    
        def sizeHint(self):
            hint = super(LabelSlider, self).sizeHint()
            # adjust the minimum hint width to accomodate the label contents
            if hint.width() < self.label.width():
                hint.setWidth(self.label.width())
            return hint
    
    
    class Widget(QWidget):
        def __init__(self, parent=None):
            super(Widget, self).__init__(parent=parent)
            self.horizontalLayout = QHBoxLayout(self)
    
            self.win = pg.GraphicsWindow(title="Basic plotting examples")
            self.horizontalLayout.addWidget(self.win)
            self.p6 = self.win.addPlot(title="My Plot")
            x = np.arange(1e5)
            self.y1 = np.random.randn(x.size)
            self.p6.plot(self.y1, pen="r")
            self.p6.setMouseEnabled(x=True, y=False)
            self.p6.setXRange(0,300)
            # set a minimum x range for the plot
            # this value *HAS* to be > 0
            self.p6.setLimits(xMin=0, xMax=len(self.y1), minXRange=1)
    
            self.p6.sigRangeChanged.connect(self.update_plot)
    
            self.slider = LabelSlider()
            self.horizontalLayout.addWidget(self.slider)
    
        def update_plot(self):
            state = self.p6.getViewBox().state
    
            # get the limits of the plot's ViewBox
            limits = state["limits"]
            minZoom, maxZoom = xLimit = limits["xLimits"]
            xRangeMin, xRangeMax = limits["xRange"]
    
            # if the minimum x range is set, use that instead
            if xRangeMin is not None:
                minZoom = xRangeMin
            # ensure that the minimum x range is > 0
            minZoom = max(1, minZoom)
            if xRangeMax is not None:
                maxZoom = xRangeMax
            xMin, xMax = self.p6.getAxis("bottom").range
            diff = xMax - xMin
    
            # get the possible minimum and maximum values based on the wheel factor
            factor = abs(state["wheelScaleFactor"])
            minimum = log(maxZoom / 100000., factor)
            maximum = log(minZoom / 100000., factor)
            value = log(diff / 100000., factor)
    
            # adjust the factor to a 0.0-1.0 range according to the possible zoom
            realValue = (value - minimum) / (maximum - minimum)
    
            # set the slider value according to the above value
            self.slider.setValue(realValue, xLimit)
    
    
    if __name__ == "__main__":
        app = QApplication(sys.argv)
        w = Widget()
        w.show()
        sys.exit(app.exec_())
    

    【讨论】:

    • 对不起,我对 Python 和 stackoverflow 的提问都是新手。我已经编辑回原始问题和代码并进行了一些调整。感谢您的帮助
    • 不客气,很高兴能为您提供帮助...别担心,我们都会犯错误 :-) 为将来记住,最好提供一个尽可能完整的示例尽可能(同时保持最少),包括所有必要的代码,以尽量减少理解和回答所需的努力;一个好的问题甚至可能需要一个多小时才能正确解决,但如果有人能够帮助你意识到他/她可以在做之前毫不费力地给出答案,那么付出努力是值得的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-19
    • 2018-08-22
    • 1970-01-01
    • 2020-03-01
    • 1970-01-01
    相关资源
    最近更新 更多