models & editors in PyQt4

S

Skink

Hi,

I created simple property classes with editing option, but since i'm not
too much experienced in PyQt4 i'd like to ask if i handle ColorProperty
changing right. Any other Property has its own editor and their control
flow is imho ok. Hovewer i'm not sure about ColorProperty.

What i do is: in createEditor(self, parent, option, index) i call
QtGui.QColorDialog.getColor() and return None

What do you think? Is it Ok?

thanks,
skink

#--------------------------------------------
import sys
from PyQt4 import QtGui, QtCore

class PropertyDelegate(QtGui.QItemDelegate):
def __init__(self, model):
QtGui.QItemDelegate.__init__(self, None)
self.model = model
def createEditor(self, parent, option, index):
if index.column() == 0:
return None
item = self.model.getItem(index)
return item.createEditor(parent, option, index)
def setEditorData(self, editor, index):
item = self.model.getItem(index)
item.setEditorData(editor, index)
def setModelData(self, editor, model, index):
item = self.model.getItem(index)
item.setModelData(editor, model, index)
# def updateEditorGeometry(self, editor, option, index):
# editor.setGeometry(option.rect)
def paint(self, painter, option, index):
item = self.model.getItem(index)
if isinstance(item, ColorProperty) and index.column() == 1:
item.paint(painter, option, index)
return
QtGui.QItemDelegate.paint(self, painter, option, index)

class SimpleModelItem:
def __init__(self, parent=0):
self._index = []
self._children = []
self._parent = parent
if parent:
parent._children.append(self)
self._parent = parent
def parent(self):
return self._parent
def children(self):
return self._children
def addIndex(self, index):
self._index.append(index)
def childId(self, child):
return id(self._children[child])
def index(self, column=0):
return self._index[column]

class Property(SimpleModelItem):
def __init__(self, parent=0, name="", data=[]):
SimpleModelItem.__init__(self, parent)
self._data = data
self._name = name
def data(self, index):
return self._data[index.column()]
def name(self):
return self._name
def createEditor(self, parent, option, index):
return None
def setEditorData(self, editor, index):
return
def setModelData(self, editor, model, index):
return

class StringProperty(Property):
def __init__(self, parent, name, t):
Property.__init__(self, parent, name, [t, ""])
def createEditor(self, parent, option, index):
editor = QtGui.QLineEdit(parent)
return editor
def setEditorData(self, editor, index):
value = index.model().getObjectData(self.name())
editor.setText(value)
def setModelData(self, editor, model, index):
index.model().setObjectData(self.name(), editor.text())
def data(self, index):
if index.column() == 0:
return self._data[0]
else:
return str(index.model().getObjectData(self.name()))
class IntegerProperty(Property):
def __init__(self, parent, name, t):
Property.__init__(self, parent, name, [t, ""])
def createEditor(self, parent, option, index):
editor = QtGui.QSpinBox(parent)
editor.setMaximum(256*256)
return editor
def setEditorData(self, editor, index):
value = index.model().getObjectData(self.name())
editor.setValue(value)
def setModelData(self, editor, model, index):
index.model().setObjectData(self.name(), editor.value())
def data(self, index):
if index.column() == 0:
return self._data[0]
else:
return str(index.model().getObjectData(self.name()))

class SizeProperty(Property):
def __init__(self, parent, name, t):
Property.__init__(self, parent, name, [t, ""])
self.items = []
self.items.append(IntegerProperty(self, name+":x", "x"))
self.items.append(IntegerProperty(self, name+":y", "y"))
self.items.append(IntegerProperty(self, name+":w", "width"))
self.items.append(IntegerProperty(self, name+":h", "height"))

class ColorProperty(Property):
def __init__(self, parent, name, t):
Property.__init__(self, parent, name, [t, ""])
def createEditor(self, parent, option, index):
color = QtGui.QColorDialog.getColor()
if color.isValid():
index.model().setObjectData(self.name(), color)
return None
def paint(self, painter, option, index):
r = option.rect
if option.state & QtGui.QStyle.State_Selected:
painter.fillRect(r, option.palette.highlight())
color = index.model().getObjectData(self.name())
colorStr = color.name()
tr = painter.boundingRect(r, QtCore.Qt.AlignLeft, colorStr)
w = tr.width() + 4
painter.fillRect(r.x() + 1, r.y() + 1, r.width() - 3 - w,
r.height() - 3, QtGui.QBrush(color))
painter.setPen(QtCore.Qt.black)
painter.drawRect(r.x() + 1, r.y() + 1, r.width() - 3 - w,
r.height() - 3)
r.setLeft(r.x() + r.width() + 1 - w)
painter.drawText(r, QtCore.Qt.AlignLeft, colorStr)

