Merge pull request #13 from LC044/dev_zsk

Dev zsk
This commit is contained in:
SiYuan 2023-11-17 23:03:12 +08:00 committed by GitHub
commit 3b277826f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1216 additions and 204 deletions

View File

@ -4,11 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="新增聊天记录导出csv格式">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="修复聊天气泡不能更改大小的bug">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/DataBase/micro_msg.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/micro_msg.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/DataBase/msg.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/msg.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/person.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/person.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/mainview.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/mainview.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/pc_decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/pc_decrypt.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/tool/tool_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/tool_window.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -80,7 +82,7 @@
<option name="stateVersion" value="1" />
</component>
<component name="ProblemsViewState">
<option name="selectedTabId" value="ProjectErrors" />
<option name="selectedTabId" value="CurrentFile" />
</component>
<component name="ProjectId" id="2JrtgPdd86dlNq47HAnVDPZuCVh" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
@ -88,15 +90,15 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Program Files/Python310/Scripts/pyuic5.exe&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;preferences.pluginManager&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"DefaultHtmlFileTemplate": "HTML File",
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"last_opened_file_path": "D:/Program Files/Python310/Scripts/pyuic5.exe",
"settings.editor.selected.configurable": "editor.preferences.tabs"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\Project\Python\WeChatMsg\app\ui_pc\tool" />
@ -104,8 +106,50 @@
<recent name="D:\Project\PythonProject\WeChatMsg\app\Ui" />
</key>
</component>
<component name="RunManager" selected="Python.decrypt_window">
<configuration name="decrypt_window" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<component name="RunManager" selected="Python.main_pc">
<configuration name="bubble_message" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app/components" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/components/bubble_message.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="chat_info" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app/ui_pc/chat" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="main_pc" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@ -117,7 +161,7 @@
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/decrypt_window.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main_pc.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
@ -126,7 +170,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="main" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<configuration name="mainview" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@ -134,35 +178,14 @@
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app/ui_pc" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/main.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/ui_pc/mainview.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="misc" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app/DataBase" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/DataBase/misc.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
@ -210,7 +233,7 @@
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<configuration name="person" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
<module name="WeChatMsg" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@ -218,14 +241,14 @@
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/app" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/test.py" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app/person.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="EMULATE_TERMINAL" value="true" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
@ -233,11 +256,11 @@
</configuration>
<recent_temporary>
<list>
<item itemvalue="Python.decrypt_window" />
<item itemvalue="Python.main_pc" />
<item itemvalue="Python.person" />
<item itemvalue="Python.bubble_message" />
<item itemvalue="Python.chat_info" />
<item itemvalue="Python.msg" />
<item itemvalue="Python.test" />
<item itemvalue="Python.main" />
<item itemvalue="Python.misc" />
</list>
</recent_temporary>
</component>
@ -253,34 +276,6 @@
<option name="presentableId" value="Default" />
<updated>1672848140146</updated>
</task>
<task id="LOCAL-00017" summary="修复了Webengine横向小的问题">
<created>1680670947385</created>
<option name="number" value="00017" />
<option name="presentableId" value="LOCAL-00017" />
<option name="project" value="LOCAL" />
<updated>1680670947385</updated>
</task>
<task id="LOCAL-00018" summary="新增聊天报告">
<created>1682305451381</created>
<option name="number" value="00018" />
<option name="presentableId" value="LOCAL-00018" />
<option name="project" value="LOCAL" />
<updated>1682305451381</updated>
</task>
<task id="LOCAL-00019" summary="readme">
<created>1684598124207</created>
<option name="number" value="00019" />
<option name="presentableId" value="LOCAL-00019" />
<option name="project" value="LOCAL" />
<updated>1684598124207</updated>
</task>
<task id="LOCAL-00020" summary="readme">
<created>1684598177829</created>
<option name="number" value="00020" />
<option name="presentableId" value="LOCAL-00020" />
<option name="project" value="LOCAL" />
<updated>1684598177830</updated>
</task>
<task id="LOCAL-00021" summary="readme">
<created>1684598440645</created>
<option name="number" value="00021" />
@ -596,7 +591,35 @@
<option name="project" value="LOCAL" />
<updated>1700145601841</updated>
</task>
<option name="localTasksCounter" value="66" />
<task id="LOCAL-00066" summary="导出所有数据库的聊天记录">
<created>1700147800698</created>
<option name="number" value="00066" />
<option name="presentableId" value="LOCAL-00066" />
<option name="project" value="LOCAL" />
<updated>1700147800698</updated>
</task>
<task id="LOCAL-00067" summary="update readme">
<created>1700150198343</created>
<option name="number" value="00067" />
<option name="presentableId" value="LOCAL-00067" />
<option name="project" value="LOCAL" />
<updated>1700150198343</updated>
</task>
<task id="LOCAL-00068" summary="数据库加锁避免多线程访问报错">
<created>1700228064961</created>
<option name="number" value="00068" />
<option name="presentableId" value="LOCAL-00068" />
<option name="project" value="LOCAL" />
<updated>1700228064961</updated>
</task>
<task id="LOCAL-00069" summary="修复聊天气泡不能更改大小的bug">
<created>1700232296923</created>
<option name="number" value="00069" />
<option name="presentableId" value="LOCAL-00069" />
<option name="project" value="LOCAL" />
<updated>1700232296923</updated>
</task>
<option name="localTasksCounter" value="70" />
<servers />
</component>
<component name="UnknownFeatures">
@ -632,9 +655,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="修复部分bug" />
<MESSAGE value="替换chat里的contact" />
<MESSAGE value="增加中文路径提示" />
<MESSAGE value="用stackWidget重写contactUI" />
<MESSAGE value="修改联系人视图架构" />
<MESSAGE value="加快打开速度" />
@ -646,7 +666,6 @@
<MESSAGE value="新增PC数据库解密" />
<MESSAGE value="main首次加载解密界面" />
<MESSAGE value="增加日志模块" />
<MESSAGE value="update readme" />
<MESSAGE value="增加PC端微信解密条件的判断" />
<MESSAGE value="删除多余的Word文件" />
<MESSAGE value="修复无法查找wxid的bug" />
@ -657,7 +676,11 @@
<MESSAGE value="修复db文件空格路径的bug" />
<MESSAGE value="修复情感分析数值显示过长的bug" />
<MESSAGE value="新增聊天记录导出csv格式" />
<option name="LAST_COMMIT_MESSAGE" value="新增聊天记录导出csv格式" />
<MESSAGE value="导出所有数据库的聊天记录" />
<MESSAGE value="update readme" />
<MESSAGE value="数据库加锁避免多线程访问报错" />
<MESSAGE value="修复聊天气泡不能更改大小的bug" />
<option name="LAST_COMMIT_MESSAGE" value="修复聊天气泡不能更改大小的bug" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
</component>
@ -679,6 +702,21 @@
<line>103</line>
<option name="timeStamp" value="9" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>101</line>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>99</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>100</line>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints>
<default-breakpoints>
<breakpoint type="python-exception">

