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

Rencontres QGIS 2025

L'appel à participation est ouvert jusqu'au 19 janvier 2025!

#1 Thu 20 August 2015 10:58

antakini
Participant occasionnel
Date d'inscription: 5 Sep 2005
Messages: 16

Python : utilisation d'une combobox pour zoomer

Bonjour,
Je viens de passer à la version 10.2 et ne dispose plus de licence VBA.
Je découvre donc le python pour automatiser certaines tâches.

Pour débuter, j'ai voulu reproduire un cas simple que je faisais en vba, à savoir créer une liste déroulante des communes (tables: communes, champs : NOM_COMM) et zoomer sur la commune sélectionnée dans la liste.
J'arrive avec des exemples piochés sur le net à créer ma ComboBox et à l'alimenter avec mes noms de communes. J'ai également créé un bouton permettant de zoomer sur une commune sélectionnée dans ma table.
Je bloque sur le lien entre les deux, à savoir : utiliser la valeur de ma ComboBox pour mon zoom.

ci-dessous mon code :

Code:

import arcpy
import pythonaddins

class ComboBoxClass7(object):
    """Implementation for PythonAddins_addin.combobox (ComboBox)"""
    def __init__(self):
        self.items = ["item1", "item2", "item3", "item4", "item5"]
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'wwwwwwwwwwwwwwwwwww'
        self.width = 'wwwwwwwwwwwwwwww'
    def onSelChange(self, selection):
        pass
    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        if focused:
            self.mxd = arcpy.mapping.MapDocument('current')
            layer1 = arcpy.mapping.ListLayers(self.mxd,"COMMUNES")[0]
            self.items = []
            values = [row[0] for row in arcpy.da.SearchCursor(layer1, ["NOM_COMM"])]  
        for uniqueVal in sorted(set(values)):  
            self.items.append(uniqueVal)
        pass    
    def onClick(self):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

