Example #1
1
class HydroprintGUI(myqt.DialogWindow):

    ConsoleSignal = QSignal(str)

    def __init__(self, datamanager, parent=None):
        super(HydroprintGUI, self).__init__(parent, maximize=True)

        self.__updateUI = True

        # Child widgets:

        self.dmngr = datamanager
        self.dmngr.wldsetChanged.connect(self.wldset_changed)
        self.dmngr.wxdsetChanged.connect(self.wxdset_changed)

        self.page_setup_win = PageSetupWin(self)
        self.page_setup_win.newPageSetupSent.connect(self.layout_changed)

        self.color_palette_win = ColorsSetupWin(self)
        self.color_palette_win.newColorSetupSent.connect(self.update_colors)

        # Memory path variable:

        self.save_fig_dir = self.workdir

        # Generate UI:

        self.__initUI__()

    def __initUI__(self):

        # ---- Toolbar

        self.btn_save = btn_save = QToolButtonNormal(icons.get_icon('save'))
        btn_save.setToolTip('Save the well hydrograph')

        # btn_draw is usefull for debugging purposes
        btn_draw = QToolButtonNormal(icons.get_icon('refresh'))
        btn_draw.setToolTip('Force a refresh of the well hydrograph')
        btn_draw.hide()

        self.btn_load_layout = QToolButtonNormal(
                icons.get_icon('load_graph_config'))
        self.btn_load_layout.setToolTip(
                "<p>Load graph layout for the current water level "
                " datafile if it exists</p>")
        self.btn_load_layout.clicked.connect(self.load_layout_isClicked)

        self.btn_save_layout = QToolButtonNormal(
                icons.get_icon('save_graph_config'))
        self.btn_save_layout.setToolTip('Save current graph layout')
        self.btn_save_layout.clicked.connect(self.save_layout_isClicked)

        btn_bestfit_waterlvl = QToolButtonNormal(icons.get_icon('fit_y'))
        btn_bestfit_waterlvl.setToolTip('Best fit the water level scale')

        btn_bestfit_time = QToolButtonNormal(icons.get_icon('fit_x'))
        btn_bestfit_time.setToolTip('Best fit the time scale')

        self.btn_page_setup = QToolButtonNormal(icons.get_icon('page_setup'))
        self.btn_page_setup.setToolTip('Show the page setup window')
        self.btn_page_setup.clicked.connect(self.page_setup_win.show)

        btn_color_pick = QToolButtonNormal(icons.get_icon('color_picker'))
        btn_color_pick.setToolTip('<p>Show a window to setup the color palette'
                                  ' used to draw the hydrograph</p.')
        btn_color_pick.clicked.connect(self.color_palette_win.show)

        self.btn_language = LangToolButton()
        self.btn_language.setToolTip(
            "Set the language of the text shown in the graph.")
        self.btn_language.sig_lang_changed.connect(self.layout_changed)
        self.btn_language.setIconSize(icons.get_iconsize('normal'))

        # ---- Zoom Panel

        btn_zoom_out = QToolButtonSmall(icons.get_icon('zoom_out'))
        btn_zoom_out.setToolTip('Zoom out (ctrl + mouse-wheel-down)')
        btn_zoom_out.clicked.connect(self.zoom_out)

        btn_zoom_in = QToolButtonSmall(icons.get_icon('zoom_in'))
        btn_zoom_in.setToolTip('Zoom in (ctrl + mouse-wheel-up)')
        btn_zoom_in.clicked.connect(self.zoom_in)

        self.zoom_disp = QSpinBox()
        self.zoom_disp.setAlignment(Qt.AlignCenter)
        self.zoom_disp.setButtonSymbols(QAbstractSpinBox.NoButtons)
        self.zoom_disp.setReadOnly(True)
        self.zoom_disp.setSuffix(' %')
        self.zoom_disp.setRange(0, 9999)
        self.zoom_disp.setValue(100)

        zoom_pan = myqt.QFrameLayout()
        zoom_pan.setSpacing(3)
        zoom_pan.addWidget(btn_zoom_out, 0, 0)
        zoom_pan.addWidget(btn_zoom_in, 0, 1)
        zoom_pan.addWidget(self.zoom_disp, 0, 2)

        # LAYOUT :

        btn_list = [btn_save, btn_draw,
                    self.btn_load_layout, self.btn_save_layout, VSep(),
                    btn_bestfit_waterlvl, btn_bestfit_time, VSep(),
                    self.btn_page_setup, btn_color_pick, self.btn_language,
                    VSep(), zoom_pan]

        subgrid_toolbar = QGridLayout()
        toolbar_widget = QWidget()

        row = 0
        for col, btn in enumerate(btn_list):
            subgrid_toolbar.addWidget(btn, row, col)

        subgrid_toolbar.setSpacing(5)
        subgrid_toolbar.setContentsMargins(0, 0, 0, 0)
        subgrid_toolbar.setColumnStretch(col + 1, 100)

        toolbar_widget.setLayout(subgrid_toolbar)

        # ---- LEFT PANEL

        # SubGrid Hydrograph Frame :

        self.hydrograph = hydrograph.Hydrograph()
        self.hydrograph_scrollarea = mplFigViewer.ImageViewer()
        self.hydrograph_scrollarea.zoomChanged.connect(self.zoom_disp.setValue)

        grid_hydrograph = QGridLayout()
        grid_hydrograph.addWidget(self.hydrograph_scrollarea, 0, 0)
        grid_hydrograph.setRowStretch(0, 500)
        grid_hydrograph.setColumnStretch(0, 500)
        grid_hydrograph.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        # ASSEMBLING SubGrids :

        grid_layout = QGridLayout()
        self.grid_layout_widget = QFrame()

        row = 0
        grid_layout.addWidget(toolbar_widget, row, 0)
        row += 1
        grid_layout.addLayout(grid_hydrograph, row, 0)

        grid_layout.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        grid_layout.setSpacing(5)
        grid_layout.setColumnStretch(0, 500)
        grid_layout.setRowStretch(1, 500)

        self.grid_layout_widget.setLayout(grid_layout)

        # ---- Right Panel

        self.tabscales = self.__init_scalesTabWidget__()

        self.right_panel = myqt.QFrameLayout()
        self.right_panel.addWidget(self.dmngr, 0, 0)
        self.right_panel.addWidget(self.tabscales, 1, 0)
        self.right_panel.setRowStretch(2, 100)

        self.right_panel.setSpacing(15)

        # ---- MAIN GRID

        mainGrid = QGridLayout()

        mainGrid.addWidget(self.grid_layout_widget, 0, 0)
        mainGrid.addWidget(VSep(), 0, 1)
        mainGrid.addWidget(self.right_panel, 0, 2)

        mainGrid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)
        mainGrid.setSpacing(15)
        mainGrid.setColumnStretch(0, 500)
        mainGrid.setColumnMinimumWidth(2, 250)

        self.setLayout(mainGrid)

        # ---- EVENTS

        # Toolbox Layout :

        btn_bestfit_waterlvl.clicked.connect(self.best_fit_waterlvl)
        btn_bestfit_time.clicked.connect(self.best_fit_time)
        btn_draw.clicked.connect(self.draw_hydrograph)
        btn_save.clicked.connect(self.select_save_path)

        # Hydrograph Layout :

        self.Ptot_scale.valueChanged.connect(self.layout_changed)
        self.qweather_bin.currentIndexChanged.connect(self.layout_changed)

        # ---- Init Image

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def __init_scalesTabWidget__(self):

        class QRowLayout(QGridLayout):
            def __init__(self, items, parent=None):
                super(QRowLayout, self).__init__(parent)

                for col, item in enumerate(items):
                    self.addWidget(item, 0, col)

                self.setContentsMargins(0, 0, 0, 0)
                self.setColumnStretch(0, 100)

        # ---- Time axis properties

        # Generate the widgets :

        self.date_start_widget = QDateEdit()
        self.date_start_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_start_widget.setAlignment(Qt.AlignCenter)
        self.date_start_widget.dateChanged.connect(self.layout_changed)

        self.date_end_widget = QDateEdit()
        self.date_end_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_end_widget.setAlignment(Qt.AlignCenter)
        self.date_end_widget.dateChanged.connect(self.layout_changed)

        self.time_scale_label = QComboBox()
        self.time_scale_label.setEditable(False)
        self.time_scale_label.setInsertPolicy(QComboBox.NoInsert)
        self.time_scale_label.addItems(['Month', 'Year'])
        self.time_scale_label.setCurrentIndex(0)
        self.time_scale_label.currentIndexChanged.connect(self.layout_changed)

        self.dateDispFreq_spinBox = QSpinBox()
        self.dateDispFreq_spinBox.setSingleStep(1)
        self.dateDispFreq_spinBox.setMinimum(1)
        self.dateDispFreq_spinBox.setMaximum(100)
        self.dateDispFreq_spinBox.setValue(
            self.hydrograph.date_labels_pattern)
        self.dateDispFreq_spinBox.setAlignment(Qt.AlignCenter)
        self.dateDispFreq_spinBox.setKeyboardTracking(False)
        self.dateDispFreq_spinBox.valueChanged.connect(self.layout_changed)

        # Setting up the layout :

        widget_time_scale = QFrame()
        widget_time_scale.setFrameStyle(0)
        grid_time_scale = QGridLayout()

        GRID = [[QLabel('From :'), self.date_start_widget],
                [QLabel('To :'), self.date_end_widget],
                [QLabel('Scale :'), self.time_scale_label],
                [QLabel('Date Disp. Pattern:'),
                 self.dateDispFreq_spinBox]]

        for i, ROW in enumerate(GRID):
            grid_time_scale.addLayout(QRowLayout(ROW), i, 1)

        grid_time_scale.setVerticalSpacing(5)
        grid_time_scale.setContentsMargins(10, 10, 10, 10)

        widget_time_scale.setLayout(grid_time_scale)

        # ----- Water level axis properties

        # Widget :

        self.waterlvl_scale = QDoubleSpinBox()
        self.waterlvl_scale.setSingleStep(0.05)
        self.waterlvl_scale.setMinimum(0.05)
        self.waterlvl_scale.setSuffix('  m')
        self.waterlvl_scale.setAlignment(Qt.AlignCenter)
        self.waterlvl_scale.setKeyboardTracking(False)
        self.waterlvl_scale.valueChanged.connect(self.layout_changed)
        self.waterlvl_scale.setFixedWidth(100)

        self.waterlvl_max = QDoubleSpinBox()
        self.waterlvl_max.setSingleStep(0.1)
        self.waterlvl_max.setSuffix('  m')
        self.waterlvl_max.setAlignment(Qt.AlignCenter)
        self.waterlvl_max.setMinimum(-1000)
        self.waterlvl_max.setMaximum(1000)
        self.waterlvl_max.setKeyboardTracking(False)
        self.waterlvl_max.valueChanged.connect(self.layout_changed)
        self.waterlvl_max.setFixedWidth(100)

        self.NZGridWL_spinBox = QSpinBox()
        self.NZGridWL_spinBox.setSingleStep(1)
        self.NZGridWL_spinBox.setMinimum(1)
        self.NZGridWL_spinBox.setMaximum(50)
        self.NZGridWL_spinBox.setValue(self.hydrograph.NZGrid)
        self.NZGridWL_spinBox.setAlignment(Qt.AlignCenter)
        self.NZGridWL_spinBox.setKeyboardTracking(False)
        self.NZGridWL_spinBox.valueChanged.connect(self.layout_changed)
        self.NZGridWL_spinBox.setFixedWidth(100)

        self.datum_widget = QComboBox()
        self.datum_widget.addItems(['Ground Surface', 'Sea Level'])
        self.datum_widget.currentIndexChanged.connect(self.layout_changed)

        # Layout :

        subgrid_WLScale = QGridLayout()

        GRID = [[QLabel('Minimum :'), self.waterlvl_max],
                [QLabel('Scale :'), self.waterlvl_scale],
                [QLabel('Grid Divisions :'), self.NZGridWL_spinBox],
                [QLabel('Datum :'), self.datum_widget]]

        for i, ROW in enumerate(GRID):
            subgrid_WLScale.addLayout(QRowLayout(ROW), i, 1)

        subgrid_WLScale.setVerticalSpacing(5)
        subgrid_WLScale.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        WLScale_widget = QFrame()
        WLScale_widget.setFrameStyle(0)
        WLScale_widget.setLayout(subgrid_WLScale)

        # ---- Weather Axis

        # Widgets :

        self.Ptot_scale = QSpinBox()
        self.Ptot_scale.setSingleStep(5)
        self.Ptot_scale.setMinimum(5)
        self.Ptot_scale.setMaximum(500)
        self.Ptot_scale.setValue(20)
        self.Ptot_scale.setSuffix('  mm')
        self.Ptot_scale.setAlignment(Qt.AlignCenter)

        self.qweather_bin = QComboBox()
        self.qweather_bin.setEditable(False)
        self.qweather_bin.setInsertPolicy(QComboBox.NoInsert)
        self.qweather_bin.addItems(['day', 'week', 'month'])
        self.qweather_bin.setCurrentIndex(1)

        # Layout :

        layout = QGridLayout()

        GRID = [[QLabel('Precip. Scale :'), self.Ptot_scale],
                [QLabel('Resampling :'), self.qweather_bin]]

        for i, row in enumerate(GRID):
            layout.addLayout(QRowLayout(row), i, 1)

        layout.setVerticalSpacing(5)
        layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        layout.setRowStretch(i+1, 100)

        widget_weather_scale = QFrame()
        widget_weather_scale.setFrameStyle(0)
        widget_weather_scale.setLayout(layout)

        # ---- ASSEMBLING TABS

        tabscales = QTabWidget()
        tabscales.addTab(widget_time_scale, 'Time')
        tabscales.addTab(WLScale_widget, 'Water Level')
        tabscales.addTab(widget_weather_scale, 'Weather')

        return tabscales

    @property
    def workdir(self):
        return self.dmngr.workdir

    # ---- Utilities

    def zoom_in(self):
        self.hydrograph_scrollarea.zoomIn()

    def zoom_out(self):
        self.hydrograph_scrollarea.zoomOut()

    def update_colors(self):
        self.hydrograph.update_colors()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    # ---- Datasets Handlers

    @property
    def wldset(self):
        return self.dmngr.get_current_wldset()

    @property
    def wxdset(self):
        return self.dmngr.get_current_wxdset()

    def wldset_changed(self):
        """Handle when the water level dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
            return
        else:
            self.hydrograph.set_wldset(self.wldset)
            self.hydrograph.gluedf = self.wldset.get_glue_at(-1)

        # Load the manual measurements.

        fname = os.path.join(
            self.workdir, "Water Levels", 'waterlvl_manual_measurements')
        tmeas, wlmeas = load_waterlvl_measures(fname, self.wldset['Well'])
        self.wldset.set_wlmeas(tmeas, wlmeas)

        # Setup the layout of the hydrograph.

        layout = self.wldset.get_layout()
        if layout is not None:
            msg = ("Loading existing graph layout for well %s." %
                   self.wldset['Well'])
            print(msg)
            self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
            self.load_graph_layout(layout)
        else:
            print('No graph layout exists for well %s.' % self.wldset['Well'])
            # Fit Water Level in Layout :
            self.__updateUI = False
            self.best_fit_waterlvl()
            self.best_fit_time()
            if self.dmngr.set_closest_wxdset() is None:
                self.draw_hydrograph()
            self.__updateUI = True

    def wxdset_changed(self):
        """Handle when the weather dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
        else:
            self.hydrograph.set_wxdset(self.wxdset)
            QCoreApplication.processEvents()
            self.draw_hydrograph()

    # ---- Draw Hydrograph Handlers
    def best_fit_waterlvl(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            WLscale, WLmin = self.hydrograph.best_fit_waterlvl()
            self.waterlvl_scale.setValue(WLscale)
            self.waterlvl_max.setValue(WLmin)

    def best_fit_time(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            date0, date1 = self.hydrograph.best_fit_time(wldset.xldates)
            self.date_start_widget.setDate(QDate(date0[0], date0[1], date0[2]))
            self.date_end_widget.setDate(QDate(date1[0], date1[1], date1[2]))

    @QSlot()
    def mrc_wl_changed(self):
        """
        Force a redraw of the MRC water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.draw_mrc_wl()
        self.hydrograph.setup_legend()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    @QSlot(GLUEDataFrameBase)
    def glue_wl_changed(self, gluedf):
        """
        Force a redraw of the GLUE water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.set_gluedf(gluedf)
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def layout_changed(self):
        """
        When an element of the graph layout is changed in the UI.
        """

        if self.__updateUI is False:
            return

        self.update_graph_layout_parameter()

        if self.hydrograph.isHydrographExists is False:
            return

        sender = self.sender()

        if sender == self.btn_language:
            self.hydrograph.draw_ylabels()
            self.hydrograph.setup_xticklabels()
            self.hydrograph.setup_legend()
        elif sender in [self.waterlvl_max, self.waterlvl_scale]:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.NZGridWL_spinBox:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.Ptot_scale:
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.datum_widget:
            yoffset = int(self.wldset['Elevation']/self.hydrograph.WLscale)
            yoffset *= self.hydrograph.WLscale

            self.hydrograph.WLmin = (yoffset - self.hydrograph.WLmin)

            self.waterlvl_max.blockSignals(True)
            self.waterlvl_max.setValue(self.hydrograph.WLmin)
            self.waterlvl_max.blockSignals(False)

            # This is calculated so that trailing zeros in the altitude of the
            # well is not carried to the y axis labels, so that they remain a
            # int multiple of *WLscale*.

            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_waterlvl()
            self.hydrograph.draw_ylabels()
        elif sender in [self.date_start_widget, self.date_end_widget]:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_figure_title()
        elif sender == self.dateDispFreq_spinBox:
            self.hydrograph.set_time_scale()
            self.hydrograph.setup_xticklabels()
        elif sender == self.page_setup_win:
            self.hydrograph.update_fig_size()
            # Implicitly call : set_margins()
            #                   draw_ylabels()
            #                   set_time_scale()
            #                   draw_figure_title
            self.hydrograph.draw_waterlvl()
            self.hydrograph.setup_legend()
        elif sender == self.qweather_bin:
            self.hydrograph.resample_bin()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_ylabels()
        elif sender == self.time_scale_label:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
        else:
            print('No action for this widget yet.')

        # !!!! temporary fix until I can find a better solution !!!!

#        sender.blockSignals(True)
        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(True)

        for i in range(10):
            QCoreApplication.processEvents()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)
        for i in range(10):
            QCoreApplication.processEvents()

        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(False)
#        sender.blockSignals(False)

    def update_graph_layout_parameter(self):

        # language :

        self.hydrograph.language = self.btn_language.language

        # Scales :

        self.hydrograph.WLmin = self.waterlvl_max.value()
        self.hydrograph.WLscale = self.waterlvl_scale.value()
        self.hydrograph.RAINscale = self.Ptot_scale.value()
        self.hydrograph.NZGrid = self.NZGridWL_spinBox.value()

        # WL Datum :

        self.hydrograph.WLdatum = self.datum_widget.currentIndex()

        # Dates :

        self.hydrograph.datemode = self.time_scale_label.currentText()

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        self.hydrograph.TIMEmin = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        self.hydrograph.TIMEmax = xldate_from_date_tuple((year, month, 1), 0)

        self.hydrograph.date_labels_pattern = self.dateDispFreq_spinBox.value()

        # Page Setup :

        self.hydrograph.fwidth = self.page_setup_win.pageSize[0]
        self.hydrograph.fheight = self.page_setup_win.pageSize[1]
        self.hydrograph.va_ratio = self.page_setup_win.va_ratio

        self.hydrograph.trend_line = self.page_setup_win.isTrendLine
        self.hydrograph.isLegend = self.page_setup_win.isLegend
        self.hydrograph.isGraphTitle = self.page_setup_win.isGraphTitle
        self.hydrograph.set_meteo_on(self.page_setup_win.is_meteo_on)
        self.hydrograph.set_glue_wl_on(self.page_setup_win.is_glue_wl_on)
        self.hydrograph.set_mrc_wl_on(self.page_setup_win.is_mrc_wl_on)
        self.hydrograph.set_figframe_lw(self.page_setup_win.figframe_lw)

        # Weather bins :

        self.hydrograph.bwidth_indx = self.qweather_bin.currentIndex()

    def clear_hydrograph(self):
        """Clear the hydrograph figure to show only a blank canvas."""
        self.hydrograph.clf()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def draw_hydrograph(self):
        if self.dmngr.wldataset_count() == 0:
            msg = 'Please import a valid water level data file first.'
            self.ConsoleSignal.emit('<font color=red>%s</font>' % msg)
            self.emit_warning(msg)
            return

        self.update_graph_layout_parameter()

        # Generate and Display Graph :

        for i in range(5):
            QCoreApplication.processEvents()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.hydrograph.set_wldset(self.dmngr.get_current_wldset())
        self.hydrograph.set_wxdset(self.dmngr.get_current_wxdset())
        self.hydrograph.generate_hydrograph()

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

        QApplication.restoreOverrideCursor()

    def select_save_path(self):
        """
        Open a dialog where the user can select a file name to save the
        hydrograph.
        """
        if self.wldset is None:
            return

        ffmat = "*.pdf;;*.svg;;*.png"
        fname = find_unique_filename(osp.join(
            self.save_fig_dir, 'hydrograph_%s.pdf' % self.wldset['Well']))

        fname, ftype = QFileDialog.getSaveFileName(
            self, "Save Figure", fname, ffmat)
        if fname:
            ftype = ftype.replace('*', '')
            fname = fname if fname.endswith(ftype) else fname + ftype
            self.save_fig_dir = os.path.dirname(fname)

            try:
                self.save_figure(fname)
            except PermissionError:
                msg = "The file is in use by another application or user."
                QMessageBox.warning(self, 'Warning', msg, QMessageBox.Ok)
                self.select_save_path()

    def save_figure(self, fname):
        """Save the hydrograph figure in a file."""
        self.hydrograph.generate_hydrograph()
        self.hydrograph.savefig(fname)

    # ---- Graph Layout Handlers

    def load_layout_isClicked(self):
        """Handle when the button to load the graph layout is clicked."""
        if self.wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = self.wldset.get_layout()
        if layout is None:
            self.emit_warning(
                "No graph layout exists for well %s." % self.wldset['Well'])
        else:
            self.load_graph_layout(layout)

    def load_graph_layout(self, layout):
        """Load the graph layout into the GUI."""

        self.__updateUI = False

        # Scales :

        date = layout['TIMEmin']
        date = xldate_as_tuple(date, 0)
        self.date_start_widget.setDate(QDate(date[0], date[1], date[2]))

        date = layout['TIMEmax']
        date = xldate_as_tuple(date, 0)
        self.date_end_widget.setDate(QDate(date[0], date[1], date[2]))

        self.dateDispFreq_spinBox.setValue(layout['date_labels_pattern'])

        self.waterlvl_scale.setValue(layout['WLscale'])
        self.waterlvl_max.setValue(layout['WLmin'])
        self.NZGridWL_spinBox.setValue(layout['NZGrid'])
        self.Ptot_scale.setValue(layout['RAINscale'])

        x = ['mbgs', 'masl'].index(layout['WLdatum'])
        self.datum_widget.setCurrentIndex(x)

        self.qweather_bin.setCurrentIndex(layout['bwidth_indx'])
        self.time_scale_label.setCurrentIndex(
            self.time_scale_label.findText(layout['datemode']))

        # ---- Language and colors
        self.btn_language.set_language(layout['language'])
        self.color_palette_win.load_colors()

        # ---- Page Setup

        self.page_setup_win.pageSize = (layout['fwidth'], layout['fheight'])
        self.page_setup_win.va_ratio = layout['va_ratio']
        self.page_setup_win.isLegend = layout['legend_on']
        self.page_setup_win.isGraphTitle = layout['title_on']
        self.page_setup_win.isTrendLine = layout['trend_line']
        self.page_setup_win.is_meteo_on = layout['meteo_on']
        self.page_setup_win.is_glue_wl_on = layout['glue_wl_on']
        self.page_setup_win.is_mrc_wl_on = layout['mrc_wl_on']
        self.page_setup_win.figframe_lw = layout['figframe_lw']

        self.page_setup_win.legend_on.set_value(layout['legend_on'])
        self.page_setup_win.title_on.set_value(layout['title_on'])
        self.page_setup_win.wltrend_on.set_value(layout['trend_line'])
        self.page_setup_win.meteo_on.set_value(layout['meteo_on'])
        self.page_setup_win.glue_wl_on.set_value(layout['glue_wl_on'])
        self.page_setup_win.mrc_wl_on.set_value(layout['mrc_wl_on'])
        self.page_setup_win.fframe_lw_widg.setValue(layout['figframe_lw'])

        self.page_setup_win.fwidth.setValue(layout['fwidth'])
        self.page_setup_win.fheight.setValue(layout['fheight'])
        self.page_setup_win.va_ratio_spinBox.setValue(layout['va_ratio'])

        # Check if Weather Dataset :

        if layout['wxdset'] in self.dmngr.wxdsets:
            self.dmngr.set_current_wxdset(layout['wxdset'])
        else:
            if self.dmngr.set_closest_wxdset() is None:
                self.draw_hydrograph()
        self.__updateUI = True

    def save_layout_isClicked(self):
        wldset = self.wldset
        if wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = wldset.get_layout()
        if layout is not None:
            msg = ('A graph layout already exists for well %s.Do you want to'
                   ' you want to replace it?') % wldset['Well']
            reply = QMessageBox.question(self, 'Save Graph Layout', msg,
                                         QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.save_graph_layout()
            elif reply == QMessageBox.No:
                msg = "Graph layout not saved for well %s." % wldset['Well']
                self.ConsoleSignal.emit('<font color=black>%s' % msg)
        else:
            self.save_graph_layout()

    def save_graph_layout(self):
        """Save the graph layout in the project hdf5 file."""
        print("Saving the graph layout for well %s..." % self.wldset['Well'],
              end=" ")

        layout = {'WLmin': self.waterlvl_max.value(),
                  'WLscale': self.waterlvl_scale.value(),
                  'RAINscale': self.Ptot_scale.value(),
                  'fwidth': self.page_setup_win.pageSize[0],
                  'fheight': self.page_setup_win.pageSize[1],
                  'va_ratio': self.page_setup_win.va_ratio,
                  'NZGrid': self.NZGridWL_spinBox.value(),
                  'bwidth_indx': self.qweather_bin.currentIndex(),
                  'date_labels_pattern': self.dateDispFreq_spinBox.value(),
                  'datemode': self.time_scale_label.currentText()}
        layout['wxdset'] = None if self.wxdset is None else self.wxdset.name

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        layout['TIMEmin'] = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        layout['TIMEmax'] = xldate_from_date_tuple((year, month, 1), 0)

        if self.datum_widget.currentIndex() == 0:
            layout['WLdatum'] = 'mbgs'
        else:
            layout['WLdatum'] = 'masl'

        # ---- Page Setup

        layout['title_on'] = bool(self.page_setup_win.isGraphTitle)
        layout['legend_on'] = bool(self.page_setup_win.isLegend)
        layout['language'] = self.btn_language.language
        layout['trend_line'] = bool(self.page_setup_win.isTrendLine)
        layout['meteo_on'] = bool(self.page_setup_win.is_meteo_on)
        layout['glue_wl_on'] = bool(self.page_setup_win.is_glue_wl_on)
        layout['mrc_wl_on'] = bool(self.page_setup_win.is_mrc_wl_on)
        layout['figframe_lw'] = self.page_setup_win.figframe_lw

        # Save the colors :

        cdb = ColorsReader()
        cdb.load_colors_db()
        layout['colors'] = cdb.RGB

        # Save the layout :

        self.wldset.save_layout(layout)
        msg = 'Layout saved successfully for well %s.' % self.wldset['Well']
        self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
        print("done")
Example #2
0
    def updateView(self, value):

        # make sure that the db record has a PREC field set,
        # otherwise the prescision of the values returned by the monitor is arbitrary
        value = float(value)
        widget = self
        QDoubleSpinBox.blockSignals(widget, True)
        QDoubleSpinBox.setValue(widget, float(value))
        QDoubleSpinBox.blockSignals(widget, False)
Example #3
0
class HydroprintGUI(myqt.DialogWindow):

    ConsoleSignal = QSignal(str)

    def __init__(self, datamanager, parent=None):
        super(HydroprintGUI, self).__init__(parent, maximize=True)

        self.__updateUI = True

        # Child widgets:

        self.dmngr = datamanager
        self.dmngr.wldsetChanged.connect(self.wldset_changed)
        self.dmngr.wxdsetChanged.connect(self.wxdset_changed)

        self.page_setup_win = PageSetupWin(self)
        self.page_setup_win.newPageSetupSent.connect(self.layout_changed)

        self.color_palette_win = ColorsSetupWin(self)
        self.color_palette_win.newColorSetupSent.connect(self.update_colors)

        # Memory path variable:

        self.save_fig_dir = self.workdir

        # Generate UI:

        self.__initUI__()

    def __initUI__(self):

        # ---- Toolbar

        self.btn_save = btn_save = QToolButtonNormal(icons.get_icon('save'))
        btn_save.setToolTip('Save the well hydrograph')

        # btn_draw is usefull for debugging purposes
        btn_draw = QToolButtonNormal(icons.get_icon('refresh'))
        btn_draw.setToolTip('Force a refresh of the well hydrograph')
        btn_draw.hide()

        self.btn_load_layout = QToolButtonNormal(
            icons.get_icon('load_graph_config'))
        self.btn_load_layout.setToolTip(
            "<p>Load graph layout for the current water level "
            " datafile if it exists</p>")
        self.btn_load_layout.clicked.connect(self.load_layout_isClicked)

        self.btn_save_layout = QToolButtonNormal(
            icons.get_icon('save_graph_config'))
        self.btn_save_layout.setToolTip('Save current graph layout')
        self.btn_save_layout.clicked.connect(self.save_layout_isClicked)

        btn_bestfit_waterlvl = QToolButtonNormal(icons.get_icon('fit_y'))
        btn_bestfit_waterlvl.setToolTip('Best fit the water level scale')

        btn_bestfit_time = QToolButtonNormal(icons.get_icon('fit_x'))
        btn_bestfit_time.setToolTip('Best fit the time scale')

        self.btn_page_setup = QToolButtonNormal(icons.get_icon('page_setup'))
        self.btn_page_setup.setToolTip('Show the page setup window')
        self.btn_page_setup.clicked.connect(self.page_setup_win.show)

        btn_color_pick = QToolButtonNormal(icons.get_icon('color_picker'))
        btn_color_pick.setToolTip('<p>Show a window to setup the color palette'
                                  ' used to draw the hydrograph</p.')
        btn_color_pick.clicked.connect(self.color_palette_win.show)

        self.btn_language = LangToolButton()
        self.btn_language.setToolTip(
            "Set the language of the text shown in the graph.")
        self.btn_language.sig_lang_changed.connect(self.layout_changed)
        self.btn_language.setIconSize(icons.get_iconsize('normal'))

        # ---- Zoom Panel

        btn_zoom_out = QToolButtonSmall(icons.get_icon('zoom_out'))
        btn_zoom_out.setToolTip('Zoom out (ctrl + mouse-wheel-down)')
        btn_zoom_out.clicked.connect(self.zoom_out)

        btn_zoom_in = QToolButtonSmall(icons.get_icon('zoom_in'))
        btn_zoom_in.setToolTip('Zoom in (ctrl + mouse-wheel-up)')
        btn_zoom_in.clicked.connect(self.zoom_in)

        self.zoom_disp = QSpinBox()
        self.zoom_disp.setAlignment(Qt.AlignCenter)
        self.zoom_disp.setButtonSymbols(QAbstractSpinBox.NoButtons)
        self.zoom_disp.setReadOnly(True)
        self.zoom_disp.setSuffix(' %')
        self.zoom_disp.setRange(0, 9999)
        self.zoom_disp.setValue(100)

        zoom_pan = myqt.QFrameLayout()
        zoom_pan.setSpacing(3)
        zoom_pan.addWidget(btn_zoom_out, 0, 0)
        zoom_pan.addWidget(btn_zoom_in, 0, 1)
        zoom_pan.addWidget(self.zoom_disp, 0, 2)

        # LAYOUT :

        btn_list = [
            btn_save, btn_draw, self.btn_load_layout, self.btn_save_layout,
            VSep(), btn_bestfit_waterlvl, btn_bestfit_time,
            VSep(), self.btn_page_setup, btn_color_pick, self.btn_language,
            VSep(), zoom_pan
        ]

        subgrid_toolbar = QGridLayout()
        toolbar_widget = QWidget()

        row = 0
        for col, btn in enumerate(btn_list):
            subgrid_toolbar.addWidget(btn, row, col)

        subgrid_toolbar.setSpacing(5)
        subgrid_toolbar.setContentsMargins(0, 0, 0, 0)
        subgrid_toolbar.setColumnStretch(col + 1, 100)

        toolbar_widget.setLayout(subgrid_toolbar)

        # ---- LEFT PANEL

        # SubGrid Hydrograph Frame :

        self.hydrograph = hydrograph.Hydrograph()
        self.hydrograph_scrollarea = mplFigViewer.ImageViewer()
        self.hydrograph_scrollarea.zoomChanged.connect(self.zoom_disp.setValue)

        grid_hydrograph = QGridLayout()
        grid_hydrograph.addWidget(self.hydrograph_scrollarea, 0, 0)
        grid_hydrograph.setRowStretch(0, 500)
        grid_hydrograph.setColumnStretch(0, 500)
        grid_hydrograph.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        # ASSEMBLING SubGrids :

        grid_layout = QGridLayout()
        self.grid_layout_widget = QFrame()

        row = 0
        grid_layout.addWidget(toolbar_widget, row, 0)
        row += 1
        grid_layout.addLayout(grid_hydrograph, row, 0)

        grid_layout.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        grid_layout.setSpacing(5)
        grid_layout.setColumnStretch(0, 500)
        grid_layout.setRowStretch(1, 500)

        self.grid_layout_widget.setLayout(grid_layout)

        # ---- Right Panel

        self.tabscales = self.__init_scalesTabWidget__()

        self.right_panel = myqt.QFrameLayout()
        self.right_panel.addWidget(self.dmngr, 0, 0)
        self.right_panel.addWidget(self.tabscales, 1, 0)
        self.right_panel.setRowStretch(2, 100)

        self.right_panel.setSpacing(15)

        # ---- MAIN GRID

        mainGrid = QGridLayout()

        mainGrid.addWidget(self.grid_layout_widget, 0, 0)
        mainGrid.addWidget(VSep(), 0, 1)
        mainGrid.addWidget(self.right_panel, 0, 2)

        mainGrid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)
        mainGrid.setSpacing(15)
        mainGrid.setColumnStretch(0, 500)
        mainGrid.setColumnMinimumWidth(2, 250)

        self.setLayout(mainGrid)

        # ---- EVENTS

        # Toolbox Layout :

        btn_bestfit_waterlvl.clicked.connect(self.best_fit_waterlvl)
        btn_bestfit_time.clicked.connect(self.best_fit_time)
        btn_draw.clicked.connect(self.draw_hydrograph)
        btn_save.clicked.connect(self.select_save_path)

        # Hydrograph Layout :

        self.Ptot_scale.valueChanged.connect(self.layout_changed)
        self.qweather_bin.currentIndexChanged.connect(self.layout_changed)

        # ---- Init Image

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def __init_scalesTabWidget__(self):
        class QRowLayout(QGridLayout):
            def __init__(self, items, parent=None):
                super(QRowLayout, self).__init__(parent)

                for col, item in enumerate(items):
                    self.addWidget(item, 0, col)

                self.setContentsMargins(0, 0, 0, 0)
                self.setColumnStretch(0, 100)

        # ---- Time axis properties

        # Generate the widgets :

        self.date_start_widget = QDateEdit()
        self.date_start_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_start_widget.setAlignment(Qt.AlignCenter)
        self.date_start_widget.dateChanged.connect(self.layout_changed)

        self.date_end_widget = QDateEdit()
        self.date_end_widget.setDisplayFormat('01 / MM / yyyy')
        self.date_end_widget.setAlignment(Qt.AlignCenter)
        self.date_end_widget.dateChanged.connect(self.layout_changed)

        self.time_scale_label = QComboBox()
        self.time_scale_label.setEditable(False)
        self.time_scale_label.setInsertPolicy(QComboBox.NoInsert)
        self.time_scale_label.addItems(['Month', 'Year'])
        self.time_scale_label.setCurrentIndex(0)
        self.time_scale_label.currentIndexChanged.connect(self.layout_changed)

        self.dateDispFreq_spinBox = QSpinBox()
        self.dateDispFreq_spinBox.setSingleStep(1)
        self.dateDispFreq_spinBox.setMinimum(1)
        self.dateDispFreq_spinBox.setMaximum(100)
        self.dateDispFreq_spinBox.setValue(self.hydrograph.date_labels_pattern)
        self.dateDispFreq_spinBox.setAlignment(Qt.AlignCenter)
        self.dateDispFreq_spinBox.setKeyboardTracking(False)
        self.dateDispFreq_spinBox.valueChanged.connect(self.layout_changed)

        # Setting up the layout :

        widget_time_scale = QFrame()
        widget_time_scale.setFrameStyle(0)
        grid_time_scale = QGridLayout()

        GRID = [[QLabel('From :'), self.date_start_widget],
                [QLabel('To :'), self.date_end_widget],
                [QLabel('Scale :'), self.time_scale_label],
                [QLabel('Date Disp. Pattern:'), self.dateDispFreq_spinBox]]

        for i, ROW in enumerate(GRID):
            grid_time_scale.addLayout(QRowLayout(ROW), i, 1)

        grid_time_scale.setVerticalSpacing(5)
        grid_time_scale.setContentsMargins(10, 10, 10, 10)

        widget_time_scale.setLayout(grid_time_scale)

        # ----- Water level axis properties

        # Widget :

        self.waterlvl_scale = QDoubleSpinBox()
        self.waterlvl_scale.setSingleStep(0.05)
        self.waterlvl_scale.setMinimum(0.05)
        self.waterlvl_scale.setSuffix('  m')
        self.waterlvl_scale.setAlignment(Qt.AlignCenter)
        self.waterlvl_scale.setKeyboardTracking(False)
        self.waterlvl_scale.valueChanged.connect(self.layout_changed)
        self.waterlvl_scale.setFixedWidth(100)

        self.waterlvl_max = QDoubleSpinBox()
        self.waterlvl_max.setSingleStep(0.1)
        self.waterlvl_max.setSuffix('  m')
        self.waterlvl_max.setAlignment(Qt.AlignCenter)
        self.waterlvl_max.setMinimum(-1000)
        self.waterlvl_max.setMaximum(1000)
        self.waterlvl_max.setKeyboardTracking(False)
        self.waterlvl_max.valueChanged.connect(self.layout_changed)
        self.waterlvl_max.setFixedWidth(100)

        self.NZGridWL_spinBox = QSpinBox()
        self.NZGridWL_spinBox.setSingleStep(1)
        self.NZGridWL_spinBox.setMinimum(1)
        self.NZGridWL_spinBox.setMaximum(50)
        self.NZGridWL_spinBox.setValue(self.hydrograph.NZGrid)
        self.NZGridWL_spinBox.setAlignment(Qt.AlignCenter)
        self.NZGridWL_spinBox.setKeyboardTracking(False)
        self.NZGridWL_spinBox.valueChanged.connect(self.layout_changed)
        self.NZGridWL_spinBox.setFixedWidth(100)

        self.datum_widget = QComboBox()
        self.datum_widget.addItems(['Ground Surface', 'Sea Level'])
        self.datum_widget.currentIndexChanged.connect(self.layout_changed)

        # Layout :

        subgrid_WLScale = QGridLayout()

        GRID = [[QLabel('Minimum :'), self.waterlvl_max],
                [QLabel('Scale :'), self.waterlvl_scale],
                [QLabel('Grid Divisions :'), self.NZGridWL_spinBox],
                [QLabel('Datum :'), self.datum_widget]]

        for i, ROW in enumerate(GRID):
            subgrid_WLScale.addLayout(QRowLayout(ROW), i, 1)

        subgrid_WLScale.setVerticalSpacing(5)
        subgrid_WLScale.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        WLScale_widget = QFrame()
        WLScale_widget.setFrameStyle(0)
        WLScale_widget.setLayout(subgrid_WLScale)

        # ---- Weather Axis

        # Widgets :

        self.Ptot_scale = QSpinBox()
        self.Ptot_scale.setSingleStep(5)
        self.Ptot_scale.setMinimum(5)
        self.Ptot_scale.setMaximum(500)
        self.Ptot_scale.setValue(20)
        self.Ptot_scale.setSuffix('  mm')
        self.Ptot_scale.setAlignment(Qt.AlignCenter)

        self.qweather_bin = QComboBox()
        self.qweather_bin.setEditable(False)
        self.qweather_bin.setInsertPolicy(QComboBox.NoInsert)
        self.qweather_bin.addItems(['day', 'week', 'month'])
        self.qweather_bin.setCurrentIndex(1)

        # Layout :

        layout = QGridLayout()

        GRID = [[QLabel('Precip. Scale :'), self.Ptot_scale],
                [QLabel('Resampling :'), self.qweather_bin]]

        for i, row in enumerate(GRID):
            layout.addLayout(QRowLayout(row), i, 1)

        layout.setVerticalSpacing(5)
        layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        layout.setRowStretch(i + 1, 100)

        widget_weather_scale = QFrame()
        widget_weather_scale.setFrameStyle(0)
        widget_weather_scale.setLayout(layout)

        # ---- ASSEMBLING TABS

        tabscales = QTabWidget()
        tabscales.addTab(widget_time_scale, 'Time')
        tabscales.addTab(WLScale_widget, 'Water Level')
        tabscales.addTab(widget_weather_scale, 'Weather')

        return tabscales

    @property
    def workdir(self):
        return self.dmngr.workdir

    # ---- Utilities

    def zoom_in(self):
        self.hydrograph_scrollarea.zoomIn()

    def zoom_out(self):
        self.hydrograph_scrollarea.zoomOut()

    def update_colors(self):
        self.hydrograph.update_colors()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    # ---- Datasets Handlers

    @property
    def wldset(self):
        return self.dmngr.get_current_wldset()

    @property
    def wxdset(self):
        return self.dmngr.get_current_wxdset()

    def wldset_changed(self):
        """Handle when the water level dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
            return
        else:
            wldset = self.wldset
            self.hydrograph.set_wldset(wldset)
            self.hydrograph.gluedf = self.wldset.get_glue_at(-1)

        # Load the manual measurements.

        fname = os.path.join(self.workdir, "Water Levels",
                             'waterlvl_manual_measurements')
        tmeas, wlmeas = load_waterlvl_measures(fname, wldset['Well'])
        wldset.set_wlmeas(tmeas, wlmeas)

        # Setup the layout of the hydrograph.

        layout = wldset.get_layout()
        if layout is not None:
            msg = 'Loading existing graph layout for well %s.' % wldset['Well']
            print(msg)
            self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
            self.load_graph_layout(layout)
        else:
            print('No graph layout exists for well %s.' % wldset['Well'])
            # Fit Water Level in Layout :
            self.__updateUI = False
            self.best_fit_waterlvl()
            self.best_fit_time()
            self.dmngr.set_closest_wxdset()
            self.__updateUI = True

    def wxdset_changed(self):
        """Handle when the weather dataset of the datamanager changes."""
        if self.wldset is None:
            self.clear_hydrograph()
        else:
            self.hydrograph.set_wxdset(self.wxdset)
            QCoreApplication.processEvents()
            self.draw_hydrograph()

    # ---- Draw Hydrograph Handlers

    def best_fit_waterlvl(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            WLscale, WLmin = self.hydrograph.best_fit_waterlvl()
            self.waterlvl_scale.setValue(WLscale)
            self.waterlvl_max.setValue(WLmin)

    def best_fit_time(self):
        wldset = self.dmngr.get_current_wldset()
        if wldset is not None:
            date0, date1 = self.hydrograph.best_fit_time(wldset['Time'])
            self.date_start_widget.setDate(QDate(date0[0], date0[1], date0[2]))
            self.date_end_widget.setDate(QDate(date1[0], date1[1], date1[2]))

    @QSlot()
    def mrc_wl_changed(self):
        """
        Force a redraw of the MRC water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.draw_mrc_wl()
        self.hydrograph.setup_legend()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    @QSlot(GLUEDataFrameBase)
    def glue_wl_changed(self, gluedf):
        """
        Force a redraw of the GLUE water levels after the results have
        changed for the dataset.
        """
        self.hydrograph.set_gluedf(gluedf)
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def layout_changed(self):
        """
        When an element of the graph layout is changed in the UI.
        """

        if self.__updateUI is False:
            return

        self.update_graph_layout_parameter()

        if self.hydrograph.isHydrographExists is False:
            return

        sender = self.sender()

        if sender == self.btn_language:
            self.hydrograph.draw_ylabels()
            self.hydrograph.setup_xticklabels()
            self.hydrograph.setup_legend()
        elif sender in [self.waterlvl_max, self.waterlvl_scale]:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.NZGridWL_spinBox:
            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.Ptot_scale:
            self.hydrograph.update_precip_scale()
            self.hydrograph.draw_ylabels()
        elif sender == self.datum_widget:
            yoffset = int(self.wldset['Elevation'] / self.hydrograph.WLscale)
            yoffset *= self.hydrograph.WLscale

            self.hydrograph.WLmin = (yoffset - self.hydrograph.WLmin)

            self.waterlvl_max.blockSignals(True)
            self.waterlvl_max.setValue(self.hydrograph.WLmin)
            self.waterlvl_max.blockSignals(False)

            # This is calculated so that trailing zeros in the altitude of the
            # well is not carried to the y axis labels, so that they remain a
            # int multiple of *WLscale*.

            self.hydrograph.setup_waterlvl_scale()
            self.hydrograph.draw_waterlvl()
            self.hydrograph.draw_ylabels()
        elif sender in [self.date_start_widget, self.date_end_widget]:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_figure_title()
        elif sender == self.dateDispFreq_spinBox:
            self.hydrograph.set_time_scale()
            self.hydrograph.setup_xticklabels()
        elif sender == self.page_setup_win:
            self.hydrograph.update_fig_size()
            # Implicitly call : set_margins()
            #                   draw_ylabels()
            #                   set_time_scale()
            #                   draw_figure_title
            self.hydrograph.draw_waterlvl()
            self.hydrograph.setup_legend()
        elif sender == self.qweather_bin:
            self.hydrograph.resample_bin()
            self.hydrograph.draw_weather()
            self.hydrograph.draw_ylabels()
        elif sender == self.time_scale_label:
            self.hydrograph.set_time_scale()
            self.hydrograph.draw_weather()
        else:
            print('No action for this widget yet.')

        # !!!! temporary fix until I can find a better solution !!!!

#        sender.blockSignals(True)
        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(True)

        for i in range(10):
            QCoreApplication.processEvents()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)
        for i in range(10):
            QCoreApplication.processEvents()

        if type(sender) in [QDoubleSpinBox, QSpinBox]:
            sender.setReadOnly(False)


