class WidgetPandas(QWidget): def __init__(self, parent=None, df=None): super().__init__(parent) if df is None: df = pd.DataFrame() self.tablemodel = PandasDataFrameModel(df=df) self.tableview = QTableView() self.tableview.setModel(self.tablemodel) tableboxlayout = QVBoxLayout() self.tableview.setMinimumWidth(500) self.tableview.setMinimumHeight(250) self.tableview.setWordWrap(False) self.tableview.setShowGrid(False) tableboxlayout.addWidget(self.tableview) self.setLayout(tableboxlayout) @Slot(pd.DataFrame) def setDataFrame(self, df: pd.DataFrame): if df is None: df = pd.DataFrame() self.tablemodel.set_df(df) @Slot() def refreshTable(self): self.tablemodel.beginResetModel() self.tablemodel.endResetModel()
def __init__(self, data_list, header, *args): QWidget.__init__(self, *args) # setGeometry(x_pos, y_pos, width, height) self.setGeometry(300, 200, 570, 450) self.setWindowTitle('Click on column title to sort') # Setup the model and view '''tmodel = MyTableModel(self, data_list, header) tview = QTableView() tview.setModel(tmodel) delegate = MyDelegate() tview.setItemDelegate(delegate)''' # Setup the proxy model for sorting and filtering tmodel = MyTableModel(self, data_list, header) pmodel = QSortFilterProxyModel() pmodel.setSourceModel(tmodel) tview = QTableView() tview.setModel(pmodel) delegate = MyDelegate() tview.setItemDelegate(delegate) # TableView properties tview.resizeColumnsToContents() # set column width to fit contents tview.setShowGrid(False) # hide gridlines #tview.verticalHeader().hide() # row labels #tview.horizontalHeader().hide() # column labels # Select a single row at a time tview.setSelectionBehavior(QTableView.SelectRows) tview.setSelectionMode(QTableView.SingleSelection) # Enable sorting tview.setSortingEnabled(True) # Drag and drop reordering using header labels '''tview.verticalHeader().setSectionsMovable(True) tview.verticalHeader().setDragEnabled(True) tview.verticalHeader().setDragDropMode(QAbstractItemView.InternalMove) tview.horizontalHeader().setSectionsMovable(True) tview.horizontalHeader().setDragEnabled(True) tview.horizontalHeader().setDragDropMode(QAbstractItemView.InternalMove)''' # Drag and drop reordering using rows tview.setDragEnabled(True) tview.setAcceptDrops(True) tview.setDragDropMode(QTableView.InternalMove) tview.setDragDropOverwriteMode(False) layout = QVBoxLayout(self) layout.addWidget(tview) self.setLayout(layout)
class FieldNameListEditor(QWidget): """A widget to edit foreign keys' field name lists.""" data_committed = Signal(name="data_committed") def __init__(self, parent, option, index): """Initialize class.""" super().__init__(parent) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.model = MinimalTableModel(self) self.model.flags = self.model_flags self.view = QTableView(self) self.view.setModel(self.model) self.view.verticalHeader().hide() self.view.horizontalHeader().hide() self.view.setShowGrid(False) check_box_delegate = CheckBoxDelegate(self) self.view.setItemDelegateForColumn(0, check_box_delegate) check_box_delegate.data_committed.connect( self._handle_check_box_data_committed) self.button = QPushButton("Ok", self) self.button.setFlat(True) self.view.verticalHeader().setDefaultSectionSize(option.rect.height()) self.button.setFixedHeight(option.rect.height()) layout.addWidget(self.view) layout.addWidget(self.button) self.button.clicked.connect(self._handle_ok_button_clicked) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup) x_offset = parent.parent().columnViewportPosition(index.column()) y_offset = parent.parent().rowViewportPosition(index.row()) self.position = parent.mapToGlobal(QPoint(0, 0)) + QPoint( x_offset, y_offset) def model_flags(self, index): """Return index flags.""" if not index.isValid(): return Qt.NoItemFlags if index.column() != 0: return ~Qt.ItemIsEditable return Qt.ItemIsEditable @Slot("QModelIndex", name="_handle_check_box_data_committed") def _handle_check_box_data_committed(self, index): """Called when checkbox delegate wants to edit data. Toggle the index's value.""" data = index.data(Qt.EditRole) self.model.setData(index, not data) @Slot("bool", name="_handle_ok_button_clicked") def _handle_ok_button_clicked(self, checked=False): """Called when user pressed Ok.""" self.data_committed.emit() def set_data(self, field_names, current_field_names): """Set values to show in the 'menu'. Reset model using those values and update geometry.""" data = [[name in current_field_names, name] for name in field_names] self.model.reset_model(data) self.view.resizeColumnsToContents() width = self.view.horizontalHeader().length() + qApp.style( ).pixelMetric(QStyle.PM_ScrollBarExtent) self.setFixedWidth(width + 2) height = self.view.verticalHeader().length() + self.button.height() parent_height = self.parent().height() self.setFixedHeight(min(height, parent_height / 2) + 2) self.move(self.position) def data(self): return ",".join( [name for checked, name in self.model._main_data if checked])
class AutoFilterWidget(QWidget): """A widget to show the auto filter 'menu'.""" def __init__(self, parent): """Initialize class.""" super().__init__(parent) layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) self.model = MinimalTableModel(self) self.model.flags = self.model_flags self.view = QTableView(self) self.view.setModel(self.model) self.view.verticalHeader().hide() self.view.horizontalHeader().hide() self.view.setShowGrid(False) check_box_delegate = CheckBoxDelegate(self) self.view.setItemDelegateForColumn(0, check_box_delegate) check_box_delegate.data_committed.connect( self._handle_check_box_data_committed) self.button = QPushButton("Ok", self) self.button.setFlat(True) layout.addWidget(self.view) layout.addWidget(self.button) self.button.clicked.connect(self.hide) self.hide() self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup) def model_flags(self, index): """Return index flags.""" if not index.isValid(): return Qt.NoItemFlags if index.column() == 1: return ~Qt.ItemIsEditable return Qt.ItemIsEditable @Slot("QModelIndex", name="_handle_check_box_data_committed") def _handle_check_box_data_committed(self, index): """Called when checkbox delegate wants to edit data. Toggle the index's value.""" data = index.data(Qt.EditRole) model_data = self.model._main_data row_count = self.model.rowCount() if index.row() == 0: # Ok row value = data in (None, False) for row in range(row_count): model_data[row][0] = value self.model.dataChanged.emit(self.model.index(0, 0), self.model.index(row_count - 1, 0)) else: # Data row self.model.setData(index, not data) self.set_ok_index_data() def set_ok_index_data(self): """Set data for ok index based on data from all other indexes.""" ok_index = self.model.index(0, 0) true_count = 0 for row_data in self.model._main_data[1:]: if row_data[0] == True: true_count += 1 if true_count == len(self.model._main_data) - 1: self.model.setData(ok_index, True) elif true_count == 0: self.model.setData(ok_index, False) else: self.model.setData(ok_index, None) def set_values(self, values): """Set values to show in the 'menu'. Reset model using those values and update geometry.""" self.model.reset_model([[None, "All"]] + values) self.set_ok_index_data() self.view.horizontalHeader().hideSection( 2) # Column 2 holds internal data (cls_id_set) self.view.resizeColumnsToContents() width = self.view.horizontalHeader().length() + qApp.style( ).pixelMetric(QStyle.PM_ScrollBarExtent) self.setFixedWidth(width + 2) height = self.view.verticalHeader().length() + self.button.height() parent_height = self.parent().height() self.setFixedHeight(min(height, parent_height / 2) + 2) def set_section_height(self, height): """Set vertical header default section size as well as button height.""" self.view.verticalHeader().setDefaultSectionSize(height) self.button.setFixedHeight(height)
class DataOpenDialog(QDialog): def __init__( self, expected_columns, obligatory_columns, all_columns=None, parent: typing.Optional[PySide2.QtWidgets.QWidget] = None, ): super().__init__(parent) self.file = None self.allcolumns = all_columns if all_columns else expected_columns self.columns = expected_columns self.obligatorycolumns = obligatory_columns self.setWindowTitle('Open Data File') self.filename = QLineEdit() self.filename.setReadOnly(True) self.filename.setMinimumWidth(150) self.loadbutton = QPushButton("Load...") self.loadbutton.clicked.connect(self.file_dialog) layout_left = QVBoxLayout() filebox = QGroupBox('File') layoutf = QFormLayout() layoutf.addRow('File', self.filename) layoutf.addRow('', self.loadbutton) filebox.setLayout(layoutf) layout_left.addWidget(filebox) self.columnsbox = ColumnsGroupBox('Columns order', expected_columns=expected_columns, all_columns=all_columns) self.columnsbox.columns_changes.connect(self.on_columns_order_changed) layout_left.addWidget(self.columnsbox) layout_left.addStretch() layout_right = QVBoxLayout() previewbox = QGroupBox('File 10 lines preview') previewboxlayout = QVBoxLayout() self.preview = QTextEdit() self.preview.setReadOnly(True) self.preview.setLineWrapMode(QTextEdit.NoWrap) self.preview.setMinimumWidth(500) self.preview.setMinimumHeight(170) previewboxlayout.addWidget(self.preview) previewbox.setLayout(previewboxlayout) layout_right.addWidget(previewbox) self.tablemodel = PandasDataFrameModel(df=pd.DataFrame()) self.tableview = QTableView() self.tableview.setModel(self.tablemodel) tablebox = QGroupBox('Data preview') tableboxlayout = QVBoxLayout() self.tableview.setMinimumWidth(500) self.tableview.setMinimumHeight(250) self.tableview.setWordWrap(False) self.tableview.setShowGrid(False) tableboxlayout.addWidget(self.tableview) tablebox.setLayout(tableboxlayout) layout_right.addWidget(tablebox) layout = QHBoxLayout() layout.addLayout(layout_left) layout.addLayout(layout_right) layoutmain = QVBoxLayout() layoutmain.addLayout(layout) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) layoutmain.addWidget(self.buttonbox) self.setLayout(layoutmain) self.update_ok() @property def df(self): return self.tablemodel.df def file_dialog(self): filedialog = QFileDialog(caption='Select Data File') filedialog.setFileMode(QFileDialog.ExistingFile) if filedialog.exec(): self.file = filedialog.selectedFiles()[0] self.filename.setText(self.file) lines = [] with open(self.file) as fd: for _ in range(10): lines.append(fd.readline()) self.preview.setText(''.join(lines)) reader = DataFrameReader(self.file) self.tablemodel.set_df(reader.df) self.tablemodel.set_columns(self.columnsbox.columns) self.tableview.resizeRowsToContents() self.update_ok() @Slot() def on_columns_order_changed(self): self.tablemodel.set_columns(self.columnsbox.columns) self.update_ok() def is_ok(self): columns = set(self.tablemodel.df.columns) for c in self.obligatorycolumns: if not isinstance(c, tuple): c = (c, ) c = set(c) if c.isdisjoint(columns): return False return True def update_ok(self): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(self.is_ok())
class RemoverDialog(QDialog): def __init__(self, parent=None): super(RemoverDialog, self).__init__(parent) self.setupUi() def setupUi(self): self.menuBar = QMenuBar() self.menuBar.show() self.pathInputBox = QLineEdit(self) self.pathInputBox.setEnabled(False) self.pathInputBox.setToolTip( 'Input a path or drag a directory here...') self.openPathButton = QPushButton('Open...', self) self.openPathButton.clicked.connect(self.openPath) inputLayout = QHBoxLayout() inputLayout.addWidget(QLabel('Path:', self)) inputLayout.addWidget(self.pathInputBox) inputLayout.addWidget(self.openPathButton) self.filterFolderCheckBox = QCheckBox('Folders', self) self.filterFolderCheckBox.setChecked(True) self.filterFolderCheckBox.toggled.connect(self.filter) self.filterFileCheckBox = QCheckBox('Files', self) self.filterFileCheckBox.setChecked(True) self.filterFileCheckBox.toggled.connect(self.filter) self.filterSuffixCheckBox = QCheckBox('Suffixes', self) self.filterSuffixCheckBox.setChecked(True) self.filterSuffixCheckBox.toggled.connect(self.filter) filterLayout = QHBoxLayout() filterLayout.addWidget(self.filterFolderCheckBox) filterLayout.addWidget(self.filterFileCheckBox) filterLayout.addWidget(self.filterSuffixCheckBox) filterLayout.addStretch() self.trashButton = QPushButton('Trash', self) self.trashButton.clicked.connect(self.trash) self.deleteButton = QPushButton('Delete', self) self.deleteButton.clicked.connect(self.delete) confirmLayout = QHBoxLayout() confirmLayout.addStretch() confirmLayout.addWidget(self.trashButton) confirmLayout.addWidget(self.deleteButton) layout = QVBoxLayout() layout.addLayout(inputLayout) layout.addLayout(filterLayout) layout.addWidget(self.createResultView()) layout.addLayout(confirmLayout) self.setAcceptDrops(True) self.setLayout(layout) self.setMinimumWidth(600) self.setWindowTitle('Remover') self.setWindowIcon(QApplication.style().standardIcon( QStyle.SP_DirIcon)) def createResultView(self): self.resultModel = ResultModel(self) self.resultView = QTableView(self) self.resultView.setSortingEnabled(True) self.resultView.setShowGrid(False) self.resultView.setAlternatingRowColors(True) self.resultView.verticalHeader().hide() self.resultView.setSelectionBehavior(QAbstractItemView.SelectRows) self.resultView.setSelectionMode(QAbstractItemView.ExtendedSelection) self.sortFilterModel = SortFilterResultModel(self.resultModel, self) self.resultView.setModel(self.sortFilterModel) return self.resultView def dragEnterEvent(self, event): if event.mimeData().hasUrls() and len(event.mimeData().urls()) == 1: event.acceptProposedAction() def dropEvent(self, event): if event.mimeData().hasUrls() and len(event.mimeData().urls()) == 1: self.pathInputBox.setText(event.mimeData().urls()[0].toLocalFile()) self.reloadPath(self.pathInputBox.text()) def openPath(self): path = QFileDialog.getExistingDirectory( parent=self, caption='Open', options=QFileDialog.ShowDirsOnly | QFileDialog.DontResolveSymlinks) if path: self.pathInputBox.setText(path) self.reloadPath(path) def filter(self): filters = [] if self.filterFolderCheckBox.isChecked(): filters.append('folder') if self.filterFileCheckBox.isChecked(): filters.append('file') if self.filterSuffixCheckBox.isChecked(): filters.append('suffix') self.sortFilterModel.setFilterRegularExpression('|'.join(filters)) self.sortFilterModel.filterRegularExpression() self.resultView.resizeRowsToContents() def trash(self): folders, files = self.collectSelectedFolderFiles() try: for file in files: send2trash(file) for folder in folders: if QFile(folder).exists(): send2trash(folder) except: QMessageBox.warning(self, 'Failed', 'Failed to trash selected files/folders') return self.reloadPath(self.pathInputBox.text()) def delete(self): folders, files = self.collectSelectedFolderFiles() try: for file in files: os.remove(file) for folder in folders: shutil.rmtree(folder) except: QMessageBox.warning(self, 'Failed', 'Failed to delete selected files/folders') return self.reloadPath(self.pathInputBox.text()) def collectSelectedFolderFiles(self): folders, files, suffixes = [], [], [] for index in self.resultView.selectedIndexes(): if index.column() != 0: # 忽略第二列的selection continue item = self.sortFilterModel.data(index, Qt.UserRole) if 'folder' == item.type: folders.append(item.name) elif 'file' == item.type: files.append(item.name) elif 'suffix' == item.type: suffixes.append(item.name[2:]) # 将后缀符合选中条件的文件添加到files中 path = self.pathInputBox.text() iterator = QDirIterator(path, filter=QDir.Files | QDir.Dirs | QDir.Hidden | QDir.NoDotAndDotDot, flags=QDirIterator.Subdirectories) folderPaths, filePaths = set(), set() while iterator.hasNext(): file = iterator.next() if '.' == file[-1] or '..' == file[-2]: continue fileInfo = QFileInfo(file) if fileInfo.isDir(): if fileInfo.fileName() in folders: folderPaths.add(fileInfo.absoluteFilePath()) if fileInfo.isFile(): if fileInfo.fileName() in files: filePaths.add(fileInfo.absoluteFilePath()) if fileInfo.suffix() in suffixes: filePaths.add(fileInfo.absoluteFilePath()) return sorted(folderPaths), filePaths def reloadPath(self, path): self.resultModel.reload(path) self.resultView.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.resultView.horizontalHeader().setSectionResizeMode( 1, QHeaderView.ResizeToContents) self.filter() self.resultView.sortByColumn(1, Qt.DescendingOrder)