I'm trying to create a user interface for a class that contains instances of other classes. Here is an illustrative example:
from traits.etsconfig.api import ETSConfig
ETSConfig.toolkit = 'qt4'
from traits.api import HasTraits, Instance, String
from traitsui.api import Action, Controller, Item, View
import sys, traits, traitsui
print ('python version :',sys.version) # 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 08:06:12) [MSC v.1900 64 bit (AMD64)]
print ('traits version :',traits.__version__) # 4.6.0
print ('traitsui version :',traitsui.__version__) # 5.1.0
class Name (HasTraits):
first = String
last = String
view = View (Item('first'), Item('last'), title='Person Name', buttons=['OK','Cancel'])
def __str__ (self):
return '{} {}'.format(self.first,self.last)
class Person (HasTraits):
name = Instance(Name)
view = View (Item('name'), title='Person Info', buttons=['OK','Cancel'])
def __str__ (self):
return str(self.name)
class Datastore (HasTraits):
def add (self, person:Person):
print ('added', person)
class Contacts (Controller):
add = Action (name='Add', action='_add')
view = View (title='Datastore', buttons=[add])
def _add (self, info):
person = Person (name=Name())
save = person.configure_traits (kind='modal')
if save:
self.model.add (person)
else:
print ('user canceled ADD action')
datastore = Datastore()
controller = Contacts (model=datastore)
controller.configure_traits()
It seems I have to use kind=modal
to pause execution in the Contacts._add
method while the user enters information.
A typical user interaction is:
Problem is: the Person Name dialog can not be made active because Person Info has focus.
How can the desired behavior (giving Person Name focus) be achieved?
Thanks.
If you wish to have different windows for the different views (rather than one window with nested views), you could also make the call to person.configure_traits
into a call to person.edit_traits
and pass in a Handler
that adds the Person
instance to the data store when the window is closed.
More specifically, I changed your controller to
class Contacts (Controller):
add = Action (name='Add', action='_add')
view = View (title='Datastore', buttons=[add])
def _add (self, info):
person = Person (name=Name())
person.edit_traits(
handler=PersonHandler(datastore=self.model)
)
The handler is passed a reference to the data store, and adds the person to it when the user clicks "OK":
class PersonHandler(Handler):
""" Handler class to add a person to a datastore.
"""
datastore = Instance(Datastore)
def close(self, info, is_ok):
if is_ok:
print('adding to datastore')
self.datastore.add(info.object)
else:
print('not added')
return super(PersonHandler, self).close(info, is_ok)
Full code sample: https://gist.github.com/jvkersch/e72f2fb4ed72b2e9ba8269c6a6a2957a