State Machines and User Interface work -- any examples/experience?
Asked Answered
E

10

32

I'm looking for ways to de-spaghttify my front-end widget code. It's been suggested that a Finite State Machine is the right way to think about what I'm doing. I know a State Machine paradigm can be applied to almost any problem. I'm wondering if there are some experienced UI programmers who actually make a habit of this.

So, the question is -- do any of you UI programmers think in terms of State Machines in your work? If so, how?

thanks, -Morgan

Ebony answered 27/2, 2009 at 15:33 Comment(4)
It ain't me, but lately folks here seem to think think that all StackOverflow questions have Wikipedia-like importance. So if you typoed, or messed up, they take it upon themselves to fix it for you. Very helpful, yes? Yes...Evonevonne
Jeff Atwood "very strongly discourages" the use of signatures, greetings, and salutations, so some of the moderators have taken up the torch and remove such superfluous information.Sensation
@Randolpho: That's part of the design of this place. Personally, I feel it's appropriate to leave both a good edit note, and a comment, but that's me...Farleigh
Here: #468803Sensation
P
31

I'm currently working with a (proprietary) framework that lends itself well to the UI-as-state-machine paradigm, and it can definitely reduce (but not eliminate) the problems with complex and unforeseen interactions between UI elements.

The main benefit is that it allows you to think at a higher level of abstraction, at a higher granularity. Instead of thinking "If button A is pressed then combobox B is locked, textfield C is cleared and and Button D is unlocked", you think "Pressing button A puts the app into the CHECKED state" - and entering that state means that certain things happen.

I don't think it's useful (or even possible) to model the entire UI as a single state machine, though. Instead, there's usually a number of smaller state machines that each handles one part of the UI (consisting of several controls that interact and belong together conceptually), and one (maybe more than one) "global" state machine that handles more fundamental issues.

Pretty answered 27/2, 2009 at 16:7 Comment(5)
Great response. Thanks Michael.Ebony
@Michael Borgwardt Could you tell me, please, what is the framework you written about. Thanks a lot.Mccullum
@Ilan: as I wrote, it was proprietary, created by a bank for internal use. It's not open source, and I don't think you can buy it either.Pretty
@Michael Borgwardt Ah, ok. Do you know occasionally any framework either open source or commercial? Thank you!Mccullum
@Ilan: the concept is easy to implement in any framework that implements sufficiently powerful data binding (one that does is Knockout: knockoutjs.com/documentation/computedObservables.html ). The idea is that you have a model property "state" which depends on (i.e. is computed from) a set of other properties, and when it changes then another set of properties is changed depending on the new state.Pretty
S
10

State machines are generally too low-level to help you think about a user interface. They make a nice implementation choice for a UI toolkit, but there are just too many states and transitions to describe in a normal application for you to describe them by hand.

I like to think about UIs with continuations. (Google it -- the term is specific enough that you will get a lot of high quality hits.)

Instead of my apps being in various states represented by status flags and modes, I use continuations to control what the app does next. It's easiest to explain with an example. Say you want to popup a confirmation dialog before sending an email. Step 1 builds an email. Step 2 gets the confirmation. Step 3 sends the email. Most UI toolkits require you to pass control back to an event loop after each step which makes this really ugly if you try to represent it with a state machine. With continuations, you don't think in terms of the steps the toolkit forces upon you -- it's all one process of building and sending an email. However, when the process needs the confirmation, you capture the state of your app in a continuation and hand that continuation to the OK button on the confirmation dialog. When OK is pressed, your app continues from where it was.

Continuations are relatively rare in programming languages, but luckily you can get sort of a poor man's version using closures. Going back to the email sending example, at the point you need to get the confirmation you write the rest of the process as a closure and then hand that closure to the OK button. Closures are sort of like anonymous nested subroutines that remember the values of all your local variables the next time they are called.

Hopefully this gives you some new directions to think about. I'll try to come back later with real code to show you how it works.

Update: Here's a complete example with Qt in Ruby. The interesting parts are in ConfirmationButton and MailButton. I'm not a Qt or Ruby expert so I'd appreciate any improvements you all can offer.

require 'Qt4'

class ConfirmationWindow < Qt::Widget
  def initialize(question, to_do_next)
    super()

    label = Qt::Label.new(question)
    ok = ConfirmationButton.new("OK")
    ok.to_do_next = to_do_next
    cancel = Qt::PushButton.new("Cancel")

    Qt::Object::connect(ok, SIGNAL('clicked()'), ok, SLOT('confirmAction()'))
    Qt::Object::connect(ok, SIGNAL('clicked()'), self, SLOT('close()'))
    Qt::Object::connect(cancel, SIGNAL('clicked()'), self, SLOT('close()'))

    box = Qt::HBoxLayout.new()
    box.addWidget(label)
    box.addWidget(ok)
    box.addWidget(cancel)

    setLayout(box)
  end
