3

I am looking for a way to print a custom feature form created in PyQt Designer for QGIS 2.4 using PyQGIS. My goal is to print/save image as the current extent of the map with the selected feature's attributes of interest on the same page.

I'm experimenting with two different ways:

First, is to use QgsComposition and QPrinter to add and print items to a pdf: including the map, legend, and attributes. My issue here is getting the only the desired attributes to display dynamically changing depending on the active layer. If I could add my feature form as a map item, I would be displaying the attributes added to the custom form.

UPDATE: The following code may be able to display my map with a legend and selective table, but I still have many issues with the map items (no text in the table, positioning, unable to include only desired layer in legend). I've included all the code associated with this print composer:

layer = qgis.utils.iface.activeLayer()

qgis.utils.iface.actionZoomToSelected().trigger()
qgis.utils.iface.mapCanvas().zoomScale(1000)

mapRenderer = iface.mapCanvas().mapRenderer()
c = QgsComposition(mapRenderer)
c.setPlotStyle(QgsComposition.Print)
x, y = 0, 0
w, h = c.paperWidth(), c.paperHeight()
composerMap = QgsComposerMap(c, x,y,w*.75,h)
c.addItem(composerMap)

legend = QgsComposerLegend(c)
c.addComposerLegend(legend)
legend.model().setLayerSet(mapRenderer.layerSet())
legend.setTitle('')

legendModel = QgsLegendModel(c)
legendModel.setLayerSet([layer.id()])

legend.updateLegend()

selected_ids = [f.id() for f in layer.selectedFeatures()]
filter_id_string = ','.join([str(id) for id in selected_ids])

table = QgsComposerAttributeTable(c)
table.setVectorLayer(layer)
table.setMaximumNumberOfFeatures(20)
table.setFeatureFilter("id in (" + filter_id_string + ")")
table.setFilterFeatures(True)
table.setItemPosition(x+50, y+50)
table.applyDefaultSize()
col1 = QgsComposerTableColumn()
col1.setAttribute('Diametre')
col2 = QgsComposerTableColumn()
col2.setAttribute('Nature')
table.setColumns([col1, col2])
c.addItem(table)

printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName('out.pdf')
printer.setPaperSize(QSizeF(c.paperWidth(), c.paperHeight()), QPrinter.Millimeter)
printer.setFullPage(True)
printer.setColorMode(QPrinter.Color)
printer.setResolution(c.printResolution())
pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
c.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()

Second, is to embed a image of the current extent (using mapCanvas().saveasImage()) using a dynamic QPixMap element on the custom form. This brings me back to the same question as to whether or not the custom feature form could be printed (to pdf, and eventually paper).

1 Answer 1

1

Which version of QGIS are you using? If it's 2.4, you can set the columns shown in a QgsComposerAttributeTable by using the setColumns method. This method takes a list of QgsComposerTableColumn. Here's an example

#create a composition and composer attribute table
composition = QgsComposition(iface.mapCanvas().mapSettings())
table = QgsComposerAttributeTable(composition)

#create columns for the table
col1 = QgsComposerTableColumn()
col1.setAttribute('feature_id')
col2 = QgsComposerTableColumn()
col2.setAttribute('feature_name')

#set table columns
table.setColumns([col1, col2])

There's a bunch of other things you can set for columns, such as the heading, alignment and sort order (see api docs here). You can even set an expression for a column in place of the attribute for added flexibility.

edit: To show only certain features you can set a filter on the table:

table.setFeatureFilter("id in (1,2,3)")
table.setFilterFeatures( True )

So, to filter a table to only selected features:

#get current layer
current_layer = qgis.utils.iface.mapCanvas().currentLayer()
#get list of selected feature ids
selected_ids = [f.id() for f in current_layer.selectedFeatures()]
#convert list to string
filter_id_string = ','.join([str(id) for id in selected_ids])
#filter table
table.setFeatureFilter("$id in (" + filter_id_string + ")")
table.setFilterFeatures( True )

update: I missed a character in the filter expression. It should have been "$id in (...". Here's a complete, working version of the code you posted. There was a few simple mistakes (the constructor for QgsLegendModel takes no arguments, there's no applyDefaultSize method for attribute tables, and I've also moved the attribute table setItemPosition line):

from PyQt4.QtCore import *
from PyQt4.QtGui import *

layer = qgis.utils.iface.activeLayer()

qgis.utils.iface.actionZoomToSelected().trigger()
qgis.utils.iface.mapCanvas().zoomScale(1000)

mapRenderer = iface.mapCanvas().mapRenderer()
c = QgsComposition(mapRenderer)
c.setPlotStyle(QgsComposition.Print)
x, y = 0, 0
w, h = c.paperWidth(), c.paperHeight()
composerMap = QgsComposerMap(c, x,y,w*.75,h)
c.addItem(composerMap)

legend = QgsComposerLegend(c)
c.addComposerLegend(legend)
legend.model().setLayerSet(mapRenderer.layerSet())
legend.setTitle('')

legendModel = QgsLegendModel()
legendModel.setLayerSet([layer.id()])

legend.updateLegend()

selected_ids = [f.id() for f in layer.selectedFeatures()]
filter_id_string = ','.join([str(id) for id in selected_ids])

table = QgsComposerAttributeTable(c)
table.setItemPosition(x+50, y+50)
table.setVectorLayer(layer)
table.setMaximumNumberOfFeatures(20)
table.setFeatureFilter("$id in (" + filter_id_string + ")")
table.setFilterFeatures(True)
col1 = QgsComposerTableColumn()
col1.setAttribute('Diametre')
col1.setHeading("Diametre")
col2 = QgsComposerTableColumn()
col2.setAttribute('Nature')
col2.setHeading("Nature")
table.setColumns([col1, col2])
c.addItem(table)

printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName('out.pdf')
printer.setPaperSize(QSizeF(c.paperWidth(), c.paperHeight()), QPrinter.Millimeter)
printer.setFullPage(True)
printer.setColorMode(QPrinter.Color)
printer.setResolution(c.printResolution())
pdfPainter = QPainter(printer)
paperRectMM = printer.pageRect(QPrinter.Millimeter)
paperRectPixel = printer.pageRect(QPrinter.DevicePixel)
c.render(pdfPainter, paperRectPixel, paperRectMM)
pdfPainter.end()
7
  • This is helpful for displaying only desired attributes. I'm still looking however, for a way to display only the attributes of selected items.
    – user25976
    Commented Jul 10, 2014 at 0:53
  • @user25976: I've edited the answer to include this
    – ndawson
    Commented Jul 10, 2014 at 1:18
  • I'm working through these methods and trying to get the table to work. I end up with two little boxes without text in a very unexpected position on the page (using QgsComposerItem.setItemPosition()). I'm sure this is an error on my part. Questions: Is setAttribute() simply expecting a string of the attribute name? Any tips on integrating loadFromTemplate()? I do like your answer and will accept in a few days--just waiting a bit to see if there are any ideas as to how to get the feature form itself printed as my company's QGIS users are very familiar with the way it's formatted.
    – user25976
    Commented Jul 11, 2014 at 0:40
  • @user25976 have you set the layer for the attribute table? I left that off my original code. You'll need something like table.setVectorLayer( current_layer ). You may also need table.setMaximumNumberOfFeatures(20) to set the number of visible features.
    – ndawson
    Commented Jul 11, 2014 at 0:45
  • yes, I've just edited my post to include the code i'm trying. I've still got the same problem even after setting the vector layer and max amount of features.
    – user25976
    Commented Jul 11, 2014 at 18:57

Not the answer you're looking for? Browse other questions tagged or ask your own question.