赞
踩
本文章实现以下功能:
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。