Home | Trees | Indices | Help |
|
---|
|
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 """Classes to layout fields on a form. These are mostly used for specifying the 29 form_display attribute in Admin classes, but they can be used on their own as 30 well. Form classes can be used recursive. 31 """ 32 33 import logging 34 logger = logging.getLogger( 'camelot.view.forms' ) 35 36 from camelot.view.model_thread import gui_function39 """Base Form class to put fields on a form. A form can be converted to a 40 QT widget by calling its render method. The base form uses the QFormLayout 41 to render a form:: 42 43 class Admin(EntityAdmin): 44 form_display = Form(['title', 'short_description', 'director', 'release_date']) 45 46 .. image:: ../_static/form/form.png 47 """ 48154 155 def next_col(self): 156 self.col = self.col + 2 157 if self.col >= columns * 2: 158 self.next_row() 159 160 def next_empty_row(self): 161 if self.col!=0: 162 self.next_row() 163 164 def __str__(self): 165 return '%s,%s'%(self.row, self.col) 166 167 c = cursor() 168 for field in self._content: 169 if isinstance( field, Form ): 170 c.next_empty_row() 171 col_span = 2 * columns 172 f = field.render( widgets, parent, True ) 173 if isinstance( f, QtGui.QLayout ): 174 form_layout.addLayout( f, c.row, c.col, row_span, col_span ) 175 else: 176 form_layout.addWidget( f, c.row, c.col, row_span, col_span ) 177 c.next_row() 178 elif field in widgets: 179 label, editor = widgets[field] 180 if isinstance( editor, ( WideEditor, ) ): 181 c.next_empty_row() 182 col_span = 2 * columns 183 if label: 184 form_layout.addWidget( label, c.row, c.col, row_span, col_span ) 185 c.next_row() 186 form_layout.addWidget( editor, c.row, c.col, row_span, col_span ) 187 c.next_row() 188 else: 189 col_span = 1 190 if label: 191 form_layout.addWidget( label, c.row, c.col, row_span, col_span ) 192 form_layout.addWidget( editor, c.row, c.col + 1, row_span, col_span ) 193 c.next_col() 194 else: 195 logger.warning('ProgrammingError : widgets should contain a widget for field %s'%unicode(field)) 196 197 if self._content and form_layout.count(): 198 # get last item in the layout 199 last_item = form_layout.itemAt( form_layout.count() - 1 ) 200 201 # if last item does not contain a widget, 0 is returned 202 # which is fine with the isinstance test 203 w = last_item.widget() 204 205 # add stretch only if last item is not expandable 206 last_size_policy = w.sizePolicy() 207 if last_size_policy.verticalPolicy()==QtGui.QSizePolicy.Expanding: 208 pass 209 else: 210 form_layout.setRowStretch( form_layout.rowCount(), 1 ) 211 212 form_widget = QtGui.QWidget( parent ) 213 214 # fix embedded forms 215 if nomargins: 216 form_layout.setContentsMargins( 0, 0, 0, 0 ) 217 218 form_widget.setSizePolicy( QtGui.QSizePolicy.Expanding, 219 QtGui.QSizePolicy.Expanding ) 220 form_widget.setLayout( form_layout ) 221 222 if self._scrollbars: 223 scroll_area = QtGui.QScrollArea( parent ) 224 scroll_area.setWidget( form_widget ) 225 scroll_area.setWidgetResizable( True ) 226 scroll_area.setFrameStyle( QtGui.QFrame.NoFrame ) 227 return scroll_area 228 229 return form_widget 23050 """ 51 :param content: a list with the field names and forms to render 52 :param columns: the number of columns in which to order the fields. 53 54 eg : with 2 columns, the fields ['street', 'city', 'country'] will 55 be ordered as : 56 57 +-------------+--------------+ 58 | street | city | 59 +-------------+--------------+ 60 | country | | 61 +-------------+--------------+ 62 63 """ 64 assert isinstance( content, list ) 65 self._content = content 66 self._scrollbars = scrollbars 67 self._columns = columns6870 """:return: the fields, visible in this form""" 71 return [field for field in self._get_fields_from_form()]7274 for field in self._content: 75 if isinstance( field, Form ): 76 for nested_field in field._get_fields_from_form(): 77 yield nested_field 78 else: 79 assert isinstance( field, ( str, unicode ) ) 80 yield field;81 8284 """Remove a field from the form, This function can be used to modify 85 inherited forms. 86 87 :param original_field: the name of the field to be removed 88 :return: True if the field was found and removed 89 """ 90 for c in self._content: 91 if isinstance( c, Form ): 92 c.removeField( original_field ) 93 if original_field in self._content: 94 self._content.remove( original_field ) 95 return True 96 if original_field in self._fields: 97 self._fields.remove( original_field ) 98 return True 99 return False100102 """Replace a field on this form with another field. This function can be used to 103 modify inherited forms. 104 105 :param original_field : the name of the field to be replace 106 :param new_field : the name of the new field 107 :return: True if the original field was found and replaced. 108 """ 109 for i, c in enumerate( self._content ): 110 if isinstance( c, Form ): 111 c.replaceField( original_field, new_field ) 112 elif c == original_field: 113 self._content[i] = new_field 114 return True 115 return False116118 self._content.append( new_field )119 122 123 @gui_function125 """:param widgets: a dictionary mapping each field in this form to a tuple 126 of (label, widget editor) 127 128 :return: a QWidget into which the form is rendered 129 """ 130 logger.debug( 'rendering %s' % self.__class__.__name__ ) 131 from camelot.view.controls.editors.wideeditor import WideEditor 132 133 from PyQt4 import QtGui 134 form_layout = QtGui.QGridLayout() 135 136 # where 1 column in the form is a label and a field, so two columns in the grid 137 columns = min(self._columns, len(self._content)) 138 # make sure all columns have the same width 139 if columns > 1: 140 for i in range(columns*2): 141 form_layout.setColumnStretch(i, 1) 142 143 row_span = 1 144 145 class cursor(object): 146 147 def __init__(self): 148 self.row = 0 149 self.col = 0150 151 def next_row(self): 152 self.row = self.row + 1 153 self.col = 0233 """Render a label with a QLabel""" 234254236 """ 237 :param label : string to be displayed in the label 238 :param alignment : alignment of text in the label. values that make sense 'left', 'right' or 'center' 239 :param style : string of cascading stylesheet instructions 240 """ 241 super( Label, self ).__init__( [] ) 242 self.label = label 243 self.alignment = alignment 244 self.style = style245 246 @gui_function256 """ 257 Render forms within a QTabWidget:: 258 259 from = TabForm([('First tab', ['title', 'short_description']), 260 ('Second tab', ['director', 'release_date'])]) 261 262 .. image:: ../_static/form/tab_form.png 263 """ 264 265 NORTH = 'North' 266 SOUTH = 'South' 267 WEST = 'West' 268 EAST = 'East' 269339271 """ 272 :param tabs: a list of tuples of (tab_label, tab_form) 273 :param position: the position of the tabs with respect to the pages 274 """ 275 assert isinstance( tabs, list ) 276 assert position in [self.NORTH, self.SOUTH, self.WEST, self.EAST] 277 self.position = position 278 for tab in tabs: 279 assert isinstance( tab, tuple ) 280 self.tabs = [( tab_label, structure_to_form( tab_form ) ) for tab_label, tab_form in tabs] 281 super( TabForm, self ).__init__( sum( ( tab_form.get_fields() 282 for tab_label, tab_form in self.tabs ), [] ) )283285 return 'TabForm { %s\n }' % ( u'\n '.join( '%s : %s' % ( label, unicode( form ) ) for label, form in self.tabs ) )286288 """Add a tab to the form at the specified index 289 290 :param tab_label: the name to the tab 291 :param tab_form: the form to display in the tab or a list of field names. 292 :param index: the position of tab in the tabs list. 293 """ 294 tab_form = structure_to_form( tab_form ) 295 self.tabs.insert( index, ( tab_label, tab_form ) ) 296 self._content.extend( [tab_form] )297299 """Add a tab to the form 300 301 :param tab_label: the name of the tab 302 :param tab_form: the form to display in the tab or a list of field names. 303 """ 304 tab_form = structure_to_form( tab_form ) 305 self.tabs.append( ( tab_label, tab_form ) ) 306 self._content.extend( [tab_form] )307309 """Get the tab form of associated with a tab_label, use this function to 310 modify the underlying tab_form in case of inheritance 311 312 :param tab_label : a label of a tab as passed in the construction method 313 :return: the tab_form corresponding to tab_label 314 """ 315 for label, form in self.tabs: 316 if label == tab_label: 317 return form318320 for _label, form in self.tabs: 321 if form.replaceField( original_field, new_field ): 322 return True 323 return False324 329 330 @gui_function332 logger.debug( 'rendering %s' % self.__class__.__name__ ) 333 from PyQt4 import QtGui 334 widget = QtGui.QTabWidget( parent ) 335 widget.setTabPosition(getattr(QtGui.QTabWidget, self.position)) 336 for tab_label, tab_form in self.tabs: 337 widget.addTab( tab_form.render( widgets, widget ), unicode(tab_label) ) 338 return widget342 """ 343 Render different forms in a horizontal box:: 344 345 form = forms.HBoxForm([['title', 'short_description'], ['director', 'release_date']]) 346 347 .. image:: ../_static/form/hbox_form.png 348 349 """ 350387352 """:param columns: a list of forms to display in the different columns 353 of the horizontal box""" 354 assert isinstance( columns, list ) 355 self.columns = [structure_to_form( col ) for col in columns] 356 super( HBoxForm, self ).__init__( sum( ( column_form.get_fields() 357 for column_form in self.columns ), [] ) )358 361363 for form in self.columns: 364 if form.replaceField( original_field, new_field ): 365 return True 366 return False367 372 373 @gui_function375 logger.debug( 'rendering %s' % self.__class__.__name__ ) 376 from PyQt4 import QtGui 377 widget = QtGui.QWidget( parent ) 378 form_layout = QtGui.QHBoxLayout() 379 for form in self.columns: 380 f = form.render( widgets, widget, nomargins ) 381 if isinstance( f, QtGui.QLayout ): 382 form_layout.addLayout( f ) 383 else: 384 form_layout.addWidget( f ) 385 widget.setLayout( form_layout ) 386 return widget389 """ 390 Render different forms or widgets in a vertical box:: 391 392 form = forms.VBoxForm([['title', 'short_description'], ['director', 'release_date']]) 393 394 .. image:: ../_static/form/vbox_form.png 395 """ 396433 439398 """:param rows: a list of forms to display in the different columns 399 of the horizontal box 400 """ 401 assert isinstance( rows, list ) 402 self.rows = [structure_to_form( row ) for row in rows] 403 super( VBoxForm, self ).__init__( sum( ( row_form.get_fields() for row_form in self.rows ), [] ) )404406 for form in self.rows: 407 if form.replaceField( original_field, new_field ): 408 return True 409 return False410 415 418 419 @gui_function421 logger.debug( 'rendering %s' % self.__class__.__name__ ) 422 from PyQt4 import QtGui 423 widget = QtGui.QWidget( parent ) 424 form_layout = QtGui.QVBoxLayout() 425 for form in self.rows: 426 f = form.render( widgets, widget, nomargins ) 427 if isinstance( f, QtGui.QLayout ): 428 form_layout.addLayout( f ) 429 else: 430 form_layout.addWidget( f ) 431 widget.setLayout( form_layout ) 432 return widget441 """Put different fields into a grid, without a label. Row or column labels can be added 442 using the Label form:: 443 444 GridForm([['title', 'short_description'], ['director','release_date']]) 445 446 .. image:: ../_static/form/grid_form.png 447 """ 448502450 """:param grid: A list for each row in the grid, containing a list with all fields that should be put in that row 451 """ 452 assert isinstance( grid, list ) 453 self._grid = grid 454 self._nomargins = nomargins 455 fields = [] 456 for row in grid: 457 assert isinstance( row, list ) 458 fields.extend( row ) 459 super( GridForm, self ).__init__( fields )460462 """:param row: the list of fields that should come in the additional row 463 use this method to modify inherited grid forms""" 464 assert isinstance( row, list ) 465 self._content.extend(row) 466 self._grid.append(row)467469 """:param column: the list of fields that should come in the additional column 470 use this method to modify inherited grid forms""" 471 assert isinstance( column, list ) 472 self._content.extend(column) 473 for row, additional_field in zip(self._grid, column): 474 row.append(additional_field)475 476 @gui_function478 from PyQt4 import QtGui 479 widget = QtGui.QWidget( parent ) 480 grid_layout = QtGui.QGridLayout() 481 for i, row in enumerate( self._grid ): 482 skip = 0 483 for j, field in enumerate( row ): 484 num = 1 485 if isinstance( field, ColumnSpan ): 486 num = field.num 487 field = field.field 488 489 if isinstance( field, Form ): 490 grid_layout.addWidget( field.render( widgets, parent ), i, j + skip, 1, num ) 491 skip += num - 1 492 else: 493 _label, editor = widgets[field] 494 grid_layout.addWidget( editor, i, j + skip, 1, num ) 495 skip += num - 1 496 497 widget.setLayout( grid_layout ) 498 if nomargins: 499 grid_layout.setContentsMargins( 0, 0, 0, 0 ) 500 501 return widget504 """Renders a single widget without its label, typically a one2many widget""" 505515507 assert isinstance( field, ( str, unicode ) ) 508 super( WidgetOnlyForm, self ).__init__( [field] )509 510 @gui_function512 logger.debug( 'rendering %s' % self.__class__.__name__ ) 513 _label, editor = widgets[self.get_fields()[0]] 514 return editor517 """ 518 Renders a form within a QGroupBox:: 519 520 class Admin(EntityAdmin): 521 form_display = GroupBoxForm('Movie', ['title', 'short_description']) 522 523 .. image:: ../_static/form/group_box_form.png 524 """ 525545526 - def __init__( self, title, content, scrollbars=None, min_width=None, min_height=None, columns=1 ):527 self.title = title 528 self.min_width = min_width 529 self.min_height = min_height 530 if isinstance(content, Form): 531 content = [content] 532 Form.__init__( self, content, scrollbars, columns=columns )533 534 @gui_function536 from PyQt4 import QtGui 537 widget = QtGui.QGroupBox( unicode(self.title), parent ) 538 layout = QtGui.QVBoxLayout() 539 if self.min_width and self.min_height: 540 widget.setMinimumSize ( self.min_width, self.min_height ) 541 widget.setLayout( layout ) 542 form = Form.render( self, widgets, widget, nomargins ) 543 layout.addWidget( form ) 544 return widget547 """Convert a python data structure to a form, using the following rules : 548 549 * if structure is an instance of Form, return structure 550 * if structure is a list, create a Form from this list 551 552 This function is mainly used in the Admin class to construct forms out of 553 the form_display attribute 554 """ 555 if isinstance( structure, Form ): 556 return structure 557 return Form( structure )558
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Sat Jun 12 15:42:13 2010 | http://epydoc.sourceforge.net |