Skip to content

Commit b8bad92

Browse files
committed
Major Component: change ValidatedTextCtrl, as sometimes saving invalid values & not always changing color
1 parent 1881114 commit b8bad92

File tree

1 file changed

+100
-20
lines changed

1 file changed

+100
-20
lines changed

GSASII/GSASIIctrlGUI.py

Lines changed: 100 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,9 @@ def __init__(self,parent,loc,key,nDig=None,notBlank=True,xmin=None,xmax=None,
445445
else:
446446
self.invalid = False
447447
self.Bind(wx.EVT_CHAR,self._GetStringValue)
448-
448+
# Mac is "special" because backspace etc. does not trigger validator
449+
if sys.platform == "darwin":
450+
self.Bind(wx.EVT_KEY_DOWN,self.OnKeyDown)
449451
# When the mouse is moved away or the widget loses focus,
450452
# display the last saved value, if an expression
451453
self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow)
@@ -549,7 +551,7 @@ def OnKeyDown(self,event):
549551
'Special callback for wx 2.9+ on Mac where backspace is not processed by validator'
550552
key = event.GetKeyCode()
551553
if key in [wx.WXK_BACK, wx.WXK_DELETE]:
552-
if self.Validator: wx.CallAfter(self.Validator.TestValid,self)
554+
wx.CallAfter(self._TestValidity)
553555
if key == wx.WXK_RETURN or key == wx.WXK_NUMPAD_ENTER:
554556
self._onLoseFocus(None)
555557
if event: event.Skip()
@@ -562,24 +564,30 @@ def _onStringKey(self,event):
562564
wx.CallAfter(self.ShowStringValidity,True) # was invalid
563565
else:
564566
wx.CallAfter(self.ShowStringValidity,False) # was valid
565-
567+
568+
def _TestValidity(self):
569+
'Check validity and change colors accordingly'
570+
if self.Validator:
571+
self.Validator.TestValid(self)
572+
self._IndicateValidity()
573+
566574
def _IndicateValidity(self):
567575
'Set the control colors to show invalid input'
568576
if self.invalid:
569577
ins = self.GetInsertionPoint()
570578
self.SetForegroundColour("red")
571579
self.SetBackgroundColour("yellow")
572-
if not sys.platform.startswith("linux"):
573-
self.SetFocus()
580+
#if not sys.platform.startswith("linux"):
581+
# self.SetFocus() # was needed -- now seems to cause problems
574582
self.Refresh() # this selects text on some Linuxes
575583
self.SetSelection(0,0) # unselect
576584
self.SetInsertionPoint(ins) # put insertion point back
577585
else: # valid input
578586
self.SetBackgroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOW))
579587
self.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_BTNTEXT))
580588
self.Refresh()
581-
if not sys.platform.startswith("linux"):
582-
self.SetFocus() # seems needed, at least on MacOS to get color change
589+
#if not sys.platform.startswith("linux"):
590+
# self.SetFocus() # seems needed, at least on MacOS to get color change
583591

584592
def _GetNumValue(self):
585593
'Get and where needed convert string from GetValue into int or float'
@@ -631,10 +639,7 @@ def _SaveStringValue(self):
631639
except RuntimeError: # ignore if control has been deleted
632640
return
633641
# always store the result
634-
if self.CIFinput and '2' in platform.python_version_tuple()[0]: # Py2/CIF make results ASCII
635-
self.result[self.key] = val.encode('ascii','replace')
636-
else:
637-
self.result[self.key] = val
642+
self.result[self.key] = val
638643

639644
def _onLeaveWindow(self,event):
640645
'''If the mouse leaves the text box, save the result, if valid,
@@ -647,10 +652,12 @@ def delayedUpdate():
647652
self._setValue(self.result[self.key])
648653
except:
649654
pass
655+
# ignore mouse crusing
656+
if self.result[self.key] == self.GetValue(): # .IsModified() seems unreliable
657+
return
650658
if self.type is not str:
651659
if not self.IsModified(): return #ignore mouse crusing
652-
elif self.result[self.key] == self.GetValue(): # .IsModified() seems unreliable for str
653-
return
660+
wx.CallAfter(self._TestValidity) # entry changed, test/show validity
654661
if self.evaluated and not self.invalid: # deal with computed expressions
655662
if self.timer:
656663
self.timer.Restart(self.delay)
@@ -671,17 +678,19 @@ def _onLoseFocus(self,event):
671678
Evaluate and update the current control contents
672679
'''
673680
if event: event.Skip()
681+
# ignore mouse crusing
682+
if self.result[self.key] == self.GetValue(): # .IsModified() seems unreliable
683+
return
674684
if self.type is not str:
675685
if not self.IsModified(): return #ignore mouse crusing
676-
elif self.result[self.key] == self.GetValue(): # .IsModified() seems unreliable for str
677-
return
686+
wx.CallAfter(self._TestValidity) # entry changed, test/show validity
678687
if self.evaluated: # deal with computed expressions
679688
if self.invalid: # don't substitute for an invalid expression
680689
return
681690
self._setValue(self.result[self.key])
682691
elif self.result is not None: # show formatted result, as Bob wants
683-
self.result[self.key] = self._GetNumValue()
684692
if not self.invalid: # don't update an invalid expression
693+
self.result[self.key] = self._GetNumValue()
685694
self._setValue(self.result[self.key])
686695

