PyQT5 和使用多列过滤表

2023-12-10

我正在尝试做一个PyQt5GUI 以表格形式显示 Pandas 数据框并提供列过滤选项,类似于 Microsoft Excel 过滤器。到目前为止,我设法采用类似的所以答案。这是 GUI 中我的表格的图片:

enter image description here

如上图所示,有两种过滤列的方法:正则表达式过滤器和单击每列。然而,我需要帮助解决一个问题:当我过滤第二列时,当前应用的过滤器(正则表达式过滤器或列单击)消失。我想要第二个过滤器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_())

如果要实现自定义过滤过程,则必须重写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)

Plus:如果要更改 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)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

PyQT5 和使用多列过滤表 的相关文章

  • Python、Tkinter、更改标签颜色

    有没有一种简单的方法来更改按钮中文本的颜色 I use button text input text here 更改按下后按钮文本的内容 是否存在类似的颜色变化 button color red Use the foreground设置按钮
  • InterfaceError:连接已关闭(使用 django + celery + Scrapy)

    当我在 Celery 任务中使用 Scrapy 解析函数 有时可能需要 10 分钟 时 我得到了这个信息 我用 姜戈 1 6 5 django celery 3 1 16 芹菜 3 1 16 psycopg2 2 5 5 我也使用了psyc
  • Pycharm Python 控制台不打印输出

    我有一个从 Pycharm python 控制台调用的函数 但没有显示输出 In 2 def problem1 6 for i in range 1 101 2 print i end In 3 problem1 6 In 4 另一方面 像
  • DreamPie 不适用于 Python 3.2

    我最喜欢的 Python shell 是DreamPie http dreampie sourceforge net 我想将它与 Python 3 2 一起使用 我使用了 添加解释器 DreamPie 应用程序并添加了 Python 3 2
  • pandas 替换多个值

    以下是示例数据框 gt gt gt df pd DataFrame a 1 1 1 2 2 b 11 22 33 44 55 gt gt gt df a b 0 1 11 1 1 22 2 1 33 3 2 44 4 3 55 现在我想根据
  • SQL Alchemy 中的 NULL 安全不等式比较?

    目前 我知道如何表达 NULL 安全的唯一方法 SQL Alchemy 中的比较 其中与 NULL 条目的比较计算结果为 True 而不是 NULL 是 or field None field value 有没有办法在 SQL Alchem
  • NameError:名称“urllib”未定义”

    CODE import networkx as net from urllib request import urlopen def read lj friends g name fetch the friend list from Liv
  • Python - 按月对日期进行分组

    这是一个简单的问题 起初我认为很简单而忽略了它 一个小时过去了 我不太确定 所以 我有一个Python列表datetime对象 我想用图表来表示它们 x 值是年份和月份 y 值是此列表中本月发生的日期对象的数量 也许一个例子可以更好地证明这
  • Python - 在窗口最小化或隐藏时使用 pywinauto 控制窗口

    我正在尝试做的事情 我正在尝试使用 pywinauto 在 python 中创建一个脚本 以在后台自动安装 notepad 隐藏或最小化 notepad 只是一个示例 因为我将编辑它以与其他软件一起使用 Problem 问题是我想在安装程序
  • 如何改变Python中特定打印字母的颜色?

    我正在尝试做一个简短的测验 并且想将错误答案显示为红色 欢迎来到我的测验 您想开始吗 是的 祝你好运 法国的首都是哪里 法国 随机答案不正确的答案 我正在尝试将其显示为红色 我的代码是 print Welcome to my Quiz be
  • 在Python中重置生成器对象

    我有一个由多个yield 返回的生成器对象 准备调用该生成器是相当耗时的操作 这就是为什么我想多次重复使用生成器 y FunctionWithYield for x in y print x here must be something t
  • 如何在 Django 中使用并发进程记录到单个文件而不使用独占锁

    给定一个在多个服务器上同时执行的 Django 应用程序 该应用程序如何记录到单个共享日志文件 在网络共享中 而不保持该文件以独占模式永久打开 当您想要利用日志流时 这种情况适用于 Windows Azure 网站上托管的 Django 应
  • VSCode:调试配置中的 Python 路径无效

    对 Python 和 VSCode 以及 stackoverflow 非常陌生 直到最近 我已经使用了大约 3 个月 一切都很好 当尝试在调试器中运行任何基本的 Python 程序时 弹出窗口The Python path in your
  • 在 Pandas DataFrame Python 中添加新列[重复]

    这个问题在这里已经有答案了 例如 我在 Pandas 中有数据框 Col1 Col2 A 1 B 2 C 3 现在 如果我想再添加一个名为 Col3 的列 并且该值基于 Col2 式中 如果Col2 gt 1 则Col3为0 否则为1 所以
  • 从 Python 中的类元信息对 __init__ 函数进行类型提示

    我想做的是复制什么SQLAlchemy确实 以其DeclarativeMeta班级 有了这段代码 from sqlalchemy import Column Integer String from sqlalchemy ext declar
  • 在python中,如何仅搜索所选子字符串之前的一个单词

    给定文本文件中的长行列表 我只想返回紧邻其前面的子字符串 例如单词狗 描述狗的单词 例如 假设有这些行包含狗 hotdog big dog is dogged dog spy with my dog brown dogs 在这种情况下 期望
  • 循环标记时出现“ValueError:无法识别的标记样式 -d”

    我正在尝试编码pyplot允许不同标记样式的绘图 这些图是循环生成的 标记是从列表中选取的 为了演示目的 我还提供了一个颜色列表 版本是Python 2 7 9 IPython 3 0 0 matplotlib 1 4 3 这是一个简单的代
  • Spark.read 在 Databricks 中给出 KrbException

    我正在尝试从 databricks 笔记本连接到 SQL 数据库 以下是我的代码 jdbcDF spark read format com microsoft sqlserver jdbc spark option url jdbc sql
  • Python:元类属性有时会覆盖类属性?

    下面代码的结果让我感到困惑 class MyClass type property def a self return 1 class MyObject object metaclass MyClass a 2 print MyObject
  • Pandas 与 Numpy 数据帧

    看这几行代码 df2 df copy df2 1 df 1 df 1 values 1 df2 ix 0 0 我们的教练说我们需要使用 values属性来访问底层的 numpy 数组 否则我们的代码将无法工作 我知道 pandas Data