View File

@ -1,5 +1,6 @@
import os.path
import sqlite3
import time
DB = None
cursor = None
@ -25,15 +26,25 @@ def is_database_exist():
def get_contact():
sql = '''select UserName,Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl.bigHeadImgUrl
from Contact inner join ContactHeadImgUrl on Contact.UserName = ContactHeadImgUrl.usrName
where Type=3 and Alias is not null
order by PYInitial
'''
cursor.execute(sql)
result = cursor.fetchall()
# pprint(result)
# print(len(result))
try:
sql = '''select UserName,Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl.bigHeadImgUrl
from Contact inner join ContactHeadImgUrl on Contact.UserName = ContactHeadImgUrl.usrName
where Type=3 and Alias is not null
order by PYInitial
limit 30
'''
cursor.execute(sql)
result = cursor.fetchall()
except:
time.sleep(0.2)
sql = '''select UserName,Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl.bigHeadImgUrl
from Contact inner join ContactHeadImgUrl on Contact.UserName = ContactHeadImgUrl.usrName
where Type=3 and Alias is not null
order by PYInitial
'''
cursor.execute(sql)
result = cursor.fetchall()
# DB.commit()
return result

View File

@ -1,6 +1,8 @@
import os.path
import sqlite3
import threading
lock = threading.Lock()
DB = None
cursor = None
misc_path = "./app/Database/Msg/Misc.db"
@ -17,11 +19,15 @@ def get_avatar_buffer(userName):
from ContactHeadImg1
where usrName=?;
'''
cursor.execute(sql, [userName])
result = cursor.fetchall()
# print(result[0][0])
if result:
return result[0][0]
try:
lock.acquire(True)
cursor.execute(sql, [userName])
result = cursor.fetchall()
# print(result[0][0])
if result:
return result[0][0]
finally:
lock.release()
return None

View File

@ -1,10 +1,12 @@
import os.path
import re
import sqlite3
import threading
DB = []
cursor = []
msg_root_path = "./app/Database/Msg/"
lock = threading.Lock()
# misc_path = './Msg/Misc.db'
if os.path.exists(msg_root_path):
for root, dirs, files in os.walk(msg_root_path):
@ -54,18 +56,41 @@ def get_messages(username_):
return result
def get_message_by_num(username_, n):
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
limit 10
'''
result = []
try:
lock.acquire(True)
for cur in cursor:
cur = cursor[-1]
cur.execute(sql, [username_])
result_ = cur.fetchall()
result += result_
return result_
finally:
lock.release()
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))
# username = 'wxid_0o18ef858vnu22'
# result = get_messages(username)
# pprint(result)
# pprint(len(result))
result = get_message_by_num('wxid_0o18ef858vnu22', 0)
print(result)

