Search code examples
pythonkivy

I'm having a problem with kivy with threading


I'm working on a project that works in python with kivy and I'm having a problem with kivy with thread "TypeError: Cannot create graphics instruction outside the main Kivy thread" When I press the button, this error appears

    def ched(self):
    if self.typ == 0:
        self.VpDone()
        self.pinpu.text = ""
    elif self.typ == 1:
        self.GpDone()
        self.pinpu.text = ""
    elif self.typ == 2:
        self.IpDone()
        self.pinpu.text = ""
def down(self):
    if self.pinpu.text is "":
        self.empty()
    else:
        try:
            self.pintd(self.pinpu.text)
            self.ched()
        except:
            self.ERorr()


def diacloce(self, obj):
    self.dialog.dismiss()

def empty(self):

    self.dialog = MDDialog(
        title="Error",
        text="You cannot download nothingness!",
        buttons=[
            MDFlatButton(text="CANCEL", on_release=self.diacloce)

            ]
        )
    self.dialog.open()



def IpDone(self):

    self.dialog = MDDialog(
        title="Done!",
        text="Image downloaded successfully!",
        buttons=[
            MDFlatButton(text="CANCEL", on_release=self.diacloce)

            ]
        )
    self.dialog.open()
def VpDone(self):

    self.dialog = MDDialog(
        title="Done!",
        text="Video downloaded successfully!",
        buttons=[
            MDFlatButton(text="CANCEL", on_release=self.diacloce)

            ]
        )
    self.dialog.open()
def GpDone(self):

    self.dialog = MDDialog(
        title="Done!",
        text="GIF downloaded successfully!",
        buttons=[
            MDFlatButton(text="CANCEL", on_release=self.diacloce)

            ]
        )
    self.dialog.open()

def ERorr(self):
    self.dialog = MDDialog(
        title="Erorr!",
        text="Error ?\nPerhaps there is a problem with the link or the Internet !",
        buttons=[
            MDFlatButton(text="CANCEL", on_release=self.diacloce)

        ]
    )
    self.dialog.open()

kv file:

    MDRectangleFlatButton:
    text:'Download content'
    pos_hint:{'center_x': 0.5, 'center_y': 0.40}
    theme_text_color:"Hint"
    size_hint:(0.35, 0.08)
    on_release:threading.Thread(target=root.down).start()

What is the solution? I saw many solutions, but I really did not understand them or I could not implement them, yet I tried to do a lot of solutions Thank you for your time


Solution

  • Your code:

    on_release:threading.Thread(target=root.down).start()
    

    will run root.down() on a new thread (not the main thread). And that code will try to create a MDDialog and a MDFlatButton on that new thread, which is not allowed. If the down() method, and the methods it calls, are simply creating dialogs, as in your post, then you don't need to use threading. Just change that line in your kv to:

    on_release: root.down()
    

    If the down() method is a long running method, it will freeze the GUI until it completes. In that case, keep the:

    on_release:threading.Thread(target=root.down).start()
    

    and arrange for anything that modifies the GUI or creates GUI widgets to be run on the main thread. A simple way to do that is to just add the @mainthread decorator on any method that must be run on the main thread. For example:

    @mainthread
    def empty(self):
    
        self.dialog = MDDialog(
            title="Error",
            text="You cannot download nothingness!",
            buttons=[
                MDFlatButton(text="CANCEL", on_release=self.diacloce)
    
                ]
            )
        self.dialog.open()
    

    This will force the empty() method to be run on the main thread (equivalent to running it using Clock.schedule_once()). The same can be done for all of your methods that just display a dialog.

    The key points to keep in mind:

    1. Updates to the GUI are performed by the Kivy main loop, which runs on the main thread.
    2. Any method that is triggered by an event (like a Button press) runs on the main thread, and the Kivy main loop must wait until that triggered method completes (so keep them short).
    3. Methods running in a thread that is not the main thread may not modify the GUI (or even create GUI widgets).

    So, you should separate GUI modification from non-GUI processing by using small short lived methods for GUI modification that do nothing other than GUI modification (like your dialog methods). These GUI modifying methods can be called from the non-GUI methods (that are running on other threads) by using Clock.schedule_once() or by decorating the GUI modifying methods with @mainthread and then calling those methods directly. Keep in mind that either of these approaches just schedules the call to the GUI modification method on the main thread.