class EnumProperty(Property):
def __init__(self, parent, name, t, te):
Property.__init__(self, parent, name, [t, ""])
self.te = te
def createEditor(self, parent, option, index):
editor = QtGui.QComboBox(parent)
i = 0
for item in self.te:
editor.addItem(item, QtCore.QVariant(i))
i += 1
return editor
def setEditorData(self, editor, index):
index = index.model().getObjectData(self.name())
editor.setCurrentIndex(index)
def setModelData(self, editor, model, index):
index.model().setObjectData(self.name(), editor.currentIndex())
def data(self, index):
if index.column() == 0:
return self._data[0]
else:
index = index.model().getObjectData(self.name())
return self.te[index]

class BooleanProperty(EnumProperty):
def __init__(self, parent, name, t):
EnumProperty.__init__(self, parent, name, t, ["False", "True"])

#-----------------------------------------------------------------------
class SimpleModel(QtCore.QAbstractItemModel):
def __init__(self, root, parent=None):
QtCore.QAbstractItemModel.__init__(self, parent)
self.ids = {}

self.root = root
self.root.addIndex(QtCore.QModelIndex())
self.ids[id(self.root)] = self.root

self.walk(root)
def walk(self, parent):
row = 0
for child in parent.children():
_id = id(child)
self.ids[_id] = child
child.addIndex(self.createIndex(row, 0, _id))
child.addIndex(self.createIndex(row, 1, _id))
self.walk(child)
row += 1
def getItem(self, index):
if not index.isValid():
return self.root
else:
return self.ids[index.internalId()]

#-----------------------------------------------------------------------
def setObject(self, obj):
self.obj = obj
self.reset()
def getObjectData(self, name):
#print "getObjectData", name, self.obj[name]
return self.obj[name]
def setObjectData(self, name, value):
#print "setObjectData", name, value
self.obj[name] = value

#-----------------------------------------------------------------------
def index(self, row, column, parent):
parentItem = self.getItem(parent)
childItem = self.ids[parentItem.childId(row)]
return childItem.index(column)
def parent(self, index):
childItem = self.getItem(index)
parentItem = childItem.parent()
return parentItem.index()
def rowCount(self, parent):
parentItem = self.getItem(parent)
return len(parentItem.children())
def columnCount(self, parent):
return 2
def data(self, index, role):
if not index.isValid() or role != QtCore.Qt.DisplayRole:
return QtCore.QVariant()
item = self.getItem(index)
return QtCore.QVariant(item.data(index))
def flags(self, index):
if not index.isValid():
return QtCore.Qt.ItemIsEnabled
else:
return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsEditable
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Horizontal and role ==
QtCore.Qt.DisplayRole:
if section == 0:
return QtCore.QVariant("Name")
else:
return QtCore.QVariant("Value")
return QtCore.QVariant()

objAData = {
"width": 6, "size:x": 10, "size:y": 15, "size:w": 20, "size:h": 25,
"frameType": 0, "visible": 1, "color": QtGui.QColor(QtCore.Qt.red),
"name": "foobar A"
}
objBData = {
"width": 66, "size:x": 100, "size:y": 150, "size:w": 200, "size:h":
250,
"frameType": 1, "visible": 0, "color": QtGui.QColor(QtCore.Qt.green),
"name": "foobar B"
}
objCData = {
"width": 666, "size:x": 1000, "size:y": 1500, "size:w": 2000,
"size:h": 2500,
"frameType": 2, "visible": 1, "color": QtGui.QColor(QtCore.Qt.blue),
"name": "foobar C"
}

root = Property()
newItem = StringProperty(root, "name", "Name")
newItem = IntegerProperty(root, "width", "Item Width")
newItem = ColorProperty(root, "color", "Background Color")
newItem = SizeProperty(root, "size", "Item Size")
newItem = EnumProperty(root, "frameType", "Frame Type", ["Solid",
"Dashed", "None"])
newItem = BooleanProperty(root, "visible", "Visibility")

