如何从 Tkinter Text Widget 获取输入?

2025-01-14 08:50:00
admin
原创
108
摘要:问题描述:如何从小Text部件获取 Tkinter 输入?编辑我问这个问题是为了帮助遇到同样问题的人 -这就是没有示例代码的原因。这个问题困扰了我好几个小时,我用这个问题来教导别人。请不要把它当作一个真正的问题来评价 - 答案才是最重要的。解决方案 1:要从文本框获取 Tkinter 输入,您必须向常规函数添...

问题描述:

如何从小Text部件获取 Tkinter 输入?

编辑

我问这个问题是为了帮助遇到同样问题的人 -就是没有示例代码的原因。这个问题困扰了我好几个小时,我用这个问题来教导别人。不要把它当作一个真正的问题来评价 - 答案才是最重要的。


解决方案 1:

要从文本框获取 Tkinter 输入,您必须向常规函数添加一些属性.get()。如果我们有一个文本框myText_Box,那么这就是检索其输入的方法。

def retrieve_input():
    input = self.myText_Box.get("1.0",END)

第一部分"1.0"表示应从第一行第 0 个字符(即第一个字符)开始读取输入。END是一个导入的常量,设置为字符串"end"。 该END部分表示读取直到到达文本框末尾。 唯一的问题是它实际上会在我们的输入中添加一个换行符。 因此,为了修复它,我们应该更改ENDend-1c(感谢Bryan Oakley)-1c删除 1 个字符,而-2c表示删除 2 个字符,依此类推。

def retrieve_input():
    input = self.myText_Box.get("1.0",'end-1c')

解决方案 2:

以下是我使用 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”并按下按钮时,我输入的内容都会被打印出来。所以我认为这就是将用户输入从文本小部件存储到变量的答案。

解决方案 3:

为了从 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()

解决方案 4:

为了获取Text小部件中的字符串,可以简单地使用get为 定义的方法来Text接受 1 到 2 个参数作为startend字符位置,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()

解决方案 5:

我面临从文本小部件获取整个文本的问题,并且以下解决方案对我有用:

txt.get("1.0", tk.END)

其中"1.0"表示第一行,第零个字符(即第一个字符之前!)是起始位置,tk.END是结束位置。

感谢 Alan Gauld 在此链接中

解决方案 6:

我认为创建 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()

关于 Bryan Oakley 的评论,我有一个更新的解决方案。Text 小部件insert、、和方法可以分别重新连接到切片或键。通过这样做delete,我们还可以稍微扩展可接受的 Text 索引,并根据您的暗示默认一些索引。replace`get`

import tkinter as tk
from typing import Any

class Text(tk.Text):
    @property
    def text(self) -> str:
        return self.get('1.0', tk.END)
        
    @text.setter
    def text(self, value:str) -> None:
        self.replace('1.0', tk.END, value)

    # test and get indices
    def __getindices(self, key) -> tuple|None:
        def indice(idx:str|int|float) -> str:
            # allow int indices
            if (idx := f'{idx}').isdigit():
                idx = f'{idx}.0'
            
            try: 
                self.get(idx, idx)
            except tk.TclError: 
                print(f'({idx}) is not a valid index')
                idx = ''
                
            return idx
            
        if isinstance(key, slice):
            start = indice(key.start or '1.0')
            stop  = indice(key.stop  or 'end-1c')
            if start and stop:
                return start, stop
        elif start := indice(key):
            return start, None
            
        return None

    # replace if slice, insert if single indice      
    def __setitem__(self, key, value) -> None:
        if idx := self.__getindices(key):
            start, stop = idx
            if start:
                if stop: self.replace(start, stop, value)
                else   : self.insert(start, value)
                return
            
        super().__setitem__(key, value)
        
    # get slice range
    def __getitem__(self, key) -> Any:
        if not (None in (idx := self.__getindices(key))):
            return self.get(*idx)
            
        return super().__getitem__(key)
      
    # delete slice range
    def __delitem__(self, key) -> None:
        if not (None in (idx := self.__getindices(key))):
            self.delete(*idx)
            return
            
        super().__delitem__(key)
用法
root = tk.Tk()
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

text = Text(root)
text.text = "abcdefghijklmnopqrstuvwxyz"
text.grid(sticky='nswe')

def deleter(event=None):
    del text['1.0':'1.3']
    return "break"
    
def printer(event=None):
    print(text['insert':'insert+3c'])
    return "break"
    
def setter(event=None):
    text['insert':'insert+3c'] = 'xyz'     # replace in slice range
    text[:]                    = 'abcdef'  # replace - ['1.0':'end-1c'] is implied
    text[1.12]                 = 'abcdef'  # insert at 1.12 - floats allowed
    text[1:'1.end']            = 'xyzabc'  # replace in slice range - ints allowed
    return "break"

text.bind('<KeyPress-Delete>', deleter)
text.bind('<KeyPress-End>', printer)
text.bind('<KeyPress-Insert>', setter)

root.mainloop()

解决方案 7:

我认为这是一个更好的方法-

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()

解决方案 8:

我也来搜索如何从 Text 小部件获取输入数据。关于字符串末尾的新行问题。您可以使用 .strip(),因为它是一个始终是字符串的 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()

解决方案 9:

假设您有一个Text名为 的小部件my_text_widget

获取输入,my_text_widget您可以使用该get函数。

假设您已导入tkinter。让我们my_text_widget先定义一下,让它成为一个简单的文本小部件。

my_text_widget = Text(self)

要从小部件获取输入text,您需要使用该get函数,textentry小部件都具有该函数。

input = my_text_widget.get()

我们将其保存到变量的原因是为了在进一步的过程中使用它,例如,测试输入是什么。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用