Я хотел бы отобразить виджеты между QHeaderView
и остальной частью QTableView
, как на приведенном ниже примере изображения (создано с помощью Photoshop), так как это кажется естественным способом включения ввода для фильтрации столбцов.
Как вставлять виджеты между QHeaderView и QTableView?
Ответы (2)
Ниже представлена демонстрация класса FilterHeader
, которую я написал для одного из своих проектов. Возможно, вам придется адаптировать его под свои нужды, но он уже должен делать то, что вам нужно. Отступы вокруг полей фильтра вряд ли будут работать одинаково на всех платформах, поэтому вам может потребоваться настроить код в методе adjustPositions
.
import sys
from PyQt4 import QtCore, QtGui
class FilterHeader(QtGui.QHeaderView):
filterActivated = QtCore.pyqtSignal()
def __init__(self, parent):
super().__init__(QtCore.Qt.Horizontal, parent)
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
self.setResizeMode(QtGui.QHeaderView.Stretch)
self.setDefaultAlignment(
QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(
self.adjustPositions)
def setFilterBoxes(self, count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
editor = QtGui.QLineEdit(self.parent())
editor.setPlaceholderText('Filter')
editor.returnPressed.connect(self.filterActivated.emit)
self._editors.append(editor)
self.adjustPositions()
def sizeHint(self):
size = super().sizeHint()
if self._editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0, 0, 0, height + self._padding)
else:
self.setViewportMargins(0, 0, 0, 0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index, editor in enumerate(self._editors):
height = editor.sizeHint().height()
editor.move(
self.sectionPosition(index) - self.offset() + 2,
height + (self._padding // 2))
editor.resize(self.sectionSize(index), height)
def filterText(self, index):
if 0 <= index < len(self._editors):
return self._editors[index].text()
return ''
def setFilterText(self, index, text):
if 0 <= index < len(self._editors):
self._editors[index].setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
class Window(QtGui.QWidget):
def __init__(self):
super(Window, self).__init__()
self.view = QtGui.QTableView()
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.view)
header = FilterHeader(self.view)
self.view.setHorizontalHeader(header)
model = QtGui.QStandardItemModel(self.view)
model.setHorizontalHeaderLabels('One Two Three Four Five'.split())
self.view.setModel(model)
header.setFilterBoxes(model.columnCount())
header.filterActivated.connect(self.handleFilterActivated)
def handleFilterActivated(self):
header = self.view.horizontalHeader()
for index in range(header.count()):
print((index, header.filterText(index)))
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 600, 300)
window.show()
sys.exit(app.exec_())
person
ekhumoro
schedule
03.06.2017
Я протестировал его (с PySide), и он работает, и это отличное начало. Тем не менее, я заметил некоторые незначительные неудобства отображения с hoverEvents (в основном просто то, что фокус исчезает/слегка мерцает без видимой причины, когда мышь находится над QLineEdit). Итак, я зарегистрировал доставку событий в QLineEdits, и кажется, что все события перехватываются FilterView. (Что, вероятно, молча распределяет его по QLineEdits, поскольку набор текста и т. Д. Все еще работает). Тем не менее, это заставляет меня задаться вопросом, связано ли это с sizeHint() FilterView, охватывающим область QLineEdits, и может ли это вызвать проблемы?
- person timmwagener; 07.06.2017
@тимвагенер. У меня все отлично работает как с PyQt4, так и с PySide (в Linux). Я думаю, что проблема должна быть на вашей стороне. В коде, который я разместил, нет ничего, что могло бы вызвать описанную вами проблему.
- person ekhumoro; 07.06.2017
Хорошо, проблема возникает в Windows 7 с включенным рабочим столом Aero (по умолчанию). Редактирование строк в представлении заголовка отображает поведение выделения, отличное от редактирования строк, добавленных в обычный макет. Вот небольшой мод. вашего примера, чтобы смоделировать его. Когда рабочий стол Aero не включен, редактирование строк не выделяется, поэтому разница не видна. Меня беспокоит менее просто визуальный шум (хотя я не мог использовать его таким образом), а больше то, что, возможно, намекает на какую-то проблему с порядком update/paintEvent!?
- person timmwagener; 07.06.2017
@тимвагенер. Боюсь, мне нечего добавить, так как я не могу тестировать в Windows. Может быть, попробуйте проверить, как часто вызывается
updateGeometries
. Кажется, вы обнаружили возможную ошибку Qt в плагине стиля Aero. Но вам нужно намного сократить пример, чтобы вы могли выделить реальную причину. Для начала я хотел бы посмотреть, возможно ли воспроизвести проблему вне представления заголовка.
- person ekhumoro; 07.06.2017
Я мог бы исправить эту проблему с подсветкой, создав LineEdits в TableView, а не в HeaderView. Теперь они получают все обновления от сигналов и размещаются правильно, но отображают то же поведение подсветки, что и обычно. Я подозреваю, что это как-то связано с
updateGeometries()
запуском событий/обновлений для всех дочерних элементов HeaderView
, что приводит к тому, что редактирование строки теряет фокус или около того. Но тут только угадаю.
- person timmwagener; 08.06.2017
@тимвагенер. Я изменил свой демонстрационный код в соответствии с вашим предложением, поскольку он не влияет на то, как он работает в Linux, и поэтому должен дать более общий ответ. Проблема, безусловно, вызвана ошибкой в стилевом плагине Aero, поскольку поведение фокуса не должно произвольно зависеть от того, какой родительский виджет используется.
- person ekhumoro; 08.06.2017
Вы когда-нибудь пытались установить
view.setSortingEnabled(True)
в табличном представлении, содержащем новый экземпляр представления заголовка? У меня возникла проблема, что представление больше не хочет сортироваться. Как будто сигнал из нового представления заголовка не подключен к измененному слоту сортировки представления после вызова view.setHorizontalHeader(header)
. Это также не относится к FilterHeader
, но происходит только при установке новых ванильных экземпляров QtGui.QHeaderView
. Индикатор сортировки отображается, но никакой сортировки не происходит. Если новый вид заголовка не установлен, сортировка работает должным образом.
- person timmwagener; 10.06.2017
Я разместил проблему сортировки с конкретным демонстрационным кодом в качестве нового вопроса здесь
- person timmwagener; 10.06.2017
Вот ответ @ekhumoro, перенесенный на PyQt5
и с небольшим изменением - добавлено поле со списком в 4-м столбце.
import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QHeaderView, QWidget, QLineEdit, QApplication, QTableView, QVBoxLayout, QLineEdit, QComboBox
from PyQt5.QtCore import pyqtSignal
class FilterHeader(QHeaderView):
filterActivated = QtCore.pyqtSignal()
def __init__(self, parent):
super().__init__(QtCore.Qt.Horizontal, parent)
self._editors = []
self._padding = 4
self.setStretchLastSection(True)
#self.setResizeMode(QHeaderView.Stretch)
self.setDefaultAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
self.setSortIndicatorShown(False)
self.sectionResized.connect(self.adjustPositions)
parent.horizontalScrollBar().valueChanged.connect(self.adjustPositions)
def setFilterBoxes(self, count):
while self._editors:
editor = self._editors.pop()
editor.deleteLater()
for index in range(count):
if index == 3:
editor = QComboBox(self.parent())
#editor.returnPressed.connect(self.filterActivated.emit)
editor.addItems(["One","Two"])
else:
editor = QLineEdit(self.parent())
editor.setPlaceholderText('Filter')
editor.returnPressed.connect(self.filterActivated.emit)
self._editors.append(editor)
self.adjustPositions()
def sizeHint(self):
size = super().sizeHint()
if self._editors:
height = self._editors[0].sizeHint().height()
size.setHeight(size.height() + height + self._padding)
return size
def updateGeometries(self):
if self._editors:
height = self._editors[0].sizeHint().height()
self.setViewportMargins(0, 0, 0, height + self._padding)
else:
self.setViewportMargins(0, 0, 0, 0)
super().updateGeometries()
self.adjustPositions()
def adjustPositions(self):
for index, editor in enumerate(self._editors):
height = editor.sizeHint().height()
editor.move( self.sectionPosition(index) - self.offset() + 2, height + (self._padding // 2))
editor.resize(self.sectionSize(index), height)
def filterText(self, index):
if 0 <= index < len(self._editors):
return self._editors[index].text()
return ''
def setFilterText(self, index, text):
if 0 <= index < len(self._editors):
self._editors[index].setText(text)
def clearFilters(self):
for editor in self._editors:
editor.clear()
class Window(QWidget):
def __init__(self):
super(Window, self).__init__()
self.view = QTableView()
layout = QVBoxLayout(self)
layout.addWidget(self.view)
header = FilterHeader(self.view)
self.view.setHorizontalHeader(header)
model = QtGui.QStandardItemModel(self.view)
model.setHorizontalHeaderLabels('One Two Three Four Five'.split())
self.view.setModel(model)
header.setFilterBoxes(model.columnCount())
header.filterActivated.connect(self.handleFilterActivated)
def handleFilterActivated(self):
header = self.view.horizontalHeader()
for index in range(header.count()):
print((index, header.filterText(index)))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = Window()
window.setGeometry(600, 100, 600, 300)
window.show()
sys.exit(app.exec_())
person
Oak_3260548
schedule
10.08.2020