2010-03-04 236 views
-1

我正在嘗試編寫簡單的屬性編輯器。我已經自動生成pyqt類(WorkZone在下面的代碼中),我需要使用PropertyEditor查看/編輯它的一些屬性,並使用代表PropertyEditorDelegate,它使用自定義編輯器ColorEditor,LineEditor等。PyQt自定義代理QTreeWidget問題

主要思想是WorkZone知道需要編輯哪些屬性以及如何編輯屬性,PropertyEditor解析WorkZone,尋找這樣的屬性,並填充QTreeWidget及其值。

但有一個問題:委託沒有開始編輯雙擊,或'輸入',或smth。它被添加到正確的行,它繪製的項目,但就是這樣。 此外,當我將Propertyeditor容器的類型切換到QTableWidget時,委託開始工作得更加正確(但編輯器畫在屏幕的角落,而不是表格中)!

PS。還有一個問題:是否有任何方法可以將某些委託添加到行中,而無需將其實例存儲在其他位置(self._delegates在腳本中),這只是醜陋的。方法setItemDelegate接受指向委託的指針,並且在C++中獲得所有權,但在PyQT中並不是這樣......除此之外,例如,setItem沒有這樣的問題。

下面的腳本說明了這個問題:

# -*- coding: utf-8 -*- 
#!/usr/bin/env python 

from PyQt4 import QtCore, QtGui 
import inspect 


class LineEditor(QtGui.QLineEdit): 


    def __init__(self, name = None, parent = None, slot = None): 
     QtGui.QLineEdit.__init__(self, parent) 
     self.textChanged.connect(slot) 
     self.name = name 

    @staticmethod 
    def paintForDelegate(delegate, painter, option, index): 
     QtGui.QItemDelegate.paint(delegate, painter, option, index) 

    def get(self): 
     return str(self.text()) 

    def set(self, val): 
     self.setText(QtCore.QString.fromUtf8(val)) 

class ColorEditor(QtGui.QComboBox): 


    def _populateList(self): 
     for name in QtGui.QColor.colorNames(): 
      self.addItem(name) 
      index = self.findText(name) 
      self.setItemData(index, QtGui.QColor(name), QtCore.Qt.DecorationRole) 

    def __init__(self, name = None, parent = None, slot = None): 
     QtGui.QComboBox.__init__(self, parent) 
     self._populateList() 
     self.currentIndexChanged.connect(slot) 
     self.name = QtCore.QString.fromUtf8(name) 

    @staticmethod 
    def paintForDelegate(delegate, painter, option, index): 
     QtGui.QItemDelegate.paint(delegate, painter, option, index) 

    def get(self): 
     qColor = QtGui.QColor(self.itemData(self.currentIndex(), QtCore.Qt.DecorationRole))   
     color = ((qColor.blue() | (qColor.green() << 8)) | (qColor.red() << 16)) 
     return color 

    def set(self, val): 
     blue = (val & 255) 
     green = ((val & 65280) >> 8) 
     red = ((val & 16711680) >> 16) 
     color = QtGui.QColor(red, green, blue) 
     index = self.findData(color, QtCore.Qt.DecorationRole) 
     self.setCurrentIndex(index) 

class PropertyEditorDelegate(QtGui.QItemDelegate): 


    def __init__(self, object, propName, parent = None): 
     QtGui.QItemDelegate.__init__(self, parent) 
     self._object = object 
     self._propName = propName 

    def paint(self, painter, option, index): 
     self._object.paintForDelegate(self._propName, self, painter, option, index) 

    def createEditor(self, parent, option, index): 
     return self._object.createEditor(self._propName) 

    def setEditorData(self, editor, index): 
     value = index.model().data(index, QtCore.Qt.EditRole) 
     editor.set(value) 

    def setModelData(self, editor, model, index): 
     if index.column() == 0: 
      model.setData(index, editor.name, QtCore.Qt.EditRole) 
     else: 
      model.setData(index, editor.get(), QtCore.Qt.EditRole) 

    def updateEditorGeometry(self, editor, option, index): 
     editor.setGeometry(option.rect) 

class PropertyEditor(QtGui.QWidget): 

    def __init__(self, parent = None): 
     QtGui.QWidget.__init__(self, parent) 
     self._object = None 
     self._delegates = [] 
     self._mainLayout = QtGui.QVBoxLayout() 
     self._mainLayout.setContentsMargins(2, 2, 2, 2) 
     self._mainLayout.setSpacing(2) 
     self.setLayout(self._mainLayout) 
     self._contents = QtGui.QTreeWidget() 
     self._contents.setColumnCount(2) 
     self._contents.currentItemChanged.connect(self.printCurrent) 
     self._mainLayout.addWidget(self._contents) 

    def printCurrent(self, curr, prev): 
     print self._contents.currentIndex().row() 
     print self._contents.currentIndex().column() 
     print self._contents.itemDelegate(self._contents.currentIndex())._propName 
     print self._contents.itemDelegate(self._contents.currentIndex()) 

    def object(self): 
     return self._object 

    def setObject(self, value):  
     self._object = value 

     def isProperty(p): 
      return isinstance(p, property) 

     for (name, value) in inspect.getmembers(type(self._object), isProperty): 
      if self._object.isEditable(name): 
       item = QtGui.QTreeWidgetItem() 
       item.setData(0, QtCore.Qt.EditRole, QtCore.QString.fromUtf8(self._object.getPropertyName(name))) 
       item.setData(1, QtCore.Qt.EditRole, self._object.get(name)) 
       self._contents.addTopLevelItem(item) 

       self._delegates.append(PropertyEditorDelegate(self._object, name, self._contents)) 
       index = self._contents.indexOfTopLevelItem(item)  
       self._contents.setItemDelegateForRow(index, self._delegates[index]) 

