Search code examples
pythonslidersignalsqscrollbar

how to prevent the arrow of a Qscrollbar from calling twice the same function?


I've made a pyqt window to display signal in a matplotlib figure with a QScrollBar to see different part of the signal. My issue is when I plot 100 signal, the arrow of the QScrollBar calls twice the function that is called on a valueChanged signal.

When I put only 10 signals (self.Sigs_dict = np.random.rand(10,105*self.Fs)) I pass only once in the update_plot function where I print time info:

first 1572956286.183867
set 0.0 

But If I increase the number of signal to 100 (self.Sigs_dict = np.random.rand(100,105*self.Fs)) I pass twice in the update_plot function. I then get :

first 1572956174.959317
set 0.009981632232666016
first 1572956175.6289513
set 0.0

I don't understand why it is happening and how I can get ride of that issue.

Here is the minimal example of my issue:

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import matplotlib
matplotlib.use('Qt5Agg')
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import numpy as np
import time

class Viewer(QMainWindow):
    def __init__(self, parent=None):
        super(Viewer, self).__init__()
        self.parent = parent
        #######################################
        self.centralWidget = QWidget()
        self.setCentralWidget(self.centralWidget)
        self.mainVBOX_param_scene = QVBoxLayout()
        self.mascene = plot(self)

        self.paramPlotV = QVBoxLayout()
        self.horizontalSliders  = QScrollBar(Qt.Horizontal)
        self.horizontalSliders.valueChanged.connect(self.update_plot)
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(1)
        self.horizontalSliders.setPageStep(1)

        self.paramPlotV.addWidget(self.horizontalSliders)

        self.mainVBOX_param_scene.addWidget(self.mascene)
        self.mainVBOX_param_scene.addLayout(self.paramPlotV)

        self.centralWidget.setLayout(self.mainVBOX_param_scene)

        self.Fs = 1024
        self.Sigs_dict = np.random.rand(100,105*self.Fs)
        self.t = np.arange(self.Sigs_dict.shape[1])/self.Fs
        self.update()

    def updateslider(self):
        self.horizontalSliders.setMinimum(0)
        self.horizontalSliders.setMaximum(np.ceil(self.t[-1]/int(10))-1)

    def update_plot(self):
        t = time.time()
        print('first',t)
        self.mascene.update_set_data()
        print('set',time.time() - t)

    def update(self):
        self.updateslider()
        self.mascene.modify_sigs()
        self.mascene.update()


class plot(QGraphicsView):
    def __init__(self, parent=None):
        super(plot, self).__init__(parent)
        self.parent = parent
        self.scene = QGraphicsScene(self)
        self.setScene(self.scene)
        self.figure = plt.figure(facecolor='white')#Figure()
        self.canvas = FigureCanvas(self.figure)
        self.widget = QWidget()
        self.widget.setLayout(QVBoxLayout())
        self.scroll = QScrollArea(self.widget)
        self.scroll.setWidget(self.canvas)

        layout = QVBoxLayout()
        layout.addWidget(self.scroll)
        self.setLayout(layout)

    def modify_sigs(self):
        self.Sigs_dict = self.parent.Sigs_dict
        self.t = self.parent.t
        self.Fs= self.parent.Fs

    def update_set_data(self):
        ts, te = self.get_ts_te()
        t = self.t[ts:te]
        for i,line in enumerate(self.Lines):
            line.set_data(t, (self.Sigs_dict[i, ts:te]) + i)
        self.axes.set_xlim((ts/ self.Fs, ts / self.Fs + 10 ))
        self.canvas.draw_idle()


    def update(self):
        self.figure.clear()
        plt.figure(self.figure.number)
        self.axes = plt.subplot(1, 1, 1)
        ts, te = self.get_ts_te()

        self.Lines = []
        for i in range(self.Sigs_dict.shape[0]):
            line, = plt.plot(self.t[ts:te], (self.Sigs_dict[i,ts:te]-np.mean(self.Sigs_dict[i,ts:te]))+i)
            self.Lines.append(line)

        self.canvas.setGeometry(0, 0, self.parent.width()-100, (self.parent.height()-100))
        self.canvas.draw_idle()

    def get_ts_te(self):
        win_num = self.parent.horizontalSliders.value()
        ts = int(10 * (win_num) * self.Fs)
        te = ts + int(10 * self.Fs)
        if te > len(self.t):
            diff = te - len(self.t)
            ts = ts - diff
            te = len(self.t)
        return ts, te

def main():
    app = QApplication(sys.argv)
    ex = Viewer(app)
    ex.show()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

Solution

  • I found a way to counter the issue. I add self.horizontalSliders.setEnabled(False) before the matplotlib figure update and I set it to True when the update is finished. So I have:

    def update_plot(self):
        self.horizontalSliders.setEnabled(False)
        t = time.time()
        print('first',t, self.horizontalSliders.value())
        self.mascene.update_set_data()
        print('set',time.time() - t)
        self.horizontalSliders.setEnabled(True)
    

    I don't know if it is the right way to do it but it works