def __init__(self) -> None: QWidget.__init__(self) # self.index stores the index of the latest item which is clicked self.index = None self.model = QStandardItemModel() file_view = QListView() file_view.setModel(self.model) file_view.setEditTriggers(QAbstractItemView.NoEditTriggers) file_view.clicked.connect(self.onClicked) file_view.doubleClicked.connect(self.onDoubleClicked) open_file_button = QPushButton('Open') open_file_button.clicked.connect(self.onOpenFile) preview_file_button = QPushButton('Preview') preview_file_button.clicked.connect(self.onPreviewFile) layout = QGridLayout() layout.addWidget(file_view, 0, 0, 1, 2) layout.addWidget(open_file_button, 1, 0, 1, 1) layout.addWidget(preview_file_button, 1, 1, 1, 1) layout.setMargin(0) self.setLayout(layout)
class MyWidget(QWidget): def __init__(self): QtWidgets.QWidget.__init__(self) self.content_layout = QHBoxLayout() self.podcasts_list = QListView() self.item_list = QListView() self.item_list.setEditTriggers(QAbstractItemView.NoEditTriggers) self.podcasts_list.setEditTriggers(QAbstractItemView.NoEditTriggers) self.duration = 0 self.playtime_label = QLabel() self.duration_label = QLabel() self.playtime_label.setTextFormat(QtCore.Qt.PlainText) self.duration_label.setTextFormat(QtCore.Qt.PlainText) self.playtime_label.setText("00:00:00") self.playtime_label.setFixedHeight(20) self.duration_label.setText("00:00:00") self.duration_label.setFixedHeight(20) self.progress_bar = QSlider(QtCore.Qt.Horizontal) self.content_layout.addWidget(self.podcasts_list) self.content_layout.setStretch(0, 2) self.content_layout.addWidget(self.item_list) self.content_layout.setStretch(1, 8) self.download_button = QPushButton("&Download") self.play_botton = QPushButton("&Play") self.status_layout = QHBoxLayout() self.status_layout.addWidget(self.playtime_label) self.status_layout.addWidget(self.progress_bar) self.status_layout.addWidget(self.duration_label) # self.status_layout.addWidget(self.play_botton) # self.status_layout.addWidget(self.download_button) self.layout = QVBoxLayout() self.layout.addLayout(self.content_layout) self.layout.addLayout(self.status_layout) self.setLayout(self.layout) def update_podcasts_list(self, names): model = QStringListModel(names) self.podcasts_list.setModel(model) def update_items_list(self, names): model = QStringListModel(names) self.item_list.setModel(model) def set_progress(self, progress): self.progress_bar.setValue(progress) self.playtime_label.setText(time_string(progress)) def set_duration(self, duration): self.duration = duration self.progress_bar.setRange(0, duration) self.duration_label.setText(time_string(duration))
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])
def __create_vertical_section(self, group_name): # Setup layout groupbox = QGroupBox(group_name) groupbox.setObjectName(group_name) vertical_layout = QVBoxLayout() groupbox.setLayout(vertical_layout) ''' Add widgets ''' # Add file selection/loading widgets file_select_organizer = QHBoxLayout() text_input = QLineEdit() # Does this need a name yet? file_select_button = QPushButton("...") file_select_organizer.addWidget(text_input) file_select_organizer.addWidget(file_select_button) # Add the section we just made to layout vertical_layout.addLayout(file_select_organizer) # add listview for excel pages excel_label = QLabel("Excel Workbook Pages") excel_sheets = QListView() excel_sheets.setEditTriggers(QAbstractItemView.NoEditTriggers) excel_label_model = QStandardItemModel() excel_sheets.setModel(excel_label_model) vertical_layout.addWidget(excel_label) vertical_layout.addWidget(excel_sheets) # add listview for column headers variable_label = QLabel("Merge on column:") variables = QListView() variables.setEditTriggers(QAbstractItemView.NoEditTriggers) self.columns[group_name] = variables variables_model = QStandardItemModel() variables.setModel(variables_model) vertical_layout.addWidget(variable_label) vertical_layout.addWidget(variables) ''' Connect Functions ''' # Connect File dialog to file selection file_select_button.clicked.connect(lambda: self.openFileNameDialog(text_input, excel_label_model, group_name)) # Connect listview to populate listview for column headers excel_sheets.clicked.connect(lambda x: self.populateColumns(x, excel_label_model, variables_model, group_name)) return groupbox
class SearchDialog(QDialog): def __init__(self, subscribe, search): super().__init__() self.podcast = podcast self.setWindowTitle("Search a podcast") self.edit = QLineEdit() self.result_list = QListView() self.layout = QVBoxLayout() self.layout.addWidget(self.edit) self.layout.addWidget(self.result_list) self.setLayout(self.layout) self.edit.returnPressed.connect(search) self.result_list.doubleClicked.connect(subscribe) self.result_list.setEditTriggers(QAbstractItemView.NoEditTriggers)
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 SourceCodeManager: def __init__(self, window): self.text_edit = QtGui.QTextEdit(window) self.text_edit.setReadOnly(True) # FIXME: write an optimized model self.traceback = None self.traceback_model = QStringListModel() self.traceback_view = QListView(window) self.traceback_view.setModel(self.traceback_model) self.traceback_view.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) window.connect( self.traceback_view.selectionModel(), QtCore.SIGNAL( "selectionChanged(const QItemSelection&, const QItemSelection&)" ), self.frame_selection_changed) # filename => (lines, mtime) self._file_cache = {} self.clear() def clear(self): self._current_file = None self._current_lineno = None self.traceback_model.setStringList([]) self.text_edit.setText('') self._file_cache.clear() def frame_selection_changed(self, selected, unselected): indexes = selected.indexes() if not indexes: return row = indexes[0].row() frame = self.traceback[row] self.show_frame(frame) def set_traceback(self, traceback, show_lineno): self.traceback = traceback if show_lineno: lines = [ '%s:%s' % (frame.filename, frame.lineno) for frame in traceback ] else: lines = [frame.filename for frame in traceback] self.traceback_model.setStringList(lines) def read_file(self, filename): try: mtime = os.stat(filename).st_mtime except OSError: return None if filename in self._file_cache: text, cache_mtime = self._file_cache[filename] if mtime == cache_mtime: return text print("Read %s content (mtime: %s)" % (filename, mtime)) with open(filename, 'rb') as fp: encoding, lines = detect_encoding(fp.readline) lineno = 1 lines = [] with io.open(filename, 'r', encoding=encoding) as fp: for lineno, line in enumerate(fp, 1): lines.append('%d: %s' % (lineno, line.rstrip())) text = '\n'.join(lines) self._file_cache[filename] = (text, mtime) return text def load_file(self, filename): if filename.startswith("<") and filename.startswith(">"): return False if self._current_file == filename: return True text = self.read_file(filename) if text is None: return False self.text_edit.setText(text) self._current_file = filename self._current_lineno = None return True def set_line_number(self, lineno): if self._current_lineno == lineno: return self._current_lineno = lineno doc = self.text_edit.document() # FIXME: complexity in O(number of lines)? block = doc.findBlockByLineNumber(lineno - 1) cursor = QTextCursor(block) cursor.select(QTextCursor.BlockUnderCursor) # FIXME: complexity in O(number of lines)? self.text_edit.setTextCursor(cursor) def show_frame(self, frame): filename = frame.filename if not self.load_file(filename): self._current_file = None self.text_edit.setText('') return if frame.lineno > 0: self.set_line_number(frame.lineno)
class LayersList(QWidget): ''' LayerList class which acts as collapsable list. ''' def __init__(self, name, layers, filter, expand=True): super().__init__() self.setWindowModality(QtCore.Qt.WindowModal) self.currently_expanded = True self.main_layout = QVBoxLayout() self.main_layout.setMargin(0) self.main_layout.setSpacing(0) self.main_layout.setContentsMargins(0, 0, 0, 0) self.expand_button = QPushButton(name) self.expand_button.setToolTip(f"List of {name} Layers") self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Down.png'))) self.layer_list = QListView() self.layer_list.setDragEnabled(True) self.layer_list.setEditTriggers(QAbstractItemView.NoEditTriggers) self.layer_list.setWrapping(False) self.layer_list.setViewMode(self.layer_list.ListMode) self.container_model = QStandardItemModel() self.model = QSortFilterProxyModel() self.model.setSourceModel(self.container_model) self.model.setFilterCaseSensitivity(QtCore.Qt.CaseInsensitive) #self.model.cas filter.textChanged.connect(self.filter_model) for l in layers: self.container_model.appendRow( QStandardItem( QIcon(os.path.join(PATH, 'LayersList_Layer_Icon.png')), l)) self.layer_list.setModel(self.model) self.main_layout.addWidget(self.expand_button, 0, Qt.AlignTop) self.main_layout.addWidget(self.layer_list, 0, Qt.AlignTop) self.expand_button.clicked.connect(self.expand) self.setLayout(self.main_layout) self.resized_size = len(layers) * (self.layer_list.sizeHintForRow(0) + self.layer_list.frameWidth()) self.layer_list.setMaximumHeight(self.resized_size) self.layer_list.setMinimumHeight(self.resized_size) self.setMinimumWidth(self.layer_list.frameWidth()) self.set_styling() if not expand: self.expand() @QtCore.Slot() def expand(self): if self.currently_expanded: self.layer_list.setMinimumHeight(0) self.currently_expanded = False self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Up2.png'))) self.layer_list.setMaximumHeight(0) else: self.layer_list.setMinimumHeight(self.resized_size) self.currently_expanded = True self.expand_button.setIcon( QIcon(os.path.join(PATH, 'LayersList_Down.png'))) self.layer_list.setMaximumHeight(self.resized_size) def set_styling(self): self.setStyleSheet(''' background-color:white; ''') self.expand_button.setStyleSheet(''' background-color:#d6d2d2; text-align:left; ''') @QtCore.Slot() def filter_model(self, text): self.show() self.model.setFilterRegExp(QRegExp(text, QtCore.Qt.CaseInsensitive)) if not self.currently_expanded: self.expand() if self.model.rowCount() == 0: self.hide()
class SymbolViewFrame(QWidget): # --- Init methods --- def __init__(self, config): """ Symbols and Labels view frame :param config: application configuration file """ QWidget.__init__(self) self.setFixedSize(QSize(320, 330)) self.setWindowTitle("DigiQt - Symbols") self.config = config self.lab_label = QLabel("Labels") self.lab_label.setAlignment(Qt.AlignCenter) self.lab_symbols = QLabel("Symbols") self.lab_symbols.setAlignment(Qt.AlignCenter) self.sig_symbol_goto = None # pushed by the controler # Initialization of the lists self.labels_view = QListView() self.labels_view.setFixedSize(QSize(150, 300)) self.labels_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.labels_view.setSelectionMode(QAbstractItemView.SingleSelection) self.dm_labels = QStandardItemModel(self.labels_view) self.symbols_view = QListView() self.symbols_view.setFixedSize(QSize(150, 300)) self.symbols_view.setEditTriggers(QAbstractItemView.NoEditTriggers) self.symbols_view.setSelectionMode(QAbstractItemView.SingleSelection) self.dm_symbols = QStandardItemModel(self.symbols_view) self.init_labels([]) self.init_symbols([]) self.labels_view.clicked.connect(self.on_label_changed) self.symbols_view.clicked.connect(self.on_symbol_changed) self.__set_layout() self.setStyleSheet(style.get_stylesheet("listviews_frame")) def init_labels(self, list_labels): """ Initiates the data model for the labels :param list_labels: list of labels """ self.__init_view(self.labels_view, self.dm_labels, list_labels) def init_symbols(self, list_symbols): """ Initiates the data model for the symbols :param list_symbols: list of symbols """ self.__init_view(self.symbols_view, self.dm_symbols, list_symbols) def __init_view(self, list_view, data_model, objects_list): """ Initiates the view given the data_model and the objects to add in it. :type list_view: QListView :type data_model: QStandardItemModel :type objects_list: list """ data_model.clear() for l in objects_list: data_model.appendRow(QStandardItem(l)) list_view.setModel(data_model) def __set_layout(self): """ Creates this Widget's Layout """ box = QGridLayout() box.setContentsMargins(0, 0, 0, 0) box.addWidget(self.lab_label, 0, 0) box.addWidget(self.labels_view, 1, 0) box.addWidget(self.lab_symbols, 0, 1) box.addWidget(self.symbols_view, 1, 1) self.setLayout(box) def on_label_changed(self, item): """ Callback method for the label change signal :param item: new item selected :type item: QItemSelection """ text = item.data() if text: self.sig_symbol_goto.emit(text) self.place_search_text(text) def on_symbol_changed(self, item): """ Callback method for the symbol change signal :param item: new item selected :type item: QItemSelection """ text = item.data() if text: self.sig_symbol_goto.emit(text) self.place_search_text(text) def place_search_text(self, text): """ Updates the searched value in the editor search field. :param text: label or symbol to place as search text """ pass def __retrieve_text(self, data_model, item): """ Gets the text with the specified data model from a QItemSelection :type item: QItemSelection :type data_model: QStandardItemModel :rtype: str """ selection = item.indexes() if selection: return data_model.item(QModelIndex(selection[0]).row()).text() # --- Close handler --- def closeEvent(self, event): """ Event called upon a red-cross click. """ self.on_close() def on_close(self): """ Reroot this method in the Main Frame in order to Updates the execution frame's open editor icon and tooltip :return: """ pass
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()