Ni

plc-user wrote:

Hello everyone!

What are we actually talking about here?
Does everyone who wants to use the plugin have to compile it themselves, or is it still possible, as before, to put a few binaries for Windows/Linux/Apple on GitHub, for example, that can be downloaded?
I don't want to go to the trouble of compiling it myself for my systems...!

Hello plc-user
On windows you can use
pip install qet-tb-generator-xd

pip uninstall qet-tb-generator-xd

I don't try it on Linux but on windows it's ok

scorpio810 wrote:

Argh

pip install customtkinter Pillow

Hum no Debian packages for customtkinter....
https://packages.debian.org/fr/source/sid/pillow



Curtom tkinter sous debian


Utiliser CustomTkinter sur Debian est une excellente idée si vous voulez donner un coup de jeune aux interfaces Python classiques (le look Windows 95, ça va un moment).

C'est assez simple, mais Debian étant ce qu'il est (très stable mais parfois strict sur les dépendances), il y a deux ou trois petites choses à savoir pour éviter les erreurs "ModuleNotFoundError" ou les problèmes d'affichage.

1. Prérequis Système
Avant d'installer la bibliothèque Python, assurez-vous que tkinter lui-même est présent sur votre système. Debian ne l'installe pas toujours par défaut avec Python.

Ouvrez un terminal et lancez :

Bash
sudo apt update
sudo apt install python3-tk
2. Installation de CustomTkinter
Il est fortement recommandé d'utiliser un environnement virtuel (venv) pour ne pas polluer votre installation système Debian.

Option A : Via un environnement virtuel (Recommandé)
Bash
# Créer l'environnement
python3 -m venv venv
# L'activer
source venv/bin/activate
# Installer la bibliothèque
pip install customtkinter
Option B : Installation globale (Si nécessaire)
Si vous ne voulez pas de venv, Debian 12+ vous empêchera probablement d'utiliser pip install directement. Utilisez alors :

Bash
pip install customtkinter --break-system-packages
3. Test de bon fonctionnement
Pour vérifier que tout est bien configuré, créez un fichier test_ctk.py :

Python
import customtkinter as ctk

# Configuration du thème
ctk.set_appearance_mode("dark")  # Modes: "System", "Dark", "Light"
ctk.set_default_color_theme("blue")  # Thèmes: "blue", "green", "dark-blue"

app = ctk.CTk()
app.geometry("400x240")
app.title("CustomTkinter sur Debian")

def button_callback():
    print("Bouton cliqué !")

button = ctk.CTkButton(app, text="Cliquez-moi", command=button_callback)
button.pack(padx=20, pady=20)

app.mainloop()
4. Astuces spécifiques à Debian / Linux
Flou des polices : Si les polices paraissent floues sur un écran haute résolution, c'est souvent lié à la gestion du DPI par X11 ou Wayland. CustomTkinter gère généralement bien l'auto-scaling, mais vérifiez que votre environnement de bureau (GNOME, KDE) n'applique pas un facteur d'échelle forcé.

Thème du système : L'option ctk.set_appearance_mode("System") fonctionne bien sur GNOME pour détecter si vous êtes en mode sombre ou clair.

Performance : Si vous utilisez une vieille version de Debian avec une version de Python < 3.7, vous pourriez rencontrer des limitations. Assurez-vous d'être au moins sur Debian 11 ou 12.

javdenech wrote:

je me suis gourer sur le nom dans pyproject.toml

il y avait des - a la place des underscore le relou

j'ai creer un compte pip pour le publier

pip install qet-tb-generator-xd

j'ai remis a jour le fichier dans le google drive

notice that on my pip account it's version 1.3.3
pip install qet-tb-generator-xd

on pip official release it's 2.0.1 but i leave the wrong name on toml file so there's a bug to launch it from qelectrotech if installed from pip official release.
I don't know if raulroda do the modification yet, i need to connect on the github but i 'am to busy for the moment.
it works on windows but i don't test it yet on linux and mac.

