PyQt how to send/recieve signals between threads - python

Hi all and thanks for the help.
I am using a PyQt GUI to read and write voltages to mass flow controls and a heat flux gage. I am using QThreads to have a thread running in the background continuously updating the measurements displayed in the GUI. I am running into trouble having the background thread actually update the GUI as it does not have access to the GUI thread's functions. From what I have read online there is something about "moving" on thread into the other but am not sure what is meant by this. Can someone clarify this/point me in the right direction?
Here is my code:
from PyQt4 import QtGui, QtCore
import sys
import design4
import piplates.DAQC2plate as DAQC2
import time
import threading
air = 0.0
propane = 0
tolerance = .1
n = 1 #number of air controllers
class App4(QtGui.QMainWindow, design4.Ui_MainWindow):
def __init__(self, parent =None):
super(self.__class__, self).__init__()
self.setupUi(self)
self.sendFlow.clicked.connect(self.inputClicked)
self.displayFlow.clicked.connect(self.outputClicked)
self.hfButton.clicked.connect(self.heatFluxRead)
self.myThread = Worker()
self.myThread.start()
def inputClicked(self):
air = float(self.AirFlowInput.text())
propane = self.PropaneFlowInput.text()
if air <= 160 and air > 0:
acv = air / 20.0 / n *2 +.05#compute air control voltage for each controller
DAQC2.setDAC(0,0,acv) #changing voltage signal for channel 0
DAQC2.setDAC(0,1,acv)
else:
DAQC2.setDAC(0,0,0)
print DAQC2.getDAC(0,1)
print DAQC2.getDAC(0,0)
def outputClicked(self):
airRead = float(DAQC2.getADC(0,0)-.01) * 40 / n #converting 0-4Volts to flow
self.airFlowMeasured.display(airRead)
#print DAQC2.getADC(0,0)
#propRead = float(DAQC2.getADC(0,2)-.01) * 20
#self.propaneFlowMeasured.display(propRead)
#print DAQC2.getADC(0,1)
#palette = self.airFlowMeasured.palette()
#role = self.airFlowMeasured.backgroundRole()
#if float(DAQC2.getADC(0,0)) < (air*(1-tolerance):
# palette.setColor(role, QColor('yellow'))
#else if
def heatFluxRead(self):
heatFlux = float(DAQC2.getADC(0,7)) * 5800.0 #converts volts to kW/m^2
self.hfNumber.display(heatFlux)
print heatFlux
print self.getADC2(0,7)
def getADC2(self,addr,channel):
DAQC2.VerifyADDR(addr)
DAQC2.VerifyAINchannel(channel)
resp= DAQC2.ppCMD(addr,0x30,channel,0,2)
value=(256*resp[0]+resp[1])
if (channel==8):
value=value*5.0*2.4/65536
else:
value=(value*24.0/65536)-12.0
value=round(value*DAQC2.calScale[addr][channel]+DAQC2.calOffset[addr][channel],5)
return value
def main():
app = QtGui.QApplication(sys.argv)
form = App4()
form.show()
app.exec_()
class Update(QtCore.QObject):
sendUpdate = QtCore.pyqtSignal()
class Worker(QtCore.QThread):
def __init__(self, parent = None):
QtCore.QThread.__init__(self, parent)
self.initSignal()
def initSignal(self):
self.u = Update()
self.u.sendUpdate.connect(self.outputClicked)
def run(self):
while True:
self.u.sendUpdate.emit()
print 1
time.sleep(.25)
if __name__ == '__main__':
main()
Thanks a ton for any help!

