diff --git a/client/ui/login_dialog.py b/client/ui/login_dialog.py index db8c47a..108bdbe 100644 --- a/client/ui/login_dialog.py +++ b/client/ui/login_dialog.py @@ -94,7 +94,7 @@ class LoginDialog(QDialog): self._login_server_input = QLineEdit() self._login_server_input.setPlaceholderText("服务器地址:端口") - self._login_server_input.setText("127.0.0.1:8888") + self._login_server_input.setText("113.45.148.222:8888") self._login_server_input.setMinimumHeight(38) self._login_server_input.setStyleSheet(self._get_input_style()) form_layout.addRow("服务器:", self._login_server_input) @@ -144,7 +144,7 @@ class LoginDialog(QDialog): self._reg_server_input = QLineEdit() self._reg_server_input.setPlaceholderText("服务器地址:端口") - self._reg_server_input.setText("127.0.0.1:8888") + self._reg_server_input.setText("113.45.148.222:8888") self._reg_server_input.setMinimumHeight(38) self._reg_server_input.setStyleSheet(self._get_input_style()) form_layout.addRow("服务器:", self._reg_server_input) diff --git a/run_client_gui.py b/run_client_gui.py index 54d7067..11fc33e 100644 --- a/run_client_gui.py +++ b/run_client_gui.py @@ -143,6 +143,39 @@ class AsyncWorker(QThread): return False return False + def send_image(self, peer_id: str, image_path: str) -> bool: + """发送图片(线程安全)""" + if self._loop and self._running: + future = asyncio.run_coroutine_threadsafe( + self.client.send_image(peer_id, image_path), + self._loop + ) + try: + return future.result(timeout=300.0) + except Exception as e: + logger.error(f"Send image error: {e}") + return False + return False + + def start_voice_call(self, peer_id: str) -> bool: + """发起语音通话(线程安全)""" + if self._loop and self._running: + future = asyncio.run_coroutine_threadsafe( + self.client.start_voice_call(peer_id), + self._loop + ) + try: + return future.result(timeout=30.0) + except Exception as e: + logger.error(f"Start voice call error: {e}") + return False + return False + + def end_voice_call(self): + """结束语音通话(线程安全)""" + if self._loop and self._running: + self.client.end_voice_call() + def refresh_users(self): """刷新用户列表(线程安全)""" if self._loop and self._running: @@ -335,6 +368,26 @@ class P2PChatGUI(QObject): f"收到来自 {sender} 的消息", 3000 ) + # 将消息添加到聊天窗口显示 + from datetime import datetime + from shared.models import ChatMessage + + msg = ChatMessage( + message_id=message.message_id, + sender_id=sender, + receiver_id=message.receiver_id, + content_type=MessageType.TEXT, + content=content, + timestamp=datetime.fromtimestamp(message.timestamp), + is_sent=True, + is_read=False + ) + + # 如果当前正在和发送者聊天,直接显示消息 + if hasattr(self, '_chat_widget') and self._chat_widget: + if self._current_chat_peer == sender: + self._chat_widget.add_message(msg, is_self=False) + logger.info(f"Message from {sender}: {content[:50]}...") elif message.msg_type == MessageType.VOICE_CALL_REQUEST: @@ -343,6 +396,8 @@ class P2PChatGUI(QObject): "来电", f"{sender} 正在呼叫你" ) + # 显示来电对话框 + self._show_incoming_call_dialog(sender) def _on_user_list_updated(self, users: List[UserInfo]): """用户列表更新回调""" @@ -379,7 +434,11 @@ class P2PChatGUI(QObject): def _on_send_file(self): """发送文件""" - from PyQt6.QtWidgets import QFileDialog + from PyQt6.QtWidgets import QFileDialog, QMessageBox + + if not self._current_chat_peer: + QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人") + return file_path, _ = QFileDialog.getOpenFileName( self.main_window, @@ -390,11 +449,20 @@ class P2PChatGUI(QObject): if file_path and self._current_chat_peer: self.main_window._statusbar.showMessage(f"正在发送文件...") - # TODO: 实现文件发送 + if self.worker: + success = self.worker.send_file(self._current_chat_peer, file_path) + if success: + self.main_window._statusbar.showMessage("文件发送成功", 3000) + else: + self.main_window._statusbar.showMessage("文件发送失败", 3000) def _on_send_image(self): """发送图片""" - from PyQt6.QtWidgets import QFileDialog + from PyQt6.QtWidgets import QFileDialog, QMessageBox + + if not self._current_chat_peer: + QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人") + return file_path, _ = QFileDialog.getOpenFileName( self.main_window, @@ -405,13 +473,60 @@ class P2PChatGUI(QObject): if file_path and self._current_chat_peer: self.main_window._statusbar.showMessage(f"正在发送图片...") - # TODO: 实现图片发送 + if self.worker: + success = self.worker.send_image(self._current_chat_peer, file_path) + if success: + self.main_window._statusbar.showMessage("图片发送成功", 3000) + else: + self.main_window._statusbar.showMessage("图片发送失败", 3000) def _on_voice_call(self): """发起语音通话""" - if self._current_chat_peer: - self.main_window._statusbar.showMessage(f"正在呼叫...") - # TODO: 实现语音通话 + from PyQt6.QtWidgets import QMessageBox + + if not self._current_chat_peer: + QMessageBox.warning(self.main_window, "提示", "请先选择一个联系人") + return + + self.main_window._statusbar.showMessage(f"正在呼叫...") + if self.worker: + success = self.worker.start_voice_call(self._current_chat_peer) + if success: + self.main_window._statusbar.showMessage("通话已建立", 3000) + else: + self.main_window._statusbar.showMessage("呼叫失败", 3000) + + def _show_incoming_call_dialog(self, caller_id: str): + """显示来电对话框""" + from PyQt6.QtWidgets import QMessageBox + + reply = QMessageBox.question( + self.main_window, + "来电", + f"{caller_id} 正在呼叫你,是否接听?", + QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No + ) + + if reply == QMessageBox.StandardButton.Yes: + # 接听 + if self.worker and self.worker._loop and self.worker._running: + future = asyncio.run_coroutine_threadsafe( + self.client.accept_voice_call(caller_id), + self.worker._loop + ) + try: + success = future.result(timeout=10.0) + if success: + self.main_window._statusbar.showMessage("通话已接通", 3000) + else: + self.main_window._statusbar.showMessage("接听失败", 3000) + except Exception as e: + logger.error(f"Accept call error: {e}") + else: + # 拒绝 + if self.client: + self.client.reject_voice_call(caller_id) + self.main_window._statusbar.showMessage("已拒绝来电", 3000) def _on_contact_selected(self, user_id: str): """联系人选中回调 - 打开聊天窗口""" @@ -452,6 +567,7 @@ class P2PChatGUI(QObject): message_id=f"msg_{datetime.now().timestamp()}", sender_id=self._current_user.user_id if self._current_user else "", receiver_id=peer_id, + content_type=MessageType.TEXT, content=content, timestamp=datetime.now(), is_sent=True