Skip to content

Commit

Permalink
Merge branch 'amol' into feature/datagrid
Browse files Browse the repository at this point in the history
  • Loading branch information
ralphbean committed Mar 13, 2012
2 parents 2d2dd44 + 95deda7 commit 9335bfa
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 2 deletions.
1 change: 1 addition & 0 deletions tw2/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@
)
from mashups import PostlabeledCheckBox, PostlabeledPartialRadioButton
from calendars import CalendarDatePicker, CalendarDateTimePicker
from datagrid import DataGrid
131 changes: 131 additions & 0 deletions tw2/forms/datagrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import operator
import tw2.core as twc

NoDefault = object()

__all__ = ["DataGrid", "Column"]


class Column(object):
"""Simple struct that describes a single DataGrid column.
Column has:
- a name, which allows to uniquely identify a column in a DataGrid
- a getter, which is used to extract the field's value
- a title, which is displayed in the table's header
- options, which is a way to carry arbitrary user-defined data
"""

def __init__(self, name, getter=None, title=None, options=None):
if not name:
raise ValueError('name is required')

if getter:
if callable(getter):
self.getter = getter
else: # assume it's an attribute name
self.getter = operator.attrgetter(getter)
else:
self.getter = attrwrapper(name)
self.name = name
self.title = title is None and name.capitalize() or title
self.options = options or {}

def get_option(self, name, default=NoDefault):
if name in self.options:
return self.options[name]
if default is NoDefault: # no such key and no default is given
raise KeyError(name)
return default

def get_field(self, row, displays_on=None):
if getattr(self.getter, '__bases__', None) and \
issubclass(self.getter, twc.Widget) or \
isinstance(self.getter, twc.Widget):
return self.getter.display(value=row, displays_on=displays_on)
return self.getter(row)

def __str__(self):
return "<Column %s>" % self.name


class DataGrid(twc.Widget):
"""Generic widget to present and manipulate data in a grid (tabular) form.
The columns to build the grid from are specified with fields constructor
argument which is a list. An element can be a Column, an accessor
(attribute name or function), a tuple (title, accessor) or a tuple
(title, accessor, options).
You can specify columns' data statically, via fields constructor parameter,
or dynamically, via 'fields' key.
"""
resources = [
twc.CSSLink(modname='tw2.forms',
filename='static/datagrid/datagrid.css')
]
template = "tw2.forms.templates.datagrid"
css_class = 'grid'

fields = twc.Param('Fields of the Grid', default=[], attribute=False)
columns = twc.Variable('Used internally', default=[])

@staticmethod
def get_field_getter(columns):
"""Return a function to access the fields of table by row, col."""
idx = {} # index columns by name
for col in columns:
idx[col.name] = col

def _get_field(row, col):
return idx[col].get_field(row)

return _get_field

def _parse(self, fields):
"""Parse field specifications into a list of Columns.
A specification can be a Column,
an accessor (attribute name or function), a tuple (title, accessor)
or a tuple (title, accessor, options).
"""
columns = []
names = {} # keep track of names to ensure there are no dups
for n, col in enumerate(fields):
if not isinstance(col, Column):
if isinstance(col, str) or callable(col):
name_or_f = col
title = options = None
else:
title, name_or_f = col[:2]
try:
options = col[2]
except IndexError:
options = None
# construct name using column index
name = 'column-' + str(n)
col = Column(name, name_or_f, title, options)
if col.name in names:
raise ValueError('Duplicate column name: %s' % col.name)
columns.append(col)
names[col.name] = 1
return columns

def prepare(self):
super(DataGrid, self).prepare()

if not self.value:
raise ValueError(
"DataGrid must be passed a value.")

if not self.fields and not self.columns:
raise ValueError(
"DataGrid must be passed either fields or columns")

if self.fields:
self.columns = self._parse(self.fields)

self.get_field = self.get_field_getter(self.columns)
21 changes: 21 additions & 0 deletions tw2/forms/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@

import tw2.core as twc
import widgets as twf
import datagrid as dg

options = ['Red', 'Orange', 'Yellow', 'Green', 'Blue']


class DemoTextField(twf.TextField):
placeholder = "Search..."


class DemoChildren(twc.CompoundWidget):
title = twf.TextField()
priority = twf.SingleSelectField(options=['', 'Normal', 'High'])
Expand Down Expand Up @@ -99,3 +101,22 @@ class DemoGridLayout(twf.GridLayout):
class DemoImageButton(twf.ImageButton):
modname = 'tw2.forms'
filename = 'static/edit-undo.png'


