2013-04-24 135 views
29

我的目標是添加一個垂直滾動條到框架中,框架中有幾個標籤。一旦框架內的標籤超過框架的高度,滾動條應自動啓用。經過搜索,我發現this有用的職位。基於這篇文章,我明白,爲了實現我想要的,(糾正我,如果我錯了,我是一個初學者)我必須先創建一個框架,然後在該框架內創建一個畫布,並將滾動條粘到那框架以及。之後,創建另一個框架並將其作爲窗口對象放在畫布內。所以,我終於想出這個框架的Python Tkinter滾動條

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

你擁有了它向後你的問題,儘管代碼乍看上去是正確的。您必須創建一個框架,將其嵌入到畫布中,然後將滾動條附加到畫布上。 – 2013-04-24 11:07:37

+2

[將滾動條添加到Tkinter中的窗口小部件的網格]的可能的重複(http://stackoverflow.com/questions/3085696/adding-a-scrollbar-to-a-grid-of-widgets-in-tkinter) – 2013-08-26 15:56:49

+0

@TrevorBoydSmith有很多東西這是一個潛在的重複,但我投票關閉這是一個不同的似乎有最好的答案的副本:http://stackoverflow.com/questions/1873575/how-could -i-get-a-frame-with-a-scrollbar-in-tkinter – ArtOfWarfare 2015-05-03 15:59:34

回答

14

我在做對吧?有沒有更好/更聰明的方法來實現這個代碼給我的輸出?

一般來說,是的,你做對了。除了畫布以外,Tkinter沒有本地可滾動容器。正如你所看到的,設置起來並不難。正如你的例子所示,它只需要5或6行代碼就可以工作 - 這取決於你如何計算行數。

我爲什麼非要用網格法?(我想地方的方法,但沒有一個標籤出現在畫布上的?)

你問爲什麼你必須使用網格。沒有要求使用網格。地方,網格和包裝都可以使用。只是有些更適合特定類型的問題。在這種情況下,它看起來像是在創建一個實際的網格 - 標籤的行和列 - 所以網格是自然選擇。

在canvas上創建窗口時,使用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的似乎並沒有做很多。似乎沒有優雅的方式來做到這一點。
您的代碼的一個問題是您必須手動設置畫布大小 - 這就是我發佈的示例代碼解決的問題。

to 2)
你說的是數據功能? Place也適合我。 (通常我更喜歡網格)。

to 3)
好吧,它將窗口放置在畫布上。

我注意到的一件事是,您的示例默認處理鼠標滾輪滾動,而我發佈的不是。將不得不看一段時間。

+0

即使你沒有回答我的問題,謝謝你的例子。但我不願意爲一個帶有滾動的框架編寫多行代碼當我的整個腳本比你的例子短。實際上我的示例代碼會做的伎倆,但我不知道爲什麼我不能使用place方法。 – 2013-04-25 08:29:58

+1

@ChrisAung:這個解決方案的好處在於它有一個可重用的類「VerticalScrolledFrame」,您可以用它來替代任何「Frame」。在解決方案中使用代碼時,需要爲每個想要滾動的「Frame」重寫所有代碼。有了這個代碼,它幾乎可以替代'Frame'。所以不要因爲線數而不願意使用它。如果你只需要一個可滾動的框架,他的解決方案是更多的代碼。如果你需要兩種技術,兩種技術都需要相同數量的代碼,而且他的代碼更易於維護(減少冗餘)。 – ArtOfWarfare 2015-05-02 15:15:04

+0

從上面繼續:如果你需要2個以上的可滾動框架,他的解決方案需要的代碼少得多,而且更容易保持。說了這麼多,我不會因爲它的警告而使用這個解決方案。此外,我不喜歡使用'.interior'的要求 - 我想要一個使'.interior'成爲實現細節的接口,而不是使用它的人必須知道的東西。 – ArtOfWarfare 2015-05-02 15:18:28

2

即使不使用畫布,我們也可以添加滾動條。我在其他許多帖子中看過它,我們不能直接在框架中添加垂直滾動條等等。但經過多次實驗發現添加垂直和水平滾動條的方式:)。請在下面的代碼中找到用於在treeView和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

我不明白這些答案使用「Tkinter」,「tk」和「ttk」。我希望在每個例子中只有一個這樣的前綴,但第三個都有三個。是什麼賦予了? – 4dummies 2017-12-16 22:14:34

7

請看我的課是一個可滾動的框架。它的垂直滾動條也被綁定到<Mousewheel>事件。所以,你所要做的就是創建一個框架,用你喜歡的方式填充小部件,然後將這個框架設爲我的ScrolledWindow.scrollwindow的子項。隨意詢問是否有不清楚的地方。

用過很多來自@ Brayan奧克利的回答接近這個質疑

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

小bug,你的最後一行應讀作self.canv.config(高度= ...)' – 2016-07-21 20:22:30

+0

@AdamSmith感謝,增加了一個修正 – 2016-07-25 06:21:48

+0

你是什麼意思,「使這個框架我ScrolledWindow.scrollwindow的孩子」。你能給個例子嗎? – Jacob 2018-01-29 22:05:50