View File

@ -1,87 +1,319 @@
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
from PIL import Image
from PyQt5 import QtGui
from PyQt5.QtCore import QSize, pyqtSignal, Qt, QThread
from PyQt5.QtGui import QPainter, QFont, QColor, QPixmap, QPolygon
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout, QSizePolicy, QVBoxLayout, QSpacerItem, \
QScrollArea, QScrollBar
class Label(QLabel):
class TextMessage(QLabel):
heightSingal = pyqtSignal(int)
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')
def __init__(self, text, is_send=False, parent=None):
super(TextMessage, self).__init__(text, parent)
self.setFont(QFont('微软雅黑', 12))
self.setWordWrap(True)
# self.adjustSize()
self.setMaximumWidth(800)
self.setMinimumWidth(100)
self.setMinimumHeight(45)
# self.resize(QSize(100,40))
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
self.adjustSize()
# self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
if is_send:
self.setAlignment(Qt.AlignCenter | Qt.AlignRight)
self.setStyleSheet(
'''
background-color:white;
border-radius:10px;
border-top: 10px solid white;
border-bottom: 10px solid white;
border-right: 10px solid white;
border-left: 10px solid white;
'''
)
else:
self.setStyleSheet(
'''
background-color:#b2e281;
border-radius:10px;
border-top: 10px solid #b2e281;
border-bottom: 10px solid #b2e281;
border-right: 10px solid #b2e281;
border-left: 10px solid #b2e281;
'''
)
w = len(text) * 16 + 30
if w < self.width():
self.setMaximumWidth(w)
def showEvent(self, event):
super(Label, self).showEvent(event)
pixmap = QtNinePatch2.createPixmapFromNinePatchImage(
self.image, self.width(), self.height())
self.setPixmap(pixmap)
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
super(TextMessage, self).paintEvent(a0)
#
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 Triangle(QLabel):
def __init__(self, Type, is_send=False, parent=None):
super().__init__(parent)
self.Type = Type
self.is_send = is_send
self.setFixedSize(6, 45)
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
super(Triangle, self).paintEvent(a0)
if self.Type == 1:
painter = QPainter(self)
triangle = QPolygon()
# print(self.width(), self.height())
if self.is_send:
painter.setPen(QColor('white'))
painter.setBrush(QColor('white'))
triangle.setPoints(0, 20, 0, 35, 6, 25)
else:
painter.setPen(QColor('#b2e281'))
painter.setBrush(QColor('#b2e281'))
triangle.setPoints(0, 25, 6, 20, 6, 35)
painter.drawPolygon(triangle)
class Notice(QLabel):
def __init__(self, text, type_=3, parent=None):
super().__init__(text, parent)
self.type_ = type_
self.setFont(QFont('微软雅黑', 12))
self.setWordWrap(True)
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.setAlignment(Qt.AlignCenter)
class Avatar(QLabel):
def __init__(self, avatar, parent=None):
super().__init__(parent)
if isinstance(avatar, str):
self.setPixmap(QPixmap(avatar).scaled(45, 45))
self.image_path = avatar
elif isinstance(avatar, QPixmap):
self.setPixmap(avatar.scaled(45, 45))
self.setFixedSize(QSize(45, 45))
class OpenImageThread(QThread):
def __init__(self, image_path):
super().__init__()
self.image_path = image_path
def run(self) -> None:
image = Image.open(self.image_path)
image.show()
class ImageMessage(QLabel):
def __init__(self, avatar, parent=None):
super().__init__(parent)
self.image = QLabel(self)
if isinstance(avatar, str):
self.setPixmap(QPixmap(avatar))
self.image_path = avatar
elif isinstance(avatar, QPixmap):
self.setPixmap(avatar)
self.setMaximumWidth(480)
self.setMaximumHeight(720)
self.setScaledContents(True)
def mousePressEvent(self, event):
if event.buttons() == Qt.LeftButton: # 左键按下
self.open_image_thread = OpenImageThread(self.image_path)
self.open_image_thread.start()
class BubbleMessage(QWidget):
def __init__(self, text, avatar, isSend=False, parent=None):
def __init__(self, str_content, avatar, Type, is_send=False, parent=None):
super().__init__(parent)
self.isSend = isSend
self.txt = text
self.isSend = is_send
# self.set
self.setStyleSheet(
'''
border:none;
'''
)
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)
layout.setSpacing(0)
layout.setContentsMargins(0, 5, 5, 5)
self.avatar = Avatar(avatar)
triangle = Triangle(Type, is_send)
if Type == 1:
self.message = TextMessage(str_content, is_send)
# self.message.setMaximumWidth(int(self.width() * 0.6))
else:
layout.addWidget(self.avatar, 0, Qt.AlignTop)
layout.addWidget(self.message)
layout.setStretch(1, 1)
self.message = ImageMessage(str_content)
# skin_aio_friend_bubble_pressed.9
'''
border-image:url(./Data/截图222.png) 20 20 20 20;
border-top: 5px transparent;
border-bottom: 5px transparent;
border-right: 5px transparent;
border-left: 5px transparent;
border-radius:10px;
'''
self.spacerItem = QSpacerItem(45 + 6, 45, QSizePolicy.Expanding, QSizePolicy.Minimum)
if is_send:
layout.addItem(self.spacerItem)
layout.addWidget(self.message, 1)
layout.addWidget(triangle, 0, Qt.AlignTop | Qt.AlignLeft)
layout.addWidget(self.avatar, 0, Qt.AlignTop | Qt.AlignLeft)
else:
layout.addWidget(self.avatar, 0, Qt.AlignTop | Qt.AlignRight)
layout.addWidget(triangle, 0, Qt.AlignTop | Qt.AlignRight)
layout.addWidget(self.message, 1)
layout.addItem(self.spacerItem)
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 ScrollAreaContent(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
# self.setStyleSheet(
# '''
# background-color:rgb(127,127,127);
# '''
# )
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
# print(self.width(),self.height())
self.setMinimumSize(self.width(), self.height())
class MainWindow(QWidget):
class ScrollArea(QScrollArea):
def __init__(self, parent=None):
super().__init__(parent)
self.setWidgetResizable(True)
self.setStyleSheet(
'''
border:none;
'''
)
def resizeEvent(self, a0: QtGui.QResizeEvent) -> None:
# return
self.widget().setMinimumSize(self.width(), self.widget().height())
self.widget().setMaximumSize(self.width(), self.widget().height())
self.widget().resize(QSize(self.width(), self.widget().height()))
#
class ScrollBar(QScrollBar):
def __init__(self):
super().__init__()
self.setStyleSheet(
'''
QScrollBar:vertical {
border-width: 0px;
border: none;
background:rgba(64, 65, 79, 0);
width:5px;
margin: 0px 0px 0px 0px;
}
QScrollBar::handle:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop: 0 #DDDDDD, stop: 0.5 #DDDDDD, stop:1 #aaaaff);
min-height: 20px;
max-height: 20px;
margin: 0 0px 0 0px;
border-radius: 2px;
}
QScrollBar::add-line:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop: 0 rgba(64, 65, 79, 0), stop: 0.5 rgba(64, 65, 79, 0), stop:1 rgba(64, 65, 79, 0));
height: 0px;
border: none;
subcontrol-position: bottom;
subcontrol-origin: margin;
}
QScrollBar::sub-line:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop: 0 rgba(64, 65, 79, 0), stop: 0.5 rgba(64, 65, 79, 0), stop:1 rgba(64, 65, 79, 0));
height: 0 px;
border: none;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::sub-page:vertical {
background: rgba(64, 65, 79, 0);
}
QScrollBar::add-page:vertical {
background: rgba(64, 65, 79, 0);
}
'''
)
class MyWidget(QWidget):
def __init__(self):
super().__init__()
self.resize(500, 200)
txt = '''在工具中单击边缘可以添加黑点,单击可以删掉黑点,拖动可以调整黑点长度。勾选等选项可以查看内容、缩放等区域右侧可预览不同拉伸情况下的效果,拖动可以调整预览的拉伸比例'''
avatar = QPixmap('Data/head.jpg').scaled(60, 60)
bubble_mesage = BubbleMessage(txt, avatar, isSend=False)
avatar = '../data/icons/default_avatar.svg'
bubble_message = BubbleMessage(txt, avatar, Type=1, is_send=False)
layout = QVBoxLayout()
bubble_mesage1 = BubbleMessage(txt, avatar, isSend=True)
layout.addWidget(bubble_mesage)
layout.addWidget(bubble_mesage1)
layout.setSpacing(0)
# 生成滚动区域
self.scrollArea = ScrollArea()
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollBar = ScrollBar()
self.scrollArea.setVerticalScrollBar(scrollBar)
# self.scrollArea.setGeometry(QRect(9, 9, 261, 211))
# 生成滚动区域的内容部署层部件
self.scrollAreaWidgetContents = ScrollAreaContent()
self.scrollAreaWidgetContents.setMinimumSize(50, 100)
# 设置滚动区域的内容部署部件为前面生成的内容部署层部件
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
layout.addWidget(self.scrollArea)
layout0 = QVBoxLayout()
layout0.setSpacing(0)
# self.scrollArea.setLayout(layout0)
self.scrollAreaWidgetContents.setLayout(layout0)
time = Notice("2023-11-17 15:44")
layout0.addWidget(time)
txt = "你说啥"
avatar_2 = '../data/icons/default_avatar.svg'
bubble_message1 = BubbleMessage(txt, avatar_2, Type=1, is_send=True)
layout0.addWidget(bubble_message)
layout0.addWidget(bubble_message1)
bubble_message2 = BubbleMessage('', avatar_2, Type=1, is_send=True)
layout0.addWidget(bubble_message2)
txt = "我啥都没说"
avatar0 = 'Data/fg1.png'
bubble_message1 = BubbleMessage("D:\Project\Python\PyQt-master\QLabel\Data\\fg1.png", avatar, Type=3,
is_send=False)
layout0.addWidget(bubble_message1)
self.spacerItem = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
layout0.addItem(self.spacerItem)
# layout.setStretch(0, 1)
self.setLayout(layout)
class Test(QWidget):
def __init__(self):
super().__init__()
layout = QVBoxLayout()
self.resize(500, 600)
w1 = MyWidget()
w2 = QLabel("nihao")
layout.addWidget(w1)
layout.addWidget(w2)
self.setLayout(layout)
if __name__ == '__main__':
app = QApplication([])
widget = Test()
# widget = MyWidget()
widget.show()
app.exec_()

View File

@ -1,2 +1,2 @@
version = '0.2.0'
version = '0.2.1'
contact = '474379264'

View File

@ -13,6 +13,7 @@ from app.Ui.Icon import Icon
class Person:
def __init__(self, wxid: str):
self.wxid = wxid
self.conRemark = data.get_conRemark(wxid)
self.nickname, self.alias = data.get_nickname(wxid)
@ -39,6 +40,33 @@ class Contact(Person):
self.bigHeadImgUrl = ''
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class MePC:
def __init__(self):
self.avatar = QPixmap(Icon.Default_avatar_path)
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 = QPixmap()
class ContactPC:
def __init__(self, contact_info: Dict):
self.wxid = contact_info.get('UserName')
@ -46,6 +74,8 @@ class ContactPC:
# Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl
self.alias = contact_info.get('Alias')
self.nickName = contact_info.get('NickName')
if not self.remark:
self.remark = self.nickName
self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
self.smallHeadImgBLOG = b''
self.avatar = QPixmap()
@ -64,3 +94,9 @@ class ContactPC:
class Group(Person):
def __init__(self, wxid: str):
super(Group, self).__init__(wxid)
if __name__ == '__main__':
p1 = MePC()
p2 = MePC()
print(p1 == p2)

View File

@ -0,0 +1 @@
from .chat_window import ChatWindow

View File

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'chatInfoUi.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")
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setSpacing(0)
self.verticalLayout.setObjectName("verticalLayout")
self.frame = QtWidgets.QFrame(Form)
self.frame.setFrameShape(QtWidgets.QFrame.NoFrame)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_reamrk = QtWidgets.QLabel(self.frame)
self.label_reamrk.setObjectName("label_reamrk")
self.horizontalLayout_2.addWidget(self.label_reamrk)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.toolButton = QtWidgets.QToolButton(self.frame)
self.toolButton.setObjectName("toolButton")
self.horizontalLayout_2.addWidget(self.toolButton)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.verticalLayout.addWidget(self.frame)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label_reamrk.setText(_translate("Form", "TextLabel"))
self.toolButton.setText(_translate("Form", "..."))

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>817</width>
<height>748</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_reamrk">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="toolButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QScrollArea" name="scrollArea">
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>797</width>
<height>700</height>
</rect>
</property>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

68
app/ui_pc/chat/chatUi.py Normal file
View File

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'chatUi.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);\n"
" ")
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"))

