2016-09-01 3 views
2

나는 플롯 된 데이터를 시각적으로 윈도우에 표시 할 수 있도록 드래그 가능한 라인이있는 간단한 GUI로 작업하고 있습니다. 때,matplotlib contains를 하나의 객체로 제한하려면 어떻게합니까?

fig = plt.figure() 
ax = fig.add_subplot(111) 

vl1 = DraggableLine('vertical', ax, 3) 
vl2 = DraggableLine('vertical', ax, 6) 

ax.set_xlim([0, 10]) 
plt.show() 

을하지만 :

import numpy as np 
import matplotlib.pyplot as plt 

class DraggableLine: 
    def __init__(self, orientation, ax, position): 
     if orientation.lower() == 'horizontal': 
      self.myline, = ax.plot(ax.get_xlim(), np.array([1, 1])*position) 
      self.orientation = orientation.lower() 
     elif orientation.lower() == 'vertical': 
      self.myline, = ax.plot(np.array([1, 1])*position, ax.get_ylim()) 
      self.orientation = orientation.lower() 
     else: 
      # throw an error 
      pass 

     self.parentfig = self.myline.figure.canvas 
     self.parentax = ax 

     self.clickpress = self.parentfig.mpl_connect('button_press_event', self.on_click) # Execute on mouse click 
     self.clicked = False 

    def on_click(self, event): 
     # Executed on mouse click 
     if event.inaxes != self.parentax: return # See if the mouse is over the parent axes object 

     # See if the click is on top of this line object 
     contains, attrs = self.myline.contains(event) 
     if not contains: return 

     self.mousemotion = self.parentfig.mpl_connect('motion_notify_event', self.on_motion) 
     self.clickrelease = self.parentfig.mpl_connect('button_release_event', self.on_release) 
     self.clicked = True 

    def on_motion(self, event): 
     # Executed on mouse motion 
     if not self.clicked: return # See if we've clicked yet 
     if event.inaxes != self.parentax: return # See if we're moving over the parent axes object 

     if self.orientation == 'vertical': 
      self.myline.set_xdata(np.array([1, 1])*event.xdata) 
      self.myline.set_ydata(self.parentax.get_ylim()) 
     elif self.orientation == 'horizontal': 
      self.myline.set_xdata(self.parentax.get_xlim()) 
      self.myline.set_ydata(np.array([1, 1])*event.ydata) 

     self.parentfig.draw() 

    def on_release(self, event): 
     self.clicked = False 

     self.parentfig.mpl_disconnect(self.mousemotion) 
     self.parentfig.mpl_disconnect(self.clickrelease) 
     self.parentfig.draw() 

예상대로 작동 라인을 생성 matplotlib's event handling documentation 작업

나는 드래그 윈도우 라인의 초기 버전을 구현 할 수있었습니다 라인이 쌓이면 matplotlib.lines.Line2D.contains()은 하나의 오브젝트가 다른 오브젝트에 의해 가려져 있다는 것을 모르기 때문에 단일 라인을 이동하는 기능이 손실됩니다. 그래서 우리는 플롯이 닫힐 때까지 물체의 덩어리를 끌고 떠납니다.

이 문제를 완화하기 위해 이미 구현 된 방법이 있습니까? 그렇지 않다면 마우스를 놓을 때 DraggableLine 클래스의 인스턴스에 대해 부모 축의 자식을 쿼리하고 위치를 확인한 다음 필요한 경우 'button_press_event'을 연결/연결 해제하는 방법을 생각해 볼 수 있습니다. 그게 가장 합리적인 계산 시간을 현명하게하는지 모르겠습니다.

+0

그것은 생각할 수 없다 줄 사이에 어떤 종류의 계층 구조를 만들지 만 둘 다 클릭하면 단 하나만 움직이십시오. 나는 그것을 위해 전역이 필요하거나 두 번째 생성자에 첫 번째 줄을 전달해야한다고 생각한다. –

+1

@AndrasDeak 그건 내가 마지막 단락에서 얻은 것입니다. "글로벌"은 부모 축의 자식이 될 것이고이 클래스의 인스턴스를 필터링 할 수 있습니다. – excaza

답변

2

하나의 접근법은 각각의 "이동"콜백을 실행하는 객체에 대한 축의 하위를 검사하고 어느 것이 맨 위에 렌더링되고 그 중 하나만 이동하는지 확인하는 것일 수 있습니다. 내가 추가 방법을 정의한 위의 예

는 :

def shouldthismove(self, event): 
    # Check to see if this object has been clicked on 
    contains, attrs = self.myline.contains(event) 
    if not contains: 
     # We haven't been clicked 
     timetomove = False 
    else: 
     # See how many draggable objects contains this event 
     firingobjs = [] 
     for child in self.parentax.get_children(): 
      if child._label == 'dragobj': 
       contains, attrs = child.contains(event) 
       if contains: 
        firingobjs.append(child) 

     # Assume the last child object is the topmost rendered object, only move if we're it 
     if firingobjs[-1] == self.myline: 
      timetomove = True 
     else: 
      timetomove = False 

    return timetomove 

on_click 방법을 재정의 :

def on_click(self, event): 
    # Executed on mouse click 
    if event.inaxes != self.parentax: return # See if the mouse is over the parent axes object 

    # Check for overlaps, make sure we only fire for one object per click 
    timetomove = self.shouldthismove(event) 
    if not timetomove: return 

    self.mousemotion = self.parentfig.canvas.mpl_connect('motion_notify_event', self.on_motion) 
    self.clickrelease = self.parentfig.canvas.mpl_connect('button_release_event', self.on_release) 
    self.clicked = True 

을 그리고 필터링을 신속하게 __init__에 내 라인 객체에 일반 레이블을 추가 아이들 축 나중에 :

self.myline._label = 'dragobj' 
관련 문제