
本次博主放大招,前方高能!!!


博主之前和大家分享过我的一篇 python3GUI--复刻微信界面 By:PyQt5,但是只是UI效果没有具体功能
本次博主重整旗鼓,采用混合开发实现微信UI+具体功能。
本次博主将通过本篇博客和大家详细分享我开发的微信界面,软件整体逻辑仿照腾讯微信的样式和功能(当然和微信比起来俺这个是个皮毛),实现了大多的功能,比如:登录注册、消息发送、语音聊天、好友管理、个性化提示、锁定&解锁,博主花费了大量时间设计界面,目的是尽可能给大家一种使用微信的感觉!
本篇我们由浅入深,从软件设计原理、UI设计思路、整体交互逻辑几个方面带大家详细介绍一下我使用PyQt5开发的微信聊天软件。
本次我们使用PyQt5进行界面的设计,设计多个(13个)个性页面,尽最大程度还原微信的UI效果。
PyQt5 是 Python 对 Qt5 应用开发框架 的绑定库,它允许开发者使用 Python 语言快速构建跨平台的桌面应用程序。Qt 本身以强大的 GUI 组件(如窗口、按钮、表格、图表)和 非GUI功能(如网络、数据库、多线程、多媒体)著称,而 PyQt5 将这些能力完整地暴露给 Python,使得开发者既能保持 Python 的简洁和高效,又能利用 Qt 提供的强大功能。它支持 信号与槽机制 实现事件驱动编程,同时与 Qt Designer 搭配可实现界面可视化设计,适合快速开发从简单工具到复杂企业级桌面应用的全栈解决方案。

开发、修改本项目代码需要一些网络基础,博主将这些知识点列举出来
服务端、客户端登录成功后都会创建对应的TCP和UDP连接,我们采用系统自动分配端口的方式避免了端口占用冲突的情况,保障了连接的准确性。
socket 是计算机网络编程中的基本抽象,表示网络中两个节点之间的通信端点。它提供了一种标准化接口,使程序能够在不同主机之间发送和接收数据,不论底层是 TCP(面向连接、可靠传输)还是 UDP(无连接、高速传输)协议。在 Python 中,socket 模块封装了这种功能,开发者可以通过创建 socket、绑定地址、建立连接、收发数据和关闭连接来实现网络通信。简而言之,socket 就是程序与网络世界之间的“插座”,让信息能够在计算机之间自由传递。

我们的聊天软件位于OSI七层模型的应用层,具体的OSI七层模型见下图。
OSI七层模型的作用是提供一个标准化的网络通信框架,把复杂的网络传输过程分成七个层次(物理层、数据链路层、网络层、传输层、会话层、表示层、应用层),每一层都负责特定的功能,并且只与相邻层交互,从而实现不同系统、设备和协议之间的互通,降低设计复杂性,便于故障定位、协议扩展和网络互操作。

