如何在GTK/QT/Clutter应用程序中使用ZeroMQ?
在应用程序中,所有执行都在如何在GTK/QT/Clutter应用程序中使用ZeroMQ?,gtk,zeromq,Gtk,Zeromq,在应用程序中,所有执行都在gtk_main函数中进行。其他图形框架作品也有类似的事件循环,比如for的app.exec和croot\u mainfor。然而,它是基于这样一种假设,即存在一个while(1)…循环,它被插入其中(例如,请参见示例) 如何将这两种执行策略结合起来 我目前想在用C编写的杂乱应用程序中使用zeromq,因此我当然希望直接回答这个问题,但也请为其他变体添加答案。听起来zeromq代码只是希望尽可能频繁地反复执行。最简单的方法是将ZeroMQ代码放入空闲函数或超时函数中,并
gtk_main
函数中进行。其他图形框架作品也有类似的事件循环,比如for的app.exec
和croot\u main
for。然而,它是基于这样一种假设,即存在一个while(1)…
循环,它被插入其中(例如,请参见示例)
如何将这两种执行策略结合起来
我目前想在用C编写的杂乱应用程序中使用zeromq,因此我当然希望直接回答这个问题,但也请为其他变体添加答案。听起来zeromq代码只是希望尽可能频繁地反复执行。最简单的方法是将ZeroMQ代码放入空闲函数或超时函数中,并使用函数的非阻塞版本(如果存在) 对于杂乱,您可以使用或。对于GTK,您将使用或
更困难但可能更好的方法是使用为ZeroMQ代码创建一个单独的线程,并且只使用
while(1)
构造和它们建议的阻塞函数。如果您这样做,您还必须找到一些方法让线程彼此通信——GLib的互斥锁和异步队列通常都可以。从源头上看,集成的核心是:
ZmqSocket::ZmqSocket(int type, QObject *parent) : QObject(parent)
{
...
notifier_ = new QSocketNotifier(fd, QSocketNotifier::Read, this);
connect(notifier_, SIGNAL(activated(int)), this, SLOT(activity()));
}
...
void ZmqSocket::activity()
{
uint32_t flags;
size_t size = sizeof(flags);
if(!getOpt(ZMQ_EVENTS, &flags, &size)) {
qWarning("Error reading ZMQ_EVENTS in ZMQSocket::activity");
return;
}
if(flags & ZMQ_POLLIN) {
emit readyRead();
}
if(flags & ZMQ_POLLOUT) {
emit readyWrite();
}
...
}
因此,它依赖于QT的集成套接字处理,而杂波将不会有类似的东西。您可以获得0MQ套接字的文件描述符(ZMQ_FD选项),并将其与事件循环集成。我认为gtk有一些处理套接字的机制。结合zmq和gtk或杂波的正确方法是将zmq队列的文件描述符连接到主事件循环。fd可以通过使用
int fd;
size_t sizeof_fd = sizeof(fd);
if(zmq_getsockopt(socket, ZMQ_FD, &fd, &sizeof_fd))
perror("retrieving zmq fd");
将其连接到主循环是使用io_add_watch的问题:
GIOChannel* channel = g_io_channel_unix_new(fd);
g_io_add_watch(channel, G_IO_IN|G_IO_ERR|G_IO_HUP, callback_func, NULL);
在回调函数中,在读取之前,有必要首先检查是否有真正需要读取的内容。否则,函数可能会阻止等待IO
gboolean callback_func(GIOChannel *source, GIOCondition condition,gpointer data)
{
uint32_t status;
size_t sizeof_status = sizeof(status);
while (1){
if (zmq_getsockopt(socket, ZMQ_EVENTS, &status, &sizeof_status)) {
perror("retrieving event status");
return 0; // this just removes the callback, but probably
// different error handling should be implemented
}
if (status & ZMQ_POLLIN == 0) {
break;
}
// retrieve one message here
}
return 1; // keep the callback active
}
请注意:这实际上并没有经过测试,我用Python+Clutter做了一个翻译,这就是我所使用的,但我非常确定它会工作。
下面是实际工作的完整Python+杂波代码供参考
import sys
from gi.repository import Clutter, GObject
import zmq
def Stage():
"A Stage with a red spinning rectangle"
stage = Clutter.Stage()
stage.set_size(400, 400)
rect = Clutter.Rectangle()
color = Clutter.Color()
color.from_string('red')
rect.set_color(color)
rect.set_size(100, 100)
rect.set_position(150, 150)
timeline = Clutter.Timeline.new(3000)
timeline.set_loop(True)
alpha = Clutter.Alpha.new_full(timeline, Clutter.AnimationMode.EASE_IN_OUT_SINE)
rotate_behaviour = Clutter.BehaviourRotate.new(
alpha,
Clutter.RotateAxis.Z_AXIS,
Clutter.RotateDirection.CW,
0.0, 359.0)
rotate_behaviour.apply(rect)
timeline.start()
stage.add_actor(rect)
stage.show_all()
stage.connect('destroy', lambda stage: Clutter.main_quit())
return stage, rotate_behaviour
def Socket(address):
ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)
sock.setsockopt(zmq.SUBSCRIBE, "")
sock.connect(address)
return sock
def zmq_callback(queue, condition, sock):
print 'zmq_callback', queue, condition, sock
while sock.getsockopt(zmq.EVENTS) & zmq.POLLIN:
observed = sock.recv()
print observed
return True
def main():
res, args = Clutter.init(sys.argv)
if res != Clutter.InitError.SUCCESS:
return 1
stage, rotate_behaviour = Stage()
sock = Socket(sys.argv[2])
zmq_fd = sock.getsockopt(zmq.FD)
GObject.io_add_watch(zmq_fd,
GObject.IO_IN|GObject.IO_ERR|GObject.IO_HUP,
zmq_callback, sock)
return Clutter.main()
if __name__ == '__main__':
sys.exit(main())
这是Python中使用PyQt4的一个示例。它是从一个工作应用程序派生的
import zmq
from PyQt4 import QtCore, QtGui
class QZmqSocketNotifier( QtCore.QSocketNotifier ):
""" Provides Qt event notifier for ZMQ socket events """
def __init__( self, zmq_sock, event_type, parent=None ):
"""
Parameters:
----------
zmq_sock : zmq.Socket
The ZMQ socket to listen on. Must already be connected or bound to a socket address.
event_type : QtSocketNotifier.Type
Event type to listen for, as described in documentation for QtSocketNotifier.
"""
super( QZmqSocketNotifier, self ).__init__( zmq_sock.getsockopt(zmq.FD), event_type, parent )
class Server(QtGui.QFrame):
def __init__(self, topics, port, mainwindow, parent=None):
super(Server, self).__init__(parent)
self._PORT = port
# Create notifier to handle ZMQ socket events coming from client
self._zmq_context = zmq.Context()
self._zmq_sock = self._zmq_context.socket( zmq.SUB )
self._zmq_sock.bind( "tcp://*:" + self._PORT )
for topic in topics:
self._zmq_sock.setsockopt( zmq.SUBSCRIBE, topic )
self._zmq_notifier = QZmqSocketNotifier( self._zmq_sock, QtCore.QSocketNotifier.Read )
# connect signals and slots
self._zmq_notifier.activated.connect( self._onZmqMsgRecv )
mainwindow.quit.connect( self._onQuit )
@QtCore.pyqtSlot()
def _onZmqMsgRecv():
self._test_info_notifier.setEnabled(False)
# Verify that there's data in the stream
sock_status = self._zmq_sock.getsockopt( zmq.EVENTS )
if sock_status == zmq.POLLIN:
msg = self._zmq_sock.recv_multipart()
topic = msg[0]
callback = self._topic_map[ topic ]
callback( msg )
self._zmq_notifier.setEnabled(True)
self._zmq_sock.getsockopt(zmq.EVENTS)
def _onQuit(self):
self._zmq_notifier.activated.disconnect( self._onZmqMsgRecv )
self._zmq_notifier.setEnabled(False)
del self._zmq_notifier
self._zmq_context.destroy(0)
根据的文档,禁用并重新启用\u ZmqMsgRecv上的中的通知程序
由于某些原因,对getsockopt的最后调用是必要的。否则,通知程序将在第一个事件后停止工作。实际上,我正准备为此发布一个新问题。有人知道为什么需要这样做吗
请注意,如果在ZMQ上下文之前不销毁通知程序,则退出应用程序时可能会出现如下错误:
QSocketNotifier: Invalid socket 16 and type 'Read', disabling...
在空闲计时器中轮询zeromq不会导致100%的CPU使用率吗?在空闲函数中,可能是的,至少在GTK主循环不执行任何其他操作时是这样。在超时函数中,不可以。您可能还想看看——ZeroMQ的另一个Qt绑定。在这里,您将找到一个基于轮询的实现。特别是看看这个班。也许你可以对杂乱做一些类似的事情。注意这对于io\u add\u watch回调必须返回True很重要。如果没有这个,回调将只被调用一次。这是一个非常旧的线程,但是当您搜索gtk和zeromq时,它是最重要的结果,这是最好的答案。我想确认,您用于组合zmq和gtk的代码确实有效,但它应该是(status&zmq_POLLIN)==0
,而不是status&zmq_POLLIN==0
。有了这个改变,它就完美地工作了。