html+css+js+python(QtWebEngineWidgets) 实现微信聊天界面-包括时间,文件,纯文本等
文章目录
- 展示
- 参考文章
- html + js + css
- python
- 代码地址
- user目录下的 chat.py为主页面, 图片都在user/images/filetype下面
- 相关资源
展示
纯html - web网页
QWebEngineWidget + Html :
参考文章
(搜索)
聊天界面html+css+javascript
-https://blog.csdn.net/lutrra/article/details/120390780html 自动包裹内容,CSS 实现div宽度根据内容自适应
-https://blog.csdn.net/weixin_32052253/article/details/117725804HTML/CSS float 属性
-https://www.w3school.com.cn/cssref/pr_class_float.aspVue 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.4430keyframes_CSS淡入淡出
-https://blog.csdn.net/culuo8053/article/details/107910312CSS3实现毛玻璃完美效果
-https://www.cnblogs.com/ivan5277/p/10007273.html
PyQt5
和 html
双向通信
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) 实现微信聊天界面-包括时间,文件,纯文本等相关推荐
- 基于node.js实现分销类微信聊天机器人
基于node.js实现分销类微信聊天机器人 文章目录 基于node.js实现分销类微信聊天机器人 前言 一.涉及技术栈 二.实现代码 1.登录模块 2.聊天消息接收事件 总结 git地址 前言 最近遇 ...
- 使用js实现的带输入状态的简单的仿微信聊天界面
使用js实现的简单的仿微信聊天界面,实现固定的聊天回复功能,只能是固定的5句,但是回复的内容可以在代码的判断中进行修改. 实现的效果有:1.实现仿微信的聊天界面 2.实现仿微信的正在输入功能. 原理: ...
- 制作微信聊天界面 CSS
CSS实现微信聊天界面 昨天刚想起来老师布置了一个作业,让我们仿写微信聊天界面,于是乎连夜补作业,给大家分享一下,不足之处请指教! 首先运行效果图如下: 手机边框是导入一个图,其他的布局模块都是用 ...
- js模拟群聊天php,jquery仿微信聊天界面实例分享
本文主要为大家详细介绍了jquery仿微信聊天界面的相关代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能帮助到大家. 首先看一下我们的效果图. 这个颜色可能搭配的有些不合适,但基本功能大 ...
- 纯HTML+CSS实战之仿微信聊天界面制作
效果图如下: 主要运用了之前实战项目中的三角形制作和before及after伪类选择器知识(其中头像图片采用30px*30px图片) 代码如下: <!DOCTYPE html> <h ...
- php写的微信聊天界面,浅谈 聊天界面 核心架构设计
本文以一个小例子简单的演示在微信小程序中使用环信SDK收发消息.官网demo 下载后把整个utils目录下的文件复制到咱自己工程的目录下.在WebIMConfig.js中将AppKey替换成自己应用的 ...
- jquery 背景特效实现_html5实现的仿网页版微信聊天界面效果源码
码农那点事儿 关注我们,一起学习进步 这是一款基于html5实现的仿网页版微信聊天界面效果源码,可实现微信网页版聊天界面效果,在编辑框编辑文字之后按Ctrl+Enter键即可提交文字到聊天对话框上.整 ...
- html5仿网页版微信聊天界面代码
2019独角兽企业重金招聘Python工程师标准>>> html5仿网页版微信聊天界面代码 转载于:https://my.oschina.net/u/1266171/blog/783 ...
- php写的微信聊天界面,Android_Android高仿微信聊天界面代码分享,微信聊天现在非常火,是因其 - phpStudy...
Android高仿微信聊天界面代码分享 微信聊天现在非常火,是因其界面漂亮吗,哈哈,也许吧.微信每条消息都带有一个气泡,非常迷人,看起来感觉实现起来非常难,其实并不难.下面小编给大家分享实现代码. 先 ...
最新文章
- SSL与OpenSSL介绍
- Review meeting还开不开?
- 中文文件名乱码_全能型Mac解压缩软件 MacZip2.0.1(41)中文版 原ezip
- Codeforces
- com.css.common.jdbcTemplate中的类
- php中add函数,php中addslashes()和addclashes()函数的区别分析
- java内存四大区,jvm基础-内存区域
- 花生葫芦球 健身新运动
- 使用Oracle做定时任务
- threejs- z-fighting 问题
- Openstack的RPC通信代码调用架构
- 灰色预测法 —— matlab
- oracle 查看dba账户,Oracle DBA常用查询
- 零基础学习嵌入式给出的10条中肯的建议
- mac下Flash cc2014的破解方法
- 修改nginx站点根目录总结经验
- 微信小程序开发之获取用户信息
- 服务器被穷举法暴力爆破、攻击的简单应对方案。
- 【BFS 广度优先搜索】详解感染橘子最短时间问题
- peerDependencies WARNING问题剖析