This commit is contained in:
睿 安
2026-01-21 16:48:36 +08:00
commit abba5cb273
246 changed files with 57473 additions and 0 deletions

45
app/ui/Icon.py Normal file
View File

@@ -0,0 +1,45 @@
from PyQt5.QtGui import QIcon
from app.resources import resource_rc
var = resource_rc.qt_resource_name
class Icon:
Default_avatar_path = ':/icons/icons/default_avatar.svg'
Default_image_path = ':/icons/icons/404.png'
logo_path = ':/icons/icons/logo99.png'
logo_ico_path = ':/icons/icons/logo3.0.ico'
MainWindow_Icon = QIcon(':/icons/icons/logo.svg')
Default_avatar = QIcon(Default_avatar_path)
Output = QIcon(':/icons/icons/output.svg')
Back = QIcon(':/icons/icons/back.svg')
ToDocx = QIcon(':/icons/icons/word.svg')
ToCSV = QIcon(':/icons/icons/csv.svg')
ToHTML = QIcon(':/icons/icons/html.svg')
ToTXT = QIcon(':/icons/icons/txt.svg')
Chat_Icon = QIcon(':/icons/icons/chat.svg')
Contact_Icon = QIcon(':/icons/icons/contact.svg')
MyInfo_Icon = QIcon(':/icons/icons/myinfo.svg')
Annual_Report_Icon = QIcon(':/icons/icons/annual_report.svg')
Analysis_Icon = QIcon(':/icons/icons/analysis.svg')
Emotion_Icon = QIcon(':/icons/icons/emotion.svg')
Search_Icon = QIcon(':/icons/icons/search.svg')
Tool_Icon = QIcon(':/icons/icons/tool.svg')
Home_Icon = QIcon(':/icons/icons/home.svg')
Help_Icon = QIcon(':/icons/icons/help.svg')
Get_info_Icon = QIcon(':/icons/icons/get_wx_info.svg')
Folder_Icon = QIcon(':/icons/icons/folder.svg')
Start_Icon = QIcon(':/icons/icons/start.svg')
Decrypt_Icon = QIcon(':/icons/icons/decrypt.svg')
Man_Icon_path = ':/icons/icons/man.svg'
Woman_Icon_path = ':/icons/icons/woman.svg'
Man_Icon = QIcon(':/icons/icons/man.svg')
Woman_Icon = QIcon(':/icons/icons/woman.svg')
Arrow_left_Icon = QIcon(':/icons/icons/arrow-left.svg')
Arrow_right_Icon = QIcon(':/icons/icons/arrow-right.svg')
Update_Icon = QIcon(':/icons/icons/update.svg')
Clear_Icon = QIcon(':/icons/icons/clear.svg')
# Man_Icon_pixmap = QPixmap(Man_Icon_path)
# Woman_Icon_pixmap = QPixmap(Woman_Icon_path)
# Logo_Icon = QIcon(':/icons/icons/logo.png')

105
app/ui/QSS/style.qss Normal file
View File

@@ -0,0 +1,105 @@
QWidget{
background: rgb(238,244,249);
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
}
QMenu::item:selected {
color: black;
background: rgb(230, 235, 240);
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
QListWidget {
min-width: 120px;
max-width: 380px;
color: black;
border:none;
}
QListWidget::item{
min-width: 80px;
max-width: 380px;
min-height: 60px;
max-height: 60px;
}
QListWidget::item:hover {
background: rgb(230, 235, 240);
}
/*被选中时的背景颜色和左边框颜色*/
QListWidget::item:selected {
background: rgb(230, 235, 240);
border-left: 2px solid rgb(62, 62, 62);
color: black;
font-weight: bold;
}
/*鼠标悬停颜色*/
HistoryPanel::item:hover {
background: rgb(52, 52, 52);
}
QCheckBox::indicator {
background: rgb(255, 255, 255);
Width:20px;
Height:20px;
border-radius: 10px
}
QCheckBox::indicator:unchecked{
Width:20px;
Height:20px;
image: url(:/icons/icons/unselect.svg);
}
QCheckBox::indicator:checked{
Width:20px;
Height:20px;
image: url(:/icons/icons/select.svg);
}
QScrollBar:vertical {
border-width: 0px;
border: none;
background:rgba(133, 135, 138, 0);
width:4px;
margin: 0px 0px 0px 0px;
}
QScrollBar::handle:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop: 0 rgb(133, 135, 138), stop: 0.5 rgb(133, 135, 138), stop:1 rgb(133, 135, 138));
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(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 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(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 0));
height: 0 px;
border: none;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::sub-page:vertical {
background: rgba(133, 135, 138, 0);
}
QScrollBar::add-page:vertical {
background: rgba(133, 135, 138, 0);
}
QProgressBar{
height:22px;
text-align:center;
font-size:14px;
color:rgb(49, 218, 27);
border-radius:11px;
background:#EBEEF5;
}
QProgressBar::chunk{
border-radius:11px;
background:qlineargradient(spread:pad,x1:0,y1:0,x2:1,y2:0,stop:0 #99ffff,stop:1 #9900ff);
}

0
app/ui/__init__.py Normal file
View File

1
app/ui/chat/__init__.py Normal file
View File

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

217
app/ui/chat/ai_chat.py Normal file
View File

@@ -0,0 +1,217 @@
import json
import sys
import time
import traceback
from urllib.parse import urljoin
import requests
from PyQt5.QtCore import QThread, pyqtSignal, QSize, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout, QApplication, QTextBrowser, QMessageBox
from app import config
from app.config import SERVER_API_URL
from app.log import logger
from app.ui.Icon import Icon
try:
from .chatInfoUi import Ui_Form
except:
from chatInfoUi import Ui_Form
from app.components.bubble_message import BubbleMessage
from app.person import Me, ContactDefault
class Message(QWidget):
def __init__(self, is_send=False, text='', parent=None):
super().__init__(parent)
self.avatar = QLabel(self)
self.textBrowser = QTextBrowser(self)
self.textBrowser.setText(text)
layout = QHBoxLayout(self)
if is_send:
pixmap = Me().avatar.scaled(45, 45)
self.textBrowser.setLayoutDirection(Qt.RightToLeft)
self.textBrowser.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.avatar.setPixmap(pixmap)
layout.addWidget(self.textBrowser)
layout.addWidget(self.avatar)
else:
pixmap = QPixmap(Icon.Default_avatar_path).scaled(45, 45)
self.avatar.setPixmap(pixmap)
layout.addWidget(self.avatar)
layout.addWidget(self.textBrowser)
# self.textBrowser.setFixedHeight(int(self.textBrowser.document().size().height()))
self.textBrowser.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.avatar.setFixedSize(QSize(60, 60))
def append(self, text: str):
self.textBrowser.insertPlainText(text)
self.textBrowser.setFixedHeight(int(self.textBrowser.document().size().height()))
class AIChat(QWidget, Ui_Form):
def __init__(self, contact, parent=None):
super().__init__(parent)
self.now_message: Message = None
self.setupUi(self)
self.last_timestamp = 0
self.last_str_time = ''
self.last_pos = 0
self.contact = contact
self.init_ui()
self.show_chats()
self.pushButton.clicked.connect(self.send_msg)
self.toolButton.clicked.connect(self.tool)
self.btn_clear.clicked.connect(self.clear_dialog)
self.btn_clear.setIcon(Icon.Clear_Icon)
def init_ui(self):
self.textEdit.installEventFilter(self)
def tool(self):
QMessageBox.information(self, "温馨提示", "暂未接入聊天数据您可进行基础的AI对话后续更新敬请期待")
def chat(self, text):
self.now_message.append(text)
self.scrollArea.verticalScrollBar().setValue(self.scrollArea.verticalScrollBar().maximum())
def send_msg(self):
msg = self.textEdit.toPlainText().strip()
self.textEdit.setText('')
if not msg:
return
print(msg)
bubble_message = BubbleMessage(
msg,
Me().avatar,
1,
True,
)
self.verticalLayout_message.addWidget(bubble_message)
message1 = Message(False)
self.verticalLayout_message.addWidget(message1)
self.show_chat_thread.msg = msg
self.now_message = message1
self.show_chat_thread.start()
self.scrollArea.verticalScrollBar().setValue(self.scrollArea.verticalScrollBar().maximum())
def clear_dialog(self):
self.show_chat_thread.history = []
while self.verticalLayout_message.count():
item = self.verticalLayout_message.takeAt(0)
widget = item.widget()
if widget is not None:
widget.deleteLater()
else:
del item
def show_chats(self):
# return
self.show_chat_thread = AIChatThread()
self.show_chat_thread.msgSignal.connect(self.chat)
def update_history_messages(self):
print('开始发送信息')
message1 = Message(False)
msg = '你好!'
self.verticalLayout_message.addWidget(message1)
self.show_chat_thread.msg = msg
self.now_message = message1
self.show_chat_thread.start()
def add_message(self, message):
print('message', message)
# self.textBrowser.append(message)
self.textBrowser.insertPlainText(message)
self.textBrowser.setFixedHeight(int(self.textBrowser.document().size().height()))
def eventFilter(self, obj, event):
if obj == self.textEdit and event.type() == event.KeyPress:
key = event.key()
if key == 16777220: # 回车键的键值
self.send_msg()
self.textEdit.setText('')
return True
return super().eventFilter(obj, event)
class AIChatThread(QThread):
msgSignal = pyqtSignal(str)
showSingal = pyqtSignal(tuple)
finishSingal = pyqtSignal(int)
msg_id = 0
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
self.msg = ''
self.history = []
def run(self) -> None:
url = urljoin(SERVER_API_URL, 'chat')
data = {
'username': Me().wxid,
'token': Me().token,
'version': config.version,
'messages': [
*self.history,
{
'role': 'user',
"content": self.msg
}
]
}
message = '''
幼儿园三班一共有35人上个月34人满勤。\n其中1月15日小明同学感冒了他的妈妈给他请了一天的病假。
'''
try:
# for s in message:
# self.msgSignal.emit(s)
# time.sleep(0.05)
# return
resp = requests.post(url, json=data, stream=True)
message = {
'role': 'user',
'content': self.msg
}
resp_message = {
'role': 'assistant',
'content': ''
}
if resp.status_code == 200:
for line in resp.iter_lines():
if line:
trunk = line.decode('utf-8')
try:
data = json.loads(trunk.strip('data: '))
answer = data.get('answer')
print(answer)
if isinstance(answer, str):
resp_message['content'] += answer
self.msgSignal.emit(answer)
except:
print(trunk)
resp_message['content'] += trunk
self.msgSignal.emit(trunk)
else:
print(resp.text)
error = resp.json().get('error')
logger.error(f'ai请求错误:{error}')
self.msgSignal.emit(error)
self.history.append(message)
self.history.append(resp_message)
except Exception as e:
error = str(e)
logger.error(f'ai请求错误:{error}{traceback.format_exc()}')
self.msgSignal.emit(error)
if __name__ == '__main__':
app = QApplication(sys.argv)
contact = ContactDefault('1')
dialog = AIChat(contact)
dialog.show()
sys.exit(app.exec_())

74
app/ui/chat/chatInfoUi.py Normal file
View File

@@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'chatInfoUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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_7 = QtWidgets.QHBoxLayout(Form)
self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout_7.setSpacing(0)
self.horizontalLayout_7.setObjectName("horizontalLayout_7")
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.btn_clear = QtWidgets.QPushButton(self.frame)
self.btn_clear.setObjectName("btn_clear")
self.horizontalLayout_2.addWidget(self.btn_clear)
self.toolButton = QtWidgets.QToolButton(self.frame)
self.toolButton.setObjectName("toolButton")
self.horizontalLayout_2.addWidget(self.toolButton)
self.verticalLayout_2.addLayout(self.horizontalLayout_2)
self.scrollArea = QtWidgets.QScrollArea(self.frame)
self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 799, 564))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.verticalLayout_message = QtWidgets.QVBoxLayout()
self.verticalLayout_message.setObjectName("verticalLayout_message")
self.verticalLayout_3.addLayout(self.verticalLayout_message)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_2.addWidget(self.scrollArea)
self.textEdit = QtWidgets.QTextEdit(self.frame)
self.textEdit.setMaximumSize(QtCore.QSize(16777215, 100))
self.textEdit.setObjectName("textEdit")
self.verticalLayout_2.addWidget(self.textEdit)
self.pushButton = QtWidgets.QPushButton(self.frame)
self.pushButton.setObjectName("pushButton")
self.verticalLayout_2.addWidget(self.pushButton)
self.horizontalLayout_7.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", "EmoAi"))
self.btn_clear.setText(_translate("Form", "清除对话"))
self.toolButton.setText(_translate("Form", "..."))
self.pushButton.setText(_translate("Form", "发送"))

66
app/ui/chat/chatUi.py Normal file
View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'chatUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(840, 752)
Form.setStyleSheet("background: rgb(240, 240, 240);")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form)
self.horizontalLayout_2.setSpacing(6)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setSpacing(6)
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("")
self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setMinimumSize(QtCore.QSize(30, 0))
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"))

188
app/ui/chat/chat_info.py Normal file
View File

@@ -0,0 +1,188 @@
import traceback
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QHBoxLayout
from app.DataBase import msg_db, hard_link_db
from app.components.bubble_message import BubbleMessage, ChatWidget, Notice
from app.person import Me
from app.util import get_abs_path
from app.util.emoji import get_emoji
class ChatInfo(QWidget):
def __init__(self, contact, parent=None):
super().__init__(parent)
self.last_timestamp = 0
self.last_str_time = ''
self.last_pos = 0
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.chat_window = ChatWidget()
self.chat_window.scrollArea.verticalScrollBar().valueChanged.connect(self.verticalScrollBar)
self.vBoxLayout.addWidget(self.chat_window)
self.setLayout(self.vBoxLayout)
def show_chats(self):
# Me().save_avatar()
# self.contact.save_avatar()
self.show_chat_thread = ShowChatThread(self.contact)
self.show_chat_thread.showSingal.connect(self.add_message)
self.show_chat_thread.finishSingal.connect(self.show_finish)
# self.show_chat_thread.start()
def show_finish(self, ok):
self.setScrollBarPos()
self.show_chat_thread.quit()
def verticalScrollBar(self, pos):
"""
滚动条到0之后自动更新聊天记录
:param pos:
:return:
"""
# print(pos)
if pos > 0:
return
# 记录当前滚动条最大值
self.last_pos = self.chat_window.verticalScrollBar().maximum()
self.update_history_messages()
def update_history_messages(self):
self.show_chat_thread.start()
def setScrollBarPos(self):
"""
将滚动条位置设置为上次看到的地方
:param pos:
:return:
"""
self.chat_window.update()
self.chat_window.show()
pos = self.chat_window.verticalScrollBar().maximum() - self.last_pos
self.chat_window.set_scroll_bar_value(pos)
def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp
return True
return False
def get_avatar_path(self, is_send, message, is_absolute_path=False) -> str:
if self.contact.is_chatroom:
avatar = message[13].smallHeadImgUrl
else:
avatar = Me().smallHeadImgUrl if is_send else self.contact.smallHeadImgUrl
if is_absolute_path:
if self.contact.is_chatroom:
# message[13].save_avatar()
avatar = message[13].avatar
else:
avatar = Me().avatar if is_send else self.contact.avatar
return avatar
def get_display_name(self, is_send, message) -> str:
if self.contact.is_chatroom:
if is_send:
display_name = Me().name
else:
display_name = message[13].remark
else:
display_name = None
return display_name
def add_message(self, message):
try:
type_ = message[2]
str_content = message[7]
str_time = message[8]
# print(type_, type(type_))
is_send = message[4]
avatar = self.get_avatar_path(is_send, message,True)
display_name = self.get_display_name(is_send, message)
timestamp = message[5]
BytesExtra = message[10]
if type_ == 1:
if self.is_5_min(timestamp):
time_message = Notice(self.last_str_time)
self.last_str_time = str_time
self.chat_window.add_message_item(time_message, 0)
bubble_message = BubbleMessage(
str_content,
avatar,
type_,
is_send,
display_name=display_name
)
self.chat_window.add_message_item(bubble_message, 0)
elif type_ == 3:
# return
if self.is_5_min(timestamp):
time_message = Notice(self.last_str_time)
self.last_str_time = str_time
self.chat_window.add_message_item(time_message, 0)
image_path = hard_link_db.get_image(content=str_content, bytesExtra=BytesExtra, up_dir=Me().wx_dir,thumb=False)
image_path = get_abs_path(image_path)
bubble_message = BubbleMessage(
image_path,
avatar,
type_,
is_send
)
self.chat_window.add_message_item(bubble_message, 0)
elif type_ == 47:
return
if self.is_5_min(timestamp):
time_message = Notice(self.last_str_time)
self.last_str_time = str_time
self.chat_window.add_message_item(time_message, 0)
image_path = get_emoji(str_content, thumb=True)
bubble_message = BubbleMessage(
image_path,
avatar,
3,
is_send
)
self.chat_window.add_message_item(bubble_message, 0)
elif type_ == 10000:
str_content = str_content.lstrip('<revokemsg>').rstrip('</revokemsg>')
message = Notice(str_content)
self.chat_window.add_message_item(message, 0)
except:
print(message)
traceback.print_exc()
class ShowChatThread(QThread):
showSingal = pyqtSignal(tuple)
finishSingal = pyqtSignal(int)
msg_id = 0
# heightSingal = pyqtSignal(int)
def __init__(self, contact):
super().__init__()
self.last_message_id = 9999999999
self.wxid = contact.wxid
def run(self) -> None:
messages = msg_db.get_message_by_num(self.wxid, self.last_message_id)
if messages:
self.last_message_id = messages[-1][0]
for message in messages:
self.showSingal.emit(message)
self.msg_id += 1
self.finishSingal.emit(1)

186
app/ui/chat/chat_window.py Normal file
View File

