class DynaTableWidget(Composite): def __init__(self, provider, columns, columnStyles, rowCount): Composite.__init__(self) self.acceptor = RowDataAcceptorImpl(self) self.outer = DockPanel() self.startRow = 0 self.grid = Grid() self.navbar = NavBar(self) self.provider = provider self.initWidget(self.outer) self.grid.setStyleName("table") self.outer.add(self.navbar, DockPanel.NORTH) self.outer.add(self.grid, DockPanel.CENTER) self.initTable(columns, columnStyles, rowCount) self.setStyleName("DynaTable-DynaTableWidget") def initTable(self, columns, columnStyles, rowCount): self.grid.resize(rowCount + 1, len(columns)) for i in range(len(columns)): self.grid.setText(0, i, columns[i]) if columnStyles: self.grid.cellFormatter.setStyleName( 0, i, columnStyles[i] + "header") def setStatusText(self, text): self.navbar.status.setText(text) def clearStatusText(self, text): self.navbar.status.setHTML(" ") def refresh(self): self.navbar.gotoFirst.setEnabled(False) self.navbar.gotoPrev.setEnabled(False) self.navbar.gotoNext.setEnabled(False) self.setStatusText("Please wait...") self.provider.updateRowData(self.startRow, self.grid.getRowCount() - 1, self.acceptor) def setRowCount(self, rows): self.grid.resizeRows(rows) def getDataRowCount(self): return self.grid.getRowCount() - 1
class DynaTableWidget(Composite): def __init__(self, provider, columns, columnStyles, rowCount): Composite.__init__(self) self.acceptor = RowDataAcceptorImpl(self) self.outer = DockPanel() self.startRow = 0 self.grid = Grid() self.navbar = NavBar(self) self.provider = provider self.initWidget(self.outer) self.grid.setStyleName("table") self.outer.add(self.navbar, DockPanel.NORTH) self.outer.add(self.grid, DockPanel.CENTER) self.initTable(columns, columnStyles, rowCount) self.setStyleName("DynaTable-DynaTableWidget") def initTable(self, columns, columnStyles, rowCount): self.grid.resize(rowCount + 1, len(columns)) for i in range(len(columns)): self.grid.setText(0, i, columns[i]) if columnStyles: self.grid.cellFormatter.setStyleName(0, i, columnStyles[i] + "header") def setStatusText(self, text): self.navbar.status.setText(text) def clearStatusText(self, text): self.navbar.status.setHTML(" ") def refresh(self): self.navbar.gotoFirst.setEnabled(False) self.navbar.gotoPrev.setEnabled(False) self.navbar.gotoNext.setEnabled(False) self.setStatusText("Please wait...") self.provider.updateRowData(self.startRow, self.grid.getRowCount() - 1, self.acceptor) def setRowCount(self, rows): self.grid.resizeRows(rows) def getDataRowCount(self): return self.grid.getRowCount() - 1
def createResultGrid( self, gridName ) : label=HTML('<br><b>'+gridName+'</b>') grid=Grid(17,17) # Create the column and row headers for index in range( 1, grid.getColumnCount() ) : grid.setWidget( 0, index, HTML('Cx%X'%(index-1)) ) for index in range( 1, grid.getRowCount() ) : grid.setWidget( index, 0, HTML('C%Xx'%(index-1)) ) self.mainPanel.add(label) self.mainPanel.add(grid) self.resultLabels[gridName]=label self.resultGrids[gridName]=grid
class Form(FormPanel): def __init__(self, svc, **kwargs): self.describe_listeners = [] if kwargs.has_key('listener'): listener = kwargs.pop('listener') self.addDescribeListener(listener) if kwargs.has_key('data'): data = kwargs.pop('data') else: data = None writebr(repr(data)) FormPanel.__init__(self, **kwargs) self.svc = svc self.grid = Grid() self.grid.resize(0, 3) self.add(self.grid) self.describer = FormDescribeGrid(self) self.saver = FormSaveGrid(self) self.getter = FormGetGrid(self) self.formsetup(data) def addDescribeListener(self, l): self.describe_listeners.append(l) def add_widget(self, description, widget): """ adds a widget, with error rows interspersed """ num_rows = self.grid.getRowCount() self.grid.resize((num_rows + 1), 3) self.grid.setHTML(num_rows, 0, description) self.grid.setWidget(num_rows, 1, widget) def get(self, **kwargs): writebr(repr(kwargs)) self.svc({}, {'get': kwargs}, self.getter) def save(self, data=None): self.clear_errors() if data is None: data = self.getValue() self.data = data writebr(repr(self.data)) self.svc(data, {'save': None}, self.saver) def save_respond(self, response): if not response['success']: errors = response['errors'] self.set_errors(errors) for l in self.describe_listeners: l.onErrors(self, errors) return for l in self.describe_listeners: l.onSaveDone(self, response) def formsetup(self, data=None): if data is None: data = {} self.data = data writebr(repr(self.data)) self.svc(data, {'describe': None}, self.describer) def clear_errors(self): for idx, fname in enumerate(self.fields): self.grid.setHTML(idx, 2, None) def set_errors(self, errors): offsets = {} for idx, fname in enumerate(self.fields): offsets[fname] = idx for k, err in errors.items(): err = "<br />".join(err) idx = offsets[k] self.grid.setHTML(idx, 2, err) def update_values(self, data=None): if data is not None: self.data = data for idx, fname in enumerate(self.fields): val = None if self.data.has_key(fname): val = self.data[fname] w = self.grid.getWidget(idx, 1) w.setValue(val) def do_get(self, response): fields = response.get('instance', None) if fields: self.update_values(fields) for l in self.describe_listeners: l.onRetrieveDone(self, fields) def do_describe(self, fields): self.fields = fields.keys() for idx, fname in enumerate(self.fields): field = fields[fname] if self.data and self.data.has_key(fname): field['initial'] = self.data[fname] writebr("%s %s %d" % (fname, field['label'], idx)) field_type = field['type'] widget_kls = widget_factory.get(field_type, CharField) fv = {} for (k, v) in field.items(): fv[str(k)] = v w = widget_kls(**fv) self.add_widget(field['label'], w) for l in self.describe_listeners: l.onDescribeDone(self) def getValue(self): res = {} for idx, fname in enumerate(self.fields): w = self.grid.getWidget(idx, 1) val = w.getValue() res[fname] = val self.data[fname] = val return res
class Form(FormPanel): def __init__(self, svc, **kwargs): self.describe_listeners = [] if kwargs.has_key('listener'): listener = kwargs.pop('listener') self.addDescribeListener(listener) if kwargs.has_key('data'): data = kwargs.pop('data') else: data = None FormPanel.__init__(self, **kwargs) self.svc = svc self.grid = Grid() self.grid.resize(0, 3) self.add(self.grid) self.describer = FormDescribeGrid(self) self.saver = FormSaveGrid(self) self.getter = FormGetGrid(self) self.formsetup(data) def addDescribeListener(self, l): self.describe_listeners.append(l) def add_widget(self, description, widget): """ adds a widget, with error rows interspersed """ num_rows = self.grid.getRowCount() self.grid.resize((num_rows+1), 3) self.grid.setHTML(num_rows, 0, description) self.grid.setWidget(num_rows, 1, widget) def get(self, **kwargs): writebr(repr(kwargs)) self.svc({}, {'get': kwargs}, self.getter) def save(self, data=None): self.clear_errors() if data is None: data = self.getValue() self.data = data writebr(repr(self.data)) self.svc(data, {'save': None}, self.saver) def save_respond(self, response): if not response['success']: errors = response['errors'] self.set_errors(errors) for l in self.describe_listeners: l.onErrors(self, errors) return for l in self.describe_listeners: l.onSaveDone(self, response) def formsetup(self, data=None): if data is None: data = {} self.data = data self.svc(data, {'describe': None}, self.describer) def clear_errors(self): for idx, fname in enumerate(self.fields): self.grid.setHTML(idx, 2, None) def set_errors(self, errors): offsets = {} for idx, fname in enumerate(self.fields): offsets[fname] = idx for k, err in errors.items(): err = "<br />".join(err) idx = offsets[k] self.grid.setHTML(idx, 2, err) def update_values(self, data = None): if data is not None: self.data = data for idx, fname in enumerate(self.fields): val = None if self.data.has_key(fname): val = self.data[fname] w = self.grid.getWidget(idx, 1) w.setValue(val) def do_get(self, response): fields = response.get('instance', None) if fields: self.update_values(fields) for l in self.describe_listeners: l.onRetrieveDone(self, fields) def do_describe(self, fields): self.fields = fields.keys() for idx, fname in enumerate(self.fields): field = fields[fname] if self.data and self.data.has_key(fname): field['initial'] = self.data[fname] field_type = field['type'] widget_kls = widget_factory.get(field_type, CharField) fv = {} for (k, v) in field.items(): fv[str(k)] = v w = widget_kls(**fv) self.add_widget(field['label'], w) for l in self.describe_listeners: l.onDescribeDone(self) def getValue(self): res = {} for idx, fname in enumerate(self.fields): w = self.grid.getWidget(idx, 1) val = w.getValue() res[fname] = val self.data[fname] = val return res
class Photos(Composite): def __init__(self): Composite.__init__(self) self.albums = [] self.photos = [] self.grid = Grid(4, 4, CellPadding=4, CellSpacing=4) self.grid.addTableListener(self) self.drill = 0 self.pos = 0 self.up = Button("Up", self) self.next = Button("Next", self) self.prev = Button("Prev", self) self.timer = Timer(notify=self) self.userid = "jameskhedley" self.album_url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "?alt=json-in-script&kind=album&hl=en_US&callback=restCb" self.doRESTQuery(self.album_url, self.timer) self.vp = VerticalPanel() self.disclosure = DisclosurePanel( "Click for boring technical details.") self.disclosure.add( HTML( '''<p>OK so you want to write client JS to do a RESTful HTTP query from picasa right? Well you can't because of the Same Origin Policy. Basically this means that because the domain of the query and the domain of the hosted site are different, then that could well be a cross-site scripting (XSS) attack. So, the workaround is to do the call from a script tag so the JSON we get back is part of the document. But since we don't know what URL to hit yet, once we find out then we have to inject a new script tag dynamically which the browser will run as soon as we append it. To be honest I'm not 100% why Google use RESTful services and not JSON-RPC or somesuch, which would be easier. Well, easier for me.''')) self.IDPanel = HorizontalPanel() self.IDPanel.add(Label("Enter google account:")) self.IDButton = Button("Go", self) self.IDBox = TextBox() self.IDBox.setText(self.userid) self.IDPanel.add(self.IDBox) self.IDPanel.add(self.IDButton) self.vp.add(self.IDPanel) self.vp.add(self.disclosure) self.vp.add(self.grid) self.initWidget(self.vp) def doRESTQuery(self, url, timer): """this is a totally different from an RPC call in that we have to dynamically add script tags to the DOM when we want to query the REST API. These rely on callbacks in the DOM so we can either add them dynamically or pre-define them in public/Main.html. Once we've done that have to wait for the response. Which means we need to provide a listener for the timer""" new_script = DOM.createElement("script") DOM.setElemAttribute(new_script, "src", url) DOM.setElemAttribute(new_script, "type", "text/javascript") doc().body.appendChild(new_script) self.timer.schedule(100) def onCellClicked(self, sender, row, col): if self.drill == 0: self.drill += 1 self.vp.clear() self.grid.clear() self.vp.add(self.up) self.vp.add(self.grid) gridcols = self.grid.getColumnCount() album = self.albums[row + col + (row * (gridcols - 1))] url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "/albumid/" + album[ "id"] + "?alt=json-in-script&kind=photo&hl=en_US&callback=restCb" self.doRESTQuery(url, self.timer) elif self.drill == 1: self.drill += 1 gridcols = self.grid.getColumnCount() self.pos = row + col + (row * (gridcols - 1)) photo = self.photos[self.pos] self.vp.clear() self.fullsize = HTML('<img src="' + photo["full"] + '"/>') hp = HorizontalPanel() hp.add(self.up) hp.add(self.prev) hp.add(self.next) hp.setSpacing(8) self.vp.add(hp) self.vp.add(self.fullsize) def onClick(self, sender): if sender == self.IDButton: self.userid = self.IDBox.getText() if self.userid == "" or self.userid.isdigit(): return self.drill = 0 self.album_url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "?alt=json-in-script&kind=album&hl=en_US&callback=restCb" self.grid.clear() self.doRESTQuery(self.album_url, self.timer) else: if self.drill == 2: if sender == self.up: self.drill = 1 self.vp.clear() self.vp.add(self.up) self.vp.add(self.grid) self.fillGrid(self.photos, "photos") else: if sender == self.next: if self.pos >= len(self.photos): return self.pos += 1 elif sender == self.prev: if self.pos < 1: return self.pos -= 1 photo = self.photos[self.pos] self.fullsize.setHTML('<img src="' + photo["full"] + '"/>') elif self.drill == 1: self.drill = 0 self.vp.clear() self.vp.add(self.IDPanel) self.vp.add(self.disclosure) self.vp.add(self.grid) self.fillGrid(self.albums, "albums") def onTimer(self, timer): fd = doc().getElementById("__pygwt_hiddenData") receiver = fd.innerHTML if receiver == 'wait': self.timer.schedule(1000) return fd.innerHTML = 'wait' if self.drill == 0: self.parseAlbums(receiver) self.fillGrid(self.albums, "albums") elif self.drill == 1: self.parsePhotos(receiver) self.fillGrid(self.photos, "photos") def fillGrid(self, items, type): self.grid.clear() cols = self.grid.getColumnCount() self.grid.resizeRows((len(items) / cols) + 1) rows = self.grid.getRowCount() for i in range(len(items)): vp = VerticalPanel() if type == 'photos': vp.add(items[i]['thumb']) else: vp.add(items[i]['thumb']) vp.add(items[i]['title']) self.grid.setWidget(int(i / cols), i % cols, vp) def parsePhotos(self, items): photo_list = json.loads(items) self.photos = [] for ph in photo_list: aphoto = {} aphoto['thumb'] = HTML( '<img src="' + ph[u"media$group"][u"media$thumbnail"][1][u"url"] + '"/>') aphoto['full'] = ph[u"media$group"][u"media$content"][0][u"url"] self.photos.append(aphoto) def parseAlbums(self, items): album_list = json.loads(items) self.albums = [] for al in album_list: analbum = {} analbum['title'] = HTML(al[u"title"][u"$t"]) analbum['thumb'] = HTML( '<img src="' + al[u"media$group"][u"media$thumbnail"][0][u"url"] + '"/>') url = al[u"id"][u"$t"] analbum['id'] = url.split(u'albumid/')[1].split(u'?alt')[0] self.albums.append(analbum)
class Photos(Composite): def __init__(self): Composite.__init__(self) self.albums = [] self.photos = [] self.grid = Grid(4, 4, CellPadding=4, CellSpacing=4) self.grid.addTableListener(self) self.drill = 0 self.pos = 0 self.up = Button("Up", self) self.next = Button("Next", self) self.prev = Button("Prev", self) self.timer = Timer(notify=self) self.userid = "jameskhedley" self.album_url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "?alt=json-in-script&kind=album&hl=en_US&callback=restCb" self.doRESTQuery(self.album_url, self.timer) self.vp = VerticalPanel() self.disclosure = DisclosurePanel("Click for boring technical details.") self.disclosure.add(HTML('''<p>OK so you want to write client JS to do a RESTful HTTP query from picasa right? Well you can't because of the Same Origin Policy. Basically this means that because the domain of the query and the domain of the hosted site are different, then that could well be a cross-site scripting (XSS) attack. So, the workaround is to do the call from a script tag so the JSON we get back is part of the document. But since we don't know what URL to hit yet, once we find out then we have to inject a new script tag dynamically which the browser will run as soon as we append it. To be honest I'm not 100% why Google use RESTful services and not JSON-RPC or somesuch, which would be easier. Well, easier for me.''')) self.IDPanel = HorizontalPanel() self.IDPanel.add(Label("Enter google account:")) self.IDButton = Button("Go", self) self.IDBox = TextBox() self.IDBox.setText(self.userid) self.IDPanel.add(self.IDBox) self.IDPanel.add(self.IDButton) self.vp.add(self.IDPanel) self.vp.add(self.disclosure) self.vp.add(self.grid) self.initWidget(self.vp) def doRESTQuery(self, url, timer): """this is a totally different from an RPC call in that we have to dynamically add script tags to the DOM when we want to query the REST API. These rely on callbacks in the DOM so we can either add them dynamically or pre-define them in public/Main.html. Once we've done that have to wait for the response. Which means we need to provide a listener for the timer""" JS("$wnd.receiver = 'wait'") new_script = DOM.createElement("script") DOM.setElemAttribute(new_script, "src", url) DOM.setElemAttribute(new_script, "type","text/javascript") JS("$wnd.document.body.appendChild(@{{new_script}})") self.timer.schedule(100) def onCellClicked(self, sender, row, col): if self.drill==0: self.drill += 1 self.vp.clear() self.grid.clear() self.vp.add(self.up) self.vp.add(self.grid) gridcols = self.grid.getColumnCount() album = self.albums[row+col+(row*(gridcols-1))] url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "/albumid/" + album["id"] + "?alt=json-in-script&kind=photo&hl=en_US&callback=restCb" self.doRESTQuery(url, self.timer) elif self.drill==1: self.drill += 1 gridcols = self.grid.getColumnCount() self.pos =row+col+(row*(gridcols-1)) photo = self.photos[self.pos] self.vp.clear() self.fullsize = HTML('<img src="' + photo["full"] + '"/>') hp = HorizontalPanel() hp.add(self.up) hp.add(self.prev) hp.add(self.next) hp.setSpacing(8) self.vp.add(hp) self.vp.add(self.fullsize) def onClick(self, sender): if sender == self.IDButton: self.userid = self.IDBox.getText() if self.userid == "" or self.userid.isdigit(): return self.drill = 0 self.album_url = "http://picasaweb.google.com/data/feed/base/user/" + self.userid + "?alt=json-in-script&kind=album&hl=en_US&callback=restCb" self.grid.clear() self.doRESTQuery(self.album_url, self.timer) else: if self.drill == 2: if sender == self.up: self.drill=1 self.vp.clear() self.vp.add(self.up) self.vp.add(self.grid) self.fillGrid(self.photos, "photos") else: if sender == self.next: if self.pos >= len(self.photos): return self.pos +=1 elif sender == self.prev: if self.pos < 1: return self.pos -=1 photo = self.photos[self.pos] self.fullsize.setHTML('<img src="' + photo["full"] + '"/>') elif self.drill == 1: self.drill=0 self.vp.clear() self.vp.add(self.IDPanel) self.vp.add(self.disclosure) self.vp.add(self.grid) self.fillGrid(self.albums, "albums") def onTimer(self, timer): receiver = JS("$wnd.receiver") if receiver == 'wait': self.timer.schedule(1000) return JS("$wnd.receiver = 'wait'") if self.drill == 0: self.parseAlbums(receiver) self.fillGrid(self.albums, "albums") elif self.drill == 1: self.parsePhotos(receiver) self.fillGrid(self.photos, "photos") def fillGrid(self, items, type): self.grid.clear() cols = self.grid.getColumnCount() self.grid.resizeRows((len(items)/cols)+1) rows = self.grid.getRowCount() for i in range(len(items)): vp = VerticalPanel() if type == 'photos': vp.add(items[i]['thumb']) else: vp.add(items[i]['thumb']) vp.add(items[i]['title']) self.grid.setWidget(int(i/cols), i%cols, vp) def parsePhotos(self, items): photo_list = JSONParser().jsObjectToPyObject(items) self.photos = [] for i in range(len(photo_list)): index = "%s" % i aphoto = {} aphoto['thumb'] = HTML('<img src="' + photo_list[index]["media$group"]["media$thumbnail"]["1"]["url"] + '"/>') aphoto['full'] = photo_list[index]["media$group"]["media$content"]["0"]["url"] self.photos.append(aphoto) def parseAlbums(self, items): album_list = JSONParser().jsObjectToPyObject(items) self.albums = [] for i in range(len(album_list)): index = "%s" % i analbum = {} analbum['title'] = HTML(album_list[index]["title"]["$t"]) analbum['thumb'] = HTML('<img src="' + album_list[index]["media$group"]["media$thumbnail"]["0"]["url"] + '"/>') url = album_list[index]["id"]["$t"] analbum['id'] = url.split('albumid/')[1].split('?alt')[0] self.albums.append(analbum)