QGIS API: 属性の変更

属性の変更方法は、v0.9からv1.0で大幅に変わっています。
v1.0の場合はこちら

v0.9までの場合

属性の変更はややこしそうですが、
わかってしまえばたいしたことはありません(でした)。
ただ、少し特殊な印象です。

要は、削除した要素のIDのセット、追加した要素の属性名と値のマップ、
変更した要素のIDと属性名・値の”マップのマップ”を、
QgsVectorLayer.commitAttributeChanges()に渡せばいいわけです。
属性の変更だけなら、前2つは空のセット、マップを渡しておけばよさそうです。

わざわざこうしているのは、筆者の勝手な想像ですが、
すべての変更をキャンセルできるようにするためかとおもわれます。

以前はchangeAttributeValue()という関数があったのですが、
現在はなくなっています。

(どうやら小生がv0.9のAPIを使っているときにv1.0のドキュメントを見ていたためのようです)。

というわけで、以下のようなコード(抜粋)で変更できるかと思われます。
この例では、QTableWidgetのセルを編集したときに
メンバ変数のマップに変更した情報をため込み、
行が変わったときに変更を反映するようにしています。

注意!

この例ではテーブルの列数をそのまま変更するフィールドの番号として使っていますが、
geometry_columnが属性フィールドの間に挟まっているような場合はこれではうまくいきません。
PostGISにデータを放り込んだ後に属性フィールドを追加したり、
ogr2ogrコマンドでPostGISに放り込んだりした場合はこの例ではうまくいかないので、注意してください。

QGIS API 属性値をテーブルに表示するときの対策
を参照してください。

  changeAtt = {}
  
  # QgsMapCanvasLayerのセット
  # こちらを参照
  layers = []
  
  # self.attributeTableはQTableWidgetオブジェクト
  
  # どこか適当なところで
    self.layers[0].layer().startEditing()
    self.connect(self.attributeTable, SIGNAL("itemChanged(QTableWidgetItem*)"), self.onItemChanged)
    self.attributeTable.emit(SIGNAL("currentCellChanged"), (-1, -1, 0, 0,))
    
  # アイテムが編集されたとき
  def onItemChanged(self, item):
    # カラムの先頭にIDを入れているので-1
    att = self.attributeTable.currentColumn()-1
    self.changeAtt.update({att:item.data(Qt.DisplayRole)})

  # 現在のセルが移動したとき
  def onSelChangeTable(self, cR, cC, pR, pC):
    layer = self.layers[0].layer()
    if not cR == self.curRow:
      if len(self.changeAtt) > 0:
        item = self.attributeTable.item(pR, 0)
        fid = int(item.text())
        aChange = {fid:self.changeAtt}
        layer.commitAttributeChanges
          (layer.deletedFeatureIds(), {}, aChange)
        self.changeAtt.clear()
      
      self.curRow = cR

startEditing()はレイヤーへの変更を許可するもので、
QgsVectorLayer.commitChanges()という関数を実行すると不許可になります。
名前がややこしいので気をつけましょう。

v1.0の場合

startEditing()関数を使って編集を開始するところは同じですが、それ以降が異なります。
上のコードではQTableWidgetのセルを編集したときに
メンバ変数に変更箇所を溜め込むようにしていますが、
その部分をAPI側で受け持ってくれるようになっているようです。

したがって、上のコードの例を書き換えると以下のようになります。

注意!

この例でも上記の例と同様に、geometry_columnが属性フィールドの間に挟まっているとうまくいかないので、
注意してください。

QGIS API 属性値をテーブルに表示するときの対策
を参照してください。

  # QgsMapCanvasLayerのセット
  # こちらを参照
  layers = []
  
  # self.attributeTableはQTableWidgetオブジェクト
  
  # どこか適当なところで
    self.connect(self.attributeTable, SIGNAL("itemChanged(QTableWidgetItem*)"), self.onItemChanged)
    self.attributeTable.emit(SIGNAL("currentCellChanged"), (-1, -1, 0, 0,))
    self.layers[0].layer().startEditing()
    
  # アイテムが編集されたとき
  def onItemChanged(self, item):
    layer = self.layers[0].layer()
    att = self.attributeTable.currentColumn()-1
    fid = int(self.attributeTable.item(self.curRow, 0).text())
    # セルの編集があった時点でレイヤーに変更を通知する
    layer.changeAttributeValue(fid, self.attributeTable.currentColumn()-1, item.data(Qt.DisplayRole))

  # 現在のセルが移動したとき
  def onSelChangeTable(self, cR, cC, pR, pC):
    layer = self.layers[0].layer()
    if not cR == self.curRow:
      # ここでは変更の確定だけを行う
      layer.commitChanges()

      self.curRow = cR
アーカイブ