Here's a complete working example. If the entry widget contains the word "invalid", the state will be changed to "invalid". You can then click out of the widget to see that the state remains invalid:
try:
import Tkinter as tk
import ttk
except ImportError:
import tkinter as tk
from tkinter import ttk
class Example(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
# give invalid entries a red background
style = ttk.Style()
style.map("TEntry", background=[('invalid', "red")])
self.entryVar = tk.StringVar()
self.entry = ttk.Entry(self, textvariable=self.entryVar)
# this label will show the current state, updated
# every second.
self.label = tk.Label(self, anchor="w")
self.after_idle(self.updateLabel)
# layout the widgets
self.entry.pack(side="top", fill="x")
self.label.pack(side="bottom", fill="x")
# add trace on the variable to do custom validation
self.entryVar.trace("w", self.validate)
# set up bindings to also do the validation when we gain
# or lose focus
self.entry.bind("<FocusIn>", self.validate)
self.entry.bind("<FocusOut>", self.validate)
def updateLabel(self):
'''Display the current entry widget state'''
state = str(self.entry.state())
self.label.configure(text=state)
self.after(1000, self.updateLabel)
def validate(self, *args):
'''Validate the widget contents'''
value = self.entryVar.get()
if "invalid" in value:
self.entry.state(["invalid"])
else:
self.entry.state(["!invalid"])
if __name__ == "__main__":
root = tk.Tk()
Example(root).pack(fill="both", expand=True)
root.mainloop()
If you can't beat them, join them. Use validate='focus'
with a validatecommand
that returns whether or not the text is valid, as if you had wanted to check this on focus events all along. And continue to set the invalid
flag when something really changes. For example:
class MyEntry(ttk.Entry):
def __init__(self, master):
self.var = tk.StringVar(master)
super().__init__(master,
textvariable = self.var,
validate = 'focus',
validatecommand = self.is_valid)
self.var.trace('w', self.revalidate)
self.revalidate()
def revalidate(self, * args):
self.state(['!invalid'
if self.is_valid()
else 'invalid'
])
def is_valid(self, * args):
#...
return
if current text is valid...
You could instead simply persist the state flag across focus events:
class ValidationDisabledEntry(ttk.Entry):
def __init__(self, * args, ** kwargs):
super().__init__( *
args,
validate = 'focus',
validatecommand = lambda * a: 'invalid'
not in self.state(), **
kwargs)
Here is a solution to your problem. You may probably want to adapt it to your need but it may give you an idea :
import tkinter as tk
from tkinter import ttk
class ValidatingEntry(ttk.Entry):
COLOR_VALID = "#99ff99"
COLOR_INVALID="#ff9999"
def __init__(self, master, *args, **kwargs):
self.stringVar = tk.StringVar()
tk.Entry.__init__(self, master, *args, textvariable = self.stringVar, **kwargs)
self.validatingFunction = None
self.bind("<FocusOut>",self.validation)
def validation(self, event):
if self.validatingFunction != None:
if self.validatingFunction():
self['bg']=ValidatingEntry.COLOR_VALID
else:
self['bg']=ValidatingEntry.COLOR_INVALID
else:
print("no evaluation possible for the content of this entry")
if __name__ == "__main__":
app = tk.Tk()
entry = ValidatingEntry(app)
entry.validatingFunction = lambda : 'foo' in entry.stringVar.get()
entry.pack()
entry2 = ValidatingEntry(app)
entry2.validatingFunction = lambda : 'bar' in entry2.stringVar.get()
entry2.pack()
app.mainloop()
A checkbutton doesn't automatically set (or create) the linked variable. Therefore, your program needs to initialize it to the appropriate starting value.,Your program can specify what makes an entry valid or invalid, as well as when to check its validity. As we'll see soon, the two are related. We'll start with a simple example, an entry that can only hold an integer up to five digits long. ,If you're unsure what configuration options a widget supports, you can ask the widget to describe them. This gives you a long list of all its options. ,If you simply need a multi-line text field for a form, there are only a few things to worry about: create and size the widget (check), provide an initial value, and retrieve the text after a user has submitted the form.
If you're using Python 3.9 or newer, the build system will look in /Library/Frameworks
, where ActiveState and other custom builds are typically installed.
% . / configure % make
When compiling Python versions prior to 3.9, you will need to add two new command-line options to the initial ./configure
in the Python build process. The first provides the locations of the Tcl and Tk include files, and the second provides the locations of the Tcl and Tk libraries. These are usually found in two different locations (i.e., Tcl.framework
and Tk.framework
). You need to provide both locations for the include files and both for the libraries. Note the location of the quotes in the command below and the spaces separating the Tcl and Tk paths.
% . / configure--with - tcltk - includes = "-I/Library/Frameworks/Tcl.framework/Headers -I/Library/Frameworks/Tk.framework/Headers"--with - tcltk - libs = "/Library/Frameworks/Tcl.framework/Tcl /Library/Frameworks/Tk.framework/Tk" %
make
When everything is built, be sure to test it out. Start Python from your terminal, e.g.
% . / python.exe
You can also get the exact version of Tcl/Tk that is being used with:
>>> tkinter.Tcl().eval('info patchlevel')
To verify the exact version of Tcl/Tk that you are running, from the Wish console type the following:
% info patchlevel
Next, you'll want to install Ruby. There are multiple ways to do this, as explained at
www.ruby-lang.org. One option is to use a package manager like
Homebrew. Once it's been installed (at /usr/local/bin/brew
)
you can install Ruby from a command prompt (e.g. Terminal) via:
% brew install ruby
Next, you'll need to download and install Ruby's Tk module, which is packaged as a Ruby gem. To do so, from the command prompt, run:
% /usr/local / opt / ruby / bin / gem install tk
To verify that everything worked, start up /usr/local/opt/ruby/bin/irb
and type:
% require 'tk' %
Tk::TK_PATCHLEVEL
Next, you'll want to install Perl. There are multiple ways to do this, as explained at
www.perl.org. One option is to use a package manager like
Homebrew. Once it's been installed (at /usr/local/bin/brew
)
you can install Ruby from a command prompt (e.g. Terminal) via:
% brew install perl
Next, you'll need to download and install Perl's Tkx module. We can grab it from CPAN. Unfortunately, at present it will not install correctly due to errors in its tests. We can bypass the tests and install it anyway. To do so, from the command prompt, run:
% /usr/local / opt / perl / bin / perl - MCPAN - e "CPAN::Shell->notest('install','Tkx')"
To check that this worked, run this from the Unix command line:
% /usr/local / opt / perl / bin / perl - MTkx - e 'print Tkx::info("patchlevel");'
Once you've installed or compiled Python, test it out to make sure Tkinter works. From the Python prompt, enter these two commands:
>>>
import tkinter
>>>
tkinter._test()
You can also get the exact version of Tcl/Tk that is being used with:
>>> tkinter.Tcl().eval('info patchlevel')
Run the installer, and follow along. You'll end up with a fresh install of ActiveTcl, usually located in C:\ActiveTcl. From a command prompt, you should then be able to run a Tcl/Tk 8.6 shell via:
% C: \ActiveTcl\ bin\ wish
This should pop up a small window titled "wish", which will contain your application. A second, larger window titled "Console" is where you can type in Tcl/Tk commands. To verify the exact version of Tcl/Tk that you are running, type the following:
% info patchlevel
The insertion cursor shows where new text will be inserted. It is displayed only when the user clicks the mouse somewhere in the widget. It usually appears as a blinking vertical line inside the widget. You can customize its appearance in several ways. , Set the insertion cursor just before the character at the given index. , Sets the selection under program control. Selects the text starting at the start index, up to but not including the character at the end index. The start position must be before the end position. , You may need to figure out which character position in the widget corresponds to a given mouse position. To simplify that process, you can use as an index a string of the form '@n', where n is the horizontal distance in pixels between the left edge of the Entry widget and the mouse. Such an index will specify the character at that horizontal mouse position.
To create a new Entry
widget in a root
window or frame named
:
parent
w = tk.Entry(parent, option, ...)