Jak odczytać wyjście monitora-dbus?

20

Mam zabawy z dbus-monitora , aby spróbować zrozumieć, jak dbus pracuje w środowisku Ubuntu. Mam kilka pytań w tym zakresie:

  1. Czy możesz dać mi znać, jak prawidłowo przeczytać poniższe informacje? Rozumiem wielki pomysł, ale nie szczegóły.

    signal sender=:1.1948 -> dest=(null destination) serial=1829990 path=/org/ayatana/menu/DA00003; interface=org.ayatana.dbusmenu; member=ItemPropertyUpdated
    int32 23
    string "enabled"
    variant boolean true
    method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1399 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications;
    member=GetCapabilities

    Rozumiem, że pierwszy to sygnał, a drugi to metoda. Czy przeznaczenie oznacza, że ​​może istnieć określony odbiornik / gniazdo dla sygnału? Co jest członkiem ? I czy pozycje na liście po sygnale przekazywane są w argumencie? Kim są nadawcy i seriale ?

  2. Zauważyłem coś o związku między regulacją głośności a powiadomieniami. Z tego, co przeczytałem z wyjścia monitora dbus

    method call sender=:1.6 -> dest=org.freedesktop.Notifications serial=1400 path=/org/freedesktop/Notifications; interface=org.freedesktop.Notifications; member=Notify
    string "gnome-settings-daemon"
    uint32 0
    string "notification-audio-volume-medium"
    string " "
    string ""
    array [
    ]
    array [
    dict entry(
    string "value"
    variant int32 38
    )
    dict entry(
    string "x-canonical-private-synchronous"
    variant string "volume"
    )
    ]
    int32 -1

    Wygląda na to, że powiadomienie jest uruchamiane za pomocą tej metody. Po prostu tak naprawdę nie rozumiem, dlaczego to działa w ten sposób. Moim zdaniem bardziej sensowne byłoby, gdyby emitowany był sygnał „powiadomienie-audio-objętość-nośnik”, podczas gdy powiadomienie nasłuchiwałoby tego sygnału i odpowiednio zareagowałoby. Jeśli wysyłanie / odbieranie byłoby publiczne, a nie prywatne, czy nie pozwoliłoby to na większą elastyczność i wydajność? Na przykład, jeśli istniał publiczny sygnał dla „powiadomienia-audio-głośności-medium” następnie kilka aplikacji mogłoby nasłuchiwać tego sygnału (co pozwoliłoby na powstanie konkurencyjnych aplikacji powiadamiających), a programiści musieliby się martwić wysyłaniem sygnałów, podczas gdy odbiór i obsługa sygnału byłaby działalnością aplikacji powiadamiającej (lub jakiejkolwiek innej) program, który potrzebuje tych sygnałów).

  3. Jestem dopiero nowym użytkownikiem Dbus i chcę dowiedzieć się więcej, pracując z Dbus nad Pythonem, głównie w celu opracowania niektórych apletów. Widziałem samouczek dbus-python, który uczy, jak słuchać wszystkich sygnałów (nie określając ani interfejsu, ani ścieżki itp.) Ale jak śledzić metody, gdy są one wywoływane, tak jak robi to dbus-monitor?

Jeśli masz cierpliwość, aby nauczyć, jak to działa, nie ma za co.

Benzoes
źródło

Odpowiedzi:

24

Wprowadzenie do D-Bus

  • D-Bus zapewnia środki komunikacji między usługami . Usługi mogą być anonimowe (identyfikowane wyłącznie na podstawie adresu autobusu, np .: 1.6), a usługi mogą uzyskiwać znane nazwy , takie jak org.freedesktop.Notificationslub org.freedesktop.NetworkManager. Nadawcą i miejscem docelowym, które można zobaczyć w dziennikach, są usługi. „Null destination” oznacza transmisję: dostarczenie do wszystkich usług.

  • Usługa może wyeksportować jeden lub kilka obiektów do magistrali. Obiekty otrzymują ścieżki obiektów , takie jak /org/freedesktop/NetworkManager/ActiveConnection/1lub /org/ayatana/menu/DA00003. Ścieżki obiektów używają ukośnika jako separatora, podobnie jak ścieżki systemu plików.

  • Każdy obiekt może obsługiwać jeden lub kilka interfejsów . Interfejs to nic innego jak zestaw metod i sygnałów, zwany potocznie członkami (bardzo podobny do interfejsu OOP). Metody i sygnały mają stałe podpisy. Członkowie są zawsze rozmieszczeni w obrębie dobrze znanych nazw interfejsów.

  • Po opublikowaniu znane nazwiska nigdy się nie zmieniają .

  • Każda usługa może połączyć się z sygnałami innej usługi i asynchronicznie wywołać swoje metody. Każda usługa może emitować sygnały.

