I am trying to make a QListView that shows a list of items and whether they're enabled or not. The code below does this, but I also want to be able to toggle the item's enabled state when the user clicks on the checkbox (not just anywhere on the list item, but specifically on the checkbox), how can I do that?
import sys
from dataclasses import dataclass
from typing import Dict, List, Union
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
#dataclass
class Filter:
expr: str
enabled: bool
class Store:
def __init__(self):
super().__init__()
self.filter_viewer: Union["FilterViewer", None] = None
self.filters: List[Filter] = []
def update(self):
if self.filter_viewer is not None:
self.filter_viewer.list_model.dataChanged.emit(QtCore.QModelIndex(), QtCore.QModelIndex())
def add_filter(self, expr: str, enabled=True):
filt = Filter(expr=expr, enabled=enabled)
self.filters.append(filt)
self.update()
def remove_filter(self, index: int):
self.filters.pop(index)
self.update()
def toggle_filter(self, index: int):
self.filters[index].enabled = not self.filters[index].enabled
self.update()
class FilterViewer(QtWidgets.QWidget):
def __init__(self, pgdf: Store):
super().__init__()
pgdf.filter_viewer = self
self.pgdf = pgdf
self.list_view = self.ListView()
self.list_model = self.ListModel(pgdf)
self.list_view.setModel(self.list_model)
self.text_input = QtWidgets.QLineEdit()
self.submit_button = QtWidgets.QPushButton("Add Filter")
self.submit_button.clicked.connect(self.add_filter)
self.text_input.returnPressed.connect(self.add_filter)
self.layout = QtWidgets.QVBoxLayout()
self.layout.addWidget(self.list_view)
self.layout.addWidget(self.text_input)
self.layout.addWidget(self.submit_button)
self.setLayout(self.layout)
def add_filter(self):
expr = self.text_input.text()
self.text_input.setText("")
self.pgdf.add_filter(expr=expr)
print(self.pgdf.filters)
class ListView(QtWidgets.QListView):
pass
class ListModel(QtCore.QAbstractListModel):
def __init__(self, pgdf: Store):
super().__init__()
self.pgdf = pgdf
def data(self, index: QtCore.QModelIndex, role: int):
row = index.row()
if role == Qt.DisplayRole:
filt = self.pgdf.filters[row]
return filt.expr
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
if filt.enabled:
return Qt.Checked
else:
return Qt.Unchecked
def rowCount(self, parent):
return len(self.pgdf.filters)
def flags(self, index):
return (QtCore.Qt.ItemIsEnabled |
QtCore.Qt.ItemIsUserCheckable)
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
pgdf = Store()
pgdf.add_filter('Generation > 3', enabled=True)
pgdf.add_filter('Size > 50', enabled=False)
fv = FilterViewer(pgdf)
fv.show()
app.exec_()
The state of the QListView checkboxes is determined by the model, so to change the model information you must override the setData() method:
class ListModel(QtCore.QAbstractListModel):
def __init__(self, pgdf: Store):
super().__init__()
self.pgdf = pgdf
def data(self, index: QtCore.QModelIndex, role: int):
row = index.row()
if role == Qt.DisplayRole:
filt = self.pgdf.filters[row]
return filt.expr
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
if filt.enabled:
return Qt.Checked
else:
return Qt.Unchecked
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
row = index.row()
if role == Qt.CheckStateRole:
filt = self.pgdf.filters[row]
filt.enabled = bool(value)
self.dataChanged.emit(index, index, (role,))
return True
return False
def rowCount(self, parent):
return len(self.pgdf.filters)
def flags(self, index):
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsUserCheckable
Related
I'm using a subclassed QAbstractTableModel with dataclasses as items. Each dataclass contains a field "field1" with a list, which I'd like to display in a listview and have it automatically change whenever I edit or add an item in the listview.
To do that I set a custom delegate to the QDataWidgetMapper which will retrieve and set the values from that dataclass. This works the way I want it to.
My problem is that I want to add additional items to that listview with the press of a button and have the QDataWidgetMapper add them automatically to the model.
This is what I have so far:
import sys
import dataclasses
from typing import List, Any
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
#dataclasses.dataclass()
class StorageItem:
field1: List[str] = dataclasses.field(default_factory=list)
class StorageModel(QAbstractTableModel):
def __init__(self, parent=None):
super().__init__(parent)
test = StorageItem()
test.field1 = ['Item °1', 'Item °2']
self._data: List[StorageItem] = [test]
def data(self, index: QModelIndex, role: int = ...) -> Any:
if not index.isValid():
return
item = self._data[index.row()]
col = index.column()
if role in {Qt.DisplayRole, Qt.EditRole}:
if col == 0:
return item.field1
else:
return None
def setData(self, index: QModelIndex, value, role: int = ...) -> bool:
if not index.isValid() or role != Qt.EditRole:
return False
item = self._data[index.row()]
col = index.column()
if col == 0:
item.field1 = value
self.dataChanged.emit(index, index)
print(self._data)
return True
def flags(self, index: QModelIndex) -> Qt.ItemFlags:
return Qt.ItemFlags(
Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
)
def rowCount(self, parent=None) -> int:
return len(self._data)
def columnCount(self, parent=None) -> int:
return len(dataclasses.fields(StorageItem))
class TestDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
def setEditorData(self, editor: QWidget, index: QModelIndex) -> None:
if isinstance(editor, QListView):
data = index.model().data(index, Qt.DisplayRole)
editor.model().setStringList(data)
else:
super().setEditorData(editor, index)
def setModelData(
self, editor: QWidget,
model: QAbstractItemModel,
index: QModelIndex
) -> None:
if isinstance(editor, QListView):
data = editor.model().stringList()
model.setData(index, data, Qt.EditRole)
else:
super().setModelData(editor, model, index)
class CustomListView(QListView):
item_added = pyqtSignal(name='itemAdded')
def __init__(self, parent=None):
super().__init__(parent)
self.setModel(QStringListModel())
def add_item(self, item: str):
str_list = self.model().stringList()
str_list.append(item)
self.model().setStringList(str_list)
self.item_added.emit()
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
cent_widget = QWidget()
self.setCentralWidget(cent_widget)
# Vertical Layout
v_layout = QVBoxLayout()
v_layout.setContentsMargins(10, 10, 10, 10)
# Listview
self.listview = CustomListView()
v_layout.addWidget(self.listview)
# Button
self.btn = QPushButton('Add')
self.btn.clicked.connect(lambda: self.listview.add_item('New Item'))
v_layout.addWidget(self.btn)
cent_widget.setLayout(v_layout)
# Set Mapping
self.mapper = QDataWidgetMapper()
self.mapper.setItemDelegate(TestDelegate())
self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
self.mapper.setModel(StorageModel())
self.mapper.addMapping(self.listview, 0)
self.mapper.toFirst()
self.listview.itemAdded.connect(self.mapper.submit)
def main():
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
if __name__ == '__main__':
main()
Currently, I'm using the signal itemAdded from inside the custom ListView to manually submit the QDataWidgetMapper.
Is there a way to do this within CustomListView, without using a custom signal?
Somehow the delegate knows when data in the listview has been edited. How can I trigger that same mechanism when new items are added?
TL; DR; It can not.
The submitPolicy QDataWidgetMapper::AutoSubmit indicates that the model will be updated when focus is lost. The model is also updated when the commitData or closeEditor signal of the delegate is invoked, which happens by default when some specific keys are pressed.
A better implementation would be to create a signal that is emitted every time a change is made in the QListView model and connect it to submit, not just the method of adding elements. Also it is better to use a custom qproperty.
class CustomListView(QListView):
items_changed = pyqtSignal(name="itemsChanged")
def __init__(self, parent=None):
super().__init__(parent)
self.setModel(QStringListModel())
self.model().rowsInserted.connect(self.items_changed)
self.model().rowsRemoved.connect(self.items_changed)
self.model().dataChanged.connect(self.items_changed)
self.model().layoutChanged.connect(self.items_changed)
def add_item(self, item: str):
self.items += [item]
#pyqtProperty(list, notify=items_changed)
def items(self):
return self.model().stringList()
#items.setter
def items(self, data):
if len(data) == len(self.items) and all(
x == y for x, y in zip(data, self.items)
):
return
self.model().setStringList(data)
self.items_changed.emit()
# Set Mapping
self.mapper = QDataWidgetMapper()
self.mapper.setModel(StorageModel())
self.mapper.addMapping(self.listview, 0, b"items")
self.mapper.toFirst()
self.listview.items_changed.connect(self.mapper.submit)
I am to learn model/view and write a demo, but my table has no data and couldn't set data.
And checkIndex method of QAbstractItemMode couldn't work ???
The code:
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtPrintSupport import *
from PyQt5.QtChart import *
import numpy as np
class TableModel(QAbstractTableModel):
def __init__(self, data: np.ndarray):
super().__init__()
self.dataArray = data
def rowCount(self, parent):
return self.dataArray.shape[0]
def columnCount(self, parent):
return self.dataArray.shape[1]
def data(self, index: QModelIndex, role=None):
# checkIndex method not working ???
# self.checkIndex(index, QAbstractItemModel::IndexIsValid)
if not index.isValid():
return None
if index.row() >= self.dataArray.shape[0] or index.column() >= self.dataArray.shape[1]:
return None
if role in [Qt.DisplayRole, Qt.EditRole]:
return self.dataArray[index.row()][index.column()]
else:
return None
def headerData(self, section, orientation, role=None):
if role != Qt.DisplayRole:
return None
if orientation == Qt.Horizontal:
return f'Column {section}'
else:
return f'Row {section}'
def flags(self, index: QModelIndex):
if not index.isValid():
return Qt.ItemIsEnabled
return super().flags(index) | Qt.ItemIsEditable
def setData(self, index: QModelIndex, value, role=None):
if index.isValid() and role == Qt.EditRole:
self.dataArray[index.row()][index.column()] = value
self.dataChanged.emit(index, index, [role])
return True
return False
class DemoA(QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
data = np.random.randint(1, 100, (4, 6))
model = TableModel(data)
# delegate = ComboBoxDelegate()
table = QTableView()
table.setModel(model)
# table.setItemDelegate(delegate)
self.setCentralWidget(table)
app = QApplication([])
demo = DemoA()
demo.show()
app.exec()
The result:
PyQt5 does not handle numpy data types so it will not display them. In your case, the data stored in the numpy array is numpy.int64, so the solution is to convert it to an integer or float:
if role in [Qt.DisplayRole, Qt.EditRole]:
return int(self.dataArray[index.row()][index.column()])
I'm starting be more an more confused. I cannot make a QTableView emit its signal like I would like. I reduced my case to something less messy, and even in that case I cannot get any signals to be fired when I click.
For example in that code the slot "onClickedRow" is called once when starting the app (I don't know why), but then I can click as much as I want anywhere and the slot is never called :
import sys
from PySide2 import QtWidgets, QtCore, QtGui
class Message(QtCore.QAbstractItemModel):
def __init__(self):
super().__init__()
self.messageList = []
def addMessage(self, typeName, data):
self.messageList.append({"type": typeName,
"data": data})
def data(self, index, role):
if not index.isValid():
return None
if role != QtCore.Qt.DisplayRole:
return None
item = self.messageList[index.row()]
if index.column() == 0:
return str(item["type"])
else:
return str(item["data"])
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal:
if role == QtCore.Qt.DisplayRole:
if section == 0:
return "type"
else:
return "data"
return None
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
return QtCore.QModelIndex()
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
else:
return self.createIndex(row, column)
def flags(self, index):
if not index.isValid():
return QtCore.Qt.NoItemFlags
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def columnCount(self, parent):
return 2
def rowCount(self, parent):
return len(self.messageList)
class FormMessageJournal(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.layout = QtWidgets.QVBoxLayout()
self.messageTable = QtWidgets.QTableView(self)
self.messageTable.clicked.connect(self.onClickedRow())
self.messageList = Message()
self.messageList.addMessage("Send", "Hello")
self.messageList.addMessage("Send", "Hello")
self.messageList.addMessage("Send", "Hello")
self.messageList.addMessage("Send", "Hello")
self.messageTable.setModel(self.messageList)
self.layout.addWidget(self.messageTable)
self.setLayout(self.layout)
def onClickedRow(self, index=None):
print("Click !")
if __name__ == "__main__":
app = QtWidgets.QApplication([])
widget = FormMessageJournal()
widget.show()
sys.exit(app.exec_())
Am I the only one having that type of issues?
self.messageTable.clicked.connect(self.onClickedRow())
Change to:
self.messageTable.clicked.connect(self.onClickedRow)
I attempt to filter self.items's objects by their self.category attribute all from inside of QAbstractTableModel's data() method by comparing this attribute agains a text currently displayed in the QComboBox. Yet, the code doesn't function properly.
Shouldn't be QAbstractTableModel's data() method used "as a substitute to proxy model's accepts row() method?
Is it be possible to achieve the filtering without using QSortFilterProxyModel? If we have to use proxy to filter the model items what would be most Pythonic way of doing this?
from PySide import QtGui, QtCore
class Item(object):
def __init__(self):
self.ID=None
self.name=None
self.category=None
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.items = []
self.filterCategory = None
def rowCount(self, parent=QtCore.QModelIndex()):
return len( [item for item in self.items if item.category==self.filterCategory] )
def columnCount(self, parent=QtCore.QModelIndex()):
return 1
def data(self, index, role):
if not index.isValid(): return
row=index.row()
item=self.items[row]
if item.category!=self.filterCategory:
return
if role == QtCore.Qt.DisplayRole:
return self.items[row].name
if role == QtCore.Qt.UserRole:
return self.items[row]
def insertRows(self, row, item, column=1, index=QtCore.QModelIndex()):
self.beginInsertRows(QtCore.QModelIndex(), row, row+1)
self.items.append(item)
self.endInsertRows()
def setFilter(self, comboText):
self.filterCategory = comboText
self.layoutChanged.emit()
def filterAcceptsRow(self, row, proc):
index=self.sourceModel().index(row, 0, proc)
item=self.sourceModel().data(index, QtCore.Qt.UserRole)
if not item: return True
resourceType=item.category
if self.filters.get(category)==False:
return False
if self.searchText and len(self.searchText)>0 and item.searchString(self.searchText)==False:
return False
return True
class MyWindow(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
vLayout=QtGui.QVBoxLayout(self)
self.setLayout(vLayout)
self.tableModel = TableModel()
self.ViewA=QtGui.QTableView(self)
self.ViewA.clicked.connect(self.viewClicked)
vLayout.addWidget(self.ViewA)
for row in range(5):
item=Item()
item.ID=row
if item.ID%2: item.category='Pet'
else: item.category='Birds'
item.name='%s_%s'%(item.category, row)
self.tableModel.insertRows(row, item)
self.ViewA.setModel(self.tableModel)
self.combo=QtGui.QComboBox()
self.combo.addItems(['Pet','Birds'])
self.combo.activated.connect(self.comboActivated)
vLayout.addWidget(self.combo)
currentComboCategory=self.combo.currentText()
self.tableModel.setFilter(currentComboCategory)
def viewClicked(self, indexClicked):
print('indexClicked() row: %s column: %s'%(indexClicked.row(), indexClicked.column() ))
def comboActivated(self, arg=None):
comboText=self.combo.currentText()
self.tableModel.setFilter(comboText)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
"no-more-proxy" code below shows how to sort and filter from inside of QAbstractTableModel instead of proxy:
import sys, os
from PyQt import QtGui, QtCore
class Item(object):
def __init__(self,ID=None,name=None,category=None,area=None):
self.ID=ID
self.name=name
self.category=category
self.area='South'
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.currentItems=[]
self.items = []
self.filterCategory = None
self.searchField = None
self.mainColumn=0
self.order=QtCore.Qt.SortOrder.DescendingOrder
def rowCount(self, parent=QtCore.QModelIndex()):
return len( self.currentItems )
def columnCount(self, parent=QtCore.QModelIndex()):
return 5
def data(self, index, role):
if not index.isValid(): return
row=index.row()
column=index.column()
item=self.currentItems[row]
if role == QtCore.Qt.DisplayRole:
if column==0: return item.ID
elif column==1: return item.name
elif column==2: return item.category
elif column==4 or column==5: return item.area
if role == QtCore.Qt.UserRole:
return item
def insertRows(self, row, item, column=1, index=QtCore.QModelIndex()):
self.beginInsertRows(QtCore.QModelIndex(), row, row+1)
self.items.append(item)
self.endInsertRows()
def setFilter(self, comboText=None, searchText=None, mainColumn=None, order=None):
if comboText: self.filterCategory=comboText
if searchText: self.searchText=searchText
if mainColumn!=None: self.mainColumn=mainColumn
self.order=order
self.currentItems=[item for item in self.items if item.category==self.filterCategory]
if searchText:
self.currentItems=[item for item in self.currentItems if searchText in '%s%s%s'%(item.ID, item.name, item.category)]
values=[]
if self.mainColumn==0: values=[[item.ID, item, False] for item in self.currentItems]
elif self.mainColumn==1: values=[[item.name, item, False] for item in self.currentItems]
elif self.mainColumn==2: values=[[item.category, item, False] for item in self.currentItems]
elif self.mainColumn==3 or self.mainColumn==4: values=[[item.area, item, False] for item in self.currentItems]
keys=sorted([value[0] for value in values if isinstance(value, list)])
if self.order==QtCore.Qt.AscendingOrder: keys=list(reversed(keys))
filtered=[]
for key in keys:
for each in values:
if each[0]!=key: continue
if each[2]==True: continue
item=each[1]
filtered.append(item)
each[2]=True
if filtered: self.currentItems=filtered
self.layoutChanged.emit()
class ItemDelegate(QtGui.QItemDelegate):
def __init__(self, parent):
QtGui.QItemDelegate.__init__(self, parent)
def flags(self, index):
if (index.column() == 1):
return QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled
else:
return QtCore.Qt.ItemIsEnabled
def createEditor(self, parent, option, index):
tableView=parent.parent()
model=tableView.model()
item=model.data(index, QtCore.Qt.UserRole)
combo=QtGui.QComboBox(parent)
combo.addItems(['South','West','North','East'])
combo.currentIndexChanged.connect(self.comboIndexChanged)
if item.area:
comboIndex=combo.findText(item.area)
if comboIndex>=0:
combo.setCurrentIndex(comboIndex)
else: combo.setCurrentIndex(0)
return combo
def comboIndexChanged(self):
self.commitData.emit(self.sender())
def setModelData(self, combo, model, index):
item=model.data(index, QtCore.Qt.UserRole)
comboText=combo.currentText()
item.area=comboText
class MyWindow(QtGui.QWidget):
def __init__(self, *args):
QtGui.QWidget.__init__(self, *args)
vLayout=QtGui.QVBoxLayout(self)
self.setLayout(vLayout)
self.tableModel = TableModel()
self.searchLine=QtGui.QLineEdit()
vLayout.addWidget(self.searchLine)
self.searchLine.textEdited.connect(self.searchLineEditied)
self.searchLine.returnPressed.connect(self.searchLineEditied)
self.tableView=QtGui.QTableView(self)
self.tableView.setSortingEnabled(True)
self.tableView.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
self.tableView.setShowGrid(False)
self.tableView.setSelectionBehavior(QtGui.QTableView.SelectRows)
self.tableView.setAlternatingRowColors(True)
self.delegate=ItemDelegate(self.tableView)
self.tableView.setItemDelegate(self.delegate)
self.tableView.clicked.connect(self.viewClicked)
vLayout.addWidget(self.tableView)
for row in range(15):
if row%2: category='Pet'
else: category='Birds'
item=Item(category=category, ID=row, name='%s_%s'%(category,row))
self.tableModel.insertRows(row, item)
self.tableView.setModel(self.tableModel)
self.combo=QtGui.QComboBox()
self.combo.addItems(['Pet','Birds'])
self.combo.activated.connect(self.comboActivated)
vLayout.addWidget(self.combo)
currentComboCategory=self.combo.currentText()
self.tableModel.setFilter(currentComboCategory)
self.horizontalHeader=self.tableView.horizontalHeader()
self.horizontalHeader.sortIndicatorChanged.connect(self.headerTriggered)
self.addComboDelegates()
def headerTriggered(self, mainColumn=None, order=None):
self.tableModel.setFilter(mainColumn=mainColumn, order=order)
self.deleteComboDelegates()
self.addComboDelegates()
def comboActivated(self, comboIndex=None):
self.deleteComboDelegates()
comboText=self.combo.currentText()
self.tableModel.setFilter(comboText=comboText)
self.addComboDelegates()
self.tableModel.layoutChanged.emit()
def searchLineEditied(self, searchText=None):
self.tableModel.setFilter(searchText=searchText)
def viewClicked(self, indexClicked):
item=self.tableModel.data(indexClicked, QtCore.Qt.UserRole)
print 'ID: %s, name: %s, category: %s'%(item.ID,item.name,item.category)
def deleteComboDelegates(self):
for row in range(self.tableModel.rowCount()):
index=self.tableModel.index(row, 3, QtCore.QModelIndex())
self.tableView.closePersistentEditor(index)
def addComboDelegates(self):
for row in range(self.tableModel.rowCount()):
index=self.tableModel.index(row, 3, QtCore.QModelIndex())
self.tableView.openPersistentEditor(index)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
w = MyWindow()
w.show()
sys.exit(app.exec_())
I'm using QDateTimeEdit as a delegate on my QTableview to show start date and end date.
when I try to populate data I receive from database, QDateTimeEdit delegate does not display it.
Here is my code:
Class DateDelegate:
class DateDelegate(QtGui.QItemDelegate):
def __init__(self, parent):
QtGui.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
self.dateEdit = QtGui.QDateTimeEdit(parent)
self.dateEdit.setCalendarPopup(True)
self.dateEdit.setMinimumDate(QtCore.QDate(2014, 03, 01))
self.dateEdit.setDisplayFormat(_translate("Form", "dd/mm/yyyy", None))
return self.dateEdit
def setModelData(self, editor, model, index):
value = self.dateEdit.dateTime().toPyDateTime()
strDate = value.strftime('%d/%m/%Y')
model.setData(index, strDate, QtCore.Qt.EditRole)
Class AssetTableModel:
class AssetTableModel(QtCore.QAbstractTableModel):
def __init__(self, assets = [], headers = [], parent = None):
QtCore.QAbstractTableModel.__init__(self, parent)
self.__assets = assets
self.__headers = headers
def rowCount(self, parent):
return len(self.__assets)
def columnCount(self, parent):
return len(self.__assets[0])
def flags(self, index):
return QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsEditable
def data(self, index, role):
row = index.row()
column = index.column()
if role == QtCore.Qt.EditRole:
return self.__assets[row][column]
if role == QtCore.Qt.DisplayRole:
print self.__assets[row][column]
return self.__assets[row][column]
def setData(self, index, value, role = QtCore.Qt.EditRole):
if role == QtCore.Qt.EditRole:
row = index.row()
column = index.column()
self.__assets[row][column] = value
self.dataChanged.emit(index, index)
return True
return False
def headerData(self, section, orientation, role):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
if section < len(self.__headers):
return self.__headers[section]
else:
return "not implimented"
else:
return "verticle not implimented"
def insertRows(self, position, rows, parent = QtCore.QModelIndex()):
self.beginInsertRows( parent, position, position + rows - 1 )
for i in range(rows):
defaultValues = [ "" for c in range( self.columnCount( None ) ) ]
self.__assets.insert( position, defaultValues )
self.endInsertRows()
return True
Class AssetWidget:
class AssetWidget(QtGui.QDialog):
def __init__(self, parent = None):
super(AssetWidget, self).__init__(parent)
uic.loadUi(uipath+'/AssetTable.ui', self)
# DB call here
self.loadAssetData()
# db call ends here
self.model = None
self.fillCombo(self.assetType)
self.cellDelegate = CellDelegate(self)
for i in range(10):
self.assetTV.setItemDelegateForColumn(i, self.cellDelegate)
self.sDateDelegate = DateDelegate(self)
self.assetTV.setItemDelegateForColumn(10, self.sDateDelegate )
self.assetTV.setItemDelegateForColumn(11, self.sDateDelegate)
self.connect(self.assettypeCB, QtCore.SIGNAL("currentIndexChanged(int)"), self.loadAssets )
self.connect(self.closeBTN , QtCore.SIGNAL("clicked()"), self.close )
self.connect(self.addRowBTN, QtCore.SIGNAL("clicked()"), self.addRow )
self.connect(self.assetTV, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.tableEdited )
self.show()
I think you are missing the setEditorData() method in your ItemDelegate.
From your attached sourcecode, I assume you are storing the date as a string? In my opinion, it is better to use a QDateTime object to store your date/time. If you do this, you do not need a ItemDelegate to provide an appropriate editor, because Qt knows which editor it needs to provide for this kind of datatype. (see Qt Documentation - Standard Editing Widgets.
However, if you still want to store your date as a string, see this sample program below on how to use delegtes.
from PyQt4 import QtCore
from PyQt4 import QtGui
import sys
class myModel(QtCore.QAbstractTableModel):
def __init__(self, parent):
QtCore.QAbstractTableModel.__init__(self, parent)
self.lst = []
#populate with a few dummy dates
#store dates as str values
dateTime = QtCore.QDateTime.currentDateTime()
for i in range(10):
strDate = dateTime.toString("dd/mm/yyyy")
self.lst.append([strDate])
dateTime = dateTime.addDays(1)
def rowCount(self, parent = QtCore.QModelIndex()):
return len(self.lst)
def columnCount(self, parent = QtCore.QModelIndex()):
return 1
def data(self, index, role = QtCore.Qt.DisplayRole):
row = index.row()
col = index.column()
if role == QtCore.Qt.DisplayRole:
return self.lst[row][col]
if role == QtCore.Qt.EditRole:
return self.lst[row][col]
def setData(self, index, value, role = QtCore.Qt.EditRole):
row = index.row()
col = index.column()
self.lst[row][col] = value
def flags(self, index):
return (QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable)
class DateDelegate(QtGui.QItemDelegate):
def __init__(self, parent):
QtGui.QItemDelegate.__init__(self, parent)
def createEditor(self, parent, option, index):
dateTimeEdit = QtGui.QDateTimeEdit(parent) #create new editor
#set properties of editor
dateTimeEdit.setDisplayFormat("dd/mm/yyyy")
dateTimeEdit.setCalendarPopup(True)
return dateTimeEdit
def setModelData(self, editor, model, index):
value = editor.dateTime().toString("dd/mm/yyyy")
model.setData(index, value)
def setEditorData(self, editor, index):
value = index.model().data(index, QtCore.Qt.EditRole)
qdate = QtCore.QDateTime().fromString(value, "dd/mm/yyyy")
editor.setDateTime(qdate)
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
table = QtGui.QTableView()
data = myModel(table)
table.setModel(data)
d = DateDelegate(table)
table.setItemDelegateForColumn(0, d)
table.resize(800, 600)
table.show()
sys.exit(app.exec_())