IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

Débutant en Python : comment surveiller un répertoire pour déclencher une action


Sujet :

Python

  1. #41
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Salut,

    Citation Envoyé par Vincent-vr Voir le message
    Donc en clair : comment faire tourner périodiquement
    os.stat() que tu cites tout en pouvant toujours agir sur l'IHM pour lui dire simplement "arrêtes de pédaler, je vais changer de répertoire" ?
    Pas la peine de thread pour des activités qui se terminent vite: la mécanique after suffit après il faut apprendre à l'utiliser. C'est dans tous les tutos car c'est un moyen de faire une animation avec tkinter, comme un chronomètre des petit jeux,...

    Et si vous voulez terminer votre projet au plus vite sans prendre le temps d'apprendre à coder ce genre de truc, c'est une mauvaise méthode...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  2. #42
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Je pense avoir compris comment marche After, le seul problème est la question de l'itération qui provoque le problème que j'ai déjà exposé...

  3. #43
    Invité
    Invité(e)
    Par défaut
    Re,

    J'ai pondu un truc, tu me diras ce que tu en penses... Malheureusement il y a un bug aléatoire avec getctime, il semblerait que parfois le fichier fraichement renommé apparait dans la liste added alors que le ctime est le même si on fait une requête avec os.stat(). Comme si le nouveau fichier n'avait pas encore chargé ses metadonnées ou truc dans le genre... Une autre méthode plus lourde serait peut-être de comparé le contenu des 2 fichiers...
    Le plus simple étant de mettre un autre fichier de destination...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
     
    import os
    import threading
     
    import win32file
    import win32event
    import win32con
     
    path_to_watch = r'C:\Users\****'
    path_to_dest = path_to_watch
     
    def thread():
        change_handle = win32file.FindFirstChangeNotification (
        path_to_watch,
        0,
        win32con.FILE_NOTIFY_CHANGE_FILE_NAME
        )
        old_path_to_watch = path_to_watch
        try:
     
            old_path_contents = dict((path_to_watch+'/'+f,os.path.getctime(path_to_watch+'/'+f)) for f in os.listdir(path_to_watch))
     
            while 1:
                result = win32event.WaitForSingleObject (change_handle, 500)
     
                if path_to_watch != old_path_to_watch:
                    return
                if result == win32con.WAIT_OBJECT_0:
                    new_path_contents = dict((path_to_watch+'/'+f,os.path.getctime(path_to_watch+'/'+f)) for f in os.listdir(path_to_watch))
                    added = [f for f in new_path_contents if new_path_contents[f] not in old_path_contents.values()]
     
                    if added:
                        for f in added:
                            print("Added : ",f,new_path_contents[f])
     
                            #Open and resize image
                            img = Image.open(f)
                            wpercent = (basewidth/float(img.size[0]))
                            hsize = int((float(img.size[1])*float(wpercent)))
                            img = img.resize((basewidth,hsize), Image.ANTIALIAS)
                            img = ImageTk.PhotoImage(img)
     
                            #Save img
                            root.pictures.append(img) 
     
                            #Create and display canva 
                            cv = Canvas(frame_imgs,width=basewidth, height=hsize)
                            cv.create_image(0,0,anchor=NW, image=img)
                            cv.grid(row=root.row, column=0)
     
                            #Create an Entry and display fname
                            entree = Entry(frame_imgs,width=50)
                            entree.insert(0,os.path.basename(f))
                            entree.grid(row=root.row, column=1)
     
                            #Save fname and canva linked
                            entree.path = f
                            entree.img = cv
     
                            #Bind Return Key
                            entree.bind("<Return>",rename)
     
                            #Add a row for the next img
                            root.row+=1
     
                            # Update images frames idle tasks and scrollbar
                            frame_imgs.update_idletasks()
                            canvas.config(scrollregion=canvas.bbox("all"))
     
                    old_path_contents = new_path_contents
                    win32file.FindNextChangeNotification (change_handle)
     
        finally:
            win32file.FindCloseChangeNotification (change_handle)
     
    def rename(event):
        path = event.widget.path
        filetype = '.'+path.split('.')[-1]
     
        #Add filetype if not exist
        new_path = event.widget.get()
        if not new_path.endswith(filetype):
            new_path+=filetype
     
        #Rename if exist
        old_path = new_path
        count = 1
        while os.path.exists(path_to_dest+'/'+new_path):
            new_path = old_path[:old_path.index(filetype)]+'_'+str(count)+filetype
            count+=1
     
        os.replace(path,path_to_dest+'/'+new_path)
     
        #Destroy img and fname
        event.widget.img.destroy()
        event.widget.destroy()
     
        #Update images frames idle tasks and scrollbar
        frame_imgs.update_idletasks()
        canvas.config(scrollregion=canvas.bbox("all"))
     
    def ask_w():
        global path_to_watch
        path_to_watch = filedialog.askdirectory ( initialdir = path_to_watch)
        threading.Thread(target=thread).start()
        B1.configure(text ="Dossier à surveiller : "+os.path.basename(path_to_watch))
     
    def ask_d():
        global path_to_dest
        path_to_dest = filedialog.askdirectory ( initialdir = path_to_watch)
        B2.configure(text ="Dossier de destination : "+os.path.basename(path_to_dest))
     
     
    def resize(event):
        #Update the widget's sizes
        canvas.configure(height=root.winfo_height()-B1.winfo_height()-4)
        frame_imgs.update_idletasks()
        canvas.config(scrollregion=canvas.bbox("all"))
     
    def on_mousewheel(event):
        #Scrollbar on mousewheel
        canvas.yview_scroll(int(-1*(event.delta/120)), "units")
     
     
    from tkinter import *
    from PIL import Image, ImageTk
    from tkinter import filedialog
     
    root = Tk()
    root.resizable(0,1)
     
    basewidth = 200
    root.row = 0
    root.pictures = []
     
    B1 = Button(root, text ="Dossier à surveiller : "+os.path.basename(path_to_watch), command = ask_w)
    B1.grid(row=0,column=0)
     
    B2 = Button(root, text ="Dossier de destination : "+os.path.basename(path_to_dest), command = ask_d)
    B2.grid(row=0,column=1)
     
    frame = Frame(root)
    frame.grid(row=1,column=0,columnspan=2)
     
    # Add a canvas in the window
    canvas = Canvas(frame, width=800)
    canvas.grid(row=0, column=0, sticky="news")
     
    # Link a scrollbar to the canvas
    vsb = Scrollbar(frame, orient="vertical", command=canvas.yview)
    vsb.grid(row=0, column=1, sticky='ns')
    canvas.configure(yscrollcommand=vsb.set)
     
    # Create a frame to contain the images
    frame_imgs = Frame(canvas)
    canvas.create_window((0, 0), window=frame_imgs, anchor='nw')
     
    #Some bindings
    canvas.bind("<MouseWheel>", on_mousewheel)
    root.bind("<Configure>",resize)
     
    threading.Thread(target=thread).start()
    mainloop()
    Nom : tttt.PNG
