web-dev-qa-db-fra.com

Barre de défilement Tkinter pour cadre

Mon objectif est d’ajouter une barre de défilement verticale à un cadre comportant plusieurs étiquettes. La barre de défilement doit automatiquement être activée dès que les étiquettes à l'intérieur du cadre dépassent sa hauteur. Après avoir cherché, j'ai trouvé this post utile. Sur la base de ce post, je comprends que pour réaliser ce que je veux, (corrigez-moi si je me trompe, je suis débutant), je dois d'abord créer un Frame, puis créer un Canvas à l'intérieur de ce cadre et collez la barre de défilement sur ce cadre également. Après cela, créez un autre cadre et placez-le à l'intérieur de la toile en tant qu'objet window. Donc, je viens enfin avec ceci:

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 le fais bien? Existe-t-il un moyen meilleur/plus intelligent d’atteindre 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 de placement, mais aucune des étiquettes n'apparaît sur la toile.)
  3. Qu'a-t-il de si spécial avec anchor='nw' lors de la création d'une fenêtre sur une toile?

Veuillez garder votre réponse simple, car je suis un débutant.

52
Chris Aung

Est-ce que je fais bien les choses? Existe-t-il un moyen meilleur/plus intelligent d'obtenir le résultat obtenu par ce code?

De manière générale, oui, vous le faites bien. Tkinter n'a pas d'autre conteneur natif que le canevas. Comme vous pouvez le constater, ce n’est vraiment pas si difficile à mettre en place. Comme votre exemple le montre, 5 à 6 lignes de code suffisent pour le faire fonctionner, en fonction du nombre de lignes.

Pourquoi dois-je utiliser la méthode grid? (J'ai essayé la méthode place, mais aucune des étiquettes n'apparaît sur le canevas?)

Vous demandez pourquoi vous devez utiliser la grille. Il n'y a aucune obligation d'utiliser la grille. Le lieu, la grille et le sac peuvent tous être utilisés. C'est simplement que certains sont plus naturellement adaptés à des types de problèmes particuliers. Dans ce cas, il semble que vous créiez une grille réelle - des rangées et des colonnes d'étiquettes - le choix de la grille est donc naturel.

Quelle est la particularité d'utiliser anchor = 'nw' lors de la création d'une fenêtre sur une toile?

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

22
Bryan Oakley

Veuillez noter que le code proposé est uniquement valide avec Python 2

Voici un exemple:

from Tkinter import *   # from x import * is bad practice
from ttk import *

# http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame

class VerticalScrolledFrame(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):
        Frame.__init__(self, parent, *args, **kw)            

        # create a canvas object and a vertical scrollbar for scrolling it
        vscrollbar = Scrollbar(self, orient=VERTICAL)
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
        canvas = 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 = 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 __== "__main__":

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


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

    app = SampleApp()
    app.mainloop()

La molette de la souris n’est pas encore reliée à la barre de défilement, mais c’est possible. Faire défiler avec la molette peut cependant devenir un peu cahoteux.

modifier:

à 1)
Le défilement IMHO des cadres est un peu délicat dans Tkinter et ne semble pas être fait beaucoup. Il semble qu'il n'y ait pas de moyen élégant de le faire.
Un problème avec votre code est que vous devez définir la taille de la toile manuellement - c'est ce que l'exemple de code que j'ai posté résout.

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

à 3)
Eh bien, il positionne la fenêtre sur la toile.

Une chose que j'ai remarquée est que votre exemple gère le défilement de la molette de la souris par défaut, contrairement à celui que j'ai posté. Devra regarder cela quelque temps.

38
Gonzo

S'il vous plaît voir ma classe qui est un cadre défilable. Sa barre de défilement verticale est liée à <Mousewheel> événement aussi. Donc, tout ce que vous avez à faire est de créer un cadre, de le remplir avec les widgets comme vous le souhaitez, puis de faire de ce cadre un enfant de mon ScrolledWindow.scrollwindow. N'hésitez pas à demander si quelque chose n'est pas clair.

