2013-04-24 4 views
29

Моя цель - добавить вертикальную полосу прокрутки к кадру, в котором есть несколько надписей. Полоса прокрутки должна автоматически включаться, как только метки внутри рамки превышают высоту кадра. После поиска я нашел this полезный пост. Основываясь на этом посту, я понимаю, что для достижения того, чего я хочу (исправьте меня, если я ошибаюсь, я начинаю), мне нужно сначала создать кадр, а затем создать холст внутри этого фрейма и наклеить полосу прокрутки на это также. После этого создайте другой фрейм и поместите его внутри холста в качестве объекта окна. Итак, я наконец-то с этимProllon Tkinter scrollbar для рамки

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. я делаю это правильно? Есть ли лучший/более умный способ достичь результата, который мне дал этот код?
  2. Почему я должен использовать метод сетки? (Я попробовал метод, но ни один из ярлыков не появился на холсте.)
  3. Что такого особенного в использовании anchor = 'nw' при создании окна на холсте?

Пожалуйста, держите свой ответ простым, поскольку я новичок. Спасибо за вашу помощь.

+1

Вы его назад в вашем вопросе, хотя код выглядит правильно на первый взгляд. Вы должны создать фрейм, вставить его в холст, а затем прикрепить полосу прокрутки к холсту. –

+2

Возможный дубликат [Добавление полосы прокрутки в сетку виджетов в Tkinter] (http://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-grid-of-widgets-in-tkinter) –

+0

@TrevorBoydSmith Есть много вещей, это потенциальный дубликат, но я голосовал, чтобы закрыть это как дубликат другого, который, кажется, имеет лучшие ответы: http://stackoverflow.com/questions/1873575/how-could -i-get-a-frame-with-a-scrollbar-in-tkinter – ArtOfWarfare

ответ

14

Я делаю это правильно? Есть ли лучший/более умный способ достичь результата, который мне дал этот код?

В общем, да, вы делаете это правильно. У Tkinter нет собственного прокручиваемого контейнера, кроме холста. Как вы можете видеть, это действительно не так сложно настроить. Как показывает ваш пример, для его работы требуется всего 5 или 6 строк кода - в зависимости от того, как вы подсчитываете строки.

Почему я должен использовать метод сетки? (Я попробовал метод место, но ни один из лейблов не появляются на холсте?)

Вы спрашиваете о том, почему вы должны использовать сетку.Нет необходимости использовать сетку. Место, сетка и упаковка могут использоваться. Просто некоторые из них более естественно подходят для конкретных типов проблем. В этом случае похоже, что вы создаете фактическую сетку - строки и столбцы меток, поэтому сетка является естественным выбором.

Что такого особенного в использовании anchor = 'nw' при создании окна на холсте?

Анкер сообщает вам, какая часть окна расположена в координатах, которые вы даете. По умолчанию центр окна будет помещен в координату. В случае вашего кода выше вы хотите, чтобы верхний левый («северо-западный») угол находился в координате.

28

Пожалуйста, обратите внимание, что предлагаемый код действителен только с Python 2

Вот пример:

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

Он еще не имеет колесико мыши привязанный к скроллинга, но можно , Однако прокрутка с помощью колеса может немного ухабироваться.

редактировать:

к 1)
ИМХО прокрутке кадров несколько сложнее в Tkinter и, кажется, не будет сделано много. Кажется, нет элегантного способа сделать это.
Одна проблема с вашим кодом заключается в том, что вы должны вручную установить размер холста - это то, что решает код примера, который я разместил.

до 2)
Вы говорите о функции данных? Место работает и для меня. (В общем, я предпочитаю сетку).

до 3)
Ну, он позиционирует окно на холсте.

Одна вещь, которую я заметил, это то, что ваш пример обрабатывает прокрутку колеса мыши по умолчанию, а тот, который я разместил, не работает. Придется взглянуть на это некоторое время.

+0

, хотя вы не отвечаете на мой вопрос, спасибо за ваш пример. Но я не хочу писать много строк кода только для рамки со свитком бар, когда весь мой скрипт короче вашего примера. На самом деле мой пример кода будет делать трюк, хотя я не знаю, почему я не могу использовать метод места. –

+1

@ChrisAung: Хорошая вещь об этом решении состоит в том, что он имеет многоразовый класс 'VerticalScrolledFrame', который вы можете использовать в качестве замены для любого' Frame'. Идя с кодом в вашем решении, вам нужно переписать весь этот код для каждого отдельного «кадра», который вы хотите прокручивать. С помощью этого кода это почти замена для «Frame». Поэтому не стоит отказываться использовать его из-за количества строк. Его решение больше кода, если вам нужен только один прокручиваемый кадр. Если вам нужно два, обе методики принимают одинаковое количество кода, а его более ремонтопригодный (менее избыточный). – ArtOfWarfare

+0

Продолжая сверху: если вам нужно более двух прокручиваемых фреймов, его решение занимает гораздо меньше кода и намного проще поддерживать. Сказав все это, я не буду использовать это решение из-за его оговорок. Кроме того, мне не нравится требование использовать '.interior' - мне нужен интерфейс, который делает' .interior' деталь реализации вместо того, чтобы делать что-то, о чем должен знать человек, использующий его. – ArtOfWarfare

2

Мы можем добавить полосу прокрутки даже без использования холста. Я прочитал его во многих других сообщениях, мы не можем добавить вертикальную полосу прокрутки в рамку напрямую и т. Д. Но после многих экспериментов выяснилось, как добавить вертикальную, а также горизонтальную полосу прокрутки :). Ниже приведен код, который используется для создания полосы прокрутки в treeView и фрейме.

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

Я не понимаю эти ответы «использование Tkinter», «tk» и «ttk». Я ожидал бы только один такой префикс в каждом примере, но третий имеет все три. Что дает? – 4dummies

7

См. Мой класс, который является прокручиваемой рамкой. Вертикальная полоса прокрутки привязана к событию <Mousewheel>. Итак, все, что вам нужно сделать, это создать фрейм, заполнить его виджетами так, как вам нравится, а затем сделать этот кадр дочерним для моего ScrolledWindow.scrollwindow. Не стесняйтесь спрашивать, что-то неясно.

использовано много от @ Brayan Oakley ответов закрыть на это ставит под сомнение

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

маленькая ошибка, ваша последняя строка должна читать 'self.canv.config (height = ...)' –

+0

@AdamSmith Спасибо, добавлена ​​коррекция –

+0

Что значит «сделайте этот кадр дочерним элементом моего ScrolledWindow.scrollwindow». Можете ли вы привести пример? – Jacob

Смежные вопросы