我创建了一个 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_())
图片:开始时: 这是启动应用程序后的样子 当我单击上面的图表时,范围会发生变化 当我点击下面的图表时,范围消失=>这是我的问题
问题是,当您单击时,就像选择一个空范围(我认为您已经意识到这一点,因为您使用大量
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_())