qtreeview root colored - colors

I have a QTreeView with a colored background but I need to have also the root colored..
I'm not able to do so.
Someone can help me?
This is the code..
Thanks
from PyQt4 import QtGui, QtCore
HORIZONTAL_HEADERS = ("Surname", "Given Name")
class person_class(object):
def init(self, sname, fname, isMale):
self.sname = sname
self.fname = fname
self.isMale = isMale
class TreeItem(object):
def init(self, person, header, parentItem):
self.person = person
self.parentItem = parentItem
self.header = header
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return 2
def data(self, column):
if self.person == None:
if column == 0:
return QtCore.QVariant(self.header)
if column == 1:
return QtCore.QVariant("")
else:
if column == 0:
return QtCore.QVariant(self.person.sname)
if column == 1:
return QtCore.QVariant(self.person.fname)
return QtCore.QVariant()
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
class treeModel(QtCore.QAbstractItemModel):
def init(self, parent=None):
super(treeModel, self).init(parent)
self.people = []
for fname, sname, isMale in (("John","Brown", 1),
("Fred", "Bloggs", 1), ("Sue", "Smith", 0)):
person = person_class(sname, fname, isMale)
self.people.append(person)
self.rootItem = TreeItem(None, "ALL", None)
self.parents = {0 : self.rootItem}
self.setupModelData()
def columnCount(self, parent=None):
if parent and parent.isValid():
return parent.internalPointer().columnCount()
else:
return len(HORIZONTAL_HEADERS)
def data(self, index, role):
if not index.isValid():
return QtCore.QVariant()
item = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
return item.data(index.column())
if role == QtCore.Qt.UserRole:
if item:
return item.person
if role == QtCore.Qt.BackgroundRole :
return QtGui.QColor('pink')
return QtCore.QVariant()
#def headerData(self, column, orientation, role):
#if (orientation == QtCore.Qt.Horizontal and
#role == QtCore.Qt.DisplayRole):
#try:
#return QtCore.QVariant(HORIZONTAL_HEADERS[column])
#except IndexError:
#pass
#return QtCore.QVariant()
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
if not childItem:
return QtCore.QModelIndex()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
p_Item = self.rootItem
else:
p_Item = parent.internalPointer()
return p_Item.childCount()
def setupModelData(self):
for person in self.people:
if person.isMale:
sex = "MALES"
else:
sex = "FEMALES"
if not self.parents.has_key(sex):
newparent = TreeItem(None, sex, self.rootItem)
self.rootItem.appendChild(newparent)
self.parents[sex] = newparent
parentItem = self.parents[sex]
newItem = TreeItem(person, "", parentItem)
parentItem.appendChild(newItem)
if name == "main":
app = QtGui.QApplication([])
model = treeModel()
dialog = QtGui.QDialog()
dialog.setMinimumSize(300,150)
layout = QtGui.QVBoxLayout(dialog)
tv = QtGui.QTreeView(dialog)
tv.setModel(model)
layout.addWidget(tv)
dialog.exec_()
app.closeAllWindows()

Related

How to use custome FilterProxyModel and QtCore.Qt.EditRole at a time?

