Preview:
import socket
from threading import Thread
from tkinter import *
from tkinter import messagebox
from tkinter import scrolledtext
import json
import time
from sys import exit
enter_connection_number = Tk()
enter_connection_number.geometry('100x100')
#create a entry to enter port number
connection_number_entry = Entry(enter_connection_number, width=10)
connection_number_entry.insert(0, '')
connection_number_entry.pack()
connection_number_label = Label(enter_connection_number, text='Enter connection IP:')
connection_number_label.pack()
#create a 'set' button for the entry
def set_connection_number():
    global connection_number, connection_number_entry, connection_number_label, connection_number_button
    #set connection_number
    answer = connection_number_entry.get()
    if answer.count('.') == 3 and not ' ' in answer:
        connection_number = connection_number_entry.get()

        #destroy connection_number_entry and connection_number_button
        connection_number_entry.destroy()
        connection_number_button.destroy()
        connection_number_label.destroy()
        #close window
        enter_connection_number.destroy()

    else:
        messagebox.showerror('Invalid connection IP', 'Please enter a connection IP in the format: xxx.xxx.xxx.xxx (no spaces)')
        connection_number_entry.delete(0, END)
connection_number_button = Button(text='Set connection number', command=set_connection_number)
connection_number_button.pack()
enter_connection_number.mainloop()


def send_message_and_get_message(message, start_timer = False):
    '''Sends a message to the server until response is valid and returns the response'''
    if start_timer:
        global timer, my_socket
        timer = time.perf_counter()
    my_socket.send(message.encode())
    response = my_socket.recv(1024).decode()
    while response.startswith('0'):
        if start_timer:
            timer = time.perf_counter()
        my_socket.send(message.encode())
        response = my_socket.recv(1024).decode()
    return response

def send_message(message, mode = 1, func=None):
    '''sends message/command to server and running the command if needed'''
    global closed, username, timer
    try:
        message = str(mode) + message
        data = send_message_and_get_message(message, func == 'timer')
    except Exception as e:
        print(f'ERROR1: {e}\nclosing connection')
        exit()
    else:
        try:
            if func == 'timer':
                timer = time.perf_counter() - timer
                messagebox.showinfo('Ping message', f'Ping Message sent in {int(timer * 10000)/10000} seconds')
            elif func == 'server data':
                messagebox.showinfo('Server data', data)
            elif func == 'connected clients':
                messagebox.showinfo('Connected clients', f' there are {len(data.split(" "))} user(s) online:   {data}')
            elif func == 'set username':
                if data.endswith('1'):
                    messagebox.showinfo('Username changed', data[:-1])
                    username.destroy()
                    username = Label(root, text=f'username: {message.split(" ")[2]}')
                    username.pack()
                else:
                    messagebox.showerror('Username change failed', data[:-1])
                    name_entry.config(state=NORMAL)
                    name_button.config(text='Change name', command=change_name)
            elif data == 'quit':
                print('quiting')
                closed = True
                exit()
            
            else:
                print('\n' + data)
        except Exception as e:
            print(f'ERROR2: {e}')
            closed = True
        
            
closed = False
root = Tk()
#create a tkinter window with label frame
root.title('Chat')
root.geometry('400x400')
frame = Frame(root)
frame.pack()
#create a label and text entry widget
label = Label(frame, text='Enter your message:')
label.pack()
entry = Entry(frame)
entry.pack()
#create a button to send message
button = Button(frame, text='Send', command=lambda: send_message(entry.get()))
button.pack()
#create scrolled text widget to display messages
messages_box = scrolledtext.ScrolledText(frame, height=10, width=100)
#lock messages box
messages_box.config(state=DISABLED)
messages_box.pack()

#create a frame for commands buttons
commands_frame = Frame(root)
commands_frame.pack()
#create a button to quit
quit_button = Button(commands_frame, text='Quit', command=lambda: send_message('QUIT', 2))
quit_button.pack(side=RIGHT)
#create a button to clear messages
def clear_messages():
    messages_box.config(state=NORMAL)
    messages_box.delete(1.0, END)
    messages_box.config(state=DISABLED)
clear_button = Button(commands_frame, text='Clear', command=clear_messages)
clear_button.pack(side=LEFT)
#create a button to send PING commands to server
ping_button = Button(commands_frame, text='Ping', command=lambda: send_message('PING', 2, 'timer'))
ping_button.pack(side=LEFT)
#create a DATA command button
data_button = Button(commands_frame, text='server data', command=lambda: send_message('SERVER', 2, 'server data'))
data_button.pack(side=LEFT)
#create connected clients command button
clients_button = Button(commands_frame, text='Connected clients', command=lambda: send_message('CONNECTED CLIENTS', 2, 'connected clients'))
clients_button.pack(side=LEFT)

#create an entry to change name
name_frame = Frame(root)
name_frame.pack()
name_label = Label(name_frame, text='Enter new username:')
name_label.pack()
name_entry = Entry(name_frame)
name_entry.pack()
#create a button to change name
def change_name():
    global name
    name = name_entry.get()
    send_message(f'SET USERNAME {name}', 2, 'set username')
    print(f'SET USERNAME {name}', 2, 'set username')
    name_entry.delete(0, END)
    name_entry.insert(0, name)
    name_button.config(text='Change name', command=change_name)
name_button = Button(name_frame, text=f'Change name', command=change_name)
name_button.pack()



#create a socket
my_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
my_socket.settimeout(5)

#label that shows the socket address


try:
    my_socket.connect((connection_number, 8820))
    username = Label(root, text=f'username: {my_socket.getsockname()[1]}')
    username.pack()
except:
    #show messagebox: server is down
    root.withdraw()
    messagebox.showerror(f'Server {connection_number} is down', 'Server is down, please try again later')
    root.destroy()
    quit()
def check_new_messages():
    global last_message, closed
    try:
        while True:
            root.update()
            time.sleep(1)
            try:
                my_socket.send(f'0{last_message}'.encode())
            except:
                if not closed:
                    messagebox.showerror('Server is down', 'Server is down, please try again later')
                    root.destroy()
                    quit()

            data = str(f'[{my_socket.recv(1024).decode()}]')
            #check if not first letter of data is upper case
            if data[1] == '0':
                data = data[2:-1]
                print(data)
                try:
                    data = json.loads(data)
                except Exception as e:
                    print(f'ERROR3: {e}\n{data}')
                else:
                    if data[len(data)-1][0] > last_message:
                        for i in data:
                            if i[0] > last_message:
                                messages_box.config(state=NORMAL)
                                messages_box.insert(END, f'{i[1]} wrote: {i[2]}\n')
                                messages_box.config(state=DISABLED)
                                last_message = i[0]
                    else:
                        continue
            else:
                print(f'ignored message {data}')
 
    except Exception as e:
        pass

last_message = -1
Thread(target=check_new_messages).start()
root.mainloop()
send_message('QUIT', 2)
closed = True
my_socket.close()
downloadDownload PNG downloadDownload JPEG downloadDownload SVG

Tip: You can change the style, width & colours of the snippet with the inspect tool before clicking Download!

Click to optimize width for Twitter