running 4 functions periodically in the background without freezing the GUI - python-3.x

I made a pyqt5 script which uses python3.6
my scripts takes input from serial device and updates the GUI and the remote database and also take remote input from web browser and make necessary changes to the serial device and update the GUI accordingly.
I am using QTimer to run the 4 functions/methods in the background to achieve this and it's working. But the problem is that after receiving a manual input or remote input (from web browser) it takes almost 15-20 seconds to implement the necessary changes in the serial device and the GUI and it makes the GUI almost unresponsive (it responds after almost 15-20 seconds).
I tried QThread but it always results in multiple access on the serial port at the same time causing the script to crash. Please, help me in this regard.
from PyQt5.QtCore import QTimer, QThread
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.uic import loadUiType
import os
import sys
import time
import serial
import MySQLdb
from pathlib import Path
import json
ui, _ = loadUiType('myapplication.ui')
class MainApp(QMainWindow, ui):
def __init__(self):
QMainWindow.__init__(self)
self.setupUi(self)
def new_method1(self):
print("This is new_method1")
def new_method2(self):
print("This is new_method2")
def new_method3(self):
print("This is new_method3")
def new_method4(self):
print("This is new_method4")
def continuous_calling(self):
self.timer = QTimer()
self.timer.timeout.connect(self.new_method1)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method2)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method3)
self.timer.start(1000)
self.timer.timeout.connect(self.new_method4)
self.timer.start(1000)
def main():
app = QApplication(sys.argv)
window = MainApp()
window.move(0, -30)
window.show()
window.continuous_calling()
app.exec_()
if __name__ == '__main__':
main()
Here, i want to run new_method1, new_method2, new_method3 & new_method4 methods continuously in the background as long as the GUI is not exiting without freezing the GUI. Thanks.
additional information::
This is an example of other functions that manages direct user inputs from GUI(button clicks from the GUI). And these functions don't run in the background repeatedly. These functions are triggered only when buttons(in the GUI) are clicked.
def ser_conn_on_zero(self):
self.ser = serial.Serial(
port=self.comm_port,
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_EVEN,
stopbits=serial.STOPBITS_ONE,
timeout=1)
if self.ser.isOpen():
print("Open: " + self.ser.name)
self.ser.flushInput()
self.ser.flushOutput()
self.ser.close()
self.ser.open()
print('Connected to :' + self.comm_port)
print("Turning ON Relay Zero")
self.ser.write(b'\x46\x6D\x45\x63\x73\x4B\x6F\x6C\x00\x05\x00\x01\x00\x01\x00\r\n')
output = self.ser.readline().decode('iso-8859-1')
print("Serial Response: " + output)
self.ser.close()
print("Serial connection Closed for Relay Zero")
conn = MySQLdb.connect("XX.XX.XX.XX", "User2", "PASSWORD", "TestSite_001", use_unicode=True, charset="utf8")
cursor = conn.cursor()
try:
cursor.execute("update some_table set chn = %s where id = %s", (1, 1))
conn.commit()
except MySQLdb.Error as e:
print("ser_conn_on_zero: couldn't update the remote database : ", str(e))
cursor.close()
conn.close()

Related

How to safely terminate a thread in Python

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

Python Serial "ClearCommError failed (OSError(9, 'The handle is invalid.', None, 6))

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.

Flask versus PyQt5 - Flask controls program flow?

