Button background as URL image

1,314 views
Skip to first unread message

garrett

unread,
Jul 2, 2012, 2:36:54 PM7/2/12
to kivy-...@googlegroups.com
Hi, I am making an app in which I am trying to create thumbnails, small buttons that have an image as the background, that will link to the respective full sized image. I know how to use the Popup widget with the Button widget to do so, but I can't seem to create a button with the background of the image I want through a URL. I put one of the images on my desktop and tried it with that instead and it worked, so having the source for the background as a URL seems to be the problem. Has anyone encountered this problem before? Any help would be greatly appreciated.
 
Thanks

Akshay Arora

unread,
Jul 3, 2012, 4:46:49 AM7/3/12
to kivy-...@googlegroups.com

garrett

unread,
Jul 3, 2012, 12:13:46 PM7/3/12
to kivy-...@googlegroups.com
Thanks! I will try that out.
Message has been deleted

garrett

unread,
Jul 5, 2012, 1:02:24 PM7/5/12
to kivy-...@googlegroups.com
I am having a bit of trouble with this. I am new to Kivy and haven't written a kv language file before. I have everything set up and the py file reads in the kv file just fine it seems. I copied the ButtonBehavior class code into my py file, but when I run the code it says there is an "invalid property name" and it refers to the line in the kv file with the "ButtonBehavior+Image" line. Instead of "Image" I also tried "AsyncImage" but it returns the same error. Do you know what this means?

On Tuesday, July 3, 2012 1:46:49 AM UTC-7, qua-non wrote:

Akshay Arora

unread,
Jul 6, 2012, 8:28:23 AM7/6/12
to kivy-...@googlegroups.com
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.properties import ObjectProperty, BooleanProperty, StringProperty
from kivy.factory import Factory

class ButtonBehavior(object):
    '''Button behavior.

:Events:
`on_press`:
Fired when a touch is pressing the widget
`on_release`:
Fired when the first touch is up
'''

    is_hover = BooleanProperty(False)

    button_grab = BooleanProperty(False)

    button_touch = ObjectProperty(None, allownone=True)

    state = StringProperty('normal')

    def __init__(self, **kwargs):
        super(ButtonBehavior, self).__init__(**kwargs)
        self.register_event_type('on_press')
        self.register_event_type('on_release')
        self.bind(
            on_touch_down=self._button_on_touch_down,
            on_touch_up=self._button_on_touch_up)
        self.state = 'normal'

    def on_press(self, touch):
        pass

    def on_release(self, touch):
        pass

    def _button_on_touch_down(self, instance, touch):
        if not self.collide_point(*touch.pos):
            return
        touch.ungrab(self)
        touch.grab(self)
        self.is_hover = True
        self.button_touch = touch
        self.state = 'down'
        self.dispatch('on_press', touch)
        return self.button_grab

    def _button_on_touch_up(self, instance, touch):
        if touch.grab_current is not self:
            return
        touch.ungrab(self)
        self.is_hover = False
        self.dispatch('on_release', touch)
        self.state = 'normal'
        self.button_touch = None
        return self.button_grab

Factory.register('ButtonBehavior', cls=ButtonBehavior)

Builder.load_string('''
[UrlBut@ButtonBehavior+AsyncImage]:
    source: ctx.source
    button_grab: True
    on_press: self.parent.pressed(*args)
    on_release: self.parent.released(*args)
    color: (1, 1, 1, 1) if self.state == 'normal' else (1, 0, 1 , 1)

<MyApp>:
    UrlBut:
        source: 'http://icons.iconseeker.com/png/fullsize/summer-collection/button.png'
    UrlBut:
        source: 'http://icons.iconseeker.com/png/fullsize/summer-collection/button.png'
''')

class MyApp(BoxLayout):

    def pressed(self, button, touch):
        print 'button [' + str(button)+'] pressed'

    def released(self, button, *touch):
        print 'button [' + str(button)+'] released'


class MyTestApp(App):
    def build(self):
        return MyApp()


if __name__ == '__main__':
    MyTestApp().run()

garrett

unread,
Jul 6, 2012, 4:15:02 PM7/6/12
to kivy-...@googlegroups.com
This looks great. Now I'll just have to put it into my code and somehow iterate over the source of the UrlBut so more than one can be created.
 
Thank you so much

qua-non

unread,
Jul 6, 2012, 4:24:32 PM7/6/12
to kivy-...@googlegroups.com
If you run the code by copy pasting it into a .py file you'll see that it creates two UrlBut instances. If you need to use the template created in  the kv lang you can follow the instructions given here http://kivy.org/docs/api-kivy.lang.html#syntax-of-template.

garrett

unread,
Jul 6, 2012, 5:31:50 PM7/6/12
to kivy-...@googlegroups.com
Yeah, I have a list of URLs that lead to images and I need to iterate over that to create many UrlBut instances.

garrett

