【问题标题】:Tkinter Canvas scroll slow renderingTkinter Canvas 滚动渲染缓慢
【发布时间】:2021-06-18 13:50:18
【问题描述】:

Tkinter 中的 canvas Widget 绘制速度非常慢,即使使用有限的小部件,在滚动时也会对应用程序视觉造成很大的失真。

我已经四处搜索,但似乎只有人们在画布上绘制多个东西而不是滚动条效果的答案。

我的代码是否有任何问题会导致此问题,或者是否有任何方法可以修复绘制时间以在视觉上更加流畅。在应用程序中,这意味着每一行都是不同的颜色,这可能会使其非常难看,并且很难找到用户正在寻找的数据。

MVCE:

#python 3.8.6
from tkinter import *
import random
class test:
    def __init__(self):
        self.words = ["troop","relieve","exact","appeal","shortage","familiar","comfortable","sniff","mold","clay","rack","square","color","book","velvet","address","elaborate","grip","neutral","pupil"]
    def scrollable_area2(self, holder):
        base_frame = Frame(holder, padx=5, pady=5)
        base_frame.pack(fill=BOTH, expand=1)
        base_frame.rowconfigure(0, weight=0) 
        base_frame.columnconfigure(0, weight=1)
        can = Canvas(base_frame, bg="white")
        can.pack(side=LEFT, expand=1, fill=BOTH)
        scrollArea = Frame(base_frame, bg="white", )
        scrollArea.pack(side=LEFT, expand=1, fill=BOTH)
        can.create_window(0, 0, window=scrollArea, anchor='nw')
        Scroll = Scrollbar(base_frame, orient=VERTICAL)
        Scroll.config(command=can.yview)
        Scroll.pack(side=RIGHT, fill=Y)
        can.config(yscrollcommand=Scroll.set)
        scrollArea.bind("<Configure>", lambda e=Event(), c=can: self.update_scrollregion(e, c))
        return scrollArea, can
    def update_scrollregion(self, event, can):
        if can.winfo_exists():
            can.configure(scrollregion=can.bbox("all"))
    def generate(self, count): #generates the rows
        for i in range(int(count.get())):
            row = Frame(self.holder)
            row.pack(side=TOP)
            for i in range(9):
                a = Label(row, text=self.words[random.randint(0, len(self.words)-1)])
                a.pack(side=LEFT)
            b = Button(row, text=self.words[random.randint(0, len(self.words)-1)])
            b.pack(side=LEFT)
    def main(self):
        opts = Frame(self.root)
        opts.pack(side=TOP)
        v= StringVar()
        e = Entry(opts, textvariable=v)
        e.pack(side=LEFT)
        b=Button(opts, text="Run", command=lambda e=Event(), v=v:self.generate(v))
        b.pack(side=LEFT)
        main_frame=Frame(self.root)
        main_frame.pack(side=TOP, fill=BOTH, expand=1)
        self.holder, can = self.scrollable_area2(main_frame)
    def run(self):
        self.root = Tk()
        self.main()
        self.root.mainloop()
if __name__ == "__main__":
    app = test()
    app.run()

我留下了一个框,您可以在其中输入行数。我尝试了从 30 行到 300 多行,尽管初始渲染时间发生了变化,但滚动问题始终相同。

注意:对于我创建滚动区域的奇怪方式感到抱歉,它来自一段更复杂的代码,如果这最终成为一个因素,我已经对其进行了修改以适应此处。

【问题讨论】:

  • 如果您要创建垂直堆栈的小部件,使用文本小部件可能比使用画布和嵌入式框架更有效。
  • 我的代码是创建一个表,每行有 10-15 列和一个按钮。不确定文本小部件是否可以做与画布和框架组合相同的事情。
  • 好吧,公平地说,您的示例没有创建表。您当然可以在文本小部件中以表格形式排列内容。
  • 您可以像添加框架一样向文本小部件添加小部件

标签: python-3.x tkinter tkinter-canvas


【解决方案1】:

由于您只是创建垂直的框架堆栈,因此使用文本小部件作为容器可能比使用画布和嵌入式框架更有效。

这是一个创建 1000 行的简单示例,类似于您在画布上的操作方式。在我的 OSX 机器上,它的性能比画布好得多。

def scrollable_area2(self, parent):
    base_frame = Frame(parent, padx=5, pady=5)
    base_frame.pack(fill="both", expand=True)

    holder = Text(base_frame)
    vsb = Scrollbar(base_frame, orient="vertical", command=holder.yview)
    holder.configure(yscrollcommand=vsb.set)
    holder.pack(side="left", fill="both", expand=True)
    vsb.pack(side="right", fill="y")
    return holder
...
def generate(self, count): #generates the rows
    for i in range(int(count.get())):
        row = Frame(self.holder)
        self.holder.window_create("end", window=row)
        self.holder.insert("end", "\n")
        ...
def main(self):
    ...
    self.holder = self.scrollable_area2(main_frame)

上面的例子保留了内部框架,但你并不真的需要它。您可以直接在文本小部件中插入文本,使代码更加高效。

您在评论中说您实际上并没有创建帧堆栈,而是创建了一个值表。您可以通过使用制表位创建列来在文本小部件中创建表格。通过直接在小部件中插入文本,您可以创建更少的小部件,这肯定会提高性能。

这是一个使用硬编码制表位的示例,但您可以根据列表中最长的单词轻松计算它们。

def scrollable_area2(self, parent):
    base_frame = Frame(parent, padx=5, pady=5)
    base_frame.pack(fill="both", expand=True)

    self.holder = Text(base_frame, wrap="none", tabs=100)
    vsb = Scrollbar(base_frame, orient="vertical", command=self.holder.yview)
    self.holder.configure(yscrollcommand=vsb.set)
    self.holder.pack(side="left", fill="both", expand=True)
    vsb.pack(side="right", fill="y")

你的generate 函数可能看起来像这样:

def generate(self, count): #generates the rows
    for i in range(int(count.get())):
        for i in range(9):
            text = "\t".join([random.choice(self.words) for x in range(9)])
            self.holder.insert("end", text + "\t")
            button = Button(self.holder, text=random.choice(self.words))
            self.holder.window_create("end", window=button)
            self.holder.insert("end", "\n")

【讨论】:

  • 嗨。感谢您的回答。我可以看到如何使用文本小部件来替换堆叠的框架效率更高,并且可能会使我可以添加的行数翻倍,但是滚动问题似乎仍然存在。我在多台机器上安装了我的应用程序,所以我知道它不仅仅是我自己有这个问题。这是一个截图,当我滚动prnt.sc/16ft1yi时它看起来像你可以看到它重新渲染真的很慢(这是缓慢移动时,如果我快速滚动它只会显示BG颜色)慢滚动= 3行/秒, 快速滚动 = 10 行/秒供参考
猜你喜欢
  • 2018-01-22
  • 2013-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-06
  • 2014-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多