Success!! After centuries of battle with many things, I was able to make a working example ! Yeah! I have included the source code below.
server.py
from gi.repository import Gio, GLib
# Thanks to Gio test files at GNOME repo: glib/blob/master/gio/tests/gdbus-example-server.c
# Generating interface from XML is easy. Look at the above file on how it is done.
# A better resource : http://maemo.org/maemo_training_material/maemo4.x/html/
# maemo_Platform_Development_Chinook/
# Chapter_03_Using_the_GLib_wrappers_for_DBus.html
# #DBusinterfacedefinitionusingXML
xml = (
"<node>"
" <interface name='org.examples.gdbus'>"
" <method name='PrintMsg'>"
" <arg type='s' name='msg' direction='in'>"
" </arg>"
" </method>"
" <method name='SayHello'>"
" <arg type='s' name='name' direction='in'/>"
" <arg type='s' name='greeting' direction='out'/>"
" </method>"
" <method name='Quit'/>"
" </interface>"
"</node>"
)
node = Gio.DBusNodeInfo.new_for_xml(xml) # We make a node for the xml
loop = GLib.MainLoop() # A loop to handle API
def handle_method_call(
connection, sender, object_path, interface_name, method_name, params, invocation
):
"""
This is the top-level function that handles all the method calls to our server.
The first four parameters are self-explanatory.
`method_name` is a string that describes our method name.
`params` is a GLib.Variant that are inputs/parameters to the method.
`invocation` is a Gio.DBusMethodInvocation, something like a messenger that transports
our reply back to sender.
"""
print("CALLED")
# We need to unpack GLib.Variant to a Python object. The unpacked one is always a
# tuple.
if method_name == "PrintMsg":
msg = params.unpack()[0] # First argument is what we need
print(f"FROM {sender} : {msg}")
invocation.return_value(None) # Nothing to say, so just return None.
elif method_name == "SayHello":
name = params.unpack()[0]
print(f"FROM {sender} : GREET {name}")
greeting = GLib.Variant(
"(s)", (f"Long live {name}",)
) # All form of return should be a
# variant.
invocation.return_value(greeting)
elif method_name == "Quit":
loop.quit()
invocation.return_value(None)
# No need of an else part to handle methods which we didn't mention in XML.
# When a client does something like that, they get an error.
# The same holds for parameters.
# Always return something (actually return value specified in XML) via invocation,
# otherwise client get a response-timeout error.
def on_bus_acquired(connection, name):
"""
The function that introduces our server to the world. It is called automatically
when we get the bus we asked.
"""
# Remember the node we made earlier? That has a list as interfaces attribute.
# From that get our interface. We made only one interface, so we get the first
# interface.
print("Bus acquired for name, ", name)
reg_id = connection.register_object(
"/org/examples/gdbus", node.interfaces[0], handle_method_call, None, None
)
def on_name_acquired(connection, name):
"""
What to do after name acquired?
"""
print("Name acquired :", name)
def on_name_lost(connection, name):
"""
What to do after our name is lost? May be just exit.
"""
print("Name lost :", name)
exit(0)
if __name__ == "__main__":
# Now we request the name and run the server.
owner_id = Gio.bus_own_name(
Gio.BusType.SESSION, # What kinda bus is it?
"org.examples.gdbus", # It's name ?
Gio.BusNameOwnerFlags.NONE, # If other has same name, what to do?
on_bus_acquired,
on_name_acquired,
on_name_lost,
)
loop.run() # Like a web server, ours has to be active for client access
Gio.bus_unown_name(owner_id)
print("Exiting...")
client.py
from gi.repository import Gio, GLib
# Client is pretty straight-forward
bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) # What is the bus type?
# Below we are doing something like, 'Hey Gio, heard there is a DBus server
# with name org.examples.gdbus and type SESSION, get me a connection to it'
proxy = Gio.DBusProxy.new_sync(
bus,
Gio.DBusProxyFlags.NONE,
None,
"org.examples.gdbus",
"/org/examples/gdbus",
"org.examples.gdbus",
None,
)
# proxy.call_sync is the way to call the method names of server.
def print_msg(msg):
var = GLib.Variant("(s)", (msg,)) # Parameters should be variant.
proxy.call_sync(
"PrintMsg", # Method name
var, # Parameters for method
Gio.DBusCallFlags.NO_AUTO_START, # Flags for call APIs
500, # How long to wait for reply? (in milliseconds)
None, # Cancellable, to cancel the call if you changed mind in middle)
)
def say_hello(name):
var = GLib.Variant("(s)", (name,))
ret_var = proxy.call_sync(
"SayHello", var, Gio.DBusCallFlags.NO_AUTO_START, 500, None
)
greeting = ret_var.unpack()[0]
print(greeting)
def quit_server():
ret_var = proxy.call_sync(
"Quit", None, Gio.DBusCallFlags.NO_AUTO_START, 500, None
)
print("Server quit")
Fire up terminal and run server.py
like python3 server.py
. Client is an interactive one, so run it like this : python3 -i client.py
. Then in interactive prompt, do whatever you want with defined methods.
>>> say_hello("GTK")
Long live GTK
>>> print_msg("It's fun") # Go to server.py console to see message
>>> quit_server() # Quits server
Server quit
Never thought this would be so easy ! This bare-bone example covers only method calls. So in my future research, I will learn and cover about signals, authentication etc.
Also we need this example (or a better one) to be available for Python GDBus in docs and other places. So I will also take a look at that.
Thanks @ebassi @lb90 @sthursfield @curioussavage and other wonderful contributors of GNOME