文章目录

  • 展示
  • 参考文章
  • html + js + css
  • python
  • 代码地址
    • user目录下的 chat.py为主页面, 图片都在user/images/filetype下面
  • 相关资源

展示

纯html - web网页
QWebEngineWidget + Html :


参考文章

(搜索)

  1. 聊天界面html+css+javascript
    -https://blog.csdn.net/lutrra/article/details/120390780

  2. html 自动包裹内容,CSS 实现div宽度根据内容自适应
    -https://blog.csdn.net/weixin_32052253/article/details/117725804

  3. HTML/CSS float 属性
    -https://www.w3school.com.cn/cssref/pr_class_float.asp

  4. Vue input textarea自动滚动到最底部
    -https://blog.csdn.net/weixin_42776111/article/details/109194393?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2aggregatepagefirst_rank_ecpm_v1~rank_v31_ecpm-6-109194393-null-null.pc_agg_new_rank&utm_term=textarea%E6%BB%9A%E5%8A%A8%E8%87%B3%E5%BA%95%E9%83%A8&spm=1000.2123.3001.4430

  5. keyframes_CSS淡入淡出
    -https://blog.csdn.net/culuo8053/article/details/107910312

  6. CSS3实现毛玻璃完美效果
    -https://www.cnblogs.com/ivan5277/p/10007273.html

PyQt5html 双向通信
python负责网络通信和API(html没有python照样可以)

html + js + css

display: inline-block 可以解决父div包裹div问题, 避免出现多个消息出现在一行