105
app/ui_pc/chat/chatUi.ui Normal file
View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>840</width>
<height>752</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="styleSheet">
<string notr="true">background: rgb(240, 240, 240);</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
<property name="spacing">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="lineEdit">
<property name="minimumSize">
<size>
<width>200</width>
<height>30</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background:transparent;
border-width:0;
border-style:outset;
background-color:rgb(226,226,226);
</string>
</property>
<property name="cursorMoveStyle">
<enum>Qt::VisualMoveStyle</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QListWidget" name="listWidget">
<property name="minimumSize">
<size>
<width>250</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>250</width>
<height>16777215</height>
</size>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,85 @@
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSpacerItem, QSizePolicy, QLabel, QHBoxLayout
from app.DataBase import msg
from app.components.bubble_message import BubbleMessage, ScrollBar, ScrollArea, ScrollAreaContent
from app.person import MePC
class ChatInfo(QWidget):
def __init__(self, contact, parent=None):
super().__init__(parent)
self.contact = contact
self.init_ui()
self.show_chats()
def init_ui(self):
self.label_reamrk = QLabel(self.contact.remark)
self.hBoxLayout = QHBoxLayout()
self.hBoxLayout.addWidget(self.label_reamrk)
self.vBoxLayout = QVBoxLayout()
self.vBoxLayout.setSpacing(0)
# self.vBoxLayout.addLayout(self.hBoxLayout)
self.scrollArea = ScrollArea()
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scrollBar = ScrollBar()
self.scrollArea.setVerticalScrollBar(scrollBar)
self.scrollAreaWidgetContents = ScrollAreaContent()
self.scrollAreaWidgetContents.setMinimumSize(200, 10000)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.vBoxLayout.addWidget(self.scrollArea)
self.scroolAreaLayout = QVBoxLayout()
self.scroolAreaLayout.setSpacing(0)
self.scrollAreaWidgetContents.setLayout(self.scroolAreaLayout)
def show_chats(self):
self.show_chat_thread = ShowChatThread(self.contact)
self.show_chat_thread.showSingal.connect(self.show_chat)
self.show_chat_thread.finishSingal.connect(self.show_finish)
self.show_chat_thread.start()
def show_finish(self, ok):
self.spacerItem = QSpacerItem(10, 10, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.scroolAreaLayout.addItem(self.spacerItem)
self.setLayout(self.vBoxLayout)
def show_chat(self, message):
try:
type_ = message[2]
# print(type_, type(type_))
is_send = message[4]
avatar = MePC().avatar if is_send else self.contact.avatar
if type_ == 1:
str_content = message[7]
bubble_message = BubbleMessage(
str_content,
avatar,
type_,
is_send
)
# print(str_content)
self.scroolAreaLayout.addWidget(bubble_message)
except:
print(message)
class ShowChatThread(QThread):
showSingal = pyqtSignal(tuple)
finishSingal = pyqtSignal(int)
# heightSingal = pyqtSignal(int)
def __init__(self, contact):
super().__init__()
self.wxid = contact.wxid
def run(self) -> None:
messages = msg.get_message_by_num(self.wxid, 0)
for message in messages:
self.showSingal.emit(message)
self.finishSingal.emit(1)

View File

@ -0,0 +1,115 @@
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 .chatUi import Ui_Form
from .chat_info import ChatInfo
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 ChatWindow(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_chats()
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_chats(self):
print('chat0')
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_chat)
self.show_thread.start()
self.ok_flag = True
def show_chat(self, contact):
contact_item = ContactQListWidgetItem(contact.nickName, contact.smallHeadImgUrl, contact.smallHeadImgBLOG)
self.listWidget.addItem(contact_item)
self.listWidget.setItemWidget(contact_item, contact_item.widget)
chat_info_window = ChatInfo(contact)
self.stackedWidget.addWidget(chat_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__)

View File

@ -10,14 +10,17 @@
from random import randint
from PyQt5.QtCore import *
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from app import config
from app.DataBase import msg
from app.DataBase import msg, misc
from app.Ui.Icon import Icon
from . import mainwindow
from .chat import ChatWindow
from .contact import ContactWindow
from .tool import ToolWindow
from ..person import MePC
# 美化样式表
Stylesheet = """
@ -67,6 +70,7 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.setWindowIcon(Icon.MainWindow_Icon)
self.setStyleSheet(Stylesheet)
self.listWidget.clear()
self.resize(QSize(800, 600))
# self.stackedWidget = QtWidgets.QStackedWidget(self.centralwidget)
self.action_desc.triggered.connect(self.about)
self.init_ui()
@ -79,14 +83,11 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
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)
tool_window.get_info_signal.connect(self.set_my_info)
self.chat_window = ChatWindow()
self.stackedWidget.addWidget(self.chat_window)
self.contact_window = ContactWindow()
# self.contact_window = QWidget()
self.stackedWidget.addWidget(self.contact_window)
label = QLabel('我是页面', self)
label.setAlignment(Qt.AlignCenter)
@ -104,6 +105,19 @@ class MainWinController(QMainWindow, mainwindow.Ui_MainWindow):
self.contact_window.show_contacts()
self.stackedWidget.setCurrentIndex(row)
def set_my_info(self, wxid):
self.avatar = QPixmap()
img_bytes = misc.get_avatar_buffer(wxid)
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)
me = MePC()
me.set_avatar(img_bytes)
self.myavatar.setScaledContents(True)
self.myavatar.setPixmap(self.avatar)
def about(self):
"""
关于

View File

@ -51,7 +51,9 @@ class Ui_Dialog(object):
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.setStyleSheet("\n"
" background:transparent;border-width:0;border-style:outset\n"
" ")
self.lineEdit.setFrame(False)
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 4, 1, 1, 1)
@ -62,6 +64,7 @@ class Ui_Dialog(object):
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.setMaximumSize(QtCore.QSize(200, 16777215))
self.label_key.setText("")
self.label_key.setObjectName("label_key")
self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1)
@ -90,6 +93,7 @@ class Ui_Dialog(object):
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.setMaximumSize(QtCore.QSize(200, 300))
self.label_db_dir.setText("")
self.label_db_dir.setObjectName("label_db_dir")
self.gridLayout.addWidget(self.label_db_dir, 6, 1, 1, 1)

