GUI components did not update themself

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

GUI components did not update themself

Sergei Gnezdov
I created a small test.  I had one model and one view with 3 components on
it.  Components:
- text edit field for the var instance variable
- label for the var instance variable
- push button to set some static text.

Model subclass: #SimpleModel
    instanceVariableNames: 'var'
initialize
    var := 'default var value'.
var: aVar
    var := aVar.

Shell subclass: #SimplePresenter
    instanceVariableNames: 'varPresenter varPresenter2'
createComponents
    super createComponents.
    varPresenter := self add: TextPresenter new name: 'textField'.
    varPresenter2 := self add: TextPresenter new name: 'textLabel'.
model: aSimpleModel
    super model: aSimpleModel.
    varPresenter model: (aSimpleModel aspectValue: #var).
    varPresenter2 model: (aSimpleModel aspectValue: #var)
aspectTriggersUpdates.
setVarCommand
    varPresenter value: ('New Value').
defaultModel
    ^SimpleModel new

When I run this example I was surprised that only text field was updated,
since I used presenter of the text field to update the value of the model.
Text label was not updated.

I made the following changes to enable automatic text label updates.
SimpleModel>>var: aVar
    var := aVar.
    self trigger: #varChanged "ADDED"
publishedEventsOfInstances
    "NEW METHOD: What is that?"
    ^super publishedEventsOfInstances
        add: #varChanged;
        yourself.

SimplePresenter>>model: aSimpleModel
    super model: aSimpleModel.
    varPresenter model: (aSimpleModel aspectValue: #var)
aspectTriggersUpdates. "CHANGED"
    varPresenter2 model: (aSimpleModel aspectValue: #var)
aspectTriggersUpdates. "CHANGED"
    varPresenter model aspectTriggers: #varChanged. "ADDED"
    varPresenter2 model aspectTriggers: #varChanged. "ADDED"

This worked.  It even updated label when I changed contents of the text
field and moved focus away from the text field.  That is what I expected to
see in the first version of the code.
I am confused.

If the following make any sense at all:

Why can't aspect of the simple model take care of the notification of two
presenters itself?

-----------------------
I am coming from MVC background in Java: create table model and get an
instance of it; create an instance of table view; set a model object for a
view.  Done.  Connect two views to a single model object: no problem.  If
forgot the details of how to use some component take a quick look at a
tutorial on sun website.


Reply | Threaded
Open this post in threaded view
|

Re: GUI components did not update themself

Ian Bartholomew-18
Sergei,

> Why can't aspect of the simple model take care of the notification of two
> presenters itself?

I think your message subject answered part of the reason.  Dolphin does not
consider your model as a "GUI" components as such - it is just a normal
object that happens to be being used in a GUI.  If every object
automatically triggered an event when its state changed, which you would
need to get the automatic notification you want, it would add a lot of
overhead and slow things down. It would also mean that every object would
expose part of it's inner workings, breaking encapsulation - one of the OO
guidelines.

[code snipped]

> When I run this example I was surprised that only text field was updated,
> since I used presenter of the text field to update the value of the model.
> Text label was not updated.
>
> I made the following changes to enable automatic text label updates.

[code snipped]

> This worked.  It even updated label when I changed contents of the text
> field and moved focus away from the text field.  That is what I expected
> to see in the first version of the code.

There was no need to do most of that.  The #aspectTriggersUpdates method is
now deprecated (i.e. you shouldn't use it for new code but it is still
temporarily provided for backward capability) so you should be using
#aspectTriggers: directly in your presenter.  You will still need to change
the #var method in your SimpleModel to trigger an event

SimpleModel>>var: aVar
    var := aVar.
    self trigger: #varChanged

but you only need to change one line in the #model method of your
SimplePresenter

SimplePresenter>>model: aSimpleModel
    super model: aSimpleModel.
    varPresenter model: (aSimpleModel aspectValue: #var).
    varPresenter2 model: ((aSimpleModel aspectValue: #var) aspectTriggers:
#varChanged)

I know it's only an example but I think your code is also slightly dangerous
in that it relies on a third party to change the value in the model.  I
would tend to code it like (just the changes)

SimplePresenter>>setVarChanged
    self model var: 'new value'

SimplePresenter>>model: aSimpleModel
    super model: aSimpleModel.
    varPresenter model: ((aSimpleModel aspectValue: #var) aspectTriggers:
#varChanged).
    varPresenter2 model: ((aSimpleModel aspectValue: #var) aspectTriggers:
#varChanged)

so that both presenters reflect the current value of the model.

Finally.  Did you realise you don't need a new model at all?.  You could
just use the model belonging to one of the TextEdits - all the trigger
wiring is then done for you.

SimplePresenter>>setVarChanged
    varPresenter model value: 'new value'

SimplePresenter>>model: aModel
    super model: aModel.
    varPresenter model: varPresenter2 model

There are lots of other ways of doing this, using a ValueModel to wrap the
instance variable for yourself for example, but I've probably confused you
enough already....

Regards
    Ian


Reply | Threaded
Open this post in threaded view
|

Re: GUI components did not update themself

Sergei Gnezdov
[snipped]
> There was no need to do most of that.  The #aspectTriggersUpdates method
is
> now deprecated (i.e. you shouldn't use it for new code but it is still
> temporarily provided for backward capability) so you should be using
> #aspectTriggers: directly in your presenter.  You will still need to
change

> the #var method in your SimpleModel to trigger an event
>
> SimpleModel>>var: aVar
>     var := aVar.
>     self trigger: #varChanged
>
> but you only need to change one line in the #model method of your
> SimplePresenter
>
> SimplePresenter>>model: aSimpleModel
>     super model: aSimpleModel.
>     varPresenter model: (aSimpleModel aspectValue: #var).
>     varPresenter2 model: ((aSimpleModel aspectValue: #var) aspectTriggers:
> #varChanged)

I followed your instructions.  Everything worked great.
I decided to sublclass not from Model, but from Object.  Code still worked
(different gui componens updated).
It does not seem that Model parent plays any role in this case.

When can I use Object instead of Model?

I provide here the code to reduce confusion:

Object subclass: #SimpleModel
    instanceVariableNames: 'var'
var: aVar
    var := aVar.
    self trigger: #varChanged

Shell subclass: #SimplePresenter
    instanceVariableNames: 'varPresenter varPresenter2'
createComponents
    super createComponents.
    varPresenter := self add: TextPresenter new name: 'textField'.
    varPresenter2 := self add: TextPresenter new name: 'textLabel'.
model: aSimpleModel
    super model: aSimpleModel.
    varPresenter model: (aSimpleModel aspectValue: #var).
    varPresenter2 model: ((aSimpleModel aspectValue: #var) aspectTriggers:
#varChanged).

[snipped]
> I know it's only an example but I think your code is also slightly
dangerous
> in that it relies on a third party to change the value in the model.  I
> would tend to code it like (just the changes)
[snipped]

Thank you.


Reply | Threaded
Open this post in threaded view
|

Re: GUI components did not update themself

Ian Bartholomew-18
Sergei,

> It does not seem that Model parent plays any role in this case.
> When can I use Object instead of Model?

You can always use Object instead of Model as a superclass.  Model is really
just a convenient place to add domain objects so they are a bit more
obvious, but it does also have a couple of code changes.

- #new automatically calls an instance side #initialize method
- As Model's will often trigger events (as part of MVP) the event collection
for Model subclasses is maintained in a more efficient way - in an instance
variable rather than in a Dictionary.  Triggering an event from a Model
subclass is therefore slightly faster than one from an Object subclass.

Regards
    Ian