Nous utilisons des cookies pour vous garantir la meilleure expérience sur notre site. Si vous continuez à utiliser ce dernier, nous considèrerons que vous acceptez l'utilisation des cookies. J'ai compris ! ou En savoir plus !.
banniere

Le portail francophone de la géomatique


Toujours pas inscrit ? Mot de passe oublié ?
Nom d'utilisateur    Mot de passe              Toujours pas inscrit ?   Mot de passe oublié ?

Annonce

Printemps des cartes 2024

#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

Sylther
Participant actif
Lieu: Mtp
Date d'inscription: 17 May 2016
Messages: 144

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.

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 :

F.Duval a écrit:

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

Sylther a écrit:
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

 

Pied de page des forums

Powered by FluxBB