简体   繁体   English

如何从非 PyQt 类发出信号?

[英]how to emit signal from a non PyQt class?

i'm programming an application in python using twisted and PyQt .我正在使用 Twisted 和 PyQt 在 python 中编写应用程序。 the problem that i'm facing is that when a function in my twisted code is executed i have to print a line in the GUI, i'm trying to achieve this by emiting a signal (Non PyQt class).我面临的问题是,当我扭曲代码中的一个函数被执行时,我必须在 GUI 中打印一行,我试图通过发出一个信号(非 PyQt 类)来实现这一点。 This does not seem to work, i have a doubt that the twisted event loop is screwing things for PyQt.这似乎不起作用,我怀疑扭曲的事件循环正在为 PyQt 搞砸事情。 Because the closeEvent signal is not being trapped by the program.因为 closeEvent 信号没有被程序捕获。

Here's the code snippet:这是代码片段:

from PyQt4 import QtGui, QtCore
import sys
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols import amp
import qt4reactor

class register_procedure(amp.Command):
    arguments = [('MAC',amp.String()),
                        ('IP',amp.String()),
                        ('Computer_Name',amp.String()),
                        ('OS',amp.String())
                        ]
    response = [('req_status', amp.String()),
         ('ALIGN_FUNCTION', amp.String()),
                         ('ALIGN_Confirmation', amp.Integer()),
                         ('Callback_offset',amp.Integer())
                        ]

class Ui_MainWindow(QtGui.QMainWindow):

    def __init__(self,reactor, parent=None):
        super(Ui_MainWindow,self).__init__(parent)
        self.reactor=reactor
        self.pf = Factory()
        self.pf.protocol = Protocol
        self.reactor.listenTCP(3610, self.pf) # listen on port 1234

        def setupUi(self,MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(903, 677)
        self.centralwidget = QtGui.QWidget(MainWindow)
        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
        self.centralwidget.setSizePolicy(sizePolicy)

        self.create_item()


        self.retranslateUi(MainWindow)
        self.connect(self, QtCore.SIGNAL('triggered()'), self.closeEvent)
        QtCore.QObject.connect(self,QtCore.SIGNAL('registered()'),self.registered)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.pushButton_4.setText(_translate("MainWindow", "Delete System ", None))
        self.pushButton.setText(_translate("MainWindow", "Add System", None))
        self.label_2.setText(_translate("MainWindow", "SYSTEM STATUS", None))
        self.label.setText(_translate("MainWindow", "Monitoring Output", None))


    def registered(self):# this function is not being triggered
        print "check" 
        self.textbrowser.append()

    def closeEvent(self, event):#neither is this being triggered
        print "asdf"
        self.rector.stop()
        MainWindow.close()
        event.accept()


class Protocol(amp.AMP):
    @register_procedure.responder
    def register_procedure(self,MAC,IP,Computer_Name,OS):
        self.bridge_conn=bridge()
        cursor_device.execute("""select * FROM devices where MAC = ?;""",[(MAC)])
        exists_val=cursor_device.fetchone()
        cursor_device.fetchone()
        print "register"
        if not exists_val== "":
            cursor_device.execute("""update devices set IP= ? , Computer_name= ? , OS = ?  where MAC= ?;""",[IP,Computer_Name,OS,MAC])
            QtCore.QObject.emit( QtCore.SIGNAL('registered')) # <--emits signal
            return {'req_status': "done" ,'ALIGN_FUNCTION':'none','ALIGN_Confirmation':0,'Callback_offset':call_offset(1)}
        else:
            cursor_device.execute("""INSERT INTO devices(Mac,Ip,Computer_name,Os) values (?,?,?,?);""",[MAC,IP,Computer_Name,OS])
            QtCore.QObject.emit( QtCore.SIGNAL('registered'))#<--emits signal
            return {'req_status': "done" ,'ALIGN_FUNCTION':'main_loop()','ALIGN_Confirmation':0,'Callback_offset':0}



if  __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)

    try:
        import qt4reactor
    except ImportError:
        from twisted.internet import qt4reactor
    qt4reactor.install()

    from twisted.internet import reactor
    MainWindow = QtGui.QMainWindow() # <-- Instantiate QMainWindow object.
    ui = Ui_MainWindow(reactor)
    ui.setupUi(MainWindow)
    MainWindow.show()
    reactor.run()

