Beispiel #1
0
class MPlotWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(MPlotWidget, self).__init__(parent)

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)

        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()

        self.plotbutton = QtGui.QPushButton('Plot')

        self.zoombutton = QtGui.QPushButton('Zoom')
        self.zoombutton.clicked.connect(self.zoom)

        self.panbutton = QtGui.QPushButton('Pan')
        self.panbutton.clicked.connect(self.pan)

        self.homebutton = QtGui.QPushButton('Home')
        self.homebutton.clicked.connect(self.home)

        self.savebutton = QtGui.QPushButton('Save')
        self.savebutton.clicked.connect(self.save)

        layout = QtGui.QVBoxLayout()

        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)

        buttonbox = QtGui.QHBoxLayout()
        buttonbox.addWidget(self.plotbutton)
        buttonbox.addWidget(self.zoombutton)
        buttonbox.addWidget(self.panbutton)
        buttonbox.addWidget(self.homebutton)
        buttonbox.addWidget(self.savebutton)

        layout.addLayout(buttonbox)
        self.setLayout(layout)

        self.ax = self.figure.add_subplot(111)
        self.ax.hold(False)

    def home(self):
        self.toolbar.home()
    def zoom(self):
        self.toolbar.zoom()
    def pan(self):
        self.toolbar.pan()
    def save(self):
        self.figure.savefig('1.png')

    """
Beispiel #2
0
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)


        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()

        # Just some button
        self.button = QtGui.QPushButton('Plot')
        self.button.clicked.connect(self.plot)

        self.button1 = QtGui.QPushButton('Zoom')
        self.button1.clicked.connect(self.zoom)

        self.button2 = QtGui.QPushButton('Pan')
        self.button2.clicked.connect(self.pan)

        self.button3 = QtGui.QPushButton('Home')
        self.button3.clicked.connect(self.home)


        # set the layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        layout.addWidget(self.button)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        layout.addWidget(self.button3)
        self.setLayout(layout)

    def home(self):
        self.toolbar.home()
    def zoom(self):
        self.toolbar.zoom()
    def pan(self):
        self.toolbar.pan()

    def plot(self):
        ''' plot some random stuff '''
        data = [random.random() for i in range(25)]
        ax = self.figure.add_subplot(111)
        ax.hold(False)
        ax.plot(data, '*-')
        self.canvas.draw()
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
 
        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
 
         
        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()
 
        # Just some button 
        self.button = QtGui.QPushButton('Plot')
        self.button.clicked.connect(self.plot)
 
        self.button1 = QtGui.QPushButton('Zoom')
        self.button1.clicked.connect(self.zoom)
         
        self.button2 = QtGui.QPushButton('Pan')
        self.button2.clicked.connect(self.pan)
         
        self.button3 = QtGui.QPushButton('Home')
        self.button3.clicked.connect(self.home)
 
 
        # set the layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.toolbar)
        layout.addWidget(self.canvas)
        layout.addWidget(self.button)
        layout.addWidget(self.button1)
        layout.addWidget(self.button2)
        layout.addWidget(self.button3)
        self.setLayout(layout)
 
    def home(self):
        self.toolbar.home()
    def zoom(self):
        self.toolbar.zoom()
    def pan(self):
        self.toolbar.pan()
         
    def plot(self):
        ''' plot some random stuff '''
        data = [random.random() for i in range(25)]
        ax = self.figure.add_subplot(111)
        ax.hold(False)
        ax.plot(data, '*-')
        self.canvas.draw()
Beispiel #4
0
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
 
        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
 
         
        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()
 
##        # Start Button
##        self.button = QtGui.QPushButton('Start')
##        self.button.clicked.connect(self.plot)
##        
        # set the layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.canvas)
##        layout.addWidget(self.button)
        self.setLayout(layout)

        global ser, Array, num_rows, num_cols, Matrix, count, sensor_num
        ser = sensorInit()
        num_rows = 7
        num_cols = 4
        Array = bytearray(num_rows*num_cols)
        Matrix = np.zeros((num_cols,num_rows))
        
        count = 33
        sensor_num = 24
        #timer info
        self.timer = QtCore.QBasicTimer()
        self.timer.start(count, self)
        self.step = 0

    def timerEvent(self, event):
        if event.timerId() == self.timer.timerId():
            self.update()
            Array = readSensors(ser, sensor_num)
            Matrix = matrixConvert(Array, num_rows, num_cols)
            ax = self.figure.add_subplot(111)
            ax.imshow(Matrix, interpolation='nearest', cmap='Spectral')
            self.canvas.draw()
            super(Window, self).timerEvent(event)
Beispiel #5
0
class Grafica(QWidget):

    valores = (20, 35, 30, 35, 27) # valores de ejemplo


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

        # FIGUREANDO
        self.ordenadas = np.arange(5)
        self.width = 1       # the width of the bars
        self.figure, self.ax = plt.subplots()
        #self.figure = plt.figure()
        self.line = self.ax.bar(self.ordenadas, self.valores, self.width, color='g')
        #self.line, = plt.plot(self.data)
        plt.ion() # animate

        N = 10
        self.xs = collections.deque(maxlen=N)
        self.ys = collections.deque(maxlen=N)
        self.xs.append(0)
        self.ys.append(0)

        self.ax = self.figure.add_subplot(111)
        self.ax.hold(False)
        self.ax.set_ylim([0, 360])

        self.canvas = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()
        self.canvas.show()

        # set the layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.toolbar)
        self.layout.addWidget(self.canvas)
        self.setLayout(self.layout)


    def add_sample(self, valores):
        self.valores = valores
        self.line = self.ax.bar(self.ordenadas, self.valores, self.width, color='g')
        self.canvas.draw()  # update the plot
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)

        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()

        # Timestream info
        self.timestreamLabel = QtGui.QLabel('Time-stream root path:')
        self.timestreamText = QtGui.QLineEdit('')
        self.timestreamText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)

        self.timestreamDateLabel = QtGui.QLabel('Start date (yyyy_mm_dd_hh_mm_ss):')
        self.timestreamDateText = QtGui.QLineEdit('')
        self.timestreamDateText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)

        self.timestreamTimeLabel = QtGui.QLabel('Time interval (seconds):')
        self.timestreamTimeText = QtGui.QLineEdit('')
        self.timestreamTimeText.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)

        self.initStreamTimeButton = QtGui.QPushButton('&Initialise timestream by time')
        self.initStreamTimeButton.clicked.connect(self.initialiseTimestreamByTime)

        self.initStreamFileButton = QtGui.QPushButton('&Initialise timestream by files')
        self.initStreamFileButton.clicked.connect(self.initialiseTimestreamByFiles)

        # Image loading and processing
        self.loadImageButton = QtGui.QPushButton('&Load (next) image')
        self.loadImageButton.clicked.connect(self.loadImage)

        self.rotateImageButton = QtGui.QPushButton('&Rotate 90-deg')
        self.rotateImageButton.clicked.connect(self.rotateImage90Degrees)

        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal)
        self.slider.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.slider.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
        self.slider.setTickPosition(QtGui.QSlider.TicksBothSides)
        self.slider.setMinimum(-16)
        self.slider.setMaximum(16)
        self.slider.setValue(0)
        self.slider.setTickInterval(4)
        self.slider.setSingleStep(1)
        self.slider.valueChanged.connect(self.rotateSmallAngle)

        self.applySmallRotationButton = QtGui.QPushButton('&Apply')
        self.applySmallRotationButton.clicked.connect(self.applySmallRotation)

        self.loadCamCalibButton = QtGui.QPushButton('Load &cam. param.')
        self.loadCamCalibButton.clicked.connect(self.loadCamCalib)

        self.colorcardRadioButton = QtGui.QRadioButton('Select color car&d')
        self.colorcardRadioButton.setChecked(False)
        self.colorcardRadioButton.clicked.connect(self.selectWhat)

        self.trayRadioButton = QtGui.QRadioButton('Select &tray')
        self.trayRadioButton.setChecked(False)
        self.trayRadioButton.clicked.connect(self.selectWhat)

        self.trayRoundCheckBox = QtGui.QCheckBox('Round')
        self.trayRoundCheckBox.setChecked(True)

        self.potRadioButton = QtGui.QRadioButton('Select &pot')
        self.potRadioButton.setChecked(False)
        self.potRadioButton.clicked.connect(self.selectWhat)

        self.zoomButton = QtGui.QPushButton('&Zoom')
        self.zoomButton.setCheckable(True)
        self.zoomButton.clicked.connect(self.zoom)

        self.panButton = QtGui.QPushButton('&Pan')
        self.panButton.setCheckable(True)
        self.panButton.clicked.connect(self.pan)

        self.homeButton = QtGui.QPushButton('&Home')
        self.homeButton.clicked.connect(self.home)

        self.correctColorButton = QtGui.QPushButton('Correct colo&r')
        self.correctColorButton.clicked.connect(self.correctColor)

        self.save2PipelineButton = QtGui.QPushButton('&Save as pipeline settings')
        self.save2PipelineButton.clicked.connect(self.savePipelineSettings)

        self.testPipelineButton = QtGui.QPushButton('Test &pipeline processing')
        self.testPipelineButton.clicked.connect(self.testPipeline)

        self.status = QtGui.QTextEdit('')
        self.status.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
        self.mousePosition = QtGui.QLabel('')

        # set the layout
        layout = QtGui.QHBoxLayout()
        rightWidget = QtGui.QWidget()
        buttonlayout = QtGui.QVBoxLayout(rightWidget)
        buttonlayout.addWidget(self.timestreamLabel)
        buttonlayout.addWidget(self.timestreamText)
        buttonlayout.addWidget(self.timestreamDateLabel)
        buttonlayout.addWidget(self.timestreamDateText)
        buttonlayout.addWidget(self.timestreamTimeLabel)
        buttonlayout.addWidget(self.timestreamTimeText)
        buttonlayout.addWidget(self.initStreamTimeButton)
        buttonlayout.addWidget(self.initStreamFileButton)
        buttonlayout.addWidget(self.loadImageButton)
        buttonlayout.addWidget(self.loadCamCalibButton)
        buttonlayout.addWidget(self.rotateImageButton)

        layoutSmallRotation = QtGui.QHBoxLayout()
        layoutSmallRotation.addWidget(self.slider)
        layoutSmallRotation.addWidget(self.applySmallRotationButton)
        buttonlayout.addLayout(layoutSmallRotation)

        buttonlayout.addWidget(self.colorcardRadioButton)

        layoutTrayShape = QtGui.QHBoxLayout()
        layoutTrayShape.addWidget(self.trayRadioButton)
        layoutTrayShape.addWidget(self.trayRoundCheckBox)
        buttonlayout.addLayout(layoutTrayShape)

        buttonlayout.addWidget(self.potRadioButton)
        buttonlayout.addWidget(self.zoomButton)
        buttonlayout.addWidget(self.panButton)
        buttonlayout.addWidget(self.homeButton)
        buttonlayout.addWidget(self.correctColorButton)
        buttonlayout.addWidget(self.save2PipelineButton)
        buttonlayout.addWidget(self.testPipelineButton)
        buttonlayout.addWidget(self.status)
        buttonlayout.addWidget(self.mousePosition)
        rightWidget.setMaximumWidth(250)
        leftLayout = QtGui.QVBoxLayout()
        leftLayout.addWidget(self.toolbar)
        leftLayout.addWidget(self.canvas)

        layout.addWidget(rightWidget)
        layout.addLayout(leftLayout)
        self.setLayout(layout)

        self.group = QtGui.QButtonGroup()
        self.group.addButton(self.colorcardRadioButton)
        self.group.addButton(self.trayRadioButton)
        self.group.addButton(self.potRadioButton)

        self.loadPreviousInputs()

        self.panMode = False
        self.zoomMode = False

        self.ts = None
        self.tsImages = None
        self.ax = None
        self.plotRect = None
        self.plotImg = None
        self.image = None
        self.potTemplate = None
        self.UndistMapX = None
        self.UndistMapY = None
        self.trayAspectRatio = 0.835
        self.colorcardAspectRatio = 1.5
        self.potAspectRatio = 1.0
        self.leftClicks = []
        self.colorcardParams = None
        self.scriptPath = os.path.dirname(os.path.realpath(__file__))
        self.timestreamRootPath = None
        self.pl = None

        # Ouput parameters
        self.ImageSize = None
        self.colorcardList = []
        self.trayList = []
        self.potList = []
        self.rotationAngle = 0.0
        self.smaleRotationAngle = 0.0
        self.CameraMatrix = None
        self.DistCoefs = None
        self.isDistortionCorrected = False
        self.settingFileName = None

    def selectWhat(self):
        if self.trayRadioButton.isChecked():
            self.status.append('Start selecting tray.')
        elif self.colorcardRadioButton.isChecked():
            self.status.append('Start selecting color bar.')
        else:
            self.status.append('Start selecting pot.')

    def home(self):
        self.toolbar.home()
    def zoom(self):
        self.toolbar.zoom()
        if not self.zoomMode:
            self.zoomMode = True
            self.panMode = False
            self.panButton.setChecked(False)
        else:
            self.zoomMode = False
    def pan(self):
        self.toolbar.pan()
        if not self.panMode:
            self.panMode = True
            self.zoomMode = False
            self.zoomButton.setChecked(False)
        else:
            self.panMode = False

    def initialiseTimestreamByTime(self):
        self.timestreamRootPath = str(self.timestreamText.text())
        if len(self.timestreamRootPath) > 0:
            self.status.append('Initialise a timestream at ' + str(self.timestreamRootPath))
            self.ts = timestream.TimeStream()
            self.ts.load(self.timestreamRootPath)
            self.status.append('Done')
            startDate = None
            timeInterval = None
            date = str(self.timestreamDateText.text())
            if len(date) > 0:
                startDate = timestream.parse.ts_parse_date(date)
            time = str(self.timestreamTimeText.text())
            if len(time) > 0:
                timeInterval = int(eval(time))
            self.tsImages = self.ts.iter_by_timepoints(start = startDate, interval = timeInterval, remove_gaps=False)
            self.loadImage()
        else:
            self.status.append('Please provide timestream root path.')

    def initialiseTimestreamByFiles(self):
        self.timestreamRootPath = str(self.timestreamText.text())
        if len(self.timestreamRootPath) > 0:
            self.status.append('Initialise a timestream at ' + str(self.timestreamRootPath))
            self.ts = timestream.TimeStream()
            self.ts.load(self.timestreamRootPath)
            self.status.append('Done')
            self.tsImages = self.ts.iter_by_files()
            self.loadImage()
        else:
            self.status.append('Please provide timestream root path.')

    def loadImage(self):
        ''' load and show an image'''
        if self.tsImages != None:
            try:
                tsImage = self.tsImages.next()
                if tsImage.pixels == np.array([]):
                    self.status.append('Missing image.')
            except:
                tsImage.pixels == np.array([])
                self.status.append('There is no more images.')

            if tsImage.pixels == np.array([]):
                self.image = None
                self.updateFigure()
                return
            self.image = tsImage.pixels
            fname = tsImage.path
        else:
            fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/mnt/phenocam/a_data/TimeStreams/Borevitz/BVZ0036/BVZ0036-GC02L-C01~fullres-orig/2014/2014_06/2014_06_24/2014_06_24_08/')
            app.processEvents()
            if len(fname) == 0:
                return
            self.image = cv2.imread(str(fname))[:,:,::-1]
        self.status.append('Loaded image from ' + str(fname))

        # reset all outputs
#        self.colorcardList = []
#        self.trayList = []
#        self.potList = []
        self.isDistortionCorrected = False

        if self.rotationAngle != None:
            self.image = cd.rotateImage(self.image, self.rotationAngle + self.smaleRotationAngle)

        # Undistort image if mapping available
        if not self.isDistortionCorrected and self.UndistMapX != None and self.UndistMapY != None:
            self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC)
            self.isDistortionCorrected = True

        self.updateFigure()

    def changeCursor(self, event):
#        cursor = Cursor(self.ax, useblit=True, color='red', linewidth=1)
        self.canvas.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor ))

    def updateFigure(self, image = None, resetFigure = False):
        if self.image != None:
            if image == None:
                image = self.image
            if self.ax == None:
                self.ax = self.figure.add_subplot(111)
                self.ax.figure.canvas.mpl_connect('button_press_event', self.onMouseClicked)
                self.ax.figure.canvas.mpl_connect('motion_notify_event', self.onMouseMoves)
                self.ax.figure.canvas.mpl_connect('figure_enter_event', self.changeCursor)
            self.ax.hold(False)
            if self.plotImg == None or resetFigure:
                self.plotImg = self.ax.imshow(image)
            else:
                self.plotImg.set_data(image)
            self.figure.tight_layout()

        xs, ys = [], []
        for Rect in self.colorcardList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for Rect in self.trayList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for Rect in self.potList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for x,y in self.leftClicks:
            xs = xs + [x]
            ys = ys + [y]
#        if self.crosshair != None:
#            xs = xs + [np.nan, 0, self.image.shape[1], np.nan, self.crosshair[0], self.crosshair[0], np.nan]
#            ys = ys + [np.nan, self.crosshair[1], self.crosshair[1], np.nan, 0, self.image.shape[0], np.nan]
        if len(xs) > 0 and len(ys) > 0:
            if self.plotRect == None:
                self.ax.hold(True)
                self.plotRect, = self.ax.plot(xs, ys, 'b')
                self.ax.hold(False)
                self.ax.set_xlim([0,self.image.shape[1]])
                self.ax.set_ylim([0,self.image.shape[0]])
                self.ax.invert_yaxis()
            else:
                self.plotRect.set_data(xs, ys)
        self.canvas.draw()

        app.processEvents()


    def loadCamCalib(self):
        ''' load camera calibration image and show an image'''
        calibPath = os.path.join(self.scriptPath, './data')
        CalibFile = QtGui.QFileDialog.getOpenFileName(self, 'Open image', calibPath)
        self.ImageSize, SquareSize, self.CameraMatrix, self.DistCoefs, RVecs, TVecs = cd.readCalibration(CalibFile)
        self.status.append('Loaded camera parameters from ' + CalibFile)
        print('CameraMatrix =', self.CameraMatrix)
        print('DistCoefs =', self.DistCoefs)
        self.UndistMapX, self.UndistMapY = cv2.initUndistortRectifyMap(self.CameraMatrix, self.DistCoefs, \
            None, self.CameraMatrix, self.ImageSize, cv2.CV_32FC1)

        if self.image != None:
            self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC)
            self.isDistortionCorrected = True
            self.status.append('Corrected image distortion.')
            self.updateFigure()

#    def loadPotTemplate(self):
#        ''' load pot template image'''
#        fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
#        self.status.append('Loading image...')
#        app.processEvents()
#        self.potTemplate = cv2.imread(str(fname))[:,:,::-1]
#        if len(self.potList) > 0:

    def correctColor(self):
        if self.colorcardParams == None:
            if len(self.colorcardList) > 0:
                medianSize = cd.getMedianRectSize(self.colorcardList)
                capturedColorcards = cd.rectifyRectImages(self.image, self.colorcardList, medianSize)
                self.colorcardColors, _ = cd.getColorcardColors(capturedColorcards[0], GridSize = [6, 4])
                self.colorcardParams = cd.estimateColorParameters(cd.CameraTrax_24ColorCard, self.colorcardColors)
            else:
                self.status.append('Need to select a color card first.')
                return

        colorMatrix, colorConstant, colorGamma = self.colorcardParams
        self.imageCorrected = cd.correctColorVectorised(self.image.astype(np.float), colorMatrix, colorConstant, colorGamma)
        self.imageCorrected[np.where(self.imageCorrected < 0)] = 0
        self.imageCorrected[np.where(self.imageCorrected > 255)] = 255
        self.imageCorrected = self.imageCorrected.astype(np.uint8)
        self.updateFigure(self.imageCorrected)

    def savePipelineSettings(self):
        ''' save to pipeline setting file'''
        self.settingFileName = str(QtGui.QFileDialog.getSaveFileName(self, \
            'Save selection (default in ./_data)', self.timestreamRootPath))
        if len(self.settingFileName) == 0:
            return
        self.settingPath = os.path.relpath(os.path.dirname(self.settingFileName), self.timestreamRootPath)

        self.settings = {}
        if self.ImageSize != None and self.CameraMatrix != None and self.DistCoefs != None:
            undistortDic = {'cameraMatrix': self.CameraMatrix.tolist(), \
                          'distortCoefs': self.DistCoefs.tolist(), \
                          'imageSize': list(self.ImageSize),
                          'rotationAngle': self.rotationAngle + self.smaleRotationAngle
                         }
        else:
            undistortDic = {}
        self.settings['undistort'] = undistortDic

        if len(self.colorcardList) > 0:
            medianSize = cd.getMedianRectSize(self.colorcardList)
            capturedColorcards = cd.rectifyRectImages(self.image, self.colorcardList, medianSize)
            colorCardFile = 'CapturedColorcard.png'
            cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, colorCardFile), capturedColorcards[0][:,:,::-1].astype(np.uint8))
            colorcardColors, colorStd  = cd.getColorcardColors(capturedColorcards[0], GridSize = [6,4])
            colorcardPosition, Width, Height, Angle = cd.getRectangleParamters(self.colorcardList[0])
            colorcardDic = {'colorcardFile': colorCardFile,\
                            'colorcardPosition': colorcardPosition.tolist(),\
                            'colorcardTrueColors': cd.CameraTrax_24ColorCard,\
                            'settingPath': '_data'
                            }
        else:
            colorcardDic = {}
        self.settings['colorcarddetect'] = colorcardDic

        self.settings['colorcorrect'] = {'minIntensity': 15}

        if len(self.trayList) > 0:
            trayMedianSize = cd.getMedianRectSize(self.trayList)
            trayImages = cd.rectifyRectImages(self.image, self.trayList, trayMedianSize)
            trayDict = {}
            trayDict['settingPath'] = '_data'
            trayDict['trayNumber'] = len(self.trayList)
            trayDict['trayFiles'] = 'Tray_%02d.png'
            trayPositions = []
            for i,tray in enumerate(trayImages):
                cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, trayDict['trayFiles'] %i), tray[:,:,::-1].astype(np.uint8))
                trayPosition, Width, Height, Angle = cd.getRectangleParamters(self.trayList[i])
                trayPositions.append(trayPosition.tolist())
            trayDict['trayPositions'] = trayPositions
        else:
            trayDict = {}
        self.settings['traydetect'] = trayDict

        if len(self.potList) > 0:
            trayMedianSize = cd.getMedianRectSize(self.trayList)
            potPosition, Width, Height, Angle = cd.getRectangleParamters(self.potList[0])
            Width, Height = int(Width), int(Height)
            topLeft = [int(self.potList[0][0][0]), int(self.potList[0][0][1])]
            self.potImage = self.image[topLeft[1]:topLeft[1]+Height, topLeft[0]:topLeft[0]+Width, :]
            potFile = 'Pot.png'
            cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, potFile), self.potImage[:,:,::-1].astype(np.uint8))
            potDict = {}
            potDict['potPositions'] = potPosition.tolist()
            potDict['potSize'] = [int(Width), int(Height)]
            potDict['traySize'] = [int(trayMedianSize[0]), int(trayMedianSize[1])]
            potDict['potFile'] = potFile
            potDict['potTemplateFile'] = 'PotTemplate.png'
            potDict['settingPath'] = '_data'
            potTemplatePathIn = os.path.join(self.scriptPath, './data/PotTemplate.png')
            potTemplatePathOut = os.path.join(self.timestreamRootPath, self.settingPath, potDict['potTemplateFile'])
            shutil.copyfile(potTemplatePathIn, potTemplatePathOut)
            if self.potTemplate != None:
                potTemplateFile = 'potTemplate.png'
                cv2.imwrite(os.path.join(self.timestreamRootPath, self.settingPath, potTemplateFile), self.potTemplate[:,:,::-1])
                potDict['potTemplateFile'] = potTemplateFile
        else:
            potDict = {}
        self.settings['potdetect'] = potDict

        self.settings['plantextract'] = {'meth': 'method1',
                                         'methargs': {'threshold' : 0.6,
                                         'kSize' : 5, 'blobMinSize' : 50} }

        with open(self.settingFileName, 'w') as outfile:
            outfile.write( yaml.dump(self.settings, default_flow_style=None) )
            self.status.append('Saved initial data to ' + self.settingFileName)

    def testPipeline(self):
        ''' try running processing pipeline based on user inputs'''
        if self.tsImages != None:
            # load setting file if missing
            if self.settingFileName == None:
                settingFileName = str(QtGui.QFileDialog.getOpenFileName(self, 'Open pipeline setting file', self.ts.path))
                if len(settingFileName) > 0:
                    self.settingFileName = settingFileName
                else:
                    return
            # initialise pipeline
            if self.pl == None:
                f = file(self.settingFileName)
                self.settings = yaml.load(f)
                self.ts.data["settings"] = self.settings
                self.pl = pipeline.ImagePipeline(self.settings)
                # process only from undistortion to pot detection
                self.pl.pipeline = self.pl.pipeline[:5]

            # read next image instant of pipeline
            try:
                tsImage = self.tsImages.next()
                if tsImage == None:
                    self.status.append('Missing image.')
                    return
            except:
                tsImage = None
                self.status.append('There is no more images.')
                return
            self.status.append('Processing ' + tsImage.path)
            context = {"rts":self.ts, "wts":None, "img":tsImage}
            result = self.pl.process(context, [tsImage])
            self.image, potPosList2 = result
            potSize = self.settings[4][1]["potSize"]
            self.potList = []
            for potPosList in potPosList2:
                if potPosList == None:
                    continue
                for potPos in potPosList:
                    tl = [potPos[0] - potSize[0]//2, potPos[1] - potSize[1]//2]
                    bl = [potPos[0] - potSize[0]//2, potPos[1] + potSize[1]//2]
                    br = [potPos[0] + potSize[0]//2, potPos[1] + potSize[1]//2]
                    tr = [potPos[0] + potSize[0]//2, potPos[1] - potSize[1]//2]
                    self.potList.append([tl, bl, br, tr])
            self.updateFigure()
            self.status.append('Done')
        else:
            self.status.append('Pipeline or setting file is missing.')

    def rotateImage90Degrees(self):
        if self.image == None:
            self.status.append('No image to rotate.')
            return
        self.rotationAngle = self.rotationAngle + 90
        if self.rotationAngle >= 360:
            self.rotationAngle = self.rotationAngle - 360
        self.image = np.rot90(self.image) #.astype(uint8)
        self.status.append('Rot. angle = %d deg' %self.rotationAngle)
        self.updateFigure(resetFigure = True)

    def rotateSmallAngle(self, value):
        self.smaleRotationAngle = float(value)/4.0
        if self.image != None:
            self.rotatedImage = cd.rotateImage(self.image, self.smaleRotationAngle)
            self.updateFigure(self.rotatedImage)
            self.status.append('Rot. angle = %f deg' %(self.rotationAngle + self.smaleRotationAngle))

    def applySmallRotation(self):
        if self.image != None:
            self.image = cd.rotateImage(self.image, self.smaleRotationAngle)
            self.updateFigure()
            self.status.append('Apply small angle rotation')

    def onMouseClicked(self, event):
        if self.panMode or self.zoomMode:
            return
        print('click', event.button, event.xdata, event.ydata)

        if event.button == 1 and event.xdata != None and event.ydata != None:
            self.leftClicks.append([event.xdata, event.ydata])
            print('self.leftClicks =', self.leftClicks)
            Rect = []
            AspectRatio = None
            if self.trayRadioButton.isChecked():
                AspectRatio = self.trayAspectRatio
            elif self.colorcardRadioButton.isChecked():
                AspectRatio = self.colorcardAspectRatio
            elif self.potRadioButton.isChecked():
                AspectRatio = self.potAspectRatio

            if len(self.leftClicks) == 2 and AspectRatio != None:
                if self.colorcardRadioButton.isChecked():
                    Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio)
                if self.trayRadioButton.isChecked():
                    Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = self.trayRoundCheckBox.isChecked())
                elif self.potRadioButton.isChecked():
                    Rect = cd.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = True)
                self.leftClicks = []
            elif len(self.leftClicks) == 4:
                Rect = [[x,y] for x,y in self.leftClicks]
                Rect = cd.correctPointOrder(Rect)
                self.leftClicks = []

            if len(Rect) > 0:
                if self.trayRadioButton.isChecked():
                    self.trayList.append(Rect)
                    self.status.append('Added tray selection.')
                elif self.colorcardRadioButton.isChecked():
                    self.colorcardList.append(Rect)
                    self.status.append('Added color card selection.')
                else:
                    self.potList.append(Rect)
                    self.status.append('Added pot selection.')
            self.updateFigure()
        elif event.button == 3:
            # remove the last selection
            if len(self.leftClicks) > 0:
                self.leftClicks = self.leftClicks[:-1]
                self.status.append('Removed a previous click')
            else:
                if self.trayRadioButton.isChecked() and len(self.trayList) > 0:
                    self.trayList = self.trayList[:-1]
                    self.status.append('Removed a previous tray selection')
                elif self.colorcardRadioButton.isChecked() and len(self.colorcardList) > 0:
                    self.colorcardList = self.colorcardList[:-1]
                    self.status.append('Removed a previous color card selection.')
                elif self.potRadioButton.isChecked() and len(self.potList) > 0:
                    self.potList = self.potList[:-1]
                    self.status.append('Removed a previous pot selection')
            self.updateFigure()
        else:
            print('Ignored click')

    def onMouseMoves(self, event):
        if event.inaxes == self.ax:
            self.mousePosition.setText('x=%d, y=%d' %(event.xdata, event.ydata))
#            self.crosshair = [event.xdata, event.ydata]
        else:
            self.mousePosition.setText('')
#            self.crosshair = None
#        self.updateFigure()

    def keyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()

    def closeEvent(self, event):

        quit_msg = "Are you sure you want to exit the program?"
        reply = QtGui.QMessageBox.question(self, 'Message',
                         quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            self.saveInputs()
            event.accept()
        else:
            event.ignore()

    def saveInputs(self):
        with open('./.inputs.yml', 'w') as myfile:
            dicInputs = {'rootPath': str(self.timestreamText.text()),
                         'startDate': str(self.timestreamDateText.text()),
                         'timeInterval': str(self.timestreamTimeText.text()),
                        }
            myfile.write( yaml.dump(dicInputs, default_flow_style=None) )

    def loadPreviousInputs(self):
        try:
            with open('./.inputs.yml', 'r') as myfile:
                dicInputs = yaml.load(myfile)
                self.timestreamText.setText(dicInputs['rootPath'])
                self.timestreamDateText.setText(dicInputs['startDate'])
                self.timestreamTimeText.setText(dicInputs['timeInterval'])
        except:
            pass
Beispiel #7
0
class PlotWidget(Canvas):
    def __init__(self, parent, x_dimension, y_dimension):
        super(PlotWidget, self).__init__(Figure())

        self.setParent(parent)
        self.figure = Figure((x_dimension, y_dimension))
        self.canvas = Canvas(self.figure)

        self.axes = self.figure.add_subplot(111)

        # navigation toolbar
        self.nav = NavigationToolbar(self.canvas, self)
        self.nav.hide()

        # background color = white
        self.figure.set_facecolor('white')

        # global variables
        self.x_limit = None

        self.plot_style()

    def plot_properties(self, x_label, y_label, x_limit=None):
        """
        Plot properties and variables
            axis labels
            x axis label (limit)
        :param x_label: x axis labels
        :param y_label: y axis labels
        :param x_limit: number of x axis labels to display
        """

        if x_label and y_label is not None:
            self.axes.set_xlabel(x_label)
            self.axes.set_ylabel(y_label)

            self.x_limit = x_limit

    def plot_style(self):

        # change axes colors - grey
        axes = ["bottom", "top", "left", "right"]
        for ax in axes:
            self.axes.spines[ax].set_color(chart_colors["grey"])

        # change x-label and y-label color
        self.axes.xaxis.label.set_color(chart_colors["dark_grey"])
        self.axes.yaxis.label.set_color(chart_colors["dark_grey"])

        # change tick color
        self.axes.tick_params(axis='x', colors=chart_colors["grey"])
        self.axes.tick_params(axis='y', colors=chart_colors["grey"])

        # change font size - labels
        rc('font', **chart_font)

        # add grid - soft grey
        self.axes.grid(True)

    def plot_lines(self, x_values, y_values, *args):

        # number of columns - range
        columns = len(x_values)
        ind = np.arange(columns)

        # convert x_values to string and y_values to float - review!
        x_labels = np.array(x_values, dtype=str)
        y = np.array(y_values, dtype=float)

        # tick labels with dates
        if columns <= self.x_limit:
            self.axes.set_xticks(np.arange(columns))

        self.axes.xaxis.get_majorticklocs()
        self.axes.set_xticklabels(x_labels)

        # show y_values
        if not args:
            self.axes.plot(ind, y)
        else:
            # multiple series - to be implemented
            for arg in args:
                print arg
            pass

        self.figure.tight_layout()

    def plot_bars(self):
        pass

    def plot_stats(self):
        pass

    def plot_pie(self):
        pass

    def plot_polar(self):
        pass
Beispiel #8
0
class LMatplotlibWidget(LWidget):

    def __init__(self, parent=None):
        super(LMatplotlibWidget, self).__init__(parent)
        self.setName(WIDGET_NAME)
        self.buildUi()
        self.dataContainer = LMyDataContainer(self)
        self.actions = LMyActions(self.dataContainer,self)
        self.setDebugLevel(5)
        self.updateUi()

    @LInAndOut(DEBUG & WIDGETS)
    def buildUi(self):
        self.verticalLayout = QVBoxLayout(self)
        self.figure = Figure()
        self.canvas = Canvas(self.figure)	                # <-- figure required
        self.navigationToolbar = NavigationToolbar(self.canvas, self)
        self.verticalLayout.addWidget(self.canvas)
        self.verticalLayout.addWidget(self.navigationToolbar)

        #Canvas.setSizePolicy(self.canvas, QSizePolicy.Expanding, QSizePolicy.Expanding)
        #Canvas.updateGeometry(self.canvas)

    @LInAndOut(DEBUG & WIDGETS)
    def updateUi(self):
        left = self.dataContainer.leftMargin
        bottom = self.dataContainer.bottomMargin
        right = self.dataContainer.rightMargin
        top = self.dataContainer.topMargin
        wspace = self.dataContainer.verticalSeparation
        hspace = self.dataContainer.horizontalSeparation
        self.figure.subplots_adjust(left, bottom, right, top, wspace, hspace)

        if self.dataContainer.showNavigationToolbar:
            self.navigationToolbar.show()
        else:
            self.navigationToolbar.hide()

    #----------------------------------------------------------------------
    # Interface (set)

    @LInAndOut(DEBUG & WIDGETS)
    def setOptions(self, options):
        super(LMatplotlibWidget, self).setOptions(options)
        if options.verboseLevel is not None:
            self.setVerboseLevel(options.verboseLevel)
        if options.navigationToolbar is not None:
            self.showNavigationToolbar(options.navigationToolbar)
        self.updateUi()

    @LInAndOut(DEBUG & WIDGETS)
    def setVerboseLevel(self, level):
        self.dataContainer.verboseLevel = level

    #----------------------------------------------------------------------
    # Interface (get)

    def getFigure(self):
        return self.figure

    #----------------------------------------------------------------------
    # Interface (misc)

    @LInAndOut(DEBUG & WIDGETS)
    def showNavigationToolbar(self, flag=True):
        self.dataContainer.showNavigationToolbar = flag
        self.updateUi()

    @LInAndOut(DEBUG & WIDGETS)
    def snapshot(self, fileName = None):
        if fileName is None:
            caption = "%s - Save File" % self.name
            filter = "PNG Image (*.png);;All files (*.*)"
            fileName = QFileDialog.getSaveFileName(self.parent(), caption=caption,filter=filter )
            fileName = "%s" % fileName
        if len(fileName) > 0:
            self.figure.savefig(fileName, facecolor='w', edgecolor='w', orientation='portrait', papertype=None, format=None, transparent=False, bbox_inches=None, pad_inches=0.1)

    @LInAndOut(DEBUG & WIDGETS)
    def draw(self):
        self.canvas.draw()
Beispiel #9
0
class MplCanvas(FigureCanvas):
    '''
        描述:将matplotlib的figure嵌入到Qt中
        成员:
        figure:画图的窗口,是matplotlib中的figure对象
        axes:画布,matplotlib.Axes对象
        方法:这些方法可以重新实现
        getSnn:返回画的曲线,是s11.s21,s12,或者s22
        getDrawedLineType:S,群时延
        getFigure: 返回figure对象
        使用:
                得到axes直接进行画图
    '''
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        self.figure = Figure(figsize=(width, height), dpi=dpi)
        self.axes = self.figure.add_subplot(111)
        self.axes.hold(False)
        FigureCanvas.__init__(self, self.figure)
        self.setParent(parent)
        FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding,
                                   QtGui.QSizePolicy.Expanding)

        FigureCanvas.updateGeometry(self)
        self.toolbar = NavigationToolbar(self, self)
        self.toolbar.hide()
        self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.createPopMenu()

    def createPopMenu(self):
        #actions
        self.popmenu = QtGui.QMenu(self)
        self.snn_ac_group = QtGui.QActionGroup(self.popmenu)
        self.snn_ac_group.setExclusive(True)

        self.ac_s11 = QtGui.QAction('S11', self.popmenu)
        self.ac_s21 = QtGui.QAction('S21', self.popmenu)
        self.ac_s12 = QtGui.QAction('S12', self.popmenu)
        self.ac_s22 = QtGui.QAction('S22', self.popmenu)
        snn_actions = [self.ac_s11, self.ac_s21, self.ac_s12, self.ac_s22]

        for ac in snn_actions:
            ac.setCheckable(True)
            self.snn_ac_group.addAction(ac)
        self.ac_s11.setChecked(True)
        self.popmenu.addActions(snn_actions)

        self.ac_delay = QtGui.QAction('show delay', self.popmenu)
        self.ac_phase = QtGui.QAction('show phase', self.popmenu)
        self.ac_expanded = QtGui.QAction('show expanded', self.popmenu)
        self.type_ac_group = QtGui.QActionGroup(self.popmenu)
        types_actions = [self.ac_delay, self.ac_phase, self.ac_expanded]
        for ac in types_actions:
            ac.setCheckable(True)
            self.type_ac_group.addAction(ac)
        self.ac_phase.setChecked(True)
        self.popmenu.addActions(types_actions)
        self.setShowPopMenu(True)

    def setShowPopMenu(self, isShow):
        if isShow:
            self.connect(self,
                         SIGNAL('customContextMenuRequested(const QPoint &)'),
                         self.show_popmenu)
        else:
            self.disconnect(
                self, SIGNAL('customContextMenuRequested(const QPoint &)'),
                self.show_popmenu)

    def show_popmenu(self, pos):
        #self.popmenu.popup(self.mapToGlobal(pos))
        pass

    def getSnn(self):
        snn = self.snn_ac_group.checkedAction().text()
        if snn == 'S11':
            return 1
        elif snn == 'S21':
            return 2
        elif snn == 'S12':
            return 3
        else:
            return 4

    def getDrawedLineType(self):
        linetype = self.type_ac_group.checkedAction().text()
        if linetype == 'show delay':
            return 'delay'
        elif linetype == 'show phase':
            return 'phase'
        elif linetype == 'show expanded':
            return 'expanded'

    def getFigure(self):
        return self.figure
Beispiel #10
0
class MatplotlibWidget(QtGui.QWidget):
    """ This subclass of QtWidget will manage the widget drawing; name matches the class in the *_ui.py file"""

    # Global colors dictionary
    colors_dict = {
        "Discharge": "b",
        "Subsurface Flow": "g",
        "Impervious Flow": "SteelBlue",
        "Infiltration Excess": "SeaGreen",
        "Initial Abstracted Flow": "MediumBlue",
        "Overland Flow": "RoyalBlue",
        "PET": "orange",
        "AET": "DarkOrange",
        "Average Soil Root zone": "Gray",
        "Average Soil Unsaturated Zone": "DarkGray",
        "Snow Pack": "PowderBlue",
        "Precipitation": "SkyBlue",
        "Storage Deficit": "Brown",
        "Return Flow": "Aqua",
        "Water Use": "DarkCyan",
        "Discharge + Water Use": "DarkBlue"
    }

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

        # create object scope variables for watertxt data plot; used by radio buttons and span selector
        self.watertxt_data = None
        self.parameter = None
        self.color_str = None
        self.axes_text = None
        self.axes_radio = None
        self.parent = parent

        # create figure
        self.figure = Figure()

        # create canvas and set some of its properties
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setParent(parent)
        self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                  QtGui.QSizePolicy.Expanding)
        self.canvas.updateGeometry()
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.canvas.setFocus()

        # set up axes and its properties
        self.ax = self.figure.add_subplot(111)
        self.ax.grid(True)

        # create toolbar
        self.matplotlib_toolbar = NavigationToolbar(
            self.canvas, parent)  # the matplotlib toolbar object

        # create the layout
        self.layout = QtGui.QVBoxLayout()

        # add the widgets to the layout
        self.layout.addWidget(self.matplotlib_toolbar)
        self.layout.addWidget(self.canvas)

        # set the layout
        self.setLayout(self.layout)

    #-------------------------------- WATER.txt Parameter Plot ------------------------------------

    def setup_watertxt_plot(self):
        """ Setup the watertxt plot """

        self.clear_watertxt_plot()

        # set up axes and its properties
        self.axes = self.figure.add_subplot(111)
        self.axes.grid(True)

        # create radio buttons
        self.axes_radio = self.figure.add_axes(
            [0.01, 0.02, 0.10, 0.15]
        )  # [left, bottom, width, height] = fractions of figure width and height
        self.figure.subplots_adjust(bottom=0.2)
        self.radio_buttons = RadioButtons(ax=self.axes_radio,
                                          labels=("Span On", "Span Off"),
                                          active=1,
                                          activecolor="r")
        self.radio_buttons.on_clicked(self.toggle_selector)

        # create SpanSelector; invisble at first unless activated with toggle selector
        self.span_selector = SpanSelector(self.axes,
                                          self.on_select_axes,
                                          'horizontal',
                                          useblit=True,
                                          rectprops=dict(alpha=0.5,
                                                         facecolor='red'))
        self.span_selector.visible = False

    def plot_watertxt_parameter(self, watertxt_data, name):
        """ Plot a parameter from a WATER.txt file """

        self.reset_watertxt_plot()

        self.dates = watertxt_data["dates"]
        self.watertxt_data = watertxt_data
        self.parameter = watertxt.get_parameter(watertxt_data, name=name)

        assert self.parameter is not None, "Parameter name {} is not in watertxt_data".format(
            name)

        self.axes.set_title("Parameter: {}".format(self.parameter["name"]))
        self.axes.set_xlabel("Date")
        ylabel = "\n".join(wrap(self.parameter["name"], 60))
        self.axes.set_ylabel(ylabel)

        # get proper color that corresponds to parameter name
        self.color_str = self.colors_dict[name.split('(')[0].strip()]

        # plot parameter
        self.axes.plot(self.dates,
                       self.parameter["data"],
                       color=self.color_str,
                       label=self.parameter["name"],
                       linewidth=2)

        # legend; make it transparent
        handles, labels = self.axes.get_legend_handles_labels()
        legend = self.axes.legend(handles, labels, fancybox=True)
        legend.get_frame().set_alpha(0.5)
        legend.draggable(state=True)

        # show text of mean, max, min values on graph; use matplotlib.patch.Patch properies and bbox
        text = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(
            self.parameter["mean"], self.parameter["max"],
            self.parameter["min"])

        patch_properties = {
            "boxstyle": "round",
            "facecolor": "wheat",
            "alpha": 0.5
        }

        self.axes_text = self.axes.text(0.05,
                                        0.95,
                                        text,
                                        transform=self.axes.transAxes,
                                        fontsize=14,
                                        verticalalignment="top",
                                        horizontalalignment="left",
                                        bbox=patch_properties)

        # use a more precise date string for the x axis locations in the toolbar and rotate labels
        self.axes.fmt_xdata = mdates.DateFormatter("%Y-%m-%d")

        # rotate and align the tick labels so they look better; note that self.figure.autofmt_xdate() does not work because of the radio button axes
        for label in self.axes.get_xticklabels():
            label.set_ha("right")
            label.set_rotation(30)

        # draw the plot
        self.canvas.draw()

    def on_select_helper(self, xmin, xmax):
        """ Helper for on_select methods """

        # convert matplotlib float dates to a datetime format
        date_min = mdates.num2date(xmin)
        date_max = mdates.num2date(xmax)

        # put the xmin and xmax in datetime format to compare
        date_min = datetime.datetime(date_min.year, date_min.month,
                                     date_min.day, date_min.hour,
                                     date_min.minute)
        date_max = datetime.datetime(date_max.year, date_max.month,
                                     date_max.day, date_max.hour,
                                     date_max.minute)

        # find the indices that were selected
        indices = np.where((self.dates >= date_min) & (self.dates <= date_max))
        indices = indices[0]

        # get the selected dates and values
        selected_dates = self.dates[indices]
        selected_values = self.parameter["data"][indices]

        # compute simple stats on selected values
        selected_values_mean = nanmean(selected_values)
        selected_value_max = np.nanmax(selected_values)
        selected_value_min = np.nanmin(selected_values)

        return selected_dates, selected_values, selected_values_mean, selected_value_max, selected_value_min

    def on_select_axes(self, xmin, xmax):
        """ A select handler for SpanSelector that updates axes with the new x and y limits selected by user """

        selected_dates, selected_values, selected_values_mean, selected_value_max, selected_value_min = self.on_select_helper(
            xmin, xmax)

        # plot the selected values and update plots limits and text
        self.axes.plot(selected_dates, selected_values, self.color_str)
        self.axes.set_xlim(selected_dates[0], selected_dates[-1])
        self.axes.set_ylim(selected_values.min(), selected_values.max())

        text = 'mean = %.2f\nmax = %.2f\nmin = %.2f' % (
            selected_values_mean, selected_value_max, selected_value_min)
        self.axes_text.set_text(text)

        # draw the updated plot
        self.canvas.draw()

    def toggle_selector(self, radio_button_label):
        """ 
        A toggle radio buttons for the matplotlib SpanSelector widget.
        """

        if radio_button_label == "Span On":
            self.span_selector.visible = True
            self.matplotlib_toolbar.hide()

        elif radio_button_label == "Span Off":
            self.span_selector.visible = False
            self.matplotlib_toolbar.show()
            self.plot_watertxt_parameter(watertxt_data=self.watertxt_data,
                                         name=self.parameter["name"])

    def clear_watertxt_plot(self):
        """ Clear the plot axes """

        self.figure.clear()
        self.canvas.draw()

    def reset_watertxt_plot(self):
        """ Clear the plot axes """

        self.axes.clear()
        self.canvas.draw()
        self.axes.grid(True)

    #-------------------------------- WATER.txt Parameter Comparison Plot ------------------------------------

    def setup_watertxtcmp_plot(self):
        """ Setup the watertxt plot """

        self.clear_watertxtcmp_plot()

        # set up axes and its properties
        self.axes1 = self.figure.add_subplot(211)
        self.axes2 = self.figure.add_subplot(212, sharex=self.axes1)

        self.axes1.grid(True)
        self.axes2.grid(True)

    def plot_watertxtcmp_parameter(self, watertxt_data1, watertxt_data2,
                                   filename1, filename2, name):
        """ Plot a parameter from a WATER.txt file """

        self.reset_watertxtcmp_plot()

        dates = watertxt_data1["dates"]

        parameter1 = watertxt.get_parameter(watertxt_data=watertxt_data1,
                                            name=name)
        parameter2 = watertxt.get_parameter(watertxt_data=watertxt_data2,
                                            name=name)

        assert parameter1 is not None, "Parameter name {} is not in watertxt_data".format(
            name)
        assert parameter2 is not None, "Parameter name {} is not in watertxt_data".format(
            name)

        # calculate the difference
        diff = parameter2["data"] - parameter1["data"]

        # plot parameters on axes1
        self.axes1.plot(dates,
                        parameter1["data"],
                        color="b",
                        label=filename1 + ": " + parameter1["name"],
                        linewidth=2)
        self.axes1.hold(True)
        self.axes1.plot(dates,
                        parameter2["data"],
                        color="r",
                        label=filename2 + ": " + parameter2["name"],
                        linewidth=2)

        # plot the difference on axes2
        self.axes2.plot(dates, diff, color="k", linewidth=2)

        # add title, labels, legend
        self.axes1.set_title(parameter1["name"])

        self.axes2.set_xlabel("Date")
        self.axes2.set_ylabel("Difference")

        handles1, labels1 = self.axes1.get_legend_handles_labels()
        legend1 = self.axes1.legend(handles1, labels1, fancybox=True)
        legend1.get_frame().set_alpha(0.5)
        legend1.draggable(state=True)

        # show text of mean, max, min values on graph; use matplotlib.patch.Patch properies and bbox
        text1 = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(
            parameter1["mean"], parameter1["max"], parameter1["min"])
        text2 = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(
            parameter2["mean"], parameter2["max"], parameter2["min"])

        text_diff = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(
            nanmean(diff), np.max(diff), np.min(diff))

        patch_properties1 = {
            "boxstyle": "round",
            "facecolor": "b",
            "alpha": 0.5
        }
        patch_properties2 = {
            "boxstyle": "round",
            "facecolor": "r",
            "alpha": 0.5
        }
        patch_properties_diff = {
            "boxstyle": "round",
            "facecolor": "wheat",
            "alpha": 0.5
        }

        self.axes1.text(0.02,
                        0.95,
                        text1,
                        transform=self.axes1.transAxes,
                        fontsize=12,
                        verticalalignment="top",
                        horizontalalignment="left",
                        bbox=patch_properties1)
        self.axes1.text(0.02,
                        0.45,
                        text2,
                        transform=self.axes1.transAxes,
                        fontsize=12,
                        verticalalignment="top",
                        horizontalalignment="left",
                        bbox=patch_properties2)
        self.axes2.text(0.02,
                        0.95,
                        text_diff,
                        transform=self.axes2.transAxes,
                        fontsize=12,
                        verticalalignment="top",
                        horizontalalignment="left",
                        bbox=patch_properties_diff)

        # use a more precise date string for the x axis locations in the toolbar and rotate labels
        self.axes2.fmt_xdata = mdates.DateFormatter("%Y-%m-%d")

        # rotate and align the tick labels so they look better
        self.figure.autofmt_xdate()

        # draw the plot
        self.canvas.draw()

    def clear_watertxtcmp_plot(self):
        """ Clear the plot axes """

        self.figure.clear()
        self.canvas.draw()

    def reset_watertxtcmp_plot(self):
        """ Clear the plot axes """

        self.axes1.clear()
        self.axes2.clear()
        self.canvas.draw()
        self.axes1.grid(True)
        self.axes2.grid(True)

    #-------------------------------- Basemap Plot ------------------------------------

    def setup_basemap_plot(self):
        """ Setup the watertxt plot """

        self.clear_basemap_plot()

        # set up axes and its properties
        self.basemap_axes = self.figure.add_subplot(111)
        self.basemap_axes.grid(True)

    def get_map_extents(self, shapefiles, shp_name=None):
        """   
        Get max and min extent coordinates from a list of shapefiles to use as the 
        extents on the map. Use the map extents to calculate the map center and the 
        standard parallels.

        Parameters
        ----------
        shapefiles : list 
            List of shapefile_data dictionaries 
        shp_name : string
            String name of shapefile to use for getting map extents 

        Returns
        -------
        extent_coords : dictionary 
            Dictionary containing "lon_min", "lon_max", "lat_max", "lat_min" keys with respective calculated values
        center_coords : dictionary 
            Dictionary containing "lon", "lat" keys with respective calculated values
        standard_parallels : dictionary 
            Dictionary containing "first", "second" keys with respective calculated values (first = min(lat), second = max(lat))   
        """
        extent_coords = {}
        center_coords = {}
        standard_parallels = {}

        lons = []
        lats = []

        if shp_name:
            for shapefile_data in shapefiles:
                if shp_name in shapefile_data["name"].split("_")[0]:
                    lons.append(shapefile_data["extents"][0:2])
                    lats.append(shapefile_data["extents"][2:])

        else:
            for shapefile_data in shapefiles:
                lons.append(shapefile_data["extents"][0:2])
                lats.append(shapefile_data["extents"][2:])

        extent_coords["lon_min"] = np.min(lons)
        extent_coords["lon_max"] = np.max(lons)
        extent_coords["lat_min"] = np.min(lats)
        extent_coords["lat_max"] = np.max(lats)

        center_coords["lon"] = np.mean(
            [extent_coords["lon_min"], extent_coords["lon_max"]])
        center_coords["lat"] = np.mean(
            [extent_coords["lat_min"], extent_coords["lat_max"]])

        standard_parallels["first"] = extent_coords["lat_min"]
        standard_parallels["second"] = extent_coords["lat_max"]

        return extent_coords, center_coords, standard_parallels

    def plot_shapefiles_map(self,
                            shapefiles,
                            display_fields=[],
                            colors=[],
                            title=None,
                            shp_name=None,
                            buff=1.0):
        """   
        Generate a map showing all the shapefiles in the shapefile_list.  
        Shapefiles should be in a Geographic Coordinate System (longitude and 
        latitude coordinates) such as World WGS84; Matplotlib"s basemap library 
        does the proper transformation to a projected coordinate system.  The projected
        coordinate system used is Albers Equal Area.
        
        Parameters
        ----------
        shapefiles : list 
            List of dictionaries containing shapefile information
        title : string 
            String title for plot
        display_fields : list 
            List of strings that correspond to a shapefile field where the corresponding value(s) will be displayed.
        colors : list
            List of strings that correspond to colors to be displayed
        shp_name : string
            String name of shapefile to use for getting map extents 
        buff : float
            Float value in coordinate degrees to buffer the map with
        """

        self.setup_basemap_plot()

        extent_coords, center_coords, standard_parallels = self.get_map_extents(
            shapefiles, shp_name=shp_name)

        # create the basemap object with Albers Equal Area Conic Projection
        bmap = Basemap(projection="aea",
                       llcrnrlon=extent_coords["lon_min"] - buff,
                       llcrnrlat=extent_coords["lat_min"] - buff,
                       urcrnrlon=extent_coords["lon_max"] + buff,
                       urcrnrlat=extent_coords["lat_max"] + buff,
                       lat_1=standard_parallels["first"],
                       lat_2=standard_parallels["second"],
                       lon_0=center_coords["lon"],
                       lat_0=center_coords["lat"],
                       resolution="h",
                       area_thresh=10000,
                       ax=self.basemap_axes)

        # have basemap object plot background stuff
        bmap.drawcoastlines()
        bmap.drawcountries()
        bmap.drawrivers(linewidth=1, color="blue")
        bmap.drawstates()
        bmap.drawmapboundary(fill_color="aqua")
        bmap.fillcontinents(color="coral", lake_color="aqua")
        bmap.drawparallels(np.arange(-80., 81., 1.),
                           labels=[1, 0, 0, 0],
                           linewidth=0.5)
        bmap.drawmeridians(np.arange(-180., 181., 1.),
                           labels=[0, 0, 0, 1],
                           linewidth=0.5)

        # plot each shapefile on the basemap
        legend_handles = []
        legend_labels = []
        colors_index = 0
        colors_list = [
            "b", "g", "y", "r", "c", "y", "m", "orange", "aqua", "darksalmon",
            "gold", "k"
        ]
        for shapefile_data in shapefiles:

            # set up colors to use
            if colors:
                color = colors[colors_index]
            elif colors_index > len(colors_list) - 1:
                color = np.random.rand(3, )
            else:
                color = colors_list[colors_index]

            full_path = os.path.join(shapefile_data["path"],
                                     shapefile_data["name"].split(".")[0])

            shp_tuple = bmap.readshapefile(
                full_path, "shp", drawbounds=False
            )  # use basemap shapefile reader for ease of plotting
            for shape_dict, shape in zip(
                    bmap.shp_info, bmap.shp
            ):  # zip the shapefile information and its shape as defined by basemap

                if shapefile_data["type"] == "POLYGON":
                    p1 = Polygon(shape,
                                 facecolor=color,
                                 edgecolor="k",
                                 linewidth=1,
                                 alpha=0.7,
                                 label=shapefile_data["name"])
                    self.basemap_axes.add_patch(p1)
                    xx, yy = zip(*shape)
                    txt_x = str(np.mean(xx))
                    txt_y = str(np.mean(yy))

                elif shapefile_data["type"] == "POINT":
                    x, y = shape

                    if "usgsgages" in shapefile_data["name"].split("_")[0]:
                        p1 = bmap.plot(x,
                                       y,
                                       color=color,
                                       marker="^",
                                       markersize=10,
                                       label=shapefile_data["name"])
                    elif "wateruse" in shapefile_data["name"].split("_")[0]:
                        p1 = bmap.plot(x,
                                       y,
                                       color=color,
                                       marker="o",
                                       markersize=5,
                                       label=shapefile_data["name"])
                    else:
                        print("what!!")
                        p1 = bmap.plot(x,
                                       y,
                                       color=color,
                                       marker="o",
                                       markersize=10,
                                       label=shapefile_data["name"])

                    txt_x = str(x)
                    txt_y = str(y)

                else:
                    xx, yy = zip(*shape)
                    p1 = bmap.plot(xx,
                                   yy,
                                   linewidth=1,
                                   color=color,
                                   label=shapefile_data["name"])
                    txt_x = str(np.mean(xx))
                    txt_y = str(np.mean(yy))

                if isinstance(p1, list):
                    p1 = p1[0]

                # control text display of shapefile fields
                for display_field in display_fields:
                    if display_field in shape_dict.keys():
                        self.basemap_axes.text(txt_x,
                                               txt_y,
                                               shape_dict[display_field],
                                               color="k",
                                               fontsize=12,
                                               fontweight="bold")

            colors_index += 1
            legend_handles.append(p1)
            legend_labels.append(shapefile_data["name"].split("_")[0])

        handles, labels = self.basemap_axes.get_legend_handles_labels()

        # edit the contents of handles and labels to only show 1 legend per shape
        handles = legend_handles
        labels = legend_labels
        legend = self.basemap_axes.legend(handles,
                                          labels,
                                          fancybox=True,
                                          numpoints=1)
        legend.get_frame().set_alpha(0.5)
        legend.draggable(state=True)

        # draw the plot
        self.canvas.draw()

    def clear_basemap_plot(self):
        """ Clear the plot axes """

        self.figure.clear()
        self.canvas.draw()

    def reset_basemap_plot(self):
        """ Clear the plot axes """

        self.basemap_axes.clear()
        self.canvas.draw()
Beispiel #11
0
class Window(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.setWindowTitle('OpenWave-2KE')
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("openwave.ico"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        #Waveform area.
        self.figure = plt.figure()
        self.figure.set_facecolor('white')

        self.canvas = FigureCanvas(self.figure)
        self.canvas.setMinimumSize(800,  400)
        
        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()

        #Zoom In/out and Capture Buttons
        self.zoomBtn = QtGui.QPushButton('Zoom')
        self.zoomBtn.setFixedSize(100, 30)
        self.zoomBtn.clicked.connect(self.toolbar.zoom)

        self.panBtn = QtGui.QPushButton('Pan')
        self.panBtn.setFixedSize(100, 30)
        self.panBtn.clicked.connect(self.toolbar.pan)

        self.homeBtn = QtGui.QPushButton('Home')
        self.homeBtn.setFixedSize(100, 30)
        self.homeBtn.clicked.connect(self.toolbar.home)

        self.captureBtn = QtGui.QPushButton('Capture')
        self.captureBtn.setFixedSize(100, 50)
        self.captureBtn.clicked.connect(self.captureAction)
        if(portNum==-1):
            self.captureBtn.setEnabled(False)

        #Type: Raw Data/Image
        self.typeBtn = QtGui.QPushButton('Raw Data')
        self.typeBtn.setToolTip("Switch to get raw data or image from DSO.")
        self.typeBtn.setFixedSize(120, 50)
        self.typeFlag=True #Initial state -> Get raw data
        self.typeBtn.setCheckable(True)
        self.typeBtn.setChecked(True)
        self.typeBtn.clicked.connect(self.typeAction)
        
        #Channel Selection.
        self.ch1checkBox = QtGui.QCheckBox('CH1')
        self.ch1checkBox.setFixedSize(60, 30)
        self.ch2checkBox = QtGui.QCheckBox('CH2')
        self.ch2checkBox.setFixedSize(60, 30)
        if(dso.chnum==4):
            self.ch3checkBox = QtGui.QCheckBox('CH3')
            self.ch3checkBox.setFixedSize(60, 30)
            self.ch4checkBox = QtGui.QCheckBox('CH4')
            self.ch4checkBox.setFixedSize(60, 30)
        
        #Set channel selection layout.
        self.selectLayout = QtGui.QHBoxLayout()
        self.selectLayout.addWidget(self.ch1checkBox)
        self.selectLayout.addWidget(self.ch2checkBox)
        if(dso.chnum==4):
            self.selectLayout2 = QtGui.QHBoxLayout()
            self.selectLayout2.addWidget(self.ch3checkBox)
            self.selectLayout2.addWidget(self.ch4checkBox)

        self.typeLayout = QtGui.QHBoxLayout()
        self.typeLayout.addWidget(self.typeBtn)
        self.typeLayout.addLayout(self.selectLayout)
        if(dso.chnum==4):
            self.typeLayout.addLayout(self.selectLayout2)

        #Save/Load/Quit button
        self.saveBtn = QtGui.QPushButton('Save')
        self.saveBtn.setFixedSize(100, 50)
        self.saveMenu = QtGui.QMenu(self)
        self.csvAction = self.saveMenu.addAction("&As CSV File")
        self.pictAction = self.saveMenu.addAction("&As PNG File")
        self.saveBtn.setMenu(self.saveMenu)
        self.saveBtn.setToolTip("Save waveform to CSV file or PNG file.")
        self.connect(self.csvAction, QtCore.SIGNAL("triggered()"), self.saveCsvAction)
        self.connect(self.pictAction, QtCore.SIGNAL("triggered()"), self.savePngAction)

        self.loadBtn = QtGui.QPushButton('Load')
        self.loadBtn.setToolTip("Load CHx's raw data from file(*.csv, *.lsf).")
        self.loadBtn.setFixedSize(100, 50)
        self.loadBtn.clicked.connect(self.loadAction)

        self.quitBtn = QtGui.QPushButton('Quit')
        self.quitBtn.setFixedSize(100, 50)
        self.quitBtn.clicked.connect(self.quitAction)

        # set the layout
        self.waveLayout = QtGui.QHBoxLayout()
        self.waveLayout.addWidget(self.canvas)
        
        self.wave_box=QtGui.QVBoxLayout()
        self.wave_box.addLayout(self.waveLayout)
        
        self.wavectrlLayout = QtGui.QHBoxLayout()
        self.wavectrlLayout.addWidget(self.zoomBtn)
        self.wavectrlLayout.addWidget(self.panBtn)
        self.wavectrlLayout.addWidget(self.homeBtn)
        self.wavectrlLayout.addWidget(self.captureBtn)
        
        self.saveloadLayout = QtGui.QHBoxLayout()
        self.saveloadLayout.addWidget(self.saveBtn)
        self.saveloadLayout.addWidget(self.loadBtn)
        self.saveloadLayout.addWidget(self.quitBtn)
        
        self.ctrl_box=QtGui.QHBoxLayout()
        self.ctrl_box.addLayout(self.typeLayout)
        self.ctrl_box.addLayout(self.saveloadLayout)
        
        main_box=QtGui.QVBoxLayout()
        main_box.addLayout(self.wave_box)         #Waveform area.
        main_box.addLayout(self.wavectrlLayout)   #Zoom In/Out...
        main_box.addLayout(self.ctrl_box)         #Save/Load/Quit
        self.setLayout(main_box)
        
    def typeAction(self):
        if(self.typeFlag==True):
            self.typeFlag=False
            self.typeBtn.setText("Image")
            self.csvAction.setEnabled(False)
        else:
            self.typeFlag=True
            self.typeBtn.setText("Raw Data")
            self.csvAction.setEnabled(True)
        self.typeBtn.setChecked(self.typeFlag)
        self.ch1checkBox.setEnabled(self.typeFlag)
        self.ch2checkBox.setEnabled(self.typeFlag)
        if(dso.chnum==4):
            self.ch3checkBox.setEnabled(self.typeFlag)
            self.ch4checkBox.setEnabled(self.typeFlag)

    def saveCsvAction(self):
        if(self.typeFlag==True): #Save raw data to csv file.
            file_name=QtGui.QFileDialog.getSaveFileName(self, "Save as", '', "Fast CSV File(*.csv)")[0]
            num=len(dso.ch_list)
            #print num
            for ch in xrange(num):
                if(dso.info[ch]==[]):
                    print('Failed to save data, raw data information is required!')
                    return
            f = open(file_name, 'w')
            item=len(dso.info[0])
            #Write file header.
            f.write('%s, \n' % dso.info[0][0])
            for x in xrange(1,  25):
                str=''
                for ch in xrange(num):
                    str+=('%s,' % dso.info[ch][x])
                str+='\n'
                f.write(str)
            str=''
            if(num==1):
                str+=('%s,' % dso.info[0][25])
            else:
                for ch in xrange(num):
                    str+=('%s,,' % dso.info[ch][25])
            str+='\n'
            f.write(str)
            #Write raw data.
            item=len(dso.iWave[0])
            #print item
            tenth=int(item/10)
            n_tenth=tenth-1
            percent=10
            for x in xrange(item):
                str=''
                if(num==1):
                    str+=('%s,' % dso.iWave[0][x])
                else:
                    for ch in xrange(num):
                        str+=('%s,,' % dso.iWave[ch][x])
                str+='\n'
                f.write(str)
                if(x==n_tenth):
                    n_tenth+=tenth
                    print('%3d %% Saved\r'%percent),
                    percent+=10
            f.close()

    def savePngAction(self):
        #Save figure to png file.
        file_name=QtGui.QFileDialog.getSaveFileName(self, "Save as", '', "PNG File(*.png)")[0]
        if(file_name==''):
            return
        if(self.typeFlag==True): #Save raw data waveform as png file.
            main.figure.savefig(file_name)
            print('Saved image to %s.'%file_name)
        else:  #Save figure to png file.
            dso.im.save(file_name)
            print('Saved image to %s.'%file_name)

    def loadAction(self):
        dso.ch_list=[]
        full_path_name=QtGui.QFileDialog.getOpenFileName(self,self.tr("Open File"),".","CSV/LSF files (*.csv *.lsf);;All files (*.*)")  
        sFileName=unicode(full_path_name).split(',')[0][3:-1] #For PySide
        print sFileName
        if(len(sFileName)<=0):
            return
        if os.path.exists(sFileName):
            print 'Reading file...'
            count=dso.readRawDataFile(sFileName)
            #Draw waveform.
            if(count>0):
                total_chnum=len(dso.ch_list)
                if(total_chnum==0):
                    return
                if(dso.dataMode=='Fast'):
                    self.drawWaveform(0)
                else:
                    self.drawWaveform(0)
        else:
            print('File not found!')

    def quitAction(self):
        if(portNum!=-1):
            dso.IO.close()
        self.close()
    
    def captureAction(self):
        dso.iWave=[[], [], [], []]
        dso.ch_list=[]
        if(self.typeFlag==True): #Get raw data.
            draw_flag=False
            #Turn on the selected channels.
            if((self.ch1checkBox.isChecked()==True) and (dso.isChannelOn(1)==False)):
                dso.write(":CHAN1:DISP ON\n")           #Set CH1 on.
            if((self.ch2checkBox.isChecked()==True) and (dso.isChannelOn(2)==False)):
                dso.write(":CHAN2:DISP ON\n")           #Set CH2 on.
            if(dso.chnum==4):
                if((self.ch3checkBox.isChecked()==True) and (dso.isChannelOn(3)==False)):
                    dso.write(":CHAN3:DISP ON\n")       #Set CH3 on.
                if((self.ch4checkBox.isChecked()==True) and (dso.isChannelOn(4)==False)):
                    dso.write(":CHAN4:DISP ON\n")       #Set CH4 on.
            #Get all the selected channel's raw datas.
            if(self.ch1checkBox.isChecked()==True):
                dso.getRawData(True, 1)              #Read CH1's raw data from DSO (including header).
            if(self.ch2checkBox.isChecked()==True):
                dso.getRawData(True, 2)              #Read CH2's raw data from DSO (including header).
            if(dso.chnum==4):
                if(self.ch3checkBox.isChecked()==True):
                    dso.getRawData(True, 3)          #Read CH3's raw data from DSO (including header).
                if(self.ch4checkBox.isChecked()==True):
                    dso.getRawData(True, 4)          #Read CH4's raw data from DSO (including header).
            #Draw waveform.
            total_chnum=len(dso.ch_list)
            if(total_chnum==0):
                return
            if(self.drawWaveform(1)==-1):
                time.sleep(5)
                self.drawWaveform(0)
        else: #Get image.
            dso.write(':DISP:OUTP?\n')                 #Send command to get image from DSO.
            dso.getBlockData()
            dso.RleDecode()
            self.showImage()
            plt.tight_layout(True)
            self.canvas.draw()
            print('Image is ready!')

    def showImage(self):
        #Turn the ticks off and show image.
        plt.clf()
        ax = plt.gca()
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        plt.imshow(dso.im)

    def drawWaveform(self, mode):
        total_chnum=len(dso.ch_list)
        num=dso.points_num
        ch_colortable=['#C0B020',  '#0060FF',  '#FF0080',  '#00FF60']
        ch=int(dso.ch_list[0][2])-1 #Get the channel of first waveform.
        plt.cla()
        plt.clf()
        #Due to the memory limitation of matplotlib, we must reduce the sample points.
        if(num==10000000):
            if(total_chnum>2):
                down_sample_factor=4
            elif(total_chnum==2):
                down_sample_factor=4
            else:
                down_sample_factor=1
            num=num/down_sample_factor
        else:
            down_sample_factor=1
        dt=dso.dt[0] #Get dt from the first opened channel.
        t_start=dso.hpos[0]-num*dt/2
        t_end  =dso.hpos[0]+num*dt/2
        t = np.arange(t_start, t_end, dt)
        #print t_start, t_end, dt, len(t)
        if((len(t)-num)==1): #Avoid floating point rounding error.
            t=t[:-1]
        wave_type='-' #Set waveform type to vector.
        #Draw waveforms.
        ax=[[], [], [], []]
        p=[]
        for ch in xrange(total_chnum):
            if(ch==0):
                ax[ch]=host_subplot(111, axes_class=AA.Axes)
                ax[ch].set_xlabel("Time (sec)")
            else:
                ax[ch]=ax[0].twinx()
            ax[ch].set_ylabel("%s Units: %s" %(dso.ch_list[ch],  dso.vunit[ch]))
            ch_color=ch_colortable[int(dso.ch_list[ch][2])-1]
            if(ch>1):
                new_fixed_axis = ax[ch].get_grid_helper().new_fixed_axis
                ax[ch].axis["right"] = new_fixed_axis(loc="right", axes=ax[ch], offset=(60*(ch-1), 0))
            ax[ch].set_xlim(t_start, t_end)
            ax[ch].set_ylim(-4*dso.vdiv[ch]-dso.vpos[ch], 4*dso.vdiv[ch]-dso.vpos[ch]) #Setup vertical display range.
            fwave=dso.convertWaveform(ch, down_sample_factor)
            #print('Length=%d'%(len(fwave)))
            if(ch==0):
                try:
                    p=ax[ch].plot(t, fwave, color=ch_color, ls=wave_type, label = dso.ch_list[ch])
                except:
                    if(mode==1):
                        #print sys.exc_info()[0]
                        time.sleep(5)
                        print 'Trying to plot again!',
                    return -1
            else:
                try:
                    p+=ax[ch].plot(t, fwave, color=ch_color, ls=wave_type, label = dso.ch_list[ch])
                except:
                    if(mode==1):
                        #print sys.exc_info()[0]
                        time.sleep(5)
                        print 'Trying to plot again!',
                    return -1
        if(total_chnum>1):
            labs = [l.get_label() for l in p]
            plt.legend(p, labs,   loc='upper right')
        plt.tight_layout() 
        self.canvas.draw()
        del ax, t, p
        return 0
class MatplotlibWidget(QtGui.QWidget):
    """ This subclass of QtWidget will manage the widget drawing; name matches the class in the *_ui.py file"""    
 
    # Global colors dictionary
    colors_dict = {
        "Discharge": "b",
        "Subsurface Flow": "g",
        "Impervious Flow": "SteelBlue",
        "Infiltration Excess": "SeaGreen",
        "Initial Abstracted Flow": "MediumBlue",
        "Overland Flow": "RoyalBlue",
        "PET": "orange",
        "AET": "DarkOrange",
        "Average Soil Root zone": "Gray",
        "Average Soil Unsaturated Zone": "DarkGray",
        "Snow Pack": "PowderBlue",
        "Precipitation": "SkyBlue",
        "Storage Deficit": "Brown",
        "Return Flow": "Aqua",
        "Water Use": "DarkCyan",
        "Discharge + Water Use": "DarkBlue"
    }

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

        # create object scope variables for watertxt data plot; used by radio buttons and span selector
        self.watertxt_data = None
        self.parameter = None
        self.color_str = None
        self.axes_text = None
        self.axes_radio = None
        self.parent = parent

        # create figure
        self.figure = Figure()

        # create canvas and set some of its properties
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setParent(parent) 
        self.canvas.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        self.canvas.updateGeometry()
        self.canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.canvas.setFocus()

        # set up axes and its properties
        self.ax = self.figure.add_subplot(111) 
        self.ax.grid(True)

        # create toolbar  
        self.matplotlib_toolbar = NavigationToolbar(self.canvas, parent) # the matplotlib toolbar object

        # create the layout
        self.layout = QtGui.QVBoxLayout()

        # add the widgets to the layout
        self.layout.addWidget(self.matplotlib_toolbar)
        self.layout.addWidget(self.canvas)

        # set the layout
        self.setLayout(self.layout)

    #-------------------------------- WATER.txt Parameter Plot ------------------------------------ 

    def setup_watertxt_plot(self):
        """ Setup the watertxt plot """

        self.clear_watertxt_plot()

        # set up axes and its properties
        self.axes = self.figure.add_subplot(111) 
        self.axes.grid(True)

        # create radio buttons
        self.axes_radio = self.figure.add_axes([0.01, 0.02, 0.10, 0.15])        # [left, bottom, width, height] = fractions of figure width and height
        self.figure.subplots_adjust(bottom=0.2)
        self.radio_buttons = RadioButtons(ax = self.axes_radio, labels = ("Span On", "Span Off"), active = 1, activecolor= "r")
        self.radio_buttons.on_clicked(self.toggle_selector)

        # create SpanSelector; invisble at first unless activated with toggle selector        
        self.span_selector = SpanSelector(self.axes, self.on_select_axes, 'horizontal', useblit = True, rectprops = dict(alpha=0.5, facecolor='red'))
        self.span_selector.visible = False

    def plot_watertxt_parameter(self, watertxt_data, name): 
        """ Plot a parameter from a WATER.txt file """

        self.reset_watertxt_plot()

        self.dates = watertxt_data["dates"]
        self.watertxt_data = watertxt_data
        self.parameter = watertxt.get_parameter(watertxt_data, name = name)     

        assert self.parameter is not None, "Parameter name {} is not in watertxt_data".format(name)

        self.axes.set_title("Parameter: {}".format(self.parameter["name"]))
        self.axes.set_xlabel("Date")
        ylabel = "\n".join(wrap(self.parameter["name"], 60))
        self.axes.set_ylabel(ylabel)

        # get proper color that corresponds to parameter name
        self.color_str = self.colors_dict[name.split('(')[0].strip()]

        # plot parameter    
        self.axes.plot(self.dates, self.parameter["data"], color = self.color_str, label = self.parameter["name"], linewidth = 2)   

        # legend; make it transparent    
        handles, labels = self.axes.get_legend_handles_labels()
        legend = self.axes.legend(handles, labels, fancybox = True)
        legend.get_frame().set_alpha(0.5)
        legend.draggable(state=True)

        # show text of mean, max, min values on graph; use matplotlib.patch.Patch properies and bbox
        text = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(self.parameter["mean"], self.parameter["max"], self.parameter["min"])

        patch_properties = {"boxstyle": "round", "facecolor": "wheat", "alpha": 0.5}
                       
        self.axes_text = self.axes.text(0.05, 0.95, text, transform = self.axes.transAxes, fontsize = 14, 
                                        verticalalignment = "top", horizontalalignment = "left", bbox = patch_properties)    

        # use a more precise date string for the x axis locations in the toolbar and rotate labels
        self.axes.fmt_xdata = mdates.DateFormatter("%Y-%m-%d")

        # rotate and align the tick labels so they look better; note that self.figure.autofmt_xdate() does not work because of the radio button axes 
        for label in self.axes.get_xticklabels():
            label.set_ha("right")
            label.set_rotation(30)

        # draw the plot
        self.canvas.draw()

    def on_select_helper(self, xmin, xmax):
        """ Helper for on_select methods """

        # convert matplotlib float dates to a datetime format
        date_min = mdates.num2date(xmin)
        date_max = mdates.num2date(xmax) 

        # put the xmin and xmax in datetime format to compare
        date_min = datetime.datetime(date_min.year, date_min.month, date_min.day, date_min.hour, date_min.minute)    
        date_max = datetime.datetime(date_max.year, date_max.month, date_max.day, date_max.hour, date_max.minute)

        # find the indices that were selected    
        indices = np.where((self.dates >= date_min) & (self.dates <= date_max))
        indices = indices[0]
        
        # get the selected dates and values
        selected_dates = self.dates[indices]
        selected_values = self.parameter["data"][indices]

        # compute simple stats on selected values 
        selected_values_mean = nanmean(selected_values)
        selected_value_max = np.nanmax(selected_values)
        selected_value_min = np.nanmin(selected_values)

        return selected_dates, selected_values, selected_values_mean, selected_value_max, selected_value_min

    def on_select_axes(self, xmin, xmax):
        """ A select handler for SpanSelector that updates axes with the new x and y limits selected by user """

        selected_dates, selected_values, selected_values_mean, selected_value_max, selected_value_min = self.on_select_helper(xmin, xmax)

        # plot the selected values and update plots limits and text
        self.axes.plot(selected_dates, selected_values, self.color_str)
        self.axes.set_xlim(selected_dates[0], selected_dates[-1])
        self.axes.set_ylim(selected_values.min(), selected_values.max())

        text = 'mean = %.2f\nmax = %.2f\nmin = %.2f' % (selected_values_mean, selected_value_max, selected_value_min)           
        self.axes_text.set_text(text)

        # draw the updated plot
        self.canvas.draw() 

    def toggle_selector(self, radio_button_label):
        """ 
        A toggle radio buttons for the matplotlib SpanSelector widget.
        """ 

        if radio_button_label == "Span On":
            self.span_selector.visible = True
            self.matplotlib_toolbar.hide()

        elif radio_button_label == "Span Off":
            self.span_selector.visible = False
            self.matplotlib_toolbar.show()         
            self.plot_watertxt_parameter(watertxt_data = self.watertxt_data, name = self.parameter["name"])

    def clear_watertxt_plot(self):
        """ Clear the plot axes """ 

        self.figure.clear()
        self.canvas.draw()

    def reset_watertxt_plot(self):
        """ Clear the plot axes """ 

        self.axes.clear()
        self.canvas.draw()
        self.axes.grid(True)

    #-------------------------------- WATER.txt Parameter Comparison Plot ------------------------------------        

    def setup_watertxtcmp_plot(self):
        """ Setup the watertxt plot """

        self.clear_watertxtcmp_plot()

        # set up axes and its properties
        self.axes1 = self.figure.add_subplot(211)
        self.axes2 = self.figure.add_subplot(212, sharex = self.axes1)

        self.axes1.grid(True)
        self.axes2.grid(True)

    def plot_watertxtcmp_parameter(self, watertxt_data1, watertxt_data2, filename1, filename2, name): 
        """ Plot a parameter from a WATER.txt file """

        self.reset_watertxtcmp_plot()

        dates = watertxt_data1["dates"]

        parameter1 = watertxt.get_parameter(watertxt_data = watertxt_data1, name = name)     
        parameter2 = watertxt.get_parameter(watertxt_data = watertxt_data2, name = name)  

        assert parameter1 is not None, "Parameter name {} is not in watertxt_data".format(name)
        assert parameter2 is not None, "Parameter name {} is not in watertxt_data".format(name)

        # calculate the difference
        diff = parameter2["data"] - parameter1["data"]

        # plot parameters on axes1  
        self.axes1.plot(dates, parameter1["data"], color = "b", label = filename1 + ": " + parameter1["name"], linewidth = 2)   
        self.axes1.hold(True)
        self.axes1.plot(dates, parameter2["data"], color = "r", label = filename2 + ": " + parameter2["name"], linewidth = 2)   

        # plot the difference on axes2
        self.axes2.plot(dates, diff, color = "k", linewidth = 2)

        # add title, labels, legend
        self.axes1.set_title(parameter1["name"])

        self.axes2.set_xlabel("Date")
        self.axes2.set_ylabel("Difference")
 
        handles1, labels1 = self.axes1.get_legend_handles_labels()
        legend1 = self.axes1.legend(handles1, labels1, fancybox = True)
        legend1.get_frame().set_alpha(0.5)
        legend1.draggable(state=True)

        # show text of mean, max, min values on graph; use matplotlib.patch.Patch properies and bbox
        text1 = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(parameter1["mean"], parameter1["max"], parameter1["min"])
        text2 = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(parameter2["mean"], parameter2["max"], parameter2["min"])

        text_diff = "mean = {:.2f}\nmax = {:.2f}\nmin = {:.2f}".format(nanmean(diff), np.max(diff), np.min(diff))

        patch_properties1 = {"boxstyle": "round", "facecolor": "b", "alpha": 0.5}
        patch_properties2 = {"boxstyle": "round", "facecolor": "r", "alpha": 0.5}
        patch_properties_diff = {"boxstyle": "round", "facecolor": "wheat", "alpha": 0.5}
                       
        self.axes1.text(0.02, 0.95, text1, transform = self.axes1.transAxes, fontsize = 12, verticalalignment = "top", horizontalalignment = "left", bbox = patch_properties1)
        self.axes1.text(0.02, 0.45, text2, transform = self.axes1.transAxes, fontsize = 12, verticalalignment = "top", horizontalalignment = "left", bbox = patch_properties2)
        self.axes2.text(0.02, 0.95, text_diff, transform = self.axes2.transAxes, fontsize = 12, verticalalignment = "top", horizontalalignment = "left", bbox = patch_properties_diff)

        # use a more precise date string for the x axis locations in the toolbar and rotate labels
        self.axes2.fmt_xdata = mdates.DateFormatter("%Y-%m-%d")

        # rotate and align the tick labels so they look better
        self.figure.autofmt_xdate()

        # draw the plot
        self.canvas.draw()

    def clear_watertxtcmp_plot(self):
        """ Clear the plot axes """ 

        self.figure.clear()
        self.canvas.draw()

    def reset_watertxtcmp_plot(self):
        """ Clear the plot axes """ 

        self.axes1.clear()
        self.axes2.clear()
        self.canvas.draw()
        self.axes1.grid(True)
        self.axes2.grid(True)


    #-------------------------------- Basemap Plot ------------------------------------

    def setup_basemap_plot(self):
        """ Setup the watertxt plot """

        self.clear_basemap_plot()

        # set up axes and its properties
        self.basemap_axes = self.figure.add_subplot(111) 
        self.basemap_axes.grid(True)

    def get_map_extents(self, shapefiles, shp_name = None):
        """   
        Get max and min extent coordinates from a list of shapefiles to use as the 
        extents on the map. Use the map extents to calculate the map center and the 
        standard parallels.

        Parameters
        ----------
        shapefiles : list 
            List of shapefile_data dictionaries 
        shp_name : string
            String name of shapefile to use for getting map extents 

        Returns
        -------
        extent_coords : dictionary 
            Dictionary containing "lon_min", "lon_max", "lat_max", "lat_min" keys with respective calculated values
        center_coords : dictionary 
            Dictionary containing "lon", "lat" keys with respective calculated values
        standard_parallels : dictionary 
            Dictionary containing "first", "second" keys with respective calculated values (first = min(lat), second = max(lat))   
        """
        extent_coords = {}    
        center_coords = {}
        standard_parallels = {}

        lons = []
        lats = []

        if shp_name:
            for shapefile_data in shapefiles:            
                if shp_name in shapefile_data["name"].split("_")[0]:
                    lons.append(shapefile_data["extents"][0:2])
                    lats.append(shapefile_data["extents"][2:])
            
        else:
            for shapefile_data in shapefiles:
                lons.append(shapefile_data["extents"][0:2])
                lats.append(shapefile_data["extents"][2:])

        extent_coords["lon_min"] = np.min(lons)
        extent_coords["lon_max"] = np.max(lons)
        extent_coords["lat_min"] = np.min(lats)
        extent_coords["lat_max"] = np.max(lats)

        center_coords["lon"] = np.mean([extent_coords["lon_min"], extent_coords["lon_max"]])
        center_coords["lat"] = np.mean([extent_coords["lat_min"], extent_coords["lat_max"]])

        standard_parallels["first"] = extent_coords["lat_min"]
        standard_parallels["second"] = extent_coords["lat_max"]
            
        return extent_coords, center_coords, standard_parallels


    def plot_shapefiles_map(self, shapefiles, display_fields = [], colors = [], title = None, shp_name = None, buff = 1.0):
        """   
        Generate a map showing all the shapefiles in the shapefile_list.  
        Shapefiles should be in a Geographic Coordinate System (longitude and 
        latitude coordinates) such as World WGS84; Matplotlib"s basemap library 
        does the proper transformation to a projected coordinate system.  The projected
        coordinate system used is Albers Equal Area.
        
        Parameters
        ----------
        shapefiles : list 
            List of dictionaries containing shapefile information
        title : string 
            String title for plot
        display_fields : list 
            List of strings that correspond to a shapefile field where the corresponding value(s) will be displayed.
        colors : list
            List of strings that correspond to colors to be displayed
        shp_name : string
            String name of shapefile to use for getting map extents 
        buff : float
            Float value in coordinate degrees to buffer the map with
        """  

        self.setup_basemap_plot()

        extent_coords, center_coords, standard_parallels = self.get_map_extents(shapefiles, shp_name = shp_name)     

        # create the basemap object with Albers Equal Area Conic Projection
        bmap = Basemap(projection = "aea", 
                       llcrnrlon = extent_coords["lon_min"] - buff, llcrnrlat = extent_coords["lat_min"] - buff, 
                       urcrnrlon = extent_coords["lon_max"] + buff, urcrnrlat = extent_coords["lat_max"] + buff, 
                       lat_1 = standard_parallels["first"], lat_2 = standard_parallels["second"],
                       lon_0 = center_coords["lon"], lat_0 = center_coords["lat"],
                       resolution = "h", area_thresh = 10000, ax = self.basemap_axes)    
        
        # have basemap object plot background stuff
        bmap.drawcoastlines()
        bmap.drawcountries()
        bmap.drawrivers(linewidth = 1, color = "blue")
        bmap.drawstates()
        bmap.drawmapboundary(fill_color = "aqua")
        bmap.fillcontinents(color = "coral", lake_color = "aqua")
        bmap.drawparallels(np.arange(-80., 81., 1.), labels = [1, 0, 0, 0], linewidth = 0.5)
        bmap.drawmeridians(np.arange(-180., 181., 1.), labels = [0, 0, 0, 1], linewidth = 0.5)
         
        # plot each shapefile on the basemap    
        legend_handles = []
        legend_labels = []
        colors_index = 0
        colors_list = ["b", "g", "y", "r", "c", "y", "m", "orange", "aqua", "darksalmon", "gold", "k"]
        for shapefile_data in shapefiles:
            
            # set up colors to use
            if colors:
                color = colors[colors_index]        
            elif colors_index > len(colors_list) - 1:
                color = np.random.rand(3,)
            else:
                color = colors_list[colors_index]         
            
            full_path = os.path.join(shapefile_data["path"], shapefile_data["name"].split(".")[0])
            
            shp_tuple = bmap.readshapefile(full_path, "shp", drawbounds = False)                            # use basemap shapefile reader for ease of plotting
            for shape_dict, shape in zip(bmap.shp_info, bmap.shp):                                          # zip the shapefile information and its shape as defined by basemap

                if shapefile_data["type"] == "POLYGON":
                    p1 = Polygon(shape, facecolor = color, edgecolor = "k",
                                             linewidth = 1, alpha = 0.7, label = shapefile_data["name"])            
                    self.basemap_axes.add_patch(p1)
                    xx, yy = zip(*shape)
                    txt_x = str(np.mean(xx))
                    txt_y = str(np.mean(yy))
                    
                elif shapefile_data["type"] == "POINT":
                    x, y = shape

                    if "usgsgages" in shapefile_data["name"].split("_")[0]:
                        p1 = bmap.plot(x, y, color = color, marker = "^", markersize = 10, label = shapefile_data["name"])
                    elif "wateruse" in shapefile_data["name"].split("_")[0]:
                        p1 = bmap.plot(x, y, color = color, marker = "o", markersize = 5, label = shapefile_data["name"])
                    else:
                        print("what!!")
                        p1 = bmap.plot(x, y, color = color, marker = "o", markersize = 10, label = shapefile_data["name"])

                    txt_x = str(x)
                    txt_y = str(y)
                    
                else:
                    xx, yy = zip(*shape)
                    p1 = bmap.plot(xx, yy, linewidth = 1, color = color, label = shapefile_data["name"])
                    txt_x = str(np.mean(xx))
                    txt_y = str(np.mean(yy))
                
                
                if isinstance(p1, list):
                    p1 = p1[0]

                # control text display of shapefile fields
                for display_field in display_fields:
                    if display_field in shape_dict.keys():
                        self.basemap_axes.text(txt_x, txt_y, shape_dict[display_field], color = "k", fontsize = 12, fontweight = "bold")

            colors_index += 1    
            legend_handles.append(p1)    
            legend_labels.append(shapefile_data["name"].split("_")[0])

        handles, labels = self.basemap_axes.get_legend_handles_labels()
        
        # edit the contents of handles and labels to only show 1 legend per shape
        handles = legend_handles
        labels = legend_labels
        legend = self.basemap_axes.legend(handles, labels, fancybox = True, numpoints = 1)
        legend.get_frame().set_alpha(0.5)
        legend.draggable(state = True)

        # draw the plot
        self.canvas.draw()


    def clear_basemap_plot(self):
        """ Clear the plot axes """ 

        self.figure.clear()
        self.canvas.draw()

    def reset_basemap_plot(self):
        """ Clear the plot axes """ 

        self.basemap_axes.clear()
        self.canvas.draw()
class Window(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.setWindowTitle('OpenWave-1KB')
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("openwave.ico"), QtGui.QIcon.Normal,
                       QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        #Waveform area.
        self.figure = plt.figure()
        self.figure.set_facecolor('white')

        self.canvas = FigureCanvas(self.figure)
        self.canvas.setMinimumSize(800, 400)

        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()

        #Zoom In/out and Capture Buttons
        self.zoomBtn = QtGui.QPushButton('Zoom')
        self.zoomBtn.setFixedSize(100, 30)
        self.zoomBtn.clicked.connect(self.toolbar.zoom)

        self.panBtn = QtGui.QPushButton('Pan')
        self.panBtn.setFixedSize(100, 30)
        self.panBtn.clicked.connect(self.toolbar.pan)

        self.homeBtn = QtGui.QPushButton('Home')
        self.homeBtn.setFixedSize(100, 30)
        self.homeBtn.clicked.connect(self.toolbar.home)

        self.captureBtn = QtGui.QPushButton('Capture')
        self.captureBtn.setFixedSize(100, 50)
        self.captureBtn.clicked.connect(self.captureAction)
        if (portNum == -1):
            self.captureBtn.setEnabled(False)

        #Type: Raw Data/Image
        self.typeBtn = QtGui.QPushButton('Raw Data')
        self.typeBtn.setToolTip("Switch to get raw data or image from DSO.")
        self.typeBtn.setFixedSize(120, 50)
        self.typeFlag = True  #Initial state -> Get raw data
        self.typeBtn.setCheckable(True)
        self.typeBtn.setChecked(True)
        self.typeBtn.clicked.connect(self.typeAction)

        #Channel Selection.
        self.ch1checkBox = QtGui.QCheckBox('CH1')
        self.ch1checkBox.setFixedSize(60, 30)
        self.ch2checkBox = QtGui.QCheckBox('CH2')
        self.ch2checkBox.setFixedSize(60, 30)
        if (dso.chnum == 4):
            self.ch3checkBox = QtGui.QCheckBox('CH3')
            self.ch3checkBox.setFixedSize(60, 30)
            self.ch4checkBox = QtGui.QCheckBox('CH4')
            self.ch4checkBox.setFixedSize(60, 30)

        #Set channel selection layout.
        self.selectLayout = QtGui.QHBoxLayout()
        self.selectLayout.addWidget(self.ch1checkBox)
        self.selectLayout.addWidget(self.ch2checkBox)
        if (dso.chnum == 4):
            self.selectLayout2 = QtGui.QHBoxLayout()
            self.selectLayout2.addWidget(self.ch3checkBox)
            self.selectLayout2.addWidget(self.ch4checkBox)

        self.typeLayout = QtGui.QHBoxLayout()
        self.typeLayout.addWidget(self.typeBtn)
        self.typeLayout.addLayout(self.selectLayout)
        if (dso.chnum == 4):
            self.typeLayout.addLayout(self.selectLayout2)

        #Save/Load/Quit button
        self.saveBtn = QtGui.QPushButton('Save')
        self.saveBtn.setFixedSize(100, 50)
        self.saveMenu = QtGui.QMenu(self)
        self.csvAction = self.saveMenu.addAction("&As CSV File")
        self.pictAction = self.saveMenu.addAction("&As PNG File")
        self.saveBtn.setMenu(self.saveMenu)
        self.saveBtn.setToolTip("Save waveform to CSV file or PNG file.")
        self.connect(self.csvAction, QtCore.SIGNAL("triggered()"),
                     self.saveCsvAction)
        self.connect(self.pictAction, QtCore.SIGNAL("triggered()"),
                     self.savePngAction)

        self.loadBtn = QtGui.QPushButton('Load')
        self.loadBtn.setToolTip("Load CHx's raw data from file(*.csv, *.lsf).")
        self.loadBtn.setFixedSize(100, 50)
        self.loadBtn.clicked.connect(self.loadAction)

        self.quitBtn = QtGui.QPushButton('Quit')
        self.quitBtn.setFixedSize(100, 50)
        self.quitBtn.clicked.connect(self.quitAction)

        # set the layout
        self.waveLayout = QtGui.QHBoxLayout()
        self.waveLayout.addWidget(self.canvas)

        self.wave_box = QtGui.QVBoxLayout()
        self.wave_box.addLayout(self.waveLayout)

        self.wavectrlLayout = QtGui.QHBoxLayout()
        self.wavectrlLayout.addWidget(self.zoomBtn)
        self.wavectrlLayout.addWidget(self.panBtn)
        self.wavectrlLayout.addWidget(self.homeBtn)
        self.wavectrlLayout.addWidget(self.captureBtn)

        self.saveloadLayout = QtGui.QHBoxLayout()
        self.saveloadLayout.addWidget(self.saveBtn)
        self.saveloadLayout.addWidget(self.loadBtn)
        self.saveloadLayout.addWidget(self.quitBtn)

        self.ctrl_box = QtGui.QHBoxLayout()
        self.ctrl_box.addLayout(self.typeLayout)
        self.ctrl_box.addLayout(self.saveloadLayout)

        main_box = QtGui.QVBoxLayout()
        main_box.addLayout(self.wave_box)  #Waveform area.
        main_box.addLayout(self.wavectrlLayout)  #Zoom In/Out...
        main_box.addLayout(self.ctrl_box)  #Save/Load/Quit
        self.setLayout(main_box)

    def typeAction(self):
        if (self.typeFlag == True):
            self.typeFlag = False
            self.typeBtn.setText("Image")
            self.csvAction.setEnabled(False)
        else:
            self.typeFlag = True
            self.typeBtn.setText("Raw Data")
            self.csvAction.setEnabled(True)
        self.typeBtn.setChecked(self.typeFlag)
        self.ch1checkBox.setEnabled(self.typeFlag)
        self.ch2checkBox.setEnabled(self.typeFlag)
        if (dso.chnum == 4):
            self.ch3checkBox.setEnabled(self.typeFlag)
            self.ch4checkBox.setEnabled(self.typeFlag)

    def saveCsvAction(self):
        if (self.typeFlag == True):  #Save raw data to csv file.
            file_name = QtGui.QFileDialog.getSaveFileName(
                self, "Save as", '', "Fast CSV File(*.csv)")[0]
            num = len(dso.ch_list)
            #print num
            for ch in xrange(num):
                if (dso.info[ch] == []):
                    print(
                        'Failed to save data, raw data information is required!'
                    )
                    return
            f = open(file_name, 'wb')
            item = len(dso.info[0])
            #Write file header.
            f.write('%s, \n' % dso.info[0][0])
            for x in xrange(1, 23):
                str = ''
                for ch in xrange(num):
                    str += ('%s,' % dso.info[ch][x])
                str += '\n'
                f.write(str)
            if (num == 1):
                str = 'Mode,Fast,\n'
                f.write(str)
                str = 'Waveform Data,\n'
                f.write(str)
            else:
                str = ''
                for ch in xrange(num):
                    str += ('Mode,Fast,')
                str += '\n'
                f.write(str)
                str = ''
                for ch in xrange(num):
                    str += ('Waveform Data,,')
                str += '\n'
                f.write(str)
            #Write raw data.
            item = len(dso.iWave[0])
            #print item
            tenth = int(item / 10)
            n_tenth = tenth - 1
            percent = 10
            for x in xrange(item):
                str = ''
                if (num == 1):
                    str += ('%s,' % dso.iWave[0][x])
                else:
                    for ch in xrange(num):
                        str += ('%s, ,' % dso.iWave[ch][x])
                str += '\n'
                f.write(str)
                if (x == n_tenth):
                    n_tenth += tenth
                    print('%3d %% Saved\r' % percent),
                    percent += 10
            f.close()

    def savePngAction(self):
        #Save figure to png file.
        file_name = QtGui.QFileDialog.getSaveFileName(self, "Save as", '',
                                                      "PNG File(*.png)")[0]
        if (file_name == ''):
            return
        if (self.typeFlag == True):  #Save raw data waveform as png file.
            main.figure.savefig(file_name)
            print('Saved image to %s.' % file_name)
        else:  #Save figure to png file.
            if (dso.nodename == 'pi'):  #For raspberry pi only.
                img = dso.im.transpose(Image.FLIP_TOP_BOTTOM)
                img.save(file_name)
            else:
                dso.im.save(file_name)
            print('Saved image to %s.' % file_name)

    def loadAction(self):
        dso.ch_list = []
        full_path_name = QtGui.QFileDialog.getOpenFileName(
            self, self.tr("Open File"), ".",
            "CSV/LSF files (*.csv *.lsf);;All files (*.*)")
        sFileName = unicode(full_path_name).split(',')[0][3:-1]  #For PySide
        print sFileName
        if (len(sFileName) <= 0):
            return
        if os.path.exists(sFileName):
            print 'Reading file...'
            count = dso.readRawDataFile(sFileName)
            #Draw waveform.
            if (count > 0):
                total_chnum = len(dso.ch_list)
                if (total_chnum == 0):
                    return
                if (dso.dataMode == 'Fast'):
                    self.drawWaveform(0)
                else:
                    self.drawWaveform(0)
        else:
            print('File not found!')

    def quitAction(self):
        if (portNum != -1):
            dso.IO.close()
        self.close()

    def captureAction(self):
        dso.iWave = [[], [], [], []]
        dso.ch_list = []
        if (self.typeFlag == True):  #Get raw data.
            draw_flag = False
            #Turn on the selected channels.
            if ((self.ch1checkBox.isChecked() == True)
                    and (dso.isChannelOn(1) == False)):
                dso.write(":CHAN1:DISP ON\n")  #Set CH1 on.
            if ((self.ch2checkBox.isChecked() == True)
                    and (dso.isChannelOn(2) == False)):
                dso.write(":CHAN2:DISP ON\n")  #Set CH2 on.
            if (dso.chnum == 4):
                if ((self.ch3checkBox.isChecked() == True)
                        and (dso.isChannelOn(3) == False)):
                    dso.write(":CHAN3:DISP ON\n")  #Set CH3 on.
                if ((self.ch4checkBox.isChecked() == True)
                        and (dso.isChannelOn(4) == False)):
                    dso.write(":CHAN4:DISP ON\n")  #Set CH4 on.
            #Get all the selected channel's raw datas.
            if (self.ch1checkBox.isChecked() == True):
                dso.getRawData(
                    True, 1)  #Read CH1's raw data from DSO (including header).
            if (self.ch2checkBox.isChecked() == True):
                dso.getRawData(
                    True, 2)  #Read CH2's raw data from DSO (including header).
            if (dso.chnum == 4):
                if (self.ch3checkBox.isChecked() == True):
                    dso.getRawData(
                        True,
                        3)  #Read CH3's raw data from DSO (including header).
                if (self.ch4checkBox.isChecked() == True):
                    dso.getRawData(
                        True,
                        4)  #Read CH4's raw data from DSO (including header).
            #Draw waveform.
            total_chnum = len(dso.ch_list)
            if (total_chnum == 0):
                return
            if (self.drawWaveform(1) == -1):
                time.sleep(5)
                self.drawWaveform(0)
        else:  #Get image.
            dso.write(':DISP:OUTP?\n')  #Send command to get image from DSO.
            dso.getBlockData()
            dso.RleDecode()
            self.showImage()
            plt.tight_layout(True)
            self.canvas.draw()
            print('Image is ready!')

    def showImage(self):
        #Turn the ticks off and show image.
        plt.clf()
        ax = plt.gca()
        ax.xaxis.set_visible(False)
        ax.yaxis.set_visible(False)
        plt.imshow(dso.im)

    def drawWaveform(self, mode):
        total_chnum = len(dso.ch_list)
        num = dso.points_num
        ch_colortable = ['#C0B020', '#0060FF', '#FF0080', '#00FF60']
        ch = int(dso.ch_list[0][2]) - 1  #Get the channel of first waveform.
        plt.cla()
        plt.clf()
        #Due to the memory limitation of matplotlib, we must reduce the sample points.
        if (num == 10000000):
            if (total_chnum > 2):
                down_sample_factor = 4
            elif (total_chnum == 2):
                down_sample_factor = 4
            else:
                down_sample_factor = 1
            num = num / down_sample_factor
        else:
            down_sample_factor = 1
        dt = dso.dt[0]  #Get dt from the first opened channel.
        t_start = dso.hpos[0] - num * dt / 2
        t_end = dso.hpos[0] + num * dt / 2
        t = np.arange(t_start, t_end, dt)
        #print t_start, t_end, dt, len(t)
        if ((len(t) - num) == 1):  #Avoid floating point rounding error.
            t = t[:-1]
        wave_type = '-'  #Set waveform type to vector.
        #Draw waveforms.
        ax = [[], [], [], []]
        p = []
        for ch in xrange(total_chnum):
            if (ch == 0):
                ax[ch] = host_subplot(111, axes_class=AA.Axes)
                ax[ch].set_xlabel("Time (sec)")
            else:
                ax[ch] = ax[0].twinx()
            ax[ch].set_ylabel("%s Units: %s" %
                              (dso.ch_list[ch], dso.vunit[ch]))
            ch_color = ch_colortable[int(dso.ch_list[ch][2]) - 1]
            if (ch > 1):
                new_fixed_axis = ax[ch].get_grid_helper().new_fixed_axis
                ax[ch].axis["right"] = new_fixed_axis(loc="right",
                                                      axes=ax[ch],
                                                      offset=(60 * (ch - 1),
                                                              0))
            ax[ch].set_xlim(t_start, t_end)
            ax[ch].set_ylim(-4 * dso.vdiv[ch] - dso.vpos[ch],
                            4 * dso.vdiv[ch] -
                            dso.vpos[ch])  #Setup vertical display range.
            fwave = dso.convertWaveform(ch, down_sample_factor)
            #print('Length=%d'%(len(fwave)))
            if (ch == 0):
                try:
                    p = ax[ch].plot(t,
                                    fwave,
                                    color=ch_color,
                                    ls=wave_type,
                                    label=dso.ch_list[ch])
                except:
                    if (mode == 1):
                        #print sys.exc_info()[0]
                        time.sleep(5)
                        print 'Trying to plot again!',
                    return -1
            else:
                try:
                    p += ax[ch].plot(t,
                                     fwave,
                                     color=ch_color,
                                     ls=wave_type,
                                     label=dso.ch_list[ch])
                except:
                    if (mode == 1):
                        #print sys.exc_info()[0]
                        time.sleep(5)
                        print 'Trying to plot again!',
                    return -1
        if (total_chnum > 1):
            labs = [l.get_label() for l in p]
            plt.legend(p, labs, loc='upper right')
        plt.tight_layout()
        self.canvas.draw()
        del ax, t, p
        return 0
Beispiel #14
0
    def rebuild_widget(self, number_of_plots, plot_stretching):

        self.__number_of_plots = number_of_plots
        self.__plot_stretching = plot_stretching

        ids = []
        if self.__top_vbox.count() < self.__number_of_plots:
            ids = range(self.__top_vbox.count(), self.__number_of_plots)

        for i in ids:

            label = QLabel()
            label.setFont(self.__font)
            label.setAlignment(Qt.AlignCenter)

            # Create the mpl Figure and FigCanvas objects. 
            # 5x4 inches, 100 dots-per-inch
            #
            #dpi = 100
            #self.fig = Figure((5.0, 4.0), dpi=self.dpi)
            fig = pyplot.figure()
            canvas = FigureCanvas(fig)
            canvas.setParent(self)
    
            # Since we have only one plot, we can use add_axes 
            # instead of add_subplot, but then the subplot
            # configuration tool in the navigation toolbar wouldn't
            # work.
            #
            axes = fig.add_subplot(111)
    
            # Create the navigation toolbar, tied to the canvas
            #
            mpl_toolbar = NavigationToolbar(canvas, self, False)
    
            if self.__show_toolbar:
                mpl_toolbar.show()
            else:
                mpl_toolbar.hide()

            # Other GUI controls
            #
    
            tmp_vbox = QVBoxLayout()
            tmp_vbox.addWidget(label)
            tmp_vbox.addWidget(canvas, 1)
            tmp_vbox.addWidget(mpl_toolbar)

            widget = QWidget()
            widget.setLayout(tmp_vbox)

            self.__plots.append((label, canvas, fig, axes, mpl_toolbar, widget))

            self.__plot_infos.append((self.PLOT_TYPE_NONE, '', None, {}))

            self.__top_vbox.addWidget(widget)

        for i in xrange(self.__number_of_plots):

            stretch = 0
            if plot_stretching != None:
                stretch = plot_stretching[i]
                self.__top_vbox.setStretch(i, stretch)
            else:
                self.__top_vbox.setStretch(i, 1)

            plot = self.__plots[i]
            label, canvas, fig, axes, mpl_toolbar, widget = plot
            widget.show()

        for i in xrange(self.__number_of_plots, self.__top_vbox.count()):

            plot = self.__plots[i]
            label, canvas, fig, axes, mpl_toolbar, widget = plot
            widget.hide()

        if self.__show_menu:
            menubar = QMenuBar()
            save_menu = menubar.addMenu("&Save")
    
            menu_items = []
            for i,plot_info in enumerate(self.__plot_infos):
                plot_caption = plot_info[1]
                menu_name = "Save &plot '%s' [%d]" % (plot_caption, i+1)
                if len(plot_caption) == 0:
                    menu_name = "Save &plot #%d" % (i+1)
                save_plot_action = self.__make_action(menu_name,
                                                     shortcut="Ctrl+P",
                                                     slot=functools.partial(self.on_save_plot, i))
                menu_items.append(save_plot_action)
            menu_items.append(None)
            save_all_action = self.__make_action("Save &all plots",
                shortcut="Ctrl+A", slot=self.on_save_all_plots)
            menu_items.append(save_all_action)
    
            self.__add_actions(save_menu, menu_items)
    
            self.layout().setMenuBar(menubar)
Beispiel #15
0
class MplCanvas(QWidget):
    """Ultimately, this is a QWidget (as well as a FigureCanvasAgg, etc.)."""
    def __init__(self, parent=None):
        super(MplCanvas, self).__init__(parent)
        #屏幕图片分辨率
        self.fig = Figure(figsize=(8, 6), dpi=100)
        self.canvas = FigureCanvas(self.fig)
        self.canvas.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.canvas.setParent(parent)
        #调整画布区
        self.fig.subplots_adjust(left=0.02, bottom=0.08, top=0.95, right=0.95)
        self.fig.clear()
        self.layout = QVBoxLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.addWidget(self.canvas)
        self.mpl_toolbar = NavigationToolbar(self.canvas,
                                             parent,
                                             coordinates=False)
        #self._init_MplToolBar()
        self.mpl_toolbar.hide()
        #self.layout.addWidget(self.mpl_toolbar)
        self.setLayout(self.layout)
        self._init_Axes()

    def _init_MplToolBar(self):
        """设置toolbar上的功能键"""
        a = self.mpl_toolbar.actions()
        for i in a:
            if i.iconText() == 'Home':
                i.setToolTip(u'初始视图')
            elif i.iconText() == 'Back':
                i.setToolTip(u'后退')
            elif i.iconText() == 'Forward':
                i.setToolTip(u'前进')
            elif i.iconText() == 'Pan':
                i.setToolTip(u'鼠标左键平移,右键横竖缩放')
            elif i.iconText() == 'Zoom':
                i.setToolTip(u'局部缩放')
            elif i.iconText() == 'Subplots':
                self.mpl_toolbar.removeAction(i)
            elif i.iconText() == 'Customize':
                self.mpl_toolbar.removeAction(i)
            elif i.iconText() == 'Save':
                i.setToolTip(u'保存图片')

    def _init_Axes(self):
        self.axes = self.fig.add_subplot(111)
        self.axes.yaxis.set_visible(False)
        #self.axes.tick_params(axis='y', left='off', labelleft='off', width=0)
        self.axes.tick_params(axis='x', top='off')
        self.axes.spines['right'].set_visible(False)
        self.axes.spines['top'].set_visible(False)
        self.axes.spines['left'].set_visible(False)
        self.axes.spines['bottom'].set_visible(True)
        self.axes.set_xlabel(u'时间(s)')
        self.cur_axes = self.axes
        self.x_left, self.x_right = self.cur_axes.get_xlim()
        self.y_bottom, self.y_top = self.cur_axes.get_ylim()

    def _init_View(self):
        """
        记录当前canvas
        """
        self.mpl_toolbar.update()
        self.mpl_toolbar.push_current()
        self.x_left, self.x_right = self.cur_axes.get_xlim()
        self.y_bottom, self.y_top = self.cur_axes.get_ylim()
Beispiel #16
0
class MplWidget(QtGui.QWidget):
    """Widget defined in Qt Designer"""
    def __init__(self, tools, toolbar=True, menu=True, parent=None):
        # initialization of Qt MainWindow widget
        QtGui.QWidget.__init__(self, parent)
        # set the canvas to the Matplotlib widget
        self.canvas = MplCanvas()
        # create a vertical box layout
        self.layout = QtGui.QVBoxLayout()
        # add mpl widget to layout
        self.layout.addWidget(self.canvas)
        # reference to toolsFrame
        self.tool = tools

        if toolbar:
            # add navigation toolbar to layout
            self.toolbar = NavigationToolbar(self.canvas, self)
            self.layout.addWidget(self.toolbar)
            # enable hover event handling
            self.setAttribute(Qt.WA_Hover)
            # create and install event filter
            self.filter = Filter(self)
            self.installEventFilter(self.filter)
            # hide toolbar
            self.initComponents()
        else:
            self.toolbar = None

        # set the layout to th vertical box
        self.setLayout(self.layout)
        # active lines list
        self.lines = []
        # legend
        self.legend = None
        # autoscale
        self.canvas.ax.autoscale_view(True, True, True)

        if menu:
            # setup context menu
            self.setContextMenuPolicy(Qt.ActionsContextMenu)
            self.initActions()
            self.alwaysAutoScale.setChecked(True)

    #-------------- initialization ---------------#
    def initComponents(self):
        if self.toolbar is not None:
            self.toolbar.hide()
            self.newIcons()

    def initActions(self):
        # toolbar
        self.toggleLegendAction = QtGui.QAction(QtGui.QIcon(RES + ICONS + LEGEND), 'Toggle legend',
                                     self, triggered=self.toggleLegend)
        self.toggleLegendAction.setCheckable(True)
        if self.toolbar is not None:
            self.toolbar.addAction(self.toggleLegendAction)

        # context menu
        self.addAction(self.toggleLegendAction)
        self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + COPY),'Copy data to table',
                                     self, triggered=self.toTable))
        self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + GRAPH),'Plot data in tools',
                                     self, triggered=self.toGraphTool))
        self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + SCALE), 'Autoscale',
                                     self, triggered=self.updateScale))

        self.alwaysAutoScale = QtGui.QAction('Scale on update', self)
        self.alwaysAutoScale.setCheckable(True)

        self.selectLinesMenu = QtGui.QMenu()
        self.selectLines = (QtGui.QAction('Plots', self))
        self.selectLines.setMenu(self.selectLinesMenu)

        aSep = QtGui.QAction('', self)
        aSep.setSeparator(True)
        self.addAction(aSep)
        self.addAction(self.selectLines)
        self.addAction(self.alwaysAutoScale)

    def newIcons(self):
        for position in range(0, self.toolbar.layout().count()):
            widget = self.toolbar.layout().itemAt(position).widget()
            if isinstance(widget, QtGui.QToolButton):
                icon = QtGui.QIcon(RES + ICONS + TOOLBAR_ICONS[position])
                self.toolbar.layout().itemAt(position).widget().setIcon(icon)

        self.toolbar.setIconSize(QSize(ICO_GRAPH, ICO_GRAPH))

    def resetGraphicEffect(self):
        if self.graphicsEffect() is not None:
            self.graphicsEffect().setEnabled(False)

    #------------- plotting methods ---------------#

    ## Hides axes in widget.
    #  @param axes Widget axes form canvas.
    @staticmethod
    def hideAxes(axes):
        axes.get_xaxis().set_visible(False)
        axes.get_yaxis().set_visible(False)

    ## Clears widget canvas, removing all data and clearing figure.
    #  @param repaint_axes Add standard plot after clearing figure.
    def clearCanvas(self, repaint_axes=True):
        self.canvas.ax.clear()
        self.canvas.fig.clear()
        if repaint_axes:
            self.canvas.ax = self.canvas.fig.add_subplot(111)

    ## Update existing data or plot anew.
    #  @param data List or array to plot/update.
    #  @param line Which line (by index) to update (if any).
    #  @param label Data label (new or existing).
    #  @param style Line style (solid, dashed, dotted).
    #  @param color Line color.
    def updatePlot(self, data, line=0, label=None, style='solid', color=None):
        if not self.canvas.ax.has_data():
            if label is not None:
                if color is not None:
                    self.lines = self.canvas.ax.plot(data, label=label, linestyle=style, color=color)
                else:
                    self.lines = self.canvas.ax.plot(data, label=label, linestyle=style)
            else:
                if color is not None:
                    self.lines = self.canvas.ax.plot(data, linestyle=style, color=color)
                else:
                    self.lines = self.canvas.ax.plot(data, linestyle=style)
        else:
            if not self.lines:
                self.lines = self.canvas.ax.get_lines()
            if label is not None:
                if label not in [l._label for l in self.lines]:
                    if color is not None:
                        self.lines.extend(self.canvas.ax.plot(data, label=label, linestyle=style, color=color))
                    else:
                        self.lines.extend(self.canvas.ax.plot(data, label=label, linestyle=style))
                    line = len(self.lines) - 1
                else:
                    line = [l._label for l in self.lines].index(label)
            line_to_update = self.lines[line]
            if len(data) != len(line_to_update._x):
                # x, y ~ data in y
                line_to_update.set_data(np.arange(len(data)), data)
            else:
                # in case data length stays the same
                line_to_update.set_data(line_to_update._x, data)
            self.canvas.draw()

        self.updateLegend()
        self.updateLinesSubmenu()

        if self.alwaysAutoScale.isChecked():
            self.updateScale()

    ## Plots scalogram for wavelet decomposition.
    #  @param data Wavelet coefficients in matrix.
    #  @param top Axis position.
    #  @param colorbar Shows colorbar for data levels.
    #  @param power Scales resulting graph by power of 2.
    def scalogram(self, data, top=True, colorbar=True, power=False):
#        self.resetGraphicEffect()
        self.clearCanvas()

        x = np.arange(len(data[0]))
        y = np.arange(len(data))

        if power:
            contour = self.canvas.ax.contourf(x, y, np.abs(data) ** 2)
        else:
            contour = self.canvas.ax.contourf(x, y, np.abs(data))

        if colorbar:
            self.canvas.fig.colorbar(contour, ax=self.canvas.ax, orientation='vertical', format='%2.1f')

        if top:
            self.canvas.ax.set_ylim((y[-1], y[0]))
        else:
            self.canvas.ax.set_ylim((y[0], y[-1]))

        self.canvas.ax.set_xlim((x[0], x[-1]))
#        self.canvas.ax.set_ylabel('scales')

        self.canvas.draw()

    ## Plots list of arrays with shared x/y axes.
    #  @param data Arrays to plot (list or matrix).
    def multiline(self, data):
#        self.resetGraphicEffect()
        # abscissa
        axprops = dict(yticks=[])
        # ordinate
        yprops = dict(rotation=0,
              horizontalalignment='right',
              verticalalignment='center',
              x=-0.01)

        # level/figure ratio
        ratio = 1. / len(data)

        # positioning (fractions of total figure)
        left = 0.1
        bottom = 1.0
        width = 0.85
        space = 0.035
        height = ratio - space

        # legend
        label = 'Lvl %d'
        i = 0

        bottom -= height
        ax = self.canvas.fig.add_axes([left, bottom - space, width, height], **axprops)

        ax.plot(data[i])
        setp(ax.get_xticklabels(), visible=False)
        ax.set_ylabel(label % i, **yprops)
        i += 1

        axprops['sharex'] = ax
        axprops['sharey'] = ax

        while i < len(data):
            bottom -= height
            ax = self.canvas.fig.add_axes([left, bottom, width, height], **axprops)
            ax.plot(data[i], label='Lvl' + str(i))
            ax.set_ylabel(label % i, **yprops)
            i += 1
            if i != len(data):
                setp(ax.get_xticklabels(), visible=False)

    #----------------- actions -----------------#
    def getTopParent(self):
        widget = self.parentWidget()
        while True:
            if widget.parentWidget() is None:
                return widget
            else:
                widget = widget.parentWidget()

    def toggleLegend(self):
        self.updateLegend()

    def updateLegend(self):
        #NB: sometimes induces random exceptions from legend.py -> offsetbox.py
        try:
            prop = font_manager.FontProperties(size=11)
            self.legend = DraggableLegend(self.canvas.ax.legend(fancybox=True, shadow=True, prop=prop))
            if self.toggleLegendAction.isChecked():
                    self.legend.legend.set_visible(True)
            else:
                    self.legend.legend.set_visible(False)
            self.canvas.draw()
        except Exception, e:
            pass
Beispiel #17
0
class MplWidget(QtGui.QWidget):
    """Widget defined in Qt Designer"""
    def __init__(self, toolbar=True, menu=True, parent=None):
        # initialization of Qt MainWindow widget
        QtGui.QWidget.__init__(self, parent)
        # set the canvas to the Matplotlib widget
        self.canvas = MplCanvas()
        # create a vertical box layout
        self.layout = QtGui.QVBoxLayout()
        # add mpl widget to layout
        self.layout.addWidget(self.canvas)

        if toolbar:
            # add navigation toolbar to layout
            self.toolbar = NavigationToolbar(self.canvas, self)
            self.layout.addWidget(self.toolbar)
            # enable hover event handling
            self.setAttribute(Qt.WA_Hover)
            # create and install event filter
            self.filter = Filter(self)
            self.installEventFilter(self.filter)
            # hide toolbar
            self.initComponents()
        else:
            self.toolbar = None

        # set the layout to th vertical box
        self.setLayout(self.layout)
        # active lines list
        self.lines = []
        # legend
        self.legend = None
        # autoscale
        self.canvas.ax.autoscale_view(True, True, True)

        if menu:
            # setup context menu
            self.setContextMenuPolicy(Qt.ActionsContextMenu)
            self.initActions()
            self.alwaysAutoScale.setChecked(True)

    #-------------- initialization ---------------#
    def initComponents(self):
        if self.toolbar is not None:
            self.toolbar.hide()
            self.newIcons()

    def initActions(self):
        # toolbar
        #self.toggleLegendAction = QtGui.QAction(QtGui.QIcon(RES + ICONS + LEGEND), 'Toggle legend',
                                     #self, triggered=self.toggleLegend)
        #self.toggleLegendAction.setCheckable(True)
        #if self.toolbar is not None:
            #self.toolbar.addAction(self.toggleLegendAction)

        # context menu
        #self.addAction(self.toggleLegendAction)
        #self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + COPY),'Copy data to table',
                                     #self, triggered=self.toTable))
        #self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + GRAPH),'Plot data in tools',
                                     #self, triggered=self.toGraphTool))
        #self.addAction(QtGui.QAction(QtGui.QIcon(RES + ICONS + SCALE), 'Autoscale',
                                     #self, triggered=self.updateScale))

        self.alwaysAutoScale = QtGui.QAction('Scale on update', self)
        self.alwaysAutoScale.setCheckable(True)

        self.selectLinesMenu = QtGui.QMenu()
        self.selectLines = (QtGui.QAction('Plots', self))
        self.selectLines.setMenu(self.selectLinesMenu)

        aSep = QtGui.QAction('', self)
        aSep.setSeparator(True)
        self.addAction(aSep)
        self.addAction(self.selectLines)
        self.addAction(self.alwaysAutoScale)

    def newIcons(self):
        for position in range(0, self.toolbar.layout().count()):
            widget = self.toolbar.layout().itemAt(position).widget()
            #if isinstance(widget, QtGui.QToolButton):
                #icon = QtGui.QIcon(RES + ICONS + TOOLBAR_ICONS[position])
                #self.toolbar.layout().itemAt(position).widget().setIcon(icon)

        #self.toolbar.setIconSize(QSize(ICO_GRAPH, ICO_GRAPH))

    def resetGraphicEffect(self):
        if self.graphicsEffect() is not None:
            self.graphicsEffect().setEnabled(False)

    #------------- plotting methods ---------------#

    def histogram(self, data):
        pass

    def kanjiStats(self, data):
        self.clearCanvas()
        picked, freqs = data
        # will lag
        #self.statPlot.canvas.ax.bar(freqs, picked)
        self.canvas.ax.plot(freqs, picked)

        self.canvas.ax.set_xlabel('Frequency')
        self.canvas.ax.set_ylabel('Number of times picked')
        self.canvas.ax.set_title('Distribution of (pseudo)randomly selected kanji')
        #self.statPlot.canvas.ax.text(max(freqs)/2, max(picked),
                                    #"""This distribution illustrates how much times (max %d)
                                    #kanji with different frequencies (max %d) has been picked"""
                                    #% (max(picked), max(freqs)), bbox=dict(facecolor='blue', alpha=0.1))
        self.canvas.ax.grid(True)
        self.canvas.ax.fill_between(freqs, picked, 1,
                                            facecolor='blue', alpha=0.5)
        self.canvas.draw()

    ## Hides axes in widget.
    #  @param axes Widget axes form canvas.
    @staticmethod
    def hideAxes(axes):
        axes.get_xaxis().set_visible(False)
        axes.get_yaxis().set_visible(False)

    ## Clears widget canvas, removing all data and clearing figure.
    #  @param repaint_axes Add standard plot after clearing figure.
    def clearCanvas(self, repaint_axes=True):
        self.canvas.ax.clear()
        self.canvas.fig.clear()
        if repaint_axes:
            self.canvas.ax = self.canvas.fig.add_subplot(111)

    ## Update existing data or plot anew.
    #  @param data List or array to plot/update.
    #  @param line Which line (by index) to update (if any).
    #  @param label Data label (new or existing).
    #  @param style Line style (solid, dashed, dotted).
    #  @param color Line color.
    def updatePlot(self, data, line=0, label=None, style='solid', color=None):
        if not self.canvas.ax.has_data():
            if label is not None:
                if color is not None:
                    self.lines = self.canvas.ax.plot(data, label=label, linestyle=style, color=color)
                else:
                    self.lines = self.canvas.ax.plot(data, label=label, linestyle=style)
            else:
                if color is not None:
                    self.lines = self.canvas.ax.plot(data, linestyle=style, color=color)
                else:
                    self.lines = self.canvas.ax.plot(data, linestyle=style)
        else:
            if not self.lines:
                self.lines = self.canvas.ax.get_lines()
            if label is not None:
                if label not in [l._label for l in self.lines]:
                    if color is not None:
                        self.lines.extend(self.canvas.ax.plot(data, label=label, linestyle=style, color=color))
                    else:
                        self.lines.extend(self.canvas.ax.plot(data, label=label, linestyle=style))
                    line = len(self.lines) - 1
                else:
                    line = [l._label for l in self.lines].index(label)
            line_to_update = self.lines[line]
            if len(data) != len(line_to_update._x):
                # x, y ~ data in y
                line_to_update.set_data(np.arange(len(data)), data)
            else:
                # in case data length stays the same
                line_to_update.set_data(line_to_update._x, data)
            self.canvas.draw()

        self.updateLegend()
        self.updateLinesSubmenu()

        if self.alwaysAutoScale.isChecked():
            self.updateScale()

    ## Plots scalogram for wavelet decomposition.
    #  @param data Wavelet coefficients in matrix.
    #  @param top Axis position.
    #  @param colorbar Shows colorbar for data levels.
    #  @param power Scales resulting graph by power of 2.
    def scalogram(self, data, top=True, colorbar=True, power=False):
#        self.resetGraphicEffect()
        self.clearCanvas()

        x = np.arange(len(data[0]))
        y = np.arange(len(data))

        if power:
            contour = self.canvas.ax.contourf(x, y, np.abs(data) ** 2)
        else:
            contour = self.canvas.ax.contourf(x, y, np.abs(data))

        if colorbar:
            self.canvas.fig.colorbar(contour, ax=self.canvas.ax, orientation='vertical', format='%2.1f')

        if top:
            self.canvas.ax.set_ylim((y[-1], y[0]))
        else:
            self.canvas.ax.set_ylim((y[0], y[-1]))

        self.canvas.ax.set_xlim((x[0], x[-1]))
#        self.canvas.ax.set_ylabel('scales')

        self.canvas.draw()

    ## Plots list of arrays with shared x/y axes.
    #  @param data Arrays to plot (list or matrix).
    def multiline(self, data):
#        self.resetGraphicEffect()
        # abscissa
        axprops = dict(yticks=[])
        # ordinate
        yprops = dict(rotation=0,
              horizontalalignment='right',
              verticalalignment='center',
              x=-0.01)

        # level/figure ratio
        ratio = 1. / len(data)

        # positioning (fractions of total figure)
        left = 0.1
        bottom = 1.0
        width = 0.85
        space = 0.035
        height = ratio - space

        # legend
        label = 'Lvl %d'
        i = 0

        bottom -= height
        ax = self.canvas.fig.add_axes([left, bottom - space, width, height], **axprops)

        ax.plot(data[i])
        setp(ax.get_xticklabels(), visible=False)
        ax.set_ylabel(label % i, **yprops)
        i += 1

        axprops['sharex'] = ax
        axprops['sharey'] = ax

        while i < len(data):
            bottom -= height
            ax = self.canvas.fig.add_axes([left, bottom, width, height], **axprops)
            ax.plot(data[i], label='Lvl' + str(i))
            ax.set_ylabel(label % i, **yprops)
            i += 1
            if i != len(data):
                setp(ax.get_xticklabels(), visible=False)

    #----------------- actions -----------------#
    def getTopParent(self):
        widget = self.parentWidget()
        while True:
            if widget.parentWidget() is None:
                return widget
            else:
                widget = widget.parentWidget()

    def toggleLegend(self):
        self.updateLegend()

    def updateLegend(self):
        #NB: sometimes induces random exceptions from legend.py -> offsetbox.py
        try:
            prop = font_manager.FontProperties(size=11)
            self.legend = DraggableLegend(self.canvas.ax.legend(fancybox=True, shadow=True, prop=prop))
            if self.toggleLegendAction.isChecked():
                    self.legend.legend.set_visible(True)
            else:
                    self.legend.legend.set_visible(False)
            self.canvas.draw()
        except Exception, e:
            pass
