PyQt 自定义翻页控件实现

105 篇文章 ¥59.90 ¥99.00
本文介绍如何使用PyQt库创建自定义翻页控件,包括安装PyQt5、创建`Paginator`类、定义UI布局及事件处理,以及在主程序中使用该控件的示例。通过这个教程,读者可以学会如何增强用户界面体验。

在本文中,我将向您展示如何使用 PyQt 库自定义翻页控件。翻页控件是一种常见的用户界面元素,用于浏览大量数据或分页显示内容。我们将创建一个简单的翻页控件,其中包含前一页、后一页和页码显示。

首先,我们需要安装 PyQt 库。您可以使用以下命令使用 pip 安装 PyQt5:

pip install pyqt5

接下来,我们将创建一个名为 “paginator.py” 的 Python 文件,并导入所需的模块:

from PyQt5.QtWidgets import QWidget, QHBoxLayout, QPushButton, QLabel


class Paginator(QWidget
<think>在PyQt5中,实现自定义表头的排序和筛选功能,可以通过使用`QSortFilterProxyModel`来实现动态筛选,同时结合自定义表头视图(如`QHeaderView`)来添加交互控件(如下拉菜单、输入框等)进行筛选。此外,排序功能可以直接利用`QTableView`或`QTableWidget`自带的排序功能(通过点击表头排序)。 但是,如果需要在表头嵌入自定义的筛选控件(例如每列都有一个下拉框或输入框),则需要自定义表头视图。下面将分步骤说明如何实现: ### 方法概述 1. **使用模型/视图架构**:推荐使用`QTableView` + `QStandardItemModel`(或自定义模型)配合`QSortFilterProxyModel`来实现筛选和排序。 2. **自定义表头**:通过继承`QHeaderView`或使用`QWidget`作为表头替代品,在表头位置放置筛选控件。但更常见的做法是使用一个单独的`QWidget`(包含一行筛选控件)放在表格上方,与列对齐。 3. **动态筛选**:当筛选控件的内容变化时,更新`QSortFilterProxyModel`的筛选条件。 ### 步骤详解 #### 1. 基本表格和代理模型设置 ```python from PyQt5.QtWidgets import QApplication, QTableView, QHeaderView, QVBoxLayout, QWidget, QLineEdit, QComboBox from PyQt5.QtCore import Qt, QSortFilterProxyModel from PyQt5.QtGui import QStandardItemModel, QStandardItem class FilterHeaderView(QWidget): """自定义表头筛选控件,放在表格上方,与列对齐""" def __init__(self, parent=None): super().__init__(parent) self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) # 用于存放每列的筛选控件 self.filter_widgets = [] def set_headers(self, headers): """设置表头,创建相应数量的筛选控件""" # 清除原有控件 for widget in self.filter_widgets: widget.deleteLater() self.filter_widgets.clear() # 创建新的控件(这里使用QLineEdit作为示例,也可以使用QComboBox等) for header in headers: line_edit = QLineEdit() line_edit.setPlaceholderText(f"筛选{header}") self.filter_widgets.append(line_edit) self.layout.addWidget(line_edit) class TableViewWithFilter(QWidget): def __init__(self): super().__init__() self.layout = QVBoxLayout(self) self.setLayout(self.layout) # 创建表格视图和模型 self.table_view = QTableView() self.model = QStandardItemModel() self.proxy_model = QSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.proxy_model.setFilterKeyColumn(-1) # 设置过滤所有列 self.table_view.setModel(self.proxy_model) # 设置表头 self.headers = ["姓名", "年龄", "城市"] self.model.setHorizontalHeaderLabels(self.headers) # 创建自定义表头筛选控件 self.filter_header = FilterHeaderView() self.filter_header.set_headers(self.headers) # 将筛选控件和表格添加到布局 self.layout.addWidget(self.filter_header) self.layout.addWidget(self.table_view) # 连接筛选控件的信号 for col, widget in enumerate(self.filter_header.filter_widgets): widget.textChanged.connect(lambda text, col=col: self.on_filter_text_changed(col, text)) def on_filter_text_changed(self, column, text): """当筛选文本变化时,设置代理模型的过滤规则""" # 设置过滤列和正则表达式(这里使用包含匹配,不区分大小写) self.proxy_model.setFilterKeyColumn(column) self.proxy_model.setFilterCaseSensitivity(Qt.CaseInsensitive) self.proxy_model.setFilterRegExp(text) # 注意:由于每次只能设置一个列的过滤,所以需要将其他列的过滤条件合并。但上面的设置是每次只过滤一列,这不符合多列同时筛选的需求。 # 因此,我们需要一个更复杂的方法来处理多列筛选。 # 为了支持多列同时筛选,我们需要自定义一个代理模型,重写filterAcceptsRow方法。 ``` #### 2. 支持多列筛选的自定义代理模型 上面的示例中,每次设置一个列的过滤条件会覆盖之前的条件。因此,我们需要一个自定义的代理模型来同时考虑多个列的过滤条件。 ```python class MultiColumnSortFilterProxyModel(QSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) self.filters = {} # 存储每列的过滤条件,key为列索引,value为过滤字符串 def setFilterByColumn(self, column, pattern): """设置某一列的过滤条件""" if pattern: self.filters[column] = pattern elif column in self.filters: del self.filters[column] self.invalidateFilter() def filterAcceptsRow(self, source_row, source_parent): """重写过滤函数,只有当所有列的条件都满足时才显示该行""" # 如果没有设置任何过滤条件,则接受所有行 if not self.filters: return True # 检查每一列是否满足条件 for column, pattern in self.filters.items(): index = self.sourceModel().index(source_row, column, source_parent) text = self.sourceModel().data(index, Qt.DisplayRole) # 获取显示文本 if text is None: return False # 如果有一列不匹配,则返回False if pattern.lower() not in text.lower(): return False return True ``` #### 3. 在表格中使用自定义代理模型 修改`TableViewWithFilter`类,使用自定义的代理模型`MultiColumnSortFilterProxyModel`,并调整信号连接: ```python class TableViewWithFilter(QWidget): def __init__(self): # ... 前面的初始化代码 ... # 使用自定义代理模型 self.proxy_model = MultiColumnSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) self.table_view.setModel(self.proxy_model) # ... 其他代码 ... def on_filter_text_changed(self, column, text): """当筛选文本变化时,设置代理模型的过滤规则""" # 调用自定义代理模型的方法设置过滤条件 self.proxy_model.setFilterByColumn(column, text) ``` #### 4. 排序功能 排序功能可以直接使用`QSortFilterProxyModel`提供的排序功能。通过设置`setSortingEnabled(True)`启用表头点击排序。 ```python self.table_view.setSortingEnabled(True) # 启用排序 ``` ### 完整代码示例 下面是一个完整的示例代码,演示如何实现自定义表头筛选(多列同时筛选)和排序功能: ```python import sys from PyQt5.QtWidgets import (QApplication, QTableView, QVBoxLayout, QWidget, QLineEdit, QHeaderView, QAbstractItemView) from PyQt5.QtCore import Qt, QSortFilterProxyModel, QModelIndex from PyQt5.QtGui import QStandardItemModel, QStandardItem class MultiColumnSortFilterProxyModel(QSortFilterProxyModel): def __init__(self, parent=None): super().__init__(parent) self.filters = {} def setFilterByColumn(self, column, pattern): if pattern: self.filters[column] = pattern elif column in self.filters: del self.filters[column] self.invalidateFilter() def filterAcceptsRow(self, source_row, source_parent): if not self.filters: return True for column, pattern in self.filters.items(): index = self.sourceModel().index(source_row, column, source_parent) if index.isValid(): text = self.sourceModel().data(index, Qt.DisplayRole) if text is None: return False if pattern.lower() not in text.lower(): return False return True class FilterHeaderView(QWidget): def __init__(self, headers, parent=None): super().__init__(parent) self.layout = QVBoxLayout(self) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.filter_widgets = [] self.set_headers(headers) def set_headers(self, headers): # 清除原有控件 for widget in self.filter_widgets: widget.deleteLater() self.filter_widgets.clear() # 创建新的控件 for header in headers: line_edit = QLineEdit() line_edit.setPlaceholderText(f"筛选{header}") self.filter_widgets.append(line_edit) self.layout.addWidget(line_edit) class TableViewWithFilter(QWidget): def __init__(self, headers, data): super().__init__() self.layout = QVBoxLayout(self) self.setLayout(self.layout) self.headers = headers # 创建模型 self.model = QStandardItemModel() self.model.setHorizontalHeaderLabels(headers) # 填充数据 for row in data: items = [QStandardItem(str(item)) for item in row] self.model.appendRow(items) # 创建代理模型 self.proxy_model = MultiColumnSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) # 创建表格视图 self.table_view = QTableView() self.table_view.setModel(self.proxy_model) self.table_view.setSortingEnabled(True) # 启用排序 # 创建自定义表头筛选控件 self.filter_header = FilterHeaderView(headers) self.layout.addWidget(self.filter_header) self.layout.addWidget(self.table_view) # 连接信号 for col, widget in enumerate(self.filter_header.filter_widgets): widget.textChanged.connect(lambda text, col=col: self.on_filter_text_changed(col, text)) def on_filter_text_changed(self, column, text): self.proxy_model.setFilterByColumn(column, text) if __name__ == '__main__': app = QApplication(sys.argv) headers = ["姓名", "年龄", "城市"] data = [ ["张三", 25, "北京"], ["李四", 30, "上海"], ["王五", 28, "广州"], ["赵六", 22, "深圳"], ["孙七", 35, "杭州"] ] window = TableViewWithFilter(headers, data) window.resize(600, 400) window.show() sys.exit(app.exec_()) ``` ### 说明 - **多列筛选**:通过自定义代理模型`MultiColumnSortFilterProxyModel`,重写`filterAcceptsRow`方法,实现多列同时筛选。 - **排序**:通过`setSortingEnabled(True)`启用点击表头排序,代理模型会自动处理排序。 - **自定义表头筛选控件**:使用一个独立的`QWidget`(`FilterHeaderView`)来放置每列的筛选输入框,并确保与表格列对齐。 ### 注意事项 - 如果需要更复杂的筛选(如数字范围、日期范围等),可以在`FilterHeaderView`中为不同列使用不同的控件(如`QSpinBox`、`QDateEdit`等),并在代理模型中实现相应的过滤逻辑。 - 上述示例中,筛选控件与表格列的对齐依赖于布局管理。如果调整列宽,可能需要同步调整筛选控件的布局(可以通过监听表头的`sectionResized`信号来实现)。 ### 相关问题 1. 如何在PyQt5实现表头筛选控件的列宽与表格列宽同步? 2. 如何为PyQt5表格的不同列使用不同类型的筛选控件(如下拉框、日期选择器等)? 3. PyQt5中如何实现表格数据的多条件排序(如先按列A升序,再按列B降序)? 4. 如何在PyQt5实现表格数据的实时高亮显示(即筛选时高亮匹配的文本)? 5. 如何将PyQt5表格中筛选后的数据导出到CSV文件?</think># PyQt5 自定义表头排序与筛选实现PyQt5实现自定义表头的排序和筛选功能,可以通过使用 `QSortFilterProxyModel` 结合自定义表头视图来实现。下面我将详细介绍实现方法,并提供完整代码示例。 ## 实现原理 1. **模型/视图架构**:使用 `QStandardItemModel` 存储数据,`QTableView` 显示数据 2. **代理模型**:使用 `QSortFilterProxyModel` 实现排序和筛选功能 3. **自定义表头**:继承 `QHeaderView` 创建自定义表头,添加筛选控件 ## 完整代码实现 ```python import sys from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView, QHeaderView, QVBoxLayout, QWidget, QLineEdit, QPushButton, QComboBox) from PyQt5.QtCore import Qt, QSortFilterProxyModel, QAbstractItemModel, QModelIndex from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont class FilterHeader(QHeaderView): """自定义表头类,添加筛选控件""" def __init__(self, orientation, parent=None): super().__init__(orientation, parent) self.setSectionsClickable(True) self.setStretchLastSection(True) self.filter_widgets = [] def set_filter_widgets(self, count): """创建并添加筛选控件""" # 清除现有控件 for widget in self.filter_widgets: widget.deleteLater() self.filter_widgets.clear() # 创建新的筛选控件 for i in range(count): line_edit = QLineEdit(self) line_edit.setPlaceholderText(f"筛选列 {i+1}") line_edit.setClearButtonEnabled(True) self.filter_widgets.append(line_edit) def sizeHint(self): """调整表头高度以适应筛选控件""" size = super().sizeHint() size.setHeight(size.height() * 2) # 增加高度以容纳筛选控件 return size def resizeEvent(self, event): """调整控件大小和位置""" super().resizeEvent(event) for i, widget in enumerate(self.filter_widgets): pos = self.sectionPosition(i) width = self.sectionSize(i) widget.setGeometry(pos, self.height() // 2, width, self.height() // 2) class CustomSortFilterProxyModel(QSortFilterProxyModel): """自定义代理模型,实现多列筛选""" def __init__(self, parent=None): super().__init__(parent) self.filters = {} def set_filter(self, column, pattern): """设置筛选条件""" if pattern: self.filters[column] = pattern elif column in self.filters: del self.filters[column] self.invalidateFilter() def filterAcceptsRow(self, source_row, source_parent): """重写筛选逻辑,支持多列筛选""" if not self.filters: return True for column, pattern in self.filters.items(): index = self.sourceModel().index(source_row, column, source_parent) data = self.sourceModel().data(index, Qt.DisplayRole) if pattern.lower() not in str(data).lower(): return False return True class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("PyQt5 自定义表头排序与筛选") self.setGeometry(100, 100, 800, 600) # 创建主部件和布局 central_widget = QWidget() self.setCentralWidget(central_widget) layout = QVBoxLayout(central_widget) # 创建模型和代理模型 self.model = self.create_model() self.proxy_model = CustomSortFilterProxyModel() self.proxy_model.setSourceModel(self.model) # 创建表格视图 self.table_view = QTableView() self.table_view.setModel(self.proxy_model) self.table_view.setSortingEnabled(True) # 启用排序 # 创建自定义表头 self.header = FilterHeader(Qt.Horizontal, self.table_view) self.table_view.setHorizontalHeader(self.header) self.header.set_filter_widgets(self.model.columnCount()) # 连接筛选信号 for i, widget in enumerate(self.header.filter_widgets): widget.textChanged.connect(lambda text, col=i: self.proxy_model.set_filter(col, text)) # 添加重置按钮 reset_btn = QPushButton("重置筛选") reset_btn.clicked.connect(self.reset_filters) # 添加排序方式选择 sort_combo = QComboBox() sort_combo.addItems(["升序 (A-Z)", "降序 (Z-A)"]) sort_combo.currentIndexChanged.connect(self.change_sort_order) # 添加到布局 layout.addWidget(self.table_view) layout.addWidget(sort_combo) layout.addWidget(reset_btn) # 调整列宽 self.table_view.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) def create_model(self): """创建示例数据模型""" model = QStandardItemModel(10, 4) model.setHorizontalHeaderLabels(["姓名", "年龄", "城市", "职业"]) # 填充数据 names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九", "吴十", "郑十一", "王十二"] ages = [25, 32, 28, 45, 22, 39, 31, 29, 35, 27] cities = ["北京", "上海", "广州", "深圳", "杭州", "成都", "重庆", "武汉", "南京", "西安"] occupations = ["工程师", "设计师", "教师", "医生", "律师", "会计", "销售", "经理", "作家", "研究员"] for row in range(model.rowCount()): model.setItem(row, 0, QStandardItem(names[row])) model.setItem(row, 1, QStandardItem(str(ages[row]))) model.setItem(row, 2, QStandardItem(cities[row])) model.setItem(row, 3, QStandardItem(occupations[row])) return model def reset_filters(self): """重置所有筛选条件""" for widget in self.header.filter_widgets: widget.clear() self.proxy_model.filters = {} self.proxy_model.invalidateFilter() def change_sort_order(self, index): """更改排序顺序""" if index == 0: # 升序 self.proxy_model.setSortOrder(Qt.AscendingOrder) else: # 降序 self.proxy_model.setSortOrder(Qt.DescendingOrder) self.proxy_model.sort(0) # 按第一列重新排序 if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec_()) ``` ## 功能说明 1. **自定义表头筛选**: - 每列表头下方添加文本筛选框 - 支持多列同时筛选 - 支持大小写不敏感的模糊匹配 2. **排序功能**: - 点击表头可对列进行排序 - 提供下拉框选择升序或降序 3. **其他功能**: - 重置按钮清除所有筛选条件 - 自适应列宽布局 ## 实现要点 1. **自定义表头类 `FilterHeader`**: - 继承自 `QHeaderView` - 重写 `sizeHint()` 增加表头高度 - 重写 `resizeEvent()` 动态调整筛选控件位置 - 为每列创建 `QLineEdit` 作为筛选控件 2. **自定义代理模型 `CustomSortFilterProxyModel`**: - 继承自 `QSortFilterProxyModel` - 实现 `set_filter()` 方法设置筛选条件 - 重写 `filterAcceptsRow()` 实现多列筛选逻辑 3. **主窗口集成**: - 连接筛选控件的 `textChanged` 信号 - 实现重置筛选功能 - 提供排序方式选择 ## 扩展建议 1. **不同类型筛选控件**: - 对于数值列,可以使用范围筛选器(如 `QSpinBox`) - 对于分类数据,可以使用 `QComboBox` 提供选项 - 对于日期列,可以使用 `QDateEdit` 2. **性能优化**: - 对于大型数据集,添加筛选延迟(使用 `QTimer`) - 实现分页加载数据 3. **高级功能**: - 添加筛选状态指示器 - 保存和加载筛选条件 - 导出筛选后的数据 ## 相关问题 1. 如何在 PyQt5 中为不同数据类型的列实现不同的筛选控件(如数值范围、日期选择等)? 2. PyQt5 中如何实现表格数据的多列排序(如先按姓名排序,再按年龄排序)? 3. 如何在 PyQt5 中为表格筛选功能添加正则表达式支持? 4. PyQt5 中如何实现表格数据的实时高亮显示(即筛选时高亮匹配的文本)? 5. 如何将 PyQt5 表格中筛选后的数据导出到 CSV 或 Excel 文件?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值