class StatusBar(QWidget): """ Use the *sizeChanged* signal for size changes. TODO: specify only isFontTab/isGlyphTab and put the details in the widget internals """ def __init__(self, parent=None): super().__init__(parent) self._shouldPropagateSize = True self.statusLabel = QLabel(self) btnColor = QColor(126, 126, 126) minusButton = Button() minusButton.setDrawingCommands([ QSize(23, 25), (_minPath, '1', btnColor), ]) minusButton.setProperty("delta", -10) minusButton.pressed.connect(self._sizeOffset) self.sizeEdit = QSpinBox(self) self.sizeEdit.setButtonSymbols(QSpinBox.NoButtons) self.sizeEdit.setFixedWidth(56) self.sizeEdit.setFrame(False) self.sizeEdit.lineEdit().setAlignment(Qt.AlignCenter) plusButton = Button() plusButton.setDrawingCommands([ QSize(23, 25), (_plusPath, '1', btnColor), ]) plusButton.setProperty("delta", 10) plusButton.pressed.connect(self._sizeOffset) layout = QHBoxLayout(self) layout.addWidget(self.statusLabel) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) layout.addWidget(spacer) layout.addWidget(minusButton) layout.addWidget(self.sizeEdit) layout.addWidget(plusButton) layout.setContentsMargins(15, 0, 15, 0) layout.setSpacing(0) self.sizeChanged = self.sizeEdit.valueChanged def text(self): return self.statusLabel.text() def setText(self, text): self.statusLabel.setText(text) def textVisible(self): return self.statusLabel.isVisible() def setTextVisible(self, value): self.statusLabel.setVisible(value) def minimumSize(self): return self.sizeEdit.minimum() def setMinimumSize(self, value): self.sizeEdit.blockSignals(True) self.sizeEdit.setMinimum(value) self.sizeEdit.blockSignals(False) def maximumSize(self): return self.sizeEdit.maximum() def setMaximumSize(self, value): self.sizeEdit.blockSignals(True) self.sizeEdit.setMaximum(value) self.sizeEdit.blockSignals(False) def shouldPropagateSize(self): return self._shouldPropagateSize def setShouldPropagateSize(self, value): self._shouldPropagateSize = value def unit(self): return self.sizeEdit.suffix() def setUnit(self, txt): self.sizeEdit.setSuffix(txt) def size(self): return self.sizeEdit.value() def setSize(self, value): value = round(value) self.sizeEdit.blockSignals(True) self.sizeEdit.setValue(value) self.sizeEdit.blockSignals(False) if self._shouldPropagateSize: self.sizeEdit.valueChanged.emit(value) # nudge label w unclamped value self._sliderSizeChanged(value) def paintEvent(self, event): painter = QPainter(self) painter.fillRect(event.rect(), Qt.white) def _sizeOffset(self): delta = self.sender().property("delta") cellsize = self.sizeEdit.value() newValue = cellsize + delta self.sizeEdit.setValue(newValue) def _sliderSizeChanged(self, value): self.sizeEdit.setValue(value)
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 ParameterControlWidget(QWidget): parameter_type_id = None parameter_type_name = None spinbox = None editingFinished = pyqtSignal(QWidget) def __init__(self, parameter_name, parameter_type, parent=None): super(ParameterControlWidget, self).__init__(parent) self.parameter_type_id = parameter_type self.spinbox = QSpinBox(self) # Hier steht im Gegensatz zu C++ -2147483648 weil nicht: http://bit.ly/uflXkq self.spinbox.setMinimum(-2147483648) self.spinbox.setMaximum(2147483647) self.spinbox.editingFinished.connect(self.someEditingFinished) self.spinbox.setFixedWidth(80) layout = QHBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(QLabel(parameter_name + ":")) layout.addWidget(self.spinbox) self.current_value = self.spinbox.value() self.setLayout(layout) def parameterTypeId(self): return self.parameter_type_id def parameterTypeName(self): return self.parameter_type_name def setParameterTypeName(self, parameter_type_name): self.parameter_type_name = parameter_type_name def value(self): return self.spinbox.value() def setValue(self, value): self.current_value = value self.spinbox.blockSignals(True) self.spinbox.setValue(value) self.spinbox.blockSignals(False) def setReadOnly(self, readonly): return self.spinbox.setReadOnly(readonly) @pyqtSlot() def someEditingFinished(self): if self.spinbox.value() != self.current_value: self.current_value = self.spinbox.value() if not self.spinbox.isReadOnly() and self.isEnabled(): self.editingFinished.emit(self) def contextMenuEvent(self, event): # TODO big numbers widget pass
class _CPaginationJumpBar(QWidget): """跳转控件 """ valueChanged = pyqtSignal(int) def __init__(self, totalPages, *args, **kwargs): super(_CPaginationJumpBar, self).__init__(*args, **kwargs) self.setAttribute(Qt.WA_StyledBackground, True) layout = QHBoxLayout(self) layout.setSpacing(4) layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(QLabel('前往', self, alignment=Qt.AlignCenter)) self.editPage = QSpinBox(self) layout.addWidget(self.editPage) layout.addWidget(QLabel('页', self, alignment=Qt.AlignCenter)) self.setTotalPages(totalPages) self.editPage.setObjectName('CPaginationBar_editJump') self.editPage.setFrame(False) self.editPage.setAlignment(Qt.AlignCenter) self.editPage.setButtonSymbols(QSpinBox.NoButtons) self.editPage.editingFinished.connect(self.onValueChanged) # 禁止鼠标滑轮 self.editPage.wheelEvent = self._wheelEvent def _wheelEvent(self, event): event.ignore() def onValueChanged(self): self.valueChanged.emit(self.editPage.value()) def setCurrentPage(self, currentPage): """设置当前页 :param currentPage: """ self.editPage.blockSignals(True) self.editPage.setValue(currentPage) self.editPage.blockSignals(False) def setTotalPages(self, totalPages): """设置最大值 :param totalPages: """ self.totalPages = max(1, totalPages) self.editPage.setRange(1, self.totalPages)
class IntegerOption(GenericOption): def __init__(self, option, container): super().__init__(option, container) self.spinbox = QSpinBox() self.spinbox.setMinimum(option["min"]) self.spinbox.setMaximum(option["max"]) self.spinbox.valueChanged.connect(self.on_change) self.container.addWidget(self.spinbox, self.row, 1) def reload(self, keyboard): value = super().reload(keyboard) self.spinbox.blockSignals(True) self.spinbox.setValue(value) self.spinbox.blockSignals(False) def value(self): return self.spinbox.value() def delete(self): super().delete() self.spinbox.hide() self.spinbox.deleteLater()
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 FrameSelectWidget(QWidget): frameSelectionChanged = pyqtSignal(int, QTime) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setMaximumWidth(960) layout = QVBoxLayout() layout.setContentsMargins(4, 4, 4, 4) layout.setSpacing(4) self.setLayout(layout) self.imageView = QImageView(self) layout.addWidget(self.imageView) self.slider = Slider(self) self.slider.setOrientation(Qt.Horizontal) self.slider.valueChanged.connect(self.handleSliderChange) self.slider.setTickInterval(1) layout.addWidget(self.slider) hlayout = QHBoxLayout() layout.addLayout(hlayout) self.prevLabel = QLabel(self) self.currentIndex = QSpinBox(self) self.currentIndex.valueChanged.connect(self.handleIndexChange) self.currentTime = QTimeEdit(self) self.currentTime.setDisplayFormat("H:mm:ss.zzz") self.currentTime.timeChanged.connect(self.handleTimeChange) self.nextLabel = QLabel(self) hlayout.addWidget(self.prevLabel) hlayout.addStretch() hlayout.addWidget(QLabel("Frame index:", self)) hlayout.addWidget(self.currentIndex) hlayout.addWidget(QLabel("Timestamp:", self)) hlayout.addWidget(self.currentTime) hlayout.addStretch() hlayout.addWidget(self.nextLabel) hlayout = QHBoxLayout() hlayout.setContentsMargins(0, 0, 0, 0) hlayout.setSpacing(4) layout.addLayout(hlayout) self.okayBtn = QPushButton("&OK", self) self.cancelBtn = QPushButton("&Cancel", self) hlayout.addStretch() hlayout.addWidget(self.okayBtn) hlayout.addWidget(self.cancelBtn) self.setFrameSource(None, None) def sizeHint(self): widgetHeights = ( self.slider.height() + max([self.okayBtn.height(), self.cancelBtn.height()]) + max([self.currentIndex.height(), self.currentTime.height()]) ) if isinstance(self.filters, BaseFilter): w, h = self.filters.width, self.filters.height sar = self.filters.sar elif isinstance(self.source, (Track, BaseFilter)): w, h = self.source.width, self.source.height sar = self.source.sar else: return super().sizeHint() dar = w*sar/h W, H = min([ max([(w, h/sar), (w*sar, h)]), (960 - 8, (960 - 8)/dar), ((720 - 20 - widgetHeights)*dar, 720 - 20 - widgetHeights) ]) return QSize(int(W + 8), int(H + 20 + widgetHeights)) def setFrameSource(self, source, filters=None): self.source = source if source is not None: self.slider.setMaximum(self.source.framecount - 1) self.currentIndex.setMaximum(self.source.framecount - 1) self.filters = filters if self.filters is not None: lastpts = self.filters.pts_time[-1] self.slider.setSnapValues(self.filters.keyframes) else: lastpts = self.source.pts_time[-1] if isinstance(self.source, BaseFilter): self.slider.setSnapValues(self.source.keyframes) else: self.slider.setSnapValues(None) ms = int(lastpts*1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.setMaximumTime(QTime(h, m, s, ms)) self.slider.setValue(0) self._frameChange(0) else: self.slider.setSnapValues(None) self.update() def handleIndexChange(self, n): self._frameChange(n) self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) def handleSliderChange(self, n): self._frameChange(n) self.currentIndex.blockSignals(True) self.currentIndex.setValue(n) self.currentIndex.blockSignals(False) def _frameChange(self, n): if self.source is not None: if self.filters is not None: nn = n m = -1 while m < 0 and nn < len(self.filters.indexMap): m = self.filters.indexMap[nn] nn += 1 try: pts = self.filters.pts_time[m] except Exception: pts = None try: frame = next(self.filters.iterFrames( m, whence="framenumber")) except StopIteration: frame = None sar = self.filters.sar else: try: pts = self.source.pts_time[n] except IndexError: pts = None try: frame = next(self.source.iterFrames( n, whence="framenumber")) except StopIteration: frame = None sar = self.source.sar if frame is not None: im = frame.to_image() self.imageView.setFrame(im.toqpixmap()) self.imageView.setSar(sar) if pts is not None: ms = int(pts*1000+0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.blockSignals(True) self.currentTime.setTime(QTime(h, m, s, ms)) self.currentTime.blockSignals(False) self.frameSelectionChanged.emit(n, self.currentTime.time()) def handleTimeChange(self, t): if self.source is not None: if self.filters is not None: pts = t.msecsSinceStartOfDay()/1000 n = self.filters.frameIndexFromPtsTime(pts, dir="-") else: pts = t.msecsSinceStartOfDay()/1000 n = self.source.frameIndexFromPtsTime(pts, dir="-") self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) self.currentIndex.blockSignals(True) self.currentIndex.setValue(n) self.currentIndex.blockSignals(False) self._frameChange(n)
class DistanceIRV2(COMCUPluginBase): NUM_VALUES = 512 DIVIDER = 2**12 // NUM_VALUES def __init__(self, *args): super().__init__(BrickletDistanceIRV2, *args) self.dist = self.device self.cbe_distance = CallbackEmulator(self.dist.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_analog_value = CallbackEmulator(self.dist.get_analog_value, None, self.cb_analog_value, self.increase_error_count) self.analog_label = AnalogLabel('Analog Value:') hlayout = QHBoxLayout() self.average_label = QLabel('Moving Average Length:') self.average_spin = QSpinBox() self.average_spin.setMinimum(1) self.average_spin.setMaximum(1000) self.average_spin.setSingleStep(1) self.average_spin.setValue(25) self.average_spin.editingFinished.connect(self.average_spin_finished) self.sensor_label = QLabel('Sensor Type:') self.sensor_combo = QComboBox() self.sensor_combo.addItem('2Y0A41 (4-30cm)') self.sensor_combo.addItem('2Y0A21 (10-80cm)') self.sensor_combo.addItem('2Y0A02 (20-150cm)') self.sensor_combo.currentIndexChanged.connect( self.sensor_combo_changed) hlayout.addWidget(self.average_label) hlayout.addWidget(self.average_spin) hlayout.addStretch() hlayout.addWidget(self.sensor_label) hlayout.addWidget(self.sensor_combo) self.current_distance = CurveValueWrapper() # float, cm plots = [('Distance', Qt.red, self.current_distance, '{} cm'.format)] self.plot_widget = PlotWidget('Distance [cm]', plots, extra_key_widgets=[self.analog_label], y_resolution=0.1) line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def sensor_combo_changed(self, index): self.dist.set_sensor_type(index) def average_spin_finished(self): self.dist.set_moving_average_configuration(self.average_spin.value()) def get_moving_average_configuration_async(self, average): self.average_spin.blockSignals(True) self.average_spin.setValue(average) self.average_spin.blockSignals(False) def get_sensor_type_async(self, sensor): self.sensor_combo.blockSignals(True) self.sensor_combo.setCurrentIndex(sensor) self.sensor_combo.blockSignals(False) def start(self): async_call(self.dist.get_moving_average_configuration, None, self.get_moving_average_configuration_async, self.increase_error_count) async_call(self.dist.get_sensor_type, None, self.get_sensor_type_async, self.increase_error_count) self.cbe_distance.set_period(10) self.cbe_analog_value.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_analog_value.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletDistanceIRV2.DEVICE_IDENTIFIER def cb_distance(self, distance): self.current_distance.value = distance / 10.0 def cb_analog_value(self, analog_value): self.analog_label.setText(analog_value)
class WosWaypointActionWidget(QWidget): waypoint_changed = pyqtSignal(int, int, int, int) waypoint_remove_pressed = pyqtSignal(int) def __init__(self, index, map_size_x, map_size_y, parent=None): QWidget.__init__(self, parent) self.index = index self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.waypoint_label = QLabel("Waypoint %s:" % index, self) self.layout.addWidget(self.waypoint_label, 0, 0) self.waypoint_x_spinbox = QSpinBox(self) self.waypoint_x_spinbox.blockSignals(True) self.waypoint_x_spinbox.setRange(0, map_size_x) self.layout.addWidget(self.waypoint_x_spinbox, 0, 1) self.waypoint_x_spinbox.valueChanged.connect(self.update_waypoint) self.waypoint_x_spinbox.blockSignals(False) self.waypoint_y_spinbox = QSpinBox(self) self.waypoint_y_spinbox.blockSignals(True) self.waypoint_y_spinbox.setRange(0, map_size_y) self.layout.addWidget(self.waypoint_y_spinbox, 0, 2) self.waypoint_y_spinbox.valueChanged.connect(self.update_waypoint) self.waypoint_y_spinbox.blockSignals(False) self.layout.addWidget(QLabel("Duration: ", self), 0, 3) self.duration_spinbox = QSpinBox(self) self.duration_spinbox.blockSignals(True) self.duration_spinbox.setRange(0, 10) self.layout.addWidget(self.duration_spinbox, 0, 4) self.duration_spinbox.valueChanged.connect(self.update_waypoint) self.duration_spinbox.blockSignals(False) self.remove_button = WosIconPushButton( qtawesome.icon('fa.minus-square-o'), qtawesome.icon('fa.minus-square'), self) self.remove_button.setToolTip('Remove Waypoint %s' % self.index) self.remove_button.released.connect(self.remove_button_pressed) self.layout.addWidget(self.remove_button, 0, 5) def get_index(self): return self.index def get_waypoint_info(self): return int(self.waypoint_x_spinbox.value()), int( self.waypoint_y_spinbox.value()), int( self.duration_spinbox.value()) def set_index(self, index): self.index = index self.waypoint_label.setText("Waypoint %s:" % index) self.remove_button.setToolTip('Remove Waypoint %s' % self.index) def remove_button_pressed(self): self.waypoint_remove_pressed.emit(self.index) def update_waypoint(self): self.waypoint_changed.emit(self.index, self.waypoint_x_spinbox.value(), self.waypoint_y_spinbox.value(), self.duration_spinbox.value())
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 WosFireActionWidget(QWidget): location_changed = pyqtSignal(int, int) def __init__(self, index, map_size_x, map_size_y, parent=None): QWidget.__init__(self, parent) self.index = index self.layout = QGridLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.layout.addWidget(QLabel("Bomb %s:" % index, self), 0, 0) self.attack_x_spinbox = QSpinBox(self) self.attack_x_spinbox.blockSignals(True) self.attack_x_spinbox.setRange(0, map_size_x - 1) self.layout.addWidget(self.attack_x_spinbox, 0, 1) self.attack_x_spinbox.valueChanged.connect(self.update_location) self.attack_x_spinbox.blockSignals(False) self.attack_y_spinbox = QSpinBox(self) self.attack_y_spinbox.blockSignals(True) self.attack_y_spinbox.setRange(0, map_size_y - 1) self.layout.addWidget(self.attack_y_spinbox, 0, 2) self.attack_y_spinbox.valueChanged.connect(self.update_location) self.attack_y_spinbox.blockSignals(False) def get_index(self): return self.index def get_fire_info(self): return self.attack_x_spinbox.value(), self.attack_y_spinbox.value() def set_fire_location(self, x, y): self.attack_x_spinbox.blockSignals(False) self.attack_x_spinbox.setValue(x) self.attack_x_spinbox.blockSignals(True) self.attack_y_spinbox.setValue(y) def update_location(self): self.location_changed.emit(self.attack_x_spinbox.value(), self.attack_y_spinbox.value())
class LaserRangeFinderV2(COMCUPluginBase): def __init__(self, *args): super().__init__(BrickletLaserRangeFinderV2, *args) self.lrf = self.device self.cbe_distance = CallbackEmulator(self.lrf.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_velocity = CallbackEmulator(self.lrf.get_velocity, None, self.cb_velocity, self.increase_error_count) self.current_distance = CurveValueWrapper() # int, cm self.current_velocity = CurveValueWrapper() # float, m/s plots_distance = [('Distance', Qt.red, self.current_distance, format_distance)] plots_velocity = [('Velocity', Qt.red, self.current_velocity, '{:.2f} m/s'.format)] self.plot_widget_distance = PlotWidget('Distance [cm]', plots_distance, y_resolution=1.0) self.plot_widget_velocity = PlotWidget('Velocity [m/s]', plots_velocity, y_resolution=0.01) self.label_average_distance = QLabel('Moving Average for Distance:') self.spin_average_distance = QSpinBox() self.spin_average_distance.setMinimum(0) self.spin_average_distance.setMaximum(255) self.spin_average_distance.setSingleStep(1) self.spin_average_distance.setValue(10) self.spin_average_distance.editingFinished.connect( self.spin_average_finished) self.label_average_velocity = QLabel('Moving Average for Velocity:') self.spin_average_velocity = QSpinBox() self.spin_average_velocity.setMinimum(0) self.spin_average_velocity.setMaximum(255) self.spin_average_velocity.setSingleStep(1) self.spin_average_velocity.setValue(10) self.spin_average_velocity.editingFinished.connect( self.spin_average_finished) self.enable_laser = QCheckBox("Enable Laser") self.enable_laser.stateChanged.connect(self.enable_laser_changed) self.label_acquisition_count = QLabel('Acquisition Count:') self.spin_acquisition_count = QSpinBox() self.spin_acquisition_count.setMinimum(1) self.spin_acquisition_count.setMaximum(255) self.spin_acquisition_count.setSingleStep(1) self.spin_acquisition_count.setValue(128) self.enable_qick_termination = QCheckBox("Quick Termination") self.label_threshold = QLabel('Threshold:') self.threshold = QCheckBox("Automatic Threshold") self.spin_threshold = QSpinBox() self.spin_threshold.setMinimum(1) self.spin_threshold.setMaximum(255) self.spin_threshold.setSingleStep(1) self.spin_threshold.setValue(1) self.label_frequency = QLabel('Frequency [Hz]:') self.frequency = QCheckBox( "Automatic Frequency (Disable for Velocity)") self.spin_frequency = QSpinBox() self.spin_frequency.setMinimum(10) self.spin_frequency.setMaximum(500) self.spin_frequency.setSingleStep(1) self.spin_frequency.setValue(10) self.spin_acquisition_count.editingFinished.connect( self.configuration_changed) self.enable_qick_termination.stateChanged.connect( self.configuration_changed) self.spin_threshold.editingFinished.connect(self.configuration_changed) self.threshold.stateChanged.connect(self.configuration_changed) self.spin_frequency.editingFinished.connect(self.configuration_changed) self.frequency.stateChanged.connect(self.configuration_changed) layout_h1 = QHBoxLayout() layout_h1.addWidget(self.plot_widget_distance) layout_h1.addWidget(self.plot_widget_velocity) layout_h2 = QHBoxLayout() layout_h2.addWidget(self.label_average_distance) layout_h2.addWidget(self.spin_average_distance) layout_h2.addWidget(self.label_average_velocity) layout_h2.addWidget(self.spin_average_velocity) layout_h2.addStretch() layout_h2.addWidget(self.enable_laser) layout_h3 = QHBoxLayout() layout_h3.addWidget(self.label_frequency) layout_h3.addWidget(self.spin_frequency) layout_h3.addWidget(self.frequency) layout_h3.addStretch() layout_h3.addWidget(self.enable_qick_termination) layout_h4 = QHBoxLayout() layout_h4.addWidget(self.label_threshold) layout_h4.addWidget(self.spin_threshold) layout_h4.addWidget(self.threshold) layout_h4.addStretch() layout_h4.addWidget(self.label_acquisition_count) layout_h4.addWidget(self.spin_acquisition_count) self.widgets_distance = [ self.plot_widget_distance, self.spin_average_distance, self.label_average_distance ] self.widgets_velocity = [ self.plot_widget_velocity, self.spin_average_velocity, self.label_average_velocity ] line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addLayout(layout_h1) layout.addWidget(line) layout.addLayout(layout_h2) layout.addLayout(layout_h3) layout.addLayout(layout_h4) def start(self): async_call(self.lrf.get_configuration, None, self.get_configuration_async, self.increase_error_count) async_call(self.lrf.get_enable, None, self.enable_laser.setChecked, self.increase_error_count) async_call(self.lrf.get_moving_average, None, self.get_moving_average_async, self.increase_error_count) self.cbe_distance.set_period(25) self.cbe_velocity.set_period(25) self.plot_widget_distance.stop = False self.plot_widget_velocity.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_velocity.set_period(0) self.plot_widget_distance.stop = True self.plot_widget_velocity.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLaserRangeFinderV2.DEVICE_IDENTIFIER def enable_laser_changed(self, state): self.lrf.set_enable(state == Qt.Checked) def cb_distance(self, distance): self.current_distance.value = distance def cb_velocity(self, velocity): self.current_velocity.value = velocity / 100.0 def configuration_changed(self): acquisition_count = self.spin_acquisition_count.value() enable_quick_termination = self.enable_qick_termination.isChecked() if self.threshold.isChecked(): threshold = 0 else: threshold = self.spin_threshold.value() if self.frequency.isChecked(): frequency = 0 for w in self.widgets_velocity: w.hide() else: frequency = self.spin_frequency.value() for w in self.widgets_velocity: w.show() self.spin_threshold.setDisabled(threshold == 0) self.spin_frequency.setDisabled(frequency == 0) self.lrf.set_configuration(acquisition_count, enable_quick_termination, threshold, frequency) def get_configuration_async(self, conf): self.spin_acquisition_count.blockSignals(True) self.spin_acquisition_count.setValue(conf.acquisition_count) self.spin_acquisition_count.blockSignals(False) self.enable_qick_termination.blockSignals(True) self.enable_qick_termination.setChecked(conf.enable_quick_termination) self.enable_qick_termination.blockSignals(False) self.spin_threshold.blockSignals(True) self.spin_threshold.setValue(conf.threshold_value) self.spin_threshold.setDisabled(conf.threshold_value == 0) self.spin_threshold.blockSignals(False) self.spin_frequency.blockSignals(True) self.spin_frequency.setValue(conf.measurement_frequency) self.spin_frequency.setDisabled(conf.measurement_frequency == 0) self.spin_frequency.blockSignals(False) self.threshold.blockSignals(True) self.threshold.setChecked(conf.threshold_value == 0) self.threshold.blockSignals(False) self.frequency.blockSignals(True) self.frequency.setChecked(conf.measurement_frequency == 0) self.frequency.blockSignals(False) self.configuration_changed() def get_moving_average_async(self, avg): self.spin_average_distance.setValue(avg.distance_average_length) self.spin_average_velocity.setValue(avg.velocity_average_length) def spin_average_finished(self): self.lrf.set_moving_average(self.spin_average_distance.value(), self.spin_average_velocity.value())
class CColorInfos(QWidget): colorChanged = pyqtSignal(QColor, int) colorAdded = pyqtSignal(QColor) def __init__(self, *args, **kwargs): super(CColorInfos, self).__init__(*args, **kwargs) layout = QGridLayout(self) layout.setContentsMargins(11, 2, 11, 2) layout.setSpacing(8) self.editHex = QLineEdit('#FF0000', self, alignment=Qt.AlignCenter, objectName='editHex', textChanged=self.onHexChanged) self.editHex.setValidator( QRegExpValidator(QRegExp('#[0-9a-fA-F]{6}$'), self.editHex)) self.labelHex = QLabel('HEX', self, alignment=Qt.AlignCenter) layout.addWidget(self.editHex, 0, 0) layout.addWidget(self.labelHex, 1, 0) layout.addItem( QSpacerItem(10, 20, QSizePolicy.Fixed, QSizePolicy.Minimum), 0, 1) self.editRed = QSpinBox(self, buttonSymbols=QSpinBox.NoButtons, alignment=Qt.AlignCenter, valueChanged=self.onRgbaChanged) self.editRed.setRange(0, 255) self.labelRed = QLabel('R', self, alignment=Qt.AlignCenter) layout.addWidget(self.editRed, 0, 2) layout.addWidget(self.labelRed, 1, 2) self.editGreen = QSpinBox(self, buttonSymbols=QSpinBox.NoButtons, alignment=Qt.AlignCenter, valueChanged=self.onRgbaChanged) self.editGreen.setRange(0, 255) self.labelGreen = QLabel('G', self, alignment=Qt.AlignCenter) layout.addWidget(self.editGreen, 0, 3) layout.addWidget(self.labelGreen, 1, 3) self.editBlue = QSpinBox(self, buttonSymbols=QSpinBox.NoButtons, alignment=Qt.AlignCenter, valueChanged=self.onRgbaChanged) self.editBlue.setRange(0, 255) self.labelBlue = QLabel('B', self, alignment=Qt.AlignCenter) layout.addWidget(self.editBlue, 0, 4) layout.addWidget(self.labelBlue, 1, 4) self.editAlpha = QSpinBox(self, buttonSymbols=QSpinBox.NoButtons, alignment=Qt.AlignCenter, valueChanged=self.onRgbaChanged) self.editAlpha.setRange(0, 255) self.labelAlpha = QLabel('A', self, alignment=Qt.AlignCenter) layout.addWidget(self.editAlpha, 0, 5) layout.addWidget(self.labelAlpha, 1, 5) layout.addItem( QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum), 0, 6) layout.addWidget( QPushButton('+', self, cursor=Qt.PointingHandCursor, toolTip='添加自定义颜色', clicked=self.onColorAdd), 0, 7) layout.setColumnStretch(0, 3) layout.setColumnStretch(1, 1) layout.setColumnStretch(2, 1) layout.setColumnStretch(3, 1) layout.setColumnStretch(4, 1) layout.setColumnStretch(5, 1) layout.setColumnStretch(6, 2) layout.setColumnStretch(7, 1) self.setFocus() self.editRed.setValue(255) self.editAlpha.setValue(255) def reset(self): pass def onColorAdd(self): self.colorAdded.emit( QColor(self.editRed.value(), self.editGreen.value(), self.editBlue.value())) def setHex(self, code): self.editHex.setText(str(code)) def updateColor(self, color): self.editRed.setValue(color.red()) self.editGreen.setValue(color.green()) self.editBlue.setValue(color.blue()) def updateAlpha(self, _, alpha): self.editAlpha.setValue(alpha) def onHexChanged(self, code): if len(code) != 7: return color = QColor(code) if color.isValid(): self.blockRgbaSignals(True) self.editHex.blockSignals(True) self.editRed.setValue(color.red()) self.editGreen.setValue(color.green()) self.editBlue.setValue(color.blue()) self.editAlpha.setValue(color.alpha()) self.colorChanged.emit(color, color.alpha()) self.editHex.blockSignals(False) self.blockRgbaSignals(False) def onRgbaChanged(self, _): self.editHex.blockSignals(True) self.blockRgbaSignals(True) color = QColor(self.editRed.value(), self.editGreen.value(), self.editBlue.value(), self.editAlpha.value()) self.editHex.setText(color.name()) self.colorChanged.emit(color, self.editAlpha.value()) self.blockRgbaSignals(False) self.editHex.blockSignals(False) def blockRgbaSignals(self, block=True): self.editRed.blockSignals(block) self.editGreen.blockSignals(block) self.editBlue.blockSignals(block) self.editAlpha.blockSignals(block) def sizeHint(self): return QSize(280, 48)
class OptionsDialog(QDialog): def __init__(self, setting: Settings, have_dutils, parent=None): super(OptionsDialog, self).__init__(parent) self.settings = setting self.enabled_video = True # temporary toggle to disable video features as they do not exist self.enabled_logging = True self.enabled_keybindings = True self.enabled_dutils = have_dutils self.setWindowTitle("Tcam-Capture Options") self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.tabs = QTabWidget() self.general_widget = QWidget() self.keybindings_widget = QWidget() self.logging_widget = QWidget() self.saving_widget = QWidget() self._setup_general_ui() self.tabs.addTab(self.general_widget, "General") self._setup_saving_ui() self.tabs.addTab(self.saving_widget, "Image/Video") self.layout.addWidget(self.tabs) # OK and Cancel buttons self.buttons = QDialogButtonBox( QDialogButtonBox.Reset | QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.layout.addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.buttons.clicked.connect(self.clicked) def _setup_general_ui(self): """ Create everything related to the general tab """ layout = QFormLayout() layout.setSpacing(20) layout.setVerticalSpacing(20) self.device_dialog_checkbox = QCheckBox(self) device_dialog_label = QLabel("Open device dialog on start:") layout.addRow(device_dialog_label, self.device_dialog_checkbox) self.reopen_device_checkbox = QCheckBox(self) reopen_device_label = QLabel( "Reopen device on start(ignores device dialog):", self) layout.addRow(reopen_device_label, self.reopen_device_checkbox) self.use_dutils_checkbox = QCheckBox(self) self.use_dutils_label = QLabel("Use tiscamera dutils, if present:", self) layout.addRow(self.use_dutils_label, self.use_dutils_checkbox) if not self.enabled_dutils: self.use_dutils_label.setToolTip( "Enabled when tiscamera-dutils are installed") self.use_dutils_label.setEnabled(False) self.use_dutils_checkbox.setToolTip( "Enabled when tiscamera-dutils are installed") self.use_dutils_checkbox.setEnabled(False) self.general_widget.setLayout(layout) def _setup_saving_ui(self): """ Create everything related to the image/video saving tab """ encoder_dict = Encoder.get_encoder_dict() form_layout = QFormLayout() layout = QVBoxLayout() layout.addLayout(form_layout) location_layout = QHBoxLayout() location_label = QLabel("Where to save images/videos:", self) self.location_edit = QLineEdit(self) location_dialog_button = QPushButton("...", self) location_dialog_button.clicked.connect(self.open_file_dialog) location_layout.addWidget(self.location_edit) location_layout.addWidget(location_dialog_button) # maintain descriptions as own labels # pyqt seems to loose the descriptions somewhere # when simple strings are used or the qlabel does not have self as owner form_layout.addRow(location_label, location_layout) self.image_type_combobox = QComboBox(self) for key, value in encoder_dict.items(): if value.encoder_type == Encoder.MediaType.image: self.image_type_combobox.addItem(key) image_type_label = QLabel("Save images as:") self.image_type_combobox.currentIndexChanged['QString'].connect( self.image_name_suffix_changed) form_layout.addRow(image_type_label, self.image_type_combobox) if self.enabled_video: self.video_type_combobox = QComboBox(self) for key, value in encoder_dict.items(): if value.encoder_type == Encoder.MediaType.video: self.video_type_combobox.addItem(key) self.video_type_combobox.currentIndexChanged['QString'].connect( self.video_name_suffix_changed) video_type_label = QLabel("Save videos as:", self) form_layout.addRow(video_type_label, self.video_type_combobox) image_name_groupbox = QGroupBox("Image File Names") groupbox_layout = QFormLayout() image_name_groupbox.setLayout(groupbox_layout) self.image_name_preview = QLabel( "<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png") self.image_name_preview_description = QLabel( "Images will be named like:") groupbox_layout.addRow(self.image_name_preview_description, self.image_name_preview) self.image_name_prefix = QLineEdit() self.image_name_prefix.textChanged.connect( self.image_name_prefix_changed) self.image_name_prefix.setMaxLength(100) self.image_name_prefix_description = QLabel("User Prefix:", self) groupbox_layout.addRow(self.image_name_prefix_description, self.image_name_prefix) self.image_name_serial = QCheckBox(self) self.image_name_serial.toggled.connect( self.image_name_properties_toggled) self.image_name_serial_description = QLabel("Include Serial:") groupbox_layout.addRow(self.image_name_serial_description, self.image_name_serial) self.image_name_format = QCheckBox(self) self.image_name_format.toggled.connect( self.image_name_properties_toggled) self.image_name_format_description = QLabel("Include Format:") groupbox_layout.addRow(self.image_name_format_description, self.image_name_format) self.image_name_counter = QCheckBox(self) self.image_name_counter.toggled.connect( self.image_name_properties_toggled) self.image_name_counter_description = QLabel("Include Counter:") groupbox_layout.addRow(self.image_name_counter_description, self.image_name_counter) self.image_name_counter_box = QSpinBox(self) self.image_name_counter_box.setRange(1, 10) self.image_name_counter_box.valueChanged.connect( self.image_name_counter_changed) self.image_name_counter_box_description = QLabel("Counter Size:") groupbox_layout.addRow(self.image_name_counter_box_description, self.image_name_counter_box) self.image_name_counter.toggled.connect( self.toggle_image_counter_box_availability) self.image_name_counter.toggled.connect( self.image_name_properties_toggled) self.image_name_timestamp = QCheckBox(self) self.image_name_timestamp.toggled.connect( self.image_name_properties_toggled) self.image_name_timestamp_description = QLabel("Include Timestamp:") groupbox_layout.addRow(self.image_name_timestamp_description, self.image_name_timestamp) layout.addWidget(image_name_groupbox) video_groupbox = QGroupBox("Video File Names") video_layout = QFormLayout() video_groupbox.setLayout(video_layout) self.video_name_preview = QLabel( "<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png") self.video_name_preview_description = QLabel( "Videos will be named like:") video_layout.addRow(self.video_name_preview_description, self.video_name_preview) self.video_name_prefix = QLineEdit() self.video_name_prefix.textChanged.connect( self.video_name_prefix_changed) self.video_name_prefix.setMaxLength(100) self.video_name_prefix_description = QLabel("User Prefix:", self) video_layout.addRow(self.video_name_prefix_description, self.video_name_prefix) self.video_name_serial = QCheckBox(self) self.video_name_serial.toggled.connect( self.video_name_properties_toggled) self.video_name_serial_description = QLabel("Include Serial:") video_layout.addRow(self.video_name_serial_description, self.video_name_serial) self.video_name_format = QCheckBox(self) self.video_name_format.toggled.connect( self.video_name_properties_toggled) self.video_name_format_description = QLabel("Include Format:") video_layout.addRow(self.video_name_format_description, self.video_name_format) self.video_name_counter = QCheckBox(self) self.video_name_counter.toggled.connect( self.video_name_properties_toggled) self.video_name_counter_description = QLabel("Include Counter:") video_layout.addRow(self.video_name_counter_description, self.video_name_counter) self.video_name_counter_box = QSpinBox(self) self.video_name_counter_box.setRange(1, 10) self.video_name_counter_box.valueChanged.connect( self.video_name_counter_changed) self.video_name_counter_box_description = QLabel("Counter Size:") video_layout.addRow(self.video_name_counter_box_description, self.video_name_counter_box) self.video_name_counter.toggled.connect( self.toggle_video_counter_box_availability) self.video_name_counter.toggled.connect( self.video_name_properties_toggled) self.video_name_timestamp = QCheckBox(self) self.video_name_timestamp.toggled.connect( self.video_name_properties_toggled) self.video_name_timestamp_description = QLabel("Include Timestamp:") video_layout.addRow(self.video_name_timestamp_description, self.video_name_timestamp) layout.addWidget(video_groupbox) self.saving_widget.setLayout(layout) def image_name_prefix_changed(self, name: str): """""" self.settings.image_name.user_prefix = self.image_name_prefix.text() self.update_image_name_preview() def image_name_suffix_changed(self, suffix: str): """""" self.update_image_name_preview() def image_name_counter_changed(self, name: str): """""" self.settings.image_name.counter_size = self.image_name_counter_box.value( ) self.update_image_name_preview() def image_name_properties_toggled(self): """""" self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked( ) self.settings.image_name.include_counter = self.image_name_counter.isChecked( ) self.settings.image_name.include_format = self.image_name_format.isChecked( ) self.settings.image_name.include_serial = self.image_name_serial.isChecked( ) self.update_image_name_preview() def update_image_name_preview(self): preview_string = "" if self.settings.image_name.user_prefix != "": max_prefix_length = 15 prefix = ( self.settings.image_name.user_prefix[:max_prefix_length] + '..') if len( self.settings.image_name.user_prefix ) > max_prefix_length else self.settings.image_name.user_prefix preview_string += prefix if self.settings.image_name.include_serial: if preview_string != "": preview_string += "-" preview_string += "00001234" if self.settings.image_name.include_format: if preview_string != "": preview_string += "-" preview_string += "gbrg_1920x1080_15_1" if self.settings.image_name.include_timestamp: if preview_string != "": preview_string += "-" preview_string += "19701230T125503" if self.settings.image_name.include_counter: if preview_string != "": preview_string += "-" preview_string += '{message:0>{fill}}'.format( message=1, fill=self.settings.image_name.counter_size) if preview_string == "": preview_string = "image" preview_string += "." + self.image_type_combobox.currentText() self.image_name_preview.setText(preview_string) def video_name_prefix_changed(self, name: str): """""" self.settings.video_name.user_prefix = self.video_name_prefix.text() self.update_video_name_preview() def video_name_suffix_changed(self, suffix: str): """""" self.update_video_name_preview() def video_name_counter_changed(self, name: str): """""" self.settings.video_name.counter_size = self.video_name_counter_box.value( ) self.update_video_name_preview() def video_name_properties_toggled(self): """""" self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked( ) self.settings.video_name.include_counter = self.video_name_counter.isChecked( ) self.settings.video_name.include_format = self.video_name_format.isChecked( ) self.settings.video_name.include_serial = self.video_name_serial.isChecked( ) self.update_video_name_preview() def update_video_name_preview(self): preview_string = "" if self.settings.video_name.user_prefix != "": # This is a convenience change to the displayed string. # We only display an amount of max_prefix_length # chars to save screen space max_prefix_length = 15 prefix = ( self.settings.video_name.user_prefix[:max_prefix_length] + '..') if len( self.settings.video_name.user_prefix ) > max_prefix_length else self.settings.video_name.user_prefix preview_string += prefix if self.settings.video_name.include_serial: if preview_string != "": preview_string += "-" preview_string += "00001234" if self.settings.video_name.include_format: if preview_string != "": preview_string += "-" preview_string += "gbrg_1920x1080_15_1" if self.settings.video_name.include_timestamp: if preview_string != "": preview_string += "-" preview_string += "19701230T125503" if self.settings.video_name.include_counter: if preview_string != "": preview_string += "-" preview_string += '{message:0>{fill}}'.format( message=1, fill=self.settings.video_name.counter_size) if preview_string == "": preview_string = "video" preview_string += "." + self.video_type_combobox.currentText() self.video_name_preview.setText(preview_string) def toggle_image_counter_box_availability(self): """""" if self.image_name_counter.isChecked(): self.image_name_counter_box.setEnabled(True) else: self.image_name_counter_box.setEnabled(False) def toggle_video_counter_box_availability(self): """""" if self.video_name_counter.isChecked(): self.video_name_counter_box.setEnabled(True) else: self.video_name_counter_box.setEnabled(False) def set_settings(self, settings: Settings): self.location_edit.setText(settings.get_save_location()) self.image_type_combobox.setCurrentText(settings.get_image_type()) if self.enabled_video: self.video_type_combobox.setCurrentText(settings.get_video_type()) self.device_dialog_checkbox.setChecked( settings.show_device_dialog_on_startup) self.reopen_device_checkbox.setChecked( settings.reopen_device_on_startup) self.use_dutils_checkbox.setChecked(settings.use_dutils) if settings.image_name.include_timestamp: self.image_name_timestamp.blockSignals(True) self.image_name_timestamp.toggle() self.image_name_timestamp.blockSignals(False) if settings.image_name.include_counter: self.image_name_counter.blockSignals(True) self.image_name_counter.toggle() self.image_name_counter.blockSignals(False) self.image_name_counter_box.blockSignals(True) self.image_name_counter_box.setValue(settings.image_name.counter_size) self.image_name_counter_box.blockSignals(False) self.toggle_image_counter_box_availability() if settings.image_name.include_format: self.image_name_format.blockSignals(True) self.image_name_format.toggle() self.image_name_format.blockSignals(False) if settings.image_name.include_serial: self.image_name_serial.blockSignals(True) self.image_name_serial.toggle() self.image_name_serial.blockSignals(False) self.image_name_prefix.blockSignals(True) self.image_name_prefix.setText(settings.image_name.user_prefix) self.image_name_prefix.blockSignals(False) self.update_image_name_preview() if settings.video_name.include_timestamp: self.video_name_timestamp.blockSignals(True) self.video_name_timestamp.toggle() self.video_name_timestamp.blockSignals(False) if settings.video_name.include_counter: self.video_name_counter.blockSignals(True) self.video_name_counter.toggle() self.video_name_counter.blockSignals(False) self.video_name_counter_box.blockSignals(True) self.video_name_counter_box.setValue(settings.video_name.counter_size) self.video_name_counter_box.blockSignals(False) self.toggle_video_counter_box_availability() if settings.video_name.include_format: self.video_name_format.blockSignals(True) self.video_name_format.toggle() self.video_name_format.blockSignals(False) if settings.video_name.include_serial: self.video_name_serial.blockSignals(True) self.video_name_serial.toggle() self.video_name_serial.blockSignals(False) self.video_name_prefix.blockSignals(True) self.video_name_prefix.setText(settings.video_name.user_prefix) self.video_name_prefix.blockSignals(False) self.update_video_name_preview() def save_settings(self): self.settings.save_location = self.location_edit.text() self.settings.image_type = self.image_type_combobox.currentText() if self.enabled_video: self.settings.video_type = self.video_type_combobox.currentText() self.settings.show_device_dialog_on_startup = self.device_dialog_checkbox.isChecked( ) self.settings.reopen_device_on_startup = self.reopen_device_checkbox.isChecked( ) self.settings.use_dutils = self.use_dutils_checkbox.isChecked() self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked( ) self.settings.image_name.include_counter = self.image_name_counter.isChecked( ) if self.image_name_counter.isChecked(): self.settings.image_name.counter_size = self.image_name_counter_box.value( ) self.settings.image_name.include_format = self.image_name_format.isChecked( ) self.settings.image_name.include_serial = self.image_name_serial.isChecked( ) self.settings.image_name.user_prefix = self.image_name_prefix.text() self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked( ) self.settings.video_name.include_counter = self.video_name_counter.isChecked( ) if self.video_name_counter.isChecked(): self.settings.video_name.counter_size = self.video_name_counter_box.value( ) self.settings.video_name.include_format = self.video_name_format.isChecked( ) self.settings.video_name.include_serial = self.video_name_serial.isChecked( ) self.settings.video_name.user_prefix = self.video_name_prefix.text() def open_file_dialog(self): fdia = QFileDialog() fdia.setFileMode(QFileDialog.Directory) fdia.setWindowTitle("Select Directory for saving images and videos") if fdia.exec_(): self.location_edit.setText(fdia.selectedFiles()[0]) def get_location(self): return self.location_edit.text() def get_image_format(self): return self.image_type_combobox.currentText() def get_video_format(self): return self.video_type_combobox.currentText() def clicked(self, button): if self.buttons.buttonRole(button) == QDialogButtonBox.ResetRole: self.reset() def reset(self): """""" log.info("reset called") self.settings.reset() self.set_settings(self.settings) @staticmethod def get_options(settings, parent=None): dialog = OptionsDialog(settings, parent) if settings is not None: dialog.set_settings(settings) result = dialog.exec_() if result == QDialog.Accepted: dialog.save_settings() settings.save() return result == QDialog.Accepted
class QTimeSlider(QWidget): """ This will be slightly different from QTimeSelect in that time down to the nearest nanosecond can be selected, and need not be snapped to the nearest video frame. """ timeSelectionChanged = pyqtSignal(int, float) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) layout = QVBoxLayout() self.setLayout(layout) self.slider = Slider(self) self.slider.setOrientation(Qt.Horizontal) self.slider.valueChanged.connect(self._handleSliderChange) self.slider.setTickInterval(1) layout.addWidget(self.slider) hlayout = QHBoxLayout() layout.addLayout(hlayout) self.leftLabel = QLabel(self) self.spinBox = QSpinBox(self) self.spinBox.valueChanged.connect(self._handleSpinboxChange) self.timeSelect = QTimeSelect(self) self.timeSelect.valueChanged.connect(self._handleTimeEditChange) # self.timeSelect.editingFinished.connect(self._handleTimeEditFinished) self.rightLabel = QLabel(self) hlayout.addWidget(self.leftLabel) hlayout.addStretch() hlayout.addWidget(QLabel("Frame index:", self)) hlayout.addWidget(self.spinBox) hlayout.addWidget(QLabel("Timestamp:", self)) hlayout.addWidget(self.timeSelect) hlayout.addStretch() hlayout.addWidget(self.rightLabel) self.setPtsTimeArray(None) def setMinimum(self, n=0, t=None): self.slider.setMinimum(n) self.spinBox.setMinimum(n) if t is None: t = self.pts_time[n] self.timeSelect.setMinimum(t) m, s = divmod(self.pts_time[n], 60) h, m = divmod(int(m), 60) self.leftLabel.setText(f"{n} ({h}:{m:02d}:{s:012.9f})") def setMaximum(self, n=None, t=None): if n is None: n = len(self.pts_time) - 1 self.slider.setMaximum(n) self.spinBox.setMaximum(n) if t is None: t = self.pts_time[n] self.timeSelect.setMaximum(t) m, s = divmod(self.pts_time[n], 60) h, m = divmod(int(m), 60) self.rightLabel.setText(f"{n} ({h}:{m:02d}:{s:012.9f})") def setStartEndVisible(self, value): self.leftLabel.setHidden(not bool(value)) self.rightLabel.setHidden(not bool(value)) def setPtsTimeArray(self, pts_time=None): self.pts_time = pts_time if pts_time is not None: N = len(pts_time) self.setMinimum(0) self.setMaximum(N - 1) self.slider.setValue(0) self.slider.setSnapValues(None) self.slider.setDisabled(False) self.spinBox.setDisabled(False) self.timeSelect.setDisabled(False) else: self.slider.setSnapValues(None) self.slider.setDisabled(True) self.spinBox.setDisabled(True) self.timeSelect.setDisabled(True) def _handleSliderChange(self, n): self.spinBox.blockSignals(True) self.spinBox.setValue(n) self.spinBox.blockSignals(False) if self.pts_time is None: self.timeSelectionChanged.emit(n, 0) return pts_time = self.pts_time[n] self.timeSelect.blockSignals(True) self.timeSelect.setValue(pts_time) self.timeSelect.blockSignals(False) self.timeSelectionChanged.emit(n, pts_time) def _handleSpinboxChange(self, n): self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) if self.pts_time is None: self.timeSelectionChanged.emit(n, 0) return pts_time = self.pts_time[n] self.timeSelect.blockSignals(True) self.timeSelect.setValue(pts_time) self.timeSelect.blockSignals(False) self.timeSelectionChanged.emit(n, pts_time) def _handleTimeEditChange(self, t): if self.pts_time is None: self.timeSelectionChanged.emit(0, t) return if t + 0.0005 >= self.pts_time[0]: n = search(self.pts_time, t + 0.0005, dir="-") else: n = 0 if n != self.slider.value(): self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) self.spinBox.blockSignals(True) self.spinBox.setValue(n) self.spinBox.blockSignals(False) self.timeSelectionChanged.emit(n, t)
class LaserRangeFinder(PluginBase): def __init__(self, *args): super().__init__(BrickletLaserRangeFinder, *args) self.lrf = self.device # the firmware version of a EEPROM Bricklet can (under common circumstances) # not change during the lifetime of an EEPROM Bricklet plugin. therefore, # it's okay to make final decisions based on it here self.has_sensor_hardware_version_api = self.firmware_version >= (2, 0, 3) self.has_configuration_api = self.firmware_version >= (2, 0, 3) self.cbe_distance = CallbackEmulator(self.lrf.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_velocity = CallbackEmulator(self.lrf.get_velocity, None, self.cb_velocity, self.increase_error_count) self.current_distance = CurveValueWrapper() # int, cm self.current_velocity = CurveValueWrapper() # float, m/s plots_distance = [('Distance', Qt.red, self.current_distance, format_distance)] plots_velocity = [('Velocity', Qt.red, self.current_velocity, '{:.2f} m/s'.format)] self.plot_widget_distance = PlotWidget('Distance [cm]', plots_distance, y_resolution=1.0) self.plot_widget_velocity = PlotWidget('Velocity [m/s]', plots_velocity, y_resolution=0.01) self.mode_label = QLabel('Mode:') self.mode_combo = QComboBox() self.mode_combo.addItem("Distance: 1cm resolution, 40m max") self.mode_combo.addItem("Velocity: 0.10 m/s resolution, 12.70m/s max") self.mode_combo.addItem("Velocity: 0.25 m/s resolution, 31.75m/s max") self.mode_combo.addItem("Velocity: 0.50 m/s resolution, 63.50m/s max") self.mode_combo.addItem("Velocity: 1.00 m/s resolution, 127.00m/s max") self.mode_combo.currentIndexChanged.connect(self.mode_changed) self.mode_combo.hide() self.label_average_distance = QLabel('Moving Average for Distance:') self.spin_average_distance = QSpinBox() self.spin_average_distance.setMinimum(0) self.spin_average_distance.setMaximum(50) self.spin_average_distance.setSingleStep(1) self.spin_average_distance.setValue(10) self.spin_average_distance.editingFinished.connect(self.spin_average_finished) self.label_average_velocity = QLabel('Moving Average for Velocity:') self.spin_average_velocity = QSpinBox() self.spin_average_velocity.setMinimum(0) self.spin_average_velocity.setMaximum(50) self.spin_average_velocity.setSingleStep(1) self.spin_average_velocity.setValue(10) self.spin_average_velocity.editingFinished.connect(self.spin_average_finished) self.enable_laser = QCheckBox("Enable Laser") self.enable_laser.stateChanged.connect(self.enable_laser_changed) self.label_acquisition_count = QLabel('Acquisition Count:') self.spin_acquisition_count = QSpinBox() self.spin_acquisition_count.setMinimum(1) self.spin_acquisition_count.setMaximum(255) self.spin_acquisition_count.setSingleStep(1) self.spin_acquisition_count.setValue(128) self.enable_qick_termination = QCheckBox("Quick Termination") self.label_threshold = QLabel('Threshold:') self.threshold = QCheckBox("Automatic Threshold") self.spin_threshold = QSpinBox() self.spin_threshold.setMinimum(1) self.spin_threshold.setMaximum(255) self.spin_threshold.setSingleStep(1) self.spin_threshold.setValue(1) self.label_frequency = QLabel('Frequency [Hz]:') self.frequency = QCheckBox("Automatic Frequency (Disable for Velocity)") self.spin_frequency = QSpinBox() self.spin_frequency.setMinimum(10) self.spin_frequency.setMaximum(500) self.spin_frequency.setSingleStep(1) self.spin_frequency.setValue(10) self.spin_acquisition_count.editingFinished.connect(self.configuration_changed) self.enable_qick_termination.stateChanged.connect(self.configuration_changed) self.spin_threshold.editingFinished.connect(self.configuration_changed) self.threshold.stateChanged.connect(self.configuration_changed) self.spin_frequency.editingFinished.connect(self.configuration_changed) self.frequency.stateChanged.connect(self.configuration_changed) layout_h1 = QHBoxLayout() layout_h1.addWidget(self.plot_widget_distance) layout_h1.addWidget(self.plot_widget_velocity) layout_h2 = QHBoxLayout() layout_h2.addWidget(self.mode_label) layout_h2.addWidget(self.mode_combo) layout_h2.addWidget(self.label_average_distance) layout_h2.addWidget(self.spin_average_distance) layout_h2.addWidget(self.label_average_velocity) layout_h2.addWidget(self.spin_average_velocity) layout_h2.addStretch() layout_h2.addWidget(self.enable_laser) layout_h3 = QHBoxLayout() layout_h3.addWidget(self.label_frequency) layout_h3.addWidget(self.spin_frequency) layout_h3.addWidget(self.frequency) layout_h3.addStretch() layout_h3.addWidget(self.enable_qick_termination) layout_h4 = QHBoxLayout() layout_h4.addWidget(self.label_threshold) layout_h4.addWidget(self.spin_threshold) layout_h4.addWidget(self.threshold) layout_h4.addStretch() layout_h4.addWidget(self.label_acquisition_count) layout_h4.addWidget(self.spin_acquisition_count) self.widgets_distance = [self.plot_widget_distance, self.spin_average_distance, self.label_average_distance] self.widgets_velocity = [self.plot_widget_velocity, self.spin_average_velocity, self.label_average_velocity] for w in self.widgets_distance: w.hide() for w in self.widgets_velocity: w.hide() line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addLayout(layout_h1) layout.addWidget(line) layout.addLayout(layout_h2) layout.addLayout(layout_h3) layout.addLayout(layout_h4) def start(self): if self.has_sensor_hardware_version_api: async_call(self.lrf.get_sensor_hardware_version, None, self.get_sensor_hardware_version_async, self.increase_error_count) else: self.get_sensor_hardware_version_async(1) if self.has_configuration_api: async_call(self.lrf.get_configuration, None, self.get_configuration_async, self.increase_error_count) async_call(self.lrf.get_mode, None, self.get_mode_async, self.increase_error_count) async_call(self.lrf.is_laser_enabled, None, self.enable_laser.setChecked, self.increase_error_count) async_call(self.lrf.get_moving_average, None, self.get_moving_average_async, self.increase_error_count) self.cbe_distance.set_period(25) self.cbe_velocity.set_period(25) self.plot_widget_distance.stop = False self.plot_widget_velocity.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_velocity.set_period(0) self.plot_widget_distance.stop = True self.plot_widget_velocity.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLaserRangeFinder.DEVICE_IDENTIFIER def enable_laser_changed(self, state): if state == Qt.Checked: self.lrf.enable_laser() else: self.lrf.disable_laser() def mode_changed(self, value): if value < 0 or value > 4: return self.lrf.set_mode(value) if value == 0: for w in self.widgets_velocity: w.hide() for w in self.widgets_distance: w.show() else: for w in self.widgets_distance: w.hide() for w in self.widgets_velocity: w.show() def cb_distance(self, distance): self.current_distance.value = distance def cb_velocity(self, velocity): self.current_velocity.value = velocity / 100.0 def configuration_changed(self): acquisition_count = self.spin_acquisition_count.value() enable_quick_termination = self.enable_qick_termination.isChecked() if self.threshold.isChecked(): threshold = 0 else: threshold = self.spin_threshold.value() if self.frequency.isChecked(): frequency = 0 for w in self.widgets_velocity: w.hide() else: frequency = self.spin_frequency.value() for w in self.widgets_velocity: w.show() self.spin_threshold.setDisabled(threshold == 0) self.spin_frequency.setDisabled(frequency == 0) self.lrf.set_configuration(acquisition_count, enable_quick_termination, threshold, frequency) def get_configuration_async(self, conf): self.spin_acquisition_count.blockSignals(True) self.spin_acquisition_count.setValue(conf.acquisition_count) self.spin_acquisition_count.blockSignals(False) self.enable_qick_termination.blockSignals(True) self.enable_qick_termination.setChecked(conf.enable_quick_termination) self.enable_qick_termination.blockSignals(False) self.spin_threshold.blockSignals(True) self.spin_threshold.setValue(conf.threshold_value) self.spin_threshold.setDisabled(conf.threshold_value == 0) self.spin_threshold.blockSignals(False) self.spin_frequency.blockSignals(True) self.spin_frequency.setValue(conf.measurement_frequency) self.spin_frequency.setDisabled(conf.measurement_frequency == 0) self.spin_frequency.blockSignals(False) self.threshold.blockSignals(True) self.threshold.setChecked(conf.threshold_value == 0) self.threshold.blockSignals(False) self.frequency.blockSignals(True) self.frequency.setChecked(conf.measurement_frequency == 0) self.frequency.blockSignals(False) self.configuration_changed() def get_sensor_hardware_version_async(self, value): if value == 1: self.mode_combo.show() self.mode_label.show() self.label_acquisition_count.hide() self.spin_acquisition_count.hide() self.enable_qick_termination.hide() self.label_threshold.hide() self.spin_threshold.hide() self.threshold.hide() self.label_frequency.hide() self.spin_frequency.hide() self.frequency.hide() else: self.mode_combo.hide() self.mode_label.hide() self.label_acquisition_count.show() self.spin_acquisition_count.show() self.enable_qick_termination.show() self.label_threshold.show() self.spin_threshold.show() self.threshold.show() self.label_frequency.show() self.spin_frequency.show() self.frequency.show() for w in self.widgets_distance: w.show() for w in self.widgets_velocity: w.show() def get_mode_async(self, value): self.mode_combo.setCurrentIndex(value) self.mode_changed(value) def get_moving_average_async(self, avg): self.spin_average_distance.setValue(avg.distance_average_length) self.spin_average_velocity.setValue(avg.velocity_average_length) def spin_average_finished(self): self.lrf.set_moving_average(self.spin_average_distance.value(), self.spin_average_velocity.value())
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 LaserRangeFinderV2(COMCUPluginBase): def __init__(self, *args): super().__init__(BrickletLaserRangeFinderV2, *args) self.lrf = self.device self.cbe_distance = CallbackEmulator(self.lrf.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_velocity = CallbackEmulator(self.lrf.get_velocity, None, self.cb_velocity, self.increase_error_count) self.current_distance = CurveValueWrapper() # int, cm self.current_velocity = CurveValueWrapper() # float, m/s plots_distance = [('Distance', Qt.red, self.current_distance, format_distance)] plots_velocity = [('Velocity', Qt.red, self.current_velocity, '{:.2f} m/s'.format)] self.plot_widget_distance = PlotWidget('Distance [cm]', plots_distance, y_resolution=1.0) self.plot_widget_velocity = PlotWidget('Velocity [m/s]', plots_velocity, y_resolution=0.01) self.label_average_distance = QLabel('Moving Average for Distance:') self.spin_average_distance = QSpinBox() self.spin_average_distance.setMinimum(0) self.spin_average_distance.setMaximum(255) self.spin_average_distance.setSingleStep(1) self.spin_average_distance.setValue(10) self.spin_average_distance.editingFinished.connect(self.spin_average_finished) self.label_average_velocity = QLabel('Moving Average for Velocity:') self.spin_average_velocity = QSpinBox() self.spin_average_velocity.setMinimum(0) self.spin_average_velocity.setMaximum(255) self.spin_average_velocity.setSingleStep(1) self.spin_average_velocity.setValue(10) self.spin_average_velocity.editingFinished.connect(self.spin_average_finished) self.enable_laser = QCheckBox("Enable Laser") self.enable_laser.stateChanged.connect(self.enable_laser_changed) self.label_acquisition_count = QLabel('Acquisition Count:') self.spin_acquisition_count = QSpinBox() self.spin_acquisition_count.setMinimum(1) self.spin_acquisition_count.setMaximum(255) self.spin_acquisition_count.setSingleStep(1) self.spin_acquisition_count.setValue(128) self.enable_qick_termination = QCheckBox("Quick Termination") self.label_threshold = QLabel('Threshold:') self.threshold = QCheckBox("Automatic Threshold") self.spin_threshold = QSpinBox() self.spin_threshold.setMinimum(1) self.spin_threshold.setMaximum(255) self.spin_threshold.setSingleStep(1) self.spin_threshold.setValue(1) self.label_frequency = QLabel('Frequency [Hz]:') self.frequency = QCheckBox("Automatic Frequency (Disable for Velocity)") self.spin_frequency = QSpinBox() self.spin_frequency.setMinimum(10) self.spin_frequency.setMaximum(500) self.spin_frequency.setSingleStep(1) self.spin_frequency.setValue(10) self.spin_acquisition_count.editingFinished.connect(self.configuration_changed) self.enable_qick_termination.stateChanged.connect(self.configuration_changed) self.spin_threshold.editingFinished.connect(self.configuration_changed) self.threshold.stateChanged.connect(self.configuration_changed) self.spin_frequency.editingFinished.connect(self.configuration_changed) self.frequency.stateChanged.connect(self.configuration_changed) layout_h1 = QHBoxLayout() layout_h1.addWidget(self.plot_widget_distance) layout_h1.addWidget(self.plot_widget_velocity) layout_h2 = QHBoxLayout() layout_h2.addWidget(self.label_average_distance) layout_h2.addWidget(self.spin_average_distance) layout_h2.addWidget(self.label_average_velocity) layout_h2.addWidget(self.spin_average_velocity) layout_h2.addStretch() layout_h2.addWidget(self.enable_laser) layout_h3 = QHBoxLayout() layout_h3.addWidget(self.label_frequency) layout_h3.addWidget(self.spin_frequency) layout_h3.addWidget(self.frequency) layout_h3.addStretch() layout_h3.addWidget(self.enable_qick_termination) layout_h4 = QHBoxLayout() layout_h4.addWidget(self.label_threshold) layout_h4.addWidget(self.spin_threshold) layout_h4.addWidget(self.threshold) layout_h4.addStretch() layout_h4.addWidget(self.label_acquisition_count) layout_h4.addWidget(self.spin_acquisition_count) self.widgets_distance = [self.plot_widget_distance, self.spin_average_distance, self.label_average_distance] self.widgets_velocity = [self.plot_widget_velocity, self.spin_average_velocity, self.label_average_velocity] line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addLayout(layout_h1) layout.addWidget(line) layout.addLayout(layout_h2) layout.addLayout(layout_h3) layout.addLayout(layout_h4) def start(self): async_call(self.lrf.get_configuration, None, self.get_configuration_async, self.increase_error_count) async_call(self.lrf.get_enable, None, self.enable_laser.setChecked, self.increase_error_count) async_call(self.lrf.get_moving_average, None, self.get_moving_average_async, self.increase_error_count) self.cbe_distance.set_period(25) self.cbe_velocity.set_period(25) self.plot_widget_distance.stop = False self.plot_widget_velocity.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_velocity.set_period(0) self.plot_widget_distance.stop = True self.plot_widget_velocity.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLaserRangeFinderV2.DEVICE_IDENTIFIER def enable_laser_changed(self, state): self.lrf.set_enable(state == Qt.Checked) def cb_distance(self, distance): self.current_distance.value = distance def cb_velocity(self, velocity): self.current_velocity.value = velocity / 100.0 def configuration_changed(self): acquisition_count = self.spin_acquisition_count.value() enable_quick_termination = self.enable_qick_termination.isChecked() if self.threshold.isChecked(): threshold = 0 else: threshold = self.spin_threshold.value() if self.frequency.isChecked(): frequency = 0 for w in self.widgets_velocity: w.hide() else: frequency = self.spin_frequency.value() for w in self.widgets_velocity: w.show() self.spin_threshold.setDisabled(threshold == 0) self.spin_frequency.setDisabled(frequency == 0) self.lrf.set_configuration(acquisition_count, enable_quick_termination, threshold, frequency) def get_configuration_async(self, conf): self.spin_acquisition_count.blockSignals(True) self.spin_acquisition_count.setValue(conf.acquisition_count) self.spin_acquisition_count.blockSignals(False) self.enable_qick_termination.blockSignals(True) self.enable_qick_termination.setChecked(conf.enable_quick_termination) self.enable_qick_termination.blockSignals(False) self.spin_threshold.blockSignals(True) self.spin_threshold.setValue(conf.threshold_value) self.spin_threshold.setDisabled(conf.threshold_value == 0) self.spin_threshold.blockSignals(False) self.spin_frequency.blockSignals(True) self.spin_frequency.setValue(conf.measurement_frequency) self.spin_frequency.setDisabled(conf.measurement_frequency == 0) self.spin_frequency.blockSignals(False) self.threshold.blockSignals(True) self.threshold.setChecked(conf.threshold_value == 0) self.threshold.blockSignals(False) self.frequency.blockSignals(True) self.frequency.setChecked(conf.measurement_frequency == 0) self.frequency.blockSignals(False) self.configuration_changed() def get_moving_average_async(self, avg): self.spin_average_distance.setValue(avg.distance_average_length) self.spin_average_velocity.setValue(avg.velocity_average_length) def spin_average_finished(self): self.lrf.set_moving_average(self.spin_average_distance.value(), self.spin_average_velocity.value())
class DistanceIRV2(COMCUPluginBase): NUM_VALUES = 512 DIVIDER = 2**12//NUM_VALUES def __init__(self, *args): super().__init__(BrickletDistanceIRV2, *args) self.dist = self.device self.cbe_distance = CallbackEmulator(self.dist.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_analog_value = CallbackEmulator(self.dist.get_analog_value, None, self.cb_analog_value, self.increase_error_count) self.analog_label = AnalogLabel('Analog Value:') hlayout = QHBoxLayout() self.average_label = QLabel('Moving Average Length:') self.average_spin = QSpinBox() self.average_spin.setMinimum(1) self.average_spin.setMaximum(1000) self.average_spin.setSingleStep(1) self.average_spin.setValue(25) self.average_spin.editingFinished.connect(self.average_spin_finished) self.sensor_label = QLabel('Sensor Type:') self.sensor_combo = QComboBox() self.sensor_combo.addItem('2Y0A41 (4-30cm)') self.sensor_combo.addItem('2Y0A21 (10-80cm)') self.sensor_combo.addItem('2Y0A02 (20-150cm)') self.sensor_combo.currentIndexChanged.connect(self.sensor_combo_changed) hlayout.addWidget(self.average_label) hlayout.addWidget(self.average_spin) hlayout.addStretch() hlayout.addWidget(self.sensor_label) hlayout.addWidget(self.sensor_combo) self.current_distance = CurveValueWrapper() # float, cm plots = [('Distance', Qt.red, self.current_distance, '{} cm'.format)] self.plot_widget = PlotWidget('Distance [cm]', plots, extra_key_widgets=[self.analog_label], y_resolution=0.1) line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addWidget(self.plot_widget) layout.addWidget(line) layout.addLayout(hlayout) def sensor_combo_changed(self, index): self.dist.set_sensor_type(index) def average_spin_finished(self): self.dist.set_moving_average_configuration(self.average_spin.value()) def get_moving_average_configuration_async(self, average): self.average_spin.blockSignals(True) self.average_spin.setValue(average) self.average_spin.blockSignals(False) def get_sensor_type_async(self, sensor): self.sensor_combo.blockSignals(True) self.sensor_combo.setCurrentIndex(sensor) self.sensor_combo.blockSignals(False) def start(self): async_call(self.dist.get_moving_average_configuration, None, self.get_moving_average_configuration_async, self.increase_error_count) async_call(self.dist.get_sensor_type, None, self.get_sensor_type_async, self.increase_error_count) self.cbe_distance.set_period(10) self.cbe_analog_value.set_period(100) self.plot_widget.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_analog_value.set_period(0) self.plot_widget.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletDistanceIRV2.DEVICE_IDENTIFIER def cb_distance(self, distance): self.current_distance.value = distance / 10.0 def cb_analog_value(self, analog_value): self.analog_label.setText(analog_value)
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 QFrameSelect(QWidget): frameSelectionChanged = pyqtSignal(int, QTime) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) layout = QVBoxLayout() self.setLayout(layout) self.slider = Slider(self) self.slider.setOrientation(Qt.Horizontal) self.slider.valueChanged.connect(self._handleSliderChange) self.slider.setTickInterval(1) layout.addWidget(self.slider) hlayout = QHBoxLayout() layout.addLayout(hlayout) self.leftLabel = QLabel(self) self.spinBox = QSpinBox(self) self.spinBox.valueChanged.connect(self._handleSpinboxChange) self.currentTime = QTimeEdit(self) self.currentTime.setDisplayFormat("H:mm:ss.zzz") self.currentTime.timeChanged.connect(self._handleTimeEditChange) self.currentTime.editingFinished.connect(self._handleTimeEditFinished) self.rightLabel = QLabel(self) hlayout.addWidget(self.leftLabel) hlayout.addStretch() hlayout.addWidget(QLabel("Frame index:", self)) hlayout.addWidget(self.spinBox) hlayout.addWidget(QLabel("Timestamp:", self)) hlayout.addWidget(self.currentTime) hlayout.addStretch() hlayout.addWidget(self.rightLabel) self.setPtsTimeArray(None) def setValue(self, n): self.slider.setValue(n) def setMinimum(self, n=0): self.slider.setMinimum(n) self.spinBox.setMinimum(n) ms = int(self.pts_time[n] * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.setMinimumTime(QTime(h, m, s, ms)) self.leftLabel.setText(f"{n} ({h}:{m:02d}:{s:02d}.{ms:03d})") def setMaximum(self, n=None): if n is None: n = len(self.pts_time) - 1 self.slider.setMaximum(n) self.spinBox.setMaximum(n) ms = int(self.pts_time[n] * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.setMaximumTime(QTime(h, m, s, ms)) self.rightLabel.setText(f"{n} ({h}:{m:02d}:{s:02d}.{ms:03d})") def setStartEndVisible(self, value): self.leftLabel.setHidden(not bool(value)) self.rightLabel.setHidden(not bool(value)) def setPtsTimeArray(self, pts_time=None): self.pts_time = pts_time if pts_time is not None: N = len(pts_time) self.setMinimum(0) self.setMaximum(N - 1) self.slider.setValue(0) self.slider.setSnapValues(None) self.slider.setDisabled(False) self.spinBox.setDisabled(False) self.currentTime.setDisabled(False) else: self.slider.setMinimum(0) self.slider.setMaximum(0) self.slider.setSnapValues(None) self.slider.setDisabled(True) self.spinBox.setDisabled(True) self.currentTime.setDisabled(True) def _handleSliderChange(self, n): self.spinBox.blockSignals(True) self.spinBox.setValue(n) self.spinBox.blockSignals(False) pts_time = self.pts_time[n] ms = int(pts_time * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.blockSignals(True) self.currentTime.setTime(QTime(h, m, s, ms)) self.currentTime.blockSignals(False) self.frameSelectionChanged.emit(n, QTime(h, m, s, ms)) def _handleSpinboxChange(self, n): self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) pts_time = self.pts_time[n] ms = int(pts_time * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.currentTime.blockSignals(True) self.currentTime.setTime(QTime(h, m, s, ms)) self.currentTime.blockSignals(False) self.frameSelectionChanged.emit(n, QTime(h, m, s, ms)) def _handleTimeEditChange(self, t): pts = t.msecsSinceStartOfDay() / 1000 try: n = search(self.pts_time, pts + 0.0005, dir="-") except IndexError: n = 0 if n != self.slider.value(): self.slider.blockSignals(True) self.slider.setValue(n) self.slider.blockSignals(False) self.spinBox.blockSignals(True) self.spinBox.setValue(n) self.spinBox.blockSignals(False) pts_time = self.pts_time[n] ms = int(pts_time * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) self.frameSelectionChanged.emit(n, QTime(h, m, s, ms)) def _handleTimeEditFinished(self): t = self.currentTime.time() pts = t.msecsSinceStartOfDay() / 1000 n = search(self.pts_time, pts + 0.0005, dir="-") pts_time = self.pts_time[n] ms = int(pts_time * 1000 + 0.5) s, ms = divmod(ms, 1000) m, s = divmod(s, 60) h, m = divmod(m, 60) T = QTime(h, m, s, ms) if t != T: self.currentTime.setTime(T)
class DateTimeDialog(QDialog): """Dialog to specify time in the recordings, either as seconds from the start of the recordings or absolute time. Parameters ---------- title : str 'Lights out' or 'Lights on' start_time : datetime absolute start time of the recordings dur : int total duration of the recordings Notes ----- The value of interest is in self.idx_seconds.value(), which is seconds from the start of the recordings. """ def __init__(self, title, start_time, dur): super().__init__() self.start_time = start_time self.dur = dur end_time = start_time + timedelta(seconds=dur) self.setWindowTitle(title) bbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.idx_ok = bbox.button(QDialogButtonBox.Ok) self.idx_cancel = bbox.button(QDialogButtonBox.Cancel) bbox.clicked.connect(self.button_clicked) self.idx_seconds = QSpinBox() self.idx_seconds.setMinimum(0) self.idx_seconds.setMaximum(dur) self.idx_seconds.valueChanged.connect(self.changed_spin) self.idx_datetime = QDateTimeEdit(start_time) self.idx_datetime.setMinimumDate(start_time) self.idx_datetime.setMaximumDate(end_time) self.idx_datetime.setDisplayFormat('dd-MMM-yyyy HH:mm:ss') self.idx_datetime.dateTimeChanged.connect(self.changed_datetime) layout = QFormLayout() layout.addRow('', QLabel('Enter ' + title + ' time')) layout.addRow('Seconds from recording start', self.idx_seconds) layout.addRow('Absolute time', self.idx_datetime) layout.addRow(bbox) self.setLayout(layout) def button_clicked(self, button): if button == self.idx_ok: self.accept() elif button == self.idx_cancel: self.reject() def changed_spin(self, i): self.idx_datetime.blockSignals(True) self.idx_datetime.setDateTime(self.start_time + timedelta(seconds=i)) self.idx_datetime.blockSignals(False) def changed_datetime(self, dt): val = (dt.toPyDateTime() - self.start_time).total_seconds() if val < 0 or val >= self.dur: val = min(self.dur, max(val, 0)) self.changed_spin(val) self.idx_seconds.blockSignals(True) self.idx_seconds.setValue(val) self.idx_seconds.blockSignals(False)
class PlotWidget(QWidget): def __init__(self, y_scale_title_text, curve_configs, clear_button='default', parent=None, x_scale_visible=True, y_scale_visible=True, curve_outer_border_visible=True, curve_motion_granularity=1.0, # seconds canvas_color=QColor(245, 245, 245), external_timer=None, key='top-value', # top-value, right-no-icon extra_key_widgets=None, update_interval=0.1, # seconds curve_start='left', moving_average_config=None, x_scale_title_text='Time [s]', x_diff=20, x_scale_skip_last_tick=True, y_resolution=None, y_scale_shrinkable=True): super().__init__(parent) self.setMinimumSize(300, 250) if y_resolution != None: y_diff_min = y_resolution * 20 else: y_diff_min = None self._stop = True self.curve_configs = [CurveConfig(*curve_config) for curve_config in curve_configs] for curve_config in self.curve_configs: if curve_config.value_wrapper != None: assert isinstance(curve_config.value_wrapper, CurveValueWrapper) self.plot = Plot(self, x_scale_title_text, y_scale_title_text, x_scale_skip_last_tick, self.curve_configs, x_scale_visible, y_scale_visible, curve_outer_border_visible, curve_motion_granularity, canvas_color, curve_start, x_diff, y_diff_min, y_scale_shrinkable, update_interval) self.set_x_scale = self.plot.set_x_scale self.set_fixed_y_scale = self.plot.set_fixed_y_scale self.key = key self.key_items = [] self.key_has_values = key.endswith('-value') if key != None else False self.first_show = True self.timestamp = 0 # seconds self.update_interval = update_interval # seconds h1layout = QHBoxLayout() h1layout.setContentsMargins(0, 0, 0, 0) h1layout_empty = True if clear_button == 'default': self.clear_button = QToolButton() self.clear_button.setText('Clear Graph') h1layout.addWidget(self.clear_button) h1layout.addStretch(1) h1layout_empty = False else: self.clear_button = clear_button if self.clear_button != None: self.clear_button.clicked.connect(self.clear_clicked) v1layout = None if self.key != None: if len(self.curve_configs) == 1: label = FixedSizeLabel(self) label.setText(self.curve_configs[0].title) self.key_items.append(label) else: for i, curve_config in enumerate(self.curve_configs): button = FixedSizeToolButton(self) button.setText(curve_config.title) if self.key.endswith('-no-icon'): button.setStyleSheet('color: ' + QColor(curve_config.color).name()) else: pixmap = QPixmap(10, 2) QPainter(pixmap).fillRect(0, 0, 10, 2, curve_config.color) button.setIcon(QIcon(pixmap)) button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) button.setCheckable(True) button.setChecked(True) button.toggled.connect(functools.partial(self.plot.show_curve, i)) self.key_items.append(button) if self.key.startswith('top-'): for key_item in self.key_items: h1layout.addWidget(key_item) h1layout_empty = False elif self.key.startswith('right-'): v1layout = QVBoxLayout() v1layout.setContentsMargins(0, 0, 0, 0) v1layout.addSpacing(self.plot.get_legend_offset_y()) for key_item in self.key_items: v1layout.addWidget(key_item) v1layout.addStretch(1) else: assert False, 'unknown key: ' + self.key if not h1layout_empty: h1layout.addStretch(1) if extra_key_widgets != None: if self.key == None or self.key.startswith('top'): for widget in extra_key_widgets: h1layout.addWidget(widget) h1layout_empty = False elif self.key.startswith('right'): if v1layout == None: v1layout = QVBoxLayout() v1layout.setContentsMargins(0, 0, 0, 0) v1layout.addSpacing(self.plot.get_legend_offset_y()) if self.key.startswith('top'): for widget in extra_key_widgets: v1layout.addWidget(widget) v2layout = QVBoxLayout(self) v2layout.setContentsMargins(0, 0, 0, 0) if not h1layout_empty: v2layout.addLayout(h1layout) if v1layout != None: h2layout = QHBoxLayout() h2layout.setContentsMargins(0, 0, 0, 0) h2layout.addWidget(self.plot) h2layout.addLayout(v1layout) v2layout.addLayout(h2layout) else: v2layout.addWidget(self.plot) self.moving_average_config = moving_average_config if moving_average_config != None: self.moving_average_label = QLabel('Moving Average Length:') self.moving_average_spinbox = QSpinBox() self.moving_average_spinbox.setMinimum(moving_average_config.min_length) self.moving_average_spinbox.setMaximum(moving_average_config.max_length) self.moving_average_spinbox.setSingleStep(1) self.moving_average_layout = QHBoxLayout() self.moving_average_layout.addStretch() self.moving_average_layout.addWidget(self.moving_average_label) self.moving_average_layout.addWidget(self.moving_average_spinbox) self.moving_average_layout.addStretch() self.moving_average_spinbox.valueChanged.connect(self.moving_average_spinbox_value_changed) v2layout.addLayout(self.moving_average_layout) if external_timer == None: self.timer = QTimer(self) self.timer.timeout.connect(self.add_new_data) self.timer.start(self.update_interval * 1000) else: # assuming that the external timer runs with the configured interval external_timer.timeout.connect(self.add_new_data) def update_y_scale_sibling(self, plot_widget): # internal total_unpadded_width_diff = plot_widget.plot.y_scale.total_unpadded_width - self.plot.y_scale.total_unpadded_width self.plot.y_scale.set_title_text_padding(total_unpadded_width_diff) def add_y_scale_sibling(self, plot_widget): plot_widget.plot.y_scale.total_width_changed.connect(functools.partial(self.update_y_scale_sibling, plot_widget)) def set_moving_average_value(self, value): self.moving_average_spinbox.blockSignals(True) self.moving_average_spinbox.setValue(value) self.moving_average_spinbox.blockSignals(False) def get_moving_average_value(self): return self.moving_average_spinbox.value() def moving_average_spinbox_value_changed(self, value): if self.moving_average_config != None: if self.moving_average_config.callback != None: self.moving_average_config.callback(value) # overrides QWidget.showEvent def showEvent(self, event): QWidget.showEvent(self, event) if self.first_show: self.first_show = False if len(self.key_items) > 1 and self.key.startswith('right'): width = max([key_item.width() for key_item in self.key_items]) for key_item in self.key_items: size = key_item.minimumSize() size.setWidth(width) key_item.setMinimumSize(size) def get_key_item(self, i): return self.key_items[i] def set_data(self, i, x, y): # FIXME: how to set potential key items from this? self.plot.set_data(i, x, y) @property def stop(self): return self._stop @stop.setter def stop(self, stop): for i, curve_config in enumerate(self.curve_configs): if curve_config.value_wrapper == None: continue curve_config.value_wrapper.valid = False curve_config.value_wrapper.locked = stop if stop: self.plot.add_jump(i) self._stop = stop # internal def add_new_data(self): if self.stop: return any_valid = False for i, curve_config in enumerate(self.curve_configs): if curve_config.value_wrapper == None: continue valid = curve_config.value_wrapper.valid value = curve_config.value_wrapper.value if valid: assert value != None any_valid = True if len(self.key_items) > 0 and self.key_has_values: self.key_items[i].setText(curve_config.title + ': ' + curve_config.value_formatter(value)) self.plot.add_data(i, self.timestamp, value) elif len(self.key_items) > 0 and self.key_has_values: self.key_items[i].setText(curve_config.title) if any_valid: self.timestamp += self.update_interval # internal def clear_clicked(self): self.plot.clear_graph() self.timestamp = 0
class OptionsDialog(QDialog): def __init__(self, setting: Settings, have_dutils, parent=None): super(OptionsDialog, self).__init__(parent) self.settings = setting self.enabled_video = True # temporary toggle to disable video features as they do not exist self.enabled_logging = True self.enabled_keybindings = True self.enabled_dutils = have_dutils self.setWindowTitle("Tcam-Capture Options") self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.tabs = QTabWidget() self.general_widget = QWidget() self.keybindings_widget = QWidget() self.logging_widget = QWidget() self.saving_widget = QWidget() self._setup_general_ui() self.tabs.addTab(self.general_widget, "General") if self.enabled_keybindings: self._setup_keybindings_ui() self.tabs.addTab(self.keybindings_widget, "Keybindings") self._setup_saving_ui() self.tabs.addTab(self.saving_widget, "Image/Video") self.layout.addWidget(self.tabs) # OK and Cancel buttons self.buttons = QDialogButtonBox( QDialogButtonBox.Reset | QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) self.layout.addWidget(self.buttons) self.buttons.accepted.connect(self.accept) self.buttons.rejected.connect(self.reject) self.buttons.clicked.connect(self.clicked) def _setup_general_ui(self): """ Create everything related to the general tab """ layout = QFormLayout() layout.setSpacing(20) layout.setVerticalSpacing(20) self.device_dialog_checkbox = QCheckBox(self) device_dialog_label = QLabel("Open device dialog on start:") layout.addRow(device_dialog_label, self.device_dialog_checkbox) self.reopen_device_checkbox = QCheckBox(self) reopen_device_label = QLabel("Reopen device on start(ignores device dialog):", self) layout.addRow(reopen_device_label, self.reopen_device_checkbox) self.use_dutils_checkbox = QCheckBox(self) self.use_dutils_label = QLabel("Use tiscamera dutils, if present:", self) layout.addRow(self.use_dutils_label, self.use_dutils_checkbox) if not self.enabled_dutils: self.use_dutils_label.setToolTip("Enabled when tiscamera-dutils are installed") self.use_dutils_label.setEnabled(False) self.use_dutils_checkbox.setToolTip("Enabled when tiscamera-dutils are installed") self.use_dutils_checkbox.setEnabled(False) self.general_widget.setLayout(layout) def _setup_saving_ui(self): """ Create everything related to the image/video saving tab """ encoder_dict = Encoder.get_encoder_dict() form_layout = QFormLayout() layout = QVBoxLayout() layout.addLayout(form_layout) location_layout = QHBoxLayout() location_label = QLabel("Where to save images/videos:", self) self.location_edit = QLineEdit(self) location_dialog_button = QPushButton("...", self) location_dialog_button.clicked.connect(self.open_file_dialog) location_layout.addWidget(self.location_edit) location_layout.addWidget(location_dialog_button) # maintain descriptions as own labels # pyqt seems to loose the descriptions somewhere # when simple strings are used or the qlabel does not have self as owner form_layout.addRow(location_label, location_layout) self.image_type_combobox = QComboBox(self) for key, value in encoder_dict.items(): if value.encoder_type == Encoder.MediaType.image: self.image_type_combobox.addItem(key) image_type_label = QLabel("Save images as:") self.image_type_combobox.currentIndexChanged['QString'].connect(self.image_name_suffix_changed) form_layout.addRow(image_type_label, self.image_type_combobox) if self.enabled_video: self.video_type_combobox = QComboBox(self) for key, value in encoder_dict.items(): if value.encoder_type == Encoder.MediaType.video: self.video_type_combobox.addItem(key) self.video_type_combobox.currentIndexChanged['QString'].connect(self.video_name_suffix_changed) video_type_label = QLabel("Save videos as:", self) form_layout.addRow(video_type_label, self.video_type_combobox) image_name_groupbox = QGroupBox("Image File Names") groupbox_layout = QFormLayout() image_name_groupbox.setLayout(groupbox_layout) self.image_name_preview = QLabel("<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png") self.image_name_preview_description = QLabel("Images will be named like:") groupbox_layout.addRow(self.image_name_preview_description, self.image_name_preview) self.image_name_prefix = QLineEdit() self.image_name_prefix.textChanged.connect(self.image_name_prefix_changed) self.image_name_prefix.setMaxLength(100) self.image_name_prefix_description = QLabel("User Prefix:", self) groupbox_layout.addRow(self.image_name_prefix_description, self.image_name_prefix) self.image_name_serial = QCheckBox(self) self.image_name_serial.toggled.connect(self.image_name_properties_toggled) self.image_name_serial_description = QLabel("Include Serial:") groupbox_layout.addRow(self.image_name_serial_description, self.image_name_serial) self.image_name_format = QCheckBox(self) self.image_name_format.toggled.connect(self.image_name_properties_toggled) self.image_name_format_description = QLabel("Include Format:") groupbox_layout.addRow(self.image_name_format_description, self.image_name_format) self.image_name_counter = QCheckBox(self) self.image_name_counter.toggled.connect(self.image_name_properties_toggled) self.image_name_counter_description = QLabel("Include Counter:") groupbox_layout.addRow(self.image_name_counter_description, self.image_name_counter) self.image_name_counter_box = QSpinBox(self) self.image_name_counter_box.setRange(1, 10) self.image_name_counter_box.valueChanged.connect(self.image_name_counter_changed) self.image_name_counter_box_description = QLabel("Counter Size:") groupbox_layout.addRow(self.image_name_counter_box_description, self.image_name_counter_box) self.image_name_counter.toggled.connect(self.toggle_image_counter_box_availability) self.image_name_counter.toggled.connect(self.image_name_properties_toggled) self.image_name_timestamp = QCheckBox(self) self.image_name_timestamp.toggled.connect(self.image_name_properties_toggled) self.image_name_timestamp_description = QLabel("Include Timestamp:") groupbox_layout.addRow(self.image_name_timestamp_description, self.image_name_timestamp) layout.addWidget(image_name_groupbox) video_groupbox = QGroupBox("Video File Names") video_layout = QFormLayout() video_groupbox.setLayout(video_layout) self.video_name_preview = QLabel("<USER-PREFIX>-<SERIAL>-<FORMAT>-<TIMESTAMP>-<COUNTER>.png") self.video_name_preview_description = QLabel("Videos will be named like:") video_layout.addRow(self.video_name_preview_description, self.video_name_preview) self.video_name_prefix = QLineEdit() self.video_name_prefix.textChanged.connect(self.video_name_prefix_changed) self.video_name_prefix.setMaxLength(100) self.video_name_prefix_description = QLabel("User Prefix:", self) video_layout.addRow(self.video_name_prefix_description, self.video_name_prefix) self.video_name_serial = QCheckBox(self) self.video_name_serial.toggled.connect(self.video_name_properties_toggled) self.video_name_serial_description = QLabel("Include Serial:") video_layout.addRow(self.video_name_serial_description, self.video_name_serial) self.video_name_format = QCheckBox(self) self.video_name_format.toggled.connect(self.video_name_properties_toggled) self.video_name_format_description = QLabel("Include Format:") video_layout.addRow(self.video_name_format_description, self.video_name_format) self.video_name_counter = QCheckBox(self) self.video_name_counter.toggled.connect(self.video_name_properties_toggled) self.video_name_counter_description = QLabel("Include Counter:") video_layout.addRow(self.video_name_counter_description, self.video_name_counter) self.video_name_counter_box = QSpinBox(self) self.video_name_counter_box.setRange(1, 10) self.video_name_counter_box.valueChanged.connect(self.video_name_counter_changed) self.video_name_counter_box_description = QLabel("Counter Size:") video_layout.addRow(self.video_name_counter_box_description, self.video_name_counter_box) self.video_name_counter.toggled.connect(self.toggle_video_counter_box_availability) self.video_name_counter.toggled.connect(self.video_name_properties_toggled) self.video_name_timestamp = QCheckBox(self) self.video_name_timestamp.toggled.connect(self.video_name_properties_toggled) self.video_name_timestamp_description = QLabel("Include Timestamp:") video_layout.addRow(self.video_name_timestamp_description, self.video_name_timestamp) layout.addWidget(video_groupbox) self.saving_widget.setLayout(layout) def image_name_prefix_changed(self, name: str): """""" self.settings.image_name.user_prefix = self.image_name_prefix.text() self.update_image_name_preview() def image_name_suffix_changed(self, suffix: str): """""" self.update_image_name_preview() def image_name_counter_changed(self, name: str): """""" self.settings.image_name.counter_size = self.image_name_counter_box.value() self.update_image_name_preview() def image_name_properties_toggled(self): """""" self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked() self.settings.image_name.include_counter = self.image_name_counter.isChecked() self.settings.image_name.include_format = self.image_name_format.isChecked() self.settings.image_name.include_serial = self.image_name_serial.isChecked() self.update_image_name_preview() def update_image_name_preview(self): preview_string = "" if self.settings.image_name.user_prefix != "": max_prefix_length = 15 prefix = (self.settings.image_name.user_prefix[:max_prefix_length] + '..') if len(self.settings.image_name.user_prefix) > max_prefix_length else self.settings.image_name.user_prefix preview_string += prefix if self.settings.image_name.include_serial: if preview_string != "": preview_string += "-" preview_string += "00001234" if self.settings.image_name.include_format: if preview_string != "": preview_string += "-" preview_string += "gbrg_1920x1080_15_1" if self.settings.image_name.include_timestamp: if preview_string != "": preview_string += "-" preview_string += "19701230T125503" if self.settings.image_name.include_counter: if preview_string != "": preview_string += "-" preview_string += '{message:0>{fill}}'.format(message=1, fill=self.settings.image_name.counter_size) if preview_string == "": preview_string = "image" preview_string += "." + self.image_type_combobox.currentText() self.image_name_preview.setText(preview_string) def video_name_prefix_changed(self, name: str): """""" self.settings.video_name.user_prefix = self.video_name_prefix.text() self.update_video_name_preview() def video_name_suffix_changed(self, suffix: str): """""" self.update_video_name_preview() def video_name_counter_changed(self, name: str): """""" self.settings.video_name.counter_size = self.video_name_counter_box.value() self.update_video_name_preview() def video_name_properties_toggled(self): """""" self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked() self.settings.video_name.include_counter = self.video_name_counter.isChecked() self.settings.video_name.include_format = self.video_name_format.isChecked() self.settings.video_name.include_serial = self.video_name_serial.isChecked() self.update_video_name_preview() def update_video_name_preview(self): preview_string = "" if self.settings.video_name.user_prefix != "": # This is a convenience change to the displayed string. # We only display an amount of max_prefix_length # chars to save screen space max_prefix_length = 15 prefix = (self.settings.video_name.user_prefix[:max_prefix_length] + '..') if len(self.settings.video_name.user_prefix) > max_prefix_length else self.settings.video_name.user_prefix preview_string += prefix if self.settings.video_name.include_serial: if preview_string != "": preview_string += "-" preview_string += "00001234" if self.settings.video_name.include_format: if preview_string != "": preview_string += "-" preview_string += "gbrg_1920x1080_15_1" if self.settings.video_name.include_timestamp: if preview_string != "": preview_string += "-" preview_string += "19701230T125503" if self.settings.video_name.include_counter: if preview_string != "": preview_string += "-" preview_string += '{message:0>{fill}}'.format(message=1, fill=self.settings.video_name.counter_size) if preview_string == "": preview_string = "video" preview_string += "." + self.video_type_combobox.currentText() self.video_name_preview.setText(preview_string) def toggle_image_counter_box_availability(self): """""" if self.image_name_counter.isChecked(): self.image_name_counter_box.setEnabled(True) else: self.image_name_counter_box.setEnabled(False) def toggle_video_counter_box_availability(self): """""" if self.video_name_counter.isChecked(): self.video_name_counter_box.setEnabled(True) else: self.video_name_counter_box.setEnabled(False) def _setup_keybindings_ui(self): """ Create everything related to the keybindings tab """ layout = QFormLayout() self.keybinding_fullscreen_label = QLabel("Toggle Fullscreen:") self.keybinding_fullscreen = QKeySequenceEdit() layout.addRow(self.keybinding_fullscreen_label, self.keybinding_fullscreen) self.keybinding_save_image_label = QLabel("Save image:") self.keybinding_save_image = QKeySequenceEdit(QKeySequence(self.settings.keybinding_save_image)) layout.addRow(self.keybinding_save_image_label, self.keybinding_save_image) self.keybinding_trigger_image_label = QLabel("Trigger images via softwaretrigger:") self.keybinding_trigger_image = QKeySequenceEdit(QKeySequence(self.settings.keybinding_trigger_image)) layout.addRow(self.keybinding_trigger_image_label, self.keybinding_trigger_image) self.keybinding_open_dialog_label = QLabel("Open device dialog:") self.keybinding_open_dialog = QKeySequenceEdit(QKeySequence(self.settings.keybinding_open_dialog)) layout.addRow(self.keybinding_open_dialog_label, self.keybinding_open_dialog) self.keybindings_widget.setLayout(layout) def set_settings(self, settings: Settings): self.location_edit.setText(settings.get_save_location()) self.image_type_combobox.setCurrentText(settings.get_image_type()) if self.enabled_video: self.video_type_combobox.setCurrentText(settings.get_video_type()) self.device_dialog_checkbox.setChecked(settings.show_device_dialog_on_startup) self.reopen_device_checkbox.setChecked(settings.reopen_device_on_startup) self.use_dutils_checkbox.setChecked(settings.use_dutils) # # keybindings # if self.enabled_keybindings: self.keybinding_fullscreen.setKeySequence(QKeySequence(self.settings.keybinding_fullscreen)) self.keybinding_save_image.setKeySequence(QKeySequence(self.settings.keybinding_save_image)) self.keybinding_trigger_image.setKeySequence(QKeySequence(self.settings.keybinding_trigger_image)) self.keybinding_open_dialog.setKeySequence(QKeySequence(self.settings.keybinding_open_dialog)) # # image saving # if settings.image_name.include_timestamp: self.image_name_timestamp.blockSignals(True) self.image_name_timestamp.toggle() self.image_name_timestamp.blockSignals(False) if settings.image_name.include_counter: self.image_name_counter.blockSignals(True) self.image_name_counter.toggle() self.image_name_counter.blockSignals(False) self.image_name_counter_box.blockSignals(True) self.image_name_counter_box.setValue(settings.image_name.counter_size) self.image_name_counter_box.blockSignals(False) self.toggle_image_counter_box_availability() if settings.image_name.include_format: self.image_name_format.blockSignals(True) self.image_name_format.toggle() self.image_name_format.blockSignals(False) if settings.image_name.include_serial: self.image_name_serial.blockSignals(True) self.image_name_serial.toggle() self.image_name_serial.blockSignals(False) self.image_name_prefix.blockSignals(True) self.image_name_prefix.setText(settings.image_name.user_prefix) self.image_name_prefix.blockSignals(False) self.update_image_name_preview() # # video saving # if settings.video_name.include_timestamp: self.video_name_timestamp.blockSignals(True) self.video_name_timestamp.toggle() self.video_name_timestamp.blockSignals(False) if settings.video_name.include_counter: self.video_name_counter.blockSignals(True) self.video_name_counter.toggle() self.video_name_counter.blockSignals(False) self.video_name_counter_box.blockSignals(True) self.video_name_counter_box.setValue(settings.video_name.counter_size) self.video_name_counter_box.blockSignals(False) self.toggle_video_counter_box_availability() if settings.video_name.include_format: self.video_name_format.blockSignals(True) self.video_name_format.toggle() self.video_name_format.blockSignals(False) if settings.video_name.include_serial: self.video_name_serial.blockSignals(True) self.video_name_serial.toggle() self.video_name_serial.blockSignals(False) self.video_name_prefix.blockSignals(True) self.video_name_prefix.setText(settings.video_name.user_prefix) self.video_name_prefix.blockSignals(False) self.update_video_name_preview() def save_settings(self): self.settings.save_location = self.location_edit.text() self.settings.image_type = self.image_type_combobox.currentText() if self.enabled_video: self.settings.video_type = self.video_type_combobox.currentText() self.settings.show_device_dialog_on_startup = self.device_dialog_checkbox.isChecked() self.settings.reopen_device_on_startup = self.reopen_device_checkbox.isChecked() self.settings.use_dutils = self.use_dutils_checkbox.isChecked() # # keybindings # if self.enabled_keybindings: self.settings.keybinding_fullscreen = self.keybinding_fullscreen.keySequence().toString() self.settings.keybinding_save_image = self.keybinding_save_image.keySequence().toString() self.settings.keybinding_trigger_image = self.keybinding_trigger_image.keySequence().toString() self.settings.keybinding_open_dialog = self.keybinding_open_dialog.keySequence().toString() # # image saving # self.settings.image_name.include_timestamp = self.image_name_timestamp.isChecked() self.settings.image_name.include_counter = self.image_name_counter.isChecked() if self.image_name_counter.isChecked(): self.settings.image_name.counter_size = self.image_name_counter_box.value() self.settings.image_name.include_format = self.image_name_format.isChecked() self.settings.image_name.include_serial = self.image_name_serial.isChecked() self.settings.image_name.user_prefix = self.image_name_prefix.text() # # video saving # self.settings.video_name.include_timestamp = self.video_name_timestamp.isChecked() self.settings.video_name.include_counter = self.video_name_counter.isChecked() if self.video_name_counter.isChecked(): self.settings.video_name.counter_size = self.video_name_counter_box.value() self.settings.video_name.include_format = self.video_name_format.isChecked() self.settings.video_name.include_serial = self.video_name_serial.isChecked() self.settings.video_name.user_prefix = self.video_name_prefix.text() def open_file_dialog(self): fdia = QFileDialog() fdia.setFileMode(QFileDialog.Directory) fdia.setWindowTitle("Select Directory for saving images and videos") if fdia.exec_(): self.location_edit.setText(fdia.selectedFiles()[0]) def get_location(self): return self.location_edit.text() def get_image_format(self): return self.image_type_combobox.currentText() def get_video_format(self): return self.video_type_combobox.currentText() def clicked(self, button): if self.buttons.buttonRole(button) == QDialogButtonBox.ResetRole: self.reset() def reset(self): """""" log.info("reset called") self.settings.reset() self.set_settings(self.settings) @staticmethod def get_options(settings, parent=None): dialog = OptionsDialog(settings, parent) if settings is not None: dialog.set_settings(settings) result = dialog.exec_() if result == QDialog.Accepted: dialog.save_settings() settings.save() return result == QDialog.Accepted
class SpinnerDialComboWidget(QWidget): value_changed = pyqtSignal() # name: The string name that will be displayed on top of the widget # default_value: The value that will be initially set as the widget's value # min_val: The minimum value that will be initially set # max_val: The maximum value that will be initially set def __init__(self, name="", default_value=0, min_val=0, max_val=100, parent=None): QWidget.__init__(self, parent=parent) # The minimum value that can be set self.min_val = min_val # The maximum value that can be set self.max_val = max_val # The widget's current value self.value = default_value self.title_label = QLabel(name) # The widget's dial self.dial = QDial(self) self.dial.setSingleStep(1) self.dial.setPageStep(1) self.dial.setMinimum(min_val) self.dial.setMaximum(max_val) self.dial.setValue(default_value) self.dial.valueChanged.connect(self.on_dial_changed) # The widget's spin box self.spinner = QSpinBox(self) self.spinner.setMinimum(min_val) self.spinner.setMaximum(max_val) self.spinner.setValue(default_value) self.spinner.valueChanged.connect(self.on_spinner_changed) self.setup_gui() # Sets up the positioning of the UI elements def setup_gui(self): vertical_layout = QVBoxLayout(self) vertical_layout.addStretch(1) vertical_layout.addWidget(self.title_label) vertical_layout.addWidget(self.spinner) vertical_layout.addWidget(self.dial) # The callback for when the dial is changes @pyqtSlot() def on_dial_changed(self): self.value = self.dial.value() self.spinner.blockSignals(True) self.spinner.setValue(self.dial.value()) self.spinner.blockSignals(False) self.value_changed.emit() # The callback for when the spin box is changed @pyqtSlot() def on_spinner_changed(self): self.value = self.spinner.value() self.dial.blockSignals(True) self.dial.setValue(self.spinner.value()) self.dial.blockSignals(False) self.value_changed.emit() # Sets the minimum value # new_min: The new minimum value to be set def set_min(self, new_min): if new_min > self.max_val: return self.min_val = new_min self.dial.blockSignals(True) self.spinner.blockSignals(True) self.spinner.setMinimum(new_min) self.dial.setMinimum(new_min) self.dial.blockSignals(False) self.spinner.blockSignals(False) self.value_changed.emit() # Sets the maximum value # new_max: The new maximum value to be set def set_max(self, new_max): if new_max < self.min_val: return self.max_val = new_max self.dial.blockSignals(True) self.spinner.blockSignals(True) self.spinner.setMaximum(new_max) self.dial.setMaximum(new_max) self.dial.blockSignals(False) self.spinner.blockSignals(False) self.value_changed.emit() # Sets the widget value # value: The value to be set def set_value(self, value): self.value = value self.dial.blockSignals(True) self.spinner.blockSignals(True) self.dial.setValue(value) self.spinner.setValue(value) self.dial.blockSignals(False) self.spinner.blockSignals(False) self.value_changed.emit()
class LaserRangeFinder(PluginBase): def __init__(self, *args): super().__init__(BrickletLaserRangeFinder, *args) self.lrf = self.device # the firmware version of a EEPROM Bricklet can (under common circumstances) # not change during the lifetime of an EEPROM Bricklet plugin. therefore, # it's okay to make final decisions based on it here self.has_sensor_hardware_version_api = self.firmware_version >= (2, 0, 3) self.has_configuration_api = self.firmware_version >= (2, 0, 3) self.cbe_distance = CallbackEmulator(self.lrf.get_distance, None, self.cb_distance, self.increase_error_count) self.cbe_velocity = CallbackEmulator(self.lrf.get_velocity, None, self.cb_velocity, self.increase_error_count) self.current_distance = CurveValueWrapper() # int, cm self.current_velocity = CurveValueWrapper() # float, m/s plots_distance = [('Distance', Qt.red, self.current_distance, format_distance)] plots_velocity = [('Velocity', Qt.red, self.current_velocity, '{:.2f} m/s'.format)] self.plot_widget_distance = PlotWidget('Distance [cm]', plots_distance, y_resolution=1.0) self.plot_widget_velocity = PlotWidget('Velocity [m/s]', plots_velocity, y_resolution=0.01) self.mode_label = QLabel('Mode:') self.mode_combo = QComboBox() self.mode_combo.addItem("Distance: 1cm resolution, 40m max") self.mode_combo.addItem("Velocity: 0.10 m/s resolution, 12.70m/s max") self.mode_combo.addItem("Velocity: 0.25 m/s resolution, 31.75m/s max") self.mode_combo.addItem("Velocity: 0.50 m/s resolution, 63.50m/s max") self.mode_combo.addItem("Velocity: 1.00 m/s resolution, 127.00m/s max") self.mode_combo.currentIndexChanged.connect(self.mode_changed) self.mode_combo.hide() self.label_average_distance = QLabel('Moving Average for Distance:') self.spin_average_distance = QSpinBox() self.spin_average_distance.setMinimum(0) self.spin_average_distance.setMaximum(50) self.spin_average_distance.setSingleStep(1) self.spin_average_distance.setValue(10) self.spin_average_distance.editingFinished.connect( self.spin_average_finished) self.label_average_velocity = QLabel('Moving Average for Velocity:') self.spin_average_velocity = QSpinBox() self.spin_average_velocity.setMinimum(0) self.spin_average_velocity.setMaximum(50) self.spin_average_velocity.setSingleStep(1) self.spin_average_velocity.setValue(10) self.spin_average_velocity.editingFinished.connect( self.spin_average_finished) self.enable_laser = QCheckBox("Enable Laser") self.enable_laser.stateChanged.connect(self.enable_laser_changed) self.label_acquisition_count = QLabel('Acquisition Count:') self.spin_acquisition_count = QSpinBox() self.spin_acquisition_count.setMinimum(1) self.spin_acquisition_count.setMaximum(255) self.spin_acquisition_count.setSingleStep(1) self.spin_acquisition_count.setValue(128) self.enable_qick_termination = QCheckBox("Quick Termination") self.label_threshold = QLabel('Threshold:') self.threshold = QCheckBox("Automatic Threshold") self.spin_threshold = QSpinBox() self.spin_threshold.setMinimum(1) self.spin_threshold.setMaximum(255) self.spin_threshold.setSingleStep(1) self.spin_threshold.setValue(1) self.label_frequency = QLabel('Frequency [Hz]:') self.frequency = QCheckBox( "Automatic Frequency (Disable for Velocity)") self.spin_frequency = QSpinBox() self.spin_frequency.setMinimum(10) self.spin_frequency.setMaximum(500) self.spin_frequency.setSingleStep(1) self.spin_frequency.setValue(10) self.spin_acquisition_count.editingFinished.connect( self.configuration_changed) self.enable_qick_termination.stateChanged.connect( self.configuration_changed) self.spin_threshold.editingFinished.connect(self.configuration_changed) self.threshold.stateChanged.connect(self.configuration_changed) self.spin_frequency.editingFinished.connect(self.configuration_changed) self.frequency.stateChanged.connect(self.configuration_changed) layout_h1 = QHBoxLayout() layout_h1.addWidget(self.plot_widget_distance) layout_h1.addWidget(self.plot_widget_velocity) layout_h2 = QHBoxLayout() layout_h2.addWidget(self.mode_label) layout_h2.addWidget(self.mode_combo) layout_h2.addWidget(self.label_average_distance) layout_h2.addWidget(self.spin_average_distance) layout_h2.addWidget(self.label_average_velocity) layout_h2.addWidget(self.spin_average_velocity) layout_h2.addStretch() layout_h2.addWidget(self.enable_laser) layout_h3 = QHBoxLayout() layout_h3.addWidget(self.label_frequency) layout_h3.addWidget(self.spin_frequency) layout_h3.addWidget(self.frequency) layout_h3.addStretch() layout_h3.addWidget(self.enable_qick_termination) layout_h4 = QHBoxLayout() layout_h4.addWidget(self.label_threshold) layout_h4.addWidget(self.spin_threshold) layout_h4.addWidget(self.threshold) layout_h4.addStretch() layout_h4.addWidget(self.label_acquisition_count) layout_h4.addWidget(self.spin_acquisition_count) self.widgets_distance = [ self.plot_widget_distance, self.spin_average_distance, self.label_average_distance ] self.widgets_velocity = [ self.plot_widget_velocity, self.spin_average_velocity, self.label_average_velocity ] for w in self.widgets_distance: w.hide() for w in self.widgets_velocity: w.hide() line = QFrame() line.setObjectName("line") line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) layout = QVBoxLayout(self) layout.addLayout(layout_h1) layout.addWidget(line) layout.addLayout(layout_h2) layout.addLayout(layout_h3) layout.addLayout(layout_h4) def start(self): if self.has_sensor_hardware_version_api: async_call(self.lrf.get_sensor_hardware_version, None, self.get_sensor_hardware_version_async, self.increase_error_count) else: self.get_sensor_hardware_version_async(1) if self.has_configuration_api: async_call(self.lrf.get_configuration, None, self.get_configuration_async, self.increase_error_count) async_call(self.lrf.get_mode, None, self.get_mode_async, self.increase_error_count) async_call(self.lrf.is_laser_enabled, None, self.enable_laser.setChecked, self.increase_error_count) async_call(self.lrf.get_moving_average, None, self.get_moving_average_async, self.increase_error_count) self.cbe_distance.set_period(25) self.cbe_velocity.set_period(25) self.plot_widget_distance.stop = False self.plot_widget_velocity.stop = False def stop(self): self.cbe_distance.set_period(0) self.cbe_velocity.set_period(0) self.plot_widget_distance.stop = True self.plot_widget_velocity.stop = True def destroy(self): pass @staticmethod def has_device_identifier(device_identifier): return device_identifier == BrickletLaserRangeFinder.DEVICE_IDENTIFIER def enable_laser_changed(self, state): if state == Qt.Checked: self.lrf.enable_laser() else: self.lrf.disable_laser() def mode_changed(self, value): if value < 0 or value > 4: return self.lrf.set_mode(value) if value == 0: for w in self.widgets_velocity: w.hide() for w in self.widgets_distance: w.show() else: for w in self.widgets_distance: w.hide() for w in self.widgets_velocity: w.show() def cb_distance(self, distance): self.current_distance.value = distance def cb_velocity(self, velocity): self.current_velocity.value = velocity / 100.0 def configuration_changed(self): acquisition_count = self.spin_acquisition_count.value() enable_quick_termination = self.enable_qick_termination.isChecked() if self.threshold.isChecked(): threshold = 0 else: threshold = self.spin_threshold.value() if self.frequency.isChecked(): frequency = 0 for w in self.widgets_velocity: w.hide() else: frequency = self.spin_frequency.value() for w in self.widgets_velocity: w.show() self.spin_threshold.setDisabled(threshold == 0) self.spin_frequency.setDisabled(frequency == 0) self.lrf.set_configuration(acquisition_count, enable_quick_termination, threshold, frequency) def get_configuration_async(self, conf): self.spin_acquisition_count.blockSignals(True) self.spin_acquisition_count.setValue(conf.acquisition_count) self.spin_acquisition_count.blockSignals(False) self.enable_qick_termination.blockSignals(True) self.enable_qick_termination.setChecked(conf.enable_quick_termination) self.enable_qick_termination.blockSignals(False) self.spin_threshold.blockSignals(True) self.spin_threshold.setValue(conf.threshold_value) self.spin_threshold.setDisabled(conf.threshold_value == 0) self.spin_threshold.blockSignals(False) self.spin_frequency.blockSignals(True) self.spin_frequency.setValue(conf.measurement_frequency) self.spin_frequency.setDisabled(conf.measurement_frequency == 0) self.spin_frequency.blockSignals(False) self.threshold.blockSignals(True) self.threshold.setChecked(conf.threshold_value == 0) self.threshold.blockSignals(False) self.frequency.blockSignals(True) self.frequency.setChecked(conf.measurement_frequency == 0) self.frequency.blockSignals(False) self.configuration_changed() def get_sensor_hardware_version_async(self, value): if value == 1: self.mode_combo.show() self.mode_label.show() self.label_acquisition_count.hide() self.spin_acquisition_count.hide() self.enable_qick_termination.hide() self.label_threshold.hide() self.spin_threshold.hide() self.threshold.hide() self.label_frequency.hide() self.spin_frequency.hide() self.frequency.hide() else: self.mode_combo.hide() self.mode_label.hide() self.label_acquisition_count.show() self.spin_acquisition_count.show() self.enable_qick_termination.show() self.label_threshold.show() self.spin_threshold.show() self.threshold.show() self.label_frequency.show() self.spin_frequency.show() self.frequency.show() for w in self.widgets_distance: w.show() for w in self.widgets_velocity: w.show() def get_mode_async(self, value): self.mode_combo.setCurrentIndex(value) self.mode_changed(value) def get_moving_average_async(self, avg): self.spin_average_distance.setValue(avg.distance_average_length) self.spin_average_velocity.setValue(avg.velocity_average_length) def spin_average_finished(self): self.lrf.set_moving_average(self.spin_average_distance.value(), self.spin_average_velocity.value())
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()