Python 窗口选择多个艺术家并将其拖动到画布上 目标

Python 窗口选择多个艺术家并将其拖动到画布上 目标,python,matplotlib,Python,Matplotlib,我想同时: 1) 通过窗口选择在matplotlib画布上选择多个美工人员 我的意思是,如果我按住鼠标键,拖动鼠标, 然后释放,由x、y位置定义的矩形中的所有美工人员 选择按钮按下和按钮释放的顺序 2) 拖动多个选定的艺术家,然后在其上方按下鼠标 选定的艺术家之一,移动鼠标,然后释放 这与普通文件浏览器中的预期行为完全相同 以前的尝试和遗留问题 为此,我开始编写两个类,WindowSelect和 Draggable,如下所示 WindowSelect实现(1)的逻辑,除了I 不知道如何手动触发选

我想同时:

1) 通过窗口选择在matplotlib画布上选择多个美工人员

我的意思是,如果我按住鼠标键,拖动鼠标, 然后释放,由x、y位置定义的矩形中的所有美工人员 选择按钮按下和按钮释放的顺序

2) 拖动多个选定的艺术家,然后在其上方按下鼠标 选定的艺术家之一,移动鼠标,然后释放

这与普通文件浏览器中的预期行为完全相同

以前的尝试和遗留问题 为此,我开始编写两个类,
WindowSelect
Draggable
,如下所示

WindowSelect
实现(1)的逻辑,除了I 不知道如何手动触发选择器功能 在本文件中提到。 相反,我调用一个占位符函数
func

