PyQT5と複数の列を使用したテーブルのフィルタリング

ベザドジャマリ

PyQt5Pandasデータフレームをテーブルの形式で表示し、MicrosoftExcelフィルターと同様の列フィルターオプションを提供するGUIを作成しようとしています。これまでのところ、私は同様のSO回答を採用することができましたこれがGUIの私のテーブルの写真です:

ここに画像の説明を入力してください

上の図に示すように、列をフィルタリングするには、正規表現フィルターと各列をクリックする2つの方法があります。ただし、対処が必要な問題があります。2番目の列をフィルター処理すると、現在適用されているフィルター(正規表現フィルターまたは列クリック)が消えます。2番目のフィルターをとして必要ですAND。つまり、列1、AND列2を満たすフィルターです

これが私のコードです:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

from PyQt5 import QtCore, QtGui, QtWidgets

import pandas as pd

class PandasModel(QtCore.QAbstractTableModel):
    def __init__(self, df=pd.DataFrame(), parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent=parent)
        self._df = df.copy()

    def toDataFrame(self):
        return self._df.copy()

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        if orientation == QtCore.Qt.Horizontal:
            try:
                return self._df.columns.tolist()[section]
            except (IndexError, ):
                return QtCore.QVariant()
        elif orientation == QtCore.Qt.Vertical:
            try:
                # return self.df.index.tolist()
                return self._df.index.tolist()[section]
            except (IndexError, ):
                return QtCore.QVariant()

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role != QtCore.Qt.DisplayRole:
            return QtCore.QVariant()

        if not index.isValid():
            return QtCore.QVariant()

        return QtCore.QVariant(str(self._df.iloc[index.row(), index.column()]))

    def setData(self, index, value, role):
        row = self._df.index[index.row()]
        col = self._df.columns[index.column()]
        if hasattr(value, 'toPyObject'):
            # PyQt4 gets a QVariant
            value = value.toPyObject()
        else:
            # PySide gets an unicode
            dtype = self._df[col].dtype
            if dtype != object:
                value = None if value == '' else dtype.type(value)
        self._df.set_value(row, col, value)
        return True

    def rowCount(self, parent=QtCore.QModelIndex()):
        return len(self._df.index)

    def columnCount(self, parent=QtCore.QModelIndex()):
        return len(self._df.columns)

    def sort(self, column, order):
        colname = self._df.columns.tolist()[column]
        self.layoutAboutToBeChanged.emit()
        self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
        self._df.reset_index(inplace=True, drop=True)
        self.layoutChanged.emit()


class myWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(myWindow, self).__init__(parent)
        self.centralwidget  = QtWidgets.QWidget(self)
        self.lineEdit       = QtWidgets.QLineEdit(self.centralwidget)
        self.view           = QtWidgets.QTableView(self.centralwidget)
        self.comboBox       = QtWidgets.QComboBox(self.centralwidget)
        self.label          = QtWidgets.QLabel(self.centralwidget)

        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
        self.gridLayout.addWidget(self.view, 1, 0, 1, 3)
        self.gridLayout.addWidget(self.comboBox, 0, 2, 1, 1)
        self.gridLayout.addWidget(self.label, 0, 0, 1, 1)

        self.setCentralWidget(self.centralwidget)
        self.label.setText("Regex Filter")

        self.load_sites()
        self.comboBox.addItems(["{0}".format(col) for col in self.model._df.columns])

        self.lineEdit.textChanged.connect(self.on_lineEdit_textChanged)
        self.comboBox.currentIndexChanged.connect(self.on_comboBox_currentIndexChanged)

        self.horizontalHeader = self.view.horizontalHeader()
        self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)


    def load_sites(self):

        df = pd.DataFrame({'site_codes': ['01', '02', '03', '04'],
                           'status': ['open', 'open', 'open', 'closed'],
                           'Location': ['east', 'north', 'south', 'east'],
                           'data_quality': ['poor', 'moderate', 'high', 'high']})

        self.model = PandasModel(df)
        self.proxy = QtCore.QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model)
        self.view.setModel(self.proxy)
        self.view.resizeColumnsToContents()

    @QtCore.pyqtSlot(int)
    def on_view_horizontalHeader_sectionClicked(self, logicalIndex):

        self.logicalIndex   = logicalIndex
        self.menuValues     = QtWidgets.QMenu(self)
        self.signalMapper   = QtCore.QSignalMapper(self)
        self.comboBox.blockSignals(True)
        self.comboBox.setCurrentIndex(self.logicalIndex)
        self.comboBox.blockSignals(True)

        valuesUnique = self.model._df.iloc[:, self.logicalIndex].unique()

        actionAll = QtWidgets.QAction("All", self)
        actionAll.triggered.connect(self.on_actionAll_triggered)
        self.menuValues.addAction(actionAll)
        self.menuValues.addSeparator()
        for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):
            action = QtWidgets.QAction(actionName, self)
            self.signalMapper.setMapping(action, actionNumber)
            action.triggered.connect(self.signalMapper.map)
            self.menuValues.addAction(action)
        self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
        headerPos = self.view.mapToGlobal(self.horizontalHeader.pos())
        posY = headerPos.y() + self.horizontalHeader.height()
        posX = headerPos.x() + self.horizontalHeader.sectionPosition(self.logicalIndex)

        self.menuValues.exec_(QtCore.QPoint(posX, posY))

    @QtCore.pyqtSlot()
    def on_actionAll_triggered(self):
        filterColumn = self.logicalIndex
        filterString = QtCore.QRegExp(  "",
                                        QtCore.Qt.CaseInsensitive,
                                        QtCore.QRegExp.RegExp
                                        )

        self.proxy.setFilterRegExp(filterString)
        self.proxy.setFilterKeyColumn(filterColumn)

    @QtCore.pyqtSlot(int)
    def on_signalMapper_mapped(self, i):
        stringAction = self.signalMapper.mapping(i).text()
        filterColumn = self.logicalIndex
        filterString = QtCore.QRegExp(  stringAction,
                                        QtCore.Qt.CaseSensitive,
                                        QtCore.QRegExp.FixedString
                                        )

        self.proxy.setFilterRegExp(filterString)
        self.proxy.setFilterKeyColumn(filterColumn)

    @QtCore.pyqtSlot(str)
    def on_lineEdit_textChanged(self, text):
        search = QtCore.QRegExp(    text,
                                    QtCore.Qt.CaseInsensitive,
                                    QtCore.QRegExp.RegExp
                                    )

        self.proxy.setFilterRegExp(search)

    @QtCore.pyqtSlot(int)
    def on_comboBox_currentIndexChanged(self, index):
        self.proxy.setFilterKeyColumn(index)


if __name__ == "__main__":
    import sys
    app  = QtWidgets.QApplication(sys.argv)
    main = myWindow()
    main.show()
    main.resize(800, 600)
    sys.exit(app.exec_())
eyllanesc

カスタムフィルタリングプロセスを実装する場合は、filterAcceptsRowメソッドをオーバーライドし、各列のテキストを取得して、条件を満たしているかどうかを確認し、Trueを返している場合は、Falseを返します。フィルタを再計算するには、invalidateFilterメソッドを呼び出す必要があります。

