I'm looking for help for my project with threading and sockets. I have to make a communication between a server and few clients, and I have to do a GUI. The issue is that when I use QRunnable, the GUI (line edit, buttons) does not do anything. For example, my function sender in the AcceptThread does not run at all, same isse with update_reply. (so my SenderThread does not launch) I did the same script for my server with QThread and that works perfectly, but only for one user.
So I want my GUI update with the clients message, and that I can send messages with my Sever too. Thank you for your help.
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socket
flag = False
arret = False
# Création d'une classe qui hérite de QThread pour gérer la réception des messages
class ReceiverThread(QThread):
# Signal émis lorsque des messages sont reçus
message_received = pyqtSignal(str)
def __init__(self, connexion, server_socket):
super().__init__()
self.conn = connexion
self.server_socket = server_socket
# La méthode run est appelée lorsque le thread démarre
def run(self):
print("ReceiverThread Up")
global flag, arret
try:
while not flag:
recep = self.conn.recv(1024).decode()
if recep == "arret" or recep == "bye":
print("Un client se déconnecte")
flag = True
if recep == "arret":
print("Arrêt du serveur")
arret = True
elif not recep:
self.conn.close()
flag = False
else:
print(f'User : {recep}\n')
# Émission du signal avec le message reçu
self.message_received.emit(recep)
except Exception as err:
print(err)
print("ReceiverThread ends\n")
def quitter(self):
QCoreApplication.instance().quit()
class SenderThread(QThread):
def __init__(self, reply, connexion):
super().__init__()
self.reply = reply
self.conn = connexion
def run(self):
print("SenderThread Up")
print(self.reply)
try:
try:
self.conn.send(self.reply.encode())
except ConnectionRefusedError as error:
print(error)
except ConnectionResetError as error:
print(error)
except Exception as err:
print(err)
print("SenderThread ends")
def quitter(self):
QCoreApplication.quit()
class AcceptThread(QRunnable):
def __init__(self, log, send, connect, server_socket, host, i):
super().__init__()
self.log = log
self.send = send
self.connect = connect
self.server_socket = server_socket
self.host = host
self.i = i
def run(self):
print(f"AcceptThread Up {self.i}")
global flag, arret
try:
while not arret:
print("En attente d'une nouvelle connexion")
self.conn, self.address = self.server_socket.accept()
self.connect.connect(self.sender)
print(f"Nouvelle connexion de {self.host} !")
self.receiver_thread = ReceiverThread(self.conn, self.server_socket)
self.receiver_thread.message_received.connect(self.update_reply)
self.receiver_thread.start()
self.receiver_thread.wait()
#on attend la fin du thread receive avant de close la connexion
self.conn.close()
flag = False
#on remet la variable globale flag en False pour que la boucle du ReceiverThread fonctionne
#nécessaire sinon à chaque itération de la boucle il y a une nouvelle connexion du bouton à la fonction sender
self.server_socket.close()
print("Socket closed")
self.quitter()
except Exception as err:
print(err)
print(f"AcceptThread ends {self.i}")
# Méthode appelée pour mettre à jour l'interface utilisateur avec le message reçu
def update_reply(self, message):
self.log.setText(message)
def listen(self):
self.server_socket.listen(100)
def sender(self):
reply = self.send.text()
self.sender_thread = SenderThread(reply, self.conn)
self.sender_thread.start()
self.sender_thread.wait()
def quitter(self):
QCoreApplication.instance().quit()
# Classe de la fenêtre principale
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.setWindowTitle("Serveur")
self.resize(250, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
# Création et connexion des widgets
self.label = QLabel("Logs")
self.label2 = QLabel("Message Serveur")
self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.label2.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.line_edit = QLineEdit()
self.line_edit2= QLineEdit()
self.countBtn = QPushButton("Envoyer")
self.btn_quit = QPushButton("Quitter")
self.dialog = QPushButton("?")
self.line_edit.setEnabled(False)
self.dialog.clicked.connect(self.button_clicked)
self.btn_quit.clicked.connect(self.quitter)
# Configuration du layout
layout = QGridLayout()
layout.addWidget(self.label, 0, 0)
layout.addWidget(self.label2, 2, 0)
layout.addWidget(self.line_edit, 1, 0)
layout.addWidget(self.line_edit2, 3, 0)
layout.addWidget(self.countBtn, 4, 0)
layout.addWidget(self.btn_quit, 5, 0)
layout.addWidget(self.dialog, 5, 1)
self.centralWidget.setLayout(layout)
self.label.setText(f"Log du serveur")
self.line_edit.setText(f"")
self.main_thread()
def main_thread(self):
log = self.line_edit
send = self.line_edit2
connect = self.countBtn.clicked
host, port = ('0.0.0.0', 11111)
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((host, port))
self.host = socket.gethostname()
self.listen()
#threadCount = QThreadPool.globalInstance().maxThreadCount()
threadCount = 2
pool = QThreadPool.globalInstance()
for i in range(threadCount):
runnable = AcceptThread(log, send, connect, self.server_socket, self.host, i)
pool.start(runnable)
def button_clicked(self, s):
dlg = QMessageBox(self)
dlg.setWindowTitle("Aide")
dlg.setText("Centrale du Serveur.")
dlg.setStandardButtons(QMessageBox.Ok)
dlg.setIcon(QMessageBox.Question)
dlg.exec()
# Méthode appelée lorsqu'on clique sur le bouton Quitter
def quitter(self):
global flag, arret
flag = True
arret = True
QCoreApplication.instance().quit()
def listen(self):
self.server_socket.listen(100)
# Configuration de l'application PyQt
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())
Client
import socket
import threading
import time, sys
flag = False
arret = False
def envoi(client_socket):
global flag, arret
while flag == False:
try:
message = str(input(">"))
client_socket.send(message.encode())
if message == "arret" or message == "bye":
print("Arret du serveur")
flag = True
arret = True
except ConnectionRefusedError as error:
print(error)
main()
except ConnectionResetError as error:
print(error)
main()
print("Arret de la Thread envoi")
def reception(client_socket):
global flag, arret
while not flag:
try:
reply = client_socket.recv(1024).decode("utf-8")
if not reply:
print("Le serveur n'est plus accessible...")
flag = True
arret = True
else:
print(f'Serveur : {reply}')
except ConnectionRefusedError as error:
print(error)
main()
except ConnectionResetError as error:
print(error)
main()
except BrokenPipeError as error:
print(f'{error} : Wait a few seconds')
main()
print("Arret de la Thread reception")
def main():
try :
host, port = ('127.0.0.1', 11111)
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((host,port))
while not arret:
t1 = threading.Thread (target=reception, args=[client_socket])
t2 = threading.Thread (target=envoi, args=[client_socket])
t1.start()
t2.start()
t1.join()
t2.join()
client_socket.close()
except ConnectionRefusedError:
print("Impossible de se connecter")
time.sleep(5)
main()
if __name__ == '__main__':
main()
I forgot QRunnable thing and just did a list "all_thread" for the client connections. Then I just append new connections in the list or remove when they disconnect. I used QThread instead of QRunnable and do not try to make a Thread for each connections now.
import sys
import time
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import socket
flag = False
arret = False
# Création d'une classe qui hérite de QThread pour gérer la réception des messages
class ReceiverThread(QThread):
# Signal émis lorsque des messages sont reçus
message_received = pyqtSignal(str)
def __init__(self, connexion, server_socket, all_threads):
super().__init__()
self.conn = connexion
self.server_socket = server_socket
self.all_threads = all_threads
# La méthode run est appelée lorsque le thread démarre
def run(self):
print("ReceiverThread Up")
global flag, arret
try:
while not flag:
recep = self.conn.recv(1024).decode()
**if recep == "arret" or recep == "bye":
print("Un client se déconnecte")
for conn in self.all_threads:
if conn != self.conn:
continue
else:
# Fermer uniquement la connexion qui a dit "bye"
conn.close()
self.all_threads.remove(conn)**
if recep == "arret":
print("Arrêt du serveur")
arret = True
self.quitter()
elif not recep:
for conn in self.all_threads:
if conn != self.conn:
continue
else:
# Fermer uniquement la connexion qui a dit "bye"
conn.close()
self.all_threads.remove(conn)
else:
print(f'User : {recep}\n')
# Émission du signal avec le message reçu
self.message_received.emit(recep)
except Exception as err:
print(f"{err}")
print("ReceiverThread ends\n")
def quitter(self):
for conn in self.all_threads:
conn.close()
self.server_socket.close()
QCoreApplication.instance().quit()
class SenderThread(QThread):
def __init__(self, reply, all_threads):
super().__init__()
self.reply = reply
self.all_threads = all_threads
def run(self):
print("SenderThread Up")
print(self.reply)
try:
try:
**for conn in self.all_threads:
conn.send(self.reply.encode())**
except ConnectionRefusedError as error:
print(error)
except ConnectionResetError as error:
print(error)
except Exception as err:
print(err)
print("SenderThread ends")
def quitter(self):
QCoreApplication.instance().quit()
class AcceptThread(QThread):
def __init__(self, server_socket, log, send, connect):
super().__init__()
self.server_socket = server_socket
self.log = log
self.send = send
self.connect = connect
def run(self):
print("AcceptThread Up")
global flag, arret
**self.all_threads = []**
try:
host, port = ('0.0.0.0', 11111)
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.server_socket.bind((host, port))
self.host = socket.gethostname()
self.listen()
self.connect.connect(self.sender)
while not arret:
print("En attente d'une nouvelle connexion")
self.conn, self.address = self.server_socket.accept()
print(f"Nouvelle connexion de {self.host} !")
self.receiver_thread = ReceiverThread(self.conn, self.server_socket, self.all_threads)
self.receiver_thread.message_received.connect(self.update_reply)
self.receiver_thread.message_received.connect(self.send_everyone)
self.receiver_thread.start()
**self.all_threads.append(self.conn)**
#nécessaire sinon à chaque itération de la boucle il y a une nouvelle connexion du bouton à la fonction sender
else:
for conn in self.all_threads:
conn.close()
self.server_socket.close()
print("Socket closed")
self.quitter()
except Exception as err:
print(err)
print("AcceptThread ends")
# Méthode appelée pour mettre à jour l'interface utilisateur avec le message reçu
def update_reply(self, message):
self.log.append(f'{self.host}: {message}')
def listen(self):
self.server_socket.listen(100)
def sender(self):
reply = self.send.text()
self.sender_thread = SenderThread(f'Serveur : {reply}', self.all_threads)
self.sender_thread.start()
self.sender_thread.wait()
def send_everyone(self, message):
print("message")
self.sender_thread = SenderThread(message, self.all_threads)
self.sender_thread.start()
self.sender_thread.wait()
def quitter(self):
QCoreApplication.instance().quit()
# Classe de la fenêtre principale
class Window(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi()
def setupUi(self):
self.setWindowTitle("Serveur")
self.resize(250, 150)
self.centralWidget = QWidget()
self.setCentralWidget(self.centralWidget)
# Création et connexion des widgets
self.label = QLabel("Logs")
self.label2 = QLabel("Message Serveur")
self.label.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.label2.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter)
self.text_edit = QTextEdit()
self.text_edit.setReadOnly(True)
self.line_edit2= QLineEdit()
self.countBtn = QPushButton("Envoyer")
self.btn_quit = QPushButton("Quitter")
self.dialog = QPushButton("?")
self.dialog.clicked.connect(self.button_clicked)
self.btn_quit.clicked.connect(self.quitter)
# Configuration du layout
layout = QGridLayout()
layout.addWidget(self.label, 0, 0)
layout.addWidget(self.label2, 2, 0)
layout.addWidget(self.text_edit, 1, 0, 1, 2)
layout.addWidget(self.line_edit2, 3, 0)
layout.addWidget(self.countBtn, 4, 0)
layout.addWidget(self.btn_quit, 5, 0)
layout.addWidget(self.dialog, 5, 1)
self.centralWidget.setLayout(layout)
self.label.setText(f"Log du serveur")
self.text_edit.setText(f"")
self.main_thread()
def main_thread(self):
log = self.text_edit
send = self.line_edit2
connect = self.countBtn.clicked
self.accept_thread = AcceptThread(self, log, send, connect)
self.accept_thread.start()
def button_clicked(self, s):
dlg = QMessageBox(self)
dlg.setWindowTitle("Aide")
dlg.setText("Centrale du Serveur.")
dlg.setStandardButtons(QMessageBox.Ok)
dlg.setIcon(QMessageBox.Question)
dlg.exec()
# Méthode appelée lorsqu'on clique sur le bouton Quitter
def quitter(self):
global flag, arret
flag = True
arret = True
QCoreApplication.instance().quit()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = Window()
window.show()
sys.exit(app.exec())