Python Tkinter callbackfunction executes before mainloop - python

I'm trying to build a gui for communication with a serial device. For this I'm using Tkinter. My problem is, that every time I execute the script only the estCon-function is executed and the mainloop, and therefore the gui is never started. If I place the definition of the estCon function after the main loop it says that the estCon function was not found.
def estCon():
# establish connection
while True:
try:
ser = serial.Serial(port, baud, bytesize)
print('Connected.')
break
except serial.SerialException:
print('waiting for device ' + port + ' to be available.')
time.sleep(3)
starttime = time.time()
outfile = open(filename, 'a')
doprint = True
root = Tk()
estConButton = Button(root, text="Establish serial connection",
command=estCon())
estConButton.pack()
root.mainLoop()

You need to change this line:
estConButton = Button(root, text="Establish serial connection", command=estCon())
To:
estConButton = Button(root, text="Establish serial connection", command=estCon)
Notice the lack of parentheses (). Basically, you need to pass a reference to the function that will be called when you press the button and not an actual call.

Related

how to make a code that does 2 processes in python

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()

How to stop continuous_threading in python

I am working on a project, where I have to read values from serial port and display them on tkinter GUI. I am using continous threading module of python. I am using a continous thread to read the data on serial port continously after every 0.5s, but now i want to stop this continous thread. So how should I stop it ?
This is the function which I am calling when a checkbutton is presssed
def scan():
print("in scan")
btn1_state = var1.get()
print("Scan: %d"%btn1_state)
t1 = continuous_threading.PeriodicThread(0.5, readserial)
if(btn1_state == 1):
t1.start()
else:
print("entered else ")
t1.stop() #I am using stop() but the thread doesn't stop
Please Help
The problem is likely that you are using a blocking read function in your readserial function. It needs a timeout. I can reproduce with this code:
import time
import continuous_threading
time_list = []
def save_time():
while True:
time.sleep(1)
time_list.append(time.time())
th = continuous_threading.PeriodicThread(0.5, save_time)
th.start()
time.sleep(4)
th.join()
print(time_list)
This never exits.
Modified from the examples.
Since continuous_threading expects it's event loop to be in control, it never gets to the stop event.

Getting different outputs on Gitbash and VScode Terminal

Below is the smallest reproducible example I could come up with. In the main function, first an object of serverUDP class is created and with the use of threading a function run is called which also creates another thread to call another function RecvData. Problem is the main thread is not printing port value until the program is stopped with ctrl + C. Cannot understand why is this happening.
import socket, simpleaudio as sa
import threading, queue
from threading import Thread
import time
class ServerUDP:
def __init__(self):
while 1:
try:
self.s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.s.bind(('127.0.0.1', 0))
self.clients = set()
self.recvPackets = queue.Queue()
break
except:
print("Couldn't bind to that port")
def get_ports(self):
return self.s.getsockname()
def RecvData(self, name, delay, run_event):
while run_event.is_set():
time.sleep(delay)
pass
def run(self, name, delay, run_event):
threading.Thread(target=self.RecvData, args = ("bob",d1,run_event)).start() #separate thread for listening to the clients
while run_event.is_set():
time.sleep(delay)
pass
self.s.close()
def close(self):
self.s.close()
if __name__ == "__main__":
roomserver = ServerUDP()
run_event = threading.Event()
run_event.set()
d1 = 1
t = Thread(target= roomserver.run, args = ("bob",d1,run_event))
t.start()
port = roomserver.get_ports()[1]
print("port is", port)
try:
while 1:
time.sleep(.1)
except KeyboardInterrupt:
print("attempting to close threads. Max wait =",d1)
run_event.clear()
t.join()
print("threads successfully closed")
UPD: I'm on windows platform and was using VScode editor for coding and Git Bash terminal to run this program. I just ran this on VScode terminal and magically it was giving the port number. Is this a known issue in Git Bash terminal?
Adding VScode and Git Bash tags to know something about it.

Incoming serial data is not getting printed on the console using pyserial

I am building a GUI in Tkinter and I want to receive incoming data from Serial (from a microcontroller).
Also, I want to print that out onto the text-editor widget in Tkinter (similar to the serial monitor console).
For that, I am right now trying to write a receiving code in Python without Tkinter but I am unable to receive any data in the console.
The code is as follows:
import serial
import time
import threading
global serial_open
serial_open = False
def serial_read():
global ser
global var
while True:
if serial_open == True:
var = ser.readline()
if var != "":
print(var)
else:
pass
ser = serial.Serial('COM3', baudrate = 19200)
time.sleep(3)
serial_open = True
print("COM3 Connected")
threading.Thread(target = serial_read).start()
time.sleep(10)
ser.close()
print("Disconnected")

Reading serial input and printing to Tkinter GUI

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.

Categories