Wednesday, January 18, 2023

My Simple Python Chat Script just got an Upgrade!

Python is a versatile programming language that is widely used for a variety of tasks, including creating chat applications. One way I created a chat application in Python is by using the built-in socket library to handle the communication between clients and the server. While this approach is functional, the user interface for such an application can be quite basic, making it difficult for me to navigate and interact with the chat. You can still check the original post here(Python A Simple Chat Script).

To improve the user experience, I can upgrade my chat application to utilize a PyQt6 user interface. PyQt6 is a library that provides a set of Python bindings for the Qt libraries, which are widely used for creating graphical user interfaces (GUIs). By using PyQt6, I can create a more user-friendly and visually appealing interface for my chat application. But like many of my Python scripts, recieving of messages is unpredictable that is why it is constantly monitoring for new incoming messages. This is why I need to include the multithreading library so that the whole application will not freeze while waiting for the arrival of new messages.

Another useful feature that I can add to my chat application is a notification message that appears in the notification tray. This can help to alert me when a new message is received, even when the application is not in focus. To achieve this, I can use the QStyledItemDelegate class from the PyQt6.QtWidgets module. This class allows me to create a toast notification that appears in the notification tray telling me that a new message has arrived.

In summary, upgrading my Python socket chat application to utilize a PyQt6 user interface with multithreading and a notification message in the notification tray using QStyledItemDelegate can greatly improve the user experience. By using PyQt6, I can create a visually appealing and user-friendly interface, and by using multithreading, I can ensure that the application remains responsive, even when handling a large number of messages or connections. Additionally, the notification message in the notification tray using QStyledItemDelegate can alert me to new messages, even when the application is not in focus.

Here is the sample screenshot:


Please do note that this simple chat only allows communications within the same pc unlike the old version which can allow communication between 2 pc's on the same LAN but can be easily modified to make it communicate with any pc connected to the internet. I also included a combo box to allow a user to connect as a server or as a client.

Also in the works as a spoiler, I am planning to create a more feature rich chat application. I already created a new gui for this but its has a very long way to go. Here is the gui on the drawing board:


The user interface can allow users to add friends, rooms very much like yahoo messenger, facebook messaging, microsoft teams, among others. And as time goes on, I may add more features like adding user time line, synchronize their timelines to twitter, facebook and linkedin, and perhaps a marketplace, the possibilities are endless.


Here is the code:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import sys
from PyQt6.QtCore import *
from PyQt6.QtGui import *
from PyQt6.QtWidgets import *
import mysql.connector as mysql
import warnings
warnings.filterwarnings('ignore')

import os
import socket
import multiprocessing as mp
import time

import winrt.windows.ui.notifications as notifications
import winrt.windows.data.xml.dom as dom

s = socket.socket()
breaker = 0
combo = ''
files = ''
conn = ''

class Delegate(QStyledItemDelegate):
    def createEditor(self, parent, option, index):
        if index.data() == "100":
            return super(Delegate, self).createEditor(parent, option, index)

class Window(QMainWindow):

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

        self.initUI()

    def initUI(self):

        widget = QWidget()

        self.setCentralWidget(widget)
        self.combo_box = QComboBox(self)

        self.combo_box.addItem("Server")
        self.combo_box.addItem("Client")
        self.conn_button = QPushButton("Connect")
        #print(dir(self.conn_button))
        self.conn_button.setFixedWidth(100)
        self.conn_button.clicked.connect(self.connect_exec)

        self.text_edit = QTextEdit()
        self.text_edit.setReadOnly(True)

        self.line_edit = QLineEdit()
        self.line_edit.setPlaceholderText("Enter message here")
        self.send_button = QPushButton("Send")
        self.send_button.clicked.connect(self.send_message)
        h_layout_conn = QHBoxLayout()
        h_layout_conn.addWidget(self.combo_box)
        h_layout_conn.addWidget(self.conn_button)
        # Layout
        layout = QVBoxLayout()
        layout.addLayout(h_layout_conn)
        layout.addWidget(self.text_edit)
        h_layout = QHBoxLayout()
        h_layout.addWidget(self.line_edit)
        h_layout.addWidget(self.send_button)
        layout.addLayout(h_layout)
        widget = QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)
        self.setGeometry(25, 45, 350, 400)
        self.setWindowTitle('Pop Chat')
        timer = QTimer(self)
        timer.timeout.connect(self.showtext)
        timer.start(1000)
        self.thread()
        self.show()
    def showtext(self):
        global files
        if files != '':
           self.text_edit.append(files)
           self.notif()
           files = ''

    def send_message(self):
        global s, conn, combo
        message = self.line_edit.text()
        if message:
            self.text_edit.append(message)
            self.line_edit.clear()
            if combo == 'Server':
              conn.send(message.encode())
            else:
                s.send(message.encode())
    def connect_exec(self):
        global s, combo, conn
        message = "connected as " + self.combo_box.currentText() + " ..."
        combo = self.combo_box.currentText()
        if self.combo_box.currentText() == 'Server':
            host=socket.gethostname()
            port=9090
            s.bind(('0.0.0.0',port))

        if message:
            self.text_edit.append(message)
            self.worker = WorkerThread()
            self.worker.start()
    def notif(self):
        app = '{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe'
        nManager = notifications.ToastNotificationManager
        notifier = nManager.create_toast_notifier(app)

        zmail = "You've Got New Message!"

        tString = """
          <toast>
            <visual>
              <binding template='ToastGeneric'>
                <text>""" + zmail + """</text>
                
              </binding>
            </visual>
            <actions>
              <action
                content="Delete"
                arguments="action=delete"/>
              <action
                content="Dismiss"
                arguments="action=dismiss"/>
            </actions>
          </toast>
        """
        #print(tString)
        xDoc = dom.XmlDocument()
        xDoc.load_xml(tString)
        notifier.show(notifications.ToastNotification(xDoc))

class WorkerThread(QThread):
   def run(self):
      global s, combo, files, conn
      if combo == 'Server':
        s.listen(1)
        conn,addr = s.accept()
      else:
        host=socket.gethostname()
        s.connect((host,9090))
      while 1:
         if combo == 'Server':
            file = conn.recv(5000)
         else:
            file = s.recv(5000)
         file = file.decode()
         files = file

def main():
   app = QApplication(sys.argv)
   ex = Window()
   ex.show()
   sys.exit(app.exec())

if __name__ == '__main__':
   main()

No comments:

Post a Comment