class Window(QtGui.QDialog):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
 
        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        self.canvas.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
         
        self.toolbar = NavigationToolbar(self.canvas, self)
        self.toolbar.hide()
 
        # Just some button 
        self.colorcardRadioButton = QtGui.QRadioButton('Select color car&d')
        self.colorcardRadioButton.setChecked(False)
        self.colorcardRadioButton.clicked.connect(self.selectWhat)
        
        self.trayRadioButton = QtGui.QRadioButton('Select &tray')
        self.trayRadioButton.setChecked(False)
        self.trayRadioButton.clicked.connect(self.selectWhat)

        self.potRadioButton = QtGui.QRadioButton('Select &pot')
        self.potRadioButton.setChecked(False)
        self.potRadioButton.clicked.connect(self.selectWhat)

        self.loadImageButton = QtGui.QPushButton('&Load image')
        self.loadImageButton.clicked.connect(self.loadImage)
 
        self.rotateImageButton = QtGui.QPushButton('&Rotate 90-deg')
        self.rotateImageButton.clicked.connect(self.rotateImage90Degrees)
 
        self.loadCamCalibButton = QtGui.QPushButton('Load &cam. param.')
        self.loadCamCalibButton.clicked.connect(self.loadCamCalib)
 
        self.saveGeometriesButton = QtGui.QPushButton('&Save selected geometries')
        self.saveGeometriesButton.clicked.connect(self.saveSelectedGeometries)
 
        self.saveTraysButton = QtGui.QPushButton('&Save selected tray images')
        self.saveTraysButton.clicked.connect(self.saveSelectedTrayImages)
 
        self.saveColorcadButton = QtGui.QPushButton('&Save sel. col. card images')
        self.saveColorcadButton.clicked.connect(self.saveSelectedColorcardImages)
 
        self.save2PipelineButton = QtGui.QPushButton('&Save as pipeline settings')
        self.save2PipelineButton.clicked.connect(self.savePipelineSettings)
 
        self.zoomButton = QtGui.QPushButton('&Zoom')
        self.zoomButton.setCheckable(True)
        self.zoomButton.clicked.connect(self.zoom)
         
        self.panButton = QtGui.QPushButton('&Pan')
        self.panButton.setCheckable(True)
        self.panButton.clicked.connect(self.pan)
         
        self.homeButton = QtGui.QPushButton('&Home')
        self.homeButton.clicked.connect(self.home)
        
        self.status = QtGui.QTextEdit('')
        self.status.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Expanding)
        self.mousePosition = QtGui.QLabel('')
 
        # set the layout
        layout = QtGui.QHBoxLayout()
        rightWidget = QtGui.QWidget()
        buttonlayout = QtGui.QVBoxLayout(rightWidget)
        buttonlayout.addWidget(self.loadImageButton)
        buttonlayout.addWidget(self.rotateImageButton)
        buttonlayout.addWidget(self.loadCamCalibButton)
        buttonlayout.addWidget(self.colorcardRadioButton)
        buttonlayout.addWidget(self.trayRadioButton)
        buttonlayout.addWidget(self.potRadioButton)
        buttonlayout.addWidget(self.zoomButton)
        buttonlayout.addWidget(self.panButton)
        buttonlayout.addWidget(self.homeButton)
        buttonlayout.addWidget(self.saveGeometriesButton)
        buttonlayout.addWidget(self.saveColorcadButton)
        buttonlayout.addWidget(self.saveTraysButton)
        buttonlayout.addWidget(self.save2PipelineButton)
        buttonlayout.addWidget(self.status)
        buttonlayout.addWidget(self.mousePosition)
        rightWidget.setMaximumWidth(200)
        leftLayout = QtGui.QVBoxLayout()
        leftLayout.addWidget(self.toolbar)
        leftLayout.addWidget(self.canvas)

        layout.addWidget(rightWidget)
        layout.addLayout(leftLayout)
        self.setLayout(layout)
 
        self.group = QtGui.QButtonGroup()
        self.group.addButton(self.colorcardRadioButton)
        self.group.addButton(self.trayRadioButton) 
        self.group.addButton(self.potRadioButton) 
        
        self.panMode = False
        self.zoomMode = False
        
        self.ax = None
        self.plotRect = None
        self.plotImg = None
        self.image = None
        self.potTemplate = None
        self.UndistMapX = None
        self.UndistMapY = None
        self.trayAspectRatio = 0.835
        self.colorcardAspectRatio = 1.5
        self.potAspectRatio = 1.0
        self.leftClicks = []
        
        self.ImageSize = None
        self.CameraMatrix = None
        self.DistCoefs = None
        