chat.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Chat</title>
</head>
<style>*{padding: 0;margin: 0;font-family: Consolas,Microsoft YaHei UI,serif;font-size: 22px;}@keyframesfadeIn{0% {opacity:0}100% {opacity:1}}.clearfix::after{content: "";display: block;clear: both;width: 0;height: 0;line-height: 0;visibility: hidden;}.chat_middle{width: 100%;height: 400px;position: relative;box-sizing: border-box;overflow: auto;border-width: 0;}.chat_left{width: 100%;height: auto;min-height: 100px;margin-top: 20px;margin-bottom: 20px;animation-name: fadeIn;animation-duration: 1.5s;zoom:1;display: inline-block;}img.chat_left_img{width: 50px;height: 50px;float: left;margin-top: 10px;margin-left: 10px;margin-right: 10px;border-radius: 25px;}.chat_left_item{width: auto;max-width: calc(100% - 70px - 15px);height: auto;float: left;margin-top: 10px;}.chat_left_item .chat_left_chat{float: left;}.chat_left_item .chat_left_content{padding: 15px; /* changed */margin-top: 10px;background-color: #f4f5f7;color: black;display: inline-block;overflow: auto;border-radius: 0 10px 10px 10px;word-wrap:break-word;word-break:break-all;position: relative;box-shadow: 0 5px 15px rgba(20, 20, 20, 0.8);align-items: center;}.chat_right{width: 100%;height: auto;min-height: 100px;margin-top: 20px;margin-bottom: 20px;animation-name: fadeIn;animation-duration: 1.5s;zoom:1;display: inline-block;}img.chat_right_img{width: 50px;height: 50px;float: right;margin-top: 10px;margin-left: 10px;margin-right: 10px;border-radius: 25px;}.chat_right_item{width: auto;max-width: calc(100% - 70px - 15px);height: auto;float: right;margin-top: 10px;}.chat_time{width: 100%;text-align: center;color: gray;}.chat_right_name{color: darkgray;text-align: right;}.chat_left_name{color: darkgray;text-align: left;}.chat_right_content{float: right;padding: 15px; /* changed */margin-top: 10px;border-radius: 10px 0 10px 10px;background: linear-gradient(rgba(0, 255, 12, 255) 0%, rgba(16, 211, 22, 255) 100%);color: black;word-wrap:break-word;word-break:break-all;position: relative;box-shadow: 0 5px 15px rgba(20, 20, 20, 0.8);display: flex;align-items: center;}.split{width: 100%;height: 1px;visibility: hidden;}.file{width:100%;height:auto;min-height: 50px;padding:10px; background-color: #f4f5f7;border: 1px;display: inline-block;}.fileinfo{ float:left;}.fileicon{ float:right; width:50px;height: 50px;}.filename{word-wrap:break-word;word-break:break-all;overflow: hidden;color: black;display: inline-block;}.filesize{width:100px;height: auto;font-size:12px;color: rgb(153, 153, 153);text-align: end;}</style>
<body><div class="chat_middle" id="chat_middle_item"></div><script>// 成功发送const send_message = document.getElementById("chat_middle_item");let _link = -1;function server_message(content){const ans = '<img class="chat_left_img clearfix" src="data:images/server.png">' +'<div class="chat_left_item">' +'<div class="chat_left_name clearfix">Server</div>' +'<span class="chat_left_content clearfix">' + content + '</span>'+ '</div>';const oLi = document.createElement("div");oLi.setAttribute("class","chat_left");oLi.innerHTML=ans;const _split = document.createElement("div");_split.setAttribute("class", "split");send_message.append(oLi);send_message.append(_split);send_message.scrollTop = send_message.scrollHeight;}function user_message(name, content, is_self){let ans;if (is_self) {ans = '<img class="chat_right_img clearfix" src="data:images/chat_user.png">' +'<div class="chat_right_item">' +'<div class="chat_right_name clearfix">' + name + '</div>' +'<span class="chat_right_content clearfix">' + content + '</span>'+ '</div>';} else {ans = '<img class="chat_left_img clearfix" src="data:images/chat_user.png">' +'<div class="chat_left_item">' +'<div class="chat_left_name clearfix">' + name + '</div>' +'<span class="chat_left_content clearfix">' + content + '</span>' +'<div class="split"></div>'+ '</div>';}const oLi = document.createElement("div");oLi.setAttribute("class","chat_right");oLi.innerHTML=ans;send_message.append(oLi);send_message.scrollTop = send_message.scrollHeight;}function file(filename, _size, ico_path, link, username, is_self) {const data = "<div class='file' οnclick='set_anchor(" + link + ");'>" +"<div class='fileinfo'>" +"<span class='filename'>" + filename + "</span>" +"<br>" +"<span class='filesize'>" + _size + "</span> " +"</div>" +"<img class='fileicon' src='" + ico_path + "'>" +"</div>"user_message(username, data, is_self)}function time(time_str){const time = document.createElement("div")time.innerHTML = "<div class='chat_time'>"+time_str+"</div>"send_message.append(time)}function height_changed(height) {send_message.style.height = height + "px";}function set_anchor( index ) {_link = index}function reset_anchor() {set_anchor(-1);}function get_anchor() {return _link;}</script>
</body>
</html>

python

PyQt5.QtWebEngineWidgets.QWebEngineView.load(QtCore.QUrl(QtCore.QFileInfo(>> file string <<).absoluteFilePath()))可以解决相对路径无法读取问题

import os
import sys
import logging
import timefrom PyQt5 import QtWebEngineWidgets, QtCore, QtWidgets, QtGuidef convert(byte, fine=False):if not isinstance(byte, (int, float)):byte = len(byte)DEI = f"{byte} bytes"units = ["b","Kb","Mb","Gb","Tb","Pb","Eb"]index = 0while True:if byte < 1024 or index + 1 >= len(units):breakbyte /= 1024index += 1if index == 0:return DEIelse:if fine:return "%0.1f%s(%s)" % (byte, units[index], DEI)else:return "%0.1f%s" % (byte, units[index])def to_logging(command):def logs(*args, **kwargs):try:_result = command(*args, **kwargs)if _result is None:return Truereturn _resultexcept:logging.exception("")return logswith open("chat.html", "r", encoding="utf-8") as f:html = f.read()def omit(string: str, max_length: int, d_text: str = "...") -> str:if len(d_text) > max_length:d_text = d_text[: max_length]if len(string) > max_length:return string[:max_length - len(d_text)] + d_textreturn stringclass ImageLoader:path = "images/filetype"unkown = os.path.join(path, "unknown.png").replace("\\", "/")filedict = {}def __init__(self):for filename in os.listdir(self.path):filepath = self.join(filename)filetype, _ = os.path.splitext(filename)self.filedict[filetype.lower()] = filepathdef join(self, filename):return os.path.join(self.path, filename).replace("\\", "/")def get_suffix_img(self, suf):return self.filedict.get(suf, self.unkown)@staticmethoddef get_suf(filename):_, suf = os.path.splitext(filename)return suf.lstrip(".").lower()def get(self, filename):return self.get_suffix_img(self.get_suf(filename))class QChatWidget(QtWebEngineWidgets.QWebEngineView):img = ImageLoader()anchorClicked = QtCore.pyqtSignal(int)user_message = QtCore.pyqtSignal(bool, str, str)server_message = QtCore.pyqtSignal(str)add_file = QtCore.pyqtSignal(str, str, bool, str, int)boundary_time = 60 * 5def __init__(self, parent=None):super(QChatWidget, self).__init__(parent)self.setWindowTitle('chat')self.setGeometry(5, 30, 468, 662)self.load(QtCore.QUrl(QtCore.QFileInfo("chat.html").absoluteFilePath()))self.startTimer(100)self.anchor = -1self.user_message.connect(self._user_message)self.server_message.connect(self._server_message)self.add_file.connect(self._add_file)self.record_time = 0def timerEvent(self, *args) -> None:self.JavaScript(f"height_changed({self.size().height()});")self.JavaScript("get_anchor();", self.checkAnchor)def check_time(self):if time.time() - self.record_time > self.boundary_time:self.record_time = time.time()self.JavaScript(f"time({repr(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(self.record_time)))})")def checkAnchor(self, index: int):"""function set_anchor( index ) {_link = index}function reset_anchor() {set_anchor(-1);}function get_anchor() {return _link;}"""if isinstance(index, int) and index != self.anchor:self.anchorClicked.emit(index)self.JavaScript("reset_anchor();")def JavaScript(self, *args, **kwargs):self.page().runJavaScript(*args, **kwargs)def _user_message(self, _is_self: bool, content: str, name: str):""" function user_message(name, content, is_self); """self.check_time()self.JavaScript(f"user_message({repr(name)}, {repr(omit(content, 400))}, {str(_is_self).lower()});")def _server_message(self, content: str):""" function server_message(content); """self.check_time()self.JavaScript(f"server_message({repr(content)});")def _add_file(self, filename: str, size: str, _is_self: bool, name: str, index: int):""" function file(filename, _size, ico_path, link, username, is_self); """ico_path = self.img.get(filename)filename = omit(filename, 50)self.check_time()self.JavaScript(f"file({repr(filename)}, {repr(size)}, {repr(ico_path)}, {index}, {repr(name)}, {str(_is_self).lower()});")def contextMenuEvent(self, a0: QtGui.QContextMenuEvent) -> None:passclass QChat(QtWidgets.QWidget):def __init__(self, parent=None, username=""):super(QChat, self).__init__(parent)self.setObjectName("Form")self.username = usernameself.resize(591, 670)self.gridLayout = QtWidgets.QGridLayout(self)self.gridLayout.setObjectName("gridLayout")self.web = QChatWidget(self)font = QtGui.QFont()font.setFamily("Consolas")font.setPointSize(12)self.web.setFont(font)self.web.setObjectName("web")self.gridLayout.addWidget(self.web, 2, 1, 2, 1)self.line_2 = QtWidgets.QFrame(self)self.line_2.setFrameShape(QtWidgets.QFrame.HLine)self.line_2.setFrameShadow(QtWidgets.QFrame.Sunken)self.line_2.setObjectName("line_2")self.gridLayout.addWidget(self.line_2, 1, 1, 1, 1)self.textEdit = QtWidgets.QTextEdit(self)self.textEdit.setObjectName("textEdit")font = QtGui.QFont()font.setFamily("Consolas")font.setPointSize(13)self.textEdit.setFont(font)self.gridLayout.addWidget(self.textEdit, 4, 1, 1, 1)self.horizontalLayout = QtWidgets.QHBoxLayout()self.horizontalLayout.setObjectName("horizontalLayout")spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)self.horizontalLayout.addItem(spacerItem)self.uploadButton = QtWidgets.QPushButton(self)font = QtGui.QFont()font.setFamily("Consolas")font.setPointSize(10)self.uploadButton.setFont(font)icon = QtGui.QIcon()icon.addPixmap(QtGui.QPixmap("images/upload.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)self.uploadButton.setIcon(icon)self.uploadButton.setObjectName("uploadButton")self.horizontalLayout.addWidget(self.uploadButton)self.sendButton = QtWidgets.QPushButton(self)self.sendButton.setEnabled(False)self.sendButton.setStyleSheet("""
QPushButton{background:#fffff;border-size: 0;
}
QPushButton:hover{background: rgb(205, 205, 205);
}""")icon1 = QtGui.QIcon()icon1.addPixmap(QtGui.QPixmap("images/send.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)self.sendButton.setIcon(icon1)self.sendButton.setObjectName("sendButton")self.horizontalLayout.addWidget(self.sendButton)self.gridLayout.addLayout(self.horizontalLayout, 5, 1, 1, 1)self.line = QtWidgets.QFrame(self)self.line.setFrameShape(QtWidgets.QFrame.HLine)self.line.setFrameShadow(QtWidgets.QFrame.Sunken)self.line.setObjectName("line")self.gridLayout.addWidget(self.line, 3, 1, 1, 1)self.label = QtWidgets.QLabel(self)font = QtGui.QFont()font.setFamily("Consolas")font.setPointSize(20)self.label.setFont(font)self.label.setObjectName("label")self.gridLayout.addWidget(self.label, 0, 1, 1, 1)self.retranslateUi()QtCore.QMetaObject.connectSlotsByName(self)self.textEdit.textChanged.connect(self.textChanged)self.sendButton.clicked.connect(self.send)self.uploadButton.clicked.connect(self.sendfile)self.sendButton.setStyleSheet("""QPushButton{border-size: 10px solid rgb(200, 200, 200);border-radius: 15px;padding: 5px 10px 5px 10px;border: 2px groove gray;border-style: outset;}QPushButton:hover{background:rgb(220, 220, 220);}QPushButton:pressed{background:rgb(210, 210, 210);}""")self.uploadButton.setStyleSheet("""QPushButton{border-size: 10px solid rgb(200, 200, 200);border-radius: 15px;padding: 5px 10px 5px 10px;border: 2px groove gray;border-style: outset;}QPushButton:hover{background:rgb(220, 220, 220);}QPushButton:pressed{background:rgb(210, 210, 210);}""")def user_message(self, _is_self: bool, content: str, name: str):self.web.user_message.emit(_is_self, content, name)def server_message(self, content: str):self.web.server_message.emit(content)def add_file(self, filename: str, size: str, _is_self: bool, name: str, index: int):self.web.add_file.emit(filename, size, _is_self, name, index)def getText(self) -> str:return self.textEdit.toPlainText().strip()def send(self) -> None:self.user_message(True, self.getText(), self.username)  ##self.textEdit.clear()def textChanged(self, *args) -> None:self.sendButton.setEnabled(0 < len(self.getText()) < 400)def setTitle(self, title: str) -> None:self.label.setText(QtCore.QCoreApplication.translate("Form", title))def retranslateUi(self):_translate = QtCore.QCoreApplication.translateself.setWindowTitle(_translate("Form", "Form"))self.sendButton.setText(_translate("Form", "发送"))self.uploadButton.setText(_translate("Form", "上传"))self.setTitle("ZServer - Chat")def sendfile(self, *args):for file in QtWidgets.QFileDialog.getOpenFileNames(self, "上传文件")[0]:if os.path.isfile(file):  ##path, filename = os.path.split(file)self.add_file(filename, convert(os.path.getsize(file)), True, self.username, 0)if __name__ == '__main__':app = QtWidgets.QApplication(sys.argv)win = QChat(username="user1")win.show()app.exit(app.exec_())

代码地址

gitcode - https://gitcode.net/m0_60394896/python

user目录下的 chat.py为主页面, 图片都在user/images/filetype下面

相关资源

html+css+js+python(QtWebEngineWidgets) 实现微信聊天界面-包括时间,文件,纯文本等

html+css+js+python(QtWebEngineWidgets) 实现微信聊天界面-包括时间,文件,纯文本等相关推荐

  1. 基于node.js实现分销类微信聊天机器人

    基于node.js实现分销类微信聊天机器人 文章目录 基于node.js实现分销类微信聊天机器人 前言 一.涉及技术栈 二.实现代码 1.登录模块 2.聊天消息接收事件 总结 git地址 前言 最近遇 ...

  2. 使用js实现的带输入状态的简单的仿微信聊天界面

    使用js实现的简单的仿微信聊天界面,实现固定的聊天回复功能,只能是固定的5句,但是回复的内容可以在代码的判断中进行修改. 实现的效果有:1.实现仿微信的聊天界面 2.实现仿微信的正在输入功能. 原理: ...

  3. 制作微信聊天界面 CSS

    CSS实现微信聊天界面 ​ 昨天刚想起来老师布置了一个作业,让我们仿写微信聊天界面,于是乎连夜补作业,给大家分享一下,不足之处请指教! 首先运行效果图如下: 手机边框是导入一个图,其他的布局模块都是用 ...

  4. js模拟群聊天php,jquery仿微信聊天界面实例分享

    本文主要为大家详细介绍了jquery仿微信聊天界面的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家. 首先看一下我们的效果图. 这个颜色可能搭配的有些不合适,但基本功能大 ...

  5. 纯HTML+CSS实战之仿微信聊天界面制作

    效果图如下: 主要运用了之前实战项目中的三角形制作和before及after伪类选择器知识(其中头像图片采用30px*30px图片) 代码如下: <!DOCTYPE html> <h ...

  6. php写的微信聊天界面,浅谈 聊天界面 核心架构设计

    本文以一个小例子简单的演示在微信小程序中使用环信SDK收发消息.官网demo 下载后把整个utils目录下的文件复制到咱自己工程的目录下.在WebIMConfig.js中将AppKey替换成自己应用的 ...

  7. jquery 背景特效实现_html5实现的仿网页版微信聊天界面效果源码

    码农那点事儿 关注我们,一起学习进步 这是一款基于html5实现的仿网页版微信聊天界面效果源码,可实现微信网页版聊天界面效果,在编辑框编辑文字之后按Ctrl+Enter键即可提交文字到聊天对话框上.整 ...

  8. html5仿网页版微信聊天界面代码

    2019独角兽企业重金招聘Python工程师标准>>> html5仿网页版微信聊天界面代码 转载于:https://my.oschina.net/u/1266171/blog/783 ...

  9. php写的微信聊天界面,Android_Android高仿微信聊天界面代码分享,微信聊天现在非常火,是因其 - phpStudy...

    Android高仿微信聊天界面代码分享 微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先 ...

最新文章

  1. SSL与OpenSSL介绍
  2. Review meeting还开不开?
  3. 中文文件名乱码_全能型Mac解压缩软件 MacZip2.0.1(41)中文版 原ezip
  4. Codeforces
  5. com.css.common.jdbcTemplate中的类
  6. php中add函数,php中addslashes()和addclashes()函数的区别分析
  7. java内存四大区,jvm基础-内存区域
  8. 花生葫芦球 健身新运动
  9. 使用Oracle做定时任务
  10. threejs- z-fighting 问题
  11. Openstack的RPC通信代码调用架构
  12. 灰色预测法 —— matlab
  13. oracle 查看dba账户,Oracle DBA常用查询
  14. 零基础学习嵌入式给出的10条中肯的建议
  15. mac下Flash cc2014的破解方法
  16. 修改nginx站点根目录总结经验
  17. 微信小程序开发之获取用户信息
  18. 服务器被穷举法暴力爆破、攻击的简单应对方案。
  19. 【BFS 广度优先搜索】详解感染橘子最短时间问题
  20. peerDependencies WARNING问题剖析

热门文章

  1. 2023年暨南大学应用心理学考研上岸前辈备考经验指导
  2. 删除用户帐户后,保留在桌面上的用户配置文件夹删除后会自动重新生成的问题...
  3. rancher环境创建pv和pvc
  4. 电脑怎么压缩mp4视频文件?mp4视频文件太大怎么变小?
  5. Python学习笔记-WXPY初识
  6. Linux之挂载新的硬盘(超详细!)
  7. Voronoi图(泰森多边形)和Delaunay三角形
  8. python中类的详细介绍及使用
  9. python处理HTML转义字符
  10. 软件测试之Android单元测试