Constantly update data from a server and print to a text box - python

So, I have a server completely written in Python 2.7:
from socket import *
from select import *
HOST = "127.0.0.1"
PORT = 1993
server = socket(AF_INET, SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(5)
clients = []
def getClients():
to_use = []
for client in clients:
to_use.append(client[0])
return to_use
while(True):
read, write, error = select([server],[],[],0)
if(len(read)):
client, address = server.accept()
clients.append([client, address, []])
to_use = getClients()
try:
read, write,error = select(to_use,[],[],0)
if(len(read)):
for client in read:
data = client.recv(1024)
print(bytes.decode(data))
if(data == 0):
for c in clients:
if c[0] == client:
clients.remove(c)
break
else:
for c in clients:
c[2].append(data)
except:
pass
try:
to_use = getClients()
read, write, error = select([], to_use, [], 0)
if(len(write)):
for client in write:
for c in clients:
if c[0] == client:
for data in c[2]:
sent = client.send(data)
if(sent == len(data)):
c[2].remove(data)
break
except:
pass
What I need to do is get constant updates for data (messages) from the
server and print them to a text box made in Tkinter.
The receiving code:
from socket import *
from select import *
HOST = "127.0.0.1"
PORT = 1993
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
while True:
data = bytes.decode(sock.recv(1024))
print data
It doesn't have to be Tkinter, but that's what I have been trying in; as long as it uses a GUI. Don't worry about sending messages I just need to be able to receive the data and print it to the text box/area.

The basic framework is to first create all of the widgets. Next, write a function that reads the data and updates the UI. Finally, arrange to have this function called every few milliseconds.
Roughly speaking, it looks something like this:
import Tkinter as tk
...
class Example(object):
def __init__(self):
self.root = tk.Tk()
self.text = tk.Text(root)
self.text.pack(fill="both", expand=True)
...
def start(self):
self.read_periodically()
self.root.mainloop()
def read_periodically(self):
# read the data
data = bytes.decode(sock.recv(1024))
# update the UI
self.text.insert("end", data)
# cause this function to be called again in 100ms
self.after(100, self.read_periodically)
example = Example()
example.start()
If the data is not a steady stream which causes sock.recv(1024) to block, your UI will freeze while it's waiting for data. If that's the case, you can move the reading of the socket to a thread, and have the thread communicate with the GUI via a thread-safe queue.
If the data is in a steady stream, or you set up a non-blocking socket, you don't have to do any of that.

I wanted to submit a comment first, but give this a try:
You can use something other than a start button to get things going I just put it there for ease of use
from Tkinter import *
import threading
from socket import *
from select import *
master = Tk() #create the GUI window
#put the test program in a seperate thread so it doesn't lock up the GUI
def test_program_thread():
thread = threading.Thread(None, test_program, None, (), {})
thread.start()
def test_program():
HOST = "127.0.0.1"
PORT = 1993
sock = socket(AF_INET, SOCK_STREAM)
sock.connect((HOST, PORT))
while True:
data = bytes.decode(sock.recv(1024))
terminal_listbox.insert(END, str(data))
master.update() #I don't think this line is necessary, but put it here just in case
# set the gui window dimensions and the title on the GUI
master.minsize(width=450, height=450)
master.wm_title("Stack Problem")
# Start button is set to y and starts the test program when hit
start_button = Button(master, text='START', command=test_program_thread)
start_button.place(x=5, y=5)
# scroll bar for the terminal outputs
scrollbar = Scrollbar(master)
scrollbar.place(x=420, y=150)
# Terminal output. Auto scrolls to the bottom but also has the scroll bar incase you want to go back up
terminal_listbox = Listbox(master, width=65, height=13)
terminal_listbox.place(x=5, y=100)
terminal_listbox.see(END)
scrollbar.config(command=terminal_listbox.yview)
#GUI loops here
master.mainloop()

Related

Customtkinter can't handle a .cget request. .cget("text") method returns _tkinter.TclError: unknown option "-text". Can't update widget [duplicate]

I have a customtkinter (CTk) button widget that, when pressed, sends an encoded message to a client depending on the button's "text" value; in this case, if the button's text is "Off", it sends the message "On" and vice versa to the client.
import tkinter as tk
import traceback
import customtkinter as cust
import socket
from threading import Thread
from tkinter import messagebox
class GUI2(cust.CTk): #second window; not the root
def __init__(self):
super().__init__()
self.PORT = 5000
self.SERVER = socket.gethostbyname(socket.gethostname())
self.ADDRESS = (self.SERVER, self.PORT)
self.FORMAT = "utf-8"
self.clients = [] #list to store all client connections
self.server = socket.socket(socket.AF_INET,
socket.SOCK_STREAM)
self.server.bind(self.ADDRESS)
self.master2 = cust.CTkToplevel()
self.ecdpower = cust.CTkButton(self.master2, text = "Off", fg_color = "Black", text_color = "White", hover_color = "Silver", command = lambda: Thread(target = self.startstream).start())
self.ecdpower.grid(row = 0, column = 0) #button to send message to client connections
self.thread = Thread(target = self.startChat)
self.thread.start() #starts function to accept client connections
def startChat(self): #function to accept client connections
self.server.listen(30)
try:
while True:
self.conn, self.addr = self.server.accept()
self.clients.append(self.conn) #stores all client connections
except:
pass
def startstream(self):
try:
if not self.clients: #checks if list is empty
messagebox.showerror("No Connections!", "No clients connected to host!")
else:
for x in self.clients:
if self.ecdpower["text"] == "Off": #ecdpower button widget acts like a "power button"
self.ecdpower.configure(text = "On", fg_color = "Green")
x.send(("On").encode(self.FORMAT))
else:
self.ecdpower.configure(text = "Off", fg_color = "Red")
x.send(("Off").encode(self.FORMAT))
except:
print (traceback.format_exc())
Error is as follows:
Traceback (most recent call last):
File "f:\project\mainmenu.py", line 390, in startstream
File "F:\Program Files (x86)\Python\lib\tkinter\__init__.py", line 1681, in cget
return self.tk.call(self._w, 'cget', '-' + key)
_tkinter.TclError: unknown option "-text"
I have also tried if self.ecdpower.cget("text") == "Off: and tried other values like fg_color; both result in the same error. When I removed the if condition, the message sending works correctly so the only problem is how to verify the button "text" value.
Any help to fix this or possible other alternatives is greatly appreciated.
Per #jasonharper's comment above, CTkButton is
not actually a Tkinter Button at all (it's made from a Frame containing a Canvas and a Label), so the normal Tkinter attribute-getting functions don't apply.
Instead of using self.ecdpower["text"] or self.ecdpower.cget("text"): I should use
self.ecdpower.text
to get the CTKbutton's text value.
The same can be applied with fg_color; use self.ecdpower.fg_color to get the value.

Cannot connect the python application with sockets properly to a public IP address

I developed a simple chat app that allows users to connect to a server and chat. This is the code for the server.
import os
from datetime import datetime
def log_file_name():
n = str(datetime.now())
m = n.replace(':','_').replace(' ','---')
l = m.split('.')[0]
l = f'{l}.txt'
return l
HOST = '127.0.0.1'
PORT = 10001
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#print(type(server_socket))
server.bind((HOST, PORT))
server.listen()
print('server_initiated...')
logfile = os.path.join('logs', log_file_name())
with open(logfile, 'w') as fh:
fh.write(f'{str(datetime.now())} log file initiated!\n\n')
clients = []
nicknames = []
#broadcast
def broadcast(message):
#print(clients)
for client in clients:
client.send(message)
def handle(client):
while True:
try:
message = client.recv(1024)
#print(f'{nicknames[clients.index(client)]} says {message}')
broadcast(message)
with open(logfile, 'a') as fh:
fh.write(f"{nicknames[clients.index(client)]} says {message.decode('utf-8')}\n")
except Exception:
index = clients.index(client)
clients.remove(client)
client.close()
left_nick = nicknames[index]
leaving_notes = f"{left_nick} just left the chat!! -- time {str(datetime.now()).split(' ')[1].split('.')[0]}"
broadcast(leaving_notes.encode('utf-8'))
with open(logfile,'a') as fh:
fh.write(f'{leaving_notes}\n')
nicknames.remove(left_nick)
break
#recieve message
def recieve():
while True:
client, address = server.accept()
#print(dir(client))
#print(client._io_refs)
#print(f'Connected with {str(address)}!!')
with open(logfile, 'a') as fh:
fh.write(f'Connected with {str(address)}!!\n')
client.send('Nickname'.encode('utf-8'))
nickname = client.recv(512).decode('utf-8')
nicknames.append(nickname)
clients.append(client)
#print(f'from 1st step clients are {clients}')
#print(f'Nickname of the client is {nickname}')
with open(logfile, 'a') as fh:
fh.write(f'Nickname of the client is {nickname}\n')
broadcast(f'Notice from server!! {nickname} just connected to the server!\n'.encode('utf-8'))
client.send(f'Connected to the server as {nickname}\n'.encode('utf-8'))
thread = threading.Thread(target=handle, args=(client,))
thread.start()
recieve()
Then I exposed that port to public using ngrok (ngrok http 10001), and it generated a public ip for me.
I coded a simple client gui using tkinter and I tried to connect the socket in to to above ngrok server I got. But I cannot broadcast messages. When I try to broadcast a message the GUI automatically closes. The reason is it meets a ConnectionAbortError.
Code for client-
import threading
import tkinter
import socket
import tkinter.simpledialog
import tkinter.scrolledtext
import os
#HOST = socket.gethostbyname(socket.gethostname())
#PORT = 10001
HOST = '3a7d-2402-4000-1245-cd8a-5d64-9c3f-25a3-79a4.in.ngrok.io'
PORT = 443 #or 80
class Client:
def __init__(self,host,port):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((host, port))
msg = tkinter.Tk()
msg.withdraw()
self.nickname = tkinter.simpledialog.askstring('Nickname', 'Please enter a nickname', parent=msg)
self.gui_done = False
self.running = True
gui_t=threading.Thread(target=self.gui_loop)
recieve_t=threading.Thread(target=self.recieve)
gui_t.start()
recieve_t.start()
def gui_loop(self):
self.win = tkinter.Tk()
self.win.configure(bg='lightgray')
self.chat_label = tkinter.Label(self.win, text='Chat:', bg='lightgray')
self.chat_label.config(font=('Arial',12))
self.chat_label.pack(padx=20, pady=5)
self.text_area = tkinter.scrolledtext.ScrolledText(self.win)
self.text_area.pack(padx=20,pady=5)
#self.text_area.config(state='disabled')
self.msg_label = tkinter.Label(self.win, text='Message:', bg='lightgray')
self.msg_label.config(font=('Arial', 12))
self.msg_label.pack(padx=20, pady=5)
self.input_area = tkinter.Text(self.win, height=3)
self.input_area.pack(padx=20, pady=5)
self.send_button = tkinter.Button(self.win, text='Send', command=self.write)
self.send_button.config(font=('Arial', 12))
self.send_button.pack(padx=20, pady=5)
self.gui_done = True
self.win.protocol('WM_DELETE_WINDOW', self.stop)
self.win.mainloop()
def write(self):
message = f"{self.nickname}: {self.input_area.get('1.0', 'end')}"
#print(message)
self.sock.send(message.encode('utf-8'))
self.input_area.delete('1.0','end')
def recieve(self):
while self.running:
try:
message = self.sock.recv(1024).decode('utf-8')
print(message)
if message == 'Nickname':
self.sock.send(self.nickname.encode('utf-8'))
else:
if self.gui_done:
#print('I hit here often')
self.text_area.config(state='normal')
self.text_area.insert('end', message)
self.text_area.yview('end')
#self.text_area.config(status='disabled') #never use this inside a loop, disabled blocks the code....
except ConnectionAbortedError: #when closed the window I come here..
print('Am i here?')
#break
os._exit(0)
#break
except:
break
print('Error')
self.sock.close()
break
def stop(self):
self.running = False
self.win.destroy()
self.sock.close()
exit(0)
client = Client(HOST, PORT)
If you run this please make sure to create a directory called logs to save the log of communication. Or else it will give an error(You can try it on localhost). I think I am doing something that doesn't work here. Can you point it out to me? or can you give me an alternative way of doing it?

Is it possible to return values from an infinite loop?

I'm writing a program that takes GPU temps and system ID from different systems on a local network and sends it to one main system. I'm having an issue with the receiving part of my program. In my function that accepts the data, I have an infinite loop to continue constantly receiving data from the client system. I also need to take this data out of this function and display it on a tkinter ui. I'm stuck on how to keep the data updating, while also returning it into my GUI
import tkinter as tk
import socket
def startServer():
HOST = socket.gethostname()
PORT = 9999
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen()
conn, addr = s.accept()
with conn:
print(f"NEW CONNECTION: {addr}")
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data)
data.decode('utf-8')
data = str(data)
data = data.split(':')
return data[0], data[1]
if __name__ == "__main__":
running = True
window = tk.Tk()
window.geometry('600x600')
button = tk.Button(window, text='Connect', command=startServer)
button.pack()
label = tk.Label(window,text='')
window.mainloop()
running = False

TimeoutError: [WinError 10060] python socket and gui tkinter server connects to the client

from tkinter import *
import _thread
from typing import TextIO
#页面代码
root = Tk()
root.title('TPC服务端')
root.geometry('640x480')
label_1 = Label(root,relief=GROOVE,text='从客户端接收的数据')
label_1.place(relx=0.1,rely=0.0,relwidth=0.4,relheight=0.1)
Operate = Text(root,relief=GROOVE)
Operate.place(relx=0.1,rely=0.1,relwidth=0.4,relheight=0.4)
Tips = Text(root,relief=GROOVE)
Tips.place(relx=0.1,rely=0.5,relwidth=0.4,relheight=0.4)
Tips.insert(END,'当前状态:\n')
Tips.configure(state=DISABLED)
root.mainloop()
class TCPServer():
def __init__(self):
self.HOST = '192.0.0.1'
self.PORT = 8080
self.BUFSIZ = 1024
self.ADDRESS = (self.HOST,self.PORT)
self.tcpServerSocket = socket(AF_INET, SOCK_STREAM)
self.tcpServerSocket.bind(self.ADDRESS) #IP地址和固定端口信息
self.tcpServerSocket.listen(5)
def try_connect(self):
global var
var='服务器正在运行,等待客户端连接...\n'
Tips.insert(END,var)
Tips.configure(state=DISABLED)
while True:
var='服务器正在运行,等待客户端连接...\n'
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
self.client_socket, self.client_address = self.tcpServerSocket.accept()
var='客户端{}已连接!\n'.format(self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
while True:
self.data = self.client_socket.recv(self.BUFSIZ)
if(self.data):
var='接收到消息 {}({} bytes) 来自 {}\n'.format(self.data.decode('utf-8'), len(self.data), self.client_address)
# 返回响应数据,接受的数据不做处理即返回
self.client_socket.send(self.data)
var='发送消息 {} 至 {}\n'.format(self.data.decode('utf-8'), self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
Operate.configure(state=NORMAL)
Operate.insert(END,self.data)
Operate.configure(state=DISABLED)
else:
var='客户端 {} 已断开!\n'.format(self.client_address)
Tips.configure(state=NORMAL)
Tips.insert(END,var)
Tips.configure(state=DISABLED)
break
# 关闭socket
self.client_socket.close()
# 取消监听socket,自此拒绝响应其它客户端
self.tcpServerSocket.close()
Server = TCPServer()
_thread.start_new_thread(Server.try_connect)
root.mainloop()
![text](enter image description here
I want to know why such a mistake happened.I am using python to make a simple network communication program.It contains both the client and server sides.This is my client program code.
I am a beginner.
The server connects to the client .i use vscode to write the code.I use python 3.10

Python : Sockets with Tkinter GUI

a new python coder here.
I am trying to make a game , where there is a 5x5 grid on numbers , and when i click on one of them , they get replaced with a cross 'X' and get greyed out. Here is the code :
import tkinter as tk
from tkinter import *
from tkinter import messagebox
import random
import socket
removed_numbers=[]
def numberClick(num,btn):
messagebox.showinfo('Message',str(num)+' is removed')
removed_numbers.append(num)
btn.configure(text='X')
btn.configure(bg='red',fg='white')
btn.configure(state="disabled")
print(removed_numbers)
root = Tk()
#root.geometry("200x200")
w = Label(root, text='Welcome to Bingo!')
linear_array = [i for i in range(1,26)]
random_array = []
for i in range(1,26):
temp = random.choice(linear_array)
linear_array.remove(temp)
random_array.append(temp)
rows=5
columns=5
btns = [[None for i in range(rows)] for j in range(columns)]
for i in range(rows):
for j in range(columns):
num = random.choice(random_array)
random_array.remove(num)
btns[i][j]=Button(root, text = num , fg ='red',height = 3, width = 5)
btns[i][j]['command']=lambda btn=btns[i][j],num=num: numberClick(num,btn)
btns[i][j].grid(row=i,column=j)
#text1=Text(root,width=47,height=1,bg='grey')
#text1.grid(row=5,column=2)
root.mainloop()
Output here :
Now i want to create a server , where two clients with the same code above connect , and when one button is pressed in a client , the action reflects in the other one as well. I tried to connect the client with server as shown :
Server.py :
import socket
c1 = None #Client socket1
addr1 = None #Client address1
c2 = None #Client socket2
addr2 = None #Client address2
server_socket1 = socket.socket() #by default it is SOCK_STREAM (TCP) and has porotocal AF_INET (IPv4)
server_socket1.bind(('127.0.0.1',9999)) #server machine's ip and port on which it will send and recieve connections from
server_socket1.listen(2) #We will only accept two connections as of now , one for each client
print("Server started successfully!!!")
print("Waiting for connections...\n\n")
flag_client1 = '0'
flag_client2 = '0'
while (((c1 is None)and(addr1 is None)) and ((c2 is None) and (addr2 is None))):
if((c1 is None) and (addr1 is None)):
c1,addr1 = server_socket1.accept()
print("User connected to client1 socket!!")
flag_client1='1'
if((c2 is None) and (addr2 is None)):
c2,addr2 = server_socket1.accept()
print("\n\nUser connected to client2 socket!!")
flag_client2='1'
if(flag_client1=='1' and flag_client2=='1'):
c1.send(bytes(flag_client1,"utf-8"))
c1.send(bytes(flag_client2,"utf-8"))
c2.send(bytes(flag_client1,"utf-8"))
c2.send(bytes(flag_client2,"utf-8"))
while True:
msg = c1.recv(4096)
if(msg!=None):
msg = msg.decode()
c2.send(bytes(msg,"utf-8"))
msg2 = c2.recv(4096)
if(msg2!=None):
msg2 = msg2.decode()
c1.send(bytes(msg2,"utf-8"))
Client.py :
#Connection Part
client_socket = socket.socket() #by default it is SOCK_STREAM (TCP) and has porotocal AF_INET (IPv4)
client_socket.connect(('127.0.0.1',9999)) #server machine's ip and port on which it will send and recieve connections from
But the connection is not getting reflected in server. I know i am wrong here.
Can someone please guide my how can i achieve the mentioned task? Can the same be done with Flask and socketIO? Will it be easy than tkinter? I really need to do it for my UNI project. TIA

Categories

Resources