def init_ui(self): super().init_ui() layout = QHBoxLayout() with self.setup_provided(layout): self.x_edit = QLineEdit() self.x_edit.setPlaceholderText('X') self.x_edit.textChanged.connect(self.change_value) layout.addWidget(self.x_edit) self.y_edit = QLineEdit() self.y_edit.setPlaceholderText('Y') self.y_edit.textChanged.connect(self.change_value) layout.addWidget(self.y_edit) self.setLayout(layout)
class PointWidget(Fidget[Tuple[float, float]]): MAKE_TITLE = MAKE_PLAINTEXT = MAKE_INDICATOR = True def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.x_edit = None self.y_edit = None self.init_ui() self.change_value() def init_ui(self): super().init_ui() layout = QHBoxLayout() with self.setup_provided(layout): self.x_edit = QLineEdit() self.x_edit.setPlaceholderText('X') self.x_edit.textChanged.connect(self.change_value) layout.addWidget(self.x_edit) self.y_edit = QLineEdit() self.y_edit.setPlaceholderText('Y') self.y_edit.textChanged.connect(self.change_value) layout.addWidget(self.y_edit) self.setLayout(layout) def parse(self): try: x = float(self.x_edit.text()) except ValueError as e: raise ParseError('error in x') from e try: y = float(self.y_edit.text()) except ValueError as e: raise ParseError('error in y') from e return x, y def plaintext_parsers(self): yield from super().plaintext_parsers() yield tuple_ yield json_dict def fill(self, t): x, y = t self.x_edit.setText(str(x)) self.y_edit.setText(str(y))
def init_ui(self, dialog=None): super().init_ui() self.dialog = self._args_to_filedialog( first_valid(dialog=dialog, DIALOG=self.DIALOG, _self=self)) layout = QHBoxLayout(self) with self.setup_provided(layout): self.edit = QLineEdit() self.edit.textChanged.connect(self.change_value) layout.addWidget(self.edit) browse_btn = QPushButton('...') browse_btn.pressed.connect(self.browse) layout.addWidget(browse_btn) self.setFocusProxy(self.edit) return layout
class FidgetFilePath(Fidget[Path]): """ A Fidget to store a Path to a file """ MAKE_INDICATOR = True MAKE_PLAINTEXT = False def __init__(self, title: str, exist_cond: Optional[bool] = None, dialog: FileDialogArgs = None, **kwargs): """ :param title: the title :param exist_cond: whether the file must exist (True), or must not exist (False) :param dialog: either a QFileDialog, a constructor, or arguments for a QFileDialog. :param kwargs: forwarded to Fidget """ super().__init__(title, **kwargs) self.exist_cond = exist_cond if exist_cond is not None else self.EXIST_COND self.dialog: QFileDialog = None self.edit: QLineEdit = None self.init_ui(dialog) DEFAULT_DIALOG_CLS : Type[QFileDialog] = RememberingFileDialog DIALOG: FileDialogArgs = RememberingFileDialog EXIST_COND = None def init_ui(self, dialog=None): super().init_ui() self.dialog = self._args_to_filedialog(first_valid(dialog=dialog, DIALOG=self.DIALOG, _self=self)) if self.exist_cond: self.dialog.setFileMode(QFileDialog.ExistingFile) else: self.dialog.setFileMode(QFileDialog.AnyFile) layout = QHBoxLayout(self) with self.setup_provided(layout): self.edit = QLineEdit() self.edit.textChanged.connect(self.change_value) layout.addWidget(self.edit) browse_btn = QPushButton('...') browse_btn.pressed.connect(self.browse) layout.addWidget(browse_btn) self.setFocusProxy(self.edit) return layout def browse(self, *a): if self.dialog.exec(): self.fill_value(self.dialog.selectedFiles()[0]) def parse(self): return Path(self.edit.text()) def validate(self, value): super().validate(value) try: exists = value.exists() except OSError: raise ValidationError('path seems invalid', offender=self.edit) if exists: if self.exist_cond not in (True, None): raise ValidationError("path already exists", offender=self.edit) # if the file exists, we don't need to check it else: if self.exist_cond not in (False, None): raise ValidationError("path doesn't exists", offender=self.edit) # so checking of a filename is valid is stupid complicated, slow, and fallible, # https://stackoverflow.com/questions/9532499/check-whether-a-path-is-valid-in-python-without-creating-a-file-at-the-paths-ta/34102855#34102855 # we're just gonna check for invalid characters if not filename_valid(value): raise ValidationError('path seems invalid', offender=self.edit) if exists \ and value.is_dir(): raise ValidationError('path is a directory', offender=self.edit) def fill(self, v: Path): self.edit.setText(str(v)) @inner_plaintext_parser @explicit @staticmethod def glob_search(pattern): i = iglob(pattern) try: ret = next(i) except StopIteration as e: raise PlaintextParseError('no paths match pattern') from e try: next(i) except StopIteration: pass else: raise PlaintextParseError('multiple paths match pattern') return Path(ret) @classmethod def cls_plaintext_parsers(cls): yield Path yield from super().cls_plaintext_parsers() @classmethod def _args_to_filedialog(cls, arg): if isinstance(arg, QFileDialog): return arg if isinstance(arg, dict): return cls.DEFAULT_DIALOG_CLS(**arg) if callable(arg): return arg()
def get_edit(cls): return QLineEdit()
class FidgetFilePaths(Fidget[List[Path]]): """ A Fidget to store a Path to a file """ MAKE_INDICATOR = True MAKE_PLAINTEXT = False def __init__(self, title: str, dialog: FileDialogArgs = None, **kwargs): """ :param title: the title :param exist_cond: whether the file must exist (True), or must not exist (False) :param dialog: either a QFileDialog, a constructor, or arguments for a QFileDialog. :param kwargs: forwarded to Fidget """ super().__init__(title, **kwargs) self.dialog: QFileDialog = None self.edit: QLineEdit = None self.init_ui(dialog) DEFAULT_DIALOG_CLS: Type[QFileDialog] = RememberingFileDialog DIALOG: FileDialogArgs = RememberingFileDialog def init_ui(self, dialog=None): super().init_ui() self.dialog = self._args_to_filedialog( first_valid(dialog=dialog, DIALOG=self.DIALOG, _self=self)) layout = QHBoxLayout(self) with self.setup_provided(layout): self.edit = QLineEdit() self.edit.textChanged.connect(self.change_value) layout.addWidget(self.edit) browse_btn = QPushButton('...') browse_btn.pressed.connect(self.browse) layout.addWidget(browse_btn) self.setFocusProxy(self.edit) return layout def browse(self, *a): if self.dialog.exec(): self.fill_value(self.dialog.selectedFiles()) def parse(self): return [Path(s) for s in self.edit.text().split(';;')] def validate(self, value): super().validate(value) for p in value: try: exists = p.exists() except OSError: raise ValidationError('path seems invalid', offender=self.edit) if not exists: raise ValidationError("path doesn't exists", offender=self.edit) if exists \ and p.is_dir(): raise ValidationError('path is a directory', offender=self.edit) def fill(self, v: List[Path]): self.edit.setText(';;'.join(str(i) for i in v)) @inner_plaintext_parser @explicit @staticmethod def glob_search(pattern): g = iglob(pattern) return [Path(i) for i in g] @classmethod def cls_plaintext_parsers(cls): yield Path yield from super().cls_plaintext_parsers() @classmethod def _args_to_filedialog(cls, arg): if isinstance(arg, QFileDialog): ret = arg elif isinstance(arg, dict): ret = cls.DEFAULT_DIALOG_CLS(**arg) elif callable(arg): ret = arg() else: raise TypeError("can't parse argument as dialog: " + str(arg)) ret.setFileMode(QFileDialog.ExistingFiles) return ret