Skip to content

Commit

Permalink
Update memo Python
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Nov 25, 2024
1 parent f9968c2 commit 36ead19
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 54 deletions.
16 changes: 12 additions & 4 deletions docs/fonctions-scripts.md
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,14 @@ with edit(layer):
Nous allons avoir besoin de plusieurs classes dans l'API QGIS :

* `QgsProject` : [PyQGIS](https://qgis.org/pyqgis/master/core/QgsProject.html) / [CPP](https://api.qgis.org/api/classQgsProject.html)
* `QgsVectorLayer` : [PyQGIS](https://qgis.org/pyqgis/master/core/QgsVectorLayer.html) / [CPP](https://api.qgis.org/api/classQgsVectorLayer.html)
* Enregistrer un fichier avec `QgsVectorFileWriter` : [PyQGIS](https://qgis.org/pyqgis/master/core/QgsVectorFileWriter.html) / [CPP](https://api.qgis.org/api/classQgsVectorFileWriter.html)
* Un champ : `QgsField` ([PyQGIS](https://qgis.org/pyqgis/master/core/QgsField.html) / [CPP](https://api.qgis.org/api/classQgsField.html)),
* Un champ dans une couche vecteur : `QgsField` ([PyQGIS](https://qgis.org/pyqgis/master/core/QgsField.html) / [CPP](https://api.qgis.org/api/classQgsField.html)),
attention à ne pas confondre avec `QgsFields` ([PyQGIS](https://qgis.org/pyqgis/master/core/QgsFields.html) / [CPP](https://api.qgis.org/api/classQgsFields.html))
qui lui représente un ensemble de champs.
* Une entité `QgsFeature` [PyQGIS](https://qgis.org/pyqgis/master/core/QgsFeature.html) / [CPP](https://api.qgis.org/api/classQgsFeature.html)
* Pour le type de géométrie : Utiliser `QgsVectorLayer::geometryType()` et également la méthode `QgsWkbTypes::geometryDisplayString()` pour sa conversion en chaîne "lisible"
* Pour le type de géométrie : Utiliser `QgsVectorLayer` `geometryType()` et également la méthode `QgsWkbTypes.geometryDisplayString()` pour sa conversion en chaîne "lisible"
* [PyQGIS](https://qgis.org/pyqgis/master/core/QgsWkbTypes.html) / [CPP](https://api.qgis.org/api/classQgsWkbTypes.html)

Pour le type de champ, on va avoir besoin de l'API Qt également :

Expand All @@ -396,8 +398,14 @@ Il va y avoir plusieurs étapes dans ce script :
1. Enregistrer en CSV la couche mémoire

!!! tip
Pour déboguer, on peut afficher la couche mémoire en question avec `QgsProject.instance().addMapLayer()`

Pour déboguer, on peut afficher la couche mémoire en question avec `QgsProject.instance().addMapLayer(layer_info)`


# QgsvectorLayer pyqgis ne cntient pas Addfeature

AJouter indice QgsWkbTypês
V4 fileName

### Solution

```python
Expand Down
83 changes: 46 additions & 37 deletions docs/memo-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,16 @@
* Grosse communauté
* De nombreux packages disponibles sur internet sur [PyPi.org](https://pypi.org/)

Exemple d'un code qui déclare une variable et compare si sa valeur est supérieur à 5 afin d'afficher un message :

```python
# Déclaration d'une variable de type entier
x = 5
x = 10

# Déclaration d'une variable chaîne de caractère
info = 'X est compris entre 0 et 10'
info = 'X est supérieur à 5'

if 0 < x < 10:
if x > 5:
print(info)
```

Expand All @@ -40,6 +42,7 @@ if 0 < x < 10:
* Python 3.7 minimum for QGIS 3.20
* Python 3.9 minimum for QGIS 3.40
* Version de Python souvent supérieure à la version minimum, sauf sur MacOS… 😑
* [Python release cycle](https://devguide.python.org/versions/#versions)

## Rappel de base sur Python

Expand Down Expand Up @@ -67,29 +70,36 @@ objet...
Il y a un faible typage des variables, c'est-à-dire qu'une variable peut changer de type au cours de l'exécution
du programme.

```python
# Pour créer une variable, on déclare juste le nom de la variable ainsi que sa valeur :
compteur = 0
```

Nous allons par la suite utiliser `type(variable` pour vérifier le **type** de la variable.

```python
mon_compteur = 0
type(mon_compteur)
<class 'int'>

mon_compteur = False
type(mon_compteur)
est_valide = False
type(est_valide)
<class 'bool'>

mon_compteur = 'oui'
type(mon_compteur)
nom_couche = 'oui'
type(nom_couche)
<class 'str'>

mon_compteur = "non"
type(mon_compteur)
nom_couche = "non"
type(nom_couche)
<class 'str'>

mon_compteur = 3.5
type(mon_compteur)
densite = 3.5
type(densite)
<class 'float'>

mon_compteur = None
type(mon_compteur)
unknown = None
type(unknown)
<class 'NoneType'>

```
Expand All @@ -102,21 +112,26 @@ Il existe quatre types de structure de données :

* les listes (modifiables)
```python
# Créer une liste vide
nombres = []
type(nombres)
<class 'list'>

nombres.append(1)
nombres.extend([2, 3, 4])
nombres
[1, 2, 3, 4]
# Créer une liste avec des éléments à l'intérieur
mois = ['janvier', 'février', 'mars']

# Ajouter un élément
mois.append('avril')
# Ajouter une autre liste
mois.extend(['mai', 'juin'])

# Nombre de mois
len(mois)

# Autre exemple
mois = ['janvier', 'février', 'mars', 'avril']
# On peut accéder à un élément avec un "index" à l'aide de []
mois[2]
mars

# Attention à l'index maximum
mois[12]
Traceback (most recent call last):
File "/usr/lib/python3.7/code.py", line 90, in runcode
Expand All @@ -128,18 +143,13 @@ IndexError: tuple index out of range
* les tuples (non modifiables)

```python
liste_vide = ()
liste = (1 , 2, 3, 'bonjour')
liste = ('oui', 'non')
type(liste)
<class 'tuple'>
len(liste)
4
2
liste[0]
1
liste[0:2]
(1, 2)
liste[2:]
(3, 'bonjour')

liste[5]
Traceback (most recent call last):
File "/usr/lib/python3.7/code.py", line 90, in runcode
Expand All @@ -152,18 +162,17 @@ IndexError: tuple index out of range
* les dictionnaires

*Attention*, les dictionnaires ne sont pas ordonnés, de façon native, même depuis Python 3.9.
Si vraiment, il y a besoin, il existe une classe `OrderedDict`, mais ce n'est pas une structure de données
native dans Python.
C'est un objet.
Si vraiment, il y a besoin, il existe une classe [OrderedDict](https://docs.python.org/3/library/collections.html),
mais ce n'est pas une structure de données native dans Python.
C'est un objet qu'il faut importer.

```python
personne = {}
type(personne)
commune = {}
type(commune)
# <class 'dict'>
personne['prenom'] = 'etienne'
personne['nom'] = 'trimaille'
personne['est_majeur'] = True
personne['age'] = 35
commune['nom'] = 'Besançon'
commune['code_insee'] = 25056
commune['est_prefecture'] = True
```

## Les commentaires
Expand Down
67 changes: 56 additions & 11 deletions docs/selection-parcours-entites.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ layer.invertSelection()
layer.removeSelection()
```

Le raccourci `iface.activeLayer()` est très pratique, mais de temps en temps on a besoin de **plusieurs** couches qui
Le raccourci `iface.activeLayer()` est très pratique, mais de temps en temps, on a besoin de **plusieurs** couches qui
sont déjà dans la légende. Il existe dans `QgsProject` plusieurs méthodes pour récupérer des couches dans la légende :

```python
Expand Down Expand Up @@ -217,7 +217,7 @@ Dans le langage informatique, une exception peut-être :
* levée ("raise" en anglais) pour déclencher une erreur
* attrapée ("catch" en anglais, ou plutôt "except" en Python) pour traiter l'erreur

Essayons dans la console de faire une opération 10 / 2 :
Essayons dans la **console** de faire une opération 10 / 2 :

```python
10 / 2
Expand All @@ -228,31 +228,76 @@ Essayons cette fois-ci 10 / 0, ce qui est mathématiquement impossible :
10 / 0
```

Passons cette fois-ci dans un **script** pour que cela soit plus simple, et voir que le script s'arrête brutalement 😉

```python
print('Début')
print(10 / 0)
print('Fin')
```

On peut "attraper" cette erreur Python à l'aide d'un `try ... except...` :

```python
print('Début')
try:
10 / 2
print(10 / 2)
except ZeroDivisionError:
print('Ceci est une division par zéro !')
print('Fin')
```

Le `try` permet d'essayer le code qui suit. Le `except` permet d'attraper en filtrant s'il y a des exceptions
et de traiter l'erreur si besoin.

!!! tip
On peut avoir une ou plusieurs lignes de code dans chacun de ces blocs. On peut appeler des fonctions, etc.

### Une exception remonte le fil d'exécution du programme

**Important**, une exception **remonte** tant qu'elle n'est pas **attrapée** :

```python
def function_3():
print("Début fonction 3")
a = 10
b = 0
print(f"{a} / {b} = {a/b}")
print("Fin fonction 3")

def function_2():
print("Début fonction 2")
function_3()
print("Fin fonction 2")

def function_1():
print("Début fonction 1")
function_2()
print("Fin fonction 1")

function_1()
```

On voit que Python, quand il peut, nous indique la "stacktrace" ou encore "traceback",
c'est-à-dire une sorte de fil d'ariane.

### Héritage des exceptions

Toutes les exceptions héritent de `Exception` donc le code ci-dessous fonctionne, mais n'est pas
recommandé, car il masque d'autres erreurs :

```python
try:
10 / 2
print(10 / 2)
except Exception:
print('Erreur inconnue')
```

On peut par contre "enchaîner" les exceptions, afin de filtrer progressivement les exceptions.

```python
try:
10 / 0
print(10 / 0)
except ZeroDivisionError:
print('Erreur, division par 0')
except Exception:
Expand All @@ -267,23 +312,23 @@ dans la `QgsMessageBar` de QGIS, sans tenir compte de la division par zéro :

```python
def diviser(a: int, b: int):
""" Divise 2 nombres et affiche le résultat dans la message bar de QGIS. """
""" Divise 2 nombres et affiche le résultat dans la "message bar" de QGIS. """
result = a / b
iface.messageBar().pushMessage('Résulat', f'{a} / {b} = {result}', Qgis.Success)
iface.messageBar().pushMessage('Résultat', f'{a} / {b} = {result}', Qgis.Success)

diviser(10, 0)
```

En tenant compte d'une possible erreur lors de l'opération mathématique :

```python
def diviser(a, b):
def diviser(a: int, b: int):
try:
result = a / b
except ZeroDivisionError:
iface.messageBar().pushMessage('Division par 0', f'{a} / {b} est impossible', Qgis.Warning)
else:
iface.messageBar().pushMessage('Résulat', f'{a} / {b} = {result}', Qgis.Success)
iface.messageBar().pushMessage('Résultat', f'{a} / {b} = {result}', Qgis.Success)

diviser(10, 2)
```
Expand All @@ -306,7 +351,7 @@ Correction possible de l'exercice :
```python
layer = iface.activeLayer()
request = QgsFeatureRequest()
# request.setFilterExpression('to_int( "POPUL" ) < 1000')
# request.setLimit(5) # Pour aller plus vite si-besoin
request.addOrderBy('NOM')
request.setSubsetOfAttributes(['NOM', 'POPUL'], layer.fields())
for feature in layer.getFeatures(request):
Expand All @@ -333,7 +378,7 @@ if 'densite' not in layer.fields().names():
index = layer.fields().indexFromName('densite')
layer.startEditing()
request = QgsFeatureRequest()
# request.setFilterExpression('to_int( "POPUL" ) > 10000')
# request.setLimit(5) # Pour aller plus vite si-besoin
request.addOrderBy('NOM_COM')
request.setSubsetOfAttributes(['NOM_COM', 'POPUL'], layer.fields())
for feature in layer.getFeatures(request):
Expand Down
4 changes: 2 additions & 2 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ nav:
- Sélection & Parcours: selection-parcours-entites.md
- Utilisation simple:
- Action: action.md
- Formulaire: formulaire.md
- Expression: expression.md
- Formulaire: formulaire.md
- Utilisation avancé:
- Script Processing: script-processing.md
- Extension Générique: extension-generique.md
Expand All @@ -31,10 +31,10 @@ nav:
- Application standalone: standalone.md
- Sujet PyGIS thématique:
- PostGIS: postgis.md
- Migration majeur: migration-majeure.md
- Outils annexes:
- Déploiement d'une extension: extension-deploiement.md
- IDE & Git: ide-git.md
- Migration majeur: migration-majeure.md

plugins:
- search
Expand Down

0 comments on commit 36ead19

Please sign in to comment.