@@ -0,0 +1,186 @@
import shutil
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit
from app.DataBase import micro_msg_db, misc_db, msg_db, close_db
from app.components import ContactQListWidgetItem, ScrollBar
from app.person import Contact, Me
from app.ui.Icon import Icon
from app.util import search
from .ai_chat import AIChat
from .chatUi import Ui_Form
from .chat_info import ChatInfo
# 美化样式表
Stylesheet = """
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
border:none;
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
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(230, 235, 240);
border-left:none;
color: black;
font-weight: bold;
}
"""
class ChatWindow(QWidget, Ui_Form):
load_finish_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.show_thread = None
self.setupUi(self)
self.ok_flag = False
self.setStyleSheet(Stylesheet)
self.contacts = [[], []]
self.init_ui()
self.show_chats()
self.visited = set()
self.now_index = 0
def init_ui(self):
search_action = QAction(self.lineEdit)
search_action.setIcon(Icon.Search_Icon)
self.lineEdit.addAction(search_action, QLineEdit.LeadingPosition)
self.lineEdit.returnPressed.connect(self.search_contact)
self.listWidget.clear()
self.listWidget.setVerticalScrollBar(ScrollBar())
self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
self.listWidget.setCurrentRow(0)
self.stackedWidget.setCurrentIndex(0)
pixmap = QPixmap(Icon.Default_avatar_path).scaled(45, 45)
contact_item = ContactQListWidgetItem('AI小助手', '', pixmap)
self.listWidget.addItem(contact_item)
self.listWidget.setItemWidget(contact_item, contact_item.widget)
chat_info_window = AIChat(Me())
self.stackedWidget.addWidget(chat_info_window)
def show_chats(self):
# return
if self.ok_flag:
return
msg_db.init_database()
micro_msg_db.init_database()
if not msg_db.open_flag:
QMessageBox.critical(self, "错误", "数据库不存在\n请先解密数据库")
self.show_thread = ShowThread()
self.show_thread.load_finish_signal.connect(self.load_finish_signal)
self.show_thread.start()
return
self.show_thread = ShowContactThread()
self.show_thread.showSingal.connect(self.show_chat)
self.show_thread.load_finish_signal.connect(self.stop_loading)
self.show_thread.start()
self.ok_flag = True
def search_contact(self):
content = self.lineEdit.text()
if not content:
return
index = self.search_contact_index(content)
self.select_contact_by_index(index)
def search_contact_index(self, content: str) -> int:
return search.search_by_content(content, self.contacts)
def select_contact_by_index(self, index):
self.stackedWidget.setCurrentIndex(index)
self.listWidget.setCurrentRow(index)
def show_chat(self, contact):
# return
self.contacts[0].append(contact.remark)
self.contacts[1].append(contact.nickName)
contact_item = ContactQListWidgetItem(contact.remark, 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)
item = self.listWidget.item(self.now_index)
item.dis_select()
self.stackedWidget.setCurrentIndex(row)
item = self.listWidget.item(row)
item.select()
self.now_index = row
if row not in self.visited:
chat_info_window = self.stackedWidget.currentWidget()
chat_info_window.update_history_messages()
self.visited.add(row)
def stop_loading(self, a0):
# self.label.setVisible(False)
self.load_finish_signal.emit(True)
class ShowContactThread(QThread):
showSingal = pyqtSignal(Contact)
load_finish_signal = pyqtSignal(bool)
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self) -> None:
contact_info_lists = micro_msg_db.get_contact()
if not contact_info_lists:
self.load_finish_signal.emit(True)
# QMessageBox.critical(None, "错误", "数据库错误,请重启电脑后重试")
close_db()
try:
shutil.rmtree('./app/Database/Msg')
except:
pass
return
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 = Contact(contact_info)
contact.smallHeadImgBLOG = misc_db.get_avatar_buffer(contact.wxid)
contact.set_avatar(contact.smallHeadImgBLOG)
self.showSingal.emit(contact)
# pprint(contact.__dict__)
self.load_finish_signal.emit(True)
class ShowThread(QThread):
showSingal = pyqtSignal(Contact)
load_finish_signal = pyqtSignal(bool)
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self) -> None:
QThread.sleep(1)
self.load_finish_signal.emit(True)

View File

@@ -0,0 +1 @@
from .contact_window import ContactWindow

View File

@@ -0,0 +1,155 @@
from datetime import datetime
from PyQt5.QtCore import pyqtSignal, QUrl, QThread
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QWidget, QMenu, QAction, QToolButton, QMessageBox
from app.ui.Icon import Icon
from .contactInfoUi import Ui_Form
from .userinfo import userinfo
from ..menu.export_time_range import TimeRangeDialog
from ...DataBase import msg_db
from ...person import Contact, Me
from app.ui.contact.export.export_dialog import ExportDialog
class ContactInfo(QWidget, Ui_Form):
"""
显示联系人信息
"""
exitSignal = pyqtSignal()
urlSignal = pyqtSignal(QUrl)
# username = ''
def __init__(self, contact, parent=None):
super(ContactInfo, self).__init__(parent)
self.time_range = None
self.setupUi(self)
self.contact: Contact = contact
self.view_userinfo = userinfo.UserinfoController(self.contact)
self.btn_back.clicked.connect(self.back)
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.btn_report.clicked.connect(self.annual_report)
self.btn_analysis.clicked.connect(self.analysis)
self.btn_emotion.clicked.connect(self.emotionale_Analysis)
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.toTxtAct = QAction(Icon.ToTXT, '导出TXT', self)
self.toAiTxtAct = QAction(Icon.ToTXT, '导出AI对话专用TXT', self)
self.toJsonAct = QAction(Icon.ToTXT, '导出json', 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)
menu.addAction(self.toTxtAct)
menu.addAction(self.toAiTxtAct)
menu.addAction(self.toJsonAct)
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)
self.toTxtAct.triggered.connect(self.output)
self.toJsonAct.triggered.connect(self.output)
self.toAiTxtAct.triggered.connect(self.output)
def set_contact(self, contact: Contact):
self.view_userinfo.set_contact(contact)
self.contact = contact
def toolButton_show(self):
self.toolButton_output.showMenu()
def analysis(self):
# QDesktopServices.openUrl(QUrl("https://memotrace.cn/"))
self.report_thread = ReportThread(self.contact)
# self.report_thread.okSignal.connect(lambda x: QDesktopServices.openUrl(QUrl("http://127.0.0.1:21314")))
self.report_thread.start()
QDesktopServices.openUrl(QUrl(f"http://127.0.0.1:21314/charts/{self.contact.wxid}"))
def annual_report(self):
date_range = None
chat_calendar = msg_db.get_messages_calendar(self.contact.wxid)
if chat_calendar:
start_time = datetime.strptime(chat_calendar[0], "%Y-%m-%d")
end_time = datetime.strptime(chat_calendar[-1], "%Y-%m-%d")
date_range = (start_time.date(), end_time.date())
self.time_range_view = TimeRangeDialog(date_range=date_range, parent=self)
self.time_range_view.date_range_signal.connect(self.set_time_range)
self.time_range_view.show()
if 'room' in self.contact.wxid:
QMessageBox.warning(
self, '警告',
'暂不支持群组'
)
return
def set_time_range(self, time_range):
self.time_range = time_range
self.contact.save_avatar()
Me().save_avatar()
self.report_thread = ReportThread(self.contact, time_range)
self.report_thread.start()
QDesktopServices.openUrl(QUrl(f"http://127.0.0.1:21314/christmas/{self.contact.wxid}"))
def emotionale_Analysis(self):
QDesktopServices.openUrl(QUrl("https://memotrace.cn/"))
def back(self):
"""
将userinfo界面设置为可见其他界面设置为不可见
"""
return
def output(self):
"""
导出聊天记录
:return:
"""
self.stackedWidget.setCurrentWidget(self.view_userinfo)
if self.sender() == self.toDocxAct:
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='docx', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
elif self.sender() == self.toCSVAct:
# self.outputThread = Output(self.contact, type_=Output.CSV)
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='csv', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
elif self.sender() == self.toHtmlAct:
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='html', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
elif self.sender() == self.toTxtAct:
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='txt', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
elif self.sender() == self.toAiTxtAct:
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='ai_txt', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
elif self.sender() == self.toJsonAct:
dialog = ExportDialog(self.contact, title='选择导出的消息类型', file_type='json', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
class ReportThread(QThread):
okSignal = pyqtSignal(bool)
def __init__(self, contact, time_range=None):
super().__init__()
self.contact = contact
self.time_range = time_range
def run(self):
from app.web_ui import web
web.contact = self.contact
web.time_range = self.time_range
web.run(port='21314')
self.okSignal.emit(True)

View File

@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'contactInfoUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(494, 748)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setSpacing(0)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.btn_analysis = QtWidgets.QPushButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_analysis.sizePolicy().hasHeightForWidth())
self.btn_analysis.setSizePolicy(sizePolicy)
self.btn_analysis.setStyleSheet("")
self.btn_analysis.setFlat(False)
self.btn_analysis.setObjectName("btn_analysis")
self.horizontalLayout_3.addWidget(self.btn_analysis)
self.btn_emotion = QtWidgets.QPushButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_emotion.sizePolicy().hasHeightForWidth())
self.btn_emotion.setSizePolicy(sizePolicy)
self.btn_emotion.setFlat(False)
self.btn_emotion.setObjectName("btn_emotion")
self.horizontalLayout_3.addWidget(self.btn_emotion)
self.btn_report = QtWidgets.QPushButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_report.sizePolicy().hasHeightForWidth())
self.btn_report.setSizePolicy(sizePolicy)
self.btn_report.setFlat(False)
self.btn_report.setObjectName("btn_report")
self.horizontalLayout_3.addWidget(self.btn_report)
self.btn_back = QtWidgets.QPushButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.btn_back.sizePolicy().hasHeightForWidth())
self.btn_back.setSizePolicy(sizePolicy)
self.btn_back.setText("")
self.btn_back.setFlat(False)
self.btn_back.setObjectName("btn_back")
self.horizontalLayout_3.addWidget(self.btn_back)
self.toolButton_output = QtWidgets.QToolButton(Form)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.toolButton_output.sizePolicy().hasHeightForWidth())
self.toolButton_output.setSizePolicy(sizePolicy)
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(Form)
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.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.toolButton_output.setText(_translate("Form", "导出聊天记录"))

View File

@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'contactUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(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(6)
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("")
self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle)
self.lineEdit.setObjectName("lineEdit")
self.horizontalLayout.addWidget(self.lineEdit)
self.label_2 = QtWidgets.QLabel(Form)
self.label_2.setMinimumSize(QtCore.QSize(30, 0))
self.label_2.setMaximumSize(QtCore.QSize(30, 16777215))
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"))

View File

@@ -0,0 +1,196 @@
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit, QLabel
from app.DataBase import micro_msg_db, misc_db, close_db
from app.components import ContactQListWidgetItem, ScrollBar
from app.person import Contact
from app.ui.Icon import Icon
from .contactInfo import ContactInfo
from .contactUi import Ui_Form
from ...DataBase.hard_link import decodeExtraBuf
from ...util import search
# 美化样式表
Stylesheet = """
QPushButton{
background-color: transparent;
}
QPushButton:hover {
background-color: lightgray;
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
border:none;
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
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(230, 235, 240);
border-left:none;
color: black;
font-weight: bold;
}
/*鼠标悬停颜色*/
HistoryPanel::item:hover {
background: rgb(52, 52, 52);
}
"""
class ContactWindow(QWidget, Ui_Form):
load_finish_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.now_index = 0
self.show_thread = None
self.setupUi(self)
self.ok_flag = False
self.setStyleSheet(Stylesheet)
self.init_ui()
self.contacts = [[], []]
self.contacts_list:List[Contact] = []
self.show_contacts()
self.contact_info_window = None
def init_ui(self):
search_action = QAction(self.lineEdit)
search_action.setIcon(Icon.Search_Icon)
self.lineEdit.addAction(search_action, QLineEdit.LeadingPosition)
self.lineEdit.returnPressed.connect(self.search_contact)
self.listWidget.clear()
self.listWidget.setVerticalScrollBar(ScrollBar())
self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
self.listWidget.setCurrentRow(0)
self.stackedWidget.setCurrentIndex(0)
def show_contacts(self):
"""
创建一个子线程来获取联系人并通过信号传递联系人信息
@return:
"""
# return
if self.ok_flag:
return
micro_msg_db.init_database()
if not micro_msg_db.open_flag:
QMessageBox.critical(self, "错误", "数据库不存在\n请先解密数据库")
self.show_thread = ShowThread()
self.show_thread.showSingal.connect(self.show_contact)
self.show_thread.load_finish_signal.connect(self.load_finish_signal)
self.show_thread.start()
return
self.show_thread = ShowContactThread()
self.show_thread.showSingal.connect(self.show_contact)
self.show_thread.load_finish_signal.connect(self.load_finish_signal)
self.show_thread.start()
self.ok_flag = True
def search_contact(self):
"""
搜索联系人
@return:
"""
keyword = self.lineEdit.text()
if keyword:
index = search.search_by_content(keyword, self.contacts)
self.listWidget.setCurrentRow(index)
self.stackedWidget.setCurrentIndex(index)
def show_contact(self, contact: Contact):
"""
显示联系人
@param contact:联系人对象
@return:
"""
# return
self.contacts[0].append(contact.remark)
self.contacts[1].append(contact.nickName)
contact_item = ContactQListWidgetItem(contact.remark, contact.smallHeadImgUrl, contact.smallHeadImgBLOG)
self.listWidget.addItem(contact_item)
self.listWidget.setItemWidget(contact_item, contact_item.widget)
self.contacts_list.append(contact)
if self.contact_info_window is None:
self.contact_info_window = ContactInfo(contact)
self.stackedWidget.addWidget(self.contact_info_window)
def setCurrentIndex(self, row):
# print(row)
item = self.listWidget.item(self.now_index)
item.dis_select()
self.stackedWidget.setCurrentIndex(row)
item = self.listWidget.item(row)
item.select()
self.now_index = row
# self.stackedWidget.setCurrentIndex(row)
self.contact_info_window.set_contact(self.contacts_list[row])
class ShowContactThread(QThread):
showSingal = pyqtSignal(Contact)
load_finish_signal = pyqtSignal(bool)
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self) -> None:
contact_info_lists = micro_msg_db.get_contact()
if not contact_info_lists:
self.load_finish_signal.emit(True)
# QMessageBox.critical(None, "错误", "数据库错误,请重启电脑后重试")
close_db()
import shutil
try:
shutil.rmtree('./app/Database/Msg')
except:
pass
return
for contact_info_list in contact_info_lists:
# UserName, Alias,Type,Remark,NickName,PYInitial,RemarkPYInitial,ContactHeadImgUrl.smallHeadImgUrl,ContactHeadImgUrl,bigHeadImgUrl,ExtraBuf
detail = decodeExtraBuf(contact_info_list[9])
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],
'detail': detail,
'label_name': contact_info_list[10],
}
contact = Contact(contact_info)
contact.smallHeadImgBLOG = misc_db.get_avatar_buffer(contact.wxid)
contact.set_avatar(contact.smallHeadImgBLOG)
self.showSingal.emit(contact)
# pprint(contact.__dict__)
self.load_finish_signal.emit(True)
class ShowThread(QThread):
showSingal = pyqtSignal(Contact)
load_finish_signal = pyqtSignal(bool)
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self) -> None:
QThread.sleep(1)
self.load_finish_signal.emit(True)

View File

View File