class CustomProxyModel(QtCore.QSortFilterProxyModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self._filters = dict()

    @property
    def filters(self):
        return self._filters

    def setFilter(self, expresion, column):
        if expresion:
            self.filters[column] = expresion
        elif column in self.filters:
            del self.filters[column]
        self.invalidateFilter()

    def filterAcceptsRow(self, source_row, source_parent):
        for column, expresion in self.filters.items():
            text = self.sourceModel().index(source_row, column, source_parent).data()
            regex = QtCore.QRegExp(
                expresion, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp
            )
            if regex.indexIn(text) == -1:
                return False
        return True
class myWindow(QtWidgets.QMainWindow):
    # ...

    def load_sites(self):
        # ...

        self.model = PandasModel(df)
        self.proxy = CustomProxyModel(self)
        self.proxy.setSourceModel(self.model)
        self.view.setModel(self.proxy)
        self.view.resizeColumnsToContents()
        print("finished loading sites")

    # ...

    @QtCore.pyqtSlot()
    def on_actionAll_triggered(self):
        filterColumn = self.logicalIndex
        self.proxy.setFilter("", filterColumn)

    @QtCore.pyqtSlot(int)
    def on_signalMapper_mapped(self, i):
        stringAction = self.signalMapper.mapping(i).text()
        filterColumn = self.logicalIndex
        self.proxy.setFilter(stringAction, filterColumn)

    @QtCore.pyqtSlot(str)
    def on_lineEdit_textChanged(self, text):
        self.proxy.setFilter(text, self.proxy.filterKeyColumn())

    @QtCore.pyqtSlot(int)
    def on_comboBox_currentIndexChanged(self, index):
        self.proxy.setFilterKeyColumn(index)

プラス: QHeaderViewのフォントを変更する場合は、以下に示すように、headerDataでフォントを返す必要があります。

class PandasModel(QtCore.QAbstractTableModel):
    def __init__(self, df=pd.DataFrame(), parent=None):
        QtCore.QAbstractTableModel.__init__(self, parent=parent)
        self._df = df.copy()
        self.bolds = dict()

    def toDataFrame(self):
        return self._df.copy()

    def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
        if orientation == QtCore.Qt.Horizontal:
            if role == QtCore.Qt.DisplayRole:
                try:
                    return self._df.columns.tolist()[section]
                except (IndexError,):
                    return QtCore.QVariant()
            elif role == QtCore.Qt.FontRole:
                return self.bolds.get(section, QtCore.QVariant())
        elif orientation == QtCore.Qt.Vertical:
            if role == QtCore.Qt.DisplayRole:
                try:
                    # return self.df.index.tolist()
                    return self._df.index.tolist()[section]
                except (IndexError,):
                    return QtCore.QVariant()
        return QtCore.QVariant()

    def setFont(self, section, font):
        self.bolds[section] = font
        self.headerDataChanged.emit(QtCore.Qt.Horizontal, 0, self.columnCount())
    # ...
class myWindow(QtWidgets.QMainWindow):
    # ...
   @QtCore.pyqtSlot()
    def on_actionAll_triggered(self):
        filterColumn = self.logicalIndex
        self.proxy.setFilter("", filterColumn)
        font = QtGui.QFont()
        self.model.setFont(filterColumn, font)

    @QtCore.pyqtSlot(int)
    def on_signalMapper_mapped(self, i):
        stringAction = self.signalMapper.mapping(i).text()
        filterColumn = self.logicalIndex
        self.proxy.setFilter(stringAction, filterColumn)
        font = QtGui.QFont()
        font.setBold(True)
        self.model.setFont(filterColumn, font)

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

PyQt5アプリを使用するためのPythonでの複数のフィルタリングDataFrame

分類Dev

リレーションを使用したAndroidRoomの検索とフィルタリング、複数のテーブルのクエリ

分類Dev

テーブル内の複数の列をフィルタリングする

分類Dev

C#:フィールドメソッドを使用したデータテーブルの複数の列によるLINQクエリのグループ化

分類Dev

str_detectを使用した複数の列のフィルタリング

分類Dev

「%in%」を使用した複数の列でのdplyrフィルタリング

分類Dev

ES6配列関数を使用した複数のアイテムのフィルタリング

分類Dev

複数の配列の値を使用したJavaScriptオブジェクトのフィルタリング

分類Dev

テーブルの複数の列のフィルタリング

分類Dev

Angularの1つのパイプを使用して、テーブルの複数の列をフィルタリングします

分類Dev

List.Generate()を使用した複数の列とさまざまな条件の動的フィルタリング

分類Dev

DRF-複数のクエリパラメータと外部キーを使用したフィルタリング

分類Dev

AngularMaterial-複数のフィルターと複数のフィルター値を同時に使用したフィルターテーブル

分類Dev

ハイブテーブル内の複数の配列フィールドでのフィルタリング

分類Dev

関連するテーブルの複数の列でフィルタリングされたDAXの合計

分類Dev

C#を使用して複数の列値でデータテーブルをフィルタリングする方法

分類Dev

複数のフィルタリングオプションを使用したテーブルのMySQLインデックス作成

分類Dev

jQuery-クラス名の配列を使用したテーブル行のフィルタリング

分類Dev

underscore.jsを使用した複数のプロパティでのオブジェクト配列フィルタリング

分類Dev

デザイナーの.uiファイルタブメニューパディングとウィンドウサイズを備えたpyqt5

分類Dev

LINQ を使用した製品テーブルのフィルタリング

分類Dev

PHPを使用したCRUDテーブルのフィルタリング

分類Dev

PyQt5でSQLite3テーブルをフィルタリングする方法

分類Dev

ブール論理を使用して1つのデータフレームの複数の列をマージおよびフィルタリングする

分類Dev

JSで複数のhtmlテーブルをフィルタリングする

分類Dev

SQLAlchemyは複数のテーブルの条件でクエリをフィルタリングします

分類Dev

複数のORを使用したクエリの表現と、Entity FrameworkCoreを使用した結合テーブルでのフィルター

分類Dev

条件でフィルタリングされた行を持つ複数のテーブルでのSQL結合

分類Dev

複数のルートとルーターアウトレットを使用した角度ルーティング

Related 関連記事

  1. 1

    PyQt5アプリを使用するためのPythonでの複数のフィルタリングDataFrame

  2. 2

    リレーションを使用したAndroidRoomの検索とフィルタリング、複数のテーブルのクエリ

  3. 3

    テーブル内の複数の列をフィルタリングする

  4. 4

    C#:フィールドメソッドを使用したデータテーブルの複数の列によるLINQクエリのグループ化

  5. 5

    str_detectを使用した複数の列のフィルタリング

  6. 6

    「%in%」を使用した複数の列でのdplyrフィルタリング

  7. 7

    ES6配列関数を使用した複数のアイテムのフィルタリング

  8. 8

    複数の配列の値を使用したJavaScriptオブジェクトのフィルタリング

  9. 9

    テーブルの複数の列のフィルタリング

  10. 10

    Angularの1つのパイプを使用して、テーブルの複数の列をフィルタリングします

  11. 11

    List.Generate()を使用した複数の列とさまざまな条件の動的フィルタリング

  12. 12

    DRF-複数のクエリパラメータと外部キーを使用したフィルタリング

  13. 13

    AngularMaterial-複数のフィルターと複数のフィルター値を同時に使用したフィルターテーブル

  14. 14

    ハイブテーブル内の複数の配列フィールドでのフィルタリング

  15. 15

    関連するテーブルの複数の列でフィルタリングされたDAXの合計

  16. 16

    C#を使用して複数の列値でデータテーブルをフィルタリングする方法

  17. 17

    複数のフィルタリングオプションを使用したテーブルのMySQLインデックス作成

  18. 18

    jQuery-クラス名の配列を使用したテーブル行のフィルタリング

  19. 19

    underscore.jsを使用した複数のプロパティでのオブジェクト配列フィルタリング

  20. 20

    デザイナーの.uiファイルタブメニューパディングとウィンドウサイズを備えたpyqt5

  21. 21

    LINQ を使用した製品テーブルのフィルタリング

  22. 22

    PHPを使用したCRUDテーブルのフィルタリング

  23. 23

    PyQt5でSQLite3テーブルをフィルタリングする方法

  24. 24

    ブール論理を使用して1つのデータフレームの複数の列をマージおよびフィルタリングする

  25. 25

    JSで複数のhtmlテーブルをフィルタリングする

  26. 26

    SQLAlchemyは複数のテーブルの条件でクエリをフィルタリングします

  27. 27

    複数のORを使用したクエリの表現と、Entity FrameworkCoreを使用した結合テーブルでのフィルター

  28. 28

    条件でフィルタリングされた行を持つ複数のテーブルでのSQL結合

  29. 29

    複数のルートとルーターアウトレットを使用した角度ルーティング

ホットタグ

アーカイブ