Annonce
Pour sa 21ème année, l’association GeoRezo a toujours besoin de vous !
10€ = 1 mois de frais bancaires ; 15€ = 12 mois de nom de domaine ; 30€ = 1 semaine de location des serveurs …
Retrouver nos membres bienfaiteurs
#1 Mon 20 January 2020 16:41
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
QGIS 3/Python: Modifier des couches vecteurs
Bonjour à tous,
Et merci à ceux qui prendront la peine de lire. Je suis novice dans le développement Python pour QGIS.
J'essaye de faire des auto-exercices 'step by step' pour acquérir les notions/traitement de bases, en suivant principalement le cookbook version 3.4.
A l'avenir j'aimerais (évidemment) être capable de remplir, pour chaque objet, un champ avec une valeur issu d'un calcul préalable, d'une géométrie d'une autre couche, etc...
Pour le moment je bloque pour tout simplement modifier un champ que je viens de créer (ici 'Alors') pour remplacer les 'NULL' par une valeur constante (ici 'Bien joué').
Voici mon code
[Je sais qu'il y a des choses inutiles, surtout au début de celui ci, mais cela me permet de garder une trace de chaque petite étape, pour plus tard)
Code:
from qgis.core import * from qgis.utils import * from qgis.gui import * iface=qgis.utils.iface pjt=QgsProject.instance() canvas=iface.mapCanvas() for layer in canvas.layers(): print (layer.name()) #Renvoie : #"Troncon CTRL" #"Listing GEOSAT" #"Listing CTRL" Tctrl = canvas.layers()[0] Lgeosat = canvas.layers()[1] Lctrl = canvas.layers()[2] #les numéros sont, a priori, dans l'ordre du canvas print(Lctrl.name()) #Renvoie : "Listing CTRL" iface.setActiveLayer(Lgeosat) #Listing GEOSAT devient la couche sélectionnée XLctrl=Lctrl.dataProvider().fields()[0] caps = Lctrl.dataProvider().capabilities() #voir si la couche vecteur est modifiable if caps & QgsVectorDataProvider.DeleteFeatures: print('C\'est possible') else : print('Pas possible') #Renvoie : "C'est possible" XLctrl=Lctrl.dataProvider().fields()[0] caps = Lctrl.dataProvider().capabilities() Lctrl.startEditing() #ouvrir mode édition de Listing Ctrl # je fais sans problème les suppressions et ajouts de champs via le dataprovider if caps & QgsVectorDataProvider.DeleteAttributes: Lctrl.dataProvider().deleteAttributes([3]) Lctrl.dataProvider().addAttributes([QgsField("Alors", QVariant.String)]) AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors") fid = 10 valeur = 'Bien joué' # j'ai réussi à faire la modif via getFeature mais sur un seul objet : fid = 10, comme décrit dans le cookbook Lctrl.changeAttributeValue(fid, AlorsLctrl, valeur) Lctrl.commitChanges()
je peux aussi le faire via le data provider (j'ai trouvé la solution en rédigeant ce post):
Code:
Lctrl.startEditing() #ouvrir mode édition de Listing Ctrl if caps & QgsVectorDataProvider.DeleteAttributes: Lctrl.dataProvider().deleteAttributes([3]) Lctrl.dataProvider().addAttributes([QgsField("Alors", QVariant.String)]) AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors") fid = 15 attrib = {3:"Bien joué"} Lctrl.dataProvider().changeAttributeValues({fid : attrib}) Lctrl.commitChanges()
J'ai cherché comment remplacer l'argument 'fid' par un symbole ou une formule qui dirait 'pour tous les objets/id', pour que tous aient la valeur 'Bien joué', mais je n'ai pas trouvé, c'est surement très simple pour certains d'entre vous...
J'avais essayé d'inclure les deux façons de faire dans une boucle mais sans réussir non plus.
un truc du style :
Code:
fid = 15 attrib = {3:"Bien joué"} for objet in Lctrl.dataProvider(): Lctrl.dataProvider.changeAttributeValues({fid : attrib})
D'ailleurs existe -t-il un glossaire des erreurs qui aiguille sur ce qui bloque, ce qu'il faut corriger : "not iterable", has no attribute", etc... ?
Dans le cook book, je n'arrive pas à comprendre la différence entre :
page 24
Code:
if caps & QgsVectorDataProvider.ChangeAttributeValues: attrs = { 0 : "hello", 1 : 123 } layer.dataProvider().changeAttributeValues({ fid : attrs })
et page 25
Code:
layer.changeAttributeValue(fid, fieldIndex, value)
si ce n'est qu'il y a une histoire de undo/redo, mais c'est une autre question...
Je récapitule mes principales questions :
Existe -t-il un 'glossaire des erreurs' qui puisse aiguiller sur ce qui bloque, ce qu'il faut corriger : lors des "not iterable", has no attribute", etc... ?
Y a t il un symbole ou une formule en code python pour dire 'pour tous les objets/id' ou 'sur toutes les lignes' ?
Est ce que quelqu'un pourrait m'indiquer comment corriger la boucle SVP ?
Je vous remercie beaucoup d'avoir lu jusque là, et encore plus si vous avez des pistes ou réponses à certaines questions.
Bien cordialement, F.DUVAL
Hors ligne
#2 Mon 20 January 2020 17:06
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
Re: QGIS 3/Python: Modifier des couches vecteurs
Concernant la boucle, j'ai peut être avancé :
Je n'ai pas de message d'erreur mais rien ne se modifie visuellement/
Code:
valeur = 'Bien joué' for objet in Lctrl.dataProvider().getFeatures(): objet.setAttribute(3, valeur) Lctrl.commitChanges()
sur des sites style stackoverflow, j'ai vu que sur qgis 2. on pouvait devoir faire un 'update' avant le 'commit', mais je ne vois pas l'équivalent sur le cookbook 3.4.
Je pensais que soit on passait pas dataProvider() soit par getFeatures(), je trouve donc bizarre que
Lctrl.dataProvider().getFeatures():
ne génère pas d'erreurs...
J'espère que vous pourrez m'apporter quelques réponses.
Cordialement,
F.DUVAL
Hors ligne
#3 Tue 21 January 2020 15:20
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
Re: QGIS 3/Python: Modifier des couches vecteurs
J'ai trouvé comment itérer l'attribution d'une valeur (ici tout simplement le texte 'Bien joué') dans une colonne(un champ) définie, sur tous les objets d'une couche. J'espère que cela pourra en aider certains.
Le code est surement perfectible mais il fonctionne :
Code:
from qgis.core import * from qgis.utils import * from qgis.gui import * iface=qgis.utils.iface pjt=QgsProject.instance() canvas=iface.mapCanvas() for layer in canvas.layers(): print (layer.name()) #Renvoie : #"Troncon CTRL" #"Listing GEOSAT" #"Listing CTRL" Tctrl = canvas.layers()[0] Lgeosat = canvas.layers()[1] Lctrl = canvas.layers()[2] #les numéros sont, a priori, dans l'ordre du canvas print(Lctrl.name()) #Renvoie : "Listing CTRL" iface.setActiveLayer(Lgeosat) #Listing GEOSAT devient la couche sélectionnée XLctrl=Lctrl.dataProvider().fields()[0] #inutile ici caps = Lctrl.dataProvider().capabilities() #savoir si la couche vecteur est modifiable if caps & QgsVectorDataProvider.DeleteFeatures: print('C\'est possible') else : print('Pas possible') #Renvoie : "C'est possible" Lctrl.startEditing() #ouvrir mode édition de Listing Ctrl # je supprime le champ indexé [3] créé lors des lancements de script précédents if caps & QgsVectorDataProvider.DeleteAttributes: Lctrl.dataProvider().deleteAttributes([3]) # puis je le recrée... cela me permet d'avoir un champ 'Alors' propre (et vide) à coup sûr... Lctrl.dataProvider().addAttributes([QgsField("Alors", QVariant.String)]) AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors") # pour le remplir avec la valeur texte 'bien joué'... valeur = 'Bien joué' for objet in Lctrl.getFeatures(): Lctrl.changeAttributeValue(objet.id(), AlorsLctrl, valeur) Lctrl.commitChanges()#enregistre les modifcations et quitte le mode édition de Listing Ctrl
J'ai remis le code complet mais le plus intéressant c'est à partir de la ligne 50.
Je me suis particulièrement aidé de ce site : https://gis.stackovernet.com/fr/q/65819
Ce n'est qu'un début, donc je reste à l'écoute de tous conseils, remarques et améliorations concernant mon code, python en général et plus particulièrement la modification des couches vecteurs. Si vous avez les réponses aux questions que j'avais posé dans les précédents posts, vous êtes les bienvenus.
J'ai une dernière question qui me vient, quelle est la différence si j'écris :
AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors")
et si j'écris :
AlorsLctrl=Lctrl.dataProvider().fields()[3]
Est ce exactement pareil ? A quelles fonctionnalités et quelles informations donnent accès l'une et l'autre ?
D'avance merci.
F.DUVAL
Dernière modification par F.Duval (Wed 22 January 2020 08:36)
Hors ligne
#4 Tue 21 January 2020 15:21
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
Re: QGIS 3/Python: Modifier des couches vecteurs
Message en double, à supprimer.
Dernière modification par F.Duval (Tue 21 January 2020 15:49)
Hors ligne
#5 Tue 21 January 2020 16:31
Re: QGIS 3/Python: Modifier des couches vecteurs
AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors")
et si j'écris :
AlorsLctrl=Lctrl.dataProvider().fields()[3]
Est ce exactement pareil ? A quelles fonctionnalités et quelles informations donnent accès l'une et l'autre ?
F.DUVAL
C'est la même chose. Dans les deux cas tu instancies un objet de la classe QgsFields.
Bon courage !
S.
Dernière modification par Sylther (Tue 21 January 2020 16:31)
Hors ligne
#6 Wed 22 January 2020 08:47
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
Re: QGIS 3/Python: Modifier des couches vecteurs
Merci Sylther pour votre réponse.
Je reviens sur mon deuxième post :
Je pensais que soit on passait pas dataProvider() soit par getFeatures(), je trouve donc bizarre que
Lctrl.dataProvider().getFeatures():
ne génère pas d'erreurs...
Est ce que Lctrl.dataProvider().getFeatures(): ou for objet in Lctrl.dataProvider().getFeatures(): peut avoir un sens pour obtenir quelque chose ou est ce que dataProvider et getFeatures sont bien des chemins séparés ?
Je me permets également de relancer quelques questions qui n’intéresseront peut être pas que moi :
Existe -t-il un 'glossaire des erreurs' qui puisse aiguiller sur ce qui bloque, ce qu'il faut corriger : lors des "not iterable", has no attribute", etc... ?
+
à ma question
Y a t il un symbole ou une formule en code python pour dire 'pour tous les objets/id' ou 'sur toutes les lignes' ?
La réponse est-elle bien : .id() ? Il n'y en a pas d'autres, pour d'autres situations ?
D'avance merci.
F.DUVAL
Hors ligne
#7 Tue 28 January 2020 09:24
- F.Duval
- Participant occasionnel
- Date d'inscription: 3 Jan 2012
- Messages: 23
Re: QGIS 3/Python: Modifier des couches vecteurs
F.Duval a écrit:AlorsLctrl=Lctrl.dataProvider().fieldNameIndex("Alors")
et si j'écris :
AlorsLctrl=Lctrl.dataProvider().fields()[3]
Est ce exactement pareil ? A quelles fonctionnalités et quelles informations donnent accès l'une et l'autre ?
F.DUVAL
C'est la même chose. Dans les deux cas tu instancies un objet de la classe QgsFields.
Bon courage !
S.
Bonjour,
Etes vous bien sûr que c'est la même chose ? Ce que je ne comprends pas c'est que dans un autre script :
Si j'écris :
Code:
ZcanaLctrl=Lctrl.dataProvider().fields()[5] # pour le remplir avec une valeur ... for objet in Lctrl.getFeatures(): valeur = objet.attributes()[2] - objet.attributes()[4] Lctrl.changeAttributeValue(objet.id(), ZcanaLctrl, valeur)
J'ai ce message : TypeError: QgsVectorLayer.changeAttributeValue(): argument 2 has unexpected type 'QgsField'
Si j'écris :
Code:
ZcanaLctrl=Lctrl.dataProvider().fieldNameIndex("Z cana") # pour le remplir avec une valeur ... for objet in Lctrl.getFeatures(): valeur = objet.attributes()[2] - objet.attributes()[4] Lctrl.changeAttributeValue(objet.id(), ZcanaLctrl, valeur)
Le script fonctionne et le champ est bien rempli par le résultat de la soustraction.
Pourquoi le premier me renvoi un message d'erreur, d'après vous, si les deux définitions de ZcanaLctrl sont sensées être les mêmes ?
Cordialement,
F.DUVAL
Dernière modification par F.Duval (Tue 28 January 2020 09:26)
Hors ligne