【发布时间】:2013-01-27 06:20:39
【问题描述】:
如何从Text 小部件获取 Tkinter 输入?
编辑
我问这个问题是为了帮助其他有同样问题的人 - 那 是没有示例代码的原因。这个问题困扰了我几个小时,我用这个问题来教别人。 请不要把它当作一个真实的问题来评价 - 答案才是最重要的。
【问题讨论】:
如何从Text 小部件获取 Tkinter 输入?
编辑
我问这个问题是为了帮助其他有同样问题的人 - 那 是没有示例代码的原因。这个问题困扰了我几个小时,我用这个问题来教别人。 请不要把它当作一个真实的问题来评价 - 答案才是最重要的。
【问题讨论】:
要从文本框中获取 Tkinter 输入,您必须向普通的 .get() 函数添加更多属性。如果我们有一个文本框myText_Box,那么这就是检索其输入的方法。
def retrieve_input():
input = self.myText_Box.get("1.0",END)
第一部分,"1.0" 意味着输入应该从第一行,字符零(即:第一个字符)读取。 END 是一个导入常量,设置为字符串"end"。 END 部分表示阅读直到到达文本框的末尾。唯一的问题是它实际上在我们的输入中添加了一个换行符。所以,为了修复它,我们应该将END 更改为end-1c(感谢Bryan Oakley)-1c 删除1 个字符,而-2c 表示删除两个字符,依此类推。
def retrieve_input():
input = self.myText_Box.get("1.0",'end-1c')
【讨论】:
"end-1c" 或END+"1c",否则你会得到文本小部件总是添加的额外换行符。
.get('1.0', 'end-1c')
end+1c 会在代码中添加一个新行吗?最后,Bryan 和 Honest Abe,非常感谢你们帮助我解决了我的简单 Tkinter 和 Python 问题。你真的帮助我更深入地理解了这门语言,并且一直彬彬有礼、迅速,而且最重要的是——知识渊博。我相信你的建议会对我升入高中及以后有所帮助!
'end-1c' 周围的引号对于它是单个字符串是必要的。 'end' 是最后一个字符之后的索引的别名。所以如果'end' 是'3.8' 那么'end-1c' 就是'3.7'。我想再次推荐审查:Text widget indices.
这是我使用 python 3.5.2 的方法:
from tkinter import *
root=Tk()
def retrieve_input():
inputValue=textBox.get("1.0","end-1c")
print(inputValue)
textBox=Text(root, height=2, width=10)
textBox.pack()
buttonCommit=Button(root, height=1, width=10, text="Commit",
command=lambda: retrieve_input())
#command=lambda: retrieve_input() >>> just means do this when i press the button
buttonCommit.pack()
mainloop()
这样,当我在文本小部件中输入“blah blah”并按下按钮时,我输入的任何内容都会被打印出来。所以我认为这是将用户输入从文本小部件存储到变量的答案。
【讨论】:
要从 python 3 中的文本框中获取 Tkinter 输入,我使用的完整学生级程序如下:
#Imports all (*) classes,
#atributes, and methods of tkinter into the
#current workspace
from tkinter import *
#***********************************
#Creates an instance of the class tkinter.Tk.
#This creates what is called the "root" window. By conventon,
#the root window in Tkinter is usually called "root",
#but you are free to call it by any other name.
root = Tk()
root.title('how to get text from textbox')
#**********************************
mystring = StringVar()
####define the function that the signup button will do
def getvalue():
## print(mystring.get())
#*************************************
Label(root, text="Text to get").grid(row=0, sticky=W) #label
Entry(root, textvariable = mystring).grid(row=0, column=1, sticky=E) #entry textbox
WSignUp = Button(root, text="print text", command=getvalue).grid(row=3, column=0, sticky=W) #button
############################################
# executes the mainloop (that is, the event loop) method of the root
# object. The mainloop method is what keeps the root window visible.
# If you remove the line, the window created will disappear
# immediately as the script stops running. This will happen so fast
# that you will not even see the window appearing on your screen.
# Keeping the mainloop running also lets you keep the
# program running until you press the close buton
root.mainloop()
【讨论】:
Text 小部件。
为了在Text 小部件中获取字符串,可以简单地使用为Text 定义的get 方法,该方法接受1 到2 个参数作为start 和end 字符位置,text_widget_object.get(start, end=None)。如果只传递了start 而没有传递end,它只返回位于start 的单个字符,如果也传递了end ,它返回两个位置之间的所有字符start 和 end 作为字符串。
还有一些特殊的字符串,它们是底层 Tk 的变量。其中之一是"end" 或tk.END,它们表示Text 小部件中最后一个字符的可变位置。一个示例是返回小部件中的所有文本,如果您不想要最后一个换行符,则使用 text_widget_object.get('1.0', 'end') 或 text_widget_object.get('1.0', 'end-1c')。
请参阅下面的演示,该演示选择带有滑块的给定位置之间的字符:
try:
import tkinter as tk
except:
import Tkinter as tk
class Demo(tk.LabelFrame):
"""
A LabeFrame that in order to demonstrate the string returned by the
get method of Text widget, selects the characters in between the
given arguments that are set with Scales.
"""
def __init__(self, master, *args, **kwargs):
tk.LabelFrame.__init__(self, master, *args, **kwargs)
self.start_arg = ''
self.end_arg = None
self.position_frames = dict()
self._create_widgets()
self._layout()
self.update()
def _create_widgets(self):
self._is_two_args = tk.Checkbutton(self,
text="Use 2 positional arguments...")
self.position_frames['start'] = PositionFrame(self,
text="start='{}.{}'.format(line, column)")
self.position_frames['end'] = PositionFrame( self,
text="end='{}.{}'.format(line, column)")
self.text = TextWithStats(self, wrap='none')
self._widget_configs()
def _widget_configs(self):
self.text.update_callback = self.update
self._is_two_args.var = tk.BooleanVar(self, value=False)
self._is_two_args.config(variable=self._is_two_args.var,
onvalue=True, offvalue=False)
self._is_two_args['command'] = self._is_two_args_handle
for _key in self.position_frames:
self.position_frames[_key].line.slider['command'] = self.update
self.position_frames[_key].column.slider['command'] = self.update
def _layout(self):
self._is_two_args.grid(sticky='nsw', row=0, column=1)
self.position_frames['start'].grid(sticky='nsew', row=1, column=0)
#self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
self.text.grid(sticky='nsew', row=2, column=0,
rowspan=2, columnspan=2)
_grid_size = self.grid_size()
for _col in range(_grid_size[0]):
self.grid_columnconfigure(_col, weight=1)
for _row in range(_grid_size[1] - 1):
self.grid_rowconfigure(_row + 1, weight=1)
def _is_two_args_handle(self):
self.update_arguments()
if self._is_two_args.var.get():
self.position_frames['end'].grid(sticky='nsew', row=1, column=1)
else:
self.position_frames['end'].grid_remove()
def update(self, event=None):
"""
Updates slider limits, argument values, labels representing the
get method call.
"""
self.update_sliders()
self.update_arguments()
def update_sliders(self):
"""
Updates slider limits based on what's written in the text and
which line is selected.
"""
self._update_line_sliders()
self._update_column_sliders()
def _update_line_sliders(self):
if self.text.lines_length:
for _key in self.position_frames:
self.position_frames[_key].line.slider['state'] = 'normal'
self.position_frames[_key].line.slider['from_'] = 1
_no_of_lines = self.text.line_count
self.position_frames[_key].line.slider['to'] = _no_of_lines
else:
for _key in self.position_frames:
self.position_frames[_key].line.slider['state'] = 'disabled'
def _update_column_sliders(self):
if self.text.lines_length:
for _key in self.position_frames:
self.position_frames[_key].column.slider['state'] = 'normal'
self.position_frames[_key].column.slider['from_'] = 0
_line_no = int(self.position_frames[_key].line.slider.get())-1
_max_line_len = self.text.lines_length[_line_no]
self.position_frames[_key].column.slider['to'] = _max_line_len
else:
for _key in self.position_frames:
self.position_frames[_key].column.slider['state'] = 'disabled'
def update_arguments(self):
"""
Updates the values representing the arguments passed to the get
method, based on whether or not the 2nd positional argument is
active and the slider positions.
"""
_start_line_no = self.position_frames['start'].line.slider.get()
_start_col_no = self.position_frames['start'].column.slider.get()
self.start_arg = "{}.{}".format(_start_line_no, _start_col_no)
if self._is_two_args.var.get():
_end_line_no = self.position_frames['end'].line.slider.get()
_end_col_no = self.position_frames['end'].column.slider.get()
self.end_arg = "{}.{}".format(_end_line_no, _end_col_no)
else:
self.end_arg = None
self._update_method_labels()
self._select()
def _update_method_labels(self):
if self.end_arg:
for _key in self.position_frames:
_string = "text.get('{}', '{}')".format(
self.start_arg, self.end_arg)
self.position_frames[_key].label['text'] = _string
else:
_string = "text.get('{}')".format(self.start_arg)
self.position_frames['start'].label['text'] = _string
def _select(self):
self.text.focus_set()
self.text.tag_remove('sel', '1.0', 'end')
self.text.tag_add('sel', self.start_arg, self.end_arg)
if self.end_arg:
self.text.mark_set('insert', self.end_arg)
else:
self.text.mark_set('insert', self.start_arg)
class TextWithStats(tk.Text):
"""
Text widget that stores stats of its content:
self.line_count: the total number of lines
self.lines_length: the total number of characters per line
self.update_callback: can be set as the reference to the callback
to be called with each update
"""
def __init__(self, master, update_callback=None, *args, **kwargs):
tk.Text.__init__(self, master, *args, **kwargs)
self._events = ('<KeyPress>',
'<KeyRelease>',
'<ButtonRelease-1>',
'<ButtonRelease-2>',
'<ButtonRelease-3>',
'<Delete>',
'<<Cut>>',
'<<Paste>>',
'<<Undo>>',
'<<Redo>>')
self.line_count = None
self.lines_length = list()
self.update_callback = update_callback
self.update_stats()
self.bind_events_on_widget_to_callback( self._events,
self,
self.update_stats)
@staticmethod
def bind_events_on_widget_to_callback(events, widget, callback):
"""
Bind events on widget to callback.
"""
for _event in events:
widget.bind(_event, callback)
def update_stats(self, event=None):
"""
Update self.line_count, self.lines_length stats and call
self.update_callback.
"""
_string = self.get('1.0', 'end-1c')
_string_lines = _string.splitlines()
self.line_count = len(_string_lines)
del self.lines_length[:]
for _line in _string_lines:
self.lines_length.append(len(_line))
if self.update_callback:
self.update_callback()
class PositionFrame(tk.LabelFrame):
"""
A LabelFrame that has two LabelFrames which has Scales.
"""
def __init__(self, master, *args, **kwargs):
tk.LabelFrame.__init__(self, master, *args, **kwargs)
self._create_widgets()
self._layout()
def _create_widgets(self):
self.line = SliderFrame(self, orient='vertical', text="line=")
self.column = SliderFrame(self, orient='horizontal', text="column=")
self.label = tk.Label(self, text="Label")
def _layout(self):
self.line.grid(sticky='ns', row=0, column=0, rowspan=2)
self.column.grid(sticky='ew', row=0, column=1, columnspan=2)
self.label.grid(sticky='nsew', row=1, column=1)
self.grid_rowconfigure(1, weight=1)
self.grid_columnconfigure(1, weight=1)
class SliderFrame(tk.LabelFrame):
"""
A LabelFrame that encapsulates a Scale.
"""
def __init__(self, master, orient, *args, **kwargs):
tk.LabelFrame.__init__(self, master, *args, **kwargs)
self.slider = tk.Scale(self, orient=orient)
self.slider.pack(fill='both', expand=True)
if __name__ == '__main__':
root = tk.Tk()
demo = Demo(root, text="text.get(start, end=None)")
with open(__file__) as f:
demo.text.insert('1.0', f.read())
demo.text.update_stats()
demo.pack(fill='both', expand=True)
root.mainloop()
【讨论】:
我认为这是一个更好的方法-
variable1=StringVar() # Value saved here
def search():
print(variable1.get())
return ''
ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)
ttk.Label(mainframe, text="label").grid(column=1, row=1)
ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)
按下按钮时,将打印文本字段中的值。 但请确保您单独导入 ttk。
基本应用程序的完整代码是-
from tkinter import *
from tkinter import ttk
root=Tk()
mainframe = ttk.Frame(root, padding="10 10 12 12")
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)
variable1=StringVar() # Value saved here
def search():
print(variable1.get())
return ''
ttk.Entry(mainframe, width=7, textvariable=variable1).grid(column=2, row=1)
ttk.Label(mainframe, text="label").grid(column=1, row=1)
ttk.Button(mainframe, text="Search", command=search).grid(column=2, row=13)
root.mainloop()
【讨论】:
我遇到了从文本小部件获取整个文本的问题,以下解决方案对我有用:
txt.get(1.0,END)
其中 1.0 表示第一行,第零个字符(即在第一个之前!) 是开始位置,END是结束位置。
感谢 Alan Gauld 在此link
【讨论】:
1.0 是不正确的。索引是字符串,而不是浮点数。 Tkinter 在这方面有点宽容,所以 1.0 和 "1.0" 被视为相同,但像 1.10 这样的东西肯定与 "1.10" 不同。
我也确实是来寻找如何从 Text 小部件获取输入数据的。关于字符串末尾换行的问题。您可以只使用 .strip(),因为它是一个始终是字符串的 Text 小部件。
另外,我正在共享代码,您可以在其中看到如何创建多个 Text 小部件并将它们作为表单数据保存在字典中,然后通过单击提交按钮获取该表单数据并使用它做任何您想做的事情。我希望它可以帮助其他人。它应该可以在任何 3.x python 中运行,并且可能也可以在 2.7 中运行。
from tkinter import *
from functools import partial
class SimpleTkForm(object):
def __init__(self):
self.root = Tk()
def myform(self):
self.root.title('My form')
frame = Frame(self.root, pady=10)
form_data = dict()
form_fields = ['username', 'password', 'server name', 'database name']
cnt = 0
for form_field in form_fields:
Label(frame, text=form_field, anchor=NW).grid(row=cnt,column=1, pady=5, padx=(10, 1), sticky="W")
textbox = Text(frame, height=1, width=15)
form_data.update({form_field: textbox})
textbox.grid(row=cnt,column=2, pady=5, padx=(3,20))
cnt += 1
conn_test = partial(self.test_db_conn, form_data=form_data)
Button(frame, text='Submit', width=15, command=conn_test).grid(row=cnt,column=2, pady=5, padx=(3,20))
frame.pack()
self.root.mainloop()
def test_db_conn(self, form_data):
data = {k:v.get('1.0', END).strip() for k,v in form_data.items()}
# validate data or do anything you want with it
print(data)
if __name__ == '__main__':
api = SimpleTkForm()
api.myform()
【讨论】:
strip 最终可能会删除不止一个尾随换行符,如果小部件有多个空行。如果目标是准确获取用户输入的内容,那么这不是正确的解决方案。
我认为创建一个简单的 Text 扩展并将 text 转换为属性是最干净的方法。然后,您可以将该扩展名粘贴到您始终导入的某个文件中,并使用它代替原始的 Text 小部件。这样,您不必记住、编写、重复等所有 tkinter 让您跳过做最简单的事情的箍,您有一个可以在任何项目中重用的简单接口。您也可以对 Entry 执行此操作,但语法略有不同。
import tkinter as tk
root = tk.Tk()
class Text(tk.Text):
@property
def text(self) -> str:
return self.get('1.0', 'end-1c')
@text.setter
def text(self, value) -> None:
self.replace('1.0', 'end-1c', value)
def __init__(self, master, **kwargs):
tk.Text.__init__(self, master, **kwargs)
#Entry version of the same concept as above
class Entry(tk.Entry):
@property
def text(self) -> str:
return self.get()
@text.setter
def text(self, value) -> None:
self.delete(0, 'end')
self.insert(0, value)
def __init__(self, master, **kwargs):
tk.Entry.__init__(self, master, **kwargs)
textbox = Text(root)
textbox.grid()
textbox.text = "this is text" #set
print(textbox.text) #get
entry = Entry(root)
entry.grid()
entry.text = 'this is text' #set
print(entry.text) #get
root.mainloop()
【讨论】:
tk.Text,我可以很容易地同意你的看法……关于tk.Entry,我很容易同意。
假设您有一个名为 my_text_widget 的 Text 小部件。
要获取来自my_text_widget 的输入,您可以使用get 函数。
假设您已导入tkinter。
让我们先定义my_text_widget,让它只是一个简单的文本小部件。
my_text_widget = Text(self)
要获取来自 text 小部件的输入,您需要使用 get 函数,text 和 entry 小部件都有此功能。
input = my_text_widget.get()
我们将它保存到变量中的原因是为了在进一步的过程中使用它,例如,测试输入是什么。
【讨论】:
Text 小部件get 方法至少需要一个参数。