支持导出HTML

This commit is contained in:
shuaikangzhou 2023-11-21 22:23:23 +08:00
parent 55425daa44
commit 12b14316ce
14 changed files with 279 additions and 13301 deletions

View File

@ -4,12 +4,21 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="显示聊天图片">
<list default="true" id="84e65474-7da9-466d-baf3-cc88dde3ffdd" name="变更" comment="支持显示聊天图片">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/components/bubble_message.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/components/bubble_message.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/decrypt/dat2pic.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/decrypt/dat2pic.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/pc_decrypt.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/tool/pc_decrypt/pc_decrypt.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/util/path.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/util/path.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/DataBase/msg.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/msg.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/DataBase/output_pc.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/DataBase/output_pc.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/0.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/1.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/2.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/3.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/4.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/5.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/6.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/data/html/index.html" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/app/person.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/person.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/chat/chat_info.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ui_pc/contact/contactInfo.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ui_pc/contact/contactInfo.py" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -20,8 +29,8 @@
<option name="RECENT_TEMPLATES">
<list>
<option value="Freeze Requirements File" />
<option value="HTML File" />
<option value="Python Script" />
<option value="HTML File" />
</list>
</option>
</component>
@ -280,13 +289,6 @@
<option name="presentableId" value="Default" />
<updated>1672848140146</updated>
</task>
<task id="LOCAL-00037" summary="用stackedWidget实现导航栏">
<created>1698850498765</created>
<option name="number" value="00037" />
<option name="presentableId" value="LOCAL-00037" />
<option name="project" value="LOCAL" />
<updated>1698850498765</updated>
</task>
<task id="LOCAL-00038" summary="修复部分bug">
<created>1698853140384</created>
<option name="number" value="00038" />
@ -623,7 +625,14 @@
<option name="project" value="LOCAL" />
<updated>1700492891759</updated>
</task>
<option name="localTasksCounter" value="86" />
<task id="LOCAL-00086" summary="支持显示聊天图片">
<created>1700574536723</created>
<option name="number" value="00086" />
<option name="presentableId" value="LOCAL-00086" />
<option name="project" value="LOCAL" />
<updated>1700574536724</updated>
</task>
<option name="localTasksCounter" value="87" />
<servers />
</component>
<component name="UnknownFeatures">
@ -659,7 +668,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="修复无法查找wxid的bug" />
<MESSAGE value="修改UI" />
<MESSAGE value="新增联系人头像组件" />
<MESSAGE value="头像支持显示二进制" />
@ -684,7 +692,8 @@
<MESSAGE value="文字消息设置圆角" />
<MESSAGE value="更新wx选择的路径" />
<MESSAGE value="显示聊天图片" />
<option name="LAST_COMMIT_MESSAGE" value="显示聊天图片" />
<MESSAGE value="支持显示聊天图片" />
<option name="LAST_COMMIT_MESSAGE" value="支持显示聊天图片" />
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="true" />
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="true" />
</component>
@ -708,17 +717,17 @@
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>100</line>
<line>103</line>
<option name="timeStamp" value="10" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>98</line>
<line>101</line>
<option name="timeStamp" value="11" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/person.py</url>
<line>99</line>
<line>102</line>
<option name="timeStamp" value="12" />
</line-breakpoint>
</breakpoints>

View File

@ -49,10 +49,14 @@ def get_messages(username_):
'''
result = []
for cur in cursor:
cur.execute(sql, [username_])
result_ = cur.fetchall()
# print(len(result))
result += result_
try:
lock.acquire(True)
cur.execute(sql, [username_])
result_ = cur.fetchall()
# print(len(result))
result += result_
finally:
lock.release()
result.sort(key=lambda x: x[5])
return result

View File

@ -5,6 +5,7 @@ from PyQt5.QtCore import pyqtSignal, QThread
from . import msg
from ..log import log
from ..person import MePC
if not os.path.exists('./data/聊天记录'):
os.mkdir('./data/聊天记录')
@ -24,6 +25,7 @@ class Output(QThread):
def __init__(self, contact, parent=None, type_=DOCX):
super().__init__(parent)
self.last_timestamp = 0
self.sec = 2 # 默认1000秒
self.contact = contact
self.ta_username = contact.wxid
@ -51,9 +53,239 @@ class Output(QThread):
df.to_csv(filename, encoding='utf-8')
self.okSignal.emit('ok')
def to_html(self):
origin_docx_path = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}"
if not os.path.exists(origin_docx_path):
os.mkdir(origin_docx_path)
messages = msg.get_messages(self.contact.wxid)
filename = f"{os.path.abspath('.')}/data/聊天记录/{self.contact.remark}/{self.contact.remark}.html"
f = open(filename, 'w', encoding='utf-8')
html_head = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
*{
padding: 0;
margin: 0;
}
body{
height: 100vh;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.container{
height: 760px;
width: 900px;
border-radius: 4px;
border: 0.5px solid #e0e0e0;
background-color: #f5f5f5;
display: flex;
flex-flow: column;
overflow: hidden;
}
.content{
width: calc(100% - 40px);
padding: 20px;
overflow-y: scroll;
flex: 1;
}
.content:hover::-webkit-scrollbar-thumb{
background:rgba(0,0,0,0.1);
}
.bubble{
max-width: 400px;
padding: 10px;
border-radius: 5px;
position: relative;
color: #000;
word-wrap:break-word;
word-break:normal;
}
.item-left .bubble{
margin-left: 15px;
background-color: #fff;
}
.item-left .bubble:before{
content: "";
position: absolute;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-top: 10px solid transparent;
border-right: 10px solid #fff;
border-bottom: 10px solid transparent;
left: -20px;
}
.item-right .bubble{
margin-right: 15px;
background-color: #9eea6a;
}
.item-right .bubble:before{
content: "";
position: absolute;
width: 0;
height: 0;
border-left: 10px solid #9eea6a;
border-top: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 10px solid transparent;
right: -20px;
}
.item{
margin-top: 15px;
display: flex;
width: 100%;
}
.item.item-right{
justify-content: flex-end;
}
.item.item-center{
justify-content: center;
}
.item.item-center span{
font-size: 12px;
padding: 2px 4px;
color: #fff;
background-color: #dadada;
border-radius: 3px;
-moz-user-select:none; /*火狐*/
-webkit-user-select:none; /*webkit浏览器*/
-ms-user-select:none; /*IE10*/
-khtml-user-select:none; /*早期浏览器*/
user-select:none;
}
.avatar img{
width: 42px;
height: 42px;
border-radius: 50%;
}
.input-area{
border-top:0.5px solid #e0e0e0;
height: 150px;
display: flex;
flex-flow: column;
background-color: #fff;
}
textarea{
flex: 1;
padding: 5px;
font-size: 14px;
border: none;
cursor: pointer;
overflow-y: auto;
overflow-x: hidden;
outline:none;
resize:none;
}
.button-area{
display: flex;
height: 40px;
margin-right: 10px;
line-height: 40px;
padding: 5px;
justify-content: flex-end;
}
.button-area button{
width: 80px;
border: none;
outline: none;
border-radius: 4px;
float: right;
cursor: pointer;
}
/* 设置滚动条的样式 */
::-webkit-scrollbar {
width:10px;
}
/* 滚动槽 */
::-webkit-scrollbar-track {
-webkit-box-shadow:inset006pxrgba(0,0,0,0.3);
border-radius:8px;
}
/* 滚动条滑块 */
::-webkit-scrollbar-thumb {
border-radius:10px;
background:rgba(0,0,0,0);
-webkit-box-shadow:inset006pxrgba(0,0,0,0.5);
}
</style>
</head>
<body>
<div class="container">
<div class="content">
'''
f.write(html_head)
MePC().avatar.save(os.path.join(origin_docx_path, 'myhead.png'))
self.contact.avatar.save(os.path.join(origin_docx_path, 'tahead.png'))
for message in messages:
type_ = message[2]
str_content = message[7]
str_time = message[8]
# print(type_, type(type_))
is_send = message[4]
avatar = MePC().avatar_path if is_send else self.contact.avatar_path
timestamp = message[5]
if type_ == 1:
if self.is_5_min(timestamp):
f.write(
f'''
<div class="item item-center"><span>{str_time}</span></div>
'''
)
if is_send:
f.write(
f'''
<div class="item item-right">
<div class="bubble bubble-right">{str_content}</div>
<div class="avatar">
<img src="myhead.png" />
</div>
</div>
'''
)
else:
f.write(
f'''
<div class="item item-left">
<div class="avatar">
<img src="tahead.png" />
</div>
<div class="bubble bubble-left">{str_content}
</div>
</div>
'''
)
html_end = '''
</div>
</div>
</body>
</html>
'''
f.write(html_end)
f.close()
self.okSignal.emit('ok')
def is_5_min(self, timestamp):
if abs(timestamp - self.last_timestamp) > 300:
self.last_timestamp = timestamp
return True
return False
def run(self):
if self.output_type == self.DOCX:
return
elif self.output_type == self.CSV:
# print("线程导出csv")
self.to_csv(self.ta_username, "path")
elif self.output_type == self.HTML:
self.to_html()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,252 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
<title></title>
<!--
@time: 2018-08-04
@version: 0.0.1
@author: Mortal
-->
<style type="text/css">
/*
* 说明:
* 标注为慎删的属性暂时认定可以删除,即在作者测试的环境下删除暂时没有影响,但不代表所有环境下删除都没有影响
* 其他属性一概不可以删除
*/
html,
body {
height: 100%;
}
body,
ul,
li,
a,
p,
div {
/*慎删*/
padding: 0px;
margin: 0px;
}
#wrap {
overflow: hidden;
width: 100%;
}
#main {
top: 0;
position: relative;
}
.page {
/*谨删*/
width: 100%;
margin: 0;
}
#pageUl {
position: fixed;
right: 10px;
}
</style>
</head>
<body>
<!--
每个全屏页面div的class为page其中的图片的class为pageImg
ul为右侧的导航栏
pageUlLi和page的数目必须相等修改数目时还应修改最下面js鼠标悬停的跳转代码
-->
<div id="wrap">
<div id="main">
<ul id="pageUl" type="circle">
<li id="pageUlLi1" class="pageUlLi" style="color: red;"> </li>
<li id="pageUlLi2" class="pageUlLi"> </li>
<li id="pageUlLi3" class="pageUlLi"> </li>
<li id="pageUlLi4" class="pageUlLi"> </li>
<li id="pageUlLi5" class="pageUlLi"> </li>
<li id="pageUlLi6" class="pageUlLi"> </li>
<li id="pageUlLi7" class="pageUlLi"> </li>
</ul>
<div id="page1" class="page">
<iframe src="0.html" frameborder="0" height="100%"
width="100%"></iframe>
</div>
<div id="page2" class="page">
<iframe src="1.html" frameborder="0" height="100%"
width="100%"></iframe>
</div>
<div style="background-color: #8a6d3b" id="page3" class="page">
<iframe src="2.html" frameborder="0" id="iframe0" height="100%"
width="100%"></iframe>
</div>
<div style="background-color: #337ab7" id="page4" class="page">
<iframe src="3.html" frameborder="0" id="iframe3" height="100%"
width="100%"></iframe>
</div>
<div style="background-color: #337ab7" id="page5" class="page">
<iframe src="4.html" frameborder="0" id="iframe4" height="100%"
width="100%"></iframe>
</div>
<div style="background-color: #337ab7" id="page6" class="page">
<iframe src="5.html" frameborder="0" id="iframe5" height="100%"
width="100%"></iframe>
</div>
<div style="background-color: #337ab7" id="page7" class="page">
<iframe src="6.html" frameborder="0" id="iframe6" height="100%"
width="100%"></iframe>
</div>
</div>
</div>
</body>
<script type="text/javascript">
//改变窗口大小时调整图片大小
window.onload = resizeImages;
window.onresize = resizeImages;
function resizeImages() {
$(function (e) {
var screenWeight = document.documentElement.clientWidth;
var screenHeight = document.documentElement.clientHeight;
$("[name=pageImg]").css("width", screenWeight).css("height", screenHeight);
$("#pageUl").css("bottom", screenHeight >> 1);
});
}
var index = 1;
var curIndex = 1;
var wrap = document.getElementById("wrap");
var main = document.getElementById("main");
var hei = document.body.clientHeight;
wrap.style.height = hei + "px";
var obj = document.getElementsByTagName("div");
for (var i = 0; i < obj.length; i++) {
if (obj[i].className == 'page') {
obj[i].style.height = hei + "px";
}
}
var pageNum = document.querySelectorAll(".page").length;
//如果不加时间控制,滚动会过度灵敏,一次翻好几屏
var startTime = 0, //翻屏起始时间
endTime = 0,
now = 0;
//浏览器兼容
if ((navigator.userAgent.toLowerCase().indexOf("firefox") != -1)) {
document.addEventListener("DOMMouseScroll", scrollFun, false);
} else if (document.addEventListener) {
document.addEventListener("mousewheel", scrollFun, false);
} else if (document.attachEvent) {
document.attachEvent("onmousewheel", scrollFun);
} else {
document.onmousewheel = scrollFun;
}
//滚动事件处理函数
function scrollFun(event) {
startTime = new Date().getTime();
var delta = event.detail || (-event.wheelDelta);
//mousewheel事件中的 “event.wheelDelta” 属性值:返回的如果是正值说明滚轮是向上滚动
//DOMMouseScroll事件中的 “event.detail” 属性值:返回的如果是负值说明滚轮是向上滚动
if ((endTime - startTime) < -1000) {
if (delta > 0 && parseInt(main.offsetTop) > -(hei * (pageNum - 1))) {
//向下滚动
index++;
toPage(index);
}
if (delta < 0 && parseInt(main.offsetTop) < 0) {
//向上滚动
index--;
toPage(index);
}
endTime = new Date().getTime();
} else {
event.preventDefault();
}
}
function toPage(idx) {
//jquery实现动画效果
if(idx!=curIndex){
index=idx
var delta=idx - curIndex;
now = now - delta * hei;
$("#main").animate({
top: (now + 'px')
}, 500);
curIndex = idx;
//更改列表的选中项
$(".pageUlLi").css("color", "black");
$("#pageUlLi" + idx).css("color", "red");
}
}
// //鼠标悬停翻页
// document.getElementById("pageUlLi1").onmouseover = function () {
// toPage(1);
// }
// document.getElementById("pageUlLi2").onmouseover = function () {
// toPage(2);
// }
// document.getElementById("pageUlLi3").onmouseover = function () {
// toPage(3);
// }
// document.getElementById("pageUlLi4").onmouseover = function () {
// toPage(4);
// }
// document.getElementById("pageUlLi5").onmouseover = function () {
// toPage(5);
// }
//鼠标点击翻页
document.getElementById("pageUlLi1").onclick = function () {
toPage(1);
}
document.getElementById("pageUlLi2").onclick = function () {
toPage(2);
}
document.getElementById("pageUlLi3").onclick = function () {
toPage(3);
}
document.getElementById("pageUlLi4").onclick = function () {
toPage(4);
}
document.getElementById("pageUlLi5").onclick = function () {
toPage(5);
}
</script>
</html>

