QGIS API: 属性値をテーブルに表示するときの対策

QGIS APIでは、属性はキーと属性値のマップで帰ってきます。
このキーは順番に並んでいる保証はないので、順番にテーブルのカラムに追加したり、
キー番号をそのままカラムの位置に使ったりすると不具合が出ることがあります。
特にPostGISデータの場合は、shp2pgsqlコマンドでShapeデータを放り込んだ後でフィールドを追加したり、
ogr2ogrコマンドで直接放り込んだ場合などでは、間にGeometryColumnが挟まるので、順番になりません。

ここはQGIS本体のコードを参考に、QTableWidgetに属性を表示する場合の例を示します。
本体コードの該当箇所はqgsattributetable.cppのQgsAttributeTable::fillTable()関数と、
QgsAttributeTable::putFeatureInTable()関数あたりです。

テーブルヘッダを設定

  # メンバにマップを作る
  attrMapIndex = {}

  provider = layer.dataProvider()
  fields = provider.fields()

  # 必ずidは最初に入れておかなければいけないらしい
  self.attributeTable.setColumnCount(len(fields)+1)
  self.attributeTable.setHorizontalHeaderItem(0, QTableWidgetItem("id"))

  # カラム番号はidが追加されたので、1からスタート
  n = 1

  # テーブルヘッダを設定しつつ、マップにキーとカラム番号を登録する
  for (i, field) in fields.iteritems():
  # v2.x系の場合
  for field in fields.toList():
    headerItem = QTableWidgetItem(field.name())
    headerItem.setData(Qt.UserRole, i)
    headerItem.setData(Qt.UserRole+1, field.name())
    headerItem.setData(Qt.UserRole+2, int(field.type()))
    self.attributeTable.setHorizontalHeaderItem(n, headerItem)
    self.attrMapIndex.update({i:n})
    n += 1

属性をテーブルに表示するところ

v1.x系の場合
  allAttrs = provider.attributeIndexes()
  provider.select(allAttrs, QgsRectangle(), False)
  feat = QgsFeature()

  self.attributeTable.setRowCount(layer.featureCount())
  n = 1
  while provider.nextFeature(feat):
    attrMap = feat.attributeMap()

    # 最初のカラムにはIDを入れる
    self.attributeTable.setItem(n, 0, QTableWidgetItem(str(feat.id())))

    for (i, attr) in attrMap.iteritems():

      # fieldのキーが見つからなければとばす
      if not ( i in self.attrMapIndex ):
        continue

      strA = attr.toString()
      # ヘッダを登録したときのマップから、カラム位置を取得する
      self.attributeTable.setItem(n, self.attrMapIndex[i], QTableWidgetItem(strA))
      n += 1
v2.x系の場合
  features = self.pglayer.getFeatures()
  feat = QgsFeature()
  while features.nextFeature( feat ):
    attrVect = feat.attributes()
    self.attributeTable.setItem(n, 0, QTableWidgetItem(str(feat.id())))

    m = 1
    for attr in attrVect:
      if attr != None:
        strA = attr.toString()
        self.attributeTable.setItem(n, m, QTableWidgetItem(strA))
      m += 1
    n += 1

属性の編集

  # 現在のセルが変更されたときのスロット
  def onItemChanged(self, item):
    layer = self.layers1[0].layer()
    att = -1

    # 現在のカラム番号から属性キーを検索する
    for (k, v) in self.attrMapIndex.iteritems():
      if ( v == self.attributeTable.currentColumn() ):
        att = k
          break

        if att == -1:
          return

        # 属性の変更
        fid = int(self.attributeTable.item(self.curRow, 0).text())
        layer.changeAttributeValue(fid, att, item.data(Qt.DisplayRole))
アーカイブ