#        sender.blockSignals(False)

    def update_graph_layout_parameter(self):

        # language :

        self.hydrograph.language = self.btn_language.language

        # Scales :

        self.hydrograph.WLmin = self.waterlvl_max.value()
        self.hydrograph.WLscale = self.waterlvl_scale.value()
        self.hydrograph.RAINscale = self.Ptot_scale.value()
        self.hydrograph.NZGrid = self.NZGridWL_spinBox.value()

        # WL Datum :

        self.hydrograph.WLdatum = self.datum_widget.currentIndex()

        # Dates :

        self.hydrograph.datemode = self.time_scale_label.currentText()

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        self.hydrograph.TIMEmin = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        self.hydrograph.TIMEmax = xldate_from_date_tuple((year, month, 1), 0)

        self.hydrograph.date_labels_pattern = self.dateDispFreq_spinBox.value()

        # Page Setup :

        self.hydrograph.fwidth = self.page_setup_win.pageSize[0]
        self.hydrograph.fheight = self.page_setup_win.pageSize[1]
        self.hydrograph.va_ratio = self.page_setup_win.va_ratio

        self.hydrograph.trend_line = self.page_setup_win.isTrendLine
        self.hydrograph.isLegend = self.page_setup_win.isLegend
        self.hydrograph.isGraphTitle = self.page_setup_win.isGraphTitle
        self.hydrograph.set_meteo_on(self.page_setup_win.is_meteo_on)
        self.hydrograph.set_glue_wl_on(self.page_setup_win.is_glue_wl_on)
        self.hydrograph.set_mrc_wl_on(self.page_setup_win.is_mrc_wl_on)
        self.hydrograph.set_figframe_lw(self.page_setup_win.figframe_lw)

        # Weather bins :

        self.hydrograph.bwidth_indx = self.qweather_bin.currentIndex()

    def clear_hydrograph(self):
        """Clear the hydrograph figure to show only a blank canvas."""
        self.hydrograph.clf()
        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

    def draw_hydrograph(self):
        if self.dmngr.wldataset_count() == 0:
            msg = 'Please import a valid water level data file first.'
            self.ConsoleSignal.emit('<font color=red>%s</font>' % msg)
            self.emit_warning(msg)
            return

        self.update_graph_layout_parameter()

        # Generate and Display Graph :

        for i in range(5):
            QCoreApplication.processEvents()

        QApplication.setOverrideCursor(Qt.WaitCursor)

        self.hydrograph.set_wldset(self.dmngr.get_current_wldset())
        self.hydrograph.set_wxdset(self.dmngr.get_current_wxdset())
        self.hydrograph.generate_hydrograph()

        self.hydrograph_scrollarea.load_mpl_figure(self.hydrograph)

        QApplication.restoreOverrideCursor()

    def select_save_path(self):
        """
        Open a dialog where the user can select a file name to save the
        hydrograph.
        """
        if self.wldset is None:
            return

        ffmat = "*.pdf;;*.svg;;*.png"
        fname = find_unique_filename(
            osp.join(self.save_fig_dir,
                     'hydrograph_%s.pdf' % self.wldset['Well']))

        fname, ftype = QFileDialog.getSaveFileName(self, "Save Figure", fname,
                                                   ffmat)
        if fname:
            ftype = ftype.replace('*', '')
            fname = fname if fname.endswith(ftype) else fname + ftype
            self.save_fig_dir = os.path.dirname(fname)

            try:
                self.save_figure(fname)
            except PermissionError:
                msg = "The file is in use by another application or user."
                QMessageBox.warning(self, 'Warning', msg, QMessageBox.Ok)
                self.select_save_path()

    def save_figure(self, fname):
        """Save the hydrograph figure in a file."""
        self.hydrograph.generate_hydrograph()
        self.hydrograph.savefig(fname)

    # ---- Graph Layout Handlers

    def load_layout_isClicked(self):
        """Handle when the button to load the graph layout is clicked."""
        if self.wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = self.wldset.get_layout()
        if layout is None:
            self.emit_warning("No graph layout exists for well %s." %
                              self.wldset['Well'])
        else:
            self.load_graph_layout(layout)

    def load_graph_layout(self, layout):
        """Load the graph layout into the GUI."""

        self.__updateUI = False

        # Scales :

        date = layout['TIMEmin']
        date = xldate_as_tuple(date, 0)
        self.date_start_widget.setDate(QDate(date[0], date[1], date[2]))

        date = layout['TIMEmax']
        date = xldate_as_tuple(date, 0)
        self.date_end_widget.setDate(QDate(date[0], date[1], date[2]))

        self.dateDispFreq_spinBox.setValue(layout['date_labels_pattern'])

        self.waterlvl_scale.setValue(layout['WLscale'])
        self.waterlvl_max.setValue(layout['WLmin'])
        self.NZGridWL_spinBox.setValue(layout['NZGrid'])
        self.Ptot_scale.setValue(layout['RAINscale'])

        x = ['mbgs', 'masl'].index(layout['WLdatum'])
        self.datum_widget.setCurrentIndex(x)

        self.qweather_bin.setCurrentIndex(layout['bwidth_indx'])
        self.time_scale_label.setCurrentIndex(
            self.time_scale_label.findText(layout['datemode']))

        # ---- Language and colors
        self.btn_language.set_language(layout['language'])
        self.color_palette_win.load_colors()

        # ---- Page Setup

        self.page_setup_win.pageSize = (layout['fwidth'], layout['fheight'])
        self.page_setup_win.va_ratio = layout['va_ratio']
        self.page_setup_win.isLegend = layout['legend_on']
        self.page_setup_win.isGraphTitle = layout['title_on']
        self.page_setup_win.isTrendLine = layout['trend_line']
        self.page_setup_win.is_meteo_on = layout['meteo_on']
        self.page_setup_win.is_glue_wl_on = layout['glue_wl_on']
        self.page_setup_win.is_mrc_wl_on = layout['mrc_wl_on']
        self.page_setup_win.figframe_lw = layout['figframe_lw']

        self.page_setup_win.legend_on.set_value(layout['legend_on'])
        self.page_setup_win.title_on.set_value(layout['title_on'])
        self.page_setup_win.wltrend_on.set_value(layout['trend_line'])
        self.page_setup_win.meteo_on.set_value(layout['meteo_on'])
        self.page_setup_win.glue_wl_on.set_value(layout['glue_wl_on'])
        self.page_setup_win.mrc_wl_on.set_value(layout['mrc_wl_on'])
        self.page_setup_win.fframe_lw_widg.setValue(layout['figframe_lw'])

        self.page_setup_win.fwidth.setValue(layout['fwidth'])
        self.page_setup_win.fheight.setValue(layout['fheight'])
        self.page_setup_win.va_ratio_spinBox.setValue(layout['va_ratio'])

        # Check if Weather Dataset :

        if layout['wxdset'] in self.dmngr.wxdsets:
            self.dmngr.set_current_wxdset(layout['wxdset'])
        else:
            self.dmngr.set_closest_wxdset()

        self.__updateUI = True

    def save_layout_isClicked(self):
        wldset = self.wldset
        if wldset is None:
            self.emit_warning(
                "Please import a valid water level data file first.")
            return

        layout = wldset.get_layout()
        if layout is not None:
            msg = ('A graph layout already exists for well %s.Do you want to'
                   ' you want to replace it?') % wldset['Well']
            reply = QMessageBox.question(self, 'Save Graph Layout', msg,
                                         QMessageBox.Yes | QMessageBox.No)
            if reply == QMessageBox.Yes:
                self.save_graph_layout()
            elif reply == QMessageBox.No:
                msg = "Graph layout not saved for well %s." % wldset['Well']
                self.ConsoleSignal.emit('<font color=black>%s' % msg)
        else:
            self.save_graph_layout()

    def save_graph_layout(self):
        """Save the graph layout in the project hdf5 file."""
        print("Saving the graph layout for well %s..." % self.wldset['Well'],
              end=" ")

        layout = {
            'WLmin': self.waterlvl_max.value(),
            'WLscale': self.waterlvl_scale.value(),
            'RAINscale': self.Ptot_scale.value(),
            'fwidth': self.page_setup_win.pageSize[0],
            'fheight': self.page_setup_win.pageSize[1],
            'va_ratio': self.page_setup_win.va_ratio,
            'NZGrid': self.NZGridWL_spinBox.value(),
            'bwidth_indx': self.qweather_bin.currentIndex(),
            'date_labels_pattern': self.dateDispFreq_spinBox.value(),
            'datemode': self.time_scale_label.currentText()
        }
        layout['wxdset'] = None if self.wxdset is None else self.wxdset.name

        year = self.date_start_widget.date().year()
        month = self.date_start_widget.date().month()
        layout['TIMEmin'] = xldate_from_date_tuple((year, month, 1), 0)

        year = self.date_end_widget.date().year()
        month = self.date_end_widget.date().month()
        layout['TIMEmax'] = xldate_from_date_tuple((year, month, 1), 0)

        if self.datum_widget.currentIndex() == 0:
            layout['WLdatum'] = 'mbgs'
        else:
            layout['WLdatum'] = 'masl'

        # ---- Page Setup

        layout['title_on'] = bool(self.page_setup_win.isGraphTitle)
        layout['legend_on'] = bool(self.page_setup_win.isLegend)
        layout['language'] = self.btn_language.language
        layout['trend_line'] = bool(self.page_setup_win.isTrendLine)
        layout['meteo_on'] = bool(self.page_setup_win.is_meteo_on)
        layout['glue_wl_on'] = bool(self.page_setup_win.is_glue_wl_on)
        layout['mrc_wl_on'] = bool(self.page_setup_win.is_mrc_wl_on)
        layout['figframe_lw'] = self.page_setup_win.figframe_lw

        # Save the colors :

        cdb = ColorsReader()
        cdb.load_colors_db()
        layout['colors'] = cdb.RGB

        # Save the layout :

        self.wldset.save_layout(layout)
        msg = 'Layout saved successfully for well %s.' % self.wldset['Well']
        self.ConsoleSignal.emit('<font color=black>%s</font>' % msg)
        print("done")
