# -*- coding: utf-8 -*- """ @File : chat.py @Author : Shuaikang Zhou @Time : 2022/12/13 17:07 @IDE : Pycharm @Version : Python3.10 @comment : 聊天窗口 """ import datetime import time import xmltodict from PIL import Image from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from .chatUi import * from ...DataBase import data from ...ImageBox.ui import MainDemo class ChatController(QWidget, Ui_Form): exitSignal = pyqtSignal() urlSignal = pyqtSignal(QUrl) # username = '' def __init__(self, Me, parent=None): super(ChatController, self).__init__(parent) self.chatroomFlag = None self.ta_avatar = None self.setupUi(self) self.message = self.message_2 self.frame = self.frame_2 self.scrollAreaWidgetContents = self.scrollAreaWidgetContents_2 self.label_remark = self.label_remark_2 self.textEdit = self.textEdit_2 self.setWindowTitle('WeChat') self.setWindowIcon(QIcon('./app/data/icon.png')) self.initui() self.Me = Me self.Thread = ChatMsg(self.Me.username, None) self.Thread.isSend_signal.connect(self.showMsg) self.Thread.okSignal.connect(self.setScrollBarPos) self.contacts = {} self.last_btn = None self.chat_flag = True # self.showChat() self.message.verticalScrollBar().valueChanged.connect(self.textbrower_verticalScrollBar) self.show_flag = False self.ta_username = None self.last_pos = 0 self.last_msg_time = 0 # 上次信息的时间 self.last_talkerId = None self.now_talkerId = None self.showChat() def initui(self): self.qurl = QUrl('baidu.com') # self.urlSignal.connect(self.hyperlink) self.message.setOpenLinks(False) self.message.setOpenExternalLinks(False) # self.message.anchorClicked(self.hyperlink()) self.message.anchorClicked.connect(self.hyperlink) # self.btn_sendMsg = QtWidgets.QPushButton(self.textEdit) # self.btn_sendMsg.setGeometry(QtCore.QRect(1, 1, 121, 51)) # font = QtGui.QFont() # font.setFamily("黑体") # font.setPointSize(15) # font.setBold(False) # font.setWeight(50) # self.btn_sendMsg.setFont(font) # self.btn_sendMsg.setCursor(QtGui.QCursor(QtCore.Qt.ArrowCursor)) # self.btn_sendMsg.setMouseTracking(False) # self.btn_sendMsg.setAutoFillBackground(False) # self.btn_sendMsg.setStyleSheet("QPushButton {background-color: #f0f0f0;\n" # "padding: 10px;\n" # "color:rgb(5,180,104);}\n" # "QPushButton:hover{background-color: rgb(198,198,198)}\n" # ) # self.btn_sendMsg.setIconSize(QtCore.QSize(40, 40)) # self.btn_sendMsg.setCheckable(False) # self.btn_sendMsg.setAutoDefault(True) # self.btn_sendMsg.setObjectName("btn_sendMsg") # _translate = QtCore.QCoreApplication.translate # self.btn_sendMsg.setText(_translate("Dialog", "发送")) self.btn_sendMsg_2.setToolTip('按Enter键发送,按Ctrl+Enter键换行') def showChat(self): """ 显示联系人界面 :return: """ if self.show_flag: return self.show_flag = True rconversations = data.get_rconversation() # max_hight = max(len(rconversations) * 80, 680) max_hight = max(len(rconversations) * 80, self.size().height()) self.scrollAreaWidgetContents.setGeometry( QtCore.QRect(0, 0, 300, max_hight)) for i in range(len(rconversations)): rconversation = rconversations[i] username = rconversation[1] # print('联系人:', i, rconversation) pushButton_2 = Contact(self.scrollAreaWidgetContents, i, rconversation) pushButton_2.setGeometry(QtCore.QRect(0, 80 * i, 300, 80)) pushButton_2.setLayoutDirection(QtCore.Qt.LeftToRight) pushButton_2.clicked.connect(pushButton_2.show_msg) pushButton_2.usernameSingal.connect(self.Chat) self.contacts[username] = pushButton_2 def Chat(self, talkerId): """ 聊天界面 点击联系人头像时候显示聊天数据 :param talkerId: :return: """ self.now_talkerId = talkerId # 把当前按钮设置为灰色 if self.last_talkerId and self.last_talkerId != talkerId: print('对方账号:', self.last_talkerId) self.contacts[self.last_talkerId].setStyleSheet( "QPushButton {background-color: rgb(220,220,220);}" "QPushButton:hover{background-color: rgb(208,208,208);}\n" ) self.last_talkerId = talkerId self.contacts[talkerId].setStyleSheet( "QPushButton {background-color: rgb(198,198,198);}" "QPushButton:hover{background-color: rgb(209,209,209);}\n" ) conRemark = self.contacts[talkerId].conRemark self.label_remark.setText(conRemark) self.message.clear() self.message.append(talkerId) self.ta_username = talkerId if '@chatroom' in talkerId: self.chatroomFlag = True else: self.chatroomFlag = False self.ta_avatar = self.contacts[talkerId].avatar self.textEdit.setFocus() self.Thread.ta_u = talkerId self.Thread.msg_id = 0 self.Thread.start() # 创建新的线程用于显示聊天记录 def closeEvent(self, a0: QtGui.QCloseEvent) -> None: print("closed") self.exitSignal.emit() self.close() def textbrower_verticalScrollBar(self, pos): """ 滚动条到0之后自动更新聊天记录 :param pos: :return: """ # print(pos) if pos > 0: return self.last_pos = self.message.verticalScrollBar().maximum() self.Thread.start() def setScrollBarPos(self, pos): """ 将滚动条位置设置为上次看到的地方 :param pos: :return: """ pos = self.message.verticalScrollBar().maximum() - self.last_pos print(pos) self.message.verticalScrollBar().setValue(pos) def check_time(self, msg_time): """ 判断两次聊天时间是否大于五分钟 超过五分钟就显示时间 :param msg_time: :return: """ dt = msg_time - self.last_msg_time # print(msg_time) if abs(dt // 1000) >= 300: s_l = time.localtime(msg_time / 1000) ts = time.strftime("%Y-%m-%d %H:%M", s_l) html = '''
%s
''' % (ts) # print(html) self.last_msg_time = msg_time self.message.insertHtml(html) def showMsg(self, message): """ 显示聊天消息 :param message: :return: """ msgId = message[0] # print(msgId, type(msgId)) self.message.moveCursor(self.message.textCursor().Start) ta_username = message[7] msgType = str(message[2]) isSend = message[4] content = message[8] imgPath = message[9] msg_time = message[6] self.check_time(msg_time) if msgType == '1': # return self.show_text(isSend, content) elif msgType == '3': # return self.show_img(isSend, imgPath, content) elif msgType == '47': # return self.show_emoji(isSend, imgPath, content) elif msgType == '268445456': self.show_recall_information(content) elif msgType == '922746929': self.pat_a_pat(content) # self.message.moveCursor(self.message.textCursor().End) def pat_a_pat(self, content): try: pat_data = xmltodict.parse(content) pat_data = pat_data['msg']['appmsg']['patMsg']['records']['record'] fromUser = pat_data['fromUser'] pattedUser = pat_data['pattedUser'] template = pat_data['template'] template = ''.join(template.split('${pattedusername@textstatusicon}')) template = ''.join(template.split('${fromusername@textstatusicon}')) template = template.replace(f'${{{fromUser}}}', data.get_conRemark(fromUser)) template = template.replace(f'${{{pattedUser}}}', data.get_conRemark(pattedUser)) print(template) except Exception as e: print(e) template = '糟糕!出错了。' html = '''
%s
''' % template self.message.insertHtml(html) def show_recall_information(self, content): html = '''
%s
''' % content self.message.insertHtml(html) def show_emoji(self, isSend, imagePath, content): imgPath = data.get_emoji(imagePath) print('emoji:', imgPath) if not imgPath: return False image = Image.open(imgPath) imagePixmap = image.size # 宽高像素 # 设置最大宽度 if imagePixmap[0] < 150: size = "" else: size = '''height="150" width="150"''' html = ''' '''.format(imgPath, size) style = 'vertical-align: top' if isSend: self.right(html, style=style) else: if self.chatroomFlag: username = content.split(':')[0] self.chatroom_left(html, username=username, style=style) self.left(html, style=style) def show_img(self, isSend, imgPath, content): 'THUMBNAIL_DIRPATH://th_29cd0f0ca87652943be9ede365aabeaa' # imgPath = imgPath.split('th_')[1] imgPath = data.get_imgPath(imgPath) imgPath = f'./app/data/image2/{imgPath[0:2]}/{imgPath[2:4]}/{imgPath}' html = ''' ''' % (imgPath, imgPath) style = 'vertical-align: top' if isSend: self.right(html, style=style) else: if self.chatroomFlag: username = content.split(':')[0] self.chatroom_left(html, username=username, style=style) else: self.left(html, style=style) def show_text(self, isSend, content): if isSend: html = '''  %s  ''' % content self.right(html) else: if self.chatroomFlag: # print(content) 'wxid_mv4jjhc0w0w521:' username = content.split(':')[0] msg = ''.join(content.split(':')[1:]) # avatar = data.get_avator(username) html = ''' %s ''' % (msg) # self.left(html, avatar=avatar) self.chatroom_left(html, username=username) else: html = '''  %s  ''' % (content) self.left(html) def hyperlink(self, url: QUrl): """ 超链接,点击之后放大显示图片 :param url: :return: """ path = data.clearImagePath(url.path()) print(url.path(), path) self.imagebox = MainDemo() self.imagebox.show() self.imagebox.box.set_image(path) def right(self, content, style='vertical-align: middle'): html = '''
%s
''' % (style, content, self.Me.my_avatar) # print('总的HTML') # print(html) self.message.insertHtml(html) def left(self, content, avatar=None, style='vertical-align: middle'): if not avatar: avatar = self.ta_avatar if self.chatroomFlag == 5: try: username, msg = content.split('\n') avatar = data.get_avator(username) html = '''
%s
''' % (style, avatar, msg) except: return else: html = '''
%s
''' % (style, avatar, content) self.message.insertHtml(html) def chatroom_left(self, content, username, style='vertical-align: middle'): # if username: avatar = data.get_avator(username) # conRemark = data.get_conRemark(username) conRemark = data.get_conRemark(username) html = '''
%s
%s
''' % (style, avatar, conRemark, content) self.message.insertHtml(html) def destroy_me(self): """注销账户""" pass class Contact(QtWidgets.QPushButton): """ 联系人类,继承自pyqt的按钮,里面封装了联系人头像等标签 """ usernameSingal = pyqtSignal(str) def __init__(self, Ui, id=None, contact=None): super(Contact, self).__init__(Ui) self.layoutWidget = QtWidgets.QWidget(Ui) self.layoutWidget.setObjectName("layoutWidget") self.gridLayout1 = QtWidgets.QGridLayout(self.layoutWidget) self.gridLayout1.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) self.gridLayout1.setContentsMargins(10, 10, 10, 10) self.gridLayout1.setSpacing(10) self.gridLayout1.setObjectName("gridLayout1") self.label_time = QtWidgets.QLabel(self.layoutWidget) font = QtGui.QFont() font.setPointSize(8) self.label_time.setFont(font) self.label_time.setLayoutDirection(QtCore.Qt.RightToLeft) self.label_time.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.label_time.setObjectName("label_time") self.gridLayout1.addWidget(self.label_time, 0, 2, 1, 1) self.label_remark = QtWidgets.QLabel(self.layoutWidget) font = QtGui.QFont() font.setFamily("Adobe 黑体 Std R") font.setPointSize(10) self.label_remark.setFont(font) self.label_remark.setObjectName("label_remark") self.gridLayout1.addWidget(self.label_remark, 0, 1, 1, 1) self.label_msg = QtWidgets.QLabel(self.layoutWidget) font = QtGui.QFont() font.setPointSize(8) self.label_msg.setFont(font) self.label_msg.setObjectName("label_msg") self.gridLayout1.addWidget(self.label_msg, 1, 1, 1, 2) self.label_avatar = QtWidgets.QLabel(self.layoutWidget) self.label_avatar.setMinimumSize(QtCore.QSize(60, 60)) self.label_avatar.setMaximumSize(QtCore.QSize(60, 60)) self.label_avatar.setLayoutDirection(QtCore.Qt.RightToLeft) self.label_avatar.setAutoFillBackground(False) self.label_avatar.setStyleSheet("background-color: #ffffff;") self.label_avatar.setInputMethodHints(QtCore.Qt.ImhNone) self.label_avatar.setFrameShape(QtWidgets.QFrame.NoFrame) self.label_avatar.setFrameShadow(QtWidgets.QFrame.Plain) self.label_avatar.setAlignment(QtCore.Qt.AlignLeading | QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter) self.label_avatar.setObjectName("label_avatar") self.gridLayout1.addWidget(self.label_avatar, 0, 0, 2, 1) self.gridLayout1.setColumnStretch(0, 1) self.gridLayout1.setColumnStretch(1, 6) self.gridLayout1.setRowStretch(0, 5) self.gridLayout1.setRowStretch(1, 3) self.setLayout(self.gridLayout1) self.setStyleSheet( "QPushButton {background-color: rgb(220,220,220);}" "QPushButton:hover{background-color: rgb(208,208,208);}\n" ) self.msgCount = contact[0] self.username = contact[1] self.conversationTime = contact[6] self.msgType = contact[7] self.digest = contact[8] hasTrunc = contact[10] attrflag = contact[11] if hasTrunc == 0: if attrflag == 0: self.digest = '[动画表情]' elif attrflag == 67108864: try: remark = data.get_conRemark(contact[9]) msg = self.digest.split(':')[1].strip('\n').strip() self.digest = f'{remark}:{msg}' except Exception as e: # print(self.digest) # print(e) pass else: pass self.show_info(id) def show_info(self, id): self.avatar = data.get_avator(self.username) # print(avatar) self.conRemark = data.get_conRemark(self.username) time = datetime.datetime.now().strftime("%m-%d %H:%M") msg = '还没说话' pixmap = QPixmap(self.avatar).scaled(60, 60) # 按指定路径找到图片 self.label_avatar.setPixmap(pixmap) # 在label上显示图片 self.label_remark.setText(self.conRemark) self.label_msg.setText(self.digest) self.label_time.setText(data.timestamp2str(self.conversationTime)[2:]) def show_msg(self): self.usernameSingal.emit(self.username) class ChatMsg(QThread): """ 多线程显示信息 """ isSend_signal = pyqtSignal(tuple) okSignal = pyqtSignal(int) def __init__(self, my_u, ta_u, parent=None): super().__init__(parent) self.sec = 2 # 默认1000秒 self.my_u = my_u self.ta_u = ta_u self.my_avatar = data.get_avator(my_u) self.msg_id = 0 def run(self): self.ta_avatar = data.get_avator(self.ta_u) messages = data.get_message(self.ta_u, self.msg_id) # messages.reverse() for message in messages: self.isSend_signal.emit(message) self.msg_id += 1 self.okSignal.emit(1) class myTextEdit(QtWidgets.QTextEdit): # 继承 原本组件 sendSignal = pyqtSignal(str) def __init__(self, parent): QtWidgets.QTextEdit.__init__(self, parent) self.parent = parent _translate = QtCore.QCoreApplication.translate self.setHtml(_translate("Dialog", "\n" "\n" "


")) def keyPressEvent(self, event): QtWidgets.QTextEdit.keyPressEvent(self, event) if event.key() == Qt.Key_Return: # 如果是Enter 按钮 modifiers = event.modifiers() if modifiers == Qt.ControlModifier: print('success press ctrl+enter key', self.toPlainText()) self.append('\0') return self.sendSignal.emit(self.toPlainText()) print('success press enter key', self.toPlainText())