-1

I'm using kivy 1.11.0 with python 2.7.15.

#-*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.image import Image
from kivy.lang import Builder
Builder.load_string("""
<InitScreen>:
    AnchorLayout:
        anchor_x:"center"
        anchor_y:"center"
        Label:
            text:"init"
<WaitScreen>:
    Image:
        id:charactor
        pos_hint:{'center_x':.5,'y':0 }
        size_hint:1,1
        source:'./wait1.png'
""")

class Charactor(Image):
    pass
class InitScreen(Screen):
    pass
class WaitScreen(Screen):
    def __init__(self, **kwargs):
        super(WaitScreen, self).__init__(**kwargs)


class View(App):
    sm = ScreenManager()
    cli = mqtt.Client(protocol=mqtt.MQTTv311)
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)

    def build(self):
        self.sm.add_widget(InitScreen(name='init'))
        self.sm.current='init'
        return self.sm

    def on_start(self):
        self.cli.on_connect = self.on_connect
        self.cli.on_message = self.on_message
        self.cli.connect('localhost', port=1883, keepalive=60)
        self.cli.loop_start()

    def on_connect(self,client, userdata, flags, respons_code):
        print('status {0}'.format(respons_code))
        client.subscribe('get/test')

    def on_message(self,client, userdata, msg):
        self.changeScreen()

    def changeScreen(self,**kwargs):
        self.sm.add_widget(WaitScreen(name='wait'))
        self.sm.current='wait'

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

if get message(on_message), change screen

but error happed

Fatal Python error: (pygame parachute) Segmentation Fault

i think detected the cause of error have Image. When on_message load Image source, error happened I think paho.mqtt callback(on_message,on_connect) is doubtful

because When self.changeScreen() is in def on_start,def build, error not happened

below code is error has not happened

class View(App):
    sm = ScreenManager()
    cli = mqtt.Client(protocol=mqtt.MQTTv311)
    waitflag = False
    def __init__(self, **kwargs):
        super(View, self).__init__(**kwargs)

    def build(self):
        self.sm.add_widget(InitScreen(name='init'))
        self.sm.current='init'
        return self.sm

    def on_start(self):
        self.cli.on_connect = self.on_connect
        self.cli.on_message = self.on_message
        self.cli.connect('localhost', port=1883, keepalive=60)
        self.cli.loop_start()
        Clock.schedule_interval(self.changeScreen, 1) 

    def on_connect(self,client, userdata, flags, respons_code):
        print('status {0}'.format(respons_code))
        client.subscribe('get/test')

    def on_message(self,client, userdata, msg):
        self.waitflag = True

    def changeScreen(self,dt):
        if self.waitflag:
            self.sm.add_widget(WaitScreen(name='wait'))
            self.sm.current='wait'
            self.waitflag = False

but this code feel bad.because if WaitScreen don't use Image source, error not happened

Please tell me how to avoid errors. i want to use upper code

3tori
  • 11
  • 2

2 Answers2

1

this question is got settled.

from kivy.clock import mainthread

and

@mainthread
def on_message(self,client, userdata, msg):

that's all

3tori
  • 11
  • 2
  • This doesn't actually explain what you changed or why it fixed the problem. Please add more context and then accept your own answer. – hardillb Jul 18 '18 at 12:51
0

Problem - SIGSEGV (Segmentation fault)

The root cause is loading image i.e. your application is waiting until the image is loaded.

Note

Replacing Image with AsyncImage will prevents your application from waiting until the image is loaded. But after loading 4 to 5 images/WaitScreen, it will crash with IndexError: list index out of range.

Solution

  1. Manually call Paho MQTT loop() method in Python script.
  2. Added number of NumericProperty type because screen's name must be unique within a ScreenManager.

Please refer to the snippet, example, and output for details. In the examples, we use an online broker like the one at iot.eclipse.org.

Snippet

def on_start(self):
    self.number = 0
    self.cli = mqtt.Client(protocol=mqtt.MQTTv311)
    self.cli.on_connect = self.on_connect
    self.cli.on_message = self.on_message
    self.cli.connect("iot.eclipse.org", port=1883, keepalive=60)
    Clock.schedule_interval(self.loop, 0.5)    # call loop every 0.5 seconds

def loop(self, dt):
    # manually call the Paho MQTT loop() method
    self.cli.loop(0.1)  # blocks for 100 ms

Screen » name

name

Name of the screen which must be unique within a ScreenManager. This is the name used for ScreenManager.current.

name is a StringProperty and defaults to ‘’.

Example

main.py

# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt

from kivy.app import App
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.image import Image
from kivy.lang import Builder
from kivy.properties import NumericProperty
from kivy.clock import Clock

Builder.load_string("""
<InitScreen>:
    AnchorLayout:
        anchor_x:"center"
        anchor_y:"center"
        Label:
            text:"init"

<WaitScreen>:
    Image:
        id: charactor
        pos_hint: {'center_x':.5, 'y':0}
        size_hint: 1, 1
        source: './wait.jpeg'
""")


class Charactor(Image):
    pass


class InitScreen(Screen):
    pass


class WaitScreen(Screen):
    pass


class View(App):

    def build(self):
        self.number = NumericProperty(0)
        self.sm = ScreenManager()
        self.sm.add_widget(InitScreen(name='init'))
        return self.sm

    def on_start(self):
        self.number = 0
        self.cli = mqtt.Client(protocol=mqtt.MQTTv311)
        self.cli.on_connect = self.on_connect
        self.cli.on_message = self.on_message
        self.cli.connect("iot.eclipse.org", port=1883, keepalive=60)
        Clock.schedule_interval(self.loop, 0.5)   # call loop every 0.5 seconds

    def loop(self, dt):
        # manually call the Paho MQTT loop() method
        self.cli.loop(0.1)  # blocks for 100 ms

    def on_connect(self, client, userdata, flags, response_code):
        print("\non_connect:")
        print("\tclient={0}, userdata={1}, flags={2}, response_code={3}".format(client, userdata, flags, response_code))
        client.subscribe("$SYS/#")

    def on_message(self, client, userdata, msg):
        print("\non_message:")
        print("\tclient={0}, userdata={1}, msg={2}".format(client, userdata, msg))
        print("\tmsg.topic={0}, msg.payload={1}".format(msg.topic, msg.payload))
        self.changeScreen()
        self.number += 1

    def changeScreen(self):
        print("\nchangeScreen:")
        print("\tnumber={}".format(self.number))
        screen_name = 'wait{}'.format(self.number)
        print("\tscreen_name={}".format(screen_name))

        self.sm.add_widget(WaitScreen(name=screen_name))
        self.sm.current = screen_name

    def on_stop(self):
        # stop the loop before exit
        self.cli.loop_stop()


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

Output

Img01

ikolim
  • 15,721
  • 2
  • 19
  • 29