Example #4
0
class WeatherStationBrowser(QWidget):
    """
    Widget that allows the user to browse and select ECCC climate stations.
    """

    ConsoleSignal = QSignal(str)
    staListSignal = QSignal(list)

    PROV_NAME = [x[0].title() for x in PROV_NAME_ABB]
    PROV_ABB = [x[1] for x in PROV_NAME_ABB]

    def __init__(self, parent=None):
        super(WeatherStationBrowser, self).__init__(parent)
        self.stn_finder_worker = WeatherStationFinder()
        self.stn_finder_worker.sig_load_database_finished.connect(
            self.receive_load_database)
        self.stn_finder_thread = QThread()
        self.stn_finder_worker.moveToThread(self.stn_finder_thread)

        self.station_table = WeatherSationView()
        self.waitspinnerbar = WaitSpinnerBar()
        self.stn_finder_worker.sig_progress_msg.connect(
            self.waitspinnerbar.set_label)
        self.__initUI__()

        self.start_load_database()

    def __initUI__(self):
        self.setWindowTitle('Weather Stations Browser')
        self.setWindowIcon(icons.get_icon('master'))
        self.setWindowFlags(Qt.Window)

        now = datetime.now()

        # ---- Tab Widget Search

        # ---- Proximity filter groupbox

        label_Lat = QLabel('Latitude :')
        label_Lat2 = QLabel('North')

        self.lat_spinBox = QDoubleSpinBox()
        self.lat_spinBox.setAlignment(Qt.AlignCenter)
        self.lat_spinBox.setSingleStep(0.1)
        self.lat_spinBox.setValue(0)
        self.lat_spinBox.setMinimum(0)
        self.lat_spinBox.setMaximum(180)
        self.lat_spinBox.setSuffix(u' °')
        self.lat_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        label_Lon = QLabel('Longitude :')
        label_Lon2 = QLabel('West')

        self.lon_spinBox = QDoubleSpinBox()
        self.lon_spinBox.setAlignment(Qt.AlignCenter)
        self.lon_spinBox.setSingleStep(0.1)
        self.lon_spinBox.setValue(0)
        self.lon_spinBox.setMinimum(0)
        self.lon_spinBox.setMaximum(180)
        self.lon_spinBox.setSuffix(u' °')
        self.lon_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        self.radius_SpinBox = QComboBox()
        self.radius_SpinBox.addItems(['25 km', '50 km', '100 km', '200 km'])
        self.radius_SpinBox.currentIndexChanged.connect(
            self.search_filters_changed)

        prox_search_grid = QGridLayout()
        row = 0
        prox_search_grid.addWidget(label_Lat, row, 1)
        prox_search_grid.addWidget(self.lat_spinBox, row, 2)
        prox_search_grid.addWidget(label_Lat2, row, 3)
        row += 1
        prox_search_grid.addWidget(label_Lon, row, 1)
        prox_search_grid.addWidget(self.lon_spinBox, row, 2)
        prox_search_grid.addWidget(label_Lon2, row, 3)
        row += 1
        prox_search_grid.addWidget(QLabel('Search Radius :'), row, 1)
        prox_search_grid.addWidget(self.radius_SpinBox, row, 2)

        prox_search_grid.setColumnStretch(0, 100)
        prox_search_grid.setColumnStretch(4, 100)
        prox_search_grid.setRowStretch(row + 1, 100)
        prox_search_grid.setHorizontalSpacing(20)
        prox_search_grid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        self.prox_grpbox = QGroupBox("Proximity filter :")
        self.prox_grpbox.setCheckable(True)
        self.prox_grpbox.setChecked(False)
        self.prox_grpbox.toggled.connect(self.proximity_grpbox_toggled)
        self.prox_grpbox.setLayout(prox_search_grid)

        # ---- Province filter

        prov_names = ['All']
        prov_names.extend(self.PROV_NAME)
        self.prov_widg = QComboBox()
        self.prov_widg.addItems(prov_names)
        self.prov_widg.setCurrentIndex(0)
        self.prov_widg.currentIndexChanged.connect(self.search_filters_changed)

        layout = QGridLayout()
        layout.addWidget(self.prov_widg, 2, 1)
        layout.setColumnStretch(2, 100)
        layout.setVerticalSpacing(10)

        prov_grpbox = QGroupBox("Province filter :")
        prov_grpbox.setLayout(layout)

        # ---- Data availability filter

        # Number of years with data

        self.nbrYear = QSpinBox()
        self.nbrYear.setAlignment(Qt.AlignCenter)
        self.nbrYear.setSingleStep(1)
        self.nbrYear.setMinimum(0)
        self.nbrYear.setValue(3)
        self.nbrYear.valueChanged.connect(self.search_filters_changed)

        subgrid1 = QGridLayout()
        subgrid1.addWidget(self.nbrYear, 0, 0)
        subgrid1.addWidget(QLabel('years of data between'), 0, 1)

        subgrid1.setHorizontalSpacing(10)
        subgrid1.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        subgrid1.setColumnStretch(2, 100)

        # Year range

        self.minYear = QSpinBox()
        self.minYear.setAlignment(Qt.AlignCenter)
        self.minYear.setSingleStep(1)
        self.minYear.setMinimum(1840)
        self.minYear.setMaximum(now.year)
        self.minYear.setValue(1840)
        self.minYear.valueChanged.connect(self.minYear_changed)

        label_and = QLabel('and')
        label_and.setAlignment(Qt.AlignCenter)

        self.maxYear = QSpinBox()
        self.maxYear.setAlignment(Qt.AlignCenter)
        self.maxYear.setSingleStep(1)
        self.maxYear.setMinimum(1840)
        self.maxYear.setMaximum(now.year)
        self.maxYear.setValue(now.year)
        self.maxYear.valueChanged.connect(self.maxYear_changed)

        subgrid2 = QGridLayout()
        subgrid2.addWidget(self.minYear, 0, 0)
        subgrid2.addWidget(label_and, 0, 1)
        subgrid2.addWidget(self.maxYear, 0, 2)

        subgrid2.setHorizontalSpacing(10)
        subgrid2.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        subgrid2.setColumnStretch(4, 100)

        # Subgridgrid assembly

        grid = QGridLayout()

        grid.addWidget(QLabel('Search for stations with at least'), 0, 0)
        grid.addLayout(subgrid1, 1, 0)
        grid.addLayout(subgrid2, 2, 0)

        grid.setVerticalSpacing(5)
        grid.setRowStretch(0, 100)
        # grid.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        self.year_widg = QGroupBox("Data Availability filter :")
        self.year_widg.setLayout(grid)

        # ---- Toolbar

        self.btn_addSta = btn_addSta = QPushButton('Add')
        btn_addSta.setIcon(icons.get_icon('add2list'))
        btn_addSta.setIconSize(icons.get_iconsize('small'))
        btn_addSta.setToolTip('Add selected found weather stations to the '
                              'current list of weather stations.')
        btn_addSta.clicked.connect(self.btn_addSta_isClicked)

        btn_save = QPushButton('Save')
        btn_save.setIcon(icons.get_icon('save'))
        btn_save.setIconSize(icons.get_iconsize('small'))
        btn_save.setToolTip('Save current found stations info in a csv file.')
        btn_save.clicked.connect(self.btn_save_isClicked)

        self.btn_fetch = btn_fetch = QPushButton('Fetch')
        btn_fetch.setIcon(icons.get_icon('refresh'))
        btn_fetch.setIconSize(icons.get_iconsize('small'))
        btn_fetch.setToolTip("Updates the climate station database by"
                             " fetching it again from the ECCC ftp server.")
        btn_fetch.clicked.connect(self.btn_fetch_isClicked)

        toolbar_grid = QGridLayout()
        toolbar_widg = QWidget()

        for col, btn in enumerate([btn_addSta, btn_save, btn_fetch]):
            toolbar_grid.addWidget(btn, 0, col + 1)

        toolbar_grid.setColumnStretch(toolbar_grid.columnCount(), 100)
        toolbar_grid.setSpacing(5)
        toolbar_grid.setContentsMargins(0, 30, 0, 0)  # (L, T, R, B)

        toolbar_widg.setLayout(toolbar_grid)

        # ---- Left Panel

        panel_title = QLabel('<b>Weather Station Search Criteria :</b>')

        left_panel = QFrame()
        left_panel_grid = QGridLayout()

        left_panel_grid.addWidget(panel_title, 0, 0)
        left_panel_grid.addWidget(self.prox_grpbox, 1, 0)
        left_panel_grid.addWidget(prov_grpbox, 2, 0)
        left_panel_grid.addWidget(self.year_widg, 3, 0)
        left_panel_grid.setRowStretch(4, 100)
        left_panel_grid.addWidget(toolbar_widg, 5, 0)

        left_panel_grid.setVerticalSpacing(20)
        left_panel_grid.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        left_panel.setLayout(left_panel_grid)

        # ----- Main grid

        # Widgets

        vLine1 = QFrame()
        vLine1.setFrameStyle(StyleDB().VLine)

        # Grid

        main_layout = QGridLayout(self)

        main_layout.addWidget(left_panel, 0, 0)
        main_layout.addWidget(vLine1, 0, 1)
        main_layout.addWidget(self.station_table, 0, 2)
        main_layout.addWidget(self.waitspinnerbar, 0, 2)

        main_layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        main_layout.setRowStretch(0, 100)
        main_layout.setHorizontalSpacing(15)
        main_layout.setVerticalSpacing(5)
        main_layout.setColumnStretch(col, 100)

    @property
    def stationlist(self):
        return self.station_table.get_stationlist()

    @property
    def search_by(self):
        return ['proximity', 'province'][self.tab_widg.currentIndex()]

    @property
    def prov(self):
        if self.prov_widg.currentIndex() == 0:
            return self.PROV_ABB
        else:
            return self.PROV_ABB[self.prov_widg.currentIndex() - 1]

    @property
    def lat(self):
        return self.lat_spinBox.value()

    def set_lat(self, x, silent=True):
        if silent:
            self.lat_spinBox.blockSignals(True)
        self.lat_spinBox.setValue(x)
        self.lat_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def lon(self):
        return self.lon_spinBox.value()

    def set_lon(self, x, silent=True):
        if silent:
            self.lon_spinBox.blockSignals(True)
        self.lon_spinBox.setValue(x)
        self.lon_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def rad(self):
        return int(self.radius_SpinBox.currentText()[:-3])

    @property
    def prox(self):
        if self.prox_grpbox.isChecked():
            return (self.lat, -self.lon, self.rad)
        else:
            return None

    @property
    def year_min(self):
        return int(self.minYear.value())

    def set_yearmin(self, x, silent=True):
        if silent:
            self.minYear.blockSignals(True)
        self.minYear.setValue(x)
        self.minYear.blockSignals(False)

    @property
    def year_max(self):
        return int(self.maxYear.value())

    def set_yearmax(self, x, silent=True):
        if silent:
            self.maxYear.blockSignals(True)
        self.maxYear.setValue(x)
        self.maxYear.blockSignals(False)

    @property
    def nbr_of_years(self):
        return int(self.nbrYear.value())

    def set_yearnbr(self, x, silent=True):
        if silent:
            self.nbrYear.blockSignals(True)
        self.nbrYear.setValue(x)
        self.nbrYear.blockSignals(False)

    # ---- Weather Station Finder Handlers

    def start_load_database(self, force_fetch=False):
        """Start the process of loading the climate station database."""
        if self.stn_finder_thread.isRunning():
            return

        self.station_table.clear()
        self.waitspinnerbar.show()

        # Start the downloading process.
        if force_fetch:
            self.stn_finder_thread.started.connect(
                self.stn_finder_worker.fetch_database)
        else:
            self.stn_finder_thread.started.connect(
                self.stn_finder_worker.load_database)
        self.stn_finder_thread.start()

    @QSlot()
    def receive_load_database(self):
        """Handles when loading the database is finished."""
        # Disconnect the thread.
        self.stn_finder_thread.started.disconnect()

        # Quit the thread.
        self.stn_finder_thread.quit()
        waittime = 0
        while self.stn_finder_thread.isRunning():
            sleep(0.1)
            waittime += 0.1
            if waittime > 15:  # pragma: no cover
                print("Unable to quit the thread.")
                break
        # Force an update of the GUI.
        self.proximity_grpbox_toggled()
        if self.stn_finder_worker.data is None:
            self.waitspinnerbar.show_warning_icon()
        else:
            self.waitspinnerbar.hide()

    # ---- GUI handlers

    def show(self):
        super(WeatherStationBrowser, self).show()
        qr = self.frameGeometry()
        if self.parent():
            parent = self.parent()
            wp = parent.frameGeometry().width()
            hp = parent.frameGeometry().height()
            cp = parent.mapToGlobal(QPoint(wp / 2, hp / 2))
        else:
            cp = QDesktopWidget().availableGeometry().center()

        qr.moveCenter(cp)
        self.move(qr.topLeft())

    # -------------------------------------------------------------------------

    def minYear_changed(self):
        min_yr = min_yr = max(self.minYear.value(), 1840)

        now = datetime.now()
        max_yr = now.year

        self.maxYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    def maxYear_changed(self):
        min_yr = 1840

        now = datetime.now()
        max_yr = min(self.maxYear.value(), now.year)

        self.minYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    # ---- Toolbar Buttons Handlers

    def btn_save_isClicked(self):
        ddir = os.path.join(os.getcwd(), 'weather_station_list.csv')
        filename, ftype = QFileDialog().getSaveFileName(
            self, 'Save normals', ddir, '*.csv;;*.xlsx;;*.xls')
        self.station_table.save_stationlist(filename)

    def btn_addSta_isClicked(self):
        rows = self.station_table.get_checked_rows()
        if len(rows) > 0:
            staList = self.station_table.get_content4rows(rows)
            self.staListSignal.emit(staList)
            print('Selected stations sent to list')
        else:
            print('No station currently selected')

    def btn_fetch_isClicked(self):
        """Handles when the button fetch is clicked."""
        self.start_load_database(force_fetch=True)

    # ---- Search Filters Handlers

    def proximity_grpbox_toggled(self):
        """
        Set the values for the reference geo coordinates that are used in the
        WeatherSationView to calculate the proximity values and forces a
        refresh of the content of the table.
        """
        if self.prox_grpbox.isChecked():
            self.station_table.set_geocoord((self.lat, -self.lon))
        else:
            self.station_table.set_geocoord(None)
        self.search_filters_changed()

    def search_filters_changed(self):
        """
        Search for weather stations with the current filter values and forces
        an update of the station table content.
        """
        if self.stn_finder_worker.data is not None:
            stnlist = self.stn_finder_worker.get_stationlist(
                prov=self.prov,
                prox=self.prox,
                yrange=(self.year_min, self.year_max, self.nbr_of_years))
            self.station_table.populate_table(stnlist)
Example #5
0
class RelSlider:
    def __init__(self, val, cb):
        self.old_val = None
        self.fto100mul = 100
        self.cb = cb

        self.sba = QDoubleSpinBox()
        self.sba.setMinimum(-MAX_NM)
        self.sba.setMaximum(MAX_NM)
        self.sba.setDecimals(6)
        self.sba.setToolTip('Effective value [nm]')
        self.sba.setValue(val)
        self.sba_color(val)
        self.sba.setSingleStep(1.25e-3)

        self.qsr = QSlider(Qt.Horizontal)
        self.qsr.setMinimum(-100)
        self.qsr.setMaximum(100)
        self.qsr.setValue(0)
        self.qsr.setToolTip('Drag to apply relative delta [nm]')

        self.sbm = QDoubleSpinBox()
        self.sbm.setMinimum(MAX_NM / 2)
        self.sbm.setMaximum(MAX_NM)
        self.sbm.setSingleStep(1.25e-3)
        self.sbm.setToolTip('Maximum relative delta [nm]')
        self.sbm.setDecimals(2)
        self.sbm.setValue(4.0)

        def sba_cb():
            def f():
                self.block()
                val = self.sba.value()
                self.sba_color(val)
                self.cb(val)
                self.unblock()

            return f

        def qs1_cb():
            def f(t):
                self.block()

                if self.old_val is None:
                    self.qsr.setValue(0)
                    self.unblock()
                    return

                val = self.old_val + self.qsr.value() / 100 * self.sbm.value()
                self.sba.setValue(val)
                self.sba_color(val)
                self.cb(val)

                self.unblock()

            return f

        def qs1_end():
            def f():
                self.block()
                self.qsr.setValue(0)
                self.old_val = None
                self.unblock()

            return f

        def qs1_start():
            def f():
                self.block()
                self.old_val = self.get_value()
                self.unblock()

            return f

        self.sba_cb = sba_cb()
        self.qs1_cb = qs1_cb()
        self.qs1_start = qs1_start()
        self.qs1_end = qs1_end()

        self.sba.valueChanged.connect(self.sba_cb)
        self.qsr.valueChanged.connect(self.qs1_cb)
        self.qsr.sliderPressed.connect(self.qs1_start)
        self.qsr.sliderReleased.connect(self.qs1_end)

    def sba_color(self, val):
        if abs(val) > 1e-4:
            self.sba.setStyleSheet("font-weight: bold;")
        else:
            self.sba.setStyleSheet("font-weight: normal;")
        # self.sba.update()

    def block(self):
        self.sba.blockSignals(True)
        self.qsr.blockSignals(True)
        self.sbm.blockSignals(True)

    def unblock(self):
        self.sba.blockSignals(False)
        self.qsr.blockSignals(False)
        self.sbm.blockSignals(False)

    def enable(self):
        self.sba.setEnabled(True)
        self.qsr.setEnabled(True)
        self.sbm.setEnabled(True)

    def disable(self):
        self.sba.setEnabled(False)
        self.qsr.setEnabled(False)
        self.sbm.setEnabled(False)

    def fto100(self, f):
        return int((f + self.m2) / (2 * self.m2) * self.fto100mul)

    def get_value(self):
        return self.sba.value()

    def set_value(self, v):
        self.sba_color(v)
        return self.sba.setValue(v)

    def add_to_layout(self, l1, ind1, ind2):
        l1.addWidget(self.sba, ind1, ind2)
        l1.addWidget(self.qsr, ind1, ind2 + 1)
        l1.addWidget(self.sbm, ind1, ind2 + 2)

    def remove_from_layout(self, l1):
        l1.removeWidget(self.sba)
        l1.removeWidget(self.qsr)
        l1.removeWidget(self.sbm)

        self.sba.setParent(None)
        self.qsr.setParent(None)
        self.sbm.setParent(None)

        self.sba.valueChanged.disconnect(self.sba_cb)
        self.qsr.valueChanged.disconnect(self.qs1_cb)
        self.qsr.sliderPressed.disconnect(self.qs1_start)
        self.qsr.sliderReleased.disconnect(self.qs1_end)

        self.sba_cb = None
        self.qs1_cb = None
        self.qs1_start = None
        self.qs1_end = None

        self.sb = None
        self.qsr = None
Example #6
0
class QHSLAdjDlg(QFilterConfig):
    allowedtypes = ("video",)

    def _createControls(self):
        self.setWindowTitle("Configure Hue/Saturation/Luminosity")

        layout = QVBoxLayout(self)
        self.setLayout(layout)

        self.sourceWidget = QWidget(self)
        self.sourceSelection = self.createSourceControl(self.sourceWidget)
        self.sourceSelection.currentDataChanged.connect(self.setFilterSource)

        srclayout = QHBoxLayout()
        srclayout.addWidget(QLabel("Source: ", self.sourceWidget))
        srclayout.addWidget(self.sourceSelection)

        self.sourceWidget.setLayout(srclayout)
        layout.addWidget(self.sourceWidget)

        self.imageView = QImageView(self)
        layout.addWidget(self.imageView)

        self.slider = QFrameSelect(self)
        self.slider.frameSelectionChanged.connect(self.loadFrame)
        layout.addWidget(self.slider)

        hueLabel = QLabel("Hue adjustment:", self)
        self.hueSpinBox = QSpinBox(self)
        self.hueSpinBox.setMinimum(-179)
        self.hueSpinBox.setMaximum(180)
        self.hueSpinBox.valueChanged.connect(
            self._handleHueSpinBoxValueChanged)

        satLabel = QLabel("Saturation factor:", self)
        self.satSpinBox = QDoubleSpinBox(self)
        self.satSpinBox.setDecimals(2)
        self.satSpinBox.setSingleStep(0.1)
        self.satSpinBox.setMinimum(0)
        self.satSpinBox.setMaximum(10)
        self.satSpinBox.valueChanged.connect(
            self._handleSatSpinBoxValueChanged)

        lumLabel = QLabel("Luminosity factor:", self)
        self.lumSpinBox = QDoubleSpinBox(self)
        self.lumSpinBox.setDecimals(2)
        self.lumSpinBox.setMinimum(0)
        self.lumSpinBox.setMaximum(10)
        self.lumSpinBox.valueChanged.connect(
            self._handleLumSpinBoxValueChanged)

        hlayout = QHBoxLayout()

        hlayout.addStretch()
        hlayout.addWidget(hueLabel)
        hlayout.addWidget(self.hueSpinBox)

        hlayout.addStretch()
        hlayout.addWidget(satLabel)
        hlayout.addWidget(self.satSpinBox)

        hlayout.addStretch()
        hlayout.addWidget(lumLabel)
        hlayout.addWidget(self.lumSpinBox)

        hlayout.addStretch()

        layout.addLayout(hlayout)

        self._prepareDlgButtons()

    def createNewFilterInstance(self):
        return HSLAdjust()

    def _resetControls(self):
        self.hueSpinBox.blockSignals(True)
        self.hueSpinBox.setValue(self.filtercopy.dh)
        self.hueSpinBox.blockSignals(False)

        self.satSpinBox.blockSignals(True)
        self.satSpinBox.setValue(self.filtercopy.sfactor)
        self.satSpinBox.blockSignals(False)

        self.lumSpinBox.blockSignals(True)
        self.lumSpinBox.setValue(self.filtercopy.lgamma)
        self.lumSpinBox.blockSignals(False)

        if self.filtercopy.prev is not None:
            self.slider.setPtsTimeArray(self.filtercopy.prev.pts_time)
            self.loadFrame(self.slider.slider.value(), QTime())

    def _handleHueSpinBoxValueChanged(self, value):
        self.filtercopy.dh = value
        self.isModified()
        self.loadFrame(self.slider.slider.value(), None)

    def _handleSatSpinBoxValueChanged(self, value):
        self.filtercopy.sfactor = value
        self.isModified()
        self.loadFrame(self.slider.slider.value(), None)

    def _handleLumSpinBoxValueChanged(self, value):
        self.filtercopy.sfactor = value
        self.isModified()
        self.loadFrame(self.slider.slider.value(), None)

    @pyqtSlot(int, QTime)
    def loadFrame(self, n, t):
        if self.filtercopy.prev is not None:
            frame = next(self.filtercopy.iterFrames(n, whence="framenumber"))
            im = frame.to_image()
            pixmap = im.convert("RGBA").toqpixmap()
            self.imageView.setFrame(pixmap)

    def _prevChanged(self, source):
        self.slider.setPtsTimeArray(source.pts_time)
        self.loadFrame(self.slider.slider.value(),
                       self.slider.currentTime.time())
Example #7
0
class QOutputConfig(QWidget):
    contentsModified = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.trackTable = OutputTrackList(self)
        self.trackTable.contentsModified.connect(self.isModified)
        layout.addWidget(self.trackTable)

        self.titleLabel = QLabel("Title:", self)
        self.titleEdit = QLineEdit(self)
        self.titleEdit.textChanged.connect(self.setOutputTitle)

        sublayout = QHBoxLayout()
        sublayout.addWidget(self.titleLabel)
        sublayout.addWidget(self.titleEdit)
        layout.addLayout(sublayout)

        self.fileLabel = QLabel("File name:", self)
        self.fileEdit = QLineEdit(self)
        self.fileEdit.textChanged.connect(self.setOutputPath)

        self.browseBtn = QPushButton(self)
        self.browseBtn.clicked.connect(self.execBrowseDlg)
        self.browseBtn.setIcon(QIcon.fromTheme("document-open"))

        sublayout = QHBoxLayout()
        sublayout.addWidget(self.fileLabel)
        sublayout.addWidget(self.fileEdit)
        sublayout.addWidget(self.browseBtn)
        layout.addLayout(sublayout)

        sublayout = QHBoxLayout()
        self.targetSizeCheckBox = QCheckBoxMatchHeight(
            "&Target File Size (Overrides settings in video encoder)", self)

        self.targetSizeSpinBox = QDoubleSpinBox(self)
        self.targetSizeSpinBox.setMinimum(1)
        self.targetSizeSpinBox.setDecimals(3)
        self.targetSizeSpinBox.setMaximum(65536)
        self.targetSizeSpinBox.setSuffix(" MB")

        self.targetSizeCheckBox.setHeightMatch(self.targetSizeSpinBox)

        self.targetSizeCheckBox.stateChanged.connect(self.setTargetSizeMode)
        self.targetSizeSpinBox.valueChanged.connect(self.setTargetSize)

        sublayout.addWidget(self.targetSizeCheckBox)
        sublayout.addWidget(self.targetSizeSpinBox)
        sublayout.addStretch()
        layout.addLayout(sublayout)

        self.settingsBtn = QPushButton("Con&figure Container...", self)
        self.settingsBtn.setIcon(QIcon.fromTheme("preferences-other"))
        self.settingsBtn.clicked.connect(self.configureContainer)

        self.btnlayout = QHBoxLayout()
        self.btnlayout.addStretch()
        self.btnlayout.addWidget(self.settingsBtn)
        layout.addLayout(self.btnlayout)

        self.setOutputFile(None)

    def setOutputTitle(self, title):
        self.output_file.title = title
        self.isModified()

    def setOutputPath(self, path):
        self.output_file.outputpathrel = path
        self.isModified()

    def setTargetSize(self, value):
        if self.targetSizeCheckBox.checkState():
            self.output_file.targetsize = value * 1024**2
            self.isModified()

    def setTargetSizeMode(self, flag):
        self.targetSizeSpinBox.setVisible(flag)

        if flag:
            self.output_file.targetsize = (self.targetSizeSpinBox.value() *
                                           1024**2)

        else:
            self.output_file.targetsize = None

        self.isModified()

    def execBrowseDlg(self):
        exts = ' '.join(f'*{ext}' for ext in self.output_file.extensions)
        filters = (f"{self.output_file.fmtname} Files ({exts})")

        if self.output_file.config and self.output_file.config.workingdir:
            fileName = os.path.join(self.output_file.config.workingdir,
                                    self.fileEdit.text())

        else:
            fileName = self.fileEdit.text()

        fileName, _ = QFileDialog.getSaveFileName(self, "Save File", fileName,
                                                  filters)

        if fileName:
            if (self.output_file.config
                    and self.output_file.config.workingdir):
                fileName = os.path.join(self.output_file.config.workingdir,
                                        fileName)

                if not os.path.relpath(
                        fileName,
                        self.output_file.config.workingdir).startswith("../"):
                    fileName = os.path.relpath(
                        fileName, self.output_file.config.workingdir)

            self.fileEdit.setText(fileName)
            self.isModified()
            return True

        return False

    def isModified(self):
        self._modified = True
        self.contentsModified.emit()

    def _resetMinimumSize(self):
        self.targetSizeSpinBox.setMinimum(self.output_file.minimumSize() /
                                          1024**2)

    def notModified(self):
        self._modified = False

    def modified(self):
        return self._modified

    def updateOutputPath(self):
        self.fileEdit.blockSignals(True)

        if (isinstance(self.output_file, BaseWriter)
                and self.output_file.outputpathrel):
            self.fileEdit.setText(self.output_file.outputpathrel or "")

        else:
            self.fileEdit.setText("")

        self.fileEdit.blockSignals(False)

    def setOutputFile(self, output_file=None):
        self.notModified()
        self.output_file = output_file

        if output_file is not None:
            self.trackTable.setOutputFile(output_file)
            self.trackTable.contentsModified.connect(self._resetMinimumSize)

            self.titleEdit.blockSignals(True)
            self.titleEdit.setText(output_file.title or "")
            self.titleEdit.blockSignals(False)

            self.updateOutputPath()

            self.targetSizeCheckBox.blockSignals(True)
            self.targetSizeCheckBox.setTristate(False)
            self.targetSizeCheckBox.setCheckState(
                2 if output_file.targetsize is not None else 0)
            self.targetSizeCheckBox.blockSignals(False)

            self.targetSizeSpinBox.setHidden(output_file.targetsize is None)

            if output_file.targetsize:
                output_file.loadOverhead()
                self.targetSizeSpinBox.blockSignals(True)
                self._resetMinimumSize()
                self.targetSizeSpinBox.setValue(output_file.targetsize /
                                                1024**2)
                self.targetSizeSpinBox.blockSignals(False)

            self.settingsBtn.setEnabled(output_file.QtDlgClass() is not None)
            self.settingsBtn.setText(f"{output_file.fmtname} Options...")

        else:
            self.titleEdit.blockSignals(True)
            self.titleEdit.setText("")
            self.titleEdit.blockSignals(False)

            self.fileEdit.blockSignals(True)
            self.fileEdit.setText("")
            self.fileEdit.blockSignals(False)

            self.trackTable.setOutputFile(None)

            self.settingsBtn.setEnabled(False)
            self.settingsBtn.setText("Options...")

            self.targetSizeCheckBox.blockSignals(True)
            self.targetSizeCheckBox.setTristate(True)
            self.targetSizeCheckBox.setCheckState(1)
            self.targetSizeCheckBox.blockSignals(False)
            self.targetSizeSpinBox.setHidden(True)

        self.setEnabled(output_file is not None)

    def configureContainer(self):
        dlg = self.output_file.QtDlg(self)

        if dlg is not None:
            dlg.settingsApplied.connect(self.contentsModified)
            dlg.exec_()
            self._resetMinimumSize()
class AnalyizeDataWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 500, 200)
        self.setWindowTitle("Analyze Datafile")
        self.layout = QGridLayout()

        # self.rangeDict = {"Default" : [[0,1],[0,100],[0,100],
        #                                [0,100],[0,100],[0,1]]}
        self.dataAnalDict = {}
        # self.dataAnalDict['force settings'] = {}
        self.dataAnalDict['Vertical force'] = {}
        self.dataAnalDict['Lateral force'] = {}
        self.initialize_dict("Default", range_clear=True)
        self.dataAnalDict['misc settings'] = {}

        self.home()

    #call this when your are adding a new roi label. #initialize force settings in dict
    def initialize_dict(self, roi_label, range_clear=True):
        # self.dataAnalDict['force settings'][roi_label] = {}
        # self.init_force_dict(roi_label, "Vertical force")
        # self.init_force_dict(roi_label, "Lateral force")
        if range_clear == True:
            self.dataAnalDict["Vertical force"]["ranges"] = {}
            self.dataAnalDict["Lateral force"]["ranges"] = {}
        self.init_force_dict("Vertical force", roi_label)
        self.init_force_dict("Lateral force", roi_label)

    #initialize sub properties of force
    def init_force_dict(self, force, roi_label):
        self.dataAnalDict[force]["ranges"][roi_label] = {}
        self.dataAnalDict[force]["ranges"][roi_label]["Zero"] = "100,200"
        self.dataAnalDict[force]["ranges"][roi_label]["Force"] = "100,200"
        self.dataAnalDict[force]["ranges"][roi_label]["Preload"] = "100,200"
        self.dataAnalDict[force]["transform"] = {}
        self.dataAnalDict[force]["transform"]["Filter"] = False
        self.dataAnalDict[force]["transform"]["Filter window"] = 43
        self.dataAnalDict[force]["transform"]["Filter order"] = 2
        self.dataAnalDict[force]["transform"]["Cross Talk"] = 0
        self.dataAnalDict[force]["transform"]["Zero subtract"] = False
        # self.dataAnalDict['force settings'][roi_label][force] = {}
        # self.dataAnalDict['force settings'][roi_label][force]["Zero"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Force"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Preload"] = "0,10"
        # self.dataAnalDict['force settings'][roi_label][force]["Filter"] = False
        # self.dataAnalDict['force settings'][roi_label][force]["Filter window"] = 43
        # self.dataAnalDict['force settings'][roi_label][force]["Filter order"] = 2
        # self.dataAnalDict['force settings'][roi_label][force]["Cross Talk"] = 0

    def home(self):

        # self.showContactArea = QCheckBox('contact area', self) #contact area
        # self.showContactArea.setChecked(True)

        # self.showROIArea = QCheckBox('ROI area', self) #roi area
        # self.showContactLength = QCheckBox('contact length', self) #contact length
        # self.showROILength = QCheckBox('ROI length', self) #roi length
        # self.showContactNumber = QCheckBox('contact number', self) #contact number
        # self.showEcc = QCheckBox('eccentricity', self) #median eccentricity

        # self.showLateralForce = QCheckBox('lateral force', self) #lateral force
        # self.showZPiezo = QCheckBox('vertical piezo', self) #z piezo
        # self.showXPiezo = QCheckBox('lateral piezo', self) #x piezo
        # self.showAdhesion = QCheckBox('adhesion calculation', self) #adhesion/preload calc line
        # self.showFriction = QCheckBox('friction calculation', self) #friction calc lines
        # self.showStress = QCheckBox('stress', self) #stress
        # self.showDeformation = QCheckBox('deformation', self) #deformation
        # self.showTitle = QCheckBox('title', self) #plt title
        # self.showTitle.setChecked(True)
        # self.showLegend2 = QCheckBox('legend2', self) #plt title
        # self.showLegend2.setChecked(True)

        # self.showWidgets = [self.showContactArea, self.showROIArea, self.showZPiezo,
        #                     self.showXPiezo, self.showAdhesion, self.showFriction,
        #                     self.showLateralForce, self.showContactLength, self.showROILength,
        #                     self.showContactNumber, self.showEcc, self.showStress,
        #                     self.showDeformation, self.showTitle, self.showLegend2]

        # self.xAxisLabel = QLabel("<b>X Axis:</b>", self)
        # self.xAxisParam = QComboBox(self) #x axis parameter
        # self.xAxisParam.addItem("Time (s)")
        # self.xAxisParam.addItem("Vertical Position (μm)")
        # self.xAxisParam.addItem("Lateral Position (μm)")
        # self.xAxisParam.addItem("Deformation (μm)")

        # self.fontLabel = QLabel("Font Size:", self)
        # self.fontSize = QDoubleSpinBox(self) #vertical force zero range start
        # self.fontSize.setValue(12)
        # self.fontSize.setSingleStep(1)
        # self.fontSize.setRange(1, 100)

        roiChoiceLabel = QLabel("ROI Label:", self)
        self.roiChoice = QComboBox(self)  #choose ROI
        self.roiChoice.addItem("Default")
        self.roiChoice.setCurrentIndex(0)
        self.roiChoice.currentIndexChanged.connect(self.update_widgets)

        dataChoiceLabel = QLabel("Data:", self)
        self.dataChoice = QComboBox(self)  #force data
        self.dataChoiceDict = {
            "Vertical force": "Adhesion",
            "Lateral force": "Friction"
        }
        self.dataChoice.addItems(list(self.dataChoiceDict.keys()))
        self.dataChoice.setCurrentIndex(0)
        self.dataChoice.currentIndexChanged.connect(self.update_widgets)

        self.zeroBtn = QPushButton("Zero Range", self)  #zero
        self.zeroLabel = QLineEdit(self)
        self.zeroLabel.setReadOnly(True)
        self.zeroLabel.setText("100,200")

        self.forceBtn = QPushButton(
            self.dataChoiceDict[self.dataChoice.currentText()] + " Range",
            self)  #adhesion/friction
        self.forceLabel = QLineEdit(self)
        self.forceLabel.setReadOnly(True)
        self.forceLabel.setText("100,200")

        self.preloadBtn = QPushButton("Preload Range", self)  #preload
        self.preloadLabel = QLineEdit(self)
        self.preloadLabel.setReadOnly(True)
        self.preloadLabel.setText("100,200")

        # self.startLabel = QLabel("Start (%):", self)
        # self.endLabel = QLabel("End (%):", self)

        # self.zeroLabel = QLabel("Zero Range", self)
        # self.adhLabel = QLabel("Adhesion Range", self)
        # self.prl1Label = QLabel("Preload Range", self)

        # self.zeroRange1 = QDoubleSpinBox(self) #vertical force zero range start
        # self.zeroRange1.setValue(0)
        # self.zeroRange1.setSingleStep(1)
        # self.zeroRange1.setRange(0, 100)
        # self.zeroRange1.valueChanged.connect(self.update_dict)

        # self.zeroRange2 = QDoubleSpinBox(self) #vertical force zero range end
        # self.zeroRange2.setValue(1)
        # self.zeroRange2.setSingleStep(1)
        # self.zeroRange2.setRange(0, 100)
        # self.zeroRange2.valueChanged.connect(self.update_dict)

        # self.adhRange1 = QDoubleSpinBox(self) #adhesion peak range start
        # self.adhRange1.setValue(0)
        # self.adhRange1.setSingleStep(1)
        # self.adhRange1.setRange(0, 100)
        # self.adhRange1.valueChanged.connect(self.update_dict)

        # self.adhRange2 = QDoubleSpinBox(self) #adhesion peak range start
        # self.adhRange2.setValue(100)
        # self.adhRange2.setSingleStep(1)
        # self.adhRange2.setRange(0, 100)
        # self.adhRange2.valueChanged.connect(self.update_dict)

        # self.prl1Range1 = QDoubleSpinBox(self) #preload peak range start
        # self.prl1Range1.setValue(0)
        # self.prl1Range1.setSingleStep(1)
        # self.prl1Range1.setRange(0, 100)
        # self.prl1Range1.valueChanged.connect(self.update_dict)

        # self.prl1Range2 = QDoubleSpinBox(self) #preload peak range start
        # self.prl1Range2.setValue(100)
        # self.prl1Range2.setSingleStep(1)
        # self.prl1Range2.setRange(0, 100)
        # self.prl1Range2.valueChanged.connect(self.update_dict)

        # self.zero2Range1 = QDoubleSpinBox(self) #lateral force zero range start
        # self.zero2Range1.setValue(0)
        # self.zero2Range1.setSingleStep(1)
        # self.zero2Range1.setRange(0, 100)
        # self.zero2Range1.valueChanged.connect(self.update_dict)

        # self.zero2Range2 = QDoubleSpinBox(self) #lateral force zero range end
        # self.zero2Range2.setValue(1)
        # self.zero2Range2.setSingleStep(1)
        # self.zero2Range2.setRange(0, 100)
        # self.zero2Range2.valueChanged.connect(self.update_dict)

        # self.filterLatF = QCheckBox('Filter stress curve', self) #filter

        self.filter = QCheckBox('Filter', self)  #filter
        # self.filter.stateChanged.connect(self.update_dict)

        windLabel = QLabel("Window Length:", self)
        self.filter_wind = QSpinBox(self)  #filter window
        self.filter_wind.setValue(43)
        self.filter_wind.setSingleStep(20)
        self.filter_wind.setRange(3, 10001)
        # self.filter_wind.valueChanged.connect(self.filter_change)

        polyLabel = QLabel("Polynomial Order:", self)
        self.filter_poly = QSpinBox(self)  #filter polynom
        self.filter_poly.setValue(2)
        self.filter_poly.setSingleStep(1)
        self.filter_poly.setRange(1, 20000)
        # self.filter_poly.valueChanged.connect(self.update_dict)

        self.zero_subtract = QCheckBox('Zero subtract', self)  #filter

        # self.startLabel2 = QLabel("Start (%):", self)
        # self.endLabel2 = QLabel("End (%):", self)

        # self.frLabel = QLabel("Friction Range", self)
        # self.prl2Label = QLabel("Preload Range", self)
        # self.zero2Label = QLabel("Zero Range", self)

        eqLabel = QLabel("Lateral Calib. Equation (μN):", self)
        self.latCalibEq = QLineEdit(self)  #lateral force calib equation
        self.latCalibEq.setText("29181.73*x")

        noiseStepsLabel = QLabel("Noisy Steps:", self)
        noiseSteps = QLineEdit(self)  #remove first data point from steps
        noiseSteps.setText("")

        # self.legendPosLabel = QLabel("Legend:", self) #legend position
        # self.legendPos = QLineEdit(self)
        # self.legendPos.setText("upper right")

        # self.startFullLabel = QLabel("Start (%):", self)
        # self.endFullLabel = QLabel("End (%):", self)

        # self.startFull = QDoubleSpinBox(self) #plot range start
        # self.startFull.setValue(0)
        # self.startFull.setSingleStep(1)
        # self.startFull.setRange(0, 100)

        # self.endFull = QDoubleSpinBox(self) #plot range end
        # self.endFull.setValue(100)
        # self.endFull.setSingleStep(1)
        # self.endFull.setRange(0, 100)

        # self.invertLatForce = QCheckBox('Invert Lateral Force', self) #invert

        applyCrossTalk = QCheckBox('Apply Cross Talk', self)  #cross talk flag
        # self.zeroShift = QCheckBox('Shift to Zero', self) #force curve shift to zero

        # self.vertCrossTalk = QDoubleSpinBox(self) #vertical cross talk slope
        # self.vertCrossTalk.setValue(0)
        # self.vertCrossTalk.setSingleStep(0.1)
        # self.vertCrossTalk.setDecimals(4)
        # self.vertCrossTalk.setRange(-1000, 1000)
        # self.vertCTlabel = QLabel("Cross Talk (μN/μN):", self)

        # self.latCrossTalk = QDoubleSpinBox(self) #lateral cross talk slope
        # self.latCrossTalk.setValue(0)
        # self.latCrossTalk.setSingleStep(0.1)
        # self.latCrossTalk.setDecimals(4)
        # self.latCrossTalk.setRange(-1000, 1000)
        # self.latCTlabel = QLabel("Cross Talk (μN/μN):", self)

        CTlabel = QLabel("Cross Talk (μN/μN):", self)  # cross talk slope
        self.crossTalk = QDoubleSpinBox(self)
        self.crossTalk.setValue(0)
        self.crossTalk.setSingleStep(0.1)
        self.crossTalk.setDecimals(4)
        self.crossTalk.setRange(-1000, 1000)
        # self.crossTalk.valueChanged.connect(self.update_dict)

        # self.frictionRange1 = QDoubleSpinBox(self) #friction range start
        # self.frictionRange1.setValue(0)
        # self.frictionRange1.setSingleStep(1)
        # self.frictionRange1.setRange(0, 100)
        # self.frictionRange1.valueChanged.connect(self.update_dict)

        # self.frictionRange2 = QDoubleSpinBox(self) #friction range end
        # self.frictionRange2.setValue(100)
        # self.frictionRange2.setSingleStep(1)
        # self.frictionRange2.setRange(0, 100)
        # self.frictionRange2.valueChanged.connect(self.update_dict)

        # self.prl2Range1 = QDoubleSpinBox(self) #friction preload peak range start
        # self.prl2Range1.setValue(0)
        # self.prl2Range1.setSingleStep(1)
        # self.prl2Range1.setRange(0, 100)
        # self.prl2Range1.valueChanged.connect(self.update_dict)

        # self.prl2Range2 = QDoubleSpinBox(self) #friction preload peak range start
        # self.prl2Range2.setValue(100)
        # self.prl2Range2.setSingleStep(1)
        # self.prl2Range2.setRange(0, 100)
        # self.prl2Range2.valueChanged.connect(self.update_dict)

        # self.fitPosLabel = QLabel("Fit Position\n(x,y):", self) #fit eq. position
        # self.fitPos = QLineEdit(self)
        # self.fitPos.setText('0.5,0.5')

        # self.showFitEq = QCheckBox('Show Slope', self) #display equation on plot

        kBeamLabel = QLabel("Beam Spring Constant (μN/μm):",
                            self)  #beam dpring constant
        kBeam = QLineEdit(self)
        kBeam.setText('30,1')

        # deformStartLabel = QLabel("Deformation Start:", self) #contact start tolerance auto detect
        self.deformBtn = QPushButton("Deformation Range", self)  #deformation
        self.deformLabel = QLineEdit(self)
        self.deformLabel.setReadOnly(True)
        # self.deformLabel.textChanged.connect(self.updateRange)
        self.deformLabel.setText("100,200")
        # self.deformStart = QSpinBox(self)
        # self.deformStart.setValue(100)
        # self.deformStart.setSingleStep(1)
        # self.deformStart.setRange(0, 10000)

        # self.dataAnalDict['misc'] = {}
        self.dataAnalDict['misc settings']['apply cross talk'] = applyCrossTalk
        self.dataAnalDict['misc settings']['noise steps'] = noiseSteps
        self.dataAnalDict['misc settings'][
            'deformation range'] = self.deformLabel
        self.dataAnalDict['misc settings']['beam spring constant'] = kBeam

        self.okBtn = QPushButton("OK", self)  #Close window

        self.updateBtn = QPushButton("Update", self)  #Update

        #update dictionary on widget value change
        self.zeroLabel.textChanged.connect(self.update_dict)
        self.forceLabel.textChanged.connect(self.update_dict)
        self.preloadLabel.textChanged.connect(self.update_dict)
        self.filter.stateChanged.connect(self.update_dict)
        self.filter_wind.valueChanged.connect(self.filter_change)
        self.filter_poly.valueChanged.connect(self.update_dict)
        self.zero_subtract.stateChanged.connect(self.update_dict)
        self.crossTalk.valueChanged.connect(self.update_dict)
        self.zeroLabel.textChanged.connect(self.update_dict)
        self.forceLabel.textChanged.connect(self.update_dict)
        self.preloadLabel.textChanged.connect(self.update_dict)

        # self.zeroGroupBox = QGroupBox("Configure Vertical Force")
        # filterGroupBox = QGroupBox("Configure Plot")
        # flagGroupBox = QGroupBox("Show")
        # self.latCalibGroupBox = QGroupBox("Configure Lateral Force")
        # self.fittingGroupBox = QGroupBox("Fit Data")

        forceGroupBox = QGroupBox("Force")
        miscGroupBox = QGroupBox("Misc")
        buttonGroupBox = QGroupBox()

        forceGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        miscGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.zeroGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # filterGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # flagGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.latCalibGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.fittingGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.fittingGroupBox.setCheckable(True)
        # self.fittingGroupBox.setChecked(False)

        # self.layout.addWidget(self.roiChoice, 0, 0, 1, 2)
        # self.layout.addWidget(self.zeroGroupBox, 1, 0)
        # self.layout.addWidget(filterGroupBox, 2, 1)
        # self.layout.addWidget(flagGroupBox, 2, 0)
        # self.layout.addWidget(self.latCalibGroupBox, 1, 1)
        # self.layout.addWidget(self.fittingGroupBox, 3, 0)
        self.layout.addWidget(forceGroupBox, 0, 0)
        self.layout.addWidget(miscGroupBox, 1, 0)
        self.layout.addWidget(buttonGroupBox, 2, 0)

        self.setLayout(self.layout)

        buttonVbox = QGridLayout()
        buttonGroupBox.setLayout(buttonVbox)
        buttonVbox.addWidget(self.updateBtn, 0, 0)
        buttonVbox.addWidget(self.okBtn, 0, 1)

        forceLayout = QGridLayout()
        forceGroupBox.setLayout(forceLayout)
        forceLayout.addWidget(roiChoiceLabel, 0, 0, 1, 1)
        forceLayout.addWidget(self.roiChoice, 0, 1, 1, 1)
        forceLayout.addWidget(dataChoiceLabel, 0, 2, 1, 1)
        forceLayout.addWidget(self.dataChoice, 0, 3, 1, 1)
        forceLayout.addWidget(self.zeroBtn, 1, 0, 1, 1)
        forceLayout.addWidget(self.zeroLabel, 1, 1, 1, 1)
        forceLayout.addWidget(self.forceBtn, 2, 0, 1, 1)
        forceLayout.addWidget(self.forceLabel, 2, 1, 1, 1)
        forceLayout.addWidget(self.preloadBtn, 3, 0, 1, 1)
        forceLayout.addWidget(self.preloadLabel, 3, 1, 1, 1)
        forceLayout.addWidget(self.filter, 1, 2, 1, 2)
        forceLayout.addWidget(windLabel, 2, 2, 1, 1)
        forceLayout.addWidget(self.filter_wind, 2, 3, 1, 1)
        forceLayout.addWidget(polyLabel, 3, 2, 1, 1)
        forceLayout.addWidget(self.filter_poly, 3, 3, 1, 1)
        forceLayout.addWidget(self.zero_subtract, 4, 2, 1, 2)
        forceLayout.addWidget(CTlabel, 4, 0, 1, 1)
        forceLayout.addWidget(self.crossTalk, 4, 1, 1, 1)

        miscLayout = QGridLayout()
        miscGroupBox.setLayout(miscLayout)
        miscLayout.addWidget(applyCrossTalk, 0, 0, 1, 2)
        miscLayout.addWidget(self.deformBtn, 1, 0, 1, 1)
        miscLayout.addWidget(self.deformLabel, 1, 1, 1, 1)
        miscLayout.addWidget(noiseStepsLabel, 0, 2, 1, 1)
        miscLayout.addWidget(noiseSteps, 0, 3, 1, 1)
        miscLayout.addWidget(kBeamLabel, 1, 2, 1, 1)
        miscLayout.addWidget(kBeam, 1, 3, 1, 1)
        miscLayout.addWidget(eqLabel, 2, 0, 1, 1)  #remove
        miscLayout.addWidget(self.latCalibEq, 2, 1, 1, 1)  #remove
        # miscLayout.addWidget(self.zeroShift, 2, 2, 1, 2) #remove

        # zeroVbox = QGridLayout()
        # self.zeroGroupBox.setLayout(zeroVbox)
        # zeroVbox.addWidget(self.zeroLabel, 0, 1, 1, 1)
        # zeroVbox.addWidget(self.adhLabel, 0, 2, 1, 1)
        # zeroVbox.addWidget(self.prl1Label, 0, 3, 1, 1)
        # zeroVbox.addWidget(self.startLabel, 1, 0, 1, 1)
        # zeroVbox.addWidget(self.endLabel, 2, 0, 1, 1)
        # zeroVbox.addWidget(self.zeroRange1, 1, 1, 1, 1)
        # zeroVbox.addWidget(self.zeroRange2, 2, 1, 1, 1)
        # zeroVbox.addWidget(self.adhRange1, 1, 2, 1, 1)
        # zeroVbox.addWidget(self.adhRange2, 2, 2, 1, 1)
        # zeroVbox.addWidget(self.prl1Range1, 1, 3, 1, 1)
        # zeroVbox.addWidget(self.prl1Range2, 2, 3, 1, 1)
        # zeroVbox.addWidget(self.vertCTlabel, 3, 0, 1, 1)
        # zeroVbox.addWidget(self.vertCrossTalk, 3, 1, 1, 1)

        # filterVbox = QGridLayout()
        # filterGroupBox.setLayout(filterVbox)
        # filterVbox.addWidget(self.filterLatF, 1, 0, 1, 2)
        # filterVbox.addWidget(self.windLabel, 2, 0, 1, 1)
        # filterVbox.addWidget(self.filter_wind, 2, 1, 1, 1)
        # filterVbox.addWidget(self.polyLabel, 3, 0, 1, 1)
        # filterVbox.addWidget(self.filter_poly, 3, 1, 1, 1)
        # # filterVbox.addWidget(self.fontLabel, 2, 2, 1, 1)
        # # filterVbox.addWidget(self.fontSize, 2, 3, 1, 1)
        # filterVbox.addWidget(self.eqLabel, 3, 2, 1, 1)
        # filterVbox.addWidget(self.latCalibEq, 3, 3, 1, 1)
        # # filterVbox.addWidget(self.invertLatForce, 0, 2, 1, 2)
        # filterVbox.addWidget(self.zeroShift, 0, 0, 1, 1)
        # filterVbox.addWidget(self.applyCrossTalk, 1, 2, 1, 2)
        # # filterVbox.addWidget(self.xAxisLabel, 0, 3, 1, 1)
        # # filterVbox.addWidget(self.xAxisParam, 1, 3, 1, 1)
        # filterVbox.addWidget(self.noiseStepsLabel, 4, 2, 1, 1)
        # filterVbox.addWidget(self.noiseSteps, 5, 2, 1, 1)
        # # filterVbox.addWidget(self.legendPosLabel, 4, 3, 1, 1)
        # # filterVbox.addWidget(self.legendPos, 5, 3, 1, 1)
        # # filterVbox.addWidget(self.startFullLabel, 4, 0, 1, 1)
        # # filterVbox.addWidget(self.endFullLabel, 5, 0, 1, 1)
        # # filterVbox.addWidget(self.startFull, 4, 1, 1, 1)
        # # filterVbox.addWidget(self.endFull, 5, 1, 1, 1)
        # filterVbox.addWidget(self.kBeamLabel, 6, 2, 1, 1)
        # filterVbox.addWidget(self.kBeam, 6, 3, 1, 1)
        # filterVbox.addWidget(self.deformStartLabel, 6, 0, 1, 1)
        # filterVbox.addWidget(self.deformStart, 6, 1, 1, 1)

        # flagVbox = QGridLayout()
        # flagGroupBox.setLayout(flagVbox)
        # flagVbox.addWidget(self.showContactArea, 0, 0)
        # flagVbox.addWidget(self.showROIArea, 0, 1)
        # flagVbox.addWidget(self.showZPiezo, 0, 2)
        # flagVbox.addWidget(self.showXPiezo, 1, 0)
        # flagVbox.addWidget(self.showAdhesion, 1, 1)
        # flagVbox.addWidget(self.showFriction, 1, 2)
        # flagVbox.addWidget(self.showLateralForce, 2, 0)
        # flagVbox.addWidget(self.showContactLength, 2, 1)
        # flagVbox.addWidget(self.showROILength, 2, 2)
        # flagVbox.addWidget(self.showContactNumber, 3, 0)
        # flagVbox.addWidget(self.showEcc, 3, 1)
        # flagVbox.addWidget(self.showStress, 3, 2)
        # flagVbox.addWidget(self.showDeformation, 4, 0)
        # flagVbox.addWidget(self.showTitle, 4, 1)
        # flagVbox.addWidget(self.showLegend2, 4, 2)

        # lastCalibVbox = QGridLayout()
        # self.latCalibGroupBox.setLayout(lastCalibVbox)
        # lastCalibVbox.addWidget(self.frLabel, 0, 1, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Label, 0, 2, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Label, 0, 3, 1, 1)
        # lastCalibVbox.addWidget(self.startLabel2, 1, 0, 1, 1)
        # lastCalibVbox.addWidget(self.frictionRange1, 1, 1, 1, 1)
        # lastCalibVbox.addWidget(self.endLabel2, 2, 0, 1, 1)
        # lastCalibVbox.addWidget(self.frictionRange2, 2, 1, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Range1, 1, 2, 1, 1)
        # lastCalibVbox.addWidget(self.prl2Range2, 2, 2, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Range1, 1, 3, 1, 1)
        # lastCalibVbox.addWidget(self.zero2Range2, 2, 3, 1, 1)
        # lastCalibVbox.addWidget(self.latCTlabel, 3, 0, 1, 1)
        # lastCalibVbox.addWidget(self.latCrossTalk, 3, 1, 1, 1)

        # fittingVbox = QGridLayout()
        # self.fittingGroupBox.setLayout(fittingVbox)
        # fittingVbox.addWidget(self.startFitLabel, 0, 0, 1, 1)
        # fittingVbox.addWidget(self.endFitLabel, 1, 0, 1, 1)
        # fittingVbox.addWidget(self.fitStart, 0, 1, 1, 1)
        # fittingVbox.addWidget(self.fitStop, 1, 1, 1, 1)
        # fittingVbox.addWidget(self.xFitLabel, 0, 2, 1, 1)
        # fittingVbox.addWidget(self.yFitLabel, 1, 2, 1, 1)
        # fittingVbox.addWidget(self.xFit, 0, 3, 1, 1)
        # fittingVbox.addWidget(self.yFit, 1, 3, 1, 1)
        # fittingVbox.addWidget(self.fitPosLabel, 0, 4, 1, 1)
        # fittingVbox.addWidget(self.fitPos, 0, 5, 1, 1)
        # fittingVbox.addWidget(self.showFitEq, 1, 4, 1, 2)

    def filter_change(self):
        if self.filter_wind.value() % 2 == 0:  #make sure its odd
            self.filter_wind.blockSignals(True)
            self.filter_wind.setValue(self.filter_wind.value() + 1)
            self.filter_wind.blockSignals(False)
        self.update_dict()

    # def update_range(self):
    #     key = self.roiChoice.currentText()
    #     if key not in self.rangeDict.keys():
    #         key = "Default"

    #     self.zeroRange1.blockSignals(True)
    #     self.zeroRange1.setValue(self.rangeDict[key][0][0])
    #     self.zeroRange1.blockSignals(False)
    #     self.zeroRange2.blockSignals(True)
    #     self.zeroRange2.setValue(self.rangeDict[key][0][1])
    #     self.zeroRange2.blockSignals(False)
    #     self.adhRange1.blockSignals(True)
    #     self.adhRange1.setValue(self.rangeDict[key][1][0])
    #     self.adhRange1.blockSignals(False)
    #     self.adhRange2.blockSignals(True)
    #     self.adhRange2.setValue(self.rangeDict[key][1][1])
    #     self.adhRange2.blockSignals(False)
    #     self.prl1Range1.blockSignals(True)
    #     self.prl1Range1.setValue(self.rangeDict[key][2][0])
    #     self.prl1Range1.blockSignals(False)
    #     self.prl1Range2.blockSignals(True)
    #     self.prl1Range2.setValue(self.rangeDict[key][2][1])
    #     self.prl1Range2.blockSignals(False)
    #     self.frictionRange1.blockSignals(True)
    #     self.frictionRange1.setValue(self.rangeDict[key][3][0])
    #     self.frictionRange1.blockSignals(False)
    #     self.frictionRange2.blockSignals(True)
    #     self.frictionRange2.setValue(self.rangeDict[key][3][1])
    #     self.frictionRange2.blockSignals(False)
    #     self.prl2Range1.blockSignals(True)
    #     self.prl2Range1.setValue(self.rangeDict[key][4][0])
    #     self.prl2Range1.blockSignals(False)
    #     self.prl2Range2.blockSignals(True)
    #     self.prl2Range2.setValue(self.rangeDict[key][4][1])
    #     self.prl2Range2.blockSignals(False)
    #     self.zero2Range1.blockSignals(True)
    #     self.zero2Range1.setValue(self.rangeDict[key][5][0])
    #     self.zero2Range1.blockSignals(False)
    #     self.zero2Range2.blockSignals(True)
    #     self.zero2Range2.setValue(self.rangeDict[key][5][1])
    #     self.zero2Range2.blockSignals(False)

    def update_widgets(self):
        self.forceBtn.setText(
            self.dataChoiceDict[self.dataChoice.currentText()] + " Range")
        # range_dict = self.dataAnalDict['force settings'][self.roiChoice.currentText()][self.dataChoice.currentText()]
        range_dict = self.dataAnalDict[self.dataChoice.currentText(
        )]["ranges"][self.roiChoice.currentText()]
        transform_dict = self.dataAnalDict[
            self.dataChoice.currentText()]["transform"]
        self.zeroLabel.blockSignals(True)
        self.zeroLabel.setText(range_dict["Zero"])
        self.zeroLabel.blockSignals(False)
        self.forceLabel.blockSignals(True)
        self.forceLabel.setText(range_dict["Force"])
        self.forceLabel.blockSignals(False)
        self.preloadLabel.blockSignals(True)
        self.preloadLabel.setText(range_dict["Preload"])
        self.preloadLabel.blockSignals(False)
        self.filter.blockSignals(True)
        self.filter.setChecked(transform_dict["Filter"])
        self.filter.blockSignals(False)
        self.filter_wind.blockSignals(True)
        self.filter_wind.setValue(transform_dict["Filter window"])
        self.filter_wind.blockSignals(False)
        self.filter_poly.blockSignals(True)
        self.filter_poly.setValue(transform_dict["Filter order"])
        self.filter_poly.blockSignals(False)
        self.zero_subtract.blockSignals(True)
        self.zero_subtract.setChecked(transform_dict["Zero subtract"])
        self.zero_subtract.blockSignals(False)
        self.crossTalk.blockSignals(True)
        self.crossTalk.setValue(transform_dict["Cross Talk"])
        self.crossTalk.blockSignals(False)

    def update_dict(self):
        # range_dict = self.dataAnalDict['force settings'][self.roiChoice.currentText()][self.dataChoice.currentText()]
        range_dict = self.dataAnalDict[self.dataChoice.currentText(
        )]["ranges"][self.roiChoice.currentText()]
        transform_dict = self.dataAnalDict[
            self.dataChoice.currentText()]["transform"]
        range_dict["Zero"] = self.zeroLabel.text()
        range_dict["Force"] = self.forceLabel.text()
        range_dict["Preload"] = self.preloadLabel.text()
        transform_dict["Filter"] = self.filter.isChecked()
        transform_dict["Filter window"] = self.filter_wind.value()
        transform_dict["Filter order"] = self.filter_poly.value()
        transform_dict["Zero subtract"] = self.zero_subtract.isChecked()
        transform_dict["Cross Talk"] = self.crossTalk.value()
        # self.rangeDict[self.roiChoice.currentText()] = [[self.zeroRange1.value(),
        #                                                    self.zeroRange2.value()],
        #                                                 [self.adhRange1.value(),
        #                                                    self.adhRange2.value()],
        #                                                 [self.prl1Range1.value(),
        #                                                    self.prl1Range2.value()],
        #                                                 [self.frictionRange1.value(),
        #                                                    self.frictionRange2.value()],
        #                                                 [self.prl2Range1.value(),
        #                                                  self.prl2Range2.value()],
        #                                                 [self.zero2Range1.value(),
        #                                                    self.zero2Range2.value()]]
        logging.debug('%s', self.dataAnalDict)

    def show_window(self):  #show window
        # self.update_range()
        self.update_dict()
        self.show()
class ConfigPlotWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(100, 100, 1000, 200)
        self.setWindowTitle("Configure Plot")
        self.layout = QGridLayout()
        self.home()
        self.rangeDict = {
            "Default": [[0, 1], [0, 100], [0, 100], [0, 100], [0, 100], [0, 1]]
        }

    def home(self):

        self.showContactArea = QCheckBox('contact area', self)  #contact area
        self.showContactArea.setChecked(True)

        self.showROIArea = QCheckBox('ROI area', self)  #roi area
        self.showContactLength = QCheckBox('contact length',
                                           self)  #contact length
        self.showROILength = QCheckBox('ROI length', self)  #roi length
        self.showContactNumber = QCheckBox('contact number',
                                           self)  #contact number
        self.showEcc = QCheckBox('eccentricity', self)  #median eccentricity

        self.showLateralForce = QCheckBox('lateral force',
                                          self)  #lateral force
        self.showZPiezo = QCheckBox('vertical piezo', self)  #z piezo
        self.showXPiezo = QCheckBox('lateral piezo', self)  #x piezo
        self.showAdhesion = QCheckBox('adhesion calculation',
                                      self)  #adhesion/preload calc line
        self.showFriction = QCheckBox('friction calculation',
                                      self)  #friction calc lines
        self.showStress = QCheckBox('stress', self)  #stress
        self.showDeformation = QCheckBox('deformation', self)  #deformation
        self.showTitle = QCheckBox('title', self)  #plt title
        self.showTitle.setChecked(True)
        self.showLegend2 = QCheckBox('legend2', self)  #plt title
        self.showLegend2.setChecked(True)

        self.showWidgets = [
            self.showContactArea, self.showROIArea, self.showZPiezo,
            self.showXPiezo, self.showAdhesion, self.showFriction,
            self.showLateralForce, self.showContactLength, self.showROILength,
            self.showContactNumber, self.showEcc, self.showStress,
            self.showDeformation, self.showTitle, self.showLegend2
        ]

        self.xAxisLabel = QLabel("<b>X Axis:</b>", self)
        self.xAxisParam = QComboBox(self)  #x axis parameter
        self.xAxisParam.addItem("Time (s)")
        self.xAxisParam.addItem("Vertical Position (μm)")
        self.xAxisParam.addItem("Lateral Position (μm)")
        self.xAxisParam.addItem("Deformation (μm)")

        self.fontLabel = QLabel("Font Size:", self)
        self.fontSize = QDoubleSpinBox(self)  #vertical force zero range start
        self.fontSize.setValue(12)
        self.fontSize.setSingleStep(1)
        self.fontSize.setRange(1, 100)

        self.roiChoice = QComboBox(self)  #choose ROI
        self.roiChoice.addItem("Default")
        self.roiChoice.setCurrentIndex(0)
        self.roiChoice.currentIndexChanged.connect(self.update_range)

        self.startLabel = QLabel("Start (%):", self)
        self.endLabel = QLabel("End (%):", self)

        self.zeroLabel = QLabel("Zero Range", self)
        self.adhLabel = QLabel("Adhesion Range", self)
        self.prl1Label = QLabel("Preload Range", self)

        self.zeroRange1 = QDoubleSpinBox(
            self)  #vertical force zero range start
        self.zeroRange1.setValue(0)
        self.zeroRange1.setSingleStep(1)
        self.zeroRange1.setRange(0, 100)
        self.zeroRange1.valueChanged.connect(self.update_dict)

        self.zeroRange2 = QDoubleSpinBox(self)  #vertical force zero range end
        self.zeroRange2.setValue(1)
        self.zeroRange2.setSingleStep(1)
        self.zeroRange2.setRange(0, 100)
        self.zeroRange2.valueChanged.connect(self.update_dict)

        self.adhRange1 = QDoubleSpinBox(self)  #adhesion peak range start
        self.adhRange1.setValue(0)
        self.adhRange1.setSingleStep(1)
        self.adhRange1.setRange(0, 100)
        self.adhRange1.valueChanged.connect(self.update_dict)

        self.adhRange2 = QDoubleSpinBox(self)  #adhesion peak range start
        self.adhRange2.setValue(100)
        self.adhRange2.setSingleStep(1)
        self.adhRange2.setRange(0, 100)
        self.adhRange2.valueChanged.connect(self.update_dict)

        self.prl1Range1 = QDoubleSpinBox(self)  #preload peak range start
        self.prl1Range1.setValue(0)
        self.prl1Range1.setSingleStep(1)
        self.prl1Range1.setRange(0, 100)
        self.prl1Range1.valueChanged.connect(self.update_dict)

        self.prl1Range2 = QDoubleSpinBox(self)  #preload peak range start
        self.prl1Range2.setValue(100)
        self.prl1Range2.setSingleStep(1)
        self.prl1Range2.setRange(0, 100)
        self.prl1Range2.valueChanged.connect(self.update_dict)

        self.zero2Range1 = QDoubleSpinBox(
            self)  #lateral force zero range start
        self.zero2Range1.setValue(0)
        self.zero2Range1.setSingleStep(1)
        self.zero2Range1.setRange(0, 100)
        self.zero2Range1.valueChanged.connect(self.update_dict)

        self.zero2Range2 = QDoubleSpinBox(self)  #lateral force zero range end
        self.zero2Range2.setValue(1)
        self.zero2Range2.setSingleStep(1)
        self.zero2Range2.setRange(0, 100)
        self.zero2Range2.valueChanged.connect(self.update_dict)

        self.filterLatF = QCheckBox('Filter stress curve', self)  #filter

        self.filter_wind = QSpinBox(self)  #filter window
        self.filter_wind.setValue(43)
        self.filter_wind.setSingleStep(20)
        self.filter_wind.setRange(3, 10001)
        self.filter_wind.valueChanged.connect(self.filter_change)
        self.windLabel = QLabel("Window Length:", self)

        self.filter_poly = QSpinBox(self)  #filter polynom
        self.filter_poly.setValue(2)
        self.filter_poly.setSingleStep(1)
        self.filter_poly.setRange(1, 20000)
        self.polyLabel = QLabel("Polynomial Order:", self)

        self.startLabel2 = QLabel("Start (%):", self)
        self.endLabel2 = QLabel("End (%):", self)

        self.frLabel = QLabel("Friction Range", self)
        self.prl2Label = QLabel("Preload Range", self)
        self.zero2Label = QLabel("Zero Range", self)

        self.eqLabel = QLabel("Lateral Calib. Equation (μN):", self)
        self.latCalibEq = QLineEdit(self)  #lateral force calib equation
        self.latCalibEq.setText("29181.73*x")

        self.noiseStepsLabel = QLabel("Noisy Steps:", self)
        self.noiseSteps = QLineEdit(self)  #remove first data point from steps
        self.noiseSteps.setText("")

        self.legendPosLabel = QLabel("Legend:", self)  #legend position
        self.legendPos = QLineEdit(self)
        self.legendPos.setText("upper right")

        self.startFullLabel = QLabel("Start (%):", self)
        self.endFullLabel = QLabel("End (%):", self)

        self.startFull = QDoubleSpinBox(self)  #plot range start
        self.startFull.setValue(0)
        self.startFull.setSingleStep(1)
        self.startFull.setRange(0, 100)

        self.endFull = QDoubleSpinBox(self)  #plot range end
        self.endFull.setValue(100)
        self.endFull.setSingleStep(1)
        self.endFull.setRange(0, 100)

        self.invertLatForce = QCheckBox('Invert Lateral Force', self)  #invert

        self.applyCrossTalk = QCheckBox('Apply Cross Talk',
                                        self)  #cross talk flag
        self.zeroShift = QCheckBox('Shift to Zero',
                                   self)  #force curve shift to zero

        self.vertCrossTalk = QDoubleSpinBox(self)  #vertical cross talk slope
        self.vertCrossTalk.setValue(0)
        self.vertCrossTalk.setSingleStep(0.1)
        self.vertCrossTalk.setDecimals(4)
        self.vertCrossTalk.setRange(-1000, 1000)
        self.vertCTlabel = QLabel("Cross Talk (μN/μN):", self)

        self.latCrossTalk = QDoubleSpinBox(self)  #lateral cross talk slope
        self.latCrossTalk.setValue(0)
        self.latCrossTalk.setSingleStep(0.1)
        self.latCrossTalk.setDecimals(4)
        self.latCrossTalk.setRange(-1000, 1000)
        self.latCTlabel = QLabel("Cross Talk (μN/μN):", self)

        self.frictionRange1 = QDoubleSpinBox(self)  #friction range start
        self.frictionRange1.setValue(0)
        self.frictionRange1.setSingleStep(1)
        self.frictionRange1.setRange(0, 100)
        self.frictionRange1.valueChanged.connect(self.update_dict)

        self.frictionRange2 = QDoubleSpinBox(self)  #friction range end
        self.frictionRange2.setValue(100)
        self.frictionRange2.setSingleStep(1)
        self.frictionRange2.setRange(0, 100)
        self.frictionRange2.valueChanged.connect(self.update_dict)

        self.prl2Range1 = QDoubleSpinBox(
            self)  #friction preload peak range start
        self.prl2Range1.setValue(0)
        self.prl2Range1.setSingleStep(1)
        self.prl2Range1.setRange(0, 100)
        self.prl2Range1.valueChanged.connect(self.update_dict)

        self.prl2Range2 = QDoubleSpinBox(
            self)  #friction preload peak range start
        self.prl2Range2.setValue(100)
        self.prl2Range2.setSingleStep(1)
        self.prl2Range2.setRange(0, 100)
        self.prl2Range2.valueChanged.connect(self.update_dict)

        # self.startFitLabel = QLabel("Start (%):", self)
        # self.endFitLabel = QLabel("End (%):", self)

        # self.fitStart = QDoubleSpinBox(self) #fitting range start
        # self.fitStart.setValue(0)
        # self.fitStart.setSingleStep(1)
        # self.fitStart.setRange(0, 100)

        # self.fitStop = QDoubleSpinBox(self) #fitting range end
        # self.fitStop.setValue(100)
        # self.fitStop.setSingleStep(1)
        # self.fitStop.setRange(0, 100)

        # self.xFitLabel = QLabel("X Parameter:", self)
        # self.yFitLabel = QLabel("Y Parameter:", self)

        # self.xFit = QComboBox(self) #x param
        # self.xFit.addItems(['Deformation (μm)',
        #                     'Vertical Position (μm)',
        #                     'Lateral Position (μm)',
        #                     'Time (s)'])
        # self.xFit.setCurrentIndex(0)

        # self.yFit = QComboBox(self) #x param
        # self.yFit.addItems(['Vertical Force (μN)', 'Lateral Force (μN)'])
        # self.yFit.setCurrentIndex(0)

        self.fitPosLabel = QLabel("Fit Position\n(x,y):",
                                  self)  #fit eq. position
        self.fitPos = QLineEdit(self)
        self.fitPos.setText('0.5,0.5')

        self.showFitEq = QCheckBox('Show Slope',
                                   self)  #display equation on plot

        self.kBeamLabel = QLabel("Beam Spring Constant (μN/μm):",
                                 self)  #beam dpring constant
        self.kBeam = QLineEdit(self)
        self.kBeam.setText('30,1')

        self.deformStartLabel = QLabel(
            "Deformation Start:", self)  #contact start tolerance auto detect
        self.deformStart = QSpinBox(self)
        self.deformStart.setValue(100)
        self.deformStart.setSingleStep(1)
        self.deformStart.setRange(0, 10000)

        self.okBtn = QPushButton("OK", self)  #Close window

        self.updateBtn = QPushButton("Update", self)  #Update

        self.zeroGroupBox = QGroupBox("Configure Vertical Force")
        filterGroupBox = QGroupBox("Configure Plot")
        flagGroupBox = QGroupBox("Show")
        self.latCalibGroupBox = QGroupBox("Configure Lateral Force")
        self.fittingGroupBox = QGroupBox("Fit Data")
        buttonGroupBox = QGroupBox()

        self.zeroGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        filterGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        flagGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        self.latCalibGroupBox.setStyleSheet(
            "QGroupBox { font-weight: bold; } ")
        self.fittingGroupBox.setStyleSheet("QGroupBox { font-weight: bold; } ")
        # self.fittingGroupBox.setCheckable(True)
        # self.fittingGroupBox.setChecked(False)

        self.layout.addWidget(self.roiChoice, 0, 0, 1, 2)
        self.layout.addWidget(self.zeroGroupBox, 1, 0)
        self.layout.addWidget(filterGroupBox, 2, 1)
        self.layout.addWidget(flagGroupBox, 2, 0)
        self.layout.addWidget(self.latCalibGroupBox, 1, 1)
        self.layout.addWidget(self.fittingGroupBox, 3, 0)
        self.layout.addWidget(buttonGroupBox, 3, 1)

        self.setLayout(self.layout)

        buttonVbox = QGridLayout()
        buttonGroupBox.setLayout(buttonVbox)
        buttonVbox.addWidget(self.updateBtn, 0, 0)
        buttonVbox.addWidget(self.okBtn, 0, 1)

        zeroVbox = QGridLayout()
        self.zeroGroupBox.setLayout(zeroVbox)
        zeroVbox.addWidget(self.zeroLabel, 0, 1, 1, 1)
        zeroVbox.addWidget(self.adhLabel, 0, 2, 1, 1)
        zeroVbox.addWidget(self.prl1Label, 0, 3, 1, 1)
        zeroVbox.addWidget(self.startLabel, 1, 0, 1, 1)
        zeroVbox.addWidget(self.endLabel, 2, 0, 1, 1)
        zeroVbox.addWidget(self.zeroRange1, 1, 1, 1, 1)
        zeroVbox.addWidget(self.zeroRange2, 2, 1, 1, 1)
        zeroVbox.addWidget(self.adhRange1, 1, 2, 1, 1)
        zeroVbox.addWidget(self.adhRange2, 2, 2, 1, 1)
        zeroVbox.addWidget(self.prl1Range1, 1, 3, 1, 1)
        zeroVbox.addWidget(self.prl1Range2, 2, 3, 1, 1)
        zeroVbox.addWidget(self.vertCTlabel, 3, 0, 1, 1)
        zeroVbox.addWidget(self.vertCrossTalk, 3, 1, 1, 1)

        filterVbox = QGridLayout()
        filterGroupBox.setLayout(filterVbox)
        filterVbox.addWidget(self.filterLatF, 1, 0, 1, 2)
        filterVbox.addWidget(self.windLabel, 2, 0, 1, 1)
        filterVbox.addWidget(self.filter_wind, 2, 1, 1, 1)
        filterVbox.addWidget(self.polyLabel, 3, 0, 1, 1)
        filterVbox.addWidget(self.filter_poly, 3, 1, 1, 1)
        filterVbox.addWidget(self.fontLabel, 2, 2, 1, 1)
        filterVbox.addWidget(self.fontSize, 2, 3, 1, 1)
        filterVbox.addWidget(self.eqLabel, 3, 2, 1, 1)
        filterVbox.addWidget(self.latCalibEq, 3, 3, 1, 1)
        filterVbox.addWidget(self.invertLatForce, 0, 2, 1, 2)
        filterVbox.addWidget(self.zeroShift, 0, 0, 1, 1)
        filterVbox.addWidget(self.applyCrossTalk, 1, 2, 1, 2)
        filterVbox.addWidget(self.xAxisLabel, 0, 3, 1, 1)
        filterVbox.addWidget(self.xAxisParam, 1, 3, 1, 1)
        filterVbox.addWidget(self.noiseStepsLabel, 4, 2, 1, 1)
        filterVbox.addWidget(self.noiseSteps, 5, 2, 1, 1)
        filterVbox.addWidget(self.legendPosLabel, 4, 3, 1, 1)
        filterVbox.addWidget(self.legendPos, 5, 3, 1, 1)
        filterVbox.addWidget(self.startFullLabel, 4, 0, 1, 1)
        filterVbox.addWidget(self.endFullLabel, 5, 0, 1, 1)
        filterVbox.addWidget(self.startFull, 4, 1, 1, 1)
        filterVbox.addWidget(self.endFull, 5, 1, 1, 1)
        filterVbox.addWidget(self.kBeamLabel, 6, 2, 1, 1)
        filterVbox.addWidget(self.kBeam, 6, 3, 1, 1)
        filterVbox.addWidget(self.deformStartLabel, 6, 0, 1, 1)
        filterVbox.addWidget(self.deformStart, 6, 1, 1, 1)

        flagVbox = QGridLayout()
        flagGroupBox.setLayout(flagVbox)
        flagVbox.addWidget(self.showContactArea, 0, 0)
        flagVbox.addWidget(self.showROIArea, 0, 1)
        flagVbox.addWidget(self.showZPiezo, 0, 2)
        flagVbox.addWidget(self.showXPiezo, 1, 0)
        flagVbox.addWidget(self.showAdhesion, 1, 1)
        flagVbox.addWidget(self.showFriction, 1, 2)
        flagVbox.addWidget(self.showLateralForce, 2, 0)
        flagVbox.addWidget(self.showContactLength, 2, 1)
        flagVbox.addWidget(self.showROILength, 2, 2)
        flagVbox.addWidget(self.showContactNumber, 3, 0)
        flagVbox.addWidget(self.showEcc, 3, 1)
        flagVbox.addWidget(self.showStress, 3, 2)
        flagVbox.addWidget(self.showDeformation, 4, 0)
        flagVbox.addWidget(self.showTitle, 4, 1)
        flagVbox.addWidget(self.showLegend2, 4, 2)

        lastCalibVbox = QGridLayout()
        self.latCalibGroupBox.setLayout(lastCalibVbox)
        lastCalibVbox.addWidget(self.frLabel, 0, 1, 1, 1)
        lastCalibVbox.addWidget(self.prl2Label, 0, 2, 1, 1)
        lastCalibVbox.addWidget(self.zero2Label, 0, 3, 1, 1)
        lastCalibVbox.addWidget(self.startLabel2, 1, 0, 1, 1)
        lastCalibVbox.addWidget(self.frictionRange1, 1, 1, 1, 1)
        lastCalibVbox.addWidget(self.endLabel2, 2, 0, 1, 1)
        lastCalibVbox.addWidget(self.frictionRange2, 2, 1, 1, 1)
        lastCalibVbox.addWidget(self.prl2Range1, 1, 2, 1, 1)
        lastCalibVbox.addWidget(self.prl2Range2, 2, 2, 1, 1)
        lastCalibVbox.addWidget(self.zero2Range1, 1, 3, 1, 1)
        lastCalibVbox.addWidget(self.zero2Range2, 2, 3, 1, 1)
        lastCalibVbox.addWidget(self.latCTlabel, 3, 0, 1, 1)
        lastCalibVbox.addWidget(self.latCrossTalk, 3, 1, 1, 1)

        fittingVbox = QGridLayout()
        self.fittingGroupBox.setLayout(fittingVbox)
        # fittingVbox.addWidget(self.startFitLabel, 0, 0, 1, 1)
        # fittingVbox.addWidget(self.endFitLabel, 1, 0, 1, 1)
        # fittingVbox.addWidget(self.fitStart, 0, 1, 1, 1)
        # fittingVbox.addWidget(self.fitStop, 1, 1, 1, 1)
        # fittingVbox.addWidget(self.xFitLabel, 0, 2, 1, 1)
        # fittingVbox.addWidget(self.yFitLabel, 1, 2, 1, 1)
        # fittingVbox.addWidget(self.xFit, 0, 3, 1, 1)
        # fittingVbox.addWidget(self.yFit, 1, 3, 1, 1)
        fittingVbox.addWidget(self.fitPosLabel, 0, 4, 1, 1)
        fittingVbox.addWidget(self.fitPos, 0, 5, 1, 1)
        fittingVbox.addWidget(self.showFitEq, 1, 4, 1, 2)

    def filter_change(self):
        if self.filter_wind.value() % 2 == 0:  #make sure its odd
            self.filter_wind.blockSignals(True)
            self.filter_wind.setValue(self.filter_wind.value() + 1)
            self.filter_wind.blockSignals(False)

    def update_range(self):
        key = self.roiChoice.currentText()
        if key not in self.rangeDict.keys():
            key = "Default"

        self.zeroRange1.blockSignals(True)
        self.zeroRange1.setValue(self.rangeDict[key][0][0])
        self.zeroRange1.blockSignals(False)
        self.zeroRange2.blockSignals(True)
        self.zeroRange2.setValue(self.rangeDict[key][0][1])
        self.zeroRange2.blockSignals(False)
        self.adhRange1.blockSignals(True)
        self.adhRange1.setValue(self.rangeDict[key][1][0])
        self.adhRange1.blockSignals(False)
        self.adhRange2.blockSignals(True)
        self.adhRange2.setValue(self.rangeDict[key][1][1])
        self.adhRange2.blockSignals(False)
        self.prl1Range1.blockSignals(True)
        self.prl1Range1.setValue(self.rangeDict[key][2][0])
        self.prl1Range1.blockSignals(False)
        self.prl1Range2.blockSignals(True)
        self.prl1Range2.setValue(self.rangeDict[key][2][1])
        self.prl1Range2.blockSignals(False)
        self.frictionRange1.blockSignals(True)
        self.frictionRange1.setValue(self.rangeDict[key][3][0])
        self.frictionRange1.blockSignals(False)
        self.frictionRange2.blockSignals(True)
        self.frictionRange2.setValue(self.rangeDict[key][3][1])
        self.frictionRange2.blockSignals(False)
        self.prl2Range1.blockSignals(True)
        self.prl2Range1.setValue(self.rangeDict[key][4][0])
        self.prl2Range1.blockSignals(False)
        self.prl2Range2.blockSignals(True)
        self.prl2Range2.setValue(self.rangeDict[key][4][1])
        self.prl2Range2.blockSignals(False)
        self.zero2Range1.blockSignals(True)
        self.zero2Range1.setValue(self.rangeDict[key][5][0])
        self.zero2Range1.blockSignals(False)
        self.zero2Range2.blockSignals(True)
        self.zero2Range2.setValue(self.rangeDict[key][5][1])
        self.zero2Range2.blockSignals(False)

    def update_dict(self):
        self.rangeDict[self.roiChoice.currentText()] = [
            [self.zeroRange1.value(),
             self.zeroRange2.value()],
            [self.adhRange1.value(),
             self.adhRange2.value()],
            [self.prl1Range1.value(),
             self.prl1Range2.value()],
            [self.frictionRange1.value(),
             self.frictionRange2.value()],
            [self.prl2Range1.value(),
             self.prl2Range2.value()],
            [self.zero2Range1.value(),
             self.zero2Range2.value()]
        ]
        print(self.rangeDict)

    def show_window(self):  #show window
        self.update_range()
        # self.update_dict()
        self.show()
