用滚动条显示 Canvas 中的大量数据

Display of a large amount of data in a Canvas with a Scrollbar

我在尝试在 tkinter 中显示大 table 时遇到问题。

首先,我尝试在 canvas 中一次显示所有标签,但超过几百行,程序就关闭了。因此,我尝试创建一个可滚动的 canvas,每次滚动时它都会更新:我收集滚动条的位置,并根据它的位置显示相应的 10 个值。

但我无法使此代码正常工作。现在它只显示黑色背景,右边有滚动条。

代码如下:

from tkinter import *

class Application(object):
    def __init__(self, parent):
        self.x = []
        for i in range(1, 1000):
            self.x.append(i)
        self.parent = parent 
        self.mainFrame =  Frame(self.parent)
        self.mainFrame.pack()
        self.canvas = Canvas(self.mainFrame, width = 200, height = 500, bg = "black")
        self.canvas.grid(row = 0, column = 0)
        self.scroll = Scrollbar(self.mainFrame, orient = VERTICAL, command = self.update)
        self.scroll.grid(row = 0, column = 1)

        self.canvas.configure(yscrollcommand = self.scroll.set)
        self.tabCursor = 0
        self.scrollPosition = self.scroll.get()

    def update(self):
        self.tabCursor = round(self.scrollPosition[0]*len(self.x))
        if ((len(self.x) - self.tabCursor) < 10):
            self.tabCursor = len(self.x) - 10
        for i in range(0, 10): #display 10 values
            label = Label(self.canvas, text = str(self.x[tabCursor + i]), width = 50)
            label.grid(column = 0, row = i)


if __name__ == '__main__':
    root = Tk()
    app = Application(root)
    root.mainloop()

编辑: 我终于有时间实施你的答案了。看起来不错,但我无法让滚动条工作,我不知道为什么。

