class GridWidget(AbsolutePanel): def __init__(self): AbsolutePanel.__init__(self) self.page=0 self.min_page=1 self.max_page=10 self.addb=Button("Next >", self) self.subb=Button("< Prev", self) self.clearb=Button("Clear", self) self.g=Grid() self.g.resize(5, 5) self.g.setWidget(0, 0, HTML("<b>Grid Test</b>")) self.g.setBorderWidth(2) self.g.setCellPadding(4) self.g.setCellSpacing(1) self.updatePageDisplay() self.add(self.subb) self.add(self.addb) self.add(self.clearb) self.add(self.g) def onClick(self, sender): if sender == self.clearb: print "clear" self.g.clear() return elif sender==self.addb: self.page+=1 elif sender==self.subb: self.page-=1 self.updatePageDisplay() def updatePageDisplay(self): if self.page<self.min_page: self.page=self.min_page elif self.page>self.max_page: self.page=self.max_page total_pages=(self.max_page-self.min_page) + 1 self.g.setHTML(0, 4, "<b>page %d of %d</b>" % (self.page, total_pages)) if self.page>=self.max_page: self.addb.setEnabled(False) else: self.addb.setEnabled(True) if self.page<=self.min_page: self.subb.setEnabled(False) else: self.subb.setEnabled(True) for y in range(1, 5): for x in range(5): self.g.setText(y, x, "%d (%d,%d)" % (self.page, x, y))
class Underway(HorizontalPanel): def __init__(self): HorizontalPanel.__init__(self, Spacing=4) self.add(Label('Underway:', StyleName='section')) s = ScrollPanel() self.add(s) v = VerticalPanel() s.add(v) self.queued = Grid(StyleName='users') v.add(self.queued) self.button = Button('Refresh', self, StyleName='refresh') self.add(self.button) self.err = Label() self.add(self.err) self.update() def update(self): remote = server.AdminService() id = remote.getUnderway(self) if id < 0: self.err.setText('oops: could not call getUnderway') def onRemoteResponse(self, result, request_info): self.button.setEnabled(True) self.queued.clear() if not result: self.queued.resize(1, 1) self.queued.setText(0, 0, 'No users are currently being added.') else: self.queued.resize(len(result) + 1, 6) self.queued.setText(0, 0, 'Pos') self.queued.setText(0, 1, 'Name') self.queued.setText(0, 2, 'Friends') self.queued.setText(0, 3, '% done') self.queued.setText(0, 4, 'Queued at') self.queued.setText(0, 5, 'Started at') row = 1 for name, nFriends, done, queuedAt, startTime in result: self.queued.setText(row, 0, row) self.queued.setText(row, 1, name) self.queued.setText(row, 2, nFriends) self.queued.setText(row, 3, '%.2f' % done) self.queued.setText(row, 4, queuedAt) self.queued.setText(row, 5, startTime) row += 1 def onRemoteError(self, code, message, request_info): self.button.setEnabled(True) self.err.setText('Could not getUnderway: ' + message) def onClick(self, sender): self.err.setText('') self.button.setEnabled(False) self.update()
class Queued(HorizontalPanel): def __init__(self): HorizontalPanel.__init__(self, Spacing=4) self.add(Label("Queued:", StyleName="section")) s = ScrollPanel() self.add(s) v = VerticalPanel() s.add(v) self.queued = Grid(StyleName="users") v.add(self.queued) self.button = Button("Refresh", self, StyleName="refresh") self.add(self.button) self.err = Label() self.add(self.err) self.update() def update(self): remote = server.AdminService() id = remote.getQueued(self) if id < 0: self.err.setText("oops: could not call getQueued") def onRemoteResponse(self, result, request_info): self.button.setEnabled(True) self.queued.clear() if not result: self.queued.resize(1, 1) self.queued.setText(0, 0, "No users are queued for addition.") else: self.queued.resize(len(result) + 1, 4) self.queued.setText(0, 0, "Pos") self.queued.setText(0, 1, "Name") self.queued.setText(0, 2, "Friends") self.queued.setText(0, 3, "Queued at") row = 1 for name, nFriends, time in result: self.queued.setText(row, 0, row) self.queued.setText(row, 1, name) self.queued.setText(row, 2, nFriends) self.queued.setText(row, 3, time) row += 1 def onRemoteError(self, code, message, request_info): self.button.setEnabled(True) self.err.setText("Could not getQueued: " + message) def onClick(self, sender): self.err.setText("") self.button.setEnabled(False) self.update()
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)
class CompaniesAppGUI(AbsolutePanel): def __init__(self): AbsolutePanel.__init__(self) self.app = CompaniesApp() self.history = [] self.save = Button("save", self) self.selectDepartment = Button("select", self) self.selectEmployee = Button("select", self) self.edit = Button("edit", self) self.cut = Button("cut", self) self.back = Button("back", self) self.name = TextBox() self.address = TextBox() self.manager = TextBox() self.departments = ListBox(Size=("100%"), VisibleItemCount="5") self.employees = ListBox(Size=("100%"), VisibleItemCount="5") self.total = TextBox() self.errors = VerticalPanel() self.grid = Grid() self.allPanels = VerticalPanel() self.allPanels.add(self.grid) self.allPanels.add(self.errors) self.add(self.allPanels) self.initCompanyGUI() def onClick(self, sender): self.errors.clear() if sender == self.cut: self.current.cut() self.total.setText(self.current.total()) if sender == self.save: if self.current.__class__.__name__ == "Employee": if self.validateEmployee(self.current.id, self.name.getText(), self.address.getText(), self.total.getText()) == True: self.current.save(self.name.getText(), self.address.getText(), float(self.total.getText())) else: if self.validateDepartment(self.current.id, self.name.getText()) == True: self.current.save(self.name.getText()) if sender == self.selectDepartment: if (self.departments.getSelectedIndex() > -1): self.history.append(self.current) self.current = self.app.getDepartment(self.departments.getValue(self.departments.getSelectedIndex())) self.initDepartmentGUI() if sender == self.selectEmployee: if (self.employees.getSelectedIndex() > -1): self.history.append(self.current) self.current = self.app.getEmployee(self.employees.getValue(self.employees.getSelectedIndex())) self.initEmployeeGUI() if sender == self.edit: self.history.append(self.current) self.current = self.current.getManager() self.initEmployeeGUI() if sender == self.back: if len(self.history) > 0: self.current = self.history.pop() if self.current.__class__.__name__ == "Company": self.initCompanyGUI() else: self.initDepartmentGUI() def validateDepartment(self, index, name): valid = True if name == "": self.errors.add(Label("- Enter a valid name, please.")) valid = False for item in self.app.departments: if item.id != index and name == item.name: self.errors.add(Label("- There is already a department with the same name. Enter a valid name, please.")) valid = False return valid def validateEmployee(self, index, name, address, salary): valid = True if name == "": self.errors.add(Label("- Enter a valid name, please.")) valid = False if address == "": self.errors.add(Label("- Enter a valid address, please.")) valid = False if salary == "": self.errors.add(Label("- Enter a valid salary, please.")) valid = False try: float(salary) except ValueError: self.errors.add(Label("- The salary must be a number. Enter a valid salary, please.")) valid = False for item in self.app.employees: if item.id != index and name == item.name and item.address == address: self.errors.add(Label("- There is already an employee with the same name and address combination. Enter a valid name and address, please.")) valid = False return valid def initCompanyGUI(self): self.current = self.app.company self.grid.clear() self.grid.resize(4, 3) # row 1 self.grid.setWidget(0, 0, Label("Name:")) self.grid.setWidget(1, 0, Label("Department:")) self.grid.setWidget(2, 0, Label("Total:")) # row 2 self.grid.setWidget(0, 1, self.name) self.grid.setWidget(1, 1, self.departments) self.grid.setWidget(2, 1, self.total) # row 3 self.grid.setWidget(0, 2, self.save) self.grid.setWidget(1, 2, self.selectDepartment) self.grid.setWidget(2, 2, self.cut) self.name.setText(self.current.name) self.departments.clear() for item in self.current.departments: self.departments.addItem(item.name, item.id) if self.departments.getItemCount() > 0: self.departments.setSelectedIndex(0) self.total.setText(self.current.total()) def initDepartmentGUI(self): self.grid.clear() self.grid.resize(6, 3) # row 1 self.grid.setWidget(0, 0, Label("Name:")) self.grid.setWidget(1, 0, Label("Manager:")) self.grid.setWidget(2, 0, Label("Department:")) self.grid.setWidget(3, 0, Label("Employee:")) self.grid.setWidget(4, 0, Label("Total:")) # row 2 self.grid.setWidget(0, 1, self.name) self.grid.setWidget(1, 1, self.manager) self.grid.setWidget(2, 1, self.departments) self.grid.setWidget(3, 1, self.employees) self.grid.setWidget(4, 1, self.total) # row 3 self.grid.setWidget(0, 2, self.save) self.grid.setWidget(1, 2, self.edit) self.grid.setWidget(2, 2, self.selectDepartment) self.grid.setWidget(3, 2, self.selectEmployee) self.grid.setWidget(4, 2, self.cut) # back self.grid.setWidget(5, 2, self.back) self.name.setText(self.current.name) self.departments.clear() self.employees.clear() for item in self.current.departments: self.departments.addItem(item.name, item.id) if self.departments.getItemCount() > 0: self.departments.setSelectedIndex(0) for item in self.current.employees: if item.manager == 0: self.employees.addItem(item.name, item.id) else: self.manager.setText(item.name) if self.employees.getItemCount() > 0: self.employees.setSelectedIndex(0) self.total.setText(self.current.total()) def initEmployeeGUI(self): self.grid.clear() self.grid.resize(4, 3) # row 1 self.grid.setWidget(0, 0, Label("Name:")) self.grid.setWidget(1, 0, Label("Address:")) self.grid.setWidget(2, 0, Label("Salary:")) # row 2 self.grid.setWidget(0, 1, self.name) self.grid.setWidget(1, 1, self.address) self.grid.setWidget(2, 1, self.total) # row 3 self.grid.setWidget(0, 2, self.save) self.grid.setWidget(2, 2, self.cut) self.grid.setWidget(3, 2, self.back) self.name.setText(self.current.name) self.address.setText(self.current.address) self.total.setText(self.current.salary)