Dàvid Nagy
Dàvid Nagy

Reputation: 159

Server-client connection in PyQt

I would like to write a simpe program that can handle a server-client connection (1:1 connection is enough) in PyQt. I was able to write the socket part but with the GUI I have no idea. I just want to have 2 dialogs one for the server and one for the client(very simple, only 1 button for sending and a textfield to type the message). I want to send a message from the server to the client and then reply to server with another message. Do you have any idea how to begin? Or maybe a sample code? Thank you

Code:

The GUI was created by the Desginer.

from PyQt5 import QtCore, QtWidgets
class Ui_Dialog(object):
    def setupUi(self, Dialog):
        Dialog.setObjectName("Dialog")
        Dialog.resize(353, 266)
        self.label = QtWidgets.QLabel(Dialog)
        self.label.setGeometry(QtCore.QRect(20, 30, 47, 13))
        self.label.setObjectName("label")
        self.pushButton = QtWidgets.QPushButton(Dialog)
        self.pushButton.setGeometry(QtCore.QRect(80, 110, 75, 23))
        self.pushButton.setObjectName("pushButton")
        self.textEdit = QtWidgets.QTextEdit(Dialog)
        self.textEdit.setGeometry(QtCore.QRect(80, 20, 141, 71))
        self.textEdit.setObjectName("textEdit")

        self.retranslateUi(Dialog)
        QtCore.QMetaObject.connectSlotsByName(Dialog)

    def retranslateUi(self, Dialog):
        _translate = QtCore.QCoreApplication.translate
        Dialog.setWindowTitle(_translate("Dialog", "Dialog"))
        self.label.setText(_translate("Dialog", "Message:"))
        self.pushButton.setText(_translate("Dialog", "Send"))

This the Dialog class:

class Dialog(QDialog, Ui_Dialog):

    def __init__(self,socket):
        super(Dialog, self).__init__()
        self.setupUi(self)
        self.socket = socket

    def send(self):
        message = self.textEdit.toPlainText()
        self.socket.send(message.encode())

Server class:

class Server():
    def __init__(self):
        host = socket.gethostname()
        port = 5000
        server_socket = socket.socket()
        server_socket.bind((host, port))
        server_socket.listen(2)
        conn, address = server_socket.accept()
        print("Connection from: " + str(address))

        gui = Dialog(server_socket)
        gui.show()
        while True:
            data = conn.recv(1024).decode()
            if not data:
                break
            print("message: " + str(data))
        conn.close()

if __name__ == '__main__':
    server = Server()

Client class:

class Client():
    def __init__(self):
        host = socket.gethostname()
        port = 5000
        client_socket = socket.socket()
        client_socket.connect((host, port))
        gui = Dialog(client_socket)
        gui.show()
        message = ""

        while message.lower().strip() != 'bye':

            data = client_socket.recv(1024).decode()
            print('message: ', data)

        client_socket.close()

if __name__ == '__main__':
    client = Client()

Upvotes: 2

Views: 9847

Answers (1)

ZF007
ZF007

Reputation: 3731

To keep a few things simple:

The resultant of your question is spending a lot of coding time (approx. 2-3 hours). Thus a tiny walk-through to get you on the right path again to do the next few steps yourself.

  1. Your main program file in this case is Dialog.py and not Client.py.
  2. If you build/compile your program Client.py is either client.pyd or another form of library. On windows Dialog will be an executable.
  3. rename Client.py into LOGIN_GUI or something that refers to its function of "local login" because that is what you try to accomplish. Use name client.py for remote logging (different GUI than server!). 3.In Dialog use pythons multiprocessing.

    3a. Process 1 runs main app;

    3b. Process 2 runs client-server;

    3c. Outsource other tasks taking more than 1-2sec (shows as "freezing" in main GUI).

  4. Dialog.py => MyMainAppNameisNOtDialog.py (avoid confusion/typo's inside your scripting code)

If you code your server ThreadingMixIn style like below you are able to log-in from two different computers. This is as far as I know not available as tool in pyqt5. See also inline comments inside the below code.

Server-side (modification of pythons Socket-server ThreadingMixIn):

import socket
import threading
import SocketServer

# MyAwesomelyNamedServerScript.py

class ThreadedTCPRequestHandler(SocketServer.BaseRequestHandler):

    def handle(self):

        def func1(scr1):
            #put code that does something here.
            print 'func1 : %s' % scr1
            return scr1

        def funct2(scr2):
            #put code that does something here.
            print 'func2 : %s' % scr2
            return scr2


        # self.request is the TCP socket connected to the client
        cur_thread = threading.current_thread()
        data = self.request.recv(1024)

        # In the data package the IP from client. 
        # Extract and add to an IP list (e.g. max 2 connection.)
        # if/else statement: if max connection reached than report back "cannot connect... waiting in queue or something".
        # limiting the request also prevent DDos attacks from the start.

        data_proc = func1(data)       # do something once
        data      = func2(data_proc)  # do something twice

        response = "{}: {}".format(cur_thread.name, data)
        self.request.sendall(response)

class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

def setupServer(ip = None, port = None, message = None):
    # Ip, port, message can be linked to below code if you want exernal input via GUI
    # Port 0 means to select an arbitrary unused port
    HOST, PORT = "localhost", 0

    server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
    ip, port = server.server_address

    # Start a thread with the server -- that thread will then start one
    # more thread for each request
    server_thread = threading.Thread(target=server.serve_forever)
    # Exit the server thread when the main thread terminates
    server_thread.daemon = True
    server_thread.start()
    print "Server loop running in thread:", server_thread.name
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((ip, port))
    try:
        sock.sendall(message)
        response = sock.recv(1024)
        print "Received: {}".format(response)
    finally:
        sock.close()

def test_main():

        #client(ip, port, "Hello World 1")
        #client(ip, port, "Hello World 2")
        #client(ip, port, "Hello World 3")

        client(message = "Hello World 1")
        client(message = "Hello World 2")
        client(message = "Hello World 3")

        server.shutdown()
        server.server_close()

if __name__ == "__main__":

    test_main()  #runs the main test

The MainApp.py script in short:

import myloginscript
import myAwesomeMainGUI

...snippet ... <incl. Qt imports, etc.> ...

#MainApp.py

class MyMainUI(QtWidgets.QMainWindow, myAwesomeMainGUI.Ui_MainWindow):

    ...snippet <your code>...

    def setUpass(self, username):
        # do whatever you want with the username
        # if Pword brought here then login.username.text() needs to be altered to get pw instead.
        self.username = username  # this variable shoud be made inside your main code.
#        self.label.setText("Username: %s" % self.username)
        print 'The username is : %s' % self.username

if _name__ = '__main__':

    app.QApplication(sys.argv)

    login= myloginscript()  # this runs your login script (GUI starts if attached therein.)

    if login.exec():
        window = MymainwindowDialog()           # main appwindow...not login GUI.
        window.setUpass(login.username.text())
        window.show()
        sys.stdout.flush()
        try:
            sys.exit(app.exec())
        except:
            print('Exit now.. wrong Uname or Pword')
    else:
        sys.exit(-1)  # user pressed 'Cancel', lets quit the program here.

Upvotes: 4

Related Questions