Newer
Older
"""
:Author: Franziska Koehn
:Created: 2015/01/13
This module contains all classes of widgets that are used for creating a query.
Franziska Koehn
committed
"""Creates all query-view-widgets.
**Parameters**
:\*args: same as gtk.VBox
:\*\*kwargs: same as gtk.VBox
"""
super(QueryView, self).__init__(*args, **kwargs)
# root-type
hBox_root_type = gtk.HBox()
self.pack_start(hBox_root_type, False, True, 0)
label_root_type = gtk.Label()
label_root_type.set_text("Root-Type: ")
hBox_root_type.pack_start(label_root_type, False, True, 0)
import xsa.datatypereader as type_reader
selected = combobox.get_active_text()
self.treeView_search.set_root_type(selected)
self.combobox = gtk.combo_box_new_text()
import xsa.datatypereader as type_reader
self.combobox.append_text(t)
self.combobox.connect('changed', changed_cb)
hBox_root_type.pack_start(self.combobox, True, True,0)
# Sash
vpaned_chart = gtk.VPaned()
vpaned_chart.set_position(250)
self.pack_start(vpaned_chart, True, True, 0)
# query-tree
sw_search = gtk.ScrolledWindow()
sw_search.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw_search.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
vpaned_chart.add1(sw_search)
self.treeView_search = TreeViewQuery()
sw_search.add(self.treeView_search)
#result fields
sw_fields = gtk.ScrolledWindow()
sw_fields.set_shadow_type(gtk.SHADOW_ETCHED_IN)
sw_fields.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
vpaned_chart.add2(sw_fields)
self.treeView_fields = TreeViewResultFields()
sw_fields.add(self.treeView_fields)
self.treeView_search.set_root_type(self.combobox.get_active_text())
"""Returns the query as a list (calls get_query() of TreeViewQuery)"""
return self.treeView_search.get_query()
return self.combobox.get_active_text()
"""Returns selected search-fields as list of strings (field-labels)"""
return self.treeView_fields.get_selected_fields()
def set_data(self, root_field = None, query = None, field_labels = None):
"""Sets the data of the respective widgets to the given root_field, query and field_labels
**Parameters**
:root_field: string, containing Root-Type
:query: list of constraints and methods
:field_labels: list of field-labels (strings)
"""
return list(e[0] for e in self.combobox.get_model()).index(root_field)
self.combobox.set_active(get_index())
self.treeView_search.set_query(root_field, query)
self.treeView_search.expand_all()
self.treeView_fields.set_fields(root_field, field_labels)
class TreeViewResultFields(gtk.TreeView):
"""TreeView, which allows the user to pick fields of a Root-Type which he or
she wants included in the query-result."""
def __init__(self):
"""Constructor, creating columns, the store and sets it as the model"""
super(TreeViewResultFields, self).__init__()
self.create_columns()
self.store = gtk.ListStore(bool, str, bool) # is_selected, label, is_required
self.set_model(self.store)
def create_columns(self):
"""Creates the columns and their renderers."""
def callback_toggled(cellrenderertoggle, path_string, col, *_):
it = self.store.get_iter_from_string(path_string)
is_active = cellrenderertoggle.get_active()
if not self.store.get_value(it, 2):
self.store.set(it, col, not is_active)
renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True)
renderer.connect("toggled", callback_toggled, 0)
column = gtk.TreeViewColumn("", renderer)
column.add_attribute(renderer, "active", 0)
column.set_sort_column_id(0)
column.set_resizable(False)
self.append_column(column)
column = gtk.TreeViewColumn("Requested Result-Fields", gtk.CellRendererText(), text=1)
column.set_sort_column_id(1)
column.set_resizable(True)
self.append_column(column)
def reset_fields(self, root_type):
"""Clears the store and adds the fields for the given Root-Type to it.
**Parameters**
:root-type: Root-Type of search
"""
import xsa.datatypereader as type_reader
self.store.clear()
for f in fields:
self.store.append( [ f[1], # is_selected
f[0], # label
f[1] # is_required
])
def set_fields(self, root_type, fields):
Resets Store for given Root-Type.
Selects all fields, that are marked as necessary (in their datatype-files).
:root_type: Root-Type of search
:fields: fields of chosen Root-Type
for row in self.store:
for field in fields:
if row[1] == field:
row[0] = True
break
Returns a list of strings, containing the labels of the selected fields.
"""Start-string for new inserted row."""
"""Tooltip that will be shown for each row and each column"""
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
_methods = None # example: ["AND", "OR"]
_fields = None # example: ["TYPE", "TE", "TI"]
_operators = None # example: ["LIKE", ">", "<", "<=", ">=", "="]
@property
def methods(self):
"""methods that are allowed for creating a query."""
if self._methods is None:
import xsa.queries as queries
self._methods = queries.get_query_methods()
return self._methods
@property
def operators(self):
"""operators that are allowed for creating a query."""
if self._operators is None:
import xsa.queries as queries
self._operators = queries.get_operators()
return self._operators
@property
def fields(self):
"""Fields that are allowed for creating a query."""
import xsa.datatypereader as type_reader
self._fields = type_reader.get_labels(self.root_type)
return self._fields
"""Calls all functions for creating this TreeView.
Defines the button-press-event for deleting rows."""
self.set_rules_hint(True)
self.create_columns()
self.set_headers_visible(True)
self.store = gtk.TreeStore(str, str, str, bool, str) # field/method , operator, value, is_second_and_third_column_writeable, tooltip
self.reset_model()
self.set_model(self.store)
def on_treeview_button_press_event(treeview, event):
if event.button == 3:
x = int(event.x)
y = int(event.y)
path_info = treeview.get_path_at_pos(x, y)
if path_info is not None:
path, col, cellx, celly = path_info
iter = self.store.get_iter(path)
if not path == (0,):
md = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
buttons=gtk.BUTTONS_YES_NO,
message_format="Remove the element and its children?"
)
response = md.run()
md.destroy()
if response == gtk.RESPONSE_YES:
self.store.remove(iter)
self.connect('button_press_event', on_treeview_button_press_event)
self.set_tooltip_column(4)
def create_columns(self):
def root_changed_callback(combo, path_string, new_iter, col, *_):
# used magic-numbers of store-columns in this method:
# 0 (str) = first column, houses methods and types
# 1 (str) = second column, if first column is set on a type: writeable, houses all operators
# if first column is set on a method: not writeable
# 2 (str) = third column, if first column is set on a type: writeable for values
# if first column is set on a method: not writeable
# 3 (bool) = is column 2 and 3 writeable
it = self.store.get_iter_from_string(path_string)
parent = self.store.iter_parent(it)
new_value = combo.get_property("model")[new_iter][0]
old_value = self.store.get_value(it, col)
if new_value == self.inital_value or new_value == old_value: # value has not changed
return
elif old_value == self.inital_value:
# a cell with no actual value (just initial_value) was changed
self.store.set(it, 0, new_value)
if parent is not None:
# re-add the initial_value-field at the end of this sub-query
self.store.append(parent, (self.inital_value, '', '', False, self.tooltip))
else: # existing value was changed
if new_value in self.fields and old_value in self.methods:
# subquery needs to be deleted, ask for confirmation
md = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
buttons=gtk.BUTTONS_YES_NO,
message_format="The subquery will be deleted! Continue?"
if response == gtk.RESPONSE_NO:
return
else:
while not self.store.iter_children(it) is None:
child = self.store.iter_children(it)
self.store.remove(child)
if new_value in self.fields:
self.store.set(it, 3, True) # make operator- and value-cell editable
# add initial_value to the end of the subquery if it isn't there already
child_count = self.store.iter_n_children(it)
if child_count == 0:
self.store.append(it, (self.inital_value, '', '', False, self.tooltip))
else:
last_child = self.store.iter_nth_child(it, child_count-1)
child_value = self.store.get_value(last_child, 0)
if child_value != self.inital_value:
self.store.append(it, (self.inital_value, '', '', False, self.tooltip))
def cell_changed_callback(combo, path_string, new_iter, col, *_):
it = self.store.get_iter_from_string(path_string)
text = combo.get_property("model")[new_iter][0]
self.store.set(it, col, str(text))
def cell_edited_callback(cellrenderertext, path_string, new_text, col, *_):
it = self.store.get_iter_from_string(path_string)
self.store.set(it, col, new_text)
rendererCombo = gtk.CellRendererCombo()
rendererCombo.set_property('editable', True)
rendererCombo.set_property ("model", self.create_combo_list_fields());
rendererCombo.connect('changed', root_changed_callback, 0)
column = gtk.TreeViewColumn("Query", rendererCombo, text=0)
column.set_sort_column_id(0)
column.set_min_width(100)
column.set_resizable(True)
self.append_column(column)
rendererCombo = gtk.CellRendererCombo()
rendererCombo.set_property('editable', True)
rendererCombo.set_property ("model", self.create_combo_list_operator())
rendererCombo.set_property ("text-column", 0);
rendererCombo.connect('changed', cell_changed_callback, 1)
column = gtk.TreeViewColumn("Operator", rendererCombo, text=1, editable=3)
column.set_sort_column_id(1)
column.set_min_width(70)
self.append_column(column)
rendererText = gtk.CellRendererText()
rendererText.set_property('editable', True)
rendererText.connect('edited', cell_edited_callback, 2)
column = gtk.TreeViewColumn("Value", rendererText, text=2, editable=3)
column.set_sort_column_id(2)
column.set_min_width(100)
column.set_resizable(True)
self.append_column(column)
Returns a new gtk.ListStore including the given labels.
**Parameters**
:labels: list of labels
"""
combo_model = gtk.ListStore(str)
for l in labels:
combo_model.append((str(l),))
return combo_model
def set_root_type(self, root_type):
Sets the Root-Type-Field, creates the list of fields of this new Root-Type and prepares this TreeView for using them.
**Parameters**
:root_type: chosen Root-Type
"""
self.root_type = root_type
self.reset_model()
self.fields
self.get_column(0).get_cell_renderers()[0].set_property ("model", self.create_combo_list_fields());
"""Creates and returns a new ListStore including the Types."""
return self.create_combo_list(self.methods+self.fields)
"""Creates and returns a new ListStore including the Operators."""
self.store.clear()
self.store.append(None, (self.inital_value, '', '', False, self.tooltip))
def set_query(self, root_type, query):
Creates a ListStore for the given query and sets it as the view's model. Sets Root-Type.
:query: a query, defined as a list (like pyxnat uses it)
"""
import xsa.datatypereader as type_reader
def add_next_to_store(values, parent):
if type(values) is tuple or (len(values) == 3 and all(type(v) is unicode for v in values)):
type_name = type_reader.get_field_label_by_field(self.root_type, values[0])
self.store.append(parent,
(type_name,values[1],values[2],True,self.tooltip))
elif type(values) is list:
for v in values:
if v in self.methods:
parent = self.store.append(parent,
(v, '', '', False, self.tooltip))
else:
add_next_to_store(v, parent)
raise xsa.errors.CorruptedQueryError("Error while reading Query")
self.store.clear()
add_next_to_store(query[0], None)
def get_query(self):
"""Creates a List containing the definition of a query, like pyxnat defines it, and returns it."""
import xsa.datatypereader as type_reader
for field in search_fields:
if field['label'] == value:
value = field['field']
operator = self.store.get_value(iter, 1)
field_value = self.store.get_value(iter, 2)
child = self.store.iter_children(iter)
if not child is None:
add_next_to_query(child, constraint)
next_ = self.store.iter_next(iter)
if not next_ is None:
query = [] # passed as reference to be filled by recursive calls