Sygnały

Teraz do twoich konkretnych pytań.

nadawca sygnału =: 1,1948 -> dest = (puste miejsce docelowe) serial = 1829990 ścieżka = / org / ajatana / menu / DA00003; interfejs = org.ayatana.dbusmenu; member = ItemPropertyUpdated
int32 23
ciąg „włączony”
wariant boolean true

Tak, masz rację, to jest sygnał. Jest nadawany przez usługę :1.1948, a obiekt „ja” to /org/ayatana/menu/DA00003. Sygnał ma nazwę ItemPropertyUpdatedzdefiniowaną w interfejsie org.ayatana.dbusmenu(jak org.ayatana.dbusmenu::ItemPropertyUpdatedw C ++). Wydaje mi się, że serial jest swego rodzaju unikalnym identyfikatorem zdarzenia w autobusie.

Następnie widzimy argumenty sygnałowe. Zgodnie z dokumentacją interfejsu , pierwszy argument int32 to identyfikator elementu, drugi ciąg to nazwa właściwości, a trzeci wariant to wartość właściwości. Tak, /org/ayatana/menu/DA00003obiekt jest powiadomienie nas, że przedmiot id # 23 zmieniła swoją enabledwłaściwość na wartość true.


Kolejny przykład sygnałów:

nadawca sygnału =: 1.1602 -> dest = (puste miejsce docelowe) serial = 20408 ścieżka = / im / pidgin / purple / PurpleObject; interfejs = im.pidgin.purple.PurpleInterface; member = SendingChatMsg
   int32 47893
   ciąg „test”
   uint32 1
nadawca sygnału =: 1.1602 -> dest = (puste miejsce docelowe) serial = 20409 ścieżka = / im / pidgin / purple / PurpleObject; interfejs = im.pidgin.purple.PurpleInterface; member = IrcSendingText
   int32 64170
   ciąg "PRIVMSG #chat: test

Wysłałem wiadomość tekstową „test” za pomocą Pidgin na kanał IRC i /im/pidgin/purple/PurpleObjectwyemitowałem dwa sygnały pod im.pidgin.purple.PurpleInterfaceinterfejsem: najpierw ogólny SendingChatMsg, potem bardziej szczegółowyIrcSendingText .

Metody

Teraz metody. Metody są sposobem na poproszenie obiektów D-Bus o zrobienie czegoś lub wykonanie zapytania i zwrócenie danych. Są dość podobne do klasycznych metod OOP, z tym wyjątkiem, że metody D-Bus są wywoływane asynchronicznie.

Nazwijmy programowo metodę D-Bus.

import dbus, dbus.proxies

#-- connect to the session bus (as opposed to the system bus)
session = dbus.SessionBus()

#-- create proxy object of D-Bus object
obj_proxy = dbus.proxies.ProxyObject(conn=session,
         bus_name="org.freedesktop.Notifications",     #-- name of the service we are retrieving object from
         object_path="/org/freedesktop/Notifications") #-- the object path

#-- create proxy object of the D-Bus object wrapped into specific interface
intf_proxy = dbus.proxies.Interface(obj_proxy, "org.freedesktop.Notifications")

#-- lastly, create proxy object of the D-Bus method
method_proxy = intf_proxy.get_dbus_method("Notify")

#-- ... and call the method
method_proxy("test from python",
             dbus.UInt32(0),
             "bluetooth",     #-- icon name
             "Notification summary",
             "Here goes notification body",
             [], {},
             5) #-- timeout

