如何删除 QTableWidget 和 QHeaderView 的外部网格线?
How can i remove the outside gridlines of QTableWidget and QHeaderView?
我想创建一个自定义 QTableView,我不想为其显示外部网格线,这意味着省略每个单元格的底线和右线。使用样式表,似乎我只能更改网格线颜色和 QHeaderView 的边框。我还想让网格线粗一点,这样只能看到里面的线。
当前样式表:
QTableWidget {
padding-left: 50px;
padding-right: 50px;
gridline-color: #9370DB;
}
QHeaderView::section:vertical {
border-top: 1px solid #9370DB
}
QHeaderView::section:horizontal {
border: 0px;
border-left: 1px solid #9370DB
}
当前输出如下所示:
我还可以看到 header 线和网格线未对齐,但这可以通过在各处使用相同的线宽来解决。
完整的测试代码:
from PySide6.QtCore import * # type: ignore
from PySide6.QtGui import * # type: ignore
from PySide6.QtWidgets import * # type: ignore
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.verticalLayout_2 = QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.indicatorsLayout = QHBoxLayout()
self.indicatorsLayout.setObjectName(u"indicatorsLayout")
self.verticalLayout_2.addLayout(self.indicatorsLayout)
self.listboxLayout = QGridLayout()
self.listboxLayout.setObjectName(u"listboxLayout")
self.listbox = QTableWidget(self.centralwidget)
if (self.listbox.columnCount() < 5):
self.listbox.setColumnCount(5)
if (self.listbox.rowCount() < 10):
self.listbox.setRowCount(10)
self.listbox.setObjectName(u"listbox")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listbox.sizePolicy().hasHeightForWidth())
self.listbox.setSizePolicy(sizePolicy)
self.listbox.setStyleSheet(u"QTableWidget {\n"
"padding-left: 50px;\n"
"padding-right: 50px;\n"
"gridline-color: #9370DB;\n"
"}\n"
"\n"
"\n"
"QHeaderView::section:vertical { \n"
"border-top: 1px solid #9370DB\n"
"}\n"
"\n"
"QHeaderView::section:horizontal { \n"
"border: 0px;\n"
"border-left: 1px solid #9370DB\n"
"}")
self.listbox.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.listbox.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
self.listbox.setRowCount(10)
self.listbox.setColumnCount(5)
self.listbox.horizontalHeader().setCascadingSectionResizes(False)
self.listbox.horizontalHeader().setDefaultSectionSize(100)
self.listboxLayout.addWidget(self.listbox, 0, 0, 1, 1)
self.verticalLayout_2.addLayout(self.listboxLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 22))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
# retranslateUi
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
网格线的宽度始终为 1 个像素,并且无法更改。错位是由于网格线始终绘制在每个索引的底部和右侧,而您将 header 的边框设置在每个部分的顶部或左侧。
这也意味着网格线始终显示在所有项目周围,包括最后一行和最后一列。
解决方案是完全禁用网格,并仅以与绘制 headers 相同的方式设置项目的边框;这样,您还可以设置“网格”的粗细。这种方法的缺点是对项目设置样式会覆盖委托的默认样式绘制,因此您还必须指定选择颜色。
QTableWidget {
qproperty-showGrid: "false";
padding-left: 50px;
padding-right: 50px;
}
QTableWidget::item {
border-top: 2px solid #9370DB;
border-left: 2px solid #9370DB;
}
QTableWidget::item:selected {
background: palette(highlight);
}
QHeaderView::section:vertical {
border: 0px;
border-top: 2px solid #9370DB
}
QHeaderView::section:horizontal {
border: 0px;
border-left: 2px solid #9370DB
}
一种略有不同的方法同时使用样式表 和 项目委托:绘制所有边框(使用所需大小的一半),不包括 headers,并且网格是使用委托绘制的,在这种情况下,为 ::item
选择器设置样式 not,确保它始终尊重提供的内部渲染按风格。
class GridDelegate(QtWidgets.QStyledItemDelegate):
pen = QtGui.QPen(QtGui.QColor('#9370DB'), 1)
def paint(self, qp, opt, index):
qp.save()
qp.setPen(self.pen)
qp.setBrush(QtCore.Qt.NoBrush)
lastRow = index.model().rowCount() - 1
lastCol = index.model().columnCount() - 1
if index.row() < lastRow and index.column() < lastCol:
qp.drawRect(opt.rect.adjusted(0, 0, -1, -1))
else:
qp.drawLine(opt.rect.bottomLeft(), opt.rect.topLeft())
qp.drawLine(opt.rect.topLeft(), opt.rect.topRight())
if index.row() < lastRow:
qp.drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight())
elif index.column() < lastCol:
qp.drawLine(opt.rect.topRight(), opt.rect.bottomRight())
qp.restore()
super().paint(qp, opt, index)
# ...
tableWidget.setItemDelegate(GridDelegate(tableWidget))
tableWidget.setStyleSheet('''
QTableWidget {
qproperty-showGrid: "false";
padding-left: 50px;
padding-right: 50px;
}
QTableCornerButton::section {
border: 0px;
border-bottom: 1px solid #A370DB;
border-right: 1px solid #A370DB;
}
QHeaderView::section:vertical {
border: 1px solid #9370DB;
border-left: none;
}
QHeaderView::section:vertical:last {
border-bottom: none;
}
QHeaderView::section:horizontal {
border: 1px solid #9370DB;
border-top: none;
}
QHeaderView::section:horizontal:last {
border-right: none;
}
''')
我想创建一个自定义 QTableView,我不想为其显示外部网格线,这意味着省略每个单元格的底线和右线。使用样式表,似乎我只能更改网格线颜色和 QHeaderView 的边框。我还想让网格线粗一点,这样只能看到里面的线。
当前样式表:
QTableWidget {
padding-left: 50px;
padding-right: 50px;
gridline-color: #9370DB;
}
QHeaderView::section:vertical {
border-top: 1px solid #9370DB
}
QHeaderView::section:horizontal {
border: 0px;
border-left: 1px solid #9370DB
}
当前输出如下所示:
我还可以看到 header 线和网格线未对齐,但这可以通过在各处使用相同的线宽来解决。
完整的测试代码:
from PySide6.QtCore import * # type: ignore
from PySide6.QtGui import * # type: ignore
from PySide6.QtWidgets import * # type: ignore
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
if not MainWindow.objectName():
MainWindow.setObjectName(u"MainWindow")
MainWindow.resize(800, 600)
self.centralwidget = QWidget(MainWindow)
self.centralwidget.setObjectName(u"centralwidget")
self.verticalLayout_2 = QVBoxLayout(self.centralwidget)
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.indicatorsLayout = QHBoxLayout()
self.indicatorsLayout.setObjectName(u"indicatorsLayout")
self.verticalLayout_2.addLayout(self.indicatorsLayout)
self.listboxLayout = QGridLayout()
self.listboxLayout.setObjectName(u"listboxLayout")
self.listbox = QTableWidget(self.centralwidget)
if (self.listbox.columnCount() < 5):
self.listbox.setColumnCount(5)
if (self.listbox.rowCount() < 10):
self.listbox.setRowCount(10)
self.listbox.setObjectName(u"listbox")
sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.listbox.sizePolicy().hasHeightForWidth())
self.listbox.setSizePolicy(sizePolicy)
self.listbox.setStyleSheet(u"QTableWidget {\n"
"padding-left: 50px;\n"
"padding-right: 50px;\n"
"gridline-color: #9370DB;\n"
"}\n"
"\n"
"\n"
"QHeaderView::section:vertical { \n"
"border-top: 1px solid #9370DB\n"
"}\n"
"\n"
"QHeaderView::section:horizontal { \n"
"border: 0px;\n"
"border-left: 1px solid #9370DB\n"
"}")
self.listbox.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
self.listbox.setSizeAdjustPolicy(QAbstractScrollArea.AdjustIgnored)
self.listbox.setRowCount(10)
self.listbox.setColumnCount(5)
self.listbox.horizontalHeader().setCascadingSectionResizes(False)
self.listbox.horizontalHeader().setDefaultSectionSize(100)
self.listboxLayout.addWidget(self.listbox, 0, 0, 1, 1)
self.verticalLayout_2.addLayout(self.listboxLayout)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QMenuBar(MainWindow)
self.menubar.setObjectName(u"menubar")
self.menubar.setGeometry(QRect(0, 0, 800, 22))
MainWindow.setMenuBar(self.menubar)
self.statusbar = QStatusBar(MainWindow)
self.statusbar.setObjectName(u"statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", None))
# retranslateUi
if __name__ == "__main__":
app = QApplication(sys.argv)
MainWindow = QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
MainWindow.show()
sys.exit(app.exec())
网格线的宽度始终为 1 个像素,并且无法更改。错位是由于网格线始终绘制在每个索引的底部和右侧,而您将 header 的边框设置在每个部分的顶部或左侧。
这也意味着网格线始终显示在所有项目周围,包括最后一行和最后一列。
解决方案是完全禁用网格,并仅以与绘制 headers 相同的方式设置项目的边框;这样,您还可以设置“网格”的粗细。这种方法的缺点是对项目设置样式会覆盖委托的默认样式绘制,因此您还必须指定选择颜色。
QTableWidget {
qproperty-showGrid: "false";
padding-left: 50px;
padding-right: 50px;
}
QTableWidget::item {
border-top: 2px solid #9370DB;
border-left: 2px solid #9370DB;
}
QTableWidget::item:selected {
background: palette(highlight);
}
QHeaderView::section:vertical {
border: 0px;
border-top: 2px solid #9370DB
}
QHeaderView::section:horizontal {
border: 0px;
border-left: 2px solid #9370DB
}
一种略有不同的方法同时使用样式表 和 项目委托:绘制所有边框(使用所需大小的一半),不包括 headers,并且网格是使用委托绘制的,在这种情况下,为 ::item
选择器设置样式 not,确保它始终尊重提供的内部渲染按风格。
class GridDelegate(QtWidgets.QStyledItemDelegate):
pen = QtGui.QPen(QtGui.QColor('#9370DB'), 1)
def paint(self, qp, opt, index):
qp.save()
qp.setPen(self.pen)
qp.setBrush(QtCore.Qt.NoBrush)
lastRow = index.model().rowCount() - 1
lastCol = index.model().columnCount() - 1
if index.row() < lastRow and index.column() < lastCol:
qp.drawRect(opt.rect.adjusted(0, 0, -1, -1))
else:
qp.drawLine(opt.rect.bottomLeft(), opt.rect.topLeft())
qp.drawLine(opt.rect.topLeft(), opt.rect.topRight())
if index.row() < lastRow:
qp.drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight())
elif index.column() < lastCol:
qp.drawLine(opt.rect.topRight(), opt.rect.bottomRight())
qp.restore()
super().paint(qp, opt, index)
# ...
tableWidget.setItemDelegate(GridDelegate(tableWidget))
tableWidget.setStyleSheet('''
QTableWidget {
qproperty-showGrid: "false";
padding-left: 50px;
padding-right: 50px;
}
QTableCornerButton::section {
border: 0px;
border-bottom: 1px solid #A370DB;
border-right: 1px solid #A370DB;
}
QHeaderView::section:vertical {
border: 1px solid #9370DB;
border-left: none;
}
QHeaderView::section:vertical:last {
border-bottom: none;
}
QHeaderView::section:horizontal {
border: 1px solid #9370DB;
border-top: none;
}
QHeaderView::section:horizontal:last {
border-right: none;
}
''')