Source code for diaGrabber.plot.Gui

# -*- coding: utf-8 *-*
from diaGrabber import  _utils
#from diaGrabber.source._dimension import mergeDimension
#from diaGrabber.source._dimension import basisDimension
from diaGrabber.plot._preferenceDock import preferenceDock

import sys
import numpy as np
import bottleneck as bn
import signal
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
import pyqtgraph.dockarea as pgDock
from copy import deepcopy


[docs]class Gui(object): def __init__(self, matrixClass, **kwargs): ''' For all possible kwargs look at **setArgs()** ''' #imports self.matrixClass = matrixClass self._merge_dim = self.matrixClass._merge_dim self._basis_dim = self.matrixClass._basis_dim self.show_dim_list = [] for n in range(len(self._merge_dim)): #as standard: display every one merge over every basis self.show_dim_list.append([[n],range(len(self._basis_dim))]) #standard self.interactive = False self._setFPS(20) self.limitReadoutRate = False self._setLPS(1) self.preferenceDock_size = 350 self.windowSize = [1200,600] self.display_size = int((self.windowSize[0]-self.preferenceDock_size)/2) self.enableAutoRange = [] self.showType = "together" self._setColorTheme("default") self.build_preferences_dock = True self.end_readOut = False self.closeWhenFinished = False #individual self.setArgs(**kwargs) #helpers self.hasInteractiveModus = True self.save_img = False self.show_window = True self.plot_method_executed = False self.done_readOut = False #create prefercence dock self._preferencesDock = preferenceDock(self)
[docs] def plot(self, **kwargs): self.plot_method_executed = True self.setArgs(**kwargs) ##create qt-window self.app = QtGui.QApplication([]) self.app.aboutToQuit.connect(self._quitApp)#ensure that closing realy close qt # exit,when strg+c are pressed signal.signal(signal.SIGINT, self._quitApp) self.win = QtGui.QMainWindow() self.win.resize(self.windowSize[0],self.windowSize[1]) self._createDisplays() self.win.show() def updateValues(): if not self.done_readOut: #lineWise updating data self.done_readOut = self.matrixClass._fill(True, self.end_readOut) if self.done_readOut or self.done_readOut == None: print "readout complete!" if self.closeWhenFinished: self._quitApp() else: self._updateDisplays()#do a last update to get the latest values else: #stop the timers because readout is done self.timerFPS.stop() self.timerLPS.stop() if self.save_img: self._save() if self.interactive: #set timer self.timerLPS = QtCore.QTimer() #get new basis-merge-values self.timerLPS.timeout.connect(updateValues) self.timerLPS.start(self.wait_ms_lps) self.timerFPS = QtCore.QTimer() self.timerFPS.timeout.connect(self._updateDisplays) self.timerFPS.start(self.wait_ms) else: #static plotting self._updateDisplays() if self.save_img: self._save() if not self.show_window: self._quitApp() elif (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'): self.app.exec_() self.plot_method_executed = False #reset
[docs] def setArgs(self, **kwargs): ''' Optional kwargs ("keyword arguments") are: ================== ======== ======= ============================ Keyword Type Default Description ================== ======== ======= ============================ *interactive* bool False [True, False] *show* list [{All}] a list of lists containing names of merge-(m) and basisDimensions(b) to show e.g. show = [ (m1,b1,b2), (m2,b2) ] *fps* float 20 frames per seconds *lps* float None readout n lines from source per second (needs limitReadoutRate = True) *limitReadoutRate* bool False Choose whether to limit the readoutrate via arg *lps* *enableAutoRange* list [] ["x", "y"] *colorTheme* string "default" ["default", "bright"] *windowSize* list [1000,600] [size_x, size_y] e.g. **[1000,600]** *showPreferences* bool True [True, False] *closeWhenFinished* bool False [True, False] Choose whether to close the plot-window when fillInteractive() is done *save* dict {} can include all keywords from the method 'save' ================== ======== ======= ============================ ''' #individual for key in kwargs: if key == "interactive": self.interactive = bool(kwargs[key]) elif key == "show": #e.g.: show = [ (m1,b1,b2), (m2,b2) ] self.show_dim_list = [] for v in kwargs[key]: if (type(v) == list or type(v) == tuple) and len(v) >= 2:#valid self.show_dim_list.append([[],[]])#[[merges],[basis']] for w in v: #check for merge found = False for i,m in enumerate(self._merge_dim): if m.name == w: self.show_dim_list[-1][0].append(i) found = True break #check for basis if not found: for i,b in enumerate(self._basis_dim): if b.name == w: self.show_dim_list[-1][1].append(i) found = True break if not found: raise KeyError("name in show %s not valid!" %w) if None in self.show_dim_list[-1]: #else: raise KeyError("list-entries for 'show' has to be names of at least one basis- and one mergeDimension") else: raise KeyError("values for 'show' have to be a list of lists of dimensions") elif key == "fps": if kwargs[key] != self.fps: self._setFPS(kwargs[key]) elif key == "lps": if (kwargs[key] != self.lps or not self.limitReadoutRate): self.limitReadoutRate = True self._setLPS(kwargs[key]) elif key == "limitReadoutRate": self.limitReadoutRate = bool(kwargs[key]) elif key == "enableAutoRange": self.enableAutoRange = kwargs[key] elif key == "colorTheme": if kwargs[key] != self.colorTheme: self._setColorTheme(kwargs[key]) elif key == "windowSize": self.windowSize = list( int(kwargs[key][0]), int(kwargs[key][1]) ) elif key == "showPreferences": self.build_preferences_dock = bool(kwargs[key]) elif key == "closeWhenFinished": self.closeWhenFinished = bool(kwargs[key]) elif key == "save": self.save(**kwargs[key]) else: raise KeyError("keyword '%s' not known" %key)
[docs] def save(self,**kwargs): ''' Optional kwargs ("keyword arguments") are: ================== ======== ======= ============================ Keyword Type Default Description ================== ======== ======= ============================ *mergeName* string [{All}] names of merges to save *name* string "" name-prefix of the image *type* string "png" typ of the image ["png","jpg","tiff","svg"] *parameters* list [] takes all parameters accepted by pyQtGraph.exporters ================== ======== ======= ============================ ''' self.save_img = True #standard self.img_name = "" self.merge_to_save = range(len(self._merge_dim))#show all merge-dim self.img_type = "png" self.img_parameters = [] #individual for key in kwargs: if key == "mergeName": self.merge_to_save = [] mergeNames = kwargs[key] if type(mergeNames) != list or type(mergeNames) != tuple: mergeNames = [mergeNames] for mergeName in mergeNames: found_merge = False for m in self._merge_dim: if m.name == mergeName: self.merge_to_save.append(m._mergeIndex) found_merge = True break if not found_merge: raise KeyError("mergeName %s not valid!" %mergeName) elif key == "name": self.img_name = str(kwargs[key]) elif key == "type": self.img_type = str(kwargs[key]).lower()#only lower-size-letters if self.img_type not in ["png","jpg","tiff","svg"]: raise KeyError("save-type '%s' not valid" %self.img_type) elif key == "parameters": self.img_parameters = dict(kwargs[key]) else: raise KeyError("keyword '%s' not known" %key) if not self.plot_method_executed:#no plot created so far self.show_window = False self.interactive = False self.plot()#do the plotting stuff without showing user
def _save(self): ############merge noch mit rein############ for n,d in enumerate(self.display_list): is_merge_in_view = False for i_merge in self.merge_to_save: if i_merge in d.show_merge: is_merge_in_view = True break if is_merge_in_view: # create an exporter instance, as an argument give it # the item you wish to export if self.img_type == "svg": exporter = pg.exporters.SVGExporter.SVGExporter(d.view) else: exporter = pg.exporters.ImageExporter.ImageExporter(d.view) for p in self.img_parameters: # set export parameters if needed try: exporter.parameters()[p] = self.img_parameters[p] except (Exception): print "KeyError: save-type '%s' doesn't support parameter '%s' ...continue without it" %(self.img_type, p) merge_names = "" for i in d.show_merge: merge_names += "_%s" %self._merge_dim[i].name # save to file exporter.export(self.img_name + merge_names +"." + self.img_type) self.save_img = False #reset def _updateDisplays(self): for d in self.display_list: d.update() def _quitApp(self,signum=None, frame=None): self.app.closeAllWindows() self.app.quit() #self.app.exec_() #self.app.deleteLater() #del self.app self.interactive = False self.app =None def _addToArea(self, display): args = {} l=len(self.display_list) if l > 1: if l == 2: args["position"] = 'right' args["relativeTo"] = self.display_list[-2].dock elif l < 5: args["position"] = 'bottom' args["relativeTo"] = self.display_list[-3].dock else: args["position"] = 'below' args["relativeTo"] = self.display_list[-2].dock self.area.addDock(display, **args) def _createNewDisplay(self): max_index = 0 for i in self.display_list: if i.index > max_index: max_index = i.index l = max_index + 1 lastDims = self.show_dim_list[-1] new_display = _Display(l, lastDims[0], lastDims[1], self) self.display_list.append(new_display) self._addToArea(new_display.create()) new_display.update() return new_display def _createDisplays(self): self.area = pgDock.DockArea() self.win.setCentralWidget(self.area) self.display_list = [] for n,d in enumerate(self.show_dim_list): new_display = _Display(n, d[0], d[1], self) self.display_list.append(new_display) self._addToArea(new_display.create()) if self.build_preferences_dock: if not self._preferencesDock.exist:#if this dock doesnt exist self._preferencesDock.build(self.preferenceDock_size) else: self._preferencesDock.addToArea() def _removeDisplay(self, index): #####besser umsetzten - so viel zu umständlich - workarround dann schicken #display_container = self.display_list[index].dock.container() dock = self.display_list[index].dock area = self.area.addTempArea() area.win.resize(dock.size()) area.moveDock(dock, 'top', None) self.area.removeTempArea(area) self.display_list.pop(index) #self.area.floatDock(dock) #self.area. #self.area.addTempArea().win.resize(dock.size()) #display_container.apoptose() #self.display_list.pop(index) def _setFPS(self, fps): self.fps = float(fps) self.wait_ms = 1000/self.fps #frames per second try: self.timerFPS.setInterval(self.wait_ms) except AttributeError:#if timer is not defined yet pass def _setLPS(self, lps): self.lps = float(lps) if self.lps <= 0: self.lps = 1e-5 if self.limitReadoutRate: print "Set readout lines per second to 1e-5" if self.limitReadoutRate: self.wait_ms_lps = 1000/self.lps #lines per second else: self.wait_ms_lps = 0 try: self.timerLPS.setInterval(self.wait_ms_lps) except AttributeError:#if timer is not defined yet pass def _setColorTheme(self, colorTheme): '''choose the colortheme for the plot-output, possible values are: * "default" * "bright" ''' self.colorTheme = colorTheme if self.colorTheme == "default" or self.colorTheme == "": self.ticks_color = (200,200,200) self.label_color = '#fff' self.bg_color = 'k'#black elif self.colorTheme == "bright": self.ticks_color = (30,30,30) self.label_color = 'k' self.bg_color = 'w' pg.setConfigOption('background', self.bg_color) pg.setConfigOption('foreground', self.label_color)
class _forkedDock(pgDock.Dock): '''adding function: setWidget to normal Dock-class''' def __init__(self, name, area=None, size=(10, 10), widget=None, hideTitle=False, autoOrientation=True): super(_forkedDock, self).__init__(name, area, size, widget, hideTitle, autoOrientation) def setWidget(self, widget, index=0, row=None, col=0, rowspan=1, colspan=1): """ Add a new widget to the interior of this Dock. Each Dock uses a QGridLayout to arrange widgets within. """ if row is None: row = self.currentRow self.currentRow = max(row+1, self.currentRow) if index > len(self.widgets)-1: #add ne widget self.widgets.append(widget) else:#change existing widget self.layout.removeWidget(self.widgets[index]) self.widgets[index] = widget self.layout.addWidget(widget, row, col, rowspan, colspan) self.raiseOverlay() class _Display(object): ''' This class contains all attributes and methods that belongs to a displays widget. ''' def __init__(self, index, show_merge, show_basis, plotC): self.index = index #print show_merge,show_basis,888 self.show_merge = show_merge#als klassen - nicht indices self.show_basis = show_basis self.plotC = plotC self._basis_dim = self.plotC._basis_dim self._merge_dim = self.plotC._merge_dim self.enableAutoRangeX = "x" in self.plotC.enableAutoRange or "X" in self.plotC.enableAutoRange self.enableAutoRangeY = "Y" in self.plotC.enableAutoRange or "Y" in self.plotC.enableAutoRange self.mergeMatrix = self.plotC.matrixClass.mergeMatrix self.densityMatrix = self.plotC.matrixClass.densityMatrix self.basisMatrix = self.plotC.matrixClass.basisMatrix self.plot_overlay = self.plotC.matrixClass.plot_overlay self.colorList = ['r', 'g', 'b', 'c', 'm', 'y', 'k', 'w'] self.plotOverlay_points = [] self.plotOverlay_brokenLines = [] self.plotOverlay_lines = [] self.dock = _forkedDock(str(self.index), size=(self.plotC.display_size, 1)) #self.dock = pgDock.Dock(str(self.index), size=(self.plotC.plot_size_x, 1)) self.isFirstTime = True self.view = None self.title,self.title_opt = "",[] self.scale_plot = True self.basis_dim_plot_range = [] self.concentrate_basis_dim = [] self.transpose_axes = False self.show_merge_as_density = [] for n in range(len(self._basis_dim)): self.basis_dim_plot_range.append(self._basis_dim[n]._plot_range) self.concentrate_basis_dim.append("mean")#default option for n in range(len(self._merge_dim)): self.show_merge_as_density.append(False) #use_unit can be overridden to show e.g. the pointdensity self._merge_dim[n].use_unit = self._merge_dim[n].unit #max 2 basis-dim can be showed - the rest has to be concentrated if len(self.show_basis) > 2: self.show_basis = self.show_basis[:2] self.yAxis = pg.AxisItem("left") self.xAxis = pg.AxisItem("bottom") def create(self): ''' create a new content in the display ''' if len(self.show_basis) == 1:#1D self.plot = pg.PlotWidget(axisItems = {"left": self.yAxis, "bottom": self.xAxis}) self.view = self.plot.getPlotItem() self.plot.setXRange(self._basis_dim[self.show_basis[0]]._include_range[0], self._basis_dim[self.show_basis[0]]._include_range[1]) self.plot.addLegend() self._createCurves1D() else: #2D - image self.view = pg.PlotItem(axisItems = {"left": self.yAxis, "bottom": self.xAxis}) self.plot = pg.ImageView(view=self.view) self.view.setAspectLocked(False) #by default images have an inverted y-axis in pyqtgraph self.view.invertY(False) self._setTitle() self._setAxisLabels() self.dock.setWidget(self.plot) return self.dock def _changeTitleOpt(self, basis, newTitleOpt=None): #find index of old pos:#remove old entry for n,i in enumerate(self.title_opt): if "%s=" %basis.name in i: self.title_opt.pop(n) break if newTitleOpt != None: self.title_opt.append("%s=%s" %(basis.name, newTitleOpt) ) self._setTitle() def _setTitle(self): self.title = "" if len(self.show_basis)>1: self.title = "%s (%s)" %(self._merge_dim[self.show_merge[0]].name, self._merge_dim[self.show_merge[0]].use_unit) for i in self.title_opt: self.title += "{%s}" %i self.view.setTitle(title=self.title ) def _createCurves1D(self): #create all 1d-curves and ink in range of the colorlist self.plot.clear() try: self.plot.plotItem.legend.items = [] except AttributeError:#when ther is no legend pass self._setAxisLabels() self.curves = [] for n,i in enumerate(self.show_merge): self.curves.append(self.plot.plot( pen=self.colorList[n%len( self.colorList)], symbol='+', name=self._merge_dim[i].name)) def _setAxisLabels(self): if len(self.show_basis) == 1: axis_units = "" c=0 for i in self.show_merge: unit = self._merge_dim[i].use_unit if unit not in axis_units: axis_units += "%s " %unit c+=1 if c > 1: axis_units = "[ %s]" %axis_units else: axis_units = axis_units[:-1] if self.transpose_axes: self.xAxis.setLabel(text= '', units = axis_units) self.yAxis.setLabel(text=self._basis_dim[self.show_basis[0]].name, units = self._basis_dim[self.show_basis[0]].unit) else: self.yAxis.setLabel(text= '', units = axis_units) self.xAxis.setLabel(text=self._basis_dim[self.show_basis[0]].name, units = self._basis_dim[self.show_basis[0]].unit) else: if self.transpose_axes: self.yAxis.setLabel(text=self._basis_dim[self.show_basis[0]].name, units = self._basis_dim[self.show_basis[0]].unit) self.xAxis.setLabel(text=self._basis_dim[self.show_basis[1]].name, units = self._basis_dim[self.show_basis[1]].unit) else: self.xAxis.setLabel(text=self._basis_dim[self.show_basis[0]].name, units = self._basis_dim[self.show_basis[0]].unit) self.yAxis.setLabel(text=self._basis_dim[self.show_basis[1]].name, units = self._basis_dim[self.show_basis[1]].unit) def update(self): #get merge-extract for n,m in enumerate(self.show_merge): if self.show_merge_as_density[m]: self.merge_extract = self.densityMatrix[m][tuple(self.basis_dim_plot_range)] else: self.merge_extract = self.mergeMatrix[m][tuple(self.basis_dim_plot_range)] for b in range(len(self._basis_dim)-1,-1,-1): #basis dim to concentrate if b not in self.show_basis: pos_corr = self.concentrate_basis_dim[:b].count("pos") if self.concentrate_basis_dim[b] == "sum": self.merge_extract = bn.nansum(self.merge_extract,b-pos_corr) elif self.concentrate_basis_dim[b] == "mean": self.merge_extract = bn.nanmean(self.merge_extract,b-pos_corr) elif self.concentrate_basis_dim[b] == "max": self.merge_extract = bn.nanmax(self.merge_extract,b-pos_corr) elif self.concentrate_basis_dim[b] == "min": self.merge_extract = bn.nanmin(self.merge_extract,b-pos_corr) for b in range(len(self._basis_dim)-1,-1,-1): basis_time_index = None if b not in self.show_basis and self.concentrate_basis_dim[b] == "time": #reshape the matrix self.merge_extract = np.rollaxis(self.merge_extract,2,0) basis_time_index = b break # dont needto continue iterating because only one dim can be 'time' if len(self.show_basis) == 1: basis_extract = self.basisMatrix[self.show_basis[0]][self._basis_dim[self.show_basis[0]]._plot_range] #if self.plot_basis_as_merge[self.show_basis[0]]: ##change auto-scale-axes-names #x="y" #y="x" #else: #x='x' #y='y' if self.scale_plot == True: self.plot.enableAutoRange('xy', True) else: if self.enableAutoRangeX: self.plot.enableAutoRange('x', True) #self.plot.setXRange(self._basis_dim[self.show_basis[0]]._include_range[0], #self._basis_dim[self.show_basis[0]]._include_range[1]) if self.enableAutoRangeY: self.plot.enableAutoRange('y', True) if self.transpose_axes: self.curves[n].setData(self.merge_extract, basis_extract) else: self.curves[n].setData(basis_extract, self.merge_extract) elif len(self.show_basis) ==2: #calc scale and zero-position for axes-tics x0=self._basis_dim[self.show_basis[0]]._include_range[0] x1=self._basis_dim[self.show_basis[0]]._include_range[1] y0=self._basis_dim[self.show_basis[1]]._include_range[0] y1=self._basis_dim[self.show_basis[1]]._include_range[1] xscale = (x1-x0) / self._basis_dim[self.show_basis[0]].resolution yscale = (y1-y0) / self._basis_dim[self.show_basis[1]].resolution args = {'pos':[x0, y0], 'scale':[xscale, yscale]} if self.transpose_axes: args = {'pos':[y0, x0], 'scale':[yscale, xscale]} #set time-ticks if basis_time_index != None: args["xvals"] = self.basisMatrix[basis_time_index] if self.enableAutoRangeX: self.view.enableAutoRange('x', True) #self.view.setXRange(**tuple(self._basis_dim[self.show_basis[0]]._include_range))#[0], #self._basis_dim[self.show_basis[0]]._include_range[1]) if self.enableAutoRangeY: self.view.enableAutoRange('y', True) #bydefault autoLevel (the colorlevel of the merge-dims) == True #(calc. by pyqtgraph) #thus it only can process array without nan-values the calc. colorlevel #is wrong when the real values are boyond the nan-replacement(zero) #therefore i calc the colorlevel by my self in case nans arein the array: if bn.anynan(self.merge_extract): mmin = bn.nanmin(self.merge_extract) mmax = bn.nanmax(self.merge_extract) if np.isnan(mmin): mmin,mmax=0,0 self.plot.setLevels(mmin, mmax) args["autoLevels"]= False ##the following line dont work with my version of pyQtGraph #args["levels"]= (np.nanmin(merge_extract), np.nanmax(merge_extract)) self.merge_extract = _utils.nanToZeros(self.merge_extract) if self.transpose_axes: self.plot.setImage(self.merge_extract.transpose(), autoRange=self.scale_plot,**args) else: self.plot.setImage(self.merge_extract, autoRange=self.scale_plot,**args) else: sys.exit("ERRORsearchcode: 125873") self.scale_plot = False def addPlotOverlay(self): #plot_overlay = [(points), (lines), (broken lines), , (ellipses), (rectangles), (text), (legend)] #(points),(broken lines), (lines) = [x-list,y-list] #(ellipses) = list[ tuple(x,y), float(width), float(height), float(angle) ) ## str(color) ] #(rectangles) = list[ tuple(x,y), float(width), float(height) ] #(text) = list[ tuple(x,y), string(text) #Llegend) = list[str(...),... ] self.plotOverlay_legend = self.view.addLegend() for m in self.show_merge: if self.plot_overlay[m][0] != []:#draw points for i in self.plot_overlay[m][0]: self.plotOverlay_points.append( self.view.plot([i[0]],[i[1]],symbol='o', name=i[2])) if self.plot_overlay[m][1] != []:#draw broken lines for n,i in enumerate(self.plot_overlay[m][1]): self.plotOverlay_brokenLines.append( self.view.plot(i[0],i[1], name=i[2], pen=self.colorList[n%len(self.colorList)])) if self.plot_overlay[m][2] != []:#draw lines for n,i in enumerate(self.plot_overlay[m][2]): self.plotOverlay_lines.append( self.view.plot(i[0],i[1], name=i[2], pen=self.colorList[n%len(self.colorList)])) if self.plot_overlay[m][5] != []:#draw text self.plotOverlay_text = self.view.addLegend(offset=(-30, 30)) for i in self.plot_overlay[m][5]: self.plotOverlay_text.addItem(None, i) def removePlotOverlay(self): for i in (self.plotOverlay_points, self.plotOverlay_brokenLines,self.plotOverlay_lines): for j in i: self.view.removeItem(j) ##doest work at the moment ... wait for futher updates of pyQtGraph ##allowing removal of legends #self.view.removeItem(self.plotOverlay_legend) self.plotOverlay_points = [] self.plotOverlay_brokenLines = [] self.plotOverlay_lines = [] try: self.plotOverlay_text.items = [] except AttributeError: pass self.plotOverlay_legend = [] def _setCrosshair(self): #draw text for crosshair self.crosshair = pg.TextItem(text='', color=(0,0,0), html=None, anchor=(0, 0), border=None, fill='w', angle=0) #draw lines self.vLine = pg.InfiniteLine(angle=90, movable=False) self.hLine = pg.InfiniteLine(angle=0, movable=False) #add to viewBox self.view.addItem(self.vLine, ignoreBounds=True) self.view.addItem(self.hLine, ignoreBounds=True) self.view.addItem(self.crosshair) def mouseMoved(evt): if self.view.sceneBoundingRect().contains(evt.x(),evt.y()): mousePoint = self.view.vb.mapSceneToView(evt) indexX = mousePoint.x() indexY = mousePoint.y() #set text of crosshair if len(self.show_basis) == 1: self.crosshair.setText(" %s=%0.3f, %s=%0.3f" %("x",indexX, "y", indexY), color=(0,0,0)) elif len(self.show_basis) == 2: if self.transpose_axes: posX = _utils.nearestPosition( self.basisMatrix[self.show_basis[0]],indexY) posY = _utils.nearestPosition( self.basisMatrix[self.show_basis[1]],indexX) else: posX = _utils.nearestPosition( self.basisMatrix[self.show_basis[0]],indexX) posY = _utils.nearestPosition( self.basisMatrix[self.show_basis[1]],indexY) #better use the 'image' from imageView - because this is 2D #even if the orig. data is nD z_value = self.plot.image[posX][posY] self.crosshair.setText(" %s=%0.3f, %s=%0.3f, %s=%0.3f" %("x",indexX, "y", indexY, "z", z_value),color=(0,0,0) ) #(mousePoint.x(), data[index], data2[index])) else: self.crosshair.setText("nD not implemented jet",color=(0,0,0)) #move text to corner self.crosshair.setPos(self.view.vb.viewRange()[0][0], self.view.vb.viewRange()[1][1]) #move crosshair-lines to mousepos. self.vLine.setPos(indexX) self.hLine.setPos(indexY) self.view.vb.scene().sigMouseMoved.connect(mouseMoved) def _unsetCrosshair(self): self.view.removeItem(self.crosshair) self.view.removeItem(self.vLine) self.view.removeItem(self.hLine)