我正在制作一個輸出大量可滾動文本的程式。我希望文本根據視窗大小自動換行,并且能夠通過配置和自動調整它的事件函式來實作。最初,用戶需要單擊一個按鈕來切換頁面以顯示結果,如下所示:
import tkinter as tk
class textwrap(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.page = 0
self.results = []
self.go = tk.Button(self, text = "Go", command = self.run)
self.go.grid()
self.swap_button = tk.Button(self, text = "Change page", command = self.swap)
self.swap_button.grid()
def run(self):
for x in range(1000):
self.results.append('abcdefg abcdefg')
def swap(self):
if self.page == 0:
self.go.destroy()
self.page = 1
self.results_label = tk.Label(self, text = self.results, wraplength = self.master.winfo_width())
self.results_label.grid()
else:
self.results_label.destroy()
self.page = 0
self.go = tk.Button(self, text = "Go", command = self.run)
self.go.grid(row = 0)
frame01 = textwrap()
frame01.mainloop()
我在這里排除了自動換行和滾動條。這個模型的問題很快就會顯現出來,即輸入過多的文本最終會導致程式在嘗試顯示資料時凍結。我嘗試使用 ScrolledText 框(內置滾動條的文本欄位)找到解決方法,將其存盤在 ttk 筆記本的第二個選項卡中,并像這樣實時寫入:
import tkinter as tk
from tkinter import scrolledtext
class textwrap2(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.grid()
self.text_field = scrolledtext.ScrolledText(state = 'disabled')
self.text_field.grid()
self.go = tk.Button(self, text = "Go", command = self.run)
self.go.grid()
def run(self):
self.text_field.config(state='normal')
for x in range(1000):
self.text_field.insert(tk.END, x)
self.text_field.config(state='disabled')
frame01 = textwrap2()
frame01.mainloop()
While this lacks the initial lag on open, the entire program begins to slow after a large amount of data is added, and the scrollbar lags terribly, even worse than with an equivalent amount of data that successfully loaded in the previous version.
What I am asking is effectively this: Is there any efficient way to create wrapped text of a large size, with a scrollbar? Trying to load an extremely large list all at once freezes the program, and loading it in real time as it is created causes the program as a whole to slow down the larger it gets. I considered loading it in real time with myriad labels created iteratively, but wasn't sure how to wrap them as a collective. Another question about scrollbar lag suggested inserting newline characters automatically after a certain distance, rather than using automatic wrapping. The problem is that if the window is ever resized, the wrapping will no longer be accurate, and there is no easy way to fix it after that point. The simple Notepad app on Windows makes child's play of this issue, so I was wondering if there was an obvious solution that was evading me.
uj5u.com熱心網友回復:
當文本行很長時,文本小部件性能不佳。來自 tk 工具包生命早期的一篇論文(寫于 1996 年左右):
唉,很長的行在 Tk 文本小部件中也很慢, (...) 。Tk 使用 b-tree 來表示小部件的內容,但是 b-tree 的葉子是行(兩個換行符之間的所有內容),因此 Tk 必須通過行進行線性搜索才能找到任何內容。
uj5u.com熱心網友回復:
解決方法是成功的(盡管它在我的原始程式中引入了新問題,但這超出了這個問題的范圍)。使用標簽滾動比使用文本欄位平滑得多,所以我又回到了那個。一次加載一個過大的標簽會導致問題,所以我通過自動將輸入分成一定大小的單獨標簽來避免這種情況。雖然這確實會在這些地方導致不完美的換行符,但標簽尺寸足夠大,因此總體上不會造成太大問題。代碼將是這樣的:
import tkinter as tk
class textwrap3(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
self.pack()
self.master.geometry("1280x720")
self.master.update_idletasks()
self.inputs_list = []
self.starting_thousand = 0
self.ending_thousand = 7000
#Creates the canvas with frame to hold data, and vertical scrollbar.
#This part isn't the solution, but is required to visualize how the solution works.
self.results_canvas = tk.Canvas(self, width = self.master.winfo_width()-20, height = self.master.winfo_height()-30)
self.results_canvas.grid(sticky='new')
self.scroll = tk.Scrollbar(self, orient = 'vertical', command = self.results_canvas.yview)
self.results_canvas.configure(yscrollcommand=self.scroll.set)
self.results_frame = tk.Frame(self.results_canvas)
self.results_canvas.create_window((0,0), window = self.results_frame, anchor = 'n')
self.results_frame.bind("<Configure>", lambda e: self.results_canvas.configure
(scrollregion = self.results_canvas.bbox("all")))
self.scroll.grid(row = 0, column = 1, sticky = 'nse')
self.current_thousand_label = tk.Label(self.results_frame, wraplength = self.master.winfo_width()-20)
self.current_thousand_label.grid()
self.go = tk.Button(self, text = "Go", command = self.run)
self.go.grid()
self.bind('<Configure>', self.wrap)
#Automaticlly wraps the text
def wrap(self, event):
self.results_canvas.config(width = self.master.winfo_width()-20, height = self.master.winfo_height() - 30)
self.results_frame.config(width = self.master.winfo_width()-20)
for widget in self.results_frame.winfo_children():
widget.config(wraplength=self.master.winfo_width()-20)
def run(self):
for x in range(99700,100300):
self.inputs_list.append(x)
self.thousand_wrap()
self.current_thousand_label.config(text = self.inputs_list[self.starting_thousand:])
#The workaround itself
def thousand_wrap(self):
if len(self.inputs_list) >= self.ending_thousand:
self.current_thousand_label.destroy()
self.previous_thousand_labels = tk.Label(self.results_frame,
text = self.inputs_list[self.starting_thousand:self.ending_thousand],
wraplength = self.master.winfo_width()-20)
self.previous_thousand_labels.grid()
self.starting_thousand = 7000
self.ending_thousand = 7000
self.current_thousand_label = tk.Label(self.results_frame, wraplength = self.master.winfo_width()-20)
self.current_thousand_label.grid()
frame01 = textwrap3()
frame01.mainloop()
達到一定數量的大標簽后,文本將被截斷。我認為這與 tkinter 畫布的固有尺寸限制有關,但這超出了我最初的問題的范圍,我相信這個概念驗證表明至少可以在沒有大量程式滯后的情況下顯示大量資料。您可以更改self.ending_thousand和添加到開始和結束千下thousand_wrap()以更改每個標簽的大小。
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/437492.html
