Строки "Итого" в QTableView PyQt5
В QTableView
добавил строки итого в виде таблицы self.inner_table
, которые находятся прямо под основной таблицей. Если количество строк такое, эта таблица не помещается в экран, то при прокрутке вниз строки "Итого" появляются.
Однако, проблема в том, что при прокрутке вверх строка "Итого" не исчезает так же, как и любые строки самой таблицы.
Для большего понимания примените код.
Также мне нужно, чтобы централизация виджетов была внутри CustomTableView без передачи в него аргумента main_window.
Пожалуйста, поправьте код так, чтобы CustomTableView был самодостаточным в плане создания строки "Итого": чтобы не требовалось передавать в него main_window и не требовался класс ContentWidget
Код прилагаю:
import sys
from PyQt5.QtWidgets import QTableView, QMainWindow, \
QApplication, QAbstractItemView, QVBoxLayout, QWidget, \
QHeaderView, QScrollArea, QHBoxLayout
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt, QSize
class ContentWidget(QWidget):
def sizeHint(self): # <--- sizeHint
return QSize(1500, 1000)
class CustomTableView(QTableView):
def __init__(self, main_window):
super().__init__()
self.setModel(QStandardItemModel(55, 10))
self.horizontalHeader().setVisible(False)
self.verticalHeader().setVisible(False)
self.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.Stretch)
self.setSelectionMode(
QAbstractItemView.SelectionMode.SingleSelection)
self.scrollArea = QScrollArea()
self.content_widget = ContentWidget()
self.scrollArea.setWidget(self.content_widget)
self.add_total_row()
self.setup_layout()
main_window.setCentralWidget(self.scrollArea)
def add_total_row(self):
self.inner_model = QStandardItemModel(2, 10)
self.inner_table = QTableView(self)
self.inner_table.setMaximumHeight(82)
self.inner_table.setModel(self.inner_model)
self.inner_table.horizontalHeader().setVisible(False)
self.inner_table.verticalHeader().setVisible(False)
self.inner_table.setVerticalScrollBarPolicy(
Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
self.inner_table.horizontalHeader().setSectionResizeMode(
QHeaderView.ResizeMode.Stretch)
self.inner_table.setEditTriggers(
QAbstractItemView.EditTrigger.NoEditTriggers)
self.inner_table.setSelectionMode(
QAbstractItemView.SelectionMode.NoSelection)
def setup_layout(self):
self.layout = QVBoxLayout(self.content_widget)
self.layout.setSpacing(0)
self.layout.addWidget(self)
self.layout.addWidget(
self.inner_table)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = CustomTableView(self) # Pass the MainWindow instance as parent
self.resize(1100, 600)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
Ответы (2 шт):
Попробуйте так:
import sys
from PyQt5.QtWidgets import QTableView, QMainWindow, \
QApplication, QAbstractItemView, QVBoxLayout, QWidget, \
QHeaderView
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt
class CustomTableView(QTableView):
def __init__(self, parent= None):
super().__init__(parent)
self.table_row_count = 55
self.outer_model = QStandardItemModel(
self.table_row_count, 10)
self.setModel(self.outer_model)
self.horizontalHeader().setVisible(False)
self.verticalHeader().setVisible(False)
self.setSelectionMode(
QAbstractItemView.SelectionMode.SingleSelection)
# self.add_total_row()
def add_total_row(self):
self.inner_model = QStandardItemModel(2, 10)
self.inner_table = QTableView(self)
self.inner_table.setObjectName('inner_table') # +
self.inner_table.setMaximumHeight(82) # +
self.inner_table.setMinimumHeight(82) # +
self.inner_table.setModel(self.inner_model)
self.inner_table.horizontalHeader().setVisible(False) # +
self.inner_table.verticalHeader().setVisible(False) # +
self.inner_table.setEditTriggers(
QAbstractItemView.EditTrigger.NoEditTriggers)
self.inner_table.setSelectionMode(
QAbstractItemView.SelectionMode.NoSelection)
def resizeEvent(self, event):
super().resizeEvent(event)
pass
'''
self.inner_table.setGeometry(
0, self.rowHeight(0)*self.table_row_count,
self.viewport().width(), self.rowHeight(0)*2+5)
'''
# +++ vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.central_widget = QWidget() # +++
self.setCentralWidget(self.central_widget)
self.table = CustomTableView()
self.table.setAlternatingRowColors(True)
self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.table.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
self.table.add_total_row()
self.table.inner_table.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
self.layout = QVBoxLayout(self.central_widget) # +++
self.layout.setSpacing(0)
self.layout.addWidget(self.table, stretch=9)
self.layout.addWidget(self.table.inner_table,
stretch=0, alignment=Qt.AlignBottom)
self.layout.addStretch(1)
for i in range(self.table.outer_model.rowCount()):
it = QStandardItem(f"{i}")
self.table.outer_model.setItem(i, 0, it)
Stylesheet = '''
/* тут вы можете установить свои стили для виджетов */
#inner_table {
border: 2px solid #f9009B;
background-color: #8EDE81;
selection-background-color: #999;
}
'''
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet(Stylesheet)
app.setStyleSheet
window = MainWindow()
window.resize(1100, 600)
window.show()
sys.exit(app.exec())
# +++ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Update:
Попробуйте так:
import sys
from PyQt5.QtWidgets import QTableView, QMainWindow, \
QApplication, QAbstractItemView, QVBoxLayout, QWidget, \
QHeaderView, QScrollArea, QHBoxLayout
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt, QSize
class ContentWidget(QWidget):
def sizeHint(self): # <--- sizeHint
return QSize(1500, 1000)
class CustomTableView(QTableView):
def __init__(self, parent= None):
super().__init__(parent)
self.table_row_count = 55
self.outer_model = QStandardItemModel(
self.table_row_count, 10)
self.setModel(self.outer_model)
self.horizontalHeader().setVisible(False)
self.verticalHeader().setVisible(False)
self.setSelectionMode(
QAbstractItemView.SelectionMode.SingleSelection)
def add_total_row(self):
self.inner_model = QStandardItemModel(2, 10)
self.inner_table = QTableView(self)
self.inner_table.setObjectName('inner_table')
self.inner_table.setMaximumHeight(82)
self.inner_table.setMinimumHeight(82)
self.inner_table.setModel(self.inner_model)
self.inner_table.horizontalHeader().setVisible(False)
self.inner_table.verticalHeader().setVisible(False)
self.inner_table.setEditTriggers(
QAbstractItemView.EditTrigger.NoEditTriggers)
self.inner_table.setSelectionMode(
QAbstractItemView.SelectionMode.NoSelection)
def resizeEvent(self, event):
super().resizeEvent(event)
pass
'''
self.inner_table.setGeometry(
0, self.rowHeight(0)*self.table_row_count,
self.viewport().width(), self.rowHeight(0)*2+5)
'''
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.scrollArea = QScrollArea() # +++
self.setCentralWidget(self.scrollArea)
self.content_widget = ContentWidget() # +++
self.scrollArea.setWidget(self.content_widget)
self.table = CustomTableView()
self.table.setAlternatingRowColors(True)
self.table.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.table.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
self.table.add_total_row()
self.table.inner_table.horizontalHeader().setSectionResizeMode(
QHeaderView.Stretch)
self.layout = QVBoxLayout(self.content_widget)
self.layout.setSpacing(0)
self.layout.addWidget(self.table, stretch=9)
self.layout.addWidget(self.table.inner_table,
stretch=0, alignment=Qt.AlignBottom)
self.layout.addStretch(1)
for i in range(self.table.outer_model.rowCount()):
it = QStandardItem(f"{i}")
self.table.outer_model.setItem(i, 0, it)
Stylesheet = '''
/* тут вы можете установить свои стили для виджетов */
#inner_table {
border: 2px solid #f9009B;
background-color: #8EDE81;
selection-background-color: #999;
}
'''
if __name__ == '__main__':
app = QApplication(sys.argv)
app.setStyleSheet(Stylesheet)
window = MainWindow()
window.resize(1100, 600)
window.show()
sys.exit(app.exec())
Мне кажется, что размещать два виджета рядом — это не самый лучший путь решения вашей задачи. Я бы на вашем месте использовал бы proxy-модель, которая будет отвечать за то, чтобы добавлять в изначальную модель дополнительную строку.
Вот пример реализации:
import sys
from PyQt5.QtWidgets import QTableView, QMainWindow, QApplication, QAbstractItemView
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtCore import Qt, QSize, QIdentityProxyModel, QModelIndex, QVariant
class TotalRowProxyModel(QIdentityProxyModel):
def __init__(self, parent = None):
super().__init__(parent)
def isTotalRow(self, row, column, parent):
return (not parent.isValid() and row == super().rowCount(parent) and column < self.columnCount())
def rowCount(self, index):
if not index.isValid():
return super().rowCount(index) + 1
return super().rowCount(index)
def index(self, row, column, parent):
if self.isTotalRow(row, column, parent):
return self.createIndex(row, column, self)
return super().index(row, column, parent)
def parent(self, child):
if child.internalPointer() is self:
return QModelIndex();
return super().parent(child)
def sibling(self, row, column, idx):
if self.isTotalRow(row, column, self.parent(idx)):
return self.createIndex(row, column, self)
return super().sibling(row, column, idx)
def mapToSource(self, proxyIndex):
if proxyIndex.internalPointer() is self:
return QModelIndex();
return super().mapToSource(proxyIndex)
def flags(self, index):
if index.internalPointer() is self:
return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren
return super().flags(index)
#Здесь вы подсовываете свои значения итого
def data(self, proxyIndex, role):
if proxyIndex.internalPointer() is self:
if role == Qt.DisplayRole:
if proxyIndex.column() == 2:
return QVariant("Итого: 2")
return QVariant("Пустое итого")
return QVariant()
return super().data(proxyIndex, role)
class CustomTableView(QTableView):
def __init__(self, parent= None):
super().__init__(parent)
self.table_row_count = 40
self.outer_model = QStandardItemModel(
self.table_row_count, 10)
self.proxy_model = TotalRowProxyModel()
self.proxy_model.setSourceModel(self.outer_model)
self.setModel(self.proxy_model)
self.horizontalHeader().setVisible(False)
self.verticalHeader().setVisible(False)
self.setSelectionMode(
QAbstractItemView.SelectionMode.SingleSelection)
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.table = CustomTableView()
self.setGeometry(0, 0, 1550, 1000)
self.setCentralWidget(self.table)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
Здесь не реализовано добавление данных, но я думаю вы сами сможете это сделать. Если вам не нужно выделение строки итого, вы можете убрать соответствующий флаг.