diff --git a/client/ConnectionDialog.py b/client/ConnectionDialog.py index adfd0a0..08d081c 100644 --- a/client/ConnectionDialog.py +++ b/client/ConnectionDialog.py @@ -78,7 +78,6 @@ class Ui_ConnectionDialog(object): self.connection_groupbox.setObjectName("connection_groupbox") self.gridLayout_4 = QtWidgets.QGridLayout(self.connection_groupbox) self.gridLayout_4.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize) - self.gridLayout_4.setContentsMargins(-1, -1, -1, 0) self.gridLayout_4.setObjectName("gridLayout_4") self.nickname_layout = QtWidgets.QHBoxLayout() self.nickname_layout.setObjectName("nickname_layout") diff --git a/client/MainWindow.py b/client/MainWindow.py index 724cb32..fd8a1d3 100644 --- a/client/MainWindow.py +++ b/client/MainWindow.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Form implementation generated from reading ui file '.\client\MainWindow.ui' +# Form implementation generated from reading ui file '.\client\ui\MainWindow.ui' # # Created by: PyQt5 UI code generator 5.15.2 # @@ -14,7 +14,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName("MainWindow") - MainWindow.resize(800, 643) + MainWindow.resize(800, 625) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(MainWindow.sizePolicy().hasHeightForWidth()) + MainWindow.setSizePolicy(sizePolicy) + MainWindow.setMinimumSize(QtCore.QSize(0, 20)) self.centralwidget = QtWidgets.QWidget(MainWindow) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -24,26 +30,12 @@ class Ui_MainWindow(object): self.centralwidget.setObjectName("centralwidget") self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) self.gridLayout.setObjectName("gridLayout") - self.messageBox = QtWidgets.QTextEdit(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.messageBox.sizePolicy().hasHeightForWidth()) - self.messageBox.setSizePolicy(sizePolicy) - self.messageBox.setMaximumSize(QtCore.QSize(16777215, 80)) - self.messageBox.setObjectName("messageBox") - self.gridLayout.addWidget(self.messageBox, 2, 1, 1, 2) - self.label = QtWidgets.QLabel(self.centralwidget) - self.label.setObjectName("label") - self.gridLayout.addWidget(self.label, 0, 2, 1, 1) - self.messageHistory = QtWidgets.QTextBrowser(self.centralwidget) - sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(5) - sizePolicy.setHeightForWidth(self.messageHistory.sizePolicy().hasHeightForWidth()) - self.messageHistory.setSizePolicy(sizePolicy) - self.messageHistory.setObjectName("messageHistory") - self.gridLayout.addWidget(self.messageHistory, 1, 1, 1, 1) + self.chat_history_label = QtWidgets.QLabel(self.centralwidget) + self.chat_history_label.setObjectName("chat_history_label") + self.gridLayout.addWidget(self.chat_history_label, 0, 1, 1, 1) + self.connections_label = QtWidgets.QLabel(self.centralwidget) + self.connections_label.setObjectName("connections_label") + self.gridLayout.addWidget(self.connections_label, 0, 2, 1, 1) self.connectionsList = QtWidgets.QListWidget(self.centralwidget) sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -53,14 +45,23 @@ class Ui_MainWindow(object): self.connectionsList.setMaximumSize(QtCore.QSize(200, 16777215)) self.connectionsList.setObjectName("connectionsList") self.gridLayout.addWidget(self.connectionsList, 1, 2, 1, 1) - self.label_2 = QtWidgets.QLabel(self.centralwidget) - self.label_2.setObjectName("label_2") - self.gridLayout.addWidget(self.label_2, 0, 1, 1, 1) - spacerItem = QtWidgets.QSpacerItem(100, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) - self.gridLayout.addItem(spacerItem, 3, 1, 1, 1) - self.data_stats = QtWidgets.QLabel(self.centralwidget) - self.data_stats.setObjectName("data_stats") - self.gridLayout.addWidget(self.data_stats, 3, 2, 1, 1) + self.messageBox = QtWidgets.QTextEdit(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Maximum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.messageBox.sizePolicy().hasHeightForWidth()) + self.messageBox.setSizePolicy(sizePolicy) + self.messageBox.setMaximumSize(QtCore.QSize(16777215, 80)) + self.messageBox.setObjectName("messageBox") + self.gridLayout.addWidget(self.messageBox, 2, 1, 1, 2) + self.messageHistory = QtWidgets.QTextBrowser(self.centralwidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(5) + sizePolicy.setHeightForWidth(self.messageHistory.sizePolicy().hasHeightForWidth()) + self.messageHistory.setSizePolicy(sizePolicy) + self.messageHistory.setObjectName("messageHistory") + self.gridLayout.addWidget(self.messageHistory, 1, 1, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtWidgets.QMenuBar(MainWindow) self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 26)) @@ -68,6 +69,9 @@ class Ui_MainWindow(object): self.menuFile = QtWidgets.QMenu(self.menubar) self.menuFile.setObjectName("menuFile") MainWindow.setMenuBar(self.menubar) + self.status_bar = QtWidgets.QStatusBar(MainWindow) + self.status_bar.setObjectName("status_bar") + MainWindow.setStatusBar(self.status_bar) self.actionConnect_to = QtWidgets.QAction(MainWindow) self.actionConnect_to.setObjectName("actionConnect_to") self.actionSave_chat_to = QtWidgets.QAction(MainWindow) @@ -85,9 +89,12 @@ class Ui_MainWindow(object): def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "TCPChat")) - self.label.setText(_translate("MainWindow", "Connections")) - self.label_2.setText(_translate("MainWindow", "Chat History")) - self.data_stats.setText(_translate("MainWindow", "0.00KB Sent, 0.00KB Received")) + self.chat_history_label.setText(_translate("MainWindow", "Chat History")) + self.connections_label.setText(_translate("MainWindow", "Connections")) + self.connectionsList.setStatusTip(_translate("MainWindow", "View and/or moderate all users connected to the server.")) + self.messageBox.setStatusTip(_translate("MainWindow", "Type a message to send to the server.")) + self.messageBox.setPlaceholderText(_translate("MainWindow", "Type your message here...")) + self.messageHistory.setStatusTip(_translate("MainWindow", "View all messages sent by the server and other users.")) self.menuFile.setTitle(_translate("MainWindow", "File")) self.actionConnect_to.setText(_translate("MainWindow", "Connect to...")) self.actionSave_chat_to.setText(_translate("MainWindow", "Export chat...")) diff --git a/client/dialog.py b/client/dialog.py index 0024e7a..0decdc3 100644 --- a/client/dialog.py +++ b/client/dialog.py @@ -1,9 +1,8 @@ import re from typing import Tuple -from PyQt5 import QtCore, QtGui from PyQt5.QtCore import QEvent -from PyQt5.QtWidgets import QDialog, QStatusBar, QWidget, QSpacerItem, QSizePolicy +from PyQt5.QtWidgets import QDialog, QStatusBar import constants from client.ConnectionDialog import Ui_ConnectionDialog diff --git a/client/gui.py b/client/gui.py index 81ae8d5..0070d81 100644 --- a/client/gui.py +++ b/client/gui.py @@ -1,17 +1,14 @@ import logging import socket -from pprint import pprint - -from PyQt5.QtCore import Qt, QEvent, QTimer -from PyQt5.QtWidgets import QMainWindow +from typing import List +from PyQt5.QtCore import Qt, QEvent +from PyQt5.QtWidgets import QMainWindow, QLabel from sortedcontainers import SortedList import constants import helpers -import re from client.MainWindow import Ui_MainWindow -from client.dialog import NicknameDialog from client.worker import ReceiveWorker IP = '127.0.0.1' @@ -23,7 +20,7 @@ logger.setLevel(logging.DEBUG) class MainWindow(QMainWindow, Ui_MainWindow): - def __init__(self, ip:str, port:int, nickname: str, *args, **kwargs): + def __init__(self, ip: str, port: int, nickname: str, *args, **kwargs): # Initial UI setup super(MainWindow, self).__init__(*args, **kwargs) self.setupUi(self) @@ -44,12 +41,16 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.receiveThread.logs.connect(self.log) # Receiving logging messages from a thread self.receiveThread.data_stats.connect(self.count_stats) # Receiving data usage stats # TODO: Improve initial client/server data exchange - self.receiveThread.sent_nick.connect(self._ready) self.receiveThread.start() - self.messageBox.setPlaceholderText('Type your message here...') + # Setup message box return key logic self.messageBox.installEventFilter(self) + self.data_stats = QLabel("0.00KB Sent, 0.00KB Received") + self.data_stats.setAlignment(Qt.AlignVCenter) + self.status_bar.addPermanentWidget(self.data_stats) + + # Variables for managing self.messages = SortedList(key=lambda message: message['time']) self.added_messages = [] self.max_message_id = -1 @@ -57,20 +58,22 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.sent, self.received = 0, 0 def count_stats(self, sent: bool, change: int) -> None: - if sent: - self.sent += change - else: - self.received += change - self.data_stats.setText(f'{helpers.sizeof_fmt(self.sent)} Sent, {helpers.sizeof_fmt(self.received)} Received') + """Handler for counting data statistics.""" + if change != 0: + if sent: + self.sent += change + else: + self.received += change + self.data_stats.setText(f'{helpers.sizeof_fmt(self.sent)} Sent, ' + f'{helpers.sizeof_fmt(self. received)} Received') - def log(self, log_data: dict) -> None: + @staticmethod + def log(log_data: dict) -> None: + """Handler for data logging from a thread.""" 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.get_message_history() - def closeEvent(self, event): + """Handle closing by stopping the receive thread.""" self.receiveThread.stop() event.accept() # let the window close @@ -89,14 +92,14 @@ class MainWindow(QMainWindow, Ui_MainWindow): def refresh_messages(self) -> None: """Completely refresh the chat box text.""" scrollbar = self.messageHistory.verticalScrollBar() - lastPosition = scrollbar.value() - atMaximum = lastPosition == scrollbar.maximum() + last_position = scrollbar.value() + at_maximum = last_position == scrollbar.maximum() self.messageHistory.setText('
'.join( msg['compiled'] for msg in self.messages )) - scrollbar.setValue(scrollbar.maximum() if atMaximum else lastPosition) + scrollbar.setValue(scrollbar.maximum() if at_maximum else last_position) def send(self, data: bytes, **kwargs) -> None: self.count_stats(True, len(data)) @@ -129,15 +132,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): } )) - def get_message_history(self) -> None: - logger.info('Requesting message history') - self.send(helpers.prepare_json( - { - 'type': constants.Types.REQUEST, - 'request': constants.Requests.GET_MESSAGE_HISTORY - } - )) - - def update_connections(self, users): + def update_connections(self, users: List[dict]): + """Update the Connections List widget""" self.connectionsList.clear() self.connectionsList.addItems([user['nickname'] for user in users]) diff --git a/client/main.py b/client/main.py index c112358..3d78bcf 100644 --- a/client/main.py +++ b/client/main.py @@ -17,5 +17,5 @@ def main(nickname: str = None): if connect_dialog.connect_pressed: settings = connect_dialog.settings - m = MainWindow(settings.ip, settings.port, settings.nickname) + window = MainWindow(settings.ip, settings.port, settings.nickname) app.exec_() diff --git a/client/ui/MainWindow.ui b/client/ui/MainWindow.ui index 4dc737f..5e837c7 100644 --- a/client/ui/MainWindow.ui +++ b/client/ui/MainWindow.ui @@ -7,9 +7,21 @@ 0 0 800 - 643 + 625 + + + 0 + 0 + + + + + 0 + 20 + + TCPChat @@ -21,39 +33,20 @@ - - - - - 0 - 0 - - - - - 16777215 - 80 - + + + + Chat History - + Connections - - - - - 0 - 5 - - - - @@ -68,32 +61,43 @@ 16777215 - - - - - - Chat History + + View and/or moderate all users connected to the server. - - - - Qt::Horizontal + + + + + 0 + 0 + - + - 100 - 20 + 16777215 + 80 - + + Type a message to send to the server. + + + Type your message here... + + - - - - 0.00KB Sent, 0.00KB Received + + + + + 0 + 5 + + + + View all messages sent by the server and other users. @@ -118,6 +122,14 @@ + + + + 0 + 20 + + + Connect to...