Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

kivy glitch with images and threading

I'm relatively new to kivy and am having an intermittent issue with certain images and button images not loading correctly, even when the same image loads correctly for other widgets using the same image. I think it's an issue with threading and kivy drawing not working well together. For the code below that reproduces the issue threading isn't necessary, but in the full app, loading all the images blocks for too long. Thanks for any help.

from kivy.app import App
from kivy.core.window import Window
from kivy.properties import ObjectProperty
from kivy.uix.screenmanager import Screen

from kivy.uix.boxlayout import BoxLayout
from kivy.uix.relativelayout import RelativeLayout
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.scrollview import ScrollView
from kivy.effects.opacityscroll import OpacityScrollEffect

from kivy.uix.textinput import TextInput
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import AsyncImage, Image
from kivy.uix.screenmanager import ScreenManager, Screen

from _functools import partial
from threading import Thread


class resultLine(RelativeLayout):
    def __init__(self, title, coverUrl, showid):
        super(RelativeLayout, self).__init__(size_hint=(None, None), size=(Window.width * 0.945, 0.12 * Window.height))
        self.cover = AsyncImage(source=coverUrl, loading_image='loading.gif', error_image='../../tv.png',
                            size=(0.2 * self.width, 0.85 * self.height), pos=(0, 0.125 * self.height), size_hint=(None, None),
                             nocache=True)
        self.cover.width = Window.width * 0.2

        self.nameButton = Button(text='', size=(self.width, self.height), x=0, size_hint=(None, None), opacity=0.85, 
                                background_normal='../../button.png', background_down='../../button_pressed.png')
        fontSize=0.05 * Window.width
        self.nameTag = Label(text='              ' + title, font_size=fontSize, width=Window.width * 0.945, 
                            height=self.height, x=0, y=0.05*self.height, 
                            size_hint=(None, None), valign='middle', halign='left', opacity=0.85,
                            padding_x=Window.width * 0.025)

        self.nameTag.text_size = self.nameTag.size
        self.nameTag.shorten = True
        self.nameTag.shorten_from = 'right'
        self.nameTag.split_str = ' ...'

        self.nameButton.bind(on_press=self.doNameButtonPress)
        self.nameButton.bind(on_release=self.doNameButtonRelease)


        self.add_widget(self.nameButton)        
        self.add_widget(self.nameTag)
        self.add_widget(self.cover)
        self._trigger_layout()

    def doNameButtonPress(self, instance):
        self.nameTag.x += 0.01 * self.width
        self.nameTag.y -= 0.1 * self.height
        # self.cover.pos_hint = {'x':0.01, 'y':0.025}
        self.cover.x += 0.01 * self.width
        self.cover.y -= 0.1 * self.height

    def doNameButtonRelease(self, instance):
        self.nameTag.x -= 0.01 * self.width
        self.nameTag.y += 0.1 * self.height
        # self.cover.pos_hint = {'x':0, 'y':0.1}
        self.cover.x -= 0.01 * self.width
        self.cover.y += 0.1 * self.height


class ShowSelector(Screen):

    bottomLayout = ScrollView(size=(0.95 * Window.width, Window.size[1] * 0.875), pos=(0.025 * Window.width, 0),
                            size_hint=(None, None), bar_margin=-0.0125 * Window.width, effect_cls=OpacityScrollEffect)
    scrollLayout = GridLayout(cols=1, size_hint=(None, None), height=Window.height, spacing=Window.height * .01)

    def __init__(self, **kwargs):

        super(Screen, self).__init__(**kwargs)
        self.add_widget(self.bottomLayout)
        self.bottomLayout.add_widget(self.scrollLayout)

        self.scrollLayout.bind(minimum_height=self.scrollLayout.setter('height'))

        self.scrollLayout.height = (Window.height * 0.12 + self.scrollLayout.spacing[1]) * 100

        Thread(target=self.action).start()

    def action(self):
        for i in xrange(1,100):
            self.scrollLayout.add_widget(resultLine(str(i), '1.jpg', 0))

class theApp(App):

    smMain =ScreenManager() 
    selectorScreen = ShowSelector(name='showsel')

    def build(self):

        self.smMain.add_widget(self.selectorScreen)
        return self.smMain

thisApp=theApp()
thisApp.run()
like image 728
dude8604 Avatar asked Feb 20 '16 10:02

dude8604


1 Answers

I figured it out. In case anyone else runs into this problem. Any changes to the GUI have to be done in the main thread. You can force a function by putting @mainthread on the line above the function definition. For example:

from kivy.clock import mainthread

@mainthread
def add something(self, w):
    self.add_widget(w)

You have to "import mainthread" for this to work.

like image 103
dude8604 Avatar answered Oct 25 '22 22:10

dude8604