Kivy + mqtt-当on_message时,发生致命的Python错误:(pygame降落伞)分段错误

时间:2018-07-18 01:16:50

标签: python python-2.7 kivy mqtt paho

我在python 2.7.15中使用kivy 1.11.0。

#-*- 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()

如果收到消息(on_message),请更改屏幕

但出现了错误

Fatal Python error: (pygame parachute) Segmentation Fault

我认为已检测到错误原因  有图像。 当on_message加载图像源时,发生错误 我认为paho.mqtt回调(on_message,on_connect)值得怀疑

因为当self.changeScreen()处于def on_start,def构建状态时,未发生错误

下面的代码是没有发生错误

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

但是这段代码感觉很糟糕。因为如果WaitScreen不使用Image source,就不会发生错误

请告诉我如何避免错误。 我想使用上层代码

2 个答案:

答案 0 :(得分:1)

此问题已解决。

from kivy.clock import mainthread

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

仅此而已

答案 1 :(得分:0)

问题-SIGSEGV(分段错误)

根本原因是加载图像,即您的应用程序正在等待加载图像。

注意

Image替换AsyncImage将防止您的应用程序等待图像加载。但是在加载4到5张图片/ WaitScreen之后,它将以IndexError: list index out of range崩溃。

解决方案

  1. 在Python脚本中手动调用Paho MQTT loop()方法。
  2. 添加了number类型的NumericProperty,因为屏幕名称在ScreenManager中必须是唯一的。

有关详细信息,请参见代码段,示例和输出。在示例中,我们使用在线经纪人,如iot.eclipse.org上的经纪人。

摘要

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
     

在ScreenManager中 必须唯一 的屏幕名称。   这是ScreenManager.current的名称。

     

name是StringProperty,默认为‘

示例

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()

输出

Img01