View File

@ -117,6 +117,12 @@
</item>
<item row="5" column="1">
<widget class="QLabel" name="label_key">
<property name="maximumSize">
<size>
<width>200</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
@ -173,6 +179,12 @@
</item>
<item row="6" column="1">
<widget class="QLabel" name="label_db_dir">
<property name="maximumSize">
<size>
<width>200</width>
<height>300</height>
</size>
</property>
<property name="text">
<string/>
</property>

View File

@ -12,7 +12,7 @@ from . import decryptUi
class DecryptControl(QWidget, decryptUi.Ui_Dialog):
DecryptSignal = pyqtSignal(str)
registerSignal = pyqtSignal(str)
get_wxidSignal = pyqtSignal(str)
def __init__(self, parent=None):
super(DecryptControl, self).__init__(parent)
@ -46,6 +46,8 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
self.label_pid.setText(str(self.info['pid']))
self.label_version.setText(self.info['version'])
self.lineEdit.setFocus()
self.checkBox.setChecked(True)
self.get_wxidSignal.emit(self.info['wxid'])
if self.wx_dir and os.path.exists(os.path.join(self.wx_dir, self.info['wxid'])):
self.label_ready.setText('已就绪')
except Exception as e:
@ -59,6 +61,7 @@ class DecryptControl(QWidget, decryptUi.Ui_Dialog):
if directory:
self.label_db_dir.setText(directory)
self.wx_dir = directory
self.checkBox_2.setChecked(True)
if self.ready:
self.label_ready.setText('已就绪')

