From 9ca75c73e2880855574bfa19a402af8e96803789 Mon Sep 17 00:00:00 2001 From: shuaikangzhou <863909694@qq.com> Date: Wed, 13 Dec 2023 22:12:50 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E5=A4=9A=E6=AC=A1=E8=A7=A3?= =?UTF-8?q?=E5=AF=86=E5=AF=BC=E8=87=B4=E7=9A=84=E6=95=B0=E6=8D=AE=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/DataBase/msg.py | 14 +- app/DataBase/output_pc.py | 2 +- app/{person_pc.py => person.py} | 0 app/ui/Icon.py | 28 +++ app/ui/__init__.py | 0 app/ui/chat/__init__.py | 1 + app/ui/chat/chatInfoUi.py | 48 +++++ app/ui/chat/chatUi.py | 74 ++++++++ app/ui/chat/chat_info.py | 160 ++++++++++++++++ app/ui/chat/chat_window.py | 165 ++++++++++++++++ app/ui/contact/__init__.py | 1 + app/ui/contact/contactInfo.py | 162 ++++++++++++++++ app/ui/contact/contactInfoUi.py | 107 +++++++++++ app/ui/contact/contactUi.py | 74 ++++++++ app/ui/contact/contact_window.py | 155 ++++++++++++++++ app/ui/contact/export_dialog.py | 105 +++++++++++ app/ui/contact/userinfo/__init__.py | 9 + app/ui/contact/userinfo/userinfo.py | 15 ++ app/ui/contact/userinfo/userinfoUi.py | 123 ++++++++++++ app/ui/mainview.py | 250 +++++++++++++++++++++++++ app/ui/mainwindow.py | 179 ++++++++++++++++++ app/ui/tool/__init__.py | 1 + app/ui/tool/pc_decrypt/__init__.py | 3 + app/ui/tool/pc_decrypt/decryptUi.py | 197 ++++++++++++++++++++ app/ui/tool/pc_decrypt/pc_decrypt.py | 258 ++++++++++++++++++++++++++ app/ui/tool/toolUI.py | 83 +++++++++ app/ui/tool/tool_window.py | 89 +++++++++ app/util/path.py | 2 +- app/web_ui/web.py | 8 +- 29 files changed, 2294 insertions(+), 19 deletions(-) rename app/{person_pc.py => person.py} (100%) create mode 100644 app/ui/Icon.py create mode 100644 app/ui/__init__.py create mode 100644 app/ui/chat/__init__.py create mode 100644 app/ui/chat/chatInfoUi.py create mode 100644 app/ui/chat/chatUi.py create mode 100644 app/ui/chat/chat_info.py create mode 100644 app/ui/chat/chat_window.py create mode 100644 app/ui/contact/__init__.py create mode 100644 app/ui/contact/contactInfo.py create mode 100644 app/ui/contact/contactInfoUi.py create mode 100644 app/ui/contact/contactUi.py create mode 100644 app/ui/contact/contact_window.py create mode 100644 app/ui/contact/export_dialog.py create mode 100644 app/ui/contact/userinfo/__init__.py create mode 100644 app/ui/contact/userinfo/userinfo.py create mode 100644 app/ui/contact/userinfo/userinfoUi.py create mode 100644 app/ui/mainview.py create mode 100644 app/ui/mainwindow.py create mode 100644 app/ui/tool/__init__.py create mode 100644 app/ui/tool/pc_decrypt/__init__.py create mode 100644 app/ui/tool/pc_decrypt/decryptUi.py create mode 100644 app/ui/tool/pc_decrypt/pc_decrypt.py create mode 100644 app/ui/tool/toolUI.py create mode 100644 app/ui/tool/tool_window.py diff --git a/app/DataBase/msg.py b/app/DataBase/msg.py index 0f4dea43..ecbfab41 100644 --- a/app/DataBase/msg.py +++ b/app/DataBase/msg.py @@ -63,7 +63,6 @@ def get_messages(self, username_): select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent from MSG where StrTalker=? - group by MsgSvrID order by CreateTime ''' try: @@ -79,7 +78,6 @@ def get_messages_all(self): sql = ''' select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,StrTalker,Reserved1,CompressContent from MSG - group by MsgSvrID order by CreateTime ''' if not self.open_flag: @@ -116,7 +114,6 @@ def get_message_by_num(self, username_, local_id): select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent from MSG where StrTalker = ? and localId < ? - group by MsgSvrID order by CreateTime desc limit 20 ''' @@ -142,7 +139,6 @@ def get_messages_by_type(self, username_, type_, is_Annual_report_=False, year_= select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent from MSG where StrTalker=? and Type=? and strftime('%Y',CreateTime,'unixepoch','localtime') = ? - group by MsgSvrID order by CreateTime ''' else: @@ -150,7 +146,6 @@ def get_messages_by_type(self, username_, type_, is_Annual_report_=False, year_= select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent from MSG where StrTalker=? and Type=? - group by MsgSvrID order by CreateTime ''' try: @@ -171,7 +166,6 @@ def get_messages_by_keyword(self, username_, keyword, num=5, max_len=10): select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra from MSG where StrTalker=? and Type=1 and LENGTH(StrContent) ? and StrTalker=? and Type=1 and IsSender=? - group by MsgSvrID limit 1 ''' self.cursor.execute(sql, [local_id, username_, 1 - is_send]) @@ -213,6 +206,7 @@ def get_messages_by_keyword(self, username_, keyword, num=5, max_len=10): ('', '', ['', ''], ''), ('', '', '', '') )) + print(keyword,res) return res def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023'): @@ -223,7 +217,6 @@ def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023') SELECT MsgSvrID, CreateTime FROM MSG WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ? - GROUP BY MsgSvrID ) group by days ''' @@ -234,7 +227,6 @@ def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023') SELECT MsgSvrID, CreateTime FROM MSG WHERE StrTalker = ? - GROUP BY MsgSvrID ) group by days ''' @@ -260,7 +252,6 @@ def get_messages_by_month(self, username_, is_Annual_report_=False, year_='2023' SELECT MsgSvrID, CreateTime FROM MSG WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ? - GROUP BY MsgSvrID ) group by days ''' @@ -271,7 +262,6 @@ def get_messages_by_month(self, username_, is_Annual_report_=False, year_='2023' SELECT MsgSvrID, CreateTime FROM MSG WHERE StrTalker = ? - GROUP BY MsgSvrID ) group by days ''' @@ -300,7 +290,6 @@ def get_messages_by_hour(self, username_, is_Annual_report_=False, year_='2023') SELECT MsgSvrID, CreateTime FROM MSG where StrTalker = ? and strftime('%Y',CreateTime,'unixepoch','localtime') = ? - GROUP BY MsgSvrID ) group by hours ''' @@ -311,7 +300,6 @@ def get_messages_by_hour(self, username_, is_Annual_report_=False, year_='2023') SELECT MsgSvrID, CreateTime FROM MSG where StrTalker = ? - GROUP BY MsgSvrID ) group by hours ''' diff --git a/app/DataBase/output_pc.py b/app/DataBase/output_pc.py index 2f06831c..2209dead 100644 --- a/app/DataBase/output_pc.py +++ b/app/DataBase/output_pc.py @@ -9,7 +9,7 @@ from .package_msg import PackageMsg from ..DataBase import hard_link_db from ..DataBase import media_msg_db -from ..person_pc import MePC +from ..person import MePC from ..util import path import shutil diff --git a/app/person_pc.py b/app/person.py similarity index 100% rename from app/person_pc.py rename to app/person.py diff --git a/app/ui/Icon.py b/app/ui/Icon.py new file mode 100644 index 00000000..4fbff96b --- /dev/null +++ b/app/ui/Icon.py @@ -0,0 +1,28 @@ +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' + 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') diff --git a/app/ui/__init__.py b/app/ui/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/ui/chat/__init__.py b/app/ui/chat/__init__.py new file mode 100644 index 00000000..00e557bb --- /dev/null +++ b/app/ui/chat/__init__.py @@ -0,0 +1 @@ +from .chat_window import ChatWindow diff --git a/app/ui/chat/chatInfoUi.py b/app/ui/chat/chatInfoUi.py new file mode 100644 index 00000000..62c03c5b --- /dev/null +++ b/app/ui/chat/chatInfoUi.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'chatInfoUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + self.verticalLayout = QtWidgets.QVBoxLayout(Form) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setSpacing(0) + self.verticalLayout.setObjectName("verticalLayout") + self.frame = QtWidgets.QFrame(Form) + self.frame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setObjectName("frame") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label_reamrk = QtWidgets.QLabel(self.frame) + self.label_reamrk.setObjectName("label_reamrk") + self.horizontalLayout_2.addWidget(self.label_reamrk) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem) + self.toolButton = QtWidgets.QToolButton(self.frame) + self.toolButton.setObjectName("toolButton") + self.horizontalLayout_2.addWidget(self.toolButton) + self.verticalLayout_2.addLayout(self.horizontalLayout_2) + + self.verticalLayout.addWidget(self.frame) + + self.retranslateUi(Form) + QtCore.QMetaObject.connectSlotsByName(Form) + + def retranslateUi(self, Form): + _translate = QtCore.QCoreApplication.translate + Form.setWindowTitle(_translate("Form", "Form")) + self.label_reamrk.setText(_translate("Form", "TextLabel")) + self.toolButton.setText(_translate("Form", "...")) diff --git a/app/ui/chat/chatUi.py b/app/ui/chat/chatUi.py new file mode 100644 index 00000000..ddf83ecd --- /dev/null +++ b/app/ui/chat/chatUi.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'chatUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(840, 752) + Form.setStyleSheet("background: rgb(240, 240, 240);") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout_2.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("background:transparent;\n" + "border-radius:5px;\n" + "border-top: 0px solid #b2e281;\n" + "border-bottom: 0px solid #b2e281;\n" + "border-right: 0px solid #b2e281;\n" + "border-left: 0px solid #b2e281;\n" + "border-style:outset;\n" + "background-color:rgb(226,226,226);\n" + " ") + self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle) + self.lineEdit.setObjectName("lineEdit") + self.horizontalLayout.addWidget(self.lineEdit) + self.label_2 = QtWidgets.QLabel(Form) + self.label_2.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")) diff --git a/app/ui/chat/chat_info.py b/app/ui/chat/chat_info.py new file mode 100644 index 00000000..ab29329c --- /dev/null +++ b/app/ui/chat/chat_info.py @@ -0,0 +1,160 @@ +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 MePC +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): + 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 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 = MePC().avatar if is_send else self.contact.avatar + 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 + ) + 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, 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('').rstrip('') + 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) diff --git a/app/ui/chat/chat_window.py b/app/ui/chat/chat_window.py new file mode 100644 index 00000000..59921bf2 --- /dev/null +++ b/app/ui/chat/chat_window.py @@ -0,0 +1,165 @@ +from PyQt5.QtCore import QThread, pyqtSignal +from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit + +from app.DataBase import micro_msg_db, misc_db, msg_db +from app.components import ContactQListWidgetItem +from app.person import ContactPC +from app.ui.Icon import Icon +from app.util import search +from .chatUi import Ui_Form +from .chat_info import ChatInfo + +# 美化样式表 +Stylesheet = """ + +/*去掉item虚线边框*/ +QListWidget, QListView, QTreeWidget, QTreeView { + outline: 0px; + border:none; + background-color:rgb(240,240,240) +} +/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/ +QListWidget { + min-width: 250px; + max-width: 250px; + min-height: 80px; + max-height: 1200px; + color: black; + border:none; +} +QListWidget::item{ + height:60px; + width:250px; +} +/*被选中时的背景颜色和左边框颜色*/ +QListWidget::item:selected { + background: rgb(204, 204, 204); + border-bottom: 2px solid rgb(9, 187, 7); + border-left:none; + color: black; + font-weight: bold; +} +/*鼠标悬停颜色*/ +HistoryPanel::item:hover { + background: rgb(52, 52, 52); +} +""" + + +class ChatWindow(QWidget, Ui_Form): + 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() + + 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.currentRowChanged.connect(self.setCurrentIndex) + self.listWidget.setCurrentRow(0) + self.stackedWidget.setCurrentIndex(0) + + 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): + 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) + self.stackedWidget.setCurrentIndex(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(ContactPC) + 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 = ContactPC(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(ContactPC) + 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) diff --git a/app/ui/contact/__init__.py b/app/ui/contact/__init__.py new file mode 100644 index 00000000..a83ae0a8 --- /dev/null +++ b/app/ui/contact/__init__.py @@ -0,0 +1 @@ +from .contact_window import ContactWindow diff --git a/app/ui/contact/contactInfo.py b/app/ui/contact/contactInfo.py new file mode 100644 index 00000000..ba2fae53 --- /dev/null +++ b/app/ui/contact/contactInfo.py @@ -0,0 +1,162 @@ +from PyQt5.QtCore import pyqtSignal, QUrl, QThread +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QWidget, QMenu, QAction, QToolButton, QMessageBox, QDialog + +from app.DataBase.output_pc import Output +from app.ui.Icon import Icon +from .contactInfoUi import Ui_Form +from .userinfo import userinfo +from ...person import ContactPC +from .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.setupUi(self) + self.contact: ContactPC = 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.label_remark.setText(self.contact.remark) + self.stackedWidget.addWidget(self.view_userinfo) + self.stackedWidget.setCurrentWidget(self.view_userinfo) + menu = QMenu(self) + self.toDocxAct = QAction(Icon.ToDocx, '导出Docx', self) + self.toCSVAct = QAction(Icon.ToCSV, '导出CSV', self) + self.toHtmlAct = QAction(Icon.ToHTML, '导出HTML', self) + self.toTxtAct = QAction(Icon.ToTXT, '导出TXT', 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) + 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) + + def toolButton_show(self): + self.toolButton_output.showMenu() + + def analysis(self): + QMessageBox.warning(self, + "别急别急", + "马上就实现该功能" + ) + return + self.stackedWidget.setCurrentWidget(self.view_analysis) + if 'room' in self.contact.wxid: + QMessageBox.warning( + self, '警告', + '暂不支持群组' + ) + return + self.view_analysis.start() + + def annual_report(self): + # QMessageBox.warning( + # self, + # "提示", + # "敬请期待" + # ) + # return + self.contact.save_avatar() + 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("http://127.0.0.1:21314")) + + def emotionale_Analysis(self): + QMessageBox.warning(self, + "别急别急", + "马上就实现该功能" + ) + return + self.stackedWidget.setCurrentWidget(self.view_emotion) + if 'room' in self.contact.wxid: + QMessageBox.warning( + self, '警告', + '暂不支持群组' + ) + return + self.view_emotion.start() + + def back(self): + """ + 将userinfo界面设置为可见,其他界面设置为不可见 + """ + self.stackedWidget.setCurrentWidget(self.view_userinfo) + + def output(self): + """ + 导出聊天记录 + :return: + """ + self.stackedWidget.setCurrentWidget(self.view_userinfo) + if self.sender() == self.toDocxAct: + print('功能暂未实现') + QMessageBox.warning(self, + "别急别急", + "马上就实现该功能" + ) + return + self.outputThread = Output(self.Me, self.contact.wxid) + elif self.sender() == self.toCSVAct: + # 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_()获取用户的操作结果 + + def hide_progress_bar(self, int): + reply = QMessageBox(self) + reply.setIcon(QMessageBox.Information) + reply.setWindowTitle('OK') + reply.setText(f"导出聊天记录成功\n在./data/目录下(跟exe文件在一起)") + reply.addButton("确认", QMessageBox.AcceptRole) + reply.addButton("取消", QMessageBox.RejectRole) + api = reply.exec_() + self.view_userinfo.progressBar.setVisible(False) + + def output_progress(self, value): + self.view_userinfo.progressBar.setProperty('value', value) + + def set_progressBar_range(self, value): + self.view_userinfo.progressBar.setVisible(True) + self.view_userinfo.progressBar.setRange(0, value) + + +class ReportThread(QThread): + okSignal = pyqtSignal(bool) + + def __init__(self, contact): + super().__init__() + self.contact = contact + + def run(self): + from app.web_ui import web + web.contact = self.contact + web.run(port='21314') + self.okSignal.emit(True) diff --git a/app/ui/contact/contactInfoUi.py b/app/ui/contact/contactInfoUi.py new file mode 100644 index 00000000..cba0b773 --- /dev/null +++ b/app/ui/contact/contactInfoUi.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'contactInfoUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(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.label_remark = QtWidgets.QLabel(Form) + self.label_remark.setMaximumSize(QtCore.QSize(120, 100)) + font = QtGui.QFont() + font.setPointSize(12) + self.label_remark.setFont(font) + self.label_remark.setText("") + self.label_remark.setObjectName("label_remark") + self.horizontalLayout_3.addWidget(self.label_remark) + self.btn_analysis = QtWidgets.QPushButton(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.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.horizontalLayout_3.setStretch(0, 1) + 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.btn_back.setText(_translate("Form", "退出")) + self.toolButton_output.setText(_translate("Form", "导出聊天记录")) diff --git a/app/ui/contact/contactUi.py b/app/ui/contact/contactUi.py new file mode 100644 index 00000000..ff13d8cd --- /dev/null +++ b/app/ui/contact/contactUi.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'contactUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtWidgets + + +class Ui_Form(object): + def setupUi(self, Form): + Form.setObjectName("Form") + Form.resize(840, 752) + Form.setStyleSheet("background: rgb(240, 240, 240);") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(Form) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setSpacing(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("background:transparent;\n" + "border-radius:5px;\n" + "border-top: 0px solid #b2e281;\n" + "border-bottom: 0px solid #b2e281;\n" + "border-right: 0px solid #b2e281;\n" + "border-left: 0px solid #b2e281;\n" + "border-style:outset;\n" + "background-color:rgb(226,226,226);\n" + " ") + self.lineEdit.setCursorMoveStyle(QtCore.Qt.VisualMoveStyle) + self.lineEdit.setObjectName("lineEdit") + self.horizontalLayout.addWidget(self.lineEdit) + self.label_2 = QtWidgets.QLabel(Form) + self.label_2.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")) diff --git a/app/ui/contact/contact_window.py b/app/ui/contact/contact_window.py new file mode 100644 index 00000000..9f3e71b0 --- /dev/null +++ b/app/ui/contact/contact_window.py @@ -0,0 +1,155 @@ +from PyQt5.QtCore import QThread, pyqtSignal +from PyQt5.QtWidgets import QWidget, QMessageBox, QAction, QLineEdit + +from app.DataBase import micro_msg_db, misc_db +from app.components import ContactQListWidgetItem +from app.person import ContactPC +from app.ui.Icon import Icon +from .contactInfo import ContactInfo +from .contactUi import Ui_Form +from ...util import search + +# 美化样式表 +Stylesheet = """ +QPushButton{ + background-color: transparent; +} +QPushButton:hover { + background-color: lightgray; +} +/*去掉item虚线边框*/ +QListWidget, QListView, QTreeWidget, QTreeView { + outline: 0px; + border:none; + background-color:rgb(240,240,240) +} +/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/ +QListWidget { + min-width: 250px; + max-width: 250px; + min-height: 80px; + max-height: 1200px; + color: black; + border:none; +} +QListWidget::item{ + height:60px; + width:250px; +} +/*被选中时的背景颜色和左边框颜色*/ +QListWidget::item:selected { + background: rgb(204, 204, 204); + border-bottom: 2px solid rgb(9, 187, 7); + border-left:none; + color: black; + font-weight: bold; +} +/*鼠标悬停颜色*/ +HistoryPanel::item:hover { + background: rgb(52, 52, 52); +} +""" + + +class ContactWindow(QWidget, Ui_Form): + 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.init_ui() + self.contacts = [[], []] + self.show_contacts() + + def init_ui(self): + search_action = QAction(self.lineEdit) + search_action.setIcon(Icon.Search_Icon) + self.lineEdit.addAction(search_action, QLineEdit.LeadingPosition) + self.lineEdit.returnPressed.connect(self.search_contact) + self.listWidget.clear() + self.listWidget.currentRowChanged.connect(self.setCurrentIndex) + self.listWidget.setCurrentRow(0) + self.stackedWidget.setCurrentIndex(0) + + def show_contacts(self): + # 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): + 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: ContactPC): + 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) + contact_info_window = ContactInfo(contact) + self.stackedWidget.addWidget(contact_info_window) + + def setCurrentIndex(self, row): + # print(row) + self.stackedWidget.setCurrentIndex(row) + + +class ShowContactThread(QThread): + showSingal = pyqtSignal(ContactPC) + 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 = ContactPC(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(ContactPC) + 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) diff --git a/app/ui/contact/export_dialog.py b/app/ui/contact/export_dialog.py new file mode 100644 index 00000000..7786fc6e --- /dev/null +++ b/app/ui/contact/export_dialog.py @@ -0,0 +1,105 @@ +from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QDialog, QVBoxLayout, QCheckBox, QHBoxLayout, \ + QProgressBar, QLabel, QMessageBox + +from app.DataBase.output_pc import Output + +types = { + '文本': 1, + '图片': 3, + '语音': 34, + '视频': 43, + '表情包': 47, + '拍一拍等系统消息': 10000 +} +Stylesheet = """ +QPushButton{ + background-color: #ffffff; +} +QPushButton:hover { + background-color: lightgray; +} +""" + +class ExportDialog(QDialog): + def __init__(self, contact=None, title="选择导出的类型", file_type="csv", parent=None): + super(ExportDialog, self).__init__(parent) + self.setStyleSheet(Stylesheet) + self.contact = contact + if file_type == 'html': + self.export_type = Output.HTML + self.export_choices = {"文本": True, "图片": True, "语音": True, "视频": True, "表情包": True, + '拍一拍等系统消息': 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} # 定义导出的数据类型,默认全部选择 + else: + self.export_choices = {"文本": True, "图片": True, "视频": True, "表情包": True} # 定义导出的数据类型,默认全部选择 + self.setWindowTitle(title) + layout = QVBoxLayout(self) + self.resize(400, 300) + self.worker = None # 导出线程 + self.progress_bar = QProgressBar(self) + self.progress_label = QLabel(self) + for export_type, default_state in self.export_choices.items(): + checkbox = QCheckBox(export_type) + checkbox.setChecked(default_state) + layout.addWidget(checkbox) + layout.addWidget(self.progress_bar) + layout.addWidget(self.progress_label) + hlayout = QHBoxLayout(self) + self.export_button = QPushButton("导出") + self.export_button.clicked.connect(self.export_data) + hlayout.addWidget(self.export_button) + + self.cancel_button = QPushButton("取消") + self.cancel_button.clicked.connect(self.reject) # 使用reject关闭对话框 + hlayout.addWidget(self.cancel_button) + layout.addLayout(hlayout) + self.setLayout(layout) + + def export_data(self): + self.export_button.setEnabled(False) + self.cancel_button.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) + self.worker.progressSignal.connect(self.update_progress) + self.worker.okSignal.connect(self.export_finished) + self.worker.start() + # self.accept() # 使用accept关闭对话框 + + def export_finished(self): + self.export_button.setEnabled(True) + self.cancel_button.setEnabled(True) + reply = QMessageBox(self) + reply.setIcon(QMessageBox.Information) + reply.setWindowTitle('OK') + reply.setText(f"导出聊天记录成功\n在./data/目录下(跟exe文件在一起)") + reply.addButton("确认", QMessageBox.AcceptRole) + reply.addButton("取消", QMessageBox.RejectRole) + api = reply.exec_() + self.accept() + + def update_progress(self, progress_percentage): + self.progress_bar.setValue(progress_percentage) + self.progress_label.setText(f"导出进度: {progress_percentage}%") + + +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_()) diff --git a/app/ui/contact/userinfo/__init__.py b/app/ui/contact/userinfo/__init__.py new file mode 100644 index 00000000..6d32454d --- /dev/null +++ b/app/ui/contact/userinfo/__init__.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +""" +@File : __init__.py.py +@Author : Shuaikang Zhou +@Time : 2022/12/24 10:34 +@IDE : Pycharm +@Version : Python3.10 +@comment : ··· +""" diff --git a/app/ui/contact/userinfo/userinfo.py b/app/ui/contact/userinfo/userinfo.py new file mode 100644 index 00000000..5a6ac0bf --- /dev/null +++ b/app/ui/contact/userinfo/userinfo.py @@ -0,0 +1,15 @@ +from PyQt5.QtWidgets import * + +from .userinfoUi import Ui_Frame + + +class UserinfoController(QWidget, Ui_Frame): + def __init__(self, contact, parent=None): + super().__init__(parent) + self.setupUi(self) + self.l_remark.setText(contact.remark) + self.l_avatar.setPixmap(contact.avatar) + self.l_nickname.setText(f'昵称:{contact.nickName}') + self.l_username.setText(f'微信号:{contact.alias}') + self.lineEdit.setText(contact.remark) + self.progressBar.setVisible(False) diff --git a/app/ui/contact/userinfo/userinfoUi.py b/app/ui/contact/userinfo/userinfoUi.py new file mode 100644 index 00000000..8494e5d3 --- /dev/null +++ b/app/ui/contact/userinfo/userinfoUi.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'userinfoUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Frame(object): + def setupUi(self, Frame): + Frame.setObjectName("Frame") + Frame.resize(624, 720) + Frame.setCursor(QtGui.QCursor(QtCore.Qt.IBeamCursor)) + Frame.setMouseTracking(True) + Frame.setTabletTracking(True) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(Frame) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem1) + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setHorizontalSpacing(6) + self.gridLayout.setObjectName("gridLayout") + self.l_avatar = QtWidgets.QLabel(Frame) + self.l_avatar.setMinimumSize(QtCore.QSize(80, 80)) + self.l_avatar.setMaximumSize(QtCore.QSize(80, 80)) + self.l_avatar.setText("") + self.l_avatar.setPixmap(QtGui.QPixmap("../../../a_img/be0fa6c0c4707fb5f7b37b660de826d3.jpg")) + self.l_avatar.setScaledContents(True) + self.l_avatar.setObjectName("l_avatar") + self.gridLayout.addWidget(self.l_avatar, 0, 0, 3, 1) + self.l_remark = QtWidgets.QLabel(Frame) + self.l_remark.setMinimumSize(QtCore.QSize(0, 30)) + self.l_remark.setMaximumSize(QtCore.QSize(16777215, 30)) + font = QtGui.QFont() + font.setPointSize(15) + self.l_remark.setFont(font) + self.l_remark.setObjectName("l_remark") + self.gridLayout.addWidget(self.l_remark, 0, 1, 1, 1) + self.l_nickname = QtWidgets.QLabel(Frame) + self.l_nickname.setMinimumSize(QtCore.QSize(0, 30)) + self.l_nickname.setMaximumSize(QtCore.QSize(16777215, 30)) + self.l_nickname.setObjectName("l_nickname") + self.gridLayout.addWidget(self.l_nickname, 1, 1, 1, 1) + self.l_username = QtWidgets.QLabel(Frame) + self.l_username.setMinimumSize(QtCore.QSize(0, 20)) + self.l_username.setMaximumSize(QtCore.QSize(16777215, 20)) + self.l_username.setObjectName("l_username") + self.gridLayout.addWidget(self.l_username, 2, 1, 1, 1) + self.gridLayout.setRowStretch(0, 1) + self.gridLayout.setRowStretch(1, 1) + self.gridLayout.setRowStretch(2, 1) + self.verticalLayout.addLayout(self.gridLayout) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem2) + self.horizontalLayout = QtWidgets.QHBoxLayout() + self.horizontalLayout.setObjectName("horizontalLayout") + self.label = QtWidgets.QLabel(Frame) + self.label.setMinimumSize(QtCore.QSize(80, 0)) + self.label.setMaximumSize(QtCore.QSize(80, 16777215)) + font = QtGui.QFont() + font.setPointSize(15) + self.label.setFont(font) + self.label.setObjectName("label") + self.horizontalLayout.addWidget(self.label) + self.lineEdit = QtWidgets.QLineEdit(Frame) + self.lineEdit.setMinimumSize(QtCore.QSize(0, 25)) + self.lineEdit.setMaximumSize(QtCore.QSize(16777215, 25)) + font = QtGui.QFont() + font.setPointSize(15) + self.lineEdit.setFont(font) + self.lineEdit.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) + self.lineEdit.setAutoFillBackground(False) + self.lineEdit.setStyleSheet("\n" +" background:transparent;border-width:0;border-style:outset\n" +" ") + self.lineEdit.setObjectName("lineEdit") + self.horizontalLayout.addWidget(self.lineEdit) + self.verticalLayout.addLayout(self.horizontalLayout) + spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem3) + self.progressBar = QtWidgets.QProgressBar(Frame) + self.progressBar.setProperty("value", 24) + self.progressBar.setObjectName("progressBar") + self.verticalLayout.addWidget(self.progressBar) + spacerItem4 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout.addItem(spacerItem4) + self.verticalLayout.setStretch(0, 2) + self.verticalLayout.setStretch(1, 3) + self.verticalLayout.setStretch(2, 4) + self.verticalLayout.setStretch(3, 1) + self.verticalLayout.setStretch(4, 4) + self.verticalLayout.setStretch(5, 1) + self.verticalLayout.setStretch(6, 2) + self.horizontalLayout_2.addLayout(self.verticalLayout) + spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem5) + self.horizontalLayout_2.setStretch(0, 1) + self.horizontalLayout_2.setStretch(1, 2) + self.horizontalLayout_2.setStretch(2, 1) + self.horizontalLayout_3.addLayout(self.horizontalLayout_2) + + self.retranslateUi(Frame) + QtCore.QMetaObject.connectSlotsByName(Frame) + + def retranslateUi(self, Frame): + _translate = QtCore.QCoreApplication.translate + Frame.setWindowTitle(_translate("Frame", "Frame")) + self.l_remark.setText(_translate("Frame", "曹雨萱")) + self.l_nickname.setText(_translate("Frame", "昵称:997")) + self.l_username.setText(_translate("Frame", "账号:TextLabel")) + self.label.setText(_translate("Frame", "备注名")) + self.lineEdit.setText(_translate("Frame", "曹雨萱")) diff --git a/app/ui/mainview.py b/app/ui/mainview.py new file mode 100644 index 00000000..da87490c --- /dev/null +++ b/app/ui/mainview.py @@ -0,0 +1,250 @@ +# -*- 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 + +from PyQt5.QtCore import pyqtSignal, QUrl, Qt, QThread, QSize +from PyQt5.QtGui import QPixmap, QFont, QDesktopServices +from PyQt5.QtWidgets import QMainWindow, QLabel, QListWidgetItem, QMessageBox + +from app import config +from app.DataBase import msg_db, misc_db, micro_msg_db, hard_link_db +from app.ui.Icon import Icon +from . import mainwindow +from .chat import ChatWindow +from .contact import ContactWindow +from .tool.tool_window import ToolWindow +from ..DataBase.output_pc import Output +from ..person import MePC + +# 美化样式表 +Stylesheet = """ + +/*去掉item虚线边框*/ +QListWidget, QListView, QTreeWidget, QTreeView { + outline: 0px; +} +/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/ +QListWidget { + min-width: 120px; + max-width: 120px; + color: black; + background: white; + border:none; +} +QListWidget::item{ + height:60; +} +/*被选中时的背景颜色和左边框颜色*/ +QListWidget::item:selected { + background: rgb(204, 204, 204); + border-left: 4px solid rgb(9, 187, 7); + color: black; + font-weight: bold; +} +/*鼠标悬停颜色*/ +HistoryPanel::item:hover { + background: rgb(52, 52, 52); +} +""" + + +class MainWinController(QMainWindow, mainwindow.Ui_MainWindow): + exitSignal = pyqtSignal(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.setWindowIcon(Icon.MainWindow_Icon) + self.setStyleSheet(Stylesheet) + self.listWidget.clear() + self.resize(QSize(800, 600)) + self.action_desc.triggered.connect(self.about) + 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')) + + def load_data(self, flag=True): + if os.path.exists('./app/data/info.json'): + with open('./app/data/info.json', 'r', encoding='utf-8') as f: + dic = json.loads(f.read()) + wxid = dic.get('wxid') + if wxid: + me = MePC() + me.wxid = dic.get('wxid') + me.name = dic.get('name') + me.mobile = dic.get('mobile') + me.wx_dir = dic.get('wx_dir') + self.set_my_info(wxid) + self.load_flag = True + else: + QMessageBox.information( + self, + '温馨提示', + '点击 工具->获取信息 重启后可以显示本人头像哦' + ) + + def init_ui(self): + 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_desc.setIcon(Icon.Help_Icon) + self.action_help_contact.triggered.connect( + lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/5"))) + self.action_help_chat.triggered.connect( + lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/5"))) + self.action_help_decrypt.triggered.connect( + lambda: QDesktopServices.openUrl(QUrl("https://blog.lc044.love/post/4"))) + self.listWidget.setVisible(False) + self.stackedWidget.setVisible(False) + self.listWidget.currentRowChanged.connect(self.setCurrentIndex) + tool_item = QListWidgetItem(Icon.Tool_Icon, '工具', self.listWidget) + chat_item = QListWidgetItem(Icon.Chat_Icon, '聊天', self.listWidget) + contact_item = QListWidgetItem(Icon.Contact_Icon, '好友', self.listWidget) + myinfo_item = QListWidgetItem(Icon.Home_Icon, '我的', self.listWidget) + tool_window = ToolWindow() + tool_window.get_info_signal.connect(self.set_my_info) + tool_window.decrypt_success_signal.connect(self.decrypt_success) + tool_window.load_finish_signal.connect(self.loading) + self.stackedWidget.addWidget(tool_window) + self.chat_window = ChatWindow() + # chat_window = QWidget() + self.stackedWidget.addWidget(self.chat_window) + self.contact_window = ContactWindow() + self.stackedWidget.addWidget(self.contact_window) + label = QLabel('该功能暂不支持哦') + label.setFont(QFont("微软雅黑", 50)) + label.setAlignment(Qt.AlignCenter) + self.stackedWidget.addWidget(label) + tool_window.load_finish_signal.connect(self.loading) + self.statusbar.showMessage('聊天窗口上划到顶部会加载新的聊天记录\n一次不行那就多来几次') + self.contact_window.load_finish_signal.connect(self.loading) + self.chat_window.load_finish_signal.connect(self.loading) + + 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() + try: + img_bytes = misc_db.get_avatar_buffer(wxid) + except AttributeError: + return + 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) + me = MePC() + me.set_avatar(img_bytes) + self.myavatar.setScaledContents(True) + self.myavatar.setPixmap(self.avatar) + + def stop_loading(self, a0): + self.label.setVisible(False) + + def loading(self, a0): + self.load_num += 1 + # self.label.setVisible(False) + print('加载一个了') + if self.load_num == 1: + print('ok了') + self.label.clear() + self.label.hide() + self.okSignal.emit(True) + self.listWidget.setVisible(True) + self.stackedWidget.setVisible(True) + if self.load_flag: + self.listWidget.setCurrentRow(1) + self.stackedWidget.setCurrentIndex(1) + else: + self.listWidget.setCurrentRow(0) + self.stackedWidget.setCurrentIndex(0) + + def output(self): + if self.sender() == self.action_output_CSV: + self.outputThread = Output(None, type_=Output.CSV_ALL) + 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.okSignal.connect( + lambda x: self.message('联系人导出成功')) + self.outputThread.start() + + def message(self, msg): + QMessageBox.about(self, "提醒", msg) + + def about(self): + """ + 关于 + """ + QMessageBox.about(self, "关于", + f'''版本:{config.version}
QQ交流群:{config.contact}
地址:https://github.com/LC044/WeChatMsg
新特性:
{''.join(['' + i for i in config.description])} + ''' + ) + + def decrypt_success(self): + QMessageBox.about(self, "解密成功", "请重新启动") + self.close() + + def close(self) -> bool: + super().close() + misc_db.close() + msg_db.close() + micro_msg_db.close() + hard_link_db.close() + self.contact_window.close() + self.exitSignal.emit(True) + + +class LoadWindowThread(QThread): + okSignal = pyqtSignal(bool) + + def __init__(self): + super().__init__() + self.num = 0 + + def loading(self): + self.num += 1 + print('加载一个了') + if self.num == 2: + self.okSignal.emit(True) + + def run(self): + self.chat_window = ChatWindow() + self.contact_window = ContactWindow() + self.contact_window.load_finish_signal.connect(self.loading) + self.chat_window.load_finish_signal.connect(self.loading) + print('加载完成') + self.okSignal.emit(True) diff --git a/app/ui/mainwindow.py b/app/ui/mainwindow.py new file mode 100644 index 00000000..20e242c5 --- /dev/null +++ b/app/ui/mainwindow.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'mainwindow.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName("MainWindow") + MainWindow.resize(1280, 779) + MainWindow.setStyleSheet("\n" +" /*去掉item虚线边框*/\n" +" QListWidget, QListView, QTreeWidget, QTreeView {\n" +" outline: 0px;\n" +" }\n" +" /*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/\n" +" QListWidget {\n" +" min-width: 120px;\n" +" max-width: 120px;\n" +" color: black;\n" +" background: white;\n" +" border:none;\n" +" }\n" +" QListWidget::item{\n" +" height:80;\n" +" }\n" +" /*被选中时的背景颜色和左边框颜色*/\n" +" QListWidget::item:selected {\n" +" background: rgb(204, 204, 204);\n" +" border-left: 4px solid rgb(9, 187, 7);\n" +" }\n" +" /*鼠标悬停颜色*/\n" +" HistoryPanel::item:hover {\n" +" background: rgb(52, 52, 52);\n" +" }\n" +" ") + self.centralwidget = QtWidgets.QWidget(MainWindow) + self.centralwidget.setObjectName("centralwidget") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setSpacing(0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.frame_info = QtWidgets.QFrame(self.centralwidget) + self.frame_info.setMinimumSize(QtCore.QSize(80, 500)) + self.frame_info.setMaximumSize(QtCore.QSize(80, 16777215)) + self.frame_info.setStyleSheet("background-color:rgb(240,240,240)") + self.frame_info.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_info.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame_info.setObjectName("frame_info") + self.myavatar = QtWidgets.QLabel(self.frame_info) + self.myavatar.setGeometry(QtCore.QRect(10, 40, 60, 60)) + self.myavatar.setObjectName("myavatar") + self.listWidget = QtWidgets.QListWidget(self.frame_info) + self.listWidget.setGeometry(QtCore.QRect(0, 230, 120, 331)) + self.listWidget.setMinimumSize(QtCore.QSize(120, 0)) + self.listWidget.setMaximumSize(QtCore.QSize(120, 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("微软雅黑") + font.setBold(False) + font.setWeight(50) + self.stackedWidget.setFont(font) + self.stackedWidget.setObjectName("stackedWidget") + self.horizontalLayout.addWidget(self.stackedWidget) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1280, 23)) + self.menubar.setObjectName("menubar") + self.menu_F = QtWidgets.QMenu(self.menubar) + self.menu_F.setObjectName("menu_F") + self.menu_data = QtWidgets.QMenu(self.menubar) + font = QtGui.QFont() + font.setFamily("微软雅黑") + 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") + self.menu_3 = QtWidgets.QMenu(self.menubar) + self.menu_3.setObjectName("menu_3") + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(MainWindow) + self.statusbar.setObjectName("statusbar") + MainWindow.setStatusBar(self.statusbar) + self.action_3 = QtWidgets.QAction(MainWindow) + self.action_3.setObjectName("action_3") + self.action_4 = QtWidgets.QAction(MainWindow) + self.action_4.setObjectName("action_4") + self.action_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.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_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_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.menubar.addAction(self.menu_3.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.menu_3.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", "导出联系人")) diff --git a/app/ui/tool/__init__.py b/app/ui/tool/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/app/ui/tool/__init__.py @@ -0,0 +1 @@ + diff --git a/app/ui/tool/pc_decrypt/__init__.py b/app/ui/tool/pc_decrypt/__init__.py new file mode 100644 index 00000000..ae1c6184 --- /dev/null +++ b/app/ui/tool/pc_decrypt/__init__.py @@ -0,0 +1,3 @@ +from .pc_decrypt import DecryptControl + +__all__ = ['DecryptControl'] diff --git a/app/ui/tool/pc_decrypt/decryptUi.py b/app/ui/tool/pc_decrypt/decryptUi.py new file mode 100644 index 00000000..ac35c660 --- /dev/null +++ b/app/ui/tool/pc_decrypt/decryptUi.py @@ -0,0 +1,197 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'decryptUi.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(611, 519) + font = QtGui.QFont() + font.setFamily("微软雅黑") + Dialog.setFont(font) + Dialog.setLayoutDirection(QtCore.Qt.LeftToRight) + self.verticalLayout_3 = QtWidgets.QVBoxLayout(Dialog) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.verticalLayout_2 = QtWidgets.QVBoxLayout() + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout() + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_4.addItem(spacerItem) + self.btn_help = QtWidgets.QPushButton(Dialog) + self.btn_help.setMaximumSize(QtCore.QSize(200, 16777215)) + self.btn_help.setObjectName("btn_help") + self.horizontalLayout_4.addWidget(self.btn_help) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_4.addItem(spacerItem1) + self.verticalLayout_2.addLayout(self.horizontalLayout_4) + spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem2) + self.horizontalLayout_3 = QtWidgets.QHBoxLayout() + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + spacerItem3 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem3) + self.verticalLayout = QtWidgets.QVBoxLayout() + self.verticalLayout.setObjectName("verticalLayout") + self.label_3 = QtWidgets.QLabel(Dialog) + font = QtGui.QFont() + font.setFamily("一纸情书") + font.setPointSize(20) + self.label_3.setFont(font) + self.label_3.setAlignment(QtCore.Qt.AlignCenter) + self.label_3.setObjectName("label_3") + self.verticalLayout.addWidget(self.label_3) + self.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_2 = QtWidgets.QGridLayout() + self.gridLayout_2.setObjectName("gridLayout_2") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.label_phone = QtWidgets.QLabel(Dialog) + self.label_phone.setText("") + self.label_phone.setObjectName("label_phone") + self.gridLayout.addWidget(self.label_phone, 2, 1, 1, 1) + self.label_7 = QtWidgets.QLabel(Dialog) + self.label_7.setObjectName("label_7") + self.gridLayout.addWidget(self.label_7, 1, 0, 1, 1) + self.lineEdit = QtWidgets.QLineEdit(Dialog) + self.lineEdit.setStyleSheet("background:transparent;\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.setFrame(False) + self.lineEdit.setObjectName("lineEdit") + self.gridLayout.addWidget(self.lineEdit, 4, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(Dialog) + self.label_5.setObjectName("label_5") + self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(Dialog) + self.label_6.setObjectName("label_6") + self.gridLayout.addWidget(self.label_6, 5, 0, 1, 1) + self.label_key = QtWidgets.QLabel(Dialog) + self.label_key.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.label = QtWidgets.QLabel(Dialog) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(Dialog) + self.label_2.setObjectName("label_2") + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) + self.label_pid = QtWidgets.QLabel(Dialog) + self.label_pid.setText("") + self.label_pid.setObjectName("label_pid") + self.gridLayout.addWidget(self.label_pid, 0, 1, 1, 1) + self.label_name = QtWidgets.QLabel(Dialog) + self.label_name.setText("") + self.label_name.setObjectName("label_name") + self.gridLayout.addWidget(self.label_name, 3, 1, 1, 1) + self.label_4 = QtWidgets.QLabel(Dialog) + self.label_4.setObjectName("label_4") + self.gridLayout.addWidget(self.label_4, 4, 0, 1, 1) + self.label_version = QtWidgets.QLabel(Dialog) + self.label_version.setText("") + self.label_version.setObjectName("label_version") + self.gridLayout.addWidget(self.label_version, 1, 1, 1, 1) + self.label_8 = QtWidgets.QLabel(Dialog) + self.label_8.setObjectName("label_8") + self.gridLayout.addWidget(self.label_8, 6, 0, 1, 1) + self.label_db_dir = QtWidgets.QLabel(Dialog) + self.label_db_dir.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.gridLayout.setColumnMinimumWidth(0, 1) + self.gridLayout.setColumnStretch(0, 1) + self.gridLayout.setColumnStretch(1, 10) + self.gridLayout_2.addLayout(self.gridLayout, 0, 0, 2, 1) + self.btn_getinfo = QtWidgets.QPushButton(Dialog) + self.btn_getinfo.setMinimumSize(QtCore.QSize(0, 60)) + self.btn_getinfo.setObjectName("btn_getinfo") + self.gridLayout_2.addWidget(self.btn_getinfo, 0, 1, 1, 1) + self.checkBox = QtWidgets.QCheckBox(Dialog) + self.checkBox.setText("") + self.checkBox.setObjectName("checkBox") + self.gridLayout_2.addWidget(self.checkBox, 0, 2, 1, 1) + self.btn_db_dir = QtWidgets.QPushButton(Dialog) + self.btn_db_dir.setMinimumSize(QtCore.QSize(0, 60)) + self.btn_db_dir.setObjectName("btn_db_dir") + self.gridLayout_2.addWidget(self.btn_db_dir, 1, 1, 1, 1) + self.checkBox_2 = QtWidgets.QCheckBox(Dialog) + self.checkBox_2.setText("") + self.checkBox_2.setObjectName("checkBox_2") + self.gridLayout_2.addWidget(self.checkBox_2, 1, 2, 1, 1) + self.verticalLayout.addLayout(self.gridLayout_2) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout() + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_2.addItem(spacerItem4) + self.pushButton_3 = QtWidgets.QPushButton(Dialog) + self.pushButton_3.setMinimumSize(QtCore.QSize(0, 60)) + self.pushButton_3.setMaximumSize(QtCore.QSize(100, 16777215)) + self.pushButton_3.setObjectName("pushButton_3") + self.horizontalLayout_2.addWidget(self.pushButton_3) + self.label_tip = QtWidgets.QLabel(Dialog) + self.label_tip.setObjectName("label_tip") + self.horizontalLayout_2.addWidget(self.label_tip) + 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", 50) + self.progressBar.setObjectName("progressBar") + self.horizontalLayout.addWidget(self.progressBar) + self.verticalLayout.addLayout(self.horizontalLayout) + self.horizontalLayout_3.addLayout(self.verticalLayout) + spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem6) + self.verticalLayout_2.addLayout(self.horizontalLayout_3) + spacerItem7 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding) + self.verticalLayout_2.addItem(spacerItem7) + self.verticalLayout_3.addLayout(self.verticalLayout_2) + + self.retranslateUi(Dialog) + QtCore.QMetaObject.connectSlotsByName(Dialog) + + def retranslateUi(self, Dialog): + _translate = QtCore.QCoreApplication.translate + Dialog.setWindowTitle(_translate("Dialog", "Dialog")) + self.btn_help.setText(_translate("Dialog", "使用说明")) + self.label_3.setText(_translate("Dialog", "解密数据库")) + self.label_9.setText(_translate("Dialog", "以下内容为自动获取,如获取失败请手动填写")) + self.label_7.setText(_translate("Dialog", "版本")) + self.label_5.setText(_translate("Dialog", "微信昵称")) + self.label_6.setText(_translate("Dialog", "密钥")) + self.label.setText(_translate("Dialog", "PID")) + self.label_2.setText(_translate("Dialog", "手机号")) + self.label_4.setText(_translate("Dialog", "wxid")) + self.label_8.setText(_translate("Dialog", "微信路径")) + self.btn_getinfo.setText(_translate("Dialog", "获取信息")) + self.btn_db_dir.setText(_translate("Dialog", "设置微信路径")) + self.pushButton_3.setText(_translate("Dialog", "开始启动")) + self.label_tip.setText(_translate("Dialog", "TextLabel")) + self.label_ready.setText(_translate("Dialog", "未就绪")) diff --git a/app/ui/tool/pc_decrypt/pc_decrypt.py b/app/ui/tool/pc_decrypt/pc_decrypt.py new file mode 100644 index 00000000..77f6b6a8 --- /dev/null +++ b/app/ui/tool/pc_decrypt/pc_decrypt.py @@ -0,0 +1,258 @@ +import json +import os.path +import time +import traceback + +from PyQt5.QtCore import pyqtSignal, QThread, QUrl, QFile, QIODevice, QTextStream +from PyQt5.QtGui import QDesktopServices +from PyQt5.QtWidgets import QWidget, QMessageBox, QFileDialog + +from app.DataBase import msg_db, misc_db +from app.DataBase.merge import merge_databases, merge_MediaMSG_databases +from app.decrypt import get_wx_info, decrypt +from app.log import logger +from app.util import path +from . import decryptUi + + +class DecryptControl(QWidget, decryptUi.Ui_Dialog): + DecryptSignal = pyqtSignal(bool) + get_wxidSignal = pyqtSignal(str) + + def __init__(self, parent=None): + super(DecryptControl, self).__init__(parent) + self.setupUi(self) + + self.pushButton_3.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.btn_help.clicked.connect(self.show_help) + self.label_tip.setVisible(False) + self.info = {} + self.lineEdit.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): + try: + file = QFile(':/data/version_list.json') + if file.open(QIODevice.ReadOnly | QIODevice.Text): + stream = QTextStream(file) + content = stream.readAll() + file.close() + VERSION_LIST = json.loads(content) + else: + return + result = get_wx_info.get_info(VERSION_LIST) + print(result) + if result == -1: + QMessageBox.critical(self, "错误", "请登录微信") + elif result == -2: + QMessageBox.critical(self, "错误", "微信版本不匹配\n请更新微信版本为:3.9.8.15") + elif result == -3: + QMessageBox.critical(self, "错误", "WeChat WeChatWin.dll Not Found") + else: + self.ready = True + self.info = result[0] + self.label_key.setText(self.info['key']) + self.lineEdit.setText(self.info['wxid']) + self.label_name.setText(self.info['name']) + self.label_phone.setText(self.info['mobile']) + self.label_pid.setText(str(self.info['pid'])) + self.label_version.setText(self.info['version']) + self.lineEdit.setFocus() + self.checkBox.setChecked(True) + self.get_wxidSignal.emit(self.info['wxid']) + 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.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('已就绪') + except Exception as e: + QMessageBox.critical(self, "未知错误", "请收集报错信息,发起issue解决问题") + logger.error(traceback.format_exc()) + + def set_wxid_(self): + self.info['wxid'] = self.lineEdit.text() + + def set_wxid(self): + self.info['wxid'] = self.lineEdit.text() + QMessageBox.information(self, "ok", f"wxid修改成功{self.info['wxid']}") + + 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.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.lineEdit.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请检查微信版本是否为最新和微信路径是否正确") + self.label_tip.setVisible(True) + self.label_tip.setText('点我之后没有反应那就多等儿吧,不要再点了') + 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请检查微信版本是否为最新和微信路径是否正确") + ) + self.thread2.start() + + def btnEnterClicked(self): + # print("enter clicked") + # 中间可以添加处理逻辑 + # QMessageBox.about(self, "解密成功", "数据库文件存储在app/DataBase/Msg文件夹下") + + self.DecryptSignal.emit(True) + # self.close() + + def setProgressBarMaxNum(self, 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'] + } + try: + os.makedirs('./app/data', exist_ok=True) + with open('./app/data/info.json', 'w', encoding='utf-8') as f: + f.write(json.dumps(dic)) + except: + with open('./info.json', 'w', encoding='utf-8') as f: + f.write(json.dumps(dic)) + # 目标数据库文件 + target_database = "app/DataBase/Msg/MSG.db" + # 源数据库文件列表 + source_databases = [f"app/DataBase/Msg/MSG{i}.db" for i in range(1, 200)] + import shutil + shutil.copy2("app/DataBase/Msg/MSG0.db", target_database) # 使用一个数据库文件作为模板 + # 合并数据库 + try: + merge_databases(source_databases, target_database) + except FileNotFoundError: + QMessageBox.critical(self, "错误", "数据库不存在\n请检查微信版本是否为最新") + + # 音频数据库文件 + target_database = "app/DataBase/Msg/MediaMSG.db" + # 源数据库文件列表 + source_databases = [f"app/DataBase/Msg/MediaMSG{i}.db" for i in range(1, 20)] + shutil.copy2("app/DataBase/Msg/MediaMSG0.db", target_database) # 使用一个数据库文件作为模板 + # 合并数据库 + try: + merge_MediaMSG_databases(source_databases, target_database) + except FileNotFoundError: + QMessageBox.critical(self, "错误", "数据库不存在\n请检查微信版本是否为最新") + + 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): + misc_db.close() + msg_db.close() + # micro_msg_db.close() + # hard_link_db.close() + output_dir = 'app/DataBase/Msg' + 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]) + 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) + self.okSignal.emit('ok') + # self.signal.emit('100') + + +class MyThread(QThread): + signal = pyqtSignal(str) + + def __init__(self): + super(MyThread, self).__init__() + + def __del__(self): + pass + + def run(self): + for i in range(100): + self.signal.emit(str(i)) + time.sleep(0.1) diff --git a/app/ui/tool/toolUI.py b/app/ui/tool/toolUI.py new file mode 100644 index 00000000..3f714817 --- /dev/null +++ b/app/ui/tool/toolUI.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'toolUI.ui' +# +# Created by: PyQt5 UI code generator 5.15.7 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_Dialog(object): + def setupUi(self, Dialog): + Dialog.setObjectName("Dialog") + Dialog.resize(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.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) diff --git a/app/ui/tool/tool_window.py b/app/ui/tool/tool_window.py new file mode 100644 index 00000000..b25a7baf --- /dev/null +++ b/app/ui/tool/tool_window.py @@ -0,0 +1,89 @@ +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 .toolUI import Ui_Dialog + +# 美化样式表 +Stylesheet = """ +QPushButton{ + background-color: #ffffff; +} +QPushButton:hover { + background-color: lightgray; +} +/*去掉item虚线边框*/ +QListWidget, QListView, QTreeWidget, QTreeView { + outline: 0px; + border:none; + background-color:rgb(240,240,240) +} +/*设置左侧选项的最小最大宽度,文字颜色和背景颜色*/ +QListWidget { + min-width: 400px; + max-width: 400px; + min-height: 80px; + max-height: 80px; + color: black; + border:none; +} +QListWidget::item{ + height:80px; + width:80px; +} +/*被选中时的背景颜色和左边框颜色*/ +QListWidget::item:selected { + background: rgb(204, 204, 204); + border-bottom: 4px solid rgb(9, 187, 7); + border-left:none; + color: black; + font-weight: bold; +} +/*鼠标悬停颜色*/ +HistoryPanel::item:hover { + background: rgb(52, 52, 52); +} +""" + + +class ToolWindow(QWidget, Ui_Dialog): + 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.Chat_Icon, '解密', self.listWidget) + contact_item = QListWidgetItem(Icon.Contact_Icon, '别点', self.listWidget) + myinfo_item = QListWidgetItem(Icon.MyInfo_Icon, '别点', self.listWidget) + tool_item = QListWidgetItem(Icon.MyInfo_Icon, '别点', self.listWidget) + decrypt_window = DecryptControl() + decrypt_window.get_wxidSignal.connect(self.get_info_signal) + decrypt_window.DecryptSignal.connect(self.decrypt_success_signal) + self.stackedWidget.addWidget(decrypt_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.stackedWidget.addWidget(label) + self.stackedWidget.addWidget(label) + self.listWidget.setCurrentRow(0) + self.stackedWidget.setCurrentIndex(0) + + def setCurrentIndex(self, row): + print(row) + self.stackedWidget.setCurrentIndex(row) diff --git a/app/util/path.py b/app/util/path.py index 1f2ddbd4..e8d44398 100644 --- a/app/util/path.py +++ b/app/util/path.py @@ -1,7 +1,7 @@ import os import winreg -from app.person_pc import MePC +from app.person import MePC from app.util import dat2pic os.makedirs('./data/image', exist_ok=True) diff --git a/app/web_ui/web.py b/app/web_ui/web.py index 77e8b7d8..5b724e2f 100644 --- a/app/web_ui/web.py +++ b/app/web_ui/web.py @@ -3,10 +3,9 @@ from flask import Flask, render_template, send_file - from app.DataBase import msg_db from app.analysis import analysis -from app.person_pc import ContactPC, MePC +from app.person import ContactPC, MePC app = Flask(__name__) @@ -19,6 +18,7 @@ def index(): # 渲染模板,并传递图表的 HTML 到模板中 return render_template("index.html") + @app.route('/home') def home(): try: @@ -39,12 +39,12 @@ def one(who): wxid = contact.wxid # wxid = 'wxid_lltzaezg38so22' # print('wxid:'+wxid) - world_cloud_data = analysis.wordcloud(wxid,who=who) # 获取与Ta的对话数据 + world_cloud_data = analysis.wordcloud(wxid, who=who) # 获取与Ta的对话数据 # print(world_cloud_data) who = "你" if who == '1' else "TA" with open('wordcloud.html', 'w', encoding='utf-8') as f: f.write(render_template('wordcloud.html', **world_cloud_data)) - return render_template('wordcloud.html', **world_cloud_data,who=who) + return render_template('wordcloud.html', **world_cloud_data, who=who) def set_text(text):