问题描述
我有一个QTableWidget,其中带有QLineEdits作为CellWidgets。 如果我清理桌子并用相同的功能重新填充它,则需要更长的时间才能完成。 在此示例中,时间差在0.1秒到3.9秒之间,但是在我的真实代码中,时间差在0.1秒到10分钟之间。
这是示例代码:
class Test_Layout(QWidget):
def __init__(self,parent=None):
super(Test_Layout,self).__init__(parent=None)
self.left = 0
self.top = 0
self.width = 0
self.height = 0
self.initUI()
self.isMaximized()
def initUI(self):
self.setGeometry(self.left,self.top,self.width,self.height)
self.createTable()
start_time = time.time()
self.fillTable()
print(time.time() - start_time)
self.combo = QComboBox(self)
self.combo.addItem("Reset")
for i in range(0,5):
self.combo.addItem(str(i))
self.combo.currentTextChanged.connect(self.on_combo_changed)
self.vBox = QVBoxLayout()
self.vBox.addWidget(self.combo)
self.vBox.addWidget(self.table)
self.setLayout(self.vBox)
def fill_row(self,row):
self.table.insertRow(row)
placeholder = QLineEdit()
self.table.setCellWidget(row,placeholder)
placeholder = QLineEdit()
self.table.setCellWidget(row,1,2,placeholder)
def on_combo_changed(self,currentText):
self.table.setRowCount(0)
if currentText == "Reset":
start_time = time.time()
self.fillTable()
print(time.time() - start_time)
else:
for row in range(0,int(currentText)):
self.fill_row(row)
def createTable(self):
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels([
"LineEdit0","LineEdit1","LineEdit2",])
header = self.table.horizontalHeader()
for i in range(0,3):
header.setSectionResizeMode(i,QHeaderView.ResizetoContents)
def fillTable(self):
for row in range(0,1000):
self.fill_row(row)
输出:
0.14005303382873535
在使用QComboBox并将其设置回“重置”之后:
3.9842889308929443
在有人问之前,我要用QLineEdits填充QTableWidget,因为我想使用占位符。
解决方法
不同之处不仅在于您在每个单元格中使用单元格小部件(而且,我说3000个小部件很多)是因为您正在调用{每次{1}}。
您还可以看到问题不是在“清除”之后发生的,而是在创建新单元格时发生的:只需删除setRowCount()
中对fillTable
的第一次调用,就会发生相同的延迟。 br />
每次更改模型布局时(通过添加/删除行或列),不仅对于模型,而且对于显示其内容的视图,都会发生很多事情,并且由于要单独添加行,因此需要更长的时间才能完成。即使您无法立即看到它,也可以使用视图来处理其内容(这是因为重新绘制已排队,并且仅在清除事件队列后才发生)。
要提高性能,在您的情况下,您应仅调用__init__
一次,并显示最终行数:
setRowCount()
最后,如果您确实要显示很多行,我强烈建议您找到一个替代方法,如文档中对setIndexWidget()
(由 def fill_row(self,row):
# comment or remove the following line
# self.table.insertRow(row)
placeholder = QLineEdit()
self.table.setCellWidget(row,placeholder)
placeholder = QLineEdit()
self.table.setCellWidget(row,1,2,placeholder)
def on_combo_changed(self,currentText):
self.table.setRowCount(0)
if currentText == "Reset":
start_time = time.time()
self.fillTable()
print(time.time() - start_time)
else:
count = int(currentText)
self.table.setRowCount(count)
for row in range(0,int(currentText)):
self.fill_row(row)
def fillTable(self):
self.table.setRowCount(1000)
for row in range(0,1000):
self.fill_row(row)
内部调用)的解释:>
此功能仅应用于在与数据项相对应的可见区域内显示静态内容。如果要显示自定义动态内容或实现自定义编辑器小部件,请改用QStyledItemDelegate子类。
这是因为大量的小部件会引起剧烈的性能问题(与您的情况完全一样)。
如果您需要一个占位符,则对每个单元格使用QLineEdit是一个坏的选择,这不仅是出于性能方面的考虑,而且还因为这样您就无法直接访问模型数据,但您始终需要在此之前找到单元小部件。
一个更优雅,更可取的解决方案是使用自定义委托,该委托将在没有单元格数据时显示占位符文本,并将setCellWidget()
添加到表编辑触发器:
CurrentChanged
一个简单的委托实现可能是这样的:
self.table.setEditTriggers(self.table.editTriggers() | self.table.CurrentChanged)
注意:您不应该使用宽度和高度为0的class PlaceholderDelegate(QStyledItemDelegate):
def __init__(self,placeholder='',parent=None):
super().__init__(parent)
self.placeholder = placeholder
def createEditor(self,parent,option,index):
editor = super().createEditor(parent,index)
if isinstance(editor,QLineEdit):
editor.setPlaceholderText(self.placeholder)
return editor
def paint(self,painter,index):
super().paint(painter,index)
if not index.data():
try:
# placeholderText palette role was introduced on Qt 5.12
color = option.palette.placeholderText().color()
except:
color = option.palette.text().color()
color.setAlpha(128)
painter.setPen(color)
style = option.widget.style()
margin = style.pixelMetric(style.PM_FocusFrameHMargin,option.widget)
rect = option.rect.adjusted(margin,-margin,0)
text = option.fontMetrics.elidedText(self.placeholder,option.textElideMode,rect.width())
painter.drawText(rect,option.displayAlignment,text)
class Test_Layout(QWidget):
# ...
def createTable(self):
# ...
for i in range(0,3):
header.setSectionResizeMode(i,QHeaderView.ResizeToContents)
self.table.setItemDelegateForColumn(
i,PlaceholderDelegate('empty {}'.format(i + 1),self.table))
,并且始终提供默认位置对于大屏幕或一个以上屏幕的用户可能会很烦人;同样,setGeometry()
和width
是所有QWidget子类的默认属性,并且绝不能用自定义属性覆盖。