class ClosableDialog(QDialog): def __init__(self, title, widget, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(title) self.setModal(True) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetFixedSize) # not resizable!!! layout.addWidget(widget) self.__button_layout = QHBoxLayout() self.close_button = QPushButton("Close") self.close_button.setObjectName("CLOSE") self.close_button.clicked.connect(self.accept) self.__button_layout.addStretch() self.__button_layout.addWidget(self.close_button) layout.addStretch() layout.addLayout(self.__button_layout) self.setLayout(layout) def disableCloseButton(self): self.close_button.setEnabled(False) def enableCloseButton(self): self.close_button.setEnabled(True) def keyPressEvent(self, q_key_event): if not self.close_button.isEnabled() and q_key_event.key() == Qt.Key_Escape: pass else: QDialog.keyPressEvent(self, q_key_event) def addButton(self, caption, listner): button = QPushButton(caption) button.setObjectName(str(caption).capitalize()) self.__button_layout.insertWidget(1,button) button.clicked.connect(listner) def toggleButton(self, caption, enabled): button = self.findChild(QPushButton,str(caption).capitalize()) if button is not None: button.setEnabled(enabled)
class WorkflowDialog(QDialog): closeButtonPressed = Signal() def __init__(self, title, widget, parent=None): QDialog.__init__(self, parent) self.setWindowTitle(title) self.setModal(True) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetFixedSize) # not resizable!!! layout.addWidget(widget) button_layout = QHBoxLayout() self.close_button = QPushButton("Close") self.close_button.clicked.connect(self.closeButtonPressed.emit) button_layout.addStretch() button_layout.addWidget(self.close_button) layout.addStretch() layout.addLayout(button_layout) self.setLayout(layout) def disableCloseButton(self): self.close_button.setEnabled(False) def enableCloseButton(self): self.close_button.setEnabled(True) def keyPressEvent(self, q_key_event): if not self.close_button.isEnabled() and q_key_event.key( ) == Qt.Key_Escape: pass else: QDialog.keyPressEvent(self, q_key_event)
class DlgGitHubLogin(QDialog): """Dialog to submit error reports to Github.""" def __init__(self, parent, username): super(DlgGitHubLogin, self).__init__(parent) title = _("Sign in to Github") self.resize(366, 248) self.setWindowTitle(title) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # Header html = ('<html><head/><body><p align="center"><img src="{mark}"/></p>' '<p align="center">{title}</p></body></html>') mark = GH_MARK_NORMAL if self.palette().base().color().lightness() < 128: mark = GH_MARK_LIGHT lbl_html = QLabel(html.format(mark=mark, title=title)) # Tabs tabs = QTabWidget() # Basic form layout basic_form_layout = QFormLayout() basic_form_layout.setContentsMargins(-1, 0, -1, -1) lbl_user = QLabel(_("Username:"******"Password: "******"Basic authentication")) # Token form layout token_form_layout = QFormLayout() token_form_layout.setContentsMargins(-1, 0, -1, -1) lbl_token = QLabel("Token: ") token_form_layout.setWidget(1, QFormLayout.LabelRole, lbl_token) self.le_token = QLineEdit() self.le_token.setEchoMode(QLineEdit.Password) self.le_token.textChanged.connect(self.update_btn_state) token_form_layout.setWidget(1, QFormLayout.FieldRole, self.le_token) # Token auth tab token_auth = QWidget() token_layout = QVBoxLayout() token_layout.addLayout(token_form_layout) token_layout.addStretch(1) token_auth.setLayout(token_layout) tabs.addTab(token_auth, _("Token authentication")) # Sign in button self.bt_sign_in = QPushButton(_("Sign in")) self.bt_sign_in.clicked.connect(self.accept) self.bt_sign_in.setDisabled(True) # Main layout layout = QVBoxLayout() layout.addWidget(lbl_html) layout.addWidget(tabs) layout.addWidget(self.bt_sign_in) self.setLayout(layout) # Final adjustments if username: self.le_user.setText(username) self.le_password.setFocus() else: self.le_user.setFocus() self.setFixedSize(self.width(), self.height()) self.le_password.installEventFilter(self) self.le_user.installEventFilter(self) def eventFilter(self, obj, event): interesting_objects = [self.le_password, self.le_user] if obj in interesting_objects and event.type() == QEvent.KeyPress: if (event.key() == Qt.Key_Return and event.modifiers() & Qt.ControlModifier and self.bt_sign_in.isEnabled()): self.accept() return True return False def update_btn_state(self): user = to_text_string(self.le_user.text()).strip() != '' password = to_text_string(self.le_password.text()).strip() != '' token = to_text_string(self.le_token.text()).strip() != '' enable = (user and password) or token self.bt_sign_in.setEnabled(enable) @classmethod def login(cls, parent, username): dlg = DlgGitHubLogin(parent, username) if dlg.exec_() == dlg.Accepted: user = dlg.le_user.text() password = dlg.le_password.text() token = dlg.le_token.text() if token != '': return (token, ) else: return user, password return None, None
class DlgGitHubLogin(QDialog): """Dialog to submit error reports to Github.""" def __init__(self, parent, username, password, token, remember=False, remember_token=False): QDialog.__init__(self, parent) title = _("Sign in to Github") self.resize(415, 375) self.setWindowTitle(title) self.setWindowFlags( self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # Header html = ('<html><head/><body><p align="center">' '{title}</p></body></html>') lbl_html = QLabel(html.format(title=title)) lbl_html.setStyleSheet('font-size: 16px;') # Tabs self.tabs = QTabWidget() # Basic form layout basic_form_layout = QFormLayout() basic_form_layout.setContentsMargins(-1, 0, -1, -1) basic_lbl_msg = QLabel(_("For regular users, i.e. users <b>without</b>" " two-factor authentication enabled")) basic_lbl_msg.setWordWrap(True) basic_lbl_msg.setAlignment(Qt.AlignJustify) lbl_user = QLabel(_("Username:"******"", QWidget()) lbl_password = QLabel(_("Password: "******"Remember me")) self.cb_remember.setToolTip(_("Spyder will save your credentials " "safely")) self.cb_remember.setChecked(remember) basic_form_layout.setWidget(4, QFormLayout.FieldRole, self.cb_remember) # Basic auth tab basic_auth = QWidget() basic_layout = QVBoxLayout() basic_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) basic_layout.addWidget(basic_lbl_msg) basic_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) basic_layout.addLayout(basic_form_layout) basic_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) basic_auth.setLayout(basic_layout) self.tabs.addTab(basic_auth, _("Password Only")) # Token form layout token_form_layout = QFormLayout() token_form_layout.setContentsMargins(-1, 0, -1, -1) token_lbl_msg = QLabel(_("For users <b>with</b> two-factor " "authentication enabled, or who prefer a " "per-app token authentication.<br><br>" "You can go <b><a href=\"{}\">here</a></b> " "and click \"Generate token\" at the bottom " "to create a new token to use for this, with " "the appropriate permissions.").format( TOKEN_URL)) token_lbl_msg.setOpenExternalLinks(True) token_lbl_msg.setWordWrap(True) token_lbl_msg.setAlignment(Qt.AlignJustify) lbl_token = QLabel("Token: ") token_form_layout.setWidget(1, QFormLayout.LabelRole, lbl_token) self.le_token = QLineEdit() self.le_token.setEchoMode(QLineEdit.Password) self.le_token.textChanged.connect(self.update_btn_state) token_form_layout.setWidget(1, QFormLayout.FieldRole, self.le_token) self.cb_remember_token = None # Same validation as with cb_remember if self.is_keyring_available() and valid_py_os: self.cb_remember_token = QCheckBox(_("Remember token")) self.cb_remember_token.setToolTip(_("Spyder will save your " "token safely")) self.cb_remember_token.setChecked(remember_token) token_form_layout.setWidget(3, QFormLayout.FieldRole, self.cb_remember_token) # Token auth tab token_auth = QWidget() token_layout = QVBoxLayout() token_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) token_layout.addWidget(token_lbl_msg) token_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) token_layout.addLayout(token_form_layout) token_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) token_auth.setLayout(token_layout) self.tabs.addTab(token_auth, _("Access Token")) # Sign in button self.bt_sign_in = QPushButton(_("Sign in")) self.bt_sign_in.clicked.connect(self.accept) self.bt_sign_in.setDisabled(True) # Main layout layout = QVBoxLayout() layout.addWidget(lbl_html) layout.addWidget(self.tabs) layout.addWidget(self.bt_sign_in) self.setLayout(layout) # Final adjustments if username and password: self.le_user.setText(username) self.le_password.setText(password) self.bt_sign_in.setFocus() elif username: self.le_user.setText(username) self.le_password.setFocus() elif token: self.le_token.setText(token) else: self.le_user.setFocus() self.setFixedSize(self.width(), self.height()) self.le_password.installEventFilter(self) self.le_user.installEventFilter(self) self.tabs.currentChanged.connect(self.update_btn_state) def eventFilter(self, obj, event): interesting_objects = [self.le_password, self.le_user] if obj in interesting_objects and event.type() == QEvent.KeyPress: if (event.key() == Qt.Key_Return and event.modifiers() & Qt.ControlModifier and self.bt_sign_in.isEnabled()): self.accept() return True return False def update_btn_state(self): user = to_text_string(self.le_user.text()).strip() != '' password = to_text_string(self.le_password.text()).strip() != '' token = to_text_string(self.le_token.text()).strip() != '' enable = ((user and password and self.tabs.currentIndex() == 0) or (token and self.tabs.currentIndex() == 1)) self.bt_sign_in.setEnabled(enable) def is_keyring_available(self): """Check if keyring is available for password storage.""" try: import keyring # analysis:ignore return True except Exception: return False @classmethod def login(cls, parent, username, password, token, remember, remember_token): dlg = DlgGitHubLogin(parent, username, password, token, remember, remember_token) if dlg.exec_() == dlg.Accepted: user = dlg.le_user.text() password = dlg.le_password.text() token = dlg.le_token.text() if dlg.cb_remember: remember = dlg.cb_remember.isChecked() else: remember = False if dlg.cb_remember_token: remember_token = dlg.cb_remember_token.isChecked() else: remember_token = False credentials = dict(username=user, password=password, token=token, remember=remember, remember_token=remember_token) return credentials return dict(username=None, password=None, token=None, remember=False, remember_token=False)
class DlgGitHubLogin(QDialog): """Dialog to submit error reports to Github.""" def __init__(self, parent, username, password, token, remember=False, remember_token=False): QDialog.__init__(self, parent) title = _("Sign in to Github") self.resize(415, 375) self.setWindowTitle(title) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) # Header html = ('<html><head/><body><p align="center">' '{title}</p></body></html>') lbl_html = QLabel(html.format(title=title)) lbl_html.setStyleSheet('font-size: 16px;') # Tabs self.tabs = QTabWidget() # Basic form layout basic_form_layout = QFormLayout() basic_form_layout.setContentsMargins(-1, 0, -1, -1) basic_lbl_msg = QLabel( _("For regular users, i.e. users <b>without</b>" " two-factor authentication enabled")) basic_lbl_msg.setWordWrap(True) basic_lbl_msg.setAlignment(Qt.AlignJustify) lbl_user = QLabel(_("Username:"******"", QWidget()) lbl_password = QLabel(_("Password: "******"Remember me")) self.cb_remember.setToolTip( _("Spyder will save your credentials " "safely")) self.cb_remember.setChecked(remember) basic_form_layout.setWidget(4, QFormLayout.FieldRole, self.cb_remember) # Basic auth tab basic_auth = QWidget() basic_layout = QVBoxLayout() basic_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) basic_layout.addWidget(basic_lbl_msg) basic_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) basic_layout.addLayout(basic_form_layout) basic_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) basic_auth.setLayout(basic_layout) self.tabs.addTab(basic_auth, _("Password Only")) # Token form layout token_form_layout = QFormLayout() token_form_layout.setContentsMargins(-1, 0, -1, -1) token_lbl_msg = QLabel( _("For users <b>with</b> two-factor " "authentication enabled, or who prefer a " "per-app token authentication.<br><br>" "You can go <b><a href=\"{}\">here</a></b> " "and click \"Generate token\" at the bottom " "to create a new token to use for this, with " "the appropriate permissions.").format(TOKEN_URL)) token_lbl_msg.setOpenExternalLinks(True) token_lbl_msg.setWordWrap(True) token_lbl_msg.setAlignment(Qt.AlignJustify) lbl_token = QLabel("Token: ") token_form_layout.setWidget(1, QFormLayout.LabelRole, lbl_token) self.le_token = QLineEdit() self.le_token.setEchoMode(QLineEdit.Password) self.le_token.textChanged.connect(self.update_btn_state) token_form_layout.setWidget(1, QFormLayout.FieldRole, self.le_token) self.cb_remember_token = None # Same validation as with cb_remember if self.is_keyring_available() and valid_py_os: self.cb_remember_token = QCheckBox(_("Remember token")) self.cb_remember_token.setToolTip( _("Spyder will save your " "token safely")) self.cb_remember_token.setChecked(remember_token) token_form_layout.setWidget(3, QFormLayout.FieldRole, self.cb_remember_token) # Token auth tab token_auth = QWidget() token_layout = QVBoxLayout() token_layout.addSpacerItem(QSpacerItem(QSpacerItem(0, 8))) token_layout.addWidget(token_lbl_msg) token_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) token_layout.addLayout(token_form_layout) token_layout.addSpacerItem( QSpacerItem(QSpacerItem(0, 50, vPolicy=QSizePolicy.Expanding))) token_auth.setLayout(token_layout) self.tabs.addTab(token_auth, _("Access Token")) # Sign in button self.bt_sign_in = QPushButton(_("Sign in")) self.bt_sign_in.clicked.connect(self.accept) self.bt_sign_in.setDisabled(True) # Main layout layout = QVBoxLayout() layout.addWidget(lbl_html) layout.addWidget(self.tabs) layout.addWidget(self.bt_sign_in) self.setLayout(layout) # Final adjustments if username and password: self.le_user.setText(username) self.le_password.setText(password) self.bt_sign_in.setFocus() elif username: self.le_user.setText(username) self.le_password.setFocus() elif token: self.le_token.setText(token) else: self.le_user.setFocus() self.setFixedSize(self.width(), self.height()) self.le_password.installEventFilter(self) self.le_user.installEventFilter(self) self.tabs.currentChanged.connect(self.update_btn_state) def eventFilter(self, obj, event): interesting_objects = [self.le_password, self.le_user] if obj in interesting_objects and event.type() == QEvent.KeyPress: if (event.key() == Qt.Key_Return and event.modifiers() & Qt.ControlModifier and self.bt_sign_in.isEnabled()): self.accept() return True return False def update_btn_state(self): user = to_text_string(self.le_user.text()).strip() != '' password = to_text_string(self.le_password.text()).strip() != '' token = to_text_string(self.le_token.text()).strip() != '' enable = ((user and password and self.tabs.currentIndex() == 0) or (token and self.tabs.currentIndex() == 1)) self.bt_sign_in.setEnabled(enable) def is_keyring_available(self): """Check if keyring is available for password storage.""" try: import keyring # analysis:ignore return True except Exception: return False @classmethod def login(cls, parent, username, password, token, remember, remember_token): dlg = DlgGitHubLogin(parent, username, password, token, remember, remember_token) if dlg.exec_() == dlg.Accepted: user = dlg.le_user.text() password = dlg.le_password.text() token = dlg.le_token.text() if dlg.cb_remember: remember = dlg.cb_remember.isChecked() else: remember = False if dlg.cb_remember_token: remember_token = dlg.cb_remember_token.isChecked() else: remember_token = False credentials = dict(username=user, password=password, token=token, remember=remember, remember_token=remember_token) return credentials return dict(username=None, password=None, token=None, remember=False, remember_token=False)
class MultipleFileWidget(QWidget): _add_state = Signal(object, bool) def __init__(self, settings: BaseSettings, load_dict: Dict[str, LoadBase], compare_in_context_menu=False): super().__init__() self.settings = settings self.state_dict: Dict[str, Dict[str, ProjectInfoBase]] = defaultdict(dict) self.state_dict_count = Counter() self.file_list = [] self.load_register = load_dict self.file_view = CustomTreeWidget(compare_in_context_menu) self.file_view.header().close() self.save_state_btn = QPushButton("Save state") self.save_state_btn.setStyleSheet("QPushButton{font-weight: bold;}") self.load_files_btn = QPushButton("Load Files") self.forget_btn = QPushButton("Forget") self.save_state_btn.clicked.connect(self.save_state) self.forget_btn.clicked.connect(self.forget) self.load_files_btn.clicked.connect(self.load_files) self.file_view.itemDoubleClicked.connect(self.load_state) self.file_view.context_load.connect(self.load_state) self.last_point = None self.custom_names_chk = QCheckBox("Custom names") layout = QGridLayout() layout.addWidget(self.file_view, 0, 0, 1, 2) layout.addWidget(self.save_state_btn, 1, 0, 1, 2) layout.addWidget(self.load_files_btn, 2, 0) layout.addWidget(self.forget_btn, 2, 1) layout.addWidget(self.custom_names_chk, 3, 0, 1, 2) self.setLayout(layout) self.setMouseTracking(True) self.file_view.setMouseTracking(True) self.file_view.context_load.connect(self.load_state) self.file_view.context_compare.connect(self.load_compare) self.file_view.context_forget.connect(self.forget_action) self.error_list = [] self._add_state.connect(self.save_state_action) def execute_load_files(self, load_data: LoadProperty, range_changed, step_changed): range_changed(0, len(load_data.load_location)) for i, el in enumerate(load_data.load_location, 1): load_list = [el] while load_data.load_class.number_of_files() > len(load_list): load_list.append(load_data.load_class.get_next_file(load_list)) if not os.path.exists(load_list[-1]): self.error_list.append(el) step_changed(i) continue state: ProjectInfoBase = load_data.load_class.load(load_list) self._add_state.emit(state, False) step_changed(i) def load_files(self): def exception_hook(exception): from qtpy.QtCore import QMetaObject instance = QApplication.instance() if isinstance(exception, MemoryError): instance.warning = "Open error", f"Not enough memory to read this image: {exception}" QMetaObject.invokeMethod(instance, "show_warning", Qt.QueuedConnection) elif isinstance(exception, IOError): instance.warning = "Open error", f"Some problem with reading from disc: {exception}" QMetaObject.invokeMethod(instance, "show_warning", Qt.QueuedConnection) elif isinstance(exception, KeyError): instance.warning = "Open error", f"Some problem project file: {exception}" QMetaObject.invokeMethod(instance, "show_warning", Qt.QueuedConnection) print(exception, file=sys.stderr) else: raise exception dial = MultipleLoadDialog(self.load_register, self.settings.get_path_history()) dial.setDirectory(self.settings.get("io.multiple_open_directory", str(Path.home()))) dial.selectNameFilter(self.settings.get("io.multiple_open_filter", next(iter(self.load_register.keys())))) self.error_list = [] if dial.exec(): result = dial.get_result() load_dir = os.path.dirname(result.load_location[0]) self.settings.set("io.multiple_open_directory", load_dir) self.settings.add_path_history(load_dir) self.settings.set("io.multiple_open_filter", result.selected_filter) dial_fun = ExecuteFunctionDialog(self.execute_load_files, [result], exception_hook=exception_hook) dial_fun.exec() if self.error_list: errors_message = QMessageBox() errors_message.setText("There are errors during load files") errors_message.setInformativeText("During load files cannot found some of files on disc") errors_message.setStandardButtons(QMessageBox.Ok) text = "\n".join(["File: " + x[0] + "\n" + str(x[1]) for x in self.error_list]) errors_message.setDetailedText(text) errors_message.exec() def load_state(self, item, _column=1): if item.parent() is None: return file_name = self.file_list[self.file_view.indexOfTopLevelItem(item.parent())] state_name = item.text(0) project_info = self.state_dict[file_name][state_name] try: self.parent().parent().parent().set_data(project_info) except AttributeError: self.settings.set_project_info(project_info) def load_compare(self, item): if item.parent() is None: return file_name = self.file_list[self.file_view.indexOfTopLevelItem(item.parent())] if self.settings.image.file_path != file_name: QMessageBox.information(self, "Wrong file", "Please select same file as main") return state_name = item.text(0) project_info = self.state_dict[file_name][state_name] if hasattr(self.settings, "set_segmentation_to_compare"): self.settings.set_segmentation_to_compare(project_info.segmentation_info) def save_state(self): state: ProjectInfoBase = self.settings.get_project_info() custom_name = self.custom_names_chk.isChecked() self.save_state_action(state, custom_name) def save_state_action(self, state: ProjectInfoBase, custom_name): # TODO left elipsis # state: ProjectInfoBase = self.get_state() normed_file_path = os.path.normpath(state.file_path) sub_dict = self.state_dict[normed_file_path] name = f"state {self.state_dict_count[normed_file_path]+1}" if custom_name: name, ok = QInputDialog.getText(self, "Save name", "Save name:", text=name) if not ok: return while name in sub_dict or name in ["raw image", "image with mask"]: name, ok = QInputDialog.getText(self, "Save name", "Save name (previous in use):", text=name) if not ok: return try: index = self.file_list.index(os.path.normpath(normed_file_path)) item = self.file_view.topLevelItem(index) except ValueError: metric = QFontMetrics(self.file_view.font()) width = self.file_view.width() - 45 clipped_text = metric.elidedText(normed_file_path, Qt.ElideLeft, width) item = QTreeWidgetItem(self.file_view, [clipped_text]) item.setToolTip(0, normed_file_path) self.file_list.append(normed_file_path) QTreeWidgetItem(item, ["raw image"]) sub_dict["raw image"] = state.get_raw_copy() if state.is_masked(): QTreeWidgetItem(item, ["image with mask"]) sub_dict["image with mask"] = state.get_raw_mask_copy() item.setExpanded(True) if state.is_raw(): return it = QTreeWidgetItem(item, [name]) self.file_view.setCurrentItem(it) sub_dict[name] = state self.state_dict_count[state.file_path] += 1 def forget(self): if not self.forget_btn.isEnabled(): return self.forget_btn.setDisabled(True) item: QTreeWidgetItem = self.file_view.currentItem() self.forget_action(item) def forget_action(self, item): if item is None: return if isinstance(item.parent(), QTreeWidgetItem): index = self.file_view.indexOfTopLevelItem(item.parent()) text = self.file_list[index] if item.text(0) not in self.state_dict[text]: return del self.state_dict[text][item.text(0)] parent = item.parent() parent.removeChild(item) if parent.childCount() == 0: self.file_view.takeTopLevelItem(index) self.file_list.remove(text) else: index = self.file_view.indexOfTopLevelItem(item) text = self.file_list[index] del self.state_dict[text] del self.state_dict_count[text] self.file_list.remove(text) self.file_view.takeTopLevelItem(index) QTimer().singleShot(500, self.enable_forget) @Slot() def enable_forget(self): self.forget_btn.setEnabled(True) def resizeEvent(self, event: QResizeEvent): metric = QFontMetrics(self.file_view.font()) width = self.file_view.width() - 45 for i, text in enumerate(self.file_list): clipped_text = metric.elidedText(text, Qt.ElideLeft, width) item: QTreeWidgetItem = self.file_view.topLevelItem(i) item.setText(0, clipped_text) def mousePressEvent(self, event: QMouseEvent): if event.x() > self.width() - 20: self.last_point = event.pos() def mouseMoveEvent(self, event: QMouseEvent): if event.x() > self.width() - 20: QApplication.setOverrideCursor(Qt.SplitHCursor) else: QApplication.setOverrideCursor(Qt.ArrowCursor) if self.last_point is None or not (event.buttons() & Qt.LeftButton): return new_width = event.x() + 10 new_width = max(new_width, 150) new_width = min(new_width, 600) self.setMinimumWidth(new_width) def leaveEvent(self, _): # pylint: disable=R0201 QApplication.setOverrideCursor(Qt.ArrowCursor) def mouseReleaseEvent(self, event: QMouseEvent): self.last_point = None def set_compare_in_context_menu(self, compare: bool): self.file_view.set_show_compare(compare) def add_states(self, states: List[ProjectInfoBase]): """add multiple states to widget""" for el in states: self.save_state_action(el, False)
class ProcessJobDialog(QDialog): disposeDialog = Signal() presentInformation = Signal(str, str, str) presentError = Signal(str, str, str) closeButtonPressed = Signal() cancelConfirmed = Signal() def __init__(self, title, parent=None): QDialog.__init__(self, parent) self.__parent = parent self.setWindowTitle(title) self.setModal(True) self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) self.setWindowFlags(self.windowFlags() & ~Qt.WindowCloseButtonHint) layout = QVBoxLayout() layout.setSizeConstraint(QLayout.SetFixedSize) widget = QWidget() widget_layout = QHBoxLayout() size = 64 spin_movie = resourceMovie("ide/loading.gif") spin_movie.setSpeed(60) spin_movie.setScaledSize(QSize(size, size)) spin_movie.start() processing_animation = QLabel() processing_animation.setMaximumSize(QSize(size, size)) processing_animation.setMinimumSize(QSize(size, size)) processing_animation.setMovie(spin_movie) widget_layout.addWidget(processing_animation) self.processing_label = QLabel("Processing job: '%s'" % title) widget_layout.addWidget(self.processing_label, Qt.AlignBottom) widget.setLayout(widget_layout) layout.addWidget(widget) button_layout = QHBoxLayout() self.close_button = QPushButton("Close") self.close_button.clicked.connect(self.closeButtonPressed.emit) button_layout.addStretch() button_layout.addWidget(self.close_button) layout.addStretch() layout.addLayout(button_layout) self.setLayout(layout) self.disposeDialog.connect(self.reject) self.presentInformation.connect(self.__presentInformation) self.presentError.connect(self.__presentError) self.closeButtonPressed.connect(self.__confirmCancel) def disableCloseButton(self): self.close_button.setEnabled(False) def enableCloseButton(self): self.close_button.setEnabled(True) def keyPressEvent(self, q_key_event): if not self.close_button.isEnabled() and q_key_event.key() == Qt.Key_Escape: pass else: QDialog.keyPressEvent(self, q_key_event) def closeEvent(self, close_event): close_event.ignore() self.closeButtonPressed.emit() def __createMsgBox(self, title, message, details): msg_box = QMessageBox(self.parent()) msg_box.setText(title) msg_box.setInformativeText(message) if len(details) > 0: msg_box.setDetailedText(details) horizontal_spacer = QSpacerItem( 500, 0, QSizePolicy.MinimumExpanding, QSizePolicy.Expanding ) layout = msg_box.layout() layout.addItem(horizontal_spacer, layout.rowCount(), 0, 1, layout.columnCount()) return msg_box def __presentInformation(self, title, message, details): msg_box = self.__createMsgBox(title, message, details) msg_box.setIcon(QMessageBox.Information) msg_box.exec_() def __presentError(self, title, message, details): msg_box = self.__createMsgBox(title, message, details) msg_box.setIcon(QMessageBox.Critical) msg_box.exec_() def __confirmCancel(self): cancel_box = self.__createMsgBox( "Confirm Cancel", "Are you sure you want to cancel the running job?", "" ) cancel_box.setIcon(QMessageBox.Question) cancel_box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) cancel_box.exec_() cancel = cancel_box.result() if cancel == QMessageBox.Yes: self.cancelConfirmed.emit()
def setup_monitors(self): ui = self.ui.monitors # Grab a fresh copy, may have been updated self.monitors_region_dict = self.ui.regions.get_region_dict() # Mark regions which are in use (this gets reset each time we get here) #for (i, data) in self.monitors.items(): # region = data['region'] # if region in self.monitors_region_dict: # self.monitors_region_dict[region]['available'] = False self.fixup_monitors_table() row = get_selected_row(ui.tablewidget_regions) # Autoselect if only 1 row if row is None and ui.tablewidget_regions.rowCount() == 1: row = 0 ui.tablewidget_regions.setCurrentCell(row, COLUMN_REGION) enabled = (row is not None) for item in (ui.toolbutton_delete, ui.bottom_frame): item.setEnabled(enabled) mon = self.monitors_current_index enable_phase = self.monitor_requires_phase(mon) #**Fluid phase tab** b = ui.pushbutton_fluid b.setText(self.fluid_phase_name) font = b.font() font.setBold(self.monitors_current_tab == FLUID_TAB) b.setFont(font) w = b.fontMetrics().boundingRect(b.text()).width() + 20 b.setMaximumWidth(w) b.setEnabled(not enable_phase) #**Solids Phase Tab** *(Requires TFM Solids)* #Each solid phase will have its own tab. The tab name should be the name of the solid solids_names = list(self.solids.keys()) if self.monitors_saved_solids_names != solids_names: # Clear out the old ones n_cols = ui.tab_layout.columnCount() for i in range(n_cols - 1, 0, -1): item = ui.tab_layout.itemAtPosition(0, i) if not item: continue widget = item.widget() if not widget: continue if widget in (ui.pushbutton_fluid, ui.pushbutton_scalar, ui.pushbutton_reactions, ui.pushbutton_phase): continue ui.tab_layout.removeWidget(widget) widget.setParent(None) widget.deleteLater() # And make new ones for (i, solid_name) in enumerate(solids_names, 1): b = QPushButton(text=solid_name) w = b.fontMetrics().boundingRect(solid_name).width() + 20 b.setMaximumWidth(w) b.setFlat(True) font = b.font() font.setBold(self.monitors_current_tab == SOLIDS_TAB and i == self.monitors_current_solid) b.setFont(font) b.pressed.connect( lambda i=i: self.monitors_change_tab(SOLIDS_TAB, i)) ui.tab_layout.addWidget(b, 0, i) # Only TFM solids for i in range(1, 1 + len(self.solids)): enabled = self.project.get_value( 'solids_model', args=[i]) == 'TFM' and not enable_phase item = ui.tab_layout.itemAtPosition(0, i) if item: widget = item.widget() if widget: widget.setEnabled(enabled) #Scalar (tab) - Tab only available if scalar equations are solved # Move the 'Scalar' button to the right of all solids, if needed b = ui.pushbutton_scalar font = b.font() font.setBold(self.monitors_current_tab == SCALAR_TAB) b.setFont(font) nscalar = self.project.get_value('nscalar', default=0) enabled = (nscalar > 0) and not enable_phase b.setEnabled(enabled) if len(self.solids) != len(self.monitors_saved_solids_names): ui.tab_layout.removeWidget(b) ui.tab_layout.addWidget(b, 0, 1 + len(self.solids)) #Reactions (tab) - Tab only available if nrr > 0 # Move the 'Reactions' button to the right of all solids, if needed b = ui.pushbutton_reactions font = b.font() font.setBold(self.monitors_current_tab == REACTIONS_TAB) b.setFont(font) nrr = self.project.get_value('nrr', default=0) enabled = (nrr > 0) and not enable_phase if nrr == 0 and not enable_phase: b.setToolTip("Requires nrr > 0") else: b.setToolTip('') b.setEnabled(enabled) if len(self.solids) != len(self.monitors_saved_solids_names): ui.tab_layout.removeWidget(b) ui.tab_layout.addWidget(b, 0, 2 + len(self.solids)) # Move the 'Phase' button to the right of all solids, if needed b = ui.pushbutton_phase font = b.font() font.setBold(self.monitors_current_tab == PHASE_TAB) b.setFont(font) b.setEnabled(enable_phase) if len(self.solids) != len(self.monitors_saved_solids_names): ui.tab_layout.removeWidget(b) ui.tab_layout.addWidget(b, 0, 3 + len(self.solids)) self.monitors_saved_solids_names = solids_names self.P = self.monitors_current_solid if mon is None: #Construct the GUI, even though disabled (species checkboxes, etc) self.monitors_setup_current_tab() return key = 'monitor_name' le = ui.lineedit_keyword_monitor_name_args_MONITOR val = self.project.get_value(key, args=[mon]) if val is None: # Construct from region name val = self.monitor_default_name(self.monitors_current_region) self.update_keyword(key, val, args=[mon]) le.updateValue(key, val) # Update table too tw = ui.tablewidget_regions for i in range(tw.rowCount()): data = tw.item(i, 0).data(UserRole) index, name = data if index == mon: tw.item(i, COLUMN_FILENAME).setText(val) #Specify write interval key = 'monitor_dt' default = 0.05 le = ui.lineedit_keyword_monitor_dt_args_MONITOR val = self.project.get_value(key, args=[mon]) if val is None: val = default self.update_keyword(key, val, args=[mon]) le.updateValue(key, val) # Don't stay on a disabled tab index = self.monitors_tab_to_index(self.monitors_current_tab, self.monitors_current_solid) item = None if index is None else ui.tab_layout.itemAtPosition( 0, index) b = item.widget() if item else None if ui.isEnabled() and not (b and b.isEnabled()): self.monitors_change_tab(*self.monitors_find_valid_tab()) else: self.monitors_setup_current_tab() # make sure underline is in the right place, as # of solids may # have changed (lifted from animate_stacked_widget, which we # don't want to call here) tab = self.monitors_current_tab line_to = self.monitors_tab_to_index(tab, self.monitors_current_solid) line = ui.tab_underline btn_layout = ui.tab_layout if line_to is not None: btn_layout.addItem(btn_layout.takeAt(btn_layout.indexOf(line)), 1, line_to)
def setup_pss(self): ui = self.ui.point_sources # Grab a fresh copy, may have been updated self.pss_region_dict = self.ui.regions.get_region_dict() # Mark regions which are in use (this gets reset each time we get here) for (i, data) in self.pss.items(): region = data['region'] if region in self.pss_region_dict: self.pss_region_dict[region]['available'] = False self.fixup_pss_table(ui.tablewidget_regions) row = get_selected_row(ui.tablewidget_regions) # Autoselect if only 1 row if row is None and ui.tablewidget_regions.rowCount() == 1: row = 0 ui.tablewidget_regions.setCurrentCell(row, 0) enabled = (row is not None) for item in (ui.toolbutton_delete, ui.bottom_frame): item.setEnabled(enabled) #Tabs group point source parameters for phases. Tabs are unavailable if no input #is required from the user. # Fluid tab - Unavailable if the fluid phase was disabled. b = ui.pushbutton_fluid b.setText(self.fluid_phase_name) b.setEnabled(not self.fluid_solver_disabled) font = b.font() font.setBold(self.pss_current_tab == 0) b.setFont(font) # Each solid phase will have its own tab. The tab name should be the name of the solid solids_names = list(self.solids.keys()) if self.pss_saved_solids_names != solids_names: # Clear out the old ones n_cols = ui.tab_layout.columnCount() for i in range(n_cols - 1, 0, -1): item = ui.tab_layout.itemAtPosition(0, i) if not item: continue widget = item.widget() if not widget: continue if widget == ui.pushbutton_fluid: continue ui.tab_layout.removeWidget(widget) widget.setParent(None) widget.deleteLater() # And make new ones for (i, solid_name) in enumerate(solids_names, 1): b = QPushButton(text=solid_name) w = b.fontMetrics().boundingRect(solid_name).width() + 20 b.setMaximumWidth(w) b.setFlat(True) font = b.font() font.setBold(self.pss_current_tab == SOLIDS_TAB and i == self.pss_current_solid) b.setFont(font) ui.tab_layout.addWidget(b, 0, i) b.pressed.connect( lambda i=i: self.pss_change_tab(SOLIDS_TAB, i)) for (i, solid_name) in enumerate(self.solids.keys(), 1): model = self.project.get_value('solids_model', args=[i]) #At this time, only TFM solids can be defined with point sources. #At some point in the future, this could be extended to PIC solids, but never DEM. b = ui.tab_layout.itemAtPosition(0, i).widget() if model == 'TFM': b.setEnabled(True) b.setToolTip(None) else: b.setEnabled(False) b.setToolTip("Only TFM solids can be defined as point sources" "") self.pss_saved_solids_names = solids_names self.P = self.pss_current_solid # Don't stay on a disabled tab index = self.pss_tab_to_index(self.pss_current_tab, self.pss_current_solid) item = None if index is None else ui.tab_layout.itemAtPosition( 0, index) b = item.widget() if item else None if ui.isEnabled() and not (b and b.isEnabled()): self.pss_change_tab(*self.pss_find_valid_tab()) else: self.pss_setup_current_tab() # make sure underline is in the right place, as # of solids may # have changed (lifted from animate_stacked_widget, which we # don't want to call here) tab = self.pss_current_tab line_to = self.pss_tab_to_index(tab, self.pss_current_solid) line = ui.tab_underline btn_layout = ui.tab_layout if line_to is not None: btn_layout.addItem(btn_layout.takeAt(btn_layout.indexOf(line)), 1, line_to)