软件设计讲解

软件框架

软件设计图

业务系统启动流程

代码讲解

事件管理

各个模块之间通过 EventMap 事件管理类进行协作。EventMap 事件管理类提供事件的绑定、取绑以及消息的同步、异步发送。其定义如下:

class EventMap(object):
    """===example===

    import EventMap

    def time_out(event=None, msg=None):
        pass

    EventMap.bind("time_out", time_out)

    EventMap.send("time_out")
    """
    __event_map = dict()
    __event_log = None

    MODE_SYNC = 0
    MODE_ASYNC = 1

    def __init__(self):
        pass

    @classmethod
    def bind(cls, event, callback):
        """
        :param event: event name
        :param callback: event callback
        """
        if None == event or "" == event:
            return
        cls.__event_map[event] = callback

    @classmethod
    def unbind(cls, event):
        """
        :param event: event name
        """
        if None == event or "" == event:
            return
        cls.__event_map.pop(event, None)

    @classmethod
    def send(cls, event, msg=None, mode=MODE_SYNC):
        """
        :param event: event name
        :param msg: event message
        :param mode: send mode, sync or async
        """
        if event not in cls.__event_map:
            return

        if cls.MODE_SYNC == mode:
            res = None
            try:
                if event in cls.__event_map:
                    res = cls.__event_map[event](event, msg)
            except Exception as e:
                if cls.__event_log:
                    cls.__event_log.info("ERROR executed (event) -> {} (params) -> {} (result) -> {}".format(event, msg, res))
                usys.print_exception(e)
            if cls.__event_log:
                cls.__event_log.info("SYNC executed (event) -> {} (params) -> {} (result) -> {}".format(event, msg, res))
            return res

        elif cls.MODE_ASYNC == mode:
            try:
                _thread.start_new_thread(cls.__event_map[event], (event, msg))
            except Exception as e:
                if cls.__event_log:
                    cls.__event_log.info("ERROR executed (event) -> {} (params) -> {} (result) -> {}".format(event, msg, res))
                usys.print_exception(e)
            if cls.__event_log:
                cls.__event_log.info("ASYNC executed (event) -> {} (params) -> {} (result) -> {}".format(event, msg, None))

抽象加载

common.py 提供了一个名为 AbstractLoad 的抽象加载基类,方便用户理解界面加载的过程,定义如下:

class AbstractLoad(object):
    def load_before(self, *args, **kwargs):
        """加载前调用"""
        pass

    def load(self, *args, **kwargs):
        """加载时调用"""
        pass

    def load_after(self, *args, **kwargs):
        """加载后调用"""
        pass

    def instance_after(self, *args, **kwargs):
        """实例化后调用"""
        pass

    def deactivate(self, *args, **kwargs):
        """失效"""
        pass

AbstractLoad 抽象加载基类会被定义的 UI 界面和 services 所继承,在加载 UI 界面或加载服务时可以调用对应的抽象类方法,实现页面和服务的加载。

比如在主界面的定义中,可以在 load 方法中添加相应的代码实现主界面的布局与样式,这样在进入主界面后就能显示出设置的 UI 界面了。

服务模块

services.py 定义了多个 service,用于提供各种服务。这些 servies 继承 AbstractLoad 抽象加载基类,便于在加载过程中提供各类服务事项。

  1. DevInfoService:提供设备信息服务
  2. MediaService:提供音频服务
  3. NetService:提供网络服务
  4. PocService:提供 Poc 对讲服务

service 之间的关系如下图:

如用户需添加 service,可参考已有 service 样式进行添加,并添加到 poc_main.py 中对应的位置即可。

界面模块

ui.py 定义了一个 Screen 基类,该基类被定义的 UI 界面继承,用于约束 UI 界面的接口定义,其定义如下:

class Screen(AbstractLoad):
    class Type():
        Init = "init"
        Normal = "normal"   # 默认
        MenuBar = "menubar"
        ToolBar = "toolbar"
        StatusBar = "statusbar"

    def __init__(self):
        self.meta = None    # lvgl meta object
        self.meta_info = {}
        self.last_screen = None

    def set_last_screen(self, name):
        self.last_screen = name

    def load_before(self):
        pass

    def load(self):
        pass

    def load_after(self):
        pass

    def instance_after(self):
        pass

    def deactivate(self):
        pass

    def key2_once_click(self):
        pass

    def key2_double_click(self):
        pass

    def key2_long_press(self):
        pass   

    def prev_idx(self, now_idx, count):
        cur_idx = now_idx - 1
        if cur_idx < 0:
            cur_idx = count - 1
        return cur_idx
    def next_idx(self, now_idx, count):
        cur_idx = now_idx + 1
        if cur_idx > count - 1:
            cur_idx = 0
        return cur_idx

ui.py 中,还定义了多个 UI 界面,如:

  1. PocUI:主 UI,提供 MenuBarPromptBoxScreen 的管理以及按键事件的响应处理
  2. MenuBar:菜单栏
  3. PromptBox:提示框
  4. Screen:UI 屏幕,也可以理解为 UI 界面,用于展示给用户看的各种界面。如 MenuBarWelcomeScreenMemberScreen

各 UI 界面之间的关系如下图:

如用户需添加 Screen,可参考已有 Screen 样式进行添加,并添加到 poc_main.py 中对应的位置即可。

APP管理

poc_main.py 中使用一个 APP 类进行管理,用户添加或修改如按键、消息框和服务等操作,调用相应的函数添加即可。

#=== 1.添加按键 ===
App.add_key(KeyManger())

#=== 2.添加主UI ===
App.set_ui(PocUI())

#=== 3.添加屏幕栏 ===
App.add_bar(MenuBar())

#=== 4.添加消息框 ===
App.add_msgbox(PromptBox())

#=== 5.添加UI屏幕 ===
App.add_screen( MenuBar()) \
    .add_screen( MainScreen()) \
    .add_screen( WelcomeScreen() ) \
    .add_screen( PromptBox() ) \
    .add_screen( MemberScreen() ) \
    .add_screen( GroupScreen() ) \
    .add_screen( SettingScreen() ) \
    .add_screen( DeviceScreen() ) \
    .add_screen( ICCIDScreen() ) \
    .add_screen( IMEIScreen()) \
    .add_screen( FirmwareScreen() )

#=== 6.添加服务 ===
App.add_service( NetService()) \
    .add_service( PocService()) \
    .add_service( MediaService()) \
    .add_service( DevInfoService() ) 

#=== 7.运行App ===
App.exec()