class DemoDataGrid(dg.DataGrid):
class DummyObject(object):
def __init__(self, name):
self._name = name

def name(self):
return self._name

def address(self):
return "Fancy pancy street."

value = [
DummyObject("Jimmy John"),
DummyObject("Sally Sue"),
]

fields = [DummyObject.name, DummyObject.address]
15 changes: 15 additions & 0 deletions tw2/forms/static/datagrid/datagrid.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
.grid { background-color:#e3e3e3;font-size:12px;font-family:verdana,sans-serif;border:none;width:100%;}
.grid td, .grid th {padding:3px;border:none;}
.grid .action_cell { text-align:right; }
.grid THEAD { text-align:left;background-color:#f0f0f0;color:#333;}
.grid .heading img { float:right;margin-left:2px;margin-right:3px; }
.grid .heading a { text-decoration:none;color:#333; }
.grid td a { text-decoration:none;color:#333}
.grid .odd{background-color:#edf3fe}
.grid .even{background-color:#fff}
.grid .pointer {cursor:pointer}
.grid .column_chooser_link {position:relative;background-color:#e3e3e3;}
.grid .column_chooser_link ul {position:absolute;display:none;top:0px;right:-20px;}
.grid .column_chooser_list a {width:200px;display:block;padding:3px;background-color:#e3e3e3;}
.grid .column_chooser_list a:hover {background-color:#cdcdcd;}
.grid .column_chooser_list { padding:0;margin:0;list-style:none;background-color:#e3e3e3; }
19 changes: 19 additions & 0 deletions tw2/forms/templates/datagrid.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<table xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
py:attrs="w.attrs" cellpadding="0" cellspacing="1" border="0">
<thead py:if="w.columns">
<tr>
<th py:for="i, col in enumerate(w.columns)"
class="col_${i}" py:content="col.title"/>
</tr>
</thead>
<tbody>
<tr py:for="i, row in enumerate(w.value)"
class="${i%2 and 'odd' or 'even'}">
<td py:for="col in w.columns"
align="${col.get_option('align', None)}"
class="${col.get_option('css_class', None)}"
py:content="col.get_field(row, displays_on='genshi')"/>
</tr>
</tbody>
</table>
24 changes: 24 additions & 0 deletions tw2/forms/templates/datagrid.mak
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<%namespace name="tw" module="tw2.core.mako_util"/>\
<table ${tw.attrs(attrs=w.attrs)} cellpadding="0" cellspacing="1" border="0">
% if w.columns:
<thead>
<tr>
% for i, col in enumerate(w.columns):
<th class="col_${str(i)}">${col.title}</th>
% endfor
</tr>
</thead>
% endif
<tbody>
% for i, row in enumerate(w.value):
<tr class="${i%2 and 'odd' or 'even'}">
% for col in w.columns:
<td ${tw.attrs(
[('align', col.get_option('align', None)),
('class', col.get_option('css_class', None))],
)}>${col.get_field(row)}</td>
% endfor
</tr>
% endfor
</tbody>
</table>\
7 changes: 5 additions & 2 deletions tw2/forms/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class InputField(FormField):
value = twc.Param(attribute=True)
template = "tw2.forms.templates.input_field"


class PostlabeledInputField(InputField):
""" Inherits :class:`InputField`, but with a :attr:`text`
label that follows the input field """
Expand All @@ -42,14 +43,16 @@ class PostlabeledInputField(InputField):

class TextField(InputField):
size = twc.Param('Size of the field', default=None, attribute=True)
placeholder = twc.Param('Placeholder text (HTML5 Only)', attribute=True, default=None)
placeholder = twc.Param(
'Placeholder text (HTML5 Only)', attribute=True, default=None)
type = 'text'


class TextArea(FormField):
rows = twc.Param('Number of rows', default=None, attribute=True)
cols = twc.Param('Number of columns', default=None, attribute=True)
placeholder = twc.Param('Placeholder text (HTML5 Only)', attribute=True, default=None)
placeholder = twc.Param(
'Placeholder text (HTML5 Only)', attribute=True, default=None)
template = "tw2.forms.templates.textarea"


Expand Down

0 comments on commit 9335bfa

Please sign in to comment.