Below is the example code:
class FilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self._filter_value = None
#property
def filter_value(self):
return self._filter_value
#filter_value.setter
def filter_value(self, value):
self._filter_value = value
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if self.filter_value is None:
return super().filterAcceptsRow(sourceRow, sourceParent)
if self.filterKeyColumn() >= 0:
value = (
self.sourceModel()
.index(sourceRow, self.filterKeyColumn(), sourceParent)
.data(self.filterRole())
)
return value == self.filter_value
for column in range(self.columnCount()):
value = (
self.sourceModel()
.index(sourceRow, column, sourceParent)
.data(self.filterRole())
)
if value == self.filter_value:
return True
return False
def setFilterRegExp(self, filter):
self.filter_value = None
super().setFilterRegExp(filter)
class UI(QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("tableview.ui", self)
self.show()
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('book.db')
db.open()
self.model = QtSql.QSqlTableModel(self)
self.model.setTable("card")
self.model.select()
self.tableView.setModel(self.model)
self.proxy = FilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
self.model.select()
self.edit.clicked.connect(self.edit_items)
self.refresh.clicked.connect(self.refresh_table)
column_names = ["Name","Age","Gender"]
self.comboBox.addItems([x for x in column_names])
self.horizontalHeader = self.tableView.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(
self.tableView_horizontalHeader_sectionClicked
)
self.lineEdit.textChanged.connect(self.lineEdit_textChanged)
def tableView_horizontalHeader_sectionClicked(self, logicalIndex):
menu = QtWidgets.QMenu(self)
values = []
for row in range(self.model.rowCount()):
value = self.model.index(row, logicalIndex).data(self.proxy.filterRole())
values.append(value)
action_all = QtWidgets.QAction("All", self)
action_all.setData(None)
menu.addAction(action_all)
menu.addSeparator()
for value in sorted(list(set(values))):
action = QtWidgets.QAction(str(value), self)
action.setData(value)
menu.addAction(action)
headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(logicalIndex)
action = menu.exec_(QtCore.QPoint(posX, posY))
if action is not None:
self.proxy.setFilterKeyColumn(logicalIndex)
self.proxy.filter_value = action.data()
def lineEdit_textChanged(self):
search = QtCore.QRegExp( self.lineEdit.text(),
QtCore.Qt.CaseInsensitive,
QtCore.QRegExp.RegExp
)
self.proxy.setFilterKeyColumn(self.comboBox.currentIndex() - 1)
self.proxy.setFilterRegExp(search)
def edit_items(self):
if not self.model.rowCount():
return
index = self.tableView.currentIndex()
if index.isValid():
row = index.row()
else:
row = 0
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Edit Window")
layout = QtWidgets.QVBoxLayout(dialog)
formLayout = QtWidgets.QFormLayout()
layout.addLayout(formLayout)
name_line = QtWidgets.QLineEdit(self.model.index(row, 0).data())
formLayout.addRow('Name', name_line)
name_line.setReadOnly(True)
age_edit = QtWidgets.QSpinBox()
formLayout.addRow('Age', age_edit)
age_edit.setFocus()
age_edit.setValue(self.model.index(row, 1).data())
genders = 'M', 'F'
gender_combo = QtWidgets.QComboBox()
formLayout.addRow('Gender', gender_combo)
gender_combo.addItems(genders)
gender = self.model.index(row, 2).data()
if gender and gender.upper() in genders:
gender_combo.setCurrentIndex(genders.index(gender.upper()))
else:
gender_combo.setCurrentIndex(-1)
updateButton = QtWidgets.QPushButton('Update')
layout.addWidget(updateButton)
updateButton.clicked.connect(dialog.accept)
if not dialog.exec_():
return
self.model.setData(self.model.index(row, 1), age_edit.value(),
QtCore.Qt.EditRole)
if gender_combo.currentIndex() >= 0:
self.model.setData(self.model.index(row, 2),
gender_combo.currentText(), QtCore.Qt.EditRole)
# submit all changes to the database
self.model.submitAll()
def refresh_table(self):
print("rsfersh")
This is working good Before filtering the columns with FilterProxyModel.
The problem is: Not getting exact row into "Edit Window", after filter the columns. For example its getting the row of db index row not from tableview.
Below is the reference Image.
As above image after filter, I want to edit 4th column("name4") but it is taking 1st column("name1"). How to edit required column after filtered.
The currentIndex method of QTableView returns a QModelIndex associated with the model established in the model, in this it is "self.proxy" and not with respect to "self.model". On the other hand, in these cases the task of mapping the columns is simplified using QDataWidgetMapper:
import sys
from PyQt5 import QtCore, QtWidgets, QtSql, uic
class FilterProxyModel(QtCore.QSortFilterProxyModel):
def __init__(self, parent=None):
super().__init__(parent)
self._filter_value = None
#property
def filter_value(self):
return self._filter_value
#filter_value.setter
def filter_value(self, value):
self._filter_value = value
self.invalidateFilter()
def filterAcceptsRow(self, sourceRow, sourceParent):
if self.filter_value is None:
return super().filterAcceptsRow(sourceRow, sourceParent)
if self.filterKeyColumn() >= 0:
value = (
self.sourceModel()
.index(sourceRow, self.filterKeyColumn(), sourceParent)
.data(self.filterRole())
)
return value == self.filter_value
for column in range(self.columnCount()):
value = (
self.sourceModel()
.index(sourceRow, column, sourceParent)
.data(self.filterRole())
)
if value == self.filter_value:
return True
return False
def setFilterRegExp(self, filter):
self.filter_value = None
super().setFilterRegExp(filter)
class UI(QtWidgets.QMainWindow):
def __init__(self):
super(UI, self).__init__()
uic.loadUi("tableview.ui", self)
self.tableView.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("book.db")
db.open()
self.model = QtSql.QSqlTableModel(self)
self.model.setTable("card")
self.model.select()
self.proxy = FilterProxyModel(self)
self.proxy.setSourceModel(self.model)
self.tableView.setModel(self.proxy)
self.model.select()
self.edit.clicked.connect(self.edit_items)
self.refresh.clicked.connect(self.refresh_table)
r = self.model.record()
column_names = [r.field(i).name().title() for i in range(r.count())]
self.comboBox.addItems([x for x in column_names])
self.horizontalHeader = self.tableView.horizontalHeader()
self.horizontalHeader.sectionClicked.connect(
self.tableView_horizontalHeader_sectionClicked
)
self.lineEdit.textChanged.connect(self.lineEdit_textChanged)
def tableView_horizontalHeader_sectionClicked(self, logicalIndex):
menu = QtWidgets.QMenu(self)
values = []
for row in range(self.model.rowCount()):
value = self.model.index(row, logicalIndex).data(self.proxy.filterRole())
values.append(value)
action_all = QtWidgets.QAction("All", self)
action_all.setData(None)
menu.addAction(action_all)
menu.addSeparator()
for value in sorted(list(set(values))):
action = QtWidgets.QAction(str(value), self)
action.setData(value)
menu.addAction(action)
headerPos = self.tableView.mapToGlobal(self.horizontalHeader.pos())
posY = headerPos.y() + self.horizontalHeader.height()
posX = headerPos.x() + self.horizontalHeader.sectionPosition(logicalIndex)
action = menu.exec_(QtCore.QPoint(posX, posY))
if action is not None:
self.proxy.setFilterKeyColumn(logicalIndex)
self.proxy.filter_value = action.data()
def lineEdit_textChanged(self):
search = QtCore.QRegExp(
self.lineEdit.text(), QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp
)
self.proxy.setFilterKeyColumn(self.comboBox.currentIndex())
self.proxy.setFilterRegExp(search)
def edit_items(self):
if not self.model.rowCount():
return
index = self.tableView.currentIndex()
if index.isValid():
row = index.row()
else:
row = 0
name_line = QtWidgets.QLineEdit(readOnly=True)
age_edit = QtWidgets.QSpinBox()
gender_combo = QtWidgets.QComboBox()
genders = "M", "F"
gender_combo.addItems(genders)
updateButton = QtWidgets.QPushButton("Update")
mapper = QtWidgets.QDataWidgetMapper()
mapper.setSubmitPolicy(QtWidgets.QDataWidgetMapper.ManualSubmit)
mapper.setModel(self.tableView.model())
mapper.addMapping(name_line, 0)
mapper.addMapping(age_edit, 1)
mapper.addMapping(gender_combo, 2)
mapper.setCurrentIndex(row)
dialog = QtWidgets.QDialog()
dialog.setWindowTitle("Edit Window")
layout = QtWidgets.QVBoxLayout(dialog)
formLayout = QtWidgets.QFormLayout()
layout.addLayout(formLayout)
formLayout.addRow("Name", name_line)
formLayout.addRow("Age", age_edit)
formLayout.addRow("Gender", gender_combo)
layout.addWidget(updateButton)
updateButton.clicked.connect(dialog.accept)
if dialog.exec_():
mapper.submit()
def refresh_table(self):
print("refresh")
def main():
app = QtWidgets.QApplication(sys.argv)
w = UI()
w.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()

QDataWidgetMapper; mapping TableWidget to model

I am not finding any documentation showing which widgets QDataWidgetMapper actually works for and have not found any implementation of mapping with a QTableWidget.
It definitely works for QLineEdit's and QComboBoxes, which are input widgets, but is it possible to map to a QTableWidget?
Goal is to use QUndoStack to undo/redo text change in each widget when added to the QUndostack. I want to be able to undo/redo text changes for the items in the QTableWidget as well as the QLineEdits and QComboBoxes.
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
class CommandTextEdit(QtWidgets.QUndoCommand):
def __init__(self, window, index, oldText, newText, description):
super(CommandTextEdit,self).__init__()
self.index = index
self.window = window
self.oldText = oldText
self.newText = newText
def redo(self):
self.index.model().itemDataChanged.disconnect(self.window.itemDataChangedSlot)
self.index.model().setData(self.index, self.newText, QtCore.Qt.EditRole)
self.index.model().itemDataChanged.connect(self.window.itemDataChangedSlot)
def undo(self):
self.index.model().itemDataChanged.disconnect(self.window.itemDataChangedSlot)
self.index.model().setData(self.index, self.oldText, QtCore.Qt.EditRole)
self.index.model().itemDataChanged.connect(self.window.itemDataChangedSlot)
class Model(QtCore.QAbstractListModel):
itemDataChanged = QtCore.pyqtSignal(object,object, object, object)
def __init__(self, text = [], parent = None):
super(Model,self).__init__(parent)
self._text = text
def rowCount(self,parent=QtCore.QModelIndex()):
return len(self._text)
def data(self,index,role):
row = index.row()
if role == QtCore.Qt.EditRole:
return self._text[row]
if role == QtCore.Qt.DisplayRole:
value = self._text[row]
return self._text[row]
def model(self):
return self
def flags(self, index):
return QtCore.Qt.ItemIsEditable |QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def setData(self, index, value, role = QtCore.Qt.UserRole + 1):
if index.isValid():
if role == QtCore.Qt.EditRole:
oldValue = self.data(index,role)
self._text[index.row()] = value
self.dataChanged.emit(index, index)
if oldValue != value:
self.itemDataChanged.emit(index, oldValue, value, role)
return True
return False
class Window(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(Window,self).__init__(parent)
self.setCentralWidget(QtWidgets.QWidget(self))
self.setWindowTitle('Widget Mapping GUI')
mainlayout = QtWidgets.QVBoxLayout()
#Information for widgets
items = ["","H","N","M"]
#LineEdit1
self.labelLineEdit1 = QtWidgets.QLabel()
self.labelLineEdit1.setText('LineEdit1')
self.LineEdit1 = QtWidgets.QLineEdit()
firstBox = QtWidgets.QHBoxLayout()
firstBox.addWidget(self.labelLineEdit1)
firstBox.addWidget(self.LineEdit1)
#LineEdit2
self.labelLineEdit2 = QtWidgets.QLabel()
self.labelLineEdit2.setText('LineEdit2')
self.LineEdit2 = QtWidgets.QLineEdit()
secondBox = QtWidgets.QHBoxLayout()
secondBox.addWidget(self.labelLineEdit2)
secondBox.addWidget(self.LineEdit2)
#ComboBox1
self.labelComboBox1 = QtWidgets.QLabel()
self.labelComboBox1.setText('ComboBox1')
self.ComboBox1 = QtWidgets.QComboBox()
self.ComboBox1.addItems(items)
thirdBox = QtWidgets.QHBoxLayout()
thirdBox.addWidget(self.labelComboBox1)
thirdBox.addWidget(self.ComboBox1)
#ComboBox2
self.labelComboBox2 = QtWidgets.QLabel()
self.labelComboBox2.setText('ComboBox2')
self.ComboBox2 = QtWidgets.QComboBox()
self.ComboBox2.addItems(items)
fourthBox = QtWidgets.QHBoxLayout()
fourthBox.addWidget(self.labelComboBox2)
fourthBox.addWidget(self.ComboBox2)
#TableWidget
self.TableWidget = QtWidgets.QTableWidget(5,1)
#Set header labels
self.TableWidget.setHorizontalHeaderLabels(["Dimensions"])
self.TableWidget.setVerticalHeaderLabels(["A","B","C","D","E"])
self.TableWidget.setItem(0,1,QtWidgets.QTableWidgetItem('1'))
fifthBox = QtWidgets.QVBoxLayout()
fifthBox.addWidget(self.TableWidget)
#Add Layouts
mainlayout.addLayout(firstBox)
mainlayout.addLayout(secondBox)
mainlayout.addLayout(thirdBox)
mainlayout.addLayout(fourthBox)
mainlayout.addLayout(fifthBox)
self.centralWidget().setLayout(mainlayout)
#Model
self.mapper = None
self.model = Model(['','','','',''])
self.setModel(self.model)
#QUndoStack
self.undoStack = QtWidgets.QUndoStack()
self.stackView = QtWidgets.QUndoView(self.undoStack)
self.stackView.setWindowTitle('StackView')
self.stackView.show()
#Run init methods
self.createActions()
self.makeConnections()
self.listViewMethod()
def createActions(self):
self.UndoAct = QtWidgets.QAction("Undo", self)
self.UndoAct.setShortcut(QtGui.QKeySequence.Undo)
self.UndoAct.triggered.connect(self.undoStack.undo)
self.RedoAct = QtWidgets.QAction("Redo", self)
self.RedoAct.setShortcut(QtGui.QKeySequence.Redo)
self.RedoAct.triggered.connect(self.undoStack.redo)
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(self.UndoAct)
self.toolbar.addAction(self.RedoAct)
def listViewMethod(self):
self.listView = QtWidgets.QListView()
self.listView.setModel(self.model)
self.listView.setWindowTitle('ListView')
self.listView.show()
def makeConnections(self):
self.model.itemDataChanged.connect(self.itemDataChangedSlot)
def setModel(self, model):
self.mapper = QtWidgets.QDataWidgetMapper(self)
self.mapper.setOrientation(QtCore.Qt.Vertical)
self.mapper.setModel(self.model)
self.mapper.addMapping(self.LineEdit1, 0)
self.mapper.addMapping(self.LineEdit2, 1)
self.mapper.addMapping(self.ComboBox1, 2)
self.mapper.addMapping(self.ComboBox2, 3)
self.mapper.addMapping(self.TableWidget,4)
self.mapper.toFirst()
def itemDataChangedSlot(self, index, oldValue, value, role):
if role == QtCore.Qt.EditRole:
command = CommandTextEdit(self, index, oldValue, value, "Text changed from '{0}' to '{1}'".format(oldValue, value))
self.undoStack.push(command)
def main():
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
To map the items of a QTableWidget, I used the setCellWidget method to map each cell to the model using a custom QLineEdit class.
Updated Code:
from PyQt5 import QtCore, QtGui, QtWidgets
import sys
#implemented stylesheets for QLineEdit to imitate a QTableWidgetItem
class CustomLineEdit(QtWidgets.QLineEdit):
def __init__(self):
super(CustomLineEdit,self).__init__()
self.setReadOnly(True)
self.setFrame(False)
self.setStyleSheet("QLineEdit { border: none; } \n" \
"QLineEdit::focus {background-color: #3daee9;} \n"\
"QLineEdit::focus::pressed {background-color: none;} \n"\
"QLineEdit::hover {background-color: #3daee9;}"
)
def mouseDoubleClickEvent(self,event):
self.setReadOnly(False)
self.setStyleSheet("QLineEdit { border: none; \nbackground-color: none;} \n" \
)
def focusOutEvent(self,event):
self.setReadOnly(True)
self.setStyleSheet("QLineEdit { border: none; } \n" \
"QLineEdit::focus {background-color: #3daee9;} \n"\
"QLineEdit::focus::pressed {background-color: none;} \n"\
"QLineEdit::hover {background-color: #3daee9;}"
)
class CommandTextEdit(QtWidgets.QUndoCommand):
def __init__(self, window, index, oldText, newText, description):
super(CommandTextEdit,self).__init__()
self.index = index
self.window = window
self.oldText = oldText
self.newText = newText
def redo(self):
self.index.model().itemDataChanged.disconnect(self.window.itemDataChangedSlot)
self.index.model().setData(self.index, self.newText, QtCore.Qt.EditRole)
self.index.model().itemDataChanged.connect(self.window.itemDataChangedSlot)
def undo(self):
self.index.model().itemDataChanged.disconnect(self.window.itemDataChangedSlot)
self.index.model().setData(self.index, self.oldText, QtCore.Qt.EditRole)
self.index.model().itemDataChanged.connect(self.window.itemDataChangedSlot)
class Model(QtCore.QAbstractListModel):
itemDataChanged = QtCore.pyqtSignal(object,object, object, object)
def __init__(self, text = [], parent = None):
super(Model,self).__init__(parent)
self._text = text
def rowCount(self,parent=QtCore.QModelIndex()):
return len(self._text)
def data(self,index,role):
row = index.row()
if role == QtCore.Qt.EditRole:
return self._text[row]
if role == QtCore.Qt.DisplayRole:
value = self._text[row]
return self._text[row]
def model(self):
return self
def flags(self, index):
return QtCore.Qt.ItemIsEditable |QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def setData(self, index, value, role = QtCore.Qt.UserRole + 1):
if index.isValid():
if role == QtCore.Qt.EditRole:
oldValue = self.data(index,role)
self._text[index.row()] = value
self.dataChanged.emit(index, index)
if oldValue != value:
self.itemDataChanged.emit(index, oldValue, value, role)
return True
return False
class Window(QtWidgets.QMainWindow):
def __init__(self, parent = None):
super(Window,self).__init__(parent)
self.setCentralWidget(QtWidgets.QWidget(self))
self.setWindowTitle('Widget Mapping GUI')
mainlayout = QtWidgets.QVBoxLayout()
#Information for widgets
items = ["","H","N","M"]
#LineEdit1
self.labelLineEdit1 = QtWidgets.QLabel()
self.labelLineEdit1.setText('LineEdit1')
self.LineEdit1 = QtWidgets.QLineEdit()
firstBox = QtWidgets.QHBoxLayout()
firstBox.addWidget(self.labelLineEdit1)
firstBox.addWidget(self.LineEdit1)
#LineEdit2
self.labelLineEdit2 = QtWidgets.QLabel()
self.labelLineEdit2.setText('LineEdit2')
self.LineEdit2 = QtWidgets.QLineEdit()
secondBox = QtWidgets.QHBoxLayout()
secondBox.addWidget(self.labelLineEdit2)
secondBox.addWidget(self.LineEdit2)
#ComboBox1
self.labelComboBox1 = QtWidgets.QLabel()
self.labelComboBox1.setText('ComboBox1')
self.ComboBox1 = QtWidgets.QComboBox()
self.ComboBox1.addItems(items)
thirdBox = QtWidgets.QHBoxLayout()
thirdBox.addWidget(self.labelComboBox1)
thirdBox.addWidget(self.ComboBox1)
#ComboBox2
self.labelComboBox2 = QtWidgets.QLabel()
self.labelComboBox2.setText('ComboBox2')
self.ComboBox2 = QtWidgets.QComboBox()
self.ComboBox2.addItems(items)
fourthBox = QtWidgets.QHBoxLayout()
fourthBox.addWidget(self.labelComboBox2)
fourthBox.addWidget(self.ComboBox2)
#TableWidget
self.TableWidget = QtWidgets.QTableWidget(5,1)
#Set header labels
self.TableWidget.setHorizontalHeaderLabels(["Dimensions"])
self.TableWidget.setVerticalHeaderLabels(["A","B","C","D","E"])
self.TableWidget.setCellWidget(0,0,CustomLineEdit())
self.TableWidget.setCellWidget(1,0,CustomLineEdit())
self.TableWidget.setCellWidget(2,0,CustomLineEdit())
self.TableWidget.setCellWidget(3,0,CustomLineEdit())
self.TableWidget.setCellWidget(4,0,CustomLineEdit())
self.TableWidget.setItem(1,0,QtWidgets.QTableWidgetItem('1'))
fifthBox = QtWidgets.QVBoxLayout()
fifthBox.addWidget(self.TableWidget)
#Add Layouts
mainlayout.addLayout(firstBox)
mainlayout.addLayout(secondBox)
mainlayout.addLayout(thirdBox)
mainlayout.addLayout(fourthBox)
mainlayout.addLayout(fifthBox)
self.centralWidget().setLayout(mainlayout)
#Model
self.mapper = None
self.model = Model(['' for i in range(9)])
self.setModel(self.model)
#QUndoStack
self.undoStack = QtWidgets.QUndoStack()
self.stackView = QtWidgets.QUndoView(self.undoStack)
self.stackView.setWindowTitle('StackView')
self.stackView.show()
#Run init methods
self.createActions()
self.makeConnections()
self.listViewMethod()
def createActions(self):
self.UndoAct = QtWidgets.QAction("Undo", self)
self.UndoAct.setShortcut(QtGui.QKeySequence.Undo)
self.UndoAct.triggered.connect(self.undoStack.undo)
self.RedoAct = QtWidgets.QAction("Redo", self)
self.RedoAct.setShortcut(QtGui.QKeySequence.Redo)
self.RedoAct.triggered.connect(self.undoStack.redo)
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(self.UndoAct)
self.toolbar.addAction(self.RedoAct)
def listViewMethod(self):
self.listView = QtWidgets.QListView()
self.listView.setModel(self.model)
self.listView.setWindowTitle('ListView')
self.listView.show()
def makeConnections(self):
self.model.itemDataChanged.connect(self.itemDataChangedSlot)
def setModel(self, model):
self.mapper = QtWidgets.QDataWidgetMapper(self)
self.mapper.setOrientation(QtCore.Qt.Vertical)
self.mapper.setModel(self.model)
self.mapper.addMapping(self.LineEdit1, 0)
self.mapper.addMapping(self.LineEdit2, 1)
self.mapper.addMapping(self.ComboBox1, 2)
self.mapper.addMapping(self.ComboBox2, 3)
self.mapper.addMapping(self.TableWidget.cellWidget(0,0), 4)
self.mapper.addMapping(self.TableWidget.cellWidget(1,0), 5)
self.mapper.addMapping(self.TableWidget.cellWidget(2,0), 6)
self.mapper.addMapping(self.TableWidget.cellWidget(3,0), 7)
self.mapper.addMapping(self.TableWidget.cellWidget(4,0), 8)
self.mapper.toFirst()
def itemDataChangedSlot(self, index, oldValue, value, role):
if role == QtCore.Qt.EditRole:
command = CommandTextEdit(self, index, oldValue, value, "Text changed from '{0}' to '{1}'".format(oldValue, value))
self.undoStack.push(command)
def main():
app = QtWidgets.QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

How to remove data from excel file displayed in PyQt5 and refresh it

My database application uses the Pandas library. I can display the excel file into my tableView but anytime I remove data from the mainframe and try to refresh the tableView. It gives me a keyError.
I'm trying to get it to display the refreshed table. I am attempting to drop the row that a user asks for. It works when it drops because I outputted the information but the tableView itself won't refresh and gives an error.
df = pd.read_excel("filename")
model = PandasModel(df)
self.tableView.setModel(model)
self.tableView.resizeColumnsToContents()
def DeletePlayer(self):
global df
choose = self.removePlayerEdit.text()
if(choose == '0'):
df = df.drop([0])
print("Player deleted")
print(df)
class PandasModel(QtCore.QAbstractTableModel):
def __init__(self, df = pd.DataFrame(), parent=None):
QtCore.QAbstractTableModel.__init__(self, parent=parent)
self._df = df
def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
if orientation == QtCore.Qt.Horizontal:
try:
return self._df.columns.tolist()[section]
except (IndexError, ):
return QtCore.QVariant()
elif orientation == QtCore.Qt.Vertical:
try:
# return self.df.index.tolist()
return self._df.index.tolist()[section]
except (IndexError, ):
return QtCore.QVariant()
def data(self, index, role=QtCore.Qt.DisplayRole):
if role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
if not index.isValid():
return QtCore.QVariant()
return QtCore.QVariant(str(self._df.ix[index.row(), index.column()]))
def setData(self, index, value, role):
row = self._df.index[index.row()]
col = self._df.columns[index.column()]
if hasattr(value, 'toPyObject'):
# PyQt4 gets a QVariant
value = value.toPyObject()
else:
# PySide gets an unicode
dtype = self._df[col].dtype
if dtype != object:
value = None if value == '' else dtype.type(value)
self._df.set_value(row, col, value)
return True
def rowCount(self, parent=QtCore.QModelIndex()):
return len(self._df.index)
def columnCount(self, parent=QtCore.QModelIndex()):
return len(self._df.columns)
def sort(self, column, order):
colname = self._df.columns.tolist()[column]
self.layoutAboutToBeChanged.emit()
self._df.sort_values(colname, ascending= order == QtCore.Qt.AscendingOrder, inplace=True)
self._df.reset_index(inplace=True, drop=True)
self.layoutChanged.emit()
When implementing a model, you should not access the element that stores the data (dataframe) directly, because if you modify it, the model will not know what is going to generate problems, instead you should create methods that modify the internal data but use the methods as beginRemoveRows and endRemoveColumns that will notify the model of the change.
def removeColumn(self, col):
if 0 <= col < self.columnCount():
self.beginRemoveRows(QtCore.QModelIndex(), col, col)
self._df.drop(
self._df.columns[[col]], axis=1, inplace=True
)
self._df.reset_index(inplace=True, drop=True)
self.endRemoveColumns()
I have improved my initial model to the following:
from PyQt5 import QtCore, QtGui, QtWidgets
import pandas as pd
import numpy as np
class FloatDelegate(QtWidgets.QStyledItemDelegate):
#property
def decimals(self):
if not hasattr(self, "_decimals"):
self._decimals = 2
return self._decimals
#decimals.setter
def decimals(self, decimals):
self._decimals = decimals
def createEditor(self, parent, option, index):
DBL_MAX = 1.7976931348623157e308
editor = QtWidgets.QDoubleSpinBox(
parent, minimum=-DBL_MAX, maximum=DBL_MAX, decimals=self.decimals
)
return editor
def setEditorData(self, editor, index):
editor.setValue(index.data())
def setModelData(self, editor, model, index):
model.setData(index, editor.value(), QtCore.Qt.DisplayRole)
def displayText(self, value, locale):
return "{}".format(value)
class DataFrameModel(QtCore.QAbstractTableModel):
DtypeRole = QtCore.Qt.UserRole + 1000
ValueRole = QtCore.Qt.UserRole + 1001
def __init__(self, df=pd.DataFrame(), parent=None):
super(DataFrameModel, self).__init__(parent)
self._dataframe = df
def setDataFrame(self, dataframe):
self.beginResetModel()
self._dataframe = dataframe.copy()
self.endResetModel()
def dataFrame(self):
return self._dataframe
dataFrame = QtCore.pyqtProperty(
pd.DataFrame, fget=dataFrame, fset=setDataFrame
)
#QtCore.pyqtSlot(int, QtCore.Qt.Orientation, result=str)
def headerData(
self,
section: int,
orientation: QtCore.Qt.Orientation,
role: int = QtCore.Qt.DisplayRole,
):
if role == QtCore.Qt.DisplayRole:
if orientation == QtCore.Qt.Horizontal:
return self._dataframe.columns[section]
else:
return str(self._dataframe.index[section])
return QtCore.QVariant()
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return len(self._dataframe.index)
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return self._dataframe.columns.size
def data(self, index, role=QtCore.Qt.DisplayRole):
if not index.isValid() or not (
0 <= index.row() < self.rowCount()
and 0 <= index.column() < self.columnCount()
):
return QtCore.QVariant()
row = self._dataframe.index[index.row()]
col = self._dataframe.columns[index.column()]
dt = self._dataframe[col].dtype
val = self._dataframe.iloc[row][col]
if role == QtCore.Qt.DisplayRole:
return val
elif role == DataFrameModel.ValueRole:
return val
if role == DataFrameModel.DtypeRole:
return dt
return QtCore.QVariant()
def setData(self, index, value, role):
row = self._dataframe.index[index.row()]
col = self._dataframe.columns[index.column()]
if hasattr(value, "toPyObject"):
# PyQt4 gets a QVariant
value = value.toPyObject()
else:
# PySide gets an unicode
dtype = self._dataframe[col].dtype
if dtype != object:
value = None if value == "" else dtype.type(value)
self._dataframe.at[row, col] = value
return True
def flags(self, index):
flags = (
QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsDragEnabled
| QtCore.Qt.ItemIsEditable
| QtCore.Qt.ItemIsEnabled
)
return flags
def roleNames(self):
roles = {
QtCore.Qt.DisplayRole: b"display",
DataFrameModel.DtypeRole: b"dtype",
DataFrameModel.ValueRole: b"value",
}
return roles
def removeRow(self, row):
if 0 <= row < self.rowCount():
self.beginRemoveRows(QtCore.QModelIndex(), row, row)
self._dataframe.drop([row], inplace=True)
self._dataframe.reset_index(inplace=True, drop=True)
self.endRemoveRows()
def removeColumn(self, col):
if 0 <= col < self.columnCount():
self.beginRemoveRows(QtCore.QModelIndex(), col, col)
self._dataframe.drop(
self._dataframe.columns[[col]], axis=1, inplace=True
)
self._dataframe.reset_index(inplace=True, drop=True)
self.endRemoveColumns()
def sort(self, column, order):
colname = self._dataframe.columns[column]
self.layoutAboutToBeChanged.emit()
self._dataframe.sort_values(
colname, ascending=order == QtCore.Qt.AscendingOrder, inplace=True
)
self._dataframe.reset_index(inplace=True, drop=True)
self.layoutChanged.emit()
class Widget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent)
tableview = QtWidgets.QTableView()
tableview.setSortingEnabled(True)
delegate = FloatDelegate(tableview)
tableview.setItemDelegate(delegate)
delegate.decimals = 4
self.spinbox_row = QtWidgets.QSpinBox()
self.button_row = QtWidgets.QPushButton(
"Delete Row", clicked=self.remove_row
)
self.spinbox_col = QtWidgets.QSpinBox()
self.button_col = QtWidgets.QPushButton(
"Delete Column", clicked=self.remove_col
)
df = pd.DataFrame(
np.random.uniform(0, 100, size=(100, 4)), columns=list("ABCD")
)
self._model = DataFrameModel(df)
tableview.setModel(self._model)
grid = QtWidgets.QGridLayout(self)
grid.addWidget(tableview, 0, 0, 1, 4)
grid.addWidget(self.spinbox_row, 1, 0)
grid.addWidget(self.button_row, 1, 1)
grid.addWidget(self.spinbox_col, 1, 2)
grid.addWidget(self.button_col, 1, 3)
self.on_rowChanged()
self.on_columnChanged()
self._model.rowsInserted.connect(self.on_rowChanged)
self._model.rowsRemoved.connect(self.on_rowChanged)
self._model.columnsInserted.connect(self.on_columnChanged)
self._model.columnsRemoved.connect(self.on_columnChanged)
#QtCore.pyqtSlot()
def on_rowChanged(self):
self.spinbox_row.setMaximum(self._model.rowCount() - 1)
#QtCore.pyqtSlot()
def on_columnChanged(self):
self.spinbox_col.setMaximum(self._model.columnCount() - 1)
#QtCore.pyqtSlot()
def remove_row(self):
row = self.spinbox_row.value()
self._model.removeRow(row)
#QtCore.pyqtSlot()
def remove_col(self):
col = self.spinbox_col.value()
self._model.removeColumn(col)
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.resize(640, 480)
w.show()
sys.exit(app.exec_())

PyQT: adding to QAbstractItemModel using a button

I'm trying to implement a TreeView using QAbstractItemModel. I want to set up an empty model and then add an item to the model using a button.
This appears to be a lot more complex than I'd realised! I've modified a custom instance of the QAbstractItemModel example found in the Lib\site-packages\PyQt4\examples\itemviews\simpletreemodel\simpletreemodel.pyw.
Rather than setting up the model all at once, I want o build it incrementally.
When I click the button, I get the message showing that the process method is executing, but the model isn't updated.
Any pointers as to why it's not working would be greatly appreciated!
from PyQt4 import QtGui, QtCore
import sys
class myWindow(QtGui.QWidget):
def __init__(self):
QtGui.QWidget.__init__(self)
self.resize(500, 400)
#Tree Widget
self.treeView = QtGui.QTreeView(self)
#Model
self.model = TreeModel()
self.treeView.setModel(self.model)
# Button
self.button = QtGui.QPushButton(self)
self.button.setText('Add Item')
self.button.clicked.connect(self.process)
# Add to Layout
self.gridLayout = QtGui.QGridLayout(self)
self.gridLayout.addWidget(self.button,0,0)
self.gridLayout.addWidget(self.treeView,1,0)
def process(self):
print "here"
newItem = TreeItem(["bob","bob","bob"],self.model.rootItem)
self.model.rootItem.appendChild(newItem)
class TreeItem(object):
def __init__(self, data, parent=None):
self.parentItem = parent
self.itemData = data
self.childItems = []
def appendChild(self, item):
self.childItems.append(item)
def child(self, row):
return self.childItems[row]
def childCount(self):
return len(self.childItems)
def columnCount(self):
return len(self.itemData)
def data(self, column):
try:
return self.itemData[column]
except IndexError:
return None
def parent(self):
return self.parentItem
def row(self):
if self.parentItem:
return self.parentItem.childItems.index(self)
return 0
class TreeModel(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super(TreeModel, self).__init__(parent)
self.rootItem = TreeItem(("Model", "Status","Location"))
def columnCount(self, parent):
if parent.isValid():
return parent.internalPointer().columnCount()
else:
return self.rootItem.columnCount()
def data(self, index, role):
if not index.isValid():
return None
if role != QtCore.Qt.DisplayRole:
return None
item = index.internalPointer()
return item.data(index.column())
def flags(self, index):
if not index.isValid():
return QtCore.Qt.NoItemFlags
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
return self.rootItem.data(section)
return None
def index(self, row, column, parent):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self.rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
def rowCount(self, parent):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self.rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
if __name__ == "__main__":
app = QtGui.QApplication.instance() # checks if QApplication already exists
if not app: # create QApplication if it doesnt exist
app = QtGui.QApplication(sys.argv)
# Window
window = myWindow()
window.show()
sys.exit(app.exec_())
Using a QStandardItemModel has worked a treat!
import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
self.resize(500, 400)
#Tree Widget
self.treeView = QTreeView()
#Model
self.model = QStandardItemModel()
self.treeView.setModel(self.model)
# Button
self.button = QPushButton()
self.button.setText('Add Item')
self.button.clicked.connect(self.addChildClick)
# Add to Layout
self.gridLayout = QGridLayout(self)
self.gridLayout.addWidget(self.button,0,0)
self.gridLayout.addWidget(self.treeView,1,0)
def addChildClick(self):
selection = self.treeView.selectedIndexes()
text = "bob"
item = QStandardItem(text)
# if nothing selected parent is model
if selection == []:
parent = self.model
else: # Otherwise parent is what is selected
s = selection[0] # Handling multiple selectons
parent = self.model.itemFromIndex(s)
parent.appendRow(item)
#cleanup
self.treeView.expandAll()
self.treeView.clearSelection()
if __name__ == "__main__":
app = QApplication.instance() # checks if QApplication already exists
if not app: # create QApplication if it doesnt exist
app = QApplication(sys.argv)
# Window
window = Window()
window.show()
app.exec_()

PyQt Custom QAbstractTableModel for combobox

In the following sample code I want to use a QDataWidgetMapper together with a Combobox to display data retrieved from a database.
Unfortunately I can't e.g. preselect the choice of the combobox and I can't retrieve the database id on selecting a value in the combobox. Therefore I think I am really missing something fundamental here.
I reduced my program to this sample code which I hope illustrates my problem.
import sys
from PyQt4 import QtGui
from PyQt4 import QtCore
class ComboModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(ComboModel, self).__init__()
# The data would normally be read from a database (not using the Qt SQL classes)
self.dbIds = [ 13, 49, 81 ]
self.dbValues = [ 'Value 1', 'Value 2', 'Value 3' ]
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return len(self.dbIds)
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return 2
def data(self, index, role=QtCore.Qt.DisplayRole):
print 'ComboModel data'
if not index.isValid() or ( role != QtCore.Qt.DisplayRole and role != QtCore.Qt.EditRole ):
print 'ComboModel return invalid QVariant'
return QtCore.QVariant()
if index.column() == 0:
return QtCore.QVariant(self.dbIds[index.row()])
if index.column() == 1:
return QtCore.QVariant(self.dbValues[index.row()])
print 'ComboModel return invalid QVariant'
return QtCore.QVariant()
class TableModel(QtCore.QAbstractTableModel):
def __init__(self, parent=None, *args):
super(TableModel, self).__init__()
self.editValue = 'Value to edit'
self.comboValue = 49 # This is the database id of the selected entry
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return 1
def columnCount(self, parent=QtCore.QModelIndex()):
if parent.isValid():
return 0
return 2
def data(self, index, role=QtCore.Qt.DisplayRole):
print 'TableModel data'
if not index.isValid() or ( role != QtCore.Qt.DisplayRole and role != QtCore.Qt.EditRole ):
print 'TableModel return invalid QVariant'
return QtCore.QVariant()
if index.column() == 0:
return QtCore.QVariant(self.editValue)
if index.column() == 1:
return QtCore.QVariant(self.comboValue)
print 'TableModel return invalid QVariant'
return QtCore.QVariant()
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
print 'TableModel setData'
if not index.isValid():
return False
if index.column() == 0:
print 'new Value: ' + value.toString()
self.editValue = value.toString()
if index.column() == 1:
print 'new combobox value'
print 'new Value: ' + value.toString()
self.comboValue = value.toString() # how do I get the dbId?
return True
class MainWindow(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Test')
self.edit = QtGui.QLineEdit(self)
self.edit.setGeometry(10, 40, 80, 30)
self.combo = QtGui.QComboBox(self)
self.combo.setGeometry(10, 80, 80, 30)
self.combo_model = ComboModel()
self.combo.setModel(self.combo_model)
self.combo.setModelColumn(1)
self.model = TableModel()
self.mapper = QtGui.QDataWidgetMapper()
self.mapper.setModel(self.model)
self.mapper.addMapping(self.edit, 0)
self.mapper.addMapping(self.combo, 1)
self.mapper.toFirst();
app = QtGui.QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

Resources