[tracker] Added gtk-sparql python program
- From: Ivan Frade <ifrade src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [tracker] Added gtk-sparql python program
- Date: Thu, 30 Jul 2009 18:05:19 +0000 (UTC)
commit 31eb994bea4c211fdc3b9dba71c651e4de8c8015
Author: Ivan Frade <ivan frade nokia com>
Date: Thu Jul 30 21:02:57 2009 +0300
Added gtk-sparql python program
Very first version of a gtk ui to run sparql queries in tracker.
Basically a UI version of tracker-sparql that allows to save queries.
python/gtk-sparql/Completion.py | 285 +++++++++++++++++++++++++++++++
python/gtk-sparql/gtk-sparql.py | 213 +++++++++++++++++++++++
python/gtk-sparql/gtk-sparql.ui | 201 ++++++++++++++++++++++
python/gtk-sparql/store.py | 68 ++++++++
python/gtk-sparql/tracker_completion.py | 123 +++++++++++++
5 files changed, 890 insertions(+), 0 deletions(-)
---
diff --git a/python/gtk-sparql/Completion.py b/python/gtk-sparql/Completion.py
new file mode 100644
index 0000000..973d104
--- /dev/null
+++ b/python/gtk-sparql/Completion.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+
+#
+# Code taken from http://code.google.com/p/emesene-plugins/
+# Specifically http://emesene-plugins.googlecode.com/svn/trunk/Completion.py
+#
+# The original file doesn't contain License header, but
+# the project is under license GPL v2
+
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, emesene-plugins project
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+VERSION = '0.1'
+import os
+import re
+
+import gobject
+import gtk
+
+class CompletionEngine:
+ def __init__(self):
+ self.complete_functions = []
+
+ def add_complete_function(self, function):
+ self.complete_functions.append(function)
+
+ def remove_complete_function(self, function):
+ self.complete_functions.remove(function)
+
+ def complete_word(self, buffer):
+ complete = []
+ for func in self.complete_functions:
+ new = func(buffer)
+ new.reverse()
+ complete.extend(new)
+ return complete
+
+class CompletionWindow(gtk.Window):
+ """Window for displaying a list of completions."""
+
+ def __init__(self, parent, callback):
+ gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
+ self.set_decorated(False)
+ self.store = None
+ self.view = None
+ self.completions = None
+ self.complete_callback = callback
+ self.set_transient_for(parent)
+ self.set_border_width(1)
+ self.text = gtk.TextView()
+ self.text.set_wrap_mode (gtk.WRAP_WORD)
+ self.text_buffer = gtk.TextBuffer()
+ self.text.set_buffer(self.text_buffer)
+ #self.text.set_size_request(130, 100)
+ self.text.set_sensitive(False)
+ self.init_tree_view()
+ self.init_frame()
+ self.cb_ids = {}
+ self.cb_ids['focus-out'] = self.connect('focus-out-event', self.focus_out_event)
+ self.cb_ids['key-press'] = self.connect('key-press-event', self.key_press_event)
+ self.grab_focus()
+ self.custom_widget = None
+
+
+ def clear(self):
+ self.text_buffer.set_text('')
+ if self.custom_widget:
+ self.custom_widget.hide()
+ self.hbox.remove(self.custom_widget)
+ self.custom_widget = None
+
+ def key_press_event(self, widget, event):
+ if event.keyval == gtk.keysyms.Escape:
+ self.clear()
+ self.hide()
+ return True
+ if event.keyval == gtk.keysyms.BackSpace:
+ self.clear()
+ self.hide()
+ return True
+ if event.keyval in (gtk.keysyms.Return, gtk.keysyms.Right):
+ self.complete()
+ return True
+ if event.keyval == gtk.keysyms.Up:
+ self.select_previous()
+ return True
+ if event.keyval == gtk.keysyms.Down:
+ self.select_next()
+ return True
+
+ char = gtk.gdk.keyval_to_unicode(event.keyval)
+ if char:
+ self.complete_callback(chr(char))
+ return False
+
+ def complete(self):
+ self.complete_callback(self.completions[self.get_selected()]['completion'])
+
+ def focus_out_event(self, *args):
+ self.hide()
+
+ def get_selected(self):
+ """Get the selected row."""
+
+ selection = self.view.get_selection()
+ return selection.get_selected_rows()[1][0][0]
+
+ def init_frame(self):
+ """Initialize the frame and scroller around the tree view."""
+
+ scroller = gtk.ScrolledWindow()
+ scroller.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ scroller.add(self.view)
+ frame = gtk.Frame()
+ frame.set_shadow_type(gtk.SHADOW_OUT)
+ self.hbox = hbox = gtk.HBox()
+ hbox.pack_start(scroller, True, True)
+
+ scroller_text = gtk.ScrolledWindow()
+ scroller_text.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
+ scroller_text.add(self.text)
+ hbox.pack_start (scroller_text, True, True)
+ frame.add(hbox)
+ self.add(frame)
+
+ #self.set_geometry_hints(scroller, min_width=200, min_height=200)
+ #self.set_geometry_hints(scroller_text, min_width=200, min_height=200)
+ self.set_geometry_hints (scroller_text, min_width=2500)
+ self.set_geometry_hints (None, min_width=400, min_height=200)
+
+ #self.set_geometry_hints(scroller, max_height=450)
+ #self.set_geometry_hints(frame, max_height=2500)
+ #self.set_geometry_hints(None, max_height=2500)
+
+
+ def init_tree_view(self):
+ """Initialize the tree view listing the completions."""
+
+ self.store = gtk.ListStore(gobject.TYPE_STRING)
+ self.view = gtk.TreeView(self.store)
+ renderer = gtk.CellRendererText()
+ column = gtk.TreeViewColumn("", renderer, text=0)
+ self.view.append_column(column)
+ self.view.set_enable_search(False)
+ self.view.set_headers_visible(False)
+ self.view.set_rules_hint(True)
+ selection = self.view.get_selection()
+ selection.set_mode(gtk.SELECTION_SINGLE)
+ #self.view.set_size_request(100, 60)
+ self.view.connect('row-activated', self.row_activated)
+
+
+ def row_activated(self, tree, path, view_column, data = None):
+ self.complete()
+
+
+ def select_next(self):
+ """Select the next completion."""
+
+ self.clear()
+ row = min(self.get_selected() + 1, len(self.store) - 1)
+ selection = self.view.get_selection()
+ selection.unselect_all()
+ selection.select_path(row)
+ self.view.scroll_to_cell(row)
+ self.show_completions()
+
+ def select_previous(self):
+ """Select the previous completion."""
+
+ self.clear()
+ row = max(self.get_selected() - 1, 0)
+ selection = self.view.get_selection()
+ selection.unselect_all()
+ selection.select_path(row)
+ self.view.scroll_to_cell(row)
+ self.show_completions()
+
+ def set_completions(self, completions):
+ """Set the completions to display."""
+
+ self.completions = completions
+ #self.completions.reverse()
+ #self.resize(1, 1)
+ self.store.clear()
+ for completion in completions:
+ self.store.append([unicode(completion['abbr'])])
+ self.view.columns_autosize()
+ self.view.get_selection().select_path(0)
+ #self.text_buffer.set_text(self.completions[self.get_selected()]['info'])
+ self.show_completions()
+
+ def show_completions(self):
+ self.clear()
+ completion = self.completions[self.get_selected()]
+ info = completion['info']
+ if not info:
+ self.text.hide()
+ else:
+ self.text_buffer.set_text(info)
+ self.text.show()
+
+ def set_font_description(self, font_desc):
+ """Set the label's font description."""
+
+ self.view.modify_font(font_desc)
+
+class Completer:
+ '''This class provides completion feature for ONE conversation'''
+ def __init__(self, engine, view, window):
+ self.engine = engine
+ self.view = view
+ self.window = window
+ self.popup = CompletionWindow(None, self.complete)
+
+ self.key_press_id = view.connect("key-press-event", self.on_key_pressed)
+
+ def disconnect(self):
+ self.view.disconnect(self.key_press_id)
+
+ def complete(self, completion):
+ """Complete the current word."""
+
+ doc = self.view.get_buffer()
+ doc.insert_at_cursor(completion)
+ self.hide_popup()
+
+ def cancel(self):
+ self.hide_popup()
+ return False
+
+ def hide_popup(self):
+ """Hide the completion window."""
+
+ self.popup.hide()
+ self.completes = None
+
+ def show_popup(self, completions, x, y):
+ """Show the completion window."""
+
+ root_x, root_y = self.window.get_position()
+ print 'root', root_x, root_y
+ self.popup.move(root_x + x + 24, root_y + y + 44)
+ self.popup.set_completions(completions)
+ self.popup.show_all()
+
+ def display_completions(self, view, event):
+ doc = view.get_buffer()
+ insert = doc.get_iter_at_mark(doc.get_insert())
+
+ window = gtk.TEXT_WINDOW_TEXT
+ rect = view.get_iter_location(insert)
+ x, y = view.buffer_to_window_coords(window, rect.x, rect.y)
+ x, y = view.translate_coordinates(self.window, x, y)
+ completes = self.engine.complete_word(doc)
+ if completes:
+ self.show_popup(completes, x, y)
+ return True
+ else:
+ return False
+
+ def on_key_pressed(self, view, event):
+ if event.keyval == gtk.keysyms.Tab:
+ #if event.state & gtk.gdk.CONTROL_MASK and event.state & gtk.gdk.MOD1_MASK and event.keyval == gtk.keysyms.Enter:
+ return self.display_completions(view, event)
+ if event.state & gtk.gdk.CONTROL_MASK:
+ return self.cancel()
+ if event.state & gtk.gdk.MOD1_MASK:
+ return self.cancel()
+ return self.cancel()
diff --git a/python/gtk-sparql/gtk-sparql.py b/python/gtk-sparql/gtk-sparql.py
new file mode 100755
index 0000000..c8ba6a9
--- /dev/null
+++ b/python/gtk-sparql/gtk-sparql.py
@@ -0,0 +1,213 @@
+#!/usr/bin/env python2.5
+
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+
+import gtk
+import dbus
+import store
+import time
+import Completion
+import tracker_completion
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+class GtkSparql:
+
+ def __init__ (self, ui):
+ self.ui = ui
+ self.store = store.QueriesStore ()
+ self.saved_queries_model = self.load_saved_queries ()
+
+ combo = ui.get_object ("queries_combo")
+ combo.set_model (self.saved_queries_model)
+ render = gtk.CellRendererText ()
+ combo.pack_start (render, True)
+ combo.add_attribute (render, "text", 0)
+ combo.connect ("changed", self.select_store_query_cb)
+
+ self.textbuffer = gtk.TextBuffer ()
+ self.textbuffer.set_text ("SELECT \nWHERE { \n\n}")
+ ui.get_object ("query_text").set_buffer (self.textbuffer)
+
+ self.completer = Completion.Completer (tracker_completion.TrackerCompletionEngine (),
+ ui.get_object ("query_text"),
+ ui.get_object ("main_window"))
+
+
+ ui.get_object ("save_button").connect ("clicked", self.save_query_cb)
+ ui.get_object ("run_button").connect ("clicked", self.execute_query_cb)
+ ui.get_object ("delete_button").connect ("clicked", self.delete_store_query_cb)
+
+ # Clean treeview columns
+ treeview = ui.get_object ("results_tv")
+ columns = treeview.get_columns ()
+ for c in columns:
+ treeview.remove_column (c)
+
+ self.connect_tracker ()
+
+ def run (self):
+ w = builder.get_object ("main_window")
+ w.connect ("destroy", gtk.main_quit)
+ w.show_all ()
+ gtk.main ()
+
+ def connect_tracker (self):
+ # TODO Check if the connection is valid
+ bus = dbus.SessionBus ()
+ tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+ self.resources = dbus.Interface (tracker,
+ dbus_interface="org.freedesktop.Tracker.Resources");
+
+
+ def execute_query_cb (self, widget, call=0):
+ label = self.ui.get_object ("info_label")
+
+ query = self.textbuffer.get_text (self.textbuffer.get_start_iter (),
+ self.textbuffer.get_end_iter ())
+ try:
+ start = time.time ()
+ result = self.resources.SparqlQuery (query)
+ end = time.time ()
+
+ label.set_text ("Query took %f sec." % (end-start))
+
+ self.set_results_in_treeview (result)
+
+ except dbus.exceptions.DBusException, e:
+ if ("org.freedesktop.DBus.Error.ServiceUnknown" in str(e) and call == 0):
+ self.connect_tracker ()
+ self.execute_query_cb (widget, 1)
+ else:
+ label.set_text (str(e).partition(':')[2])
+
+
+ def set_results_in_treeview (self, result_set):
+ if (len (result_set) < 1):
+ return None
+
+ columns = len (result_set[0])
+ params = tuple (columns * [str])
+ store = gtk.ListStore (*params)
+ self.configure_tree_view (columns)
+
+ counter = 0
+ for r in result_set:
+ if (counter < 500):
+ counter += 1
+ store.append (r)
+ else:
+ label = self.ui.get_object ("info_label")
+ current_text = label.get_text ()
+ label.set_text (current_text +
+ "(More than 500 results. Showing only first 500)")
+ break
+
+ self.ui.get_object ("results_tv").set_model (store)
+
+ def configure_tree_view (self, columns):
+ """
+ Add of remove columns in the current treeview to have exactly 'columns'
+ """
+ treeview = self.ui.get_object ("results_tv")
+ current_columns = treeview.get_columns ()
+ if (len (current_columns) == columns):
+ return
+
+ if (len (current_columns) > columns):
+ for i in range (columns, len (current_columns)):
+ treeview.remove_column (current_columns [i])
+
+
+ # Add columns. Is the only chance at this moment
+ for i in range (len (current_columns), columns):
+ render = gtk.CellRendererText()
+ column = gtk.TreeViewColumn ("%d" % (i),
+ render,
+ text=i) # primera columna de datos
+ treeview.append_column (column)
+
+
+ def save_query_cb (self, widget):
+ dialog = gtk.Dialog ("Save query...",
+ None,
+ gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+ (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,
+ gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+ vbox = dialog.get_content_area ()
+
+ query = self.textbuffer.get_text (self.textbuffer.get_start_iter (),
+ self.textbuffer.get_end_iter ())
+ dialog_query_label = gtk.Label (query)
+ vbox.pack_start (dialog_query_label)
+ dialog_name_entry = gtk.Entry ()
+ vbox.pack_start (dialog_name_entry)
+ vbox.show_all ()
+
+ # Disable 'ok' button unless there is a name
+ dialog.set_response_sensitive (gtk.RESPONSE_ACCEPT, False)
+ def validate_entry (entry, dialog):
+ dialog.set_response_sensitive (gtk.RESPONSE_ACCEPT, len (entry.get_text ()) > 0)
+ dialog_name_entry.connect ("changed", validate_entry, dialog)
+
+ response = dialog.run ()
+ if (response == gtk.RESPONSE_ACCEPT):
+ name = dialog_name_entry.get_text ()
+ self.save_query (name, query)
+ dialog.destroy ()
+
+
+ def select_store_query_cb (self, combobox):
+ it = combobox.get_active_iter ()
+ selected = combobox.get_model ().get (it, 1)[0]
+ self.textbuffer.set_text (selected)
+
+
+ def delete_store_query_cb (self, button):
+ combo = self.ui.get_object ("queries_combo")
+ it = combo.get_active_iter ()
+ name = self.saved_queries_model.get (it, 0)[0]
+ self.store.delete_query (name)
+ self.saved_queries_model.remove (it)
+ combo.set_active (0)
+
+
+ def load_saved_queries (self):
+ queries_model = gtk.ListStore (str, str)
+ queries = self.store.get_all_queries ()
+ for row in queries:
+ queries_model.append (row)
+ return queries_model
+
+
+ def save_query (self, name, value):
+ self.store.save_query (name, value)
+ it = self.saved_queries_model.append ((name, value))
+ self.ui.get_object ("queries_combo").set_active_iter (it)
+
+
+if __name__ == "__main__":
+
+ builder = gtk.Builder ()
+ builder.add_from_file ("gtk-sparql.ui")
+
+ app = GtkSparql (builder)
+ app.run ()
diff --git a/python/gtk-sparql/gtk-sparql.ui b/python/gtk-sparql/gtk-sparql.ui
new file mode 100644
index 0000000..6f94518
--- /dev/null
+++ b/python/gtk-sparql/gtk-sparql.ui
@@ -0,0 +1,201 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.16"/>
+ <!-- interface-naming-policy project-wide -->
+ <object class="GtkListStore" id="liststore1"/>
+ <object class="GtkListStore" id="liststore2"/>
+ <object class="GtkWindow" id="main_window">
+ <property name="border_width">6</property>
+ <property name="icon_name">applications-development</property>
+ <child>
+ <object class="GtkVBox" id="vbox1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkHBox" id="hbox1">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes"><b>Saved queries</b></property>
+ <property name="use_markup">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="queries_combo">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="delete_button">
+ <property name="label" translatable="yes">gtk-delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVPaned" id="vpaned1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkVBox" id="vbox2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkTextView" id="query_text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="hbox2">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkButton" id="save_button">
+ <property name="label" translatable="yes">gtk-save</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVSeparator" id="vseparator1">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="previous_button">
+ <property name="label" translatable="yes">gtk-go-back</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="next_button">
+ <property name="label" translatable="yes">gtk-go-forward</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkVSeparator" id="vseparator2">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ </object>
+ <packing>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="run_button">
+ <property name="label" translatable="yes">gtk-execute</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="info_label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">False</property>
+ <property name="shrink">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">automatic</property>
+ <property name="vscrollbar_policy">automatic</property>
+ <child>
+ <object class="GtkTreeView" id="results_tv">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="resize">True</property>
+ <property name="shrink">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>
diff --git a/python/gtk-sparql/store.py b/python/gtk-sparql/store.py
new file mode 100644
index 0000000..538f4c6
--- /dev/null
+++ b/python/gtk-sparql/store.py
@@ -0,0 +1,68 @@
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+import sqlite3
+import os
+
+DEFAULT_EXAMPLE = "SELECT ?u \nWHERE { ?u a nie:InformationElement. }\n\n"
+EMPTY_QUERY = "SELECT \nWHERE { \n\n}"
+
+class QueriesStore ():
+
+ def __init__ (self):
+ create = False
+ self.db_dir, self.db_path = self.get_db_dir_file ()
+
+ if (not os.path.exists (self.db_dir)):
+ os.makedirs (self.db_dir)
+ create = True
+
+ if (create or not os.path.exists (self.db_path)):
+ conn = sqlite3.connect (self.db_path)
+ c = conn.cursor ()
+ c.execute ("CREATE TABLE saved_queries ( Name Text not null, Query Text );")
+ c.execute ("INSERT INTO saved_queries VALUES ('', '%s')" % (EMPTY_QUERY))
+ c.execute ("INSERT INTO saved_queries VALUES ('Example', '%s')" % (DEFAULT_EXAMPLE))
+ conn.commit ()
+ c.close ()
+
+ def get_db_dir_file (self):
+ return (os.path.join (os.getenv ("HOME"), ".local", "share", "tracker-query"),
+ os.path.join (os.getenv ("HOME"), ".local", "share", "tracker-query", "queries.db"))
+
+ def save_query (self, name, value):
+ conn = sqlite3.connect (self.db_path)
+ c = conn.cursor ()
+ c.execute ("INSERT INTO saved_queries VALUES ('%s', '%s');" % (name, value))
+ conn.commit ()
+ c.close ()
+
+ def delete_query (self, name):
+ conn = sqlite3.connect (self.db_path)
+ c = conn.cursor ()
+ c.execute ("DELETE FROM saved_queries WHERE Name='%s';" % (name))
+ conn.commit ()
+ c.close ()
+
+
+ def get_all_queries (self):
+ conn = sqlite3.connect (self.db_path)
+ c = conn.cursor ()
+ c.execute ("SELECT * FROM saved_queries;")
+ results = c.fetchall ()
+ c.close ()
+ return results
diff --git a/python/gtk-sparql/tracker_completion.py b/python/gtk-sparql/tracker_completion.py
new file mode 100644
index 0000000..8725055
--- /dev/null
+++ b/python/gtk-sparql/tracker_completion.py
@@ -0,0 +1,123 @@
+# GtkSparql - Gtk UI to try SparQL queries against tracker.
+# Copyright (C) 2009, Ivan Frade <ivan frade gmail com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+
+import dbus
+import re
+
+TRACKER = 'org.freedesktop.Tracker'
+TRACKER_OBJ = '/org/freedesktop/Tracker/Resources'
+
+ALL_NAMESPACES = """
+SELECT ?u ?prefix WHERE { ?u a tracker:Namespace; tracker:prefix ?prefix. }
+"""
+
+ALL_PROPERTIES = """
+SELECT ?u ?comment
+WHERE { ?u a rdf:Property. OPTIONAL { ?u rdfs:comment ?comment.} }
+"""
+
+ALL_CLASSES = """
+SELECT ?u ?comment WHERE { ?u a rdfs:Class. OPTIONAL { ?u rdfs:comment ?comment.} }
+"""
+
+class TrackerCompletionEngine:
+
+ def __init__ (self):
+ self.connect_tracker ()
+
+ self.namespaces = {}
+ for row in self.resources.SparqlQuery (ALL_NAMESPACES):
+ self.namespaces [row[0]] = row[1]
+
+ self.properties = {}
+ for (uri, comment) in self.resources.SparqlQuery (ALL_PROPERTIES):
+ self.properties [str(self.qname_to_short (uri))] = str(comment)
+
+ self.classes = {}
+ for (uri, comment) in self.resources.SparqlQuery (ALL_CLASSES):
+ self.classes [str(self.qname_to_short (uri))] = str(comment)
+
+ def qname_to_short (self, uri):
+ ns, classname = self.split_ns_uri (uri)
+ prefix = self.namespaces [ns]
+ return prefix + ":" + classname
+
+ def split_ns_uri (self, uri):
+ pieces = uri.split ("#")
+ if ( len (pieces) > 1):
+ classname = pieces [1]
+ namespace = pieces [0] + "#"
+ else:
+ classname = uri[uri.rindex ('/')+1:]
+ namespace = uri[:uri.rindex ('/')+1]
+ return namespace, classname
+
+
+ def connect_tracker (self):
+ # TODO Check if the connection is valid
+ bus = dbus.SessionBus ()
+ tracker = bus.get_object (TRACKER, TRACKER_OBJ)
+ self.resources = dbus.Interface (tracker,
+ dbus_interface="org.freedesktop.Tracker.Resources");
+
+ def complete_word (self, textbuffer):
+ """
+ Return a list of dictionaries with the following keys:
+ completion: text to set in the buffer if selected the option
+ abbr: text visible for the user in the list of options
+ info: test description showed near to the option
+ """
+ completions = []
+ uncomplete_word = self.get_last_word (textbuffer)
+ for k in self.classes.iterkeys ():
+ if (k.startswith (uncomplete_word)):
+ c = {'completion':k[len (uncomplete_word):],
+ 'abbr':k,
+ 'info':self.classes [k]}
+ completions.insert (0, c)
+
+ for k in self.properties.iterkeys ():
+ if (k.startswith (uncomplete_word)):
+ c = {'completion':k[len (uncomplete_word):],
+ 'abbr':k,
+ 'info':self.properties [k]}
+ completions.insert (0, c)
+
+ return completions
+
+
+ def get_last_word (self, textbuffer):
+ insert = textbuffer.get_iter_at_mark(textbuffer.get_insert())
+ lineno = insert.get_line ()
+ linestart = textbuffer.get_iter_at_line (lineno)
+ regex = re.compile (r"\S+$", re.MULTILINE)
+ line = textbuffer.get_slice (linestart, insert, False)
+ match = re.search (regex, line)
+ if (not match):
+ return []
+ uncomplete_word = match.group (0)
+ return uncomplete_word
+
+if __name__ == "__main__":
+
+ import gtk
+ engine = TrackerCompletionEngine ()
+ buf = gtk.TextBuffer ()
+ buf.set_text ("SELECT ?u WHERE { ?u a nie:InformationElement;\n nie:t")
+ print "Last word: ", engine.get_last_word (buf)
+ print engine.complete_word (buf)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]