【问题标题】:Draggable line with draggable points具有可拖动点的可拖动线
【发布时间】:2015-03-16 02:27:28
【问题描述】:

我需要在 figureCanvas 中有 2 个可拖动的点。但我有一个补充约束:这两个点必须用一条线连接起来。 当然,当我拖动一个点时(否则就不好笑了),这条线必须是动态绘制的,并且仍然链接到这两个点。

我设法用这个主题创建了 2 个可拖动点: Matplotlib drag overlapping points interactively

我修改了一些代码以通过 FigureCanvas 的子类使用它(稍后将图形包含在 PyQt 应用程序中):

import matplotlib.pyplot as plt
import matplotlib.patches as patches


class DraggablePoint:

    # https://stackoverflow.com/questions/21654008/matplotlib-drag-overlapping-points-interactively

    lock = None #  only one can be animated at a time

    def __init__(self, parent, x=0.1, y=0.1):

        self.parent = parent
        self.point = patches.Ellipse((x, y), 0.01, 0.03, fc='r', alpha=0.5)
        self.x = x
        self.y = y
        parent.fig.axes[0].add_patch(self.point)
        self.press = None
        self.background = None
        self.connect()


    def connect(self):

        'connect to all the events we need'

        self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)
        self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)
        self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)


    def on_press(self, event):

        if event.inaxes != self.point.axes: return
        if DraggablePoint.lock is not None: return
        contains, attrd = self.point.contains(event)
        if not contains: return
        self.press = (self.point.center), event.xdata, event.ydata
        DraggablePoint.lock = self

        # draw everything but the selected rectangle and store the pixel buffer
        canvas = self.point.figure.canvas
        axes = self.point.axes
        self.point.set_animated(True)
        canvas.draw()
        self.background = canvas.copy_from_bbox(self.point.axes.bbox)

        # now redraw just the rectangle
        axes.draw_artist(self.point)

        # and blit just the redrawn area
        canvas.blit(axes.bbox)


    def on_motion(self, event):

        if DraggablePoint.lock is not self:
            return
        if event.inaxes != self.point.axes: return
        self.point.center, xpress, ypress = self.press
        dx = event.xdata - xpress
        dy = event.ydata - ypress
        self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy)

        canvas = self.point.figure.canvas
        axes = self.point.axes
        # restore the background region
        canvas.restore_region(self.background)

        # redraw just the current rectangle
        axes.draw_artist(self.point)

        # blit just the redrawn area
        canvas.blit(axes.bbox)


    def on_release(self, event):

        'on release we reset the press data'
        if DraggablePoint.lock is not self:
            return

        self.press = None
        DraggablePoint.lock = None

        # turn off the rect animation property and reset the background
        self.point.set_animated(False)
        self.background = None

        # redraw the full figure
        self.point.figure.canvas.draw()

        self.x = self.point.center[0]
        self.y = self.point.center[1]

    def disconnect(self):

        'disconnect all the stored connection ids'

        self.point.figure.canvas.mpl_disconnect(self.cidpress)
        self.point.figure.canvas.mpl_disconnect(self.cidrelease)
        self.point.figure.canvas.mpl_disconnect(self.cidmotion)

未来图上只会有 2 个点,我可以通过 self.parent 类从 DraggablePoint 类访问另一个点。

我想我需要在函数 on_motion 中在两点之间画一条线。但我试过了,一无所获。 你知道如何实现这一目标吗?

