Ejemplo n.º 1
0
    sys.exit(-1)

from PyQt4 import QtCore, QtGui
from PyQt4.Qwt5 import QwtPlot, QwtPlotCurve
import pickle

data = sys.argv[1]

app = QtGui.QApplication(sys.argv)

p = QwtPlot()

curve = QwtPlotCurve("Segon i tal")

fn = resDir + '/' + data
f = file(fn)
v = pickle.load(f)

y = v
x = range(len(y))

curve.setData(x, y)

curve.attach(p)

p.replot()

p.show()

sys.exit(app.exec_())
Ejemplo n.º 2
0
class profileViewerDialog(QtGui.QDialog, FORM_CLASS):
    closeWindow = pyqtSignal()

    def __init__(self, parent=None):
        super(profileViewerDialog, self).__init__(parent)
        self.setupUi(self)

        self.TecFileList.setItemHidden(self.TecFileList.headerItem(), True)
        self.TecFileList.clear()
        self.setPlotWidget()
        self.setLayerBoxWidget()
        self.layerCombo.setEnabled(False)
        self.fieldCombo.setEnabled(False)
        self.mdl = QStandardItemModel(0, 6)
        self.resetBtn.clicked.connect(self.setRescale)
        self.methodSelector.currentIndexChanged.connect(
            self.lockMapLayerSelector)
        self.setIcons()
        self.settingDiag = exportSettings()
        self.batchChecker.setEnabled(False)

        self.exportSettingBtn.clicked.connect(self.openExportSettings)
        self.layerCombo.layerChanged.connect(self.fieldCombo.setLayer)

    def lockMapLayerSelector(self, idx):
        if idx == 0:
            self.layerCombo.setEnabled(False)
            self.reActivateBtn.setEnabled(True)
            self.batchChecker.setEnabled(False)
            self.fieldCombo.setEnabled(False)
        elif idx == 1:
            self.layerCombo.setEnabled(True)
            self.reActivateBtn.setEnabled(False)
            self.batchChecker.setEnabled(True)
            self.fieldCombo.setEnabled(True)

    def openExportSettings(self):
        self.settingDiag.run()

    def setIcons(self):
        pixMap = QPixmap(os.path.join(
            os.path.dirname(os.path.dirname(__file__)), 'settings.svg'))
        settingIcon = QIcon(pixMap)
        self.exportSettingBtn.setIcon(settingIcon)
        self.exportSettingBtn.setIconSize(0.085*pixMap.rect().size())

        pixMap2 = QPixmap(os.path.join(
            os.path.dirname(os.path.dirname(__file__)), 'if_arrow-up.svg'))
        icon2 = QIcon(pixMap2)
        self.moveForwardBtn.setIcon(icon2)
        self.moveForwardBtn.setIconSize(self.moveForwardBtn.size())

        pixMap3 = QPixmap(os.path.join(
            os.path.dirname(os.path.dirname(__file__)), 'if_arrow-down.svg'))
        icon3 = QIcon(pixMap3)
        self.moveBackwardBtn.setIcon(icon3)
        self.moveBackwardBtn.setIconSize(self.moveBackwardBtn.size())

    def setPlotWidget(self):
        self.plotWidget = QwtPlot(self.plotFrame)
        sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(1)
        sizePolicy.setHeightForWidth(
            self.plotWidget.sizePolicy().hasHeightForWidth())
        self.plotWidget.setSizePolicy(sizePolicy)
        self.plotWidget.setMinimumSize(QSize(0, 0))
        self.plotWidget.setAutoFillBackground(False)
        self.plotWidget.setCanvasBackground(Qt.white)
        self.plotWidget.plotLayout().setAlignCanvasToScales(True)

        zoomer = QwtPlotZoomer(QwtPlot.xBottom, QwtPlot.yLeft,
                               QwtPicker.DragSelection, QwtPicker.AlwaysOff,
                               self.plotWidget.canvas())
        zoomer.setRubberBandPen(QPen(Qt.blue))
        # The pen to draw zone of zooming.

        picker = QwtPlotPicker(QwtPlot.xBottom, QwtPlot.yLeft,
                               QwtPicker.NoSelection,
                               QwtPlotPicker.CrossRubberBand,
                               QwtPicker.AlwaysOn, self.plotWidget.canvas())
        picker.setTrackerPen(QPen(Qt.green))
        grid = QwtPlotGrid()
        grid.setPen(QPen(QColor('grey'), 0, Qt.DotLine))
        grid.attach(self.plotWidget)

        layout = self.plotLayout
        layout.addWidget(self.plotWidget)

    def setLayerBoxWidget(self):
        self.layerCombo = QgsMapLayerComboBox()
        self.layerCombo.setFilters(QgsMapLayerProxyModel.LineLayer)
        layout = self.layerComboLayout
        layout.addWidget(self.layerCombo)

        self.fieldCombo = QgsFieldComboBox()
        self.layoutForField.addWidget(self.fieldCombo)

    def setRescale(self):
        self.plotWidget.setAxisAutoScale(0)
        self.plotWidget.setAxisAutoScale(2)
        self.plotWidget.replot()

    def closeEvent(self, event):
        self.closeWindow.emit()

        # Delete the icons under plugin folder
        folderPath = os.path.dirname(__file__)
        folderPath = unicode(folderPath.decode('utf-8'))
        fileUnderFold = os.listdir(folderPath)

        for _file in fileUnderFold:
            if _file.endswith('.svg'):
                os.remove(os.path.join(folderPath, _file))
