简单(但具体)的侦听器和发送器 Python 3 DBus 示例
Posted
技术标签:
【中文标题】简单(但具体)的侦听器和发送器 Python 3 DBus 示例【英文标题】:SImple (but specific) listener and sender Python 3 DBus example 【发布时间】:2014-03-14 16:02:00 【问题描述】:我想制作一个包含两个部分的程序。一个监听器(一个服务器,如果你愿意的话)和一个发送器(客户端)。我做了一些研究,了解到这是通过程序员称为 IPC(进程间通信)的方法完成的;我相信你知道它的意思,我只是扩展首字母缩写词,以便你知道我不认为它意味着 Internet Pet Cannibals(或其他一些不相关的不愉快事物)。
我读到,实现这一目标的一个好方法是使用 dbus。所以我对dbus做了一些研究,现在我很困惑。显然你可以用 dbus 做很多事情,比如向 Gnome Shell 发送通知或与网络管理器交谈。我不想做那些事!我只想制作两个相互对话的简单程序。除此之外,一些教程和文档展示了 python 2 的示例,一些使用 3,一些 import dbus 和一些 import Gio!我发现的很多信息都在我的脑海中,这也影响了我的努力。
谁能给我展示一个简单而优雅的例子,说明如何制作一个基本上可以做到这一点的程序:
$ ./server
Server is not running yet. Putting on listening ears.
$ ./client Hi
server: a client said "Hi"
$ ./server
Server is already running.
$ ./server stop
Server exiting...
$ ./client Do a barrel roll
client: No one can hear me!!
这就是简单会话的方式(当然使用 bash shell)。我想使用 Python 3 和目前最合适的任何 dbus 绑定(我猜那将是 gi.repository)。澄清一下,这适用于 Linux。
【问题讨论】:
我认为您不想通过 dbus 执行此操作。您可以使用常规网络库来实现相同的目标。你到底想达到什么目的? 我基本上是在尝试制作一个可以通过命令控制的音乐播放器程序。所以一个更新的例子是:./player play favorites
,然后如果我想去下一首歌,我会做./player-remote next track
。这样就有一个界面可以从其他程序控制音乐播放器,通过 ssh 等。这也是为了帮助我了解更多并开始使用 dbus,但这对我来说不是优先事项,特别是如果它导致我采取错误的方法来解决这个问题。
对于这样的用例,dbus肯定是你想要的,但我怕我不精通。
【参考方案1】:
在 python3 中没有很多关于 dbus 的文档,但我设法弄清楚了,所以我将在此处记录:与所有 python2 示例的主要区别是将import gobject
替换为import gi.repository.GLib
。
您可以在the dbus-python examples directory 中找到更多示例(使用的功能比我需要的更多)。
我没有在服务器中实现自后台,因为这种风格的守护进程最近已经过时了。
common.py:
# well-known name for our program
ECHO_BUS_NAME = 'com.***.question_21793826.EchoService'
# interfaces implemented by some objects in our program
ECHO_INTERFACE = 'com.***.question_21793826.EchoInterface'
QUIT_INTERFACE = 'com.***.question_21793826.QuitInterface'
# paths to some objects in our program
ECHO_OBJECT_PATH = '/EchoServerObject'
server.py:
#!/usr/bin/env python3
# standard includes
import sys
# dbus includes
import gi.repository.GLib
import dbus
import dbus.service
import dbus.mainloop.glib
# project includes
import common
class EchoServerObject(dbus.service.Object):
# TODO it would be nice to make a better decorator using annotations:
# def foo(self, a: 's', b: 's') -> '': pass
# but the existing dbus decorator does its own reflection which
# fails if there are any annotations (or keyword-only arguments)
@dbus.service.method(common.ECHO_INTERFACE,
in_signature='s', out_signature='')
def echo(self, message):
message = str(message) # get rid of subclass for repr
print('server: a client said %r' % message)
@dbus.service.method(common.QUIT_INTERFACE,
in_signature='', out_signature='')
def quit(self):
# this should be a separate object, but I'm
# showing how one object can have multiple interfaces
self.mainloop.quit()
def stop():
bus = dbus.SessionBus()
proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
iface = dbus.Interface(proxy, common.QUIT_INTERFACE)
iface.quit()
def server():
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
try:
name = dbus.service.BusName(common.ECHO_BUS_NAME, bus, do_not_queue=True)
except dbus.NameExistsException:
sys.exit('Server is already running.')
else:
print('Server is not running yet. Putting on listening ears.')
echo = EchoServerObject(bus, common.ECHO_OBJECT_PATH)
mainloop = gi.repository.GLib.MainLoop()
echo.mainloop = mainloop
mainloop.run()
def main(exe, args):
if args == ['stop']:
stop()
elif not args:
server()
else:
sys.exit('Usage: %s [stop]' % exe)
if __name__ == '__main__':
main(sys.argv[0], sys.argv[1:])
client.py:
#!/usr/bin/env python3
# standard includes
import sys
# dbus includes
import dbus
# project includes
import common
def client(mes):
bus = dbus.SessionBus()
try:
proxy = bus.get_object(common.ECHO_BUS_NAME, common.ECHO_OBJECT_PATH)
except dbus.DBusException as e:
# There are actually two exceptions thrown:
# 1: org.freedesktop.DBus.Error.NameHasNoOwner
# (when the name is not registered by any running process)
# 2: org.freedesktop.DBus.Error.ServiceUnknown
# (during auto-activation since there is no .service file)
# TODO figure out how to suppress the activation attempt
# also, there *has* to be a better way of managing exceptions
if e._dbus_error_name != 'org.freedesktop.DBus.Error.ServiceUnknown':
raise
if e.__context__._dbus_error_name != 'org.freedesktop.DBus.Error.NameHasNoOwner':
raise
print('client: No one can hear me!!')
else:
iface = dbus.Interface(proxy, common.ECHO_INTERFACE)
iface.echo(mes)
def main(exe, args):
if args:
client(' '.join(args))
else:
sys.exit('Usage: %s message...' % exe)
if __name__ == '__main__':
main(sys.argv[0], sys.argv[1:])
【讨论】:
这正是我想要的。谢谢!以上是关于简单(但具体)的侦听器和发送器 Python 3 DBus 示例的主要内容,如果未能解决你的问题,请参考以下文章