Francis Dolbec
Francis Dolbec

Reputation: 225

Using one socket with multiple window on a client application

Quick question here... (By the way, sorry if it has been already answered...) I have a QDialog window (created by a QMainWindow) where I created and used a socket. After some manipulations, the QDialog window closes (by using the QDialog.accept() command) then a new different QDialog window is opened (by the QMainWindow). From that new window, I want to be able to send infos to the server with the already opened socket.

I tried to do that by using these lines of code:

server = "192.168.100.195"
port = 5555
address = (server, port)
# ----- Some other stuff... ----
socket_de_connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Not using the socket.connect() because the previous QDialog already connected to the server

Then, when I tried to send something I get the following error:

OSError: [ErrnOSError: [Errno 57] Socket is not connected

But the server is showing me in the console that the socket created in the previous QDialog is still opened (I didn't closed it on purpose, because I want to continue to use it through the multiple windows of my program.)

Is there a way to reuse that already opened socket trough multiple window?

By the way, each window in my program has there code in separate file.

EDIT:

Here is some more infos to help out:

Code for my first QDialog:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import QRegExpValidator
import socket
import pickle
import sys
server = "192.168.100.195"
port = 5555


class Verification(QDialog):
    def __init__(self):
        super(Verification, self).__init__()
        self.boite_texte_code = QLineEdit(self)
        self.bouton_verifier = QPushButton(self)
        self.bouton_annuler2 = QPushButton(self)
        self.invite_code = QLabel(self)
        self.accept_local_test = "Ok"
        self.regex2 = QRegExp()
        self.validator2 = QRegExpValidator()
        self.msg = QMessageBox()
        self.socket_de_connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.init_ui()

    def init_ui(self):

        address = (server, port)
        self.socket_de_connexion.connect(address)
        # print(self.socket_connexion)
        self.resize(290, 110)
        self.center()
        self.setWindowTitle("Veuillez saisir le code d'invitation:")
        self.regex2 = QRegExp("^[0-9]{4}")
        self.validator2 = QRegExpValidator(self.regex2)
        self.boite_texte_code.setValidator(self.validator2)
        self.boite_texte_code.setGeometry(150, 20, 100, 25)
        self.bouton_annuler2.setText("Annuler")
        self.bouton_annuler2.setGeometry(157, 60, 100, 40)
        self.bouton_annuler2.clicked.connect(self.annuler2)
        self.bouton_verifier.setDefault(True)
        self.bouton_verifier.setEnabled(False)
        self.bouton_verifier.setText("Confirmer")
        self.bouton_verifier.setGeometry(57, 60, 100, 40)
        self.bouton_verifier.clicked.connect(self.confirmer2)
        self.invite_code.setText("Code d'invitation:")
        self.invite_code.adjustSize()
        self.invite_code.move(30, 25)
        self.boite_texte_code.textChanged.connect(self.texte_change)

    def center(self):
        # SOME OTHER CODE NOT NEEDED FOR THIS QUESTION

    def annuler2(self):
        self.socket_de_connexion.close()
        self.close()

    def confirmer2(self):
        print("Trying to send informations to server...")
        msg = self.socket_de_connexion.recv(2048)
        message = pickle.loads(msg)
        print(message)
        if message == "Connecté!":
            print("in...")
            data_to_send = pickle.dumps("join")
            self.socket_de_connexion.send(data_to_send)
            verif_pickled = self.socket_de_connexion.recv(2048)
            verif = pickle.loads(verif_pickled)
            if verif == "code?":
                print("asked")
                code_to_send = pickle.dumps(self.boite_texte_code.text())
                self.socket_de_connexion.send(code_to_send)
                answer_pickled = self.socket_de_connexion.recv(2048)
                answer = pickle.loads(answer_pickled)
                print(answer)
                if answer == "No":
                    print("Oups!")
                    self.msg.setIcon(QMessageBox.Critical)
                    self.msg.setText("Erreur! Le code est invalide!")
                    self.msg.setInformativeText(
                        "Veuillez vérifier la validité du code. Le programme va maintenant quitter.")
                    self.msg.setWindowTitle("Oups!")
                    self.msg.setStandardButtons(QMessageBox.Ok)
                    self.msg.exec_()
                    # add msg box here
                    # self.close()
                    # socket_de_connexion.shutdown()
                    # socket_de_connexion.close()
                    sys.exit()
                elif answer == "Yes":
                    print("yes")
                    #return self.socket_de_connexion
                    self.accept()
        if self.accept_local_test == "Ok":
            if self.boite_texte_code.text() != "":
                pass
                # self.accept()
        else:
            print("Nope!")

    def texte_change(self):
        # SOME OTHER CODE NOT NEEDED FOR THIS QUESTION


if __name__ == '__main__':
    app = QDialog()
    verification = Verification()
    verification.show()
    app.exec_()

And here is my second QDialog in a different file:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
import jeu
import socket
import pickle
server = "192.168.100.195"
port = 5555
address = (server, port)


class JoinPartie(QDialog):
    def __init__(self):
        super(JoinPartie, self).__init__()
        self.socket_de_connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.une_partie = None
        self.code = int
        self.setWindowTitle("Veuillez remplir les éléments suivants")
        self.boite_texte_username = QLineEdit(self)
        self.regex = QRegExp("^\w[\w|\s|\.]+")
        self.validator = QRegExpValidator(self.regex)
        self.boite_texte_username.setValidator(self.validator)
        self.bouton_creer = QPushButton(self)
        self.bouton_annuler = QPushButton(self)
        self.invite_username = QLabel(self)
        self.invite_couleur = QLabel(self)
        self.choix_de_couleur = QComboBox(self)
        self.init_ui()

    def init_ui(self):
        # --------------- Paramètres de la fenêtre --------------------
        self.resize(640, 325)
        self.center()
        self.invite_username.setText("Nom d'utilisateur à afficher durant la partie: ")
        self.invite_couleur.setText("Veuillez choisir une couleur:")
        self.invite_username.adjustSize()
        self.invite_couleur.adjustSize()
        self.invite_username.move(30, 75)
        self.invite_couleur.move(30, 120)
        self.boite_texte_username.setGeometry(300, 70, 300, 25)
        self.bouton_annuler.setText("Annuler")
        self.bouton_annuler.setGeometry(530, 275, 100, 40)
        self.bouton_annuler.clicked.connect(self.annuler)
        self.bouton_creer.setDefault(True)
        self.bouton_creer.setText("Confirmer")
        self.bouton_creer.setGeometry(430, 275, 100, 40)
        self.bouton_creer.setEnabled(False)
        self.bouton_creer.clicked.connect(self.confirmer)
        self.choix_de_couleur.setGeometry(307, 115, 300, 25)
        self.choix_de_couleur.addItems(["Couleur 1", "Couleur 2", "Couleur 3"])
        self.boite_texte_username.textChanged.connect(self.texte_change)

    def texte_change(self):
        # SOME CODE NOT NEEDED FOR THIS QUESTION

    def center(self):
        # SOME CODE NOT NEEDED FOR THIS QUESTION

    def annuler(self):
        self.close()

    def confirmer(self):
        test = pickle.dumps("test'")
        self.socket_de_connexion.send(test) # --> HERE IT FAILS TO SEND, GIVING ME THE ERROR MESSAGE MENTIONNED ABOVE
        self.accept()
        if not self.isVisible():
            self.une_partie = jeu.Jeu(2, self.boite_texte_username.text(),  self.choix_de_couleur.currentText())
            self.une_partie.show()


if __name__ == '__main__':
    app = QDialog()
    join_partie = JoinPartie()
    join_partie.show()
    app.exec_()

Here is the code on the server side:

import socket
import pickle
import random
from _thread import *
games = {}
couleurs = ["Bleu", "Rouge", "Jaune", "Vert"]
paquet_de_carte_initial = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
                           3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
                           5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
                           7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
                           9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
                           11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
                           12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
                           "SB", "SB", "SB", "SB", "SB", "SB", "SB", "SB", "SB", "SB", "SB", "SB"]
server = "192.168.100.195"
port = 5555
socket_de_connexion = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


def code_invitation():
    print("")
    digit1 = random.choice('0123456789')  # Chooses a random element
    digit2 = random.choice('0123456789')
    digit3 = random.choice('0123456789')
    digit4 = random.choice('0123456789')
    code = digit1 + digit2 + digit3 + digit4
    games[code] = {}
    print(games)
    return code


def split_cards(nbre):
    print("in")
    random.shuffle(paquet_de_carte_initial)
    random.shuffle(paquet_de_carte_initial)
    random.shuffle(paquet_de_carte_initial)
    print(paquet_de_carte_initial)
    if nbre == 2:
        print(2)
        player0 = []
        player1 = []
        main0 = []
        main1 = []
        for i in range(30):
            player0.append(paquet_de_carte_initial[0])
            paquet_de_carte_initial.pop(0)
            player1.append(paquet_de_carte_initial[0])
            paquet_de_carte_initial.pop(0)
        # print(player0)
        # print(player1)
        for j in range(5):
            main0.append(paquet_de_carte_initial[0])
            paquet_de_carte_initial.pop(0)
            main1.append(paquet_de_carte_initial[0])
            paquet_de_carte_initial.pop(0)
        # print(main0)
        # print(main1)
        # print(paquet_de_carte_initial)
        setup = (player0, main0, player1, main1, paquet_de_carte_initial)
        return setup


try:
    socket_de_connexion.bind((server, port))
except socket.error as e:
    str(e)

socket_de_connexion.listen(4)
print("Le server a démaré. \n En attente d'une connexion...")


def threaded_client(conn):
    msg = pickle.dumps("Connecté!")
    conn.send(msg)
    while True:
        try:
            data_recu = conn.recv(2048)
            data = pickle.loads(data_recu)
            print(data)
            if data == "create":
                print("création d'un code d'invitation...")
                code_cree = code_invitation()
                print(code_cree)
                code_pickled = pickle.dumps(code_cree)
                conn.send(code_pickled)
                joueurs = conn.recv(2048)
                info_joueurs = pickle.loads(joueurs)
                print(info_joueurs)
                (nbre_de_joueurs, joueur0) = info_joueurs
                if nbre_de_joueurs == 2:
                    cle = str(code_cree)
                    # games[cle] = {"0": joueur0}
                    # test = games[cle]["0"]
                    setup = split_cards(nbre_de_joueurs)
                    (deck_joueur0, main_joueur0, deck_joueur1, main_joueur1, talon) = setup
                    joueur0['deck_joueur'] = deck_joueur0
                    joueur0['main_joueur'] = main_joueur0
                    print(joueur0)
                    joueur1 = {'username': '', 'couleur': ''}
                    joueur1['deck_joueur'] = deck_joueur1
                    joueur1['main_joueur'] = main_joueur0
                    joueur0['defausse0'] = []
                    joueur0['defausse1'] = []
                    joueur0['defausse2'] = []
                    joueur0['defausse3'] = []
                    joueur1['defausse0'] = []
                    joueur1['defausse1'] = []
                    joueur1['defausse2'] = []
                    joueur1['defausse3'] = []
                    joueur0['count'] = 30
                    joueur1['count'] = 30
                    # games[cle] = {"0": joueur0}
                    # games[cle] = {"1": joueur1}
                    games[cle] = {"talon": talon}
                    games[cle]['0'] = joueur0
                    games[cle]['1'] = joueur1
                    print(games[cle])
                    couleurs_restantes = couleurs
                    couleurs_restantes.remove(joueur0['couleur'])
                    print(couleurs_restantes)
                    test = games[cle]['1']['main_joueur']
                    print(len(test))
                    to_send_to_j0 = (
                    games[cle]["talon"], games[cle]['0']['deck_joueur'], games[cle]['0']['main_joueur'],
                    games[cle]['0']['count'], games[cle]['1']['deck_joueur'][0], len(test), games[cle]['1']['count'])
                    print(to_send_to_j0)
                    depart = pickle.dumps(to_send_to_j0)
                    conn.send(depart)
            elif data == "join":
                print("in")
                ask = pickle.dumps("code?")
                conn.send(ask)
                code_recu_pickled = conn.recv(2048)
                code_recu = pickle.loads(code_recu_pickled)
                print(code_recu)
                verif = games.get(str(code_recu))
                if verif is not None:
                    print("yes")
                    reponse = pickle.dumps("Yes")
                    conn.send(reponse)
                    infos_joueur_pickled = conn.recv(2048)
                    infos_joueur = pickle.loads(infos_joueur_pickled)
                    print(infos_joueur)
                else:
                    print("No")
                    reponse = pickle.dumps("No")
                    conn.send(reponse)
                    conn.close()

        except:
            break
    print("Connexion perdu")
    # conn.close()


while True:
    conn, addr = socket_de_connexion.accept()
    print("Connecté à: ", addr)
    start_new_thread(threaded_client, (conn,))

Upvotes: 1

Views: 266

Answers (1)

eyllanesc
eyllanesc

Reputation: 244132

If you want it to be the same socket then you must share the same socket between the windows.

In my solution I have created a class that handles the socket (in this case I use a QTcpSocket), and if you want to handle a logic in which there is information exchange then you must implement a Command.

import pickle
import logging

from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork

logging.basicConfig(level=logging.DEBUG)

SERVER = "192.168.100.195"
PORT = 5555


class Command:
    def process_message(self, msg):
        pass


class Client(QtCore.QObject):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._socket = QtNetwork.QTcpSocket(self)
        self.socket.connected.connect(self._handle_connected)
        self.socket.readyRead.connect(self._handle_readyRead)
        self.socket.stateChanged.connect(self._handle_state_changed)

        self._command = None

    @property
    def command(self):
        return self._command

    @command.setter
    def command(self, command):
        self._command = command

    def connectTo(self, host, port):
        self.socket.connectToHost(host, port)

    def disconnect(self):
        self.socket.disconnectFromHost()

    @property
    def socket(self):
        return self._socket

    @QtCore.pyqtSlot()
    def _handle_connected(self):
        logging.debug("Connected")

    @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketState)
    def _handle_state_changed(self, state):
        logging.debug("state: %s" % state)

    @QtCore.pyqtSlot()
    def _handle_readyRead(self):
        msg = self.read_data()
        logging.debug("receive message: %s" % msg)
        if isinstance(self.command, Command):
            answer = self.command.process_message(msg)
            if isinstance(answer, str):
                self.send_data(answer)

    def encode_message(self, message):
        return pickle.dumps(message)

    def decode_message(self, message):
        return pickle.loads(message)

    def send_data(self, text):
        logging.debug("send message: %s" % text)
        data = self.encode_message(text)
        self.socket.write(data)

    def read_data(self):
        data = self.socket.readAll()
        msg = self.decode_message(data)
        return msg


