Idea: Simplified GTK

G

Gregory Millam

Received: Thu, 8 Apr 2004 06:04:18 +0900
with_context (bindings) {...} # push onto dynamic bindings stack
find_in_context(var) # lookup bindings stack from innermost to outer

Yup - I already replied to this in another email.
btw: I think some form of dynamic binding should be in the Ruby standard
libraries, just like resumable exceptions (whose implementation would
probably also use callcc/catch/throw).

Agreed. They're just so handy =).
 
G

Gregory Millam

Received: Thu, 8 Apr 2004 06:14:18 +0900
.... unless we named it ... style?

or ... with?
with (bindings) { ...}

yeah, with() { ... } is better.
automatically placed in the current context parent, like my @storefunc
earlier.
as per normal

The same approach works with "with_context". If a binding for :parent is not
found, simply skip the add_child bit (with the current with_context code
this would need a catch()). So I prefer to use a uniform technique.

Well I guess it makes sense to do it anyway - even if I wouldn't use it myself, it'd be trivial to add it.

One problem with the the whole context thing came into mind, though...

def button(label,&block)
b = Button.new
b.signal_attach("clicked",block)

if has_context:)color)
...
end
if has_context:)bgcolor)
...
end
if etcetcetc....... Gtk has what, dozens of configurable things? fonts, and the like.
end
end

That would be pretty much done for every single item in EzGtk. Making it considerably slower than:

button {
color "red"
bgcolor "blue"
}

And ... Don't Gtk+ children inherit attributes from their parents?

window {
color "red"
bgcolor "blue"
button { ... } # Button will be red/blue too!
}

OTOH, EzGtk would really only apply in the creation of widgets, not their runtime, so the only lag (depending on machine speed :D) would be seen when creating windows with lots of widgets.
 
A

Alexey Verkhovsky

Phil said:
Do others use state-machines for [driving the logic of rich user interfaces]?

Ain't know about Ruby [yet], but when writing thick GUIs in Java I use
state machines for anything bigger than a simple popup dialog.

Resulting code is somewhat more verbose, but comes out rather stable due
to very clean separation of buttons and logic.

In really complex situations, write a separate class for each state.
Transition from state to state is then a factory method in the
originating state, which creates an instance of the destination state,
and passes control there. Main loop (translated to Ruby) is something like:

state = InitialState.new
state = state.getNextState until state.final?

Best regards,
Alexey Verkhovsky
 
J

Jean-Hugues ROBERT

Not entirely true.

Hash uses #hash and #eql? to decide how to store entries. Some objects
(String) implement #hash, but by default Ruby uses #object_id for #hash:

Thanks for the clarification.
How about instead extending Strings you want stored uniquely to return
#object_id for #hash?

Would be OK if my IdentityHash were to contain strings only. But I
need to put arbitrary objects in it.

BTW: I need that to detect "true" circular paths when walking a tree/graph.
Rigth now I detect false circulars when nodes' values compare equal
where in fact it is two different node objects (with the same "value").

Thanks.

Jean-Hugues


 
J

Jean-Hugues ROBERT

At said:
btw: I think some form of dynamic binding should be in the Ruby standard
libraries, just like resumable exceptions (whose implementation would
probably also use callcc/catch/throw).

I definitely agree, everything seems to be ready there in the interpretor.
A caller() that would return some Binding would make the job feasible I
guess.

Jean-Hugues
 
G

George Ogata

Jean-Hugues ROBERT said:
BTW: I need that to detect "true" circular paths when walking a tree/graph.
Rigth now I detect false circulars when nodes' values compare equal
where in fact it is two different node objects (with the same "value").

Can you just hash the ids?

hash[obj.id] = blah
 
P

Phil Tomson

Phil said:
Do others use state-machines for [driving the logic of rich user interfaces]?

Ain't know about Ruby [yet], but when writing thick GUIs in Java I use
state machines for anything bigger than a simple popup dialog.

Resulting code is somewhat more verbose, but comes out rather stable due
to very clean separation of buttons and logic.

In really complex situations, write a separate class for each state.

I'm doing things this way. Each state has a do_action method that gets
called to do tasks and then transition to next state based on user input
and the results of the actions.

All of the state classes are singletons.
Transition from state to state is then a factory method in the
originating state, which creates an instance of the destination state,
and passes control there. Main loop (translated to Ruby) is something like:

state = InitialState.new
state = state.getNextState until state.final?

I've got a StateMachine class, here's most of it:

class StateMachine
include Observable
def initialize(state=InitialState)
@current_state = state
end

def handle_event(event_class)
@current_state.instance.do_action(event_class)
@current_state = @current_state.instance.next_state
changed
notify_observers(@current_state)
end

...
end

As long as the State classes define a do_action method and a next_state
accessor it's usable by this StateMachine class. Either the State class
sets up a default next_state or it sets it's @next_state attrib during the
call to do_action. I suppose I could just have do_action return the
next_state, as well, but I think the way I have it looks a bit clearer.


Another nice consequence of doing things this way (with a state
machine) is that you can set up
some unit tests that test your state_machine without needing to have the
GUI connected up. That way you can thoroughly test your logic without
needing to do a whole bunch of GUI testing to get at it. (of course you'll
still need to do final GUI testing, but perhaps not as much, besides, I'm
not sure how to setup unit tests for a GUI otherwise, I suspect there
are ways, but they're probably very specific to the toolkit being used
or you have to have a GUI testing tool).

Phil
 
H

Hal Fulton

[snippage]

Well, I value beauty over uniformity. I am not especially in favor of
using find_in_context and with_context and such, as it is just more
for my eyeballs to stumble over.

I think we may be wandering away from the realm of "simple" into the
realm of "merely different."

Is it really *that* bad to do additional tweaking with a separate method
call?


Hal
 
J

Jean-Hugues ROBERT

Jean-Hugues ROBERT said:
BTW: I need that to detect "true" circular paths when walking a tree/graph.
Rigth now I detect false circulars when nodes' values compare equal
where in fact it is two different node objects (with the same "value").

Can you just hash the ids?

hash[obj.id] = blah
Yes !
So: an IdentityHash in ruby is just a Hash, but
with obj.object_id() in it vs the obj itself.
Makes a lot of sense.
Thanks George !

Jean-Hugues
PS: As one said, in Computer Science everything is solvable by
adding one indirection level.
 
H

Hal Fulton

Its said:
No, it isn't. It's just that I don't see
(1) button "Label" {
block
}
as that much better than
(s) button {
label "Label"
on_click { block }
}

For one thing (and this is a minor point) I like to make things a single
line whenever I can (without using semicolons).

So I'd rather do: button("Label") { call_me }

It's fewer characters, tokens, and lines this way.

Maybe we could allow both. I'm a believer (though some are not) in a
little "magic" parameter handling.

In other words, we could code it so as to permit calling it either way.

For example, when would a button ever be labelless? Default it to nil,
and if no label is specified, it's the second kind of call.
Is the bigger question though: how to best scale with no (or minimal) change
to incorporate all the wonderful widgets that might exist?

Hmm, I think every container would work almost the same way.

Why don't we take a moderately complex example program and implement
enough to get it to work?

Maybe something from http://ruby-gnome2.sourceforge.net/tut/toc.htm ?

Hal
 
G

Gregory Millam

Received: Thu, 8 Apr 2004 15:18:53 +0900
Maybe we could allow both. I'm a believer (though some are not) in a
little "magic" parameter handling.

In other words, we could code it so as to permit calling it either way.

This is, of course, doable, but I don't think this kind of "overloading" would be easily readable or understandable when learning ezgtk. I'm more in favor of shortcuts like:

button {
label "foo"
onclick {...}
}

button_onclick "foo" {
...
}

Maybe an easier name like:

ezbutton "foo" {
...
}

for most common action type things?

- Greg Millam
 
H

Hal Fulton

Gregory said:
Received: Thu, 8 Apr 2004 15:18:53 +0900
And lo, Hal wrote:



This is, of course, doable, but I don't think this kind of "overloading"
would be easily readable or understandable when learning ezgtk.

To me, it's like gsub with/without a block, ranges or regexes
in String#[], and so on. TMTOWTDI.

I don't wish to be unreasonable or be "not a team player" -- assuming
we actually do anything with this -- but if it doesn't at least *permit*
coding in some style that I like, then my motivation to help work on it
is diminished.
I'm more
in favor of shortcuts like:

button {
label "foo"
onclick {...}
}

The Tk-ish look. That's ok for complex cases.
button_onclick "foo" {
...
}

Hmm, I disapprove. It's like making a name out of what should be a name
and some kind of qualifier.
Maybe an easier name like:

ezbutton "foo" {
...
}

Why would ezbutton be better than button here?

I'm still leaning toward writing it in such a way that we can each
choose our styles.

To reiterate, I'd like the simplest case to look like this:

@b1 = button "Label" { call_me }

with the possibility of modifying a created widget:

@b1.setup do
border 10
blah blah
whatever 123
on_enter { do_this }
end

though I'm not picky about the name "setup" -- I considered others
such as: do, change, modify, etc.

And I do think it's reasonable to allow the other form also:

button do
label "Label"
this "that"
on_click { do_something }
blah foo
end

But let's talk... only trouble is I'm very tired right now, as it's
2:40 am here.


Cheers,
Hal
 
G

Gregory Millam

Received: Thu, 8 Apr 2004 16:40:32 +0900
To me, it's like gsub with/without a block, ranges or regexes
in String#[], and so on. TMTOWTDI.

Those are with or without blocks. What I'm arguing here is that it acts differently depending on wether or not a string argument is also passed. And that determines if the block is called now or on an event.

What if someone tried:

button "foo" {
on_click { ... }
on_mouseover { ... }
}
I don't wish to be unreasonable or be "not a team player" -- assuming
we actually do anything with this -- but if it doesn't at least *permit*
coding in some style that I like, then my motivation to help work on it
is diminished.

I think we all feel that way - only problem is we all like different styles, and there's no one, 'obvious' way.
Why would ezbutton be better than button here?

Because of the reason I gave above - I'm trying to differentiate between blocks executed now or later.
with the possibility of modifying a created widget:

@b1.setup do
border 10
blah blah
whatever 123
on_enter { do_this }
end

though I'm not picky about the name "setup" -- I considered others
such as: do, change, modify, etc.

If EzGtk functions return Gtk objects, this isn't really doable without either modifying Gtk classes or making a host of EzGtk.

On the other hand ... I remember some sort of thread a long while back regarding changing contexts - can't remember if anything came of it. But if it was easily implemented, it'd allow us to modify the gtk widgets in their own context.

vb = vbox {...}
vb.in_context do
set_width 100 # Gtk here, not EzGtk
set_color "red" # shortcut for vb.set_color
end

4:26 am here. But then, I woke up at midnight.

- Greg Millam
 
G

Gregory Millam

Gaah! I've been working on an implementation of EzGtk and I came across this problem:

What's a good way to specify packing options using EzGtk style? - Or for that matter, alignment?

Mostly came up during this

vbox {
expand false # don't expand and fill, give other widgets priority
menubar {
menu "File" {
menuitem "Open" { ... }
...
}
align_right
menu "Help" {
...
}
}
expand true # Widgets after this get all the extra space
scintilla { ... }
}

Thoughts? ideas?

(Regarding the 'scintilla' bit: I wrote a ruby-gtk2 binding for scintilla last December (for gtk2, as opposed to fxscintilla). I just haven't bothered finishing it up yet, or figured out how get extconf working to make it releasable. It's still highly usable - the only things not implemented are those that involve styles (where a string is a set of 16 bit "characters" - one character byte, one style byte))

- Greg Millam
 
N

Nathaniel Talbott

If I were you, I'd put together a working example (not too complex,
not too simple) and write an article about it. Bet Dr Dobbs would
go for it.

Well, I'm not him, but I'll go ahead and do a little "show and tell"
here... I recently worked on an application with a Fox GUI, and in the
process I wrapped some code around Fox to make GUI creation easier.
Part of my thinking when I built it was that there are two things we do
when we set up a GUI, and it would be cleaner if we did them as two
steps instead of mashing them together. Those two steps are setting up
the components relationships to one another, and setting up the
component's properties, event handlers, etc. So I ended up with code
that looks like this:

class LoginDialog
include FXUtil

attr_reader :credentials

def initialize(app)
super()
create(app)
end

def create(app)
template(app) do
dialog("Login"){vertical{
matrix{label:)please_log_in); empty
label("User name:"); text_field:)username)
label("Password:"); text_field:)password)}
horizontal:)buttons){button:)ok); button:)cancel)}}}
end

setup :please_log_in do |w|
w.text = "Please log in"
w.bold = true
end

setup :username do |w|
w.columns_visible = 20
end

setup :password do |w|
w.options |= TEXTFIELD_PASSWD
w.columns_visible = 20
w.on_keypress do |source, selector, event|
if(event.text == "\r")
ok(source)
else
false
end
end
end

setup :buttons do |w|
w.options = LAYOUT_CENTER_X
end

setup :eek:k do |w|
w.text = "&OK"
w.options |= BUTTON_DEFAULT|BUTTON_INITIAL
w.on_click{|source, s, e| ok(source)}
end

setup :cancel do |w|
w.text = "&Cancel"
w.on_click{|source, s, e| cancel(source)}
end

build
end

def ok(source)
@credentials = [widget:)username).text, widget:)password).text]
widget.handle(source, MKUINT(FXDialogBox::ID_ACCEPT,
SEL_COMMAND), nil)
end

def cancel(source)
widget.handle(source, MKUINT(FXDialogBox::ID_CANCEL,
SEL_COMMAND), nil)
end

def execute
widget:)username).setFocus
(widget.execute(PLACEMENT_SCREEN) == 1) ? true : false
end
end

Currently 'FXUtil' is in no way ready for prime time, but hopefully
this code (which is presently live production code) illustrates one
better way that I found of building GUIs.

Just my $0.02,


Nathaniel
Terralien, Inc.

<:((><
 
J

Joel VanderWerf

Its said:
The current discussion Hal started is quite good. But it is focused on one
part of the problem e.g. it does not directly address the potentially
complicated problem of hooking together UI, dialog state (enabling/disabling
etc), and domain objects, keeping all in sync.

At the risk of broadening things ... I am partial to Joel BanderWerf's work
on FoxTails for FxRuby. The biggest things that comes to mind are:

- a great Observable: you don't observe an object, you observe an Attribute
of an object
- a nice way to attach any object (not just strings) to widgets like a
ComboBox or TextField
- a nice way to extensible events e.g. "when selection changes" and "file
chosen"

The Hal/Chad/Gregory style of "vbox { button {}; button {}; ...}" is on
FoxTails 0.1 to-do list. Seems like we might complement nicely.

Joel, care to comment / join in?

I've been following the discussion with interest, but probably won't
really digest it until the weekend.

The "specification language" style of GUI programming is very tempting,
but OTOH the Fox API is pretty good already, so that's one reason why
it's still on my to-do list :)

Maybe the possibility of abstracting from the particulars of the GUI
toolkit is the point that will tip the scales towards this specification
approach.

I'm also interested in the explicit state machine idea that came up
elsewhere in the this thread, and the possibility of integrating it into
FoxTails. Observable attrs can already be thought of as synchronization
of state machines, but the state is just a bunch of variables. Sometimes
it is useful to have an explicit notion of state in the sense of
"discrete mode" and explicit transition rules, entry/exit actions, etc.
Expressing state in this way, if possible, may be clearer than just
using some related variables and observer code.
 
G

Gregory Millam

Received: Fri, 9 Apr 2004 03:44:16 +0900
Hmmm. From platform-independent toolkit to toolkit-independent platform? If
doable, it would make a lot of very protracted and agonizing decisions about
which toolkit much simpler for a whole bunch of poor saps like me.

I like that idea.

..

And I just wrote a mini editor in EzGtk :-/. I don't know much about the Fox or TK bindings, but if they provide boxes a la gtk's hbox and vbox, or something mimicing that, it should be easy to port my EzGtk to ... oh, "EzGui" and just use

EzGui.init(EzGui::Gtk)
EzGui.init(EzGui::Fox) ... or whatever.
 
P

Phil Tomson

If I were you, I'd put together a working example (not too complex,
not too simple) and write an article about it. Bet Dr Dobbs would
go for it.

Well, I'm not him, but I'll go ahead and do a little "show and tell"
here... I recently worked on an application with a Fox GUI, and in the
process I wrapped some code around Fox to make GUI creation easier.
Part of my thinking when I built it was that there are two things we do
when we set up a GUI, and it would be cleaner if we did them as two
steps instead of mashing them together. Those two steps are setting up
the components relationships to one another, and setting up the
component's properties, event handlers, etc. So I ended up with code
that looks like this:

class LoginDialog
include FXUtil

attr_reader :credentials

def initialize(app)
super()
create(app)
end

def create(app)
template(app) do
dialog("Login"){vertical{
matrix{label:)please_log_in); empty
label("User name:"); text_field:)username)
label("Password:"); text_field:)password)}
horizontal:)buttons){button:)ok); button:)cancel)}}}
end

setup :please_log_in do |w|
w.text = "Please log in"
w.bold = true
end

setup :username do |w|
w.columns_visible = 20
end

setup :password do |w|
w.options |= TEXTFIELD_PASSWD
w.columns_visible = 20
w.on_keypress do |source, selector, event|
if(event.text == "\r")
ok(source)
else
false
end
end
end

