Esempio n. 1
0
def add_scrollbars(outer, InnerType, *inner_args, **inner_kw):
    """ Creates widget of type `InnerType` inside `outer` widget and adds
vertical and horizontal scroll bars for created widget.
    """

    outer.rowconfigure(0, weight=1)
    outer.rowconfigure(1, weight=0)
    outer.columnconfigure(0, weight=1)
    outer.columnconfigure(1, weight=0)

    # container for inner frame, it supports scrolling
    canvas = Canvas(outer, width=1, height=1)
    canvas.grid(row=0, column=0, sticky="NESW")

    inner = InnerType(canvas, *inner_args, **inner_kw)

    inner_id = canvas.create_window((0, 0), window=inner, anchor="nw")

    h_sb = Scrollbar(outer, orient="horizontal", command=canvas.xview)
    h_sb._visible = False

    v_sb = Scrollbar(outer, orient="vertical", command=canvas.yview)
    v_sb._visible = False

    canvas.configure(xscrollcommand=h_sb.set, yscrollcommand=v_sb.set)

    def scrollbar_visibility():
        "Automatically shows & hides the scroll bar."

        cnv_w, cnv_h = canvas.winfo_width(), canvas.winfo_height()
        inner_w, inner_h = inner.winfo_width(), inner.winfo_height()

        if inner_w <= cnv_w:
            if h_sb._visible:
                h_sb.grid_forget()
                h_sb._visible = False
        else:
            if not h_sb._visible:
                h_sb.grid(row=1, column=0, sticky="NESW")
                h_sb._visible = True

        if inner_h <= cnv_h:
            if v_sb._visible:
                v_sb.grid_forget()
                v_sb._visible = False
        else:
            if not v_sb._visible:
                v_sb.grid(row=0, column=1, sticky="NESW")
                v_sb._visible = True

    def inner_configure(_):
        scrollbar_visibility()
        canvas.configure(
            scrollregion=canvas.bbox(ALL),
            # Require as many space as inner widget do.
            width=inner.winfo_reqwidth(),
            height=inner.winfo_reqheight())

    inner.bind("<Configure>", inner_configure, "+")

    def canvas_configure(e):
        scrollbar_visibility()
        # Get to the inner widget at least desired space.
        # Stretch it when possible.
        canvas.itemconfig(inner_id,
                          width=max(e.width, inner.winfo_reqwidth()),
                          height=max(e.height, inner.winfo_reqheight()))

    canvas.bind("<Configure>", canvas_configure, "+")

    return inner
Esempio n. 2
0
def add_scrollbars(outer, InnerType, *inner_args, **kw):
    """ Creates widget of type `InnerType` inside `outer` widget and adds
vertical and horizontal scroll bars for created widget.

:param kw:
    those arguments are passed to `InnerType` constructor except for:

    - row/column: `grid` coordinates inside `outer` (default: 0)
    - wheel: support wheel scrolling when mouse is over inner widget
        (default: False)
    - inner_kw: see below

    To pass same named arguments to `InnerType` wrap them into a `dict` and
pass it with name "inner_kw" in "kw".
    """

    row, column = kw.pop("row", 0), kw.pop("column", 0)
    wheel = kw.pop("wheel", False)

    kw.update(kw.pop("inner_kw", {}))

    outer.rowconfigure(row, weight=1)
    outer.rowconfigure(row + 1, weight=0)
    outer.columnconfigure(column, weight=1)
    outer.columnconfigure(column + 1, weight=0)

    # container for inner frame, it supports scrolling
    canvas = Canvas(outer, width=1, height=1)
    canvas.grid(row=row, column=row, sticky="NESW")

    inner = InnerType(canvas, *inner_args, **kw)

    inner_id = canvas.create_window((0, 0), window=inner, anchor="nw")

    h_sb = Scrollbar(outer, orient="horizontal", command=canvas.xview)
    h_sb._visible = False

    v_sb = Scrollbar(outer, orient="vertical", command=canvas.yview)
    v_sb._visible = False

    canvas.configure(xscrollcommand=h_sb.set, yscrollcommand=v_sb.set)

    def scrollbar_visibility():
        "Automatically shows & hides the scroll bar."

        cnv_w, cnv_h = canvas.winfo_width(), canvas.winfo_height()
        inner_w, inner_h = inner.winfo_width(), inner.winfo_height()

        if inner_w <= cnv_w:
            if h_sb._visible:
                h_sb.grid_forget()
                h_sb._visible = False
        else:
            if not h_sb._visible:
                h_sb.grid(row=row + 1, column=column, sticky="NESW")
                h_sb._visible = True

        if inner_h <= cnv_h:
            if v_sb._visible:
                v_sb.grid_forget()
                v_sb._visible = False
        else:
            if not v_sb._visible:
                v_sb.grid(row=row, column=column + 1, sticky="NESW")
                v_sb._visible = True

    def inner_configure(_):
        scrollbar_visibility()
        canvas.configure(
            scrollregion=canvas.bbox(ALL),
            # Require as many space as inner widget do.
            width=inner.winfo_reqwidth(),
            height=inner.winfo_reqheight())

    inner.bind("<Configure>", inner_configure, "+")

    def canvas_configure(e):
        scrollbar_visibility()
        # Get to the inner widget at least desired space.
        # Stretch it when possible.
        canvas.itemconfig(inner_id,
                          width=max(e.width, inner.winfo_reqwidth()),
                          height=max(e.height, inner.winfo_reqheight()))

    canvas.bind("<Configure>", canvas_configure, "+")

    if not wheel:
        return inner

    def on_wheel(e):
        w = e.widget

        # Is the `w`idget our inner?
        m = w
        while m is not None:
            if m is inner:
                break
            m = m.master
        else:
            # Outer widget
            return

        cls = w.winfo_class()

        if cls in WHEELED_WIDGETS:
            # When mouse pointer is over `WHEELED_WIDGETS` the canvas
            # must not be scrolled. But there are few exceptions for
            # convenience.
            try:
                a, b = w.yview()
            except:
                # not all "wheeled" widgets provides `yview` and those values
                # prevents heuristics about fully scrolled `w`idgets below.
                a, b = None, None

            if e.delta > 0:
                if a == 0.0:
                    pass  # w is fully scrolled up
                elif w.winfo_rooty() < canvas.winfo_rooty():
                    pass  # user does not see upper border of w
                    # XXX: we also have to prevent scrolling of `w` but
                    # returning "break" does not work.
                else:
                    return
            elif e.delta < 0:
                if b == 1.0:
                    pass  # w is fully scrolled down
                elif (w.winfo_rooty() + w.winfo_height() >
                      canvas.winfo_rooty() + canvas.winfo_height()):
                    pass  # user does not see bottom border of w
                else:
                    return
            else:
                return

        canvas.yview("scroll", -e.delta, "units")

    bind_all_mouse_wheel(inner, on_wheel, "+")

    return inner