Package Camelot :: Package camelot :: Package admin :: Package form_action |
[frames] | no frames]

# Source Code for Package Camelot.camelot.admin.form_action

      1  """
      2  Form actions are objects that can be put in the form_actions list of the
      3  object admin interfaces.  Those actions then appear on the form and can
      4  be executed by the end users.  The most convenient method to create custom actions
      5  is to subclass FormAction and implement a custom run method ::
      6
      7    class MyAction(FormAction):
      8
      9      def run(self, entity_getter):
     10        print 'Hello World'
     11
     12    class Movie(Entity):
     13      title = Field(Unicode(60), required=True)
     14
     15      Admin(EntityAdmin):
     16        list_display = ['title']
     17        form_actions = [MyAction('Hello')]
     18
     19  Several subclasses of FormAction exist to provide common use cases such as executing
     20  a function in the model thread or printing a report.
     21
     22  To customize the look of the action button on the form, the render method should be
     23  overwritten.
     24  """
     25
     26  from PyQt4 import QtGui, QtCore
     27  from camelot.view.art import Icon
     28  from camelot.view.model_thread import gui_function, model_function, post
     29  from camelot.view.controls.progress_dialog import ProgressDialog
     30
     31 -class FormAction( object ):
     32      """Abstract base class to implement form actions"""
     33
     34 -    def __init__( self, name, icon = None ):
     35          """
     36          :param name: the name used in the button to trigger the action
     37          :param icon: Icon to be used in the button to trigger the action
     38          """
     39          self._name = name
     40          self._icon = icon
     41
     42 -    def get_name(self):
     43          return self._name
     44
     45 -    def get_icon(self):
     46          return self._icon
     47
     48      @gui_function
     49 -    def render( self, parent, entity_getter ):
     50          """Returns a QWidget the user can use to trigger the action"""
     51          from camelot.view.controls.action_widget import ActionWidget
     52          return ActionWidget( self, entity_getter, parent=parent )
     53
     54      @gui_function
     55 -    def run( self, entity_getter ):
     56          """Overwrite this method to create an action that does something"""
     57          raise NotImplementedError
     58
     59      @model_function
     60 -    def enabled(self, entity):
     61          """Overwrite this method to have the action only enabled for certain states of the
     62          entity displayed
     63          :param entity: the entity currently in the form view
     64          :return: True or False, returns True by default
     65          """
     66          return True
     67
     68 -class FormActionFromGuiFunction( FormAction ):
     69      """Convert a function that is supposed to run in the GUI thread to a FormAction,
     70      or"""
     71
     72 -    def __init__( self, name, gui_function, icon = None ):
     73          FormAction.__init__( self, name, icon )
     74          self._gui_function = gui_function
     75
     76      @gui_function
     77 -    def run( self, entity_getter ):
     78          self._gui_function( entity_getter )
     79
     80 -class FormActionProgressDialog(ProgressDialog):
     81
     82 -    def __init__(self, name, icon=Icon('tango/32x32/actions/appointment-new.png')):
     83          super(FormActionProgressDialog, self).__init__(name=name, icon=icon)
     84          self.html_document = None
     85
     86 -    def print_result(self, html):
     87          from camelot.view.export.printer import open_html_in_print_preview_from_gui_thread
     88          self.close()
     89          open_html_in_print_preview_from_gui_thread(html, self.html_document)
     90
     91 -class FormActionFromModelFunction( FormAction ):
     92      """Convert a function that is supposed to run in the model thread to a FormAction.
     93      """
     94
     95 -    def __init__( self, name, model_function, icon = None, flush=False, enabled=lambda obj:True ):
     96          """
     97          :param name: the name of the action
     98          :param model_function: a function that has 1 arguments : the object on which to apply the action
     99          :param icon: an Icon
    100          :param flush: flush the object to the db and refresh it in the views
    101          :param enabled: a function that has 1 argument : the object on which the action would be applied
    102          """
    103          FormAction.__init__( self, name, icon )
    104          self._model_function = model_function
    105          self._flush = flush
    106          self._enabled = enabled
    107
    108      @model_function
    109 -    def enabled(self, entity):
    110          return self._enabled( entity )
    111
    112      @gui_function
    113 -    def run( self, entity_getter ):
    114          progress = FormActionProgressDialog(self._name)
    115
    116          def create_request( entity_getter ):
    117
    118              def request():
    119                  from sqlalchemy.orm.session import Session
    120                  from camelot.view.remote_signals import get_signal_handler
    121                  o = entity_getter()
    122                  self._model_function( o )
    123                  if self._flush:
    124                      sh = get_signal_handler()
    125                      Session.object_session( o ).flush( [o] )
    126                      sh.sendEntityUpdate( self, o )
    127                  return True
    128
    129              return request
    130
    131          post( create_request( entity_getter ), progress.finished, exception = progress.exception )
    132          progress.exec_()
    133
    134 -class PrintHtmlFormAction( FormActionFromModelFunction ):
    135      """Create an action for a form that pops up a print preview for generated html.
    136    Overwrite the html function to customize the html that should be shown::
    137
    138      class PrintMovieAction(PrintHtmlFormAction):
    139
    140        def html(self, movie):
    141          html = '<h1>' + movie.title + '</h1>'
    142          html += movie.description
    143        return html
    144
    145      class Movie(Entity):
    146        title = Field(Unicode(60), required=True)
    147        description = Field(camelot.types.RichText)
    148
    149        class Admin(EntityAdmin):
    150          list_display = ['title', 'description']
    151          form_actions = [PrintMovieAction('summary')]
    152
    153    will put a print button on the form :
    154
    155    .. image:: ../_static/formaction/print_html_form_action.png
    156
    157
    158    .. attribute:: HtmlDocument the class used to render the html, by default this is
    159    a QTextDocument, but a QtWebKit.QWebView can be used as well.
    160
    161      """
    162
    163      HtmlDocument = QtGui.QTextDocument
    164
    165 -    def __init__( self, name, icon = Icon( 'tango/16x16/actions/document-print.png' ) ):
    166          FormActionFromModelFunction.__init__( self, name, self.html, icon )
    167
    168 -    def html( self, o ):
    169          """Overwrite this function to generate custom html to be printed
    170          :arg o: the object that is displayed in the form"""
    171          return '<h1>' + unicode( o ) + '<h1>'
    172
    173      @gui_function
    174 -    def run( self, entity_getter ):
    175          progress = FormActionProgressDialog(self._name)
    176          progress.html_document = self.HtmlDocument
    177
    178          def create_request( entity_getter ):
    179
    180              def request():
    181                  o = entity_getter()
    182                  return self.html( o )
    183
    184              return request
    185
    186          post( create_request( entity_getter ), progress.print_result, exception = progress.exception )
    187          progress.exec_()
    188
    189 -class DocxFormAction( FormActionFromModelFunction ):
    190      """Action that generates a .docx file and opens it.  It does so by generating an xml document
    191      with jinja templates that is a valid word document.
    192      """
    193
    194 -    def __init__( self, name, icon = Icon( 'tango/16x16/mimetypes/x-office-document.png' ) ):
    195          FormActionFromModelFunction.__init__( self, name, self.open_xml, icon )
    196
    197 -    def get_context(self, obj):
    198          """:return: a dictionary with objects to be used as context when jinja fills up the xml document"""
    199          return {}
    200
    201 -    def get_environment(self, obj):
    202          """Return the jinja environment to be used to render the xml document"""
    203          from jinja import Environment
    204          e = Environment()
    205          return e
    206
    207 -    def get_template(self, obj):
    208          """:return: the name of the jinja template for xml document"""
    209          raise NotImplemented
    210
    211 -    def document(self, obj):
    212          """:return: the xml content of the document"""
    213          e = self.get_environment(obj)
    214          context = self.get_context(obj)
    215          t = e.get_template(self.get_template(obj))
    216          document_xml = t.render(context)
    217          return document_xml
    218
    219 -    def open_xml(self, obj):
    220          from camelot.view.export.word import open_document_in_word
    221          import tempfile
    222          import os
    223          fd, fn = tempfile.mkstemp(suffix='.xml')
    224          docx_file = os.fdopen(fd, 'wb')
    225          docx_file.write(self.document(obj).encode('utf-8'))
    226          docx_file.close()
    227          open_document_in_word(fn)
    228
    229 -def structure_to_form_actions( structure ):
    230      """Convert a list of python objects to a list of form actions.  If the python
    231      object is a tuple, a FormActionFromGuiFunction is constructed with this tuple as arguments.  If
    232      the python object is an instance of a FormAction, it is kept as is.
    233      """
    234
    235      def object_to_action( o ):
    236          if isinstance( o, FormAction ):
    237              return o
    238          return FormActionFromGuiFunction( o[0], o[1] )
    239
    240      return [object_to_action( o ) for o in structure]
    241