ui factory - what should it do?

John Arbash Meinel john at arbash-meinel.com
Tue Feb 21 23:28:21 GMT 2006


Robert Collins wrote:
> The current ui factory is designed for the following uses:
> 
>  * To let library code be agnostic to the need for different uis. We
> ship two uis by default: a silent 'library-like' ui, and a 'interactive'
> text user interface.
>  * To remove the need for *every* function to have a parameter for the
> active ui elements. [many trivial functions call non trivial ones. So
> any function might end up doing slow things deep down.]
> 
> But there are issues:
>  * Thread wise we can have competing UI elements.
>  * New progress bars do not cooperate with existing progress bars. This
> may apply to other UI elements like user-input requests too.
> 
> 
> I think the claims that the ui factory are bogus are bogus ;). Its
> definately an anti-pattern to have such a pervasive thing being passed
> around everywhere. So heres a proposal for improving the ui factory...
> 
> Firstly lets assume that functions do not need to know their callers
> intent - they can present a progress bar anytime they choose too, ditto
> input dialogs etc.
> 
> For thread safety, we have a number of options. But lets qualify what we
> are aiming to achieve. Do we want a silent UI in thread 1, and a GUI in
> thread 2 with a text ui in thread 3? I think thats nuts and not
> something we should aim for supporting [but not deliberately prevent
> either]. For console applications, showing multiple threads of execution
> at once is a pain, and I'd expect a ncurses or other windowing facility
> to be in play rather than our default text ui. So having multiple
> factories is not needed IMO, but... once we qualify how progress
> interactions are meant to work across threads we may need some thread
> local storage for thread specific state. Or not. Lets see..
> 
> If I have a function foo calls function quux, then in a console app I
> might want a progress bar in quux to alter the appearance of the one foo
> is using:
> [=====/--         ] Total progress/Current action
> 
> In a GUI I might want something like:
> [=======          ] Total progress
> [=====            ] Current action
> 
> 
> This implies that the progress bar for quux is aware of that for foo in
> the console case, and that they can exist independently for a GUI. So
> our API could be something like
> pb = ui.ui_factory.progress_bar(parents_pb)
> which suggests that quux has a 'parents_pb' parameter.
> 
> Alternatively it could be:
> pb = ui.ui_factory.progress_bar()
> and the factory knows what the parent is.
> 
> 
> Now lets consider threads a little:
> Both those apis are thread-capable. The former by call-stack-state, the
> latter by threads-specific-state.
> 
> If we want foo and quux to be in *different* threads then there is an
> obvious difference, but that also implies that foo might have multiple
> quux's at once - so the individual quux-level pb's will *still* have to
> know about each other to interact properly - but a simple stack of pb's
> would be wrong, as would just-a-set, or thread specific stacks.
> 
> 
> So, I'm not at all convinced that passing pbs around in functions
> 'solves' thread safety issues. Its going to be complex any which way its
> tackled, and IF that comes up, I'd rather design to address it then.

But if you have to pass pbs around so that you can create a child
progress bar, then you are already passing around a progress bar.

Otherwise the ui_factory needs to know whether this call to
'ui_factory.progress_bar' is intended as a sub-progress to the current
one, or a separate process.

I implemented a Callback object for my code, which allows you to do
logging (which might pop up a dialog box), or do progress. It does get
passed around to all the code that might care about it. (Though most of
my objects just save a reference to it as self._cb so it doesn't have to
be passed around internally). But my code is image processing, and the
class is generally an obvious unit of processing. With bzr, we have a
lot of different layers, so it is less obvious what is going to need a
progress bar.

> 
> Until then, the simple factory we have now is thread-capable - anyone
> can write a ui that is thread aware and install it.
> 
> What I would like is nested-progress bars to be part of the api: for pb
> requests to automatically stack inside the factory and to be handed back
> by objects when they are finished with.
> 
> Rob

My suggestion for nested progress bars (having used it), is to have each
progress have a list of children. Then you end up with a tree, which can
be represented quite well in a GUI, and moderately approximated in text
mode.

Children need to inform their parents when they are done, so that the
parent can clean them up if it chooses.

My feeling is that we either need to pass progress bars around so that
we can create a tree (or in the simple case just a stack), or we have
ui_factory just assume that all progresses are either children of
eachother, or at the same level. If you don't want to expose to callers
that something slow might be going on deep down, then you have
difficulty exposing the layering as well.

I also prefer passing both a progress bar, and a callback message.
Because Weave.join() doesn't really care what weave it is working on.
But at a higher level you care that you are merging inventories right
now, and that makes a difference versus merging 400 other weaves.
(Specifically, you would like merging bulk weaves to print out what
weave it is on, but you would like merging inventory.weave to print out
what revision it is on).

Maybe this could be taken care of with global debouncing. (So you wait
to display a new progress for 1 second or so).

John
=:->


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 254 bytes
Desc: OpenPGP digital signature
Url : https://lists.ubuntu.com/archives/bazaar/attachments/20060221/5c27bd22/attachment.pgp 


More information about the bazaar mailing list