我如何制作一个在不同的线段上具有唯一ID的PlotDataItem?

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

我也张贴在pyqtgraph论坛here

我的总体目标是在图像上叠加几个可点击区域,如果单击任何区域的图线边界,我都会得到一个带有该区域ID的信号。像这样:enter image description here

如果我仅使用一个带有nan分隔曲线的PlotDataItem,则每个边界发送相同的信号。但是,对每个边界使用单独的PlotDataItem会使应用​​程序非常缓慢。

我最终继承了ScatterPlotItem的子类,并重写pointsAt函数,该函数实现了我想要的功能。现在的问题是我无法找出更改ScatterPlotItem的boundingRect的适当方法。我采用正确的方法吗?有更好的方法吗?enter image description here

import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore, QtGui


class CustScatter(pg.ScatterPlotItem):
  def pointsAt(self, pos: QtCore.QPointF):
    """
    The default implementation only checks a square around each spot. However, this is not
    precise enough for my needs. It also triggers when clicking *inside* the spot boundary,
    which I don't want.
    """
    pts = []
    for spot in self.points(): # type: pg.SpotItem
      symb = QtGui.QPainterPath(spot.symbol())
      symb.translate(spot.pos())
      stroker = QtGui.QPainterPathStroker()
      mousePath = stroker.createStroke(symb)
      # Only trigger when clicking a boundary, not the inside of the shape
      if mousePath.contains(pos):
        pts.append(spot)
    return pts[::-1]

"""Make some sample data"""
tri = np.array([[0,2.3,0,1,4,5,0], [0,4,4,8,8,3,0]]).T
tris = []
xyLocs = []
datas = []
for ii in np.arange(0, 16, 5):
  curTri = tri + ii
  tris.append(curTri)
  xyLocs.append(curTri.min(0))
  datas.append(ii)

def ptsClicked(item, pts):
  print(f'ID {pts[0].data()} Clicked!')

"""Logic for making spot shapes from a list of (x,y) vertices"""
def makeSymbol(verts: np.ndarray):
  outSymbol = QtGui.QPainterPath()
  symPath = pg.arrayToQPath(*verts.T)
  outSymbol.addPath(symPath)
  # From pyqtgraph.examples for plotting text
  br = outSymbol.boundingRect()
  tr = QtGui.QTransform()
  tr.translate(-br.x(), -br.y())
  outSymbol = tr.map(outSymbol)
  return outSymbol

app = pg.mkQApp()
pg.setConfigOption('background', 'w')

symbs = []
for xyLoc, tri in zip(xyLocs, tris):
  symbs.append(makeSymbol(tri))

"""Create the scatterplot"""
xyLocs = np.vstack(xyLocs)
tri2 = pg.PlotDataItem()
scat = CustScatter(*xyLocs.T, symbol=symbs, data=datas, connect='finite',
                   pxMode=False, brush=None, pen=pg.mkPen(width=5), size=1)
scat.sigClicked.connect(ptsClicked)
# Now each 'point' is one of the triangles, hopefully

"""Construct GUI window"""
w = pg.PlotWindow()
w.plotItem.addItem(scat)
plt: pg.PlotItem = w.plotItem
plt.showGrid(True, True, 1)
w.show()
app.exec()
python qt5 pyqtgraph qgraphicsitem
1个回答
0
投票
import numpy as np import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui class CustScatter(pg.ScatterPlotItem): def pointsAt(self, pos: QtCore.QPointF): """ The default implementation only checks a square around each spot. However, this is not precise enough for my needs. It also triggers when clicking *inside* the spot boundary, which I don't want. """ pts = [] for spot in self.points(): # type: pg.SpotItem symb = QtGui.QPainterPath(spot.symbol()) symb.translate(spot.pos()) stroker = QtGui.QPainterPathStroker() mousePath = stroker.createStroke(symb) # Only trigger when clicking a boundary, not the inside of the shape if mousePath.contains(pos): pts.append(spot) return pts[::-1] def measureSpotSizes(self, dataSet): """ Override the method so that it takes symbol size into account """ for rec in dataSet: ## keep track of the maximum spot size and pixel size symbol, size, pen, brush = self.getSpotOpts(rec) br = symbol.boundingRect() size = max(br.width(), br.height())*2 width = 0 pxWidth = 0 if self.opts['pxMode']: pxWidth = size + pen.widthF() else: width = size if pen.isCosmetic(): pxWidth += pen.widthF() else: width += pen.widthF() self._maxSpotWidth = max(self._maxSpotWidth, width) self._maxSpotPxWidth = max(self._maxSpotPxWidth, pxWidth) self.bounds = [None, None] """Make some sample data""" tri = np.array([[0,2.3,0,1,4,5,0], [0,4,4,8,8,3,0]]).T tris = [] xyLocs = [] datas = [] for ii in np.arange(0, 16, 5): curTri = tri + ii tris.append(curTri) xyLocs.append(curTri.min(0)) datas.append(ii) def ptsClicked(item, pts): print(f'ID {pts[0].data()} Clicked!') """Logic for making spot shapes from a list of (x,y) vertices""" def makeSymbol(verts: np.ndarray): outSymbol = QtGui.QPainterPath() symPath = pg.arrayToQPath(*verts.T) outSymbol.addPath(symPath) # From pyqtgraph.examples for plotting text br = outSymbol.boundingRect() tr = QtGui.QTransform() tr.translate(-br.x(), -br.y()) outSymbol = tr.map(outSymbol) return outSymbol app = pg.mkQApp() pg.setConfigOption('background', 'd') symbs = [] for xyLoc, tri in zip(xyLocs, tris): symbs.append(makeSymbol(tri)) """Create the scatterplot""" xyLocs = np.vstack(xyLocs) tri2 = pg.PlotDataItem() scat = CustScatter(*xyLocs.T, symbol=symbs, data=datas, connect='finite', pxMode=False, brush=None, pen=pg.mkPen(width=5), size=1) scat.sigClicked.connect(ptsClicked) # Now each 'point' is one of the triangles, hopefully """Construct GUI window""" w = pg.PlotWindow() w.plotItem.addItem(scat) plt: pg.PlotItem = w.plotItem plt.showGrid(True, True, 1) w.show() app.exec()
© www.soinside.com 2019 - 2024. All rights reserved.