I think that reading through DAQC2.getADC() function does not consume much time so it is not necessary to use threads, what you want to do is a periodic task, so the most appropriate option is to use QTimer:
class App4(QtGui.QMainWindow, design4.Ui_MainWindow):
def __init__(self, parent =None):
super(self.__class__, self).__init__()
self.setupUi(self)
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.outputClicked)
self.timer.setInterval(250)
self.sendFlow.clicked.connect(self.inputClicked)
self.displayFlow.clicked.connect(self.timer.start)
self.hfButton.clicked.connect(self.heatFluxRead)
def inputClicked(self):
air = float(self.AirFlowInput.text())
propane = self.PropaneFlowInput.text()
if air <= 160 and air > 0:
acv = air / 20.0 / n *2 +.05#compute air control voltage for each controller
DAQC2.setDAC(0,0,acv) #changing voltage signal for channel 0
DAQC2.setDAC(0,1,acv)
else:
DAQC2.setDAC(0,0,0)
print DAQC2.getDAC(0,1)
print DAQC2.getDAC(0,0)
def outputClicked(self):
airRead = float(DAQC2.getADC(0,0)-.01) * 40 / n #converting 0-4Volts to flow
self.airFlowMeasured.display(airRead)
#print DAQC2.getADC(0,0)
#propRead = float(DAQC2.getADC(0,2)-.01) * 20
#self.propaneFlowMeasured.display(propRead)
#print DAQC2.getADC(0,1)
#palette = self.airFlowMeasured.palette()
#role = self.airFlowMeasured.backgroundRole()
#if float(DAQC2.getADC(0,0)) < (air*(1-tolerance):
# palette.setColor(role, QColor('yellow'))
#else if
def heatFluxRead(self):
heatFlux = float(DAQC2.getADC(0,7)) * 5800.0 #converts volts to kW/m^2
self.hfNumber.display(heatFlux)
print heatFlux
print self.getADC2(0,7)
def getADC2(self,addr,channel):
DAQC2.VerifyADDR(addr)
DAQC2.VerifyAINchannel(channel)
resp= DAQC2.ppCMD(addr,0x30,channel,0,2)
value=(256*resp[0]+resp[1])
if (channel==8):
value=value*5.0*2.4/65536
else:
value=(value*24.0/65536)-12.0
value=round(value*DAQC2.calScale[addr][channel]+DAQC2.calOffset[addr][channel],5)
return value

Related

Get current frame number of video playing with python-vlc

I'm trying to make a python program that plays a video in sync with a light show done via Arduino. To achieve this I need to know what frame of the video is currently playing to send out data to the Arduino.
I modified the cocoavlc.py example of python-vlc to play a video, pause, resume and rewind it, go forward and backwards by 10 seconds.
To keep track of the frame number tried the method get_time of python-vlc but it only updates a few times a second. I had the idea to interpolate its data with a timer but it's not perfect.
Here's the code (without the timer):
import vlc
from pycocoa import __name__ as _pycocoa_
from pycocoa import App, Item, ItemSeparator, MediaWindow, Menu, OpenPanel
from os.path import isfile
from threading import Thread
from time import sleep
import time
_Movies = '.m4v', '.mov', '.mp4'
_Select = 'select a video file from the panel'
class AppVLC(App):
player = None
sized = None
Toggle = None
video = None
window = None
def __init__(self, video = None, title = 'AppVLC'):
super(AppVLC, self).__init__(title = title)
self.media = None
self.Toggle = Item('Play', self.menuToggle_, key='p')
self.video = video
self.player = vlc.MediaPlayer()
def appLaunched_(self, app):
super(AppVLC, self).appLaunched_(app)
self.window = MediaWindow(title=self.video or self.title)
if self.player and self.video and isfile(self.video):
# the VLC player on macOS needs an ObjC NSView
self.media = self.player.set_mrl(self.video)
self.player.set_nsobject(self.window.NSview)
menu = Menu('VLC')
menu.append(
Item('Open...', key='o'),
ItemSeparator(),
self.Toggle,
Item('Rewind', key='r'),
Item('Forward', key='f'),
ItemSeparator(),
Item('Back', key='b'))
ItemSeparator(),
self.append(menu)
self.menuPlay_(None)
self.window.front()
def menuOpen_(self, item):
self.menuPause_(item)
self.badge.label = 'O'
v = OpenPanel(_Select).pick(_Movies)
if v:
self.window.title = self.video = v
self.player.set_mrl(v)
self.sized = None
def menuPause_(self, item, pause = False):
if pause or self.player.is_playing():
self.player.pause()
self.badge.label = 'S'
self.Toggle.title = 'Play'
def menuPlay_(self, item_or_None):
self.player.play()
self._resizer()
self.badge.label = 'P'
self.Toggle.title = 'Pause'
def menuRewind_(self, item):
self.player.set_position(0.0)
self.badge.label = 'R'
self.sized = None
def menuForward_(self, item):
time = self.player.get_time()
lenght = self.player.get_length()
time = time + 10000
if time < lenght:
self.player.set_time(time)
self.sized = None
def menuBack_(self, item):
time = self.player.get_time()
lenght = self.player.get_length()
time = time - 10000
if time > 0:
self.player.set_time(time)
self.sized = None
def menuToggle_(self, item):
if self.player.is_playing():
self.menuPause_(item, pause=True)
else:
self.menuPlay_(item)
def windowClose_(self, window):
if window is self.window:
self.terminate()
super(AppVLC, self).windowClose_(window)
def windowResize_(self, window):
if window is self.window:
self._resizer()
super(AppVLC, self).windowResize_(window)
def _resizer(self):
if self.sized:
self.window.ratio = self.sized
else:
Thread(target=self._sizer).start()
def _sizer(self, secs=0.1):
while True:
w, h = self.player.video_get_size(0)
if h > 0 and w > 0:
self.window.ratio = self.sized = w, h
break
elif secs > 0.001:
sleep(secs)
else:
break
def get_frame(self):
#?
def trackframes():
frame = 0
while True:
if frame != app.get_frame():
frame = app.get_frame()
send(frame) #send data to the Arduino
if __name__ == '__main__':
_video = OpenPanel('Select a video file').pick(_Movies)
app = AppVLC(video = _video)
Thread(target = trackframes).start()
app.run(timeout = None)
The only sensible way I have discovered to approximate the frame number in python vlc, is to take the frames per second player.get_fps() or 25, if not available and calculate the frame number based on the current playing time.
sec = player.get_time() / 1000
frame_no = int(round(sec * fps))

