def _on_patch_checkbox_changed(self, field_name: str, checkbox: QCheckBox, value: int): with self._editor as editor: patch_configuration = editor.configuration.patches new_config = dataclasses.replace( patch_configuration, **{field_name: checkbox.isChecked()}) editor.set_configuration_field("patches", new_config)
class OptionalCurrencyComboBox(QWidget): changed = Signal() name_updated = Signal(str) def __init__(self, parent): QWidget.__init__(self, parent) self._id = 0 self.layout = QHBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.null_flag = QCheckBox(parent) self.null_flag.setChecked(False) self.null_flag.setText(self.tr("Currency")) self.layout.addWidget(self.null_flag) self.currency = CurrencyComboBox(parent) self.currency.setEnabled(False) self.layout.addWidget(self.currency) self.setLayout(self.layout) self.setFocusProxy(self.null_flag) self.null_flag.clicked.connect(self.onClick) self.currency.changed.connect(self.onCurrencyChange) def setText(self, text): self.null_flag.setText(text) def getId(self): return self._id if self._id else None def setId(self, new_value): if self._id == new_value: return self._id = new_value self.updateView() name = JalDB().get_asset_name(self._id) self.name_updated.emit('' if name is None else name) currency_id = Property(int, getId, setId, notify=changed, user=True) def updateView(self): has_value = True if self._id else False if has_value: self.currency.selected_id = self._id self.null_flag.setChecked(has_value) self.currency.setEnabled(has_value) @Slot() def onClick(self): if self.null_flag.isChecked(): if self.currency.selected_id == 0: self.currency.selected_id = JalSettings().getValue('BaseCurrency') self.currency_id = self.currency.selected_id else: self.currency_id = 0 self.changed.emit() @Slot() def onCurrencyChange(self, _id): self.currency_id = self.currency.selected_id self.changed.emit()
def _on_check_world(c: QtWidgets.QCheckBox, _): if not self.during_batch_check_update: world_list = self.game_description.world_list w = world_list.world_with_name(c.world_name) world_areas = [ identifier for a in w.areas if c.is_dark_world == a.in_dark_aether if (identifier := world_list.identifier_for_area(a) ) in checks_for_area ] on_check(world_areas, c.isChecked())
class InterpolateBadsDialog(QDialog): def __init__(self, parent): super().__init__(parent) self.setWindowTitle("Interpolate bad channels") vbox = QVBoxLayout(self) grid = QGridLayout() grid.addWidget(QLabel("Reset bads:"), 0, 0) self.reset_bads_checkbox = QCheckBox() self.reset_bads_checkbox.setChecked(True) grid.addWidget(self.reset_bads_checkbox, 0, 1) grid.addWidget(QLabel("Mode:"), 1, 0) self.mode_select = QComboBox() self.modes = {"Accurate": "accurate", "Fast": "fast"} self.mode_select.addItems(self.modes.keys()) self.mode_select.setCurrentText("Accurate") grid.addWidget(self.mode_select, 1, 1) grid.addWidget(QLabel("Origin (x, y, z):"), 2, 0) hbox = QHBoxLayout() self.x = QDoubleSpinBox() self.x.setValue(0) self.x.setDecimals(3) hbox.addWidget(self.x) self.y = QDoubleSpinBox() self.y.setValue(0) self.y.setDecimals(3) hbox.addWidget(self.y) self.z = QDoubleSpinBox() self.z.setValue(0.04) self.z.setDecimals(3) hbox.addWidget(self.z) grid.addLayout(hbox, 2, 1) vbox.addLayout(grid) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(buttonbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) @property def origin(self): x = float(self.x.value()) y = float(self.y.value()) z = float(self.z.value()) return x, y, z @property def mode(self): return self.mode_select.currentText() @property def reset_bads(self): return self.reset_bads_checkbox.isChecked()
def on_click_set_part(self, ent_part: QLineEdit, combo_1: QComboBox, check: QCheckBox, ent_desc: QLineEdit, combo_2: QComboBox): # obtain part number num_part = ent_part.text() ent_part.setText(None) # obtain original part number if selected id_part_orig = 'NULL' if combo_1.isEnabled(): num_part_org = combo_1.currentText() sql1 = self.db.sql( "SELECT id_part FROM part WHERE num_part = '?';", [num_part_org]) print(sql1) out = self.db.get(sql1) for id in out: id_part_orig = id[0] combo_1.clear() combo_1.clearEditText() combo_1.setEnabled(False) # clear QCheckBox if check.isChecked(): check.setEnabled(False) # obtain part description description = ent_desc.text() ent_desc.setText(None) # obtain id_supplier from selected supplier on the QComboBox supplier = combo_2.currentText() sql2 = self.db.sql( "SELECT id_supplier FROM supplier WHERE name_supplier_short = '?';", [supplier]) print(sql2) out = self.db.get(sql2) for id in out: id_supplier = id[0] print(num_part) print(description) print(supplier) print(id_supplier) # insert new part to part table sql3 = self.db.sql( "INSERT INTO part VALUES(NULL, ?, ?, '?', '?', NULL, NULL);", [id_part_orig, id_supplier, num_part, description]) print(sql3) self.db.put(sql3)
class FindToolBar(QToolBar): find = QtCore.Signal(str, QWebEnginePage.FindFlags) def __init__(self): super(FindToolBar, self).__init__() self._line_edit = QLineEdit() self._line_edit.setClearButtonEnabled(True) self._line_edit.setPlaceholderText("Find...") self._line_edit.setMaximumWidth(300) self._line_edit.returnPressed.connect(self._find_next) self.addWidget(self._line_edit) self._previous_button = QToolButton() style_icons = ':/qt-project.org/styles/commonstyle/images/' self._previous_button.setIcon(QIcon(style_icons + 'up-32.png')) self._previous_button.clicked.connect(self._find_previous) self.addWidget(self._previous_button) self._next_button = QToolButton() self._next_button.setIcon(QIcon(style_icons + 'down-32.png')) self._next_button.clicked.connect(self._find_next) self.addWidget(self._next_button) self._case_sensitive_checkbox = QCheckBox('Case Sensitive') self.addWidget(self._case_sensitive_checkbox) self._hideButton = QToolButton() self._hideButton.setShortcut(QKeySequence(Qt.Key_Escape)) self._hideButton.setIcon(QIcon(style_icons + 'closedock-16.png')) self._hideButton.clicked.connect(self.hide) self.addWidget(self._hideButton) def focus_find(self): self._line_edit.setFocus() def _emit_find(self, backward): needle = self._line_edit.text().strip() if needle: flags = QWebEnginePage.FindFlags() if self._case_sensitive_checkbox.isChecked(): flags |= QWebEnginePage.FindCaseSensitively if backward: flags |= QWebEnginePage.FindBackward self.find.emit(needle, flags) def _find_next(self): self._emit_find(False) def _find_previous(self): self._emit_find(True)
class TriggerDialog(QDialog): def __init__(self, parent, trigger_data): super().__init__(parent) self.trigger_data = trigger_data self.tag = self.trigger_data.get('Tag') self.uuid = str(uuid4()) self.path = self.parent().path / 'img' self.name = QLineEdit(self.trigger_data.get('Name', '')) self.search_text = QLineEdit(self.trigger_data.get('SearchText', '')) self.alert_text = QLineEdit(self.trigger_data.get('AlertText', '')) self.use_audio = QCheckBox() self.use_audio.setChecked( eval(self.trigger_data.get('UseAudio', 'False'))) self.button_box = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Save) self.button_box.accepted.connect(self.save) self.button_box.rejected.connect(self.reject) self.create_gui() def create_gui(self): self.setWindowTitle('Trigger Editor') icon_path = self.path / 'trigger.png' icon = QIcon(str(icon_path.resolve())) self.setWindowIcon(icon) form_layout = QFormLayout() form_layout.addRow('Trigger Name:', self.name) form_layout.addRow('Search Text:', self.search_text) form_layout.addRow('Alert Text:', self.alert_text) form_layout.addRow('Use Audio:', self.use_audio) form_layout.addRow(self.button_box) self.setLayout(form_layout) self.open() @Slot() def save(self): self.trigger_data = { 'Tag': self.tag, 'Name': self.name.text(), 'SearchText': self.search_text.text(), 'AlertText': self.alert_text.text(), 'UseAudio': str(self.use_audio.isChecked()), 'id': self.uuid } self.accept()
class CheckableImageEntry(ImageEntry): """ Extends ImageEntry. An ImageEntry with a checkbox under the thumbnail. """ state_changed = Signal(int) def __init__(self, parent: QWidget, image: QImage, name: str, image_path: Optional[str] = None, array_path: Optional[str] = None, default_check: bool = True): """ Initialize a CheckableImageEntry :param QWidget parent: The parent containing the ImageEntry. :param QImage image: The image that will be draw in the thumbnail. :param str name: The name that will be shown below the thumbnail. Also used for image basename. :param image_path: Path to the image file. :type image_path: str, optional :param array_path: Path to the Numpy array file. :type array_path: str, optional :param bool default_check: If True, the checkbox will be checked. """ super(CheckableImageEntry, self).__init__(parent, image, name, image_path, array_path) self.__check_box = QCheckBox(self) self.__check_box.setSizePolicy( QSizePolicy.Preferred, QSizePolicy.Minimum) self.__check_box.setChecked(default_check) self.__check_box.stateChanged.connect(self.state_changed) self.layout().insertWidget(1, self.__check_box, alignment=Qt.AlignHCenter) def isChecked(self) -> bool: """ :return: `True` if checkbox is checked, `False` otherwise. :rtype: bool """ return self.__check_box.isChecked() def setChecked(self, checked) -> None: """ Changes the entry checkbox state :param checked: If `True`, set the checkbox to checked, otherwise set to unchecked. """ self.__check_box.setChecked(checked)
def _dismissableMessage(parent: Optional[QWidget], message: str, icon: QIcon, buttons: QMessageBox.StandardButton, dismissed_messages: MutableMapping[ str, QMessageBox.StandardButton]) -> QMessageBox.StandardButton: if message in dismissed_messages: return dismissed_messages[message] message_box = QMessageBox(icon, QApplication.applicationName(), message, buttons, parent) checkbox = QCheckBox(message_box) checkbox.setText('Don\'t warn again') message_box.setCheckBox(checkbox) button = message_box.exec() if checkbox.isChecked(): dismissed_messages[message] = button return button
class Window(QWidget): def __init__(self): super(Window, self).__init__() self.proxyModel = QSortFilterProxyModel() self.proxyModel.setDynamicSortFilter(True) self.sourceGroupBox = QGroupBox("Original Model") self.proxyGroupBox = QGroupBox("Sorted/Filtered Model") self.sourceView = QTreeView() self.sourceView.setRootIsDecorated(False) self.sourceView.setAlternatingRowColors(True) self.proxyView = QTreeView() self.proxyView.setRootIsDecorated(False) self.proxyView.setAlternatingRowColors(True) self.proxyView.setModel(self.proxyModel) self.proxyView.setSortingEnabled(True) self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting") self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter") self.filterPatternLineEdit = QLineEdit() self.filterPatternLineEdit.setClearButtonEnabled(True) self.filterPatternLabel = QLabel("&Filter pattern:") self.filterPatternLabel.setBuddy(self.filterPatternLineEdit) self.filterSyntaxComboBox = QComboBox() self.filterSyntaxComboBox.addItem("Regular expression", REGULAR_EXPRESSION) self.filterSyntaxComboBox.addItem("Wildcard", WILDCARD) self.filterSyntaxComboBox.addItem("Fixed string", FIXED_STRING) self.filterSyntaxLabel = QLabel("Filter &syntax:") self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox) self.filterColumnComboBox = QComboBox() self.filterColumnComboBox.addItem("Subject") self.filterColumnComboBox.addItem("Sender") self.filterColumnComboBox.addItem("Date") self.filterColumnLabel = QLabel("Filter &column:") self.filterColumnLabel.setBuddy(self.filterColumnComboBox) self.filterPatternLineEdit.textChanged.connect(self.filterRegExpChanged) self.filterSyntaxComboBox.currentIndexChanged.connect(self.filterRegExpChanged) self.filterColumnComboBox.currentIndexChanged.connect(self.filterColumnChanged) self.filterCaseSensitivityCheckBox.toggled.connect(self.filterRegExpChanged) self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged) sourceLayout = QHBoxLayout() sourceLayout.addWidget(self.sourceView) self.sourceGroupBox.setLayout(sourceLayout) proxyLayout = QGridLayout() proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3) proxyLayout.addWidget(self.filterPatternLabel, 1, 0) proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2) proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0) proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2) proxyLayout.addWidget(self.filterColumnLabel, 3, 0) proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2) proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2) proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2) self.proxyGroupBox.setLayout(proxyLayout) mainLayout = QVBoxLayout() mainLayout.addWidget(self.sourceGroupBox) mainLayout.addWidget(self.proxyGroupBox) self.setLayout(mainLayout) self.setWindowTitle("Basic Sort/Filter Model") self.resize(500, 450) self.proxyView.sortByColumn(1, Qt.AscendingOrder) self.filterColumnComboBox.setCurrentIndex(1) self.filterPatternLineEdit.setText("Andy|Grace") self.filterCaseSensitivityCheckBox.setChecked(True) self.sortCaseSensitivityCheckBox.setChecked(True) def setSourceModel(self, model): self.proxyModel.setSourceModel(model) self.sourceView.setModel(model) def filterRegExpChanged(self): syntax_nr = self.filterSyntaxComboBox.currentData() pattern = self.filterPatternLineEdit.text() if syntax_nr == WILDCARD: pattern = QRegularExpression.wildcardToRegularExpression(pattern) elif syntax_nr == FIXED_STRING: pattern = QRegularExpression.escape(pattern) regExp = QRegularExpression(pattern) if not self.filterCaseSensitivityCheckBox.isChecked(): options = regExp.patternOptions() options |= QRegularExpression.CaseInsensitiveOption regExp.setPatternOptions(options) self.proxyModel.setFilterRegularExpression(regExp) def filterColumnChanged(self): self.proxyModel.setFilterKeyColumn(self.filterColumnComboBox.currentIndex()) def sortChanged(self): if self.sortCaseSensitivityCheckBox.isChecked(): caseSensitivity = Qt.CaseSensitive else: caseSensitivity = Qt.CaseInsensitive self.proxyModel.setSortCaseSensitivity(caseSensitivity)
def checkbox_changed(self, checkbox: QCheckBox, settings_item): if settings_item.value != checkbox.isChecked(): settings_item.value = checkbox.isChecked() self.settings_changed_signal.emit()
class Config(QWidget): def __init__(self): super().__init__() # global self.config_params self.xml_root = None # self.tab = QWidget() # self.tabs.resize(200,5) #------------------------------------------- label_width = 110 domain_value_width = 100 value_width = 60 label_height = 20 units_width = 70 self.scroll = QScrollArea() # might contain centralWidget self.config_params = QWidget() self.vbox = QVBoxLayout() self.vbox.addStretch(0) #============ Domain ================================ label = QLabel("Domain (micron)") label.setFixedHeight(label_height) label.setStyleSheet("background-color: orange") label.setAlignment(QtCore.Qt.AlignCenter) self.vbox.addWidget(label) hbox = QHBoxLayout() label = QLabel("Xmin") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.xmin = QLineEdit() self.xmin.setFixedWidth(domain_value_width) self.xmin.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.xmin) label = QLabel("Xmax") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.xmax = QLineEdit() self.xmax.setFixedWidth(domain_value_width) self.xmax.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.xmax) label = QLabel("dx") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.xdel = QLineEdit() self.xdel.setFixedWidth(value_width) self.xdel.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.xdel) self.vbox.addLayout(hbox) #---------- hbox = QHBoxLayout() label = QLabel("Ymin") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.ymin = QLineEdit() self.ymin.setFixedWidth(domain_value_width) self.ymin.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.ymin) label = QLabel("Ymax") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.ymax = QLineEdit() self.ymax.setFixedWidth(domain_value_width) self.ymax.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.ymax) label = QLabel("dy") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.ydel = QLineEdit() self.ydel.setFixedWidth(value_width) self.ydel.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.ydel) self.vbox.addLayout(hbox) #---------- hbox = QHBoxLayout() label = QLabel("Zmin") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.zmin = QLineEdit() self.zmin.setFixedWidth(domain_value_width) self.zmin.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.zmin) label = QLabel("Zmax") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.zmax = QLineEdit() self.zmax.setFixedWidth(domain_value_width) self.zmax.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.zmax) label = QLabel("dz") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.zdel = QLineEdit() self.zdel.setFixedWidth(value_width) self.zdel.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.zdel) self.vbox.addLayout(hbox) #---------- hbox = QHBoxLayout() self.virtual_walls = QCheckBox("Virtual walls") # self.motility_enabled.setAlignment(QtCore.Qt.AlignRight) # label.setFixedWidth(label_width) hbox.addWidget(self.virtual_walls) self.vbox.addLayout(hbox) # self.vbox.addWidget(QHLine()) #============ Misc ================================ label = QLabel("Misc runtime parameters") label.setFixedHeight(label_height) label.setStyleSheet("background-color: orange") label.setAlignment(QtCore.Qt.AlignCenter) self.vbox.addWidget(label) hbox = QHBoxLayout() # hbox.setFixedHeight(label_width) label = QLabel("Max Time") # label_width = 210 label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.max_time = QLineEdit() # self.max_time.setFixedWidth(200) self.max_time.setFixedWidth(domain_value_width) self.max_time.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.max_time) label = QLabel("min") label.setFixedWidth(units_width) label.setAlignment(QtCore.Qt.AlignLeft) hbox.addWidget(label) self.vbox.addLayout(hbox) #---------- hbox = QHBoxLayout() label = QLabel("# threads") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.num_threads = QLineEdit() # self.num_threads.setFixedWidth(value_width) self.num_threads.setFixedWidth(domain_value_width) self.num_threads.setValidator(QtGui.QIntValidator()) hbox.addWidget(self.num_threads) label = QLabel(" ") label.setFixedWidth(units_width) label.setAlignment(QtCore.Qt.AlignLeft) hbox.addWidget(label) self.vbox.addLayout(hbox) #------------------ hbox = QHBoxLayout() label = QLabel("Save data:") label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignLeft) hbox.addWidget(label) #------ self.save_svg = QCheckBox("SVG") # self.motility_2D.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(self.save_svg) label = QLabel("every") # label_width = 210 # label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.svg_interval = QLineEdit() self.svg_interval.setFixedWidth(value_width) self.svg_interval.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.svg_interval) label = QLabel("min") # label.setFixedWidth(units_width) label.setAlignment(QtCore.Qt.AlignLeft) hbox.addWidget(label) #------ self.save_full = QCheckBox("Full") # self.motility_2D.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(self.save_full) label = QLabel("every") # label_width = 210 # label.setFixedWidth(label_width) label.setAlignment(QtCore.Qt.AlignRight) hbox.addWidget(label) self.full_interval = QLineEdit() self.full_interval.setFixedWidth(value_width) self.full_interval.setValidator(QtGui.QDoubleValidator()) hbox.addWidget(self.full_interval) label = QLabel("min") # label.setFixedWidth(units_width) label.setAlignment(QtCore.Qt.AlignLeft) hbox.addWidget(label) self.vbox.addLayout(hbox) #============ Cells IC ================================ label = QLabel("Initial conditions of cells (x,y,z, type)") label.setFixedHeight(label_height) label.setStyleSheet("background-color: orange") label.setAlignment(QtCore.Qt.AlignCenter) self.vbox.addWidget(label) self.cells_csv = QCheckBox("config/cells.csv") self.vbox.addWidget(self.cells_csv) #-------------------------- # Dummy widget for filler?? # label = QLabel("") # label.setFixedHeight(1000) # # label.setStyleSheet("background-color: orange") # label.setAlignment(QtCore.Qt.AlignCenter) # self.vbox.addWidget(label) self.vbox.addStretch() #================================================================== self.config_params.setLayout(self.vbox) self.scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.scroll.setWidgetResizable(True) self.scroll.setWidget( self.config_params) # self.config_params = QWidget() self.layout = QVBoxLayout(self) self.layout.addWidget(self.scroll) # @QtCore.Slot() # def save_xml(self): # # self.text.setText(random.choice(self.hello)) # pass def fill_gui(self): self.xmin.setText(self.xml_root.find(".//x_min").text) self.xmax.setText(self.xml_root.find(".//x_max").text) self.xdel.setText(self.xml_root.find(".//dx").text) self.ymin.setText(self.xml_root.find(".//y_min").text) self.ymax.setText(self.xml_root.find(".//y_max").text) self.ydel.setText(self.xml_root.find(".//dy").text) self.zmin.setText(self.xml_root.find(".//z_min").text) self.zmax.setText(self.xml_root.find(".//z_max").text) self.zdel.setText(self.xml_root.find(".//dz").text) self.max_time.setText(self.xml_root.find(".//max_time").text) self.num_threads.setText(self.xml_root.find(".//omp_num_threads").text) self.svg_interval.setText(self.xml_root.find(".//SVG//interval").text) # NOTE: do this *after* filling the mcds_interval, directly above, due to the callback/constraints on them?? if self.xml_root.find(".//SVG//enable").text.lower() == 'true': self.save_svg.setChecked(True) else: self.save_svg.setChecked(False) self.full_interval.setText( self.xml_root.find(".//full_data//interval").text) if self.xml_root.find(".//full_data//enable").text.lower() == 'true': self.save_full.setChecked(True) else: self.save_full.setChecked(False) # Read values from the GUI widgets and generate/write a new XML def fill_xml(self): # pass # self.xmin.setText(self.xml_root.find(".//x_min").text) print("config_tab: fill_xml: xmin=", str(self.xmin.text)) self.xml_root.find(".//x_min").text = self.xmin.text() self.xml_root.find(".//x_max").text = self.xmax.text() self.xml_root.find(".//dx").text = self.xdel.text() self.xml_root.find(".//y_min").text = self.ymin.text() self.xml_root.find(".//y_max").text = self.ymax.text() self.xml_root.find(".//dy").text = self.ydel.text() self.xml_root.find(".//z_min").text = self.zmin.text() self.xml_root.find(".//z_max").text = self.zmax.text() self.xml_root.find(".//dz").text = self.zdel.text() if not self.xml_root.find(".//virtual_wall_at_domain_edge"): # create it? print("config_tab.py: no virtual_wall_at_domain_edge tag") else: if self.virtual_walls.isChecked(): self.xml_root.find( ".//virtual_wall_at_domain_edge").text = 'true' else: self.xml_root.find( ".//virtual_wall_at_domain_edge").text = 'false' self.xml_root.find(".//max_time").text = self.max_time.text() self.xml_root.find(".//omp_num_threads").text = self.num_threads.text() if self.save_svg.isChecked(): self.xml_root.find(".//SVG//enable").text = 'true' else: self.xml_root.find(".//SVG//enable").text = 'false' self.xml_root.find(".//SVG//interval").text = self.svg_interval.text() if self.save_full.isChecked(): self.xml_root.find(".//full_data//enable").text = 'true' else: self.xml_root.find(".//full_data//enable").text = 'false' self.xml_root.find( ".//full_data//interval").text = self.full_interval.text() if self.cells_csv.isChecked(): self.xml_root.find(".//initial_conditions//cell_positions" ).attrib['enabled'] = 'true' else: self.xml_root.find(".//initial_conditions//cell_positions" ).attrib['enabled'] = 'false'
def _on_check_update(self, check: QCheckBox, field_name: str, _): self.preferences = dataclasses.replace( self.preferences, **{field_name: check.isChecked()})
class TableClothGenerator(QMainWindow): def __init__(self, parent=None): super().__init__(parent) # Main UI settings self.setWindowTitle('Tablecloth Generator') self.setWindowIcon(QIcon('icon.ico')) self.centralWidget = QWidget() self.setCentralWidget(self.centralWidget) self.resize(350, 350) self.center() self._createMenuBar() self.MainUI() def MainUI(self): # Obtain the configs fp_config = open(THISDIR + "\\config\\config.json", "r", encoding="utf-8") self.config = json.loads(fp_config.read()) fp_config.close() # Obtain and List the teams fp_teams = open(THISDIR + "\\config\\teams.json", "r", encoding="utf-8") conf_teams = json.loads(fp_teams.read()) fp_teams.close() self.teams = conf_teams["teams"] self.players = conf_teams["players"] # Obtain all images needed to create the tablecloth self.background = Image.open(THISDIR + "\\images\\mat.png") self.table_border = Image.open(THISDIR + "\\images\\table_border.png") self.tech_lines = Image.open(THISDIR + "\\images\\technical_lines.png") # Check if there's no configuration set up # and prompt to create/import one if self.config["total_teams"] == 0: self.no_config = QMessageBox.question(self, "No configuration", "No configuration has been found. Do you wish to set up a new one?", QMessageBox.Yes | QMessageBox.No) if self.no_config == QMessageBox.Yes: self.CreateTeamsWindow() self.bg_image = self.config["image_route"] self.players_combobox = QComboBox() self.UpdatePlayersList() self.players_combobox.setEditable(True) self.players_combobox.completer()\ .setCompletionMode(QCompleter.PopupCompletion) self.players_combobox.setInsertPolicy(QComboBox.NoInsert) # Set up the GUI self.statusBar().showMessage("Remember: Rig responsibly.") # Bottom (EAST) self.label_east = QLabel(self) self.label_east.setText("<h1>East Seat</h1>") self.label_east.setAlignment(QtCore.Qt.AlignCenter) self.image_east = QLabel(self) self.image_east.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.image_east.setAlignment(QtCore.Qt.AlignCenter) self.search_east = QLineEdit() self.search_east.setAlignment(QtCore.Qt.AlignCenter) self.search_east.editingFinished.connect( lambda: self.searchPlayer(self.search_east.text(), self.cloth_east)) self.cloth_east = QComboBox() self.cloth_east.setModel(self.players_combobox.model()) self.cloth_east.currentIndexChanged.connect( lambda: self.SwitchImage(self.cloth_east, self.image_east)) # Right (SOUTH) self.label_south = QLabel(self) self.label_south.setText("<h1>South Seat</h1>") self.label_south.setAlignment(QtCore.Qt.AlignCenter) self.image_south = QLabel(self) self.image_south.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.image_south.setAlignment(QtCore.Qt.AlignCenter) self.image_south.show() self.search_south = QLineEdit() self.search_south.setAlignment(QtCore.Qt.AlignCenter) self.search_south.editingFinished.connect( lambda: self.searchPlayer(self.search_south.text(), self.cloth_south)) self.cloth_south = QComboBox() self.cloth_south.setModel(self.players_combobox.model()) self.cloth_south.currentIndexChanged.connect( lambda: self.SwitchImage(self.cloth_south, self.image_south)) # Top (WEST) self.label_west = QLabel(self) self.label_west.setText("<h1>West Seat</h1>") self.label_west.setAlignment(QtCore.Qt.AlignCenter) self.image_west = QLabel(self) self.image_west.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.image_west.setAlignment(QtCore.Qt.AlignCenter) self.image_west.show() self.cloth_west = QComboBox() self.search_west = QLineEdit() self.search_west.setAlignment(QtCore.Qt.AlignCenter) self.search_west.editingFinished.connect( lambda: self.searchPlayer(self.search_west.text(), self.cloth_west)) self.cloth_west.setModel(self.players_combobox.model()) self.cloth_west.currentIndexChanged.connect( lambda: self.SwitchImage(self.cloth_west, self.image_west)) # Left (NORTH) self.label_north = QLabel(self) self.label_north.setText("<h1>North Seat</h1>") self.label_north.setAlignment(QtCore.Qt.AlignCenter) self.image_north = QLabel(self) self.image_north.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.image_north.setAlignment(QtCore.Qt.AlignCenter) self.image_north.show() self.cloth_north = QComboBox() self.search_north = QLineEdit() self.search_north.setAlignment(QtCore.Qt.AlignCenter) self.search_north.editingFinished.connect( lambda: self.searchPlayer(self.search_north.text(), self.cloth_north)) self.cloth_north.setModel(self.players_combobox.model()) self.cloth_north.currentIndexChanged.connect( lambda: self.SwitchImage(self.cloth_north, self.image_north)) # Technical lines self.technical_lines = QCheckBox("Show Technical lines", self) # Generate button self.generate = QPushButton(self) self.generate.setText("Generate Tablecloth") self.generate.clicked.connect(self.GeneratePreview) # Add custom mat self.custom_mat = QPushButton(self) self.custom_mat.setText("Add Mat") self.custom_mat.clicked.connect(self.MatDialog) # Create the layout grid_layout = QGridLayout() grid_layout.setAlignment(QtCore.Qt.AlignCenter) grid_layout.setAlignment(QtCore.Qt.AlignTop) # Labels East, West grid_layout.addWidget(self.label_east, 1, 1) grid_layout.addWidget(self.label_west, 1, 2) # Image preview East, West grid_layout.addWidget(self.image_east, 2, 1) grid_layout.addWidget(self.image_west, 2, 2) # Search player East, West grid_layout.addWidget(self.search_east, 3, 1) grid_layout.addWidget(self.search_west, 3, 2) # Player combobox East, West grid_layout.addWidget(self.cloth_east, 4, 1) grid_layout.addWidget(self.cloth_west, 4, 2) # Labes South, North grid_layout.addWidget(self.label_south, 5, 1) grid_layout.addWidget(self.label_north, 5, 2) # Image preview South, North grid_layout.addWidget(self.image_south, 6, 1) grid_layout.addWidget(self.image_north, 6, 2) # Search player South, North grid_layout.addWidget(self.search_south, 7, 1) grid_layout.addWidget(self.search_north, 7, 2) # Player combobox South, North grid_layout.addWidget(self.cloth_south, 8, 1) grid_layout.addWidget(self.cloth_north, 8, 2) # Technical lines grid_layout.addWidget(self.technical_lines, 9, 1) # Custom mat/bg grid_layout.addWidget(self.custom_mat, 10, 1) # Generate grid_layout.addWidget(self.generate, 10, 2) self.centralWidget.setLayout(grid_layout) # Create the window self.show() def _createMenuBar(self): # Settings and stuff for the toolbar menubar = QMenuBar(self) file_menu = QMenu("&File", self) file_menu.addAction("Create Team(s)", self.CreateTeamsWindow) file_menu.addAction("Edit Team(s)", self.EditTeamsWindow) file_menu.addAction("Exit", self.close) settings_menu = QMenu("&Settings", self) settings_menu.addAction("Version", self.SeeVersion) settings_menu.addAction("Help", self.GetHelp) menubar.addMenu(file_menu) menubar.addMenu(settings_menu) self.setMenuBar(menubar) def _createProgressBar(self): self.progress_bar = QProgressBar() self.progress_bar.minimum = 0 self.progress_bar.maximum = 100 self.progress_bar.setValue(0) self.progress_bar.setTextVisible(False) self.progress_bar.setGeometry(50, 50, 10, 10) self.progress_bar.setAlignment(QtCore.Qt.AlignRight) self.progress_bar.adjustSize() self.statusBar().addPermanentWidget(self.progress_bar) self.ChangeAppStatus(False) def SwitchImage(self, cloth, image): # It shows you the team logo. No way you can miss those, right? team_id = self.SearchTeamID(cloth, True) image.setPixmap(QPixmap( "images/logos/team%d.png" % team_id).scaled(100,100)) def searchPlayer(self, text, combobox): # It even searches the player for you. What more could you want? search_index = combobox.findText(text, QtCore.Qt.MatchContains) if search_index == -1: QMessageBox.warning(self, "Error", "No player found") else: combobox.setCurrentIndex(search_index) def CreateTeamsWindow(self): self.teamcreation_wid = EditionWidget() self.teamcreation_wid.resize(400, 200) self.teamcreation_wid.setWindowTitle("Teams configuration") self.new_config = {} id_label = QLabel(self) id_label.setText("Team ID: ") self.num_id = QLabel(self) current_id = str(self.config["total_teams"] + 1) self.num_id.setText(current_id) name_label = QLabel(self) name_label.setText("Team Name:") name_label.setFocus() self.name_input = QLineEdit(self) members_label = QLabel(self) members_label.setText("Members (write and press enter):") members_input = QLineEdit(self) members_input.editingFinished.connect( lambda: self.AddMember(members_input)) self.members_list = QListWidget(self) import_image = QPushButton(self) import_image.setText("Import Team Image") import_image.clicked.connect(self.ImportTeamImage) add_team = QPushButton(self) add_team.setText("Add Team") add_team.clicked.connect( lambda: self.addTeamFunction(self.name_input.text(), self.members_list)) import_config = QPushButton(self) import_config.setText("Import configuration") import_config.clicked.connect(self.importTeamFunction) config_lay = QGridLayout() config_lay.addWidget(id_label, 1, 0) config_lay.addWidget(self.num_id, 1, 1) config_lay.addWidget(name_label, 2, 0) config_lay.addWidget(self.name_input, 2, 1) config_lay.addWidget(members_label, 3, 0) config_lay.addWidget(members_input, 3, 1) config_lay.addWidget(self.members_list, 4, 0, 2, 2) config_lay.addWidget(add_team, 6, 0) config_lay.addWidget(import_image, 6, 1) config_lay.addWidget(import_config, 7, 0, 1, 2) self.teamcreation_wid.setLayout(config_lay) self.teamcreation_wid.setWindowModality(QtCore.Qt.ApplicationModal) self.teamcreation_wid.activateWindow() self.teamcreation_wid.raise_() self.teamcreation_wid.show() def addTeamFunction(self, name, members): fp_teams = open(THISDIR + "\\config\\teams.json", "r", encoding="utf-8") current_teams = json.loads(fp_teams.read()) fp_teams.close() team = {} current_teams["teams"].append(name) current_teams["players"][name] = [str(self.members_list.item(i).text())\ for i in range(self.members_list.count())] new_team = open(THISDIR + "\\config\\teams.json", "w+", encoding="utf-8") add_config = open(THISDIR + "\\config\\config.json", "w+", encoding="utf-8") self.teams = current_teams["teams"] self.players = current_teams["players"] self.config["total_teams"] += 1 new_id = self.config["total_teams"] + 1 self.num_id.setText(str(new_id)) add_config.write(json.dumps(self.config, indent=4)) new_team.write(json.dumps(current_teams, indent=4)) new_team.close() self.name_input.clear() self.members_list.clear() self.UpdatePlayersList() def ImportTeamImage(self): image_dialog = QFileDialog(self) image_dialog = QFileDialog.getOpenFileName(filter="Images (*.png)", selectedFilter="Images (*.png)") if image_dialog[0] != "": new_team_logo = Image.open(image_dialog[0]).convert("RGBA") if new_team_logo.size != (250, 250): new_team_logo.resize((250, 250)) new_team_logo.save(THISDIR+"\\images\\logos\\team%s.png"\ % self.num_id.text()) QMessageBox.information(self, "Team Image", "Team image added.") def importTeamFunction(self): file_dialog = QFileDialog(self) file_dialog = QFileDialog.getOpenFileName( filter="Team Files (*.json *.zip)", selectedFilter="Team Files (*.json *.zip)") if file_dialog[0] != "": if is_zipfile(file_dialog[0]): with ZipFile(file_dialog[0]) as zip_import: list_of_files = zip_import.namelist() for fimp in list_of_files: if fimp.startswith('logos'): zip_import.extract(fimp, path=THISDIR+'\\images\\') imported_teams = zip_import.read('teams.json') imported_teams = imported_teams.decode('utf-8') else: imported_teams = open(file_dialog[0], "r", encoding="utf-8").read() json_teams = json.loads(imported_teams) self.teams = json_teams["teams"] self.players = json_teams["players"] new_teams = open(THISDIR + "\\config\\teams.json", "w+", encoding="utf-8") new_teams.write(json.dumps(json_teams, indent=4)) new_teams.close() old_config = open(THISDIR + "\\config\\config.json", "r", encoding="utf-8").read() old_config = json.loads(old_config) old_config["total_teams"] = len(json_teams["teams"]) self.config = old_config new_config = open(THISDIR + "\\config\\config.json", "w+", encoding="utf-8") new_config.write(json.dumps(self.config, indent=4)) new_config.close() self.UpdatePlayersList() self.image_east.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.cloth_east.setModel(self.players_combobox.model()) self.image_south.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.cloth_south.setModel(self.players_combobox.model()) self.image_west.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.cloth_west.setModel(self.players_combobox.model()) self.image_north.setPixmap(QPixmap("images/logos/team1.png")\ .scaled(100,100)) self.cloth_north.setModel(self.players_combobox.model()) self.statusBar().showMessage("Teams imported successfully.") self.teamcreation_wid.close() def AddMember(self, member): self.members_list.addItem(member.text()) member.clear() def EditTeamsWindow(self): self.teamedit_wid = EditionWidget() self.teamedit_wid.resize(400, 320) self.teamedit_wid.setWindowTitle("Edit Teams") self.teams_list = QComboBox(self) self.teams_list.addItem("--- Select a team ---") for team in self.teams: self.teams_list.addItem(team) self.teams_list.currentIndexChanged.connect(self.UpdateTeamInfo) team_id_label = QLabel(self) team_id_label.setText("Team ID: ") self.config_team_id = QLabel(self) team_name_label = QLabel(self) team_name_label.setText("Team name: ") self.config_team_name = QLabel(self) team_members_label = QLabel(self) team_members_label.setText("Team members: ") self.config_team_members = QListWidget(self) add_member_label = QLabel(self) add_member_label.setText("Add new member: ") add_member_input = QLineEdit(self) add_member_input.editingFinished.connect(self.AddNewMember) delete_member = QPushButton(self) delete_member.setText("Delete member") delete_member.clicked.connect(self.DeleteMember) delete_team = QPushButton(self) delete_team.setText("Delete Team") delete_team.clicked.connect(self.DeleteTeam) save_changes = QPushButton(self) save_changes.setText("Save changes") save_changes.clicked.connect(self.SaveEdits) export_config = QPushButton(self) export_config.setText("Export Configuration") export_config.clicked.connect(self.ExportTeams) config_lay = QGridLayout() config_lay.addWidget(self.teams_list, 1, 0) config_lay.addWidget(team_id_label, 2, 0) config_lay.addWidget(self.config_team_id, 2, 1) config_lay.addWidget(team_name_label, 3, 0) config_lay.addWidget(self.config_team_name, 3, 1, 1, 2) config_lay.addWidget(team_members_label, 4, 0) config_lay.addWidget(self.config_team_members, 5, 0) config_lay.addWidget(add_member_label, 6, 0) config_lay.addWidget(add_member_input, 6, 1, 1, 2) config_lay.addWidget(delete_member, 7, 0) config_lay.addWidget(delete_team, 7, 1) config_lay.addWidget(save_changes, 8, 0) config_lay.addWidget(export_config, 8, 1) self.teamedit_wid.setLayout(config_lay) self.teamedit_wid.setWindowModality(QtCore.Qt.ApplicationModal) self.teamedit_wid.activateWindow() self.teamedit_wid.raise_() self.teamedit_wid.show() def UpdateTeamInfo(self): sender = self.sender() if sender.currentIndex() > 0: team_id = sender.currentIndex() self.config_team_id.setText(str(team_id)) self.config_team_name.setText(sender.currentText()) if self.config_team_members.count() > 0: self.config_team_members.clear() self.config_team_members.addItems( self.players[sender.currentText()]) def AddNewMember(self): sender = self.sender() self.config_team_members.addItem(sender.text()) sender.clear() def DeleteMember(self): list_members = self.config_team_members.selectedItems() if len(list_members) == 0: QMessageBox.warning(self, "Error", "No player selected") else: for member in list_members: self.config_team_members.takeItem( self.config_team_members.row(member)) def DeleteTeam(self): team_id = int(self.config_team_id.text()) is_last_item = self.teams[self.teams.index( self.config_team_name.text())] == (self.teams[len(self.teams)-1]) self.teams.pop(self.teams.index(self.config_team_name.text())) self.players.pop(self.config_team_name.text()) new_teamlist = {} new_teamlist["teams"] = self.teams new_teamlist["players"] = self.players current_teams = open(THISDIR + "\\config\\teams.json", "w+", encoding="utf-8") current_teams.write(json.dumps(new_teamlist, indent=4)) current_teams.close() if is_last_item == True: self.teams_list.setCurrentIndex(1) else: self.teams_list.setCurrentIndex(team_id+1) self.teams_list.removeItem(team_id) self.UpdatePlayersList() self.cloth_east.setModel(self.players_combobox.model()) self.cloth_south.setModel(self.players_combobox.model()) self.cloth_west.setModel(self.players_combobox.model()) self.cloth_north.setModel(self.players_combobox.model()) def ExportTeams(self): export_dir = self.config["save_route"] if self.config["save_route"] \ is not None else THISDIR exported_file = QFileDialog.getSaveFileName(self, "Save File", export_dir, "Save files (*.zip)") if exported_file[0] != "": export_filename = exported_file[0] if export_filename.endswith(".zip") is False: export_filename += ".zip" files_to_export = [] files_to_export.append("config\\teams.json") for root, directories, files in os.walk(THISDIR+"\\images\\logos"): for filename in files: filepath = os.path.join(root, filename) files_to_export.append(filepath) with ZipFile(export_filename, "w") as export_zip: for exp_file in files_to_export: export_name = exp_file if exp_file.endswith(".json"): split_name = exp_file.split("\\") export_name = split_name[-1] if exp_file.endswith(".png"): split_name = exp_file.split("\\") export_name = "\\logos\\" + split_name[-1] export_zip.write(exp_file, arcname=export_name) export_zip.close() if os.path.exists(export_filename): QMessageBox.information(self, "Export", "The export was successful") def SaveEdits(self): list_members = [str(self.config_team_members.item(i).text()) for i in \ range(self.config_team_members.count())] self.players[self.config_team_name.text()] = list_members new_teamlist = {} new_teamlist["teams"] = self.teams new_teamlist["players"] = self.players current_teams = open(THISDIR + "\\config\\teams.json", "w+", encoding="utf-8") current_teams.write(json.dumps(new_teamlist, indent=4)) current_teams.close() self.teamedit_wid.close() self.statusBar().showMessage("Settings saved.") def MatDialog(self): mat_dialog = QFileDialog(self) mat_dialog = QFileDialog.getOpenFileName(filter="Images (*.png *.jpg)", selectedFilter="Images (*.png *.jpg)") if mat_dialog[0] != "": self.GenerateMat(mat_dialog[0]) def GenerateMat(self, image): self.background = image background = Image.open(self.background).resize((2048,2048))\ .convert("RGBA") self.mat_thread = QThread() east_id = self.SearchTeamID(self.cloth_east, True) south_id = self.SearchTeamID(self.cloth_south, True) west_id = self.SearchTeamID(self.cloth_west, True) north_id = self.SearchTeamID(self.cloth_north, True) if self.config["save_route"] is None: save_to_route = THISDIR else: save_to_route = self.config["save_route"] self._createProgressBar() self.mat_worker = GenerateImageThread(background, self.table_border, east_id, south_id, west_id, north_id, self.technical_lines.isChecked(), save_to_route, self.bg_image, True) self.mat_worker.moveToThread(self.mat_thread) self.mat_thread.started.connect(self.mat_worker.run) self.mat_worker.update_progress.connect(self.UpdateStatus) self.mat_worker.finished.connect(self.mat_thread.quit) self.mat_worker.finished.connect(self.mat_worker.deleteLater) self.mat_thread.finished.connect(self.mat_thread.deleteLater) self.mat_thread.finished.connect(self.MatPreviewWindow) self.mat_thread.start() def MatPreviewWindow(self): self.statusBar().showMessage('Mat preview generated.') self.statusBar().removeWidget(self.progress_bar) # Now you can go back to rigging self.ChangeAppStatus(True) self.mat_wid = QWidget() self.mat_wid.resize(600, 600) self.mat_wid.setWindowTitle("Background preview") mat_preview_title = QLabel(self) mat_preview_title.setText("Selected image (1/4 scale)") mat_preview = QLabel(self) mat_preview.setPixmap(QPixmap(tempfile.gettempdir()+"\\Table_Dif.jpg")\ .scaled(512,512)) confirm = QPushButton(self) confirm.setText("Confirm") confirm.clicked.connect( lambda: self.ChangeMatImage(self.background)) vbox = QVBoxLayout() vbox.setAlignment(QtCore.Qt.AlignCenter) vbox.addWidget(mat_preview_title) vbox.addWidget(mat_preview) vbox.addWidget(confirm) self.mat_wid.setLayout(vbox) self.mat_wid.setWindowModality(QtCore.Qt.ApplicationModal) self.mat_wid.activateWindow() self.mat_wid.raise_() self.mat_wid.show() def ChangeMatImage(self, image): new_bg = Image.open(image) if new_bg.size != (2048, 2048): new_bg = new_bg.resize((2048, 2048)) if new_bg.mode != "RGBA": new_bg = new_bg.convert("RGBA") if self.config["save_route"] is not None: new_bg.save(self.config["save_route"]+"\\images\\mat.png") self.bg_image = self.config["save_route"]+"\\images\\mat.png" else: new_bg.save(THISDIR+"\\images\\mat.png") self.bg_image = THISDIR+"\\images\\mat.png" self.background = new_bg self.config["image_route"] = self.bg_image new_file = open(THISDIR + "\\config\\config.json", "w+", encoding="utf-8") new_file.write(json.dumps(self.config, indent=4)) new_file.close() self.statusBar().showMessage('New background added.') self.statusBar().removeWidget(self.progress_bar) self.ChangeAppStatus(True) self.mat_wid.close() def GeneratePreview(self): self.preview_thread = QThread() east_id = self.SearchTeamID(self.cloth_east, True) south_id = self.SearchTeamID(self.cloth_south, True) west_id = self.SearchTeamID(self.cloth_west, True) north_id = self.SearchTeamID(self.cloth_north, True) if self.config["save_route"] is None: save_to_route = THISDIR else: save_to_route = self.config["save_route"] self._createProgressBar() self.preview_worker = GenerateImageThread(self.background, self.table_border, east_id, south_id, west_id, north_id, self.technical_lines.isChecked(), save_to_route, self.bg_image, True) self.preview_worker.moveToThread(self.preview_thread) self.preview_thread.started.connect(self.preview_worker.run) self.preview_worker.update_progress.connect(self.UpdateStatus) self.preview_worker.finished.connect(self.preview_thread.quit) self.preview_worker.finished.connect(self.preview_worker.deleteLater) self.preview_thread.finished.connect(self.preview_thread.deleteLater) self.preview_thread.finished.connect(self.PreviewWindow) self.preview_thread.start() def PreviewWindow(self): self.statusBar().showMessage('Tablecloth preview generated.') self.statusBar().removeWidget(self.progress_bar) # Now you can go back to rigging self.ChangeAppStatus(True) self.preview_wid = QWidget() self.preview_wid.resize(600, 600) self.preview_wid.setWindowTitle("Tablecloth preview") tablecloth = QPixmap(tempfile.gettempdir()+"\\Table_Dif.jpg") tablecloth_preview_title = QLabel(self) tablecloth_preview_title.setText("Tablecloth preview (1/4 scale)") tablecloth_preview = QLabel(self) tablecloth_preview.setPixmap(tablecloth.scaled(512,512)) confirm = QPushButton(self) confirm.setText("Confirm") confirm.clicked.connect(self.GenerateImage) confirm.clicked.connect(self.preview_wid.close) vbox = QVBoxLayout() vbox.setAlignment(QtCore.Qt.AlignCenter) vbox.addWidget(tablecloth_preview_title) vbox.addWidget(tablecloth_preview) vbox.addWidget(confirm) self.preview_wid.setLayout(vbox) self.preview_wid.setWindowModality(QtCore.Qt.ApplicationModal) self.preview_wid.activateWindow() self.preview_wid.raise_() self.preview_wid.show() def GeneratedDialog(self): self.statusBar().showMessage('Tablecloth generated. Happy rigging!') self.statusBar().removeWidget(self.progress_bar) # Now you can go back to rigging self.ChangeAppStatus(True) mbox = QMessageBox() mbox.setWindowTitle("Tablecloth Generator") mbox.setText("Tablecloth Generated!") mbox.setStandardButtons(QMessageBox.Ok) mbox.exec() def UpdateStatus(self, status): self.progress_bar.setValue(status) def GenerateImage(self): self.statusBar().showMessage('Generating image...') self._createProgressBar() if self.config["save_route"] is None: self.config["save_route"] = THISDIR save_to_route = QFileDialog.getExistingDirectory(self, "Where to save the image", self.config["save_route"], QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if self.config["save_route"] != save_to_route: temp_file = open(THISDIR + "\\config\\config.json", "r", encoding="utf-8") fp_teams = json.loads(temp_file.read()) fp_teams["save_route"] = save_to_route fp_teams["image_route"] = self.bg_image new_file = open(THISDIR + "\\config\\config.json", "w+", encoding="utf-8") new_file.write(json.dumps(fp_teams, indent=4)) new_file.close() self.background = Image.open(THISDIR + "\\images\\mat.png") self.table_border = Image.open(THISDIR + "\\images\\table_border.png") self.tech_lines = Image.open(THISDIR + "\\images\\technical_lines.png") self.thread = QThread() east_id = self.SearchTeamID(self.cloth_east, True) south_id = self.SearchTeamID(self.cloth_south, True) west_id = self.SearchTeamID(self.cloth_west, True) north_id = self.SearchTeamID(self.cloth_north, True) self.worker = GenerateImageThread(self.background, self.table_border, east_id, south_id, west_id, north_id, self.technical_lines.isChecked(), save_to_route, self.bg_image) self.worker.moveToThread(self.thread) self.thread.started.connect(self.worker.run) self.worker.update_progress.connect(self.UpdateStatus) self.worker.finished.connect(self.thread.quit) self.worker.finished.connect(self.worker.deleteLater) self.thread.finished.connect(self.thread.deleteLater) self.thread.finished.connect(self.GeneratedDialog) self.thread.start() def ChangeAppStatus(self, status): # True for enable, False for disable. self.cloth_east.setEnabled(status) self.search_east.setEnabled(status) self.cloth_south.setEnabled(status) self.search_south.setEnabled(status) self.cloth_west.setEnabled(status) self.search_west.setEnabled(status) self.cloth_north.setEnabled(status) self.search_north.setEnabled(status) self.generate.setEnabled(status) def SearchTeamID(self, cloth, plus_one=False): team_id = self.teams.index(cloth.itemData(cloth.currentIndex())) if plus_one: team_id += 1 return team_id def UpdatePlayersList(self): for team, members in self.players.items(): for member in members: self.players_combobox.addItem(member, team) def center(self): qr = self.frameGeometry() cp = QScreen().availableGeometry().center() qr.moveCenter(cp) def SeeVersion(self): git_url = "https://raw.githubusercontent.com/vg-mjg/tablecloth-" git_url += "generator/main/version.txt" with urllib.request.urlopen(git_url) as response: url_version = response.read().decode("utf-8") version = "Your version is up to date!" if url_version != VERSION: version = "Your version is outdated." version += "Please check the <a href='https://github.com/vg-mjg/" version += "tablecloth-generator/releases'>Github page</a>" version +=" for updates." version_message = QMessageBox(self) version_message.setWindowTitle("Checking version") version_message.setText("""<h1>Tablecloth generator</h1> <br> <b>Current Version:</b> %s<br> <b>Your Version:</b> %s<br> <i>%s</i> """ % (url_version, VERSION, version)) version_message.exec() def GetHelp(self): webbrowser.open("https://github.com/vg-mjg/tablecloth-generator/wiki")
class CropDialog(QDialog): def __init__(self, parent, start, stop): super().__init__(parent) self.setWindowTitle("Crop data") vbox = QVBoxLayout(self) grid = QGridLayout() self.start_checkbox = QCheckBox("Start time:") self.start_checkbox.setChecked(True) self.start_checkbox.stateChanged.connect(self.toggle_start) grid.addWidget(self.start_checkbox, 0, 0) self._start = QDoubleSpinBox() self._start.setMaximum(999999) self._start.setValue(start) self._start.setDecimals(2) self._start.setSuffix(" s") grid.addWidget(self._start, 0, 1) self.stop_checkbox = QCheckBox("Stop time:") self.stop_checkbox.setChecked(True) self.stop_checkbox.stateChanged.connect(self.toggle_stop) grid.addWidget(self.stop_checkbox, 1, 0) self._stop = QDoubleSpinBox() self._stop.setMaximum(999999) self._stop.setValue(stop) self._stop.setDecimals(2) self._stop.setSuffix(" s") grid.addWidget(self._stop, 1, 1) vbox.addLayout(grid) buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(buttonbox) buttonbox.accepted.connect(self.accept) buttonbox.rejected.connect(self.reject) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) @property def start(self): if self.start_checkbox.isChecked(): return self._start.value() else: return None @property def stop(self): if self.stop_checkbox.isChecked(): return self._stop.value() else: return None @Slot() def toggle_start(self): if self.start_checkbox.isChecked(): self._start.setEnabled(True) else: self._start.setEnabled(False) @Slot() def toggle_stop(self): if self.stop_checkbox.isChecked(): self._stop.setEnabled(True) else: self._stop.setEnabled(False)
class RenderData(QWidget): def __init__(self, db_name_from_visualize): super().__init__() self.db_name_from_visual_btn = db_name_from_visualize self.type_of_display = QLabel( "Please choose how you'd like to display the data", self) self.type_of_display.move(20, 20) self.type_of_display.setStyleSheet("border: 1px solid black;") self.type_of_display.setFont(QFont("Calibre", 12)) self.which_data = QLabel( "Please select which data you'd like to display", self) self.which_data.move(430, 20) self.which_data.setStyleSheet("border: 1px solid black") self.which_data.setFont(QFont("Calibre", 12)) # LIST BOXES display_list = QListWidget(self) self.list_control = display_list display_list.resize(770, 440) display_list.move(10, 200) # CHECK BOXES self.color_coded_checkbox = QCheckBox("Color coded text in a list", self) self.color_coded_checkbox.move(100, 60) self.color_coded_checkbox.clicked.connect( self.swap_color_coded_checkbox) self.render_map_checkbox = QCheckBox("Render a Map", self) self.render_map_checkbox.move(100, 90) self.render_map_checkbox.toggle() self.render_map_checkbox.clicked.connect(self.swap_render_map_checkbox) self.analysis_type1_checkbox = QCheckBox( "Compare the number of college graduates in a state \n(for the most recent " "year) with number of jobs in that \nstate that likely expect a college " "education.", self) self.analysis_type1_checkbox.setGeometry(430, 20, 300, 100) self.analysis_type1_checkbox.clicked.connect( self.swap_num_grads_checkbox) self.analysis_type2_checkbox = QCheckBox( "Compare the 3 year graduate cohort declining balance \npercentage to " "the 25% salary in the state.", self) self.analysis_type2_checkbox.setGeometry(430, 70, 400, 100) self.analysis_type2_checkbox.toggle() self.analysis_type2_checkbox.clicked.connect( self.swap_3_yr_cohort_checkbox) # PUSH BUTTONS self.render_data_button = QPushButton("VISUALIZE", self) self.render_data_button.setGeometry(10, 150, 770, 40) self.render_data_button.clicked.connect(self.display_visualization) self.sort_ascending_button = QPushButton("Sort Ascending", self) self.sort_ascending_button.setGeometry(10, 650, 120, 30) self.sort_ascending_button.clicked.connect(self.sort_ascending) self.sort_descending_button = QPushButton("Sort Descending", self) self.sort_descending_button.setGeometry(130, 650, 120, 30) self.sort_descending_button.clicked.connect(self.sort_descending) self.close_visual_window_button = QPushButton("CLOSE", self) self.close_visual_window_button.setGeometry(660, 650, 120, 30) self.close_visual_window_button.clicked.connect(lambda: self.close()) self.setWindowTitle( "Data Visualization for Project 1 - Sprint 4 - Ryan O'Connor - COMP490 - T/R" ) self.setGeometry(100, 100, 800, 700) self.setFixedSize(800, 700) self.center() def display_visualization(self): conn, cursor = open_db(self.db_name_from_visual_btn) cursor.execute( 'SELECT school_state, student_size_2018 FROM school_export') table = cursor.fetchall() self.list_control.clear() num_grads_in_state = { "AK": 0, "AL": 0, "AR": 0, "AS": 0, "AZ": 0, "CA": 0, "CO": 0, "CT": 0, "DC": 0, "DE": 0, "FL": 0, "FM": 0, "GA": 0, "GU": 0, "HI": 0, "IA": 0, "ID": 0, "IL": 0, "IN": 0, "KS": 0, "KY": 0, "LA": 0, "MA": 0, "MD": 0, "ME": 0, "MH": 0, "MI": 0, "MN": 0, "MO": 0, "MP": 0, "MS": 0, "MT": 0, "NC": 0, "ND": 0, "NE": 0, "NH": 0, "NJ": 0, "NM": 0, "NV": 0, "NY": 0, "OH": 0, "OK": 0, "OR": 0, "PA": 0, "PR": 0, "PW": 0, "RI": 0, "SC": 0, "SD": 0, "TN": 0, "TX": 0, "UT": 0, "VA": 0, "VI": 0, "VT": 0, "WA": 0, "WI": 0, "WV": 0, "WY": 0 } for idx, row in enumerate(table): if row[1] is None: continue else: state_abbr_from_table = row[0] student_size_2018 = row[1] state_total = num_grads_in_state[state_abbr_from_table] num_grads_in_state[ state_abbr_from_table] = state_total + student_size_2018 # Dividing by 4 years here - to simplify the size of a senior graduating class for student_total_2018 in num_grads_in_state: num_grads_in_state[student_total_2018] = num_grads_in_state[ student_total_2018] / 4 # DATA ANALYSIS - PART 1B num_jobs_in_state = { "AK": 0, "AL": 0, "AR": 0, "AS": 0, "AZ": 0, "CA": 0, "CO": 0, "CT": 0, "DC": 0, "DE": 0, "FL": 0, "FM": 0, "GA": 0, "GU": 0, "HI": 0, "IA": 0, "ID": 0, "IL": 0, "IN": 0, "KS": 0, "KY": 0, "LA": 0, "MA": 0, "MD": 0, "ME": 0, "MH": 0, "MI": 0, "MN": 0, "MO": 0, "MP": 0, "MS": 0, "MT": 0, "NC": 0, "ND": 0, "NE": 0, "NH": 0, "NJ": 0, "NM": 0, "NV": 0, "NY": 0, "OH": 0, "OK": 0, "OR": 0, "PA": 0, "PR": 0, "PW": 0, "RI": 0, "SC": 0, "SD": 0, "TN": 0, "TX": 0, "UT": 0, "VA": 0, "VI": 0, "VT": 0, "WA": 0, "WI": 0, "WV": 0, "WY": 0 } cursor.execute( 'SELECT area_title, occ_code, tot_emp FROM jobdata_by_state') table = cursor.fetchall() for idx, row in enumerate(table): check_occ_code = row[1] check_occ_code = int(check_occ_code[:2]) if 30 <= check_occ_code <= 49: continue else: state_from_school_export = str(row[0]) abbr_state = abbreviate_state(state_from_school_export) tot_emp_jobs_in_state = int(row[2]) tot_emp = num_jobs_in_state[abbr_state] num_jobs_in_state[abbr_state] = tot_emp + tot_emp_jobs_in_state compare_total_jobs_to_grads = { k: (num_jobs_in_state[k] / num_grads_in_state[k]) for k in num_jobs_in_state } display_data = open("display_map_data.csv", "w+") display_data.writelines("state,data\n") for key in compare_total_jobs_to_grads: total_jobs_rounded = (round(compare_total_jobs_to_grads[key], 2)) if total_jobs_rounded == 0: display_data.writelines(f"{key}, {total_jobs_rounded}\n") else: display_data.writelines(f"{key}, {total_jobs_rounded}\n") display_data.close() # DATA ANALYSIS PART 2A repayment_2016_dict = { "AK": 0, "AL": 0, "AR": 0, "AS": 0, "AZ": 0, "CA": 0, "CO": 0, "CT": 0, "DC": 0, "DE": 0, "FL": 0, "FM": 0, "GA": 0, "GU": 0, "HI": 0, "IA": 0, "ID": 0, "IL": 0, "IN": 0, "KS": 0, "KY": 0, "LA": 0, "MA": 0, "MD": 0, "ME": 0, "MH": 0, "MI": 0, "MN": 0, "MO": 0, "MP": 0, "MS": 0, "MT": 0, "NC": 0, "ND": 0, "NE": 0, "NH": 0, "NJ": 0, "NM": 0, "NV": 0, "NY": 0, "OH": 0, "OK": 0, "OR": 0, "PA": 0, "PR": 0, "PW": 0, "RI": 0, "SC": 0, "SD": 0, "TN": 0, "TX": 0, "UT": 0, "VA": 0, "VI": 0, "VT": 0, "WA": 0, "WI": 0, "WV": 0, "WY": 0 } cursor.execute( 'SELECT school_state, repayment_repayment_cohort_3_year_declining_balance_2016 FROM school_export' ) table = cursor.fetchall() for idx, row in enumerate(table): if row[1] is None: continue else: state_from_school_export = str(row[0]) repayment_2016_data = row[1] repayment_2016_data_tot_sum = repayment_2016_dict[ state_from_school_export] repayment_2016_dict[ state_from_school_export] = repayment_2016_data + repayment_2016_data_tot_sum for key in repayment_2016_dict: if repayment_2016_dict[key] == 0: repayment_2016_dict[ key] = 0.0001 # States with 0 data get this due to dividing by 0 # DATA ANALYSIS - PART 2B a_pc25_dict = { "AK": 0, "AL": 0, "AR": 0, "AS": 0, "AZ": 0, "CA": 0, "CO": 0, "CT": 0, "DC": 0, "DE": 0, "FL": 0, "FM": 0, "GA": 0, "GU": 0, "HI": 0, "IA": 0, "ID": 0, "IL": 0, "IN": 0, "KS": 0, "KY": 0, "LA": 0, "MA": 0, "MD": 0, "ME": 0, "MH": 0, "MI": 0, "MN": 0, "MO": 0, "MP": 0, "MS": 0, "MT": 0, "NC": 0, "ND": 0, "NE": 0, "NH": 0, "NJ": 0, "NM": 0, "NV": 0, "NY": 0, "OH": 0, "OK": 0, "OR": 0, "PA": 0, "PR": 0, "PW": 0, "RI": 0, "SC": 0, "SD": 0, "TN": 0, "TX": 0, "UT": 0, "VA": 0, "VI": 0, "VT": 0, "WA": 0, "WI": 0, "WV": 0, "WY": 0 } cursor.execute('SELECT area_title, a_pct25 FROM jobdata_by_state') table = cursor.fetchall() for idx, row in enumerate(table): a_pct25_from_job_date_by_state = str(row[0]) abbr_state = abbreviate_state(a_pct25_from_job_date_by_state) current_a_pct25 = row[1] tot_ann_pct25_dict = a_pc25_dict[abbr_state] a_pc25_dict[abbr_state] = current_a_pct25 + tot_ann_pct25_dict compare_a_pct25_to_2016_repayment = { k: (a_pc25_dict[k] / repayment_2016_dict[k]) for k in a_pc25_dict } display_data = open("display_map_data2.csv", "w+") display_data.writelines("state,data\n") for key in compare_a_pct25_to_2016_repayment: if key in ("GU", "VI" ): # Omitting Guam & Virgin Islands due to skewing data continue else: tot_apc25_2016_repay_rounded = (round( compare_a_pct25_to_2016_repayment[key], 2)) display_data.writelines( f"{key}, {tot_apc25_2016_repay_rounded}\n") display_data.close() if self.analysis_type1_checkbox.isChecked(): type_of_analysis = "1" else: type_of_analysis = "2" if self.render_map_checkbox.isChecked(): DisplayMap.display_map(type_of_analysis) else: if type_of_analysis == "1": for key in compare_total_jobs_to_grads: total_jobs_rounded = (round( compare_total_jobs_to_grads[key], 2)) if total_jobs_rounded == 0: display_text = f"State: {key}\t Total jobs: {num_jobs_in_state[key]}\t\t Total college grads: " \ f"{num_grads_in_state[key]}\t\t {total_jobs_rounded} jobs available " \ f"per graduating student" else: display_text = f"State: {key}\t Total jobs: {num_jobs_in_state[key]}\t Total college grads: " \ f"{num_grads_in_state[key]}\t\t {total_jobs_rounded} jobs available " \ f"per graduating student" list_item = QListWidgetItem(display_text, listview=self.list_control) list_item.setForeground(Qt.darkRed) else: for key in compare_a_pct25_to_2016_repayment: apc25_to_2016_repay_rounded = (round( compare_a_pct25_to_2016_repayment[key], 2)) repay_2016_rounded = (round(repayment_2016_dict[key], 2)) if apc25_to_2016_repay_rounded == 0: display_text = f"State: {key}\t 3 year graduate cohort declining balance percent: " \ f"{repayment_2016_dict[key]}\t 25% salary: {a_pc25_dict[key]}\t\t Result: " \ f"{apc25_to_2016_repay_rounded}" else: display_text = f"State: {key}\t 3 year graduate cohort declining balance percent: " \ f"{repay_2016_rounded}\t 25% salary: {a_pc25_dict[key]}\t Result: " \ f"{apc25_to_2016_repay_rounded}" list_item = QListWidgetItem(display_text, listview=self.list_control) list_item.setForeground(Qt.darkBlue) close_db(conn) def sort_ascending(self): self.list_control.sortItems(Qt.AscendingOrder) def sort_descending(self): self.list_control.sortItems(Qt.DescendingOrder) def swap_color_coded_checkbox(self): status = self.color_coded_checkbox.isChecked() if status: self.render_map_checkbox.setChecked(False) else: self.render_map_checkbox.setChecked(True) def swap_render_map_checkbox(self): status = self.render_map_checkbox.isChecked() if status: self.color_coded_checkbox.setChecked(False) else: self.color_coded_checkbox.setChecked(True) def swap_num_grads_checkbox(self): status = self.analysis_type1_checkbox.isChecked() if status: self.analysis_type2_checkbox.setChecked(False) else: self.analysis_type2_checkbox.setChecked(True) def swap_3_yr_cohort_checkbox(self): status = self.analysis_type2_checkbox.isChecked() if status: self.analysis_type1_checkbox.setChecked(False) else: self.analysis_type1_checkbox.setChecked(True) def center(self): screen_center = QScreen.availableGeometry( QApplication.primaryScreen()).center() self_geometry = self.frameGeometry() self_geometry.moveCenter(screen_center) self.move(self_geometry.topLeft())
def _on_check_area(c: QtWidgets.QCheckBox, _): if not self.during_batch_check_update: on_check([c.area_location], c.isChecked())
class XDFStreamsDialog(QDialog): def __init__(self, parent, rows, selected=None, disabled=None): super().__init__(parent) self.setWindowTitle("Select XDF Stream") self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels( ["ID", "Name", "Type", "Channels", "Format", "Sampling Rate"]) for index, stream in enumerate(rows): items = [] for item in stream: tmp = QStandardItem() tmp.setData(item, Qt.DisplayRole) items.append(tmp) for item in items: item.setEditable(False) if disabled is not None and index in disabled: item.setFlags(Qt.NoItemFlags) self.model.appendRow(items) self.view = QTableView() self.view.setModel(self.model) self.view.verticalHeader().setVisible(False) self.view.horizontalHeader().setStretchLastSection(True) self.view.setShowGrid(False) self.view.setSelectionMode(QAbstractItemView.SingleSelection) self.view.setSelectionBehavior(QAbstractItemView.SelectRows) if selected is not None: self.view.selectRow(selected) self.view.setSortingEnabled(True) self.view.sortByColumn(0, Qt.AscendingOrder) vbox = QVBoxLayout(self) vbox.addWidget(self.view) hbox = QHBoxLayout() self._effective_srate = QCheckBox("Use effective sampling rate") self._effective_srate.setChecked(True) hbox.addWidget(self._effective_srate) self._prefix_markers = QCheckBox("Prefix markers with stream ID") self._prefix_markers.setChecked(False) if not disabled: self._prefix_markers.setEnabled(False) hbox.addWidget(self._prefix_markers) vbox.addLayout(hbox) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.resize(775, 650) self.view.setColumnWidth(0, 90) self.view.setColumnWidth(1, 200) self.view.setColumnWidth(2, 140) @property def effective_srate(self): return self._effective_srate.isChecked() @property def prefix_markers(self): return self._prefix_markers.isChecked()
class EpochDialog(QDialog): def __init__(self, parent, events): super().__init__(parent) self.setWindowTitle("Create Epochs") grid = QGridLayout(self) label = QLabel("Events:") label.setAlignment(Qt.AlignTop) grid.addWidget(label, 0, 0, 1, 1) self.events = QListWidget() self.events.insertItems(0, unique(events[:, 2]).astype(str)) self.events.setSelectionMode(QListWidget.ExtendedSelection) grid.addWidget(self.events, 0, 1, 1, 2) grid.addWidget(QLabel("Interval around events:"), 1, 0, 1, 1) self.tmin = QDoubleSpinBox() self.tmin.setMinimum(-10000) self.tmin.setValue(-0.2) self.tmin.setSingleStep(0.1) self.tmin.setAlignment(Qt.AlignRight) self.tmax = QDoubleSpinBox() self.tmax.setMinimum(-10000) self.tmax.setValue(0.5) self.tmax.setSingleStep(0.1) self.tmax.setAlignment(Qt.AlignRight) grid.addWidget(self.tmin, 1, 1, 1, 1) grid.addWidget(self.tmax, 1, 2, 1, 1) self.baseline = QCheckBox("Baseline Correction:") self.baseline.setChecked(True) self.baseline.stateChanged.connect(self.toggle_baseline) grid.addWidget(self.baseline, 2, 0, 1, 1) self.a = QDoubleSpinBox() self.a.setMinimum(-10000) self.a.setValue(-0.2) self.a.setSingleStep(0.1) self.a.setAlignment(Qt.AlignRight) self.b = QDoubleSpinBox() self.b.setMinimum(-10000) self.b.setValue(0) self.b.setSingleStep(0.1) self.b.setAlignment(Qt.AlignRight) grid.addWidget(self.a, 2, 1, 1, 1) grid.addWidget(self.b, 2, 2, 1, 1) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) grid.addWidget(self.buttonbox, 3, 0, 1, -1) self.events.itemSelectionChanged.connect(self.toggle_ok) self.toggle_ok() grid.setSizeConstraint(QGridLayout.SetFixedSize) @Slot() def toggle_ok(self): if self.events.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) @Slot() def toggle_baseline(self): if self.baseline.isChecked(): self.a.setEnabled(True) self.b.setEnabled(True) else: self.a.setEnabled(False) self.b.setEnabled(False)
class Maker(QWidget): def __init__(self, parent=None): super(Maker, self).__init__(parent) self.setWindowTitle("Project Maker") self.userFilepath = QLineEdit() self.userFilepath.setPlaceholderText("Your filepath here...") self.projName = QLineEdit() self.projName.setPlaceholderText("Your project name here...") self.makeButton = QPushButton("Create Project") self.fileSearchButton = QPushButton("...") self.fileSearchButton.setToolTip( "Search for a directory for your project") self.goProj = QRadioButton("Go Project") self.goProj.setToolTip("You will still need a go.mod file") self.pyProj = QRadioButton("Python Project") self.versionControlFiles = QCheckBox( "Create README.md and .gitignore?") self.versionControlFiles.setToolTip( "Creates the files used in online version control, such as Github") self.pyProj.setChecked(True) self.versionControlFiles.setChecked(True) projSelect = QGroupBox("Project Selection") projectOptions = QVBoxLayout() projectOptions.addWidget(self.pyProj) projectOptions.addWidget(self.goProj) projectOptions.addWidget(self.versionControlFiles) projectOptions.stretch(1) projSelect.setLayout(projectOptions) searchLayout = QHBoxLayout() searchLayout.addWidget(self.userFilepath) searchLayout.addWidget(self.fileSearchButton) searchLayout.stretch(1) layout = QVBoxLayout() layout.addLayout(searchLayout) layout.addWidget(self.projName) layout.addWidget(self.makeButton) layout.addWidget(self.fileSearchButton) layout.addWidget(projSelect) self.setLayout(layout) self.makeButton.clicked.connect(self.createFiles) self.fileSearchButton.clicked.connect(self.onClickFileSearch) @Slot() def onClickFileSearch(self): fileSearch = QFileDialog.getExistingDirectory(self, "Select a Directory...", "C:/Users") self.userFilepath.setText(fileSearch) @Slot() def createFiles(self): p = Path(f"{self.userFilepath.text()}/{self.projName.text()}") try: p.mkdir() except FileExistsError as exc: msgbox = QMessageBox() msgbox.setText(f"{exc}") msgbox.exec() else: os.chdir(f"{self.userFilepath.text()}\\{self.projName.text()}") if self.pyProj.isChecked(): fileType = "py" elif self.goProj.isChecked(): fileType = "go" with open(f"{self.projName.text()}.{fileType}", "w") as f: f.write(f"# Created on {date.today()}") if self.versionControlFiles.isChecked(): open("README.md", "a").close() open(".gitignore", "a").close()
class AdapterSettingsDialog(QDialog): def __init__(self, parent, data): assert type(data) == BinaryView self.bv = data QDialog.__init__(self, parent) debug_state = binjaplug.get_state(self.bv) self.setWindowTitle("Debug Adapter Settings") self.setMinimumSize(UIContext.getScaledWindowSize(400, 130)) self.setAttribute(Qt.WA_DeleteOnClose) layout = QVBoxLayout() layout.setSpacing(0) titleLabel = QLabel("Adapter Settings") titleLayout = QHBoxLayout() titleLayout.setContentsMargins(0, 0, 0, 0) titleLayout.addWidget(titleLabel) self.adapterEntry = QPushButton(self) self.adapterMenu = QMenu(self) for adapter in DebugAdapter.ADAPTER_TYPE: if not DebugAdapter.ADAPTER_TYPE.can_use(adapter): continue def select_adapter(adapter): return lambda: self.selectAdapter(adapter) self.adapterMenu.addAction(adapter.name, select_adapter(adapter)) if adapter == debug_state.adapter_type: self.adapterEntry.setText(adapter.name) self.adapterEntry.setMenu(self.adapterMenu) self.argumentsEntry = QLineEdit(self) self.addressEntry = QLineEdit(self) self.portEntry = QLineEdit(self) self.requestTerminalEmulator = QCheckBox(self) self.formLayout = QFormLayout() self.formLayout.addRow("Adapter Type", self.adapterEntry) self.formLayout.addRow("Command Line Arguments", self.argumentsEntry) self.formLayout.addRow("Address", self.addressEntry) self.formLayout.addRow("Port", self.portEntry) self.formLayout.addRow("Request Terminal Emulator", self.requestTerminalEmulator) buttonLayout = QHBoxLayout() buttonLayout.setContentsMargins(0, 0, 0, 0) self.cancelButton = QPushButton("Cancel") self.cancelButton.clicked.connect(lambda: self.reject()) self.acceptButton = QPushButton("Accept") self.acceptButton.clicked.connect(lambda: self.accept()) self.acceptButton.setDefault(True) buttonLayout.addStretch(1) buttonLayout.addWidget(self.cancelButton) buttonLayout.addWidget(self.acceptButton) layout.addLayout(titleLayout) layout.addSpacing(10) layout.addLayout(self.formLayout) layout.addStretch(1) layout.addSpacing(10) layout.addLayout(buttonLayout) self.setLayout(layout) self.addressEntry.setText(debug_state.remote_host) self.portEntry.setText(str(debug_state.remote_port)) self.addressEntry.textEdited.connect(lambda: self.apply()) self.portEntry.textEdited.connect(lambda: self.apply()) self.argumentsEntry.setText(' '.join( shlex.quote(arg) for arg in debug_state.command_line_args)) self.argumentsEntry.textEdited.connect(lambda: self.updateArguments()) self.requestTerminalEmulator.setChecked( debug_state.request_terminal_emulator) self.requestTerminalEmulator.stateChanged.connect(lambda: self.apply()) self.accepted.connect(lambda: self.apply()) def selectAdapter(self, adapter): self.bv.store_metadata('debugger.adapter_type', adapter.value) debug_state = binjaplug.get_state(self.bv) debug_state.adapter_type = adapter self.adapterEntry.setText(adapter.name) if DebugAdapter.ADAPTER_TYPE.use_exec(adapter): self.argumentsEntry.setEnabled(True) self.addressEntry.setEnabled(False) self.portEntry.setEnabled(False) elif DebugAdapter.ADAPTER_TYPE.use_connect(adapter): self.argumentsEntry.setEnabled(False) self.addressEntry.setEnabled(True) self.portEntry.setEnabled(True) def apply(self): debug_state = binjaplug.get_state(self.bv) arguments = shlex.split(self.argumentsEntry.text()) debug_state.command_line_args = arguments self.bv.store_metadata('debugger.command_line_args', arguments) address = self.addressEntry.text() port = int(self.portEntry.text()) debug_state.remote_host = address debug_state.remote_port = port self.bv.store_metadata('debugger.remote_host', address) self.bv.store_metadata('debugger.remote_port', port) request_terminal_emulator = self.requestTerminalEmulator.isChecked() debug_state.request_terminal_emulator = request_terminal_emulator self.bv.store_metadata('debugger.request_terminal_emulator', request_terminal_emulator) def updateArguments(self): try: arguments = shlex.split(self.argumentsEntry.text()) self.acceptButton.setEnabled(True) except: self.acceptButton.setEnabled(False)
class VideoFinderAddLink(AddLinkWindow): running_thread = None threadPool = {} def __init__(self, parent, receiver_slot, settings, video_dict={}): super().__init__(parent, receiver_slot, settings, video_dict) self.setWindowTitle( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Video Finder')) self.size_label.hide() # empty lists for no_audio and no_video and video_audio files self.no_audio_list = [] self.no_video_list = [] self.video_audio_list = [] self.media_title = '' # add support for other languages locale = str(self.persepolis_setting.value('settings/locale')) QLocale.setDefault(QLocale(locale)) self.translator = QTranslator() if self.translator.load(':/translations/locales/ui_' + locale, 'ts'): QCoreApplication.installTranslator(self.translator) # extension_label self.extension_label = QLabel(self.link_frame) self.change_name_horizontalLayout.addWidget(self.extension_label) # Fetch Button self.url_submit_pushButtontton = QPushButton(self.link_frame) self.link_horizontalLayout.addWidget(self.url_submit_pushButtontton) # Status Box self.status_box_textEdit = QTextEdit(self.link_frame) self.status_box_textEdit.setMaximumHeight(150) self.link_verticalLayout.addWidget(self.status_box_textEdit) # Select format horizontal layout select_format_horizontalLayout = QHBoxLayout() # Selection Label self.select_format_label = QLabel(self.link_frame) select_format_horizontalLayout.addWidget(self.select_format_label) # Selection combobox self.media_comboBox = QComboBox(self.link_frame) self.media_comboBox.setMinimumWidth(200) select_format_horizontalLayout.addWidget(self.media_comboBox) # Duration label self.duration_label = QLabel(self.link_frame) select_format_horizontalLayout.addWidget(self.duration_label) self.format_selection_frame = QFrame(self) self.format_selection_frame.setLayout(select_format_horizontalLayout) self.link_verticalLayout.addWidget(self.format_selection_frame) # advanced_format_selection_checkBox self.advanced_format_selection_checkBox = QCheckBox(self) self.link_verticalLayout.addWidget( self.advanced_format_selection_checkBox) # advanced_format_selection_frame self.advanced_format_selection_frame = QFrame(self) self.link_verticalLayout.addWidget( self.advanced_format_selection_frame) advanced_format_selection_horizontalLayout = QHBoxLayout( self.advanced_format_selection_frame) # video_format_selection self.video_format_selection_label = QLabel( self.advanced_format_selection_frame) self.video_format_selection_comboBox = QComboBox( self.advanced_format_selection_frame) # audio_format_selection self.audio_format_selection_label = QLabel( self.advanced_format_selection_frame) self.audio_format_selection_comboBox = QComboBox( self.advanced_format_selection_frame) for widget in [ self.video_format_selection_label, self.video_format_selection_comboBox, self.audio_format_selection_label, self.audio_format_selection_comboBox ]: advanced_format_selection_horizontalLayout.addWidget(widget) # Set Texts self.url_submit_pushButtontton.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Fetch Media List')) self.select_format_label.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Select a format')) self.video_format_selection_label.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Video format:')) self.audio_format_selection_label.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Audio format:')) self.advanced_format_selection_checkBox.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Advanced options')) # Add Slot Connections self.url_submit_pushButtontton.setEnabled(False) self.change_name_lineEdit.setEnabled(False) self.ok_pushButton.setEnabled(False) self.download_later_pushButton.setEnabled(False) self.format_selection_frame.setEnabled(True) self.advanced_format_selection_frame.setEnabled(False) self.advanced_format_selection_checkBox.toggled.connect( self.advancedFormatFrame) self.url_submit_pushButtontton.clicked.connect(self.submitClicked) self.media_comboBox.activated.connect( partial(self.mediaSelectionChanged, 'video_audio')) self.video_format_selection_comboBox.activated.connect( partial(self.mediaSelectionChanged, 'video')) self.audio_format_selection_comboBox.activated.connect( partial(self.mediaSelectionChanged, 'audio')) self.link_lineEdit.textChanged.disconnect( super().linkLineChanged) # Should be disconnected. self.link_lineEdit.textChanged.connect(self.linkLineChangedHere) self.setMinimumSize(650, 480) self.status_box_textEdit.hide() self.format_selection_frame.hide() self.advanced_format_selection_frame.hide() self.advanced_format_selection_checkBox.hide() if 'link' in video_dict.keys() and video_dict['link']: self.link_lineEdit.setText(video_dict['link']) self.url_submit_pushButtontton.setEnabled(True) else: # check clipboard clipboard = QApplication.clipboard() text = clipboard.text() if (("tp:/" in text[2:6]) or ("tps:/" in text[2:7])): self.link_lineEdit.setText(str(text)) self.url_submit_pushButtontton.setEnabled(True) def advancedFormatFrame(self, button): if self.advanced_format_selection_checkBox.isChecked(): self.advanced_format_selection_frame.setEnabled(True) self.format_selection_frame.setEnabled(False) self.mediaSelectionChanged( 'video', int(self.video_format_selection_comboBox.currentIndex())) else: self.advanced_format_selection_frame.setEnabled(False) self.format_selection_frame.setEnabled(True) self.mediaSelectionChanged('video_audio', int(self.media_comboBox.currentIndex())) def getReadableSize(self, size): try: return '{:1.2f} MB'.format(int(size) / 1048576) except: return str(size) def getReadableDuration(self, seconds): try: seconds = int(seconds) hours = seconds // 3600 seconds = seconds % 3600 minutes = seconds // 60 seconds = seconds % 60 return '{:02d}:{:02d}:{:02d}'.format(hours, minutes, seconds) except: return str(seconds) # Define native slots def urlChanged(self, value): if ' ' in value or value == '': self.url_submit_pushButtontton.setEnabled(False) self.url_submit_pushButtontton.setToolTip( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Please enter a valid video link')) else: self.url_submit_pushButtontton.setEnabled(True) self.url_submit_pushButtontton.setToolTip('') def submitClicked(self, button=None): # Clear media list self.media_comboBox.clear() self.format_selection_frame.hide() self.advanced_format_selection_checkBox.hide() self.advanced_format_selection_frame.hide() self.video_format_selection_comboBox.clear() self.audio_format_selection_comboBox.clear() self.change_name_lineEdit.clear() self.threadPool.clear() self.change_name_checkBox.setChecked(False) self.video_audio_list.clear() self.no_video_list.clear() self.no_audio_list.clear() self.url_submit_pushButtontton.setEnabled(False) self.status_box_textEdit.setText( QCoreApplication.translate("ytaddlink_src_ui_tr", 'Fetching Media Info...')) self.status_box_textEdit.show() self.ok_pushButton.setEnabled(False) self.download_later_pushButton.setEnabled(False) dictionary_to_send = deepcopy(self.plugin_add_link_dictionary) # More options more_options = self.collectMoreOptions() for k in more_options.keys(): dictionary_to_send[k] = more_options[k] dictionary_to_send['link'] = self.link_lineEdit.text() fetcher_thread = MediaListFetcherThread(self.fetchedResult, dictionary_to_send, self) self.parent.threadPool.append(fetcher_thread) self.parent.threadPool[len(self.parent.threadPool) - 1].start() def fileNameChanged(self, value): if value.strip() == '': self.ok_pushButton.setEnabled(False) def mediaSelectionChanged(self, combobox, index): try: if combobox == 'video_audio': if self.media_comboBox.currentText() == 'Best quality': self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_audio_list[-1]['ext']) else: self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText( '.' + self.video_audio_list[index]['ext']) self.change_name_checkBox.setChecked(True) elif combobox == 'video': if self.video_format_selection_comboBox.currentText( ) != 'No video': self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_audio_list[index - 1]['ext']) self.change_name_checkBox.setChecked(True) else: if self.audio_format_selection_comboBox.currentText( ) != 'No audio': self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_video_list[ int(self.audio_format_selection_comboBox. currentIndex()) - 1]['ext']) self.change_name_checkBox.setChecked(True) else: self.change_name_lineEdit.setChecked(False) elif combobox == 'audio': if self.audio_format_selection_comboBox.currentText( ) != 'No audio' and self.video_format_selection_comboBox.currentText( ) == 'No video': self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_video_list[index - 1]['ext']) self.change_name_checkBox.setChecked(True) elif (self.audio_format_selection_comboBox.currentText() == 'No audio' and self.video_format_selection_comboBox.currentText() != 'No video') or ( self.audio_format_selection_comboBox.currentText() != 'No audio' and self.video_format_selection_comboBox.currentText() != 'No video'): self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_audio_list[ int(self.video_format_selection_comboBox.currentIndex( )) - 1]['ext']) self.change_name_checkBox.setChecked(True) elif self.audio_format_selection_comboBox.currentText( ) == 'No audio' and self.video_format_selection_comboBox.currentText( ) == 'No video': self.change_name_checkBox.setChecked(False) except Exception as ex: logger.sendToLog(ex, "ERROR") def fetchedResult(self, media_dict): self.url_submit_pushButtontton.setEnabled(True) if 'error' in media_dict.keys(): self.status_box_textEdit.setText('<font color="#f11">' + str(media_dict['error']) + '</font>') self.status_box_textEdit.show() else: # Show the media list # add no audio and no video options to the comboboxes self.video_format_selection_comboBox.addItem('No video') self.audio_format_selection_comboBox.addItem('No audio') self.media_title = media_dict['title'] if 'formats' not in media_dict.keys( ) and 'entries' in media_dict.keys(): formats = media_dict['entries'] formats = formats[0] media_dict['formats'] = formats['formats'] elif 'formats' not in media_dict.keys( ) and 'format' in media_dict.keys(): media_dict['formats'] = [media_dict.copy()] try: i = 0 for f in media_dict['formats']: no_audio = False no_video = False text = '' if 'acodec' in f.keys(): # only video, no audio if f['acodec'] == 'none': no_audio = True # resolution if 'height' in f.keys(): text = text + ' ' + '{}p'.format(f['height']) if 'vcodec' in f.keys(): # if f['vcodec'] == 'none' and f['acodec'] != 'none': # continue # No video, show audio bit rate if f['vcodec'] == 'none': text = text + '{}kbps'.format(f['abr']) no_video = True if 'ext' in f.keys(): text = text + ' ' + '.{}'.format(f['ext']) if 'filesize' in f.keys() and f['filesize']: # Youtube api does not supply file size for some formats, so check it. text = text + ' ' + '{}'.format( self.getReadableSize(f['filesize'])) else: # Start spider to find file size input_dict = deepcopy(self.plugin_add_link_dictionary) input_dict['link'] = f['url'] more_options = self.collectMoreOptions() for key in more_options.keys(): input_dict[key] = more_options[key] size_fetcher = FileSizeFetcherThread(input_dict, i) self.threadPool[str(i)] = { 'thread': size_fetcher, 'item_id': i } self.parent.threadPool.append(size_fetcher) self.parent.threadPool[len(self.parent.threadPool) - 1].start() self.parent.threadPool[len(self.parent.threadPool) - 1].FOUND.connect( self.findFileSize) # Add current format to the related comboboxes if no_audio: self.no_audio_list.append(f) self.video_format_selection_comboBox.addItem(text) elif no_video: self.no_video_list.append(f) self.audio_format_selection_comboBox.addItem(text) else: self.video_audio_list.append(f) self.media_comboBox.addItem(text) i = i + 1 self.status_box_textEdit.hide() if 'duration' in media_dict.keys(): self.duration_label.setText( 'Duration ' + self.getReadableDuration(media_dict['duration'])) self.format_selection_frame.show() self.advanced_format_selection_checkBox.show() self.advanced_format_selection_frame.show() self.ok_pushButton.setEnabled(True) self.download_later_pushButton.setEnabled(True) # if we have no options for separate audio and video, then hide advanced_format_selection... if len(self.no_audio_list) == 0 and len( self.no_video_list) == 0: self.advanced_format_selection_checkBox.hide() self.advanced_format_selection_frame.hide() # set index of comboboxes on best available quality. # we have both audio and video if len(self.no_audio_list) != 0 and len( self.no_video_list) != 0: self.media_comboBox.addItem('Best quality') self.media_comboBox.setCurrentIndex( len(self.video_audio_list)) self.change_name_lineEdit.setText(self.media_title) self.extension_label.setText('.' + self.no_audio_list[-1]['ext']) self.change_name_checkBox.setChecked(True) # video and audio are not separate elif len(self.video_audio_list) != 0: self.media_comboBox.setCurrentIndex( len(self.video_audio_list) - 1) if len(self.no_audio_list) != 0: self.video_format_selection_comboBox.setCurrentIndex( len(self.no_audio_list)) if len(self.no_video_list) != 0: self.audio_format_selection_comboBox.setCurrentIndex( len(self.no_video_list)) # if we have only audio or we have only video then hide media_comboBox if len(self.video_audio_list) == 0: self.media_comboBox.hide() self.select_format_label.hide() # only video if len(self.no_video_list) != 0 and len( self.no_audio_list) == 0: self.mediaSelectionChanged( 'video', int(self.video_format_selection_comboBox. currentIndex())) self.advanced_format_selection_checkBox.setChecked( True) self.advanced_format_selection_checkBox.hide() # only audio elif len(self.no_video_list) == 0 and len( self.no_audio_list) != 0: self.mediaSelectionChanged( 'audio', int(self.audio_format_selection_comboBox. currentIndex())) self.advanced_format_selection_checkBox.setChecked( True) self.advanced_format_selection_checkBox.hide() # audio and video else: self.mediaSelectionChanged( 'video_audio', int(self.media_comboBox.currentIndex())) except Exception as ex: logger.sendToLog(ex, "ERROR") def findFileSize(self, result): try: item_id = self.threadPool[str(result['thread_key'])]['item_id'] if result['file_size'] and result['file_size'] != '0': text = self.media_comboBox.itemText(item_id) self.media_comboBox.setItemText( item_id, '{} - {}'.format(text, result['file_size'])) except Exception as ex: logger.sendToLog(ex, "ERROR") def linkLineChangedHere(self, lineEdit): if str(lineEdit) == '': self.url_submit_pushButtontton.setEnabled(False) else: self.url_submit_pushButtontton.setEnabled(True) # This method collects additional information like proxy ip, user, password etc. def collectMoreOptions(self): options = { 'ip': None, 'port': None, 'proxy_user': None, 'proxy_passwd': None, 'download_user': None, 'download_passwd': None } if self.proxy_checkBox.isChecked(): options['ip'] = self.ip_lineEdit.text() options['port'] = self.port_spinBox.value() options['proxy_user'] = self.proxy_user_lineEdit.text() options['proxy_passwd'] = self.proxy_pass_lineEdit.text() if self.download_checkBox.isChecked(): options['download_user'] = self.download_user_lineEdit.text() options['download_passwd'] = self.download_pass_lineEdit.text() # These info (keys) are required for spider to find file size, because spider() does not check if key exists. additional_info = [ 'header', 'load_cookies', 'user_agent', 'referer', 'out' ] for i in additional_info: if i not in self.plugin_add_link_dictionary.keys(): options[i] = None return options # user submitted information by pressing ok_pushButton, so get information # from VideoFinderAddLink window and return them to the mainwindow with callback! def okButtonPressed(self, download_later, button=None): link_list = [] # separate audio format and video format is selected. if self.advanced_format_selection_checkBox.isChecked(): if self.video_format_selection_comboBox.currentText( ) == 'No video' and self.audio_format_selection_comboBox.currentText( ) != 'No audio': # only audio link must be added to the link_list audio_link = self.no_video_list[ self.audio_format_selection_comboBox.currentIndex() - 1]['url'] link_list.append(audio_link) elif self.video_format_selection_comboBox.currentText( ) != 'No video' and self.audio_format_selection_comboBox.currentText( ) == 'No audio': # only video link must be added to the link_list video_link = self.no_audio_list[ self.video_format_selection_comboBox.currentIndex() - 1]['url'] link_list.append(video_link) elif self.video_format_selection_comboBox.currentText( ) != 'No video' and self.audio_format_selection_comboBox.currentText( ) != 'No audio': # video and audio links must be added to the link_list audio_link = self.no_video_list[ self.audio_format_selection_comboBox.currentIndex() - 1]['url'] video_link = self.no_audio_list[ self.video_format_selection_comboBox.currentIndex() - 1]['url'] link_list = [video_link, audio_link] elif self.video_format_selection_comboBox.currentText( ) == 'No video' and self.audio_format_selection_comboBox.currentText( ) == 'No audio': # no video and audio is selected! REALLY?!. user is DRUNK! close the window! :)) self.close() else: if self.media_comboBox.currentText() == 'Best quality': # the last item in no_video_list and no_audio_list are the best. video_link = self.no_audio_list[-1]['url'] audio_link = self.no_video_list[-1]['url'] link_list = [video_link, audio_link] else: audio_and_video_link = self.video_audio_list[ self.media_comboBox.currentIndex()]['url'] link_list.append(audio_and_video_link) # write user's new inputs in persepolis_setting for next time :) self.persepolis_setting.setValue('add_link_initialization/ip', self.ip_lineEdit.text()) self.persepolis_setting.setValue('add_link_initialization/port', self.port_spinBox.value()) self.persepolis_setting.setValue('add_link_initialization/proxy_user', self.proxy_user_lineEdit.text()) self.persepolis_setting.setValue( 'add_link_initialization/download_user', self.download_user_lineEdit.text()) # get proxy information if not (self.proxy_checkBox.isChecked()): ip = None port = None proxy_user = None proxy_passwd = None else: ip = self.ip_lineEdit.text() if not (ip): ip = None port = self.port_spinBox.value() if not (port): port = None proxy_user = self.proxy_user_lineEdit.text() if not (proxy_user): proxy_user = None proxy_passwd = self.proxy_pass_lineEdit.text() if not (proxy_passwd): proxy_passwd = None # get download username and password information if not (self.download_checkBox.isChecked()): download_user = None download_passwd = None else: download_user = self.download_user_lineEdit.text() if not (download_user): download_user = None download_passwd = self.download_pass_lineEdit.text() if not (download_passwd): download_passwd = None # check that if user limits download speed. if not (self.limit_checkBox.isChecked()): limit = 0 else: if self.limit_comboBox.currentText() == "KiB/s": limit = str(self.limit_spinBox.value()) + str("K") else: limit = str(self.limit_spinBox.value()) + str("M") # get start time for download if user set that. if not (self.start_checkBox.isChecked()): start_time = None else: start_time = self.start_time_qDataTimeEdit.text() # get end time for download if user set that. if not (self.end_checkBox.isChecked()): end_time = None else: end_time = self.end_time_qDateTimeEdit.text() # set name for file(s) if self.change_name_checkBox.isChecked(): name = str(self.change_name_lineEdit.text()) if name == '': name = 'video_finder_file' else: name = 'video_finder_file' # video finder always finds extension # but if it can't find file extension # use mp4 for extension. if str(self.extension_label.text()) == '': extension = '.mp4' else: extension = str(self.extension_label.text()) # did user select separate audio and video? if len(link_list) == 2: video_name = name + extension audio_name = name + '.' + \ str(self.no_video_list[self.audio_format_selection_comboBox.currentIndex() - 1]['ext']) name_list = [video_name, audio_name] else: name_list = [name + extension] # get number of connections connections = self.connections_spinBox.value() # get download_path download_path = self.download_folder_lineEdit.text() # referer if self.referer_lineEdit.text() != '': referer = self.referer_lineEdit.text() else: referer = None # header if self.header_lineEdit.text() != '': header = self.header_lineEdit.text() else: header = None # user_agent if self.user_agent_lineEdit.text() != '': user_agent = self.user_agent_lineEdit.text() else: user_agent = None # load_cookies if self.load_cookies_lineEdit.text() != '': load_cookies = self.load_cookies_lineEdit.text() else: load_cookies = None add_link_dictionary_list = [] if len(link_list) == 1: # save information in a dictionary(add_link_dictionary). add_link_dictionary = { 'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies, 'out': name_list[0], 'start_time': start_time, 'end_time': end_time, 'link': link_list[0], 'ip': ip, 'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd, 'download_user': download_user, 'download_passwd': download_passwd, 'connections': connections, 'limit_value': limit, 'download_path': download_path } add_link_dictionary_list.append(add_link_dictionary) else: video_add_link_dictionary = { 'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies, 'out': name_list[0], 'start_time': start_time, 'end_time': end_time, 'link': link_list[0], 'ip': ip, 'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd, 'download_user': download_user, 'download_passwd': download_passwd, 'connections': connections, 'limit_value': limit, 'download_path': download_path } audio_add_link_dictionary = { 'referer': referer, 'header': header, 'user_agent': user_agent, 'load_cookies': load_cookies, 'out': name_list[1], 'start_time': None, 'end_time': end_time, 'link': link_list[1], 'ip': ip, 'port': port, 'proxy_user': proxy_user, 'proxy_passwd': proxy_passwd, 'download_user': download_user, 'download_passwd': download_passwd, 'connections': connections, 'limit_value': limit, 'download_path': download_path } add_link_dictionary_list = [ video_add_link_dictionary, audio_add_link_dictionary ] # get category of download category = str(self.add_queue_comboBox.currentText()) del self.plugin_add_link_dictionary # return information to mainwindow self.callback(add_link_dictionary_list, download_later, category) # close window self.close()
class UITransferROIWindow: def setup_ui(self, transfer_roi_window_instance, signal_roi_transferred_to_fixed_container, signal_roi_transferred_to_moving_container): self.patient_dict_container = PatientDictContainer() self.moving_dict_container = MovingDictContainer() self.fixed_image_initial_rois = self.patient_dict_container.get("rois") self.moving_image_initial_rois = self.moving_dict_container.get("rois") self.transfer_roi_window_instance = transfer_roi_window_instance self.signal_roi_transferred_to_fixed_container = \ signal_roi_transferred_to_fixed_container self.signal_roi_transferred_to_moving_container = \ signal_roi_transferred_to_moving_container self.fixed_to_moving_rois = {} self.moving_to_fixed_rois = {} self.add_suffix = True self.progress_window = ProgressWindow( self, QtCore.Qt.WindowTitleHint | QtCore.Qt.WindowCloseButtonHint) self.progress_window.setFixedSize(250, 100) self.progress_window.signal_loaded \ .connect(self.onTransferRoiFinished) self.progress_window.signal_error.connect(self.onTransferRoiError) self.init_layout() def retranslate_ui(self, transfer_roi_window_instance): _translate = QtCore.QCoreApplication.translate transfer_roi_window_instance.setWindowTitle( _translate("TransferRoiWindowInstance", "OnkoDICOM - Transfer Region of Interest")) self.add_suffix_checkbox.setText( _translate("AddSuffixCheckBox", "Add Suffix")) self.patient_A_label.setText( _translate("PatientAROILabel", "First Image Set ROIs")) self.patient_B_label.setText( _translate("PatientBROILabel", "Second Image Set ROIs")) self.transfer_all_rois_to_patient_B_button.setText( _translate("ROITransferToBButton", "All")) self.transfer_all_rois_to_patient_A_button.setText( _translate("ROITransferToAButton", "All")) self.save_button.setText(_translate("SaveButton", "Save")) self.reset_button.setText(_translate("ResetButton", "Reset")) def init_layout(self): """ Initialize the layout for the Transfer ROI Window. """ if platform.system() == 'Darwin': self.stylesheet_path = "res/stylesheet.qss" else: self.stylesheet_path = "res/stylesheet-win-linux.qss" stylesheet = open(resource_path(self.stylesheet_path)).read() window_icon = QIcon() window_icon.addPixmap(QPixmap(resource_path("res/images/icon.ico")), QIcon.Normal, QIcon.Off) self.transfer_roi_window_instance.setObjectName( "TransferRoiWindowInstance") self.transfer_roi_window_instance.setWindowIcon(window_icon) # Creating a grid layout to hold all elements self.transfer_roi_window_grid_layout = QGridLayout() self.transfer_roi_window_grid_layout.setColumnStretch(0, 1) self.transfer_roi_window_grid_layout.setColumnStretch(1, 1) self.transfer_roi_window_grid_layout.setColumnStretch(2, 1) self.init_patient_labels() self.init_transfer_arrow_buttons() self.init_patient_A_initial_roi_list() self.init_patient_B_rois_to_A_layout() self.init_patient_A_rois_to_B_layout() self.init_patient_B_initial_roi_list() self.init_add_suffix_checkbox() self.init_save_and_reset_button_layout() # Create a new central widget to hold the grid layout self.transfer_roi_window_instance_central_widget = QWidget() self.transfer_roi_window_instance_central_widget.setLayout( self.transfer_roi_window_grid_layout) self.retranslate_ui(self.transfer_roi_window_instance) self.transfer_roi_window_instance.setStyleSheet(stylesheet) self.transfer_roi_window_instance.setCentralWidget( self.transfer_roi_window_instance_central_widget) QtCore.QMetaObject.connectSlotsByName( self.transfer_roi_window_instance) def init_transfer_arrow_buttons(self): """ Initialize the layout for arrow buttons """ self.transfer_all_rois_to_patient_B_button = QPushButton() self.transfer_all_rois_to_patient_B_button.setObjectName( "ROITransferToBButton") transfer_all_rois_to_patient_B_icon = QtGui.QIcon() transfer_all_rois_to_patient_B_icon.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/forward_slide_icon.png')), QtGui.QIcon.Normal, QtGui.QIcon.On) self.transfer_all_rois_to_patient_B_button \ .setIcon(transfer_all_rois_to_patient_B_icon) self.transfer_all_rois_to_patient_B_button.clicked.connect( self.transfer_all_rois_to_patient_B_button_clicked) self.transfer_roi_window_grid_layout.addWidget( self.transfer_all_rois_to_patient_B_button, 1, 1) self.transfer_all_rois_to_patient_A_button = QPushButton() self.transfer_all_rois_to_patient_A_button.setObjectName( "ROITransferToAButton") self.transfer_all_rois_to_patient_A_button.setMaximumWidth(100) transfer_all_rois_to_patient_A_icon = QtGui.QIcon() transfer_all_rois_to_patient_A_icon.addPixmap( QtGui.QPixmap( resource_path('res/images/btn-icons/backward_slide_icon.png')), QtGui.QIcon.Normal, QtGui.QIcon.On) self.transfer_all_rois_to_patient_A_button \ .setIcon(transfer_all_rois_to_patient_A_icon) self.transfer_all_rois_to_patient_A_button.clicked.connect( self.transfer_all_rois_to_patient_A_button_clicked) self.transfer_roi_window_grid_layout.addWidget( self.transfer_all_rois_to_patient_A_button, 2, 1) def transfer_all_rois_to_patient_B_button_clicked(self): """ This function is triggered when the right arrow button is clicked. """ self.fixed_to_moving_rois.clear() self.patient_A_rois_to_B_list_widget.clear() for i in range(0, len(self.fixed_image_initial_rois)): self.patient_A_initial_roi_double_clicked( self.patient_A_initial_rois_list_widget.item(i)) def transfer_all_rois_to_patient_A_button_clicked(self): """ This function is triggered when the left arrow button is clicked. """ self.moving_to_fixed_rois.clear() self.patient_B_rois_to_A_list_widget.clear() for i in range(0, len(self.moving_image_initial_rois)): self.patient_B_initial_roi_double_clicked( self.patient_B_initial_rois_list_widget.item(i)) def init_add_suffix_checkbox(self): """ Initialize the layout for add suffix checkbox """ self.add_suffix_checkbox = QCheckBox() self.add_suffix_checkbox.setObjectName("AddSuffixCheckBox") self.add_suffix_checkbox.setChecked(self.add_suffix) self.add_suffix_checkbox.clicked.connect( self.add_suffix_checkbox_clicked) self.transfer_roi_window_grid_layout.addWidget( self.add_suffix_checkbox, 3, 0) def init_patient_labels(self): """ Initialize the layout for two patient labels """ self.patient_A_label = QLabel() self.patient_A_label.setObjectName("PatientAROILabel") self.patient_A_label.setMinimumHeight(50) self.patient_A_label.setAlignment(Qt.AlignCenter) self.patient_A_label.setStyleSheet( "QLabel { background-color : green; color : white; " "font-size: 15pt; font-weight: bold;}") self.patient_B_label = QLabel() self.patient_B_label.setObjectName("PatientBROILabel") self.patient_B_label.setMinimumHeight(50) self.patient_B_label.setAlignment(Qt.AlignCenter) self.patient_B_label.setStyleSheet( "QLabel { background-color : red; color : white; " "font-size: 15pt; font-weight: bold;}") self.transfer_roi_window_grid_layout.addWidget(self.patient_A_label, 0, 0) self.transfer_roi_window_grid_layout.addWidget(self.patient_B_label, 0, 2) def init_save_and_reset_button_layout(self): """ Initialize the layout for save and reset buttons """ self.reset_and_save_buttons_layout = QHBoxLayout() self.reset_button = QPushButton() self.reset_button.setObjectName("ResetButton") self.reset_button.clicked.connect(self.reset_clicked) self.save_button = QPushButton() self.save_button.setObjectName("SaveButton") self.save_button.setDisabled(True) self.save_button.clicked.connect(self.transfer_roi_clicked) self.reset_and_save_buttons_layout.setAlignment(Qt.AlignRight) self.reset_and_save_buttons_layout.addWidget(self.reset_button) self.reset_and_save_buttons_layout.addWidget(self.save_button) # Create a widget to hold Reset and Save buttons self.reset_and_save_button_central_widget = QWidget() self.reset_and_save_button_central_widget.setLayout( self.reset_and_save_buttons_layout) self.transfer_roi_window_grid_layout.addWidget( self.reset_and_save_button_central_widget, 3, 2) def add_suffix_checkbox_clicked(self): """ This function is triggered when the add suffix checkbox is clicked """ self.add_suffix = self.add_suffix_checkbox.isChecked() def init_patient_B_rois_to_A_layout(self): """ Initialize the layout for transfer rois from B to A container """ # Create scrolling area widget to contain the content. self.patient_B_rois_to_A_list_widget = QListWidget(self) self.transfer_roi_window_grid_layout \ .addWidget(self.patient_B_rois_to_A_list_widget, 2, 0) self.patient_B_rois_to_A_list_widget.itemDoubleClicked.connect( self.patient_B_to_A_rois_double_clicked) def init_patient_A_rois_to_B_layout(self): """ Initialize the layout for transfer rois from A to B container """ self.patient_A_rois_to_B_list_widget = QListWidget(self) self.transfer_roi_window_grid_layout \ .addWidget(self.patient_A_rois_to_B_list_widget, 1, 2) self.patient_A_rois_to_B_list_widget.itemDoubleClicked.connect( self.patient_A_to_B_rois_double_clicked) def init_patient_A_initial_roi_list(self): """ Initialize the layout for patient A's roi list """ self.patient_A_initial_rois_list_widget = QListWidget(self) self.patient_A_initial_rois_list_widget.itemDoubleClicked.connect( self.patient_A_initial_roi_double_clicked) for idx in self.fixed_image_initial_rois: roi_label = QListWidgetItem( self.fixed_image_initial_rois[idx]['name']) roi_label.setForeground(Qt.darkGreen) roi_label.setData(Qt.UserRole, self.fixed_image_initial_rois[idx]) self.patient_A_initial_rois_list_widget.addItem(roi_label) self.transfer_roi_window_grid_layout.addWidget( self.patient_A_initial_rois_list_widget, 1, 0) def init_patient_B_initial_roi_list(self): """ Initialize the layout for patient B's roi list """ self.patient_B_initial_rois_list_widget = QListWidget(self) self.patient_B_initial_rois_list_widget.itemDoubleClicked.connect( self.patient_B_initial_roi_double_clicked) for idx in self.moving_image_initial_rois: roi_label = QListWidgetItem( self.moving_image_initial_rois[idx]['name']) roi_label.setForeground(Qt.red) roi_label.setData(Qt.UserRole, self.moving_image_initial_rois[idx]) self.patient_B_initial_rois_list_widget.addItem(roi_label) self.transfer_roi_window_grid_layout.addWidget( self.patient_B_initial_rois_list_widget, 2, 2) def patient_A_to_B_rois_double_clicked(self, item): """ This function is triggered when a roi in "A to B" list is double-clicked. """ roi_to_remove = item.data(Qt.UserRole) to_delete_value = roi_to_remove['name'] self.fixed_to_moving_rois.pop(to_delete_value) self.patient_A_rois_to_B_list_widget.clear() for key, value in self.fixed_to_moving_rois.items(): roi_label = QListWidgetItem(value) roi_label.setForeground(Qt.red) roi_label.setData(Qt.UserRole, {'name': key}) self.patient_A_rois_to_B_list_widget.addItem(roi_label) if self.transfer_list_is_empty(): self.save_button.setDisabled(True) def patient_B_to_A_rois_double_clicked(self, item): """ This function is triggered when a roi in "B to A" list is double-clicked. """ roi_to_remove = item.data(Qt.UserRole) to_delete_value = roi_to_remove['name'] self.moving_to_fixed_rois.pop(to_delete_value) self.patient_B_rois_to_A_list_widget.clear() for key, value in self.moving_to_fixed_rois.items(): roi_label = QListWidgetItem(value) roi_label.setForeground(Qt.red) roi_label.setData(Qt.UserRole, {'name': key}) self.patient_B_rois_to_A_list_widget.addItem(roi_label) if self.transfer_list_is_empty(): self.save_button.setDisabled(True) def patient_A_initial_roi_double_clicked(self, item): """ This function is triggered when a roi in patient A's roi list is double-clicked. """ roi_to_add = item.data(Qt.UserRole) transferred_roi_name = roi_to_add['name'] # If the clicked roi is already transferred, return if transferred_roi_name in self.fixed_to_moving_rois.keys(): QMessageBox.about(self, "Transfer Failed", "Chosen ROI has already been transferred!") return # Create a set to store all current roi names in target patient # including both initial rois name and added roi names so far patient_B_initial_roi_name_list = set() for item in self.fixed_to_moving_rois.values(): patient_B_initial_roi_name_list.add(item) for idx in self.moving_image_initial_rois: patient_B_initial_roi_name_list.add( self.moving_image_initial_rois[idx]['name']) # Check if clicked roi name has duplicate # in patient B's initial roi names list if transferred_roi_name in patient_B_initial_roi_name_list: if self.add_suffix: transferred_roi_name = generate_non_duplicated_name( transferred_roi_name, patient_B_initial_roi_name_list) else: QMessageBox.about( self, "Transfer Failed", "Duplicated ROI name. " "Please consider adding suffix.") return # Add clicked roi to transferred list self.fixed_to_moving_rois[roi_to_add['name']] = transferred_roi_name roi_label = QListWidgetItem(transferred_roi_name) roi_label.setForeground(Qt.red) roi_label.setData(Qt.UserRole, roi_to_add) self.patient_A_rois_to_B_list_widget.addItem(roi_label) self.save_button.setDisabled(False) def patient_B_initial_roi_double_clicked(self, item): """ This function is triggered when a roi in patient B's roi list is double-clicked. """ roi_to_add = item.data(Qt.UserRole) transferred_roi_name = roi_to_add['name'] # If the clicked roi is already transferred, return if transferred_roi_name in self.moving_to_fixed_rois.keys(): QMessageBox.about(self, "Transfer Failed", "Chosen ROI has already been transferred!") return # Create a set to store all current roi names in target patient # including both initial rois name and added roi names so far patient_A_current_roi_name_list = set() for item in self.moving_to_fixed_rois.values(): patient_A_current_roi_name_list.add(item) for idx in self.fixed_image_initial_rois: patient_A_current_roi_name_list.add( self.fixed_image_initial_rois[idx]['name']) # Check if clicked roi name has duplicate in # target patient's roi names list if transferred_roi_name in patient_A_current_roi_name_list: # if add suffix is ticked, iteratively try adding suffix # from _A to _Z, stop when no duplicate found if self.add_suffix: transferred_roi_name = generate_non_duplicated_name( transferred_roi_name, patient_A_current_roi_name_list) else: QMessageBox.about( self, "Transfer Failed", "Duplicated ROI name. " "Please consider adding suffix.") return # Add clicked roi to transferred list self.moving_to_fixed_rois[roi_to_add['name']] = transferred_roi_name roi_label = QListWidgetItem(transferred_roi_name) roi_label.setForeground(Qt.red) roi_label.setData(Qt.UserRole, roi_to_add) self.patient_B_rois_to_A_list_widget.addItem(roi_label) self.save_button.setDisabled(False) def reset_clicked(self): """ This function is triggered when reset button is clicked. """ self.fixed_to_moving_rois.clear() self.moving_to_fixed_rois.clear() self.patient_A_rois_to_B_list_widget.clear() self.patient_B_rois_to_A_list_widget.clear() self.save_button.setDisabled(True) def transfer_list_is_empty(self): """ This function is to check if the transfer list is empty """ return len(self.fixed_to_moving_rois) == 0 \ and len(self.moving_to_fixed_rois) == 0 def save_clicked(self, interrupt_flag, progress_callback): """ This function is triggered when the save button is clicked. It contains all steps in the ROI transferring process. :param interrupt_flag: interrupt flag to stop process :param progress_callback: signal that receives the current progress of the loading. """ progress_callback.emit(("Converting images to sitk", 0)) # check if interrupt flag is set if not check_interrupt_flag(interrupt_flag): return False rtss = self.patient_dict_container.get("dataset_rtss") # get sitk for the fixed image dicom_image = read_dicom_image_to_sitk( self.patient_dict_container.filepaths) if not check_interrupt_flag(interrupt_flag): return False # get array of roi indexes from sitk images rois_images_fixed = transform_point_set_from_dicom_struct( dicom_image, rtss, self.fixed_to_moving_rois.keys(), spacing_override=None, interrupt_flag=interrupt_flag) moving_rtss = self.moving_dict_container.get("dataset_rtss") if not check_interrupt_flag(interrupt_flag): return False # get sitk for the moving image moving_dicom_image = read_dicom_image_to_sitk( self.moving_dict_container.filepaths) if not check_interrupt_flag(interrupt_flag): return False # get array of roi indexes from sitk images progress_callback \ .emit(("Retrieving ROIs from \nboth image sets", 20)) # check if interrupt flag is set if not check_interrupt_flag(interrupt_flag): return False if moving_rtss: rois_images_moving = transform_point_set_from_dicom_struct( moving_dicom_image, moving_rtss, self.moving_to_fixed_rois.keys(), spacing_override=None, interrupt_flag=interrupt_flag) else: rois_images_moving = ([], []) if not check_interrupt_flag(interrupt_flag): return False tfm = self.moving_dict_container.get("tfm") progress_callback.emit( ("Transfering ROIs from moving \nto fixed image set", 40)) # check if interrupt flag is set if not check_interrupt_flag(interrupt_flag): return False # transform roi from moving_dict to fixed_dict self.transfer_rois(self.moving_to_fixed_rois, tfm, dicom_image, rois_images_moving, self.patient_dict_container) progress_callback.emit( ("Transfering ROIs from fixed \nto moving image set", 60)) if not check_interrupt_flag(interrupt_flag): return False # transform roi from moving_dict to fixed_dict self.transfer_rois(self.fixed_to_moving_rois, tfm.GetInverse(), moving_dicom_image, rois_images_fixed, self.moving_dict_container) progress_callback.emit(("Saving ROIs to RTSS", 80)) # check if interrupt flag is set if not check_interrupt_flag(interrupt_flag): return False progress_callback.emit(("Reloading window", 90)) return True def transfer_roi_clicked(self): """ telling progress window to start ROI transfer """ self.progress_window.start(self.save_clicked) def onTransferRoiError(self, exception): """ This function is triggered when there is an error in the ROI transferring process. :param exception: exception thrown """ QMessageBox.about(self.progress_window, "Unable to transfer ROIs", "Please check your image set and ROI data.") self.progress_window.close() def onTransferRoiFinished(self, result): """ This function is triggered when ROI transferring process is finished. """ # emit changed dataset to structure_modified function and # auto_save_roi function if result[0] is True: if len(self.fixed_to_moving_rois) > 0: self.signal_roi_transferred_to_moving_container.emit( (self.moving_dict_container.get("dataset_rtss"), { "transfer": None })) if len(self.moving_to_fixed_rois) > 0: self.signal_roi_transferred_to_fixed_container.emit( (self.patient_dict_container.get("dataset_rtss"), { "transfer": None })) self.progress_window.close() QMessageBox.about(self.transfer_roi_window_instance, "Saved", "ROIs are successfully transferred!") else: QMessageBox.about(self.transfer_roi_window_instance, "Cancelled", "ROIs Transfer is cancelled.") self.closeWindow() def transfer_rois(self, transfer_dict, tfm, reference_image, original_roi_list, patient_dict_container): """ Converting (transferring) ROIs from one image set to another and save the transferred rois to rtss. :param transfer_dict: dictionary of rois to be transfer. key is original roi names, value is the name after transferred. :param original_roi_list: tuple of sitk rois from the base image. :param tfm: the tfm that contains information for transferring rois :param reference_image: the reference (base) image :param patient_dict_container: container of the transfer image set. """ for roi_name, new_roi_name in transfer_dict.items(): for index, name in enumerate(original_roi_list[1]): if name == roi_name: sitk_image = original_roi_list[0][index] new_contour = apply_linear_transform( input_image=sitk_image, transform=tfm, reference_image=reference_image, is_structure=True) contour = sitk.GetArrayViewFromImage(new_contour) contours = np.transpose(contour.nonzero()) self.save_roi_to_patient_dict_container( contours, new_roi_name, patient_dict_container) def save_roi_to_patient_dict_container(self, contours, roi_name, patient_dict_container): """ Save the transferred ROI to the corresponding rtss. :param contours: np array of coordinates of the ROI to be saved. :param roi_name: name of the ROI to be saved :param patient_dict_container: container of the transfer image set. """ pixels_coords_dict = {} slice_ids_dict = get_dict_slice_to_uid(patient_dict_container) total_slices = len(slice_ids_dict) for contour in contours: curr_slice_id = total_slices - contour[0] if curr_slice_id >= total_slices: curr_slice_id = 0 if curr_slice_id not in pixels_coords_dict: pixels_coords_dict[curr_slice_id] = [ tuple([contour[2], contour[1]]) ] else: pixels_coords_dict[curr_slice_id].append( tuple([contour[2], contour[1]])) rois_to_save = {} for key in pixels_coords_dict.keys(): coords = pixels_coords_dict[key] polygon_list = ROI.calculate_concave_hull_of_points(coords) if len(polygon_list) > 0: rois_to_save[key] = { 'ds': patient_dict_container.dataset[key], 'coords': polygon_list } roi_list = ROI.convert_hull_list_to_contours_data( rois_to_save, patient_dict_container) if len(roi_list) > 0: print("Saving ", roi_name) if isinstance(patient_dict_container, MovingDictContainer): new_rtss = ROI.create_roi( patient_dict_container.get("dataset_rtss"), roi_name, roi_list, rtss_owner="MOVING") self.moving_dict_container.set("dataset_rtss", new_rtss) self.moving_dict_container.set("rtss_modified", True) else: new_rtss = ROI.create_roi( patient_dict_container.get("dataset_rtss"), roi_name, roi_list) self.patient_dict_container.set("dataset_rtss", new_rtss) self.patient_dict_container.set("rtss_modified", True) def closeWindow(self): """ function to close transfer roi window """ self.close()
class SettingsWindow(QDialog): def __init__(self, parent: Optional[QWidget] = None, firstStart: bool = False) -> None: super().__init__(parent, ) if parent: self.setWindowTitle('Settings') else: self.setWindowTitle(getTitleString('Settings')) self.setAttribute(Qt.WA_DeleteOnClose) settings = QSettings() mainLayout = QVBoxLayout(self) mainLayout.setContentsMargins(5, 5, 5, 5) # First Start info if firstStart: firstStartInfo = QLabel( ''' <p><strong>Hello! It looks like this is your first time using w3modmanager, or the game installation path recently changed.</strong></p> <p> Please review the settings below. </p> ''', self) firstStartInfo.setWordWrap(True) firstStartInfo.setContentsMargins(10, 10, 10, 10) firstStartInfo.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) mainLayout.addWidget(firstStartInfo) # Game gbGame = QGroupBox('Game Path', self) mainLayout.addWidget(gbGame) gbGameLayout = QVBoxLayout(gbGame) gamePathLayout = QHBoxLayout() self.gamePath = QLineEdit(self) self.gamePath.setPlaceholderText('Path to witcher3.exe...') if settings.value('gamePath'): self.gamePath.setText(str(settings.value('gamePath'))) self.gamePath.textChanged.connect( lambda: self.validateGamePath(self.gamePath.text())) gamePathLayout.addWidget(self.gamePath) self.locateGame = QPushButton('Detect', self) self.locateGame.clicked.connect(self.locateGameEvent) self.locateGame.setToolTip( 'Automatically detect the game path if possible') gamePathLayout.addWidget(self.locateGame) selectGame = QPushButton('Browse', self) selectGame.clicked.connect(self.selectGameEvent) gamePathLayout.addWidget(selectGame) gbGameLayout.addLayout(gamePathLayout) gamePathInfoLayout = QHBoxLayout() self.gamePathInfo = QLabel('', self) self.gamePathInfo.setContentsMargins(4, 4, 4, 4) self.gamePathInfo.setMinimumHeight(40) self.gamePathInfo.setWordWrap(True) gamePathInfoLayout.addWidget(self.gamePathInfo) gbGameLayout.addLayout(gamePathInfoLayout) # Config gbConfig = QGroupBox('Game Config', self) mainLayout.addWidget(gbConfig) gbConfigLayout = QVBoxLayout(gbConfig) configPathLayout = QHBoxLayout() self.configPath = QLineEdit(self) self.configPath.setPlaceholderText('Path to config folder...') if settings.value('configPath'): self.configPath.setText(str(settings.value('configPath'))) self.configPath.textChanged.connect( lambda: self.validateConfigPath(self.configPath.text())) configPathLayout.addWidget(self.configPath) self.locateConfig = QPushButton('Detect', self) self.locateConfig.clicked.connect(self.locateConfigEvent) self.locateConfig.setToolTip( 'Automatically detect the config folder if possible') configPathLayout.addWidget(self.locateConfig) selectConfig = QPushButton('Browse', self) selectConfig.clicked.connect(self.selectConfigEvent) configPathLayout.addWidget(selectConfig) gbConfigLayout.addLayout(configPathLayout) configPathInfoLayout = QHBoxLayout() self.configPathInfo = QLabel('', self) self.configPathInfo.setContentsMargins(4, 4, 4, 4) self.configPathInfo.setMinimumHeight(40) self.configPathInfo.setWordWrap(True) configPathInfoLayout.addWidget(self.configPathInfo) gbConfigLayout.addLayout(configPathInfoLayout) # Script Merger gbScriptMerger = QGroupBox('Script Merger', self) mainLayout.addWidget(gbScriptMerger) gbScriptMergerLayout = QVBoxLayout(gbScriptMerger) scriptMergerPathLayout = QHBoxLayout() self.scriptMergerPath = QLineEdit(self) self.scriptMergerPath.setPlaceholderText( 'Path to WitcherScriptMerger.exe...') if settings.value('scriptMergerPath'): self.scriptMergerPath.setText( str(settings.value('scriptMergerPath'))) self.scriptMergerPath.textChanged.connect( lambda: self.validateScriptMergerPath(self.scriptMergerPath.text() )) scriptMergerPathLayout.addWidget(self.scriptMergerPath) self.locateScriptMerger = QPushButton('Detect', self) self.locateScriptMerger.clicked.connect(self.locateScriptMergerEvent) self.locateScriptMerger.setToolTip( 'Automatically detect the script merger path if possible') scriptMergerPathLayout.addWidget(self.locateScriptMerger) selectScriptMerger = QPushButton('Browse', self) selectScriptMerger.clicked.connect(self.selectScriptMergerEvent) scriptMergerPathLayout.addWidget(selectScriptMerger) gbScriptMergerLayout.addLayout(scriptMergerPathLayout) scriptMergerPathInfoLayout = QHBoxLayout() self.scriptMergerPathInfo = QLabel('', self) self.scriptMergerPathInfo.setOpenExternalLinks(True) self.scriptMergerPathInfo.setContentsMargins(4, 4, 4, 4) self.scriptMergerPathInfo.setMinimumHeight(40) self.scriptMergerPathInfo.setWordWrap(True) scriptMergerPathInfoLayout.addWidget(self.scriptMergerPathInfo) gbScriptMergerLayout.addLayout(scriptMergerPathInfoLayout) # Nexus Mods API gbNexusModsAPI = QGroupBox('Nexus Mods API', self) mainLayout.addWidget(gbNexusModsAPI) gbNexusModsAPILayout = QVBoxLayout(gbNexusModsAPI) self.nexusAPIKey = QLineEdit(self) self.nexusAPIKey.setPlaceholderText('Personal API Key...') if settings.value('nexusAPIKey'): self.nexusAPIKey.setText(str(settings.value('nexusAPIKey'))) self.nexusAPIKey.textChanged.connect( lambda: self.validateApiKey(self.nexusAPIKey.text())) gbNexusModsAPILayout.addWidget(self.nexusAPIKey) self.nexusAPIKeyInfo = QLabel('🌐', self) self.nexusAPIKeyInfo.setOpenExternalLinks(True) self.nexusAPIKeyInfo.setWordWrap(True) self.nexusAPIKeyInfo.setContentsMargins(4, 4, 4, 4) self.nexusAPIKeyInfo.setMinimumHeight(48) gbNexusModsAPILayout.addWidget(self.nexusAPIKeyInfo) self.nexusGetInfo = QCheckBox('Get Mod details after adding a new mod', self) self.nexusGetInfo.setChecked( settings.value('nexusGetInfo', 'True') == 'True') self.nexusGetInfo.setDisabled(True) gbNexusModsAPILayout.addWidget(self.nexusGetInfo) self.nexusCheckUpdates = QCheckBox('Check for Mod updates on startup', self) self.nexusCheckUpdates.setChecked( settings.value('nexusCheckUpdates', 'False') == 'True') self.nexusCheckUpdates.setDisabled(True) gbNexusModsAPILayout.addWidget(self.nexusCheckUpdates) self.nexusCheckClipboard = QCheckBox( 'Monitor the Clipboard for Nexus Mods URLs', self) self.nexusCheckClipboard.setChecked( settings.value('nexusCheckClipboard', 'False') == 'True') self.nexusCheckClipboard.setDisabled(True) gbNexusModsAPILayout.addWidget(self.nexusCheckClipboard) # Output gbOutput = QGroupBox('Output Preferences', self) mainLayout.addWidget(gbOutput) gbOutputLayout = QVBoxLayout(gbOutput) self.unhideOutput = QCheckBox('Auto-show output panel', self) self.unhideOutput.setChecked( settings.value('unhideOutput', 'True') == 'True') gbOutputLayout.addWidget(self.unhideOutput) self.debugOutput = QCheckBox('Show debug output', self) self.debugOutput.setChecked( settings.value('debugOutput', 'False') == 'True') gbOutputLayout.addWidget(self.debugOutput) # Actions actionsLayout = QHBoxLayout() actionsLayout.setAlignment(Qt.AlignRight) self.save = QPushButton('Save', self) self.save.clicked.connect(self.saveEvent) self.save.setAutoDefault(True) self.save.setDefault(True) actionsLayout.addWidget(self.save) cancel = QPushButton('Cancel', self) cancel.clicked.connect(self.cancelEvent) actionsLayout.addWidget(cancel) mainLayout.addLayout(actionsLayout) # Setup if not settings.value('gamePath'): self.locateGameEvent() self.setMinimumSize(QSize(440, 440)) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) self.validGamePath = False self.validConfigPath = False self.validNexusAPIKey = False self.validScriptMergerPath = False self.validateGamePath(self.gamePath.text()) self.validateConfigPath(self.configPath.text()) self.validateApiKey(self.nexusAPIKey.text()) self.validateScriptMergerPath(self.scriptMergerPath.text()) self.updateSaveButton() self.finished.connect( lambda: self.validateApiKey.cancel()) # type: ignore def saveEvent(self) -> None: settings = QSettings() settings.setValue('settingsWindowGeometry', self.saveGeometry()) settings.setValue('gamePath', self.gamePath.text()) settings.setValue('configPath', self.configPath.text()) settings.setValue('scriptMergerPath', self.scriptMergerPath.text()) settings.setValue('nexusAPIKey', self.nexusAPIKey.text()) settings.setValue('nexusGetInfo', str(self.nexusGetInfo.isChecked())) settings.setValue('nexusCheckUpdates', str(self.nexusCheckUpdates.isChecked())) settings.setValue('nexusCheckClipboard', str(self.nexusCheckClipboard.isChecked())) settings.setValue('debugOutput', str(self.debugOutput.isChecked())) settings.setValue('unhideOutput', str(self.unhideOutput.isChecked())) self.close() def cancelEvent(self) -> None: self.close() def selectGameEvent(self) -> None: dialog: QFileDialog = QFileDialog(self, 'Select witcher3.exe', '', 'The Witcher 3 (witcher3.exe)') dialog.setOptions(QFileDialog.ReadOnly) dialog.setFileMode(QFileDialog.ExistingFile) if (dialog.exec_()): if dialog.selectedFiles(): self.gamePath.setText(dialog.selectedFiles()[0]) def selectConfigEvent(self) -> None: dialog: QFileDialog = QFileDialog(self, 'Select config folder', '', 'The Witcher 3') dialog.setOptions(QFileDialog.ReadOnly) dialog.setFileMode(QFileDialog.Directory) if (dialog.exec_()): if dialog.selectedFiles(): self.configPath.setText(dialog.selectedFiles()[0]) def selectScriptMergerEvent(self) -> None: dialog: QFileDialog = QFileDialog( self, 'Select WitcherScriptMerger.exe', '', 'Script Merger (WitcherScriptMerger.exe)') dialog.setOptions(QFileDialog.ReadOnly) dialog.setFileMode(QFileDialog.ExistingFile) if (dialog.exec_()): if dialog.selectedFiles(): self.scriptMergerPath.setText(dialog.selectedFiles()[0]) def locateGameEvent(self) -> None: game = fetcher.findGamePath() if game: self.gamePath.setText(str(game)) else: self.gamePathInfo.setText(''' <font color="#888"> Could not detect The Witcher 3!<br> Please make sure the game is installed, or set the path manually. </font>''') def locateConfigEvent(self) -> None: config = fetcher.findConfigPath() if config: self.configPath.setText(str(config)) else: self.configPathInfo.setText(''' <font color="#888"> Could not detect a valid config path! Please make sure the The Witcher 3 was started at least once, or set the path manually. </font>''') def locateScriptMergerEvent(self) -> None: scriptmerger = findScriptMergerPath() if scriptmerger: self.scriptMergerPath.setText(str(scriptmerger)) else: self.scriptMergerPathInfo.setText(''' <font color="#888"> Could not detect Script Merger! Please make sure Script Merger is running,<br> or set the path manually. Download Script Merger <a href="https://www.nexusmods.com/witcher3/mods/484">here</a>. </font>''') def validateGamePath(self, text: str) -> bool: # validate game installation path if not verifyGamePath(Path(text)): self.gamePath.setStyleSheet(''' *{ border: 1px solid #B22222; padding: 1px 0px; } ''') self.gamePathInfo.setText( '<font color="#888">Please enter a valid game path.</font>') self.validGamePath = False self.locateGame.setDisabled(False) self.updateSaveButton() return False else: self.gamePath.setStyleSheet('') self.gamePathInfo.setText( '<font color="#888">Everything looks good!</font>') self.validGamePath = True self.locateGame.setDisabled(True) self.updateSaveButton() return True def validateConfigPath(self, text: str) -> bool: # validate game config path if not verifyConfigPath(Path(text)): self.configPath.setStyleSheet(''' *{ border: 1px solid #B22222; padding: 1px 0px; } ''') self.configPathInfo.setText('''<font color="#888"> Please enter a valid config path. You need to start the The Witcher 3 at least once to generate the necessary user.settings and input.settings files.</font> ''') self.validConfigPath = False self.locateConfig.setDisabled(False) self.updateSaveButton() return False else: self.configPath.setStyleSheet('') self.configPathInfo.setText( '<font color="#888">Everything looks good!</font>') self.validConfigPath = True self.locateConfig.setDisabled(True) self.updateSaveButton() return True def validateScriptMergerPath(self, text: str) -> bool: # validate script merger path if not text: self.scriptMergerPath.setStyleSheet('') self.scriptMergerPathInfo.setText(''' <font color="#888">Script Merger is used to resolve conflicts between mods \ by merging scripts and other text files. \ Download Script Merger <a href="https://www.nexusmods.com/witcher3/mods/484">here</a>.</font> ''') self.validScriptMergerPath = True self.updateSaveButton() return True if not verifyScriptMergerPath(Path(text)): self.scriptMergerPath.setStyleSheet(''' *{ border: 1px solid #B22222; padding: 1px 0px; } ''') self.scriptMergerPathInfo.setText( '''<font color="#888">Please enter a valid script merger path.</font> ''') self.validScriptMergerPath = False self.locateScriptMerger.setDisabled(False) self.updateSaveButton() return False else: self.scriptMergerPath.setStyleSheet('') self.scriptMergerPathInfo.setText( '<font color="#888">Everything looks good!</font>') self.validScriptMergerPath = True self.locateScriptMerger.setDisabled(True) self.updateSaveButton() return True @debounce(200, cancel_running=True) async def validateApiKey(self, text: str) -> bool: # validate neus mods api key self.nexusGetInfo.setDisabled(True) self.nexusCheckUpdates.setDisabled(True) self.nexusCheckClipboard.setDisabled(True) self.nexusAPIKey.setStyleSheet('') if not text: self.nexusAPIKeyInfo.setText(''' <font color="#888">The API Key is used to check for mod updates, \ to get mod details and to download mods. \ Get your Personal API Key <a href="https://www.nexusmods.com/users/myaccount?tab=api">here</a>.</font> ''') self.validNexusAPIKey = True self.updateSaveButton() return True self.nexusAPIKeyInfo.setText('🌐') try: apiUser = await getUserInformation(text) except UnauthorizedError: self.nexusAPIKey.setStyleSheet(''' *{ border: 1px solid #B22222; padding: 1px 0px; } ''') self.nexusAPIKeyInfo.setText(''' <font color="#888">Not a valid API Key. \ Get your Personal API Key <a href="https://www.nexusmods.com/users/myaccount?tab=api">here</a>.</font> ''') self.validNexusAPIKey = False self.updateSaveButton() return False except (RequestError, ResponseError, Exception) as e: self.nexusAPIKey.setStyleSheet(''' *{ border: 1px solid #B22222; padding: 1px 0px; } ''') self.nexusAPIKeyInfo.setText(f''' <font color="#888">Could not validate API Key: {str(e) if str(e) else 'Request error'}.</font> ''') self.validNexusAPIKey = False self.updateSaveButton() return False self.nexusAPIKeyInfo.setText( f'<font color="#888">Valid API Key for {apiUser["name"]}!</font>') self.validNexusAPIKey = True self.nexusGetInfo.setDisabled(False) self.nexusCheckUpdates.setDisabled(False) self.nexusCheckClipboard.setDisabled(False) self.updateSaveButton() return True def updateSaveButton(self) -> None: # TODO: release: disable saving invalid settings # self.save.setDisabled(not all(( # self.validConfigPath, # self.validGamePath, # self.validNexusAPIKey, # self.validScriptMergerPath, # ))) # noqa self.save.setDisabled(False)