#        # change cursor shape
#        self.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor ))
        
        # Ouput parameters
        self.colorcardList = []
        self.trayList = []
        self.potList = []
        self.rotationAngle = 0
        self.isDistortionCorrected = False

    def selectWhat(self):
        if self.trayRadioButton.isChecked():
            self.status.append('Start selecting tray.')
        elif self.colorcardRadioButton.isChecked():
            self.status.append('Start selecting color bar.')
        else:
            self.status.append('Start selecting pot.')
        
    def home(self):
        self.toolbar.home()
    def zoom(self):
        self.toolbar.zoom()
        if not self.zoomMode:
            self.zoomMode = True
            self.panMode = False
            self.panButton.setChecked(False)
        else:
            self.zoomMode = False
    def pan(self):
        self.toolbar.pan()
        if not self.panMode:
            self.panMode = True
            self.zoomMode = False
            self.zoomButton.setChecked(False)
        else:
            self.panMode = False
         
    def loadImage(self):
        ''' load and show an image'''
        fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/mnt/phenocam/a_data/TimeStreams/BorevitzTest/BVZ0036/BVZ0036-GC02L~fullres-orig/2014/2014_06/2014_06_20/2014_06_20_12')
        self.status.append('Loading image...')
        app.processEvents()
        self.image = cv2.imread(str(fname))[:,:,::-1]
        self.status.append('Loaded image from ' + str(fname))
        
        # reset all outputs
        self.colorcardList = []
        self.trayList = []
        self.potList = []
        self.rotationAngle = 0
        self.isDistortionCorrected = False

        # Undistort image if mapping available
        if not self.isDistortionCorrected and self.UndistMapX != None and self.UndistMapY != None:
            self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC)
            self.isDistortionCorrected = True

        if self.image != None:
            if self.ax == None:
                self.ax = self.figure.add_subplot(111)
                self.ax.figure.canvas.mpl_connect('button_press_event', self.onMouseClicked)
                self.ax.figure.canvas.mpl_connect('motion_notify_event', self.onMouseMoves)
                self.ax.figure.canvas.mpl_connect('figure_enter_event', self.changeCursor)
            self.ax.hold(False)
            if self.plotImg == None:
                self.plotImg = self.ax.imshow(self.image)
            else:
                self.plotImg.set_data(self.image)
            self.figure.tight_layout()
            self.canvas.draw()
        
    def changeCursor(self, event):
