92 votes

Barre de défilement Tkinter pour le cadre

Mon objectif est d'ajouter une barre de défilement verticale à un cadre contenant plusieurs étiquettes. La barre de défilement doit s'activer automatiquement dès que les étiquettes à l'intérieur du cadre dépassent la hauteur du cadre. Après avoir cherché, j'ai trouvé cette poste utile. En me basant sur cet article, je comprends que pour réaliser ce que je veux, (corrigez-moi si je me trompe, je suis un débutant), je dois créer un Frame d'abord, puis créer un Canvas à l'intérieur de ce cadre et coller la barre de défilement à ce cadre également. Ensuite, créez un autre cadre et placez-le à l'intérieur du canevas en tant qu'objet fenêtre. Voici ce que j'ai obtenu :

from Tkinter import *

def data():
    for i in range(50):
       Label(frame,text=i).grid(row=i,column=0)
       Label(frame,text="my text"+str(i)).grid(row=i,column=1)
       Label(frame,text="..........").grid(row=i,column=2)

def myfunction(event):
    canvas.configure(scrollregion=canvas.bbox("all"),width=200,height=200)

root=Tk()
sizex = 800
sizey = 600
posx  = 100
posy  = 100
root.wm_geometry("%dx%d+%d+%d" % (sizex, sizey, posx, posy))

myframe=Frame(root,relief=GROOVE,width=50,height=100,bd=1)
myframe.place(x=10,y=10)

canvas=Canvas(myframe)
frame=Frame(canvas)
myscrollbar=Scrollbar(myframe,orient="vertical",command=canvas.yview)
canvas.configure(yscrollcommand=myscrollbar.set)

myscrollbar.pack(side="right",fill="y")
canvas.pack(side="left")
canvas.create_window((0,0),window=frame,anchor='nw')
frame.bind("<Configure>",myfunction)
data()
root.mainloop()
  1. Est-ce que je fais bien les choses ? Existe-t-il une meilleure façon d'obtenir le résultat que ce code m'a donné ?
  2. Pourquoi dois-je utiliser la méthode de la grille ? (J'ai essayé la méthode place, mais aucune des étiquettes n'apparaît sur le canevas).
  3. Qu'y a-t-il de si spécial dans l'utilisation de anchor='nw' lors de la création d'une fenêtre sur le canevas ?

Merci de rester simple dans votre réponse, car je suis un débutant.

61voto

Gonzo Points 594

Voici un exemple de code adapté de la page VerticalScrolledFrame sur la page aujourd'hui disparue Wiki Tkinter qui a été modifié pour fonctionner avec Python 2.7 et 3+.

try:  # Python 2
    import tkinter as tk
    import tkinter.ttk as ttk
    from tkinter.constants import *
except ImportError:  # Python 2
    import Tkinter as tk
    import ttk
    from tkinter.constants import *

# Based on
#   https://web.archive.org/web/20170514022131id_/http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame

class VerticalScrolledFrame(ttk.Frame):
    """A pure Tkinter scrollable frame that actually works!
    * Use the 'interior' attribute to place widgets inside the scrollable frame.
    * Construct and pack/place/grid normally.
    * This frame only allows vertical scrolling.
    """
    def __init__(self, parent, *args, **kw):
        ttk.Frame.__init__(self, parent, *args, **kw)

        # Create a canvas object and a vertical scrollbar for scrolling it.
        vscrollbar = ttk.Scrollbar(self, orient=VERTICAL)
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
        canvas = tk.Canvas(self, bd=0, highlightthickness=0,
                           yscrollcommand=vscrollbar.set)
        canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
        vscrollbar.config(command=canvas.yview)

        # Reset the view
        canvas.xview_moveto(0)
        canvas.yview_moveto(0)

        # Create a frame inside the canvas which will be scrolled with it.
        self.interior = interior = ttk.Frame(canvas)
        interior_id = canvas.create_window(0, 0, window=interior,
                                           anchor=NW)

        # Track changes to the canvas and frame width and sync them,
        # also updating the scrollbar.
        def _configure_interior(event):
            # Update the scrollbars to match the size of the inner frame.
            size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
            canvas.config(scrollregion="0 0 %s %s" % size)
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # Update the canvas's width to fit the inner frame.
                canvas.config(width=interior.winfo_reqwidth())
        interior.bind('<Configure>', _configure_interior)

        def _configure_canvas(event):
            if interior.winfo_reqwidth() != canvas.winfo_width():
                # Update the inner frame's width to fill the canvas.
                canvas.itemconfigure(interior_id, width=canvas.winfo_width())
        canvas.bind('<Configure>', _configure_canvas)

if __name__ == "__main__":

    class SampleApp(tk.Tk):
        def __init__(self, *args, **kwargs):
            root = tk.Tk.__init__(self, *args, **kwargs)

            self.frame = VerticalScrolledFrame(root)
            self.frame.pack()
            self.label = ttk.Label(self, text="Shrink the window to activate the scrollbar.")
            self.label.pack()
            buttons = []
            for i in range(10):
                buttons.append(ttk.Button(self.frame.interior, text="Button " + str(i)))
                buttons[-1].pack()

    app = SampleApp()
    app.mainloop()

La molette de la souris n'est pas encore liée à la barre de défilement, mais c'est possible. Le défilement à l'aide de la molette peut cependant être un peu irrégulier.

éditer :

à 1)
Le défilement des cadres est quelque peu délicat dans Tkinter et ne semble pas être utilisé souvent. Il semble qu'il n'y ait pas de manière élégante de le faire.
L'un des problèmes de votre code est que vous devez définir manuellement la taille du canevas - c'est ce que le code d'exemple que j'ai posté résout.

à 2)
Vous parlez de la fonction de données ? Place fonctionne pour moi aussi. (En général, je préfère la grille).

à 3)
Il positionne la fenêtre sur la toile.

Une chose que j'ai remarquée, c'est que votre exemple gère par défaut le défilement de la molette de la souris, alors que celui que j'ai posté ne le fait pas. Il faudra y jeter un coup d'œil un de ces jours.

36voto

Bryan Oakley Points 63365

"Est-ce que je m'y prends bien ? Y a-t-il une meilleure façon d'obtenir le résultat que ce code m'a donné ?

D'une manière générale, oui, vous faites bien les choses. Tkinter n'a pas de conteneur défilable natif autre que le canevas. Comme vous pouvez le voir, ce n'est pas si difficile à mettre en place. Comme le montre votre exemple, il ne faut que 5 ou 6 lignes de code pour que cela fonctionne - selon la façon dont vous comptez les lignes.

"Pourquoi dois-je utiliser la méthode de la grille (j'ai essayé la méthode de l'emplacement, mais aucune des étiquettes n'apparaît sur le canevas) ?

Vous demandez pourquoi vous devez utiliser la grille. Il n'est pas nécessaire d'utiliser une grille. Place, grid et pack peuvent tous être utilisés. C'est simplement que certains sont plus naturellement adaptés à des types de problèmes particuliers. Dans le cas présent, il semble que vous créiez une véritable grille - des lignes et des colonnes d'étiquettes - et la grille est donc le choix naturel.

"Qu'y a-t-il de spécial dans l'utilisation de anchor='nw' lors de la création d'une fenêtre sur le canevas ?

L'ancre vous indique quelle partie de la fenêtre est positionnée aux coordonnées que vous avez indiquées. Par défaut, le centre de la fenêtre sera placé à cette coordonnée. Dans le cas de votre code ci-dessus, vous voulez que le coin supérieur gauche ("nord-ouest") soit à la coordonnée.

4voto

mp035 Points 153

Pour ceux qui tomberaient sur ce document (comme ce fut le cas lorsque j'ai cherché ma propre liste), je tiens à jour une liste exactement dans ce but à l'adresse suivante https://gist.github.com/mp035/9f2027c3ef9172264532fcd6262f3b01 Il prend en charge les molettes de défilement pour différents systèmes d'exploitation, est commenté et comporte une démo intégrée dans le fichier.

2voto

Aditi Raghuvanshi Points 110

Nous pouvons a

f = Tkinter.Frame(self.master,width=3)
f.grid(row=2, column=0, columnspan=8, rowspan=10, pady=30, padx=30)
f.config(width=5)
self.tree = ttk.Treeview(f, selectmode="extended")
scbHDirSel =tk.Scrollbar(f, orient=Tkinter.HORIZONTAL, command=self.tree.xview)
scbVDirSel =tk.Scrollbar(f, orient=Tkinter.VERTICAL, command=self.tree.yview)
self.tree.configure(yscrollcommand=scbVDirSel.set, xscrollcommand=scbHDirSel.set)           
self.tree["columns"] = (self.columnListOutput)
self.tree.column("#0", width=40)
self.tree.heading("#0", text='SrNo', anchor='w')
self.tree.grid(row=2, column=0, sticky=Tkinter.NSEW,in_=f, columnspan=10, rowspan=10)
scbVDirSel.grid(row=2, column=10, rowspan=10, sticky=Tkinter.NS, in_=f)
scbHDirSel.grid(row=14, column=0, rowspan=2, sticky=Tkinter.EW,in_=f)
f.rowconfigure(0, weight=1)
f.columnconfigure(0, weight=1)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X