Добавление полосы прокрутки к группе виджетов в Tkinter - PullRequest
55 голосов
/ 21 июня 2010

Я использую Python для анализа записей из файла журнала и отображения содержимого записей с помощью Tkinter, и до сих пор это было отлично.Вывод представляет собой сетку виджетов с метками, но иногда строк больше, чем можно отобразить на экране.Я хотел бы добавить полосу прокрутки, которая выглядит так, как будто она должна быть очень простой, но я не могу понять это.

Документация подразумевает, что только виджеты List, Textbox, Canvas и Entry поддерживают полосу прокруткиинтерфейс.Ни один из них не подходит для отображения сетки виджетов.Можно поместить произвольные виджеты в виджет Canvas, но вам, похоже, приходится использовать абсолютные координаты, поэтому я не смог бы использовать менеджер компоновки сетки?

Я пытался поместить виджетсетка во фрейм, но кажется, что она не поддерживает интерфейс полосы прокрутки, поэтому это не работает:

mainframe = Frame(root, yscrollcommand=scrollbar.set)

Может кто-нибудь предложить способ обойти это ограничение?Я бы не хотел переписывать в PyQt и так сильно увеличивать размер исполняемого образа, просто добавив полосу прокрутки!

Ответы [ 2 ]

97 голосов
/ 22 июня 2010

Обзор

Вы можете связать полосы прокрутки только с несколькими виджетами, и корневой виджет и Frame не являются частью этой группы виджетов.

Наиболее распространенное решение - создать виджет холста и связать полосы прокрутки с этим виджетом.Затем в этот холст вставьте фрейм, содержащий ваши виджеты с ярлыками.Определите ширину / высоту рамки и укажите ее в параметре canvas scrollregion, чтобы область прокрутки точно соответствовала размеру рамки.

Рисование текстовых элементов непосредственно на холсте не очень сложное,поэтому вы можете пересмотреть этот подход, если решение frame-embedded-in-a-canvas кажется слишком сложным.Так как вы создаете сетку, координаты каждого текстового элемента будет очень легко вычислить, особенно если каждая строка имеет одинаковую высоту (что, вероятно, если вы используете один шрифт).

Для рисования непосредственно на холсте, просто определите высоту строки шрифта, который вы используете (и для этого есть команды).Тогда каждая координата y равна row*(lineheight+spacing).Координата x будет фиксированным числом, основанным на самом широком элементе в каждом столбце.Если вы дадите всем тег для столбца, в котором он находится, вы можете с помощью одной команды отрегулировать координату x и ширину всех элементов в столбце.

Объектно-ориентированное решение

ВотПример решения Frame-Embedded-In-Canvas с использованием объектно-ориентированного подхода:

import tkinter as tk

class Example(tk.Frame):
    def __init__(self, root):

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

        self.vsb.pack(side="right", fill="y")
        self.canvas.pack(side="left", fill="both", expand=True)
        self.canvas.create_window((4,4), window=self.frame, anchor="nw", 
                                  tags="self.frame")

        self.frame.bind("<Configure>", self.onFrameConfigure)

        self.populate()

    def populate(self):
        '''Put in some fake data'''
        for row in range(100):
            tk.Label(self.frame, text="%s" % row, width=3, borderwidth="1", 
                     relief="solid").grid(row=row, column=0)
            t="this is the second column for row %s" %row
            tk.Label(self.frame, text=t).grid(row=row, column=1)

    def onFrameConfigure(self, event):
        '''Reset the scroll region to encompass the inner frame'''
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

if __name__ == "__main__":
    root=tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Процедурное решение

Вот решение, которое не использует объекты:

import tkinter as tk

def populate(frame):
    '''Put in some fake data'''
    for row in range(100):
        tk.Label(frame, text="%s" % row, width=3, borderwidth="1", 
                 relief="solid").grid(row=row, column=0)
        t="this is the second column for row %s" %row
        tk.Label(frame, text=t).grid(row=row, column=1)

def onFrameConfigure(canvas):
    '''Reset the scroll region to encompass the inner frame'''
    canvas.configure(scrollregion=canvas.bbox("all"))

root = tk.Tk()
canvas = tk.Canvas(root, borderwidth=0, background="#ffffff")
frame = tk.Frame(canvas, background="#ffffff")
vsb = tk.Scrollbar(root, 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))

populate(frame)

root.mainloop()

Примечание: , чтобы сделать это в Python 2.x, используйте Tkinter вместо tkinter в операторе импорта

3 голосов
/ 27 декабря 2017

Сделать прокручиваемым

Используйте этот удобный класс, чтобы прокручивать фрейм, содержащий ваши виджеты.Выполните следующие действия:

  1. создайте рамку
  2. отобразите ее (упаковка, сетка и т. Д.)
  3. сделайте ее прокручиваемой
  4. добавьте виджеты внутри нее
  5. вызов метода update ()

import tkinter as tk
from tkinter import ttk

class Scrollable(tk.Frame):
    """
       Make a frame scrollable with scrollbar on the right.
       After adding or removing widgets to the scrollable frame, 
       call the update() method to refresh the scrollable area.
    """

    def __init__(self, frame, width=16):

        scrollbar = tk.Scrollbar(frame, width=width)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y, expand=False)

        self.canvas = tk.Canvas(frame, yscrollcommand=scrollbar.set)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        scrollbar.config(command=self.canvas.yview)

        self.canvas.bind('<Configure>', self.__fill_canvas)

        # base class initialization
        tk.Frame.__init__(self, frame)         

        # assign this obj (the inner frame) to the windows item of the canvas
        self.windows_item = self.canvas.create_window(0,0, window=self, anchor=tk.NW)


    def __fill_canvas(self, event):
        "Enlarge the windows item to the canvas width"

        canvas_width = event.width
        self.canvas.itemconfig(self.windows_item, width = canvas_width)        

    def update(self):
        "Update the canvas and the scrollregion"

        self.update_idletasks()
        self.canvas.config(scrollregion=self.canvas.bbox(self.windows_item))

Пример использования

root = tk.Tk()

header = ttk.Frame(root)
body = ttk.Frame(root)
footer = ttk.Frame(root)
header.pack()
body.pack()
footer.pack()

ttk.Label(header, text="The header").pack()
ttk.Label(footer, text="The Footer").pack()


scrollable_body = Scrollable(body, width=32)

for i in range(30):
    ttk.Button(scrollable_body, text="I'm a button in the scrollable frame").grid()

scrollable_body.update()

root.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...