def _createLabels(self, points, viewbox, logMode): for x, y in points: # fill the text xtext = self._getXText(x) ytext = self._getYText(y) text_item = TextItem() text_item.setHtml(("<div style='{}'> " + "<span><b>x=</b>{} " + "<span><b>y=</b>{}</span> " + "</div>").format( self._label_style, xtext, ytext)) # add text_item in the right position (take into account log mode) if logMode[0]: x = numpy.log10(x) if logMode[1]: y = numpy.log10(y) text_item.setPos(x, y) self._labels.append(text_item) viewbox.addItem(text_item, ignoreBounds=True) # Add "highlight" marker at each point highlight = PlotDataItem( numpy.array(points), pen=None, symbol="s", symbolBrush="35393C88", pxMode=True, symbolSize=self.trigger_point_size, ) # set log mode highlight.setLogMode(*logMode) # hack to make the CurvesPropertiesTool ignore the highlight points highlight._UImodifiable = False # Add it to the vbox and keep a reference viewbox.addItem(highlight, ignoreBounds=True) self._highlights.append(highlight)
def setData(self, **kwds): self.data = kwds if 'pos' in self.data: for n in range(kwds['pos'].shape[0]): text_item = TextItem(kwds['text'][n]) text_pos = (kwds['pos'][n][0], kwds['pos'][n][1]) text_item.setPos(*kwds['pos'][n]) text_item.setParentItem(self) self.text = kwds.pop('text', []) GraphItem.setData(self, **self.data)
def update(self, x, y, colors, cutpoint_x=None, selection_limit=None, names=None): """ Function replots a graph. Parameters ---------- x : np.ndarray One-dimensional array with X coordinates of the points y : array-like List of np.ndarrays that contains an array of Y values for each sequence. colors : array-like List of Qt colors (eg. Qt.red) for each sequence. cutpoint_x : int, optional A starting cutpoint - the location of the vertical line. selection_limit : tuple The tuple of two values that limit the range for selection. names : array-like The name of each sequence that shows in the legend, if None legend is not shown. legend_anchor : array-like The anchor of the legend in the graph """ self.clear_plot() if names is None: names = [None] * len(y) self.sequences = y self.x = x self.selection_limit = selection_limit self.data_increasing = [np.sum(d[1:] - d[:-1]) > 0 for d in y] # plot sequence for s, c, n, inc in zip(y, colors, names, self.data_increasing): c = QColor(c) self.plot(x, s, pen=mkPen(c, width=2), antialias=True) if n is not None: label = TextItem(text=n, anchor=(0, 1), color=QColor(0, 0, 0, 128)) label.setPos(x[-1], s[-1]) self._set_anchor(label, len(x) - 1, inc) self.addItem(label) self._plot_cutpoint(cutpoint_x) self.autoRange()
def calcPartitionNamePlacement(self, label: pg.TextItem, index: int, emit_signal=False): start_point = self.segments[index].getXPos() end_point = self.segments[index + 1].getXPos() mid_point = start_point + ((end_point - start_point) / 2) label.setPos(mid_point, self.vertical_placement) label.updateTextPos() if emit_signal: self.item.updatePartitionValuePosition.emit(index, mid_point)
def _createLabels(self, points): for x, y in points: # create label at x,y _x = self._getXValue(x) _y = self._getYValue(y) text_item = TextItem() text_item.setPos(x, y) text_item.setHtml(("<div style='{}'> " + "<span><b>x=</b>{} " + "<span><b>y=</b>{}</span> " + "</div>").format( self._label_style, _x, _y)) self._labels.append(text_item) self._plot_item.addItem(text_item, ignoreBounds=True) # Add "highlight" marker at each point self._highlights.setData(pos=points)
def __init__(self, viewBox, el_net): self.arrows = [] self.arrow_labels = [] self.line_flows = relative_line_load(el_net) for l in self.line_flows: arrow = ArrowItem(angle = l['angle'], tipAngle = 40, headLen= 10, tailLen=0, pen={'color': 'w', 'width': 1}, brush = 'y', pxMode = True) arrow.setPos(*l['pos']) arrow_label = TextItem("{0:.0f}%".format(l['rel_load'])) arrow_label.setPos(*l['pos']) viewBox.addItem(arrow) viewBox.addItem(arrow_label) self.arrows.append(arrow) self.arrow_labels.append(arrow_label)
def update(self): self.p5main.clear() self.p5main.addItem(self.pointer) self.pointer.setData( pos=np.array([[glo_var.alpha, glo_var.beta]], dtype=float)) self.pointer.legend(glo_var.alpha, glo_var.beta) self.value_declaration() if glo_var.alpha > 2 * glo_var.alpha_star: glo_var.alpha = 2 * glo_var.alpha_star if glo_var.beta > 2 * glo_var.beta_star: glo_var.beta = 2 * glo_var.beta_star HD = TextItem(html='HD', anchor=(glo_var.alpha_star, 0.5 * glo_var.beta_star), border='w', fill=(255, 0, 0, 250)) HD.setPos(glo_var.alpha_star, 0.5 * glo_var.beta_star) LD = TextItem(html='LD', anchor=(glo_var.alpha_star * 0.3, glo_var.beta_star * 1.3), border='w', fill=(0, 255, 0, 200)) LD.setPos(glo_var.alpha_star * 0.3, glo_var.beta_star * 1.3) MC = TextItem(html='MC', anchor=(glo_var.alpha_star * 1.2, glo_var.beta_star * 1.3), border='w', fill=(0, 0, 255, 200)) MC.setPos(glo_var.alpha_star * 1.2, glo_var.beta_star * 1.3) self.bounds1 = np.array([[glo_var.alpha_star, glo_var.beta_star], [1, glo_var.beta_star]]) self.bounds2 = np.array([[glo_var.alpha_star, glo_var.beta_star], [glo_var.alpha_star, 1]]) linspace = np.linspace(0, glo_var.alpha_star, 20) trans_line_val = [] for i in linspace: trans_line_val += [self.trans_func(i)] self.p5main.plot(self.bounds1, pen='k') self.p5main.plot(self.bounds2, pen='k') self.p5main.plot(linspace, trans_line_val, pen='k') self.set_range()
class verticalScaleBar(GraphicsObject, GraphicsWidgetAnchor): """ Displays a rectangular bar to indicate the relative scale of objects on the view. """ def __init__(self, size, width=5, brush=None, pen=None, suffix='m', offset=None): GraphicsObject.__init__(self) GraphicsWidgetAnchor.__init__(self) self.setFlag(self.ItemHasNoContents) self.setAcceptedMouseButtons(QtCore.Qt.NoButton) if brush is None: brush = getConfigOption('foreground') self.brush = fn.mkBrush(brush) self.pen = fn.mkPen(pen) self._width = width self.size = size if offset == None: offset = (0, 0) self.offset = offset self.bar = QtGui.QGraphicsRectItem() self.bar.setPen(self.pen) self.bar.setBrush(self.brush) self.bar.setParentItem(self) self.text = TextItem(text=fn.siFormat(size, suffix=suffix), anchor=(0.5, 1), angle=90, color=(0, 0, 0)) self.text.setParentItem(self) def parentChanged(self): view = self.parentItem() if view is None: return view.sigRangeChanged.connect(self.updateBar) self.updateBar() def updateBar(self): view = self.parentItem() if view is None: return p1 = view.mapFromViewToItem(self, QtCore.QPointF(0, 0)) p2 = view.mapFromViewToItem(self, QtCore.QPointF(0, self.size)) w = (p2 - p1).y() self.bar.setRect(QtCore.QRectF(0, 0, self._width, w)) self.text.setPos(0, w / 2.) def boundingRect(self): return QtCore.QRectF() def setParentItem(self, p): ret = GraphicsObject.setParentItem(self, p) if self.offset is not None: offset = Point(self.offset) anchorx = 1 if offset[0] <= 0 else 0 anchory = 1 if offset[1] <= 0 else 0 anchor = (anchorx, anchory) self.anchor(itemPos=anchor, parentPos=anchor, offset=offset) return ret
class baselinePlotWidget(SpectraPlotWidget): def __init__(self): super(baselinePlotWidget, self).__init__() self.line.setValue(800) self.txt = TextItem('', anchor=(0, 0)) self.cross = PlotDataItem([800], [0], symbolBrush=(255, 255, 0), symbolPen=(255, 255, 0), symbol='+',symbolSize=20) self.line.sigPositionChanged.connect(self.getMu) self._mu = None def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = y[idx] if not self._meanSpec: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ Spectrum #{self.spectrumInd}</div>' else: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ {self._mean_title}</div>' txt_html += f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt = TextItem(html=txt_html, anchor=(0, 0)) ymax = np.max(y) self._y = y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], 0.95 * ymax) self.cross.setData([x_val], [y_val]) self.addItem(self.txt) def getMu(self): if self._mu is not None: x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self._x) x_val = self._x[idx] y_val = self._mu[idx] txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def addDataCursor(self, x, y): self.addItem(self.line) self.addItem(self.cross) ymax = np.max(y) self.txt.setText('') r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], 0.95 * ymax) self.addItem(self.txt) self.getMu() def plotBase(self, dataGroup, plotType='raw'): """ make plots for Larch Group object :param dataGroup: Larch Group object :return: """ # add legend self.plotItem.addLegend(offset=(-1, -1)) x = self._x = dataGroup.energy # self._x, self._mu for getEnergy y = self._mu = dataGroup.specTrim n = len(x) # array length self._y = None # disable getEnergy func if plotType == 'raw': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) elif plotType == 'rubber_base': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) self.plotItem.plot(x, dataGroup.rubberBaseline, name='Rubberband baseline', pen=mkPen('g', style=Qt.DotLine, width=2)) self.plotItem.plot(dataGroup.wav_anchor, dataGroup.spec_anchor, symbol='o', symbolPen='r', symbolBrush=0.5) elif plotType == 'kohler_base': self.plotItem.plot(x, y, name='Raw', pen=mkPen('w', width=2)) self.plotItem.plot(x, dataGroup.kohlerBaseline, name='Kohler EMSC baseline', pen=mkPen('g', style=Qt.DotLine, width=2)) elif plotType == 'rubberband': y = self._mu = dataGroup.rubberDebased self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) elif plotType == 'kohler': y = self._mu = dataGroup.kohlerDebased self.plotItem.plot(x, y, name='Kohler EMSC debased', pen=mkPen('r', width=2)) elif plotType == 'deriv2_rubberband': y = self._mu = dataGroup.rubberDebased # for data cursor scale, offset = self.alignTwoCurve(dataGroup.rubberDebased[n//4:n*3//4], dataGroup.deriv2_rubber[n//4:n*3//4]) deriv2Scaled = dataGroup.deriv2_rubber * scale + offset ymin, ymax = np.min(y), np.max(y) self.getViewBox().setYRange(ymin, ymax, padding=0.1) self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) self.plotItem.plot(x, deriv2Scaled, name='2nd derivative (scaled, Rubberband)', pen=mkPen('g', width=2)) elif plotType == 'deriv2_kohler': y = self._mu = dataGroup.kohlerDebased scale, offset = self.alignTwoCurve(dataGroup.kohlerDebased[n//4:n*3//4], dataGroup.deriv2_kohler[n//4:n*3//4]) deriv2Scaled = dataGroup.deriv2_kohler * scale + offset ymin, ymax = np.min(y), np.max(y) self.getViewBox().setYRange(ymin, ymax, padding=0.1) self.plotItem.plot(x, y, name='Rubberband debased', pen=mkPen('r', width=2)) self.plotItem.plot(x, deriv2Scaled, name='2nd derivative (scaled, Kohler)', pen=mkPen('g', width=2)) # add infinityline, cross self.addDataCursor(x, y) def alignTwoCurve(self, y1, y2): """ Align the scale of y2 to that of y1 :param y1: the main curve :param y2: the curve to be aligned :return: scale: scale factor offset: y offset """ y1Range, y2Range = np.max(y1) - np.min(y1), np.max(y2) - np.min(y2) scale = y1Range / y2Range y = y2 * scale offset = np.max(y1) - np.max(y) return scale, offset
class SpectraPlotWidget(PlotWidget): sigEnergyChanged = Signal(object) def __init__(self, linePos=650, txtPosRatio=0.35, invertX=True, *args, **kwargs): """ A widget to display a 1D spectrum :param linePos: the initial position of the InfiniteLine :param txtPosRatio: a coefficient that determines the relative position of the textItem :param invertX: whether to invert X-axis """ super(SpectraPlotWidget, self).__init__(*args, **kwargs) self._data = None assert (txtPosRatio >= 0) and (txtPosRatio <= 1), 'Please set txtPosRatio value between 0 and 1.' self.txtPosRatio = txtPosRatio self.positionmap = dict() self.wavenumbers = None self._meanSpec = True # whether current spectrum is a mean spectrum self.line = InfiniteLine(movable=True) self.line.setPen((255, 255, 0, 200)) self.line.setValue(linePos) self.line.sigPositionChanged.connect(self.sigEnergyChanged) self.line.sigPositionChanged.connect(self.getEnergy) self.addItem(self.line) self.cross = PlotDataItem([linePos], [0], symbolBrush=(255, 0, 0), symbolPen=(255, 0, 0), symbol='+', symbolSize=20) self.cross.setZValue(100) self.addItem(self.cross) self.txt = TextItem() self.getViewBox().invertX(invertX) self.spectrumInd = 0 self.selectedPixels = None self._y = None def getEnergy(self, lineobject): if self._y is not None: x_val = lineobject.value() idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = self._y[idx] if not self._meanSpec: txt_html = toHtml(f'Spectrum #{self.spectrumInd}') else: txt_html = toHtml(f'{self._mean_title}') txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field # get wavenumbers spectraEvent = next(header.events(fields=['spectra'])) self.wavenumbers = spectraEvent['wavenumbers'] self.N_w = len(self.wavenumbers) self.rc2ind = spectraEvent['rc_index'] # make lazy array from document data = None try: data = header.meta_array(field) except IndexError: msg.logMessage(f'Header object contained no frames with field {field}.', msg.ERROR) if data is not None: # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2]) self._data = data def showSpectra(self, i=0): if (self._data is not None) and (i < len(self._data)): self.getViewBox().clear() self._meanSpec = False self.spectrumInd = i self.plot(self.wavenumbers, self._data[i]) def getSelectedPixels(self, selectedPixels): self.selectedPixels = selectedPixels # print(selectedPixels) def clearAll(self): # remove legend _legend = self.plotItem.legend if (_legend is not None) and (_legend.scene() is not None): _legend.scene().removeItem(_legend) self.getViewBox().clear() def showMeanSpectra(self): self._meanSpec = True self.getViewBox().clear() if self.selectedPixels is not None: n_spectra = len(self.selectedPixels) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): # j: jth selected pixel row_col = tuple(self.selectedPixels[j]) tmp[j, :] = self._data[self.rc2ind[row_col]] self._mean_title = f'ROI mean of {n_spectra} spectra' else: n_spectra = len(self._data) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): tmp[j, :] = self._data[j] self._mean_title = f'Total mean of {n_spectra} spectra' if n_spectra > 0: meanSpec = np.mean(tmp, axis=0) else: meanSpec = np.zeros_like(self.wavenumbers) + 1e-3 self.plot(self.wavenumbers, meanSpec) def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = y[idx] if not self._meanSpec: txt_html = toHtml(f'Spectrum #{self.spectrumInd}') else: txt_html = toHtml(f'{self._mean_title}') txt_html += toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt.setHtml(txt_html) ymax = np.max(y) self._y = y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], ymax) self.cross.setData([x_val], [y_val]) self.addItem(self.txt)
class jalpha: def __init__(self, dalpha, rh): self.dalpha = dalpha self.p3main = glo_var.MyPW(x="\u03b1",y1="J",y2= "\u27e8\u2374\u27e9", set_range = self.set_range) # self.p3main._rescale = self.set_range self.p3 = self.p3main.plotItem self.rh=rh self.na = "ha" self.p3.setLabel('bottom',"\u03b1",**glo_var.labelstyle) self.p3.setLabel('left',"J",**glo_var.labelstyle) self.p3.setLabel('right',"\u27e8\u2374\u27e9",**glo_var.labelstyle) self.p3.addLegend = glo_var.myaddLegend self.p3.addLegend(self.p3, offset = (20,20)) # to use it as coordinate label self.p3main.tempplotitem = PlotItem() # self.p3main.set_range = self.set_range self.p3_2 = self.p3main.tempplotitem.vb self.p3.showAxis('right') self.p3.scene().addItem(self.p3_2) self.p3.getAxis('right').linkToView(self.p3_2) self.p3_2.setXLink(self.p3) self.p3_2.setBackgroundColor('w') self.rho_dash = mkPen(color=(16,52,166),width=glo_var.line_width,style=QtCore.Qt.DashLine) self.dash = mkPen('r',width=glo_var.line_width ,style=QtCore.Qt.DashLine) self.jpen = mkPen('k',width=glo_var.line_width) self.alpha_pen = mkPen('k',width = glo_var.line_width) self.p3main.coordinate_label = QtGui.QLabel() self.frame = glo_var.setframe(self.p3main, width = 1, coordinate_label = self.p3main.coordinate_label) self.dalpha.addWidget(self.frame) self.p3.vb.setLimits(xMin = 0, yMin = 0, xMax = 1, yMax = 1) self.p3_2.setLimits(xMin = 0, yMin = 0, xMax = 1, yMax = 1) self.p3.vb.sigResized.connect(self.updateview) self.update() self.legend() def set_range(self): self.uplim1 = min(self.jpost * 1.3, 1) self.lolim1 = 0 self.uplim2 = min(1, max(max(self.rho_avg_pre)*1.4,max(self.rho_avg_post)*1.4)) self.lolim2 = max(0, min(min(self.rho_avg_pre)*0.6,min(self.rho_avg_post)*0.6)) self.p3.vb.setRange(xRange=[0,3*self.trans_point],yRange=[self.lolim1,self.uplim1],padding =0) self.p3_2.setRange(xRange=[0,3*self.trans_point],yRange=[self.lolim2,self.uplim2], padding = 0) def checkbox(self): self.alphline = QtGui.QCheckBox('\u03B1 line') self.alphline.stateChanged.connect(self.alphstate) self.alproxy=QtGui.QGraphicsProxyWidget() self.alproxy.setWidget(self.alphline) self.p3_w = self.win.addLayout(row=2,col=0) self.p3_w.addItem(self.alproxy,row=0,col=1) def update(self): self.p3.clear() self.p3_2.clear() self.value_declaration() self.vlim = 1/pow(1+sqrt(glo_var.l),2) self.trans_point = self.trans_func(glo_var.beta) self.alphas_pre = np.linspace(0,self.trans_point - 0.000001,20) # explain here in the meeting # self.alphas_to_add = np.array([self.trans_point-0.000001, self.trans_point, self.trans_point+0.000001]) # self.alphas_pre = np.concatenate((self.alphas_pre_pre[:-1],self.alphas_to_add)) self.alphas_post=np.array([self.trans_point+ 0.000001, 1]) self.j_l_values=np.array([i*(self.lambda_0-i)/(self.lambda_0 + (glo_var.l-1)*i) for i in self.alphas_pre]) self.rho_avg_pre = [] for i in self.alphas_pre: self.rho_avg_pre += [self.cal_rho(self.js(i,glo_var.beta))] self.rho_avg_post = [] for i in self.alphas_post: self.rho_avg_post += [self.cal_rho(self.js(i,glo_var.beta))] self.num=30 self.xs =np.linspace(0,self.trans_point, self.num) # minused 0.00000001 since it is not working self.p3.plot(self.alphas_pre,self.j_l_values, pen = self.jpen) # Can alpha_star be 0? then I need to add conner case if glo_var.beta >= glo_var.beta_star: self.jpost= self.j_c else: self.jpost= self.j_r self.p3.plot([self.trans_point,1],[self.jpost,self.jpost],pen = self.jpen) self.trans_line = self.p3.plot([self.trans_point,self.trans_point],[0,1],pen=self.dash) if self.alphacheck == 1: self.text = TextItem(html='<span style="color: #1034A6; font-size: 16pt;">\u03b1</span></div>', anchor=(0.5,1.5)) self.p3.addItem(self.text) self.arrow = ArrowItem(pos=(glo_var.alpha,0),angle=-90) self.p3.addItem(self.arrow) self.alphacheck=0 self.text.setPos(glo_var.alpha,0) self.arrow.setPos(glo_var.alpha,0) self.make_right_axis() self.set_range() if self.jpost < 0.1: self.set_range() def legend(self): self.p3.plot(pen=self.jpen, name='J') self.p3.plot(pen=self.rho_dash, name='\u27e8\u2374\u27e9') def updateview(self): self.p3_2.setGeometry(self.p3.vb.sceneBoundingRect()) self.p3_2.linkedViewChanged(self.p3.vb, self.p3_2.XAxis) def make_right_axis(self): self.p3_2.addItem(PlotCurveItem(self.alphas_pre,self.rho_avg_pre,pen=self.rho_dash)) self.p3_2.addItem(PlotCurveItem(self.alphas_post,self.rho_avg_post,pen=self.rho_dash)) def trans_func(self, point): if point >= glo_var.beta_star: return glo_var.alpha_star self.B = point*(self.lambda_1 - point)/(self.lambda_1 + (glo_var.l -1) * point) self.trans_b = - self.lambda_0 +(glo_var.l-1)*self.B self.trans_intercal = 0 if pow(self.trans_b,2) - 4*self.B*self.lambda_0 < 0.00001 else sqrt(pow(self.trans_b,2) - 4*self.B*self.lambda_0) self.trans = (-self.trans_b - self.trans_intercal)/2 return self.trans def cal_rho(self,jval): self.xperlambdas = round(150/glo_var.lambdas_degree) self.rhointercal=[] self.rho_l = [] self.rho_r = [] for lambda_x in self.rh.lambdas_yval: if lambda_x !=0: self.intercal1 = 1/(2*self.l) + jval*(self.l-1)/(2*self.l*lambda_x) self.intercal2 = pow(1/(2*self.l) + jval*(self.l-1)/(2*self.l*lambda_x),2) - jval/(self.l*lambda_x) self.rhointercal+=[(self.intercal1,self.intercal2)] else: print('lambda_x cannot be 0') for x,y in self.rhointercal: self.inter_y=sqrt(0 if y < 0.000001 else y) self.rho_l += [x - self.inter_y] self.rho_r += [x + self.inter_y] self.plot_scat(self.rh.scat_step) return sum(self.scat_ys)/len(self.scat_ys) def check_two_mins(self): self.minlocation = [] self.maxlocation = [] counter=0 for i in self.lambdas_ys: if i == self.lambda_min: self.minlocation+=[counter] counter += 1 num=len(self.minlocation) if num > 1: for j in range(num - 1): val = max(self.lambdas_ys[self.minlocation[j]:self.minlocation[j+1]]) self.maxlocation += [self.lambdas_ys.index(val,self.minlocation[j])] return num def plot_scat(self,steps): self.num_mins = self.check_two_mins() self.scat_ys = [] self.scat_xs = [] if self.region == 3: if self.num_mins > 1: self.index1 = self.minlocation[0]*self.xperlambdas self.scat_xs += self.getscatarray(self.rh.lambdas_xval[:self.index1],steps) self.scat_ys += self.getscatarray(self.rho_r[:self.index1],steps) for i in range(1, self.num_mins): self.index1 = self.minlocation[i - 1]*self.xperlambdas self.index2 = self.minlocation[i]*self.xperlambdas self.indexmax = self.maxlocation[i - 1]*self.xperlambdas self.scat_xs += self.getscatarray(self.rh.lambdas_xval[self.index1:self.indexmax],steps) self.scat_ys += self.getscatarray(self.rho_l[self.index1:self.indexmax],steps) self.scat_xs += self.getscatarray(self.rh.lambdas_xval[self.indexmax:self.index2],steps) self.scat_ys += self.getscatarray(self.rho_r[self.indexmax:self.index2],steps) self.scat_xs += self.getscatarray(self.rh.lambdas_xval[self.index2:],steps) self.scat_ys += self.getscatarray(self.rho_l[self.index2:],steps) else : self.index = self.minlocation[0]*self.xperlambdas self.scat_xs += self.getscatarray(self.rh.lambdas_xval[:self.index],steps) + self.getscatarray(self.rh.lambdas_xval[self.index:],steps) self.scat_ys += self.getscatarray(self.rho_r[:self.index],steps) + self.getscatarray(self.rho_l[self.index:],steps) elif self.region == 2: self.scat_xs = self.getscatarray(self.rh.lambdas_xval,steps) self.scat_ys = self.getscatarray(self.rho_r,steps) else: self.scat_xs = self.getscatarray(self.rh.lambdas_xval,steps) self.scat_ys = self.getscatarray(self.rho_l,steps) def getscatarray(self,array,step): return array[::step] def js(self, alpha, beta): # LD 1, HD 2, MC 3 if beta >= self.beta_star: if alpha <= self.alpha_star: self.region = 1 return alpha*(self.lambda_0-alpha)/(self.lambda_0+(self.l-1)*alpha) else : self.region = 3 return self.lambda_min/pow((1+sqrt(self.l)),2) elif beta < self.beta_star: if alpha < self.alpha_star: self.jl = alpha*(self.lambda_0-alpha)/(self.lambda_0+(self.l-1)*alpha) self.jr = beta*(self.lambda_1-beta)/(self.lambda_1+(self.l-1)*beta) if self.jl <= self.jr: self.region = 1 return self.jl else : self.region = 2 return self.jr else : self.region = 2 return beta*(self.lambda_1-beta)/(self.lambda_1+(self.l-1)*beta) def value_declaration(self): self.lambdas_xs, self.lambdas_ys = zip(*sorted(glo_var.lambdas)) self.lambda_min = min(self.lambdas_ys) self.lambda_0 = glo_var.lambdas[0][1] self.lambda_1 = glo_var.lambdas[-1][1] self.j_c = self.lambda_min/pow(1 + sqrt(glo_var.l),2) self.j_r = glo_var.beta*(self.lambda_1-glo_var.beta)/(self.lambda_1 + (glo_var.l-1)*glo_var.beta) self.alpha_star = glo_var.alpha_star self.beta_star = glo_var.beta_star self.alpha=glo_var.alpha self.beta=glo_var.beta self.l=glo_var.l self.alphacheck = 1 self.transcheck = 1 def plot_sum_rho(self): self.basic_1 = 1/(2*glo_var.l) self.basic_2 = (glo_var.l - 1)/pow((1+sqrt(glo_var.l)),2) self.inter_sum = 0 self.rho_sum=[] self.domain = np.concatenate((self.alphas_pre,self.alphas_post)) for i in self.domain: self.j_inter=self.js(i,glo_var.beta) if self.region == 1: for j in range(self.rh.min_location_1): self.inter_cal = pow((self.basic_1 + self.j_inter*self.basic_2),2) - self.j_inter/(glo_var.l*self.lambdas_ys[j]) self.inter_sum -= 0 if self.inter_cal < 0.0001 else sqrt(self.inter_cal) for q in range(self.rh.min_location_1,glo_var.lambdas_degree): self.inter_cal = pow((self.basic_1 + self.j_inter*self.basic_2),2) - self.j_inter/(glo_var.l*self.lambdas_ys[q]) self.inter_sum += 0 if self.inter_cal < 0.0001 else sqrt(self.inter_cal) else : for j in range(self.rh.min_location_1): self.inter_cal = pow((self.basic_1 + self.j_inter*self.basic_2),2) - self.j_inter/(glo_var.l*self.lambdas_ys[j]) self.inter_sum += 0 if self.inter_cal < 0.0001 else sqrt(self.inter_cal) for q in range(self.rh.min_location_1,glo_var.lambdas_degree): self.inter_cal = pow((self.basic_1 + self.j_inter*self.basic_2),2) - self.j_inter/(glo_var.l*self.lambdas_ys[q]) self.inter_sum -= 0 if self.inter_cal < 0.0001 else sqrt(self.inter_cal) self.rho_sum += [self.basic_1 + self.j_inter*self.basic_2 +pow(-1,self.region == 1) * self.inter_sum/glo_var.lambdas_degree] self.inter_sum = 0
class SpectraPlotWidget(PlotWidget): sigEnergyChanged = Signal(object) def __init__(self, *args, **kwargs): super(SpectraPlotWidget, self).__init__(*args, **kwargs) self._data = None self.positionmap = dict() self.wavenumbers = None self._meanSpec = True # whether current spectrum is a mean spectrum self.line = InfiniteLine(movable=True) self.line.setPen((255, 255, 0, 200)) self.line.setZValue(100) self.line.sigPositionChanged.connect(self.sigEnergyChanged) self.line.sigPositionChanged.connect(self.getEnergy) self.addItem(self.line) self.getViewBox().invertX(True) self.selectedPixels = None self._y = None def getEnergy(self, lineobject): if self._y is not None: x_val = lineobject.value() idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = self._y[idx] if not self._meanSpec: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ Spectrum #{self.spectrumInd}</div>' else: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ {self._mean_title}</div>' txt_html += f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt.setHtml(txt_html) def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field # get wavenumbers spectraEvent = next(header.events(fields=['spectra'])) self.wavenumbers = spectraEvent['wavenumbers'] self.N_w = len(self.wavenumbers) self.rc2ind = spectraEvent['rc_index'] # make lazy array from document data = None try: data = header.meta_array(field) except IndexError: msg.logMessage('Header object contained no frames with field ''{field}''.', msg.ERROR) if data is not None: # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2]) self._data = data def showSpectra(self, i=0): if self._data is not None: self.clear() self._meanSpec = False self.spectrumInd = i self.plot(self.wavenumbers, self._data[i]) def getSelectedPixels(self, selectedPixels): self.selectedPixels = selectedPixels # print(selectedPixels) def showMeanSpectra(self): self._meanSpec = True self.clear() if self.selectedPixels is not None: n_spectra = len(self.selectedPixels) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): # j: jth selected pixel row_col = tuple(self.selectedPixels[j]) tmp[j, :] = self._data[self.rc2ind[row_col]] self._mean_title = f'ROI mean of {n_spectra} spectra' else: n_spectra = len(self._data) tmp = np.zeros((n_spectra, self.N_w)) for j in range(n_spectra): tmp[j, :] = self._data[j] self._mean_title = f'Total mean of {n_spectra} spectra' if n_spectra > 0: meanSpec = np.mean(tmp, axis=0) else: meanSpec = np.zeros_like(self.wavenumbers) + 1e-3 self.plot(self.wavenumbers, meanSpec) def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) x_val = self.line.value() if x_val == 0: y_val = 0 else: idx = val2ind(x_val, self.wavenumbers) x_val = self.wavenumbers[idx] y_val = y[idx] if not self._meanSpec: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ Spectrum #{self.spectrumInd}</div>' else: txt_html = f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ {self._mean_title}</div>' txt_html += f'<div style="text-align: center"><span style="color: #FFF; font-size: 12pt">\ X = {x_val: .2f}, Y = {y_val: .4f}</div>' self.txt = TextItem(html=txt_html, anchor=(0, 0)) ymax = max(y) self._y = y self.txt.setPos(1500, 0.95 * ymax) self.addItem(self.txt)
def update_peak_finding(self): try: if len(self.raw_datas) != 0: peak_options = [] for channel in self.filenames: opts = dict([]) for child in self.settings.child(('peak_options')): if child.child(('channel')).value() == channel: children = [ ch.name() for ch in child.children() if not (ch.name() == 'use_opts' or ch.name() == 'channel') ] if child.child(('use_opts')).value(): param_opt = child.child((children[0])) opts[param_opt.name()] = param_opt.value() if len(opts) != 0: peak_options.append(dict(channel=channel, opts=opts)) self.peak_indexes = [] self.peak_amplitudes = [] if len(peak_options) != 0: for option in peak_options: peak_indexes, properties = find_peaks( self.raw_datas[option['channel']], **option['opts']) self.peak_indexes.extend(list(peak_indexes)) self.peak_amplitudes.extend( list(self.raw_datas[option['channel']] [peak_indexes])) self.peak_indexes = np.array(self.peak_indexes) self.peak_amplitudes = np.array(self.peak_amplitudes) arg_sorted_indexes = np.argsort(self.peak_indexes) self.peak_indexes = self.peak_indexes[arg_sorted_indexes] self.peak_amplitudes = self.peak_amplitudes[ arg_sorted_indexes] if len(self.peak_indexes) != 0: self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.plot_peak_item) while len(self.text_peak_items) != 0: self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.text_peak_items.pop(0)) self.viewer_data.viewer.plotwidget.plotItem.removeItem( self.arrow_peak_items.pop(0)) self.plot_peak_item = self.viewer_data.viewer.plotwidget.plot( self.raw_axis[self.peak_indexes], self.peak_amplitudes, pen=None, symbol='+') for ind, peak_index in enumerate(self.peak_indexes): item = TextItem('({:.00f},{:.02f})'.format( self.raw_axis[peak_index], self.peak_amplitudes[ind]), angle=45, color='w', anchor=(0, 1)) size = self.viewer_data.viewer.plotwidget.plotItem.vb.itemBoundingRect( item) item.setPos( self.raw_axis[peak_index], self.peak_amplitudes[ind] + size.height()) self.text_peak_items.append(item) item_ar = ArrowItem( pos=(self.raw_axis[peak_index], self.peak_amplitudes[ind] + size.height() / 5), angle=-90, tipAngle=30, baseAngle=20, headLen=10, tailLen=20, tailWidth=1, pen=None, brush='w') self.arrow_peak_items.append(item_ar) self.viewer_data.viewer.plotwidget.plotItem.addItem( item) self.viewer_data.viewer.plotwidget.plotItem.addItem( item_ar) self.table_model = PandasModel( pd.DataFrame([[False, ind, 0] for ind in self.peak_indexes], columns=[ 'Use', 'Pxl', self.settings.child( 'fit_options', 'fit_units').value() ])) self.settings.child( ('peaks_table')).setValue(self.table_model) except Exception as e: logger.exception(str(e))
class ChangeBarChart(Chart): def __init__(self, x: list, y: list, graphics_view: PlotWidget, labels: str, units=None, x_labels=None, width=0.4): """ Constructor / Instantiation of class Parameters ---------- x : list x-values y : list y-values graphics_view : PlotWidget widget to add items labels : str legend labels units : tuple measurement units for tuple width : int, float width of any bars x_labels : array-like array of dates, i.e. time-period """ super().__init__() Assertor.assert_data_types([x, y, graphics_view, labels, units, x_labels, width], [list, list, PlotWidget, str, (type(None), tuple), (type(None), list), (float, int)]) self.x = asarray(arange(1, len(x) + 1, 1)) self.y = asarray([float(val.replace(" ", "").replace("%", "")) if val else 0 for val in y]) self.graphics_view = graphics_view self.labels = labels self.units = units if units else tuple(["" for _ in range(10)]) self.x_time = x_labels self.width = width self.bar_item_1 = BarGraphItem(x=self.x, height=self.y, width=self.width, brush="#a8ccec") self.graphics_view.addItem(self.bar_item_1) self.bar_item_2 = None self.label = TextItem() pen = mkPen(color="#4c96d7", style=Qt.SolidLine, width=1) self.vertical_line = InfiniteLine(angle=90, movable=False, pen=pen) self.graphics_view.addItem(self.vertical_line) self.view_box = self.graphics_view.getViewBox() self.configure_cross_hair() self.graphics_view.plotItem.vb.setLimits(xMin=0, xMax=max(self.x) + 1) self.graphics_view.setMenuEnabled(False) def configure_cross_hair(self): """ method for configuring cross hair """ place = percentile(array(insert(self.x, 0, 0)), 2) self.label.setPos(place, int(abs(max(self.y, key=abs)) * 1.5)) self.graphics_view.addItem(self.label) def move_vertical_lines(self, pos): """ method for moving the vertical lines on the plot """ mouse_point = self.view_box.mapSceneToView(pos) x_val = int(round(mouse_point.x())) x_idx = where(self.x == x_val) y_val = int(self.y[x_idx]) if self.y[x_idx] else 0 self.vertical_line.setPos(x_val) limits = min(self.x) <= x_val <= max(self.x) if len(self.graphics_view.getViewBox().allChildren()) > 3 and limits: self.highlight_bar_items(x_val, y_val) return x_val, y_val def highlight_bar_items(self, x_val, y_val): """ method for highlighting bar items """ self.graphics_view.removeItem(self.bar_item_2) self.bar_item_2 = BarGraphItem(x=[x_val], height=y_val, width=self.width, brush="#69a8de") self.graphics_view.addItem(self.bar_item_2) def mouse_moved(self, evt): """ method for moving the vertical lines based on mouse placement """ pos = evt[0] x_val, y_val = self.move_vertical_lines(pos) x_label_idx = where(array(self.x) == x_val)[0] x_label = self.x_time[x_label_idx.item()] if \ self.x_time and x_label_idx.size != 0 else \ Amount(str(x_val)).amount + self.units[0] y_label = str(y_val) + self.units[1] if min(self.x) <= x_val <= max(self.x): self.label.setHtml('<div style="text-align: center">' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">({})</span>' '</div>'.format(self.labels, x_label, y_label))
class RatioChart(Chart): """ Implementation of the RatioChart graphics used for visualizing the ratio between sqm-prices in area vs municipality. """ def __init__(self, x: list, y_1: list, y_2: list, graphics_view: PlotWidget, labels: str, units=None, x_labels=None, precision=0, width=0.4): """ Constructor / Instantiate the class Parameters ---------- x : list x-values y_1 : np.ndarray y-values y_2 : np.ndarray y-values graphics_view : PlotWidget widget to add items labels : str legend labels units : tuple measurement units for tuple x_labels : array-like array of x_labels precision : int precision for rounding, default is zero width : int, float width of any bars """ Assertor.assert_data_types([ x, y_1, y_2, graphics_view, labels, units, x_labels, precision, width ], [ list, list, list, PlotWidget, str, (type(None), tuple), (type(None), list), (float, int), (float, int) ]) super().__init__() self.y_1, self.x = self.create_bins(x, y_1, bins=x) self.y_2, self.x = self.create_bins(x, y_2, bins=x) self.x = self.x[:-1] self.labels = labels self.units = units if units else ("", "") self.precision = precision self.ratio = (self.y_1 / self.y_2) * 100 self.x_labels = x_labels if x_labels else self.ratio self.graphics_view = graphics_view self.view_box = self.graphics_view.getViewBox() self.width = width self.label = TextItem() self.bar_item_1 = BarGraphItem(x=self.x, height=self.ratio, width=self.width, brush="#a8ccec") self.graphics_view.addItem(self.bar_item_1) self.configure_cross_hair() self.bar_item_2 = None pen = mkPen(color="#4c96d7", style=Qt.SolidLine, width=1) self.vertical_line = InfiniteLine(angle=90, movable=False, pen=pen) self.graphics_view.addItem(self.vertical_line) self.graphics_view.plotItem.vb.setLimits(xMin=min(self.x) - width, xMax=max(self.x) + width) self.graphics_view.setMenuEnabled(False) def configure_cross_hair(self): """ method for configuring cross hair """ place = percentile(array(insert(self.x, 0, 0)), 2) self.label.setPos(place, int(abs(max(self.ratio, key=abs)) * 1.5)) self.graphics_view.addItem(self.label) def move_vertical_lines(self, pos): """ method for moving the vertical lines on the plot """ mouse_point = self.view_box.mapSceneToView(pos) x_val = int(round(mouse_point.x(), self.precision)) x_idx = where(self.x == x_val) y_val = self.ratio[x_idx] if self.ratio[x_idx] else 0 self.vertical_line.setPos(x_val) limits = min(self.x) <= x_val <= max(self.x) if len(self.graphics_view.getViewBox().allChildren()) > 3 and limits: self.highlight_bar_items(x_val, y_val) return x_val, y_val def highlight_bar_items(self, x_val, y_val): """ method for highlighting bar items """ self.graphics_view.removeItem(self.bar_item_2) self.bar_item_2 = BarGraphItem(x=[x_val], height=y_val, width=self.width, brush="#69a8de") self.graphics_view.addItem(self.bar_item_2) def mouse_moved(self, evt): """ method for moving the vertical lines based on mouse placement """ pos = evt[0] if self.graphics_view.sceneBoundingRect().contains(pos): x_val, y_val = self.move_vertical_lines(pos) percent = Percent( str(y_val / 100).replace("[", "").replace("]", "")) if min(self.x) <= x_val <= max(self.x): self.label.setHtml( '<div style="text-align: center">' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">{} {}</span><br>' '<span style="font-size: 10pt">({})</span>' '</div>'.format(self.labels, Amount(str(x_val)).amount, self.units[0], percent.value))
class DoubleCrossHair(QObject): """ Double cross hair implementation """ def __init__(self, x_1: ndarray, y_1: ndarray, x_2: ndarray, y_2: ndarray, plot_widget_1: PlotWidget, plot_widget_2: PlotWidget, labels: tuple, units=None, precision=0, width=1, x_labels=None, display=int, highlight_bars=True): """ Constructor / Instantiation of class Parameters ---------- x_1 : np.ndarray x-values y_1 : np.ndarray y-values x_2 : np.ndarray x-values y_2 : np.ndarray y-values plot_widget_1 : PlotWidget graphics view to place chart plot_widget_2 : PlotWidget graphics view to place chart labels : tuple labels for legend precision : int precision for rounding, default is zero width : int, float width of any bars x_labels : array-like array of dates, i.e. time-period or other labels display : type display dtype for labels highlight_bars : bool whether to highlight bars in plot """ super().__init__(parent=None) Assertor.assert_data_types( [x_1, y_1, x_2, y_2, plot_widget_1, plot_widget_2, labels, precision, width], [ndarray, ndarray, ndarray, ndarray, PlotWidget, PlotWidget, tuple, int, (int, float)]) self.x_1, self.y_1 = x_1, y_1 self.x_2, self.y_2 = x_2, y_2 self.plot_widget_1 = plot_widget_1 self.plot_widget_2 = plot_widget_2 self.precision = precision self.width = width self.x_time = x_labels self.display = display self.bar_item_1 = None self.bar_item_2 = None self.highlight_bars = highlight_bars pen = mkPen(color="#4c96d7", style=Qt.SolidLine, width=1) self.vertical_line_1 = InfiniteLine(angle=90, movable=False, pen=pen) self.vertical_line_2 = InfiniteLine(angle=90, movable=False, pen=pen) self.label_1 = TextItem() self.label_2 = TextItem() self.labels = labels self.units = units if units else tuple(["" for _ in range(10)]) if self.highlight_bars: self.plot_widget_1.addItem(self.vertical_line_1) self.plot_widget_2.addItem(self.vertical_line_2) self.view_box_1 = self.plot_widget_1.getViewBox() self.view_box_2 = self.plot_widget_2.getViewBox() self.configure_cross_hair() def configure_cross_hair(self): """ method for configuring cross hair """ place = percentile(insert(array(self.x_1), 0, 0), 2) self.label_1.setPos(place, int(abs(max(self.y_1, key=abs)) * 1.5)) self.label_2.setPos(place, int(abs(max(self.y_2, key=abs)) * 1.5)) self.plot_widget_1.addItem(self.label_1) self.plot_widget_2.addItem(self.label_2) def move_vertical_lines(self, pos): """ method for moving the vertical lines on the plot """ mouse_point_1 = self.view_box_1.mapSceneToView(pos) mouse_point_2 = self.view_box_2.mapSceneToView(pos) x_val_1 = int(round(mouse_point_1.x(), self.precision)) x_val_2 = int(round(mouse_point_2.x(), self.precision)) x_idx_1 = where(self.x_1 == x_val_1) x_idx_2 = where(self.x_2 == x_val_2) y_val_1 = self.display(self.y_1[x_idx_1]) if self.y_1[x_idx_1] else 0 y_val_2 = self.display(self.y_2[x_idx_2]) if self.y_2[x_idx_2] else 0 self.vertical_line_1.setPos(x_val_1) self.vertical_line_2.setPos(x_val_1) limits = min(self.x_1) <= x_val_1 <= max(self.x_1) if len(self.plot_widget_1.getViewBox().allChildren()) > 3 and limits \ and self.highlight_bars: self.highlight_bar_items(x_val_1, y_val_1, x_val_2, y_val_2) return x_val_1, y_val_1, x_val_2, y_val_2, limits def highlight_bar_items(self, x_val_1, y_val_1, x_val_2, y_val_2): """ method for highlighting bar items """ self.plot_widget_1.removeItem(self.bar_item_1) self.plot_widget_2.removeItem(self.bar_item_2) self.bar_item_1 = BarGraphItem(x=[x_val_1], height=y_val_1, width=self.width, brush="#69a8de") self.bar_item_2 = BarGraphItem(x=[x_val_2], height=y_val_2, width=self.width, brush="#69a8de") self.plot_widget_1.addItem(self.bar_item_1) self.plot_widget_2.addItem(self.bar_item_2) def mouse_moved(self, evt): """ method for moving the vertical lines based on mouse placement """ pos = evt[0] if self.plot_widget_1.sceneBoundingRect().contains(pos) or \ self.plot_widget_2.sceneBoundingRect().contains(pos): x_val_1, y_val_1, x_val_2, y_val_2, limits = self.move_vertical_lines(pos) x_label_idx = where(array(self.x_1) == x_val_1)[0] x_label_1 = self.x_time[x_label_idx.item()] + self.units[0] if \ self.x_time and x_label_idx.size != 0 else \ Amount(str(x_val_1)).amount + self.units[0] y_label_1 = Amount(str(y_val_1)).amount + self.units[1] x_label_2 = self.x_time[x_label_idx.item()] + self.units[0] if \ self.x_time and x_label_idx.size != 0 else \ Amount(str(x_val_2)).amount + self.units[2] y_label_2 = Amount(str(y_val_2)).amount + self.units[3] if limits: self.label_1.setHtml('<div style="text-align: center">' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">({})</span>' '</div>'.format(self.labels[0], x_label_1, y_label_1)) self.label_2.setHtml('<div style="text-align: center">' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">{}</span><br>' '<span style="font-size: 10pt">({})</span>' '</div>'.format(self.labels[1], x_label_2, y_label_2))
class ClusterSpectraWidget(SpectraPlotWidget): def __init__(self): super(ClusterSpectraWidget, self).__init__(txtPosRatio=0.35) self._x = None self.ymax, self.zmax = 0, 100 self._plots = [] def getEnergy(self): if self._y is not None: x_val = self.line.value() idx = val2ind(x_val, self._x) x_val = self._x[idx] y_val = self._y[idx] txt_html = toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt.setHtml(txt_html) self.cross.setData([x_val], [y_val]) def setColors(self, colorLUT): self.colorLUT = colorLUT.copy() # self.colorLUT[0, :] = np.ones(3) * 255 def plotClusterSpectra(self): if self._data is not None: if self._plots: self.clearAll() self._plots = [] self.ymax = 0 self.plotItem.addLegend(offset=(1, 1)) self.nSpectra = len(self._data) for i in range(self.nSpectra): name = 'Cluster #' + str(i + 1) tmp = self.plot(self.wavenumbers, self._data[i], pen=mkPen(self.colorLUT[i + 1], width=2), name=name) tmp.curve.setClickable(True) tmp.curve.sigClicked.connect(partial(self.curveHighLight, i)) self._plots.append(tmp) self.addItem(self.txt) def curveHighLight(self, k): for i in range(self.nSpectra): if i == k: self._plots[i].setPen(mkPen(self.colorLUT[k + 1], width=6)) self._plots[i].setZValue(50) else: self._plots[i].setPen(mkPen(self.colorLUT[i + 1], width=2)) self._plots[i].setZValue(0) self._x, self._y = self._plots[k].getData() ymin, ymax = np.min(self._y), np.max(self._y) self.getViewBox().setYRange(ymin, ymax, padding=0.1) r = self.txtPosRatio self.txt.setPos(r * self._x[-1] + (1 - r) * self._x[0], ymax) self.getEnergy() def plot(self, x, y, *args, **kwargs): # set up infinity line and get its position plot_item = self.plotItem.plot(x, y, *args, **kwargs) self.addItem(self.line) self.addItem(self.cross) x_val = self.line.value() idx = val2ind(x_val, x) x_val = x[idx] y_val = y[idx] txt_html = toHtml(f'X = {x_val: .2f}, Y = {y_val: .4f}') self.txt = TextItem(html=txt_html, anchor=(0, 0)) self.txt.setZValue(self.zmax - 1) self.cross.setData([x_val], [y_val]) self.cross.setZValue(self.zmax) ymax = np.max(y) if ymax > self.ymax: self.ymax = ymax self._x, self._y = x, y r = self.txtPosRatio self.txt.setPos(r * x[-1] + (1 - r) * x[0], self.ymax) return plot_item
class BarChartWithLine(Chart): """ Implementation of the Bar chart with line graphics """ def __init__(self, x: list, y: list, graphics_view: PlotWidget, table_view: QTableView, legend: str = "", width=0.5, reverse=True): """ Constructor / Instantiation of class Parameters ---------- x : list x-values y : list y-values graphics_view : PlotWidget graphics view to place chart table_view : QTableView table view to link graphics_view legend : HTML str legend width : int, float width of bars reverse : bool reverse selection in table """ Assertor.assert_data_types( [x, y, graphics_view, table_view, legend, width], [list, list, PlotWidget, QTableView, str, (int, float)]) super().__init__() self.x = x self.y = y self.graphics_view = graphics_view self.table_view = table_view self.label = TextItem() self.width = width self.reverse = reverse place = percentile(insert(array(self.x), 0, 0), 2) self.label.setPos(place, int(max(y) * 1.5)) self.label.setHtml(legend) self.graphics_view.addItem(self.label, ignore_bounds=True) self.bar_item = BarGraphItem(x=self.x, height=self.y, width=self.width, brush="#d2e5f5") self.graphics_view.addItem(self.bar_item) pen = mkPen(color="#d2e5f5", style=Qt.DotLine, width=2) self.graphics_view.plot(x=self.x, y=self.y, pen=pen, symbol='+', symbolSize=14) self.graphics_view.plotItem.vb.setLimits(xMin=min(self.x) - width, xMax=max(self.x) + width) self.graphics_view.setMenuEnabled(False) self.graphics_view.getViewBox().enableAutoRange() def table_view_mapping(self): """ method for mapping table rows to chart bars """ self.table_view.clicked.connect(self.row_clicked) def row_clicked(self, item): """ method for accessing row in table_view """ pen_1 = mkPen(color="#69a8de", style=Qt.DotLine, width=2) pen_2 = mkPen(color="#d2e5f5", style=Qt.DotLine, width=2) bar_item = BarGraphItem(x=self.x, height=self.y, width=self.width, brush="#d2e5f5") chart_item = PlotDataItem(x=self.x, y=self.y, pen=pen_1, symbol='+', symbolSize=14) clear_line = PlotDataItem(x=self.x, y=self.y, pen=pen_2, symbol='+', symbolSize=14) if item.row() < len(self.x): row = len(self.x) - 1 - item.row() if self.reverse else item.row() clicked_item = BarGraphItem(x=[self.x[row]], height=self.y[row], width=self.width, brush="#69a8de") self.graphics_view.addItem(bar_item) self.graphics_view.addItem(clicked_item) self.graphics_view.addItem(chart_item) else: self.graphics_view.addItem(bar_item) self.graphics_view.addItem(clear_line) @staticmethod def clear_graphics(graphics_view: PlotWidget, table_view: QTableView): """ static method for clearing content in all graphics Parameters ---------- graphics_view : PlotWidget graphics view to place chart table_view : QTableView table view to link graphics_view """ Assertor.assert_data_types([graphics_view, table_view], [PlotWidget, QTableView]) table_view.setModel(None) table_view.clearSpans() graphics_view.clear()
class MapViewWidget(DynImageView): sigShowSpectra = Signal(int) def __init__(self, *args, **kwargs): super(MapViewWidget, self).__init__(*args, **kwargs) # self.scene.sigMouseMoved.connect(self.showSpectra) self.scene.sigMouseClicked.connect(self.showSpectra) self.view.invertY(True) # add arrow # self.arrow = ArrowItem(angle=60, headLen=15, tipAngle=45, baseAngle=30, brush = (200, 80, 20)) # self.arrow.setPos(0, 0) self.cross = PlotDataItem([0], [0], symbolBrush=(200, 0, 0), symbolPen=(200, 0, 0), symbol='+', symbolSize=16) self.view.addItem(self.cross) self.cross.hide() #add txt self.txt = TextItem('', anchor=(0, 0)) self.addItem(self.txt) def setEnergy(self, lineobject): E = lineobject.value() # map E to index i = val2ind(E, self.wavenumbers) # print('E:', E, 'wav:', self.wavenumbers[i]) self.setCurrentIndex(i) def showSpectra(self, event): pos = event.pos() if self.view.sceneBoundingRect().contains( pos ): # Note, when axes are added, you must get the view with self.view.getViewBox() mousePoint = self.view.mapSceneToView(pos) x, y = int(mousePoint.x()), int(mousePoint.y()) y = self.row - y - 1 try: ind = self.rc2ind[(y, x)] self.sigShowSpectra.emit(ind) # print(x, y, ind, x + y * self.n_col) #update arrow self.cross.setData([x + 0.5], [self.row - y - 0.5]) self.cross.show() # update text self.txt.setHtml( f'<div style="text-align: center"><span style="color: #FFF; font-size: 8pt">X: {x}</div>\ <div style="text-align: center"><span style="color: #FFF; font-size: 8pt">Y: {y}</div>\ <div style="text-align: center"><span style="color: #FFF; font-size: 8pt">Point: #{ind}</div>' ) except Exception: self.cross.hide() def setHeader(self, header: NonDBHeader, field: str, *args, **kwargs): self.header = header self.field = field imageEvent = next(header.events(fields=['image'])) self.rc2ind = imageEvent['rc_index'] self.wavenumbers = imageEvent['wavenumbers'] # make lazy array from document data = None try: data = header.meta_array(field) self.row = data.shape[1] self.col = data.shape[2] self.txt.setPos(self.col, 0) except IndexError: msg.logMessage( 'Header object contained no frames with field ' '{field}' '.', msg.ERROR) if data is not None: # kwargs['transform'] = QTransform(1, 0, 0, -1, 0, data.shape[-2]) self.setImage(img=data, *args, **kwargs) self._data = data def updateImage(self, autoHistogramRange=True): super(MapViewWidget, self).updateImage(autoHistogramRange) self.ui.roiPlot.setVisible(False) def setImage(self, img, **kwargs): super(MapViewWidget, self).setImage(img, **kwargs) self.ui.roiPlot.setVisible(False) def makeMask(self, thresholds): peak1550 = val2ind(1550, self.wavenumbers) thr1550 = thresholds[0] mask = self._data[peak1550] > thr1550 mask = mask.astype(np.int) return mask
class NaturalPlotView(GraphicsView): """Creates a simple about dialog. The about dialog contains general information about the application and shows the copyright notice. That's why the class has no attributes or return values. """ def __init__(self): super().__init__() # settings self.setBackground("#fff") self.setFrameStyle(QFrame.StyledPanel|QFrame.Sunken) self.setAntialiasing(True) # create custom view box view_box = ViewBox() view_box.setMouseEnabled(False, False) view_box.setLimits(xMin=0, yMin=0, minXRange=10, minYRange=100) view_box.setRange(xRange=(0, 400), yRange=(0, 5000)) view_box.enableAutoRange() # create natural axis items self.x_axis = NaturalAxis("bottom") self.x_axis.setLabel(QApplication.translate("NaturalPlotView", "Fence length"), "m") self.y_axis = NaturalAxis("left") self.y_axis.setLabel(QApplication.translate("NaturalPlotView", "Number of plants")) # create fence information text self.fenceItem = TextItem(border=pyqtgraph.mkPen(width=2, color="#555"), fill=pyqtgraph.mkBrush((255, 255, 255, 200))) # create tube information text self.tubeItem = TextItem(border=pyqtgraph.mkPen(width=2, color="#555"), fill=pyqtgraph.mkBrush((255, 255, 255, 200)), anchor=(1,1)) # create plot item with custom view box and natural axis items self.plotItem = PlotItem(viewBox=view_box, axisItems={"bottom" : self.x_axis, "left" : self.y_axis}, enableMenu=False) self.plotItem.setContentsMargins(5, 5, 12, 5) self.plotItem.hideButtons() self.plotItem.hide() self.setCentralWidget(self.plotItem) # connect actions view_box.sigResized.connect(self.updateTubeLegendPosition) # translate the plot item self.retranslateUi() def retranslateUi(self): # title label titleStyle = "color: #111; font-size: 15px; font-weight: bold" titleLabel = "<span style='{style}'>{title}</span>".format(style=titleStyle, title="Ergebnis: Funktion(en) der Kostengleichheit") self.plotItem.setTitle(titleLabel) # axis items #self.x_axis.setLabel(QApplication.translate("NaturalPlotView", "Fence length"), "m") #self.y_axis.setLabel(QApplication.translate("NaturalPlotView", "Number of plants")) # fence hint self.fenceItem.setHtml("<p style='margin: 0; color: #555'><b>" + QApplication.translate("NaturalPlotView", "Pfeil über Funktion(en):") + "</b> " + QApplication.translate("NaturalPlotView", "Zaun günstiger") + "</p>") # tube hint self.tubeItem.setHtml("<p style='margin: 0; color: #555'><b>" + QApplication.translate("NaturalPlotView", "Pfeil unter Funktion(en):") + "</b> " + QApplication.translate("NaturalPlotView", "Wuchshülle günstiger") + "</p>") def addPlotItem(self, item, *args, **kwargs): # first show the plot item if not self.plotItem.isVisible(): self.plotItem.show() self.plotItem.addItem(item, *args, **kwargs) def removePlotItem(self, item): self.plotItem.removeItem(item) #TODO: def clear(self): self.plotItem.hide() self.plotItem.clear() def showDescription(self): viewBox = self.plotItem.getViewBox() self.fenceItem.setPos(15, 10) self.fenceItem.setParentItem(viewBox) rect = viewBox.screenGeometry() self.tubeItem.setPos(rect.width() - 15, rect.height() - 10) self.tubeItem.setParentItem(viewBox) def updateTubeLegendPosition(self): rect = self.plotItem.getViewBox().screenGeometry() self.tubeItem.setPos(rect.width() - 15, rect.height() - 10) def export(self, gfxfile): exporter = TestImageExporter(self.plotItem) exporter.parameters()["width"] = 2007.0 # 17 cm / 300 DPI # export the graphics to the selected file exporter.export(gfxfile)
class FanDiagram(QtGui.QWidget): """ Produces a fan diagram alphas/gammas should be the matrices saved from the FanCompiler, of form: arb | niralpha1 | niralpha2 | niralpha3 | niralpha4 | ... 1st sb | 1sb alpha | 1sb alpha | . 2nd sb | 2sb alpha | 2sb alpha | . 3rd sb | 3sb alpha | 3sb alpha | . . . . Assumes both alphas/gammas are the same shape Alternatively, pass nirAlpha and SBs as 1st/2nd args (as 1D arrays) to have it create the fan without any data Or, if alphaData and gammaData are strings, assumes they're paths to data files to plot :param alphas: :param gammas: :param kwargs: :return: """ def __init__(self, alphaData, gammaData=None, view=None): super(FanDiagram, self).__init__() if gammaData is None and isinstance(alphaData, FanCompiler): alphaData, gammaData = alphaData.build(withErrors=False)[:2] self.layout = QtWidgets.QHBoxLayout() self.histAlpha = HistogramLUTWidget(self) self.centralView = GraphicsView() self.histGamma = HistogramLUTWidget(self) self.histAlpha.setMinimumWidth(150) self.histGamma.setMinimumWidth(150) self.layout.addWidget(self.histGamma) self.layout.addWidget(self.centralView) self.layout.addWidget(self.histAlpha) self.layout.setContentsMargins(0,0,0,0) self.layout.setSpacing(0) self.setLayout(self.layout) if view is None: self.view = ViewBox() else: self.view = view self.centralView.setCentralItem(self.view) self.view.setAspectLocked(True) self.view.invertY(True) if isinstance(alphaData, str) and isinstance(gammaData, str): alphaData = np.genfromtxt(alphaData, delimiter=',') gammaData = np.genfromtxt(gammaData, delimiter=',') if alphaData.ndim == gammaData.ndim == 1: # Assume you just want it to be created, and will later populate it nirAlphas = alphaData sbs = gammaData alphaData = np.ones((sbs.shape[0] + 1, nirAlphas.shape[0] + 1)) * -1 alphaData[1:, 0] = sbs alphaData[0, 1:] = nirAlphas gammas = np.ones((sbs.shape[0] + 1, nirAlphas.shape[0] + 1)) * -1 gammas[1:, 0] = sbs gammas[0, 1:] = nirAlphas sbs = alphaData[1:, 0] maxSB = sbs.max() nirAlphas = alphaData[0, 1:] self.alphaItem = PolarImageItem(r=sbs, theta=nirAlphas) self.alphaItem.setImage(alphaData[1:,1:]) # nirAlphas+180 is what causes the gamma angles to appear on the left side of the # fan. This seemed easier than doing some sort of coordinate inversion/flipping # on the plot itself. self.gammaItem = PolarImageItem(sbs, nirAlphas+180, gammaData[1:,1:]) self.view.addItem(self.alphaItem) self.view.addItem(self.gammaItem) self.histAlpha.setImageItem(self.alphaItem) self.histGamma.setImageItem(self.gammaItem) # manually set the default state to the black-gold-white-green-black. Not sure # if it's necessary to have this be a free parameter vs being hardcoded self.histAlpha.gradient.restoreState({ "mode": "rgb", "ticks": [ (0, (0, 0, 0, 255)), (.25, (128, 128, 0, 255)), (.5, (255, 255, 255, 255)), (.75, (0, 128, 0, 255)), (1, (0, 0, 0, 255)) ] }) # Set the default spacings for the alpha color axis. Again, not sure if it's # better to leave the 18pt font hard-coded or not, but I am self.histAlpha.axis.setTickFont(QtGui.QFont("Arial", 18)) self.histAlpha.axis.setTickSpacing(30, 15) self.histAlpha.axis.setLabel("α (°)", **{'font-family': 'Times', "font-size": "18pt"}) # As with alpha, hard code the initial color space for gamma (blue-white-red) # and the font spacings and stuff self.histGamma.gradient.restoreState({ "mode": "rgb", "ticks": [ (0, (255, 0, 0, 255)), (.5, (255, 255, 255, 255)), (1, (0, 0, 255, 255)) ] }) self.histGamma.axis.setTickFont(QtGui.QFont("Arial", 18)) self.histGamma.axis.setTickSpacing(15, 15) self.histGamma.axis.setLabel("γ (°)", **{'font-family': 'Times', "font-size": "18pt"}) self.histAlpha.item.setLevels(-90, 90) self.histGamma.item.setLevels(-45, 45) self.histAlpha.autoHistogramRange() self.histGamma.autoHistogramRange() # Make it the right dimensions, making sure that the width is appropriate. # This makes it easier to automate plotting/saving fans and making sure # their dimensions are consistent. g = self.geometry() # I found these by eye, there's not very much important about them g.setWidth(773) g.setHeight(480) # Manually center it on the screen, since geometry isn't well defined at this point # before events are processed g.moveCenter(QtWidgets.QApplication.desktop().screenGeometry().center()) self.setGeometry(g) # Add in the radial axes for it self.axes = { "radial": PolarAxis("radial"), "azimuthal": PolarAxis("azimuthal") } # Lighten the radial font to make it distinct from the other p = self.axes["radial"].pen() p.setColor(mkColor("#666666")) self.axes["radial"].setPen(p) for a in self.axes.values(): # Make sure the axes sit on top of all other items a.setZValue(10000) # make sure that they scale appropriately, instead of just floating on top a.linkToView(self.view) # Ignore bounds prevents the window from resizing to try and fit in # the axes items self.addItem(a, ignoreBounds=True) # manually set the positions and string values for alpha angles. [-90, 90] work # well. The other half needs the +-180 to make sure the gamma angles have the # correctly labeled with respect to alpha_nir self.axes["azimuthal"].setTicks( [ [(ii, str(ii)) for ii in np.arange(-90, 91, 30)] + # alpha side (Q1+Q4) [(ii, str(ii + 180)) for ii in np.arange(-180, -91, 30)] + #Q3 [(ii, str(ii - 180)) for ii in np.arange(120, 151, 30)], #Q1 ] ) # add a title (without text) self.titleItem = TextItem() self.titleItem.setAnchor(Point(0.5, 1)) # anchor on bottom-center # Again, not sure if it's necessary to have the font color/size being # a free parameter self.titleItem.setColor("k") self.titleItem.setFont(QtGui.QFont("Arial", 15)) # Ignore bounds so that the view won't try to account for it (which # causes a conflict because the title is placed with respect to the # view region) self.view.addItem(self.titleItem, ignoreBounds=True) self.show() # Arbitrary forcing updates to try and track down why some things don't # update correctly QtWidgets.QApplication.processEvents() self.view.updateViewRange(True, True) def setAlphaImage(self, img): self.alphaItem.setImage(img) def setGammaImage(self, img): self.gammaItem.setImage(img) def setImages(self, alpha, gamma): self.setAlphaImage(alpha) self.setGammaImage(gamma) def export(self, fname, hideHistograms=True, pngScale = 4): """ Save fan diagrams to file, with the full image, and color bars on the alpha/gamma values :param fname: the fname to save as hideHistograms - (True) Prevent rendering the histograms, often ncier for figures/presentations If fname.endswith(".svg"), it outputs as an SVG. Howver, it's not the cleanest thing (the files are quite large/unoptimized, and I can't think of an easy way to correct that). Also, when the svg is converted to pdf via Inkscape, things get f****d up for some reasons (axes get thicker, fonts get borked, pixels get messed up). So, it kinda works, but there's stuff seriously wrong. One thing to make things cleaner is to use this site: https://jakearchibald.github.io/svgomg/ which optimizies the svg and makes it a lot easier to work with :return: """ # defaults = { # "hideHistograms": False # } # # defaults.update(kwargs) doSvg = fname.endswith(".svg") if hideHistograms: # Hide the histogram data (and shrink the plot) # to avoid confusing people self.histAlpha.plot.hide() self.histAlpha.vb.setMaximumWidth(20) self.histGamma.plot.hide() self.histGamma.vb.setMaximumWidth(20) QtWidgets.QApplication.processEvents() self.histGamma.axis.setRange(-46.75, 46.75) self.histAlpha.axis.setRange(-94, 94) width, height = self.width(), self.height() if doSvg: from PyQt5 import QtSvg outputImage = QtSvg.QSvgGenerator() outputImage.setFileName(fname) outputImage.setSize(QtCore.QSize(int(width), int(height))) # I'm not sure why it has to be this, but the axis on the histogrm # were fuckingup without it outputImage.setResolution(96) else: outputImage = QtGui.QImage(width * pngScale, height * pngScale, QtGui.QImage.Format_ARGB32) # outputImage.setDotsPerMeterX(650 * 100 / 2.54) # outputImage.setDotsPerMeterY(650 * 100 / 2.54) # this gives a moderatly high quality image outputImage.setDevicePixelRatio(pngScale) outputImage.fill(QtGui.QColor("white")) outputPainter = QtGui.QPainter(outputImage) self.render(outputPainter) if not doSvg: ret = outputImage.save(fname) outputPainter.end() def addItem(self, item, ignoreBounds=False): self.view.addItem(item, ignoreBounds) def setViewRadius(self, r): # Set the view range of the fan diagram such that radius r is visible self.view.setRange(QtCore.QRect(-r, -r, 2*r, 2*r), padding=0) def hideHistogramAxes(self, hideTicks=True): # Hide the histogram region item and plots and all that for # less cluttered plots. Definitely useful if export is called with # hideHistograms=True, where the regions are useless. # Hide the linear regions self.histGamma.region.hide() self.histAlpha.region.hide() # Keep a reference to the old paint methods so you can reverse it if desired # This stops the painting of the bars which go from the linear region to the # gradient editor self.histGamma.item.oldPaint = self.histGamma.item.paint self.histAlpha.item.oldPaint = self.histAlpha.item.paint # Overwriting the functions to return None causes all the other rendering # things to abort self.histGamma.item.paint = lambda *x: None self.histAlpha.item.paint = lambda *x: None if hideTicks: # Hide the ticks which can be used for changing the stops/colors of # the gradients, which are rather ugly # Note: Since this only hides ticks which are present, I don't think [ii.hide() for ii in self.histAlpha.item.gradient.ticks.keys()] [ii.hide() for ii in self.histGamma.item.gradient.ticks.keys()] QtWidgets.QApplication.processEvents() # Hard coded numbers which make it look like the axes values line up with # the gradient item, which is more in-line with how color bars are interpreted self.histGamma.axis.setRange(-46.75, 46.75) self.histAlpha.axis.setRange(-94, 94) def showHistogramAxes(self, showTicks=True): try: self.histGamma.item.paint = self.histGamma.item.oldPaint self.histAlpha.item.paint = self.histAlpha.item.oldPaint del self.histAlpha.item.oldPaint del self.histGamma.item.oldPaint except AttributeError: # You didn't hide them first (or at least not here return self.histGamma.region.show() self.histAlpha.region.show() if showTicks: [ii.show() for ii in self.histAlpha.item.gradient.ticks.keys()] [ii.show() for ii in self.histGamma.item.gradient.ticks.keys()] @staticmethod def fromTMatrix(tMatrix, angle = 45, sbs=None): """ Create a fan diagram from T matrices directly. The angle needs to be specified so the T matrix can be converted to a J matrix. The angle is relative to what's specified in the qwp.extractMatrices.makeU function. if you pass a string, it assumes it's a file name from a saved one. It'll load that and plot it. If you also pass values to sbs, it'll make sure only the passed values are plotted. Otherwise, it'll plot all the sbs in the file If you pass a tMatrix as returned from the fitting routines, you also need to pass the sbs directly in this case, since the tMatrices don't include them. :param tMatrix: :param angle: :param sbs: :return: """ if isinstance(tMatrix, str): # a file is passed if sbs is not None: # Pass an array of sbs with a string, and this'll parse # out the sidebands which aren't included in the passed array wantsbs = sbs else: wantsbs = None tMatrix, sbs = loadT(tMatrix) # Handle if only a select number of sidebands is specified if wantsbs is not None: try: # Find the indices of the desired sidebands within the array of # sidebands actually loaded wantIdx = [sbs.tolist().index(ii) for ii in wantsbs] # Cull it to only those specified sbs = sbs[wantIdx] # tMatrix is multidimensional (tMatrix.ndim>2), so ellipses cut # out the other axes tMatrix = tMatrix[..., wantIdx] # Ensure that you got everything you want. Could happen if sidebands # are requested (passed to the function) and not found assert np.all(wantsbs == sbs) except ValueError as e: raise IndexError("Invalid sideband requested ({} is not in loaded)".format( e.args[0].split(' ')[0] )) except AssertionError: raise IndexError("Invalid sideband requested") jMatrix = makeJfromT(tMatrix, angle) if sbs is None: raise RuntimeWarning("Desired sidebands to plot should be specified as kwarg sbs") sbs = np.arange(8, 38, 2) alpha, gamma = jonesToFans(sbs = sbs, J=jMatrix) return FanDiagram(alpha, gamma) def setTitle(self, title="", adjustBounds=True): """ Sets the title of the fan diagram, positioning the text right above the center of the fan :param title: :param adjustBounds: :return: """ self.titleItem.setText(title) # Move the title so the bottom is at the top of the outer axis self.titleItem.setPos(0, self.axes["azimuthal"].fullBoundingRect.top()) QtWidgets.QApplication.processEvents() # Double up because of some weird f*****g issue with Qt not appropriately # updating things when requested self.titleItem.setPos(0, self.axes["azimuthal"].fullBoundingRect.top()) QtWidgets.QApplication.processEvents() # print(self.titleItem.mapRectToView(self.titleItem.boundingRect())) if adjustBounds: # Readjust the viewbox to frame the fan better # Find the top, based on the coordinates of the top of the title top = self.titleItem.mapRectToView(self.titleItem.boundingRect()).top() # Bottom is defiend by the bottom of the axes (includes the text) # Note: this assumes the bottom = self.axes["azimuthal"].fullBoundingRect.bottom() # print("bottom", bottom) w = abs(top-bottom) # print("new rect", QtCore.QRectF(-w/2, top, w, w)) self.view.setRange(QtCore.QRectF(-w/2, top, w, w), padding=0) self.view.setRange(QtCore.QRectF(-w/2, top, w, w), padding=0) # self.view.update() def setMaxRadius(self, radius=40): # Set the maximum value for both of the axes to the value specified. # The 1e-6 is to prevent it from producing an "r=0" label and stuff self.axes["azimuthal"]._bounds["radial"] = [1e-6, radius] self.axes["radial"]._bounds["radial"] = [1e-6, radius] # Need to invalidate the cache for the axes, forcing it to redraw and update # the bounding rect and stuff self.axes["azimuthal"].picture = None self.axes["radial"].picture = None
def setNewTraces(self, waveform_traces, plot_event): """ Function for scaling and plotting waveform traces """ self.plot_widget.getPlotItem().clear() offset = 0 first_event_timestamp = None pick_dict = {} for pick in plot_event.data: if pick.station_code not in pick_dict: pick_dict[pick.station_code] = [] pick_dict[pick.station_code].append(pick) for key in waveform_traces.keys(): for tr in waveform_traces[key]: if first_event_timestamp is None: first_event_timestamp = tr.stats['starttime'] elif first_event_timestamp > tr.stats['starttime']: first_event_timestamp = tr.stats['starttime'] for key in waveform_traces.keys(): for tr in waveform_traces[key]: tr_filter = self.filter_stats.getCurrentFilter() tr_copy = filterTrace(tr_filter, tr) data_array = tr_copy.data time_array = tr_copy.times() time_offset = tr_copy.stats['starttime'] - first_event_timestamp min_value = numpy.amin(data_array) max_value = numpy.amax(data_array) - min_value data_array = ((data_array - min_value) / max_value) - offset time_array += time_offset self.plot_widget.getPlotItem().plot(time_array, data_array, pen=self.plot_pen) channel_text_item = TextItem("{0} - {1}".format( key, tr.stats['channel'])) self.plot_widget.getPlotItem().addItem(channel_text_item) channel_text_item.setPos(-20, 0.5 - offset) offset += 1 if key in pick_dict: for pick in pick_dict[key]: if pick.phase_type[0] not in ['S', 'P', 'M']: continue tr_size = len(waveform_traces[key]) time_pos = UTCDateTime( pick.observation_time) - first_event_timestamp if pick.phase_type[0] == 'S': pick_pen = self.s_pick_pen elif pick.phase_type[0] == 'P': pick_pen = self.p_pick_pen else: pick_pen = self.msg_pick_pen pick_bar = ErrorBarItem( x=numpy.array([time_pos]), y=numpy.array([float(tr_size) / 2 - offset + 1]), height=tr_size, pen=pick_pen) pick_text = TextItem(pick.phase_type, color=pick_pen.color()) self.plot_widget.getPlotItem().addItem(pick_bar) self.plot_widget.getPlotItem().addItem(pick_text) pick_text.setPos(time_pos, 1.1 + tr_size - offset)