I wish I heard about locate() a few days ago as it would have greatly 
simplified this troubleshooting process... Thanks for the tip!

The final step was indeed to subclass measureSpotSizes. It's a shame so 
much was copy-pasted from the base method, but at least it works:
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):
    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]Enter code here...



On Sunday, June 7, 2020 at 11:04:29 PM UTC-4, Patrick wrote:
>
> Hi,
>
> As a guess, I'm thinking that your CustScatter is still only assuming the 
> scatter plot points are just points with width the size of the pen width 
> (see measureSpotSizes in ScatterPlotItem). You may need to override that 
> method to calculate the shape sizes.
>
> Also, in the past I stumbled across the locate method of ViewBox 
> <https://pyqtgraph.readthedocs.io/en/latest/graphicsItems/viewbox.html#pyqtgraph.ViewBox.locate>
>  
> which has helped diagnosing problems like this, maybe it's useful to try 
> here too.
>
> Patrick
>
> On Monday, 8 June 2020 12:06:30 UTC+9:30, Nathan Jessurun wrote:
>>
>> Attempt number 3: I almost have it doing what I want! I looked at what 
>> PlotCurveItem does to make the curveClicked signal, and saw I had to make a 
>> stroke path.
>>
>> The *last* piece of the puzzle is how to inform the graphics handler 
>> that my spot size extends a bit beyond the natural bounding rect. Does 
>> anyone know of a way to make this happen? I.e. the programming logic works, 
>> but the check for self.itemsNearEvent() in the graphics scene fails for 
>> a click near the top-right shape.
>> import pyqtgraph as pg
>> from pyqtgraph.Qt import QtWidgets, QtCore, QtGui
>> import numpy as np
>>
>> class CustScatter(pg.ScatterPlotItem):
>>   def pointsAt(self, pos: QtCore.QPointF):
>>     # if isinstance(pos, QtCore.QPointF):
>>     #   halfSz = 0.25
>>     #   pos = QtCore.QRectF(pos.x()-halfSz, pos.y()-halfSz, 2*halfSz, 
>> 2*halfSz)
>>     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)
>>       if mousePath.contains(pos):
>>         pts.append(spot)
>>     return pts[::-1]
>>
>> 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!')
>>
>> 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))
>>
>> 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
>>
>> w = pg.PlotWindow()
>> w.plotItem.addItem(scat)
>> plt: pg.PlotItem = w.plotItem
>> plt.showGrid(True, True, 1)
>> w.show()
>> app.exec()
>>
>>
>>
>>
>> On Saturday, June 6, 2020 at 9:44:36 PM UTC-4, Nathan Jessurun wrote:
>>>
>>> Hi there,
>>>
>>> My overall goal is to have several clickable regions overlaid on an 
>>> image, and if the plot boundary of any region is clicked I get a signal 
>>> with the ID of that region. Something like this:
>>> I tried making each boundary a separate plot data item, but I have 1000s 
>>> of bounds that update frequently so it lagged a ton. So, I just used 
>>> connect='finite' within the same plot item to make it much faster.
>>>
>>> However, now I can't give a separate ID to each boundary. I have to make 
>>> the vertices stand out, and connect to sigclicked(...pts) instead:
>>>
>>> This works, but I'd love to be able to click anywhere on one of the line 
>>> segments for the same effect. Is there any way to make this happen?
>>>
>>> Relevant code is just a PlotDataItem on a plot widget.
>>>
>>> Thanks!
>>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pyqtgraph/cbb002a9-3815-48be-b83b-e9c33110a12ao%40googlegroups.com.

Reply via email to