Ejemplo n.º 3
0
class ImageControlDialog(QDialog):
    def __init__(self, parent, rc, imgman):
        """An ImageControlDialog is initialized with a parent widget, a RenderControl object,
        and an ImageManager object"""
        QDialog.__init__(self, parent)
        image = rc.image
        self.setWindowTitle("%s: Colour Controls" % image.name)
        self.setWindowIcon(pixmaps.colours.icon())
        self.setModal(False)
        self.image = image
        self._rc = rc
        self._imgman = imgman
        self._currier = PersistentCurrier()

        # init internal state
        self._prev_range = self._display_range = None, None
        self._hist = None
        self._geometry = None

        # create layouts
        lo0 = QVBoxLayout(self)
        #    lo0.setContentsMargins(0,0,0,0)

        # histogram plot
        whide = self.makeButton("Hide", self.hide, width=128)
        whide.setShortcut(Qt.Key_F9)
        lo0.addWidget(Separator(self, "Histogram and ITF", extra_widgets=[whide]))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        self._histplot = QwtPlot(self)
        self._histplot.setAutoDelete(False)
        lo1.addWidget(self._histplot, 1)
        lo2 = QHBoxLayout()
        lo2.setContentsMargins(0, 0, 0, 0)
        lo2.setSpacing(2)
        lo0.addLayout(lo2)
        lo0.addLayout(lo1)
        self._wautozoom = QCheckBox("autozoom", self)
        self._wautozoom.setChecked(True)
        self._wautozoom.setToolTip("""<P>If checked, then the histrogram plot will zoom in automatically when
      you narrow the current intensity range.</P>""")
        self._wlogy = QCheckBox("log Y", self)
        self._wlogy.setChecked(True)
        self._ylogscale = True
        self._wlogy.setToolTip(
            """<P>If checked, a log-scale Y axis is used for the histogram plot instead of a linear one.""")
        QObject.connect(self._wlogy, SIGNAL("toggled(bool)"), self._setHistLogScale)
        self._whistunzoom = self.makeButton("", self._unzoomHistogram, icon=pixmaps.full_range.icon())
        self._whistzoomout = self.makeButton("-", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(.1)))
        self._whistzoomin = self.makeButton("+", self._currier.curry(self._zoomHistogramByFactor, math.sqrt(10)))
        self._whistzoomin.setToolTip("""<P>Click to zoom into the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistzoomout.setToolTip("""<P>Click to zoom out of the histogram plot by one step. This does not
      change the current intensity range.</P>""")
        self._whistunzoom.setToolTip("""<P>Click to reset the histogram plot back to its full extent.
      This does not change the current intensity range.</P>""")
        self._whistzoom = QwtWheel(self)
        self._whistzoom.setOrientation(Qt.Horizontal)
        self._whistzoom.setMaximumWidth(80)
        self._whistzoom.setRange(10, 0)
        self._whistzoom.setStep(0.1)
        self._whistzoom.setTickCnt(30)
        self._whistzoom.setTracking(False)
        QObject.connect(self._whistzoom, SIGNAL("valueChanged(double)"), self._zoomHistogramFinalize)
        QObject.connect(self._whistzoom, SIGNAL("sliderMoved(double)"), self._zoomHistogramPreview)
        self._whistzoom.setToolTip("""<P>Use this wheel control to zoom in/out of the histogram plot.
      This does not change the current intensity range.
      Note that the zoom wheel should also respond to your mouse wheel, if you have one.</P>""")
        # This works around a stupid bug in QwtSliders -- when using the mousewheel, only sliderMoved() signals are emitted,
        # with no final  valueChanged(). If we want to do a fast preview of something on sliderMoved(), and a "slow" final
        # step on valueChanged(), we're in trouble. So we start a timer on sliderMoved(), and if the timer expires without
        # anything else happening, do a valueChanged().
        # Here we use a timer to call zoomHistogramFinalize() w/o an argument.
        self._whistzoom_timer = QTimer(self)
        self._whistzoom_timer.setSingleShot(True)
        self._whistzoom_timer.setInterval(500)
        QObject.connect(self._whistzoom_timer, SIGNAL("timeout()"), self._zoomHistogramFinalize)
        # set same size for all buttons and controls
        width = 24
        for w in self._whistunzoom, self._whistzoomin, self._whistzoomout:
            w.setMinimumSize(width, width)
            w.setMaximumSize(width, width)
        self._whistzoom.setMinimumSize(80, width)
        self._wlab_histpos_text = "(hover here for help)"
        self._wlab_histpos = QLabel(self._wlab_histpos_text, self)
        self._wlab_histpos.setToolTip("""
      <P>The plot shows a histogram of either the full image or its selected subset
      (as per the "Data subset" section below).</P>
      <P>The current intensity range is indicated by the grey box
      in the plot.</P>
      <P>Use the left mouse button to change the low intensity limit, and the right
      button (on Macs, use Ctrl-click) to change the high limit.</P>
      <P>Use Shift with the left mouse button to zoom into an area of the histogram,
      or else use the "zoom wheel" control or the plus/minus toolbuttons above the histogram to zoom in or out.
      To zoom back out to the full extent of the histogram, click on the rightmost button above the histogram.</P>
      """)
        lo2.addWidget(self._wlab_histpos, 1)
        lo2.addWidget(self._wautozoom)
        lo2.addWidget(self._wlogy, 0)
        lo2.addWidget(self._whistzoomin, 0)
        lo2.addWidget(self._whistzoom, 0)
        lo2.addWidget(self._whistzoomout, 0)
        lo2.addWidget(self._whistunzoom, 0)
        self._zooming_histogram = False

        sliced_axes = rc.slicedAxes()
        dprint(1, "sliced axes are", sliced_axes)
        self._stokes_axis = None

        # subset indication
        lo0.addWidget(Separator(self, "Data subset"))
        # sliced axis selectors
        self._wslicers = []
        if sliced_axes:
            lo1 = QHBoxLayout()
            lo1.setContentsMargins(0, 0, 0, 0)
            lo1.setSpacing(2)
            lo0.addLayout(lo1)
            lo1.addWidget(QLabel("Current slice:  ", self))
            for i, (iextra, name, labels) in enumerate(sliced_axes):
                lo1.addWidget(QLabel("%s:" % name, self))
                if name == "STOKES":
                    self._stokes_axis = iextra
                # add controls
                wslicer = QComboBox(self)
                self._wslicers.append(wslicer)
                wslicer.addItems(labels)
                wslicer.setToolTip("""<P>Selects current slice along the %s axis.</P>""" % name)
                wslicer.setCurrentIndex(self._rc.currentSlice()[iextra])
                QObject.connect(wslicer, SIGNAL("activated(int)"), self._currier.curry(self._rc.changeSlice, iextra))
                lo2 = QVBoxLayout()
                lo1.addLayout(lo2)
                lo2.setContentsMargins(0, 0, 0, 0)
                lo2.setSpacing(0)
                wminus = QToolButton(self)
                wminus.setArrowType(Qt.UpArrow)
                QObject.connect(wminus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, 1))
                if i == 0:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F7)
                elif i == 1:
                    wminus.setShortcut(Qt.SHIFT + Qt.Key_F8)
                wplus = QToolButton(self)
                wplus.setArrowType(Qt.DownArrow)
                QObject.connect(wplus, SIGNAL("clicked()"), self._currier.curry(self._rc.incrementSlice, iextra, -1))
                if i == 0:
                    wplus.setShortcut(Qt.Key_F7)
                elif i == 1:
                    wplus.setShortcut(Qt.Key_F8)
                wminus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                wplus.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
                sz = QSize(12, 8)
                wminus.setMinimumSize(sz)
                wplus.setMinimumSize(sz)
                wminus.resize(sz)
                wplus.resize(sz)
                lo2.addWidget(wminus)
                lo2.addWidget(wplus)
                lo1.addWidget(wslicer)
                lo1.addSpacing(5)
            lo1.addStretch(1)
        # subset indicator
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1)
        self._wlab_subset = QLabel("Subset: xxx", self)
        self._wlab_subset.setToolTip("""<P>This indicates the current data subset to which the histogram
      and the stats given here apply. Use the "Reset to" control on the right to change the
      current subset and recompute the histogram and stats.</P>""")
        lo1.addWidget(self._wlab_subset, 1)

        self._wreset_full = self.makeButton("\u2192 full", self._rc.setFullSubset)
        lo1.addWidget(self._wreset_full)
        if sliced_axes:
            #      if self._stokes_axis is not None and len(sliced_axes)>1:
            #        self._wreset_stokes = self.makeButton(u"\u21920Stokes",self._rc.setFullSubset)
            self._wreset_slice = self.makeButton("\u2192 slice", self._rc.setSliceSubset)
            lo1.addWidget(self._wreset_slice)
        else:
            self._wreset_slice = None

        # min/max controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wlab_stats = QLabel(self)
        lo1.addWidget(self._wlab_stats, 0)
        self._wmore_stats = self.makeButton("more...", self._showMeanStd)
        self._wlab_stats.setMinimumHeight(self._wmore_stats.height())
        lo1.addWidget(self._wmore_stats, 0)
        lo1.addStretch(1)

        # intensity controls
        lo0.addWidget(Separator(self, "Intensity mapping"))
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo1.setSpacing(2)
        lo0.addLayout(lo1, 0)
        self._range_validator = FloatValidator(self)
        self._wrange = QLineEdit(self), QLineEdit(self)
        self._wrange[0].setToolTip("""<P>This is the low end of the intensity range.</P>""")
        self._wrange[1].setToolTip("""<P>This is the high end of the intensity range.</P>""")
        for w in self._wrange:
            w.setValidator(self._range_validator)
            QObject.connect(w, SIGNAL("editingFinished()"), self._changeDisplayRange)
        lo1.addWidget(QLabel("low:", self), 0)
        lo1.addWidget(self._wrange[0], 1)
        self._wrangeleft0 = self.makeButton("\u21920", self._setZeroLeftLimit, width=32)
        self._wrangeleft0.setToolTip("""<P>Click this to set the low end of the intensity range to 0.</P>""")
        lo1.addWidget(self._wrangeleft0, 0)
        lo1.addSpacing(8)
        lo1.addWidget(QLabel("high:", self), 0)
        lo1.addWidget(self._wrange[1], 1)
        lo1.addSpacing(8)
        self._wrange_full = self.makeButton(None, self._setHistDisplayRange, icon=pixmaps.intensity_graph.icon())
        lo1.addWidget(self._wrange_full)
        self._wrange_full.setToolTip(
            """<P>Click this to reset the intensity range to the current extent of the histogram plot.</P>""")
        # add menu for display range
        range_menu = QMenu(self)
        wrange_menu = QToolButton(self)
        wrange_menu.setText("Reset to")
        wrange_menu.setToolTip("""<P>Use this to reset the intensity range to various pre-defined settings.</P>""")
        lo1.addWidget(wrange_menu)
        self._qa_range_full = range_menu.addAction(pixmaps.full_range.icon(), "Full subset",
                                                   self._rc.resetSubsetDisplayRange)
        self._qa_range_hist = range_menu.addAction(pixmaps.intensity_graph.icon(), "Current histogram limits",
                                                   self._setHistDisplayRange)
        for percent in (99.99, 99.9, 99.5, 99, 98, 95):
            range_menu.addAction("%g%%" % percent, self._currier.curry(self._changeDisplayRangeToPercent, percent))
        wrange_menu.setMenu(range_menu)
        wrange_menu.setPopupMode(QToolButton.InstantPopup)

        lo1 = QGridLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        self._wimap = QComboBox(self)
        lo1.addWidget(QLabel("Intensity policy:", self), 0, 0)
        lo1.addWidget(self._wimap, 1, 0)
        self._wimap.addItems(rc.getIntensityMapNames())
        QObject.connect(self._wimap, SIGNAL("currentIndexChanged(int)"), self._rc.setIntensityMapNumber)
        self._wimap.setToolTip("""<P>Use this to change the type of the intensity transfer function (ITF).</P>""")

        # log cycles control
        lo1.setColumnStretch(1, 1)
        self._wlogcycles_label = QLabel("Log cycles: ", self)
        lo1.addWidget(self._wlogcycles_label, 0, 1)
        #    self._wlogcycles = QwtWheel(self)
        #    self._wlogcycles.setTotalAngle(360)
        self._wlogcycles = QwtSlider(self)
        self._wlogcycles.setToolTip(
            """<P>Use this to change the log-base for the logarithmic intensity transfer function (ITF).</P>""")
        # This works around a stupid bug in QwtSliders -- see comments on histogram zoom wheel above
        self._wlogcycles_timer = QTimer(self)
        self._wlogcycles_timer.setSingleShot(True)
        self._wlogcycles_timer.setInterval(500)
        QObject.connect(self._wlogcycles_timer, SIGNAL("timeout()"), self._setIntensityLogCycles)
        lo1.addWidget(self._wlogcycles, 1, 1)
        self._wlogcycles.setRange(1., 10)
        self._wlogcycles.setStep(0.1)
        self._wlogcycles.setTracking(False)
        QObject.connect(self._wlogcycles, SIGNAL("valueChanged(double)"), self._setIntensityLogCycles)
        QObject.connect(self._wlogcycles, SIGNAL("sliderMoved(double)"), self._previewIntensityLogCycles)
        self._updating_imap = False

        # lock intensity map
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 0)
        #    lo1.addWidget(QLabel("Lock range accross",self))
        wlock = QCheckBox("Lock display range", self)
        wlock.setToolTip("""<P>If checked, then the intensity range will be locked. The ranges of all locked images
      change simultaneously.</P>""")
        lo1.addWidget(wlock)
        wlockall = QToolButton(self)
        wlockall.setIcon(pixmaps.locked.icon())
        wlockall.setText("Lock all to this")
        wlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wlockall.setAutoRaise(True)
        wlockall.setToolTip("""<P>Click this to lock together the intensity ranges of all images.</P>""")
        lo1.addWidget(wlockall)
        wunlockall = QToolButton(self)
        wunlockall.setIcon(pixmaps.unlocked.icon())
        wunlockall.setText("Unlock all")
        wunlockall.setToolButtonStyle(Qt.ToolButtonTextBesideIcon)
        wunlockall.setAutoRaise(True)
        wunlockall.setToolTip("""<P>Click this to unlock the intensity ranges of all images.</P>""")
        lo1.addWidget(wunlockall)
        wlock.setChecked(self._rc.isDisplayRangeLocked())
        QObject.connect(wlock, SIGNAL("clicked(bool)"), self._rc.lockDisplayRange)
        QObject.connect(wlockall, SIGNAL("clicked()"),
                        self._currier.curry(self._imgman.lockAllDisplayRanges, self._rc))
        QObject.connect(wunlockall, SIGNAL("clicked()"), self._imgman.unlockAllDisplayRanges)
        QObject.connect(self._rc, SIGNAL("displayRangeLocked"), wlock.setChecked)

        #    self._wlock_imap_axis = [ QCheckBox(name,self) for iaxis,name,labels in sliced_axes ]
        #    for iw,w in enumerate(self._wlock_imap_axis):
        #      QObject.connect(w,SIGNAL("toggled(bool)"),self._currier.curry(self._rc.lockDisplayRangeForAxis,iw))
        #      lo1.addWidget(w,0)
        lo1.addStretch(1)

        # lo0.addWidget(Separator(self,"Colourmap"))
        # color bar
        self._colorbar = QwtPlot(self)
        lo0.addWidget(self._colorbar)
        self._colorbar.setAutoDelete(False)
        self._colorbar.setMinimumHeight(32)
        self._colorbar.enableAxis(QwtPlot.yLeft, False)
        self._colorbar.enableAxis(QwtPlot.xBottom, False)
        # color plot
        self._colorplot = QwtPlot(self)
        lo0.addWidget(self._colorplot)
        self._colorplot.setAutoDelete(False)
        self._colorplot.setMinimumHeight(64)
        self._colorplot.enableAxis(QwtPlot.yLeft, False)
        self._colorplot.enableAxis(QwtPlot.xBottom, False)
        # self._colorplot.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Preferred)
        self._colorbar.hide()
        self._colorplot.hide()
        # color controls
        lo1 = QHBoxLayout()
        lo1.setContentsMargins(0, 0, 0, 0)
        lo0.addLayout(lo1, 1)
        lo1.addWidget(QLabel("Colourmap:", self))
        # colormap list
        ### NB: use setIconSize() and icons in QComboBox!!!
        self._wcolmaps = QComboBox(self)
        self._wcolmaps.setIconSize(QSize(128, 16))
        self._wcolmaps.setToolTip("""<P>Use this to select a different colourmap.</P>""")
        for cmap in self._rc.getColormapList():
            self._wcolmaps.addItem(QIcon(cmap.makeQPixmap(128, 16)), cmap.name)
        lo1.addWidget(self._wcolmaps)
        QObject.connect(self._wcolmaps, SIGNAL("activated(int)"), self._rc.setColorMapNumber)
        # add widgetstack for colormap controls
        self._wcolmap_control_stack = QStackedWidget(self)
        self._wcolmap_control_blank = QWidget(self._wcolmap_control_stack)
        self._wcolmap_control_stack.addWidget(self._wcolmap_control_blank)
        lo0.addWidget(self._wcolmap_control_stack)
        self._colmap_controls = []
        # add controls to stack
        for index, cmap in enumerate(self._rc.getColormapList()):
            if isinstance(cmap, Colormaps.ColormapWithControls):
                controls = cmap.makeControlWidgets(self._wcolmap_control_stack)
                self._wcolmap_control_stack.addWidget(controls)
                QObject.connect(cmap, SIGNAL("colormapChanged"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                QObject.connect(cmap, SIGNAL("colormapPreviewed"),
                                self._currier.curry(self._previewColormapParameters, index, cmap))
                self._colmap_controls.append(controls)
            else:
                self._colmap_controls.append(self._wcolmap_control_blank)

        # connect updates from renderControl and image
        self.image.connect(SIGNAL("slice"), self._updateImageSlice)
        QObject.connect(self._rc, SIGNAL("intensityMapChanged"), self._updateIntensityMap)
        QObject.connect(self._rc, SIGNAL("colorMapChanged"), self._updateColorMap)
        QObject.connect(self._rc, SIGNAL("dataSubsetChanged"), self._updateDataSubset)
        QObject.connect(self._rc, SIGNAL("displayRangeChanged"), self._updateDisplayRange)

        # update widgets
        self._setupHistogramPlot()
        self._updateDataSubset(*self._rc.currentSubset())
        self._updateColorMap(image.colorMap())
        self._updateIntensityMap(rc.currentIntensityMap(), rc.currentIntensityMapNumber())
        self._updateDisplayRange(*self._rc.displayRange())

    def makeButton(self, label, callback=None, width=None, icon=None):
        btn = QToolButton(self)
        #    btn.setAutoRaise(True)
        label and btn.setText(label)
        icon and btn.setIcon(icon)
        #    btn = QPushButton(label,self)
        #   btn.setFlat(True)
        if width:
            btn.setMinimumWidth(width)
            btn.setMaximumWidth(width)
        if icon:
            btn.setIcon(icon)
        if callback:
            QObject.connect(btn, SIGNAL("clicked()"), callback)
        return btn

    #  def closeEvent (self,ev):
    #    ev.ignore()
    #    self.hide()

    def hide(self):
        self._geometry = self.geometry()
        QDialog.hide(self)

    def show(self):
        dprint(4, "show entrypoint")
        if self._geometry:
            dprint(4, "setting geometry")
            self.setGeometry(self._geometry)
        if self._hist is None:
            busy = BusyIndicator()
            dprint(4, "updating histogram")
            self._updateHistogram()
            dprint(4, "updating stats")
            self._updateStats(self._subset, self._subset_range)
            busy = None
        dprint(4, "calling QDialog.show")
        QDialog.show(self)

    # number of bins used to compute intensity transfer function
    NumItfBins = 1000
    # number of bins used for displaying histograms
    NumHistBins = 500
    # number of bins used for high-res histograms
    NumHistBinsHi = 10000
    # colorbar height, as fraction of plot area
    ColorBarHeight = 0.1

    class HistLimitPicker(QwtPlotPicker):
        """Auguments QwtPlotPicker with functions for selecting hist min/max values"""

        def __init__(self, plot, label, color="green", mode=QwtPicker.PointSelection,
                     rubber_band=QwtPicker.VLineRubberBand, tracker_mode=QwtPicker.ActiveOnly, track=None):
            QwtPlotPicker.__init__(self, QwtPlot.xBottom, QwtPlot.yRight, mode, rubber_band, tracker_mode,
                                   plot.canvas())
            self.plot = plot
            self.label = label
            self.track = track
            self.color = QColor(color)
            self.setRubberBandPen(QPen(self.color))

        def trackerText(self, pos):
            x, y = self.plot.invTransform(QwtPlot.xBottom, pos.x()), self.plot.invTransform(QwtPlot.yLeft, pos.y())
            if self.track:
                text = self.track(x, y)
                if text is not None:
                    return text
            if self.label:
                text = QwtText(self.label % dict(x=x, y=y))
                text.setColor(self.color)
                return text
            return QwtText()

        def widgetLeaveEvent(self, ev):
            if self.track:
                self.track(None, None)
            QwtPlotPicker.widgetLeaveEvent(self, ev)

    class ColorBarPlotItem(QwtPlotItem):
        def __init__(self, y0, y1, *args):
            QwtPlotItem.__init__(self, *args)
            self._y0 = y1
            self._dy = y1 - y0

        def setIntensityMap(self, imap):
            self.imap = imap

        def setColorMap(self, cmap):
            self.cmap = cmap

        def draw(self, painter, xmap, ymap, rect):
            """Implements QwtPlotItem.draw(), to render the colorbar on the given painter."""
            xp1, xp2, xdp, xs1, xs2, xds = xinfo = xmap.p1(), xmap.p2(), xmap.pDist(), xmap.s1(), xmap.s2(), xmap.sDist()
            yp1, yp2, ydp, ys1, ys2, yds = yinfo = ymap.p1(), ymap.p2(), ymap.pDist(), ymap.s1(), ymap.s2(), ymap.sDist()
            # xp: coordinates of pixels xp1...xp2 in data units
            xp = xs1 + (xds / xdp) * (0.5 + numpy.arange(int(xdp)))
            # convert y0 and y1 into pixel coordinates
            y0 = yp1 - (self._y0 - ys1) * (ydp / yds)
            dy = self._dy * (ydp / yds)
            # remap into an Nx1 image
            qimg = self.cmap.colorize(self.imap.remap(xp.reshape((len(xp), 1))))
            # plot image
            painter.drawImage(QRect(xp1, y0, xdp, dy), qimg)

    class HistogramLineMarker(object):
        """Helper class implementing a line marker for a histogram plot"""

        def __init__(self, plot, color="black", linestyle=Qt.DotLine, align=Qt.AlignBottom | Qt.AlignRight, z=90,
                     label="", zlabel=None, linewidth=1, spacing=2,
                     yaxis=QwtPlot.yRight):
            self.line = TiggerPlotCurve()
            self.color = color = color if isinstance(color, QColor) else QColor(color)
            self.line.setPen(QPen(color, linewidth, linestyle))
            self.marker = TiggerPlotMarker()
            self.marker.setLabelAlignment(align)
            try:
                self.marker.setSpacing(spacing)
            except AttributeError:
                pass
            self.setText(label)
            self.line.setZ(z)
            self.marker.setZ(zlabel if zlabel is not None else z)
            # set axes -- using yRight, since that is the "markup" z-axis
            self.line.setAxis(QwtPlot.xBottom, yaxis)
            self.marker.setAxis(QwtPlot.xBottom, yaxis)
            # attach to plot
            self.line.attach(plot)
            self.marker.attach(plot)

        def show(self):
            self.line.show()
            self.marker.show()

        def hide(self):
            self.line.hide()
            self.marker.hide()

        def setText(self, text):
            label = QwtText(text)
            label.setColor(self.color)
            self.marker.setLabel(label)

    def _setupHistogramPlot(self):
        self._histplot.setCanvasBackground(QColor("lightgray"))
        self._histplot.setAxisFont(QwtPlot.yLeft, QApplication.font())
        self._histplot.setAxisFont(QwtPlot.xBottom, QApplication.font())
        # add histogram curves
        self._histcurve1 = TiggerPlotCurve()
        self._histcurve2 = TiggerPlotCurve()
        self._histcurve1.setStyle(QwtPlotCurve.Steps)
        self._histcurve2.setStyle(QwtPlotCurve.Steps)
        self._histcurve1.setPen(QPen(Qt.NoPen))
        self._histcurve1.setBrush(QBrush(QColor("slategrey")))
        pen = QPen(QColor("red"))
        pen.setWidth(1)
        self._histcurve2.setPen(pen)
        self._histcurve1.setZ(0)
        self._histcurve2.setZ(100)
        #    self._histcurve1.attach(self._histplot)
        self._histcurve2.attach(self._histplot)
        # add maxbin and half-max curves
        self._line_0 = self.HistogramLineMarker(self._histplot, color="grey50", linestyle=Qt.SolidLine,
                                                align=Qt.AlignTop | Qt.AlignLeft, z=90)
        self._line_mean = self.HistogramLineMarker(self._histplot, color="black", linestyle=Qt.SolidLine,
                                                   align=Qt.AlignBottom | Qt.AlignRight, z=91,
                                                   label="mean", zlabel=151)
        self._line_std = self.HistogramLineMarker(self._histplot, color="black", linestyle=Qt.SolidLine,
                                                  align=Qt.AlignTop | Qt.AlignRight, z=91,
                                                  label="std", zlabel=151)
        sym = QwtSymbol()
        sym.setStyle(QwtSymbol.VLine)
        sym.setSize(8)
        self._line_std.line.setSymbol(sym)
        self._line_maxbin = self.HistogramLineMarker(self._histplot, color="green", linestyle=Qt.DotLine,
                                                     align=Qt.AlignTop | Qt.AlignRight, z=92,
                                                     label="max bin", zlabel=150)
        self._line_halfmax = self.HistogramLineMarker(self._histplot, color="green", linestyle=Qt.DotLine,
                                                      align=Qt.AlignBottom | Qt.AlignRight, z=90,
                                                      label="half-max", yaxis=QwtPlot.yLeft)
        # add current range
        self._rangebox = TiggerPlotCurve()
        self._rangebox.setStyle(QwtPlotCurve.Steps)
        self._rangebox.setYAxis(QwtPlot.yRight)
        self._rangebox.setPen(QPen(Qt.NoPen))
        self._rangebox.setBrush(QBrush(QColor("darkgray")))
        self._rangebox.setZ(50)
        self._rangebox.attach(self._histplot)
        self._rangebox2 = TiggerPlotCurve()
        self._rangebox2.setStyle(QwtPlotCurve.Sticks)
        self._rangebox2.setYAxis(QwtPlot.yRight)
        self._rangebox2.setZ(60)
        #  self._rangebox2.attach(self._histplot)
        # add intensity transfer function
        self._itfcurve = TiggerPlotCurve()
        self._itfcurve.setStyle(QwtPlotCurve.Lines)
        self._itfcurve.setPen(QPen(QColor("blue")))
        self._itfcurve.setYAxis(QwtPlot.yRight)
        self._itfcurve.setZ(120)
        self._itfcurve.attach(self._histplot)
        self._itfmarker = TiggerPlotMarker()
        label = QwtText("ITF")
        label.setColor(QColor("blue"))
        self._itfmarker.setLabel(label)
        try:
            self._itfmarker.setSpacing(0)
        except AttributeError:
            pass
        self._itfmarker.setLabelAlignment(Qt.AlignTop | Qt.AlignRight)
        self._itfmarker.setZ(120)
        self._itfmarker.attach(self._histplot)
        # add colorbar
        self._cb_item = self.ColorBarPlotItem(1, 1 + self.ColorBarHeight)
        self._cb_item.setYAxis(QwtPlot.yRight)
        self._cb_item.attach(self._histplot)
        # add pickers
        self._hist_minpicker = self.HistLimitPicker(self._histplot, "low: %(x).4g")
        self._hist_minpicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton)
        QObject.connect(self._hist_minpicker, SIGNAL("selected(const QwtDoublePoint &)"), self._selectLowLimit)
        self._hist_maxpicker = self.HistLimitPicker(self._histplot, "high: %(x).4g")
        self._hist_maxpicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.RightButton)
        QObject.connect(self._hist_maxpicker, SIGNAL("selected(const QwtDoublePoint &)"), self._selectHighLimit)
        self._hist_maxpicker1 = self.HistLimitPicker(self._histplot, "high: %(x).4g")
        self._hist_maxpicker1.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton, Qt.CTRL)
        QObject.connect(self._hist_maxpicker1, SIGNAL("selected(const QwtDoublePoint &)"), self._selectHighLimit)
        self._hist_zoompicker = self.HistLimitPicker(self._histplot, label="zoom",
                                                     tracker_mode=QwtPicker.AlwaysOn, track=self._trackHistCoordinates,
                                                     color="black",
                                                     mode=QwtPicker.RectSelection,
                                                     rubber_band=QwtPicker.RectRubberBand)
        self._hist_zoompicker.setMousePattern(QwtEventPattern.MouseSelect1, Qt.LeftButton, Qt.SHIFT)
        QObject.connect(self._hist_zoompicker, SIGNAL("selected(const QwtDoubleRect &)"), self._zoomHistogramIntoRect)

    def _trackHistCoordinates(self, x, y):
        self._wlab_histpos.setText((DataValueFormat + " %d") % (x, y) if x is not None else self._wlab_histpos_text)
        return QwtText()

    def _updateITF(self):
        """Updates current ITF array."""
        # do nothing if no histogram -- means we're not visible
        if self._hist is not None:
            xdata = self._itf_bins
            ydata = self.image.intensityMap().remap(xdata)
            self._rangebox.setData(self._rc.displayRange(), [1, 1])
            self._rangebox2.setData(self._rc.displayRange(), [1, 1])
            self._itfcurve.setData(xdata, ydata)
            self._itfmarker.setValue(xdata[0], 1)

    def _updateHistogram(self, hmin=None, hmax=None):
        """Recomputes histogram. If no arguments, computes full histogram for
        data subset. If hmin/hmax is specified, computes zoomed-in histogram."""
        busy = BusyIndicator()
        self._prev_range = self._display_range
        dmin, dmax = self._subset_range
        hmin0, hmax0 = dmin, dmax
        if hmin0 >= hmax0:
            hmax0 = hmin0 + 1
        subset, mask = self.image.optimalRavel(self._subset)
        # compute full-subset hi-res histogram, if we don't have one (for percentile stats)
        if self._hist_hires is None:
            dprint(1, "computing histogram for full subset range", hmin0, hmax0)
            self._hist_hires = measurements.histogram(subset, hmin0, hmax0, self.NumHistBinsHi, labels=mask,
                                                      index=None if mask is None else False)
            self._hist_bins_hires = hmin0 + (hmax0 - hmin0) * (numpy.arange(self.NumHistBinsHi) + 0.5) / float(
                self.NumHistBinsHi)
            self._hist_binsize_hires = (hmax0 - hmin0) / self.NumHistBins
        # if hist limits not specified, then compute lo-res histogram based on the hi-res one
        if hmin is None:
            hmin, hmax = hmin0, hmax0
            # downsample to low-res histogram
            self._hist = self._hist_hires.reshape((self.NumHistBins, self.NumHistBinsHi / self.NumHistBins)).sum(1)
        else:
            # zoomed-in low-res histogram
            # bracket limits at subset range
            hmin, hmax = max(hmin, dmin), min(hmax, dmax)
            if hmin >= hmax:
                hmax = hmin + 1
            dprint(1, "computing histogram for", self._subset.shape, self._subset.dtype, hmin, hmax)
            self._hist = measurements.histogram(subset, hmin, hmax, self.NumHistBins, labels=mask,
                                                index=None if mask is None else False)
        dprint(1, "histogram computed")
        # compute bins
        self._itf_bins = hmin + (hmax - hmin) * (numpy.arange(self.NumItfBins)) / (float(self.NumItfBins) - 1)
        self._hist_bins = hmin + (hmax - hmin) * (numpy.arange(self.NumHistBins) + 0.5) / float(self.NumHistBins)
        # histogram range and position of peak
        self._hist_range = hmin, hmax
        self._hist_min, self._hist_max, self._hist_imin, self._hist_imax = measurements.extrema(self._hist)
        self._hist_peak = self._hist_bins[self._hist_imax]
        # set controls accordingly
        if dmin >= dmax:
            dmax = dmin + 1
        zoom = math.log10((dmax - dmin) / (hmax - hmin))
        self._whistzoom.setValue(zoom)
        self._whistunzoom.setEnabled(zoom > 0)
        self._whistzoomout.setEnabled(zoom > 0)
        # reset scales
        self._histplot.setAxisScale(QwtPlot.xBottom, hmin, hmax)
        self._histplot.setAxisScale(QwtPlot.yRight, 0, 1 + self.ColorBarHeight)
        # update curves
        # call _setHistLogScale() (with current setting) to update axis scales and set data
        self._setHistLogScale(self._ylogscale, replot=False)
        # set plot lines
        self._line_0.line.setData([0, 0], [0, 1])
        self._line_0.marker.setValue(0, 0)
        self._line_maxbin.line.setData([self._hist_peak, self._hist_peak], [0, 1])
        self._line_maxbin.marker.setValue(self._hist_peak, 0)
        self._line_maxbin.setText(("max bin:" + DataValueFormat) % self._hist_peak)
        # set half-max line
        self._line_halfmax.line.setData(self._hist_range, [self._hist_max / 2, self._hist_max / 2])
        self._line_halfmax.marker.setValue(hmin, self._hist_max / 2)
        # update ITF
        self._updateITF()

    def _updateStats(self, subset, minmax):
        """Recomputes subset statistics."""
        if subset.size <= (2048 * 2048):
            self._showMeanStd(busy=False)
        else:
            self._wlab_stats.setText(
                ("min: %s  max: %s  np: %d" % (DataValueFormat, DataValueFormat, self._subset.size)) % minmax)
            self._wmore_stats.show()

    def _updateDataSubset(self, subset, minmax, desc, subset_type):
        """Called when the displayed data subset is changed. Updates the histogram."""
        self._subset = subset
        self._subset_range = minmax
        self._wlab_subset.setText("Subset: %s" % desc)
        self._hist = self._hist_hires = None
        self._wreset_full.setVisible(subset_type is not RenderControl.SUBSET_FULL)
        self._wreset_slice and self._wreset_slice.setVisible(subset_type is not RenderControl.SUBSET_SLICE)
        # hide the mean/std markers, they will only be shown when _showMeanStd() is called
        self._line_mean.hide()
        self._line_std.hide()
        # if we're visibile, recompute histograms and stats
        if self.isVisible():
            # if subset is sufficiently small, compute extended stats on-the-fly. Else show the "more" button to compute them later
            self._updateHistogram()
            self._updateStats(subset, minmax)
            self._histplot.replot()

    def _showMeanStd(self, busy=True):
        if busy:
            busy = BusyIndicator()
        dmin, dmax = self._subset_range
        subset, mask = self.image.optimalRavel(self._subset)
        dprint(5, "computing mean")
        mean = measurements.mean(subset, labels=mask, index=None if mask is None else False)
        dprint(5, "computing std")
        std = measurements.standard_deviation(subset, labels=mask, index=None if mask is None else False)
        dprint(5, "done")
        text = "  ".join([("%s: " + DataValueFormat) % (name, value) for name, value in
                          ("min", dmin), ("max", dmax), ("mean", mean), ("std", std)] + ["np: %d" % self._subset.size])
        self._wlab_stats.setText(text)
        self._wmore_stats.hide()
        # update markers
        ypos = 0.3
        self._line_mean.line.setData([mean, mean], [0, 1])
        self._line_mean.marker.setValue(mean, ypos)
        self._line_mean.setText(("\u03BC=" + DataValueFormat) % mean)
        self._line_mean.show()
        self._line_std.line.setData([mean - std, mean + std], [ypos, ypos])
        self._line_std.marker.setValue(mean, ypos)
        self._line_std.setText(("\u03C3=" + DataValueFormat) % std)
        self._line_std.show()
        self._histplot.replot()

    def _setIntensityLogCyclesLabel(self, value):
        self._wlogcycles_label.setText("Log cycles: %4.1f" % value)

    def _previewIntensityLogCycles(self, value):
        self._setIntensityLogCycles(value, notify_image=False, write_config=False)
        self._wlogcycles_timer.start(500)

    def _setIntensityLogCycles(self, value=None, notify_image=True, write_config=True):
        if value is None:
            value = self._wlogcycles.value()
        # stop timer if being called to finalize the change in value
        if notify_image:
            self._wlogcycles_timer.stop()
        if not self._updating_imap:
            self._setIntensityLogCyclesLabel(value)
            self._rc.setIntensityMapLogCycles(value, notify_image=notify_image, write_config=write_config)
            self._updateITF()
            self._histplot.replot()

    def _updateDisplayRange(self, dmin, dmax):
        self._rangebox.setData([dmin, dmax], [.9, .9])
        self._wrange[0].setText(DataValueFormat % dmin)
        self._wrange[1].setText(DataValueFormat % dmax)
        self._wrangeleft0.setEnabled(dmin != 0)
        self._display_range = dmin, dmax
        # if auto-zoom is on, zoom the histogram
        # try to be a little clever about this. Zoom only if (a) both limits have changed (so that adjusting one end of the range
        # does not cause endless rezooms), or (b) display range is < 1/10 of the histogram range
        if self._wautozoom.isChecked() and self._hist is not None:
            if (dmax - dmin) / (self._hist_range[1] - self._hist_range[0]) < .1 or (
                    dmin != self._prev_range[0] and dmax != self._prev_range[1]):
                margin = (dmax - dmin) / 8
                self._updateHistogram(dmin - margin, dmax + margin)
        self._updateITF()
        self._histplot.replot()

    def _updateIntensityMap(self, imap, index):
        self._updating_imap = True
        try:
            self._cb_item.setIntensityMap(imap)
            self._updateITF()
            self._histplot.replot()
            self._wimap.setCurrentIndex(index)
            if isinstance(imap, Colormaps.LogIntensityMap):
                self._wlogcycles.setValue(imap.log_cycles)
                self._setIntensityLogCyclesLabel(imap.log_cycles)
                self._wlogcycles.show()
                self._wlogcycles_label.show()
            else:
                self._wlogcycles.hide()
                self._wlogcycles_label.hide()
        finally:
            self._updating_imap = False

    def _updateColorMap(self, cmap):
        self._cb_item.setColorMap(cmap)
        self._histplot.replot()
        try:
            index = self._rc.getColormapList().index(cmap)
        except:
            return
        self._setCurrentColormapNumber(index, cmap)

    def _previewColormapParameters(self, index, cmap):
        """Called to preview a new colormap parameter value"""
        self._histplot.replot()
        self._wcolmaps.setItemIcon(index, QIcon(cmap.makeQPixmap(128, 16)))

    def _setCurrentColormapNumber(self, index, cmap):
        self._wcolmaps.setCurrentIndex(index)
        # show controls for colormap
        self._wcolmap_control_stack.setCurrentWidget(self._colmap_controls[index])

    def _changeDisplayRange(self):
        """Gets display range from widgets and updates the image with it."""
        try:
            newrange = [float(str(w.text())) for w in self._wrange]
        except ValueError:
            return
        self._rc.setDisplayRange(*newrange)

    def _setHistDisplayRange(self):
        self._rc.setDisplayRange(*self._hist_range)

    def _updateImageSlice(self, slice):
        for i, (iextra, name, labels) in enumerate(self._rc.slicedAxes()):
            self._wslicers[i].setCurrentIndex(slice[iextra])

    def _changeDisplayRangeToPercent(self, percent):
        busy = BusyIndicator()
        if self._hist is None:
            self._updateHistogram()
            self._updateStats(self._subset, self._subset_range)
        # delta: we need the [delta,100-delta] interval of the total distribution
        delta = self._subset.size * ((100. - percent) / 200.)
        # get F(x): cumulative sum
        cumsum = numpy.zeros(len(self._hist_hires) + 1, dtype=int)
        cumsum[1:] = numpy.cumsum(self._hist_hires)
        bins = numpy.zeros(len(self._hist_hires) + 1, dtype=float)
        bins[0] = self._subset_range[0]
        bins[1:] = self._hist_bins_hires + self._hist_binsize_hires / 2
        # use interpolation to find value interval corresponding to [delta,100-delta] of the distribution
        dprint(2, self._subset.size, delta, self._subset.size - delta)
        dprint(2, cumsum, self._hist_bins_hires)
        # if first bin is already > delta, then set colour range to first bin
        x0, x1 = numpy.interp([delta, self._subset.size - delta], cumsum, bins)
        # and change the display range (this will also cause a histplot.replot() via _updateDisplayRange above)
        self._rc.setDisplayRange(x0, x1)

    def _setZeroLeftLimit(self):
        self._rc.setDisplayRange(0., self._rc.displayRange()[1])

    def _selectLowLimit(self, pos):
        self._rc.setDisplayRange(pos.x(), self._rc.displayRange()[1])

    def _selectHighLimit(self, pos):
        self._rc.setDisplayRange(self._rc.displayRange()[0], pos.x())

    def _unzoomHistogram(self):
        self._updateHistogram()
        self._histplot.replot()

    def _zoomHistogramByFactor(self, factor):
        """Changes histogram limits by specified factor"""
        # get max distance of plot limit from peak
        dprint(1, "zooming histogram by", factor)
        halfdist = (self._hist_range[1] - self._hist_range[0]) / (factor * 2)
        self._updateHistogram(self._hist_peak - halfdist, self._hist_peak + halfdist)
        self._histplot.replot()

    def _zoomHistogramIntoRect(self, rect):
        hmin, hmax = rect.bottomLeft().x(), rect.bottomRight().x()
        if hmax > hmin:
            self._updateHistogram(rect.bottomLeft().x(), rect.bottomRight().x())
            self._histplot.replot()

    def _zoomHistogramPreview(self, value):
        dprint(2, "wheel moved to", value)
        self._zoomHistogramFinalize(value, preview=True)
        self._whistzoom_timer.start()

    def _zoomHistogramFinalize(self, value=None, preview=False):
        if self._zooming_histogram:
            return
        self._zooming_histogram = True
        try:
            if value is not None:
                dmin, dmax = self._subset_range
                dist = max(dmax - self._hist_peak, self._hist_peak - dmin) / 10 ** value
                self._preview_hist_range = max(self._hist_peak - dist, dmin), min(self._hist_peak + dist, dmax)
            if preview:
                self._histplot.setAxisScale(QwtPlot.xBottom, *self._preview_hist_range)
            else:
                dprint(2, "wheel finalized at", value)
                self._whistzoom_timer.stop()
                self._updateHistogram(*self._preview_hist_range)
            self._histplot.replot()
        finally:
            self._zooming_histogram = False

    def _setHistLogScale(self, logscale, replot=True):
        self._ylogscale = logscale
        if logscale:
            self._histplot.setAxisScaleEngine(QwtPlot.yLeft, QwtLog10ScaleEngine())
            ymax = max(1, self._hist_max)
            self._histplot.setAxisScale(QwtPlot.yLeft, 1, 10 ** (math.log10(ymax) * (1 + self.ColorBarHeight)))
            y = self._hist.copy()
            y[y == 0] = 1
            self._histcurve1.setData(self._hist_bins, y)
            self._histcurve2.setData(self._hist_bins, y)
        else:
            self._histplot.setAxisScaleEngine(QwtPlot.yLeft, QwtLinearScaleEngine())
            self._histplot.setAxisScale(QwtPlot.yLeft, 0, self._hist_max * (1 + self.ColorBarHeight))
            self._histcurve1.setData(self._hist_bins, self._hist)
            self._histcurve2.setData(self._hist_bins, self._hist)
        if replot:
            self._histplot.replot()
Ejemplo n.º 4
0
class RTTView(QtGui.QWidget, Ui_RTTView):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        self.setupUi(self)

        self.initSetting()

        self.initQwtPlot()

        self.daplink = None

        self.tmrDAP = QtCore.QTimer()
        self.tmrDAP.setInterval(10)
        self.tmrDAP.timeout.connect(self.on_tmrDAP_timeout)
        self.tmrDAP.start()

        self.tmrCntr = 0  # tmrDAP超时一次,tmrCntr加一

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w')

        self.conf = ConfigParser.ConfigParser()
        self.conf.read('setting.ini')

        if not self.conf.has_section('globals'):
            self.conf.add_section('globals')
            self.conf.set('globals', 'mappath', '[]')
        for path in eval(self.conf.get('globals', 'mappath')):
            self.cmbMap.insertItem(10, path)

    def initQwtPlot(self):
        self.PlotBuff = ''
        self.PlotData = [0] * 1000

        self.qwtPlot = QwtPlot(self)
        self.vLayout0.insertWidget(0, self.qwtPlot)

        self.PlotCurve = QwtPlotCurve()
        self.PlotCurve.attach(self.qwtPlot)
        self.PlotCurve.setData(range(1, len(self.PlotData) + 1), self.PlotData)

        self.on_cmbMode_currentIndexChanged(u'文本')

    def parseRTTAddr(self):
        with open(self.cmbMap.currentText(), 'r') as f:
            for line in f:
                match = re.match(
                    '\s+_SEGGER_RTT\s+(0x[0-9a-fA-F]{8})\s+Data.+', line)
                if match: return int(match.group(1), 16)
            else:
                raise Exception('Can not found _SEGGER_RTT')

    @QtCore.pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == u'打开连接':
            try:
                self.daplink.open()

                self.dp = coresight.dap.DebugPort(self.daplink)
                self.dp.init()
                self.dp.power_up_debug()

                self.ap = coresight.ap.AHB_AP(self.dp, 0)
                self.ap.init()

                self.RTTAddr = self.parseRTTAddr()
            except Exception as e:
                print e
            else:
                self.btnOpen.setText(u'关闭连接')
                self.lblOpen.setPixmap(QtGui.QPixmap("./Image/inopening.png"))
        else:
            try:
                self.daplink.close()
            except Exception as e:
                print e
            finally:
                self.btnOpen.setText(u'打开连接')
                self.lblOpen.setPixmap(QtGui.QPixmap("./Image/inclosing.png"))

    def aUpEmpty(self):
        LEN = (16 + 4 * 2) + (4 * 6) * 4

        buf = self.ap.readBlockMemoryUnaligned8(self.RTTAddr, LEN)

        arr = struct.unpack('16sLLLLLLLL24xLLLLLL24x',
                            ''.join([chr(x) for x in buf]))

        self.aUp = RingBuffer(arr[3:9])

        print 'WrOff=%d, RdOff=%d' % (self.aUp.WrOff, self.aUp.RdOff)

        self.aDown = RingBuffer(arr[9:15])

        return (self.aUp.RdOff == self.aUp.WrOff)

    def aUpRead(self):
        if self.aUp.RdOff < self.aUp.WrOff:
            len_ = self.aUp.WrOff - self.aUp.RdOff

            arr = self.ap.readBlockMemoryUnaligned8(
                self.aUp.pBuffer + self.aUp.RdOff, len_)

            self.aUp.RdOff += len_

            self.ap.write32(self.RTTAddr + (16 + 4 * 2) + 4 * 4,
                            self.aUp.RdOff)
        else:
            len_ = self.aUp.SizeOfBuffer - self.aUp.RdOff + 1

            arr = self.ap.readBlockMemoryUnaligned8(
                self.aUp.pBuffer + self.aUp.RdOff, len_)

            self.aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.ap.write32(self.RTTAddr + (16 + 4 * 2) + 4 * 4,
                            self.aUp.RdOff)

        return ''.join([chr(x) for x in arr])

    def on_tmrDAP_timeout(self):
        if self.btnOpen.text() == u'关闭连接':
            ss = ''
            try:
                if not self.aUpEmpty():
                    ss = self.aUpRead()
            except Exception as e:
                pass

            if ss:
                if self.mode == u'文本':
                    if len(self.txtMain.toPlainText()) > 50000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(ss)

                elif self.mode == u'波形':
                    self.PlotBuff += ss
                    if self.PlotBuff.rfind(',') == -1: return
                    try:
                        d = [
                            int(x) for x in self.
                            PlotBuff[0:self.PlotBuff.rfind(',')].split(',')
                        ]
                        for x in d:
                            self.PlotData.pop(0)
                            self.PlotData.append(x)
                    except:
                        self.PlotBuff = ''
                    else:
                        self.PlotBuff = self.PlotBuff[self.PlotBuff.
                                                      rfind(',') + 1:]

                    self.PlotCurve.setData(range(1,
                                                 len(self.PlotData) + 1),
                                           self.PlotData)
                    self.qwtPlot.replot()

        self.detect_daplink()  # 自动检测 DAPLink 的热插拔

    def detect_daplink(self):
        daplinks = pyDAPAccess.DAPAccess.get_connected_devices()

        if self.daplink and (daplinks == []):  # daplink被拔下
            try:
                self.daplink.close()
            except Exception as e:
                print e
            finally:
                self.daplink = None
                self.linDAP.clear()

                self.btnOpen.setText(u'打开连接')
                self.lblOpen.setPixmap(QtGui.QPixmap("./Image/inclosing.png"))

        if not self.daplink and daplinks != []:
            self.daplink = daplinks[0]

            self.linDAP.clear()
            self.linDAP.setText(self.daplink._product_name)

    @QtCore.pyqtSlot()
    def on_btnMap_clicked(self):
        path = QtGui.QFileDialog.getOpenFileName(
            caption=u'项目.map文件路径',
            filter=u'MDK .map file (*.map)',
            directory=self.cmbMap.currentText())
        if path != '':
            self.cmbMap.insertItem(0, path)
            self.cmbMap.setCurrentIndex(0)

    @QtCore.pyqtSlot(str)
    def on_cmbMode_currentIndexChanged(self, str):
        self.mode = str
        self.txtMain.setVisible(self.mode == u'文本')
        self.qwtPlot.setVisible(self.mode == u'波形')

    @QtCore.pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        paths = []
        for i in range(min(10, self.cmbMap.count())):
            paths.append(self.cmbMap.itemText(i))
        self.conf.set('globals', 'mappath', repr(paths))
        self.conf.write(open('setting.ini', 'w'))
Ejemplo n.º 5
0
class RTTView(QtGui.QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)
        
        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''
        
        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()
    
    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w')
        
        self.conf = ConfigParser.ConfigParser()
        self.conf.read('setting.ini')
        
        if not self.conf.has_section('J-Link'):
            self.conf.add_section('J-Link')
            self.conf.set('J-Link', 'dllpath', '')
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'StartAddr', '0x20000000')

        self.linDLL.setText(self.conf.get('J-Link', 'dllpath').decode('gbk'))

    def initQwtPlot(self):
        self.PlotData = [0]*1000
        
        self.qwtPlot = QwtPlot(self)
        self.qwtPlot.setVisible(False)
        self.vLayout.insertWidget(0, self.qwtPlot)
        
        self.PlotCurve = QwtPlotCurve()
        self.PlotCurve.attach(self.qwtPlot)
        self.PlotCurve.setData(range(1, len(self.PlotData)+1), self.PlotData)
    
    @QtCore.pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == u'打开连接':
            try:
                self.jlink = ctypes.cdll.LoadLibrary(self.linDLL.text())

                err_buf = (ctypes.c_char * 64)()
                self.jlink.JLINKARM_ExecCommand('Device = Cortex-M0', err_buf, 64)

                self.jlink.JLINKARM_TIF_Select(1)
                self.jlink.JLINKARM_SetSpeed(8000)
                
                buff = ctypes.create_string_buffer(1024)
                Addr = int(self.conf.get('Memory', 'StartAddr'), 16)
                for i in range(256):
                    self.jlink.JLINKARM_ReadMem(Addr + 1024*i, 1024, buff)
                    index = buff.raw.find('SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024*i + index

                        buff = ctypes.create_string_buffer(ctypes.sizeof(SEGGER_RTT_CB))
                        self.jlink.JLINKARM_ReadMem(self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB), buff)

                        rtt_cb = SEGGER_RTT_CB.from_buffer(buff)
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append('\n_SEGGER_RTT @ 0x%08X with %d aUp and %d aDown\n' %(self.RTTAddr, rtt_cb.MaxNumUpBuffers, rtt_cb.MaxNumDownBuffers))
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append('\n%s\n' %str(e))
            else:
                self.linDLL.setEnabled(False)
                self.btnDLL.setEnabled(False)
                self.btnOpen.setText(u'关闭连接')
        else:
            self.linDLL.setEnabled(True)
            self.btnDLL.setEnabled(True)
            self.btnOpen.setText(u'打开连接')
    
    def aUpRead(self):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aUpAddr, ctypes.sizeof(RingBuffer), buf)

        aUp = RingBuffer.from_buffer(buf)
        
        if aUp.RdOff == aUp.WrOff:
            str = ctypes.create_string_buffer(0)

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff, cnt, str)
            
            aUp.RdOff += cnt
            
            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4*4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            str = ctypes.create_string_buffer(cnt)
            self.jlink.JLINKARM_ReadMem(ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff, cnt, str)
            
            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件
            
            self.jlink.JLINKARM_WriteU32(self.aUpAddr + 4*4, aUp.RdOff)
        
        return str.raw
    
    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == u'关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join('%02X ' %ord(c) for c in self.rcvbuff)

                    else:
                        text = self.rcvbuff

                    if len(self.txtMain.toPlainText()) > 25000: self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(',') == -1: return
                    
                    d = [int(x) for x in self.rcvbuff[0:self.rcvbuff.rfind(',')].split(',')]
                    for x in d:
                        self.PlotData.pop(0)
                        self.PlotData.append(x)
                    self.PlotCurve.setData(range(1, len(self.PlotData)+1), self.PlotData)
                    self.qwtPlot.replot()

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(',')+1:]

            except Exception as e:
                self.rcvbuff = b''
                self.txtMain.append('\n%s\n' %str(e))
    
    def aDownWrite(self, bytes):
        buf = ctypes.create_string_buffer(ctypes.sizeof(RingBuffer))
        self.jlink.JLINKARM_ReadMem(self.aDownAddr, ctypes.sizeof(RingBuffer), buf)

        aDown = RingBuffer.from_buffer(buf)
        
        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0: cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:                cnt = min(aDown.SizeOfBuffer - 1 - aDown.WrOff, len(bytes))    # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value + aDown.WrOff, cnt, str)
            
            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:     # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff, len(bytes))    # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            str = ctypes.create_string_buffer(bytes[:cnt])
            self.jlink.JLINKARM_WriteMem(ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value + aDown.WrOff, cnt, str)

            aDown.WrOff += cnt

        self.jlink.JLINKARM_WriteU32(self.aDownAddr + 4*3, aDown.WrOff)

    @QtCore.pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == u'关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    bytes = ''.join([chr(int(x, 16)) for x in text.split()])

                else:
                    bytes = text

                self.aDownWrite(bytes)

            except Exception as e:
                self.txtMain.append('\n%s\n' %str(e))

    @QtCore.pyqtSlot()
    def on_btnDLL_clicked(self):
        path = QtGui.QFileDialog.getOpenFileName(caption=u'JLinkARM.dll路径', filter=u'动态链接库文件 (*.dll)', directory=self.linDLL.text())
        if path != '':
            self.linDLL.setText(path)

    @QtCore.pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.qwtPlot.setVisible(state == QtCore.Qt.Checked)
        self.txtMain.setVisible(state == QtCore.Qt.Unchecked)

    @QtCore.pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()
    
    def closeEvent(self, evt):
        self.conf.set('J-Link', 'dllpath', self.linDLL.text().encode('gbk'))
        self.conf.write(open('setting.ini', 'w'))
Ejemplo n.º 6
0
class ToftofProfileWindow(DlgUtils, QMainWindow):
    def __init__(self, parent):
        QMainWindow.__init__(self, parent)
        DlgUtils.__init__(self, 'Live data')
        self.panel = parent
        layout1 = QVBoxLayout()
        self.plot = QwtPlot(self)
        layout1.addWidget(self.plot)
        self.curve = QwtPlotCurve()
        self.curve.setRenderHint(QwtPlotCurve.RenderAntialiased)
        self.curve.attach(self.plot)
        self.marker = QwtPlotMarker()
        self.marker.attach(self.plot)
        self.markerpen = QPen(Qt.red)
        self.marker.setSymbol(
            QwtSymbol(QwtSymbol.Ellipse, QBrush(), self.markerpen, QSize(7,
                                                                         7)))
        self.zoomer = QwtPlotZoomer(self.plot.canvas())
        self.zoomer.setMousePattern(QwtPlotZoomer.MouseSelect3, Qt.NoButton)
        self.picker = QwtPlotPicker(self.plot.canvas())
        self.picker.setSelectionFlags(QwtPlotPicker.PointSelection
                                      | QwtPlotPicker.ClickSelection)
        self.picker.setMousePattern(QwtPlotPicker.MouseSelect1, Qt.MidButton)
        self.picker.selected.connect(self.pickerSelected)
        layout2 = QHBoxLayout()
        layout2.addWidget(QLabel('Scale:', self))
        self.scale = QComboBox(self)
        self.scale.addItems([
            'Single detectors, sorted by angle',
            'Scattering angle 2theta (deg)', 'Q value (A-1)'
        ])
        self.scale.currentIndexChanged[int].connect(self.scaleChanged)
        layout2.addWidget(self.scale)
        layout2.addStretch()
        self.scaleframe = QFrame(self)
        self.scaleframe.setLayout(layout2)
        self.scaleframe.setVisible(False)
        layout1.addWidget(self.scaleframe)
        mainframe = QFrame(self)
        mainframe.setLayout(layout1)
        self.setCentralWidget(mainframe)
        self.setContentsMargins(6, 6, 6, 6)
        plotfont = scaledFont(self.font(), 0.7)
        self.plot.setAxisFont(QwtPlot.xBottom, plotfont)
        self.plot.setAxisFont(QwtPlot.yLeft, plotfont)
        self.plot.setCanvasBackground(Qt.white)
        self.resize(800, 200)

        self._detinfo = None
        self._anglemap = None
        self._infowindow = None
        self._infolabel = None
        self._xs = self._ys = None
        self._type = None

    def _retrieve_detinfo(self):
        if self._detinfo is None:
            info = self.panel.client.eval(
                'det._detinfo_parsed, '
                'det._anglemap', None)
            if not info:
                return self.showError('Cannot retrieve detector info.')
            self._lambda = self.panel.client.eval('chWL()', None)
            if not self._lambda:
                return self.showError('Cannot retrieve wavelength.')
            self._detinfo, self._anglemap = info
            self._inverse_anglemap = 0
            self._infowindow = QMainWindow(self)
            self._infolabel = QLabel(self._infowindow)
            self._infolabel.setTextFormat(Qt.RichText)
            self._infowindow.setCentralWidget(self._infolabel)
            self._infowindow.setContentsMargins(10, 10, 10, 10)
            self._inv_anglemap = [[
                entry for entry in self._detinfo[1:]
                if entry[12] == self._anglemap[detnr] + 1
            ][0] for detnr in range(len(self._xs))]

    def scaleChanged(self, scale):
        self.update(self._type, self._orig_nbins, self._orig_x, self._orig_y)

    def update(self, proftype, nbins, x, y):
        self._orig_x = x
        self._orig_y = y
        self._orig_nbins = nbins
        x.setsize(8 * nbins)
        y.setsize(8 * nbins)
        xs = struct.unpack('d' * nbins, x)
        ys = struct.unpack('d' * nbins, y)
        if proftype == 0:
            if self.scale.currentIndex() == 0:
                xs = xs
            elif self.scale.currentIndex() == 1:
                self._retrieve_detinfo()
                xs = [self._inv_anglemap[int(xi)][5] for xi in xs]
            else:
                self._retrieve_detinfo()
                if self._lambda is None:
                    self.showError('Could not determine wavelength.')
                    self.scale.setCurrentIndex(1)
                    return
                xs = [
                    4 * pi / self._lambda *
                    sin(radians(self._inv_anglemap[int(xi)][5] / 2.))
                    for xi in xs
                ]
        self._xs = xs
        self._ys = ys
        self.curve.setData(xs, ys)
        self.plot.setAxisAutoScale(QwtPlot.xBottom)
        self.plot.setAxisAutoScale(QwtPlot.yLeft)
        self.marker.setVisible(False)
        self.zoomer.setZoomBase(True)
        self._type = proftype
        if proftype == 0:
            self.setWindowTitle(
                'Single detector view (time-channel integrated)')
            self.scaleframe.setVisible(True)
        elif proftype == 1:
            self.setWindowTitle('Time channel view (detector integrated)')
            self.scaleframe.setVisible(False)
        else:
            self.scaleframe.setVisible(False)

    def pickerSelected(self, point):
        if self._type != 0:
            return
        self._retrieve_detinfo()
        index = self.curve.closestPoint(self.picker.transform(point))[0]
        detentry = self._inv_anglemap[index][:]
        detentry.append(self._xs[index])
        detentry.append(self._ys[index])
        self.marker.setXValue(self._xs[index])
        self.marker.setYValue(self._ys[index])
        self.marker.setVisible(True)
        self.plot.replot()
        self._infowindow.show()
        entrynames = [
            'EntryNr', 'Rack', 'Plate', 'Pos', 'RPos', '2Theta', 'CableNr',
            'CableType', 'CableLen', 'CableEmpty', 'Card', 'Chan', 'Total',
            'DetName', 'BoxNr', 'BoxChan', 'XValue', 'Counts'
        ]
        formats = [
            '%s', '%d', '%d', '%d', '%d', '%.3f', '%d', '%d', '%.2f', '%d',
            '%d', '%d', '%d', '%r', '%d', '%d', '%s', '%d'
        ]
        empties = [1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0]
        self._infolabel.setText('Detector info:<br><table>' + ''.join(
            '<tr><td>%s</td><td></td><td>%s</td></tr>%s' %
            (name, format % value, '<tr></tr>' if empty else '')
            for (name, format, empty,
                 value) in zip(entrynames, formats, empties, detentry)) +
                                '</table>')

    def closeEvent(self, event):
        if self._infowindow:
            self._infowindow.close()
Ejemplo n.º 7
0
app = QtGui.QApplication(sys.argv)

p = QwtPlot()


curve = QwtPlotCurve("Segon i tal")


fn = resDir + '/' + data
f = file(fn)
v = pickle.load(f)


y = v
x = range(len(y))

curve.setData(x, y)

curve.attach(p)

p.replot()

p.show()

sys.exit(app.exec_())




Ejemplo n.º 8
0
class qwtchart(chart):
    colours = {'red'    : Qt.red,
               'green'  : Qt.green,
               'blue'   : Qt.blue,
               'yellow' : Qt.yellow,
               'magenta': Qt.magenta,
               'black'  : Qt.black}
    styles = {'-' : Qt.SolidLine,
              '--': Qt.DashLine,
              ':' : Qt.DotLine,
              '-.': Qt.DashDotLine}

    def getPen(self, colour, style):
        return QPen(self.colours[colour], 1, self.styles[style])

    def __init__(self, spurset, fef, parent):
        chart.__init__(self, spurset, fef, parent)
        self.plot = QwtPlot(parent)

        self.plot.setAxisScale(xaxis, self.spurset.RFmin,
                               self.spurset.RFmax)
        self.plot.setAxisScale(yaxis, -self.spurset.dspan/2,
                               self.spurset.dspan/2)

        self.plot.setCanvasBackground(Qt.white)
        grid = QwtPlotGrid()
        grid.setMajPen(QPen(Qt.black, 1, Qt.DotLine))
        grid.attach(self.plot)

        self.plot.insertLegend(QwtLegend(self.parent), QwtPlot.ExternalLegend)

        # a picker to be used for the front-end filter parallelogram
        self.picker = QwtPlotPicker(xaxis, yaxis,
                                    QwtPicker.PointSelection,
                                    QwtPlotPicker.NoRubberBand,
                                    QwtPicker.AlwaysOff,
                                    self.plot.canvas())
        # gonna need this to implement ondrop()
        self._mouseRelease = self.picker.widgetMouseReleaseEvent
        self._picked_obj = None

        self.picker.connect(self.picker, SIGNAL('appended(const QPoint&)'),
                            self.onpick)
        self.picker.connect(self.picker, SIGNAL('moved(const QPoint&)'),
                            self.ondrag)
        # all about the monkey-patching
        self.picker.widgetMouseReleaseEvent = self.ondrop

    def redraw(self):
        xscale = self.plot.axisScaleDiv(xaxis)
        yscale = self.plot.axisScaleDiv(yaxis)
        #TODO check if it hurts to just set the scales every time, as in mpl
        if (xscale.lowerBound() != self.spurset.RFmin or
            xscale.upperBound() != self.spurset.RFmax):
            self.plot.setAxisScale(xaxis, self.spurset.RFmin,
                                   self.spurset.RFmax)
        if (yscale.lowerBound() != -self.spurset.dspan/2 or
            yscale.upperBound() != self.spurset.dspan/2):
            self.plot.setAxisScale(yaxis, -self.spurset.dspan/2,
                                   self.spurset.dspan/2)
        self.plot.replot()

    def mkline(self, xdata, ydata, style=('black','-'), title=''):
        line = QwtPlotCurve(title)
        if title is '':
            # don't display it in the legend
            # kind of ugly, that the title variable is doing double duty
            line.setItemAttribute(QwtPlotItem.Legend, False)
        pen = self.getPen(*style)
        line.setPen(pen)
        line.setRenderHint(QwtPlotItem.RenderAntialiased)
        line.setData(xdata, ydata)
        return line

    def add_line(self, line):
        line.attach(self.plot)

    def del_line(self, line):
        line.detach()

    def legend(self):
        return self.plot.legend()

    def _xval(self, pos):
        # from a QPoint referring to a pixel on the plot, find the x value
        return self.plot.invTransform(xaxis, pos.x())

    def _yval(self, pos):
        # from a QPoint referring to a pixel on the plot, find the y value
        return self.plot.invTransform(yaxis, pos.y())

    def onpick(self, pos):
        # for now, we only worry about picking the two vertical lines of the
        # front-end filter parallelogram. Other clicks are no-ops.
        if abs(pos.x() - self.plot.transform(xaxis, self.fef.start)) <= 10:
            # TODO check pos.y() as well
            self._picked_obj = self.fef.startline
            self.pick(self.fef.startline, self._xval(pos), self._yval(pos))
        elif abs(pos.x() - self.plot.transform(xaxis, self.fef.stop)) <= 10:
            self._picked_obj  = self.fef.stopline
            self.pick(self.fef.stopline, self._xval(pos), self._yval(pos))
        else:
            return
        # TODO decide if a pick should also trigger a drag.
        # TODO this may be duplicated somewhere...
        self.ondrag(pos)

    def ondrag(self, pos):
        # no dragging action if we haven't already successfully picked something
        if self._picked_obj is not None:
            self.drag(self._picked_obj, self._xval(pos), self._yval(pos))

    def ondrop(self, evt):
        # need to make sure we're only triggering on left button releases
        if evt.button() == Qt.LeftButton and self._picked_obj is not None:
            self.drop(self._picked_obj, self._xval(evt), self._yval(evt))
            self._picked_obj = None
        # also important to let the original event handler happen
        self._mouseRelease(evt)
Ejemplo n.º 9
0
class qwtchart(chart):
    colours = {
        'red': Qt.red,
        'green': Qt.green,
        'blue': Qt.blue,
        'yellow': Qt.yellow,
        'magenta': Qt.magenta,
        'black': Qt.black
    }
    styles = {
        '-': Qt.SolidLine,
        '--': Qt.DashLine,
        ':': Qt.DotLine,
        '-.': Qt.DashDotLine
    }

    def getPen(self, colour, style):
        return QPen(self.colours[colour], 1, self.styles[style])

    def __init__(self, spurset, fef, parent):
        chart.__init__(self, spurset, fef, parent)
        self.plot = QwtPlot(parent)

        self.plot.setAxisScale(xaxis, self.spurset.RFmin, self.spurset.RFmax)
        self.plot.setAxisScale(yaxis, -self.spurset.dspan / 2,
                               self.spurset.dspan / 2)

        self.plot.setCanvasBackground(Qt.white)
        grid = QwtPlotGrid()
        grid.setMajPen(QPen(Qt.black, 1, Qt.DotLine))
        grid.attach(self.plot)

        self.plot.insertLegend(QwtLegend(self.parent), QwtPlot.ExternalLegend)

        # a picker to be used for the front-end filter parallelogram
        self.picker = QwtPlotPicker(xaxis, yaxis, QwtPicker.PointSelection,
                                    QwtPlotPicker.NoRubberBand,
                                    QwtPicker.AlwaysOff, self.plot.canvas())
        # gonna need this to implement ondrop()
        self._mouseRelease = self.picker.widgetMouseReleaseEvent
        self._picked_obj = None

        self.picker.connect(self.picker, SIGNAL('appended(const QPoint&)'),
                            self.onpick)
        self.picker.connect(self.picker, SIGNAL('moved(const QPoint&)'),
                            self.ondrag)
        # all about the monkey-patching
        self.picker.widgetMouseReleaseEvent = self.ondrop

    def redraw(self):
        xscale = self.plot.axisScaleDiv(xaxis)
        yscale = self.plot.axisScaleDiv(yaxis)
        #TODO check if it hurts to just set the scales every time, as in mpl
        if (xscale.lowerBound() != self.spurset.RFmin
                or xscale.upperBound() != self.spurset.RFmax):
            self.plot.setAxisScale(xaxis, self.spurset.RFmin,
                                   self.spurset.RFmax)
        if (yscale.lowerBound() != -self.spurset.dspan / 2
                or yscale.upperBound() != self.spurset.dspan / 2):
            self.plot.setAxisScale(yaxis, -self.spurset.dspan / 2,
                                   self.spurset.dspan / 2)
        self.plot.replot()

    def mkline(self, xdata, ydata, style=('black', '-'), title=''):
        line = QwtPlotCurve(title)
        if title is '':
            # don't display it in the legend
            # kind of ugly, that the title variable is doing double duty
            line.setItemAttribute(QwtPlotItem.Legend, False)
        pen = self.getPen(*style)
        line.setPen(pen)
        line.setRenderHint(QwtPlotItem.RenderAntialiased)
        line.setData(xdata, ydata)
        return line

    def add_line(self, line):
        line.attach(self.plot)

    def del_line(self, line):
        line.detach()

    def legend(self):
        return self.plot.legend()

    def _xval(self, pos):
        # from a QPoint referring to a pixel on the plot, find the x value
        return self.plot.invTransform(xaxis, pos.x())

    def _yval(self, pos):
        # from a QPoint referring to a pixel on the plot, find the y value
        return self.plot.invTransform(yaxis, pos.y())

    def onpick(self, pos):
        # for now, we only worry about picking the two vertical lines of the
        # front-end filter parallelogram. Other clicks are no-ops.
        if abs(pos.x() - self.plot.transform(xaxis, self.fef.start)) <= 10:
            # TODO check pos.y() as well
            self._picked_obj = self.fef.startline
            self.pick(self.fef.startline, self._xval(pos), self._yval(pos))
        elif abs(pos.x() - self.plot.transform(xaxis, self.fef.stop)) <= 10:
            self._picked_obj = self.fef.stopline
            self.pick(self.fef.stopline, self._xval(pos), self._yval(pos))
        else:
            return
        # TODO decide if a pick should also trigger a drag.
        # TODO this may be duplicated somewhere...
        self.ondrag(pos)

    def ondrag(self, pos):
        # no dragging action if we haven't already successfully picked something
        if self._picked_obj is not None:
            self.drag(self._picked_obj, self._xval(pos), self._yval(pos))

    def ondrop(self, evt):
        # need to make sure we're only triggering on left button releases
        if evt.button() == Qt.LeftButton and self._picked_obj is not None:
            self.drop(self._picked_obj, self._xval(evt), self._yval(evt))
            self._picked_obj = None
        # also important to let the original event handler happen
        self._mouseRelease(evt)
Ejemplo n.º 10
0
class RTTView(QtGui.QWidget):
    def __init__(self, parent=None):
        super(RTTView, self).__init__(parent)

        uic.loadUi('RTTView.ui', self)

        self.initSetting()

        self.initQwtPlot()

        self.rcvbuff = b''

        self.daplink = None

        self.tmrRTT = QtCore.QTimer()
        self.tmrRTT.setInterval(10)
        self.tmrRTT.timeout.connect(self.on_tmrRTT_timeout)
        self.tmrRTT.start()

        self.tmrRTT_Cnt = 0

    def initSetting(self):
        if not os.path.exists('setting.ini'):
            open('setting.ini', 'w')

        self.conf = ConfigParser.ConfigParser()
        self.conf.read('setting.ini')

        if not self.conf.has_section('Memory'):
            self.conf.add_section('Memory')
            self.conf.set('Memory', 'StartAddr', '0x20000000')

    def initQwtPlot(self):
        self.PlotData = [0] * 1000

        self.qwtPlot = QwtPlot(self)
        self.qwtPlot.setVisible(False)
        self.vLayout.insertWidget(0, self.qwtPlot)

        self.PlotCurve = QwtPlotCurve()
        self.PlotCurve.attach(self.qwtPlot)
        self.PlotCurve.setData(range(1, len(self.PlotData) + 1), self.PlotData)

    @QtCore.pyqtSlot()
    def on_btnOpen_clicked(self):
        if self.btnOpen.text() == u'打开连接':
            try:
                self.daplink = self.daplinks[self.cmbDAP.currentText()]
                self.daplink.open()

                dp = coresight.dap.DebugPort(self.daplink, None)
                dp.init()
                dp.power_up_debug()

                ap = coresight.ap.AHB_AP(dp, 0)
                ap.init()

                self.dap = coresight.cortex_m.CortexM(None, ap)

                Addr = int(self.conf.get('Memory', 'StartAddr'), 16)
                for i in range(256):
                    buff = self.dap.read_memory_block8(Addr + 1024 * i, 1024)
                    buff = ''.join([chr(x) for x in buff])
                    index = buff.find('SEGGER RTT')
                    if index != -1:
                        self.RTTAddr = Addr + 1024 * i + index

                        buff = self.dap.read_memory_block8(
                            self.RTTAddr, ctypes.sizeof(SEGGER_RTT_CB))

                        rtt_cb = SEGGER_RTT_CB.from_buffer(bytearray(buff))
                        self.aUpAddr = self.RTTAddr + 16 + 4 + 4
                        self.aDownAddr = self.aUpAddr + ctypes.sizeof(
                            RingBuffer) * rtt_cb.MaxNumUpBuffers

                        self.txtMain.append(
                            '\n_SEGGER_RTT @ 0x%08X with %d aUp and %d aDown\n'
                            % (self.RTTAddr, rtt_cb.MaxNumUpBuffers,
                               rtt_cb.MaxNumDownBuffers))
                        break
                else:
                    raise Exception('Can not find _SEGGER_RTT')
            except Exception as e:
                self.txtMain.append('\n%s\n' % str(e))
            else:
                self.cmbDAP.setEnabled(False)
                self.btnOpen.setText(u'关闭连接')
        else:
            self.daplink.close()
            self.cmbDAP.setEnabled(True)
            self.btnOpen.setText(u'打开连接')

    def aUpRead(self):
        buf = self.dap.read_memory_block8(self.aUpAddr,
                                          ctypes.sizeof(RingBuffer))
        aUp = RingBuffer.from_buffer(bytearray(buf))

        if aUp.RdOff == aUp.WrOff:
            buf = []

        elif aUp.RdOff < aUp.WrOff:
            cnt = aUp.WrOff - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff += cnt

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        else:
            cnt = aUp.SizeOfBuffer - aUp.RdOff
            buf = self.dap.read_memory_block8(
                ctypes.cast(aUp.pBuffer, ctypes.c_void_p).value + aUp.RdOff,
                cnt)

            aUp.RdOff = 0  #这样下次再读就会进入执行上个条件

            self.dap.write32(self.aUpAddr + 4 * 4, aUp.RdOff)

        return ''.join([chr(x) for x in buf])

    def on_tmrRTT_timeout(self):
        if self.btnOpen.text() == u'关闭连接':
            try:
                self.rcvbuff += self.aUpRead()

                if self.txtMain.isVisible():
                    if self.chkHEXShow.isChecked():
                        text = ''.join('%02X ' % ord(c) for c in self.rcvbuff)
                    else:
                        text = self.rcvbuff

                    if len(self.txtMain.toPlainText()) > 25000:
                        self.txtMain.clear()
                    self.txtMain.moveCursor(QtGui.QTextCursor.End)
                    self.txtMain.insertPlainText(text)

                    self.rcvbuff = b''

                else:
                    if self.rcvbuff.rfind(',') == -1: return

                    d = [
                        int(x) for x in
                        self.rcvbuff[0:self.rcvbuff.rfind(',')].split(',')
                    ]
                    for x in d:
                        self.PlotData.pop(0)
                        self.PlotData.append(x)
                    self.PlotCurve.setData(range(1,
                                                 len(self.PlotData) + 1),
                                           self.PlotData)
                    self.qwtPlot.replot()

                    self.rcvbuff = self.rcvbuff[self.rcvbuff.rfind(',') + 1:]

            except Exception as e:
                self.rcvbuff = b''
                self.txtMain.append('\n%s\n' % str(e))

        else:
            self.tmrRTT_Cnt += 1
            if self.tmrRTT_Cnt % 20 == 0:
                self.detect_daplink()  # 自动检测 DAPLink 的热插拔

    def aDownWrite(self, bytes):
        buf = self.dap.read_memory_block8(self.aDownAddr,
                                          ctypes.sizeof(RingBuffer))
        aDown = RingBuffer.from_buffer(bytearray(buf))

        if aDown.WrOff >= aDown.RdOff:
            if aDown.RdOff != 0:
                cnt = min(aDown.SizeOfBuffer - aDown.WrOff, len(bytes))
            else:
                cnt = min(
                    aDown.SizeOfBuffer - 1 - aDown.WrOff,
                    len(bytes))  # 写入操作不能使得 aDown.WrOff == aDown.RdOff,以区分满和空
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in bytes[:cnt]])

            aDown.WrOff += cnt
            if aDown.WrOff == aDown.SizeOfBuffer: aDown.WrOff = 0

            bytes = bytes[cnt:]

        if bytes and aDown.RdOff != 0 and aDown.RdOff != 1:  # != 0 确保 aDown.WrOff 折返回 0,!= 1 确保有空间可写入
            cnt = min(aDown.RdOff - 1 - aDown.WrOff,
                      len(bytes))  # - 1 确保写入操作不导致WrOff与RdOff指向同一位置
            self.dap.write_memory_block8(
                ctypes.cast(aDown.pBuffer, ctypes.c_void_p).value +
                aDown.WrOff, [ord(x) for x in bytes[:cnt]])

            aDown.WrOff += cnt

        self.dap.write32(self.aDownAddr + 4 * 3, aDown.WrOff)

    @QtCore.pyqtSlot()
    def on_btnSend_clicked(self):
        if self.btnOpen.text() == u'关闭连接':
            text = self.txtSend.toPlainText()

            try:
                if self.chkHEXSend.isChecked():
                    bytes = ''.join([chr(int(x, 16)) for x in text.split()])

                else:
                    bytes = text

                self.aDownWrite(bytes)

            except Exception as e:
                self.txtMain.append('\n%s\n' % str(e))

    def detect_daplink(self):
        daplinks = aggregator.DebugProbeAggregator.get_all_connected_probes()

        if len(daplinks) != self.cmbDAP.count():
            self.cmbDAP.clear()
            for daplink in daplinks:
                self.cmbDAP.addItem(daplink.product_name)

            self.daplinks = collections.OrderedDict([
                (daplink.product_name, daplink) for daplink in daplinks
            ])

            if self.daplink and self.daplink.product_name in self.daplinks:
                self.cmbDAP.setCurrentIndex(self.daplinks.keys().index(
                    self.daplink.product_name))
            else:  # daplink被拔掉
                self.btnOpen.setText(u'打开连接')

    @QtCore.pyqtSlot(int)
    def on_chkWavShow_stateChanged(self, state):
        self.qwtPlot.setVisible(state == QtCore.Qt.Checked)
        self.txtMain.setVisible(state == QtCore.Qt.Unchecked)

    @QtCore.pyqtSlot()
    def on_btnClear_clicked(self):
        self.txtMain.clear()

    def closeEvent(self, evt):
        self.conf.write(open('setting.ini', 'w'))