end

class ConfirmationButton < Qt::PushButton
  slots 'confirmAction()'
  attr_accessor :to_do_next
  def confirmAction()
    @to_do_next.call()
  end
end

class MailButton < Qt::PushButton
  slots 'sendMail()'
  def sendMail()
    lucky = rand().to_s()
    message = "hello world. here's your lucky number: " + lucky
    do_next = lambda {
      # Everything in this block will be delayed until the
      # the confirmation button is clicked. All the local
      # variables calculated earlier in this method will retain
      # their values.
      print "sending mail: " + message + "\n"
    }
    popup = ConfirmationWindow.new("Really send " + lucky + "?", do_next)
    popup.show()
  end
end

app = Qt::Application.new(ARGV)

window = Qt::Widget.new()
send_mail = MailButton.new("Send Mail")
quit = Qt::PushButton.new("Quit")

Qt::Object::connect(send_mail, SIGNAL('clicked()'), send_mail, SLOT('sendMail()'))
Qt::Object::connect(quit, SIGNAL('clicked()'), app, SLOT('quit()'))

box = Qt::VBoxLayout.new(window)
box.addWidget(send_mail)
box.addWidget(quit)

window.setLayout(box)
window.show()
app.exec()
Soviet answered 27/2, 2009 at 17:14 Comment(1)
Very interesting. I'll follow your google recommendation for sure.Ebony
P
9

It's not the UI that needs to be modeled as a state machine; it's the objects being displayed that it can be helpful to model as state machines. Your UI then becomes (oversimplification) a bunch of event handlers for change-of-state in the various objects.

It's a change from:

DoSomethingToTheFooObject();  
UpdateDisplay1();  // which is the main display for the Foo object  
UpdateDisplay2();  // which has a label showing the Foo's width,
                   // which may have changed  
...  

to:

Foo.DoSomething();  

void OnFooWidthChanged() { UpdateDisplay2(); }  
void OnFooPaletteChanged() { UpdateDisplay1(); }  

Thinking about what changes in the data you are displaying should cause what repainting can be clarifying, both from the client UI side and the server Foo side.

If you find that, of the 100 UI thingies that may need to be repainted when Foo's state changes, all of them have to be redrawn when the palette changes, but only 10 when the width changes, it might suggest something about what events/state changes Foo should be signaling. If you find that you have an large event handler OnFooStateChanged() that checks through a number of Foo's properties to see what has changed, in an attempt to minimize UI updates, it suggests something about the granularity of Foo's event model. If you find you want to write a little standalone UI widget you can use in multiple places in your UI, but that it needs to know when Foo changes and you don't want to include all the code that Foo's implementation brings with it, it suggests something about the organization of you data relative to your UI, where you are using classes vs interfaces, etc.... Fundamentally, it makes you think more seriously about what is your presentation layer, more seriously than "all the code in my form classes".

-PC

Polyphony answered 27/2, 2009 at 16:45 Comment(1)
Back in the day, we called this MVC. That was before Rails destroyed the term.Brom
P
7

There is a book out there about this topic. Sadly its out of print and the rare used ones available are very expensive.

Constructing the User Interface with Statecharts
by Ian Horrocks, Addison-Wesley, 1998
Preemie answered 1/7, 2009 at 8:50 Comment(2)
The book is up on ScribdSuperintendent
The Scribd upload is just taken from bookzz.org (which has no reg well).Subak
P
5

We were just talking about Horrocks' Constructing the User Interface with Statecharts, prices 2nd-hand range from $250 up to nearly $700. Our software development manager rates it as one of the most important books he's got (sadly, he lives on the other side of the world).

Samek's books on statecharts draw significantly from this work although in a slightly different domain and reportedly not as clear. "Practical UML Statecharts in C/C++Event-Driven Programming for Embedded Systems" is also available on Safari.

Horrocks is cited quite heavily - there are twenty papers on the ACM Portal so if you have access there you might find something useful.

There's a book and software FlashMX for Interactive Simulation. They have a PDF sample chapter on statecharts.

Objects, Components, and Frameworks with UML: The Catalysis(SM) Approach has a chapter on Behaviour Models which includes about ten pages of useful examples of using statecharts (I note that it is available very cheaply second hand). It is rather formal and heavy going but that section is easy reading.

Payne answered 20/7, 2011 at 14:45 Comment(1)
Horrock's book is up on ScribdSuperintendent
S
3

Its not really a UI problem, to be honest.

