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):
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' ) ):
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' ) ):
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