How threads works in python

I'm trying to create app for controlling djitello basing on some tutorials and i want to create possibility to click on some gui tabs and during that control drone by keyboard.
So i thought about using threads but i can't write it. Right now when i use my app it freezes after clicking button which activates method for keyboard control. Can someone explain what am i doing wrong?
This is class for keyboardControl:
from Modules.KeyPressModule import GetKeyPressed as keyPressed
from time import sleep
import logging
class KeyboardControlService():
def __init__(self,passedTello):
self.tello = passedTello
self.startControl = False
def Initialize(self,ui):
self.ui = ui
def GetKeyboardInput(self):
lr,fb,ud,yv = 0,0,0,0
speed = 30
if(keyPressed("left")): lr = speed
elif (keyPressed("right")): lr = -speed
if(keyPressed("up")): fb = speed
elif (keyPressed("down")): fb = -speed
if(keyPressed("w")): ud = speed
elif (keyPressed("s")): ud = -speed
if(keyPressed("a")): yv = speed
elif (keyPressed("d")): yv = -speed
if(keyPressed("q")): self.tello.land()
elif (keyPressed("e")): self.tello.takeoff()
return [lr,fb,ud,yv]
def StartControl(self):
self.startControl = True
while self.startControl:
values = self.GetKeyboardInput()
print("test")
#self.tello.send_rc_control(values[0],values[1],values[2],values[3])
sleep(0.05)
def EndControl(self):
self.startControl = False
This class is for button clicking response:
from djitellopy import Tello
from threading import Thread
from Services.KeyboardControlService import KeyboardControlService as keyboardControlService
class TelloService():
def __init__(self):
self.tello = Tello()
self.keyboardControlService = keyboardControlService(self.tello)
def UseKeyboardControl(self,UseIt,UI):
if(UseIt == True):
self.keyboardControlService.Initialize(UI)
self.keyboardThread = Thread(target=self.keyboardControlService.StartControl(), daemon=True)
self.keyboardThread.start()
else:
self.keyboardControlService.EndControl()
self.keyboardThread.join()
Okey i resolved it
The trouble was here :
self.keyboardThread = Thread(target=self.keyboardControlService.StartControl(), daemon=True)
I passed function not the object

Run multiple function in python