Zwróć uwagę na argumenty, zwłaszcza nazwę ikony. W twoim przykładzie"notification-audio-volume-medium" była ikona głośnika o średniej mocy.

Usługi niestandardowe

Absolutnie możliwe jest uruchamianie własnych usług D-Bus, eksportowanie własnych obiektów D-Bus i definiowanie własnych interfejsów D-Bus za pomocą własnych metod i sygnałów. Wszystko to można zrobić w Pythonie dość łatwo po zrozumieniu ogólnej koncepcji i przeczytaniu dbusdokumentacji modułu.:)

ulidtko
źródło
Dyskusja jest mile widziana, ale może nie być dostępna za dzień lub dwa.
ulidtko
Dzięki :) To dużo wyjaśnia. To zabawne, że nadawcy mogą być anonimowi, kiedy używam DFeet, dla każdego nadawcy istnieje nazwa procesu, ale nie ma to odzwierciedlenia w wyjściu monitora-dbus. Czy można śledzić procesy? Teraz z Pythonem widziałem, że mogę wysyłać sygnały lub dostarczać metody lub wyzwalać metody innych podmiotów. Czy można również przechwytywać metody? Załóżmy, że chcę sprawdzić, czy program A wyzwala metodę Dbus B i coś z tym zrobić?
Benjamin
Informacje o powiadomieniach: powiadomienie-osd jest pasywnie uruchamiane przez inne aplikacje, zamiast aktywnie szukać sygnałów. Czy to nie jest niepraktyczne, czy też źle rozumiem o Dbus? Chcę stworzyć aplikację, która zastąpi not-osd i zbierze powiadomienia w rodzaju skrzynki odbiorczej. Czy mogę wtedy przechwytywać powiadomienia, słuchając sygnałów?
Benjamin
@Benjamin, cóż, jeśli chcesz przechwytywać wywołania metod skierowane do zagranicznych usług, najprawdopodobniej myślisz o zepsutym projekcie. Co należy zrobić, aby zastąpić zawiadomić-OSD jest napisanie programu, który dostarcza do org.freedesktop.Notificationsserwisu. W ten sposób wszystkie wywołania metod do tej usługi będą obsługiwane przez Twój kod.
ulidtko
Co to jest „self” obejct?
kawing-chiu
10

Szukałem również rozwiązania do zbierania powiadomień na pulpicie przez dbus ze skryptem python. To pytanie było najbliższe google, ale napisanie zamiennika dla not-osd wydawało się przesadą :)

Patrząc na źródła apletów z ostatnimi powiadomieniami dostałem kilka wskazówek, jak monitorować komunikaty dbus, a oto implementacja Pythona, którą wymyśliłem:

import gtk
import dbus
from dbus.mainloop.glib import DBusGMainLoop

def filter_cb(bus, message):
    # the NameAcquired message comes through before match string gets applied
    if message.get_member() != "Notify":
        return
    args = message.get_args_list()
    # args are
    # (app_name, notification_id, icon, summary, body, actions, hints, timeout)
    print("Notification from app '%s'" % args[0])
    print("Summary: %s" % args[3])
    print("Body: %s", args[4])


DBusGMainLoop(set_as_default=True)
bus = dbus.SessionBus()
bus.add_match_string(
    "type='method_call',interface='org.freedesktop.Notifications',member='Notify'")
bus.add_message_filter(filter_cb)
gtk.main()

Mam nadzieję, że to komuś pomoże, ponieważ wydaje się, że nie ma wielu prostych przykładów Pythona związanych z monitorowaniem komunikatów dbus.

Keto
źródło
1
Z pewnością mi pomogło! Dziękuję Ci bardzo! Kilka propozycji: „type = 'method_call'” nie jest koniecznością, ponieważ powiadomienia używają tylko wywołań metod. Brak sygnałów w specyfikacji. Również „member = 'Notify'” również nie jest konieczne, ponieważ już filtrujesz to w swojej funkcji (i, jak słusznie powiedziałeś, nie możesz tego uniknąć z powodu pierwszej NameAquiredwiadomości)
MestreLion