Affichages : 215
Taille : 201,3 Ko

  4. #44
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Wahouh merci mais alors là c'est du lourd à digérer JE vais essayer de comprendre comment ça marche...

  5. #45
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Citation Envoyé par Vincent-vr Voir le message
    Je pense avoir compris comment marche After, le seul problème est la question de l'itération qui provoque le problème que j'ai déjà exposé...
    Vous pensez?

    Restons factuel: ça ne fonctionne pas et sans poster un code minimal permettant de reproduire l'erreur... difficile de comprendre pourquoi ce que vous avez fait ne fonctionne pas.

    Après s'il faut vous recopier un exemple qu'on peut trouver dans tous les tutos ou en cherchant un peu sur le forum ou Internet.

    Ca ne vous servira à rien car un code qui fonctionne et comprendre pourquoi il fonctionne pour le réutiliser dans votre petit projet est juste le but de l'apprentissage que vous ne voulez pas prendre le temps de faire.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #46
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Voilà mon code...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
     
    # Modules
    from tkinter import *
    from tkinter import ttk
    from tkinter.filedialog import *
    from openpyxl import load_workbook
    import os, time
    import os
    import win32file
    import win32event
    import win32con
    import threading, time
     
    RepertoireASurv="."
    before=""
    after=""
    Surveiller=0
     
    # Fonctions
    def ouvrir_fichier():
        FichierExcel = askopenfilename(title="Choisir une liste d'élèves",filetypes=[('Excel files','.xlsx'),('all files','.*')])
        Fichier = load_workbook(FichierExcel)
        Feuille = Fichier.active
        i=1
        LabelNomFichier['text']=FichierExcel
        liste.delete(0,END)
        for row in Feuille.iter_rows(min_row = 2, min_col = 1, max_col = 4, values_only=True):
            Eleve = row[0]+' '+row[1]+' '+row[2]
            liste.insert(i,Eleve)
            i=i+1
     
    def repertoire_asurv():
        RepertoireASurv = askdirectory(title="Choisir le répertoire à surveiller")
        LabelRepertoire['text']=RepertoireASurv
     
    def init_surveillance():
        global before
        before = dict ((f, None) for f in os.listdir (RepertoireASurv))
        Surveiller=1
        Photoscol.after(1,surveiller_rep())
     
    def surveiller_rep():
        global after, before
        while surveiller==1:
            after = dict ((f, None) for f in os.listdir (RepertoireASurv))
            added = [f for f in after if not f in before]
            removed = [f for f in before if not f in after]
            if added: print("Added: ", ", ".join (added))
            before = after
            surveiller_rep(1000,surveiller_rep())
     
    def arreter_surveiller_rep():
        surveiller=0
     
    # Initialisations
     
    # Widgets et code
     
    Photoscol = Tk()
    Photoscol.title('Photoscol')
    Photoscol.geometry('1080x960')
    surveiller = 1
    #Bouton choix fichier élèves
    BoutonFichier = Button(Photoscol, text="Fichier élèves", command=ouvrir_fichier)
    BoutonFichier.grid(row=0, column = 0,sticky=NW, padx=10, pady=10)
     
    #Liste d'élèves en cours
    LabelNomFichier = Label(Photoscol, text="Choisir un fichier élèves", bg='#ffffff', width=50)
    LabelNomFichier.grid(row=0, column=1, sticky=NW, pady=10)
     
    #Bouton choix répertoire pdv
    BoutonRepertoire = Button(Photoscol, text="Répertoire pdv", command=repertoire_asurv)
    BoutonRepertoire.grid(row=0, column = 2,sticky=NW, padx=10, pady=10)
     
    #Répertoire à surveiller
    LabelRepertoire = Label(Photoscol, text="Choisir un répertoire de pdv", bg='#ffffff', width=50)
    LabelRepertoire.grid(row=0, column=3, sticky=NW, pady=10)
     
    #Affichage liste élèves
    liste = Listbox(Photoscol, width=80, bg="Ivory", height=50)
    liste.grid(row=1, columnspan=2, sticky=NW)
     
    #Bouton surveillance
    BoutonSurveiller = Button(Photoscol, text="Surveiller", command=surveiller_rep)
    BoutonSurveiller.grid(row=1, column = 2,sticky=NW, padx=10, pady=10)
     
    #Bouton arrêt surveillance
    BoutonArreterSurveiller = Button(Photoscol, text="Arrêter de surveiller", command=arreter_surveiller_rep)
    BoutonArreterSurveiller.grid(row=1, column = 3,sticky=NW, padx=10, pady=10)
     
    #Sortir de l'appli
    BoutonQuitter = Button(Photoscol, text='Quitter', command=Photoscol.destroy)
    BoutonQuitter.grid(row=2, columnspan=2, padx=10, pady=40)
     
    Photoscol.mainloop()
     
    # Fin widget et code
    Les exemples sur le net se contentent d'incrémenter un compteur... Pas de problème de mémorisation d'un résultat à T à comparer avec un résultat à T+1...

  7. #47
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Re,

    J'ai pondu un truc, tu me diras ce que tu en penses... Malheureusement il y a un bug aléatoire avec getctime, il semblerait que parfois le fichier fraichement renommé apparait dans la liste added alors que le ctime est le même si on fait une requête avec os.stat(). Comme si le nouveau fichier n'avait pas encore chargé ses metadonnées ou truc dans le genre... Une autre méthode plus lourde serait peut-être de comparé le contenu des 2 fichiers...
    Le plus simple étant de mettre un autre fichier de destination...
    C'est vrai que ça bégaye un peu, si je fais du RAW+JPEG il me remonte 3 fois le jpeg mais une seule fois le RAW, bizarre... Bon, ça progresse !
    J'ai mis le démarrage du thread dans un bouton "Start" et du coup ça évite l'erreur au démarrage faute de répertoire valide. Comment on arrête un thread ?

  8. #48
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Citation Envoyé par Vincent-vr Voir le message
    Les exemples sur le net se contentent d'incrémenter un compteur... Pas de problème de mémorisation d'un résultat à T à comparer avec un résultat à T+1...
    Déjà la récursion vient de la ligne 50:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          surveiller_rep(1000,surveiller_rep())
    où vous avez confondu passer la fonction en paramètre avec passer ce que retourne la fonction (et on descend dans la pile d'appel recursive jusqu'à ce que Python dise "stop").

    Quand je parle des bases, c'est pas du pipeau!

    Et le reste du code est du même acabit... c'est pas mal pour un débutant mais çà serait bien mieux si vous aviez pris le temps de débuter. Car faire des choses aussi abstraites sans maîtriser les constructions de bases, ça craint. Dans les tutos on vous propose des exos avec des nombres, des lettres, des trucs que vous pouvez gratter sur une feuille de papier pour comprendre la mécanique. Et faire les exercices vous familiarise avec les messages d'erreurs dans des cas simples (histoire de relire votre code et comprendre ce qu'il s'y passe).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #49
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Vincent-vr Voir le message
    Wahouh merci mais alors là c'est du lourd à digérer JE vais essayer de comprendre comment ça marche...
    Ca devrait le faire, si ça vient de ma part ! ^^

    Ah et puis les précautions d'usage sont de mises... C'est à dire faire une sauvegarde de tes photos avant d'utiliser mon script, on ne sait jamais.

    Une update pour gagner en rapidité de ton côté !!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    import os, time, string
    import threading
    from winsound import *
     
    import win32file
    import win32event
    import win32con
     
    path_to_watch = r'C:\Users\***'
    path_to_dest = path_to_watch
     
    printables = [x for x in string.printable if x not in r'<>:"/\|?* ']
     
    #Liste des élèves
    file = open("liste.txt","r")
    students = [student.rstrip() for student in file.readlines()]
    file.close()
     
    def thread():
        change_handle = win32file.FindFirstChangeNotification (
        path_to_watch,
        0,
        win32con.FILE_NOTIFY_CHANGE_FILE_NAME
        )
        old_path_to_watch = path_to_watch
        try:
     
            old_path_contents = dict((path_to_watch+'/'+f,os.path.getctime(path_to_watch+'/'+f)) for f in os.listdir(path_to_watch))
     
            while 1:
                result = win32event.WaitForSingleObject (change_handle, 500)
     
                if path_to_watch != old_path_to_watch:
                    return
                if result == win32con.WAIT_OBJECT_0:
                    new_path_contents = dict((path_to_watch+'/'+f,os.path.getctime(path_to_watch+'/'+f)) for f in os.listdir(path_to_watch))
                    added = [f for f in new_path_contents if new_path_contents[f] not in old_path_contents.values()]
     
                    if added:
                        for f in added:
                            print("Added : ",f,new_path_contents[f])
     
                            #Open and resize image
                            img = Image.open(f)
                            wpercent = (basewidth/float(img.size[0]))
                            hsize = int((float(img.size[1])*float(wpercent)))
                            img = img.resize((basewidth,hsize), Image.ANTIALIAS)
                            img = ImageTk.PhotoImage(img)
     
                            #Save img
                            root.pictures.append(img) 
     
                            #Create and display canva 
                            cv = Canvas(frame_imgs,width=basewidth, height=hsize)
                            cv.create_image(0,0,anchor=NW, image=img)
                            cv.grid(row=root.row, column=0)
     
                            #Create an Entry and display fname
                            entree = Entry(frame_imgs,width=50)
                            entree.insert(0,os.path.basename(f))
                            entree.grid(row=root.row, column=1)
     
                            #Save fname and canva linked
                            entree.path = f
                            '''[UPDATE]'''
                            entree.img = img 
                            entree.cv = cv
                            entree.pressed = False
                            if not root.row:
                                root.focus()
                                entree.focus_force()
                                root.update()
                                entree.selection_range(0, END)
     
                            #Bind entree
                            entree.bind("<Return>",rename)
                            entree.bind("<Key>",launch_search)
                            '''[/UPDATE]'''
                            #Add a row for the next img
                            root.row+=1
     
                            # Update images frames idle tasks and scrollbar
                            frame_imgs.update_idletasks()
                            canvas.config(scrollregion=canvas.bbox("all"))
     
                    old_path_contents = new_path_contents
                    win32file.FindNextChangeNotification (change_handle)
     
        finally:
            win32file.FindCloseChangeNotification (change_handle)
     
    '''[UPDATE]'''
    def launch_search(event):
        if event.char in printables:
            time.sleep(0.05)
            threading.Thread(target=search_student,args=(event,)).start()
     
    def search_student(event):
        text = event.widget.get()
        founds = []
        for s in students:
            if text.lower() in s.lower():
                founds.append(s)
     
        if len(founds) == 1:
            threading.Thread(target=sound).start()
            event.widget.delete(0, END)
            event.widget.insert(0,founds[0])
            if chkValue.get():
                rename(event)
            else:
                event.widget.config(state='disabled')
                time.sleep(2)
                event.widget.config(state='normal')
     
        elif not len(founds):
            print("Eleve inconnu")
     
    def sound():
        Beep(300,50)
        Beep(600,50)
    '''[/UPDATE]'''
     
    def rename(event):
        path = event.widget.path
        filetype = '.'+path.split('.')[-1]
     
        #Add filetype if not exist
        new_path = event.widget.get()
        if not new_path.endswith(filetype):
            new_path+=filetype
     
        #Rename if exist
        old_path = new_path
        count = 1
        while os.path.exists(path_to_dest+'/'+new_path):
            new_path = old_path[:old_path.index(filetype)]+'_'+str(count)+filetype
            count+=1
     
        os.replace(path,path_to_dest+'/'+new_path)
     
        #Destroy img and fname + Select next entry
        '''[UPDATE]'''
        found = False
        for child in frame_imgs.winfo_children():
            if event.widget == child:
                found = True
     
            elif found and type(child) == Entry:            
                child.selection_range(0, END)
                break
     
        event.widget.tk_focusNext().focus()
     
        root.pictures.remove(event.widget.img)
        '''[/UPDATE]'''
        event.widget.cv.destroy()
        event.widget.destroy()
     
     
        #Update images frames idle tasks and scrollbar
        frame_imgs.update_idletasks()
        canvas.config(scrollregion=canvas.bbox("all"))
     
    def ask_w():
        global path_to_watch
        path_to_watch = filedialog.askdirectory ( initialdir = path_to_watch)
        threading.Thread(target=thread).start()
        B1.configure(text ="Dossier à surveiller : "+os.path.basename(path_to_watch))
     
    def ask_d():
        global path_to_dest
        path_to_dest = filedialog.askdirectory ( initialdir = path_to_watch)
        B2.configure(text ="Dossier de destination : "+os.path.basename(path_to_dest))
     
    def resize(event):
        #Update the widget's sizes
        canvas.configure(height=root.winfo_height()-B1.winfo_height()-4)
        frame_imgs.update_idletasks()
        canvas.config(scrollregion=canvas.bbox("all"))
     
    def on_mousewheel(event):
        #Scrollbar on mousewheel
        canvas.yview_scroll(int(-1*(event.delta/120)), "units")
     
     
    from tkinter import *
    from PIL import Image, ImageTk
    from tkinter import filedialog
     
    root = Tk()
    root.resizable(0,1)
    root.attributes('-topmost',True)
     
    basewidth = 200
    root.row = 0
    root.pictures = []
     
    B1 = Button(root, text ="Dossier à surveiller : "+os.path.basename(path_to_watch), command = ask_w)
    B1.grid(row=0,column=0)
     
    B2 = Button(root, text ="Dossier de destination : "+os.path.basename(path_to_dest), command = ask_d)
    B2.grid(row=0,column=1)
     
    chkValue = BooleanVar() 
    chkValue.set(True)
    C1 = Checkbutton(root, text="Validation auto si reconnu", var=chkValue)
    C1.grid(row=0,column=2)
     
    frame = Frame(root)
    frame.grid(row=1,column=0,columnspan=3)
     
    # Add a canvas in the window
    canvas = Canvas(frame, width=800)
    canvas.grid(row=0, column=0, sticky="news")
     
    # Link a scrollbar to the canvas
    vsb = Scrollbar(frame, orient="vertical", command=canvas.yview)
    vsb.grid(row=0, column=1, sticky='ns')
    canvas.configure(yscrollcommand=vsb.set)
     
    # Create a frame to contain the images
    frame_imgs = Frame(canvas)
    canvas.create_window((0, 0), window=frame_imgs, anchor='nw')
     
    #Some bindings
    canvas.bind("<MouseWheel>", on_mousewheel)
    root.bind("<Configure>",resize)
     
    threading.Thread(target=thread).start()
    mainloop()
    Résultat en image :

  10. #50
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Vincent-vr Voir le message
    C'est vrai que ça bégaye un peu, si je fais du RAW+JPEG il me remonte 3 fois le jpeg mais une seule fois le RAW, bizarre... Bon, ça progresse !
    J'ai mis le démarrage du thread dans un bouton "Start" et du coup ça évite l'erreur au démarrage faute de répertoire valide. Comment on arrête un thread ?
    Un thread s'arrête comme un script "basique".
    Donc s'il y a une boucle infinie ben le thread ne s'arrêtera jamais, il faut que la boucle s'arrête.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    >>> import time, threading
    >>> def go():
    	global c
    	c=5
    	threading.Thread(target=test).start()
    	while c>0:
    		c-=1
    		time.sleep(1)
     
     
    >>> def test():
    	while c:
    		print('hello')
    		time.sleep(0.5)
     
     
    >>> go()
    hello
    hello
    hello
    hello
    hello
    hello
    hello
    hello
    >>>
    PS : Moi je trouve que ton code est pas si mal, il y a juste des petits trucs à revoir, rien de méchant...
    Par exemple :
    dict ((f, None) for f in os.listdir (RepertoireASurv))
    Ne sert à rien de nos jours ! Plutôt :
    before= os.listdir (RepertoireASurv)
    after = os.listdir (RepertoireASurv)

    L'intérêt de créer un dictionnaire ça va être "d'accoler" une valeur à une clé, ici on accole None donc rien au nom du fichier, c'est inutile... De mon côté j'ai "accolé" au nom du fichier, la valeur ctime du fichier (dernière date de modification) qui normalement ne change pas à chaque renommage. Mais comme je le disais je pense que la boucle doit analyser certains fichiers avant que ce ctime ne soit recopié dans le fichier renommé ou que sais-je... PS : J'utilise le numéro d'inode maintenant...

    Et puis :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        i=1
        LabelNomFichier['text']=FichierExcel
        liste.delete(0,END)
        for row in Feuille.iter_rows(min_row = 2, min_col = 1, max_col = 4, values_only=True):
            Eleve = row[0]+' '+row[1]+' '+row[2]
            liste.insert(i,Eleve)
            i+=1
    Peut être raccourci en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        LabelNomFichier['text']=FichierExcel
        liste.delete(0,END)
        for i,row in enumerate(Feuille.iter_rows(min_row = 2, min_col = 1, max_col = 4, values_only=True)):
            Eleve = row[0]+' '+row[1]+' '+row[2]
            liste.insert(i,Eleve)
    Sinon je n'aime pas tellement utiliser after pour ma part, je ne sais pas pourquoi wiz te préconises ça plutôt qu'un thread mais il doit avoir une bonne raison.

    [UPDATE 🔗] (script hébergé sur replit.com)
    Dernière modification par Invité ; 08/09/2021 à 02h40.

  11. #51
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Déjà la récursion vient de la ligne 50:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
          surveiller_rep(1000,surveiller_rep())
    où vous avez confondu passer la fonction en paramètre avec passer ce que retourne la fonction (et on descend dans la pile d'appel recursive jusqu'à ce que Python dise "stop").

    Quand je parle des bases, c'est pas du pipeau!

    Et le reste du code est du même acabit... c'est pas mal pour un débutant mais çà serait bien mieux si vous aviez pris le temps de débuter. Car faire des choses aussi abstraites sans maîtriser les constructions de bases, ça craint. Dans les tutos on vous propose des exos avec des nombres, des lettres, des trucs que vous pouvez gratter sur une feuille de papier pour comprendre la mécanique. Et faire les exercices vous familiarise avec les messages d'erreurs dans des cas simples (histoire de relire votre code et comprendre ce qu'il s'y passe).

    - W
    Ok ok je vais m'y mettre...😉

  12. #52
    Membre averti
    Homme Profil pro
    ex-informaticien photographe
    Inscrit en
    Septembre 2021
    Messages
    37
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Cher (Centre)

    Informations professionnelles :
    Activité : ex-informaticien photographe
    Secteur : Communication - Médias

    Informations forums :
    Inscription : Septembre 2021
    Messages : 37
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Un thread s'arrête comme un script "basique".
    Donc s'il y a une boucle infinie ben le thread ne s'arrêtera jamais, il faut que la boucle s'arrête.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    >>> import time, threading
    >>> def go():
    	global c
    	c=5
    	threading.Thread(target=test).start()
    	while c>0:
    		c-=1
    		time.sleep(1)
     
     
    >>> def test():
    	while c:
    		print('hello')
    		time.sleep(0.5)
     
     
    >>> go()
    hello
    hello
    hello
    hello
    hello
    hello
    hello
    hello
    >>>
    PS : Moi je trouve que ton code est pas si mal, il y a juste des petits trucs à revoir, rien de méchant...
    Par exemple :
    dict ((f, None) for f in os.listdir (RepertoireASurv))
    Ne sert à rien de nos jours ! Plutôt :
    before= os.listdir (RepertoireASurv)
    after = os.listdir (RepertoireASurv)

    L'intérêt de créer un dictionnaire ça va être "d'accoler" une valeur à une clé, ici on accole None donc rien au nom du fichier, c'est inutile... De mon côté j'ai "accolé" au nom du fichier, la valeur ctime du fichier (dernière date de modification) qui normalement ne change pas à chaque renommage. Mais comme je le disais je pense que la boucle doit analyser certains fichiers avant que ce ctime ne soit recopié dans le fichier renommé ou que sais-je... PS : J'utilise le numéro d'inode maintenant...

    Et puis :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        i=1
        LabelNomFichier['text']=FichierExcel
        liste.delete(0,END)
        for row in Feuille.iter_rows(min_row = 2, min_col = 1, max_col = 4, values_only=True):
            Eleve = row[0]+' '+row[1]+' '+row[2]
            liste.insert(i,Eleve)
            i+=1
    Peut être raccourci en :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        LabelNomFichier['text']=FichierExcel
        liste.delete(0,END)
        for i,row in enumerate(Feuille.iter_rows(min_row = 2, min_col = 1, max_col = 4, values_only=True)):
            Eleve = row[0]+' '+row[1]+' '+row[2]
            liste.insert(i,Eleve)
    Sinon je n'aime pas tellement utiliser after pour ma part, je ne sais pas pourquoi wiz te préconises ça plutôt qu'un thread mais il doit avoir une bonne raison.

    [UPDATE 🔗] (script hébergé sur replit.com)
    Ça a l'air nickel ! Merci ! Je vais acheter une boîte de Doliprane et je me plonge dans l'analyse pour comprendre 😁👍🥵

  13. #53
    Expert confirmé
    Avatar de jurassic pork
    Homme Profil pro
    Bidouilleur
    Inscrit en
    Décembre 2008
    Messages
    4 249
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Bidouilleur
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2008
    Messages : 4 249
    Par défaut
    hello,
    LeNarvalo, bravo pour ton script. Par contre j'ai un souci sur cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #Open and resize image
    img = Image.open(f)
    j'ai cette erreur :
    PermissionError: [Errno 13] Permission denied:
    en mettant une temporisation avant l'ouverture du fichier je n'ai plus d'erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #Open and resize image
    time.sleep(1)
    img = Image.open(f)
    Ami calmant, J.P

  14. #54
    Membre prolifique
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 853
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 853
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Malheureusement il y a un bug aléatoire avec getctime, il semblerait que parfois le fichier fraichement renommé apparait dans la liste added alors que le ctime est le même si on fait une requête avec os.stat(). Comme si le nouveau fichier n'avait pas encore chargé ses metadonnées ou truc dans le genre...
    Il faut faire attention avec l'interprétaion de la ctime car on parle souvent de "date de création" mais en réalité c'est "date de changement dans l'inode" (enfin pour les OS Unixlike). Si par exemple on change les droits d'un fichier, alors la ctime change. La ctime est aussi impactée par un renommage même si ce renommage se passe dans le même fs (pourtant dans ce cas cela n'impacte en réalité pas le fichier ni son inode mais juste le dossier qui le contient mais tant pis, c'est un choix délibéré des programmeurs du fs probablement pour avoir un comportement équivalent avec un renommage inter fs). Ensuite si c'est sous win là je sais pas comment le truc se comporte mais à mon sens ça devrait être codé de façon analogue...

    Citation Envoyé par LeNarvalo Voir le message
    Une autre méthode plus lourde serait peut-être de comparé le contenu des 2 fichiers...
    Alors au-moins faire comme le programme cmp => s'arrêter à la première différence. Sinon on peut aussi (enfin toujours sous Unix) regarder le numéro d'inode car nouveau fichier=nouvel inode...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  15. #55
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Salut,

    Citation Envoyé par LeNarvalo Voir le message
    Sinon je n'aime pas tellement utiliser after pour ma part, je ne sais pas pourquoi wiz te préconises ça plutôt qu'un thread mais il doit avoir une bonne raison.
    Un thread, c'est bien pour des opérations qui durent longtemps et pour lesquelles on n'a pas trop la main (genre opération réseau).

    Ici, on fait juste un os.listdir. Si on avait la bonne idée de mettre le noms des fichiers dans un set, la différence serait facile et rapide.

    Après les deux fonctionnent mais il faut savoir quel outil utiliser plutôt que de tout ramener à un clou (if the only tool you have is a hammer, to treat everything as if it were a nail).

    Citation Envoyé par jurassic pork Voir le message
    hello,
    LeNarvalo, bravo pour ton script. Par contre j'ai un souci sur cette ligne :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    #Open and resize image
    img = Image.open(f)
    Essentiellement entre voir un nouveau fichier arriver dans le répertoire et attendre qu'il ait été écrit et disponible à la lecture prendra du temps... et des erreurs pathologiques à traiter avec des retry/temporisation.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #56
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par jurassic pork Voir le message
    LeNarvalo, bravo pour ton script. Par contre j'ai un souci sur cette ligne :
    en mettant une temporisation avant l'ouverture du fichier je n'ai plus d'erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    #Open and resize image
    time.sleep(1)
    img = Image.open(f)
    Ami calmant, J.P
    Merci et étonnant !
    Je n'ai pas eu ce genre de message même en testant avec des images super lourdes.
    Peut-être que je suis tombé au bon moment, j'ai aussi un PC un chouïa au dessus de la moyenne niveau performance... (En tout ça un peu plus rapide qu'un ZX81 )
    En tout cas merci pour la suggestion ! =)

    @Sve@r Alors au-moins faire comme le programme cmp => s'arrêter à la première différence. Sinon on peut aussi (enfin toujours sous Unix) regarder le numéro d'inode car nouveau fichier=nouvel inode...
    He he !
    J'ai pensé à l'inode avant que tu me le suggère, voire mon précédent post ! Je m'améliore ! Il semblerait que je n'ai plus le problème. (A tester)

    Sinon pour le ctime, 95% du temps ça ne change pas le ctime, je crois même que c'est un truc lié à une valeur par défaut !
    Quand il n'a pas le ctime il renvoie le atime, comme je le montrais dans un précédent commentaire. Et quand tu fais une requête dans la foulée ben il te renvoie correctement le ctime, trop chelou !

    @Vincent-vr Ça a l'air nickel ! Merci ! Je vais acheter une boîte de Doliprane et je me plonge dans l'analyse pour comprendre
    Ouai bon courage, j'ai mis un peu moins d'annotations par la suite.
    Il faudrait revoir le code lié à tkinterdnd2, il renvoit un event.data sous un format tout pourri, ce qui m'a imposé de mettre des try except pour pallier sans trop réfléchir au problème puisque en fonction du lecteur (C, D, ...) j'ai des résultats différents.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    event.data = "{D:/test/sample1.pdf} {D:/test/sample2.pdf}"
    Il y a donc 2, 3 modules à installer en plus !
    Pour le drag and drop avec tkinterdnd2 et shutil peut-être je ne sais plus s'il est dans python de base ou pas.
    Au cas où : pip install nom_du_module
    (Bordel je viens de faire pip install toto et ça existe ! )

    @Wiz Si on avait la bonne idée de mettre le noms des fichiers dans un set, la différence serait facile et rapide.
    Si tu le dis ! ^^ Encore un truc dont j'avais oublié l'existence.
    Par contre une question j'aurai peut-être pu utiliser after au lieu de faire ce qui suit, pour récupérer la valeur de l'Entry, ou alors ça freeze aussi l'Entry ??? (J'ai pourtant utilisé after dans des versions précédentes de python, mais c'est plus très clair dans ma tête). Une Entry est "bindé" et appelle la fonction launch_search.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    def launch_search(event):
        if event.char in printables:        
            threading.Thread(target=search_student,args=(event,)).start()
     
    def search_student(event):
        time.sleep(0.05)
        text = event.widget.get()

  17. #57
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Par contre une question j'aurai peut-être pu utiliser after au lieu de faire ce qui suit, pour récupérer la valeur de l'Entry, ou alors ça freeze aussi l'Entry ???
    On ne fait jamais de mise à jour de l'interface graphique en dehors du thread principal (celle de l'interface graphique).

    C'est le gros piège dans lequel on a tendance à tomber lorsqu'on utilise des threads. Et le pire, c'est que çà "marchotte" i.e. ça fonctionne un peu... et quand ça ne marche plus, on vous raconte qu'il ne faut pas le faire.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  18. #58
    Invité
    Invité(e)
    Par défaut
    Du coup, la "vraie" solution c'est quoi ?
    Ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from tkinter import *
     
    root = Tk()
    def test(event,toto=False):
        if not toto:
            event.widget.after(50,lambda :test(event,True))
            return
        print(event.widget.get())
     
    ent = Entry(root)
    ent.pack()
    ent.bind("<Key>",test)

  19. #59
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 778
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 778
    Par défaut
    Citation Envoyé par LeNarvalo Voir le message
    Du coup, la "vraie" solution c'est quoi ?
    La solution est d'apprendre à programmer.

    D'abord on explique ce qu'on cherche à faire... puis on essaie d'apprendre à le coder.

    Pourquoi faire un callback qui va se déclencher à chaque entrée de caractère pour afficher avec print le contenu de l'Entry 50ms après la saisie?

    Puis on apprend à coder... un truc comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                event.widget.after(50,lambda :test(event,True))
    s'écrit plus proprement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                event.widget.after(50, test, event, True)
    Et si le but de tout çà c'est juste le print le contenu de l'Entry 50ms après la saisie, autant écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def test(event):
        event.widget.after(50, print, event.widget.get())
    i.e. il faut retrouver l'intention de départ juste en lisant le code...

    Et quand on montre du code, on le relit histoire de constater l'oubli de l'appel à mainloop() tout en bas.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  20. #60
    Invité
    Invité(e)
    Par défaut
    La solution est d'apprendre à programmer.
    Le roi de la casse est retour !
    effbot.org est mourru et tkinter.fdex.eu aussi, ça devient de plus en plus difficile de trouver une information fiable et détaillée.

    D'abord on explique ce qu'on cherche à faire...
    Le but est de faire une autocomplétion.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    event.widget.after(50, test, event, True)
    Oh purée ! *facepalm*

    Voilà le problème :
    https://stackoverflow.com/questions/...-to-solve-this
    Et leurs solutions me plaisent pas vraiment, je trouve ça assez lourd.

    J'avais bien vu qu'il manquait le mainloop(), mais dans mon exemple, ça n'a pas d'importance.

Discussions similaires

  1. Réponses: 5
    Dernier message: 10/08/2011, 11h16
  2. comment surveiller un répertoire à distance?
    Par rainy_kll dans le forum Développement de jobs
    Réponses: 2
    Dernier message: 08/07/2010, 18h04
  3. Réponses: 2
    Dernier message: 31/12/2008, 12h16
  4. Réponses: 7
    Dernier message: 21/10/2004, 09h13
  5. [PowerAMC] Comment s'en servir pour creer une base?
    Par Elmilouse dans le forum Access
    Réponses: 2
    Dernier message: 27/07/2004, 09h53

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo