import os import re import time import docx import numpy as np import pandas as pd import xmltodict from PyQt5.QtCore import * from docx import shared from docx.enum.table import WD_ALIGN_VERTICAL from docx.enum.text import WD_COLOR_INDEX, WD_PARAGRAPH_ALIGNMENT from docxcompose.composer import Composer from . import data # import data def IS_5_min(last_m, now_m): """ #! 判断两次聊天时间是不是大于五分钟 #! 若大于五分钟则显示时间 #! 否则不显示 """ '''两次聊天记录时间差,单位是秒''' dt = now_m - last_m return abs(dt // 1000) >= 300 def time_format(timestamp): ''' #! 将字符串类型的时间戳转换成日期 #! 返回格式化的时间字符串 #! %Y-%m-%d %H:%M:%S ''' timestamp = timestamp / 1000 time_tuple = time.localtime(timestamp) return time.strftime("%Y-%m-%d %H:%M:%S", time_tuple) class Output(QThread): """ 发送信息线程 """ progressSignal = pyqtSignal(int) rangeSignal = pyqtSignal(int) okSignal = pyqtSignal(int) i = 1 CSV = 0 DOCX = 1 HTML = 2 def __init__(self, Me, ta_u, parent=None, type_=DOCX): super().__init__(parent) self.Me = Me self.sec = 2 # 默认1000秒 self.ta_username = ta_u self.my_avatar = self.Me.my_avatar self.ta_avatar = data.get_avator(ta_u) self.msg_id = 0 self.output_type = type_ self.total_num = 0 def merge_docx(self, conRemark, n): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{conRemark}" all_file_path = [] for i in range(n): file_name = f"{conRemark}{i}.docx" all_file_path.append(origin_docx_path + '/' + file_name) filename = f"{conRemark}.docx" # print(all_file_path) doc = docx.Document() doc.save(origin_docx_path + '/' + filename) master = docx.Document(origin_docx_path + '/' + filename) middle_new_docx = Composer(master) num = 0 for word in all_file_path: word_document = docx.Document(word) word_document.add_page_break() if num != 0: middle_new_docx.append(word_document) num = num + 1 middle_new_docx.save(origin_docx_path + '/' + filename) def progress(self, value): self.i += 1 # 处理完成之后将多个文件合并 if self.i == self.total_num: QThread.sleep(1) conRemark = data.get_conRemark(self.ta_username) self.progressSignal.emit(self.total_num - 1) self.merge_docx(conRemark, self.n) print('ok') self.progressSignal.emit(self.total_num) self.okSignal.emit(1) self.progressSignal.emit(self.i) def to_csv(self, conRemark, path): origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{conRemark}" messages = data.get_all_message(self.ta_username) # print(messages) self.Child0 = ChildThread(self.Me, self.ta_username, messages, conRemark, 0, type_=ChildThread.CSV) self.Child0.progressSignal.connect(self.progress) self.Child0.start() print("成功导出CSV文件:", origin_docx_path) def run(self): conRemark = data.get_conRemark(self.ta_username) data.mkdir(f"{os.path.abspath('.')}/data/聊天记录/{conRemark}") if self.output_type == self.DOCX: self.Child = {} if 1: messages = data.get_all_message(self.ta_username) self.total_num = len(messages) self.rangeSignal.emit(self.total_num) l = len(messages) self.n = 10 for i in range(self.n): q = i * (l // self.n) p = (i + 1) * (l // self.n) if i == self.n - 1: p = l len_data = messages[q:p] # self.to_docx(len_data, i, conRemark) self.Child[i] = ChildThread(self.Me, self.ta_username, len_data, conRemark, i) self.Child[i].progressSignal.connect(self.progress) self.Child[i].start() elif self.output_type == self.CSV: # print("线程导出csv") # self.to_csv(self.ta_username, "path") origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.ta_username}" messages = data.get_all_message(self.ta_username) # print(messages) self.Child0 = ChildThread(self.Me, self.ta_username, messages, conRemark, 0, type_=ChildThread.CSV) self.Child0.progressSignal.connect(self.progress) self.Child0.run() self.okSignal.emit(1) class ChildThread(QThread): """ 子线程,用于导出部分聊天记录 """ progressSignal = pyqtSignal(int) rangeSignal = pyqtSignal(int) i = 1 CSV = 0 DOCX = 1 HTML = 2 def __init__(self, Me, ta_u, message, conRemark, num, parent=None, type_=DOCX): super().__init__(parent) self.Me = Me self.sec = 2 # 默认1000秒 self.ta_username = ta_u self.num = num self.my_avatar = self.Me.my_avatar self.ta_avatar = data.get_avator(ta_u) self.conRemark = conRemark self.message = message self.msg_id = 0 self.output_type = type_ def create_table(self, doc, isSend): ''' #! 创建一个1*2表格 #! isSend = 1 (0,0)存聊天内容,(0,1)存头像 #! isSend = 0 (0,0)存头像,(0,1)存聊天内容 #! 返回聊天内容的坐标 ''' table = doc.add_table(rows=1, cols=2, style='Normal Table') table.cell(0, 1).height = shared.Inches(0.5) table.cell(0, 0).height = shared.Inches(0.5) text_size = 1 if isSend: '''表格右对齐''' table.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT avatar = table.cell(0, 1).paragraphs[0].add_run() '''插入头像,设置头像宽度''' avatar.add_picture(self.my_avatar, width=shared.Inches(0.5)) '''设置单元格宽度跟头像一致''' table.cell(0, 1).width = shared.Inches(0.5) content_cell = table.cell(0, 0) '''聊天内容右对齐''' content_cell.paragraphs[0].paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT else: avatar = table.cell(0, 0).paragraphs[0].add_run() avatar.add_picture(self.ta_avatar, width=shared.Inches(0.5)) '''设置单元格宽度''' table.cell(0, 0).width = shared.Inches(0.5) content_cell = table.cell(0, 1) '''聊天内容垂直居中对齐''' content_cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER return content_cell def text(self, doc, isSend, message, status): if status == 5: message += '(未发出) ' content_cell = self.create_table(doc, isSend) content_cell.paragraphs[0].add_run(message) content_cell.paragraphs[0].font_size = shared.Inches(0.5) # self.self_text.emit(message) if isSend: p = content_cell.paragraphs[0] p.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT doc.add_paragraph() def image(self, doc, isSend, Type, content, imgPath): ''' #! 插入聊天图片 #! isSend = 1 只有缩略图 #! isSend = 0 有原图 :param doc: :param isSend: :param Type: :param content: :param imgPath: :return: ''' content = self.create_table(doc, isSend) run = content.paragraphs[0].add_run() if Type == 3: imgPath = imgPath.split('th_')[1] imgPath = f'./app/data/image2/{imgPath[0:2]}/{imgPath[2:4]}/th_{imgPath}' imgPath = data.clearImagePath(imgPath) try: run.add_picture(f'{imgPath}', height=shared.Inches(2)) doc.add_paragraph() except Exception: print("Error!image") # run.add_picture(f'{Path}/{imgPath}', height=shared.Inches(2)) def emoji(self, doc, isSend, content, imgPath): ''' #! 添加表情包 :param isSend: :param content: :param imgPath: :return: ''' imgPath = data.get_emoji(imgPath) if 1: is_Exist = os.path.exists(imgPath) self.image(doc, isSend, Type=47, content=content, imgPath=imgPath) def wx_file(self, doc, isSend, content, status): ''' #! 添加微信文件 :param isSend: :param content: :param status: :return: ''' pattern = re.compile(r"