Tkinter multiprocessing can't pickle - python

I'm trying to write a simple messenger app with Python and Tkinter. I'm listening to to a UDPSock to receive input. However as Tkinter's mainloop() blocks the thread, I cannot receive input with only one thread. I've tried to send a reference to the Tkinter GUI to the Process that loops and listens to input but I get a Can't pickle 'tkapp' object error. As the input is continuous and the Tkinter thread can't do anything else except run the mainloop to handle the GUI I cannot put the received input onto the GUI. Even if I use a Pipe the Tkinter thread is running the mainloop and so there is nowhere I can put a
while True:
data = pipe_conn.recv()
# do something with data
or anything.
Any help would be much appreciated!
My code:
-- imports --
class MessageApp():
def __init__(self, root, host, conn, port=13000):
# Setup GUI
self.send_sock = socket(AF_INET, SOCK_DGRAM)
p = Process(target=self.recv, args=(conn,))
def recv(self, conn):
buff = 1024
print('recvin')
while True:
(data, addr) = conn.recvfrom(buff)
self.add_msg(addr + ": " + data)
def start(self):
self.root.mainloop()
def add_msg(self, text):
self.msgs.config(state=NORMAL)
self.msgs.insert(INSERT, text + "\n")
self.msgs.config(state=DISABLED)
def send(self):
self.add_msg(self.inpt.get())
self.send_sock.sendto(self.inpt.get(), self.send_addr)
self.inpt.delete(0, 'end')
# ----------WHERE CAN I PUT THIS------------
def msg_listen(messenger, conn, port=13000):
buff = 1024
recv_sock = socket(AF_INET, SOCK_DGRAM)
recv_sock.bind(('', port))
while True:
(data, addr) = recv_sock.recvfrom(buff)
conn.send()
recv_sock.close()
# ------------------------------------------
def msgr_init(conn):
root = Tk()
messenger = MessageApp(root, ip, conn)
messenger.start()
if __name__ == "__main__":
root = Tk(
par_conn, chd_conn = Pipe(True)
msg_proc = Process(target=msgr_init, args=(chd_conn,))
msg_proc.start()
msg_listen(messenger, par_conn)

I discovered Tkinter.after() after reading the docs and it solved my problem.

Related

not responding in tkinter when click button completely

Client Code:
import tkinter as tk
from tkinter.filedialog import asksaveasfilename
import tkinter.messagebox
from PIL import Image
import socket
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
def ConnectToServer():
HOST=entry.get()
PORT=65432
test=True
try:
s.connect((HOST,PORT))
except Exception as e:
tkinter.messagebox.showerror(title="Error",message="Error")
test=False
if test:
tkinter.messagebox.showinfo(title="Noti",message="Connect Successfully")
def takeScreen():
msg="TAKEPIC"
s.sendall(bytes(msg,"utf8"))
file = open('server_image.jpg', "wb")
image_chunk = s.recv(2048)
while image_chunk:
if not image_chunk:
break
file.write(image_chunk)
image_chunk = s.recv(2048)
file.close()
return
def Thoat():
msg="QUIT"
s.sendall(bytes(msg,"utf8"))
s.close()
root.destroy()
def thread1():
t1=threading.Thread(target=takeScreen)
t1.start()
def thread2():
t2=threading.Thread(target=ProcessRunning)
t2.start()
root = tk.Tk()
canvas1 = tk.Canvas(root, width=300, height=300)
canvas1.pack()
entry=tk.Entry()
myButton_connect=tk.Button(text="Connect",command=ConnectToServer)
myButton_TakePic = tk.Button(text="Take screenshot", command=thread1, font=10)
myButton_Process = tk.Button(text="Process Running", command=thread2, font=10)
myButton_Exit=tk.Button(text="Thoat",command=Thoat,font=10)
canvas1.create_window(150,50,window=entry)
canvas1.create_window(150, 100, window=myButton_TakePic)
canvas1.create_window(150, 150, window=myButton_Process)
canvas1.create_window(250,50,window=myButton_connect)
canvas1.create_window(150,200,window=myButton_Exit)
root.mainloop()
Server code:
def pic():
myScreenshot=pyautogui.screenshot()
save_path=asksaveasfilename()
myScreenshot.save(save_path+"_capture1.png")
return save_path+"_capture1.png"
HOST=''
PORT=65432
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((HOST,PORT))
s.listen()
while True:
conn,addr=s.accept()
while True:
data=conn.recv(1024).decode("utf8")
if data=="TAKEPIC":
path=pic()
im=Image.open(path)
im.show()
file=open(path,'rb')
img_data=file.read(2048)
while img_data:
conn.send(img_data)
img_data=file.read(2048)
file.close()
elif data=="app running":
Process(conn)
elif data=="QUIT":
break;
break;
s.close()
img_gui_tkinter:
After I clicked the "Take screenshot" button, the server program still took pictures and saved them, then it sent to the client successfully (because I was able to open the image).
But the problem is that it pops up a dialog like this:
img_after click take screenshot:
and make my program hang. When I press the close button on the child dialog box, it appears
img_hanging error:
If you choose Close the program, the program on the server side will end.
I found that the client side has an error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\KHAI\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
return self.func(*args)
File "d:\MangMaYTinh\TuHoc_DoAn\CreateBox_control\Box_Control.py", line 29, in takeScreen
image_chunk = s.recv(2048)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host
Exception in thread Thread-1:
Traceback (most recent call last):
File "C:\Users\KHAI\AppData\Local\Programs\Python\Python39\lib\threading.py", line 954, in _bootstrap_inner
self.run()
File "C:\Users\KHAI\AppData\Local\Programs\Python\Python39\lib\threading.py", line 892, in run
self._target(*self._args, **self._kwargs)
File "d:\MangMaYTinh\TuHoc_DoAn\CreateBox_control\Box_Control_Ver3.py", line 31, in takeScreen
image_chunk = s.recv(2048)
ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine
I've tried to the best of my ability but still can't resolve that error. Hope everyone can help me. I'm just getting started with Python
I see two problems.
Main problem is that socket is low-level object and it doesn't know if it received all file data so it runs next recv(2048) which waits for more data - and this blocks socket and all code.
Code if not image_chunk: break works only when you close socket after sending data but you need socket to send other commands so you can't close it after sending file. You have to send file size before data and then client will know how many data to receive.
you can use struct to convert integer to 4 bytes - so client will have to receive 4 bytes to get size (and convert to integer using struct). Using string with number you don't know how long will be this string and client will not know how many bytes to receive.
Other problem is that sending file may need longer time and socket blocks mainloop which get key/mouse events from system, send events to widgets, update/redraw window/widgets - so GUI freezes.
I skiped this problem. It would need longer and more complex code with thread.
My version with other changes.
server.py
import socket
#from tkinter.filedialog import asksaveasfilename
#from PIL import Image
import pyautogui
#import threading
import struct
import os
import datetime
# --- functions ---
def take_screenshot(): # PEP8: verb as function name
screenshot = pyautogui.screenshot() # PEP8: `lower_case_names` for variables
# PEP8: spaces around `=`
# using `asksaveasfilename()` (or any other GUI elements) on server is useless - user can't see GUI from server
#path = asksaveasfilename()
#path = path + "_capture1.png"
path = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f_capture.png')
screenshot.save(path)
return path
def handle_client(conn, addr):
while True:
print('[handle_client] read command')
command = conn.recv(1024).decode("utf8")
command = command.lower()
print('[handle_client] run command:', command)
if command == "takepic":
print('[handle_client] take screenshot')
path = take_screenshot()
# displaying on serer make no sense because user can't see it.
#im = Image.open(path)
#im.show()
file_size = os.stat(path).st_size
print('[handle_client] file size:', file_size)
file_size = struct.pack('>I', file_size) # convert `integer` to `4 bytes`
conn.send(file_size)
print('[handle_client] open file')
file_handler = open(path,'rb')
total_size = 0
while True:
print('[handle_client] read data chunk from file')
data_chunk = file_handler.read(2048)
size = len(data_chunk)
if not data_chunk:
break
total_size += size
print('[handle_client] send data chunk:', size, 'total:', total_size)
conn.send(data_chunk)
print('[handle_client] close file')
file_handler.close()
elif command == "app running":
Process(conn)
elif command == "quit":
conn.close()
break
# --- main ---
HOST = '' # PEP8: spaces around `=`
PORT = 65432 # PEP8: spaces around `=`
print('Starting ...')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # PEP8: space after `,`
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # solution for "[Error 89] Address already in use". Use before bind()
s.bind((HOST, PORT))
s.listen()
#all_threads = []
all_clients = []
try:
while True:
print('Waiting for client')
conn, addr = s.accept()
print('Handle client')
# run in current thread - only one client can connect
handle_client(conn, addr)
all_clients.append( (conn, addr) )
# run in separated thread - many clients can connect
#t = threading.Thread(taget=handle_client, args=(conn, addr))
#t.start()
#all_threads.append(t)
except KeyboardInterrupt:
print('Stopped by Ctrl+C')
finally:
s.close()
#for t in all_threads:
# t.join()
for conn, addr in all_clients:
conn.close()
client.py
import socket
import tkinter as tk
import tkinter.messagebox
from PIL import Image
import threading
import struct
# --- functions ---
def connect_to_server(): # PEP8: `lower_case_names` for functions
HOST = entry.get() # PEP8: spaces around `=`
PORT = 65432
try:
s.connect((HOST, PORT)) # PEP8: `
tkinter.messagebox.showinfo(title="Noti", message="Connect Successfully")
except Exception as e:
tkinter.messagebox.showerror(title="Error", message="Error: " + str(e))
def take_screen():
msg = "TAKEPIC"
print('[take_screen] send command:', msg)
s.sendall(msg.encode())
file_size = s.recv(4)
file_size = struct.unpack('>I', file_size)[0] # convert `4 bytes` to `integer`
print('[take_screen] file size:', file_size)
print('[take_screen] open file')
file_handler = open('server_image.jpg', "wb")
total_size = 0
while total_size < file_size:
print('[take_screen] recv data chunk')
data_chunk = s.recv(2048)
size = len(data_chunk)
total_size += size
print('[handle_client] write data chunk:', size, 'total:', total_size)
file_handler.write(data_chunk)
print('[take_screen] close file')
file_handler.close()
tkinter.messagebox.showinfo(title="Success", message="Downloaded Successfully")
def on_exit():
msg = "QUIT"
print('[on_exit] send command:', msg)
s.sendall(msg.encode())
s.close()
root.destroy()
# --- main ---
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
root = tk.Tk()
canvas = tk.Canvas(root, width=300, height=300)
canvas.pack()
entry = tk.Entry()
button_connect = tk.Button(text="Connect", command=connect_to_server)
button_take_pic = tk.Button(text="Take screenshot", command=take_screen)
#button_process = tk.Button(text="Process Running", command=processrunning)
button_exit = tk.Button(text="Exit", command=on_exit)
canvas.create_window(150, 50, window=entry)
canvas.create_window(150, 100, window=button_take_pic)
#canvas.create_window(150, 150, window=button_process)
canvas.create_window(250, 50 , window=button_connect)
canvas.create_window(150, 200, window=button_exit)
root.mainloop()
PEP 8 -- Style Guide for Pyhon Code

Sending and receiving using sockets python

I am trying to create a function to send and receive information over a socket client & server. It appears that my code is somehow blocking. In the code the first command iteration in my for loop is carried out but then the process becomes blocked. Does anyone have any suggestions how to do this using threading or multithreading?
My code is below:
import socket
import json
import sys
import time
import select
import queue
Ni_Rio_IP= "172.22.11.2"
Ni_Base_IP= "172.22.11.1"
class AliceRio:
def __init__(self, ip_rio, ip_pc):
self.ip_rio = ip_rio
AliceRio.udp_port_rio = 60006
self.ip_pc = ip_pc
AliceRio.udp_port_pc = 50005
AliceRio.json= '{"Dest":"","Name":"","Time":"","Val":{"Str":[],"Pos":[[]],"Data":[[]]},"IP":0,"Port":0,"RT error":{"status":false,"code":0,"source":""}}'
AliceRio.dict= json.loads(self.json)
def PrintUDP(self):
print("RIO IP: %s" % self.ip_rio)
print("RIO UDP port: %s" % self.udp_port_rio)
print("PC IP: %s" % self.ip_pc)
print("PC UDP port: %s" % self.udp_port_pc)
def SendRec(self, send_str):
# Set up socket for sending
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Internet, UDP
sock.sendto(bytes(send_str, 'utf-8'), (self.ip_rio, self.udp_port_rio))
sock.close()
print('got here')
# Set up socket for receiving
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Internet, UDP
sock.bind((self.ip_pc, self.udp_port_pc))
rec_str, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
print('got here2')
sock.close()
return rec_str
def Receive(self, rec_str):
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) # Internet, UDP
sock.bind((self.ip_pc, self.udp_port_pc))
rec_str, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
sock.close()
return rec_str
def Send(self, send_str):
# Set up socket for sending
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Internet, UDP
sock.sendto(bytes(send_str, 'utf-8'), (self.ip_rio, self.udp_port_rio))
sock.close()
#return rec_str
def Aim(self, aim_perc):
if aim_perc < 0 or aim_perc > 100: return "aim_perc out of range"
send_dict=AliceRio.dict
send_dict["Dest"]='Rio'
send_dict["Name"]='Laser Control'
Laser_Mode=1
Simmer_A=0
Pulse_A= 0
Pulse_ms= 20
send_dict["Val"]["Str"]=[str(Laser_Mode), str(aim_perc), str(Simmer_A), str(Pulse_A), str(Pulse_ms)]
send_json=json.dumps(send_dict)
# send it out
self.SendRec(send_json)
rec_json= self.SendRec(send_json)
rec_dict=json.loads(rec_json)
return "Aim laser now at " + rec_dict["Val"]["Str"][1] +'%'
def PWM_Laser_Fan(self, fan_perc):
send_dict=AliceRio.dict
send_dict["Dest"]='Rio'
send_dict["Name"]='PWM Laser'
send_dict["Val"]["Str"][0]=str(fan_perc)
send_json=json.dumps(send_dict)
# send it out
rec_json= self.SendRec(send_json)
rec_dict=json.loads(rec_json)
return rec_dict["Val"]["Str"][0]
def Poll(self):
send_dict=AliceRio.dict
send_dict["Dest"]='Rio'
send_dict["Name"]='Poll'
send_json=json.dumps(send_dict)
# send it out
rec_json= self.SendRec(send_json)
rec_dict=json.loads(rec_json)
if rec_dict["Val"]["Data"][0][0]==0: pid_mode='off'
else: pid_mode='PID'
print('PID mode:', pid_mode)
print('Pos X:', rec_dict["Val"]["Data"][0][1])
print('Pos Y:', rec_dict["Val"]["Data"][0][2])
print('Home:', rec_dict["Val"]["Data"][0][3])
print('Enabled:', rec_dict["Val"]["Data"][0][4])
def PIDControl(self, pid_mode,pid_center):
if pid_mode=="off": mode= 0
elif pid_mode=="PID":mode =1
else: return "pid_mode not valid"
if pid_center[0] not in range(-2048,2048): return "center x-pos not in range"
if pid_center[1] not in range(-2048,2048): return "center y-pos not in range"
send_dict=AliceRio.dict
send_dict["Dest"]='Rio'
send_dict["Name"]='PID Control'
send_dict["Val"]["Str"]=[str(mode), str(pid_center[0]), str(pid_center[1])]
send_json=json.dumps(send_dict)
# send it out
rec_json= self.SendRec(send_json)
rec_dict=json.loads(rec_json)
return "PID mode now at " + rec_dict["Val"]["Str"][0]
Alice1 = AliceRio(Ni_Rio_IP, Ni_Base_IP)
Alice1.PrintUDP()
for i in range(10):
Alice1.Aim((i*10)+10)
time.sleep(0.2)
I would suggest learning to use Pdb and trace through the execution of your program to find where it is getting caught.
Also when learning/developing with sockets I've found that it helps to have separate programs for your client and server in the beginning so you can see how both sides are handling exchanges instead of going the threading route to start since the logging can get confusing, best of luck!
Module threading does help in this scenario.
We can create a thread to receiving incoming messages. And when new message received the thread trigger an event to notify the waiting method SendRec.
import sys
import socket
import json
import threading
import time
class AliceRio:
def __init__(self, .....):
# .........
self.s_in = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.s_in.bind((self.ip_pc, self.udp_port_pc))
self.evt = threading.Event()
self.last_msg = None
def _recv(self):
while True:
msg, _ = self.s_in.recvfrom(1024)
self.last_msg = msg
self.evt.set()
def SendRec(self, send_str):
if not hasattr(self, 'th_recv'):
th = threading.Thread(target=self._recv)
th.setDaemon(True)
th.start()
self.th_recv = th
self.evt.clear()
rio_endpoint = (self.ip_rio, self.udp_port_rio)
s_out = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s_out.sendto(bytes(send_str, 'utf-8'), rio_endpoint)
s_out.close()
if self.evt.wait(timeout=15.0) and self.last_msg:
return self.last_msg
raise Exception('timeout waiting for response.')

How can i use TCP/IP communication routine in a different thread for continuous operation

I am working in a project where I interfaces two rotary encoder with Raspberry pi and at same time read two sensor values and shown them on tkinter window and now as per project I need to send these values to PC whenever a query command receive from PC .I read from google that if we want two continuous loop then we cab achieve via thread method. But I don't have enough knowledge about threading.
Below I am providing tkinter code as well as TCP/IP code.
import pigpio
import tkinter as tk
from PIL import ImageTk,Image
import time
from RPi import GPIO
import serial
i="*00T%"
j="*01T%"
s=serial.Serial(port='/dev/ttyS0',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
class decoder: # class for decoding rotary encoder
def __init__(self, pi, gpioA, gpioB, callback):
----------
---------------
----------------
if __name__ == "__main__":
rpm=0
tor=0
pow=0
pi = pigpio.pi()
st=1
pos=0
def callback(way): # interrupt event sense on pin no 17,18
global pos
global st
if st ==1:
pos += way
if pos >= 9999:
pos=9999
if pos <= 0:
pos=0
var.set(pos)
print("pos={}".format(pos))
def rpm_update():
--------------
rpm read code
---------------
def tor_update():
-------------------
torque read code
---------------------
def pow_update():
------------------------
power Calculation
--------------------------
def update():
--------------------
root.after(200,update)
path="/home/pi/logo.png"
root=tk.Tk()
img=ImageTk.PhotoImage(Image.open(path))
panel=tk.Label(root,image=img)
panel.pack()
panel.place(x=195,y=10,height=50,width=80)
root.title("Dynalec")
root.geometry("500x600")
{
body of tkinter screen
}
decoder=decoder(pi, 17, 18, callback)
root.after(200,update)
root.mainloop()
and my server code which is working perfectly in while loop.
I want to merge this two code two achieve desire result perfectly.
import socket
import encodings
rpm = 2000
tor = 35
pow = 7.33
HOST = '169.254.29.78'
PORT = 65432
def data():
global rpm, tor, pow
my_sensor = "{},{},{}".format(rpm,tor,pow)
return my_sensor # return data seperated by comma
def my_server():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
print("Server Started waiting for client to connect ")
s.bind((HOST, PORT))
s.listen(5)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024).decode('utf-8')
if str(data) == "Data":
print("Ok Sending data ")
my_data = data()
x_encoded_data = my_data.encode('utf-8')
conn.sendall(x_encoded_data)
elif str(data) == "Quit":
print("shutting down server ")
break
if not data:
break
else:
pass
if __name__ == '__main__':
while 1:
my_server()

