class UsersTab(RPCBase): def __init__(self,app): global gw self.app=app self.vpanel = VerticalPanel() self.vpanel.setWidth("100%") self.table = FlexTable() self.table.setHTML(0, 0, "<b>ID</b>") self.table.setHTML(0, 1, "<b>Handle</b>") self.table.setHTML(0, 2, "<b>Name</b>") self.vpanel.add(self.table) if self.app.admin: self.btn = Button("Create User",self.createUser) self.vpanel.add(self.btn) self.cnt = 1 gw.listUsers(self) def reload(self): global gw gw.listUsers(self) def createUser(self,_): UserTab(self.app, -1) def userLambda(self,uid): return lambda: UserTab(self.app, uid) def getRoot(self): return self.vpanel def onRemoteResponse(self, response, request_info): while self.cnt > 1: self.cnt -= 1 self.table.removeRow(self.cnt) for line in response: l=Hyperlink(line[0]) l.addClickListener(self.userLambda(line[0])); self.table.setWidget(self.cnt, 0, l) l=Hyperlink(line[1]) l.addClickListener(self.userLambda(line[0])); self.table.setWidget(self.cnt, 1, l) l=Hyperlink(line[2]) l.addClickListener(self.userLambda(line[0])); self.table.setWidget(self.cnt, 2, l) self.cnt += 1
class StockWatcher: def onModuleLoad(self): ''' This is the main entry point method. ''' # Setup JSON RPC self.remote = DataService() # Initialize member variables self.mainPanel = VerticalPanel() self.stocksFlexTable = FlexTable() self.addPanel = HorizontalPanel() self.newSymbolTextBox = TextBox() self.lastUpdatedLabel = Label() self.addStockButton = Button('Add', self.addStock) self.stocks = [] self.stocksTableColumns = ['Symbol', 'Price', 'Change', 'Remove'] # Add styles to elements in the stock list table self.stocksFlexTable.getRowFormatter().addStyleName(0, 'watchListHeader') self.stocksFlexTable.addStyleName('watchList') self.stocksFlexTable.getCellFormatter().addStyleName(0, 1, 'watchListNumericColumn') self.stocksFlexTable.getCellFormatter().addStyleName(0, 2, 'watchListNumericColumn') self.stocksFlexTable.getCellFormatter().addStyleName(0, 3, 'watchListRemoveColumn') # Create table for stock data for i in range(len(self.stocksTableColumns)): self.stocksFlexTable.setText(0, i, self.stocksTableColumns[i]) # Assemble Add Stock panel self.addPanel.add(self.newSymbolTextBox) self.addPanel.add(self.addStockButton) self.addPanel.addStyleName('addPanel') # Assemble Main panel self.mainPanel.add(self.stocksFlexTable) self.mainPanel.add(self.addPanel) self.mainPanel.add(self.lastUpdatedLabel) # Associate the Main panel with the HTML host page RootPanel().add(self.mainPanel) # Move cursor focus to the input box self.newSymbolTextBox.setFocus(True) # Setup timer to refresh list automatically refresh = self.refreshWatchlist class MyTimer(Timer): def run(self): refresh() refreshTimer = MyTimer() refreshTimer.scheduleRepeating(5000) # Listen for keyboard events in the input box self_addStock = self.addStock class StockTextBox_KeyboardHandler(): def onKeyPress(self, sender, keycode, modifiers): if keycode == KEY_ENTER: self_addStock() def onKeyDown(self, sender, keycode, modifiers): return def onKeyUp(self, sender, keycode, modifiers): return self.newSymbolTextBox.addKeyboardListener(StockTextBox_KeyboardHandler()) # Load the stocks self.remote.getStocks(self) def addStock(self, sender, symbol=None): ''' Add stock to FlexTable. Executed when the user clicks the addStockButton or presses enter in the newSymbolTextBox ''' if symbol is None: # Get the symbol symbol = self.newSymbolTextBox.getText().upper().trim() self.newSymbolTextBox.setText('') # Don't add the stock if it's already in the table if symbol in self.stocks: return # Tell the server that we're adding this stock self.remote.addStock(symbol, self) self.newSymbolTextBox.setFocus(True) # Stocks code must be between 1 and 10 chars that are numbers/letters/dots p = re.compile('^[0-9A-Z\\.]{1,10}$') if p.match(symbol) == None: Window.alert('"%s" is not a valid symbol.' % symbol) self.newSymbolTextBox.selectAll() return # Add the stock to the table row = self.stocksFlexTable.getRowCount() self.stocks.append(symbol) self.stocksFlexTable.setText(row, 0, symbol) self.stocksFlexTable.setWidget(row, 2, Label()) self.stocksFlexTable.getCellFormatter().addStyleName(row, 1, 'watchListNumericColumn') self.stocksFlexTable.getCellFormatter().addStyleName(row, 2, 'watchListNumericColumn') self.stocksFlexTable.getCellFormatter().addStyleName(row, 3, 'watchListRemoveColumn') # Add a button to remove this stock from the table def _removeStockButton_Click(event): if symbol not in self.stocks: return removedIndex = self.stocks.index(symbol) self.remote.deleteStock(symbol, self) self.stocks.remove(symbol) self.stocksFlexTable.removeRow(removedIndex + 1) removeStockButton = Button('x', _removeStockButton_Click) removeStockButton.addStyleDependentName('remove') self.stocksFlexTable.setWidget(row, 3, removeStockButton) # Get the stock price self.refreshWatchlist() def refreshWatchlist(self): ''' Update the price change for each stock ''' MAX_PRICE = 100.0 MAX_PRICE_CHANGE = 0.02 prices = [] for i in range(len(self.stocks)): price = random.random() * MAX_PRICE change = price * MAX_PRICE_CHANGE * (random.random() * 2.0 - 1.0) prices.append(StockPrice(self.stocks[i], price, change)) self.updateTable(prices) def updateTable(self, prices): ''' Update the price and change fields of all the rows in the stock table prices -- List of StockPrice objects for all rows ''' # Type checking assert isinstance(prices, list) for price in prices: assert isinstance(price, StockPrice) # Nothing to do... if len(prices) == 0: return # Update each individual row for i in range(len(prices)): self.updateRow(prices[i]) # Display timestamp showing last refresh self.lastUpdatedLabel.setText("Last update: %s" % datetime.datetime.now().strftime("%m/%d/%Y %I:%M:%S %p")) def updateRow(self, price): ''' Update a single row in the stock table price -- StockPrice object for a single row ''' # Type checking assert isinstance(price, StockPrice) # Make sure the stock is still in the stock table if price.symbol not in self.stocks: return # Find the index of row = self.stocks.index(price.symbol) + 1 # Populate the price and change fields with new data self.stocksFlexTable.setText(row, 1, '%.2f' % price.price) changeWidget = self.stocksFlexTable.getWidget(row, 2) changeWidget.setText('%.2f (%.2f%%)' % (price.change, price.getChangePercent())) # Change the color of the text in the Change field based on its value changeStyleName = 'noChange' if price.getChangePercent() < -0.1: changeStyleName = 'negativeChange' else: changeStyleName = 'positiveChange' changeWidget.setStyleName(changeStyleName) def onRemoteResponse(self, response, request_info): ''' Called when a response is received from a RPC. ''' if request_info.method in DataService.methods: # Compare self.stocks and the stocks in response stocks_set = set(self.stocks) response_set = set(response) # Add the differences for symbol in list(response_set.difference(stocks_set)): self.addStock(None, symbol) else: Window.alert('Unrecognized JSONRPC method.') def onRemoteError(self, code, message, request_info): Window.alert(message)
class AlarmWidget(object): weekday_name = { 0: 'Mo', 1: 'Di', 2: 'Mi', 3: 'Do', 4: 'Fr', 5: 'Sa', 6: 'So' } def __init__(self): self.alarms = Alarms(self) self.make_table() self.fill_table() self.status = Label() self.panel = self.make_panel() def make_panel(self): message = Label( 'The configuration has been changed.\n' 'You must apply the changes in order for them to take effect.') DOM.setStyleAttribute(message.getElement(), "whiteSpace", 'pre') msgbox = Grid(1, 2, StyleName='changes') msgbox.setWidget(0, 0, Image('icons/exclam.png')) msgbox.setWidget(0, 1, message) msgbox.getCellFormatter().setStyleName(0, 0, 'changes-image') msgbox.getCellFormatter().setStyleName(0, 1, 'changes-text') button = Button('apply changes') button.addClickListener(self.apply_clicked) self.changes = VerticalPanel() self.changes.setHorizontalAlignment('right') self.changes.setVisible(False) self.changes.add(msgbox) self.changes.add(button) panel = VerticalPanel() panel.setSpacing(10) panel.add(self.table) panel.add(self.status) panel.add(self.changes) return panel def make_table(self): self.table = FlexTable(StyleName='alarms') self.table.setBorderWidth(1) self.make_header() self.make_footer() def make_header(self): headers = [ 'Time', 'Days', 'Duration' ] for col, text in enumerate(headers): self.table.setText(0, col, text) self.table.getCellFormatter().setStyleName(0, col, 'tablecell header') def make_footer(self): self.time = {} self.time['hour'] = ListBox() self.time['hour'].setVisibleItemCount(1) for hour in range(24): self.time['hour'].addItem('%02d' % hour) self.time['hour'].setSelectedIndex(12) self.time['minute'] = ListBox() self.time['minute'].setVisibleItemCount(1) for minute in range(0, 60, 5): self.time['minute'].addItem('%02d' % minute) self.time['minute'].setSelectedIndex(0) time_panel = HorizontalPanel() time_panel.setVerticalAlignment('center') time_panel.add(self.time['hour']) time_panel.add(Label(':')) time_panel.add(self.time['minute']) self.table.setWidget(1, 0, time_panel) weekdays_panel = HorizontalPanel() weekdays_panel.setSpacing(5) self.weekdays = {} for i in range(7): self.weekdays[i] = CheckBox(AlarmWidget.weekday_name[i]) self.weekdays[i].setChecked(i < 6) weekdays_panel.add(self.weekdays[i]) self.table.setWidget(1, 1, weekdays_panel) self.duration = ListBox() self.duration.setVisibleItemCount(1) choices = [ 1, 2, 3, 4, 5, 7, 10, 15, 20, 25, 30, 40, 50, 60 ] for seconds in choices: self.duration.addItem( '%ds' % seconds if seconds < 60 else '%dm' % (seconds / 60), seconds) self.duration.setSelectedIndex(2) self.table.setWidget(1, 2, self.duration) image = Image('icons/plus.png') image.setTitle('add'); image.addClickListener(self.plus_clicked) self.table.setWidget(1, 3, image) for col in range(4): self.table.getCellFormatter().setStyleName(1, col, 'tablecell noborder') def fill_table(self): for idx, alarm in enumerate(self.alarms.get()): self.add(alarm['time'], alarm['weekdays'], alarm['duration']) def add(self, time, weekdays=range(5), duration=3): row = self.table.getRowCount()-1 self.table.insertRow(row) self.table.setText(row, 0, time) weekdays_str = [] for weekday in weekdays: weekdays_str.append(AlarmWidget.weekday_name[weekday]) self.table.setText(row, 1, ', '.join(weekdays_str)) self.table.setText(row, 2, str(duration) + 's') image = Image('icons/x.png') image.setTitle('delete'); image.addClickListener(lambda x: self.x_clicked(row-1)) self.table.setWidget(row, 3, image) for col in range(3): self.table.getCellFormatter().setStyleName(row, col, 'tablecell') self.table.getCellFormatter().setStyleName(row, 3, 'tablecell noborder') def remove(self, idx): if idx >= 0 and idx < self.table.getRowCount()-2: #self.status.setText('removing idx: %d' % idx) self.table.removeRow(idx+1) else: #self.status.setText('tried to remove idx: %d' % idx) pass def plus_clicked(self): self.changes.setVisible(True) getSelectedValue = lambda widget: widget.getValue(widget.getSelectedIndex()) hour = getSelectedValue(self.time['hour']) minute = getSelectedValue(self.time['minute']) time = '%02d:%02d:%02d' % ( hour, minute, 0 ) weekdays = [ i for i in range(7) if self.weekdays[i].isChecked() ] duration = getSelectedValue(self.duration) self.add(time, weekdays, duration) self.alarms.add({'time': time, 'weekdays': weekdays, 'duration': duration}) def x_clicked(self, idx): self.changes.setVisible(True) self.remove(idx) #self.alarms.remove(idx) def apply_clicked(self): self.alarms.save() self.changes.setVisible(False)