View File

@ -1,6 +1,6 @@
from random import randint
from PyQt5.QtCore import Qt
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtWidgets import QWidget, QListWidgetItem, QLabel
from .pc_decrypt import DecryptControl
@ -45,6 +45,8 @@ HistoryPanel::item:hover {
class ToolWindow(QWidget, Ui_Dialog):
get_info_signal = pyqtSignal(str)
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
@ -58,8 +60,9 @@ class ToolWindow(QWidget, Ui_Dialog):
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)
decrypt_window = DecryptControl()
decrypt_window.get_wxidSignal.connect(self.get_info_signal)
self.stackedWidget.addWidget(decrypt_window)
label = QLabel('我是页面', self)
label.setAlignment(Qt.AlignCenter)
# 设置label的背景颜色(这里随机)

View File

@ -1,11 +1,9 @@
import ctypes
import sys
import time
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import *
from app.ui_pc import mainview
from app.ui_pc.tool.pc_decrypt import pc_decrypt
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
@ -28,23 +26,6 @@ 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文件夹下")
@ -52,7 +33,5 @@ class ViewController(QWidget):
if __name__ == '__main__':
app = QApplication(sys.argv)
view = ViewController()
# view.loadPCDecryptView()
view.loadMainWinView()
# view.show_success()
view.loadPCDecryptView()
sys.exit(app.exec_())

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