HOW TO DO Socket Multiple Clients

this is a part from a code that making GUI for process list. I'm trying to do a Socket Multiple Clients but it is'nt working. Can some one help me with this?
import socket, pickle
import threading
import select
BUF_SIZE = 8192
class Network(threading.Thread):
def __init__(self, frame):
threading.Thread.__init__(self)
self.frame = frame
self.server_sock = socket.socket()
def run(self, client_sock=None):
self.server_sock.bind(('', 1729))
self.server_sock.listen(5)
self.open_client_sockets = []
while True:
self.rlist, self.wlist, self.xlist = select.select([self.server_sock] + self.open_client_sockets, [], [])
if self.currect_sock is self.server_sock:
client_sock, client_address = self.server_sock.accept()
self.open_client_sockets.append(self.new_sock)
data = client_sock.recv(BUF_SIZE)
processes_list = pickle.loads(data)
for process_details in processes_list:
self.frame.add_line(process_details)
else:
data = self.client_sock.recv(BUF_SIZE)
processes_list = pickle.loads(data)
for process_details in processes_list:
self.frame.add_line(process_details)
client_sock.close()
self.server_sock.close()
self.server_sock.close()

program fails to join thread used for server socket

I have a GUI and i need to be able to open a server socket that listens to incoming traffic, but when I try to join the server thread it gives an error:
RuntimeError: cannot join current thread
Here is the relevant code:
def create_widgets(self):
...other widgets...
self.thread1 = threading.Thread(target = self.serverstart)
self.button2 = Button(text = "Start", command = self.thread1.start)
self.button2.grid(row=2, column=3)
def serverstart(self):
import tcpServer
tcpServer.start(self.intip)
self.thread1.join()
Here is the code for tcpServer:
import socket
def start(intip):
host = intip
port = 5000
s = socket.socket(socket.AF_INET6)
s.bind((host, port))
s.listen(1)
c, addr = s.accept()
print "Connection from: " + str(addr)
while True:
data = c.recv(1024)
if not data:
break
print "from connected user: " + str(data)
data = str(data).upper()
print "sending: " + str(data)
c.send(data)
c.close()
if __name__ == '__main__':
start()
Thank you in advance for your help
RuntimeError: cannot join current thread means that the current thread is trying to join itself.
Indeed, you launch your thread routine (bound method serverstart) and store the returned thread. That routine, then, attempts to join that thread ... which is itself. RuntimeError

Categories