Related
I have created a class within tkinter that builds a frame and buttons (example below).
I am also trying to create a series of radiobuttons each with a command, and rather than having to type three times the same, I am trying to use a dictionary that stores the function name and the function (again see below):
import tkinter as tk
from tkinter import ttk
from tkinter import *
class EnoxDoseCalculator:
def __init__(self, root):
root.title('Enoxaparin Dose Calculator')
indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
mainframe = ttk.Frame(root)
mainframe.grid(column = 0, row = 0, sticky='nsew')
test_button = ttk.Button(mainframe, text= 'Push Mew!!', command = self.format)
test_button.grid(row = 0, column = 0)
def vte(self):
pass
def af(self):
pass
def mech(self):
pass
def format(self):
self.var3 = tk.StringVar()
for key, value in self.indication.items():
ttk.Radiobutton(mainframe, text = self.indication.key, variable = self.var3, value = self.indication.key, command = self.indication.value).grid(row = n+1, column =0, sticky = 'nswe')
root = Tk()
EnoxDoseCalculator(root)
print(dir(EnoxDoseCalculator))
root.mainloop()
However, I keep getting this message:
I get this is to do with the dictionary functions being listed before they are created, so my question is, where do I place said dictionary, because I am sure this is possible. or is it that I am using the wrong names for the function?
Try this:
class EnoxDoseCalculator:
def __init__(self, root):
self.root = root # add this (mostly for consistency/best practice)
self.root.title('Enoxaparin Dose Calculator')
self.indication = {'VTE': self.vte, 'Atrial Fibrilation': self.af, 'Mechanical Heart valve': self.mech}
self.mainframe = ttk.Frame(self.root) # update this to include 'self'
self.mainframe.grid(column=0, row=0, sticky='nsew')
# update this to include 'self'
self.test_button = ttk.Button(self.mainframe, text='Push Mew!!', command=self.format)
self.test_button.grid(row=0, column=0)
Using self to namespace your class variables to EnoxDoseCalculator
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.
I have frames generated automatically. these frames contain objects such as labels and only 1 entry.
I manage to identify the Entry with the following command:
for widget in FrameCalc.winfo_children():
print("widget.winfo_children()[4]", widget.winfo_children()[4])
which gives me this
.! toplevel.labels.! frame2.! entry
How can I get the value contained in the target Entry?
thank you in advance for your time
Welcome to Stack Overflow community!
In your case, you can use any of SrtingVar()(holing string), IntVar()(holing integer), DoubleVar()(holding float) or BooleanVar()(holding boolean values) depending on your requirement and assign a textvariable to the entry widget. You can then append these variables into a list and use .get() method to retrieve it's contents when required. Here's an example, using a loop to create many entries with StringVar() and getting their values later.
from tkinter import *
root = Tk()
def display(ent):
global disp, var_list
disp.set(var_list[ent].get())
var_list = []
for i in range (0, 5):
var = StringVar()
entry = Entry(root, textvariable = var)
var_list.append(var)
entry.pack()
button = Button(root, text = "Show", command = lambda ent = i: display(ent))
button.pack()
disp = StringVar()
label = Label(root, textvariable = disp)
label.pack()
root.mainloop()
I believe this is the answer you are looking for.
use isinstance() to check for the widget type
use get() to return the value
import tkinter as tk
#a dummy widget for example purposes
class DummyWidget(tk.Frame):
def __init__(self, master, t, e, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
tk.Label(self, text=t).grid(row=0, column=0)
ent = tk.Entry(self)
ent.grid(row=0, column=1)
ent.insert(0, e)
#extend root
class App(tk.Tk):
#application constants
TITLE = 'Application'
WIDTH, HEIGHT, X, Y = 800, 600, 50, 50
def __init__(self):
tk.Tk.__init__(self)
DummyWidget(self, "label 1", "entry 1").grid(row=0, column=0)
DummyWidget(self, "label 2", "entry 2").grid(row=1, column=0)
DummyWidget(self, "label 3", "entry 3").grid(row=2, column=0)
#this is the answer portion of the example
for widget in self.winfo_children():
for i, subwidget in enumerate(widget.winfo_children()):
if isinstance(subwidget, tk.Entry):
print(f'child {i} of widget', subwidget.get())
#properly initialize your app
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}+{App.X}+{App.Y}')
#app.resizable(width=False, height=False)
app.mainloop()
This concept could also be turned into a utility, so you have a dynamic system of finding whatever you want, starting from wherever you want. I would definitely consider this preferable to rewriting the above multi-dimensional loop (that stops at grandchildren) every time you need to find specific instance types.
import tkinter as tk
from dataclasses import dataclass
from typing import Type
#a dummy widget for example purposes
class DummyWidget(tk.Frame):
def __init__(self, master, t, e, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
tk.Label(self, text=t).grid(row=0, column=0)
ent = tk.Entry(self)
ent.grid(row=0, column=1)
ent.insert(0, e)
#to illustrate inheritance
class DummyEntry(tk.Entry):
def __init__(self, master, text, **kwargs):
tk.Entry.__init__(self, master, **kwargs)
self.insert(0, text)
#used in Utils.GetInstancesAsDataFrom(...) to store individual widget data
#dataclass
class WidgetData_dc:
type: Type
parent: tk.Widget
childindex: int
path: str
class Utils:
""" GetInstancesFrom
deep search of every child, grandchild, etc.. for a specific widget type
#start ~ parent widget to start the search from
#wtype ~ the type of widget to find
#inst ~ used internally to pass the dictionary to this method's internal calls of itself
returns a dictionary of all found instances
"""
#staticmethod
def GetInstancesFrom(start, wtype, inst=None):
instances = dict() if inst is None else inst
for widget in start.winfo_children():
if isinstance(widget, wtype):
instances[f'{widget}'] = widget
Utils.GetInstancesFrom(widget, wtype, instances)
return instances
""" GetInstancesAsDataFrom
deep search of every child, grandchild, etc.. for a specific widget type
#start ~ parent widget to start the search from
#wtype ~ the type of widget to find
#inst ~ used internally to pass the dictionary to this method's internal calls of itself
returns a dictionary of all found instances
"""
#staticmethod
def GetInstancesAsDataFrom(start, wtype, inst=None):
instances = dict() if inst is None else inst
for i, widget in enumerate(start.winfo_children()):
if isinstance(widget, wtype):
instances[widget] = WidgetData_dc(type(widget), start, i, f'{widget}')
Utils.GetInstancesAsDataFrom(widget, wtype, instances)
return instances
#extend root
class App(tk.Tk):
#application constants
TITLE = 'Application'
WIDTH, HEIGHT, X, Y = 800, 600, 50, 50
def __init__(self):
tk.Tk.__init__(self)
#a bunch of junk instances for example purposes
DummyWidget(self, "label 1", "entry 1").grid(column=0)
DummyWidget(self, "label 2", "entry 2").grid(column=0)
DummyWidget(self, "label 3", "entry 3").grid(column=0)
DummyEntry(self, text='entry 4').grid(column=0) #this extends tk.Entry so it qualifies as a tk.Entry
#answer portion of the example
for path, widget in Utils.GetInstancesFrom(self, tk.Entry).items():
print(f'{path}: {widget.get()}')
print('') #skip a line
#alternate implementation
for widget, data in Utils.GetInstancesAsDataFrom(self, tk.Entry).items():
print(f'{data.parent}[{data.childindex}]:{data.type} has value "{widget.get()}"')
#properly initialize your app
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}+{App.X}+{App.Y}')
#app.resizable(width=False, height=False)
app.mainloop()
I'm going to need a list control with multiple selection and a way for the user to edit the displayed list.
Judging by Python - python-list - ttk Listbox, ttk.Treeview is the new black way to display a list and a replacement for Tkinter.Listbox.
Is there some stock/recommended way provided to incorporate list editing, too?
This is typically done with three buttons "add"/"edit"/"delete" somewhere around the control (the first two may cause a small window with an edit control to pop up), or a list entry itself becomes an editor e.g. on a double click. Implementing either logic by hand would be nontrivial.
Tk and thus Tkinter only provide base widgets; reusable combinations of them are beyond their scope. The abandoned Tix package offered a mechanism for and a collection of these, called "mega-widgets", but the idea didn't catch on apparently.
I did it my way the following way (using the Model-View-Presenter design pattern).
It looks like this:
import tkinter
from tkinter import ttk
from tkinter import W, E
class View_EditableList(object):
def __init__(self,root,root_row):
""" List with buttons to edit its contents.
:param root: parent widget
:param roow_row: row in `root'. The section takes 2 rows.
"""
self.list = tkinter.Listbox(root, selectmode=tkinter.EXTENDED)
self.list.grid(row=root_row, sticky=(W, E))
root.rowconfigure(root_row, weight=1)
self.frame = ttk.Frame(root)
self.frame.grid(row=root_row+1, sticky=(W, E))
self.add = ttk.Button(self.frame, text="+", width=5)
self.add.grid(row=0, column=1)
self.edit = ttk.Button(self.frame, text="*", width=5)
self.edit.grid(row=0, column=2)
self.del_ = ttk.Button(self.frame, text="-", width=5)
self.del_.grid(row=0, column=3)
self.up = ttk.Button(self.frame, text=u"↑", width=5)
self.up.grid(row=0, column=4)
self.down = ttk.Button(self.frame, text=u"↓", width=5)
self.down.grid(row=0, column=5)
self.frame.grid_columnconfigure(0, weight=1)
self.frame.grid_columnconfigure(6, weight=1)
class Presenter_EditableList(object):
def __init__(self,view,root):
"""
:param view: View_EditableList
:param root: root widget to be used as parent for modal windows
"""
self.root = root
self.view = view
view.add.configure(command=self.add)
view.edit.configure(command=self.edit)
view.del_.configure(command=self.del_)
view.up.configure(command=self.up)
view.down.configure(command=self.down)
def add(self):
w=View_AskText(self.root)
self.root.wait_window(w.top)
if w.value:
self.view.list.insert(self.view.list.size(),w.value)
def edit(self):
l=self.view.list
try:
[index]=l.curselection()
except ValueError:
return
w=View_AskText(self.root,l.get(index))
self.root.wait_window(w.top)
if w.value:
l.delete(index)
l.insert(index,w.value)
def del_(self):
l=self.view.list
try:
[index]=l.curselection()
except ValueError:
return
l.delete(index)
l.select_set(max(index,l.size()-1))
def up(self):
l = self.view.list
try:
[index] = l.curselection()
except ValueError:
return
if index>0:
v = l.get(index)
l.delete(index)
l.insert(index-1,v)
l.select_set(index-1)
def down(self):
l = self.view.list
try:
[index] = l.curselection()
except ValueError:
return
if index<l.size()-1:
v = l.get(index)
l.delete(index)
l.insert(index+1,v)
l.select_set(index+1)
def getlist(self):
return [self.view.list.get(i) for i in range(self.view.list.size())]
def setlist(self,list_):
self.view.list.delete(0,tkinter.END)
for i,v in enumerate(list_):
self.view.list.insert(i,v)
# supplemental class; it's in another file in my actual code
class View_AskText(object):
"""
A simple dialog that asks for a text value.
"""
def __init__(self, master, value=u""):
self.value = None
top = self.top = tkinter.Toplevel(master)
top.grab_set()
self.l = ttk.Label(top, text=u"Value:")
self.l.pack()
self.e = ttk.Entry(top)
self.e.pack()
self.b = ttk.Button(top, text='Ok', command=self.save)
self.b.pack()
if value: self.e.insert(0, value)
self.e.focus_set()
top.bind('<Return>', self.save)
def save(self, *_):
self.value = self.e.get()
self.top.destroy()
root = tkinter.Tk()
view = View_EditableList(root, 5)
Presenter_EditableList(view, root)
root.mainloop()
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.