camelot.camelot.view.filters

Package camelot :: Package camelot :: Package view :: Module filters |
[hide private]
[frames] | no frames]

# Source Code for Module camelot.camelot.view.filters

      1  #  ============================================================================

      2  #

      3  #  Copyright (C) 2007-2008 Conceptive Engineering bvba. All rights reserved.

      4  #  www.conceptive.be / project-camelot@conceptive.be

      5  #

      6  #  This file is part of the Camelot Library.

      7  #

      8  #  This file may be used under the terms of the GNU General Public

      9  #  License version 2.0 as published by the Free Software Foundation

     10  #  and appearing in the file LICENSE.GPL included in the packaging of

     11  #  this file.  Please review the following information to ensure GNU

     12  #  General Public Licensing requirements will be met:

     13  #  http://www.trolltech.com/products/qt/opensource.html

     14  #

     15  #  If you are unsure which license is appropriate for your use, please

     16  #  review the following information:

     17  #  http://www.trolltech.com/products/qt/licensing.html or contact

     18  #  project-camelot@conceptive.be.

     19  #

     20  #  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE

     21  #  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

     22  #

     23  #  For use of this library in commercial applications, please contact

     24  #  project-camelot@conceptive.be

     25  #

     26  #  ============================================================================

     27

     28  """

     29  Python structures to represent filters.

     30  These structures can be transformed to QT forms.

     31  """
     32

     33 -def structure_to_filter(structure):
     34    """Convert a python data structure to a filter, using the following rules :

     35

     36    if structure is an instance of Filter, return structure

     37    else create a GroupBoxFilter from the structure

     38    """
     39    if isinstance(structure, Filter):
     40      return structure
     41    return GroupBoxFilter(structure)
     42

     43 -class Filter(object):
     44    """Base class for filters"""
     45

     46 -  def __init__(self, attribute, value_to_string=lambda x:unicode(x)):
     47      """

     48      @param attribute: the attribute on which to filter, this attribute

     49      may contain dots to indicate relationships that need to be followed,

     50      eg.  'person.groups.name'

     51      @param value_to_string: function that converts a value of the attribute to

     52      a string that will be displayed in the filter

     53      """
     54      self.attribute = attribute
     55      self._value_to_string = value_to_string
     56

     57 -  def render(self, parent, name, options):
     58      """Render this filter as a qt object

     59      @param parent: its parent widget

     60      @param name: the name of the filter

     61      @param options: the options that can be selected, where each option is a list

     62      of tuples containting (option_name, query_decorator)

     63

     64      The name and the list of options can be fetched with get_name_and_options"""
     65      raise NotImplementedException()
     66

     67 -  def get_name_and_options(self, admin):
     68      """return a tuple of the name of the filter and a list of options that can be selected.

     69      Each option is a tuple of the name of the option, and a filter function to

     70      decorate a query

     71      @return:  (filter_name, [(option_name, query_decorator), ...)

     72      """
     73      from sqlalchemy.sql import select
     74      from elixir import session
     75      #session.bind = self.entity.table.metadata.bind

     76      filter_names = []
     77      joins = []
     78      table = admin.entity.table
     79      for field_name in self.attribute.split('.'):
     80        attributes = admin.getFieldAttributes(field_name)
     81        filter_names.append(attributes['name'])
     82        if attributes['widget'] in ('one2many', 'many2many', 'many2one'):
     83          admin = attributes['admin']
     84          joins.append(field_name)
     85          if attributes['widget'] in ('many2one'):
     86            table = admin.entity.table.join(table)
     87          else:
     88            table = admin.entity.table
     89

     90

     91      col = getattr(admin.entity, field_name)
     92

     93      query = select([col], distinct=True, order_by=col.asc()).select_from(table)
     94

     95      def create_decorator(col, value, joins):
     96

     97        def decorator(q):
     98          if joins:
     99            q = q.join(joins, aliased=True)
    100          return q.filter(col==value)
    101

    102        return decorator
    103

    104      options = [(self._value_to_string(value[0]), create_decorator(col, value[0], joins))

    105                 for value in session.execute(query)]
    106      return (filter_names[0],[('All', lambda q: q)] + options)
    107

    108 -class GroupBoxFilter(Filter):
    109    """Filter where the items are displayed in a QGroupBox"""
    110

    111 -  def render(self, parent, name, options):
    112

    113      from PyQt4 import QtCore, QtGui
    114

    115      class FilterWidget(QtGui.QGroupBox):
    116        """A box containing a filter that can be applied on a table view, this filter is

    117        based on the distinct values in a certain column"""
    118

    119        def __init__(self, name, choices, parent):
    120          QtGui.QGroupBox.__init__(self, name, parent)
    121          self.group = QtGui.QButtonGroup()
    122          self.item = name
    123          self.unique_values = []
    124          self.choices = None
    125          self.setChoices(choices)
    126

    127        def emit_filter_changed(self, state):
    128          self.emit(QtCore.SIGNAL('filter_changed'))
    129

    130        def setChoices(self, choices):
    131          self.choices = choices
    132          layout = QtGui.QVBoxLayout()
    133          for i,name in enumerate([unicode(c[0]) for c in choices]):
    134            button = QtGui.QRadioButton(name)
    135            layout.addWidget(button)
    136            self.group.addButton(button, i)
    137            if i==0:
    138              button.setChecked(True)
    139            self.connect(button, QtCore.SIGNAL('toggled(bool)'), self.emit_filter_changed)
    140          layout.addStretch()
    141          self.setLayout(layout)
    142

    143        def decorate_query(self, query):
    144          checked = self.group.checkedId()
    145          if checked>=0:
    146            return self.choices[checked][1](query)
    147          return query
    148

    149      return FilterWidget(name, options, parent)
    150

    151 -class ComboBoxFilter(Filter):
    152    """Filter where the items are displayed in a QComboBox"""
    153

    154 -  def render(self, parent, name, options):
    155

    156      from PyQt4 import QtCore, QtGui
    157

    158      class FilterWidget(QtGui.QGroupBox):
    159

    160        def __init__(self, name, choices, parent):
    161          super(FilterWidget, self).__init__(name, parent)
    162          layout = QtGui.QVBoxLayout()
    163          self.choices = choices
    164          combobox = QtGui.QComboBox(self)
    165          for i,(name,decorator) in enumerate(choices):
    166            combobox.insertItem(i, unicode(name), QtCore.QVariant(decorator))
    167          layout.addWidget(combobox)
    168          self.setLayout(layout)
    169          self.current_index = 0
    170          self.connect(combobox, QtCore.SIGNAL('currentIndexChanged(int)'), self.emit_filter_changed)
    171

    172        def emit_filter_changed(self, index):
    173          self.current_index = index
    174          self.emit(QtCore.SIGNAL('filter_changed'))
    175

    176        def decorate_query(self, query):
    177          if self.current_index>=0:
    178            return self.choices[self.current_index][1](query)
    179          return query
    180

    181      return FilterWidget(name, options, parent)
    182