2

I'm working on an application to edit DNA sequences and I'd like to have a tkinter text widget in which only letters atgcATGC can be entered.

Is there any easy way to do that?

Thanks, David

dbikard
  • 491
  • 1
  • 4
  • 13
  • Thanks for the answers, but I really need a Text widget and not an Entry widget. Text widgets don't have validation. – dbikard Jun 30 '11 at 11:36

4 Answers4

3

You can use the validatecommand feature of the Entry widget. The best documentation I can find is this answer to a similar question. Following that example,

import Tkinter as tk

class MyApp():
    def __init__(self):
        self.root = tk.Tk()
        vcmd = (self.root.register(self.validate), '%S')
        self.entry = tk.Entry(self.root, validate="key", 
                              validatecommand=vcmd)
        self.entry.pack()
        self.root.mainloop()

    def validate(self, S):

        return all(c in 'atgcATGC' for c in S)

app=MyApp()
Community
  • 1
  • 1
Stephen Terry
  • 6,169
  • 1
  • 28
  • 31
  • Thanks for the answer, but I really need a Text widget and not an Entry widget. As far as I know Text widgets don't have validation. – dbikard Jun 30 '11 at 11:38
2

I finally found a way to have the exact behavior I want:

from Tkinter import Text, BOTH
import re

class T(Text):

    def __init__(self, *a, **b):

        # Create self as a Text.
        Text.__init__(self, *a, **b)

        #self.bind("<Button-1>", self.click)
        self.bind("<Key>", self.key)
        self.bind("<Control-v>", self.paste)

    def key(self,k):
        if k.char and k.char not in "atgcATGC":
            return "break"

    def paste(self,event):
        clip=self.selection_get(selection='CLIPBOARD')
        clip=clip.replace("\n","").replace("\r","")
        m=re.match("[atgcATGC]*",clip)
        if m and m.group()==clip:
            self.clipboard_clear()
            self.clipboard_append(clip)
        else:
            self.clipboard_clear()
            return


t = T()
t.pack(expand=1, fill=BOTH)
t.mainloop()
dbikard
  • 491
  • 1
  • 4
  • 13
  • I really apreciate your code, it has really helped me out, but this way i m not able to use backspace either to delete characters. Do you know how to add this option – Breno Baiardi Jan 18 '18 at 22:11
  • actually I found out that I just needed to add "\b" to the filter string Now it is working perfectly – Breno Baiardi Jan 18 '18 at 22:14
1

You will have to catch the "<Key>" event on the widget where you're entering text. Then you can just filter

if key.char and key.char not in "atgcATGC":
    return "break"

Here's some info on handling events in tkinter: http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm

Thomas K
  • 39,200
  • 7
  • 84
  • 86
  • You don't _have_ to. That's one of at least a couple of solutions. Also, capturing `` won't handle pasting via the mouse (and as implemented won't handle pasting a string via shortcut). It's better to use the built-in input validation features of the widget -- this is exactly what they are for. – Bryan Oakley Jun 28 '11 at 19:28
  • @Bryan: Oh yes, that is a better option. I didn't know about the validation features. – Thomas K Jun 28 '11 at 19:59
0

I would recommend the Pmw toolkit which provides a lot of handy extras to Tkinter. The Pmw EntryField class allows you to write an arbitrary validator for any text field. Pmw is lightweight and very usable, if you are developing anything in Tkinter you will probably find its features useful.

RoundTower
  • 899
  • 5
  • 9