我在使用PyQt4插槽/信号时遇到问题。在
我用的是Pyric,我在听遥控器上的按键。这部分我要在Qt之外工作。当从按钮监听线程发出信号并试图调用主线程中的插槽时,我的问题就来了。在
My button listener是一个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
插槽是按钮侦听器的内部插槽。在
要将按钮侦听器移动到另一个线程来执行此工作,我使用以下命令:
^{pr2}$然后在主线程中,我有我的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
类内部被正确调用,但是{
我四处看看,但什么也没找到。在阅读了You're doing it wrong之后,我改为使用QObject。如有任何帮助,我们将不胜感激。如果你还需要什么,请告诉我。在
编辑:感谢您的回复!这里有一大块代码给你们:
ButtonEvent(这个类使用singleton模式,请原谅代码编写不好,因为我对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
这是视频表控制器:
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_())
原来我只是犯了一个愚蠢的Python错误。信号正确发出,事件循环在所有线程中都正常运行。我的问题是,在我的
Main.__init__
函数中,我创建了一个VideoTableController
对象,但是我没有在Main
中保留一个副本,所以我的controller
没有持续存在,这意味着插槽也离开了。当把它换成所有的东西都在附近,而且插槽被正确地调用了。在
故事的寓意:这并不总是对图书馆的误用,它可能是对语言的误用。在
我还没有真正测试过这个(因为我无法访问您编译的UI文件),但我相当肯定我是对的。在
ButtonEvent的run方法(应该在线程中运行)可能在主线程中运行(可以通过导入python
threading
模块并添加print threading.current_thread().name
行来测试这一点。要解决这个问题,请用@pyqtSlot()
装饰run方法如果这不能解决这个问题,请将上面的
print
语句添加到不同的位置,直到找到不应该在主线程中运行的东西。下面一行的SO答案可能包含修复它的答案。在有关详细信息,请参阅以下答案:https://stackoverflow.com/a/20818401/1994235
似乎最快的解决方法是在此处更改
ButtonEvent
代码:为此:
^{pr2}$对这个问题的简短解释是PyQt在内部使用代理,这样可以确保避免这种情况。毕竟,您的方法应该是基于
connect
语句的slot。在好吧。。。现在,我鼓励您考虑一下您当前的软件设计。似乎您正在使用一个专用线程中的类来处理Qt按钮事件。这也许是个好主意,我不确定,但至少我以前没见过这个。在
我认为你将来可以用一种更好的方法将这个类从按钮信号直接连接到处理程序插槽。但是,这不是专用线程中的
run
“槽”,而是canonical处理程序。在在多线程应用程序中引入超出需要的复杂性并不是一个好的设计实践。希望这有帮助。在
相关问题 更多 >
编程相关推荐