微信办公助手、微信群发、微信群控、微信机器人wechat-robot、百度文心AI机器人、ChatGPT机器人
class AiConstant:
    # 映射界面的选择
    DEFAULT_WENXIN_ROBOT: str = "系统默认机器人"

    SELF__WENXIN_ROBOT: str = "自定义百度文心"

    SELF_CHATGPT3_ROBOT: str = "自定义ChatGPT3.5"

    DEFAULT_ROBOT_TYPE: dict = {
        "全能机器人": "你扮演名字叫图图的角色，你擅长英文问答、文本创作、知识推理、数学计算、代码理解与编写、作画、翻译等",
        "面试官": "我希望你能充当面试官。我将扮演求职者的角色，你将为「职位」职位向我提问面试问题。请只以面试官的身份回复。不要一次性写下所有对话内容。我希望你只和我进行面试。一个一个问题地问我，等待我的回答。请不要解释。像面试官那样逐个问题地问我，等待我的回答。我的第一个句子是\"Hi\"。",
        "辩论教练": "我希望你能充当一名辩论教练。我会给你一个辩论团队和他们即将进行的辩论主题。你的目标是通过组织训练回合，专注于有说服力的演讲、有效的时间策略、驳斥对立观点，并从提供的证据中得出深入的结论，为团队的成功做好准备。我的第一个请求是：'我希望我们的团队能够为即将举行的关于前端开发是否容易的辩论做好准备。'",
        "小说家": "我希望你能充当一位小说家。你将创作出引人入胜的创意故事，能够吸引读者长时间阅读。你可以选择任何类型的小说，如奇幻、浪漫、历史虚构等，但目标是写出具有出色情节、吸引人物和意外高潮的作品。我的第一个请求是：\"我需要写一本设定在未来的科幻小说。\"",
        "恋爱教练": "我希望你能充当一位恋爱教练。我会提供一些有关两个有冲突的人的细节，你的任务是提出建议，帮助他们解决彼此之间的问题。这可能包括关于沟通技巧或改善对彼此观点理解的不同策略的建议。我的第一个请求是：\"我需要帮助解决我与配偶之间的冲突。\"",
        "诗人": "我希望你能充当一位诗人。你将创作能够唤起情感并有力地触动人们心灵的诗歌。你可以以任何主题或主题进行创作，但请确保你的文字以美丽而有意义的方式传达你想要表达的感觉。你也可以构思简短的诗句，它们仍然足够有力，能够在读者心中留下印记。我的第一个请求是：\"我需要一首关于爱的诗。\"",
        "哲学教师": "我希望你能充当一位哲学教师。我会提供一些与哲学研究相关的话题，你的任务是以易于理解的方式解释这些概念。这可能包括提供例子、提出问题或将复杂的思想拆分为更容易理解的部分。我的第一个请求是：\"我需要帮助理解不同的哲学理论如何应用于日常生活中。\"",
        "数学老师": "我希望你能充当一位数学老师。我会提供一些数学方程或概念，你的任务是用易于理解的术语解释它们。这可能包括提供逐步解决问题的说明，通过图示展示各种技巧，或建议在线资源供进一步学习。我的第一个请求是：\"我需要帮助理解概率如何工作。\"",
        "网络安全专家": "我希望你能充当一位网络安全专家。我会提供一些关于数据存储和共享方式的具体信息，你的任务是制定保护这些数据免受恶意行为者侵害的策略。这可能包括建议加密方法、创建防火墙或实施将某些活动标记为可疑的策略。我的第一个请求是：\"我需要帮助为我的公司制定一套有效的网络安全策略。\"",
        "医生": "我希望你能扮演一名医生，为疾病提供创新的治疗方法。你应该能够推荐常规药物、草药疗法和其他自然疗法。在提供建议时，你还需要考虑患者的年龄、生活方式和病史。我的第一个建议请求是：“为一位患有关节炎的老年患者提出一份以整体疗法为重点的治疗计划。”",
        "投资经理": "寻求经验丰富的员工对金融市场的指导，考虑因素如通胀率、收益预测以及长时间跟踪股价等，最终帮助客户了解行业，然后建议最安全的投资选择，根据客户的需求和兴趣进行资金配置！开始的问题是 - “当前短期投资的最佳方式是什么？”",
        "IT架构师": "我希望你能充当一名IT架构师。我会提供一些关于应用程序或其他数字产品功能的细节，你的任务是提出将其整合到IT架构中的方式。这可能涉及分析业务需求，进行差距分析，并将新系统的功能与现有的IT架构进行映射。下一步是创建解决方案设计，物理网络蓝图，系统集成的接口定义以及部署环境的蓝图。我首先的请求是：“我需要帮助整合一个CMS系统。”",
        "疯子": "我希望你能充当一个疯子。疯子的话毫无意义，使用的词语完全随意。疯子以任何方式都不会构造出逻辑上的句子。我首先的建议是：“我需要帮助为我新系列《炙热之骷髅》创作一些疯狂的句子，所以请给我写10个句子。”",
        "正则表达式生成器": "我希望你能充当一个正则表达式生成器。你的任务是生成可以匹配文本中特定模式的正则表达式。你应该以可以轻松复制和粘贴到支持正则表达式的文本编辑器或编程语言中的格式提供正则表达式。不要写解释或示例来说明正则表达式的工作原理，只提供正则表达式本身。我的第一个提示是生成一个可以匹配电子邮件地址的正则表达式。",
        "朋友": "我希望你扮演我的朋友的角色。我会告诉你我生活中正在发生的事情，然后你会用一些有帮助和支持的话来回复，帮助我度过困难时期。不要写任何解释，只回复建议或支持的话。我第一个请求是：“我已经在一个项目上工作了很长时间，现在我感到非常沮丧，因为我不确定它是否朝着正确的方向发展。请帮助我保持积极的态度，专注于重要的事情。”",
        "语言文学评论家": "我希望你能充当语言的文学评论家。我会提供一些文学作品的摘录给你。你需要在给定的语境下进行分析，包括文学作品的流派、主题、情节结构、人物塑造、语言和风格以及历史和文化背景等方面。你应该以对其意义和重要性的更深入理解作为结尾。我的第一个要求是 \"To be or not to be, that is the question.\"。",
        "佛陀": "从现在开始，我希望你充当佛陀（又称释迦牟尼佛或释迦摩尼佛），并提供《三藏经》中所包含的同样的指导和建议。请使用《大正藏》中的写作风格，特别是《中部》、《相应》、《增支》和《长部》中的写作风格。当我向你提问时，你将以佛陀的身份回答，并且只谈论佛陀时代存在的事物。我将假装自己是一个有很多需要学习的凡夫。我将向你提问，以增进对你的法教和教诲的了解。请完全沉浸于佛陀的角色中。尽力保持佛陀的形象。不要打破角色。让我们开始：此时，你（佛陀）正在耆那教伽耶的医生果园附近逗留。我来到你的跟前，与你互致问候。问候和礼貌的交谈结束后，我坐到一边，向你提出我的第一个问题：阿罗汉果是否是你主张已经觉悟到的最高圆满觉悟？",
        "高级前端开发者": "我希望你能充当一名高级前端开发者。我将描述一个项目的细节，你将使用以下工具编写该项目：Create React App、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axios。你应该将文件合并为一个单独的 index.js 文件，不要写解释。我的第一个请求是“创建一个宝可梦应用，该应用列出了从 PokeAPI 精灵端点获取的带有图像的宝可梦”。",
        "占星师": "我希望你能充当一名占星师。你将学习星座符号及其含义，了解行星位置以及它们如何影响人类生活，能够准确解读星盘，与那些寻求指导或建议的人分享你的见解。我首先的建议是：“我需要帮助为一位客户提供关于职业发展的深入解读，基于他们的出生星盘。”",
        "会计师": "我希望你能扮演一名会计师，并提出创造性的财务管理方法。在为客户制定财务计划时，你需要考虑预算编制、投资策略和风险管理。在某些情况下，你还可能需要就税法和法规提供建议，以帮助客户最大化利润。我的第一个建议请求是：“为一家小型企业制定一份以成本节约和长期投资为重点的财务计划。”",
        "记者": "我希望你能充当一名记者。你将报道突发新闻，撰写特写报道和观点文章，发展研究技巧来验证信息和揭示消息来源，遵守新闻伦理，并以自己独特的风格进行准确的报道。我首先的建议是：“我需要帮助写一篇关于全球主要城市空气污染的文章。",
        "产品经理": "请确认我的以下请求。请以产品经理的身份回复我。我将提供主题，你将帮助我为该主题撰写一个PRD，其中包括以下标题：主题、介绍、问题陈述、目标和目标、用户故事、技术要求、益处、关键绩效指标、开发风险、结论。在我特定要求某个主题、功能或开发的PRD之前，请不要撰写任何PRD。"

    }

frame/ai_constant.py
wangtu/wechat_robot-manage-demo
from frame import controller
from frame.constant import Constant
from puremvc.patterns.facade import Facade


class AppFacade(Facade):
    STARTUP: str = "startup"

    def __init__(self) -> None:
        self.initializeFacade()

    @staticmethod
    def getInstance():
        return AppFacade()

    def initializeFacade(self) -> None:
        super(AppFacade, self).initializeFacade()
        self.initializeController()

    def initializeController(self) -> None:
        super(AppFacade, self).initializeController()

        super(AppFacade, self).registerCommand(AppFacade.STARTUP, controller.StartupCommand)
        super(AppFacade, self).registerCommand(Constant.SWITCH_QWIDGET, controller.SwitchQWidgetCommand)
        super(AppFacade, self).registerCommand(Constant.CLOSE_QWIDGET, controller.CloseQWidgetCommand)
        super(AppFacade, self).registerCommand(Constant.CLOSE_CHILD_QWIDGET, controller.CloseChildQWidgetCommand)


if __name__ == '__main__':
    print(f'Hi, app-facade')

frame/appfacade.py
wangtu/wechat_robot-manage-demo
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import logging
import logging.config

"""
这个类是个单例，解决了UI对应mediator获取到日志，刷新到UI界面去
"""


class MyLogHandler(logging.Handler, object):
    """
    自定义日志handler
    """
    instance = None

    def __init__(self, name, other_attr=None, *args, **kwargs):
        logging.Handler.__init__(self)
        self.set_name(name)
        print('初始化自定义日志处理器：', name)
        print('其它属性值：', other_attr)

        # 自定义回调函数
        self.callbackFunc = None

    def emit(self, record):
        """
        emit函数为自定义handler类时必重写的函数，这里可以根据需要对日志消息做一些处理，比如发送日志到服务器

        发出记录(Emit a record)
        """
        try:
            msg = self.format(record)
            # print('获取到的消息为：', msg)
            if self.callbackFunc is not None:
                self.callbackFunc(msg)
            # for item in dir(record):
            #     if item in ['process', 'processName', 'thread', 'threadName']:
            #         print(item, '：', getattr(record, item))
        except Exception as e:
            # self.handleError(record)
            print(e)

    def __new__(cls, *args, **kwargs):
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(MyLogHandler, cls).__new__(cls)
        return cls.instance

    @staticmethod
    def getInstance():
        return MyLogHandler.instance


if __name__ == '__main__':
    # 测试
    logging.basicConfig()
    logger = logging.getLogger("logger")
    logger.setLevel(logging.INFO)
    my_log_handler = MyLogHandler('LoggerHandler')
    logger.addHandler(my_log_handler)
    logger.info('hello，shouke')

frame/base/log_handler.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtCore import QThread, pyqtSignal


class SignalThread(QThread):
    signal = pyqtSignal(str)  # 括号里填写信号传递的参数

    def __init__(self):
        super().__init__()
        self.data = None

    def __del__(self):
        # self.wait()
        pass

    def run(self):
        # 进行任务操作
        self.signal.emit(self.data)  # 发射信号

frame/base/signal_thread.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtCore import QThread, pyqtSignal


class SignalThreadTuple(QThread):
    signal = pyqtSignal(tuple)  # 括号里填写信号传递的参数

    def __init__(self):
        super().__init__()
        self.data = None

    def __del__(self):
        # self.wait()
        pass

    def run(self):
        # 进行任务操作
        self.signal.emit(self.data)  # 发射信号

frame/base/signal_thread_tuple.py
wangtu/wechat_robot-manage-demo
from builtins import list

from wcferry import Wcf, wcf_pb2

from frame.vo.wxdb_contact_vo import WxDbContact


