如何在此开源手风琴中放置垂直滚动条?

How to put vertical scroll bar in this Open Source Accordion?

我试图在我的 Tkinter 应用程序中添加一个滚动条,但不幸的是它不能应用于根 window,这正是我想要实现的。我尝试在这个开源 Accordion 代码中放置一个滚动条。但是做不到。

Tkinter 只允许某些小部件和 canvas 有滚动条。

来源:http://code.activestate.com/recipes/580781-metro-accordion-for-tkinter/

# author: Miguel Martinez Lopez

try:
    from Tkinter import Tk, Frame, BitmapImage, Label
    from Tkconstants import *
except ImportError:
    from tkinter import Tk, Frame, BitmapImage, Label
    from tkinter.constants import *

import base64

class Animation(object):
    def __init__(self, w, ticks, config_function, duration=1, interval_time=None, easing_function=None, start_value=0, end_value=1, callback=None):
        self._w = w

        self._tick = 0
        self._total_ticks = float(ticks)

        if easing_function is None:
            self._easing_function = lambda x: x

        self._duration = duration

        if interval_time:
            self._interval_time = int(interval_time * 1000)
        else:
            self._interval_time = int(duration * 1000 / self._total_ticks)

        self._start_value = start_value
        self._end_value = end_value
        self._interval_value = end_value - start_value

        self._config_function = config_function

        self._callback = callback

    def start_animation(self, after=0):        
        if after != 0:
            self.after(int(after*1000), self._animate)
        else:
            self._animate()

    def _animate(self):
        t =  self._tick / self._total_ticks

        value = self._start_value + self._interval_value * self._easing_function(t)
        self._config_function(value)

        self._tick += 1

        if self._tick <= self._total_ticks:
            self._w.after(self._interval_time, self._animate)
        else:
            if self._callback is not None:
                self._w.after(self._interval_time, self._callback)


class Chord(Frame):
    RIGHT_ARROW_ICON = 'I2RlZmluZSBpbWFnZV93aWR0aCAxNwojZGVmaW5lIGltYWdlX2hlaWdodCAxNwpzdGF0aWMgY2hhciBpbWFnZV9iaXRzW10gPSB7CjB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLAoweDYwLDB4MDAsMHgwMCwweGUwLDB4MDAsMHgwMCwweGUwLDB4MDMsMHgwMCwweGUwLDB4MGYsMHgwMCwweGUwLDB4MDMsMHgwMCwKMHhlMCwweDAxLDB4MDAsMHg2MCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsCjB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwCn07'
    DOWN_ARROW_ICON = 'I2RlZmluZSBpbWFnZV93aWR0aCAxNwojZGVmaW5lIGltYWdlX2hlaWdodCAxNwpzdGF0aWMgY2hhciBpbWFnZV9iaXRzW10gPSB7CjB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLAoweDAwLDB4MDAsMHgwMCwweGUwLDB4MGYsMHgwMCwweGUwLDB4MGYsMHgwMCwweGMwLDB4MDcsMHgwMCwweGMwLDB4MDMsMHgwMCwKMHg4MCwweDAzLDB4MDAsMHgwMCwweDAxLDB4MDAsMHgwMCwweDAxLDB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwLDB4MDAsCjB4MDAsMHgwMCwweDAwLDB4MDAsMHgwMCwweDAwCn07'

    def __init__(self, master, title, width, body_background="white", background="#f0f0f0", foreground="#333333", selected_background="#1ba1e2", selected_foreground="white", active_foreground="#0067cb", cursor="hand1"):
        Frame.__init__(self, master, background="white")
        self._title = title

        self._background = background
        self._foreground = foreground
        self._active_foreground = active_foreground
        self._selected_foreground = selected_foreground
        self._selected_background = selected_background

        self._cursor = cursor

        self._right_arrow_icon = BitmapImage(data=base64.b64decode(Chord.RIGHT_ARROW_ICON))
        self._down_arrow_icon = BitmapImage(data=base64.b64decode(Chord.DOWN_ARROW_ICON))

        self._caption = Frame(self, width =width, background=background, padx=2)
        self._caption.pack(fill=X, pady=(0,2))
        self._caption.pack_propagate(False)

        self._icon_label = Label(self._caption, image=self._right_arrow_icon, background=background)
        self._icon_label.pack(side=LEFT)

        self._title_label = Label(self._caption, text=title, bg = background, fg=foreground)
        self._title_label.pack(side=LEFT, padx=4, fill=X)

        self._caption.configure(height= self._title_label.winfo_reqheight())

        self.body = Frame(self, background=body_background)
        self._body_height = None

        self._is_opened = False
        self._is_animating = False

        self._caption.bind('<Button-1>', self._on_click)
        self._title_label.bind('<Button-1>', self._on_click)
        self._icon_label.bind('<Button-1>', self._on_click)

        self._caption.bind('<Enter>', self._on_enter)
        self._caption.bind('<Leave>', self._on_leave)

    @property
    def title(self):
        return self._title

    @title.setter
    def title(self, text):
        self._title = text
        self._title_label.configure(text=text)

    def _on_enter(self, event):
        if not self._is_opened:
            self._down_arrow_icon.configure(foreground=self._active_foreground)
            self._right_arrow_icon.configure(foreground=self._active_foreground)

        self.config(cursor=self._cursor)

    def _on_leave(self, event):
        if not self._is_opened:
            self._down_arrow_icon.configure(foreground=self._foreground)
            self._right_arrow_icon.configure(foreground=self._foreground)

        self.config(cursor="arrow")

    def _on_click(self, event):
        if self._is_animating: return

        self.toggle()

    def open(self):
        if self._is_animating: return

        if not self._is_opened: self._open()

    def _open(self):        
        self.body.pack()
        self.body.pack_propagate(False)

        self._icon_label.configure(image=self._down_arrow_icon, background = self._selected_background)
        self._title_label.configure(foreground= self._selected_foreground, background = self._selected_background)
        self._caption.configure(background = self._selected_background)

        self._down_arrow_icon.configure(foreground=self._selected_foreground)

        if self._body_height is None:
            self._body_height= self.body.winfo_reqheight()

        end_value = self._body_height

        self.body.configure(width=self.winfo_width())
        self._is_opened = True
        self._is_animating = True

        animation = Animation(
            self,
            ticks=16,
            interval_time=0.01,
            start_value=0, 
            end_value=end_value,
            config_function=lambda height: self.body.configure(height=int(height)), 
            callback=self._on_finnish_animation)

        animation.start_animation()

    def _on_finnish_animation(self):
        self._is_animating = False

        if not self._is_opened:
            self.body.pack_forget()

    def close(self):
        if self._is_animating:
            return

        if self._is_opened: self._close()

    def _close(self):
        self._icon_label.configure(image=self._right_arrow_icon, background = self._background)
        self._title_label.configure(foreground= self._foreground, background = self._background)
        self._caption.configure(background = self._background)

        self._right_arrow_icon.configure(foreground=self._foreground)

        start_value = self.body.winfo_height()

        self._is_opened = False
        self._is_animating = True

        animation = Animation(
            self,
            ticks=16,
            interval_time=0.01,
            start_value=start_value, 
            end_value=0,
            config_function=lambda height: self.body.configure(height=int(height)), 
            callback=self._on_finnish_animation)

        animation.start_animation()

    def toggle(self):
        if self._is_opened:
            self._close()
        else:
            self._open()