Draggable
实现(2)的逻辑,除了 一次只挑选一个艺术家(无耻地从 另一位代表(答复是这样的)。这个 指示应同时拾取多个艺术家 可能(网页上的最后一个示例)。然而,如果我 例如,将选择器的公差设置为非常高,只有一个 艺术家似乎被选中/然后可以在屏幕上拖动 画布,因此我不确定代码需要如何更改以适应 多位艺术家

代码
将numpy导入为np
导入matplotlib.pyplot作为plt;plt.ion()
将matplotlib.patches导入为修补程序
类WindowsSelect(对象):
定义初始(自我,艺术家):
self.artists=艺术家
self.canvases=set(在self.artists中艺术家的artist.figure.canvas)
对于self.canvas中的画布:
canvas.mpl\u connect('button\u press\u event',self.on\u press)
canvas.mpl\u connect('button\u release\u event',self.on\u release)
self.current\u drawing=False
def on_按下(自身,事件):
如果不是自拖动,则当前拖动:
self.x0=event.xdata
self.y0=event.ydata
self.current\u drawing=True
def on_释放(自身、事件):
如果self.u正在拖动:
self.x1=event.xdata
self.y1=event.ydata
对于self.artists中的艺术家:
如果self.is_in_rect(*艺术家中心):
self.func(艺术家)
对于self.canvas中的画布:
canvas.draw()
self.current\u drawing=False
def为内部矩形(自、x、y):
xlim=np.sort([self.x0,self.x1])
ylim=np.sort([self.y0,self.y1])

if(xlim[0]您的第二个类需要的唯一修改是一次跟踪和移动多个
艺术家
。修改以下函数将允许此操作

def on_pick(self, event):
    if self.current_artist is None:
        self.current_artist = event.artist
        ecoord = array([event.mouseevent.xdata, event.mouseevent.ydata])
        self.offsets = [a.center for a in self.artists] - ecoord

def on_motion(self, event):
    if not self.currently_dragging:
        return
    if self.current_artist is None:
        return

    center = event.xdata, event.ydata
    for artist, delta in zip(self.artists, self.offsets):
        artist.center = center + delta
    self.artists[0].figure.canvas.draw()

很难将选择和拖动分开。这是因为根据您当前是选择还是拖动,相应的事件需要执行不同的任务。因此,在每个时间点,您都需要知道当前正在执行的操作以及最好在单个类中执行的操作,您可以在单个类中执行这些操作有两个不同的标志,
self.Current\u selecting
self.Current\u Draging
。您还需要一个选定艺术家的列表,
self.selected\u Artisters
,可以在需要时进行拖动

我会去掉
pick_事件
,因为它很难与
按钮_press_事件
区分开来(它们会同时触发,并且不确定哪个先发生)。相反,一个
按钮\u press\u event
可以处理所有逻辑;即,找出是否在艺术家身上单击,如果是,开始拖动,否则开始选择

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches


class WindowSelect(object):

    def __init__(self, artists):
        self.artists = artists
        self.colors = [a.get_facecolor() for a in self.artists]
        # assume all artists are in the same figure, otherwise selection is meaningless
        self.fig = self.artists[0].figure
        self.ax = self.artists[0].axes

        self.fig.canvas.mpl_connect('button_press_event', self.on_press)
        self.fig.canvas.mpl_connect('button_release_event', self.on_release)
        self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)

        self.currently_selecting = False
        self.currently_dragging = False
        self.selected_artists = []
        self.offset = np.zeros((1,2))
        self.rect = plt.Rectangle((0,0),1,1, linestyle="--",
                                  edgecolor="crimson", fill=False)
        self.ax.add_patch(self.rect)
        self.rect.set_visible(False)

    def on_press(self, event):
        # is the press over some artist
        isonartist = False
        for artist in self.artists:
            if artist.contains(event)[0]:
                isonartist = artist
        self.x0 = event.xdata
        self.y0 = event.ydata
        if isonartist:
            # add clicked artist to selection
            self.select_artist(isonartist)
            # start dragging
            self.currently_dragging = True
            ac = np.array([a.center for a in self.selected_artists])
            ec = np.array([event.xdata, event.ydata])
            self.offset = ac - ec
        else:
            #start selecting
            self.currently_selecting = True
            self.deseclect_artists()

    def on_release(self, event):
        if self.currently_selecting:

            for artist in self.artists:
                if self.is_inside_rect(*artist.center):
                    self.select_artist(artist)
            self.fig.canvas.draw_idle()
            self.currently_selecting = False
            self.rect.set_visible(False)

        elif self.currently_dragging:
            self.currently_dragging = False


    def on_motion(self, event):
        if self.currently_dragging:
            newcenters = np.array([event.xdata, event.ydata])+self.offset
            for i, artist in enumerate(self.selected_artists):
                artist.center = newcenters[i]
            self.fig.canvas.draw_idle()
        elif self.currently_selecting:
            self.x1 = event.xdata
            self.y1 = event.ydata
            #add rectangle for selection here
            self.selector_on()
            self.fig.canvas.draw_idle()

    def is_inside_rect(self, x, y):
        xlim = np.sort([self.x0, self.x1])
        ylim = np.sort([self.y0, self.y1])
        if (xlim[0]<=x) and (x<xlim[1]) and (ylim[0]<=y) and (y<ylim[1]):
            return True
        else:
            return False

    def select_artist(self, artist):
        artist.set_color('k')
        if artist not in self.selected_artists:
            self.selected_artists.append(artist)

    def deseclect_artists(self):
        for artist,color in zip(self.artists, self.colors):
            artist.set_color(color)
        self.selected_artists = []

    def selector_on(self):
        self.rect.set_visible(True)
        xlim = np.sort([self.x0, self.x1])
        ylim = np.sort([self.y0, self.y1])
        self.rect.set_xy((xlim[0],ylim[0] ) )
        self.rect.set_width(np.diff(xlim))
        self.rect.set_height(np.diff(ylim))


def demo():

    fig, ax = plt.subplots(1,1)
    xlim = [-5, 5]
    ylim = [-5, 5]
    ax.set(xlim=xlim, ylim=ylim)

    circles = [patches.Circle((3.0, 3.0), 0.5, fc='r', alpha=1.0),
               patches.Circle((0.0, 0.0), 0.5, fc='b', alpha=1.0),
               patches.Circle((0.0, 3.0), 0.5, fc='g', alpha=1.0)]

    for circle in circles:
        ax.add_patch(circle)

    w = WindowSelect(circles)
    plt.show()

if __name__ == '__main__':
    demo()
将numpy导入为np
将matplotlib.pyplot作为plt导入
将matplotlib.patches导入为修补程序
类WindowsSelect(对象):
定义初始(自我,艺术家):
self.artists=艺术家
self.colors=[a.获取self.artists中a的\u facecolor()
#假设所有艺术家都在同一个图形中,否则选择将毫无意义
self.fig=self.artists[0]。图
self.ax=self.artists[0]。轴
self.fig.canvas.mpl\u connect(“按钮按下事件”,self.on\u按下)
self.fig.canvas.mpl\u connect('button\u release\u event',self.on\u release)
self.fig.canvas.mpl\u connect('motion\u notify\u event',self.on\u motion)
self.current\u selection=False
self.current\u drawing=False
self.selected_艺术家=[]
自偏移=np.零((1,2))
self.rect=plt.Rectangle((0,0),1,1,linestyle=“-”,
edgecolor=“深红色”,填充=假)
self.ax.add_补丁(self.rect)
self.rect.set_可见(False)
def on_按下(自身,事件):
#新闻界对某位艺术家有兴趣吗
同位语者=假
对于self.artists中的艺术家:
如果艺术家包含(事件)[0]:
艺术家
self.x0=event.xdata
self.y0=event.ydata
如果是同构者:
#将单击的艺术家添加到选择中
自我选择艺术家(isonartist)
#开始拖动
self.current\u drawing=True
ac=np.array([a.center for a in self.selected_美工])
ec=np.array([event.xdata,event.ydata])
自偏移=ac-ec
其他:
#开始选择
self.current\u selecting=True
self.deseclect_艺术家()
def on_释放(自身、事件):
如果self.u当前正在选择:
对于self.artists中的艺术家:
如果self.is_in_rect(*艺术家中心):
自我选择艺术家(艺术家)
self.fig.canvas.draw_idle()
self.current\u selection=False
self.rect.set_可见(False)
艾利夫自我电流
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches


class WindowSelect(object):

    def __init__(self, artists):
        self.artists = artists
        self.colors = [a.get_facecolor() for a in self.artists]
        # assume all artists are in the same figure, otherwise selection is meaningless
        self.fig = self.artists[0].figure
        self.ax = self.artists[0].axes

        self.fig.canvas.mpl_connect('button_press_event', self.on_press)
        self.fig.canvas.mpl_connect('button_release_event', self.on_release)
        self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion)

        self.currently_selecting = False
        self.currently_dragging = False
        self.selected_artists = []
        self.offset = np.zeros((1,2))
        self.rect = plt.Rectangle((0,0),1,1, linestyle="--",
                                  edgecolor="crimson", fill=False)
        self.ax.add_patch(self.rect)
        self.rect.set_visible(False)

    def on_press(self, event):
        # is the press over some artist
        isonartist = False
        for artist in self.artists:
            if artist.contains(event)[0]:
                isonartist = artist
        self.x0 = event.xdata
        self.y0 = event.ydata
        if isonartist:
            # add clicked artist to selection
            self.select_artist(isonartist)
            # start dragging
            self.currently_dragging = True
            ac = np.array([a.center for a in self.selected_artists])
            ec = np.array([event.xdata, event.ydata])
            self.offset = ac - ec
        else:
            #start selecting
            self.currently_selecting = True
            self.deseclect_artists()

    def on_release(self, event):
        if self.currently_selecting:

            for artist in self.artists:
                if self.is_inside_rect(*artist.center):
                    self.select_artist(artist)
            self.fig.canvas.draw_idle()
            self.currently_selecting = False
            self.rect.set_visible(False)

        elif self.currently_dragging:
            self.currently_dragging = False


    def on_motion(self, event):
        if self.currently_dragging:
            newcenters = np.array([event.xdata, event.ydata])+self.offset
            for i, artist in enumerate(self.selected_artists):
                artist.center = newcenters[i]
            self.fig.canvas.draw_idle()
        elif self.currently_selecting:
            self.x1 = event.xdata
            self.y1 = event.ydata
            #add rectangle for selection here
            self.selector_on()
            self.fig.canvas.draw_idle()

    def is_inside_rect(self, x, y):
        xlim = np.sort([self.x0, self.x1])
        ylim = np.sort([self.y0, self.y1])
        if (xlim[0]<=x) and (x<xlim[1]) and (ylim[0]<=y) and (y<ylim[1]):
            return True
        else:
            return False

    def select_artist(self, artist):
        artist.set_color('k')
        if artist not in self.selected_artists:
            self.selected_artists.append(artist)

    def deseclect_artists(self):
        for artist,color in zip(self.artists, self.colors):
            artist.set_color(color)
        self.selected_artists = []

    def selector_on(self):
        self.rect.set_visible(True)
        xlim = np.sort([self.x0, self.x1])
        ylim = np.sort([self.y0, self.y1])
        self.rect.set_xy((xlim[0],ylim[0] ) )
        self.rect.set_width(np.diff(xlim))
        self.rect.set_height(np.diff(ylim))


def demo():

    fig, ax = plt.subplots(1,1)
    xlim = [-5, 5]
    ylim = [-5, 5]
    ax.set(xlim=xlim, ylim=ylim)

    circles = [patches.Circle((3.0, 3.0), 0.5, fc='r', alpha=1.0),
               patches.Circle((0.0, 0.0), 0.5, fc='b', alpha=1.0),
               patches.Circle((0.0, 3.0), 0.5, fc='g', alpha=1.0)]

    for circle in circles:
        ax.add_patch(circle)

    w = WindowSelect(circles)
    plt.show()

if __name__ == '__main__':
    demo()