setup :buttons do |w|
w.options = LAYOUT_CENTER_X
end

setup :eek:k do |w|
w.text = "&OK"
w.options |= BUTTON_DEFAULT|BUTTON_INITIAL
w.on_click{|source, s, e| ok(source)}
end

setup :cancel do |w|
w.text = "&Cancel"
w.on_click{|source, s, e| cancel(source)}
end

build
end

def ok(source)
@credentials = [widget:)username).text, widget:)password).text]
widget.handle(source, MKUINT(FXDialogBox::ID_ACCEPT,
SEL_COMMAND), nil)
end

def cancel(source)
widget.handle(source, MKUINT(FXDialogBox::ID_CANCEL,
SEL_COMMAND), nil)
end

def execute
widget:)username).setFocus
(widget.execute(PLACEMENT_SCREEN) == 1) ? true : false
end
end

Currently 'FXUtil' is in no way ready for prime time, but hopefully
this code (which is presently live production code) illustrates one
better way that I found of building GUIs.

Interesting. I'd be interested in seeing your implementation of setup and
template. Would that be possible? Wouldn't have to see everything.

Phil
 
P

Phil Tomson

Its Me wrote:

I've been following the discussion with interest, but probably won't
really digest it until the weekend.

The "specification language" style of GUI programming is very tempting,
but OTOH the Fox API is pretty good already, so that's one reason why
it's still on my to-do list :)

Maybe the possibility of abstracting from the particulars of the GUI
toolkit is the point that will tip the scales towards this specification
approach.

Toolkit-neutral GUI code would be cool.
I'm also interested in the explicit state machine idea that came up
elsewhere in the this thread, and the possibility of integrating it into
FoxTails. Observable attrs can already be thought of as synchronization
of state machines, but the state is just a bunch of variables. Sometimes
it is useful to have an explicit notion of state in the sense of
"discrete mode" and explicit transition rules, entry/exit actions, etc.
Expressing state in this way, if possible, may be clearer than just
using some related variables and observer code.

I define a class for each state in the machine (and I 'include
Singleton' in their superclass, State ). The
transitions/actions/etc are contained in the state classes. Each state
class has a 'do_action(event)' method, that looks something like:

class LoginState < State
def do_action(event_class)
if event_class == NextEvent #Next button or 'return' key in console

begin
status = do_login(@@ui_obj.email,@@ui_obj.serial_number)
rescue Errno::ECONNREFUSED
@next_state = ConnectionErrorState
@@ui_obj.display_message("Could not connect to site")
return
end
#figure out which state to goto next
case status
when NewUser
@next_state = VerifyEmailState
when DuplicateSN
@@ui_obj.display_message("Duplicate Serial Number; please
re-enter")
@next_state = LoginState
when...
...
end
end
end

NOTE: @@ui_obj points to a proxy class that acts as a 'translator' between
the state machine and the actual GUI code. It should make
it easier to use different toolkits.

The StateMachine class itself is Observable, not the states themselves in
this implementation. Sounds kind of like what you're describing.

Phil
 
N

Nathaniel Talbott

Interesting. I'd be interested in seeing your implementation of setup
and
template. Would that be possible? Wouldn't have to see everything.

Well, you're certainly welcome to see it, though I'm not sure how much
it will help you:


def template(parent)
raise "You must call super in initialize!"
unless(defined?(@widgets))
@real_parent = parent
yield
end

def setup(name, &block)
raise "Invalid widget name #{name}" unless(@widgets[name])
@widgets[name].setup(&block)
end

One thing that is key to the implementation is that I don't manipulate
the real Fox widgets... instead, I wrap each one that I use and the
final widgets only get built when I call build at the end of the create
method. Here's the implementation for one widget wrapper:

class TextField < Widget
widget_attr :columns_visible => 1
widget_attr :eek:ptions => TEXTFIELD_NORMAL|LAYOUT_FILL_X

widget_event :eek:n_change, SEL_CHANGED
widget_event :eek:n_keypress, SEL_KEYPRESS

def do_build(real_parent)
FXTextField::new(real_parent, @columns_visible, nil, 0,
@options)
end
end

Probably all I've really done is to tease you more... at some point I
would like to release the utility, but I'm not doing Ruby much right
now (new Java contract) so it may be a little while.

HTH,


Nathaniel
Terralien, Inc.

<:((><
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,127
Latest member
CyberDefense
Top