Ejemplo n.º 1
0
 def _icon(self, name):
     if name.startswith(imgutils.RESSOURCE_PATH):
         img = QtGui.QImage()
         img.loadFromData(
             pkg_resources.resource_string(imgutils.__name__, name))
         return QtGui.QIcon(QtGui.QPixmap.fromImage(img))
     return NavigationToolbar._icon(self, name)
Ejemplo n.º 2
0
class PyWeramiWindow(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, filename=None, parent=None):
        super(PyWeramiWindow, self).__init__(parent)
        self.settings = QtCore.QSettings("LX", "pywerami")
        self.setupUi(self)
        self._fig = Figure(facecolor="white")
        self._ax = self._fig.add_subplot(111)

        self._canvas = FigureCanvas(self._fig)
        self._canvas.setParent(self.widget)
        self._canvas.setFocusPolicy(QtCore.Qt.StrongFocus)
        self.matplot.addWidget(self._canvas)
        self.mpl_toolbar = NavigationToolbar(self._canvas, self.widget)
        self.mpl_toolbar.hide()
        self.matplot.addWidget(self.mpl_toolbar)
        self.setWindowTitle('PyWerami')
        window_icon = resource_filename(__name__, 'images/pywerami.png')
        self.setWindowIcon(QtGui.QIcon(window_icon))
        self.about_dialog = AboutDialog(__version__)

        #set combos
        self.cmaps = [
            'viridis', 'inferno', 'plasma', 'magma', 'Blues', 'BuGn', 'BuPu',
            'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd', 'PuBu', 'PuBuGn',
            'PuRd', 'Purples', 'RdPu', 'Reds', 'YlGn', 'YlGnBu', 'YlOrBr',
            'YlOrRd', 'afmhot', 'autumn', 'bone', 'cool', 'copper',
            'gist_heat', 'gray', 'hot', 'pink', 'spring', 'summer', 'winter',
            'BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr', 'RdBu', 'RdGy',
            'RdYlBu', 'RdYlGn', 'Spectral', 'seismic', 'gist_earth', 'terrain',
            'ocean', 'gist_stern', 'brg', 'CMRmap', 'cubehelix', 'gnuplot',
            'gnuplot2', 'gist_ncar', 'nipy_spectral', 'jet', 'rainbow',
            'gist_rainbow', 'hsv', 'flag', 'prism'
        ]
        self.mapstyle.addItems(self.cmaps)

        # set validators
        self.levelmin.setValidator(QtGui.QDoubleValidator(self.levelmin))
        self.levelmax.setValidator(QtGui.QDoubleValidator(self.levelmax))
        self.levelnum.setValidator(QtGui.QIntValidator(self.levelnum))
        self.levelstep.setValidator(QtGui.QDoubleValidator(self.levelstep))
        self.clipmin.setValidator(QtGui.QDoubleValidator(self.clipmin))
        self.clipmax.setValidator(QtGui.QDoubleValidator(self.clipmax))

        # Set icons in toolbar
        self.actionOpen.setIcon(QtGui.QIcon.fromTheme('document-open'))
        self.actionSave.setIcon(QtGui.QIcon.fromTheme('document-save'))
        self.actionSaveas.setIcon(QtGui.QIcon.fromTheme('document-save-as'))
        self.actionImport.setIcon(
            QtGui.QIcon.fromTheme('x-office-spreadsheet'))
        self.actionHome.setIcon(self.mpl_toolbar._icon('home.png'))
        self.actionPan.setIcon(self.mpl_toolbar._icon('move.png'))
        self.actionZoom.setIcon(self.mpl_toolbar._icon('zoom_to_rect.png'))
        self.actionGrid.setIcon(QtGui.QIcon.fromTheme('format-justify-fill'))
        self.actionAxes.setIcon(
            self.mpl_toolbar._icon('qt4_editor_options.png'))
        self.actionSavefig.setIcon(self.mpl_toolbar._icon('filesave.png'))
        #self.action3D.setIcon(QtGui.QIcon.fromTheme(''))
        self.actionProperties.setIcon(
            QtGui.QIcon.fromTheme('preferences-other'))
        self.actionQuit.setIcon(QtGui.QIcon.fromTheme('application-exit'))
        self.actionAbout.setIcon(QtGui.QIcon.fromTheme('help-about'))

        # connect signals
        self.actionOpen.triggered.connect(self.openProject)
        self.actionSave.triggered.connect(self.saveProject)
        self.actionSaveas.triggered.connect(self.saveProjectAs)
        self.actionImport.triggered.connect(self.import_data)
        self.actionHome.triggered.connect(self.mpl_toolbar.home)
        self.actionPan.triggered.connect(self.plotpan)
        self.actionZoom.triggered.connect(self.plotzoom)
        self.actionGrid.triggered.connect(self.plotgrid)
        self.actionAxes.triggered.connect(self.mpl_toolbar.edit_parameters)
        self.actionSavefig.triggered.connect(self.mpl_toolbar.save_figure)
        self.actionProperties.triggered.connect(self.edit_options)
        self.actionQuit.triggered.connect(self.close)
        self.actionAbout.triggered.connect(self.about_dialog.exec)

        # buttons signals
        self.buttonBox.button(
            QtWidgets.QDialogButtonBox.Apply).clicked.connect(self.apply_props)
        self.buttonBox.button(
            QtWidgets.QDialogButtonBox.RestoreDefaults).clicked.connect(
                self.restore_props)
        self.contcolor.clicked.connect(self.contours_color)
        self.action3D.triggered.connect(self.switch3d)
        # signals to calculate step size
        self.levelmin.editingFinished.connect(self.step_from_levels)
        self.levelmax.editingFinished.connect(self.step_from_levels)
        self.levelnum.editingFinished.connect(self.step_from_levels)
        self.setlevels.toggled.connect(self.step_from_levels)
        # almost done
        self.ready = False
        self.changed = False
        self.project = None

        if filename:
            self.import_data(filename)

        # ready
        self.statusbar.showMessage("Ready", 5000)

    def closeEvent(self, event):
        if self.changed:
            quit_msg = 'Project have been changed. Save ?'
            qb = QtWidgets.QMessageBox
            reply = qb.question(self, 'Message', quit_msg,
                                qb.Cancel | qb.Discard | qb.Save, qb.Save)

            if reply == qb.Save:
                self.do_save()
                if self.project is not None:
                    event.accept()
                else:
                    event.ignore()
            elif reply == qb.Discard:
                event.accept()
            else:
                event.ignore()

    def import_data(self, filename=None):
        if not filename:
            filename = QtWidgets.QFileDialog.getOpenFileName(
                self, "Import data file", ".",
                "Perple_X Table (*.tab *.TAB);;TCInvestigator (*.tci *.TCI)"
            )[0]
        if filename:
            if filename.lower().endswith('.tab'):
                self.data = GridData.from_tab(filename)
            elif filename.lower().endswith('.tci'):
                self.data = GridData.from_tci(filename)
            else:
                raise Exception('Unsupported file format')
            # populate listview and setup properties
            self.datafilename = filename
            self.ready = True
            self.project = None
            self.changed = True
            self.props = {}
            self._model = QtGui.QStandardItemModel(self.listView)
            for var in self.data.dep:
                item = QtGui.QStandardItem(var)
                item.setCheckable(True)
                self._model.appendRow(item)
                self.default_var_props(var)

            self.listView.setModel(self._model)
            self.listView.show()

            # connect listview signals
            self.varSel = self.listView.selectionModel()
            try:
                elf.varSel.selectionChanged.disconnect()
            except Exception:
                pass
            self.varSel.selectionChanged.connect(self.on_var_changed)
            try:
                self._model.itemChanged.disconnect()
            except Exception:
                pass
            self._model.itemChanged.connect(self.plot)

            # all done focus
            self.action3D.setChecked(False)  # no 3d on import
            self.varSel.setCurrentIndex(
                self._model.index(0,
                                  0), QtCore.QItemSelectionModel.ClearAndSelect
                | QtCore.QItemSelectionModel.Rows)
            self.listView.setFocus()
            self.plot()
            self.statusbar.showMessage(
                "Data from {} imported".format(self.data.label), 5000)

    def openProject(self, checked, projfile=None):
        """Open pywerami project
        """
        if self.changed:
            quit_msg = 'Project have been changed. Save ?'
            qb = QtWidgets.QMessageBox
            reply = qb.question(self, 'Message', quit_msg,
                                qb.Discard | qb.Save, qb.Save)

            if reply == qb.Save:
                self.do_save()
        if projfile is None:
            qd = QtWidgets.QFileDialog
            filt = 'pywermi project (*.pwp)'
            projfile = qd.getOpenFileName(self, 'Open project',
                                          os.path.expanduser('~'), filt)[0]
        if os.path.exists(projfile):
            stream = gzip.open(projfile, 'rb')
            data = pickle.load(stream)
            stream.close()
            # set actual working dir in case folder was moved
            self.datafilename = data['datafilename']
            self.import_data(self.datafilename)
            self.props = data['props']
            # all done
            self.ready = True
            self.project = projfile
            self.changed = False
            # all done focus
            self.action3D.setChecked(False)  # no 3d on import
            self.varSel.setCurrentIndex(
                self._model.index(0,
                                  0), QtCore.QItemSelectionModel.ClearAndSelect
                | QtCore.QItemSelectionModel.Rows)
            self.listView.setFocus()
            self.plot()
            self.statusbar.showMessage("Project loaded.", 5000)

    def saveProject(self):
        """Save project
        """
        if self.ready:
            if self.project is None:
                filename = QtWidgets.QFileDialog.getSaveFileName(
                    self, 'Save current project',
                    os.path.dirname(self.datafilename),
                    'pywerami project (*.pwp)')[0]
                if filename:
                    if not filename.lower().endswith('.pwp'):
                        filename = filename + '.pwp'
                    self.project = filename
                    self.do_save()
            else:
                self.do_save()

    def saveProjectAs(self):
        """Save project as
        """
        if self.ready:
            filename = QtWidgets.QFileDialog.getSaveFileName(
                self, 'Save current project as',
                os.path.dirname(self.datafilename),
                'pywerami project (*.pwp)')[0]
            if filename:
                if not filename.lower().endswith('.pwp'):
                    filename = filename + '.pwp'
                self.project = filename
                self.do_save()

    def do_save(self):
        """Do saving of poject
        """
        if self.project:
            # put to dict
            data = {'datafilename': self.datafilename, 'props': self.props}
            # do save
            stream = gzip.open(self.project, 'wb')
            pickle.dump(data, stream)
            stream.close()
            self.changed = False
            self.statusBar().showMessage('Project saved.')

    def contours_color(self):
        if self.ready:
            col = QtWidgets.QColorDialog.getColor()
            if col.isValid():
                self.contcolor.setStyleSheet("background-color: {}".format(
                    col.name()))

    def step_from_levels(self):
        if self.ready:
            if int(self.levelnum.text()) < 2:
                self.levelnum.setText('2')
            if float(self.levelmax.text()) < float(self.levelmin.text()):
                self.levelmin.setText(self.levelmax.text())
            if self.setlevels.isChecked():
                step = (float(self.levelmax.text()) - float(
                    self.levelmin.text())) / (int(self.levelnum.text()) - 1)
                self.levelstep.setText(repr(step))
                self.props[self.var]['step'] = step
                self.changed = True

    def default_var_props(self, var):
        if self.ready:
            data = self.data.get_var(var)
            prop = {}
            #levels
            prop['min'] = data.min()
            prop['max'] = data.max()
            prop['num'] = 10
            prop['step'] = (prop['max'] - prop['min']) / (prop['num'] - 1)
            prop['levels'] = 'num'
            prop['type'] = 'linear'
            #style
            prop['fill'] = False
            prop['opacity'] = 100
            prop['cmap'] = 'viridis'
            prop['contours'] = 'color'
            prop['color'] = '#000000'
            prop['label'] = False
            prop['digits'] = 3
            #processing
            prop['resample'] = 1
            prop['median'] = 1
            prop['gauss'] = 0
            prop['clipmin'] = data.min()
            prop['clipmax'] = data.max()

            self.props[var] = prop

    def set_var_props(self, var):
        if self.ready:
            #levels
            self.levelmin.setText(repr(self.props[var]['min']))
            self.levelmax.setText(repr(self.props[var]['max']))
            self.levelnum.setText(repr(self.props[var]['num']))
            self.levelstep.setText(repr(self.props[var]['step']))
            if self.props[var]['levels'] == 'num':
                self.setlevels.setChecked(True)
            else:
                self.setstep.setChecked(True)
            if self.props[var]['type'] == 'linear':
                self.linlevel.setChecked(True)
            else:
                self.cdflevel.setChecked(True)
            #style
            if self.props[var]['fill']:
                self.fillstyle.setChecked(True)
            else:
                self.fillstyle.setChecked(False)
            self.opacity.setValue(self.props[var]['opacity'])
            self.mapstyle.setCurrentIndex(
                self.cmaps.index(self.props[var]['cmap']))
            self.contcolor.setStyleSheet("background-color: {}".format(
                self.props[var]['color']))
            self.labelDigits.setValue(self.props[var]['digits'])
            if self.props[var]['contours'] == 'map':
                self.contcheckmap.setChecked(True)
            elif self.props[var]['contours'] == 'color':
                self.contcheckcolor.setChecked(True)
            else:
                self.contchecknone.setChecked(True)
            if self.props[var]['label']:
                self.contlabel.setChecked(True)
            else:
                self.contlabel.setChecked(False)
            #processing
            self.resample.setValue(self.props[var]['resample'])
            self.filtersize.setValue(self.props[var]['median'])
            self.filtersigma.setValue(self.props[var]['gauss'])
            self.clipmin.setText(repr(self.props[var]['clipmin']))
            self.clipmax.setText(repr(self.props[var]['clipmax']))

    def on_var_changed(self, selected):
        if self.ready:
            self.var = self.data.dep[selected.indexes()[0].row()]
            self.set_var_props(self.var)
            if self.action3D.isChecked():
                self.plot()

    def apply_props(self):
        if self.ready:
            #levels
            self.props[self.var]['min'] = float(self.levelmin.text())
            self.props[self.var]['max'] = float(self.levelmax.text())
            self.props[self.var]['num'] = int(self.levelnum.text())
            self.props[self.var]['step'] = float(self.levelstep.text())
            if self.setlevels.isChecked():
                self.props[self.var]['levels'] = 'num'
            else:
                self.props[self.var]['levels'] = 'step'
            if self.linlevel.isChecked():
                self.props[self.var]['type'] = 'linear'
            else:
                self.props[self.var]['type'] = 'cdf'
            #style
            if self.fillstyle.isChecked():
                self.props[self.var]['fill'] = True
            else:
                self.props[self.var]['fill'] = False
            self.props[self.var]['opacity'] = self.opacity.value()
            self.props[self.var]['cmap'] = str(self.mapstyle.currentText())
            self.props[self.var]['color'] = str(
                self.contcolor.palette().color(1).name())
            self.props[self.var]['digits'] = self.labelDigits.value()
            if self.contcheckmap.isChecked():
                self.props[self.var]['contours'] = 'map'
            elif self.contcheckcolor.isChecked():
                self.props[self.var]['contours'] = 'color'
            else:
                self.props[self.var]['contours'] = ''
            if self.contlabel.isChecked():
                self.props[self.var]['label'] = True
            else:
                self.props[self.var]['label'] = False
            #processing
            self.props[self.var]['resample'] = self.resample.value()
            self.props[self.var]['median'] = self.filtersize.value()
            self.props[self.var]['gauss'] = self.filtersigma.value()
            self.props[self.var]['clipmin'] = float(self.clipmin.text())
            self.props[self.var]['clipmax'] = float(self.clipmax.text())
            self.changed = True
            self.plot()

    def restore_props(self):
        if self.ready:
            self.default_var_props(self.var)
            self.set_var_props(self.var)
            self.plot()

    def edit_options(self):
        dlg = OptionsForm(self)
        dlg.exec_()

    def plotpan(self):
        self.actionZoom.setChecked(False)
        self.mpl_toolbar.pan()

    def plotzoom(self):
        self.actionPan.setChecked(False)
        self.mpl_toolbar.zoom()

    def plotgrid(self):
        self._ax.grid()
        self._canvas.draw()

    def switch3d(self):
        if self.ready:
            if not self.action3D.isChecked():
                self._fig.clear()
                self._ax = self._fig.add_subplot(111)
            else:
                self._fig.clear()
                self._ax = self._fig.add_subplot(111, projection='3d')
            self.plot()

    def plot(self, item=None):
        if self.ready:
            self._ax.cla()
            if item:
                index = self._model.createIndex(item.row(), item.column())
                if index.isValid():
                    self.listView.setCurrentIndex(index)
            if not self.action3D.isChecked():
                extent = self.data.get_extent()
                i = 0
                while self._model.item(i):
                    if self._model.item(i).checkState():
                        CS = None
                        var = str(self._model.item(i).text())
                        # get data, smooth and clip
                        data = self.data.get_var(var,
                                                 nan=np.float(
                                                     self.settings.value(
                                                         "nan",
                                                         "NaN",
                                                         type=str)))
                        if self.props[var]['resample'] > 1:
                            data = np.ma.array(
                                ndimage.zoom(data.filled(0),
                                             self.props[var]['resample']),
                                mask=ndimage.zoom(data.mask,
                                                  self.props[var]['resample'],
                                                  order=0))
                        if self.props[var]['median'] > 1:
                            data = np.ma.array(ndimage.median_filter(
                                data,
                                size=self.props[var]['median'] *
                                self.props[var]['resample']),
                                               mask=data.mask)
                        if self.props[var]['gauss'] > 0:
                            data = np.ma.array(ndimage.gaussian_filter(
                                data,
                                sigma=self.props[var]['gauss'] *
                                self.props[var]['resample']),
                                               mask=data.mask)
                        data = np.ma.masked_outside(data,
                                                    self.props[var]['clipmin'],
                                                    self.props[var]['clipmax'])
                        if self.props[var]['fill']:
                            self._ax.imshow(
                                data,
                                interpolation='none',
                                origin='lower',
                                extent=extent,
                                aspect='auto',
                                cmap=cm.get_cmap(self.props[var]['cmap']),
                                alpha=self.props[var]['opacity'] / 100.0)
                        if self.props[var]['min'] == self.props[var]['max']:
                            clevels = np.array([self.props[var]['min']])
                        else:
                            if self.props[var]['type'] == 'linear':
                                if self.props[var]['levels'] == 'num':
                                    clevels = np.linspace(
                                        self.props[var]['min'],
                                        self.props[var]['max'],
                                        self.props[var]['num'])
                                else:
                                    # trick to include max in levels
                                    clevels = np.arange(
                                        self.props[var]['min'],
                                        self.props[var]['max'] +
                                        np.finfo(np.float32).eps,
                                        self.props[var]['step'])
                            else:
                                # cdf based on histogram binned acording to the Freedman-Diaconis rule
                                data = np.ma.masked_outside(
                                    data, self.props[var]['min'],
                                    self.props[var]['max'])
                                v = np.sort(data.compressed())
                                IQR = v[int(round(
                                    (v.size - 1) * float(0.75)))] - v[int(
                                        round((v.size - 1) * float(0.25)))]
                                bin_size = 2 * IQR * v.size**(-1.0 / 3)
                                nbins = int(
                                    round(
                                        max(self.props[var]['num'],
                                            (v[-1] - v[0]) /
                                            (bin_size + 0.001))))
                                hist, bin_edges = np.histogram(v, bins=nbins)
                                cdf = np.cumsum(hist)
                                cdfx = np.cumsum(np.diff(
                                    bin_edges)) + bin_edges[:2].sum() / 2
                                #clevels = np.interp(np.linspace(cdf[0],cdf[-1],self.props[var]['num'] + 2)[1:-1], cdf, cdfx)
                                clevels = np.interp(
                                    np.linspace(cdf[0], cdf[-1],
                                                self.props[var]['num']), cdf,
                                    cdfx)
                        clevels = np.round(
                            10**self.props[var]['digits'] *
                            clevels) / 10**self.props[var]['digits']
                        # Contour levels must be increasing
                        clevels = np.append(clevels[:1],
                                            clevels[1:][np.diff(clevels) > 0])
                        if self.props[var]['contours'] == 'map':
                            CS = self._ax.contour(
                                self.data.get_xrange(
                                    self.props[var]['resample']),
                                self.data.get_yrange(
                                    self.props[var]['resample']),
                                data,
                                clevels,
                                cmap=cm.get_cmap(self.props[var]['cmap']))
                        elif self.props[var]['contours'] == 'color':
                            CS = self._ax.contour(
                                self.data.get_xrange(
                                    self.props[var]['resample']),
                                self.data.get_yrange(
                                    self.props[var]['resample']),
                                data,
                                clevels,
                                colors=self.props[var]['color'])
                        if self.props[var]['label'] and CS:
                            self._ax.clabel(CS, fontsize=8, inline=1, fmt='%g')
                    i += 1

                self._ax.axis(extent)
                self._ax.set_title(self.data.label)
            else:
                # get data, smooth and clip
                data = self.data.get_var(self.var)
                if self.props[self.var]['resample'] > 1:
                    data = np.ma.array(
                        ndimage.zoom(data.filled(0),
                                     self.props[self.var]['resample']),
                        mask=ndimage.zoom(data.mask,
                                          self.props[self.var]['resample'],
                                          order=0))
                if self.props[self.var]['median'] > 1:
                    data = np.ma.array(ndimage.median_filter(
                        data,
                        size=self.props[self.var]['median'] *
                        self.props[self.var]['resample']),
                                       mask=data.mask)
                if self.props[self.var]['gauss'] > 0:
                    data = np.ma.array(ndimage.gaussian_filter(
                        data,
                        sigma=self.props[self.var]['gauss'] *
                        self.props[self.var]['resample']),
                                       mask=data.mask)
                data = np.ma.masked_outside(data,
                                            self.props[self.var]['clipmin'],
                                            self.props[self.var]['clipmax'])
                x, y = np.meshgrid(
                    self.data.get_xrange(self.props[self.var]['resample']),
                    self.data.get_yrange(self.props[self.var]['resample']))
                self._ax.plot_surface(
                    x,
                    y,
                    data.filled(np.NaN),
                    vmin=data.min(),
                    vmax=data.max(),
                    cmap=cm.get_cmap(self.props[self.var]['cmap']),
                    linewidth=0.5,
                    alpha=self.props[self.var]['opacity'] / 100.0)
                self._ax.view_init(azim=235, elev=30)

            self._ax.set_xlabel(self.data.ind[self.data.xvar]['name'])
            self._ax.set_ylabel(self.data.ind[self.data.yvar]['name'])
            self._fig.tight_layout()
            self._canvas.draw()