class WorkZone(object): 


    def __init__(self): 

     self._name = '' 
     self.currentEditor = None 
     self.red = 100 
     self.green = 100 
     self.blue = 100 
     self._width = 1 

    def _getColor(self): 
     color = ((self.blue | (self.green << 8)) | (self.red << 16)) 
     return color 

    def _setColor(self, color): 
     self.blue = (color & 255) 
     self.green = ((color & 65280) >> 8) 
     self.red = ((color & 16711680) >> 16) 

    color = property(_getColor, _setColor) 

    def currentColorChanged(self, index): 
     if self.currentEditor is not None: 
      self.color = self.currentEditor.get() 
     print self.color 

    def currentNameChanged(self, newName): 
     if self.currentEditor is not None: 
      self.name = self.currentEditor.get() 
     print self.name 

    def createEditor(self, prop): 
     if prop == 'color': 
      self.currentEditor = ColorEditor('Color', None, self.currentColorChanged) 
      self.currentEditor.set(self.color) 
      return self.currentEditor 
     elif prop == 'name': 
      self.currentEditor = LineEditor('Name', None, self.currentNameChanged) 
      self.currentEditor.set(self.name) 
      return self.currentEditor 
     else: 
      return None 

    def releaseEditor(self): 
     self.currentEditor = None 

    def isEditable(self, prop): 
     if prop == 'color': 
      return True 
     elif prop == 'name': 
      return True 
     else: 
      return False 

    def set(self, prop, val): 
     if prop == 'color': 
      self.color = val 
     elif prop == 'name': 
      self.name = val 

    def get(self, prop): 
     if prop == 'color': 
      return self.color 
     elif prop == 'name': 
      return self.name 

    def getPropertyName(self, prop): 
     if prop == 'color': 
      return 'Color' 
     elif prop == 'name': 
      return 'Name' 

    def paintForDelegate(self, prop, delegate, painter, option, index): 
     if prop == 'color': 
      ColorEditor.paintForDelegate(delegate, painter, option, index) 
     elif prop == 'name': 
      LineEditor.paintForDelegate(delegate, painter, option, index) 

    def _setWidth(self, Width): 
     self._width = Width 

    def _getWidth(self): 
     return self._width 

    width = property(_getWidth, _setWidth) 

    def _getName(self): 
     return self._name 

    def _setName(self, val): 
     self._name = val 

    name = property(_getName, _setName) 



if __name__ == '__main__': 

    import sys 

    app = QtGui.QApplication(sys.argv) 
    zone = WorkZone() 
    zone.color = 0 
    zone.width = 1 
    propertyEditor = PropertyEditor() 
    propertyEditor.setObject(zone) 


    propertyEditor.show() 
    sys.exit(app.exec_()) 

回答

2

我結束了處理雙擊設置爲可編輯,強制項目編輯模式與editItem(),然後將其設置回來。代表本身處理所有的顯示和編輯。

# In __init__: 
    self.tree.itemActivated.connect(self.onDoubleClick) 


def onDoubleClick(self, item, index): 
    """ 
    The logic will happen in the editor delegate. This is needed to let 
    the delegate run by making this editable 
    """   
    item.setFlags(QtCore.Qt.ItemIsSelectable | 
        QtCore.Qt.ItemIsEnabled | 
        QtCore.Qt.ItemIsEditable)    

    # Force the item in to edit mode so the delegate picks it up 
    self.tree.editItem(item, index) 

    # Set the item back to not editable. The delegate will still do its 
    # job, but the read-only state will already be set when done! 
    item.setFlags(QtCore.Qt.ItemIsSelectable | 
        QtCore.Qt.ItemIsEnabled) 

第一個setFlags可能只需要ItemIsEditable工作,但這種感覺是對的。

0

答案很簡單,一如既往。對於QTreeWidgetItem%) 默認標誌不包括QtCore.Qt.ItemIsEditable。

1

On PS。

從PyQt的

QAbstractItemView.setItemDelegate的文檔(個體經營,QAbstractItemDelegate)

設置此視圖和模型委託的項目委託。如果您想完全控制項目的編輯和顯示,這非常有用。

任何現有的委託將被刪除,但不會被刪除。 QAbstractItemView不承擔委託代理的所有權。

警告:您不應在視圖之間共享委託的同一個實例。這樣做會導致不正確或不直觀的編輯行爲,因爲連接到給定委託的每個視圖都可能會收到closeEditor()信號,並嘗試訪問,修改或關閉已關閉的編輯器。