@ Brayan Oakley a utilisé beaucoup de réponses pour répondre à ces questions

class ScrolledWindow(tk.Frame):
    """
    1. Master widget gets scrollbars and a canvas. Scrollbars are connected 
    to canvas scrollregion.

    2. self.scrollwindow is created and inserted into canvas

    Usage Guideline:
    Assign any widgets as children of <ScrolledWindow instance>.scrollwindow
    to get them inserted into canvas

    __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs)
    docstring:
    Parent = master of scrolled window
    canv_w - width of canvas
    canv_h - height of canvas

    """


    def __init__(self, parent, canv_w = 400, canv_h = 400, *args, **kwargs):
        """Parent = master of scrolled window
        canv_w - width of canvas
        canv_h - height of canvas

       """
        super().__init__(parent, *args, **kwargs)

        self.parent = parent

        # creating a scrollbars
        self.xscrlbr = ttk.Scrollbar(self.parent, orient = 'horizontal')
        self.xscrlbr.grid(column = 0, row = 1, sticky = 'ew', columnspan = 2)         
        self.yscrlbr = ttk.Scrollbar(self.parent)
        self.yscrlbr.grid(column = 1, row = 0, sticky = 'ns')         
        # creating a canvas
        self.canv = tk.Canvas(self.parent)
        self.canv.config(relief = 'flat',
                         width = 10,
                         heigh = 10, bd = 2)
        # placing a canvas into frame
        self.canv.grid(column = 0, row = 0, sticky = 'nsew')
        # accociating scrollbar comands to canvas scroling
        self.xscrlbr.config(command = self.canv.xview)
        self.yscrlbr.config(command = self.canv.yview)

        # creating a frame to inserto to canvas
        self.scrollwindow = ttk.Frame(self.parent)

        self.canv.create_window(0, 0, window = self.scrollwindow, anchor = 'nw')

        self.canv.config(xscrollcommand = self.xscrlbr.set,
                         yscrollcommand = self.yscrlbr.set,
                         scrollregion = (0, 0, 100, 100))

        self.yscrlbr.lift(self.scrollwindow)        
        self.xscrlbr.lift(self.scrollwindow)
        self.scrollwindow.bind('<Configure>', self._configure_window)  
        self.scrollwindow.bind('<Enter>', self._bound_to_mousewheel)
        self.scrollwindow.bind('<Leave>', self._unbound_to_mousewheel)

        return

    def _bound_to_mousewheel(self, event):
        self.canv.bind_all("<MouseWheel>", self._on_mousewheel)   

    def _unbound_to_mousewheel(self, event):
        self.canv.unbind_all("<MouseWheel>") 

    def _on_mousewheel(self, event):
        self.canv.yview_scroll(int(-1*(event.delta/120)), "units")  

    def _configure_window(self, event):
        # update the scrollbars to match the size of the inner frame
        size = (self.scrollwindow.winfo_reqwidth(), self.scrollwindow.winfo_reqheight())
        self.canv.config(scrollregion='0 0 %s %s' % size)
        if self.scrollwindow.winfo_reqwidth() != self.canv.winfo_width():
            # update the canvas's width to fit the inner frame
            self.canv.config(width = self.scrollwindow.winfo_reqwidth())
        if self.scrollwindow.winfo_reqheight() != self.canv.winfo_height():
            # update the canvas's width to fit the inner frame
            self.canv.config(height = self.scrollwindow.winfo_reqheight())
10
Mikhail T.

Nous pouvons ajouter une barre de défilement même sans utiliser Canvas. Je l'ai lu dans de nombreux autres articles, nous ne pouvons pas ajouter de barre de défilement verticale dans le cadre directement, etc. etc. Mais après plusieurs expériences, nous avons trouvé un moyen d'ajouter une barre de défilement verticale et horizontale :). Veuillez trouver ci-dessous le code utilisé pour créer une barre de défilement dans treeView et frame.

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)
4