Package Camelot :: Package camelot :: Package view :: Package controls :: Module tableview |
[frames] | no frames]

# Source Code for Module Camelot.camelot.view.controls.tableview

      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  """Tableview"""
     29

     30  import logging
     31  logger = logging.getLogger( 'camelot.view.controls.tableview' )
     32

     33  from PyQt4 import QtCore, QtGui
     34  from PyQt4.QtGui import QSizePolicy
     35  from PyQt4.QtCore import SIGNAL
     36  from PyQt4.QtCore import Qt
     37

     38  from camelot.view.proxy.queryproxy import QueryTableProxy
     39  from camelot.view.controls.filterlist import filter_changed_signal
     40  from camelot.view.controls.view import AbstractView
     41  from camelot.view.controls.user_translatable_label import UserTranslatableLabel
     42  from camelot.view.model_thread import model_function, gui_function, post
     43  from camelot.core.utils import ugettext as _
     44

     45  from search import SimpleSearchControl
     46

     47 -class TableWidget( QtGui.QTableView):
     48      """A widget displaying a table, to be used within a TableView"""
     49

     50 -    def __init__( self, parent = None ):
     51          QtGui.QTableView.__init__( self, parent )
     52          logger.debug( 'create TableWidget' )
     53          self.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows )
     54          self.setEditTriggers( QtGui.QAbstractItemView.SelectedClicked | QtGui.QAbstractItemView.DoubleClicked )
     55          self.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Expanding )
     56          # set to false while sorting is not implemented in CollectionProxy

     57          self.horizontalHeader().setClickable( True )
     58          self._header_font_required = QtGui.QApplication.font()
     59          self._header_font_required.setBold( True )
     60          self._minimal_row_height = QtGui.QFontMetrics(QtGui.QApplication.font()).lineSpacing() + 10
     61          self.verticalHeader().setDefaultSectionSize( self._minimal_row_height )
     62          self.connect( self.horizontalHeader(), QtCore.SIGNAL('sectionClicked(int)'), self.horizontal_section_clicked )
     63

     64 -    def horizontal_section_clicked( self, logical_index ):
     65          """Update the sorting of the model and the header"""
     66          header = self.horizontalHeader()
     67          order = Qt.AscendingOrder
     68          if not header.isSortIndicatorShown():
     69              header.setSortIndicatorShown( True )
     70          elif header.sortIndicatorSection()==logical_index:
     71              # apparently, the sort order on the header is allready switched when the section

     72              # was clicked, so there is no need to reverse it

     73              order = header.sortIndicatorOrder()
     74          header.setSortIndicator( logical_index, order )
     75          self.model().sort( logical_index, order )
     76

     77 -    def setModel( self, model ):
     78          QtGui.QTableView.setModel( self, model )
     79          self.connect( self.selectionModel(), SIGNAL( 'currentChanged(const QModelIndex&,const QModelIndex&)' ), self.activated )
     80

     81 -    def activated( self, selectedIndex, previousSelectedIndex ):
     82          option = QtGui.QStyleOptionViewItem()
     83          newSize = self.itemDelegate( selectedIndex ).sizeHint( option, selectedIndex )
     84          row = selectedIndex.row()
     85          if previousSelectedIndex.row() >= 0:
     86              oldSize = self.itemDelegate( previousSelectedIndex ).sizeHint( option, selectedIndex )
     87              previousRow = previousSelectedIndex.row()
     88              self.setRowHeight( previousRow, oldSize.height() )
     89          self.setRowHeight( row, newSize.height() )
     90

     91 -class RowsWidget( QtGui.QLabel ):
     92      """Widget that is part of the header widget, displaying the number of rows

     93      in the table view"""
     94

     95      _number_of_rows_font = QtGui.QApplication.font()
     96

     97 -    def __init__( self, parent ):
     98          QtGui.QLabel.__init__( self, parent )
     99          self.setFont( self._number_of_rows_font )
    100

    101 -    def setNumberOfRows( self, rows ):
    102          self.setText( _('(%i rows)')%rows )
    103

    104 -class HeaderWidget( QtGui.QWidget ):
    105      """HeaderWidget for a tableview, containing the title, the search widget,

    106      and the number of rows in the table"""
    107

    108      search_widget = SimpleSearchControl
    109      rows_widget = RowsWidget
    110

    111      _title_font = QtGui.QApplication.font()
    112      _title_font.setBold( True )
    113

    114 -    def __init__( self, parent, admin ):
    115          QtGui.QWidget.__init__( self, parent )
    116          self._admin = admin
    117          layout = QtGui.QVBoxLayout()
    118          widget_layout = QtGui.QHBoxLayout()
    119          search = self.search_widget( self )
    120          self.connect(search, SimpleSearchControl.expand_search_options_signal, self.expand_search_options)
    121          title = UserTranslatableLabel( admin.get_verbose_name_plural(), self )
    122          title.setFont( self._title_font )
    123          widget_layout.addWidget( title )
    124          widget_layout.addWidget( search )
    125          if self.rows_widget:
    126              self.number_of_rows = self.rows_widget( self )
    127              widget_layout.addWidget( self.number_of_rows )
    128          else:
    129              self.number_of_rows = None
    130          layout.addLayout( widget_layout )
    131          self._expanded_filters_created = False
    132          self._expanded_search = QtGui.QWidget()
    133          self._expanded_search.hide()
    134          layout.addWidget(self._expanded_search)
    135          self.setLayout( layout )
    136          self.setSizePolicy( QSizePolicy.Minimum, QSizePolicy.Fixed )
    137          self.setNumberOfRows( 0 )
    138          self.search = search
    139

    140 -    def _fill_expanded_search_options(self, columns):
    141          from camelot.view.controls.filter_operator import FilterOperator
    142          layout = QtGui.QHBoxLayout()
    143          for field, attributes in columns:
    144              if 'operators' in attributes and attributes['operators']:
    145                  widget = FilterOperator( self._admin.entity, field, attributes, self)
    146                  self.connect( widget, filter_changed_signal,  self._filter_changed )
    147                  layout.addWidget( widget )
    148          layout.addStretch()
    149          self._expanded_search.setLayout( layout )
    150          self._expanded_filters_created = True
    151

    152 -    def _filter_changed(self):
    153          self.emit(QtCore.SIGNAL('filters_changed'))
    154

    155 -    def decorate_query(self, query):
    156          """Apply expanded filters on the query"""
    157          if self._expanded_filters_created:
    158              for i in range(self._expanded_search.layout().count()):
    159                  if self._expanded_search.layout().itemAt(i).widget():
    160                      query = self._expanded_search.layout().itemAt(i).widget().decorate_query(query)
    161          return query
    162

    163 -    def expand_search_options(self):
    164          if self._expanded_search.isHidden():
    165              if not self._expanded_filters_created:
    166                  post( self._admin.get_columns, self._fill_expanded_search_options )
    167              self._expanded_search.show()
    168          else:
    169              self._expanded_search.hide()
    170

    171      @gui_function
    172 -    def setNumberOfRows( self, rows ):
    173          if self.number_of_rows:
    174              self.number_of_rows.setNumberOfRows( rows )
    175

    176 -class TableView( AbstractView  ):
    177      """A generic tableview widget that puts together some other widgets.  The behaviour of this class and

    178    the resulting interface can be tuned by specifying specific class attributes which define the underlying

    179    widgets used ::

    180

    181      class MovieRentalTableView(TableView):

    182        title_format = 'Grand overview of recent movie rentals'

    183

    184    The attributes that can be specified are :

    185

    186    .. attribute:: header_widget

    187

    188    The widget class to be used as a header in the table view::

    189

    190      header_widget = HeaderWidget

    191

    192    .. attribute:: table_widget

    193

    194    The widget class used to display a table within the table view ::

    195

    196    table_widget = TableWidget

    197

    198    .. attribute:: title_format

    199

    200    A string used to format the title of the view ::

    201

    202    title_format = '%(verbose_name_plural)s'

    203

    204    .. attribute:: table_model

    205

    206    A class implementing QAbstractTableModel that will be used as a model for the table view ::

    207

    208      table_model = QueryTableProxy

    209

    210    - emits the row_selected signal when a row has been selected

    211    """
    212

    213      header_widget = HeaderWidget
    214      table_widget = TableWidget
    215

    216      #

    217      # The proxy class to use

    218      #

    219      table_model = QueryTableProxy
    220      #

    221      # Format to use as the window title

    222      #

    223      title_format = '%(verbose_name_plural)s'
    224

    225 -    def __init__( self, admin, search_text = None, parent = None ):
    226          AbstractView.__init__( self, parent )
    227          self.admin = admin
    228          post( self.get_title, self.change_title )
    229          widget_layout = QtGui.QVBoxLayout()
    230          if self.header_widget:
    231              self.header = self.header_widget( self, admin )
    232              widget_layout.addWidget( self.header )
    233              self.connect( self.header.search, SIGNAL( 'search' ), self.startSearch )
    234              self.connect( self.header.search, SIGNAL( 'cancel' ), self.cancelSearch )
    235              if search_text:
    236                  self.header.search.search( search_text )
    237          else:
    238              self.header = None
    239          widget_layout.setSpacing( 0 )
    240          widget_layout.setMargin( 0 )
    241          self.splitter = QtGui.QSplitter( self )
    242          widget_layout.addWidget( self.splitter )
    243          table_widget = QtGui.QWidget( self )
    244          filters_widget = QtGui.QWidget( self )
    245          self.table_layout = QtGui.QVBoxLayout()
    246          self.table_layout.setSpacing( 0 )
    247          self.table_layout.setMargin( 0 )
    248          self.table = None
    249          self.filters_layout = QtGui.QVBoxLayout()
    250          self.filters_layout.setSpacing( 0 )
    251          self.filters_layout.setMargin( 0 )
    252          self.filters = None
    253          self.actions = None
    254          self._table_model = None
    255          table_widget.setLayout( self.table_layout )
    256          filters_widget.setLayout( self.filters_layout )
    257          #filters_widget.hide()

    258          self.set_admin( admin )
    259          self.splitter.addWidget( table_widget )
    260          self.splitter.addWidget( filters_widget )
    261          self.setLayout( widget_layout )
    262          self.closeAfterValidation = QtCore.SIGNAL( 'closeAfterValidation()' )
    263          self.search_filter = lambda q: q
    264          shortcut = QtGui.QShortcut(QtGui.QKeySequence(QtGui.QKeySequence.Find), self)
    265          self.connect( shortcut, QtCore.SIGNAL( 'activated()' ), self.activate_search )
    266          if self.header_widget:
    267              self.connect( self.header, QtCore.SIGNAL('filters_changed'),  self.rebuildQuery )
    268          # give the table widget focus to prevent the header and its search control to

    269          # receive default focus, as this would prevent the displaying of 'Search...' in the

    270          # search control, but this conflicts with the MDI, resulting in the window not

    271          # being active and the menus not to work properly

    272          #table_widget.setFocus( QtCore.Qt.OtherFocusReason )

    273          #self.setFocusProxy(table_widget)

    274          #self.setFocus( QtCore.Qt.OtherFocusReason )

    275          post( self.admin.get_subclass_tree, self.setSubclassTree )
    276

    277 -    def activate_search(self):
    278          self.header.search.setFocus(QtCore.Qt.ShortcutFocusReason)
    279

    280      @model_function
    281 -    def get_title( self ):
    282          return self.title_format % {'verbose_name_plural':self.admin.get_verbose_name_plural()}
    283

    284      @gui_function
    285 -    def setSubclassTree( self, subclasses ):
    286          if len( subclasses ) > 0:
    287              from inheritance import SubclassTree
    288              class_tree = SubclassTree( self.admin, self.splitter )
    289              self.splitter.insertWidget( 0, class_tree )
    290              self.connect( class_tree, SIGNAL( 'subclassClicked' ), self.set_admin )
    291

    292 -    def sectionClicked( self, section ):
    293          """emits a row_selected signal"""
    294          self.emit( SIGNAL( 'row_selected' ), section )
    295

    296 -    def copy_selected_rows( self ):
    297          """Copy the selected rows in this tableview"""
    298          logger.debug( 'delete selected rows called' )
    299          if self.table and self._table_model:
    300              for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
    301                  self._table_model.copy_row( row )
    302

    303 -    def select_all_rows( self ):
    304          self.table.selectAll()
    305

    306 -    def create_table_model( self, admin ):
    307          """Create a table model for the given admin interface"""
    308          return self.table_model( admin,

    309                                   admin.get_query,

    310                                   admin.get_columns )
    311

    312 -    def get_admin(self):
    313          return self.admin
    314

    315 -    def get_model(self):
    316          return self._table_model
    317

    318      @gui_function
    319 -    def set_admin( self, admin ):
    320          """Switch to a different subclass, where admin is the admin object of the

    321          subclass"""
    322          logger.debug('set_admin called')
    323          self.admin = admin
    324          if self.table:
    325              self.disconnect(self._table_model, QtCore.SIGNAL( 'layoutChanged()' ), self.tableLayoutChanged )
    326              self.table_layout.removeWidget(self.table)
    327              self.table.deleteLater()
    328              self._table_model.deleteLater()
    329          self.table = self.table_widget( self.splitter )
    330          self._table_model = self.create_table_model( admin )
    331          self.table.setModel( self._table_model )
    332          self.connect( self.table.verticalHeader(),

    333                        SIGNAL( 'sectionClicked(int)' ),

    334                        self.sectionClicked )
    335          self.connect( self._table_model, QtCore.SIGNAL( 'layoutChanged()' ), self.tableLayoutChanged )
    336          self.tableLayoutChanged()
    337          self.table_layout.insertWidget( 1, self.table )
    338

    339          def get_filters_and_actions():
    340              return ( admin.get_filters(), admin.get_list_actions() )
    341

    342          post( get_filters_and_actions,  self.set_filters_and_actions )
    343          post( admin.get_list_charts, self.setCharts )
    344

    345      @gui_function
    346 -    def tableLayoutChanged( self ):
    347          logger.debug('tableLayoutChanged')
    348          if self.header:
    349              self.header.setNumberOfRows( self._table_model.rowCount() )
    350          item_delegate = self._table_model.getItemDelegate()
    351          if item_delegate:
    352              self.table.setItemDelegate( item_delegate )
    353          for i in range( self._table_model.columnCount() ):
    354              self.table.setColumnWidth( i, self._table_model.headerData( i, Qt.Horizontal, Qt.SizeHintRole ).toSize().width() )
    355

    356      @gui_function
    357 -    def setCharts( self, charts ):
    358          """creates and display charts"""
    359          pass
    360  #    if charts:

    361  #

    362  #      from matplotlib.figure import Figure

    363  #      from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as \

    364  #                                                     FigureCanvas

    365  #

    366  #      chart = charts[0]

    367  #

    368  #      def getData():

    369  #        """fetches data for chart"""

    370  #        from sqlalchemy.sql import select, func

    371  #        from elixir import session

    372  #        xcol = getattr( self.admin.entity, chart['x'] )

    373  #        ycol = getattr( self.admin.entity, chart['y'] )

    374  #        session.bind = self.admin.entity.table.metadata.bind

    375  #        result = session.execute( select( [xcol, func.sum( ycol )] ).group_by( xcol ) )

    376  #        summary = result.fetchall()

    377  #        return [s[0] for s in summary], [s[1] for s in summary]

    378  #

    379  #      class MyMplCanvas( FigureCanvas ):

    380  #        """Ultimately, this is a QWidget (as well as a FigureCanvasAgg)"""

    381  #

    382  #        def __init__( self, parent = None, width = 5, height = 4, dpi = 100 ):

    383  #          fig = Figure( figsize = ( width, height ), dpi = dpi, facecolor = 'w' )

    384  #          self.axes = fig.add_subplot( 111, axisbg = 'w' )

    385  #          # We want the axes cleared every time plot() is called

    386  #          self.axes.hold( False )

    387  #          self.compute_initial_figure()

    388  #          FigureCanvas.__init__( self, fig )

    389  #          self.setParent( parent )

    390  #          FigureCanvas.setSizePolicy( self,

    391  #                                     QSizePolicy.Expanding,

    392  #                                     QSizePolicy.Expanding )

    393  #          FigureCanvas.updateGeometry( self )

    394  #

    395  #

    396  #        def compute_initial_figure( self ):

    397  #          pass

    398  #

    399  #      def setData( data ):

    400  #        """set chart data"""

    401  #

    402  #        class MyStaticMplCanvas( MyMplCanvas ):

    403  #          """simple canvas with a sine plot"""

    404  #

    405  #          def compute_initial_figure( self ):

    406  #            """computes initial figure"""

    407  #            x, y = data

    408  #            bar_positions = [i - 0.25 for i in range( 1, len( x ) + 1 )]

    409  #            width = 0.5

    410  #            self.axes.bar( bar_positions, y, width, color = 'b' )

    411  #            self.axes.set_xlabel( 'Year' )

    412  #            self.axes.set_ylabel( 'Sales' )

    413  #            self.axes.set_xticks( range( len( x ) + 1 ) )

    414  #            self.axes.set_xticklabels( [''] + [str( d ) for d in x] )

    415  #

    416  #        sc = MyStaticMplCanvas( self, width = 5, height = 4, dpi = 100 )

    417  #        self.table_layout.addWidget( sc )

    418  #

    419  #      self.admin.mt.post( getData, setData )

    420

    421 -    def deleteSelectedRows( self ):
    422          """delete the selected rows in this tableview"""
    423          logger.debug( 'delete selected rows called' )
    424          confirmation_message = self.admin.get_confirm_delete()
    425          confirmed = True
    426          if confirmation_message:
    427              if QtGui.QMessageBox.question(self,

    428                                            _('Please confirm'),

    429                                            unicode(confirmation_message),

    430                                            QtGui.QMessageBox.Yes,

    431                                            QtGui.QMessageBox.No) == QtGui.QMessageBox.No:
    432                  confirmed = False
    433          if confirmed:
    434              for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
    435                  self._table_model.removeRow( row )
    436

    437      @gui_function
    438 -    def newRow( self ):
    439          """Create a new row in the tableview"""
    440          from camelot.view.workspace import get_workspace
    441          workspace = get_workspace()
    442          form = self.admin.create_new_view( workspace,

    443                                             oncreate = lambda o:self._table_model.insertEntityInstance( 0, o ),

    444                                             onexpunge = lambda o:self._table_model.removeEntityInstance( o ) )
    445          workspace.addSubWindow( form )
    446          form.show()
    447

    448 -    def closeEvent( self, event ):
    449          """reimplements close event"""
    450          logger.debug( 'tableview closed' )
    451          # remove all references we hold, to enable proper garbage collection

    452          del self.table_layout
    453          del self.table
    454          del self.filters
    455          del self._table_model
    456          event.accept()
    457

    458 -    def selectTableRow( self, row ):
    459          """selects the specified row"""
    460          self.table.selectRow( row )
    461

    462 -    def makeImport(self):
    463          pass
    464  #        for row in data:

    465  #            o = self.admin.entity()

    466  #            #For example, setattr(x, 'foobar', 123) is equivalent to x.foobar = 123

    467  #            # if you want to import all attributes, you must link them to other objects

    468  #            #for example: a movie has a director, this isn't a primitive like a string

    469  #            # but a object fetched from the db

    470  #            setattr(o, object_attributes[0], row[0])

    471  #            name = row[2].split( ' ' ) #director

    472  #            o.short_description = "korte beschrijving"

    473  #            o.genre = ""

    474  #            from sqlalchemy.orm.session import Session

    475  #            Session.object_session(o).flush([o])

    476  #

    477  #    post( makeImport )

    478

    479 -    def selectedTableIndexes( self ):
    480          """returns a list of selected rows indexes"""
    481          return self.table.selectedIndexes()
    482

    483 -    def getColumns( self ):
    484          """return the columns to be displayed in the table view"""
    485          return self.admin.get_columns()
    486

    487 -    def getData( self ):
    488          """generator for data queried by table model"""
    489          for d in self._table_model.getData():
    490              yield d
    491

    492 -    def getTitle( self ):
    493          """return the name of the entity managed by the admin attribute"""
    494          return self.admin.get_verbose_name()
    495

    496 -    def viewFirst( self ):
    497          """selects first row"""
    498          self.selectTableRow( 0 )
    499

    500 -    def viewLast( self ):
    501          """selects last row"""
    502          self.selectTableRow( self._table_model.rowCount() - 1 )
    503

    504 -    def viewNext( self ):
    505          """selects next row"""
    506          first = self.selectedTableIndexes()[0]
    507          next = ( first.row() + 1 ) % self._table_model.rowCount()
    508          self.selectTableRow( next )
    509

    510 -    def viewPrevious( self ):
    511          """selects previous row"""
    512          first = self.selectedTableIndexes()[0]
    513          prev = ( first.row() - 1 ) % self._table_model.rowCount()
    514          self.selectTableRow( prev )
    515

    516 -    def _set_query(self, query_getter):
    517          self._table_model.setQuery(query_getter)
    518          self.table.clearSelection()
    519

    520 -    def rebuildQuery( self ):
    521          """resets the table model query"""
    522

    523          def rebuild_query():
    524              query = self.admin.entity.query
    525              query = self.header.decorate_query(query)
    526              if self.filters:
    527                  query = self.filters.decorate_query( query )
    528              if self.search_filter:
    529                  query = self.search_filter( query )
    530              query_getter = lambda:query
    531              return query_getter
    532

    533          post( rebuild_query, self._set_query )
    534

    535 -    def startSearch( self, text ):
    536          """rebuilds query based on filtering text"""
    537          from camelot.view.search import create_entity_search_query_decorator
    538          logger.debug( 'search %s' % text )
    539          self.search_filter = create_entity_search_query_decorator( self.admin, text )
    540          self.rebuildQuery()
    541

    542 -    def cancelSearch( self ):
    543          """resets search filtering to default"""
    544          logger.debug( 'cancel search' )
    545          self.search_filter = lambda q: q
    546          self.rebuildQuery()
    547

    548 -    def get_selection_getter(self):
    549          """:return: a function that returns all the objects corresponging to the selected rows in the

    550          table """
    551

    552          def selection_getter():
    553              selection = []
    554              for row in set( map( lambda x: x.row(), self.table.selectedIndexes() ) ):
    555                  selection.append( self._table_model._get_object(row) )
    556              return selection
    557

    558          return selection_getter
    559

    560      @gui_function
    561 -    def set_filters_and_actions( self, filters_and_actions ):
    562          """sets filters for the tableview"""
    563          filters, actions = filters_and_actions
    564          from filterlist import FilterList
    565          from actionsbox import ActionsBox
    566          logger.debug( 'setting filters for tableview' )
    567

    568          if self.filters:
    569              self.disconnect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
    570              self.filters_layout.removeWidget(self.filters)
    571              self.filters.deleteLater()
    572              self.filters = None
    573          if self.actions:
    574              self.filters_layout.removeWidget(self.actions)
    575              self.actions.deleteLater()
    576              self.actions = None
    577          if filters:
    578              self.filters = FilterList( filters, parent=self.splitter )
    579              self.filters_layout.addWidget( self.filters )
    580              self.connect( self.filters, SIGNAL( 'filters_changed' ), self.rebuildQuery )
    581              #

    582              # filters might have default values, so we need to rebuild the queries

    583              #

    584              self.rebuildQuery()
    585          if actions:
    586              selection_getter = self.get_selection_getter()
    587              self.actions = ActionsBox( self,

    588                                         self._table_model.get_collection_getter(),

    589                                         selection_getter )
    590

    591              self.actions.setActions( actions )
    592              self.filters_layout.addWidget( self.actions )
    593

    594 -    def to_html( self ):
    595          """generates html of the table"""
    596          table = [[getattr( row, col[0] ) for col in self.admin.get_columns()]

    597                   for row in self.admin.entity.query.all()]
    598          context = {

    599            'title': self.admin.get_verbose_name_plural(),

    600            'table': table,

    601            'columns': [field_attributes['name'] for _field, field_attributes in self.admin.get_columns()],

    602          }
    603          from camelot.view.templates import loader
    604          from jinja import Environment
    605          env = Environment( loader = loader )
    606          tp = env.get_template( 'table_view.html' )
    607          return tp.render( context )
    608

    609 -    def importFromFile( self ):
    610          """"import data : the data will be imported in the activeMdiChild """
    611          logger.info( 'call import method' )
    612          from camelot.view.wizard.importwizard import ImportWizard
    613          wizard = ImportWizard(self, self.admin)
    614          wizard.exec_()
    615