This is what I use in my one code for sending signals from QGraphicsItems (because they are not derived from QObject and cannot send/receive signals by default). 这是我在我的一个代码中用于从QGraphicsItems发送信号的原因(因为它们不是从QObject派生的,并且默认情况下不能发送/接收信号)。 It's basically a simplified version of Radio-'s answer. 它基本上是Radio-answer的简化版本。

from PyQt4 import QtGui as QG
from PyQt4 import QtCore as QC

class SenderObject(QC.QObject):
    something_happened = QC.pyqtSignal()

SenderObject is a tiny class derived from QObject where you can put all the signals you need to emit. SenderObject是一个从QObject派生的小类,您可以在其中放置所需的所有信号。 In this case only one is defined. 在这种情况下,只定义了一个。

class SnapROIItem(QG.QGraphicsRectItem):
    def __init__(self, parent = None):
        super(SnapROIItem, self).__init__(parent)
        self.sender = SenderObject()
    def do_something_and_emit(self):
        ...
        self.sender.something_happened.emit()

In the non-QObject class you add a SenderObject as a sender variable. 在非QObject类中,您将SenderObject添加为sender变量。 Anywhere where the non-QObject class is used you can connect the signal from the sender to anything you need. 在使用非QObject类的任何地方,您都可以将sender的信号连接到您需要的任何信号。

class ROIManager(QC.QObject):
    def add_snaproi(self, snaproi):
        snaproi.sender.something_happened.connect(...)

UPDATE UPDATE

The full code is this and should print out "Something happened...": 完整的代码是这个,应该打印出“发生了什么......”:

from PyQt4 import QtGui as QG
from PyQt4 import QtCore as QC

class SenderObject(QC.QObject):
    something_happened = QC.pyqtSignal()

class SnapROIItem(QG.QGraphicsItem):
    def __init__(self, parent = None):
        super(SnapROIItem, self).__init__(parent)
        self.sender = SenderObject()
    def do_something_and_emit(self):
        self.sender.something_happened.emit()

class ROIManager(QC.QObject):
    def __init__(self, parent=None):
        super(ROIManager,self).__init__(parent)
    def add_snaproi(self, snaproi):
        snaproi.sender.something_happened.connect(self.new_roi)
    def new_roi(self):
        print 'Something happened in ROI!'

if __name__=="__main__":)
    roimanager = ROIManager()
    snaproi = SnapROIItem()
    roimanager.add_snaproi(snaproi)
    snaproi.do_something_and_emit()

UPDATE 2 更新2

Instead of 代替

QtCore.QObject.connect(self,QtCore.SIGNAL('registered()'),self.registered) 

you should have: 你应该有:

protocol.sender.registered.connect(self.registered)

This means you also need to get hold of the protocol instance in self.pf (by the way, do you import Protocol and then define it yourself as well?) 这意味着您还需要在self.pf获取protocol实例(顺便说一句,您是否导入Protocol ,然后自己定义它?)

In the Protocol class instead of 在Protocol类而不是

