def add_supplier_list_to_combo(self, combo: QComboBox): # clear QComboBox combo.clear() combo.clearEditText() if self.parent.sheets is None: combo.setEnabled(False) return if self.db is None: self.flag_db = False combo.setEnabled(False) return # DB Query and update QConboBox sql = "SELECT name_supplier_short FROM supplier;" out = self.db.get(sql) for supplier in out: combo.addItem(supplier[0]) name = self.get_supplier_name() index = combo.findText(name) if index >= 0: combo.setCurrentIndex(index) combo.setEnabled(False) else: combo.setEnabled(True)
def generateDataTypesField(dataType=None): """ @rtype: QComboBox """ dataTypes = QComboBox() dataTypes.addItems( ['TINYINT', 'SMALLINT', 'MEDIUMINT', 'INT', 'BIGINT', 'BIT']) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems(['FLOAT', 'DOUBLE', 'DECIMAL']) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems( ['CHAR', 'VARCHAR', 'TINYTEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT']) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems([ 'BINARY', 'VARBINARY', 'TINYBLOB', 'BLOB', 'MEDIUMBLOB', 'LONGBLOB' ]) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems(['DATE', 'TIME', 'YEAR', 'DATETIME', 'TIMESTAMP']) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems([ 'POINT', 'LINESTRING', 'POLYGON', 'GEOMETRY', 'MULTIPOINT', 'MULTILINESTRING', 'MULTIPOLYGON', 'GEOMETRYCOLLECTION' ]) dataTypes.insertSeparator(dataTypes.count()) dataTypes.addItems(['ENUM', 'SET']) if dataType is not None: dataTypes.setCurrentIndex(dataTypes.findText(dataType.upper())) return dataTypes
def comboBoxSelectItemByText(combobox: QtWidgets.QComboBox, value, block=False): index = combobox.findText(value) if index >= 0: if block: block_state = combobox.blockSignals(True) combobox.setCurrentIndex(index) if block: combobox.blockSignals(block_state)
class DatatypeSelector(QGroupBox): def __init__(self, title: str, datatype_to_widget, parent: "QWidget" = None): super().__init__(title, parent) # Maps a datatype with its respective widget. The widget is optional self._datatype_to_widget = datatype_to_widget self._datatype_combobox = QComboBox() self._stacked_widgets = QStackedWidget() for (i, (name, datatype_factory)) in enumerate( DataTypeContainer.providers.items()): datatype_instance = datatype_factory() self._datatype_combobox.addItem(name, datatype_instance) if datatype_factory in self._datatype_to_widget: self._stacked_widgets.insertWidget( i, self._datatype_to_widget[datatype_factory]( datatype_instance)) self._main_layout = QVBoxLayout() self._main_layout.addWidget(self._datatype_combobox) self._main_layout.addWidget(self._stacked_widgets) self.setLayout(self._main_layout) self._datatype_combobox.currentIndexChanged[int].connect( self._change_active_widget) @property def selected_datatype(self): return self._datatype_combobox.currentData() def change_current_datatype(self, new_datatype_dict: dict): index = self._datatype_combobox.findText(new_datatype_dict["class"]) if index != -1: self._datatype_combobox.setCurrentIndex(index) self._datatype_combobox.currentData().from_dict(new_datatype_dict) self._stacked_widgets.currentWidget().reload() def _change_active_widget(self, index): self._stacked_widgets.setCurrentIndex(index) # Hide the `stacked_widgets` when the current datatype doesn't needs to display # a widget if self._stacked_widgets.currentIndex() != index: self._stacked_widgets.setVisible(False) else: self._stacked_widgets.setVisible(True) def to_dict(self): return self.selected_datatype.to_dict()
def initUI(self): mainLayout = QGridLayout(self) mainLayout.setAlignment(Qt.AlignLeft | Qt.AlignTop) # mainLayout.setColumnStretch(0,0) mainLayout.setColumnStretch(1,1) mainLayout.setColumnStretch(2,1) # mainLayout.setColumnStretch(3,1) self.setLayout(mainLayout) line = 0 title = QLabel('Change Current Namespace to:') title.setAlignment(Qt.AlignLeft) mainLayout.addWidget(title, line, 0, 1, 3) line += 1 for each in self.namespace: dyName = QLabel(each) dyCombo = QComboBox() dyCombo.setLineEdit(QLineEdit()) for valName in self.validNamespace: dyCombo.addItem(valName,self.validNamespace.index(valName)) dyCombo.setCurrentIndex(dyCombo.findText(each,Qt.MatchExactly)) mainLayout.addWidget(dyName, line, 0, 1, 1) mainLayout.addWidget(dyCombo, line, 1, 1, 2) self.model[each] = dyCombo line += 1 line += 1 okBtn = QPushButton("OK") okBtn.clicked.connect(self.accept) cancelBtn = QPushButton("Cancel") cancelBtn.clicked.connect(self.reject) mainLayout.addWidget(okBtn, line, 1, 1, 1) mainLayout.addWidget(cancelBtn, line, 2, 1, 1) self.setWindowTitle("Choose Namespace") self.setWindowFlags(Qt.WindowStaysOnTopHint)
class AdvancedTab(QWidget): def __init__(self, parent_dialog, parent): """Initialize the advanced tab """ super(AdvancedTab, self).__init__(parent) self.prefs = parent_dialog.prefs self.parent = parent # Log Details log_label = QLabel("Log Detail Level:") self.log_edit = QComboBox(self) self.log_edit.addItems(['INFO', 'DEBUG', 'WARNING']) self.log_edit.setCurrentIndex(self.log_edit.findText(self.prefs['log_level'])) self.log_edit.currentIndexChanged.connect(self.log_level) self.log_edit.setMaximumWidth(100) # Clear IDD cache self.clear_idd_button = QPushButton("Clear IDD Cache") self.clear_idd_button.setMaximumWidth(200) self.clear_idd_button.clicked.connect(self.clear_idd_cache) clear_text = QLabel("This will delete the pre-processed IDD files and force " "IDF+ to reprocess them the next time they are required. " "This should happen automatically when necessary, but if " "there are problems after updating to a new version of " "IDF+, it can sometimes help to force it here.") clear_text.setWordWrap(True) clear_text.setMaximumWidth(450) clear_text.setMinimumHeight(40) # Clear IDD cache group box code self.clear_idd_group_box = QGroupBox("Clear Pre-processed IDD Cache:") clear_group_box = QVBoxLayout() clear_group_box.addWidget(clear_text) clear_group_box.addSpacing(10) clear_group_box.addWidget(self.clear_idd_button) clear_group_box.addStretch(1) self.clear_idd_group_box.setLayout(clear_group_box) # Open dirs code self.open_settings_button = QPushButton("Open Settings Directory") self.open_settings_button.setMaximumWidth(175) self.open_settings_button.clicked.connect(lambda: self.parent.show_in_folder(config.CONFIG_FILE_PATH)) self.open_log_button = QPushButton("Open Log Directory") self.open_log_button.setMaximumWidth(175) self.open_log_button.clicked.connect(lambda: self.parent.show_in_folder(config.LOG_DIR)) self.open_data_button = QPushButton("Open Data Directory") self.open_data_button.setMaximumWidth(175) self.open_data_button.clicked.connect(lambda: self.parent.show_in_folder(config.DATA_DIR)) # Default IDF file version code idd_label = QLabel("Default IDF File Version:") idd_label.setToolTip('Default version to use if none is detected.') self.idd_edit = QComboBox(self) self.idd_edit.addItems(config.idd_versions()) self.idd_edit.setMaximumWidth(100) self.idd_edit.setCurrentIndex(self.idd_edit.findText(self.prefs['default_idd_version'])) self.idd_edit.currentIndexChanged.connect(self.update_idd_version) # Main layout code main_layout = QVBoxLayout() main_layout.addWidget(idd_label) main_layout.addWidget(self.idd_edit) main_layout.addSpacing(10) main_layout.addWidget(log_label) main_layout.addWidget(self.log_edit) main_layout.addSpacing(10) main_layout.addWidget(self.clear_idd_group_box) main_layout.addStretch(1) main_layout.addWidget(self.open_settings_button) main_layout.addWidget(self.open_log_button) main_layout.addWidget(self.open_data_button) self.setLayout(main_layout) def log_level(self): self.prefs['log_level'] = self.log_edit.currentText() def clear_idd_cache(self): self.prefs['clear_idd_cache'] = True def update_idd_version(self): self.prefs['default_idd_version'] = self.idd_edit.currentText()
class Parameters(QWidget): onChangeBGColor = Signal(QColor) onChangeFontColor = Signal(QColor) onChangeFontSize = Signal(int) onChangeText = Signal(str) onChangeShape = Signal(str) def __init__(self, parent=None): super(Parameters, self).__init__(parent) self._parent = parent # self.setMinimumHeight(1) self.setFixedHeight(35) self.setMinimumWidth(1) # ------------- Main Layout -------------- main_layout = QHBoxLayout(self) main_layout.setContentsMargins(5, 5, 5, 5) main_layout.setSpacing(5) main_layout.setAlignment(Qt.AlignTop) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) # ------- Row 1 ------- # ---------------------------------------- allLayout = QHBoxLayout() allLayout.setContentsMargins(0, 0, 0, 0) allLayout.setSpacing(3) allLayout.setAlignment(Qt.AlignTop | Qt.AlignLeft) # Color colorLabel = QLabel(u"Color:") colorLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.color_btn = QPushColorButton(columns=0, rows=6, palette=COLOR_PALETTE) self.color_btn.colorSelected.connect(self.change_bg_color) # Size sizeLabel = QLabel(u"Size:") sizeLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.size_combo = QComboBox() for i in range(1, 100): self.size_combo.addItem(str(i), i) self.size_combo.activated.connect(self.change_font_size) # Shape shapeLabel = QLabel(u"Shape:") shapeLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.shape_combo = QComboBox() self.shape_combo.addItem(QIconSVG('square'), PickShape.SQUARE, PickShape.SQUARE) self.shape_combo.addItem(QIconSVG('circle'), PickShape.CIRCLE, PickShape.CIRCLE) self.shape_combo.addItem(QIconSVG('triangle'), PickShape.TRIANGLE, PickShape.TRIANGLE) self.shape_combo.addItem(QIconSVG('plus'), PickShape.PLUS, PickShape.PLUS) self.shape_combo.activated.connect(self.change_shape) # Name nameLabel = QLabel(u"Label:") nameLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.nameIn = QLineEdit() self.nameIn.textEdited.connect(self.change_name) self.textColor_btn = QPushColorButton(columns=1, rows=0, palette=BW_PALETTE) self.textColor_btn.colorSelected.connect(self.change_font_color) # Add widget to Layout allLayout.addWidget(colorLabel) allLayout.addWidget(self.color_btn) allLayout.addWidget(sizeLabel) allLayout.addWidget(self.size_combo) allLayout.addWidget(shapeLabel) allLayout.addWidget(self.shape_combo) allLayout.addWidget(nameLabel) allLayout.addWidget(self.nameIn) allLayout.addWidget(self.textColor_btn) main_layout.addLayout(allLayout) def change_name(self): ''' Sending Signal from "onChangeText" to change the text ''' self.onChangeText.emit(self.nameIn.text()) def change_bg_color(self, color=QColor): ''' Sending Signal from "onChangeBGColor" to change the background color. Parameters ---------- color: (QColor) QColor value to send over. ''' self.onChangeBGColor.emit(color) def change_font_color(self, color=QColor): ''' Sending Signal from "onChangeFontColor" to change the font color. Parameters ---------- color: (QColor) QColor value to send over. ''' self.onChangeFontColor.emit(color) def change_font_size(self, value): ''' Sending Signal from "onChangeFontSize" to change the font size. ''' self.onChangeFontSize.emit(value + 1) def change_shape(self): ''' Sending Signal from "onChangeShape" to change the shape of node. ''' self.onChangeShape.emit(self.shape_combo.currentText()) def update_param(self, text=str, fontSize=int, fontColor=QColor, bgColor=QColor, shapeName=str()): ''' Update the UI parameters. Parameters ---------- text: (str) Name of the node. fontSize: (int) Size of the font. fontColor: (QColor) Color of the font. bgColor: (QColor) Color of the background. ''' self.color_btn.CurrentColor = bgColor self.textColor_btn.CurrentColor = fontColor self.size_combo.setCurrentIndex(self.size_combo.findText( str(fontSize))) self.nameIn.setText(text) if shapeName: self.shape_combo.setCurrentIndex( self.shape_combo.findText(shapeName)) self.shape_combo.setEnabled(True) else: self.shape_combo.setEnabled(False) self.shape_combo.setCurrentIndex(-1) def get_name(self): return self.nameIn.text() def set_name(self, value=str()): self.nameIn.setText(value) Name = property(get_name, set_name)
class TallyDock(PlotterDock): def __init__(self, model, font_metric, parent=None): super().__init__(model, font_metric, parent) self.setAllowedAreas(QtCore.Qt.RightDockWidgetArea) # Dock maps for tally information self.tally_map = {} self.filter_map = {} self.score_map = {} self.nuclide_map = {} # Tally selector self.tallySelectorLayout = QFormLayout() self.tallySelector = QComboBox(self) self.tallySelector.currentTextChanged[str].connect( self.main_window.editSelectedTally) self.tallySelectorLayout.addRow(self.tallySelector) self.tallySelectorLayout.setLabelAlignment(QtCore.Qt.AlignLeft) self.tallySelectorLayout.setFieldGrowthPolicy( QFormLayout.AllNonFixedFieldsGrow) # Add selector to its own box self.tallyGroupBox = QGroupBox('Selected Tally') self.tallyGroupBox.setLayout(self.tallySelectorLayout) # Create submit button self.applyButton = QPushButton("Apply Changes") self.applyButton.setMinimumHeight(self.font_metric.height() * 1.6) self.applyButton.clicked.connect(self.main_window.applyChanges) # Color options section self.tallyColorForm = ColorForm(self.model, self.main_window, 'tally') self.scoresGroupBox = Expander(title="Scores:") self.scoresListWidget = QListWidget() self.nuclidesListWidget = QListWidget() # Main layout self.dockLayout = QVBoxLayout() self.dockLayout.addWidget(QLabel("Tallies")) self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.tallyGroupBox) self.dockLayout.addStretch() self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.tallyColorForm) self.dockLayout.addWidget(HorizontalLine()) self.dockLayout.addWidget(self.applyButton) # Create widget for dock and apply main layout self.scroll = QScrollArea() self.scroll.setWidgetResizable(True) self.widget = QWidget() self.widget.setLayout(self.dockLayout) self.scroll.setWidget(self.widget) self.setWidget(self.scroll) def _createFilterTree(self, spatial_filters): av = self.model.activeView tally = self.model.statepoint.tallies[av.selectedTally] filters = tally.filters # create a tree for the filters self.treeLayout = QVBoxLayout() self.filterTree = QTreeWidget() self.treeLayout.addWidget(self.filterTree) self.treeExpander = Expander("Filters:", layout=self.treeLayout) self.treeExpander.expand() # start with filters expanded header = QTreeWidgetItem(["Filters"]) self.filterTree.setHeaderItem(header) self.filterTree.setItemHidden(header, True) self.filterTree.setColumnCount(1) self.filterTree.itemChanged.connect(self.updateFilters) self.filter_map = {} self.bin_map = {} for tally_filter in filters: filter_label = str(type(tally_filter)).split(".")[-1][:-2] filter_item = QTreeWidgetItem(self.filterTree, (filter_label,)) self.filter_map[tally_filter] = filter_item # make checkable if not spatial_filters: filter_item.setFlags(QtCore.Qt.ItemIsUserCheckable) filter_item.setToolTip(0, "Only tallies with spatial filters are viewable.") else: filter_item.setFlags(filter_item.flags() | QtCore.Qt.ItemIsTristate | QtCore.Qt.ItemIsUserCheckable) filter_item.setCheckState(0, QtCore.Qt.Unchecked) # all mesh bins are selected by default and not shown in the dock if isinstance(tally_filter, openmc.MeshFilter): filter_item.setCheckState(0, QtCore.Qt.Checked) filter_item.setFlags(QtCore.Qt.ItemIsUserCheckable) filter_item.setToolTip(0, "All Mesh bins are selected automatically") continue def _bin_sort_val(bin): if isinstance(bin, Iterable) and all([isinstance(val, float) for val in bin]): return np.sum(bin) else: return bin for bin in sorted(tally_filter.bins, key=_bin_sort_val): item = QTreeWidgetItem(filter_item, [str(bin),]) if not spatial_filters: item.setFlags(QtCore.Qt.ItemIsUserCheckable) item.setToolTip(0, "Only tallies with spatial filters are viewable.") else: item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable) item.setCheckState(0, QtCore.Qt.Unchecked) bin = bin if not isinstance(bin, Iterable) else tuple(bin) self.bin_map[tally_filter, bin] = item # start with all filters selected if spatial filters are present if spatial_filters: filter_item.setCheckState(0, QtCore.Qt.Checked) def selectFromModel(self): cv = self.model.currentView self.selectedTally(cv.selectedTally) def selectTally(self, tally_label=None): # using active view to populate tally options live av = self.model.activeView # reset form layout for i in reversed(range(self.tallySelectorLayout.count())): self.tallySelectorLayout.itemAt(i).widget().setParent(None) # always re-add the tally selector to the layout self.tallySelectorLayout.addRow(self.tallySelector) self.tallySelectorLayout.addRow(HorizontalLine()) if tally_label is None or tally_label == "None" or tally_label == "": av.selectedTally = None self.score_map = None self.nuclide_map = None self.filter_map = None av.tallyValue = "Mean" else: # get the tally tally = self.model.statepoint.tallies[av.selectedTally] # populate filters filter_types = {type(f) for f in tally.filters} spatial_filters = bool(filter_types.intersection(_SPATIAL_FILTERS)) if not spatial_filters: self.filter_description = QLabel("(No Spatial Filters)") self.tallySelectorLayout.addRow(self.filter_description) self._createFilterTree(spatial_filters) self.tallySelectorLayout.addRow(self.treeExpander) self.tallySelectorLayout.addRow(HorizontalLine()) # value selection self.tallySelectorLayout.addRow(QLabel("Value:")) self.valueBox = QComboBox(self) self.values = tuple(_TALLY_VALUES.keys()) for value in self.values: self.valueBox.addItem(value) self.tallySelectorLayout.addRow(self.valueBox) self.valueBox.currentTextChanged[str].connect( self.main_window.editTallyValue) self.updateTallyValue() if not spatial_filters: self.valueBox.setEnabled(False) self.valueBox.setToolTip("Only tallies with spatial filters are viewable.") # scores self.score_map = {} self.scoresListWidget.itemClicked.connect( self.main_window.updateScores) self.score_map.clear() self.scoresListWidget.clear() sorted_scores = sorted(tally.scores) # always put total first if present if 'total' in sorted_scores: idx = sorted_scores.index('total') sorted_scores.insert(0, sorted_scores.pop(idx)) for score in sorted_scores: ql = QListWidgetItem() ql.setText(score.capitalize()) ql.setCheckState(QtCore.Qt.Unchecked) if not spatial_filters: ql.setFlags(QtCore.Qt.ItemIsUserCheckable) else: ql.setFlags(ql.flags() | QtCore.Qt.ItemIsUserCheckable) ql.setFlags(ql.flags() & ~QtCore.Qt.ItemIsSelectable) self.score_map[score] = ql self.scoresListWidget.addItem(ql) # select the first score item by default for item in self.score_map.values(): item.setCheckState(QtCore.Qt.Checked) break self.updateScores() self.scoresGroupBoxLayout = QVBoxLayout() self.scoresGroupBoxLayout.addWidget(self.scoresListWidget) self.scoresGroupBox = Expander("Scores:", layout=self.scoresGroupBoxLayout) self.tallySelectorLayout.addRow(self.scoresGroupBox) # nuclides self.nuclide_map = {} self.nuclidesListWidget.itemClicked.connect(self.main_window.updateNuclides) self.nuclide_map.clear() self.nuclidesListWidget.clear() sorted_nuclides = sorted(tally.nuclides) # always put total at the top if 'total' in sorted_nuclides: idx = sorted_nuclides.index('total') sorted_nuclides.insert(0, sorted_nuclides.pop(idx)) for nuclide in sorted_nuclides: ql = QListWidgetItem() ql.setText(nuclide.capitalize()) ql.setCheckState(QtCore.Qt.Unchecked) if not spatial_filters: ql.setFlags(QtCore.Qt.ItemIsUserCheckable) else: ql.setFlags(ql.flags() | QtCore.Qt.ItemIsUserCheckable) ql.setFlags(ql.flags() & ~QtCore.Qt.ItemIsSelectable) self.nuclide_map[nuclide] = ql self.nuclidesListWidget.addItem(ql) # select the first nuclide item by default for item in self.nuclide_map.values(): item.setCheckState(QtCore.Qt.Checked) break self.updateNuclides() self.nuclidesGroupBoxLayout = QVBoxLayout() self.nuclidesGroupBoxLayout.addWidget(self.nuclidesListWidget) self.nuclidesGroupBox = Expander("Nuclides:", layout=self.nuclidesGroupBoxLayout) self.tallySelectorLayout.addRow(self.nuclidesGroupBox) def updateMinMax(self): self.tallyColorForm.updateMinMax() def updateTallyValue(self): cv = self.model.currentView idx = self.valueBox.findText(cv.tallyValue) self.valueBox.setCurrentIndex(idx) def updateSelectedTally(self): cv = self.model.currentView idx = 0 if cv.selectedTally: idx = self.tallySelector.findData(cv.selectedTally) self.tallySelector.setCurrentIndex(idx) def updateFilters(self): applied_filters = defaultdict(tuple) for f, f_item in self.filter_map.items(): if type(f) == openmc.MeshFilter: continue filter_checked = f_item.checkState(0) if filter_checked != QtCore.Qt.Unchecked: selected_bins = [] for idx, b in enumerate(f.bins): b = b if not isinstance(b, Iterable) else tuple(b) bin_checked = self.bin_map[(f, b)].checkState(0) if bin_checked == QtCore.Qt.Checked: selected_bins.append(idx) applied_filters[f] = tuple(selected_bins) self.model.appliedFilters = applied_filters def updateScores(self): applied_scores = [] for score, score_box in self.score_map.items(): if score_box.checkState() == QtCore.Qt.CheckState.Checked: applied_scores.append(score) self.model.appliedScores = tuple(applied_scores) if not applied_scores: # if no scores are selected, enable all scores again for score, score_box in self.score_map.items(): sunits = _SCORE_UNITS.get(score, _REACTION_UNITS) empty_item = QListWidgetItem() score_box.setFlags(empty_item.flags() | QtCore.Qt.ItemIsUserCheckable) score_box.setFlags(empty_item.flags() & ~QtCore.Qt.ItemIsSelectable) elif 'total' in applied_scores: self.model.appliedScores = ('total',) # if total is selected, disable all other scores for score, score_box in self.score_map.items(): if score != 'total': score_box.setFlags(QtCore.Qt.ItemIsUserCheckable) score_box.setToolTip("De-select 'total' to enable other scores") else: # get units of applied scores selected_units = _SCORE_UNITS.get(applied_scores[0], _REACTION_UNITS) # disable scores with incompatible units for score, score_box in self.score_map.items(): sunits = _SCORE_UNITS.get(score, _REACTION_UNITS) if sunits != selected_units: score_box.setFlags(QtCore.Qt.ItemIsUserCheckable) score_box.setToolTip("Score is incompatible with currently selected scores") else: score_box.setFlags(score_box.flags() | QtCore.Qt.ItemIsUserCheckable) score_box.setFlags(score_box.flags() & ~QtCore.Qt.ItemIsSelectable) def updateNuclides(self): applied_nuclides = [] for nuclide, nuclide_box in self.nuclide_map.items(): if nuclide_box.checkState() == QtCore.Qt.CheckState.Checked: applied_nuclides.append(nuclide) self.model.appliedNuclides = tuple(applied_nuclides) if 'total' in applied_nuclides: self.model.appliedNuclides = ['total',] for nuclide, nuclide_box in self.nuclide_map.items(): if nuclide != 'total': nuclide_box.setFlags(QtCore.Qt.ItemIsUserCheckable) nuclide_box.setToolTip("De-select 'total' to enable other nuclides") elif not applied_nuclides: # if no nuclides are selected, enable all nuclides again for nuclide, nuclide_box in self.nuclide_map.items(): empty_item = QListWidgetItem() nuclide_box.setFlags(empty_item.flags() | QtCore.Qt.ItemIsUserCheckable) nuclide_box.setFlags(empty_item.flags() & ~QtCore.Qt.ItemIsSelectable) def update(self): # update the color form self.tallyColorForm.update() if self.model.statepoint: self.tallySelector.clear() self.tallySelector.setEnabled(True) self.tallySelector.addItem("None") for idx, tally in enumerate(self.model.statepoint.tallies.values()): if tally.name == "": self.tallySelector.addItem('Tally {}'.format(tally.id), userData=tally.id) else: self.tallySelector.addItem('Tally {} "{}"'.format(tally.id, tally.name), userData=tally.id) self.tally_map[idx] = tally self.updateSelectedTally() self.updateMinMax() else: self.tallySelector.clear() self.tallySelector.setDisabled(True)
class ColorForm(QWidget): """ Class for handling a field with a colormap, alpha, and visibility Attributes ---------- model : PlotModel The model instance used when updating information on the form. colormapBox : QComboBox Holds the string of the matplotlib colorbar being used visibilityBox : QCheckBox Indicator for whether or not the field should be visible alphaBox : QDoubleSpinBox Holds the alpha value for the displayed field data colormapBox : QComboBox Selector for colormap dataIndicatorCheckBox : QCheckBox Inidcates whether or not the data indicator will appear on the colorbar userMinMaxBox : QCheckBox Indicates whether or not the user defined values in the min and max will be used to set the bounds of the colorbar. maxBox : ScientificDoubleSpinBox Max value of the colorbar. If the userMinMaxBox is checked, this will be the user's input. If the userMinMaxBox is not checked, this box will hold the max value of the visible data. minBox : ScientificDoubleSpinBox Min value of the colorbar. If the userMinMaxBox is checked, this will be the user's input. If the userMinMaxBox is not checked, this box will hold the max value of the visible data. scaleBox : QCheckBox Indicates whether or not the data is displayed on a log or linear scale maskZeroBox : QCheckBox Indicates whether or not values equal to zero are displayed clipDataBox : QCheckBox Indicates whether or not values outside the min/max are displayed contoursBox : QCheckBox Inidicates whether or not data is displayed as contours contourLevelsLine : QLineEdit Controls the contours of the data. If this line contains a single integer, that number of levels is used to display the data. If a comma-separated set of values is entered, those values will be used as levels in the contour plot. """ def __init__(self, model, main_window, field, colormaps=None): super().__init__() self.model = model self.main_window = main_window self.field = field self.layout = QFormLayout() # Visibility check box self.visibilityBox = QCheckBox() visible_connector = partial(main_window.toggleTallyVisibility) self.visibilityBox.stateChanged.connect(visible_connector) # Alpha value self.alphaBox = QDoubleSpinBox() self.alphaBox.setDecimals(2) self.alphaBox.setRange(0, 1) self.alphaBox.setSingleStep(0.05) alpha_connector = partial(main_window.editTallyAlpha) self.alphaBox.valueChanged.connect(alpha_connector) # Color map selector self.colormapBox = QComboBox() if colormaps is None: colormaps = sorted(m for m in mcolormaps.datad if not m.endswith("_r")) for colormap in colormaps: self.colormapBox.addItem(colormap) cmap_connector = partial(main_window.editTallyDataColormap) self.colormapBox.currentTextChanged[str].connect(cmap_connector) # Data indicator line check box self.dataIndicatorCheckBox = QCheckBox() data_indicator_connector = partial(main_window.toggleTallyDataIndicator) self.dataIndicatorCheckBox.stateChanged.connect(data_indicator_connector) # User specified min/max check box self.userMinMaxBox = QCheckBox() minmax_connector = partial(main_window.toggleTallyDataUserMinMax) self.userMinMaxBox.stateChanged.connect(minmax_connector) # Data min spin box self.minBox = ScientificDoubleSpinBox() self.minBox.setMinimum(0.0) min_connector = partial(main_window.editTallyDataMin) self.minBox.valueChanged.connect(min_connector) # Data max spin box self.maxBox = ScientificDoubleSpinBox() self.maxBox.setMinimum(0.0) max_connector = partial(main_window.editTallyDataMax) self.maxBox.valueChanged.connect(max_connector) # Linear/Log scaling check box self.scaleBox = QCheckBox() scale_connector = partial(main_window.toggleTallyLogScale) self.scaleBox.stateChanged.connect(scale_connector) # Masking of zero values check box self.maskZeroBox = QCheckBox() zero_connector = partial(main_window.toggleTallyMaskZero) self.maskZeroBox.stateChanged.connect(zero_connector) # Clip data to min/max check box self.clipDataBox = QCheckBox() clip_connector = partial(main_window.toggleTallyDataClip) self.clipDataBox.stateChanged.connect(clip_connector) # Display data as contour plot check box self.contoursBox = QCheckBox() self.contoursBox.stateChanged.connect(main_window.toggleTallyContours) self.contourLevelsLine = QLineEdit() self.contourLevelsLine.textChanged.connect( main_window.editTallyContourLevels) # Organize widgets on layout self.layout.addRow("Visible:", self.visibilityBox) self.layout.addRow("Alpha: ", self.alphaBox) self.layout.addRow("Colormap: ", self.colormapBox) self.layout.addRow("Data Indicator: ", self.dataIndicatorCheckBox) self.layout.addRow("Custom Min/Max: ", self.userMinMaxBox) self.layout.addRow("Min: ", self.minBox) self.layout.addRow("Max: ", self.maxBox) self.layout.addRow("Log Scale: ", self.scaleBox) self.layout.addRow("Clip Data: ", self.clipDataBox) self.layout.addRow("Mask Zeros: ", self.maskZeroBox) self.layout.addRow("Contours: ", self.contoursBox) self.layout.addRow("Contour Levels:", self.contourLevelsLine) self.setLayout(self.layout) def updateTallyContours(self): cv = self.model.currentView self.contoursBox.setChecked(cv.tallyContours) self.contourLevelsLine.setText(cv.tallyContourLevels) def updateDataIndicator(self): cv = self.model.currentView self.dataIndicatorCheckBox.setChecked(cv.tallyDataIndicator) def setMinMaxEnabled(self, enable): enable = bool(enable) self.minBox.setEnabled(enable) self.maxBox.setEnabled(enable) def updateMinMax(self): cv = self.model.currentView self.minBox.setValue(cv.tallyDataMin) self.maxBox.setValue(cv.tallyDataMax) self.setMinMaxEnabled(cv.tallyDataUserMinMax) def updateTallyVisibility(self): cv = self.model.currentView self.visibilityBox.setChecked(cv.tallyDataVisible) def updateMaskZeros(self): cv = self.model.currentView self.maskZeroBox.setChecked(cv.tallyMaskZeroValues) def updateDataClip(self): cv = self.model.currentView self.clipDataBox.setChecked(cv.clipTallyData) def update(self): cv = self.model.currentView # set colormap value in selector cmap = cv.tallyDataColormap idx = self.colormapBox.findText(cmap, QtCore.Qt.MatchFixedString) self.colormapBox.setCurrentIndex(idx) self.alphaBox.setValue(cv.tallyDataAlpha) self.visibilityBox.setChecked(cv.tallyDataVisible) self.userMinMaxBox.setChecked(cv.tallyDataUserMinMax) self.scaleBox.setChecked(cv.tallyDataLogScale) self.updateMinMax() self.updateMaskZeros() self.updateDataClip() self.updateDataIndicator() self.updateTallyContours()
class FontsTab(QWidget): def __init__(self, parent_dialog, parent): """Initialize the fonts tab """ super(FontsTab, self).__init__(parent_dialog) self.prefs = parent_dialog.prefs self.parent = parent # Base application font code app = QApplication.instance() self.font_base = app.font() font_label_base = QLabel("Base Application Font:") self.font_combo_base = QFontComboBox() self.font_combo_base.setMaximumWidth(175) self.font_combo_base.setCurrentFont(self.font_base.family()) self.font_combo_base.currentFontChanged.connect(self.update) self.font_size_combo_base = QComboBox() points_base = self.font_points(self.font_base) self.font_size_combo_base.addItems(points_base) self.font_size_combo_base.setMaximumWidth(50) self.font_size_combo_base.setCurrentIndex(self.font_size_combo_base.findText(str(self.font_base.pointSize()))) self.font_size_combo_base.currentIndexChanged.connect(self.update) base_layout = QHBoxLayout() base_layout.addWidget(font_label_base) base_layout.addWidget(self.font_combo_base) base_layout.addWidget(self.font_size_combo_base) # Class Tree font code self.font_classTree = self.parent.classTree.font() font_label_classTree = QLabel("Class Tree Font:") self.font_combo_classTree = QFontComboBox() self.font_combo_classTree.setMaximumWidth(175) self.font_combo_classTree.setCurrentFont(self.font_classTree.family()) self.font_combo_classTree.currentFontChanged.connect(self.update) self.font_size_combo_classTree = QComboBox() points_classTree = self.font_points(self.font_classTree) self.font_size_combo_classTree.addItems(points_classTree) self.font_size_combo_classTree.setMaximumWidth(50) self.font_size_combo_classTree.setCurrentIndex(self.font_size_combo_classTree.findText(str(self.font_classTree.pointSize()))) self.font_size_combo_classTree.currentIndexChanged.connect(self.update) classTree_layout = QHBoxLayout() classTree_layout.addWidget(font_label_classTree) classTree_layout.addWidget(self.font_combo_classTree) classTree_layout.addWidget(self.font_size_combo_classTree) # Class Table font code self.font_classTable = self.parent.classTable.font() font_label_classTable = QLabel("Class Table Font:") self.font_combo_classTable = QFontComboBox() self.font_combo_classTable.setMaximumWidth(175) self.font_combo_classTable.setCurrentFont(self.font_classTable.family()) self.font_combo_classTable.currentFontChanged.connect(self.update) self.font_size_combo_classTable = QComboBox() points_classTable = self.font_points(self.font_classTable) self.font_size_combo_classTable.addItems(points_classTable) self.font_size_combo_classTable.setMaximumWidth(50) self.font_size_combo_classTable.setCurrentIndex(self.font_size_combo_classTable.findText(str(self.font_classTable.pointSize()))) self.font_size_combo_classTable.currentIndexChanged.connect(self.update) classTable_layout = QHBoxLayout() classTable_layout.addWidget(font_label_classTable) classTable_layout.addWidget(self.font_combo_classTable) classTable_layout.addWidget(self.font_size_combo_classTable) # Comments view font code self.font_comments = self.parent.commentView.font() font_label_comments = QLabel("Comments Font:") self.font_combo_comments = QFontComboBox() self.font_combo_comments.setMaximumWidth(175) self.font_combo_comments.setCurrentFont(self.font_comments.family()) self.font_combo_comments.currentFontChanged.connect(self.update) self.font_size_combo_comments = QComboBox() points_comments = self.font_points(self.font_comments) self.font_size_combo_comments.addItems(points_comments) self.font_size_combo_comments.setMaximumWidth(50) self.font_size_combo_comments.setCurrentIndex(self.font_size_combo_comments.findText(str(self.font_comments.pointSize()))) self.font_size_combo_comments.currentIndexChanged.connect(self.update) comments_layout = QHBoxLayout() comments_layout.addWidget(font_label_comments) comments_layout.addWidget(self.font_combo_comments) comments_layout.addWidget(self.font_size_combo_comments) # Main layout code mainLayout = QVBoxLayout() mainLayout.addLayout(base_layout) mainLayout.addSpacing(10) mainLayout.addLayout(classTree_layout) mainLayout.addSpacing(10) mainLayout.addLayout(classTable_layout) mainLayout.addSpacing(10) mainLayout.addLayout(comments_layout) mainLayout.addStretch(1) self.setLayout(mainLayout) def update(self): self.font_base.setFamily(self.font_combo_base.currentFont().family()) self.font_base.setPointSize(int(self.font_size_combo_base.currentText())) self.font_classTree.setFamily(self.font_combo_classTree.currentFont().family()) self.font_classTree.setPointSize(int(self.font_size_combo_classTree.currentText())) self.font_classTable.setFamily(self.font_combo_classTable.currentFont().family()) self.font_classTable.setPointSize(int(self.font_size_combo_classTable.currentText())) self.font_comments.setFamily(self.font_combo_comments.currentFont().family()) self.font_comments.setPointSize(int(self.font_size_combo_comments.currentText())) self.prefs['base_font'] = self.font_base.toString() self.prefs['class_tree_font'] = self.font_classTree.toString() self.prefs['class_table_font'] = self.font_classTable.toString() self.prefs['comments_font'] = self.font_comments.toString() # self.prefs['log_font'] = self.font_base.toString() # self.prefs['ref_font'] = self.font_base.toString() # self.prefs['info_font'] = self.font_base.toString() # self.prefs['undo_font'] = self.font_base.toString() def font_points(self, font): points = [str(p) for p in QFontDatabase().pointSizes(font.family())] if not points: points = [str(p) for p in QFontDatabase().standardSizes()] return points
class TabDisplays(QTabWidget): def __init__(self, parent=None): super(TabDisplays, self).__init__(parent) # Initialize logging logging_conf_file = os.path.join(os.path.dirname(__file__), 'cfg/aecgviewer_aecg_logging.conf') logging.config.fileConfig(logging_conf_file) self.logger = logging.getLogger(__name__) self.studyindex_info = aecg.tools.indexer.StudyInfo() self.validator = QWidget() self.studyinfo = QWidget() self.statistics = QWidget() self.waveforms = QWidget() self.waveforms.setAccessibleName("Waveforms") self.scatterplot = QWidget() self.histogram = QWidget() self.trends = QWidget() self.xmlviewer = QWidget() self.xml_display = QTextEdit(self.xmlviewer) self.options = QWidget() self.aecg_display_area = QScrollArea() self.cbECGLayout = QComboBox() self.aecg_display = EcgDisplayWidget(self.aecg_display_area) self.aecg_display_area.setWidget(self.aecg_display) self.addTab(self.validator, "Study information") self.addTab(self.waveforms, "Waveforms") self.addTab(self.xmlviewer, "XML") self.addTab(self.options, "Options") self.setup_validator() self.setup_waveforms() self.setup_xmlviewer() self.setup_options() size = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) size.setHeightForWidth(False) self.setSizePolicy(size) # Initialized a threpool with 2 threads 1 for the GUI, 1 for long # tasks, so GUI remains responsive self.threadpool = QThreadPool() self.threadpool.setMaxThreadCount(2) self.validator_worker = None self.indexing_timer = QElapsedTimer() def setup_validator(self): self.directory_indexer = None # aecg.indexing.DirectoryIndexer() self.validator_layout_container = QWidget() self.validator_layout = QFormLayout() self.validator_form_layout = QFormLayout( self.validator_layout_container) self.validator_grid_layout = QGridLayout() self.study_info_file = QLineEdit() self.study_info_file.setToolTip("Study index file") self.study_info_description = QLineEdit() self.study_info_description.setToolTip("Description") self.app_type = QLineEdit() self.app_type.setToolTip("Application type (e.g., NDA, IND, BLA, IDE)") self.app_num = QLineEdit() self.app_num.setToolTip("Six-digit application number") self.app_num.setValidator(QIntValidator(self.app_num)) self.study_id = QLineEdit() self.study_id.setToolTip("Study identifier") self.study_sponsor = QLineEdit() self.study_sponsor.setToolTip("Sponsor of the study") self.study_annotation_aecg_cb = QComboBox() self.study_annotation_aecg_cb.addItems( ["Rhythm", "Derived beat", "Holter-rhythm", "Holter-derived"]) self.study_annotation_aecg_cb.setToolTip( "Waveforms used to perform the ECG measurements (i.e., " "annotations)\n" "\tRhythm: annotations in a rhythm strip or discrete ECG " "extraction (e.g., 10-s strips)\n" "\tDerived beat: annotations in a representative beat derived " "from a rhythm strip\n" "\tHolter-rhythm: annotations in a the analysis window of a " "continuous recording\n" "\tHolter-derived: annotations in a representative beat derived " "from analysis window of a continuous recording\n") self.study_annotation_lead_cb = QComboBox() self.ui_leads = ["GLOBAL"] + aecg.STD_LEADS[0:12] +\ [aecg.KNOWN_NON_STD_LEADS[1]] + aecg.STD_LEADS[12:15] + ["Other"] self.study_annotation_lead_cb.addItems(self.ui_leads) self.study_annotation_lead_cb.setToolTip( "Primary analysis lead annotated per protocol. There could be " "annotations in other leads also, but only the primary lead should" " be selected here.\n" "Select global if all leads were used at the " "same time (e.g., superimposed on screen).\n" "Select other if the primary lead used is not in the list.") self.study_numsubjects = QLineEdit() self.study_numsubjects.setToolTip( "Number of subjects with ECGs in the study") self.study_numsubjects.setValidator( QIntValidator(self.study_numsubjects)) self.study_aecgpersubject = QLineEdit() self.study_aecgpersubject.setToolTip( "Number of scheduled ECGs (or analysis windows) per subject as " "specified in the study protocol.\n" "Enter average number of ECGs " "per subject if the protocol does not specify a fixed number of " "ECGs per subject.") self.study_aecgpersubject.setValidator( QIntValidator(self.study_aecgpersubject)) self.study_numaecg = QLineEdit() self.study_numaecg.setToolTip( "Total number of aECG XML files in the study") self.study_numaecg.setValidator(QIntValidator(self.study_numaecg)) self.study_annotation_numbeats = QLineEdit() self.study_annotation_numbeats.setToolTip( "Minimum number of beats annotated in each ECG or analysis window" ".\nEnter 1 if annotations were done in the derived beat.") self.study_annotation_numbeats.setValidator( QIntValidator(self.study_annotation_numbeats)) self.aecg_numsubjects = QLineEdit() self.aecg_numsubjects.setToolTip( "Number of subjects found across the provided aECG XML files") self.aecg_numsubjects.setReadOnly(True) self.aecg_aecgpersubject = QLineEdit() self.aecg_aecgpersubject.setToolTip( "Average number of ECGs per subject found across the provided " "aECG XML files") self.aecg_aecgpersubject.setReadOnly(True) self.aecg_numaecg = QLineEdit() self.aecg_numaecg.setToolTip( "Number of aECG XML files found in the study aECG directory") self.aecg_numaecg.setReadOnly(True) self.subjects_less_aecgs = QLineEdit() self.subjects_less_aecgs.setToolTip( "Percentage of subjects with less aECGs than specified per " "protocol") self.subjects_less_aecgs.setReadOnly(True) self.subjects_more_aecgs = QLineEdit() self.subjects_more_aecgs.setToolTip( "Percentage of subjects with more aECGs than specified per " "protocol") self.subjects_more_aecgs.setReadOnly(True) self.aecgs_no_annotations = QLineEdit() self.aecgs_no_annotations.setToolTip( "Percentage of aECGs with no annotations") self.aecgs_no_annotations.setReadOnly(True) self.aecgs_less_qt_in_primary_lead = QLineEdit() self.aecgs_less_qt_in_primary_lead.setToolTip( "Percentage of aECGs with less QT intervals in the primary lead " "than specified per protocol") self.aecgs_less_qt_in_primary_lead.setReadOnly(True) self.aecgs_less_qts = QLineEdit() self.aecgs_less_qts.setToolTip( "Percentage of aECGs with less QT intervals than specified per " "protocol") self.aecgs_less_qts.setReadOnly(True) self.aecgs_annotations_multiple_leads = QLineEdit() self.aecgs_annotations_multiple_leads.setToolTip( "Percentage of aECGs with QT annotations in multiple leads") self.aecgs_annotations_multiple_leads.setReadOnly(True) self.aecgs_annotations_no_primary_lead = QLineEdit() self.aecgs_annotations_no_primary_lead.setToolTip( "Percentage of aECGs with QT annotations not in the primary lead") self.aecgs_annotations_no_primary_lead.setReadOnly(True) self.aecgs_with_errors = QLineEdit() self.aecgs_with_errors.setToolTip("Number of aECG files with errors") self.aecgs_with_errors.setReadOnly(True) self.aecgs_potentially_digitized = QLineEdit() self.aecgs_potentially_digitized.setToolTip( "Number of aECG files potentially digitized (i.e., with more than " "5% of samples missing)") self.aecgs_potentially_digitized.setReadOnly(True) self.study_dir = QLineEdit() self.study_dir.setToolTip("Directory containing the aECG files") self.study_dir_button = QPushButton("...") self.study_dir_button.clicked.connect(self.select_study_dir) self.study_dir_button.setToolTip("Open select directory dialog") self.validator_form_layout.addRow("Application Type", self.app_type) self.validator_form_layout.addRow("Application Number", self.app_num) self.validator_form_layout.addRow("Study name/ID", self.study_id) self.validator_form_layout.addRow("Sponsor", self.study_sponsor) self.validator_form_layout.addRow("Study description", self.study_info_description) self.validator_form_layout.addRow("Annotations in", self.study_annotation_aecg_cb) self.validator_form_layout.addRow("Annotations primary lead", self.study_annotation_lead_cb) self.validator_grid_layout.addWidget(QLabel(""), 0, 0) self.validator_grid_layout.addWidget( QLabel("Per study protocol or report"), 0, 1) self.validator_grid_layout.addWidget(QLabel("Found in aECG files"), 0, 2) self.validator_grid_layout.addWidget(QLabel("Number of subjects"), 1, 0) self.validator_grid_layout.addWidget(self.study_numsubjects, 1, 1) self.validator_grid_layout.addWidget(self.aecg_numsubjects, 1, 2) self.validator_grid_layout.addWidget( QLabel("Number of aECG per subject"), 2, 0) self.validator_grid_layout.addWidget(self.study_aecgpersubject, 2, 1) self.validator_grid_layout.addWidget(self.aecg_aecgpersubject, 2, 2) self.validator_grid_layout.addWidget(QLabel("Total number of aECG"), 3, 0) self.validator_grid_layout.addWidget(self.study_numaecg, 3, 1) self.validator_grid_layout.addWidget(self.aecg_numaecg, 3, 2) self.validator_grid_layout.addWidget( QLabel("Number of beats per aECG"), 4, 0) self.validator_grid_layout.addWidget(self.study_annotation_numbeats, 4, 1) self.validator_grid_layout.addWidget( QLabel("Subjects with fewer ECGs"), 5, 1) self.validator_grid_layout.addWidget(self.subjects_less_aecgs, 5, 2) self.validator_grid_layout.addWidget(QLabel("Subjects with more ECGs"), 6, 1) self.validator_grid_layout.addWidget(self.subjects_more_aecgs, 6, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without annotations"), 7, 1) self.validator_grid_layout.addWidget(self.aecgs_no_annotations, 7, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs in primary lead"), 8, 1) self.validator_grid_layout.addWidget( self.aecgs_less_qt_in_primary_lead, 8, 2) self.validator_grid_layout.addWidget( QLabel("aECGs without expected number of QTs"), 9, 1) self.validator_grid_layout.addWidget(self.aecgs_less_qts, 9, 2) self.validator_grid_layout.addWidget( QLabel("aECGs annotated in multiple leads"), 10, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_multiple_leads, 10, 2) self.validator_grid_layout.addWidget( QLabel("aECGs with annotations not in primary lead"), 11, 1) self.validator_grid_layout.addWidget( self.aecgs_annotations_no_primary_lead, 11, 2) self.validator_grid_layout.addWidget(QLabel("aECGs with errors"), 12, 1) self.validator_grid_layout.addWidget(self.aecgs_with_errors, 12, 2) self.validator_grid_layout.addWidget( QLabel("Potentially digitized aECGs"), 13, 1) self.validator_grid_layout.addWidget(self.aecgs_potentially_digitized, 13, 2) self.validator_form_layout.addRow(self.validator_grid_layout) tmp = QHBoxLayout() tmp.addWidget(self.study_dir) tmp.addWidget(self.study_dir_button) self.validator_form_layout.addRow("Study aECGs directory", tmp) self.validator_form_layout.addRow("Study index file", self.study_info_file) self.validator_layout.addWidget(self.validator_layout_container) self.validator_effective_dirs = QLabel("") self.validator_effective_dirs.setWordWrap(True) self.validator_layout.addWidget(self.validator_effective_dirs) self.val_button = QPushButton("Generate/update study index") self.val_button.clicked.connect(self.importstudy_dialog) self.validator_layout.addWidget(self.val_button) self.cancel_val_button = QPushButton("Cancel study index generation") self.cancel_val_button.clicked.connect(self.cancel_validator) self.cancel_val_button.setEnabled(False) self.validator_layout.addWidget(self.cancel_val_button) self.validator_pl = QLabel("") self.validator_layout.addWidget(self.validator_pl) self.validator_pb = QProgressBar() self.validator_layout.addWidget(self.validator_pb) self.validator.setLayout(self.validator_layout) self.stop_indexing = False self.lastindexing_starttime = None self.update_validator_effective_dirs() def effective_aecgs_dir(self, navwidget, silent=False): aecgs_effective_dir = self.study_dir.text() if navwidget.project_loaded != '': # Path specified in the GUI potential_aecgs_dirs = [self.study_dir.text()] # StudyDir path from current working directory potential_aecgs_dirs += [self.studyindex_info.StudyDir] # StudyDir path from directory where the index is located potential_aecgs_dirs += [ os.path.join(os.path.dirname(navwidget.project_loaded), self.studyindex_info.StudyDir) ] # StudyDir replaced with the directory where the index is located potential_aecgs_dirs += [os.path.dirname(navwidget.project_loaded)] dir_found = False # Get xml and zip filenames from first element in the index aecg_xml_file = navwidget.data_index["AECGXML"][0] zipfile = "" if aecg_xml_file != "": zipfile = navwidget.data_index["ZIPFILE"][0] for p in potential_aecgs_dirs: testfn = os.path.join(p, aecg_xml_file) if zipfile != "": testfn = os.path.join(p, zipfile) if os.path.isfile(testfn): dir_found = True aecgs_effective_dir = p break if not silent: if not dir_found: QMessageBox.warning( self, f"Study aECGs directory not found", f"The following paths were checked:" f"{[','.join(p) for p in potential_aecgs_dirs]} and " f"none of them is valid") elif p != self.study_dir.text(): QMessageBox.warning( self, f"Study aECGs directory not found", f"The path specified in the study aECGs directory is " f"not valid and {p} is being used instead. Check and " f"update the path in Study aECGs directory textbox if " f"the suggested path is not the adequate path") return aecgs_effective_dir def update_validator_effective_dirs(self): msg = f"Working directory: {os.getcwd()}" if self.parent() is not None: if isinstance(self.parent(), QSplitter): navwidget = self.parent().parent() else: # Tabs widget has not been allocated the QSplitter yet navwidget = self.parent() project_loaded = navwidget.project_loaded if project_loaded != '': msg = f"{msg}\nLoaded project index: "\ f"{navwidget.project_loaded}" effective_aecgs_path = self.effective_aecgs_dir(navwidget) msg = f"{msg}\nEffective study aECGs directory: "\ f"{effective_aecgs_path}" else: msg = f"{msg}\nLoaded project index: None" else: msg = f"{msg}\nLoaded project index: None" self.validator_effective_dirs.setText(msg) def load_study_info(self, fileName): self.study_info_file.setText(fileName) try: study_info = pd.read_excel(fileName, sheet_name="Info") self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.__dict__.update( study_info.set_index("Property").transpose().reset_index( drop=True).to_dict('index')[0]) sponsor = "" description = "" if self.studyindex_info.Sponsor is not None and\ isinstance(self.studyindex_info.Sponsor, str): sponsor = self.studyindex_info.Sponsor if self.studyindex_info.Description is not None and\ isinstance(self.studyindex_info.Description, str): description = self.studyindex_info.Description self.study_sponsor.setText(sponsor) self.study_info_description.setText(description) self.app_type.setText(self.studyindex_info.AppType) self.app_num.setText(f"{int(self.studyindex_info.AppNum):06d}") self.study_id.setText(self.studyindex_info.StudyID) self.study_numsubjects.setText(str(self.studyindex_info.NumSubj)) self.study_aecgpersubject.setText( str(self.studyindex_info.NECGSubj)) self.study_numaecg.setText(str(self.studyindex_info.TotalECGs)) anns_in = self.studyindex_info.AnMethod.upper() idx = 0 if anns_in == "RHYTHM": idx = 0 elif anns_in == "DERIVED": idx = 1 elif anns_in == "HOLTER_RHYTHM": idx = 2 elif anns_in == "HOLTER_MEDIAN_BEAT": idx = 3 else: idx = int(anns_in) - 1 self.study_annotation_aecg_cb.setCurrentIndex(idx) the_lead = self.studyindex_info.AnLead idx = self.study_annotation_lead_cb.findText(str(the_lead)) if idx == -1: idx = self.study_annotation_lead_cb.findText("MDC_ECG_LEAD_" + str(the_lead)) if idx == -1: idx = int(the_lead) self.study_annotation_lead_cb.setCurrentIndex(idx) self.study_annotation_numbeats.setText( str(self.studyindex_info.AnNbeats)) if self.studyindex_info.StudyDir == "": self.studyindex_info.StudyDir = os.path.dirname(fileName) self.study_dir.setText(self.studyindex_info.StudyDir) self.update_validator_effective_dirs() self.setCurrentWidget(self.validator) except Exception as ex: QMessageBox.critical( self, "Import study error", "Error reading the study information file: '" + fileName + "'") def setup_waveforms(self): wflayout = QVBoxLayout() # ECG plot layout selection box self.cbECGLayout.addItems( ['12-lead stacked', '3x4 + lead II rhythm', 'Superimposed']) self.cbECGLayout.currentIndexChanged.connect( self.ecgplotlayout_changed) # Zoom buttons blayout = QHBoxLayout() pb_zoomin = QPushButton() pb_zoomin.setText("Zoom +") pb_zoomin.clicked.connect(self.zoom_in) pb_zoomreset = QPushButton() pb_zoomreset.setText("Zoom 1:1") pb_zoomreset.clicked.connect(self.zoom_reset) pb_zoomout = QPushButton() pb_zoomout.setText("Zoom -") pb_zoomout.clicked.connect(self.zoom_out) blayout.addWidget(self.cbECGLayout) blayout.addWidget(pb_zoomout) blayout.addWidget(pb_zoomreset) blayout.addWidget(pb_zoomin) wflayout.addLayout(blayout) # Add QScrollArea to main layout of waveforms tab self.aecg_display_area.setWidgetResizable(False) wflayout.addWidget(self.aecg_display_area) self.waveforms.setLayout(wflayout) size = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) size.setHeightForWidth(False) self.aecg_display_area.setSizePolicy(size) self.waveforms.setSizePolicy(size) def setup_xmlviewer(self): wf_layout = QHBoxLayout() wf_layout.addWidget(self.xml_display) self.xmlviewer.setLayout(wf_layout) def setup_options(self): self.options_layout = QFormLayout() self.aecg_schema_filename = QLineEdit(aecg.get_aecg_schema_location()) self.options_layout.addRow("aECG XML schema path", self.aecg_schema_filename) self.save_index_every_n_aecgs = QSpinBox() self.save_index_every_n_aecgs.setMinimum(0) self.save_index_every_n_aecgs.setMaximum(50000) self.save_index_every_n_aecgs.setValue(0) self.save_index_every_n_aecgs.setSingleStep(100) self.save_index_every_n_aecgs.setSuffix(" aECGs") self.save_index_every_n_aecgs.setToolTip( "Set o 0 to save the study index file only after its generation " "is completed.\nOtherwise, the file is saved everytime the " " specified number of ECGs have been appended to the index.") self.options_layout.addRow("Save index every ", self.save_index_every_n_aecgs) self.save_all_intervals_cb = QCheckBox("") self.save_all_intervals_cb.setChecked(False) self.options_layout.addRow("Save individual beat intervals", self.save_all_intervals_cb) self.parallel_processing_cb = QCheckBox("") self.parallel_processing_cb.setChecked(True) self.options_layout.addRow("Parallel processing of files", self.parallel_processing_cb) self.options.setLayout(self.options_layout) def zoom_in(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor + 0.1) def zoom_out(self): self.aecg_display.apply_zoom(self.aecg_display.zoom_factor - 0.1) def zoom_reset(self): self.aecg_display.apply_zoom(1.0) def ecgplotlayout_changed(self, i): self.aecg_display.update_aecg_plot( ecg_layout=aecg.utils.ECG_plot_layout(i + 1)) def update_search_progress(self, i, n): self.validator_pl.setText( f"Searching aECGs in directory ({n} XML files found)") def update_progress(self, i, n): j = i m = n if i <= 1: j = 1 if self.validator_pb.value() > 0: j = self.validator_pb.value() + 1 m = self.validator_pb.maximum() running_time = self.indexing_timer.elapsed() * 1e-3 # in seconds time_per_item = running_time / j # reamining = seconds per item so far * total pending items to process remaining_time = time_per_item * (m - j) eta = datetime.datetime.now() +\ datetime.timedelta(seconds=round(remaining_time, 0)) self.validator_pl.setText( f"Validating aECG {j}/{m} | " f"Execution time: " f"{str(datetime.timedelta(0,seconds=round(running_time)))} | " f"{round(1/time_per_item,2)} aECGs per second | " f"ETA: {eta.isoformat(timespec='seconds')}") self.validator_pb.setValue(j) if self.save_index_every_n_aecgs.value() > 0 and\ len(self.directory_indexer.studyindex) % \ self.save_index_every_n_aecgs.value() == 0: self.save_validator_results( pd.concat(self.directory_indexer.studyindex, ignore_index=True)) def save_validator_results(self, res): if res.shape[0] > 0: self.studyindex_info = aecg.tools.indexer.StudyInfo() self.studyindex_info.StudyDir = self.study_dir.text() self.studyindex_info.IndexFile = self.study_info_file.text() self.studyindex_info.Sponsor = self.study_sponsor.text() self.studyindex_info.Description =\ self.study_info_description.text() self.studyindex_info.Date = self.lastindexing_starttime.isoformat() self.studyindex_info.End_date = datetime.datetime.now().isoformat() self.studyindex_info.Version = aecg.__version__ self.studyindex_info.AppType = self.app_type.text() self.studyindex_info.AppNum = f"{int(self.app_num.text()):06d}" self.studyindex_info.StudyID = self.study_id.text() self.studyindex_info.NumSubj = int(self.study_numsubjects.text()) self.studyindex_info.NECGSubj = int( self.study_aecgpersubject.text()) self.studyindex_info.TotalECGs = int(self.study_numaecg.text()) anmethod = aecg.tools.indexer.AnnotationMethod( self.study_annotation_aecg_cb.currentIndex()) self.studyindex_info.AnMethod = anmethod.name self.studyindex_info.AnLead =\ self.study_annotation_lead_cb.currentText() self.studyindex_info.AnNbeats = int( self.study_annotation_numbeats.text()) # Calculate stats study_stats = aecg.tools.indexer.StudyStats( self.studyindex_info, res) # Save to file aecg.tools.indexer.save_study_index(self.studyindex_info, res, study_stats) validator_data_ready = Signal() def save_validator_results_and_load_index(self, res): self.save_validator_results(res) self.validator_data_ready.emit() def indexer_validator_results(self, res): self.studyindex_df = pd.concat([self.studyindex_df, res], ignore_index=True) def subindex_thread_complete(self): return def index_directory_thread_complete(self): tmp = self.validator_pl.text().replace("ETA:", "Completed: ").replace( "Validating", "Validated") self.validator_pl.setText(tmp) self.val_button.setEnabled(True) self.cancel_val_button.setEnabled(False) self.validator_layout_container.setEnabled(True) def index_directory(self, progress_callback): self.lastindexing_starttime = datetime.datetime.now() self.indexing_timer.start() studyindex_df = [] n_cores = os.cpu_count() aecg_schema = None if self.aecg_schema_filename.text() != "": aecg_schema = self.aecg_schema_filename.text() if self.parallel_processing_cb.isChecked(): studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, n_cores, progress_callback) else: studyindex_df = self.directory_indexer.index_directory( self.save_all_intervals_cb.isChecked(), aecg_schema, 1, progress_callback) return studyindex_df def importstudy_dialog(self): dirName = os.path.normpath(self.study_dir.text()) if dirName != "": if os.path.exists(dirName): self.directory_indexer = aecg.indexing.DirectoryIndexer() self.directory_indexer.set_aecg_dir( dirName, self.update_search_progress) self.validator_pb.setMaximum(self.directory_indexer.num_files) self.validator_pb.reset() self.stop_indexing = False self.validator_layout_container.setEnabled(False) self.val_button.setEnabled(False) self.cancel_val_button.setEnabled(True) self.validator_worker = Worker(self.index_directory) self.validator_worker.signals.result.connect( self.save_validator_results_and_load_index) self.validator_worker.signals.finished.connect( self.index_directory_thread_complete) self.validator_worker.signals.progress.connect( self.update_progress) # Execute self.threadpool.start(self.validator_worker) else: QMessageBox.critical( self, "Directory not found", f"Specified study directory not found:\n{dirName}") else: QMessageBox.critical(self, "Import study error", "Study directory cannot be empty") def cancel_validator(self): self.cancel_val_button.setEnabled(False) self.stop_indexing = True self.directory_indexer.cancel_indexing = True self.threadpool.waitForDone(3000) self.val_button.setEnabled(True) def select_study_dir(self): cd = self.study_dir.text() if cd == "": cd = "." dir = QFileDialog.getExistingDirectory( self, "Open Directory", cd, QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if dir != "": self.study_dir.setText(dir)
class AppearanceTab(QWidget): def __init__(self, parent): """Initialize the appearance tab """ super(AppearanceTab, self).__init__(parent) self.prefs = parent.prefs # Default column width code col_width_label = QLabel("Default Column Width:") self.col_width_edit = QLineEdit(str(self.prefs['default_column_width'])) self.col_width_edit.setMinimumWidth(40) self.col_width_edit.setMaximumWidth(100) validator = QIntValidator(10, 200, self) self.col_width_edit.setValidator(validator) # Visual style code style_label = QLabel("Visual Style of Application:") self.style_edit = QComboBox(self) self.style_edit.addItems(list(QStyleFactory.keys())) self.style_edit.setMaximumWidth(200) self.style_edit.setCurrentIndex(self.style_edit.findText(self.prefs['style'])) # Units display code units_header_label = QLabel("Units Display:") self.header_units_check = QCheckBox('Show Units in Table Headers', self) checked_header = Qt.Checked if self.prefs['show_units_in_headers'] == 1 else Qt.Unchecked self.header_units_check.setCheckState(checked_header) self.cells_units_check = QCheckBox('Show Units in Table Cells', self) checked_cells = Qt.Checked if self.prefs['show_units_in_cells'] == 1 else Qt.Unchecked self.cells_units_check.setCheckState(checked_cells) # Handling of file options code default_handling_text = QLabel("Select how options saved directly within an IDF " "file are treated. See \"Save Options\" tab " "for the options in question.") default_handling_text.setWordWrap(True) default_handling_text.setMaximumWidth(450) self.button_force = QRadioButton("Force Session Options:", self) force_text = QLabel("Options from the current session will be used for all " "files, ignoring any options saved in the IDF file.") force_text.setWordWrap(True) force_text.setMaximumWidth(450) force_text.setMinimumHeight(30) force_text.setIndent(25) self.button_obey = QRadioButton("Obey IDF Options if Present:", self) obey_text = QLabel("Obey options saved in the IDF file. If none are " "present, use the current session's options.") obey_text.setWordWrap(True) obey_text.setMaximumWidth(450) obey_text.setMinimumHeight(30) obey_text.setIndent(25) # Handling of file options group box self.behaviour_group_box = QGroupBox("Handling of File-based Options") self.behaviour_group_box.setMinimumHeight(220) self.behaviour_group_box.setMinimumWidth(450) behaviour_box = QVBoxLayout() behaviour_box.addWidget(default_handling_text) behaviour_box.addWidget(self.button_force) behaviour_box.addWidget(force_text) behaviour_box.addSpacing(5) behaviour_box.addWidget(self.button_obey) behaviour_box.addWidget(obey_text) behaviour_box.addStretch(1) self.behaviour_group_box.setLayout(behaviour_box) self.behaviour_button_group = QButtonGroup(self) self.behaviour_button_group.addButton(self.button_force) self.behaviour_button_group.addButton(self.button_obey) self.behaviour_button_group.setId(self.button_force, 0) self.behaviour_button_group.setId(self.button_obey, 1) self.behaviour_button_group.button(self.prefs['obey_idf_options']).setChecked(True) # Main layout code mainLayout = QVBoxLayout() mainLayout.addWidget(col_width_label) mainLayout.addWidget(self.col_width_edit) mainLayout.addSpacing(10) mainLayout.addWidget(style_label) mainLayout.addWidget(self.style_edit) mainLayout.addSpacing(10) mainLayout.addWidget(self.behaviour_group_box) mainLayout.addSpacing(10) mainLayout.addWidget(units_header_label) mainLayout.addWidget(self.header_units_check) mainLayout.addWidget(self.cells_units_check) mainLayout.addStretch(1) self.setLayout(mainLayout) # Update settings self.behaviour_button_group.buttonClicked.connect(self.update) self.col_width_edit.textChanged.connect(self.update) self.style_edit.currentIndexChanged.connect(self.update) self.header_units_check.stateChanged.connect(self.update) self.cells_units_check.stateChanged.connect(self.update) def update(self): self.prefs['default_column_width'] = self.col_width_edit.text() self.prefs['style'] = self.style_edit.currentText() self.prefs['obey_idf_options'] = self.behaviour_button_group.checkedId() self.prefs['show_units_in_headers'] = 1 if self.header_units_check.checkState() else 0 self.prefs['show_units_in_cells'] = 1 if self.cells_units_check.checkState() else 0
class CamView(AbstractView): def __init__(self, name: str = "CAM_NONE", log_handlers: [StreamHandler] = None): self._logger = getLogger(__name__) if log_handlers: for h in log_handlers: self._logger.addHandler(h) self._logger.debug("Initializing") super().__init__(name) """ Min size for cam window """ self._subwindow_height = 222 self._subwindow_width = 518 self._initialization_bar_frame = EasyFrame() self._initialization_bar_frame.setMouseTracking(True) self._initialization_bar_frame.setMaximumHeight(70) self._initialization_bar_layout = QVBoxLayout( self._initialization_bar_frame) self._initialization_bar_label = QLabel(self._initialization_bar_frame) self._initialization_bar_label.setMouseTracking(True) self._initialization_bar = QProgressBar(self._initialization_bar_frame) self._initialization_bar.setMouseTracking(True) self._initialization_bar.setMaximumHeight(15) self._initialization_bar.setTextVisible(True) self._initialization_bar.setAlignment(Qt.AlignHCenter) self._initialization_bar_layout.addWidget( self._initialization_bar_label) self._initialization_bar_layout.addWidget(self._initialization_bar) self._cam_settings_frame = EasyFrame() self._cam_settings_layout = QGridLayout(self._cam_settings_frame) self._resolution_selector_label = QLabel(self._cam_settings_frame) self._resolution_selector_label.setAlignment(Qt.AlignLeft) self._resolution_selector = QComboBox(self._cam_settings_frame) self._resolution_selector.setMaximumHeight(22) self._cam_settings_layout.addWidget(self._resolution_selector_label, 0, 0) self._cam_settings_layout.addWidget(self._resolution_selector, 0, 1) self._fps_selector_label = QLabel(self._cam_settings_frame) self._fps_selector_label.setAlignment(Qt.AlignLeft) self._fps_selector = QComboBox(self._cam_settings_frame) self._fps_selector.setMaximumHeight(22) self._cam_settings_layout.addWidget(self._fps_selector_label, 1, 0) self._cam_settings_layout.addWidget(self._fps_selector, 1, 1) self._show_feed_checkbox_label = QLabel(self._cam_settings_frame) self._show_feed_checkbox_label.setAlignment(Qt.AlignLeft) self._show_feed_checkbox = QCheckBox() self._show_feed_checkbox.setChecked(True) self._show_feed_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._show_feed_checkbox_label, 2, 0) self._cam_settings_layout.addWidget(self._show_feed_checkbox, 2, 1) self._use_cam_checkbox_label = QLabel(self._cam_settings_frame) self._use_cam_checkbox_label.setAlignment(Qt.AlignLeft) self._use_cam_checkbox = QCheckBox() self._use_cam_checkbox.setChecked(True) self._use_cam_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._use_cam_checkbox_label, 3, 0) self._cam_settings_layout.addWidget(self._use_cam_checkbox, 3, 1) self._use_overlay_checkbox_label = QLabel(self._cam_settings_frame) self._use_overlay_checkbox_label.setAlignment(Qt.AlignLeft) self._use_overlay_checkbox = QCheckBox() self._use_overlay_checkbox.setChecked(True) self._use_overlay_checkbox.setLayoutDirection(Qt.RightToLeft) self._cam_settings_layout.addWidget(self._use_overlay_checkbox_label, 4, 0) self._cam_settings_layout.addWidget(self._use_overlay_checkbox, 4, 1) self._image_display_frame = EasyFrame() self._image_display_layout = QVBoxLayout(self._image_display_frame) self._image_display_label = QLabel(self._image_display_frame) self._image_display_label.setAlignment(Qt.AlignHCenter) self._image_display = QLabel(self._image_display_frame) self._image_display.setAlignment(Qt.AlignCenter) self._image_display.setMouseTracking(True) self._image_display_layout.addWidget(self._image_display_label) self._image_display_layout.addWidget(self._image_display) spacer = QSpacerItem(1, 1, vData=QSizePolicy.Expanding) self._dev_sets_frame = EasyFrame() self._dev_sets_layout = QVBoxLayout(self._dev_sets_frame) self.config_button = ClickAnimationButton() self.config_button.clicked.connect(self._config_button_handler) self.layout().addWidget(self.config_button, 0, 0, Qt.AlignTop | Qt.AlignRight) self.config_button.setFixedSize(30, 25) self.config_button.setStyleSheet( "background-color: rgba(200, 200, 200, 50%)") self._dev_sets_layout.addWidget(self._cam_settings_frame) self._dev_sets_layout.addItem(spacer) self.layout().addWidget(self._image_display, 0, 0) self.layout().addWidget(self._initialization_bar_frame, 0, 0) self._config_items = [ self._resolution_selector, self._fps_selector, self._use_cam_checkbox, self._show_feed_checkbox, self._use_overlay_checkbox, ] config_win_w = 350 config_win_h = len(self._config_items) * 30 self.config_button.raise_() self._config_win = ConfigPopUp() self._config_win.setLayout(self._dev_sets_layout) self._config_win.setFixedSize(config_win_w, config_win_h) self._rec_label = QLabel() self._rec_label.setStyleSheet( "background-color: rgba(0, 0, 0, 0%); color: red; font: 20px") self._rec_label.hide() self.layout().addWidget(self._rec_label, 0, 0, Qt.AlignBottom | Qt.AlignRight) self.layout().setMargin(0) self._window_changing = False self._showing_images = False self._hidden = False w = 320 self._aspect_ratio = 9 / 16 self.resize(w, self.heightForWidth(w)) self._strings = dict() self._lang_enum = LangEnum.ENG self.setMinimumSize(self._subwindow_width, self._subwindow_height) self.old_size = QSize(self.width(), self.height()) self._logger.debug("Initialized") def set_show_feed_button_handler(self, func) -> None: """ Add handler for show camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._show_feed_checkbox.toggled.connect(func) self._logger.debug("done") def set_resolution_selector_handler(self, func) -> None: """ Add handler for resolution selector. :param func: The handler. :return None: """ self._logger.debug("running") self._resolution_selector.activated.connect(func) self._logger.debug("done") def set_fps_selector_handler(self, func) -> None: """ Add handler for resolution selector. :param func: The handler. :return None: """ self._logger.debug("running") self._fps_selector.activated.connect(func) self._logger.debug("done") def set_use_cam_button_handler(self, func) -> None: """ Add handler for use camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._use_cam_checkbox.toggled.connect(func) self._logger.debug("done") def set_use_overlay_button_handler(self, func) -> None: """ Add handler for use camera selector. :param func: The handler. :return None: """ self._logger.debug("running") self._use_overlay_checkbox.toggled.connect(func) self._logger.debug("done") def _config_button_handler(self) -> None: """ Show config pop up. :return None: """ self._logger.debug("running") self.config_button.setStyleSheet( "background-color: rgba(200, 200, 200, 50%)") self._config_win.exec_() self._logger.debug("done") @property def language(self) -> LangEnum: """ :return: The current lang enum being used. """ return self._lang_enum @language.setter def language(self, lang: LangEnum) -> None: """ Set the language for this view object. :param lang: The language to use. :return None: """ self._logger.debug("running") self._lang_enum = lang self._strings = strings[lang] self._set_texts() self._set_tooltips() self._logger.debug("done") @property def resolution_list(self) -> list: """ Get list of resolutions. :return list: The list of resolutions. """ ret = list() return ret @resolution_list.setter def resolution_list(self, res_list: list) -> None: """ Set list of resolutions available to res_list. :param res_list: The list of available resolutions. :return None: """ self._logger.debug("running") self._resolution_selector.clear() for item in res_list: self._resolution_selector.addItem(str(item)) self._logger.debug("done") @property def resolution(self) -> str: """ Get the current resolution selection. :return str: The current resolution. """ return self._resolution_selector.currentText() @resolution.setter def resolution(self, res: str) -> None: """ Set the current resolution selection. :param res: The resolution to set to. :return None: """ self._resolution_selector.setCurrentIndex( self._resolution_selector.findText(res)) @property def fps_list(self) -> list: """ Get list of fps options. :return list: The list of fps options. """ ret = list() return ret @fps_list.setter def fps_list(self, fps_list: list) -> None: """ Set list of available fps to fps_list. :param fps_list: :return None: """ self._logger.debug("running") self._fps_selector.clear() for item in fps_list: self._fps_selector.addItem(str(item)) self._logger.debug("done") @property def fps(self) -> str: """ Get the current fps selection. :return str: The current fps selection. """ return self._fps_selector.currentText() @fps.setter def fps(self, fps: str) -> None: """ Set the current fps selection. :param fps: The fps to set to. :return None: """ self._logger.debug("running") self._fps_selector.setCurrentIndex(self._fps_selector.findText(fps)) self._logger.debug("done") @property def use_feed(self) -> bool: """ Get the current use_cam setting. :return bool: User selection for using cam. """ return self._show_feed_checkbox.isChecked() @use_feed.setter def use_feed(self, useable: bool) -> None: """ Set use_cam setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._show_feed_checkbox.setChecked(useable) self._logger.debug("Done") @property def use_cam(self) -> bool: """ Get the current use_cam setting. :return bool: User selection for using cam. """ return self._use_cam_checkbox.isChecked() @use_cam.setter def use_cam(self, useable: bool) -> None: """ Set use_cam setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._use_cam_checkbox.setChecked(useable) self._logger.debug("Done") @property def use_overlay(self) -> bool: """ Get the current use_overlay setting. :return bool: User selection for using overlay. """ return self._use_overlay_checkbox.isChecked() @use_overlay.setter def use_overlay(self, useable: bool) -> None: """ Set use_overlay setting. :param useable: The setting to set to. :return None: """ self._logger.debug("running") self._use_overlay_checkbox.setChecked(useable) self._logger.debug("Done") def heightForWidth(self, w: int) -> int: return int(w * self._aspect_ratio) def widthForHeight(self, h: int) -> int: return int(h / self._aspect_ratio) def hideEvent(self, hideEvent: QHideEvent): """ Track minimize event. :param hideEvent: :return None: """ self._hidden = True def showEvent(self, showEvent: QShowEvent): """ Track restore event. :param showEvent: :return None: """ self._hidden = False def update_image(self, image: QPixmap = None, msg: str = None) -> None: """ Update image viewer with new image. :param image: The new image to show. :param msg: The text to show if no image. :return None: """ self._logger.debug("running") if not self._window_changing: if image is not None: temp_image_w = image.scaledToWidth(self.width() - 15) temp_image_h = image.scaledToHeight(self.height() - 35) if temp_image_w.height() > self.height() - 35: self._image_display.setPixmap(temp_image_h) else: self._image_display.setPixmap(temp_image_w) elif msg is not None: self._image_display.setText(msg) self._logger.debug("done") def show_images(self) -> None: """ Show image display and hide initialization bar. :return None: """ self._logger.debug("running") geo = self.geometry() self._showing_images = True self._initialization_bar_frame.hide() self._image_display.show() self.config_button.show() self.setStyleSheet("background-color: black") self.setGeometry(geo) self._logger.debug("done") def show_initialization(self) -> None: """ Show initialization bar and hide image display. :return None: """ self._logger.debug("running") self._showing_images = False self._image_display.hide() self.config_button.hide() self._initialization_bar_frame.show() self._logger.debug("done") def update_init_bar(self, progress: int) -> None: """ set progress bar value to progress. :param progress: The value to set progress bar to. :return None: """ self._logger.debug("running") if progress > 100: progress = 100 elif progress < 0: progress = 0 self._initialization_bar.setValue(progress) self._logger.debug("done") def set_config_active(self, is_active: bool) -> None: """ Set whether this camera config options are usable. :param is_active: Usable. :return None: """ self._logger.debug("running") if self._showing_images: if is_active: self._rec_label.hide() elif self.use_cam: self._rec_label.show() for item in self._config_items: item.setEnabled(is_active) self._logger.debug("done") def _set_texts(self) -> None: """ Set the texts in this view object. :return None: """ self._logger.debug("running") self._initialization_bar_label.setText( self._strings[StringsEnum.INITIALIZATION_BAR_LABEL]) self._initialization_bar.setValue(0) self._image_display_label.setText( self._strings[StringsEnum.IMAGE_DISPLAY_LABEL]) self._image_display.setText(self._strings[StringsEnum.IMAGE_DISPLAY]) self._show_feed_checkbox_label.setText( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_LABEL]) self._use_cam_checkbox_label.setText( self._strings[StringsEnum.USE_CAM_CHECKBOX_LABEL]) self._use_overlay_checkbox_label.setText( self._strings[StringsEnum.USE_OVERLAY_CHECKBOX_LABEL]) self._resolution_selector_label.setText( self._strings[StringsEnum.RESOLUTION_SELECTOR_LABEL]) self._fps_selector_label.setText( self._strings[StringsEnum.FPS_SELECTOR_LABEL]) self._config_win.setWindowTitle( self.get_name() + " " + self._strings[StringsEnum.CONFIG_TAB_LABEL]) self.config_button.setText("...") self._rec_label.setText("rec ●") self._logger.debug("done") def _set_tooltips(self) -> None: """ Set the tooltips in this view object. :return None: """ self._logger.debug("running") self._resolution_selector_label.setToolTip( self._strings[StringsEnum.RESOLUTION_SELECTOR_TOOLTIP]) self._resolution_selector.setToolTip( self._strings[StringsEnum.RESOLUTION_SELECTOR_TOOLTIP]) self._fps_selector_label.setToolTip( self._strings[StringsEnum.FPS_SELECTOR_TOOLTIP]) self._fps_selector.setToolTip( self._strings[StringsEnum.FPS_SELECTOR_TOOLTIP]) self._show_feed_checkbox_label.setToolTip( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_TOOLTIP]) self._show_feed_checkbox.setToolTip( self._strings[StringsEnum.SHOW_FEED_CHECKBOX_TOOLTIP]) self._use_cam_checkbox_label.setToolTip( self._strings[StringsEnum.USE_CAM_CHECKBOX_TOOLTIP]) self._use_cam_checkbox.setToolTip( self._strings[StringsEnum.USE_CAM_CHECKBOX_TOOLTIP]) self._use_overlay_checkbox_label.setToolTip( self._strings[StringsEnum.USE_OVERLAY_TOOLTIP]) self._use_overlay_checkbox.setToolTip( self._strings[StringsEnum.USE_OVERLAY_TOOLTIP]) self._image_display.setToolTip( self._strings[StringsEnum.IMAGE_DISPLAY_TOOLTIP]) self._logger.debug("done")
class TTVSetsImporterWidget(QWidget): ttv_updated = Signal(TTVSets) def __init__( self, format_to_widget, parent: "QWidget" = None, ): super().__init__(parent) # Components self._ttv = None # Maps a with its respective Widget self._format_to_widget = format_to_widget self._stacked_widgets = QStackedWidget() self._name_textbox = QLineEdit("Unnamed") self._formatter_selector = QComboBox() for (dataset_io_name, widget_factory) in self._format_to_widget.items(): self._formatter_selector.addItem( dataset_io_name, getattr(DatasetIORegistry, dataset_io_name)) self._stacked_widgets.addWidget( self._format_to_widget[dataset_io_name]()) def horizontal_line(): line = QFrame() line.setFrameShape(QFrame.HLine) line.setFrameShadow(QFrame.Sunken) line.setFixedHeight(2) line.setContentsMargins(0, 30, 0, 0) return line self._x_datatype_selector = DatatypeSelectorFactory( title="X (Input) Datatype") self._y_datatype_selector = DatatypeSelectorFactory( title="Y (Output) Datatype") datatypes_layout = QHBoxLayout() datatypes_layout.addWidget(self._x_datatype_selector) datatypes_layout.addWidget(self._y_datatype_selector) self._info_layout = QFormLayout() self._info_layout.addRow("Name", self._name_textbox) self._info_layout.addRow("Format", self._formatter_selector) self._main_layout = QVBoxLayout() self._main_layout.addLayout(self._info_layout) self._main_layout.addWidget(horizontal_line()) self._main_layout.addWidget(self._stacked_widgets) self._main_layout.addWidget(horizontal_line()) self._main_layout.addLayout(datatypes_layout) self._load_ttv_from_file_button = QPushButton("Load from file...") self._load_ttv_from_file_button.clicked.connect( self._load_ttv_from_file) self._update_ttv_button = QPushButton("Load TTV") self._update_ttv_button.clicked.connect(self._update_ttv) self._button_box = QDialogButtonBox() self._button_box.addButton(self._load_ttv_from_file_button, QDialogButtonBox.ResetRole) self._button_box.addButton(self._update_ttv_button, QDialogButtonBox.ApplyRole) self._main_layout.addWidget(self._button_box) self.setLayout(self._main_layout) self._formatter_selector.currentIndexChanged[int].connect( lambda i: self._stacked_widgets.setCurrentIndex(i)) def get_ttv(self) -> "TTVSets": return self._ttv def _update_ttv(self): self._ttv = self._stacked_widgets.currentWidget().load_ttv( self._name_textbox.text(), self._x_datatype_selector.selected_datatype, self._y_datatype_selector.selected_datatype, ) self.ttv_updated.emit(self._ttv) def _load_ttv_from_file(self): print("Loading from file...") description_file_path = QFileDialog.getOpenFileName( self, "Open TTV .json file")[0] if description_file_path: LOGGER.debug("Loading %s...", description_file_path) ttv_dir = os.path.dirname(description_file_path) ttv_description = TTVSetsIO.load_ttv_description( description_file_path) # Update name self._name_textbox.setText(ttv_description["name"]) # Pick the new formatter formatter_idx = self._formatter_selector.findText( ttv_description["format"]) if formatter_idx != -1: self._formatter_selector.setCurrentIndex(formatter_idx) self._selected_index_changed(formatter_idx) # TODO: Cover case when "train" is null self._x_datatype_selector.change_current_datatype( ttv_description["train"]["x_type"]) self._y_datatype_selector.change_current_datatype( ttv_description["train"]["y_type"]) self._stacked_widgets.currentWidget().update_widgets( ttv_dir, ttv_description) self._update_ttv() else: LOGGER.debug("Loading cancelled") def _selected_index_changed(self, index: int): self._stacked_widgets.setCurrentIndex(index) def sizeHint(self) -> "QSize": """Optimal size of the widget.""" return QSize(450, 150) def __reduce__(self): return ( TTVSetsImporterWidget, (self._ttv_sets_dialog, None), )
def account_dialog(self, new_account=False): self.account_dialog_window = QDialog(self) self.account_dialog_window.setMinimumSize(300, 125) if self.account_dialog_window.isVisible(): self.account_dialog_window.hide() # Main layout dialog_layout = QVBoxLayout() self.account_dialog_window.setLayout(dialog_layout) account_name_edit = QLineEdit() comment_edit = QLineEdit() comment_edit.setPlaceholderText(_("Comment")) steam_skin_select = QComboBox() steam_skin_select.addItems(self.switcher.steam_skins) if new_account: user = {} self.account_dialog_window.setWindowTitle(_("Add account")) self.submit_button = QPushButton(_("Add")) self.submit_button.setDisabled(True) else: login_name_selected = self.accounts_list.currentItem().data(5) user = self.switcher.settings["users"].get(login_name_selected, {}) self.account_dialog_window.setWindowTitle( _("Edit account {0}").format(login_name_selected)) self.submit_button = QPushButton(_("Edit")) account_name_edit.setText(login_name_selected) comment_edit.setText(user.get("comment")) steam_skin_select_index = steam_skin_select.findText( user.get("steam_skin", _("default"))) if steam_skin_select_index != -1: steam_skin_select.setCurrentIndex(steam_skin_select_index) else: steam_skin_select.setCurrentIndex(1) def submit_enabled(item): if 3 < len(item) < 32: self.submit_button.setEnabled(True) else: self.submit_button.setEnabled(False) account_name_edit.setPlaceholderText(_("Login name")) account_name_edit.textChanged.connect(submit_enabled) close_button = QPushButton(_("Close")) dialog_layout.addWidget(account_name_edit) dialog_layout.addWidget(comment_edit) dialog_layout.addWidget(steam_skin_select) def update_user(u: dict) -> dict: u["comment"] = comment_edit.text() u["steam_skin"] = steam_skin_select.currentText() return u self.submit_button.clicked.connect(lambda: self.save_account( account_name_edit.text(), update_user(user), login_name_selected if not new_account else None)) close_button.clicked.connect(self.account_dialog_window.close) buttons = QHBoxLayout() buttons.addWidget(self.submit_button) buttons.addWidget(close_button) dialog_layout.addLayout(buttons) self.account_dialog_window.show()
class RootsApp(QMainWindow): standard_deviation_threshold = 0.1 # when I receive a measurement from the sensor I check if its standard deviation; if it's too low it means the sensor is not working temporary_database_filename = "temporary.db" # the current session is stored in a temporary database. When the user saves, it is copied at the desired location def __init__(self): super().__init__(); self.setWindowTitle("Roots") self.setFixedWidth(1200) self.resize(1200, 1200) self.threadpool = QThreadPool(); self.object_list = list() self.is_training_on = False self.interaction_under_training = None self.n_measurements_collected = 0 self.n_measurements_to_collect = 3 self.sensor_not_responding = True self.sensor_not_responding_timeout = 2000 # milliseconds self.database_connection = self.create_temporary_database() self.active_object = None self.number_of_objects_added = 0 self.sensor_start_freq = 250000 self.sensor_end_freq = 3000000 # creates the plot self.plotWidget = pyqtgraph.PlotWidget(title = "Sensor Response") self.plotWidget.setFixedHeight(300) self.plotWidget.getAxis("bottom").setLabel("Excitation frequency", "Hz") self.plotWidget.getAxis("left").setLabel("Volts", "V") self.dataPlot = self.plotWidget.plot() # timer used to see if the sensor is responding self.timer = QTimer() self.timer.setInterval(self.sensor_not_responding_timeout) self.timer.timeout.connect(self.timer_timeout) self.timer_timeout() # defines the actions in the file menu with button actions iconExit = QIcon("icons/icon_exit.png") btnActionExit = QAction(iconExit, "Exit", self) btnActionExit.setStatusTip("Click to terminate the program") btnActionExit.triggered.connect(self.exit) iconSave = QIcon("icons/icon_save.ico") buttonActionSave = QAction(iconSave, "Save current set of objects", self) # buttonActionSave.setStatusTip("Click to perform action 2") buttonActionSave.triggered.connect(self.save) iconOpen = QIcon("icons/icon_load.png") buttonActionOpen = QAction(iconOpen, "Load set of objects", self) buttonActionOpen.triggered.connect(self.open) # toolbar toolBar = QToolBar("Toolbar") toolBar.addAction(buttonActionSave) toolBar.addAction(buttonActionOpen) toolBar.setIconSize(QSize(64, 64)) toolBar.setStyleSheet(styles.toolbar) self.addToolBar(toolBar) # menu menuBar = self.menuBar() menuBar.setStyleSheet(styles.menuBar) menuFile = menuBar.addMenu("File") menuOptions = menuBar.addMenu("Options") menuView = menuBar.addMenu("View") menuConnect = menuBar.addMenu("Connect") menuFile.addAction(buttonActionSave) menuFile.addAction(buttonActionOpen) menuFile.addAction(btnActionExit) # status bar self.setStatusBar(QStatusBar(self)) # creates the "My Objects" label labelMyObjects = QLabel("My Objects") labelMyObjects.setFixedHeight(100) labelMyObjects.setAlignment(Qt.AlignCenter) labelMyObjects.setStyleSheet(styles.labelMyObjects) # button "add object" icon_plus = QIcon("icons/icon_add.png") self.btn_create_object = QPushButton("Add Object") self.btn_create_object.setCheckable(False) self.btn_create_object.setIcon(icon_plus) self.btn_create_object.setFixedHeight(80) self.btn_create_object.setStyleSheet(styles.addObjectButton) self.btn_create_object.clicked.connect(self.create_object) # defines the layout of the "My Objects" section self.verticalLayout = QVBoxLayout() self.verticalLayout.setContentsMargins(0,0,0,0) self.verticalLayout.addWidget(labelMyObjects) self.verticalLayout.addWidget(self.btn_create_object) self.spacer = QSpacerItem(0,2000, QSizePolicy.Expanding, QSizePolicy.Expanding) self.verticalLayout.setSpacing(0) self.verticalLayout.addSpacerItem(self.spacer) #adds spacer # defines the ComboBox which holds the names of the objects self.comboBox = QComboBox() self.comboBox.addItem("- no object selected") self.comboBox.currentIndexChanged.connect(self.comboBox_index_changed) self.comboBox.setFixedSize(300, 40) self.comboBox.setStyleSheet(styles.comboBox) self.update_comboBox() # defines the label "Selected Object" (above the comboBox) self.labelComboBox = QLabel() self.labelComboBox.setText("Selected Object:") self.labelComboBox.setStyleSheet(styles.labelComboBox) self.labelComboBox.adjustSize() # vertical layout for the combobox and its label self.VLayoutComboBox = QVBoxLayout() self.VLayoutComboBox.addWidget(self.labelComboBox) self.VLayoutComboBox.addWidget(self.comboBox) # label with the output text (the big one on the right) self.labelClassification = QLabel() self.labelClassification.setText("No interaction detected") self.labelClassification.setFixedHeight(80) self.labelClassification.setStyleSheet(styles.labelClassification) self.labelClassification.adjustSize() HLayoutComboBox = QHBoxLayout() HLayoutComboBox.addLayout(self.VLayoutComboBox) HLayoutComboBox.addSpacerItem(QSpacerItem(1000,0, QSizePolicy.Expanding, QSizePolicy.Expanding)); #adds spacer HLayoutComboBox.addWidget(self.labelClassification) # creates a frame that contains the combobox and the labels frame = QFrame() frame.setStyleSheet(styles.frame) frame.setLayout(HLayoutComboBox) # sets the window layout with the elements created before self.windowLayout = QVBoxLayout() self.windowLayout.addWidget(self.plotWidget) self.windowLayout.addWidget(frame) self.windowLayout.addLayout(self.verticalLayout) # puts everything into a frame and displays it on the window self.mainWindowFrame = QFrame() self.mainWindowFrame.setLayout(self.windowLayout) self.mainWindowFrame.setStyleSheet(styles.mainWindowFrame) self.setCentralWidget(self.mainWindowFrame) self.create_object() # creates one object at the beginning # ----------------------------------------------------------------------------------------------------------- # Shows a welcome message def show_welcome_msg(self): welcome_msg = QMessageBox() welcome_msg.setText("Welcome to the Roots application!") welcome_msg.setIcon(QMessageBox.Information) welcome_msg.setInformativeText(strings.welcome_text) welcome_msg.setWindowTitle("Welcome") welcome_msg.exec_() # ----------------------------------------------------------------------------------------------------------- # When the user changes the object in the combobox, updates the active object def comboBox_index_changed(self, index): object_name = self.comboBox.currentText() for object in self.object_list: if object.name == object_name: self.set_active_object(object) print("DEBUG: selected object changed. Object name: {0}".format(object.name)) return # ----------------------------------------------------------------------------------------------------------- # This function allows to save the current objects on a file def save(self): current_path = os.getcwd() directory_path = current_path + "/Saved_Workspaces" if not os.path.exists(directory_path): os.mkdir(directory_path) file_path = None [file_path, file_extension] = QFileDialog.getSaveFileName(self,"Roots", directory_path, "Roots database (*.db)") if file_path is None: return temp_database_path = current_path + "/" + RootsApp.temporary_database_filename shutil.copyfile(temp_database_path, file_path) # copies the temporary database to save the current workspace return # ----------------------------------------------------------------------------------------------------------- # this function creates a clean database where all the data of this session will be temporarily stored def create_temporary_database(self): current_path = os.getcwd() file_path = current_path + "/" + RootsApp.temporary_database_filename if os.path.exists(file_path): # if the database is already there it deletes it to reset it os.remove(file_path) print("DEBUG: removing database. (in 'RootsApp.create_temporary_database()'") database_connection = database.create_connection(RootsApp.temporary_database_filename) # creates the temporary database database.create_tables(database_connection) # initializes the database database.reset_db(database_connection) # resets the database (not needed but it doesn't cost anything to put it) return database_connection # ----------------------------------------------------------------------------------------------------------- # This function allows to load previously created objects from a file def open(self): current_path = os.getcwd() saved_files_directory = current_path + "/Saved_Workspaces" [file_path, file_extension] = QFileDialog.getOpenFileName(self,"Roots", saved_files_directory, "Roots database (*.db)"); if file_path == '': return for object in self.object_list.copy(): # deletes all the objects print("DEBUG: deleting object {0} (in 'open()')".format(object.name)) self.delete_object(object) temp_database_path = current_path + "/" + RootsApp.temporary_database_filename self.database_connection.close() os.remove(temp_database_path) shutil.copyfile(file_path, temp_database_path) # replaces the temporary database with the file to open self.database_connection = database.create_connection(temp_database_path) object_tuples = database.get_all_objects(self.database_connection) for object_tuple in object_tuples: object_ID, object_name = object_tuple location_IDs = database.get_locations_id_for_object(self.database_connection, object_ID) formatted_location_IDs = [] for location_ID in location_IDs: formatted_location_IDs.append(location_ID[0]) print("DEBUG: loading object {0} with location IDs {1}. (in 'RootsApp.open()')".format(object_name, formatted_location_IDs)) self.add_object(object_name, object_ID, formatted_location_IDs) self.train_classifiers() return # ----------------------------------------------------------------------------------------------------------- # This function updates the ComboBox whenever objects are created, destroyed or the active object has changed def update_comboBox(self): print("DEBUG: repainting ComboBox. (in 'RootsApp.update_comboBox()'") self.comboBox.clear() self.comboBox.addItem("none") for object in self.object_list: self.comboBox.addItem(object.name) self.comboBox.adjustSize() # ----------------------------------------------------------------------------------------------------------- # This is a timer which is restarted every time a measurement is received. If it elapses it means that the sesnor is not connected def timer_timeout(self): print("DEBUG: timer timeout. (in 'RootsApp.timer_timeout()'") self.sensor_not_responding = True self.statusBar().showMessage(strings.sensor_disconnected) self.statusBar().setStyleSheet(styles.statusBarError) self.plotWidget.setTitle("Sensor not connected") # ----------------------------------------------------------------------------------------------------------- # This function creates a new object in the database and then calls the "add_object" function, which adds the newly created object to the application def create_object(self): new_object_name = "Object {0}".format(self.number_of_objects_added + 1) [new_object_ID, location_IDs] = database.create_object(self.database_connection, new_object_name) self.add_object(new_object_name, new_object_ID, location_IDs) # ----------------------------------------------------------------------------------------------------------- # This function deletes an object from the database, and from the application object list. It alsos destroys the object def delete_object(self, object): print("DEBUG: deleting object {0}. (in 'RootsApp.delete_object()')".format(object.ID)) database.delete_object(self.database_connection, object.ID) self.object_list.remove(object) self.verticalLayout.removeItem(object.layout) self.update_comboBox() object.delete() # ----------------------------------------------------------------------------------------------------------- # This function adds an object to the current application. Note that if you want to create an object ex-novo you should call "create_object". This function is useful when loading existing objects from a file def add_object(self, name, object_ID, location_IDs): self.number_of_objects_added += 1 new_object = Object(name, object_ID, location_IDs, self) self.object_list.append(new_object) for ID in location_IDs: # initializes the measurements with 0 if the measurement is empty #print("DEBUG: initializing location ID {0}".format(ID)) measurements = database.get_measurements_for_location(self.database_connection, ID) print("DEBUG: location {0} of object {1} is trained: {2}. (in 'RootsApp.add_object()')".format(ID, new_object.name, database.is_location_trained(self.database_connection, ID))) if len(measurements) == 0: database.save_points(self.database_connection, [0], ID) database.set_location_trained(self.database_connection, ID, "FALSE") elif database.is_location_trained(self.database_connection, ID) == "TRUE": new_object.get_interaction_by_ID(ID).setCalibrated(True) # inserts the newly created object before the "Add Object" button index = self.verticalLayout.indexOf(self.btn_create_object) self.verticalLayout.insertLayout(index, new_object.layout) self.update_comboBox() print("DEBUG: object {0} added. (in 'RootsApp.add_object()')".format(new_object.name)) return # ----------------------------------------------------------------------------------------------------------- # This function takes as input the measurement data and formats it to plot it on the graph def update_graph(self, data): frequency_step = (self.sensor_end_freq - self.sensor_start_freq) / len(data) x_axis = numpy.arange(self.sensor_start_freq, self.sensor_end_freq, frequency_step) formatted_data = numpy.transpose(numpy.asarray([x_axis, data])) self.dataPlot.setData(formatted_data) # ----------------------------------------------------------------------------------------------------------- # This function starts the UDP server that receives the measurements def run_UDP_server(self, UDP_IP, UDP_PORT): self.UDPServer = UDPServer(UDP_IP, UDP_PORT) self.UDPServer.signals.measurementReceived.connect(self.process_measurement) self.threadpool.start(self.UDPServer) # ----------------------------------------------------------------------------------------------------------- # This function changes some global variables to tell the application to save the incoming measurements into the database. The measurements belong to the interaction passed as argument def start_collecting_measurements(self, interaction): if self.sensor_not_responding: print("DEBUG: warning! Can't start calibration, the sensor is not responding! (in 'RootsApp.start_collecting_measurements()')") error_msg = QMessageBox() error_msg.setText("Can't start calibration!") error_msg.setIcon(QMessageBox.Critical) error_msg.setInformativeText('The sensor is not responding, make sure it is connected') error_msg.setWindowTitle("Error") error_msg.exec_() else: print("starting to collect measurements into the database at location ID {0} (in 'RootsApp.start_collecting_measurements()')".format(interaction.ID)); self.is_training_on = True self.interaction_under_training = interaction database.delete_measurements_from_location(self.database_connection, interaction.ID) # resets the location measurements self.progress_dialog = QProgressDialog("Calibrating", "Abort", 0, self.n_measurements_to_collect, self) self.progress_dialog.setWindowModality(Qt.WindowModal) self.progress_dialog.setWindowTitle("Calibration") self.progress_dialog.setFixedSize(400, 200) self.progress_dialog.setValue(0) self.progress_dialog.exec_() # ----------------------------------------------------------------------------------------------------------- # This function is called by the UDP thread every time that a measurement is received. It does the following: # 1. Plots the incoming measurement # 2. IF training mode IS on: # Predicts the interaction (tries to guess where the user is touching) # ELSE: # Saves the measurement and retrains the classifier with the new data def process_measurement(self, received_data): self.sensor_not_responding = False self.plotWidget.setTitle("Sensor response") self.timer.start() # starts the timer that checks if we are receiving data from the sensor measurement = received_data.split(' ') # get rid of separator measurement = [float(i) for i in measurement] # convert strings to float self.update_graph(measurement) self.predict_interaction(measurement) # checks the standard deviation of the received data to see if the sensor is working well if (numpy.std(measurement) < self.standard_deviation_threshold): self.statusBar().showMessage(strings.sensor_not_working) self.statusBar().setStyleSheet(styles.statusBarError) else: self.statusBar().setStyleSheet(styles.statusBar) if self.is_training_on: print("saving measurement {0} into database at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_collected + 1, self.interaction_under_training.ID)) database.save_points(self.database_connection, measurement, self.interaction_under_training.ID) self.n_measurements_collected += 1 self.progress_dialog.setValue(self.n_measurements_collected) if (self.n_measurements_collected >= self.n_measurements_to_collect): self.is_training_on = False self.n_measurements_collected = 0 print("DEBUG: {0} measurements were saved at location_ID {1}. (in 'RootsApp.process_measurement()')".format(self.n_measurements_to_collect, self.interaction_under_training.ID)) self.train_classifiers() self.interaction_under_training.setCalibrated(True) # this makes the button "Calibrate" change coulour # ----------------------------------------------------------------------------------------------------------- # This function retrains the classifiers using all the measurements present in the database and assigns to each object its classifier def train_classifiers(self): #[objects_ID, classifiers] classifiers = classifier.get_classifiers(self.database_connection) print("DEBUG: the following classifiers were created: {0}. (in 'RootsApp.train_classifiers')".format(classifiers)) for object in self.object_list: for index, tuple in enumerate(classifiers): object_ID, classif = tuple; # extracts the object ID and the classifier from the tuple if object_ID == object.ID: object.classifier = classif del classifiers[index] # ----------------------------------------------------------------------------------------------------------- # This function changes the current active object (the software tries to guess where the user is touching using the calibration data from the active object) def set_active_object(self, active_object): self.active_object = active_object for obj in self.object_list: if obj == active_object: active_object.set_highlighted(True) else: obj.set_highlighted(False) index = self.comboBox.findText(self.active_object.name) # updates the index of the ComboBox self.comboBox.setCurrentIndex(index) # ----------------------------------------------------------------------------------------------------------- # This function changes the name of an object. It updates the database AND the application data structure. def rename_object(self, object, new_name): print("DEBUG: changing name of object '{0}' (in 'RootsApp.rename_object')".format(object.name)) object.set_name(new_name) database.rename_object(self.database_connection, object.ID, new_name) self.update_comboBox() # ----------------------------------------------------------------------------------------------------------- # This function uses the classifier of the active object to guess where the user is touching, based on the incoming measurement def predict_interaction(self, measurement): if (len(self.object_list) <= 0): self.labelClassification.setText("No objects available") self.statusBar().showMessage(strings.no_objects) return if self.active_object is None: self.labelClassification.setText("No object selected") self.statusBar().showMessage(strings.no_object_selected) return if self.active_object.classifier is None: self.labelClassification.setText("The object is not calibrated") self.statusBar().showMessage(strings.object_not_calibrated) return else: predicted_interaction_id = self.active_object.classifier(measurement) interaction = self.active_object.get_interaction_by_ID(predicted_interaction_id) self.labelClassification.setText(interaction.name) self.statusBar().showMessage("") #print("DEBUG: predicted interaction ID: ", interaction.ID) # ----------------------------------------------------------------------------------------------------------- # This is a system event that gets called whenever the user tries to close the application. It calls the "exit()" # function (just below) to open a dialog to make sure the user really wants to quit. def closeEvent(self, event): if not self.exit(): event.ignore() # ----------------------------------------------------------------------------------------------------------- # This function gets called when the user cliks on the "Exit" button in the "File" menu or when it tries to close the window (indirectly) # Here we open a dialog to make sure the user really wants to quit. def exit(self): dialogWindow = DialogExit() answer = dialogWindow.exec_() if (answer == True): self.UDPServer.stop() self.close() return answer
class MainWindow(QObject): """ Appliaction GUI """ current_port_signal = Signal(str) current_table = Signal(str) def __init__(self, ui_fil, parent=None): super(MainWindow, self).__init__(parent) self.ui_file = QFile(ui_fil) self.ui_file.open(QFile.ReadOnly) self.loader = QUiLoader() self.loader.setLanguageChangeEnabled(True) self.window = self.loader.load(self.ui_file) self.window.setGeometry(50, 50, 860, 640) self.window.setWindowIcon(QIcon('icons/icons8-wi-fi-64.png')) self.ui_file.close() self.trans = QTranslator() self.trans.load('lang/pl_PL.qm') self.conn_baudrates = [ "9600", "115200", "300", "1200", "2400", "4800", "14400", "19200", "31250", "38400", "57600" ] self.conn_boards = ["ESP32", "ESP8266"] self.widget_tab = self.window.findChild(QTabWidget, 'tabWidget') self.widget_tab.setIconSize(QSize(36, 36)) self.widget_tab.setTabIcon(0, QIcon('icons/icons8-automatic-64.png')) self.widget_tab.setTabIcon(1, QIcon('icons/icons8-bar-chart-50.png')) self.widget_tab.setTabIcon(2, QIcon('icons/icons8-menu-64.png')) self.widget_tab.setTabIcon(3, QIcon('icons/icons8-timeline-64.png')) self.widget_tab.setTabIcon(4, QIcon('icons/icons8-bluetooth-50.png')) self.widget_tab.setTabIcon(5, QIcon('icons/icons8-console-64.png')) self.lang_combo_box = self.window.findChild(QComboBox, 'lang_combo_box') self.lang_combo_box.addItem("English") self.lang_combo_box.addItem("Polski") self.lang_combo_box.currentTextChanged.connect(self.change_language) self.serial_port_box = self.window.findChild(QComboBox, 'serial_port_box') self.baud_rate_box = self.window.findChild(QComboBox, 'baud_rate_box') self.select_board_box = self.window.findChild(QComboBox, 'select_board_box') self.scan_time_edit = self.window.findChild(QTimeEdit, 'scan_time_edit') self.wifi_scan_box = self.window.findChild(QCheckBox, 'wifi_scan_box') self.blue_scan_box = self.window.findChild(QCheckBox, 'blue_scan_box') self.save_data_check = self.window.findChild(QCheckBox, 'save_data_check') self.connection_status_edit = self.window.findChild( QLineEdit, 'connection_status_edit') self.start_button = self.window.findChild(QPushButton, 'start_button') self.stop_button = self.window.findChild(QPushButton, 'stop_button') self.stop_button.setEnabled(False) self.vertical_wifi_layout = self.window.findChild( QVBoxLayout, 'verticalLayout_5') self.wifi_list_view = self.window.findChild(QListWidget, 'wifi_list_view') self.wifi_list_view.setIconSize(QSize(64, 64)) self.vertical_timeline_layout = self.window.findChild( QVBoxLayout, 'verticalLayout_7') self.blue_list_view = self.window.findChild(QListWidget, 'blue_list_view') self.blue_list_view.setIconSize(QSize(64, 64)) self.console_logging_check = self.window.findChild( QCheckBox, 'console_logging_check') self.console_autoscroll_check = self.window.findChild( QCheckBox, 'console_autoscroll_check') self.console_text_edit = self.window.findChild(QTextEdit, 'console_text_edit') self.select_board_box.activated[str].connect(self.change_board) #Settings tab for i in self.conn_baudrates: self.baud_rate_box.addItem(i) for i in self.conn_boards: self.select_board_box.addItem(i) self.connection_status_edit.setText('Not connected') self.connection_status_edit.setStyleSheet( "background: red; color: white; font-size: 14px; border-width: 1px; \ border-style: solid; border-radius: 2px; border-color: red;") thread1 = ReadSerialPortsThread.ReadSerialPortsThread(self) thread1.add_serial_port.connect( lambda p: self.serial_port_box.addItem(p)) thread1.remove_serial_port.connect( lambda p: self.serial_port_box.removeItem( self.serial_port_box.findText(p))) thread1.start() thread2 = MakeSerialConnection.MakeSerialConnection(self) thread4 = WriteToDatabase.WriteToDatabase(self) self.serial_port_box.currentTextChanged.connect(thread2.get_curr_port) self.baud_rate_box.activated[str].connect(thread2.get_curr_baud) self.select_board_box.activated[str].connect(thread2.get_curr_board) self.scan_time_edit.timeChanged.connect(thread2.get_curr_time) self.start_button.clicked.connect(thread2.start) self.stop_button.clicked.connect(thread2.stop_serial_communication) self.wifi_scan_box.clicked.connect(thread2.get_wifi_check) self.blue_scan_box.clicked.connect(thread2.get_blue_check) self.save_data_check.clicked.connect(thread2.enable_saving_to_db) self.save_data_check.clicked.connect(thread4.enable_saving_data_func) self.lang_combo_box.currentTextChanged.connect( lambda s: thread2.get_current_lang(s)) thread2.baud_box_state.connect( lambda b: self.baud_rate_box.setEnabled(b)) thread2.port_box_state.connect( lambda b: self.serial_port_box.setEnabled(b)) thread2.board_box_state.connect( lambda b: self.select_board_box.setEnabled(b)) thread2.time_edit_state.connect( lambda b: self.scan_time_edit.setEnabled(b)) thread2.wifi_check_state.connect( lambda b: self.wifi_scan_box.setEnabled(b)) thread2.blue_check_state.connect( lambda b: self.blue_scan_box.setEnabled(b)) thread2.serial_port_placeholder.connect( lambda t: self.serial_port_box.addItem(t)) thread2.serial_port_clear.connect(self.serial_port_box.clear) thread2.send_text_signal.connect( lambda t: self.console_text_edit.append(t)) thread2.start_btn_state.connect( lambda b: self.start_button.setEnabled(b)) thread2.stop_btn_state.connect( lambda b: self.stop_button.setEnabled(b)) thread2.conn_stat_text.connect( lambda t: self.connection_status_edit.setText(t)) thread2.conn_stat_style.connect( lambda s: self.connection_status_edit.setStyleSheet(s)) #Wi-Fi Chart tab self.chart = QtCharts.QChart() self.axis_x = QtCharts.QValueAxis() self.axis_y = QtCharts.QValueAxis() self.line_series = QtCharts.QLineSeries() self.line_series.append(QPoint(0, 0)) self.chart.setAxisX(self.axis_x, self.line_series) self.axis_x.setRange(2400, 2483) self.axis_x.setTickCount(10) self.axis_x.setMinorTickCount(4) self.axis_x.applyNiceNumbers() self.axis_x.setTitleText("Frequency [MHz]") self.chart.setAxisY(self.axis_y, self.line_series) self.axis_y.setRange(-100, -30) self.axis_y.applyNiceNumbers() self.axis_y.setTickCount(9) self.axis_y.setMinorTickCount(4) self.axis_y.setTitleText("RSSI [dBm]") self.chart.legend().setVisible(True) self.chart.legend().setAlignment(Qt.AlignRight) self.chart.setBackgroundRoundness(0) self.chart_view = QtCharts.QChartView(self.chart) self.chart_view.setRenderHint(QPainter.Antialiasing) self.vertical_wifi_layout.addWidget(self.chart_view) #WiFi List tab thread2.clear_wifi_list.connect(self.wifi_list_view.clear) thread2.append_wifi_list_item.connect( lambda i: self.wifi_list_view.addItem(i)) thread2.append_wifi_timeline_data.connect( lambda d: self.append_data(d)) thread2.save_wifi_timeline_data.connect( lambda t: thread4.update_db_file(t)) thread2.clear_wifi_series.connect(self.chart.removeAllSeries) thread2.add_wifi_series.connect(lambda s: self.chart.addSeries(s)) thread2.set_axis_x_series.connect( lambda s: self.chart.setAxisX(self.axis_x, s)) thread2.set_axis_y_series.connect( lambda s: self.chart.setAxisY(self.axis_y, s)) thread2.wifi_data_to_func.connect( lambda d: thread2.append_wifi_data(d)) thread2.wifi_data_to_func.connect( lambda d: thread2.append_data_to_wifi_graph(d)) thread2.wifi_data_to_func.connect( lambda d: thread2.append_data_to_wifi_timeline(d)) thread2.blue_data_to_func.connect( lambda d: thread2.append_blue_data(d)) #Wi-Fi Timeline tab self.wifi_channels_occupancy_array = [] self.wifi_channels_timestamps = [] self.deleted_empty_vals = False self.freeze_graph_bool_val = False self.last_item = 0 self.graph_item_color = QColor(255, 195, 0) self.bars = QtDataVisualization.Q3DBars() self.column_axis = QtDataVisualization.QCategory3DAxis() self.column_axis.setTitle('Channels') self.column_axis.setTitleVisible(True) self.column_axis.setLabels([ 'Channel 1', 'Channel 2', 'Channel 3', 'Channel 4', 'Channel 5', 'Channel 6', 'Channel 7', 'Channel 8', 'Channel 9', 'Channel 10', 'Channel 11', 'Channel 12', 'Channel 13' ]) self.column_axis.setLabelAutoRotation(45) self.column_axis.setAutoAdjustRange(True) self.row_axis = QtDataVisualization.QCategory3DAxis() self.row_axis.setTitle('Timeline') self.row_axis.setTitleVisible(True) self.value_axis = QtDataVisualization.QValue3DAxis() self.value_axis.setTitle('RSSI [dBm]') self.value_axis.setTitleVisible(True) self.value_axis.setRange(-100, -10) self.bars.setRowAxis(self.row_axis) self.bars.setColumnAxis(self.column_axis) self.bars.setValueAxis(self.value_axis) self.bars.setBarSpacingRelative(False) self.bars.setFloorLevel(-100) self.series = QtDataVisualization.QBar3DSeries() self.array_data = [[]] self.series.dataProxy().addRows( self.data_to_bar_data_array(self.array_data)) self.series.setBaseColor(self.graph_item_color) self.bars.setPrimarySeries(self.series) self.container = QWidget.createWindowContainer(self.bars) if not self.bars.hasContext(): print("Couldn't initialize the OpenGL context") sys.exit(-1) camera = self.bars.scene().activeCamera() camera.setXRotation(-45.0) camera.setYRotation(22.5) geometry = QGuiApplication.primaryScreen().geometry() size = geometry.height() * 3 / 4 self.container.setMinimumSize(size, size) self.container.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored) self.container.setFocusPolicy(Qt.StrongFocus) self.append_data([ -100, -80, -100, -80, -100, -80, -100, -80, -100, -80, -100, -80, -100 ]) self.nav_layout_h = QHBoxLayout() self.freeze_check = QCheckBox() self.freeze_check.setText("Freeze") self.freeze_check.clicked.connect(self.enable_scrolling_data) self.freeze_check.stateChanged.connect(self.freeze_graph) self.prev_button = QPushButton() self.prev_button.setText("Previous") self.prev_button.setEnabled(False) self.prev_button.clicked.connect(self.previous_data) self.next_button = QPushButton() self.next_button.setText("Next") self.next_button.setEnabled(False) self.next_button.clicked.connect(self.next_data) self.nav_layout_h.addWidget(self.freeze_check) self.nav_layout_h.addWidget(self.prev_button) self.nav_layout_h.addWidget(self.next_button) self.load_data_check = QCheckBox() self.load_data_check.setText("Load archival data") self.load_data_check.clicked.connect(self.enable_load_data) self.load_data_combo = QComboBox() self.load_data_combo.addItem("No data found") self.load_data_combo.setEnabled(False) self.load_data_btn = QPushButton() self.load_data_btn.setText("Load") self.load_data_btn.setEnabled(False) self.nav_layout_h2 = QHBoxLayout() self.nav_layout_h2.addWidget(self.load_data_check) self.nav_layout_h2.addWidget(self.load_data_combo) self.nav_layout_h2.addWidget(self.load_data_btn) thread4.start() thread4.remove_defualt_item.connect( lambda: self.load_data_combo.clear()) thread4.append_available_day.connect( lambda s: self.load_data_combo.addItem(s)) thread4.send_data_from_database.connect( lambda t: self.append_data_from_database(t)) self.load_data_combo.currentTextChanged.connect( lambda t: thread4.get_curr_table(t)) self.load_data_btn.clicked.connect(thread4.load_data_button) self.vertical_timeline_layout.addWidget(self.container) self.vertical_timeline_layout.addLayout(self.nav_layout_h) self.vertical_timeline_layout.addLayout(self.nav_layout_h2) #Bluetooth tab thread2.clear_blue_list.connect(self.blue_list_view.clear) thread2.append_blue_list_item.connect( lambda i: self.blue_list_view.addItem(i)) #Console tab self.console_autoscroll_check.stateChanged.connect( self.enable_auto_scroll) thread3 = WriteToFile.WriteToFile(self) self.console_logging_check.stateChanged.connect(thread3.enable_logging) thread2.send_text_signal.connect(lambda t: thread3.write_to_file(t)) self.window.show() def change_board(self): """ Disable Bluetooth scan option if ESP8266 board is selected """ print(self.select_board_box.currentText()) if self.select_board_box.currentText() == "ESP8266": self.blue_scan_box.setEnabled(False) elif self.select_board_box.currentText() == "ESP32": self.blue_scan_box.setEnabled(True) def enable_auto_scroll(self): """ Enable scrolling in text console """ if self.console_autoscroll_check.checkState() == Qt.Checked: self.console_text_edit.moveCursor(QTextCursor.End) else: self.console_text_edit.moveCursor(QTextCursor.Start) def freeze_graph(self, state): """ Stop showing live data in 3d Wi-Fi graph """ if state == 2: self.freeze_graph_bool_val = True else: self.freeze_graph_bool_val = False def data_to_bar_data_row(self, data): """ DOCSTRING """ return list(QtDataVisualization.QBarDataItem(d) for d in data) def data_to_bar_data_array(self, data): """ DOCSTRING """ return list(self.data_to_bar_data_row(row) for row in data) def append_data(self, data): """ DOCSTRING """ self.wifi_channels_occupancy_array.append(data) self.wifi_channels_timestamps.append(str(datetime.datetime.now())) self.update_graph() def update_graph(self): """ DOCSTRING """ if len(self.wifi_channels_occupancy_array) < 10: missing_vals = 10 - len(self.wifi_channels_occupancy_array) for z in range(missing_vals): self.wifi_channels_occupancy_array.append([ -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 ]) self.wifi_channels_timestamps.append("") self.print_freshes_ten() elif len(self.wifi_channels_occupancy_array ) > 20 and self.deleted_empty_vals is False: for y in range( self.wifi_channels_occupancy_array.count([ -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 ])): self.wifi_channels_occupancy_array.remove([ -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100 ]) self.wifi_channels_timestamps.remove("") self.deleted_empty_vals = True self.print_freshes_ten() else: self.print_freshes_ten() def previous_data(self): """ DOCSTRING """ if self.last_item < 9 and len(self.wifi_channels_occupancy_array) < 9: QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "To few information to rewind!", QMessageBox.Ok) elif self.last_item < 9: print('out of range') QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "No more data to rewind!", QMessageBox.Ok) else: temp_array = [] temp_timestamp_array = [] if self.last_item - 9 < 0: print('out of range') QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "No more data to rewind!", QMessageBox.Ok) else: for x in range(self.last_item - 10, self.last_item - 1): temp_array.append(self.wifi_channels_occupancy_array[x]) temp_timestamp_array.append( self.wifi_channels_timestamps[x]) self.last_item = self.last_item - 9 self.row_axis.setLabels(temp_timestamp_array) self.series.dataProxy().resetArray() self.series.dataProxy().addRows( self.data_to_bar_data_array(temp_array)) def next_data(self): """ DOCSTRING """ if self.last_item < 9 and len(self.wifi_channels_occupancy_array) < 9: QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "To few information to rewind!", QMessageBox.Ok) elif self.last_item > len(self.wifi_channels_occupancy_array): print('out of range') QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "No more data to rewind!", QMessageBox.Ok) else: temp_array = [] temp_timestamp_array = [] if self.last_item + 9 > len(self.wifi_channels_occupancy_array): print('out of range') QMessageBox.warning(self.window, 'ISM 2.4GHz Scanner - Warning', "No more data to rewind!", QMessageBox.Ok) else: for x in range(self.last_item + 1, self.last_item + 10): temp_array.append(self.wifi_channels_occupancy_array[x]) temp_timestamp_array.append( self.wifi_channels_timestamps[x]) self.last_item = self.last_item + 9 self.row_axis.setLabels(temp_timestamp_array) self.series.dataProxy().resetArray() self.series.dataProxy().addRows( self.data_to_bar_data_array(temp_array)) def print_freshes_ten(self): """ DOCSTRING """ if self.freeze_graph_bool_val is False: i = 0 temp_array = [] temp_timestamp_array = [] self.last_item = 0 for x in range(len(self.wifi_channels_occupancy_array) - 1): if i < 10: temp_array.append(self.wifi_channels_occupancy_array[ len(self.wifi_channels_occupancy_array) - x - 1]) temp_timestamp_array.append(self.wifi_channels_timestamps[ len(self.wifi_channels_timestamps) - x - 1]) i += 1 elif i == 10: self.last_item = len( self.wifi_channels_occupancy_array) - 10 i += 1 self.row_axis.setLabels(temp_timestamp_array) self.series.dataProxy().resetArray() self.series.dataProxy().addRows( self.data_to_bar_data_array(temp_array)) def enable_scrolling_data(self, state): """ DOCSTRING """ if state is True: self.prev_button.setEnabled(True) self.next_button.setEnabled(True) else: self.prev_button.setEnabled(False) self.next_button.setEnabled(False) def enable_load_data(self, state): """ DOCSTRING """ if state is True: self.load_data_combo.setEnabled(True) if (self.load_data_combo.findText("No data found") == -1 or self.load_data_combo.findText("Nie znaleziono danych")): self.load_data_btn.setEnabled(True) else: self.load_data_combo.setEnabled(False) self.load_data_btn.setEnabled(False) self.wifi_channels_occupancy_array = [] self.wifi_channels_timestamps = [] self.print_freshes_ten() def change_language(self, lang): """ DOCSTRING """ if lang == "Polski": QCoreApplication.installTranslator(self.trans) self.axis_x.setTitleText("Częstotliwość [MHz]") self.freeze_check.setText("Zamróź") self.load_data_check.setText("Otwórz dane archiwalne") self.load_data_combo.removeItem( self.load_data_combo.findText("No data found")) self.load_data_combo.addItem("Nie znaleziono danych") self.load_data_btn.setText("Otwórz") self.next_button.setText("Następny") self.prev_button.setText("Poprzedni") self.column_axis.setTitle('Kanały') self.column_axis.setLabels([ 'Kanał 1', 'Kanał 2', 'Kanał 3', 'Kanał 4', 'Kanał 5', 'Kanał 6', 'Kanał 7', 'Kanał 8', 'Kanał 9', 'Kanał 10', 'Kanał 11', 'Kanał 12', 'Kanał 13' ]) self.row_axis.setTitle('Oś czasu') self.bars.setColumnAxis(self.column_axis) self.bars.setRowAxis(self.row_axis) else: QCoreApplication.removeTranslator(self.trans) self.axis_x.setTitleText("Frequency [MHz]") self.freeze_check.setText("Freeze") self.load_data_check.setText("Load archival data") self.load_data_combo.removeItem( self.load_data_combo.findText("Nie znaleziono danych")) self.load_data_combo.addItem("No data found") self.load_data_btn.setText("Load") self.next_button.setText("Next") self.prev_button.setText("Previous") self.column_axis.setTitle('Channels') self.column_axis.setLabels([ 'Channel 1', 'Channel 2', 'Channel 3', 'Channel 4', 'Channel 5', 'Channel 6', 'Channel 7', 'Channel 8', 'Channel 9', 'Channel 10', 'Channel 11', 'Channel 12', 'Channel 13' ]) self.row_axis.setTitle('Timeline') self.bars.setColumnAxis(self.column_axis) self.bars.setRowAxis(self.row_axis) def append_data_from_database(self, data): """ DOCSTRING !!! """ self.wifi_channels_occupancy_array = [] self.wifi_channels_timestamps = [] self.print_freshes_ten() for row in data: itm_nmbr = 0 self.temp_db_array = [] for item in row: if itm_nmbr == 1: self.wifi_channels_timestamps.append( "" + str(self.load_data_combo.currentText()) + " " + item + "") elif itm_nmbr > 1: self.temp_db_array.append(item) itm_nmbr += 1 self.wifi_channels_occupancy_array.append(self.temp_db_array) self.print_freshes_ten()
class ControlBox(qtw.QWidget): def __init__(self, parent=None): super().__init__(parent) self.jointlabel = QLabel("Joint name") self.humannum = QLabel("Human number") self.mkhuman = QPushButton("Create(&Z)") self.rmhuman = QPushButton("Remove(&x)") self.joint_next = QPushButton("Next_joint(&A)") self.joint_prev = QPushButton("Pre_joint(&S)") self.nextButton = QPushButton("Next") self.prevButton = QPushButton("Prev") self.jointcombo = QComboBox() self.jointcombo.addItem("0.nose") self.jointcombo.addItem("1.neck") self.jointcombo.addItem("2.Right shoulder") self.jointcombo.addItem("3.Right elbow") self.jointcombo.addItem("4.Right hand") self.jointcombo.addItem("5.Left shoulder") self.jointcombo.addItem("6.Left elbow") self.jointcombo.addItem("7.Left hand") self.jointcombo.addItem("8.Right hip") self.jointcombo.addItem("9.Right knee") self.jointcombo.addItem("10.Right foot") self.jointcombo.addItem("11.Left hip") self.jointcombo.addItem("12.Left knee") self.jointcombo.addItem("13.Left foot") self.jointcombo.addItem("14.Right eye") self.jointcombo.addItem("15.Left eye") self.jointcombo.addItem("16.Right ear") self.jointcombo.addItem("17.Left ear") self.humancombo = QComboBox() self.humancombo.addItem("None") self.controlBox = self.createMainControl() self.mkhuman.clicked.connect(self.add_new_human) self.rmhuman.clicked.connect(self.rm_current_human) self.joint_next.clicked.connect(self.next_to_joint) self.joint_prev.clicked.connect(self.prev_to_joint) def createMainControl(self): self.grid = QGridLayout() self.grid.addWidget(self.jointlabel, 0, 0) self.grid.addWidget(self.jointcombo, 0, 1) self.grid.addWidget(self.joint_next, 0, 2) self.grid.addWidget(self.joint_prev, 0, 3) self.grid.addWidget(self.humannum, 1, 0) self.grid.addWidget(self.humancombo, 1, 1) self.grid.addWidget(self.mkhuman, 1, 2) self.grid.addWidget(self.rmhuman, 1, 3) self.grid.addWidget(self.prevButton, 3, 0, 1, 1) self.grid.addWidget(self.nextButton, 3, 1, 1, 1) return self.grid def add_new_human(self): if (self.humancombo.currentText() == 'None') | (self.humancombo.currentText() == ''): self.nonenum = self.humancombo.findText('None') self.humancombo.removeItem(self.nonenum) self.humancombo.addItem('1') else: currentNum = int(self.humancombo.currentText()) # print(currentNum) nextNum = currentNum # print('next ', nextNum) if nextNum < 100: self.humancombo.setCurrentIndex(nextNum) def rm_current_human(self): nowText = self.humancombo.currentText() nowNum = self.humancombo.findText(nowText) if nowNum >= 1: self.humancombo.setCurrentIndex(nowNum - 1) def next_to_joint(self): #print('現在のコンボindex', self.jointcombo.currentIndex()) next_combo = self.jointcombo.currentIndex() + 1 #print('次のコンボ', next_combo) if next_combo < 18: self.jointcombo.setCurrentIndex(next_combo) def prev_to_joint(self): #print('現在のコンボindex', self.jointcombo.currentIndex()) prev_combo = self.jointcombo.currentIndex() - 1 #print('前のコンボ', prev_combo) if prev_combo >= 0: self.jointcombo.setCurrentIndex(prev_combo)
class WMatSelectV(QGroupBox): """ Material related widget including a Label, a Combobox to select a material and a Button to edit a material libary. WMatSelect is instantiated to empty material data, so it has to be referenced to actual material data with the update method prior to its first usage. """ # Signal to W_MachineSetup to know that the save popup is needed saveNeeded = Signal() def __init__(self, parent=None): """ Set a reference to a material libray and material data path, updates the Combobox by the material names of the libary and set a referenced material by name. Parameters ---------- self : A WMatSelect object parent : A reference to the widgets parent Returns ------- """ # Build the interface according to the .ui file QGroupBox.__init__(self, parent) self.verticalLayout = QVBoxLayout(self) self.c_mat_type = QComboBox(self) self.c_mat_type.setObjectName(u"c_mat_type") self.verticalLayout.addWidget(self.c_mat_type) self.b_matlib = QPushButton(self) self.b_matlib.setObjectName(u"b_matlib") self.b_matlib.setText("Edit Materials") self.verticalLayout.addWidget(self.b_matlib) # Create the property of the widget self.mat_win = None # DMatLib widget self.obj = None # object that has a material attribute self.mat_attr_name = "" # material attribute name self.matlib = list() # Matlib self.matlib_path = "" # Path to save the matlib self.def_mat = "M400-50A" # Default material self.is_hide_button = False # To hide the "Edit material" button # Connect the signals self.c_mat_type.currentIndexChanged.connect(self.set_mat_type) self.b_matlib.clicked.connect(self.s_open_matlib) def update(self, obj, mat_attr_name, matlib, matlib_path=""): """ Set a reference to a material libray and material data path, updates the Combobox by the material names of the libary and set a referenced material by name. Parameters ---------- self : A WMatSelect object obj : A pyleecan object that has a material attribute mat_attr_name : A string of the material attribute name matlib : A material libary, i.e. a list of Material objects matlib_path : A string containing the path of material data Returns ------- """ self.c_mat_type.blockSignals(True) # Set material combobox according to matlib names self.obj = obj self.mat_attr_name = mat_attr_name self.matlib = matlib self.matlib_path = matlib_path if self.is_hide_button: self.b_matlib.hide() else: self.b_matlib.show() # Update the list of materials self.c_mat_type.clear() items_to_add = [] # Add RefMatLib materials items_to_add.extend([mat.name for mat in matlib.dict_mat["RefMatLib"]]) # Add machine-specific materials items_to_add.extend( [mat.name for mat in matlib.dict_mat["MachineMatLib"]]) self.c_mat_type.addItems(items_to_add) mat = getattr(self.obj, mat_attr_name, None) if mat is None or mat.name is None: # Default lamination material: M400-50A index = self.c_mat_type.findText(self.def_mat) if index != -1: # self.mat.__init__(init_dict=self.matlib[index].as_dict()) setattr( self.obj, self.mat_attr_name, self.matlib.dict_mat["RefMatLib"][index], ) else: index = self.c_mat_type.findText(mat.name) self.c_mat_type.setCurrentIndex(index) self.c_mat_type.blockSignals(False) def setText(self, txt): """ Set the Label's text Parameters ---------- self : A WMatSelect object txt : A text string Returns ------- """ self.setTitle(txt) def set_mat_type(self, index): """ Signal to set the referenced material from the material libary by the selected Combobox index Parameters ---------- self : A WMatSelect object index : Current index of the combobox Returns ------- """ if index >= len(self.matlib.dict_mat["RefMatLib"]): index -= len(self.matlib.dict_mat["RefMatLib"]) dict_key = "MachineMatLib" else: dict_key = "RefMatLib" setattr(self.obj, self.mat_attr_name, self.matlib.dict_mat[dict_key][index]) # Notify the machine GUI that the machine has changed self.saveNeeded.emit() def s_open_matlib(self): """ Open the GUI (DMatLib widget) to Edit the Material library Parameters ---------- self : A WMatSelect object Returns ------- """ if self.c_mat_type.currentIndex() >= len( self.matlib.dict_mat["RefMatLib"]): index = self.c_mat_type.currentIndex() - len( self.matlib.dict_mat["RefMatLib"]) key = "MachineMatLib" else: index = self.c_mat_type.currentIndex() key = "RefMatLib" self.mat_win = DMatLib(self.matlib, key, index) self.mat_win.accepted.connect(self.set_matlib) self.mat_win.saveNeeded.connect(self.emit_save) self.mat_win.show() def emit_save(self): """ Emit saveNeeded if a material has been edited """ self.saveNeeded.emit() def set_matlib(self): """Update the matlib with the new value Parameters ---------- self : A WMatSelect object Returns ------- """ # Empty and fill the list to keep the same object (to change it everywhere) # del self.matlib[:] # self.matlib.extend(self.mat_win.matlib) # Update the material # index = int(self.mat_win.nav_mat.currentItem().text()[:3]) - 1 # not needed if machine materials are "connected" properly # mat_dict = (self.mat_win.matlib[index]).as_dict() # self.mat.__init__(init_dict=mat_dict) # Do not clear for now to keep editor (DMatLib) open # # Clear the window # self.mat_win.deleteLater() # self.mat_win = None # Update the widget # Avoid trigger signal currentIndexChanged self.c_mat_type.blockSignals(True) self.c_mat_type.clear() items_to_add = [] # Add RefMatLib materials items_to_add.extend( [mat.name for mat in self.matlib.dict_mat["RefMatLib"]]) # Add machine-specific materials items_to_add.extend( [mat.name for mat in self.matlib.dict_mat["MachineMatLib"]]) self.c_mat_type.addItems(items_to_add) index = self.c_mat_type.findText( getattr(self.obj, self.mat_attr_name).name) self.c_mat_type.setCurrentIndex(index) self.c_mat_type.blockSignals(False)
def initialise(self): self.settingsFile = os.path.join(self.findNukeHomeDir(), "deadline_settings.ini") print("Loading settings: " + self.settingsFile) # Initialize the submission settings. self.settings = QSettings(self.settingsFile, QSettings.IniFormat) print("Grabbing submitter info...") try: dcOutput = CallDeadlineCommand([ "-prettyJSON", "-GetSubmissionInfo", "Pools", "Groups", "MaxPriority", "TaskLimit", "UserHomeDir", "RepoDir:submission/Hiero/Main", "RepoDir:submission/Integration/Main", ], useDeadlineBg=True) output = json.loads(dcOutput, encoding="utf-8") except ValueError as e: print("Unable to get submitter info from Deadline:\n\n" + e.message) raise if output["ok"]: self.submissionInfo = output["result"] else: raise ValueError( "DeadlineCommand returned a bad result and was unable to grab the submitter info.\n\n" + output["result"]) # Get the Deadline temp directory. deadlineHome = self.submissionInfo["UserHomeDir"].strip() self.deadlineTemp = os.path.join(deadlineHome, "temp") self.integrationDir = self.submissionInfo["RepoDirs"][ "submission/Integration/Main"].strip() # Get maximum priority. maximumPriority = self.submissionInfo["MaxPriority"] # Collect the pools and groups. pools = self.submissionInfo["Pools"] secondaryPools = [""] secondaryPools.extend(pools) groups = self.submissionInfo["Groups"] # Set up the other default arrays. onJobComplete = ("Nothing", "Archive", "Delete") nukeVersions = ("6.0", "6.1", "6.2", "6.3", "6.4", "7.0", "7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2", "8.3", "8.4", "9.0", "9.1", "9.2", "9.3", "9.4", "10.0", "10.1", "10.2", "10.3", "10.4", "11.0", "11.1", "11.2", "11.3") buildsToForce = ("None", "32bit", "64bit") # Main Window mainWindow = hiero.ui.mainWindow() dialog = QDialog(mainWindow) self.dialog = dialog dialog.setWindowTitle("Submit to Deadline (and render with Nuke)") # Main Layout topLayout = QVBoxLayout() dialog.setLayout(topLayout) tabWidget = QTabWidget(dialog) jobTab = QWidget() jobTabLayout = QVBoxLayout() jobTab.setLayout(jobTabLayout) # Job Info Layout jobInfoGroupBox = QGroupBox("Job Description") jobTabLayout.addWidget(jobInfoGroupBox) jobInfoLayout = QGridLayout() jobInfoGroupBox.setLayout(jobInfoLayout) # Job Name jobInfoLayout.addWidget(QLabel("Job Name"), 0, 0) jobNameWidget = QLineEdit(self.settings.value("JobName", "")) jobInfoLayout.addWidget(jobNameWidget, 0, 1) # Comment jobInfoLayout.addWidget(QLabel("Comment"), 1, 0) commentWidget = QLineEdit(self.settings.value("Comment", "")) jobInfoLayout.addWidget(commentWidget, 1, 1) # Department jobInfoLayout.addWidget(QLabel("Department"), 2, 0) departmentWidget = QLineEdit(self.settings.value("Department", "")) jobInfoLayout.addWidget(departmentWidget, 2, 1) # Job Options Layout jobOptionsGroupBox = QGroupBox("Job Options") jobTabLayout.addWidget(jobOptionsGroupBox) jobOptionsLayout = QGridLayout() jobOptionsGroupBox.setLayout(jobOptionsLayout) # Pool jobOptionsLayout.addWidget(QLabel("Pool"), 0, 0) poolWidget = QComboBox() for pool in pools: poolWidget.addItem(pool) defaultPool = self.settings.value("Pool", "none") defaultIndex = poolWidget.findText(defaultPool) if defaultIndex != -1: poolWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(poolWidget, 0, 1, 1, 3) # Secondary Pool jobOptionsLayout.addWidget(QLabel("Secondary Pool"), 1, 0) secondaryPoolWidget = QComboBox() for secondaryPool in secondaryPools: secondaryPoolWidget.addItem(secondaryPool) defaultSecondaryPool = self.settings.value("SecondaryPool", "") defaultIndex = secondaryPoolWidget.findText(defaultSecondaryPool) if defaultIndex != -1: secondaryPoolWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(secondaryPoolWidget, 1, 1, 1, 3) # Group jobOptionsLayout.addWidget(QLabel("Group"), 2, 0) groupWidget = QComboBox() for group in groups: groupWidget.addItem(group) defaultGroup = self.settings.value("Group", "none") defaultIndex = groupWidget.findText(defaultGroup) if defaultIndex != -1: groupWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(groupWidget, 2, 1, 1, 3) # Priority initPriority = int(self.settings.value("Priority", "50")) if initPriority > maximumPriority: initPriority = maximumPriority / 2 jobOptionsLayout.addWidget(QLabel("Priority"), 3, 0) priorityWidget = QSpinBox() priorityWidget.setRange(0, maximumPriority) priorityWidget.setValue(initPriority) jobOptionsLayout.addWidget(priorityWidget, 3, 1) # Task Timeout jobOptionsLayout.addWidget(QLabel("Task Timeout"), 4, 0) taskTimeoutWidget = QSpinBox() taskTimeoutWidget.setRange(0, 1000000) taskTimeoutWidget.setValue(int(self.settings.value("TaskTimeout", "0"))) jobOptionsLayout.addWidget(taskTimeoutWidget, 4, 1) # Auto Task Timeout autoTaskTimeoutWidget = QCheckBox("Enable Auto Task Timeout") autoTaskTimeoutWidget.setChecked( strtobool(self.settings.value("AutoTaskTimeout", "False"))) jobOptionsLayout.addWidget(autoTaskTimeoutWidget, 4, 2) # Concurrent Tasks jobOptionsLayout.addWidget(QLabel("Concurrent Tasks"), 5, 0) concurrentTasksWidget = QSpinBox() concurrentTasksWidget.setRange(1, 16) concurrentTasksWidget.setValue( int(self.settings.value("ConcurrentTasks", "1"))) jobOptionsLayout.addWidget(concurrentTasksWidget, 5, 1) # Limit Tasks To Slave's Task Limit limitConcurrentTasksWidget = QCheckBox( "Limit Tasks To Slave's Task Limit") limitConcurrentTasksWidget.setChecked( strtobool(self.settings.value("LimitConcurrentTasks", "True"))) jobOptionsLayout.addWidget(limitConcurrentTasksWidget, 5, 2) # Machine Limit jobOptionsLayout.addWidget(QLabel("Machine Limit"), 6, 0) machineLimitWidget = QSpinBox() machineLimitWidget.setRange(0, 1000000) machineLimitWidget.setValue( int(self.settings.value("MachineLimit", "1"))) jobOptionsLayout.addWidget(machineLimitWidget, 6, 1) # Machine List Is A Blacklist isBlacklistWidget = QCheckBox("Machine List Is A Blacklist") isBlacklistWidget.setChecked( strtobool(self.settings.value("IsBlacklist", "False"))) jobOptionsLayout.addWidget(isBlacklistWidget, 6, 2) # Machine List jobOptionsLayout.addWidget(QLabel("Machine List"), 7, 0) machineListWidget = QLineEdit(self.settings.value("MachineList", "")) jobOptionsLayout.addWidget(machineListWidget, 7, 1, 1, 2) def browseMachineList(): output = CallDeadlineCommand( ["-selectmachinelist", str(machineListWidget.text())], False) output = output.replace("\r", "").replace("\n", "") if output != "Action was cancelled by user": machineListWidget.setText(output) machineListButton = QPushButton("Browse") machineListButton.pressed.connect(browseMachineList) jobOptionsLayout.addWidget(machineListButton, 7, 3) # Limits jobOptionsLayout.addWidget(QLabel("Limits"), 8, 0) limitsWidget = QLineEdit(self.settings.value("Limits", "")) jobOptionsLayout.addWidget(limitsWidget, 8, 1, 1, 2) def browseLimitList(): output = CallDeadlineCommand( ["-selectlimitgroups", str(limitsWidget.text())], False) output = output.replace("\r", "").replace("\n", "") if output != "Action was cancelled by user": limitsWidget.setText(output) limitsButton = QPushButton("Browse") limitsButton.pressed.connect(browseLimitList) jobOptionsLayout.addWidget(limitsButton, 8, 3) # On Job Complete jobOptionsLayout.addWidget(QLabel("On Job Complete"), 9, 0) onJobCompleteWidget = QComboBox() for option in onJobComplete: onJobCompleteWidget.addItem(option) defaultOption = self.settings.value("OnJobComplete", "Nothing") defaultIndex = onJobCompleteWidget.findText(defaultOption) if defaultIndex != -1: onJobCompleteWidget.setCurrentIndex(defaultIndex) jobOptionsLayout.addWidget(onJobCompleteWidget, 9, 1) # Submit Job As Suspended submitSuspendedWidget = QCheckBox("Submit Job As Suspended") submitSuspendedWidget.setChecked( strtobool(self.settings.value("SubmitSuspended", "False"))) jobOptionsLayout.addWidget(submitSuspendedWidget, 9, 2) # Nuke Options nukeOptionsGroupBox = QGroupBox("Nuke Options") jobTabLayout.addWidget(nukeOptionsGroupBox) nukeOptionsLayout = QGridLayout() nukeOptionsGroupBox.setLayout(nukeOptionsLayout) # Version nukeOptionsLayout.addWidget(QLabel("Version"), 0, 0) versionWidget = QComboBox() for version in nukeVersions: versionWidget.addItem(version) defaultVersion = self.settings.value("Version", "7.0") defaultIndex = versionWidget.findText(defaultVersion) if defaultIndex != -1: versionWidget.setCurrentIndex(defaultIndex) nukeOptionsLayout.addWidget(versionWidget, 0, 1) # Submit Nuke Script File With Job submitScriptWidget = QCheckBox("Submit Nuke Script File With Job") submitScriptWidget.setChecked( strtobool(self.settings.value("SubmitScript", "False"))) nukeOptionsLayout.addWidget(submitScriptWidget, 0, 2) # Build To Force nukeOptionsLayout.addWidget(QLabel("Build To Force"), 1, 0) buildWidget = QComboBox() for build in buildsToForce: buildWidget.addItem(build) defaultBuild = self.settings.value("Build", "None") defaultIndex = buildWidget.findText(defaultBuild) if defaultIndex != -1: buildWidget.setCurrentIndex(defaultIndex) nukeOptionsLayout.addWidget(buildWidget, 1, 1) # Render With NukeX useNukeXWidget = QCheckBox("Render With NukeX") useNukeXWidget.setChecked( strtobool(self.settings.value("UseNukeX", "False"))) nukeOptionsLayout.addWidget(useNukeXWidget, 1, 2) # Max RAM Usage (MB) nukeOptionsLayout.addWidget(QLabel("Max RAM Usage (MB)"), 2, 0) memoryWidget = QSpinBox() memoryWidget.setRange(0, 5000) memoryWidget.setValue(int(self.settings.value("Memory", "0"))) nukeOptionsLayout.addWidget(memoryWidget, 2, 1) # Continue On Error continueOnErrorWidget = QCheckBox("Continue On Error") continueOnErrorWidget.setChecked( strtobool(self.settings.value("ContinueOnError", "False"))) nukeOptionsLayout.addWidget(continueOnErrorWidget, 2, 2) # Threads nukeOptionsLayout.addWidget(QLabel("Threads"), 3, 0) threadsWidget = QSpinBox() threadsWidget.setRange(0, 256) threadsWidget.setValue(int(self.settings.value("Threads", "0"))) nukeOptionsLayout.addWidget(threadsWidget, 3, 1) # Use Batch Mode batchModeWidget = QCheckBox("Use Batch Mode") batchModeWidget.setChecked( strtobool(self.settings.value("BatchMode", "False"))) nukeOptionsLayout.addWidget(batchModeWidget, 3, 2) # Frames Per Task nukeOptionsLayout.addWidget(QLabel("Frames Per Task"), 4, 0) framesPerTaskWidget = QSpinBox() framesPerTaskWidget.setRange(1, 1000000) framesPerTaskWidget.setValue( int(self.settings.value("FramesPerTask", "1"))) nukeOptionsLayout.addWidget(framesPerTaskWidget, 4, 1) nukeOptionsLayout.addWidget( QLabel("(this only affects non-movie jobs)"), 4, 2) tabWidget.addTab(jobTab, "Job Options") # Button Box (Extra work required to get the custom ordering we want) self.pipelineToolStatusLabel.setAlignment(Qt.AlignRight | Qt.AlignVCenter) pipelineToolStatus = self.retrievePipelineToolStatus() self.updatePipelineToolStatusLabel(pipelineToolStatus) integrationButton = QPushButton("Pipeline Tools") integrationButton.clicked.connect(self.OpenIntegrationWindow) submitButton = QPushButton("Submit") submitButton.clicked.connect(dialog.accept) submitButton.setDefault(True) cancelButton = QPushButton("Cancel") cancelButton.clicked.connect(dialog.reject) buttonGroupBox = QGroupBox() buttonLayout = QGridLayout() buttonLayout.setColumnStretch( 0, 1) # Makes the pipeline status label expand, not the buttons buttonGroupBox.setLayout(buttonLayout) buttonGroupBox.setAlignment(Qt.AlignRight) buttonGroupBox.setFlat(True) buttonLayout.addWidget(self.pipelineToolStatusLabel, 0, 0) buttonLayout.addWidget(integrationButton, 0, 1) buttonLayout.addWidget(submitButton, 0, 2) buttonLayout.addWidget(cancelButton, 0, 3) topLayout.addWidget(tabWidget) topLayout.addWidget(buttonGroupBox) # Show the dialog. result = (dialog.exec_() == QDialog.DialogCode.Accepted) if result: # Need to pass integration dir path to render task self.settings.setValue("IntegrationDir", self.integrationDir) self.settings.setValue("JobName", jobNameWidget.text()) self.settings.setValue("Comment", commentWidget.text()) self.settings.setValue("Department", departmentWidget.text()) self.settings.setValue("Pool", poolWidget.currentText()) self.settings.setValue("SecondaryPool", secondaryPoolWidget.currentText()) self.settings.setValue("Group", groupWidget.currentText()) self.settings.setValue("Priority", priorityWidget.value()) self.settings.setValue("TaskTimeout", taskTimeoutWidget.value()) self.settings.setValue("AutoTaskTimeout", str(autoTaskTimeoutWidget.isChecked())) self.settings.setValue("ConcurrentTasks", concurrentTasksWidget.value()) self.settings.setValue("LimitConcurrentTasks", str(limitConcurrentTasksWidget.isChecked())) self.settings.setValue("MachineLimit", machineLimitWidget.value()) self.settings.setValue("IsBlacklist", str(isBlacklistWidget.isChecked())) self.settings.setValue("MachineList", machineListWidget.text()) self.settings.setValue("Limits", limitsWidget.text()) self.settings.setValue("OnJobComplete", onJobCompleteWidget.currentText()) self.settings.setValue("SubmitSuspended", str(submitSuspendedWidget.isChecked())) self.settings.setValue("Version", versionWidget.currentText()) self.settings.setValue("SubmitScript", str(submitScriptWidget.isChecked())) self.settings.setValue("Build", buildWidget.currentText()) self.settings.setValue("UseNukeX", str(useNukeXWidget.isChecked())) self.settings.setValue("FramesPerTask", framesPerTaskWidget.value()) self.settings.setValue("ContinueOnError", str(continueOnErrorWidget.isChecked())) self.settings.setValue("Threads", threadsWidget.value()) self.settings.setValue("BatchMode", str(batchModeWidget.isChecked())) self.settings.setValue("Memory", memoryWidget.value()) print("Saving settings: " + self.settingsFile) self.settings.sync() else: print("Submission canceled") self.settings = None # Not sure if there is a better way to stop the export process. This works, but it leaves all the tasks # in the Queued state. self.setError("Submission was canceled")
class SaveTab(QWidget): def __init__(self, parent): """Initialize the save tab """ super(SaveTab, self).__init__(parent) self.prefs = parent.prefs self.order_options = {'SortedOrder': 'Sorted (based on IDD file)', 'OriginalOrderTop': 'Original order (With new objects on top)', 'OriginalOrderBottom': 'Original order (With new objects on bottom)'} self.format_options = {'UseSpecialFormat': 'Use special formatting for some objects', 'None': 'Do not use special formatting'} # Sort Order Code order_label = QLabel("Save Order for Objects:") self.order_edit = QComboBox(self) self.order_edit.addItems(list(self.order_options.values())) self.order_edit.setMaximumWidth(350) order_setting = self.order_options[self.prefs['sort_order']] self.order_edit.setCurrentIndex(self.order_edit.findText(order_setting)) self.order_edit.currentIndexChanged.connect(self.update_order) format_label = QLabel("Special Formatting:") self.format_edit = QComboBox(self) self.format_edit.addItems(list(self.format_options.values())) self.format_edit.setMaximumWidth(350) format_setting = self.format_options[self.prefs['special_formatting']] self.format_edit.setCurrentIndex(self.format_edit.findText(format_setting)) self.order_edit.currentIndexChanged.connect(self.update_format) self.save_group_box = QGroupBox("Default Save Options") save_box = QVBoxLayout() save_box.addWidget(order_label) save_box.addWidget(self.order_edit) save_box.addSpacing(10) save_box.addWidget(format_label) save_box.addWidget(self.format_edit) save_box.addStretch(1) self.save_group_box.setLayout(save_box) self.save_group_box.setEnabled(False) # Save additional options code self.save_units_check = QCheckBox('Units to display by default (SI vs IP)', self) checked_header = Qt.Checked if self.prefs['save_units'] == 1 else Qt.Unchecked self.save_units_check.setCheckState(checked_header) self.save_units_check.stateChanged.connect(self.update) self.save_hidden_classes_check = QCheckBox('Whether empty classes are hidden', self) checked_cells = Qt.Checked if self.prefs['save_hidden_classes'] == 1 else Qt.Unchecked self.save_hidden_classes_check.setCheckState(checked_cells) self.save_hidden_classes_check.stateChanged.connect(self.update) self.save_groups_check = QCheckBox('Whether to hide group headers', self) checked_groups = Qt.Checked if self.prefs['save_hide_groups'] == 1 else Qt.Unchecked self.save_groups_check.setCheckState(checked_groups) self.save_groups_check.stateChanged.connect(self.update) # Save additional options group box code self.save_additional_group_box = QGroupBox("Save Additional Options in IDF File") save_additional_box = QVBoxLayout() save_additional_box.addWidget(self.save_units_check) save_additional_box.addWidget(self.save_hidden_classes_check) save_additional_box.addWidget(self.save_groups_check) save_additional_box.addStretch(1) self.save_additional_group_box.setLayout(save_additional_box) # Main layout code main_layout = QVBoxLayout() main_layout.addWidget(self.save_additional_group_box) main_layout.addSpacing(10) main_layout.addWidget(self.save_group_box) main_layout.addStretch(1) self.setLayout(main_layout) def update_order(self): text = self.order_edit.currentText() for key, val in list(self.order_options.items()): if val == text: to_save = key self.prefs['sort_order'] = to_save def update_format(self): text = self.format_edit.currentText() for key, val in self.format_options.items(): if val == text: to_save = key self.prefs['format'] = to_save def update(self): self.prefs['save_units'] = 1 if self.save_units_check.checkState() else 0 self.prefs['save_hidden_classes'] = 1 if self.save_hidden_classes_check.checkState() else 0 self.prefs['save_hide_groups'] = 1 if self.save_groups_check.checkState() else 0
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QHBoxLayout() self.ax = pg.PlotWidget() self.ax.showGrid(True, True) self.line = pg.InfiniteLine( pos=-20, pen=pg.mkPen('k', width=3), movable=False # We have our own code to handle dragless moving. ) self.ax.addItem(self.line) self.ax.setLimits(xMin=-HISTORIC_DAYS_N + 1, xMax=0) self.ax.getPlotItem().scene().sigMouseMoved.connect( self.mouse_move_handler) self.base_currency = DEFAULT_BASE_CURRENCY # Store a reference to lines on the plot, and items in our # data viewer we can update rather than redraw. self._data_lines = dict() self._data_items = dict() self._data_colors = dict() self._data_visible = DEFAULT_DISPLAY_CURRENCIES self._last_updated = None self.listView = QTableView() self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(["Currency", "Rate"]) self.model.itemChanged.connect(self.check_check_state) self.listView.setModel(self.model) self.threadpool = QThreadPool() self.worker = False layout.addWidget(self.ax) layout.addWidget(self.listView) widget = QWidget() widget.setLayout(layout) self.setCentralWidget(widget) self.listView.setFixedSize(226, 400) self.setFixedSize(650, 400) toolbar = QToolBar("Main") self.addToolBar(toolbar) self.currencyList = QComboBox() toolbar.addWidget(self.currencyList) self.update_currency_list(DEFAULT_DISPLAY_CURRENCIES) self.currencyList.setCurrentText(self.base_currency) self.currencyList.currentTextChanged.connect(self.change_base_currency) self.progress = QProgressBar() self.progress.setRange(0, 100) toolbar.addWidget(self.progress) self.refresh_historic_rates() self.setWindowTitle("Doughnut") self.show() def update_currency_list(self, currencies): for currency in currencies: if self.currencyList.findText(currency) == -1: self.currencyList.addItem(currency) self.currencyList.model().sort(0) def check_check_state(self, i): if not i.isCheckable(): # Skip data columns. return currency = i.text() checked = i.checkState() == Qt.Checked if currency in self._data_visible: if not checked: self._data_visible.remove(currency) self.redraw() else: if checked: self._data_visible.append(currency) self.redraw() def get_currency_color(self, currency): if currency not in self._data_colors: self._data_colors[currency] = next(BREWER12PAIRED) return self._data_colors[currency] def add_data_row(self, currency): citem = QStandardItem() citem.setText(currency) citem.setForeground(QBrush(QColor(self.get_currency_color(currency)))) citem.setColumnCount(2) citem.setCheckable(True) if currency in DEFAULT_DISPLAY_CURRENCIES: citem.setCheckState(Qt.Checked) vitem = QStandardItem() vitem.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) self.model.setColumnCount(2) self.model.appendRow([citem, vitem]) self.model.sort(0) return citem, vitem def get_or_create_data_row(self, currency): if currency not in self._data_items: self._data_items[currency] = self.add_data_row(currency) return self._data_items[currency] def mouse_move_handler(self, pos): pos = self.ax.getViewBox().mapSceneToView(pos) self.line.setPos(pos.x()) self.update_data_viewer(int(pos.x())) def update_data_row(self, currency, value): citem, vitem = self.get_or_create_data_row(currency) vitem.setText("%.4f" % value) def update_data_viewer(self, d): try: data = self.data[d] except IndexError: # Skip update if out of bounds. return if not data: # Skip update if we have no data. return for k, v in data.items(): self.update_data_row(k, v) def change_base_currency(self, currency): self.base_currency = currency self.refresh_historic_rates() def refresh_historic_rates(self): if self.worker: # If we have a current worker, send a kill signal self.worker.signals.cancel.emit() # Prefill our data store with None ('no data') self.data = [None] * HISTORIC_DAYS_N self.worker = UpdateWorker(self.base_currency) # Handle callbacks with data and trigger refresh. self.worker.signals.data.connect(self.result_data_callback) self.worker.signals.finished.connect(self.refresh_finished) self.worker.signals.progress.connect(self.progress_callback) self.threadpool.start(self.worker) def result_data_callback(self, n, rates): self.data[n] = rates # Refresh plot if we haven't for >1 second. if (self._last_updated is None or self._last_updated < datetime.now() - timedelta(seconds=1)): self.redraw() self._last_updated = datetime.now() def progress_callback(self, progress): self.progress.setValue(progress) def refresh_finished(self): self.worker = False self.redraw() # Ensure all currencies we know about are in the dropdown list now. self.update_currency_list(self._data_items.keys()) def redraw(self): """ Process data from store and prefer to draw. :return: """ today = date.today() plotd = defaultdict(list) x_ticks = [] tick_step_size = HISTORIC_DAYS_N / 6 # Pre-process data into lists of x, y values for n, data in enumerate(self.data): if data: for currency, v in data.items(): plotd[currency].append((-n, v)) when = today - timedelta(days=n) if (n - tick_step_size // 2) % tick_step_size == 0: x_ticks.append((-n, when.strftime('%d-%m'))) # Update the plot keys = sorted(plotd.keys()) y_min, y_max = sys.maxsize, 0 for currency in keys: x, y = zip(*plotd[currency]) if currency in self._data_visible: y_min = min(y_min, *y) y_max = max(y_max, *y) else: x, y = [], [] if currency in self._data_lines: self._data_lines[currency].setData(x, y) else: self._data_lines[currency] = self.ax.plot( x, y, # Unpack a list of tuples into two lists, passed as individual args. pen=pg.mkPen(self.get_currency_color(currency), width=2)) self.ax.setLimits(yMin=y_min * 0.9, yMax=y_max * 1.1) self.ax.getAxis('bottom').setTicks([x_ticks, []])
class mainScreen(QWidget): def __init__(self): QWidget.__init__(self) self.setWindowTitle('PER Lap Timer') self.connectionStatus = 'Disconnected' self.stopEvent = threading.Event() self.lastLapNumber = 0 self.lastLapTime = 0 self.connectionLabel = QLabel(self.connectionStatus) self.connectButton = QPushButton('Connect') self.lapNumberLabel = QLabel('Lap Number') self.lapTimeLabel = QLabel('Lap Time') self.lapNumber = QLabel('') self.lapTime = QLabel('') self.filler = QLabel(' ') self.portBox = QComboBox() self.portBox.addItem('Port') self.layout = QGridLayout() self.layout.addWidget(self.connectionLabel, 0, 0, 1, 2) self.layout.addWidget(self.lapNumberLabel, 1, 0, 1, 1) self.layout.addWidget(self.lapTimeLabel, 1, 4, 1, 1) self.layout.addWidget(self.lapNumber, 2, 0, 1, 1) self.layout.addWidget(self.lapTime, 2, 4, 1, 1) self.layout.addWidget(self.filler, 3, 0, 1, 4) self.layout.addWidget(self.portBox, 5, 0, 1, 3) self.layout.addWidget(self.connectButton, 5, 4, 1, 2) self.setLayout(self.layout) self.connectButton.clicked.connect(self.connect) self.portBox.activated.connect(self.comboHandler) self.findThread = threading.Thread(name='portFind', target=self.findPorts) self.findThread.setDaemon(True) self.findThread.start() def findPorts(self): while True: oldPorts = [] if sys.platform.startswith('win'): ports = ['COM%s' % (i + 1) for i in range(256)] elif sys.platform.startswith('linux') or sys.platform.startswith( 'cygwin'): ports = glob.glob('/dev/tty[A-Za-z]*') elif sys.platform.startswith('darwin'): ports = glob.glob('/dev/tty.*') else: raise EnvironmentError('Unsupported platform') if oldPorts != ports: self.portBox.clear() self.portBox.addItem('Port') for port in ports: self.portBox.addItem(port) try: index = self.portBox.findText(self.port) except: index = 0 self.portBox.setCurrentIndex(index) oldPorts = ports time.sleep(5) def setStatusLabel(self): self.connectionLabel.setText(self.connectionStatus) def connect(self): try: self.serialStream = serial.Serial(self.port, 9600, timeout=10) except: self.connectionLabel.setText('Could not connect to lap timer!') t = Timer(3, self.setStatusLabel) t.start() return None self.connectionStatus = 'Connected' self.connectionLabel.setText(self.connectionStatus) self.connectButton.setText(' Stop ') self.connectButton.clicked.connect(self.stop) self.stopEvent.clear() self.daemonRead = threading.Thread(name='serialRead', target=logSerial, args=(self, )) self.daemonRead.setDaemon(True) self.daemonRead.start() def stop(self): self.stopEvent.set() try: self.serialStream.close() except: None self.connectionStatus = 'Disconnected' self.connectionLabel.setText('Device disconnected') self.connectButton.setText('Connect') self.connectButton.clicked.connect(self.connect) t = Timer(3, self.setStatusLabel) t.start() def comboHandler(self, index): self.port = self.portBox.itemText(index)
class MainWidget(QWidget): # CONSTANTS EMPTY_STRING = "" # TOGGLE BUTTON CONSTANTS BUTTON_CLOSED_ICON_PATH = 'resources/menu_closed_button_icon.png' BUTTON_OPENED_ICON_PATH = 'resources/menu_opened_button_icon.png' TOOGLE_BUTTON_CSS_PROPERTY = "QPushButton {background-color: rgb(1,150,250); border: none ; qproperty-iconSize: 80px}" CURRENT_PATH_LINE_EDIT_PROPERTY = "QLineEdit {background-color: rgba(240, 240, 240, 1);}" TOOGLE_CLOSED_COLOR = "background-color: rgb(1,150,250);" TOOGLE_OPENED_COLOR = "background-color: rgba(44, 53, 57, 0.2);" WHITE = "background-color: rgb(255, 255, 255);" FRAME_BUTTON_STYLE = "background-color: rgb(0, 191, 255); color: rgb(255, 255, 255);" FRAME_LINE_EDIT_STYLE = "background-color: rgb(255, 255, 255); color: rgb(0, 0, 0);" FRAME_LABEL_STYLE = "QLabel { background-color : rgba(44, 53, 57, 0); }" SEARCH_ICON_STYLE = "QPushButton {background-color: rgb(1,150,250);}" BACK_ICON_STYLE = "QPushButton {background-color: rgb(1,150,250);} QPushButton:pressed {background-color: rgb(40,170,250);}" WIDTH = 1440 HEIGHT = 900 # Size of the operations widget buttons OPERATION_BUTTON_WIDTH = 40 OPERATION_BUTTON_HEIGHT = 40 FILES_LIST_VIEW_WIDTH = 820 FILES_LIST_VIEW_HEIGHT = 680 TOGGLE_BUTTON_ANIMATION_DURATION = 200 RIGHT_MENU_ANIMATION_DURATION = 250 # ICONS PATHS # Actions icons ADD_FILE_ICON_PATH = "resources/add_file_icon.png" ADD_DIRECTORY_ICON_PATH = "resources/add_directory_icon.png" COPY_ELEMENT_ICON_PATH = "resources/copy_element_icon.png" MOVE_ELEMENT_ICON_PATH = "resources/move_element_icon.png" DELETE_ELEMENT_ICON_PATH = "resources/delete_element_icon.png" SEARCH_ICON_PATH = "resources/search_icon_white.png" CREATE_ARCHIVE_ICON_PATH = "resources/create_archive_icon.png" UNARCHIVE_FILE_ICON_PATH = "resources/unarchive_file_icon.png" RENAME_ELEMENT_ICON_PATH = "resources/rename_icon_white.png" BACK_ICON_PATH = "resources/back_icon.png" # Elements icons FILE_ICON_PATH = "resources/file_icon.png" FOLDER_ICON_PATH = "resources/folder_icon.png" ZIP_ICON_PATH = "resources/zip_icon.png" # Tooltips COPY_TOOLTIP = "Copy files / folders" MOVE_TOOLTIP = "Move files / folders" DELETE_TOOLTIP = "Delete files / folders" ARCHIVE_TOOLTIP = "Archive files / folders" UNARCHIVE_TOOLTIP = "Unarchive files / folders" CREATE_FILE_TOOLTIP = "Create a new text file" CREATE_DIRECTORY_TOOLTIP = "Create a new folder" RENAME_TOOLTIP = "Rename a file / folder" BACK_TOOLTIP = "Move up one directory" # Buttons name COPY = "Copy" MOVE = "Move" DELETE = "Delete" ARCHIVE = "Archive" UNARCHIVE = "Unarchive" RENAME = "Rename" CREATE_FILE = "Create file" CREATE_FOLDER = "Create folder" # Labels SELECTED_ITEMS_TEXT = "Selected items:" ARCHIVE_NAME_TEXT = "Archive name:" CREATE_FILE_NAME_TEXT = "File name:" CREATE_FOLDER_NAME_TEXT = "Folder name:" RENAME_ELEMENT_TEXT = "New name:" def __init__(self, parent, model, controller): super(MainWidget, self).__init__(parent) self._model = model self._controller = controller # FILES LIST VIEW self.filesListView = FilesListView(self.FILES_LIST_VIEW_WIDTH, self.FILES_LIST_VIEW_HEIGHT, self._model, self._controller, self) self.filesListView.move(230, 120) # TREE VIEW FOR SYSTEM DIRECTORY HIERARCHY self.treeView = TreeView(self._model, self._controller, self) self.treeView.setFixedSize(200, 820) self.treeView.move(0, 60) # DISC SELECTION COMBO BOX self.discSelectionComboBox = QComboBox(self) self.discSelectionComboBox.setFixedSize(100, 30) self.discSelectionComboBox.move(230, 60) self.discSelectionComboBox.addItems(self._model.combo_box_list) # CONNECT WIDGETS TO CONTROLLER self.discSelectionComboBox.currentTextChanged.connect( self._controller.change_combo_box_selection) # LISTEN FOR MODEL EVENT SIGNALS self._model.combo_box_selection_changed.connect( self.on_combo_box_selection_changed) self.frameRightMenu = QWidget(self) self.frameRightMenu.setFixedSize(360, self.HEIGHT) self.frameRightMenu.move(1450, 15) self.frameRightMenu.setStyleSheet(self.TOOGLE_CLOSED_COLOR) # BUTTON WHICH TOOGLE ON/OFF THE TREE VIEW CONTAINING THE SELECTED ITEMS TREE VIEW self.toggleButton = QPushButton(QIcon(self.BUTTON_CLOSED_ICON_PATH), self.EMPTY_STRING, self) self.toggleButton.setFixedSize(100, 70) self.toggleButton.move(1300, 0) self.toggleButton.setStyleSheet(self.TOOGLE_BUTTON_CSS_PROPERTY) self._enable = True self.toggleButton.clicked.connect( lambda: Animations.toggleMenu(self, self._enable)) # Label that shows "Selected items:" self.selectedItemsLabel = QLabel(self.SELECTED_ITEMS_TEXT, self.frameRightMenu) self.selectedItemsLabel.move(100, 20) self.selectedItemsLabel.setStyleSheet(self.FRAME_LABEL_STYLE) # The list with selected elements from FilesListView self.selectedFilesListWidget = QListWidget(self.frameRightMenu) self.selectedFilesListWidget.setFixedSize(320, 500) self.selectedFilesListWidget.move(23, 60) self.selectedFilesListWidget.setStyleSheet(self.WHITE) # ACTIONS BUTTONS # Copy button self.copyElementButton = OperationsWidgetButton( QIcon(self.COPY_ELEMENT_ICON_PATH), self.EMPTY_STRING, self) self.copyElementButton.clicked.connect(self.doCopy) self.copyElementButton.move(230, 0) self.copyElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.copyElementButton.setToolTip(self.COPY_TOOLTIP) # Move button self.moveElementButton = OperationsWidgetButton( QIcon(self.MOVE_ELEMENT_ICON_PATH), self.EMPTY_STRING, self) self.moveElementButton.clicked.connect(self.doMove) self.moveElementButton.move(290, 0) self.moveElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.moveElementButton.setToolTip(self.MOVE_TOOLTIP) # Delete button self.deleteElementButton = OperationsWidgetButton( QIcon(self.DELETE_ELEMENT_ICON_PATH), self.EMPTY_STRING, self) self.deleteElementButton.clicked.connect(self.doDelete) self.deleteElementButton.move(350, 0) self.deleteElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.deleteElementButton.setToolTip(self.DELETE_TOOLTIP) # Archive button self.archiveElementButton = OperationsWidgetButton( QIcon(self.CREATE_ARCHIVE_ICON_PATH), self.EMPTY_STRING, self) self.archiveElementButton.clicked.connect(self.doArchive) self.archiveElementButton.move(410, 0) self.archiveElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.archiveElementButton.setToolTip(self.ARCHIVE_TOOLTIP) # Unarchive button self.unarchiveElementButton = OperationsWidgetButton( QIcon(self.UNARCHIVE_FILE_ICON_PATH), self.EMPTY_STRING, self) self.unarchiveElementButton.clicked.connect(self.doUnarchive) self.unarchiveElementButton.move(470, 0) self.unarchiveElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.unarchiveElementButton.setToolTip(self.UNARCHIVE_TOOLTIP) # Add file button self.addFileButton = OperationsWidgetButton( QIcon(self.ADD_FILE_ICON_PATH), self.EMPTY_STRING, self) self.addFileButton.clicked.connect(self.doCreateFile) self.addFileButton.move(530, 0) self.addFileButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.addFileButton.setToolTip(self.CREATE_FILE_TOOLTIP) # Add directory button self.addDirectoryButton = OperationsWidgetButton( QIcon(self.ADD_DIRECTORY_ICON_PATH), self.EMPTY_STRING, self) self.addDirectoryButton.clicked.connect(self.doCreateFolder) self.addDirectoryButton.move(590, 0) self.addDirectoryButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.addDirectoryButton.setToolTip(self.CREATE_DIRECTORY_TOOLTIP) # Move button self.renameElementButton = OperationsWidgetButton( QIcon(self.RENAME_ELEMENT_ICON_PATH), self.EMPTY_STRING, self) self.renameElementButton.clicked.connect(self.doRename) self.renameElementButton.move(650, 0) self.renameElementButton.setFixedSize(self.OPERATION_BUTTON_WIDTH, self.OPERATION_BUTTON_HEIGHT) self.renameElementButton.setToolTip(self.RENAME_TOOLTIP) # Text field needed for displaying the current path self.currentPathLineEdit = QLineEdit(self.EMPTY_STRING, self) self.currentPathLineEdit.move(350, 80) self.currentPathLineEdit.setFixedSize(400, 27) self.currentPathLineEdit.setStyleSheet( self.CURRENT_PATH_LINE_EDIT_PROPERTY) self.currentPathLineEdit.setReadOnly(True) self.currentPathLineEdit.setText(self._model.current_path) self.currentPathLineEdit.textChanged.connect( self._controller.change_current_path_text) self._model.current_path_changed.connect(self.on_current_path_changed) # Text field needed for searching file / folder by the name self.searchLineEdit = QLineEdit(self.EMPTY_STRING, self) self.searchLineEdit.move(810, 80) self.searchLineEdit.setFixedSize(200, 27) self.searchLineEdit.textChanged.connect( self._controller.change_search_text) self._model.current_search_changed.connect( self.on_current_search_changed) self.searchIconButton = OperationsWidgetButton( QIcon(self.SEARCH_ICON_PATH), self.EMPTY_STRING, self) self.searchIconButton.move(1015, 78) self.searchIconButton.setFixedSize(30, 30) self.searchIconButton.setStyleSheet(self.SEARCH_ICON_STYLE) self.searchIconButton.setEnabled(False) self.backButton = OperationsWidgetButton(QIcon(self.BACK_ICON_PATH), self.EMPTY_STRING, self) self.backButton.clicked.connect(self.doBack) self.backButton.move(765, 80) self.backButton.setFixedSize(25, 25) self.backButton.setStyleSheet(self.BACK_ICON_STYLE) self.backButton.setToolTip(self.BACK_TOOLTIP) self.backButton.setEnabled(True) # ACTIONS LABELS self.archiveLabel = QLabel(self.ARCHIVE_NAME_TEXT, self.frameRightMenu) self.createFolderLabel = QLabel(self.CREATE_FOLDER_NAME_TEXT, self.frameRightMenu) self.createFileLabel = QLabel(self.CREATE_FILE_NAME_TEXT, self.frameRightMenu) self.renameElementLabel = QLabel(self.RENAME_ELEMENT_TEXT, self.frameRightMenu) # ACTIONS LINE EDITS self.archiveNameEditLine = QLineEdit(self.EMPTY_STRING, self.frameRightMenu) self.createFileEditLine = QLineEdit(self.EMPTY_STRING, self.frameRightMenu) self.createFolderEditLine = QLineEdit(self.EMPTY_STRING, self.frameRightMenu) self.renameElementEditLine = QLineEdit(self.EMPTY_STRING, self.frameRightMenu) # ACTIONS BUTTONS self.copyButton = ToggleMenuButton(self.COPY, self.frameRightMenu) self.moveButton = ToggleMenuButton(self.MOVE, self.frameRightMenu) self.deleteButton = ToggleMenuButton(self.DELETE, self.frameRightMenu) self.archiveButton = ToggleMenuButton(self.ARCHIVE, self.frameRightMenu) self.unarchiveButton = ToggleMenuButton(self.UNARCHIVE, self.frameRightMenu) self.createFileButton = ToggleMenuButton(self.CREATE_FILE, self.frameRightMenu) self.createFolderButton = ToggleMenuButton(self.CREATE_FOLDER, self.frameRightMenu) self.renameElementButton = ToggleMenuButton(self.RENAME, self.frameRightMenu) self.__createFrameLayoutElements() @Slot(str) def on_current_path_changed(self, path): self._model.elements_list.clear() self._model.selected_items.clear() self.selectedFilesListWidget.clear() self._model.elements_list = Directory.get_all_subelements(path) self.filesListView.model().populateList() @Slot(str) def on_current_search_changed(self, name): print(name) def removeFromSelectedItems(self, item): Animations.toggleMenu(self, True) count = self.selectedFilesListWidget.count() index = 0 for i in range(count): if item.text() == self.selectedFilesListWidget.item(i).text(): index = i break self.selectedFilesListWidget.takeItem(index) def appendToSelectedItems(self, item): Animations.toggleMenu(self, True) listItem = ListWidgetItem(item) self.selectedFilesListWidget.insertItem(0, listItem) def setCurrentPath(self, path): self.currentPathLineEdit.setText(path) @Slot(str) def on_combo_box_selection_changed(self, value): index = self.discSelectionComboBox.findText(value) self.discSelectionComboBox.setCurrentIndex(index) Slot() def doBack(self): path = self.currentPathLineEdit.text() parentPath = os.path.dirname(path) self.setCurrentPath(parentPath) @Slot() def doCopy(self): self.__setCopyLayout(True) Animations.toggleMenu(self, True) @Slot() def doMove(self): self.__setMoveLayout(True) Animations.toggleMenu(self, True) @Slot() def doDelete(self): self.__setDeleteLayout(True) Animations.toggleMenu(self, True) @Slot() def doArchive(self): self.__setArchiveLayout(True) Animations.toggleMenu(self, True) @Slot() def doUnarchive(self): self.__setUnarchiveLayout(True) Animations.toggleMenu(self, True) @Slot() def doCreateFile(self): self.__setCreateFileLayout(True) Animations.toggleMenu(self, True) @Slot() def doCreateFolder(self): self.__setCreateFolderLayout(True) Animations.toggleMenu(self, True) @Slot() def doRename(self): self.__setRenameLayout(True) Animations.toggleMenu(self, True) # Method that creates all layout object from the frame layout def __createFrameLayoutElements(self): # ================= COPY ACTION ======================= # Copy button on right frame self.copyButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.copyButton.move(120, 600) self.copyButton.setFixedSize(120, 32) self.copyButton.setVisible(False) # ================= MOVE ACTION ======================= # Move button on right frame self.moveButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.moveButton.move(120, 600) self.moveButton.setFixedSize(120, 32) self.moveButton.setVisible(False) # ================= DELETE ACTION ======================= # Delete button on right frame self.deleteButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.deleteButton.move(120, 600) self.deleteButton.setFixedSize(120, 32) self.deleteButton.setVisible(False) # ================= UNARCHIVE ACTION ======================= # Unarchive button on right frame self.unarchiveButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.unarchiveButton.move(120, 600) self.unarchiveButton.setFixedSize(120, 32) self.unarchiveButton.setVisible(False) # ================= ARCHIVE ACTION ======================= self.archiveLabel.move(100, 590) self.archiveLabel.setStyleSheet(self.FRAME_LABEL_STYLE) self.archiveLabel.setVisible(False) self.archiveNameEditLine.move(55, 620) self.archiveNameEditLine.setFixedSize(250, 30) self.archiveNameEditLine.setStyleSheet(self.FRAME_LINE_EDIT_STYLE) self.archiveNameEditLine.setVisible(False) self.archiveButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.archiveButton.move(120, 700) self.archiveButton.setFixedSize(120, 32) self.archiveButton.setVisible(False) # ================= CREATE NEW FILE ACTION ======================= self.createFileLabel.move(120, 590) self.createFileLabel.setStyleSheet(self.FRAME_LABEL_STYLE) self.createFileLabel.setVisible(False) self.createFileEditLine.move(55, 620) self.createFileEditLine.setFixedSize(250, 30) self.createFileEditLine.setStyleSheet(self.FRAME_LINE_EDIT_STYLE) self.createFileEditLine.setVisible(False) self.createFileButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.createFileButton.move(120, 700) self.createFileButton.setFixedSize(120, 32) self.createFileButton.setVisible(False) # ================= CREATE NEW FOLDER ACTION ======================= self.createFolderLabel.move(120, 590) self.createFolderLabel.setStyleSheet(self.FRAME_LABEL_STYLE) self.createFolderLabel.setVisible(False) self.createFolderEditLine.move(55, 620) self.createFolderEditLine.setFixedSize(250, 30) self.createFolderEditLine.setStyleSheet(self.FRAME_LINE_EDIT_STYLE) self.createFolderEditLine.setVisible(False) self.createFolderButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.createFolderButton.move(120, 700) self.createFolderButton.setFixedSize(120, 32) self.createFolderButton.setVisible(False) # ================= RENAME ELEMENT ACTION ======================= self.renameElementLabel.move(120, 590) self.renameElementLabel.setStyleSheet(self.FRAME_LABEL_STYLE) self.renameElementLabel.setVisible(False) self.renameElementEditLine.move(55, 620) self.renameElementEditLine.setFixedSize(250, 30) self.renameElementEditLine.setStyleSheet(self.FRAME_LINE_EDIT_STYLE) self.renameElementEditLine.setVisible(False) self.renameElementButton.setStyleSheet(self.FRAME_BUTTON_STYLE) self.renameElementButton.move(120, 700) self.renameElementButton.setFixedSize(120, 32) self.renameElementButton.setVisible(False) # Method that sets the frame layout for rename operation def __setRenameLayout(self, shown): if shown == True: self.renameElementLabel.setVisible(True) self.renameElementEditLine.setVisible(True) self.renameElementButton.setVisible(True) self.__setCopyLayout(False) self.__setDeleteLayout(False) self.__setMoveLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.renameElementLabel.setVisible(False) self.renameElementEditLine.setVisible(False) self.renameElementButton.setVisible(False) # Method that sets the frame layout for archive operation def __setCreateFileLayout(self, shown): if shown == True: self.createFileLabel.setVisible(True) self.createFileEditLine.setVisible(True) self.createFileButton.setVisible(True) self.__setCopyLayout(False) self.__setDeleteLayout(False) self.__setMoveLayout(False) self.__setRenameLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.createFileLabel.setVisible(False) self.createFileEditLine.setVisible(False) self.createFileButton.setVisible(False) # Method that sets the frame layout for archive operation def __setCreateFolderLayout(self, shown): if shown == True: self.createFolderLabel.setVisible(True) self.createFolderEditLine.setVisible(True) self.createFolderButton.setVisible(True) self.__setCopyLayout(False) self.__setDeleteLayout(False) self.__setMoveLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.createFolderLabel.setVisible(False) self.createFolderEditLine.setVisible(False) self.createFolderButton.setVisible(False) # Method that sets the frame layout for archive operation def __setArchiveLayout(self, shown): if shown == True: self.archiveLabel.setVisible(True) self.archiveNameEditLine.setVisible(True) self.archiveButton.setVisible(True) self.__setCopyLayout(False) self.__setDeleteLayout(False) self.__setMoveLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setUnarchiveLayout(False) else: self.archiveLabel.setVisible(False) self.archiveNameEditLine.setVisible(False) self.archiveButton.setVisible(False) # Method that sets the frame layout for copy operation def __setCopyLayout(self, shown): if shown == True: self.copyButton.setVisible(True) self.__setMoveLayout(False) self.__setDeleteLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.copyButton.setVisible(False) # Method that sets the frame layout for move operation def __setMoveLayout(self, shown): if shown == True: self.moveButton.setVisible(True) self.__setDeleteLayout(False) self.__setCopyLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.moveButton.setVisible(False) # Method that sets the frame layout for delete operation def __setDeleteLayout(self, shown): if shown == True: self.deleteButton.setVisible(True) self.__setCopyLayout(False) self.__setMoveLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setUnarchiveLayout(False) else: self.deleteButton.setVisible(False) # Method that sets the frame layout for unarchive operation def __setUnarchiveLayout(self, shown): if shown == True: self.unarchiveButton.setVisible(True) self.__setCopyLayout(False) self.__setMoveLayout(False) self.__setRenameLayout(False) self.__setCreateFileLayout(False) self.__setCreateFolderLayout(False) self.__setArchiveLayout(False) self.__setDeleteLayout(False) else: self.unarchiveButton.setVisible(False)