@@ -0,0 +1,112 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'exportUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(553, 394)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_3 = QtWidgets.QLabel(Dialog)
self.label_3.setObjectName("label_3")
self.horizontalLayout.addWidget(self.label_3)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.btn_select_all = QtWidgets.QPushButton(Dialog)
self.btn_select_all.setObjectName("btn_select_all")
self.horizontalLayout.addWidget(self.btn_select_all)
self.comboBox_time = QtWidgets.QComboBox(Dialog)
self.comboBox_time.setObjectName("comboBox_time")
self.comboBox_time.addItem("")
self.comboBox_time.addItem("")
self.comboBox_time.addItem("")
self.horizontalLayout.addWidget(self.comboBox_time)
self.comboBox_type = QtWidgets.QComboBox(Dialog)
self.comboBox_type.setObjectName("comboBox_type")
self.comboBox_type.addItem("")
self.comboBox_type.addItem("")
self.horizontalLayout.addWidget(self.comboBox_type)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.verticalLayout_2.addWidget(self.label_2)
self.horizontalLayout_2.addLayout(self.verticalLayout_2)
self.horizontalLayout_2.setStretch(0, 1)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.textBrowser = QtWidgets.QTextBrowser(Dialog)
self.textBrowser.setObjectName("textBrowser")
self.verticalLayout.addWidget(self.textBrowser)
self.progressBar = QtWidgets.QProgressBar(Dialog)
self.progressBar.setProperty("value", 24)
self.progressBar.setObjectName("progressBar")
self.verticalLayout.addWidget(self.progressBar)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_time = QtWidgets.QLabel(Dialog)
self.label_time.setText("")
self.label_time.setObjectName("label_time")
self.horizontalLayout_3.addWidget(self.label_time)
self.label_process = QtWidgets.QLabel(Dialog)
self.label_process.setText("")
self.label_process.setObjectName("label_process")
self.horizontalLayout_3.addWidget(self.label_process)
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem1)
self.btn_start = QtWidgets.QPushButton(Dialog)
self.btn_start.setStyleSheet("QPushButton{\n"
" background-color: rgb(233,233,233);\n"
" border-radius: 5px;\n"
" padding: 10px;\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"border:1px solid rgb(31,156,220) ;\n"
"}")
self.btn_start.setObjectName("btn_start")
self.horizontalLayout_3.addWidget(self.btn_start)
self.btn_cancel = QtWidgets.QPushButton(Dialog)
self.btn_cancel.setStyleSheet("QPushButton{\n"
" background-color: rgb(233,233,233);\n"
" border-radius: 5px;\n"
" padding: 10px;\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"border:1px solid rgb(31,156,220) ;\n"
"}")
self.btn_cancel.setObjectName("btn_cancel")
self.horizontalLayout_3.addWidget(self.btn_cancel)
self.verticalLayout.addLayout(self.horizontalLayout_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_select_all.setText(_translate("Dialog", "全选"))
self.comboBox_time.setItemText(0, _translate("Dialog", "全部时间"))
self.comboBox_time.setItemText(1, _translate("Dialog", "最近三个月"))
self.comboBox_time.setItemText(2, _translate("Dialog", "自定义时间"))
self.comboBox_type.setItemText(0, _translate("Dialog", "全部聊天记录"))
self.comboBox_type.setItemText(1, _translate("Dialog", "不含图片/视频/文件"))
self.label_2.setText(_translate("Dialog", "消息类型"))
self.btn_start.setText(_translate("Dialog", "开始"))
self.btn_cancel.setText(_translate("Dialog", "取消"))

View File

@@ -0,0 +1,229 @@
import os
import sys
import time
from datetime import datetime, timedelta
from PyQt5.QtCore import QTimer, QObject, pyqtSignal
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox
from app.DataBase import msg_db
from app.components import ScrollBar
from app.config import OUTPUT_DIR
from app.ui.menu.export_time_range import TimeRangeDialog
from .exportUi import Ui_Dialog
from app.util.exporter.output import Output
types = {
'文本': 1,
'图片': 3,
'语音': 34,
'视频': 43,
'表情包': 47,
'音乐与音频': 4903,
'文件': 4906,
'分享卡片': 4905,
'转账': 492000,
'音视频通话': 50,
'拍一拍等系统消息': 10000,
}
Stylesheet = """
"""
class EmittingStr(QObject):
textWritten = pyqtSignal(str) # 定义一个发送str的信号
def write(self, text):
self.textWritten.emit(str(text))
class ExportDialog(QDialog, Ui_Dialog):
def __init__(self, contact=None, title="选择导出的类型", file_type="csv", parent=None):
super(ExportDialog, self).__init__(parent)
self.select_all_flag = False
self.setupUi(self)
self.setStyleSheet(Stylesheet)
# 下面将输出重定向到textBrowser中
sys.stdout = EmittingStr(textWritten=self.outputWritten)
sys.stderr = EmittingStr(textWritten=self.outputWritten)
scroll_bar = ScrollBar()
self.textBrowser.setVerticalScrollBar(scroll_bar)
self.contact = contact
if file_type == 'html':
self.export_type = Output.HTML
self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False,
'音乐与音频': False, '分享卡片': False, '文件': False,
'转账': False, '音视频通话': False, '拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择
elif file_type == 'csv':
self.export_type = Output.CSV
self.export_choices = {"文本": True, "图片": True, "视频": True, "表情包": True} # 定义导出的数据类型,默认全部选择
elif file_type == 'txt':
self.export_type = Output.TXT
self.export_choices = {"文本": True, "图片": True, "语音": True, "视频": True, "表情包": True,
'音乐与音频': True, '分享卡片': True, '文件': True,
'拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择
elif file_type == 'ai_txt':
self.export_type = Output.AI_TXT
self.export_choices = {"文本": True} # 定义导出的数据类型,默认全部选择
elif file_type == 'docx':
self.export_type = Output.DOCX
self.export_choices = {"文本": True, "图片": False, "语音": False, "视频": False,
"表情包": False, '拍一拍等系统消息': True} # 定义导出的数据类型,默认全部选择
elif file_type == 'json':
self.export_type = Output.JSON
self.export_choices = {} # 定义导出的数据类型,默认全部选择
else:
self.export_choices = {"文本": True, "图片": True, "视频": True, "表情包": True} # 定义导出的数据类型,默认全部选择
self.setWindowTitle(title)
self.resize(400, 300)
self.worker = None # 导出线程
for export_type, default_state in self.export_choices.items():
checkbox = QCheckBox(export_type)
checkbox.setChecked(default_state)
self.verticalLayout_2.addWidget(checkbox)
self.btn_select_all.clicked.connect(self.select_all)
self.btn_start.clicked.connect(self.export_data)
self.btn_cancel.clicked.connect(self.reject) # 使用reject关闭对话框
self.comboBox_time.activated.connect(self.set_export_date)
self.timer = QTimer(self)
self.time = 0
self.total_msg_num = 99999 # 总的消息个数
self.num = 0 # 当前完成的消息个数
self.timer.timeout.connect(self.update_elapsed_time)
self.time_range = None
def export_data(self):
self.btn_start.setEnabled(False)
self.btn_cancel.setEnabled(False)
# 在这里获取用户选择的导出数据类型
selected_types = {types[export_type]: checkbox.isChecked() for export_type, checkbox in
zip(self.export_choices.keys(), self.findChildren(QCheckBox))}
# 在这里根据用户选择的数据类型执行导出操作
print("选择的数据类型:", selected_types)
self.worker = Output(self.contact, type_=self.export_type, message_types=selected_types,
time_range=self.time_range)
self.worker.progressSignal.connect(self.update_progress)
self.worker.okSignal.connect(self.export_finished)
self.worker.rangeSignal.connect(self.set_total_msg_num)
self.worker.start()
# 启动定时器每1000毫秒更新一次任务进度
self.timer.start(1000)
self.start_time = time.time()
# self.accept() # 使用accept关闭对话框
def outputWritten(self, text):
cursor = self.textBrowser.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.textBrowser.setTextCursor(cursor)
self.textBrowser.ensureCursorVisible()
def set_export_date(self):
date_range = self.comboBox_time.currentText()
if date_range == '全部时间':
pass
elif date_range == '最近三个月':
# 获取今天的日期和时间
today = datetime.now()
# 获取今天的日期
today_date = today.date()
# 获取今天的24:00:00的时间戳
today_midnight = datetime.combine(today_date, datetime.min.time()) + timedelta(days=1)
today_midnight_timestamp = int(today_midnight.timestamp())
# 获取三个月前的日期
three_months_ago = today - timedelta(days=90)
# 获取三个月前的00:00:00的时间戳
three_months_ago_date = three_months_ago.date()
three_months_ago_midnight = datetime.combine(three_months_ago_date, datetime.min.time())
three_months_ago_midnight_timestamp = int(three_months_ago_midnight.timestamp())
self.time_range = (three_months_ago_midnight_timestamp, today_midnight_timestamp)
elif date_range == '自定义时间':
date_range = None
chat_calendar = msg_db.get_messages_calendar(self.contact.wxid)
if chat_calendar:
start_time = datetime.strptime(chat_calendar[0], "%Y-%m-%d")
end_time = datetime.strptime(chat_calendar[-1], "%Y-%m-%d")
date_range = (start_time.date(), end_time.date())
self.time_range_view = TimeRangeDialog(date_range=date_range, parent=self)
self.time_range_view.date_range_signal.connect(self.set_time_range)
self.time_range_view.show()
self.comboBox_time.setCurrentIndex(0)
# QMessageBox.warning(self,
# "别急别急",
# "马上就实现该功能"
# )
def set_time_range(self, time_range):
self.time_range = time_range
self.comboBox_time.setCurrentIndex(2)
def set_total_msg_num(self, num):
self.total_msg_num = num
# b''+num +(1,1)
def export_finished(self):
self.btn_start.setEnabled(True)
self.btn_cancel.setEnabled(True)
self.time = 0
end_time = time.time()
print(f'总耗时:{end_time - self.start_time}s')
reply = QMessageBox(self)
reply.setIcon(QMessageBox.Information)
reply.setWindowTitle('OK')
reply.setText(
f"导出聊天记录成功\n{OUTPUT_DIR}目录下(跟exe文件在一起)\n{os.path.normpath(os.path.join(os.getcwd(), OUTPUT_DIR))}")
reply.addButton("确认", QMessageBox.AcceptRole)
reply.addButton("取消", QMessageBox.RejectRole)
api = reply.exec_()
# 在任务完成时重置sys.stdout
sys.stdout = sys.__stdout__
self.accept()
def select_all(self):
self.select_all_flag = not self.select_all_flag
print('全选', self.select_all_flag)
if self.select_all_flag:
for checkbox in self.findChildren(QCheckBox):
checkbox.setChecked(True)
self.btn_select_all.setText('全不选')
else:
for checkbox in self.findChildren(QCheckBox):
checkbox.setChecked(False)
self.btn_select_all.setText('全选')
def update_elapsed_time(self):
self.time += 1
self.label_time.setText(f"耗时: {self.time}s")
def update_progress(self, progress_percentage):
self.num += 1
progress_percentage = int((self.num) / self.total_msg_num * 100)
self.progressBar.setValue(progress_percentage)
self.label_process.setText(f"导出进度: {progress_percentage}%")
def close(self):
sys.stdout = sys.__stdout__
del self.worker
super().close()
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dialog = ExportDialog()
result = dialog.exec_() # 使用exec_()获取用户的操作结果
if result == QDialog.Accepted:
print("用户点击了导出按钮")
else:
print("用户点击了取消按钮")
sys.exit(app.exec_())

View File

@@ -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 : ···
"""

View File

@@ -0,0 +1,62 @@
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import *
from app.person import Contact
from app.util.region_conversion import conversion_region_to_chinese
from .userinfoUi import Ui_Frame
from ...Icon import Icon
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.l_region.setVisible(False)
self.l_contact_label.setText(contact.label_name)
if contact.detail:
self.l_signature.setText(contact.detail.get('signature'))
self.l_tel.setText(contact.detail.get('telephone'))
region = contact.detail.get('region')
area = conversion_region_to_chinese(region)
self.l_region.setText(f'地区:{area}')
gender_code = contact.detail.get('gender')
gender = ''
pixmap =QPixmap()
if gender_code == 1:
gender = ''
pixmap = QPixmap(Icon.Man_Icon_path)
elif gender_code == 2:
gender = ''
pixmap = QPixmap(Icon.Woman_Icon_path)
self.l_gender.setPixmap(pixmap)
# self.l_gender.setText()
def set_contact(self,contact:Contact):
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.l_region.setVisible(False)
self.l_contact_label.setText(contact.label_name)
if contact.detail:
self.l_signature.setText(contact.detail.get('signature'))
self.l_tel.setText(contact.detail.get('telephone'))
region = contact.detail.get('region')
area = conversion_region_to_chinese(region)
self.l_region.setText(f'地区:{area}')
gender_code = contact.detail.get('gender')
gender = ''
pixmap = QPixmap()
if gender_code == 1:
gender = ''
pixmap = QPixmap(Icon.Man_Icon_path)
elif gender_code == 2:
gender = ''
pixmap = QPixmap(Icon.Woman_Icon_path)
self.l_gender.setPixmap(pixmap)
# self.l_gender.setText()

View File

@@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'userinfoUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(624, 720)
Frame.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor))
Frame.setMouseTracking(True)
Frame.setTabletTracking(True)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Frame)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem = QtWidgets.QSpacerItem(162, 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_2 = QtWidgets.QGridLayout()
self.gridLayout_2.setVerticalSpacing(2)
self.gridLayout_2.setObjectName("gridLayout_2")
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.setAlignment(QtCore.Qt.AlignCenter)
self.l_avatar.setObjectName("l_avatar")
self.gridLayout_2.addWidget(self.l_avatar, 0, 0, 4, 1)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
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.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_remark.setObjectName("l_remark")
self.horizontalLayout.addWidget(self.l_remark)
self.l_gender = QtWidgets.QLabel(Frame)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.l_gender.sizePolicy().hasHeightForWidth())
self.l_gender.setSizePolicy(sizePolicy)
self.l_gender.setText("")
self.l_gender.setObjectName("l_gender")
self.horizontalLayout.addWidget(self.l_gender)
self.gridLayout_2.addLayout(self.horizontalLayout, 0, 1, 1, 1)
self.l_nickname = QtWidgets.QLabel(Frame)
self.l_nickname.setMinimumSize(QtCore.QSize(0, 20))
self.l_nickname.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.l_nickname.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_nickname.setObjectName("l_nickname")
self.gridLayout_2.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, 16777215))
self.l_username.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_username.setObjectName("l_username")
self.gridLayout_2.addWidget(self.l_username, 2, 1, 1, 1)
self.l_region = QtWidgets.QLabel(Frame)
self.l_region.setText("")
self.l_region.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_region.setObjectName("l_region")
self.gridLayout_2.addWidget(self.l_region, 3, 1, 1, 1)
self.verticalLayout.addLayout(self.gridLayout_2)
spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem2)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(Frame)
self.label.setMinimumSize(QtCore.QSize(80, 0))
self.label.setMaximumSize(QtCore.QSize(100, 16777215))
font = QtGui.QFont()
font.setPointSize(15)
self.label.setFont(font)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
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("\n"
" background:transparent;border-width:0;border-style:outset\n"
" ")
self.lineEdit.setObjectName("lineEdit")
self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
self.label_4 = QtWidgets.QLabel(Frame)
self.label_4.setMinimumSize(QtCore.QSize(80, 0))
self.label_4.setMaximumSize(QtCore.QSize(100, 16777215))
font = QtGui.QFont()
font.setPointSize(15)
self.label_4.setFont(font)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 1, 0, 1, 1)
self.l_tel = QtWidgets.QLabel(Frame)
self.l_tel.setText("")
self.l_tel.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_tel.setObjectName("l_tel")
self.gridLayout.addWidget(self.l_tel, 1, 1, 1, 1)
self.label_3 = QtWidgets.QLabel(Frame)
self.label_3.setMinimumSize(QtCore.QSize(80, 0))
self.label_3.setMaximumSize(QtCore.QSize(100, 16777215))
font = QtGui.QFont()
font.setPointSize(15)
self.label_3.setFont(font)
self.label_3.setObjectName("label_3")
self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
self.l_contact_label = QtWidgets.QLabel(Frame)
self.l_contact_label.setText("")
self.l_contact_label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_contact_label.setObjectName("l_contact_label")
self.gridLayout.addWidget(self.l_contact_label, 2, 1, 1, 1)
self.label_2 = QtWidgets.QLabel(Frame)
self.label_2.setMinimumSize(QtCore.QSize(0, 0))
self.label_2.setMaximumSize(QtCore.QSize(100, 16777215))
font = QtGui.QFont()
font.setPointSize(15)
self.label_2.setFont(font)
self.label_2.setWordWrap(False)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1)
self.l_signature = QtWidgets.QLabel(Frame)
self.l_signature.setMinimumSize(QtCore.QSize(300, 0))
self.l_signature.setMaximumSize(QtCore.QSize(300, 100))
self.l_signature.setText("")
self.l_signature.setWordWrap(True)
self.l_signature.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse)
self.l_signature.setObjectName("l_signature")
self.gridLayout.addWidget(self.l_signature, 3, 1, 1, 1)
self.verticalLayout.addLayout(self.gridLayout)
spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem3)
self.horizontalLayout_2.addLayout(self.verticalLayout)
spacerItem4 = QtWidgets.QSpacerItem(162, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem4)
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", "曹雨萱"))
self.label_4.setText(_translate("Frame", "电话"))
self.label_3.setText(_translate("Frame", "标签"))
self.label_2.setText(_translate("Frame", "个性签名"))

0
app/ui/home/__init__.py Normal file
View File

View File

@@ -0,0 +1,79 @@
from PyQt5.QtCore import Qt, pyqtSignal, QThread, QUrl
from PyQt5.QtGui import QFont, QDesktopServices
from PyQt5.QtWidgets import QWidget, QMessageBox
from app.ui.Icon import Icon
from .home_windowUi import Ui_Dialog
from ...person import Me
Stylesheet = """
QPushButton{
border-radius: 5px;
padding: 8px;
border-right: 2px solid #888888; /* 按钮边框2px宽白色 */
border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */
border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */
border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */
}
QPushButton:hover {
background-color: lightgray;
}
"""
class HomeWindow(QWidget, Ui_Dialog):
load_finish_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.init_ui()
self.setStyleSheet(Stylesheet)
self.load_finish_signal.emit(True)
self.btn_report.clicked.connect(self.report)
self.btn_save.clicked.connect(self.save_info)
def init_ui(self):
self.label_wxid.setText(Me().wxid)
self.lineEdit_name.setText(Me().name)
self.lineEdit_phone.setText(Me().mobile)
def save_info(self):
if self.lineEdit_name.text():
Me().name = self.lineEdit_name.text()
else:
QMessageBox.critical(self, "错误",
"昵称不能为空")
return
if self.lineEdit_phone.text():
Me().mobile = self.lineEdit_phone.text()
else:
QMessageBox.critical(self, "错误",
"手机号不能为空")
return
Me().save_info()
QMessageBox.information(self, "修改成功",
"个人信息修改成功")
def report(self):
time_range = ['2023-01-01 00:00:00', '2024-02-10 00:00:00']
self.report_thread = ReportThread(Me(), time_range)
self.report_thread.start()
QDesktopServices.openUrl(QUrl(f"http://127.0.0.1:21314/"))
class ReportThread(QThread):
okSignal = pyqtSignal(bool)
def __init__(self, contact, time_range=None):
super().__init__()
self.contact = contact
self.time_range = time_range
def run(self):
from app.web_ui import web
web.contact = self.contact
web.time_range = self.time_range
web.run(port='21314')
self.okSignal.emit(True)

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'home_windowUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(590, 547)
font = QtGui.QFont()
font.setFamily("微软雅黑")
Dialog.setFont(font)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
spacerItem = QtWidgets.QSpacerItem(20, 157, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.btn_report = QtWidgets.QPushButton(Dialog)
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(30)
self.btn_report.setFont(font)
self.btn_report.setObjectName("btn_report")
self.verticalLayout.addWidget(self.btn_report)
self.label = QtWidgets.QLabel(Dialog)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label_4 = QtWidgets.QLabel(Dialog)
self.label_4.setObjectName("label_4")
self.gridLayout.addWidget(self.label_4, 0, 0, 1, 1)
self.lineEdit_phone = QtWidgets.QLineEdit(Dialog)
self.lineEdit_phone.setStyleSheet("background:transparent;\n"
"\n"
" border-radius:5px;\n"
" border-top: 0px solid #b2e281;\n"
" border-bottom: 2px solid black;\n"
" border-right: 0px solid #b2e281;\n"
" border-left: 0px solid #b2e281;\n"
"\n"
"\n"
" border-style:outset\n"
" ")
self.lineEdit_phone.setFrame(False)
self.lineEdit_phone.setObjectName("lineEdit_phone")
self.gridLayout.addWidget(self.lineEdit_phone, 0, 1, 1, 1)
self.lineEdit_name = QtWidgets.QLineEdit(Dialog)
self.lineEdit_name.setStyleSheet("background:transparent;\n"
"\n"
" border-radius:5px;\n"
" border-top: 0px solid #b2e281;\n"
" border-bottom: 2px solid black;\n"
" border-right: 0px solid #b2e281;\n"
" border-left: 0px solid #b2e281;\n"
"\n"
"\n"
" border-style:outset\n"
" ")
self.lineEdit_name.setFrame(False)
self.lineEdit_name.setObjectName("lineEdit_name")
self.gridLayout.addWidget(self.lineEdit_name, 1, 1, 1, 1)
self.label_wxid = QtWidgets.QLabel(Dialog)
self.label_wxid.setText("")
self.label_wxid.setObjectName("label_wxid")
self.gridLayout.addWidget(self.label_wxid, 2, 1, 1, 1)
self.label_5 = QtWidgets.QLabel(Dialog)
self.label_5.setObjectName("label_5")
self.gridLayout.addWidget(self.label_5, 1, 0, 1, 1)
self.label_6 = QtWidgets.QLabel(Dialog)
self.label_6.setObjectName("label_6")
self.gridLayout.addWidget(self.label_6, 2, 0, 1, 1)
self.verticalLayout.addLayout(self.gridLayout)
self.btn_save = QtWidgets.QPushButton(Dialog)
self.btn_save.setObjectName("btn_save")
self.verticalLayout.addWidget(self.btn_save)
self.horizontalLayout.addLayout(self.verticalLayout)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem2)
self.verticalLayout_2.addLayout(self.horizontalLayout)
spacerItem3 = QtWidgets.QSpacerItem(20, 157, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem3)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btn_report.setText(_translate("Dialog", "我的年度聊天报告"))
self.label.setText(_translate("Dialog", "修改个人信息"))
self.label_4.setText(_translate("Dialog", "手机号"))
self.label_5.setText(_translate("Dialog", "微信昵称"))
self.label_6.setText(_translate("Dialog", "wxid"))
self.btn_save.setText(_translate("Dialog", "保存"))

524
app/ui/mainview.py Normal file
View File

@@ -0,0 +1,524 @@
# -*- coding: utf-8 -*-
"""
@File : mainview.py
@Author : Shuaikang Zhou
@Time : 2022/12/13 15:07
@IDE : Pycharm
@Version : Python3.10
@comment : 主窗口
"""
import json
import os.path
import subprocess
import sys
import time
import traceback
from urllib.parse import urljoin
import requests
from PyQt5.QtCore import pyqtSignal, QThread, QSize, QUrl, Qt
from PyQt5.QtGui import QPixmap, QIcon, QDesktopServices
from PyQt5.QtWidgets import QMainWindow, QLabel, QMessageBox, QPushButton
from app.DataBase import misc_db, micro_msg_db, close_db
from app.ui.Icon import Icon
from . import mainwindow
# 不能删,删了会出错
from app.ui.chat import ChatWindow
from app.ui.contact import ContactWindow
from app.ui.tool.tool_window import ToolWindow
from app.ui.home.home_window import HomeWindow
from .menu.export import ExportDialog
from app.util.exporter.output import Output
from ..components.QCursorGif import QCursorGif
from ..config import INFO_FILE_PATH, DB_DIR, SERVER_API_URL, version
from ..log import logger
from ..person import Me
try:
from app.ui.menu.about_dialog import AboutDialog
except ModuleNotFoundError:
logger.error(f'Python版本错误:Python>=3.10,仅支持3.10、3.11、3.12')
raise ValueError('Python版本错误:Python>=3.10,仅支持3.10、3.11、3.12')
# 美化样式表
Stylesheet = """
QMessageBox QPushButton{
background-color: rgb(250,252,253);
border-radius: 5px;
padding: 8px;
border-right: 2px solid #888888; /* 按钮边框2px宽白色 */
border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */
border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */
border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */
}
QPushButton{
background-color: rgb(238,244,249);
border-radius: 5px;
padding: 8px;
}
QPushButton:hover {
background-color: lightgray;
}
QWidget{
background: rgb(238,244,249);
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
}
QMenu::item:selected {
color: black;
background: rgb(230, 235, 240);
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
QListWidget {
min-width: 120px;
max-width: 380px;
color: black;
border:none;
}
QListWidget::item{
min-width: 80px;
max-width: 380px;
min-height: 60px;
max-height: 60px;
}
QListWidget::item:hover {
background: rgb(230, 235, 240);
}
/*被选中时的背景颜色和左边框颜色*/
QListWidget::item:selected {
background: rgb(230, 235, 240);
border-left: 2px solid rgb(62, 62, 62);
color: black;
font-weight: bold;
}
/*鼠标悬停颜色*/
HistoryPanel::item:hover {
background: rgb(52, 52, 52);
}
QCheckBox::indicator {
background: rgb(255, 255, 255);
Width:20px;
Height:20px;
border-radius: 10px
}
QCheckBox::indicator:unchecked{
Width:20px;
Height:20px;
image: url(:/icons/icons/unselect.svg);
}
QCheckBox::indicator:checked{
Width:20px;
Height:20px;
image: url(:/icons/icons/select.svg);
}
QScrollBar:vertical {
border-width: 0px;
border: none;
background:rgba(133, 135, 138, 0);
width:4px;
margin: 0px 0px 0px 0px;
}
QScrollBar::handle:vertical {
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
stop: 0 rgb(133, 135, 138), stop: 0.5 rgb(133, 135, 138), stop:1 rgb(133, 135, 138));
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(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 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(133, 135, 138, 0), stop: 0.5 rgba(133, 135, 138, 0), stop:1 rgba(133, 135, 138, 0));
height: 0 px;
border: none;
subcontrol-position: top;
subcontrol-origin: margin;
}
QScrollBar::sub-page:vertical {
background: rgba(133, 135, 138, 0);
}
QScrollBar::add-page:vertical {
background: rgba(133, 135, 138, 0);
}
QProgressBar{
height:22px;
text-align:center;
font-size:14px;
color:black;
border-radius:11px;
background:#EBEEF5;
}
QProgressBar::chunk{
border-radius:11px;
background:qlineargradient(spread:pad,x1:0,y1:0,x2:1,y2:0,stop:0 #99ffff,stop:1 #9900ff);
}
QComboBox
{
border-radius:3px;
border:0px ;
padding-top: 2px;
padding-left: 2px;
}
QComboBox:disabled
{
background-color:rgba(50,50,50,200);
color:rgb(160,160,160);
}
QComboBox:hover
{
background-color:rgba(230,230,230,200);
border:1px solid rgb(31,156,220) ;
}
/*下拉箭头的边框*/
QComboBox::drop-down
{
border:none;
}
/*下拉箭头样式*/
QComboBox::down-arrow
{
right:0px;/*控制箭头左右偏移*/
width: 16px;
height: 16px;
image: url(:/icons/icons/down.svg);
}
/*下拉箭头点击样式*/
QComboBox::down-arrow:on
{
width: 16px;
height: 16px;
image: url(:/icons/icons/up.svg);
}
QLineEdit
{
background:transparent;
border-radius:10px;
border-top: 0px solid #b2e281;
border-bottom: 1px solid rgb(227,228,222);
border-right: 1px solid rgb(227,228,222);
border-left: 0px solid #b2e281;
border-style:outset;
background-color:rgb(247,248,252);
}
QLineEdit:hover
{
background-color:rgb(238,241,248);
}
"""
'''
/*点击combox的样式*/
QComboBox:on
{
border-radius:3px;
background-color:rgba(35,35,35);
font: 75 9pt "Microsoft YaHei";
color:rgb(255,255,255);
border:1px solid rgb(31,156,220) ;
}
/*下拉框的样式*/
QComboBox QAbstractItemView
{
outline: 0px solid gray; /*取消选中虚线*/
border: 1px solid rgb(31,156,220);
font: 75 9pt "Microsoft YaHei";
color: rgb(255,255,255);
background-color: rgb(45,45,45);
selection-background-color: rgb(90,90,90);
}
/*选中每一项高度*/
QComboBox QAbstractItemView::item
{
height: 25px;
}
/*选中每一项的字体颜色和背景颜色*/
QComboBox QAbstractItemView::item:selected
{
color: rgb(31,163,246);
background-color: rgb(90,90,90);
}
'''
class Avatar(QLabel):
def __init__(self, parent=None):
super().__init__(parent)
def mouseDoubleClickEvent(self, e): # 双击
super().mouseDoubleClickEvent()
QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7"))
def mousePressEvent(self, e): # 单击
super().mousePressEvent(e)
QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/7"))
class MainWinController(QMainWindow, mainwindow.Ui_MainWindow, QCursorGif):
exitSignal = pyqtSignal(bool)
okSignal = pyqtSignal(bool)
# username = ''
def __init__(self, username, parent=None):
super(MainWinController, self).__init__(parent)
self.outputThread0 = None
self.outputThread = None
self.setupUi(self)
# self.myavatar = Avatar(self)
# self.setWindowIcon(Icon.MainWindow_Icon)
pixmap = QPixmap(Icon.logo_ico_path)
icon = QIcon(pixmap)
self.setWindowIcon(icon)
self.setStyleSheet(Stylesheet)
self.listWidget.clear()
self.resize(QSize(800, 600))
self.info = QLabel(self)
self.info.setText('Tips ')
self.info.setAlignment(Qt.AlignRight)
self.statusbar.addPermanentWidget(self.info)
self.statusbar.showMessage('遇到问题可添加QQ群咨询', 5000)
self.load_flag = False
self.load_data()
self.load_num = 0
self.label = QLabel(self)
self.label.setGeometry((self.width() - 300) // 2, (self.height() - 100) // 2, 300, 100)
self.label.setPixmap(QPixmap(':/icons/icons/loading.svg'))
self.menu_output.setIcon(Icon.Output)
self.action_output_CSV.setIcon(Icon.ToCSV)
self.action_output_CSV.triggered.connect(self.output)
self.action_output_contacts.setIcon(Icon.Output)
self.action_output_contacts.triggered.connect(self.output)
self.action_batch_export.setIcon(Icon.Output)
self.action_batch_export.triggered.connect(self.output)
self.action_desc.setIcon(Icon.Help_Icon)
self.action_update.setIcon(Icon.Update_Icon)
def load_data(self, flag=True):
if os.path.exists(INFO_FILE_PATH):
with open(INFO_FILE_PATH, 'r', encoding='utf-8') as f:
dic = json.loads(f.read())
wxid = dic.get('wxid')
if wxid:
me = Me()
me.wxid = dic.get('wxid')
me.name = dic.get('name')
me.nickName = dic.get('name')
me.remark = dic.get('name')
me.mobile = dic.get('mobile')
me.wx_dir = dic.get('wx_dir')
me.token = dic.get('token')
self.set_my_info(wxid)
self.load_flag = True
else:
pass
def init_ui(self):
# 设置忙碌光标图片数组
self.initCursor([':/icons/icons/Cursors/%d.png' %
i for i in range(8)])
self.setCursorTimeout(100)
self.startBusy()
self.action_update.triggered.connect(self.update)
self.action_help_faq.triggered.connect(
lambda: QDesktopServices.openUrl(QUrl("https://memotrace.cn/doc/posts/error/")))
self.about_view = AboutDialog(main_window=self, parent=self)
self.update_thread = UpdateThread(check_time=True)
self.update_thread.updateSignal.connect(self.show_update)
self.update_thread.start()
def setCurrentIndex(self, row):
self.stackedWidget.setCurrentIndex(row)
if row == 2:
self.stackedWidget.currentWidget().show_contacts()
if row == 1:
self.stackedWidget.currentWidget().show_chats()
def setWindow(self, window):
try:
window.load_finish_signal.connect(self.loading)
except:
pass
self.stackedWidget.addWidget(window)
def set_my_info(self, wxid):
self.avatar = QPixmap()
img_bytes = misc_db.get_avatar_buffer(wxid)
if not img_bytes:
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)
contact_info_list = micro_msg_db.get_contact_by_username(wxid)
if not contact_info_list:
close_db()
import shutil
try:
shutil.rmtree(DB_DIR)
except:
pass
QMessageBox.critical(self, "数据库错误", "数据库错误请删除app文件夹后重启电脑再运行软件")
return
me = Me()
me.set_avatar(img_bytes)
me.smallHeadImgUrl = contact_info_list[7]
self.myavatar.setScaledContents(True)
self.myavatar.setPixmap(self.avatar)
def stop_loading(self, a0):
self.label.setVisible(False)
self.stopBusy()
def loading(self, a0):
self.load_num += 1
if self.load_num == 1:
self.label.clear()
self.label.hide()
self.okSignal.emit(True)
self.listWidget.setVisible(True)
self.stackedWidget.setVisible(True)
self.stopBusy()
if self.load_flag:
self.listWidget.setCurrentRow(1)
self.stackedWidget.setCurrentIndex(1)
else:
self.listWidget.setCurrentRow(0)
self.stackedWidget.setCurrentIndex(0)
def output(self):
# self.startBusy()
if self.sender() == self.action_output_CSV:
self.outputThread = Output(None, type_=Output.CSV_ALL)
self.outputThread.startSignal.connect(lambda x: self.startBusy())
self.outputThread.okSignal.connect(
lambda x: self.message('聊天记录导出成功'))
self.outputThread.start()
elif self.sender() == self.action_output_contacts:
self.outputThread = Output(None, type_=Output.CONTACT_CSV)
self.outputThread.startSignal.connect(lambda x: self.startBusy())
self.outputThread.okSignal.connect(
lambda x: self.message('联系人导出成功'))
self.outputThread.start()
elif self.sender() == self.action_batch_export:
dialog = ExportDialog(None, title='批量导出聊天记录', parent=self)
result = dialog.exec_() # 使用exec_()获取用户的操作结果
def message(self, msg):
self.stopBusy()
QMessageBox.about(self, "提醒", msg)
def update(self):
self.update_thread = UpdateThread()
self.update_thread.updateSignal.connect(self.show_update)
self.update_thread.start()
def show_update(self, update_info):
if not update_info.get('update_available'):
QMessageBox.information(self, '更新通知', "当前已是最新版本")
return
detail = f'''
当前版本:{version},最新版本:{update_info.get('latest_version')}<br>
更新内容:
{update_info.get('description')}
<br><a href='https://memotrace.cn/'>查看详情</a>
'''
# 创建一个 QMessageBox 对象
error_box = QMessageBox()
# 设置对话框的标题
error_box.setWindowTitle("更新通知")
pixmap = QPixmap(Icon.logo_ico_path)
icon = QIcon(pixmap)
error_box.setWindowIcon(icon)
# 设置对话框的文本消息
error_box.setText(detail)
# 设置对话框的图标,使用 QMessageBox.Critical 作为图标类型
error_box.setIcon(QMessageBox.Information)
# 添加一个“确定”按钮
# 添加自定义按钮
custom_button = error_box.addButton('更新', QMessageBox.ActionRole)
is_update_online = update_info.get('is_update_online')
custom_button.clicked.connect(lambda x: self.update_(update_info.get('download_url'), is_update_online))
error_box.addButton(QMessageBox.Cancel)
# 显示对话框
error_box.exec_()
def update_(self, url, is_update_online):
QDesktopServices.openUrl(QUrl("https://memotrace.cn/"))
def about(self):
"""
关于
"""
self.about_view.show()
def decrypt_success(self):
QMessageBox.about(self, "解密成功", "请重新启动")
self.close()
def closeEvent(self, event):
close_db()
def close(self) -> bool:
close_db()
self.contact_window.close()
super().close()
self.exitSignal.emit(True)
class UpdateThread(QThread):
updateSignal = pyqtSignal(dict)
def __init__(self, check_time=False):
super().__init__()
self.check_time = check_time
def run(self):
now_time = time.time()
try:
with open(INFO_FILE_PATH, "r", encoding="utf-8") as f:
data = json.load(f)
update_time = data.get('update_time')
if update_time:
if now_time - update_time < 14400 and self.check_time:
return
except:
os.makedirs(os.path.dirname(INFO_FILE_PATH), exist_ok=True)
data = {
'update_time': now_time
}
data['update_time'] = now_time
with open(INFO_FILE_PATH, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=4)
server_url = urljoin(SERVER_API_URL, 'update')
data = {'version': version}
try:
response = requests.post(server_url, json=data)
if response.status_code == 200:
update_info = response.json()
self.updateSignal.emit(update_info)
else:
print("检查更新失败")
except:
update_info = {'update_available': False}
self.updateSignal.emit(update_info)

191
app/ui/mainwindow.py Normal file
View File

@@ -0,0 +1,191 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'mainwindow.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(605, 565)
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
MainWindow.setFont(font)
MainWindow.setMouseTracking(True)
MainWindow.setStyleSheet("")
MainWindow.setIconSize(QtCore.QSize(50, 24))
MainWindow.setDockOptions(QtWidgets.QMainWindow.AllowTabbedDocks|QtWidgets.QMainWindow.AnimatedDocks)
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setMouseTracking(True)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setContentsMargins(10, 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(16777215, 16777215))
self.frame_info.setStyleSheet("")
self.frame_info.setFrameShape(QtWidgets.QFrame.NoFrame)
self.frame_info.setFrameShadow(QtWidgets.QFrame.Plain)
self.frame_info.setLineWidth(5)
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.setMinimumSize(QtCore.QSize(0, 0))
self.listWidget.setMaximumSize(QtCore.QSize(16777215, 16777215))
self.listWidget.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.listWidget.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustIgnored)
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("Microsoft YaHei UI")
font.setPointSize(15)
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, 605, 33))
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
self.menubar.setFont(font)
self.menubar.setObjectName("menubar")
self.menu_F = QtWidgets.QMenu(self.menubar)
self.menu_F.setObjectName("menu_F")
self.menu_data = QtWidgets.QMenu(self.menubar)
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
self.menu_data.setFont(font)
self.menu_data.setObjectName("menu_data")
self.menu_output = QtWidgets.QMenu(self.menu_data)
self.menu_output.setObjectName("menu_output")
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)
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(12)
font.setBold(False)
font.setWeight(50)
self.statusbar.setFont(font)
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_help_decrypt = QtWidgets.QAction(MainWindow)
font = QtGui.QFont()
font.setFamily("Microsoft YaHei UI")
self.action_help_decrypt.setFont(font)
self.action_help_decrypt.setObjectName("action_help_decrypt")
self.action_desc = QtWidgets.QAction(MainWindow)
self.action_desc.setObjectName("action_desc")
self.action_help_chat = QtWidgets.QAction(MainWindow)
self.action_help_chat.setObjectName("action_help_chat")
self.action_help_contact = QtWidgets.QAction(MainWindow)
self.action_help_contact.setObjectName("action_help_contact")
self.action_output_CSV = QtWidgets.QAction(MainWindow)
self.action_output_CSV.setObjectName("action_output_CSV")
self.action_output_contacts = QtWidgets.QAction(MainWindow)
self.action_output_contacts.setObjectName("action_output_contacts")
self.action_batch_export = QtWidgets.QAction(MainWindow)
self.action_batch_export.setObjectName("action_batch_export")
self.action_update = QtWidgets.QAction(MainWindow)
self.action_update.setObjectName("action_update")
self.action_help_faq = QtWidgets.QAction(MainWindow)
self.action_help_faq.setObjectName("action_help_faq")
self.menu_F.addSeparator()
self.menu_F.addSeparator()
self.menu_F.addAction(self.action_3)
self.menu_F.addAction(self.action_4)
self.menu_output.addAction(self.action_output_CSV)
self.menu_data.addAction(self.menu_output.menuAction())
self.menu_data.addAction(self.action_output_contacts)
self.menu_data.addAction(self.action_batch_export)
self.menu_2.addAction(self.action_help_faq)
self.menu_2.addSeparator()
self.menu_2.addAction(self.action_help_decrypt)
self.menu_2.addAction(self.action_help_chat)
self.menu_2.addAction(self.action_help_contact)
self.menu_about.addAction(self.action_update)
self.menu_about.addSeparator()
self.menu_about.addAction(self.action_desc)
self.menubar.addAction(self.menu_F.menuAction())
self.menubar.addAction(self.menu_data.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_data.setTitle(_translate("MainWindow", "数据"))
self.menu_output.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_help_decrypt.setText(_translate("MainWindow", "解密教程"))
self.action_desc.setText(_translate("MainWindow", "关于软件"))
self.action_help_chat.setText(_translate("MainWindow", "聊天相关"))
self.action_help_contact.setText(_translate("MainWindow", "好友相关"))
self.action_output_CSV.setText(_translate("MainWindow", "CSV"))
self.action_output_contacts.setText(_translate("MainWindow", "导出联系人"))
self.action_batch_export.setText(_translate("MainWindow", "批量导出"))
self.action_update.setText(_translate("MainWindow", "检查更新"))
self.action_help_faq.setText(_translate("MainWindow", "常见问题解答"))

0
app/ui/menu/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

Binary file not shown.

61
app/ui/menu/dialog.py Normal file
View File

@@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'dialog.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(553, 394)
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label_version = QtWidgets.QLabel(Dialog)
self.label_version.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_version.setAlignment(QtCore.Qt.AlignCenter)
self.label_version.setObjectName("label_version")
self.verticalLayout.addWidget(self.label_version)
self.label_logo = QtWidgets.QLabel(Dialog)
self.label_logo.setMinimumSize(QtCore.QSize(100, 100))
self.label_logo.setObjectName("label_logo")
self.verticalLayout.addWidget(self.label_logo)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.horizontalLayout.addLayout(self.verticalLayout)
self.textBrowser = QtWidgets.QTextBrowser(Dialog)
self.textBrowser.setFrameShape(QtWidgets.QFrame.NoFrame)
self.textBrowser.setObjectName("textBrowser")
self.horizontalLayout.addWidget(self.textBrowser)
self.verticalLayout_2.addLayout(self.horizontalLayout)
self.label_weixin = QtWidgets.QLabel(Dialog)
self.label_weixin.setAlignment(QtCore.Qt.AlignCenter)
self.label_weixin.setObjectName("label_weixin")
self.verticalLayout_2.addWidget(self.label_weixin)
self.buttonBox = QtWidgets.QDialogButtonBox(Dialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok)
self.buttonBox.setObjectName("buttonBox")
self.verticalLayout_2.addWidget(self.buttonBox)
self.retranslateUi(Dialog)
self.buttonBox.accepted.connect(Dialog.accept) # type: ignore
self.buttonBox.rejected.connect(Dialog.reject) # type: ignore
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label_version.setText(_translate("Dialog", "TextLabel"))
self.label_logo.setText(_translate("Dialog", "logo"))
self.label_weixin.setText(_translate("Dialog", "TextLabel"))

288
app/ui/menu/export.py Normal file
View File

@@ -0,0 +1,288 @@
import os
import sys as sys_
import time
from typing import List
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, QObject
from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox
from app.DataBase import micro_msg_db, misc_db
from app.util.exporter.output import Output
from app.components import ScrollBar
from app.components.export_contact_item import ContactQListWidgetItem
from app.config import OUTPUT_DIR
from app.person import Contact
from app.ui.menu.exportUi import Ui_Dialog
from app.ui.menu.export_time_range import TimeRangeDialog
types = {
'文本': 1,
'图片': 3,
'语音': 34,
'视频': 43,
'表情包': 47,
'音乐与音频': 4903,
'文件': 4906,
'分享卡片': 4905,
'转账': 492000,
'音视频通话': 50,
'拍一拍等系统消息': 10000,
}
file_format = {
'Docx': Output.DOCX,
'TXT': Output.TXT,
'HTML': Output.HTML,
'CSV': Output.CSV,
'JSON': Output.JSON,
}
Stylesheet = """
"""
class EmittingStr(QObject):
textWritten = pyqtSignal(str) # 定义一个发送str的信号
def write(self, text):
self.textWritten.emit(str(text))
class ExportDialog(QDialog, Ui_Dialog):
def __init__(self, contact=None, title="选择导出的类型", file_type="html", parent=None):
super(ExportDialog, self).__init__(parent)
self.setupUi(self)
self.contacts: List[Contact] = []
self.setStyleSheet(Stylesheet)
self.contact = contact
self.btn_select_all.clicked.connect(self.select_all)
self.select_all_flag = False
self.btn_start.clicked.connect(self.export_data)
self.comboBox_time.activated.connect(self.set_export_date)
# 下面将输出重定向到textBrowser中
sys_.stdout = EmittingStr(textWritten=self.outputWritten)
# sys.stderr = EmittingStr(textWritten=self.outputWritten)
scroll_bar = ScrollBar()
self.textBrowser.setVerticalScrollBar(scroll_bar)
self.export_choices = {"文本": True, "图片": True, "语音": False, "视频": False, "表情包": False,
'音乐与音频': False, '分享卡片': False, '文件': False,
'转账': False, '音视频通话': False,'拍一拍等系统消息': True} # 定义导出的数据类型
self.setWindowTitle(title)
self.checkBox_word.setEnabled(False)
self.checkBox_word.setText('Docx(暂时不可用)')
self.resize(800, 600)
self.worker = None # 导出线程
for export_type, default_state in self.export_choices.items():
checkbox = QCheckBox(export_type)
checkbox.setObjectName('message_type')
checkbox.setChecked(default_state)
self.verticalLayout_2.addWidget(checkbox)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem1)
self.timer = QTimer(self)
self.time = 0
self.total_msg_num = 99999 # 总的消息个数
self.num = 0 # 当前完成的消息个数
self.timer.timeout.connect(self.update_elapsed_time)
self.show_thread = ShowContactThread()
self.show_thread.showSingal.connect(self.show_contact)
# self.show_thread.load_finish_signal.connect(self.stop_loading)
self.show_thread.start()
self.listWidget.setVerticalScrollBar(ScrollBar())
# self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
self.listWidget.itemClicked.connect(self.setCurrentIndex)
self.visited = set()
self.now_index = 0
self.time_range = None
def set_export_date(self):
date_range = self.comboBox_time.currentText()
if date_range == '全部时间':
pass
elif date_range == '最近三个月':
from datetime import datetime, timedelta
# 获取今天的日期和时间
today = datetime.now()
# 获取今天的日期
today_date = today.date()
# 获取今天的24:00:00的时间戳
today_midnight = datetime.combine(today_date, datetime.min.time()) + timedelta(days=1)
today_midnight_timestamp = int(today_midnight.timestamp())
# 获取三个月前的日期
three_months_ago = today - timedelta(days=90)
# 获取三个月前的00:00:00的时间戳
three_months_ago_date = three_months_ago.date()
three_months_ago_midnight = datetime.combine(three_months_ago_date, datetime.min.time())
three_months_ago_midnight_timestamp = int(three_months_ago_midnight.timestamp())
self.time_range = (three_months_ago_midnight_timestamp, today_midnight_timestamp)
elif date_range == '自定义时间':
self.time_range_view = TimeRangeDialog(parent=self)
self.time_range_view.date_range_signal.connect(self.set_time_range)
self.time_range_view.show()
self.comboBox_time.setCurrentIndex(0)
# QMessageBox.warning(self,
# "别急别急",
# "马上就实现该功能"
# )
def set_time_range(self, time_range):
self.time_range = time_range
self.comboBox_time.setCurrentIndex(2)
def outputWritten(self, text):
cursor = self.textBrowser.textCursor()
cursor.movePosition(QTextCursor.End)
cursor.insertText(text)
self.textBrowser.setTextCursor(cursor)
self.textBrowser.ensureCursorVisible()
def export_data(self):
self.btn_start.setEnabled(False)
# 在这里获取用户选择的导出数据类型
selected_types = {types[export_type]: checkbox.isChecked() for export_type, checkbox in
zip(self.export_choices.keys(), self.findChildren(QCheckBox, 'message_type'))}
# 在这里根据用户选择的数据类型执行导出操作
print("选择的数据类型:", selected_types)
file_types = []
for checkbox in [self.checkBox_txt, self.checkBox_csv, self.checkBox_html, self.checkBox_word,self.checkBox_json]:
if checkbox.isChecked():
file_types.append(file_format[checkbox.text()])
select_contacts = []
count = self.listWidget.count()
# 遍历listwidget中的内容
for i in range(count):
item = self.listWidget.item(i)
if item.is_select:
select_contacts.append(self.contacts[i])
# 在这里根据用户选择的数据类型执行导出操作
print("选择的文件格式:", file_types)
self.worker = Output(select_contacts, type_=Output.Batch, message_types=selected_types, sub_type=file_types,
time_range=self.time_range)
# self.worker.progressSignal.connect(self.update_progress)
self.worker.okSignal.connect(self.export_finished)
self.worker.rangeSignal.connect(self.set_total_msg_num)
self.worker.nowContact.connect(self.update_progress)
self.worker.start()
# 启动定时器每1000毫秒更新一次任务进度
self.timer.start(1000)
self.start_time = time.time()
# self.accept() # 使用accept关闭对话框
# 绑定点击槽函数 点击显示对应item中的name
def set_total_msg_num(self, num):
self.total_msg_num = num
# b''+num +(1,1)
def setCurrentIndex(self, item):
# print(row)
# row = self.listWidget.it
# item = self.listWidget.item(row)
item.select()
item.dis_select()
# self.now_index = row
def select_all(self):
self.select_all_flag = not self.select_all_flag
print('全选', self.select_all_flag)
if self.select_all_flag:
count = self.listWidget.count()
# 遍历listwidget中的内容
for i in range(count):
item = self.listWidget.item(i)
item.force_select()
self.btn_select_all.setText('全不选')
else:
count = self.listWidget.count()
# 遍历listwidget中的内容
for i in range(count):
item = self.listWidget.item(i)
item.force_dis_select()
self.btn_select_all.setText('全选')
def export_finished(self):
self.btn_start.setEnabled(True)
self.time = 0
end_time = time.time()
print(f'总耗时:{end_time - self.start_time}s')
reply = QMessageBox(self)
reply.setIcon(QMessageBox.Information)
reply.setWindowTitle('OK')
reply.setText(f"导出聊天记录成功\n{OUTPUT_DIR}目录下(跟exe文件在一起)\n{os.path.normpath(os.path.join(os.getcwd(), OUTPUT_DIR))}")
reply.addButton("确认", QMessageBox.AcceptRole)
reply.addButton("取消", QMessageBox.RejectRole)
api = reply.exec_()
# 在任务完成时重置sys.stdout
sys_.stdout = sys_.__stdout__
self.accept()
def close(self):
sys_.stdout = sys_.__stdout__
del self.worker
super().close()
def show_contact(self, contact):
# return
# print(contact.remark)
contact_item = ContactQListWidgetItem(contact.remark, contact.smallHeadImgUrl, contact.smallHeadImgBLOG)
self.listWidget.addItem(contact_item)
self.listWidget.setItemWidget(contact_item, contact_item.widget)
self.contacts.append(contact)
def update_elapsed_time(self):
self.time += 1
self.label_time.setText(f"耗时: {self.time}s")
def update_progress(self, remark):
self.num += 1
progress_percentage = int((self.num) / self.total_msg_num * 100)
self.progressBar.setValue(progress_percentage)
self.label_process.setText(f"导出进度: {progress_percentage}% {remark}")
class ShowContactThread(QThread):
showSingal = pyqtSignal(Contact)
load_finish_signal = pyqtSignal(bool)
# heightSingal = pyqtSignal(int)
def __init__(self):
super().__init__()
def run(self) -> None:
contact_info_lists = micro_msg_db.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 = Contact(contact_info)
contact.smallHeadImgBLOG = misc_db.get_avatar_buffer(contact.wxid)
contact.set_avatar(contact.smallHeadImgBLOG)
self.showSingal.emit(contact)
# print(contact.wxid)
# pprint(contact.__dict__)
self.load_finish_signal.emit(True)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
dialog = ExportDialog()
result = dialog.exec_() # 使用exec_()获取用户的操作结果
if result == QDialog.Accepted:
print("用户点击了导出按钮")
else:
print("用户点击了取消按钮")
sys.exit(app.exec_())

166
app/ui/menu/exportUi.py Normal file
View File

@@ -0,0 +1,166 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'exportUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(791, 599)
self.verticalLayout_3 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_3 = QtWidgets.QLabel(Dialog)
self.label_3.setObjectName("label_3")
self.horizontalLayout.addWidget(self.label_3)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.btn_select_all = QtWidgets.QPushButton(Dialog)
self.btn_select_all.setObjectName("btn_select_all")
self.horizontalLayout.addWidget(self.btn_select_all)
self.comboBox_time = QtWidgets.QComboBox(Dialog)
self.comboBox_time.setObjectName("comboBox_time")
self.comboBox_time.addItem("")
self.comboBox_time.addItem("")
self.comboBox_time.addItem("")
self.horizontalLayout.addWidget(self.comboBox_time)
self.comboBox_type = QtWidgets.QComboBox(Dialog)
self.comboBox_type.setObjectName("comboBox_type")
self.comboBox_type.addItem("")
self.comboBox_type.addItem("")
self.horizontalLayout.addWidget(self.comboBox_type)
self.verticalLayout_3.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label.sizePolicy().hasHeightForWidth())
self.label.setSizePolicy(sizePolicy)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.checkBox_word = QtWidgets.QCheckBox(Dialog)
self.checkBox_word.setObjectName("checkBox_word")
self.verticalLayout.addWidget(self.checkBox_word)
self.checkBox_html = QtWidgets.QCheckBox(Dialog)
self.checkBox_html.setObjectName("checkBox_html")
self.verticalLayout.addWidget(self.checkBox_html)
self.checkBox_csv = QtWidgets.QCheckBox(Dialog)
self.checkBox_csv.setObjectName("checkBox_csv")
self.verticalLayout.addWidget(self.checkBox_csv)
self.checkBox_txt = QtWidgets.QCheckBox(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.checkBox_txt.sizePolicy().hasHeightForWidth())
self.checkBox_txt.setSizePolicy(sizePolicy)
self.checkBox_txt.setChecked(True)
self.checkBox_txt.setObjectName("checkBox_txt")
self.verticalLayout.addWidget(self.checkBox_txt)
self.checkBox_json = QtWidgets.QCheckBox(Dialog)
self.checkBox_json.setObjectName("checkBox_json")
self.verticalLayout.addWidget(self.checkBox_json)
self.label_2 = QtWidgets.QLabel(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_2.sizePolicy().hasHeightForWidth())
self.label_2.setSizePolicy(sizePolicy)
self.label_2.setObjectName("label_2")
self.verticalLayout.addWidget(self.label_2)
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setSizeConstraint(QtWidgets.QLayout.SetMinimumSize)
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.verticalLayout.addLayout(self.verticalLayout_2)
spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem1)
self.horizontalLayout_2.addLayout(self.verticalLayout)
self.listWidget = QtWidgets.QListWidget(Dialog)
self.listWidget.setMinimumSize(QtCore.QSize(0, 0))
self.listWidget.setObjectName("listWidget")
self.horizontalLayout_2.addWidget(self.listWidget)
self.horizontalLayout_2.setStretch(0, 1)
self.horizontalLayout_2.setStretch(1, 1)
self.verticalLayout_3.addLayout(self.horizontalLayout_2)
self.textBrowser = QtWidgets.QTextBrowser(Dialog)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.textBrowser.sizePolicy().hasHeightForWidth())
self.textBrowser.setSizePolicy(sizePolicy)
self.textBrowser.setMaximumSize(QtCore.QSize(16777215, 80))
self.textBrowser.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.textBrowser.setTabStopWidth(0)
self.textBrowser.setOpenExternalLinks(False)
self.textBrowser.setObjectName("textBrowser")
self.verticalLayout_3.addWidget(self.textBrowser)
self.progressBar = QtWidgets.QProgressBar(Dialog)
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.verticalLayout_3.addWidget(self.progressBar)
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.verticalLayout_3.addLayout(self.horizontalLayout_4)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_time = QtWidgets.QLabel(Dialog)
self.label_time.setText("")
self.label_time.setObjectName("label_time")
self.horizontalLayout_3.addWidget(self.label_time)
self.label_process = QtWidgets.QLabel(Dialog)
self.label_process.setText("")
self.label_process.setObjectName("label_process")
self.horizontalLayout_3.addWidget(self.label_process)
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem2)
self.btn_start = QtWidgets.QPushButton(Dialog)
self.btn_start.setStyleSheet("QPushButton{\n"
" background-color: rgb(233,233,233);\n"
" border-radius: 5px;\n"
" padding: 10px;\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"border:1px solid rgb(31,156,220) ;\n"
"}")
self.btn_start.setAutoDefault(False)
self.btn_start.setDefault(False)
self.btn_start.setFlat(False)
self.btn_start.setObjectName("btn_start")
self.horizontalLayout_3.addWidget(self.btn_start)
self.verticalLayout_3.addLayout(self.horizontalLayout_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_select_all.setText(_translate("Dialog", "全选"))
self.comboBox_time.setItemText(0, _translate("Dialog", "全部时间"))
self.comboBox_time.setItemText(1, _translate("Dialog", "最近三个月"))
self.comboBox_time.setItemText(2, _translate("Dialog", "自定义时间"))
self.comboBox_type.setItemText(0, _translate("Dialog", "全部聊天记录"))
self.comboBox_type.setItemText(1, _translate("Dialog", "不含图片/视频/文件"))
self.label.setText(_translate("Dialog", "导出格式"))
self.checkBox_word.setText(_translate("Dialog", "Docx"))
self.checkBox_html.setText(_translate("Dialog", "HTML"))
self.checkBox_csv.setText(_translate("Dialog", "CSV"))
self.checkBox_txt.setText(_translate("Dialog", "TXT"))
self.checkBox_json.setText(_translate("Dialog", "JSON"))
self.label_2.setText(_translate("Dialog", "消息类型"))
self.btn_start.setText(_translate("Dialog", "开始"))

View File

@@ -0,0 +1,109 @@
from datetime import datetime
from PyQt5 import QtWidgets
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt, QPoint
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QApplication, QDialog, QCheckBox, QMessageBox, QCalendarWidget, QWidget, QVBoxLayout, QLabel
from app.components.calendar_dialog import CalendarDialog
try:
from .time_range import Ui_Dialog
except:
from time_range import Ui_Dialog
Stylesheet = '''
QToolButton{
color:#000000;
}
QCalendarWidget QAbstractItemView:disabled {
}
QCalendarWidget QAbstractItemView:enabled{
}
'''
class TimeRangeDialog(QDialog, Ui_Dialog):
date_range_signal = pyqtSignal(tuple)
def __init__(self, date_range=None, parent=None):
"""
@param date_range: tuple[Union[QDate, datetime.date],Union[QDate, datetime.date]] 限定的可选择范围
@param parent:
"""
super().__init__(parent)
self.calendar = None
self.setupUi(self)
self.setWindowTitle('选择日期范围')
# self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.FramelessWindowHint)
self.setStyleSheet(Stylesheet)
self.toolButton_start_time.clicked.connect(self.select_date_start)
self.toolButton_end_time.clicked.connect(self.select_date_end)
self.calendar = CalendarDialog(date_range=date_range, parent=self)
self.calendar.selected_date_signal.connect(self.set_date)
self.btn_ok.clicked.connect(self.ok)
self.btn_ok.setEnabled(False)
self.btn_ok_num = 0
self.btn_cancel.clicked.connect(lambda x: self.close())
self.start_time_flag = True
self.start_timestamp = 0
self.end_timestamp = 0
def set_date(self, timestamp):
if self.start_time_flag:
self.start_timestamp = timestamp
date_object = datetime.fromtimestamp(timestamp)
str_start = date_object.strftime("%Y-%m-%d")
self.toolButton_start_time.setText(str_start)
self.btn_ok_num += 1
else:
date_object = datetime.fromtimestamp(timestamp)
str_start = date_object.strftime("%Y-%m-%d")
self.end_timestamp = timestamp + 86399
self.toolButton_end_time.setText(str_start)
self.btn_ok_num += 1
if self.btn_ok_num == 2:
self.btn_ok.setEnabled(True)
def ok(self):
date_range = (self.start_timestamp, self.end_timestamp)
self.date_range_signal.emit(date_range)
self.close()
def select_date_start(self):
self.start_time_flag = True
self.calendar.set_start_date()
self.calendar.show()
def select_date_end(self):
self.start_time_flag = False
self.calendar.set_end_date()
self.calendar.show()
def mouseMoveEvent(self, e: QMouseEvent): # 重写移动事件
if self._tracking:
self._endPos = e.pos() - self._startPos
self.move(self.pos() + self._endPos)
def mousePressEvent(self, e: QMouseEvent):
if e.button() == Qt.LeftButton:
self._startPos = QPoint(e.x(), e.y())
self._tracking = True
def mouseReleaseEvent(self, e: QMouseEvent):
if e.button() == Qt.LeftButton:
self._tracking = False
self._startPos = None
self._endPos = None
if __name__ == '__main__':
import sys
from datetime import datetime
app = QApplication(sys.argv)
# 设置日期范围
start_date = datetime(2023, 12, 11)
end_date = datetime(2024, 1, 9)
date_range = (start_date.date(), end_date.date())
ex = TimeRangeDialog(date_range=date_range)
ex.show()
sys.exit(app.exec_())

73
app/ui/menu/time_range.py Normal file
View File

@@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'time_range.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(280, 200)
Dialog.setMinimumSize(QtCore.QSize(280, 200))
Dialog.setMaximumSize(QtCore.QSize(280, 200))
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(Dialog)
self.label.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label.setAlignment(QtCore.Qt.AlignCenter)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.toolButton_start_time = QtWidgets.QToolButton(Dialog)
self.toolButton_start_time.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.toolButton_start_time.setArrowType(QtCore.Qt.DownArrow)
self.toolButton_start_time.setObjectName("toolButton_start_time")
self.horizontalLayout_2.addWidget(self.toolButton_start_time)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.label_3 = QtWidgets.QLabel(Dialog)
self.label_3.setObjectName("label_3")
self.horizontalLayout.addWidget(self.label_3)
self.toolButton_end_time = QtWidgets.QToolButton(Dialog)
self.toolButton_end_time.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
self.toolButton_end_time.setArrowType(QtCore.Qt.DownArrow)
self.toolButton_end_time.setObjectName("toolButton_end_time")
self.horizontalLayout.addWidget(self.toolButton_end_time)
self.verticalLayout.addLayout(self.horizontalLayout)
spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout.addItem(spacerItem)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.btn_ok = QtWidgets.QPushButton(Dialog)
self.btn_ok.setObjectName("btn_ok")
self.horizontalLayout_3.addWidget(self.btn_ok)
self.btn_cancel = QtWidgets.QPushButton(Dialog)
self.btn_cancel.setObjectName("btn_cancel")
self.horizontalLayout_3.addWidget(self.btn_cancel)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.label.setText(_translate("Dialog", "自定义时间"))
self.label_2.setText(_translate("Dialog", "开始日期"))
self.toolButton_start_time.setText(_translate("Dialog", "请选择时间"))
self.label_3.setText(_translate("Dialog", "结束日期"))
self.toolButton_end_time.setText(_translate("Dialog", "请选择时间"))
self.btn_ok.setText(_translate("Dialog", "确定"))
self.btn_cancel.setText(_translate("Dialog", "取消"))

1
app/ui/tool/__init__.py Normal file
View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,206 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'getBiasAddrUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(650, 580)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.scrollArea = QtWidgets.QScrollArea(Form)
self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 632, 562))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.label_4 = QtWidgets.QLabel(self.scrollAreaWidgetContents)
self.label_4.setMaximumSize(QtCore.QSize(16777215, 20))
self.label_4.setAlignment(QtCore.Qt.AlignCenter)
self.label_4.setObjectName("label_4")
self.verticalLayout_3.addWidget(self.label_4)
self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.label = QtWidgets.QLabel(self.widget)
self.label.setMinimumSize(QtCore.QSize(80, 0))
self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.lineEdit_tel = QtWidgets.QLineEdit(self.widget)
self.lineEdit_tel.setObjectName("lineEdit_tel")
self.horizontalLayout.addWidget(self.lineEdit_tel)
self.verticalLayout_3.addWidget(self.widget)
self.widget_3 = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget_3.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget_3.setObjectName("widget_3")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.widget_3)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.label_2 = QtWidgets.QLabel(self.widget_3)
self.label_2.setMinimumSize(QtCore.QSize(80, 0))
self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_2.setObjectName("label_2")
self.horizontalLayout_3.addWidget(self.label_2)
self.lineEdit_wx_alias = QtWidgets.QLineEdit(self.widget_3)
self.lineEdit_wx_alias.setObjectName("lineEdit_wx_alias")
self.horizontalLayout_3.addWidget(self.lineEdit_wx_alias)
self.verticalLayout_3.addWidget(self.widget_3)
self.widget_4 = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget_4.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget_4.setObjectName("widget_4")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.widget_4)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.label_3 = QtWidgets.QLabel(self.widget_4)
self.label_3.setMinimumSize(QtCore.QSize(80, 0))
self.label_3.setLayoutDirection(QtCore.Qt.LeftToRight)
self.label_3.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_3.setObjectName("label_3")
self.horizontalLayout_4.addWidget(self.label_3)
self.lineEdit_wx_name = QtWidgets.QLineEdit(self.widget_4)
self.lineEdit_wx_name.setObjectName("lineEdit_wx_name")
self.horizontalLayout_4.addWidget(self.lineEdit_wx_name)
self.verticalLayout_3.addWidget(self.widget_4)
self.btn_get_bias_addr = QtWidgets.QPushButton(self.scrollAreaWidgetContents)
self.btn_get_bias_addr.setObjectName("btn_get_bias_addr")
self.verticalLayout_3.addWidget(self.btn_get_bias_addr)
self.widget_2 = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget_2.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget_2.setObjectName("widget_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget_2)
self.horizontalLayout_2.setContentsMargins(9, -1, -1, -1)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.commandLinkButton = QtWidgets.QCommandLinkButton(self.widget_2)
self.commandLinkButton.setEnabled(True)
self.commandLinkButton.setTabletTracking(False)
self.commandLinkButton.setFocusPolicy(QtCore.Qt.StrongFocus)
self.commandLinkButton.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.commandLinkButton.setToolTipDuration(-1)
self.commandLinkButton.setLayoutDirection(QtCore.Qt.LeftToRight)
self.commandLinkButton.setAutoFillBackground(False)
self.commandLinkButton.setCheckable(False)
self.commandLinkButton.setChecked(False)
self.commandLinkButton.setAutoRepeat(False)
self.commandLinkButton.setAutoExclusive(False)
self.commandLinkButton.setAutoDefault(False)
self.commandLinkButton.setDefault(False)
self.commandLinkButton.setObjectName("commandLinkButton")
self.horizontalLayout_2.addWidget(self.commandLinkButton)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.label_error_log = QtWidgets.QLabel(self.widget_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_error_log.sizePolicy().hasHeightForWidth())
self.label_error_log.setSizePolicy(sizePolicy)
self.label_error_log.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_error_log.setObjectName("label_error_log")
self.horizontalLayout_2.addWidget(self.label_error_log)
self.checkBox_send_error_log = QtWidgets.QCheckBox(self.widget_2)
self.checkBox_send_error_log.setText("")
self.checkBox_send_error_log.setIconSize(QtCore.QSize(64, 64))
self.checkBox_send_error_log.setChecked(True)
self.checkBox_send_error_log.setObjectName("checkBox_send_error_log")
self.horizontalLayout_2.addWidget(self.checkBox_send_error_log)
self.verticalLayout_3.addWidget(self.widget_2)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout.addWidget(self.scrollArea)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label_4.setText(_translate("Form", "为避免输入错误,下面信息请从微信里复制"))
self.label.setText(_translate("Form", "手机号:"))
self.lineEdit_tel.setPlaceholderText(_translate("Form", "填入微信绑定的手机号"))
self.label_2.setText(_translate("Form", "微信号:"))
self.lineEdit_wx_alias.setPlaceholderText(_translate("Form", "填入您的微信号"))
self.label_3.setText(_translate("Form", "微信昵称:"))
self.lineEdit_wx_name.setPlaceholderText(_translate("Form", "填入您的微信昵称"))
self.btn_get_bias_addr.setText(_translate("Form", "获取信息"))
self.commandLinkButton.setText(_translate("Form", "收集版本信息"))
self.commandLinkButton.setDescription(_translate("Form", "需要收集微信版本信息以支持更多用户"))
self.label_error_log.setText(_translate("Form", ""))

View File

@@ -0,0 +1,146 @@
import json
import os.path
from urllib.parse import urljoin
import requests
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtWidgets import QWidget, QMessageBox
from app.components.QCursorGif import QCursorGif
from app.config import SERVER_API_URL
from app.decrypt.get_bias_addr import BiasAddr
from .getBiasAddrUi import Ui_Form
Stylesheet = """
QPushButton{
background-color: rgb(250,252,253);
border-radius: 5px;
padding: 8px;
border-right: 2px solid #888888; /* 按钮边框2px宽白色 */
border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */
border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */
border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */
}
QPushButton:hover {
background-color: lightgray;
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
border:none;
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
QListWidget {
min-width: 400px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
color: black;
border:none;
}
QListWidget::item{
min-width: 80px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
}
/*被选中时的背景颜色和左边框颜色*/
QListWidget::item:selected {
border-left:none;
color: black;
font-weight: bold;
}
QCheckBox::indicator {
background: rgb(251, 251, 251);
Width:60px;
Height:60px;
border-radius: 10px;
}
QCheckBox::indicator:unchecked{
Width:60px;
Height:60px;
image: url(:/icons/icons/按钮_关闭.svg);
}
QCheckBox::indicator:checked{
Width:60px;
Height:60px;
image: url(:/icons/icons/按钮_开启.svg);
}
"""
class GetBiasAddrControl(QWidget, Ui_Form, QCursorGif):
biasAddrSignal = pyqtSignal(dict)
def __init__(self, parent=None):
super(GetBiasAddrControl, self).__init__(parent)
self.thread = None
self.setStyleSheet(Stylesheet)
self.setupUi(self)
self.init_ui()
def init_ui(self):
self.initCursor([':/icons/icons/Cursors/%d.png' %
i for i in range(8)], self)
self.setCursorTimeout(100)
self.btn_get_bias_addr.clicked.connect(self.get_bias_addr)
self.commandLinkButton.clicked.connect(self.show_info)
self.checkBox_send_error_log.clicked.connect(self.set_error_log)
def set_error_log(self):
if self.checkBox_send_error_log.isChecked():
self.label_error_log.setText('')
else:
self.label_error_log.setText('')
def show_info(self):
QMessageBox.information(self, "收集版本信息",
"为了适配更多版本,需要收集微信的版本信息,该操作不会上传包括手机号、微信号、昵称等在内的任何信息\n示例数据:\n\"3.9.9.27\": [68065304, 0, 68065112, 0, 68066576]"
)
def upload(self, version_data):
url = urljoin(SERVER_API_URL, 'wxBiasAddr')
try:
requests.post(url, json={'bias_dict': version_data})
print('版本信息上传成功')
except:
pass
def get_bias_addr(self):
account = self.lineEdit_wx_alias.text()
mobile = self.lineEdit_tel.text()
name = self.lineEdit_wx_name.text()
if not all([account, mobile, name]):
QMessageBox.critical(self, "错误",
"请把所有信息填写完整")
return
key = None
db_path = "test"
self.startBusy()
self.thread = MyThread(account, mobile, name, key, db_path)
self.thread.signal.connect(self.set_bias_addr)
self.thread.start()
def set_bias_addr(self, data):
if self.checkBox_send_error_log.isChecked():
self.upload(data)
self.stopBusy()
self.biasAddrSignal.emit(data)
class MyThread(QThread):
signal = pyqtSignal(dict)
def __init__(self, account, mobile, name, key, db_path):
super(MyThread, self).__init__()
self.account = account
self.mobile = mobile
self.name = name
self.key = key
self.db_path = db_path
def run(self):
bias_addr = BiasAddr(self.account, self.mobile, self.name, self.key, self.db_path)
data = bias_addr.run(logging_path=True)
self.signal.emit(data)

View File

@@ -0,0 +1,3 @@
from .pc_decrypt import DecryptControl
__all__ = ['DecryptControl']

View File

@@ -0,0 +1,206 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'decryptUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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("微软雅黑")
font.setPointSize(12)
Dialog.setFont(font)
Dialog.setLayoutDirection(QtCore.Qt.LeftToRight)
Dialog.setStyleSheet("QPushButton {\n"
" background: rgb(238,244,249);\n"
"}\n"
"QPushButton:hover{\n"
" background: rgb(230, 235, 240);\n"
"}")
self.verticalLayout_2 = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout_2.setObjectName("verticalLayout_2")
spacerItem = QtWidgets.QSpacerItem(20, 79, 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(78, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem1)
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_4.addItem(spacerItem2)
self.btn_help = QtWidgets.QPushButton(Dialog)
self.btn_help.setMinimumSize(QtCore.QSize(0, 40))
self.btn_help.setMaximumSize(QtCore.QSize(200, 16777215))
self.btn_help.setStyleSheet("")
self.btn_help.setObjectName("btn_help")
self.horizontalLayout_4.addWidget(self.btn_help)
spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_4.addItem(spacerItem3)
self.verticalLayout.addLayout(self.horizontalLayout_4)
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.label_9 = QtWidgets.QLabel(Dialog)
self.label_9.setAlignment(QtCore.Qt.AlignCenter)
self.label_9.setObjectName("label_9")
self.verticalLayout.addWidget(self.label_9)
self.gridLayout = QtWidgets.QGridLayout()
self.gridLayout.setObjectName("gridLayout")
self.label = QtWidgets.QLabel(Dialog)
self.label.setObjectName("label")
self.gridLayout.addWidget(self.label, 0, 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_7 = QtWidgets.QLabel(Dialog)
self.label_7.setObjectName("label_7")
self.gridLayout.addWidget(self.label_7, 1, 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.btn_getinfo = QtWidgets.QPushButton(Dialog)
self.btn_getinfo.setMinimumSize(QtCore.QSize(0, 40))
self.btn_getinfo.setObjectName("btn_getinfo")
self.gridLayout.addWidget(self.btn_getinfo, 1, 2, 2, 1)
self.checkBox = QtWidgets.QCheckBox(Dialog)
self.checkBox.setText("")
self.checkBox.setCheckable(False)
self.checkBox.setObjectName("checkBox")
self.gridLayout.addWidget(self.checkBox, 1, 3, 2, 1)
self.label_2 = QtWidgets.QLabel(Dialog)
self.label_2.setObjectName("label_2")
self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1)
self.lineEdit_phone = QtWidgets.QLineEdit(Dialog)
self.lineEdit_phone.setStyleSheet("background:transparent;\n"
"\n"
" border-radius:5px;\n"
" border-top: 0px solid #b2e281;\n"
" border-bottom: 2px solid black;\n"
" border-right: 0px solid #b2e281;\n"
" border-left: 0px solid #b2e281;\n"
"\n"
"\n"
" border-style:outset\n"
" ")
self.lineEdit_phone.setFrame(False)
self.lineEdit_phone.setObjectName("lineEdit_phone")
self.gridLayout.addWidget(self.lineEdit_phone, 2, 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.lineEdit_name = QtWidgets.QLineEdit(Dialog)
self.lineEdit_name.setStyleSheet("background:transparent;\n"
"\n"
" border-radius:5px;\n"
" border-top: 0px solid #b2e281;\n"
" border-bottom: 2px solid black;\n"
" border-right: 0px solid #b2e281;\n"
" border-left: 0px solid #b2e281;\n"
"\n"
"\n"
" border-style:outset\n"
" ")
self.lineEdit_name.setFrame(False)
self.lineEdit_name.setObjectName("lineEdit_name")
self.gridLayout.addWidget(self.lineEdit_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_wxid = QtWidgets.QLabel(Dialog)
self.label_wxid.setText("")
self.label_wxid.setObjectName("label_wxid")
self.gridLayout.addWidget(self.label_wxid, 4, 1, 1, 1)
self.btn_db_dir = QtWidgets.QPushButton(Dialog)
self.btn_db_dir.setMinimumSize(QtCore.QSize(0, 40))
self.btn_db_dir.setObjectName("btn_db_dir")
self.gridLayout.addWidget(self.btn_db_dir, 4, 2, 3, 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.setMaximumSize(QtCore.QSize(400, 16777215))
self.label_key.setText("")
self.label_key.setObjectName("label_key")
self.gridLayout.addWidget(self.label_key, 5, 1, 1, 1)
self.checkBox_2 = QtWidgets.QCheckBox(Dialog)
self.checkBox_2.setText("")
self.checkBox_2.setCheckable(False)
self.checkBox_2.setObjectName("checkBox_2")
self.gridLayout.addWidget(self.checkBox_2, 5, 3, 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.setMaximumSize(QtCore.QSize(400, 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)
self.verticalLayout.addLayout(self.gridLayout)
self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem4)
self.btn_start = QtWidgets.QPushButton(Dialog)
self.btn_start.setMinimumSize(QtCore.QSize(80, 40))
self.btn_start.setMaximumSize(QtCore.QSize(200, 16777215))
self.btn_start.setObjectName("btn_start")
self.horizontalLayout_2.addWidget(self.btn_start)
spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem5)
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", 0)
self.progressBar.setObjectName("progressBar")
self.horizontalLayout.addWidget(self.progressBar)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_3.addLayout(self.verticalLayout)
spacerItem6 = QtWidgets.QSpacerItem(128, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(spacerItem6)
self.verticalLayout_2.addLayout(self.horizontalLayout_3)
spacerItem7 = QtWidgets.QSpacerItem(20, 79, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
self.verticalLayout_2.addItem(spacerItem7)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btn_help.setText(_translate("Dialog", "使用说明"))
self.label_3.setText(_translate("Dialog", "解密数据库"))
self.label_9.setText(_translate("Dialog", "以下内容为自动获取,如获取失败请手动填写"))
self.label.setText(_translate("Dialog", "PID"))
self.label_7.setText(_translate("Dialog", "版本"))
self.btn_getinfo.setText(_translate("Dialog", "获取信息"))
self.label_2.setText(_translate("Dialog", "手机号"))
self.label_5.setText(_translate("Dialog", "微信昵称"))
self.label_4.setText(_translate("Dialog", "wxid"))
self.btn_db_dir.setText(_translate("Dialog", "设置微信路径"))
self.label_6.setText(_translate("Dialog", "密钥"))
self.label_8.setText(_translate("Dialog", "微信路径"))
self.btn_start.setText(_translate("Dialog", "开始启动"))
self.label_ready.setText(_translate("Dialog", "未就绪"))

View File

@@ -0,0 +1,340 @@
import json
import os.path
import sys
import traceback
from urllib.parse import urljoin
import requests
from PyQt5.QtCore import pyqtSignal, QThread, QUrl
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtWidgets import QWidget, QMessageBox, QFileDialog
from app.DataBase import msg_db, misc_db, close_db
from app.DataBase.merge import merge_databases, merge_MediaMSG_databases
from app.components.QCursorGif import QCursorGif
from app.config import INFO_FILE_PATH, DB_DIR, SERVER_API_URL
from app.decrypt import get_wx_info, decrypt
from app.log import logger
from app.util import path
from . import decryptUi
from ...Icon import Icon
from ...menu.about_dialog import Decrypt
class DecryptControl(QWidget, decryptUi.Ui_Dialog, QCursorGif):
DecryptSignal = pyqtSignal(bool)
get_wxidSignal = pyqtSignal(str)
versionErrorSignal = pyqtSignal(str)
def __init__(self, parent=None):
super(DecryptControl, self).__init__(parent)
self.max_val = 0
self.setupUi(self)
# 设置忙碌光标图片数组
self.initCursor([':/icons/icons/Cursors/%d.png' %
i for i in range(8)], self)
self.setCursorTimeout(100)
self.version_list = None
self.btn_start.clicked.connect(self.decrypt)
self.btn_getinfo.clicked.connect(self.get_info)
self.btn_db_dir.clicked.connect(self.select_db_dir)
# self.lineEdit.returnPressed.connect(self.set_wxid)
# self.lineEdit.textChanged.connect(self.set_wxid_)
self.lineEdit_name.returnPressed.connect(self.set_wxid)
self.lineEdit_name.textChanged.connect(self.set_wxid_)
self.lineEdit_phone.returnPressed.connect(self.set_wxid)
self.lineEdit_phone.textChanged.connect(self.set_wxid_)
self.btn_help.clicked.connect(self.show_help)
self.btn_getinfo.setIcon(Icon.Get_info_Icon)
self.btn_db_dir.setIcon(Icon.Folder_Icon)
self.btn_start.setIcon(Icon.Start_Icon)
self.btn_help.setIcon(Icon.Help_Icon)
self.info = {}
self.lineEdit_name.setFocus()
self.ready = False
self.wx_dir = None
def show_help(self):
# 定义网页链接
url = QUrl("https://blog.lc044.love/post/4")
# 使用QDesktopServices打开网页
QDesktopServices.openUrl(url)
# @log
def get_info(self):
self.startBusy()
self.get_info_thread = MyThread(self.version_list)
self.get_info_thread.signal.connect(self.set_info)
self.get_info_thread.start()
def set_info(self, result):
# print(result)
if result[0] == -1:
QMessageBox.critical(self, "错误", "请登录微信")
elif result[0] == -2:
self.versionErrorSignal.emit(result[1])
QMessageBox.critical(self, "错误",
"微信版本不匹配\n请手动填写信息")
elif result[0] == -3:
QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found")
elif result[0] == -10086:
QMessageBox.critical(self, "错误", "未知错误,请收集错误信息")
else:
self.ready = True
self.info = result[0]
self.label_key.setText(self.info['key'])
self.label_wxid.setText(self.info['wxid'])
self.lineEdit_name.setText(self.info['name'])
self.lineEdit_phone.setText(self.info['mobile'])
self.label_pid.setText(str(self.info['pid']))
self.label_version.setText(self.info['version'])
self.lineEdit_name.setFocus()
self.checkBox.setCheckable(True)
self.checkBox.setChecked(True)
self.get_wxidSignal.emit(self.info['wxid'])
directory = os.path.join(path.wx_path(), self.info['wxid'])
if os.path.exists(directory):
self.label_db_dir.setText(directory)
self.wx_dir = directory
self.checkBox_2.setCheckable(True)
self.checkBox_2.setChecked(True)
self.ready = True
if self.ready:
self.label_ready.setText('已就绪')
if self.wx_dir and os.path.exists(os.path.join(self.wx_dir)):
self.label_ready.setText('已就绪')
self.stopBusy()
def set_wxid_(self):
if self.sender() == self.lineEdit_name:
self.info['name'] = self.lineEdit_name.text()
elif self.sender() == self.lineEdit_phone:
self.info['mobel'] = self.lineEdit_phone.text()
def set_wxid(self):
if self.sender() == self.lineEdit_name:
self.info['name'] = self.lineEdit_name.text()
QMessageBox.information(self, "ok", f"昵称修改成功{self.info['name']}")
elif self.sender() == self.lineEdit_phone:
self.info['mobile'] = self.lineEdit_phone.text()
QMessageBox.information(self, "ok", f"手机号修改成功{self.info['mobile']}")
def select_db_dir(self):
directory = QFileDialog.getExistingDirectory(
self, "选取微信文件保存目录——能看到Msg文件夹",
path.wx_path()
) # 起始路径
db_dir = os.path.join(directory, 'Msg')
if not os.path.exists(db_dir):
QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾")
return
self.label_db_dir.setText(directory)
self.wx_dir = directory
self.checkBox_2.setCheckable(True)
self.checkBox_2.setChecked(True)
if self.ready:
self.label_ready.setText('已就绪')
def decrypt(self):
if not self.ready:
QMessageBox.critical(self, "错误", "请先获取信息")
return
if not self.wx_dir:
QMessageBox.critical(self, "错误", "请先选择微信安装路径")
return
if self.label_wxid.text() == 'None':
QMessageBox.critical(self, "错误", "请填入wxid")
return
db_dir = os.path.join(self.wx_dir, 'Msg')
if self.ready:
if not os.path.exists(db_dir):
QMessageBox.critical(self, "错误", "文件夹选择错误\n一般以wxid_xxx结尾")
return
if self.info.get('key') == 'None':
QMessageBox.critical(self, "错误",
"密钥错误\n请查看教程解决相关问题")
close_db()
self.thread2 = DecryptThread(db_dir, self.info['key'])
self.thread2.maxNumSignal.connect(self.setProgressBarMaxNum)
self.thread2.signal.connect(self.progressBar_view)
self.thread2.okSignal.connect(self.btnExitClicked)
self.thread2.errorSignal.connect(
lambda x: QMessageBox.critical(self, "错误",
"错误\n请检查微信版本是否为最新和微信路径是否正确\n或者关闭微信多开")
)
self.thread2.start()
def btnEnterClicked(self):
# print("enter clicked")
# 中间可以添加处理逻辑
# QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下")
self.progressBar_view(self.max_val)
self.DecryptSignal.emit(True)
# self.close()
def setProgressBarMaxNum(self, max_val):
self.max_val = max_val
self.progressBar.setRange(0, max_val)
def progressBar_view(self, value):
"""
进度条显示
:param value: 进度0-100
:return: None
"""
self.progressBar.setProperty('value', value)
# self.btnExitClicked()
# data.init_database()
def btnExitClicked(self):
# print("Exit clicked")
dic = {
'wxid': self.info['wxid'],
'wx_dir': self.wx_dir,
'name': self.info['name'],
'mobile': self.info['mobile'],
'token': Decrypt.decrypt(self.info['wxid'])
}
try:
with open(INFO_FILE_PATH, "w", encoding="utf-8") as f:
json.dump(dic, f, ensure_ascii=False, indent=4)
except:
with open('./info.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(dic))
self.progressBar_view(self.max_val)
self.DecryptSignal.emit(True)
self.close()
class DecryptThread(QThread):
signal = pyqtSignal(str)
maxNumSignal = pyqtSignal(int)
okSignal = pyqtSignal(str)
errorSignal = pyqtSignal(bool)
def __init__(self, db_path, key):
super(DecryptThread, self).__init__()
self.db_path = db_path
self.key = key
self.textBrowser = None
def __del__(self):
pass
def run(self):
close_db()
output_dir = DB_DIR
os.makedirs(output_dir, exist_ok=True)
tasks = []
if os.path.exists(self.db_path):
for root, dirs, files in os.walk(self.db_path):
for file in files:
if '.db' == file[-3:]:
if 'xInfo.db' == file:
continue
inpath = os.path.join(root, file)
# print(inpath)
output_path = os.path.join(output_dir, file)
tasks.append([self.key, inpath, output_path])
else:
try:
name, suffix = file.split('.')
if suffix.startswith('db_SQLITE'):
inpath = os.path.join(root, file)
# print(inpath)
output_path = os.path.join(output_dir, name + '.db')
tasks.append([self.key, inpath, output_path])
except:
continue
self.maxNumSignal.emit(len(tasks))
for i, task in enumerate(tasks):
if decrypt.decrypt(*task) == -1:
self.errorSignal.emit(True)
self.signal.emit(str(i))
# print(self.db_path)
# 目标数据库文件
target_database = os.path.join(DB_DIR, 'MSG.db')
# 源数据库文件列表
source_databases = [os.path.join(DB_DIR, f"MSG{i}.db") for i in range(1, 50)]
import shutil
if os.path.exists(target_database):
os.remove(target_database)
shutil.copy2(os.path.join(DB_DIR, 'MSG0.db'), target_database) # 使用一个数据库文件作为模板
# 合并数据库
merge_databases(source_databases, target_database)
# 音频数据库文件
target_database = os.path.join(DB_DIR, 'MediaMSG.db')
# 源数据库文件列表
if os.path.exists(target_database):
os.remove(target_database)
source_databases = [os.path.join(DB_DIR, f"MediaMSG{i}.db") for i in range(1, 50)]
shutil.copy2(os.path.join(DB_DIR, 'MediaMSG0.db'), target_database) # 使用一个数据库文件作为模板
# 合并数据库
merge_MediaMSG_databases(source_databases, target_database)
self.okSignal.emit('ok')
# self.signal.emit('100')
class MyThread(QThread):
signal = pyqtSignal(list)
def __init__(self, version_list=None):
super(MyThread, self).__init__()
self.version_list = version_list
def __del__(self):
pass
def get_bias_add(self, version):
url = urljoin(SERVER_API_URL, 'wxBiasAddr')
data = {
'version': version
}
try:
response = requests.get(url, json=data)
print(response)
print(response.text)
if response.status_code == 200:
update_info = response.json()
return update_info
else:
return {}
except:
return {}
def run(self):
if self.version_list:
VERSION_LIST = self.version_list
else:
file_path = './app/resources/data/version_list.json'
if not os.path.exists(file_path):
resource_dir = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
file_path = os.path.join(resource_dir, 'app', 'resources', 'data', 'version_list.json')
with open(file_path, "r", encoding="utf-8") as f:
VERSION_LIST = json.loads(f.read())
try:
result = get_wx_info.get_info(VERSION_LIST)
if result == -1:
result = [result]
elif result == -2:
result = [result]
elif result == -3:
result = [result]
elif isinstance(result, str):
version = result
# version = '3.9.9.43'
version_bias = self.get_bias_add(version)
if version_bias.get(version):
logger.info(f"从云端获取内存基址:{version_bias}")
result = get_wx_info.get_info(version_bias)
else:
logger.info(f"从云端获取内存基址失败:{version}")
result = [-2, version]
except:
logger.error(traceback.format_exc())
result = [-10086]
self.signal.emit(result)

View File

@@ -0,0 +1,208 @@
import json
import os.path
import time
import requests
from PyQt5.QtCore import pyqtSignal, QThread
from PyQt5.QtWidgets import QWidget, QMessageBox
from app.config import SEND_LOG_FLAG
from app.person import Me
from .settingUi import Ui_Form
Stylesheet = """
QPushButton{
background-color: rgb(250,252,253);
border-radius: 5px;
padding: 8px;
border-right: 2px solid #888888; /* 按钮边框2px宽白色 */
border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */
border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */
border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */
}
QPushButton:hover {
background-color: lightgray;
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
border:none;
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
QListWidget {
min-width: 400px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
color: black;
border:none;
}
QListWidget::item{
min-width: 80px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
}
/*被选中时的背景颜色和左边框颜色*/
QListWidget::item:selected {
border-left:none;
color: black;
font-weight: bold;
}
QCheckBox::indicator {
background: rgb(251, 251, 251);
Width:60px;
Height:60px;
border-radius: 10px;
}
QCheckBox::indicator:unchecked{
Width:60px;
Height:60px;
image: url(:/icons/icons/按钮_关闭.svg);
}
QCheckBox::indicator:checked{
Width:60px;
Height:60px;
image: url(:/icons/icons/按钮_开启.svg);
}
"""
def set_SEND_LOG_FLAG(flag):
# noinspection PyGlobalUndefined
global SEND_LOG_FLAG
SEND_LOG_FLAG = flag
class SettingControl(QWidget, Ui_Form):
def __init__(self, parent=None):
super(SettingControl, self).__init__(parent)
self.setStyleSheet(Stylesheet)
self.setupUi(self)
self.btn_addstopword.clicked.connect(self.add_stopwords)
self.btn_addnewword_2.clicked.connect(self.add_new_words)
self.commandLinkButton_send_error_log.clicked.connect(self.show_info)
self.btn_send_error_log.clicked.connect(self.send_error_log)
self.init_ui()
self.read_data()
def init_ui(self):
self.checkBox.setText('')
self.checkBox_send_error_log.clicked.connect(self.set_error_log)
def show_info(self):
QMessageBox.information(self, "收集错误信息",
"为了更好的解决用户问题,需要收集软件崩溃导致的错误信息,该操作不会上传包括手机号、微信号、昵称等在内的任何信息\n"
)
def set_error_log(self):
if self.checkBox_send_error_log.isChecked():
self.label_error_log.setText('')
set_SEND_LOG_FLAG(True)
else:
self.label_error_log.setText('')
set_SEND_LOG_FLAG(False)
print('SEND_LOG_FLAG:', SEND_LOG_FLAG)
def read_data(self):
os.makedirs('./app/data', exist_ok=True)
stopwords = ['裂开', '苦涩', '叹气', '凋谢', '让我看看', '', '奋斗', '疑问', '擦汗', '抠鼻', '鄙视', '勾引',
'奸笑', '嘿哈', '捂脸', '机智', '加油', '吃瓜', '尴尬', '炸弹', '旺柴']
new_words = ['YYDS', '666', '显眼包', '遥遥领先']
if os.path.exists('./app/data/stopwords.txt'):
with open('./app/data/stopwords.txt', 'r', encoding='utf-8') as f:
stopwords = set(f.read().splitlines())
self.plainTextEdit.setPlainText(' '.join(stopwords))
else:
self.plainTextEdit.setPlainText(' '.join(stopwords))
stopwords = '\n'.join(stopwords)
with open('./app/data/stopwords.txt', 'w', encoding='utf-8') as f:
f.write(stopwords)
if os.path.exists('./app/data/new_words.txt'):
with open('./app/data/new_words.txt', 'r', encoding='utf-8') as f:
new_words = set(f.read().splitlines())
self.plainTextEdit_newword.setPlainText(' '.join(new_words))
else:
self.plainTextEdit_newword.setPlainText(' '.join(new_words))
stopwords = '\n'.join(new_words)
with open('./app/data/new_words.txt', 'w', encoding='utf-8') as f:
f.write(stopwords)
def add_stopwords(self):
text = self.plainTextEdit.toPlainText()
stopwords = '\n'.join(text.split())
with open('./app/data/stopwords.txt', 'w', encoding='utf-8') as f:
f.write(stopwords)
QMessageBox.about(self, "添加成功", "停用词添加成功")
def add_new_words(self):
text = self.plainTextEdit_newword.toPlainText()
new_words = '\n'.join(text.split())
with open('./app/data/new_words.txt', 'w', encoding='utf-8') as f:
f.write(new_words)
QMessageBox.about(self, "添加成功", "自定义词添加成功")
def send_error_log(self):
self.send_thread = MyThread()
self.send_thread.signal.connect(self.show_resp)
self.send_thread.start()
def show_resp(self, message):
if message.get('code') == 200:
QMessageBox.about(self, "发送结果", f"日志发送成功\n{message.get('message')}")
else:
QMessageBox.about(self, "发送结果", f"{message.get('code')}:{message.get('errmsg')}")
class MyThread(QThread):
signal = pyqtSignal(dict)
def __init__(self, message=''):
super(MyThread, self).__init__()
if message:
self.message = message
else:
filename = time.strftime("%Y-%m-%d", time.localtime(time.time()))
file_path = f'{filename}-log.log'
if os.path.exists(file_path):
with open(file_path, 'r', encoding='gbk') as f:
self.message = f.read()
elif os.path.exists(f'./app/log/logs/{filename}-log.log'):
with open(f'./app/log/logs/{filename}-log.log', 'r', encoding='gbk') as f:
self.message = f.read()
def __del__(self):
pass
def send_error_msg(self, message):
url = "http://api.lc044.love/error"
if not message:
return {
'code': 201,
'errmsg': '日志为空'
}
data = {
'username': Me().wxid,
'error': message
}
try:
response = requests.post(url, json=data)
if response.status_code == 200:
resp_info = response.json()
return resp_info
else:
return {
'code': 503,
'errmsg': '服务器错误'
}
except:
return {
'code': 404,
'errmsg': '客户端错误'
}
def run(self):
resp_info = self.send_error_msg(self.message)
self.signal.emit(resp_info)

View File

@@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'settingUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(638, 696)
self.verticalLayout_6 = QtWidgets.QVBoxLayout(Form)
self.verticalLayout_6.setObjectName("verticalLayout_6")
self.scrollArea = QtWidgets.QScrollArea(Form)
self.scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
self.scrollArea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
self.scrollArea.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
self.scrollArea.setWidgetResizable(True)
self.scrollArea.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
self.scrollArea.setObjectName("scrollArea")
self.scrollAreaWidgetContents = QtWidgets.QWidget()
self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, -20, 595, 728))
self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents")
self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents)
self.verticalLayout_3.setObjectName("verticalLayout_3")
self.widget = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget.setObjectName("widget")
self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.widget)
self.verticalLayout_4.setObjectName("verticalLayout_4")
self.verticalLayout = QtWidgets.QVBoxLayout()
self.verticalLayout.setObjectName("verticalLayout")
self.label = QtWidgets.QLabel(self.widget)
self.label.setObjectName("label")
self.verticalLayout.addWidget(self.label)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.plainTextEdit = QtWidgets.QPlainTextEdit(self.widget)
self.plainTextEdit.setFrameShape(QtWidgets.QFrame.Box)
self.plainTextEdit.setFrameShadow(QtWidgets.QFrame.Sunken)
self.plainTextEdit.setObjectName("plainTextEdit")
self.horizontalLayout.addWidget(self.plainTextEdit)
self.btn_addstopword = QtWidgets.QPushButton(self.widget)
self.btn_addstopword.setObjectName("btn_addstopword")
self.horizontalLayout.addWidget(self.btn_addstopword)
self.verticalLayout.addLayout(self.horizontalLayout)
self.verticalLayout_4.addLayout(self.verticalLayout)
self.verticalLayout_3.addWidget(self.widget)
self.widget_3 = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget_3.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget_3.setObjectName("widget_3")
self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.widget_3)
self.verticalLayout_5.setObjectName("verticalLayout_5")
self.verticalLayout_2 = QtWidgets.QVBoxLayout()
self.verticalLayout_2.setObjectName("verticalLayout_2")
self.label_2 = QtWidgets.QLabel(self.widget_3)
self.label_2.setObjectName("label_2")
self.verticalLayout_2.addWidget(self.label_2)
self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.plainTextEdit_newword = QtWidgets.QPlainTextEdit(self.widget_3)
self.plainTextEdit_newword.setFrameShape(QtWidgets.QFrame.Box)
self.plainTextEdit_newword.setFrameShadow(QtWidgets.QFrame.Sunken)
self.plainTextEdit_newword.setPlainText("")
self.plainTextEdit_newword.setObjectName("plainTextEdit_newword")
self.horizontalLayout_3.addWidget(self.plainTextEdit_newword)
self.btn_addnewword_2 = QtWidgets.QPushButton(self.widget_3)
self.btn_addnewword_2.setObjectName("btn_addnewword_2")
self.horizontalLayout_3.addWidget(self.btn_addnewword_2)
self.verticalLayout_2.addLayout(self.horizontalLayout_3)
self.verticalLayout_5.addLayout(self.verticalLayout_2)
self.verticalLayout_3.addWidget(self.widget_3)
self.widget_2 = QtWidgets.QWidget(self.scrollAreaWidgetContents)
self.widget_2.setStyleSheet("QWidget{\n"
" background-color:rgb(251,251,251);\n"
" border-radius: 10px;\n"
"}\n"
"\n"
"QPushButton{\n"
" background-color: rgb(250,252,253);\n"
" border-radius: 5px;\n"
" padding: 8px;\n"
" border-right: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */\n"
" border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
" border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */\n"
"}\n"
"QPushButton:hover { \n"
" background-color: lightgray;\n"
"}")
self.widget_2.setObjectName("widget_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.widget_2)
self.horizontalLayout_2.setContentsMargins(9, -1, -1, -1)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.commandLinkButton_send_error_log = QtWidgets.QCommandLinkButton(self.widget_2)
self.commandLinkButton_send_error_log.setEnabled(True)
self.commandLinkButton_send_error_log.setTabletTracking(False)
self.commandLinkButton_send_error_log.setFocusPolicy(QtCore.Qt.StrongFocus)
self.commandLinkButton_send_error_log.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)
self.commandLinkButton_send_error_log.setToolTipDuration(-1)
self.commandLinkButton_send_error_log.setLayoutDirection(QtCore.Qt.LeftToRight)
self.commandLinkButton_send_error_log.setAutoFillBackground(False)
self.commandLinkButton_send_error_log.setCheckable(False)
self.commandLinkButton_send_error_log.setChecked(False)
self.commandLinkButton_send_error_log.setAutoRepeat(False)
self.commandLinkButton_send_error_log.setAutoExclusive(False)
self.commandLinkButton_send_error_log.setAutoDefault(False)
self.commandLinkButton_send_error_log.setDefault(False)
self.commandLinkButton_send_error_log.setObjectName("commandLinkButton_send_error_log")
self.horizontalLayout_2.addWidget(self.commandLinkButton_send_error_log)
self.btn_send_error_log = QtWidgets.QPushButton(self.widget_2)
self.btn_send_error_log.setObjectName("btn_send_error_log")
self.horizontalLayout_2.addWidget(self.btn_send_error_log)
spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.label_error_log = QtWidgets.QLabel(self.widget_2)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.label_error_log.sizePolicy().hasHeightForWidth())
self.label_error_log.setSizePolicy(sizePolicy)
self.label_error_log.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter)
self.label_error_log.setObjectName("label_error_log")
self.horizontalLayout_2.addWidget(self.label_error_log)
self.checkBox_send_error_log = QtWidgets.QCheckBox(self.widget_2)
self.checkBox_send_error_log.setText("")
self.checkBox_send_error_log.setIconSize(QtCore.QSize(64, 64))
self.checkBox_send_error_log.setChecked(True)
self.checkBox_send_error_log.setObjectName("checkBox_send_error_log")
self.horizontalLayout_2.addWidget(self.checkBox_send_error_log)
self.verticalLayout_3.addWidget(self.widget_2)
self.checkBox_2 = QtWidgets.QCheckBox(self.scrollAreaWidgetContents)
self.checkBox_2.setObjectName("checkBox_2")
self.verticalLayout_3.addWidget(self.checkBox_2)
self.checkBox = QtWidgets.QCheckBox(self.scrollAreaWidgetContents)
self.checkBox.setObjectName("checkBox")
self.verticalLayout_3.addWidget(self.checkBox)
self.commandLinkButton_2 = QtWidgets.QCommandLinkButton(self.scrollAreaWidgetContents)
self.commandLinkButton_2.setCheckable(True)
self.commandLinkButton_2.setObjectName("commandLinkButton_2")
self.verticalLayout_3.addWidget(self.commandLinkButton_2)
self.radioButton = QtWidgets.QRadioButton(self.scrollAreaWidgetContents)
self.radioButton.setObjectName("radioButton")
self.verticalLayout_3.addWidget(self.radioButton)
self.scrollArea.setWidget(self.scrollAreaWidgetContents)
self.verticalLayout_6.addWidget(self.scrollArea)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "Form"))
self.label.setText(_translate("Form", "文本框里输入年度报告词云停用词,每个词之间用空格隔开"))
self.plainTextEdit.setPlainText(_translate("Form", "哈哈哈 发呆"))
self.btn_addstopword.setText(_translate("Form", "添加停用词"))
self.label_2.setText(_translate("Form", "文本框里输入年度报告词云自定义词,每个词之间用空格隔开"))
self.btn_addnewword_2.setText(_translate("Form", "添加自定义词"))
self.commandLinkButton_send_error_log.setText(_translate("Form", "收集错误日志"))
self.commandLinkButton_send_error_log.setDescription(_translate("Form", "收集错误信息以帮助改进"))
self.btn_send_error_log.setText(_translate("Form", "手动发送"))
self.label_error_log.setText(_translate("Form", ""))
self.checkBox_2.setText(_translate("Form", "CheckBox"))
self.checkBox.setText(_translate("Form", "CheckBox"))
self.commandLinkButton_2.setText(_translate("Form", "CommandLinkButton"))
self.radioButton.setText(_translate("Form", "RadioButton"))

85
app/ui/tool/toolUI.py Normal file
View File

@@ -0,0 +1,85 @@
# -*- 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(590, 547)
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.setText("")
self.label.setObjectName("label")
self.horizontalLayout.addWidget(self.label)
self.listWidget = QtWidgets.QListWidget(Dialog)
self.listWidget.setMinimumSize(QtCore.QSize(100, 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.setText("")
self.label_2.setObjectName("label_2")
self.horizontalLayout.addWidget(self.label_2)
self.verticalLayout.addLayout(self.horizontalLayout)
self.stackedWidget = QtWidgets.QStackedWidget(Dialog)
self.stackedWidget.setFrameShape(QtWidgets.QFrame.NoFrame)
self.stackedWidget.setFrameShadow(QtWidgets.QFrame.Raised)
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"))
__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)

111
app/ui/tool/tool_window.py Normal file
View File

@@ -0,0 +1,111 @@
from PyQt5.QtCore import Qt, pyqtSignal
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QWidget, QListWidgetItem, QLabel
from app.ui.Icon import Icon
from .pc_decrypt import DecryptControl
from .setting.setting import SettingControl
from .get_bias_addr.get_bias_addr import GetBiasAddrControl
from .toolUI import Ui_Dialog
# 美化样式表
Stylesheet = """
QPushButton{
background-color: rgb(250,252,253);
border-radius: 5px;
padding: 8px;
border-right: 2px solid #888888; /* 按钮边框2px宽白色 */
border-bottom: 2px solid #888888; /* 按钮边框2px宽白色 */
border-left: 1px solid #ffffff; /* 按钮边框2px宽白色 */
border-top: 1px solid #ffffff; /* 按钮边框2px宽白色 */
}
QPushButton:hover {
background-color: lightgray;
}
/*去掉item虚线边框*/
QListWidget, QListView, QTreeWidget, QTreeView {
outline: 0px;
border:none;
}
/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/
QListWidget {
min-width: 400px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
color: black;
border:none;
}
QListWidget::item{
min-width: 80px;
max-width: 400px;
min-height: 80px;
max-height: 80px;
}
/*被选中时的背景颜色和左边框颜色*/
QListWidget::item:selected {
border-left:none;
color: black;
font-weight: bold;
}
"""
class ToolWindow(QWidget, Ui_Dialog):
get_info_signal = pyqtSignal(str)
decrypt_success_signal = pyqtSignal(bool)
load_finish_signal = pyqtSignal(bool)
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.setStyleSheet(Stylesheet)
self.init_ui()
self.load_finish_signal.emit(True)
def init_ui(self):
self.listWidget.clear()
self.listWidget.currentRowChanged.connect(self.setCurrentIndex)
chat_item = QListWidgetItem(Icon.Decrypt_Icon, '解密', self.listWidget)
contact_item = QListWidgetItem(Icon.Contact_Icon, '设置', self.listWidget)
myinfo_item = QListWidgetItem(Icon.Home_Icon, '解密2', self.listWidget)
tool_item = QListWidgetItem(Icon.Home_Icon, '别点', self.listWidget)
self.decrypt_window = DecryptControl()
self.decrypt_window.get_wxidSignal.connect(self.get_info_signal)
self.decrypt_window.DecryptSignal.connect(self.decrypt_success_signal)
self.decrypt_window.versionErrorSignal.connect(self.show_decrypt2)
self.stackedWidget.addWidget(self.decrypt_window)
setting_window = SettingControl()
self.stackedWidget.addWidget(setting_window)
self.get_bias_addr_window = GetBiasAddrControl()
self.get_bias_addr_window.biasAddrSignal.connect(self.decrypt)
self.stackedWidget.addWidget(self.get_bias_addr_window)
label = QLabel('都说了不让你点', self)
label.setFont(QFont("微软雅黑", 50))
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.listWidget.setCurrentRow(0)
self.stackedWidget.setCurrentIndex(0)
def decrypt(self, version_list):
self.listWidget.setCurrentRow(0)
self.stackedWidget.setCurrentIndex(0)
self.decrypt_window.version_list = version_list
self.decrypt_window.get_info()
def show_decrypt2(self, version):
self.listWidget.setCurrentRow(2)
self.stackedWidget.setCurrentIndex(2)
def setCurrentIndex(self, row):
self.stackedWidget.setCurrentIndex(row)

11
app/ui/update/update.py Normal file
View File

@@ -0,0 +1,11 @@
from app.ui.update.updateUi import Ui_Dialog
import shutil
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit
class UpdateControl(QWidget, Ui_Dialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)

52
app/ui/update/updateUi.py Normal file
View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'updateUi.ui'
#
# Created by: PyQt5 UI code generator 5.15.10
#
# 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(590, 547)
font = QtGui.QFont()
font.setFamily("微软雅黑")
Dialog.setFont(font)
self.verticalLayout = QtWidgets.QVBoxLayout(Dialog)
self.verticalLayout.setObjectName("verticalLayout")
self.btn_report = QtWidgets.QPushButton(Dialog)
font = QtGui.QFont()
font.setFamily("微软雅黑")
font.setPointSize(30)
self.btn_report.setFont(font)
self.btn_report.setObjectName("btn_report")
self.verticalLayout.addWidget(self.btn_report)
self.progressBar = QtWidgets.QProgressBar(Dialog)
self.progressBar.setProperty("value", 0)
self.progressBar.setObjectName("progressBar")
self.verticalLayout.addWidget(self.progressBar)
self.horizontalLayout = QtWidgets.QHBoxLayout()
self.horizontalLayout.setObjectName("horizontalLayout")
self.btn_start_update = QtWidgets.QPushButton(Dialog)
self.btn_start_update.setObjectName("btn_start_update")
self.horizontalLayout.addWidget(self.btn_start_update)
self.btn_cancle = QtWidgets.QPushButton(Dialog)
self.btn_cancle.setObjectName("btn_cancle")
self.horizontalLayout.addWidget(self.btn_cancle)
self.verticalLayout.addLayout(self.horizontalLayout)
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
_translate = QtCore.QCoreApplication.translate
Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
self.btn_report.setText(_translate("Dialog", "更新程序"))
self.btn_start_update.setText(_translate("Dialog", "开始更新"))
self.btn_cancle.setText(_translate("Dialog", "取消更新"))