Как предотвратить, чтобы стрелка Qscrollbar дважды вызывала одну и ту же функцию? - PullRequest
0 голосов
/ 05 ноября 2019

Я сделал окно pyqt для отображения сигнала в виде matplotlib с QScrollBar, чтобы увидеть другую часть сигнала. Моя проблема заключается в том, что когда я строю сигнал 100, стрелка QScrollBar дважды вызывает функцию, которая вызывается для сигнала valueChanged.

Когда я ставлю только 10 сигналов (self.Sigs_dict = np.random.rand(10,105*self.Fs)), я передаю толькоодин раз в функции update_plot, где я печатаю информацию о времени:

first 1572956286.183867
set 0.0 

Но если я увеличу число сигналов до 100 (self.Sigs_dict = np.random.rand(100,105*self.Fs)), я дважды передам функцию update_plot. Затем я получаю:

first 1572956174.959317
set 0.009981632232666016
first 1572956175.6289513
set 0.0

Я не понимаю, почему это происходит и как я могу решить эту проблему.

Вот минимальный пример моей проблемы:

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

Ответы [ 2 ]

1 голос
/ 05 ноября 2019

Я вижу, что это происходит, когда вы пытаетесь переместить ползунок более чем на один шаг. Как только значение изменяется на 1 шаг, вы получаете обновление, а затем другое, когда значение снова меняется. Поскольку время рисования для 100 сигналов заметно медленнее, чем для 10, отклик GUI медленнее, поэтому ползунок имеет тенденцию выдавать несколько обновлений вместо перехода к конечному значению.

Простое исправление для этого заключается в измененииваш подключенный сигнал к

self.horizontalSliders.sliderReleased.connect(self.update_plot)

Сигнал sliderReleased обеспечит завершение перемещения ползунка перед выполнением обновления.

Недостатком этого подхода является то, что один щелчок мыши по стрелке отображается. Не пойман, только перетащить бар. Если вы хотите обрабатывать и то и другое, предотвращая множественные обновления, вам нужно создать переменную состояния, чтобы решить, в каком режиме вы находитесь. Вы можете посмотреть на сигнал sliderPressed, чтобы увидеть, перетаскивает ли пользователь ползунок. Если это так, посмотрите на сигнал sliderReleased для завершения. Если нет, используйте сигнал valueChanged.

КСТАТИ ... Сигналы и слоты QT велики, но этот тип рекурсии очень распространен, и вам часто приходится устанавливать дополнительные проверки, чтобы предотвратить многочисленные звонки на дорогиефункции перерисовки и т.д ..

0 голосов
/ 06 ноября 2019

Я нашел способ противостоять проблеме. Я добавляю self.horizontalSliders.setEnabled(False) перед обновлением фигуры matplotlib и устанавливаю значение True, когда обновление завершено. Итак, у меня есть:

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)

Я не знаю, правильно ли это сделать, но это работает

...