class CodeCommand(QtCore.QObject, Command):
    received = QtCore.pyqtSignal(bool)

    def __init__(self, code="", parent=None):
        super().__init__(parent)

        self._code = code

    @property
    def code(self):
        return self._code

    def process_message(self, msg):
        if msg == "Connecté!":
            return "join"
        elif msg == "code?":
            return self.code
            self.send_data(self.code)
        elif msg == "No":
            self.received.emit(False)
        elif msg == "Yes":
            self.received.emit(True)


class VerificationDialog(QtWidgets.QDialog):
    def __init__(self, client, parent=None):
        super().__init__(parent)
        self._client = client

        self.code_le = QtWidgets.QLineEdit(placeholderText=self.tr("Code"))
        code_validator = QtGui.QRegExpValidator(QtCore.QRegExp(r"^[0-9]{4}"))
        self.code_le.setValidator(code_validator)

        confirm_btn = QtWidgets.QPushButton(self.tr("Confirm"))
        cancel_btn = QtWidgets.QPushButton(self.tr("Cancel"))

        gridlayout = QtWidgets.QGridLayout(self)
        gridlayout.addWidget(
            QtWidgets.QLabel(self.tr("Invitation Code:")),
            0,
            0,
            alignment=QtCore.Qt.AlignRight,
        )
        gridlayout.addWidget(self.code_le, 0, 1, alignment=QtCore.Qt.AlignLeft)
        gridlayout.addWidget(confirm_btn, 1, 0, alignment=QtCore.Qt.AlignRight)
        gridlayout.addWidget(cancel_btn, 1, 1, alignment=QtCore.Qt.AlignLeft)

        gridlayout.setColumnStretch(0, 1)
        gridlayout.setColumnStretch(1, 1)

        self.code_le.setFixedSize(100, 25)
        confirm_btn.setFixedSize(100, 40)
        cancel_btn.setFixedSize(100, 40)
        size = self.sizeHint()
        self.setFixedSize(size)

        confirm_btn.clicked.connect(self.confirm)
        cancel_btn.clicked.connect(self.cancel)

    @QtCore.pyqtSlot()
    def confirm(self):
        code = self.code_le.text()
        command = CodeCommand(code)
        command.received.connect(self.on_received)
        self.client.command = command
        self.client.connectTo(SERVER, PORT)

    @QtCore.pyqtSlot()
    def cancel(self):
        self.client.disconnect()
        self.reject()

    @QtCore.pyqtSlot(bool)
    def on_received(self, state):
        if state:
            self.accept()
        else:
            messagebox = QtWidgets.QMessageBox()
            messagebox.setIcon(QtWidgets.QMessageBox.Critical)
            messagebox.setText("Fault! The code is invalid!")
            messagebox.setInformativeText(
                "Please check the validity of the code. The program will now exit."
            )
            messagebox.setWindowTitle("Oops!")
            messagebox.setStandardButtons(QtWidgets.QMessageBox.Ok)
            messagebox.exec_()
            QtCore.QCoreApplication.quit()

    @property
    def client(self):
        return self._client


class TestDialog(QtWidgets.QDialog):
    def __init__(self, client, parent=None):
        super().__init__(parent)
        self._client = client

        self.username_le = QtWidgets.QLineEdit()
        self.color_cmb = QtWidgets.QComboBox()
        self.color_cmb.addItems(["Couleur 1", "Couleur 2", "Couleur 3"])

        confirm_btn = QtWidgets.QPushButton(self.tr("Confirm"))
        cancel_btn = QtWidgets.QPushButton(self.tr("Cancel"))

        gridlayout = QtWidgets.QGridLayout(self)
        gridlayout.addWidget(
            QtWidgets.QLabel(self.tr("Username to display during the game:")), 0, 0
        )
        gridlayout.addWidget(self.username_le, 0, 1)
        gridlayout.addWidget(QtWidgets.QLabel(self.tr("Please choose a color:")), 1, 0)
        gridlayout.addWidget(self.color_cmb, 1, 1)

        lay = QtWidgets.QHBoxLayout()
        lay.addStretch()
        lay.addWidget(confirm_btn)
        lay.addWidget(cancel_btn)

        gridlayout.addLayout(lay, 3, 0, 1, 2)
        gridlayout.setRowStretch(2, 1)

        self.resize(640, 325)

        cancel_btn.clicked.connect(self.reject)
        confirm_btn.clicked.connect(self.confirm)

    @QtCore.pyqtSlot()
    def confirm(self):
        self.client.send_data("test")
        self.accept()

    @property
    def client(self):
        return self._client


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)

        self._client = Client()

        commands_menu = self.menuBar().addMenu("Commands")

        verification_action = commands_menu.addAction("Verification")
        test_action = commands_menu.addAction("Test")

        verification_action.triggered.connect(self.verification)
        test_action.triggered.connect(self.test)

    @property
    def client(self):
        return self._client

    @QtCore.pyqtSlot()
    def verification(self):
        d = VerificationDialog(self.client)
        d.exec_()

    @QtCore.pyqtSlot()
    def test(self):
        self.client.command = None
        d = TestDialog(self.client)
        d.exec_()


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Upvotes: 2

Related Questions