class WcfSSS(Wcf):
    instance = None

    def __init__(self, host: str = None, port: int = 10086, debug: bool = True) -> None:
        super().__init__(host, port, debug)
        # 好友
        self.allFriends = []
        # 群聊
        self.chatRooms = []
        # 个人信息
        self.userInfo = {}

    def cleanup1(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(WcfSSS, cls).__new__(cls)
        return cls.instance

    @staticmethod
    def getInstance():
        return WcfSSS.instance

    def __del__(self):
        super().__del__()

    def is_running(self) -> bool:
        return self._is_running

    def getAllContacts(self) -> dict:
        """
        慎重使用，数量量大后，卡慢
        获取联系人（包括好友、公众号、服务号、群成员……）
        格式: {"wxid": "NickName"}
        """
        contacts = self.query_sql("MicroMsg.db", "SELECT UserName, NickName FROM Contact;")
        return {contact["UserName"]: contact["NickName"] for contact in contacts}

    def getAllFriends(self) -> list:
        self.allFriends = []
        contacts = self.query_sql("MicroMsg.db",
                                  """
                                    SELECT
                                        UserName,Remark,NickName,PYInitial,RemarkPYInitial,t2.smallHeadImgUrl
                                    FROM
                                    Contact t1 LEFT JOIN ContactHeadImgUrl t2
                                    ON
                                        t1.UserName = t2.usrName
                                    WHERE
                                        t1.VerifyFlag = 0
                                    AND (t1.Type = 3 OR t1.Type > 50)
                                    AND t1.UserName NOT IN ("qmessage", "tmessage")
                                    ORDER BY
                                        t1.Remark DESC
                                  """)
        for contact in contacts:
            vo = WxDbContact()
            vo.UserName = contact["UserName"]
            vo.Remark = contact["Remark"]
            vo.NickName = contact["NickName"]
            vo.PYInitial = contact["PYInitial"]
            vo.RemarkPYInitial = contact["RemarkPYInitial"]
            vo.smallHeadImgUrl = contact["smallHeadImgUrl"]
            self.allFriends.append(vo)
        return self.allFriends

    def getAllChatRooms(self) -> list:
        self.chatRooms = []
        # t2.smallHeadImgUrl
        # LEFT JOIN ContactHeadImgUrl t2 ON t1.UserName = t2.usrName
        contacts = self.query_sql("MicroMsg.db",
                                  """
                                    SELECT
                                        UserName,
                                        Remark,
                                        NickName,
                                        PYInitial,
                                        RemarkPYInitial
                                    FROM
                                        Contact t1
                                    WHERE
                                        t1.Type = 2
                                  """)
        for contact in contacts:
            vo = WxDbContact()
            vo.UserName = contact["UserName"]
            vo.Remark = ""
            vo.NickName = contact["NickName"]
            vo.PYInitial = contact["PYInitial"]
            vo.RemarkPYInitial = ""
            # vo.smallHeadImgUrl = contact["smallHeadImgUrl"]
            self.chatRooms.append(vo)
        return self.chatRooms

    def getContact_NickName(self, wx_id) -> str:
        """
        获取联系人（包括好友、公众号、服务号、群成员……）
        """
        contacts = self.query_sql("MicroMsg.db", f"SELECT UserName, NickName FROM Contact WHERE UserName='{wx_id}';")
        if len(contacts) > 0:
            return contacts[0].get("NickName", "")
        return ""

    def getChatRoom_Name(self, wx_id, count=5) -> str:
        """
        如果群未添加名称，可调用该方法获取群成员姓名
        """
        if not wx_id:
            return ""
        result = ""
        roomNames = self.query_sql("MicroMsg.db", f"SELECT UserNameList FROM ChatRoom WHERE ChatRoomName = '{wx_id}';")
        userNameListStr = ""
        if len(roomNames) > 0:
            userNameList = roomNames[0].get("UserNameList", "")
            array = userNameList.split("^G")
            if len(array) > 2:
                array = array[1:count + 1]
                userNameListStr = ','.join(f"'{x}'" for x in array)

        if userNameListStr:
            contacts = self.query_sql("MicroMsg.db",
                                      f"SELECT Remark, NickName FROM Contact t1 WHERE t1.UserName in ({userNameListStr});")
            # [{'NickName': '军xx', 'Remark': '张xx'}, {'NickName': '斌xx', 'Remark': '陈xx'}]
            if len(contacts) > 0:
                for item in contacts:
                    result += item.get("Remark") if item.get("Remark") else item.get("NickName")
                    result += "、"

        return result

    def get_ChatRoom_User(self, wxid, roomid) -> str:
        """是否已经在群内, 如果命中，则同时也返回群内其它成员"""
        contacts = self.query_sql("MicroMsg.db",
                                  f"SELECT UserNameList FROM ChatRoom t1 WHERE t1.ChatRoomName = '{roomid}' AND t1.UserNameList LIKE '%{wxid}%';")
        if len(contacts) > 0:
            return contacts[0].get("UserNameList", "")
        return ""

    def init_user_info(self) -> dict:
        # {'wxid': 'wang****da', 'name': '图*', 'mobile': '180****7525', 'home': 'F:\\文档备份\\WeChat Files\\'}
        self.userInfo = super().get_user_info()
        return self.userInfo

    def send_xml(self, receiver: str, xml: str, type: int, path: str = None) -> int:
        """发送 XML

        Args:
            receiver (str): 消息接收人，wxid 或者 roomid
            xml (str): xml 内容
            type (int): xml 类型，如：0x21 为小程序
            path (str): 封面图片路径

        Returns:
            int: 0 为成功，其他失败
        """
        req = wcf_pb2.Request()
        req.func = wcf_pb2.FUNC_SEND_XML  # FUNC_SEND_XML
        req.xml.receiver = receiver
        req.xml.content = xml
        req.xml.type = type
        if path:
            req.xml.path = path
        rsp = self._send_request(req)
        return rsp.status


if __name__ == '__main__':
    wcf = WcfSSS(port=10086, debug=True)
    data = wcf.getContact("chen199508148")
    list = wcf.getAllFriends()
    print(list)

frame/base/wcf_sss.py
wangtu/wechat_robot-manage-demo
class Constant:
    WCF_PORT = 10086
    WECHAT_VERSION = "3.9.2.23"
    VERSION = "V1.0"

    # 退出指令，回收各种系统参数，无需传参
    EXIT: str = "exit"

    SEND_WX_TEXT: str = "sendWxText"

    # 唤起新模块
    # 传递参数 from frame.vo.module_vo import ModuleVO
    SWITCH_QWIDGET: str = "switch_QWidget"
    # 传递参数 self, 继承BaseQWidgetMediator
    CLOSE_QWIDGET: str = "close_QWidget"

    # 删除子类，传递数组继承BaseQWidgetMediator,[baseMediator1,baseMediator2]
    CLOSE_CHILD_QWIDGET: str = "close_child_QWidget"

    START_WCF: str = "startWCF"

    START_WCF_SUCCESS: str = "startWCFSuccess"

    STOP_WCF: str = "stopWCF"

    STOP_WCF_SUCCESS: str = "stopWCFSuccess"

    # 获取角色权限成功后，刷新主界面数据
    UPDATE_USER_VIEW: str = "updateUserView"

    UPDATE_USER_VIEW_MESSAGE_COUNT: str = "updateUserView_message_count"

    UPDATE_USER_VIEW_KEY_COUNT: str = "updateUserView_key_count"

    # 发出html截图指令
    # 传递参数 from frame.vo.html2img_vo import Html2ImgVO
    HTML2IMG_START: str = "html2img_start"

    # 完成截图指令
    # 传递参数 from frame.vo.html2img_vo import Html2ImgVO
    HTML2IMG_START_COMPLETE: str = "html2img_complete"

frame/constant.py
wangtu/wechat_robot-manage-demo
import logging

import puremvc.interfaces
import puremvc.patterns.command
from frame.model.config_proxy import ConfigProxy
from frame.model.demo_proxy import DemoProxy
from frame.model.switch_QWidget_proxy import SwitchQWidgetProxy
from frame.view.wcf_mediator import WcfMediator

LOG = logging.getLogger("controller.py")


class StartupCommand(puremvc.patterns.command.SimpleCommand, puremvc.interfaces.ICommand):
    def execute(self, notification):
        LOG.info("StartupCommand start!")
        # 初始化config.yaml，并实例化日志
        self.facade.registerProxy(ConfigProxy())
        self.facade.registerProxy(DemoProxy())
        # 核心类，实现pyqt的UI初始化、关联mediator
        self.facade.registerProxy(SwitchQWidgetProxy())
        # 微信端控制mediator
        self.facade.registerMediator(WcfMediator())
        LOG.info("StartupCommand complete!")


class SwitchQWidgetCommand(puremvc.patterns.command.SimpleCommand, puremvc.interfaces.ICommand):
    def execute(self, notification):
        switchModuleProxy: SwitchQWidgetProxy = self.facade.retrieveProxy(SwitchQWidgetProxy.NAME)
        switchModuleProxy.loadQWidgetWindow(notification.getBody())


class CloseQWidgetCommand(puremvc.patterns.command.SimpleCommand, puremvc.interfaces.ICommand):
    def execute(self, notification):
        switchModuleProxy: SwitchQWidgetProxy = self.facade.retrieveProxy(SwitchQWidgetProxy.NAME)
        switchModuleProxy.closeQWidgetWindow(notification.getBody())


class CloseChildQWidgetCommand(puremvc.patterns.command.SimpleCommand, puremvc.interfaces.ICommand):
    def execute(self, notification):
        switchModuleProxy: SwitchQWidgetProxy = self.facade.retrieveProxy(SwitchQWidgetProxy.NAME)
        switchModuleProxy.closeChildQWidget(notification.getBody())

frame/controller.py
wangtu/wechat_robot-manage-demo
import logging.config
import os

import yaml

import puremvc.patterns.proxy


def _load_config() -> dict:
    pwd = os.path.dirname(os.path.abspath(__file__))
    # 顶级目录
    pwd = os.path.abspath(pwd + os.path.sep + ".." + os.path.sep + "..")
    try:
        with open(f"{pwd}/config.yaml", "rb") as fp:
            yconfig = yaml.safe_load(fp)
    except FileNotFoundError:
        pass

    return yconfig


class ConfigProxy(puremvc.patterns.proxy.Proxy):
    NAME = "ConfigProxy"

    def __init__(self, proxyName=None, data=None):
        super(ConfigProxy, self).__init__(ConfigProxy.NAME, [])
        # self.EMAIL = None
        # self.WENXIN = None
        # self.NEWS = None
        # self.GROUPS = None
        # self.JOB = None
        self.reload()

    def reload(self) -> None:
        """
        初始化配置文件
        :return:
        """
        yconfig: dict = _load_config()
        logging.config.dictConfig(yconfig["logging"])
        # self.GROUPS = yconfig["groups"]["enable"]
        # self.NEWS = yconfig["news"]["receivers"]
        # self.WENXIN = yconfig.get("wenxin")
        # self.HELP = yconfig.get("monitor_help")
        # self.JOB = yconfig.get("job")
        # self.EMAIL = yconfig.get("email")


if __name__ == '__main__':
    cur_path = os.path.dirname(os.path.abspath(__file__))
    cur_path = os.path.abspath(cur_path + os.path.sep + ".." + os.path.sep + "..")
    print(cur_path)

    config = ConfigProxy(None)
    config.reload()
    print(config.GROUPS)

frame/model/config_proxy.py
wangtu/wechat_robot-manage-demo
import puremvc.patterns.proxy


class DemoProxy(puremvc.patterns.proxy.Proxy):
    NAME = "DemoProxy"

    def __init__(self, proxyName=None, data=None):
        super(DemoProxy, self).__init__(DemoProxy.NAME, [])

    def onRegister(self):
        print("DemoProxy onRegister")

    def setData(self, data):
        self.data = data

    def getData(self):
        return self.data

frame/model/demo_proxy.py
wangtu/wechat_robot-manage-demo
import logging

from PyQt5.QtWidgets import QWidget, QStackedWidget

import puremvc.patterns.proxy
from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from frame.vo.module_vo import ModuleVO


class SwitchQWidgetProxy(puremvc.patterns.proxy.Proxy):
    NAME = "SwitchModuleProxy"

    def __init__(self, proxyName=None, data=None):
        super(SwitchQWidgetProxy, self).__init__(SwitchQWidgetProxy.NAME, [])
        self.LOG = logging.getLogger("SwitchQWidgetProxy")

        self.moduleDic = {}

    def onRegister(self):
        print("SwitchQWidgetProxy onRegister")

    def loadQWidgetWindow(self, module: ModuleVO):
        baseQWidgetMediator: BaseQWidgetMediator = self.moduleDic.get(str(module.mediatorClass))
        if baseQWidgetMediator is not None:
            print(f"{str(module.mediatorClass)} 已实例化，请勿重复请求")
            if isinstance(module.parentUI, QStackedWidget):
                module.parentUI.setCurrentWidget(baseQWidgetMediator)
            baseQWidgetMediator.show()
            return

        if module.mediatorClass.NAME is not None:
            baseQWidgetMediator = module.mediatorClass()
            # 实例化界面
            mainWindow: QWidget = baseQWidgetMediator.viewClass()
            self.moduleDic[str(module.mediatorClass)] = baseQWidgetMediator
            mainWindow.setupUi(baseQWidgetMediator)
            baseQWidgetMediator.show()

            if module.parentUI is not None:
                if isinstance(module.parentUI, QStackedWidget):
                    module.parentUI.addWidget(baseQWidgetMediator)
                    module.parentUI.setCurrentWidget(baseQWidgetMediator)
                elif isinstance(module.parentUI, QWidget):
                    module.parentUI.addWidget(baseQWidgetMediator)

            baseQWidgetMediator.setViewComponent(mainWindow)
            baseQWidgetMediator.data = module.data
            self.__registerMediator(baseQWidgetMediator, module)

    def closeQWidgetWindow(self, baseQWidgetMediator: BaseQWidgetMediator):
        self.LOG.info("closeQWidgetWindow！")
        self.facade.removeMediator(baseQWidgetMediator.getMediatorName())
        viewClass = str(type(baseQWidgetMediator))
        self.LOG.info(type(baseQWidgetMediator))
        if self.moduleDic.get(viewClass) is not None:
            print("删除字典")
            del self.moduleDic[viewClass]

        baseQWidgetMediator.close()

    def closeChildQWidget(self, _list: list):
        for baseQWidgetMediatorClass in _list:
            self.LOG.info("closeChildQWidget！" + baseQWidgetMediatorClass.NAME)
            self.facade.removeMediator(baseQWidgetMediatorClass.NAME)
            if self.moduleDic.get(str(baseQWidgetMediatorClass)) is not None:
                print("删除字典")
                del self.moduleDic[str(baseQWidgetMediatorClass)]

    def __registerMediator(self, baseMediator: BaseQWidgetMediator, vo: ModuleVO) -> bool:
        if self.facade.hasMediator(baseMediator.getMediatorName()):
            self.LOG.info("已经注册")
            pass
        else:
            self.facade.registerMediator(baseMediator)

    def setData(self, data):
        self.data = data

    def getData(self):
        return self.data

frame/model/switch_QWidget_proxy.py
wangtu/wechat_robot-manage-demo
import asyncio
import logging

from WenXinAPI.V1 import ChatBot
from frame.vo.chat_record_vo import ChatRecordVO
from util.sqlite_util import SqliteUtil

import puremvc.patterns.proxy
from util.dict_code_constant import DictCodeConstant

LOG = logging.getLogger("WeixinChatBotProxy.py")

# 文心一言的代理机器人，操控githubg 开源 V1对象
# 开源地址：https://github.com/umbrella-leaf/WenXinAPI/
""" 
该类的在本项目中，上游 由ai_user_proxy触发调度
本项目只处理发送逻辑
"""


class WeixinChatBotProxy(puremvc.patterns.proxy.Proxy):
    NAME = "WeixinChatBotProxy"

    def __init__(self, proxyName=None, data=None):
        super(WeixinChatBotProxy, self).__init__(WeixinChatBotProxy.NAME, [])
        self.default_chatbot: ChatBot = None
        self.self_chatbot: ChatBot = None
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        self._config = None

        self.LOG = logging.getLogger("WeixinChatBotProxy")

    def init_default_robot(self):  # 初始化配置启动机器人
        # 初始化默认机器人
        if self.default_chatbot is None:
            key = "HaV80rXWTocBSqBkOX5viwlp"
            secret = "oDYwigVjY4oCgX9c3zDhEjLCnAxgtONX"
            self.default_chatbot = ChatBot(key, secret)
        else:
            self.default_chatbot.prompt_conversations = {}
            self.default_chatbot.last_ask_time = {}
            self.default_chatbot.conversations = {"default": []}

    def init_self_robot(self):  # 初始化自定义百度文心机器人
        # 初始化自定义百度文心机器人
        db = SqliteUtil.get_instance()
        key = db.query_dict_value(DictCodeConstant.WENXIN_ROBOT_KEY)
        secret = db.query_dict_value(DictCodeConstant.WENXIN_ROBOT_SECRET)
        if len(key.strip()) == 0 or len(secret.strip()) == 0:
            return
        if self.self_chatbot is None:
            self.self_chatbot = ChatBot(key, secret)
        else:
            self.self_chatbot.api_key = key
            self.self_chatbot.secret_key = secret
            self.self_chatbot.prompt_conversations = {}
            self.self_chatbot.last_ask_time = {}
            self.self_chatbot.conversations = {"default": []}

    def init_self_prompt(self, prompt: str, convo_id: str = "default"):
        if self.self_chatbot and prompt:
            self.self_chatbot.add_prompt(prompt, convo_id)

    def init_default_prompt(self, prompt: str, convo_id: str = "default"):
        if self.default_chatbot:
            self.default_chatbot.add_prompt(prompt, convo_id)

    def init_self_robot2(self, key, secret):  # 初始化自定义百度文心机器人
        # 初始化自定义百度文心机器人
        if self.self_chatbot is None:
            self.self_chatbot = ChatBot(key, secret)
        else:
            self.self_chatbot.api_key = key
            self.self_chatbot.secret_key = secret
            self.self_chatbot.prompt_conversations = {}
            self.self_chatbot.last_ask_time = {}
            self.self_chatbot.conversations = {"default": []}

    def default_ask(self, vo: any) -> str:
        return self._ask(self.default_chatbot, vo)

    def self_ask(self, vo: any) -> str:
        return self._ask(self.self_chatbot, vo)

    def _ask(self, chat: ChatBot, vo: any) -> str | None:
        if chat is None:
            return None

        def _sync_ask(question: str, convo_id: str = "default") -> str:
            try:
                task1 = self.loop.create_task(chat.ask(question, convo_id))

                self.loop.run_until_complete(task1)
                rsp = task1.result()
            except Exception:
                rsp = ""
            return rsp

        answer = ""
        if isinstance(vo, ChatRecordVO):
            if vo.wxmsg.from_group():
                answer = _sync_ask(vo.question, vo.wxmsg.roomid)
            else:
                answer = _sync_ask(vo.question, vo.wxmsg.sender)

        elif isinstance(vo, str):
            answer = _sync_ask(vo)

        return answer

    def onRemove(self):
        pass


if __name__ == '__main__':
    _proxy = WeixinChatBotProxy(None)
    _proxy.init_default_robot()
    print("_ask  start")

    # -------测试百度文心prompt提示词---------
    _proxy.init_self_robot2("HaV80rXWTocBSqBkOX5viwlp", "oDYwigVjY4oCgX9c3zDhEjLCnAxgtONX")
    _proxy.init_self_prompt("你将扮演名字叫做小白的IT技术人")
    _ask = _proxy.self_ask("你是谁")
    print(_ask)

    # _ask = _proxy.default_ask("我字的拼音是什么")
    # print(f"_ask  step 2 {_ask}")
    # _ask = _proxy.default_ask("你字的拼音是什么")
    # print(f"_ask  step 3 {_ask}")
    # _ask = _proxy.default_ask("他字的拼音是什么")
    # print(f"_ask  step 4 {_ask}")

    # while True:
    #     q = input(">>> ")

frame/model/wenxin_chatbot_proxy.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtWidgets import QMainWindow

from frame.constant import Constant
from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from puremvc.patterns.mediator import Mediator


class BaseQMainWindowMediator(QMainWindow, BaseQWidgetMediator):
    NAME = 'BaseQMainWindowMediator'

    def __init__(self, mediatorName=None):
        QMainWindow.__init__(self)
        Mediator.__init__(self, mediatorName, None)

    def onRegister(self):
        self.LOG.info("MainWindowMediator onRegister")
        pass

    def closeEvent(self, event):
        """继承QWidget，实现关闭自身UI，以及对应的mediator"""
        print("BaseQMainWindowMediator closeEvent received!")
        self.sendNotification(Constant.CLOSE_QWIDGET, self)

    def onRemove(self):
        pass

frame/view/base_qmainwindow_mediator.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtWidgets import QWidget

from frame.constant import Constant
from puremvc.interfaces import IMediator
from puremvc.patterns.mediator import Mediator


class BaseQWidgetMediator(Mediator, IMediator, QWidget):
    NAME = 'ScreenBaseMediator'
    data: any = None  # 数据
    viewClass: QWidget = None  # UI界面

    def __init__(self, mediatorName=None):
        Mediator.__init__(self, mediatorName, None)
        QWidget.__init__(self)

    def onRegister(self):
        print("ScreenBaseMediator onRegister")

    def getSelectListWidget(self):
        """业务放这里虽然不合理，但是此项目是很方便"""
        from yyds.screen.main_window_mediator import MainWindowMediator
        mediator: MainWindowMediator = self.facade.retrieveMediator(MainWindowMediator.NAME)
        return mediator.getListItems()

    def closeEvent(self, event):
        """继承QWidget，实现关闭自身UI，以及对应的mediator"""
        print("BaseQWidgetMediator closeEvent received!")
        self.sendNotification(Constant.CLOSE_QWIDGET, self)

    def onRemove(self):
        print("ScreenBaseMediator onRemove")

frame/view/base_qwidget_mediator.py
wangtu/wechat_robot-manage-demo
import logging
import re
import threading
import xml.etree.ElementTree as ET
from queue import Empty

from wcferry import Wcf, WxMsg

import puremvc.interfaces
import puremvc.patterns.mediator
from frame.base.wcf_sss import WcfSSS
from frame.constant import Constant
from frame.model.config_proxy import ConfigProxy
from frame.vo.wx_text_vo import WxTextVO
from puremvc.patterns.observer import Notification


# 机器人控制层，启动注入dll，拦截手发操控微信客户端
# wechat控制指令参考，https://github.com/lich0821/WeChatRobot
# 微信注入、拦截、伪装、控制参考，https://github.com/lich0821/WeChatFerry
# WeChatFerry: 一个玩微信的工具，参考 https://mp.weixin.qq.com/s/CGLfSaNDy8MyuyPWGjGJ7w
class WcfMediator(puremvc.patterns.mediator.Mediator, puremvc.interfaces.IMediator):
    NAME = 'WcfMediator'

    def __init__(self, viewComponent=None):
        super(WcfMediator, self).__init__(WcfMediator.NAME, viewComponent)
        self.USER_INFO = None
        self.WX_ID = None
        self.wcf: WcfSSS = None
        self.LOG = logging.getLogger("WcfMediator")

        self.sayHelloProxy = None

    def onRegister(self):
        print("WcfMediator onRegister")

    def listNotificationInterests(self):
        return [
            Constant.SEND_WX_TEXT,
            Constant.EXIT,
            Constant.START_WCF,
            Constant.STOP_WCF,
        ]

    def handleNotification(self, notification: Notification):
        notName = notification.getName()
        vo = notification.getBody()
        if notName == Constant.EXIT:
            self.onRemove()
        elif notName == Constant.START_WCF:
            # 为了释放异步刷新界面的能力
            thread = threading.Thread(target=self.start_wcf)
            thread.start()
        elif notName == Constant.STOP_WCF:
            # 为了释放异步刷新界面的能力
            thread = threading.Thread(target=self.stop_wcf)
            thread.start()
        elif notName == Constant.SEND_WX_TEXT:
            if isinstance(vo, WxTextVO) and vo.answer:
                self.sendTextMsg(vo.answer, vo.receiver, vo.at_list)

    def start_wcf(self):
        self.LOG.info("正在启动机器人...")
        self.wcf = WcfSSS(port=Constant.WCF_PORT, debug=True)
        self.LOG.info("机器人启动成功！")
        if self.wcf.is_running():
            self.LOG.info("正在读取联系人信息...")
            self.wcf.getAllChatRooms()
            self.wcf.getAllFriends()
            self.LOG.info("联系人信息读取成功")
            self.LOG.info("正在读取登录信息...")

            self.USER_INFO = self.wcf.init_user_info()
            self.WX_ID = self.USER_INFO.get("wxid")
            print(self.USER_INFO)
            self.LOG.info(f"读取登录信息成功{self.WX_ID}")
            self.sendNotification(Constant.START_WCF_SUCCESS, self.wcf)
            # 侦听消息
            self.enableReceivingMsg()

    def stop_wcf(self):
        if self.wcf is None:
            self.LOG.info("机器人wcf尚未启动")
            return

        self.LOG.info("正在关闭机器人wcf...")
        self.wcf.cleanup()  # 退出前清理环境
        self.LOG.info("机器人退出成功！!")
        self.sendNotification(Constant.STOP_WCF_SUCCESS)

    def __getConfig(self) -> ConfigProxy:
        if self.config is not None:
            return self.config
        else:
            _config: ConfigProxy = self.facade.retrieveProxy(ConfigProxy.NAME)
            if _config is not None:
                self.config = _config
            return self.config

    def sendTextMsg(self, msg: str, receiver: str, at_list: str = "") -> None:
        """ 发送消息
        :param msg: 消息字符串
        :param receiver: 接收人wxid或者群id
        :param at_list: 要@的wxid, @所有人的wxid为：notify@all
        """
        # msg 中需要有 @ 名单中一样数量的 @
        ats = ""
        if at_list:
            if at_list == "notify@all":
                ats = "notify@all"
            else:
                wx_ids = at_list.split(",")
                for wx_id in wx_ids:
                    # 这里偷个懒，直接 @昵称。有必要的话可以通过 MicroMsg.db 里的 ChatRoom 表，解析群昵称
                    ats += f" @{self.wcf.getContact_NickName(wx_id)}"

        # {msg}{ats} 表示要发送的消息内容后面紧跟@，例如 北京天气情况为：xxx @张三，微信规定需这样写，否则@不生效
        if ats == "":
            self.LOG.info(f"To {receiver}: {msg}")
            self.wcf.send_text(f"{msg}", receiver, at_list)
        else:
            self.LOG.info(f"To {receiver}: {ats}\r{msg}")
            self.wcf.send_text(f"{ats} {msg}", receiver, at_list)

    def enableReceivingMsg(self) -> None:
        def innerProcessMsg(wcf: Wcf):
            while wcf.is_receiving_msg():
                try:
                    msg = wcf.get_msg()
                    if msg.from_group():
                        self.LOG.info(msg.roomid + ": " + msg.content)
                    else:
                        self.LOG.info(msg.sender + ": " + msg.content)
                    self.processMsg(msg)
                except Empty:
                    continue  # Empty message
                except Exception as e:
                    self.LOG.error(f"Receiving message error: {e}")

        self.wcf.enable_receiving_msg()
        threading.Thread(target=innerProcessMsg, name="GetMessage", args=(self.wcf,), daemon=True).start()

    def processMsg(self, msg: WxMsg) -> None:
        """当接收到消息的时候，会调用本方法。如果不实现本方法，则打印原始消息。
        此处可进行自定义发送的内容,如通过 msg.content 关键字自动获取当前天气信息，并发送到对应的群组@发送者
        群号：msg.roomid  微信ID：msg.sender  消息内容：msg.content
        content = "xx天气信息为："
        receivers = msg.roomid
        self.sendTextMsg(content, receivers, msg.sender)
        """
        # 非群聊信息，按消息类型进行处理
        print(msg.type, msg.from_group(), msg.sender)
        if msg.type == 37:  # 好友请求
            pass
        elif msg.type == 10000:  # 系统信息
            # self.sayHiToNewFriend(msg)
            pass
        elif msg.type == 0x01:  # 文本消息
            # 让配置加载更灵活，自己可以更新配置。也可以利用定时任务更新。
            # 自己发的文本消息，可以作命令控制后台
            if msg.from_self():
                pass
            else:
                question = re.sub(r"@.*?[\u2005|\s]", "", msg.content).replace(" ", "")
                msg.content = question
                self.LOG.info(msg.sender + ": " + msg.content)

    def autoAcceptFriendRequest(self, msg: WxMsg) -> None:
        # 自动添加好友
        try:
            xml = ET.fromstring(msg.content)
            v3 = xml.attrib["encryptusername"]
            v4 = xml.attrib["ticket"]
            scene = int(xml.attrib["scene"])
            self.wcf.accept_new_friend(v3, v4, scene)

        except Exception as e:
            self.LOG.error(f"同意好友出错：{e}")

    def onRemove(self):
        self.LOG.info("关闭机器人wcf机器")
        if self.wcf is not None:
            self.wcf.cleanup()  # 退出前清理环境

frame/view/wcf_mediator.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtWidgets import QWidget

from frame.view.base_qwidget_mediator import BaseQWidgetMediator


class ModuleVO:

    def __init__(self, mediatorClass: BaseQWidgetMediator, data: any = None):
        # 传递class类，通常是继承 baseQWidgetMediator
        # from frame.view.screen_base_mediator import BaseQWidgetMediator
        self.mediatorClass = mediatorClass
        # 已实例化的父容器，非必传
        self.parentUI: QWidget = None
        # 携带的参数
        self.data = data

    # # 携带的参数
    # data: any = None
    # # 传递class类
    # mediatorClass: ScreenBaseMediator = None

frame/vo/module_vo.py
wangtu/wechat_robot-manage-demo
class WxImgVO:
    img_path = ""
    receiver = None
    at_list = None

frame/vo/wx_img_vo.py
wangtu/wechat_robot-manage-demo
"""
主动推送文本信息
message_group_window 群发文本信息就有使用该对象
"""


class WxTextVO:
    answer = ""
    receiver = None
    at_list = None

frame/vo/wx_text_vo.py
wangtu/wechat_robot-manage-demo
class WxDbContact:
    # wxid,房间id，示例：25596501119@chatroom，wxid_j1jew5sfz9jr11 等
    UserName: str = None
    # 昵称
    NickName: str = None
    # 备注名称
    Remark: str = None
    # 拼音
    PYInitial: str = None
    # 备注拼音
    RemarkPYInitial: str = None
    # 头像
    smallHeadImgUrl: str = None

    # 群里面非好友的类型为4, 未知类型是2
    Type: int = 0

    # 0非群内， 1 群内好友
    ChatRoomType: int = 0

    # 微信头像
    SmallHeadImgUrl: str = None
    # 用来判断是否是公众号或服务号的字段
    VerifyFlag: int = 0

frame/vo/wxdb_contact_vo.py
wangtu/wechat_robot-manage-demo
import logging

from frame.appfacade import AppFacade
from frame.constant import Constant
from frame.vo.module_vo import ModuleVO
from yyds.screen.index_window_mediator import IndexWindowMediator

app: AppFacade = AppFacade.getInstance()
app1: AppFacade = AppFacade.getInstance()
LOG = logging.getLogger("main.py")


def print_hi(name):
    # 在下面的代码行中使用断点来调试脚本。
    print(f'Hi, {name}')  # 按 Ctrl+F8 切换断点。


def main():
    app.sendNotification(AppFacade.STARTUP, "hello puremvc")

    module: ModuleVO = ModuleVO(IndexWindowMediator, "pure switchview success")
    # module: ModuleVO = ModuleVO(MainWindowMediator)
    app.sendNotification(Constant.SWITCH_QWIDGET, module)


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication
    from PyQt5.QtCore import Qt

    # 解决界面模糊，缩放比例问题
    QApplication.setHighDpiScaleFactorRoundingPolicy(Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
    # 适应高DPI设备
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    # 解决图片在不同分辨率显示模糊问题
    QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps)
    # 更换windows风格
    # QApplication.setStyle(QStyleFactory.create('windows'))
    qApp = QApplication(sys.argv)
    print_hi('PyCharm')

    main()
    sys.exit(qApp.exec_())

main.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.interfaces
import puremvc.patterns.observer


class Controller(puremvc.interfaces.IController):
    """
    A Singleton C{IController} implementation.
    
    In PureMVC, the C{Controller} class follows the
    'Command and Controller' strategy, and assumes these 
    responsibilities:

    Remembering which C{ICommand}s 
    are intended to handle which C{INotifications}.
    
    Registering itself as an C{IObserver} with
    the C{View} for each C{INotification} 
    that it has an C{ICommand} mapping for.
    
    Creating a new instance of the proper C{ICommand}
    to handle a given C{INotification} when notified by the C{View}.
    
    Calling the C{ICommand}'s C{execute}
    method, passing in the C{INotification}. 
    
    Your application must register C{ICommands} with the 
    Controller.

    The simplest way is to subclass C{Facade}, 
    and use its C{initializeController} method to add your 
    registrations. 
    
    @see: L{View<puremvc.core.view.View>}
    @see: L{Observer<puremvc.patterns.observer.Observer>}
    @see: L{Notification<puremvc.patterns.observer.Notification>}
    @see: L{SimpleCommand<puremvc.patterns.command.SimpleCommand>}
    @see: L{MacroCommand<puremvc.patterns.command.MacroCommand>}
    """
    instance = None
    view = None
    commandMap = None

    def __new__(cls, *args, **kwargs):
        """
        This C{IController} implementation is a Singleton, so you should not call the constructor 
        directly, but instead call the static Singleton method C{Controller.getInstance()}
        """
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(Controller, cls).__new__(cls, *args, **kwargs)
            cls.instance.initializeController()
        return cls.instance

    @staticmethod
    def getInstance():
        """
        C{Controller} Singleton Static method.
        
        @return: the Singleton instance of C{Controller}
        """
        return Controller()

    def initializeController(self):
        """
        Initialize the Singleton C{Controller} instance.

        Called automatically by the constructor.

        Note that if you are using a subclass of C{View}
        in your application, you will need to initialize the view property
        """
        self.view = View.getInstance()
        self.commandMap = {}

    def executeCommand(self, note):
        """
        If an C{ICommand} has previously been registered 
        to handle a the given C{INotification}, then it is executed.
        
        @param note: an C{INotification}
        """
        commandClassRef = self.commandMap.get(note.getName(), None)
        if (commandClassRef == None):
            return

        commandInstance = commandClassRef()
        commandInstance.execute(note)

    def registerCommand(self, notificationName, commandClassRef):
        """
        Register a particular C{ICommand} class as the handler 
        for a particular C{INotification}.
        
        If an C{ICommand} has already been registered to 
        handle C{INotification}s with this name, it is no longer
        used, the new C{ICommand} is used instead.
        
        The Observer for the new ICommand is only created if this the 
        first time an ICommand has been regisered for this Notification name.
        
        @param notificationName: the name of the C{INotification}
        @param commandClassRef: the C{Class} of the C{ICommand}
        """
        if (self.commandMap.get(notificationName, None) == None):
            self.view.registerObserver(notificationName, puremvc.patterns.observer.Observer(self.executeCommand, self))

        self.commandMap[notificationName] = commandClassRef

    def hasCommand(self, notificationName):
        """
        Check if a Command is registered for a given Notification 
        
        @param notificationName: the name of the C{INotification}
        @return: whether a Command is currently registered for the given C{notificationName}.
        """
        return self.commandMap.get(notificationName, None) is not None

    def removeCommand(self, notificationName):
        """
        Remove a previously registered C{ICommand} to C{INotification} mapping.
        
        @param notificationName: the name of the C{INotification} to remove the C{ICommand} mapping for
        """
        if self.hasCommand(notificationName):
            self.view.removeObserver(notificationName, self)
            del self.commandMap[notificationName]


class Model(puremvc.interfaces.IModel):
    """
    A Singleton C{IModel} implementation.
    
    In PureMVC, the C{Model} class provides
    access to model objects (Proxies) by named lookup. 
    
    The C{Model} assumes these responsibilities:
    
    Maintain a cache of C{IProxy} instances.
    
    Provide methods for registering, retrieving, and removing C{IProxy} instances.
    
    Your application must register C{IProxy} instances 
    with the C{Model}. Typically, you use an 
    C{ICommand} to create and register C{IProxy} 
    instances once the C{Facade} has initialized the Core 
    actors.

    @see: L{Proxy<puremvc.patterns.proxy.Proxy>}
    @see: L{IProxy<puremvc.interfaces.IProxy>}
    """
    instance = None
    proxyMap = None

    def __new__(cls, *args, **kwargs):
        """
        This C{IModel} implementation is a Singleton, so you should not call the constructor 
        directly, but instead call the static Singleton method C{Model.getInstance()}
        """
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(Model, cls).__new__(cls, *args, **kwargs)
            cls.instance.initializeModel()
        return cls.instance

    @staticmethod
    def getInstance():
        """
        C{Model} Singleton Static method.

        @return: the Singleton instance of C{Model}
        """
        return Model()

    def initializeModel(self):
        """
        Initialize the Singleton C{Model} instance.

        Called automatically by the constructor.
        """
        self.proxyMap = {}

    def registerProxy(self, proxy):
        """
        Register an C{IProxy} with the C{Model}.
        
        @param proxy: an C{IProxy} to be held by the C{Model}.
        """
        self.proxyMap[proxy.getProxyName()] = proxy
        proxy.onRegister()

    def retrieveProxy(self, proxyName):
        """
        Retrieve an C{IProxy} from the C{Model}.
        
        @param proxyName: the name of the C{IProxy}
        @return: the C{IProxy} instance previously registered with the given C{proxyName}.
        """
        return self.proxyMap.get(proxyName, None)

    def hasProxy(self, proxyName):
        """
        Check if a Proxy is registered
        
        @param proxyName: the name of the C{IProxy}
        @return: whether a Proxy is currently registered with the given C{proxyName}.
        """
        return self.proxyMap.get(proxyName, None) is not None

    def removeProxy(self, proxyName):
        """
        Remove an C{IProxy} from the C{Model}.
        
        @param proxyName: name of the C{IProxy} instance to be removed.
        @return: the C{IProxy} that was removed from the C{Model}
        """
        proxy = self.proxyMap.get(proxyName, None)
        if proxy:
            del self.proxyMap[proxyName]
            proxy.onRemove()
        return proxy


class View(puremvc.interfaces.IView):
    """
    A Singleton C{IView} implementation.
    
    In PureMVC, the C{View} class assumes these responsibilities:

    Maintain a cache of C{IMediator} instances.
    
    Provide methods for registering, retrieving, and removing C{IMediators}.
    
    Notifiying C{IMediators} when they are registered or removed.
    
    Managing the observer lists for each C{INotification} in the application.
    
    Providing a method for attaching C{IObservers} to an C{INotification}'s observer list.
    
    Providing a method for broadcasting an C{INotification}.
    
    Notifying the C{IObservers} of a given C{INotification} when it broadcast.

    
    @see: L{Mediator<puremvc.patterns.mediator.Mediator>}
    @see: L{Observer<puremvc.patterns.observer.Observer>}
    @see: L{Notification<puremvc.patterns.observer.Notification>}
    """
    instance = None
    observerMap = None
    mediatorMap = None

    def __new__(cls, *args, **kwargs):
        """
        This C{iView} implementation is a Singleton, so you should not call the constructor 
        directly, but instead call the static Singleton method C{View.getInstance()}
        """
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(View, cls).__new__(cls, *args, **kwargs)
            cls.instance.initializeView()
        return cls.instance

    @staticmethod
    def getInstance():
        """
        C{View} Singleton Static method.

        @return: the Singleton instance of C{View}
        """
        return View()

    def initializeView(self):
        """
        Initialize the Singleton C{View} instance.

        Called automatically by the constructor.
        """
        self.observerMap = {}
        self.mediatorMap = {}

    def registerObserver(self, notificationName, observer):
        """
        Register an C{IObserver} to be notified
        of C{INotifications} with a given name.
        
        @param notificationName: the name of the C{INotifications} to notify this C{IObserver} of
        @param observer: the C{IObserver} to register
        """
        if not notificationName in self.observerMap:
            self.observerMap[notificationName] = []
        self.observerMap[notificationName].append(observer)

    def notifyObservers(self, notification):
        """
        Notify the C{IObservers} for a particular C{INotification}.
        
        All previously attached C{IObservers} for this C{INotification}'s
        list are notified and are passed a reference to the C{INotification} in 
        the order in which they were registered.
        
        @param notification: the C{INotification} to notify C{IObservers} of.
        """
        observers = self.observerMap.get(notification.getName(), [])[:]
        for obsvr in observers:
            obsvr.notifyObserver(notification)

    def removeObserver(self, notificationName, notifyContext):
        """
        Remove the observer for a given notifyContext from an observer list for a given Notification name.
        
        @param notificationName: which observer list to remove from 
        @param notifyContext: remove the observer with this object as its notifyContext
        """
        observers = self.observerMap[notificationName]

        for i in range(len(observers) - 1, -1, -1):
            if observers[i].compareNotifyContext(notifyContext):
                observers.pop(i)
                break

        if len(observers) == 0:
            del self.observerMap[notificationName]

    def registerMediator(self, mediator):
        """
        Register an C{IMediator} instance with the C{View}.

        Registers the C{IMediator} so that it can be retrieved by name,
        and further interrogates the C{IMediator} for its 
        C{INotification} interests.

        If the C{IMediator} returns any C{INotification} 
        names to be notified about, an C{Observer} is created encapsulating 
        the C{IMediator} instance's C{handleNotification} method 
        and registering it as an C{Observer} for all C{INotifications} the 
        C{IMediator} is interested in.
        
        @param mediator: a reference to the C{IMediator} instance
        """
        # do not allow re-registration (you must to removeMediator fist)
        if mediator.getMediatorName() in self.mediatorMap:
            return

        self.mediatorMap[mediator.getMediatorName()] = mediator
        interests = mediator.listNotificationInterests()
        if len(interests) > 0:
            obsvr = puremvc.patterns.observer.Observer(mediator.handleNotification, mediator)

            for i in range(0, len(interests)):
                self.registerObserver(interests[i], obsvr)

        mediator.onRegister()

    def retrieveMediator(self, mediatorName):
        """
        Retrieve an C{IMediator} from the C{View}.
        
        @param mediatorName: the name of the C{IMediator} instance to retrieve.
        @return: the C{IMediator} instance previously registered with the given C{mediatorName}.
        """
        return self.mediatorMap.get(mediatorName, None)

    def removeMediator(self, mediatorName):
        """
        Remove an C{IMediator} from the C{View}.
        
        @param mediatorName: name of the C{IMediator} instance to be removed.
        @return: the C{IMediator} that was removed from the C{View}
        """
        for notificationName in list(self.observerMap.keys()):
            observers = self.observerMap[notificationName]
            for i in range(len(observers) - 1, -1, -1):
                if observers[i].compareNotifyContext(self.retrieveMediator(mediatorName)) == True:
                    observers.pop(i)

            if len(observers) == 0:
                del self.observerMap[notificationName]

        mediator = self.mediatorMap.get(mediatorName, None)

        if mediator is not None:
            del self.mediatorMap[mediatorName]
            mediator.onRemove()
        return mediator

    def hasMediator(self, mediatorName):
        """
        Check if a Mediator is registered or not
        
        @param mediatorName: the name of the C{IMediator}
        @return: whether a Mediator is registered with the given C{mediatorName}.
        """
        return self.mediatorMap.get(mediatorName, None) is not None

puremvc/core.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""


class ICommand(object):
    """
    The interface definition for a PureMVC Command.

    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def execute(self, notification):
        """
        Execute the C{ICommand}'s logic to handle a given C{INotification}.

        Raises C{NotImplemented} if subclass does not implement this method

        @param notification: an C{INotification} to handle.
        """
        raise NotImplemented


class IController(object):
    """
    The interface definition for a PureMVC Controller.

    In PureMVC, an C{IController} implementor 
    follows the 'Command and Controller' strategy, and 
    assumes these responsibilities:

    Remembering which C{ICommand}s 
    are intended to handle which C{INotifications}.

    Registering itself as an C{IObserver} with
    the C{View} for each C{INotification} 
    that it has an C{ICommand} mapping for.
  
    Creating a new instance of the proper C{ICommand}
    to handle a given C{INotification} when notified by the C{View}.
  
    Calling the C{ICommand}'s C{execute}
    method, passing in the C{INotification}.
    

    @see: L{INotification<puremvc.interfaces.INotification>}
    @see: L{ICommand<puremvc.interfaces.ICommand>}
    """

    def registerCommand(self, notificationName, commandClassRef):
        """
        Register a particular C{ICommand} class as the handler for a particular C{INotification}.
        
        @param notificationName: the name of the C{INotification}
        @param commandClassRef: the Class of the C{ICommand}
        """
        raise NotImplemented

    def executeCommand(self, notification):
        """
        Execute the C{ICommand} previously registered as thehandler for C{INotification}s with the given notification name.
        
        @param notification: the C{INotification} to execute the associated C{ICommand} for
        """
        raise NotImplemented

    def removeCommand(self, notificationName):
        """
        Remove a previously registered C{ICommand} to C{INotification} mapping.
        
        @param notificationName: the name of the C{INotification} to remove the C{ICommand} mapping fo
        """
        raise NotImplemented

    def hasCommand(self, notificationName):
        """
        Check if a Command is registered for a given Notification 
        
        @param notificationName: the name of the C{INotification}
        @return: whether a Command is currently registered for the given C{notificationName}.
        """
        raise NotImplemented


class IFacade(object):
    """
    The interface definition for a PureMVC Facade.
    
    The Facade Pattern suggests providing a single
    class to act as a central point of communication 
    for a subsystem. 
    
    In PureMVC, the Facade acts as an interface between
    the core MVC actors (Model, View, Controller) and
    the rest of your application.
    
    @see: L{IModel<puremvc.interfaces.IModel>}
    @see: L{IView<puremvc.interfaces.IView>}
    @see: L{IController<puremvc.interfaces.IController>}
    @see: L{ICommand<puremvc.interfaces.ICommand>}
    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def notifyObservers(self, note):
        """
        Notify the C{IObserver}s for a particular C{INotification}.

        All previously attached IObservers for this INotification's list are notified 
        and are passed a reference to the INotification in the order in which they were registered.

        NOTE: Use this method only if you are sending custom Notifications. Otherwise use the 
        sendNotification method which does not require you to create the Notification instance.
        
        @param note: the C{INotification} to notify C{IObserver}s of.
        """
        raise NotImplemented

    def registerProxy(self, proxy):
        """
        Register an C{IProxy} with the C{Model} by name.
        
        @param proxy: the C{IProxy} to be registered with the C{Model}.
        """
        raise NotImplemented

    def retreieveProxy(self, proxyName):
        """
        Retrieve a C{IProxy} from the C{Model} by name.
        
        @param proxyName: the name of the C{IProxy} instance to be retrieved.
        @return: the C{IProxy} previously regisetered by C{proxyName} with the C{Model}.
        """
        raise NotImplemented

    def removeProxy(self, proxyName):
        """
        Remove an C{IProxy} instance from the C{Model} by name.
        
        @param proxyName: the C{IProxy} to remove from the C{Model}.
        @return: the C{IProxy} that was removed from the C{Model}
        """
        raise NotImplemented

    def hasProxy(self, proxyName):
        """
        Check if a Proxy is registered
        
        @param proxyName:
        @return: whether a Proxy is currently registered with the given C{proxyName}.
        """
        raise NotImplemented

    def registerCommand(self, noteName, commandClassRef):
        """
        Register an C{ICommand} with the C{Controller}.
        
        @param noteName: the name of the C{INotification} to associate the C{ICommand} with.
        @param commandClassRef: a reference to the C{Class} of the C{ICommand}.
        """
        raise NotImplemented

    def removeCommand(self, notificationName):
        """
         Remove a previously registered C{ICommand} to C{INotification} mapping from the Controller.
 
        @param notificationName: the name of the C{INotification} to remove the C{ICommand} mapping for
        """
        raise NotImplemented

    def hasCommand(self, notificationName):
        """
        Check if a Command is registered for a given Notification 

        @param notificationName: the name of the C{INotification}
        @return: whether a Command is currently registered for the given C{notificationName}.
        """
        raise NotImplemented

    def registerMediator(self, mediator):
        """
        Register an C{IMediator} instance with the C{View}.
 
        @param mediator: a reference to the C{Mediator} instance
        """
        raise NotImplemented

    def retreieveMediator(self, mediatorName):
        """
        Retrieve an C{IMediator} instance from the C{View}.
        
        @param mediatorName: the name of the C{IMediator} instance to retrievve
        @return: the C{IMediator} previously registered with the given C{mediatorName}.
        """
        raise NotImplemented

    def removeMediator(self, mediatorName):
        """
        Remove a C{IMediator} instance from the C{View}.
        
        @param mediatorName: name of the C{IMediator} instance to be removed.
        @return: the C{IMediator} instance previously registered with the given C{mediatorName}.
        """
        raise NotImplemented


class IMediator(object):
    """
    The interface definition for a PureMVC Mediator.
    
    In PureMVC, C{IMediator} implementors assume these responsibilities:

    Implement a common method which returns a list of all C{INotification}s the C{IMediator} has interest in.
    
    Implement a notification callback method.
    
    Implement methods that are called when the IMediator is registered or removed from the View.
    
    
    Additionally, C{IMediator}s typically:

    Act as an intermediary between one or more view components such as text boxes or list controls, maintaining references and coordinating their behavior.
    
    In Flash-based apps, this is often the place where event listeners are
    added to view components, and their handlers implemented.
    
    Respond to and generate C{INotifications}, interacting with of the rest of the PureMVC app.
    
    When an C{IMediator} is registered with the C{IView}, the C{IView} will call the C{IMediator}'s C{listNotificationInterests} method. The C{IMediator} will 
    return an C{List} of C{INotification} names which 
    it wishes to be notified about.
    

    The C{IView} will then create an C{Observer} object 
    encapsulating that C{IMediator}'s (C{handleNotification}) method
    and register it as an Observer for each C{INotification} name returned by 
    C{listNotificationInterests}.
    
    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def getMediatorName(self):
        """
        Get the C{IMediator} instance name
        
        @return: the C{IMediator} instance name
        """
        raise NotImplemented

    def getViewComponent(self):
        """
        Get the C{IMediator}'s view component.
        
        @return: the view component
        """
        raise NotImplemented

    def setViewComponent(self, viewComponent):
        """
        Set the C{IMediator}'s view component.
        
        @param viewComponent: the view component
        """
        raise NotImplemented

    def listNotificationInterests(self):
        """
        List C{INotification} interests.
        
        @return: an C{List} of the C{INotification} names this C{IMediator} has an interest in.
        """
        raise NotImplemented

    def handleNotification(self, notification):
        """
        Handle an C{INotification}.
        
        @param notification: the C{INotification} to be handled
        """
        raise NotImplemented

    def onRegister(self):
        """
        Called by the View when the Mediator is registered
        """
        raise NotImplemented

    def onRemove(self):
        """
        Called by the View when the Mediator is removed
        """
        raise NotImplemented


class IModel(object):
    """
    The interface definition for a PureMVC Model.
    
    In PureMVC, C{IModel} implementors provide
    access to C{IProxy} objects by named lookup.
    
    An C{IModel} assumes these responsibilities:
    
    Maintain a cache of C{IProxy} instances and Provide methods for registering, retrieving, and removing C{IProxy} instances
    """

    def registerProxy(self, proxy):
        """
        Register an C{IProxy} instance with the C{Model}.
        
        @param proxy: an object reference to be held by the C{Model}.
        """
        raise NotImplemented

    def retrieveProxy(self, proxyName):
        """
        Retrieve an C{IProxy} instance from the Model.
        
        @param proxyName: name of the C{IProxy} instance to retrieve.
        @return: the C{IProxy} instance previously registered with the given C{proxyName}.
        """
        raise NotImplemented

    def removeProxy(self, proxyName):
        """
        Remove an C{IProxy} instance from the Model.
        
        @param proxyName: name of the C{IProxy} instance to be removed.
        @return: the C{IProxy} that was removed from the C{Model}
        """
        raise NotImplemented

    def hasProxy(self, proxyName):
        """
        Check if a Proxy is registered
        
        @param proxyName: name of the C{IProxy} instance
        @return: whether a Proxy is currently registered with the given C{proxyName}.
        """
        raise NotImplemented


class INotification(object):
    """
    The interface definition for a PureMVC Notification.

    PureMVC does not rely upon underlying event models such 
    as the one provided with Flash, and ActionScript 3 does 
    not have an inherent event model.
    
    The Observer Pattern as implemented within PureMVC exists 
    to support event-driven communication between the 
    application and the actors of the MVC triad.
    
    Notifications are not meant to be a replacement for Events
    in Flex/Flash/AIR. Generally, C{IMediator} implementors
    place event listeners on their view components, which they
    then handle in the usual way. This may lead to the broadcast of C{Notification}s to 
    trigger C{ICommand}s or to communicate with other C{IMediators}. C{IProxy} and C{ICommand}
    instances communicate with each other and C{IMediator}s by broadcasting C{INotification}s.
    
    A key difference between Flash C{Event}s and PureMVC 
    C{Notification}s is that C{Event}s follow the 
    'Chain of Responsibility' pattern, 'bubbling' up the display hierarchy 
    until some parent component handles the C{Event}, while
    PureMVC C{Notification}s follow a 'Publish/Subscribe'
    pattern. PureMVC classes need not be related to each other in a 
    parent/child relationship in order to communicate with one another
    using C{Notification}s.
    
    @see: L{IView<puremvc.interfaces.IView>}
    @see: L{IObserver<puremvc.interfaces.IObserver>}
    """

    def getName(self):
        """
        Get the name of the C{INotification} instance. 
        """
        raise NotImplemented

    def setBody(self, body):
        """
        Set the body of the C{INotification} instance
        """
        raise NotImplemented

    def getBody(self):
        """
        Get the body of the C{INotification} instance
        """
        raise NotImplemented

    def setType(self, type):
        """
        Set the type of the C{INotification} instance
        """
        raise NotImplemented

    def getType(self):
        """
        Get the type of the C{INotification} instance
        """
        raise NotImplemented

    def str(self):
        """
        Get the string representation of the C{INotification} instance
        """
        raise NotImplemented


class INotifier(object):
    """
    The interface definition for a PureMVC Notifier.
    
    C{MacroCommand, Command, Mediator} and C{Proxy}
    all have a need to send C{Notifications}.
    
    The C{INotifier} interface provides a common method called
    C{sendNotification} that relieves implementation code of 
    the necessity to actually construct C{Notifications}.
    
    The C{Notifier} class, which all of the above mentioned classes
    extend, also provides an initialized reference to the C{Facade}
    Singleton, which is required for the convienience method
    for sending C{Notifications}, but also eases implementation as these
    classes have frequent C{Facade} interactions and usually require
    access to the facade anyway.
    
    @see: L{IFacade<puremvc.interfaces.IFacade>}
    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def sendNotification(self, notificationName, body=None, type=None):
        """
        Send a C{INotification}.

        Convenience method to prevent having to construct new 
        notification instances in our implementation code.
        
        @param notificationName: the name of the notification to send
        @param body: the body of the notification (optional)
        @param type: the type of the notification (optional)
        """
        raise NotImplemented


class IObserver(object):
    """
    The interface definition for a PureMVC Observer.

    In PureMVC, C{IObserver} implementors assume these responsibilities:

    Encapsulate the notification (callback) method of the interested object.
    
    Encapsulate the notification context of the interested object.
    
    Provide methods for setting the interested object' notification method and context.
    
    Provide a method for notifying the interested object.
    

    PureMVC does not rely upon underlying event
    models such as the one provided with Flash,
    and ActionScript 3 does not have an inherent
    event model.
    

    The Observer Pattern as implemented within
    PureMVC exists to support event driven communication
    between the application and the actors of the
    MVC triad.
    

    An Observer is an object that encapsulates information
    about an interested object with a notification method that
    should be called when an C{INotification} is broadcast. The Observer then
    acts as a proxy for notifying the interested object.
    
    
    Observers can receive C{Notification}s by having their
    C{notifyObserver} method invoked, passing
    in an object implementing the C{INotification} interface, such
    as a subclass of C{Notification}.
    
    @see: L{IView<puremvc.interfaces.IView>}
    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def setNotifyMethod(self, notifyMethod):
        """
        Set the notification method.
        
        The notification method should take one parameter of type C{INotification}
        
        @param notifyMethod: the notification (callback) method of the interested object
        """
        raise NotImplemented

    def setNotifyContext(self, notifyContext):
        """
        Set the notification context.
        
        @param notifyContext: the notification context of the interested object
        """
        raise NotImplemented

    def notifyObserver(self, notification):
        """
        Notify the interested object.
        
        @param notification: the C{INotification} to pass to the interested object's notification method
        """
        raise NotImplemented

    def compareNotifyContext(self, object):
        """
        Compare the given object to the notificaiton context object.
        
        @param object: the object to compare.
        @return: boolean indicating if the notification context and the object are the same.
        """
        raise NotImplemented


class IProxy(object):
    """
    The interface definition for a PureMVC Proxy.

    In PureMVC, C{IProxy} implementors assume these responsibilities:

    Implement a common method which returns the name of the Proxy.
    
    Provide methods for setting and getting the data object.
    
    Additionally, C{IProxy}s typically:

    Maintain references to one or more pieces of model data.
    Provide methods for manipulating that data.
    Generate C{INotifications} when their model data changes.
    Expose their name as a C{public static const} called C{NAME}, if they are not instantiated multiple times.
    Encapsulate interaction with local or remote services used to fetch and persist model data.
    """

    def getProxyName(self):
        """
        Get the Proxy name
        
        @return: the Proxy instance name
        """
        raise NotImplemented

    def setData(self, data):
        """
        Set the data object
         
         @param data: the data object
        """
        raise NotImplemented

    def getData(self):
        """
        Get the data object
         
         @return: the data as type Object
        """
        raise NotImplemented

    def onRegister(self):
        """
        Called by the Model when the Proxy is registered
        """
        raise NotImplemented

    def onRemove(self):
        """
        Called by the Model when the Proxy is removed
        """
        raise NotImplemented


class IView(object):
    """
    The interface definition for a PureMVC View.
    
    In PureMVC, C{IView} implementors assume these responsibilities:
    
    In PureMVC, the C{View} class assumes these responsibilities:
    
    Maintain a cache of C{IMediator} instances.
    
    Provide methods for registering, retrieving, and removing C{IMediators}.
    
    Managing the observer lists for each C{INotification} in the application.
    
    Providing a method for attaching C{IObservers} to an C{INotification}'s observer list.
    
    Providing a method for broadcasting an C{INotification}.
    
    Notifying the C{IObservers} of a given C{INotification} when it broadcast.
    
    @see: L{IMediator<puremvc.interfaces.IMediator>}
    @see: L{IObserver<puremvc.interfaces.IObserver>}
    @see: L{INotification<puremvc.interfaces.INotification>}
    """

    def registerObserver(self, notificationName, observer):
        """
        Register an C{IObserver} to be notified of C{INotifications} with a given name.
        
        @param notificationName: the name of the C{INotifications} to notify this C{IObserver} of
        @param observer: the C{IObserver} to register
        """
        raise NotImplemented

    def notifyObservers(self, notification):
        """
        Notify the C{IObservers} for a particular C{INotification}.
        
        All previously attached C{IObservers} for this C{INotification}'s
        list are notified and are passed a reference to the C{INotification} in 
        the order in which they were registered.</P>
        
        @param notification: the C{INotification} to notify C{IObservers} of.
        """
        raise NotImplemented

    def registerMediator(self, mediator):
        """
        Register an C{IMediator} instance with the C{View}.
        
        Registers the C{IMediator} so that it can be retrieved by name,
        and further interrogates the C{IMediator} for its 
        C{INotification} interests.

        If the C{IMediator} returns any C{INotification} 
        names to be notified about, an C{Observer} is created encapsulating 
        the C{IMediator} instance's C{handleNotification} method 
        and registering it as an C{Observer} for all C{INotifications} the 
        C{IMediator} is interested in.
        
        @param mediator: a reference to the C{IMediator} instance
        """
        raise NotImplemented

    def retrieveMediator(self, mediatorName):
        """
        Retrieve an C{IMediator} from the C{View}.
        
        @param mediatorName: the name of the C{IMediator} instance to retrieve.
        @return: the C{IMediator} instance previously registered with the given C{mediatorName}.
        """
        raise NotImplemented

    def removeMediator(self, mediatorName):
        """
        Remove an C{IMediator} from the C{View}.
        
        @param mediatorName: name of the C{IMediator} instance to be removed.
        @return: the C{IMediator} that was removed from the C{View}
        """
        raise NotImplemented

    def hasMediator(self, mediatorName):
        """
        Check if a Mediator is registered or not
        
        @param mediatorName: name of the C{IMediator}
        @return: whether a Mediator is registered with the given C{mediatorName}.
        """
        raise NotImplemented

puremvc/interfaces.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.interfaces
import puremvc.patterns.observer


class MacroCommand(puremvc.patterns.observer.Notifier, puremvc.interfaces.ICommand, puremvc.interfaces.INotifier):
    """
    A base C{ICommand} implementation that executes other C{ICommand}s.

    A C{MacroCommand} maintains an list of C{ICommand} Class references called I{SubCommands}.
    
    When C{execute} is called, the C{MacroCommand} 
    instantiates and calls C{execute} on each of its I{SubCommands} turn.
    Each I{SubCommand} will be passed a reference to the original
    C{INotification} that was passed to the C{MacroCommand}'s 
    C{execute} method.
    
    Unlike C{SimpleCommand}, your subclass
    should not override C{execute}, but instead, should 
    override the C{initializeMacroCommand} method, 
    calling C{addSubCommand} once for each I{SubCommand}
    to be executed.
    
    @see: L{Controller<puremvc.core.controller.Controller>}
    @see: L{Notification<puremvc.patterns.observer.Notification>}
    @see: L{SimpleCommand<puremvc.patterns.command.SimpleCommand>}
    """

    subCommands = None

    def __init__(self):
        """
        MacroCommand Constructor
        
        You should not need to define a constructor, 
        instead, override the C{initializeMacroCommand}
        method.
        """
        self.subCommands = []
        self.initializeMacroCommand()

    def initializeMacroCommand(self):
        """
        Initialize the C{MacroCommand}.
        
        In your subclass, override this method to 
        initialize the C{MacroCommand}'s I{SubCommand}
        list with C{ICommand} class references like 
        this:
        
        Note that I{SubCommand}s may be any C{ICommand} implementor,
        C{MacroCommand}s or C{SimpleCommands} are both acceptable.
        """
        pass

    def addSubCommand(self, commandClassRef):
        """
        Add a I{SubCommand}.
        
        The I{SubCommands} will be called in First In/First Out (FIFO)
        order.
        
        @param commandClassRef: a reference to the C{Class} of the C{ICommand}.
        """
        self.subCommands.append(commandClassRef)

    def execute(self, notification):
        """
        Execute this C{MacroCommand}'s I{SubCommands}.
        
        The I{SubCommands} will be called in First In/First Out (FIFO)
        order. 
        
        @param notification: the C{INotification} object to be passsed to each I{SubCommand}.
        """
        while len(self.subCommands) > 0:
            commandClassRef = self.subCommands.pop(0)
            commandInstance = commandClassRef()
            commandInstance.execute(notification)


class SimpleCommand(puremvc.patterns.observer.Notifier, puremvc.interfaces.ICommand, puremvc.interfaces.INotifier):
    """
    A base C{ICommand} implementation.
    
    Your subclass should override the C{execute} 
    method where your business logic will handle the C{INotification}.
    
    @see: L{Controller<puremvc.core.controller.Controller>}
    @see: L{Notification<puremvc.patterns.observer.Notification>}
    @see: L{MacroCommand<puremvc.patterns.command.MacroCommand>}
    """

    def execute(self, notification):
        """
        Fulfill the use-case initiated by the given C{INotification}.
        
        In the Command Pattern, an application use-case typically
        begins with some user action, which results in an C{INotification} being broadcast, which 
        is handled by business logic in the C{execute} method of an
        C{ICommand}.
        
        @param notification: the C{INotification} to handle.
        """
        pass

puremvc/patterns/command.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.core
import puremvc.interfaces
import puremvc.patterns.observer


class Facade(puremvc.interfaces.IFacade):
    """
    A base Singleton C{IFacade} implementation.
    
    In PureMVC, the C{Facade} class assumes these 
    responsibilities:
    
    Initializing the C{Model}, C{View} and C{Controller} Singletons.

    Providing all the methods defined by the C{IModel, IView, & IController} interfaces.
    
    Providing the ability to override the specific C{Model}, C{View} and C{Controller} Singletons created.
    
    Providing a single point of contact to the application for registering C{Commands} and notifying C{Observers}

    
    @see: L{Model<org.puremvc.as3.core.model.Model>}
    @see: L{View<org.puremvc.as3.core.view.View>}
    @see: L{Controller<org.puremvc.as3.core.controller.Controller>}
    @see: L{Notification<org.puremvc.as3.patterns.observer.Notification>}
    @see: L{Mediator<org.puremvc.as3.patterns.mediator.Mediator>}
    @see: L{Proxy<org.puremvc.as3.patterns.proxy.Proxy>}
    @see: L{SimpleCommand<org.puremvc.as3.patterns.command.SimpleCommand>}
    @see: L{MacroCommand<org.puremvc.as3.patterns.command.MacroCommand>}
    """

    instance = None
    controller = None
    model = None
    view = None

    def __new__(cls, *args, **kwargs):
        """
        This C{IFacade} implementation is a Singleton, so you should not call the constructor 
        directly, but instead call the static Singleton method C{Facade.getInstance()}
        """
        if not cls.instance or not isinstance(cls.instance, cls):
            cls.instance = super(Facade, cls).__new__(cls, *args, **kwargs)
            cls.instance.initializeFacade()
        return cls.instance

    @staticmethod
    def getInstance():
        """
        C{Facade} Singleton Static method.
        
        @return: the Singleton instance of C{Facade}
        """
        return Facade()

    def initializeFacade(self):
        """
        Initialize the Singleton C{Facade} instance.
        
        Called automatically by the constructor. Override in your
        subclass to do any subclass specific initializations. Be
        sure to call C{Facade.initializeFacade()}, though.
        """
        self.initializeController()
        self.initializeModel()
        self.initializeView()

    def initializeController(self):
        """
        Initialize the C{Controller}.
        
        Called by the C{initializeFacade} method.
        Override this method in your subclass of C{Facade} 
        if one or both of the following are true:

        You wish to initialize a different C{IController}.
        You have C{Commands} to register with the C{Controller} at startup.      

        If you don't want to initialize a different C{IController}, 
        call C{super.initializeController()} at the beginning of your method, then register C{Proxy}s.
        
        Note: This method is I{rarely} overridden; in practice you are more
        likely to use a C{Command} to create and register C{Proxy}s
        with the C{Model}, since C{Proxy}s with mutable data will likely
        need to send C{INotification}s and thus will likely want to fetch a reference to 
        the C{Facade} during their construction.
        """
        if (self.controller is not None):
            return
        self.controller = puremvc.core.Controller.getInstance()

    def initializeModel(self):
        """
        Initialize the C{Model}.
        
        Called by the C{initializeFacade} method.
        Override this method in your subclass of C{Facade} 
        if one or both of the following are true:
            
        You wish to initialize a different C{IModel}.
        
        You have C{Proxy}s to register with the Model that do not 
        retrieve a reference to the Facade at construction time.

        If you don't want to initialize a different C{IModel}, 
        call C{super.initializeModel()} at the beginning of your
        method, then register C{Proxy}s.

        Note: This method is I{rarely} overridden; in practice you are more
        likely to use a C{Command} to create and register C{Proxy}s
        with the C{Model}, since C{Proxy}s with mutable data will likely
        need to send C{INotification}s and thus will likely want to fetch a reference to 
        the C{Facade} during their construction.
        """
        if (self.model is not None):
            return
        self.model = puremvc.core.Model.getInstance()

    def initializeView(self):
        """
        Initialize the C{View}.
        

        Called by the C{initializeFacade} method.
        Override this method in your subclass of C{Facade} 
        if one or both of the following are true:

        You wish to initialize a different C{IView}.
        
        You have C{Observers} to register with the C{View}

        If you don't want to initialize a different C{IView}, 
        call C{super.initializeView()} at the beginning of your
        method, then register C{IMediator} instances.

        Note: This method is I{rarely} overridden; in practice you are more
        likely to use a C{Command} to create and register C{Mediator}s
        with the C{View}, since C{IMediator} instances will need to send 
        C{INotification}s and thus will likely want to fetch a reference 
        to the C{Facade} during their construction.
        """
        if (self.view is not None):
            return
        self.view = puremvc.core.View.getInstance()

    def registerCommand(self, notificationName, commandClassRef):
        """
        Register an C{ICommand} with the C{Controller} by Notification name.
        
        @param notificationName: the name of the C{INotification} to associate the C{ICommand} with
        @param commandClassRef: a reference to the Class of the C{ICommand}
        """
        self.controller.registerCommand(notificationName, commandClassRef)

    def removeCommand(self, notificationName):
        """
        Remove a previously registered C{ICommand} to C{INotification} mapping from the Controller.
        
        @param notificationName: the name of the C{INotification} to remove the C{ICommand} mapping for
        """
        self.controller.removeCommand(notificationName)

    def hasCommand(self, notificationName):
        """
        Check if a Command is registered for a given Notification 
        
        @param notificationName: the name of the C{INotification}
        @return: whether a Command is currently registered for the given C{notificationName}.
        """
        return self.controller.hasCommand(notificationName)

    def registerProxy(self, proxy):
        """
        Register an C{IProxy} with the C{Model} by name.

        @param proxy: the C{IProxy} instance to be registered with the C{Model}.
        """
        self.model.registerProxy(proxy)

    def retrieveProxy(self, proxyName):
        """
        Retrieve an C{IProxy} from the C{Model} by name.
        
        @param proxyName: the name of the proxy to be retrieved.
        @return: the C{IProxy} instance previously registered with the given C{proxyName}.
        """
        return self.model.retrieveProxy(proxyName)

    def removeProxy(self, proxyName):
        """
        Remove an C{IProxy} from the C{Model} by name.

        @param proxyName: the C{IProxy} to remove from the C{Model}.
        @return: the C{IProxy} that was removed from the C{Model}
        """
        proxy = None
        if (self.model is not None):
            proxy = self.model.removeProxy(proxyName)
        return proxy

    def hasProxy(self, proxyName):
        """
        Check if a Proxy is registered
        
        @param proxyName: the name of the C{IProxy}
        @return: whether a Proxy is currently registered with the given C{proxyName}.
        """
        return self.model.hasProxy(proxyName)

    def registerMediator(self, mediator):
        """
        Register a C{IMediator} with the C{View}.
        
        @param mediator: a reference to the C{IMediator}
        """
        if (self.view is not None):
            self.view.registerMediator(mediator)

    def retrieveMediator(self, mediatorName):
        """
        Retrieve an C{IMediator} from the C{View}.
        
        @param mediatorName: the name of the C{IMediator}
        @return: the C{IMediator} previously registered with the given C{mediatorName}.
        """
        return self.view.retrieveMediator(mediatorName)

    def removeMediator(self, mediatorName):
        """
        Remove an C{IMediator} from the C{View}.
        
        @param mediatorName: name of the C{IMediator} to be removed.
        @return: the C{IMediator} that was removed from the C{View}
        """
        mediator = None
        if (self.view is not None):
            mediator = self.view.removeMediator(mediatorName)
        return mediator

    def hasMediator(self, mediatorName):
        """
        Check if a Mediator is registered or not
        
        @param mediatorName: the name of the C{IMediator}
        @return: whether a Mediator is registered with the given C{mediatorName}.
        """
        return self.view.hasMediator(mediatorName)

    def sendNotification(self, notificationName, body=None, type=None):
        """
        Create and send an C{INotification}.
        
        Keeps us from having to construct new notification 
        instances in our implementation code.
        
        @param notificationName: the name of the notiification to send
        @param body: the body of the notification (optional)
        @param type: the type of the notification (optional)
        """
        self.notifyObservers(puremvc.patterns.observer.Notification(notificationName, body, type))

    def notifyObservers(self, notification):
        """
        Notify C{Observer}s.

        This method is left public mostly for backward 
        compatibility, and to allow you to send custom 
        notification classes using the facade.
 
        Usually you should just call sendNotification
        and pass the parameters, never having to 
        construct the notification yourself.
        
        @param notification: the C{INotification} to have the C{View} notify C{Observers} of.
        """
        if (self.view is not None):
            self.view.notifyObservers(notification)

puremvc/patterns/facade.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.interfaces
import puremvc.patterns.facade
import puremvc.patterns.observer


class Mediator(puremvc.patterns.observer.Notifier, puremvc.interfaces.IMediator, puremvc.interfaces.INotifier):
    """
    A base C{IMediator} implementation. 
     
    @see: L{View<org.puremvc.as3.core.view.View>}
    """

    NAME = "Mediator"
    facade = None
    viewComponent = None
    mediatorName = None

    def __init__(self, mediatorName=None, viewComponent=None):
        """
        Mediator Constructor
        
        Typically, a C{Mediator} will be written to serve
        one specific control or group controls and so,
        will not have a need to be dynamically named.
        """
        self.facade = puremvc.patterns.facade.Facade.getInstance()
        mediatorName = mediatorName or self.NAME
        if mediatorName is None:
            raise ValueError("Madiator name cannot be None")
        self.mediatorName = mediatorName
        self.viewComponent = viewComponent

    def getMediatorName(self):
        """
        Get the name of the C{Mediator}.
        @return: the Mediator name
        """
        return self.mediatorName

    def setViewComponent(self, viewComponent):
        """
        Set the C{IMediator}'s view component.
        
        @param viewComponent: the view component
        """
        self.viewComponent = viewComponent

    def getViewComponent(self):
        """
        Get the C{Mediator}'s view component.
        
        @return: the view component
        """
        return self.viewComponent

    def listNotificationInterests(self):
        """
        List the C{INotification} names this
        C{Mediator} is interested in being notified of.
        
        @return: List the list of C{INotification} names
        """
        return []

    def handleNotification(self, notification):
        """
        Handle C{INotification}s.
        
        Typically this will be handled in a if/else statement,
        with one 'comparison' entry per C{INotification}
        the C{Mediator} is interested in.
        """
        pass

    def onRegister(self):
        """
        Called by the View when the Mediator is registered
        """
        pass

    def onRemove(self):
        """
        Called by the View when the Mediator is removed
        """
        pass

puremvc/patterns/mediator.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.interfaces
import puremvc.patterns.facade


class Observer(puremvc.interfaces.IObserver):
    """
    A base C{IObserver} implementation.
    
    An C{Observer} is an object that encapsulates information
    about an interested object with a method that should 
    be called when a particular C{INotification} is broadcast.
    
    In PureMVC, the C{Observer} class assumes these responsibilities:

    Encapsulate the notification (callback) method of the interested object.
    
    Encapsulate the notification context (this) of the interested object.
    
    Provide methods for setting the notification method and context.
    
    Provide a method for notifying the interested object.
    
    @see: L{View<org.puremvc.as3.core.view.View>}
    @see: L{Notification<org.puremvc.as3.patterns.observer.Notification>}
    """
    notify = None
    context = None

    def __init__(self, notifyMethod, notifyContext):
        """
        Constructor. 
        
        The notification method on the interested object should take 
        one parameter of type C{INotification}
        
        @param notifyMethod: the notification method of the interested object
        @param notifyContext: the notification context of the interested object
        """
        self.setNotifyMethod(notifyMethod)
        self.setNotifyContext(notifyContext)

    def setNotifyMethod(self, notifyMethod):
        """
        Set the notification method.
        
        The notification method should take one parameter of type C{INotification}.
        
        @param notifyMethod: the notification (callback) method of the interested object.
        """
        self.notify = notifyMethod

    def setNotifyContext(self, notifyContext):
        """
        Set the notification context.
        
        @param notifyContext: the notification context (this) of the interested object.
        """
        self.context = notifyContext

    def getNotifyMethod(self):
        """
        Get the notification method.
        
        @return: the notification (callback) method of the interested object.
        """
        return self.notify

    def getNotifyContext(self):
        """
        Get the notification context.
        
        @return: the notification context (C{this}) of the interested object.
        """
        return self.context

    def notifyObserver(self, notification):
        """
        Notify the interested object.
        
        @param notification: the C{INotification} to pass to the interested object's notification method.
        """
        self.getNotifyMethod()(notification)

    def compareNotifyContext(self, obj):
        """
        Compare an object to the notification context. 
        
        @param obj: the object to compare
        @return: boolean indicating if the object and the notification context are the same
        """
        return (obj is self.context)


class Notifier(puremvc.interfaces.INotifier):
    """
    A Base C{INotifier} implementation.
    
    C{MacroCommand, Command, Mediator} and C{Proxy} 
    all have a need to send C{Notifications}.

    The C{INotifier} interface provides a common method called
    C{sendNotification} that relieves implementation code of 
    the necessity to actually construct C{Notifications}.

    The C{Notifier} class, which all of the above mentioned classes
    extend, provides an initialized reference to the C{Facade}
    Singleton, which is required for the convienience method
    for sending C{Notifications}, but also eases implementation as these
    classes have frequent C{Facade} interactions and usually require
    access to the facade anyway.
    
    @see: L{Facade<org.puremvc.as3.patterns.facade.Facade>}
    @see: L{Mediator<org.puremvc.as3.patterns.mediator.Mediator>}
    @see: L{Proxy<org.puremvc.as3.patterns.proxy.Proxy>}
    @see: L{SimpleCommand<org.puremvc.as3.patterns.command.SimpleCommand>}
    @see: L{MacroCommand<org.puremvc.as3.patterns.command.MacroCommand>}
    """

    facade = None

    def __init__(self):
        """
        Notifier Constructor
        """
        self.facade = puremvc.patterns.facade.Facade.getInstance()

    def sendNotification(self, notificationName, body=None, type=None):
        """
        Create and send an C{INotification}.
        
        Keeps us from having to construct new INotification 
        instances in our implementation code.
        
        @param notificationName: the name of the notiification to send
        @param body: the body of the notification (optional)
        @param type: the type of the notification (optional)
        """
        self.facade.sendNotification(notificationName, body, type)


class Notification(puremvc.interfaces.INotification):
    """
    A base C{INotification} implementation.
    
    PureMVC does not rely upon underlying event models such 
    as the one provided with Flash, and ActionScript 3 does 
    not have an inherent event model.</P>
    
    The Observer Pattern as implemented within PureMVC exists 
    to support event-driven communication between the 
    application and the actors of the MVC triad.</P>
    
    Notifications are not meant to be a replacement for Events
    in Flex/Flash/Apollo. Generally, C{IMediator} implementors
    place event listeners on their view components, which they
    then handle in the usual way. This may lead to the broadcast of C{Notification}s to 
    trigger C{ICommand}s or to communicate with other C{IMediators}. C{IProxy} and C{ICommand}
    instances communicate with each other and C{IMediator}s 
    by broadcasting C{INotification}s.</P>
    
    A key difference between Flash C{Event}s and PureMVC 
    C{Notification}s is that C{Event}s follow the 
    'Chain of Responsibility' pattern, 'bubbling' up the display hierarchy 
    until some parent component handles the C{Event}, while
    PureMVC C{Notification}s follow a 'Publish/Subscribe'
    pattern. PureMVC classes need not be related to each other in a 
    parent/child relationship in order to communicate with one another
    using C{Notification}s.
    
    @see: L{Observer<org.puremvc.as3.patterns.observer.Observer>}
    """

    name = None
    body = None
    type = None

    def __init__(self, name, body=None, type=None):
        """
        Constructor. 
        
        @param name: name of the C{Notification} instance. (required)
        @param body: the C{Notification} body. (optional)
        @param type; the type of the C{Notification} (optional)
        """
        self.name = name
        self.body = body
        self.type = type

    def getName(self):
        """
        Get the name of the C{Notification} instance.
        
        @return: the name of the C{Notification} instance.
        """
        return self.name

    def setBody(self, body):
        """
        Set the body of the C{Notification} instance.
        """
        self.body = body

    def getBody(self):
        """
        Get the body of the C{Notification} instance.
        
        @return: the body object.
        """
        return self.body

    def setType(self, type):
        """
        Set the type of the C{Notification} instance.
        """
        self.type = type;

    def getType(self):
        """
        Get the type of the C{Notification} instance.
        
        @return: the type
        """
        return self.type;

    def str(self):
        """
        Get the string representation of the C{Notification} instance.
        
        @return: the string representation of the C{Notification} instance.
        """
        msg = "Notification Name: " + self.getName();

        bd = "None"
        if self.body is not None:
            bd = str(self.body)

        ty = "None"
        if self.type is not None:
            ty = self.type

        msg += "\nBody:" + bd
        msg += "\nType:" + ty
        return msg;

puremvc/patterns/observer.py
wangtu/wechat_robot-manage-demo
"""
 PureMVC Python Port by Toby de Havilland <toby.de.havilland@puremvc.org> 
 PureMVC - Copyright(c) 2006-08 Futurescale, Inc., Some rights reserved. 
 Your reuse is governed by the Creative Commons Attribution 3.0 License 
"""

import puremvc.interfaces
import puremvc.patterns.facade
import puremvc.patterns.observer


class Proxy(puremvc.patterns.observer.Notifier, puremvc.interfaces.IProxy, puremvc.interfaces.INotifier):
    """
    A base C{IProxy} implementation. 
    
    In PureMVC, C{Proxy} classes are used to manage parts of the 
    application's data model.
    
    A C{Proxy} might simply manage a reference to a local data object, 
    in which case interacting with it might involve setting and 
    getting of its data in synchronous fashion.
    
    C{Proxy} classes are also used to encapsulate the application's 
    interaction with remote services to save or retrieve data, in which case, 
    we adopt an asyncronous idiom; setting data (or calling a method) on the 
    C{Proxy} and listening for a C{Notification} to be sent 
    when the C{Proxy} has retrieved the data from the service.
    
    @see: L{Model<org.puremvc.as3.core.model.Model>}
    """

    NAME = "Proxy"
    facade = None
    proxyName = None
    data = None

    def __init__(self, proxyName=None, data=None):
        """
        Proxy Constructor
        
        @param proxyName: the name of the proxy instance (optional)
        @param data: the proxy data (optional)
        """
        self.facade = puremvc.patterns.facade.Facade.getInstance()
        proxyName = proxyName or self.NAME
        if proxyName is None:
            raise ValueError("Proxy name cannot be None")
        self.proxyName = proxyName
        if data:
            self.setData(data)

    def getProxyName(self):
        """
        Get the Proxy name
        
        @return: the proxy name
        """
        return self.proxyName

    def setData(self, data):
        """
        Set the Proxy data
        
        @param data: the Proxy data object
        """
        self.data = data

    def getData(self):
        """
        Get the proxy data
        
        @return: the Proxy data object
        """
        return self.data

    def onRegister(self):
        """
        Called by the Model when the Proxy is registered
        """
        pass

    def onRemove(self):
        """
        Called by the Model when the Proxy is removed
        """
        pass

puremvc/patterns/proxy.py
wangtu/wechat_robot-manage-demo
"""
coding:utf-8
file: common_util.py
@desc:
"""
import datetime
import uuid

import yaml
from PyQt5.QtWidgets import QMessageBox


def read_yaml(path):
    with open(path, encoding='utf-8') as f:
        stm = f.read()
    content = yaml.load(stm, Loader=yaml.FullLoader)
    return content


def get_uuid():
    return str(uuid.uuid1()).replace('-', '')


def msg_box(widget, title, msg):
    QMessageBox.warning(widget, title, msg, QMessageBox.Yes)


def accept_box(widget, title, msg):
    return QMessageBox.warning(widget, title, msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No)


def get_current_time():
    dt = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    return dt


def get_return_day(day):
    return (datetime.datetime.now() + datetime.timedelta(days=day)).strftime('%Y-%m-%d %H:%M:%S')

util/common_util.py
wangtu/wechat_robot-manage-demo
import re
import winreg


class SoftwareManager:
    def __init__(self):
        self.sub_keys = [
            r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
            r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
        ]
        self.__installed = self.__load_all_installed()

    def __get_value(self, each_key, name):
        try:
            value = winreg.QueryValueEx(each_key, name)[0]
        except Exception:
            value = None
        return value

    def __load_all_installed(self):
        installed = []
        for sub_key in self.sub_keys:
            with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, sub_key) as sub_key_handle:
                for i in range(winreg.QueryInfoKey(sub_key_handle)[0]):
                    try:
                        each_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,
                                                  f'{sub_key}\\{winreg.EnumKey(sub_key_handle, i)}')
                    except Exception as e:
                        ...
                    else:
                        DisplayName = self.__get_value(each_key, "DisplayName")
                        if not DisplayName:
                            continue
                        installed.append({
                            "DisplayName": DisplayName,
                            "DisplayVersion": self.__get_value(each_key, "DisplayVersion"),
                            "DisplayIcon": self.__get_value(each_key, "DisplayIcon"),
                            "InstallDate": self.__get_value(each_key, "InstallDate"),
                            "InstallLocation": self.__get_value(each_key, "InstallLocation"),
                            "Publisher": self.__get_value(each_key, "Publisher"),
                        })
                    finally:
                        each_key.Close()
        return installed

    def get_all(self):
        """
        获取所有安装列表
        :return:
        """
        return self.__installed

    def get_by_name(self, name):
        """
        通过名字查找已安装软件
        :param name:
        :return:
        """
        one = [i for i in self.__installed if i.get('DisplayName') == name]
        return one[0] if one else None

    def re_search(self, pattern, flags=0):
        """
        通过正则搜索匹配项
        :param pattern:
        :param flags:
        :return:
        """
        return [i for i in self.__installed if re.search(pattern=pattern, string=i.get('DisplayName', ''), flags=flags)]


if __name__ == '__main__':
    manager = SoftwareManager()

    # 通过名字获取
    wechat = manager.get_by_name("微信")
    print(
        wechat['DisplayName'],  # 软件名
        wechat['DisplayVersion'],  # 软件版本
        wechat['InstallLocation'],  # 软件安装路径
    )

util/software_manager.py
wangtu/wechat_robot-manage-demo
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMessageBox

from frame.base.signal_thread_tuple import SignalThreadTuple
from frame.base.wcf_sss import WcfSSS


def permission_wcf(func):
    """切面权限判断，必须先启动wcf才允许操作函数"""

    def wrapper(self, *args, **kwargs):
        if WcfSSS.getInstance() is None:
            show_message("提示", "请先到tab启动页首次启动程序")
            return
        if not WcfSSS.getInstance().is_running():
            show_message("提示", "请先到tab启动页重新启动程序")
            return

        res = func(self)
        return res

    return wrapper


def show_message(title, content):
    # 异步刷新界面，为了性能，可读性是弱了点
    done_thread.data = (title, content)
    done_thread.start()


def show_message2(data):
    msg_box = QMessageBox(QMessageBox.Question, data[0], data[1])  # 退出表示弹出框标题，"你确定退出吗？"表示弹出框的内容
    msg_box.setTextInteractionFlags(Qt.TextSelectableByMouse)
    msg_box.exec_()  # 执行弹出框


done_thread = SignalThreadTuple()
done_thread.signal.connect(show_message2)

yyds/aop/permission_aop.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'aichat_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(437, 528)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.key_count_label = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.key_count_label.sizePolicy().hasHeightForWidth())
        self.key_count_label.setSizePolicy(sizePolicy)
        self.key_count_label.setStyleSheet("")
        self.key_count_label.setObjectName("key_count_label")
        self.horizontalLayout_3.addWidget(self.key_count_label)
        self.limit_label = QtWidgets.QLabel(Form)
        self.limit_label.setObjectName("limit_label")
        self.horizontalLayout_3.addWidget(self.limit_label)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.hisTableWidget = QtWidgets.QTableWidget(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.hisTableWidget.sizePolicy().hasHeightForWidth())
        self.hisTableWidget.setSizePolicy(sizePolicy)
        self.hisTableWidget.setMinimumSize(QtCore.QSize(100, 0))
        self.hisTableWidget.setObjectName("hisTableWidget")
        self.hisTableWidget.setColumnCount(4)
        self.hisTableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(3, item)
        self.verticalLayout.addWidget(self.hisTableWidget)
        self.delButton = QtWidgets.QPushButton(Form)
        self.delButton.setObjectName("delButton")
        self.verticalLayout.addWidget(self.delButton)
        self.verticalLayout_3.addLayout(self.verticalLayout)
        self.line = QtWidgets.QFrame(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
        self.line.setSizePolicy(sizePolicy)
        self.line.setMinimumSize(QtCore.QSize(20, 2))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setLineWidth(1)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout_3.addWidget(self.line)
        self.formLayout_2 = QtWidgets.QFormLayout()
        self.formLayout_2.setObjectName("formLayout_2")
        self.label_3 = QtWidgets.QLabel(Form)
        self.label_3.setObjectName("label_3")
        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_3)
        self.range_comboBox = QtWidgets.QComboBox(Form)
        self.range_comboBox.setObjectName("range_comboBox")
        self.range_comboBox.addItem("")
        self.range_comboBox.addItem("")
        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.range_comboBox)
        self.verticalLayout_3.addLayout(self.formLayout_2)
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setObjectName("tabWidget")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.horizontalLayout = QtWidgets.QHBoxLayout(self.tab_3)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.formLayout = QtWidgets.QFormLayout()
        self.formLayout.setLabelAlignment(QtCore.Qt.AlignCenter)
        self.formLayout.setFormAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
        self.formLayout.setVerticalSpacing(10)
        self.formLayout.setObjectName("formLayout")
        self.label_4 = QtWidgets.QLabel(self.tab_3)
        self.label_4.setMinimumSize(QtCore.QSize(50, 0))
        self.label_4.setObjectName("label_4")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_4)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.sys_robot_comboBox = QtWidgets.QComboBox(self.tab_3)
        self.sys_robot_comboBox.setObjectName("sys_robot_comboBox")
        self.horizontalLayout_4.addWidget(self.sys_robot_comboBox)
        self.line_2 = QtWidgets.QFrame(self.tab_3)
        self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.horizontalLayout_4.addWidget(self.line_2)
        self.formLayout.setLayout(0, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_4)
        self.label = QtWidgets.QLabel(self.tab_3)
        self.label.setObjectName("label")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
        self.default_prompt_plainTextEdit = QtWidgets.QPlainTextEdit(self.tab_3)
        self.default_prompt_plainTextEdit.setMaximumSize(QtCore.QSize(16777215, 100))
        self.default_prompt_plainTextEdit.setReadOnly(True)
        self.default_prompt_plainTextEdit.setObjectName("default_prompt_plainTextEdit")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.default_prompt_plainTextEdit)
        self.horizontalLayout.addLayout(self.formLayout)
        self.tabWidget.addTab(self.tab_3, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tab_4)
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.formLayout_3 = QtWidgets.QFormLayout()
        self.formLayout_3.setObjectName("formLayout_3")
        self.label_6 = QtWidgets.QLabel(self.tab_4)
        self.label_6.setObjectName("label_6")
        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_6)
        self.wenxin_key_lineEdit = QtWidgets.QLineEdit(self.tab_4)
        self.wenxin_key_lineEdit.setMaxLength(100)
        self.wenxin_key_lineEdit.setObjectName("wenxin_key_lineEdit")
        self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.wenxin_key_lineEdit)
        self.label_7 = QtWidgets.QLabel(self.tab_4)
        self.label_7.setObjectName("label_7")
        self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_7)
        self.wenxin_secret_lineEdit = QtWidgets.QLineEdit(self.tab_4)
        self.wenxin_secret_lineEdit.setMaxLength(50)
        self.wenxin_secret_lineEdit.setObjectName("wenxin_secret_lineEdit")
        self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.wenxin_secret_lineEdit)
        self.label_8 = QtWidgets.QLabel(self.tab_4)
        self.label_8.setObjectName("label_8")
        self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_8)
        self.wenxin_prompt_lineEdit = QtWidgets.QLineEdit(self.tab_4)
        self.wenxin_prompt_lineEdit.setMaxLength(500)
        self.wenxin_prompt_lineEdit.setReadOnly(False)
        self.wenxin_prompt_lineEdit.setObjectName("wenxin_prompt_lineEdit")
        self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.wenxin_prompt_lineEdit)
        self.test_wenxin_pushButton = QtWidgets.QPushButton(self.tab_4)
        self.test_wenxin_pushButton.setObjectName("test_wenxin_pushButton")
        self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.test_wenxin_pushButton)
        self.verticalLayout_5.addLayout(self.formLayout_3)
        self.tabWidget.addTab(self.tab_4, "")
        self.tab_5 = QtWidgets.QWidget()
        self.tab_5.setObjectName("tab_5")
        self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.tab_5)
        self.verticalLayout_6.setObjectName("verticalLayout_6")
        self.formLayout_4 = QtWidgets.QFormLayout()
        self.formLayout_4.setObjectName("formLayout_4")
        self.gpt3_base_url_lineEdit = QtWidgets.QLineEdit(self.tab_5)
        self.gpt3_base_url_lineEdit.setObjectName("gpt3_base_url_lineEdit")
        self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.gpt3_base_url_lineEdit)
        self.label_9 = QtWidgets.QLabel(self.tab_5)
        self.label_9.setObjectName("label_9")
        self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label_9)
        self.gpt3_key_lineEdit = QtWidgets.QLineEdit(self.tab_5)
        self.gpt3_key_lineEdit.setMaxLength(100)
        self.gpt3_key_lineEdit.setObjectName("gpt3_key_lineEdit")
        self.formLayout_4.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.gpt3_key_lineEdit)
        self.label_10 = QtWidgets.QLabel(self.tab_5)
        self.label_10.setObjectName("label_10")
        self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_10)
        self.gpt3_proxy_lineEdit = QtWidgets.QLineEdit(self.tab_5)
        self.gpt3_proxy_lineEdit.setMaxLength(50)
        self.gpt3_proxy_lineEdit.setObjectName("gpt3_proxy_lineEdit")
        self.formLayout_4.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.gpt3_proxy_lineEdit)
        self.label_11 = QtWidgets.QLabel(self.tab_5)
        self.label_11.setObjectName("label_11")
        self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_11)
        self.gpt3_prompt_lineEdit = QtWidgets.QLineEdit(self.tab_5)
        self.gpt3_prompt_lineEdit.setMaxLength(500)
        self.gpt3_prompt_lineEdit.setReadOnly(False)
        self.gpt3_prompt_lineEdit.setObjectName("gpt3_prompt_lineEdit")
        self.formLayout_4.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.gpt3_prompt_lineEdit)
        self.test_gpt3_pushButton = QtWidgets.QPushButton(self.tab_5)
        self.test_gpt3_pushButton.setObjectName("test_gpt3_pushButton")
        self.formLayout_4.setWidget(4, QtWidgets.QFormLayout.FieldRole, self.test_gpt3_pushButton)
        self.label_2 = QtWidgets.QLabel(self.tab_5)
        self.label_2.setObjectName("label_2")
        self.formLayout_4.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.verticalLayout_6.addLayout(self.formLayout_4)
        self.tabWidget.addTab(self.tab_5, "")
        self.verticalLayout_3.addWidget(self.tabWidget)
        self.label_5 = QtWidgets.QLabel(Form)
        self.label_5.setWordWrap(True)
        self.label_5.setObjectName("label_5")
        self.verticalLayout_3.addWidget(self.label_5)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setSpacing(1)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout_4 = QtWidgets.QVBoxLayout()
        self.verticalLayout_4.setSpacing(10)
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.verticalLayout_2.addLayout(self.verticalLayout_4)
        self.addBtn = QtWidgets.QPushButton(Form)
        self.addBtn.setObjectName("addBtn")
        self.verticalLayout_2.addWidget(self.addBtn)
        self.verticalLayout_3.addLayout(self.verticalLayout_2)

        self.retranslateUi(Form)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.key_count_label.setText(_translate("Form", "已配置AI机器人列表"))
        self.limit_label.setText(_translate("Form", "系统AI当天剩余问答次数："))
        item = self.hisTableWidget.horizontalHeaderItem(0)
        item.setText(_translate("Form", "配置联系人"))
        item = self.hisTableWidget.horizontalHeaderItem(1)
        item.setText(_translate("Form", "AI名称"))
        item = self.hisTableWidget.horizontalHeaderItem(2)
        item.setText(_translate("Form", "AI类型"))
        item = self.hisTableWidget.horizontalHeaderItem(3)
        item.setText(_translate("Form", "AI提示词"))
        self.delButton.setText(_translate("Form", "移除AI智能聊天"))
        self.label_3.setText(_translate("Form", "限定范围自动回答："))
        self.range_comboBox.setItemText(0, _translate("Form", "左侧列表的群和好友"))
        self.range_comboBox.setItemText(1, _translate("Form", "全部群和好友"))
        self.label_4.setText(_translate("Form", "选择扮演角色："))
        self.label.setText(_translate("Form", "prompt提示词："))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "系统默认机器人"))
        self.label_6.setText(_translate("Form", "API Key："))
        self.wenxin_key_lineEdit.setPlaceholderText(_translate("Form", "输入百度文心API Key"))
        self.label_7.setText(_translate("Form", "Secret Key："))
        self.wenxin_secret_lineEdit.setPlaceholderText(_translate("Form", "输入百度文心Secret Key"))
        self.label_8.setText(_translate("Form", "prompt提示词："))
        self.wenxin_prompt_lineEdit.setPlaceholderText(
            _translate("Form", "角色设定，如你扮演名字叫做小白，你是一名IT技术人"))
        self.test_wenxin_pushButton.setText(_translate("Form", "检测配置，(默认提问：你是谁)"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "自定义百度文心"))
        self.gpt3_base_url_lineEdit.setPlaceholderText(_translate("Form", "接口地址，如果你不知道这是干嘛的，就不要改"))
        self.label_9.setText(_translate("Form", "API Key："))
        self.gpt3_key_lineEdit.setPlaceholderText(_translate("Form", "填写你 ChatGPT 的 key"))
        self.label_10.setText(_translate("Form", "代理proxy:"))
        self.gpt3_proxy_lineEdit.setPlaceholderText(_translate("Form", "f非必填，可以为空，或填国内代理，域名或IP:端口号"))
        self.label_11.setText(_translate("Form", "prompt提示词："))
        self.gpt3_prompt_lineEdit.setPlaceholderText(_translate("Form", "角色设定，如 你是智能聊天机器人，你叫XX"))
        self.test_gpt3_pushButton.setText(_translate("Form", "检测配置，(默认提问：你是谁)"))
        self.label_2.setText(_translate("Form", "API_BASE_URL:"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_5), _translate("Form", "自定义ChatGPT3.5"))
        self.label_5.setText(_translate("Form", "AI智能配置说明：\n"
                                                "1、使用系统默认自带机器人占服务器资源，AI答复次数以用户等级每日限额\n"
                                                "2、自定义机器人配置保存您本地，无答复次数限制\n"
                                                "3、自定义机器人配置信息全局唯一，修改会覆盖配置"))
        self.addBtn.setText(_translate("Form", "添加AI智能聊天"))

yyds/child/aichat_group_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.aichat_group_window import Ui_Form


class AIChatGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'AIChatGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(AIChatGroupWindowMediator, self).__init__(AIChatGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("AIChatGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("AIChatGroupWindowMediator onRegister")

    def onRemove(self):
        self.LOG.info("AIChatGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/aichat_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'job_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(436, 545)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
        self.verticalLayout_2.setSpacing(1)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.key_count_label = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.key_count_label.sizePolicy().hasHeightForWidth())
        self.key_count_label.setSizePolicy(sizePolicy)
        self.key_count_label.setStyleSheet("")
        self.key_count_label.setObjectName("key_count_label")
        self.verticalLayout_2.addWidget(self.key_count_label)
        self.hisTableWidget = QtWidgets.QTableWidget(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.hisTableWidget.sizePolicy().hasHeightForWidth())
        self.hisTableWidget.setSizePolicy(sizePolicy)
        self.hisTableWidget.setMinimumSize(QtCore.QSize(100, 0))
        self.hisTableWidget.setObjectName("hisTableWidget")
        self.hisTableWidget.setColumnCount(5)
        self.hisTableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        self.hisTableWidget.setHorizontalHeaderItem(4, item)
        self.verticalLayout_2.addWidget(self.hisTableWidget)
        self.delButton = QtWidgets.QPushButton(Form)
        self.delButton.setObjectName("delButton")
        self.verticalLayout_2.addWidget(self.delButton)
        self.line_2 = QtWidgets.QFrame(Form)
        self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.verticalLayout_2.addWidget(self.line_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.cron_tabWidget = QtWidgets.QTabWidget(Form)
        self.cron_tabWidget.setTabPosition(QtWidgets.QTabWidget.North)
        self.cron_tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
        self.cron_tabWidget.setElideMode(QtCore.Qt.ElideLeft)
        self.cron_tabWidget.setObjectName("cron_tabWidget")
        self.tab_8 = QtWidgets.QWidget()
        self.tab_8.setObjectName("tab_8")
        self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tab_8)
        self.horizontalLayout_7.setObjectName("horizontalLayout_7")
        self.gridLayout_3 = QtWidgets.QGridLayout()
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.label_5 = QtWidgets.QLabel(self.tab_8)
        self.label_5.setObjectName("label_5")
        self.gridLayout_3.addWidget(self.label_5, 1, 2, 1, 1)
        self.label_26 = QtWidgets.QLabel(self.tab_8)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label_26.sizePolicy().hasHeightForWidth())
        self.label_26.setSizePolicy(sizePolicy)
        self.label_26.setMinimumSize(QtCore.QSize(50, 0))
        self.label_26.setObjectName("label_26")
        self.gridLayout_3.addWidget(self.label_26, 1, 0, 1, 1)
        self.start_dateTimeEdit = QtWidgets.QDateTimeEdit(self.tab_8)
        self.start_dateTimeEdit.setDateTime(QtCore.QDateTime(QtCore.QDate(2023, 9, 14), QtCore.QTime(9, 0, 0)))
        self.start_dateTimeEdit.setMinimumDateTime(QtCore.QDateTime(QtCore.QDate(2023, 9, 14), QtCore.QTime(0, 0, 0)))
        self.start_dateTimeEdit.setMaximumDate(QtCore.QDate(2099, 12, 31))
        self.start_dateTimeEdit.setCalendarPopup(True)
        self.start_dateTimeEdit.setObjectName("start_dateTimeEdit")
        self.gridLayout_3.addWidget(self.start_dateTimeEdit, 1, 1, 1, 1)
        self.end_dateTimeEdit = QtWidgets.QDateTimeEdit(self.tab_8)
        self.end_dateTimeEdit.setDateTime(QtCore.QDateTime(QtCore.QDate(2030, 1, 1), QtCore.QTime(23, 59, 59)))
        self.end_dateTimeEdit.setMinimumDate(QtCore.QDate(2023, 9, 14))
        self.end_dateTimeEdit.setCalendarPopup(True)
        self.end_dateTimeEdit.setObjectName("end_dateTimeEdit")
        self.gridLayout_3.addWidget(self.end_dateTimeEdit, 1, 3, 1, 1)
        self.spinBox = QtWidgets.QSpinBox(self.tab_8)
        self.spinBox.setMinimum(1)
        self.spinBox.setMaximum(9999)
        self.spinBox.setObjectName("spinBox")
        self.gridLayout_3.addWidget(self.spinBox, 0, 2, 1, 1)
        self.interval_comboBox = QtWidgets.QComboBox(self.tab_8)
        self.interval_comboBox.setObjectName("interval_comboBox")
        self.interval_comboBox.addItem("")
        self.interval_comboBox.addItem("")
        self.interval_comboBox.addItem("")
        self.interval_comboBox.addItem("")
        self.interval_comboBox.addItem("")
        self.gridLayout_3.addWidget(self.interval_comboBox, 0, 0, 1, 2)
        self.gridLayout_3.setColumnStretch(0, 1)
        self.gridLayout_3.setColumnStretch(1, 2)
        self.gridLayout_3.setColumnStretch(2, 1)
        self.gridLayout_3.setColumnStretch(3, 2)
        self.horizontalLayout_7.addLayout(self.gridLayout_3)
        self.cron_tabWidget.addTab(self.tab_8, "")
        self.tab_7 = QtWidgets.QWidget()
        self.tab_7.setObjectName("tab_7")
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_7)
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.formLayout_8 = QtWidgets.QFormLayout()
        self.formLayout_8.setObjectName("formLayout_8")
        self.label = QtWidgets.QLabel(self.tab_7)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setMinimumSize(QtCore.QSize(50, 0))
        self.label.setObjectName("label")
        self.formLayout_8.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.label)
        self.once_dateTimeEdit = QtWidgets.QDateTimeEdit(self.tab_7)
        self.once_dateTimeEdit.setCalendarPopup(True)
        self.once_dateTimeEdit.setObjectName("once_dateTimeEdit")
        self.formLayout_8.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.once_dateTimeEdit)
        self.horizontalLayout_6.addLayout(self.formLayout_8)
        self.cron_tabWidget.addTab(self.tab_7, "")
        self.horizontalLayout_3.addWidget(self.cron_tabWidget)
        self.horizontalLayout_3.setStretch(0, 1)
        self.verticalLayout_2.addLayout(self.horizontalLayout_3)
        self.formLayout = QtWidgets.QFormLayout()
        self.formLayout.setObjectName("formLayout")
        self.label_15 = QtWidgets.QLabel(Form)
        self.label_15.setObjectName("label_15")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_15)
        self.range_comboBox = QtWidgets.QComboBox(Form)
        self.range_comboBox.setObjectName("range_comboBox")
        self.range_comboBox.addItem("")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.range_comboBox)
        self.verticalLayout_2.addLayout(self.formLayout)
        self.line_3 = QtWidgets.QFrame(Form)
        self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_3.setObjectName("line_3")
        self.verticalLayout_2.addWidget(self.line_3)
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setTabShape(QtWidgets.QTabWidget.Rounded)
        self.tabWidget.setElideMode(QtCore.Qt.ElideNone)
        self.tabWidget.setUsesScrollButtons(False)
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tab.sizePolicy().hasHeightForWidth())
        self.tab.setSizePolicy(sizePolicy)
        self.tab.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.tab.setObjectName("tab")
        self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.tab)
        self.verticalLayout_5.setObjectName("verticalLayout_5")
        self.txt_plainTextEdit = QtWidgets.QPlainTextEdit(self.tab)
        self.txt_plainTextEdit.setObjectName("txt_plainTextEdit")
        self.verticalLayout_5.addWidget(self.txt_plainTextEdit)
        self.tabWidget.addTab(self.tab, "")
        self.tab_9 = QtWidgets.QWidget()
        self.tab_9.setObjectName("tab_9")
        self.verticalLayout_8 = QtWidgets.QVBoxLayout(self.tab_9)
        self.verticalLayout_8.setObjectName("verticalLayout_8")
        self.imgPlainTextEdit = QtWidgets.QTextEdit(self.tab_9)
        self.imgPlainTextEdit.setMaximumSize(QtCore.QSize(491, 16777215))
        self.imgPlainTextEdit.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.imgPlainTextEdit.setObjectName("imgPlainTextEdit")
        self.verticalLayout_8.addWidget(self.imgPlainTextEdit)
        self.tabWidget.addTab(self.tab_9, "")
        self.tab_2 = QtWidgets.QWidget()
        self.tab_2.setObjectName("tab_2")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab_2)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.filePlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_2)
        self.filePlainTextEdit.setObjectName("filePlainTextEdit")
        self.horizontalLayout_4.addWidget(self.filePlainTextEdit)
        self.tabWidget.addTab(self.tab_2, "")
        self.verticalLayout_2.addWidget(self.tabWidget)
        self.addBtn = QtWidgets.QPushButton(Form)
        self.addBtn.setObjectName("addBtn")
        self.verticalLayout_2.addWidget(self.addBtn)
        self.horizontalLayout_2.addLayout(self.verticalLayout_2)
        self.horizontalLayout_2.setStretch(0, 1)

        self.retranslateUi(Form)
        self.cron_tabWidget.setCurrentIndex(0)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.key_count_label.setText(_translate("Form", "配置任务列表"))
        item = self.hisTableWidget.horizontalHeaderItem(0)
        item.setText(_translate("Form", "推送群/人"))
        item = self.hisTableWidget.horizontalHeaderItem(1)
        item.setText(_translate("Form", "任务类型"))
        item = self.hisTableWidget.horizontalHeaderItem(2)
        item.setText(_translate("Form", "周期"))
        item = self.hisTableWidget.horizontalHeaderItem(3)
        item.setText(_translate("Form", "推送类型"))
        item = self.hisTableWidget.horizontalHeaderItem(4)
        item.setText(_translate("Form", "推送内容"))
        self.delButton.setText(_translate("Form", "选中删除"))
        self.label_5.setText(_translate("Form", "结束："))
        self.label_26.setText(_translate("Form", "开始："))
        self.start_dateTimeEdit.setDisplayFormat(_translate("Form", "yyyy-M-d H:mm:ss"))
        self.end_dateTimeEdit.setDisplayFormat(_translate("Form", "yyyy-M-d H:mm:ss"))
        self.interval_comboBox.setItemText(0, _translate("Form", "间隔几周"))
        self.interval_comboBox.setItemText(1, _translate("Form", "间隔几天"))
        self.interval_comboBox.setItemText(2, _translate("Form", "间隔几小时"))
        self.interval_comboBox.setItemText(3, _translate("Form", "间隔几分钟"))
        self.interval_comboBox.setItemText(4, _translate("Form", "间隔多少秒"))
        self.cron_tabWidget.setTabText(self.cron_tabWidget.indexOf(self.tab_8), _translate("Form", "间隔触发"))
        self.label.setText(_translate("Form", "触发一次："))
        self.once_dateTimeEdit.setDisplayFormat(_translate("Form", "yyyy-M-d H:mm:ss"))
        self.cron_tabWidget.setTabText(self.cron_tabWidget.indexOf(self.tab_7), _translate("Form", "定时触发"))
        self.label_15.setText(_translate("Form", "通知对象："))
        self.range_comboBox.setItemText(0, _translate("Form", "左侧列表的群和好友"))
        self.txt_plainTextEdit.setPlaceholderText(_translate("Form", "通知文本内容"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "发送文本"))
        self.imgPlainTextEdit.setPlaceholderText(
            _translate("Form", "请输入本机图片绝对路径，如C:\\Users\\wangtu\\Pictures\\a.png"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_9), _translate("Form", "发送图片"))
        self.filePlainTextEdit.setPlaceholderText(
            _translate("Form", "回复文件路径，如C:\\Users\\wangtu\\Pictures\\file.txt"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "发送文件"))
        self.addBtn.setText(_translate("Form", "添加新任务"))

yyds/child/job_group_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.job_group_window import Ui_Form


class JobGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'JobGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(JobGroupWindowMediator, self).__init__(JobGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("JobGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("JobGroupWindowMediator onRegister")

    def onRemove(self):
        self.LOG.info("JobGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/job_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'key_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(437, 528)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.horizontalLayout_22 = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout_22.setObjectName("horizontalLayout_22")
        self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_21.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.horizontalLayout_21.setObjectName("horizontalLayout_21")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.key_count_label = QtWidgets.QLabel(Form)
        self.key_count_label.setObjectName("key_count_label")
        self.verticalLayout.addWidget(self.key_count_label)
        self.tableWidget = QtWidgets.QTableWidget(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
        self.tableWidget.setSizePolicy(sizePolicy)
        self.tableWidget.setMinimumSize(QtCore.QSize(100, 0))
        self.tableWidget.setGridStyle(QtCore.Qt.SolidLine)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(5)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(4, item)
        self.verticalLayout.addWidget(self.tableWidget)
        self.pdelButton = QtWidgets.QPushButton(Form)
        self.pdelButton.setObjectName("pdelButton")
        self.verticalLayout.addWidget(self.pdelButton)
        self.line = QtWidgets.QFrame(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
        self.line.setSizePolicy(sizePolicy)
        self.line.setMinimumSize(QtCore.QSize(20, 2))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setLineWidth(1)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout.addWidget(self.line)
        self.formLayout = QtWidgets.QFormLayout()
        self.formLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
        self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
        self.formLayout.setLabelAlignment(QtCore.Qt.AlignCenter)
        self.formLayout.setFormAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
        self.formLayout.setObjectName("formLayout")
        self.label_7 = QtWidgets.QLabel(Form)
        self.label_7.setAlignment(QtCore.Qt.AlignCenter)
        self.label_7.setObjectName("label_7")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_7)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.radioEqButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.radioEqButton.sizePolicy().hasHeightForWidth())
        self.radioEqButton.setSizePolicy(sizePolicy)
        self.radioEqButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioEqButton.setChecked(False)
        self.radioEqButton.setObjectName("radioEqButton")
        self.horizontalLayout_2.addWidget(self.radioEqButton)
        self.radioHasButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.radioHasButton.sizePolicy().hasHeightForWidth())
        self.radioHasButton.setSizePolicy(sizePolicy)
        self.radioHasButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioHasButton.setObjectName("radioHasButton")
        self.horizontalLayout_2.addWidget(self.radioHasButton)
        self.radioReButton = QtWidgets.QRadioButton(Form)
        self.radioReButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioReButton.setObjectName("radioReButton")
        self.horizontalLayout_2.addWidget(self.radioReButton)
        self.atCheckBox = QtWidgets.QCheckBox(Form)
        self.atCheckBox.setObjectName("atCheckBox")
        self.horizontalLayout_2.addWidget(self.atCheckBox)
        self.formLayout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2)
        self.label_6 = QtWidgets.QLabel(Form)
        self.label_6.setAlignment(QtCore.Qt.AlignCenter)
        self.label_6.setObjectName("label_6")
        self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_6)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.keyLineEdit = QtWidgets.QLineEdit(Form)
        self.keyLineEdit.setInputMask("")
        self.keyLineEdit.setText("")
        self.keyLineEdit.setMaxLength(500)
        self.keyLineEdit.setObjectName("keyLineEdit")
        self.horizontalLayout.addWidget(self.keyLineEdit)
        self.formLayout.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
        self.label = QtWidgets.QLabel(Form)
        self.label.setObjectName("label")
        self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label)
        self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_12.setSpacing(1)
        self.horizontalLayout_12.setObjectName("horizontalLayout_12")
        self.noRadioButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.noRadioButton.sizePolicy().hasHeightForWidth())
        self.noRadioButton.setSizePolicy(sizePolicy)
        self.noRadioButton.setMinimumSize(QtCore.QSize(50, 0))
        self.noRadioButton.setChecked(True)
        self.noRadioButton.setObjectName("noRadioButton")
        self.horizontalLayout_12.addWidget(self.noRadioButton)
        self.atOneRadioButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.atOneRadioButton.sizePolicy().hasHeightForWidth())
        self.atOneRadioButton.setSizePolicy(sizePolicy)
        self.atOneRadioButton.setMinimumSize(QtCore.QSize(50, 0))
        self.atOneRadioButton.setObjectName("atOneRadioButton")
        self.horizontalLayout_12.addWidget(self.atOneRadioButton)
        self.atAllRadioButton = QtWidgets.QRadioButton(Form)
        self.atAllRadioButton.setMinimumSize(QtCore.QSize(50, 0))
        self.atAllRadioButton.setObjectName("atAllRadioButton")
        self.horizontalLayout_12.addWidget(self.atAllRadioButton)
        self.formLayout.setLayout(5, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_12)
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setMinimumSize(QtCore.QSize(0, 150))
        self.tabWidget.setMaximumSize(QtCore.QSize(16777215, 150))
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.txtPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab)
        self.txtPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.txtPlainTextEdit.setAutoFillBackground(False)
        self.txtPlainTextEdit.setMaximumBlockCount(15)
        self.txtPlainTextEdit.setObjectName("txtPlainTextEdit")
        self.horizontalLayout_4.addWidget(self.txtPlainTextEdit)
        self.tabWidget.addTab(self.tab, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_4)
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.imgPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_4)
        self.imgPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.imgPlainTextEdit.setMaximumBlockCount(15)
        self.imgPlainTextEdit.setObjectName("imgPlainTextEdit")
        self.horizontalLayout_6.addWidget(self.imgPlainTextEdit)
        self.tabWidget.addTab(self.tab_4, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.tab_3)
        self.horizontalLayout_7.setObjectName("horizontalLayout_7")
        self.filePlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_3)
        self.filePlainTextEdit.setMaximumBlockCount(15)
        self.filePlainTextEdit.setObjectName("filePlainTextEdit")
        self.horizontalLayout_7.addWidget(self.filePlainTextEdit)
        self.tabWidget.addTab(self.tab_3, "")
        self.formLayout.setWidget(6, QtWidgets.QFormLayout.SpanningRole, self.tabWidget)
        self.rangeComboBox = QtWidgets.QComboBox(Form)
        self.rangeComboBox.setObjectName("rangeComboBox")
        self.rangeComboBox.addItem("")
        self.rangeComboBox.addItem("")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.rangeComboBox)
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setObjectName("label_2")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.verticalLayout.addLayout(self.formLayout)
        self.addBtn = QtWidgets.QPushButton(Form)
        self.addBtn.setObjectName("addBtn")
        self.verticalLayout.addWidget(self.addBtn)
        self.horizontalLayout_21.addLayout(self.verticalLayout)
        self.horizontalLayout_21.setStretch(0, 1)
        self.horizontalLayout_22.addLayout(self.horizontalLayout_21)
        self.horizontalLayout_22.setStretch(0, 2)

        self.retranslateUi(Form)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.key_count_label.setText(_translate("Form", "关键词侦听"))
        self.tableWidget.setSortingEnabled(True)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("Form", "群/好友"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("Form", "关键字"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("Form", "关键词类型"))
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText(_translate("Form", "提问需要@"))
        item = self.tableWidget.horizontalHeaderItem(4)
        item.setText(_translate("Form", "回复内容"))
        self.pdelButton.setText(_translate("Form", "选中删除"))
        self.label_7.setText(_translate("Form", "侦听类型"))
        self.radioEqButton.setText(_translate("Form", "等于"))
        self.radioHasButton.setText(_translate("Form", "包含"))
        self.radioReButton.setText(_translate("Form", "正则表达式"))
        self.atCheckBox.setText(_translate("Form", "群被人@"))
        self.label_6.setText(_translate("Form", "侦听内容"))
        self.keyLineEdit.setPlaceholderText(_translate("Form", "输入关键字，如：你是谁"))
        self.label.setText(_translate("Form", "群回复时"))
        self.noRadioButton.setText(_translate("Form", "直接回复"))
        self.atOneRadioButton.setText(_translate("Form", "@提问者"))
        self.atAllRadioButton.setText(_translate("Form", "@所有(群主)"))
        self.txtPlainTextEdit.setPlaceholderText(_translate("Form", "回复文本内容"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "文本"))
        self.imgPlainTextEdit.setPlaceholderText(
            _translate("Form", "回复图片路径，如C:\\Users\\wangtu\\Pictures\\Default.jpg"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "图片"))
        self.filePlainTextEdit.setPlaceholderText(
            _translate("Form", "回复文件路径，如C:\\Users\\wangtu\\Pictures\\file.txt"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "文件"))
        self.rangeComboBox.setItemText(0, _translate("Form", "左侧列表的群和好友"))
        self.rangeComboBox.setItemText(1, _translate("Form", "全部群和好友"))
        self.label_2.setText(_translate("Form", "侦听对象"))
        self.addBtn.setText(_translate("Form", "添加关键词监控"))

yyds/child/key_group_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.key_group_window import Ui_Form


class KeyGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'KeyGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(KeyGroupWindowMediator, self).__init__(KeyGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("KeyGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("KeyGroupWindowMediator onRegister")

    def onRemove(self):
        self.LOG.info("KeyGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/key_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'message_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(374, 432)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setSpacing(1)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.message_count_label = QtWidgets.QLabel(Form)
        self.message_count_label.setStyleSheet("")
        self.message_count_label.setObjectName("message_count_label")
        self.verticalLayout.addWidget(self.message_count_label)
        self.logPlainTextEdit = QtWidgets.QPlainTextEdit(Form)
        self.logPlainTextEdit.setReadOnly(True)
        self.logPlainTextEdit.setPlainText("")
        self.logPlainTextEdit.setMaximumBlockCount(6000)
        self.logPlainTextEdit.setObjectName("logPlainTextEdit")
        self.verticalLayout.addWidget(self.logPlainTextEdit)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.imgPushButton = QtWidgets.QPushButton(Form)
        self.imgPushButton.setObjectName("imgPushButton")
        self.horizontalLayout_4.addWidget(self.imgPushButton)
        self.filePushButton = QtWidgets.QPushButton(Form)
        self.filePushButton.setEnabled(True)
        self.filePushButton.setObjectName("filePushButton")
        self.horizontalLayout_4.addWidget(self.filePushButton)
        self.pushButton_5 = QtWidgets.QPushButton(Form)
        self.pushButton_5.setEnabled(False)
        self.pushButton_5.setObjectName("pushButton_5")
        self.horizontalLayout_4.addWidget(self.pushButton_5)
        self.verticalLayout_2.addLayout(self.horizontalLayout_4)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setSpacing(1)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.wxTextEdit = QtWidgets.QTextEdit(Form)
        self.wxTextEdit.setEnabled(True)
        self.wxTextEdit.setObjectName("wxTextEdit")
        self.horizontalLayout.addWidget(self.wxTextEdit)
        self.verticalLayout_2.addLayout(self.horizontalLayout)
        self.wxTextPushButton = QtWidgets.QPushButton(Form)
        self.wxTextPushButton.setObjectName("wxTextPushButton")
        self.verticalLayout_2.addWidget(self.wxTextPushButton)
        self.horizontalLayout_2.addLayout(self.verticalLayout_2)
        self.horizontalLayout_2.setStretch(0, 2)
        self.horizontalLayout_6.addLayout(self.horizontalLayout_2)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.message_count_label.setText(_translate("Form", "新消息"))
        self.imgPushButton.setText(_translate("Form", "发送图片"))
        self.filePushButton.setText(_translate("Form", "发送文件"))
        self.pushButton_5.setText(_translate("Form", "功能待定"))
        self.wxTextEdit.setPlaceholderText(
            _translate("Form", "选择列表后，输入要群发的文字或选择图片、文件。Shift+Enter换行，Enter发送"))
        self.wxTextPushButton.setText(_translate("Form", "发送/Enter"))

yyds/child/message_group_window.py
wangtu/wechat_robot-manage-demo
import logging
import random
import threading
import time

from PyQt5 import QtCore
from PyQt5.QtCore import Qt

from frame.base.signal_thread import SignalThread
from frame.base.wcf_sss import WcfSSS
from frame.constant import Constant
from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from frame.vo.wx_text_vo import WxTextVO
from yyds.aop.permission_aop import permission_wcf, show_message
from yyds.child.message_group_window import Ui_Form

"""
tab 消息群发控制器
"""


class MessageGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'MessageGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(MessageGroupWindowMediator, self).__init__(MessageGroupWindowMediator.NAME)
        self.log_done_thread = None
        self.LOG = logging.getLogger("MessageGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("MessageGroupWindowMediator onRegister")
        self.log_done_thread = SignalThread()
        self.log_done_thread.signal.connect(self.refresh_log)

        self.getViewComponent().wxTextPushButton.clicked.connect(self.send_wx_text)
        # 这段代码解决了文本框回车发送的问题
        self.getViewComponent().wxTextEdit.installEventFilter(self)

    @permission_wcf
    def send_wx_text(self):
        txt = self.getViewComponent().wxTextEdit.toPlainText()
        if txt == "":
            return
        items = self.getSelectListWidget()
        if len(items) == 0:
            show_message("提示", "请先选择要发送的联系人")
            return
        self.getViewComponent().wxTextEdit.setText("")
        # 为了释放异步刷新界面的能力
        thread = threading.Thread(target=self.start_sendTXT, args=(txt,))
        thread.start()

    def start_sendTXT(self, txt):
        items = self.getSelectListWidget()
        if len(items) == 0:
            return False
        for item in items:
            if not WcfSSS.getInstance().is_running():
                break
            key = item.data(Qt.UserRole)
            vo = WxTextVO()
            vo.answer = txt
            vo.receiver = key
            self.sendNotification(Constant.SEND_WX_TEXT, vo)
            time.sleep(random.randint(2, 4))

            userName = item.text() + " 文本已发送！"
            # 为了释放异步刷新界面的能力
            self.log_done_thread.data = userName
            self.log_done_thread.start()
        self.LOG.info("文本信息群发完毕！")
        return True

    def refresh_log(self, text):
        self.getViewComponent().logPlainTextEdit.appendPlainText(text)

    def eventFilter(self, obj, event):
        if event.type() == QtCore.QEvent.KeyPress and obj is self.getViewComponent().wxTextEdit:
            shift = False
            if event.modifiers() & Qt.ShiftModifier:
                shift = True
            if event.key() == Qt.Key_Return and self.getViewComponent().wxTextEdit.hasFocus():
                if shift:
                    self.getViewComponent().wxTextEdit.insertPlainText("")
                    return False
                self.send_wx_text()
                return True
        return False

    def onRemove(self):
        self.LOG.info("MessageGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/message_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'room_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(448, 528)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_21.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.horizontalLayout_21.setObjectName("horizontalLayout_21")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.key_count_label = QtWidgets.QLabel(Form)
        self.key_count_label.setObjectName("key_count_label")
        self.verticalLayout.addWidget(self.key_count_label)
        self.tableWidget = QtWidgets.QTableWidget(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tableWidget.sizePolicy().hasHeightForWidth())
        self.tableWidget.setSizePolicy(sizePolicy)
        self.tableWidget.setMinimumSize(QtCore.QSize(100, 0))
        self.tableWidget.setGridStyle(QtCore.Qt.SolidLine)
        self.tableWidget.setObjectName("tableWidget")
        self.tableWidget.setColumnCount(5)
        self.tableWidget.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(2, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(3, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidget.setHorizontalHeaderItem(4, item)
        self.verticalLayout.addWidget(self.tableWidget)
        self.pdelButton = QtWidgets.QPushButton(Form)
        self.pdelButton.setObjectName("pdelButton")
        self.verticalLayout.addWidget(self.pdelButton)
        self.line = QtWidgets.QFrame(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
        self.line.setSizePolicy(sizePolicy)
        self.line.setMinimumSize(QtCore.QSize(20, 2))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setLineWidth(1)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout.addWidget(self.line)
        self.formLayout = QtWidgets.QFormLayout()
        self.formLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
        self.formLayout.setFieldGrowthPolicy(QtWidgets.QFormLayout.AllNonFixedFieldsGrow)
        self.formLayout.setLabelAlignment(QtCore.Qt.AlignCenter)
        self.formLayout.setFormAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignTop)
        self.formLayout.setObjectName("formLayout")
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setObjectName("label_2")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.rangeComboBox = QtWidgets.QComboBox(Form)
        self.rangeComboBox.setObjectName("rangeComboBox")
        self.rangeComboBox.addItem("")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.rangeComboBox)
        self.label_7 = QtWidgets.QLabel(Form)
        self.label_7.setAlignment(QtCore.Qt.AlignCenter)
        self.label_7.setObjectName("label_7")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.label_7)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.radioEqButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.radioEqButton.sizePolicy().hasHeightForWidth())
        self.radioEqButton.setSizePolicy(sizePolicy)
        self.radioEqButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioEqButton.setChecked(True)
        self.radioEqButton.setObjectName("radioEqButton")
        self.horizontalLayout_2.addWidget(self.radioEqButton)
        self.radioHasButton = QtWidgets.QRadioButton(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.radioHasButton.sizePolicy().hasHeightForWidth())
        self.radioHasButton.setSizePolicy(sizePolicy)
        self.radioHasButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioHasButton.setObjectName("radioHasButton")
        self.horizontalLayout_2.addWidget(self.radioHasButton)
        self.radioReButton = QtWidgets.QRadioButton(Form)
        self.radioReButton.setMinimumSize(QtCore.QSize(0, 0))
        self.radioReButton.setObjectName("radioReButton")
        self.horizontalLayout_2.addWidget(self.radioReButton)
        self.formLayout.setLayout(3, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout_2)
        self.label_6 = QtWidgets.QLabel(Form)
        self.label_6.setAlignment(QtCore.Qt.AlignCenter)
        self.label_6.setObjectName("label_6")
        self.formLayout.setWidget(4, QtWidgets.QFormLayout.LabelRole, self.label_6)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.keyLineEdit = QtWidgets.QLineEdit(Form)
        self.keyLineEdit.setInputMask("")
        self.keyLineEdit.setText("")
        self.keyLineEdit.setMaxLength(500)
        self.keyLineEdit.setObjectName("keyLineEdit")
        self.horizontalLayout.addWidget(self.keyLineEdit)
        self.formLayout.setLayout(4, QtWidgets.QFormLayout.FieldRole, self.horizontalLayout)
        self.label_4 = QtWidgets.QLabel(Form)
        self.label_4.setObjectName("label_4")
        self.formLayout.setWidget(5, QtWidgets.QFormLayout.LabelRole, self.label_4)
        self.action_comboBox = QtWidgets.QComboBox(Form)
        self.action_comboBox.setObjectName("action_comboBox")
        self.action_comboBox.addItem("")
        self.action_comboBox.addItem("")
        self.formLayout.setWidget(5, QtWidgets.QFormLayout.FieldRole, self.action_comboBox)
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setMinimumSize(QtCore.QSize(0, 150))
        self.tabWidget.setMaximumSize(QtCore.QSize(16777215, 150))
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.txtPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab)
        self.txtPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.txtPlainTextEdit.setAutoFillBackground(False)
        self.txtPlainTextEdit.setMaximumBlockCount(15)
        self.txtPlainTextEdit.setObjectName("txtPlainTextEdit")
        self.horizontalLayout_4.addWidget(self.txtPlainTextEdit)
        self.tabWidget.addTab(self.tab, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_4)
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.imgPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_4)
        self.imgPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.imgPlainTextEdit.setMaximumBlockCount(15)
        self.imgPlainTextEdit.setObjectName("imgPlainTextEdit")
        self.horizontalLayout_6.addWidget(self.imgPlainTextEdit)
        self.tabWidget.addTab(self.tab_4, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_3)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.filePlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_3)
        self.filePlainTextEdit.setMaximumBlockCount(15)
        self.filePlainTextEdit.setObjectName("filePlainTextEdit")
        self.verticalLayout_3.addWidget(self.filePlainTextEdit)
        self.tabWidget.addTab(self.tab_3, "")
        self.formLayout.setWidget(6, QtWidgets.QFormLayout.SpanningRole, self.tabWidget)
        self.verticalLayout.addLayout(self.formLayout)
        self.addBtn = QtWidgets.QPushButton(Form)
        self.addBtn.setObjectName("addBtn")
        self.verticalLayout.addWidget(self.addBtn)
        self.horizontalLayout_21.addLayout(self.verticalLayout)
        self.horizontalLayout_21.setStretch(0, 1)
        self.verticalLayout_2.addLayout(self.horizontalLayout_21)

        self.retranslateUi(Form)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.key_count_label.setText(_translate("Form", "群管理(加群、踢群)"))
        self.tableWidget.setSortingEnabled(True)
        item = self.tableWidget.horizontalHeaderItem(0)
        item.setText(_translate("Form", "管理群"))
        item = self.tableWidget.horizontalHeaderItem(1)
        item.setText(_translate("Form", "识别关键字"))
        item = self.tableWidget.horizontalHeaderItem(2)
        item.setText(_translate("Form", "关键字类型"))
        item = self.tableWidget.horizontalHeaderItem(3)
        item.setText(_translate("Form", "关键词动作"))
        item = self.tableWidget.horizontalHeaderItem(4)
        item.setText(_translate("Form", "执行动作后通知"))
        self.pdelButton.setText(_translate("Form", "选中删除"))
        self.label_2.setText(_translate("Form", "要管理群"))
        self.rangeComboBox.setItemText(0, _translate("Form", "左侧列表的群（仅是管理员权限的群生效）"))
        self.label_7.setText(_translate("Form", "侦听类型"))
        self.radioEqButton.setText(_translate("Form", "等于"))
        self.radioHasButton.setText(_translate("Form", "包含"))
        self.radioReButton.setText(_translate("Form", "正则表达式"))
        self.label_6.setText(_translate("Form", "侦听内容"))
        self.keyLineEdit.setPlaceholderText(_translate("Form", "输入关键字，如：你是谁"))
        self.label_4.setText(_translate("Form", "执行动作"))
        self.action_comboBox.setItemText(0, _translate("Form", "自动加群"))
        self.action_comboBox.setItemText(1, _translate("Form", "自动踢群"))
        self.txtPlainTextEdit.setPlaceholderText(_translate("Form", "执行加群/踢群后，群内通知的文本内容"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "文本"))
        self.imgPlainTextEdit.setPlaceholderText(
            _translate("Form", "回复图片路径，如C:\\Users\\wangtu\\Pictures\\Default.jpg"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "图片"))
        self.filePlainTextEdit.setPlaceholderText(
            _translate("Form", "回复文件路径，如C:\\Users\\wangtu\\Pictures\\file.txt"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "文件"))
        self.addBtn.setText(_translate("Form", "添加自动群管理"))

yyds/child/room_group_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.room_group_window import Ui_Form


class RoomGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'RoomGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(RoomGroupWindowMediator, self).__init__(RoomGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("RoomGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("RoomGroupWindowMediator onRegister")

    def onRemove(self):
        self.LOG.info("RoomGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/room_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'select_contacts_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(439, 421)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setContentsMargins(5, 5, 5, 5)
        self.gridLayout.setSpacing(3)
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButton_3 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.horizontalLayout.addWidget(self.pushButton_3)
        self.pushButton = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton.setObjectName("pushButton")
        self.horizontalLayout.addWidget(self.pushButton)
        self.gridLayout.addLayout(self.horizontalLayout, 4, 0, 1, 1)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.gridLayout_2.setHorizontalSpacing(4)
        self.gridLayout_2.setVerticalSpacing(3)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.listWidget_3 = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget_3.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.listWidget_3.setObjectName("listWidget_3")
        item = QtWidgets.QListWidgetItem()
        self.listWidget_3.addItem(item)
        self.gridLayout_2.addWidget(self.listWidget_3, 2, 0, 1, 1)
        self.label_3 = QtWidgets.QLabel(self.centralwidget)
        self.label_3.setMinimumSize(QtCore.QSize(100, 0))
        self.label_3.setAlignment(QtCore.Qt.AlignCenter)
        self.label_3.setObjectName("label_3")
        self.gridLayout_2.addWidget(self.label_3, 0, 2, 1, 1)
        self.listWidget_2 = QtWidgets.QListWidget(self.centralwidget)
        self.listWidget_2.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.listWidget_2.setObjectName("listWidget_2")
        item = QtWidgets.QListWidgetItem()
        self.listWidget_2.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.listWidget_2.addItem(item)
        self.gridLayout_2.addWidget(self.listWidget_2, 2, 2, 1, 1)
        self.label = QtWidgets.QLabel(self.centralwidget)
        self.label.setAlignment(QtCore.Qt.AlignCenter)
        self.label.setObjectName("label")
        self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.pushButton_2 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_2.setMinimumSize(QtCore.QSize(100, 0))
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout_3.addWidget(self.pushButton_2)
        self.pushButton_4 = QtWidgets.QPushButton(self.centralwidget)
        self.pushButton_4.setMaximumSize(QtCore.QSize(100, 16777215))
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout_3.addWidget(self.pushButton_4)
        self.gridLayout_2.addLayout(self.verticalLayout_3, 2, 1, 1, 1)
        self.horizontalLayout_2.addLayout(self.gridLayout_2)
        self.line = QtWidgets.QFrame(self.centralwidget)
        self.line.setMaximumSize(QtCore.QSize(2, 16777215))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setFrameShape(QtWidgets.QFrame.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.horizontalLayout_2.addWidget(self.line)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.line_2 = QtWidgets.QFrame(self.centralwidget)
        self.line_2.setMaximumSize(QtCore.QSize(16777215, 2))
        self.line_2.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.verticalLayout_2.addWidget(self.line_2)
        self.horizontalLayout_2.addLayout(self.verticalLayout_2)
        self.gridLayout.addLayout(self.horizontalLayout_2, 1, 0, 1, 1)
        self.line_4 = QtWidgets.QFrame(self.centralwidget)
        self.line_4.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_4.setObjectName("line_4")
        self.gridLayout.addWidget(self.line_4, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 439, 22))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_3.setText(_translate("MainWindow", "取消"))
        self.pushButton.setText(_translate("MainWindow", "确定"))
        __sortingEnabled = self.listWidget_3.isSortingEnabled()
        self.listWidget_3.setSortingEnabled(False)
        item = self.listWidget_3.item(0)
        item.setText(_translate("MainWindow", "群1"))
        self.listWidget_3.setSortingEnabled(__sortingEnabled)
        self.label_3.setText(_translate("MainWindow", "待处理联系人"))
        __sortingEnabled = self.listWidget_2.isSortingEnabled()
        self.listWidget_2.setSortingEnabled(False)
        item = self.listWidget_2.item(0)
        item.setText(_translate("MainWindow", "群1"))
        item = self.listWidget_2.item(1)
        item.setText(_translate("MainWindow", "群2"))
        self.listWidget_2.setSortingEnabled(__sortingEnabled)
        self.label.setText(_translate("MainWindow", "微信联系人"))
        self.pushButton_2.setText(_translate("MainWindow", "添加->>>"))
        self.pushButton_4.setText(_translate("MainWindow", "<<<---移除"))

yyds/child/select_contacts_window.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'setting_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(448, 528)
        Form.setAutoFillBackground(False)
        Form.setStyleSheet("")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout_21 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_21.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.horizontalLayout_21.setObjectName("horizontalLayout_21")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.key_count_label = QtWidgets.QLabel(Form)
        self.key_count_label.setObjectName("key_count_label")
        self.verticalLayout.addWidget(self.key_count_label)
        self.line = QtWidgets.QFrame(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.line.sizePolicy().hasHeightForWidth())
        self.line.setSizePolicy(sizePolicy)
        self.line.setMinimumSize(QtCore.QSize(20, 2))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setLineWidth(1)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout.addWidget(self.line)
        self.formLayout_2 = QtWidgets.QFormLayout()
        self.formLayout_2.setObjectName("formLayout_2")
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setObjectName("label_2")
        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.label_2)
        self.autoCheckBox = QtWidgets.QCheckBox(Form)
        self.autoCheckBox.setObjectName("autoCheckBox")
        self.formLayout_2.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.autoCheckBox)
        self.verticalLayout.addLayout(self.formLayout_2)
        self.label_4 = QtWidgets.QLabel(Form)
        self.label_4.setObjectName("label_4")
        self.verticalLayout.addWidget(self.label_4)
        self.tabWidget = QtWidgets.QTabWidget(Form)
        self.tabWidget.setMinimumSize(QtCore.QSize(0, 150))
        self.tabWidget.setMaximumSize(QtCore.QSize(16777215, 150))
        self.tabWidget.setObjectName("tabWidget")
        self.tab = QtWidgets.QWidget()
        self.tab.setObjectName("tab")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.tab)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.txtPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab)
        self.txtPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.txtPlainTextEdit.setAutoFillBackground(False)
        self.txtPlainTextEdit.setMaximumBlockCount(15)
        self.txtPlainTextEdit.setObjectName("txtPlainTextEdit")
        self.horizontalLayout_4.addWidget(self.txtPlainTextEdit)
        self.tabWidget.addTab(self.tab, "")
        self.tab_4 = QtWidgets.QWidget()
        self.tab_4.setObjectName("tab_4")
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.tab_4)
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.imgPlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_4)
        self.imgPlainTextEdit.setMinimumSize(QtCore.QSize(0, 0))
        self.imgPlainTextEdit.setMaximumBlockCount(15)
        self.imgPlainTextEdit.setObjectName("imgPlainTextEdit")
        self.horizontalLayout_6.addWidget(self.imgPlainTextEdit)
        self.tabWidget.addTab(self.tab_4, "")
        self.tab_3 = QtWidgets.QWidget()
        self.tab_3.setObjectName("tab_3")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab_3)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.filePlainTextEdit = QtWidgets.QPlainTextEdit(self.tab_3)
        self.filePlainTextEdit.setMaximumBlockCount(15)
        self.filePlainTextEdit.setObjectName("filePlainTextEdit")
        self.verticalLayout_3.addWidget(self.filePlainTextEdit)
        self.tabWidget.addTab(self.tab_3, "")
        self.verticalLayout.addWidget(self.tabWidget)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.addBtn = QtWidgets.QPushButton(Form)
        self.addBtn.setObjectName("addBtn")
        self.verticalLayout.addWidget(self.addBtn)
        self.horizontalLayout_21.addLayout(self.verticalLayout)
        self.horizontalLayout_21.setStretch(0, 1)
        self.verticalLayout_2.addLayout(self.horizontalLayout_21)

        self.retranslateUi(Form)
        self.tabWidget.setCurrentIndex(0)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.key_count_label.setText(_translate("Form", "基础设置"))
        self.label_2.setText(_translate("Form", "好友添加时："))
        self.autoCheckBox.setText(_translate("Form", "自动通过好友添加"))
        self.label_4.setText(_translate("Form", "好友添加后，打招呼的内容，如下："))
        self.txtPlainTextEdit.setPlaceholderText(_translate("Form", "好友添加后，通知的欢迎文本内容"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "文本"))
        self.imgPlainTextEdit.setPlaceholderText(
            _translate("Form", "回复图片路径，如C:\\Users\\wangtu\\Pictures\\Default.jpg"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_4), _translate("Form", "图片"))
        self.filePlainTextEdit.setPlaceholderText(
            _translate("Form", "回复文件路径，如C:\\Users\\wangtu\\Pictures\\file.txt"))
        self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), _translate("Form", "文件"))
        self.addBtn.setText(_translate("Form", "保存设置"))

yyds/child/setting_group_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.setting_group_window import Ui_Form


class SettingGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'SettingGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(SettingGroupWindowMediator, self).__init__(SettingGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("SettingGroupWindowMediator")

    def onRegister(self):
        self.LOG.info("SettingGroupWindowMediator onRegister")

    def onRemove(self):
        self.LOG.info("SettingGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/setting_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'switch_group_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(379, 464)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(Form)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.startButton = QtWidgets.QPushButton(Form)
        self.startButton.setObjectName("startButton")
        self.horizontalLayout_2.addWidget(self.startButton)
        self.stopButton = QtWidgets.QPushButton(Form)
        self.stopButton.setObjectName("stopButton")
        self.horizontalLayout_2.addWidget(self.stopButton)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label = QtWidgets.QLabel(Form)
        self.label.setObjectName("label")
        self.horizontalLayout_3.addWidget(self.label)
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout_3.addItem(spacerItem)
        self.clear_pushButton = QtWidgets.QPushButton(Form)
        self.clear_pushButton.setMinimumSize(QtCore.QSize(50, 0))
        self.clear_pushButton.setObjectName("clear_pushButton")
        self.horizontalLayout_3.addWidget(self.clear_pushButton)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.debugTextEdit = QtWidgets.QPlainTextEdit(Form)
        font = QtGui.QFont()
        font.setFamily("Arial")
        font.setPointSize(9)
        font.setBold(False)
        font.setItalic(False)
        font.setWeight(50)
        self.debugTextEdit.setFont(font)
        self.debugTextEdit.setStyleSheet("font: 9pt \"Arial\";")
        self.debugTextEdit.setReadOnly(True)
        self.debugTextEdit.setMaximumBlockCount(500)
        self.debugTextEdit.setObjectName("debugTextEdit")
        self.verticalLayout.addWidget(self.debugTextEdit)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_5 = QtWidgets.QLabel(Form)
        self.label_5.setObjectName("label_5")
        self.verticalLayout_3.addWidget(self.label_5)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.wxIdEdit = QtWidgets.QLineEdit(Form)
        self.wxIdEdit.setReadOnly(True)
        self.wxIdEdit.setObjectName("wxIdEdit")
        self.gridLayout.addWidget(self.wxIdEdit, 1, 1, 1, 1)
        self.label_4 = QtWidgets.QLabel(Form)
        self.label_4.setObjectName("label_4")
        self.gridLayout.addWidget(self.label_4, 2, 0, 1, 1)
        self.mobileEdit = QtWidgets.QLineEdit(Form)
        self.mobileEdit.setReadOnly(True)
        self.mobileEdit.setObjectName("mobileEdit")
        self.gridLayout.addWidget(self.mobileEdit, 2, 1, 1, 1)
        self.label_2 = QtWidgets.QLabel(Form)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.wxNameEdit = QtWidgets.QLineEdit(Form)
        self.wxNameEdit.setReadOnly(True)
        self.wxNameEdit.setObjectName("wxNameEdit")
        self.gridLayout.addWidget(self.wxNameEdit, 1, 3, 1, 1)
        self.label_3 = QtWidgets.QLabel(Form)
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 1, 2, 1, 1)
        self.label_7 = QtWidgets.QLabel(Form)
        self.label_7.setObjectName("label_7")
        self.gridLayout.addWidget(self.label_7, 3, 0, 1, 1)
        self.roomLenEdit = QtWidgets.QLineEdit(Form)
        self.roomLenEdit.setReadOnly(True)
        self.roomLenEdit.setObjectName("roomLenEdit")
        self.gridLayout.addWidget(self.roomLenEdit, 3, 1, 1, 1)
        self.label_8 = QtWidgets.QLabel(Form)
        self.label_8.setObjectName("label_8")
        self.gridLayout.addWidget(self.label_8, 3, 2, 1, 1)
        self.friendLenEdit = QtWidgets.QLineEdit(Form)
        self.friendLenEdit.setReadOnly(True)
        self.friendLenEdit.setObjectName("friendLenEdit")
        self.gridLayout.addWidget(self.friendLenEdit, 3, 3, 1, 1)
        self.verticalLayout_3.addLayout(self.gridLayout)
        self.line = QtWidgets.QFrame(Form)
        self.line.setFrameShape(QtWidgets.QFrame.HLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.verticalLayout_3.addWidget(self.line)
        self.line_2 = QtWidgets.QFrame(Form)
        self.line_2.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.verticalLayout_3.addWidget(self.line_2)
        self.label_9 = QtWidgets.QLabel(Form)
        self.label_9.setObjectName("label_9")
        self.verticalLayout_3.addWidget(self.label_9)
        self.gridLayout_3 = QtWidgets.QGridLayout()
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.messageCountLineEdit = QtWidgets.QLineEdit(Form)
        self.messageCountLineEdit.setReadOnly(True)
        self.messageCountLineEdit.setObjectName("messageCountLineEdit")
        self.gridLayout_3.addWidget(self.messageCountLineEdit, 0, 1, 1, 1)
        self.label_10 = QtWidgets.QLabel(Form)
        self.label_10.setStyleSheet("border-color: rgb(0, 170, 0);")
        self.label_10.setObjectName("label_10")
        self.gridLayout_3.addWidget(self.label_10, 0, 0, 1, 1)
        self.label_11 = QtWidgets.QLabel(Form)
        self.label_11.setStyleSheet("border-color: rgb(0, 170, 0);")
        self.label_11.setObjectName("label_11")
        self.gridLayout_3.addWidget(self.label_11, 0, 2, 1, 1)
        self.ai_limit_lineEdit = QtWidgets.QLineEdit(Form)
        self.ai_limit_lineEdit.setReadOnly(True)
        self.ai_limit_lineEdit.setPlaceholderText("")
        self.ai_limit_lineEdit.setObjectName("ai_limit_lineEdit")
        self.gridLayout_3.addWidget(self.ai_limit_lineEdit, 0, 3, 1, 1)
        self.key_count_lineEdit = QtWidgets.QLineEdit(Form)
        self.key_count_lineEdit.setReadOnly(True)
        self.key_count_lineEdit.setPlaceholderText("")
        self.key_count_lineEdit.setObjectName("key_count_lineEdit")
        self.gridLayout_3.addWidget(self.key_count_lineEdit, 1, 1, 1, 1)
        self.label_12 = QtWidgets.QLabel(Form)
        self.label_12.setObjectName("label_12")
        self.gridLayout_3.addWidget(self.label_12, 1, 0, 1, 1)
        self.job_count_lineEdit = QtWidgets.QLineEdit(Form)
        self.job_count_lineEdit.setObjectName("job_count_lineEdit")
        self.gridLayout_3.addWidget(self.job_count_lineEdit, 1, 3, 1, 1)
        self.label_13 = QtWidgets.QLabel(Form)
        self.label_13.setObjectName("label_13")
        self.gridLayout_3.addWidget(self.label_13, 1, 2, 1, 1)
        self.label_14 = QtWidgets.QLabel(Form)
        self.label_14.setObjectName("label_14")
        self.gridLayout_3.addWidget(self.label_14, 2, 0, 1, 1)
        self.ai_user_lineEdit = QtWidgets.QLineEdit(Form)
        self.ai_user_lineEdit.setObjectName("ai_user_lineEdit")
        self.gridLayout_3.addWidget(self.ai_user_lineEdit, 2, 1, 1, 1)
        self.label_6 = QtWidgets.QLabel(Form)
        self.label_6.setObjectName("label_6")
        self.gridLayout_3.addWidget(self.label_6, 2, 2, 1, 1)
        self.room_count_lineEdit = QtWidgets.QLineEdit(Form)
        self.room_count_lineEdit.setObjectName("room_count_lineEdit")
        self.gridLayout_3.addWidget(self.room_count_lineEdit, 2, 3, 1, 1)
        self.verticalLayout_3.addLayout(self.gridLayout_3)
        self.verticalLayout.addLayout(self.verticalLayout_3)
        self.horizontalLayout.addLayout(self.verticalLayout)
        self.horizontalLayout.setStretch(0, 2)
        self.verticalLayout_2.addLayout(self.horizontalLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.startButton.setText(_translate("Form", "①启动办公"))
        self.stopButton.setText(_translate("Form", "停止工具"))
        self.label.setText(_translate("Form", "终端控制台"))
        self.clear_pushButton.setText(_translate("Form", "清理控制台"))
        self.label_5.setText(_translate("Form", "一、基本信息"))
        self.label_4.setText(_translate("Form", "手机号"))
        self.label_2.setText(_translate("Form", "微信号"))
        self.label_3.setText(_translate("Form", "微信昵称"))
        self.label_7.setText(_translate("Form", "群数量"))
        self.label_8.setText(_translate("Form", "好友数量"))
        self.label_9.setText(_translate("Form", "二、权限情况"))
        self.label_10.setText(_translate("Form", "当天剩余群发"))
        self.label_11.setText(_translate("Form", "当天剩余AI聊天"))
        self.label_12.setText(_translate("Form", "总剩余关键词"))
        self.label_13.setText(_translate("Form", "总剩余定时任务"))
        self.label_14.setText(_translate("Form", "总剩余AI群"))
        self.label_6.setText(_translate("Form", "总剩余群管理"))

yyds/child/switch_group_window.py
wangtu/wechat_robot-manage-demo
import logging
import os
import signal

from frame.base.log_handler import MyLogHandler
from frame.base.signal_thread import SignalThread
from frame.base.wcf_sss import WcfSSS
from frame.constant import Constant
from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.child.switch_group_window import Ui_Form

"""
tab - 启动的主控制器
"""


class SwitchGroupWindowMediator(BaseQWidgetMediator):
    NAME = 'SwitchGroupWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(SwitchGroupWindowMediator, self).__init__(SwitchGroupWindowMediator.NAME)
        self.LOG = logging.getLogger("SwitchGroupWindowMediator")
        self.maximum_block_count: int = 1000

        self.log_done_thread = SignalThread()
        self.log_done_thread.signal.connect(self.receiveSignal)

    def onRegister(self):
        self.LOG.info("SwitchGroupWindowMediator onRegister")
        self.getViewComponent().startButton.clicked.connect(self.startHandler)
        self.getViewComponent().stopButton.clicked.connect(self.stopHandler)
        self.getViewComponent().clear_pushButton.clicked.connect(self.clearHandler)
        self.getViewComponent().debugTextEdit.setMaximumBlockCount(self.maximum_block_count)
        myLogHandler = MyLogHandler.getInstance()
        myLogHandler.callbackFunc = self.refreshConsole

    def refreshConsole(self, record):
        """控制台日志文本刷新"""
        self.log_done_thread.data = record
        self.log_done_thread.start()

    def receiveSignal(self, text):
        """异步线程实现刷新UI界面"""
        # 槽函数
        self.getViewComponent().debugTextEdit.appendPlainText(text)

    # @thread_check(True)
    def startHandler(self):
        try:
            self.cleanPortHandler()
            self.sendNotification(Constant.START_WCF)
            self.getViewComponent().startButton.setEnabled(False)
            self.getViewComponent().stopButton.setEnabled(True)
        except Exception as e:
            print(e)

    def stopHandler(self):
        self.sendNotification(Constant.STOP_WCF)
        self.getViewComponent().startButton.setEnabled(True)
        self.getViewComponent().stopButton.setEnabled(False)

    def cleanPortHandler(self):
        self.cleanPort(Constant.WCF_PORT)
        self.cleanPort(Constant.WCF_PORT + 1)

    def checkPort(self, port: int) -> list:
        res_list = []
        # 判断端口是否被占用，占用直接把占用端口的进程给杀掉
        with os.popen(f'netstat -aon|findstr "{port}"') as res:
            res = res.read().split('\n')
            for line in res:
                temp = [i for i in line.split(' ') if i != '']
                if len(temp) > 4 and int(temp[4]) > 1:
                    res_list.append(temp[4])
        return res_list

    def cleanPort(self, port: int = Constant.WCF_PORT):
        # 判断端口是否被占用，占用直接把占用端口的进程给杀掉
        res_list = self.checkPort(port)
        try:
            if len(res_list) > 0:
                for i in res_list:
                    print(f"os kill {i}")
                    os.kill(int(i), signal.SIGINT)
        except Exception as e:
            print(e)

    def clearHandler(self):
        self.getViewComponent().debugTextEdit.clear()

    def handleNotification(self, notification):
        notName = notification.getName()
        if notName == Constant.START_WCF_SUCCESS:
            wcf: WcfSSS = notification.getBody()
            try:
                self.getViewComponent().wxIdEdit.setText(wcf.userInfo.get("wxid"))
                self.getViewComponent().wxNameEdit.setText(wcf.userInfo.get("name"))
                self.getViewComponent().mobileEdit.setText(wcf.userInfo.get("mobile"))
                self.getViewComponent().roomLenEdit.setText(str(len(wcf.chatRooms)))
                self.getViewComponent().friendLenEdit.setText(str(len(wcf.allFriends)))
            except Exception as e:
                self.LOG.info("捕获错误", str(e))
            wxid = wcf.userInfo.get("wxid")
            self.LOG.info(f"正在读取 {wxid} 角色权限")
            name = wcf.userInfo.get("name")
            mobile = wcf.userInfo.get("mobile")
            # 刷新main主界面
            self.sendNotification(Constant.UPDATE_USER_VIEW)
            self.LOG.info(f"{wxid} 角色权限获取成功")

        elif notName == Constant.STOP_WCF_SUCCESS:
            self.getViewComponent().wxIdEdit.setText("")
            self.getViewComponent().wxNameEdit.setText("")
            self.getViewComponent().mobileEdit.setText("")
            self.getViewComponent().roomLenEdit.setText("")
            self.getViewComponent().friendLenEdit.setText("")

    def listNotificationInterests(self):
        return [
            Constant.START_WCF_SUCCESS,
            Constant.STOP_WCF_SUCCESS
        ]

    def onRemove(self):
        self.LOG.info("SwitchGroupWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/child/switch_group_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'index_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(482, 308)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
        Form.setSizePolicy(sizePolicy)
        Form.setMinimumSize(QtCore.QSize(0, 0))
        Form.setStyleSheet("")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setMaximumSize(QtCore.QSize(300, 25))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.checkButton = QtWidgets.QPushButton(Form)
        font = QtGui.QFont()
        font.setFamily("Adobe Arabic")
        font.setPointSize(12)
        self.checkButton.setFont(font)
        self.checkButton.setObjectName("checkButton")
        self.verticalLayout.addWidget(self.checkButton)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.textEdit = QtWidgets.QTextEdit(Form)
        self.textEdit.setReadOnly(True)
        self.textEdit.setObjectName("textEdit")
        self.horizontalLayout_2.addWidget(self.textEdit)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.cleanPortButton = QtWidgets.QPushButton(Form)
        self.cleanPortButton.setObjectName("cleanPortButton")
        self.verticalLayout_2.addWidget(self.cleanPortButton)
        self.downButton = QtWidgets.QPushButton(Form)
        self.downButton.setObjectName("downButton")
        self.verticalLayout_2.addWidget(self.downButton)
        self.horizontalLayout_2.addLayout(self.verticalLayout_2)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.yes_checkBox = QtWidgets.QCheckBox(Form)
        self.yes_checkBox.setMinimumSize(QtCore.QSize(20, 0))
        self.yes_checkBox.setMaximumSize(QtCore.QSize(20, 16777215))
        self.yes_checkBox.setText("")
        self.yes_checkBox.setChecked(True)
        self.yes_checkBox.setObjectName("yes_checkBox")
        self.horizontalLayout_3.addWidget(self.yes_checkBox)
        self.note_label = QtWidgets.QLabel(Form)
        font = QtGui.QFont()
        font.setBold(False)
        font.setUnderline(True)
        font.setWeight(50)
        self.note_label.setFont(font)
        self.note_label.setObjectName("note_label")
        self.horizontalLayout_3.addWidget(self.note_label)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.login_pushButton = QtWidgets.QPushButton(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.login_pushButton.setFont(font)
        self.login_pushButton.setObjectName("login_pushButton")
        self.verticalLayout.addWidget(self.login_pushButton)
        self.horizontalLayout_4.addLayout(self.verticalLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "授权使用"))
        self.label.setText(_translate("Form", "欢迎使用办公小助手！"))
        self.checkButton.setText(_translate("Form", "1、环境检测"))
        self.textEdit.setPlaceholderText(_translate("Form",
                                                    "使用说明：所依赖的微信版本必须匹配3.9.2.23 ; 安装微信后到设置页选择通用设置关闭（有更新时自动升级微信）"))
        self.cleanPortButton.setText(_translate("Form", "1.1清理占用端口"))
        self.downButton.setText(_translate("Form", "1.2下载对应微信"))
        self.note_label.setText(_translate("Form", "我已阅读同意<服务和隐私协议>，同时授权系统使用登录微信做权限管理"))
        self.login_pushButton.setText(_translate("Form", "2、进入使用"))

yyds/screen/index_window.py
wangtu/wechat_robot-manage-demo
import logging
import os
import signal
import time
import webbrowser

from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QApplication

from frame.constant import Constant
from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from frame.vo.module_vo import ModuleVO
from util.software_manager import SoftwareManager
from yyds.aop.permission_aop import show_message
from yyds.screen.index_window import Ui_Form
from yyds.screen.main_window_mediator import MainWindowMediator


class IndexWindowMediator(BaseQWidgetMediator):
    NAME = 'IndexWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(IndexWindowMediator, self).__init__(IndexWindowMediator.NAME)
        self.LOG = logging.getLogger("IndexWindowMediator")

    def onRegister(self):
        self.getViewComponent().checkButton.clicked.connect(self.testEnvironment)
        self.getViewComponent().cleanPortButton.clicked.connect(self.cleanPortHandler)
        self.getViewComponent().downButton.clicked.connect(self.openDownUrl)
        self.getViewComponent().login_pushButton.clicked.connect(self.loginMain)
        self.getViewComponent().note_label.mouseReleaseEvent = self.open_webpage

    def open_webpage(self, event):
        url = "https://docs.qq.com/doc/DRVhaVU9Qa0tFU050"
        webbrowser.open(url)

    def testEnvironment(self):
        self.LOG.info("检查环境变量")
        step1: str = "-> 1- 检查环境依赖端口 " + str(Constant.WCF_PORT) + " 占用情况"
        self.getViewComponent().textEdit.append(step1)
        QApplication.processEvents()
        time.sleep(0.5)
        self.getViewComponent().textEdit.append(self.checkPortToStr(Constant.WCF_PORT))

        step1 = "\n-> 2- 检查环境依赖端口 " + str(Constant.WCF_PORT + 1) + " 占用情况"
        self.getViewComponent().textEdit.append(step1)
        QApplication.processEvents()

        time.sleep(0.5)
        self.getViewComponent().textEdit.append(self.checkPortToStr(Constant.WCF_PORT + 1))

        step1 = "\n-> 3- 检查微信版本号 "
        self.getViewComponent().textEdit.append(step1)
        QApplication.processEvents()

        time.sleep(0.5)
        manager = SoftwareManager()
        # 通过名字获取
        wechat = manager.get_by_name("微信")
        step1 = "-> " + wechat['DisplayName'] + "  " + wechat['DisplayVersion'] + "  " + wechat['InstallLocation']
        self.getViewComponent().textEdit.append(step1)

        if Constant.WECHAT_VERSION == wechat['DisplayVersion']:
            step1 = "-> 微信版本符合环境要求  √"
        else:
            step1 = "-> !!!注意！，微信版本不符合环境要求，需要安装:" + Constant.WECHAT_VERSION + "  ×"
        self.getViewComponent().textEdit.append(step1)

    @staticmethod
    def checkPortToStr(port: int) -> str:
        res_list = IndexWindowMediator.checkPort(port)
        if len(res_list) > 0:
            res = "-> !!!注意，依赖端口" + str(Constant.WCF_PORT) + "已被占用 ×"
        else:
            res = "-> 环境端口 " + str(Constant.WCF_PORT) + " 空闲，检测OK √"
        return res

    @staticmethod
    def checkPort(port: int) -> str:
        res_list = []
        # 判断端口是否被占用，占用直接把占用端口的进程给杀掉
        with os.popen(f'netstat -aon|findstr "{port}"') as res:
            res = res.read().split('\n')
            for line in res:
                temp = [i for i in line.split(' ') if i != '']
                if len(temp) > 4 and int(temp[4]) > 1:
                    res_list.append(temp[4])

        return res_list

    def cleanPortHandler(self):
        self.cleanPort(Constant.WCF_PORT)
        self.cleanPort(Constant.WCF_PORT + 1)

    def cleanPort(self, port: int = Constant.WCF_PORT):
        # 判断端口是否被占用，占用直接把占用端口的进程给杀掉
        res_list = IndexWindowMediator.checkPort(port)
        if len(res_list) > 0:
            for i in res_list:
                os.kill(int(i), signal.SIGINT)
                print(f"杀死占用端口的进程成功，该进程pid：{i}")
                tip = "被占用端口" + str(port) + "的进程成功清理，该进程pid：" + str(i)

        else:
            tip = str(port) + "端口没有被占用，无需清理"
        self.getViewComponent().textEdit.append(tip)

    @staticmethod
    def openDownUrl():
        QDesktopServices.openUrl(QUrl("https://www.gpsmap.cc/soft/121596.html"))

    def loginMain(self):
        ischeck = self.getViewComponent().yes_checkBox.isChecked()
        if not ischeck:
            show_message("提示", "请阅读服务和隐私协议，并确定勾选同意")
            return

        module: ModuleVO = ModuleVO(MainWindowMediator)
        self.sendNotification(Constant.SWITCH_QWIDGET, module)
        self.close()

    def onRemove(self):
        self.LOG.info("IndexWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_Form:
        return super().getViewComponent()

yyds/screen/index_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'login_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtGui, QtWidgets


class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        Form.resize(430, 226)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(Form.sizePolicy().hasHeightForWidth())
        Form.setSizePolicy(sizePolicy)
        Form.setMinimumSize(QtCore.QSize(0, 0))
        Form.setStyleSheet("background-color: rgb(255, 255, 255);")
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout(Form)
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinAndMaxSize)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(Form)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
        self.label.setSizePolicy(sizePolicy)
        self.label.setMaximumSize(QtCore.QSize(300, 25))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label.setFont(font)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_2 = QtWidgets.QLabel(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.username_lineEdit = QtWidgets.QLineEdit(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.username_lineEdit.setFont(font)
        self.username_lineEdit.setObjectName("username_lineEdit")
        self.horizontalLayout.addWidget(self.username_lineEdit)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.label_3 = QtWidgets.QLabel(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_2.addWidget(self.label_3)
        self.password_lineEdit = QtWidgets.QLineEdit(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.password_lineEdit.setFont(font)
        self.password_lineEdit.setEchoMode(QtWidgets.QLineEdit.PasswordEchoOnEdit)
        self.password_lineEdit.setObjectName("password_lineEdit")
        self.horizontalLayout_2.addWidget(self.password_lineEdit)
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.register_pushButton = QtWidgets.QPushButton(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.register_pushButton.setFont(font)
        self.register_pushButton.setObjectName("register_pushButton")
        self.horizontalLayout_3.addWidget(self.register_pushButton)
        self.login_pushButton = QtWidgets.QPushButton(Form)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.login_pushButton.setFont(font)
        self.login_pushButton.setObjectName("login_pushButton")
        self.horizontalLayout_3.addWidget(self.login_pushButton)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4.addLayout(self.verticalLayout)

        self.retranslateUi(Form)
        QtCore.QMetaObject.connectSlotsByName(Form)

    def retranslateUi(self, Form):
        _translate = QtCore.QCoreApplication.translate
        Form.setWindowTitle(_translate("Form", "Form"))
        self.label.setText(_translate("Form", "微信管理后台系统"))
        self.label_2.setText(_translate("Form", "用户:"))
        self.username_lineEdit.setText(_translate("Form", "test1"))
        self.label_3.setText(_translate("Form", "密码:"))
        self.password_lineEdit.setText(_translate("Form", "test"))
        self.register_pushButton.setText(_translate("Form", "注册"))
        self.login_pushButton.setText(_translate("Form", "登录"))

yyds/screen/login_window.py
wangtu/wechat_robot-manage-demo
import logging

from frame.view.base_qwidget_mediator import BaseQWidgetMediator
from yyds.screen.login_window import Ui_Form


class LoginWindowMediator(BaseQWidgetMediator):
    NAME = 'LoginWindowMediator'
    viewClass = Ui_Form

    def __init__(self, mediatorName=None):
        super(LoginWindowMediator, self).__init__(LoginWindowMediator.NAME)
        self.LOG = logging.getLogger("LoginWindowMediatorMediator")

    def onRegister(self):
        pass

    def onRemove(self):
        self.LOG.info("LoginWindowMediatorMediator onRemove")
        pass

yyds/screen/login_window_mediator.py
wangtu/wechat_robot-manage-demo
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'main_window.ui'
#
# Created by: PyQt5 UI code generator 5.15.9
#
# WARNING: Any manual changes made to this file will be lost when pyuic5 is
# run again.  Do not edit this file unless you know what you are doing.


from PyQt5 import QtCore, QtWidgets


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(944, 600)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout.setContentsMargins(5, 5, 5, 5)
        self.gridLayout.setSpacing(3)
        self.gridLayout.setObjectName("gridLayout")
        self.line_4 = QtWidgets.QFrame(self.centralwidget)
        self.line_4.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_4.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_4.setObjectName("line_4")
        self.gridLayout.addWidget(self.line_4, 1, 0, 1, 1)
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.line_3 = QtWidgets.QFrame(self.centralwidget)
        self.line_3.setMaximumSize(QtCore.QSize(16777215, 2))
        self.line_3.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line_3.setFrameShape(QtWidgets.QFrame.HLine)
        self.line_3.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_3.setObjectName("line_3")
        self.verticalLayout.addWidget(self.line_3)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_2 = QtWidgets.QLabel(self.centralwidget)
        self.label_2.setStyleSheet("font: 12pt \"宋体\";")
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.current_username_label = QtWidgets.QLabel(self.centralwidget)
        self.current_username_label.setMinimumSize(QtCore.QSize(70, 0))
        self.current_username_label.setStyleSheet("font: 12pt \"宋体\";")
        self.current_username_label.setText("")
        self.current_username_label.setObjectName("current_username_label")
        self.horizontalLayout.addWidget(self.current_username_label)
        self.label_4 = QtWidgets.QLabel(self.centralwidget)
        self.label_4.setMinimumSize(QtCore.QSize(50, 0))
        self.label_4.setStyleSheet("font: 12pt \"宋体\";")
        self.label_4.setObjectName("label_4")
        self.horizontalLayout.addWidget(self.label_4)
        self.current_role_label = QtWidgets.QLabel(self.centralwidget)
        self.current_role_label.setMinimumSize(QtCore.QSize(50, 0))
        self.current_role_label.setStyleSheet("font: 12pt \"宋体\";")
        self.current_role_label.setText("")
        self.current_role_label.setObjectName("current_role_label")
        self.horizontalLayout.addWidget(self.current_role_label)
        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem)
        self.version_lable = QtWidgets.QLabel(self.centralwidget)
        self.version_lable.setMinimumSize(QtCore.QSize(100, 0))
        self.version_lable.setObjectName("version_lable")
        self.horizontalLayout.addWidget(self.version_lable)
        self.exitButton = QtWidgets.QPushButton(self.centralwidget)
        self.exitButton.setStyleSheet("font: 12pt \"宋体\";")
        self.exitButton.setObjectName("exitButton")
        self.horizontalLayout.addWidget(self.exitButton)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)
        self.main_hbox = QtWidgets.QHBoxLayout()
        self.main_hbox.setObjectName("main_hbox")
        self.tabListWidget = QtWidgets.QListWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.tabListWidget.sizePolicy().hasHeightForWidth())
        self.tabListWidget.setSizePolicy(sizePolicy)
        self.tabListWidget.setMaximumSize(QtCore.QSize(100, 16777215))
        self.tabListWidget.setObjectName("tabListWidget")
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        item = QtWidgets.QListWidgetItem()
        self.tabListWidget.addItem(item)
        self.main_hbox.addWidget(self.tabListWidget)
        self.line = QtWidgets.QFrame(self.centralwidget)
        self.line.setMaximumSize(QtCore.QSize(2, 16777215))
        self.line.setStyleSheet("background-color: rgb(222, 222, 222);")
        self.line.setFrameShape(QtWidgets.QFrame.VLine)
        self.line.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line.setObjectName("line")
        self.main_hbox.addWidget(self.line)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.gridLayout_2 = QtWidgets.QGridLayout()
        self.gridLayout_2.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
        self.gridLayout_2.setHorizontalSpacing(4)
        self.gridLayout_2.setVerticalSpacing(3)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.sel_label = QtWidgets.QLabel(self.centralwidget)
        self.sel_label.setMinimumSize(QtCore.QSize(80, 0))
        self.sel_label.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)
        self.sel_label.setObjectName("sel_label")
        self.gridLayout_2.addWidget(self.sel_label, 2, 6, 1, 1)
        self.selAllCheckBox = QtWidgets.QCheckBox(self.centralwidget)
        self.selAllCheckBox.setLayoutDirection(QtCore.Qt.LeftToRight)
        self.selAllCheckBox.setObjectName("selAllCheckBox")
        self.gridLayout_2.addWidget(self.selAllCheckBox, 2, 2, 1, 1)
        self.line_5 = QtWidgets.QFrame(self.centralwidget)
        self.line_5.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_5.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_5.setObjectName("line_5")
        self.gridLayout_2.addWidget(self.line_5, 2, 3, 5, 1)
        self.clearPushButton = QtWidgets.QPushButton(self.centralwidget)
        self.clearPushButton.setMinimumSize(QtCore.QSize(60, 0))
        self.clearPushButton.setObjectName("clearPushButton")
        self.gridLayout_2.addWidget(self.clearPushButton, 2, 5, 1, 1)
        self.refreshPushButton = QtWidgets.QPushButton(self.centralwidget)
        self.refreshPushButton.setMinimumSize(QtCore.QSize(60, 0))
        self.refreshPushButton.setObjectName("refreshPushButton")
        self.gridLayout_2.addWidget(self.refreshPushButton, 2, 1, 1, 1)
        self.selectListWidget = QtWidgets.QListWidget(self.centralwidget)
        self.selectListWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.selectListWidget.setToolTip("")
        self.selectListWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.selectListWidget.setObjectName("selectListWidget")
        self.gridLayout_2.addWidget(self.selectListWidget, 5, 4, 1, 4)
        self.typComboBox = QtWidgets.QComboBox(self.centralwidget)
        self.typComboBox.setMinimumSize(QtCore.QSize(85, 0))
        self.typComboBox.setObjectName("typComboBox")
        self.typComboBox.addItem("")
        self.typComboBox.addItem("")
        self.typComboBox.addItem("")
        self.gridLayout_2.addWidget(self.typComboBox, 2, 0, 1, 1)
        self.rowListFilterEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.rowListFilterEdit.setObjectName("rowListFilterEdit")
        self.gridLayout_2.addWidget(self.rowListFilterEdit, 3, 0, 1, 3)
        self.SelectListFilterEdit = QtWidgets.QLineEdit(self.centralwidget)
        self.SelectListFilterEdit.setObjectName("SelectListFilterEdit")
        self.gridLayout_2.addWidget(self.SelectListFilterEdit, 3, 4, 1, 4)
        self.rawListWidget = QtWidgets.QListWidget(self.centralwidget)
        self.rawListWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
        self.rawListWidget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
        self.rawListWidget.setObjectName("rawListWidget")
        self.gridLayout_2.addWidget(self.rawListWidget, 5, 0, 1, 3)
        self.horizontalLayout_2.addLayout(self.gridLayout_2)
        self.main_hbox.addLayout(self.horizontalLayout_2)
        self.line_2 = QtWidgets.QFrame(self.centralwidget)
        self.line_2.setFrameShape(QtWidgets.QFrame.VLine)
        self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)
        self.line_2.setObjectName("line_2")
        self.main_hbox.addWidget(self.line_2)
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
        sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.stackedWidget.sizePolicy().hasHeightForWidth())
        self.stackedWidget.setSizePolicy(sizePolicy)
        self.stackedWidget.setObjectName("stackedWidget")
        self.verticalLayout_2.addWidget(self.stackedWidget)
        self.main_hbox.addLayout(self.verticalLayout_2)
        self.main_hbox.setStretch(0, 1)
        self.main_hbox.setStretch(1, 3)
        self.main_hbox.setStretch(2, 3)
        self.main_hbox.setStretch(4, 5)
        self.gridLayout.addLayout(self.main_hbox, 2, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "启动和管理窗口"))
        self.label_2.setText(_translate("MainWindow", "用户:"))
        self.label_4.setText(_translate("MainWindow", " 角色:"))
        self.version_lable.setText(_translate("MainWindow", "版本号"))
        self.exitButton.setText(_translate("MainWindow", "登出"))
        __sortingEnabled = self.tabListWidget.isSortingEnabled()
        self.tabListWidget.setSortingEnabled(False)
        item = self.tabListWidget.item(0)
        item.setText(_translate("MainWindow", "启动"))
        item = self.tabListWidget.item(1)
        item.setText(_translate("MainWindow", "消息群发"))
        item = self.tabListWidget.item(2)
        item.setText(_translate("MainWindow", "定时回复"))
        item = self.tabListWidget.item(3)
        item.setText(_translate("MainWindow", "自动回复"))
        item = self.tabListWidget.item(4)
        item.setText(_translate("MainWindow", "智能AI聊天"))
        item = self.tabListWidget.item(5)
        item.setText(_translate("MainWindow", "群管理"))
        item = self.tabListWidget.item(6)
        item.setText(_translate("MainWindow", "基础设置"))
        item = self.tabListWidget.item(7)
        item.setText(_translate("MainWindow", "关于"))
        self.tabListWidget.setSortingEnabled(__sortingEnabled)
        self.sel_label.setText(_translate("MainWindow", "联系人0"))
        self.selAllCheckBox.setText(_translate("MainWindow", "全选"))
        self.clearPushButton.setText(_translate("MainWindow", "清空"))
        self.refreshPushButton.setText(_translate("MainWindow", "数据刷新"))
        self.typComboBox.setItemText(0, _translate("MainWindow", "所有联系人"))
        self.typComboBox.setItemText(1, _translate("MainWindow", "选择群"))
        self.typComboBox.setItemText(2, _translate("MainWindow", "选择好友"))
        self.rowListFilterEdit.setPlaceholderText(_translate("MainWindow", "结果过滤"))
        self.SelectListFilterEdit.setPlaceholderText(_translate("MainWindow", "待操作联系人过滤"))

yyds/screen/main_window.py
wangtu/wechat_robot-manage-demo
import logging
import re
import threading
import time

from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QListWidgetItem, QListWidget
from PyQt5.QtWidgets import QMessageBox

from frame.base.wcf_sss import WcfSSS
from frame.constant import Constant
from frame.view.base_qmainwindow_mediator import BaseQMainWindowMediator
from frame.vo.module_vo import ModuleVO
from yyds.aop.permission_aop import permission_wcf, show_message
from yyds.child.aichat_group_window_mediator import AIChatGroupWindowMediator
from yyds.child.job_group_window_mediator import JobGroupWindowMediator
from yyds.child.key_group_window_mediator import KeyGroupWindowMediator
from yyds.child.message_group_window_mediator import MessageGroupWindowMediator
from yyds.child.room_group_window_mediator import RoomGroupWindowMediator
from yyds.child.setting_group_window_mediator import SettingGroupWindowMediator
from yyds.child.switch_group_window_mediator import SwitchGroupWindowMediator
from yyds.screen.main_window import Ui_MainWindow


class MainWindowMediator(BaseQMainWindowMediator):
    NAME = 'MainWindowMediator'
    viewClass = Ui_MainWindow

    def __init__(self, mediatorName=None):
        super(MainWindowMediator, self).__init__(MainWindowMediator.NAME)
        self.selectContracts = {}
        self.LOG = logging.getLogger("MainWindowMediator")
        self.IS_EXIT = False

    def onRegister(self):
        self.LOG.info("MainWindowMediator onRegister")
        self.getViewComponent().version_lable.setText("当前版本:" + Constant.VERSION)

        # 为了释放异步刷新界面的能力
        thread = threading.Thread(target=self.start_handler)
        thread.start()

    def start_handler(self):
        self.getViewComponent().tabListWidget.currentItemChanged.connect(self.item_changed)
        self.getViewComponent().exitButton.clicked.connect(self.log_out)

        self.getViewComponent().refreshPushButton.clicked.connect(self.refresh_contracts_list)
        self.getViewComponent().typComboBox.currentTextChanged.connect(self.change_typeCombobox)
        self.getViewComponent().selAllCheckBox.clicked.connect(self.all_check_contracts)
        # self.getViewComponent().selCheckBox.clicked.connect(self.sel_check_contracts)
        self.getViewComponent().clearPushButton.clicked.connect(self.clear_select_contracts)
        self.getViewComponent().rowListFilterEdit.textChanged.connect(self.on_textChangedL)
        self.getViewComponent().rawListWidget.itemSelectionChanged.connect(self.itemSelectHandler)
        self.getViewComponent().selectListWidget.itemSelectionChanged.connect(self.itemDelHandler)
        self.getViewComponent().SelectListFilterEdit.textChanged.connect(self.on_textChangedR)

        self.getViewComponent().tabListWidget.setCurrentRow(0)

        self.getViewComponent().version_lable.setText("当前版本:" + Constant.VERSION)

    @permission_wcf
    def refresh_contracts_list(self):
        print("refresh_contracts_list click")
        dataList = []
        self.getViewComponent().rawListWidget.clear()

        txt = self.getViewComponent().typComboBox.currentText()
        if txt == "所有联系人":
            dataList = WcfSSS.getInstance().chatRooms + WcfSSS.getInstance().allFriends
        elif txt == "选择群":
            dataList = WcfSSS.getInstance().chatRooms
        elif txt == "选择好友":
            dataList = WcfSSS.getInstance().allFriends

        for data in dataList:
            if data.Remark:
                item = QListWidgetItem(f"[{data.Remark}] {data.NickName}")
            elif data.NickName and not re.match(r'^\s*$', data.NickName):
                item = QListWidgetItem(data.NickName)
            else:
                if data.UserName.endswith("@chatroom") == "选择群":
                    roomName = "[群]" + WcfSSS.getInstance().getChatRoom_Name(data.UserName, 6)
                    if roomName:
                        item = QListWidgetItem(roomName)
                    else:
                        item = QListWidgetItem(data.UserName)
                else:
                    item = QListWidgetItem(data.UserName)
            item.setData(Qt.UserRole, data.UserName)
            self.getViewComponent().rawListWidget.addItem(item)

    def change_typeCombobox(self):
        self.refresh_contracts_list()

    def all_check_contracts(self):
        if self.getViewComponent().selAllCheckBox.isChecked():
            self.getViewComponent().rawListWidget.selectAll()
        else:
            self.getViewComponent().rawListWidget.clearSelection()

    def clear_select_contracts(self):
        """ 清空已选择列表"""
        self.getViewComponent().selectListWidget.clear()
        self.selectContracts = {}
        self.getViewComponent().sel_label.setText("联系人0")

    def add_select_contracts(self):
        """添加选择联系"""
        items = self.getViewComponent().rawListWidget.selectedItems()

        for item in items:
            key = item.data(Qt.UserRole)
            if self.selectContracts.get(key) is None:
                self.selectContracts[key] = item.text()
                self.getViewComponent().selectListWidget.addItem(item.clone())

        if len(items) > 0:
            items = self.getListItems(self.getViewComponent().selectListWidget)
            self.getViewComponent().sel_label.setText(f"联系人{len(items)}")

    def del_select_contracts(self):
        """删除选择的联系人"""
        items = self.getViewComponent().selectListWidget.selectedItems()
        for item in items:
            row = self.getViewComponent().selectListWidget.row(item)
            self.getViewComponent().selectListWidget.takeItem(row)

            key = item.data(Qt.UserRole)
            del self.selectContracts[key]

        self.getViewComponent().sel_label.setText(f"联系人{self.getViewComponent().selectListWidget.count()}")

    def itemSelectHandler(self):
        self.add_select_contracts()

    def itemDelHandler(self):
        self.del_select_contracts()

    def getListItems(self, lw: QListWidget = None):
        if lw is None:
            lw = self.getViewComponent().selectListWidget
        items = []
        for x in range(lw.count()):
            i = lw.item(x)
            if not i.isHidden():
                items.append(i)
        return items

    def on_textChangedL(self, text):
        self.getViewComponent().rawListWidget.clearSelection()
        self._on_text_changed(text, self.getViewComponent().rawListWidget)

    def on_textChangedR(self, text):
        self.getViewComponent().selectListWidget.clearSelection()
        self._on_text_changed(text, self.getViewComponent().selectListWidget)
        items = self.getListItems(self.getViewComponent().selectListWidget)
        self.getViewComponent().sel_label.setText(f"联系{len(items)}")

    def _on_text_changed(self, text, listWidget: QListWidget):
        count = listWidget.count()
        for row in range(count):
            it = listWidget.item(row)
            if text:
                it.setHidden(not self.filter(text, it.text()))
            else:
                it.setHidden(False)

    def filter(self, text, keywords):
        # foo filter
        # in the example the text must be in keywords
        return text in keywords

    def item_changed(self, current, previous):
        print('Current item:', current.text())
        print('Previous item:', previous.text() if previous else 'None')
        if current.text() == "启动":
            module: ModuleVO = ModuleVO(SwitchGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "消息群发":
            module: ModuleVO = ModuleVO(MessageGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "定时回复":
            module: ModuleVO = ModuleVO(JobGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "自动回复":
            module: ModuleVO = ModuleVO(KeyGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "智能AI聊天":
            module: ModuleVO = ModuleVO(AIChatGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "群管理":
            module: ModuleVO = ModuleVO(RoomGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "基础设置":
            module: ModuleVO = ModuleVO(SettingGroupWindowMediator)
            module.parentUI = self.getViewComponent().stackedWidget
            self.sendNotification(Constant.SWITCH_QWIDGET, module)
        elif current.text() == "关于":
            show_message("更多信息，请关注", "https://k23x0697eg.feishu.cn/docx/LpdSdSVQ1oBVi4xVZ8lccBi9nYn")

    def log_out(self):
        self.close()

    def closeEvent(self, event):
        a = QMessageBox.question(self, '退出', '你确定要退出吗?', QMessageBox.Yes | QMessageBox.No,
                                 QMessageBox.No)  # "退出"代表的是弹出框的标题,"你确认退出.."表示弹出框的内容
        if a == QMessageBox.Yes:
            self.IS_EXIT = True
            wcf = WcfSSS.getInstance()
            if wcf is not None and wcf.is_running():
                self.sendNotification(Constant.STOP_WCF)
                time.sleep(2)
            event.accept()  # 接受关闭事件
            super().closeEvent(event)
        else:
            event.ignore()  # 忽略关闭事件

    def handleNotification(self, notification):
        notName = notification.getName()
        if notName == Constant.UPDATE_USER_VIEW:
            self.refresh_contracts_list()
        elif notName == Constant.STOP_WCF_SUCCESS:
            self.clear_select_contracts()
            self.getViewComponent().rawListWidget.clear()

    def listNotificationInterests(self):
        return [
            Constant.STOP_WCF_SUCCESS,
            Constant.UPDATE_USER_VIEW
        ]

    def onRemove(self):
        self.sendNotification(Constant.CLOSE_CHILD_QWIDGET,
                              [MessageGroupWindowMediator, JobGroupWindowMediator, KeyGroupWindowMediator,
                               AIChatGroupWindowMediator])
        self.LOG.info("MainWindowMediator onRemove")
        pass

    def getViewComponent(self) -> Ui_MainWindow:
        return super().getViewComponent()

yyds/screen/main_window_mediator.py
wangtu/wechat_robot-manage-demo