Tkinter GUI updating depending on serial connection - python

Good afternoon thank you all who look at my issue,
I am building a script to do initial configuration on switches through console port, I can make a connection to the console port the issue comes when building a GUI in TKINTER.
I want the gui to connect through a specified some port when the button is pressed and when the connection is made make a light(using canvas) go green showing a successful connection went through.
My issue is getting the light to change to green and stay when a connection is made I have tried
my initally also tried to use global then realized the window loop constantly set it back to red
nested while loop--- breaks Tkinker
importing a file I created called variableset which stores variable to set green but since it constantly has a new instance it just sets the variable in tinker back to red.
any help would be greatly appreciated
GUI CODE
import tkinter as tk
from tkinter import *
import connect
import variableset
setting = variableset.setting
window = tk.Tk()
window.title("Network Wizard 1")
window.geometry('300x500')
# setting =tk.IntVar()
# setting.set(variableset.setting)
#serial port pick the right one
serialportlabel = tk.Label(text="COM Number")
serialport = tk.Entry(width= 7)
serialportlabel.pack()
serialport.pack()
#color alerting!! red bad
alert = Canvas(window, width=50, height=20)
alert.pack()
if setting == 0:
a=alert.create_rectangle(5, 0, 50, 50, fill='red')
else:
a=alert.create_rectangle(5, 0, 50, 50, fill='green')
# #connection part woop woop
connection = tk.Button(text="connect", command = lambda: bus())
connection.pack()
# #firmwarecheck
# firmwarecheck = tk.Button(text="Firmware check")
# firmwarecheck.pack()
# #firmware update
# firmwareupdate= tk.Button(text="Firmware update")
# firmwareupdate.pack()
# #software update
# software = tk.Button(text="software update")
# connect.pack()
# #vmlans
# vlanupdate = tk.Button(text="Vlanupdate")
# vlanupdate.pack()
# disconnect = tk.Button(text="disconnect")
# disconnect.pack()
# qut = tk.Button(text="quite")
# qut.pack()
def bus():
global ser
ser = connect.connect(serialport.get())
global setting
setting = variableset.initial
# def firmware(ser):
# if ser.isOpen() == true :
window.mainloop()
Connect code
import serial
import time
import sys
#connect
def connect(com):
ser = serial.Serial(
port = com, #COM
baudrate=9600,
parity='N',
stopbits=1,
bytesize=8,
timeout=8
)
ser.isOpen()
print(ser.name)
#set variables
enter = str.encode('\r\n') #enter
user = str.encode('admin#sytem\r\n') #default user name
pwd = str.encode('\r\n') #defualt password
qut = str.encode('quit\r')
time.sleep(1.0)
# ser.inWaiting()
ser.write(enter) #promt login
time.sleep(0.5)
ser.write(user) #enter user name
time.sleep(0.5)
ser.write(pwd) #enter password
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(str.encode("sytem\r\n"))
time.sleep(0.5)
ser.write(qut)
ser.write(qut)
input_read = ser.read(500)
input_read = input_read.decode("utf-8","ignore")
print(input_read)
ser.close()
return ser
def write(ser):
ser.write(str.encode(''+'\r\n'))
def disconnect(ser):
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.close()
Variableset Code
global setting
setting = 0
def initial():
global setting
setting = 1
return setting
def unset():
global setting
setting = 0
return setting
Any help greatly appreciated

Your if-else statement only runs once, before the setting variable has been set, as far as we can tell. It's not in a loop or a function that gets called from somewhere else. What exactly is the problem you're having with the code that you posted? The issues you described seem to relate to a loop setting it back to red in a different version of the code.
In any event, I suspect that your problem is trying to use infinite loops to keep checking the setting, which prevents the tkinter mainloop() from running, and therefore blocks the GUI from updating. If so, the best method to fix it is probably to change your code for "check the setting and then change the color" into its own function, which gets called for the first time at the end of your connect function. Then, the end of the check setting function should schedule itself to be run after a time delay using the tkinter after() method, which is non-blocking (asnychronous) and allows the mainloop() to keep running. Alternative methods include a separate thread to run the check-setting code, or using the tkinter update() method, but after() is easiest.
Note that your sleep() functions in the connect code will also block the mainloop while they're running. I'm also not clear what you're trying to do with the variableset code; it appears to set the setting to 1 (aka green light) when you call initial(), regardless of the actual status on the serial port.

Related

Tkinter got freeze with threading

I have plan to make a simple test with tkinter which will change the left text with new input.
But it seems threading is not easy as my thought. It still be hang after 2 times of input.
from tkinter import *
import threading
win = Tk()
label1 = Label(win, text="this is a test on the left")
label1.pack(side=LEFT)
label2 = Label(win, text="this is a test on the right")
label2.pack(side=RIGHT)
def set_text():
while(True):
content=input("let enter the substuition:")
label1.config(text = content)
win.after(100, set_text)
setTextthr=threading.Thread(target = set_text)
setTextthr.start()
win.mainloop()
It is very impressed if you can point out why it happened and how to fix.
Thanks
There are some unwritten rules about threading in combination with tkinter.
One of them is not to touch any widget outside of the thread of tkinter.
This line violates this rule.
label1.config(text = content)
In order to do this you can use a tkinter.StingVar which isn't directly in the main event loop of tkinter.
The call from inside the function to itself with after some time will create a new stack of a while-loops on top. Below you find a working example which has its limits. For another approach you can take a look at this or this.
from tkinter import *
import threading
win = Tk()
var = StringVar(value="this is a test on the left")
label1 = Label(win,textvariable=var)
label1.pack(side=LEFT)
label2 = Label(win, text="this is a test on the right")
label2.pack(side=RIGHT)
def set_text():
while(True):
content=input("let enter the substuition:")
var.set(content)
setTextthr=threading.Thread(target = set_text)
setTextthr.start()
win.mainloop()
The usual way to approach getting data from threads in an event-driven program (as it is with tkinter) is to use some update loop (.after) to schedule a check on the data container (queue.Queue or a simple list) that contains data from the thread. Then additionally check if the thread is alive at all, and if it is not you can stop the update loop because there is nothing to update anymore. Also use some flag (threading.Event) to stop the thread loop upon destroying the window. The only issue is that input is used which makes it so that at the end at least the Enter key has to be pressed.
The code example (explanation in code comments):
import tkinter as tk
import threading
import queue
# this is the function that will run in another thread
def ask_input(q): # argument is the queue
# here check if the event is set, if it is
# don't loop anymore, however it will still wait
# for input so that has to be entered and
# then the thread will stop
while not event.is_set():
user_input = input('New content: ')
# put the user entered data into the queue
q.put(user_input)
# the function that will update the label
def update_label(q, t): # pass in the the queue and thread
# this loop simply gets the last item in the queue
# otherwise (not in this case perhaps) it may be a little
# too slow in updating since it only runs every 100 ms
# so it need to catch up
while not q.empty():
# get data from the queue
text = q.get()
# here it is save to update the label since it is the same
# thread where all of the tkinter runs
label.config(text=text)
# check if thread still runs (in this case this specific check
# is unnecessary since the thread is stopped only after
# the main window is closed)
if t.is_alive():
# schedule the next call with the same queue and thread
root.after(100, update_label, q, t)
# the function that initiates the thread
def start_thread():
# create a queue to pass as the argument to the thread and updater
_queue = queue.Queue()
# create and start a thread
thread = threading.Thread(target=ask_input, args=(_queue,))
thread.start()
# start updating the label
update_label(_queue, thread)
# check if the code is run without importing
if __name__ == '__main__':
root = tk.Tk()
# set this attribute so that the window always stays on top
# so that you can immediately see the changes in the label
root.attributes('-topmost', True)
# create the event
event = threading.Event()
# if window is destroyed set event which will break the loop
root.bind('<Destroy>', lambda _: event.set())
label = tk.Label(root)
label.pack()
start_thread()
root.mainloop()

Simulate "button pressed" an rise an event in gpiozero

I try to develop some code on a machine without GPIO. As GPIO library I selected a gpiozero to be able to write my code without access to gpio of raspberry pi.
My problem, I cant get ride of .when_pressed event in the code.
I simulate state change of the button, but the function is not called.
Device.pin_factory = MockFactory()
def interrupt_Event(channel):
print("%s puted in the queue", channel)
InputPin.Device.pin_factory.pin(channel)
InputPin.when_pressed = interrupt_Event
def main():
try:
while True:
time.sleep(1)
InputPins[channel].pull=drive_high()
time.sleep(0.1)
print("State CHANNEL %s" % channel)
print(InputPins[channel].state)
InputPins[channel].drive_low()
Till now I have no Idea what is wrong.
when_pressed function should not have arguments (see 2.7 in https://gpiozero.readthedocs.io/en/stable/recipes.html).
You could define the callback using a loop :Creating functions in a loop
(use channel=channel to force early binding of channel value as in example below)
for channel in channels:
def onpress(channel=channel):
print("%s puted in the queue", channel)
InputPins[channel].when_pressed = onpress
I am not convinced that you are using drive_high and drive_low to simulate the button pushing.
I have a almost identical problem. using Mock pins to develop a Pi program on windows, I find that the callback routines are not called.
from gpiozero.pins.mock import MockFactory
from gpiozero import Device, Button, LED
from time import sleep
Device.pin_factory = MockFactory() # set default pin
factory
btn = Button(16)
# Get a reference to mock pin 16 (used by the button)
btn_pin = Device.pin_factory.pin(16)
def pressed(): # callback
print('pressed')
def released(): # callback
print('released')
btn.when_pressed = pressed
btn.when_released = released # callback routine
for i in range(3): # now try to signal sensor
print('pushing the button')
btn_pin.drive_high
sleep(0.1)
btn_pin.drive_low
sleep(0.2)
The output has no callbacks, just
pushing the button
pushing the button
pushing the button
>>>

Threading in python on a raspberry pi issues

I am trying to run a GUI that spawns threads that perform very basic and not computationally complicated tasks on a raspberry pi 1 and I cannot seem to get the threads to work.
I developed the code on a x86 intel computer and it works perfectly well. The threading commands basically just allow for button presses and listening for serial data concurrently.
def extra_thread_disable():
# Disables buttons that would interfere with data that is currently being sent
while threading.active_count() == 3:
run_file_butt.config(state = 'disabled')
run_butt.config(state = 'disabled')
serial_butt.config(state = 'disabled')
popup_butt.config(state = 'disabled')
homing_butt.config(state = 'disabled')
level_butt.config(state = 'disabled')
zero_button1.config(state = 'disabled')
zero_button2.config(state = 'disabled')
zero_button3.config(state = 'disabled')
else:
run_file_butt.config(state = 'normal')
run_butt.config(state = 'normal')
serial_butt.config(state = 'normal')
popup_butt.config(state = 'normal')
homing_butt.config(state = 'normal')
level_butt.config(state = 'normal')
zero_button1.config(state = 'normal')
zero_button2.config(state = 'normal')
zero_button3.config(state = 'normal')
pass
def thread_data():
# Starts a thread to send data while allowing stop button to be pressed
try:
global t2
t2 = threading.Thread(name='send_line', target = send_data, daemon = True)
t_disable = threading.Thread(name='disable', target = extra_thread_disable, daemon = True)
t2.start()
t_disable.start()
except:
update_textbox("Threading Error: data thread not properly created")
def send_data():
# Sends single motion commands and waits for response to continue
global save_path
global motor_param
vals = get_vals()
try:
data = struct.pack("!llllhhhhhhhh", vals['dist1'], vals['dist2'], vals['dist34'], vals['dist34'], vals['speed1'], vals['speed2'], vals['speed34'], vals['speed34'], vals['accel1'], vals['accel2'], vals['accel34'], vals['accel34'])
try:
ser.write(data)
update_textbox("Running...")
except:
update_textbox("Error: Data not sent")
try:
motor1pos = int(ser.readline())
motor2pos = int(ser.readline())
motor3pos = int(ser.readline())
motor4pos = int(ser.readline())
ready = ser.read(1)
update_textbox("Movement complete")
axis1_current.set(str(reverse_convert(motor1pos, 1)))
axis2_current.set(str(reverse_convert(motor2pos, 2)))
axis3_current.set(str(reverse_convert(motor3pos, 3)))
writetofile()
except:
update_textbox("Error: reading data improperly")
except:
update_textbox("Error: data not sent properly")
pass
The code basically just allows the main GUI thread to allow for a stop button to be pressed and disable all the buttons that could interfere with the sent data. That thread then just waits for the response from an arduino it is connected to. Again this all works flawlessly on a normal computer. I get no errors or warnings in the terminal when run on a raspberry pi but it seems to be blocking. I thought maybe it was just such a slow computer or the infamous GIL. It seems like that might be the reason. If so, should I switch to the multiprocessing library in python? is there a way to get around this? It doesn't work when run in terminal calling python3 and it doesn't work when it was compiled to a static binary using pyinstaller.
The answer is to get better hardware. I changed nothing in my code but instead bought a newer raspberry pi 4 instead of the original raspberry pi and the threading worked as it originally did on my other pc running arch.

Update Tkinter from within an Exscript function

I am writing a Python script that uses Exscript to log into network switches to gather information. I'm using Tkinter to present a graphical interface.
I'm trying to create a canvas window with status so that when the "go" button is clicked, the status windows is made visible and as the script logs into devices a label on the status window is updated (logging into device x of y).
I found the .update() and .update_idletasks(), which works when I enter the initial function (clicking the go button), but updating the label happens 2 functions deep in the Exscript start() function. In there anything I do to my canvas causes the script to hang and I have to force close it.
onhost = 0
hostcount = 0
def go_main():
canvas.itemconfig(statuswindow, state='normal')
canvas.itemconfig(statuslabel_window, state='normal')
canvas.update()
filename = InvEntry.get()
accounts = [Account(unameEntry.get(), pwordEntry.get())]
workbook = xlrd.open_workbook(filename)
sheet = workbook.sheet_by_name("Infrastructure")
global hostcount
hostcount = sheet.nrows
hosts = []
for x in range (1, hostcount):
hosts.append(conproto.get() + "://" + sheet.cell_value(x, 0))
start(accounts, hosts, login_func)
def login_func(job, host, conn):
global onhost
global hostcount
onhost = onhost + 1
newstat = "Gathering MACs on switch %s of %s..." % (onhost, hostcount)
stat.set(newstat)
#canvas.update()
conn.execute('term len 0') #We don't want a pause in the output
conn.execute('sh ver') #Make sure we're a Nexus or Catalyst switch before continuing
r = conn.response
if r != '' and ('nx-os' or 'catalyst') in r.lower():
#Grab the device's hostname:
As soon as it hits the "stat.set(newstat)" line it hangs. What am I doing wrong? Sorry if I slaughtered all the programmer lingo, I'm a network engineer pretending to be a programmer. Thanks!

python auto repeating serial data

Hello I have the bellow code which sends a character to the serial port depending on the state of a check button in tkinter. This works well but I require the character to be repeatability sent over the serial port (every 10 seconds or so) regardless to whether or not the state of the check button has changed. I have tried adding in a while loop but this makes the program crash. Any help would be grand.
(This is a small section of a larger code, as it stands it works as described above)
def setCheckButtonText():
if varCheckButton.get():
ser.write(bytes('L'))
else:
ser.write(bytes('H'))
def setCheckButtonText2():
if varCheckButton2.get():
ser.write(bytes('F'))
else:
ser.write(bytes('O'))
def setCheckButtonText3():
if varCheckButton3.get():
ser.write(bytes('N'))
else:
ser.write(bytes('Y'))
ser = serial.Serial('/dev/ttyUSB0', 9600)
varCheckButton = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton,
command=setCheckButtonText)
tkCheckButton.place(x=60, y=50)
varCheckButton2 = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton2,
command=setCheckButtonText2)
tkCheckButton.place(x=60, y=90)
varCheckButton3 = tk.IntVar()
tkCheckButton = tk.Checkbutton(
mGui,
variable=varCheckButton3,
command=setCheckButtonText3)
tkCheckButton.place(x=60, y=130)
You may want to capsule sending and setting the variables.
In your methods, you can just set the appropriate variables to the characters instead of sending them directly.
In another method, you can use those characters to send them to the serial port. You can use after to call a method after a delay.
def send():
#Send variables here
mGui.after(10000, send)
mGui.after(10000, send)

Categories

Resources