Kellermorph wrote:

Are the Terminals, the bridge, the cable etc created automatic or did u write it yourself for each terminal?

It's automatic

je me suis gourer sur le nom dans pyproject.toml

il y avait des - a la place des underscore le relou

j'ai creer un compte pip pour le publier

pip install qet-tb-generator-xd

j'ai remis a jour le fichier dans le google drive

Bonjour laurent,

je pense que oui car j'ai integrer une fonction sort by id sur le bouton a gauche
et j'ai fait plusieurs test les fils sont bien raccorder avec les bonnes bornes

les hoses et les conductor sont bien enregistrer dans la partie fonction ainsi que les couleur

les fonction sur les borne sont cleannable avec l'outil qet nomenclature.

Bonjour laurent,
I think so, because I integrated a sort by ID function on the button to the left.

And I've done several tests; the wires are correctly connected to the right terminals.

The hoses and conductors are correctly registered in the function section, as are their colors.

The functions on the terminals can be cleaned up with the QET Nomenclature tool.

unalcalde wrote:

Hi, I deploy version 2.0.0 to pypi.

To install the last version: pip install qet-tb-generator==2.0.0

To install previous version: pip install qet-tb-generator==1.3.1

ok cool did you take my last one from my google drive ?
because you deploy the wrong one from grosfichier.com
because cannot launch 2.0 from qelectrotech on windows....

https://drive.google.com/drive/folders/ … bNicnXMdAo

QET_TB_GENERATOR_PIP.ZIP

normally this last one can be launch from qelectrotech

unalcalde wrote:

Hi family. I'll try to deploy it on the weekend.

Nowadays I'm using Windows at work and I don't have the development tools installed.

I'll try to install it and create the pip version.

Thanks @javdenech for your excellence version.

I make a clean package to deploy on pip at the begining of the discution i will try to deploy on pip with a new name to maintain for later update on the link
https://drive.google.com/drive/folders/ … drive_link
Here the link of all my tool for qelectrotech including
>new qet generator raulroda updated xd (me)
>num wire for automatic numerotation
>qet element for automatic numerotation of element K KM YV B IS renamed KA1 KA2 KA3 etc except slave element
also include terminal with and without stage numbering in automatic serveral X1 to X1:1 X1:2 ETC
>qet nomenclature to copy paste from excel directly in your diagram for every propertys of element

it will be cool to have a configurable extension plugin list in qelectrotech to launch other program

unalcalde wrote:

Hi family. I'll try to deploy it on the weekend.

Nowadays I'm using Windows at work and I don't have the development tools installed.

I'll try to install it and create the pip version.

Thanks @javdenech for your excellence version.

De nada

There a setup file to install it on windows as a standalone exe program in the folder output for windows with no link launch it from qet generator from qelectrotech.
and also in the dist folder the exe if you want to test it directly with all the python extension in folder _extention to run without installing anything.
The setup was done with "inno setup 6" software and the script to create the setup for inno is QET_TB_Generator_Setup.iss.

Also, I modifing src script with antigravity and i don't know if setup.py and run.py is ok...

11

(7 replies, posted in Terminal block generator)

J ai fais une version qui gère ce cas

https://qelectrotech.org/forum/viewtopic.php?id=3115

raulroda contact me and asking me if I want him to preparing a version for pip.
will see...

code source dispo ici par contre j'ai fais ca sur windows pour compiler en exe je sais pas si tout est bon pour du deploiement pip

I sent to him all the source code,I hope he can test and deploy for other because this new plugin change life for every electrician in my compagny. Hope it will be useful for other user in the world ! nomicons/wink

If someone know where to change the link in qelectrorotech folder, to launch the plugin link in windows...

The bridge management work also for stage with O to start a bridge and F to stop à bridge connections. And there is also a automatic bridge analyser with stage by stage mode to connect bridge with cable with the same polarity O O O O F
to trace exception betweew stage.

