mirror of
https://github.com/LC044/WeChatMsg
synced 2025-02-22 19:02:17 +08:00
数据库加锁避免多线程访问报错
This commit is contained in:
parent
142a260f01
commit
fc0b1250c4
@ -4,18 +4,19 @@
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="导出所有数据库的聊天记录">
|
||||
<change afterPath="$PROJECT_DIR$/doc/images/pc_contact.png" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/doc/电脑端使用教程.md" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/main_pc.py" afterDir="false" />
|
||||
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="update readme">
|
||||
<change afterPath="$PROJECT_DIR$/app/ui_pc/chat/__init__.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_window.py" afterDir="false" />
|
||||
<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/misc.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/misc.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/components/bubble_message.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/components/bubble_message.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/tool/pc_decrypt/decryptUi.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/decryptUi.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/decryptUi.ui" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/decryptUi.ui" 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$/decrypt_window.py" beforeDir="false" afterPath="$PROJECT_DIR$/decrypt_window.py" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/doc/数据库介绍.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/数据库介绍.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/readme.md" beforeDir="false" afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app/ui_pc/contact/contactInfoUi.ui" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/contact/contactInfoUi.ui" 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$/doc/电脑端使用教程.md" beforeDir="false" afterPath="$PROJECT_DIR$/doc/电脑端使用教程.md" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@ -112,6 +113,48 @@
|
||||
</key>
|
||||
</component>
|
||||
<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="decrypt_window" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
|
||||
<module name="WeChatMsg" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
@ -133,27 +176,6 @@
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="main" 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$" />
|
||||
<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="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="" />
|
||||
@ -169,7 +191,28 @@
|
||||
<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="false" />
|
||||
<option name="EMULATE_TERMINAL" value="true" />
|
||||
<option name="MODULE_MODE" value="false" />
|
||||
<option name="REDIRECT_INPUT" value="false" />
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="mainview" type="PythonConfigurationType" factoryName="Python" 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" />
|
||||
<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/mainview.py" />
|
||||
<option name="PARAMETERS" value="" />
|
||||
<option name="SHOW_COMMAND_LINE" 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="" />
|
||||
@ -217,34 +260,13 @@
|
||||
<option name="INPUT_FILE" value="" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="test" 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$" />
|
||||
<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="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>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Python.main_pc" />
|
||||
<item itemvalue="Python.decrypt_window" />
|
||||
<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.decrypt_window" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
@ -260,13 +282,6 @@
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1672848140146</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" />
|
||||
@ -603,7 +618,14 @@
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1700147800698</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="67" />
|
||||
<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>
|
||||
<option name="localTasksCounter" value="68" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="UnknownFeatures">
|
||||
@ -652,7 +674,6 @@
|
||||
<MESSAGE value="新增PC数据库解密" />
|
||||
<MESSAGE value="main首次加载解密界面" />
|
||||
<MESSAGE value="增加日志模块" />
|
||||
<MESSAGE value="update readme" />
|
||||
<MESSAGE value="增加PC端微信解密条件的判断" />
|
||||
<MESSAGE value="删除多余的Word文件" />
|
||||
<MESSAGE value="修复无法查找wxid的bug" />
|
||||
@ -664,7 +685,8 @@
|
||||
<MESSAGE value="修复情感分析数值显示过长的bug" />
|
||||
<MESSAGE value="新增聊天记录导出csv格式" />
|
||||
<MESSAGE value="导出所有数据库的聊天记录" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="导出所有数据库的聊天记录" />
|
||||
<MESSAGE value="update readme" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="update readme" />
|
||||
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
|
||||
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
|
||||
</component>
|
||||
|
@ -1,5 +1,6 @@
|
||||
import os.path
|
||||
import sqlite3
|
||||
import time
|
||||
|
||||
DB = None
|
||||
cursor = None
|
||||
@ -25,15 +26,24 @@ 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
|
||||
'''
|
||||
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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
@ -1,9 +1,9 @@
|
||||
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
|
||||
from PyQt5.QtCore import QSize, pyqtSignal, Qt, QThread
|
||||
|
||||
|
||||
class TextMessage(QLabel):
|
||||
@ -11,13 +11,14 @@ class TextMessage(QLabel):
|
||||
|
||||
def __init__(self, text, is_send=False, parent=None):
|
||||
super(TextMessage, self).__init__(text, parent)
|
||||
self.setFont(QFont('SimSun', 15))
|
||||
self.setFont(QFont('微软雅黑', 12))
|
||||
self.setWordWrap(True)
|
||||
# self.adjustSize()
|
||||
self.setMaximumWidth(800)
|
||||
self.setMinimumWidth(100)
|
||||
self.setMinimumHeight(10)
|
||||
self.setMinimumHeight(45)
|
||||
# self.resize(QSize(100,40))
|
||||
|
||||
self.setTextInteractionFlags(Qt.TextSelectableByMouse)
|
||||
self.setSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored)
|
||||
# self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Minimum)
|
||||
@ -44,6 +45,9 @@ class TextMessage(QLabel):
|
||||
border-left: 10px solid #b2e281;
|
||||
'''
|
||||
)
|
||||
w = len(text) * 16 + 30
|
||||
if w < self.width():
|
||||
self.setMaximumWidth(w)
|
||||
|
||||
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
|
||||
super(TextMessage, self).paintEvent(a0)
|
||||
@ -58,7 +62,7 @@ class Triangle(QLabel):
|
||||
|
||||
def paintEvent(self, a0: QtGui.QPaintEvent) -> None:
|
||||
super(Triangle, self).paintEvent(a0)
|
||||
if self.Type == 3:
|
||||
if self.Type == 1:
|
||||
painter = QPainter(self)
|
||||
triangle = QPolygon()
|
||||
# print(self.width(), self.height())
|
||||
@ -90,9 +94,8 @@ class Avatar(QLabel):
|
||||
self.setPixmap(QPixmap(avatar).scaled(45, 45))
|
||||
self.image_path = avatar
|
||||
elif isinstance(avatar, QPixmap):
|
||||
self.setPixmap(avatar)
|
||||
self.setMaximumWidth(45)
|
||||
self.setMaximumHeight(45)
|
||||
self.setPixmap(avatar.scaled(45, 45))
|
||||
self.setFixedSize(QSize(45, 45))
|
||||
|
||||
|
||||
class OpenImageThread(QThread):
|
||||
@ -108,7 +111,6 @@ class OpenImageThread(QThread):
|
||||
class ImageMessage(QLabel):
|
||||
def __init__(self, avatar, parent=None):
|
||||
super().__init__(parent)
|
||||
self.image_path = './Data/head.jpg'
|
||||
self.image = QLabel(self)
|
||||
if isinstance(avatar, str):
|
||||
self.setPixmap(QPixmap(avatar))
|
||||
@ -117,6 +119,7 @@ class ImageMessage(QLabel):
|
||||
self.setPixmap(avatar)
|
||||
self.setMaximumWidth(480)
|
||||
self.setMaximumHeight(720)
|
||||
self.setScaledContents(True)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.buttons() == Qt.LeftButton: # 左键按下
|
||||
@ -139,8 +142,9 @@ class BubbleMessage(QWidget):
|
||||
layout.setContentsMargins(0, 5, 5, 5)
|
||||
self.avatar = Avatar(avatar)
|
||||
triangle = Triangle(Type, is_send)
|
||||
if Type == 3:
|
||||
if Type == 1:
|
||||
self.message = TextMessage(str_content, is_send)
|
||||
# self.message.setMaximumWidth(int(self.width() * 0.6))
|
||||
else:
|
||||
self.message = ImageMessage(str_content)
|
||||
# skin_aio_friend_bubble_pressed.9
|
||||
@ -169,6 +173,11 @@ class BubbleMessage(QWidget):
|
||||
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())
|
||||
@ -246,8 +255,8 @@ class MyWidget(QWidget):
|
||||
super().__init__()
|
||||
self.resize(500, 200)
|
||||
txt = '''在工具中单击边缘可以添加黑点,单击可以删掉黑点,拖动可以调整黑点长度。勾选等选项可以查看内容、缩放等区域右侧可预览不同拉伸情况下的效果,拖动可以调整预览的拉伸比例'''
|
||||
avatar = 'Data/head.jpg'
|
||||
bubble_message = BubbleMessage(txt, avatar, Type=3, is_send=False)
|
||||
avatar = '../data/icons/default_avatar.svg'
|
||||
bubble_message = BubbleMessage(txt, avatar, Type=1, is_send=False)
|
||||
layout = QVBoxLayout()
|
||||
layout.setSpacing(0)
|
||||
|
||||
@ -265,31 +274,44 @@ class MyWidget(QWidget):
|
||||
layout.addWidget(self.scrollArea)
|
||||
layout0 = QVBoxLayout()
|
||||
layout0.setSpacing(0)
|
||||
self.scrollArea.setLayout(layout0)
|
||||
# self.scrollArea.setLayout(layout0)
|
||||
self.scrollAreaWidgetContents.setLayout(layout0)
|
||||
|
||||
time = Notice("2023-11-17 15:44")
|
||||
layout0.addWidget(time)
|
||||
txt = "你说啥"
|
||||
avatar_2 = 'Data/fg1.png'
|
||||
bubble_message1 = BubbleMessage(txt, avatar_2, Type=3, is_send=True)
|
||||
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=3, is_send=True)
|
||||
bubble_message2 = BubbleMessage('', avatar_2, Type=1, is_send=True)
|
||||
layout0.addWidget(bubble_message2)
|
||||
txt = "我啥都没说"
|
||||
avatar0 = 'Data/fg1.png'
|
||||
bubble_message1 = BubbleMessage('Data/fg1.png', avatar, Type=1, is_send=False)
|
||||
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)
|
||||
# layout.setStretch(0, 1)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
class Test(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
layout = QVBoxLayout()
|
||||
w1 = MyWidget()
|
||||
w2 = QLabel("nihao")
|
||||
layout.addWidget(w1)
|
||||
layout.addWidget(w2)
|
||||
self.setLayout(layout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
widget = MyWidget()
|
||||
widget = Test()
|
||||
# widget = MyWidget()
|
||||
widget.show()
|
||||
app.exec_()
|
||||
|
@ -39,6 +39,42 @@ 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, 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')
|
||||
if not self.remark:
|
||||
self.remark = self.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 ContactPC:
|
||||
def __init__(self, contact_info: Dict):
|
||||
self.wxid = contact_info.get('UserName')
|
||||
@ -66,3 +102,7 @@ class ContactPC:
|
||||
class Group(Person):
|
||||
def __init__(self, wxid: str):
|
||||
super(Group, self).__init__(wxid)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pass
|
||||
|
1
app/ui_pc/chat/__init__.py
Normal file
1
app/ui_pc/chat/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .chat_window import ChatWindow
|
49
app/ui_pc/chat/chatInfoUi.py
Normal file
49
app/ui_pc/chat/chatInfoUi.py
Normal file
@ -0,0 +1,49 @@
|
||||
# -*- 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")
|
||||
Form.resize(817, 748)
|
||||
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", "..."))
|
96
app/ui_pc/chat/chatInfoUi.ui
Normal file
96
app/ui_pc/chat/chatInfoUi.ui
Normal 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>
|
78
app/ui_pc/chat/chat_info.py
Normal file
78
app/ui_pc/chat/chat_info.py
Normal file
@ -0,0 +1,78 @@
|
||||
from PyQt5.QtCore import QThread, pyqtSignal, Qt
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QSpacerItem, QSizePolicy
|
||||
|
||||
from app.DataBase import msg
|
||||
from app.components.bubble_message import BubbleMessage, ScrollBar, ScrollArea, ScrollAreaContent
|
||||
from .chatInfoUi import Ui_Form
|
||||
|
||||
|
||||
class ChatInfo(QWidget, Ui_Form):
|
||||
def __init__(self, contact, parent=None):
|
||||
super().__init__(parent)
|
||||
self.scrollArea = None
|
||||
self.setupUi(self)
|
||||
self.contact = contact
|
||||
self.init_ui()
|
||||
self.show_chats()
|
||||
|
||||
def init_ui(self):
|
||||
|
||||
self.label_reamrk.setText(self.contact.remark)
|
||||
self.scrollArea = ScrollArea()
|
||||
|
||||
self.verticalLayout_2.addWidget(self.scrollArea, 1)
|
||||
|
||||
self.scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
scrollBar = ScrollBar()
|
||||
self.scrollArea.setVerticalScrollBar(scrollBar)
|
||||
|
||||
self.scrollAreaWidgetContents = ScrollAreaContent()
|
||||
self.scrollAreaWidgetContents.setMinimumSize(200, 100)
|
||||
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
|
||||
|
||||
self.scroolAreaLayout = QVBoxLayout()
|
||||
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)
|
||||
|
||||
def show_chat(self, message):
|
||||
try:
|
||||
type_ = message[2]
|
||||
# print(type_, type(type_))
|
||||
if type_ == 1:
|
||||
str_content = message[7]
|
||||
is_send = message[4]
|
||||
bubble_message = BubbleMessage(
|
||||
str_content,
|
||||
self.contact.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)
|
115
app/ui_pc/chat/chat_window.py
Normal file
115
app/ui_pc/chat/chat_window.py
Normal 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__)
|
@ -16,6 +16,7 @@ from app import config
|
||||
from app.DataBase import msg
|
||||
from app.Ui.Icon import Icon
|
||||
from . import mainwindow
|
||||
from .chat import ChatWindow
|
||||
from .contact import ContactWindow
|
||||
from .tool import ToolWindow
|
||||
|
||||
@ -67,6 +68,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 +81,10 @@ 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)
|
||||
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)
|
||||
|
@ -37,6 +37,7 @@ pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
|
||||
4. 设置微信安装路径
|
||||
可以到微信->设置->文件管理查看
|
||||
|
||||

|
||||
|
||||
点击**设置微信路径**按钮,选择该文件夹路径
|
||||
|
Loading…
Reference in New Issue
Block a user