Python->子图->在显示范围的子图中单击时,将跨度中心移动到不显示范围的单击位置

问题描述 投票:0回答:0

我创建了一个 2 情节的子情节。在两个子图中选择范围效果很好。单击上部子图(缩放图)可以很好地移动两个视图,但是当单击下部(总视图)图时,所选范围(SpanSelector)区域消失。我错过了什么。

下一步,我打算将十字光标放置到顶部(缩放)图。

附上代码示例:

import sys
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.widgets import MultiCursor
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.ticker import FuncFormatter
from matplotlib.widgets import SpanSelector
import matplotlib.ticker as ticker

class MainWindow_code_serarch(object):

    def setup_code_serarch(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1024, 800)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 1024, 800))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0,0,0,0)
        self.verticalLayout.setObjectName("verticalLayout")


        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.verticalLayout.addWidget(self.canvas)
        axes, axes2 = self.figure.subplots(nrows=2, sharex=True)
    
        datacount = 100000
    
        data_x = []
        data_y = []
    
        for i in range(1, datacount):
            data_x.append(i)
            if i % 250 <= 50:
                data_y.append(np.nan)
            else:
                data_y.append(np.sin(i/100)+0.05*np.sin(i))
    
        #** matplotlib subplot *********************************************
        self.figure.subplots_adjust(left=0.03, right=0.97, top=0.975, bottom=0.075, wspace=0, hspace=0.2)
    
        ax1, ax2 = self.figure.subplots(2, height_ratios=[0.8,0.2])
        ax1.grid()
        ax1.tick_params(axis = 'x', length=0)
        ax1.tick_params(axis = 'y', length=0)

        ax2.tick_params(axis = 'x', length=0)
        ax2.tick_params(axis = 'y', length=0)
        ax2.grid()
    
        def format_tick_labels(x, pos):
            return '{0:2.0e}'.format(x)

        ax2.xaxis.set_major_formatter(FuncFormatter(format_tick_labels))
        
        ax2.plot(data_x, data_y, linewidth = 0.5)
        ax2.set_xlim(0, datacount)
        ax2.set_ylim(-1.2, 1.2)
    
        self.line2, = ax1.plot([], [], linewidth=0.5)
    
        def onselect_span(xmin, xmax):
            print('onselect_span')
            indmin, indmax = np.searchsorted(data_x, (xmin, xmax))
        
            print(f'Span: select indmin = {indmin}, indmax = {indmax}')

            region_x = data_x[indmin:indmax]   
            region_y = data_y[indmin:indmax]   
        

            if len(region_x)>=2:
                self.line2.set_data(region_x, region_y)
                ax1.set_xlim(region_x[0], region_x[-1])     #select from region start till region end...
                ax1.set_ylim(-1.2,1.2)#region_y[0], region_y[-1])
                self.span2.extents = (region_x[0], region_x[-1])
                self.canvas.draw_idle()
                self.mouse_SpanSelected = True;    
            else: 
                print('onselect_span: region too small') 
            
            self.span1.clear()
            print('onselect_span end')
  
    
        self.span1 = SpanSelector(
            ax1,
            onselect_span,
            "horizontal",
            useblit=True,
            props=dict(alpha=0.3, facecolor="tab:red"),
            interactive=True,
            drag_from_anywhere=True,
            grab_range = 3,
        )
    
        self.span2 = SpanSelector(
            ax2,
            onselect_span,
            "horizontal",
            useblit=True,
            props=dict(alpha=0.3, facecolor="tab:red"),
            interactive=True,
            drag_from_anywhere=True,
            grab_range = 3,
        )
    
        def onclick(event):
            global ix
            ix = event.xdata
            print('onclick end')

                
        def onrelease(event):
            print('onrelease')
            global ix, ixrel
            ixrel = event.xdata

            if abs(ix-ixrel)<=2:
                print('Release: region too small')
                width_half = int ((self.line2._x[-1] - self.line2._x[0])/2)
                self.span2.extents = (ixrel - width_half, ixrel + width_half)
                onselect_span(ixrel - width_half, ixrel + width_half)
                self.span2.update()
            
            self.canvas.draw_idle()
            
            print('onrelease end')
        
        
        click_id = self.figure.canvas.mpl_connect('button_press_event', onclick)
        relaese_id = self.figure.canvas.mpl_connect('button_release_event', onrelease)

        self.span2.extents = (4000,15501)  #set selectred region
        onselect_span(4000,15501)

        self.canvas.draw()
    
        MainWindow.setCentralWidget(self.centralwidget)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = MainWindow_code_serarch()
    ui.setup_code_serarch(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

图片:开始时: 这是启动应用程序后的样子 this is how it looks after starting the app 当我单击上图中时,范围会发生变化 when I click in the upper graph the range shifts 当我点击下面的图表时,范围消失=>这是我的问题 when I click in the lower graph the range disappears =>这是我的问题”/></a></p>
<p>我希望有人能指出我的代码错误。
问候
GvTT</p>
    </question>
	<answer tick=

问题是,当您单击时,就像选择一个空范围(我认为您已经意识到这一点,因为您使用大量

print
来检测代码,它们可以准确地告诉您发生了什么以及使用什么值) .

当您选择空范围时,SpanSelector 将被删除。这甚至在您有机会调用回调之前就发生了。 (我有一种模糊的感觉,但非常模糊,我没有花时间去理解为什么你用回调来完成所有这些事情,不仅针对

select
而且还针对单击和释放,你正在重新编码 SpanSelector 已经在做的事情).

您可以通过使用

minspan
的参数
SpanSelector
来防止删除。让它
minspan=-1
,然后它们就永远不会被移除。当然,从理论上讲这是一种畸变(默认
minspan=0
应该是有意义的最小值),但就您而言,因为您将在需要时采取措施将其放回原处...

无论如何,它似乎像我想象的那样工作

所以,我的最小可重复答案:-)(这只是你问题的副本,还有两个额外的

minspan=-1

import sys
import numpy as np
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.pyplot as plt
from matplotlib.widgets import MultiCursor
from PyQt5.QtWidgets import QMainWindow,QVBoxLayout
from PyQt5.QtWidgets import QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
from matplotlib.ticker import FuncFormatter
from matplotlib.widgets import SpanSelector
import matplotlib.ticker as ticker

class MainWindow_code_serarch(object):

    def setup_code_serarch(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(1024, 800)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(0, 0, 1024, 800))
        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setContentsMargins(0,0,0,0)
        self.verticalLayout.setObjectName("verticalLayout")


        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.verticalLayout.addWidget(self.canvas)
        axes, axes2 = self.figure.subplots(nrows=2, sharex=True)
    
        datacount = 100000
    
        data_x = []
        data_y = []
    
        for i in range(1, datacount):
            data_x.append(i)
            if i % 250 <= 50:
                data_y.append(np.nan)
            else:
                data_y.append(np.sin(i/100)+0.05*np.sin(i))
    
        #** matplotlib subplot *********************************************
        self.figure.subplots_adjust(left=0.03, right=0.97, top=0.975, bottom=0.075, wspace=0, hspace=0.2)
    
        ax1, ax2 = self.figure.subplots(2, height_ratios=[0.8,0.2])
        ax1.grid()
        ax1.tick_params(axis = 'x', length=0)
        ax1.tick_params(axis = 'y', length=0)

        ax2.tick_params(axis = 'x', length=0)
        ax2.tick_params(axis = 'y', length=0)
        ax2.grid()
    
        def format_tick_labels(x, pos):
            return '{0:2.0e}'.format(x)

        ax2.xaxis.set_major_formatter(FuncFormatter(format_tick_labels))
        
        ax2.plot(data_x, data_y, linewidth = 0.5)
        ax2.set_xlim(0, datacount)
        ax2.set_ylim(-1.2, 1.2)
    
        self.line2, = ax1.plot([], [], linewidth=0.5)
    
        def onselect_span(xmin, xmax):
            print('onselect_span')
            indmin, indmax = np.searchsorted(data_x, (xmin, xmax))
        
            print(f'Span: select indmin = {indmin}, indmax = {indmax}')

            region_x = data_x[indmin:indmax]   
            region_y = data_y[indmin:indmax]   
        

            if len(region_x)>=2:
                self.line2.set_data(region_x, region_y)
                ax1.set_xlim(region_x[0], region_x[-1])     #select from region start till region end...
                ax1.set_ylim(-1.2,1.2)#region_y[0], region_y[-1])
                self.span2.extents = (region_x[0], region_x[-1])
                self.canvas.draw_idle()
                self.mouse_SpanSelected = True;    
            else: 
                print('onselect_span: region too small') 
            
            self.span1.clear()
            print('onselect_span end')
  
    
        self.span1 = SpanSelector(
            ax1,
            onselect_span,
            "horizontal",
            useblit=True,
            props=dict(alpha=0.3, facecolor="tab:red"),
            interactive=True,
            drag_from_anywhere=True,
            minspan=-1,                      ## <<<<<<<< HERE
            grab_range = 3,
        )
    
        self.span2 = SpanSelector(
            ax2,
            onselect_span,
            "horizontal",
            useblit=True,
            props=dict(alpha=0.3, facecolor="tab:red"),
            interactive=True,
            drag_from_anywhere=True,
            minspan=-1,                            ## <<<<<<<< AND HERE
            grab_range = 3,
        )
    
        def onclick(event):
            global ix
            ix = event.xdata
            print('onclick end')

                
        def onrelease(event):
            print('onrelease')
            global ix, ixrel
            ixrel = event.xdata

            if abs(ix-ixrel)<=2:
                print('Release: region too small')
                width_half = int ((self.line2._x[-1] - self.line2._x[0])/2)
                self.span2.extents = (ixrel - width_half, ixrel + width_half)
                onselect_span(ixrel - width_half, ixrel + width_half)
                self.span2.update()
            
            self.canvas.draw_idle()
            
            print('onrelease end')
        
        
        click_id = self.figure.canvas.mpl_connect('button_press_event', onclick)
        relaese_id = self.figure.canvas.mpl_connect('button_release_event', onrelease)

        self.span2.extents = (4000,15501)  #set selectred region
        onselect_span(4000,15501)

        self.canvas.draw()
    
        MainWindow.setCentralWidget(self.centralwidget)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = MainWindow_code_serarch()
    ui.setup_code_serarch(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())
python matplotlib range subplot
© www.soinside.com 2019 - 2024. All rights reserved.