Python Forum
Issues in Chat App for Local Network
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Issues in Chat App for Local Network
#1
Dear All

I hope everyone is doing well. Below is the code for a rudimentary chat app. The server side seems to be working fine but the client side is unable to send messages to the server size and instead just double prints the message. It's not complete and 'Will' contain a lot of bad coding but I'm learning a lot of new stuff day by day this way.

from PyQt5 import QtWidgets
import sys
import socket
import threading
import ipaddress


class MainWindow(QtWidgets.QWidget):

    def __init__(self):
        super(MainWindow, self).__init__()

        self.service = ""
        self.server = socket.socket()
        self.client = socket.socket()
        self.machineIP = socket.gethostbyname(socket.gethostname())
        self.isconnected = ""
        self.connectedusers = []

        self.chatwindow = QtWidgets.QListWidget()
        self.userwindow = QtWidgets.QListWidget()
        self.typingfield = QtWidgets.QLineEdit()
        self.sendbutton = QtWidgets.QPushButton("Send")
        self.sendbutton.clicked.connect(self.sendpressed)

        self.setupUI()
        self.show()

    def setupUI(self):
        mainlayout = QtWidgets.QVBoxLayout()
        displaybox = QtWidgets.QHBoxLayout()
        userlist = QtWidgets.QVBoxLayout()
        userlist.addWidget(QtWidgets.QLabel("Users Connected:"))
        userlist.addWidget(self.userwindow)
        displaybox.addWidget(self.chatwindow, 3)
        displaybox.addLayout(userlist, 1)
        inputbox = QtWidgets.QHBoxLayout()
        inputbox.addWidget(self.typingfield)
        inputbox.addWidget(self.sendbutton)
        mainlayout.addLayout(displaybox)
        mainlayout.addLayout(inputbox)
        self.setLayout(mainlayout)
        self.chatwindow.addItem("/host to setup server on local host (Port 7000)")
        self.chatwindow.addItem("/[email protected] to connect to server on local host (Port 7000)")

        self.typingfield.setFocus()
        self.setWindowTitle("TAG CHAT")

    def sendpressed(self):
        typedtext = self.typingfield.text()
        if typedtext:
            if typedtext[0] == "/":
                self.metaprocessor(typedtext)
            else:
                if self.service:
                    message = self.machineIP + " : " + str(typedtext)
                    self.chatwindow.addItem(message)
                    if self.service == "server":
                        self.broadcast(typedtext)
                    elif self.service == "client":
                        self.server.send(message.encode())
                else:
                    self.chatwindow.addItem("Not connected to any group.")
        self.typingfield.clear()
        self.typingfield.setFocus()

    def metaprocessor(self, typedtext):
        if typedtext == "/host":
            serverstart = threading.Thread(target=self.makeserver, args=())
            serverstart.daemon = True
            serverstart.start()
        elif typedtext[0:9] == "/connect@" and len(typedtext) > 9:
            userip = typedtext[9:]
            try:
                ipaddress.ip_address(userip)
                clientstart = threading.Thread(target=self.startclient, args=(userip, 7000))
                clientstart.daemon = True
                clientstart.start()
            except ValueError:
                self.chatwindow.addItem("Invalid IP address provided '" + userip + "'")

        else:
            self.chatwindow.addItem("Invalid syntax")

    def startclient(self, serverip, port):
        self.server.connect((str(serverip), port))
        self.service = "client"
        while True:
            message = self.server.recv(1024)
            self.chatwindow.addItem(message.decode())

    def makeserver(self):
        self.chatwindow.addItem("Creating server @ " + self.machineIP + ":7000")
        self.server.bind((self.machineIP, 7000))
        self.chatwindow.addItem("Server started successfully...\nListening to all connections")
        self.service = "server"
        self.server.listen(100)
        while True:
            conn, addr = self.server.accept()
            message = "Connected to " + self.machineIP + ":7000"
            conn.send(message.encode())
            self.connectedusers.append((conn, addr))
            newconnection = threading.Thread(target=self.startconnection, args=(conn, addr))
            newconnection.daemon = True
            newconnection.start()

    def startconnection(self, conn, addr):
        self.chatwindow.addItem("User " + str(addr) + " has joined the group.")
        while True:
            data = conn.recv(1024)
            message = addr[0] + " : " + data.decode()
            self.broadcast(message)

    def broadcast(self, typedtext):
        for each in self.connectedusers:
            message = each[1][0] + " : " + str(typedtext)
            each[0].send(message.encode())


if __name__ == "__main__":

    app = QtWidgets.QApplication(sys.argv)
    mainApplication = MainWindow()
    sys.exit(app.exec_())
You'll need to run two instances of the script i guess. To hoset use '/host' to connect to a host use '/connect@[ip address of host]'. Port is set to default 7000.

Any help will be highly appreciated.

Regards
iMu
Reply
#2
I'd be interested in helping if you can reproduce the problem with fewer lines of code (I'm thinking 10-20 tops) and only necessary imports.
Reply
#3
(Jul-19-2019, 04:11 AM)micseydel Wrote: I'd be interested in helping if you can reproduce the problem with fewer lines of code (I'm thinking 10-20 tops) and only necessary imports.

Ahh, I'm really sorry, i forgot to check back after no one replied initially. Anyways I found the problem myself, i.e. human errors, while reviewing. I took the project a little further with some more features then abandoned it after i got bored and started new projects @.@

The final (at the time) code is as below for anyone interested.

from PyQt5 import QtWidgets
import sys
import socket
import threading
import ipaddress
import time
import pickle


class MainWindow(QtWidgets.QWidget):

    def __init__(self):
        super(MainWindow, self).__init__()

        self.service = ""
        self.server = socket.socket()
        self.client = socket.socket()
        self.machineIP = socket.gethostbyname(socket.gethostname())
        self.isconnected = ""
        self.connectedusers = []
        self.unreadcount = 0
        self.oldtime = time.time()

        self.chatwindow = QtWidgets.QListWidget()
        self.userwindow = QtWidgets.QListWidget()
        self.typingfield = QtWidgets.QLineEdit()
        self.typingfield.returnPressed.connect(self.sendpressed)
        self.sendbutton = QtWidgets.QPushButton("Send")
        self.sendbutton.clicked.connect(self.sendpressed)

        self.tray_icon = QtWidgets.QSystemTrayIcon(self)
        self.tray_icon.activated.connect(self.onTrayIconActivated)
        self.tray_icon.setIcon(self.style().standardIcon(QtWidgets.QStyle.SP_ComputerIcon))

        show_action = QtWidgets.QAction("Show", self)
        quit_action = QtWidgets.QAction("Exit", self)
        show_action.triggered.connect(self.show)
        quit_action.triggered.connect(self.shutdownapp)
        tray_menu = QtWidgets.QMenu()
        tray_menu.addAction(show_action)
        tray_menu.addAction(quit_action)
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()

        self.setupUI()
        self.show()

    def closeEvent(self, event):
        if self.service == "server":
            event.ignore()
            self.hide()
            self.tray_icon.showMessage(
                "TAG IM",
                "App Minimized (Running as Host)",
                QtWidgets.QSystemTrayIcon.Information,
                1000)
        elif self.service == "client":
            event.ignore()
            self.hide()
            self.tray_icon.showMessage(
                "TAG IM",
                "App Minimized (Running as Client)",
                QtWidgets.QSystemTrayIcon.Information,
                1000)

    def shutdownapp(self):
        if self.service == "server":
            self.service == ""
            self.broadcast("Host has shutdown")
            self.server.close()
            app.quit()
        elif self.service == "client":
            self.service == ""
            message = "Left the chat"
            self.client.send(message.encode())
            self.client.close()
            print("Client closed manually")
            app.quit()
        elif self.service == "":
            app.quit()

    def onTrayIconActivated(self, reason):
        if reason == QtWidgets.QSystemTrayIcon.Trigger:
            self.show()
            self.focusWidget()

    def setupUI(self):
        mainlayout = QtWidgets.QVBoxLayout()
        displaybox = QtWidgets.QHBoxLayout()
        userlist = QtWidgets.QVBoxLayout()
        userlist.addWidget(QtWidgets.QLabel("Users Connected:"))
        userlist.addWidget(self.userwindow)
        displaybox.addWidget(self.chatwindow, 3)
        displaybox.addLayout(userlist, 1)
        inputbox = QtWidgets.QHBoxLayout()
        inputbox.addWidget(self.typingfield)
        inputbox.addWidget(self.sendbutton)
        mainlayout.addLayout(displaybox)
        mainlayout.addLayout(inputbox)
        self.setLayout(mainlayout)
        self.chatwindow.addItem("/host to setup server on local host (Port 7000)")
        self.chatwindow.addItem("/[email protected] to connect to server on local host (Port 7000)")
        self.chatwindow.scrollToBottom()

        self.typingfield.setFocus()
        self.setWindowTitle("TAG CHAT")

    def sendpressed(self):
        typedtext = self.typingfield.text()
        if typedtext:
            if typedtext[0] == "/":
                self.metaprocessor(typedtext)
            else:
                if self.service:
                    if self.service == "server":
                        self.broadcast(typedtext)
                    elif self.service == "client":
                        self.client.send(typedtext.encode())
                else:
                    self.chatwindow.addItem("Not connected to any group.")
                    self.chatwindow.scrollToBottom()
        self.typingfield.clear()
        self.typingfield.setFocus()

    def metaprocessor(self, typedtext):
        if typedtext == "/host":
            serverstart = threading.Thread(target=self.makeserver, args=())
            serverstart.daemon = True
            serverstart.start()
        elif typedtext[0:9] == "/connect@" and len(typedtext) > 9:
            userip = typedtext[9:]
            try:
                ipaddress.ip_address(userip)
                clientstart = threading.Thread(target=self.startclient, args=(userip, 7000))
                clientstart.daemon = True
                clientstart.start()
            except ValueError:
                self.chatwindow.addItem("Invalid IP address provided '" + userip + "'")
                self.chatwindow.scrollToBottom()
        elif typedtext == "/quit":
            self.shutdownapp()

        else:
            self.chatwindow.addItem("Invalid syntax")
            self.chatwindow.scrollToBottom()

    def startclient(self, serverip, port):
        self.client.connect((str(serverip), port))
        self.service = "client"
        while True:
            try:
                message = self.client.recv(4096)
                if message[0:10] == "~connected".encode():
                    connectedusers = pickle.loads(message[10:])
                    self.updateclientlistclient(connectedusers)
                else:
                    self.unreadnotification()
                    self.chatwindow.addItem(message.decode())
                    self.chatwindow.scrollToBottom()
            except:
                self.chatwindow.addItem("Host has closed the connection".decode())
                self.chatwindow.scrollToBottom()
                self.userwindow.clear()
                self.service = ""
                break

    def makeserver(self):
        self.chatwindow.addItem("Creating server @ " + self.machineIP + ":7000")
        self.server.bind((self.machineIP, 7000))
        self.chatwindow.addItem("Server started successfully...\nListening to all connections")
        self.chatwindow.scrollToBottom()
        self.service = "server"
        self.server.listen(100)
        while True:
            conn, addr = self.server.accept()
            message = "Connected to " + self.machineIP + ":7000"
            conn.send(message.encode())
            self.connectedusers.append((conn, addr))
            self.updateclientlistserver(self.connectedusers)
            newconnection = threading.Thread(target=self.startconnection, args=(conn, addr))
            newconnection.daemon = True
            newconnection.start()

    def startconnection(self, conn, addr):
        self.chatwindow.addItem("User " + str(addr) + " has joined the group.")
        self.chatwindow.scrollToBottom()
        while True:
            try:
                data = conn.recv(4096)
                self.unreadnotification()
                self.broadcast(data.decode())
            except:
                print("Client closing caught by server")
                conn.close()
                self.connectedusers.remove((conn, addr))
                self.updateclientlistserver(self.connectedusers)
                break

    def updateclientlistserver(self, connectedusers):
        self.userwindow.clear()
        for each in connectedusers:
            self.userwindow.addItem(each[1][0])

    def updateclientlistclient(self, connectedusers):
        self.userwindow.clear()
        for each in connectedusers:
            self.userwindow.addItem(each)

    def broadcast(self, typedtext):
        servermessage = self.machineIP + " : " + str(typedtext)
        self.chatwindow.addItem(servermessage)
        self.chatwindow.scrollToBottom()
        for each in self.connectedusers:
            message = each[1][0] + " : " + str(typedtext)
            each[0].send(message.encode())
            iplist = []
            for ips in self.connectedusers:
                iplist.append(ips[1][0])
            data = pickle.dumps(iplist)
            each[0].send("~connected".encode() + data)

    def unreadnotification(self):
        if not self.isVisible():
            self.unreadcount += 1
            if time.time() - self.oldtime > 60:
                self.oldtime = time.time()
                self.tray_icon.showMessage(
                    "TAG IM",
                    str(self.unreadcount) + " messages unread",
                    QtWidgets.QSystemTrayIcon.Information,
                    1000)
        else:
            self.unreadcount = 0


if __name__ == "__main__":

    # Back up the reference to the exceptionhook
    sys._excepthook = sys.excepthook


    def my_exception_hook(exctype, value, traceback):
        # Print the error and traceback
        print(exctype, value, traceback)
        # Call the normal Exception hook after
        sys._excepthook(exctype, value, traceback)
        sys.exit(1)


    # Set the exception hook to our wrapping function
    sys.excepthook = my_exception_hook

    app = QtWidgets.QApplication(sys.argv)
    mainApplication = MainWindow()

    try:
        sys.exit(app.exec_())
    except:
        print("Exiting")
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Chat box locking up with multiple clients running Clunk_Head 1 1,955 Nov-27-2019, 04:13 PM
Last Post: Clunk_Head
  How to restart another PC on the local network? Kundan 1 2,107 Sep-21-2019, 11:31 AM
Last Post: ThomasL
  Chat (Client-Server) andresdrr 3 3,696 Aug-26-2019, 02:01 PM
Last Post: ThomasL

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020