zoukankan      html  css  js  c++  java
  • Python3.5+PyQt5多线程+itchat实现微信防撤回桌面版代码

    weChatThread线程类

    之前一直不会python多线程,写这个程序的时候,发现不用多线程会陷入无限未响应状态。于是学了半天python多线程,但是在主函数里写的时候,发现一个问题,Ui主线程和工作线程没有分离,使用itchat等库的时候会堵塞主线程,换句话说PyQt中子线程不能操作GUI界面。之前写的多线程仍然属于Ui主线程,是其子线程,所以才造成未响应。
    既然知道问题了,那就查资料解决问题,后来,在几篇博客上找到了解决办法

    然后仿照第一篇博客,重写了QThread类,并借鉴第三篇博客,学会了PyQt多线程中的信号/槽机制,用来传递参数。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    __author__ = 'memgq'
    
    from PyQt5.QtCore import QThread,pyqtSignal
    import itchat
    import time,os
    import shutil
    import re
    
    from itchat.content import *
    
    class weChatWord(QThread):
        getMsgSignal = pyqtSignal(str) #pyqtSignal()必须写在__init__前面,里面可接收的参数类型挺多的,str,list,dict都支持
        def __init__(self,parent=None):
            super(weChatWord,self).__init__(parent)
            self.msg_list=[]
            self.type_list=['Picture','Recording', 'Attachment','Video']
    
    
    
        def clearList(self):
            '''
            清空缓存消息和文件
            :return: 
            '''
            tm_now=time.time()
            len_list=len(self.msg_list)
            if len_list>0:
                for i in range(len_list):
                    if tm_now-self.msg_list[i]['msg_time']>121:
                        if self.msg_list[i]['msg_type'] in self.type_list:
                            try:
                                os.remove(".\BackUp\"+self.msg_list[i]['msg_content'])
                            except Exception as e:
                                print(e)
                            finally:
                                pass
                    else:break
                self.msg_list=self.msg_list[i:]
    
    
    
        def run(self):
            '''
            重写run()函数,
            :return:
            '''
            @itchat.msg_register([TEXT, PICTURE, MAP, CARD, SHARING, RECORDING, ATTACHMENT, VIDEO, FRIENDS], isFriendChat=True,
                          isGroupChat=True)
            def getMsg(msg):
                '''
                注册消息类型,并对不同类型的消息执行不用的操作
                :param msg: 
                :return: 
                '''
                msg_dict={}
                # pprint.pprint(msg)
                msg_id = msg['MsgId']  # 消息ID
                msg_time = msg['CreateTime']
                msg_url=None
                msg_group=""
                if (itchat.search_friends(userName=msg['FromUserName'])):
                    if itchat.search_friends(userName=msg['FromUserName'])['RemarkName']:
                        msg_from = itchat.search_friends(userName=msg['FromUserName'])['RemarkName']  # 消息发送人备注
                    elif itchat.search_friends(userName=msg['FromUserName'])['NickName']:  # 消息发送人昵称
                        msg_from = itchat.search_friends(userName=msg['FromUserName'])['NickName']  # 消息发送人昵称
                    else:
                        msg_from = r"读取发送消息好友失败"
                else:
                    msg_group = msg['User']['NickName']
                    msg_from = msg['ActualNickName']
                msg_type = msg['Type']
                if msg_type in ['Text', 'Friends','Sharing']:
                    msg_content = msg['Text']
                    msg_url = msg['Url']
                elif msg_type in self.type_list:
                    msg_content=msg['FileName']
                    msg['Text'](msg['FileName'])
                    shutil.move(msg_content,r'.\BackUp\')
                elif msg['Type'] == 'Card':
                    msg_content = msg['RecommendInfo']['NickName'] + r" 的名片"
                elif msg['Type'] == 'Map':
                    x, y, location = re.search("<location x="(.*?)" y="(.*?)".*label="(.*?)".*",
                                               msg['OriContent']).group(1,
                                                                        2,
                                                                        3)
                    if location is None:
                        msg_content = r"纬度->" + x.__str__() + " 经度->" + y.__str__()
                    else:
                        msg_content = r"" + location
    
                msg_dict={'msg_id':msg_id,'msg_time':msg_time,'msg_from':msg_from,'msg_group':msg_group,
                          'msg_content':msg_content,'msg_type':msg_type,'msg_url':msg_url}
                self.msg_list.append(msg_dict)
                self.clearList()
    
    
            @itchat.msg_register([NOTE],isFriendChat=True, isGroupChat=True)
            def recall(msg):
                '''
                当消息类型为通知类的时候,查找消息内容是否为撤回消息,如果是,则执行撤回后的防撤回操作
                :param msg: 
                :return: 
                '''
                # pprint.pprint(msg)
                msg_content=msg['Content']
                if re.search(r'<replacemsg><![CDATA[(.*)撤回了一条消息]]></replacemsg>',msg_content):
                    msg_note=re.search(r'<replacemsg><![CDATA[(.*)]]></replacemsg>',msg_content).group(1)
                    old_msg_id=re.search(r'<msgid>([0-9]+)</msgid>',msg_content).group(1)
                    for each in self.msg_list:
                        if each['msg_id']==old_msg_id:
                            timeArray = time.localtime()
                            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S,", timeArray)
                            msg_note = msg_note + ',撤回内容为:' + each['msg_content']
                            if each['msg_group']!='':
                                msg_note = "群组("+each['msg_group']+")中"+msg_note
                            msg_note=otherStyleTime+msg_note
                            itchat.send(msg_note,toUserName='filehelper')
                            self.msg_list.pop(self.msg_list.index(each))
                            self.getMsgSignal.emit(msg_note)
                            break
    
            #创建BuckUp文件夹
            if not os.path.exists(".\BackUp\"):
                os.mkdir('.\BackUp\')
            #启动itchat()    
            itchat.auto_login(hotReload=True)
            itchat.run()

    主程序类

    class mainwindowapp(QMainWindow,wechatunrecall.Ui_MainWindow):
        def __init__(self):
            super().__init__()
            self.setupUi(self)
            self.createActions()
            self.createTrayIcon()
            self.pushButton.clicked.connect(self.saveLog)
            self.pushButton_2.clicked.connect(self.clearlog)
            self.pushButton_3.clicked.connect(self.houtai)
            self.trayIcon.activated.connect(self.iconActivated)
            timeArray = time.localtime()
            otherStyleTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
            self.setLog(otherStyleTime+",程序运行时,请用手机扫描弹出的二维码进行登录,并确保电脑上自带的Window照片查"
                                       "看器可用,撤回的图片文件等可下载附件连同运行日志保存在程序目录下BackUp文件夹中。
    ")
            self.weChatBigWord()
    
    
        def saveLog(self):
            '''
            保存日志
            :return: 
            '''
            if not os.path.exists(".\BackUp\"):
                os.mkdir(".\BackUp\")
            timeArray = time.localtime()
            otherStyleTime = time.strftime("%Y-%m-%d%H%M%S", timeArray)
            text=self.textBrowser.toPlainText()
            logPath=".\BackUp\"+otherStyleTime+'.txt'
            with open(logPath,'w') as f:
                f.write(text)
    
        def setLog(self,msg):
            '''
            往运行日志窗口写撤回消息的内容
            :param msg: 
            :return: 
            '''
            self.textBrowser.append(msg)
    
        def createTrayIcon(self):
            '''
            创建托盘图标,可以让程序最小化到windows托盘中运行
            :return: 
            '''
            self.trayIconMenu=QMenu(self)
            self.trayIconMenu.addAction(self.restoreAction)
            self.trayIconMenu.addSeparator()
            self.trayIconMenu.addAction(self.quitAction)
            self.trayIcon=QSystemTrayIcon(self)
            self.trayIcon.setContextMenu(self.trayIconMenu)
            self.trayIcon.setIcon(QIcon('./media/images/maincion.png'))
            self.setWindowIcon(QIcon('./media/images/maincion.png'))
            self.trayIcon.show()
    
        def createActions(self):
            '''
            为托盘图标添加功能
            :return: 
            '''
            self.restoreAction=QAction("恢复",self,triggered=self.showNormal)
            self.quitAction=QAction("退出",self,triggered=QApplication.instance().quit)
    
    
        def iconActivated(self,reason):
            '''
            激活托盘功能
            :param reason: 
            :return: 
            '''
            if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick):
                self.showNormal()
    
    
        def houtai(self):
            self.hide()
    
        def clearlog(self):
            self.textBrowser.clear()
    
    
        def weChatBigWord(self):
            '''
            weChatThread类实例化,并启动线程
            :return: 
            '''
            from weChatThread import weChatWord
            self.wcBWThread=weChatWord()
            self.wcBWThread.getMsgSignal.connect(self.setLog)
            self.wcBWThread.start()

    程序界面

    程序界面仍然由Qtdesigner设计

    后记

    第一次尝试多线程编程,并且具体应用到实际项目中去,收获良多。

  • 相关阅读:
    HDU 1010 Tempter of the Bone
    HDU 4421 Bit Magic(奇葩式解法)
    HDU 2614 Beat 深搜DFS
    HDU 1495 非常可乐 BFS 搜索
    Road to Cinema
    Sea Battle
    Interview with Oleg
    Spotlights
    Substring
    Dominating Patterns
  • 原文地址:https://www.cnblogs.com/pythonClub/p/10251939.html
Copyright © 2011-2022 走看看