TCP(传输控制协议,Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,用于在计算机网络中保证数据按顺序、完整地从一端传输到另一端。它通过建立连接(三次握手)、数据分段、确认应答和重传机制,确保通信双方的数据可靠传输,同时提供流量控制和拥塞控制功能,广泛用于网页浏览、文件传输、电子邮件等需要高可靠性的网络应用。

在实际的应用过程中博主遇到了TCP粘包的情况,导致接收到的数据不完整被拆分,最后采用接收端使用“定长包头 + 消息体”机制解决了 TCP 粘包/拆包。
TCP 粘包是指在基于 TCP 协议的通信中,由于 TCP 是面向字节流的协议,发送方连续发送的多条消息在接收方可能被合并成一个数据块接收,或者一条消息被拆分成多个数据块接收的现象。粘包问题常出现在发送频繁的小数据包或消息边界不明确的情况下,会导致接收方无法直接按原始消息解析数据,因此需要通过固定长度、分隔符或在消息头中标记长度等方式进行处理。4.UDP UDP(用户数据报协议,User Datagram Protocol)是一种无连接的传输层协议,它在网络中以数据报的形式发送信息,不保证数据的顺序或可靠性,因此传输速度快、开销小。UDP 适合对实时性要求高但允许丢包的场景,如语音通话、视频直播、在线游戏和 DNS 查询等。

TCP与UDP的区别:

我们能得出结论:需要稳定无差错的数据传输(比如消息发送、命令提交)可以使用TCP,对于需要时效性准确定低且可以接受对包的场景(比如语音视频聊天)采用UDP。
我们的项目分为客户端和服务端,双端可以部署在局域网内不同的机器,下面我采用截图的方式给大家介绍我们的项目效果
服务端是面向开发者和运维用户的,我们这里几乎采用了pyqt的原生UI效果,并没有定制化地去设计样式,宗旨是:能用就行。
服务端启动后会自动创建TCP和UDP连接,所有的登录用户都会连接到服务端,服务端为用户提供聊天消息服务,用户所有发送的消息内容都会先发给服务端保存,然后通过广播(broadcast)的方式广播通知给目标接受用户,最后通知用户产生了新的消息。
我们的服务端主要是用来控制聊天服务的开启与停止,同时支持发送全服(指定用户)公告通知、踢人下线、用户信息查看。

除了右侧实时展示当前日志信息,运维人员可通过双击左侧列表项,查看用户的详细信息,对指定用户进行操作

我们本次一共有3张数据表,用来记录不同维度的持久化数据,相关人员可以通过服务端的GUI对数据进行查看,由于三个页面结构一样,这里为了避免博客冗余,仅展示一个页面的UI效果。
我们为页面设计了贴心的翻页组件,另外支持多个条件字段的数据检索,这里的密码采用了加密,只有用户自己知道自己的密码,加密相关思路在后文博主会详细介绍,头像我们采用base64字符串存储,实际上肯定不会把这种数据放到数据库,最佳实践应该是放到CDN服务器中,数据库内只保存一个网络地址。
CDN(内容分发网络,Content Delivery Network)服务器是一种分布在不同地理位置的数据缓存节点,其主要作用是将网站或应用的静态资源(如图片、视频、CSS、JavaScript等)存储在离用户最近的节点上,从而加速内容的访问速度,降低源服务器的压力,并提升用户体验。通过智能路由和负载均衡,CDN能够根据用户的地理位置、网络状况和服务器负载,动态选择最佳节点响应请求,实现高可用性和稳定性,同时还可提供防DDoS攻击、缓存优化等安全和性能增强功能。*

客户端是我们整套系统的核心,我们主要的精力也都是放到了客户端的开发中。客户端安装在每个用户的实体物理PC机器上,客户端通过中间键与数据库交互,使用TCP的方式发送消息给服务端,服务端来处理数据。
登录和注册对于我们这样的聊天工具是不可或缺的,我们设计了简约的登录注册界面,尽可能简化用户的使用路径同时保障我们软件系统的相对安全。
用户使用自己的手机号+密码+正确的验证码即可登录到本系统。

没有账号的朋友也不要担心,点击最下方的注册按钮,到注册页面,通过输入手机号、密码、确认密码、验证码之后完成注册

成功注册的用户会跳转回登录界面。

下面就是我们客户端的主界面啦,我们设计了多个自由区域的页面,通过内部机制联动起来,最左侧为窗口页面操作区域,支持窗口最大化还原、最小化、功能页面切换(消息页面、联系人页面)、功能菜单展示(锁定、清理缓存、关于、退出登录、退出微信),目前实现了联系人聊天以及公众号内容推荐,聊天内容支持文本、emoji、图片、系统消息提示(加好友成功提示)等,左侧联系人操作支持:置顶(取消置顶)、标记已读(未读)、开启(关闭)消息免打扰、联系人不显示,右侧聊天区域支持emoji选择输入、截图(支持快捷键)、消息转发、消息复制、粘贴、更换聊天区域背景(仅当次生效)、以及个性化可爱气泡、个性化名片。
我们贴心设计了“公众号”页面,点开是博主的博客集合,支持无限滚动下拉加载,点击后在浏览器打开对应的博客,这个内容作为推荐内容穿插在聊天联系人列表的第三个,目前作为一个常驻的内容推荐区域暂时无法隐藏。

我们的聊天软件不仅支持简单的图片文字消息,还支持语音聊天,用户可以选择目标用户通过点击聊天界面或者联系人卡片的语音聊天按钮发起语音聊天,若对方不在线则提示语音聊天发起者“对方不在线,无法进行语音聊天”,如果对方在线对方能实时收到语音聊天的邀请,接受者可以选择接听或者取消(拒绝),点击取消之后直接结束,点击接听之后双方将建立UDP的语音聊天通道,所有的语音数据都先发送到服务端,服务端转发这些语音数据给目标用户,实现双方的实时语音聊天功能。
PS:这里的逻辑博主调整了好多次,经过多次重写之后实现了具体功能,实属不易!

在本页面支持用户对联系人进行管理,首先系统会对联系人进行统计与分组,分为“新朋友”和“联系人”,用户可以在主界面根据手机号或者联系人微信ID搜索目标用户发送添加好友请求,对方如果在线,就会立即收到请求,邀请发送者可以在本页面查看加好友进度,另外能根据联系人首字母查看已经添加的朋友,可以查看他们的详细信息并进行操作:发起聊天、修改备注、删除好友等。

文件发送是通过用户先发送到服务端,服务端转发给目标用户的方式实现的,服务器不存储文件,所以要求接收者在线,才能完成整个文件发送流程,具体的文件发送流程是这样的:首先用户A通过在聊天界面点击“发送文件”按钮选择一个本地文件,然后系统会弹出确认对话框与用户确认文件信息以及发送目标用户信息,用户确认无误后点击确定按钮,文件就会传送至对方,文件发送且文件接收完成后,系统记录下本次消息发送,将发送文件的消息放到聊天对话框中,用户可以直接点击文件使用系统打开方式打开目标文件。

下面我放了一张动图,用来展示文件发送功能。

这个页面也是仿照出来的,目的是:在登录的情况下锁定软件,不发声、用户可使用4位解锁码解除锁定,未设置解锁码的用户可以在个人页面或者首次使用锁定功能时设置,我们提供了多个入口,这里和原本功能逻辑是有区别的,博主在这里进行了简化,无需配合手机进行解锁。

除了上面主要的页面之外我们还设计了:托盘菜单、联系人搜索、图片查看、消息转发、个性名片等页面,这里只展示一个图片查看页面吧,因为这个页面我们的还原度也很高,别的就不详细介绍了。
这个图片查看页面支持放大缩小、还原、旋转、保存本地、全屏查看、左右切换,并且支持快捷键,通过“子模块”的方式和我们的主程序联动起来,支持右击菜单快捷操作,每次放大、缩小或者照片到了第一张或者最后一张都会在图片中央贴心地显示提示信息~

快捷键这一块,为了在非主窗口按下快捷键能够响应,我们采用了全局快捷键(Ctrl+Alt+X)来进行截图操作,这样用户可以在所有任意场景下按下此组合键,进行截图,这个截图方式也符合用户的使用习惯,下面我来详细介绍一下具体代码实现:
在 Windows 系统下使用 PyQt5 创建一个后台线程来监听全局热键 Ctrl+Alt+X,当用户按下该热键时,线程会通过信号将事件通知主 GUI 线程,从而触发相应的操作;整个过程通过 Windows 消息循环实现,并在退出时自动注销热键,确保系统资源不被占用。
另外我们贴心的加入了判断:如果当前在聊天窗口内,则截图完成后自动将截图内容插入到聊天界面输入框中,方便用户后续操作
用户通过按下快捷键或者点击聊天区域的截图按钮开始截图,软件会对当前桌面进行取图,按下鼠标并拖动,软件会在左上角实时更新截图区域的宽高数值,并对截图区域高亮展示,当鼠标松开时,软件会自动在底部展示截图操作功能按钮(包含矩形、圆形、自定义涂鸦、关闭截图、完成截图并插入到聊天窗口、保存到本地)和颜色选择按钮(支持多种自定义线条颜色,可拓展),用户亦可按下Esc快捷键取消截图、或者按下回车键完成截图,这个功能的使用习惯完全参照了微信截图。

采用个性化的提示音能够提升用户与软件之间的黏着性,直观地声音往往比文字、图像更能快速地提示用户,当新消息到来(聊天消息、好友请求、语音邀请等),我们调用了自己创建的AudioThread线程(继承自QThread),用来播放声音,在这个线程里支持声音循环播放(比如语音邀请的场景),当语音结束或者被挂断之后都会滴的一声提示用户,这个使用习惯是和微信一样的。
我们的代码是多线程并行执行的,在 GUI 应用中播放提示音或背景音而不影响界面响应,非常适合播放提示音。
下图为资源目录,包含音频和图片资源

我们的项目中文件发送和语音消息的发送都是基于文件的,具体来说都是发送文件,和只发送文件功能不同,发送语音消息是发送具体的.wav格式文件,这个文件承载着我们通过在界面语音录入的消息内容,使用语音录制组件录制好的语音会打包成数据包,消息类型为message_type为send_audio的数据通过客户端线程发给服务端,服务端检测到对方在线会通知对方客户端接收语音消息和文件,这样同一份wav文件就转发到了对方的客户端上,用户通过点击语音消息进行语音消息的播放,在语音播放的过程中我们还单独设计了遮罩,目的是让用户更专注地收听发送or接收到的语音消息,我们限制了语音的长度,语音长度小于0.5秒会录制失败并且提示用户“语音消息时间太短”,同时语音消息的录制时长也不能超过5分钟,否则转发过程中可能会增加服务端的压力,在语音播放过程中我们贴心设计了播放进度以及播放图标的展示,目的是让用户听完语音消息,减少焦虑。
我们在新消息到来之时,不止会有提示音的通知提醒,还会有系统的消息提醒,例如windows右下角通知提示,这里我们继承了QSystemTrayIcon类,通过调用showMessage方法,实现了发送系统通知的功能。

showMessage的方法参数我放到下面表格里啦
参数/属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
|
| 必填 | 气泡通知的标题文本 |
|
| 必填 | 气泡通知的正文内容 |
|
|
| 通知的图标,可以使用内置枚举:
• |
|
|
| 气泡显示时长(毫秒),实际时长可能受操作系统限制 |
信号关联 |
| — | 当用户点击消息时触发的信号 |
返回值 |
| — | 方法本身不返回值,只显示消息 |
博主使用画图工具画了两张系统架构图,一张简单一张详细,我认为图文配合更能让读者了解、理解我的开发思路以及软件核心逻辑。
我们的系统架构很简单,整体包含数据服务器以及聊天服务器,每个用户(客户端)和服务器(服务端)之间是双向通信的,用户把消息发送给服务器,服务器将消息转发并广播出去同时记录聊天消息到数据服务器,所以我们的系统支持多机器部署的,数据服务器和聊天服务器可以分开,如果想拓展到广域网,此架构能够可以给您一个参考!

这里博主又画了一张详细的数据流转、交互图,用文字和线段标注了数据流转过程。我们的软件系统只要是涉及到了GUI(pyqt)就都使用了多线程(QThread),避免了界面卡顿,支持多任务同时操作并发,使用QT信号和槽机制进行数据交互,具体来说是:线程里进行耗时操作,操作结束后将数据处理结果发射会GUI所在的主线程。

为了让读者更直观地理解和使用本系统,博主特地绘制了系统主要功能流程图,聊天系统的所有核心功能都在图中标注出来了。

我们在系统里继承了一种低延迟的语音通话方案,这里我详细介绍一下语音聊天的具体流程,方便读者理解我们实现的思路和过程:
1、用户在聊天页面或者用户个人信息页面点击语音聊天按钮,发起语音聊天
2、调用客户端线程(ClientThread)的send_data方法,将语音邀请(voice_invite)的消息发给服务端
3、服务端线程(ServerThread)接收到语音邀请的消息之后,转发此消息给消息接收者
4、接收者收到voice_invite邀请之后,弹出“语音聊天对话框”,并选择接听,双方调用服务端的start_udp方法,建立与服务端的UDP连接
5、服务端通过start_udp_server里的pickle将语音消息实时解包后发给对方,服务端充当“消息转发者”的桥梁,实现用户之间的语音聊天
6、当其中一方点击了“挂断”之后,会调用客户端的stop_udp,终止语音聊天,同时发射voice_end的消息给服务端,服务端清理掉UDP通道,语音聊天结束

看到这里的朋友可能打算了解一下本项目的源码,这里单独采用一个章节进行介绍。
项目名称:pyqt-socket-chat
源代码目录:src/
客户端代码目录:client/
数据库中间键代码目录:middleware/
服务端代码目录:server/
项目依赖:requirements.txt
一键安装依赖
pip install -r requirements.txt
psutil==5.9.5
PyAudio==0.2.13
PyQt5==5.15.11
PyQt5_sip==12.15.0
QtAwesome==1.3.1
服务端代码包含几个包,见名知意,核心为server_thread.py,GUI入口为:server_main.py,每个组件和模块都是支持单独调试运行的。

中间键是数据库引擎,提供一些基础数据服务

客户端是我们最核心的部分,粗略估计整体代码量为大于1万行,大家不要被这个数字吓到哈,其中有超过七千行是我们混合开发的代码,主要是unique_widgets.py这个里面代码产生的,里面的内容是高度自定义的。

如果您只是要学习或者研究源码的话,没必要开启虚拟机或者其他机器,一台机器就能跑起来本项目,具体部署流程很简单:
1、配置开发环境(博主是python3.8)
2、安装项目依赖:requirements.txt
3、首先开启服务端GUI:server_main.py,我们在代码里限制了,服务端当前只能开启一个。
4、然后开启客户端,client_main.py,客户端是支持多开的,这意味着您可以自行测试消息发送等其他功能!
<font color=blue font-size=blue>博主可以有偿帮忙部署!您放心,代码一定能跑起来!</font>
虽然本项目整体代码量不小,但是明显省掉了很多设计UI的流程,我们可以更加模块化的设计UI效果,每个模块都支持单独调试,这也是混合开发的一个优势,我们的软件使用起来也是相当流畅的,很多的UI细节也都能够实现,这里真心推荐大家学习、讨论本项目!
比如下面这个聊天界面,支持单独调试,模块化设计,支持您方便地移植到别的项目,致力于敏捷开发!

还没有拿到项目源码的朋友肯定会有这个疑问,就是在项目里能学到啥,为啥需要源码,这里博主回答,您能学到包括但不限于以下内容:
1、PyQt开发思路,个性化组件开发,信号与槽的实际应用、多线程避免界面卡顿
2、混合开发、模块化开发,个性化UI设计方法
3、socket编程,内网中数据交互逻辑,以及TCP、UDP在Python中的使用
4、项目的架构以及具体代码实现,
5、数据流转以及数据存储与数据库结合
我整理了一张图,是已经处理过的事件,对应不同的页面,当然不限于次。
Cate(分类) | Action(动作) | 对应页面/组件 | 功能描述 |
|---|---|---|---|
contactList | pin / unpin | 联系人列表页/contactList | 联系人置顶 / 取消置顶 |
contactList | donot_disturb | 联系人列表页/contactList | 设置消息免打扰 |
contactList | mark_unread / mark_read | 联系人列表页/contactList | 标记消息已读 / 未读 |
contactList | remove | 联系人列表页/contactList | 从联系人列表移除,必要时跳转空白页 |
contactInfo | msg | 联系人信息页/contactInfoPage | 联系人信息页 → 打开聊天 |
contactInfo | verify_ok | 联系人信息页/contactInfoPage | 通过好友验证,更新状态并发送通知 |
contactInfo | remark_changed | 联系人信息页/contactInfoPage | 修改联系人备注并刷新 |
contactInfo | delete | 联系人信息页/contactInfoPage | 删除好友关系,跳转联系人页 |
chatPage | send | 聊天页/chatPage | 发送新消息,刷新联系人列表 |
chatPage | save_image | 聊天页/chatPage | 保存图片消息到本地 |
chatPage | send_file | 聊天页/chatPage | 发送文件消息 |
chatPage | send_audio | 聊天页/chatPage + 语音组件/audioWidget | 打开发送语音窗口 |
chatPage | msg_audio_complete | 聊天页/chatPage | 语音播放完成,提示音效 |
chatPage | screen_shot | 聊天页/chatPage | 截图功能 |
chatPage | recall_msg | 聊天页/chatPage | 撤回消息并刷新对话 |
chatPage | msg_click (image) | 聊天页/chatPage → 图片查看器/imageViewerWidget | 查看图片消息 |
chatPage | msg_click (finish_file_send) | 聊天页/chatPage → 系统文件管理器 | 打开已发送文件 |
chatPage | msg_click (finish_voice) | 聊天页/chatPage → 语音页/voicePage | 打开语音聊天 |
chatPage | msg_click (finish_audio) | 聊天页/chatPage | 播放语音消息 |
ImageViewer | copy | 图片查看器/ImageViewer | 复制图片到剪贴板 |
ImageViewer | save | 图片查看器/ImageViewer | 保存图片 |
ImageViewer | show_in_folder | 图片查看器/ImageViewer → 系统文件管理器 | 在文件夹中显示图片 |
ImageViewer | open_system | 图片查看器/ImageViewer → 系统默认应用 | 用系统程序打开图片 |
loginPage | login | 登录页/loginPage | 登录账号 |
loginPage | register | 注册页/loginPage | 注册新账号 |
trayMenu | clean_cache | 托盘菜单/trayMenu | 清理缓存 |
trayMenu | settings | 托盘菜单/trayMenu | 打开设置(占位) |
trayMenu | about | 托盘菜单/trayMenu → 浏览器 | 打开作者博客 |
trayMenu | logout | 托盘菜单/trayMenu | 退出登录 |
trayMenu | exit | 托盘菜单/trayMenu | 退出软件 |
trayMenu | lock | 托盘菜单/trayMenu → 锁屏页/lockScreenPage | 锁定应用,需解锁码 |
ForwardPage | forward | 转发页/forwardDialog + 聊天页/chatPage | 转发消息给联系人 |
ForwardPage | image_clicked | 转发页/forwardDialog → 图片查看器/imageViewerWidget | 查看转发的图片 |
LockScreen | unlock | 锁屏页/lockScreenPage | 输入解锁码解锁 |
LockScreen | set | 锁屏页/lockScreenPage | 设置解锁码并上锁 |
LockScreen | close | 锁屏页/lockScreenPage | 关闭锁屏界面 |
ContactCard | save | 名片页/businessCardWidget + 左侧栏/leftTabArea | 保存个人名片信息 |
AudioPage | audio_record_finish | 语音页/audioWidget + 聊天页/chatPage | 完成语音录制并发送消息 |
所谓混合开发指的是:在 PyQt5 应用中通过 QWebEngineView(或 QWebView)嵌入网页,把 前端 HTML/CSS/JavaScript 用来构建现代化界面或可重用的 Web 页面,同时利用 Python/PyQt5 提供本地能力(如文件系统访问、数据库操作、硬件交互等)。前后端之间可以通过 QWebChannel 实现双向通信:前端调用 Python 方法,Python 向前端推送数据,从而既能保持 Web 前端的灵活与美观,又能享受 PyQt5 桌面应用的系统集成功能,常用于管理工具、跨平台桌面壳应用或需要快速搭建 UI 的项目。
我们的界面中封装了多个web页面,我来列举一些:联系人列表、聊天页面、联系人信息页、锁定页面、图片查看窗口、托盘菜单,这些丰富的h5元素给我们的界面增添了灵活性,只要传入相同格式的数据,每个部分都支持单独调试运行,这里我以"无边框的托盘菜单"为例,给出最小源码,大家复制之后可以直接运行,方便大家理解原理!
import os
import sys
import time
import json
from PyQt5.QtWebChannel import QWebChannel
from PyQt5.QtCore import pyqtSlot, QObject, QUrl, Qt
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QDesktopWidget, QApplication
from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile
class TrayBridge(QObject):
def __init__(self, parent=None):
super().__init__(parent)
@pyqtSlot(str)
def handle_action(self, action: str):
print("trayMenu", action, {"value": {}})
class TrayWidget(QWidget):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.ui_init()
def ui_init(self):
self.setWindowFlags(Qt.Tool | Qt.FramelessWindowHint) # 小窗口无边框
self.setFixedSize(120, 190)
# WebEngine 设置
profile = QWebEngineProfile(f"profile_{id(self)}", self)
profile.setCachePath(f"./web_cache_tray_{int(time.time())}")
profile.setPersistentStoragePath(f"./web_cache_tray_{int(time.time())}")
self.view = QWebEngineView(self)
self.view.setAttribute(Qt.WA_TranslucentBackground, True)
self.view.setStyleSheet("background: transparent;")
self.view.page().setBackgroundColor(Qt.transparent)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.view)
# WebChannel 绑定
self.channel = QWebChannel()
self.bridge = TrayBridge(self)
self.channel.registerObject("qt_channel", self.bridge)
self.view.page().setWebChannel(self.channel)
self.view.setHtml(self.get_html(), QUrl("qrc:///"))
def get_html(self):
return r"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<style>
body {
margin:0;
font-family:"Microsoft YaHei";
font-size:10pt;
background:#ffffff;
}
html, body {
margin: 0;
overflow: hidden; /* 禁止滚动条 */
}
.menu {
display:flex;
flex-direction:column;
}
.menuItem {
padding:7px 15px;
cursor:pointer;
font-size:10pt;
color:#000;
background:#fff;
text-align:center; /* 居中 */
}
.menuItem:hover {
background:rgb(220,218,217);
}
.exit:hover {
background:#06B75B;
color:#fff;
}
</style>
</head>
<body>
<div class="menu">
<div class="menuItem" onclick="doAction('settings')">设置</div>
<div class="menuItem" onclick="doAction('lock')">锁定</div>
<div class="menuItem" onclick="doAction('about')">关于</div>
<div class="menuItem" onclick="doAction('clean_cache')">清除缓存</div>
<div class="menuItem" onclick="doAction('logout')">退出登录</div>
<div class="menuItem exit" onclick="doAction('exit')">退出微信</div>
</div>
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script>
var qt_channel = null;
new QWebChannel(qt.webChannelTransport, function(channel) {
qt_channel = channel.objects.qt_channel;
});
function doAction(act){
if(qt_channel && qt_channel.handle_action){
qt_channel.handle_action(act);
}
}
</script>
</body>
</html>
"""
if __name__ == "__main__":
app = QApplication(sys.argv)
w = TrayWidget()
w.show()
sys.exit(app.exec_())代码效果

PS:这里可能有的朋友会反驳我,“这么简单的需求为什么不直接用QWidget实现呀?”,在此统一回复哈:这里只是个例子,旨在于演示混合开发的思路、混合开发解耦效果,给大家提供一个思路!
本次和大家详细分享了我使用pyqt5+socket开发的局域网聊天软件,实现了多种类型消息发送,博主敢说本软件目前(2025年9月6日)为CSDN最像微信的pyqt语言开发出来的仿品,无论是UI效果还是实际功能都尽最大程度还原了原版,最后,欢迎大家来了解我的项目,祝您成功!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。