From b61aa1fa494a852f72c855b2182a2731af9837c3 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 13:39:26 -0500 Subject: [PATCH 1/9] Multiline text edit (Qt support) --- enaml/qt/qt_factories.py | 6 ++ enaml/qt/qt_multiline_edit.py | 140 ++++++++++++++++++++++++++++++++ enaml/widgets/api.py | 1 + enaml/widgets/multiline_edit.py | 78 ++++++++++++++++++ 4 files changed, 225 insertions(+) create mode 100644 enaml/qt/qt_multiline_edit.py create mode 100644 enaml/widgets/multiline_edit.py diff --git a/enaml/qt/qt_factories.py b/enaml/qt/qt_factories.py index bf691d87..3b832665 100644 --- a/enaml/qt/qt_factories.py +++ b/enaml/qt/qt_factories.py @@ -142,6 +142,11 @@ def mpl_canvas_factory(): return QtMPLCanvas +def multiline_edit_factory(): + from .qt_multiline_edit import QtMultiLineEdit + return QtMultiLineEdit + + def notebook_factory(): from .qt_notebook import QtNotebook return QtNotebook @@ -269,6 +274,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) + register('MultiLineEdit', multiline_edit_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/qt/qt_multiline_edit.py b/enaml/qt/qt_multiline_edit.py new file mode 100644 index 00000000..ac925390 --- /dev/null +++ b/enaml/qt/qt_multiline_edit.py @@ -0,0 +1,140 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2012, Enthought, Inc. +# All rights reserved. +#------------------------------------------------------------------------------ + +from .qt.QtGui import QTextEdit +from .qt.QtCore import Signal, QTimer +from .qt_control import QtControl + + +class QtFocusMultiLineEdit(QTextEdit): + """ A QLineEdit subclass which converts a lost focus event into + a lost focus signal. + + """ + lostFocus = Signal() + delayedTextChanged = Signal() + + def __init__(self, parent=None): + super(QtFocusMultiLineEdit, self).__init__(parent) + + # Set up a collapsing timer to fire 200 milliseconds + # after the text is changed + self._changed_timer = timer = QTimer() + timer.setInterval(200) + timer.setSingleShot(True) + timer.timeout.connect(self.delayedTextChanged.emit) + + # Connect the timer to the text changed signal + self.textChanged.connect(timer.start) + + def focusOutEvent(self, event): + self.lostFocus.emit() + return super(QtFocusMultiLineEdit, self).focusOutEvent(event) + + +class QtMultiLineEdit(QtControl): + """ A Qt4 implementation of an Enaml Field. + + """ + #: The list of submit triggers for when to submit a text change. + _submit_triggers = [] + + #-------------------------------------------------------------------------- + # Setup Methods + #-------------------------------------------------------------------------- + def create_widget(self, parent, tree): + """ Creates the underlying QLineEdit widget. + + """ + return QtFocusMultiLineEdit(parent) + + def create(self, tree): + """ Create and initialize the underlying widget. + + """ + super(QtMultiLineEdit, self).create(tree) + self.set_text(tree['text']) + self.set_submit_triggers(tree['submit_triggers']) + self.set_read_only(tree['read_only']) + widget = self.widget() + widget.lostFocus.connect(self.on_lost_focus) + widget.delayedTextChanged.connect(self.on_text_changed) + + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + def _submit_text(self): + """ Submit the given text as an update to the server widget. + + Parameters + ---------- + text : unicode + The unicode text to send to the server widget. + + """ + text = self.widget().toPlainText() + content = {'text': text} + self.send_action('submit_text', content) + + #-------------------------------------------------------------------------- + # Signal Handlers + #-------------------------------------------------------------------------- + def on_lost_focus(self): + """ The signal handler for 'lostFocus' signal. + + """ + if 'lost_focus' in self._submit_triggers: + self._submit_text() + + def on_text_changed(self): + """ The signal handler for 'delayedTextChanged' signal. + + """ + if 'text_changed' in self._submit_triggers: + self._submit_text() + + #-------------------------------------------------------------------------- + # Message Handlers + #-------------------------------------------------------------------------- + def on_action_set_text(self, content): + """ Handle the 'set_text' action from the Enaml widget. + + """ + self.set_text(content['text']) + + def on_action_set_submit_triggers(self, content): + """ Handle the 'set_submit_triggers' action from the Enaml + widget. + + """ + self.set_submit_triggers(content['submit_triggers']) + + def on_action_set_read_only(self, content): + """ Handle the 'set_read_only' action from the Enaml widget. + + """ + self.set_read_only(content['read_only']) + + #-------------------------------------------------------------------------- + # Widget Update Methods + #-------------------------------------------------------------------------- + def set_text(self, text): + """ Set the text in the underlying widget. + + """ + self.widget().setText(text) + + def set_submit_triggers(self, triggers): + """ Set the submit triggers for the underlying widget. + + """ + self._submit_triggers = triggers + + def set_read_only(self, read_only): + """ Set whether or not the widget is read only. + + """ + self.widget().setEnabled(not read_only) + diff --git a/enaml/widgets/api.py b/enaml/widgets/api.py index 07d2c740..56d16b7f 100644 --- a/enaml/widgets/api.py +++ b/enaml/widgets/api.py @@ -35,6 +35,7 @@ from .menu import Menu from .menu_bar import MenuBar from .mpl_canvas import MPLCanvas +from .multiline_edit import MultiLineEdit from .notebook import Notebook from .page import Page from .progress_bar import ProgressBar diff --git a/enaml/widgets/multiline_edit.py b/enaml/widgets/multiline_edit.py new file mode 100644 index 00000000..c70ca089 --- /dev/null +++ b/enaml/widgets/multiline_edit.py @@ -0,0 +1,78 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2012, Enthought, Inc. +# All rights reserved. +#------------------------------------------------------------------------------ +from traits.api import Bool, Unicode, Enum, List + +from enaml.validation.validator import Validator + +from .control import Control + + +class MultiLineEdit(Control): + """ A multi line editable text widget. + + """ + #: The unicode text to display in the field. + text = Unicode + + #: The list of actions which should cause the client to submit its + #: text to the server for validation and update. The currently + #: supported values are 'lost_focus' and 'return_pressed'. + submit_triggers = List( + Enum('lost_focus', 'text_changed'), ['lost_focus', 'text_changed'] + ) + + #: Whether or not the field is read only. Defaults to False. + read_only = Bool(False) + + #: How strongly a component hugs it's contents' width. Text boxes ignore + #: both the width and height hugs by default, so they expand freely in both + #: directions + hug_width = 'ignore' + hug_height = 'ignore' + + #-------------------------------------------------------------------------- + # Initialization + #-------------------------------------------------------------------------- + def snapshot(self): + """ Returns the snapshot dict for the textedit + + """ + snap = super(MultiLineEdit, self).snapshot() + snap['text'] = self.text + snap['submit_triggers'] = self.submit_triggers + snap['read_only'] = self.read_only + return snap + + def bind(self): + """ A method called after initialization which allows the widget + to bind any event handlers necessary. + + """ + super(MultiLineEdit, self).bind() + attrs = ( + 'text', 'read_only', + ) + self.publish_attributes(*attrs) + self.on_trait_change(self._send_submit_triggers, 'submit_triggers[]') + + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + def _send_submit_triggers(self): + """ Send the new submit triggers to the client widget. + + """ + content = {'submit_triggers': self.submit_triggers} + self.send_action('set_submit_triggers', content) + + #-------------------------------------------------------------------------- + # Message Handling + #-------------------------------------------------------------------------- + def on_action_submit_text(self, content): + """ Handle the 'submit_text' action from the client widget. + + """ + text = content['text'] + self.set_guarded(text=text) From 2a0852379230c96bd9eaa52945b9c0f2674f9f50 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 13:39:57 -0500 Subject: [PATCH 2/9] Add multiline edit to group box example --- examples/widgets/group_box.enaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/widgets/group_box.enaml b/examples/widgets/group_box.enaml index 1d87ed6d..bc3baa6a 100644 --- a/examples/widgets/group_box.enaml +++ b/examples/widgets/group_box.enaml @@ -11,6 +11,7 @@ an optional bounding box and title. from enaml.layout.api import vbox, hbox, spacer, align from enaml.widgets.api import ( Window, Container, GroupBox, Form, Label, Field, CheckBox, ComboBox, + MultiLineEdit, ) @@ -21,6 +22,7 @@ enamldef Main(Window): vbox( grp_box, hbox(spacer, title_check, flat_check, combo_box), + multiline, ), align('v_center', title_check, flat_check, combo_box), ] @@ -53,4 +55,5 @@ enamldef Main(Window): id: combo_box index = 0 items = ['left', 'center', 'right'] - + MultiLineEdit: + id: multiline From 85e49c795b19cb0fc41e18f1f1ae2ddf439cd516 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 12:39:58 -0500 Subject: [PATCH 3/9] Wx implementation of multi line edit --- enaml/wx/wx_factories.py | 6 ++ enaml/wx/wx_multiline_edit.py | 185 ++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 enaml/wx/wx_multiline_edit.py diff --git a/enaml/wx/wx_factories.py b/enaml/wx/wx_factories.py index 1f717ea1..122898cb 100644 --- a/enaml/wx/wx_factories.py +++ b/enaml/wx/wx_factories.py @@ -102,6 +102,11 @@ def mpl_canvas_factory(): return WxMPLCanvas +def multiline_edit_factory(): + from wx_multiline_edit import WxMultiLineEdit + return WxMultiLineEdit + + def notebook_factory(): from .wx_notebook import WxNotebook return WxNotebook @@ -194,6 +199,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) + register('MultiLineEdit', multiline_edit_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/wx/wx_multiline_edit.py b/enaml/wx/wx_multiline_edit.py new file mode 100644 index 00000000..a4626e7e --- /dev/null +++ b/enaml/wx/wx_multiline_edit.py @@ -0,0 +1,185 @@ +#------------------------------------------------------------------------------ +# Copyright (c) 2012, Enthought, Inc. +# All rights reserved. +#------------------------------------------------------------------------------ +import wx + +from .wx_control import WxControl + + +class wxMultiLineEdit(wx.TextCtrl): + """ A wx.TextCtrl subclass which is similar to a QMultiLineEdit in terms + of features and behavior. + + """ + def __init__(self, *args, **kwargs): + """ Initialize a wxMultiLineEdit. + + Parameters + ---------- + *args, **kwargs + The positional and keyword arguments to initialize a + wx.TextCtrl. + + """ + super(wxMultiLineEdit, self).__init__(*args, **kwargs) + self._timer = wx.Timer(self) + self.Bind(wx.EVT_TIMER, self.OnTimerFired, self._timer) + self.Bind(wx.TEXT, self.OnTextEdited) + + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + + #-------------------------------------------------------------------------- + # Event Handlers + #-------------------------------------------------------------------------- + def OnTextEdited(self, event): + """ Start the collapsing timer when the text is edited + + """ + self._timer.Start(200, oneShot=True) + event.Skip() + + def OnTimerFired(self, event): + """ Handles the wx.EVT_TIMER event for delayed text change event + + """ + print "timer fired" + + #-------------------------------------------------------------------------- + # Public API + #-------------------------------------------------------------------------- + def GetBestSize(self): + """ Overridden best size method to add 246 pixels in width and 176 + pixels to the height. This makes Wx consistent with Qt. + + """ + size = super(wxMultiLineEdit, self).GetBestSize() + return wx.Size(size.GetWidth() + 246, size.GetHeight() + 176) + + def ChangeValue(self, text): + """ Overridden method which moves the insertion point to the end + of the field when changing the text value. This causes the field + to behave like Qt. + + """ + super(wxMultiLineEdit, self).ChangeValue(text) + self.SetInsertionPoint(len(text)) + + +class WxMultiLineEdit(WxControl): + """ A Wx implementation of an Enaml Field. + + """ + #: The list of submit triggers for when to submit a text change. + _submit_triggers = [] + + #-------------------------------------------------------------------------- + # Setup Methods + #-------------------------------------------------------------------------- + def create_widget(self, parent, tree): + """ Creates the underlying wxLineEdit. + + """ + # We have to do a bit of initialization in the create method + # since wx requires the style of certain things to be set at + # the point of instantiation + style = wx.TE_MULTILINE + if tree['read_only']: + style |= wx.TE_READONLY + else: + style &= ~wx.TE_READONLY + return wxLineEdit(parent, style=style) + + def create(self, tree): + """ Create and initialize the wx field control. + + """ + super(WxMultiLineEdit, self).create(tree) + self.set_text(tree['text']) + self.set_submit_triggers(tree['submit_triggers']) + widget = self.widget() + widget.Bind(wx.EVT_KILL_FOCUS, self.on_lost_focus) + widget.Bind(wx.EVT_TEXT_CHANGED, self.on_text_changed) + + #-------------------------------------------------------------------------- + # Private API + #-------------------------------------------------------------------------- + def _submit_text(self): + """ Submit the given text as an update to the server widget. + + Parameters + ---------- + text : unicode + The unicode text to send to the server widget. + + """ + text = self.widget().GetValue() + content = {'text': text} + self.send_action('submit_text', content) + + #-------------------------------------------------------------------------- + # Event Handling + #-------------------------------------------------------------------------- + def on_lost_focus(self, event): + """ The event handler for EVT_KILL_FOCUS event. + + """ + event.Skip() + if 'lost_focus' in self._submit_triggers: + self._submit_text() + + def on_text_changed(self, event): + """ The event handler for EVT_TEXT_CHANGED event. + + """ + event.Skip() + if 'text_changed' in self._submit_triggers: + self._submit_text() + + #-------------------------------------------------------------------------- + # Message Handling + #-------------------------------------------------------------------------- + def on_action_set_text(self, content): + """ Handle the 'set_text' action from the Enaml widget. + + """ + self.set_text(content['text']) + + def on_action_set_submit_triggers(self, content): + """ Handle the 'set_submit_triggers' action from the Enaml + widget. + + """ + self.set_submit_triggers(content['sumbit_triggers']) + + def on_action_set_read_only(self, content): + """ Handle the 'set_read_only' action from the Enaml widget. + + """ + self.set_read_only(content['read_only']) + + #-------------------------------------------------------------------------- + # Widget Update Methods + #-------------------------------------------------------------------------- + def set_text(self, text): + """ Updates the text control with the given unicode text. + + """ + self.widget().ChangeValue(text) + + def set_submit_triggers(self, triggers): + """ Set the submit triggers for the underlying widget. + + """ + self._submit_triggers = triggers + + def set_read_only(self, read_only): + """ Sets the read only state of the widget. + + """ + # Wx cannot change the read only state dynamically. It requires + # creating a brand-new control, so we just ignore the change. + pass + From 501ccb2a7ed58aee206051929b59e3e1266c9175 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 13:32:08 -0500 Subject: [PATCH 4/9] Add wx event for delayed timer --- enaml/wx/wx_multiline_edit.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/enaml/wx/wx_multiline_edit.py b/enaml/wx/wx_multiline_edit.py index a4626e7e..9b3b882a 100644 --- a/enaml/wx/wx_multiline_edit.py +++ b/enaml/wx/wx_multiline_edit.py @@ -7,6 +7,11 @@ from .wx_control import WxControl +# Create event for a delayed text changed +wxEVT_TEXT_CHANGED = wx.NewEventType() +EVT_TEXT_CHANGED = wx.PyEventBinder(wxEVT_TEXT_CHANGED, 1) + + class wxMultiLineEdit(wx.TextCtrl): """ A wx.TextCtrl subclass which is similar to a QMultiLineEdit in terms of features and behavior. @@ -25,7 +30,7 @@ def __init__(self, *args, **kwargs): super(wxMultiLineEdit, self).__init__(*args, **kwargs) self._timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnTimerFired, self._timer) - self.Bind(wx.TEXT, self.OnTextEdited) + self.Bind(wx.EVT_TEXT, self.OnTextEdited) #-------------------------------------------------------------------------- # Private API @@ -45,7 +50,8 @@ def OnTimerFired(self, event): """ Handles the wx.EVT_TIMER event for delayed text change event """ - print "timer fired" + textEvent = wx.CommandEvent(wxEVT_TEXT_CHANGED, self.GetId()) + self.GetEventHandler().ProcessEvent(textEvent) #-------------------------------------------------------------------------- # Public API @@ -90,7 +96,7 @@ def create_widget(self, parent, tree): style |= wx.TE_READONLY else: style &= ~wx.TE_READONLY - return wxLineEdit(parent, style=style) + return wxMultiLineEdit(parent, style=style) def create(self, tree): """ Create and initialize the wx field control. @@ -101,7 +107,7 @@ def create(self, tree): self.set_submit_triggers(tree['submit_triggers']) widget = self.widget() widget.Bind(wx.EVT_KILL_FOCUS, self.on_lost_focus) - widget.Bind(wx.EVT_TEXT_CHANGED, self.on_text_changed) + widget.Bind(EVT_TEXT_CHANGED, self.on_text_changed) #-------------------------------------------------------------------------- # Private API From 621fc84a9b144e02569137db78bf99cf68422d6b Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 22:24:18 -0500 Subject: [PATCH 5/9] Cleanup requested by Chris --- enaml/qt/qt_multiline_edit.py | 22 +++++++++------------- enaml/widgets/multiline_edit.py | 2 +- enaml/wx/wx_multiline_edit.py | 5 +++-- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/enaml/qt/qt_multiline_edit.py b/enaml/qt/qt_multiline_edit.py index ac925390..eefe9082 100644 --- a/enaml/qt/qt_multiline_edit.py +++ b/enaml/qt/qt_multiline_edit.py @@ -1,37 +1,37 @@ #------------------------------------------------------------------------------ -# Copyright (c) 2012, Enthought, Inc. +# Copyright (c) 2013, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ - from .qt.QtGui import QTextEdit from .qt.QtCore import Signal, QTimer from .qt_control import QtControl -class QtFocusMultiLineEdit(QTextEdit): +class QFocusMultiLineEdit(QTextEdit): """ A QLineEdit subclass which converts a lost focus event into a lost focus signal. """ lostFocus = Signal() + delayedTextChanged = Signal() def __init__(self, parent=None): - super(QtFocusMultiLineEdit, self).__init__(parent) + super(QFocusMultiLineEdit, self).__init__(parent) # Set up a collapsing timer to fire 200 milliseconds # after the text is changed self._changed_timer = timer = QTimer() timer.setInterval(200) timer.setSingleShot(True) - timer.timeout.connect(self.delayedTextChanged.emit) + timer.timeout.connect(self.delayedTextChanged) # Connect the timer to the text changed signal self.textChanged.connect(timer.start) def focusOutEvent(self, event): self.lostFocus.emit() - return super(QtFocusMultiLineEdit, self).focusOutEvent(event) + return super(QFocusMultiLineEdit, self).focusOutEvent(event) class QtMultiLineEdit(QtControl): @@ -48,7 +48,7 @@ def create_widget(self, parent, tree): """ Creates the underlying QLineEdit widget. """ - return QtFocusMultiLineEdit(parent) + return QFocusMultiLineEdit(parent) def create(self, tree): """ Create and initialize the underlying widget. @@ -68,11 +68,6 @@ def create(self, tree): def _submit_text(self): """ Submit the given text as an update to the server widget. - Parameters - ---------- - text : unicode - The unicode text to send to the server widget. - """ text = self.widget().toPlainText() content = {'text': text} @@ -124,7 +119,8 @@ def set_text(self, text): """ Set the text in the underlying widget. """ - self.widget().setText(text) + with self.loopback_guard('text'): + self.widget().setText(text) def set_submit_triggers(self, triggers): """ Set the submit triggers for the underlying widget. diff --git a/enaml/widgets/multiline_edit.py b/enaml/widgets/multiline_edit.py index c70ca089..3f47eff7 100644 --- a/enaml/widgets/multiline_edit.py +++ b/enaml/widgets/multiline_edit.py @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------ -# Copyright (c) 2012, Enthought, Inc. +# Copyright (c) 2013, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ from traits.api import Bool, Unicode, Enum, List diff --git a/enaml/wx/wx_multiline_edit.py b/enaml/wx/wx_multiline_edit.py index 9b3b882a..a1fb8eaf 100644 --- a/enaml/wx/wx_multiline_edit.py +++ b/enaml/wx/wx_multiline_edit.py @@ -1,5 +1,5 @@ #------------------------------------------------------------------------------ -# Copyright (c) 2012, Enthought, Inc. +# Copyright (c) 2013, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ import wx @@ -173,7 +173,8 @@ def set_text(self, text): """ Updates the text control with the given unicode text. """ - self.widget().ChangeValue(text) + with self.loopback_guard('text'): + self.widget().ChangeValue(text) def set_submit_triggers(self, triggers): """ Set the submit triggers for the underlying widget. From 75e6470fd528fbf944b155a9829f2693c1f8f863 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 22:32:58 -0500 Subject: [PATCH 6/9] Rename MultiLineEdit to MultiLineField --- enaml/qt/qt_factories.py | 8 ++++---- ...ultiline_edit.py => qt_multiline_field.py} | 8 ++++---- enaml/widgets/api.py | 2 +- .../{multiline_edit.py => multiline_field.py} | 6 +++--- enaml/wx/wx_factories.py | 8 ++++---- ...ultiline_edit.py => wx_multiline_field.py} | 20 +++++++++---------- examples/widgets/group_box.enaml | 4 ++-- 7 files changed, 28 insertions(+), 28 deletions(-) rename enaml/qt/{qt_multiline_edit.py => qt_multiline_field.py} (95%) rename enaml/widgets/{multiline_edit.py => multiline_field.py} (95%) rename enaml/wx/{wx_multiline_edit.py => wx_multiline_field.py} (91%) diff --git a/enaml/qt/qt_factories.py b/enaml/qt/qt_factories.py index 3b832665..8dd15630 100644 --- a/enaml/qt/qt_factories.py +++ b/enaml/qt/qt_factories.py @@ -142,9 +142,9 @@ def mpl_canvas_factory(): return QtMPLCanvas -def multiline_edit_factory(): - from .qt_multiline_edit import QtMultiLineEdit - return QtMultiLineEdit +def multiline_field_factory(): + from .qt_multiline_field import QtMultiLineField + return QtMultiLineField def notebook_factory(): @@ -274,7 +274,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) - register('MultiLineEdit', multiline_edit_factory) + register('MultiLineField', multiline_field_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/qt/qt_multiline_edit.py b/enaml/qt/qt_multiline_field.py similarity index 95% rename from enaml/qt/qt_multiline_edit.py rename to enaml/qt/qt_multiline_field.py index eefe9082..78de85be 100644 --- a/enaml/qt/qt_multiline_edit.py +++ b/enaml/qt/qt_multiline_field.py @@ -8,7 +8,7 @@ class QFocusMultiLineEdit(QTextEdit): - """ A QLineEdit subclass which converts a lost focus event into + """ A QTextEdit subclass which converts a lost focus event into a lost focus signal. """ @@ -34,7 +34,7 @@ def focusOutEvent(self, event): return super(QFocusMultiLineEdit, self).focusOutEvent(event) -class QtMultiLineEdit(QtControl): +class QtMultiLineField(QtControl): """ A Qt4 implementation of an Enaml Field. """ @@ -45,7 +45,7 @@ class QtMultiLineEdit(QtControl): # Setup Methods #-------------------------------------------------------------------------- def create_widget(self, parent, tree): - """ Creates the underlying QLineEdit widget. + """ Creates the underlying QFocusMultiLineEdit widget. """ return QFocusMultiLineEdit(parent) @@ -54,7 +54,7 @@ def create(self, tree): """ Create and initialize the underlying widget. """ - super(QtMultiLineEdit, self).create(tree) + super(QtMultiLineField, self).create(tree) self.set_text(tree['text']) self.set_submit_triggers(tree['submit_triggers']) self.set_read_only(tree['read_only']) diff --git a/enaml/widgets/api.py b/enaml/widgets/api.py index 56d16b7f..7b7540a9 100644 --- a/enaml/widgets/api.py +++ b/enaml/widgets/api.py @@ -35,7 +35,7 @@ from .menu import Menu from .menu_bar import MenuBar from .mpl_canvas import MPLCanvas -from .multiline_edit import MultiLineEdit +from .multiline_field import MultiLineField from .notebook import Notebook from .page import Page from .progress_bar import ProgressBar diff --git a/enaml/widgets/multiline_edit.py b/enaml/widgets/multiline_field.py similarity index 95% rename from enaml/widgets/multiline_edit.py rename to enaml/widgets/multiline_field.py index 3f47eff7..bf9329f7 100644 --- a/enaml/widgets/multiline_edit.py +++ b/enaml/widgets/multiline_field.py @@ -9,7 +9,7 @@ from .control import Control -class MultiLineEdit(Control): +class MultiLineField(Control): """ A multi line editable text widget. """ @@ -39,7 +39,7 @@ def snapshot(self): """ Returns the snapshot dict for the textedit """ - snap = super(MultiLineEdit, self).snapshot() + snap = super(MultiLineField, self).snapshot() snap['text'] = self.text snap['submit_triggers'] = self.submit_triggers snap['read_only'] = self.read_only @@ -50,7 +50,7 @@ def bind(self): to bind any event handlers necessary. """ - super(MultiLineEdit, self).bind() + super(MultiLineField, self).bind() attrs = ( 'text', 'read_only', ) diff --git a/enaml/wx/wx_factories.py b/enaml/wx/wx_factories.py index 122898cb..fb43eea1 100644 --- a/enaml/wx/wx_factories.py +++ b/enaml/wx/wx_factories.py @@ -102,9 +102,9 @@ def mpl_canvas_factory(): return WxMPLCanvas -def multiline_edit_factory(): - from wx_multiline_edit import WxMultiLineEdit - return WxMultiLineEdit +def multiline_field_factory(): + from wx_multiline_field import WxMultiLineField + return WxMultiLineField def notebook_factory(): @@ -199,7 +199,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) - register('MultiLineEdit', multiline_edit_factory) + register('MultiLineField', multiline_field_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/wx/wx_multiline_edit.py b/enaml/wx/wx_multiline_field.py similarity index 91% rename from enaml/wx/wx_multiline_edit.py rename to enaml/wx/wx_multiline_field.py index a1fb8eaf..2c5f547a 100644 --- a/enaml/wx/wx_multiline_edit.py +++ b/enaml/wx/wx_multiline_field.py @@ -12,13 +12,13 @@ EVT_TEXT_CHANGED = wx.PyEventBinder(wxEVT_TEXT_CHANGED, 1) -class wxMultiLineEdit(wx.TextCtrl): - """ A wx.TextCtrl subclass which is similar to a QMultiLineEdit in terms +class wxMultiLineField(wx.TextCtrl): + """ A wx.TextCtrl subclass which is similar to a QMultiLineField in terms of features and behavior. """ def __init__(self, *args, **kwargs): - """ Initialize a wxMultiLineEdit. + """ Initialize a wxMultiLineField. Parameters ---------- @@ -27,7 +27,7 @@ def __init__(self, *args, **kwargs): wx.TextCtrl. """ - super(wxMultiLineEdit, self).__init__(*args, **kwargs) + super(wxMultiLineField, self).__init__(*args, **kwargs) self._timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnTimerFired, self._timer) self.Bind(wx.EVT_TEXT, self.OnTextEdited) @@ -61,7 +61,7 @@ def GetBestSize(self): pixels to the height. This makes Wx consistent with Qt. """ - size = super(wxMultiLineEdit, self).GetBestSize() + size = super(wxMultiLineField, self).GetBestSize() return wx.Size(size.GetWidth() + 246, size.GetHeight() + 176) def ChangeValue(self, text): @@ -70,11 +70,11 @@ def ChangeValue(self, text): to behave like Qt. """ - super(wxMultiLineEdit, self).ChangeValue(text) + super(wxMultiLineField, self).ChangeValue(text) self.SetInsertionPoint(len(text)) -class WxMultiLineEdit(WxControl): +class WxMultiLineField(WxControl): """ A Wx implementation of an Enaml Field. """ @@ -85,7 +85,7 @@ class WxMultiLineEdit(WxControl): # Setup Methods #-------------------------------------------------------------------------- def create_widget(self, parent, tree): - """ Creates the underlying wxLineEdit. + """ Creates the underlying wxMultiLineField. """ # We have to do a bit of initialization in the create method @@ -96,13 +96,13 @@ def create_widget(self, parent, tree): style |= wx.TE_READONLY else: style &= ~wx.TE_READONLY - return wxMultiLineEdit(parent, style=style) + return wxMultiLineField(parent, style=style) def create(self, tree): """ Create and initialize the wx field control. """ - super(WxMultiLineEdit, self).create(tree) + super(WxMultiLineField, self).create(tree) self.set_text(tree['text']) self.set_submit_triggers(tree['submit_triggers']) widget = self.widget() diff --git a/examples/widgets/group_box.enaml b/examples/widgets/group_box.enaml index bc3baa6a..bd463419 100644 --- a/examples/widgets/group_box.enaml +++ b/examples/widgets/group_box.enaml @@ -11,7 +11,7 @@ an optional bounding box and title. from enaml.layout.api import vbox, hbox, spacer, align from enaml.widgets.api import ( Window, Container, GroupBox, Form, Label, Field, CheckBox, ComboBox, - MultiLineEdit, + MultiLineField, ) @@ -55,5 +55,5 @@ enamldef Main(Window): id: combo_box index = 0 items = ['left', 'center', 'right'] - MultiLineEdit: + MultiLineField: id: multiline From f31326357ee33da19394fe0b2577f4624a5abe13 Mon Sep 17 00:00:00 2001 From: Naveen Michaud-Agrawal Date: Tue, 12 Feb 2013 22:46:27 -0500 Subject: [PATCH 7/9] Updated group box example - Pressing on a button doesn't cause focus to change --- examples/widgets/group_box.enaml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/widgets/group_box.enaml b/examples/widgets/group_box.enaml index bd463419..d01dfb23 100644 --- a/examples/widgets/group_box.enaml +++ b/examples/widgets/group_box.enaml @@ -11,7 +11,7 @@ an optional bounding box and title. from enaml.layout.api import vbox, hbox, spacer, align from enaml.widgets.api import ( Window, Container, GroupBox, Form, Label, Field, CheckBox, ComboBox, - MultiLineField, + MultiLineField, PushButton, ) @@ -21,7 +21,7 @@ enamldef Main(Window): constraints = [ vbox( grp_box, - hbox(spacer, title_check, flat_check, combo_box), + hbox(push, spacer, title_check, flat_check, combo_box), multiline, ), align('v_center', title_check, flat_check, combo_box), @@ -51,9 +51,14 @@ enamldef Main(Window): id: flat_check text = "Flat" checked := grp_box.flat + PushButton: + id: push + text = "Submit" + clicked :: print multiline.text ComboBox: id: combo_box index = 0 items = ['left', 'center', 'right'] MultiLineField: id: multiline + submit_triggers = ['lost_focus'] From 77c62527d24caac7ea2cbbf03ae3a5e80e81b444 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Wed, 13 Feb 2013 10:50:25 -0500 Subject: [PATCH 8/9] Cleanup --- enaml/qt/qt_factories.py | 6 +- enaml/qt/qt_multiline_field.py | 68 ++++++------------ enaml/widgets/api.py | 2 +- enaml/widgets/multiline_field.py | 64 +++++++---------- enaml/wx/wx_factories.py | 6 +- enaml/wx/wx_group_box.py | 18 ++--- enaml/wx/wx_multiline_field.py | 117 ++++++++++++------------------- examples/widgets/group_box.enaml | 11 +-- 8 files changed, 119 insertions(+), 173 deletions(-) diff --git a/enaml/qt/qt_factories.py b/enaml/qt/qt_factories.py index 8dd15630..257af69b 100644 --- a/enaml/qt/qt_factories.py +++ b/enaml/qt/qt_factories.py @@ -143,8 +143,8 @@ def mpl_canvas_factory(): def multiline_field_factory(): - from .qt_multiline_field import QtMultiLineField - return QtMultiLineField + from .qt_multiline_field import QtMultilineField + return QtMultilineField def notebook_factory(): @@ -274,7 +274,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) - register('MultiLineField', multiline_field_factory) + register('MultilineField', multiline_field_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/qt/qt_multiline_field.py b/enaml/qt/qt_multiline_field.py index 78de85be..6b685c99 100644 --- a/enaml/qt/qt_multiline_field.py +++ b/enaml/qt/qt_multiline_field.py @@ -7,39 +7,27 @@ from .qt_control import QtControl -class QFocusMultiLineEdit(QTextEdit): - """ A QTextEdit subclass which converts a lost focus event into - a lost focus signal. +class QMultilineEdit(QTextEdit): + """ A QTextEdit which notifies on a collapsing timer. """ - lostFocus = Signal() - delayedTextChanged = Signal() def __init__(self, parent=None): - super(QFocusMultiLineEdit, self).__init__(parent) - - # Set up a collapsing timer to fire 200 milliseconds - # after the text is changed + super(QMultilineEdit, self).__init__(parent) self._changed_timer = timer = QTimer() timer.setInterval(200) timer.setSingleShot(True) timer.timeout.connect(self.delayedTextChanged) - - # Connect the timer to the text changed signal self.textChanged.connect(timer.start) - def focusOutEvent(self, event): - self.lostFocus.emit() - return super(QFocusMultiLineEdit, self).focusOutEvent(event) - -class QtMultiLineField(QtControl): +class QtMultilineField(QtControl): """ A Qt4 implementation of an Enaml Field. """ - #: The list of submit triggers for when to submit a text change. - _submit_triggers = [] + #: Whether or not to auto synchronize the text on change. + _auto_sync_text = True #-------------------------------------------------------------------------- # Setup Methods @@ -48,47 +36,38 @@ def create_widget(self, parent, tree): """ Creates the underlying QFocusMultiLineEdit widget. """ - return QFocusMultiLineEdit(parent) + return QMultilineEdit(parent) def create(self, tree): """ Create and initialize the underlying widget. """ - super(QtMultiLineField, self).create(tree) + super(QtMultilineField, self).create(tree) + self._auto_sync_text = tree['auto_sync_text'] self.set_text(tree['text']) - self.set_submit_triggers(tree['submit_triggers']) self.set_read_only(tree['read_only']) widget = self.widget() - widget.lostFocus.connect(self.on_lost_focus) widget.delayedTextChanged.connect(self.on_text_changed) #-------------------------------------------------------------------------- # Private API #-------------------------------------------------------------------------- - def _submit_text(self): - """ Submit the given text as an update to the server widget. + def _send_text_changed(self): + """ Send the current text as an update to the server widget. """ text = self.widget().toPlainText() - content = {'text': text} - self.send_action('submit_text', content) + self.send_action('text_changed', {'text': text}) #-------------------------------------------------------------------------- # Signal Handlers #-------------------------------------------------------------------------- - def on_lost_focus(self): - """ The signal handler for 'lostFocus' signal. - - """ - if 'lost_focus' in self._submit_triggers: - self._submit_text() - def on_text_changed(self): """ The signal handler for 'delayedTextChanged' signal. """ - if 'text_changed' in self._submit_triggers: - self._submit_text() + if self._auto_sync_text and 'text' not in self.loopback_guard: + self._send_text_changed() #-------------------------------------------------------------------------- # Message Handlers @@ -99,12 +78,11 @@ def on_action_set_text(self, content): """ self.set_text(content['text']) - def on_action_set_submit_triggers(self, content): - """ Handle the 'set_submit_triggers' action from the Enaml - widget. + def on_action_set_auto_sync_text(self, content): + """ Handle the 'set_auto_sync_text' action from the Enaml widget. """ - self.set_submit_triggers(content['submit_triggers']) + self._auto_sync_text = content['auto_sync_text'] def on_action_set_read_only(self, content): """ Handle the 'set_read_only' action from the Enaml widget. @@ -112,6 +90,12 @@ def on_action_set_read_only(self, content): """ self.set_read_only(content['read_only']) + def on_action_sync_text(self, content): + """ Handle the 'sync_text' action from the Enaml widget. + + """ + self._send_text_changed() + #-------------------------------------------------------------------------- # Widget Update Methods #-------------------------------------------------------------------------- @@ -122,12 +106,6 @@ def set_text(self, text): with self.loopback_guard('text'): self.widget().setText(text) - def set_submit_triggers(self, triggers): - """ Set the submit triggers for the underlying widget. - - """ - self._submit_triggers = triggers - def set_read_only(self, read_only): """ Set whether or not the widget is read only. diff --git a/enaml/widgets/api.py b/enaml/widgets/api.py index 7b7540a9..780f2509 100644 --- a/enaml/widgets/api.py +++ b/enaml/widgets/api.py @@ -35,7 +35,7 @@ from .menu import Menu from .menu_bar import MenuBar from .mpl_canvas import MPLCanvas -from .multiline_field import MultiLineField +from .multiline_field import MultilineField from .notebook import Notebook from .page import Page from .progress_bar import ProgressBar diff --git a/enaml/widgets/multiline_field.py b/enaml/widgets/multiline_field.py index bf9329f7..aa37d7aa 100644 --- a/enaml/widgets/multiline_field.py +++ b/enaml/widgets/multiline_field.py @@ -2,33 +2,28 @@ # Copyright (c) 2013, Enthought, Inc. # All rights reserved. #------------------------------------------------------------------------------ -from traits.api import Bool, Unicode, Enum, List - -from enaml.validation.validator import Validator +from traits.api import Bool, Unicode from .control import Control -class MultiLineField(Control): - """ A multi line editable text widget. +class MultilineField(Control): + """ A simple multiline editable text widget. """ #: The unicode text to display in the field. text = Unicode - #: The list of actions which should cause the client to submit its - #: text to the server for validation and update. The currently - #: supported values are 'lost_focus' and 'return_pressed'. - submit_triggers = List( - Enum('lost_focus', 'text_changed'), ['lost_focus', 'text_changed'] - ) - - #: Whether or not the field is read only. Defaults to False. + #: Whether or not the field is read only. read_only = Bool(False) - #: How strongly a component hugs it's contents' width. Text boxes ignore - #: both the width and height hugs by default, so they expand freely in both - #: directions + #: Whether the text in the control should be auto-synchronized with + #: the text attribute on the field. If this is True, the text will + #: be updated every time the user edits the control. In order to be + #: efficient, the toolkit will batch updates on a collapsing timer. + auto_sync_text = Bool(True) + + #: Multiline fields expand freely in width and height by default. hug_width = 'ignore' hug_height = 'ignore' @@ -36,43 +31,38 @@ class MultiLineField(Control): # Initialization #-------------------------------------------------------------------------- def snapshot(self): - """ Returns the snapshot dict for the textedit + """ Get the snapshot dict for the control. """ - snap = super(MultiLineField, self).snapshot() + snap = super(MultilineField, self).snapshot() snap['text'] = self.text - snap['submit_triggers'] = self.submit_triggers snap['read_only'] = self.read_only + snap['auto_sync_text'] = self.auto_sync_text return snap def bind(self): - """ A method called after initialization which allows the widget - to bind any event handlers necessary. + """ Bind the change handlers for the control. """ - super(MultiLineField, self).bind() - attrs = ( - 'text', 'read_only', - ) - self.publish_attributes(*attrs) - self.on_trait_change(self._send_submit_triggers, 'submit_triggers[]') + super(MultilineField, self).bind() + self.publish_attributes('text', 'read_only', 'auto_sync_text') #-------------------------------------------------------------------------- - # Private API + # Message Handling #-------------------------------------------------------------------------- - def _send_submit_triggers(self): - """ Send the new submit triggers to the client widget. + def on_action_text_changed(self, content): + """ Handle the 'text_changed' action from the client widget. """ - content = {'submit_triggers': self.submit_triggers} - self.send_action('set_submit_triggers', content) + text = content['text'] + self.set_guarded(text=text) #-------------------------------------------------------------------------- - # Message Handling + # Public API #-------------------------------------------------------------------------- - def on_action_submit_text(self, content): - """ Handle the 'submit_text' action from the client widget. + def sync_text(self): + """ Send a message to the toolkit to synchronize the text. """ - text = content['text'] - self.set_guarded(text=text) + self.send_action('sync_text', {}) + diff --git a/enaml/wx/wx_factories.py b/enaml/wx/wx_factories.py index fb43eea1..d9ea5c37 100644 --- a/enaml/wx/wx_factories.py +++ b/enaml/wx/wx_factories.py @@ -103,8 +103,8 @@ def mpl_canvas_factory(): def multiline_field_factory(): - from wx_multiline_field import WxMultiLineField - return WxMultiLineField + from wx_multiline_field import WxMultilineField + return WxMultilineField def notebook_factory(): @@ -199,7 +199,7 @@ def register_default(): register('Menu', menu_factory) register('MenuBar', menu_bar_factory) register('MPLCanvas', mpl_canvas_factory) - register('MultiLineField', multiline_field_factory) + register('MultilineField', multiline_field_factory) register('Notebook', notebook_factory) register('Page', page_factory) register('PushButton', push_button_factory) diff --git a/enaml/wx/wx_group_box.py b/enaml/wx/wx_group_box.py index 7bea9daf..5fe0acb6 100644 --- a/enaml/wx/wx_group_box.py +++ b/enaml/wx/wx_group_box.py @@ -24,7 +24,7 @@ def __init__(self, *args, **kwargs): Parameters ---------- *args, **kwargs - The positional and keyword arguments to initialize a + The positional and keyword arguments to initialize a wxContainer. """ @@ -37,7 +37,7 @@ def __init__(self, *args, **kwargs): self._label_size = self._label.GetBestSize() self._title_alignment = wx.ALIGN_LEFT self._flat = False - # Set the panel to double buffered or suffer terrible + # Set the panel to double buffered or suffer terrible # rendering artifacts self.SetDoubleBuffered(True) @@ -45,7 +45,7 @@ def __init__(self, *args, **kwargs): # Public API #-------------------------------------------------------------------------- def GetAlignment(self): - """ Return the wx alignment flag for the current alignment + """ Return the wx alignment flag for the current alignment of the group box title. """ @@ -58,7 +58,7 @@ def SetAlignment(self, alignment): """ self._title_alignment = alignment self._update_layout() - + def GetFlat(self): """ Returns a boolean indicating whether the group box is using a flat style. @@ -82,7 +82,7 @@ def SetFlat(self, flat): def GetTitle(self): """ Return the current title text in the group box. - + """ # Undo the hack applied in SetTitle(...) title = self._title @@ -144,7 +144,7 @@ def GetContentsMargins(self): # Private API #-------------------------------------------------------------------------- def _update_layout(self): - """ Synchronizes the drawing of the group box decorations with + """ Synchronizes the drawing of the group box decorations with the panel. """ @@ -161,7 +161,7 @@ def _update_border_geometry(self): """ width, height = self.GetSizeTuple() self._border.SetSizeWH(width, height) - + def _update_line_geometry(self): """ Updates the geometry of the line. @@ -238,7 +238,7 @@ def on_action_set_title(self, content): self.set_title(content['title']) new_margins = widget.GetContentsMargins() if old_margins != new_margins: - self.refresh_contents_constraints() + self.contents_margins_updated() def on_action_set_title_align(self, content): """ Handle the 'set_title_align' action from the Enaml widget. @@ -266,7 +266,7 @@ def set_flat(self, flat): """ self.widget().SetFlat(flat) - + def set_title_align(self, align): """ Updates the alignment of the title of the group box. diff --git a/enaml/wx/wx_multiline_field.py b/enaml/wx/wx_multiline_field.py index 2c5f547a..7416d949 100644 --- a/enaml/wx/wx_multiline_field.py +++ b/enaml/wx/wx_multiline_field.py @@ -3,22 +3,21 @@ # All rights reserved. #------------------------------------------------------------------------------ import wx +import wx.lib.newevent from .wx_control import WxControl -# Create event for a delayed text changed -wxEVT_TEXT_CHANGED = wx.NewEventType() -EVT_TEXT_CHANGED = wx.PyEventBinder(wxEVT_TEXT_CHANGED, 1) +#: The event used to signal a delayed text change. +wxTextChangedEvent, EVT_TEXT_CHANGED = wx.lib.newevent.NewEvent() -class wxMultiLineField(wx.TextCtrl): - """ A wx.TextCtrl subclass which is similar to a QMultiLineField in terms - of features and behavior. +class wxMultilineField(wx.TextCtrl): + """ A text control which notifies on a collpasing timer. """ def __init__(self, *args, **kwargs): - """ Initialize a wxMultiLineField. + """ Initialize a wxMultilineField. Parameters ---------- @@ -27,59 +26,59 @@ def __init__(self, *args, **kwargs): wx.TextCtrl. """ - super(wxMultiLineField, self).__init__(*args, **kwargs) + super(wxMultilineField, self).__init__(*args, **kwargs) self._timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.OnTimerFired, self._timer) self.Bind(wx.EVT_TEXT, self.OnTextEdited) - #-------------------------------------------------------------------------- - # Private API - #-------------------------------------------------------------------------- - #-------------------------------------------------------------------------- # Event Handlers #-------------------------------------------------------------------------- def OnTextEdited(self, event): - """ Start the collapsing timer when the text is edited + """ Restart the collapsing timer when the text is edited. """ self._timer.Start(200, oneShot=True) event.Skip() def OnTimerFired(self, event): - """ Handles the wx.EVT_TIMER event for delayed text change event + """ Handles the wx.EVT_TIMER event for delayed text change. """ - textEvent = wx.CommandEvent(wxEVT_TEXT_CHANGED, self.GetId()) - self.GetEventHandler().ProcessEvent(textEvent) + event = wxTextChangedEvent() + event.SetEventObject(self) + self.GetEventHandler().ProcessEvent(event) #-------------------------------------------------------------------------- # Public API #-------------------------------------------------------------------------- def GetBestSize(self): - """ Overridden best size method to add 246 pixels in width and 176 - pixels to the height. This makes Wx consistent with Qt. + """ A reimplemented best size method. + + This adds 246 pixels in width and 176 pixels to the height to + make Wx consistent with Qt. """ - size = super(wxMultiLineField, self).GetBestSize() + size = super(wxMultilineField, self).GetBestSize() return wx.Size(size.GetWidth() + 246, size.GetHeight() + 176) def ChangeValue(self, text): - """ Overridden method which moves the insertion point to the end - of the field when changing the text value. This causes the field - to behave like Qt. + """ An overridden parent class method. + + This moves the insertion point to the end of the field when the + text value changes. This makes the field to behave like Qt. """ - super(wxMultiLineField, self).ChangeValue(text) - self.SetInsertionPoint(len(text)) + super(wxMultilineField, self).ChangeValue(text) + self.SetInsertionPointEnd() -class WxMultiLineField(WxControl): +class WxMultilineField(WxControl): """ A Wx implementation of an Enaml Field. """ - #: The list of submit triggers for when to submit a text change. - _submit_triggers = [] + #: Whether or not to auto synchronize the text on change. + _auto_sync_text = True #-------------------------------------------------------------------------- # Setup Methods @@ -88,61 +87,40 @@ def create_widget(self, parent, tree): """ Creates the underlying wxMultiLineField. """ - # We have to do a bit of initialization in the create method - # since wx requires the style of certain things to be set at - # the point of instantiation - style = wx.TE_MULTILINE - if tree['read_only']: - style |= wx.TE_READONLY - else: - style &= ~wx.TE_READONLY - return wxMultiLineField(parent, style=style) + style = wx.TE_MULTILINE | wx.TE_RICH + return wxMultilineField(parent, style=style) def create(self, tree): """ Create and initialize the wx field control. """ - super(WxMultiLineField, self).create(tree) + super(WxMultilineField, self).create(tree) + self._auto_sync_text = tree['auto_sync_text'] self.set_text(tree['text']) - self.set_submit_triggers(tree['submit_triggers']) + self.set_read_only(tree['read_only']) widget = self.widget() - widget.Bind(wx.EVT_KILL_FOCUS, self.on_lost_focus) widget.Bind(EVT_TEXT_CHANGED, self.on_text_changed) #-------------------------------------------------------------------------- # Private API #-------------------------------------------------------------------------- - def _submit_text(self): - """ Submit the given text as an update to the server widget. - - Parameters - ---------- - text : unicode - The unicode text to send to the server widget. + def _send_text_changed(self): + """ Send the current text as an update to the server widget. """ text = self.widget().GetValue() - content = {'text': text} - self.send_action('submit_text', content) + self.send_action('text_changed', {'text': text}) #-------------------------------------------------------------------------- # Event Handling #-------------------------------------------------------------------------- - def on_lost_focus(self, event): - """ The event handler for EVT_KILL_FOCUS event. - - """ - event.Skip() - if 'lost_focus' in self._submit_triggers: - self._submit_text() - def on_text_changed(self, event): """ The event handler for EVT_TEXT_CHANGED event. """ event.Skip() - if 'text_changed' in self._submit_triggers: - self._submit_text() + if self._auto_sync_text and 'text' not in self.loopback_guard: + self._send_text_changed() #-------------------------------------------------------------------------- # Message Handling @@ -153,12 +131,11 @@ def on_action_set_text(self, content): """ self.set_text(content['text']) - def on_action_set_submit_triggers(self, content): - """ Handle the 'set_submit_triggers' action from the Enaml - widget. + def on_action_set_auto_sync_text(self, content): + """ Handle the 'set_auto_sync_text' action from the Enaml widget. """ - self.set_submit_triggers(content['sumbit_triggers']) + self._auto_sync_text = content['auto_sync_text'] def on_action_set_read_only(self, content): """ Handle the 'set_read_only' action from the Enaml widget. @@ -166,6 +143,12 @@ def on_action_set_read_only(self, content): """ self.set_read_only(content['read_only']) + def on_action_sync_text(self, content): + """ Handle the 'sync_text' action from the Enaml widget. + + """ + self._send_text_changed() + #-------------------------------------------------------------------------- # Widget Update Methods #-------------------------------------------------------------------------- @@ -176,17 +159,9 @@ def set_text(self, text): with self.loopback_guard('text'): self.widget().ChangeValue(text) - def set_submit_triggers(self, triggers): - """ Set the submit triggers for the underlying widget. - - """ - self._submit_triggers = triggers - def set_read_only(self, read_only): """ Sets the read only state of the widget. """ - # Wx cannot change the read only state dynamically. It requires - # creating a brand-new control, so we just ignore the change. - pass + self.widget().SetEditable(not read_only) diff --git a/examples/widgets/group_box.enaml b/examples/widgets/group_box.enaml index d01dfb23..60e039a3 100644 --- a/examples/widgets/group_box.enaml +++ b/examples/widgets/group_box.enaml @@ -11,7 +11,7 @@ an optional bounding box and title. from enaml.layout.api import vbox, hbox, spacer, align from enaml.widgets.api import ( Window, Container, GroupBox, Form, Label, Field, CheckBox, ComboBox, - MultiLineField, PushButton, + MultilineField, PushButton, ) @@ -30,6 +30,7 @@ enamldef Main(Window): id: grp_box title << "Personal Details" if title_check.checked else "" title_align << combo_box.items[combo_box.index] + hug_height = 'strong' Form: Label: text = "First name:" @@ -54,11 +55,13 @@ enamldef Main(Window): PushButton: id: push text = "Submit" - clicked :: print multiline.text + clicked :: + print 'Submit' + print multiline.text ComboBox: id: combo_box index = 0 items = ['left', 'center', 'right'] - MultiLineField: + MultilineField: id: multiline - submit_triggers = ['lost_focus'] + From 400f7e2b2ac42e0e52865798113b4d593f65be47 Mon Sep 17 00:00:00 2001 From: Chris Colbert Date: Wed, 13 Feb 2013 11:01:55 -0500 Subject: [PATCH 9/9] Remove unneeded Skip --- enaml/wx/wx_multiline_field.py | 1 - 1 file changed, 1 deletion(-) diff --git a/enaml/wx/wx_multiline_field.py b/enaml/wx/wx_multiline_field.py index 7416d949..6e1b7ee2 100644 --- a/enaml/wx/wx_multiline_field.py +++ b/enaml/wx/wx_multiline_field.py @@ -118,7 +118,6 @@ def on_text_changed(self, event): """ The event handler for EVT_TEXT_CHANGED event. """ - event.Skip() if self._auto_sync_text and 'text' not in self.loopback_guard: self._send_text_changed()