diff options
author | Thomas Bechtold <thomasbechtold@jpberlin.de> | 2012-08-04 13:38:50 +0200 |
---|---|---|
committer | Thomas Bechtold <thomasbechtold@jpberlin.de> | 2012-09-14 21:18:15 +0200 |
commit | cf4b5a71a1e1fff59b07be4b458f928fb83190c4 (patch) | |
tree | 3ec0281e3f78c430802b31e367351db2ed58c7be | |
parent | aa65bf360dc38b1006ca9e1f8d61d32bdf1ce035 (diff) | |
download | d-feet-cf4b5a71a1e1fff59b07be4b458f928fb83190c4.tar.gz |
Port to pygi and gdbus
* Port from pygtk to pygi
* Port from python-dbus to gdbus
* restructure code
* add (still simple) testsuite
https://bugzilla.gnome.org/show_bug.cgi?id=681093
34 files changed, 2064 insertions, 2336 deletions
@@ -1 +1,3 @@ *.pyc +*~ +\#*# @@ -2,6 +2,8 @@ John (J5) Palmieri <johnp@redhat.com> Ray Strode <rstrode@redhat.com> Marcel Holtmann <marcel@holtmann.org> Johan Dahlin <johan@gnome.org> +Thomas Bechtold <thomasbechtold@jpberlin.de> + Some code taken and modified from the D-Bus Python project http://dbus.freedesktop.org Some code taken and modified from the Jokosher Project http://jokosher.org @@ -2,10 +2,10 @@ Welcome to D-Feet (http://live.gnome.org/d-feet) Requirements: -D-Bus > 1.0 -D-Bus Python > 0.82.3 -PyGtk -Python 2.5 +python >= 2.7 +glib >= 2.34 +gobject-introspection +python-gi Optional Requriements: @@ -0,0 +1,10 @@ +* reimplement the bustab list save/restore stuff +* Add checkbox in addconnectiondialog.ui to be able to start a + peer-to-peer connection +* Add filter for introspector treeview. Should be possible to filter + methods, properties and interfaces +* Add menu entry to start a service by name + (org.freedesktop.DBus.StartServiceByName) +* Add a list with activateable services + (org.freedesktop.DBus.ListActivatableNames) + @@ -29,8 +29,6 @@ if options.use_local_dirs: "DFEET_LOCALE_PATH" : "locale/", "DFEET_HELP_PATH" : "/usr/share/gnome/dfeet/" } - - else: ENV_PATHS = {"DFEET_DATA_PATH" : "/usr/share/dfeet/", "DFEET_IMAGE_PATH" : "/usr/share/dfeet/pixmaps/", diff --git a/d-feet.doap b/d-feet.doap index 3252439..1827172 100644 --- a/d-feet.doap +++ b/d-feet.doap @@ -7,7 +7,9 @@ <name xml:lang="en">D-Feet</name> <shortdesc xml:lang="en">D-Bus Debugger</shortdesc> <homepage rdf:resource="http://live.gnome.org/DFeet" /> - + <download-page rdf:resource="http://download.gnome.org/sources/d-feet/" /> + <bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=d-feet" /> + <maintainer> <foaf:Person> <foaf:name>John (J5) Palmieri</foaf:name> @@ -15,4 +17,11 @@ <gnome:userid>johnp</gnome:userid> </foaf:Person> </maintainer> + <maintainer> + <foaf:Person> + <foaf:name>Thomas Bechtold</foaf:name> + <foaf:mbox rdf:resource="mailto:thomasbechtold@jpberlin.de" /> + <gnome:userid>toabctl</gnome:userid> + </foaf:Person> + </maintainer> </Project> diff --git a/dfeet/DFeetApp.py b/dfeet/DFeetApp.py index 6463592..ef407c7 100644 --- a/dfeet/DFeetApp.py +++ b/dfeet/DFeetApp.py @@ -1,209 +1,141 @@ +# -*- coding: utf-8 -*- import os import sys -from gi.repository import Gtk -import _ui -import _util -import dbus_introspector -import introspect_data +from gi.repository import Gtk, Gio, GObject -from dbus_introspector import BusWatch + +from bus_watch import BusWatch +from introspection import AddressInfo from settings import Settings from _ui.uiloader import UILoader from _ui.addconnectiondialog import AddConnectionDialog from _ui.executemethoddialog import ExecuteMethodDialog +class NotebookTabLabel(Gtk.Box): + __gsignals__ = { + "close-clicked": (GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, ()), + } + def __init__(self, label_text): + Gtk.Box.__init__(self) + self.set_orientation(Gtk.Orientation.HORIZONTAL) + self.set_spacing(5) + # label + label = Gtk.Label(label_text) + self.pack_start(label, True, True, 0) + # close button + button = Gtk.Button() + button.set_relief(Gtk.ReliefStyle.NONE) + button.set_focus_on_click(False) + button.add(Gtk.Image.new_from_stock(Gtk.STOCK_CLOSE, Gtk.IconSize.MENU)) + button.connect("clicked", self.__button_clicked) + self.pack_end(button, False, False, 0) + self.show_all() + + def __button_clicked(self, button, data=None): + self.emit("close-clicked") + + class DFeetApp: HISTORY_MAX_SIZE = 10 def __init__(self): - signal_dict = {'add_session_bus': self.add_session_bus_cb, - 'add_system_bus': self.add_system_bus_cb, - 'add_bus_address': self.add_bus_address_cb, - 'reconnect_current_bus': self.reconnect_current_bus_cb, - 'execute_method': self.execute_current_method_cb, - 'quit': self.quit_cb} - - self.ICON_SIZE_CLOSE_BUTTON = Gtk.icon_size_register('ICON_SIZE_CLOSE_BUTTON', 14, 14) + signal_dict = { + 'action_systembus_connect_activate_cb': self.__systembus_connect_cb, + 'action_sessionbus_connect_activate_cb': self.__sessionbus_connect_cb, + 'action_otherbus_connect_activate_cb': self.__otherbus_connect_cb, + 'action_close_activate_cb': self.__close_cb, + } settings = Settings.get_instance() ui = UILoader(UILoader.UI_MAINWINDOW) - self.main_window = ui.get_root_widget() - self.main_window.set_icon_name('dfeet-icon') - self.main_window.connect('delete-event', self._quit_dfeet) + self.main_window.connect('delete-event', self.__quit_dfeet) + self.main_window.set_default_size(int(settings.general['windowwidth']), + int(settings.general['windowheight'])) self.notebook = ui.get_widget('display_notebook') self.notebook.show_all() + self.notebook_page_widget = ui.get_widget('box_notebook_page') - self.execute_method_action = ui.get_widget('execute_method') - self.reconnect_current_bus_action = ui.get_widget('reconnect_current_bus') + #create bus history list and load entries from settings + self.__bus_history = [] + for bus in settings.general['addbus_list']: + if bus != '': + self.__bus_history.append(bus) - self.notebook.connect('switch-page', self.switch_tab_cb) + ui.connect_signals(signal_dict) + self.main_window.show() - self.main_window.set_default_size(int(settings.general['windowwidth']), - int(settings.general['windowheight'])) - self._load_tabs(settings) - self._load_addbus_history(settings) + @property + def bus_history(self): + return self.__bus_history - self.main_window.show() - ui.connect_signals(signal_dict) + def __systembus_connect_cb(self, action): + """ connect to system bus """ + bw = BusWatch(Gio.BusType.SYSTEM) + self.__notebook_append_page(bw.paned_buswatch, "System Bus") - def _load_tabs(self, settings): - for bus_name in settings.general['bustabs_list']: - if bus_name == 'Session Bus': - self.add_bus(dbus_introspector.SESSION_BUS) - elif bus_name == 'System Bus': - self.add_bus(dbus_introspector.SYSTEM_BUS) - else: - self.add_bus(address = bus_name) - - def _load_addbus_history(self, settings): - self.add_bus_history = [] - self.combo_addbus_history_model = Gtk.ListStore(str) - for bus_add in settings.general['addbus_list']: - if bus_add != '': - self.add_bus_history.append(bus_add) - - def _add_bus_tab(self, bus_watch, position=None): - name = bus_watch.get_bus_name() - bus_paned = _ui.BusBox(bus_watch) - bus_paned.connect('introspectnode-selected', - self.introspect_node_selected_cb) - bus_paned.show_all() - hbox = Gtk.HBox() - hbox.pack_start(Gtk.Label(name), True, True, 0) - close_btn = Gtk.Button() - img = Gtk.Image() - img.set_from_stock(Gtk.STOCK_CLOSE, self.ICON_SIZE_CLOSE_BUTTON) - img.show() - close_btn.set_image(img) - close_btn.set_relief(Gtk.ReliefStyle.NONE) - close_btn.connect('clicked', self.close_tab_cb, bus_paned) - hbox.pack_start(close_btn, False, False, 0) - hbox.show_all() - - if position: - self.notebook.insert_page(bus_paned, hbox, position) - self.notebook.set_current_page(position) - self.notebook.set_tab_reorderable(bus_paned, True) - else: - p = self.notebook.append_page(bus_paned, hbox) - self.notebook.set_current_page(p) - self.notebook.set_tab_reorderable(bus_paned, True) - - def introspect_node_selected_cb(self, widget, node): - if isinstance(node, introspect_data.Method): - self.execute_method_action.set_sensitive(True) - else: - self.execute_method_action.set_sensitive(False) - - def execute_current_method_cb(self, action): - page = self.notebook.get_current_page() - if page >= 0: - busbox = self.notebook.get_nth_page(page) - node = busbox.get_selected_introspect_node() - busname = busbox.get_selected_busname() - dialog = ExecuteMethodDialog(busname, node) - dialog.run() - - def close_tab_cb(self, button, child): - n = self.notebook.page_num(child) - if child.get_bus_watch().get_bus_name() not in [u'Session Bus', u'System Bus']: - child.get_bus_watch().close_bus() - self.notebook.remove_page(n) - if self.notebook.get_n_pages() <= 0: - self.reconnect_current_bus_action.set_sensitive(False) - - def switch_tab_cb(self, notebook, page, page_num): - child = self.notebook.get_nth_page(page_num) - if child.get_bus_watch().get_bus_name() not in [u'Session Bus', u'System Bus']: - self.reconnect_current_bus_action.set_sensitive(True) - else: - self.reconnect_current_bus_action.set_sensitive(False) - - def select_or_add_bus(self, address): - for i in range(self.notebook.get_n_pages()): - page = self.notebook.get_nth_page(i) - tab_label = self.notebook.get_tab_label(page) - if tab_label.get_children()[0].get_text() == address: - self.notebook.set_current_page(i) - break - else: - self.add_bus(address=address) - - def add_bus(self, bus_type=None, address=None): - if bus_type == dbus_introspector.SESSION_BUS or bus_type == dbus_introspector.SYSTEM_BUS: - bus_watch = BusWatch(bus_type) - self._add_bus_tab(bus_watch) - else: - try: - bus_watch = BusWatch(None, address=address) - self._add_bus_tab(bus_watch) - except Exception, e: - print e - - def add_session_bus_cb(self, action): - self.add_bus(dbus_introspector.SESSION_BUS) - - def add_system_bus_cb(self, action): - self.add_bus(dbus_introspector.SYSTEM_BUS) - - def add_bus_address_cb(self, action): - dialog = AddConnectionDialog(self.main_window) - self.combo_addbus_history_model.clear() - # Load combo box history - for el in self.add_bus_history: - self.combo_addbus_history_model.append([el]) - dialog.set_model(self.combo_addbus_history_model) - result = dialog.run() - if result == 1: - bus_address = dialog.get_address() - if bus_address == 'Session Bus': - self.add_bus(dbus_introspector.SESSION_BUS) - elif bus_address == 'System Bus': - self.add_bus(dbus_introspector.SYSTEM_BUS) - else: - self.add_bus(address = bus_address) - # Fill history - if bus_address in self.add_bus_history: - self.add_bus_history.remove(bus_address) - self.add_bus_history.insert(0, bus_address) - # Truncating history - if (len(self.add_bus_history) > self.HISTORY_MAX_SIZE): - self.add_bus_history = self.add_bus_history[0:self.HISTORY_MAX_SIZE] - dialog.destroy() - - def reconnect_current_bus_cb(self, action): - page = self.notebook.get_current_page() - if page >= 0: - child = self.notebook.get_nth_page(page) - bus_watch = child.get_bus_watch() + def __sessionbus_connect_cb(self, action): + """ connect to session bus """ + bw = BusWatch(Gio.BusType.SESSION) + self.__notebook_append_page(bw.paned_buswatch, "Session Bus") - bus_type = bus_watch.get_bus_type() - bus_address = bus_watch.get_bus_address() - if bus_type == dbus_introspector.SESSION_BUS or bus_type == dbus_introspector.SYSTEM_BUS: - pass + def __otherbus_connect_cb(self, action): + """ connect to other bus """ + dialog = AddConnectionDialog(self.main_window, self.bus_history) + result = dialog.run() + if result == Gtk.ResponseType.OK: + address = dialog.address + if address == 'Session Bus': + self.__sessionbus_connect_cb(None) + return + elif address == 'System Bus': + self.__systembus_connect_cb(None) + return else: - bus_watch.close_bus() - self.notebook.remove_page(page) try: - new_bus_watch = BusWatch(None, address=bus_address) - self._add_bus_tab(new_bus_watch, page) + bw = BusWatch(address) + self.__notebook_append_page(bw.paned_buswatch, address) + # Fill history + if address in self.bus_history: + self.bus_history.remove(address) + self.bus_history.insert(0, address) + # Truncating history + if (len(self.bus_history) > self.HISTORY_MAX_SIZE): + self.bus_history = self.bus_history[0:self.HISTORY_MAX_SIZE] except Exception, e: - print e + print "can not connect to '%s': %s" % (address, str(e)) + dialog.destroy() + + + def __notebook_append_page(self, widget, text): + """ add a page to the notebook """ + ntl = NotebookTabLabel(text) + page_nbr = self.notebook.append_page(widget, ntl) + ntl.connect("close-clicked", self.__notebook_page_close_clicked_cb, widget) + - def quit_cb(self, action): + def __notebook_page_close_clicked_cb(self, button, widget): + """ remove a page from the notebook """ + nbr = self.notebook.page_num(widget) + self.notebook.remove_page(nbr) + + + def __close_cb(self, action): + """ quit program """ self._quit_dfeet(self.main_window, None) - def _quit_dfeet(self, main_window, event): + + def __quit_dfeet(self, main_window, event): + """ quit d-feet application and store some settings """ settings = Settings.get_instance() size = main_window.get_size() pos = main_window.get_position() @@ -211,24 +143,17 @@ class DFeetApp: settings.general['windowwidth'] = size[0] settings.general['windowheight'] = size[1] - n = self.notebook.get_n_pages() - tab_list = [] - for i in xrange(n): - child = self.notebook.get_nth_page(i) - bus_watch = child.get_bus_watch() - tab_list.append(bus_watch.get_bus_name()) - - self.add_bus_history = self.add_bus_history[0:self.HISTORY_MAX_SIZE] + self.bus_history = self.bus_history[0:self.HISTORY_MAX_SIZE] - settings.general['bustabs_list'] = tab_list - settings.general['addbus_list'] = self.add_bus_history - + settings.general['addbus_list'] = self.bus_history settings.write() - Gtk.main_quit() + def run(self): + """ start the application """ Gtk.main() + if __name__ == "__main__": DFeetApp().run() diff --git a/dfeet/_introspect_parser.py b/dfeet/_introspect_parser.py deleted file mode 100644 index 0746cba..0000000 --- a/dfeet/_introspect_parser.py +++ /dev/null @@ -1,150 +0,0 @@ -# Copyright (C) 2003, 2004, 2005, 2006 Red Hat Inc. <http://www.redhat.com/> -# Copyright (C) 2003 David Zeuthen -# Copyright (C) 2004 Rob Taylor -# Copyright (C) 2005, 2006 Collabora Ltd. <http://www.collabora.co.uk/> -# Copyright (C) 2007 John (J5) Palmieri -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation -# files (the "Software"), to deal in the Software without -# restriction, including without limitation the rights to use, copy, -# modify, merge, publish, distribute, sublicense, and/or sell copies -# of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - -from xml.parsers.expat import ExpatError, ParserCreate -from dbus.exceptions import IntrospectionParserException - -class _Parser(object): - __slots__ = ('map', - 'in_iface', - 'in_method', - 'in_signal', - 'in_property', - 'property_access', - 'in_sig', - 'out_sig', - 'node_level', - 'in_signal') - def __init__(self): - self.map = {'child_nodes':[],'interfaces':{}} - self.in_iface = '' - self.in_method = '' - self.in_signal = '' - self.in_property = '' - self.property_access = '' - self.in_sig = [] - self.out_sig = [] - self.node_level = 0 - - def parse(self, data): - parser = ParserCreate('UTF-8', ' ') - parser.buffer_text = True - parser.StartElementHandler = self.StartElementHandler - parser.EndElementHandler = self.EndElementHandler - parser.Parse(data) - return self.map - - def StartElementHandler(self, name, attributes): - if name == 'node': - self.node_level += 1 - if self.node_level == 2: - self.map['child_nodes'].append(attributes['name']) - elif not self.in_iface: - if (not self.in_method and name == 'interface'): - self.in_iface = attributes['name'] - else: - if (not self.in_method and name == 'method'): - self.in_method = attributes['name'] - elif (self.in_method and name == 'arg'): - arg_type = attributes['type'] - arg_name = attributes.get('name', None) - if attributes.get('direction', 'in') == 'in': - self.in_sig.append({'name': arg_name, 'type': arg_type}) - if attributes.get('direction', 'out') == 'out': - self.out_sig.append({'name': arg_name, 'type': arg_type}) - elif (not self.in_signal and name == 'signal'): - self.in_signal = attributes['name'] - elif (self.in_signal and name == 'arg'): - arg_type = attributes['type'] - arg_name = attributes.get('name', None) - - if attributes.get('direction', 'in') == 'in': - self.in_sig.append({'name': arg_name, 'type': arg_type}) - elif (not self.in_property and name == 'property'): - prop_type = attributes['type'] - prop_name = attributes['name'] - - self.in_property = prop_name - self.in_sig.append({'name': prop_name, 'type': prop_type}) - self.property_access = attributes['access'] - - - def EndElementHandler(self, name): - if name == 'node': - self.node_level -= 1 - elif self.in_iface: - if (not self.in_method and name == 'interface'): - self.in_iface = '' - elif (self.in_method and name == 'method'): - if not self.map['interfaces'].has_key(self.in_iface): - self.map['interfaces'][self.in_iface]={'methods':{}, 'signals':{}, 'properties':{}} - - if self.map['interfaces'][self.in_iface]['methods'].has_key(self.in_method): - print "ERROR: Some clever service is trying to be cute and has the same method name in the same interface" - else: - self.map['interfaces'][self.in_iface]['methods'][self.in_method] = (self.in_sig, self.out_sig) - - self.in_method = '' - self.in_sig = [] - self.out_sig = [] - elif (self.in_signal and name == 'signal'): - if not self.map['interfaces'].has_key(self.in_iface): - self.map['interfaces'][self.in_iface]={'methods':{}, 'signals':{}, 'properties':{}} - - if self.map['interfaces'][self.in_iface]['signals'].has_key(self.in_signal): - print "ERROR: Some clever service is trying to be cute and has the same signal name in the same interface" - else: - self.map['interfaces'][self.in_iface]['signals'][self.in_signal] = (self.in_sig,) - - self.in_signal = '' - self.in_sig = [] - self.out_sig = [] - elif (self.in_property and name == 'property'): - if not self.map['interfaces'].has_key(self.in_iface): - self.map['interfaces'][self.in_iface]={'methods':{}, 'signals':{}, 'properties':{}} - - if self.map['interfaces'][self.in_iface]['properties'].has_key(self.in_property): - print "ERROR: Some clever service is trying to be cute and has the same property name in the same interface" - else: - self.map['interfaces'][self.in_iface]['properties'][self.in_property] = (self.in_sig, self.property_access) - - self.in_property = '' - self.in_sig = [] - self.out_sig = [] - self.property_access = '' - - -def process_introspection_data(data): - """Return a structure mapping all of the elements from the introspect data - to python types TODO: document this structure - - :Parameters: - `data` : str - The introspection XML. Must be an 8-bit string of UTF-8. - """ - try: - return _Parser().parse(data) - except Exception, e: - raise IntrospectionParserException('%s: %s' % (e.__class__, e)) diff --git a/dfeet/_ui/__init__.py b/dfeet/_ui/__init__.py index 5c8eacb..d7b0c3c 100644 --- a/dfeet/_ui/__init__.py +++ b/dfeet/_ui/__init__.py @@ -1,2 +1,2 @@ -from busbox import BusBox +#from busbox import BusBox diff --git a/dfeet/_ui/addconnectiondialog.py b/dfeet/_ui/addconnectiondialog.py index 4cecaf0..643d2d0 100644 --- a/dfeet/_ui/addconnectiondialog.py +++ b/dfeet/_ui/addconnectiondialog.py @@ -1,34 +1,56 @@ -from gi.repository import Gtk +from gi.repository import Gtk, Gio from uiloader import UILoader class AddConnectionDialog: - RESPONSE_CANCEL = -1 - RESPONSE_CONNECT = 1 - def __init__(self, parent): - ui = UILoader(UILoader.UI_ADDCONNECTIONDIALOG) + def __init__(self, parent, address_bus_history=[]): + ui = UILoader(UILoader.UI_ADDCONNECTIONDIALOG) self.dialog = ui.get_root_widget() - self.combo_entry = ui.get_widget('address_comboentry1') - - self.combo_entry.get_child().connect('activate', self.activate_combo) - self.dialog.add_button('gtk-cancel', self.RESPONSE_CANCEL) - self.dialog.add_button('gtk-connect', self.RESPONSE_CONNECT) - def get_address(self): - return self.combo_entry.get_active_text() + #get the hbox and add address combo box with model + hbox1 = ui.get_widget('hbox1') + self.address_combo_box_store = Gtk.ListStore(str) + self.address_combo_box = Gtk.ComboBox.new_with_model_and_entry(self.address_combo_box_store) + self.address_combo_box.set_entry_text_column(0) + self.label_status = ui.get_widget('label_status') + + hbox1.pack_start(self.address_combo_box, True, True, 0) + hbox1.show_all() + + #add history to model + for el in address_bus_history: + self.address_combo_box_store.append([el]) + + self.dialog.add_button('gtk-cancel', Gtk.ResponseType.CANCEL) + self.dialog.add_button('gtk-connect', Gtk.ResponseType.OK) + + + @property + def address(self): + tree_iter = self.address_combo_box.get_active_iter() + if tree_iter != None: + model = self.address_combo_box.get_model() + return model[tree_iter][0] + else: + entry = self.address_combo_box.get_child() + return entry.get_text() def run(self): - return self.dialog.run() + response = self.dialog.run() + if response == Gtk.ResponseType.CANCEL: + return response + elif response == Gtk.ResponseType.OK: + #check if given address is valid + try: + is_supported = Gio.dbus_is_supported_address(self.address) + except Exception, e: + self.label_status.set_text(str(e)) + self.run() + else: + return Gtk.ResponseType.OK + def destroy(self): self.dialog.destroy() - def activate_combo(self, user_data): - self.dialog.response(self.RESPONSE_CONNECT) - return True - - def set_model(self, model): - self.combo_entry.set_model(model) - self.combo_entry.set_text_column(0) - diff --git a/dfeet/_ui/busbox.py b/dfeet/_ui/busbox.py deleted file mode 100644 index 13ba36a..0000000 --- a/dfeet/_ui/busbox.py +++ /dev/null @@ -1,95 +0,0 @@ -import gobject -from gi.repository import Gtk - -from dfeet import _util - -from busnamebox import BusNameBox -from busnameinfobox import BusNameInfoBox -from uiloader import UILoader - -class BusBox(Gtk.VBox): - __gsignals__ = { - 'introspectnode-selected': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)) - } - - def __init__(self, watch): - super(BusBox, self).__init__() - - # FilterBox - signal_dict = { - 'hide_private_toggled' : self.hide_private_toggled_cb, - 'filter_entry_changed': self.filter_entry_changed_cb - } - - - self.bus_watch = watch - - ui = UILoader(UILoader.UI_FILTERBOX) - filter_box = ui.get_root_widget() - filter_entry = ui.get_widget('filter_entry1') - - self.pack_start(filter_box, False, False, 0) - - self.completion = Gtk.EntryCompletion() - self.completion.set_model(watch) - self.completion.set_inline_completion(True) - - # older gtk+ does not support this method call - # but it is not fatal - try: - self.completion.set_inline_selection(True) - except: - pass - - filter_entry.set_completion(self.completion) - - # Content - self.paned = Gtk.HPaned() - self.busname_box = BusNameBox(watch) - self.busname_info_box = BusNameInfoBox() - - self.busname_box.connect('busname-selected', self.busname_selected_cb) - self.busname_info_box.connect('selected', self.busname_info_selected_cb) - - self.paned.pack1(self.busname_box) - self.paned.pack2(self.busname_info_box) - self.paned.set_position(300) - self.pack_start(self.paned, True, True, 0) - - ui.connect_signals(signal_dict) - - def busname_selected_cb(self, busname_box, busname): - self.busname_info_box.set_busname(busname) - - def busname_info_selected_cb(self, busname_info_box, node): - self.emit('introspectnode-selected', node) - - def get_selected_introspect_node(self): - return self.busname_info_box.get_selected_node() - - def get_selected_busname(self): - return self.busname_box.get_selected_busname() - - def filter_entry_changed_cb(self, entry, button): - value = entry.get_text() - if value == '': - value = None - - self.busname_box.set_filter_string(value) - - def hide_private_toggled_cb(self, toggle): - a = toggle.get_active() - if a: - toggle.set_label("Show Private") - else: - toggle.set_label("Hide Private") - - self.busname_box.set_hide_private(a) - - def sort_combo_changed_cb(self, combo): - value = combo.get_active_text() - self.busname_box.set_sort_col(value) - - def get_bus_watch(self): - return self.bus_watch diff --git a/dfeet/_ui/busnamebox.py b/dfeet/_ui/busnamebox.py deleted file mode 100644 index 393d2a3..0000000 --- a/dfeet/_ui/busnamebox.py +++ /dev/null @@ -1,65 +0,0 @@ -import gobject -from gi.repository import Gtk - -from dfeet.dbus_introspector import BusWatch - -from busnameview import BusNameView - -class BusNameBox(Gtk.VBox): - __gsignals__ = { - 'busname-selected' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, - (gobject.TYPE_PYOBJECT,)) - } - - def __init__(self, watch): - super(BusNameBox, self).__init__() - - self.tree_view = BusNameView(watch) - self.tree_view.connect('cursor_changed', self.busname_selected_cb) - - scroll = Gtk.ScrolledWindow() - scroll.add(self.tree_view) - self.pack_start(scroll, True, True, 0) - scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - - self.show_all() - - def _completion_match_func(self, completion, key, iter): - print completion, key, iter - return self.tree_view._is_iter_equal(completion.get_model(), - iter, key) - - def get_selected_busname(self): - (model, iter) = self.tree_view.get_selection().get_selected() - if not iter: - return None - - busname = model.get_value(iter, BusWatch.BUSNAME_OBJ_COL) - - return busname - - def busname_selected_cb(self, treeview): - busname = self.get_selected_busname() - self.emit('busname-selected', busname) - - def set_filter_string(self, value): - self.tree_view.set_filter_string(value) - self.tree_view.refilter() - - def set_hide_private(self, hide_private): - self.tree_view.set_hide_private(hide_private) - self.tree_view.refilter() - - def set_sort_col(self, value): - if value == 'Common Name': - col = BusWatch.COMMON_NAME_COL - elif value == 'Unique Name': - col = BusWatch.UNIQUE_NAME_COL - elif value == 'Process Name': - col = BusWatch.PROCESS_NAME_COL - else: - raise Exception('Value "' + value + '" is not a valid sort value') - - self.tree_view.set_sort_column(col) - #self.tree_view.sort_column_changed() - diff --git a/dfeet/_ui/busnameinfobox.py b/dfeet/_ui/busnameinfobox.py index c84f274..05d1d6f 100644 --- a/dfeet/_ui/busnameinfobox.py +++ b/dfeet/_ui/busnameinfobox.py @@ -1,6 +1,6 @@ -import gobject +from gi.repository import GObject as gobject from gi.repository import Gtk -import pango +from gi.repository import Pango as pango from dfeet import _util from dfeet.introspect_data import IntrospectData, Method, Signal diff --git a/dfeet/_ui/busnameview.py b/dfeet/_ui/busnameview.py deleted file mode 100644 index eda692a..0000000 --- a/dfeet/_ui/busnameview.py +++ /dev/null @@ -1,162 +0,0 @@ -import gobject -from gi.repository import Gtk - -from dfeet.dbus_introspector import BusWatch - -class BusNameView(Gtk.TreeView): - def __init__(self, watch): - super(BusNameView, self).__init__() - - self.hide_private = False - self.filter_string = None - - self.set_property('enable-search', True) - self.set_property('enable-grid-lines', False) - self.watch = watch - - self.filter_model = self.watch.filter_new(None) - self.filter_model.set_visible_func(self._filter_cb, None) - - self.sort_model = Gtk.TreeModelSort(model=self.filter_model) - self.sort_model.set_sort_column_id(self.watch.COMMON_NAME_COL, Gtk.SortType.ASCENDING) - self.sort_model.set_sort_func(self.watch.COMMON_NAME_COL, self._sort_on_name, (self.watch.COMMON_NAME_COL, self.watch.UNIQUE_NAME_COL)) - - renderer = Gtk.CellRendererPixbuf() - column = Gtk.TreeViewColumn("", - renderer, - pixbuf=watch.ICON_COL - ) - column.set_resizable(False) - column.set_sort_column_id(watch.PROCESS_ID_COL) - self.append_column(column) - - renderer = Gtk.CellRendererText() - column = Gtk.TreeViewColumn("Bus Name", - renderer, - markup=watch.DISPLAY_COL - ) - column.set_resizable(True) - column.set_sort_column_id(watch.COMMON_NAME_COL) - self.append_column(column) - - """ - column = Gtk.TreeViewColumn("Unique Name", - renderer, - text=watch.UNIQUE_NAME_COL - ) - column.set_resizable(True) - column.set_sort_column_id(watch.UNIQUE_NAME_COL) - self.append_column(column) - """ - - column = Gtk.TreeViewColumn("Process", - renderer, - text=watch.PROCESS_NAME_COL - ) - column.set_resizable(True) - column.set_sort_column_id(watch.PROCESS_NAME_COL) - self.append_column(column) - - """ - column = Gtk.TreeViewColumn("PID", - renderer, - text=watch.PROCESS_ID_COL - ) - column.set_resizable(True) - column.set_sort_column_id(watch.PROCESS_ID_COL) - self.append_column(column) - """ - - self.set_headers_clickable(True) - self.set_reorderable(False) - self.set_search_equal_func(self._search_cb, None) - self.set_model(self.sort_model) - - def set_sort_column(self, col): - self.sort_model.set_sort_column_id(col, Gtk.SortType.ASCENDING) - - def set_hide_private(self, value): - self.hide_private = value - - def set_filter_string(self, value): - self.filter_string = value - - def refilter(self): - self.filter_model.refilter() - - - def _is_iter_equal(self, model, iter, key): - (unique_name, - common_name, - process_id, - process_path, - is_public) = model.get(iter, - BusWatch.UNIQUE_NAME_COL, - BusWatch.COMMON_NAME_COL, - BusWatch.PROCESS_ID_COL, - BusWatch.PROCESS_PATH_COL, - BusWatch.IS_PUBLIC_COL) - - if self.hide_private: - if not is_public: - return False - - # TODO: support filtering on introspect data - if key: - if (unique_name and unique_name.find(key)!=-1) or \ - (common_name and common_name.find(key)!=-1) or \ - str(process_id).startswith(key): - return True - - if process_path: - for item in process_path: - if (item.find(key)!=-1): - return True - - return False - - return True - - - def _search_cb(self, model, column, key, iter_): - return not self._is_iter_equal(model, iter_, key) - - def _filter_cb(self, model, iter_, data): - return self._is_iter_equal(model, iter_, self.filter_string) - - def _sort_on_name(self, model, iter1, iter2, cols): - (col, alt_col) = cols - - un1 = model.get_value(iter1, col) - un2 = model.get_value(iter2, col) - - if un1 is None: - un1 = model.get_value(iter1, alt_col) - - if un2 is None: - un2 = model.get_value(iter2, alt_col) - - # covert to integers if comparing two unique names - if un1[0] == ':' and un2[0] == ':': - un1 = un1[1:].split('.') - un1 = tuple(map(int, un1)) - - un2 = un2[1:].split('.') - un2 = tuple(map(int, un2)) - - elif un1[0] == ':' and un2[0] != ':': - return 1 - elif un1[0] != ':' and un2[0] == ':': - return -1 - else: - un1 = un1.split('.') - un2 = un2.split('.') - - if un1 == un2: - return 0 - elif un1 > un2: - return 1 - else: - return -1 - - diff --git a/dfeet/_ui/executemethoddialog.py b/dfeet/_ui/executemethoddialog.py index 7a542ae..564303f 100644 --- a/dfeet/_ui/executemethoddialog.py +++ b/dfeet/_ui/executemethoddialog.py @@ -1,33 +1,13 @@ -from gi.repository import Gtk -import dbus - -from dfeet import _util - -from uiloader import UILoader - +# -*- coding: utf-8 -*- +import time from pprint import pformat +from gi.repository import Gtk, GLib, Gio -def unwrap(x): - """Hack to unwrap D-Bus values, so that they're easier to read when - printed.""" - - if isinstance(x, list): - return map(unwrap, x) - - if isinstance(x, tuple): - return tuple(map(unwrap, x)) - - if isinstance(x, dict): - return dict([(unwrap(k), unwrap(v)) for k, v in x.iteritems()]) - - for t in [unicode, str, long, int, float, bool]: - if isinstance(x, t): - return t(x) +from uiloader import UILoader - return x class ExecuteMethodDialog: - def __init__(self, busname, method): + def __init__(self, connection, connection_is_bus, bus_name, method_obj): signal_dict = { 'execute_dbus_method_cb' : self.execute_cb, 'execute_dialog_close_cb': self.close_cb @@ -35,62 +15,102 @@ class ExecuteMethodDialog: ui = UILoader(UILoader.UI_EXECUTEDIALOG) self.dialog = ui.get_root_widget() - self.command_label = ui.get_widget('commandlabel1') + self.label_method_name = ui.get_widget('label_method_name') + self.label_object_path = ui.get_widget('label_object_path') + self.label_interface = ui.get_widget('label_interface') self.notebook = ui.get_widget('notebook1') self.parameter_textview = ui.get_widget('parametertextview1') self.source_textview = ui.get_widget('sourcetextview1') - self.notebook.set_tab_label_text(self.source_textview.get_parent(), - 'Source') self.prettyprint_textview = ui.get_widget('prettyprinttextview1') - self.notebook.set_tab_label_text(self.prettyprint_textview.get_parent(), - 'Pretty Print') + self.method_execution_count_spinbutton = ui.get_widget('method_exec_count_spinbutton') + self.label_avg = ui.get_widget('label_avg') + self.label_min = ui.get_widget('label_min') + self.label_max = ui.get_widget('label_max') ui.connect_signals(signal_dict) - self.busname = busname - self.method = method + self.connection = connection + self.connection_is_bus = connection_is_bus + self.bus_name = bus_name + self.method_obj = method_obj - # FIXME: get the interface and object path - text = 'Execute ' + str(self.method) - self.command_label.set_text(text) + self.label_method_name.set_markup("%s" % (self.method_obj.markup_str)) + self.label_object_path.set_markup("%s" % (self.method_obj.object_path)) + self.label_interface.set_markup("%s" % (self.method_obj.iface_info.name)) def execute_cb(self, widget): - # TODO: make call async, time it and add spinner to dialog + #get given parameters + buf = self.parameter_textview.get_buffer() + params = buf.get_text(buf.get_start_iter(), + buf.get_end_iter(), False) + + #reset the statistics stuff + self.label_avg.set_text("") + self.label_min.set_text("") + self.label_max.set_text("") + user_data = { + 'avg': 0, + 'count': 0, + } + try: - args = () - buf = self.parameter_textview.get_buffer() - params = buf.get_text(buf.get_start_iter(), - buf.get_end_iter()) + #build a GVariant if params: params = '(' + params + ',)' - args = eval(params, {'Boolean':dbus.Boolean, - 'Byte':dbus.Byte, - 'Int16':dbus.Int16, - 'Int32':dbus.Int32, - 'Int64':dbus.Int64, - 'UInt16':dbus.UInt16, - 'UInt32':dbus.UInt32, - 'UInt64':dbus.UInt64, - 'Double':dbus.Double, - 'ObjectPath':dbus.ObjectPath, - 'Signature':dbus.Signature, - 'String':dbus.String, - 'UTF8String':dbus.UTF8String}) - - result = self.method.dbus_call(self.busname.get_bus(), - self.busname.get_display_name(), - *args) - except Exception, e: # FIXME: treat D-Bus errors differently - # from parameter errors? - result = str(e) - - if result is None: - result = 'This method did not return anything' - - prettified = pformat(unwrap(result)) - self.prettyprint_textview.get_buffer().set_text(prettified) - - self.source_textview.get_buffer().set_text(str(result)) - + params_gvariant = GLib.Variant.parse(None, params, None, None) + else: + params_gvariant = None + + if self.connection_is_bus: + proxy = Gio.DBusProxy.new_sync(self.connection, + Gio.DBusProxyFlags.NONE, + None, + self.bus_name, + self.method_obj.object_path, + self.method_obj.iface_info.name, + None) + #call the function + for i in range(0, self.method_execution_count_spinbutton.get_value_as_int()): + user_data['method_call_time_start'] = time.time() + proxy.call(self.method_obj.method_info.name, params_gvariant, Gio.DBusCallFlags.NONE, -1, None, self.method_connection_bus_cb, user_data) + else: + #FIXME: implement p2p connection execution + raise Exception("Function execution on p2p connections not yet implemented") + #self.connection.call(None, object_path, self.method_obj.iface_obj.iface_info.name, self.method_obj.method_info.name, params_gvariant, GLib.VariantType.new("(s)"), Gio.DBusCallFlags.NONE, -1, None) + + + except Exception, e: + #output the exception + self.source_textview.get_buffer().set_text(str(e)) + self.prettyprint_textview.get_buffer().set_text(pformat(str(e))) + + + def method_connection_bus_cb(self, proxy, res_async, user_data): + """ async callback for executed method """ + try: + #get the result from the dbus method call + result = proxy.call_finish(res_async) + #remember the needed time for the method call + method_call_time_end = time.time() + method_call_time_needed = method_call_time_end - user_data['method_call_time_start'] + + #update avg, min, max + user_data['avg'] += method_call_time_needed + user_data['count'] += 1 + self.label_avg.set_text("%.4f" % (float(user_data['avg'] / user_data['count']))) + self.label_min.set_text("%.4f" % min(float(self.label_min.get_text() or "999"), method_call_time_needed)) + self.label_max.set_text("%.4f" % max(float(self.label_max.get_text() or "0"), method_call_time_needed)) + + #output result + if result: + self.source_textview.get_buffer().set_text(str(result)) + self.prettyprint_textview.get_buffer().set_text(str(result.unpack()[0])) + else: + self.prettyprint_textview.get_buffer().set_text('This method did not return anything') + except Exception, e: + #output the exception + self.source_textview.get_buffer().set_text(str(e)) + self.prettyprint_textview.get_buffer().set_text(pformat(str(e))) + def run(self): self.dialog.run() diff --git a/dfeet/_ui/uiloader.py b/dfeet/_ui/uiloader.py index a25ead1..2a38875 100644 --- a/dfeet/_ui/uiloader.py +++ b/dfeet/_ui/uiloader.py @@ -1,41 +1,40 @@ +# -*- coding: utf-8 -*- +import os from gi.repository import Gtk -from dfeet import _util class UILoader: instance = None - UI_COUNT = 5 + UI_COUNT = 5 (UI_MAINWINDOW, - UI_FILTERBOX, - UI_INTROSPECTVIEW, + UI_INTROSPECTION, + UI_BUSWATCH, UI_EXECUTEDIALOG, UI_ADDCONNECTIONDIALOG ) = range(UI_COUNT) # {ui_id: ((files,...), root widget)} - _ui_map = {UI_MAINWINDOW : (('default-actiongroup.ui','mainwindow.ui'), - 'appwindow1'), - UI_FILTERBOX : (('filterbox.ui',), - 'filterbox_table1'), - UI_INTROSPECTVIEW : (('introspectview.ui',), - 'introspectview_table1'), - UI_EXECUTEDIALOG : (('executedialog.ui',), - 'executedialog1'), + _ui_map = {UI_MAINWINDOW : (('mainwindow.ui',), + 'appwindow1'), + UI_INTROSPECTION : (('introspection.ui',), + 'box_introspectview'), + UI_BUSWATCH : (('buswatch.ui',), + 'paned_buswatch'), + UI_EXECUTEDIALOG : (('executedialog.ui',), + 'executedialog1'), UI_ADDCONNECTIONDIALOG : (('addconnectiondialog.ui',), 'add_connection_dialog1') } def __init__(self, ui): - ui_dir = _util.get_ui_dir() - ui_info = self._ui_map[ui] self.ui = Gtk.Builder() #load ui files for file in ui_info[0]: - self.ui.add_from_file(ui_dir + '/' + file) + self.ui.add_from_file(self.ui_dir + '/' + file) self.root_widget_name = ui_info[1] @@ -48,3 +47,12 @@ class UILoader: def connect_signals(self, obj_or_map): self.ui.connect_signals(obj_or_map) + @property + def ui_dir(self): + try: + ui_dir = os.environ['DFEET_DATA_PATH'] + except: + ui_dir = "../ui" + + return ui_dir + diff --git a/dfeet/_ui/wnck_utils.py b/dfeet/_ui/wnck_utils.py index a5f1e21..3b77e9a 100644 --- a/dfeet/_ui/wnck_utils.py +++ b/dfeet/_ui/wnck_utils.py @@ -2,7 +2,7 @@ # icon information. If the wnck module is not installed we fallback to default # behvior -import gobject +from gi.repository import GObject as gobject from gi.repository import Gtk try: diff --git a/dfeet/_util.py b/dfeet/_util.py deleted file mode 100644 index e0ebc94..0000000 --- a/dfeet/_util.py +++ /dev/null @@ -1,32 +0,0 @@ -import os - -# TODO: Check against other Unix's -def get_proc_from_pid(pid): - procpath = '/proc/' + str(pid) + '/cmdline' - fullpath = '' - - try: - f = open(procpath, 'r') - fullpath = f.readline().split('\0') - f.close() - except: - pass - - return fullpath - -# TODO: figure out more robust way to do this -def get_ui_dir(): - try: - ui_dir = os.environ['DFEET_DATA_PATH'] - except: - ui_dir = "../ui" - - return ui_dir - -def print_method(m): - def decorator(*args): - print "call:", m,args - r = m(*args) - print "return:", r - return r - return decorator diff --git a/dfeet/bus_watch.py b/dfeet/bus_watch.py new file mode 100644 index 0000000..f3db940 --- /dev/null +++ b/dfeet/bus_watch.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- + +import os +from gi.repository import GObject, GdkPixbuf, Gtk, Gio, GLib +from _ui.uiloader import UILoader +from introspection import AddressInfo + + +class DBusBusName(GObject.GObject): + """ class to represent a name on the bus """ + def __init__(self, bus_name_unique): + super(DBusBusName, self).__init__() + self.__bus_name_unique = bus_name_unique + self.__pid = 0 + self.__cmdline = '' + + def __repr__(self): + return u"%s (pid: %s)" % (self.bus_name_unique, self.pid) + + def __update_cmdline(self): + if self.pid > 0: + procpath = '/proc/' + str(self.pid) + '/cmdline' + with open(procpath, 'r') as f: + self.__cmdline = " ".join(f.readline().split('\0')) + else: + self.__cmdline = '' + + @property + def bus_name_unique(self): + return self.__bus_name_unique + + @property + def pid(self): + return self.__pid + + @pid.setter + def pid(self, pid_new): + self.__pid = pid_new + try: + self.__update_cmdline() + except Exception, e: + self.__cmdline = '' + + @property + def cmdline(self): + return self.__cmdline + + +class BusWatch: + """ watch for a given bus """ + def __init__(self, address): + self.address = address + #setup UI + ui = UILoader(UILoader.UI_BUSWATCH) + self.paned_buswatch = ui.get_root_widget() + self.liststore_model = ui.get_widget('liststore_buswatch') + self.treeview = ui.get_widget('treeview_buswatch') + self.entry_filter = ui.get_widget('entry_filter') + self.grid_bus_name_selected_info = ui.get_widget('grid_bus_name_info') + self.label_bus_name_selected_name = ui.get_widget('label_bus_name_selected_name') + self.label_bus_name_selected_pid = ui.get_widget('label_bus_name_selected_pid') + self.label_bus_name_selected_cmdline = ui.get_widget('label_bus_name_selected_cmdline') + self.addr_info = None # hold the currently selected AddressInfo object + + self.treeview.connect('cursor-changed', + self.__tree_view_cursor_changed_cb) + + + #setup the conection + if self.address == Gio.BusType.SYSTEM or self.address == Gio.BusType.SESSION: + self.connection = Gio.bus_get_sync(self.address, None) + elif Gio.dbus_is_supported_address(self.address) == True: + self.connection = Gio.DBusConnection.new_for_address_sync(self.address, + Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, + None, None) + + #setup signals + self.connection.signal_subscribe(None, "org.freedesktop.DBus", "NameOwnerChanged", + None, None, 0, self.__name_owner_changed_cb, None) + + self.bus_proxy = Gio.DBusProxy.new_sync(self.connection, + Gio.DBusProxyFlags.NONE, + None, + 'org.freedesktop.DBus', + '/org/freedesktop/DBus', + 'org.freedesktop.DBus', None) + + #list all names + self.bus_proxy.ListNames('()', + result_handler=self.__list_names_handler, + error_handler=self.__list_names_error_handler) + + def __tree_view_cursor_changed_cb(self, treeview): + """ do something when a row is selected """ + selection = self.treeview.get_selection() + if selection: + model, iter = selection.get_selected() + if not iter: + return + + bus_name_obj = model.get(iter, 0)[0] + #remove current child + c2 = self.paned_buswatch.get_child2() + if c2: + c2.destroy() + try: + del(self.addr_info) + except: + pass + + #add Introspection to paned + self.addr_info = AddressInfo(self.address, bus_name_obj.bus_name_unique, connection_is_bus=True) + self.paned_buswatch.add2(self.addr_info.introspect_box) + + #update info about selected bus name + self.label_bus_name_selected_name.set_text(bus_name_obj.bus_name_unique) + self.label_bus_name_selected_pid.set_text("%s" % bus_name_obj.pid) + self.label_bus_name_selected_cmdline.set_text(bus_name_obj.cmdline) + self.grid_bus_name_selected_info.set_visible(True) + + + def __liststore_model_add(self, bus_name_obj): + """ add a DBusBusName object to the liststore model """ + #update bus info stuff + self.bus_proxy.GetConnectionUnixProcessID('(s)', bus_name_obj.bus_name_unique, + result_handler = self.__get_unix_process_id_cb, + error_handler = self.__get_unix_process_id_error_cb, + user_data=bus_name_obj) + #add bus to liststore + return self.liststore_model.append([bus_name_obj, 0, u"%s" % (bus_name_obj.bus_name_unique), bus_name_obj.cmdline]) + + + def __liststore_model_remove(self, bus_name_obj): + """ remove a DBusBusName object to the liststore model """ + for n, obj in enumerate(self.liststore_model): + if obj[2] == bus_name_obj.bus_name_unique: + del(self.liststore_model[n]) + break + + def __liststore_model_get(self, bus_name_obj): + """ get a object from the liststore """ + for n, obj in enumerate(self.liststore_model): + if obj[2] == bus_name_obj.bus_name_unique: + return obj + raise Exception("bus name object '%s' not found in liststore" % (bus_name_obj)) + + + def __name_owner_changed_cb(self, connection, sender_name, object_path, interface_name, signal_name, parameters, user_data): + """ bus name added or removed """ + bus_name = parameters[0] + old_owner = parameters[1] + new_owner = parameters[2] + + bus_name_obj = DBusBusName(bus_name) + + if bus_name[0] == ':': + if not old_owner: + self.__liststore_model_add(bus_name_obj) + else: + self.__liststore_model_remove(bus_name_obj) + else : + if new_owner: + self.__liststore_model_add(bus_name_obj) + if old_owner: + self.__liststore_model_remove(bus_name_obj) + + + def __list_names_handler(self, obj, names, userdata): + for n in names: + bus_name_obj = DBusBusName(n) + self.__liststore_model_add(bus_name_obj) + + + def __list_names_error_handler(self, obj, error, userdata): + print "error getting bus names: %s" % str(error) + + + def __get_unix_process_id_cb(self, obj, pid, bus_name_obj): + bus_name_obj.pid = pid + + + def __get_unix_process_id_error_cb(self, obj, error, bus_name_obj): + print "error getting unix process id for %s: %s" % (bus_name_obj.bus_name_unique, str(error)) + bus_name_obj.pid = 0 + + +if __name__ == "__main__": + """ for debugging """ + import sys + import argparse + + parser = argparse.ArgumentParser(description='show a given bus address') + parser.add_argument('addr') + p = parser.parse_args() + + if p.addr.lower() == 'system': + addr = Gio.BusType.SYSTEM + elif p.addr.lower() == 'session': + addr = Gio.BusType.SESSION + else: + addr = p.addr + + bw = BusWatch(addr) + + win = Gtk.Window() + win.connect("delete-event", Gtk.main_quit) + win.set_default_size(1024, 768) + win.add(bw.paned_buswatch) + win.show_all() + try: + Gtk.main() + except (KeyboardInterrupt, SystemExit): + Gtk.main_quit() + + diff --git a/dfeet/dbus_introspector.py b/dfeet/dbus_introspector.py deleted file mode 100644 index 5bda27d..0000000 --- a/dfeet/dbus_introspector.py +++ /dev/null @@ -1,477 +0,0 @@ - -from gi.repository import GObject, Gdk, GdkPixbuf, Gtk, Gio -import _util -import _introspect_parser -import os -import _ui.wnck_utils - -from introspect_data import IntrospectData - -SESSION_BUS = 1 -SYSTEM_BUS = 2 -ADDRESS_BUS = 3 - -class Error(Exception): - pass - -class BusAddressError(Error): - def __init__(self, address): - self.address = address - - def __str__(self): - return repr('Bus address \'%s\' is not a valid bus address' % self.address) - -class InvalidColumnError(Error): - def __init__(self, col): - self.column = col - - def __str__(self): - return repr('Column number \'%i\' requested but is not valid' % self.column) - -class CommonNameData(GObject.GObject): - __gsignals__ = { - 'introspect_data_changed' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,()) - } - - def __init__(self): - super(CommonNameData, self).__init__() - - self.unique_name = None - self.bus = None - self.process_id = None - self.process_path = None - self.process_name = 'Unknown or Remote' - self._introspecting = False - self._introspection_data = IntrospectData() - self._introspect_object_queue = [] - - # there is a race condition here but D-Bus - # doesn't have a signal for introspection data change - # which would be impractical for some highly dynamic objects - def _introspect(self, name, node): - self._introspecting = True - obj = self.bus.get_object(name, node, follow_name_owner_changes=True) - obj.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable', - reply_handler=lambda xml: self._query_introspect_data_cb(name, node, xml), - error_handler=self._query_introspect_data_error_cb) - - def _query_introspect_data_error_cb(self, error): - print error - self._introspecting = False - # errors are expected - - def _query_introspect_data_cb(self, name, parent_node, xml): - self._introspecting = False - data = _introspect_parser.process_introspection_data(xml) - for node in data['child_nodes']: - cnode = '' - if parent_node != '/': - cnode = parent_node - - cnode += '/' + node - self._introspect_object_queue.append(cnode) - - if self._introspect_object_queue: - obj = self._introspect_object_queue.pop(0) - self._introspect(name, obj) - - if data['interfaces']: - self._introspection_data.append(parent_node, data) - - - self.emit('introspect_data_changed') - -class BusName(GObject.GObject): - __gsignals__ = { - 'changed' : (GObject.SIGNAL_RUN_LAST, GObject.TYPE_NONE,()) - } - - - def __init__(self, unique_name, bus, common_name = None, clone_from_busname = None): - - super(BusName, self).__init__() - - if not clone_from_busname: - self.common_data = CommonNameData() - else: - self.common_data = clone_from_busname.common_data - - if common_name: - self.set_common_name(str(common_name)) - else: - self.clear_common_name() - - self.common_data.bus = bus - self.common_data.unique_name = str(unique_name) - self.common_data.connect('introspect_data_changed', - self._introspect_data_changed_cb) - - self.busname_is_public = not (not common_name) - - def query_introspect(self, node = '/'): - self.common_data._introspect(self.get_display_name(), node) - - def _introspect_data_changed_cb(self, data): - ''' - print "*********************************************" - print len(data._introspection_data.object_paths.object_path_list) - i = 0 - for o in data._introspection_data.object_paths.object_path_list: - print i, o - i+=1 - print "*********************************************"''' - self.emit('changed') - - def set_common_name(self, common_name): - self.busname_is_public = True - self.common_name = common_name - self.emit('changed') - - def get_display_name(self): - if self.is_public(): - result = self.get_common_name() - else: - result = self.get_unique_name() - - return result - - def get_icon(self): - icon_table = _ui.wnck_utils.IconTable.get_instance() - return icon_table.get_icon(self.get_process_id()) - - def get_bus(self): - return self.common_data.bus - - def clear_common_name(self): - self.busname_is_public = False - self.common_name = None - - def is_public(self): - return self.busname_is_public - - def set_process_info(self, id, path): - self.common_data.process_id = id - self.common_data.process_path = path - if path: - self.common_data.process_name = os.path.basename(path[0]) - - self.emit('changed') - - def get_unique_name(self): - return self.common_data.unique_name - - def get_common_name(self): - return self.common_name - - def get_process_id(self): - return self.common_data.process_id - - def get_process_path(self): - return self.common_data.process_path - - def get_process_name(self): - return self.common_data.process_name - - def __str__(self): - result = self.common_data.unique_name - result += '\n Process ID: ' - if self.common_data.process_id: - result += str(self.common_data.process_id) - else: - result += 'Unknown' - - result += '\n Process Name: ' - if self.common_data.process_path: - result += self.common_data.process_name - else: - result += 'Unknown' - - result += '\n Well Known Name' - if not self.common_name: - result += ': None' - else: - result += ': ' - result += self.common_name - - return result - -class BusWatch(Gtk.ListStore): - NUM_COL = 9 - - (BUSNAME_OBJ_COL, - UNIQUE_NAME_COL, - COMMON_NAME_COL, - IS_PUBLIC_COL, # has a common name - PROCESS_ID_COL, - PROCESS_PATH_COL, - PROCESS_NAME_COL, - DISPLAY_COL, - ICON_COL) = range(NUM_COL) - - COL_TYPES = (GObject.TYPE_PYOBJECT, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GObject.TYPE_BOOLEAN, - GObject.TYPE_STRING, - GObject.TYPE_PYOBJECT, - GObject.TYPE_STRING, - GObject.TYPE_STRING, - GdkPixbuf.Pixbuf) - - def __init__(self, bus, address=None): - self.bus = None - self.unique_names = {} - self.name_list = [] - self.bus_type = bus - self.address = address - #self.completion_model = Gtk.ListStore() - - super(BusWatch, self).__init__(*self.COL_TYPES) - - if bus == SESSION_BUS: - self.bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) - elif bus == SYSTEM_BUS: - self.bus = Gio.bus_get_sync(Gio.BusType.SYSTEM, None) - else: - if not address: - raise BusAddressError(address) - self.bus = Gio.DBusConnection.new_for_address_sync( - address, - Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, - None, None) - - if not self.bus: - raise BusAddressError(address) - - self.bus.signal_subscribe('', - 'org.freedesktop.DBus', - 'NameOwnerChanged', - '', - '', - 0, - self.name_owner_changed_cb, - None) - self.bus_proxy = Gio.DBusProxy.new_sync(self.bus, - Gio.DBusProxyFlags.NONE, - None, - 'org.freedesktop.DBus', - '/org/freedesktop/DBus', - 'org.freedesktop.DBus', None) - - self.bus_proxy.ListNames('()', - result_handler=self.list_names_handler, - error_handler=self.list_names_error_handler) - - def get_bus_name(self): - if self.bus_type == SESSION_BUS: - return "Session Bus" - elif self.bus_type == SYSTEM_BUS: - return "System Bus" - else: - return self.address - - def get_bus_type(self): - return self.bus_type - - def get_bus_address(self): - return self.address - - def close_bus(self): - self.bus.close() - - def close_bus(self): - self.bus.close() - - def get_completion_model(self): - return self.completion_model() - - def get_unix_process_id_cb(self, obj, id_, name): - names = self.unique_names[name] - if not names: - return - - name = names[0] - if not name: - return - - process_path = _util.get_proc_from_pid(id_) - name.set_process_info(id_, process_path) - - def get_unix_process_id_error_cb(self, obj, error, name): - print error - - # get the Unix process ID so we can associate the name - # with a process (this will only work under Unix like OS's) - def get_unix_process_id_async_helper(self, name): - self.bus_proxy.GetConnectionUnixProcessID('(s)', name, - result_handler = self.get_unix_process_id_cb, - error_handler = self.get_unix_process_id_error_cb, - user_data=name) - - - def name_changed_cb(self, name): - pass - path = (self.name_list.index(name,)) - iter = self.get_iter(path) - self.row_changed(path, iter) - - # if name is not unique and owner is set add the name to the BusName object - # else create a new BusName object - def add_name(self, name, owner=None): - if name[0] == ':': - if self.unique_names.has_key(name): - return - - busnameobj = BusName(name, self.bus) - busnameobj.connect('changed', self.name_changed_cb) - self.unique_names[name] = [busnameobj] - self.get_unix_process_id_async_helper(name) - self.name_list.append(busnameobj) - - self.append((busnameobj, - busnameobj.get_unique_name(), - busnameobj.get_common_name(), - busnameobj.is_public(), - busnameobj.get_process_id(), - busnameobj.get_process_path(), - busnameobj.get_process_name(), - busnameobj.get_unique_name(), - busnameobj.get_icon())) - else: - if not owner: - owner = self.bus_proxy.GetNameOwner('(s)', name) - if owner == 'org.freedesktop.DBus': - return - - # if owner still does not exist then we move on - if not owner: - return - - if self.unique_names.has_key(owner): - busnameobj = self.unique_names[owner][0] - if busnameobj.is_public(): - busnameobj = BusName(owner, self.bus, name, busnameobj) - self.unique_names[owner].append(busnameobj) - self.name_list.append(busnameobj) - - self.append((busnameobj, - busnameobj.get_unique_name(), - busnameobj.get_common_name(), - busnameobj.is_public(), - busnameobj.get_process_id(), - busnameobj.get_process_path(), - busnameobj.get_process_name(), - busnameobj.get_common_name(), - busnameobj.get_icon())) - else: - busnameobj.set_common_name(name) - - else: - busnameobj = BusName(owner, self.bus, name) - self.unique_names[owner] = [busnameobj] - self.name_list.append(busnameobj) - self.get_unix_process_id_async_helper(owner) - self.append((busnameobj, - busnameobj.get_unique_name(), - busnameobj.get_common_name(), - busnameobj.is_public(), - busnameobj.get_process_id(), - busnameobj.get_process_path(), - busnameobj.get_process_name(), - busnameobj.get_common_name(), - busnameobj.get_icon())) - - def remove_name(self, name, owner=None): - if not name: - return - - if self.unique_names.has_key(name): - busnames = self.unique_names[name] - for n in busnames: - self.remove_name(n.common_name, name) - - index = self.name_list.index(self.unique_names[name][0]) - self.name_list.pop(index) - - del(self.unique_names[name]) - self.row_deleted((index,)) - else: - if not owner: - return - - # names may have been deleted already - if self.unique_names.has_key(owner): - busnames = self.unique_names[owner] - if len(busnames) == 1: - if busnames[0].common_name == name: - busnames[0].clear_common_name() - path = (self.name_list.index(busnames[0]),) - iter = self.get_iter(path) - self.row_changed(path, iter) - else: - for n in busnames: - if n.common_name == name: - self.unique_names[owner].remove(n) - index = self.name_list.index(n) - self.name_list.pop(index) - self.row_deleted((index,)) - - def name_owner_changed_cb(self, name, old_owner, new_owner): - if name[0] == ':': - if not old_owner: - self.add_name(name) - else: - self.remove_name(name) - - else : - if new_owner: - self.add_name(name, new_owner) - - if old_owner: - self.remove_name(name, old_owner) - - def list_names_handler(self, obj, results, userdata): - names = results - for n in names: - self.add_name(n) - - def list_names_error_handler(self, obj, error, userdata): - print "error getting bus names - %s" % str(error) - - def get_name_list(self): - return self.name_list - - def on_get_value(self, rowref, column): - name = rowref[0] - child = -1 - if len(rowref) > 1: - child = rowref[1] - - if column == self.BUSNAME_OBJ_COL: - return name - elif column == self.UNIQUE_NAME_COL: - return name.get_unique_name() - elif column == self.COMMON_NAME_COL: - return name.get_common_name() - elif column == self.IS_PUBLIC_COL: - return name.is_public() - elif column == self.PROCESS_ID_COL: - return name.get_process_id() - elif column == self.PROCESS_PATH_COL: - return name.get_process_path() - elif column == self.PROCESS_NAME_COL: - return name.get_process_name() - elif column == self.ICON_COL: - return name.get_icon() - elif column == self.DISPLAY_COL: - if child == -1: - return '<b>' + GObject.markup_escape_text(name.get_display_name()) + '</b>' - elif child == 1: - return '<b>Command Line: </b>' + GObject.markup_escape_text(name.get_process_name()) + ' (' + GObject.markup_escape_text(str(name.get_process_id())) + ')' - elif child == 0: - return '<b>Unique Name: </b>'+ GObject.markup_escape_text(name.get_unique_name()) - else: - raise InvalidColumnError(column) - - return None - diff --git a/dfeet/dbus_utils.py b/dfeet/dbus_utils.py index 10554bf..fa38ba5 100644 --- a/dfeet/dbus_utils.py +++ b/dfeet/dbus_utils.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- def convert_complex_type(subsig): result = None len_consumed = 0 diff --git a/dfeet/introspect_data.py b/dfeet/introspect_data.py deleted file mode 100644 index 8a71746..0000000 --- a/dfeet/introspect_data.py +++ /dev/null @@ -1,678 +0,0 @@ -from gi.repository import Gtk -import gobject -import dbus_utils -import dbus -import _util - -class Node: - def __init__(self, model, parent = None): - self.child_list = [] - self.parent = parent - self.model = model - self.expanded = False - self.valid = True - - # default markup for signatures - self.sig_props = 'foreground="#2E8B57" size="smaller"' - self.name_props = 'foreground="#000000" size="smaller"' - - def on_selected(self, busname): - # do nothing here but this is called whenever - # a node is selected in the model so can be - # overridden (see Property class for example) - pass - - def mark_tree_invalid(self): - self.valid = False - for child in self.child_list: - child.mark_tree_invalid() - - def mark_valid(self): - self.valid = True - - def reap_ivalid(self): - for child in self.child_list: - if child.reap_invalid(): - pass - # FIXME - we need to reap items that are no longer valid - return not self.valid - - def set_expanded(self, expanded): - self.expanded = expanded - - def is_expanded(self): - expanded = False - if self.child_list: - expanded = self.expanded - - return expanded - - def index(self, child): - return self.child_list.index(child) - - def on_get_iter(self, path): - op = self.child_list[path[0]] - if len(path) == 1: - return op - else: - return op.on_get_iter(path[1:]) - - def get_path_pos(self): - pos = 0 - if self.parent: - pos = self.parent.index(self) - - return pos - - def get_next(self): - next = None - if self.parent: - next = self.parent.get_next_child(self) - - return next - - def get_next_child(self, child): - i = self.index(child) - try: - return self.child_list[i+1] - except IndexError: - return None - - def get_first_child(self): - if self.child_list: - return self.child_list[0] - else: - return None - - def get_nth_child(self, n): - try: - return self.child_list[n] - except IndexError: - return None - - def count_children(self): - c = len(self.child_list) - return c - - def _row_changed(self, path): - iter = self.model.get_iter(path) - self.model.row_changed(path, iter) - - def _row_inserted(self, path): - iter = self.model.get_iter(path) - self.model.row_inserted(path, iter) - - def _calculate_path(self, *append_args): - p = [self.get_path_pos()] - parent = self.parent - while parent: - p.insert(0, parent.get_path_pos()) - parent = parent.parent - - for pos in append_args: - p.append(pos) - - return p - - def append(self, child): - self.insert(-1, child) - - def insert(self, i, child): - if i == -1: - self.child_list.append(child) - i = len(self.child_list) - 1 - else: - self.child_list.insert(i, child) - - child_path = self._calculate_path(i) - self._row_inserted(tuple(child_path)) - - #my_path = tuple(child_path[0:-1]) - #my_iter = None - #if my_path: - # my_iter = self.model.get_iter(my_path) - - #l = len(self.child_list) - #reorder_list = range(l) - #self.model.rows_reordered(my_path, my_iter, reorder_list) - - - #if i < l - 1: - # last_path_item = len(path) - 1 - # for n in range(i + 1, l): - # path[last_path_item] = n - # self._row_changed(tuple(path)) - - def find(self, node): - i = 0 - for path in self.child_list: - spath = str(path) - if spath >= node: - return i - - i+=1 - - return -1 - - def add(self, data): - # every node needs to figure this out for themselves - pass - - def _add_child(self, child, data): - child_path = None - - i = self.find(str(child)) - if i == -1: - self.append(child) - else: - objpath = self.child_list[i] - if str(objpath) == str(child): - child = objpath - child.mark_valid() - child_path = tuple(self._calculate_path(i)) - else: - self.insert(i, child) - - child.add(data) - if child_path: - child._row_changed(child_path) - - def get_icon_name(self): - return None - - def to_markup_str(self): - return gobject.markup_escape_text(str(self)) - - def _sig_list_to_string(self, sig_list): - result = '' - - for sig in sig_list: - if sig == []: - print sig_list - return '' - result += sig['type'] - if sig['name']: - result += ' ' + sig['name'] - - result += ', ' - - # get rid of the last comma and space before returning - return result[0:-2] - - def _sig_list_to_markup(self, sig_list): - result = '' - - for sig in sig_list: - result += dbus_utils.sig_to_markup(sig['type'], self.sig_props) - if sig['name']: - result += ' <span ' + self.name_props + '>' + sig['name'] + '</span>' - result += ', ' - - # get rid of the last comma and space before returning - return result[0:-2] - - -""" -Leaf nodes are in a special class because they have special functionality -The LeafNode class is a helper class which provide common functionality that -is only of interest to leaf nodes -""" -class LeafNode(Node): - def get_proxy_interface(self, bus, name): - # it is not strictly necisary to traverse the tree - # but it future proofs things just in case we move - # or remove node types - iface = '' - obj_path = '' - node = self.parent - - while node is not None: - if isinstance(node, Interface): - iface = str(node) - - if isinstance(node, ObjectPath): - obj_path = str(node) - - node = node.parent - - if not (iface and obj_path): - raise Exception('introspection tree is corrupt') - - proxy = bus.get_object(name, obj_path) - iface = dbus.Interface(proxy, iface) - - return (proxy, iface) - -class Method(LeafNode): - # tree path = (0,x,0,y,0,z) - def __init__(self, model, parent, method, insig, outsig): - LeafNode.__init__(self, model, parent) - - self.method = method - self.insig = insig - self.outsig = outsig - - def dbus_call(self, bus, name, *args): - (proxy, iface) = self.get_proxy_interface(bus, name) - func = getattr(iface, self.method) - - return func(*args) - - def to_markup_str(self): - sig_props = 'foreground="#2E8B57" size="smaller"' - name_props = '' - result = self.method + '<span foreground="#FF00FF">(</span>' - result += self._sig_list_to_markup(self.insig) - result += '<span foreground="#FF00FF">)</span>' - - if self.outsig: - result += '<span foreground="#A52A2A">' + u' \u27A1 ' + '</span>' - result += '<span foreground="#FF00FF">(</span>' - result += self._sig_list_to_markup(self.outsig) - result += '<span foreground="#FF00FF">)</span>' - - return result - - def get_icon_name(self): - return 'dfeet-method' - - def __str__(self): - result = self.method + '(' - result += self._sig_list_to_string(self.insig) + ')' - - if self.outsig: - result += u' \u27A1 (' + self._sig_list_to_string(self.outsig) + ')' - - return result - -class Signal(LeafNode): - # tree path = (0,x,0,y,1,z) - def __init__(self, model, parent, signal, insig): - self.signal = signal - self.insig = insig - - LeafNode.__init__(self, model, parent) - - def dbus_call(self, bus, name, *args): - # for testing out signals - # this actually isn't going to work since I - # can't spoof the sender unless I can own the name - pass - - def get_icon_name(self): - return 'dfeet-signal' - - def to_markup_str(self): - sig_props = 'foreground="#2E8B57" size="smaller"' - result = self.signal + '<span foreground="#FF00FF">(</span>' - result += self._sig_list_to_markup(self.insig) - result += '<span foreground="#FF00FF">)</span>' - - return result - - def __str__(self): - result = self.signal + '(' - result += self._sig_list_to_string(self.insig) + ')' - - return result - -class Property(LeafNode): - # tree path = (0,x,0,y,2,z) - def __init__(self, model, parent, property, sig, access): - self.property = property - self.sig = sig - self.access = access - self.value = None - - LeafNode.__init__(self, model, parent) - - def on_selected(self, busname): - # when selected use the properties interface - # to read a value if this is a read or - # readwrite property - if self.access.startswith('read'): - bus = busname.get_bus() - (proxy, iface) = self.get_proxy_interface(bus, busname.get_display_name()) - - proxy.Get(iface.dbus_interface, - self.property, - dbus_interface='org.freedesktop.DBus.Properties', - reply_handler=self.property_get_handler, - error_handler=self.property_get_error_handler) - - - - def property_get_handler(self, value): - self.value = value - - path = tuple(self._calculate_path()) - self._row_changed(path) - - def property_get_error_handler(self, e): - print e - - def dbus_call(self, bus, name, *args): - # for testing out properties - # this is not implemented yet - pass - - def get_icon_name(self): - icon = 'dfeet-property' - if self.access == 'read': - icon = 'dfeet-read-property' - elif self.access == 'write': - icon = 'dfeet-write-property' - elif self.access == 'readwrite': - icon = 'dfeet-readwrite-property' - - return icon - - def to_markup_str(self): - self.sig_props = 'foreground="#2E8B57" size="smaller"' - self.name_props = '' - result = self._sig_list_to_markup(self.sig) - - if self.value: - result += ' = ' + str(self.value) - - return result - - def __str__(self): - result = self._sig_list_to_string(self.sig) - if self.value: - result += ' = ' + str(self.value) - - return result - -class MethodLabel(Node): - # tree path = (0,x,0,y,0) - def __init__(self, model, parent): - Node.__init__(self, model, parent) - self.set_expanded(True) - self._label = 'Methods' - - def add(self, data): - method_list = data.keys() - method_list.sort() - for method_name in method_list: - method = Method(self.model, self, method_name, data[method_name][0], data[method_name][1]) - self._add_child(method, None) - - def get_icon_name(self): - return 'dfeet-method-category' - - - def to_markup_str(self): - return '<b>' + gobject.markup_escape_text(self._label) + '</b>' - - def __str__(self): - return self._label - -class SignalLabel(Node): - # tree path = (0,x,0,y,1) - def __init__(self, model, parent): - Node.__init__(self, model, parent) - self.set_expanded(True) - self._label = 'Signals' - - def add(self, data): - signal_list = data.keys() - signal_list.sort() - for signal_name in signal_list: - signal = Signal(self.model, self, signal_name, data[signal_name][0]) - self._add_child(signal, None) - - def get_icon_name(self): - return 'dfeet-signal-category' - - def to_markup_str(self): - return '<b>' + gobject.markup_escape_text(self._label) + '</b>' - - def __str__(self): - return self._label - -class PropertyLabel(Node): - # tree path = (0,x,0,y,2) - def __init__(self, model, parent): - Node.__init__(self, model, parent) - self.set_expanded(True) - self._label = 'Properties' - - def add(self, data): - property_list = data.keys() - property_list.sort() - for property_name in property_list: - property = Property(self.model, self, property_name, data[property_name][0], data[property_name][1]) - self._add_child(property, None) - - def get_icon_name(self): - return 'dfeet-property-category' - - def to_markup_str(self): - return '<b>' + gobject.markup_escape_text(self._label) + '</b>' - - def __str__(self): - return self._label - -class Interface(Node): - # tree path = (0,x,0,y) - def __init__(self, model, parent, interface): - self.iface = interface - Node.__init__(self, model, parent) - - def add(self, data): - method_data = data['methods'] - signal_data = data['signals'] - property_data = data['properties'] - - if method_data: - methods = MethodLabel(self.model, self) - self._add_child(methods, method_data) - - if signal_data: - signals = SignalLabel(self.model, self) - self._add_child(signals, signal_data) - - if property_data: - properties = PropertyLabel(self.model, self) - self._add_child(properties, property_data) - - def __str__(self): - return self.iface - -class InterfaceLabel(Node): - # tree path = (0,x,0) - def __init__(self, model, parent): - Node.__init__(self, model, parent) - self.set_expanded(True) - - self._label='Interfaces' - - def add(self, data): - iface_list = data.keys() - iface_list.sort() - for iface in iface_list: - interface = Interface(self.model, self, iface) - self._add_child(interface, data[iface]) - - def to_markup_str(self): - return '<b>' + gobject.markup_escape_text(self._label) + '</b>' - - def __str__(self): - return self._label - -class ObjectPath(Node): - # tree path = (0,x) - def __init__(self, model, parent, path): - self.path = path - - Node.__init__(self, model, parent) - - def add(self, data): - iface_data = data['interfaces'] - - interfaces = InterfaceLabel(self.model, self) - self._add_child(interfaces, iface_data) - - def get_icon_name(self): - return 'dfeet-object' - - def __str__(self): - return self.path - -class ObjectPathLabel(Node): - # tree path = (0,) - def __init__(self, model): - #super(Node, self).__init__(model) - Node.__init__(self, model) - self.set_expanded(True) - - self._label='Object Paths' - - def add(self, data, node): - obj_path = ObjectPath(self.model, self, node) - - self._add_child(obj_path, data) - - def to_markup_str(self): - return '<b>' + gobject.markup_escape_text(self._label) + '</b>' - - def __str__(self): - return self._label - -class IntrospectData(Gtk.TreeStore): - NUM_COL = 3 - - (SUBTREE_COL, - DISPLAY_COL, - ICON_NAME_COL) = range(NUM_COL) - - COL_TYPES = (gobject.TYPE_PYOBJECT, - gobject.TYPE_STRING, - gobject.TYPE_STRING) - - """ - Path Values: - (0,) - "Object Paths" - (0,x) - object path - (0,x,0) - "Interfaces" - (0,x,0,y) - interface - (0,x,0,y,0) - "Methods" - (0,x,0,y,1) - "Signals" - (0,x,0,y,2) - "Properties" - (0,x,0,y,0,z) - method signature - (0,x,0,y,1,z) - signal signature - (0,x,0,y,1,z) - property signature - - """ - - def __init__(self): - super(IntrospectData, self).__init__() - - self.object_paths = ObjectPathLabel(self) - - def append(self, parent_node, data): - # mark the object path tree invalid if it already - # exists so we can keep track of state and reap - # any branches which are not in the new introspect - # data - i = self.object_paths.find(parent_node) - if i >= 0: - objpath = self.object_paths.child_list[i] - if str(objpath) == parent_node: - objpath.mark_tree_invalid() - - self.object_paths.add(data, parent_node) - del(data) - - def on_get_flags(self): - return Gtk.TREE_MODEL_ITERS_PERSIST - - def on_get_n_columns(self): - return self.NUM_COL - - def on_get_column_type(self, n): - return self.COL_TYPES[n] - - def on_get_iter(self, path): - if len(path) == 1: - if path[0] > 0: - return None - return self.object_paths - else: - return self.object_paths.on_get_iter(path[1:]) - - def on_get_path(self, rowref): - pl = [rowref.get_path_pos()] - parent = rowref.parent - while parent: - pl.insert(0, parent.get_path_pos()) - parent = parent.parent - - return tuple(pl) - - def on_get_value(self, rowref, column): - if column == self.SUBTREE_COL: - return rowref - elif column == self.DISPLAY_COL: - return rowref.to_markup_str() - elif column == self.ICON_NAME_COL: - return rowref.get_icon_name() - else: - raise InvalidColumnError(column) - - return None - - def on_iter_next(self, rowref): - try: - return rowref.get_next() - except: - return None - - def on_iter_children(self, parent): - if parent: - return parent.get_first_child() - - return self.object_paths - - def on_iter_has_child(self, rowref): - if rowref.get_first_child(): - return True - else: - return False - - def on_iter_n_children(self, rowref): - if rowref: - return rowref.count_children() - - return 1 - - def on_iter_nth_child(self, parent, n): - if parent: - return parent.get_nth_child(n) - - if n == 0: - return self.object_paths - else: - return None - - def on_iter_parent(self, child): - return child.parent - - def _node_to_str(self, node, prefix=''): - if not node: - return "" - - if prefix == None: - prefix = "" - - result = prefix + str(node) + '\n' - for child in node.child_list: - result += self._node_to_str(child, prefix + '\t') - - return result - - def __str__(self): - - return self._node_to_str(self.object_paths) - diff --git a/dfeet/introspection.py b/dfeet/introspection.py new file mode 100644 index 0000000..a37a811 --- /dev/null +++ b/dfeet/introspection.py @@ -0,0 +1,229 @@ +# -*- coding: utf-8 -*- + +from gi.repository import GObject, Gdk, GdkPixbuf, Gtk, Gio, Pango, GLib +import dbus_utils +from _ui.executemethoddialog import ExecuteMethodDialog +import time + +from _ui.uiloader import UILoader + +from introspection_helper import DBusNode, DBusInterface, DBusProperty, DBusSignal, DBusMethod + + +class AddressInfo(): + """ + class to handle information about a name (eg "org.freedesktop.NetworkManager") + on a given address (eg Gio.BusType.SYSTEM or unix:path=/var/run/dbus/system_bus_socket) + """ + def __init__(self, address, name, connection_is_bus=True): + self.address = address # can be Gio.BusType.SYSTEM or Gio.BusType.SYSTEM or an other address + self.name = name + self.connection_is_bus = connection_is_bus # is it a bus or a p2p connection? + self.introspect_time = -1 # time needed to introspect the given bus name (in seconds) + + #setup UI + ui = UILoader(UILoader.UI_INTROSPECTION) + self.introspect_box = ui.get_root_widget() + self.tree_model = ui.get_widget('treestore') + self.treeview = ui.get_widget('treeview') + button_reload = ui.get_widget('button_reload') + self.label_name = ui.get_widget('label_name') + self.label_unique_name = ui.get_widget('label_unique_name') + self.label_address = ui.get_widget('label_address') + cr_name = Gtk.CellRendererText() + col_name = Gtk.TreeViewColumn() + col_name.pack_start(cr_name, True) + col_name.add_attribute(cr_name, 'markup', 0) + self.treeview.append_column(col_name) + + #connect signals + button_reload.connect("clicked", self.__on_button_reload_clicked, self) + self.treeview.connect('cursor-changed', + self.tree_view_cursor_changed_cb) + self.treeview.connect('row-activated', + self.tree_view_row_activated_cb) + + if self.connection_is_bus: + #we expect a bus connection + if self.address == Gio.BusType.SYSTEM or self.address == Gio.BusType.SESSION: + self.connection = Gio.bus_get_sync(self.address, None) + self.label_address.set_text(Gio.dbus_address_get_for_bus_sync(self.address, None)) + elif Gio.dbus_is_supported_address(self.address) == True: + self.connection = Gio.DBusConnection.new_for_address_sync(self.address, + Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT | Gio.DBusConnectionFlags.MESSAGE_BUS_CONNECTION, + None, None) + self.label_address.set_text(self.address) + else: + self.connection = None + raise Exception("Invalid bus address '%'" % (self.address)) + else: + #we have a peer-to-peer connection + if Gio.dbus_is_supported_address(self.address) == True: + self.connection = Gio.DBusConnection.new_for_address_sync(self.address, + Gio.DBusConnectionFlags.AUTHENTICATION_CLIENT, + None, None) + self.label_address.set_text(self.address) + else: + self.connection = None + raise Exception("Invalid p2p address '%'" % (self.address)) + + #start processing data + self.introspect() + self.treeview.expand_all() #FIXME: only good for testing! + + def tree_view_cursor_changed_cb(self, treeview): + """ do something when a row is selected """ + selection = self.treeview.get_selection() + if selection: + model, iter = selection.get_selected() + if not iter: + return + + node_obj = model.get(iter, 1)[0] + #print "NODE: %s" % (node_obj) + + def tree_view_row_activated_cb(self, treeview, path, view_column): + model = treeview.get_model() + iter = model.get_iter(path) + + obj = model.get_value(iter, 1) + + if isinstance(obj, DBusMethod): + #execute the selected method + dialog = ExecuteMethodDialog(self.connection, self.connection_is_bus, self.name, obj) + dialog.run() + elif isinstance(obj, DBusProperty): + #update the selected property (TODO: do this async) + proxy = Gio.DBusProxy.new_sync(self.connection, + Gio.DBusProxyFlags.NONE, + None, + self.name, + obj.object_path, + "org.freedesktop.DBus.Properties", None) + args = GLib.Variant('(ss)', (obj.iface_info.name, obj.property_info.name)) + result = proxy.call_sync("Get", args, 0, -1, None) + #update the object value so markup string is calculated correct + obj.value = result[0] + #set new markup string + model[iter][0] = obj.markup_str + else: + if treeview.row_expanded(path): + treeview.collapse_row(path) + else: + treeview.expand_row(path, False) + + + def introspect(self): + """ introspect the given bus name and update the tree model """ + #cleanup current tree model + self.tree_model.clear() + #start introspection and measure introspection time + introspect_start = time.time() + self.__dbus_node_introspect(self.name, "/") + self.introspect_time = time.time() - introspect_start + #update name, unique name, ... + self.label_name.set_text(self.name) + try: + self.label_unique_name.set_text(self.connection.get_unique_name()) + except: + pass + + + def __on_button_reload_clicked(self, widget, address_info): + """ reload the introspection data """ + address_info.introspect() + print "needed %.3f s to introspect name '%s'" % (address_info.introspect_time, address_info.name) + + + def __dbus_node_introspect(self, name, object_path): + """ iterate over the given object_path and introspect the path recursive """ + tree_iter = None + if self.connection_is_bus: + proxy = Gio.DBusProxy.new_sync(self.connection, + Gio.DBusProxyFlags.NONE, + None, + name, + object_path, + 'org.freedesktop.DBus.Introspectable', None) + node_info = Gio.DBusNodeInfo.new_for_xml(proxy.Introspect()) + else: + res = self.connection.call_sync(None, object_path, 'org.freedesktop.DBus.Introspectable', 'Introspect', None, GLib.VariantType.new("(s)"), Gio.DBusCallFlags.NONE, -1, None) + node_info = Gio.DBusNodeInfo.new_for_xml(res[0]) + + #create a GObject node and add to tree-model + if len(node_info.interfaces) > 0: + node_obj = DBusNode(name, object_path, node_info) + tree_iter = self.tree_model.append(tree_iter, ["%s" % object_path, node_obj]) + #tree_iter = self.tree_model.append(tree_iter, ["Hallo", None]) + + #append interfaces to tree model + if len(node_info.interfaces) > 0: + name_iter = self.tree_model.append(tree_iter, ["<b>Interfaces</b>", None]) + for iface in node_info.interfaces: + iface_obj = DBusInterface(node_obj, iface) + iface_iter = self.tree_model.append(name_iter, ["%s" % iface.name, iface_obj]) + #interface methods + if len(iface.methods) > 0: + iface_methods_iter = self.tree_model.append(iface_iter, ["<b>Methods</b>", None]) + for iface_method in iface.methods: + method_obj = DBusMethod(iface_obj, iface_method) + self.tree_model.append(iface_methods_iter, ["%s" % method_obj.markup_str, method_obj]) + #interface signals + if len(iface.signals) > 0: + iface_signals_iter = self.tree_model.append(iface_iter, ["<b>Signals</b>", None]) + for iface_signal in iface.signals: + signal_obj = DBusSignal(iface_obj, iface_signal) + self.tree_model.append(iface_signals_iter, ["%s" % signal_obj.markup_str, signal_obj]) + #interface properties + if len(iface.properties) > 0: + iface_properties_iter = self.tree_model.append(iface_iter, ["<b>Properties</b>", None]) + for iface_property in iface.properties: + property_obj = DBusProperty(iface_obj, iface_property) + self.tree_model.append(iface_properties_iter, ["%s" % property_obj.markup_str, property_obj]) + #interface annotations #FIXME: add Annotation object!!! + if len(iface.annotations) > 0: + iface_annotations_iter = self.tree_model.append(iface_iter, ["<b>Annotations</b>", None]) + for iface_annotation in iface.annotations: + self.tree_model.append(iface_annotations_iter, ["%s" % iface_annotation.name, iface_annotation]) + + + for node in node_info.nodes: + #node_iter = self.tree_model.append(tree_iter, [node.path, node]) + if object_path == "/": + object_path = "" + object_path_new = object_path + "/" + node.path + self.__dbus_node_introspect(name, object_path_new) + + + +if __name__ == "__main__": + """ for debugging """ + import sys + import argparse + + parser = argparse.ArgumentParser(description='introspect a given dbus address and name') + parser.add_argument('-p', '--p2p', action='store_true', default=False) + parser.add_argument('addr') + parser.add_argument('name') + p = parser.parse_args() + + if p.addr.lower() == 'system': + addr = Gio.BusType.SYSTEM + elif p.addr.lower() == 'session': + addr = Gio.BusType.SESSION + else: + addr = p.addr + + + name = p.name + ai = AddressInfo(addr, name, not p.p2p) + + win = Gtk.Window() + win.connect("delete-event", Gtk.main_quit) + win.set_default_size(1024, 768) + win.add(ai.introspect_box) + win.show_all() + try: + Gtk.main() + except (KeyboardInterrupt, SystemExit): + Gtk.main_quit() diff --git a/dfeet/introspection_helper.py b/dfeet/introspection_helper.py new file mode 100644 index 0000000..d7214eb --- /dev/null +++ b/dfeet/introspection_helper.py @@ -0,0 +1,218 @@ +# -*- coding: utf-8 -*- + +from gi.repository import GObject, Gio, GLib +import dbus_utils + + +def args_signature_markup(arg_signature): + return '<small><span foreground="#2E8B57">%s</span></small>' % (arg_signature) + +def args_name_markup(arg_name): + return '<small><span foreground="#000000">%s</span></small>' % (arg_name) + + + +class DBusNode(GObject.GObject): + """ object to represent a DBus Node (object path) """ + def __init__(self, name, object_path, node_info): + GObject.GObject.__init__(self) + self.__name = name + self.__object_path = object_path + self.__node_info = node_info # Gio.GDBusNodeInfo object + + def __repr__(self): + return u"Name: %s ; ObjPath: %s ; NodeInfo: %s" % (self.name, self.object_path, self.node_info) + + @property + def name(self): + return self.__name + + @property + def object_path(self): + return self.__object_path + + @property + def node_info(self): + return self.__node_info + + +class DBusInterface(DBusNode): + """ object to represent a DBus Interface """ + def __init__(self, dbus_node_obj, iface_info): + DBusNode.__init__(self, dbus_node_obj.name, dbus_node_obj.object_path, dbus_node_obj.node_info) + self.__iface_info = iface_info # Gio.GDBusInterfaceInfo object + + def __repr__(self): + return u"iface '%s' on node '%s'" % (self.iface_info.name, self.node_info.path) + + @property + def iface_info(self): + return self.__iface_info + + +class DBusProperty(DBusInterface): + """ object to represent a DBus Property """ + def __init__(self, dbus_iface_obj, property_info): + DBusInterface.__init__(self, dbus_iface_obj, dbus_iface_obj.iface_info) + self.__property_info = property_info # Gio.GDBusPropertyInfo object + self.__value = None #the value + + def __repr__(self): + sig = dbus_utils.sig_to_string(self.property_info.signature) + return u"%s %s (%s)" % (sig, self.property_info.name, self.property_info.flags) + + @property + def property_info(self): + return self.__property_info + + @property + def value(self): + return self.__value + + @value.setter + def value(self, new_val): + self.__value = new_val + + + @property + def markup_str(self): + sig = dbus_utils.sig_to_string(self.property_info.signature) + readwrite = list() + if self.readable: + readwrite.append("read") + if self.writable: + readwrite.append("write") + s = "%s %s <small>(%s)</small>" % (args_signature_markup(sig), args_name_markup(self.property_info.name), " / ".join(readwrite)) + if self.value: + s += " = %s" % (self.value) + return s + + @property + def readable(self): + if int(self.property_info.flags) == int(Gio.DBusPropertyInfoFlags.READABLE) or int(self.property_info.flags) == (int(Gio.DBusPropertyInfoFlags.WRITABLE | Gio.DBusPropertyInfoFlags.READABLE)): + return True + else: + return False + + @property + def writable(self): + if int(self.property_info.flags) == int(Gio.DBusPropertyInfoFlags.WRITABLE) or int(self.property_info.flags) == (int(Gio.DBusPropertyInfoFlags.WRITABLE | Gio.DBusPropertyInfoFlags.READABLE)): + return True + else: + return False + + +class DBusSignal(DBusInterface): + """ object to represent a DBus Signal """ + def __init__(self, dbus_iface_obj, signal_info): + DBusInterface.__init__(self, dbus_iface_obj, dbus_iface_obj.iface_info) + self.__signal_info = signal_info #Gio.GDBusSignalInfo object + + def __repr__(self): + return u"%s" % (self.signal_info.name) + + @property + def signal_info(self): + return self.__signal_info + + @property + def args(self): + args = list() + for arg in self.signal_info.args: + sig = dbus_utils.sig_to_string(arg.signature) + args.append({'signature': sig, 'name': arg.name}) + return args + + @property + def args_markup_str(self): + result = '' + result += '<span foreground="#FF00FF">(</span>' + result += ', '.join('%s' % (args_signature_markup(arg['signature'])) for arg in self.args) + result += '<span foreground="#FF00FF">)</span>' + return result + + @property + def markup_str(self): + return "%s %s" % (self.signal_info.name, self.args_markup_str) + + +class DBusMethod(DBusInterface): + """ object to represent a DBus Method """ + def __init__(self, dbus_iface_obj, method_info): + DBusInterface.__init__(self, dbus_iface_obj, dbus_iface_obj.iface_info) + self.__method_info = method_info #Gio.GDBusMethodInfo object + + def __repr__(self): + return u"%s(%s) ↦ %s (%s)" % (self.method_info.name, self.in_args_str, self.out_args_str, DBusInterface.__repr__(self)) + + @property + def method_info(self): + return self.__method_info + + @property + def iface_info(self): + return super(DBusMethod, self).iface_info + + @property + def name(self): + return super(DBusMethod, self).name + + @property + def object_path(self): + return super(DBusMethod, self).object_path + + @property + def node_info(self): + return super(DBusMethod, self).node_info + + @property + def markup_str(self): + return u"%s %s <b>↦</b> %s" % (self.method_info.name, self.in_args_markup_str, self.out_args_markup_str) + + @property + def in_args(self): + in_args = list() + for in_arg in self.method_info.in_args: + sig = dbus_utils.sig_to_string(in_arg.signature) + in_args.append({'signature': sig, 'name': in_arg.name}) + return in_args + + @property + def out_args(self): + out_args = list() + for out_arg in self.method_info.out_args: + sig = dbus_utils.sig_to_string(out_arg.signature) + out_args.append({'signature': sig, 'name': out_arg.name}) + return out_args + + @property + def in_args_str(self): + result = u'' + for arg in self.in_args: + result += "%s %s, " % (arg['signature'], arg['name']) + + return result[0:-2] + + @property + def out_args_str(self): + result = u'' + for arg in self.out_args: + result += "%s %s, " % (arg['signature'], arg['name']) + + return result[0:-2] + + def __args_markup_str(self, args): + """ markup a given list of args """ + result = '' + result += '<span foreground="#FF00FF">(</span>' + result += ', '.join('%s %s' % (args_signature_markup(arg['signature']), args_name_markup(arg['name'])) for arg in args) + result += '<span foreground="#FF00FF">)</span>' + return result + + @property + def in_args_markup_str(self): + return self.__args_markup_str(self.in_args) + + @property + def out_args_markup_str(self): + return self.__args_markup_str(self.out_args) diff --git a/tests/tests.py b/tests/tests.py new file mode 100755 index 0000000..bb58ffd --- /dev/null +++ b/tests/tests.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +import sys, os +sys.path.insert(0, os.path.abspath(os.path.join(__file__, "../../"))) + +from gi.repository import Gtk, Gio, GLib +from dfeet.introspection import AddressInfo +from dfeet.introspection_helper import DBusNode, DBusInterface, DBusProperty, DBusSignal, DBusMethod +import unittest +import time + +XML = """ +<node> + <interface name='org.gnome.d-feet.TestInterface'> + <method name='HelloWorld'> + <arg type='s' name='greeting' direction='in'/> + <arg type='s' name='response' direction='out'/> + </method> + <property name="TestString" type="s" access="readwrite"/> + <property name="TestBool" type="b" access="read"/> + <signal name="TestSignal"> + <arg name="SigString" type="s"/> + <arg name="SigBool" type="b"/> + </signal> + </interface> +</node> +""" + +class IntrospectionHelperTest(unittest.TestCase): + """ tests for the introspection helper classes """ + def setUp(self): + self.name = "org.gnome.d-feet" + self.object_path = "/org/gnome/d-feet" + self.node_info = Gio.DBusNodeInfo.new_for_xml(XML) + + def test_dbus_node_info(self): + """ test DBusNode class """ + obj_node = DBusNode(self.name, self.object_path, self.node_info) + self.assertEqual(obj_node.name, self.name) + self.assertEqual(obj_node.object_path, self.object_path) + self.assertEqual(len(obj_node.node_info.interfaces), 1) + + def test_dbus_interface(self): + """ test DBusInterface class """ + obj_node = DBusNode(self.name, self.object_path, self.node_info) + obj_iface = DBusInterface(obj_node, obj_node.node_info.interfaces[0]) + self.assertEqual(obj_iface.name, self.name) + self.assertEqual(obj_iface.object_path, self.object_path) + self.assertEqual(len(obj_iface.iface_info.methods), 1) + self.assertEqual(len(obj_iface.iface_info.properties), 2) + self.assertEqual(len(obj_iface.iface_info.signals), 1) + + def test_dbus_property(self): + """ test DBusProperty class """ + obj_node = DBusNode(self.name, self.object_path, self.node_info) + obj_iface = DBusInterface(obj_node, obj_node.node_info.interfaces[0]) + obj_prop = DBusProperty(obj_iface, obj_iface.iface_info.properties[0]) + self.assertEqual(obj_prop.name, self.name) + self.assertEqual(obj_prop.object_path, self.object_path) + + def test_dbus_signal(self): + """ test DBusSignal class """ + obj_node = DBusNode(self.name, self.object_path, self.node_info) + obj_iface = DBusInterface(obj_node, obj_node.node_info.interfaces[0]) + obj_sig = DBusSignal(obj_iface, obj_iface.iface_info.signals[0]) + self.assertEqual(obj_sig.name, self.name) + self.assertEqual(obj_sig.object_path, self.object_path) + + +class AddressInfoTest(unittest.TestCase): + """ tests for the AddressInfo class and the introspection stuff """ + + def test_system_bus(self): + """ introspect a name on the system bus """ + ai = AddressInfo(Gio.BusType.SYSTEM, "org.freedesktop.DBus") + + def test_session_bus(self): + """ introspect a name on the session bus """ + return + ai = AddressInfo(Gio.BusType.SESSION, "org.freedesktop.DBus") + + def test_other_bus(self): + """ test another bus """ + return + sysbus_addr = os.getenv("DBUS_SYSTEM_BUS_ADDRESS") + ai = AddressInfo(sysbus_addr, "org.freedesktop.DBus") + #TODO: create another bus and test with the other bus + + @unittest.skip("TODO:peer to peer test not implemented") + def test_peer_to_peer(self): + """ test a p2p connection """ + #TODO: setup a gdbus server and test a peer to peer connection + #(see http://developer.gnome.org/gio/unstable/GDBusServer.html#gdbus-peer-to-peer) + pass + +if __name__ == "__main__": + #FIXME: this is copied from the file "f-deet" + ENV_PATHS = {"DFEET_DATA_PATH" : "ui/", + "DFEET_IMAGE_PATH" : "ui/", + "DFEET_LOCALE_PATH" : "locale/", + "DFEET_HELP_PATH" : "/usr/share/gnome/dfeet/" + } + for var, path in ENV_PATHS.iteritems(): + os.environ.setdefault(var, path) + + #run tests + unittest.main() diff --git a/tests/tests.py~ b/tests/tests.py~ new file mode 100755 index 0000000..9a49ded --- /dev/null +++ b/tests/tests.py~ @@ -0,0 +1,79 @@ +#!/usr/bin/env python +import sys, os +sys.path.insert(0, os.path.abspath(os.path.join(__file__, "../../"))) + +from gi.repository import Gtk, Gio, GLib +from dfeet.gdbus_introspector import BusNameInfo +import threading +import unittest +import time + + +class DBusServer: + """ a dbus server for testing. Sever runs inside a thread """ + XML = ''' +<node> + <interface name='de.toabctl.Demo.Hello'> + <method name='hello'> + <arg type='s' name='ping' direction='in'/> + <arg type='s' name='pong' direction='out'/> + </method> + </interface> +</node>''' + def __init__(self, addr): + self.addr = addr + self.server = Gio.DBusServer.new_sync(self.addr, + Gio.DBusServerFlags.AUTHENTICATION_ALLOW_ANONYMOUS, + Gio.dbus_generate_guid(), + None, None) + self.server.start() + self.server.connect("new-connection", self.new_connection_cb, self) + self.loop = GLib.MainLoop() + self.vtable = Gio.DBusInterfaceVTable() + self.vtable.method_call(self.handle_method_call_cb) + self.vtable.get_property(None) + self.vtable.set_property(None) + + def handle_method_call_cb(self, connection, sender, object_path, interface_name, method_name, parameters, invocation, user_data): + print "method call" + if method_name == "hello": + ret = GLib.Variant(("s"), ("Adios")) + involcation.return_value(ret) + + def new_connection_cb(self, server, connection, user_data): + print "new connection" + connection.register_object("/de/toabctl/Demo/", DBusServer.XML, + self.vtable, None, None) + + def start(self): + self.loop.run() + + def quit(self): + self.server.stop() + self.loop.quit() + +class Introspector(unittest.TestCase): + """ tests for gdbus_introspector.py """ + + def test_system_bus(self): + return + bni = BusNameInfo(Gio.BusType.SYSTEM, "org.freedesktop.DBus") + + def test_session_bus(self): + return + bni = BusNameInfo(Gio.BusType.SESSION, "org.freedesktop.DBus") + +if __name__ == '__main__': + #FIXME: this is copied from the file "f-deet" + ENV_PATHS = {"DFEET_DATA_PATH" : "ui/", + "DFEET_IMAGE_PATH" : "ui/", + "DFEET_LOCALE_PATH" : "locale/", + "DFEET_HELP_PATH" : "/usr/share/gnome/dfeet/" + } + for var, path in ENV_PATHS.iteritems(): + os.environ.setdefault(var, path) + + #run tests + #unittest.main() + s = DBusServer("unix:abstract=myaddr") + s.start() diff --git a/tests/uifile_tests.py b/tests/uifile_tests.py deleted file mode 100755 index ca2294e..0000000 --- a/tests/uifile_tests.py +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env python -from gi.repository import Gtk - -main_ui = Gtk.Builder() -main_ui.add_from_file("../ui/default-actiongroup.ui") -main_ui.add_from_file("../ui/mainwindow.ui") - -win = main_ui.get_object("appwindow1") -win.show_all() - -filterbox_ui = Gtk.Builder() -filterbox_ui.add_from_file("../ui/filterbox.ui") - -filterbox_win = Gtk.Window(Gtk.WindowType.TOPLEVEL) -filterbox = filterbox_ui.get_object("filterbox_table1") -filterbox_win.add(filterbox) -filterbox_win.show_all() - -introspectview_ui = Gtk.Builder() -introspectview_ui.add_from_file("../ui/introspectview.ui") - -introspectview_win = Gtk.Window(Gtk.WindowType.TOPLEVEL) -introspectview = introspectview_ui.get_object("introspectview_table1") -introspectview_win.add(introspectview) -introspectview_win.show_all() - -executedialog_ui = Gtk.Builder() -executedialog_ui.add_from_file("../ui/executedialog.ui") - -executedialog_win = executedialog_ui.get_object("executedialog1") -executedialog_win.show_all() - -Gtk.main() diff --git a/ui/addconnectiondialog.ui b/ui/addconnectiondialog.ui index f1096dd..4806dcb 100644 --- a/ui/addconnectiondialog.ui +++ b/ui/addconnectiondialog.ui @@ -1,58 +1,77 @@ -<?xml version="1.0" standalone="no"?> -<!--*- mode: xml -*--> +<?xml version="1.0" encoding="UTF-8"?> <interface> + <!-- interface-requires gtk+ 3.0 --> <object class="GtkDialog" id="add_connection_dialog1"> + <property name="can_focus">False</property> <property name="border_width">5</property> - <property name="default_height">50</property> + <property name="title" translatable="yes" context="yes">Add a Connection</property> <property name="default_width">480</property> + <property name="default_height">50</property> <property name="destroy_with_parent">True</property> - <property name="has_separator">False</property> - <property name="title" context="yes" translatable="yes">Add a Connection</property> <property name="type_hint">normal</property> <child internal-child="vbox"> - <object class="GtkVBox" id="dialog1-vbox"> - <property name="border_width">2</property> + <object class="GtkBox" id="dialog1-vbox"> <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> <child> <object class="GtkHBox" id="hbox1"> <property name="visible">True</property> + <property name="can_focus">False</property> <child> <object class="GtkLabel" id="label1"> - <property name="label" context="yes" translatable="yes"><b>Bus Address: </b></property> - <property name="use_markup">True</property> <property name="visible">True</property> - <property name="xalign">0.0</property> + <property name="can_focus">False</property> + <property name="tooltip_text" translatable="yes">See http://dbus.freedesktop.org/doc/dbus-specification.html#addresses</property> + <property name="xalign">0</property> + <property name="label" translatable="yes" context="yes">Address:</property> </object> <packing> <property name="expand">False</property> - <property name="fill">False</property> + <property name="fill">True</property> + <property name="position">0</property> </packing> </child> <child> - <object class="GtkComboBoxEntry" id="address_comboentry1"> - <property name="items">Session Bus</property> - <property name="visible">True</property> - </object> - <packing> - <property name="position">1</property> - </packing> + <placeholder/> </child> </object> <packing> + <property name="expand">False</property> + <property name="fill">True</property> <property name="padding">5</property> - <property name="position">1</property> + <property name="position">0</property> </packing> </child> <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog1-action_area"> - <property name="border_width">5</property> + <object class="GtkButtonBox" id="dialog1-action_area"> + <property name="visible">True</property> + <property name="can_focus">False</property> <property name="layout_style">end</property> - <property name="spacing">6</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_status"> <property name="visible">True</property> + <property name="can_focus">False</property> </object> <packing> <property name="expand">False</property> + <property name="fill">True</property> <property name="pack_type">end</property> + <property name="position">2</property> </packing> </child> </object> diff --git a/ui/buswatch.ui b/ui/buswatch.ui new file mode 100644 index 0000000..fdba11a --- /dev/null +++ b/ui/buswatch.ui @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <object class="GtkListStore" id="liststore_buswatch"> + <columns> + <!-- column-name bus_name_obj --> + <column type="GObject"/> + <!-- column-name bus_name_pid --> + <column type="guint"/> + <!-- column-name bus_name_unique --> + <column type="gchararray"/> + <!-- column-name bus_name_cmdline --> + <column type="gchararray"/> + </columns> + </object> + <object class="GtkPaned" id="paned_buswatch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <child> + <object class="GtkBox" id="box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Filter:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="entry_filter"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">•</property> + <property name="invisible_char_set">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="treeview_buswatch"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_tooltip">True</property> + <property name="halign">start</property> + <property name="valign">start</property> + <property name="model">liststore_buswatch</property> + <property name="search_column">2</property> + <property name="tooltip_column">2</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection"/> + </child> + <child> + <object class="GtkTreeViewColumn" id="treeviewcolumn_unique_bus_name"> + <property name="title" translatable="yes">unique bus name</property> + <property name="clickable">True</property> + <property name="sort_indicator">True</property> + <property name="sort_column_id">2</property> + <child> + <object class="GtkCellRendererText" id="cellrenderertext_unique_bus_name"/> + <attributes> + <attribute name="text">2</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkGrid" id="grid_bus_name_info"> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Command line:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_bus_name_selected_cmdline"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="wrap">True</property> + <property name="wrap_mode">char</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Process ID:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_bus_name_selected_pid"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0.52999997138977051</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Name:</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_bus_name_selected_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="resize">False</property> + <property name="shrink">True</property> + </packing> + </child> + <child> + <object class="GtkImage" id="image1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="pixbuf">dfeet-icon.png</property> + </object> + <packing> + <property name="resize">True</property> + <property name="shrink">True</property> + </packing> + </child> + </object> +</interface> diff --git a/ui/default-actiongroup.ui b/ui/default-actiongroup.ui deleted file mode 100644 index f35a955..0000000 --- a/ui/default-actiongroup.ui +++ /dev/null @@ -1,99 +0,0 @@ -<?xml version="1.0" standalone="no"?> - -<interface> - <object class="GtkUIManager" id="default-uiman"> - <child> - <object class="GtkActionGroup" id="DefaultActions"> - <child> - <object class="GtkAction" id="file_toplevel"> - <property name="label" translatable="yes">_File</property> - </object> - </child> - <child> - <object class="GtkAction" id="add_bus_address"> - <property name="name">add_bus_address</property> - <property name="label" translatable="yes">Connect to _Other Bus...</property> - <property name="short_label" translatable="yes">Bus...</property> - <property name="tooltip" translatable="yes">Add a bus tab using an address</property> - <property name="stock_id">gtk-connect</property> - <signal handler="add_bus_address" name="activate"/> - </object> - </child> - <child> - <object class="GtkAction" id="reconnect_current_bus"> - <property name="name">reconnect_current_bus</property> - <property name="label" translatable="yes">Reconnect current Bus...</property> - <property name="short_label" translatable="yes">Reconnect</property> - <property name="tooltip" translatable="yes">Reconnect the bus of the current tab address</property> - <property name="sensitive">False</property> - <property name="stock_id">gtk-refresh</property> - <signal handler="reconnect_current_bus" name="activate"/> - </object> - </child> - <child> - <object class="GtkAction" id="add_session_bus"> - <property name="name">add_session_bus</property> - <property name="label" translatable="yes">Connect to S_ession Bus</property> - <property name="short_label" translatable="yes">Session</property> - <property name="tooltip" translatable="yes">Add a session bus tab</property> - <signal handler="add_session_bus" name="activate"/> - </object> - </child> - <child> - <object class="GtkAction" id="add_system_bus"> - <property name="name">add_system_bus</property> - <property name="label" translatable="yes">Connect to S_ystem Bus</property> - <property name="short_label" translatable="yes">System</property> - <property name="tooltip" translatable="yes">Add a system bus tab</property> - <signal handler="add_system_bus" name="activate"/> - </object> - </child> - <child> - <object class="GtkAction" id="execute_method"> - <property name="name">execute_method</property> - <property name="label" translatable="yes">E_xecute Method</property> - <property name="short_label" translatable="yes">Execute</property> - <property name="tooltip" translatable="yes">Execute the selected method</property> - <property name="sensitive">False</property> - <property name="stock_id">gtk-execute</property> - <signal handler="execute_method" name="activate"/> - </object> - </child> - <child> - <object class="GtkAction" id="quit"> - <property name="name">quit</property> - <property name="label" translatable="yes">_Quit</property> - <property name="tooltip" translatable="yes">Quit D-Feet</property> - <property name="stock_id">gtk-quit</property> - <signal handler="quit" name="activate"/> - </object> - </child> - </object> - </child> - <ui> - <menubar name="menubar1"> - <menu action="file_toplevel"> - <menuitem name="ConnSessionMenu" action="add_session_bus" /> - <menuitem name="ConnSystemMenu" action="add_system_bus" /> - <menuitem name="ConnOtherMenu" action="add_bus_address" /> - <menuitem name="QuitMenu" action="quit" /> - </menu> - </menubar> - <toolbar name="toolbar1"> - <toolitem name="ConnOtherTool" action="add_bus_address"> - <!-- FIXME: This doesn't work and needs to be fixed in Gtk - so that the GtkAction can specify using GtkToolMenuButton - instead of just a regular GtkToolButton - <menu action="file_toplevel"> - <menuitem name="ConnSessionMenu" action="add_session_bus" /> - <menuitem name="ConnSystemMenu" action="add_system_bus" /> - </menu> - --> - </toolitem> - <toolitem name="ConnReconnect" action="reconnect_current_bus"/> - <separator/> - <toolitem name="ExecuteMethod" action="execute_method"/> - </toolbar> - </ui> - </object> -</interface> diff --git a/ui/executedialog.ui b/ui/executedialog.ui index 92b8c0c..06c3a89 100644 --- a/ui/executedialog.ui +++ b/ui/executedialog.ui @@ -1,146 +1,446 @@ -<?xml version="1.0"?> - +<?xml version="1.0" encoding="UTF-8"?> <interface> + <!-- interface-requires gtk+ 3.0 --> + <object class="GtkAdjustment" id="adjustment1"> + <property name="lower">1</property> + <property name="upper">100</property> + <property name="value">1</property> + <property name="step_increment">1</property> + <property name="page_increment">10</property> + </object> <object class="GtkDialog" id="executedialog1"> - <property name="allow_shrink">True</property> - <property name="default_height">400</property> + <property name="can_focus">False</property> + <property name="title" translatable="yes" context="yes">Execute D-Bus Method</property> <property name="default_width">320</property> + <property name="default_height">400</property> <property name="destroy_with_parent">True</property> - <property name="title" context="yes" translatable="yes">Execute D-Bus Method</property> - <!--<property name="type_hint">normal</property>--> - <signal handler="execute_dialog_close_cb" name="close"/> + <property name="type_hint">normal</property> + <signal name="close" handler="execute_dialog_close_cb" swapped="no"/> <child internal-child="vbox"> - <object class="GtkVBox" id="dialog1-vbox"> - <property name="border_width">2</property> + <object class="GtkBox" id="dialog1-vbox"> <property name="visible">True</property> + <property name="can_focus">False</property> + <child internal-child="action_area"> + <object class="GtkButtonBox" id="dialog1-action_area"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="layout_style">end</property> + <child> + <object class="GtkButton" id="closebutton1"> + <property name="label" context="yes">gtk-close</property> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="receives_default">False</property> + <property name="use_action_appearance">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="execute_dialog_close_cb" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="executebutton1"> + <property name="label" translatable="yes" context="yes">_Execute</property> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="is_focus">True</property> + <property name="receives_default">False</property> + <property name="use_action_appearance">False</property> + <property name="use_underline">True</property> + <signal name="clicked" handler="execute_dbus_method_cb" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">0</property> + </packing> + </child> <child> <object class="GtkVBox" id="vbox3"> <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label_method_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="ypad">5</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Method name:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label10"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Object Path:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label11"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Interface:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_object_path"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_interface"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> <child> - <object class="GtkLabel" id="commandlabel1"> - <property name="label" context="yes" translatable="yes">Execute i.c on object path o</property> + <object class="GtkLabel" id="label4"> <property name="visible">True</property> - <property name="xalign">0.0</property> - <property name="ypad">5</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_top">10</property> + <property name="label" translatable="yes"><b>Method input</b></property> + <property name="use_markup">True</property> </object> <packing> <property name="expand">False</property> <property name="fill">False</property> + <property name="position">1</property> </packing> </child> <child> - <object class="GtkFrame" id="frame1"> - <property name="label" context="yes" translatable="yes">Parameters (in python syntax)</property> - <property name="shadow">none</property> - <property name="shadow_type">none</property> + <object class="GtkScrolledWindow" id="scrolledwindow1"> <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> <child> - <object class="GtkScrolledWindow" id="scrolledwindow1"> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">in</property> + <object class="GtkTextView" id="parametertextview1"> <property name="visible">True</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkTextView" id="parametertextview1"> - <property name="is_focus">True</property> - <property name="visible">True</property> - <property name="wrap_mode">word</property> - </object> - </child> + <property name="can_focus">True</property> </object> </child> </object> <packing> - <property name="position">1</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label3"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="margin_top">10</property> + <property name="yalign">0.49000000953674316</property> + <property name="xpad">1</property> + <property name="label" translatable="yes"><b>Method output</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> </packing> </child> <child> <object class="GtkFrame" id="frame2"> - <property name="label" context="yes" translatable="yes">Output</property> - <property name="shadow">none</property> - <property name="shadow_type">none</property> <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label_xalign">0</property> + <property name="shadow_type">none</property> <child> <object class="GtkNotebook" id="notebook1"> - <property name="tab_pos">bottom</property> <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tab_pos">bottom</property> <child> <object class="GtkScrolledWindow" id="scrolledwindow2"> - <property name="hscrollbar_policy">never</property> <property name="visible">True</property> - <property name="vscrollbar_policy">automatic</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> <child> <object class="GtkTextView" id="prettyprinttextview1"> - <property name="editable">False</property> <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> <property name="wrap_mode">word</property> </object> </child> </object> </child> + <child type="tab"> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Pretty print</property> + </object> + <packing> + <property name="tab_fill">False</property> + </packing> + </child> <child> <object class="GtkScrolledWindow" id="scrolledwindow3"> - <property name="hscrollbar_policy">never</property> <property name="visible">True</property> - <property name="vscrollbar_policy">automatic</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> <child> <object class="GtkTextView" id="sourcetextview1"> - <property name="editable">False</property> <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">False</property> <property name="wrap_mode">word</property> </object> </child> </object> <packing> <property name="position">1</property> - <property name="tab_label" context="yes" translatable="yes">Page 2</property> + </packing> + </child> + <child type="tab"> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Source</property> + </object> + <packing> + <property name="position">1</property> + <property name="tab_fill">False</property> </packing> </child> </object> </child> </object> <packing> - <property name="position">2</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">4</property> </packing> </child> - </object> - <packing> - <property name="position">2</property> - </packing> - </child> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog1-action_area"> - <property name="border_width">5</property> - <property name="layout_style">end</property> - <property name="spacing">6</property> - <property name="visible">True</property> <child> - <object class="GtkButton" id="closebutton1"> - <property name="label" context="yes" translatable="yes">gtk-close</property> - <property name="use_stock">True</property> + <object class="GtkBox" id="box2"> <property name="visible">True</property> - <signal handler="execute_dialog_close_cb" name="clicked"/> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="yalign">0.49000000953674316</property> + <property name="xpad">1</property> + <property name="label" translatable="yes"><b>Method execution</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkSpinButton" id="method_exec_count_spinbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_tooltip">True</property> + <property name="tooltip_markup" translatable="yes">Number of method executions</property> + <property name="tooltip_text" translatable="yes">Number of method executions</property> + <property name="invisible_char">•</property> + <property name="invisible_char_set">True</property> + <property name="adjustment">adjustment1</property> + <property name="climb_rate">1</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">end</property> + <property name="position">1</property> + </packing> + </child> </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> </child> <child> - <object class="GtkButton" id="executebutton1"> - <property name="is_focus">True</property> - <property name="use_underline">True</property> - <property name="label" context="yes" translatable="yes">_Execute</property> + <object class="GtkBox" id="box_output_stats"> <property name="visible">True</property> - <signal handler="execute_dbus_method_cb" name="clicked"/> + <property name="can_focus">False</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkLabel" id="label5"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Ø:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_avg"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_tooltip">True</property> + <property name="tooltip_markup" translatable="yes">Average method execution time in seconds</property> + <property name="tooltip_text" translatable="yes">Average method execution time in seconds</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Min:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_min"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_tooltip">True</property> + <property name="tooltip_markup" translatable="yes">Minimal method execution time in seconds</property> + <property name="tooltip_text" translatable="yes">Minimal method execution time in seconds</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">Max:</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">4</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_max"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="has_tooltip">True</property> + <property name="tooltip_markup" translatable="yes">Maximal method execution time in seconds</property> + <property name="tooltip_text" translatable="yes">Maximal method execution time in seconds</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">5</property> + </packing> + </child> </object> <packing> - <property name="position">1</property> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">6</property> </packing> </child> - </object> + </object> <packing> - <property name="expand">False</property> - <property name="pack_type">end</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> </packing> </child> </object> </child> + <action-widgets> + <action-widget response="0">closebutton1</action-widget> + <action-widget response="0">executebutton1</action-widget> + </action-widgets> </object> </interface> diff --git a/ui/introspection.ui b/ui/introspection.ui new file mode 100644 index 0000000..4185fcd --- /dev/null +++ b/ui/introspection.ui @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <object class="GtkBox" id="box_introspectview"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkBox" id="box"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkGrid" id="grid1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <child> + <object class="GtkLabel" id="label2"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Unique name:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Name:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_unique_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">2</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_name"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">1</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label4"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes"><b>Address:</b></property> + <property name="use_markup">True</property> + </object> + <packing> + <property name="left_attach">0</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="label_address"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="selectable">True</property> + </object> + <packing> + <property name="left_attach">1</property> + <property name="top_attach">0</property> + <property name="width">1</property> + <property name="height">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkButton" id="button_reload"> + <property name="label">gtk-refresh</property> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="receives_default">True</property> + <property name="use_action_appearance">False</property> + <property name="use_stock">True</property> + <signal name="clicked" handler="on_button_reload_clicked" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkScrolledWindow" id="scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="model">treestore</property> + <property name="search_column">0</property> + <child internal-child="selection"> + <object class="GtkTreeSelection" id="treeview-selection1"/> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + </object> + <object class="GtkTreeStore" id="treestore"> + <columns> + <!-- column-name gchararray1 --> + <column type="gchararray"/> + <!-- column-name GObject1 --> + <column type="GObject"/> + </columns> + </object> +</interface> diff --git a/ui/introspectview.ui b/ui/introspectview.ui deleted file mode 100644 index 0f67c0d..0000000 --- a/ui/introspectview.ui +++ /dev/null @@ -1,118 +0,0 @@ -<?xml version="1.0"?> - -<interface> - <object class="GtkTable" id="introspectview_table1"> - <property name="n_columns">2</property> - <property name="n_rows">4</property> - <property name="visible">True</property> - <child> - <object class="GtkLabel" id="label15"> - <property name="label" context="yes" translatable="yes"><b>Name: </b></property> - <property name="use_markup">True</property> - <property name="visible">True</property> - <property name="xalign">1.0</property> - <property name="yalign">0.0</property> - </object> - <packing> - <property name="x_options">fill</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label16"> - <property name="can_focus">True</property> - <property name="label" context="yes" translatable="yes"><b>Unique Name: </b></property> - <property name="selectable">True</property> - <property name="use_markup">True</property> - <property name="visible">True</property> - <property name="xalign">1.0</property> - <property name="yalign">0.0</property> - </object> - <packing> - <property name="bottom_attach">2</property> - <property name="top_attach">1</property> - <property name="x_options">fill</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label17"> - <property name="label" context="yes" translatable="yes"><b>Command Line: </b></property> - <property name="use_markup">True</property> - <property name="visible">True</property> - <property name="xalign">1.0</property> - <property name="yalign">0.0</property> - </object> - <packing> - <property name="bottom_attach">3</property> - <property name="top_attach">2</property> - <property name="x_options">fill</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="introspect_box1"> - <property name="height_request">5</property> - <property name="visible">True</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="bottom_attach">4</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="name_label1"> - <property name="can_focus">True</property> - <property name="ellipsize">end</property> - <property name="is_focus">True</property> - <property name="selectable">True</property> - <property name="visible">True</property> - <property name="xalign">0.0</property> - <property name="yalign">0.0</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="unique_name_label1"> - <property name="can_focus">True</property> - <property name="ellipsize">end</property> - <property name="selectable">True</property> - <property name="visible">True</property> - <property name="xalign">0.0</property> - </object> - <packing> - <property name="bottom_attach">2</property> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="process_label1"> - <property name="can_focus">True</property> - <property name="ellipsize">end</property> - <property name="is_focus">True</property> - <property name="selectable">True</property> - <property name="visible">True</property> - <property name="wrap">True</property> - <property name="xalign">0.0</property> - </object> - <packing> - <property name="bottom_attach">3</property> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="y_options">fill | shrink</property> - </packing> - </child> - </object> -</interface> diff --git a/ui/mainwindow.ui b/ui/mainwindow.ui index c1093c2..fdef12d 100644 --- a/ui/mainwindow.ui +++ b/ui/mainwindow.ui @@ -1,45 +1,102 @@ -<?xml version="1.0" standalone="no"?> - +<?xml version="1.0" encoding="UTF-8"?> <interface> + <!-- interface-requires gtk+ 3.0 --> + <object class="GtkAction" id="action_close"> + <property name="label" translatable="yes">Close</property> + <property name="stock_id">gtk-close</property> + <signal name="activate" handler="action_close_activate_cb" swapped="no"/> + </object> + <object class="GtkActionGroup" id="actiongroup_connect"> + <child> + <object class="GtkAction" id="action_systembus_connect"> + <property name="label" translatable="yes">Connect to System Bus</property> + <property name="short_label" translatable="yes">Connect to System Bus</property> + <property name="tooltip" translatable="yes">Connect to System Bus</property> + <property name="stock_id">gtk-connect</property> + <signal name="activate" handler="action_systembus_connect_activate_cb" swapped="no"/> + </object> + </child> + <child> + <object class="GtkAction" id="action_sessionbus_connect"> + <property name="label" translatable="yes">Connect to Session Bus</property> + <property name="short_label" translatable="yes">Connect to Session Bus</property> + <property name="tooltip" translatable="yes">Connect to Session Bus</property> + <property name="stock_id">gtk-connect</property> + <signal name="activate" handler="action_sessionbus_connect_activate_cb" swapped="no"/> + </object> + </child> + <child> + <object class="GtkAction" id="action_otherbus_connect"> + <property name="label" translatable="yes">Connect to other Bus</property> + <property name="short_label" translatable="yes">Connect to other Bus</property> + <property name="tooltip" translatable="yes">Connect to other Bus</property> + <property name="stock_id">gtk-connect</property> + <signal name="activate" handler="action_otherbus_connect_activate_cb" swapped="no"/> + </object> + </child> + </object> <object class="GtkWindow" id="appwindow1"> - <property name="default_height">480</property> - <property name="default_width">640</property> - <property name="height_request">200</property> - <property name="title" context="yes" translatable="yes">D-Feet D-Bus debugger</property> - <property name="visible">False</property> <property name="width_request">300</property> + <property name="height_request">200</property> + <property name="can_focus">False</property> + <property name="title" translatable="yes" context="yes">D-Feet D-Bus debugger</property> + <property name="default_width">640</property> + <property name="default_height">480</property> + <property name="icon_name">dfeet-icon</property> <child> - <object class="GtkVBox" id="main_vbox"> + <object class="GtkVBox" id="vbox1"> <property name="visible">True</property> + <property name="can_focus">False</property> <child> - <object class="GtkFrame" id="frame1"> - <property name="visible">True</property> + <object class="GtkMenuBar" id="menubar1"> + <property name="can_focus">False</property> <child> - <object class="GtkVBox" id="vbox1"> + <object class="GtkMenuItem" id="menuitem_file"> + <property name="use_action_appearance">False</property> <property name="visible">True</property> - <child> - <object class="GtkMenuBar" id="menubar1" constructor="default-uiman" /> - <packing> - <property name="expand">False</property> - </packing> - </child> - <child> - <object constructor="default-uiman" class="GtkToolbar" id="toolbar1"> - <property name="toolbar_style">both-horiz</property> - <property name="visible">True</property> - </object> - <packing> - <property name="expand">False</property> - </packing> - </child> - <child> - <object class="GtkNotebook" id="display_notebook"> - <property name="enable_popup">True</property> - <property name="is_focus">True</property> - <property name="scrollable">True</property> + <property name="can_focus">False</property> + <property name="label" translatable="yes">File</property> + <property name="use_underline">True</property> + <child type="submenu"> + <object class="GtkMenu" id="menu1"> <property name="visible">True</property> + <property name="can_focus">False</property> <child> - <placeholder/> + <object class="GtkMenuItem" id="menuitem_systembus"> + <property name="related_action">action_systembus_connect</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="menuitem_sessionbus"> + <property name="related_action">action_sessionbus_connect</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkMenuItem" id="menuitem_otherbus"> + <property name="related_action">action_otherbus_connect</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkSeparatorMenuItem" id="menuitem1"> + <property name="use_action_appearance">False</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + </object> + </child> + <child> + <object class="GtkImageMenuItem" id="menuitem_close"> + <property name="related_action">action_close</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + </object> </child> </object> </child> @@ -47,35 +104,77 @@ </child> </object> <packing> - <property name="expand">True</property> - </packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> </child> <child> - <object class="GtkFrame" id="frame2"> + <object class="GtkToolbar" id="toolbar1"> <property name="visible">True</property> + <property name="can_focus">False</property> <child> - <object class="GtkNotebook" id="console_notebook"> - <property name="enable_popup">True</property> - <property name="is_focus">True</property> - <property name="scrollable">True</property> + <object class="GtkToolButton" id="toolbutton_systembus_connect"> + <property name="related_action">action_systembus_connect</property> <property name="visible">True</property> - <child> - <placeholder/> - </child> + <property name="can_focus">False</property> + <property name="related_action">action_systembus_connect</property> + <property name="label" translatable="yes">toolbutton1</property> + <property name="use_underline">True</property> </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="toolbutton_sessionbus_connect"> + <property name="related_action">action_sessionbus_connect</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="related_action">action_sessionbus_connect</property> + <property name="label" translatable="yes">toolbutton1</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> + </child> + <child> + <object class="GtkToolButton" id="toolbutton_otherbus_connect"> + <property name="related_action">action_otherbus_connect</property> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="related_action">action_otherbus_connect</property> + <property name="label" translatable="yes">toolbutton1</property> + <property name="use_underline">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="homogeneous">True</property> + </packing> </child> </object> <packing> <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> </packing> </child> <child> - <object class="GtkStatusbar" id="statusbar1"> + <object class="GtkNotebook" id="display_notebook"> <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="scrollable">True</property> + <child> + <placeholder/> + </child> </object> <packing> - <property name="expand">False</property> - <property name="pack_type">end</property> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">2</property> </packing> </child> </object> |