#python-3.x #tkinter #tkinter-canvas
Вопрос:
Виджет canvas в Tkinter очень медленно рисует, что приводит к большим искажениям визуальных элементов приложений при прокрутке, даже при использовании ограниченных виджетов.
Я провел поиск, но, похоже, получил ответы только от людей, которые рисуют несколько вещей на холсте, а не на эффектах полосы прокрутки.
Есть ли какие-либо проблемы с моим кодом, которые могли бы вызвать эту проблему, или есть какие-либо методы, позволяющие исправить время отрисовки, чтобы оно было более плавным визуально. В приложении это означает, что каждая строка имеет свой цвет, что может сделать ее чрезвычайно уродливой на вид и затруднить поиск данных, которые ищет пользователь.
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 строк, и хотя начальное время рендеринга меняется, проблема прокрутки всегда одна и та же.
ПРИМЕЧАНИЕ: извините за странный способ, которым я создаю область прокрутки, это из более сложного фрагмента кода, который я изменил, чтобы он соответствовал здесь, если это окажется фактором.
Комментарии:
1. Если вы создаете вертикальный стек виджетов, использование текстового виджета может быть более эффективным, чем использование холста и встроенной рамки.
2. Мой код состоит в том, чтобы создать таблицу, в каждой строке которой будет 10-15 столбцов и кнопка. Не уверен, что текстовый виджет может делать то же самое, что комбинация холста и рамки.
3. Ну, честно говоря, ваш пример не создает таблицу. Вы, конечно, можете упорядочить все в табличной форме в текстовом виджете.
4. Вы можете добавлять виджеты в текстовый виджет так же, как и в рамку
Ответ №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")
Комментарии:
1. Привет. Спасибо за ваш ответ. Я вижу, как использование текстового виджета для замены сложенных кадров намного эффективнее и потенциально может удвоить количество строк, которые я могу добавить, однако проблема прокрутки, похоже, все еще существует. У меня установлено приложение на нескольких машинах, поэтому я знаю, что эта проблема возникает не только у меня. Вот скриншот с тем, как это выглядит, когда я прокручиваю prnt.sc/16ft1yi Как вы можете видеть, повторная визуализация происходит очень медленно (это происходит при медленном движении, цвет отображается только при быстрой прокрутке) Медленная прокрутка = 3 строки в секунду, быстрая прокрутка = 10 строк в секунду для справки