class ZoomToSelectedFeatures(object):
    """Implementation for dlu_PythonAddins_addin.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
   
        """Implementation for dlu_PythonAddins_addin.button (Button)"""
    def onClick(self):
        COMMUNES = "COMMUNES"
        arcpy.SelectLayerByAttribute_management(COMMUNES, "NEW_SELECTION", "\"NOM_COMM\" = 'TOTO'")
        mxd = arcpy.mapping.MapDocument("current")
        df = arcpy.mapping.ListDataFrames(mxd)[0]
        df.zoomToSelectedFeatures()
    pass

Merci par avance pour vos conseils

Hors ligne

 

#2 Thu 20 August 2015 13:09

anne13
Participant actif
Lieu: Villeurbanne
Date d'inscription: 12 May 2009
Messages: 71

Re: Python : utilisation d'une combobox pour zoomer

Bonjour, je ne suis pas experte en python...
Mais peut être en utilisant "global" qui permet de passer une variable d'une class à une autre...


Anne B, Ville de Villeurbanne

Hors ligne

 

#3 Thu 20 August 2015 14:00

antakini
Participant occasionnel
Date d'inscription: 5 Sep 2005
Messages: 16

Re: Python : utilisation d'une combobox pour zoomer

merci pour ta réponse Anne13.

J'avais pensé utiliser une variable globale, mais je ne sais pas trop ce que je dois déclarer.
je pensais à ma variable "values", mais je ne suis pas certain quelle retourne la bonne valeur.
Et je ne sais pas si je peux utiliser ensuite cette variable pour zoomer ainsi :

Code:

arcpy.SelectLayerByAttribute_management(COMMUNES, "NEW_SELECTION", "\"NOM_COMM\" = values")

Je cherche. Ce qui était pratique avec le vba, c'est qu'on avait le débogage pour savoir où se situait l'erreur.

Hors ligne

 

#4 Thu 20 August 2015 14:34

lebon henri
Participant actif
Lieu: Vendée
Date d'inscription: 14 Jan 2008
Messages: 99

Re: Python : utilisation d'une combobox pour zoomer

Bonjour,

Si tu veux utiliser une variable d'une classe A dans une classe B. Il suffit d'écrire dans la classe B : classeA.maVariable.

Par exemple :

Code:

class Premiere:

    toto = "TOTO"


class Deuxieme:

    print Premiere.toto


Deuxieme()

Pour mieux comprendre renseigne toi sur l'usage des classes en python.

Cordialement.

Hors ligne

 

#5 Thu 20 August 2015 16:38

antakini
Participant occasionnel
Date d'inscription: 5 Sep 2005
Messages: 16

Re: Python : utilisation d'une combobox pour zoomer

merci lebon henri
j'envisage bien de me pencher sur les contraintes d'écriture liées à python.

je comprend tout  à fait cette logique d'appel d'une variable d'une classe, dans une autre.
là ou je bloque c'est dans mon code de la combobox, je ne vois pas comment déclarer ma variable qui serait le résultat de mon choix...

Hors ligne

 

#6 Fri 21 August 2015 08:31

anne13
Participant actif
Lieu: Villeurbanne
Date d'inscription: 12 May 2009
Messages: 71

Re: Python : utilisation d'une combobox pour zoomer

Voilà ce que j'ai dans mon code :
dans la 1e combobox
def onSelChange(self, selection):
        global staticVal
        staticVal = selection

dans la 2e combobox
def __init__(self):
       global staticVal
... puis après d'autres lignes de def
def onFocus(self, focused):
....
with arcpy.da.SearchCursor(layer, ["NOM"], where_clause="CATEGORIE = '" + staticVal + "'") as cursor:

Ca me permettait de retrouver StaticVal choisie dans ma 1e combobox et de l'utiliser dans la 2e.
Si ça peut t'aider...


Anne B, Ville de Villeurbanne

Hors ligne

 

#7 Fri 21 August 2015 10:27

lebon henri
Participant actif
Lieu: Vendée
Date d'inscription: 14 Jan 2008
Messages: 99

Re: Python : utilisation d'une combobox pour zoomer

Bonjour, voilà comment faire.

Cordialement.

Code:

import arcpy
import pythonaddins


class LectureCoucheCommune:

    def __init__(self):

        self.listCommune = self.__getCommunesList(r"\\avi-fs-003\Geodatabases\national\ADMINISTRATIF.gdb\COMM")


    def __getCommunesList(self, fc):

        inseeCommunes = []

        with arcpy.da.SearchCursor(fc, "insee") as cursor:

            for row in cursor:

                inseeCommunes.append(row[0])

        return inseeCommunes



class testComboClass(object):
    """Implementation for combo_addin.testCombo (ComboBox)"""
    def __init__(self):

        lc = LectureCoucheCommune()

        self.items = lc.listCommune[0:100]
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWW'
        self.width = 'WWWWWW'


    def onSelChange(self, selection):

        print selection ##ce qui print l'insee de la commune, toi tu n'as qu'à executer ta fonction de zoom grace à la variable selection.

    def onEditChange(self, text):
        pass
    def onFocus(self, focused):
        pass
    def onEnter(self):
        pass
    def refresh(self):
        pass

Hors ligne

 

#8 Mon 31 August 2015 09:29

anne13
Participant actif
Lieu: Villeurbanne
Date d'inscription: 12 May 2009
Messages: 71

Re: Python : utilisation d'une combobox pour zoomer

Bonjour, à mon tour de demander de l'aide. Je suis en train moi aussi de développer avec un script python (addin) un outil pour se localiser (adresse et équipements) avec des combobox.

J'arrive bien à faire fonctionner mon outil, c'est à dire que je sélectionne une voie puis un numéro dans cette voie, mais c'est du "one shot", c'est à dire qu'une fois que l'outil a zoomé sur l'adresse, je n'arrive pas à "réinitialiser" les combobox et refaire fonctionner pour une autre adresse.
J'ai rajouté un bouton pour réinitialiser, mais je sèche pour trouver ce que je dois indiquer dedans. Quelqu'un a t il une idée?
Sinon, autre piste, que ce bouton rajouté serve de "zoom sur l'adresse" qui est indiquée dans mes combobox, et que les combobox continuent à s'actualiser selon les choix souris...

Mon code ci dessous (désolé, il n'est pas commenté!) :

Code:

import arcpy
import pythonaddins

class ButtonClass1(object):
    """Implementation for Localisateur_Adresses_addin.button (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        mxd = arcpy.mapping.MapDocument("CURRENT")  
        df = arcpy.mapping.ListDataFrames(mxd,"*")[0] 
        groupeloclyr = arcpy.mapping.Layer(r"K:\Localisateurs\Localisateurs.lyr")
        groupeloc = 'Localisateurs'
        if arcpy.mapping.ListLayers(mxd, groupeloc) == []:
            arcpy.mapping.AddLayer(df, groupeloclyr, "BOTTOM")
            arcpy.RefreshTOC()
            pythonaddins.MessageBox('Vous pouvez utiliser le localisateur.', 'Information', 0)
        else:
            pythonaddins.MessageBox('Vous pouvez utiliser le localisateur.', 'Information', 0)

class ButtonClass2(object):
    """Implementation for Localisateur_Adresses_addin.button_1 (Button)"""
    def __init__(self):
        self.enabled = True
        self.checked = False
    def onClick(self):
        pythonaddins.MessageBox('Vous pouvez faire une nouvelle recherche.', 'Information', 0)

class ComboBoxClass3(object):
    """Implementation for Localisateur_Adresses_addin.combobox (ComboBox)"""
    def __init__(self):
        self.items = []
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWWWWWWWWWW'
        self.width = 'WWWWWWWWWWWW'
    def onSelChange(self, selection):
        global staticVal
        staticVal = selection
    def onFocus(self, focused):
        self.mxd = arcpy.mapping.MapDocument('current')
        layer = arcpy.mapping.ListLayers(self.mxd, "Localisateur_Voies")[0]
        self.items = []
        with arcpy.da.SearchCursor(layer, ["NOM"]) as cursor:
            for row in cursor:
                self.items.append(row[0])
        self.items = sorted(set(self.items))
    def onEnter(self):
        arcpy.SelectLayerByAttribute_management("Localisateur_Voies", "NEW_SELECTION", "NOM = '" + staticVal + "'")
        pmxd = arcpy.mapping.MapDocument('current')
        pdf = arcpy.mapping.ListDataFrames(pmxd)[0]
        pdf.zoomToSelectedFeatures()

class ComboBoxClass4(object):
    """Implementation for Localisateur_Adresses_addin.combobox_1 (ComboBox)"""
    def __init__(self):
        self.items = []
        self.editable = True
        self.enabled = True
        self.dropdownWidth = 'WWWWWWWWWWWWWW'
        self.width = 'WWWWWWWWWWWW'
    def onSelChange(self, selections):
        global intermediaire
        intermediaire = selections
        arcpy.SelectLayerByAttribute_management("LocalisateurNumeros", "NEW_SELECTION", "NUMERO = '" + intermediaire + "' AND NOMMINUSCULE = '" + staticVal + "'")
        pmxd2 = arcpy.mapping.MapDocument('current')
        pdf2 = arcpy.mapping.ListDataFrames(pmxd2)[0]
        pdf2.zoomToSelectedFeatures()
    def onFocus(self, focused):
        # print(staticVal)
        self.mxd = arcpy.mapping.MapDocument('current')
        layer2 = arcpy.mapping.ListLayers(self.mxd, "LocalisateurNumeros")[0]
        self.items = []
        with arcpy.da.SearchCursor(layer2, ["NUMERO"], where_clause="NOMMINUSCULE = '" + staticVal + "'") as cursor:
            for row in cursor:
                self.items.append(row[0])
        self.items = sorted(set(self.items))

Dernière modification par anne13 (Mon 31 August 2015 09:30)


Anne B, Ville de Villeurbanne

Hors ligne

 

#9 Mon 31 August 2015 12:47

lebon henri
Participant actif
Lieu: Vendée
Date d'inscription: 14 Jan 2008
Messages: 99

Re: Python : utilisation d'une combobox pour zoomer

Bonjour,

j'ai regardé un peu votre code. D'après ce que je comprend, voici quelques pistes et questions.

Du coup, vous utilisez 2 couches (Localisateur_Voies, LocalisateurNumeros). Pourquoi ? Il s'agit bien d'une couche Polyligne et d'une ponctuelle ? La couche numéros n'est pas toujours complète ?

Vous avez peut être intérêt à réorganiser vos classes.

1)Une classe pour la lecture des données.
2)Une classe combobox pour le numéros. L'utilisateur rentre le numéros qu'il veut, il faudrait ensuite que le script cherche le plus proche.
3)Une classe pour combobox pour la rue. C'est peut être envisageable de rafraîchir la liste de la combobox au fur et à mesure que l'utilisateur tape des lettres grâce à une query avec une clause de type like 'lettresTapées%'
4)Une classe pour les zooms correspondant au bouton ou une classe avec la méthode du zoom, qui serais appelée par une fonction onEnter pour la classe 2 et 3.