#        cursor = Cursor(self.ax, useblit=True, color='red', linewidth=1)
        self.canvas.setCursor(QtGui.QCursor(QtCore.Qt.CrossCursor ))
        
    def updateFigure(self):
        xs, ys = [], []
        for Rect in self.colorcardList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for Rect in self.trayList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for Rect in self.potList:
            tl, bl, br, tr = Rect
            xs = xs + [tl[0], bl[0], br[0], tr[0], tl[0], np.nan]
            ys = ys + [tl[1], bl[1], br[1], tr[1], tl[1], np.nan]
        for x,y in self.leftClicks:
            xs = xs + [x]
            ys = ys + [y]
#        if self.crosshair != None:
#            xs = xs + [np.nan, 0, self.image.shape[1], np.nan, self.crosshair[0], self.crosshair[0], np.nan]
#            ys = ys + [np.nan, self.crosshair[1], self.crosshair[1], np.nan, 0, self.image.shape[0], np.nan]
        if len(xs) > 0 and len(ys) > 0:
            if self.plotRect == None:
                self.ax.hold(True)
                self.plotRect, = self.ax.plot(xs, ys, 'b')
                self.ax.hold(False)
                self.ax.set_xlim([0,self.image.shape[1]])
                self.ax.set_ylim([0,self.image.shape[0]])
                self.ax.invert_yaxis()
            else:
                self.plotRect.set_data(xs, ys)
        self.canvas.draw()
           
        app.processEvents()

         
    def loadCamCalib(self):
        ''' load camera calibration image and show an image'''
        CalibFile = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
        self.ImageSize, SquareSize, self.CameraMatrix, self.DistCoefs, RVecs, TVecs = utils.readCalibration(CalibFile)
        self.status.append('Loaded camera parameters from ' + CalibFile) 
        print('CameraMatrix =', self.CameraMatrix)
        print('DistCoefs =', self.DistCoefs)
        self.UndistMapX, self.UndistMapY = cv2.initUndistortRectifyMap(self.CameraMatrix, self.DistCoefs, \
            None, self.CameraMatrix, self.ImageSize, cv2.CV_32FC1)    

        if self.image != None:
            self.image = cv2.remap(self.image.astype(np.uint8), self.UndistMapX, self.UndistMapY, cv2.INTER_CUBIC)
            self.isDistortionCorrected = True
            self.status.append('Corrected image distortion.') 
            if self.plotImg == None:
                self.plotImg = self.ax.imshow(self.image)
            else:
                self.plotImg.set_data(self.image)
            self.canvas.draw()
        