unread,
Jul 9, 2012, 4:00:50 PM7/9/12
to kivy-...@googlegroups.com
I'm sorry to be asking for so much help with this, but there is one last thing I need answered. I see how the UrlBut instances work into the app in your code when that is all that needs to be done, but I am trying to create many UrlBut instances, as you know, in a GridLayout within an accordion. I don't see how it could work with so much other code in my py file. Is it possible to make a widget of the UrlBut template?
 

On Friday, July 6, 2012 1:24:32 PM UTC-7, qua-non wrote:

garrett

unread,
Jul 11, 2012, 3:14:59 PM7/11/12
to kivy-...@googlegroups.com
Nevermind, I got it to work with Builder.template. Thanks again for all of your help.

garrett

unread,
Jul 11, 2012, 3:40:56 PM7/11/12
to kivy-...@googlegroups.com
If I haven't already frustrated you with my numerous questions, I have one more. Whenever I try to click on the image/button, an error is returned, which says "GridLayout object has no attribute 'pressed'". I have the images/buttons in a GridLayout within a Accordion widget. Do you know why that happens?  

Akshay Arora

unread,
Jul 11, 2012, 4:03:12 PM7/11/12
to kivy-...@googlegroups.com
Builder.load_string('''
[UrlBut@ButtonBehavior+
AsyncImage]:
    source: ctx.source
    button_grab: True
    on_press: self.parent.pressed(*args)
    on_release: self.parent.released(*args)
    color: (1, 1, 1, 1) if self.state == 'normal' else (1, 0, 1 , 1)

I called self.parent.pressed() because I defines pressed() in UrlBut's parent. You should change accordingly.

garrett

unread,
Jul 11, 2012, 4:20:41 PM7/11/12
to kivy-...@googlegroups.com
Right, I forgot about that.

garrett

unread,
Jul 11, 2012, 5:28:31 PM7/11/12
to kivy-...@googlegroups.com
How can I make the pressed() function useable in the GridLayout format?

On Wednesday, July 11, 2012 1:03:12 PM UTC-7, qua-non wrote:

garrett

unread,
Jul 12, 2012, 1:23:01 PM7/12/12
to kivy-...@googlegroups.com
I have the UrlBit template in the kv file, and the structure of the py file that is set up like this
 
class ButtonBehavior(object)
    all of the code...
 
class FirstApp(App):
    def build(self):
        all of the code...
 
And the UrlBit instances are loaded in with Builder.template and used as widgets. I have tried restructuring my code to look like your original example but that didn't work out. Since the UrlBut instance's parent is GridLayout, I have tried defining pressed() after I create the GridLayout widget but that doesn't work. I feel like I may be missing something very simple.  

garrett

unread,
Jul 12, 2012, 4:04:19 PM7/12/12
to kivy-...@googlegroups.com
I added the pressed function to the gridlayout.py file and it doesn't return an error then, so is it possible to define it in there to make it work?

garrett

unread,
Jul 12, 2012, 7:31:42 PM7/12/12
to kivy-...@googlegroups.com
I guarantee this is the last thing I will need help with :)

qua-non

unread,
Jul 13, 2012, 3:17:15 AM7/13/12
to kivy-...@googlegroups.com
Instead of changing the place the function is placed, you should change the line `on_press: self.parent.pressed(*args)`.
If you have something like ::

<YourWidget>
    GridLayout:
        UrlBut:


Then you should change the line to `root.pressed(*args)` and put the `pressed` function in YourWidget's class definition.

garrett

unread,
Jul 13, 2012, 2:04:29 PM7/13/12
to kivy-...@googlegroups.com
I think understand now, but what if I did not have a rule that defines how the accordion and gridlayout work. I tried this:
 
<FirstApp>:
    Accordion:
        GridLayout:
            UrlBut:
                on_press: root.pressed(*args)
 
But it didn't work. Should I move all of the definitions of those things from the py file to the kv file? Would it be easier if I showed you some of my code?

Thomas Hansen

unread,
Jul 13, 2012, 4:40:17 PM7/13/12
to kivy-...@googlegroups.com
What do you want to happen when the button is pressed?

Sent from my iPhone

garrett

unread,
Jul 13, 2012, 4:44:54 PM7/13/12
to kivy-...@googlegroups.com
To pull up the image itself, and then I can add a scatter to it so it can be manipulated. Is there a better/easier solution?

Thomas Hansen

unread,
Jul 13, 2012, 5:08:15 PM7/13/12
to kivy-...@googlegroups.com
you could create a subclass of GridLayout, with a function to handle
the button press and use that instead of the standard GridLayout. If
yoyu want to use your custom class in the kv file, all you have to do
is register it in the factory.

For Example:
class MyGridLayout(GridLayout):
def handle_button_press(self, button):
print "This button was pressed", button

from kivy.factory import Factory
Factory.register('MyGridLayout', MyGridLayout)


then in your kv file
....
MyGridLayout:
UrlBut:
on_press: self.poarent.handle_button_press(self)


If you need to call the method of a object in the rule, but it's not
your immediate parent or ptherwise easily traceable, you can also
assign id'd in the kv file to reference specific widgets. e.g. to call
some_method on the accordion below:

<MyRoot>
Accordion:
id: my_accordion
GridLayout:
Button:
on_press: my_accordion.some_method("argument 2 is this
button instance", self)

--
Thomas

garrett

unread,
Jul 13, 2012, 7:52:09 PM7/13/12
to kivy-...@googlegroups.com
I really like this idea and it should work well. But should the "MyGridLayout" in the kv file be in <>? If I run it without them it says "name 'self' not defined". If there are <> then the app does run but clicking on the images does nothing.
 
Also, since I am trying to return a Popup which has the scatter image in it after clicking on the image, would I need to use the <MyRoot> way, since the Popup is a separate widget? Here's what I have:
 
in the kv file:
<MyGridLayout>: 
    UrlBut:
        on_press: self.parent.handle_button_press(self)
 
<FirstApp>
    Accordion:
        id: my_accordion
        GridLayout:
            Button:
                on_press: my_accordion.full_image_popup(handle_button_press, self)  # this is probably wrong
in the py file:
class MyGridLayout(GridLayout):
    def handle_button_press(self, touch, button):
        return full_image_popup

qua-non

unread,
Jul 16, 2012, 4:58:00 AM7/16/12
to kivy-...@googlegroups.com
Hello garret,
    Please go through the tutorials and the documentation on kivy.org/docs and the examples in kivy/examples directory. That will clear up a most of the issues you are having.

garrett

unread,
Jul 16, 2012, 2:17:43 PM7/16/12
to kivy-...@googlegroups.com
I have reviewed some of that info again but it seems the only way I can fix this problem is to drastically restructure my py file. The way it is now I just have
 
class FirstApp(App):
    def build(self):
        #all of the code that retrieves the images from the website, puts them into accordion, creates the home page, which links to the     accordion page, and then returns the home page.
 
if __name__ == '__main__':
    FirstApp().run()
 
I have seen a few examples that have a widget being created in the kv file with its function being passed, and a function that returns the previous function. I don't want to do this because I am too far along to change anything and don't want to make a mistake. And I can't make the UrlBut the root widget because the home page with a boxlayout should be the root widget because it links to everything else. It just wouldn't be easy.
 
Anyway, in Thomas' response, he wrote:
  ....
  MyGridLayout:
       UrlBut:
         on_press: self.poarent.handle_button_press(self)
 
Does the four periods before MyGridLayout indicate that there should be something there?

qua-non

unread,
Jul 16, 2012, 6:03:09 PM7/16/12
to kivy-...@googlegroups.com

I think(not sure) the `....` are meant to indicate that you replace your `Gridlayout` with MyGridLayout.

In any case the difference is that if you do::

<MyRoot> :
    Accordion:
        MyGridLayout:
            UrlBut:  
                ...     

Here you are adding MyGridLayout to the Accordion and adding UrlBut to MyGridLayout
If you do ::

<MyGridLayout>:
    UrlBut:
        ...
 
take care UrlBut is a template so simply
You are defining a rule. So any use of MyGridLayout will have a UrlBut instance added to it.
So to achieve what you want you can do this::

[UrlBut@ButtonBehavior+AsyncImage]:
    source: ctx.random_string
    button_grab: True
    on_press: self.parent.handle_button_pressed(*args)

    color: (1, 1, 1, 1) if self.state == 'normal' else (1, 0, 1 , 1)


<MyTestApp>
    Accordion:
        id: my_accordion
        MyGridLayout:
            UrlBut:
                random_string: 'http://icons.iconseeker.com/png/fullsize/summer-collection/button.png

Another thing to note is that because UrlBut is a template any value set outside the template definition
cannot access dynamic values. so `self` will throw a error.

If you need to call a different function through UrlBut every time then you can do something like this.


[UrlBut@ButtonBehavior+AsyncImage]:
    source: ctx.source
    button_grab: True
    on_press: eval(ctx.on_press)

    color: (1, 1, 1, 1) if self.state == 'normal' else (1, 0, 1 , 1)


<MyTestApp>
    Accordion:
        id: my_accordion
        MyGridLayout:
            UrlBut:
                 source: 'http://icons.iconseeker.com/png/fullsize/summer-collection/button.png'
                 on_press: 'self.parent.handle_button_pressed(*args)'

Hope this helps.

garrett

unread,
Jul 18, 2012, 12:40:23 PM7/18/12
to kivy-...@googlegroups.com
Thank you for all of your help. I decided to stop trying to make this work and am using a button below each image temporarily. I will resume this venture later.
Reply all
Reply to author
Forward
0 new messages