QGIS – Saving and Applying Layout of Columns in Attribute Table

attribute-tableorderpyqgisqgissorting

I have multiple projects with similar layers.

Here is an example, with all attributes (fields) visible in default order.

enter image description here

I do use this layout for certain tasks. But for other tasks, I wish to hide some columns and re-order others using the 'Organize Columns' option – right-click click top bar of the attribute table.

In the example below, apart from hiding some layers, I have moved the column (attributes or fields) called "Priority", and others too.

enter image description here

So the attribute table now looks like this…
enter image description here

I would like to switch between these attribute table layouts in the current layer and apply the same to other similar layers in another project.

Is this possible? Happy to consider PyQGIS solutions.

Best Answer

I can suggest a PyQGIS approach to (1) adjust, (2) export, (3) downgrade and (4) finally import configurations applied to the attribute table of a layer. It is mainly based on the QgsAttributeTableConfig() class and its methods.

Let's assume there is a point layer called 'points_in_polygon' with a corresponding attribute table, see the image below:

input

The first thing to start is to analyze all the configurations that the attribute table of the layer possesses.

<attributetableconfig sortOrder="0" sortExpression="" actionWidgetStyle="dropDown">
  <columns>
   <column hidden="0" type="field" width="-1" name="id"/>
   <column hidden="0" type="field" width="-1" name="city"/>
   <column hidden="0" type="field" width="-1" name="month"/>
   <column hidden="0" type="field" width="-1" name="temp"/>
   <column hidden="0" type="field" width="-1" name="avg"/>
   <column hidden="0" type="field" width="-1" name="avg2"/>
   <column hidden="1" type="actions" width="-1"/>
  </columns>
 </attributetableconfig>

For the attribute table itself via the QgsAttributeTableConfig class:

method value explanation
sortOrder() sortOrder="0" Gets the sort order.
sortExpression() sortExpression="" Gets the expression used for sorting.
actionWidgetStyle() actionWidgetStyle="dropDown" Gets the style of the action widget.

For the fields/columns by means of the columns() method, that launches the ColumnConfig structure:

method value explanation
hidden() hidden="0" Flag that controls if the column is hidden.
name() name="id" The name of the attribute if this column represents a field.
type() type="field" The type of this column.
width() width="-1" Width of column, or -1 for default width.

Now, the following things will be executed:

  1. apply some configurations to the layer's attribute table
  2. export those configurations to the XML file
  3. switch back to the default configurations
  4. import the configurations from the XML file

Step 1 : Adjusting the attribute table of the layer

There are several useful threads that can reinforce this procedure:

They can be easily combined into a single PyQGIS script to perform this task.

For the initial attribute table, it is supposed to:

  • hide "temp", "avg", and "avg2" columns
  • change the sort in descending order to the "month" field
  • swap the order of fields to "city", "month", and "id"

Now the attribute table will look like this:

result

Check how the QgsAttributeTableConfig has changed:

<attributetableconfig actionWidgetStyle="dropDown" sortOrder="1" sortExpression="&quot;month&quot;">
  <columns>
   <column type="field" hidden="0" name="city" width="-1"/>
   <column type="field" hidden="0" name="month" width="-1"/>
   <column type="field" hidden="0" name="id" width="-1"/>
   <column type="field" hidden="1" name="temp" width="-1"/>
   <column type="field" hidden="1" name="avg" width="-1"/>
   <column type="field" hidden="1" name="avg2" width="-1"/>
   <column type="actions" hidden="1" width="-1"/>
  </columns>
 </attributetableconfig>

Step 2 : Exporting attribute table settings to the XML file

Export those styles to an XML file. Inspiration I found in this QGIS/tests/src/python/test_qgsvectorlayer_namedstyle.py dir.

By means of the following code:

# imports
from qgis.core import QgsProject
from PyQt5.QtXml import QDomDocument
from os.path import realpath

# referring to a vector layer by its name
layer = QgsProject.instance().mapLayersByName("points_in_polygon")[0]

# getting the config of the layer's attribute table
config = layer.attributeTableConfig()

# creating QDomDocument and QDomNode holders for the config 
settings = QDomDocument()
string_content = f"<!DOCTYPE {layer.name()}><settings></settings>"
settings.setContent(string_content)
node = settings.firstChild()

# writing attribute table config into an XML structure
config.writeXml(node)

# converting the QDomDocument to a string
settings_content = settings.toString()

# writting settings to an XML file
path_to_xml = realpath('D:/qgis_test/settings.xml')
with open(path_to_xml, 'w') as xml_file:
    xml_file.write(settings_content)

Now there is an XML file 'settings.xml':

step_2

Step 3 : Switching back to the default attribute table settings

Now let's revert back to the default (empty) config of the layer's attribute table.

With this piece of code:

# imports
from qgis.core import QgsProject, QgsAttributeTableConfig

# referring to a vector layer by its name
layer = QgsProject.instance().mapLayersByName("points_in_polygon")[0]

# initiating a new config for the layer's attribute table
config = QgsAttributeTableConfig()

# downgrading to the default config of the layer's attribute table
layer.setAttributeTableConfig(config)

it is possible to get this:

step_3

Step 4 : Importing attribute table settings from the XML file

On the final step let's import styles from the 'settings.xml' XML file.

With this script:

# imports
from qgis.utils import iface
from qgis.core import QgsProject
from PyQt5.QtXml import QDomDocument
from os.path import realpath
from xml.dom import minidom

# referring to a vector layer by its name
layer = QgsProject.instance().mapLayersByName("points_in_polygon")[0]

# getting the config of the layer's attribute table
config = layer.attributeTableConfig()
# or initiating a new one also works
# config = QgsAttributeTableConfig()

# openning the XML file with settings
path_to_xml = realpath('D:/qgis_test/settings.xml')
xml_file = minidom.parse(path_to_xml)

# creating QDomDocument and QDomNode holders for the config
settings = QDomDocument()
settings.setContent(xml_file.toxml())
node = settings.firstChildElement('settings')

# reading attribute table config from an XML structure
config.readXml(node)

# applying a new setting for the layer's attribute table 
layer.setAttributeTableConfig(config)

# showing the attribute table
iface.showAttributeTable(layer)

one can easily return to the custom config of the layer's attribute table:

step_4


References:

Related Question