class DnDReorderListWidget(_QListWidget): """ List widget with drag-n-drop reordering """ reordered = _pyqtSignal() def __init__(self, parent): super(DnDReorderListWidget, self).__init__(parent) self.setAcceptDrops(True) self.setEditTriggers(_QtWidgets.QAbstractItemView.NoEditTriggers) self.setDragEnabled(True) self.setDragDropMode(_QtWidgets.QAbstractItemView.InternalMove) self.setDefaultDropAction(_QtCore.Qt.MoveAction) self.setAlternatingRowColors(False) self.setSelectionMode(_QtWidgets.QAbstractItemView.SingleSelection) self.setObjectName("listWidgetDatasets") item = _QtWidgets.QListWidgetItem() self.addItem(item) item = _QtWidgets.QListWidgetItem() self.addItem(item) self.setSortingEnabled(False) item = self.item(0) item.setText('A') item = self.item(1) item.setText('B') def dropEvent(self, e): super().dropEvent(e) self.reordered.emit()
class AbstractPlotEffectPlugin(_QWidget): parameters = { 'name' : 'Name', 'long_name' : 'Longer Name' } labels_orig = { 'x_label' : 'x', 'y_label' : 'y', 'title' : 'Original' } labels_affected = { 'x_label' : 'x', 'y_label' : 'y', 'title' : 'Affected' } changed = _pyqtSignal() # def __init__(self): # raise NotImplementedError def fcn(self, data_in): raise NotImplementedError
class widgetNothing(_QWidget): """ Plugin widget for PlotEffect subUI. This plugin does nothing (i.e., it is a template and for demonstration \ purposes). Software Info -------------- Original Python branch: Feb 16 2015 author: ("Charles H Camp Jr") email: ("*****@*****.**") version: ("16.02.29") """ changed = _pyqtSignal() def __init__(self, parent = None): super(widgetNothing, self).__init__(parent) ### EDIT ### self.ui = Ui_Nothing_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### def fcn(self, data_in): return data_in
class widgetNothing(_QWidget): """ Plugin widget for PlotEffect subUI. This plugin does nothing (i.e., it is a template and for demonstration \ purposes). """ changed = _pyqtSignal() def __init__(self, parent = None): super(widgetNothing, self).__init__(parent) ### EDIT ### self.ui = Ui_Nothing_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### def fcn(self, data_in): return data_in
class TableModelLines(_AbstractTableModelMpl): """ Signals ------- dataSeleted : int, int Row, id of plot """ _HEADERS = [ 'Color', 'Alpha', 'LineWidth', 'LineStyle', 'Marker', 'Marker Size', 'Label', 'Delete' ] _COL_COLOR = _HEADERS.index('Color') _COL_ALPHA = _HEADERS.index('Alpha') _COL_LINEWIDTH = _HEADERS.index('LineWidth') _COL_LINESTYLE = _HEADERS.index('LineStyle') _COL_MARKER = _HEADERS.index('Marker') _COL_MARKERSIZE = _HEADERS.index('Marker Size') _COL_LABEL = _HEADERS.index('Label') _COL_DELETE = _HEADERS.index('Delete') # _COL_ID = _HEADERS.index('ID') dataDeleted = _pyqtSignal(int, float) def __init__(self, parent=None): super(_QAbstractTableModel, self).__init__(parent) self.headers = TableModelLines._HEADERS self._model_data = [] def rowCount(self, parent=_QModelIndex()): return len(self._model_data) def columnCount(self, parent=_QModelIndex()): return len(self.headers) def headerData(self, col, orientation, role): if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: return self.headers[col] return _QVariant() def doubleClickCheck(self, index): col = index.column() if col == TableModelLines._COL_COLOR: # Color self.changeColor(index) elif col == TableModelLines._COL_DELETE: # Delete? self.deleteData(index) def deleteData(self, index): self.setData(index, True) def changeColor(self, index): row = index.row() color = self._model_data[row]['color'] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) result = _QColorDialog.getColor(qcolor) if _QColor.isValid(result): self.setData(index, result.getRgb()) else: return None def data(self, index, role=_Qt.DisplayRole): if not index.isValid() or not 0 <= index.row() < self.rowCount(): return _QVariant() row = index.row() col = index.column() if role == _Qt.DisplayRole: if col == TableModelLines._COL_COLOR: return str(round_list(self._model_data[row]['color'])) elif col == TableModelLines._COL_ALPHA: return str(self._model_data[row]['alpha']) elif col == TableModelLines._COL_LINEWIDTH: return str(self._model_data[row]['linewidth']) elif col == TableModelLines._COL_LINESTYLE: ls = self._model_data[row]['linestyle'] return str(MplLines.LINESTYLE_DICT[ls]) elif col == TableModelLines._COL_MARKER: mk = self._model_data[row]['marker'] return str(MplMarkers.MARKER_DICT[mk]) elif col == TableModelLines._COL_MARKERSIZE: return str(self._model_data[row]['markersize']) elif col == TableModelLines._COL_LABEL: return str(self._model_data[row]['label']) elif col == TableModelLines._COL_DELETE: return '' elif role == _Qt.DecorationRole: if col == TableModelLines._COL_COLOR: color = self._model_data[row]['color'] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon elif col == TableModelLines._COL_DELETE: color = [1, 0, 0] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon else: pass else: return _QVariant() def setData(self, index, value, role=_Qt.EditRole): if role == _Qt.EditRole: row = index.row() col = index.column() if col == TableModelLines._COL_COLOR: color_255 = value[0:-1] color = [ round(color_255[0] / 255, 2), round(color_255[1] / 255, 2), round(color_255[2] / 255, 2) ] self._model_data[row]['color'] = color # self.colorChanged.emit(row) elif col == TableModelLines._COL_ALPHA: self._model_data[row]['alpha'] = float(value) elif col == TableModelLines._COL_LINEWIDTH: self._model_data[row]['linewidth'] = float(value) elif col == TableModelLines._COL_LINESTYLE: self._model_data[row]['linestyle'] = value elif col == TableModelLines._COL_MARKER: self._model_data[row]['marker'] = value elif col == TableModelLines._COL_MARKERSIZE: self._model_data[row]['markersize'] = float(value) elif col == TableModelLines._COL_LABEL: self._model_data[row]['label'] = value elif col == TableModelLines._COL_DELETE: if value: out = self._model_data.pop(row) self.layoutChanged.emit() self.dataDeleted.emit(row, out['id']) self.dataChanged.emit(index, index) def flags(self, index): flag = super(_QAbstractTableModel, self).flags(index) return flag | _Qt.ItemIsEditable
class TableModelImages(_AbstractTableModelMpl): _HEADERS = [ 'Cmap', 'Alpha', 'Clim Low', 'Clim High', 'Label', 'Colorbar', 'Delete' ] _COL_CMAP = _HEADERS.index('Cmap') _COL_ALPHA = _HEADERS.index('Alpha') _COL_CLIM_LOW = _HEADERS.index('Clim Low') _COL_CLIM_HIGH = _HEADERS.index('Clim High') _COL_CBAR = _HEADERS.index('Colorbar') _COL_LABEL = _HEADERS.index('Label') _COL_DELETE = _HEADERS.index('Delete') dataDeleted = _pyqtSignal(int, float) def __init__(self, parent=None): super(_QAbstractTableModel, self).__init__(parent) self.headers = TableModelImages._HEADERS self._model_data = [] def rowCount(self, parent=_QModelIndex()): return len(self._model_data) def columnCount(self, parent=_QModelIndex()): return len(self.headers) def headerData(self, col, orientation, role): if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: return self.headers[col] return _QVariant() def doubleClickCheck(self, index): col = index.column() # if col == TableModelImages._COL_CMAP: # CMAP # self.changeColor(index) if col == TableModelImages._COL_DELETE: # Delete? self.deleteData(index) def deleteData(self, index): self.setData(index, True) def data(self, index, role=_Qt.DisplayRole): if not index.isValid() or not 0 <= index.row() < self.rowCount(): return _QVariant() row = index.row() col = index.column() if role == _Qt.DisplayRole: if col == TableModelImages._COL_CMAP: return str(self._model_data[row]['cmap_name']) elif col == TableModelImages._COL_ALPHA: return str(self._model_data[row]['alpha']) elif col == TableModelImages._COL_CLIM_LOW: return str(self._model_data[row]['clim_low']) elif col == TableModelImages._COL_CLIM_HIGH: return str(self._model_data[row]['clim_high']) elif col == TableModelImages._COL_CBAR: # print('1') return str(self._model_data[row]['colorbar']) elif col == TableModelImages._COL_LABEL: return str(self._model_data[row]['label']) elif col == TableModelImages._COL_DELETE: return '' elif role == _Qt.DecorationRole: if col == TableModelImages._COL_DELETE: color = [1, 0, 0] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon else: return _QVariant() def setData(self, index, value, role=_Qt.EditRole): if role == _Qt.EditRole: row = index.row() col = index.column() if col == TableModelImages._COL_CMAP: self._model_data[row]['cmap_name'] = value elif col == TableModelImages._COL_ALPHA: self._model_data[row]['alpha'] = float(value) elif col == TableModelImages._COL_CLIM_LOW: self._model_data[row]['clim_low'] = float(value) elif col == TableModelImages._COL_CLIM_HIGH: self._model_data[row]['clim_high'] = float(value) elif col == TableModelImages._COL_CBAR: # print('2') self._model_data[row]['colorbar'] = bool(value) elif col == TableModelImages._COL_LABEL: self._model_data[row]['label'] = value elif col == TableModelImages._COL_DELETE: if value: out = self._model_data.pop(row) self.layoutChanged.emit() self.dataDeleted.emit(row, out['id']) self.dataChanged.emit(index, index) def flags(self, index): flag = super(_QAbstractTableModel, self).flags(index) return flag | _Qt.ItemIsEditable
class TableModelFillBetween(_AbstractTableModelMpl): _HEADERS = ['Facecolor', 'Alpha', 'Edgecolor', 'LineWidth', 'Label', 'Delete'] _COL_FACECOLOR = _HEADERS.index('Facecolor') _COL_ALPHA = _HEADERS.index('Alpha') _COL_EDGECOLOR = _HEADERS.index('Edgecolor') _COL_LINEWIDTH = _HEADERS.index('LineWidth') _COL_LABEL = _HEADERS.index('Label') _COL_DELETE = _HEADERS.index('Delete') dataDeleted = _pyqtSignal(int, float) def __init__(self, parent=None): super(_QAbstractTableModel, self).__init__(parent) self.headers = TableModelFillBetween._HEADERS self._model_data = [] def rowCount(self, parent=_QModelIndex()): """ Return row count of table view """ return len(self._model_data) def columnCount(self, parent=_QModelIndex()): """ Return col count of table view """ return len(self.headers) def headerData(self, col, orientation, role): """ Basic horizontal header with no special role """ if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: return self.headers[col] return _QVariant() def doubleClickCheck(self, index): """ Double-clicking certain columns has special effects. In this case, the color change columns and the delete column """ col = index.column() if (col == TableModelFillBetween._COL_FACECOLOR or col == TableModelFillBetween._COL_EDGECOLOR): # Face- or EdgeColor self.changeColor(index) elif col == TableModelFillBetween._COL_DELETE: # Delete? self.deleteData(index) def deleteData(self, index): self.setData(index, True) def changeColor(self, index): row = index.row() col = index.column() if col == TableModelFillBetween._COL_FACECOLOR: color = self._model_data[row]['facecolor'] else: color = self._model_data[row]['edgecolor'] # from [0,1] -> [0,255] color scale color_256 = [color[0]*255, color[1]*255, color[2]*255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) result = _QColorDialog.getColor(qcolor) if _QColor.isValid(result): self.setData(index, result.getRgb()) else: return None def data(self, index, role=_Qt.DisplayRole): if not index.isValid() or not 0 <= index.row() < self.rowCount(): return _QVariant() row = index.row() col = index.column() if role == _Qt.DisplayRole: if col == TableModelFillBetween._COL_FACECOLOR: return str(self._model_data[row]['facecolor']) elif col == TableModelFillBetween._COL_ALPHA: return str(self._model_data[row]['alpha']) elif col == TableModelFillBetween._COL_EDGECOLOR: return str(self._model_data[row]['edgecolor']) elif col == TableModelFillBetween._COL_LINEWIDTH: return str(self._model_data[row]['linewidth']) elif col == TableModelFillBetween._COL_LABEL: return str(self._model_data[row]['label']) elif col == TableModelFillBetween._COL_DELETE: return '' elif role == _Qt.DecorationRole: if (col == TableModelFillBetween._COL_FACECOLOR or col == TableModelFillBetween._COL_EDGECOLOR): if col == TableModelFillBetween._COL_FACECOLOR: color = self._model_data[row]['facecolor'] elif col == TableModelFillBetween._COL_EDGECOLOR: color = self._model_data[row]['edgecolor'] color_256 = [color[0]*255, color[1]*255, color[2]*255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon elif col == TableModelFillBetween._COL_DELETE: color = [1, 0, 0] color_256 = [color[0]*255, color[1]*255, color[2]*255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon else: return _QVariant() else: return _QVariant() def setData(self, index, value, role=_Qt.EditRole): if role == _Qt.EditRole: row = index.row() col = index.column() if col == TableModelFillBetween._COL_FACECOLOR: color_255 = value[0:-1] color = [round(color_255[0]/255, 2), round(color_255[1]/255, 2), round(color_255[2]/255, 2)] self._model_data[row]['facecolor'] = color elif col == TableModelFillBetween._COL_EDGECOLOR: color_255 = value[0:-1] color = [round(color_255[0]/255, 2), round(color_255[1]/255, 2), round(color_255[2]/255, 2)] self._model_data[row]['edgecolor'] = color elif col == TableModelFillBetween._COL_ALPHA: self._model_data[row]['alpha'] = float(value) elif col == TableModelFillBetween._COL_LINEWIDTH: self._model_data[row]['linewidth'] = float(value) elif col == TableModelFillBetween._COL_LABEL: self._model_data[row]['label'] = value elif col == TableModelFillBetween._COL_DELETE: if value: out = self._model_data.pop(row) self.layoutChanged.emit() self.dataDeleted.emit(row, out['id']) self.dataChanged.emit(index, index) def flags(self, index): flag = super(_QAbstractTableModel, self).flags(index) return flag | _Qt.ItemIsEditable
class widgetCalibrate(_QWidget): """ This plugin widget provides calibration functionality. """ DEFAULT_N_PIX = 1600 DEFAULT_CTR_WL = 730.0 DEFAULT_CTR_WL0 = 730.0 DEFAULT_A_VEC = (-0.167740721307557, 863.8736708961577) DEFAULT_PROBE = 771.461 DEFAULT_MEAS = 1004.0 CALIB_DICT = {'n_pix' : DEFAULT_N_PIX, 'ctr_wl' : DEFAULT_CTR_WL, 'ctr_wl0' : DEFAULT_CTR_WL0, 'a_vec' : DEFAULT_A_VEC, 'probe' : DEFAULT_PROBE} changed = _pyqtSignal() def __init__(self, calib_dict=None, parent = None): super(widgetCalibrate, self).__init__(parent) ### EDIT ### self.ui = Ui_Calibrate_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### if calib_dict is None: self.calib_dict = self.CALIB_DICT else: self.calib_dict = calib_dict if isinstance(self.calib_dict['a_vec'], tuple): self.calib_dict['a_vec'] = list(self.calib_dict['a_vec']) self.new_calib_dict = _copy.deepcopy(self.calib_dict) self.updateUI() self.WN, _ = _calib_pix_wn(self.calib_dict) self.WN_2, _ = _calib_pix_wn(self.new_calib_dict) # self.WN, self.WL, _ = _make_freq_vector(self.calib_dict) # self.WN_2, self.WL_2, _ = _make_freq_vector(self.new_calib_dict) self.ui.spinBoxMeas.setValue(self.DEFAULT_MEAS) self.ui.spinBoxCorrect.setValue(self.DEFAULT_MEAS) self.meas = self.DEFAULT_MEAS self.correct = self.DEFAULT_MEAS self.ui.spinBoxCorrect.editingFinished.connect(self.calcCalibDict) self.ui.spinBoxMeas.editingFinished.connect(self.calcCalibDict) def fcn(self, data_in): """ Performs the KK. Parameters ---------- data : list data[0] : Wavenumber vector data[1] : NRB spectrum(a) data[2] : CARS spectrum(a) Returns ------- out : np.array Imaginary component the of KK-retrieved spectrum(a) See also -------- crikit.process.phase_retr, crikit.process.maths.kk """ if data_in.ndim == 1: spl = _UnivariateSpline(self.WN_2, data_in, s=0, ext=0) output = spl(self.WN) elif data_in.ndim == 2: output = _np.zeros(data_in.shape) for num, spect in enumerate(data_in): spl = _UnivariateSpline(self.WN_2, spect, s=0, ext=0) output[num,:] = spl(self.WN) return output #return data_in def calcCalibDict(self): """ (Re)-Calculate calibration dictionary components and recalculate \ wavenumber/wavelength vector """ self.meas = self.ui.spinBoxMeas.value() self.correct = self.ui.spinBoxCorrect.value() delta_lambda = 1/(((self.correct)/1e7) + 1/self.calib_dict['probe']) - \ 1/(((self.meas)/1e7) + 1/self.calib_dict['probe']) self.new_calib_dict['a_vec'][1] = self.calib_dict['a_vec'][1] + delta_lambda self.WN_2, _ = _calib_pix_wn(self.new_calib_dict) self.updateUI() self.changed.emit() def updateUI(self): # Set calibration values self.ui.spinBoxNPix.setValue(self.calib_dict['n_pix']) self.ui.spinBoxNPix_2.setValue(self.new_calib_dict['n_pix']) self.ui.spinBoxCenterWL.setValue(self.calib_dict['ctr_wl']) self.ui.spinBoxCenterWL_2.setValue(self.new_calib_dict['ctr_wl']) self.ui.spinBoxCalibWL.setValue(self.calib_dict['ctr_wl0']) self.ui.spinBoxCalibWL_2.setValue(self.new_calib_dict['ctr_wl0']) self.ui.spinBoxSlope.setValue(self.calib_dict['a_vec'][0]) self.ui.spinBoxSlope_2.setValue(self.new_calib_dict['a_vec'][0]) self.ui.spinBoxIntercept.setValue(self.calib_dict['a_vec'][1]) self.ui.spinBoxIntercept_2.setValue(self.new_calib_dict['a_vec'][1]) self.ui.spinBoxProbeWL.setValue(self.calib_dict['probe']) self.ui.spinBoxProbeWL_2.setValue(self.new_calib_dict['probe'])
class widgetSG(_QWidget): """ Plugin widget for PlotEffect subUI. This performs the Savitky-Golay filtering Attributes ---------- win_size : int Window size order : int Order (polynomial) Methods --------- fcn : Performs the Savitky-Golay Signals: changed : a value in the UI has changed References ---------- [1] C. H. Camp Jr, Y. J. Lee, and M. T. Cicerone, "Quantitative, Comparable Coherent Anti-Stokes Raman Scattering (CARS) Spectroscopy: Correcting Errors in Phase Retrieval" """ changed = _pyqtSignal() # WIN_SIZE = 601 # Window size # ORDER = 2 # Order def __init__(self, win_size=601, order=2, parent = None): super(widgetSG, self).__init__(parent) ### EDIT ### self.ui = Ui_SG_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### self.win_size = win_size self.order = order # Window size must be > order if self.win_size <= self.order: self.win_size = self.order + 1 # Window size must be odd if self.win_size%2 == 1: pass else: self.win_size += 1 self.ui.spinBoxWinSize.setValue(self.win_size) self.ui.spinBoxOrder.setValue(self.order) self.ui.spinBoxWinSize.valueChanged.connect(self.changeWinSize) self.ui.spinBoxOrder.valueChanged.connect(self.changeOrder) def fcn(self, data_in): baseline = _sg(data_in, window_length=self.win_size, polyorder=self.order, axis=-1) data_out = data_in - baseline return data_out def changeWinSize(self): temp_win_size = self.ui.spinBoxWinSize.value() if temp_win_size%2 == 1: self.win_size = temp_win_size else: self.ui.spinBoxWinSize.setValue(temp_win_size+1) self.win_size = temp_win_size+1 self.changed.emit() def changeOrder(self): self.order = self.ui.spinBoxOrder.value() self.changed.emit()
class widgetALS(_QWidget): """ Plugin widget for PlotEffect subUI. This performs detrending with the asymmetric least squares algorithms Attributes ---------- p : float ALS asymmetry parameter lam : float ALS smoothness parameter redux : int Methods --------- fcn : Performs the Asymmetric Least Squares Signals: changed : a value in the UI has changed Notes ----- Please cite the C. H. Camp Jr., et al. reference (below) if you use this software. References ---------- [1] C. H. Camp Jr, Y. J. Lee, and M. T. Cicerone, "Quantitative, Comparable Coherent Anti-Stokes Raman Scattering (CARS) Spectroscopy: Correcting Errors in Phase Retrieval" [2] P. H. C. Eilers, "A perfect smoother," Anal. Chem. 75, 3631-3636 (2003). [3] P. H. C. Eilers and H. F. M. Boelens, "Baseline correction with asymmetric least squares smoothing," Report. October 21, 2005. """ changed = _pyqtSignal() P_VAL = 1e-3 # Asymmetry LAMBDA_VAL = 1 # Smothness REDUX = 10 # Interpolation step size (pixels) def __init__(self, parent = None): super(widgetALS, self).__init__(parent) ### EDIT ### self.ui = Ui_ALS_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### self.ui.spinBoxP.setValue(self.P_VAL) self.ui.spinBoxLambda.setValue(self.LAMBDA_VAL) self.ui.spinBoxRedux.setValue(self.REDUX) self.p = self.P_VAL self.lam = self.LAMBDA_VAL self.redux = self.REDUX self.ui.spinBoxP.valueChanged.connect(self.changeP) self.ui.spinBoxLambda.valueChanged.connect(self.changeLambda) self.ui.spinBoxRedux.valueChanged.connect(self.changeRedux) def fcn(self, data_in): data_out = _np.zeros(data_in.shape) if data_in.ndim == 1: baseline = _als(data_in, redux_factor=self.redux, redux_full=False, smoothness_param=self.lam, asym_param=self.p)[0] data_out = data_in - baseline else: for num, spectrum in enumerate(data_in): baseline = _als(spectrum, redux_factor=self.redux, redux_full=False, smoothness_param=self.lam, asym_param=self.p, print_iteration=False)[0] data_out[num,:] = spectrum - baseline return data_out def changeP(self): self.p = self.ui.spinBoxP.value() self.changed.emit() def changeLambda(self): self.lam = self.ui.spinBoxLambda.value() self.changed.emit() def changeRedux(self): self.redux = self.ui.spinBoxRedux.value() self.changed.emit()
class widgetKK(_QWidget): """ Plugin widget for PlotEffect subUI. This plugin performs the Kramers-Kronig (KK) relation phase retrieval. Attributes ---------- nrb_norm : bool Normalize by the NRB flag cars_bias : (int, float) Constant added to the input CARS spectrum(a) nrb_bias : (int, float) Constant added to the input NRB spectrum(a) phaselin : (int, float) Constant phase correction to the retrieved phase phasesig1 : (int, float) For sigmoidal phase correction, the is the starting phase phasesig2 : (int, float) For sigmoidal phase correction, the is the ending phase sigrate : (int, float) For sigmoidal phase correction, this is the rate of change from \ phasesig1 to phasesig2 pad_factor : int Padding factor to use with the KK algorithm phase_type : str Global phase addition type: 'linear' or 'sigmoidal' Methods --------- fcn : Performs the KK Signals : changed : a value in the UI has changed """ changed = _pyqtSignal() NRB_NORM = True PHASE_TYPE = 'Linear' CARS_BIAS = 0 NRB_BIAS = 0 PHASELIN = 0 PHASESIG1 = 0 PHASESIG2 = 0 SIGRATE = 1 PADFACTOR = 1 def __init__(self, parent = None): super(widgetKK, self).__init__(parent) ### EDIT ### self.ui = Ui_KK_Form() ### EDIT ### self.ui.setupUi(self) ### EDIT ### self.setupKK() def setupKK(self): self.ui.checkBoxNRBNorm.setChecked(self.NRB_NORM) self.nrb_norm = self.NRB_NORM self.ui.spinBoxCARSBias.setValue(self.CARS_BIAS) self.ui.sliderCARSBias.setValue(self.CARS_BIAS) self.cars_bias = self.CARS_BIAS self.ui.spinBoxNRBBias.setValue(self.NRB_BIAS) self.ui.sliderNRBBias.setValue(self.NRB_BIAS) self.nrb_bias = self.NRB_BIAS self.ui.spinBoxPhaseLin.setValue(self.PHASELIN) self.ui.sliderPhaseLin.setValue(self.PHASELIN) self.phaselin = self.PHASELIN self.ui.spinBoxSigPhase1.setValue(self.PHASESIG1) self.ui.sliderSigPhase1.setValue(self.PHASESIG1) self.phasesig1 = self.PHASESIG1 self.ui.spinBoxSigPhase2.setValue(self.PHASESIG2) self.ui.sliderSigPhase2.setValue(self.PHASESIG2) self.phasesig2 = self.PHASESIG2 self.ui.spinBoxSigRate.setValue(self.SIGRATE) self.ui.sliderSigRate.setValue(self.SIGRATE) self.sigrate = self.SIGRATE self.ui.spinBoxPadFactor.setValue(self.PADFACTOR) self.pad_factor = self.PADFACTOR if self.PHASE_TYPE.lower() == 'linear': self.ui.tabWidget.setCurrentIndex(0) self.phase_type = 'linear' elif self.PHASE_TYPE.lower() == 'sigmoidal': self.ui.tabWidget.setCurrentIndex(1) self.phase_type = 'sigmoidal' else: self.ui.tabWidget.setCurrentIndex(0) self.phase_type = 'linear' # Signals-Slots self.ui.sliderCARSBias.valueChanged.connect(self.changeSliderCARSBiasPre) self.ui.sliderCARSBias.sliderReleased.connect(self.changeSliderCARSBiasFinal) self.ui.spinBoxCARSBias.valueChanged.connect(self.changeSpinBoxCARSBias) self.ui.sliderNRBBias.valueChanged.connect(self.changeSliderNRBBiasPre) self.ui.sliderNRBBias.sliderReleased.connect(self.changeSliderNRBBiasFinal) self.ui.spinBoxNRBBias.valueChanged.connect(self.changeSpinBoxNRBBias) self.ui.sliderPhaseLin.valueChanged.connect(self.changeSliderPhaseLinPre) self.ui.sliderPhaseLin.sliderReleased.connect(self.changeSliderPhaseLinFinal) self.ui.spinBoxPhaseLin.valueChanged.connect(self.changeSpinBoxPhaseLin) self.ui.sliderSigPhase1.valueChanged.connect(self.changeSliderSigPhase1Pre) self.ui.sliderSigPhase1.sliderReleased.connect(self.changeSliderSigPhase1Final) self.ui.spinBoxSigPhase1.valueChanged.connect(self.changeSpinBoxSigPhase1) self.ui.sliderSigPhase2.valueChanged.connect(self.changeSliderSigPhase2Pre) self.ui.sliderSigPhase2.sliderReleased.connect(self.changeSliderSigPhase2Final) self.ui.spinBoxSigPhase2.valueChanged.connect(self.changeSpinBoxSigPhase2) self.ui.sliderSigRate.valueChanged.connect(self.changeSliderSigRatePre) self.ui.sliderSigRate.sliderReleased.connect(self.changeSliderSigRateFinal) self.ui.spinBoxSigRate.valueChanged.connect(self.changeSpinBoxSigRate) self.ui.spinBoxPadFactor.valueChanged.connect(self.changeSpinBoxPadFactor) self.ui.checkBoxNRBNorm.clicked.connect(self.changeCheckBoxNRBNorm) self.ui.checkBoxLockBias.clicked.connect(self.changeCheckBoxLockBias) self.ui.radioButtonPhaseLin.clicked.connect(self.changeRadioPhaseLin) self.ui.radioButtonPhaseSig.clicked.connect(self.changeRadioPhaseSig) # Disabled sigmoidal phase correction for now self.ui.radioButtonPhaseSig.setEnabled(False) self.ui.sliderNRBBias.setEnabled(False) self.ui.spinBoxNRBBias.setEnabled(False) self.ui.sliderCARSBias.sliderReleased.connect(self.sliderBiasLock) self.ui.spinBoxCARSBias.valueChanged.connect(self.spinBoxBiasLock) def fcn(self, data_in): """ Performs the KK. Parameters ---------- data : list data[0] : Wavenumber vector data[1] : NRB spectrum(a) data[2] : CARS spectrum(a) Returns ------- out : np.array Imaginary component the of KK-retrieved spectrum(a) See also -------- crikit.process.phase_retr, crikit.process.maths.kk """ assert isinstance(data_in, list), 'KK plot effect fcn requires the data input be a list with length 3: WN, NRB, CARS' out = _kk(data_in[1] + self.nrb_bias, data_in[2] + self.cars_bias, phase_offset=self.phaselin*_np.pi/360, norm_by_bg=self.nrb_norm, pad_factor=self.pad_factor) return out.imag def changeSliderCARSBiasPre(self): self.ui.spinBoxCARSBias.setValue(self.ui.sliderCARSBias.value()) self.cars_bias = self.ui.sliderCARSBias.value() self.changed.emit() def changeSliderCARSBiasFinal(self): self.ui.spinBoxCARSBias.setValue(self.ui.sliderCARSBias.value()) self.cars_bias = self.ui.sliderCARSBias.value() self.changed.emit() def changeSpinBoxCARSBias(self): self.ui.sliderCARSBias.setValue(self.ui.spinBoxCARSBias.value()) self.cars_bias = self.ui.spinBoxCARSBias.value() self.changed.emit() def changeSliderNRBBiasPre(self): self.ui.spinBoxNRBBias.setValue(self.ui.sliderNRBBias.value()) self.nrb_bias = self.ui.sliderNRBBias.value() self.changed.emit() def changeSliderNRBBiasFinal(self): self.ui.spinBoxNRBBias.setValue(self.ui.sliderNRBBias.value()) self.nrb_bias = self.ui.sliderNRBBias.value() self.changed.emit() def changeSpinBoxNRBBias(self): self.ui.sliderNRBBias.setValue(self.ui.spinBoxNRBBias.value()) self.nrb_bias = self.ui.spinBoxNRBBias.value() self.changed.emit() def changeSliderPhaseLinPre(self): self.ui.spinBoxPhaseLin.setValue(self.ui.sliderPhaseLin.value()) self.phaselin = self.ui.sliderPhaseLin.value() self.changed.emit() def changeSliderPhaseLinFinal(self): self.ui.spinBoxPhaseLin.setValue(self.ui.sliderPhaseLin.value()) self.phaselin = self.ui.sliderPhaseLin.value() self.changed.emit() def changeSpinBoxPhaseLin(self): self.ui.sliderPhaseLin.setValue(self.ui.spinBoxPhaseLin.value()) self.phaselin = self.ui.spinBoxPhaseLin.value() self.changed.emit() def changeSliderSigPhase1Pre(self): self.ui.spinBoxSigPhase1.setValue(self.ui.sliderSigPhase1.value()) self.phasesig1 = self.ui.sliderSigPhase1.value() self.changed.emit() def changeSliderSigPhase1Final(self): self.ui.spinBoxSigPhase1.setValue(self.ui.sliderSigPhase1.value()) self.phasesig1 = self.ui.sliderSigPhase1.value() self.changed.emit() def changeSpinBoxSigPhase1(self): self.ui.sliderSigPhase1.setValue(self.ui.spinBoxSigPhase1.value()) self.phasesig1 = self.ui.spinBoxSigPhase1.value() self.changed.emit() def changeSliderSigPhase2Pre(self): self.ui.spinBoxSigPhase2.setValue(self.ui.sliderSigPhase2.value()) self.phasesig2 = self.ui.sliderSigPhase2.value() self.changed.emit() def changeSliderSigPhase2Final(self): self.ui.spinBoxSigPhase2.setValue(self.ui.sliderSigPhase2.value()) self.phasesig2 = self.ui.sliderSigPhase2.value() self.changed.emit() def changeSpinBoxSigPhase2(self): self.ui.sliderSigPhase2.setValue(self.ui.spinBoxSigPhase2.value()) self.phasesig2 = self.ui.spinBoxSigPhase2.value() self.changed.emit() def changeSliderSigRatePre(self): self.ui.spinBoxSigRate.setValue(self.ui.sliderSigRate.value()) self.sigrate = self.ui.sliderSigRate.value() self.changed.emit() def changeSliderSigRateFinal(self): self.ui.spinBoxSigRate.setValue(self.ui.sliderSigRate.value()) self.sigrate = self.ui.sliderSigRate.value() self.changed.emit() def changeSpinBoxSigRate(self): self.ui.sliderSigRate.setValue(self.ui.spinBoxSigRate.value()) self.sigrate = self.ui.spinBoxSigRate.value() self.changed.emit() def changeCheckBoxLockBias(self): if self.ui.checkBoxLockBias.isChecked(): self.ui.sliderNRBBias.setEnabled(False) self.ui.spinBoxNRBBias.setEnabled(False) self.ui.sliderCARSBias.sliderReleased.connect(self.sliderBiasLock) self.ui.spinBoxCARSBias.valueChanged.connect(self.spinBoxBiasLock) self.sliderBiasLock() self.spinBoxBiasLock() else: self.ui.sliderNRBBias.setEnabled(True) self.ui.spinBoxNRBBias.setEnabled(True) self.ui.sliderCARSBias.sliderReleased.disconnect(self.sliderBiasLock) self.ui.spinBoxCARSBias.valueChanged.disconnect(self.spinBoxBiasLock) self.changed.emit() def sliderBiasLock(self): self.ui.sliderNRBBias.setValue(self.ui.sliderCARSBias.value()) self.nrb_bias = self.ui.sliderCARSBias.value() self.changed.emit() def spinBoxBiasLock(self): self.ui.spinBoxNRBBias.setValue(self.ui.spinBoxCARSBias.value()) self.nrb_bias = self.ui.sliderCARSBias.value() self.changed.emit() def changeCheckBoxNRBNorm(self): if self.ui.checkBoxNRBNorm.isChecked(): self.nrb_norm = True else: self.nrb_norm = False self.changed.emit() def changeRadioPhaseLin(self): self.phase_type = 'linear' self.ui.tabWidget.setCurrentIndex(0) def changeRadioPhaseSig(self): self.phase_type = 'sigmoidal' self.ui.tabWidget.setCurrentIndex(1) def changeSpinBoxPadFactor(self): self.pad_factor = self.ui.spinBoxPadFactor.value() self.changed.emit()
class SciPlotUI(_QMainWindow): """ Scientific plotting user-interface for creating publication-quality plots and images Parameters ---------- limit_to : list, optional (default = None) Limit the application to implement only certain functionality. \ Default is all elements turned ON. See Notes for options. show : bool, optional (default = True) Whether to show the UI upon instantiation Methods ------- plot : MPL-like plotting functionality imshow : MPL-like imshow bar : MPL-like bar plot EXCEPT centered (rather than left-edge defined) hist : MPL-like histogram fill_between : MPL-like fill_between Internal Methods ---------------- updatePlotDataStyle : Make updates to plots (lines) when a stylistic \ change is made within the model-table updatePlotDataDelete : Remove a plot when deleted from model-table updateFillBetweenDataStyle : Make updates to fill between's when a \ stylistic change is made within the model-table updateFillBetweenDataDelete : Remove a fill between when deleted from \ model-table updateImagesDataStyle : Make updates to images when a stylistic \ change is made within the model-table updateImageDataDelete : Remove an image when deleted from model-table updateBarsDataStyle : Make updates to bars plots when a stylistic \ change is made within the model-table updateBarsDataDelete : Remove a bar plot when deleted from model-table refreshAllPlots : Delete all plots and re-plot updateAllLabels : Update all labels (x-, y-, title, etc) on MPL widget, \ in model, in data container, and in UI lineEdits updateLineEditLabels : Update all labels (x-, y-, title, etc) in UI \ lineEdits updateDataLabels : Update all labels (x-, y-, title, etc) in data \ container updateMplLabels : Update all labels (x-, y-, title, etc) on MPL widget updateLabelsFromLineEdit : Update all labels (x-, y-, title, etc) on MPL \ widget, in model, and in data container. Edits came from lineEdits. axisAspect : Set MPL-axis aspect ratio setting axisScaling : Set MPL-axis scaling ratio setting axisVisible : Set MPL-axis on or off axisLimits : Set MPL-axis limits updateAxisParameters : Query and update UI lineEdits related to axis \ properties such as limits, visibility (on/off), scaling, and aspect \ ratio Notes ----- * limit_to options: 'lines', 'fill betweens', 'bars', images' """ # Signal emitted when clearAll is called # Added for external programs all_cleared = _pyqtSignal(int) def __init__(self, limit_to=None, parent=None, show=True): self.__version__ = sciplot.__version__ self.list_ids = [] self.list_all = [] # There are a number of changes and deprectaion # in MPL v2; thus, this will be tracked # so MPL 1 and 2 can be used seemlessly self._mpl_v2 = int(_mpl.__version__.rsplit('.')[0]) == 2 # Check to see if QApp already exists # if not, one has to be created self.app = None if _QApplication.instance() is None: print('\nNo QApplication instance (this is common with certain \ version of Matplotlib). Creating one.\n\r\ You will need to exec manually after you finish plotting.\n\ -----------Example---------------\n\ import sciplot\n\ sp = sciplot.main()\n\n\ # Plot a line\n\ sp.plot((0,1),(0,1))\n\n\ # Start the QApplication\n\ sp.app.exec_()') self.app = _QApplication(_sys.argv) self.app.setQuitOnLastWindowClosed(True) self.setup(limit_to=limit_to, parent=parent) if show: self.show() # if app is not None: # # print('Here') # if app.exec_(): # print('Here') def closeEvent(self, event): pass def _tabAvailability(self, limit_to=None): """ If limit_to is provided, limits the tabs (elements) that are available. May be useful for built-upon applications. """ if limit_to is None: self.elements = ['lines', 'fill betweens', 'images', 'bars'] self._to_setup = [ self.setupLines, self.setupFillBetweens, self.setupImages, self.setupBars ] else: self._to_setup = [] self.elements = [] if limit_to.count('lines'): self.elements.append('lines') self._to_setup.append(self.setupLines) if limit_to.count('fill betweens'): self.elements.append('fill betweens') self._to_setup.append(self.setupFillBetweens) if limit_to.count('images'): self.elements.append('images') self._to_setup.append(self.setupImages) if limit_to.count('bars'): self.elements.append('bars') self._to_setup.append(self.setupBars) def setupLines(self): """ Enable and setup line plotting """ # Enable line plotting self.plot = self.__plot self.updatePlotDataStyle = self.__updatePlotDataStyle self.updatePlotDataDelete = self.__updatePlotDataDelete # Initial and insert table view for line plots self.tableViewLine = _QTableView() self.ui.modelTabWidget.addTab(self.tableViewLine, 'Lines') # Set model and delegates # Lines self.modelLine = _TableModelLines() self.delegateLine = _EditDelegateLines() self.tableViewLine.setModel(self.modelLine) self.tableViewLine.setItemDelegate(self.delegateLine) self.tableViewLine.show() # RESIZE COLUMNS header = self.tableViewLine.horizontalHeader() # alpha col = self.modelLine._COL_ALPHA new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewLine.setColumnWidth(col, new_width) # linewidth col = self.modelLine._COL_LINEWIDTH new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewLine.setColumnWidth(col, new_width) # markersize col = self.modelLine._COL_MARKERSIZE new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewLine.setColumnWidth(col, new_width) # delete col = self.modelLine._COL_DELETE new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewLine.setColumnWidth(col, new_width) # SIGNALS AND SLOTS # Make use of double-clicking within table self.tableViewLine.doubleClicked.connect( self.modelLine.doubleClickCheck) # When a model (table) elements changes or is deleted self.modelLine.dataChanged.connect(self.updatePlotDataStyle) self.modelLine.dataDeleted.connect(self.updatePlotDataDelete) # Export lines to csv self.ui.actionExport_Lines_to_CSV.setVisible(True) self.ui.actionExport_Lines_to_CSV.triggered.connect( self.export_lines_csv) def setupFillBetweens(self): """ Enable and setup fill between plotting """ # Enable fill_between plotting self.fill_between = self.__fill_between self.updateFillBetweenDataStyle = self.__updateFillBetweenDataStyle self.updateFillBetweenDataDelete = self.__updateFillBetweenDataDelete # Initial and insert table view for fill_between plots self.tableViewFillBetween = _QTableView() self.ui.modelTabWidget.addTab(self.tableViewFillBetween, 'Fill Between') # Fill Between self.modelFillBetween = _TableModelFillBetween() self.delegateFillBetween = _EditDelegateFillBetween() self.tableViewFillBetween.setModel(self.modelFillBetween) self.tableViewFillBetween.setItemDelegate(self.delegateFillBetween) self.tableViewFillBetween.show() # RESIZE COLUMNS header = self.tableViewFillBetween.horizontalHeader() # alpha col = self.modelFillBetween._COL_ALPHA new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewFillBetween.setColumnWidth(col, new_width) # linewidth col = self.modelFillBetween._COL_LINEWIDTH new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewFillBetween.setColumnWidth(col, new_width) # delete col = self.modelFillBetween._COL_DELETE new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewFillBetween.setColumnWidth(col, new_width) # SIGNALS AND SLOTS # Make use of double-clicking within table self.tableViewFillBetween.doubleClicked.connect( self.modelFillBetween.doubleClickCheck) # When a model (table) elements changes or is deleted self.modelFillBetween.dataChanged.connect( self.updateFillBetweenDataStyle) self.modelFillBetween.dataDeleted.connect( self.updateFillBetweenDataDelete) # Export fillbetweens to csv self.ui.actionExport_Fill_Between_to_CSV.setVisible(True) self.ui.actionExport_Fill_Between_to_CSV.triggered.connect( self.export_fillbetweens_csv) def setupImages(self): """ Enable and setup image plotting """ # Enable imaging self.imshow = self.__imshow self.updateImagesDataStyle = self.__updateImagesDataStyle self.updateImagesDataDelete = self.__updateImagesDataDelete # images data-- similar to plot_data above # Initial and insert table view for images self.tableViewImages = _QTableView() self.ui.modelTabWidget.addTab(self.tableViewImages, 'Images') # Images self.modelImages = _TableModelImages() self.delegateImages = _EditDelegateImages() self.tableViewImages.setModel(self.modelImages) self.tableViewImages.setItemDelegate(self.delegateImages) self.tableViewImages.show() # RESIZE COLUMNS header = self.tableViewImages.horizontalHeader() # alpha col = self.modelImages._COL_ALPHA new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewImages.setColumnWidth(col, new_width) # clim low col = self.modelImages._COL_CLIM_LOW new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewImages.setColumnWidth(col, new_width) # clim high col = self.modelImages._COL_CLIM_HIGH new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewImages.setColumnWidth(col, new_width) # delete col = self.modelImages._COL_DELETE new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewImages.setColumnWidth(col, new_width) # SIGNALS AND SLOTS # Make use of double-clicking within table self.tableViewImages.doubleClicked.connect( self.modelImages.doubleClickCheck) # When a model (table) elements changes or is deleted self.modelImages.dataChanged.connect(self.updateImagesDataStyle) self.modelImages.dataDeleted.connect(self.updateImagesDataDelete) def setupBars(self): """ Enable and setup bar and histogram plotting """ # Enable bar plotting self.bar = self.__bar self.hist = self.__hist self.updateBarsDataStyle = self.__updateBarsDataStyle self.updateBarsDataDelete = self.__updateBarsDataDelete # Initial and insert table view for bars self.tableViewBars = _QTableView() self.ui.modelTabWidget.addTab(self.tableViewBars, 'Bars') # Bars/Bars self.modelBars = _TableModelBars() self.delegateBars = _EditDelegateBars() self.tableViewBars.setModel(self.modelBars) self.tableViewBars.setItemDelegate(self.delegateBars) self.tableViewBars.show() # RESIZE COLUMNS header = self.tableViewBars.horizontalHeader() # alpha col = self.modelBars._COL_ALPHA new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewBars.setColumnWidth(col, new_width) # linewidth col = self.modelBars._COL_LINEWIDTH new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewBars.setColumnWidth(col, new_width) # widthfactor col = self.modelBars._COL_WIDTH_FACTOR new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewBars.setColumnWidth(col, new_width) # delete col = self.modelBars._COL_DELETE new_width = int(1.1 * header.sectionSizeHint(col)) self.tableViewBars.setColumnWidth(col, new_width) # SIGNALS AND SLOTS # Make use of double-clicking within table self.tableViewBars.doubleClicked.connect( self.modelBars.doubleClickCheck) # When a model (table) elements changes or is deleted self.modelBars.dataChanged.connect(self.updateBarsDataStyle) self.modelBars.dataDeleted.connect(self.updateBarsDataDelete) # Export bars to csv self.ui.actionExport_Bars_to_CSV.setVisible(True) self.ui.actionExport_Bars_to_CSV.triggered.connect( self.export_bars_csv) def setup(self, limit_to=None, parent=None): """ Basic UI setup """ # Generic start to any pyQT program super(SciPlotUI, self).__init__(parent) self.ui = Ui_Plotter() self.ui.setupUi(self) self.setSizePolicy(_QSizePolicy.Expanding, _QSizePolicy.Expanding) # Global "data" i.e., title, x-label, y-label, etc self._global_data = _DataGlobal() # MPL plot widget self.mpl_widget = _MplCanvas(height=6, dpi=100) # Hold is deprecated in MPL2 if not self._mpl_v2: self.mpl_widget.ax.hold(True) # Insert MPL widget and toolbar self.ui.verticalLayout.insertWidget(0, self.mpl_widget) self.ui.verticalLayout.insertWidget(0, self.mpl_widget.toolbar) self.updateAxisParameters() self.mpl_widget.draw() # Insert TabWidget self.ui.modelTabWidget = _QTabWidget() self.ui.verticalLayout.insertWidget(-1, self.ui.modelTabWidget) # Setup what tabs are available: self._tabAvailability(limit_to) for count in self._to_setup: count() # SIGNALS AND SLOTS # Global labels self.ui.lineEditTitle.editingFinished.connect( self.updateLabelsFromLineEdit) self.ui.lineEditXLabel.editingFinished.connect( self.updateLabelsFromLineEdit) self.ui.lineEditYLabel.editingFinished.connect( self.updateLabelsFromLineEdit) # Non-tracked (not saved) properties self.ui.comboBoxAspect.currentIndexChanged.connect(self.axisAspect) self.ui.comboBoxAxisScaling.currentIndexChanged.connect( self.axisScaling) self.ui.checkBoxAxisVisible.stateChanged.connect(self.axisVisible) self.ui.lineEditXLimMin.editingFinished.connect(self.axisLimits) self.ui.lineEditXLimMax.editingFinished.connect(self.axisLimits) self.ui.lineEditYLimMin.editingFinished.connect(self.axisLimits) self.ui.lineEditYLimMax.editingFinished.connect(self.axisLimits) # Actions self.ui.pushButtonClearAll.pressed.connect(self.clearAll) self.ui.pushButtonDefaultView.pressed.connect(self.defaultView) def __plot(self, x, y, label=None, x_label=None, y_label=None, meta={}, **kwargs): """ MPL-like plotting functionality Parameters ---------- x : ndarray (1D) X-axis data y : ndarray (1D, for now) Y-axis data label : str Label of plot x_label : str X-axis label (units) y_label : str Y-axis label (units) kwargs : dict Other parameters sent directly to mpl-plot """ # Temporary plot-data plot_data = _DataLine() plot_data.x = x plot_data.y = y plot_data.label = label plot_data.meta = meta plot_data.id = _time.time() # Plot outputs a line object plot_data.mplobj = self.mpl_widget.ax.plot(x, y, label=label, **kwargs) try: self.mpl_widget.ax.legend(loc='best') except: pass # If labels are provided, update the global data and the linEdits if x_label is not None or y_label is not None: self.updateAllLabels(x_label=x_label, y_label=y_label) self.mpl_widget.fig.tight_layout() self.axisAspect() self.mpl_widget.draw() # Since the plot was not fed style-info (unless kwargs were used) # we rely on the mpl stylesheet to setup color, linewidth, etc. # Thus, we plot, then retrieve what the style info was plot_data.retrieve_style_from_line(plot_data.mplobj[0]) # Append this specific plot data to out list of all plots self.list_ids.append(plot_data.id) self.list_all.append(plot_data) # Update model self.modelLine._model_data.append(plot_data.model_style) self.modelLine.layoutChanged.emit() def updateMplLabels(self, x_label=None, y_label=None, title=None): """ Within the MPL widget, update the x- and y-labels and the title """ if x_label is not None: self.mpl_widget.ax.set_xlabel(x_label) if y_label is not None: self.mpl_widget.ax.set_ylabel(y_label) if title is not None: self.mpl_widget.ax.set_title(title) self.mpl_widget.fig.tight_layout() self.mpl_widget.draw() def updateDataLabels(self, x_label=None, y_label=None, title=None): """ Within the global data container, update the x- and y-labels and the \ title """ if x_label is not None: self._global_data.labels['x_label'] = x_label if y_label is not None: self._global_data.labels['y_label'] = y_label if title is not None: self._global_data.labels['title'] = title def updateLineEditLabels(self, x_label=None, y_label=None, title=None): """ Within the pyQT lineEdit widgets, update the x- and y-labels and the \ title """ if x_label is not None: self.ui.lineEditXLabel.setText(x_label) if y_label is not None: self.ui.lineEditYLabel.setText(y_label) if title is not None: self.ui.lineEditTitle.setText(title) def updateAllLabels(self, x_label=None, y_label=None, title=None): """ Update the x- and y-labels and the title in the MPL widget, the \ lineEdit boxes, and the global data container """ self.updateMplLabels(x_label=x_label, y_label=y_label, title=title) self.updateDataLabels(x_label=x_label, y_label=y_label, title=title) self.updateLineEditLabels(x_label=x_label, y_label=y_label, title=title) def updateLabelsFromLineEdit(self): """ From the linEdit widgets, update the x- and y-labels and the title \ in the MPL widget and the global data container """ title = None x_label = None y_label = None sender = self.sender() if sender == self.ui.lineEditTitle: title = self.ui.lineEditTitle.text() elif sender == self.ui.lineEditXLabel: x_label = self.ui.lineEditXLabel.text() elif sender == self.ui.lineEditYLabel: y_label = self.ui.lineEditYLabel.text() self.updateDataLabels(x_label=x_label, y_label=y_label, title=title) self.updateMplLabels(x_label=x_label, y_label=y_label, title=title) def __updatePlotDataStyle(self): """ Something style-related changed in the model; thus, need to change \ these elements in the plot data """ for num, style_info in enumerate(self.modelLine._model_data): idx = self.list_ids.index(style_info['id']) self.list_all[idx].model_style = style_info self.refreshAllPlots() def __updatePlotDataDelete(self, row, plt_id): """ A plot was deleted (likely from within the model); thus, need to \ remove the corresponding plot data """ try: # print('Plot id: {}'.format(plt_id)) idx_to_remove = self.list_ids.index(plt_id) self.list_ids.pop(idx_to_remove) self.list_all.pop(idx_to_remove) except: print('Error in __updatePlotDataDelete: {}'.format(idx_to_remove)) self.refreshAllPlots() def refreshAllPlots(self): """ Clear and re-plot all plot data of all types """ # Clear axis -- in the future, maybe clear figure and recreate axis self.mpl_widget.ax.clear() for itm in self.list_all: if isinstance(itm, _DataLine): # print('Line') if not self._mpl_v2: self.mpl_widget.ax.hold(True) # Hide label if alpha=0 if itm.style_dict['alpha'] == 0: label = None else: label = itm.label itm.mplobj = self.mpl_widget.ax.plot( itm.x, itm.y, label=label, color=itm.style_dict['color'], alpha=itm.style_dict['alpha'], linewidth=itm.style_dict['linewidth'], linestyle=itm.style_dict['linestyle'], marker=itm.style_dict['marker'], markersize=itm.style_dict['markersize']) elif isinstance(itm, _DataBar): # print('Bar') if not self._mpl_v2: self.mpl_widget.ax.hold(True) # Hide label if alpha=0 if itm.style_dict['alpha'] == 0: label = None else: label = itm.label itm.mplobj = self.mpl_widget.ax.bar( itm._left, itm.y, bottom=itm.bottom, width=itm._width, label=label, facecolor=itm.style_dict['facecolor'], alpha=itm.style_dict['alpha'], edgecolor=itm.style_dict['edgecolor'], linewidth=itm.style_dict['linewidth']) elif isinstance(itm, _DataImages): # print('Images') if not self._mpl_v2: self.mpl_widget.ax.hold(True) # Hide label if alpha=0 if itm.style_dict['alpha'] == 0: label = None else: label = itm.label if itm.cbar['obj'] is not None: try: # Have had some unknown exceptions with .remove() itm.cbar['obj'].remove() itm.cbar['obj'] = None except: pass itm.mplobj = self.mpl_widget.ax.imshow( itm.img, label=label, interpolation='none', origin='lower', cmap=_mpl.cm.cmap_d[itm.style_dict['cmap_name']], alpha=itm.style_dict['alpha'], clim=itm.style_dict['clim']) if itm.cbar['show']: itm.cbar['obj'] = self.mpl_widget.fig.colorbar( itm.mplobj, use_gridspec=True) elif isinstance(itm, _DataFillBetween): # print('Fill Between') if not self._mpl_v2: self.mpl_widget.ax.hold(True) # Hide label if alpha=0 if itm.style_dict['alpha'] == 0: label = None else: label = itm.label itm.mplobj = self.mpl_widget.ax.fill_between( itm.x, itm.y_low, itm.y_high, label=label, facecolor=itm.style_dict['facecolor'], edgecolor=itm.style_dict['edgecolor'], alpha=itm.style_dict['alpha'], linewidth=itm.style_dict['linewidth']) else: print('Unknown') # Only add a legend if a plot exists # Only certain objects provide labels label_object_count = len(self.list_all) if label_object_count > 0: self.mpl_widget.ax.legend(loc='best') # Apply x- and y-labels and a title if they are set if self._global_data.labels['title'] is not None: self.mpl_widget.ax.set_title(self._global_data.labels['title']) if self._global_data.labels['x_label'] is not None: self.mpl_widget.ax.set_xlabel(self._global_data.labels['x_label']) if self._global_data.labels['y_label'] is not None: self.mpl_widget.ax.set_ylabel(self._global_data.labels['y_label']) self.mpl_widget.fig.tight_layout() self.updateAxisParameters() self.axisAspect() self.mpl_widget.draw() def __fill_between(self, x, y_low, y_high, label=None, meta={}, x_label=None, y_label=None, **kwargs): """ MPL-like fill_between plotting functionality Parameters ---------- x : ndarray (1D) X-axis data y_low : ndarray (1D, for now) Low Y-axis data y_high : ndarray (1D, for now) High Y-axis data label : str Label of plot x_label : str X-axis label (units) y_label : str Y-axis label (units) kwargs : dict Other parameters sent directly to mpl-fill_between """ # Temporary fill_between-data fill_between_data = _DataFillBetween() fill_between_data.x = x fill_between_data.y_low = y_low fill_between_data.y_high = y_high fill_between_data.label = label fill_between_data.meta = meta fill_between_data.id = _time.time() # Fill between outputs a polycollection fill_between_data.mplobj = self.mpl_widget.ax.fill_between(x, y_low, y_high, label=label, **kwargs) self.mpl_widget.ax.legend(loc='best') self.mpl_widget.fig.tight_layout() self.axisAspect() self.mpl_widget.draw() # Since the fill_between was not fed style-info (unless kwargs were used) # we rely on the mpl stylesheet to setup color, linewidth, etc. # Thus, we plot, then retrieve what the style info was fill_between_data.retrieve_style_from_polycollection( fill_between_data.mplobj) # Append this specific plot data to out list of all plots self.list_ids.append(fill_between_data.id) self.list_all.append(fill_between_data) # Update model self.modelFillBetween._model_data.append(fill_between_data.model_style) self.modelFillBetween.layoutChanged.emit() def __updateFillBetweenDataStyle(self): """ Something style-related changed in the model; thus, need to change \ these elements in the fill_between data """ for num, style_info in enumerate(self.modelFillBetween._model_data): idx = self.list_ids.index(style_info['id']) self.list_all[idx].model_style = style_info self.refreshAllPlots() def __updateFillBetweenDataDelete(self, row, plt_id): """ A plot was deleted (likely from within the model); thus, need to \ remove the corresponding plot data """ idx_to_remove = self.list_ids.index(plt_id) self.list_ids.pop(idx_to_remove) self.list_all.pop(idx_to_remove) self.refreshAllPlots() def __imshow(self, img, x=None, y=None, label=None, meta={}, x_label=None, y_label=None, cbar=False, **kwargs): """ MPL-like plotting functionality Parameters ---------- img : ndarray (2D) Image data x : ndarray (1D) X-axis data y : ndarray (1D, for now) Y-axis data label : str Label of plot x_label : str X-axis label (units) y_label : str Y-axis label (units) cbar : bool Attach a colorbar to the img kwargs : dict Other parameters sent directly to mpl-imshow """ # Temporary plot-data image_data = _DataImages() image_data.img = img image_data.x = x image_data.y = y image_data.label = label image_data.meta = meta image_data.id = _time.time() image_data.cbar['show'] = cbar # Imshow outputs an image object image_data.mplobj = self.mpl_widget.ax.imshow(img, interpolation='None', origin='lower', label=label, **kwargs) if image_data.cbar['show']: image_data.cbar['obj'] = self.mpl_widget.fig.colorbar( image_data.mplobj, use_gridspec=True) # self.mpl_widget.ax.legend(loc='best') # If labels are provided, update the global data and the linEdits if x_label is not None or y_label is not None: self.updateAllLabels(x_label=x_label, y_label=y_label) self.mpl_widget.fig.tight_layout() self.axisAspect() self.mpl_widget.draw() # Since the image was not fed style-info (unless kwargs were used) # we rely on the mpl stylesheet to setup cmap, etc. # Thus, we plot, then retrieve what the style info was image_data.retrieve_style_from_image(image_data.mplobj) # Append this specific plot data to out list of all plots self.list_ids.append(image_data.id) self.list_all.append(image_data) # Update model self.modelImages._model_data.append(image_data.model_style) self.modelImages.layoutChanged.emit() def __updateImagesDataStyle(self): """ Something style-related changed in the model; thus, need to change \ these elements in the fill_between data """ for num, style_info in enumerate(self.modelImages._model_data): idx = self.list_ids.index(style_info['id']) self.list_all[idx].model_style = style_info self.refreshAllPlots() def __updateImagesDataDelete(self, row, plt_id): """ A plot was deleted (likely from within the model); thus, need to \ remove the corresponding plot data """ idx_to_remove = self.list_ids.index(plt_id) self.list_ids.pop(idx_to_remove) popd = self.list_all.pop(idx_to_remove) if popd.cbar['obj'] is not None: popd.cbar['obj'].remove() # self.axisAspect() self.refreshAllPlots() def __bar(self, x, y, bottom=0, width_factor=1.0, use_real_width=False, label=None, meta={}, x_label=None, y_label=None, **kwargs): """ MPL-like plotting functionality Note ---- Unlike MPL bar, this method uses centered data. Thus, x is the center \ position of the bar Parameters ---------- x : ndarray (1D) X-axis data (center of bars) y : ndarray (1D, for now) Y-axis data (height) bottom : float (for now) Baseline of bars width_factor: float If legnth of y>1, fraction of space between bars taken up by bar \ (e.g. 1.0 leads to bars that tough). If y is a single-value OR \ use_real_width is True), is the width of the bar. use_real_width : bool, optional (default=False): If True, width_factor is the real width (in x-units) label : str Label of plot x_label : str X-axis label (units) y_label : str Y-axis label (units) kwargs : dict Other parameters sent directly to mpl-plot """ # Temporary plot-data bar_data = _DataBar() bar_data.x = x bar_data.y = y bar_data.bottom = bottom bar_data.label = label bar_data.meta = meta bar_data.id = _time.time() bar_data.style_dict['width_factor'] = width_factor _multi_value = None if isinstance(y, (int, float)): _multi_value = False if isinstance(y, _np.ndarray): if y.size == 1: _multi_value = False else: _multi_value = True if isinstance(y, (list, tuple)): if len(y) == 1: _multi_value = False else: _multi_value = True if _multi_value and use_real_width == False: # Distance between bars bar_data._gap = _np.abs(x[1] - x[0]) # Width of a bar is a fraction of the gap bar_data._width = bar_data._gap * bar_data.style_dict[ 'width_factor'] else: # Single-valued: no gap bar_data._gap = None bar_data._width = width_factor # MPL-bar uses left-edge rather than center bar_data._left = bar_data.x - bar_data._width / 2 # Plot outputs a list of patch objects bar_data.mplobj = self.mpl_widget.ax.bar(bar_data._left, y, bottom=bar_data.bottom, width=bar_data._width, label=label, **kwargs) self.mpl_widget.ax.legend(loc='best') # If labels are provided, update the global data and the linEdits if x_label is not None or y_label is not None: self.updateAllLabels(x_label=x_label, y_label=y_label) self.mpl_widget.fig.tight_layout() self.axisAspect() self.mpl_widget.draw() # Since the plot was not fed style-info (unless kwargs were used) # we rely on the mpl stylesheet to setup color, linewidth, etc. # Thus, we plot, then retrieve what the style info was bar_data.retrieve_style_from_bar(bar_data.mplobj[0]) # Append this specific plot data to out list of all plots self.list_ids.append(bar_data.id) self.list_all.append(bar_data) # Update model self.modelBars._model_data.append(bar_data.model_style) self.modelBars.layoutChanged.emit() # Note: New in MPL2, edgecolor is RGBA with A defaulting to 0 # (ie transparent, which Sciplot does not currently support). self.refreshAllPlots() def __hist(self, data, bins=10, label=None, meta={}, x_label=None, y_label='Counts', **kwargs): """ MPL-like histogram plotting Parameters ---------- data : ndarray (1D, for now) Data (center of bars) bins : int Number of histogram bins label : str Label of plot x_label : str X-axis label (units) y_label : str Y-axis label (units) kwargs : dict Other parameters sent directly to mpl-plot """ counts, lefts = _np.histogram(data, bins=bins) gap = _np.abs(lefts[1] - lefts[0]) offset = gap / 2 self.bar(lefts[:-1] + offset, counts, width_factor=1.0, label=label, x_label=x_label, y_label=y_label, meta=meta, **kwargs) def __updateBarsDataStyle(self): """ Something style-related changed in the model; thus, need to change \ these elements in the fill_between data """ for num, style_info in enumerate(self.modelBars._model_data): idx = self.list_ids.index(style_info['id']) self.list_all[idx].model_style = style_info self.refreshAllPlots() def __updateBarsDataDelete(self, row, plt_id): """ A plot was deleted (likely from within the model); thus, need to \ remove the corresponding plot data """ idx_to_remove = self.list_ids.index(plt_id) self.list_ids.pop(idx_to_remove) self.list_all.pop(idx_to_remove) self.refreshAllPlots() def axisAspect(self): """ Set axis aspect ratio property """ aspect = self.ui.comboBoxAspect.currentText() self.mpl_widget.ax.set_aspect(aspect) self.mpl_widget.fig.tight_layout() self.updateAxisParameters() self.mpl_widget.draw() def axisScaling(self): """ Set axis scaling property """ ratio = self.ui.comboBoxAxisScaling.currentText() self.mpl_widget.ax.axis(ratio) self.mpl_widget.fig.tight_layout() self.updateAxisParameters() self.mpl_widget.draw() def axisVisible(self): """ Set whether axis is on or off """ state = self.ui.checkBoxAxisVisible.isChecked() if state: state = 'on' else: state = 'off' self.mpl_widget.ax.axis(state) self.mpl_widget.fig.tight_layout() self.updateAxisParameters() self.mpl_widget.draw() def axisLimits(self): """ Set axis limits """ if self.sender() == self.ui.lineEditXLimMin: value = float(self.ui.lineEditXLimMin.text()) self.mpl_widget.ax.axis(xmin=value) elif self.sender() == self.ui.lineEditXLimMax: value = float(self.ui.lineEditXLimMax.text()) self.mpl_widget.ax.axis(xmax=value) elif self.sender() == self.ui.lineEditYLimMin: value = float(self.ui.lineEditYLimMin.text()) self.mpl_widget.ax.axis(ymin=value) elif self.sender() == self.ui.lineEditYLimMax: value = float(self.ui.lineEditYLimMax.text()) self.mpl_widget.ax.axis(ymax=value) self.mpl_widget.fig.tight_layout() self.updateAxisParameters() self.mpl_widget.draw() def updateAxisParameters(self): """ Query current state of axis settings and update appropriate lineEdit's """ axis_visible = self.mpl_widget.ax.axison self.ui.checkBoxAxisVisible.setChecked(axis_visible) xmin, xmax, ymin, ymax = self.mpl_widget.ax.axis() self.ui.lineEditXLimMin.setText(str(xmin)) self.ui.lineEditXLimMax.setText(str(xmax)) self.ui.lineEditYLimMin.setText(str(ymin)) self.ui.lineEditYLimMax.setText(str(ymax)) def defaultView(self): """ Set default and Home view to the current one """ self.mpl_widget.toolbar._views.clear() self.mpl_widget.toolbar._positions.clear() self.mpl_widget.toolbar.update() def clearAllBars(self): try: self.modelBars._model_data = [] ids = self.list_bar_ids for i in ids: self.clearID(i) self.modelBars.layoutChanged.emit() except: print('Error in clearAllBars') def clearID(self, clear_id): idx_to_remove = self.list_ids.index(clear_id) self.list_ids.pop(idx_to_remove) self.list_all.pop(idx_to_remove) def clearAll(self): """ Clear all plots and graphs and images """ try: self.modelLine._model_data = [] self.modelLine.layoutChanged.emit() except: print('Error in clear all of plots/lines') try: self.modelBars._model_data = [] self.modelBars.layoutChanged.emit() except: print('Error in clear all of bars') try: # Need to iterate as to check for colorbar existance for num, model_data in enumerate(self.modelImages._model_data): idx_to_remove = self.list_ids.index(model_data['id']) self.list_ids.pop(idx_to_remove) popd = self.list_all.pop(idx_to_remove) if popd.cbar['obj'] is not None: popd.cbar['obj'].remove() self.modelImages._model_data = [] self.modelImages.layoutChanged.emit() except: print('Error in clear all of images') try: self.modelFillBetween._model_data = [] self.modelFillBetween.layoutChanged.emit() except: print('Error in clear all of fill-betweens') try: self.list_ids = [] self.list_all = [] except: print('Error in clear all') finally: self.refreshAllPlots() self.all_cleared.emit(id(self)) def export_bars_csv(self): ret = _QFileDialog.getSaveFileName( filter="Comma-Separated Values (*.csv);;All Files (*.*)") if ret[0]: # pth, fname = _os.path.split(ret[0]) with open(ret[0], 'w') as f: for q in self.list_bar_objs: f.write('{}\n'.format(q.label)) f.write('left,') q._left.tofile(f, sep=',') f.write('\nx,') q.x.tofile(f, sep=',') f.write('\ny,') q.y.tofile(f, sep=',') f.write('\n\n') def export_lines_csv(self): ret = _QFileDialog.getSaveFileName( filter="Comma-Separated Values (*.csv);;All Files (*.*)") if ret[0]: # pth, fname = _os.path.split(ret[0]) with open(ret[0], 'w') as f: for q in self.list_line_objs: f.write('{}\n'.format(q.label)) f.write('x,') q.x.tofile(f, sep=',') f.write('\ny,') q.y.tofile(f, sep=',') f.write('\n\n') def export_fillbetweens_csv(self): ret = _QFileDialog.getSaveFileName( filter="Comma-Separated Values (*.csv);;All Files (*.*)") if ret[0]: # pth, fname = _os.path.split(ret[0]) with open(ret[0], 'w') as f: for q in self.list_fillbetween_objs: f.write('{}\n'.format(q.label)) f.write('x,') q.x.tofile(f, sep=',') f.write('\ny_low,') q.y_low.tofile(f, sep=',') f.write('\ny_high,') q.y_high.tofile(f, sep=',') f.write('\n\n') @property def n_lines(self): return sum(isinstance(x, _DataLine) for x in self.list_all) @property def n_bars(self): return sum(isinstance(x, _DataBar) for x in self.list_all) @property def n_fillbetweens(self): return sum(isinstance(x, _DataFillBetween) for x in self.list_all) @property def n_images(self): return sum(isinstance(x, _DataImages) for x in self.list_all) @property def list_line_objs(self): return [x for x in self.list_all if isinstance(x, _DataLine)] @property def list_line_ids(self): return [x.id for x in self.list_all if isinstance(x, _DataLine)] @property def list_bar_objs(self): return [x for x in self.list_all if isinstance(x, _DataBar)] @property def list_bar_ids(self): return [x.id for x in self.list_all if isinstance(x, _DataBar)] @property def list_fillbetween_objs(self): return [x for x in self.list_all if isinstance(x, _DataFillBetween)] @property def list_fillbetween_ids(self): return [x.id for x in self.list_all if isinstance(x, _DataFillBetween)] @property def list_image_objs(self): return [x for x in self.list_all if isinstance(x, _DataImages)] @property def list_image_ids(self): return [x.id for x in self.list_all if isinstance(x, _DataImages)]
class TableModelBars(_AbstractTableModelMpl): _HEADERS = [ 'Facecolor', 'Alpha', 'Edgecolor', 'Line Width', 'Width Factor', 'Label', 'Delete' ] _COL_FACECOLOR = _HEADERS.index('Facecolor') _COL_ALPHA = _HEADERS.index('Alpha') _COL_EDGECOLOR = _HEADERS.index('Edgecolor') _COL_LINEWIDTH = _HEADERS.index('Line Width') _COL_WIDTH_FACTOR = _HEADERS.index('Width Factor') _COL_LABEL = _HEADERS.index('Label') _COL_DELETE = _HEADERS.index('Delete') # Emit row, plot ID dataDeleted = _pyqtSignal(int, float) def __init__(self, parent=None): super(_QAbstractTableModel, self).__init__(parent) self.headers = TableModelBars._HEADERS self._model_data = [] def rowCount(self, parent=_QModelIndex()): return len(self._model_data) def columnCount(self, parent=_QModelIndex()): return len(self.headers) def headerData(self, col, orientation, role): if orientation == _Qt.Horizontal and role == _Qt.DisplayRole: return self.headers[col] return _QVariant() def doubleClickCheck(self, index): col = index.column() if (col == TableModelBars._COL_FACECOLOR or col == TableModelBars._COL_EDGECOLOR): # Colors self.changeColor(index) elif col == TableModelBars._COL_DELETE: # Delete? self.deleteData(index) def deleteData(self, index): self.setData(index, True) def changeColor(self, index): row = index.row() col = index.column() if col == TableModelBars._COL_FACECOLOR: color = self._model_data[row]['facecolor'] elif col == TableModelBars._COL_EDGECOLOR: color = self._model_data[row]['edgecolor'] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) result = _QColorDialog.getColor(qcolor) if _QColor.isValid(result): self.setData(index, result.getRgb()) else: return None def data(self, index, role=_Qt.DisplayRole): if not index.isValid() or not 0 <= index.row() < self.rowCount(): return _QVariant() row = index.row() col = index.column() if role == _Qt.DisplayRole: if col == TableModelBars._COL_FACECOLOR: return str(round_list(self._model_data[row]['facecolor'])) elif col == TableModelBars._COL_ALPHA: return str(self._model_data[row]['alpha']) elif col == TableModelBars._COL_EDGECOLOR: return str(round_list(self._model_data[row]['edgecolor'])) elif col == TableModelBars._COL_LINEWIDTH: return str(self._model_data[row]['linewidth']) elif col == TableModelBars._COL_WIDTH_FACTOR: return str(self._model_data[row]['width_factor']) elif col == TableModelBars._COL_LABEL: return str(self._model_data[row]['label']) elif col == TableModelBars._COL_DELETE: return '' elif role == _Qt.DecorationRole: if col == TableModelBars._COL_FACECOLOR: color = self._model_data[row]['facecolor'] elif col == TableModelBars._COL_EDGECOLOR: color = self._model_data[row]['edgecolor'] if (col == TableModelBars._COL_FACECOLOR or col == TableModelBars._COL_EDGECOLOR): color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon elif col == TableModelBars._COL_DELETE: color = [1, 0, 0] color_256 = [color[0] * 255, color[1] * 255, color[2] * 255] qcolor = _QColor(color_256[0], color_256[1], color_256[2]) pm = _QPixmap(20, 20) pm.fill(qcolor) icon = _QIcon(pm) return icon else: pass else: return _QVariant() def setData(self, index, value, role=_Qt.EditRole): if role == _Qt.EditRole: row = index.row() col = index.column() if (col == TableModelBars._COL_FACECOLOR or col == TableModelBars._COL_EDGECOLOR): color_255 = value[0:-1] color = [ round(color_255[0] / 255, 2), round(color_255[1] / 255, 2), round(color_255[2] / 255, 2) ] if col == TableModelBars._COL_FACECOLOR: self._model_data[row]['facecolor'] = color if col == TableModelBars._COL_EDGECOLOR: self._model_data[row]['edgecolor'] = color elif col == TableModelBars._COL_ALPHA: self._model_data[row]['alpha'] = float(value) elif col == TableModelBars._COL_LINEWIDTH: self._model_data[row]['linewidth'] = float(value) elif col == TableModelBars._COL_WIDTH_FACTOR: self._model_data[row]['width_factor'] = float(value) elif col == TableModelBars._COL_LABEL: self._model_data[row]['label'] = value elif col == TableModelBars._COL_DELETE: if value: out = self._model_data.pop(row) self.layoutChanged.emit() self.dataDeleted.emit(row, out['id']) self.dataChanged.emit(index, index) def flags(self, index): flag = super(_QAbstractTableModel, self).flags(index) return flag | _Qt.ItemIsEditable