Добавление полосы прокрутки группе виджетов в Tkinter

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

Документация подразумевает, что только Список, Текстовое поле, Холст и виджеты Записи поддерживают интерфейс полосы прокрутки. Ни один из них, кажется, не подходит для отображения сетки виджетов. Возможно поместить произвольные виджеты в виджет Холста, но Вы, кажется, должны использовать абсолютные координаты, таким образом, я не смог бы использовать менеджера по расположению сетки?

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

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

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

57
задан stovfl 8 October 2019 в 08:09
поделиться

1 ответ

Обзор

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

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

Рисовать текстовые элементы непосредственно на холсте не очень сложно, поэтому вы можете пересмотреть этот подход, если решение с рамкой, встроенной в холст, кажется вам слишком сложным. Поскольку вы создаете сетку, координаты каждого элемента текста будет очень легко вычислить, особенно если каждая строка имеет одинаковую высоту (что, вероятно, так и есть, если вы используете один шрифт).

Для рисования непосредственно на холсте просто определите высоту строки используемого шрифта (для этого существуют команды). Тогда каждая координата 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 в операторе import

100
ответ дан 24 November 2019 в 19:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: