diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index bc75c5a..066ce8f 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,10 +4,11 @@
-
+
-
-
+
+
+
@@ -28,6 +29,11 @@
+
@@ -38,6 +44,29 @@
}
+
@@ -70,31 +99,12 @@
}
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -116,27 +126,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -152,13 +141,13 @@
-
+
-
+
@@ -170,7 +159,28 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -200,13 +210,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
@@ -222,62 +253,6 @@
1672848140146
-
- 1675168912759
-
-
-
- 1675168912759
-
-
- 1675169020409
-
-
-
- 1675169020409
-
-
- 1675268807882
-
-
-
- 1675268807882
-
-
- 1678034382207
-
-
-
- 1678034382207
-
-
- 1680232548867
-
-
-
- 1680232548867
-
-
- 1680441801589
-
-
-
- 1680441801589
-
-
- 1680624068689
-
-
-
- 1680624068689
-
-
- 1680630659802
-
-
-
- 1680630659802
-
1680670947385
@@ -565,7 +540,63 @@
1699973865386
-
+
+ 1700046106844
+
+
+
+ 1700046106844
+
+
+ 1700052873083
+
+
+
+ 1700052873083
+
+
+ 1700056651470
+
+
+
+ 1700056651470
+
+
+ 1700058733119
+
+
+
+ 1700058733119
+
+
+ 1700060367255
+
+
+
+ 1700060367255
+
+
+ 1700063620952
+
+
+
+ 1700063620952
+
+
+ 1700064404522
+
+
+
+ 1700064404522
+
+
+ 1700145601841
+
+
+
+ 1700145601841
+
+
@@ -601,14 +632,6 @@
-
-
-
-
-
-
-
-
@@ -626,7 +649,15 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/app/DataBase/micro_msg.py b/app/DataBase/micro_msg.py
index 972a258..2abfa61 100644
--- a/app/DataBase/micro_msg.py
+++ b/app/DataBase/micro_msg.py
@@ -1,9 +1,27 @@
+import os.path
import sqlite3
-from pprint import pprint
-DB = sqlite3.connect("./de_MicroMsg.db", check_same_thread=False)
-# '''创建游标'''
-cursor = DB.cursor()
+DB = None
+cursor = None
+micromsg_path = "./app/Database/Msg/MicroMsg.db"
+if os.path.exists(micromsg_path):
+ DB = sqlite3.connect(micromsg_path, check_same_thread=False)
+ # '''创建游标'''
+ cursor = DB.cursor()
+
+
+def init_database():
+ global DB
+ global cursor
+ if not DB:
+ if os.path.exists(micromsg_path):
+ DB = sqlite3.connect(micromsg_path, check_same_thread=False)
+ # '''创建游标'''
+ cursor = DB.cursor()
+
+
+def is_database_exist():
+ return os.path.exists(micromsg_path)
def get_contact():
@@ -14,10 +32,16 @@ def get_contact():
'''
cursor.execute(sql)
result = cursor.fetchall()
- pprint(result)
- print(len(result))
+ # pprint(result)
+ # print(len(result))
return result
+def close():
+ global DB
+ if DB:
+ DB.close()
+
+
if __name__ == '__main__':
get_contact()
diff --git a/app/DataBase/misc.py b/app/DataBase/misc.py
new file mode 100644
index 0000000..357d6ce
--- /dev/null
+++ b/app/DataBase/misc.py
@@ -0,0 +1,29 @@
+import os.path
+import sqlite3
+
+DB = None
+cursor = None
+misc_path = "./app/Database/Msg/Misc.db"
+# misc_path = './Msg/Misc.db'
+if os.path.exists(misc_path):
+ DB = sqlite3.connect(misc_path, check_same_thread=False)
+ # '''创建游标'''
+ cursor = DB.cursor()
+
+
+def get_avatar_buffer(userName):
+ sql = '''
+ select smallHeadBuf
+ from ContactHeadImg1
+ where usrName=?;
+ '''
+ cursor.execute(sql, [userName])
+ result = cursor.fetchall()
+ # print(result[0][0])
+ if result:
+ return result[0][0]
+ return None
+
+
+if __name__ == '__main__':
+ get_avatar_buffer('wxid_al2oan01b6fn11')
diff --git a/app/DataBase/msg.py b/app/DataBase/msg.py
new file mode 100644
index 0000000..6aa9b6b
--- /dev/null
+++ b/app/DataBase/msg.py
@@ -0,0 +1,71 @@
+import os.path
+import re
+import sqlite3
+
+DB = []
+cursor = []
+msg_root_path = "./app/Database/Msg/"
+# misc_path = './Msg/Misc.db'
+if os.path.exists(msg_root_path):
+ for root, dirs, files in os.walk(msg_root_path):
+ for file in files:
+ if re.match('^MSG[0-9]+\.db$', file):
+ # print('ok', file)
+ msg_path = os.path.join(msg_root_path, file)
+ DB0 = sqlite3.connect(msg_path, check_same_thread=False)
+ # '''创建游标'''
+ cursor0 = DB0.cursor()
+ DB.append(DB0)
+ cursor.append(cursor0)
+
+
+def init_database():
+ global DB
+ global cursor
+ if not DB:
+ if os.path.exists(msg_root_path):
+ for root, dirs, files in os.walk(msg_root_path):
+ for file in files:
+ # print(file)
+ if re.match('^MSG[0-9]+\.db$', file):
+ print('ok', file)
+ msg_path = os.path.join(msg_root_path, file)
+ DB0 = sqlite3.connect(msg_path, check_same_thread=False)
+ # '''创建游标'''
+ cursor0 = DB0.cursor()
+ DB.append(DB0)
+ cursor.append(cursor0)
+
+
+def get_messages(username_):
+ sql = '''
+ select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
+ from MSG
+ where StrTalker=?
+ order by CreateTime
+ '''
+ result = []
+ for cur in cursor:
+ cur.execute(sql, [username_])
+ result_ = cur.fetchall()
+ # print(len(result))
+ result += result_
+ result.sort(key=lambda x: x[5])
+ return result
+
+
+def close():
+ for db in DB:
+ db.close()
+
+
+if __name__ == '__main__':
+ from pprint import pprint
+
+ msg_root_path = './Msg/'
+ init_database()
+
+ username = 'wxid_0o18ef858vnu22'
+ result = get_messages(username)
+ pprint(result)
+ pprint(len(result))
diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py
new file mode 100644
index 0000000..34bf547
--- /dev/null
+++ b/app/DataBase/output_pc.py
@@ -0,0 +1,56 @@
+import os
+
+import pandas as pd
+from PyQt5.QtCore import pyqtSignal, QThread
+
+from . import msg
+from ..log import log
+
+
+class Output(QThread):
+ """
+ 发送信息线程
+ """
+ progressSignal = pyqtSignal(int)
+ rangeSignal = pyqtSignal(int)
+ okSignal = pyqtSignal(int)
+ i = 1
+ CSV = 0
+ DOCX = 1
+ HTML = 2
+
+ def __init__(self, contact, parent=None, type_=DOCX):
+ super().__init__(parent)
+ self.sec = 2 # 默认1000秒
+ self.contact = contact
+ self.ta_username = contact.wxid
+ self.msg_id = 0
+ self.output_type = type_
+ self.total_num = 0
+ self.num = 0
+
+ @log
+ def to_csv(self, conRemark, path):
+ origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
+ if not os.path.exists(origin_docx_path):
+ os.mkdir(origin_docx_path)
+ filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.csv"
+ # columns = ["用户名", "消息内容", "发送时间", "发送状态", "消息类型", "isSend", "msgId"]
+ columns = ['localId', 'TalkerId', 'Type', 'SubType',
+ 'IsSender', 'CreateTime', 'Status', 'StrContent',
+ 'StrTime']
+ messages = msg.get_messages(self.contact.wxid)
+ # print()
+ df = pd.DataFrame(
+ data=messages,
+ columns=columns,
+ )
+ df.to_csv(filename, encoding='utf-8')
+ self.okSignal.emit('ok')
+
+ def run(self):
+ if self.output_type == self.DOCX:
+ return
+ elif self.output_type == self.CSV:
+ # print("线程导出csv")
+ self.to_csv(self.ta_username, "path")
diff --git a/app/Ui/MyComponents/__init__.py b/app/Ui/MyComponents/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/app/Ui/__init__.py b/app/Ui/__init__.py
index 380c558..730bbfa 100644
--- a/app/Ui/__init__.py
+++ b/app/Ui/__init__.py
@@ -13,6 +13,5 @@ from app.Ui import mainview
# 文件__init__.py
# from login import login
from app.Ui.decrypt import decrypt
-from app.Ui.pc_decrypt import pc_decrypt
-__all__ = ["decrypt", 'mainview', 'chat', 'pc_decrypt']
+__all__ = ["decrypt", 'mainview', 'chat']
diff --git a/app/Ui/chat/chat.py b/app/Ui/chat/chat.py
index 068a8ff..e913a63 100644
--- a/app/Ui/chat/chat.py
+++ b/app/Ui/chat/chat.py
@@ -17,7 +17,7 @@ from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
-from app.Ui.MyComponents.Button_Contact import ContactUi
+from app.components.Button_Contact import ContactUi
from app.log import logger, log
from .chatUi import *
from ...DataBase import data
diff --git a/app/Ui/contact/contact.py b/app/Ui/contact/contact.py
index 99556b7..aab92cb 100644
--- a/app/Ui/contact/contact.py
+++ b/app/Ui/contact/contact.py
@@ -13,7 +13,7 @@ from PyQt5 import QtCore
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
-import app.Ui.MyComponents.Button_Contact as MyLabel
+import app.components.Button_Contact as MyLabel
from app import person
from app.DataBase import data
from app.Ui.contact.contactInfo import ContactInfo
diff --git a/app/Ui/contact/emotion/emotion.py b/app/Ui/contact/emotion/emotion.py
index f0ede65..c33a4e8 100644
--- a/app/Ui/contact/emotion/emotion.py
+++ b/app/Ui/contact/emotion/emotion.py
@@ -32,6 +32,7 @@ def emotion_analysis(wxid):
emotions.append(s / len(messages))
emotions = np.array(emotions)
emotions = np.around(emotions, 3) * 100
+ emotions = np.around(emotions, 1)
return dates, emotions
diff --git a/app/Ui/decrypt/decrypt.py b/app/Ui/decrypt/decrypt.py
index 981879b..7204a7c 100644
--- a/app/Ui/decrypt/decrypt.py
+++ b/app/Ui/decrypt/decrypt.py
@@ -51,7 +51,11 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
def get_db(self):
self.db_path, _ = QFileDialog.getOpenFileName(self, 'Open file', r'..', "Database files (*.db)")
if self.db_path:
- if self.db_path.isascii():
+ if ' ' in self.db_path:
+ self.label_db.setText('数据库未就绪')
+ QMessageBox.critical(self, "错误", "db文件路径请不要带有空格\n可以放在D:\\\\data 目录下")
+ self.db_path = ''
+ elif self.db_path.isascii():
self.label_db.setText('数据库已就绪')
return self.db_path
else:
diff --git a/app/Ui/mainview.py b/app/Ui/mainview.py
index ce69bf6..74b6129 100644
--- a/app/Ui/mainview.py
+++ b/app/Ui/mainview.py
@@ -11,13 +11,14 @@ from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
-from app import person, config
-from app.DataBase import *
+from app import config
+from app.DataBase import data
from app.Ui import mainwindow
from app.Ui.Icon import Icon
-from app.Ui.MyComponents.prompt_bar import PromptBar
from app.Ui.chat import chat
from app.Ui.contact import contact
+from app.components.prompt_bar import PromptBar
+from app.person import Me
class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
@@ -28,9 +29,9 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
super(MainWinController, self).__init__(parent)
self.setupUi(self)
self.setWindowIcon(Icon.MainWindow_Icon)
- self.Me = person.Me(data.get_myinfo())
- self.setAttribute(Qt.WA_AttributeCount)
+ self.setAttribute(Qt.WA_AttributeCount)
+ self.Me = Me(data.get_myinfo())
self.chatView = chat.ChatController(self.Me, parent=None)
self.lay = QHBoxLayout()
self.page_chat.setLayout(self.lay)
@@ -60,12 +61,6 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.statusbar.setVisible(False)
self.prompt_bar = PromptBar(self)
self.chat_view()
- # self.state_lable = QLabel(self)
- # self.state_lable.raise_()
- # pixmap = QPixmap('./app/data/icons/default_avatar.svg').scaled(32, 32) # 按指定路径找到图片
- # self.state_lable.setPixmap(pixmap)
- # self.state_lable.setText("T")
- # 创建右键菜单函数
def init_ui(self):
# self.setWindowFlags(QtCore.Qt.FramelessWindowHint)
diff --git a/app/Ui/pc_decrypt/__init__.py b/app/Ui/pc_decrypt/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/app/Ui/pc_decrypt/decryptUi.py b/app/Ui/pc_decrypt/decryptUi.py
deleted file mode 100644
index 57a8b66..0000000
--- a/app/Ui/pc_decrypt/decryptUi.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'decryptUi.ui'
-#
-# Created by: PyQt5 UI code generator 5.15.7
-#
-# 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_Dialog(object):
- def setupUi(self, Dialog):
- Dialog.setObjectName("Dialog")
- Dialog.resize(400, 300)
- font = QtGui.QFont()
- font.setFamily("微软雅黑")
- Dialog.setFont(font)
- self.label_3 = QtWidgets.QLabel(Dialog)
- self.label_3.setGeometry(QtCore.QRect(140, 0, 221, 51))
- font = QtGui.QFont()
- font.setFamily("一纸情书")
- font.setPointSize(20)
- self.label_3.setFont(font)
- self.label_3.setObjectName("label_3")
- self.progressBar = QtWidgets.QProgressBar(Dialog)
- self.progressBar.setGeometry(QtCore.QRect(90, 260, 271, 23))
- self.progressBar.setProperty("value", 50)
- self.progressBar.setObjectName("progressBar")
- self.btn_getinfo = QtWidgets.QPushButton(Dialog)
- self.btn_getinfo.setGeometry(QtCore.QRect(290, 60, 91, 41))
- self.btn_getinfo.setObjectName("btn_getinfo")
- self.btn_db_dir = QtWidgets.QPushButton(Dialog)
- self.btn_db_dir.setGeometry(QtCore.QRect(20, 210, 91, 41))
- self.btn_db_dir.setObjectName("btn_db_dir")
- self.label_db_dir = QtWidgets.QLabel(Dialog)
- self.label_db_dir.setGeometry(QtCore.QRect(120, 220, 251, 21))
- self.label_db_dir.setText("")
- self.label_db_dir.setObjectName("label_db_dir")
- self.label_ready = QtWidgets.QLabel(Dialog)
- self.label_ready.setGeometry(QtCore.QRect(40, 260, 41, 21))
- self.label_ready.setObjectName("label_ready")
- self.layoutWidget = QtWidgets.QWidget(Dialog)
- self.layoutWidget.setGeometry(QtCore.QRect(20, 50, 221, 151))
- self.layoutWidget.setObjectName("layoutWidget")
- self.gridLayout = QtWidgets.QGridLayout(self.layoutWidget)
- self.gridLayout.setContentsMargins(0, 0, 0, 0)
- self.gridLayout.setObjectName("gridLayout")
- self.label = QtWidgets.QLabel(self.layoutWidget)
- self.label.setObjectName("label")
- self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
- self.label_2 = QtWidgets.QLabel(self.layoutWidget)
- self.label_2.setObjectName("label_2")
- self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
- self.label_5 = QtWidgets.QLabel(self.layoutWidget)
- self.label_5.setObjectName("label_5")
- self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
- self.label_4 = QtWidgets.QLabel(self.layoutWidget)
- self.label_4.setObjectName("label_4")
- self.gridLayout.addWidget(self.label_4, 4, 0, 1, 1)
- self.label_key = QtWidgets.QLabel(self.layoutWidget)
- self.label_key.setText("")
- self.label_key.setObjectName("label_key")
- self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1)
- self.label_phone = QtWidgets.QLabel(self.layoutWidget)
- self.label_phone.setText("")
- self.label_phone.setObjectName("label_phone")
- self.gridLayout.addWidget(self.label_phone, 2, 1, 1, 1)
- self.label_6 = QtWidgets.QLabel(self.layoutWidget)
- self.label_6.setObjectName("label_6")
- self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
- self.label_pid = QtWidgets.QLabel(self.layoutWidget)
- self.label_pid.setText("")
- self.label_pid.setObjectName("label_pid")
- self.gridLayout.addWidget(self.label_pid, 0, 1, 1, 1)
- self.label_name = QtWidgets.QLabel(self.layoutWidget)
- self.label_name.setText("")
- self.label_name.setObjectName("label_name")
- self.gridLayout.addWidget(self.label_name, 3, 1, 1, 1)
- self.label_7 = QtWidgets.QLabel(self.layoutWidget)
- self.label_7.setObjectName("label_7")
- self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
- self.label_version = QtWidgets.QLabel(self.layoutWidget)
- self.label_version.setText("")
- self.label_version.setObjectName("label_version")
- self.gridLayout.addWidget(self.label_version, 1, 1, 1, 1)
- self.lineEdit = QtWidgets.QLineEdit(self.layoutWidget)
- self.lineEdit.setStyleSheet("background:transparent;border-width:0;border-style:outset")
- self.lineEdit.setFrame(False)
- self.lineEdit.setObjectName("lineEdit")
- self.gridLayout.addWidget(self.lineEdit, 4, 1, 1, 1)
- self.gridLayout.setColumnMinimumWidth(0, 1)
- self.gridLayout.setColumnMinimumWidth(1, 2)
- self.gridLayout.setRowMinimumHeight(5, 10)
- self.gridLayout.setColumnStretch(0, 1)
- self.gridLayout.setColumnStretch(1, 3)
- self.gridLayout.setRowStretch(5, 10)
- self.pushButton_3 = QtWidgets.QPushButton(Dialog)
- self.pushButton_3.setGeometry(QtCore.QRect(290, 130, 91, 41))
- self.pushButton_3.setObjectName("pushButton_3")
-
- self.retranslateUi(Dialog)
- QtCore.QMetaObject.connectSlotsByName(Dialog)
-
- def retranslateUi(self, Dialog):
- _translate = QtCore.QCoreApplication.translate
- Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
- self.label_3.setText(_translate("Dialog", "解密数据库"))
- self.btn_getinfo.setText(_translate("Dialog", "获取信息"))
- self.btn_db_dir.setText(_translate("Dialog", "设置微信路径"))
- self.label_ready.setText(_translate("Dialog", "未就绪"))
- self.label.setText(_translate("Dialog", "PID"))
- self.label_2.setText(_translate("Dialog", "手机号"))
- self.label_5.setText(_translate("Dialog", "微信昵称"))
- self.label_4.setText(_translate("Dialog", "wxid"))
- self.label_6.setText(_translate("Dialog", "密钥"))
- self.label_7.setText(_translate("Dialog", "版本"))
- self.pushButton_3.setText(_translate("Dialog", "开始解密"))
diff --git a/app/Ui/pc_decrypt/decryptUi.ui b/app/Ui/pc_decrypt/decryptUi.ui
deleted file mode 100644
index fe1b558..0000000
--- a/app/Ui/pc_decrypt/decryptUi.ui
+++ /dev/null
@@ -1,221 +0,0 @@
-
-
- Dialog
-
-
-
- 0
- 0
- 400
- 300
-
-
-
-
- 微软雅黑
-
-
-
- Dialog
-
-
-
-
- 140
- 0
- 221
- 51
-
-
-
-
- 一纸情书
- 20
-
-
-
- 解密数据库
-
-
-
-
-
- 90
- 260
- 271
- 23
-
-
-
- 50
-
-
-
-
-
- 290
- 60
- 91
- 41
-
-
-
- 获取信息
-
-
-
-
-
- 20
- 210
- 91
- 41
-
-
-
- 设置微信路径
-
-
-
-
-
- 120
- 220
- 251
- 21
-
-
-
-
-
-
-
-
-
- 40
- 260
- 41
- 21
-
-
-
- 未就绪
-
-
-
-
-
- 20
- 50
- 221
- 151
-
-
-
- -
-
-
- PID
-
-
-
- -
-
-
- 手机号
-
-
-
- -
-
-
- 微信昵称
-
-
-
- -
-
-
- wxid
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- 密钥
-
-
-
- -
-
-
-
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- 版本
-
-
-
- -
-
-
-
-
-
-
- -
-
-
- background:transparent;border-width:0;border-style:outset
-
-
- false
-
-
-
-
-
-
-
-
- 290
- 130
- 91
- 41
-
-
-
- 开始解密
-
-
-
-
-
-
diff --git a/app/Ui/MyComponents/Button_Contact.py b/app/components/Button_Contact.py
similarity index 100%
rename from app/Ui/MyComponents/Button_Contact.py
rename to app/components/Button_Contact.py
diff --git a/app/components/CAvatar.py b/app/components/CAvatar.py
new file mode 100644
index 0000000..4330cb5
--- /dev/null
+++ b/app/components/CAvatar.py
@@ -0,0 +1,297 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Created on 2019年7月26日
+@author: Irony
+@site: https://pyqt5.com https://github.com/892768447
+@email: 892768447@qq.com
+@file: CustomWidgets.CAvatar
+@description: 头像
+"""
+import os
+
+from PyQt5.QtCore import QUrl, QRectF, Qt, QSize, QTimer, QPropertyAnimation, \
+ QPointF, pyqtProperty
+from PyQt5.QtGui import QPixmap, QColor, QPainter, QPainterPath, QMovie
+from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkDiskCache, \
+ QNetworkRequest
+from PyQt5.QtWidgets import QWidget, qApp
+
+__Author__ = 'Irony'
+__Copyright__ = 'Copyright (c) 2019 Irony'
+__Version__ = 1.0
+
+
+class CAvatar(QWidget):
+ Circle = 0 # 圆圈
+ Rectangle = 1 # 圆角矩形
+ SizeLarge = QSize(128, 128)
+ SizeMedium = QSize(64, 64)
+ SizeSmall = QSize(32, 32)
+ StartAngle = 0 # 起始旋转角度
+ EndAngle = 360 # 结束旋转角度
+
+ def __init__(self, *args, shape=0, url='', img_bytes=None, cacheDir=False, size=QSize(64, 64), animation=False,
+ **kwargs):
+ super(CAvatar, self).__init__(*args, **kwargs)
+ self.url = ''
+ self._angle = 0 # 角度
+ self.pradius = 0 # 加载进度条半径
+ self.animation = animation # 是否使用动画
+ self._movie = None # 动态图
+ self._pixmap = QPixmap() # 图片对象
+ self.pixmap = QPixmap() # 被绘制的对象
+ self.isGif = url.endswith('.gif')
+ # 进度动画定时器
+ self.loadingTimer = QTimer(self, timeout=self.onLoading)
+ # 旋转动画
+ self.rotateAnimation = QPropertyAnimation(
+ self, b'angle', self, loopCount=1)
+ self.setShape(shape)
+ self.setCacheDir(cacheDir)
+ self.setSize(size)
+ if img_bytes:
+ self.setBytes(img_bytes)
+ else:
+ self.setUrl(url)
+
+ def paintEvent(self, event):
+ super(CAvatar, self).paintEvent(event)
+ # 画笔
+ painter = QPainter(self)
+ painter.setRenderHint(QPainter.Antialiasing, True)
+ painter.setRenderHint(QPainter.HighQualityAntialiasing, True)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
+ # 绘制
+ path = QPainterPath()
+ diameter = min(self.width(), self.height())
+ if self.shape == self.Circle:
+ radius = int(diameter / 2)
+ elif self.shape == self.Rectangle:
+ radius = 4
+ halfW = self.width() / 2
+ halfH = self.height() / 2
+ painter.translate(halfW, halfH)
+ path.addRoundedRect(
+ QRectF(-halfW, -halfH, diameter, diameter), radius, radius)
+ painter.setClipPath(path)
+ # 如果是动画效果
+ if self.rotateAnimation.state() == QPropertyAnimation.Running:
+ painter.rotate(self._angle) # 旋转
+ painter.drawPixmap(
+ QPointF(-self.pixmap.width() / 2, -self.pixmap.height() // 2), self.pixmap)
+ else:
+ painter.drawPixmap(-int(halfW), -int(halfH), self.pixmap)
+ # 如果在加载
+ if self.loadingTimer.isActive():
+ diameter = 2 * self.pradius
+ painter.setBrush(
+ QColor(45, 140, 240, int((1 - self.pradius / 10) * 255)))
+ painter.setPen(Qt.NoPen)
+ painter.drawRoundedRect(
+ QRectF(-self.pradius, -self.pradius, diameter, diameter), self.pradius, self.pradius)
+
+ def enterEvent(self, event):
+ """鼠标进入动画
+ :param event:
+ """
+ if not (self.animation and not self.isGif):
+ return
+ self.rotateAnimation.stop()
+ cv = self.rotateAnimation.currentValue() or self.StartAngle
+ self.rotateAnimation.setDuration(
+ 540 if cv == 0 else int(cv / self.EndAngle * 540))
+ self.rotateAnimation.setStartValue(cv)
+ self.rotateAnimation.setEndValue(self.EndAngle)
+ self.rotateAnimation.start()
+
+ def leaveEvent(self, event):
+ """鼠标离开动画
+ :param event:
+ """
+ if not (self.animation and not self.isGif):
+ return
+ self.rotateAnimation.stop()
+ cv = self.rotateAnimation.currentValue() or self.EndAngle
+ self.rotateAnimation.setDuration(int(cv / self.EndAngle * 540))
+ self.rotateAnimation.setStartValue(cv)
+ self.rotateAnimation.setEndValue(self.StartAngle)
+ self.rotateAnimation.start()
+
+ def onLoading(self):
+ """更新进度动画
+ """
+ if self.loadingTimer.isActive():
+ if self.pradius > 9:
+ self.pradius = 0
+ self.pradius += 1
+ else:
+ self.pradius = 0
+ self.update()
+
+ def onFinished(self):
+ """图片下载完成
+ """
+ self.loadingTimer.stop()
+ self.pradius = 0
+ reply = self.sender()
+
+ if self.isGif:
+ self._movie = QMovie(reply, b'gif', self)
+ if self._movie.isValid():
+ self._movie.frameChanged.connect(self._resizeGifPixmap)
+ self._movie.start()
+ else:
+ data = reply.readAll().data()
+ reply.deleteLater()
+ del reply
+ self._pixmap.loadFromData(data)
+ if self._pixmap.isNull():
+ self._pixmap = QPixmap(self.size())
+ self._pixmap.fill(QColor(204, 204, 204))
+ self._resizePixmap()
+
+ def onError(self, code):
+ """下载出错了
+ :param code:
+ """
+ self._pixmap = QPixmap(self.size())
+ self._pixmap.fill(QColor(204, 204, 204))
+ self._resizePixmap()
+
+ def refresh(self):
+ """强制刷新
+ """
+ self._get(self.url)
+
+ def isLoading(self):
+ """判断是否正在加载
+ """
+ return self.loadingTimer.isActive()
+
+ def setShape(self, shape):
+ """设置形状
+ :param shape: 0=圆形, 1=圆角矩形
+ """
+ self.shape = shape
+
+ def setBytes(self, img_bytes):
+ self._pixmap = QPixmap()
+ if img_bytes[:4] == b'\x89PNG':
+ self._pixmap.loadFromData(img_bytes, format='PNG')
+ else:
+ self._pixmap.loadFromData(img_bytes, format='jfif')
+ self._resizePixmap()
+
+ def setUrl(self, url):
+ """设置url,可以是本地路径,也可以是网络地址
+ :param url:
+ """
+ self.url = url
+ self._get(url)
+
+ def setCacheDir(self, cacheDir=''):
+ """设置本地缓存路径
+ :param cacheDir:
+ """
+ self.cacheDir = cacheDir
+ self._initNetWork()
+
+ def setSize(self, size):
+ """设置固定尺寸
+ :param size:
+ """
+ if not isinstance(size, QSize):
+ size = self.SizeMedium
+ self.setMinimumSize(size)
+ self.setMaximumSize(size)
+ self._resizePixmap()
+
+ @pyqtProperty(int)
+ def angle(self):
+ return self._angle
+
+ @angle.setter
+ def angle(self, value):
+ self._angle = value
+ self.update()
+
+ def _resizePixmap(self):
+ """缩放图片
+ """
+ if not self._pixmap.isNull():
+ self.pixmap = self._pixmap.scaled(
+ self.width(), self.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+ self.update()
+
+ def _resizeGifPixmap(self, _):
+ """缩放动画图片
+ """
+ if self._movie:
+ self.pixmap = self._movie.currentPixmap().scaled(
+ self.width(), self.height(), Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
+ self.update()
+
+ def _initNetWork(self):
+ """初始化异步网络库
+ """
+ if not hasattr(qApp, '_network'):
+ network = QNetworkAccessManager(self.window())
+ setattr(qApp, '_network', network)
+ # 是否需要设置缓存
+ if self.cacheDir and not qApp._network.cache():
+ cache = QNetworkDiskCache(self.window())
+ cache.setCacheDirectory(self.cacheDir)
+ qApp._network.setCache(cache)
+
+ def _get(self, url):
+ """设置图片或者请求网络图片
+ :param url:
+ """
+ if not url:
+ self.onError('')
+ return
+ if url.startswith('http') and not self.loadingTimer.isActive():
+ url = QUrl(url)
+ request = QNetworkRequest(url)
+ # request.setHeader(QNetworkRequest.UserAgentHeader, b'CAvatar')
+ # request.setRawHeader(b'Author', b'Irony')
+ request.setAttribute(
+ QNetworkRequest.FollowRedirectsAttribute, True)
+ if qApp._network.cache():
+ request.setAttribute(
+ QNetworkRequest.CacheLoadControlAttribute, QNetworkRequest.PreferNetwork)
+ request.setAttribute(
+ QNetworkRequest.CacheSaveControlAttribute, True)
+ reply = qApp._network.get(request)
+ self.pradius = 0
+ self.loadingTimer.start(50) # 显示进度动画
+ reply.finished.connect(self.onFinished)
+ reply.error.connect(self.onError)
+ return
+ self.pradius = 0
+ if os.path.exists(url) and os.path.isfile(url):
+ if self.isGif:
+ self._movie = QMovie(url, parent=self)
+ if self._movie.isValid():
+ self._movie.frameChanged.connect(self._resizeGifPixmap)
+ self._movie.start()
+ else:
+ self._pixmap = QPixmap(url)
+ self._resizePixmap()
+ else:
+ self.onError('')
+
+
+if __name__ == '__main__':
+ import sys
+ from PyQt5.QtWidgets import QApplication
+
+ app = QApplication(sys.argv)
+ w = CAvatar(
+ img_bytes=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x84\x00\x00\x00\x84\x08\x00\x00\x00\x00t/\xdc{\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f\x0b\xfca\x05\x00\x00\x00 cHRM\x00\x00z&\x00\x00\x80\x84\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00u0\x00\x00\xea`\x00\x00:\x98\x00\x00\x17p\x9c\xbaQ<\x00\x00\x00\x02bKGD\x00\xff\x87\x8f\xcc\xbf\x00\x00\x0e\x8fIDATx\xda\xed\x9b[\x8c]\xd7Y\xc7\xbf\xff\xb7\xd6\xde\xfb\x9c3w\xdf&\xbe\xdf\x9d\xa4\xbe\xc4\x89krmI\xdc\x06\xa5i\xa0$\x95\xa0\x95\x02U\x0bBH(\\\xfa\x80\x10<\xf0\xd6\x07\xfa\x80D\x1e\xa0P\xa2\x02-\xa1)%j\x13\xd2\xe6~O\x9a\xc4J\x9a\xc4\x8e\xeb\xcb8\xf8\x9eq\xec\x8c\xe7~\xf6\xdek}\x7f\x1e\xce\x8c\xc7g:\x05\xe7\xa0\x11\x8d\xaf\x85m\xa1o\xcf\xdb\xc7d(]\xb8\xe2\xc6\x1d\xd7\xd4\x9cYR\x0e~{/v\xdce\x89c\xa4\x93(\x8a\xb9YG\xe9\x87zox\xf9\xe4\xc3\xab\xd6\xa4!\xb1\x8b\xdb\x06H\x81\x18\xc2\x8e\xab\xde\xdd\xd7\x7f\xf4x\xfb\x95_Y0\xbf\xcd\x8fV\x10\x92\xfa\xab\xafiu\xad\xef\xff\xa8\xad\xc3k.N\x8c\xde\xe6\x02\x91X\xfb\xc8\x96\xbb\xef\xef{q%9W\xa7I8qq\xe9\xaa\xba\x7f\xe4\xaf\xd0}CI\xa9w\x94\x92\xe4|M\x92\xc1\x1f\xffp\xa0\\\xbaz\xed-\xcb\x02\xc5I\x9c\x93\x89F\xc9\xdb\xf4\xa6u#oEK"/\xae\x97\x13a\x8aP\xab{\x89\x9ai\x10I\xd2h\xa4\xd7M\xcep\xf0\xe0P\xb1\xf7\x89\x87\xfb\xcdT\xa35\xbd\xf7\x02Q\xb42Z\xe9\xbd\xb3\\e\x19\xa3\x83\\,\x88Lz\xd5\xec\xfa\xe6\xa9E\x7f\xb2\xcd\x19\xd3\xa0\xa5y\xdb\xf7\xd0\x01\xae\xfe\xf2\x1as\x05\xc5ISVq\x81\xeb(\x13\x83\x99\xa51\n`\x93\x12\xa4\xc1\x899\x03g\xf4\x1c\xc1\r?\xb87\xb5\xfdeU\x8f\xc9\xday\xae,3Ww\xce\x87\x03\x7f\xb3/n\xf9\xdaV\x9a+t:\xfd\xec\x1e\x93,\xd5\x99(c\xc6\xd2CCC\x14\xce\xd1D\x83\xe8\xcc1-\xa9\xa3~hl,\xf3y\\w\xe7\x1d\xedCY-\x94\xd5\x10\x8b\x9d\x0f\x1c\xf5\xb7\xfd\xc1<\x12\x92\x80\xd6\x1c\x04fuV\x02OD\x07\x19T\x11\x188q\x1dQ\xd5\x04\xe6\xc1\x99<\x18$\xb4]\xb5\xf2\xe4@\x84\xdf\xf6\xa5\xb5C\xbe\xeaL\xbc!>\xfb\xd0\x01\xd7yko\xae\xa9\x16\x89L\x0b\xa2\x82\xd9\xc6\xd4 \xb32d\x05\x1f\x18\xea\xd9\xbe\x99>LlCd\x1c\x1e\xec\xad\x00\xcef\xda\x86 \xbe\xef\x9f_\tze\xd7\xe9p\xd3\xe7{\x05\x9a\x87\xe7\xff\xb1\xdf\xa5\xd6\xd3\xd9\xbd\xf6\xc6++\x88\xe2%\x9ck\xa3~\xfa\x9b\x01S\x98\x90*c\x99\xaf\x8f<\xf2\x83\xb0\xf9\x97\x95\xa1\xf1\xc9A\xd3\xf2\xfb\x8f\xf7\xddq\xcf\xd5\xa9\x988kd\xb2\x84PI\x15C\x89dt\xc9\x8a\x97\x93\xb0/P\x0f\xad\xee\x8db\x95#\x8f\x7f8?J\xc6\xc1\x93\xaf\xfd\xe4\x86\xdf\\\xee\x94\xe6&\xee\x92\xa2\xc2&\x08\x98\x12A%B`\xa20=\xfe\xd8\x13\xf3v|nY\x14g"0g\xaaq\xffw\xeb\xdd/\x9e\xba\xf7\xc6\x89\x84\xeb\x9c\xfc\x9eP\xf3&\xd9\xce\x9d\x06\xcb\x13\xcf\rW\x88de\xb9\xe8W7V\xbb*\x95\x04\xa3\xfb\x1e~\xe6\xc3\xfb\xd6\xda\x94m@8M\x12\x06(\x9dF:\x13GI\xeb\xbb\xbfs\xb0\xe7\xf77v$\xb9\x06O:3\x04\x05\xaf\x1b\xda\'{~\xb2\xb9\xcdEN\xe6j\x80\x882B\x11\xad|\xf1\xfb\x07T\xb6v\xbdw|\xc1\xe7\xd6\xa9\x14\xd0\xb6\xed\xb7\xb8\xb2f\x92\xc4m\xaf\x1fy\xe7G_\xeb\xd0sL\x94\xc04\xeb(\xb2\xc2\x9bc\x84)\xf3\xddO\xbdT\xbd\xe3\x8b\xf3\x13\xe6ItA\x11|\x84\x037\xfd\xe5\xb1\xbf\xdec;\x87k\xc0\x84;\x07i>\x06u\x8cq\xfc\xcd\x9f\xf6\xf5\xa7\xba\xed\x8f\x16\xbe;\xb8hSn\xc1%%{\xf2\x98Y\t\x14X\xb4\xdf?\xbf\xf6\xf38\x1b:(Nl\x1aD\x02\xc4$OH\x8d\xbb\xdex\xb2\xbe\xfc\xcb\xd7u0\xba`e\xadT\x8d\x12\x1c(l\x93")RD\xe7\x08C#h0\x00.\x8e\x1f\xdb\xb3\xfb\xb5\x81\xaa\xc0_\xb7H\xb6\x92\xa52\x95 RG\xc5\xe2/\x0e/\x08\xbbv\xb3}\xe0\xe0x\xdb\x94q@8]\'\xdc@\xa5V*a\x07~\xfc\xf6\xc9\xee;\xefXn\xfd\x03\xebU\xd4["\xa7\xdf]\xb1\xba\xf0j \xa3\xf94u\r\xff\xa5\xc2(p&c\xc7\xf7\xee~kt\xacm\xd5\xe9T\xe5\x13\t\xcaX\x89"\xb1D\xd5\x0c6\xbe\xf3?\xde[06d\xc9G\xeboH\xa9\x93NFAk\x86\x10\x8d\xffj7\xadh\x1b\xf9\xf93?\x8f=\xb7\xff\xc6Ro\xc3\x0f\x8c\xfd\xf1\xbc$\xf8\xdc\xeb\xfb\xf7/\xf8\xf35#\x15\x8dZ\x96\x11\xddU\x11\xa3\x8a\xd0\x9c\xe3\x9b\x8f~\x84\xb1#\xa3\x95\x9eUk~e\xe9\xb7\x9e)\xbb\xf2\x07O\xec\xd8\x94\x8b\xa3\xa4\x16\x89\xfc\xf0s\x8f\xf6\xf7\x1c\xae@\xfc\xf2?\xbdR4\xb8Is\x8c\xb1\xa2\xbe\x99!\xa9<\xf7\xea\xfa\xcec\xfbG\xe7\x7f\xea\xfa\rm\x12\xb0\xf7\xf5\rB\xaay\x97\x9f\x1a\xdf\xf7\xd3{ky\xa6(\x8dZ\xf3\x93^\xdbEV\x96\xac\x7f\xeeH\xd1\xb1l\xd5\x96\r+\xab\'NE;\xfd\xf7\x1f\x9c9\xf1\xd5\xf5\xde\xa2\x9a\xa5CG_}\xf9h\xdeY\xaaI\xb2\xe0\xb7\xd6\x01\xe6\xa7,\xd4Yl\x82\xa0\xc8\x17\xd7?\xfdz\xde\xbe\xfc\xfam\xcbj\x96\xd4\xb5xe\xb8\xb7\xad\xf4\x11\xde\x92\x81\x9co~j\xa3D53a\xd5M\xe6:!\xb1\xf1\xc5wo=:x\xc5\xf2\x9ev\'\xe1\xe8\xb1\xb2"\xfb\x1c_\xdd\xb4\xc2\xb2$\x1f;\xbd\xf7\xcd\x83\x83\x03d\x99*C\xf5\x9e\x9b\xb3hv\xd6\xdbR46[Gte\xe7\xcd\xdb\x8f\xd4\x17t\xa5!#\xea\xa9\xbe\xf1\xfa\xf85\xa9DoQR\xa5\x9d8q\x95\x830\x880=\x1b:\xd2zE\xcbl\xf3U\x96\xb0t$\xf6\x9d\xaa\x85\xf2K\xd9\x8bG\x9f\xba9\x7f\xf6\xf8\xf0\xc0\xe9\xd14\xe9\xea9\xed0\xae\xa2\x8b\xcbg\xb6\xcdO\xf4\xac\xc7WFi\x86pTZ\xba\xca\x993\x15\x81\x8f\xe5sG\xe6_i\nz\xcb\x13\x85\x1b?Z8\x15\x94\x14\xd5\xb3\xc9VH\x83x+UIOp\xf4\xa0\x85$Y\x97\xbe\x15O\xed\xb9\xde\x8e\x9c\t\xc9\xc6\r\x1b\x92G\xcf\x98T"]\xdf!\xfd\xb3OM\xa5\xeeT-\x1d\xa79+1\xd0\x91E\xca\xe8$w\xfb_\xaal\xe9\xf4\x88\xce\xe8\x83P\xfdXLJg\x12\'*\xfe\t\xe5\x92F\xb6AB\xa8g\x8e\x88+\xf0\x0fE\x9e\r\xbf\xf3\x85\xaf|\xf6\x0c\xb1\xaa-\xff\xbb}\xc1G\x13\xb8z\xfb\x86\x95\x0eg}\x15\x08\xfa\xb2\xd9D)\x80D\xaf\xb4\xac \xb2\x0f\xbf;\xda~}\x879\xb1\xa4\x9eh`6:f\xd1G\x1d\x8f\x8ej\xaa\xb0\x08B\x04l\x04v\x08\xcc\x1f\xeaW\xbba\xf1cQ\x87\xab\x03\x83m\xeb\xbc9;\xfa\xed\xc7\xb3\xb4\x84\xa8\x8dm\xfc\xf5\xdbj\xa5\x9emR\x10\x94\xc25C4B\x98\xc9\xe8\x07m\xdd\x15\x94/\xec\xd1y\xeb4\x1a\x90\'\xc6U=\x1fU\xbb3\x8a\xa0\x1e`TD\xc5\xf4n\x1a\x95\x87sm\xbf\xed\x97^\x18f\xc5\xf5\x8d\xb5k\x94\x88\xa3\xbbk\x89Q\x1ct\xdeo\x7f2\r\x06L\xe6\x86\xa0x\x86\xe9\xd7A\x80\xe2\xfb\x1e\x1a\xbcv\xf5R{\xb6\xbf\xb2\xf9\n\x1a\x9dDO\xbb\xfa\xb3Ovm\xf0\x14\xf3\xa5\x08\x89F\xf0l\x86\x80\xf0x\xb0x\xfa\xbdz\x18\xaf\xb8b\xb8\xc7\xa7\xac\xcb\x8a\xd5\xa7G\xa5=\x8f\xea\xee\xd8R\xb3\xe8t*\xab1&h\x0e`P\t\xa2\xd4|+\x9f\xfc\x9et\x97\xb1\xd6\xb3\xbd\xdd\x92\x02\x96F\xb0\xfb\xabwe\x0b\x05T\xb3\x02"4\x17\xb5\x11\xc6\xcf5\xf2\xe2X\xe9\x86\xff%=\xb3\xa4\xf3\xc4\x90\r\xfbXH\xac\xcd\xbf\xefo\x0f\xb7\xa7\xfd\xfda\xf1u]#i\xd4\xa9\x9e\n\x9c\x19\x9b!h\x02E)p\xd7,]\xfd\xfc\x81\xc1\xb6|\xde\x92\xb4\xee\xd0hD\x96\xd9R\x88\x98\xa8\xa5\xa6g\x1bR\xcd\x08\x8a\xb1\x0f\xa1\xba\xa0\xb6\xe4\xa6\xe5\xdf\xa8&\x87\xb7\xa83\x89\x95\x15_?\xe1:\xef\x1f@\xe7\x92\x90\xd1\x89\x9e\xcdX\xa3\xea\xf4\xa4F#\x1d\x98E\x1d\x98\xdfqO\xf1^w\xbd}\xe0\x85\xb6\xe5\x05(\xd1\x19\xd4\x94B\x88Z{57j\x9c^\x99QDlt$)z>s\xab.x\xdf\xb4\xb4\xe8\x02\x13\x11\xce\x9f\xe7\x07+ERs \xc4\xa6r\x1aG\x89:\xbd\x02\xf3e\xb0\xc2b\xcfx\xb5\xef\r7\x86\xd1\xfe\x07\xbf\xf1\xbe\x0b\n\x13\x15\xa2\x91>\x98u\xb8\xe8\xb3\xe8\xcb\xc4\xceM4)B\xd1\xfa`\x90+n\\\xb5\xb8+\xb2\x88=\tM\x0c\x02c=D/\xb5\x84"T\xea\xd4\x1e\xea\xb42\xd0`\x89w\x11(\xed\xd8\xbf\xedcm\xdb\'+\xd8\xf5\xc8GY\x107\x95Z#$\x1d\xed\xc2\xae\x00\x15\x9e\xdbjhTi\x83\x92b\xc9Rbx Ij\x8b\x11+\tb\x8c\xaa\xcey\'\xa9\nM\xcek>5{\xcc\x18\x92Rq\xc6\xe7\x87\x1e\xf9\xd9p\xc7=_\x18\xfb\xa7\x97\xf4\x95\x9b\x17E\x7fN\xa9C\xfaR\xa4gm\x12M8\xbd\xa0\xd3\x98\xa4!\xac\xaf\xf9\x0f\x1ey\xb3\xae+\x17\x05\xa3@E\xa20\x86R3\x9d\xb1\xc5\xd0\x04\x11\x90&c\x08?xmtt\xa0\xed\xda_\xdb\xa1\xbd__\xfb\x9f~$:\xb3svk\xfc`\x90\xb5e\x1a\xd0\xe8\x07\x9c#\n\x9a.\xdf\xfaN\xba\xd9\x15\xed\xe9\t\xad\xdd\xd8\x053\nI\x07U\xe7\xac\xa63\xb6g\xa7\xc5\x8e\xa8\x80\xdbpr_\xbef\xfbg\xd6%\xa5u\xde\xb9|dC\xb3\xde\xc0\x99H\x1c\xab\xd7\xbd64\xe5\xdc\x9fA\xba\x7f\xe7)Y\xa9\xec\xba\xbdvp\xc5\xed,\x12\x90\x02%\x85P\xa9\x81\xa4^\x18\x820&!\xbde\xfb{\x83\xcbV\'&i\xa9\xdd\xb7\x8e\xd7\xc6*\xe7\x1a"`\xf3\xdaF\\ThP\xb5f\x131\xc8\x86\xd5E\xad^\xb1ewGICb\x10\x80\x86\x86<2\x87\x8bJB\x90D\xd1\x02\xb5\x1b-\x82N"\x83\xe6\xde &Sb\xa4\xc8\xc2\x8a3\xe7\x83sFLS\n\xd1\\\xdaC\x1aM\xbd\x17q\x9cL\xc6)\x0e!\xebp\x86\x19*\xd8&\xd9 \xc2\xb1\xac$,\xa2(,\x8a\x8395\x1f\xa1r\xb6cD\x8d\xce\x99\xf7\x9c\xa9\xbf\xa2\xa4\xf3\xa2l\xb4\xa2qN^\xaf\x14\xf8\nDf\xa8\xdb\x9aN"\x023\x1f\xc4\xab\xc5(NH\x07\xa7Z\x89q\xaax\x84D\xc4H\xf5\xb34\x91\xe8@B\xed\xbc\x89\x82\x95\xb1\xda6s\xff\xad\tB#,W\xd2\xe8\xb3FAS7Z\x18\xcf\xdc\x94\xd4\xa9\x96\x8e\x97\xd0Tg\xaa\xca\'Z(!:\rM2\x16\x0b\xb9\xfav\xc1E\'?11QS\x80f*F \x05\xa9\x12\x85S\x9b\xe9C=h-\x99\xf13\tH\x8a\xa9\xd0\xa6\xc9\x98\xc5\x88\xf75@f\x18\xba4\xeb\x84\x89\x98\x906YcR\x8ch\x8c\xd1l\nbl\xbcd\xcd\xdb\xcc=7*\xe8Hh\xd3\xd0\x8d\xe0\xf8\x10;\xdb`3\xb5\xa7\xcf;\xa9Y^\x13i\\C\xbf\xd98"\xe8\xa1\x11\xeb\xae\xaa5\xa6~\xcd\x97B\xd8\xe4\xeco\xf2\x84\xc6\x800=U/{:\xa8q.\x10\x17X\xa0\x13\x03J\xd9\x1b\xa4\xbb\x1a\xe6\xb6\x15\x12@\xb8\xc8\x8f\xa2-H8\xd3\xf4\xe9\xd2\x86\xb3&\xa6\x81\xc9\x81\x83.[\x98\xccq.Eq"f\x8eG\x86\xb18\x99\xb9\x1dz\t\x10\x84\xa8+S\x8c<\xdeWV\xd7\xcc\xa5\xdf\xdd\xd8\xe6\n\xa81\xef\xa3\xaeP\\\xea\\\xf4\xbc\x15]\x8c\x9e\x1f>\xfd\xac\xc3\xda\x95\xca\x8b\xb5\xfd\x1b\x0b\x16T\x04\xf8\xb0/\xce[,\x98\xd1\xbb\\\x02\x044z\x1c\x7fl\xd7\xf1q\xb8\x1bz\\\xa0\xcdI\x8c\xae\xac\x14\x86\xf8\xce \x16\xcf\x9b6\xc0\xbe,Id\xa3i\xa5\xd6\xd1\xdb\xbb\xf6\x13;\x1cA77\xad\xa8\x8cV\xeb\xd9\xae\x87$\xb9\xba\xcbh\xfa?\xbc\x0e\x1d\xcf\xd8s\xef@\xe1\xabH\x181G\xcd4&\xca\xa7~tX\x16m\xb3Y&i\x97\x00AT\xc7\x1dc\x97\xc1\x11\x91P\x9b\xd36U\x8e\xa7?;\xbc\xa0\xbeu\xa3\xcd\xa2\xca\x98\xfb\xbf\xdb\xc0\x8a\xaa\x04U"\x84D\x04s\x1b\x1e\x13E\xd5dh$\xef\xaeVl\x96=\x97\x00A\x8d\xde\x08\x81\x01\x86\x99\x1d\xf0\x0c\x82\x08\xe2\x83\x13\xf3\xf5t6A\\\x8a\x9f@\xf4\x91\r\xf7M\x05q\xf1I\xa9\x88H\xd4$x#\xa2\x9f}\xb4z\t\x92\xf8\xdf[\xff\xe7\xfe\xa6\xa6\x05\xd1\x82hA\xb4 Z\x10\x1f\xfbjA\xb4 Z\x10-\x88\x16D\x0b\xa2\x05\xd1\x82\xf8\x7f\x01\xf1\xdf\x91\xd3\xe9`7\x9a\x1c\x88\x00\x00\x00\x12tEXtexif:ExifOffset\x00620\x1a\xa3x\x00\x00\x00\x12tEXtexif:ImageLength\x000\xc1\xc5N\xce\x00\x00\x00\x11tEXtexif:ImageWidth\x000/\xffv\xa0\x00\x00\x00\x12tEXtexif:LightSource\x000x\x05kH\x00\x00\x00\x00IEND\xaeB`\x82'
+ ,
+ url='https://wx.qlogo.cn/mmhead/ver_1/DpDqmvTDORNWfLrMj26YicorEUREffl1G8FapawdKgINVH9g1icudfWesGrH9LqeGAz16z4PmkW9U1KAIM3btWgozZ1GaLF66bdKdxlMdazmibn2hpFeiaa4613dN6HM4Vfk/132')
+ w.show()
+ sys.exit(app.exec_())
diff --git a/app/components/__init__.py b/app/components/__init__.py
new file mode 100644
index 0000000..00aed52
--- /dev/null
+++ b/app/components/__init__.py
@@ -0,0 +1 @@
+from .contact_info_ui import ContactQListWidgetItem
diff --git a/app/components/bubble_message.py b/app/components/bubble_message.py
new file mode 100644
index 0000000..fe3b46b
--- /dev/null
+++ b/app/components/bubble_message.py
@@ -0,0 +1,87 @@
+from Lib import QtNinePatch2
+
+from PyQt5.QtCore import Qt, QRectF
+from PyQt5.QtGui import QImage, QPainter, QColor, QFont, QPixmap
+from PyQt5.QtWidgets import QLabel, QWidget, QHBoxLayout, QVBoxLayout, QSizePolicy
+
+
+class Label(QLabel):
+
+ def __init__(self, *args, **kwargs):
+ super(Label, self).__init__()
+ # .9 格式的图片
+ filp = kwargs.get('filp')
+ self.image = QImage('Data/skin_aio_friend_bubble_pressed.9.png')
+ if filp:
+ self.image = self.image.mirrored(True, False)
+ self.txt = kwargs.get('text')
+ self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
+ self.adjustSize()
+
+ def showEvent(self, event):
+ super(Label, self).showEvent(event)
+ pixmap = QtNinePatch2.createPixmapFromNinePatchImage(
+ self.image, self.width(), self.height())
+ self.setPixmap(pixmap)
+
+ #
+ def paintEvent(self, event) -> None:
+ super(Label, self).paintEvent(event)
+ painter = QPainter(self)
+ painter.begin(self)
+ painter.setPen(QColor(150, 100, 23))
+ painter.setFont(QFont('SimSun', 20))
+ painter.setRenderHint(QPainter.Antialiasing)
+ painter.setRenderHint(QPainter.SmoothPixmapTransform)
+ rec = QRectF(30, 40, self.width() - 60, self.height() - 60)
+ painter.drawText(rec, Qt.TextWordWrap, self.txt)
+ painter.end()
+
+ #
+ def resizeEvent(self, event):
+ super(Label, self).resizeEvent(event)
+ pixmap = QtNinePatch2.createPixmapFromNinePatchImage(
+ self.image, self.width(), self.height())
+ self.setPixmap(pixmap)
+
+
+class BubbleMessage(QWidget):
+ def __init__(self, text, avatar, isSend=False, parent=None):
+ super().__init__(parent)
+ self.isSend = isSend
+
+ self.txt = text
+ layout = QHBoxLayout()
+ self.avatar = QLabel()
+ self.avatar.setPixmap(avatar)
+ self.message = Label(text=text, filp=isSend)
+ if isSend:
+ layout.addWidget(self.message)
+ layout.addWidget(self.avatar, 0, Qt.AlignTop)
+ layout.setStretch(0, 1)
+ else:
+ layout.addWidget(self.avatar, 0, Qt.AlignTop)
+ layout.addWidget(self.message)
+ layout.setStretch(1, 1)
+ self.setLayout(layout)
+
+ def resizeEvent(self, a0) -> None:
+ w = (self.message.width() - 60) // 27
+ row = int(len(self.txt) // w) + 1
+ print('row', row)
+ self.message.setMaximumHeight(row * 31 + 80)
+ return
+
+
+class MainWindow(QWidget):
+ def __init__(self):
+ super().__init__()
+ txt = '''在工具中单击边缘可以添加黑点,单击可以删掉黑点,拖动可以调整黑点长度。勾选等选项可以查看内容、缩放等区域右侧可预览不同拉伸情况下的效果,拖动可以调整预览的拉伸比例'''
+ avatar = QPixmap('Data/head.jpg').scaled(60, 60)
+ bubble_mesage = BubbleMessage(txt, avatar, isSend=False)
+ layout = QVBoxLayout()
+ bubble_mesage1 = BubbleMessage(txt, avatar, isSend=True)
+ layout.addWidget(bubble_mesage)
+ layout.addWidget(bubble_mesage1)
+ # layout.setStretch(0, 1)
+ self.setLayout(layout)
diff --git a/app/components/contact_info_ui.py b/app/components/contact_info_ui.py
new file mode 100644
index 0000000..681ed2a
--- /dev/null
+++ b/app/components/contact_info_ui.py
@@ -0,0 +1,57 @@
+import sys
+
+from PyQt5.Qt import *
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+from .CAvatar import CAvatar
+
+
+# 自定义的item 继承自QListWidgetItem
+class ContactQListWidgetItem(QListWidgetItem):
+ def __init__(self, name, url, img_bytes=None):
+ super().__init__()
+ # 自定义item中的widget 用来显示自定义的内容
+ self.widget = QWidget()
+ # 用来显示name
+ self.nameLabel = QLabel()
+ self.nameLabel.setText(name)
+ # 用来显示avator(图像)
+ self.avatorLabel = CAvatar(None, shape=CAvatar.Rectangle, size=QSize(60, 60),
+ url=url, img_bytes=img_bytes)
+ # 设置布局用来对nameLabel和avatorLabel进行布局
+ self.hbox = QHBoxLayout()
+ self.hbox.addWidget(self.avatorLabel)
+ self.hbox.addWidget(self.nameLabel)
+ self.hbox.addStretch(1)
+ # 设置widget的布局
+ self.widget.setLayout(self.hbox)
+ # 设置自定义的QListWidgetItem的sizeHint,不然无法显示
+ self.setSizeHint(self.widget.sizeHint())
+
+
+if __name__ == "__main__":
+ app = QApplication(sys.argv)
+
+ # 主窗口
+ w = QWidget()
+ w.setWindowTitle("QListWindow")
+ # 新建QListWidget
+ listWidget = QListWidget(w)
+ listWidget.resize(300, 300)
+
+ # 新建两个自定义的QListWidgetItem(customQListWidgetItem)
+ item1 = ContactQListWidgetItem("鲤鱼王", "liyuwang.jpg")
+ item2 = ContactQListWidgetItem("可达鸭", "kedaya.jpg")
+
+ # 在listWidget中加入两个自定义的item
+ listWidget.addItem(item1)
+ listWidget.setItemWidget(item1, item1.widget)
+ listWidget.addItem(item2)
+ listWidget.setItemWidget(item2, item2.widget)
+
+ # 绑定点击槽函数 点击显示对应item中的name
+ listWidget.itemClicked.connect(lambda item: print(item.nameLabel.text()))
+
+ w.show()
+ sys.exit(app.exec_())
diff --git a/app/Ui/MyComponents/prompt_bar.py b/app/components/prompt_bar.py
similarity index 100%
rename from app/Ui/MyComponents/prompt_bar.py
rename to app/components/prompt_bar.py
diff --git a/app/data/icons/search.svg b/app/data/icons/search.svg
new file mode 100644
index 0000000..076b141
--- /dev/null
+++ b/app/data/icons/search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/app/person.py b/app/person.py
index 68f8e5c..09f22a4 100644
--- a/app/person.py
+++ b/app/person.py
@@ -1,8 +1,11 @@
import os.path
+from typing import Dict
+from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from app.DataBase import data
+from app.Ui.Icon import Icon
# from app.Ui.Icon import Icon
@@ -32,6 +35,30 @@ class Me(Person):
class Contact(Person):
def __init__(self, wxid: str):
super(Contact, self).__init__(wxid)
+ self.smallHeadImgUrl = ''
+ self.bigHeadImgUrl = ''
+
+
+class ContactPC:
+ def __init__(self, contact_info: Dict):
+ self.wxid = contact_info.get('UserName')
+ self.remark = contact_info.get('Remark')
+ # Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
+ self.alias = contact_info.get('Alias')
+ self.nickName = contact_info.get('NickName')
+ self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
+ self.smallHeadImgBLOG = b''
+ self.avatar = QPixmap()
+
+ def set_avatar(self, img_bytes):
+ if not img_bytes:
+ self.avatar.load(Icon.Default_avatar_path)
+ return
+ if img_bytes[:4] == b'\x89PNG':
+ self.avatar.loadFromData(img_bytes, format='PNG')
+ else:
+ self.avatar.loadFromData(img_bytes, format='jfif')
+ self.avatar.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
class Group(Person):
diff --git a/app/ui_pc/Icon.py b/app/ui_pc/Icon.py
new file mode 100644
index 0000000..88a0f0e
--- /dev/null
+++ b/app/ui_pc/Icon.py
@@ -0,0 +1,19 @@
+from PyQt5.QtGui import QIcon
+
+
+class Icon:
+ Default_avatar_path = './app/data/icons/default_avatar.svg'
+ MainWindow_Icon = QIcon('./app/data/icons/logo.svg')
+ Default_avatar = QIcon(Default_avatar_path)
+ Output = QIcon('./app/data/icons/output.svg')
+ Back = QIcon('./app/data/icons/back.svg')
+ ToDocx = QIcon('app/data/icons/word.svg')
+ ToCSV = QIcon('app/data/icons/csv.svg')
+ ToHTML = QIcon('app/data/icons/html.svg')
+ Chat_Icon = QIcon('./app/data/icons/chat.svg')
+ Contact_Icon = QIcon('./app/data/icons/contact.svg')
+ MyInfo_Icon = QIcon('./app/data/icons/myinfo.svg')
+ Annual_Report_Icon = QIcon('./app/data/icons/annual_report.svg')
+ Analysis_Icon = QIcon('./app/data/icons/analysis.svg')
+ Emotion_Icon = QIcon('./app/data/icons/emotion.svg')
+ Search_Icon = QIcon('./app/data/icons/search.svg')
diff --git a/app/ui_pc/__init__.py b/app/ui_pc/__init__.py
new file mode 100644
index 0000000..5cbf985
--- /dev/null
+++ b/app/ui_pc/__init__.py
@@ -0,0 +1,3 @@
+from . import mainview
+
+__all__ = ['mainview']
diff --git a/app/ui_pc/contact/__init__.py b/app/ui_pc/contact/__init__.py
new file mode 100644
index 0000000..a83ae0a
--- /dev/null
+++ b/app/ui_pc/contact/__init__.py
@@ -0,0 +1 @@
+from .contact_window import ContactWindow
diff --git a/app/ui_pc/contact/contactInfo.py b/app/ui_pc/contact/contactInfo.py
new file mode 100644
index 0000000..107977c
--- /dev/null
+++ b/app/ui_pc/contact/contactInfo.py
@@ -0,0 +1,143 @@
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+from app import person
+from app.DataBase.output_pc import Output
+from app.Ui.Icon import Icon
+from .contactInfoUi import Ui_Form
+from .userinfo import userinfo
+
+
+class ContactInfo(QWidget, Ui_Form):
+ exitSignal = pyqtSignal()
+ urlSignal = pyqtSignal(QUrl)
+
+ # username = ''
+ def __init__(self, contact, me: person.Me = None, parent=None):
+ super(ContactInfo, self).__init__(parent)
+ self.setupUi(self)
+ self.contact = contact
+ self.view_userinfo = userinfo.UserinfoController(self.contact)
+
+ # self.btn_analysis.clicked.connect(self.analysis)
+ # self.btn_emotion.clicked.connect(self.emotionale_Analysis)
+ # self.btn_report.clicked.connect(self.annual_report)
+ self.btn_back.clicked.connect(self.back)
+ self.Me = me
+ # self.
+ self.init_ui()
+
+ def init_ui(self):
+ self.btn_back.setIcon(Icon.Back)
+ self.btn_report.setIcon(Icon.Annual_Report_Icon)
+ self.btn_analysis.setIcon(Icon.Analysis_Icon)
+ self.btn_emotion.setIcon(Icon.Emotion_Icon)
+ self.label_remark.setText(self.contact.remark)
+ self.stackedWidget.addWidget(self.view_userinfo)
+ self.stackedWidget.setCurrentWidget(self.view_userinfo)
+ menu = QMenu(self)
+ self.toDocxAct = QAction(Icon.ToDocx, '导出Docx', self)
+ self.toCSVAct = QAction(Icon.ToCSV, '导出CSV', self)
+ self.toHtmlAct = QAction(Icon.ToHTML, '导出HTML', self)
+ self.toolButton_output.setPopupMode(QToolButton.MenuButtonPopup)
+ self.toolButton_output.clicked.connect(self.toolButton_show)
+ menu.addAction(self.toDocxAct)
+ menu.addAction(self.toCSVAct)
+ menu.addAction(self.toHtmlAct)
+ self.toolButton_output.setMenu(menu)
+ self.toolButton_output.setIcon(Icon.Output)
+ # self.toolButton_output.addSeparator()
+ self.toHtmlAct.triggered.connect(self.output)
+ self.toDocxAct.triggered.connect(self.output)
+ self.toCSVAct.triggered.connect(self.output)
+
+ def toolButton_show(self):
+ self.toolButton_output.showMenu()
+
+ def analysis(self):
+ self.stackedWidget.setCurrentWidget(self.view_analysis)
+ if 'room' in self.contact.wxid:
+ QMessageBox.warning(
+ self, '警告',
+ '暂不支持群组'
+ )
+ return
+ self.view_analysis.start()
+
+ def annual_report(self):
+ QMessageBox.warning(
+ self,
+ "提示",
+ "敬请期待"
+ )
+ return
+ # self.report = report.ReportController(self.contact)
+ # self.report.show()
+
+ def emotionale_Analysis(self):
+ self.stackedWidget.setCurrentWidget(self.view_emotion)
+ if 'room' in self.contact.wxid:
+ QMessageBox.warning(
+ self, '警告',
+ '暂不支持群组'
+ )
+ return
+ self.view_emotion.start()
+
+ def back(self):
+ """
+ 将userinfo界面设置为可见,其他界面设置为不可见
+ """
+ self.stackedWidget.setCurrentWidget(self.view_userinfo)
+
+ def output(self):
+ """
+ 导出聊天记录
+ :return:
+ """
+ self.stackedWidget.setCurrentWidget(self.view_userinfo)
+ if self.sender() == self.toDocxAct:
+ print('功能暂未实现')
+ QMessageBox.warning(self,
+ "别急别急",
+ "马上就实现该功能"
+ )
+ return
+ self.outputThread = Output(self.Me, self.contact.wxid)
+ elif self.sender() == self.toCSVAct:
+ # QMessageBox.warning(self,
+ # "别急别急",
+ # "马上就实现该功能"
+ # )
+ # print('开始导出csv')
+ # return
+ self.outputThread = Output(self.contact, type_=Output.CSV)
+ print('导出csv')
+ elif self.sender() == self.toHtmlAct:
+ print('功能暂未实现')
+ QMessageBox.warning(self,
+ "别急别急",
+ "马上就实现该功能"
+ )
+ return
+ self.outputThread.progressSignal.connect(self.output_progress)
+ self.outputThread.rangeSignal.connect(self.set_progressBar_range)
+ self.outputThread.okSignal.connect(self.hide_progress_bar)
+ self.outputThread.start()
+
+ def hide_progress_bar(self, int):
+ reply = QMessageBox(self)
+ reply.setIcon(QMessageBox.Information)
+ reply.setWindowTitle('OK')
+ reply.setText(f"导出聊天记录成功\n在.\\data\\目录下")
+ reply.addButton("确认", QMessageBox.AcceptRole)
+ reply.addButton("取消", QMessageBox.RejectRole)
+ api = reply.exec_()
+ self.view_userinfo.progressBar.setVisible(False)
+
+ def output_progress(self, value):
+ self.view_userinfo.progressBar.setProperty('value', value)
+
+ def set_progressBar_range(self, value):
+ self.view_userinfo.progressBar.setVisible(True)
+ self.view_userinfo.progressBar.setRange(0, value)
diff --git a/app/ui_pc/contact/contactInfoUi.py b/app/ui_pc/contact/contactInfoUi.py
new file mode 100644
index 0000000..aa973a1
--- /dev/null
+++ b/app/ui_pc/contact/contactInfoUi.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'contactInfoUi.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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(817, 748)
+ self.horizontalLayout = QtWidgets.QHBoxLayout(Form)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.frame = QtWidgets.QFrame(Form)
+ self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
+ self.frame.setObjectName("frame")
+ self.verticalLayout = QtWidgets.QVBoxLayout(self.frame)
+ self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+ self.verticalLayout.setSpacing(0)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setSpacing(0)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.label_remark = QtWidgets.QLabel(self.frame)
+ self.label_remark.setMaximumSize(QtCore.QSize(16777215, 100))
+ font = QtGui.QFont()
+ font.setPointSize(12)
+ self.label_remark.setFont(font)
+ self.label_remark.setText("")
+ self.label_remark.setObjectName("label_remark")
+ self.horizontalLayout_3.addWidget(self.label_remark)
+ self.btn_analysis = QtWidgets.QPushButton(self.frame)
+ self.btn_analysis.setStyleSheet("")
+ self.btn_analysis.setFlat(True)
+ self.btn_analysis.setObjectName("btn_analysis")
+ self.horizontalLayout_3.addWidget(self.btn_analysis)
+ self.btn_emotion = QtWidgets.QPushButton(self.frame)
+ self.btn_emotion.setFlat(True)
+ self.btn_emotion.setObjectName("btn_emotion")
+ self.horizontalLayout_3.addWidget(self.btn_emotion)
+ self.btn_report = QtWidgets.QPushButton(self.frame)
+ self.btn_report.setFlat(True)
+ self.btn_report.setObjectName("btn_report")
+ self.horizontalLayout_3.addWidget(self.btn_report)
+ self.btn_back = QtWidgets.QPushButton(self.frame)
+ self.btn_back.setFlat(True)
+ self.btn_back.setObjectName("btn_back")
+ self.horizontalLayout_3.addWidget(self.btn_back)
+ self.toolButton_output = QtWidgets.QToolButton(self.frame)
+ icon = QtGui.QIcon()
+ icon.addPixmap(QtGui.QPixmap("../../data/icons/output.svg"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
+ self.toolButton_output.setIcon(icon)
+ self.toolButton_output.setCheckable(False)
+ self.toolButton_output.setPopupMode(QtWidgets.QToolButton.MenuButtonPopup)
+ self.toolButton_output.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
+ self.toolButton_output.setAutoRaise(True)
+ self.toolButton_output.setArrowType(QtCore.Qt.NoArrow)
+ self.toolButton_output.setObjectName("toolButton_output")
+ self.horizontalLayout_3.addWidget(self.toolButton_output)
+ self.verticalLayout.addLayout(self.horizontalLayout_3)
+ self.stackedWidget = QtWidgets.QStackedWidget(self.frame)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.page_3 = QtWidgets.QWidget()
+ self.page_3.setObjectName("page_3")
+ self.stackedWidget.addWidget(self.page_3)
+ self.page_4 = QtWidgets.QWidget()
+ self.page_4.setObjectName("page_4")
+ self.stackedWidget.addWidget(self.page_4)
+ self.verticalLayout.addWidget(self.stackedWidget)
+ self.horizontalLayout.addWidget(self.frame)
+
+ self.retranslateUi(Form)
+ self.stackedWidget.setCurrentIndex(1)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
+ self.btn_analysis.setText(_translate("Form", "统计信息"))
+ self.btn_emotion.setText(_translate("Form", "情感分析"))
+ self.btn_report.setText(_translate("Form", "年度报告"))
+ self.btn_back.setText(_translate("Form", "退出"))
+ self.toolButton_output.setText(_translate("Form", "导出聊天记录"))
diff --git a/app/ui_pc/contact/contactInfoUi.ui b/app/ui_pc/contact/contactInfoUi.ui
new file mode 100644
index 0000000..18591fb
--- /dev/null
+++ b/app/ui_pc/contact/contactInfoUi.ui
@@ -0,0 +1,168 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 817
+ 748
+
+
+
+ Form
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ 0
+
+
-
+
+
+
+ 16777215
+ 100
+
+
+
+
+ 12
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+ 统计信息
+
+
+ true
+
+
+
+ -
+
+
+ 情感分析
+
+
+ true
+
+
+
+ -
+
+
+ 年度报告
+
+
+ true
+
+
+
+ -
+
+
+ 退出
+
+
+ true
+
+
+
+ -
+
+
+ 导出聊天记录
+
+
+
+ ../../data/icons/output.svg
+ ../../data/icons/output.svg
+
+
+
+ false
+
+
+ QToolButton::MenuButtonPopup
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+ Qt::NoArrow
+
+
+
+
+
+ -
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui_pc/contact/contactUi.py b/app/ui_pc/contact/contactUi.py
new file mode 100644
index 0000000..3c70aca
--- /dev/null
+++ b/app/ui_pc/contact/contactUi.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'contactUi.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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(840, 752)
+ Form.setStyleSheet("background: rgb(240, 240, 240);")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form)
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setSpacing(0)
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(Form)
+ self.label.setText("")
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.lineEdit = QtWidgets.QLineEdit(Form)
+ self.lineEdit.setMinimumSize(QtCore.QSize(200, 30))
+ self.lineEdit.setMaximumSize(QtCore.QSize(200, 16777215))
+ self.lineEdit.setStyleSheet("background:transparent;\n"
+ "border-width:0;\n"
+ "border-style:outset;\n"
+ "background-color:rgb(226,226,226);")
+ self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle)
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+ self.label_2 = QtWidgets.QLabel(Form)
+ self.label_2.setText("")
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout.addWidget(self.label_2)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.verticalLayout_2.addLayout(self.verticalLayout)
+ self.listWidget = QtWidgets.QListWidget(Form)
+ self.listWidget.setMinimumSize(QtCore.QSize(250, 0))
+ self.listWidget.setMaximumSize(QtCore.QSize(250, 16777215))
+ self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.listWidget.setObjectName("listWidget")
+ self.verticalLayout_2.addWidget(self.listWidget)
+ self.verticalLayout_2.setStretch(1, 1)
+ self.horizontalLayout_2.addLayout(self.verticalLayout_2)
+ self.stackedWidget = QtWidgets.QStackedWidget(Form)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.horizontalLayout_2.addWidget(self.stackedWidget)
+ self.horizontalLayout_2.setStretch(1, 1)
+
+ self.retranslateUi(Form)
+ self.stackedWidget.setCurrentIndex(-1)
+ QtCore.QMetaObject.connectSlotsByName(Form)
+
+ def retranslateUi(self, Form):
+ _translate = QtCore.QCoreApplication.translate
+ Form.setWindowTitle(_translate("Form", "Form"))
diff --git a/app/ui_pc/contact/contactUi.ui b/app/ui_pc/contact/contactUi.ui
new file mode 100644
index 0000000..116c90c
--- /dev/null
+++ b/app/ui_pc/contact/contactUi.ui
@@ -0,0 +1,105 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 840
+ 752
+
+
+
+ Form
+
+
+ background: rgb(240, 240, 240);
+
+
+ -
+
+
+ 0
+
+
-
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+
+ 200
+ 30
+
+
+
+
+ 200
+ 16777215
+
+
+
+ background:transparent;
+ border-width:0;
+ border-style:outset;
+ background-color:rgb(226,226,226);
+
+
+
+ Qt::VisualMoveStyle
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 250
+ 0
+
+
+
+
+ 250
+ 16777215
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+
+
+
+ -
+
+
+ -1
+
+
+
+
+
+
+
+
diff --git a/app/ui_pc/contact/contact_window.py b/app/ui_pc/contact/contact_window.py
new file mode 100644
index 0000000..1496928
--- /dev/null
+++ b/app/ui_pc/contact/contact_window.py
@@ -0,0 +1,114 @@
+from PyQt5.QtCore import QThread, pyqtSignal
+from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit
+
+from app.DataBase import micro_msg, misc
+from app.components import ContactQListWidgetItem
+from app.person import ContactPC
+from .contactInfo import ContactInfo
+from .contactUi import Ui_Form
+from ..Icon import Icon
+
+# 美化样式表
+Stylesheet = """
+
+/*去掉item虚线边框*/
+QListWidget, QListView, QTreeWidget, QTreeView {
+ outline: 0px;
+ border:none;
+ background-color:rgb(240,240,240)
+}
+/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
+QListWidget {
+ min-width: 250px;
+ max-width: 250px;
+ min-height: 80px;
+ max-height: 1200px;
+ color: black;
+ border:none;
+}
+QListWidget::item{
+ height:60px;
+ width:250px;
+}
+/*被选中时的背景颜色和左边框颜色*/
+QListWidget::item:selected {
+ background: rgb(204, 204, 204);
+ border-bottom: 2px solid rgb(9, 187, 7);
+ border-left:none;
+ color: black;
+ font-weight: bold;
+}
+/*鼠标悬停颜色*/
+HistoryPanel::item:hover {
+ background: rgb(52, 52, 52);
+}
+"""
+
+
+class ContactWindow(QWidget, Ui_Form):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.show_thread = None
+ self.setupUi(self)
+ self.ok_flag = False
+ self.setStyleSheet(Stylesheet)
+ self.init_ui()
+ self.show_contacts()
+
+ def init_ui(self):
+ search_action = QAction(self.lineEdit)
+ search_action.setIcon(Icon.Search_Icon)
+ self.lineEdit.addAction(search_action, QLineEdit.LeadingPosition)
+ self.listWidget.clear()
+ self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
+ self.listWidget.setCurrentRow(0)
+ self.stackedWidget.setCurrentIndex(0)
+
+ def show_contacts(self):
+ if self.ok_flag:
+ return
+ micro_msg.init_database()
+ if not micro_msg.is_database_exist():
+ QMessageBox.critical(self, "错误", "数据库不存在\n请先解密数据库")
+ return
+ self.show_thread = ShowContactThread()
+ self.show_thread.showSingal.connect(self.show_contact)
+ self.show_thread.start()
+ self.ok_flag = True
+
+ def show_contact(self, contact):
+ contact_item = ContactQListWidgetItem(contact.nickName, contact.smallHeadImgUrl, contact.smallHeadImgBLOG)
+ self.listWidget.addItem(contact_item)
+ self.listWidget.setItemWidget(contact_item, contact_item.widget)
+ contact_info_window = ContactInfo(contact)
+ self.stackedWidget.addWidget(contact_info_window)
+
+ def setCurrentIndex(self, row):
+ print(row)
+ self.stackedWidget.setCurrentIndex(row)
+
+
+class ShowContactThread(QThread):
+ showSingal = pyqtSignal(ContactPC)
+
+ # heightSingal = pyqtSignal(int)
+ def __init__(self):
+ super().__init__()
+
+ def run(self) -> None:
+ contact_info_lists = micro_msg.get_contact()
+ for contact_info_list in contact_info_lists:
+ # UserName, Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
+ contact_info = {
+ 'UserName': contact_info_list[0],
+ 'Alias': contact_info_list[1],
+ 'Type': contact_info_list[2],
+ 'Remark': contact_info_list[3],
+ 'NickName': contact_info_list[4],
+ 'smallHeadImgUrl': contact_info_list[7]
+ }
+ contact = ContactPC(contact_info)
+ contact.smallHeadImgBLOG = misc.get_avatar_buffer(contact.wxid)
+ contact.set_avatar(contact.smallHeadImgBLOG)
+ self.showSingal.emit(contact)
+ # pprint(contact.__dict__)
diff --git a/app/ui_pc/contact/userinfo/__init__.py b/app/ui_pc/contact/userinfo/__init__.py
new file mode 100644
index 0000000..6d32454
--- /dev/null
+++ b/app/ui_pc/contact/userinfo/__init__.py
@@ -0,0 +1,9 @@
+# -*- coding: utf-8 -*-
+"""
+@File : __init__.py.py
+@Author : Shuaikang Zhou
+@Time : 2022/12/24 10:34
+@IDE : Pycharm
+@Version : Python3.10
+@comment : ···
+"""
diff --git a/app/ui_pc/contact/userinfo/userinfo.py b/app/ui_pc/contact/userinfo/userinfo.py
new file mode 100644
index 0000000..5a6ac0b
--- /dev/null
+++ b/app/ui_pc/contact/userinfo/userinfo.py
@@ -0,0 +1,15 @@
+from PyQt5.QtWidgets import *
+
+from .userinfoUi import Ui_Frame
+
+
+class UserinfoController(QWidget, Ui_Frame):
+ def __init__(self, contact, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)
+ self.l_remark.setText(contact.remark)
+ self.l_avatar.setPixmap(contact.avatar)
+ self.l_nickname.setText(f'昵称:{contact.nickName}')
+ self.l_username.setText(f'微信号:{contact.alias}')
+ self.lineEdit.setText(contact.remark)
+ self.progressBar.setVisible(False)
diff --git a/app/ui_pc/contact/userinfo/userinfoUi.py b/app/ui_pc/contact/userinfo/userinfoUi.py
new file mode 100644
index 0000000..8154cf6
--- /dev/null
+++ b/app/ui_pc/contact/userinfo/userinfoUi.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'userinfoUi.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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_Frame(object):
+ def setupUi(self, Frame):
+ Frame.setObjectName("Frame")
+ Frame.resize(800, 720)
+ Frame.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
+ Frame.setMouseTracking(True)
+ Frame.setTabletTracking(True)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout(Frame)
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem1)
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setHorizontalSpacing(6)
+ self.gridLayout.setObjectName("gridLayout")
+ self.l_avatar = QtWidgets.QLabel(Frame)
+ self.l_avatar.setMinimumSize(QtCore.QSize(80, 80))
+ self.l_avatar.setMaximumSize(QtCore.QSize(80, 80))
+ self.l_avatar.setText("")
+ self.l_avatar.setPixmap(QtGui.QPixmap("../../../a_img/be0fa6c0c4707fb5f7b37b660de826d3.jpg"))
+ self.l_avatar.setScaledContents(True)
+ self.l_avatar.setObjectName("l_avatar")
+ self.gridLayout.addWidget(self.l_avatar, 0, 0, 3, 1)
+ self.l_remark = QtWidgets.QLabel(Frame)
+ self.l_remark.setMinimumSize(QtCore.QSize(0, 30))
+ self.l_remark.setMaximumSize(QtCore.QSize(16777215, 30))
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.l_remark.setFont(font)
+ self.l_remark.setObjectName("l_remark")
+ self.gridLayout.addWidget(self.l_remark, 0, 1, 1, 1)
+ self.l_nickname = QtWidgets.QLabel(Frame)
+ self.l_nickname.setMinimumSize(QtCore.QSize(0, 30))
+ self.l_nickname.setMaximumSize(QtCore.QSize(16777215, 30))
+ self.l_nickname.setObjectName("l_nickname")
+ self.gridLayout.addWidget(self.l_nickname, 1, 1, 1, 1)
+ self.l_username = QtWidgets.QLabel(Frame)
+ self.l_username.setMinimumSize(QtCore.QSize(0, 20))
+ self.l_username.setMaximumSize(QtCore.QSize(16777215, 20))
+ self.l_username.setObjectName("l_username")
+ self.gridLayout.addWidget(self.l_username, 2, 1, 1, 1)
+ self.gridLayout.setRowStretch(0, 1)
+ self.gridLayout.setRowStretch(1, 1)
+ self.gridLayout.setRowStretch(2, 1)
+ self.verticalLayout.addLayout(self.gridLayout)
+ spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem2)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(Frame)
+ self.label.setMinimumSize(QtCore.QSize(80, 0))
+ self.label.setMaximumSize(QtCore.QSize(80, 16777215))
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.label.setFont(font)
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.lineEdit = QtWidgets.QLineEdit(Frame)
+ self.lineEdit.setMinimumSize(QtCore.QSize(0, 25))
+ self.lineEdit.setMaximumSize(QtCore.QSize(16777215, 25))
+ font = QtGui.QFont()
+ font.setPointSize(15)
+ self.lineEdit.setFont(font)
+ self.lineEdit.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor))
+ self.lineEdit.setAutoFillBackground(False)
+ self.lineEdit.setStyleSheet("background:transparent;border-width:0;border-style:outset")
+ self.lineEdit.setObjectName("lineEdit")
+ self.horizontalLayout.addWidget(self.lineEdit)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem3)
+ self.progressBar = QtWidgets.QProgressBar(Frame)
+ self.progressBar.setProperty("value", 24)
+ self.progressBar.setObjectName("progressBar")
+ self.verticalLayout.addWidget(self.progressBar)
+ spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout.addItem(spacerItem4)
+ self.verticalLayout.setStretch(0, 2)
+ self.verticalLayout.setStretch(1, 3)
+ self.verticalLayout.setStretch(2, 4)
+ self.verticalLayout.setStretch(3, 1)
+ self.verticalLayout.setStretch(4, 4)
+ self.verticalLayout.setStretch(5, 1)
+ self.verticalLayout.setStretch(6, 2)
+ self.horizontalLayout_2.addLayout(self.verticalLayout)
+ spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem5)
+ self.horizontalLayout_2.setStretch(0, 1)
+ self.horizontalLayout_2.setStretch(1, 2)
+ self.horizontalLayout_2.setStretch(2, 1)
+ self.horizontalLayout_3.addLayout(self.horizontalLayout_2)
+
+ self.retranslateUi(Frame)
+ QtCore.QMetaObject.connectSlotsByName(Frame)
+
+ def retranslateUi(self, Frame):
+ _translate = QtCore.QCoreApplication.translate
+ Frame.setWindowTitle(_translate("Frame", "Frame"))
+ self.l_remark.setText(_translate("Frame", "曹雨萱"))
+ self.l_nickname.setText(_translate("Frame", "昵称:997"))
+ self.l_username.setText(_translate("Frame", "账号:TextLabel"))
+ self.label.setText(_translate("Frame", "备注名"))
+ self.lineEdit.setText(_translate("Frame", "曹雨萱"))
diff --git a/app/ui_pc/contact/userinfo/userinfoUi.ui b/app/ui_pc/contact/userinfo/userinfoUi.ui
new file mode 100644
index 0000000..3bba2de
--- /dev/null
+++ b/app/ui_pc/contact/userinfo/userinfoUi.ui
@@ -0,0 +1,280 @@
+
+
+ Frame
+
+
+
+ 0
+ 0
+ 800
+ 720
+
+
+
+ IBeamCursor
+
+
+ true
+
+
+ true
+
+
+ Frame
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ 6
+
+
-
+
+
+
+ 80
+ 80
+
+
+
+
+ 80
+ 80
+
+
+
+
+
+
+ ../../../a_img/be0fa6c0c4707fb5f7b37b660de826d3.jpg
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 30
+
+
+
+
+ 16777215
+ 30
+
+
+
+
+ 15
+
+
+
+ 曹雨萱
+
+
+
+ -
+
+
+
+ 0
+ 30
+
+
+
+
+ 16777215
+ 30
+
+
+
+ 昵称:997
+
+
+
+ -
+
+
+
+ 0
+ 20
+
+
+
+
+ 16777215
+ 20
+
+
+
+ 账号:TextLabel
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
-
+
+
+
+ 80
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+
+ 15
+
+
+
+ 备注名
+
+
+
+ -
+
+
+
+ 0
+ 25
+
+
+
+
+ 16777215
+ 25
+
+
+
+
+ 15
+
+
+
+ ArrowCursor
+
+
+ false
+
+
+
+ background:transparent;border-width:0;border-style:outset
+
+
+
+ 曹雨萱
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
+ 24
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui_pc/mainview.py b/app/ui_pc/mainview.py
new file mode 100644
index 0000000..0b051e7
--- /dev/null
+++ b/app/ui_pc/mainview.py
@@ -0,0 +1,121 @@
+# -*- coding: utf-8 -*-
+"""
+@File : mainview.py
+@Author : Shuaikang Zhou
+@Time : 2022/12/13 15:07
+@IDE : Pycharm
+@Version : Python3.10
+@comment : 主窗口
+"""
+from random import randint
+
+from PyQt5.QtCore import *
+from PyQt5.QtWidgets import *
+
+from app import config
+from app.DataBase import msg
+from app.Ui.Icon import Icon
+from . import mainwindow
+from .contact import ContactWindow
+from .tool import ToolWindow
+
+# 美化样式表
+Stylesheet = """
+QPushButton {
+ background-color: rgb(240,240,240);
+ border:none;
+}
+QPushButton:hover{
+ background-color: rgb(209,209,209);
+}
+/*去掉item虚线边框*/
+QListWidget, QListView, QTreeWidget, QTreeView {
+ outline: 0px;
+}
+/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
+QListWidget {
+ min-width: 120px;
+ max-width: 120px;
+ color: black;
+ background: white;
+ border:none;
+}
+QListWidget::item{
+ height:60;
+}
+/*被选中时的背景颜色和左边框颜色*/
+QListWidget::item:selected {
+ background: rgb(204, 204, 204);
+ border-left: 4px solid rgb(9, 187, 7);
+ color: black;
+ font-weight: bold;
+}
+/*鼠标悬停颜色*/
+HistoryPanel::item:hover {
+ background: rgb(52, 52, 52);
+}
+"""
+
+
+class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
+ exitSignal = pyqtSignal()
+
+ # username = ''
+ def __init__(self, username, parent=None):
+ super(MainWinController, self).__init__(parent)
+ self.setupUi(self)
+ self.setWindowIcon(Icon.MainWindow_Icon)
+ self.setStyleSheet(Stylesheet)
+ self.listWidget.clear()
+ # self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
+ self.action_desc.triggered.connect(self.about)
+ self.init_ui()
+
+ def init_ui(self):
+ self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
+ chat_item = QListWidgetItem(Icon.Chat_Icon, '聊天', self.listWidget)
+ contact_item = QListWidgetItem(Icon.Contact_Icon, '好友', self.listWidget)
+ myinfo_item = QListWidgetItem(Icon.MyInfo_Icon, '我的', self.listWidget)
+ tool_item = QListWidgetItem(Icon.MyInfo_Icon, '工具', self.listWidget)
+
+ tool_window = ToolWindow()
+ label = QLabel('我是页面', self)
+ label.setAlignment(Qt.AlignCenter)
+ # 设置label的背景颜色(这里随机)
+ # 这里加了一个margin边距(方便区分QStackedWidget和QLabel的颜色)
+ label.setStyleSheet('background: rgb(%d, %d, %d);margin: 50px;' % (
+ randint(0, 255), randint(0, 255), randint(0, 255)))
+ self.stackedWidget.addWidget(label)
+ self.contact_window = ContactWindow()
+ self.stackedWidget.addWidget(self.contact_window)
+ label = QLabel('我是页面', self)
+ label.setAlignment(Qt.AlignCenter)
+ # 设置label的背景颜色(这里随机)
+ # 这里加了一个margin边距(方便区分QStackedWidget和QLabel的颜色)
+ label.setStyleSheet('background: rgb(%d, %d, %d);margin: 50px;' % (
+ randint(0, 255), randint(0, 255), randint(0, 255)))
+ self.stackedWidget.addWidget(label)
+ self.stackedWidget.addWidget(tool_window)
+ self.listWidget.setCurrentRow(3)
+ self.stackedWidget.setCurrentIndex(3)
+
+ def setCurrentIndex(self, row):
+ if row == 1:
+ self.contact_window.show_contacts()
+ self.stackedWidget.setCurrentIndex(row)
+
+ def about(self):
+ """
+ 关于
+ """
+ QMessageBox.about(self, "关于",
+ f"版本:{config.version}\n"
+ f"QQ交流群:{config.contact}\n"
+ "地址:https://github.com/LC044/WeChatMsg"
+ )
+
+ def close(self) -> bool:
+ del self.listWidget
+ del self.stackedWidget
+ msg.close()
+ self.contact_window.close()
diff --git a/app/ui_pc/mainwindow.py b/app/ui_pc/mainwindow.py
new file mode 100644
index 0000000..9347ae1
--- /dev/null
+++ b/app/ui_pc/mainwindow.py
@@ -0,0 +1,149 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'mainwindow.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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_MainWindow(object):
+ def setupUi(self, MainWindow):
+ MainWindow.setObjectName("MainWindow")
+ MainWindow.resize(1280, 779)
+ MainWindow.setStyleSheet("\n"
+ "/*去掉item虚线边框*/\n"
+ "QListWidget, QListView, QTreeWidget, QTreeView {\n"
+ " outline: 0px;\n"
+ "}\n"
+ "/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/\n"
+ "QListWidget {\n"
+ " min-width: 120px;\n"
+ " max-width: 120px;\n"
+ " color: black;\n"
+ " background: white;\n"
+ " border:none;\n"
+ "}\n"
+ "QListWidget::item{\n"
+ " height:80;\n"
+ "}\n"
+ "/*被选中时的背景颜色和左边框颜色*/\n"
+ "QListWidget::item:selected {\n"
+ " background: rgb(204, 204, 204);\n"
+ " border-left: 4px solid rgb(9, 187, 7);\n"
+ "}\n"
+ "/*鼠标悬停颜色*/\n"
+ "HistoryPanel::item:hover {\n"
+ " background: rgb(52, 52, 52);\n"
+ "}")
+ self.centralwidget = QtWidgets.QWidget(MainWindow)
+ self.centralwidget.setObjectName("centralwidget")
+ self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
+ self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.frame_info = QtWidgets.QFrame(self.centralwidget)
+ self.frame_info.setMinimumSize(QtCore.QSize(80, 500))
+ self.frame_info.setMaximumSize(QtCore.QSize(80, 16777215))
+ self.frame_info.setStyleSheet("background-color:rgb(240,240,240)")
+ self.frame_info.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.frame_info.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.frame_info.setObjectName("frame_info")
+ self.myavatar = QtWidgets.QLabel(self.frame_info)
+ self.myavatar.setGeometry(QtCore.QRect(10, 40, 60, 60))
+ self.myavatar.setObjectName("myavatar")
+ self.listWidget = QtWidgets.QListWidget(self.frame_info)
+ self.listWidget.setGeometry(QtCore.QRect(0, 230, 120, 331))
+ self.listWidget.setObjectName("listWidget")
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ self.horizontalLayout.addWidget(self.frame_info)
+ self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
+ font = QtGui.QFont()
+ font.setFamily("微软雅黑")
+ font.setBold(False)
+ font.setWeight(50)
+ self.stackedWidget.setFont(font)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.horizontalLayout.addWidget(self.stackedWidget)
+ MainWindow.setCentralWidget(self.centralwidget)
+ self.menubar = QtWidgets.QMenuBar(MainWindow)
+ self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 23))
+ self.menubar.setObjectName("menubar")
+ self.menu_F = QtWidgets.QMenu(self.menubar)
+ self.menu_F.setObjectName("menu_F")
+ self.menu = QtWidgets.QMenu(self.menubar)
+ font = QtGui.QFont()
+ font.setFamily("微软雅黑")
+ self.menu.setFont(font)
+ self.menu.setObjectName("menu")
+ self.menu_2 = QtWidgets.QMenu(self.menubar)
+ self.menu_2.setObjectName("menu_2")
+ self.menu_about = QtWidgets.QMenu(self.menubar)
+ self.menu_about.setObjectName("menu_about")
+ MainWindow.setMenuBar(self.menubar)
+ self.statusbar = QtWidgets.QStatusBar(MainWindow)
+ self.statusbar.setObjectName("statusbar")
+ MainWindow.setStatusBar(self.statusbar)
+ self.action_3 = QtWidgets.QAction(MainWindow)
+ self.action_3.setObjectName("action_3")
+ self.action_4 = QtWidgets.QAction(MainWindow)
+ self.action_4.setObjectName("action_4")
+ self.action = QtWidgets.QAction(MainWindow)
+ font = QtGui.QFont()
+ font.setFamily("Microsoft YaHei UI")
+ self.action.setFont(font)
+ self.action.setObjectName("action")
+ self.action_desc = QtWidgets.QAction(MainWindow)
+ self.action_desc.setObjectName("action_desc")
+ self.menu_F.addSeparator()
+ self.menu_F.addSeparator()
+ self.menu_F.addAction(self.action_3)
+ self.menu_F.addAction(self.action_4)
+ self.menu_2.addAction(self.action)
+ self.menu_about.addAction(self.action_desc)
+ self.menubar.addAction(self.menu_F.menuAction())
+ self.menubar.addAction(self.menu.menuAction())
+ self.menubar.addAction(self.menu_2.menuAction())
+ self.menubar.addAction(self.menu_about.menuAction())
+
+ self.retranslateUi(MainWindow)
+ QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+ def retranslateUi(self, MainWindow):
+ _translate = QtCore.QCoreApplication.translate
+ MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.myavatar.setText(_translate("MainWindow", "avatar"))
+ __sortingEnabled = self.listWidget.isSortingEnabled()
+ self.listWidget.setSortingEnabled(False)
+ item = self.listWidget.item(0)
+ item.setText(_translate("MainWindow", "新建项目"))
+ item = self.listWidget.item(1)
+ item.setText(_translate("MainWindow", "新建项目"))
+ item = self.listWidget.item(2)
+ item.setText(_translate("MainWindow", "新建项目"))
+ item = self.listWidget.item(3)
+ item.setText(_translate("MainWindow", "新建项目"))
+ item = self.listWidget.item(4)
+ item.setText(_translate("MainWindow", "新建项目"))
+ self.listWidget.setSortingEnabled(__sortingEnabled)
+ self.menu_F.setTitle(_translate("MainWindow", "文件(F)"))
+ self.menu.setTitle(_translate("MainWindow", "编辑"))
+ self.menu_2.setTitle(_translate("MainWindow", "帮助"))
+ self.menu_about.setTitle(_translate("MainWindow", "关于"))
+ self.action_3.setText(_translate("MainWindow", "保存"))
+ self.action_4.setText(_translate("MainWindow", "退出"))
+ self.action.setText(_translate("MainWindow", "关于"))
+ self.action_desc.setText(_translate("MainWindow", "说明"))
diff --git a/app/ui_pc/mainwindow.ui b/app/ui_pc/mainwindow.ui
new file mode 100644
index 0000000..3bbcb76
--- /dev/null
+++ b/app/ui_pc/mainwindow.ui
@@ -0,0 +1,242 @@
+
+
+ MainWindow
+
+
+
+ 0
+ 0
+ 1280
+ 779
+
+
+
+ MainWindow
+
+
+
+ /*去掉item虚线边框*/
+ QListWidget, QListView, QTreeWidget, QTreeView {
+ outline: 0px;
+ }
+ /*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
+ QListWidget {
+ min-width: 120px;
+ max-width: 120px;
+ color: black;
+ background: white;
+ border:none;
+ }
+ QListWidget::item{
+ height:80;
+ }
+ /*被选中时的背景颜色和左边框颜色*/
+ QListWidget::item:selected {
+ background: rgb(204, 204, 204);
+ border-left: 4px solid rgb(9, 187, 7);
+ }
+ /*鼠标悬停颜色*/
+ HistoryPanel::item:hover {
+ background: rgb(52, 52, 52);
+ }
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 80
+ 500
+
+
+
+
+ 80
+ 16777215
+
+
+
+ background-color:rgb(240,240,240)
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
+
+ 10
+ 40
+ 60
+ 60
+
+
+
+ avatar
+
+
+
+
+
+ 0
+ 230
+ 80
+ 331
+
+
+
+
+ 80
+ 0
+
+
+
+
+ 80
+ 16777215
+
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ QAbstractScrollArea::AdjustIgnored
+
+
-
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+
+
+
+ -
+
+
+
+ 微软雅黑
+ 50
+ false
+
+
+
+
+
+
+
+
+
+
+ 保存
+
+
+
+
+ 退出
+
+
+
+
+ 关于
+
+
+
+ Microsoft YaHei UI
+
+
+
+
+
+ 说明
+
+
+
+
+
+
diff --git a/app/ui_pc/tool/__init__.py b/app/ui_pc/tool/__init__.py
new file mode 100644
index 0000000..8fe2f8e
--- /dev/null
+++ b/app/ui_pc/tool/__init__.py
@@ -0,0 +1,3 @@
+from .tool_window import ToolWindow
+
+__all__ = ['ToolWindow']
diff --git a/app/ui_pc/tool/pc_decrypt/__init__.py b/app/ui_pc/tool/pc_decrypt/__init__.py
new file mode 100644
index 0000000..ae1c618
--- /dev/null
+++ b/app/ui_pc/tool/pc_decrypt/__init__.py
@@ -0,0 +1,3 @@
+from .pc_decrypt import DecryptControl
+
+__all__ = ['DecryptControl']
diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.py b/app/ui_pc/tool/pc_decrypt/decryptUi.py
new file mode 100644
index 0000000..44145d5
--- /dev/null
+++ b/app/ui_pc/tool/pc_decrypt/decryptUi.py
@@ -0,0 +1,164 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'decryptUi.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(611, 519)
+ font = QtGui.QFont()
+ font.setFamily("微软雅黑")
+ Dialog.setFont(font)
+ Dialog.setLayoutDirection(QtCore.Qt.LeftToRight)
+ self.horizontalLayout_4 = QtWidgets.QHBoxLayout(Dialog)
+ self.horizontalLayout_4.setObjectName("horizontalLayout_4")
+ self.verticalLayout_2 = QtWidgets.QVBoxLayout()
+ self.verticalLayout_2.setObjectName("verticalLayout_2")
+ spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_2.addItem(spacerItem)
+ self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_3.setObjectName("horizontalLayout_3")
+ spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem1)
+ self.verticalLayout = QtWidgets.QVBoxLayout()
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.label_3 = QtWidgets.QLabel(Dialog)
+ font = QtGui.QFont()
+ font.setFamily("一纸情书")
+ font.setPointSize(20)
+ self.label_3.setFont(font)
+ self.label_3.setAlignment(QtCore.Qt.AlignCenter)
+ self.label_3.setObjectName("label_3")
+ self.verticalLayout.addWidget(self.label_3)
+ self.gridLayout_2 = QtWidgets.QGridLayout()
+ self.gridLayout_2.setObjectName("gridLayout_2")
+ self.gridLayout = QtWidgets.QGridLayout()
+ self.gridLayout.setObjectName("gridLayout")
+ self.label_phone = QtWidgets.QLabel(Dialog)
+ self.label_phone.setText("")
+ self.label_phone.setObjectName("label_phone")
+ self.gridLayout.addWidget(self.label_phone, 2, 1, 1, 1)
+ self.label_7 = QtWidgets.QLabel(Dialog)
+ self.label_7.setObjectName("label_7")
+ self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1)
+ self.lineEdit = QtWidgets.QLineEdit(Dialog)
+ self.lineEdit.setStyleSheet("background:transparent;border-width:0;border-style:outset")
+ self.lineEdit.setFrame(False)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout.addWidget(self.lineEdit, 4, 1, 1, 1)
+ self.label_5 = QtWidgets.QLabel(Dialog)
+ self.label_5.setObjectName("label_5")
+ self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
+ self.label_6 = QtWidgets.QLabel(Dialog)
+ self.label_6.setObjectName("label_6")
+ self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1)
+ self.label_key = QtWidgets.QLabel(Dialog)
+ self.label_key.setText("")
+ self.label_key.setObjectName("label_key")
+ self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1)
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setObjectName("label_2")
+ self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
+ self.label_pid = QtWidgets.QLabel(Dialog)
+ self.label_pid.setText("")
+ self.label_pid.setObjectName("label_pid")
+ self.gridLayout.addWidget(self.label_pid, 0, 1, 1, 1)
+ self.label_name = QtWidgets.QLabel(Dialog)
+ self.label_name.setText("")
+ self.label_name.setObjectName("label_name")
+ self.gridLayout.addWidget(self.label_name, 3, 1, 1, 1)
+ self.label_4 = QtWidgets.QLabel(Dialog)
+ self.label_4.setObjectName("label_4")
+ self.gridLayout.addWidget(self.label_4, 4, 0, 1, 1)
+ self.label_version = QtWidgets.QLabel(Dialog)
+ self.label_version.setText("")
+ self.label_version.setObjectName("label_version")
+ self.gridLayout.addWidget(self.label_version, 1, 1, 1, 1)
+ self.label_8 = QtWidgets.QLabel(Dialog)
+ self.label_8.setObjectName("label_8")
+ self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1)
+ self.label_db_dir = QtWidgets.QLabel(Dialog)
+ self.label_db_dir.setText("")
+ self.label_db_dir.setObjectName("label_db_dir")
+ self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1)
+ self.gridLayout.setColumnMinimumWidth(0, 1)
+ self.gridLayout.setColumnStretch(0, 1)
+ self.gridLayout.setColumnStretch(1, 10)
+ self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 2, 1)
+ self.btn_getinfo = QtWidgets.QPushButton(Dialog)
+ self.btn_getinfo.setMinimumSize(QtCore.QSize(0, 60))
+ self.btn_getinfo.setObjectName("btn_getinfo")
+ self.gridLayout_2.addWidget(self.btn_getinfo, 0, 1, 1, 1)
+ self.checkBox = QtWidgets.QCheckBox(Dialog)
+ self.checkBox.setText("")
+ self.checkBox.setObjectName("checkBox")
+ self.gridLayout_2.addWidget(self.checkBox, 0, 2, 1, 1)
+ self.btn_db_dir = QtWidgets.QPushButton(Dialog)
+ self.btn_db_dir.setMinimumSize(QtCore.QSize(0, 60))
+ self.btn_db_dir.setObjectName("btn_db_dir")
+ self.gridLayout_2.addWidget(self.btn_db_dir, 1, 1, 1, 1)
+ self.checkBox_2 = QtWidgets.QCheckBox(Dialog)
+ self.checkBox_2.setText("")
+ self.checkBox_2.setObjectName("checkBox_2")
+ self.gridLayout_2.addWidget(self.checkBox_2, 1, 2, 1, 1)
+ self.verticalLayout.addLayout(self.gridLayout_2)
+ self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
+ self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+ spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem2)
+ self.pushButton_3 = QtWidgets.QPushButton(Dialog)
+ self.pushButton_3.setMinimumSize(QtCore.QSize(0, 60))
+ self.pushButton_3.setMaximumSize(QtCore.QSize(100, 16777215))
+ self.pushButton_3.setObjectName("pushButton_3")
+ self.horizontalLayout_2.addWidget(self.pushButton_3)
+ spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_2.addItem(spacerItem3)
+ self.verticalLayout.addLayout(self.horizontalLayout_2)
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label_ready = QtWidgets.QLabel(Dialog)
+ self.label_ready.setObjectName("label_ready")
+ self.horizontalLayout.addWidget(self.label_ready)
+ self.progressBar = QtWidgets.QProgressBar(Dialog)
+ self.progressBar.setProperty("value", 50)
+ self.progressBar.setObjectName("progressBar")
+ self.horizontalLayout.addWidget(self.progressBar)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.horizontalLayout_3.addLayout(self.verticalLayout)
+ spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+ self.horizontalLayout_3.addItem(spacerItem4)
+ self.verticalLayout_2.addLayout(self.horizontalLayout_3)
+ spacerItem5 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.verticalLayout_2.addItem(spacerItem5)
+ self.horizontalLayout_4.addLayout(self.verticalLayout_2)
+
+ self.retranslateUi(Dialog)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.label_3.setText(_translate("Dialog", "解密数据库"))
+ self.label_7.setText(_translate("Dialog", "版本"))
+ self.label_5.setText(_translate("Dialog", "微信昵称"))
+ self.label_6.setText(_translate("Dialog", "密钥"))
+ self.label.setText(_translate("Dialog", "PID"))
+ self.label_2.setText(_translate("Dialog", "手机号"))
+ self.label_4.setText(_translate("Dialog", "wxid"))
+ self.label_8.setText(_translate("Dialog", "微信路径"))
+ self.btn_getinfo.setText(_translate("Dialog", "获取信息"))
+ self.btn_db_dir.setText(_translate("Dialog", "设置微信路径"))
+ self.pushButton_3.setText(_translate("Dialog", "开始解密"))
+ self.label_ready.setText(_translate("Dialog", "未就绪"))
diff --git a/app/ui_pc/tool/pc_decrypt/decryptUi.ui b/app/ui_pc/tool/pc_decrypt/decryptUi.ui
new file mode 100644
index 0000000..846fc4c
--- /dev/null
+++ b/app/ui_pc/tool/pc_decrypt/decryptUi.ui
@@ -0,0 +1,328 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 611
+ 519
+
+
+
+
+ 微软雅黑
+
+
+
+ Dialog
+
+
+ Qt::LeftToRight
+
+
+ -
+
+
-
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
-
+
+
+
+ 一纸情书
+ 20
+
+
+
+ 解密数据库
+
+
+ Qt::AlignCenter
+
+
+
+ -
+
+
-
+
+
-
+
+
+
+
+
+
+ -
+
+
+ 版本
+
+
+
+ -
+
+
+
+ background:transparent;border-width:0;border-style:outset
+
+
+
+ false
+
+
+
+ -
+
+
+ 微信昵称
+
+
+
+ -
+
+
+ 密钥
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ PID
+
+
+
+ -
+
+
+ 手机号
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ wxid
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ 微信路径
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+ 获取信息
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+ 设置微信路径
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 60
+
+
+
+
+ 100
+ 16777215
+
+
+
+ 开始解密
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
-
+
+
+ 未就绪
+
+
+
+ -
+
+
+ 50
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/Ui/pc_decrypt/pc_decrypt.py b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py
similarity index 99%
rename from app/Ui/pc_decrypt/pc_decrypt.py
rename to app/ui_pc/tool/pc_decrypt/pc_decrypt.py
index 9f236f2..af71116 100644
--- a/app/Ui/pc_decrypt/pc_decrypt.py
+++ b/app/ui_pc/tool/pc_decrypt/pc_decrypt.py
@@ -88,7 +88,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
# 中间可以添加处理逻辑
# QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下")
self.DecryptSignal.emit('ok')
- self.close()
+ # self.close()
def setProgressBarMaxNum(self, max_val):
self.progressBar.setRange(0, max_val)
diff --git a/app/ui_pc/tool/toolUI.py b/app/ui_pc/tool/toolUI.py
new file mode 100644
index 0000000..c459a95
--- /dev/null
+++ b/app/ui_pc/tool/toolUI.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'toolUI.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.7
+#
+# 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_Dialog(object):
+ def setupUi(self, Dialog):
+ Dialog.setObjectName("Dialog")
+ Dialog.resize(630, 633)
+ font = QtGui.QFont()
+ font.setFamily("微软雅黑")
+ Dialog.setFont(font)
+ self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
+ self.verticalLayout.setObjectName("verticalLayout")
+ self.horizontalLayout = QtWidgets.QHBoxLayout()
+ self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
+ self.horizontalLayout.setSpacing(0)
+ self.horizontalLayout.setObjectName("horizontalLayout")
+ self.label = QtWidgets.QLabel(Dialog)
+ self.label.setMaximumSize(QtCore.QSize(80, 80))
+ self.label.setObjectName("label")
+ self.horizontalLayout.addWidget(self.label)
+ self.listWidget = QtWidgets.QListWidget(Dialog)
+ self.listWidget.setMinimumSize(QtCore.QSize(500, 80))
+ self.listWidget.setMaximumSize(QtCore.QSize(500, 80))
+ self.listWidget.setFrameShape(QtWidgets.QFrame.NoFrame)
+ self.listWidget.setFrameShadow(QtWidgets.QFrame.Plain)
+ self.listWidget.setLineWidth(0)
+ self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
+ self.listWidget.setFlow(QtWidgets.QListView.LeftToRight)
+ self.listWidget.setObjectName("listWidget")
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ item = QtWidgets.QListWidgetItem()
+ self.listWidget.addItem(item)
+ self.horizontalLayout.addWidget(self.listWidget)
+ self.label_2 = QtWidgets.QLabel(Dialog)
+ self.label_2.setMaximumSize(QtCore.QSize(80, 80))
+ self.label_2.setObjectName("label_2")
+ self.horizontalLayout.addWidget(self.label_2)
+ self.verticalLayout.addLayout(self.horizontalLayout)
+ self.stackedWidget = QtWidgets.QStackedWidget(Dialog)
+ self.stackedWidget.setObjectName("stackedWidget")
+ self.verticalLayout.addWidget(self.stackedWidget)
+ self.verticalLayout.setStretch(1, 1)
+
+ self.retranslateUi(Dialog)
+ self.stackedWidget.setCurrentIndex(-1)
+ QtCore.QMetaObject.connectSlotsByName(Dialog)
+
+ def retranslateUi(self, Dialog):
+ _translate = QtCore.QCoreApplication.translate
+ Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
+ self.label.setText(_translate("Dialog", "TextLabel"))
+ __sortingEnabled = self.listWidget.isSortingEnabled()
+ self.listWidget.setSortingEnabled(False)
+ item = self.listWidget.item(0)
+ item.setText(_translate("Dialog", "新建项目"))
+ item = self.listWidget.item(1)
+ item.setText(_translate("Dialog", "新建项目"))
+ item = self.listWidget.item(2)
+ item.setText(_translate("Dialog", "新建项目"))
+ item = self.listWidget.item(3)
+ item.setText(_translate("Dialog", "新建项目"))
+ item = self.listWidget.item(4)
+ item.setText(_translate("Dialog", "新建项目"))
+ self.listWidget.setSortingEnabled(__sortingEnabled)
+ self.label_2.setText(_translate("Dialog", "TextLabel"))
diff --git a/app/ui_pc/tool/toolUI.ui b/app/ui_pc/tool/toolUI.ui
new file mode 100644
index 0000000..19fb825
--- /dev/null
+++ b/app/ui_pc/tool/toolUI.ui
@@ -0,0 +1,128 @@
+
+
+ Dialog
+
+
+
+ 0
+ 0
+ 630
+ 547
+
+
+
+
+ 微软雅黑
+
+
+
+ Dialog
+
+
+ -
+
+
+ 0
+
+
+ QLayout::SetDefaultConstraint
+
+
-
+
+
+
+ 80
+ 80
+
+
+
+ TextLabel
+
+
+
+ -
+
+
+
+ 500
+ 80
+
+
+
+
+ 500
+ 80
+
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+ 0
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ Qt::ScrollBarAlwaysOff
+
+
+ QListView::LeftToRight
+
+
-
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+ -
+
+ 新建项目
+
+
+
+
+ -
+
+
+
+ 80
+ 80
+
+
+
+ TextLabel
+
+
+
+
+
+ -
+
+
+ -1
+
+
+
+
+
+
+
+
diff --git a/app/ui_pc/tool/tool_window.py b/app/ui_pc/tool/tool_window.py
new file mode 100644
index 0000000..29d1bf3
--- /dev/null
+++ b/app/ui_pc/tool/tool_window.py
@@ -0,0 +1,77 @@
+from random import randint
+
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QWidget, QListWidgetItem, QLabel
+
+from .pc_decrypt import DecryptControl
+from .toolUI import Ui_Dialog
+from ...Ui.Icon import Icon
+
+# 美化样式表
+Stylesheet = """
+
+/*去掉item虚线边框*/
+QListWidget, QListView, QTreeWidget, QTreeView {
+ outline: 0px;
+ border:none;
+ background-color:rgb(240,240,240)
+}
+/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
+QListWidget {
+ min-width: 400px;
+ max-width: 400px;
+ min-height: 80px;
+ max-height: 80px;
+ color: black;
+ border:none;
+}
+QListWidget::item{
+ height:80px;
+ width:80px;
+}
+/*被选中时的背景颜色和左边框颜色*/
+QListWidget::item:selected {
+ background: rgb(204, 204, 204);
+ border-bottom: 4px solid rgb(9, 187, 7);
+ border-left:none;
+ color: black;
+ font-weight: bold;
+}
+/*鼠标悬停颜色*/
+HistoryPanel::item:hover {
+ background: rgb(52, 52, 52);
+}
+"""
+
+
+class ToolWindow(QWidget, Ui_Dialog):
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.setupUi(self)
+ self.setStyleSheet(Stylesheet)
+ self.init_ui()
+
+ def init_ui(self):
+ self.listWidget.clear()
+ self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
+ chat_item = QListWidgetItem(Icon.Chat_Icon, '解密', self.listWidget)
+ contact_item = QListWidgetItem(Icon.Contact_Icon, 'None', self.listWidget)
+ myinfo_item = QListWidgetItem(Icon.MyInfo_Icon, 'None', self.listWidget)
+ tool_item = QListWidgetItem(Icon.MyInfo_Icon, 'None', self.listWidget)
+ tool_window = DecryptControl()
+ self.stackedWidget.addWidget(tool_window)
+ label = QLabel('我是页面', self)
+ label.setAlignment(Qt.AlignCenter)
+ # 设置label的背景颜色(这里随机)
+ # 这里加了一个margin边距(方便区分QStackedWidget和QLabel的颜色)
+ label.setStyleSheet('background: rgb(%d, %d, %d);margin: 50px;' % (
+ randint(0, 255), randint(0, 255), randint(0, 255)))
+ self.stackedWidget.addWidget(label)
+ self.stackedWidget.addWidget(label)
+ self.stackedWidget.addWidget(label)
+ self.listWidget.setCurrentRow(0)
+ self.stackedWidget.setCurrentIndex(0)
+
+ def setCurrentIndex(self, row):
+ print(row)
+ self.stackedWidget.setCurrentIndex(row)
diff --git a/decrypt_window.py b/decrypt_window.py
index 2918b42..04f7b7b 100644
--- a/decrypt_window.py
+++ b/decrypt_window.py
@@ -1,10 +1,12 @@
import ctypes
import sys
+import time
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import *
-from app.Ui import pc_decrypt
+from app.ui_pc import mainview
+from app.ui_pc.tool.pc_decrypt import pc_decrypt
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
@@ -26,6 +28,23 @@ class ViewController(QWidget):
self.viewDecrypt.DecryptSignal.connect(self.show_success)
self.viewDecrypt.show()
+ def loadMainWinView(self, username=None):
+ """
+ 聊天界面
+ :param username: 账号
+ :return:
+ """
+ username = ''
+ start = time.time()
+ self.viewMainWIn = mainview.MainWinController(username=username)
+ self.viewMainWIn.setWindowTitle("Chat")
+ # print(username)
+ self.viewMainWIn.username = username
+ # self.viewMainWIn.exitSignal.connect(self.loadDecryptView) # 不需要回到登录界面可以省略
+ self.viewMainWIn.show()
+ end = time.time()
+ print('ok', end - start)
+
def show_success(self):
QMessageBox.about(self, "解密成功", "数据库文件存储在\napp/DataBase/Msg\n文件夹下")
@@ -33,6 +52,7 @@ class ViewController(QWidget):
if __name__ == '__main__':
app = QApplication(sys.argv)
view = ViewController()
- view.loadPCDecryptView()
+ # view.loadPCDecryptView()
+ view.loadMainWinView()
# view.show_success()
sys.exit(app.exec_())
diff --git a/main.py b/main.py
index 6fdd1dd..b21f867 100644
--- a/main.py
+++ b/main.py
@@ -5,7 +5,7 @@ import time
from PyQt5.QtWidgets import *
import app.DataBase.data as DB
-from app.Ui import decrypt, mainview, pc_decrypt
+from app.Ui import decrypt, mainview
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")