Ejemplo n.º 3
0
class MplWidget(QtWidgets.QWidget):
    """
    Widget defined in Qt Designer.
    """

    zoom_to_full_view = pyqtSignal()
    map_press = pyqtSignal(float, float)

    def __init__(self, parent = None):

        # initialization of Qt MainWindow widget
        QtWidgets.QWidget.__init__(self, parent)
        
        # set the canvas to the Matplotlib widget
        self.canvas = MplCanvas()

        self.ntb = NavigationToolbar(self.canvas, self)
        
        #self.ntb.removeAction(self.ntb.buttons[0])
        self.ntb.clear()
        
        program_folder = os.path.join(os.path.dirname(__file__), "ims")

        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "world.png")), 'Home', self.zoom2fullextent)
        a.setToolTip('Reset original view')
        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_left.png")), 'Back', self.ntb.back)
        a.setToolTip('Back to previous view')
        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_right.png")), 'Forward', self.ntb.forward)
        a.setToolTip('Forward to next view')

        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "arrow_out.png")), 'Pan', self.ntb.pan)
        a.setToolTip('Pan axes with left mouse, zoom with right')
        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "zoom.png")), 'Zoom', self.ntb.zoom)
        a.setToolTip('Zoom to rectangle')

        action_SetSrcPt = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "bullet_red.png")), 'Source point', self.pt_map)
        action_SetSrcPt.setToolTip('Set source point in map') 

        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "camera.png")), 'Save',
                self.ntb.save_figure)
        a.setToolTip('Save map as image')

        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "help.png")), 'Help',
                self.openHelp)
        a.setToolTip('Help')
        
        a = self.ntb.addAction(self.ntb._icon(os.path.join(program_folder, "information.png")), 'About',
                self.helpAbout)
        a.setToolTip('About')        
                                            
        # create a vertical box layout
        self.vbl = QtWidgets.QVBoxLayout()
        
        # add widgets to the vertical box
        self.vbl.addWidget(self.canvas)
        self.vbl.addWidget(self.ntb)
                                                             
        # set the layout to the vertical box
        self.setLayout(self.vbl)        
        
    def onclick(self, event): 
        """
        Emit a signal to induce the update of source point location.
        
        @param event: press event.
        @type event: Matplotlib event.
        """          
        global set_srcpt_count, cid
        
        set_srcpt_count += 1
        
        if set_srcpt_count == 1:
            self.map_press.emit(event.xdata, event.ydata)

        self.canvas.fig.canvas.mpl_disconnect(cid)
                
    def pt_map(self):
        """
        Connect the press event with the function for updating the source point location.
        
        """        
        global set_srcpt_count, cid
            
        set_srcpt_count = 0
        
        cid = self.canvas.fig.canvas.mpl_connect('button_press_event', self.onclick)    
                
    def zoom2fullextent(self):
        """
        Emit the signal for updating the map view to the extent of the DEM, in alternative of
        the shapefile, or at the standard extent.
        
        """
        self.zoom_to_full_view.emit()

    def openHelp(self):
        """
        Open an Help HTML file

        after CADTOOLS module in QG
        """
        help_path = os.path.join(os.path.dirname(__file__), 'help', 'help.html')         
        webbrowser.open(help_path)
            
    def helpAbout(self):
        """
        Visualize an About window.
        """
        QtWidgets.QMessageBox.about(self, "About gSurf",
        """
            <p>gSurf<br />License: GPL v. 3</p>
            <p>This application calculates the intersection between a plane and a DEM in an interactive way.
            The result is a set of points/lines that can be saved as shapefiles.            
            </p>
             <p>Report any bug to <a href="mailto:[email protected]">[email protected]</a></p>
        """)