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")
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)
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")
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)
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
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())
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()
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
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()
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)
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()