Topic: script auto numerotation des bornes
Script pour l'auto numérotation des bornes
You are not logged in. Please login or register.
QElectroTech → Scripts → script auto numerotation des bornes
Script pour l'auto numérotation des bornes
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Programme pour autonuméroter les bornes dans les fichiers QElectroTech (.qet)
Version finale qui fonctionne correctement
"""
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import xml.etree.ElementTree as ET
import os
from collections import OrderedDict
class QETBorneNumbering:
def __init__(self, root):
self.root = root
self.root.title("Autonumérotation des bornes QElectroTech")
self.root.geometry("800x600")
# Variables
self.file_path = None
self.xml_root = None
self.bornes_trouvees = []
self.borne_selectionnee = None
self.use_wire_num = tk.BooleanVar(value=False)
self.create_widgets()
def create_widgets(self):
"""Crée l'interface graphique"""
# Frame principale
main_frame = ttk.Frame(self.root, padding="10")
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# Configuration du poids pour le redimensionnement
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=1)
main_frame.rowconfigure(3, weight=1)
# Bouton pour ouvrir le fichier
btn_open = ttk.Button(main_frame, text="Ouvrir un fichier .qet", command=self.open_file)
btn_open.grid(row=0, column=0, columnspan=2, pady=10, sticky=tk.W+tk.E)
# Label pour afficher le fichier ouvert
self.label_file = ttk.Label(main_frame, text="Aucun fichier sélectionné", foreground="gray")
self.label_file.grid(row=1, column=0, columnspan=2, sticky=tk.W)
# Frame pour les bornes trouvées
frame_bornes = ttk.LabelFrame(main_frame, text="Bornes trouvées", padding="5")
frame_bornes.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=10)
frame_bornes.columnconfigure(0, weight=1)
# Listbox pour les bornes
self.listbox_bornes = tk.Listbox(frame_bornes, height=6)
self.listbox_bornes.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
self.listbox_bornes.bind('<<ListboxSelect>>', self.on_borne_select)
# Scrollbar pour la listbox
scrollbar = ttk.Scrollbar(frame_bornes, orient="vertical", command=self.listbox_bornes.yview)
scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
self.listbox_bornes.configure(yscrollcommand=scrollbar.set)
# Frame pour les boutons d'action
frame_actions = ttk.LabelFrame(main_frame, text="Actions", padding="5")
frame_actions.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=10)
frame_actions.columnconfigure(0, weight=1)
# Bouton d'autonumérotation
self.btn_numeroter = ttk.Button(frame_actions, text="Numéroter les bornes sélectionnées",
command=self.numeroter_bornes, state="disabled")
self.btn_numeroter.grid(row=0, column=0, pady=5, sticky=tk.W+tk.E)
# Checkbox pour le mode de numérotation (Numéro de fil)
self.chk_wire = ttk.Checkbutton(frame_actions, text="Utiliser n° fil (Label_Fil)",
variable=self.use_wire_num)
self.chk_wire.grid(row=1, column=0, pady=5, sticky=tk.W)
# Bouton pour sauvegarder
self.btn_save = ttk.Button(frame_actions, text="Sauvegarder le fichier",
command=self.save_file, state="disabled")
self.btn_save.grid(row=1, column=0, pady=5, sticky=tk.W+tk.E)
self.btn_save.grid(row=2, column=0, pady=5, sticky=tk.W+tk.E)
# Zone de statut
self.status_var = tk.StringVar(value="Prêt")
status_bar = ttk.Label(main_frame, textvariable=self.status_var, relief=tk.SUNKEN)
status_bar.grid(row=4, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(10, 0))
def open_file(self):
"""Ouvre un fichier .qet et analyse les bornes"""
file_path = filedialog.askopenfilename(
title="Sélectionner un fichier QElectroTech",
filetypes=[("Fichiers QElectroTech", "*.qet"), ("Tous les fichiers", "*.*")]
)
if file_path:
try:
self.file_path = file_path
self.label_file.config(text=f"Fichier: {os.path.basename(file_path)}")
# Lecture et analyse du fichier XML
self.analyze_file()
# Réinitialiser l'état de modification pour le nouveau fichier
if hasattr(self, '_already_modified'):
del self._already_modified
self.status_var.set(f"Fichier chargé: {len(self.bornes_trouvees)} bornes trouvées")
except Exception as e:
messagebox.showerror("Erreur", f"Impossible d'ouvrir le fichier:\n{str(e)}")
self.status_var.set("Erreur lors de l'ouverture du fichier")
def analyze_file(self):
"""Analyse le fichier XML pour trouver les bornes"""
try:
# Parser le fichier XML
tree = ET.parse(self.file_path)
self.xml_root = tree.getroot()
# Extraire les bornes
self.bornes_trouvees = self.extract_bornes()
# Mettre à jour la listbox (sans doublons)
self.update_listbox()
except ET.ParseError as e:
raise Exception(f"Erreur de parsing XML: {str(e)}")
except Exception as e:
raise Exception(f"Erreur lors de l'analyse: {str(e)}")
def extract_bornes(self):
"""Extrait toutes les bornes du fichier XML"""
bornes = []
# Parcourir tous les diagrammes pour trouver les folios
for diagram in self.xml_root.iter('diagram'):
folio = diagram.get('folio', '1')
# Parcourir les éléments dans ce diagramme
for element in diagram.iter('element'):
x = float(element.get('x', '0'))
y = float(element.get('y', '0'))
# Vérifier si c'est une borne (commence par X)
for child in element:
if child.tag == 'elementInformations':
for info in child:
if info.tag == 'elementInformation':
name = info.get('name', '')
if name == 'label':
text = info.text
if text and text.upper().startswith('X'):
bornes.append({
'element': element,
'label': text,
'position': (x, y),
'folio': folio
})
break
return bornes
def get_borne_base(self, label):
"""Extrait la base du label (tronque après ':')"""
if ':' in label:
return label.split(':')[0]
return label
def update_listbox(self):
"""Met à jour la listbox avec les bornes uniques"""
self.listbox_bornes.delete(0, tk.END)
# Extraire les bases de labels uniques (tronquer avant ':')
bases_uniques = list(OrderedDict.fromkeys([self.get_borne_base(b['label']) for b in self.bornes_trouvees]))
for label in sorted(bases_uniques):
self.listbox_bornes.insert(tk.END, label)
# Activer le bouton de numérotation si des bornes sont trouvées
if bases_uniques:
self.btn_numeroter.config(state="normal")
else:
self.btn_numeroter.config(state="disabled")
def on_borne_select(self, event):
"""Gère la sélection d'une borne dans la listbox"""
selection = self.listbox_bornes.curselection()
if selection:
index = selection[0]
self.borne_selectionnee = self.listbox_bornes.get(index)
self.status_var.set(f"Borne sélectionnée: {self.borne_selectionnee}")
def get_connected_wire_text(self, diagram, element_id):
"""Trouve le texte du conducteur connecté à un élément donné"""
if not element_id:
return ""
# Chercher dans les conducteurs du diagramme
for conductor in diagram.iter('conductor'):
# Vérification directe des attributs element1 et element2 (Format QET standard)
# Le numéro de fil est dans l'attribut 'num'
if conductor.get('element1') == element_id or conductor.get('element2') == element_id:
text = conductor.get('num', '')
if text and text.strip() and text.strip() != "_":
return text.strip()
# Compatibilité avec d'autres formats potentiels (points de connexion)
for point in conductor.iter('point'):
if point.get('element') == element_id:
# Vérifier l'attribut text ou num
text = conductor.get('text', '')
if not text:
text = conductor.get('num', '')
if text and text.strip() and text.strip() != "_":
return text.strip()
return ""
def numeroter_bornes(self):
"""Numérote les bornes sélectionnées dans leur ordre d'apparition"""
if not self.borne_selectionnee:
messagebox.showwarning("Avertissement", "Veuillez sélectionner une borne à numéroter")
return
try:
# Si c'est la première numérotation, recharger le fichier XML
if not hasattr(self, '_already_modified'):
tree = ET.parse(self.file_path)
self.xml_root = tree.getroot()
self._already_modified = True
# Sinon, utiliser l'arbre XML déjà modifié
# Filtrer les bornes correspondant à la base sélectionnée
bornes_a_numeroter = []
for diagram in self.xml_root.iter('diagram'):
folio = diagram.get('folio', '1')
for element in diagram.iter('element'):
x = float(element.get('x', '0'))
y = float(element.get('y', '0'))
for child in element:
if child.tag == 'elementInformations':
for info in child:
if info.tag == 'elementInformation':
name = info.get('name', '')
if name == 'label':
text = info.text
if text and self.get_borne_base(text) == self.borne_selectionnee:
bornes_a_numeroter.append({
'element': element,
'label': text,
'position': (x, y),
'folio': folio,
'diagram': diagram # Nécessaire pour trouver les fils
})
break
if not bornes_a_numeroter:
messagebox.showwarning("Avertissement", "Aucune borne trouvée pour ce label")
return
def natural_sort_key(s):
"""Fonction utilitaire pour trier les folios numériquement si possible"""
import re
return [int(text) if text.isdigit() else text.lower()
for text in re.split('([0-9]+)', s)]
# Trier par folio puis par position (gauche à droite)
# On utilise natural_sort_key pour le folio pour éviter que "10" soit avant "2"
bornes_triees = sorted(bornes_a_numeroter,
key=lambda x: (natural_sort_key(x['folio']), x['position'][0], x['position'][1]))
# Numéroter les bornes avec format X1:00, X1:01, etc.
for i, borne in enumerate(bornes_triees):
num_seq = i + 1
if self.use_wire_num.get():
# Mode concaténation : X1:1_102 (Bornier:Index_Fil)
# QET utilise l'attribut 'uuid' pour l'identifiant unique
element_id = borne['element'].get('uuid')
if not element_id:
element_id = borne['element'].get('id')
wire_text = self.get_connected_wire_text(borne['diagram'], element_id)
suffixe = wire_text if wire_text else "?"
nouveau_label = f"{self.borne_selectionnee}:{num_seq}_{suffixe}"
else:
# Mode séquentiel classique
nouveau_label = f"{self.borne_selectionnee}:{num_seq}"
# Modifier elementInformation
for child in borne['element']:
if child.tag == 'elementInformations':
for info in child:
if info.tag == 'elementInformation':
name = info.get('name', '')
if name == 'label':
info.text = nouveau_label
# Modifier dynamic_text
elif child.tag == 'dynamic_texts':
for dynamic_text in child:
if dynamic_text.tag == 'dynamic_elmt_text':
for sub_child in dynamic_text:
if sub_child.tag == 'info_name' and sub_child.text == 'label':
for text_elem in dynamic_text:
if text_elem.tag == 'text':
text_elem.text = nouveau_label
self.status_var.set(f"{len(bornes_triees)} bornes {self.borne_selectionnee} numérotées avec succès")
self.btn_save.config(state="normal")
messagebox.showinfo("Succès", f"{len(bornes_triees)} bornes {self.borne_selectionnee} ont été numérotées")
except Exception as e:
messagebox.showerror("Erreur", f"Erreur lors de la numérotation:\n{str(e)}")
def save_file(self):
"""Sauvegarde le fichier modifié"""
if not self.xml_root:
messagebox.showwarning("Avertissement", "Aucun fichier à sauvegarder")
return
try:
# Demander où sauvegarder
save_path = filedialog.asksaveasfilename(
title="Sauvegarder le fichier",
defaultextension=".qet",
filetypes=[("Fichiers QElectroTech", "*.qet"), ("Tous les fichiers", "*.*")],
initialfile=os.path.basename(self.file_path)
)
if save_path:
# Créer une copie de l'arbre XML pour la sauvegarde
tree = ET.ElementTree(self.xml_root)
# Configuration pour un beau formatage
ET.indent(tree, space=" ", level=0)
# Sauvegarder le fichier
tree.write(save_path, encoding='utf-8', xml_declaration=True)
self.status_var.set(f"Fichier sauvegardé: {os.path.basename(save_path)}")
messagebox.showinfo("Succès", "Fichier sauvegardé avec succès")
except Exception as e:
messagebox.showerror("Erreur", f"Erreur lors de la sauvegarde:\n{str(e)}")
def main():
"""Fonction principale"""
root = tk.Tk()
app = QETBorneNumbering(root)
root.mainloop()
if __name__ == "__main__":
main()
QElectroTech → Scripts → script auto numerotation des bornes
Powered by PunBB, supported by Informer Technologies, Inc.
Generated in 0.032 seconds (28% PHP - 72% DB) with 11 queries