diff --git a/client/gui.py b/client/gui.py index 1ee4cc5..bff2966 100644 --- a/client/gui.py +++ b/client/gui.py @@ -1,4 +1,5 @@ import json +import logging import socket import traceback @@ -13,6 +14,10 @@ from client.dialog import NicknameDialog IP = '127.0.0.1' PORT = 55555 +logging.basicConfig(level=logging.DEBUG, + format='[%(asctime)s] [%(levelname)s] [%(threadName)s] %(message)s') +logger = logging.getLogger('gui') + HEADER_LENGTH = 10 @@ -21,11 +26,16 @@ class ReceiveWorker(QThread): client_list = pyqtSignal(list) error = pyqtSignal() sent_nick = pyqtSignal() + logs = pyqtSignal(dict) def __init__(self, client: socket.socket, nickname: str, parent=None): QThread.__init__(self, parent) self.client = client self.nickname = nickname + self.__isRunning = True + + def stop(self) -> None: + self.__isRunning = False def __extract_message(self, data) -> dict: return { @@ -36,39 +46,58 @@ class ReceiveWorker(QThread): 'id': data['id'] } + def log(self, message: str, level: int = logging.INFO, error: Exception = None): + """Send a log message out from this QThread to the MainThread""" + self.logs.emit( + { + 'message': message, + 'level': level, + 'error': error + } + ) + def run(self): - while True: - try: - raw_length = self.client.recv(HEADER_LENGTH).decode('utf-8') - if not raw_length: - continue - raw = self.client.recv(int(raw_length)).decode('utf-8') - if not raw: - continue - message = json.loads(raw) + try: + while self.__isRunning: + try: + raw_length = self.client.recv(HEADER_LENGTH).decode('utf-8') + if not raw_length: + continue + raw = self.client.recv(int(raw_length)).decode('utf-8') + if not raw: + continue + message = json.loads(raw) - if message['type'] == constants.Types.REQUEST: - if message['request'] == constants.Requests.REQUEST_NICK: - self.client.send(helpers.prepare_json( - { - 'type': constants.Types.NICKNAME, - 'nickname': self.nickname - } - )) - self.sent_nick.emit() - elif message['type'] == constants.Types.MESSAGE: - self.messages.emit(self.__extract_message(message)) - elif message['type'] == constants.Types.USER_LIST: - self.client_list.emit(message['users']) - elif message['type'] == constants.Types.MESSAGE_HISTORY: - for submessage in message['messages']: - self.messages.emit(self.__extract_message(submessage)) + if message['type'] == constants.Types.REQUEST: + self.log(f'Data[{int(raw_length)}] received, {message["type"]}/{message["request"]}.', level=logging.DEBUG) + else: + self.log(f'Data[{int(raw_length)}] received, {message["type"]}.', level=logging.DEBUG) - except Exception as e: - traceback.print_exc() - self.error.emit() - self.client.close() - break + + if message['type'] == constants.Types.REQUEST: + if message['request'] == constants.Requests.REQUEST_NICK: + self.client.send(helpers.prepare_json( + { + 'type': constants.Types.NICKNAME, + 'nickname': self.nickname + } + )) + self.sent_nick.emit() + elif message['type'] == constants.Types.MESSAGE: + self.messages.emit(self.__extract_message(message)) + elif message['type'] == constants.Types.USER_LIST: + self.client_list.emit(message['users']) + elif message['type'] == constants.Types.MESSAGE_HISTORY: + for submessage in message['messages']: + self.messages.emit(self.__extract_message(submessage)) + + except Exception as e: + self.log(str(e), level=logging.CRITICAL, error=e) + self.error.emit() + break + finally: + self.log('Closing socket.', level=logging.INFO) + self.client.close() class MainWindow(QMainWindow, Ui_MainWindow): @@ -83,7 +112,10 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.nicknameDialog = NicknameDialog(self) self.nicknameDialog.exec_() self.nickname = self.nicknameDialog.lineEdit.text().strip() - if len(self.nickname) >= 3 or self.closed: + if len(self.nickname) >= 3: + self.closed = True + break + elif self.closed: break # Connect to server @@ -94,6 +126,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.receiveThread = ReceiveWorker(self.client, self.nickname) self.receiveThread.messages.connect(self.addMessage) self.receiveThread.client_list.connect(self.update_connections) + self.receiveThread.logs.connect(self.log) self.receiveThread.start() self.connectionsListTimer = QTimer() @@ -106,14 +139,23 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.messages = [] + def log(self, log_data: dict) -> None: + logger.log(level=log_data['level'], msg=log_data['message'], exc_info=log_data['error']) + def _ready(self): + logger.debug('Nickname sent. Ready to communicate with server further...') self.refresh_connections() self.get_message_history() def closeEvent(self, event): - if self.nicknameDialog: + if self.nicknameDialog and not self.closed: + logger.debug('Closing nickname dialog before main window') self.closed = True self.nicknameDialog.close() + else: + self.receiveThread.stop() + self.connectionsListTimer.stop() + event.accept() # let the window close def eventFilter(self, obj, event) -> bool: @@ -130,11 +172,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): def addMessage(self, message: dict) -> None: self.messages.append(message) - self.messageHistory.append(f'<{message["nickname"]}> {message["message"]}') + self.messageHistory.append( + f'<{message["nickname"]}> {message["message"]}') def sendMessage(self, message: str) -> None: message = message.strip() if len(message) > 0: + logger.info(f'Sending message of length {len(message)}') self.client.send(helpers.prepare_json( { 'type': constants.Types.MESSAGE, @@ -143,6 +187,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): )) def refresh_connections(self) -> None: + logger.info('Requesting connections list') self.client.send(helpers.prepare_json( { 'type': constants.Types.REQUEST, @@ -151,6 +196,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): )) def get_message_history(self) -> None: + logger.info('Requesting message history') self.client.send(helpers.prepare_json( { 'type': constants.Types.REQUEST,