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

Tkinter Python Discussion :

ttk:treeview drag and drop.


Sujet :

Tkinter Python

  1. #1
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 459
    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 459
    Points : 37 054
    Points
    37 054
    Par défaut ttk:treeview drag and drop.
    Salut,

    Je ne pose pas souvent de questions car j'aime bien farfouiller dans Google et les sources mais là je suis un peu coincé par des accès internet sporadique.

    J'aimerai pouvoir utiliser ttk:treeview à la façon windows explorer, i.e. sélectionner des "noeuds" et les déplacer d'un parent à un autre.
    treeview vient avec les methodes .detach et .attach pour "re-parenter" les items, mais je ne vois pas comment réaliser le "visuel" de ce "drag and drop", i.e "bouger" la sélection vers la destination.

    Est-ce que j'ai loupé quelque chose?
    Pas regardé au bon endroit?

    Merci pour vos idées éclairées.
    Cordialement
    - W

  2. #2
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 947
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 947
    Points : 7 360
    Points
    7 360
    Par défaut
    Avec la méthode move peut-être, mais je t'avoue ne pas avoir tout compris

  3. #3
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Ce qui me gène dans ce genre de Widget c'est que ce n'est qu'une view, un 'cliché' du filesystem à un moment donné. A partir de là si on souhaite avoir une 'view' proche de la réalité on est obligé de 'surveiller' le fs.
    Cela passe soit par des modules plateforme dépendants soit par un thread et os.stat.
    Plus simplement, dans ce cas, n'est il pas plus sage de re remplir un node avec os.lisdir que d'utiliser move ?
    Un petit exemple*:
    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
    import tkinter
    from tkinter import ttk
    import os
    from shutil import move
    from glob import glob
     
    def remplir_tree(t, n):
        chemin = t.set(n, "chemin")
        t.delete(*t.get_children(n))
        parent = t.parent(n)
        if parent:
            pdirs = []
        else:
            pdirs = glob('.') + glob('..')
        sitems = pdirs + os.listdir(chemin)
        sitems.sort()
        for item in sitems:
            item = os.path.join(chemin, item).replace("\\", "/")
            if os.path.isdir(item):
                itemtype = "dossier"
            elif os.path.isfile(item):
                itemtype = "fichier"
            else:
                # TODO
                itemtype = "?"
            itemname = os.path.basename(item)
            iditem = t.insert(n, "end", text=itemname, values=[item, itemtype])
            if itemtype == "dossier" and itemname not in (".", ".."):
                t.item(iditem, text=itemname)
                t.insert(iditem, 0)
                t.see(iditem)
     
    def remplir_r(t):
        currentdir = os.path.abspath(os.getcwd())
        n = t.insert("", "end", text=currentdir, values=[currentdir, "dossier"])
        remplir_tree(t, n)
     
    def update_tree(event):
        t = event.widget
        n = t.focus()
        if t.set(n, "type") == "dossier":
            remplir_tree(t, n)
     
    def change_dir(event):
        t = event.widget
        n = t.focus()
        if t.parent(n):
            c = os.path.abspath(t.set(n, "chemin"))
            if os.path.isdir(c):
                os.chdir(c)
                t.delete(t.get_children(""))
                remplir_r(t)
     
    def setscrl(s, d, f):
        d = float(d)
        f = float(f)
        if d <= 0 and f >= 1:
            s.grid_remove()
        else:
            s.set(d, f)
            s.grid()
     
    def movemouse(event):
        t = event.widget
        n = t.selection()
        moveitem.set(t.set(n, "chemin"))
     
    def mouserelease(event, l):
        t = event.widget
        src = moveitem.get()
        if src and t.identify_region(event.x, event.y) == "tree":
            n = t.identify_row(event.y)
            dest = t.set(n, "chemin")
            if t.set(n, "type") == "dossier" and not dest.startswith(src):
                try:
                    move(src, dest)
                    l.config(text='Déplacement de %s'% os.path.basename(src))
                except:
                    t.delete(*t.get_children(0))
                    remplir_r(t)
                    l.config(text="Erreur lors de l'opération")
            else:
                l.config(text="Choisissez un dossier pour éffectuer un déplacement")
        else:
            l.config(text="Les déplacements se fonts dans l'arboréscence")
        l.update()
        l.after(1000, l.config(text=''))
        moveitem.set("")
     
    def intercepte(event=None):
        r.after(500, r.quit)
     
    r = tkinter.Tk()
    r.title("ttk.Treeview test")
    moveitem = tkinter.StringVar()
    vs = ttk.Scrollbar(orient="vertical")
    t = ttk.Treeview(r, columns=("chemin", "type"), selectmode="browse",
                     displaycolumns=("chemin", "type"),
                     yscrollcommand=lambda deb, fin: setscrl(vs, deb, fin))
    vs["command"] = t.yview
    t.heading("#0", text="Affichage")
    t.heading("chemin", text="Chemin")
    t.heading("type", text="Type")
    remplir_r(t)
    lbinfo = tkinter.Label(r, borderwidth=1, anchor=tkinter.W,
                            relief=tkinter.SUNKEN, justify=tkinter.LEFT)
    t.grid(column=0, row=0, sticky="nswe")
    vs.grid(column=1, row=0, sticky="ns")
    lbinfo.grid(column=0, row=1, columnspan=2, sticky="ew", padx=5, pady=5)
    t.bind("<<TreeviewOpen>>", update_tree)
    t.bind("<Double-Button-1>", change_dir)
    t.bind("<ButtonRelease-1>", lambda event: mouserelease(event, lbinfo))
    t.bind("<B1-Motion>", movemouse)
    r.protocol("WM_DELETE_WINDOW", intercepte)
    r.mainloop()
    Cela évite les surprises non ? Qu'en pensez vous ?

    @+

    * Je n'utilise pas ttk et c'est la première fois que j'utilise Treeview alors soyez indulgents

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 459
    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 459
    Points : 37 054
    Points
    37 054
    Par défaut
    Salut,

    Merci pour l'exemple.

    Ce qui me gène dans ce genre de Widget c'est que ce n'est qu'une view, un 'cliché' du filesystem à un moment donné. A partir de là si on souhaite avoir une 'view' proche de la réalité on est obligé de 'surveiller' le fs.
    Cela passe soit par des modules plateforme dépendants soit par un thread et os.stat.
    Ce que nous montre le widget est effectivement une View, au sens représentation du modèle, qui sera "éventuellement consistante" avec la réalité car toujours un peu "en retard"...

    Windows Explorer est un exemple de "la chose" que je cherche à faire... et j'avais précisé que ce que je cherchais c'était le "visuel" et non les "actions" c'était pour rester dans la problématique Tkinter sans trop se poser des questions sur la "consistance".

    La lecture des sources de ttk ne m'a guère inspiré.

    Pour donner une illusion de mouvement sur les "boites" sélectionnées, je peux récupérer les bbox mais comment créer une boite à partir de là et surtout la faire bouger... sauf à "plonger" la treeview dans un canvas et faire un micmac entre les deux pour obtenir l'effet "visuel" recherché.
    Ce n'est pas impossible mais j'ai d'autres chats à fouetter sur ce projet pour l'instant.

    En tout cas merci beaucoup,
    - W

  5. #5
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Citation Envoyé par wiztricks Voir le message
    Pour donner une illusion de mouvement
    Changer le curseur ?

    @+

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 459
    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 459
    Points : 37 054
    Points
    37 054
    Par défaut
    Salut,
    Citation Envoyé par PauseKawa Voir le message
    Changer le curseur ?
    Je pensais à créer des bbox "au dessus" de celles de la sélection qui bougent avec le pointeur de la souris vers la destination.
    - W

  7. #7
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir wiztricks,

    En fait je pense que je ne vois pas l'effet' visuel demandé (Trop de temps sur la banquise sans doute et pas de poste MS @home).
    Un exemple ou une explication plus 'graphique' ?

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 459
    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 459
    Points : 37 054
    Points
    37 054
    Par défaut
    Salut,

    On fait une sélection dans le widget (une suite de <<ctrl-mb-1>> clicks)

    Pour dire qu'on souhaite déplacer la sélection "dans" un autre "nœud", on peut faire:
    1. copy de la selection (<<mb3-click>> => context menu => action : copy)
    2. on pointe (avec la souris) le noeud cible,
    3. <<mb3-click>> => context menu => action: move. La sélection se déplace (est renommée) sous le noeud cible.

    A la windows explorer, le dialogue est plus straightforward:
    1. on "drag" la sélection vers le noeud cible,
    2. on "release" le button et la sélection se déplace...

    - W

  9. #9
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour wiztricks,

    Une proposition.

    1) Sélectionner la source avec un bind
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def movemouse(event):
        n = t.focus()
        moveitem.set(n)
    ...
    moveitem = tkinter.StringVar()
    t.bind("<B1-Motion>", movemouse)
    2) Sélectionner la cible de même
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    def mouserelease(event, l):
        t = event.widget
        src = moveitem.get()
        if src and t.identify_region(event.x, event.y) == "tree":
            n = t.identify_row(event.y)
            #dest = t.set(n, "chemin")
    ...
    t.bind("<ButtonRelease-1>", lambda event: mouserelease(event, lbinfo))
    Jusque là c'est le code plus haut.
    3) Tester la cible (fichier/dossier)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
            if t.set(n, "type") == "fichier":
                target = t.parent(n)
            target = t.set(n, "chemin")
    4) Si la source est un fichier:
    le copier physiquement.
    faire un t.reattach(source, target, 0)
    Si la source est un dossier créer un node (.insert).
    Pour chaque children (t.get_children(n)) l’effacer du node, le copier physiquement, faire un insert.

    Cela te semble t'il assez visuel ?

    @+

  10. #10
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Plus 'visuel'

    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
    import tkinter
    from tkinter import ttk
    import time
     
    def setscrl(s, d, f):
        d = float(d)
        f = float(f)
        if d <= 0 and f >= 1:
            s.grid_remove()
        else:
            s.set(d, f)
            s.grid()
     
    def movemouse(event):
        t = event.widget
        n = t.selection()
        moveitem.set(t.focus())
        x = event.x
        y = event.y
        mitem.geometry("%dx%d+%d+%d" % (20, 10, x, y+90))
        mitem.deiconify()
        t['cursor'] = "hand2"
     
    def mouserelease(event):
        mitem.withdraw()
        t = event.widget
        t['cursor'] = save_cursor
        src = moveitem.get()
        if src and t.identify_region(event.x, event.y) == "tree":
            dest = t.identify_row(event.y)
            if t.set(dest, "type") == "file":
                dest = t.parent(dest)
            if t.set(src, "type") == "file":
                if t.parent(src) == dest:
                    moveitem.set("")
                    return
            else:
                if src == dest:
                    moveitem.set("")
                    return
            if t.set(src, "type") == "dir":
                id = t.insert(dest, "end", text=t.item(src, "text"),
                              values=[t.set(src, "chemin"), "dir"])
                for e in t.get_children(src):
                    txt = t.item(e, "text")
                    chm = t.set(e, "chemin")
                    tp = t.set(e, "type")
                    t.delete(e)
                    #t.update()
                    # pour simuler le mouvement
                    time.sleep(1)
                    elem = t.insert(id, "end", text=txt, values=[chm, tp])
                    t.see(elem)
                    t.update()
                t.delete(src)
            else:
                txt = t.item(src, "text")
                chm = t.set(src, "chemin")
                t.delete(src)
                time.sleep(1)
                id = t.insert(dest, "end", text=txt, values=[chm, "file"])
                t.see(id)
                t.update()
        moveitem.set("")
     
     
    r = tkinter.Tk()
    r.title("ttk.Treeview test")
    moveitem = tkinter.StringVar()
    vs = ttk.Scrollbar(orient="vertical")
    t = ttk.Treeview(r, columns=("chemin", "type"), selectmode="browse",
                     displaycolumns=("chemin"),
                     yscrollcommand=lambda deb, fin: setscrl(vs, deb, fin))
    vs["command"] = t.yview
    t.heading("chemin", text="chemin")
    t.insert("", 0, "dir1", text="directory 1", values=("d1", "dir"))
    t.insert("dir1", "end", "dir 1", text="file 1", values=("f1", "file"))
    id = t.insert("", "end", "dir2", text="directory 2", values=("d1", "dir"))
    t.insert("dir2", "end", text="dir 2", values=("d2", "dir"))
    t.insert(id, "end", text="dir 3", values=("d3", "dir"))
    t.insert(id, "end", text="file 2", values=("f1", "file"))
    t.grid(column=0, row=0, sticky="nswe")
    vs.grid(column=1, row=0, sticky="ns")
    save_cursor = t['cursor'] or ""
    mitem = tkinter.Toplevel(r)
    mitem.overrideredirect(1)
    mitem.config(bg="white")
    mitem.withdraw()
    t.bind("<ButtonRelease-1>", mouserelease)
    t.bind("<B1-Motion>", movemouse)
    r.mainloop()
    Sans doute a revoir mais c'est l'idée

    @+

    Edit: un update plus tard...

  11. #11
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Re,

    Il est aussi possible de faire 'voler' le Toplevel de la source vers la destination lors du déplacement.
    Mais bon: Ou arrêter le visuel et rester pratique ?

    @+

Discussions similaires

  1. Drag and drop élement d'un treeview vers une autre
    Par djo_matrix dans le forum ASP.NET
    Réponses: 0
    Dernier message: 14/04/2009, 12h02
  2. Drag and drop treeview
    Par exile69 dans le forum C#
    Réponses: 3
    Dernier message: 21/02/2008, 11h14
  3. [VB.net] Drag and drop dans une Treeview
    Par gégécap dans le forum Windows Forms
    Réponses: 2
    Dernier message: 19/10/2006, 11h05
  4. [Débutant(e)][VB.NET] Drag and drop entre 2 treeviews
    Par - Manuella Leray - dans le forum Windows Forms
    Réponses: 8
    Dernier message: 13/10/2005, 16h54
  5. [VB.NET] Microsoft TreeView drag and drop ?
    Par bigtoof dans le forum ASP.NET
    Réponses: 7
    Dernier message: 24/05/2004, 15h50

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