2013-04-24 12 views
29

Moim celem jest dodanie pionowego paska przewijania do ramki zawierającej kilka etykiet. Pasek przewijania powinien być automatycznie włączony, gdy tylko etykiety wewnątrz ramki przekraczają wysokość ramki. Po przeszukaniu znalazłem przydatny post. Na podstawie tego posta rozumiem, że aby osiągnąć to, czego chcę, (popraw mnie, jeśli się mylę, jestem początkującym), najpierw muszę utworzyć ramkę, następnie utworzyć płótno wewnątrz tej ramki i przykleić pasek przewijania do tego rama również. Następnie utwórz kolejną ramkę i umieść ją wewnątrz płótna jako obiekt okna. Więc w końcu wymyślić tegoPasek przewijania Python Tkinter do ramki

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. robię to dobrze? Czy istnieje lepszy/mądrzejszy sposób osiągnięcia wyników, które dostarczył mi ten kod?
  2. Dlaczego muszę używać metody siatkowej? (Próbowałem metody place, ale żadna z etykiet nie pojawia się na płótnie).
  3. Co jest takiego specjalnego w używaniu anchor = 'nw' podczas tworzenia okna na płótnie?

Proszę zachować prostą odpowiedź, ponieważ jestem początkującym. Dzięki za pomoc.

+1

Trzeba go do tyłu w swoim pytaniu, choć poprawny kod wygląda na pierwszy rzut oka. Musisz utworzyć ramkę, osadzić ją w obszarze roboczym, a następnie dołączyć pasek przewijania do obszaru roboczego. –

+2

możliwy duplikat [Dodawanie paska przewijania do siatki widżetów w Tkinter] (http://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-grid-of-widgets-in-tkinter) –

+0

@TrevorBoydSmith Istnieje wiele rzeczy, które są potencjalnym duplikatem, ale głosowałem, aby zamknąć to jako duplikat innego, który wydaje się mieć najlepsze odpowiedzi: http://stackoverflow.com/questions/1873575/how-could -i-get-a-frame-with-a-scrollbar-in-tkinter – ArtOfWarfare

Odpowiedz

14

Czy robię to dobrze? Czy istnieje lepszy/mądrzejszy sposób na uzyskanie wyników, które dał mi ten kod?

Ogólnie rzecz biorąc, tak, robisz to dobrze. Tkinter nie ma rodzimego przewijanego pojemnika innego niż płótno. Jak widać, nie jest to takie trudne. Jak pokazuje Twój przykład, wystarczy tylko 5 lub 6 linii kodu - w zależności od tego, jak liczyć linie.

Dlaczego muszę użyć metody siatki? (Próbowałem metody miejsce, ale żaden z etykietami pojawiają się na płótnie?)

Pytasz dlaczego trzeba użyć siatki.Nie ma wymogu korzystania z siatki. Miejsce, siatka i opakowanie mogą być użyte. Po prostu niektóre są bardziej naturalnie dostosowane do określonych rodzajów problemów. W tym przypadku wygląda na to, że tworzysz rzeczywistą siatkę - rzędy i kolumny etykiet - więc siatka jest naturalnym wyborem.

Co jest takiego specjalnego w używaniu anchor = 'nw' podczas tworzenia okna na płótnie?

Zakotwiczenie mówi, która część okna jest ustawiona na współrzędnych, które podajesz. Domyślnie środek okna zostanie umieszczony we współrzędnych. W powyższym kodzie lewy górny róg ("północny zachód") ma być współrzędną.

28

Zwróć uwagę, że proponowany kod jest ważny tylko z Python 2

Oto przykład:

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

Nie ma jeszcze kółka myszy związany z paska przewijania, ale jest to możliwe . Przewijanie za pomocą koła może być nieco wyboiste.

edit:

do 1)
IMHO przewijania klatek jest nieco kłopotliwe w Tkinter i nie wydaje się, aby to zrobić dużo. Wygląda na to, że nie ma na to eleganckiego sposobu.
Jednym z problemów z kodem jest to, że musisz ręcznie ustawić rozmiar płótna - to właśnie rozwiązał przykładowy kod, który wysłałem.

do 2) Mówisz o funkcji danych? Miejsce też działa dla mnie. (Generalnie wolę siatkę).

do 3) Cóż, umieszcza okno na płótnie.

Jedną z rzeczy, którą zauważyłem, jest to, że twój przykład domyślnie obsługuje przewijanie kółkiem myszy, podczas gdy ten, który opublikowałem, nie działa. Będę musiał to kiedyś sprawdzić.

+0

mimo, że nie odpowiadasz na moje pytanie, dziękuję za twój przykład.Ale niechętnie piszę tyle linijek kodu tylko dla ramki z przewijaniem pasek, gdy cały mój skrypt jest krótszy niż twój przykład. W rzeczywistości mój przykładowy kod będzie działał, chociaż nie mam pojęcia, dlaczego nie mogę użyć metody miejsca. –

+1

@ChrisAung: Zaletą tego rozwiązania jest to, że ma klasę wielokrotnego użytku "VerticalScrolledFrame", której można użyć jako zamiennika dowolnego 'Frame'. Przechodząc z kodem w twoim rozwiązaniu, musisz przepisać cały ten kod dla każdego "ramki", którą chcesz przewinąć. Z tym kodem jest to prawie zamiennik dla 'Frame'. Nie należy więc niechętnie używać go z powodu liczby linii. Jego rozwiązanie to więcej kodu, jeśli potrzebujesz tylko przewijanej ramki. Jeśli potrzebujesz dwóch, obie techniki mają taką samą ilość kodu, a jego jest łatwiejsza w utrzymaniu (mniej zbędna). – ArtOfWarfare

+0

Kontynuując od góry: Jeśli potrzebujesz więcej niż 2 przewijanych ramek, jego rozwiązanie zajmuje znacznie mniej kodu i jest o wiele łatwiejsze utrzymać. Powiedziawszy to wszystko, nie użyłbym tego rozwiązania ze względu na zastrzeżenia, które ma. Nie podoba mi się również wymóg używania '.interior' - chciałbym mieć interfejs, który sprawia, że' .interior' jest szczegółem implementacji, zamiast tworzyć coś, o czym musi wiedzieć osoba, która go używa. – ArtOfWarfare

2

Możemy dodać pasek przewijania, nawet bez użycia Canvas. Przeczytałem to w wielu innych postach, nie możemy dodać pionowego paska przewijania bezpośrednio w ramce itp. Ale po wykonaniu wielu eksperymentów dowiedzieliśmy się, jak dodać pionowy i poziomy pasek przewijania :). Poniżej znajdziesz kod, który służy do tworzenia paska przewijania w treeView i ramce.

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

Nie rozumiem tych odpowiedzi "użycie" Tkinter "," tk "i" ttk ". Spodziewam się tylko jednego takiego prefiksu w każdym przykładzie, ale trzeci ma wszystkie trzy. Co daje? – 4dummies

7

Proszę zobaczyć moją klasę, która jest przewijaną ramką. To pionowy pasek przewijania jest również powiązany z wydarzeniem <Mousewheel>. Wszystko, co musisz zrobić, to stworzyć ramkę, wypełnić ją widżetami, tak jak lubisz, a następnie uczynić tę ramkę dzieckiem mojego ScrolledWindow.scrollwindow. Możesz zapytać, czy coś jest niejasne.

Używane dużo od @ odpowiedzi Brayan Oakley, aby zamknąć to kwestionuje

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

mały błąd, twoja ostatnia linia powinna brzmieć "self.canv.config (height = ...)" –

+0

@AdamSmith Dzięki, dodała korektę –

+0

Co masz na myśli, "spraw, aby ta ramka była dzieckiem mojego ScrolledWindow.scrollwindow". Czy możesz dać przykład? – Jacob