class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__() self.setWindowTitle('Data Analysis for NSOR project') self.setWindowIcon( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\window_icon.png')) ''' q actions that are intend to be in menu or toolbar ''' openFile = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\open_file.png'), '&Open File...', self) openFile.setShortcut('Ctrl+O') openFile.setStatusTip('Open the data file') openFile.triggered.connect(self.open_file) exitProgram = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\exit_program.png'), '&Exit', self) exitProgram.setShortcut("Ctrl+W") exitProgram.setStatusTip('Close the Program') exitProgram.triggered.connect(self.exit_program) renewData = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\renew.png'), '&Renew', self) renewData.setShortcut("Ctrl+R") renewData.setStatusTip('Reload the original data') renewData.triggered.connect(self.renew_data) self.verticalZoom = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\vertical_zoom.png'), '&Vertical Zoom', self) self.verticalZoom.setShortcut("Ctrl+Shift+V") self.verticalZoom.setStatusTip('Zoom in the vertical direction') self.verticalZoom.setCheckable(True) self.verticalZoom.toggled.connect(self.vzoom) self.horizontalZoom = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\horizontal_zoom.png'), '&Horizontal Zoom', self) self.horizontalZoom.setShortcut("Ctrl+Shift+H") self.horizontalZoom.setStatusTip('Zoom in the horizaontal direction') self.horizontalZoom.setCheckable(True) self.horizontalZoom.toggled.connect(self.hzoom) self.moveCursor = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\move_cursor.png'), '&Move Cursor', self) self.moveCursor.setShortcut("Ctrl+M") self.moveCursor.setStatusTip('Move cursors') self.moveCursor.setCheckable(True) self.moveCursor.toggled.connect(self.move_cursor) self.autoAxis = {} self.autoAxis['time_x'] = MyQAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_time_x.png'), '&Auto X axis (time)', 'time_x', self) self.autoAxis['time_y'] = MyQAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_time_y.png'), '&Auto Y axis (time)', 'time_y', self) self.autoAxis['freq_x'] = MyQAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_freq_x.png'), '&Auto X axis (freq)', 'freq_x', self) self.autoAxis['freq_y'] = MyQAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_freq_y.png'), '&Auto Y axis (freq)', 'freq_y', self) editParameters = QAction('&Edit Parameter', self) editParameters.setShortcut('Ctrl+E') editParameters.setStatusTip('open and edit the parameter file') editParameters.triggered.connect(self.edit_parameters) saveParameters = QAction('&Save Parameter', self) saveParameters.setShortcut('Ctrl+S') saveParameters.setStatusTip('save the parameters on screen to file') saveParameters.triggered.connect(self.save_parameters) self.data_type = QComboBox() self.data_type.setStatusTip( 'bin for legacy data recorded from labview program, big endian coded binary data, npy for numpy type data' ) self.data_type.addItems(['bin', '.npy']) ''' setting menubar ''' mainMenu = self.menuBar() #create a menuBar fileMenu = mainMenu.addMenu('&File') #add a submenu to the menu bar fileMenu.addAction( openFile) # add what happens when this menu is interacted fileMenu.addSeparator() fileMenu.addAction(exitProgram) # add an exit menu parameterMenu = mainMenu.addMenu('&Parameter') parameterMenu.addAction(editParameters) parameterMenu.addAction(saveParameters) ''' setting toolbar ''' self.toolbar = self.addToolBar( 'nsor_toolbar') #add a tool bar to the window if app.desktop().screenGeometry().height() == 2160: self.toolbar.setIconSize(QSize(100, 100)) else: self.toolbar.setIconSize(QSize(60, 60)) self.toolbar.addAction( openFile) # add what happens when this tool is interacted self.toolbar.addWidget(self.data_type) self.toolbar.addAction(renewData) self.toolbar.addSeparator() self.toolbar.addAction(self.verticalZoom) self.toolbar.addAction(self.horizontalZoom) self.toolbar.addAction(self.moveCursor) self.toolbar.addSeparator() for key, item in self.autoAxis.items(): self.autoAxis[key].setStatusTip( f'set {key} axis to size automatically') self.autoAxis[key].btnToggled.connect(self.auto_axis) self.toolbar.addAction(self.autoAxis[key]) self.statusBar() #create a status bar ''' setting matplotlib ''' if app.desktop().screenGeometry().height() == 2160: matplotlib.rcParams.update({'font.size': 28}) else: matplotlib.rcParams.update({'font.size': 14}) self.canvas = FigureCanvas(Figure(figsize=(40, 9))) self.fig = self.canvas.figure ''' setting axis as dictionary, containing two axes of time and freq ax['time'] ax['freq'] also initiate the vertical lines vline['time_l'] vline['time_r'] vline['freq_l'] vline['freq_r'] ''' self.ax = {} self.vline = {} self.ax['time'] = self.fig.add_subplot(121) self.ax['freq'] = self.fig.add_subplot(122) for axis in self.ax.values(): if app.desktop().screenGeometry().height() == 2160: axis.tick_params(pad=20) elif app.desktop().screenGeometry().height() == 1080: axis.tick_params(pad=10) # axis.ticklabel_format(style='sci', axis='y', scilimits=(0,0)) self.fourier_lb = QLabel("Ready", self) self.parameters = read_parameter(PARAMETER_FILE) ''' setting edits and labels as dictionary, representing all time and freq edits "file_name" is excluded "time_x_limit" "time_y_limit" "freq_x_limit" "freq_y_imit" "time_cursor" "freq_cursor" ''' self.edits = {} labels = {} for key, value in self.parameters.items(): if type(value) == list: val = str(value[0]) + ' ' + str(value[1]) if key == 'file_name': continue labels[key] = QLabel(key.replace('_', ' ').title(), self) self.edits[key] = MyLineEdit(key, val, self) self.edits[key].setStatusTip(f'{key}') self.edits[key].textModified.connect(self.limit_and_cursor) if key[0:4] == 'freq': self.edits[key].setFixedWidth(250) if 'cursor' in key: self.vline[key[0:4] + '_l'] = self.ax[key[0:4]].axvline( float(value[0]), c='red') self.vline[key[0:4] + '_r'] = self.ax[key[0:4]].axvline( float(value[1]), c='red') self.vline[key[0:4] + '_l'].set_animated(True) self.vline[key[0:4] + '_r'].set_animated(True) self.integral_label = QLabel('Peak Intensity: \n0', self) self.zeroPadPower = QComboBox(self) self.zeroPadPower.addItems(['x1', 'x2', 'x4', 'x8']) self.zeroPadPower.setStatusTip('This sets the zerofilling of the data') self.zeroPadPower.activated[str].connect(self.zero_padding) ''' phase stuff ''' self.toolbar.addSeparator() first_order_phase_check = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_phase_check.png'), '&First order on', self) first_order_phase_check.setStatusTip('Check to enbale 1st order phase') first_order_phase_check.setShortcut('Ctrl+F') first_order_phase_check.toggled.connect(self.first_order_phase_check) first_order_phase_check.setCheckable(True) self.toolbar.addAction(first_order_phase_check) auto_phase_btn = QAction( QIcon(BASE_FOLDER + r'\pyqt_analysis\icons\auto_phase_btn.png'), '&Auto Phase', self) auto_phase_btn.setStatusTip('Auto phase the peak (0th order only)') auto_phase_btn.setShortcut('Ctrl+A') auto_phase_btn.triggered.connect(self.auto_phase) self.toolbar.addAction(auto_phase_btn) self.zeroth_slider = QSlider(self) self.zeroth_slider.setMinimum(0) self.zeroth_slider.setMaximum(360) self.zeroth_slider.setValue(0) self.zeroth_slider.setTickInterval(1) self.zeroth_slider.valueChanged.connect(self.zeroth_order_phase) self.zeroth_slider.sliderReleased.connect(self.slider_released) self.first_slider = QSlider(self) self.first_slider.setMinimum(0) self.first_slider.setMaximum(360) self.first_slider.setValue(0) self.first_slider.hide() self.first_slider.valueChanged.connect(self.first_order_phase) self.phase_info = QLabel('Current Phase: \n0th: 0\n1st: 0 \nInt: 0', self) ''' setting layout ''' self._main = QWidget() self.setCentralWidget(self._main) layout1 = QHBoxLayout(self._main) layout2 = QVBoxLayout() layout3 = QVBoxLayout() layout4 = QVBoxLayout() layout5 = QHBoxLayout() for key in labels.keys(): if key[0:4] == 'time': layout2.addWidget(labels[key]) layout2.addWidget(self.edits[key]) elif key[0:4] == 'freq': layout4.addWidget(labels[key]) layout4.addWidget(self.edits[key]) layout4.addWidget(self.integral_label) layout4.addWidget(self.phase_info) layout4.addLayout(layout5) layout5.addWidget(self.zeroth_slider) layout5.addWidget(self.first_slider) layout2.addWidget(self.zeroPadPower) layout1.addLayout(layout2) layout2.addStretch(1) layout1.addLayout(layout3) layout3.addWidget(self.canvas) layout3.addWidget(self.fourier_lb) layout1.addLayout(layout4) # layout4.addStretch(1) self.threadpool = QThreadPool() #Multithreading ''' ################################################################################ phase ''' def slider_released(self): self.canvas.draw() key = 'freq' self.ax[key[0:4]].ticklabel_format( style='sci', axis='both', scilimits=(0, 0)) # format the tick label of the axes for k in self.ax.keys(): self.ax[k].draw_artist(self.vline[k + '_l']) self.ax[k].draw_artist(self.vline[k + '_r']) def first_order_phase_check(self, toggle_state): if toggle_state: self.first_slider.show() else: self.first_slider.setValue(0) self.first_slider.hide() def auto_phase(self): try: reft = self.data['freq_y'].real[self.csL:self.csR] imft = self.data['freq_y'].imag[self.csL:self.csR] intensity_int = np.array([]) for angle in range(360): phi = angle / 360 * 2 * pi intensity_int = np.append( intensity_int, np.sum(np.cos(phi) * reft + np.sin(phi) * imft)) best_angle = intensity_int.argmax() best_phi = best_angle / 360 * 2 * pi self.zeroth_slider.setValue(best_angle) self.data['freq_real'] = self.data['freq_y'].real*np.cos(best_phi) + \ self.data['freq_y'].imag*np.sin(best_phi) self.draw_phased_data() except AttributeError: dlg = QMessageBox.warning(self, 'WARNING', 'No original data available!', QMessageBox.Ok) def zeroth_order_phase(self, value): phi = value / 360 * 2 * pi try: reft = self.data['freq_y'].real[self.csL:self.csR] imft = self.data['freq_y'].imag[self.csL:self.csR] self.data['freq_real'] = self.data['freq_y'].real*np.cos(phi) + \ self.data['freq_y'].imag*np.sin(phi) intensity = np.sum(np.cos(phi) * reft + np.sin(phi) * imft) str = self.phase_info.text() str_lst = str.split('\n') intensity_str = "{:.5f}".format(intensity * 2) self.phase_info.setText(f'Current Phase: \n0th: {value}\n' + str_lst[2] + f'\nInt: {intensity_str}') self.draw_phased_data() self.canvas.blit(self.ax['freq'].bbox) except AttributeError: dlg = QMessageBox.warning(self, 'WARNING', 'No original data available!', QMessageBox.Ok) def first_order_phase(self, value): intensity = 0 str = self.phase_info.text() str_lst = str.split('\n') intensity_str = "{:.5f}".format(intensity * 2) self.phase_info.setText('Current Phase: \n' + str_lst[1] + f'\n1st: {value}' + f'\nInt: {intensity_str}') def draw_phased_data(self): key = 'freq' self.ax[key].clear() self.ax[key].plot(self.data[key + '_x'], self.data[key + '_real']) cs_value = [ float(x) for x in self.edits[key + '_cursor'].text().split(' ') ] self.vline[key + '_l'].set_xdata([cs_value[0], cs_value[0]]) self.vline[key + '_r'].set_xdata([cs_value[1], cs_value[1]]) lm_value = [ float(x) for x in self.edits[key + '_x_limit'].text().split(' ') ] self.ax[key].set_xlim(lm_value[0], lm_value[1]) self.canvas.draw() self.ax[key].ticklabel_format( style='sci', axis='both', scilimits=(0, 0)) # format the tick label of the axes for k in self.ax.keys(): self.ax[k].draw_artist(self.vline[k + '_l']) self.ax[k].draw_artist(self.vline[k + '_r']) ''' ################################################################################ some less complicated slot ''' def edit_parameters(self): os.startfile(PARAMETER_FILE) def save_parameters(self): for key in self.parameters.keys(): if key == 'file_name': continue str = self.edits[key].text() self.parameters[key] = str.split(' ') save_parameter(PARAMETER_FILE, **self.parameters) def auto_axis(self, key): ''' auto scale the axis ''' if key != 'time_y': self.ax[key[0:4]].autoscale(axis=key[5]) else: try: average = np.mean(np.abs(self.data['time_y'])) self.ax['time'].set_ylim(-2 * average, 2 * average) except AttributeError: self.ax[key[0:4]].autoscale(axis=key[5]) self.canvas.draw() self.ax[key[0:4]].ticklabel_format( style='sci', axis='both', scilimits=(0, 0)) # format the tick label of the axes for k in self.ax.keys(): self.ax[k].draw_artist(self.vline[k + '_l']) self.ax[k].draw_artist(self.vline[k + '_r']) ''' ################################################################################ browse the figure calculate based on cursors ''' def limit_and_cursor(self, key, text): ''' respond to the change of text in the edits ''' try: value = [float(x) for x in text.split(' ')] if 'limit' in key: if 'x' in key: self.ax[key[0:4]].set_xlim(value[0], value[1]) elif 'y' in key: self.ax[key[0:4]].set_ylim(value[0], value[1]) elif 'cursor' in key: self.vline[key[0:4] + '_l'].set_xdata([value[0], value[0]]) self.vline[key[0:4] + '_r'].set_xdata([value[1], value[1]]) try: cs1 = np.argmin( np.abs(self.data[key[0:4] + '_x'] - value[0]) ) # finding the index corresponding to the time stamp cs2 = np.argmin( np.abs(self.data[key[0:4] + '_x'] - value[1])) if cs1 > cs2: self.cursor_operation(key, cs2, cs1) else: self.cursor_operation(key, cs1, cs2) except AttributeError: dlg = QMessageBox.warning(self, 'WARNING', 'No original data available!', QMessageBox.Ok) self.canvas.draw() self.ax[key[0:4]].ticklabel_format( style='sci', axis='both', scilimits=(0, 0)) # format the tick label of the axes for k in self.ax.keys(): self.ax[k].draw_artist(self.vline[k + '_l']) self.ax[k].draw_artist(self.vline[k + '_r']) except ValueError: dlg = QMessageBox.warning(self, 'WARNING', 'Input only number', QMessageBox.Ok) def cursor_operation(self, key, csL, csR): self.csL = csL self.csR = csR if 'time' in key: self.zero_padding(self.zeroPadPower.currentText(), [csL, csR]) elif 'freq' in key: intensity = (np.sum(self.data['freq_y'].real)**2 + np.sum(self.data['freq_y'].imag)**2)**(1 / 2) intensity_str = "{:.5f}".format(intensity) self.integral_label.setText( f'Peak Intensity: \n{intensity_str}') # def cursor_lines_in_axis(self, ax): if ax == self.ax['time']: line1 = self.vline['time_l'] line2 = self.vline['time_r'] else: line1 = self.vline['freq_l'] line2 = self.vline['freq_r'] return line1, line2 def move_cursor(self, state): def on_press(event): if self.in_ax: if self.current_line != None: if event.button == 1: ax = event.inaxes self.last_ax = ax self.c_lock = True self.x0 = event.xdata self.current_line.set_xdata([event.xdata, event.xdata]) line1, line2 = self.cursor_lines_in_axis(ax) self.canvas.draw() self.background = self.canvas.copy_from_bbox(ax.bbox) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) def on_motion(event): ax = event.inaxes if ax != None: line1, line2 = self.cursor_lines_in_axis(ax) if self.c_lock: self.current_line.set_xdata([event.xdata, event.xdata]) self.canvas.restore_region(self.background) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) if self.x0 > event.xdata: self.c_side = 'left' else: self.c_side = 'right' else: if abs(event.xdata - line1.get_xdata()[0]) / self.xrange <= 0.02: if self.cursor == 'arrow': QApplication.setOverrideCursor(Qt.CrossCursor) self.current_line = line1 self.cursor = 'cross' elif abs(event.xdata - line2.get_xdata()[0]) / self.xrange <= 0.02: if self.cursor == 'arrow': QApplication.setOverrideCursor(Qt.CrossCursor) self.cursor = 'cross' self.current_line = line2 else: if self.cursor == 'cross': QApplication.restoreOverrideCursor() self.cursor = 'arrow' self.current_line = None def on_release(event): if self.c_lock: self.background = None self.c_lock = False ax = event.inaxes if ax != self.last_ax: ax = self.last_ax limit = ax.get_xlim() if self.c_side == 'left': event.xdata = limit[0] else: event.xdata = limit[1] line1, line2 = self.cursor_lines_in_axis(ax) str1 = "{:.5E}".format(line1.get_xdata()[0]) str2 = "{:.5E}".format(line2.get_xdata()[0]) if line2.get_xdata()[0] < line1.get_xdata()[0]: str1, str2 = str2, str1 if ax == self.ax['freq']: self.edits['freq_cursor'].setText(str1 + ' ' + str2) else: self.edits['time_cursor'].setText(str1 + ' ' + str2) def move_in_ax(event): self.in_ax = True ax = event.inaxes xmin, xmax = ax.get_xlim() self.xrange = xmax - xmin def move_out_ax(event): self.out_ax = False if state: self.cursor = 'arrow' self.verticalZoom.setChecked(False) self.horizontalZoom.setChecked(False) self.c_lock = False self.c_onpick = False self.c_cid_press = self.canvas.mpl_connect('button_press_event', on_press) self.c_cid_release = self.canvas.mpl_connect( 'button_release_event', on_release) self.c_cid_motion = self.canvas.mpl_connect( 'motion_notify_event', on_motion) self.c_in_ax = self.canvas.mpl_connect('axes_enter_event', move_in_ax) self.c_out_ax = self.canvas.mpl_connect('axes_leave_event', move_out_ax) else: self.canvas.mpl_disconnect(self.c_cid_press) self.canvas.mpl_disconnect(self.c_cid_release) self.canvas.mpl_disconnect(self.c_cid_motion) self.canvas.mpl_disconnect(self.c_in_ax) self.canvas.mpl_disconnect(self.c_out_ax) def vzoom(self, state): def on_press(event): if self.in_ax: ax = event.inaxes line1, line2 = self.cursor_lines_in_axis(ax) try: if event.button == 1: self.vlock = True self.last_ax = ax ymin, ymax = ax.get_ylim() self.yrange = ymax - ymin xmin, xmax = ax.get_xlim() self.xrange = xmax - xmin self.y0 = event.ydata self.top_ln, = ax.plot([ event.xdata - self.xrange * 0.02, event.xdata + self.xrange * 0.02 ], [event.ydata, event.ydata]) self.btm_ln, = ax.plot([ event.xdata - self.xrange * 0.02, event.xdata + self.xrange * 0.02 ], [event.ydata, event.ydata]) self.vzoom_ln, = ax.plot([event.xdata, event.xdata], [event.ydata, event.ydata]) self.top_ln.set_color('m') self.btm_ln.set_color('m') self.vzoom_ln.set_color('m') # print(self.right_ln.get_xdata(), self.right_ln.get_ydata()) self.btm_ln.set_animated(True) self.vzoom_ln.set_animated(True) self.canvas.draw() self.background = self.canvas.copy_from_bbox(ax.bbox) ax.draw_artist(self.vzoom_ln) ax.draw_artist(self.btm_ln) line1, line2 = self.cursor_lines_in_axis(ax) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) else: self.top_ln.remove() self.vzoom_ln.remove() self.btm_ln.remove() self.canvas.draw() self.background = None self.vlock = False ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) except: print('no') def on_release(event): if self.vlock: try: self.top_ln.remove() self.vzoom_ln.remove() self.btm_ln.remove() self.canvas.draw() self.background = None self.vlock = False ax = event.inaxes if ax != self.last_ax: ax = self.last_ax limit = ax.get_ylim() if self.vside == 'btm': event.ydata = limit[0] else: event.ydata = limit[1] if self.y0 > event.ydata: self.y0, event.ydata = event.ydata, self.y0 str1 = "{:.5E}".format(self.y0) str2 = "{:.5E}".format(event.ydata) if ax == self.ax['freq']: self.edits['freq_y_limit'].setText(str1 + ' ' + str2) else: self.edits['time_y_limit'].setText(str1 + ' ' + str2) except: print('no') def on_motion(event): if self.vlock: ax = event.inaxes if ax != None: self.btm_ln.set_ydata([event.ydata, event.ydata]) self.vzoom_ln.set_ydata([self.y0, event.ydata]) self.canvas.restore_region(self.background) ax.draw_artist(self.vzoom_ln) ax.draw_artist(self.btm_ln) line1, line2 = self.cursor_lines_in_axis(ax) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) if self.y0 > event.ydata: self.vside = 'btm' else: self.vside = 'top' def move_in_ax(event): self.in_ax = True def move_out_ax(event): self.out_ax = False if state: self.horizontalZoom.setChecked(False) self.moveCursor.setChecked(False) self.vlock = False self.vcid_press = self.canvas.mpl_connect('button_press_event', on_press) self.vcid_release = self.canvas.mpl_connect( 'button_release_event', on_release) self.vcid_motion = self.canvas.mpl_connect('motion_notify_event', on_motion) self.vin_ax = self.canvas.mpl_connect('axes_enter_event', move_in_ax) self.vout_ax = self.canvas.mpl_connect('axes_leave_event', move_out_ax) else: self.canvas.mpl_disconnect(self.vcid_press) self.canvas.mpl_disconnect(self.vcid_release) self.canvas.mpl_disconnect(self.vcid_motion) self.canvas.mpl_disconnect(self.vin_ax) self.canvas.mpl_disconnect(self.vout_ax) def hzoom(self, state): def on_press(event): if self.in_ax: ax = event.inaxes line1, line2 = self.cursor_lines_in_axis(ax) try: if event.button == 1: self.hlock = True self.last_ax = ax ymin, ymax = ax.get_ylim() self.yrange = ymax - ymin xmin, xmax = ax.get_xlim() self.xrange = xmax - xmin self.x0 = event.xdata self.left_ln, = ax.plot([event.xdata, event.xdata], [ event.ydata - self.yrange * 0.02, event.ydata + self.yrange * 0.02 ]) self.right_ln, = ax.plot([event.xdata, event.xdata], [ event.ydata - self.yrange * 0.02, event.ydata + self.yrange * 0.02 ]) self.hzoom_ln, = ax.plot([event.xdata, event.xdata], [event.ydata, event.ydata]) self.left_ln.set_color('m') self.right_ln.set_color('m') self.hzoom_ln.set_color('m') # print(self.right_ln.get_xdata(), self.right_ln.get_ydata()) self.right_ln.set_animated(True) self.hzoom_ln.set_animated(True) self.canvas.draw() self.background = self.canvas.copy_from_bbox(ax.bbox) ax.draw_artist(self.hzoom_ln) ax.draw_artist(self.right_ln) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) else: self.left_ln.remove() self.hzoom_ln.remove() self.right_ln.remove() self.canvas.draw() self.background = None self.hlock = False ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) except: print('no') def on_motion(event): if self.hlock: ax = event.inaxes if ax != None: self.right_ln.set_xdata([event.xdata, event.xdata]) self.hzoom_ln.set_xdata([self.x0, event.xdata]) self.canvas.restore_region(self.background) ax.draw_artist(self.hzoom_ln) ax.draw_artist(self.right_ln) line1, line2 = self.cursor_lines_in_axis(ax) ax.draw_artist(line1) ax.draw_artist(line2) self.canvas.blit(ax.bbox) if self.x0 > event.xdata: self.hside = 'left' else: self.hside = 'right' def on_release(event): if self.hlock: try: self.left_ln.remove() self.hzoom_ln.remove() self.right_ln.remove() self.canvas.draw() self.background = None self.hlock = False ax = event.inaxes if ax != self.last_ax: ax = self.last_ax limit = ax.get_xlim() if self.hside == 'left': event.xdata = limit[0] else: event.xdata = limit[1] if self.x0 > event.xdata: self.x0, event.xdata = event.xdata, self.x0 # ax.set_xlim(self.x0, event.xdata) str1 = "{:.5E}".format(self.x0) str2 = "{:.5E}".format(event.xdata) if ax == self.ax['freq']: self.edits['freq_x_limit'].setText(str1 + ' ' + str2) else: self.edits['time_x_limit'].setText(str1 + ' ' + str2) except: print('no') def move_in_ax(event): self.in_ax = True def move_out_ax(event): self.out_ax = False if state: self.moveCursor.setChecked(False) self.verticalZoom.setChecked(False) self.hlock = False self.hcid_press = self.canvas.mpl_connect('button_press_event', on_press) self.hcid_release = self.canvas.mpl_connect( 'button_release_event', on_release) self.hcid_motion = self.canvas.mpl_connect('motion_notify_event', on_motion) self.hin_ax = self.canvas.mpl_connect('axes_enter_event', move_in_ax) self.hout_ax = self.canvas.mpl_connect('axes_leave_event', move_out_ax) else: self.canvas.mpl_disconnect(self.hcid_press) self.canvas.mpl_disconnect(self.hcid_release) self.canvas.mpl_disconnect(self.hcid_motion) self.canvas.mpl_disconnect(self.hin_ax) self.canvas.mpl_disconnect(self.hout_ax) ''' ################################################################################ Multithreading fft calculation ''' def fourier_multithreading(self, time_sig): self.fourier_lb.setText('Waiting...') fourier_worker = FourierWorker(time_sig, self.f_max) fourier_worker.signals.data.connect(self.set_fourier) fourier_worker.signals.finished.connect(self.fourier_finished) self.threadpool.start(fourier_worker) def set_fourier(self, data): self.data['freq_x'] = data[0] self.data['freq_y'] = data[1] self.draw('freq') self.edits['freq_x_limit'].returnPressed.emit() self.edits['freq_cursor'].returnPressed.emit() def fourier_finished(self): self.fourier_lb.setText('Ready') ''' ################################################################################ make zerofilling work ''' def zero_padding(self, pad_power, value=[]): if value == []: value = [float(val) for val in self.parameters['time_cursor']] cs1 = np.argmin(np.abs( self.data['time_x'] - value[0])) # finding the index corresponding to the time stamp cs2 = np.argmin(np.abs(self.data['time_x'] - value[1])) else: cs1 = value[0] cs2 = value[1] try: time_data = self.data['time_y'][cs1:cs2] pad_power = int(pad_power[1:]) x = np.ceil(np.log2(len(self.data['time_y']))) n = 2**(pad_power - 1) l = int(2**x * n) time_sig = np.pad(time_data, (0, l - len(time_data)), 'constant') self.fourier_multithreading(time_sig) except AttributeError: dlg = QMessageBox.warning(self, 'WARNING', 'No original data available!', QMessageBox.Ok) self.zeroPadPower.setCurrentIndex(0) ''' ################################################################################ other miscellaneous function ''' def renew_data(self): try: self.data['time_x'] = self.data['raw_x'] self.data['time_y'] = self.data['raw_y'] self.draw('time') self.zeroPadPower.setCurrentIndex(0) except AttributeError: dlg = QMessageBox.warning(self, 'WARNING', 'No original data available!', QMessageBox.Ok) def exit_program(self): choice = QMessageBox.question( self, 'Exiting', 'Are you sure about exit?', QMessageBox.Yes | QMessageBox.No) #Set a QMessageBox when called if choice == QMessageBox.Yes: # give actions when answered the question sys.exit() def open_file(self): ''' open file and assign data to a dictionary self.Data self.data['raw_x'] self.data['raw_y'] above two are the original data self.data['time_x'] self.data['time_y'] self.data['freq_x'] self.data['freq_y'] ''' dlg = QFileDialog() dlg.setDirectory(read_parameter(PARAMETER_FILE)['file_name']) if dlg.exec_(): file_name = dlg.selectedFiles()[0] save_parameter(PARAMETER_FILE, **{"file_name": file_name}) if str(self.data_type.currentText()) == 'bin': raw_data = np.fromfile(file_name, '>f8') elif str(self.data_type.currentText()) == '.npy': raw_data = np.load(file_name) self.data = {} self.data['raw_x'] = raw_data[::2] self.data['raw_y'] = raw_data[1::2] self.data['time_x'] = self.data['raw_x'] self.data['time_y'] = self.data['raw_y'] dt = self.data['time_x'][1] - self.data['time_x'][0] self.f_max = 1 / (2 * dt) self.fourier_multithreading(self.data['time_y']) self.edits['time_cursor'].returnPressed.emit() self.draw('time') ''' ################################################################################ ''' def draw(self, key): self.ax[key].clear() if key == 'time': self.ax[key].plot(self.data[key + '_x'], self.data[key + '_y']) elif key == 'freq': self.ax[key].plot(self.data[key + '_x'], np.abs(self.data[key + '_y'])) value = [ float(x) for x in self.edits[key + '_cursor'].text().split(' ') ] self.vline[key + '_l'].set_xdata([value[0], value[0]]) self.vline[key + '_r'].set_xdata([value[1], value[1]]) self.canvas.draw() self.ax[key].ticklabel_format( style='sci', axis='both', scilimits=(0, 0)) # format the tick label of the axes self.ax[key].draw_artist(self.vline[key + '_l']) self.ax[key].draw_artist(self.vline[key + '_r']) self.canvas.blit(self.ax[key].bbox)
class InteractiveMPLGraph: def __init__(self, parent): self.fig = Figure(dpi=170) self.canvas = FigureCanvas(self.fig) self.ax = self.fig.add_subplot(111) self.cax = None self.selected_nodes = [] self._node = {} self._adj = {} self._current_node = None self._current_node_handle = None self._parent = parent self._analysis = parent.analysis self._canvas_toolbar = None self._default_size = {} node_positions = self._analysis.get_current_node_positions() if node_positions is None: node_positions = self._analysis.get_node_positions_2d() node_labels = self._analysis.get_short_node_labels() edge_labels_occupancy = { (key.split(':')[0], key.split(':')[1]): value for key, value in self._analysis.get_occupancies( as_labels=True).items() } frame_time, frame_unit = self._parent._search_parameter['frame_time'] edge_labels_endurance = { (key.split(':')[0], key.split(':')[1]): value for key, value in self._analysis.get_endurance_times( as_labels=True, frame_time=frame_time, frame_unit=frame_unit).items() } if self._parent._analysis_type == 'ww': edge_labels_nb_water = { (key.split(':')[0], key.split(':')[1]): value for key, value in self._analysis.get_nb_waters( as_labels=True).items() } graph = self._analysis.initial_graph f = 0.55 len2fontsize = defaultdict( lambda: 6 * f, { 2: 11 * f, 3: 11 * f, 4: 11 * f, 5: 11 * f, 6: 10 * f, 7: 9 * f, 8: 8 * f, 9: 7 * f, 10: 7 * f }) for node in graph.nodes: label_length = len(node_labels[node]) if not self._analysis.residuewise: label_length -= 1 subgraph = graph.subgraph([node]) label = nx.draw_networkx_labels( subgraph, node_positions, labels={node: node_labels[node]}, font_weight='bold', font_size=len2fontsize[label_length], ax=self.ax)[node] handle = nx.draw_networkx_nodes(subgraph, node_positions, node_color=default_colors[0], alpha=0.5, ax=self.ax) self._node[node] = { 'handle': handle, 'label': label, 'active': True, 'color': default_colors[0] } self._default_size['node'] = (handle.get_sizes()[0], len2fontsize[label_length]) self._adj[node] = {} for u, v in graph.edges: subgraph = graph.subgraph([u, v]) direction = list(subgraph.edges)[0] handle = nx.draw_networkx_edges(subgraph, node_positions, width=1.0, alpha=0.5, ax=self.ax) self._default_size['edge'] = handle.get_linewidth() segments = handle.get_segments() ta = trans_angle(segments[0][0], segments[0][1], self.ax) try: edge_label = {direction: edge_labels_occupancy[(u, v)]} except KeyError: edge_label = {direction: edge_labels_occupancy[(v, u)]} edge_label_occupancy = nx.draw_networkx_edge_labels( subgraph, node_positions, edge_labels=edge_label, font_weight='bold', font_size=len2fontsize[6], ax=self.ax)[direction] edge_label_occupancy.set_visible(False) edge_label_occupancy.set_rotation(ta) self._default_size['label'] = edge_label_occupancy.get_fontsize() try: edge_label = {direction: edge_labels_endurance[(u, v)]} except KeyError: edge_label = {direction: edge_labels_endurance[(v, u)]} edge_label_endurance = nx.draw_networkx_edge_labels( subgraph, node_positions, edge_labels=edge_label, font_weight='bold', font_size=len2fontsize[6], ax=self.ax)[direction] edge_label_endurance.set_visible(False) edge_label_endurance.set_rotation(ta) if self._parent._analysis_type == 'ww': try: edge_label = {direction: edge_labels_nb_water[(u, v)]} except KeyError: edge_label = {direction: edge_labels_nb_water[(v, u)]} edge_label_water = nx.draw_networkx_edge_labels( subgraph, node_positions, edge_labels=edge_label, font_weight='bold', font_size=len2fontsize[6], ax=self.ax)[direction] edge_label_water.set_visible(False) edge_label_water.set_rotation(ta) edge_data = { 'handle': handle, 'direction': direction, 'active': True, 'color': 'black', 'all_labels': { 'occupancy': edge_label_occupancy, 'endurance': edge_label_endurance, 'nb_water': edge_label_water } } else: edge_data = { 'handle': handle, 'direction': direction, 'active': True, 'color': 'black', 'all_labels': { 'occupancy': edge_label_occupancy, 'endurance': edge_label_endurance } } self._adj[u][v] = edge_data self._adj[v][u] = edge_data self.fig.tight_layout() self.cax = self.fig.add_axes([0.73, 0.1, 0.2, 0.01]) self.cax.axis('off') self._cidpress = self.fig.canvas.mpl_connect('button_press_event', self.on_press) self._cidrelease = self.fig.canvas.mpl_connect('button_release_event', self.on_release) self._cidmotion = self.fig.canvas.mpl_connect('motion_notify_event', self.on_motion) def on_press(self, event): if event.inaxes != self.ax: return if (self._canvas_toolbar is None) or (self._canvas_toolbar.mode != ""): return if self._current_node is not None: return clicked_on_node = False for node in self._node: if not self._node[node]['active']: continue node_handle = self._node[node]['handle'] contains, attrd = node_handle.contains(event) if contains: clicked_on_node = True break if not clicked_on_node: return self.moved = False x0, y0 = node_handle.get_offsets()[0] self.press = x0, y0, event.xdata, event.ydata self._current_node_handle = node_handle self._current_node = node node_handle.set_animated(True) label = self._node[node]['label'] label.set_animated(True) for other_node in self._adj[node]: other_node_handle = self._node[other_node]['handle'] edge = self._adj[node][other_node]['handle'] edge.set_animated(True) for label_type in self._adj[node][other_node]['all_labels']: label = self._adj[node][other_node]['all_labels'][label_type] label.set_animated(True) other_node_handle.set_animated(True) self.canvas.draw() self.background = self.fig.canvas.copy_from_bbox(self.ax.bbox) for other_node in self._adj[node]: other_node_handle = self._node[other_node]['handle'] edge = self._adj[node][other_node]['handle'] self.ax.draw_artist(edge) self.ax.draw_artist(other_node_handle) self.ax.draw_artist(node_handle) self.ax.draw_artist(label) self.canvas.blit(self.ax.bbox) def on_motion(self, event): if self._current_node is None: return if event.inaxes != self.ax: return node = self._current_node node_handle = self._current_node_handle self.moved = True x0, y0, xpress, ypress = self.press dx = event.xdata - xpress dy = event.ydata - ypress node_handle.set_offsets([x0 + dx, y0 + dy]) for other_node in self._adj[node]: edge = self._adj[node][other_node]['handle'] direction = self._adj[node][other_node]['direction'] segments = edge.get_segments() index = direction.index(node) segments[0][index] = [x0 + dx, y0 + dy] edge.set_segments(segments) label_pos = np.array(segments[0]).sum(axis=0) / 2 ta = trans_angle(segments[0][0], segments[0][1], self.ax) for label_type in self._adj[node][other_node]['all_labels']: edge_label = self._adj[node][other_node]['all_labels'][ label_type] edge_label.set_position(label_pos) edge_label.set_rotation(ta) label = self._node[node]['label'] label.set_position([x0 + dx, y0 + dy]) self.canvas.restore_region(self.background) for other_node in self._adj[node]: other_node_handle = self._node[other_node]['handle'] edge = self._adj[node][other_node]['handle'] for label_type in self._adj[node][other_node]['all_labels']: edge_label = self._adj[node][other_node]['all_labels'][ label_type] self.ax.draw_artist(edge_label) self.ax.draw_artist(edge) self.ax.draw_artist(other_node_handle) self.ax.draw_artist(node_handle) self.ax.draw_artist(label) self.canvas.blit(self.ax.bbox) def on_release(self, event): if self._current_node is None: return node_handle = self._current_node_handle node = self._current_node label = self._node[node]['label'] if not self.moved: if node in self.selected_nodes: self.selected_nodes.remove(node) node_handle.set_linewidth(1.0) node_handle.set_edgecolor(self._node[node]['color']) else: self.selected_nodes.append(node) self._parent.statusbar.showMessage( self.get_current_node_info()) self.process_selected_nodes() self.ax.draw_artist(node_handle) self.canvas.blit(self.ax.bbox) node_handle.set_animated(False) label.set_animated(False) for other_node in self._adj[node]: other_node_handle = self._node[other_node]['handle'] edge = self._adj[node][other_node]['handle'] for label_type in self._adj[node][other_node]['all_labels']: edge_label = self._adj[node][other_node]['all_labels'][ label_type] edge_label.set_animated(False) edge.set_animated(False) other_node_handle.set_animated(False) self.background = None self.canvas.draw_idle() self.press = None self._current_node = None def edges(self): seen = {} for node, neighbours in self._adj.items(): for neighbor, data in neighbours.items(): if neighbor not in seen: yield (node, neighbor, data) seen[node] = True del seen def nodes(self): for node in self._node: yield node def get_current_node_info(self): return self._current_node def reset_selected_nodes(self): self.selected_nodes = [] for node in self._node: node_handle = self._node[node]['handle'] node_handle.set_linewidth(1.0) node_handle.set_edgecolor(self._node[node]['color']) self.canvas.draw_idle() def process_selected_nodes(self): focus_widget = self._parent.focusWidget() if focus_widget is self._parent.line_bonds_connected_root: self._parent.line_bonds_connected_root.setText(self._current_node) elif focus_widget is self._parent.line_bonds_path_root: self._parent.line_bonds_path_root.setText(self._current_node) elif focus_widget is self._parent.line_bonds_path_goal: self._parent.line_bonds_path_goal.setText(self._current_node) elif focus_widget is self._parent.lineEdit_specific_path: self._parent.lineEdit_specific_path.setText(', '.join( self.selected_nodes)) current_plugin = self._parent.comboBox_plugins.currentText() plugin_ui = self._parent._plugins[current_plugin].ui plugin_lineEdits = [ getattr(plugin_ui, lineEdit) for lineEdit in ['lineEdit_node_picker' + str(i) for i in range(1, 4)] if hasattr(plugin_ui, lineEdit) ] for lineEdit in plugin_lineEdits: if focus_widget is lineEdit: lineEdit.setText(self._current_node) rem_nodes = [] for node in self.selected_nodes: node_handle = self._node[node]['handle'] if node not in self._analysis.filtered_graph.nodes: rem_nodes.append(node) node_handle.set_linewidth(1.0) node_handle.set_edgecolor(self._node[node]['color']) node_handle.set_linewidth(2.0) node_handle.set_edgecolor('black') for node in rem_nodes: self.selected_nodes.remove(node) def set_edge_color(self): for node, other_node, edge_data in self.edges(): edge_handle = edge_data['handle'] edge_handle.set_facecolor(edge_data['color']) edge_handle.set_edgecolor(edge_data['color']) self.canvas.draw_idle() def set_subgraph(self, **kwargs): subgraph = self._analysis.filtered_graph node_labels_active = self._parent.checkBox_bonds_graph_labels.isChecked( ) for node in self._node: if node in subgraph.nodes: self._node[node]['active'] = True node_handle = self._node[node]['handle'] node_handle.set_visible(True) label = self._node[node]['label'] if node_labels_active: label.set_visible(True) else: label.set_visible(False) else: self._node[node]['active'] = False node_handle = self._node[node]['handle'] node_handle.set_visible(False) label = self._node[node]['label'] label.set_visible(False) for node, other_node, edge_data in self.edges(): edge_handle = edge_data['handle'] for edge_label_type, edge_label in edge_data['all_labels'].items(): if (node, other_node) in subgraph.edges: edge_handle.set_visible(True) edge_data['active'] = True else: edge_handle.set_visible(False) edge_label.set_visible(False) edge_data['active'] = False self.set_edge_labels(draw=False) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_colors(self, **kwargs): if self.ax.get_legend() is not None: self.ax.get_legend().remove() if self.cax is not None: self.cax.clear() self.cax.axis('off') if self._parent.radioButton_color.isChecked(): color = self._parent.comboBox_single_color.currentText() if color == '': color = default_colors[0] if not is_color_like(color): Error( 'Color Error' + ' ' * 30, "Did not understand color definition '{}'. You can use strings like green or shorthands like g or RGB codes like #15b01a." .format(color)) return for node in self._node: self._node[node]['color'] = color elif self._parent.radioButton_colors.isChecked(): for node in self._node: segname = node.split('-')[0] color = self._parent._segname_colors[segname] if not is_color_like(color): Error( 'Color Error' + ' ' * 30, "Did not understand color definition '{}'. You can use strings like green or shorthands like g or RGB codes like #15b01a." .format(color)) return self._node[node]['color'] = color if self._parent.checkBox_segnames_legend.isChecked(): custom_lines = [ Line2D([0], [0], marker='o', color='w', markerfacecolor=color, alpha=0.6, markersize=12, lw=4) for color in self._parent._segname_colors.values() ] segnames = [ segname for segname in self._parent._segname_colors.keys() ] self.ax.legend(custom_lines, segnames) elif self._parent.radioButton_degree.isChecked( ) or self._parent.radioButton_betweenness.isChecked(): avg_type = self._parent.checkBox_centralities_avg.isChecked() norm_type = self._parent.checkBox_centralities_norm.isChecked() if self._parent.radioButton_degree.isChecked(): centralities = self._analysis.centralities['degree'][avg_type][ norm_type] elif self._parent.radioButton_betweenness.isChecked(): centralities = self._analysis.centralities['betweenness'][ avg_type][norm_type] max_centrality = sorted(centralities.values())[-1] if self._parent.radioButton_degree.isChecked() and ( not (avg_type or norm_type)): max_centrality = round(max_centrality) cmap = plt.get_cmap('jet') for node in centralities: centrality_value = centralities[node] self._node[node]['color'] = rgb_to_string( cmap(centrality_value / max_centrality)) if self._parent.checkBox_color_legend.isChecked(): sm = plt.cm.ScalarMappable(cmap=cmap) sm.set_array([0.0, max_centrality]) plt.colorbar(sm, cax=self.cax, ticks=[0, max_centrality], orientation='horizontal') self.cax.set_xticklabels([str(0), str(max_centrality)]) self.cax.axis('on') for node in self._node: node_handle = self._node[node]['handle'] color = self._node[node]['color'] if not self._parent.checkBox_white.isChecked(): node_handle.set_facecolor(color) else: node_handle.set_facecolor('white') node_handle.set_edgecolor(color) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_node_positions(self, **kwargs): projection = 'PCA' if self._parent.radioButton_rotation_xy.isChecked(): projection = 'XY' elif self._parent.radioButton_rotation_zy.isChecked(): projection = 'ZY' adjust_water = False frame = int(self._parent.label_frame.text()) positions = self._parent.analysis.get_node_positions_2d( projection=projection, in_frame=frame, adjust_water_positions=adjust_water) all_pos = np.array([positions[key] for key in positions]) minx, maxx, miny, maxy = np.min(all_pos[:, 0]), np.max( all_pos[:, 0]), np.min(all_pos[:, 1]), np.max(all_pos[:, 1]) xmargin = (maxx - minx) / 20 ymargin = (maxy - miny) / 20 for node in self._node: node_handle = self._node[node]['handle'] node_handle.set_offsets(positions[node]) node_label = self._node[node]['label'] node_label.set_position(positions[node]) for node, other_node, edge_data in self.edges(): edge = edge_data['handle'] direction = edge_data['direction'] index = direction.index(node) other_index = direction.index(other_node) edge_positions = np.array([positions[node], positions[other_node] ])[[index, other_index]] edge.set_segments([edge_positions]) label_pos = np.array(edge_positions).sum(axis=0) / 2 ta = trans_angle(edge_positions[0], edge_positions[1], self.ax) for label_type in edge_data['all_labels']: edge_label = edge_data['all_labels'][label_type] edge_label.set_position(label_pos) edge_label.set_rotation(ta) self.ax.set_xlim(minx - xmargin, maxx + xmargin) self.ax.set_ylim(miny - ymargin, maxy + ymargin) if ('draw' not in kwargs): self.canvas.draw_idle() def set_nodesize(self, **kwargs): for node in self._node: offset = self._default_size['node'][0] / 2 size = offset + 2 / (self._default_size['node'][0]) * ( self._parent.horizontalSlider_nodes.value() / 100 * self._default_size['node'][0])**2 node_handle = self._node[node]['handle'] node_handle.set_sizes([size]) offset = self._default_size['node'][1] / 2 size = offset + self._parent.horizontalSlider_nodes.value( ) / 100 * self._default_size['node'][1] label_handle = self._node[node]['label'] label_handle.set_size(size) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_edgesize(self, **kwargs): for node, other_node, edge_data in self.edges(): offset = self._default_size['edge'] / 2 size = offset + self._default_size['edge'] * ( self._parent.horizontalSlider_edges.value() / 100) edge_data['handle'].set_linewidth(size) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_labelsize(self, **kwargs): for node, other_node, edge_data in self.edges(): offset = self._default_size['label'] / 2 size = offset + self._default_size['label'] * ( self._parent.horizontalSlider_labels.value() / 100) for typ, label_handle in edge_data['all_labels'].items(): label_handle.set_fontsize(size) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_node_labels(self, **kwargs): labels_active = self._parent.checkBox_bonds_graph_labels.isChecked() for node in self._node: show_label = self._node[node]['active'] label = self._node[node]['label'] if labels_active and show_label: label.set_visible(True) else: label.set_visible(False) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def set_edge_labels(self, **kwargs): if self._parent.checkBox_bonds_occupancy.isChecked(): active_label_type = 'occupancy' elif self._parent.checkBox_bonds_endurance.isChecked(): active_label_type = 'endurance' elif self._parent.checkBox_nb_water.isChecked(): active_label_type = 'nb_water' else: active_label_type = None for node, other_node, edge_data in self.edges(): show_label = edge_data['active'] labels = edge_data['all_labels'] for label_type in labels: label = labels[label_type] if (label_type == active_label_type) and show_label: label.set_visible(True) else: label.set_visible(False) if ('draw' not in kwargs) or kwargs['draw']: self.canvas.draw_idle() def get_active_nodes(self): return [node for node in self._node if self._node[node]['active']] def set_current_pos(self): node_pos = {} for node in self._node: node_handle = self._node[node]['handle'] x, y = node_handle.get_offsets()[0] node_pos[node] = (x, y) self._analysis._current_node_positions = node_pos def add_toolbar(self): self._canvas_toolbar = NavigationToolbar(self.canvas, self._parent) self._canvas_toolbar.home = self.set_node_positions self._parent.addToolBar(self._canvas_toolbar) def remove_toolbar(self): self._parent.removeToolBar(self._canvas_toolbar) def _disconnect(self): 'disconnect all the stored connection ids' self.canvas.mpl_disconnect(self.cidpress) self.canvas.mpl_disconnect(self.cidrelease) self.canvas.mpl_disconnect(self.cidmotion)
class Base_Plot(QtCore.QObject): def __init__(self, parent, widget, mpl_layout): super().__init__(parent) self.parent = parent self.widget = widget self.mpl_layout = mpl_layout self.fig = mplfigure.Figure() mpl.scale.register_scale(AbsoluteLogScale) mpl.scale.register_scale(BiSymmetricLogScale) # Set plot variables self.x_zoom_constraint = False self.y_zoom_constraint = False self.create_canvas() self.NavigationToolbar(self.canvas, self.widget, coordinates=True) # AutoScale self.autoScale = [True, True] self.i = 0 # Connect Signals self._draw_event_signal = self.canvas.mpl_connect('draw_event', self._draw_event) self.canvas.mpl_connect('button_press_event', lambda event: self.click(event)) self.canvas.mpl_connect('key_press_event', lambda event: self.key_press(event)) # self.canvas.mpl_connect('key_release_event', lambda event: self.key_release(event)) self._draw_event() def create_canvas(self): self.canvas = FigureCanvas(self.fig) self.mpl_layout.addWidget(self.canvas) self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus) self.canvas.draw() # Set scales scales = {'linear': True, 'log': 0, 'abslog': 0, 'bisymlog': 0} for ax in self.ax: ax.scale = {'x': scales, 'y': deepcopy(scales)} ax.ticklabel_format(scilimits=(-4, 4), useMathText=True) # Get background self.background_data = self.canvas.copy_from_bbox(ax.bbox) def _find_calling_axes(self, event): for axes in self.ax: # identify calling axis if axes == event or (hasattr(event, 'inaxes') and event.inaxes == axes): return axes def set_xlim(self, axes, x): if not self.autoScale[0]: return # obey autoscale right click option if axes.get_xscale() in ['linear']: # range = np.abs(np.max(x) - np.min(x)) # min = np.min(x) - range*0.05 # if min < 0: # min = 0 # xlim = [min, np.max(x) + range*0.05] xlim = [np.min(x), np.max(x)] if 'log' in axes.get_xscale(): abs_x = np.abs(x) abs_x = abs_x[np.nonzero(abs_x)] # exclude 0's if axes.get_xscale() in ['log', 'abslog', 'bisymlog']: min_data = np.ceil(np.log10(np.min(abs_x))) max_data = np.floor(np.log10(np.max(abs_x))) xlim = [10**(min_data-1), 10**(max_data+1)] if np.isnan(xlim).any() or np.isinf(xlim).any(): pass elif xlim != axes.get_xlim(): # if xlim changes axes.set_xlim(xlim) def set_ylim(self, axes, y): if not self.autoScale[1]: return # obey autoscale right click option min_data = np.array(y)[np.isfinite(y)].min() max_data = np.array(y)[np.isfinite(y)].max() if min_data == max_data: min_data -= 10**-1 max_data += 10**-1 if axes.get_yscale() == 'linear': range = np.abs(max_data - min_data) ylim = [min_data - range*0.1, max_data + range*0.1] elif axes.get_yscale() in ['log', 'abslog']: abs_y = np.abs(y) abs_y = abs_y[np.nonzero(abs_y)] # exclude 0's abs_y = abs_y[np.isfinite(abs_y)] # exclude nan, inf if abs_y.size == 0: # if no data, assign ylim = [10**-7, 10**-1] else: min_data = np.ceil(np.log10(np.min(abs_y))) max_data = np.floor(np.log10(np.max(abs_y))) ylim = [10**(min_data-1), 10**(max_data+1)] elif axes.get_yscale() == 'bisymlog': min_sign = np.sign(min_data) max_sign = np.sign(max_data) if min_sign > 0: min_data = np.ceil(np.log10(np.abs(min_data))) elif min_data == 0 or max_data == 0: pass else: min_data = np.floor(np.log10(np.abs(min_data))) if max_sign > 0: max_data = np.floor(np.log10(np.abs(max_data))) elif min_data == 0 or max_data == 0: pass else: max_data = np.ceil(np.log10(np.abs(max_data))) # TODO: ylim could be incorrect for neg/neg, checked for pos/pos, pos/neg ylim = [min_sign*10**(min_data-min_sign), max_sign*10**(max_data+max_sign)] if ylim != axes.get_ylim(): # if ylim changes, update axes.set_ylim(ylim) def update_xylim(self, axes, xlim=[], ylim=[], force_redraw=True): data = self._get_data(axes) # on creation, there is no data, don't update if np.shape(data['x'])[0] < 2 or np.shape(data['y'])[0] < 2: return for (axis, lim) in zip(['x', 'y'], [xlim, ylim]): # Set Limits if len(lim) == 0: eval('self.set_' + axis + 'lim(axes, data["' + axis + '"])') else: eval('axes.set_' + axis + 'lim(lim)') # If bisymlog, also update scaling, C if eval('axes.get_' + axis + 'scale()') == 'bisymlog': self._set_scale(axis, 'bisymlog', axes) ''' # TODO: Do this some day, probably need to create annotation during canvas creation # Move exponent exp_loc = {'x': (.89, .01), 'y': (.01, .96)} eval(f'axes.get_{axis}axis().get_offset_text().set_visible(False)') ax_max = eval(f'max(axes.get_{axis}ticks())') oom = np.floor(np.log10(ax_max)).astype(int) axes.annotate(fr'$\times10^{oom}$', xy=exp_loc[axis], xycoords='axes fraction') ''' if force_redraw: self._draw_event() # force a draw def _get_data(self, axes): # NOT Generic # get experimental data for axes data = {'x': [], 'y': []} if 'exp_data' in axes.item: data_plot = axes.item['exp_data'].get_offsets().T if np.shape(data_plot)[1] > 1: data['x'] = data_plot[0,:] data['y'] = data_plot[1,:] # append sim_x if it exists if 'sim_data' in axes.item and hasattr(axes.item['sim_data'], 'raw_data'): if axes.item['sim_data'].raw_data.size > 0: data['x'] = np.append(data['x'], axes.item['sim_data'].raw_data[:,0]) elif 'weight_unc_fcn' in axes.item: data['x'] = axes.item['weight_unc_fcn'].get_xdata() data['y'] = axes.item['weight_unc_fcn'].get_ydata() elif any(key in axes.item for key in ['density', 'qq_data', 'sim_data']): name = np.intersect1d(['density', 'qq_data'], list(axes.item.keys()))[0] for n, coord in enumerate(['x', 'y']): xyrange = np.array([]) for item in axes.item[name]: if name == 'qq_data': coordData = item.get_offsets() if coordData.size == 0: continue else: coordData = coordData[:,n] elif name == 'density': coordData = eval('item.get_' + coord + 'data()') coordData = np.array(coordData)[np.isfinite(coordData)] if coordData.size == 0: continue xyrange = np.append(xyrange, [coordData.min(), coordData.max()]) xyrange = np.reshape(xyrange, (-1,2)) data[coord] = [np.min(xyrange[:,0]), np.max(xyrange[:,1])] return data def _set_scale(self, coord, type, event, update_xylim=False): def RoundToSigFigs(x, p): x = np.asarray(x) x_positive = np.where(np.isfinite(x) & (x != 0), np.abs(x), 10**(p-1)) mags = 10 ** (p - 1 - np.floor(np.log10(x_positive))) return np.round(x * mags) / mags # find correct axes axes = self._find_calling_axes(event) # for axes in self.ax: # if axes == event or (hasattr(event, 'inaxes') and event.inaxes == axes): # break # Set scale menu boolean if coord == 'x': shared_axes = axes.get_shared_x_axes().get_siblings(axes) else: shared_axes = axes.get_shared_y_axes().get_siblings(axes) for shared in shared_axes: shared.scale[coord] = dict.fromkeys(shared.scale[coord], False) # sets all types: False shared.scale[coord][type] = True # set selected type: True # Apply selected scale if type == 'linear': str = 'axes.set_{:s}scale("{:s}")'.format(coord, 'linear') elif type == 'log': str = 'axes.set_{0:s}scale("{1:s}", nonpos{0:s}="mask")'.format(coord, 'log') elif type == 'abslog': str = 'axes.set_{:s}scale("{:s}")'.format(coord, 'abslog') elif type == 'bisymlog': # default string to evaluate str = 'axes.set_{0:s}scale("{1:s}")'.format(coord, 'bisymlog') data = self._get_data(axes)[coord] if len(data) != 0: finite_data = np.array(data)[np.isfinite(data)] # ignore nan and inf min_data = finite_data.min() max_data = finite_data.max() if min_data != max_data: # if zero is within total range, find largest pos or neg range if np.sign(max_data) != np.sign(min_data): processed_data = [finite_data[finite_data>=0], finite_data[finite_data<=0]] C = 0 for data in processed_data: range = np.abs(data.max() - data.min()) if range > C: C = range max_data = data.max() else: C = np.abs(max_data-min_data) C *= 10**(OoM(max_data) + 2) # scaling factor TODO: + 1 looks loglike, + 2 linear like C = RoundToSigFigs(C, 1) # round to 1 significant figure str = 'axes.set_{0:s}scale("{1:s}", C={2:e})'.format(coord, 'bisymlog', C) eval(str) if type == 'linear' and coord == 'x': formatter = MathTextSciSIFormatter(useOffset=False, useMathText=True) axes.xaxis.set_major_formatter(formatter) elif type == 'linear' and coord == 'y': formatter = mpl.ticker.ScalarFormatter(useOffset=False, useMathText=True) formatter.set_powerlimits([-3, 4]) axes.yaxis.set_major_formatter(formatter) if update_xylim: self.update_xylim(axes) def _animate_items(self, bool=True): for axis in self.ax: axis.xaxis.set_animated(bool) axis.yaxis.set_animated(bool) if axis.get_legend() is not None: axis.get_legend().set_animated(bool) for item in axis.item.values(): if isinstance(item, list): for subItem in item: if isinstance(subItem, dict): subItem['line'].set_animated(bool) else: subItem.set_animated(bool) else: item.set_animated(bool) def _draw_items_artist(self): self.canvas.restore_region(self.background_data) for axis in self.ax: axis.draw_artist(axis.xaxis) axis.draw_artist(axis.yaxis) for item in axis.item.values(): if isinstance(item, list): for subItem in item: if isinstance(subItem, dict): axis.draw_artist(subItem['line']) else: axis.draw_artist(subItem) else: axis.draw_artist(item) if axis.get_legend() is not None: axis.draw_artist(axis.get_legend()) self.canvas.update() def set_background(self): self.canvas.mpl_disconnect(self._draw_event_signal) self.canvas.draw() # for when shock changes. Without signal disconnect, infinite loop self._draw_event_signal = self.canvas.mpl_connect('draw_event', self._draw_event) self.background_data = self.canvas.copy_from_bbox(self.fig.bbox) def _draw_event(self, event=None): # After redraw (new/resizing window), obtain new background self._animate_items(True) self.set_background() self._draw_items_artist() #self.canvas.draw_idle() def clear_plot(self, ignore=[], draw=True): for axis in self.ax: if axis.get_legend() is not None: axis.get_legend().remove() for item in axis.item.values(): if hasattr(item, 'set_offsets'): # clears all data points if 'scatter' not in ignore: item.set_offsets(([np.nan, np.nan])) elif hasattr(item, 'set_xdata') and hasattr(item, 'set_ydata'): if 'line' not in ignore: item.set_xdata([np.nan, np.nan]) # clears all lines item.set_ydata([np.nan, np.nan]) elif hasattr(item, 'set_text'): # clears all text boxes if 'text' not in ignore: item.set_text('') if draw: self._draw_event() def click(self, event): if event.button == 3: # if right click if not self.toolbar.mode: self._popup_menu(event) # if self.toolbar._active is 'ZOOM': # if zoom is on, turn off # self.toolbar.press_zoom(event) # cancels current zooom # self.toolbar.zoom() # turns zoom off elif event.dblclick: # if double right click, go to default view self.toolbar.home() def key_press(self, event): if event.key == 'escape': if self.toolbar.mode == 'zoom rect': # if zoom is on, turn off self.toolbar.zoom() # turns zoom off elif self.toolbar.mode == 'pan/zoom': self.toolbar.pan() # elif event.key == 'shift': elif event.key == 'x': # Does nothing, would like to make sticky constraint zoom/pan self.x_zoom_constraint = not self.x_zoom_constraint elif event.key == 'y': # Does nothing, would like to make sticky constraint zoom/pan self.y_zoom_constraint = not self.y_zoom_constraint elif event.key in ['s', 'l', 'L', 'k']: pass else: key_press_handler(event, self.canvas, self.toolbar) # def key_release(self, event): # print(event.key, 'released') def NavigationToolbar(self, *args, **kwargs): ## Add toolbar ## self.toolbar = CustomNavigationToolbar(self.canvas, self.widget, coordinates=True) self.mpl_layout.addWidget(self.toolbar) def _popup_menu(self, event): axes = self._find_calling_axes(event) # find axes calling right click if axes is None: return pos = self.parent.mapFromGlobal(QtGui.QCursor().pos()) popup_menu = QMenu(self.parent) xScaleMenu = popup_menu.addMenu('x-scale') yScaleMenu = popup_menu.addMenu('y-scale') for coord in ['x', 'y']: menu = eval(coord + 'ScaleMenu') for type in axes.scale[coord].keys(): action = QAction(type, menu, checkable=True) if axes.scale[coord][type]: # if it's checked action.setEnabled(False) else: action.setEnabled(True) menu.addAction(action) action.setChecked(axes.scale[coord][type]) fcn = lambda event, coord=coord, type=type: self._set_scale(coord, type, axes, True) action.triggered.connect(fcn) # Create menu for AutoScale options X Y All popup_menu.addSeparator() autoscale_options = ['AutoScale X', 'AutoScale Y', 'AutoScale All'] for n, text in enumerate(autoscale_options): action = QAction(text, menu, checkable=True) if n < len(self.autoScale): action.setChecked(self.autoScale[n]) else: action.setChecked(all(self.autoScale)) popup_menu.addAction(action) action.toggled.connect(lambda event, n=n: self._setAutoScale(n, event, axes)) popup_menu.exec_(self.parent.mapToGlobal(pos)) def _setAutoScale(self, choice, event, axes): if choice == len(self.autoScale): for n in range(len(self.autoScale)): self.autoScale[n] = event else: self.autoScale[choice] = event if event: # if something toggled true, update limits self.update_xylim(axes)
class MplWidget(QtWidgets.QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) self.scroll = QtWidgets.QScrollArea(self) self.scroll.setParent(None) #self.fig =Figure(tight_layout=True) self.fig = Figure() left = 0.0 bottom = 0.0 width = 1 height = 1 self.fig.add_axes([left, bottom, width, height]) self.canvas = FigureCanvas(self.fig) self.fig.set_facecolor([0.23, 0.23, 0.23, 0.5]) self.canvas.axes = self.canvas.figure.gca() #self.canvas.figure.tight_layout(pad=0) self.vertical_layout = QVBoxLayout() self.vertical_layout.addWidget(self.canvas) self.mpl_toolbar = my_toolbar(self.canvas, self) self.mpl_toolbar.setParentClass(self) self.mpl_toolbar.setMinimumWidth(100) self.mpl_toolbar.setFixedHeight(26) self.mpl_toolbar.setStyleSheet( "QToolBar { opacity: 1;border: 0px; background-color: rgb(133, 196, 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.mpl_toolbar.setObjectName("myToolBar") #self.canvas.mpl_connect("resize_event", self.resize) self.vertical_layout.addWidget(self.mpl_toolbar) self.setLayout(self.vertical_layout) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setSpacing(0) self.rect = Rectangle((0, 0), 1, 1) self.updateSecondImage = None self.patchesTotal = 0 self.typeOfAnnotation = "autoDetcted" self.frameAtString = "Frame 0" self.currentSelectedOption = None self.AllBoxListDictionary = { "eraseBox": [], "oneWormLive": [], "multiWormLive": [], "oneWormDead": [], "multiWormDead": [], "miscBoxes": [], "autoDetcted": [] } self.eraseBoxXYValues = self.AllBoxListDictionary["eraseBox"] self.addBoxXYValues = self.AllBoxListDictionary["miscBoxes"] self.oneWormLiveBoxXYValues = self.AllBoxListDictionary["oneWormLive"] self.multiWormLiveBoxXYValues = self.AllBoxListDictionary[ "multiWormLive"] self.oneWormDeadBoxXYValues = self.AllBoxListDictionary["oneWormDead"] self.multiWormDeadBoxXYValues = self.AllBoxListDictionary[ "multiWormDead"] self.autoDetectedBoxXYValues = self.AllBoxListDictionary["autoDetcted"] self.tempList = [] def resetAllBoxListDictionary(self): self.AllBoxListDictionary = { "eraseBox": [], "oneWormLive": [], "multiWormLive": [], "oneWormDead": [], "multiWormDead": [], "miscBoxes": [], "autoDetcted": [] } def updateAllBoxListDictionary(self): self.AllBoxListDictionary["eraseBox"] = self.eraseBoxXYValues self.AllBoxListDictionary["miscBoxes"] = self.addBoxXYValues self.AllBoxListDictionary["oneWormLive"] = self.oneWormLiveBoxXYValues self.AllBoxListDictionary[ "multiWormLive"] = self.multiWormLiveBoxXYValues self.AllBoxListDictionary["oneWormDead"] = self.oneWormDeadBoxXYValues self.AllBoxListDictionary[ "multiWormDead"] = self.multiWormDeadBoxXYValues self.AllBoxListDictionary["autoDetcted"] = self.autoDetectedBoxXYValues def updateAllListFromAllBoxListDictionary(self): self.eraseBoxXYValues = self.AllBoxListDictionary["eraseBox"] self.addBoxXYValues = self.AllBoxListDictionary["miscBoxes"] self.oneWormLiveBoxXYValues = self.AllBoxListDictionary["oneWormLive"] self.multiWormLiveBoxXYValues = self.AllBoxListDictionary[ "multiWormLive"] self.oneWormDeadBoxXYValues = self.AllBoxListDictionary["oneWormDead"] self.multiWormDeadBoxXYValues = self.AllBoxListDictionary[ "multiWormDead"] self.autoDetectedBoxXYValues = self.AllBoxListDictionary["autoDetcted"] def setFrameAtString(self, text): self.frameAtString = text def getFrameAtString(self): return self.frameAtString def getCurrentSelectedOption(self): return self.currentSelectedOption def setCurrentSelectedOption(self, option): self.currentSelectedOption = option def setDarkTheme(self): self.mpl_toolbar.setStyleSheet( "QToolBar#myToolBar{ border: 0px; background-color: rgb(133, 0,s 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.fig.set_facecolor([0.23, 0.23, 0.23, 0.5]) #self.fig.set_facecolor('grey') self.canvas.draw() def setGreenTheme(self): self.mpl_toolbar.setStyleSheet( "QToolBar { border: 0px; background-color: rgb(133, 196, 65); border-bottom: 1px solid #19232D;padding: 2px; font-weight: bold;spacing: 2px; } " ) self.fig.set_facecolor('grey') self.canvas.draw() def setTypeOfAnnotation(self, text): self.typeOfAnnotation = text def restrictCanvasMinimumSize(self, size): self.canvas.setMinimumSize(size) def unmountWidgetAndClear(self): self.vertical_layout.removeWidget(self.canvas) self.vertical_layout.removeWidget(self.scroll) self.scroll.setParent(None) self.canvas.setParent(None) sip.delete(self.scroll) del self.canvas self.scroll = None self.canvas = None self.canvas = FigureCanvas(Figure()) self.canvas.axes = self.canvas.figure.gca() #self.canvas.figure.tight_layout() self.scroll = QtWidgets.QScrollArea(self) self.scroll.setWidgetResizable(True) def connectClickListnerToCurrentImageForCrop(self, givenController, updateSecondImage=None, listOfControllers=None, keyForController=None): self.cid1 = self.canvas.mpl_connect("button_press_event", self.on_press_for_crop) self.cid2 = self.canvas.mpl_connect("motion_notify_event", self.onmove_for_crop) self.cid3 = self.canvas.mpl_connect("button_release_event", self.on_release_for_crop) self.givenControllerObject = givenController self.updateSecondImage = updateSecondImage self.pressevent = None self.listOfControllers = listOfControllers self.keyForController = keyForController def on_press_for_crop(self, event): if (self.mpl_toolbar.mode): return try: self.rect.remove() except: pass self.addedPatch = None self.x0 = event.xdata self.y0 = event.ydata self.rect = Rectangle((self.x0, self.y0), 1, 1) self.rect._alpha = 0.5 self.rect._linewidth = 2 self.rect.set_color("C2") self.rect.set self.pressevent = 1 self.addedPatch = self.canvas.axes.add_patch(self.rect) def on_release_for_crop(self, event): if (self.mpl_toolbar.mode): return self.pressevent = None minMaxVertices = [ int(np.ceil(min(self.x0, self.x1))), int(np.ceil(min(self.y0, self.y1))), int(np.round(max(self.x0, self.x1))), int(np.round(max(self.y0, self.y1))), ] self.givenControllerObject.updateManualCropCoordinates(minMaxVertices) image = self.givenControllerObject.showManualCropImage() self.canvas.axes.clear() self.canvas.axes.axis("off") self.canvas.axes.imshow(image) self.canvas.draw() if self.updateSecondImage is not None: self.updateSecondImage.canvas.axes.clear() self.updateSecondImage.canvas.axes.axis("off") self.updateSecondImage.canvas.axes.imshow( self.givenControllerObject.getCroppedImage(0)) self.updateSecondImage.canvas.draw() self.listOfControllers[ self.keyForController] = self.givenControllerObject def onmove_for_crop(self, event): if self.pressevent is None: return self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) self.canvas.draw() def disconnectClickListnerFromCurrentImageForCrop(self): try: self.canvas.mpl_disconnect(self.cid1) self.canvas.mpl_disconnect(self.cid2) self.canvas.mpl_disconnect(self.cid3) self.updateSecondImage = None except: pass def getCurrentScrollParam(self): self.currentVerticalSliderValue = self.scroll.verticalScrollBar( ).value() self.currentHorizontalSliderValue = self.scroll.horizontalScrollBar( ).value() def resetCurrentScrollParam(self): self.scroll.verticalScrollBar().setValue( self.currentVerticalSliderValue) self.scroll.horizontalScrollBar().setValue( self.currentHorizontalSliderValue) def resize(self, event): # on resize reposition the navigation toolbar to (0,0) of the axes. x, y = self.fig.axes[0].transAxes.transform((0, 0)) figw, figh = self.fig.get_size_inches() ynew = figh * self.fig.dpi - y - self.mpl_toolbar.frameGeometry( ).height() self.mpl_toolbar.move(x, ynew) def connectClickListnerToCurrentImageForAnnotate(self, givenController, updateSecondImage=None): self.cid4 = self.canvas.mpl_connect("button_press_event", self.on_press_for_annotate) self.cid7 = self.canvas.mpl_connect('pick_event', self.onpick) #self.cid7 = self.canvas.mpl_connect('button_press_event', self.right_click_press_for_annotate) self.givenControllerObject = givenController self.updateSecondImage = updateSecondImage self.pressevent = None def autoAnnotateOnOverlay(self, autoDetectedObjects): for index, row in autoDetectedObjects.iterrows(): print(row.bbox3) #if self.pressevent is None: # return #self.x1 = event.xdata #self.y1 = event.ydata self.rect.set_width(row.bbox3 - row.bbox1) self.rect.set_height(row.bbox2 - row.bbox0) self.rect.set_xy((row.bbox1, row.bbox0)) self.canvas.draw() self.rect = Rectangle((row.bbox1, row.bbox0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.patchesTotal = self.patchesTotal + 1 if [row.bbox1, row.bbox0, row.bbox3, row.bbox2] not in self.autoDetectedBoxXYValues: self.autoDetectedBoxXYValues.append( [row.bbox1, row.bbox0, row.bbox3, row.bbox2]) # Update latest values self.updateAllBoxListDictionary() #print(self.typeOfAnnotation) '''if self.typeOfAnnotation == "eraseBox": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation not in ["eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead"]: if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1])''' #self.canvas.draw() #return(self.canvas) def on_press_for_annotate(self, event): # try: # self.rect.remove() # except: # pass if (self.mpl_toolbar.mode): return if event.button == 1: self.cid5 = self.canvas.mpl_connect("motion_notify_event", self.onmove_for_annotate) self.cid6 = self.canvas.mpl_connect("button_release_event", self.on_release_for_annotate) self.x0 = event.xdata self.y0 = event.ydata self.rect = Rectangle((self.x0, self.y0), 1, 1, picker=True) self.rect._alpha = 1 if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "autoDetcted": self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "eraseBox": self.rect._edgecolor = (0, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "oneWormLive": self.rect._edgecolor = (0, 0, 1, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "multiWormLive": self.rect._edgecolor = (1, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "oneWormDead": self.rect._edgecolor = (1, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) elif self.typeOfAnnotation == "multiWormDead": self.rect._edgecolor = (1, 1, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.patchesTotal = self.patchesTotal + 1 def on_release_for_annotate(self, event): if (self.mpl_toolbar.mode): return if event.button == 1: self.canvas.mpl_disconnect(self.cid5) if (self.rect.get_height() == 1) and (self.rect.get_width() == 1): self.rect.remove() self.pressevent = None self.canvas.mpl_disconnect(self.cid6) if self.typeOfAnnotation == "eraseBox": #print(self.typeOfAnnotation) self.eraseBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: #print(self.typeOfAnnotation) self.addBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "oneWormLive": self.oneWormLiveBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "multiWormLive": self.multiWormLiveBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "oneWormDead": self.oneWormDeadBoxXYValues.append(self.tempList[-1]) self.tempList = [] if self.typeOfAnnotation == "multiWormDead": self.multiWormDeadBoxXYValues.append(self.tempList[-1]) self.tempList = [] # updateAllBoxListDictionary(self) self.updateAllBoxListDictionary() # self.givenControllerObject.updateManualCropCoordinates(minMaxVertices) # image = self.givenControllerObject.showManualCropImage() # self.canvas.axes.clear() # self.canvas.axes.axis("off") # self.canvas.axes.imshow(image) # self.canvas.draw() # if self.updateSecondImage is not None: # self.updateSecondImage.canvas.axes.clear() # self.updateSecondImage.canvas.axes.axis("off") # self.updateSecondImage.canvas.axes.imshow(self.givenControllerObject.getCroppedImage(0)) # self.updateSecondImage.canvas.draw() def onmove_for_annotate(self, event): if self.pressevent is None: return self.x1 = event.xdata self.y1 = event.ydata self.rect.set_width(self.x1 - self.x0) self.rect.set_height(self.y1 - self.y0) self.rect.set_xy((self.x0, self.y0)) #print(self.typeOfAnnotation) if self.typeOfAnnotation == "eraseBox": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation not in [ "eraseBox", "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead" ]: if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormLive": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "oneWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) if self.typeOfAnnotation == "multiWormDead": if [self.x0, self.y0, self.x1, self.y1] not in self.tempList: self.tempList.append([self.x0, self.y0, self.x1, self.y1]) self.canvas.draw() def getEraseBoxXYValues(self): return (self.eraseBoxXYValues) def getAutoDetctedBoxXYValues(self): return (self.autoDetectedBoxXYValues) def getAddBoxXYValues(self): return (self.addBoxXYValues) def getOneWormLiveBoxXYValues(self): return (self.oneWormLiveBoxXYValues) def getMultiWormLiveBoxXYValues(self): return (self.multiWormLiveBoxXYValues) def getOneWormDeadBoxXYValues(self): return (self.oneWormDeadBoxXYValues) def getMultiWormDeadBoxXYValues(self): return (self.multiWormDeadBoxXYValues) def resetEraseBoxXYValues(self): self.eraseBoxXYValues = [] def resetAutoDetctedBoxXYValues(self): self.autoDetectedBoxXYValues = [] def resetAddBoxXYValues(self): self.addBoxXYValues = [] def resetOneWormLiveBoxXYValues(self): self.oneWormLiveBoxXYValues = [] def resetMultiWormLiveBoxXYValues(self): self.multiWormLiveBoxXYValues = [] def resetOneWormDeadBoxXYValues(self): self.oneWormDeadBoxXYValues = [] def resetMultiWormDeadBoxXYValues(self): self.multiWormDeadBoxXYValues = [] def disconnectClickListnerFromCurrentImageForAnnotate(self): try: self.canvas.mpl_disconnect(self.cid4) self.canvas.mpl_disconnect(self.cid7) self.updateSecondImage = None except: pass def onpick(self, event): #if event.button == 3: #"3" is the right button # print "you click the right button" # print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( # event.button, event.x, event.y, event.xdata, event.ydata) #Get the coordinates of the mouse click #I create the action if (self.mpl_toolbar.mode): return if event.mouseevent.button == 3: self.objectPicked = event.artist noteAction_1 = QtWidgets.QAction('Delete Box', self) noteAction_2 = QtWidgets.QAction('Classify', self) #noteAction_5 = QtWidgets.QAction('Add Once',self) #noteAction_2 = QtWidgets.QAction('Add Through',self) #noteAction_3 = QtWidgets.QAction('Mask Here',self) #noteAction_4 = QtWidgets.QAction('Mask Through',self) #noteAction_6 = QtWidgets.QAction('Live here',self) #noteAction_7 = QtWidgets.QAction('Live all',self) #noteAction_8 = QtWidgets.QAction('Dead here',self) #noteAction_9 = QtWidgets.QAction('Dead all',self) #I create the context menu self.popMenu = QtWidgets.QMenu(self) self.popMenu.addAction(noteAction_1) self.popMenu.addAction(noteAction_2) # self.popMenu.addAction(noteAction_2) # self.popMenu.addAction(noteAction_3) # self.popMenu.addAction(noteAction_4) # self.popMenu.addAction(noteAction_5) # self.popMenu.addAction(noteAction_6) # self.popMenu.addAction(noteAction_7) # self.popMenu.addAction(noteAction_8) # self.popMenu.addAction(noteAction_9) cursor = QtGui.QCursor() #self.connect(self.figure_canvas, SIGNAL("clicked()"), self.context_menu) #self.popMenu.exec_(self.mapToGlobal(event.globalPos())) noteAction_1.triggered.connect(lambda: self.removeThisArea(1)) noteAction_2.triggered.connect( lambda: self.classifyAsCurrentSelection(1)) # noteAction_2.triggered.connect(lambda :self.removeThisArea(2)) # noteAction_3.triggered.connect(lambda :self.removeThisArea(3)) # noteAction_4.triggered.connect(lambda :self.removeThisArea(4)) # noteAction_5.triggered.connect(lambda :self.removeThisArea(5)) # noteAction_6.triggered.connect(lambda :self.removeThisArea(5)) # noteAction_7.triggered.connect(lambda :self.removeThisArea(2)) # noteAction_8.triggered.connect(lambda :self.removeThisArea(3)) # noteAction_9.triggered.connect(lambda :self.removeThisArea(4)) self.popMenu.popup(cursor.pos()) else: return def right_click_press_for_annotate(self, event): if (self.mpl_toolbar.mode): return if event.button == 3: #"3" is the right button # print "you click the right button" # print 'button=%d, x=%d, y=%d, xdata=%f, ydata=%f'%( # event.button, event.x, event.y, event.xdata, event.ydata) #Get the coordinates of the mouse click #I create the action noteAction_1 = QtWidgets.QAction('Remove', self) noteAction_2 = QtWidgets.QAction('Add', self) #I create the context menu self.popMenu = QtWidgets.QMenu(self) self.popMenu.addAction(noteAction_1) self.popMenu.addAction(noteAction_2) cursor = QtGui.QCursor() #self.connect(self.figure_canvas, SIGNAL("clicked()"), self.context_menu) #self.popMenu.exec_(self.mapToGlobal(event.globalPos())) noteAction_1.triggered.connect( lambda eventData=object: self.removeThisArea(eventData)) noteAction_2.triggered.connect( lambda eventData=object: self.classifyAsCurrentSelection( eventData)) self.popMenu.popup(cursor.pos()) def classifyAsCurrentSelection(self, caseNumber): # Get all the list values for this frame self.updateAllListFromAllBoxListDictionary() print("INSIDE classifyAsCurrentSelection") try: if caseNumber == 1: # green delete print(type(self.objectPicked)) X0 = self.objectPicked.get_xy()[0] Y0 = self.objectPicked.get_xy()[1] X1 = X0 + self.objectPicked.get_width() Y1 = Y0 + self.objectPicked.get_height() selectedBoxCoords = [X0, Y0, X1, Y1] if self.currentSelectedOption == "eraseBox": #self.autoDetectedBoxXYValues.remove(selectedBoxCoords) #self.eraseBoxXYValues.append(selectedBoxCoords) print("Use Delte Option! Right Click -> Delete Box") if self.currentSelectedOption == "autoDetcted": #self.autoDetectedBoxXYValues.remove(selectedBoxCoords) #self.addBoxXYValues.append(selectedBoxCoords) print("Already Selected!") if self.currentSelectedOption not in [ "oneWormLive", "multiWormLive", "oneWormDead", "multiWormDead", "autoDetcted" ]: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.addBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "oneWormLive" and selectedBoxCoords not in self.oneWormLiveBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.oneWormLiveBoxXYValues.append(selectedBoxCoords) self.canvas.draw() self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (0, 0, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) self.canvas.draw() if self.currentSelectedOption == "multiWormLive" and selectedBoxCoords not in self.multiWormLiveBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.multiWormLiveBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 1, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "oneWormDead" and selectedBoxCoords not in self.oneWormDeadBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.oneWormDeadBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 0, 0, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) if self.currentSelectedOption == "multiWormDead" and selectedBoxCoords not in self.multiWormDeadBoxXYValues: self.autoDetectedBoxXYValues.remove(selectedBoxCoords) self.multiWormDeadBoxXYValues.append(selectedBoxCoords) self.rect.set_width(X1 - X0) self.rect.set_height(Y1 - Y0) self.rect.set_xy((X0, Y0)) self.rect = Rectangle((X0, Y0), 1, 1, picker=True) self.rect._alpha = 1 self.rect._edgecolor = (1, 1, 1, 1) self.rect._facecolor = (0, 0, 0, 0) self.canvas.draw() self.rect._linewidth = 1 self.rect.set_linestyle('dashed') self.rect.addName = self.typeOfAnnotation self.pressevent = 1 self.canvas.axes.add_patch(self.rect) except: print("Delete and Redraw!") # updateAllBoxListDictionary(self) self.updateAllBoxListDictionary() def removeThisArea(self, caseNumber): # Get all the list values for this frame self.updateAllListFromAllBoxListDictionary() if caseNumber == 1: # green delete print(type(self.objectPicked)) X0 = self.objectPicked.get_xy()[0] Y0 = self.objectPicked.get_xy()[1] X1 = X0 + self.objectPicked.get_width() Y1 = Y0 + self.objectPicked.get_height() removeBoxCoords = [X0, Y0, X1, Y1] #print(removeBoxCoords) self.objectPicked.remove() self.patchesTotal = self.patchesTotal - 1 try: if removeBoxCoords in self.eraseBoxXYValues: self.eraseBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.addBoxXYValues: self.addBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.oneWormLiveBoxXYValues: #print(self.oneWormLiveBoxXYValues) self.oneWormLiveBoxXYValues.remove(removeBoxCoords) #print(self.oneWormLiveBoxXYValues) if removeBoxCoords in self.multiWormLiveBoxXYValues: self.multiWormLiveBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.oneWormDeadBoxXYValues: self.oneWormDeadBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.multiWormDeadBoxXYValues: self.multiWormDeadBoxXYValues.remove(removeBoxCoords) if removeBoxCoords in self.autoDetectedBoxXYValues: print(len(self.autoDetectedBoxXYValues)) self.autoDetectedBoxXYValues.remove(removeBoxCoords) print(len(self.autoDetectedBoxXYValues)) except: pass # elif caseNumber == 2: # orange add all # self.objectPicked._facecolor = (1.0, 0.64, 0.0,0.5) # self.objectPicked._alpha = 0.5 # self.objectPicked.addName ="addAll" # elif caseNumber == 3: # black # self.objectPicked._facecolor = (0,0, 0, 0.8) # self.objectPicked._alpha = 0.8 # self.objectPicked.addName ="eraseBox" # elif caseNumber == 4: # self.objectPicked._facecolor = ( 0, 0, 0, 0.2) # self.objectPicked._alpha = 0.2 # self.objectPicked.addName ="deleteAll" # elif caseNumber == 5: # self.objectPicked.set_color("C2") # self._edgecolor = (0, 0, 0, 0) # self.objectPicked.addName ="addBox" self.canvas.draw() #print(len(self.canvas.axes.patches)) #self.canvas.draw() #self.on_release_for_annotate(None) def initializeAnnotationDictionary(self): self.currentAnnotationFrame = None self.annotationRecordDictionary = {} def updateAnnotationDictionary(self): # When you move away from current Frame call this previousFrame = self.currentAnnotationFrame if previousFrame is not None: self.annotationRecordDictionary[str( previousFrame)] = self.canvas.axes.patches def getAnnotationDictionary(self): return self.annotationRecordDictionary def applyAnnotationDictionary(self, frameNumber): self.currentAnnotationFrame = frameNumber self.canvas.axes.patches = [] if str(frameNumber) in self.annotationRecordDictionary.keys(): for patch in self.annotationRecordDictionary[str(frameNumber)]: self.canvas.axes.add_patch(patch) def setAnnotationDictionary(self): pass