class Widget(QtGui.QWidget):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
mainLayout = QtGui.QVBoxLayout()

self.treeView = QtGui.QTreeView()

self.treeView.setEditTriggers(QtGui.QAbstractItemView.SelectedClicked)
self.model = SimpleModel(root)
self.delegate = PropertyDelegate(self.model)
self.treeView.setItemDelegate(self.delegate)
self.model.setObject(objAData)
self.treeView.setModel(self.model)
mainLayout.addWidget(self.treeView)
b1 = QtGui.QPushButton("Object A")
self.connect(b1, QtCore.SIGNAL("clicked()"), self.objAClicked)
mainLayout.addWidget(b1)
b2 = QtGui.QPushButton("Object B")
self.connect(b2, QtCore.SIGNAL("clicked()"), self.objBClicked)
mainLayout.addWidget(b2)
b3 = QtGui.QPushButton("Object C")
self.connect(b3, QtCore.SIGNAL("clicked()"), self.objCClicked)
mainLayout.addWidget(b3)
self.setLayout(mainLayout)
def objAClicked(self):
self.model.setObject(objAData)
def objBClicked(self):
self.model.setObject(objBData)
def objCClicked(self):
self.model.setObject(objCData)


if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
widget = Widget()
widget.show()
sys.exit(app.exec_())
#--------------------------------------------
 
D

David Boddie

Skink said:
I created simple property classes with editing option, but since i'm not
too much experienced in PyQt4 i'd like to ask if i handle ColorProperty
changing right. Any other Property has its own editor and their control
flow is imho ok. Hovewer i'm not sure about ColorProperty.

What i do is: in createEditor(self, parent, option, index) i call
QtGui.QColorDialog.getColor() and return None

What do you think? Is it Ok?

It should be OK - it shouldn't crash, anyway. It depends on the view
doing the right thing when it finds that it hasn't received an editor
widget.

You could create an empty placeholder widget in the createEditor()
method, call QColorDialog.getColor() with the existing color from the
model in setEditorData(), record the color returned by the dialog in
some internal instance variable, and finally set the data in the model
when setModelData() is called:

class ColorProperty(Property):
...
def createEditor(self, parent, option, index):
return QtGui.QWidget(parent)
def setEditorData(self, editor, index):
self.color = QtGui.QColorDialog.getColor(
index.model().getObjectData(self.name()))
def setModelData(self, editor, model, index):
if self.color.isValid():
index.model().setObjectData(self.name(), self.color)
...

I find it strange that you have to triple-click to edit any of the
items in your example. Do you see the same behaviour?

David
 
S

Skink

David said:
It should be OK - it shouldn't crash, anyway. It depends on the view
doing the right thing when it finds that it hasn't received an editor
widget.

You could create an empty placeholder widget in the createEditor()
method, call QColorDialog.getColor() with the existing color from the
model in setEditorData(), record the color returned by the dialog in
some internal instance variable, and finally set the data in the model
when setModelData() is called:

class ColorProperty(Property):
...
def createEditor(self, parent, option, index):
return QtGui.QWidget(parent)
def setEditorData(self, editor, index):
self.color = QtGui.QColorDialog.getColor(
index.model().getObjectData(self.name()))
def setModelData(self, editor, model, index):
if self.color.isValid():
index.model().setObjectData(self.name(), self.color)

thanks for tip, i'll check it how it works.
...

I find it strange that you have to triple-click to edit any of the
items in your example. Do you see the same behaviour?
oh, this is default Qt behavoiur: first click selects row, second select
editor (for ColorProperty, IntProperty & StringProperty you can now
change the value) but third click is required only for properties w/
QCombobox editor (EnumProperty & BooleanProperty) ...

skink
 
D

David Boddie

Skink said:
David Boddie wrote:
oh, this is default Qt behavoiur: first click selects row, second select
editor (for ColorProperty, IntProperty & StringProperty you can now
change the value) but third click is required only for properties w/
QCombobox editor (EnumProperty & BooleanProperty) ...

It seemed that, even if the row was already selected, it took more than
a double click to start editing. I'll have to take another look at it.

David
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top