#    def loadPotTemplate(self):
#        ''' load pot template image'''
#        fname = QtGui.QFileDialog.getOpenFileName(self, 'Open image', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
#        self.status.append('Loading image...')
#        app.processEvents()
#        self.potTemplate = cv2.imread(str(fname))[:,:,::-1]
#        if len(self.potList) > 0:
            

    def saveSelectedGeometries(self):
        ''' save selected geometries'''
        fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected geometries', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
        colorcardList2 = []
        for colorcard in self.colorcardList:
            colorcardList2 = colorcardList2 + colorcard
        trayList2 = []
        for tray in self.trayList:
            trayList2 = trayList2 + tray
        potList2 = []
        for pot in self.potList:
            potList2 = potList2 + pot
        dicdata = {'colorcardself.crosshair = NoneList':np.asarray(colorcardList2), \
                   'trayList':np.asarray(trayList2), \
                   'potList':np.asarray(potList2), \
                   'rotationAngle':self.rotationAngle, \
                   'distortionCorrected':int(self.isDistortionCorrected)}
        cv2yml.dic2yml(fname, dicdata)
        self.status.append('Saved selected geometries to ' + fname)
        
    def saveSelectedTrayImages(self):
        ''' save selected trays'''
        fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected tray images', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
        medianWidth, medianHeight = utils.getMedianRectSize(self.trayList)
        rectifiedTrayImages = utils.rectifyRectImages(self.image, self.trayList, MedianSize = [medianWidth, medianHeight])
        for i,rectifiedImage in enumerate(rectifiedTrayImages):
            cv2.imwrite(str(fname) %i, rectifiedImage)

    def saveSelectedColorcardImages(self):
        ''' save selected trays'''
        fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selected color card images', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
        medianWidth, medianHeight = utils.getMedianRectSize(self.colorcardList)
        rectifiedColorcardImages = utils.rectifyRectImages(self.image, self.colorcardList, MedianSize = [medianWidth, medianHeight])
        for i,rectifiedImage in enumerate(rectifiedColorcardImages):
            cv2.imwrite(str(fname) %i, rectifiedImage)
            
    def savePipelineSettings(self):
        ''' save to pipeline setting file'''
        fname = QtGui.QFileDialog.getSaveFileName(self, 'Save selection to pipeline settings', '/home/chuong/Workspace/traitcapture-bin/unwarp_rectify/data')
        settingPath = os.path.dirname(str(fname))
        settings = []
        if self.ImageSize != None and self.CameraMatrix != None and self.DistCoefs != None:
            undistort = ['undistort', \
                         {'mess': 'perform optical undistortion', \
                          'cameraMatrix': self.CameraMatrix.tolist(), \
                          'distortCoefs': self.DistCoefs.tolist(), \
                          'imageSize': list(self.ImageSize),
                          'rotationAngle': self.rotationAngle
                         } \
                        ]
        else:
            undistort = ['undistort', {'mess': '---skip optical undistortion---'}]
        settings.append(undistort)
        
        if len(self.colorcardList) > 0:
            medianSize = utils.getMedianRectSize(self.colorcardList)
            capturedColorcards = utils.rectifyRectImages(self.image, self.colorcardList, medianSize)
            colorCardFile = 'CapturedColorcard.png'
            cv2.imwrite(os.path.join(settingPath, colorCardFile), capturedColorcards[0][:,:,::-1].astype(np.uint8))
            colorcardColors, colorStd  = utils.getColorcardColors(capturedColorcards[0], GridSize = [6,4])
            colorcardPosition, Width, Height, Angle = utils.getRectangleParamters(self.colorcardList[0])
            colorcarddetect = ['colorcarddetect', \
                               {'mess': '---perform color card detection---', \
                                'colorcardFile': colorCardFile,\
                                'colorcardPosition': colorcardPosition.tolist(),\
                                'colorcardTrueColors': utils.CameraTrax_24ColorCard
                               }
                              ]
        else:
            colorcarddetect = ['colorcarddetect', {'mess': '---skip color card detection---'}]
        settings.append(colorcarddetect)
            
        colorcorrect = ['colorcorrect', {'mess': '---perform color correction---'}]
        settings.append(colorcorrect)

        if len(self.trayList) > 0:
            trayMedianSize = utils.getMedianRectSize(self.trayList)
            trayImages = utils.rectifyRectImages(self.image, self.trayList, trayMedianSize)
            colorcardColors, colorStd  = utils.getColorcardColors(capturedColorcards[0], GridSize = [6,4])
            trayDict = {'mess': '---perform tray detection---'}
            trayDict['trayNumber'] = len(self.trayList)
            trayDict['trayFiles'] = 'Tray_%02d.png'
            trayPositions = []
            for i,tray in enumerate(trayImages):
                cv2.imwrite(os.path.join(settingPath, trayDict['trayFiles'] %i), tray[:,:,::-1].astype(np.uint8))
                trayPosition, Width, Height, Angle = utils.getRectangleParamters(self.trayList[i])
                trayPositions.append(trayPosition.tolist())
            trayDict['trayPositions'] = trayPositions
            traydetect = ['traydetect', trayDict]
        else:
            traydetect = ['traydetect', {'mess': '---skip tray detection---'}]
        settings.append(traydetect)

        if len(self.potList) > 0:
            trayMedianSize = utils.getMedianRectSize(self.trayList)
            potPosition, Width, Height, Angle = utils.getRectangleParamters(self.potList[0])
            Width, Height = int(Width), int(Height)
            topLeft = [int(self.potList[0][0][0]), int(self.potList[0][0][1])]
            self.potImage = self.image[topLeft[1]:topLeft[1]+Height, topLeft[0]:topLeft[0]+Width, :]
            potFile = 'Pot.png'
            cv2.imwrite(os.path.join(settingPath, potFile), self.potImage[:,:,::-1].astype(np.uint8))
            potDict = {'mess': '---perform pot detection---'}
            potDict['potPosition'] = potPosition.tolist()
            potDict['potSize'] = [int(Width), int(Height)]
            potDict['traySize'] = [int(trayMedianSize[0]), int(trayMedianSize[1])]
            potDict['potFile'] = potFile
            if self.potTemplate != None:
                potTemplateFile = 'potTemplate.png'
                cv2.imwrite(os.path.join(settingPath, potTemplateFile), self.potTemplate[:,:,::-1])
                potDict['potTemplateFile'] = potTemplateFile
            potdetect = ['potdetect', potDict]
        else:
            potdetect = ['potdetect', {'mess': '---skip pot detection---'}]
        settings.append(potdetect)
        
        plantextract = ['plantextract', {'mess': '---perfrom plant biometrics extraction---'}]
        settings.append(plantextract)
        
        with open(fname, 'w') as outfile:
            outfile.write( yaml.dump(settings, default_flow_style=None) )


    def rotateImage90Degrees(self):
        if self.image == None:
            self.status.append('No image to rotate.')
            return
        self.rotationAngle = self.rotationAngle + 90
        if self.rotationAngle >= 360:
            self.rotationAngle = self.rotationAngle - 360
        self.image = np.rot90(self.image) #.astype(uint8)
        self.status.append('Rot. angle = %d deg' %self.rotationAngle) 
        if self.plotImg == None:
            self.plotImg = self.ax.imshow(self.image)
        else:
            self.plotImg.set_data(self.image)
        self.canvas.draw()

    def onMouseClicked(self, event):
        if self.panMode or self.zoomMode:
            return
        print('click', event.button, event.xdata, event.ydata)
        
        if event.button == 1 and event.xdata != None and event.ydata != None:
            self.leftClicks.append([event.xdata, event.ydata])
            print('self.leftClicks =', self.leftClicks)
            Rect = []
            AspectRatio = None
            if self.trayRadioButton.isChecked():
                AspectRatio = self.trayAspectRatio
            elif self.colorcardRadioButton.isChecked():
                AspectRatio = self.colorcardAspectRatio
            elif self.potRadioButton.isChecked():
                AspectRatio = self.potAspectRatio
                
            if len(self.leftClicks) == 2 and AspectRatio != None:
                if self.potRadioButton.isChecked():
                    Rect = utils.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio, Rounded = True)
                else:
                    Rect = utils.getRectCornersFrom2Points(self.image, self.leftClicks, AspectRatio)
                self.leftClicks = []            
            elif len(self.leftClicks) == 4:
                Rect = [[x,y] for x,y in self.leftClicks]
                Rect = utils.correctPointOrder(Rect)
                self.leftClicks = []            
    
            if len(Rect) > 0:
                if self.trayRadioButton.isChecked():
                    self.trayList.append(Rect)
                    self.status.append('Added tray selection.')
                elif self.colorcardRadioButton.isChecked():
                    self.colorcardList.append(Rect)
                    self.status.append('Added color card selection.')
                else:
                    self.potList.append(Rect)
                    self.status.append('Added pot selection.')
            self.updateFigure()
        elif event.button == 3:
            # remove the last selection
            if len(self.leftClicks) > 0:
                self.leftClicks = self.leftClicks[:-1]
                self.status.append('Removed a previous click')
            else:
                if self.trayRadioButton.isChecked() and len(self.trayList) > 0:
                    self.trayList = self.trayList[:-1]
                    self.status.append('Removed a previous tray selection')
                elif self.colorcardRadioButton.isChecked() and len(self.colorcardList) > 0:
                    self.colorcardList = self.colorcardList[:-1]
                    self.status.append('Removed a previous color card selection.')
                elif self.potRadioButton.isChecked() and len(self.potList) > 0:
                    self.potList = self.potList[:-1]
                    self.status.append('Removed a previous pot selection')
            self.updateFigure()
        else:
            print('Ignored click')

    def onMouseMoves(self, event):
        if event.inaxes == self.ax:
            self.mousePosition.setText('x=%d, y=%d' %(event.xdata, event.ydata))
#            self.crosshair = [event.xdata, event.ydata]
        else:
            self.mousePosition.setText('')
#            self.crosshair = None
#        self.updateFigure()
        
    def keyPressEvent(self, e):
        if e.key() == QtCore.Qt.Key_Escape:
            self.close()

    def closeEvent(self, event):
    
        quit_msg = "Are you sure you want to exit the program?"
        reply = QtGui.QMessageBox.question(self, 'Message', 
                         quit_msg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
    
        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()