I'd do the following:

  1. Define your states
  2. Define your transiations - which states are accessible from which others?
  3. How are these transitions triggered? What are the events?
  4. Write your state machine - store the current state, receive events, and if that event can cause a valid transition from the current state then change the state accordingly.
Svensen answered 27/2, 2009 at 15:44 Comment(4)
Thanks. The question is not "what is a state machine", or "what would you do", but "what have you done".Ebony
@[morgancodes]: perhaps you need to rephrase your question instead of downvoting attempts to be helpfulCupel
Indeed. What I have sugegsted is what I have done, on several occasions. Why wouldnt I follow my own advice?Svensen
@Visage Sorry, I misunderstood, and thought you were speaking hypothetically rather than from experience. I have steadied my downvote trigger finger.Ebony
C
3

I got a prezi-presentation about a pattern that I call "State First".

It is a combination of MPV/IoC/FSM and I've used it successfully in .Net/WinForms, .Net/Silverlight and Flex (at the moment).

You start by coding your FSM:

class FSM
    IViewFactory ViewFactory;
    IModelFactory ModelFactory;
    Container Container; // e.g. a StackPanel in SL
    ctor((viewFactory,modelFactory,container) {
        ...assignments...
        start();
    }

    start() {
        var view = ViewFactory.Start();
        var model = ModelFactory.Start();
        view.Context = model;
        view.Login += (s,e) => {
            var loginResult = model.TryLogin(); // vm contains username/password now
            if(loginResult.Error) {
                // show error?
            } else {
                loggedIn(loginResult.UserModel); // jump to loggedIn-state
            }
        };
        show(view);
    }


    loggedIn(UserModel model) {
        var view = ViewFactory.LoggedIn();
        view.Context = model;
        view.Logout += (s,e) => {
            start(); // jump to start
        };
        show(view);
    }

Next up you create your IViewFactory and IModelFactory (your FSM makes it easy to see what you need)

public interface IViewFactory {
    IStartView Start();
    ILoggedInView LoggedIn();
}

public interface IModelFactory {
    IStartModel Start();
}

Now all you need to do is implement IViewFactory, IModelFactory, IStartView, ILoggedInView and the models. The advantage here is that you can see all transitions in the FSM, you get über-low coupling between the views/models, high testability and (if your language permits) a great deal of type safely.

One important point in using the FSM is that your shouldn't just jump between the states - you should also carry all stateful data with you in the jump (as arguments, see loggedIn above). This will help you avoid global states that usually litter gui-code.

You can watch the presentation at http://prezi.com/bqcr5nhcdhqu/ but it contains no code examples at the moment.

Cori answered 1/10, 2009 at 7:18 Comment(1)
Very interesting idea!Basion
S
2

Each interface item that's presented to the user can go to another state from the current one. You basically need to create a map of what button can lead to what other state.

This mapping will allow you to see unused states or ones where multiple buttons or paths can lead to the same state and no others (ones that can be combined).

Saltire answered 27/2, 2009 at 15:37 Comment(4)
Thanks. The question is not "what is a state machine", or "what would you do", but "what have you done".Ebony
@[morgancodes]: downvoting attempts to be helpful does not encourage others to help you.Cupel
@[Steve A. Lowe]Didn't mean to be discouraging, just wanted to clarify the question. In what situations is downvoting the right move?Ebony
downvoting is the right move when the answer is off-topic or inappropriate. It it meant to be used sparingly. Answers that need revision should be commented instead.Richia
H
2

Hey Morgan, we're building a custom framework in AS3 here at Radical and use the state machine paradigm to power any front end UI activity.

We have a state machine setup for all button events, all display events and more.

AS3, being an event driven language, makes this a very attractive option.

When certain events are caught, states of buttons / display objects are automatically changed.

Having a generalized set of states could definitely help de-spaghttify your code!

Hilaire answered 27/2, 2009 at 16:12 Comment(2)
Thanks Praveen. You seen any results yet? Has it made your life easier, or too early to tell?Ebony
Yes! Its made my life easier. Using STATES for UI elements in the framework has reduced almost 50 - 100 lines of code on my end per template class.Hilaire
S
1

A state machine is something that allows code to work with other state machines. A state machine is simply logic that has memory of past events.

Therefore humans are state machines, and often they expect their software to remember what they've done in the past so that they can proceed.

For instance, you can put the entire survey on one page, but people are more comfortable with multiple smaller pages of questions. Same with user registrations.

So state machine have a lot of applicability to user interfaces.

They should be understood before being deployed, though, and the entire design must be complete before code is written - state machine can, are, and will be abused, and if you don't have a very clear idea of why you're using one, and what the goal is, you may end up worse off than other techniques.

-Adam

Sensation answered 27/2, 2009 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.