class LoadImage(QtWidgets.QMainWindow, Ui_MainWindow): """Load board images from the menu and the pins icon list.""" def __init__(self): """Set up the user interference from the QT Designer. Initiate the menu bar and clicks on menu options. """ super().__init__() """Set up the user interference from QT Designer.""" self.setupUi(self) self.df_cls = None self.board_type = None self.board_label = None self.first_load = True self.first_resize = True self.resize_timer = None self.list_items = {} self.centralwidget.setDisabled(True) # Creating two menu options: Boards and Export self.file_menu = self.menuBar().addMenu('&File') self.boards_menu = self.menuBar().addMenu('&Boards') # self.export_menu = self.menuBar().addMenu('&Export') self.refresh_menus() self.export_action = self.file_menu.addAction('Save Board Label') self.run_macro_action = self.file_menu.addAction('Run the Macro File') self.exit_action = self.file_menu.addAction('Exit') self.exit_action.triggered.connect(QtWidgets.qApp.quit) # self.export_action = self.export_menu.addAction('Save Board Label') def refresh_menus(self): self.boards_menu.clear() unprocessed_menu = self.boards_menu.addMenu('Unprocessed') processed_menu = self.boards_menu.addMenu('Processed') config = ConfigParser() config.read('socketear.ini') file_path = os.path.normpath(config.get('path', 'dirpath')) # Going through the Board folder to create the appropriate actions for dirpath, dirnames, filenames in os.walk( os.path.join(file_path, 'Processed')): if 'pins' in dirnames and all( fn in filenames for fn in ['classification.csv', 'stitched.png', 'info.txt']): # Add actions for the right path action_cb = processed_menu.addAction( os.path.split(os.path.basename(dirpath))[-1]) action_cb.triggered.connect( functools.partial(self.load_image, dirpath)) for dirpath, dirnames, filenames in os.walk( os.path.join(file_path, 'Unprocessed')): if all(fn.endswith(".tif") for fn in filenames) and not dirnames: board_name = os.path.split(os.path.split(dirpath)[0])[1] save_path = os.path.join(file_path, 'Processed') action_nb = unprocessed_menu.addAction(board_name) action_nb.triggered.connect( functools.partial(self.load_model, dirpath, board_name, save_path)) def remove_board(self): """Remove the layer of matplotlib container and widgetlist. Also, disconnects all the buttons. """ self.mpl_vl.removeWidget(self.canvas) self.canvas.close() # self.mpl_vl.removeWidget(self.toolbar) # self.toolbar.close() self.mpl_figs.clear() self.mpl_figs.currentItemChanged.disconnect() self.nwo_button.disconnect() self.nco_button.disconnect() self.normal_button.disconnect() self.hnp_button.disconnect() self.sb_button.disconnect() self.type1_button.disconnect() self.type2_button.disconnect() self.type3_button.disconnect() self.type4_button.disconnect() self.reject_button.disconnect() # self.no_crack_button.disconnect() self.type_a_button.disconnect() self.type_b_button.disconnect() self.type_c_button.disconnect() self.type_d_button.disconnect() self.type_e_button.disconnect() self.refresh_sjr_button.disconnect() self.refresh_sjq_button.disconnect() self.sjr_image_button.disconnect() self.export_action.disconnect() self.export_action.setDisabled(True) def add_mpl(self): """Add a layer of matplotlib container.""" self.figure = Figure(figsize=(5, 4), dpi=100) self.canvas = FigureCanvas(self.figure) self.mpl_vl.addWidget(self.canvas) self.canvas.draw() # self.toolbar = NavigationToolbar(self.canvas, # self.mpl_window, # coordinates=True) # self.addToolBar(self.toolbar) def button_clicked(self, value): """Change the radio button. Updates the CSV file after a radio button was clicked. """ pin_index = self.mpl_figs.currentItem().data(32) if self.board_type == 'SJR': if isinstance(value, six.string_types): self.df_cls.set_value(pin_index, 'DYE Correction', value) elif isinstance(value, int): self.df_cls.set_value(pin_index, 'SJR Correction', value) elif self.board_type == 'SJQ': self.df_cls.set_value(pin_index, 'SJQ Correction', value) image_path = self.df_cls.loc[pin_index]['Image Path'] csv_path = os.path.split( os.path.split(os.path.normpath(image_path))[0])[0] # Saving the CSV file header = [ 'Row', 'Col', 'StitchedX', 'StitchedY', 'Pin', 'SJQ', 'SJR', 'DYE', 'SJQ Correction', 'SJR Correction', 'DYE Correction', 'Type Change', 'Sorted SJQ', 'Image Path', 'Label Coords', 'DYE Image Path' ] self.df_cls.to_csv(os.path.join(csv_path, 'classification.csv'), columns=header) def nwo_button_clicked(self, enabled): """Activate the nwo button for SJQ.""" if enabled: self.button_clicked(0) def nco_button_clicked(self, enabled): """Activate the nco button for SJQ.""" if enabled: self.button_clicked(1) def normal_button_clicked(self, enabled): """Activate the normal button for SJQ.""" if enabled: self.button_clicked(2) def hnp_button_clicked(self, enabled): """Activate the hnp button for SJQ.""" if enabled: self.button_clicked(3) def sb_button_clicked(self, enabled): """Activate the sb button for SJQ.""" if enabled: self.button_clicked(4) def type1_button_clicked(self, enabled): """Activate the type1 button for SJR.""" if enabled: self.button_clicked(0) def type2_button_clicked(self, enabled): """Activate the type2 button for SJR.""" if enabled: self.button_clicked(1) def type3_button_clicked(self, enabled): """Activate the normal button for SJR.""" if enabled: self.button_clicked(2) def type4_button_clicked(self, enabled): """Activate the type4 button for SJR.""" if enabled: self.button_clicked(3) def reject_button_clicked(self, enabled): """Activate the type4 button for SJR.""" if enabled: self.button_clicked(4) # def no_crack_button_clicked(self, enabled): # """Activate the no crack button for SJR.""" # if enabled: # self.button_clicked('O') def type_a_button_clicked(self, enabled): """Activate the typeA button for SJR.""" if enabled: self.button_clicked('A') def type_b_button_clicked(self, enabled): """Activate the typeB button for SJR.""" if enabled: self.button_clicked('B') def type_c_button_clicked(self, enabled): """Activate the typeC button for SJR.""" if enabled: self.button_clicked('C') def type_d_button_clicked(self, enabled): """Activate the typeD button for SJR.""" if enabled: self.button_clicked('D') def type_e_button_clicked(self, enabled): """Activate the typeE button for SJR.""" if enabled: self.button_clicked('E') def refresh(self, predictions, dye_predictions=None): # start_refresh = datetime.now() self.board_label = self.drawing_tool(shape=1000, pred=predictions, dyepred=dye_predictions) # print('remove scatters!') self.axes1_scatter.remove() self.axes2_scatter.remove() self.axes2_image = self.axes2.imshow(self.board_label) self.canvas.draw() self.background_board = self.canvas.copy_from_bbox(self.axes2.bbox) pin_index = self.mpl_figs.currentItem().data(32) self.load_pin_cls(pin_index) cx_image = self.df_cls.ix[pin_index, 'StitchedX'] cy_image = self.df_cls.ix[pin_index, 'StitchedY'] self.axes1_scatter = self.axes1.scatter(cy_image, cx_image, edgecolor='#ff01d0', marker='s', s=80, linewidth='2', facecolors='none') cx_label = self.df_cls.ix[pin_index, 'Label Coords'][0] * 1000 cy_label = self.df_cls.ix[pin_index, 'Label Coords'][1] * 1000 self.axes2_scatter = self.axes2.scatter(cy_label, cx_label, edgecolor='#ff01d0', marker='s', s=80, linewidth='2', facecolors='none') self.canvas.blit(self.axes1.bbox) self.canvas.blit(self.axes2.bbox) # print('timer canvas.draw: ', datetime.now() - start_refresh) # start_refresh = datetime.now() self.sort_classification() # print('timer sort_classification: ', datetime.now() - start_refresh) # start_refresh = datetime.now() self.pin_dividers() # print('timer pin_dividers: ', datetime.now() - start_refresh) self.mpl_figs.clear() # start_refresh = datetime.now() self.load_pins() # print('timer load_pins: ', datetime.now() - start_refresh) self.mpl_figs.setCurrentItem(self.mpl_figs.item(0)) board_pin_info = self.board_info + "\nPin name: {0}".format( str(self.df_cls.ix[pin_index, 'Pin'])) self.board_display_label.setText(board_pin_info) def refresh_sjq_clicked(self): """Redraw the label board for SJQ model.""" # start_refresh = datetime.now() print('Starting to refresh...') predictions = self.df_cls.set_index('Pin')['SJQ Correction'].to_dict() dye_predictions = None self.refresh(predictions, dye_predictions) # print('start_refresh: ', datetime.now() - start_refresh) def refresh_sjr_clicked(self): """Redraw the label board for SJQ model.""" # start_refresh = datetime.now() print('Starting to refresh...') predictions = self.df_cls.set_index('Pin')['SJR Correction'].to_dict() dye_predictions = self.df_cls.set_index( 'Pin')['DYE Correction'].to_dict() self.refresh(predictions, dye_predictions) # print('start_refresh: ', datetime.now() - start_refresh) def sjr_image_switch(self): """Switch the pin image and dyed pin image for SJR models.""" pin_index = self.mpl_figs.currentItem().data(32) if self.pin_dye_image == self.df_cls.loc[pin_index, 'Image Path']: self.pin_dye_image = self.df_cls.loc[pin_index, 'DYE Image Path'] self.pin_image.setPixmap( QtGui.QPixmap(QtGui.QImage(self.pin_dye_image))) elif self.pin_dye_image == self.df_cls.loc[pin_index, 'DYE Image Path']: self.pin_dye_image = self.df_cls.loc[pin_index, 'Image Path'] self.pin_image.setPixmap( QtGui.QPixmap(QtGui.QImage(self.pin_dye_image))) def on_click(self, event): if not event.inaxes: print('Clicked outside axes bounds but inside plot window') return if event.inaxes == self.axes1: self.df_cls['Image Coords'] = self.df_cls[[ 'StitchedX', 'StitchedY' ]].apply(tuple, axis=1) z = self.df_cls[['StitchedX', 'StitchedY']].values distance = np.linalg.norm( (z - np.array([event.ydata, event.xdata])), axis=1) row_index = np.argmin(distance) current_item = None for index in range(self.mpl_figs.count()): item = self.mpl_figs.item(index) if item.data(32) == row_index: current_item = item if current_item: self.mpl_figs.setCurrentItem(current_item) pin_index = current_item.data(32) self.load_pin_cls(pin_index) else: print('No item found for row {}'.format(row_index)) if event.inaxes == self.axes2: z = np.zeros(shape=(len(self.guide.pins), 2)) for j, pin in enumerate(self.guide.pins): z[j] = 1000 * np.array(self.guide.position(pin)) distance = np.linalg.norm( (z - np.array([event.ydata, event.xdata])), axis=1) k = np.argmin(distance) pin_name = self.guide.pins[k] row = self.df_cls.Pin[self.df_cls.Pin == pin_name].index.tolist()[0] current_item = None for index in range(self.mpl_figs.count()): item = self.mpl_figs.item(index) if item.data(32) == row: current_item = item if current_item: self.mpl_figs.setCurrentItem(current_item) pin_index = current_item.data(32) self.load_pin_cls(pin_index) else: print('No item found for row {}'.format(row)) def current_item_changed(self): """Update the zoom-in pin image and the board image position. Based on the items in the scrollable list(WidgetListItem). """ if self.mpl_figs.currentItem(): pin_index = self.mpl_figs.currentItem().data(32) self.load_pin_cls(pin_index) cx_label = self.df_cls.ix[pin_index, 'Label Coords'][0] * 1000 cy_label = self.df_cls.ix[pin_index, 'Label Coords'][1] * 1000 try: self.axes2_scatter.remove() except: pass self.axes2_scatter = self.axes2.scatter(cy_label, cx_label, edgecolor='#ff01d0', marker='s', s=80, linewidth='2', facecolors='none') cx_image = self.df_cls.ix[pin_index, 'StitchedX'] cy_image = self.df_cls.ix[pin_index, 'StitchedY'] try: self.axes1_scatter.remove() except: pass self.axes1_scatter = self.axes1.scatter(cy_image, cx_image, edgecolor='#ff01d0', marker='s', s=80, linewidth='2', facecolors='none') start_canvas = datetime.now() self.canvas.restore_region(self.background_image) self.canvas.restore_region(self.background_board) self.axes1.draw_artist(self.axes1_scatter) self.axes2.draw_artist(self.axes2_scatter) self.canvas.blit(self.axes1.bbox) self.canvas.blit(self.axes2.bbox) self.canvas.flush_events() # print('canvas: ', datetime.now() - start_canvas) board_pin_info = self.board_info + "\nPin name: {0}".format( str(self.df_cls.ix[pin_index, 'Pin'])) self.board_display_label.setText(board_pin_info) def load_pin_cls(self, pin_index): """Load the zoom-in pin image. And activate the appropriate radio button. """ self.pin_dye_image = self.df_cls.ix[pin_index, 'Image Path'] self.pin_image.setScaledContents(True) self.pin_image.setPixmap( QtGui.QPixmap(QtGui.QImage(self.pin_dye_image))) if self.board_type == 'SJR': classification = self.df_cls.ix[pin_index, 'SJR Correction'] dye_cls = self.df_cls.ix[pin_index, 'DYE Correction'] if classification == 0: self.type1_button.setChecked(True) elif classification == 1: self.type2_button.setChecked(True) elif classification == 2: self.type3_button.setChecked(True) elif classification == 3: self.type4_button.setChecked(True) elif classification == 4: self.reject_button.setChecked(True) # if dye_cls == 'O': # self.no_crack_button.setChecked(True) if dye_cls == 'A': self.type_a_button.setChecked(True) elif dye_cls == 'B': self.type_b_button.setChecked(True) elif dye_cls == 'C': self.type_c_button.setChecked(True) elif dye_cls == 'D': self.type_d_button.setChecked(True) elif dye_cls == 'E': self.type_e_button.setChecked(True) elif self.board_type == 'SJQ': classification = self.df_cls.ix[pin_index, 'SJQ Correction'] if classification == 0: self.nwo_button.setChecked(True) elif classification == 1: self.nco_button.setChecked(True) elif classification == 2: self.normal_button.setChecked(True) elif classification == 3: self.hnp_button.setChecked(True) elif classification == 4: self.sb_button.setChecked(True) def sort_sjq(self, row): if row['SJQ Correction'] == 0: value = 'a' elif row['SJQ Correction'] == 1: value = 'b' elif row['SJQ Correction'] == 2: value = 'e' elif row['SJQ Correction'] == 3: value = 'c' elif row['SJQ Correction'] == 4: value = 'd' else: raise ValueError('Unknown classification') return value def set_buttons(self, column_names): if all(cn in column_names for cn in ['SJR', 'SJQ', 'DYE']): if not(self.df_cls['SJR'].isnull().all()) and \ not(self.df_cls['DYE'].isnull().all()) and \ self.df_cls['SJQ'].isnull().all(): self.board_type = 'SJR' self.nwo_button.setDisabled(True) self.nco_button.setDisabled(True) self.normal_button.setDisabled(True) self.hnp_button.setDisabled(True) self.sb_button.setDisabled(True) self.refresh_sjq_button.setDisabled(True) self.type1_button.setDisabled(False) self.type2_button.setDisabled(False) self.type3_button.setDisabled(False) self.type4_button.setDisabled(False) self.reject_button.setDisabled(False) # self.no_crack_button.setDisabled(False) self.type_a_button.setDisabled(False) self.type_b_button.setDisabled(False) self.type_c_button.setDisabled(False) self.type_d_button.setDisabled(False) self.type_e_button.setDisabled(False) self.refresh_sjr_button.setDisabled(False) self.sjr_image_button.setDisabled(False) elif not(self.df_cls['SJR'].isnull().all()) and \ not(self.df_cls['SJQ'].isnull().all()) and \ self.df_cls['DYE'].isnull().all(): self.board_type = 'SJQ' self.nwo_button.setDisabled(False) self.nco_button.setDisabled(False) self.normal_button.setDisabled(False) self.hnp_button.setDisabled(False) self.sb_button.setDisabled(False) self.refresh_sjq_button.setDisabled(False) self.type1_button.setDisabled(True) self.type2_button.setDisabled(True) self.type3_button.setDisabled(True) self.type4_button.setDisabled(True) self.reject_button.setDisabled(True) # self.no_crack_button.setDisabled(True) self.type_a_button.setDisabled(True) self.type_b_button.setDisabled(True) self.type_c_button.setDisabled(True) self.type_d_button.setDisabled(True) self.type_e_button.setDisabled(True) self.refresh_sjr_button.setDisabled(True) self.sjr_image_button.setDisabled(True) else: raise ValueError('Incomplete CSV file') else: raise ValueError('Unknown board') def sort_classification(self): if self.board_type == 'SJQ': self.df_cls['Sorted SJQ'] = self.df_cls['SJQ Correction'] self.df_cls['Sorted SJQ'] = self.df_cls.apply(self.sort_sjq, axis=1) self.df_cls = self.df_cls.sort_values( by=['Sorted SJQ', 'SJR Correction'], ascending=[True, True]) self.df_cls = self.df_cls.reset_index(drop=True) if self.board_type == 'SJR': self.df_cls = self.df_cls.sort_values( by=['SJR Correction', 'DYE Correction'], ascending=[True, False]) self.df_cls = self.df_cls.reset_index(drop=True) def pin_dividers(self): # Create columns to indicate the divider location for the QListWidgetItem if self.board_type == 'SJQ': self.df_cls['Type Change'] = self.df_cls['SJQ Correction'].shift( -1) != self.df_cls['SJQ Correction'] if self.df_cls.ix[0, 'SJQ Correction'] != self.df_cls.ix[ 1, 'SJQ Correction']: self.df_cls.ix[0, 'Type Change'] = True else: self.df_cls.ix[0, 'Type Change'] = False elif self.board_type == 'SJR': self.df_cls['Type Change'] = self.df_cls['SJR Correction'].shift( -1) != self.df_cls['SJR Correction'] if self.df_cls.ix[0, 'SJR Correction'] != self.df_cls.ix[ 1, 'SJR Correction']: self.df_cls.ix[0, 'Type Change'] = True else: self.df_cls.ix[0, 'Type Change'] = False def load_pins(self, first_load=False): if first_load: self.list_items = {} count_image_cat = 0 # Create a list of the 'Image Path' column image_path_values = self.df_cls['Image Path'].values.tolist() for image_path in self.df_cls['Image Path'].tolist(): if first_load: icon = QtGui.QIcon(image_path) else: icon = self.list_items[image_path] # Adding the pin images to the scrollable list (WidgetListItem) item = QtWidgets.QListWidgetItem() item.setIcon(icon) self.list_items[image_path] = icon index = image_path_values.index(image_path) item.setData(32, index) self.mpl_figs.addItem(item) count_image_cat += 1 if self.df_cls.iloc[index]['Type Change'] == True: mod_of_5 = count_image_cat % 5 if mod_of_5 != 0: amount_of_white = (5 - mod_of_5) + 5 else: amount_of_white = 5 for x in range(0, amount_of_white): item = QtWidgets.QListWidgetItem() # delimiter item.setData(32, -1) item.setFlags( QtCore.Qt.NoItemFlags) # item should not be selectable self.mpl_figs.addItem(item) count_image_cat = 0 def get_pins(self, root): """Load list of pin icons.""" # Reading the CSV file from the appropriate path self.df_cls = pd.read_csv(os.path.join(root, 'classification.csv')) if not ('SJQ Correction' in self.df_cls.columns): self.df_cls['SJQ Correction'] = self.df_cls['SJQ'].copy() if not ('SJR Correction' in self.df_cls.columns): self.df_cls['SJR Correction'] = self.df_cls['SJR'].copy() if not ('DYE Correction' in self.df_cls.columns): self.df_cls['DYE Correction'] = self.df_cls['DYE'].copy() # Adding the Pin column and adding the pin names to it by # combining the Col and Row Columns self.df_cls['Pin'] = self.df_cls[['Row', 'Col']].apply( lambda x: '{}{}{}'.format(x[0], '_', x[1]), axis=1) # Adding a label for the index column self.df_cls.columns.names = ['Index'] column_names = list(self.df_cls) self.set_buttons(column_names) self.sort_classification() df_pin_list = self.df_cls['Pin'].tolist() # Adding the path of the each pin image to the Dataframe # Create a column in the dataframe to store the image paths self.df_cls['Image Path'] = np.nan # Create a column in the dataframe to store the DYE image paths self.df_cls['DYE Image Path'] = np.nan # Matching the pins with the correct image and adding them to # the 'Image Path' column # for every file (image_path) in the pins folder: for f in os.listdir(os.path.join(root, 'pins')): if f.endswith("_1.tif"): image_path = os.path.join(os.getcwd(), os.path.join(root, 'pins'), f) pin_name = os.path.basename(image_path).replace("_1.tif", "") # finding the index of the row where the pin is located in # the dataframe if pin_name in df_pin_list: index = self.df_cls[self.df_cls['Pin'] == pin_name].index[0] # adding the pin's image path to the same row in the dataframe # where the pin is located self.df_cls.loc[index, 'Image Path'] = image_path if self.board_type == 'SJR': if 'dye' in os.listdir(os.path.join(root, '')): for f in os.listdir(os.path.join(root, 'dye')): dye_path = os.path.join(os.getcwd(), os.path.join(root, 'dye'), f) pin_name = os.path.basename(dye_path).replace("_1.tif", "") if pin_name in df_pin_list: index = self.df_cls[self.df_cls['Pin'] == pin_name].index[0] self.df_cls.loc[index, 'DYE Image Path'] = dye_path else: print('Unable to locate the DYE folder. Thus no dye images') self.sjr_image_button.setDisabled(True) self.pin_dividers() self.load_pins(first_load=True) def load_board(self, root): """Load the appropriate board based on the menu clicked.""" self.centralwidget.setDisabled(True) print('Path: ', root) board_name = os.path.basename(root) print('Board name: ', board_name) print('Board type: ', self.board_type) new_font = QtGui.QFont("Arial", 10, QtGui.QFont.Bold) self.board_display_label.setFont(new_font) self.board_info = "Board name: {0}\nBoard type: {1}".format( board_name, self.board_type) self.board_display_label.setText(self.board_info) with open(os.path.join(root, 'info.txt')) as json_file: data = json.load(json_file) print('guide path: ', data['guidePath']) csvpath = data['guidePath'] b2p_ratio = int(data['b2p_ratio']) from guides.generalguide import GeneralGuide self.guide = GeneralGuide(csvpath, b2p_ratio) pin_coords = [] for pin in self.df_cls['Pin'].tolist(): if pin in self.guide.pins: pin_coords.append(tuple(self.guide.position(pin))) self.df_cls['Label Coords'] = pd.Series(pin_coords, index=self.df_cls.index) self.drawing_tool = DrawPredictions(guide=self.guide, mode=self.board_type) cls_corrected_col = "{} Correction".format(self.board_type) # print('cls_corrected_col', cls_corrected_col) predictions = self.df_cls.set_index('Pin')[cls_corrected_col].to_dict() if self.board_type == 'SJR': dye_predictions = self.df_cls.set_index( 'Pin')['DYE Correction'].to_dict() self.board_label = self.drawing_tool(shape=1000, pred=predictions, dyepred=dye_predictions) elif self.board_type == 'SJQ': self.board_label = self.drawing_tool(shape=1000, pred=predictions) else: raise ValueError('Unknown board') self.board_img = mpimg.imread(os.path.join(root, 'stitched.png')) gs = gridspec.GridSpec(1, 2) gs.update(left=0.005, right=0.99, wspace=0.05) self.axes1 = self.figure.add_subplot(gs[0]) self.axes1.get_xaxis().set_visible(False) self.axes1.get_yaxis().set_visible(False) self.axes2 = self.figure.add_subplot(gs[1]) self.axes2.get_xaxis().set_visible(False) self.axes2.get_yaxis().set_visible(False) # print('init scatter') self.axes1_scatter = None self.axes2_scatter = None self.canvas.mpl_connect('resize_event', self.connect_resize) self.export_action.triggered.connect( functools.partial(self.save_board_label, root)) self.export_action.setDisabled(False) self.run_macro_action.triggered.connect( functools.partial(self.run_excel_macro, root)) self.first_load = False self.centralwidget.setDisabled(False) def connect_resize(self, event): print('resize event', event) self.centralwidget.setDisabled(True) self.perform_resize() # try: # print('resize event try', event) # self.resize_timer.cancel() # except: # pass # # self.resize_timer = Timer(1, self.perform_resize) # self.resize_timer.start() def perform_resize(self): # print('perform_resize') self.axes1.imshow(self.board_img) self.axes2.imshow(self.board_label) try: self.axes1_scatter.remove() except: print('Unable to remove axes1_scatter') pass try: self.axes2_scatter.remove() except: print('Unable to remove axes2_scatter') pass self.canvas.draw() self.background_image = self.canvas.copy_from_bbox(self.axes1.bbox) self.background_board = self.canvas.copy_from_bbox(self.axes2.bbox) self.centralwidget.setDisabled(False) def connect_click(self): """Just testing this.""" # connect the click or arrow keys press on the the scrollable # image list to the currentItemChanged self.mpl_figs.currentItemChanged.connect(self.current_item_changed) self.nwo_button.toggled.connect(self.nwo_button_clicked) self.nco_button.toggled.connect(self.nco_button_clicked) self.normal_button.toggled.connect(self.normal_button_clicked) self.hnp_button.toggled.connect(self.hnp_button_clicked) self.sb_button.toggled.connect(self.sb_button_clicked) self.type1_button.toggled.connect(self.type1_button_clicked) self.type2_button.toggled.connect(self.type2_button_clicked) self.type3_button.toggled.connect(self.type3_button_clicked) self.type4_button.toggled.connect(self.type4_button_clicked) self.reject_button.toggled.connect(self.reject_button_clicked) # self.no_crack_button.toggled.connect(self.no_crack_button_clicked) self.type_a_button.toggled.connect(self.type_a_button_clicked) self.type_b_button.toggled.connect(self.type_b_button_clicked) self.type_c_button.toggled.connect(self.type_c_button_clicked) self.type_d_button.toggled.connect(self.type_d_button_clicked) self.type_e_button.toggled.connect(self.type_e_button_clicked) self.refresh_sjq_button.clicked.connect(self.refresh_sjq_clicked) self.refresh_sjr_button.clicked.connect(self.refresh_sjr_clicked) self.sjr_image_button.clicked.connect(self.sjr_image_switch) self.canvas.callbacks.connect('button_press_event', self.on_click) def save_board_label(self, path): import cv2 cv2.imwrite(os.path.join(path, 'board.png'), 255 * self.board_label[:, :, ::-1]) def run_excel_macro(self, path): try: xlApp = win32com.client.Dispatch('Excel.Application') xlsPath = os.path.abspath(os.path.join(path, 'MacroTest.xlsm')) print(xlsPath) macroxl = xlApp.Workbooks.Open(xlsPath) xlApp.Run('MacroTest.xlsm!TEST_1') macroxl.Save() xlApp.Quit() print("Macro ran successfully!") except Exception as e: print("Error found while running the excel macro!") raise def load_image(self, root): """Construct the board image and load it on matplotlib container.""" # Rest the matplotlib container and the buttons print('Starting to load the images...') if not self.first_load: self.remove_board() # Add a matplotlib container self.add_mpl() # load pin images in the zoom-in container and # the scrollable image list self.get_pins(root) self.load_pin_cls(0) self.load_board(root) self.connect_click() def closeEvent(self, event): try: self.resize_timer.cancel() except: pass def load_model(self, root, board_name, save_path): default_settings = { 'sampleID': board_name, 'rawImgPath': root, 'cropSavePath': os.path.join(save_path, "{}", 'pins'), 'savePath': os.path.join(save_path, "{}"), 'additionalCrop': 'None', 'b2p_ratio': '150', 'device': 'gpu0' } dialog = Ui_SettingsWindow(default_settings) dialog.setAttribute(QtCore.Qt.WA_DeleteOnClose) exit_code = dialog.exec_() # print(exit_code) if exit_code: # print(dialog.values) settings_dict = dialog.values userName = settings_dict['userName'] sampleID = settings_dict['sampleID'] analysisType = settings_dict['analysisType'] rawImgPath = settings_dict['rawImgPath'] cropSavePath = settings_dict['cropSavePath'] segDataPath = settings_dict['segDataPath'] savePath = settings_dict['savePath'] guidePath = settings_dict['guidePath'] additionalCrop = settings_dict['additionalCrop'] b2p_ratio = int(settings_dict['b2p_ratio']) device = settings_dict['device'] class TaskThread(QtCore.QThread): def run(self): print("task is running") try: from guides.generalguide import GeneralGuide guide = GeneralGuide(guidePath, b2p_ratio) from model import Model valid_additionalCrop = None if additionalCrop == 'None' else ( int(additionalCrop), int(additionalCrop)) f_model = Model(sampleID, analysisType, guide, valid_additionalCrop, rawImgPath, segDataPath, cropSavePath, savePath, device) from time import time st = time() print('Starting the analysis...') f_model(saveResults=True) sp = time() - st print('Analysis took {} seconds'.format(sp)) except Exception as e: print(e) task_thread = TaskThread() from progress_bar import ConstantProgressBar progress_dialog = ConstantProgressBar(task_thread) exit_code = progress_dialog.exec_() print(exit_code) json.dump(settings_dict, open(os.path.join(savePath, 'info.txt'), 'w'), indent=4) self.refresh_menus() self.load_image(savePath)
class MainWidget(QWidget): def __init__(self): super(MainWidget, self).__init__() self.initUI() def initUI(self): self.resize(1100, 650) self.setWindowTitle("Stok Viewer") vbox = QVBoxLayout() self.setLayout(vbox) dailyBtn = QPushButton("Daily", self) weeklyBtn = QPushButton("Weeky", self) monthlyBtn = QPushButton("Monthly", self) dailyBtn.clicked.connect(lambda: self.plot("day")) weeklyBtn.clicked.connect(lambda: self.plot("week")) monthlyBtn.clicked.connect(lambda: self.plot("month")) timelineBtnBox = QHBoxLayout() timelineBtnBox.addStretch(1) timelineBtnBox.addWidget(dailyBtn) timelineBtnBox.addWidget(weeklyBtn) timelineBtnBox.addWidget(monthlyBtn) # widgets and layouts for plotting self.ybuffer = 5.5 # buffer area for bottom and top(%) self.zoomSpeed = 5 # How much area to zoom for one scroll self.figure = Figure() self.canvas = Canvas(self.figure) self.canvas.mpl_connect("button_press_event", self.onPressEvent) self.canvas.mpl_connect("button_release_event", self.onReleaseEvent) self.canvas.mpl_connect("scroll_event", self.onScrollEvent) self.chartBox = QHBoxLayout() self.chartBox.addWidget(self.canvas) vbox.addLayout(timelineBtnBox) vbox.addLayout(self.chartBox) self.plot("day") self.show() def plot(self, timeline=""): self.figure.clf() self.df = DataFetcher.getStockByTimeline("006400.KS", timeline) self.ax = self.figure.add_subplot() rsiList = getRsi(self.df) self.canvas.draw_idle() custplot(self.ax, self.df) ### 중복되는 기능, delete it later self.lineEndList = [] self.lineStartList = [] for obj in self.ax.get_children(): if type(obj) is type(matplotlib.collections.LineCollection([])): self.lineStartList.append( obj.get_paths()[0].get_extents().get_points()[0][1]) self.lineEndList.append( obj.get_paths()[0].get_extents().get_points()[1][1]) ### self.autoscale() def onPressEvent(self, event): # check if the pressed button was "LEFT" if not event.button == 1: return self.lineEndList = [] self.lineStartList = [] for obj in self.ax.get_children(): if type(obj) is type(matplotlib.collections.LineCollection([])): self.lineStartList.append( obj.get_paths()[0].get_extents().get_points()[0][1]) self.lineEndList.append( obj.get_paths()[0].get_extents().get_points()[1][1]) for obj in self.ax.get_children(): if type(obj) is type(matplotlib.patches.Rectangle): break #print(obj) self.xStart = self.ax.get_xlim()[0] self.xCursor = event.x self.cidDrag = self.canvas.mpl_connect("motion_notify_event", self.onDragEvent) def onDragEvent(self, event): xlim = self.ax.get_xlim() # make drag limits to prevent overflow ### # IMPLMENT PENDING ### movement = (event.x - self.xCursor) / 10 self.xCursor = event.x self.ax.set_xlim(xlim[0] - movement, xlim[1] - movement) self.autoscale() self.canvas.draw_idle() def onReleaseEvent(self, event): self.canvas.mpl_disconnect(self.cidDrag) # method for getting new range of axes and updating them to the df def updateAxes(self): # calculate how much x axis was moved xlim = self.ax.get_xlim() xEnd = -xlim[0] # get new df ogdate = self.df["Date"].iloc[0] newdf = DataFetcher.getStockByDate("006400.KS", ogdate, xEnd) newax = custplot(self.ax, newdf, timeline="day") # update the new df to the ax background = self.canvas.copy_from_bbox(self.ax.bbox) self.ax.draw_artist(newax) self.canvas.blit(self.ax.bbox) def onScrollEvent(self, event): xlim = self.ax.get_xlim() width = xlim[1] - xlim[0] if event.button == "up" or event.button == "down": if event.button == "up": xmin = xlim[0] + (width / 100 * self.zoomSpeed) xmax = xlim[1] - (width / 100 * self.zoomSpeed) else: xmin = xlim[0] - (width / 100 * self.zoomSpeed) xmax = xlim[1] + (width / 100 * self.zoomSpeed) self.ax.set_xlim(xmin, xmax) self.autoscale() self.canvas.draw_idle() else: print("error: unrecognized scroll movement") # method for autoscaling y axis heights according to y limits def autoscale(self): xlim = self.ax.get_xlim() ylim = self.ax.get_ylim() # check if updating lines are out of bound start = int(xlim[0]) if int(xlim[0]) > 0 else 0 end = int(xlim[1]) if int(xlim[1]) < len(self.df["High"]) else ( len(self.df["High"]) - 1) # get the highest and lowest point of y from start to end highest = max(self.lineEndList[start:end + 1]) lowest = min(self.lineStartList[start:end + 1]) height = highest - lowest self.ax.set_ylim(lowest - (height / 100 * self.ybuffer), highest + (height / 100 * self.ybuffer))
class MatPlotLibBase(QWidget): def __init__(self, parent, file_dialog_service, h_margin=(0.8, 0.1), v_margin=(0.5, 0.15), h_axes=[Size.Scaled(1.0)], v_axes=[Size.Scaled(1.0)], nx_default=1, ny_default=1): QWidget.__init__(self, parent) self._file_dialog_service = file_dialog_service self._figure = Figure() self._canvas = FigureCanvas(self._figure) h = [Size.Fixed(h_margin[0]), *h_axes, Size.Fixed(h_margin[1])] v = [Size.Fixed(v_margin[0]), *v_axes, Size.Fixed(v_margin[1])] self._divider = Divider(self._figure, (0.0, 0.0, 1.0, 1.0), h, v, aspect=False) self._axes = LocatableAxes(self._figure, self._divider.get_position()) self._axes.set_axes_locator( self._divider.new_locator(nx=nx_default, ny=ny_default)) self._axes.set_zorder(2) self._axes.patch.set_visible(False) for spine in ['top', 'right']: self._axes.spines[spine].set_visible(False) self._figure.add_axes(self._axes) self._canvas.setParent(self) self._layout = QVBoxLayout(self) self._layout.setContentsMargins(0, 0, 0, 0) self._layout.addWidget(self._canvas) self.setLayout(self._layout) self._figure.canvas.mpl_connect('scroll_event', self._on_scroll) self._xy_extents = None self._background_cache = None self._decoration_artists = [] self._is_panning = False self._zoom_selector = _RectangleSelector(self._axes, self._zoom_selected) self._zoom_selector.set_active(False) self._x_extent_padding = 0.01 self._y_extent_padding = 0.01 self._axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) self._axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) self._active_tools = {} self._span = _SpanSeletor(self._axes, self._handle_span_select, 'horizontal', rectprops=dict(alpha=0.2, facecolor='red', edgecolor='k'), span_stays=True) self._span.set_on_select_none(self._handle_span_select_none) self.span = self._previous_span = None self._span_center_mouse_event = None self._span_left_mouse_event = None self._span_right_mouse_event = None self._figure.canvas.mpl_connect('button_press_event', self._handle_press) self._figure.canvas.mpl_connect('motion_notify_event', self._handle_move) self._figure.canvas.mpl_connect('button_release_event', self._handle_release) self._figure.canvas.mpl_connect('resize_event', self._handle_resize) self.activateTool(ToolType.span, self.isActiveDefault(ToolType.span)) self._pan_event = None self._pending_draw = None self._pending_artists_draw = None self._other_draw_events = [] self._draw_timer = QTimer(self) self._draw_timer.timeout.connect(self._do_draw_events) self._draw_timer.start(20) self._zoom_skew = None self._menu = QMenu(self) self._copy_image_action = QAction(self.tr('Copy To Clipboard'), self) self._copy_image_action.triggered.connect(self.copyToClipboard) self._copy_image_action.setShortcuts(QKeySequence.Copy) self._save_image_action = QAction(self.tr('Save As Image'), self) self._save_image_action.triggered.connect(self.saveAsImage) self._show_table_action = QAction(self.tr('Show Table'), self) self._show_table_action.triggered.connect(self.showTable) self._menu.addAction(self._copy_image_action) self._menu.addAction(self._save_image_action) self._menu.addAction(self._show_table_action) self.addAction(self._copy_image_action) self._table_view = None self._single_axis_zoom_enabled = True self._cached_label_width_height = None if hasattr(type(self), 'dataChanged'): self.dataChanged.connect(self._on_data_changed) self._options_view = None self._secondary_axes = self._secondary_y_extent = self._secondary_x_extent = None self._legend = None self._draggable_legend = None self._setting_axis_limits = False self.hasHiddenSeries = False enabledToolsChanged = pyqtSignal() spanChanged = pyqtSignal(SpanModel) hasHiddenSeriesChanged = pyqtSignal(bool) span = AutoProperty(SpanModel) hasHiddenSeries = AutoProperty(bool) def setOptionsView(self, options_view): self._options_view = options_view self._options_view.setSecondaryYLimitsEnabled( self._secondary_y_enabled()) self._options_view.setSecondaryXLimitsEnabled( self._secondary_x_enabled()) self._options_view.showGridLinesChanged.connect( self._update_grid_lines) self._options_view.xAxisLowerLimitChanged.connect( self._handle_options_view_limit_changed(x_min_changed=True)) self._options_view.xAxisUpperLimitChanged.connect( self._handle_options_view_limit_changed(x_max_changed=True)) self._options_view.yAxisLowerLimitChanged.connect( self._handle_options_view_limit_changed(y_min_changed=True)) self._options_view.yAxisUpperLimitChanged.connect( self._handle_options_view_limit_changed(y_max_changed=True)) self._options_view.xAxisLimitsChanged.connect( self._handle_options_view_limit_changed(x_min_changed=True, x_max_changed=True)) self._options_view.yAxisLimitsChanged.connect( self._handle_options_view_limit_changed(y_min_changed=True, y_max_changed=True)) self._options_view.secondaryXAxisLowerLimitChanged.connect( self._handle_options_view_secondary_limit_changed( x_min_changed=True)) self._options_view.secondaryXAxisUpperLimitChanged.connect( self._handle_options_view_secondary_limit_changed( x_max_changed=True)) self._options_view.secondaryYAxisLowerLimitChanged.connect( self._handle_options_view_secondary_limit_changed( y_min_changed=True)) self._options_view.secondaryYAxisUpperLimitChanged.connect( self._handle_options_view_secondary_limit_changed( y_max_changed=True)) self._options_view.secondaryXAxisLimitsChanged.connect( self._handle_options_view_secondary_limit_changed( x_min_changed=True, x_max_changed=True)) self._options_view.secondaryYAxisLimitsChanged.connect( self._handle_options_view_secondary_limit_changed( y_min_changed=True, y_max_changed=True)) def setLegendControl(self, legend_control): self._legend_control = legend_control self._legend_control.seriesUpdated.connect(self._legend_series_updated) self._legend_control.showLegendChanged.connect(self._show_legend) self._legend_control.seriesNameChanged.connect( self._handle_series_name_changed) self._legend_control.showSeriesChanged.connect( self._handle_show_series_changed) bind(self._legend_control, self, 'hasHiddenSeries', two_way=False) def _legend_series_updated(self): if self._legend is not None: self._show_legend(self._legend_control.showLegend) def _show_legend(self, show): if self._legend and not show: self._legend.remove() self._legend = None self.draw() elif show: if self._legend: self._legend.remove() show_series = self._legend_control.showSeries handles = [ h for h, s in zip(self._legend_control.seriesHandles, show_series) if s ] names = [ n for n, s in zip(self._legend_control.seriesNames, show_series) if s ] axes = (self._secondary_axes if self._secondary_axes and self._secondary_axes.get_visible() and self._secondary_axes.get_zorder() > self._axes.get_zorder() else self._axes) self._legend = self._create_legend( axes, handles, names, markerscale=self._get_legend_markerscale()) if self._get_legend_text_color() is not None: for text in self._legend.texts: text.set_color(self._get_legend_text_color()) self._draggable_legend = DraggableLegend(self._legend) self.draw() def _get_legend_markerscale(self): return 5 def _create_legend(self, axes, handles, names, **kwargs): return axes.legend(handles, names, **kwargs) def _get_legend_text_color(self): return None def _handle_series_name_changed(self, index, series_name): if self._legend is not None and index < len( self._legend_control.seriesHandles): visible_handles = [ h for h, s in zip(self._legend_control.seriesHandles, self._legend_control.showSeries) if s and h is not None ] try: legend_index = visible_handles.index( self._legend_control.seriesHandles[index]) except ValueError: return if legend_index < len(self._legend.texts): self._legend.texts[legend_index].set_text(series_name) self.draw() def _handle_show_series_changed(self, index, show_series): if index < len(self._legend_control.seriesHandles): self._set_series_visibility( self._legend_control.seriesHandles[index], show_series) if self._legend is not None: self._show_legend(self._legend_control.showLegend) else: self.draw() def _set_series_visibility(self, handle, visible): if not handle: return if hasattr(handle, 'set_visible'): handle.set_visible(visible) elif hasattr(handle, 'get_children'): for child in handle.get_children(): self._set_series_visibility(child, visible) def _update_grid_lines(self): show_grid_lines = False if self._options_view is None else self._options_view.showGridLines gridline_color = self._axes.spines['bottom'].get_edgecolor() gridline_color = gridline_color[0], gridline_color[1], gridline_color[ 2], 0.5 kwargs = dict(color=gridline_color, alpha=0.5) if show_grid_lines else {} self._axes.grid(show_grid_lines, **kwargs) self.draw() def _handle_options_view_limit_changed(self, x_min_changed=False, x_max_changed=False, y_min_changed=False, y_max_changed=False): def _(): if self._options_view is None or self._setting_axis_limits: return (x_min, x_max), (y_min, y_max) = (new_x_min, new_x_max), ( new_y_min, new_y_max) = self._get_xy_extents() (x_opt_min, x_opt_max), (y_opt_min, y_opt_max) = self._get_options_view_xy_extents() if x_min_changed: new_x_min = x_opt_min if x_max_changed: new_x_max = x_opt_max if y_min_changed: new_y_min = y_opt_min if y_max_changed: new_y_max = y_opt_max if [new_x_min, new_x_max, new_y_min, new_y_max ] != [x_min, x_max, y_min, y_max]: self._xy_extents = (new_x_min, new_x_max), (new_y_min, new_y_max) self._set_axes_limits() self.draw() return _ def _get_options_view_xy_extents(self): (x_data_min, x_data_max), (y_data_min, y_data_max) = self._get_data_xy_extents() x_min = x_data_min if np.isnan( self._options_view.xAxisLowerLimit ) else self._options_view.xAxisLowerLimit x_max = x_data_max if np.isnan( self._options_view.xAxisUpperLimit ) else self._options_view.xAxisUpperLimit y_min = y_data_min if np.isnan( self._options_view.yAxisLowerLimit ) else self._options_view.yAxisLowerLimit y_max = y_data_max if np.isnan( self._options_view.yAxisUpperLimit ) else self._options_view.yAxisUpperLimit return (x_min, x_max), (y_min, y_max) def _handle_options_view_secondary_limit_changed(self, x_min_changed=False, x_max_changed=False, y_min_changed=False, y_max_changed=False): def _(): if self._options_view is None or self._setting_axis_limits: return updated = False (x_opt_min, x_opt_max), ( y_opt_min, y_opt_max) = self._get_options_view_secondary_xy_extents() if self._has_secondary_y_extent() and (y_min_changed or y_max_changed): y_min, y_max = new_y_min, new_y_max = self._get_secondary_y_extent( ) if y_min_changed: new_y_min = y_opt_min if y_max_changed: new_y_max = y_opt_max if [new_y_min, new_y_max] != [y_min, y_max]: self._secondary_y_extent = (new_y_min, new_y_max) updated = True if self._has_secondary_x_extent() and (x_min_changed or x_max_changed): x_min, x_max = new_x_min, new_x_max = self._get_secondary_x_extent( ) if x_min_changed: new_x_min = x_opt_min if x_max_changed: new_x_max = x_opt_max if [new_x_min, new_x_max] != [x_min, x_max]: self._secondary_x_extent = (new_x_min, new_x_max) updated = True if updated: self._set_axes_limits() self.draw() return _ def _get_options_view_secondary_xy_extents(self): x_data_min, x_data_max = self._get_data_secondary_x_extent() y_data_min, y_data_max = self._get_data_secondary_y_extent() x_min = x_data_min if np.isnan( self._options_view.secondaryXAxisLowerLimit ) else self._options_view.secondaryXAxisLowerLimit x_max = x_data_max if np.isnan( self._options_view.secondaryXAxisUpperLimit ) else self._options_view.secondaryXAxisUpperLimit y_min = y_data_min if np.isnan( self._options_view.secondaryYAxisLowerLimit ) else self._options_view.secondaryYAxisLowerLimit y_max = y_data_max if np.isnan( self._options_view.secondaryYAxisUpperLimit ) else self._options_view.secondaryYAxisUpperLimit return (x_min, x_max), (y_min, y_max) def _on_data_changed(self): self._cached_label_width_height = None def closeEvent(self, event): QWidget.closeEvent(self, event) if event.isAccepted(): self._zoom_selector.onselect = self._span.onselect = self._span._select_none_handler = None def set_divider_h_margin(self, h_margin): h = [ Size.Fixed(h_margin[0]), Size.Scaled(1.0), Size.Fixed(h_margin[1]) ] self._divider.set_horizontal(h) def set_divider_v_margin(self, v_margin): v = [ Size.Fixed(v_margin[0]), Size.Scaled(1.0), Size.Fixed(v_margin[1]) ] self._divider.set_vertical(v) @property def x_extent_padding(self): return self._x_extent_padding @x_extent_padding.setter def x_extent_padding(self, value): self._x_extent_padding = value @property def y_extent_padding(self): return self._y_extent_padding @y_extent_padding.setter def y_extent_padding(self, value): self._y_extent_padding = value def _in_interval(self, value, interval): return interval[0] <= value <= interval[1] def _interval_skew(self, value, interval): return (value - interval[0]) / (interval[1] - interval[0]) def _in_x_scroll_zone(self, event): return self._in_interval(event.x, self._axes.bbox.intervalx ) and event.y <= self._axes.bbox.intervaly[1] def _in_y_scroll_zone(self, event): return self._in_interval(event.y, self._axes.bbox.intervaly ) and event.x <= self._axes.bbox.intervalx[1] def _on_scroll(self, event): if self._secondary_axes is not None: self._handle_scroll_secondary(event) in_x = self._in_x_scroll_zone(event) in_y = self._in_y_scroll_zone(event) if in_x or in_y and event.button in ['up', 'down']: (x_min, x_max), (y_min, y_max) = self._get_actual_xy_extents() if (in_x and self._single_axis_zoom_enabled) or (in_x and in_y): skew = self._zoom_skew and self._zoom_skew[0] skew = self._interval_skew( event.x, self._axes.bbox.intervalx) if skew is None else skew x_min, x_max = self._zoom(x_min, x_max, skew, event.button) if (in_y and self._single_axis_zoom_enabled) or (in_x and in_y): skew = self._zoom_skew and self._zoom_skew[1] skew = self._interval_skew( event.y, self._axes.bbox.intervaly) if skew is None else skew y_min, y_max = self._zoom(y_min, y_max, skew, event.button) self._xy_extents = (x_min, x_max), (y_min, y_max) self._set_axes_limits() self.draw() def _in_secondary_y_scroll_zone(self, event): return self._in_interval(event.y, self._axes.bbox.intervaly) and \ event.x >= self._axes.bbox.intervalx[1] def _in_secondary_x_scroll_zone(self, event): return self._in_interval(event.x, self._axes.bbox.intervalx) and \ event.y >= self._axes.bbox.intervaly[1] def _handle_scroll_secondary(self, event): if self._has_secondary_y_extent(): in_secondary_y = self._in_secondary_y_scroll_zone(event) if in_secondary_y and event.button in ['up', 'down']: self._secondary_y_extent = self._zoom( *self._get_secondary_y_extent(), self._interval_skew(event.y, self._axes.bbox.intervaly), event.button) if self._has_secondary_x_extent(): in_secondary_x = self._in_secondary_x_scroll_zone(event) if in_secondary_x and event.button in ['up', 'down']: self._secondary_x_extent = self._zoom( *self._get_secondary_x_extent(), self._interval_skew(event.x, self._axes.bbox.intervalx), event.button) def _get_zoom_multiplier(self): return 20 / 19 def _zoom(self, min_, max_, skew, direction): zoom_multiplier = self._get_zoom_multiplier( ) if direction == 'up' else 1 / self._get_zoom_multiplier() range_ = max_ - min_ diff = (range_ * (1 / zoom_multiplier)) - range_ max_ += diff * (1 - skew) min_ -= diff * skew return min_, max_ def _set_axes_limits(self): try: self._setting_axis_limits = True if self._secondary_axes is not None: self._set_secondary_axes_limits() self._update_ticks() (x_min, x_max), (y_min, y_max) = self._get_xy_extents() if self._options_view is not None: if self._options_view.x_limits: self._options_view.setXLimits(float(x_min), float(x_max)) if self._options_view.y_limits: self._options_view.setYLimits(float(y_min), float(y_max)) self._axes.set_xlim(*_safe_limits(x_min, x_max)) self._axes.set_ylim(*_safe_limits(y_min, y_max)) finally: self._setting_axis_limits = False def _set_secondary_axes_limits(self): if self._options_view is not None: if self._options_view.secondary_y_limits: enabled = self._secondary_y_enabled() secondary_y_min, secondary_y_max = self._get_secondary_y_extent( ) if enabled else (float('nan'), float('nan')) self._options_view.setSecondaryYLimitsEnabled(enabled) self._options_view.setSecondaryYLimits(float(secondary_y_min), float(secondary_y_max)) if self._options_view.secondary_x_limits: enabled = self._secondary_x_enabled() secondary_x_min, secondary_x_max = self._get_secondary_x_extent( ) if enabled else (float('nan'), float('nan')) self._options_view.setSecondaryXLimitsEnabled(enabled) self._options_view.setSecondaryXLimits(float(secondary_x_min), float(secondary_x_max)) if self._has_secondary_y_extent(): self._secondary_axes.set_ylim(*_safe_limits( *self._get_secondary_y_extent())) if self._has_secondary_x_extent(): self._secondary_axes.set_xlim(*_safe_limits( *self._get_secondary_x_extent())) def _secondary_y_enabled(self): return True if self._secondary_axes and self._secondary_axes.get_visible( ) and self._has_secondary_y_extent() else False def _secondary_x_enabled(self): return True if self._secondary_axes and self._secondary_axes.get_visible( ) and self._has_secondary_x_extent() else False def _set_axes_labels(self): self._axes.set_xlabel(self.data.xAxisTitle) self._axes.set_ylabel(self.data.yAxisTitle) def _set_center(self, center): if not all(c is not None for c in center): center = (0, 0) x_extent, y_extent = self._get_xy_extents() span = x_extent[1] - x_extent[0], y_extent[1] - y_extent[0] x_extent = center[0] - span[0] / 2, center[0] + span[0] / 2 y_extent = center[1] - span[1] / 2, center[1] + span[1] / 2 self._xy_extents = x_extent, y_extent def _get_xy_extents(self): if self.data is None: return (0, 0), (0, 0) if self._xy_extents is None: return self._get_data_xy_extents() return self._xy_extents def _get_data_xy_extents(self): if self.data is None: return (0, 0), (0, 0) (x_min, x_max), (y_min, y_max) = self.data.get_xy_extents() return self._pad_extent(x_min, x_max, self.x_extent_padding), self._pad_extent( y_min, y_max, self.y_extent_padding) def _has_secondary_y_extent(self): return hasattr(self.data, 'get_secondary_y_extent') def _get_secondary_y_extent(self): if self._secondary_y_extent is not None: return self._secondary_y_extent if self.data is not None: return self._get_data_secondary_y_extent() return (0, 0) def _get_data_secondary_y_extent(self): if self.data is None: return (0, 0) return self._pad_extent(*self.data.get_secondary_y_extent(), self.y_extent_padding) def _has_secondary_x_extent(self): return hasattr(self.data, 'get_secondary_x_extent') def _get_secondary_x_extent(self): if self._secondary_x_extent is not None: return self._secondary_x_extent if self.data is not None: return self._get_data_secondary_x_extent() return (0, 0) def _get_data_secondary_x_extent(self): if self.data is None or not hasattr(self.data, 'get_secondary_x_extent'): return (0, 0) return self._pad_extent(*self.data.get_secondary_x_extent(), self.x_extent_padding) def _get_actual_xy_extents(self): return self._axes.get_xlim(), self._axes.get_ylim() def _pad_extent(self, min_, max_, padding): min_, max_ = self._zero_if_nan(min_), self._zero_if_nan(max_) range_ = max_ - min_ return min_ - padding * range_, max_ + padding * range_ def _zoom_selected(self, start_pos, end_pos): x_min, x_max = min(start_pos.xdata, end_pos.xdata), max(start_pos.xdata, end_pos.xdata) y_min, y_max = min(start_pos.ydata, end_pos.ydata), max(start_pos.ydata, end_pos.ydata) self._xy_extents = (x_min, x_max), (y_min, y_max) self._set_axes_limits() self.draw() def _handle_span_select(self, x_min, x_max): x_min, x_max = self._round_to_bin_width(x_min, x_max) self._update_span_rect(x_min, x_max) self.span = SpanModel(self, x_min, x_max) self.draw() def _handle_span_select_none(self): self.span = None def _handle_press(self, event): if event.button == 1: if self._is_panning: self._pan_event = event elif self._span.active: self._handle_span_press(event) def _handle_move(self, event): if event.xdata and self._pan_event: self._handle_pan_move(event) elif event.xdata and any(self._span_events()): self._handle_span_move(event) def _handle_release(self, event): if self._pan_event: self._pan_event = None elif any(self._span_events()): self._handle_span_release(event) def _handle_pan_move(self, event): from_x, from_y = self._axes.transData.inverted().transform( (self._pan_event.x, self._pan_event.y)) to_x, to_y = self._axes.transData.inverted().transform( (event.x, event.y)) self._pan(from_x - to_x, from_y - to_y) self._pan_event = event def _pan(self, delta_x, delta_y): (x_min, x_max), (y_min, y_max) = self._get_xy_extents() self._xy_extents = (x_min + delta_x, x_max + delta_x), (y_min + delta_y, y_max + delta_y) self._set_axes_limits() self.draw() def _span_events(self): return self._span_center_mouse_event, self._span_left_mouse_event, self._span_right_mouse_event def _handle_span_press(self, event): if not event.xdata: return span_min, span_max = (self.span.left, self.span.right) if self.span else (0, 0) edge_tolerance = self._span_tolerance() if abs(span_min - event.xdata) < edge_tolerance: self._span.active = False self._span_left_mouse_event = event elif abs(span_max - event.xdata) < edge_tolerance: self._span.active = False self._span_right_mouse_event = event elif span_min < event.xdata < span_max: self._span.active = False self._span_center_mouse_event = event def _handle_span_move(self, event): if not self.span: return x_min, x_max = self.span.left, self.span.right last_event = next(x for x in self._span_events() if x) diff_x = event.xdata - last_event.xdata if self._span_center_mouse_event is not None: self._update_span_rect(x_min + diff_x) elif self._span_left_mouse_event is not None: self._update_span_rect(x_min + diff_x, x_max) elif self._span_right_mouse_event is not None: self._update_span_rect(x_min, x_max + diff_x) self.draw([self._span.rect]) def _handle_span_release(self, _event): x_min = self._span.rect.get_x() x_max = x_min + self._span.rect.get_width() x_min, x_max = self._round_to_bin_width(x_min, x_max) self._update_span_rect(x_min, x_max) self.span = SpanModel(self, x_min, x_max) self.draw() self._span.active = True self._span_center_mouse_event = self._span_left_mouse_event = self._span_right_mouse_event = None def _update_span_rect(self, x_min, x_max=None): self._span.rect.set_x(x_min) self._span.stay_rect.set_x(x_min) if x_max: self._span.rect.set_width(x_max - x_min) self._span.stay_rect.set_width(x_max - x_min) def _round_to_bin_width(self, x_min, x_max): return x_min, x_max def _span_tolerance(self): return 5 def toolEnabled(self, _tool_type): return False def toolAvailable(self, _tool_type): return False def activateTool(self, tool_type, active): if tool_type == ToolType.zoom: self._zoom_selector.set_active(active) elif tool_type == ToolType.span: if self._span.active and not active: self._previous_span = self.span self.span = None for r in [self._span.rect, self._span.stay_rect]: self._remove_artist(r) elif not self._span.active and active: self.span = self._previous_span for r in [self._span.rect, self._span.stay_rect]: self._add_artist(r) self._span.active = active self.draw() elif tool_type == ToolType.pan: self._is_panning = active self._active_tools[tool_type] = active def toolActive(self, tool_type): return self._active_tools.get(tool_type, False) def isActiveDefault(self, _tool_type): return False def _add_artist(self, artist): self._axes.add_artist(artist) self._decoration_artists.append(artist) def _remove_artist(self, artist): artist.remove() if artist in self._decoration_artists: self._decoration_artists.remove(artist) def _handle_resize(self, _event): self._update_ticks() return self.draw() def draw(self, artists=None): if artists is None: def _update(): for a in self._decoration_artists: a.remove() self._canvas.draw() self._background_cache = self._canvas.copy_from_bbox( self._figure.bbox) for a in self._decoration_artists: self._axes.add_artist(a) self._axes.draw_artist(a) self._canvas.update() self._pending_draw = _update else: def _update(): if self._background_cache is None: raise RuntimeError('Must run draw before drawing artists!') self._canvas.restore_region(self._background_cache) for a in artists: self._axes.draw_artist(a) self._canvas.update() self._pending_artists_draw = _update def _do_draw_events(self): if self._pending_draw is not None: self._pending_draw() self._pending_draw = None if self._pending_artists_draw is not None: self._pending_artists_draw() self._pending_artists_draw = None if self._other_draw_events: for draw_event in self._other_draw_events: draw_event() self._other_draw_events = [] def addDrawEvent(self, draw_event): self._other_draw_events.append(draw_event) def resetZoom(self): self._secondary_y_extent = self._secondary_x_extent = None self._xy_extents = None self._set_axes_limits() self.draw() def _twinx(self, ylabel): axes = self._axes.twinx() for spine in ['top', 'left']: axes.spines[spine].set_visible(False) axes.set_ylabel(ylabel) axes.set_zorder(1) return axes @property def axes(self): return self._axes @property def secondary_axes(self): if self._secondary_axes is None: self._set_secondary_axes(self._twinx('')) return self._secondary_axes def _set_secondary_axes(self, axes): self._secondary_axes = axes @staticmethod def sizeHint(): """function::sizeHint() Override the default sizeHint to ensure the plot has an initial size """ return QSize(600, 400) def minimumSizeHint(self): """function::sizeHint() Override the default sizeHint to ensure the plot does not shrink below minimum size """ return self.sizeHint() @staticmethod def _zero_if_nan(value): return value if not isinstance(value, float) or not np.isnan(value) else 0 def canShowTable(self): return hasattr(self, 'data') and self.data is not None and hasattr( self.data, 'table') def contextMenuEvent(self, event): self._show_table_action.setEnabled(self.canShowTable()) self._menu.exec_(event.globalPos()) def copyToClipboard(self): with BytesIO() as buffer: self._figure.savefig(buffer, facecolor=self._figure.get_facecolor()) QApplication.clipboard().setImage( QImage.fromData(buffer.getvalue())) def saveAsImage(self): filename = self._file_dialog_service.get_save_filename( self, self.tr('Portable Network Graphics (*.png)')) if filename: self._figure.savefig(filename, facecolor=self._figure.get_facecolor()) def showTable(self): if self.canShowTable(): self._table_view = TableView(None) self._table_view.pasteEnabled = False self._table_view.setModel(self.data.table) self._table_view.setMinimumSize(800, 600) self._table_view.show() def _update_ticks(self): if not self.data: return if hasattr(self.data, 'x_labels'): step = self.data.x_tick_interval if hasattr( self.data, 'x_tick_interval') else None x_ticks, x_labels = self._get_labels(self.data.x_labels, step, horizontal=True) self._axes.set_xticks(x_ticks) self._axes.set_xticklabels(x_labels) if hasattr(self.data, 'y_labels'): step = self.data.y_tick_interval if hasattr( self.data, 'y_tick_interval') else None y_ticks, y_labels = self._get_labels(self.data.y_labels, step, horizontal=False) self._axes.set_yticks(y_ticks) self._axes.set_yticklabels(y_labels) def _get_labels(self, labels, step, horizontal=True): (x0, x1), (y0, y1) = self._get_xy_extents() start, end = (int(x0), int(x1)) if horizontal else (int(y0), int(y1)) visible_points = end - start if not (step and step > 0): width, height = self._get_label_width_height(labels) axes_bbox = self._axes.get_window_extent( self._figure.canvas.get_renderer()).transformed( self._figure.dpi_scale_trans.inverted()) plot_size = (axes_bbox.width if horizontal else axes_bbox.height) * self._figure.dpi size = (width if horizontal else height) if plot_size == 0 or size == 0: n_labels = 16 else: n_labels = int(plot_size / size) if n_labels == 0: n_labels = 16 step = int(visible_points / n_labels) + 1 else: step = int(step) indexes = list(range(len(labels))) display_labels = list(labels) for i in indexes: if i % step: display_labels[i] = '' return indexes, display_labels def _get_label_width_height(self, labels): if not self._cached_label_width_height: font = MatPlotLibFont.default() width = 0 height = 0 for label in labels: next_width, next_height = font.get_size( str(label), matplotlib.rcParams['font.size'], self._figure.dpi) width = max(width, next_width) height = max(height, next_height) self._cached_label_width_height = width, height return self._cached_label_width_height def _create_new_axes(self, nx=1, ny=1) -> LocatableAxes: axes = LocatableAxes(self._figure, self._divider.get_position()) axes.set_axes_locator(self._divider.new_locator(nx=nx, ny=ny)) self._figure.add_axes(axes) return axes @staticmethod def _create_secondary_xy_axes(figure, divider, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position()) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) axes.xaxis.tick_top() axes.xaxis.set_label_position('top') axes.yaxis.tick_right() axes.yaxis.set_label_position('right') axes.patch.set_visible(visible) axes.set_zorder(z_order) figure.add_axes(axes) axes.ticklabel_format(style='sci', axis='x', scilimits=(-4, 4)) axes.ticklabel_format(style='sci', axis='y', scilimits=(-4, 4)) return axes @staticmethod def _create_shared_axes(figure, divider, shared_axes, nx=1, ny=1, visible=False, z_order=1): axes = LocatableAxes(figure, divider.get_position(), sharex=shared_axes, sharey=shared_axes, frameon=False) axes.set_axes_locator(divider.new_locator(nx=nx, ny=ny)) for spine in axes.spines.values(): spine.set_visible(False) for axis in axes.axis.values(): axis.set_visible(False) axes.patch.set_visible(False) axes.set_visible(False) axes.set_zorder(z_order) figure.add_axes(axes) return axes
class twoDimage(QWidget): def __init__(self, viewer): super(twoDimage, self).__init__() self.data = Spectrum() self.viewer = viewer self.aspect = "auto" #init Image self.fig = Figure(figsize=(5, 3), dpi=150) gs = GridSpec(3, 3, figure=self.fig) self.ax_XDC = self.fig.add_subplot(gs[0, 0:2]) # X Distribution Curve self.ax_Spec = self.fig.add_subplot(gs[1:, 0:2]) # Spectrum self.ax_YDC = self.fig.add_subplot(gs[1:, -1]) # Y Distribution Curve self.lx1 = self.ax_Spec.axhline(self.ax_Spec.get_ybound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) # the horiz line 1 self.ly1 = self.ax_Spec.axvline(self.ax_Spec.get_xbound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) # the vert line 1 self.lx2 = self.ax_Spec.axhline(self.ax_Spec.get_ybound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) # the horiz line 2 self.ly2 = self.ax_Spec.axvline(self.ax_Spec.get_xbound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) # the vert line 2 self.vl = self.ax_XDC.axvline(self.ax_XDC.get_xbound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) self.hl = self.ax_YDC.axhline(self.ax_YDC.get_ybound()[0], visible=False, animated=True, color='k', linestyle='--', linewidth=0.75) # the horiz line self.ax_cmap = self.fig.add_subplot(gs[0, 2]) # color bar self.gradient = np.vstack((np.linspace(0, 1, 256), np.linspace(0, 1, 256))) self.setTickLabelFont(self.ax_Spec, "Comic Sans MS") self.setTickLabelFont(self.ax_XDC, "Comic Sans MS") self.setTickLabelFont(self.ax_YDC, "Comic Sans MS") self.ax_Spec.set_axisbelow(False) self.ax_Spec.tick_params(direction='inout', labelsize=8.5) self.ax_XDC.tick_params(axis='x', labelsize=8.5, labelbottom=False) self.ax_XDC.tick_params(axis='y', which='both', left=False, labelleft=False) self.ax_YDC.tick_params(axis='x', which='both', bottom=False, labelbottom=False) self.ax_YDC.tick_params(axis='y', labelsize=8.5, labelleft=False) self.fig.subplots_adjust(top=0.96, bottom=0.08, left=0.1, right=0.96, wspace=0.15, hspace=0.15) x0, y0, width, height = self.ax_cmap.get_position().bounds nheight = height * 0.2 ny0 = y0 + height * 0.5 * (1 - 0.2) self.ax_cmap.set_position([x0, ny0, width, nheight]) self.ax_cmap.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False) self.ax_cmap.set_title("Color Scale", {'fontsize': 10}, fontname="Comic Sans Ms") self.fig.patch.set_facecolor("None") self.canvas = FigureCanvas(self.fig) self.canvas.setStyleSheet("background-color:transparent;") self.canvas.setFocusPolicy(Qt.ClickFocus) self.XDCLine = Line2D(np.array([0]), np.array([0]), color="blue", linewidth=1, animated=True, solid_joinstyle="round") self.YDCLine = Line2D(np.array([0]), np.array([0]), color="blue", linewidth=1, animated=True, solid_joinstyle="round") self.ax_XDC.add_line(self.XDCLine) self.ax_YDC.add_line(self.YDCLine) self.selector = Selector(self.ax_Spec, self.canvas) self.cid_press = self.canvas.mpl_connect('button_press_event', self.OnPress) self.cid_release = self.canvas.mpl_connect('button_release_event', self.OnRelease) self.cid_keypress = self.canvas.mpl_connect('key_press_event', self.OnKeyPress) #self.cid_scroll = self.canvas.mpl_connect('scroll_event', self.OnExpand) #contextMenu self.contextMenu = QMenu(self) self.CropAction = self.contextMenu.addAction("Crop") self.CropAction.setIcon(QIcon("./image/crop.ico")) self.CropAction.triggered.connect(self.crop) self.RestoreAction = self.contextMenu.addAction("Restore") self.RestoreAction.setIcon(QIcon("./image/restore.ico")) self.RestoreAction.triggered.connect( self.viewer.Win.DataProcessor.tab_single.RestoreData) self.SelectMenu = self.contextMenu.addMenu("Select") self.SelectMenu.setIcon(QIcon("./image/select.ico")) self.SelectX = self.SelectMenu.addAction("X Range") self.SelectX.triggered.connect(self.setSelectionRange) self.SelectY = self.SelectMenu.addAction("Y Range") self.SelectY.triggered.connect(self.setSelectionRange) self.SelectXY = self.SelectMenu.addAction("XY Area") self.SelectXY.triggered.connect(self.setSelectionRange) self.outputMenu = self.contextMenu.addMenu("Output") self.outputMenu.setIcon(QIcon("./image/output.ico")) self.XDC_action = self.outputMenu.addAction("XDC") self.YDC_action = self.outputMenu.addAction("YDC") #init Tool self.toolPanel = QGroupBox("Coordinate System") self.toolPanel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.toolPanel.setCheckable(True) self.toolPanel.setChecked(False) self.toolPanel.setStyle(QStyleFactory.create('Fusion')) self.toolPanel.toggled.connect(self.begincursor) self.X = QDoubleSpinBox() self.X.setFixedWidth(100) self.X.setDecimals(6) self.X.setRange(-1, 1) self.X.setSingleStep(0.01) self.X.setKeyboardTracking(False) self.X.setStyle(QStyleFactory.create('Fusion')) self.X.setValue(-1) self.X.valueChanged.connect(self.setValueFromSpinBox) self.XLabel = QLabel("X:") self.XLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.Y = QDoubleSpinBox() self.Y.setFixedWidth(100) self.Y.setDecimals(6) self.Y.setRange(-1, 1) self.Y.setSingleStep(0.01) self.Y.setKeyboardTracking(False) self.Y.setStyle(QStyleFactory.create('Fusion')) self.Y.setValue(-1) self.Y.valueChanged.connect(self.setValueFromSpinBox) self.YLabel = QLabel("Y:") self.YLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) font = QFont() font.setPointSize(12) font.setBold(True) self.IntLabel = QLabel("Count:") self.IntLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.Int = QLabel("0") self.Int.setFont(font) self.Int.setMinimumWidth(100) self.Int.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.Int.setStyleSheet("QLabel {background-color: white;}") self.Int.setFrameStyle(QFrame.Panel) self.XSlider = QSlider(Qt.Horizontal) self.XSlider.setRange(0, 10000) self.XSlider.setStyle(QStyleFactory.create('Fusion')) self.XSlider.sliderMoved.connect(self.setValueFromSlider) self.XSLabel = QLabel("X:") self.YSlider = QSlider(Qt.Horizontal) self.YSlider.setRange(0, 10000) self.YSlider.setStyle(QStyleFactory.create('Fusion')) self.YSlider.sliderMoved.connect(self.setValueFromSlider) self.YSLabel = QLabel("Y:") self.XSlice = QSpinBox() self.XSlice.setRange(1, 10000) self.XSlice.setSingleStep(2) self.XSlice.setKeyboardTracking(False) self.XSlice.setStyle(QStyleFactory.create('Fusion')) self.XSlice.valueChanged.connect(self.setSlice) self.XSliceLabel = QLabel("Slice:") self.YSlice = QSpinBox() self.YSlice.setRange(1, 10000) self.YSlice.setSingleStep(2) self.YSlice.setKeyboardTracking(False) self.YSlice.setStyle(QStyleFactory.create('Fusion')) self.YSlice.valueChanged.connect(self.setSlice) self.YSliceLabel = QLabel("Slice:") hbox_tool = QHBoxLayout() hbox_tool.addStretch(2) hbox_tool.addWidget(self.XLabel) hbox_tool.addWidget(self.X) hbox_tool.addStretch(1) hbox_tool.addWidget(self.YLabel) hbox_tool.addWidget(self.Y) hbox_tool.addStretch(1) hbox_tool.addWidget(self.IntLabel) hbox_tool.addWidget(self.Int) hbox_tool.addStretch(2) hbox2_tool = QHBoxLayout() hbox2_tool.addWidget(self.XSLabel) hbox2_tool.addWidget(self.XSlider) hbox2_tool.addWidget(self.XSliceLabel) hbox2_tool.addWidget(self.XSlice) hbox3_tool = QHBoxLayout() hbox3_tool.addWidget(self.YSLabel) hbox3_tool.addWidget(self.YSlider) hbox3_tool.addWidget(self.YSliceLabel) hbox3_tool.addWidget(self.YSlice) vbox_tool = QVBoxLayout() vbox_tool.addLayout(hbox_tool) vbox_tool.addLayout(hbox2_tool) vbox_tool.addLayout(hbox3_tool) self.toolPanel.setLayout(vbox_tool) #color scale self.colorPanel = QGroupBox("Color Scale") self.colorPanel.setFixedHeight(105) self.colorPanel.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.cmapmenu = CustomComboBox() self.cmapmenu.setFixedWidth(120) self.cmapmenu.setStyle(QStyleFactory.create('Fusion')) self.cmapmenu.setCurrentText("twilight") self.cmapmenu.currentIndexChanged.connect(self.changecmapbyindex) self.Vmin = QDoubleSpinBox() self.Vmin.setFixedWidth(100) self.Vmin.setRange(-1e15, 1e15) self.Vmin.setDecimals(4) self.Vmin.setKeyboardTracking(False) self.Vmin.valueChanged.connect(self.setDynamicRange) self.Vmin.setStyle(QStyleFactory.create('Fusion')) self.VminLabel = QLabel("Min:") self.VminLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.Vmax = QDoubleSpinBox() self.Vmax.setFixedWidth(100) self.Vmax.setRange(-1e15, 1e15) self.Vmax.setDecimals(4) self.Vmax.setKeyboardTracking(False) self.Vmax.valueChanged.connect(self.setDynamicRange) self.Vmax.setStyle(QStyleFactory.create('Fusion')) self.VmaxLabel = QLabel("Max:") self.VmaxLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.resetVrange = QPushButton("Re") self.resetVrange.setFixedWidth(25) self.resetVrange.clicked.connect(self.resetDynamicRange) self.resetVrange.setStyle(QStyleFactory.create('Fusion')) self.revcmap = QCheckBox("R") self.revcmap.stateChanged.connect(self.reversecmap) self.GammaLabel = QLabel("Gamma:") self.GammaLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.Gamma = QDoubleSpinBox() self.Gamma.setFixedWidth(100) self.Gamma.setDecimals(3) self.Gamma.setRange(0.05, 20) self.Gamma.setSingleStep(0.01) self.Gamma.setValue(1) self.Gamma.setKeyboardTracking(False) self.Gamma.valueChanged.connect(self.setGammaFromSpinBox) self.Gamma.setStyle(QStyleFactory.create('Fusion')) self.GSlider = QSlider(Qt.Horizontal) self.GSlider.setRange(1000000 * np.log(0.05), 1000000 * np.log(20)) self.GSlider.setValue(0) self.GSlider.sliderMoved.connect(self.setGammaFromSlider) self.GSlider.setStyle(QStyleFactory.create('Fusion')) hbox1_cmap = QHBoxLayout() hbox1_cmap.addWidget(self.cmapmenu) hbox1_cmap.addStretch(1) hbox1_cmap.addWidget(self.VminLabel) hbox1_cmap.addWidget(self.Vmin) hbox1_cmap.addStretch(1) hbox1_cmap.addWidget(self.VmaxLabel) hbox1_cmap.addWidget(self.Vmax) hbox1_cmap.addStretch(1) hbox1_cmap.addWidget(self.resetVrange) hbox2_cmap = QHBoxLayout() hbox2_cmap.addWidget(self.GammaLabel) hbox2_cmap.addWidget(self.Gamma) hbox2_cmap.addWidget(self.GSlider) hbox2_cmap.addWidget(self.revcmap) vbox_cmap = QVBoxLayout() vbox_cmap.addLayout(hbox1_cmap) vbox_cmap.addLayout(hbox2_cmap) self.colorPanel.setLayout(vbox_cmap) self.colorPanel.setEnabled(False) #Layout box = QVBoxLayout() box.addWidget(self.toolPanel) box.addWidget(self.canvas) box.addWidget(self.colorPanel) self.setLayout(box) def loaddata(self, data): self.canvas.draw() self.selector.set_visible(False) self.data = data self.GetWhiteBackground() self.X.setRange(data.xmin, data.xmax) self.X.setSingleStep(data.xstep) self.Y.setRange(data.ymin, data.ymax) self.Y.setSingleStep(data.ystep) newX = 0 if data.xmin * data.xmax < 0: newX = 0 else: newX = (data.xmin + data.xmax) / 2 if round(newX, 6) == self.X.value(): self.X.valueChanged.emit(newX) else: self.X.setValue(newX) newY = 0 if data.ymin * data.ymax < 0: newY = 0 else: newY = (data.ymin + data.ymax) / 2 if round(newY, 6) == self.Y.value(): self.Y.valueChanged.emit(newY) else: self.Y.setValue(newY) if data.data[~np.isnan(data.data)].max() > 0: self.Vmax.setValue(data.data[~np.isnan(data.data)].max()) self.Vmin.setValue(data.data[~np.isnan(data.data)].min()) else: self.Vmin.setValue(data.data[~np.isnan(data.data)].min()) self.Vmax.setValue(data.data[~np.isnan(data.data)].max()) self.XSlice.setRange(1, self.data.dimension[0]) self.YSlice.setRange(1, self.data.dimension[1]) self.plotimage(data, self.getcurrentcmap(), self.Vmin.value(), self.Vmax.value(), False) self.updatecolorscale(self.getcurrentcmap(), False) self.updateAxesPosition(True) self.colorPanel.setEnabled(True) def setValueFromSlider(self, value): if self.sender() == self.XSlider: self.X.setValue(value * (self.X.maximum() - self.X.minimum()) / 10000 + self.X.minimum()) elif self.sender() == self.YSlider: self.Y.setValue(value * (self.Y.maximum() - self.Y.minimum()) / 10000 + self.Y.minimum()) def setValueFromSpinBox(self, value): if self.sender() == self.X: self.XSlider.setValue( (value - self.X.minimum()) / (self.X.maximum() - self.X.minimum()) * 10000) half_wid_num = (self.XSlice.value() - 1) / 2 self.ly1.set_xdata(value - self.data.xstep * half_wid_num) self.ly2.set_xdata(value + self.data.xstep * half_wid_num) self.vl.set_xdata(value) self.YDC = self.get_YDC(value) self.resetYDC() self.plotYDC(True, True) self.plotXDC(True, True) elif self.sender() == self.Y: self.YSlider.setValue( (value - self.Y.minimum()) / (self.Y.maximum() - self.Y.minimum()) * 10000) half_wid_num = (self.YSlice.value() - 1) / 2 self.lx1.set_ydata(value - self.data.ystep * half_wid_num) self.lx2.set_ydata(value + self.data.ystep * half_wid_num) self.hl.set_ydata(value) self.XDC = self.get_XDC(value) self.resetXDC() self.plotXDC(True, True) self.plotYDC(True, True) self.plotArtist() self.setIntensity() def setIntensity(self): intensity = self.get_intensity(self.X.value(), self.Y.value()) if np.isnan(intensity): self.Int.setText("NaN") elif np.abs(intensity) >= 0.01: self.Int.setText("%.2f" % intensity) else: self.Int.setText("%.2e" % intensity) def setSlice(self, value): if self.sender() == self.XSlice: if value % 2 == 0: self.XSlice.setValue(value - 1) else: if value == 1: self.ly2.set_visible(False) else: self.ly2.set_visible(True) half_wid_num = (self.XSlice.value() - 1) / 2 self.ly1.set_xdata(self.X.value() - self.data.xstep * half_wid_num) self.ly2.set_xdata(self.X.value() + self.data.xstep * half_wid_num) self.plotArtist() self.YDC = self.get_YDC(self.X.value()) self.resetYDC() self.plotYDC(True, True) if self.sender() == self.YSlice: if value % 2 == 0: self.YSlice.setValue(value - 1) else: if value == 1: self.lx2.set_visible(False) else: self.lx2.set_visible(True) half_wid_num = (self.YSlice.value() - 1) / 2 self.lx1.set_ydata(self.Y.value() - self.data.ystep * half_wid_num) self.lx2.set_ydata(self.Y.value() + self.data.ystep * half_wid_num) self.plotArtist() self.XDC = self.get_XDC(self.Y.value()) self.resetXDC() self.plotXDC(True, True) self.setIntensity() def setDynamicRange(self, value): if self.sender() == self.Vmin: if value <= self.Vmax.value(): self.plotimage(self.data, self.getcurrentcmap(), value, self.Vmax.value(), True) else: self.plotimage(self.data, self.getcurrentcmap(), self.Vmax.value(), self.Vmax.value(), True) elif self.sender() == self.Vmax: if value >= self.Vmin.value(): self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(), value, True) else: self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(), self.Vmin.value(), True) def resetDynamicRange(self): self.Vmin.setValue(self.data.data[~np.isnan(self.data.data)].min()) self.Vmax.setValue(self.data.data[~np.isnan(self.data.data)].max()) def setTickLabelFont(self, ax, font): for tick in ax.get_xticklabels(): tick.set_fontname(font) for tick in ax.get_yticklabels(): tick.set_fontname(font) def scale2pnt(self, value, scale): return (np.abs(scale - value)).argmin() def get_intensity(self, xvalue, yvalue): xidx = self.scale2pnt(xvalue, self.data.xscale) yidx = self.scale2pnt(yvalue, self.data.yscale) half_wid_xnum = int((self.XSlice.value() - 1) / 2) half_wid_ynum = int((self.YSlice.value() - 1) / 2) if xidx < half_wid_xnum: xminidx = 0 else: xminidx = xidx - half_wid_xnum xmaxidx = xidx + half_wid_xnum + 1 if yidx < half_wid_ynum: yminidx = 0 else: yminidx = yidx - half_wid_ynum ymaxidx = yidx + half_wid_ynum + 1 return self.data.data[xminidx:xmaxidx, yminidx:ymaxidx].sum() def get_XDC(self, yvalue): yidx = self.scale2pnt(yvalue, self.data.yscale) half_wid_num = int((self.YSlice.value() - 1) / 2) if yidx < half_wid_num: yminidx = 0 else: yminidx = yidx - half_wid_num ymaxidx = yidx + half_wid_num + 1 array = self.data.data[:, yminidx:ymaxidx] return np.sum(array, axis=1) def get_YDC(self, xvalue): xidx = self.scale2pnt(xvalue, self.data.xscale) half_wid_num = int((self.XSlice.value() - 1) / 2) if xidx < half_wid_num: xminidx = 0 else: xminidx = xidx - half_wid_num xmaxidx = xidx + half_wid_num + 1 array = self.data.data[xminidx:xmaxidx, :] return np.sum(array, axis=0) def getcurrentcmap(self): cmap_str = self.cmapmenu.currentText() if self.revcmap.checkState() == Qt.Checked: cmap_str += "_r" raw_cmap = cm.get_cmap(cmap_str, 256) linearIndex = np.linspace(0, 1, 256) nonlinearIndex = np.power(linearIndex, self.Gamma.value()) new_cmap = ListedColormap(raw_cmap(nonlinearIndex)) return new_cmap def setGammaFromSpinBox(self, value): self.updatecolorscale(self.getcurrentcmap(), True) self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(), self.Vmax.value(), True) self.GSlider.setValue(1000000 * np.log(value)) def setGammaFromSlider(self, value): self.Gamma.setValue(np.exp(value / 1000000)) def reversecmap(self, state): self.updatecolorscale(self.getcurrentcmap(), True) self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(), self.Vmax.value(), True) def changecmapbyindex(self, index): self.updatecolorscale(self.getcurrentcmap(), True) self.plotimage(self.data, self.getcurrentcmap(), self.Vmin.value(), self.Vmax.value(), True) def updatecolorscale(self, cmap, removeflag): if removeflag: for img in self.ax_cmap.get_images(): img.remove() self.cmapimage = self.ax_cmap.imshow(self.gradient, cmap=cmap, origin="lower", aspect="auto") self.ax_cmap.redraw_in_frame() self.canvas.blit(self.ax_cmap.bbox) def begincursor(self, on): if on: self.lx1.set_visible(True) self.ly1.set_visible(True) if self.YSlice.value() > 1: self.lx2.set_visible(True) if self.XSlice.value() > 1: self.ly2.set_visible(True) self.vl.set_visible(True) self.hl.set_visible(True) self.plotArtist() self.plotXDC(True, True) self.plotYDC(True, True) self.cid_mouse = self.canvas.mpl_connect('motion_notify_event', self.navigate) self.cid_key = self.canvas.mpl_connect('key_press_event', self.navigate) else: self.lx1.set_visible(False) self.ly1.set_visible(False) self.lx2.set_visible(False) self.ly2.set_visible(False) self.vl.set_visible(False) self.hl.set_visible(False) self.plotXDC(True, False) self.plotYDC(True, False) self.plotArtist() self.canvas.mpl_disconnect(self.cid_mouse) self.canvas.mpl_disconnect(self.cid_key) def navigate(self, event): if event.inaxes == self.ax_Spec and event.key == "control": self.X.setValue(event.xdata) self.Y.setValue(event.ydata) def OnPress(self, event): if event.inaxes == self.ax_Spec and event.button == 1: if not self.selector.visible: #draw selector from nothing self.selector.moving_state = "draw" self.selector.origin = (event.xdata, event.ydata) self.cid_drawRS = self.canvas.mpl_connect( 'motion_notify_event', self.OnDrawRS) else: dist = self.selector.nearestCorner(event.x, event.y) if dist < 10: self.selector.moving_state = "moveHandle" self.selector.origin = (event.xdata, event.ydata) self.cid_moveHandle = self.canvas.mpl_connect( 'motion_notify_event', self.OnMoveHandle) else: if self.selector.isinRegion( event.xdata, event.ydata): #move the selector self.selector.moving_state = "move" self.selector.origin = (event.xdata, event.ydata) self.cid_moveRS = self.canvas.mpl_connect( 'motion_notify_event', self.OnMoveRS) else: #clear the selector self.selector.set_visible(False) self.plotArtist() def OnDrawRS(self, event): if event.inaxes == self.ax_Spec and event.button == 1: if event.xdata != self.selector.origin[ 0] and event.ydata != self.selector.origin[1]: self.selector.set_visible(True) self.selector.resize(self.selector.origin[0], self.selector.origin[1], event.xdata, event.ydata) self.plotArtist() def OnMoveRS(self, event): if event.inaxes == self.ax_Spec and event.button == 1: xmin, ymin, xmax, ymax = self.selector.region self.selector.resize(xmin + event.xdata - self.selector.origin[0], ymin + event.ydata - self.selector.origin[1], xmax + event.xdata - self.selector.origin[0], ymax + event.ydata - self.selector.origin[1]) self.plotArtist() self.selector.origin = (event.xdata, event.ydata) def OnMoveHandle(self, event): if event.inaxes == self.ax_Spec and event.button == 1: xmin, ymin, xmax, ymax = self.selector.region if self.selector.active_handle == 0: self.selector.resize( xmin + event.xdata - self.selector.origin[0], ymin + event.ydata - self.selector.origin[1], xmax, ymax) elif self.selector.active_handle == 1: self.selector.resize( xmin, ymin + event.ydata - self.selector.origin[1], xmax, ymax) elif self.selector.active_handle == 2: self.selector.resize( xmin, ymin + event.ydata - self.selector.origin[1], xmax + event.xdata - self.selector.origin[0], ymax) elif self.selector.active_handle == 3: self.selector.resize( xmin, ymin, xmax + event.xdata - self.selector.origin[0], ymax) elif self.selector.active_handle == 4: self.selector.resize( xmin, ymin, xmax + event.xdata - self.selector.origin[0], ymax + event.ydata - self.selector.origin[1]) elif self.selector.active_handle == 5: self.selector.resize( xmin, ymin, xmax, ymax + event.ydata - self.selector.origin[1]) elif self.selector.active_handle == 6: self.selector.resize( xmin + event.xdata - self.selector.origin[0], ymin, xmax, ymax + event.ydata - self.selector.origin[1]) elif self.selector.active_handle == 7: self.selector.resize( xmin + event.xdata - self.selector.origin[0], ymin, xmax, ymax) self.plotArtist() self.selector.origin = (event.xdata, event.ydata) def OnRelease(self, event): if event.inaxes == self.ax_Spec: if event.button == 1: if self.selector.moving_state == "draw": self.canvas.mpl_disconnect(self.cid_drawRS) elif self.selector.moving_state == "move": self.canvas.mpl_disconnect(self.cid_moveRS) elif self.selector.moving_state == "moveHandle": self.canvas.mpl_disconnect(self.cid_moveHandle) if event.button == 3: if self.selector.visible and self.selector.isinRegion( event.xdata, event.ydata): self.CropAction.setEnabled(True) self.SelectMenu.setEnabled(True) else: self.CropAction.setEnabled(False) self.SelectMenu.setEnabled(False) self.showContextMenu() else: if event.button == 3: self.CropAction.setEnabled(False) self.SelectMenu.setEnabled(False) self.showContextMenu() def showContextMenu(self): self.contextMenu.move(QCursor.pos()) self.contextMenu.show() def crop(self): x0, y0, x1, y1 = self.selector.region self.viewer.Win.DataProcessor.tab_single.setSelectXYRange( x0, x1, y0, y1, True) self.selector.set_visible(False) self.plotArtist() self.viewer.Win.DataProcessor.tab_single.crop2D.clicked.emit() def setSelectionRange(self): x0, y0, x1, y1 = self.selector.region if self.sender() == self.SelectX: self.viewer.Win.DataProcessor.tab_single.setSelectXRange(x0, x1) elif self.sender() == self.SelectY: self.viewer.Win.DataProcessor.tab_single.setSelectYRange(y0, y1) elif self.sender() == self.SelectXY: self.viewer.Win.DataProcessor.tab_single.setSelectXYRange( x0, x1, y0, y1, True) self.selector.set_visible(False) self.plotArtist() def outputData(self): if self.sender() == self.XDC_action: pass elif self.sender() == self.YDC_action: pass def OnKeyPress(self, event): if event.key == 'ctrl+a': if self.toolPanel.isChecked(): self.toolPanel.setChecked(False) else: self.toolPanel.setChecked(True) def GetWhiteBackground(self): self.ax_XDC.redraw_in_frame() self.canvas.blit(self.ax_XDC.get_window_extent()) self.ax_YDC.redraw_in_frame() self.canvas.blit(self.ax_YDC.get_window_extent()) self.background_XDC = self.canvas.copy_from_bbox( self.ax_XDC.get_window_extent()) self.background_YDC = self.canvas.copy_from_bbox( self.ax_YDC.get_window_extent()) self.background_Spec = self.canvas.copy_from_bbox( self.ax_Spec.get_window_extent()) def plotArtist(self): self.canvas.restore_region(self.background_Spec) for line in self.ax_Spec.get_lines(): self.ax_Spec.draw_artist(line) for artist in self.selector.artist: self.ax_Spec.draw_artist(artist) self.canvas.blit(self.ax_Spec.get_window_extent()) def plotimage(self, data, cmap, vmin, vmax, useblit): for img in self.ax_Spec.get_images(): img.remove() self.twoDspec = self.ax_Spec.imshow( data.data.T, cmap=cmap, vmin=vmin, vmax=vmax, origin="lower", extent=(data.xmin - 0.5 * data.xstep, data.xmax + 0.5 * data.xstep, data.ymin - 0.5 * data.ystep, data.ymax + 0.5 * data.ystep), aspect=self.aspect, interpolation='none') self.setTickLabelFont(self.ax_Spec, "Comic Sans MS") visible_flag = self.selector.visible self.selector.set_visible(False) if useblit: self.refreshimage() else: pass self.background_Spec = self.canvas.copy_from_bbox( self.ax_Spec.get_window_extent() ) #this line makes sure that the background will be updated when user change color map self.selector.set_visible(visible_flag) self.plotArtist() def refreshimage(self): self.ax_Spec.redraw_in_frame() self.canvas.blit(self.ax_Spec.get_window_extent()) def plotXDC(self, useblit, usecursor): if useblit: self.canvas.restore_region(self.background_XDC) self.ax_XDC.draw_artist(self.XDCLine) if usecursor: self.ax_XDC.draw_artist(self.vl) self.canvas.blit(self.ax_XDC.get_window_extent()) else: self.canvas.draw() def plotYDC(self, useblit, usecursor): if useblit: self.canvas.restore_region(self.background_YDC) self.ax_YDC.draw_artist(self.YDCLine) if usecursor: self.ax_YDC.draw_artist(self.hl) self.canvas.blit(self.ax_YDC.get_window_extent()) else: self.canvas.draw() def resetXDC(self): mask = np.isnan(self.XDC) self.XDCLine.set_data(self.data.xscale[~mask], self.XDC[~mask]) if len(self.XDC[~mask]) > 1: minvalue = min(self.XDC[~mask]) maxvalue = max(self.XDC[~mask]) if minvalue == maxvalue: minvalue = minvalue - 0.5 maxvalue = maxvalue + 0.5 else: minvalue = 0 maxvalue = 1 self.ax_XDC.set_ybound(lower=minvalue - 0.01 * (maxvalue - minvalue), upper=maxvalue + 0.01 * (maxvalue - minvalue)) self.ax_XDC.set_xbound(lower=self.data.xmin - self.data.xstep * 0.5, upper=self.data.xmax + self.data.xstep * 0.5) def resetYDC(self): mask = np.isnan(self.YDC) self.YDCLine.set_data(self.YDC[~mask], self.data.yscale[~mask]) if len(self.YDC[~mask]) > 1: minvalue = min(self.YDC[~mask]) maxvalue = max(self.YDC[~mask]) if minvalue == maxvalue: minvalue = minvalue - 0.5 maxvalue = maxvalue + 0.5 else: minvalue = 0 maxvalue = 1 self.ax_YDC.set_xbound(lower=minvalue - 0.01 * (maxvalue - minvalue), upper=maxvalue + 0.01 * (maxvalue - minvalue)) self.ax_YDC.set_ybound(lower=self.data.ymin - self.data.ystep * 0.5, upper=self.data.ymax + self.data.ystep * 0.5) def changeAspect(self, state): if state: self.aspect = "equal" else: self.aspect = "auto" self.ax_Spec.set_aspect(self.aspect) self.updateAxesPosition(True) def updateAxesPosition(self, useDraw): x_Spec, y_Spec, w_Spec, h_Spec = self.ax_Spec.get_position().bounds x_XDC, y_XDC, w_XDC, h_XDC = self.ax_XDC.get_position().bounds x_YDC, y_YDC, w_YDC, h_YDC = self.ax_YDC.get_position().bounds self.ax_XDC.set_position([x_Spec, y_XDC, w_Spec, h_XDC]) self.ax_YDC.set_position([x_YDC, y_Spec, w_YDC, h_Spec]) visible_flag = self.selector.visible self.selector.set_visible(False) if useDraw: self.canvas.draw() self.GetWhiteBackground() self.selector.set_visible(visible_flag) self.plotArtist() self.plotXDC(True, True) self.plotYDC(True, True) def resizeEvent_wrapper(self): self.updateAxesPosition(False)