shoes / shoes3

a tiny graphical app kit for ruby
http://walkabout.mvmanila.com
Other
181 stars 19 forks source link

Cannot access attribute from window block #233

Closed Dassadar closed 8 years ago

Dassadar commented 8 years ago

Hello,

Here is my code:

Shoes.app do
    window title:"test", width: 400, height: 120 do
        @a = "2"
    end
    alert "/#{@a}/"
end

I would like the alert to display /2/ and not //: what's wrong please?

Regards, David

ccoupe commented 8 years ago

This one does need a start{} - @a is not defined when alert is started. The Window do is not finished before alert runs.

ccoupe commented 8 years ago

There is a very important Shoes concept here that trips up everyone. If your Window do.. end was replaced with a button do..end then you see immediately that the @a is not defined because you didn't click the button. All Shoes GUI blocks {} or do..end will delay 'block' execution, even calls that you wouldn't think of like window because it can't proceed until the new window is on the screen, meanwhile alert has no block{} to wait on so it gets the current value of @a

Shoes.app do
   @a = 'default'
    window title:"test", width: 400, height: 120 do
        @a = "2"
    end
    alert "/#{@a}/"
end

This actually a feature, not a bug and it bites every one who uses alerts in the Shoes.app block. It can take seconds or days before that light bulb goes bright.

Dassadar commented 8 years ago

Ok, still I don't get it in my real case which is a bit more context. In my window block, I am declaring a list_box (let's call it @a) with a list of choices. My windows does appear with the list_box, so @a is well defined and not nil.

Later on, I am refreshing the display (upon a click somewhere on the main window), and referring to @a to update the list of choices ==> @a is undefined, why ? :-/ And if not a bug, what is the correct way to refer to an object within a window block, outside this window block?

ccoupe commented 8 years ago

You want to set a widget's content or control it from one Shoes.app in another Shoes.app? (Window) Not as easy as you hoped for. https://github.com/Shoes3/shoes3/wiki/Poking-in-Shoes.app is an attempt to explain that mystery. @passenger94 , thinks I've made an error or two in that description but basically '@a' in Window1 may be different from '@a' in Window2. - you can get and compare object id's for both '@a' in each window - are they the same object? I suspect they are not.

Setting up an instance_eval from window 1 to an object in window 2 or vice versa requires clever meta programming and much head scratching. The Shoe manual and design explicitly states that each Shoes.app is independent . For example thats how the manual can run code snippets without crashing Shoes. For better or worse, that initial design decision is unlikely to change. Perhaps you can set up a observer/listener network to propagate changes between windows to maintain the illusion of a central MVC data store.

passenger94 commented 8 years ago

There is something else involved here besides the delay : at the time you invoke alert, @a is not yet defined and :
The window method is launching a new App and every App instance is like it's own closed world, whatever is defined inside the window block is unknown to the outside world. Even though the window method was called inside the app block ! There is the owner method on every App instance for those situation, inside the window block (the "test" app) owner refers to the outside App, let's call it "main"

Shoes.app title: "main" do
    @a = 'default'
    button("check @a") {alert "/#{@a}/"}

    window title:"test", width: 400, height: 120 do
        @a = "2"
        owner.set_a(@a)
    end

    def set_a(val)
        @a = val
    end

    alert "/#{@a}/"

end
Dassadar commented 8 years ago

Ok thx for the explanations, that's clearer to me now. To implement an observer/listener mechanism however, I need to "talk" to the window block - how can I do that? If I am adding a method within the window block, can it be seen from outside? If so, how can I call it? Sorry for all these newbie questions ;-)

passenger94 commented 8 years ago

@a inside window is a totally different beast than the @a inside "main" you have to notify "main" by any means, observer for example, about the changes The alert you launch from the button knows about the changes, but not the first one.

Dassadar commented 8 years ago

Should it work the other way round? Something like:

Shoes.app title: "main" do
    @a = 'default'
    button("check @a") {alert "/#{@a}/"}

    myWin = window title:"test", width: 400, height: 120 do
        @a = 2

        def setValue(val)
            @a = val
        end
    end
    myWin.setValue(3)
end
passenger94 commented 8 years ago

haha, would be cool ! but i'm afraid not without going into metaprogramming ... i didn’t try though At least we can say, not an easy, obvious way to do it like that !

passenger94 commented 8 years ago

This works !

Shoes.app title: "main" do
    @a = 'default'
    button("check @a") {alert "/#{@a}/"}
    button("call my_met on test_app") {alert @test_app.my_met}

    @test_app = window title:"test", width: 400, height: 120 do
        @a = "2"
        owner.set_a(@a)
        def my_met
            "my_met called"
        end
    end

    def set_a(val)
        @a = val
    end

    alert "/#{@a}/"

end
Dassadar commented 8 years ago

So the question is open :-)

My app will not work if all windows are not refreshed when a selection is done on one of them - else some unauthorized values in the list_box may be selected...

Hoping this metaprogramming for the master to talk to the child window can be done in a not-to-heavy way!

Le 2016-03-31 10:07, passenger94 a écrit :

haha, would be cool ! but i'm afraid not without going into metaprogramming ... i didn't try though At least we can say, not an easy, obvious way to do it like that !

You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub [1]

Links:

[1] https://github.com/Shoes3/shoes3/issues/233#issuecomment-203807742

ccoupe commented 8 years ago

My Ytm demo uses $vars for it's data store nstead of @vars for just this reason but that has strange side effects which is why it never moved past 'demo'. But I'm lazy and a bad programmer. However one $var which is a reference to a new Ruby class that holds the data for all views and has methods to add observers and notify them would not offend the ruby design gods, much. If that class also encapsulated your sql access (my assumption) then you could propagate changes to the db/class to the listeners.

Dassadar commented 8 years ago

Great passenger94, thanks!

passenger94 commented 8 years ago

Oh yes, using globals should work too !

Dassadar commented 8 years ago

I've just tested it on my apps (method within the window block called by outside block) --> just works perfectly! :-) ... and better than globals ;-)