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

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", "个性签名"))