I'm trying to use PySide and pySerial to make a cross-platform app that interacts with the serial port. I originally used Qtimers to poll the serial for data, but this put a large load on the cpu. So I've been attempting to use threads instead.
unfortunately using threads causes either Qt or pySerial to Segfault.
I've tried both python threads and QThreads, same problem, it happens on OSX, windows 8 and Ubuntu 12.04. using python 2.7 and Qt4
This question seemed to have a similar problem, here:
this thread also seems to be a similar problem
below is a small app that recreates the problem
#! /usr/bin/python
import sys
import serial
import threading
from PySide import QtCore, QtGui, QtUiTools
class serial_port_class(object):
def __init__(self, ui):
self.ui = ui
self.connected = False
def __del__(self):
self.disconnect_port()
def connect_port(self):
try:
self.serial_port = serial.Serial("/dev/tty.usbmodem1451", 9600, timeout = None)
self.connected = True
except serial.SerialException, e:
self.connected = False
if self.connected:
self.serial_thread = threading.Thread(target=self.recieve_port, args=([self.ui]))
self.serial_thread.start()
def disconnect_port(self):
self.connected = False
self.serial_thread.join()
self.serial_port.close()
def recieve_port(self, ui):
while self.connected:
try:
text = self.serial_port.read(1)
if text != '':
ui.plain_edit.appendPlainText(text)
except serial.SerialException, e:
connected = False
class KeyPressEater(QtCore.QObject):
def eventFilter(self, obj, event):
global serial_port
if event.type() == QtCore.QEvent.KeyPress:
ch = event.text().encode('utf-8')
if serial_port.connected == True:
serial_port.serial_port.write(ch)
return QtCore.QObject.eventFilter(self, obj, event)
def main():
global serial_port
app = QtGui.QApplication(sys.argv)
ui = QtGui.QWidget()
ui.plain_edit = QtGui.QPlainTextEdit(ui)
keyFilter = KeyPressEater(ui)
ui.plain_edit.installEventFilter(keyFilter)
serial_port = serial_port_class(ui)
serial_port.connect_port()
ui.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
sometimes it takes a bit of data IO to trigger the segfault, sometime a single character will trigger it...
I'm not really sure how I would go about debugging this further....
All help is appreciated!!!!!!
UPDATE
I've recreated the the problem with different hard, and have asked this same question on the Qt community forum
Qt objects are not thread safe, so calling
ui.plain_edit.appendPlainText(text)
from another thread was bad.
a way around this is to use QThreads and the inbuilt thread safe messageing and events system
something like:
class receivePort(QThread):
message = QtCore.Signal(str)
def __init__(self):
self.connected = False
QThread.__init__(self)
def run(self):
while self.connected:
try:
text = self.serial_port.read(1)
if text != '':
self.message.emit(text)
except serial.SerialException, e:
connected = False
and the following to connect it up:
serial_thread = receivePort()
serial_thread.message.connect(write_terminal, QtCore.Qt.QueuedConnection)
serial_thread.start()
where write_terminal has a signature of:
def write_terminal(text):
ui.plain_edit.appendPlainText(text)
Related
I have below code, where I am using OpenCV to start webcam video. Along with that I also have a thread running that pings www.google.com to check network connectivity.
import time
import cv2
import os
from threading import Thread
stopThread = False
def CheckNetwork():
global stopThread
while True:
time.sleep(60)
host = "www.google.com"
response = os.system("ping " + host)
if response == 0:
print("Internet host reachable")
else:
print("Internet host not reachable")
if stopThread:
break
def main():
global stopThread
Thread(target=CheckNetwork).start()
cam = cv2.VideoCapture(0)
while True:
ret_val, img = cam.read()
cv2.imshow('Camera', img)
key = cv2.waitKey(1)
if key == ord('q'):
stopThread = True
break
cv2.destroyAllWindows()
main()
This code is running fine. If I have to close the application by pressing q, OpenCV window closes but application keeps running for 60sec because of the thread and only after 60sec whole application terminates safely.
I wanted to know if this is a good way to close the threads. Is there any better way available which can immediately terminate threads in Python?
There's no native way of stopping a thread in Python. Instead of using a stop flag, you can also use ctypes that calls the Python API to raise an exception in the thread.
import ctypes
# Other imports...
class ThreadWithException(threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
# code here...
def get_id(self):
# returns id of the respective thread
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
res = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id,
ctypes.py_object(SystemExit))
if res > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
print('Exception raise failure')
I am new to python and multiprocessing (Electronics background). I am writing an GUI application to communicate with micro controller over serial port. The interaction with user is through some buttons on the GUI, which sends some command on serial port to the MCU and displays data received from MCU in the same GUI(there can be data from MCU without any command being sent). I am using PyQt5 module and QT designer to design the UI. I converted the UI to py code using uic.compileUi. I am then calling this python file in my main code.
After reading through several documents and stack overflow posts I decided to use to QThreads for the application.
I am however facing issue with serial communication when being used inside thread (It works fine if used in a single main loop without threads).
My code is:
from PyQt5 import uic
from PyQt5.QtWidgets import QApplication,QWidget,QMainWindow #
from PyQt5.QtCore import QCoreApplication, QObject, pyqtSignal, pyqtSlot, QThread
import serial
from GUI import Ui_MainGUIWindow
import sys
serialString = ""
def ClosePort():
serialPort.close()
# print("port closed")
def OpenPort():
serialPort.open()
#print("port open")
def sendFV():
serialPort.write('fv\r'.encode())
def configSerial(port,baudRate):
global serialPort
serialPort=serial.Serial(port = port, baudrate=baudRate, bytesize=8, timeout=None, stopbits=serial.STOPBITS_ONE)
if not serialPort.isOpen():
serialPort.open()
class Worker(QObject):
finished = pyqtSignal()
dataReady = pyqtSignal(['QString'])#[int], ['Qstring']
print("workerinit")
def run(self):
print ("worker run")
while(1):
if(serialPort.in_waiting > 0):
serialString = serialPort.read()
self.dataReady.emit(str(serialString))
self.finished.emit()
class GUI(QWidget):
def __init__(self):
self.obj = Worker()
self.thread = QThread()
self.obj.dataReady.connect(self.onDataReady)
self.obj.moveToThread(self.thread)
self.obj.finished.connect(self.thread.quit)
self.thread.started.connect(self.obj.run)
self.thread.finished.connect(app.exit)
self.thread.start()
self.initUI()
def initUI(self):
#Form, Window = uic.loadUiType("TestGUI.ui")
#print(Form,Window)
#self.window = Window()
#self.form=Form()
self.window = QMainWindow()
self.form = Ui_MainGUIWindow()
self.form.setupUi(self.window)
self.form.FVButton.clicked.connect(sendFV)
print("guiinit")
self.window.show()
def onDataReady(self, serialString):
print("here")
print(serialString.decode('Ascii'))
self.form.plainTextOutput.insertPlainText((serialString.decode('Ascii')))
self.form.plainTextOutput.ensureCursorVisible()
if __name__ == '__main__':
configSerial("COM20",9600)
"""
app = QCoreApplication.instance()
if app is None:
app = QCoreApplication(sys.argv)
"""
app = QApplication(sys.argv)
form=GUI()
app.exec_()
sys.exit(ClosePort())
I get following error on line if(serialPort.in_waiting > 0):
File "C:\Users\...\Anaconda3\lib\site-packages\serial\serialwin32.py", line 259, in in_waiting
raise SerialException("ClearCommError failed ({!r})".format(ctypes.WinError()))
SerialException: ClearCommError failed (OSError(9, 'The handle is invalid.', None, 6))
I found this post talking about similar issue, but do know how exactly to implement the suggested solution in my code.
Also I get "kernel died" error multiple times when I run the code on spyder IDE. I added a check if I am creating multiple instances of QT application but that did not help.Running the code from anaconda command prompt works fine.
I'm trying to develop a multithreaded program with a TUI interface . Basically I have a main Loop deciding what to do and some task (like the TUI or reading data from a queue and processing it ) are running in separate thread .
My TUI is using curses and is a thread derived class that look like this (i removed non essential code for clarity) :
import threading
from time import sleep
import curses
import logging
from curses.textpad import Textbox, rectangle
from datetime import datetime
import re
from curses import panel
import os
import sys
class GenericTUI(threading.Thread):
def __init__(self, logger=logging.getLogger()):
threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=True)
self.keyPressedList = list()
self.alive = True
self._myStdscr = None
self.title = ""
self.logger = logger
self.lock = threading.Lock()
def run(self):
curses.wrapper(self.main)
curses.nocbreak()
curses.echo()
curses.noraw()
sys.exit(0)
def main(self,stdscr):
self._myStdscr = stdscr
self._myStdscr.nodelay(True)
self._myStdscr.keypad(True)
self._myStdscr.box()
while self.alive:
sleep(0.4)
try :
key = self._myStdscr.getkey()
if re.match('[A-Z_\+\-\*/]', key):
self.keyPressedList.append(key)
except Exception as e:
## ignoring no key presssed
pass
try :
with self.lock :
self._myStdscr.clear()
self._myStdscr.addstr(1, 2, str(datetime.now())+" "+ sys.argv[0] +" "+self.title )
### printing other things
self._myStdscr.refresh()
except Exception as e:
self.logger.error(e, exc_info=True)
continue
self._myStdscr.clear()
self._myStdscr.keypad(0)
def getKeyPressed(self):
if self.keyPressedList :
return self.keyPressedList.pop()
else :
return None
def stop(self):
self.alive = False
def updateTitle(self,title):
with self.lock : self.title = title
if __name__ == "__main__":
## the main is used for some test when the lib is called directly
testGUI = GenericTUI()
alive = True
testGUI.logger.addHandler(logging.StreamHandler())
testGUI.logger.setLevel(logging.DEBUG)
testGUI.start()
while alive :
testGUI.updateTitle('title %s'%str(datetime.now() ))
k = testGUI.getKeyPressed()
if k is not None:
if k=='Q' :
alive = False
else :
testGUI.addMessage('unknown key %s'%k , maj=True)
sleep(0.1)
the main loop of my program instantiate and start a genericTUI object and get keypressed from it or set value to display.
But when i quit the program , my terminal is in a funny state even if I used the curses wrapper function or try to reset manually using curses.nocbreak() and others.
I can't figure what I did wrong ? Am i mistaken using curses inside a thread ??
I found the answer but puting it in the comment section makes it hard to read . So I also put it here :
the curses wapper does not like the thread in daemon mode :
so the following code works fine and restore the terminal in a correct state :
class GenericTUI(threading.Thread):
def __init__(self, logger=logging.getLogger()):
threading.Thread.__init__(self,name="genericTUI" + str(os.getpid()), daemon=False)
self.keyPressedList = list()
and in the stop function adding a curses.endwin() helps :
def stop(self):
curses.endwin()
self.alive = False
hope it helps other
#WesModes I use the stop to have a clean way to stop the TUI. The endwin is cleaning the screen .
example :
testGUI=GenericTUI()
alive = True
testGUI.start()
while alive:
try :
k = testGUI.getKeyPressed()
if k is not None:
if k== 'Q':
testGUI.stop()
alive = False
I'm looking to mock a set of REST APIs for some tests. The following main() function works fine (i.e. it returns {"some-data": 1234} as json to the browser when I GET localhost:8099). The issue is it blocks the main thread:
from gevent import monkey, sleep, pywsgi
monkey.patch_all()
import flask
from flask_restful import reqparse, abort, Api, Resource
import queue
import sys
import threading
STUFFS = {"some-data": 1234}
class Stuff(Resource):
def get(self):
return flask.jsonify(STUFFS)
class ControlThread(threading.Thread):
def __init__(self, http_server, stop_event):
threading.Thread.__init__(self)
self.stop_event = stop_event
self.http_server = http_server
self.running = False
def run(self):
try:
while not self.stop_event.is_set():
if not self.running:
self.http_server.start()
self.running = True
sleep(0.001)
except (KeyboardInterrupt, SystemExit):
pass
self.http_server.stop()
class StuffMock:
def __init__(self, port, name=None):
if name is None:
name = __name__
self.app = flask.Flask(name)
self.api = Api(self.app)
self.api.add_resource(Stuff, "/stuff/")
self.stop_event = threading.Event()
self.http_server = pywsgi.WSGIServer(('', port), self.app)
self.serving_thread = ControlThread(self.http_server,
self.stop_event)
self.serving_thread.daemon = True
def start(self):
self.serving_thread.start()
def stop(self):
self.stop_event.set()
self.serving_thread.join()
def main():
mocker = StuffMock(8099)
mocker.start()
try:
while True:
sleep(0.01)
except (KeyboardInterrupt, SystemExit):
mocker.stop()
sys.exit()
if __name__ == "__main__":
main()
Without the sleep() call in the while loop above, nothing resolves. Here is a more succinct usage to demonstrate:
import time
from stuff_mock import StuffMock
mocker = StuffMock(8099)
mocker.start()
while True:
user_text = input("let's do some work on the main thread: ")
# will only resolve the GET request after user input
# (i.e. when the main thread executes this sleep call)
time.sleep(0.1)
if user_text == "q":
break
mocker.stop()
The gevent threading module seems to work differently from the core one. Does anyone have any tips or ideas about what's going on under the hood?
Found that if I switch out threading for multiprocessing (and threading.Thread for multiprocessing.Process), everything works as expected, and I can spin up arbitrary numbers of mockers without blocking.
My tkinter application runs fine, but to implement dbus functionality I had to use gobject. Got it working and all, except that, running both tkinter's and gobject's mainloops makes the "close window" button from the standard window manager ('x' button in the window interface) not to work. :/ Everything else works fine, including resizing, minimizing/maximizing, restoring, and moving the window.
Any help is appreciated,
Thanks,
Little code snippet:
import dbus
from dbus.service import Object
from dbus.mainloop.glib import DBusGMainLoop
class TBOPlayerDBusInterface (Object):
tboplayer_instance = None
def __init__(self, tboplayer_instance):
self.tboplayer_instance = tboplayer_instance
dbus_loop = DBusGMainLoop()
bus_name = dbus.service.BusName("org.tboplayer.TBOPlayer", bus = dbus.SessionBus(mainloop = dbus_loop))
Object.__init__(self, bus_name, "/org/tboplayer/TBOPlayer")
#dbus.service.method('org.tboplayer.TBOPlayer', in_signature = 'as')
def openFiles(self, files):
self.tboplayer_instance._add_files(files)
# ***************************************
# MAIN
# ***************************************
if __name__ == "__main__":
datestring=" 28 Fev 2017"
dbusif_tboplayer = None
try:
bus = dbus.SessionBus()
bus_object = bus.get_object("org.tboplayer.TBOPlayer", "/org/tboplayer>/TBOPlayer", introspect = False)
dbusif_tboplayer = dbus.Interface(bus_object, "org.tboplayer.TBOPlayer")
except Exception, e:
print e
if dbusif_tboplayer is None:
tk.CallWrapper = ExceptionCatcher
bplayer = TBOPlayer()
TBOPlayerDBusInterface(bplayer)
def refresh_player():
bplayer.root.update()
return True
def run_gobject():
gobject.MainLoop().run()
gobject.idle_add(refresh_player)
bplayer.root.after(100, run_gobject)
bplayer.root.mainloop()
else:
if len(sys.argv[1:]) > 0:
dbusif_tboplayer.openFiles(sys.argv[1:])
exit()
I found the problem. For some reason, using tkinter's and gobject's mainloops interferes with the behavior of the WM_DELETE_WINDOW event, which I was using for saving some data before closing the program. Solved the problem by binding to the Configure event instead. And now the main method is as follows:
if __name__ == "__main__":
datestring=" 28 Fev 2017"
dbusif_tboplayer = None
try:
bus = dbus.SessionBus()
bus_object = bus.get_object("org.tboplayer.TBOPlayer", "/org/tboplayer/TBOPlayer", introspect = False)
dbusif_tboplayer = dbus.Interface(bus_object, "org.tboplayer.TBOPlayer")
except Exception, e:
print e
if dbusif_tboplayer is None:
tk.CallWrapper = ExceptionCatcher
bplayer = TBOPlayer()
TBOPlayerDBusInterface(bplayer)
gobject_loop = gobject.MainLoop()
def refresh_player():
try:
bplayer.root.update()
return True
except Exception, e:
bplayer.quit_omx()
gobject_loop.quit()
def run_gobject():
gobject_loop.run()
gobject.idle_add(refresh_player)
bplayer.root.after(100, run_gobject)
bplayer.root.mainloop()
else:
if len(sys.argv[1:]) > 0:
dbusif_tboplayer.openFiles(sys.argv[1:])
exit()