Example #10
0
class ScenarioTab(QWidget):
    def __init__(self, parent, debug=False):   
        super(QWidget, self).__init__(parent)
        global debugger
        debugger = Debug(debug,'ScenarioTab:')
        self.dirty = True
        self.settings = {}
        self.notebook = parent
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        matrix = 'ptfe'
        self.settings['Matrix'] = matrix
        self.settings['Matrix density'] = support_matrix_db[matrix][0]
        self.settings['Matrix permittivity'] = support_matrix_db[matrix][1]
        self.settings['Bubble radius'] = 30.0
        self.settings['Bubble volume fraction'] = 0.0
        self.settings['Mass fraction'] = 0.1
        self.settings['Volume fraction'] = 0.1
        self.settings['Particle size(mu)'] = 0.0001
        self.settings['Particle size distribution sigma(mu)'] = 0.0
        self.settings['Ellipsoid a/b'] = 1.0
        self.settings['Unique direction - h'] = 0
        self.settings['Unique direction - k'] = 0
        self.settings['Unique direction - l'] = 1
        self.settings['Mass or volume fraction'] = 'volume'
        self.settings['ATR material refractive index'] = 4.0
        self.settings['ATR theta'] = 45.0
        self.settings['ATR S polarisation fraction'] = 0.5
        # get the reader from the main tab
        self.notebook = parent
        self.reader = self.notebook.mainTab.reader
        self.settings['Effective medium method'] = 'Maxwell-Garnett'
        # self.methods = ['Maxwell-Garnett', 'Bruggeman', 'Averaged Permittivity', 'Mie', 'Anisotropic-Mie']
        self.methods = ['Maxwell-Garnett', 'Bruggeman', 'Averaged Permittivity', 'Mie']
        self.settings['Particle shape'] = 'Sphere'
        self.shapes = ['Sphere', 'Needle', 'Plate', 'Ellipsoid']
        self.scenarioIndex = None
        # Create a scenario tab 
        vbox = QVBoxLayout()
        form = QFormLayout()
        #
        # Support matrix
        #
        self.matrix_cb = QComboBox(self)
        self.matrix_cb.setToolTip('Define the permittivity and density of the support matrix')
        self.matrix_cb.addItems(support_matrix_db)
        index = self.matrix_cb.findText(self.settings['Matrix'], Qt.MatchFixedString)
        if index >=0:
            self.matrix_cb.setCurrentIndex(index)
        else:
            print('support matrix index was not 0',matrix)
        self.matrix_cb.activated.connect(self.on_matrix_cb_activated)
        label = QLabel('Support matrix',self)
        label.setToolTip('Define the permittivity and density of the support matrix')
        form.addRow(label, self.matrix_cb)
        #
        # Support matrix permittivity
        #
        self.density_sb = QDoubleSpinBox(self) 
        self.density_sb.setRange(0.001, 100.0)
        self.density_sb.setSingleStep(0.01)
        self.density_sb.setDecimals(3)
        self.density_sb.setToolTip('Define the support matrix density. \nThis makes changes to the support density and permittivity')
        self.density_sb.setValue(self.settings['Matrix density'])
        self.density_sb.valueChanged.connect(self.on_density_sb_changed)
        label = QLabel('Support density', self)
        label.setToolTip('Define the support matrix density. \nThis makes changes to the support density and permittivity')
        form.addRow(label, self.density_sb)
        #
        # Support matrix permittivity
        #
        self.permittivity_sb = QDoubleSpinBox(self) 
        self.permittivity_sb.setRange(0.001, 100.0)
        self.permittivity_sb.setSingleStep(0.01)
        self.permittivity_sb.setDecimals(3)
        self.permittivity_sb.setToolTip('Define the support matrix permittivity')
        self.permittivity_sb.setValue(self.settings['Matrix permittivity'])
        self.permittivity_sb.valueChanged.connect(self.on_permittivity_sb_changed)
        label = QLabel('Support permittivity', self)
        label.setToolTip('Define the support matrix permittivity')
        form.addRow(label, self.permittivity_sb)
        #
        # Bubble volume fraction
        #
        self.bubble_vf_sb = QDoubleSpinBox(self) 
        self.bubble_vf_sb.setRange(0.0, 100.0*(1.0-self.settings['Volume fraction']))
        self.bubble_vf_sb.setSingleStep(1.0)
        self.bubble_vf_sb.setDecimals(1)
        self.bubble_vf_sb.setToolTip('Define the % volume fraction of air bubble inclusions in the matrix')
        self.bubble_vf_sb.setValue(100*self.settings['Bubble volume fraction'])
        self.bubble_vf_sb.valueChanged.connect(self.on_bubble_vf_sb_changed)
        label = QLabel('% Air void volume fraction', self)
        label.setToolTip('Define the % volume fraction of air bubble inclusions in the matrix')
        form.addRow(label, self.bubble_vf_sb)
        #
        # Bubble radius in microns
        #
        self.bubble_radius_sb = QDoubleSpinBox(self) 
        self.bubble_radius_sb.setRange(0.001, 1000.0)
        self.bubble_radius_sb.setSingleStep(1.0)
        self.bubble_radius_sb.setDecimals(3)
        self.bubble_radius_sb.setToolTip('Define the air bubble radius')
        self.bubble_radius_sb.setValue(self.settings['Bubble radius'])
        self.bubble_radius_sb.valueChanged.connect(self.on_bubble_radius_sb_changed)
        label = QLabel('Air void radius (μm)', self)
        label.setToolTip('Define the air void radius')
        form.addRow(label, self.bubble_radius_sb)
        #
        # Mass fraction of dielectric medium
        #
        self.mf_sb = QDoubleSpinBox(self)
        self.mf_sb.setRange(0.000001, 100.0)
        self.mf_sb.setSingleStep(0.1)
        self.mf_sb.setDecimals(6)
        self.mf_sb.setToolTip('The percentage mass fraction of the dielectric medium. \nNote that volume and mass fraction are linked')
        self.mf_sb.setValue(100.0*self.settings['Mass fraction'])
        self.mf_sb.valueChanged.connect(self.on_mf_sb_changed)
        label = QLabel('% Mass fraction of dielectric', self)
        label.setToolTip('The percentage mass fraction of the dielectric medium. \nNote that volume and mass fraction are linked')
        form.addRow(label, self.mf_sb)
        #
        # Volume fraction of dielectric medium
        #
        self.vf_sb = QDoubleSpinBox(self)
        self.vf_sb.setRange(0.000001, 100.0*(1.0-self.settings['Bubble volume fraction']))
        self.vf_sb.setSingleStep(0.1)
        self.vf_sb.setDecimals(6)
        self.vf_sb.setToolTip('The percentage volume fraction of the dielectric medium. \nNote that volume and mass fraction are linked')
        self.vf_sb.valueChanged.connect(self.on_vf_sb_changed)
        self.vf_sb.setValue(100.0*self.settings['Volume fraction'])
        label = QLabel('% Volume fraction of dielectric', self)
        label.setToolTip('The percentage volume fraction of the dielectric medium. \nNote that volume and mass fraction are linked')
        form.addRow(label, self.vf_sb)
        #
        # Calculation method
        #
        self.methods_cb = QComboBox(self)
        self.methods_cb.setToolTip('Choose the calculation method for the effective medium theory')
        self.methods_cb.addItems(self.methods)
        index = self.methods_cb.findText(self.settings['Effective medium method'], Qt.MatchFixedString)
        if index >=0:
            self.methods_cb.setCurrentIndex(index)
        else:
            print('Method index was not 0',self.settings['Effective medium method'])
        self.methods_cb.activated.connect(self.on_methods_cb_activated)
        label = QLabel('Method',self)
        label.setToolTip('Choose the calculation method for the effective medium theory')
        form.addRow(label, self.methods_cb)
        #
        # Particle size option
        #
        self.size_sb = QDoubleSpinBox(self)
        self.size_sb.setRange(0.000001, 1000.0)
        self.size_sb.setSingleStep(0.1)
        self.size_sb.setDecimals(6)
        self.size_sb.setToolTip('Define the particle radius of the sphere in μm.')
        self.size_sb.setValue(self.settings['Particle size(mu)'])
        self.size_sb.valueChanged.connect(self.on_size_sb_changed)
        label = QLabel('Particle radius (μm)',self)
        label.setToolTip('Define the particle radius of the sphere in μm.')
        form.addRow(label, self.size_sb)
        #
        # Particle sigma option
        #
        self.sigma_sb = QDoubleSpinBox(self)
        self.sigma_sb.setRange(0.0, 1000.0)
        self.sigma_sb.setSingleStep(0.1)
        self.sigma_sb.setDecimals(6)
        self.sigma_sb.setToolTip('Define the particle size distribution as a lognormal distribution with the given sigma. \nOnly applicable for the Mie method')
        self.sigma_sb.setValue(self.settings['Particle size distribution sigma(mu)'])
        self.sigma_sb.valueChanged.connect(self.on_sigma_sb_changed)
        label = QLabel('Particle sigma (μm)',self)
        label.setToolTip('Define the particle size distribition as a lognormal with the given sigma. \nOnly applicable for the Mie method')
        form.addRow(label, self.sigma_sb)
        #
        # Crystallite shape
        #
        self.shape_cb = QComboBox(self)
        self.shape_cb.setToolTip('Choose a particle shape. \nFor the Mie methods only sphere is allowed.  \nFor shapes other than sphere there is a unique direction. \nFor ellipsoidal and needle like this is a direction [abc].  \nFor a plate the perpendicular to a crystal face (hkl) is used to define the unique direction')
        self.shape_cb.addItems(self.shapes)
        index = self.shape_cb.findText(self.settings['Particle shape'], Qt.MatchFixedString)
        if index >=0:
            self.shape_cb.setCurrentIndex(index)
        else:
            print('Method index was not 0',self.settings['Particle shape'])
        self.shape_cb.activated.connect(self.on_shape_cb_activated)
        label = QLabel('Particle shape',self)
        label.setToolTip('Choose a particle shape. \nFor the Mie methods only sphere is allowed.  \nFor shapes other than sphere there is a unique direction. \nFor ellipsoidal and needle like this is a direction [abc].  \nFor a plate the perpendicular to a crystal face (hkl) is used to define the unique direction')
        form.addRow(label, self.shape_cb)
        #
        # Particle shape information
        # unique direction (hkl) or [abc]
        self.h_sb = QSpinBox(self)
        self.h_sb.setToolTip('Define the h dimension of the unique direction')
        self.h_sb.setRange(-20,20)
        self.h_sb.setValue(self.settings['Unique direction - h'])
        self.h_sb.valueChanged.connect(self.on_h_sb_changed)
        self.k_sb = QSpinBox(self)
        self.k_sb.setToolTip('Define the k dimension of the unique direction')
        self.k_sb.setRange(-20,20)
        self.k_sb.setValue(self.settings['Unique direction - k'])
        self.k_sb.valueChanged.connect(self.on_k_sb_changed)
        self.l_sb = QSpinBox(self)
        self.l_sb.setToolTip('Define the l dimension of the unique direction')
        self.l_sb.setRange(-20,20)
        self.l_sb.setValue(self.settings['Unique direction - l'])
        self.l_sb.valueChanged.connect(self.on_l_sb_changed)
        hbox = QHBoxLayout()
        hbox.addWidget(self.h_sb)
        hbox.addWidget(self.k_sb)
        hbox.addWidget(self.l_sb)
        self.hkl_label = QLabel('Unique direction [abc]',self)
        self.hkl_label.setToolTip('Define the unique direction by [abc] or (hkl). \n[abc] is used by needles and ellipsoids.  It defines the unique direction in crystallographic units. \n(hkl) is used by plates it defines a surface and the unique direction is perpendicular to it.')

        form.addRow(self.hkl_label, hbox)
        #
        # a over b ratio for ellipse
        #
        self.aoverb_sb = QDoubleSpinBox(self)
        self.aoverb_sb.setRange(0.0, 1000.0)
        self.aoverb_sb.setSingleStep(0.1)
        self.aoverb_sb.setDecimals(6)
        self.aoverb_sb.setToolTip('Define the ellipsoid a/b ratio or eccentricity.  \nOnly applicable for the ellipsoid shapes \na/b < 1: oblate ellipsoid \na/b > 1: prolate ellipsoid')
        self.aoverb_sb.setValue(self.settings['Ellipsoid a/b'])
        self.aoverb_sb.valueChanged.connect(self.on_aoverb_sb_changed)
        label = QLabel('Ellipsoid a/b eccentricty',self)
        label.setToolTip('Define the ellipsoid a/b ratio or eccentricity.  \nOnly applicable for the ellipsoid shapes \na/b < 1: oblate ellipsoid \na/b > 1: prolate ellipsoid')
        form.addRow(label, self.aoverb_sb)
        #
        # Add ATR options
        # Refractive Index
        self.atr_index_sb = QDoubleSpinBox(self) 
        self.atr_index_sb.setRange(0.001, 100.0)
        self.atr_index_sb.setSingleStep(0.01)
        self.atr_index_sb.setDecimals(3)
        self.atr_index_sb.setToolTip('Define the ATR material refractive index')
        self.atr_index_sb.setValue(self.settings['ATR material refractive index'])
        self.atr_index_sb.valueChanged.connect(self.on_atr_index_sb_changed)
        label = QLabel('ATR material refractive index', self)
        label.setToolTip('Define the ATR material refractive index')
        form.addRow(label, self.atr_index_sb)
        # Incident angle in degreees
        self.atr_incident_ang_sb = QDoubleSpinBox(self) 
        self.atr_incident_ang_sb.setRange(0.0, 180.0)
        self.atr_incident_ang_sb.setSingleStep(0.1)
        self.atr_incident_ang_sb.setDecimals(1)
        self.atr_incident_ang_sb.setToolTip('Define the ATR incident angle')
        self.atr_incident_ang_sb.setValue(self.settings['ATR theta'])
        self.atr_incident_ang_sb.valueChanged.connect(self.on_atr_incident_ang_sb_changed)
        label = QLabel('ATR incident angle', self)
        label.setToolTip('Define the ATR incident angle')
        form.addRow(label, self.atr_incident_ang_sb)
        # S polarisation fraction
        self.atr_spolfrac_sb = QDoubleSpinBox(self) 
        self.atr_spolfrac_sb.setRange(0.0, 1.0)
        self.atr_spolfrac_sb.setSingleStep(0.01)
        self.atr_spolfrac_sb.setDecimals(3)
        self.atr_spolfrac_sb.setToolTip('Define the ATR S polarisation fraction, the rest is P polarisation')
        self.atr_spolfrac_sb.setValue(self.settings['ATR S polarisation fraction'])
        self.atr_spolfrac_sb.valueChanged.connect(self.on_atr_spolfrac_sb_changed)
        label = QLabel('ATR S polarisation fraction', self)
        label.setToolTip('Define the S polarisation fraction, the rest is P polarisation')
        form.addRow(label, self.atr_spolfrac_sb)
        #
        # Add a legend option
        #
        self.legend_le = QLineEdit(self) 
        self.legend_le.setToolTip('The legend will be used to describe the results in the plot')
        self.legend_le.setText('Scenario legend')
        self.legend_le.textChanged.connect(self.on_legend_le_changed)
        label = QLabel('Scenario legend',self)
        label.setToolTip('The legend will be used to describe the results in the plot')
        form.addRow(label, self.legend_le)

        #
        # Final buttons
        #
        hbox = QHBoxLayout()
        self.pushButton1 = QPushButton('Add another scenario')
        self.pushButton1.setToolTip('Use another scenario to calculate the effect of changing the material on the absorption and permittivity')
        self.pushButton1.clicked.connect(self.pushButton1Clicked)
        hbox.addWidget(self.pushButton1)
        self.pushButton3 = QPushButton('Delete this scenario')
        self.pushButton3.setToolTip('Delete the current scenario')
        self.pushButton3.clicked.connect(self.pushButton3Clicked)
        hbox.addWidget(self.pushButton3)
        form.addRow(hbox)
        vbox.addLayout(form)
        # finalise the layout
        self.setLayout(vbox)
        # sort out greying of boxes
        self.change_greyed_out()

    def pushButton1Clicked(self):
        # Add another scenario
        debugger.print('Button 1 pressed')
        self.notebook.addScenario(copyFromIndex=self.scenarioIndex)

    def pushButton3Clicked(self):
        # Delete a scenario
        debugger.print('Button 3 pressed')
        self.notebook.deleteScenario(self.scenarioIndex)

    def crystal_density(self):
        if not self.reader:
            return 1.0
        volume = self.reader.volume
        mass = 0.0
        for m in self.reader.masses:
            mass += m
        density = mass / (avogadro_si * volume * 1.0e-24)
        return density
        

    def on_h_sb_changed(self,value):
        debugger.print('on_h_sb_changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Unique direction - h'] = value

    def on_k_sb_changed(self,value):
        debugger.print('on_k_sb_changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Unique direction - k'] = value

    def on_l_sb_changed(self,value):
        debugger.print('on_l_sb_changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Unique direction - l'] = value

    def on_shape_cb_activated(self,index):
        debugger.print('on shape cb activated', index)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Particle shape'] = self.shapes[index]
        if self.settings['Particle shape'] == 'Sphere':
            self.settings['Unique direction - h'] = 0
            self.settings['Unique direction - k'] = 0
            self.settings['Unique direction - l'] = 0
        self.change_greyed_out()

    def on_methods_cb_activated(self,index):
        debugger.print('on methods cb activated', index)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Effective medium method'] = self.methods[index]
        if self.settings['Effective medium method'] == 'Mie':
            self.settings['Particle shape'] = 'Sphere'
        elif self.settings['Effective medium method'] == 'Anisotropic-Mie':
            self.settings['Particle shape'] = 'Sphere'
        elif self.settings['Effective medium method'] == 'Maxwell-Garnett':
            self.settings['Particle size distribution sigma(mu)'] = 0.0
        elif self.settings['Effective medium method'] == 'Bruggeman':
            self.settings['Particle size distribution sigma(mu)'] = 0.0
        elif self.settings['Effective medium method'] == 'Averaged Permittivity':
            self.settings['Particle size(mu)'] = 0.0001
            self.settings['Particle size distribution sigma(mu)'] = 0.0
        self.change_greyed_out()

    def on_mf_sb_changed(self,value):
        debugger.print('on mass fraction line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Mass or volume fraction'] = 'mass'
        self.settings['Mass fraction'] =  value/100.0
        self.update_vf_sb()

    def update_vf_sb(self):
        mf1 = self.settings['Mass fraction']
        mf2 = 1.0 - mf1
        rho1 = self.crystal_density()
        rho2 = self.settings['Matrix density']
        vf1 = ( 1.0 - self.settings['Bubble volume fraction'] ) * (mf1/mf2)*(rho2/rho1) / ( 1 + (mf1/mf2)*(rho2/rho1))
#        vf1 = 1.0 / ( 1.0 + mf2/mf1 * (rho1/rho2) )
        self.settings['Volume fraction'] = vf1
        self.vf_sb.blockSignals(True)
        self.vf_sb.setValue(100.0*vf1)
        self.vf_sb.blockSignals(False)
        self.bubble_vf_sb.setRange(0.0, 100.0*(1.0-self.settings['Volume fraction']))
        self.vf_sb.setRange(0.0, 100.0*(1.0-self.settings['Bubble volume fraction']))
        debugger.print('Update_vf_sb')
        debugger.print('rho 1', rho1)
        debugger.print('rho 2', rho2)
        debugger.print('vf 1 ', vf1)
        
    def on_aoverb_sb_changed(self,value):
        debugger.print('on_aoverb_le_changed',value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Ellipsoid a/b'] = value

    def on_legend_le_changed(self,text):
        debugger.print('on legend change', text)
        self.dirty = True
        self.settings['Legend'] = text

    def on_sigma_sb_changed(self,value):
        debugger.print('on sigma line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Particle size distribution sigma(mu)'] = value

    def on_size_sb_changed(self,value):
        debugger.print('on size line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Particle size(mu)'] = value

    def on_vf_sb_changed(self,value):
        debugger.print('on volume fraction line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.settings['Mass or volume fraction'] = 'volume'
        self.settings['Volume fraction'] = value/100.0
        self.update_mf_sb()

    def update_mf_sb(self):
        vf1 = self.settings['Volume fraction']
        vf2 = 1.0 - vf1 - self.settings['Bubble volume fraction']
        rho1 = self.crystal_density()
        rho2 = self.settings['Matrix density']
        # mf1 = 1.0 / ( 1.0 + (vf2/vf1) * (rho2/rho1) )
        mf1 = rho1*vf1 / ( rho1*vf1 + rho2*vf2 )
        self.settings['Mass fraction'] = mf1
        self.mf_sb.blockSignals(True)
        self.mf_sb.setValue(100.0*mf1)
        self.mf_sb.blockSignals(False)
        debugger.print('Update_mf_sb')
        debugger.print('rho 1', rho1)
        debugger.print('rho 2', rho2)
        debugger.print('mf 1 ', mf1)

    def on_matrix_cb_activated(self,index):
        debugger.print('on matrix combobox activated', index)
        debugger.print('on matrix combobox activated', self.matrix_cb.currentText())
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        matrix = self.matrix_cb.currentText()
        self.matrix_cb.blockSignals(True)
        self.density_sb.blockSignals(True)
        self.permittivity_sb.blockSignals(True)
        self.settings['Matrix'] = matrix
        self.settings['Matrix density'] = support_matrix_db[matrix][0]
        self.settings['Matrix permittivity'] = support_matrix_db[matrix][1]
        self.density_sb.setValue(self.settings['Matrix density'])
        self.permittivity_sb.setValue(self.settings['Matrix permittivity'])
        # volume fraction takes precedence
        if self.settings['Mass or volume fraction'] == 'volume':
            self.update_mf_sb()
            self.update_vf_sb()
        else:
            self.update_vf_sb()
            self.update_mf_sb()
        self.matrix_cb.blockSignals(False)
        self.density_sb.blockSignals(False)
        self.permittivity_sb.blockSignals(False)

    def on_density_sb_changed(self,value):
        self.settings['Matrix density'] = value
        # volume fraction taked precedence
        if self.settings['Mass or volume fraction'] == 'volume':
            self.update_mf_sb()
            self.update_vf_sb()
        else:
            self.update_vf_sb()
            self.update_mf_sb()
        debugger.print('on density line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_bubble_vf_sb_changed(self,value):
        self.settings['Bubble volume fraction'] = value/100.0
        if self.settings['Mass or volume fraction'] == 'volume':
            self.update_mf_sb()
        else:
            self.update_vf_sb()
        debugger.print('on bubble volume fraction changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_bubble_radius_sb_changed(self,value):
        self.settings['Bubble radius'] = value
        debugger.print('on permittivity line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_permittivity_sb_changed(self,value):
        self.settings['Matrix permittivity'] = value
        debugger.print('on permittivity line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_atr_index_sb_changed(self,value):
        self.settings['ATR material refractive index'] = value
        debugger.print('on atr index line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_atr_incident_ang_sb_changed(self,value):
        self.settings['ATR theta'] = value
        debugger.print('on atr incident angle line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def on_atr_spolfrac_sb_changed(self,value):
        self.settings['ATR S polarisation fraction'] = value
        debugger.print('on atr spolfraction line edit changed', value)
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True

    def set_reader(self,reader):
        self.dirty = True
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        self.reader = reader

    def change_greyed_out(self):
        # Have a look through the settings and see if we need to grey anything out
        method = self.settings['Effective medium method']
        if method == 'Mie' or method == 'Anisotropic-Mie':
            self.size_sb.setEnabled(True)
            self.sigma_sb.setEnabled(True)
            for i,shape in enumerate(self.shapes):
                self.shape_cb.model().item(i).setEnabled(False)
            self.settings['Particle shape'] = 'Sphere'
            self.shape_cb.setEnabled(True)
            index = self.shape_cb.findText(self.settings['Particle shape'], Qt.MatchFixedString)
            if index >=0:
                self.shape_cb.model().item(index).setEnabled(True)
                self.shape_cb.setCurrentIndex(index)
            else:
                print('Method index was not 0',self.settings['Particle shape'])
        elif method == 'Averaged Permittivity':
            self.size_sb.setEnabled(False)
            self.sigma_sb.setEnabled(False)
            self.settings['Particle shape'] = 'Sphere'
            index = self.shape_cb.findText(self.settings['Particle shape'], Qt.MatchFixedString)
            if index >=0:
                self.shape_cb.model().item(index).setEnabled(True)
                self.shape_cb.setCurrentIndex(index)
            self.shape_cb.setEnabled(False)
            for i,shape in enumerate(self.shapes):
                self.shape_cb.model().item(i).setEnabled(False)
        elif method == 'Maxwell-Garnett' or method == 'Bruggeman':
            self.size_sb.setEnabled(True)
            self.sigma_sb.setEnabled(False)
            self.shape_cb.setEnabled(True)
            for i,shape in enumerate(self.shapes):
                self.shape_cb.model().item(i).setEnabled(True)
        else:
            self.size_sb.setEnabled(False)
            self.sigma_sb.setEnabled(False)
            self.shape_cb.setEnabled(True)
            for i,shape in enumerate(self.shapes):
                self.shape_cb.model().item(i).setEnabled(True)
        # deal with shapes
        if self.settings['Particle shape'] == 'Ellipsoid':
            self.h_sb.setEnabled(True)
            self.k_sb.setEnabled(True)
            self.l_sb.setEnabled(True)
            self.hkl_label.setText('Unique direction [abc]')
            self.aoverb_sb.setEnabled(True)
        elif self.settings['Particle shape'] == 'Plate':
            self.h_sb.setEnabled(True)
            self.k_sb.setEnabled(True)
            self.l_sb.setEnabled(True)
            self.hkl_label.setText('Unique direction (hkl)')
            self.aoverb_sb.setEnabled(False)
        elif self.settings['Particle shape'] == 'Needle':
            self.h_sb.setEnabled(True)
            self.k_sb.setEnabled(True)
            self.l_sb.setEnabled(True)
            self.hkl_label.setText('Unique direction [abc]')
            self.aoverb_sb.setEnabled(False)
        elif self.settings['Particle shape'] == 'Sphere':
            self.h_sb.setEnabled(False)
            self.k_sb.setEnabled(False)
            self.l_sb.setEnabled(False)
            self.aoverb_sb.setEnabled(False)
        else:
            print('ScenarioTab: Shape not recognised', self.settings['Particle shape'])
        
    def setScenarioIndex(self,index):
        self.scenarioIndex = index
        text = self.legend_le.text()
        if text == 'Scenario legend':
            self.legend_le.setText('Scenario '+str(index + 1))
        return

    def print_settings(self):
        print('#')
        print('# Scenario tab')
        print('#')
        print('tab = self.notebook.scenarios')
        for key in self.settings:
            print(key, self.settings[key]) 
        
    def refresh(self,force=False):
        if not self.dirty and not force:
            debugger.print('refresh aborted', self.dirty,force)
            return
        debugger.print('refresh', force)
        # Tell the main notebook that we need to recalculate any plot
        self.notebook.plottingCalculationRequired = True
        self.notebook.fittingCalculationRequired = True
        # First see if we can get the reader from the mainTab
        self.reader = self.notebook.mainTab.reader
        #
        # Block signals during refresh
        # 
        for w in self.findChildren(QWidget):
            w.blockSignals(True)
        # use the settings values to initialise the widgets
        index = self.matrix_cb.findText(self.settings['Matrix'], Qt.MatchFixedString)
        self.matrix_cb.setCurrentIndex(index)
        self.density_sb.setValue(self.settings['Matrix density'])
        self.permittivity_sb.setValue(self.settings['Matrix permittivity'])
        self.bubble_vf_sb.setValue(100*self.settings['Bubble volume fraction'])
        self.bubble_radius_sb.setValue(self.settings['Bubble radius'])
        if self.settings['Mass or volume fraction'] == 'volume':
            # volume fraction takes precedence
            self.update_mf_sb()
            self.update_vf_sb()
        else:
            # mass fraction takes precedence
            self.update_vf_sb()
            self.update_mf_sb()
        #
        index = self.methods_cb.findText(self.settings['Effective medium method'], Qt.MatchFixedString)
        self.methods_cb.setCurrentIndex(index)
        self.size_sb.setValue(self.settings['Particle size(mu)'])
        self.sigma_sb.setValue(self.settings['Particle size distribution sigma(mu)'])
        index = self.shape_cb.findText(self.settings['Particle shape'], Qt.MatchFixedString)
        self.shape_cb.setCurrentIndex(index)
        self.h_sb.setValue(self.settings['Unique direction - h'])
        self.k_sb.setValue(self.settings['Unique direction - k'])
        self.l_sb.setValue(self.settings['Unique direction - l'])
        self.aoverb_sb.setValue(self.settings['Ellipsoid a/b'])
        self.legend_le.setText(self.settings['Legend'])
        self.change_greyed_out()
        #
        # Unblock signals after refresh
        # 
        for w in self.findChildren(QWidget):
            w.blockSignals(False)
        self.dirty = False
        return
Example #11
0
class QLevels(ZoneDlg):
    allowedtypes = ("video", )
    zonename = "Levels Zone"
    title = "Levels Editor"

    def createNewFilterInstance(self):
        return Levels()

    def _createControls(self):
        self.setWindowTitle("Configure Levels Zones")

        self.rchan = ChannelEditor((128, 0, 0), self)
        self.gchan = ChannelEditor((0, 128, 0), self)
        self.bchan = ChannelEditor((0, 0, 128), self)

        self.rchan.minSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.rchan.gammaSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.rchan.maxSpinBox.valueChanged.connect(self.widgetValuesChanged)

        self.gchan.minSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.gchan.gammaSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.gchan.maxSpinBox.valueChanged.connect(self.widgetValuesChanged)

        self.bchan.minSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.bchan.gammaSpinBox.valueChanged.connect(self.widgetValuesChanged)
        self.bchan.maxSpinBox.valueChanged.connect(self.widgetValuesChanged)

        layout = QHBoxLayout(self)
        self.setLayout(layout)

        llayout = QVBoxLayout()
        rlayout = QVBoxLayout()
        layout.addLayout(llayout)
        layout.addLayout(rlayout)

        llayout.addWidget(self.rchan)
        llayout.addWidget(self.gchan)
        llayout.addWidget(self.bchan)

        self.sourceWidget = QWidget(self)
        self.sourceSelection = self.createSourceControl(self.sourceWidget)
        self.sourceSelection.currentDataChanged.connect(self.setFilterSource)

        srclayout = QHBoxLayout()
        srclayout.addWidget(QLabel("Source: ", self.sourceWidget))
        srclayout.addWidget(self.sourceSelection)

        self.sourceWidget.setLayout(srclayout)
        rlayout.addWidget(self.sourceWidget)

        self._createImageView(rlayout)
        self.imageView.mousePressed.connect(self.setFocus)

        self._createZoneNavControls(rlayout)
        self._createZoneControls(rlayout)
        self._createZoneButtons(rlayout)
        self._createGlobalControls(rlayout)
        self._createDlgButtons(rlayout)

    def _createZoneControls(self, layout=None, index=None):
        if layout is None:
            layout = self.layout()

        self.prevColor = ColorPreview(QColor(), self)
        self.prevColor.setFixedSize(32, 32)
        self.prevColorLabel = QLabel("—", self)

        self.nextColor = ColorPreview(QColor(), self)
        self.nextColor.setFixedSize(32, 32)
        self.nextColorLabel = QLabel("—", self)

        self.transitionCheckBox = QCheckBox("Transition Zone", self)
        self.transitionCheckBox.stateChanged.connect(
            self.setCurrentZoneTransition)

        self.analyzeBtn = QPushButton("Anal&yze Zone", self)
        self.analyzeBtn.clicked.connect(self.analyzeZone)  # 162523

        self.gammaLabel = QLabel("Gamma:", self)

        self.gammaSpinBox = QDoubleSpinBox(self)
        self.gammaSpinBox.setSingleStep(0.1)
        self.gammaSpinBox.setDecimals(2)
        self.gammaSpinBox.setMinimum(0.25)
        self.gammaSpinBox.setMaximum(4)
        self.gammaSpinBox.valueChanged.connect(self.widgetValuesChanged)

        self.suggBtn = QPushButton("&Suggestion", self)
        self.suggBtn.clicked.connect(self.useAutoGamma)  # 162523

        inpLabel = QLabel("Input", self)
        outLabel = QLabel("Output", self)

        self.redAvgIntensIn = QLabel("Avg Red Intensity: —", self)
        self.greenAvgIntensIn = QLabel("Avg Green Intensity: —", self)
        self.blueAvgIntensIn = QLabel("Avg Blue Intensity: —", self)

        self.redAvgIntensOut = QLabel("Avg Red Intensity: —", self)
        self.greenAvgIntensOut = QLabel("Avg Green Intensity: —", self)
        self.blueAvgIntensOut = QLabel("Avg Blue Intensity: —", self)

        sublayout = QHBoxLayout()
        sublayout.addStretch()
        sublayout.addWidget(self.prevColor)
        sublayout.addWidget(self.prevColorLabel)
        sublayout.addStretch()
        sublayout.addWidget(self.nextColor)
        sublayout.addWidget(self.nextColorLabel)
        sublayout.addStretch()
        sublayout.addWidget(self.transitionCheckBox)
        sublayout.addStretch()
        sublayout.addWidget(self.analyzeBtn)
        sublayout.addStretch()
        sublayout.addWidget(self.gammaLabel)
        sublayout.addWidget(self.gammaSpinBox)
        sublayout.addWidget(self.suggBtn)
        sublayout.addStretch()

        layout.addLayout(sublayout)

        sublayout = QHBoxLayout()
        sublayout.addStretch()
        sublayout.addWidget(inpLabel)
        sublayout.addStretch()
        sublayout.addWidget(self.redAvgIntensIn)
        sublayout.addStretch()
        sublayout.addWidget(self.greenAvgIntensIn)
        sublayout.addStretch()
        sublayout.addWidget(self.blueAvgIntensIn)
        sublayout.addStretch()

        layout.addLayout(sublayout)

        sublayout = QHBoxLayout()
        sublayout.addStretch()
        sublayout.addWidget(outLabel)
        sublayout.addStretch()
        sublayout.addWidget(self.redAvgIntensOut)
        sublayout.addStretch()
        sublayout.addWidget(self.greenAvgIntensOut)
        sublayout.addStretch()
        sublayout.addWidget(self.blueAvgIntensOut)
        sublayout.addStretch()

        layout.addLayout(sublayout)

        self.currentFrame = None
        self.setFocus(None, None)

    @pyqtSlot(float, float)
    def setFocus(self, x, y):
        self._x = x
        self._y = y

        if (self.currentFrame is not None and self._x is not None
                and self._y is not None):
            A = self.currentFrame.to_rgb().to_ndarray()
            h, w, n = A.shape
            X = arange(w)
            Y = arange(h)
            X, Y = meshgrid(X, Y)
            G = exp(-((X - self._x)**2 + (Y - self._y)**2) / 6)
            self._ker = G / G.sum()

        else:
            self._ker = None

        self.updateColors()

    def loadFrame(self, n, t):
        super().loadFrame(n, t)

        try:
            self.updateColors()

        except Exception:
            for line in traceback.format_exception(*sys.exc_info()):
                print(line, file=sys.stderr, end="")

    def generatePreview(self, n):
        self.currentFrame = next(
            self.filtercopy.prev.iterFrames(n, whence="framenumber"))

        return super().generatePreview(n)

    def updateColors(self):
        if (self.currentFrame is not None and self._x is not None
                and self._y is not None and self._ker is not None):
            A = self.currentFrame.to_rgb().to_ndarray()
            avg = int0((moveaxis(A, 2, 0) * self._ker).sum(axis=(1, 2)) + 0.5)
            R, G, B = avg

            self.prevColor.setColor(QColor(R, G, B))
            self.prevColorLabel.setText(f"({R}, {G}, {B})")

            N = arange(256, dtype=float64)
            n = self.slider.slider.value()

            if (self.transitionCheckBox.checkState()
                    and self.zonecopy.prev is not None
                    and self.zonecopy.next is not None):
                t = (n - self.zonecopy.prev_start + 1) / \
                    (self.zonecopy.prev_framecount + 1)
                rmin = (1 - t)*self.zonecopy.prev.rmin + \
                    t*self.zonecopy.next.rmin
                gmin = (1 - t)*self.zonecopy.prev.gmin + \
                    t*self.zonecopy.next.gmin
                bmin = (1 - t)*self.zonecopy.prev.bmin + \
                    t*self.zonecopy.next.bmin

                rmax = (1 - t)*self.zonecopy.prev.rmax + \
                    t*self.zonecopy.next.rmax
                gmax = (1 - t)*self.zonecopy.prev.gmax + \
                    t*self.zonecopy.next.gmax
                bmax = (1 - t)*self.zonecopy.prev.bmax + \
                    t*self.zonecopy.next.bmax

                rgamma = (1 - t)*self.zonecopy.prev.rgamma + \
                    t*self.zonecopy.next.rgamma
                ggamma = (1 - t)*self.zonecopy.prev.ggamma + \
                    t*self.zonecopy.next.ggamma
                bgamma = (1 - t)*self.zonecopy.prev.bgamma + \
                    t*self.zonecopy.next.bgamma

                gamma = (1 - t)*self.zonecopy.prev.gamma + \
                    t*self.zonecopy.next.gamma
            else:
                rmin = self.rchan.minSpinBox.value()
                gmin = self.gchan.minSpinBox.value()
                bmin = self.bchan.minSpinBox.value()
                rmax = self.rchan.maxSpinBox.value()
                gmax = self.gchan.maxSpinBox.value()
                bmax = self.bchan.maxSpinBox.value()
                rgamma = self.rchan.gammaSpinBox.value()
                ggamma = self.gchan.gammaSpinBox.value()
                bgamma = self.bchan.gammaSpinBox.value()
                gamma = self.gammaSpinBox.value()

            RR = (N.clip(min=rmin, max=rmax) - rmin) / (rmax - rmin)
            RR = 1 - (1 - RR)**(rgamma * gamma)
            RR = uint8((256 * RR).clip(max=255) + 0.5)
            GG = (N.clip(min=gmin, max=gmax) - gmin) / (gmax - gmin)
            GG = 1 - (1 - GG)**(ggamma * gamma)
            GG = uint8((256 * GG).clip(max=255) + 0.5)
            BB = (N.clip(min=bmin, max=bmax) - bmin) / (bmax - bmin)
            BB = 1 - (1 - BB)**(bgamma * gamma)
            BB = uint8((256 * BB).clip(max=255) + 0.5)

            R = RR[R]
            G = GG[G]
            B = BB[B]

            self.nextColor.setColor(QColor(R, G, B))
            self.nextColorLabel.setText(f"({R}, {G}, {B})")

    def analyzeZone(self):
        dlg = ZoneAnalysis(self.zonecopy, self)
        dlg.exec_()
        self.zoneModified()

        if self.zonecopy.histogram is not None:
            self.rchan.setHistogram(self.zonecopy.histogram[0])
            self.gchan.setHistogram(self.zonecopy.histogram[1])
            self.bchan.setHistogram(self.zonecopy.histogram[2])

            self.suggBtn.setEnabled(True)
            gamma = self.autogamma()
            self.suggBtn.setText(f"Suggestion: {gamma:.2f}")

    def autogamma(self):
        zone = self.zonecopy
        a = arange(0, 256, 0.25)
        Ia = -log(1 - a / 256)
        r, g, b = self.rchan.minSpinBox.value(), self.gchan.minSpinBox.value(
        ), self.bchan.minSpinBox.value()
        R, G, B = self.rchan.maxSpinBox.value(), self.gchan.maxSpinBox.value(
        ), self.bchan.maxSpinBox.value()
        rg, gg, bg = self.rchan.gammaSpinBox.value(
        ), self.gchan.gammaSpinBox.value(), self.bchan.gammaSpinBox.value()

        IR = -log(1 - (a.clip(min=r, max=R + 0.75) - r) / (R + 1 - r)) * rg
        IG = -log(1 - (a.clip(min=g, max=G + 0.75) - g) / (G + 1 - g)) * gg
        IB = -log(1 - (a.clip(min=b, max=B + 0.75) - b) / (B + 1 - b)) * bg

        gamma = (((Ia * zone.histogram[0]).sum() +
                  (Ia * zone.histogram[1]).sum() +
                  (Ia * zone.histogram[2]).sum()) /
                 ((IR * zone.histogram[0]).sum() +
                  (IG * zone.histogram[1]).sum() +
                  (IB * zone.histogram[2]).sum()))
        return float(gamma)

    def useAutoGamma(self):
        gamma = self.autogamma()
        self.gammaSpinBox.setValue(gamma)

    def setCurrentZoneTransition(self, state):
        flag = state == Qt.Unchecked

        self.rchan.setEnabled(flag)
        self.gchan.setEnabled(flag)
        self.bchan.setEnabled(flag)
        self.gammaSpinBox.setEnabled(flag)
        self.widgetValuesChanged()

    def _resetZoneControls(self):
        zone = self.zonecopy

        if self.zonecopy.transition:
            self.transitionCheckBox.blockSignals(True)
            self.transitionCheckBox.setChecked(True)
            self.transitionCheckBox.blockSignals(False)
            self.transitionCheckBox.setEnabled(True)
            self.rchan.setEnabled(False)
            self.gchan.setEnabled(False)
            self.bchan.setEnabled(False)
            self.gammaSpinBox.setEnabled(False)
            self.rchan.setHistogram(None)
            self.gchan.setHistogram(None)
            self.bchan.setHistogram(None)

        else:
            self.transitionCheckBox.blockSignals(True)
            self.transitionCheckBox.setChecked(False)
            self.transitionCheckBox.blockSignals(False)
            self.transitionCheckBox.setEnabled(
                self.zone not in (self.zone.parent.start, self.zone.parent.end)
                and
                not (self.zone.prev.transition or self.zone.next.transition))

            self.rchan.setEnabled(True)
            self.gchan.setEnabled(True)
            self.bchan.setEnabled(True)
            self.gammaSpinBox.setEnabled(True)

            self.rchan.minSpinBox.blockSignals(True)
            self.rchan.minSpinBox.setValue(zone.rmin)
            self.rchan.histogram.setClipMinimum(zone.rmin)
            self.rchan.histogram.setGamma(zone.rgamma * zone.gamma)
            self.rchan.minSpinBox.blockSignals(False)

            self.rchan.maxSpinBox.blockSignals(True)
            self.rchan.maxSpinBox.setValue(zone.rmax)
            self.rchan.histogram.setClipMaximum(zone.rmax)
            self.rchan.maxSpinBox.blockSignals(False)

            self.rchan.gammaSpinBox.blockSignals(True)
            self.rchan.gammaSpinBox.setValue(zone.rgamma)
            self.rchan.gammaSpinBox.blockSignals(False)

            self.gchan.minSpinBox.blockSignals(True)
            self.gchan.minSpinBox.setValue(zone.gmin)
            self.gchan.histogram.setClipMinimum(zone.gmin)
            self.gchan.minSpinBox.blockSignals(False)

            self.gchan.maxSpinBox.blockSignals(True)
            self.gchan.maxSpinBox.setValue(zone.gmax)
            self.gchan.histogram.setClipMaximum(zone.gmax)
            self.gchan.histogram.setGamma(zone.ggamma * zone.gamma)
            self.gchan.maxSpinBox.blockSignals(False)

            self.gchan.gammaSpinBox.blockSignals(True)
            self.gchan.gammaSpinBox.setValue(zone.ggamma)
            self.gchan.gammaSpinBox.blockSignals(False)

            self.bchan.minSpinBox.blockSignals(True)
            self.bchan.minSpinBox.setValue(zone.bmin)
            self.bchan.histogram.setClipMinimum(zone.bmin)
            self.bchan.minSpinBox.blockSignals(False)

            self.bchan.maxSpinBox.blockSignals(True)
            self.bchan.maxSpinBox.setValue(zone.bmax)
            self.bchan.histogram.setClipMaximum(zone.bmax)
            self.bchan.histogram.setGamma(zone.bgamma * zone.gamma)
            self.bchan.maxSpinBox.blockSignals(False)

            self.bchan.gammaSpinBox.blockSignals(True)
            self.bchan.gammaSpinBox.setValue(zone.bgamma)
            self.bchan.gammaSpinBox.blockSignals(False)

            self.gammaSpinBox.blockSignals(True)
            self.gammaSpinBox.setValue(zone.gamma)
            self.gammaSpinBox.blockSignals(False)

        self.updateStats()

    def updateStats(self):
        if self.zonecopy.histogram is not None:
            R, G, B = self.zonecopy.histogram
            self.rchan.setHistogram(R)
            self.gchan.setHistogram(G)
            self.bchan.setHistogram(B)

            self.suggBtn.setEnabled(True)
            gamma = self.autogamma()
            self.suggBtn.setText(f"Suggestion: {gamma:.2f}")

            if not self.zonecopy.transition:
                N = arange(0, 256, 0.25)
                Ia = -log(1 - N / 256) / log(2)

                rmin = self.zonecopy.rmin
                rmax = self.zonecopy.rmax
                Nr = (N.clip(min=rmin, max=rmax - 0.25) - rmin) / (rmax - rmin)
                Ir = -self.zonecopy.gamma * \
                    self.zonecopy.rgamma*log(1 - Nr)/log(2)

                gmin = self.zonecopy.gmin
                gmax = self.zonecopy.gmax
                Ng = (N.clip(min=gmin, max=gmax - 0.25) - gmin) / (gmax - gmin)
                Ig = -self.zonecopy.gamma * \
                    self.zonecopy.ggamma*log(1 - Ng)/log(2)

                bmin = self.zonecopy.bmin
                bmax = self.zonecopy.bmax
                Nb = (N.clip(min=bmin, max=bmax - 0.25) - bmin) / (bmax - bmin)
                Ib = -self.zonecopy.gamma * \
                    self.zonecopy.bgamma*log(1 - Nb)/log(2)

                self.redAvgIntensIn.setText(
                    f"Avg Red Intensity: {(Ia*R).sum()/R.sum():.2f}")
                self.greenAvgIntensIn.setText(
                    f"Avg Green Intensity: {(Ia*G).sum()/G.sum():.2f}")
                self.blueAvgIntensIn.setText(
                    f"Avg Blue Intensity: {(Ia*B).sum()/B.sum():.2f}")

                self.redAvgIntensOut.setText(
                    f"Avg Red Intensity: {(Ir*R).sum()/R.sum():.2f}")
                self.greenAvgIntensOut.setText(
                    f"Avg Green Intensity: {(Ig*G).sum()/G.sum():.2f}")
                self.blueAvgIntensOut.setText(
                    f"Avg Blue Intensity: {(Ib*B).sum()/B.sum():.2f}")

            else:
                self.redAvgIntensOut.setText("Avg Red Intensity: —")
                self.greenAvgIntensOut.setText("Avg Green Intensity: —")
                self.blueAvgIntensOut.setText("Avg Blue Intensity: —")

                self.redAvgIntensIn.setText("Avg Red Intensity: —")
                self.greenAvgIntensIn.setText("Avg Green Intensity: —")
                self.blueAvgIntensIn.setText("Avg Blue Intensity: —")

        else:
            self.rchan.setHistogram(None)
            self.gchan.setHistogram(None)
            self.bchan.setHistogram(None)

            self.suggBtn.setEnabled(False)
            self.suggBtn.setText("No Suggestion")

            self.redAvgIntensOut.setText("Avg Red Intensity: —")
            self.greenAvgIntensOut.setText("Avg Green Intensity: —")
            self.blueAvgIntensOut.setText("Avg Blue Intensity: —")

            self.redAvgIntensIn.setText("Avg Red Intensity: —")
            self.greenAvgIntensIn.setText("Avg Green Intensity: —")
            self.blueAvgIntensIn.setText("Avg Blue Intensity: —")

    def updateZoneValues(self):
        self.zonecopy.transition = bool(self.transitionCheckBox.checkState())

        if self.zonecopy is not None and not self.zonecopy.transition:
            self.zonecopy.rmin = self.rchan.minSpinBox.value()
            self.zonecopy.rgamma = self.rchan.gammaSpinBox.value()
            self.zonecopy.rmax = self.rchan.maxSpinBox.value()

            self.zonecopy.gmin = self.gchan.minSpinBox.value()
            self.zonecopy.ggamma = self.gchan.gammaSpinBox.value()
            self.zonecopy.gmax = self.gchan.maxSpinBox.value()

            self.zonecopy.bmin = self.bchan.minSpinBox.value()
            self.zonecopy.bgamma = self.bchan.gammaSpinBox.value()
            self.zonecopy.bmax = self.bchan.maxSpinBox.value()

            self.zonecopy.gamma = self.gammaSpinBox.value()

        self.updateStats()
        self.loadFrame(self.slider.slider.value(),
                       self.slider.currentTime.time())

    def widgetValuesChanged(self):
        self.updateColors()
        self.updateZoneValues()
        self.zoneModified()

        zone = self.zonecopy
        self.rchan.histogram.setGamma(zone.rgamma * zone.gamma)
        self.gchan.histogram.setGamma(zone.ggamma * zone.gamma)
        self.bchan.histogram.setGamma(zone.bgamma * zone.gamma)

    def apply(self):
        self.updateZoneValues()
        super().apply()
Example #12
0
class WeatherStationBrowser(QWidget):
    """
    Widget that allows the user to browse and select ECCC climate stations.
    """

    ConsoleSignal = QSignal(str)
    staListSignal = QSignal(list)

    PROV_NAME = [x[0].title() for x in PROV_NAME_ABB]
    PROV_ABB = [x[1] for x in PROV_NAME_ABB]

    def __init__(self, parent=None):
        super(WeatherStationBrowser, self).__init__(parent)
        self.stn_finder_worker = WeatherStationFinder()
        self.stn_finder_worker.sig_load_database_finished.connect(
                self.receive_load_database)
        self.stn_finder_thread = QThread()
        self.stn_finder_worker.moveToThread(self.stn_finder_thread)

        self.station_table = WeatherSationView()
        self.waitspinnerbar = WaitSpinnerBar()
        self.stn_finder_worker.sig_progress_msg.connect(
                self.waitspinnerbar.set_label)
        self.__initUI__()

        self.start_load_database()

    def __initUI__(self):
        self.setWindowTitle('Weather Stations Browser')
        self.setWindowIcon(icons.get_icon('master'))
        self.setWindowFlags(Qt.Window)

        now = datetime.now()

        # ---- Tab Widget Search

        # ---- Proximity filter groupbox

        label_Lat = QLabel('Latitude :')
        label_Lat2 = QLabel('North')

        self.lat_spinBox = QDoubleSpinBox()
        self.lat_spinBox.setAlignment(Qt.AlignCenter)
        self.lat_spinBox.setSingleStep(0.1)
        self.lat_spinBox.setValue(0)
        self.lat_spinBox.setMinimum(0)
        self.lat_spinBox.setMaximum(180)
        self.lat_spinBox.setSuffix(u' °')
        self.lat_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        label_Lon = QLabel('Longitude :')
        label_Lon2 = QLabel('West')

        self.lon_spinBox = QDoubleSpinBox()
        self.lon_spinBox.setAlignment(Qt.AlignCenter)
        self.lon_spinBox.setSingleStep(0.1)
        self.lon_spinBox.setValue(0)
        self.lon_spinBox.setMinimum(0)
        self.lon_spinBox.setMaximum(180)
        self.lon_spinBox.setSuffix(u' °')
        self.lon_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        self.radius_SpinBox = QComboBox()
        self.radius_SpinBox.addItems(['25 km', '50 km', '100 km', '200 km'])
        self.radius_SpinBox.currentIndexChanged.connect(
                self.search_filters_changed)

        prox_search_grid = QGridLayout()
        row = 0
        prox_search_grid.addWidget(label_Lat, row, 1)
        prox_search_grid.addWidget(self.lat_spinBox, row, 2)
        prox_search_grid.addWidget(label_Lat2, row, 3)
        row += 1
        prox_search_grid.addWidget(label_Lon, row, 1)
        prox_search_grid.addWidget(self.lon_spinBox, row, 2)
        prox_search_grid.addWidget(label_Lon2, row, 3)
        row += 1
        prox_search_grid.addWidget(QLabel('Search Radius :'), row, 1)
        prox_search_grid.addWidget(self.radius_SpinBox, row, 2)

        prox_search_grid.setColumnStretch(0, 100)
        prox_search_grid.setColumnStretch(4, 100)
        prox_search_grid.setRowStretch(row+1, 100)
        prox_search_grid.setHorizontalSpacing(20)
        prox_search_grid.setContentsMargins(10, 10, 10, 10)  # (L, T, R, B)

        self.prox_grpbox = QGroupBox("Proximity filter :")
        self.prox_grpbox.setCheckable(True)
        self.prox_grpbox.setChecked(False)
        self.prox_grpbox.toggled.connect(self.proximity_grpbox_toggled)
        self.prox_grpbox.setLayout(prox_search_grid)

        # ---- Province filter

        prov_names = ['All']
        prov_names.extend(self.PROV_NAME)
        self.prov_widg = QComboBox()
        self.prov_widg.addItems(prov_names)
        self.prov_widg.setCurrentIndex(0)
        self.prov_widg.currentIndexChanged.connect(self.search_filters_changed)

        layout = QGridLayout()
        layout.addWidget(self.prov_widg, 2, 1)
        layout.setColumnStretch(2, 100)
        layout.setVerticalSpacing(10)

        prov_grpbox = QGroupBox("Province filter :")
        prov_grpbox.setLayout(layout)

        # ---- Data availability filter

        # Number of years with data

        self.nbrYear = QSpinBox()
        self.nbrYear.setAlignment(Qt.AlignCenter)
        self.nbrYear.setSingleStep(1)
        self.nbrYear.setMinimum(0)
        self.nbrYear.setValue(3)
        self.nbrYear.valueChanged.connect(self.search_filters_changed)

        subgrid1 = QGridLayout()
        subgrid1.addWidget(self.nbrYear, 0, 0)
        subgrid1.addWidget(QLabel('years of data between'), 0, 1)

        subgrid1.setHorizontalSpacing(10)
        subgrid1.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        subgrid1.setColumnStretch(2, 100)

        # Year range

        self.minYear = QSpinBox()
        self.minYear.setAlignment(Qt.AlignCenter)
        self.minYear.setSingleStep(1)
        self.minYear.setMinimum(1840)
        self.minYear.setMaximum(now.year)
        self.minYear.setValue(1840)
        self.minYear.valueChanged.connect(self.minYear_changed)

        label_and = QLabel('and')
        label_and.setAlignment(Qt.AlignCenter)

        self.maxYear = QSpinBox()
        self.maxYear.setAlignment(Qt.AlignCenter)
        self.maxYear.setSingleStep(1)
        self.maxYear.setMinimum(1840)
        self.maxYear.setMaximum(now.year)
        self.maxYear.setValue(now.year)
        self.maxYear.valueChanged.connect(self.maxYear_changed)

        subgrid2 = QGridLayout()
        subgrid2.addWidget(self.minYear, 0, 0)
        subgrid2.addWidget(label_and, 0, 1)
        subgrid2.addWidget(self.maxYear, 0, 2)

        subgrid2.setHorizontalSpacing(10)
        subgrid2.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)
        subgrid2.setColumnStretch(4, 100)

        # Subgridgrid assembly

        grid = QGridLayout()

        grid.addWidget(QLabel('Search for stations with at least'), 0, 0)
        grid.addLayout(subgrid1, 1, 0)
        grid.addLayout(subgrid2, 2, 0)

        grid.setVerticalSpacing(5)
        grid.setRowStretch(0, 100)
        # grid.setContentsMargins(0, 0, 0, 0)  # (L, T, R, B)

        self.year_widg = QGroupBox("Data Availability filter :")
        self.year_widg.setLayout(grid)

        # ---- Toolbar

        self.btn_addSta = btn_addSta = QPushButton('Add')
        btn_addSta.setIcon(icons.get_icon('add2list'))
        btn_addSta.setIconSize(icons.get_iconsize('small'))
        btn_addSta.setToolTip('Add selected found weather stations to the '
                              'current list of weather stations.')
        btn_addSta.clicked.connect(self.btn_addSta_isClicked)

        btn_save = QPushButton('Save')
        btn_save.setIcon(icons.get_icon('save'))
        btn_save.setIconSize(icons.get_iconsize('small'))
        btn_save.setToolTip('Save current found stations info in a csv file.')
        btn_save.clicked.connect(self.btn_save_isClicked)

        self.btn_fetch = btn_fetch = QPushButton('Fetch')
        btn_fetch.setIcon(icons.get_icon('refresh'))
        btn_fetch.setIconSize(icons.get_iconsize('small'))
        btn_fetch.setToolTip("Updates the climate station database by"
                             " fetching it again from the ECCC ftp server.")
        btn_fetch.clicked.connect(self.btn_fetch_isClicked)

        toolbar_grid = QGridLayout()
        toolbar_widg = QWidget()

        for col, btn in enumerate([btn_addSta, btn_save, btn_fetch]):
            toolbar_grid.addWidget(btn, 0, col+1)

        toolbar_grid.setColumnStretch(toolbar_grid.columnCount(), 100)
        toolbar_grid.setSpacing(5)
        toolbar_grid.setContentsMargins(0, 30, 0, 0)  # (L, T, R, B)

        toolbar_widg.setLayout(toolbar_grid)

        # ---- Left Panel

        panel_title = QLabel('<b>Weather Station Search Criteria :</b>')

        left_panel = QFrame()
        left_panel_grid = QGridLayout()

        left_panel_grid.addWidget(panel_title, 0, 0)
        left_panel_grid.addWidget(self.prox_grpbox, 1, 0)
        left_panel_grid.addWidget(prov_grpbox, 2, 0)
        left_panel_grid.addWidget(self.year_widg, 3, 0)
        left_panel_grid.setRowStretch(4, 100)
        left_panel_grid.addWidget(toolbar_widg, 5, 0)

        left_panel_grid.setVerticalSpacing(20)
        left_panel_grid.setContentsMargins(0, 0, 0, 0)   # (L, T, R, B)
        left_panel.setLayout(left_panel_grid)

        # ----- Main grid

        # Widgets

        vLine1 = QFrame()
        vLine1.setFrameStyle(StyleDB().VLine)

        # Grid

        main_layout = QGridLayout(self)

        main_layout.addWidget(left_panel, 0, 0)
        main_layout.addWidget(vLine1, 0, 1)
        main_layout.addWidget(self.station_table, 0, 2)
        main_layout.addWidget(self.waitspinnerbar, 0, 2)

        main_layout.setContentsMargins(10, 10, 10, 10)  # (L,T,R,B)
        main_layout.setRowStretch(0, 100)
        main_layout.setHorizontalSpacing(15)
        main_layout.setVerticalSpacing(5)
        main_layout.setColumnStretch(col, 100)

    @property
    def stationlist(self):
        return self.station_table.get_stationlist()

    @property
    def search_by(self):
        return ['proximity', 'province'][self.tab_widg.currentIndex()]

    @property
    def prov(self):
        if self.prov_widg.currentIndex() == 0:
            return self.PROV_ABB
        else:
            return self.PROV_ABB[self.prov_widg.currentIndex()-1]

    @property
    def lat(self):
        return self.lat_spinBox.value()

    def set_lat(self, x, silent=True):
        if silent:
            self.lat_spinBox.blockSignals(True)
        self.lat_spinBox.setValue(x)
        self.lat_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def lon(self):
        return self.lon_spinBox.value()

    def set_lon(self, x, silent=True):
        if silent:
            self.lon_spinBox.blockSignals(True)
        self.lon_spinBox.setValue(x)
        self.lon_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def rad(self):
        return int(self.radius_SpinBox.currentText()[:-3])

    @property
    def prox(self):
        if self.prox_grpbox.isChecked():
            return (self.lat, -self.lon, self.rad)
        else:
            return None

    @property
    def year_min(self):
        return int(self.minYear.value())

    def set_yearmin(self, x, silent=True):
        if silent:
            self.minYear.blockSignals(True)
        self.minYear.setValue(x)
        self.minYear.blockSignals(False)

    @property
    def year_max(self):
        return int(self.maxYear.value())

    def set_yearmax(self, x, silent=True):
        if silent:
            self.maxYear.blockSignals(True)
        self.maxYear.setValue(x)
        self.maxYear.blockSignals(False)

    @property
    def nbr_of_years(self):
        return int(self.nbrYear.value())

    def set_yearnbr(self, x, silent=True):
        if silent:
            self.nbrYear.blockSignals(True)
        self.nbrYear.setValue(x)
        self.nbrYear.blockSignals(False)

    # ---- Weather Station Finder Handlers

    def start_load_database(self, force_fetch=False):
        """Start the process of loading the climate station database."""
        if self.stn_finder_thread.isRunning():
            return

        self.station_table.clear()
        self.waitspinnerbar.show()

        # Start the downloading process.
        if force_fetch:
            self.stn_finder_thread.started.connect(
                    self.stn_finder_worker.fetch_database)
        else:
            self.stn_finder_thread.started.connect(
                    self.stn_finder_worker.load_database)
        self.stn_finder_thread.start()

    @QSlot()
    def receive_load_database(self):
        """Handles when loading the database is finished."""
        # Disconnect the thread.
        self.stn_finder_thread.started.disconnect()

        # Quit the thread.
        self.stn_finder_thread.quit()
        waittime = 0
        while self.stn_finder_thread.isRunning():
            sleep(0.1)
            waittime += 0.1
            if waittime > 15:                                # pragma: no cover
                print("Unable to quit the thread.")
                break
        # Force an update of the GUI.
        self.proximity_grpbox_toggled()
        if self.stn_finder_worker.data is None:
            self.waitspinnerbar.show_warning_icon()
        else:
            self.waitspinnerbar.hide()

    # ---- GUI handlers

    def show(self):
        super(WeatherStationBrowser, self).show()
        qr = self.frameGeometry()
        if self.parent():
            parent = self.parent()
            wp = parent.frameGeometry().width()
            hp = parent.frameGeometry().height()
            cp = parent.mapToGlobal(QPoint(wp/2, hp/2))
        else:
            cp = QDesktopWidget().availableGeometry().center()

        qr.moveCenter(cp)
        self.move(qr.topLeft())

    # -------------------------------------------------------------------------

    def minYear_changed(self):
        min_yr = min_yr = max(self.minYear.value(), 1840)

        now = datetime.now()
        max_yr = now.year

        self.maxYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    def maxYear_changed(self):
        min_yr = 1840

        now = datetime.now()
        max_yr = min(self.maxYear.value(), now.year)

        self.minYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    # ---- Toolbar Buttons Handlers

    def btn_save_isClicked(self):
        ddir = os.path.join(os.getcwd(), 'weather_station_list.csv')
        filename, ftype = QFileDialog().getSaveFileName(
                self, 'Save normals', ddir, '*.csv;;*.xlsx;;*.xls')
        self.station_table.save_stationlist(filename)

    def btn_addSta_isClicked(self):
        rows = self.station_table.get_checked_rows()
        if len(rows) > 0:
            staList = self.station_table.get_content4rows(rows)
            self.staListSignal.emit(staList)
            print('Selected stations sent to list')
        else:
            print('No station currently selected')

    def btn_fetch_isClicked(self):
        """Handles when the button fetch is clicked."""
        self.start_load_database(force_fetch=True)

    # ---- Search Filters Handlers

    def proximity_grpbox_toggled(self):
        """
        Set the values for the reference geo coordinates that are used in the
        WeatherSationView to calculate the proximity values and forces a
        refresh of the content of the table.
        """
        if self.prox_grpbox.isChecked():
            self.station_table.set_geocoord((self.lat, -self.lon))
        else:
            self.station_table.set_geocoord(None)
        self.search_filters_changed()

    def search_filters_changed(self):
        """
        Search for weather stations with the current filter values and forces
        an update of the station table content.
        """
        if self.stn_finder_worker.data is not None:
            stnlist = self.stn_finder_worker.get_stationlist(
                    prov=self.prov, prox=self.prox,
                    yrange=(self.year_min, self.year_max, self.nbr_of_years))
            self.station_table.populate_table(stnlist)
Example #13
0
class WeatherStationDownloader(QMainWindow):
    """
    Widget that allows the user to browse and select ECCC climate stations.
    """
    sig_download_process_ended = QSignal()
    ConsoleSignal = QSignal(str)
    staListSignal = QSignal(list)

    PROV_NAME = [x[0].title() for x in PROV_NAME_ABB]
    PROV_ABB = [x[1] for x in PROV_NAME_ABB]

    def __init__(self, parent=None, workdir=None):
        super().__init__(parent)
        self.workdir = workdir or get_home_dir()

        self.stn_finder_worker = WeatherStationFinder()
        self.stn_finder_worker.sig_load_database_finished.connect(
            self.receive_load_database)
        self.stn_finder_thread = QThread()
        self.stn_finder_worker.moveToThread(self.stn_finder_thread)
        self._database_isloading = False

        self.station_table = WeatherSationView()
        self.waitspinnerbar = WaitSpinnerBar()
        self.stn_finder_worker.sig_progress_msg.connect(
            self.waitspinnerbar.set_label)

        self.__initUI__()
        self._restore_window_geometry()

        # Setup the raw data downloader.
        self._dwnld_inprogress = False
        self._dwnld_stations_list = []
        self.dwnld_thread = QThread()
        self.dwnld_worker = RawDataDownloader()
        self.dwnld_worker.moveToThread(self.dwnld_thread)

        self.dwnld_worker.sig_download_finished.connect(
            self.process_station_data)
        self.dwnld_worker.sig_update_pbar.connect(self.progressbar.setValue)

        self.start_load_database()

    def __initUI__(self):
        self.setWindowTitle('Download Weather Data')
        self.setWindowIcon(get_icon('master'))
        self.setWindowFlags(Qt.Window)

        now = datetime.now()

        # Setup the proximity filter group.
        self.lat_spinBox = QDoubleSpinBox()
        self.lat_spinBox.setAlignment(Qt.AlignCenter)
        self.lat_spinBox.setSingleStep(0.1)
        self.lat_spinBox.setDecimals(3)
        self.lat_spinBox.setValue(CONF.get('download_data', 'latitude', 0))
        self.lat_spinBox.setMinimum(0)
        self.lat_spinBox.setMaximum(180)
        self.lat_spinBox.setSuffix(u' °')
        self.lat_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        self.lon_spinBox = QDoubleSpinBox()
        self.lon_spinBox.setAlignment(Qt.AlignCenter)
        self.lon_spinBox.setSingleStep(0.1)
        self.lon_spinBox.setDecimals(3)
        self.lon_spinBox.setValue(CONF.get('download_data', 'longitude', 0))
        self.lon_spinBox.setMinimum(0)
        self.lon_spinBox.setMaximum(180)
        self.lon_spinBox.setSuffix(u' °')
        self.lon_spinBox.valueChanged.connect(self.proximity_grpbox_toggled)

        self.radius_SpinBox = QComboBox()
        self.radius_SpinBox.addItems(['25 km', '50 km', '100 km', '200 km'])
        self.radius_SpinBox.setCurrentIndex(
            CONF.get('download_data', 'radius_index', 0))
        self.radius_SpinBox.currentIndexChanged.connect(
            self.search_filters_changed)

        self.prox_grpbox = QGroupBox("Proximity Filter")
        self.prox_grpbox.setCheckable(True)
        self.prox_grpbox.setChecked(
            CONF.get('download_data', 'proximity_filter', False))
        self.prox_grpbox.toggled.connect(self.proximity_grpbox_toggled)

        prox_search_grid = QGridLayout(self.prox_grpbox)
        prox_search_grid.addWidget(QLabel('Latitude:'), 0, 0)
        prox_search_grid.addWidget(self.lat_spinBox, 0, 1)
        prox_search_grid.addWidget(QLabel('North'), 0, 2)
        prox_search_grid.addWidget(QLabel('Longitude:'), 1, 0)
        prox_search_grid.addWidget(self.lon_spinBox, 1, 1)
        prox_search_grid.addWidget(QLabel('West'), 1, 2)
        prox_search_grid.addWidget(QLabel('Search Radius:'), 2, 0)
        prox_search_grid.addWidget(self.radius_SpinBox, 2, 1)
        prox_search_grid.setColumnStretch(0, 100)
        prox_search_grid.setRowStretch(3, 100)

        # ---- Province filter
        self.prov_widg = QComboBox()
        self.prov_widg.addItems(['All'] + self.PROV_NAME)
        self.prov_widg.setCurrentIndex(
            CONF.get('download_data', 'province_index', 0))
        self.prov_widg.currentIndexChanged.connect(self.search_filters_changed)

        prov_grpbox = QGroupBox()
        prov_layout = QGridLayout(prov_grpbox)
        prov_layout.addWidget(QLabel('Province:'), 0, 0)
        prov_layout.addWidget(self.prov_widg, 0, 1)
        prov_layout.setColumnStretch(0, 1)
        prov_layout.setRowStretch(1, 1)

        # ---- Data availability filter

        # Number of years with data
        self.nbrYear = QSpinBox()
        self.nbrYear.setAlignment(Qt.AlignCenter)
        self.nbrYear.setSingleStep(1)
        self.nbrYear.setMinimum(0)
        self.nbrYear.setValue(CONF.get('download_data', 'min_nbr_of_years', 3))
        self.nbrYear.valueChanged.connect(self.search_filters_changed)

        subgrid1 = QGridLayout()
        subgrid1.setContentsMargins(0, 0, 0, 0)
        subgrid1.addWidget(QLabel('for at least'), 0, 0)
        subgrid1.addWidget(self.nbrYear, 0, 1)
        subgrid1.addWidget(QLabel('year(s)'), 0, 2)
        subgrid1.setColumnStretch(3, 100)
        subgrid1.setHorizontalSpacing(5)

        # Year range
        self.minYear = QSpinBox()
        self.minYear.setAlignment(Qt.AlignCenter)
        self.minYear.setSingleStep(1)
        self.minYear.setMinimum(1840)
        self.minYear.setMaximum(now.year)
        self.minYear.setValue(
            CONF.get('download_data', 'year_range_left_bound', 1840))
        self.minYear.valueChanged.connect(self.minYear_changed)

        label_and = QLabel('and')
        label_and.setAlignment(Qt.AlignCenter)

        self.maxYear = QSpinBox()
        self.maxYear.setAlignment(Qt.AlignCenter)
        self.maxYear.setSingleStep(1)
        self.maxYear.setMinimum(1840)
        self.maxYear.setMaximum(now.year)
        self.maxYear.setValue(
            CONF.get('download_data', 'year_range_right_bound', now.year))

        self.maxYear.valueChanged.connect(self.maxYear_changed)

        subgrid2 = QGridLayout()
        subgrid2.addWidget(QLabel('between'), 0, 0)
        subgrid2.addWidget(self.minYear, 0, 1)
        subgrid2.addWidget(label_and, 0, 2)
        subgrid2.addWidget(self.maxYear, 0, 3)
        subgrid2.setContentsMargins(0, 0, 0, 0)
        subgrid2.setColumnStretch(4, 100)
        subgrid2.setHorizontalSpacing(5)

        # Subgridgrid assembly
        self.year_widg = QGroupBox("Data Availability filter")
        self.year_widg.setCheckable(True)
        self.year_widg.setChecked(
            CONF.get('download_data', 'data_availability_filter', False))
        self.year_widg.toggled.connect(self.search_filters_changed)

        grid = QGridLayout(self.year_widg)
        grid.setRowMinimumHeight(0, 10)
        grid.addWidget(QLabel('Search for stations with data available'), 1, 0)
        grid.addLayout(subgrid1, 2, 0)
        grid.addLayout(subgrid2, 3, 0)
        grid.setRowStretch(4, 100)
        grid.setVerticalSpacing(8)

        # Setup the toolbar.
        self.btn_addSta = QPushButton('Add')
        self.btn_addSta.setIcon(get_icon('add2list'))
        self.btn_addSta.setIconSize(get_iconsize('small'))
        self.btn_addSta.setToolTip(
            'Add selected stations to the current list of weather stations.')
        self.btn_addSta.clicked.connect(self.btn_addSta_isClicked)
        self.btn_addSta.hide()

        btn_save = QPushButton('Save')
        btn_save.setToolTip('Save the list of selected stations to a file.')
        btn_save.clicked.connect(self.btn_save_isClicked)
        btn_save.hide()

        self.btn_download = QPushButton('Download')
        self.btn_download.clicked.connect(self.start_download_process)

        btn_close = QPushButton('Close')
        btn_close.clicked.connect(self.close)

        self.btn_fetch = btn_fetch = QPushButton('Refresh')
        btn_fetch.setToolTip(
            "Update the list of climate stations by fetching it from "
            "the ECCC remote location.")
        btn_fetch.clicked.connect(self.btn_fetch_isClicked)

        toolbar_widg = QWidget()
        toolbar_grid = QGridLayout(toolbar_widg)
        toolbar_grid.addWidget(self.btn_addSta, 1, 1)
        toolbar_grid.addWidget(btn_save, 1, 2)
        toolbar_grid.addWidget(btn_fetch, 1, 3)
        toolbar_grid.addWidget(self.btn_download, 1, 4)
        toolbar_grid.addWidget(btn_close, 1, 5)
        toolbar_grid.setColumnStretch(0, 100)
        toolbar_grid.setContentsMargins(0, 10, 0, 0)

        # Setup the left panel.
        self.left_panel = QFrame()
        left_panel_grid = QGridLayout(self.left_panel)
        left_panel_grid.setContentsMargins(0, 0, 0, 0)
        left_panel_grid.addWidget(QLabel('Search Criteria'), 0, 0)
        left_panel_grid.addWidget(prov_grpbox, 1, 0)
        left_panel_grid.addWidget(self.prox_grpbox, 2, 0)
        left_panel_grid.addWidget(self.year_widg, 3, 0)
        left_panel_grid.setRowStretch(3, 100)

        # Setup the progress bar.
        self.progressbar = QProgressBar()
        self.progressbar.setValue(0)
        self.progressbar.hide()

        # Setup the central widget.
        main_widget = QWidget()
        main_layout = QGridLayout(main_widget)
        main_layout.addWidget(self.left_panel, 0, 0)
        main_layout.addWidget(self.station_table, 0, 1)
        main_layout.addWidget(self.waitspinnerbar, 0, 1)
        main_layout.addWidget(toolbar_widg, 1, 0, 1, 2)
        main_layout.addWidget(self.progressbar, 2, 0, 1, 2)
        main_layout.setColumnStretch(1, 100)

        self.setCentralWidget(main_widget)

    @property
    def stationlist(self):
        return self.station_table.get_stationlist()

    @property
    def search_by(self):
        return ['proximity', 'province'][self.tab_widg.currentIndex()]

    @property
    def prov(self):
        if self.prov_widg.currentIndex() == 0:
            return self.PROV_NAME
        else:
            return [self.PROV_NAME[self.prov_widg.currentIndex() - 1]]

    @property
    def lat(self):
        return self.lat_spinBox.value()

    def set_lat(self, x, silent=True):
        if silent:
            self.lat_spinBox.blockSignals(True)
        self.lat_spinBox.setValue(x)
        self.lat_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def lon(self):
        return self.lon_spinBox.value()

    def set_lon(self, x, silent=True):
        if silent:
            self.lon_spinBox.blockSignals(True)
        self.lon_spinBox.setValue(x)
        self.lon_spinBox.blockSignals(False)
        self.proximity_grpbox_toggled()

    @property
    def rad(self):
        return int(self.radius_SpinBox.currentText()[:-3])

    @property
    def prox(self):
        if self.prox_grpbox.isChecked():
            return (self.lat, -self.lon, self.rad)
        else:
            return None

    @property
    def year_min(self):
        return int(self.minYear.value())

    def set_yearmin(self, x, silent=True):
        if silent:
            self.minYear.blockSignals(True)
        self.minYear.setValue(x)
        self.minYear.blockSignals(False)

    @property
    def year_max(self):
        return int(self.maxYear.value())

    def set_yearmax(self, x, silent=True):
        if silent:
            self.maxYear.blockSignals(True)
        self.maxYear.setValue(x)
        self.maxYear.blockSignals(False)

    @property
    def nbr_of_years(self):
        return int(self.nbrYear.value())

    def set_yearnbr(self, x, silent=True):
        if silent:
            self.nbrYear.blockSignals(True)
        self.nbrYear.setValue(x)
        self.nbrYear.blockSignals(False)

    # ---- Load Station Database
    def start_load_database(self, force_fetch=False):
        """Start the process of loading the climate station database."""
        if self._database_isloading is False:
            self._database_isloading = True

            self.station_table.clear()
            self.waitspinnerbar.show()

            # Start the downloading process.
            if force_fetch:
                self.stn_finder_thread.started.connect(
                    self.stn_finder_worker.fetch_database)
            else:
                self.stn_finder_thread.started.connect(
                    self.stn_finder_worker.load_database)
            self.stn_finder_thread.start()

    @QSlot()
    def receive_load_database(self):
        """Handles when loading the database is finished."""
        # Disconnect the thread.
        self.stn_finder_thread.started.disconnect()

        # Quit the thread.
        self.stn_finder_thread.quit()
        waittime = 0
        while self.stn_finder_thread.isRunning():
            sleep(0.1)
            waittime += 0.1
            if waittime > 15:
                print("Unable to quit the thread.")
                break
        # Force an update of the GUI.
        self.proximity_grpbox_toggled()
        if self.stn_finder_worker.data is None:
            self.waitspinnerbar.show_warning_icon()
        else:
            self.waitspinnerbar.hide()
        self._database_isloading = False

    # ---- GUI handlers
    def minYear_changed(self):
        min_yr = min_yr = max(self.minYear.value(), 1840)

        now = datetime.now()
        max_yr = now.year

        self.maxYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    def maxYear_changed(self):
        min_yr = 1840

        now = datetime.now()
        max_yr = min(self.maxYear.value(), now.year)

        self.minYear.setRange(min_yr, max_yr)
        self.search_filters_changed()

    # ---- Toolbar Buttons Handlers
    def btn_save_isClicked(self):
        ddir = os.path.join(os.getcwd(), 'weather_station_list.csv')
        filename, ftype = QFileDialog().getSaveFileName(
            self, 'Save normals', ddir, '*.csv;;*.xlsx;;*.xls')
        self.station_table.save_stationlist(filename)

    def btn_addSta_isClicked(self):
        rows = self.station_table.get_checked_rows()
        if len(rows) > 0:
            staList = self.station_table.get_content4rows(rows)
            self.staListSignal.emit(staList)
            print('Selected stations sent to list')
        else:
            print('No station currently selected')

    def btn_fetch_isClicked(self):
        """Handles when the button fetch is clicked."""
        self.start_load_database(force_fetch=True)

    # ---- Search Filters Handlers
    def proximity_grpbox_toggled(self):
        """
        Set the values for the reference geo coordinates that are used in the
        WeatherSationView to calculate the proximity values and forces a
        refresh of the content of the table.
        """
        if self.prox_grpbox.isChecked():
            self.station_table.set_geocoord((self.lat, -self.lon))
        else:
            self.station_table.set_geocoord(None)
        self.search_filters_changed()

    def search_filters_changed(self):
        """
        Search for weather stations with the current filter values and forces
        an update of the station table content.
        """
        if self.stn_finder_worker.data is not None:
            stnlist = self.stn_finder_worker.get_stationlist(
                prov=self.prov,
                prox=self.prox,
                yrange=((self.year_min, self.year_max, self.nbr_of_years)
                        if self.year_widg.isChecked() else None))
            self.station_table.populate_table(stnlist)

    # ---- Download weather data
    def start_download_process(self):
        """Start the downloading process of raw weather data files."""
        if self._dwnld_inprogress is True:
            self.stop_download_process()
            return

        # Grab the info of the weather stations that are selected.
        rows = self.station_table.get_checked_rows()
        self._dwnld_stations_list = self.station_table.get_content4rows(rows)
        if len(self._dwnld_stations_list) == 0:
            QMessageBox.warning(self, 'Warning',
                                "No weather station currently selected.",
                                QMessageBox.Ok)
            return

        # Update the UI.
        self.progressbar.show()
        self.btn_download.setText("Cancel")
        self.left_panel.setEnabled(False)
        self.station_table.setEnabled(False)
        self.btn_fetch.setEnabled(False)

        # Set thread working directory.
        self.dwnld_worker.dirname = self.workdir

        # Start downloading data.
        self._dwnld_inprogress = True
        self.download_next_station()

    def stop_download_process(self):
        """Stop the downloading process."""
        print('Stopping the download process...')
        self.dwnld_worker.stop_download()
        self._dwnld_stations_list = []
        self.btn_download.setEnabled(False)

        self.wait_for_thread_to_quit()
        self.btn_download.setEnabled(True)
        self.btn_download.setText("Download")
        self.left_panel.setEnabled(True)
        self.station_table.setEnabled(True)
        self.btn_fetch.setEnabled(True)

        self._dwnld_inprogress = False
        self.sig_download_process_ended.emit()
        print('Download process stopped.')

    def download_next_station(self):
        self.wait_for_thread_to_quit()
        try:
            dwnld_station = self._dwnld_stations_list.pop(0)
        except IndexError:
            # There is no more data to download.
            print('Raw weather data downloaded for all selected stations.')
            self.btn_download.setText("Download")
            self.progressbar.hide()
            self.left_panel.setEnabled(True)
            self.station_table.setEnabled(True)
            self.btn_fetch.setEnabled(True)
            self._dwnld_inprogress = False
            self.sig_download_process_ended.emit()
            return

        # Set worker attributes.
        self.dwnld_worker.StaName = dwnld_station[0]
        self.dwnld_worker.stationID = dwnld_station[1]
        self.dwnld_worker.yr_start = dwnld_station[2]
        self.dwnld_worker.yr_end = dwnld_station[3]
        self.dwnld_worker.climateID = dwnld_station[5]

        # Highlight the row of the next station to download data from.
        self.station_table.selectRow(
            self.station_table.get_row_from_climateid(dwnld_station[5]))

        # Start the downloading process.
        try:
            self.dwnld_thread.started.disconnect(
                self.dwnld_worker.download_data)
        except TypeError:
            # The method self.dwnld_worker.download_data is not connected.
            pass
        finally:
            self.dwnld_thread.started.connect(self.dwnld_worker.download_data)
            self.dwnld_thread.start()

    def wait_for_thread_to_quit(self):
        self.dwnld_thread.quit()
        waittime = 0
        while self.dwnld_thread.isRunning():
            print('Waiting for the downloading thread to close')
            sleep(0.1)
            waittime += 0.1
            if waittime > 15:
                print("Unable to close the weather data dowloader thread.")
                return

    def process_station_data(self, climateid, file_list=None):
        """
        Read, concatenate, and save to csv the raw weather data that were
        just downloaded for the station corresponding to the specified
        climate ID.
        """
        if file_list:
            station_metadata = self.station_table.get_content4rows(
                [self.station_table.get_row_from_climateid(climateid)])[0]
            station_data = read_raw_datafiles(file_list)
            print(
                'Formating and concatenating raw data for station {}.'.format(
                    station_metadata[0]))

            # Define the concatenated filename.
            station_name = (station_metadata[0].replace('\\',
                                                        '_').replace('/', '_'))
            min_year = min(station_data.index).year
            max_year = max(station_data.index).year
            filename = osp.join("%s (%s)_%s-%s.csv" %
                                (station_name, climateid, min_year, max_year))

            # Save the concatenated data to csv.
            data_headers = [
                'Year', 'Month', 'Day', 'Max Temp (°C)', 'Min Temp (°C)',
                'Mean Temp (°C)', 'Total Precip (mm)'
            ]
            fcontent = [['Station Name', station_metadata[0]],
                        ['Province', station_metadata[4]],
                        ['Latitude (dd)', station_metadata[6]],
                        ['Longitude (dd)', station_metadata[7]],
                        ['Elevation (m)', station_metadata[8]],
                        ['Climate Identifier', station_metadata[5]], [],
                        data_headers]
            fcontent = fcontent + station_data[data_headers].values.tolist()

            # Save the data to csv.
            filepath = osp.join(self.dwnld_worker.dirname, filename)
            with open(filepath, 'w', encoding='utf-8') as f:
                writer = csv.writer(f, delimiter=',', lineterminator='\n')
                writer.writerows(fcontent)
        self.download_next_station()

    # ---- Main window settings
    def _restore_window_geometry(self):
        """
        Restore the geometry of this mainwindow from the value saved
        in the config.
        """
        hexstate = CONF.get('download_data', 'window/geometry', None)
        if hexstate:
            hexstate = hexstate_to_qbytearray(hexstate)
            self.restoreGeometry(hexstate)
        else:
            self.resize(1000, 450)

    def _save_window_geometry(self):
        """
        Save the geometry of this mainwindow to the config.
        """
        hexstate = qbytearray_to_hexstate(self.saveGeometry())
        CONF.set('download_data', 'window/geometry', hexstate)

    # ---- Qt overrides
    def closeEvent(self, event):
        self._save_window_geometry()

        # Proximity Filter Options.
        CONF.set('download_data', 'proximity_filter',
                 self.prox_grpbox.isChecked())
        CONF.set('download_data', 'latitude', self.lat)
        CONF.set('download_data', 'longitude', self.lon)
        CONF.set('download_data', 'radius_index',
                 self.radius_SpinBox.currentIndex())
        CONF.set('download_data', 'province_index',
                 self.prov_widg.currentIndex())

        # Data Availability Filter Options.
        CONF.set('download_data', 'data_availability_filter',
                 self.year_widg.isChecked())
        CONF.set('download_data', 'min_nbr_of_years', self.nbrYear.value())
        CONF.set('download_data', 'year_range_left_bound',
                 self.minYear.value())
        CONF.set('download_data', 'year_range_right_bound',
                 self.maxYear.value())
        event.accept()