P
Phil Schmidt
I'm trying to make a custom entry widget, as in the code that follows.
There are two problems I'm trying to fix:
1) I would like the widget to behave as myEntry.Escape() does now,
except that it happens on loss of focus, not when pressing Esc.
2) TABbing between multiple entry fields does undesired things with
the selection, and with cursor placement.
Can anyone offer any suggestions for how to fix this? I'm attemting to
replicate, more or less, the behavior one would get with, for example,
MS Excel, in navigating and entering data in an array of entry fields.
Also, I'd appreciate any critique of my overall technique,
particularly in how I've implemented the "undo" behavior - it works,
but doesn't seem particularly clean.
Thanks!
------------------------------------------------------
from Tkinter import *
class history:
"""this is a mixin for my custom widgets,
providing for "infinite" undo."""
LOG = []
COUNT = {}
def log(self):
history.LOG.append((self, self.value))
history.COUNT.setdefault(id(self), 0)
history.COUNT[id(self)] += 1
def undo(self):
try:
s, v = history.LOG.pop()
if history.COUNT[id(s)]:
s.value = v
history.COUNT[id(s)] -= 1
if not history.COUNT[id(s)]:
del history.COUNT[id(s)]
s.Show()
except IndexError:
pass
class myEntry(Entry, history):
"""
This works pretty well, with one exception: I would like the
widget to behave as Escape() does now, except that it happens
on loss of focus, not when pressing Esc.
Also, TABbing between entry fields causes the text in the next
widget to be selected, with the cursor at the end of the text.
When keys are pressed, the text is appended to the selected
text, rather than replacing the selection as would be expected.
"""
def __init__(self, master=None, state=NORMAL, **config):
Entry.__init__(self, master, **config)
if master == None:
self.pack()
self.insert(INSERT, str(self))
self.bind('<Return>', self.Return)
self.bind('<Escape>', self.Escape)
self.bind('<Key>', self.Key)
self.bind('<Control-z>', self.ctrl_z)
self.mode = 'display'
if state == DISABLED:
self.config(state=DISABLED)
def Show(self):
self.delete(0, END)
self.insert(INSERT, str(self))
self.mode = 'display'
try:
history.COUNT[id(self)] # fails if no history
(changes)
self.config(bg='pink', fg='black')
except:
self.config(bg='white', fg='black')
def Key(self, event):
try:
x = ord(event.char) # fails if not an ASCII key
if event.keysym == 'Tab':
self.Return(None)
elif self.mode == 'display':
self.config(bg='red', fg='white')
self.mode = 'entry'
E, I = self.index(END), self.index(INSERT)
self.delete(0, END)
self.insert(INSERT, self.STR())
i = self.index(END)
self.icursor(i - (E - I))
except:
pass
def Return(self, event):
if self.focus_get():
self.Validate(self.get())
self.Show()
def Escape(self, event):
self.Show()
def ctrl_z(self, event):
self.undo()
def Validate(self, value):
pass
class gui_int(myEntry):
def __init__(self, value=0, master=None, **config):
self.value = 0
myEntry.__init__(self, master, **config)
def Validate(self, value):
try:
v = int(value)
if v != self.value:
self.log()
self.value = v
except:
pass
def __str__(self):
return 'INT: ' + str(self.value)
def STR(self):
return str(self.value)
x=gui_int(value=22.34, font='Courier 12', state=DISABLED, width=11,
justify=RIGHT)
y=gui_int(font='Courier 12', width=11, justify=RIGHT)
z=gui_int(value=10.99, font='Courier 12', width=11, justify=RIGHT)
There are two problems I'm trying to fix:
1) I would like the widget to behave as myEntry.Escape() does now,
except that it happens on loss of focus, not when pressing Esc.
2) TABbing between multiple entry fields does undesired things with
the selection, and with cursor placement.
Can anyone offer any suggestions for how to fix this? I'm attemting to
replicate, more or less, the behavior one would get with, for example,
MS Excel, in navigating and entering data in an array of entry fields.
Also, I'd appreciate any critique of my overall technique,
particularly in how I've implemented the "undo" behavior - it works,
but doesn't seem particularly clean.
Thanks!
------------------------------------------------------
from Tkinter import *
class history:
"""this is a mixin for my custom widgets,
providing for "infinite" undo."""
LOG = []
COUNT = {}
def log(self):
history.LOG.append((self, self.value))
history.COUNT.setdefault(id(self), 0)
history.COUNT[id(self)] += 1
def undo(self):
try:
s, v = history.LOG.pop()
if history.COUNT[id(s)]:
s.value = v
history.COUNT[id(s)] -= 1
if not history.COUNT[id(s)]:
del history.COUNT[id(s)]
s.Show()
except IndexError:
pass
class myEntry(Entry, history):
"""
This works pretty well, with one exception: I would like the
widget to behave as Escape() does now, except that it happens
on loss of focus, not when pressing Esc.
Also, TABbing between entry fields causes the text in the next
widget to be selected, with the cursor at the end of the text.
When keys are pressed, the text is appended to the selected
text, rather than replacing the selection as would be expected.
"""
def __init__(self, master=None, state=NORMAL, **config):
Entry.__init__(self, master, **config)
if master == None:
self.pack()
self.insert(INSERT, str(self))
self.bind('<Return>', self.Return)
self.bind('<Escape>', self.Escape)
self.bind('<Key>', self.Key)
self.bind('<Control-z>', self.ctrl_z)
self.mode = 'display'
if state == DISABLED:
self.config(state=DISABLED)
def Show(self):
self.delete(0, END)
self.insert(INSERT, str(self))
self.mode = 'display'
try:
history.COUNT[id(self)] # fails if no history
(changes)
self.config(bg='pink', fg='black')
except:
self.config(bg='white', fg='black')
def Key(self, event):
try:
x = ord(event.char) # fails if not an ASCII key
if event.keysym == 'Tab':
self.Return(None)
elif self.mode == 'display':
self.config(bg='red', fg='white')
self.mode = 'entry'
E, I = self.index(END), self.index(INSERT)
self.delete(0, END)
self.insert(INSERT, self.STR())
i = self.index(END)
self.icursor(i - (E - I))
except:
pass
def Return(self, event):
if self.focus_get():
self.Validate(self.get())
self.Show()
def Escape(self, event):
self.Show()
def ctrl_z(self, event):
self.undo()
def Validate(self, value):
pass
class gui_int(myEntry):
def __init__(self, value=0, master=None, **config):
self.value = 0
myEntry.__init__(self, master, **config)
def Validate(self, value):
try:
v = int(value)
if v != self.value:
self.log()
self.value = v
except:
pass
def __str__(self):
return 'INT: ' + str(self.value)
def STR(self):
return str(self.value)
x=gui_int(value=22.34, font='Courier 12', state=DISABLED, width=11,
justify=RIGHT)
y=gui_int(font='Courier 12', width=11, justify=RIGHT)
z=gui_int(value=10.99, font='Courier 12', width=11, justify=RIGHT)