I am writing a program for Raspberry Pi that is utilizing multiple function in classes. I have one class that is dedicated to determining when a capacitive touch sensor is clicked and another one class dedicated to determining the weight from HX711 chip. I want to automatize all: when click the capacitive sensor the cell start the weight scale and then, when I click the capacitive sensor for the second times the system call the function cleanAndExit() but I don't know how I can do this stuff.
import time
import sys
import RPi.GPIO as GPIO
from hx711 import HX711
GPIO.setmode(GPIO.BCM)
padPin = 21
GPIO.setup(padPin, GPIO.IN)
class WeightScale:
def cleanAndExit(self):
print "Clean GPIO..."
GPIO.cleanup()
print "Complete!"
sys.exit()
def cliccato(self):
hx = HX711(5, 6)
hx.set_reading_format("LSB", "MSB")
hx.reset()
hx.tare()
hx.set_reference_unit(-421)
while True:
time.sleep(0.1)
n = 100
//Run for max 100 seconds and create an array with the last value from weight scale sensor
for i in range (1, n):
val = max(0, int (hx.get_weight(5)))
add = []
hx.power_down()
hx.power_up()
time.sleep(0.6)
add.append(val)
add1 = []
add1.append(add[-1:])
print add1
time.sleep(1)
class TouchSensor:
def click(self):
alreadyPressed = False
while True:
padPressed = GPIO.input(padPin)
if padPressed and not alreadyPressed:
print "click"
time.sleep(2)
c=WeightScale()
t=TouchSensor()
In order you to correctly create a class, you need to define an init method with in the class definition. So for example:
class TouchSensor:
def __init__(self):
self.padPin = 21
def click(self):
alreadyPressed = False
while True:
padPressed = GPIO.input(padPin)
if padPressed and not alreadyPressed:
print "click"
time.sleep(2)
Then once you create your object of that class, it is initialized with padPin = 21. Then you use the methods within each class to perform what you're calling 'functions.'
t = TouchSensor()
t.click()
Try making adjustments and post whatever errors you end up with!
I make some adjustments to my code:
import time
import sys
from multiprocessing import Process
import RPi.GPIO as GPIO
from hx711 import HX711
GPIO.setmode(GPIO.BCM)
padPin = 21
GPIO.setup(padPin, GPIO.IN)
class WeightScale:
def cleanAndExit(self):
print ("Clean GPIO...")
GPIO.cleanup()
print ("Complete!")
sys.exit()
def pesatura(self):
hx = HX711(5, 6)
hx.set_reading_format("LSB", "MSB")
hx.reset()
hx.tare()
hx.set_reference_unit(-421)
while t.status==True:
time.sleep(0.1)
n = 100
for i in range (1, n):
val = max(0, int (hx.get_weight(5)))
add = []
hx.power_down()
hx.power_up()
time.sleep(0.6)
add.append(val)
add1 = []
add1.append(add[-1:])
print (add1)
time.sleep(1)
cleanAndExit()
class TouchSensor:
def __init__(self):
self.status = False
def changeStatus(self):
if self.status == False:
self.status = True
print (self.status)
else:
self.status = False
print (self.status)
def click(self):
while True:
padPressed = GPIO.input(padPin)
if padPressed:
print ("click")
t.changeStatus()
alreadyPressed = padPressed
time.sleep(0.5)
c=WeightScale()
t = TouchSensor()
if __name__ == '__main__':
p1 = Process (target = t.click())
p1.start()
p2 = Process (target = c.pesatura())
p2.start()
I have added a multiprocessing function and method changeStatus for the button click,I want to use it to start the loop of pesatura(self) when self.status==True and after, when self.Status== False (another click) start cleanAndExit(). But when I start the script it gives me the error ''cleanAndExit() takes exactly 1 argument (0 given)''.

PyQt Qthread automatic restart

