class MatplotlibWidget(QWidget): def __init__(self, parent = None): QWidget.__init__(self, parent) self.CanvasWindow = parent self.canvas = FigureCanvas(Figure(dpi=DPI)) # attach matplotlib canvas to layout vertical_layout = QVBoxLayout() vertical_layout.addWidget(self.canvas) self.setLayout(vertical_layout) # canvas setup self.canvas.axis = self.canvas.figure.add_subplot(111) self.canvas.figure.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0) self.canvas.axis.spines['top'].set_visible(False) self.canvas.axis.spines['left'].set_visible(False) self.canvas.axis.set_xlim(AXIS_LIMIT) self.canvas.axis.set_ylim(AXIS_LIMIT) # variables self.pressed = False self.plotted = False self.pen = False self.N = 0 self.arr_drawing_complex = [] self.time = [] self.arr_coordinates = [] self.line_draw, = self.canvas.axis.plot([], [], c='#eb4034', linewidth=1) self.arr_drawing = np.empty((1,2)) # listeners self.canvas.mpl_connect('button_press_event', self.on_press) self.canvas.mpl_connect('motion_notify_event', self.on_motion) self.canvas.mpl_connect('button_release_event', self.on_release) def on_press(self, event): if len(self.arr_drawing[1:]) > 0: self.animation._stop() if self.plotted: # reset variables self.plotted = False self.arr_drawing = np.empty((1,2)) self.N = 0 # clear and reload subplot self.canvas.axis.clear() self.canvas.axis.set_xlim(AXIS_LIMIT) self.canvas.axis.set_ylim(AXIS_LIMIT) self.canvas.draw() self.pressed = True self.pen = True def on_motion(self, event): if self.pressed: self.plotted = True # plot drawing points coordinates = [event.xdata, event.ydata] self.arr_drawing = np.append(self.arr_drawing, [coordinates], axis=0) self.line_draw.set_data(self.arr_drawing[1:,0],self.arr_drawing[1:,1]) self.canvas.axis.draw_artist(self.line_draw) self.canvas.blit(self.canvas.axis.bbox) self.canvas.flush_events() def on_release(self, event): self.pressed = False self.line_draw.set_data([],[]) if len(self.arr_drawing) > 0: self.runAnimation() def getSizes(self): arr_radius = np.array([item['amplitude'] for item in self.ft.arr_epicycles]) rr_pix = (self.canvas.axis.transData.transform(np.vstack([arr_radius, arr_radius]).T) - self.canvas.axis.transData.transform(np.vstack([np.zeros(self.N), np.zeros(self.N)]).T)) rpix, _ = rr_pix.T size_pt = (2*rpix/DPI*72)**2 return size_pt def runAnimation(self): self.animation = animation.FuncAnimation( self.canvas.figure, self.animate, init_func=self.init, interval=ANIMATION_INTERVAL, blit=True) def init(self): # set new values if self.pen: self.arr_drawing_complex = [complex(coordinates[0], coordinates[1]) for coordinates in self.arr_drawing[1:]] else: self.arr_drawing_complex = [complex(coordinates[0], coordinates[1]) for coordinates in self.arr_drawing[:]] self.N = len(self.arr_drawing_complex) self.CanvasWindow.horizontalSlider.setMaximum(self.N) self.CanvasWindow.horizontalSlider.setValue(self.N) # calculate fourier transform via FFT algorithm self.ft = FourierTransform(self.arr_drawing_complex) self.ft.toEpicycles() # calculate all points in time range from 0 to 2pi self.time = np.linspace(0,2*pi,endpoint = False, num=self.N) self.arr_coordinates = np.array([self.ft.getPoint(dt) for dt in self.time]) # create all components responsible for showing animation self.circle = self.canvas.axis.scatter([],[], fc='None', ec='#9ac7e4', lw=1) self.circle.set_sizes(self.getSizes()) self.line_connect, = self.canvas.axis.plot([], [], c='#9ac7e4', lw=1) self.line_plot, = self.canvas.axis.plot([], [], c='#4c6bd5', lw=2) self.line_plot_all, = self.canvas.axis.plot([], [], c='#4c6bd5', lw=0.5) return [self.circle, self.line_connect, self.line_plot, self.line_plot_all] def animate(self,i): s = self.CanvasWindow.horizontalSlider.value()+1 # update components self.circle.set_offsets(self.arr_coordinates[:,:s][i%self.N,:-1]) self.line_connect.set_data(self.arr_coordinates[:,:s][i%self.N,:,0],self.arr_coordinates[:,:s][i%self.N,:,1]) self.line_plot.set_data(self.arr_coordinates[:,:s][:i%self.N+1,-1,0],self.arr_coordinates[:,:s][:i%self.N+1,-1,1]) self.line_plot_all.set_data(self.arr_coordinates[:,:s][:,-1,0],self.arr_coordinates[:,:s][:,-1,1]) return [self.circle, self.line_connect, self.line_plot, self.line_plot_all]
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)