问题描述
我试图弄清楚如何使用具有排序/过滤可能性的小部件对表格进行编码。 我使用 QItemDelegate、QAbstractTableModel、QTableView。
已经检查了关于这个主题的许多线程,这让我对我的应用程序的一部分进行了编码。
我想了解的是如何正确删除行以保持所有这些索引和数据的一致性。一开始我虽然我做对了,但是多次排序和删除显示了带有按钮删除小部件的列的非常奇怪的行为。
例如,如果我一直单击同一表格行(例如 5)中的按钮,这些删除按钮只会向上移动,而前 3 列已正确更新。还有一些删除按钮被复制。
完整代码:
import sys
from pyside2.QtWidgets import QApplication
app = QApplication( )
from pyside2.QtWidgets import QApplication,QLabel,QMainWindow,QWidget,QHBoxLayout,QVBoxLayout,qgridLayout,QStackedLayout,QTableView
from pyside2.QtCore import Qt
# QHBoxLayout Linear horizontal layout
# QVBoxLayout Linear vertical layout
# qgridLayout In indexable grid XxY
# QStackedLayout
def get_layout(ll):
if ll not in ['grid','hBox','vBox','stack']:
# print(ll)
# print('wrong layout name')
exit()
ret_layout = qgridLayout()
if ll=='hBox':
ret_layout = QHBoxLayout()
elif ll=='vBox':
ret_layout = QVBoxLayout()
elif ll=='stack':
ret_layout = QStackedLayout()
return ret_layout
class MainWindow(QMainWindow):
def __init__(self,win_title='Default title',win_layout='grid',main_widget = QWidget()):
super(MainWindow,self).__init__()
self.setwindowTitle(win_title)
self.layout_name=win_layout
self.main_layout=get_layout(win_layout)
self.setGeometry(100,100,1000,800)
main_widget.setLayout(self.main_layout)
self.setCentralWidget(main_widget)
def addFrame(self,frame):
if self.layout_name!='grid':
if type(frame)==type([]):
self.main_layout.addWidget(frame[-1])
else:
self.main_layout.addWidget(frame)
else:
self.main_layout.addWidget(frame[2],frame[0],frame[1])
import sys
import json
import pyside2
from pyside2 import QtCore,QtGui,QtWidgets
from pyside2.QtCore import Qt #,QFile,qiodevice
class ExampleWidget(QtWidgets.QWidget):
def __init__(self,x,index,parent=None):
super(ExampleWidget,self).__init__(parent)
self.orig_index=index
self.p_index = QtCore.QPersistentModelIndex(index)
self.content_button = QtWidgets.QWidget(self)
lay = QtWidgets.QHBoxLayout(self.content_button)
lay.setContentsMargins(0,0)
self.delete_btn = QtWidgets.QPushButton("delete "+str(index.row()))
self.delete_btn.clicked.connect(self.delete_clicked)
lay.addWidget(self.delete_btn)
self.content_button.move(x,0)
@QtCore.Slot()
def delete_clicked(self):
model = self.p_index.model() # QSortFilterProxyModel
src_model=model.sourceModel()
src_index=model.mapToSource(self.orig_index)
rows_to_del=1
zxc=src_model.removeRows(src_index.row(),rows_to_del,src_index,self.p_index )
# self.deleteLater()
# self.QtWidgets.~QWidget()
class CustomDelegate(QtWidgets.QItemDelegate): #qstyledItemDelegate
def __init__(self,parent=None):
super(CustomDelegate,self).__init__(parent)
def paint(self,painter,option,index):
self.parent().openPersistentEditor(index)
super(CustomDelegate,self).paint(painter,index)
def createEditor(self,parent,index):
if index.column()==3:
return ExampleWidget(300,parent)
class TableModel(QtCore.QAbstractTableModel ):
def __init__(self,table=[],col_headers=[],row_headers=[],bg_color=None ):
super(TableModel,self).__init__()
self.table = table
self.col_headers=col_headers
self.row_headers=row_headers
self.bg_color=bg_color or QtGui.QColor('lightgrey')
def itemFromIndex(self,index):
if not index.isValid():
return None
rr=index.row()
cc=index.column()
return str ( self.table[rr][cc] )
def removeRows(self,row,count,p_index):
self.layoutAboutToBeChanged.emit()
self.beginRemoveRows(index,row)
del self.table[row]
self.endRemoveRows()
self.layoutChanged.emit()
return True
def data(self,role ):
if not index.isValid():
return None
rr=index.row()
cc=index.column()
if role == Qt.displayRole:
vvalue = str ( self.table[rr][cc] )
return vvalue #QtWidgets.QPushButton
if role == Qt.BackgroundRole:
return self.bg_color
def rowCount(self,index):
return len(self.table)
# return 5
def columnCount(self,index):
if len(self.table)>0:
return len(self.table[0])
return 0
def headerData(self,section,orientation,role):
if section<0:
return
if role == Qt.displayRole:
if orientation == Qt.Horizontal:
if len(self.col_headers)>section:
return str(self.col_headers[section])
if orientation == Qt.Vertical:
if len(self.row_headers)>section:
return str(self.row_headers[section])
class TableView( QtWidgets.QTableView):
def __init__(self,params={}):
super(TableView,self).__init__( )
self.model = TableModel()
self.sorting_proxy = QtCore.QSortFilterProxyModel()
self.sorting_proxy.setSourceModel( self.model)
self.setModel(self.sorting_proxy)
self.delegate=CustomDelegate(self)
self.setItemDelegate(self.delegate)
self.setSortingEnabled(True)
self.setCornerButtonEnabled(False)
self.horizontalHeader().setStretchLastSection(True)
self.clicked.connect(self.onClick)
self.verticalHeader().hide()
tmp_header_bg_color='lightgrey'
if 'header_bg_color' in params:
tmp_header_bg_color=params['header_bg_color']
tmp_style="""QWidget { background-color:%s; border:none; }
QHeaderView::section { background-color:%s; border:none; }
QTableCornerButton::section { background-color:%s; border:none; }
""" % (tmp_header_bg_color,tmp_header_bg_color,tmp_header_bg_color)
self.setStyleSheet(tmp_style)
if len(params)>0:
if 'show_grid' in params:
self.setShowGrid(params['show_grid'])
if 'auto_resize':
self.auto_resize=True
@QtCore.Slot(QtCore.QModelIndex)
def onClick(self,ix):
# it = self.model.itemFromIndex(ix)
it=self.sorting_proxy.data(ix)
if hasattr(it,'data'):
print(it.data())
else:
print(it)
def sortByCol(self,cc,dir='asc'):
if dir=='asc':
self.sortByColumn(cc,Qt.AscendingOrder)
else:
self.sortByColumn(cc,Qt.DescendingOrder)
def setdata(self,data_list,row_headers=[]):
self.model.table=data_list
self.model.col_headers=col_headers
self.model.row_headers=row_headers
self.model.layoutChanged.emit()
if hasattr(self,'auto_resize'):
self.resizeColumnsToContents()
self.resizeRowsToContents()
data_list = [
('ACETIC ACID',117.9,16.7,1.049),('ACETIC ANHYDRIDE',140.1,-73.1,1.087),('ACetoNE',56.3,-94.7,0.791),('ACetoNITRILE',81.6,-43.8,0.786),('ANISOLE',154.2,-37.0,0.995),('BENZYL ALCOHOL',205.4,-15.3,1.045),('BENZYL BENZOATE',323.5,19.4,1.112),('BUTYL ALCOHOL norMAL',117.7,-88.6,0.81),('BUTYL ALCOHOL SEC',99.6,-114.7,0.805),('BUTYL ALCOHOL TERTIARY',82.2,25.5,('CHLOROBENZENE',131.7,-45.6,1.111),('CYCLOHEXANE',80.7,6.6,0.779),('CYCLOHEXANOL',161.1,25.1,0.971),('CYCLOHEXANONE',155.2,-47.0,0.947),('DICHLOROETHANE 1 2',83.5,-35.7,1.246),('DICHLOROMETHANE',39.8,-95.1,1.325),('DIETHYL ETHER',34.5,-116.2,0.715),('DIMETHYLACETAMIDE',166.1,-20.0,0.937),('DIMETHYLFORMAMIDE',153.3,-60.4,0.944),('DIMETHYLSULFOXIDE',189.4,18.5,1.102),('dioXANE 1 4',101.3,11.8,1.034),('DIPHENYL ETHER',258.3,26.9,1.066),('ETHYL ACETATE',77.1,-83.9,0.902),('ETHYL ALCOHOL',78.3,-114.1,0.789),('ETHYL DIGLYME',188.2,-45.0,0.906),('ETHYLENE CARBONATE',248.3,36.4,1.321),('ETHYLENE GLYCOL',197.3,-13.2,1.114),('FORMIC ACID',100.6,8.3,1.22),('HEPTANE',98.4,-90.6,0.684),('HEXAMETHYL PHOSPHORAMIDE',233.2,7.2,1.027),('HEXANE',68.7,-95.3,0.659),('ISO OCTANE',99.2,-107.4,0.692),('ISOPROPYL ACETATE',88.6,-73.4,0.872),('ISOPROPYL ALCOHOL',82.3,-88.0,0.785),('METHYL ALCOHOL',64.7,-97.7,('METHYL ETHYLKetoNE',79.6,-86.7,('METHYL ISOBUTYL KetoNE',116.5,-84.0,0.798),('METHYL T-BUTYL ETHER',55.5,-10.0,0.74),('METHYLPYRROLIDINONE N',203.2,-23.5,('MORPHOLINE',128.9,-3.1,1.0),('NITROBENZENE',210.8,5.7,1.208),('NITROMETHANE',101.2,-28.5,1.131),('PENTANE',36.1,' -129.7',0.626),('PHENOL',181.8,40.9,('PROPANENITRILE',97.1,-92.8,0.782),('PROPIONIC ACID',141.1,-20.7,0.993),('PROPIONITRILE',97.4,('PROPYLENE GLYCOL',187.6,-60.1,1.04),('PYRIDINE',115.4,-41.6,0.978),('SULFOLANE',287.3,28.5,1.262),('TETRAHYDROFURAN',66.2,-108.5,0.887),('TOLUENE',110.6,-94.9,0.867),('TRIETHYL PHOSPHATE',215.4,-56.4,1.072),('TRIETHYLAmine',89.5,0.726),('TRIFLUOROACETIC ACID',71.8,1.489),('WATER',100.0,0.0,('XYLEnes',139.1,-47.8,0.86)
]
tmpheaders=['a','b','c']
mw = MainWindow(win_layout='hBox' )
ff=TableView(params={'show_grid':False,'auto_resize':1 })
mw.addFrame(ff)
ff.setdata(data_list,tmpheaders)
mw.show()
app.exec_()
解决方法
您必须创建一个删除行的方法:
class TableModel(QAbstractTableModel):
# ...
def removeRow(self,row):
self.beginRemoveRows(QModelIndex(),row,row)
del self.table[row]
self.endRemoveRows()
然后映射 sourceModel 中的行:
class ExampleWidget(QWidget):
# ...
@Slot()
def delete_clicked(self):
index = QModelIndex(self.p_index)
model = index.model()
source_index = model.mapToSource(index)
source_model = source_index.model()
source_model.removeRow(source_index.row())