I'm trying to understand how thread works, and i'm stuck with this problem. That's my program explained:
i made a simple GUI in pyqt that use a QObject as a worker class. When i press the botton start the gui read a random value from a list and pass it to the thread, that print the
next five number. When the thread finish the work, it pass the data to the gui. Now i want the GUI to restart automatically a new thread with a new start value. I can restart the thread by pressing start again, but i need to start it without human interaction. Are there
any method?
thanks in advance
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import time
import sys
import numpy as np
class SomeObject(QObject):
finished = pyqtSignal(object)
valore = pyqtSignal(object)
vector = pyqtSignal(object)
def __init():
super(SomeObject, self).__init__()
def longRunning(self):
vec = []
end = self.count + 5
while self.count < end:
time.sleep(1)
vec.append(self.count)
self.valore.emit(self.count)
self.count += 1
self.finished.emit(vec)
#self.vector.emit()
def setCount(self, num):
self.count = num
class GUI(QDialog):
def __init__(self, parent = None):
super(GUI, self).__init__(parent)
#declare QThread object
self.objThread = QThread()
#declare SomeObject type, and move it to thread
self.obj = SomeObject()
self.obj.moveToThread(self.objThread)
#connect finished signal to nextVector method
self.obj.finished.connect(self.nextVector)
#connect valore to self.prova method
self.obj.valore.connect(self.prova)
#self.obj.vector.connect(self.nextVector)
#Connect thread.start to the method long running
self.objThread.started.connect(self.obj.longRunning)
botton = QPushButton("start")
self.connect(botton, SIGNAL("clicked()"), self.showcount)
box = QHBoxLayout()
box.addWidget(botton)
self.setLayout(box)
#a list of random number
a = np.random.randint(10, size = 5)
self.iter = iter(a)
def showcount(self):
"""
When botton clicked, read the next value from iter, pass it to
setCount and when start the thread
"""
try:
a = self.iter.next()
print a
self.obj.setCount(a)
self.objThread.start()
except StopIteration:
print "finito"
#self.obj.setCount(a)
#self.objThread.start()
#print self.objThread.currentThreadId()
def prova(self, value):
"""
Connected to signal valore, print the value
"""
print value
def nextVector(self, vec):
"""
Print the whole vector
"""
print vec
self.objThread.quit()
try:
a = self.iter.next()
print a
self.obj.setCount(a)
self.objThread.start()
except StopIteration:
print "finito"
app = QApplication(sys.argv)
form = GUI()
form.show()
app.exec_()
You already have it set up. When your thread is finished it emits the finished signal which calls the nextVector method, so just call the start method at the end of nextVector.
def nextVector(self, vec):
...
self.showcount()
# end nextVector
You may also want to change to the new signal connection for your QPushButton
button.clicked.connect(self.showcount)

How to connect pyqtSignal between classes in PyQT

How to connect pyqtSignal between two different objects (classes) PROPERLY? I mean best practice.
Look what I have done to achieve the goal: The Thermometer class is notified when Pot increases its temperature:
from PyQt4 import QtCore
class Pot(QtCore.QObject):
temperatureRaisedSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
super(Pot, self).__init__(parent)
self.temperature = 1
def Boil(self):
self.temperature += 1
self.temperatureRaisedSignal.emit()
def RegisterSignal(self, obj):
self.temperatureRaisedSignal.connect(obj)
class Thermometer():
def __init__(self, pot):
self.pot = pot
self.pot.RegisterSignal(self.temperatureWarning)
def StartMeasure(self):
self.pot.Boil()
def temperatureWarning(self):
print("Too high temperature!")
if __name__ == '__main__':
pot = Pot()
th = Thermometer(pot)
th.StartMeasure()
Or is there any easier / better way to do it?
I also insist (if possible) on using "new" style PyQt signals.
from PyQt4 import QtCore
class Pot(QtCore.QObject):
temperatureRaisedSignal = QtCore.pyqtSignal()
def __init__(self, parent=None):
QtCore.QObject.__init__(self)
self.temperature = 1
def Boil(self):
self.temperatureRaisedSignal.emit()
self.temperature += 1
class Thermometer():
def __init__(self, pot):
self.pot = pot
self.pot.temperatureRaisedSignal.connect(self.temperatureWarning)
def StartMeasure(self):
self.pot.Boil()
def temperatureWarning(self):
print("Too high temperature!")
if __name__ == '__main__':
pot = Pot()
th = Thermometer(pot)
th.StartMeasure()
This is how I would've done it according to the docs:
http://www.riverbankcomputing.com/static/Docs/PyQt4/html/new_style_signals_slots.html

Categories

Resources