是否可以调整Tkinter文本小部件以适应其内容?
即:如果我放一行文字就会缩小,但如果我放了5行就会增长
答案 0 :(得分:6)
我能想到实现这一目标的唯一方法是每次用户在Text小部件中输入文本时计算宽度和高度,然后将小部件的大小设置为该值。但是这里的限制是只有单倍间距的字体才能正常工作,但无论如何它都在这里:
import Tkinter
class TkExample(Tkinter.Frame):
def __init__(self, parent):
Tkinter.Frame.__init__(self, parent)
self.init_ui()
def init_ui(self):
self.pack()
text_box = Tkinter.Text(self)
text_box.pack()
text_box.bind("<Key>", self.update_size)
def update_size(self, event):
widget_width = 0
widget_height = float(event.widget.index(Tkinter.END))
for line in event.widget.get("1.0", Tkinter.END).split("\n"):
if len(line) > widget_width:
widget_width = len(line)+1
event.widget.config(width=widget_width, height=widget_height)
if __name__ == '__main__':
root = Tkinter.Tk()
TkExample(root)
root.mainloop()
答案 1 :(得分:4)
在Google搜索的顶部找到此主题,因此,也许有需要的人会找到它。经过数小时的搜索后,找不到答案。所以这就是我提出的HACK。
我想要一个弹出窗口,它可以在Text小部件中的任何未知但预定的文本周围正确匹配,而不是用户输入。此外,Text小部件需要在其文本内容周围正确地形成自适应。
tkinter.Label
效果很好,但它没有tkinter.Text.tag_configure
和tkinter.Text.tag_bind
我需要用tkinter的富文本标签替换某些HTML标签。 tkinter.Text
具有丰富的文本标记,但不能很好地扩展,而tkinter.Label
可以很好地扩展,但没有丰富的文本标记。此外,我只是讨厌滚动条和自动换行,除非它们真的需要。这正是我想要的。虽然,这只是这个论坛的一个非常简单,有效的摘要。适用于任何字体。仅在Ubuntu 13.10(Linux)中使用Python 3.3进行测试。
#!/usr/bin/env python3
import tkinter as tk
class MyFrame(tk.Frame):
def __init__(self):
tk.Frame.__init__(self)
root = self.master
root.title("My Window Title")
# Pack Frame into root window and make it expand in "both" x and y
self.pack(side="top", fill="both", expand=True, padx=10, pady=10)
# Statistical weight of 1 = 100% for cell (0, 0) to expand 100%
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
# The string text
text = """Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed
diam nonummy nibh euismod tincidunt ut laoreet dolore magna
aliquam erat volutpat. Ut wisi enim ad minim veniam, quis
nostrud exerci tation ullamcorper suscipit lobortis nisl ut
aliquip ex ea commodo consequat. Duis autem vel eum iriure
dolor in hendrerit in vulputate velit esse molestie consequat,
vel illum dolore eu feugiat nulla facilisis at vero eros et
accumsan et iusto odio dignissim qui blandit praesent luptatum
zzril delenit augue duis dolore te feugait nulla facilisi. Nam
liber tempor cum soluta nobis eleifend option congue nihil
imperdiet doming id quod mazim placerat facer possim assum.
Typi non habent claritatem insitam; est usus legentis in iis qui
facit eorum claritatem. Investigationes demonstraverunt lectores
legere me lius quod ii legunt saepius. Claritas est etiam
processus dynamicus, qui sequitur mutationem consuetudium
lectorum. Mirum est notare quam littera gothica, quam nunc
putamus parum claram, anteposuerit litterarum formas
humanitatis per seacula quarta decima et quinta decima. Eodem
modo typi, qui nunc nobis videntur parum clari, fiant sollemnes
in futurum."""
# Add a tk.Text widget to Frame (self) and its configuration
textwidget = tk.Text(self, wrap="none", font=("Comic Sans MS", 12),
padx=10, pady=10)
textwidget.grid(row=0, column=0, sticky="nesw")
# Add the text to textwidget and disable editing
textwidget.insert(tk.END, text)
textwidget.config(state=tk.DISABLED)
# Here is where the HACK begins
def is_scroll(wh, lower, upper):
nonlocal size
size[wh][0] = upper < '1.0' or lower > '0.0'
size[wh][1] += 20 * size[wh][0] # += 1 for accuracy but slower
# Call the is_scroll function when textwidget scrolls
textwidget.config(xscrollcommand=lambda *args: is_scroll('w', *args),
yscrollcommand=lambda *args: is_scroll('h', *args))
# Add a tk.Button to the Frame (self) and its configuration
tk.Button(self, text="OK", command=self.quit).grid(row=1, column=0,
sticky="we")
# For reasons of magic, hide root window NOW before updating
root.withdraw()
# Initially, make root window a minimum of 50 x 50 just for kicks
root.geometry('50x50')
size = {'w': [False, 50], 'h': [False, 50]}
# Update to trigger the is_scroll function
root.update()
while size['w'][0] or size['h'][0]:
# If here, we need to update the size of the root window
root.geometry('{}x{}'.format(size['w'][1], size['h'][1]))
root.update()
# Center root window on mouse pointer
x, y = root.winfo_pointerxy()
root.geometry('+{}+{}'.format(x-size['w'][1]//2, y-size['h'][1]//2))
# Now reveal the root window in all its glory
root.deiconify()
# Print textwidget dimensions to the console
print(textwidget.winfo_width(), textwidget.winfo_height())
def main():
"""Show main window."""
MyFrame().mainloop()
if __name__ == '__main__':
main()
说明:TRICK甚至不用担心尝试直接扩展或缩小Text小部件是徒劳的。答案有点反直觉,因为我们首先想到的是直接使用Text小部件并对其做些什么。而是展开根(最外层)窗口(在本例中为self.master
),然后单独保留Text小部件。容易腻。
粘贴("nesw"
)Text小部件到Frame,它在根窗口中打包100%扩展。随着根窗口的扩展,其中的Frame和Text小部件也会扩展。但是,当您正在扩展根窗口时,如果lower
和upper
边界已经消失,文本小部件的xscrollcommand
和yscrollcommand
(不再滚动),则测试。这些命令将lower
和upper
个参数作为百分位数发送给滚动条所需的回调函数,通常为tkinter.Scrollbar.set
。但是,我们正在使用这些命令,因为我们根本不想要滚动条或任何滚动。我们想要一个完美的适合。
如果lower
和upper
边界已经消失(低于&lt; = 0.0且高于&gt; = 1.0),这意味着我们的文本小部件周围有一个完美的窗口,它也非常合适围绕其文字内容。 TADA!
添加了一个按钮,以证明即使添加了其他小部件,它仍能正常工作。删除一些文字,看它仍然完美无缺。
答案 2 :(得分:1)
通过重新使用sc0tt的答案,以及Bryan Oakley在这里的答案Get of number of lines of a Text tkinter widget,我们可以使用随时可用的代码(此处发布以供将来参考),该代码也适用于< strong> proprtional fonts :
import Tkinter as Tk
import tkFont
class Texte(Tk.Text):
def __init__(self, event=None, x=None, y=None, size=None, txt=None, *args, **kwargs):
Tk.Text.__init__(self, master=root, *args, **kwargs)
self.font = tkFont.Font(family="Helvetica Neue LT Com 55 Roman",size=35)
self.place(x=10,y=10)
self.insert(Tk.INSERT,' blah ')
self.config(font=self.font)
self.update_size(event=None)
bindtags = list(self.bindtags())
bindtags.insert(2, "custom")
self.bindtags(tuple(bindtags))
self.bind_class("custom", "<Key>", self.update_size)
def update_size(self, event):
width=0
lines=0
for line in self.get("1.0", "end-1c").split("\n"):
width=max(width,self.font.measure(line))
lines += 1
self.config(height=lines)
self.place(width=width+10)
root = Tk.Tk()
root.geometry("500x500")
Texte()
root.mainloop()
答案 3 :(得分:0)
以sc0tt的帖子为基础,如果你没有使用换行符(例如只使用固定的宽度并使高度成为唯一的扩展变量),这个辅助函数效果很好:
<?php
$baseUrl = 'www.example.com/page';
if (!empty($_GET['number']))
{
$firstNumber = (int) $_GET['number'][0]
}
if (isset($firstNumber))
{
header('Location: ' . $baseUrl . $number);
}
else
{
// We don't have a number from the input.
}