QSocketNotifier 如何通知我管道已准备好读取?

Posted

技术标签:

【中文标题】QSocketNotifier 如何通知我管道已准备好读取?【英文标题】:How can I get signaled by QSocketNotifier that a pipe is ready for reading? 【发布时间】:2014-07-23 18:02:43 【问题描述】:

在 Windows 和 Unix 上使用 Python 3.4,我的意思是使用 QSocketNotifier 来发现管道已准备好读取。这是一种人为的例子,因为管道是在单个进程中使用的。无论哪种方式,问题的关键在于,当管道上有要阅读的内容时,我需要采取行动。

我想出了下面的演示程序,但是 QSocketNotifier 从不发出它的activated 信号。我错过了什么明显的东西吗?

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import os


def can_read(fd):
   print('File  can be read'.format(fd))


def start():
   print('Starting')
   reader, writer = os.pipe()
   notifier = QSocketNotifier(reader, QSocketNotifier.Read, w)
   notifier.setEnabled(True)
   notifier.activated.connect(can_read)
   os.write(writer, b'a')
   os.close(writer)


app = QApplication([])
QTimer.singleShot(0, start)
w = QMainWindow()
w.show()
app.exec_()

【问题讨论】:

你在管道上写过什么吗? @mdurant 在 start() 中有 os.write 调用。 【参考方案1】:

事实上,这并没有回答如何从可读管道接收通知,但它确实提供了一种跨平台的替代方案,即监控套接字(而不是管道):

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import socket

def _create_sock_pair(port=0):
    """Create socket pair.

    If socket.socketpair isn't available, we emulate it.
    """
    # See if socketpair() is available.
    have_socketpair = hasattr(socket, 'socketpair')
    if have_socketpair:
        client_sock, srv_sock = socket.socketpair()
        return client_sock, srv_sock

    # Create a non-blocking temporary server socket
    temp_srv_sock = socket.socket()
    temp_srv_sock.setblocking(False)
    temp_srv_sock.bind(('', port))
    port = temp_srv_sock.getsockname()[1]
    temp_srv_sock.listen(1)

    # Create non-blocking client socket
    client_sock = socket.socket()
    client_sock.setblocking(False)
    try:
        client_sock.connect(('localhost', port))
    except socket.error as err:
        # Error 10035 (operation would block) is not an error, as we're doing this with a
        # non-blocking socket.
        if err.errno != 10035:
            raise

    # Use select to wait for connect() to succeed.
    import select
    timeout = 1
    readable = select.select([temp_srv_sock], [], [], timeout)[0]
    if temp_srv_sock not in readable:
        raise Exception('Client socket not connected in  second(s)'.format(timeout))
    srv_sock, _ = temp_srv_sock.accept()

    return client_sock, srv_sock


def can_read():
    print('Server can read')
    app.quit()


def write():
    print('Client writing')
    client_sock.send(b'a')
    client_sock.close()


app = QApplication([])

client_sock, srv_sock = _create_sock_pair()
notifier = QSocketNotifier(srv_sock.fileno(), QSocketNotifier.Read)
notifier.activated.connect(can_read)
write()

w = QMainWindow()
w.show()
app.exec_()
srv_sock.close()

【讨论】:

socket.socketpair 在 Windows 上不可用。 抱歉,没有windows box,看来我无法为您测试解决方案。 对。不过不用担心,我可能只是找到了解决方案。 我已经粘贴了我的工作脚本,模拟为socket.socketpair 干得好!另一个想法:select 可以用于以非阻塞方式监视套接字,而不是 QSocketNotifier,在某些情况下可能更简单。【参考方案2】:

可能与让变量超出范围有关。如果您存储所有相关部分,例如包装在一个类中,它就可以工作。这是我的解决方案(使用 pyqt4,我很抱歉),它一直运行到窗口关闭:

from PyQt4.QtGui import *
from PyQt4.QtCore import *
import os

class win(QMainWindow):


    def can_read(self,fd):
       print('File  can be read'.format(fd))


    def start(self):
       print('Starting')
       self.reader, self.writer = os.pipe()
       notifier = QSocketNotifier(self.reader, QSocketNotifier.Read, self)
       notifier.setEnabled(True)
       notifier.activated.connect(self.can_read)
       os.write(self.writer, b'a')
       os.close(self.writer)


app = QApplication([])
#QTimer.singleShot(0, start)
w = win()
w.start()
w.show()
app.exec_()

【讨论】:

PS:这是在 linux 上的 - 如果管道在 windows 下的行为不同,我不会感到惊讶。 你说得对,reader, writer 超出范围,呃,但我仍然没有收到激活信号 :( 如果它对你有用,我猜这是由于平台差异。 您是否考虑过实际的套接字而不是操作系统管道?我确信这些可以在 Windows 上运行。 实际上是的,我刚刚间接地(通过 asyncio)玩过套接字,但也无法让它工作。如果你能提供一个可行的例子,我一定会尝试!

以上是关于QSocketNotifier 如何通知我管道已准备好读取?的主要内容,如果未能解决你的问题,请参考以下文章

QSocketNotifier - 如何使用它来观看文件? - linux

QSocketNotifier 与 nanomsg

带有 Qt 5.12 QSocketNotifier 的 ZeroMQ 只触发一次

如何从 GitLab 管道向 Telegram 发送通知?

Kubeflow 管道终止通知

通知 GitLab 中失败的管道的所有组成员