class Accordion(Frame):

    def __init__(self, parent, width, **kwargs):
        Frame.__init__(self, **kwargs)

        self._width = width
        self._list_of_chords = []

    def create_chord(self, title, background="white"):
        chord = Chord(self, title=title, body_background=background, width=self._width)
        self._list_of_chords.append(chord)

        if len(self._list_of_chords) == 1:
            chord.pack(fill=X)
        else:
            chord.pack(fill=X, pady=(1,0))

        return chord

if __name__ == '__main__':
    try:
        from Tkinter import Entry, Button, Text
    except ImportError:
        from tkinter import Entry, Button, Text

    root = Tk()
    root.geometry("400x300")

    root.configure(background="white")


    # create the Accordion
    accordion = Accordion(root, width=200)
    accordion.pack(pady=10)

    first_chord = accordion.create_chord('First Chord')
    Label(first_chord.body, text='hello world', bg='white').pack()

    # second chord
    second_chord = accordion.create_chord('Second Chord')
    Entry(second_chord.body).pack()
    Button(second_chord.body, text='Button').pack()

    # third chord
    third_chord = accordion.create_chord(title='Third Chord')
    Text(third_chord.body).pack()

    root.mainloop()

我想添加一个滚动条,如果上面程序中有很多和弦,我可以一直垂直滚动。

编辑

我尝试过的:

 root = Tk()

    canvas = Canvas(root, borderwidth=0, background="#ffffff")
    frame = Frame(canvas, background="#ffffff")
    vsb = Scrollbar(frame, orient="vertical", command=canvas.yview)
    canvas.configure(yscrollcommand=vsb.set)

    vsb.pack(side="right", fill="y")
    canvas.pack(side="left", fill="both", expand=True)
    canvas.create_window((4,4), window=frame, anchor="nw")
    frame.bind("<Configure>", lambda event, canvas=canvas: onFrameConfigure(canvas))

    root.geometry("1080x720")
    root.configure(background="black")
    heading = Label(frame, text = "Welcome", bg = "black", fg = "orange", font = ("Arial Bold", 20))
    heading.pack()

    #Logo
    gif1 = PhotoImage(file = 'logo.gif')
    label1 = Label(frame, image=gif1)
    label1.image = gif1 
    label1.pack()

    #Text1 Properties
    text1 = Label(frame, text = "Please select the file :", bg = "black", fg = "white", font = ("Arial", 15))
    text1.pack()

    # create the Accordion
    accordion = Accordion(frame, width=200)
    accordion.pack(pady=10)
    i = 0
    chords = []
    var1Index = 0

    for pick in tests:
        nChords = accordion.create_chord(tests[i][1])
        populateFunct(nChords, tests[i][1], i)
        i += 1

在上面的程序中,我在一帧中得到 "Welcome"、"logo" 和 "text",在同一帧中得到另一帧中的和弦 window(Side by边)。我正在努力将和弦放在显示欢迎、徽标和文本的同一框架中,但默认情况下,虽然我将框架发送到 Accordion 函数,但和弦在根 window 中占据一席之地。这就是我面临的问题。 PS: Accordion功能就是前面提到的

我看到的第一个问题是您将滚动条放在 canvas 内的框架内。滚动条需要在框架之外,而且通常也在 canvas 之外。

对于这个具体的例子,滚动条可以在根 window 中,因为根 window 否则只有 canvas.

vsb = Scrollbar(root, orient="vertical", command=canvas.yview)

第二个问题是 Accordion 中的错误。它忽略了 parent 参数。您需要将 __init__ 更改为以下内容。注意 parent 参数添加到 Frame.__init:

class Accordion(Frame):

    def __init__(self, parent, width, **kwargs):
        Frame.__init__(self, parent, **kwargs)

        self._width = width
        self._list_of_chords = []

除此之外,我认为代码可能没问题。我无法 运行 你的代码,因为你遗漏了几个关键部分。