Vous pouvez vous affranchir de la présence des couches en questions dans votre mxd en jouant sur l'extent du dataframe directement. Ca éviterais de devoir faire un addlayer.

On en rediscute.

Cordialement.

Hors ligne

 

#10 Mon 31 August 2015 14:10

anne13
Participant actif
Lieu: Villeurbanne
Date d'inscription: 12 May 2009
Messages: 71

Re: Python : utilisation d'une combobox pour zoomer

Merci pour votre réponse rapide!

J'ai effectivement une couche de polylignes pour les voies et une couche de ponctuels pour les num. Les scripts permettent de faire un zoom sur une rue complète (onEnter dans la ComboBoxClass3), utilisé pour certaines rues qui n'ont pas encore de numéro (nouvelle rue) et certaines places, bretelles ou autres.

Je pense que je vais conserver mon appel couches dans mon mxd car ça permet d'avoir en sélection les objets zoomés... mais je vais y réfléchir.

Je regarde toutes les pistes que vous m'avez indiqué et je reviens vers vous.


Anne B, Ville de Villeurbanne

Hors ligne

 

#11 Thu 03 September 2015 12:35

anne13
Participant actif
Lieu: Villeurbanne
Date d'inscription: 12 May 2009
Messages: 71

Re: Python : utilisation d'une combobox pour zoomer

Bonjour, je viens vous tenir au courant de l'avancée de mes modifications.

1)Une classe pour la lecture des données.


CHECK!

2)Une classe combobox pour le numéros. L'utilisateur rentre le numéros qu'il veut, il faudrait ensuite que le script cherche le plus proche.


Combobox CHECK, par contre, je vais discuter avec mes utilisateurs de l'interet de saisir directement dans la box et de chercher un numéro proche... Pas une priorité!
J'ai aussi à régler le problème du tri de la liste de numéros (ex 1, 11, 2, 25, 257, 3, 3bis, etc...) impossible à convertir en int car j'ai les bis/ter/quater à gérer. J'ai une solution de contournement, créer un nouveau champ avec (001, 011, 002, 025, 257, 003, 003bis, etc...), mais ce n'est pas très beau et ça pose le problème de la mise à jour de ce champ quand une nouvelle adresse est créée... Ou créer une liste de tous les num que je peux rencontrer dans l'ordre qui m'interresse et faire trier selon cette liste... C'est un peu long en code (mes num vont jusqu'à 451...) mais efficace et invisible pour l'utilisateur.
Avez vous d'autres idées???

3)Une classe pour combobox pour la rue. C'est peut être envisageable de rafraîchir la liste de la combobox au fur et à mesure que l'utilisateur tape des lettres grâce à une query avec une clause de type like 'lettresTapées%'


Y'a plus qu'à! Ca serait un vrai plus car ma liste de rues est très longue. Je vais essayer de faire ça, si je patauge, je lancerai un nouveau help...

4)Une classe pour les zooms correspondant au bouton ou une classe avec la méthode du zoom, qui serais appelée par une fonction onEnter pour la classe 2 et 3.


Y'a plus qu'à! Du coup, ca permettrait de l'appeler soit par onEnter dans la combobox, soit par le bouton Zoom, je pense qu'au niveau ergonomie, c'est le top! J'y avais pas pensé...

J'ai réussi à utiliser l'extent pour zoomer, ce qui m'affranchit de la sélection du numéro sur lequel zoomer, et du coup ça permet à mes 2 combobox de fonctionner même après le zoom (ce qui était un de mes plus gros problèmes!), et je me pose toujours la question de garder ou pas mon addLayer... à voir avec mes utilisateurs aussi.

Il me reste à rajouter un message quand la combobox de recherche de numéro est activée et que la voie n'a pas de numéro... avec un choix oui/non pour zoomer sur la voie...

En tout cas, merci Henri Lebon pour toutes ces très bonnes idées. Des fois, un petit coup de pouce fait bien avancer.
Je mettrai le code quand il sera amélioré...

Anne


Anne B, Ville de Villeurbanne

Hors ligne

 

#12 Thu 03 September 2015 18:10

lebon henri
Participant actif
Lieu: Vendée
Date d'inscription: 14 Jan 2008
Messages: 99

Re: Python : utilisation d'une combobox pour zoomer

Bonjour,

Content que ça avance. En attendant de voir le résultat final, voici une fonction permettant un "tri" logique pour les numéros de rue.

Code:

def logical_sort(targetList):

    import re

    def __atoi(text):
        return int(text) if text.isdigit() else text

    def __natural_keys(text):
        return [ __atoi(c) for c in re.split('(\d+)', text) ]

    targetList.sort(key=__natural_keys)

    return targetList

Cordialement.

Hors ligne

 

#13 Fri 04 September 2015 10:10

lebon henri
Participant actif
Lieu: Vendée
Date d'inscription: 14 Jan 2008
Messages: 99

Re: Python : utilisation d'une combobox pour zoomer

Bonjour,

Voici une modeste façon de faire l'autocompletion. Il y a des bibliothèques pour le faire mieux en python mais ça peu peut être suffire. L'utilisateur tape un mot et appuie sur la flèche de menu déroulant à nouveau et la liste se rafraîchi avec les mot contenant la chaîne de caractère saisie. 

   

Code:

def onEditChange(self, text):

        shortList = [item for item in maListeDeValeur if text.lower().strip().rstrip() in item.lower().strip().rstrip()]

        if shortList != []:

            self.items = shortList[0:50]

    def onFocus(self, focused):
        self.refresh()

Hors ligne

 

Pied de page des forums

Powered by FluxBB