2013-04-24 30 views
29

Il mio obiettivo è aggiungere una barra di scorrimento verticale a una cornice con più etichette. La barra di scorrimento dovrebbe essere abilitata automaticamente non appena le etichette all'interno del fotogramma superano l'altezza del fotogramma. Dopo aver cercato attraverso, ho trovato il post utile this. Sulla base di quel post capisco che per ottenere ciò che voglio, (correggimi se sbaglio, sono un principiante) devo prima creare un Frame, quindi creare una Canvas all'interno di quella cornice e attaccare la barra di scorrimento a quella cornice pure. Successivamente, crea un altro frame e lo metti all'interno del canvas come oggetto window. Così, ho finalmente venire con questoPython Barra di scorrimento Tkinter per frame

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. sto facendo la cosa giusta? C'è un modo migliore/più intelligente per ottenere l'output che questo codice mi ha dato?
  2. Perché devo utilizzare il metodo della griglia? (Ho provato il metodo place, ma nessuna delle etichette appare sul canvas.)
  3. Cosa c'è di così speciale nell'usare anchor = 'nw' quando si crea una finestra su tela?

Si prega di mantenere la risposta semplice come io sono un principiante. Grazie per l'aiuto.

+1

Hai all'indietro nella sua interrogazione, se il codice è corretto a prima vista. È necessario creare una cornice, incorporarla nella tela, quindi collegare la barra di scorrimento alla tela. –

+2

possibile duplicato di [Aggiunta di una barra di scorrimento a una griglia di widget in Tkinter] (http://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-grid-of-widgets-in-tkinter) –

+0

@TrevorBoydSmith C'è un sacco di cose questo è un potenziale duplicato di, ma ho votato per chiuderlo come duplicato di un altro che sembra avere le migliori risposte: http://stackoverflow.com/questions/1873575/how-could -i-get-a-frame-con-a-scrollbar-in-tkinter – ArtOfWarfare

risposta

14

Sto facendo bene? C'è un modo migliore/più intelligente per ottenere l'output che questo codice mi ha dato?

In generale, sì, lo stai facendo bene. Tkinter non ha alcun contenitore scrollabile nativo oltre alla tela. Come puoi vedere, non è davvero così difficile da configurare. Come mostra il tuo esempio, bastano 5 o 6 righe di codice per farlo funzionare, a seconda di come contate le linee.

Perché devo utilizzare il metodo di griglia? (Ho provato il metodo posto, ma nessuna delle etichette compaiono sulla tela?)

Mi chiedi perché è necessario utilizzare griglia.Non è necessario utilizzare la griglia. Luogo, griglia e pacco possono essere tutti usati. È semplicemente che alcuni sono più adatti a particolari tipi di problemi. In questo caso sembra che tu stia creando una griglia reale: righe e colonne di etichette, quindi la griglia è la scelta naturale.

Cosa c'è di così speciale nell'usare anchor = 'nw' durante la creazione di una finestra su tela?

L'ancoraggio indica quale parte della finestra è posizionata rispetto alle coordinate fornite. Per impostazione predefinita, il centro della finestra verrà posizionato sulle coordinate. Nel caso del tuo codice sopra, vuoi che l'angolo in alto a sinistra ("nord-ovest") si trovi sulla coordinata.

28

Si prega di notare che il codice proposto è valida solo con Python 2

Ecco un esempio:

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 __name__ == "__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() 

Non ha ancora la rotella del mouse legato alla barra di scorrimento ma è possibile . Scorrere con la rotellina può diventare un po 'accidentato, però.

edit:

-1)
IMHO telai di scorrimento è un po 'difficile in Tkinter e non sembra essere fatto molto. Sembra che non ci sia un modo elegante per farlo.
Un problema con il tuo codice è che devi impostare manualmente la dimensione della tela: questo è ciò che il codice di esempio che ho pubblicato risolve.

a 2)
Stai parlando della funzione dati? Posto funziona anche per me. (In generale preferisco la griglia).

a 3)
Bene, posiziona la finestra sulla tela.

Una cosa che ho notato è che il tuo esempio gestisce lo scorrimento della rotellina del mouse di default mentre quello che ho postato non lo fa. Dovrà guardarlo un po 'di tempo.

+0

anche se non stai rispondendo alla mia domanda, grazie per il tuo esempio.Ma sono riluttante a scrivere tante righe di codice solo per un frame con scroll barra quando il mio intero script è più breve del tuo esempio. Infatti il ​​mio codice di esempio farà il trucco anche se non ho idea del motivo per cui non posso usare il metodo place. –

+1

@ChrisAung: La cosa bella di questa soluzione è che ha una classe riutilizzabile, 'VerticalScrolledFrame', che puoi usare come sostituto di qualsiasi' Frame'. Andando con il codice nella soluzione, è necessario riscrivere tutto il codice per ogni singolo 'Frame' che si desidera poter scorrere. Con questo codice, è quasi un rimpiazzo per "Frame". Quindi non essere riluttante a usarlo a causa del numero di righe. La sua soluzione è più codice se hai solo bisogno di un singolo frame scorrevole. Se hai bisogno di due, entrambe le tecniche prendono una quantità uguale di codice e la sua è più mantenibile (meno ridondante). – ArtOfWarfare

+0

Continua dall'alto: se hai bisogno di più di 2 frame scorrevoli, la sua soluzione richiede molto meno codice ed è molto più facile mantenere. Detto questo, non userei questa soluzione a causa delle avvertenze che ha. Inoltre, non mi piace il requisito di usare '.interior' - Vorrei un'interfaccia che renda' .interior' un dettaglio di implementazione invece di renderlo qualcosa che la persona che lo usa deve sapere. – ArtOfWarfare

2

Possiamo aggiungere barra di scorrimento anche senza usare Canvas. Ho letto in molti altri post che non possiamo aggiungere la barra di scorrimento verticale direttamente nel frame ecc. Ecc. Ma dopo aver fatto molti esperimenti ho trovato il modo di aggiungere la barra di scorrimento verticale e orizzontale :). Si prega di trovare sotto il codice che viene utilizzato per creare la barra di scorrimento in treeView e 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) 
+0

Non capisco l'uso di queste risposte di "Tkinter", "tk" e "ttk". Mi aspetterei solo uno di questi prefissi in ciascun esempio, ma il terzo ha tutti e tre. Cosa dà? – 4dummies

7

Si prega di vedere la mia classe che è una cornice scorrevole. La sua barra di scorrimento verticale è associata anche all'evento <Mousewheel>. Quindi, tutto quello che devi fare è creare una cornice, riempirla con i widget nel modo che preferisci, e quindi rendere questo frame un figlio del mio ScrolledWindow.scrollwindow. Sentiti libero di chiedere se qualcosa non è chiaro.

usato un sacco da @ risposte Brayan Oakley per chiudere a questa discussione

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()) 
+1

piccolo bug, la tua ultima riga dovrebbe leggere 'self.canv.config (height = ...)' –

+0

@AdamSmith Grazie, aggiunta una correzione –

+0

Cosa intendi con "fai di questo frame un figlio della mia ScrolledWindow.scrollwindow". Puoi fare un esempio? – Jacob

Problemi correlati