Rotary encoder value looking sticky after using tkinter function " root.after" - python-3.x

I am quite new to python and tkinter ,I am working on a project where i give demand to slave device through rotary encoder value and reading feedback value from slave devices through communication.
I face issue when i use root.after () function .
Any suggestion regarding proper use of root.after () function so that my interrupt routine not affected welcome heartly.
Thank you.
Here is my code.
import pigpio
import tkinter as tk
from PIL import ImageTk,Image
import time
from RPi import GPIO
import serial
i="*00T%"
j="*01T%"
s=serial.Serial(port='/dev/ttyS0',
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
class decoder: # class for decoding rotary encoder
def __init__(self, pi, gpioA, gpioB, callback):
----------
---------------
----------------
if __name__ == "__main__":
import RPi.GPIO as GPIO
import time
import pigpio
import tkinter as tk
import board
import busio
rpm=0
tor=0
pow=0
pi = pigpio.pi()
st=1
pos=0
def callback(way): # interrupt event sense on pin no 17,18
global pos
global st
if st ==1:
pos += way
if pos >= 9999:
pos=9999
if pos <= 0:
pos=0
var.set(pos)
print("pos={}".format(pos))
def rpm_update():
global rpm
s.write(str.encode(i))
print(i)
time.sleep(0.2)
if s.inWaiting():
rpm=s.readline(s.inWaiting())
rpm=rpm.decode('utf-8',errors='ignore')
rpm=rpm[5:-1]
print(rpm)
var2.set(rpm)
def tor_update():
global tor
s.write(str.encode(j))
print(j)
time.sleep(0.2)
if s.inWaiting():
tor=s.readline(s.inWaiting())
tor=tor.decode('utf-8',errors='ignore')
tor=tor[4:-1]
print(tor)
var3.set(tor)
def pow_update():
global rpm,tor,pow
try:
rpm=float(rpm)
tor=float(tor)
except ValueError:
pass
pow=int(rpm*tor/5252)
var4.set(pow)
def update():
rpm_update()
time.sleep(0.5)
tor_update()
time.sleep(0.5)
pow_update()
root.after(1000,update)
path="/home/pi/logo.png"
root=tk.Tk()
img=ImageTk.PhotoImage(Image.open(path))
panel=tk.Label(root,image=img)
panel.pack()
panel.place(x=195,y=10,height=50,width=80)
root.title("Dynalec")
root.geometry("500x600")
{
body of tkinter screen
}
decoder=decoder(pi, 17, 18, callback)
root.after(1000,update)
root.mainloop()
everything is working fine before root.after() function " root.after(1000,update)
but i need it because i have to read slave values in every sec and update in gui screen.

Adding as a answer because long for comments, will remove soon.
Try this out:
def update():
root.after(500,rpm_update)
root.after(500,tor_update)
root.after(500,pow_update)
root.after(1000,update)
Do let me know.

Related

running 4 functions periodically in the background without freezing the GUI

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

why a thread does not end

I'm trying to make an app to interact with an usb rfid reader on a Raspberry pi.
it looks like the solution is using the trheading module (otherwise the data entry leads to a disfunction)
After some essays I made an app that works... the first time. After that, a message error says "threads can only be started once"... so the interpretation is that the thread does not end naturally...why??
import time
import sys
import os
from tkinter import *
from tkinter.ttk import *
import threading
def activa_fil(self):
print ('activant fil')
fil.start()
def prova2():
print('hola')
print(Control.get())
print(NumCorr.get())
print(time.strftime("%H:%M:%S"))
DisplayCorr.config(text=NumCorr.get())
Llistacorr.insert(0, (Control.get(),NumCorr.get(), time.strftime("%H:%M:%S")))
marc.contador +=1
Contador.config (text=marc.contador)
print(marc.contador)
file = open("prova.txt", "a")
file.write(Control.get())
file.write(NumCorr.get())
file.write (time.strftime("%H:%M:%S"))
file.write('\n')
file.close()
NumCorr.after(500, NumCorr.delete(0,END))
def tick():
time_string = time.strftime("%H:%M:%S")
Hora.config(text=time_string)
Hora.after(200, tick)
def canvia_focus(self):
NumCorr.focus()
fil=threading.Timer(0.5, prova2)
marc = Tk()
marc.geometry('480x320')
marc.bind_all(''<'Key-Return'>'', activa_fil)
marc.contador =0
Control = Combobox(marc)
Control['values']=('sortida','can cuias','can campanya','papiol','sortida papiol', 'can olivero', 'ullastrell', 'coll olesa','pla fideuer','aeri', 'arribada')
Control.config(font="helvetica 30", )
Control.grid(column='0', row='0', columnspan='2')
Llistacorr = Listbox(marc)
Llistacorr.config(font='arial')
Llistacorr.grid_propagate(0)
Llistacorr.grid(column='0', row='1', rowspan='3')
DisplayCorr=Label(marc, font='Arial 10')
DisplayCorr.grid(column='1', row='1')
NumCorr = Entry(marc)
NumCorr.config(font='helvetica 6')
Control.bind('<<ComboboxSelected>>', canvia_focus)
NumCorr.grid(column='0', row='4', columnspan='2')
Contador=Label(marc, font='arial 30')
Contador.grid(column='1', row='2')
Hora=Label(marc, font='arial 30')
Hora.grid(column='1', row='3')
tick()
marc.mainloop()

How would I constantly update a variable within a script/program?

I am attempting to make a thermostat of sorts. To do this, I am using a Pi3 with a DHT22 Temperature sensor, and Python3.
What I need is for the temperature to be polled and the corresponding variable to update on its own.
Attempting to do so with any sort of While True: statements results in the gui I'm testing with, not opening.
I'm lost (And yes, this code is hacked together from others. LOL)
#! python3
import time
import RPi.GPIO as GPIO
import string
import tkinter
import tkinter.ttk
import Adafruit_DHT
from tkinter import messagebox
from tkinter import *
root = Tk()
root.title('PiTEST')
root.configure(background='black')
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
sensor = Adafruit_DHT.DHT22
pin = 4
def PRINTTEST():
print(temperature, humidity)
TESTTEXT = Label(root,text="TESTING",fg="white",bg="black",font='Consolas 20 bold')
TESTTEXT.grid(row=1,column=1,sticky="W,S,E")
B1 = tkinter.Button(root,bd=5,text="TEST",bg="gray",fg="white",command=PRINTTEST,height=4,width=20)
B1.grid(row=2,column=1,sticky="N,S,E,W",padx=8,pady=8)
while True:
humidity, temperature = Adafruit_DHT.read_retry(sensor, pin)
temperature = temperature * 9/5.0 + 32
root.mainloop()
GPIO.cleanup()
Here is a code example without your GPIO things:
#! python3
import time
import string
import tkinter
import random
from tkinter import messagebox
from tkinter import *
root = Tk()
root.title('PiTEST')
root.configure(background='black')
def PRINTTEST():
temperature = random.randint(0,100)
humidity = random.randint(0,100)
print(temperature, humidity)
root.after(1000, PRINTTEST)
TESTTEXT = Label(root,text="TESTING",fg="white",bg="black",font='Consolas 20 bold')
TESTTEXT.grid(row=1,column=1,sticky="W,S,E")
B1 = tkinter.Button(root,bd=5,text="TEST",bg="gray",fg="white",command=PRINTTEST,height=4,width=20)
B1.grid(row=2,column=1,sticky="N,S,E,W",padx=8,pady=8)
root.mainloop()
This will print 2 random integers every second in your terminal.

faster FFT issue in Python using pyqtgraph

As you can see below I have a program that uses pyqtgraph to display a spectrum of two signals on some noise using numpy. It may well be I am pushing the limits here. I am using a sample rate of 300000 to put some pressure on things. None-the-less I saw no improvements in splitting up this app using multiprocess or threading from putting the code lines in the signal() function from having it all under the update() function. I also tried pyfftw, which showed no improvement when substituting that in for np.fft. I show 1 core always at 100% so I suspect multiprocess (or threading) may not really be working the way I expect it either. Depending on your computer at aa rate 300000 it updates and pauses and updates and pauses. I would like to hit something like 2400000 smoothly without the pauses between updates. Anyone know how I can speed this up please?
# -*- coding: utf-8 -*-
from pyqtgraph.Qt import QtGui, QtCore
import numpy as np
from threading import Thread
from queue import Queue
from numpy import arange, sin, cos, pi
from scipy.fftpack import fft, rfft
import pyqtgraph as pg
import sys
import multiprocessing
class Plot2D():
def __init__(self):
self.traces = dict()
#QtGui.QApplication.setGraphicsSystem('raster')
self.app = QtGui.QApplication([])
#mw = QtGui.QMainWindow()
#mw.resize(800,800)
self.win = pg.GraphicsWindow(title="Basic plotting examples")
self.win.resize(1000,600)
self.win.setWindowTitle('pyqtgraph example: Plotting')
# Enable antialiasing for prettier plots
pg.setConfigOptions(antialias=True)
self.canvas = self.win.addPlot(title="Pytelemetry")
self.canvas.setYRange(-10, 100, padding=0)
def start(self):
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
QtGui.QApplication.instance().exec_()
def trace(self,name,dataset_x,dataset_y):
if name in self.traces:
self.traces[name].setData(dataset_x,dataset_y)
else:
self.traces[name] = self.canvas.plot(pen='y')
## Start Qt event loop unless running in interactive mode or using pyside.
if __name__ == '__main__':
p = Plot2D()
i = 0
def signal():
rate = 300000 # sampling rate
t = np.arange(0, 10, 1/rate)
sig = np.sin(2000*np.pi*4*t) + np.sin(2000*np.pi*7*t) + np.random.randn(len(t))*0.02 #4k + 7k tone + noise
return sig
def update():
rate = 300000 # sampling rate
z = 20*np.log10(np.abs(np.fft.rfft(signal()))) #rfft trims imag and leaves real values
f = np.linspace(0, rate/2, len(z))
p.trace("Amplitude", f, z)
timer = QtCore.QTimer()
timer.timeout.connect(lambda: update())
timer.start(10)
p.start()
t1 = multiprocessing.Process(target=signal)
t1.start

PyQt MainWindow using multiprocessing on Windows

I try to create a PyQt application. In order to run process in background and keep the PyQt5 application available for new instruction, I want to use multiprocessing.
On the Windows OS, when I call a function from the Qt MainWindow class with multiprocessing.process, I have an error about pickling this class. But it is running find on Linux.
Here is an example:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
import multiprocessing
#
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class Frame_main(QMainWindow):
def __init__(self, parent = None):
super(Frame_main, self).__init__(parent)
self.thrd_list=[]
self.initUI()
def initUI(self):
# Button
btn_run = QPushButton('Run', self)
btn_run.clicked.connect(lambda: self.ThdSomething(self.DoRun) ) #
btn_stop = QPushButton('Stop', self)
btn_stop.clicked.connect(self.DoStop)
### TEXT Edit
self.textEdit_term = QTextEdit("terminal: \n ")
self.textEdit_term.append("")
self.textEdit_term.setStyleSheet("color: rgb(255, 255, 255); background-color: black;")
self.textEdit_term.setLineWrapMode(QTextEdit.NoWrap)
self.textEdit_term.setToolTip(' terminal message ')
self.textEdit_term.setStatusTip('textEdit1')
### LAYOUT
Wid_main = QWidget() #
grid_major = QGridLayout() #
grid_major.addWidget( btn_run, 1, 5)
grid_major.addWidget( btn_stop, 2, 5)
Wid_main.setLayout(grid_major)
self.setCentralWidget(Wid_main)
### Affichage
self.show() #
#
def DoRun(self):
print('g starts')
time_start=time.time()
time_stop=time.time()
name='bob'
n=0
while time_stop-time_start <2 :
n+=1
time_stop=time.time()
time.sleep(0.8)
print ('hola', name,n, flush=True)
print('g stops')
def DoStop(self):
''' subourtine kill all the thread '''
print('stop action detected')
while len(self.thrd_list) > 0 :
print("Terminating the job: {}".format(self.thrd[-1].pid) )
os.kill(self.thrd[-1].pid, signal.SIGTERM)
self.thrd_list[-1].terminate()
self.thrd_list.pop()
def ThdSomething(self, job):
''' subourtine to create a new thread to do the job subroutine '''
arg=''
p=multiprocessing.Process(target=job, args=(arg))
self.thrd_list.append( p )
p.start()
print("Start the job GUI: {} with PID: {}".format(str(job) ,self.thrd[-1].pid), file=sys.stdout )
def closeEvent(self, event):
''' subroutine to define what happen when closing the main frame'''
self.statusBar().showMessage('waiting for a respond')
reply = QMessageBox.question(self, 'Message',
"Are you sure to quit?", QMessageBox.Yes |
QMessageBox.No, QMessageBox.No)
if reply == QMessageBox.Yes:
event.accept()
else:
event.ignore()
# Main
if __name__ == '__main__':
# Create QApplication and Main Frame
Qapp = QApplication(sys.argv) # creation de lappli Qt
Qapp.setStyle("fusion") #
frame1 = Frame_main() #
sys.exit(Qapp.exec_()) #
EDIT:
I found several similar case, like :
Python: multiprocessing in pyqt application
but none of them helped. I think this might be linked to fact that my case is using function and attributes of the MainWindow class.
You are correct that this is due to the attempt to fork on a method within the GUI class. Unfortunately windows doesn't really have the ability to fork processes the way linux does, which is why your code works on linux and not on windows. The Python 3 documentation for the multiprocessing library has some useful information on the differences and what the default method for starting a new process is under different platforms.
To answer your question: because your target is a method associated with a GUI object, it has to send the object state to the new process but fails to pickle it because a PyQt GUI object is far too complex to pickle.
If you rewrite your code so that the target function (and args specified) are picklable, your code will work.

Resources