View File

@ -55,6 +55,7 @@ def singleton(cls):
class MePC:
def __init__(self):
self.avatar = QPixmap(Icon.Default_avatar_path)
self.avatar_path = 'D:\Project\Python\WeChatMsg\\app\data\icons\default_avatar.svg'
self.wxid = ''
self.wx_dir = ''
self.name = ''
@ -82,6 +83,7 @@ class ContactPC:
self.smallHeadImgUrl = contact_info.get('smallHeadImgUrl')
self.smallHeadImgBLOG = b''
self.avatar = QPixmap()
self.avatar_path = 'D:\Project\Python\WeChatMsg\\app\data\icons\default_avatar.svg'
def set_avatar(self, img_bytes):
if not img_bytes:
@ -91,6 +93,7 @@ class ContactPC:
self.avatar.loadFromData(img_bytes, format='PNG')
else:
self.avatar.loadFromData(img_bytes, format='jfif')
self.avatar.scaled(60, 60, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)

View File

@ -100,6 +100,7 @@ class ChatInfo(QWidget):
)
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

View File

@ -114,12 +114,13 @@ class ContactInfo(QWidget, Ui_Form):
self.outputThread = Output(self.contact, type_=Output.CSV)
print('导出csv')
elif self.sender() == self.toHtmlAct:
print('功能暂未实现')
QMessageBox.warning(self,
"别急别急",
"马上就实现该功能"
)
return
self.outputThread = Output(self.contact, type_=Output.HTML)
# print('功能暂未实现')
# QMessageBox.warning(self,
# "别急别急",
# "马上就实现该功能"
# )
# return
self.outputThread.progressSignal.connect(self.output_progress)
self.outputThread.rangeSignal.connect(self.set_progressBar_range)
self.outputThread.okSignal.connect(self.hide_progress_bar)