BIN
doc/images/pc_contact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -1,3 +1,4 @@
# 微信数据库介绍
**这个人比较懒,还什么都没写**
**这个人比较懒,还什么都没写**

View File

@ -0,0 +1,71 @@
# 一、解密微信数据库
## 主要功能
1. 解密微信数据库
2. 查看聊天记录
3. 导出聊天记录
* CSV
* docx(待实现)
* HTML(待实现)
## 安装
```shell
git clone https://github.com/LC044/WeChatMsg
cd WeChatMsg
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
```
## 解密
<details>
解密步骤:
1. 登录微信
2. 运行程序
```shell
python decrypt_window.py
```
3. 点击获取信息
![](./images/pc_decrypt_info.png)
4. 设置微信安装路径
可以到微信->设置->文件管理查看
![](./images/setting.png)
点击**设置微信路径**按钮,选择该文件夹路径
5. 获取到密钥和微信路径之后点击开始解密
6. 解密后的数据库文件保存在./app/DataBase/Msg路径下
</details>
## 查看聊天记录
<details>
1. 运行程序
```shell
python main_pc.py
```
2. 选择联系人
<img src='./images/pc_contact.png' alt="运行图片"/>
3. 导出聊天记录
聊天记录保存在 **/data/聊天记录/** 文件夹下
<img src='./images/messages_demo.png' />
</details>

58
main_pc.py Normal file
View File

@ -0,0 +1,58 @@
import ctypes
import sys
import time
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import *
from app.ui_pc import mainview
from app.ui_pc.tool.pc_decrypt import pc_decrypt
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID("WeChatReport")
class ViewController(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('解密')
self.setWindowIcon(QIcon('./app/data/icons/logo.svg'))
self.viewMainWIn = None
self.viewDecrypt = None
def loadPCDecryptView(self):
"""
登录界面
:return:
"""
self.viewDecrypt = pc_decrypt.DecryptControl()
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文件夹下")
if __name__ == '__main__':
app = QApplication(sys.argv)
view = ViewController()
# view.loadPCDecryptView()
view.loadMainWinView()
# view.show_success()
sys.exit(app.exec_())

View File

@ -94,7 +94,8 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
随便下载一个SQLite数据库查看软件就能打开数据库例如[DB Browser for SQLite](https://sqlitebrowser.org/dl/)
[数据库功能介绍](./doc/数据库介绍.md)
* [数据库功能介绍](./doc/数据库介绍.md)
* [更多功能介绍](./doc/电脑端使用教程.md)
</details>