จะรับข้อมูลจาก Tkinter Text Widget ได้อย่างไร?


109

จะรับอินพุต Tkinter จากTextวิดเจ็ตได้อย่างไร?

แก้ไข

ฉันถามคำถามนี้เพื่อช่วยเหลือผู้อื่นที่มีปัญหาเดียวกันนั่นคือสาเหตุที่ไม่มีโค้ดตัวอย่าง ปัญหานี้ทำให้ฉันหนักใจมาหลายชั่วโมงแล้วและฉันใช้คำถามนี้เพื่อสอนคนอื่น ๆ โปรดอย่าให้คะแนนราวกับว่าเป็นคำถามจริงคำตอบคือสิ่งที่สำคัญ

คำตอบ:


140

ในการรับอินพุต Tkinter จากกล่องข้อความคุณต้องเพิ่มแอตทริบิวต์อีกสองสามรายการใน.get()ฟังก์ชันปกติ หากเรามีกล่องข้อความmyText_Boxนี่คือวิธีการดึงข้อมูลที่ป้อนเข้ามา

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

ส่วนแรก"1.0"หมายความว่าอินพุตควรอ่านจากบรรทัดที่หนึ่งอักขระศูนย์ (เช่น: อักขระตัวแรก) เป็นค่าคงที่นำเข้าซึ่งเป็นชุดที่สตริงEND ส่วนหนึ่งหมายถึงการอ่านจนกว่าจะสิ้นสุดของกล่องข้อความถึง ปัญหาเดียวของเรื่องนี้คือการเพิ่มบรรทัดใหม่ในข้อมูลของเรา ดังนั้นในการแก้ไขเราควรเปลี่ยนเป็น(ขอบคุณBryan Oakley ) การลบอักขระ 1 ตัวในขณะที่หมายถึงลบสองตัวอักษรและอื่น ๆ"end"ENDENDend-1c-1c-2c

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

20
คุณควรทำ"end-1c"หรือEND+"1c"มิฉะนั้นคุณจะได้รับบรรทัดใหม่พิเศษที่วิดเจ็ตข้อความจะเพิ่มเสมอ
Bryan Oakley

2
@xxmbabanexx: ไม่ "-1c" หมายถึง "ลบหนึ่งอักขระ"
Bryan Oakley

2
นี่คือสิ่งที่คุณต้องการ:.get('1.0', 'end-1c')
Honest Abe

1
ขอบคุณ! ด้วยความอยากรู้อยากเห็นถ้าฉันจะเขียนend+1cมันจะเพิ่มบรรทัดใหม่ให้กับโค้ดหรือไม่? สุดท้ายนี้ Bryan และ Honest Abe ขอขอบคุณพวกคุณมากที่ช่วยตอบคำถาม Tkinter และ Python ง่ายๆของฉัน คุณช่วยให้ฉันเข้าใจภาษาได้ลึกซึ้งขึ้นจริง ๆ และเป็นคนที่มีความสุภาพรวดเร็วและดีที่สุดคือมีความรู้ ฉันแน่ใจว่าคำแนะนำของคุณจะช่วยฉันได้เมื่อฉันย้ายไปเรียนมัธยมปลายและที่อื่น ๆ !
xxmbabanexx

1
ตัวอย่างที่คุณเพิ่มไม่ได้ผล เครื่องหมายคำพูดรอบ'end-1c'จำเป็นเพื่อให้เป็นสตริงเดียว 'end'เป็นนามแฝงสำหรับดัชนีหลังอักขระสุดท้าย ดังนั้นถ้า'end'เป็น'3.8'แล้วจะเป็น'end-1c' '3.7'ผมอยากจะแนะนำอีกครั้ง reviewing: ดัชนีวิดเจ็ตข้อความ
Honest Abe

20

นี่คือวิธีที่ฉันทำกับ 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" ในวิดเจ็ตข้อความและกดปุ่มสิ่งที่ฉันพิมพ์จะถูกพิมพ์ออกมา ดังนั้นฉันคิดว่านั่นคือคำตอบสำหรับการจัดเก็บอินพุตของผู้ใช้จากวิดเจ็ตข้อความไปยังตัวแปร


9

ในการรับอินพุต Tkinter จากกล่องข้อความใน python 3 โปรแกรมระดับนักเรียนที่สมบูรณ์ที่ฉันใช้อยู่ภายใต้:

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

7

เพื่อให้ได้สตริงในส่วนTextหนึ่งเครื่องมือสามารถเพียงแค่ใช้getวิธีการที่กำหนดไว้สำหรับการTextที่รับ 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()

2

ฉันคิดว่านี่เป็นวิธีที่ดีกว่า -

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

0