687696
if self.OnLeave:
@@ -930,10 +939,7 @@ def TestValid(self,tc):
930939
:param wx.TextCtrl tc: A reference to the TextCtrl that the validator
931940
is associated with.
932941
'''
933-
if '2' in platform.python_version_tuple()[0]:
934-
self.result[self.key] = tc.GetValue().encode('ascii','replace')
935-
else:
936-
self.result[self.key] = tc.GetValue()
942+
self.result[self.key] = tc.GetValue()
937943

938944
def OnChar(self, event):
939945
'''Called each type a key is pressed
@@ -10129,6 +10135,80 @@ def SelectPkgInstall(event):
1012910135
print ('exiting GSAS-II')
1013010136
sys.exit()
1013110137

10138+
def StringSearchTemplate(parent,title,prompt,start,help=None):
10139+
'''Dialog to obtain a single string value from user
10140+
10141+
:param wx.Frame parent: name of parent frame
10142+
:param str title: title string for dialog
10143+
:param str prompt: string to tell use what they are inputting
10144+
:param str start: default input value, if any
10145+
:param str help: if supplied, a help button is added to the dialog that
10146+
can be used to display the supplied help text/URL for setting this
10147+
variable. (Default is '', which is ignored.)
10148+
'''
10149+
def on_invalid():
10150+
G2MessageBox(parent,
10151+
'The pattern must retain some non-blank characters from the original string. Resetting so you can start again.','Try again')
10152+
valItem.SetValue(start)
10153+
return
10154+
def on_char_typed(event):
10155+
keycode = event.GetKeyCode()
10156+
if keycode == 32 or keycode == 63: # ' ' or '?' - replace with '?'
10157+
#has a range been selected?
10158+
sel = valItem.GetSelection()
10159+
if sel[0] == sel[1]:
10160+
insertion_point = valItem.GetInsertionPoint()
10161+
sel = (insertion_point,insertion_point+1)
10162+
for i in range(*sel):
10163+
valItem.Replace(i, i + 1, '?')
10164+
# Move the insertion point forward one character
10165+
valItem.SetInsertionPoint(i + 1)
10166+
# make sure some characters remain
10167+
if len(valItem.GetValue().replace('?','').strip()) == 0:
10168+
wx.CallAfter(on_invalid)
10169+
event.Skip(False)
10170+
elif keycode >= wx.WXK_SPACE: # anything else printable, ignore
10171+
event.Skip(False)
10172+
else: # arrows etc are processed naturally
10173+
event.Skip(True)
10174+
dlg = wx.Dialog(parent,wx.ID_ANY,title,pos=wx.DefaultPosition,
10175+
style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
10176+
dlg.CenterOnParent()
10177+
mainSizer = wx.BoxSizer(wx.VERTICAL)
10178+
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
10179+
txt = wx.StaticText(dlg,wx.ID_ANY,prompt)
10180+
sizer1.Add((10,-1),1,wx.EXPAND)
10181+
txt.Wrap(500)
10182+
sizer1.Add(txt,0,wx.ALIGN_CENTER)
10183+
sizer1.Add((10,-1),1,wx.EXPAND)
10184+
if help:
10185+
sizer1.Add(HelpButton(dlg,help),0,wx.ALL)
10186+
mainSizer.Add(sizer1,0,wx.EXPAND)
10187+
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
10188+
valItem = wx.TextCtrl(dlg,wx.ID_ANY,value=start,style=wx.TE_PROCESS_ENTER)
10189+
valItem.Bind(wx.EVT_CHAR, on_char_typed)
10190+
valItem.Bind(wx.EVT_TEXT_ENTER, lambda event: event.Skip(False))
10191+
wx.CallAfter(valItem.SetSelection,0,0) # clear the initial selection
10192+
mainSizer.Add(valItem,1,wx.EXPAND)
10193+
btnsizer = wx.StdDialogButtonSizer()
10194+
OKbtn = wx.Button(dlg, wx.ID_OK)
10195+
OKbtn.SetDefault()
10196+
btnsizer.AddButton(OKbtn)
10197+
btn = wx.Button(dlg, wx.ID_CANCEL)
10198+
btnsizer.AddButton(btn)
10199+
btnsizer.Realize()
10200+
mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER)
10201+
dlg.SetSizer(mainSizer)
10202+
mainSizer.Fit(dlg)
10203+
ans = dlg.ShowModal()
10204+
if ans != wx.ID_OK:
10205+
dlg.Destroy()
10206+
return
10207+
else:
10208+
val = valItem.GetValue()
10209+
dlg.Destroy()
10210+
return val
10211+
1013210212
if __name__ == '__main__':
1013310213
app = wx.App()
1013410214
GSASIIpath.InvokeDebugOpts()

0 commit comments

Comments
 (0)