i made a messenger that works with the socket library. it has 2 sides : server and client.
i later decided to make a GUI for it , too. ( with tkinter )
when i was making it , i realized that programs does not work correctly. here :
import socket
from tkinter import *
win = Tk()
win.geometry("300x300")
win.resizable(False,False)
def disc() :
s = socket.socket()
ip = "0.0.0.0"
port = 9999
s.bind((ip,port))
s.listen()
print ('please wait...')
c , addr =s.accept()
print ('someone has joined!')
while True :
msg = input('your message : ' )
c.send(msg.encode('utf8'))
print (c.recv(1024).decode())
lbl_1 = Label(win,text="mychat",bg="light blue")
lbl_1.place(x=130,y=20)
lbl_2 = Label(win,text="your message: ")
lbl_2.place(x=130,y=50)
lbl_3 = Label(win,text="recieved message: ")
lbl_3.place(x=130,y=70)
btn_1 = Button(win,text="make your device discoverable",command=disc)
btn_1.pack()
txt_1 = Text(win)
txt_1.pack()
word = "messages"
txt_1.insert(END, word)
win.mainloop()
here , you can see what i've tried. i have two parts : the socket part and the gui part.
the socket part is in the def block.
but this does not work correctly. if you put the mainloop before socket part , it will never be executed because mainloop is not finished until i close the program.
if you put main loop after socket part , the GUI will not be displayed until someone join the server.(because socket part is not finished)
here , you see i've tried another thing. i put the socket part in def and then made a button for it. but this doesn't work either. when you press the button , the program stops and gives a ( not responding ) error on title bar . ( so until someone has joined , it will not respond. )
i want a solution for this code that the GUI part works and doesn't care to socket part(dismissing it). in other words , python executes the 2 parts in one time.
Sounds like you want to start using threads.
You basically have two "while True" loops, the first is the win.mainloop() that stops executing when the user exists the GUI. The second is the while True in the disc function, that waits for input from the user (msg = input(...)) and then waits for data from the client (s.recv)
import threading
def disc():
...
connection = threading.Thread(target=disc).start()
The rest of the GUI code goes below the connection thread without the make your device discoverable part as it is now unnecessary:
# remove this part
btn_1 = Button(win,text="make your device discoverable",command=disc)
btn_1.pack()
Related
I'm using the pylutron_caseta python package for use with Lutron devices. At this stage I'm trying to listen for button presses. I'm able to pair with the bridge and get the buttons, but I'm unable to listen for button presses. Here's my code:
import asyncio
from pylutron_caseta.smartbridge import Smartbridge
from pylutron_caseta.pairing import async_pair
from os.path import exists
bridgeIp = "192.168.1.40"
async def pair(host: str):
def _ready():
print("Press the small black button on the back of the bridge.")
data = await async_pair(host, _ready)
with open("caseta-bridge.crt", "w") as cacert:
cacert.write(data["ca"])
with open("caseta.crt", "w") as cert:
cert.write(data["cert"])
with open("caseta.key", "w") as key:
key.write(data["key"])
print(f"Successfully paired with {data['version']}")
async def registerButton(bridge,button_id):
print("Press the small button on the button device.")
def printThis(x):
print(x)
async def connect():
bridge = Smartbridge.create_tls(bridgeIp, "caseta.key", "caseta.crt", "caseta-bridge.crt")
await bridge.connect()
buttons = bridge.get_buttons()
print (buttons)
for b in buttons:
print (b)
loopListen = asyncio.get_event_loop()
asyncio.ensure_future(listen(bridge,int(b)))
loopListen.run_forever()
async def listen(bridge,_buttonID):
while True:
bridge.add_button_subscriber(str(_buttonID), printThis)
#Program
if exists("caseta-bridge.crt"):
print("found pair files")
loop = asyncio.get_event_loop()
loop.run_until_complete(connect())
else:
loop = asyncio.get_event_loop()
loop.run_until_complete(pair(bridgeIp))
loop = asyncio.get_event_loop()
loop.run_until_complete(connect())
I expect that when I press a button on the Lutron Pico remote that I get some sort of response printed. I get nothing printed. I'm guessing I have the "listen" function incorrect, just not sure how.
Any help is appreciated!
I was able to make my Lutron Pico remotes work with a node js module called lutronpro found here: https://www.npmjs.com/package/lutronpro.
I had to upgrade to the pro lutron controller module to make it work. I would prefer to do this in Python, but at least I could get this to work, and know when a remote button was pressed and from there I could call a python web service to do the rest. I ultimately just made it start up this program on boot on my raspberry pi, and now I use my pico remote to control almost anything in the hous
I am working on a TCP chatroom with python 2.7, my server should manage multiple clients (I used select module) and used threads for clients, I have a problem on what I am printing.
Since my raw_input in the client has text in it, it gets easily ugly when the other thread in charge of printing other clients messages gets triggered .
My code's results looks like this : (on client's window)
WELCOME TO CHATROOM
you are connected
your pseudo is PSEUDO
other_user joined the chatroom
and the my raw_input() pops out and it goes like this :
* < your message > * :e chatroom
Or sometimes I get another user's message over Raw_input text
because it replaces the last line printed, I figured I could delete the last printed line if it's equal to my raw_input text to print an unexpected message over it and then reprint my Raw_input but I don't know to implement it.
I have tried moving my cursor with "\x1b["-like commands and deleting last printed line but those commands are not working for me.
class Sender(Thread):
"""would send messages to the server"""
def __init__(self, socket, username):
Thread.__init__(self)
self.socket = socket
self.username = username
def run(self):
while 1:
message = raw_input("* < your message > * :")
self.socket.send(message)
class Receiver(Thread):
import sys
def __init__(self,socket):
Thread.__init__(self)
self.connexion = socket
def run(self) :
while 1:
message_received = self.connexion.recv(4096)
message_received = message_received.decode()
sys.stdout.write("\r" + message_received)
sys.stdout.flush()
if message_recu =='' or message_recu.upper() == "OFF":
break
self._Thread__stop()
print "\nClient arrete. Connexion interrompue."
self.connexion.close()
For \b and \r to work you need to be ending your print statements with a space instead of a newline.
EG:
print 'hello there', the , at the end of the print statement defines that it wont go forwards one line, but instead end it with a space. Then you can use another print statement to delete the text.
EG:
print '\r ' this would go to the beginning of the line and replace it with a space, then you can output text over it.
I'm trying to make a Tkinter based GUI for an Arduino printing sensors value and responding to user input.
The code I'm trying to use to eliminate while loops is this, which doesn't print any sensor information, the only output is "Trying.../dev/ttyACM0" followed by the tkinter window opening.
import serial
import time
from Tkinter import *
connected = False
write_to_file_path = "output.txt"
output_file = open(write_to_file_path, "w+")
locations=['/dev/ttyACM0','/dev/ttyACM1','/dev/ttyACM2','/dev/ttyACM3']
for device in locations:
try:
print "Trying...",device
ser = serial.Serial(device, 9600)
break
except:
print "Failed to connect on",device
## loop until the arduino tells us it is ready
while not connected:
serin = ser.read()
connected = True
time.sleep(0.1)
ser.flushInput()
time.sleep(1)
def serialRead():
if ser.inWaiting():
line = ser.readline()
data = line.decode("utf-8").split('\t')
print(data)
output_file.write(line)
root.after(1000, serialRead)
root = Tk()
root.title("Temperature Control")
serialRead()
root.mainloop()
This, on the other hand, works perfectly with the exception of not having a tkinter window. But it removes old input from the buffer and reads in the new input.
import serial
import time
connected = False
write_to_file_path = "output.txt"
output_file = open(write_to_file_path, "w+")
serial_port = '/dev/ttyACM0'
baud_rate = 9600
ser = serial.Serial(serial_port, baud_rate, timeout=5)
time.sleep(0.1)
ser.flushInput()
time.sleep(1)
while True:
if ser.inWaiting():
line = ser.readline()
data = line.decode("utf-8").split('\t') #ser.readline returns a binary, convert to string
print data[0] + '\t' + data[1]
output_file.write(line)
This was inspired by a different stackoverflow post from a while ago: Run an infinite loop in the backgroung in Tkinter
I've seen some example using threading but I don't know much about python nor threading so I'd really like to get it to work with root.after() if that's possible. I've also tried the example using root.after, all of which are pretty similar to the one I linked, and I couldn't get any of them working. Am I doing anything obviously wrong or in a way that's much more difficult than it needs to be? I would appreciate it greatly if someone would point me in the right direction.
I made a UI in TK for reading data from a GPS receiver and I had difficulties getting root.mainloop() to run, so instead I put a callback inside the TK app that ends up calling root.update() rather than mainloop.
The code looks something like this:
class App:
def __init__(self, master):
self.sats = tk.StringVar()
self.satsnum = tk.Label(self.frame, textvariable=self.sats, bg="blue")
self.satsnum.pack()
def update_display(self, master):
while source:
self.sats.set(n_sats)
if n_sats < 10:
satsbgcolor = 'red'
else:
satsbgcolor = 'green'
self.satsnum.configure(bg = satsbgcolor)
master.update()
time.sleep(1)
with serial_link.get_base_args_driver(args) as driver:
with Handler(Framer(driver.read, driver.write, verbose=True)) as source:
root = tk.Tk()
app = App(root)
app.update_display(root)
Note, the time.sleep(1) is necessary on MacOS as tk.update() will leak memory if the update is called too fast.
I would like to ping every second in my network a certain hostname and change in return the name of the corresponding button.
For now I have this :
import tkinter as tk
import time
# variables
hostname = ("ASUS-PC")
mot= "inactif"
class test(tk.Tk):
# make buttons on grid
def __init__(self):
tk.Tk.__init__(self)
self.button = list()
for i in range(10):
i=i+1
RN = ('RN '+str(i))
self.button.append(tk.Button(text=RN))
self.button[-1].grid(row=0,column=i)
# ping hostname every second
def ping(self):
import subprocess
import os
result=subprocess.Popen(["ping", "-n", "1", "-w", "200", hostname],shell=True).wait()
if result == 0:
print (hostname, "active")
else:
B = self.button[0]
B ['text'] = mot
time.sleep(1)
while True:
ping()
app = test ()
app.mainloop()
It doesn't work and I don't know why. At beginning it was a "self" problem but now it seems to be related to how I ping every second (I took it from there What is the best way to repeatedly execute a function every x seconds in Python?) If someone know the answer...
Thanks for your help
The reason your code "doesn't work" is that you have an infinite loop that calls sleep. This prevents the event loop from processing any events, including events that tell Tkinter to refresh the display. Your program is actually working, you just can't see any results.
The solution is to use after to call your function once a second. Between those calls, tkinter will have time to update the display without you having to do any extra work.
All you need to do is remove the call to sleep, and add a call to after. Once you do that, remove your while loop, and call this function once. Tkinter will then call this function once every second (approximately)
It looks something like this:
def ping(self):
<all your other code>
self.after(1000, self.ping)
It looks a bit like recursion, but it's not. You're simply adding an event on the queue of events managed by Tkinter. When the time comes, it pulls the item off of the queue and executes it.
Thanks a lot everyone ! Now it works :
...
# ping hostname every second
def ping(self):
import subprocess
import os
result=subprocess.Popen(["ping", "-n", "1", "-w", "200", hostname],shell=True).wait()
if result == 0:
print (hostname, "active")
else:
print (hostname, "inactive")
B = self.button[0]
B ['text'] = mot
self.after(1000, self.ping)
app = test ()
app.ping()
app.mainloop()
first time SO user, please excuse any etiquette errors. I'm trying to implement a multithreaded program in python and am having troubles. This is no doubt due to a lack of understanding of how threading is implemented, but hopefully you can help me figure it out.
I have a basic program that continually listens for messages on a serial port and can then print/save/process/etc them, which works fine. It basically looks like this:
import serial
def main():
usb = serial.Serial('/dev/cu.usbserial-A603UBRB', 57600) #open serial w\ baud rate
while True:
line = usb.readline()
print(line)
However what I want to do is continually listen for the messages on a serial port, but not necessarily do anything with them. This should run in the background, and meanwhile in the foreground I want to have some kind of interface where the user can command the program to read/use/save these data for a while and then stop again.
So I created the following code:
import time
import serial
import threading
# this runs in the background constantly, reading the serial bus input
class serial_listener(threading.Thread):
def __init__(self, line, event):
super(serial_listener, self).__init__()
self.event = threading.Event()
self.line = ''
self.usb = serial.Serial('/dev/cu.usbserial-A603UBRB', 57600)
def run(self):
while True:
self.line = self.usb.readline()
self.event.set()
self.event.clear()
time.sleep(0.01)
# this lets the user command the software to record several values from serial
class record_data(threading.Thread):
def __init__(self):
super(record_data, self).__init__()
self.line = ''
self.event = threading.Event()
self.ser = serial_listener(self.line,self.event)
self.ser.start() #run thread
def run(self):
while(True):
user_input = raw_input('Record data: ')
if user_input == 'r':
event_counter = 0
while(event_counter < 16):
self.event.wait()
print(self.line)
event_counter += 1
# this is going to be the mother function
def main():
dat = record_data()
dat.start()
# this makes the code behave like C code.
if __name__ == '__main__':
main()
It compiles and runs, but when I order the program to record by typing r into the CLI, nothing happens. It doesn't seem to be receiving any events.
Any clues how to make this work? Workarounds are also fine, the only thing is that I can't constantly open and close the serial interface, it has to remain open the whole time, or else the device stops working until un/replugged.
Instead of using multiple threads, I would suggest using multiple processes. When you use threads, you have to think about the global interpreter lock. So you either listen to events or do something in your main thread. Both at the same time will not work.
When using multiple processes I would then use a queue to forward the events from your watchdog that you would like to handle. Or you could code your own event handler. Here you can find an example for multiprocess event handlers