【问题讨论】:

    标签: python matplotlib pyqt


    【解决方案1】:

    好的,我终于找到了解决方案。我把它贴在这里给那些可能需要它的人。这段代码基本上允许有 2 个可拖动的点由一条线链接。如果您移动其中一个点,则该线将跟随。在科学应用中创建基线非常有用。

    import matplotlib.pyplot as plt
    import matplotlib.patches as patches
    from matplotlib.lines import Line2D
    
    
    class DraggablePoint:
    
        # http://stackoverflow.com/questions/21654008/matplotlib-drag-overlapping-points-interactively
    
        lock = None #  only one can be animated at a time
    
        def __init__(self, parent, x=0.1, y=0.1, size=0.1):
    
            self.parent = parent
            self.point = patches.Ellipse((x, y), size, size * 3, fc='r', alpha=0.5, edgecolor='r')
            self.x = x
            self.y = y
            parent.fig.axes[0].add_patch(self.point)
            self.press = None
            self.background = None
            self.connect()
    
            if self.parent.list_points:
                line_x = [self.parent.list_points[0].x, self.x]
                line_y = [self.parent.list_points[0].y, self.y]
    
                self.line = Line2D(line_x, line_y, color='r', alpha=0.5)
                parent.fig.axes[0].add_line(self.line)
    
    
        def connect(self):
    
            'connect to all the events we need'
    
            self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)
            self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)
            self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
    
    
        def on_press(self, event):
    
            if event.inaxes != self.point.axes: return
            if DraggablePoint.lock is not None: return
            contains, attrd = self.point.contains(event)
            if not contains: return
            self.press = (self.point.center), event.xdata, event.ydata
            DraggablePoint.lock = self
    
            # draw everything but the selected rectangle and store the pixel buffer
            canvas = self.point.figure.canvas
            axes = self.point.axes
            self.point.set_animated(True)
            if self == self.parent.list_points[1]:
                self.line.set_animated(True)
            else:
                self.parent.list_points[1].line.set_animated(True)
            canvas.draw()
            self.background = canvas.copy_from_bbox(self.point.axes.bbox)
    
            # now redraw just the rectangle
            axes.draw_artist(self.point)
    
            # and blit just the redrawn area
            canvas.blit(axes.bbox)
    
    
        def on_motion(self, event):
    
            if DraggablePoint.lock is not self:
                return
            if event.inaxes != self.point.axes: return
            self.point.center, xpress, ypress = self.press
            dx = event.xdata - xpress
            dy = event.ydata - ypress
            self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy)
    
            canvas = self.point.figure.canvas
            axes = self.point.axes
            # restore the background region
            canvas.restore_region(self.background)
    
            # redraw just the current rectangle
            axes.draw_artist(self.point)
    
            if self == self.parent.list_points[1]:
                axes.draw_artist(self.line)
            else:
                self.parent.list_points[1].line.set_animated(True)
                axes.draw_artist(self.parent.list_points[1].line)
    
            self.x = self.point.center[0]
            self.y = self.point.center[1]
    
            if self == self.parent.list_points[1]:
                line_x = [self.parent.list_points[0].x, self.x]
                line_y = [self.parent.list_points[0].y, self.y]
                self.line.set_data(line_x, line_y)
            else:
                line_x = [self.x, self.parent.list_points[1].x]
                line_y = [self.y, self.parent.list_points[1].y]
    
                self.parent.list_points[1].line.set_data(line_x, line_y)
    
            # blit just the redrawn area
            canvas.blit(axes.bbox)
    
    
        def on_release(self, event):
    
            'on release we reset the press data'
            if DraggablePoint.lock is not self:
                return
    
            self.press = None
            DraggablePoint.lock = None
    
            # turn off the rect animation property and reset the background
            self.point.set_animated(False)
            if self == self.parent.list_points[1]:
                self.line.set_animated(False)
            else:
                self.parent.list_points[1].line.set_animated(False)
    
            self.background = None
    
            # redraw the full figure
            self.point.figure.canvas.draw()
    
            self.x = self.point.center[0]
            self.y = self.point.center[1]
    
        def disconnect(self):
    
            'disconnect all the stored connection ids'
    
            self.point.figure.canvas.mpl_disconnect(self.cidpress)
            self.point.figure.canvas.mpl_disconnect(self.cidrelease)
            self.point.figure.canvas.mpl_disconnect(self.cidmotion)
    

    更新:

    如何在 PyQt5 中使用 DraggablePoint 类:

    #!/usr/bin/python
    # -*-coding:Utf-8 -*
    
    import sys
    import matplotlib
    matplotlib.use("Qt5Agg")
    from PyQt5 import QtWidgets, QtGui
    
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure
    
    # Personnal modules
    from drag import DraggablePoint
    
    
    class MyGraph(FigureCanvas):
    
        """A canvas that updates itself every second with a new plot."""
    
        def __init__(self, parent=None, width=5, height=4, dpi=100):
    
            self.fig = Figure(figsize=(width, height), dpi=dpi)
            self.axes = self.fig.add_subplot(111)
    
            self.axes.grid(True)
    
            FigureCanvas.__init__(self, self.fig)
            self.setParent(parent)
    
            FigureCanvas.setSizePolicy(self,
                                       QtWidgets.QSizePolicy.Expanding,
                                       QtWidgets.QSizePolicy.Expanding)
            FigureCanvas.updateGeometry(self)
    
            # To store the 2 draggable points
            self.list_points = []
    
    
            self.show()
            self.plotDraggablePoints([0.1, 0.1], [0.2, 0.2], [0.1, 0.1])
    
    
        def plotDraggablePoints(self, xy1, xy2, size=None):
    
            """Plot and define the 2 draggable points of the baseline"""
    
            # del(self.list_points[:])
            self.list_points.append(DraggablePoint(self, xy1[0], xy1[1], size))
            self.list_points.append(DraggablePoint(self, xy2[0], xy2[1], size))
            self.updateFigure()
    
    
        def clearFigure(self):
    
            """Clear the graph"""
    
            self.axes.clear()
            self.axes.grid(True)
            del(self.list_points[:])
            self.updateFigure()
    
    
        def updateFigure(self):
    
            """Update the graph. Necessary, to call after each plot"""
    
            self.draw()
    
    if __name__ == '__main__':
    
        app = QtWidgets.QApplication(sys.argv)
        ex = MyGraph()
        sys.exit(app.exec_())
    

    【讨论】:

    • 感谢您自行回答并提供代码。如果您愿意,您现在也可以接受您的解决方案。 :)
    • 能否请您在您使用的地方或您的 DraggablePoint 点类中编写一个小代码?我对 MatPlotlib 非常陌生,但未能正确实例化并显示可拖动点:(
    • 当然。我编辑了我的答案。只需复制/粘贴新脚本,您就会得到一个带有可拖动点的画布。
    • 我认为 self.list_points.append(DraggablePoint(self, xy1[0], xy1[1], size)) 需要阅读 self.list_points.append(DraggablePoint(self, xy1[0] , xy1[1], size[0])) 或类似的东西,以避免“TypeError: can't multiply sequence by non-int of type 'float'”
    • 感谢发布如此完整的解决方案!我必须更改:self.point = patches.Ellipse((x, y), size, size * 3, fc='r', alpha=0.5, edgecolor='r')self.point = patches.Ellipse((x, y), size[0], size[1], fc='r', alpha=0.5, edgecolor='r') 才能让它工作。
    【解决方案2】:

    我需要在图中更多点,所以我修改了 JPFrancoia 完成的解决方案,以便让更多点与线相连。我希望其他人能发现它有用,所以新的 drag.py 文件内容如下:

    # drag.py
    import matplotlib.pyplot as plt
    import matplotlib.patches as patches
    from matplotlib.lines import Line2D
    
    
    class DraggablePoint:
    
        # http://stackoverflow.com/questions/21654008/matplotlib-drag-overlapping-points-interactively
    
        lock = None #  only one can be animated at a time
    
        def __init__(self, parent, x=0.1, y=0.1, size=0.1):
    
            self.parent = parent
            self.point = patches.Ellipse((x, y), size, size * 3, fc='r', alpha=0.5, edgecolor='r')
            self.x = x
            self.y = y
            parent.fig.axes[0].add_patch(self.point)
            self.press = None
            self.background = None
            self.connect()
            
            # if another point already exist we draw a line
            if self.parent.list_points:
                line_x = [self.parent.list_points[-1].x, self.x]
                line_y = [self.parent.list_points[-1].y, self.y]
    
                self.line = Line2D(line_x, line_y, color='r', alpha=0.5)
                parent.fig.axes[0].add_line(self.line)
    
    
        def connect(self):
    
            'connect to all the events we need'
    
            self.cidpress = self.point.figure.canvas.mpl_connect('button_press_event', self.on_press)
            self.cidrelease = self.point.figure.canvas.mpl_connect('button_release_event', self.on_release)
            self.cidmotion = self.point.figure.canvas.mpl_connect('motion_notify_event', self.on_motion)
    
    
        def on_press(self, event):
    
            if event.inaxes != self.point.axes: return
            if DraggablePoint.lock is not None: return
            contains, attrd = self.point.contains(event)
            if not contains: return
            self.press = (self.point.center), event.xdata, event.ydata
            DraggablePoint.lock = self
    
            # draw everything but the selected rectangle and store the pixel buffer
            canvas = self.point.figure.canvas
            axes = self.point.axes
            self.point.set_animated(True)
            
            # TODO also the line of some other points needs to be released
            point_number =  self.parent.list_points.index(self)
            
            if self == self.parent.list_points[0]:
                self.parent.list_points[1].line.set_animated(True)            
            elif self == self.parent.list_points[-1]:
                self.line.set_animated(True)            
            else:
                self.line.set_animated(True)            
                self.parent.list_points[point_number+1].line.set_animated(True)                
                
                
                
            
            canvas.draw()
            self.background = canvas.copy_from_bbox(self.point.axes.bbox)
    
            # now redraw just the rectangle
            axes.draw_artist(self.point)
    
            # and blit just the redrawn area
            canvas.blit(axes.bbox)
    
    
        def on_motion(self, event):
    
            if DraggablePoint.lock is not self:
                return
            if event.inaxes != self.point.axes: return
            self.point.center, xpress, ypress = self.press
            dx = event.xdata - xpress
            dy = event.ydata - ypress
            self.point.center = (self.point.center[0]+dx, self.point.center[1]+dy)
    
            canvas = self.point.figure.canvas
            axes = self.point.axes
            # restore the background region
            canvas.restore_region(self.background)
    
            # redraw just the current rectangle
            axes.draw_artist(self.point)
            
            point_number =  self.parent.list_points.index(self)
            self.x = self.point.center[0]
            self.y = self.point.center[1]
            
            
            
            
            
            # We check if the point is A or B        
            if self == self.parent.list_points[0]:
                # or we draw the other line of the point
                self.parent.list_points[1].line.set_animated(True)
                axes.draw_artist(self.parent.list_points[1].line)
            
            elif self == self.parent.list_points[-1]:
                # we draw the line of the point            
                axes.draw_artist(self.line)    
    
            else:
                # we draw the line of the point
                axes.draw_artist(self.line)
                #self.parent.list_points[point_number+1].line.set_animated(True)
                axes.draw_artist(self.parent.list_points[point_number+1].line)
                
                    
            
            
            if self == self.parent.list_points[0]:
                # The first point is especial because it has no line
                line_x = [self.x, self.parent.list_points[1].x]
                line_y = [self.y, self.parent.list_points[1].y]      
                # this is were the line is updated
                self.parent.list_points[1].line.set_data(line_x, line_y)
                
            elif self == self.parent.list_points[-1]:
                line_x = [self.parent.list_points[-2].x, self.x]
                line_y = [self.parent.list_points[-2].y, self.y]
                self.line.set_data(line_x, line_y)        
            else:
                # The first point is especial because it has no line
                line_x = [self.x, self.parent.list_points[point_number+1].x]
                line_y = [self.y, self.parent.list_points[point_number+1].y]      
                # this is were the line is updated
                self.parent.list_points[point_number+1].line.set_data(line_x, line_y)
                
                line_x = [self.parent.list_points[point_number-1].x, self.x]
                line_y = [self.parent.list_points[point_number-1].y, self.y]
                self.line.set_data(line_x, line_y)        
    
            # blit just the redrawn area
            canvas.blit(axes.bbox)
    
    
        def on_release(self, event):
    
            'on release we reset the press data'
            if DraggablePoint.lock is not self:
                return
    
            self.press = None
            DraggablePoint.lock = None
    
            # turn off the rect animation property and reset the background
            self.point.set_animated(False)
            
            point_number =  self.parent.list_points.index(self)
            
            if self == self.parent.list_points[0]:
                self.parent.list_points[1].line.set_animated(False)            
            elif self == self.parent.list_points[-1]:
                self.line.set_animated(False)            
            else:
                self.line.set_animated(False)            
                self.parent.list_points[point_number+1].line.set_animated(False)       
                
    
            self.background = None
    
            # redraw the full figure
            self.point.figure.canvas.draw()
    
            self.x = self.point.center[0]
            self.y = self.point.center[1]
    
        def disconnect(self):
    
            'disconnect all the stored connection ids'
    
            self.point.figure.canvas.mpl_disconnect(self.cidpress)
            self.point.figure.canvas.mpl_disconnect(self.cidrelease)
            self.point.figure.canvas.mpl_disconnect(self.cidmotion)
    

    执行如下:

    import sys
    import matplotlib
    matplotlib.use("Qt5Agg")
    from PyQt5 import QtWidgets, QtGui
    
    from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
    from matplotlib.figure import Figure
    
    # Personnal modules
    from drag import DraggablePoint
    
    
    class MyGraph(FigureCanvas):
    
        """A canvas that updates itself every second with a new plot."""
    
        def __init__(self, parent=None, width=5, height=4, dpi=100):
    
            self.fig = Figure(figsize=(width, height), dpi=dpi)
            self.axes = self.fig.add_subplot(111)
    
            self.axes.grid(True)
    
            FigureCanvas.__init__(self, self.fig)
            self.setParent(parent)
    
            FigureCanvas.setSizePolicy(self,
                                       QtWidgets.QSizePolicy.Expanding,
                                       QtWidgets.QSizePolicy.Expanding)
            FigureCanvas.updateGeometry(self)
    
            # To store the 2 draggable points
            self.list_points = []
    
    
            self.show()
            self.plotDraggablePoints()
    
    
        def plotDraggablePoints(self, size=0.05):
    
            """Plot and define the 2 draggable points of the baseline"""
      
            # del(self.list_points[:])
            self.list_points.append(DraggablePoint(self, 0.1, 0.1, size))
            self.list_points.append(DraggablePoint(self, 0.2, 0.2, size))
            self.list_points.append(DraggablePoint(self, 0.5, 0.5, size))
            self.list_points.append(DraggablePoint(self, 0.6, 0.5, size))
            self.list_points.append(DraggablePoint(self, 0.7, 0.5, size))
    
            self.updateFigure()
    
    
        def clearFigure(self):
    
            """Clear the graph"""
    
            self.axes.clear()
            self.axes.grid(True)
            del(self.list_points[:])
            self.updateFigure()
    
    
        def updateFigure(self):
    
            """Update the graph. Necessary, to call after each plot"""
    
            self.draw()
    
    if __name__ == '__main__':
    
        app = QtWidgets.QApplication(sys.argv)
        ex = MyGraph()
        sys.exit(app.exec_())
    

    【讨论】:

      【解决方案3】:

      这是我添加或删除点的附加功能的简单解决方案。 然后,您就有了一条可拖动的分段线,其中包含点上的控件。

      尽管有事件处理,但代码很简单。欢迎改进。

      import matplotlib.pyplot as plt
      import matplotlib.patches as patches
      from matplotlib.lines import Line2D
      
      #------------------------------------------------
      listLabelPoints = []
      point_alpha_default = 0.8
      mousepress = None
      currently_dragging = False
      current_artist = None
      offset = [0,0]
      n = 0
      line_object = None
      
      #------------------------------------------------
      def on_press(event):
          global currently_dragging
          global mousepress
          currently_dragging = True
          if event.button == 3:
              mousepress = "right"
          elif event.button == 1:
              mousepress = "left"
      
      #------------------------------------------------
      def on_release(event):
          global current_artist, currently_dragging
          current_artist = None
          currently_dragging = False
      
      #------------------------------------------------
      def on_pick(event):
          global current_artist, offset, n
          global listLabelPoints
          if current_artist is None:
              current_artist = event.artist
              #print("pick ", current_artist)
              if isinstance(event.artist, patches.Circle):
                  if event.mouseevent.dblclick:
                      if mousepress == "right":
                          #print("double click right")
                          if len(ax.patches) > 2:
                              #print("\ndelete", event.artist.get_label())
                              event.artist.remove()
                              xdata = list(line_object[0].get_xdata())
                              ydata = list(line_object[0].get_ydata())
                              for i in range(0,len(xdata)):
                                  if event.artist.get_label() == listLabelPoints[i]:
                                      xdata.pop(i) 
                                      ydata.pop(i) 
                                      listLabelPoints.pop(i)
                                      break
                              #print('--->', listLabelPoints)
                              line_object[0].set_data(xdata, ydata)
                              plt.draw()
                  else:
                      x0, y0 = current_artist.center
                      x1, y1 = event.mouseevent.xdata, event.mouseevent.ydata
                      offset = [(x0 - x1), (y0 - y1)]
              elif isinstance(event.artist, Line2D):
                  if event.mouseevent.dblclick:
                      if mousepress == "left":
                          #print("double click left")
                          n = n+1
                          x, y = event.mouseevent.xdata, event.mouseevent.ydata
                          newPointLabel = "point"+str(n)
                          point_object = patches.Circle([x, y], radius=50, color='r', fill=False, lw=2,
                                  alpha=point_alpha_default, transform=ax.transData, label=newPointLabel)
                          point_object.set_picker(5)
                          ax.add_patch(point_object)
                          xdata = list(line_object[0].get_xdata())
                          ydata = list(line_object[0].get_ydata())
                          #print('\ninit', listLabelPoints)
                          pointInserted = False
                          for i in range(0,len(xdata)-1):
                              #print("--> testing inclusion %s in [%s-%s]" 
                              #        %(newPointLabel, listLabelPoints[i], listLabelPoints[i+1]))
                              #print('----->', min(xdata[i],xdata[i+1]), '<', x, '<', max(xdata[i],xdata[i+1]))
                              #print('----->', min(ydata[i],ydata[i+1]), '<', y, '<', max(ydata[i],ydata[i+1]))
                              if x > min(xdata[i],xdata[i+1]) and x < max(xdata[i],xdata[i+1]) and \
                                 y > min(ydata[i],ydata[i+1]) and y < max(ydata[i],ydata[i+1]) :
                                  xdata.insert(i+1, x)
                                  ydata.insert(i+1, y)
                                  listLabelPoints.insert(i+1, newPointLabel)
                                  pointInserted = True
                                  #print("include", newPointLabel)
                                  break
                          line_object[0].set_data(xdata, ydata)
                          #print('final', listLabelPoints)
                          plt.draw()
                          if not pointInserted:
                              print("Error: point not inserted")
                  else:
                      xdata = event.artist.get_xdata()
                      ydata = event.artist.get_ydata()
                      x1, y1 = event.mouseevent.xdata, event.mouseevent.ydata
                      offset = xdata[0] - x1, ydata[0] - y1
      
      #------------------------------------------------
      def on_motion(event):
          global current_artist
          if not currently_dragging:
              return
          if current_artist == None:
              return
          if event.xdata == None:
              return
          dx, dy = offset
          if isinstance(current_artist, patches.Circle):
              cx, cy =  event.xdata + dx, event.ydata + dy
              current_artist.center = cx, cy
              #print("moving", current_artist.get_label())
              xdata = list(line_object[0].get_xdata())
              ydata = list(line_object[0].get_ydata())
              for i in range(0,len(xdata)): 
                  if listLabelPoints[i] == current_artist.get_label():
                      xdata[i] = cx
                      ydata[i] = cy
                      break
              line_object[0].set_data(xdata, ydata)
          elif isinstance(current_artist, Line2D):
              xdata = list(line_object[0].get_xdata())
              ydata = list(line_object[0].get_ydata())
              xdata0 = xdata[0]
              ydata0 = ydata[0]
              for i in range(0,len(xdata)): 
                      xdata[i] = event.xdata + dx + xdata[i] - xdata0
                      ydata[i] = event.ydata + dy + ydata[i] - ydata0 
              line_object[0].set_data(xdata, ydata)
              for p in ax.patches:
                  pointLabel = p.get_label()
                  i = listLabelPoints.index(pointLabel) 
                  p.center = xdata[i], ydata[i]
          plt.draw()
      
      #------------------------------------------------
      def on_click(event):
          global n, line_object
          if event and event.dblclick:
              if len(listLabelPoints) < 2:
                  n = n+1
                  x, y = event.xdata, event.ydata
                  newPointLabel = "point"+str(n)
                  point_object = patches.Circle([x, y], radius=50, color='r', fill=False, lw=2,
                          alpha=point_alpha_default, transform=ax.transData, label=newPointLabel)
                  point_object.set_picker(5)
                  ax.add_patch(point_object)
                  listLabelPoints.append(newPointLabel)
                  if len(listLabelPoints) == 2:
                      xdata = []
                      ydata = []
                      for p in ax.patches:
                          cx, cy = p.center
                          xdata.append(cx)
                          ydata.append(cy)
                      line_object = ax.plot(xdata, ydata, alpha=0.5, c='r', lw=2, picker=True)
                      line_object[0].set_pickradius(5)
                  plt.draw()
      
      #================================================
      fig, ax = plt.subplots()
      
      ax.set_title("Double click left button to create draggable point\nDouble click right to remove a point", loc="left")
      ax.set_xlim(0, 4000)
      ax.set_ylim(0, 3000)
      ax.set_aspect('equal')
      
      fig.canvas.mpl_connect('button_press_event', on_click)
      fig.canvas.mpl_connect('button_press_event', on_press)
      fig.canvas.mpl_connect('button_release_event', on_release)
      fig.canvas.mpl_connect('pick_event', on_pick)
      fig.canvas.mpl_connect('motion_notify_event', on_motion)
      
      plt.grid(True)
      plt.show()
      

      【讨论】:

        猜你喜欢
        • 2018-01-19
        • 2012-11-13
        • 1970-01-01
        • 1970-01-01
        • 2014-09-25
        • 1970-01-01
        • 1970-01-01
        • 2013-11-20
        • 2010-12-31
        相关资源
        最近更新 更多