QtCore.QObject.emit( QtCore.SIGNAL('registered')

you need to, first, instantiate a SenderObject in the Protocol. 首先,您需要在协议中实例化SenderObject。

class Protocol(amp.AMP):
    def __init__( self, *args, **kw ):
       super(Protocol, self).__init__(*args, **kw)
       self.sender = SenderObject()

and then, in register_procedure emit the signal through sender : self.sender.registered.emit() 然后,在register_procedure通过sender发出信号:self.sender.registered.emit()

For all of this to work you'll have to have defined SenderObject as: 要使所有这些工作,您必须将SenderObject定义为:

class SenderObject(QC.QObject):
    registered = QC.pyqtSignal()

This is an old post, but it helped me. 这是一个老帖子,但它帮了我。 Here is my version. 这是我的版本。 One item that is not a QObject signaling two other non QObject's to run their methods. 一个不是QObject的项目用信号通知另外两个非QObject来运行它们的方法。

from PyQt4 import QtGui, QtCore

class Signal(object):
    class Emitter(QtCore.QObject):
        registered = QtCore.pyqtSignal()
        def __init__(self):
            super(Signal.Emitter, self).__init__()

    def __init__(self):
        self.emitter = Signal.Emitter()

    def register(self):
        self.emitter.registered.emit()

    def connect(self, signal, slot):
        signal.emitter.registered.connect(slot)

class item(object):
    def __init__(self, name):
        self.name = name
        self.signal = Signal()

    def something(self):
        print self.name, ' says something'

>>> itemA = item('a')
>>> itemB = item('b')
>>> itemC = item('c')
>>> itemA.signal.connect(itemA.signal, itemB.something)
>>> itemA.signal.connect(itemA.signal, itemC.something)
>>> itemA.signal.register()

b  says something
c  says something

The two basic problems are that: 两个基本问题是:

1) Something has to know the sender and receiver of the signals 1)必须知道信号的发送者和接收者

Consider a more frequent case in Qt where you may have multiple buttons, each with a 'clicked' signal. 考虑Qt中更频繁的情况,您可能有多个按钮,每个按钮都有一个“点击”信号。 Slots need to know which button was clicked, so catching a generic signal does not make much sense. 插槽需要知道单击了哪个按钮,因此捕获通用信号没有多大意义。

and 2) The signals have to originate from a QObject. 2)信号必须来自QObject。


Having said that, I'm not sure what the canonical implementation is. 话虽如此,我不确定规范实现是什么。 Here is one way to do it, using the idea of a Bridge that you had in one of your earlier posts, and a special Emitter class inside of Protocol. 这是一种方法,使用您在之前的帖子中拥有的Bridge的概念,以及Protocol内部的特殊Emitter类。 Running this code will simply print 'Working it' when protocol.register() is called. 运行此代码只会在调用protocol.register()时打印“Working it”。

from PyQt4 import QtGui, QtCore
import sys

class Ui_MainWindow(QtGui.QMainWindow):
    def __init__(self):
        super(Ui_MainWindow, self).__init__()

    def work(self):
        print "Working it"

class Protocol(object):
    class Emitter(QtCore.QObject):
        registered = QtCore.pyqtSignal()
        def __init__(self):
            super(Protocol.Emitter, self).__init__()

    def __init__(self):
        self.emitter = Protocol.Emitter()

    def register(self):
        self.emitter.registered.emit()

class Bridge(QtCore.QObject):
    def __init__(self, gui, protocol):
        super(Bridge, self).__init__()
        self.gui = gui
        self.protocol = protocol

    def bridge(self):
        self.protocol.emitter.registered.connect(self.gui.work)

app = QtGui.QApplication(sys.argv)
gui = Ui_MainWindow()
protocol = Protocol()
bridge = Bridge(gui, protocol)
bridge.bridge()
#protocol.register() #uncomment to see 'Working it' printed to the console

I improved the solution of @Ryan Trowbridge a bit to deal with a generic type.我改进了@Ryan Trowbridge 的解决方案来处理泛型类型。 I hoped to use Generic[T] but turned out too complicated.我希望使用Generic[T]但结果太复杂了。

class GenSignal:
    def __init__(self,typ):
        Emitter = type('Emitter', (QtCore.QObject,), {'signal': Signal(typ)})
        self.emitter = Emitter()

    def emit(self,*args,**kw):
        self.emitter.signal.emit(*args,**kw)

    def connect(self,  slot):
        self.emitter.signal.connect(slot)

To use, x=GenSignal(int) and continue as usual.要使用, x=GenSignal(int)并照常继续。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM