#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