I'm trying to connect a slot to a signal emitted through DBus in PyQt 5.6 with Python 3.5.
When I run my script like this QDBUS_DEBUG=1 python3 qtdbustest.py it never reaches the call to print('Connected') but instead just hangs at the bus.connect(...) call. The signal is seen on the bus as evident in the debug output:
QDBusConnectionPrivate(0x7f3e60002b00) : connected successfully
QDBusConnectionPrivate(0x7f3e60002b00) got message (signal):
QDBusMessage(type=Signal, service="org.freedesktop.DBus",
path="/org/freedesktop/DBus", interface="org.freedesktop.DBus",
member="NameAcquired", signature="s", contents=(":1.137") )
QDBusConnectionPrivate(0x7f3e60002b00) delivery is suspended
Here is my minimal working example:
#!/usr/bin/python3
import sys
from PyQt5.QtCore import QObject, pyqtSlot
from PyQt5.QtWidgets import QApplication
from PyQt5.QtDBus import QDBusConnection, QDBusMessage
class DbusTest(QObject):
def __init__(self):
super(DbusTest, self).__init__()
bus = QDBusConnection.systemBus()
bus.connect(
'org.freedesktop.DBus',
'/org/freedesktop/DBus',
'org.freedesktop.DBus',
'NameAcquired',
self.testMessage
)
print('Connected')
#pyqtSlot(QDBusMessage)
def testMessage(self, msg):
print(msg)
if __name__ == '__main__':
app = QApplication(sys.argv)
discoverer = DbusTest()
sys.exit(app.exec_())
What am I doing wrong? There must be something I overlooked so that the call to bus.connect(...) actually returns.
I was able to fix your example like this:
bus = QDBusConnection.systemBus()
bus.registerObject('/', self)
bus.connect( ...
However, I have to admit I don't exactly understand why it works (which is to say, I couldn't find any corroborating documentation). It does seem to make sense that you'd need to register the receiver object before attempting to make the connection, though.
Related
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 struggling to simultaneously run my Kivy app alongside a python script that is being locally imported.
Full python code
import Client # Locall import
import time
from threading import Thread
from kivy.app import App
from kivy.uix.button import Button
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
class MainWindow(Screen):
pass
class SecondWindow(Screen):
pass
class WindowManager(ScreenManager):#will manage navigation of windows
pass
kv = Builder.load_file("my.kv")
class Sound(App):
def build(self):
return kv
def ipconfig(self,input_ip):
if len(input_ip) == 13:
print('Address binded!!')
Client.host = input_ip #Modify ip adress
else:
print('Invalid input_ip')```
if __name__ == '__main__':
#Sound().run()#Kivy run method
Thread(target = Sound().run()).start()
time.sleep(10)
Thread(target = Client.father_main).start()
Where the threading happens
if __name__ == '__main__':
#Sound().run()#Kivy run method
Thread(target = Sound().run()).start()
time.sleep(10)
Thread(target = Client.father_main).start() #Client is locally imported
PROBLEMS
1.Only the kivy app runs but the father_main function fails to.
2.The only time father_main runs is when I close the kivy application.
3.If i try and remove the 'run()' from Sound(). I get TypeError: 'Sound' object is not callable and father_main immediately runs
4.If i only remove the parenthesis from 'run()' so it turns into 'run'. I get Segmentation fault (core dumped)
kivy does not encourage the use of time.sleep() and i still have no clue of what exactly your program is but here a solution.
create an on_start method (A method that runs when kivy app started) and add start the ipconfig method from there but you're going to start it asynchronously.
from multiprocessing.pool import ThreadPool
class Sound(App):
def on_start(self):
pool = ThreadPool(processes=1)
async_start = pool.apply_async(self.ip_config, ("value for ip_input here"))
# do some other things inside the main thread here
if __name__ == "__main__":
Sound().run()
You need to run the App on the main thread. I would suggest something like:
def start_father_main(dt):
Thread(target = Client.father_main).start() #Client is locally imported
if __name__ == '__main__':
Clock.schedule_once(start_father_main, 10)
Sound().run()
I haven't tested this code, but it should give you the idea.
I wrote simple program which has pyQt interface with 2 buttons (start and cancel). Start button runs some calculations in the background (by starting update function) and thanks to threading I can still use UI.
But the application crashes after 10sec - 2 minutes. UI just dissapears, program shutdown.
when I use pythonw to run app without console thread crashes after ~25 sec but gui still works.
#!/usr/bin/python
import threading
import sys
from PyQt4 import QtGui, QtCore
import time
import os
class Class(QtGui.QWidget):
def __init__(self):
#Some init variables
self.initUI()
def initUI(self):
#some UI
self.show()
def update(self,stop_event):
while True and not stop_event.isSet():
self.updateSpeed()
self.updateDistance()
self.printLogs()
self.saveCSV()
self.guiUpdate()
time.sleep(1)
#gui button function
def initiate(self):
self.stop_event = threading.Event()
self.c_thread = threading.Thread(target = self.update, args=(self.stop_event,))
self.c_thread.start()
#Also gui button function
def cancelTracking(self):
self.stop_event.set()
self.close()
def main():
app = QtGui.QApplication(sys.argv)
ex = Class()
sys.exit(app.exec_())
ex.update()
if __name__ == '__main__':
main()
I dont know if I'm doing threading right. I found example like this on stack. I'm quite new to python and I'm using threading for the first time.
It is most likely due to calling a GUI function in your separate thread. PyQt GUI calls like setText() on a QLineEdit are not allowed from a thread. Anything that has PyQt painting outside of the main thread will not work. One way to get around this is to have your thread emit a signal to update the GUI when data is ready. The other way is to have a timer periodically checking for new data and updating the paintEvent after a certain time.
========== EDIT ==========
To Fix this issue I created a library named qt_thread_updater. https://github.com/justengel/qt_thread_updater This works by continuously running a QTimer. When you call call_latest the QTimer will run the function in the main thread.
from qt_thread_updater import get_updater
lbl = QtWidgets.QLabel('Value: 1')
counter = {'a': 1}
def run_thread():
while True:
text = 'Value: {}'.format(counter['a'])
get_updater().call_latest(lbl.setText, text)
counter['a'] += 1
time.sleep(0.1)
th = threading.Thread(target=run_thread)
th.start()
========== END EDIT ==========
#!/usr/bin/python
import threading
import sys
from PyQt4 import QtGui, QtCore
import time
import os
class Class(QtGui.QWidget):
display_update = QtCore.pyqtSignal() # ADDED
def __init__(self):
#Some init variables
self.initUI()
def initUI(self):
#some UI
self.display_update.connect(self.guiUpdate) # ADDED
self.show()
def update(self):
while True and not self.stop_event.isSet():
self.updateSpeed()
self.updateDistance()
self.printLogs()
self.saveCSV()
# self.guiUpdate()
self.display_update.emit() # ADDED
time.sleep(1)
#gui button function
def initiate(self):
self.stop_event = threading.Event()
self.c_thread = threading.Thread(target = self.update)
self.c_thread.start()
#Also gui button function
def cancelTracking(self):
self.stop_event.set()
self.close()
def main():
app = QtGui.QApplication(sys.argv)
ex = Class()
sys.exit(app.exec_())
# ex.update() # - this does nothing
if __name__ == '__main__':
main()
The other thing that could be happening is deadlock from two threads trying to access the same variable. I've read that this shouldn't be possible in python, but I have experienced it from the combination of PySide and other Python C extension libraries.
May also want to join the thread on close or use the QtGui.QApplication.aboutToQuit signal to join the thread before the program closes.
The Qt documentation for QThreads provides two popular patterns for using threading. You can either subclass QThread (the old way), or you can use the Worker Model, where you create a custom QObject with your worker functions and run them in a separate QThread.
In either case, you can't directly update the GUI from the background thread, so in your update function, the guiUpdate call will most likely crash Qt if it tries to change any of the GUI elements.
The proper way to run background processes is to use one of the two QThread patterns and communicate with the main GUI thread via Signals and Slots.
Also, in the following bit of code,
app = QtGui.QApplication(sys.argv)
ex = Class()
sys.exit(app.exec_())
ex.update()
app.exec_ starts the event loop and will block until Qt exits. Python won't run the ex.update() command until Qt has exited and the ex window has already been deleted, so you should just delete that command.
Well, I am familiar with Qt, but when using PyQt, the syntax of signal/slot really confused me.
When using C++/Qt, the compiler will give you a hint where you are wrong about the signal/slot, but the PyQt default configuration doesn't give a hint about error. Is there a ways or such as debug trigger mode to enable PyQt to display more information?
the Code is as following:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import time
class workThread(QThread):
def __init__(self,parent = None):
super(workThread,self).__init__(parent)
self.mWorkDoneSignal = pyqtSignal() ## some people say this should be defined as clas member, however, I defined it as class member and still fails.
def run(self):
print "workThread start"
time.sleep(1)
print "workThread stop"
print self.emit(SIGNAL("mWorkDoneSignal"))
class MainWidget(QWidget):
def __init__(self , parent = None):
super(MainWidget,self).__init__(parent)
#pyqtSlot()
def display(self):
print "dispaly"
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
c = workThread()
d = MainWidget()
##In Qt, when using QObject::connect or such things, the return value will show the
## signal/slot binding is success or failed
print QObject.connect(c,SIGNAL('mWorkDoneSignal()'),d,SLOT('display()'))
c.start()
d.show()
app.exec_()
In C++,the QObject::connect return value will show the signal/slot binding is success or not. In PyQt, the return value is True, but it doesn't trigger the slot.
My Question:
1) Is the signal shoud be a class member or instance member?
2) If QObject.connect 's return value can't give the hint of the binding is success or not, is there other ways to detect it?
I want to bind the signal/slot outside the signal sender and slot receiver, so I perfer to use the QObject.connect ways. But how can I write this correct, I tried the following ways,both fail.
QObject.connect(c,SIGNAL('mWorkDoneSignal'),d,SLOT('display'))
QObject.connect(c,SIGNAL('mWorkDoneSignal()'),d,SLOT('display()'))
First, you should really use new style signals with pyqt. In fact, QObject.connect and QObject.emit will not even be there anymore in PyQt5.
def __init__(self,parent = None):
super(workThread,self).__init__(parent)
self.mWorkDoneSignal = pyqtSignal()
This creates an unbound signal and assigns it to a instance variable mWorkDoneSignal, wich dosn't really have an effect. If you want to create an signal, then you really have to declare it on the class.
So if you didn't really create a signal here, then why did this call succeed:
QObject.connect(c,SIGNAL('mWorkDoneSignal()'),d,SLOT('display()'))
The answer lies in the handling of old style signals by PyQt4:
The act of emitting a PyQt4 signal implicitly defines it.
For that reason when you connect a signal to a slot, only the existence of the slot is checked. The signal itself doesn't really need to exist at that point, so the call will always succeed unless the slot doesn't exist.
I tried the following ways,both fail.
QObject.connect(c,SIGNAL('mWorkDoneSignal'),d,SLOT('display'))
QObject.connect(c,SIGNAL('mWorkDoneSignal()'),d,SLOT('display()'))
The first fails because display (without parenthesis) isn't a valid slot.
The second succeeds. The reason it doesn't work is because you emit mWorkDoneSignal, but what you actually need to emit is:
self.emit(SIGNAL("mWorkDoneSignal()"))
Using new style signals, there's no way to mess things like this up:
from utils import sigint
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import time
class workThread(QThread):
mWorkDoneSignal = pyqtSignal()
def __init__(self,parent = None):
super(workThread,self).__init__(parent)
def run(self):
print "workThread start"
time.sleep(1)
print "workThread stop"
self.mWorkDoneSignal.emit()
class MainWidget(QWidget):
def __init__(self , parent = None):
super(MainWidget,self).__init__(parent)
#pyqtSlot()
def display(self):
print "dispaly"
if __name__ == "__main__":
import sys
app = QApplication(sys.argv)
c = workThread()
d = MainWidget()
c.mWorkDoneSignal.connect(d.display)
c.start()
d.show()
app.exec_()
Ok so maybe it isnt a bug, but I cant get it to work.
say you have to classes that both use PyQt4. One is called Audio.py and uses Phonon to play a sound file. the other is called GUI.py and uses QtGui to display a screen. GUI needs to be able to call and use Audio.py whenever it wants (import Audio). It will import and send the call to my Audio class but because Audio is not started (double click to run) it does not the code (app=QApplication(sys.argv); sys.exit(app.exec_())). so while the Audio class runs when it is run, when you import it, it will not play sounds because its own QApplication loop has not been started.
Any Help?
Edit:
Added class Engine
these are 2 seperate python files (.py)
import Library,Player,sys
from PyQt4.QtGui import QApplication
class Engine(object):
def __init__(self,path,song=None):
self.counter=0
self.path=path
self.lib=Library.Library(self.path)
if song is None:
self.player=Player.Player(self.lib.getSong(self.counter))
else:
self.player=Player.Player(path+song)
def updatePlayer(self,songStatus):
self.player.findStatus(songStatus)
def getCurrentSong(self):
return self.lib.getSong(self.counter)
if __name__=='__main__':
app=QApplication(sys.argv)
e=Engine('D:/Music/','Yeah!.mp3')
e.updatePlayer('Play')
sys.exit(app.exec_())
import sys
from PyQt4.QtGui import QApplication
from PyQt4.QtCore import QObject
from PyQt4.phonon import Phonon
class Player(QObject):
def __init__(self,song):
super(QObject,self).__init__()
self.song=song
self.media=None
#self.metaInfo=Phonon.MediaObject(self)
#self.metaInfo.currentSourceChanged.connect(self.disMetaData)
self.initMedia()
self.findStatus()
def initMedia(self):
if not self.media:
self.media=Phonon.MediaObject()
audioOutput=Phonon.AudioOutput(Phonon.MusicCategory,self)
Phonon.createPath(self.media,audioOutput)
self.media.setCurrentSource(Phonon.MediaSource(self.song))
def findStatus(self,status=None):
if status is not None:
if status=='Play':
self.playSong()
return
if status=='Stop':
self.stopSong()
return
if status=='Pause':
self.pauseSong()
return
if status=='Next':
nextSong()
return
if status=='Previous':
self.previousSong()
return
def playSong(self):
self.media.play()
def stopSong(self):
self.media.stop()
def pauseSong(self):
self.media.pause()
def nextSong(self):
'''nextSong code'''
def previousSong(self):
'''previousSong code'''
if __name__=='__main__':
app=QApplication(sys.argv)
p=Player('D:/Music/Yeah!.mp3')
p.findStatus('Play')
sys.exit(app.exec_())
To be sure you make actions when the event loop is already running use QTimer::singleShot:
QtCore.QTimer.singleShot(0, some_function)