Exemple #1
0
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()
Exemple #2
0
    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])
Exemple #4
0
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)
Exemple #5
0
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())
Exemple #6
0
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)