I send the script to raulroda but i don't know if i can share his work ! Waiting a response from him, maybe he can take a look and update his package.it provide compatibility with previous version, because the name X3:1 can be used also and you can also manually configure the stage... else it take .1 .2 .3 automatically to défine the stage position from the id. The sort by id also take care of that and if you have three stage with the same id the sorting take care of cable name to sorting them.

I rewrite the software from raulroda with management of stage and other

pip install qet-tb-generator-xd

Here the link of all my tool for qelectrotech including
>new qet generator raulroda updated xd (me)
>num wire for automatic numerotation
>qet element for automatic numerotation of element K KM YV B IS renamed KA1 KA2 KA3 etc except slave element
also include terminal with and without stage numbering in automatic serveral X1 to X1:1 X1:2 ETC
>qet nomenclature to copy paste from excel directly in your diagram for every propertys of elements

https://drive.google.com/drive/folders/ … sp=sharing

19

(9 replies, posted in Scripts)

compile.bat

@echo off
:: Se placer dans le dossier du script actuel
cd /d "%~dp0"

echo ========================================================
echo   COMPILATION DE QET_NOMENCLATURE EN EXECUTABLE (.EXE)
echo ========================================================
echo.

:: 1. Vérifier et installer les prérequis
echo [1/3] Verification et installation des librairies requises...
:: On s'assure que pyinstaller et tksheet sont bien installés
python -m pip install --upgrade pip
python -m pip install tksheet pyinstaller
if %errorlevel% neq 0 (
    echo.
    echo ERREUR: Impossible d'installer les librairies. Verifiez votre installation Python.
    pause
    exit /b
)

:: 2. Nettoyer les anciennes compilations pour éviter les conflits
echo.
echo [2/3] Nettoyage des anciens fichiers...
if exist build rmdir /s /q build
if exist dist rmdir /s /q dist
if exist *.spec del *.spec

:: 3. Lancer la compilation
echo.
echo [3/3] Creation de l'executable (cela peut prendre 1 ou 2 minutes)...
:: --noconsole : Masque la fenêtre noire (console) au lancement
:: --onefile : Crée un seul fichier .exe autonome (au lieu d'un dossier)
:: --hidden-import : Force l'inclusion de tksheet pour être sûr
pyinstaller --noconsole --onefile --hidden-import=tksheet --name "QET_Nomenclature" "QET_NOMENCLATURE.PY"

echo.
echo ========================================================
if exist "dist\QET_Nomenclature.exe" (
    echo   SUCCES !
    echo   Votre executable est pret dans le dossier : dist\QET_Nomenclature.exe
    echo.
    echo   Vous pouvez copier ce fichier "QET_Nomenclature.exe" n'importe ou.
    echo   Il contient deja tksheet et Python.
) else (
    echo   ECHEC DE LA COMPILATION.
    echo   Veuillez verifier les messages d'erreur ci-dessus.
)
echo ========================================================
pause

20

(9 replies, posted in Scripts)

script updated tu use tksheet to copy paste from excel file

21

(2 replies, posted in Scripts)

this script searh all element deginning by Xxx: to make a list

X1
X2
X3
X4
X5

so you just have to put several X1 in your driagram

if you click on X1 in the list it will put X1:00 X1:01 with left to right logic across all the folio

you can also activate with wire to have X1:00_100 etc to concatenate with the num value of the wire connected on it

this for use with the new generator in dev actually

also if the you make modification it will renew all the name on a already taged X1:xxx element

22

(9 replies, posted in Scripts)

it's running for me to write in the qet files the propertie of each element.
Don't forget to save the project and open the saving project.

it's the first release for the moment and i used if to french diagram may be should be adapted to used with other language, in case of i think of properties don"t have the same name, i need to check later,
I will upgrade to use tksheet later also.

23

(2 replies, posted in Scripts)

#!/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()

24

(2 replies, posted in Scripts)

Script pour l'auto numérotation des bornes

Script mis a jour 08/02/2026