使用pyqt的QUdpSocket传文本信息、用QTcpServer传文件的样例

使用pyqt的QUdpSocket传文本信息、用QTcpServer传文件的样例

本文章实现以下功能:

1.用QUdpSocket收发文本消息。
2.用QTcpServer接收文件,用QTcpSocket发送文件;支持大容量文件传输。测试过7G的文件,传输前后md5值一致。
3.文本消息采用气泡文本class BubbleLabel(QLabel)的方式展示,根据文本长度自动计算气泡宽度。
4.文件收发成功后,在聊天窗口展示固定格式的class FileInfoWidget(QWidget),并实现了简单的右键菜单。
5.重复接收同名文件时,自动重命名,避免覆盖传文件。

如下图所示:
在这里插入图片描述
一共3个py文件:netServer.py、netClient.py、netUtil.py

1.其中netServer.py和netClient.py都是QMainWindow,是聊天窗口的ui,代码几乎完全相同,实际上可以只保留netClient.py(更名为ChatWidget更合适)。我只是懒得搞虚拟机,才强行分为server和client两个文件,把upd、tcp占用的端口写死在代码里,方便在同一台开发电脑上测试而已。
2.聊天记录的显示既不用QTextBrowser,也不用QTextEdit,而是QListWidget,因为只有QListWidget才能通过QListWidgetItem插入自定义的QWidget:QListWidget.setItemWidget(QListWidgetItem, QWidget)
3.核心代码在netUtil.py里,需要先理解pyqt的信号、槽机制。实际上,任何一个(类似netClient.py的)QWidget,from netUtil import NetUtil之后,都可以使用udp、tcp进行数据传输。

废话不多说,直接上代码,自己看注释:
netServer.py(可收发udp文本消息,仅接收文件)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import sys
import time
import shutil
from math import sqrt
from datetime import datetime
from PyQt6 import QtWidgets, QtCore, QtGui
from PyQt6.QtWidgets import *
from netUtil import NetUtil