ฉันยังค้นหาวิธีรับข้อมูลอินพุตจากวิดเจ็ตข้อความ เกี่ยวกับปัญหาเกี่ยวกับบรรทัดใหม่ที่ส่วนท้ายของสตริง คุณสามารถใช้ .strip () เนื่องจากเป็นวิดเจ็ตข้อความที่เป็นสตริงเสมอ

นอกจากนี้ฉันกำลังแชร์รหัสที่คุณสามารถดูว่าคุณสามารถสร้างวิดเจ็ตข้อความทวีคูณและบันทึกไว้ในพจนานุกรมเป็นข้อมูลแบบฟอร์มได้อย่างไรจากนั้นคลิกปุ่มส่งเพื่อรับข้อมูลแบบฟอร์มนั้นและทำสิ่งที่คุณต้องการ ฉันหวังว่ามันจะช่วยคนอื่น ๆ มันควรจะทำงานใน python 3.x ใด ๆ และอาจจะทำงานใน 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อาจจบลงด้วยการลบมากกว่าการขึ้นบรรทัดใหม่เพียงบรรทัดเดียวหากวิดเจ็ตมีบรรทัดว่างหลายบรรทัด หากเป้าหมายคือเพื่อให้ได้สิ่งที่ผู้ใช้ป้อนอย่างถูกต้องนี่ไม่ใช่วิธีแก้ปัญหาที่ถูกต้อง
Bryan Oakley

0

ฉันขอยืนยันว่าการสร้างส่วนขยายข้อความง่ายๆและเปลี่ยน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()

การใช้ setter นั้นดีถ้าสิ่งที่คุณต้องการคือเนื้อหาทั้งหมดของวิดเจ็ตข้อความ อย่างไรก็ตามมักเป็นกรณีที่คุณต้องแยกข้อความเพียงบางส่วน (ตัวอย่างเช่นเมื่อตัดคำประโยคหรือย่อหน้า) นั่นเป็นเหตุผลว่าทำไม "ห่วง" เหล่านั้นจึงมีอยู่
Bryan Oakley

@BryanOakley - ฉันสามารถตกลงได้อย่างง่ายดายกับคุณเกี่ยวกับtk.Text... tk.Entryไม่มากเกี่ยวกับ
Michael Guidry

-1

ฉันประสบปัญหาในการรับข้อความทั้งหมดจากวิดเจ็ตข้อความและวิธีแก้ปัญหาต่อไปนี้ใช้ได้กับฉัน:

txt.get(1.0,END)

โดยที่ 1.0 หมายถึงบรรทัดแรกอักขระ zeroth (เช่นก่อนหน้า!) คือตำแหน่งเริ่มต้นและ END คือตำแหน่งสิ้นสุด

ขอบคุณ Alan Gauld ในลิงค์นี้


อาร์กิวเมนต์แรกควรอยู่ในเครื่องหมายคำพูด ไม่ใช่ตัวเลขลอย แต่เป็นสตริงที่มีจำนวนเต็มสองจำนวนและตัวคั่นจุด
RufusVS

ลิงก์นั้นมีข้อผิดพลาดนั้นและแม้แต่แหล่งที่มาของลิงก์นั้นก็ผิดพลาด นี่คือลิงค์ที่ดีกว่า: mail.python.org/pipermail/tutor/2005-Feb February/035667.html
RufusVS

สิ่งนี้จะให้ปัญหาเดียวกันกับที่เขียนไว้ในคำถาม นอกจากนี้ในทางเทคนิคดัชนี1.0ไม่ถูกต้อง ดัชนีคือสตริงไม่ใช่ตัวเลขทศนิยม Tkinter เป็นบิตให้อภัยในเรื่องนี้เพื่อให้1.0และ"1.0"ได้รับการปฏิบัติเหมือนกัน แต่บางอย่างเช่นไม่แน่นอนเช่นเดียวกับ1.10 "1.10"
Bryan Oakley

-3

ช่วยบอกว่าคุณมีเครื่องมือที่เรียกว่าTextmy_text_widget

ในการรับข้อมูลจากmy_text_widgetคุณสามารถใช้getฟังก์ชัน

tkinterสมมติว่าคุณได้นำเข้า ให้กำหนดmy_text_widgetก่อนให้มันเป็นเพียงวิดเจ็ตข้อความธรรมดา

my_text_widget = Text(self)

ในการรับอินพุตจากtextวิดเจ็ตคุณต้องใช้getฟังก์ชันทั้งสองอย่างtextและentryวิดเจ็ตมีสิ่งนี้

input = my_text_widget.get()

เหตุผลที่เราบันทึกลงในตัวแปรคือเพื่อใช้ในกระบวนการต่อไปตัวอย่างเช่นการทดสอบว่าอินพุตคืออะไร


1
คำตอบนี้ไม่ถูกต้อง Textเครื่องมือgetวิธีการต้องมีอย่างน้อยหนึ่งอาร์กิวเมนต์
Bryan Oakley
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.