简体   繁体   English

如何在PyQtGraph中与圆表面交互?

[英]How to interact with circle surface in PyQtGraph?

I have a circle that has been rendered using PyQtGraph. 我有一个使用PyQtGraph渲染的圆。 Inside the circle another graph is rendered. 在圆内绘制另一个图形。 The graph consists of nodes and edges as usual. 该图通常包含节点和边。 Currently, I can click on the nodes though and get their coordinates and do some calculations. 目前,我可以单击节点并获取其坐标并进行一些计算。 But at the same time, if I click anywhere on the hollow surface of the circle it does not invoke any event. 但是同时,如果我单击圆的空心表面上的任意位置,则不会调用任何事件。 My requirement is to get the click point anywhere inside the circle. 我的要求是在圆内的任何位置获得点击点。 Below you can find the code to render my unit radius circle (considering the center of circle to be the origin) and the custom class. 在下面,您可以找到渲染我的单位半径圆(将圆心视为原点)和自定义类的代码。

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

class Graph(pg.GraphItem):
    def __init__(self):
        self.dragPoint = None
        self.dragOffset = None
        self.textItems = []
        pg.GraphItem.__init__(self)
        self.scatter.sigClicked.connect(self.onclick)
        self.data = lambda x: None
        self.text = lambda x: None

    def setData(self, **kwds):
        self.text = kwds.pop('text', [])
        self.data = kwds
        if 'pos' in self.data:            
            npts = self.data['pos'].shape[0]
            self.data['data'] = np.empty(npts, dtype=[('index', int)])
            self.data['data']['index'] = np.arange(npts)
        self.settexts(self.text)
        self.updategraph()

    def settexts(self, text):
        for i in self.textItems:
            i.scene().removeItem(i)
        self.textItems = []
        for t in text:
            item = pg.TextItem(t)
            self.textItems.append(item)
            item.setParentItem(self)

    def updategraph(self):
        pg.GraphItem.setData(self, **self.data)
        for i, item in enumerate(self.textItems):
            item.setPos(*self.data['pos'][i])

    def mouseDragEvent(self, ev):
        if ev.button() != QtCore.Qt.LeftButton:
            ev.ignore()
            return

        if ev.isStart():
            # We are already one step into the drag.
            # Find the point(s) at the mouse cursor when the button was first
            # pressed:
            pos = ev.buttonDownPos()
            pts = self.scatter.pointsAt(pos)
            if len(pts) == 0:
                ev.ignore()
                return
            self.dragPoint = pts[0]
            ind = pts[0].data()[0]
            self.dragOffset = self.data['pos'][ind] - pos
        elif ev.isFinish():
            self.dragPoint = None
            return
        else:
            if self.dragPoint is None:
                ev.ignore()
                return

        ind = self.dragPoint.data()[0]
        self.data['pos'][ind] = ev.pos() + self.dragOffset
        self.updategraph()
        ev.accept()

    # Once a node on the graph is clicked, the clicked node should become the center of the graph
    def onclick(plot, points):
        x = 0
        y = 0
        x, y = points.ptsClicked[0]._data[0], points.ptsClicked[0]._data[1]     # position of the clicked point
        print('Clicked point is (' + str(x) + ', ' + str(y) + ')')


# Construct a unit radius circle for the graph
def plot_poincare_disc(graph_item_1, graph_item_2):
    # Two semicircles have been produced first and then joined later
    # As PyQtGraph needs a position matrix along with an adjacency matrix, hence pos and adj arrays

    # Semi-Circle 1
    pos1 = []
    adj1 = []
    length = 0
    # calculating y coordinates for 1000 evenly spaced points in (-1,1)
    for x in np.linspace(-1, 1, 1000):
        y = math.sqrt(1 - x ** 2)
        pos1.append([x, y])
        if len(pos1) > 1:
            adj1.append([length - 1, length])
        length = length + 1

    pos1 = np.array(pos1)
    adj1 = np.array(adj1)
    graph_item_1.setData(pos=pos1, adj=adj1, size=0.07)

    # Semi-circle 2
    pos2 = []
    adj2 = []
    length = 0
    # calculating y coordinates for 1000 evenly spaced points in (1,-1)
    for x in np.linspace(1, -1, 1000):
        y = -math.sqrt(1 - x ** 2)
        pos2.append([x, y])
        if len(pos2) > 1:
            adj2.append([length - 1, length])
        length = length + 1

    pos2 = np.array(pos2)
    adj2 = np.array(adj2)
    graph_item_2.setData(pos=pos2, adj=adj2, size=0.07)

if __name__ == '__main__':
    position = [(-0.5,0), (0.5,0)]
    adjacency = [(0,1)]

    w = pg.GraphicsWindow()
    w.setWindowTitle('Title of the window') 
    v = w.addViewBox()
    v.setAspectLocked()
    g = Graph()
    v.addItem(g)

    g.setData(pos=np.array(position), adj=np.array(adjacency), pxMode=False, size=0.1)

    g2 = pg.GraphItem()
    v.addItem(g2)
    g3 = pg.GraphItem()
    v.addItem(g3)

    plot_poincare_disc(g2,g3)

    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QGuiApplication.instance().exec_()

I know, although, inside of the circle does not contain any scatter point, that is why it does not invoke any event. 我知道,尽管圆内不包含任何分散点,所以这就是它不调用任何事件的原因。 Is there a possibility to make inside hollow clickable? 是否有可能使内部空心可点击?

Edit1: Code replaced by minimal, complete and meaningful code. Edit1:用最少,完整和有意义的代码代替代码。

Instead of drawing the points and generating the circumference, a better strategy is to create a class that draws a transparent circle, creating a signal indicating the position of the click using the mousePressEvent method, the class from which it inherits must be QGraphicsObject : 除了绘制点并生成圆周以外,更好的策略是创建一个绘制透明圆的类,并使用mousePressEvent方法创建一个指示点击位置的信号,该类必须继承自QGraphicsObject

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

class Graph(pg.GraphItem):
    def __init__(self):
        ...


# Construct a unit radius circle for the graph
class EllipseObject(QtGui.QGraphicsObject):
    sigClicked = QtCore.pyqtSignal(float, float)
    def __init__(self, center= (0.0, 0.0), radius=1.0, pen=QtGui.QPen(QtCore.Qt.white)):
        QtGui.QGraphicsObject.__init__(self)
        self.center = center
        self.radius = radius
        self.pen = pen

    def boundingRect(self):
        rect = QtCore.QRectF(0, 0, 2*self.radius, 2*self.radius)
        rect.moveCenter(QtCore.QPointF(*self.center))
        return rect

    def paint(self, painter, option, widget):
        painter.setPen(self.pen)
        painter.drawEllipse(self.boundingRect())

    def mousePressEvent(self, event):
        p = event.pos()
        self.sigClicked.emit(p.x(), p.y())
        QtGui.QGraphicsEllipseItem.mousePressEvent(self, event)

if __name__ == '__main__':
    position = [(-0.5,0), (0.5,0)]
    adjacency = [(0,1)]
    w = pg.GraphicsWindow()
    w.setWindowTitle('Title of the window') 
    v = w.addViewBox()
    v.setAspectLocked()
    g = Graph()
    v.addItem(g)

    g.setData(pos=np.array(position), adj=np.array(adjacency), pxMode=False, size=0.1)
    item = EllipseObject()
    item.sigClicked.connect(lambda x, y: print(x, y))
    v.addItem(item)

    import sys

    if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
        QtGui.QApplication.instance().exec_()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM