This is the definition which is used to update the labels in the GUI:
def updateV(self, event):
""""""
global v
ser = serial.Serial( port='COM3', baudrate=9600)
x = ser.read() # read one byte
ser.close()
print x
if v>3:
self.labelOne.SetBackgroundColour('red')
self.labelOne.SetLabel('Battery Voltage : ' + x)
else:
self.labelOne.SetBackgroundColour('white')
self.labelOne.SetLabel('Battery Voltage : ' + str(v))
self.Refresh()
This is the simple arduino code i have been using:
int a;
void setup() {
Serial.begin(9600);// put your setup code here, to run once:
}
void loop() {
a=5;
Serial.println(a);
delay(10);
}
I have been using this definition to update my labels for my GUI. I recently started to set up serial communication on my GUI using that code. Logically using the mainloop() of the wx library, i thought i could update the 'x' value and get it printed on the GUI. But all the GUI window shows in 0.0 even though the python console prints 5 regularly. Please help! I am pretty new to this.
Your issue is that ser.read() will block. Even if you tweak the timeout of your serial.Serial instance, it still will keep the GUI busy. In that situation I do not know a method to "force" a refresh/wx.Yield(), it simply will not work. The standard solution for blocking calls is to spin up a thread
or poll regularily (e. g. with wx.Timer). However, I was only able to make threading work. The example is based on wxTerminal in pyserial.
# -*- coding: utf-8 -*-
import wx
import serial
from threading import Thread
ARDUINO_NEWLINE = '\r\n'
class serial_reader(object):
def __init__(self, callback=None):
"""Creates serial reader.
:param callback: callable, gets called when byte on serial arrives.
"""
self.callback = callback
self.thread = None
# Signal if serial is alive and should be read
self.alive = False
def start_reader(self, serial_cfg):
"""Start the receiver thread.
:param serial_cfg: dictionary, gets unpacked to parameters for :class:`serial.Serial`
"""
self.ser_cfg = serial_cfg
self.serial = serial.Serial(**serial_cfg)
# set != None so it will not block for longer than timeout on shutdown
self.serial.timeout = 0.1
self.alive = True
self.thread = Thread(target=self.serial_read)
self.thread.daemon = True
self.thread.start()
def stop_reader(self):
"""Stop the receiver thread, wait util it is finished."""
if self.thread is not None:
# signal no more reads
self.alive = False
# wait until thread has finished
self.thread.join()
self.thread = None
# cleanup
self.serial.close()
def serial_read(self):
"""Thread that handles the incoming traffic."""
while self.alive:
try:
text = self.serial.read()
if text and self.callback:
# return value to main loop in thread-safe manner
wx.CallAfter(self.callback, text)
except serial.serialutil.SerialException:
# will happen when Windows goes in sleep mode
print 'serial.serialutil.SerialException'
class ser_frm(wx.Frame):
def __init__(self, *args, **kwds):
wx.Frame.__init__(self, *args, **kwds)
self.txt = wx.TextCtrl(self, -1, '', style=wx.TE_MULTILINE)
class serial_controller(object):
def __init__(self, app):
self.app = app
# buffer for serial data
self.ser_buf = ''
self.frm = ser_frm(None, -1, 'testfrm')
# setup serial configuration
self.serial_cfg = {'port': 'COM4', 'baudrate': 9600}
# When parameter dsrdtr is set to True, the Arduino
# will not reset on serial open, for details see
# http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection
self.serial_cfg['dsrdtr'] = True
self.ser_rd = serial_reader(callback=self.on_serial)
tit = 'Arduino on port {port} at baudrate {baudrate}'.format(**self.serial_cfg)
self.frm.SetTitle(tit)
self.ser_rd.start_reader(self.serial_cfg)
self.frm.Show()
self.frm.Bind(wx.EVT_CLOSE, self.on_close)
def on_close(self, evt):
"""Shutdown serial read thread before closing."""
if self.ser_rd.alive:
self.ser_rd.stop_reader()
evt.Skip()
def on_serial(self, text):
"""Handle input from the serial port."""
self.ser_buf += text
if self.ser_buf.endswith(ARDUINO_NEWLINE):
if self.frm.txt.GetInsertionPoint() > 1000:
self.frm.txt.SetValue('')
self.frm.txt.AppendText(self.ser_buf)
self.ser_buf = ''
if __name__ == "__main__":
app = wx.App(redirect=False)
serialctr = serial_controller(app)
app.MainLoop()
EDIT: It is not necessary to tinker with DSR/DTR on Arduinos with USB on chip (e.g. the Arduino micro), so delete the line
self.serial_cfg['dsrdtr'] = True
and it will still work properly.
Related
I have the following class that is used to run a third party command line tool which I have no control over.
I run this ina Qthread in a PyQt Gui.
I turn the gui into an EXE using Pyinstaller
Problems are more prevalent when it is an EXE
class CLI_Interface:
def process_f(self, command, bsize=4096):
self.kill_process(CLI_TOOL)
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_HIDE
p = Popen(command, stdout=PIPE, stderr=PIPE,
startupinfo=startupinfo, bufsize=bsize, universal_newlines=True)
try:
out, err = p.communicate(timeout=120)
except TimeoutExpired:
p.kill()
out, err = p.communicate()
return out.split(), err.split()
def kill_process(self, proc):
# Check process is running, Kill it if it is,
# return False if not.
# uses its own popen for Stderr >> stdout
# If we use the self.process_f method, it will create an infinite loop
startupinfo = STARTUPINFO()
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
startupinfo.wShowWindow = SW_HIDE
try:
kill_proc = Popen("TaskKill /IM {} /T /F".format(proc), stdout=PIPE, stderr=STDOUT,
startupinfo=startupinfo, universal_newlines=True).communicate()[0]
if 'ERROR' not in kill_proc.split():
return True # Process Killed
else:
self.kill_process(proc)
except Exception as e:
return False
def download_data(self, code):
""" download data from the device based on a 5 digit code """
command = '"{}" -l {},{} {}'.format(CLI_TOOL_PATH,
code[0], code[2], code[1])
try:
p = self.process_f(command)
proc, err = p[0], p[1]
try:
if err[-2] == '-p':
return False
return True
except IndexError:
if not proc:
return False # This means there is no data but the file is still saved!!
pass
return True
except Exception as e:
return False
def ....
def ....
def ....
Thread:
class GetDataThread(QThread):
taskFinished = pyqtSignal()
notConnected = pyqtSignal()
def __init__(self, f, parent=None):
super(GetDataThread, self).__init__(parent)
self.f = f
def run(self):
is_dongle_connected()
DD = cli.download_data(self.f)
if not DD:
self.notConnected.emit()
else:
self.taskFinished.emit()
I either get a done! or error - This is normal when running from the command line.
Sometimes I get an empty list returned and I put this back into a recursive loop after killing the program.
However, it does not seem to restart properly and the problem continues - it gets stuck in a loop of nothing!.
Meanwhile, the csv files the cli tool produces are created as normal yet I have no data from stdout / stderr
Looking at processes the conhost and the cli tool are destroyed no problem.
The gui will continue to fail (until I unplug and plug in the dongle and / or restart the program / computer.
When I open the CLI and run the same command, it works fine or throws an error (which I catch in the program no problem)
I have tried setting a buffer as some files generated can reach 2.4mb
I tried setting a higher timeout to allow for it to finish.
There does not seem to be a correlation with file size though and it can get stuck at any size.
The flow is like so:
Gui >> CLI >> Dongle >> Sensor
Running on Windows 10
How can I make the connection more solid or debug what processes might still be lingering around and stopping this?
Is it blocking?
Is it a pipe buffer overflow? - If so How do I determine the correct bufsize?
Is it something to do with PyQt and Python Subprocess or Pyinstaller?
Would it be better to use QProcess instead of Subprocess?
Thanks in advance!
The following code is an attempt of mine at a voice recognition program. The voice recognition works fine and can understand me, but I encountered a problem at certain points in the program the code would sort of freeze, or hang but without an error.
To get around this I attempted to add a timer using multi-threading which should begin at a = True and after 3 seconds the program would automatically close. If the recognising worked perfectly the a = False should stop the timer before it closed the program. This obviously hasn't worked or I wouldn't be here.
I added a few print statements here and there so I could visually see where the code was when running, and I saw the the code for the timer begins however the code for voice recognition does not.
import speech_recognition as sr
r = sr.Recognizer()
import os, threading, time, sys
import subprocess as sp
print("Voice Recognition Software\n\n***********************************\n")
class myThread (threading.Thread):
def run():
print("Checking")
while True:
if a == True:
if a == False:
continue
for x in range(3):
time.sleep(1)
if a == False:
break
sys.exit()
def program():
while True:
print("voice recog has begun")
r.energy_threshold = 8000
t = None
with sr.Microphone() as source:
print (">")
a = True
audio = r.listen(source)
a = False
try:
a = True
print("Processing...")
t = r.recognize_google(audio)
a = False
print (": " + t)
except sr.UnknownValueError:
print("Unknown input")
continue
except sr.RequestError as e:
print("An error occured at GAPI\nA common cause is lack of internet connection")
continue
if "open" in t:
t = t.replace("open","")
t = t.replace(" ","")
t = t + ".exe"
print (t)
for a,d,f in os.walk("C:\\"):
for files in f:
if files == t.lower() or files == t.capitalize() or files == t.upper():
pat = os.path.join(a,files)
print (pat)
sp.call([pat])
success = True
if success == True:
continue
a = False
success = False
thread1 = myThread.run()
thread2 = myThread.program()
thread1.start()
thread2.start()
EDIT:
I see some mistakes of my own here like indentation of the def function but even after fixing what I see it doesn't work as intended.
I have a chat window for the client portion of a chat application that uses Tkinter for the GUI:
import socket
import select
import time
from threading import Thread
from multiprocessing import Process
import sys
from tkinter import *
HOST = "localhost"
PORT = 5678
client_socket = socket.socket()
client_socket.settimeout(2)
try:
client_socket.connect((HOST, PORT))
except:
print("Connection failed.")
sys.exit()
print("Connected to [" + str(HOST) + "," + str(PORT) + "] successfully")
class ChatWindow:
def __init__(self):
form = Tk()
form.minsize(200, 200)
form.resizable(0, 0)
form.title("Chat")
box = Entry(form)
form.bind("<Return>", lambda x: self.sendmessage(self.textbox.get()))
area = Text(form, width=20, height=10)
area.config(state=DISABLED)
area.grid(row=0, column=1, padx=5, pady=5, sticky=W)
box.grid(row=1, column=1, padx=5, pady=5, sticky=W)
self.textbox = box
self.textarea = area
p1 = Process(target=updating)
p1.start()
p2 = Process(target=tryrecvmessage)
p2.start()
def addchat(self, msg, clear=False):
self.textarea.config(state=NORMAL)
self.textarea.insert(END, msg + "\n")
if clear:
# Option to clear text in box on adding
self.textbox.delete(0, END)
self.textarea.see(END)
self.textarea.config(state=DISABLED)
def sendmessage(self, msg):
data = str.encode(msg)
client_socket.send(data)
self.addchat("<You> " + msg, True)
def updating(self):
while True:
form.update()
form.update_idletasks()
time.sleep(0.01)
def tryrecvmessage(self):
while True:
read_sockets, write_sockets, error_sockets = select.select([client_socket], [], [])
for sock in read_sockets:
data = sock.recv(4096)
if data:
self.addchat(data)
else:
self.addchat("Disconnected...")
sys.exit()
if __name__ == "__main__":
window = ChatWindow()
I want the updating() function and the tryrecvmessage() function to run simultaneously, so that the GUI continues to update while the client still receives messages from the server. I've tried using the threading module as well, but I need to have the threads created below where the other functions are defined, but the other functions need to be defined below __init__(). What do I do?
You can attach the functions to the Tk event loop using the after method, as I explained in this question. Essentially the syntax for after goes like this:
after(ms, command = [function object])
What it does is attach the function object passed in as the command argument to the Tk event loop, repeating it each time ms milliseconds has passed.
One caveat here: you would want to remove the while True from the functions as after would be constantly repeating them anyway.
I´m trying to connect to a mosquitto broker using autobahn python.
If I use sub.py that has this code inside:
import mosquitto
def on_connect(mosq, obj, rc):
print("rc: "+str(rc))
def on_message(mosq, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def on_publish(mosq, obj, mid):
print("mid: "+str(mid))
def on_subscribe(mosq, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def on_log(mosq, obj, level, string):
print(string)
mqttc = mosquitto.Mosquitto()
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
mqttc.on_log = on_log
mqttc.connect("localhost", 1883, 60)
mqttc.subscribe("control", 0)
rc = 0
while rc == 0:
rc = mqttc.loop()
print("rc: "+str(rc))
it is connecting to the broker and retrieving all messages that a client publish to control channel.
I´d like to push somehow those messages using websockets to a webpage for that I am trying to use websocket autobahn py and modify the example from here http://autobahn.ws/python/getstarted#yourfirstserver
My code is like this
import sys
import mosquitto
from twisted.internet import reactor
from twisted.python import log
from autobahn.websocket import WebSocketServerFactory, \
WebSocketServerProtocol, \
listenWS
class EchoServerProtocol(WebSocketServerProtocol):
def on_connect(mosq, obj, rc):
print("rc: "+str(rc))
def on_message(mosq, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def onMessage(self, msg, binary):
print "sending echo:", msg
self.sendMessage(msg, binary)
def on_publish(mosq, obj, mid):
print("mid: "+str(mid))
def on_subscribe(mosq, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
mqttc = mosquitto.Mosquitto()
mqttc.on_message = on_message
mqttc.on_connect = on_connect
mqttc.on_publish = on_publish
mqttc.on_subscribe = on_subscribe
# Uncomment to enable debug messages
mqttc.on_log = on_log
mqttc.connect("192.168.2.109", 1883, 60)
mqttc.subscribe("control", 0)
rc = 0
while rc == 0:
rc = mqttc.loop()
print("rc: "+str(rc))
if __name__ == '__main__':
log.startLogging(sys.stdout)
factory = WebSocketServerFactory("ws://192.168.2.109:8899", debug = TRUE)
factory.protocol = EchoServerProtocol
listenWS(factory)
reactor.run()
but I receive this error when I try to run it:
root#Ubuntu:~/authobahn# python myserver.py Traceback (most recent
call last): File "myserver.py", line 30, in
mqttc.on_message = on_message NameError: name 'on_message' is not defined
The problem is that you have defined your on_message() function inside the EchoServerProtocol class. This means it is not visible to the global mqttc variable. You probably want to put all of the mqttc code inside your class as well, although it depends on what you actually want to achieve.
You could do something like the code below:
import mosquitto
class MyMQTTClass:
def __init__(self, clientid=None):
self._mqttc = mosquitto.Mosquitto()
self._mqttc.on_message = self.mqtt_on_message
self._mqttc.on_connect = self.mqtt_on_connect
self._mqttc.on_publish = self.mqtt_on_publish
self._mqttc.on_subscribe = self.mqtt_on_subscribe
def mqtt_on_connect(self, mosq, obj, rc):
print("rc: "+str(rc))
def mqtt_on_message(self, mosq, obj, msg):
print(msg.topic+" "+str(msg.qos)+" "+str(msg.payload))
def mqtt_on_publish(self, mosq, obj, mid):
print("mid: "+str(mid))
def mqtt_on_subscribe(self, mosq, obj, mid, granted_qos):
print("Subscribed: "+str(mid)+" "+str(granted_qos))
def mqtt_on_log(self, mosq, obj, level, string):
print(string)
def run(self):
self._mqttc.connect("test.mosquitto.org", 1883, 60)
self._mqttc.subscribe("$SYS/#", 0)
rc = 0
while rc == 0:
rc = self._mqttc.loop()
return rc
# If you want to use a specific client id, use
# mqttc = MyMQTTClass("client-id")
# but note that the client id must be unique on the broker. Leaving the client
# id parameter empty will generate a random id for you.
mqttc = MyMQTTClass()
rc = mqttc.run()
print("rc: "+str(rc))
I cannot figure out why SvcShutdown() is not being called in my win32 service when Windows shuts down. Everything else works perfectly (stopping, pause/continue etc). After scouring the 'Net for hours, I find nothing.
Any help would be very, VERY appreciated!!
Thanks in advance,
James
import win32serviceutil
import servicemanager
import win32service
import win32event
import win32api
import datetime
LOGINFO = 0
LOGWARNING = 1
LOGERROR = 2
class MyService(win32serviceutil.ServiceFramework):
_svc_name_ = 'MyService'
_svc_display_name_ = 'MyService service'
def __init__(self, *args):
win32serviceutil.ServiceFramework.__init__(self, *args)
# Create events for service stop, pause & continue
# CreateEvent(securityAttrs, bManualReset, bInitialState, name)
self.evStop = win32event.CreateEvent(None, 0, 0, None)
self.evPause = win32event.CreateEvent(None, 0, 0, None)
self.evContinue = win32event.CreateEvent(None, 0, 0, None)
# Create event list for WaitForMultipleObjects()
self.evhandles = self.evStop, self.evPause, self.evContinue
# sigStatus must be this range for a valid SCM event
self.validSignals = range(win32event.WAIT_OBJECT_0,
win32event.MAXIMUM_WAIT_OBJECTS)
# Signal ID returned from WFMO() else None
self.sigStatus = None
# Service run state. False means pausing/paused or stopping
self.runState = True
def logEvent(self, msg, logtype=LOGINFO, logcategory=None):
import servicemanager
if logtype == LOGINFO:
servicemanager.LogInfoMsg(str(msg))
elif logtype == LOGWARNING:
servicemanager.LogWarningMsg(str(msg))
elif logtype == LOGERROR:
servicemanager.LogErrorMsg(str(msg))
def sleep(self, sec):
'''A delay method sympathetic to SCM notifications.'''
while sec > 0:
# SCM event has taken place?
if self.notificationFromSCM():
break
win32api.Sleep(1000)
sec = sec -1
def notificationFromSCM(self):
'''Returns True if SCM notification(s) have taken place.
sigStatus has the value.
Note: that calls to WaitForMultipleObjects() only returns the event
status ONCE, after which it's reset (ie. calling it may return
WAIT_OBJECT_0 and an immediate subsequent call will yield WAIT_TIMEOUT
or similar.'''
if self.sigStatus is not None:
# Still have a live SCM event to process, so exit
return True
# WaitForMultipleObjects(handles, bWaitAll, dwMilliseconds)
self.sigStatus = win32event.WaitForMultipleObjects(self.evhandles, 0, 0)
if self.sigStatus in self.validSignals:
return True
else:
# Timeout signal or similar, so MUST reset sigStatus
self.sigStatus = None
return False
def SvcDoRun(self):
self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
self.logEvent('Starting {0} Service...'.format(self._svc_display_name_))
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
self.logEvent('{0} Service started.'.format(self._svc_display_name_))
while True:
if self.runState:
try:
# Insert service work activity here...
self.logEvent('Working: {0}'.format(datetime.datetime.now()))
self.sleep(10)
except Exception as x:
self.logEvent('Exception : {0}'.format(x), LOGERROR)
else:
self.sleep(30)
# SCM notification?
if self.notificationFromSCM():
if self.sigStatus == self.evhandles.index(self.evStop):
# STOP event
self.logEvent('Stopping {0} Service...'.format(self._svc_display_name_))
break
elif self.sigStatus == self.evhandles.index(self.evPause):
# PAUSE event
self.logEvent('Pausing {0} Service...'.format(self._svc_display_name_))
self.runState = False
# Other cleanup code here...
self.logEvent('{0} Service paused.'.format(self._svc_display_name_))
self.ReportServiceStatus(win32service.SERVICE_PAUSED)
elif self.sigStatus == self.evhandles.index(self.evContinue):
# CONTINUE event
self.logEvent('Resuming {0} service...'.format(self._svc_display_name_))
self.runState = True
# Reset pause & continue to non-signaled state
win32event.ResetEvent(self.evPause)
win32event.ResetEvent(self.evContinue)
# Other cleanup code here...
self.logEvent('{0} Service started.'.format(self._svc_display_name_))
self.ReportServiceStatus(win32service.SERVICE_RUNNING)
# Clear signal flag
self.sigStatus = None
# If we get here, then service has been stopped/shutdown
self.logEvent('{0} Service stopped.'.format(self._svc_display_name_))
self.ReportServiceStatus(win32service.SERVICE_STOPPED)
def SvcStop(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
# Signal STOP event
win32event.SetEvent(self.evStop)
def SvcPause(self):
self.ReportServiceStatus(win32service.SERVICE_PAUSE_PENDING)
# Signal PAUSE event
win32event.SetEvent(self.evPause)
def SvcContinue(self):
self.ReportServiceStatus(win32service.SERVICE_CONTINUE_PENDING)
# Signal CONTINUE event
win32event.SetEvent(self.evContinue)
def SvcShutdown(self):
self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
self.logEvent('**SvcShutdown event**')
# Shutdown code here...
win32event.SetEvent(self.evStop)
Have a look at this:
[python-win32] win32 service not receiving shutdown notification on reboot