how to print the contents of the Text widget in python [duplicate] - python
How to get Tkinter input from the Text widget?
EDIT
I asked this question to help others with the same problem - that is the reason why there is no example code. This issue had been troubling me for hours and I used this question to teach others. Please do not rate it as if it was a real question - the answer is the thing that matters.
To get Tkinter input from the text box, you must add a few more attributes to the normal .get() function. If we have a text box myText_Box, then this is the method for retrieving its input.
def retrieve_input():
input = self.myText_Box.get("1.0",END)
The first part, "1.0" means that the input should be read from line one, character zero (ie: the very first character). END is an imported constant which is set to the string "end". The END part means to read until the end of the text box is reached. The only issue with this is that it actually adds a newline to our input. So, in order to fix it we should change END to end-1c(Thanks Bryan Oakley) The -1c deletes 1 character, while -2c would mean delete two characters, and so on.
def retrieve_input():
input = self.myText_Box.get("1.0",'end-1c')
Here is how I did it with 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()
with that, when i typed "blah blah" in the text widget and pressed the button, whatever i typed got printed out. So i think that is the answer for storing user input from Text widget to variable.
To get Tkinter input from the text box in python 3 the complete student level program used by me is as under:
#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()
In order to obtain the string in a Text widget one can simply use get method defined for Text which accepts 1 to 2 arguments as start and end positions of characters, text_widget_object.get(start, end=None). If only start is passed and end isn't passed it returns only the single character positioned at start, if end is passed as well, it returns all characters in between positions start and end as string.
There are also special strings, that are variables to the underlying Tk. One of them would be "end" or tk.END which represents the variable position of the very last char in the Text widget. An example would be to returning all text in the widget, with text_widget_object.get('1.0', 'end') or text_widget_object.get('1.0', 'end-1c') if you don't want the last newline character.
Demo
See below demonstration that selects the characters in between the given positions with sliders:
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()
I think this is a better way-
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)
On pressing the button, the value in the text field would get printed.
But make sure You import the ttk separately.
The full code for a basic application is-
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()
I faced the problem of gettng entire text from Text widget and following solution worked for me :
txt.get(1.0,END)
Where 1.0 means first line, zeroth character (ie before the first!)
is the starting position and END is the ending position.
Thanks to Alan Gauld in this link
I did come also in search of how to get input data from the Text widget. Regarding the problem with a new line on the end of the string. You can just use .strip() since it is a Text widget that is always a string.
Also, I'm sharing code where you can see how you can create multiply Text widgets and save them in the dictionary as form data, and then by clicking the submit button get that form data and do whatever you want with it. I hope it helps others. It should work in any 3.x python and probably will work in 2.7 also.
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()
I would argue that creating a simple extension of Text and turning text into a property is the cleanest way to go. You can then stick that extension in some file that you always import, and use it instead of the original Text widget. This way, instead of having to remember, write, repeat, etc all the hoops tkinter makes you jump through to do the simplest things, you have a butt-simple interface that can be reused in any project. You can do this for Entry, as well, but the syntax is slightly different.
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()
Lets say that you have a Text widget called my_text_widget.
To get input from the my_text_widget you can use the get function.
Let's assume that you have imported tkinter.
Lets define my_text_widget first, lets make it just a simple text widget.
my_text_widget = Text(self)
To get input from a text widget you need to use the get function, both, text and entry widgets have this.
input = my_text_widget.get()
The reason we save it to a variable is to use it in the further process, for example, testing for what's the input.
Related
Get search terms from input box
I am trying to create a GUI for an auto-complete prototype and am new to tkinter. I want to get the entire input when Space is pressed but I am unable to do so. The idea is to get all the entries in the text box so that I can do some analysis inside a function call. This is the code: def kp(event): app.create_widgets(1) import random def getFromScript(text): #########THIS IS A PLACE HOLDER FOR ACTUAL IMPLEMENTATION i= random.randint(1,100) return ['hello'+str(i),'helou'+text] from tkinter import * class Application(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.pack() self.create_widgets(0) # Create main GUI window def create_widgets(self,i): self.search_var = StringVar() self.search_var.trace("w", lambda name, index, mode: self.update_list(i)) self.lbox = Listbox(self, width=45, height=15) if i==0: self.entry = Entry(self, textvariable=self.search_var, width=13) self.entry.grid(row=0, column=0, padx=10, pady=3) self.lbox.grid(row=1, column=0, padx=10, pady=3) # Function for updating the list/doing the search. # It needs to be called here to populate the listbox. self.update_list(i) def update_list(self,i): search_term = self.search_var.get()#### THIS LINE SHOULD READ THE TEXT # Just a generic list to populate the listbox if(i==0): lbox_list = ['Excellent','Very Good','Shabby', 'Unpolite'] if(i==1): lbox_list = getFromScript(search_term)####### PASS TEXT HERE self.lbox.delete(0, END) for item in lbox_list: if search_term.lower() in item.lower(): self.lbox.insert(END, item) root = Tk() root.title('Filter Listbox Test') root.bind_all('<space>', kp) app = Application(master=root) app.mainloop() Any kind of help is highly appreciable. Thanks in advance
Problem is you are creating a new StringVar on each create_widgets call. Create StringVar in your __init__. class Application(Frame): def __init__(self, master=None): ... self.search_var = StringVar() ...
python - binding to a tkinter widget is called twice if set() is called on a StringVar()
I have an entry, a listbox(dropdown) and another listbox. Whenever more than 3 characters are typed inside the entry. A completion list is looked up and inserted to the dropdown and the dropdown is shown. If an item is selected from the dropdown. It's value should fill the entry and the entry should get the focus again and the cursor should go to the end of the entry. And then, when Enter key is pressed the value of the entry should be inserted to the other listbox. I've developed a code for that with much help from this utility and the code works perfectly fine. Except, I realized that whenever I select an option from the dropdown the corresponding method is called twice(I get two prints in the console from the same thing). But if I select the first option of the dropdown, it's called once(which is what should have actually happened in the other case) but the focus does not go to the entry (which is a problem). Here is my code: from tkinter import * class Autocomplete(Frame, object): def __init__(self, width, height, entries, *args, **kwargs): super(Autocomplete, self).__init__(*args, **kwargs) self._entries = entries self.listbox_height = height self.entry_width = width self.text = StringVar() self.entry = Entry( self, textvariable=self.text, width=self.entry_width ) self.frame = Frame(self) self.listbox = Listbox( self.frame, height=self.listbox_height, width=self.entry_width ) self.dropdown = Listbox( self.frame, height=self.listbox_height, width=self.entry_width, background="#cfeff9" ) def build(self): self.text.trace("w", lambda name, index, mode, text=self.text: self._update_autocomplete()) self.entry.bind("<Return>", lambda event,: self._add_course()) self.entry.focus_set() self.entry.pack() self.frame.pack() self.listbox.grid(column=0, row=0, sticky=N) self.dropdown.bind("<<ListboxSelect>>", self._select_entry) self.dropdown.grid(column=0, row=0, sticky=N) self.dropdown.grid_forget() return self def _update_autocomplete(self): self.dropdown["height"] = self.listbox_height self.dropdown.delete(0, END) text = self.text.get() if len(text) < 3: self.dropdown.grid_forget() return else: for entry in self._entries: if text.lower() in entry.strip().lower(): self.dropdown.insert(END, entry) listbox_size = self.dropdown.size() if not listbox_size: self.dropdown.insert(END, "No results found for '{}'") self.dropdown["height"] = 1 else: if listbox_size <= self.dropdown["height"]: self.dropdown["height"] = listbox_size self.dropdown.grid(column=0, row=0, sticky=N) def _select_entry(self, event): widget = event.widget value = widget.get(int(widget.curselection()[0])) print(value) self.text.set(value) self.entry.focus_set() self.entry.icursor(END) def _add_course(self): self.listbox.insert(END, self.text.get()) So what am I missing here? By the way any general improvement to the code will also be much appreciated. And here is how I call it: from tkinter import * from autocomplete import Autocomplete from main import * courses = load_courses_from_file("courses.txt") root = Tk() autocomplete_frame = Autocomplete( 60, 10, list(set(course.name + ", " + course.instructor for course in courses)) ).build().pack() mainloop()
The selection of the listbox changes when you click on the item -- this is the default behavior of the listbox. This causes the entry widget value to change, which triggers a call to _update_autocomplete. That function deletes everything in the listbox, causing the selection to change again.
Visible textvariable in tkinter Entry widget
I'm having trouble with using the Entry box widget in tkinter. I want to have a new window to open when the user selects Edit -> Backbone... . In this window there will be a number of Entry widgets (just one coded for below for simplicity) that show default Strings stored in various instances the class Elements. The user should be able to edit this string and save it by clicking OK and returning it to its default value by clicking default. The entry box should always show the current value of the variable each time the backbone editor is reopened (If the whole program is restarted it does not need to remember the user input). Upon opening the 'Backbone Editor' window the Entry box should show the string text variable but I can't make it appear. from tkinter import * from tkinter import ttk class View(ttk.Frame): """Main GUI class""" def __init__(self, master = None): self.WIDTH = 450 self.HEIGHT = 500 self.lib = MolecularLibrary() # Set up the main window ttk.Frame.__init__(self, master, borderwidth=5, width=self.WIDTH, height=self.WIDTH) self.master.resizable(FALSE, FALSE) self.grid(column=0, row=0, sticky=(N, S, E, W)) self.columnconfigure(0, weight=1) self.create_menus() def create_menus(self): """Produces the menu layout for the main window""" self.master.option_add('*tearOff', FALSE) self.menubar = Menu(self.master) self.master['menu'] = self.menubar # Menu Variables menu_edit = Menu(self.menubar) # Add the menus to the menubar and assign their variables self.menubar.add_cascade(menu=menu_edit, label = "Edit") ### Edit ### menu_edit.add_command(label="Backbone...", command=lambda : self.edit_backbone()) def edit_backbone(self): """Shows a window where the backbone constituents can be edited""" backbone_window = Toplevel(borderwidth = 5) backbone_window.title("Backbone Editor") backbone_window.resizable(FALSE, FALSE) print("sugar value", self.lib.sugar_var) # LABELS FOR BACKBONE # # Phosphate annotations and input sugar_label = ttk.Label(backbone_window, text = "Sugar") #inputs sugar = ttk.Entry(backbone_window, textvariable = self.lib.sugar_var, justify = 'center', width=10) ### Buttons ### default = ttk.Button(backbone_window, text = "Defaults", command=lambda : defaults()) okay = ttk.Button(backbone_window, text = "Okay", command=lambda : okay()) cancel = ttk.Button(backbone_window, text = "Cancel", command=lambda : backbone_window.destroy()) #content.grid(column=0, row=0) sugar_label.grid(column=2, row=1) sugar.grid(column=1, row=2, columnspan=3) default.grid(column=0, row=12, columnspan=3, pady=2) okay.grid(column=6, row=12, columnspan=3, pady=2) cancel.grid(column=9, row=12, columnspan=4, pady=2) backbone_window.focus() def defaults(): """Reset the backbone and bases to their defaults.""" self.lib.set_molecules() def okay(): """Set the backbone and base variables to the user set values.""" self.lib.sugar_var.new_formula(sugar.get()) backbone_window.destroy() class MolecularLibrary: """ """ def __init__(self, atom_file = r'C:\MyPyProgs\OSeq\resources\ATOMS.txt', precision = 4): self.molecules = {} self.atom_file = atom_file # self.molecule_file = molecule_file # Variables self.set_molecules() def set_molecules(self): """ Set all of the molecules for the backbone and bases to their default values and forumlae. """ ### Sugar ### self.sugar_var = Element('C5H8O3', 'A') def add_molecule(self, molecule): """(MolecularLibrary, list) -> None Returns a dictionary of the molecule name as an Element {molecule[0]: Element} """ print(molecule) tmp = self.get_mass(molecule[1]) return {molecule[0]: Element(molecule[1], molecule[0], tmp[0], tmp[0])} class Element: """ Creates an element with the following construct: [symbol, name, monoisotopic, average] """ def __init__(self, symbol, name): self.symbol = symbol self.name = name def __str__(self): return str([self.symbol, self.name]) def get_name(self): """Returns the name of the Element""" return self.name def get_symbol(self): """Returns the symbol of the Element""" return self.symbol def new_formula(self, new_formula): """replace the formula with new_formaula and recalculate the average and monoisotopic masses.""" self.symbol = new_formula if __name__ == "__main__": root = Tk() root.title("Sequencer") view = View(root) root.mainloop() The code above is a heavily stripped version of my program but has the same basic architecture. I'm afraid there is still quite a lot of code, I would normally try strip it down more, but I'm not sure if the issue I'm having is from the architecture or not.
In order to use the textvariable attribute, you must give it an instance of a tkinter variable: StringVar, IntVar, BooleanVar, or DoubleVar A good starting point to learn more about these variables is here: http://effbot.org/tkinterbook/variable.htm
How to get the input from the Tkinter Text Widget?
How to get Tkinter input from the Text widget? EDIT I asked this question to help others with the same problem - that is the reason why there is no example code. This issue had been troubling me for hours and I used this question to teach others. Please do not rate it as if it was a real question - the answer is the thing that matters.
To get Tkinter input from the text box, you must add a few more attributes to the normal .get() function. If we have a text box myText_Box, then this is the method for retrieving its input. def retrieve_input(): input = self.myText_Box.get("1.0",END) The first part, "1.0" means that the input should be read from line one, character zero (ie: the very first character). END is an imported constant which is set to the string "end". The END part means to read until the end of the text box is reached. The only issue with this is that it actually adds a newline to our input. So, in order to fix it we should change END to end-1c(Thanks Bryan Oakley) The -1c deletes 1 character, while -2c would mean delete two characters, and so on. def retrieve_input(): input = self.myText_Box.get("1.0",'end-1c')
Here is how I did it with 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() with that, when i typed "blah blah" in the text widget and pressed the button, whatever i typed got printed out. So i think that is the answer for storing user input from Text widget to variable.
To get Tkinter input from the text box in python 3 the complete student level program used by me is as under: #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()
In order to obtain the string in a Text widget one can simply use get method defined for Text which accepts 1 to 2 arguments as start and end positions of characters, text_widget_object.get(start, end=None). If only start is passed and end isn't passed it returns only the single character positioned at start, if end is passed as well, it returns all characters in between positions start and end as string. There are also special strings, that are variables to the underlying Tk. One of them would be "end" or tk.END which represents the variable position of the very last char in the Text widget. An example would be to returning all text in the widget, with text_widget_object.get('1.0', 'end') or text_widget_object.get('1.0', 'end-1c') if you don't want the last newline character. Demo See below demonstration that selects the characters in between the given positions with sliders: 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()
I think this is a better way- 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) On pressing the button, the value in the text field would get printed. But make sure You import the ttk separately. The full code for a basic application is- 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()
I faced the problem of gettng entire text from Text widget and following solution worked for me : txt.get(1.0,END) Where 1.0 means first line, zeroth character (ie before the first!) is the starting position and END is the ending position. Thanks to Alan Gauld in this link
I did come also in search of how to get input data from the Text widget. Regarding the problem with a new line on the end of the string. You can just use .strip() since it is a Text widget that is always a string. Also, I'm sharing code where you can see how you can create multiply Text widgets and save them in the dictionary as form data, and then by clicking the submit button get that form data and do whatever you want with it. I hope it helps others. It should work in any 3.x python and probably will work in 2.7 also. 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()
I would argue that creating a simple extension of Text and turning text into a property is the cleanest way to go. You can then stick that extension in some file that you always import, and use it instead of the original Text widget. This way, instead of having to remember, write, repeat, etc all the hoops tkinter makes you jump through to do the simplest things, you have a butt-simple interface that can be reused in any project. You can do this for Entry, as well, but the syntax is slightly different. 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()
Lets say that you have a Text widget called my_text_widget. To get input from the my_text_widget you can use the get function. Let's assume that you have imported tkinter. Lets define my_text_widget first, lets make it just a simple text widget. my_text_widget = Text(self) To get input from a text widget you need to use the get function, both, text and entry widgets have this. input = my_text_widget.get() The reason we save it to a variable is to use it in the further process, for example, testing for what's the input.
Updating a python Tkinter frame
I am having difficulty updating a python Tkinter frame. I draw the frame with some labels and text fields, when a person presses a button, I want to do some calculations and update the labels and text fields. I can print the data to my stdout, but I cannot get the Tk screen to update. How can I get the countFld to display an updated value? class Application(Frame): def __init__(self): self.root = Tk() Frame.__init__(self, self.root) self.count = 0 self.createWidgets() def createWidgets(self): self.countFrame = Frame(self, bd=2, relief=RIDGE) Label(self.countFrame, text='Count:').pack(side=LEFT, padx=5) self.countFld = IntVar() Label(self.countFrame, text=str(self.count)).pack(side=RIGHT, padx=5) self.countFld.set(self.count) self.countFrame.pack(expand=1, fill=X, pady=10, padx=5) self.CNTBTN = Button(self) self.CNTBTN["text"] = "UPDATE" self.CNTBTN["fg"] = "red" self.CNTBTN["command"] = self.update_count self.CNTBTN.pack({"side": "left"}) def update_count(self): self.count = self.count + 1 print "Count = %" % self.count #prints correct value self.countFld.set(self.count) #Does not update display
Your problem is that you do not attach the Variable to the widget. In addition you need to use a StringVar, as the Label Widget operates on Strings and not Ints. Try something like: self.countStr = StringVar() self.countStr.set(str(self.count)) Label(self.countFrame, textvariable=self.countStr).pack(side=RIGHT, padx=5) Tk updates the display when the eventloop is idle. So you need to re-enter the event loop after you set the new value.
You should try destroying the label itself and making it again in the code with the updated text and use self.root.update()