Flask appears to prevent PyQt5 UI from updating.
The respective code works properly for either PyQt5 or Flask - but not together. I understand that it may need to do with the way threading is set up.
Any assistance would be greatly appreciated. TIA.
`
import sys
import serial
import threading
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from flask import Flask, render_template, request, redirect, url_for
app1 = Flask(__name__)
ser = serial.Serial ("/dev/ttyS0", 57600,timeout=3) #Open port with baud rate
count=0
temp = []
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
global count
count = 1
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('PyQt5 vs Flask')
self.lbl1 = QLabel('Count '+str(count), self)
self.lbl1.move(100, 50)
self.show()
threading.Timer(5,self.refresh).start()
def refresh(self):
global count
count +=1
print("UI ",count)
self.lbl1.setText('Count'+str(count))
threading.Timer(5,self.refresh).start()
def get_uart():
global temp
if ser.inWaiting()>0:
temp =[str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]
print(temp)
threading.Timer(1,get_uart).start()
#app1.route("/")
def index():
global temp
templateData = {'temp1' : temp[1] ,'temp2' : temp[2]}
return render_template('index.html',**templateData)
if __name__ == "__main__":
app = QApplication(sys.argv)
pyqt5 = Example()
threading.Timer(1,get_uart).start()
ser.flushInput()
#app1.run(host='0.0.0.0',threaded=True, port=5000) # ,debug=True)
sys.exit(app.exec_())
`
Need to have a UI to control the data analysis to be displayed on Website.
[SOLVED]
All Flask parameters can be defined as:
port = int(os.environ.get('PORT', local_port))
kwargs = {'host': '127.0.0.1', 'port': port , 'threaded' : True, 'use_reloader': False, 'debug':False}
threading.Thread(target=app.run, daemon = True, kwargs=kwargs).start()
and Flask will NOT Block and run with the parameters defined in kwargs.
The better way deal with (possibly waiting) processes, is to use Qt's own threads.
In this example I've created a QObject subclass that does all processing and eventually sends a signal whenever the condition is valid. I can't install flask right now, so I've not tested the whole code, but you'll get the idea.
The trick is to use a "worker" QObject that does the processing. Once the object is created is moved to a new QThread, where it does all its processing without blocking the event loop (thus, the GUI).
You can also create other signals for that object and connect to your slots (which might also be standard python functions/methods outside the QtApplication) which will be called whenever necessary.
class Counter(QtCore.QObject):
changed = QtCore.pyqtSignal(str)
def __init__(self):
super().__init__()
self.count = 0
def run(self):
while True:
self.thread().sleep(1)
if ser.inWaiting() > 0:
self.changed.emit('{}: {}'.format(self.count, [str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]))
self.count += 1
class Example(QtWidgets.QWidget):
def __init__(self):
super().__init__()
self.counter = Counter()
self.counterThread = QtCore.QThread()
self.counter.moveToThread(self.counterThread)
self.counterThread.started.connect(self.counter.run)
self.initUI()
def initUI(self):
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('PyQt5 vs Flask')
self.lbl1 = QtWidgets.QLabel('Count {}'.format(self.counter.count), self)
self.lbl1.move(100, 50)
self.counter.changed.connect(self.lbl1.setText)
self.counterThread.start()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
pyqt5 = Example()
pyqt5.show()
I think the problem stems from how Flask is activated. If the app.run command is given any parameters (even if in a Thread), then it blocks other commands.
The only way I was able to make Flask and PyQt5 work at the same time, was to activate Flask in a dedicated Thread WITHOUT any parameters - SEE BELOW for the various combinations.
Question: Is this a Flask/Python Bug or Feature or some other explanation related to Development vs Production deployment??
In any case, I would like any help with finding a way to deploy flask in a Port other than 5000 - WITHOUT Flask Blocking other code.
import sys
import serial
import threading
import atexit
from PyQt5.QtWidgets import QWidget, QLabel, QApplication
from flask import Flask, render_template, request, redirect, url_for
ser = serial.Serial ("/dev/ttyS0", 57600,timeout=3) #Open port with baud rate
app = Flask(__name__)
count=0
temp = []
class Example(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
global count
count = 1
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('PyQt5 vs Flask')
self.lbl1 = QLabel("Count "+str(count)+" ", self)
self.lbl1.move(100, 50)
self.show()
threading.Timer(5,self.refresh).start()
def refresh(self):
global count
global str_data
count +=1
self.lbl1.setText("Count "+str(count)+" ")
threading.Timer(0.5,self.refresh).start()
def get_uart():
global temp
if ser.inWaiting()>0:
temp =[str(float(x.decode('utf-8'))) for x in ser.read_until().split(b',')]
print(temp)
threading.Timer(1,get_uart).start()
#app.route("/")
def blank():
global count
data="Count "+str(count)
return data
if __name__ == "__main__":
threading.Timer(5,get_uart).start()
#app.run ## Does not block further execution. Website IS NOT available
#app.run() ## Blocks further execution. Website available at port 5000 without Refresh value
#app.run(port=5123) ## Blocks further execution. Website available at port 5123 without Refresh value
#app.run(threaded=True) ## Blocks further execution. Website available at port 5000 without Refresh value
#threading.Thread(target=app.run()).start() ## Blocks further execution. Website available at port 5000 without Refresh value
#threading.Thread(target=app.run(port=5123)).start() ## Blocks further execution. Website available at port 5123 without Refresh value
#threading.Thread(target=app.run(threaded=True)).start() ## Blocks further execution. Website available at port 5000 without Refresh value
threading.Thread(target=app.run).start() ## Flask DOES NOT block. Website is available at port 5000 with Refresh value
print("Flask does not block")
app1 = QApplication(sys.argv)
pyqt5 = Example()
sys.exit(app1.exec_())

tkinter window not closing properly

My tkinter app windows are not closing properly. I am using python 3.6.6 with tkinter 8.6
My code does basically this:
Open a process, where:
A test function is called via a thread that closes Gui window after 3s
Gui window is created
Wait for thread to complete (join) and guess window was closed
I tried to use:
quit -> window only closes when i hover my mouse over it
destroy -> destroy does not return
I stripped it down to the following code, please copy & execute and/or tell me whats wrong...
from time import sleep, time
import threading
from multiprocessing import Process, set_start_method
from tkinter import *
CtrlApplObj = None
def Start():
global CtrlApplObj
CtrlApplObj = None
CtrlApplObj = ControlApplication()
CtrlApplObj.run()
def End():
print("Quit now...")
#CtrlApplObj.root.destroy()
CtrlApplObj.root.quit()
class ControlApplication():
def __init__(self):
pass
def run(self):
self.root=Tk()
print("Mainloop...")
self.root.mainloop()
def test():
sleep(3)
End()
def execute():
T1 = threading.Thread(target=test)
T1.start()
Start()
T1.join()
if __name__ == "__main__":
set_start_method("spawn")
for i in range(2):
TestProcess = Process(target=execute)
TestProcess.start()
TestProcess.join()
My final solution was not using any tkinter operations in test thread. Then destroy worked.
I had another problem with my test process not closing. This was because a queue was not empty. That porevented process to close.

BottlePy and PyWebView

I am trying to build a small app in Bottle, and thought I'd try using pywebview as a viewer. When I run the following file, I get two instances of the webview window. The first one shows the page, the second shows a spinning wheel cursor. Closing the second window is shutting down the web server, I believe, but not killing the thread.
Why are there two windows showing up?
import sys
import threading
from bottle import Bottle, ServerAdapter
import webview
class MyWSGIRefServer(ServerAdapter):
server = None
def run(self, handler):
from wsgiref.simple_server import make_server, WSGIRequestHandler
if self.quiet:
class QuietHandler(WSGIRequestHandler):
def log_request(*args, **kw): pass
self.options['handler_class'] = QuietHandler
self.server = make_server(self.host, self.port, handler, **self.options)
self.server.serve_forever()
def stop(self):
# self.server.server_close() <--- alternative but causes bad fd exception
self.server.shutdown()
app = Bottle()
listen_addr = 'localhost'
listen_port = 8080
server = MyWSGIRefServer(host='localhost', port=8080)
#app.route('/')
def hello():
return "Hello World!"
def start_server():
app.run(server=server, reloader=True)
try:
print(threading.enumerate())
serverthread = threading.Thread(target=start_server)
serverthread.daemon = True
print("starting web server")
serverthread.start()
print("starting webview")
webview.create_window('bottle test', "http://localhost:8080/")
print("webview closed. closing server")
sys.exit()
server.stop()
except Exception as ex:
print(ex)
The issue was using reloader=True when running the server. Setting this to False prevents the second window from appearing.

Resources