Friday, 20 July 2018

labeling - MultiLine field-name label referencing in PyQGIS?


A labeling routing (which is updated often) is being implemented via pyqgis and a QComboBox.


The code below worked great, that is until needs for narrowed column headers spawned the use of pythons '\n' (and subsequent problems thereafter).


This string formatting switch has caused any multiline field reference to return blank labels, while neighboring fields with single lines are displayed correctly.


A remedy I initially sought was to use fieldname (as strings) for the label settings, but this did not work. Then it was supposed that a index based context would work better (avoiding syntax), but such did not solve the problem


I am suspicious that I might be missing a line in my publishing of the QgsPalLayerSettings() class to the layer. Because when I do it manually through the QGIS labeling menu, multiline field names work perfectly fine.


Related to a previous question: Connecting vector label size with map-units?



field = str(self.csvGridFieldNames[index])
activeLayerName = self.iface.activeLayer().name()
csvGrid = ftools_utils.getMapLayerByName(unicode(activeLayerName))
# fieldIdx = csvGrid.fieldNameIndex(field)
textSize = 'coconut hoves'
valLabel = QgsPalLayerSettings()
valLabel.readFromLayer(csvGrid)
valLabel.enabled = True
valLabel.placement = QgsPalLayerSettings.OverPoint
valLabel.fieldName = field

# valLabel.fieldIndex = fieldIdx
valLabel.fontSizeInMapUnits = True
valLabel.setDataDefinedProperty(QgsPalLayerSettings.Size,True,True,'%f' %(textSize),'')
valLabel.writeToLayer(csvGrid)
valLabel.writeToLayer(csvGrid)
iface.legendInterface().refreshLayerSymbology(csvGrid)
iface.mapCanvas().refresh()



For sake of missing information, the following is a list of a few fieldNames (as contained in the list 'self.csvGridFieldNames');



While 'Status' and 'Kx' publish labels correctly, both 'Top\nElevation','Bottom\nElevation' do not.



['Status','Top\nElevation','Bottom\nElevation','Kx']





Per advice, the following also does not work:


(And 'print repr(field)' returns expected 'Top\\nElevation')


    rawField = str(self.csvGridFieldNames[index])
if '\n' in rawField:
beginingOfString,endOfString = rawField.split("\n")

field = beginingOfString+'\\n'+endOfString
else: field = rawField
valLable.wrapChar = '\\n' #<<=== with/without this line, still blank labels



Link to a ultra-beta QgsPointLayer Labeler pulled from a larger (yet unpublished) plugin. Extract contents to Qgis Python Plugins Dir as normal.


ReadMe instructions are a must for functionality.


QgsPointArray Labeler



Answer



After having a look at your CSV data file I have to say that the field names you are handling are not really recommended, since they generate all sorts of problems.



When you stated your question I guessed your field names looked like "Top\nElevation", which is not the case if you look at your data file, since it contains field names in the form:


"Top 
Elevation"

and that is certainly different.


As you have acknowledged, a fieldIndex should be enough to overcome those weird field names, but the fieldIndex is not implemented if you look at the QGIS code.


My Answer


I suggest you to implement a workaround.


I understand you need field names like those you have, mainly for presentation purposes. So, why not setting a multi-line alias for those fields and leaving your original field names in one line?


How to do that?





  1. Change your CSV field names to something like this (concatenate the multiple words with an underscore "_" ):


    enter image description here




  2. Right click on the layer Grid and select Properties.




  3. Go to the menu Fields and in the row corresponding to the "Top_Elevation" field, edit the alias value.



    Write TopElevation. As you can notice any ENTER you press won't start a new line. To insert the new line character you can open a text editor, press ENTER and then select and copy the character created (you need to have faith to do this :D).


    enter image description here


    With such a magic character in your clipboard, edit the alias again and paste the character between Top and Elevation. It will seem that you just entered a space, but you need to keep the faith :D.


    Finally, click on OK to close the dialog. This is how my dialog looks like before I click on OK:




enter image description here


And this is the result running your plugin (see the fields in the attribute table, the one which we defined an alias on is a multi-line field name):


enter image description here


This way you will be able to set a label on all your fields. In case that you have troubles with the code, I'm pasting you the following code snippet for reference:



valLabel=QgsPalLayerSettings()
valLabel.readFromLayer(iface.activeLayer())
field = iface.activeLayer().dataProvider().fields()[6].name()
valLabel.fieldName = field
valLabel.writeToLayer(iface.activeLayer())
iface.mapCanvas().refresh()

No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...