class TableauDeDonnees(object):
    "Tableau de données -- Onglet Tableau de données"
    def __init__(self, data, parent):
        self.parent = parent
        self.data = data
        print(self.data[0], self.data[1])
        print(len(self.data[0]), len(self.data[1]))
        self.labels = []
        self.navigationFrame = Frame(self.parent)
        self.canvas = Canvas(self.parent, bg = "black", width = 200, height = 500)
        self.mainFrame = Frame(self.canvas)
        self.navigationFrame.pack()
        print(len(data))
        for row in range(50):
            for column in range(len(data)):
                self.labels.append(Label(self.canvas, text = str(data[column][row])))
        for i in range(len(self.labels)):
            self.labels[i].grid(row = i // 2, column = i % 2, sticky = NSEW)
        self.boutonRetour = Button(self.navigationFrame, text = "Retour", command = lambda: self.move(-2))
        self.quickNav = Entry(self.navigationFrame, width = 3)
        self.quickNav.bind('<Return>', lambda x: self.move(self.quickNav.get()))
        self.boutonSuivant = Button(self.navigationFrame, text = "Suivant", command = lambda: self.move(0))

        temp  = divmod(len(data[0]), len(self.labels) // 2)
        self.pages = temp[0] + (1 if temp[1] else 0)

        self.position = Label(self.navigationFrame, text='Page 1 sur ' + str(self.pages))
        self.pageCourante = 1

        self.boutonRetour.grid(row = 0, column = 0)
        self.quickNav.grid(row = 0, column = 1)
        self.boutonSuivant.grid(row = 0, column = 2)
        self.position.grid(row = 0, column = 3)

        self.scroll = Scrollbar(self.parent, orient = VERTICAL, command = self.canvas.yview)
        self.canvas.configure(yscrollcommand = self.scroll.set)
        self.scroll.pack(side = RIGHT, fill='y')
        self.canvas.pack(side = LEFT, fill = 'both')

        self.canvas.create_window((4,4), window = self.mainFrame, anchor = "nw", tags = "frame")
        self.canvas.configure(yscrollcommand = self.scroll.set)
        self.mainFrame.bind("<Configure>", self.update)
        self.canvas.configure(scrollregion = self.canvas.bbox("all"))

    def update(self, event):
        self.canvas.configure(scrollregion = self.canvas.bbox("all"))

    def move(self, direction):
        if (self.pageCourante == 1 and direction == -2) or (self.pageCourante == self.pages and direction == 0):
            return
        if direction in (-2, 0):
            self.pageCourante += direction + 1
        else:
            try:
                temp = int(direction)
                if temp not in range(1, self.pages + 1):
                    return
            except ValueError:
                return
            else:
                self.pageCourante = temp
        for i in range(len(self.labels)):
            try:
                location = str(self.data[i % 2][len(self.labels)*(self.pageCourante - 1) + i])
            except IndexError:
                location = ''
            self.labels[i].config(text = location)
        self.position.config(text = 'Page ' + str(self.pageCourante) + ' sur ' + str(self.pages))

我不明白为什么滚动条不能正常工作。请注意,我的 parent 是笔记本。 此外,显示的项目数量也存在问题。页数正确,但显示的页数似乎超出了应有的数量,导致最后一页为空,最后显示的值似乎正确。

感谢您的关注

滚动条无法通过无限地不断创建新的小部件来工作。您还遗漏了一些关键部分 - 不幸的是,Scrollbar 不像大多数 tkinter 小部件那样简单。

from tkinter import *

class Application(object):
    def __init__(self, parent):
        self.parent = parent 
        self.canvas =  Canvas(self.parent, bg='black', width = 200, height = 500)
        self.mainFrame = Frame(self.canvas)
        self.scroll = Scrollbar(self.parent, orient = VERTICAL, command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)
        self.scroll.pack(side='right', fill='y')
        self.canvas.pack(side='left', fill='both')
        self.canvas.create_window((4,4), window=self.mainFrame, anchor="nw", tags="frame")

        self.canvas.configure(yscrollcommand = self.scroll.set)
        self.mainFrame.bind("<Configure>", self.update)        
        self.x = []
        for i in range(1000):
            self.x.append(Label(self.mainFrame, text=str(i)))
            self.x[i].grid()

    def update(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))


if __name__ == '__main__':
    root = Tk()
    app = Application(root)
    root.mainloop()

如果您想一次只显示几个页面并提供类似论坛的界面,您可以使用 Buttons 在页面之间导航。此示例允许用户使用“后退”和“前进”按钮进行导航,以及在框中输入页码并按 Enter

from tkinter import *

class Application(object):
    def __init__(self, parent):
        self.x = list(range(1000))
        self.labels = []
        self.parent = parent
        self.navigation_frame = Frame(self.parent)
        self.canvas =  Canvas(self.parent, bg='black', width = 200, height = 500)
        self.mainFrame = Frame(self.canvas)
        self.navigation_frame.pack()
        for i in range(100):
            self.labels.append(Label(self.mainFrame, text=str(i)))
            self.labels[i].grid()
        self.back_button = Button(self.navigation_frame, text='Back', command=lambda: self.move(-2))
        self.quick_nav = Entry(self.navigation_frame, width=3)
        self.quick_nav.bind('<Return>', lambda x: self.move(self.quick_nav.get()))
        self.forward_button = Button(self.navigation_frame, text='Forward', command=lambda: self.move(0))

        temp = divmod(len(self.x), len(self.labels))
        self.pages = temp[0] + (1 if temp[1] else 0)

        self.you_are_here = Label(self.navigation_frame, text='Page 1 of ' + str(self.pages))
        self.current_page = 1
        self.back_button.grid(row=0, column=0)
        self.quick_nav.grid(row=0, column=1)
        self.forward_button.grid(row=0, column=2)
        self.you_are_here.grid(row=0, column=3)
        self.scroll = Scrollbar(self.parent, orient = VERTICAL, command=self.canvas.yview)
        self.canvas.configure(yscrollcommand=self.scroll.set)
        self.scroll.pack(side='right', fill='y')
        self.canvas.pack(side='left', fill='both')
        self.canvas.create_window((4,4), window=self.mainFrame, anchor="nw", tags="frame")

        self.canvas.configure(yscrollcommand = self.scroll.set)
        self.mainFrame.bind("<Configure>", self.update)   

    def update(self, event):
        self.canvas.configure(scrollregion=self.canvas.bbox("all"))

    def move(self, direction):
        if (self.current_page == 1 and direction == -2) or (self.current_page == self.pages and direction == 0):
            return
        if direction in (-2, 0):
            self.current_page += direction + 1
        else:
            try:
                temp = int(direction)
                if temp not in range(1, self.pages+1):
                    return
            except ValueError:
                return
            else:
                self.current_page = temp
        for i in range(len(self.labels)):
            try:
                location = str(self.x[len(self.labels)*(self.current_page - 1) + i])
            except IndexError:
                location = ''
            self.labels[i].config(text=location)
        self.you_are_here.config(text='Page ' + str(self.current_page) + ' of ' + str(self.pages))

if __name__ == '__main__':
    root = Tk()
    app = Application(root)
    root.mainloop()