diff --git a/client/MainWindow.py b/client/MainWindow.py
index ea64ca6..aa7c091 100644
--- a/client/MainWindow.py
+++ b/client/MainWindow.py
@@ -14,7 +14,7 @@ from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
- MainWindow.resize(800, 609)
+ MainWindow.resize(800, 651)
self.centralwidget = QtWidgets.QWidget(MainWindow)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
@@ -24,31 +24,38 @@ class Ui_MainWindow(object):
self.centralwidget.setObjectName("centralwidget")
self.gridLayout = QtWidgets.QGridLayout(self.centralwidget)
self.gridLayout.setObjectName("gridLayout")
- self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
- self.horizontalLayout_2.setObjectName("horizontalLayout_2")
- self.textBrowser = QtWidgets.QTextBrowser(self.centralwidget)
+ self.label = QtWidgets.QLabel(self.centralwidget)
+ self.label.setObjectName("label")
+ self.gridLayout.addWidget(self.label, 0, 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(0)
- sizePolicy.setHeightForWidth(self.textBrowser.sizePolicy().hasHeightForWidth())
- self.textBrowser.setSizePolicy(sizePolicy)
- self.textBrowser.setMaximumSize(QtCore.QSize(16777215, 16777215))
- self.textBrowser.setObjectName("textBrowser")
- self.horizontalLayout_2.addWidget(self.textBrowser)
- self.gridLayout.addLayout(self.horizontalLayout_2, 0, 0, 1, 1)
- self.horizontalLayout = QtWidgets.QHBoxLayout()
- self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetDefaultConstraint)
- self.horizontalLayout.setObjectName("horizontalLayout")
- self.textEdit = QtWidgets.QTextEdit(self.centralwidget)
- sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed)
+ 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.connectionsList = QtWidgets.QListWidget(self.centralwidget)
+ sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Expanding)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
- sizePolicy.setHeightForWidth(self.textEdit.sizePolicy().hasHeightForWidth())
- self.textEdit.setSizePolicy(sizePolicy)
- self.textEdit.setMaximumSize(QtCore.QSize(16777215, 70))
- self.textEdit.setObjectName("textEdit")
- self.horizontalLayout.addWidget(self.textEdit)
- self.gridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
+ sizePolicy.setHeightForWidth(self.connectionsList.sizePolicy().hasHeightForWidth())
+ self.connectionsList.setSizePolicy(sizePolicy)
+ 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)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
@@ -76,6 +83,8 @@ class Ui_MainWindow(object):
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
+ self.label.setText(_translate("MainWindow", "Connections"))
+ self.label_2.setText(_translate("MainWindow", "Chat History"))
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/MainWindow.ui b/client/MainWindow.ui
index dcd03b8..d836ca3 100644
--- a/client/MainWindow.ui
+++ b/client/MainWindow.ui
@@ -7,7 +7,7 @@
0
0
800
- 609
+ 651
@@ -21,48 +21,61 @@
- -
-
-
-
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 16777215
-
-
-
-
-
-
- -
-
-
- QLayout::SetDefaultConstraint
+
-
+
+
+ Connections
-
-
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 70
-
-
-
-
-
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 80
+
+
+
+
+ -
+
+
+
+ 0
+ 5
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 200
+ 16777215
+
+
+
+
+ -
+
+
+ Chat History
+
+
diff --git a/client/Nickname.ui b/client/Nickname.ui
new file mode 100644
index 0000000..0122111
--- /dev/null
+++ b/client/Nickname.ui
@@ -0,0 +1,96 @@
+
+
+ NicknameDialog
+
+
+
+ 0
+ 0
+ 449
+ 85
+
+
+
+ Dialog
+
+
+ -
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::Maximum
+
+
+
+ 20
+ 10
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Ok
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ NicknameDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ NicknameDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/client/dialog.py b/client/dialog.py
new file mode 100644
index 0000000..670f3d5
--- /dev/null
+++ b/client/dialog.py
@@ -0,0 +1,35 @@
+from PyQt5.QtWidgets import QDialog
+
+from client.nickname import Ui_NicknameDialog
+
+
+class NicknameDialog(QDialog, Ui_NicknameDialog):
+
+ def __init__(self, *args, **kwargs):
+ super(NicknameDialog, self).__init__(*args, **kwargs)
+ self.setupUi(self)
+
+ self.disabled = True
+ self.buttonBox.setDisabled(True)
+
+ self.setWindowTitle("Set Nickname")
+
+ self.lineEdit.returnPressed.connect(self.trySubmit)
+ self.lineEdit.textEdited.connect(self.controlSubmit)
+ self.show()
+
+ def controlSubmit(self):
+ """Updates whether or not the dialog box is allowed to proceed"""
+ if len(self.lineEdit.text()) > 2:
+ if self.disabled:
+ self.disabled = False
+ self.buttonBox.setDisabled(False)
+ else:
+ if not self.disabled:
+ self.disabled = True
+ self.buttonBox.setDisabled(True)
+
+ def trySubmit(self):
+ """Tries to submit the Dialog through the QLineEdit via Enter key"""
+ if not self.disabled:
+ self.accept()
diff --git a/client/gui.py b/client/gui.py
index b3cf01f..e7097d7 100644
--- a/client/gui.py
+++ b/client/gui.py
@@ -1,11 +1,14 @@
+import json
import socket
-from pprint import pprint
+import traceback
-from PyQt5.QtCore import QThread, pyqtSignal, Qt, QEvent
-from PyQt5.QtWidgets import QMainWindow, QDialog, QDialogButtonBox, QVBoxLayout
+from PyQt5.QtCore import QThread, pyqtSignal, Qt, QEvent, QTimer
+from PyQt5.QtWidgets import QMainWindow
+import constants
+import helpers
from client.MainWindow import Ui_MainWindow
-from config import config
+from client.dialog import NicknameDialog
IP = '127.0.0.1'
PORT = 55555
@@ -15,6 +18,7 @@ HEADER_LENGTH = 10
class ReceiveWorker(QThread):
messages = pyqtSignal(str)
+ client_list = pyqtSignal(list)
error = pyqtSignal()
def __init__(self, client: socket.socket, nickname: str, parent=None):
@@ -25,73 +29,89 @@ class ReceiveWorker(QThread):
def run(self):
while True:
try:
- length = int(self.client.recv(HEADER_LENGTH).decode('ascii'))
- message = self.client.recv(length).decode('ascii')
- if message == 'NICK':
- header = f'{len(self.nickname):<{HEADER_LENGTH}}'
- final = header + self.nickname
- pprint(final)
- self.client.send(final.encode('ascii'))
- else:
- self.messages.emit(message)
+ length = int(self.client.recv(HEADER_LENGTH).decode('utf-8'))
+ raw = self.client.recv(length).decode('utf-8')
+ if len(raw) == 0:
+ 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
+ }
+ ))
+ elif message['type'] == constants.Types.MESSAGE:
+ self.messages.emit(message['content'])
+ elif message['type'] == constants.Types.USER_LIST:
+ self.client_list.emit(message['users'])
except:
+ traceback.print_exc()
self.error.emit()
self.client.close()
break
-class CustomDialog(QDialog):
-
- def __init__(self, *args, **kwargs):
- super(CustomDialog, self).__init__(*args, **kwargs)
-
- self.setWindowTitle("HELLO!")
-
- buttons = QDialogButtonBox.Ok | QDialogButtonBox.Cancel
-
- self.buttonBox = QDialogButtonBox(buttons)
- self.buttonBox.accepted.connect(self.accept)
- self.buttonBox.rejected.connect(self.reject)
-
- self.layout = QVBoxLayout()
- self.layout.addWidget(self.buttonBox)
- self.setLayout(self.layout)
-
-
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.setupUi(self)
+ self.show()
+ # Get Nickname
+ nicknameDialog = NicknameDialog(self)
+ nicknameDialog.exec_()
+ self.nickname = nicknameDialog.lineEdit.text()
+
+ # Connect to server
self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.client.connect((IP, PORT))
- # nicknameDialog = CustomDialog(self)
- # if nicknameDialog.exec_():
- # print('s')
-
- self.nickname = 'Default'
-
- self.textEdit.installEventFilter(self)
-
+ # Setup message receiving thread worker
self.receiveThread = ReceiveWorker(self.client, self.nickname)
self.receiveThread.messages.connect(self.addMessage)
+ self.receiveThread.client_list.connect(self.updateConnectionsList)
self.receiveThread.start()
- self.show()
+ self.connectionsListTimer = QTimer()
+ self.connectionsListTimer.timeout.connect(self.refreshConnectionsList)
+ self.connectionsListTimer.start(1000 * 30)
+ self.refreshConnectionsList()
+
+ self.messageBox.installEventFilter(self)
def eventFilter(self, obj, event):
- if event.type() == QEvent.KeyPress and obj is self.textEdit:
- if event.key() == Qt.Key_Return and self.textEdit.hasFocus():
- self.sendMessage(self.textEdit.toPlainText())
- self.textEdit.clear()
+ if event.type() == QEvent.KeyPress and obj is self.messageBox:
+ if event.key() == Qt.Key_Return and self.messageBox.hasFocus():
+ self.sendMessage(self.messageBox.toPlainText())
+ self.messageBox.clear()
+ self.messageBox.setText('')
+ cursor = self.messageBox.textCursor()
+ cursor.setPosition(0)
+ self.messageBox.setTextCursor(cursor)
return super().eventFilter(obj, event)
def addMessage(self, message: str) -> None:
- self.textBrowser.append(message)
+ self.messageHistory.append(message)
def sendMessage(self, message: str) -> None:
- header = f'{len(message):<{HEADER_LENGTH}}'
- final = header + message
- pprint(final)
- self.client.send(final.encode('ascii'))
+ self.client.send(helpers.prepare_json(
+ {
+ 'type': constants.Types.MESSAGE,
+ 'content': message
+ }
+ ))
+
+ def refreshConnectionsList(self):
+ self.client.send(helpers.prepare_json(
+ {
+ 'type': constants.Types.REQUEST,
+ 'request': constants.Requests.REFRESH_CONNECTIONS_LIST
+ }
+ ))
+
+ def updateConnectionsList(self, users):
+ self.connectionsList.clear()
+ self.connectionsList.addItems(users)
diff --git a/client/gui.ui b/client/gui.ui
deleted file mode 100644
index d836ca3..0000000
--- a/client/gui.ui
+++ /dev/null
@@ -1,120 +0,0 @@
-
-
- MainWindow
-
-
-
- 0
- 0
- 800
- 651
-
-
-
- MainWindow
-
-
-
-
- 0
- 0
-
-
-
- -
-
-
- Connections
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 16777215
- 80
-
-
-
-
- -
-
-
-
- 0
- 5
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
- 200
- 16777215
-
-
-
-
- -
-
-
- Chat History
-
-
-
-
-
-
-
-
-
- Connect to...
-
-
-
-
- Export chat...
-
-
-
-
- Quit
-
-
-
-
-
-
diff --git a/client/nickname.py b/client/nickname.py
new file mode 100644
index 0000000..ad0ebd1
--- /dev/null
+++ b/client/nickname.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file '.\Nickname.ui'
+#
+# Created by: PyQt5 UI code generator 5.15.2
+#
+# WARNING: Any manual changes made to this file will be lost when pyuic5 is
+# run again. Do not edit this file unless you know what you are doing.
+
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+
+
+class Ui_NicknameDialog(object):
+ def setupUi(self, NicknameDialog):
+ NicknameDialog.setObjectName("NicknameDialog")
+ NicknameDialog.resize(449, 85)
+ self.gridLayout = QtWidgets.QGridLayout(NicknameDialog)
+ self.gridLayout.setObjectName("gridLayout")
+ self.lineEdit = QtWidgets.QLineEdit(NicknameDialog)
+ self.lineEdit.setObjectName("lineEdit")
+ self.gridLayout.addWidget(self.lineEdit, 1, 0, 1, 1)
+ spacerItem = QtWidgets.QSpacerItem(20, 10, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Maximum)
+ self.gridLayout.addItem(spacerItem, 2, 0, 1, 1)
+ self.buttonBox = QtWidgets.QDialogButtonBox(NicknameDialog)
+ self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
+ self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName("buttonBox")
+ self.gridLayout.addWidget(self.buttonBox, 4, 0, 1, 1)
+ spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
+ self.gridLayout.addItem(spacerItem1, 0, 0, 1, 1)
+
+ self.retranslateUi(NicknameDialog)
+ self.buttonBox.accepted.connect(NicknameDialog.accept)
+ self.buttonBox.rejected.connect(NicknameDialog.reject)
+ QtCore.QMetaObject.connectSlotsByName(NicknameDialog)
+
+ def retranslateUi(self, NicknameDialog):
+ _translate = QtCore.QCoreApplication.translate
+ NicknameDialog.setWindowTitle(_translate("NicknameDialog", "Dialog"))