class TransformationLibrary(QGroupBox): def __init__(self, transformation_confirmation): super().__init__("Transformation Library") self.transformation_configuration = transformation_confirmation self.setLayout(QVBoxLayout()) self._initialize_library() def _initialize_library(self): self.transformation_list = QListView() self.transformation_list.setEditTriggers( QAbstractItemView.NoEditTriggers) self.layout().addWidget(self.transformation_list) self.transformation_list.setModel(QStandardItemModel()) self.transformation_list.clicked.connect(self.configure_transformation) # Populate transformations list for transformation in TRANSFORMATIONS: transformation_name = transformation.schema["metadata"]["name"] transformation_description = transformation.schema["metadata"][ "description"] transformation_element = QStandardItem() transformation_element.setText(transformation_name) transformation_element.setToolTip(transformation_description) self.transformation_list.model().appendRow(transformation_element) def configure_transformation(self, x): """Resolve selected transformation and pass it to transformation_configuration to display config""" model = self.transformation_list.model() selected_transformation = TRANSFORMATIONS_BY_NAME[model.data(x)] logging.debug(f"Selected transformation: {selected_transformation}") self.transformation_configuration.swap_configuration( selected_transformation)
class FilePicker(QGroupBox): def __init__(self, files): super().__init__("File Picker") self.setLayout(QVBoxLayout()) self.file_sequence = FileSequence([]) self._initialize_file_list() self.file_sequence = FileSequence(files) self._update_file_picker_list([str(file) for file in files]) def log_file_sequence_status(self): logging.debug(f"Current file sequence: {self.file_sequence}") def clear_file_list(self): self.file_sequence = FileSequence([]) self.file_list.model().clear() self.log_file_sequence_status() def _initialize_file_list(self): self.file_list = QListView() self.file_list.setEditTriggers(QAbstractItemView.NoEditTriggers) self.layout().addWidget(self.file_list) self.file_list.setModel(QStandardItemModel()) select_files = QPushButton("Select Files") select_files.clicked.connect(self._select_files_listener) self.layout().addWidget(select_files) def _update_file_picker_list(self, file_names): model = self.file_list.model() # Update File Picker list from files[] model.clear() for f in range(self.file_sequence.rowCount()): item = QStandardItem() item.setText(file_names[f]) model.appendRow(item) self.log_file_sequence_status() def _select_files_listener(self): """Handles selection of files to rename""" files = QFileDialog.getOpenFileNames(self, "Select Files", ".") self.file_sequence = FileSequence(files[0]) self._update_file_picker_list(files[0])
class ListerView(QDialog): def __init__(self, title, message, items, parent=None): """ Constructor of ListerView which creates a new dialog with scrolled items which the user can mark Args: title (str): The dialog title that should appear message (str): The message that the user will see at the top of the dialog items ([str]): A list of strings that will be showns as options """ super(ListerView, self).__init__(parent=parent) form = QFormLayout(self) form.addRow(QLabel(message)) self.listView = QListView(self) self.listView.clicked.connect(self.mouse_click_event) form.addRow(self.listView) model = QStandardItemModel(self.listView) self.setWindowTitle(title) for item in items: standardItem = QStandardItem(item) standardItem.setCheckable(True) standardItem.setEditable(False) model.appendRow(standardItem) self.listView.setModel(model) def mouse_click_event(self): """ Callback method that will trigger then the user presses a mouse button while hovering over an item """ row = [qmi.row() for qmi in self.listView.selectedIndexes()][0] item = self.listView.model().item(row) checkState = item.checkState() if checkState == Qt.Checked: checkState = Qt.Unchecked else: checkState = Qt.Checked item.setCheckState(checkState) def enable_button_box(self): """ Method for enabling the buttons the bottom that correspond to OK and Cancel """ form = self.layout() buttonBox = QDialogButtonBox( QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self) form.addRow(buttonBox) buttonBox.accepted.connect(self.accept) buttonBox.rejected.connect(self.reject) def get_results(self): """ Method for quering the results of the user markings The result is a list of strings """ selected = [] model = self.listView.model() i = 0 while model.item(i): if model.item(i).checkState(): selected.append(model.item(i).text()) i += 1 return selected
class PipelineEditor(QGroupBox): def __init__(self, file_picker): super().__init__("Pipeline Editor") self.setLayout(QVBoxLayout()) self.file_picker = file_picker self.pipeline = Pipeline() self.pipelineView = QListView() self.pipelineView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.pipelineView.setModel(QStandardItemModel()) self.layout().addWidget(self.pipelineView) # === BUTTON CONTAINER === button_rows = QWidget() self.layout().addWidget(button_rows) button_rows_layout = QVBoxLayout(button_rows) button_rows_layout.setContentsMargins(0, 0, 0, 0) # === ROW 1 === row_1 = QWidget() button_rows_layout.addWidget(row_1) row_1_layout = QHBoxLayout(row_1) row_1_layout.setContentsMargins(0, 0, 0, 0) self.moveUpButton = QPushButton("Move Up") row_1_layout.addWidget(self.moveUpButton) self.moveUpButton.clicked.connect(self._move_up_listener) self.moveDownButton = QPushButton("Move Down") row_1_layout.addWidget(self.moveDownButton) self.moveDownButton.clicked.connect(self._move_down_listener) self.deleteButton = QPushButton("Delete") row_1_layout.addWidget(self.deleteButton) self.deleteButton.clicked.connect(self._delete_listener) # === ROW 2 === row_2 = QWidget() button_rows_layout.addWidget(row_2) row_2_layout = QHBoxLayout(row_2) row_2_layout.setContentsMargins(0, 0, 0, 0) self.applyButton = QPushButton("Apply Pipeline") row_2_layout.addWidget(self.applyButton) self.applyButton.clicked.connect(self._apply_pipeline_listener) self.update_pipeline_view() def update_pipeline_view(self): logging.debug(self.pipeline) model = self.pipelineView.model() model.clear() for t in range(self.pipeline.rowCount()): item = QStandardItem() item.setText(repr(self.pipeline.data(t))) model.appendRow(item) def _modify_transformation_listener(self): # TODO raise NotImplementedError def _move_up_listener(self): try: to_move = self.pipelineView.selectionModel().selectedIndexes( )[0].row() if to_move < 1: return self.pipeline.move_transformation_up(to_move) self.update_pipeline_view() self.pipelineView.setCurrentIndex(self.pipelineView.model().index( to_move - 1, 0)) except IndexError: return def _move_down_listener(self): try: to_move = self.pipelineView.selectionModel().selectedIndexes( )[0].row() if to_move > self.pipelineView.model().rowCount() - 2: return self.pipeline.move_transformation_down(to_move) self.update_pipeline_view() self.pipelineView.setCurrentIndex(self.pipelineView.model().index( to_move + 1, 0)) except IndexError: return def _delete_listener(self): try: to_delete = self.pipelineView.selectionModel().selectedIndexes( )[0].row() self.pipeline.remove_transformation(to_delete) self.update_pipeline_view() if to_delete > self.pipelineView.model().rowCount() - 1: self.pipelineView.setCurrentIndex( self.pipelineView.model().index(to_delete - 1, 0)) else: self.pipelineView.setCurrentIndex( self.pipelineView.model().index(to_delete, 0)) except IndexError: return def _apply_pipeline_listener(self): # Check that at least one transformation has been added to the pipeline if self.pipeline.rowCount() == 0: no_transformations_messagebox = QMessageBox(self) no_transformations_messagebox.setText( "ERROR: No transformations selected") no_transformations_messagebox.exec_() return file_sequence = self.file_picker.file_sequence.files # Check that at least one file has been added to the file sequence if len(file_sequence) == 0: no_files_messagebox = QMessageBox(self) no_files_messagebox.setText("ERROR: No files selected") no_files_messagebox.exec_() return transformed_sequence = self.pipeline.resolve(file_sequence) before_after = list(zip(file_sequence, transformed_sequence)) preview_text_lines = [] for rename in before_after: preview_text_lines.append(f"{rename[0].name} -> {rename[1].name}") preview_text = "\n".join(preview_text_lines) confirmation = QMessageBox(self) confirmation.setText("Are you sure you want to apply the pipeline?") confirmation.setDetailedText(preview_text) confirmation.setStandardButtons(QMessageBox.Yes | QMessageBox.No) confirmation.setDefaultButton(QMessageBox.No) ret = confirmation.exec_() if ret == int(QMessageBox.Yes): for rename in before_after: from_path = rename[0] to_path = rename[1] shutil.move(str(from_path), str(to_path)) self.file_picker.clear_file_list()