我在使用PyQt4插槽/信号时遇到了麻烦。
我正在使用PyLIRC,我正在听遥控器上的按键按下。这部分我已经在Qt以外的地方工作了。当从按钮监听线程发出信号并试图调用主线程中的插槽时,我的问题出现了。
我的按钮监听器是一个像这样初始化的QObject:
buttonPressed = pyqtSignal(int)
def __init__(self):
super(ButtonEvent, self).__init__()
self.buttonPressed.connect(self.onButtonPressed)
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
onButtonPressed
插槽位于按钮侦听器的内部,用于测试目的。
要将按钮侦听器移动到另一个线程来完成工作,我使用以下命令:
event = ButtonEvent()
eventThread = QThread()
event.moveToThread(eventThread)
eventThread.started.connect(event.run)
然后在主线程中,我的VideoTableController
类包含主线程中不会被调用的槽。 __init__
内部我有这个:
class VideoTableController(QObject):
def __init__(self, buttonEvent):
buttonEvent.buttonPressed.connect(self.onButtonPressed)
在这种情况下,onButtonPressed
为:
@pyqtSlot(int)
def onButtonPressed(self, bid):
print 'handling button press'
if bid not in listenButtons: return
{ ButtonEnum.KEY_LEFT : self.handleBack,
#...
因此,当我启动事件线程时,它会开始正确监听。当我按下遥控器上的按钮时,onButtonPressed
类内部的ButtonEvent
插槽被正确调用,但不会调用驻留在主线程中的VideoTableController
内的插槽。在将插槽连接到信号后我开始了我的监听线程,我测试了相反的方式,但无济于事。
我环顾四周,但我找不到任何东西。我在阅读You're doing it wrong后转而使用QObject。非常感谢任何帮助。如果您还有其他需要,请告诉我。
编辑:感谢您的回复!以下是您的大量代码:
ButtonEvent(这个类使用单例模式,原谅糟糕的编码,因为我对Python的这个领域也有些新意见):
import pylirc
from PyQt4.QtCore import QObject, pyqtSignal, QThread, pyqtSlot
from PyQt4 import QtCore
class ButtonEvent(QObject):
"""
A class used for firing button events
"""
_instance = None
_blocking = 0
_isListening = False
buttonPressed = pyqtSignal(int)
def __new__(cls, configFileName="~/.lircrc", blocking=0, *args, **kwargs):
if not cls._instance:
cls._instance = super(ButtonEvent, cls).__new__(cls, args, kwargs)
cls._blocking = blocking
if not pylirc.init("irexec", configFileName, blocking):
raise RuntimeError("Problem initilizing PyLIRC")
cls._isListening = True
return cls._instance
def __init__(self):
"""
Creates an instance of the ButtonEvent class
"""
super(ButtonEvent, self).__init__()
self.buttonPressed.connect(self.button)
### init
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
def stopListening(self):
print 'stopping'
self._isListening = False
@pyqtSlot(int)
def button(self, bid):
print 'Got ' + str(bid)
def setupAndConnectButtonEvent(configFileName="~/.lircrc", blocking=0):
"""
Initializes the ButtonEvent and puts it on a QThread.
Returns the QThread it is running on.
Does not start the thread
"""
event = ButtonEvent().__new__(ButtonEvent, configFileName, blocking)
eventThread = QThread()
event.moveToThread(eventThread)
eventThread.started.connect(event.run)
return eventThread
这是VideoTableController:
from ControllerBase import ControllerBase
from ButtonEnum import ButtonEnum
from ButtonEvent import ButtonEvent
from PyQt4.QtCore import pyqtSlot
from PyQt4 import QtCore
class VideoTableController(ControllerBase):
listenButtons = [ ButtonEnum.KEY_LEFT,
ButtonEnum.KEY_UP,
ButtonEnum.KEY_OK,
ButtonEnum.KEY_RIGHT,
ButtonEnum.KEY_DOWN,
ButtonEnum.KEY_BACK ]
def __init__(self, model, view, parent=None):
super(VideoTableController, self).__init__(model, view, parent)
self._currentRow = 0
buttonEvent = ButtonEvent()
buttonEvent.buttonPressed.connect(self.onButtonPressed)
self.selectRow(self._currentRow)
@pyqtSlot(int)
def onButtonPressed(self, bid):
print 'handling button press'
if bid not in listenButtons: return
{ ButtonEnum.KEY_LEFT : self.handleBack,
ButtonEnum.KEY_UP : self.handleUp,
ButtonEnum.KEY_OK : self.handleOk,
ButtonEnum.KEY_RIGHT : self.handleRight,
ButtonEnum.KEY_DOWN : self.handleDown,
ButtonEnum.KEY_BACK : self.handleBack,
}.get(bid, None)()
这是我的启动脚本:
import sys
from PyQt4 import QtCore, QtGui
from ui_main import Ui_MainWindow
from VideoTableModel import VideoTableModel
from VideoTableController import VideoTableController
from ButtonEvent import *
class Main(QtGui.QMainWindow):
def __init__(self, parent=None):
QtGui.QWidget.__init__(self, parent)
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.buttonEvent = ButtonEvent()
self.bEventThread = setupAndConnectButtonEvent()
model = VideoTableModel("/home/user/Videos")
self.ui.videoView.setModel(model)
controller = VideoTableController(model, self.ui.videoView)
self.bEventThread.start()
def closeEvent(self, event):
self.buttonEvent.stopListening()
self.bEventThread.quit()
event.accept()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
buttonEvent = ButtonEvent()
myapp = Main()
myapp.show()
sys.exit(app.exec_())
答案 0 :(得分:1)
事实证明我只是犯了一个愚蠢的Python错误。信号正确发出,事件循环在所有线程中正常运行。我的问题是在我的Main.__init__
函数中我创建了一个VideoTableController
对象,但我没有在Main
中保留副本,所以我的controller
没有保留,这意味着插槽也离开了。将其更改为
self.controller = VideoTableController(model, self.ui.videoView)
一切都待在那里,插槽被正确调用。
故事的道德:它并不总是滥用图书馆,可能是对该语言的误用。
答案 1 :(得分:0)
似乎最快的解决方法是在此处更改您的ButtonEvent
代码:
...
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
...
到此:
@pyqtSlot()
def run(self):
print 'running'
while(self._isListening):
s = pylirc.nextcode()
if (s):
print 'emitting'
self.buttonPressed.emit(int(s[0]))
对此问题的简短解释是PyQt在内部使用代理,这样您就可以确保避免这种情况。毕竟,您的方法应该是基于connect
语句的插槽。
对......现在,我鼓励您考虑一下当前的软件设计。您似乎在专用线程中使用类来处理Qt按钮事件。这可能是个好主意,我不确定,但我至少没有见过这个。
我认为将来可以通过更好的方法完全摆脱该类,从按钮信号直接连接到处理程序插槽。但是,这不是你的专用线程中的run
“槽”,而是一个规范的处理程序。
引入更多复杂性(尤其是在多线程应用程序中)并不是一个好的设计实践。希望这会有所帮助。
答案 2 :(得分:-1)
我实际上没有对此进行测试(因为我无法访问您编译的UI文件),但我确信我是对的。
你的ButtonEvent的run方法(应该在一个线程中运行)很可能在mainthread中运行(你可以通过导入python threading
模块并添加行print threading.current_thread().name
来测试它要解决此问题,请使用@pyqtSlot()
如果仍然无法解决问题,请将上述print
语句添加到各个位置,直到找到主线程中不应运行的内容为止。下面列出的SO答案可能包含解决问题的答案。
有关详细信息,请参阅此答案:https://stackoverflow.com/a/20818401/1994235