class BubbleLabel(QLabel):
    """气泡文字"""
    border = 2
    trigon = 15  # 指向左、右的三角箭头的大小

    def __init__(self, listWidgetItem, listWidget, text, maxWidth, myself=True):
        super(BubbleLabel, self).__init__(text)
        self.listWidgetItem = listWidgetItem
        self.listWidget = listWidget
        self.text = text
        self.myself = myself  # 标志绘制左还是右
        self.setTextInteractionFlags(QtCore.Qt.TextInteractionFlag.TextSelectableByMouse)  # 文本可被选中
        self.setWordWrap(True)  # 文本可换行
        self.setFont(QtGui.QFont("Times New Roman", 12, QtGui.QFont.Weight.Normal))
        self.autoAdjustSize(maxWidth)
        self.setState(False)  # 设置鼠标不进入状态,方便绘图区域的颜色更新
        if self.myself:
            '''为了让实现显示的图片不会在super(BubbleImage, self).paintEvent(e)时和绘制的背景气泡冲突,
            设置控件的setContentsMargins绘图范围保证图像的绘图区域。'''
            self.setContentsMargins(int(self.trigon * sqrt(3) / 2) + 15, self.border + 10, self.border + 15, self.border + 10)
        else:
            self.setContentsMargins(self.border + 15, self.border + 10, int(self.trigon * sqrt(3) / 2) + 15, self.border + 10)

    def autoAdjustSize(self, maxWidth):
        '''根据QLabel的字体、字号计算QLabel文本的像素长度,控制一行长度不超过maxWidth'''
        fm = QtGui.QFontMetrics(self.font())
        pixels = fm.horizontalAdvance(self.text)
        self.setMinimumWidth(min(maxWidth, pixels + (self.trigon * 4)))  # 不是setMaximumWidth

    def paintEvent(self, e):
        size = self.size()
        qp = QtGui.QPainter()
        qp.begin(self)
        if self.myself:
            self.leftBubble(qp, size.width(), size.height())
        else:
            self.rightBubble(qp, size.width(), size.height())
        qp.end()
        super(BubbleLabel, self).paintEvent(e)

    def leftBubble(self, qp, w, h):
        qp.setPen(self.colorLeftE)  # 设置画笔颜色,绘制的矩形边缘颜色
        qp.setBrush(self.colorLeftM)  # 设置红色的笔刷
        middle = int(h * (1 - 0.618))
        shifty = int(self.trigon / 2)
        shiftx = int(self.trigon * sqrt(3) / 2)
        rL = QtCore.QRectF(shiftx, 1, w - shiftx - self.border, h - 3)
        pL = QtGui.QPolygonF()  # 更改为圆角矩形
        pL.append(QtCore.QPointF(0, middle))  # 起始点
        pL.append(QtCore.QPointF(shiftx, middle + shifty))  # 第二点
        pL.append(QtCore.QPointF(shiftx, middle - shifty))  # 第三点
        """
        pL.append(QtCore.QPointF(w - self.border, h - self.border)) #第四点
        pL.append(QtCore.QPointF(w - self.border, self.border)) #第五点
        pL.append(QtCore.QPointF(shiftx, self.border)) #第六点
        pL.append(QtCore.QPointF(shiftx, middle - shifty)) #第七点
        """
        qp.drawPolygon(pL)
        qp.drawRoundedRect(rL, 10, 10)
        qp.setPen(self.colorLeftM)
        line = QtCore.QLine(shiftx, middle + shifty, shiftx, middle - shifty)
        qp.drawLine(line)

    def rightBubble(self, qp, w, h):
        qp.setPen(self.colorRightE)  # 设置画笔颜色,绘制的矩形边缘颜色
        qp.setBrush(self.colorRightM)  # 设置红色的笔刷
        middle = int(h * (1 - 0.618))
        shifty = int(self.trigon / 2)
        shiftx = int(self.trigon * sqrt(3) / 2)
        rL = QtCore.QRectF(self.border, 1, w - shiftx - self.border, h - 3)
        pL = QtGui.QPolygonF()  # 更改为圆角矩形
        pL.append(QtCore.QPointF(w, middle))  # 起始点
        pL.append(QtCore.QPointF(w - shiftx, middle + shifty))  # 第二点
        pL.append(QtCore.QPointF(w - shiftx, middle - shifty))  # 第三点
        """
        pL.append(QtCore.QPointF(w - self.border, h - self.border)) #第四点
        pL.append(QtCore.QPointF(w - self.border, self.border)) #第五点
        pL.append(QtCore.QPointF(shiftx, self.border)) #第六点
        pL.append(QtCore.QPointF(shiftx, middle - shifty)) #第七点
        """
        qp.drawPolygon(pL)
        qp.drawRoundedRect(rL, 10, 10)
        qp.setPen(self.colorRightM)
        line = QtCore.QLine(w - shiftx, middle + shifty, w - shiftx, middle - shifty)
        qp.drawLine(line)

    def setState(self, mouse):
        '''鼠标进入和鼠标出时需要显示不一样的效果,主要就是更新颜色变量,然后调用update更新重绘'''
        if mouse:  # 鼠标进入
            self.colorLeftM = QtGui.QColor("#eaeaea")
            self.colorLeftE = QtGui.QColor("#D6D6D6")
            self.colorRightM = QtGui.QColor("#8FD648")
            self.colorRightE = QtGui.QColor("#85AF65")
        else:
            self.colorLeftM = QtGui.QColor("#fafafa")
            self.colorLeftE = QtGui.QColor("#D6D6D6")
            self.colorRightM = QtGui.QColor("#9FE658")
            self.colorRightE = QtGui.QColor("#85AF65")
        self.update()  # 更新界面,不用执行也可以更新,但是不实时

    def enterEvent(self, e):
        self.setState(True)

    def leaveEvent(self, e):
        self.setState(False)

    def contextMenuEvent(self, event):
        ''' 右键菜单实现文本的复制和控件的删除'''
        at_copy = QtGui.QAction('复制', self, triggered=self.copyItemText)
        at_copyAll = QtGui.QAction('复制全部', self, triggered=self.copyItemTextAll)
        at_del = QtGui.QAction('删除', self, triggered=self.delListWidgetItem)
        at_clear = QtGui.QAction('清空', self, triggered=self.clearListWidget)
        menu = QMenu()
        menu
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值