随机推荐

  • Google Play 应用签名和即时应用

    相当直接的问题 有人知道 Google Play 应用签名是否支持即时应用吗 我问的原因是 输入应用程序的签名配置 或选择密钥库文件 虽然可以在测试期间使用调试配置或密钥库 但生成的数字资产链接文件将与应用程序的发布版本不兼容 如果您确实上
  • 从 MVC 控制器创建/获取 DefaultHtmlGenerator

    我正在尝试在 MVC6 控制器方法内为 Microsoft AspNet Mvc Rendering DefaultHtmlGenerator 创建 或以某种方式获取它的实例 我想在我的 asp net mvc 控制器中生成用于验证我的模型
  • 生成正则表达式可以在 Python 中匹配的值列表

    我尝试使用正则表达式作为输入 并从那里生成正则表达式匹配的所有可能值 因此 例如 如果正则表达式是 以 a 开头 以 c 结尾的三个字母单词 则代码将生成一个包含值 aac abc acc adc a1c 的列表 是否有捷径可寻 我正在使用
  • 在Google云机器学习上部署Retrained inception模型

    我设法使用通用初始模型重新训练我的特定分类模型tutorial 我现在想将其部署在谷歌云机器学习上steps 我已经设法将其导出为 MetaGraph 但我无法获得正确的输入和输出 在本地使用它 我的图表入口点是DecodeJpeg con
  • OpenXML Sax 方法可将 100K+ 行快速导出到 Excel

    我一直在尝试提高写入 xlsx 的 SAX 方法的性能 我知道 Excel 中的行数限制为 1048576 行 我只达到过这个极限几次 在大多数情况下 我只写出大约 125K 到 250K 行 一个大数据集 我尝试过的代码似乎没有那么快 因
  • 如何过滤 top_hits 指标聚合结果 [Elasticsearch]

    我想按地址分组 然后按日期获取最新地址 然后按状态过滤此结果 ex address A date 10 10 1991 status sold address A date 10 10 2016 status active address
  • 外连接 Pandas 数据框

    我正在尝试外部连接 在 df1 上 两个 pandas 数据框 以下是示例数据框 df1 Index Team 1 Team 2 Team1 Score Team2 Score 0 A B 25 56 1 B C 30 55 2 D E 3
  • 向图例添加额外的项目

    我有以下数据 trait beta se p analysis signif trait1 0 078 0 01 9 00E 13 group1 1 trait2 0 076 0 01 1 70E 11 group1 1 trait3 0
  • Amazon Cognito 将 IAM 角色分配给用户池中的组并与身份池集成

    我正在尝试使用用户池中新添加的用户组并将其与联合身份集成 我按照以下步骤操作 在用户池中创建组 其中在 IAM 中创建的角色具有单独的角色 政策 创建用户并将其添加到用户组 创建一个身份池并在下面添加该 Cognito 提供程序身份验证提供
  • 使用异步解析 Json url

    运行此代码时出现异常 我想解析 url 它是一个 json 对象数组 package com example compsci 734t import java io BufferedReader import java io InputSt
  • C# 中的随机名称生成器

    我有一个女性和男性名字的列表 然后是数组中的姓氏列表 我想做的是使用随机生成器获取这些数组中的这些名称 并根据我的调用输出随机的名字和姓氏 完成后 我将在其他类中引用该方法 而不必每次都将其写出来 这是我到目前为止的代码 private v
  • 在 PrimeFaces 中将图标从 jQuery UI 更改为 FontAwesome

    我有一个 PrimeFacesp tree我可以使用添加字体很棒的展开和折叠图标this 但之前有一个来自 PrimeFaces 的箭头图标 我无法弄清楚如何将其切换为很棒的箭头字体 fa arrow circle down and fa
  • C# 中的基本算术运算是原子的

    基本算术运算是线程安全的吗 例如 如果有 对全局变量的操作 会被不同线程修改 是否有必要在它周围加锁 例如 void MyThread can have many running instances aGlobal 或者应该是 void M
  • 如何在宏中添加前缀/后缀标识符? [复制]

    这个问题在这里已经有答案了 当使用定义函数的宏时 是否可以为函数添加前缀 macro rules my test id ident arg expr gt test fn id my test impl stringify id arg 例
  • Laravel,无法复制目录或移动目录

    目前 我在 Laravel 中遇到问题 无法使用 moveDirectory 和 copyDirectory 但是 makeDirectory 或 deleteDirectory 工作正常 代码如下
  • 如何从 Win32 上的 Perl 中杀死一个可能不存在的程序?

    我正在寻找一种方法让 Perl 杀死 Win32 上的所有 firefox exe 进程 并且如果不存在进程则不会给出错误 我目前正在使用 system taskkill F IM firefox exe 当 firefox 不存在时 会抛
  • 将 BLOB 转换为图像并显示它(ReactJS)

    MySQL 数据库保存着用户图像 我想查询该图像并将其显示在导航栏上 这是我的导航栏组件 它使用axios post向我的服务器发出请求 我尝试将 blob 转换为图像并将其存储在变量中 但我不确定为什么img onload从不火灾 exp
  • 删除特定控件的所有事件处理程序

    我正在 winForm 中编写一个应用程序 我在 from1 中有一个面板 它有很多事件处理程序 当我处理 panel1 并创建新 panel 时 先前的事件存在并触发 为了删除 panel1 事件 我尝试了下面的代码 panel1 Cli
  • 这看起来不像一个函数。这是什么?

    一个朋友让我用 C 语言编写一个函数来返回数组的第 100 个元素 我对 C 不太熟悉 所以我不确定如何创建一个可以对任何类型的数组执行此操作的通用函数 所以我作弊并假设它是一个整数数组并编写了这个函数 int GetHundredthEl
  • PyQT5 和使用多列过滤表

    我正在尝试做一个PyQt5GUI 以表格形式显示 Pandas 数据框并提供列过滤选项 类似于 Microsoft Excel 过滤器 到目前为止 我设法采用类似的所以答案 这是 GUI 中我的表格的图片 如上图所示 有两种过滤列的方法 正