How to apply safe multi-threading in tkinter? - python

I have a system that is connected with multiple hardware devices(cameras, router, sensors etc.). So I thought about having a separate GUI that pings the IP addresses of these devices almost every second in order to check whenever a device gets disconnected, and I need to ping them all together.
This is the ping script:
def testRouter(self):
response = os.popen(f"ping {self.router_ip} {self.count} 1").read()
if "Received = 1" in response:
self.router_status.configure(image=self.connected)
self.router_button.configure(command=self.clickedNone)
else:
self.router_status.configure(image=self.disconnected)
self.router_button.configure(command=self.clickedRouter)
self.root.after(5000, self.testRouter)
But the GUI freezes for more than a minute till it displays the content, and many other times it just freezes forever. So I tried to implement a thread for each ping but I came across the fact that tkinter is not a thread-safe library with an error RuntimeError: main thread is not in main loop
Any idea on how to implement this? (without using mtTkinter please)

Related

Serial communication error with Arduino using a Threading.Timer

In my python project, I use serial communication to send commands and receive responses from the Arduino. I have several tasks that need to occur cyclically, but in different time frames. For that, I use threading.Timer. Then, from time to time, I send a command to the Arduino, which returns a value (taken from the sensor).
Everything works perfectly. However, occasionally, when I press another button that sends information (considerably long) to the Arduino, the Thread simply stops being called when it should, as if the command was interrupted in the middle of the process, which causes an error in the serial communication of the thread.
No error information appears on the console.
Is there a way to force threading.Timer to wait for the last command to finish, so that it is executed?
Cyclical function:
def action_5_seconds(self):
global state_banho
if state_banho == False:
self.apresenta_parametros()
else:
self.verify_pid_banho()
self.verify_pid_lamp()
#self.agente_ar_automatic()
print("Threand 5s OK...")
clock_5_sec = threading.Timer(5, self.action_5_seconds)
clock_5_sec.start()
Function that sends to arduino:
def power_off_ar(self):
serial_port.flush()
serial_port.write(b's,2,99,3450,1650,450,450,450,1250,450,400,450,450,400,450,450,400,450,400,450,450,400,450,450,400,450,400,450,400,450,450,400,1300,450,400,450,450,400,450,450,400,450,400,450,400,450,450,400,450,450,400,450,1300,400,450,450,400,450,400,450,400,450,450,400,450,450,400,450,400,450,450,400,450,450,1250,450,400,450,1300,450,1250,450,450,400,450,450,400,450,400,450,1300,400,450,450,1250,450,1300,450,400,450,1250,450')
#time.sleep(0.5)
print("Comando Enviado para o AR")

How to cancel a blocking thread caused by input() in Python?

I'm starting to learn more about TCP protocols in Python and I've been having some trouble with blocking threads inside clients.
Ideally, my application would work like this: I have different clients with thread functions, each one of them containing an input function in order to receive a specific command to send to the server (for example 'X'). When the 'X' is tapped in ONE client, the server receives it and sends a message to all the other clients informing that the program will continue and releasing them from their input functions - almost like cancelling them.
The problem lies on the fact that the input functions are blocking the clients from leaving the loop. I've tried setting the input thread functions as daemon but it blocks until you tap something anyway - which is unfortunately the only workaround that I've found so far.
I would like to use socket and the select module for connection, without being attached to any particular OS (so no msvcrt that works on Windows or the select module to monitor the stdin, which is only available in UNIX based OS).
Any help would be greatly appreciated!

Python Sockets Select is hanging - Doing other tasks while waiting for socket data?

I am rather a noob here, but trying to setup a script where I can poll a socket, and when no socket data has been sent, a loop continues to run and do other things. I have been playing with several examples I found using select(), but no matter how I organize the code, it seems to stop on or near the server.recv() line and wait for a response. I want to skip out of this if no data has been sent by a client, or if no client connection exists.
Note that this application does not require the server script to send any reply data, if it makes any difference.
The actual application is to run a loop and animate some LEDs (which needs root access to the I/O on a Raspberry Pi). I am going to send this script data from another separate script via sockets that will pass in control parameters for the animations. This way the external script does not require root access.
So far the sending and receiving of data works great, I just can't get loop to keep spinning in the absence of incoming data. It is my understanding that this is what select() was intended to allow, but the examples I've found don't seem to be working that way.
I have attempted adding server.setblocking(0) a few different places to no avail. (If I understand correctly a non-blocking instance should allow the code to skip over the recv() if no data has been sent, but I may be off on this).
I have based my code on an example here:
http://ilab.cs.byu.edu/python/select/echoserver.html
Here is the server side script followed by the client side script.
Server Code: sockselectserver.py
#!/usr/bin/env python
import select
import socket
import sys
server = socket.socket()
host = socket.gethostname()
port = 20568
size = 1024
server.bind((host,port))
server.listen(5)
input = [server,sys.stdin]
running = 1
while running:
inputready,outputready,exceptready = select.select(input,[],[])
for s in inputready:
if s == server:
# handle the server socket
client, address = server.accept()
input.append(client)
elif s == sys.stdin:
# handle standard input
junk = sys.stdin.readline()
running = 0
else:
# handle all other sockets
data = s.recv(size)
if data:
s.send(data)
else:
s.close()
input.remove(s)
print "looping"
server.close()
Client Code: skclient.py
#!/usr/bin/python # This is client.py file
import socket # Import socket module
s = socket.socket() # Create a socket object
host = socket.gethostname() # Get local machine name
port = 20568 # Reserve a port for your service.
s.connect((host, port))
data = "123:120:230:51:210:120:55:12:35:24"
s.send(data)
print s.recv(1024)
s.close # Close the socket when done
What I would like to achieve by this example is to see "looping" repeated forever, then when the client script sends data, see that data print, then see the "looping" resume printing over and over. That would tell me it's doing what is intended I can take it from there.
Interesting enough, when I test this as is, whenever I run the client, I see "looping" printed 3 times on the screen, then no more. I don't fully understand what is happening inside the select, but I'd assume it would only print 1 time.
I tried moving the inputready.. select.select() around to different places but found it appears to need to be called each time, otherwise the server stops responding (for example if it is called once prior to the endless while: loop).
I'm hoping this can be made simple enough that it can be taught to other hacker types in a maker class, so I'm hopeful I don't need to get too crazy with multi-threading and more elaborate solutions. As a last resort I'm considering logging all my parameters to mySQL from the external script then using this script to query them back out of tables. I've got experience there and would probably work, but it seems this socket angle would be a more direct solution.
Any help very much appreciated.
Great news. This was an easy fix, wanted to post in case anyone else needed it. The suggestion from acw1668 above got me going.
Simply added a timeout of "0" to the select.select() like this:
inputready,outputready,exceptready = select.select(input,[],[],0)
This is in the python docs but somehow I missed it. Link here: https://docs.python.org/2/library/select.html
Per the docs:
The optional timeout argument specifies a time-out as a floating point number in seconds. When the timeout argument is omitted the function blocks until at least one file descriptor is ready. A time-out value of zero specifies a poll and never blocks.
I tested the same code as above, adding a delay of 5 seconds using time.sleep(5) right after the print "looping" line. With the delay, if no data or client is present the code just loops every 5 seconds and prints "looping" to the screen. If I kick off the client script during the 5 second delay, it pauses and the message is processed the next time the 5 second delay ends. Occasionally it doesn't respond the very next loop, but rather the loop following. I assume this is because the first time through the server.accept is running and the next time through the s.recv() is running which actually exchanges the data.

PySide QTextEdit or QPlainTextEdit update faster?

I am now trying to make a GUI on my PC communicate with a Server per sockets.
here is part of the code of GUI:
def listenToServer(self):
""" keep listening to the server until receiving 'All Contracts Finished' """
self.feedbackWindow.appendPlainText('--Executing the Contracts, Listening to Server--')
contentsListend = ''
while contentsListend != 'All Contracts Finished':
#keep listen from the socket
contentsListend = self.skt.recv(1024)
#make the GUI show the text
self.feedbackWindow.appendPlainText(contentsListend)
On the Other side, the server will send data one by one but with some interval. Here is the test code simulating the server:
for i in range(7):
print 'send back msg, round: ', i # this will be printed on the screen of the server, to let me know that the server works
time.sleep(1) # make some interval
# c is the connected socket, which can send messages
# just send the current loop number
c.send('send back msg' + str(i))
c.send('All Contracts Finished')
c.close()# Close the connection
Now, everything works except the problem that, the GUI will only show the received messages after the whole for loop in the server.
Once I run the server and the GUI. The server side print the messages onto the screen with correct speed one by one, but the GUI has no response, it does not update. Till the end of the program, all the 7 lines occurs all at once at GUI side. I want them to appear one by one, so that later I can inspect the state of the server with this GUI on my PC.
Can anybody help, thanks a lot!
This has nothing to do with "fast" or "slow".
The GUI runs on the same thread as your listenToServer method - so as long as it's running nothing can happen on the GUI thread. You'll notice that you can't move, resize or click anything in the GUI while you're waiting socket input.
You'll have to run your listenToServer method on a thread separate from the GUI. The proper way to do that would be to implement a Worker object that receives data from the socket and notifies you textEdit via a Signal->Slot connection that there's data ready to receive.
I answered a similar question a while back, that might help
https://stackoverflow.com/a/24821300/2319400
A really quick and dirty alternative would be to process all queued events when you've appended new data, via:
QApplication.processEvents()
This gives Qt time to e.g. repaint the GUI on the screen with new text.
Your GUI will however not respond to any events while python is waiting for data to come from the socket!

How to allow simultaneous messages over a socket?

I'm working on a game (Risk). I've already finished set-up, so a solution that doesn't require me to completely redesign networking would be preferable. The problem is I've gotten to main gameplay, and I need to allow messages from multiple users at the same time. The general idea is that when one player is taking their turn, another player can send a forfeit message, and still exit out of the game without crashing the whole server. Is there a way to do this, possibly using threading?
There is no reason that your main thread should be blocked on one connection.
You need one listening thread, when a connection is made background threads handle communications to clients.
You do need to maintian a bullet proof state machine so that clients know the appropriate messages they can send at any given state, and the server needs to know which valid messages can be processed at any given state.
Search stackoverflow, you'll find many examples, such as this:
Sockets example

Categories

Resources