class MyGraphWindow(QMainWindow): ALPHA_OTHER = 0.2 #lasso非選択データの透明度 PICKER = 5 #lineをクリックしたときに取得できるための領域 def __init__(self, fig=None, geo=None, title='temp'): super().__init__() #mutableなオブジェクトをデフォルトに指定するとまずいのでこのように書く if fig == None: self.fig = Figure(figsize=(6, 6), dpi=100) else: self.fig = fig if geo == None: self.geo = QRect(30, 30, 500, 500) else: self.geo = geo self.canvas = FigureCanvas(self.fig) self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.navi_toolbar = NavigationToolbar(self.canvas, self) self.lassoAction = QAction('lasso', self.canvas) self.pointerAction = QAction('pointer', self.canvas) self.lineAction = QAction('line', self.canvas) self.lassoAction.setCheckable(True) self.pointerAction.setCheckable(True) self.lineAction.setCheckable(True) self.navi_toolbar.addAction(self.lassoAction) self.navi_toolbar.addAction(self.pointerAction) self.navi_toolbar.addAction(self.lineAction) self.lassoAction.triggered.connect(self.lassoTriggered) self.pointerAction.triggered.connect(self.pointerTriggered) self.lineAction.triggered.connect(self.lineTriggered) self.lassoTarget = None #選択対象 lasso時はcilickでalphaを下げる self.selectedLine = None #選択中の点をself.lassoTargetの上にプロットする self.pre_alpha = 1 #alphaを下げる前の値 self.pre_settings = None #markerの色とサイズselectedLineをplotする時に使う #setTargetで指定したlineの点とそこからlassoで選ばれたindexを保存 self.xys = None #numpy.array 列数2の行列 self.selectedIndices = set() self.unselectedIndices = set() #クリックされたlineを記憶しておいてgetLineで参照を渡す self.line = None self.pre_width = 0 #太くした後元に戻すのに使う #点の座標を表示する self.pointer = MyPointer() self.pointer.deactivate() self.vbl = QVBoxLayout() self.vbl.addWidget(self.navi_toolbar) self.vbl.addWidget(self.canvas) self.vbl.addWidget(self.pointer) self.frontPanel = QWidget() self.frontPanel.setLayout(self.vbl) self.setCentralWidget(self.frontPanel) self.setGeometry(self.geo) self.setWindowTitle(title) self.show() def __reduce_ex__(self, proto): #pickleのためのメソッド QMainWindowはそのままpickleできないことに注意 return geneMyGraph, (self.fig, self.geometry(), self.windowTitle()) def getSelected(self): points = self.xys[list(self.selectedIndices)] return points def getUnSelected(self): points = self.xys[list(self.unselectedIndices)] return points def getLine(self): return self.line def plot(self, *args, **kwargs): #pickerはlineの選択に使うので必要 add_kwargs = self.addPickerToValue(kwargs) try: #axが指定されていたらそのaxに対して描画を行う axが無いまたはaxオブジェクト以外が指定されていると引数にaxが無い状態で次 ax = kwargs.pop('ax') ax.plot(*args, **add_kwargs) except: #無ければself.figの最初のaxに描画 それもなければ新たにaxを追加して描画 try: ax = self.fig.get_axes()[0] ax.plot(*args, **add_kwargs) except: ax = self.fig.add_subplot(111) ax.plot(*args, **add_kwargs) self.canvas.draw() def addPickerToValue(self, kwargs): #pickerが無いとlassoで選択できないのでユーザーが引数にpickerを指定しなかった場合はpickerを加える if 'picker' in list(kwargs.keys()): return kwargs else: kwargs['picker'] = self.PICKER return kwargs def lassoTriggered(self): if self.lassoAction.isChecked() == True: #pointer Off lasso On if self.pointerAction.isChecked() == True: self.pointerAction.setChecked(False) self.pointer.deactivate() if self.lineAction.isChecked() == True: self.lineAction.setChecked(False) self.removeLine() self.setLasso() else: self.removeLasso() def pointerTriggered(self): if self.pointerAction.isChecked() == True: #pointer on lasso off if self.lassoAction.isChecked() == True: self.lassoAction.setChecked(False) self.removeLasso() if self.lineAction.isChecked() == True: self.lineAction.setChecked(False) self.removeLine() self.pointer.activate(self.canvas) else: self.pointer.deactivate() def lineTriggered(self): if self.lineAction.isChecked() == True: #他をoff if self.lassoAction.isChecked() == True: self.lassoAction.setChecked(False) self.removeLasso() if self.pointerAction.isChecked() == True: self.pointerAction.setChecked(False) self.pointer.deactivate() self.setLine() else: self.removeLine() def setLine(self): self.linepick = self.canvas.mpl_connect('pick_event', self.highlightLine) def removeLine(self): try: self.canvas.mpl_disconnect(self.linepick) if not self.line == None: #選択されていたものを元に戻す self.line.set_linewidth(self.pre_width) self.canvas.draw() self.line = None self.pre_width = 0 except: print('line is still not sellected') def highlightLine(self, event): #選択中のlineの輪郭を太くする if not len(event.ind): return True if not self.line == None: #初回のclickでない場合は先に選択されていたものを元に戻す self.line.set_linewidth(self.pre_width) #plotの設定を保存 self.line = event.artist self.pre_width = self.line.get_linewidth() #alphaの調整とselectedLineの描画 まだinvisible データの取得 self.line.set_linewidth(self.pre_width * 5) self.canvas.draw() def setLasso(self): self.pick = self.canvas.mpl_connect('pick_event', self.setTarget) def removeLasso(self): try: self.canvas.mpl_disconnect(self.pick) del self.lasso if not self.lassoTarget == None: #先に選択されていたものを元に戻す self.lassoTarget.set_alpha(self.pre_alpha) self.selectedLine.remove() self.canvas.draw() #多分必要ないが念のために初期化 self.lassoTarget = None self.selectedLine = None self.pre_alpha = 1 self.pre_settings = None self.xys = None self.selectedIndices = set() self.unselectedIndices = set() except: print('Lasso is still not setted') def setTarget(self, event): #lassoの対象をclickから決定する if not len(event.ind): return True if not self.lassoTarget == None: #初回のclickでない場合は先に選択されていたものを元に戻す self.lassoTarget.set_alpha(self.pre_alpha) try: #あったら掃除 多分いつもある self.selectedLine.remove() except: pass self.selectedIndices = set() self.unselectedIndices = set() #plotの設定を保存 self.lassoTarget = event.artist self.pre_alpha = self.lassoTarget.get_alpha() self.pre_settings = self.getSettings(self.lassoTarget) #alphaの調整とselectedLineの描画 まだinvisible データの取得 self.lassoTarget.set_alpha(self.ALPHA_OTHER) self.selectedLine, = self.lassoTarget.axes.plot([], [], visible=False, linestyle='None', **self.pre_settings) self.lasso = LassoSelector(self.lassoTarget.axes, onselect=self.onselect) self.xys = np.array([[x, y] for x, y in zip( self.lassoTarget.get_xdata(), self.lassoTarget.get_ydata())]) self.canvas.draw() def getSettings(self, line): settings = { 'color': line.get_color(), 'marker': line.get_marker(), 'markersize': line.get_markersize() } return settings def onselect(self, verts): path = Path(verts) try: selected_ind = set( np.nonzero([path.contains_point(xy) for xy in self.xys])[0]) all_ind = set(range(self.xys.shape[0])) unselected_ind = all_ind - selected_ind except: print('canot determin selected points') #選択される度にselcectedlineに追加 self.selectedIndices = self.selectedIndices | selected_ind self.unselectedIndices = self.unselectedIndices | unselected_ind self.selectedLine.set_visible(True) points = self.xys[list(self.selectedIndices)] self.selectedLine.set_data(points[:, 0], points[:, 1]) self.canvas.draw()