Package muntjac :: Package addon :: Package invient :: Module invient_charts
[hide private]
[frames] | no frames]

Source Code for Module muntjac.addon.invient.invient_charts

   1  # @INVIENT_COPYRIGHT@ 
   2  #  
   3  # Licensed under the Apache License, Version 2.0 (the "License");  
   4  # you may not use this file except in compliance with the License.  
   5  # You may obtain a copy of the License at  
   6  #  
   7  #     http://www.apache.org/licenses/LICENSE-2.0  
   8  #  
   9  # Unless required by applicable law or agreed to in writing, software  
  10  # distributed under the License is distributed on an "AS IS" BASIS,  
  11  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  12  # See the License for the specific language governing permissions and  
  13  # limitations under the License. 
  14   
  15  try: 
  16      from collections import OrderedDict 
  17  except ImportError: 
  18      from ordereddict import OrderedDict 
  19   
  20  from muntjac.util \ 
  21      import OrderedSet 
  22   
  23  from datetime \ 
  24      import datetime 
  25   
  26  from muntjac.ui.abstract_component \ 
  27      import AbstractComponent 
  28   
  29  from muntjac.ui.component \ 
  30      import Event as ComponentEvent 
  31   
  32  from muntjac.addon.invient.invient_charts_config \ 
  33      import BaseLineConfig, PointConfig, SeriesConfig 
  34   
  35  from muntjac.addon.invient.invient_charts_util \ 
  36      import writeTitleConfig, writeSubtitleConfig, writeCreditConfig, \ 
  37      writeLegendConfig, writeTooltipConfig, writeGeneralChartConfig, \ 
  38      writeSeriesConfigPerSeriesType, writeXAxes, writeYAxes, \ 
  39      writeChartLabelConfig, writeSeries, writeChartDataUpdates, getDate 
40 41 42 -class InvientCharts(AbstractComponent):
43 """A Muntjac component representing charts. It is a the main class of 44 InvientCharts library. 45 46 A chart typically contains one or more series of same or different types. 47 This class allows us to specify series of different types say line and pie 48 and hence it makes it easy to build a combination chart. 49 50 After a chart L{InvientCharts} is created, the following changes to the 51 chart will be reflected rendered on the webkit. 52 53 * Set or update chart L{Title} and/or L{SubTitle} 54 * Modify chart size 55 * Add, update and remove one or more instances of L{PlotBand} and 56 L{PlotLine} 57 * Set or update axis categories 58 * Set or update axis min and max values 59 * Add, update and remove one or more instances of L{Series} 60 * Show or hide one or more instances of L{Series} 61 * Add and remove one or more instances of L{Point} 62 * Register and unregister event listeners 63 64 @author: Invient 65 @author: Richard Lincoln 66 """ 67 68 CLIENT_WIDGET = None #ClientWidget(VInvientCharts) 69 70 TYPE_MAPPING = 'com.invient.vaadin.charts.InvientCharts' 71
72 - def __init__(self, chartConfig):
73 """Creates this chart object with given chart configuration 74 75 @param chartConfig 76 """ 77 if chartConfig is None: 78 raise ValueError('The chart cannot be created without ' 79 'chartConfig argument.') 80 81 super(InvientCharts, self).__init__() 82 83 self._chartConfig = chartConfig 84 self._chartConfig.setInvientCharts(self) 85 86 self._isRetrieveSVG = False 87 self._isPrint = False 88 89 self._pointClickListeners = dict() 90 self._pointRemoveListeners = dict() 91 self._pointUnselectListeners = dict() 92 self._pointSelectListeners = dict() 93 self._seriesClickListeners = dict() 94 self._seriesHideListeners = dict() 95 self._seriesShowListeners = dict() 96 self._seriesLegendItemClickListeners = dict() 97 self._pieChartLegendItemClickListener = set() 98 self._chartClickListener = set() 99 self._chartAddSeriesListener = set() 100 self._chartZoomListener = set() 101 self._chartResetZoomListener = set() 102 self._svgAvailableListener = None 103 self._chartSeries = OrderedSet() 104 self._reloadChartSeries = False 105 self._seriesCURMap = OrderedDict()
106 107
108 - def getConfig(self):
109 """Returns chart configuration object 110 111 @return: Returns chart configuration object 112 """ 113 return self._chartConfig
114 115
116 - def paintContent(self, target):
117 super(InvientCharts, self).paintContent(target) 118 119 # Update all series with reference of x and y axis. 120 self.setAxisInAllSeriesIfNotSetAlready() 121 122 # Configurations options 123 target.startTag('options') 124 if self._chartConfig is not None: 125 writeTitleConfig(target, self._chartConfig.getTitle()) 126 writeSubtitleConfig(target, self._chartConfig.getSubtitle()) 127 writeCreditConfig(target, self._chartConfig.getCredit()) 128 writeLegendConfig(target, self._chartConfig.getLegend()) 129 writeTooltipConfig(target, self._chartConfig.getTooltip()) 130 writeGeneralChartConfig(target, 131 self._chartConfig.getGeneralChartConfig()) 132 writeSeriesConfigPerSeriesType(target, 133 self._chartConfig.getSeriesConfig()) 134 writeXAxes(target, self._chartConfig.getXAxes(), self._chartConfig) 135 writeYAxes(target, self._chartConfig.getYAxes(), self._chartConfig) 136 writeChartLabelConfig(target, self._chartConfig.getChartLabel()) 137 target.endTag('options') 138 target.startTag('chartData') 139 140 writeSeries(target, 141 self._chartConfig.getGeneralChartConfig().getType(), 142 self._chartSeries, 143 self._chartConfig.getXAxes(), 144 self._chartConfig.getYAxes()) 145 target.endTag('chartData') 146 147 # A flag to indicate whether to retrieve svg from client or not. 148 target.addAttribute('isRetrieveSVG', self._isRetrieveSVG) 149 150 # A flag to indicate whether to prompt print dialog on client or not 151 target.addAttribute('isPrint', self._isPrint) 152 153 # Events 154 target.startTag('events') 155 156 # Chart Events 157 self.paintChartEvents(target) 158 159 # Series/Point Events 160 self.paintSeriesAndPointEvents(target) 161 target.endTag('events') 162 163 # If the flag reloadChartData is true then the 164 # client will ignore seriesOperations and 165 # remove all existing series of a chart and 166 # add series info received from the server. 167 target.addAttribute('reloadChartSeries', self._reloadChartSeries) 168 target.startTag('chartDataUpdates') 169 if not self._reloadChartSeries: 170 writeChartDataUpdates(target, self._seriesCURMap) 171 target.endTag('chartDataUpdates') 172 173 # reset flag 174 self._reloadChartSeries = False 175 176 # reset series operations 177 self._seriesCURMap.clear() 178 179 # reset to not retrieve svg when other updates on the chart occurs. 180 # The svg is retrieved only when a svg available listener is registered 181 # on this chart 182 self._isRetrieveSVG = False 183 self._isPrint = False
184 185
186 - def paintChartEvents(self, target):
187 target.startTag('chartEvents') 188 189 if (self._chartAddSeriesListener is not None 190 and len(self._chartAddSeriesListener) > 0): 191 target.addAttribute('addSeries', True) 192 193 if (self._chartClickListener is not None 194 and len(self._chartClickListener) > 0): 195 target.addAttribute('click', True) 196 197 if (self._chartZoomListener is not None 198 and len(self._chartZoomListener) > 0): 199 target.addAttribute('selection', True) 200 201 target.endTag('chartEvents')
202 203
204 - def paintSeriesAndPointEvents(self, target):
205 target.startTag('seriesEvents') 206 # For each series type, check if listeners exist. If so then add. 207 for seriesType in SeriesType.values(): 208 self.paintSeriesEvents(target, seriesType) 209 target.endTag('seriesEvents')
210 211
212 - def paintSeriesEvents(self, target, seriesType):
213 tagName = seriesType.getName() 214 target.startTag(tagName) 215 216 if (seriesType in self._seriesClickListeners 217 and len(self._seriesClickListeners[seriesType]) > 0): 218 target.addAttribute('click', True) 219 220 if (seriesType in self._seriesHideListeners 221 and len(self._seriesHideListeners[seriesType]) > 0): 222 target.addAttribute('hide', True) 223 224 if (seriesType in self._seriesShowListeners 225 and len(self._seriesShowListeners[seriesType]) > 0): 226 target.addAttribute('show', True) 227 228 if (seriesType in self._seriesLegendItemClickListeners 229 and len(self._seriesLegendItemClickListeners[seriesType]) > 0): 230 target.addAttribute('legendItemClick', True) 231 232 # Check for point events 233 self.paintPointEvents(target, seriesType) 234 target.endTag(tagName)
235 236
237 - def paintPointEvents(self, target, seriesType):
238 target.startTag('pointEvents') 239 240 if (seriesType in self._pointClickListeners 241 and len(self._pointClickListeners[seriesType]) > 0): 242 target.addAttribute('click', True) 243 244 if (seriesType in self._pointRemoveListeners 245 and len(self._pointRemoveListeners[seriesType]) > 0): 246 target.addAttribute('remove', True) 247 248 if (seriesType in self._pointSelectListeners 249 and len(self._pointSelectListeners[seriesType]) > 0): 250 target.addAttribute('select', True) 251 252 if (seriesType in self._pointUnselectListeners 253 and len(self._pointUnselectListeners[seriesType]) > 0): 254 target.addAttribute('unselect', True) 255 256 # Event applicable only for pie chart 257 if (SeriesType.PIE == seriesType 258 and len(self._pieChartLegendItemClickListener) > 0): 259 target.addAttribute('legendItemClick', True) 260 261 target.endTag('pointEvents')
262 263
264 - def changeVariables(self, source, variables):
265 if 'event' in variables: 266 eventData = variables.get('eventData') 267 eventName = variables.get('event') 268 if eventName.lower() == "addSeries".lower(): 269 self.fireAddSeries() 270 elif eventName.lower() == "chartClick".lower(): 271 xAxisPos = float(eventData.get("xAxisPos")) 272 yAxisPos = float(eventData.get("yAxisPos")) 273 274 mousePosition = self.getClickPosition(eventData) 275 self.fireChartClick(DecimalPoint(self, xAxisPos, yAxisPos), 276 mousePosition) 277 elif eventName.lower() == "chartZoom".lower(): 278 xAxisMin = float(eventData.get("xAxisMin")) 279 xAxisMax = float(eventData.get("xAxisMax")) 280 yAxisMin = float(eventData.get("yAxisMin")) 281 yAxisMax = float(eventData.get("yAxisMax")) 282 self.fireChartZoom(ChartArea(xAxisMin, xAxisMax, yAxisMin, 283 yAxisMax)) 284 elif eventName.lower() == "chartResetZoom".lower(): 285 self.fireChartResetZoom() 286 elif eventName.lower() == "chartSVGAvailable".lower(): 287 self.fireChartSVGAvailable(eventData.get("svg")) 288 elif eventName.lower() == "seriesClick".lower(): 289 pointEventData = self.getPointEventData(eventData) 290 291 mousePosition = self.getClickPosition(eventData) 292 self.fireSeriesClick( 293 self.getSeriesFromEventData( 294 pointEventData.getSeriesName()), 295 self.getPointFromEventData(pointEventData), 296 mousePosition) 297 elif eventName.lower() == "seriesHide".lower(): 298 seriesName = eventData.get("seriesName") 299 self.fireSeriesHide(self.getSeriesFromEventData(seriesName)) 300 elif eventName.lower() == "seriesShow".lower(): 301 seriesName = eventData.get("seriesName") 302 self.fireSeriesShow(self.getSeriesFromEventData(seriesName)) 303 elif eventName.lower() == "seriesLegendItemClick".lower(): 304 seriesName = eventData.get("seriesName") 305 self.fireSeriesLegendItemClick( 306 self.getSeriesFromEventData(seriesName)) 307 elif eventName.lower() == "pieLegendItemClick".lower(): 308 pointEventData = self.getPointEventData(eventData) 309 self.fireLegendItemClick( 310 self.getPointFromEventData(pointEventData)) 311 elif eventName.lower() == "pointClick".lower(): 312 mousePosition = self.getClickPosition(eventData) 313 314 pointEventData = self.getPointEventData(eventData) 315 self.firePointClick(pointEventData.getCategory(), 316 self.getPointFromEventData(pointEventData), 317 mousePosition) 318 elif eventName.lower() == "pointSelect".lower(): 319 pointEventData = self.getPointEventData(eventData) 320 self.firePointSelect(pointEventData.getCategory(), 321 self.getPointFromEventData(pointEventData)) 322 elif eventName.lower() == "pointUnselect".lower(): 323 pointEventData = self.getPointEventData(eventData) 324 self.firePointUnselect(pointEventData.getCategory(), 325 self.getPointFromEventData(pointEventData)) 326 elif eventName.lower() == "pointRemove".lower(): 327 pointEventData = self.getPointEventData(eventData) 328 self.firePointRemove(pointEventData.getCategory(), 329 self.getPointFromEventData(pointEventData))
330 331
332 - def getPointFromEventData(self, eventData):
333 # First locate a series and then point 334 series = self.getSeriesFromEventData(eventData.getSeriesName()) 335 if series is not None: 336 if isinstance(series, XYSeries): 337 for point in series.getPoints(): 338 if (point.getY() is not None 339 and cmp(point.getY(), eventData.getPointY()) == 0 340 and point.getX() is not None 341 and cmp(point.getX(), eventData.getPointX()) == 0): 342 return point 343 else: 344 for point in series.getPoints(): 345 if (point.getY() is not None 346 and cmp(point.getY(), eventData.getPointY()) == 0 347 and point.getX() is not None 348 and getDate(point.getX(), 349 series.isIncludeTime()) == eventData.getPointX()): 350 return point 351 return None
352 353
354 - def getSeriesFromEventData(self, seriesName):
355 for series in self._chartSeries: 356 if series.getName() == seriesName: 357 return series 358 # should not happen 359 return None
360 361
362 - def fireAddSeries(self):
363 self.fireEvent(ChartAddSeriesEvent(self, self))
364 365
366 - def fireChartClick(self, point, mousePosition):
367 self.fireEvent(ChartClickEvent(self, self, point, mousePosition))
368 369
370 - def fireChartZoom(self, selectedArea):
371 self.fireEvent(ChartZoomEvent(self, self, selectedArea))
372 373
374 - def fireChartSVGAvailable(self, svg):
375 self.fireEvent(ChartSVGAvailableEvent(self, self, svg))
376 377
378 - def fireChartResetZoom(self):
379 self.fireEvent(ChartResetZoomEvent(self, self))
380 381
382 - def fireSeriesClick(self, series, point, mousePosition):
383 self.fireEvent(SeriesClickEvent(self, self, series, point, 384 mousePosition))
385 386
387 - def fireSeriesShow(self, series):
388 self.fireEvent(SeriesShowEvent(self, self, series))
389 390
391 - def fireSeriesHide(self, series):
392 self.fireEvent(SeriesHideEvent(self, self, series))
393 394
395 - def fireSeriesLegendItemClick(self, series):
396 self.fireEvent(SeriesLegendItemClickEvent(self, self, series))
397 398
399 - def firePointClick(self, category, point, mousePosition):
400 self.fireEvent(PointClickEvent(self, self, category, point, 401 mousePosition))
402 403
404 - def firePointSelect(self, category, point):
405 self.fireEvent(PointSelectEvent(self, self, category, point))
406 407
408 - def firePointUnselect(self, category, point):
409 self.fireEvent(PointUnselectEvent(self, self, category, point))
410 411
412 - def firePointRemove(self, category, point):
413 self.fireEvent(PointRemoveEvent(self, self, category, point))
414 415
416 - def fireLegendItemClick(self, point):
417 self.fireEvent(PieChartLegendItemClickEvent(self, self, point))
418 419
420 - def getPointEventData(self, eventData):
421 seriesName = eventData['seriesName'] 422 category = eventData['category'] 423 pointX = float(eventData['pointX']) 424 pointY = float(eventData['pointY']) 425 return PointEventData(seriesName, category, pointX, pointY)
426 427
428 - def getClickPosition(self, eventData):
429 mouseX = None 430 if isinstance(eventData['mouseX'], int): 431 mouseX = eventData['mouseX'] 432 mouseY = None 433 if isinstance(eventData['mouseY'], int): 434 mouseY = eventData['mouseY'] 435 if mouseX is not None and mouseY is not None: 436 return MousePosition(mouseX, mouseY) 437 return None
438 439
440 - def addListener(self, listener, seriesTypes=None):
441 """Adds the listener. If the argument seriesTypes is not specified 442 then the listener will be added for all series type otherwise it will 443 be added for a specific series type 444 445 @param listener: 446 the L{IListener} to be added. 447 """ 448 if seriesTypes is None: 449 if isinstance(listener, ChartAddSeriesListener): 450 self._chartAddSeriesListener.add(listener) 451 self.registerListener(ChartAddSeriesEvent, listener, 452 _CHART_ADD_SERIES_METHOD) 453 elif isinstance(listener, ChartClickListener): 454 self._chartClickListener.add(listener) 455 self.registerListener(ChartClickEvent, listener, 456 _CHART_CLICK_METHOD) 457 elif isinstance(listener, ChartResetZoomListener): 458 self._chartResetZoomListener.add(listener) 459 self.registerListener(ChartResetZoomEvent, listener, 460 _CHART_RESET_ZOOM_METHOD) 461 elif isinstance(listener, ChartSVGAvailableListener): 462 if (self._svgAvailableListener is not None 463 and self._svgAvailableListener != listener): 464 # remove earlier listener 465 self.removeListener(self._svgAvailableListener) 466 self._svgAvailableListener = listener 467 self.registerListener(ChartSVGAvailableEvent, 468 self._svgAvailableListener, 469 _CHART_SVG_AVAILABLE_METHOD) 470 self._isRetrieveSVG = True 471 self.requestRepaint() 472 elif isinstance(listener, ChartZoomListener): 473 self._chartZoomListener.add(listener) 474 self.registerListener(ChartZoomEvent, listener, 475 _CHART_ZOOM_METHOD) 476 elif isinstance(listener, PieChartLegendItemClickListener): 477 self._pieChartLegendItemClickListener.add(listener) 478 self.registerListener(PieChartLegendItemClickEvent, listener, 479 _LEGENDITEM_CLICK_METHOD) 480 else: 481 super(InvientCharts, self).addListener(listener, seriesTypes) 482 else: 483 if isinstance(listener, PointClickListener): 484 if len(seriesTypes) == 0: 485 seriesTypes = [SeriesType.COMMONSERIES] 486 for seriesType in seriesTypes: 487 if seriesType in self._pointClickListeners: 488 self._pointClickListeners[seriesType].add(listener) 489 else: 490 listeners = set() 491 listeners.add(listener) 492 self._pointClickListeners[seriesType] = listeners 493 self.registerListener(PointClickEvent, listener, 494 _POINT_CLICK_METHOD) 495 elif isinstance(listener, PointRemoveListener): 496 if len(seriesTypes) == 0: 497 seriesTypes = [SeriesType.COMMONSERIES] 498 for seriesType in seriesTypes: 499 if seriesType in self._pointRemoveListeners: 500 self._pointRemoveListeners[seriesType].add(listener) 501 else: 502 listeners = set() 503 listeners.add(listener) 504 self._pointRemoveListeners[seriesType] = listeners 505 self.registerListener(PointRemoveEvent, listener, 506 _POINT_REMOVE_METHOD) 507 elif isinstance(listener, PointSelectListener): 508 if len(seriesTypes) == 0: 509 seriesTypes = [SeriesType.COMMONSERIES] 510 for seriesType in seriesTypes: 511 if seriesType in self._pointSelectListeners: 512 self._pointSelectListeners[seriesType].add(listener) 513 else: 514 listeners = set() 515 listeners.add(listener) 516 self._pointSelectListeners[seriesType] = listeners 517 self.registerListener(PointSelectEvent, listener, 518 _POINT_SELECT_METHOD) 519 elif isinstance(listener, PointUnselectListener): 520 if len(seriesTypes) == 0: 521 seriesTypes = [SeriesType.COMMONSERIES] 522 for seriesType in seriesTypes: 523 if seriesType in self._pointUnselectListeners: 524 self._pointUnselectListeners[seriesType].add(listener) 525 else: 526 listeners = set() 527 listeners.add(listener) 528 self._pointUnselectListeners[seriesType] = listeners 529 self.registerListener(PointUnselectEvent, listener, 530 _POINT_UNSELECT_METHOD) 531 elif isinstance(listener, SeriesClickListerner): 532 if len(seriesTypes) == 0: 533 seriesTypes = [SeriesType.COMMONSERIES] 534 for seriesType in seriesTypes: 535 if seriesType in self._seriesClickListeners: 536 self._seriesClickListeners[seriesType].add(listener) 537 else: 538 listeners = set() 539 listeners.add(listener) 540 self._seriesClickListeners[seriesType] = listeners 541 self.registerListener(SeriesClickEvent, listener, 542 _SERIES_CLICK_METHOD) 543 elif isinstance(listener, SeriesHideListerner): 544 if len(seriesTypes) == 0: 545 seriesTypes = [SeriesType.COMMONSERIES] 546 for seriesType in seriesTypes: 547 if seriesType in self._seriesHideListeners: 548 self._seriesHideListeners[seriesType].add(listener) 549 else: 550 listeners = set() 551 listeners.add(listener) 552 self._seriesHideListeners[seriesType] = listeners 553 self.registerListener(SeriesHideEvent, listener, 554 _SERIES_HIDE_METHOD) 555 elif isinstance(listener, SeriesLegendItemClickListerner): 556 if len(seriesTypes) == 0: 557 seriesTypes = [SeriesType.COMMONSERIES] 558 for seriesType in seriesTypes: 559 if seriesType in self._seriesLegendItemClickListeners: 560 self._seriesLegendItemClickListeners[seriesType].add( 561 listener) 562 else: 563 listeners = set() 564 listeners.add(listener) 565 self._seriesLegendItemClickListeners[seriesType] = \ 566 listeners 567 self.registerListener(SeriesLegendItemClickEvent, listener, 568 _SERIES_LEGENDITEM_CLICK_METHOD) 569 elif isinstance(listener, SeriesShowListerner): 570 if len(seriesTypes) == 0: 571 seriesTypes = [SeriesType.COMMONSERIES] 572 for seriesType in seriesTypes: 573 if seriesType in self._seriesShowListeners: 574 self._seriesShowListeners[seriesType].add(listener) 575 else: 576 listeners = set() 577 listeners.add(listener) 578 self._seriesShowListeners[seriesType] = listeners 579 self.registerListener(SeriesShowEvent, listener, 580 _SERIES_SHOW_METHOD) 581 else: 582 iface = seriesTypes 583 super(InvientCharts, self).addListener(listener, iface)
584 585
586 - def removeListener(self, listener, seriesTypes=None):
587 """Removes the listener. If the argument seriesTypes is not specified 588 then the listener will be removed only for a series type 589 SeriesType.COMMONSERIES otherwise the listener will be removed for all 590 specified series types. 591 592 @param listener: 593 the listener to be removed 594 @param seriesTypes: 595 one or more series types as defined by L{SeriesType} 596 """ 597 if seriesTypes is None: 598 if isinstance(listener, ChartAddSeriesListener): 599 self._chartAddSeriesListener.remove(listener) 600 self.withdrawListener(ChartAddSeriesEvent, listener, 601 _CHART_ADD_SERIES_METHOD) 602 elif isinstance(listener, ChartClickListener): 603 self._chartClickListener.remove(listener) 604 self.withdrawListener(ChartClickEvent, listener, 605 _CHART_CLICK_METHOD) 606 elif isinstance(listener, ChartResetZoomListener): 607 self._chartResetZoomListener.remove(listener) 608 self.withdrawListener(ChartResetZoomEvent, listener, 609 _CHART_RESET_ZOOM_METHOD) 610 elif isinstance(listener, ChartSVGAvailableListener): 611 if self._svgAvailableListener == listener: 612 self.withdrawListener(ChartSVGAvailableEvent, listener, 613 _CHART_SVG_AVAILABLE_METHOD) 614 self._isRetrieveSVG = False 615 self._svgAvailableListener = None 616 elif isinstance(listener, ChartZoomListener): 617 self._chartZoomListener.remove(listener) 618 self.withdrawListener(ChartZoomEvent, listener, 619 _CHART_ZOOM_METHOD) 620 elif isinstance(listener, PieChartLegendItemClickListener): 621 self._pieChartLegendItemClickListener.remove(listener) 622 self.withdrawListener(PieChartLegendItemClickEvent, listener, 623 _LEGENDITEM_CLICK_METHOD) 624 else: 625 super(InvientCharts, self).removeListener(listener, seriesTypes) 626 else: 627 if isinstance(listener, PointClickListener): 628 if len(seriesTypes) == 0: 629 seriesTypes = [SeriesType.COMMONSERIES] 630 for seriesType in seriesTypes: 631 if seriesType in self._pointClickListeners: 632 self._pointClickListeners[seriesType].remove(listener) 633 self.withdrawListener(PointClickEvent, listener, 634 _POINT_CLICK_METHOD) 635 elif isinstance(listener, PointRemoveListener): 636 if len(seriesTypes) == 0: 637 seriesTypes = [SeriesType.COMMONSERIES] 638 for seriesType in seriesTypes: 639 if seriesType in self._pointRemoveListeners: 640 self._pointRemoveListeners[seriesType].remove(listener) 641 self._pointRemoveListeners.remove(listener) 642 self.withdrawListener(PointRemoveEvent, listener, 643 _POINT_REMOVE_METHOD) 644 elif isinstance(listener, PointSelectListener): 645 if len(seriesTypes) == 0: 646 seriesTypes = [SeriesType.COMMONSERIES] 647 for seriesType in seriesTypes: 648 if seriesType in self._pointSelectListeners: 649 self._pointSelectListeners[seriesType].remove(listener) 650 self.withdrawListener(PointSelectEvent, listener, 651 _POINT_SELECT_METHOD) 652 elif isinstance(listener, PointUnselectListener): 653 if len(seriesTypes) == 0: 654 seriesTypes = [SeriesType.COMMONSERIES] 655 for seriesType in seriesTypes: 656 if seriesType in self._pointUnselectListeners: 657 self._pointUnselectListeners[seriesType].remove( 658 listener) 659 self.withdrawListener(PointUnselectEvent, listener, 660 _POINT_UNSELECT_METHOD) 661 elif isinstance(listener, SeriesClickListerner): 662 if len(seriesTypes) == 0: 663 seriesTypes = [SeriesType.COMMONSERIES] 664 for seriesType in seriesTypes: 665 if seriesType in self._seriesClickListeners: 666 self._seriesClickListeners[seriesType].remove(listener) 667 self.withdrawListener(SeriesClickEvent, listener, 668 _SERIES_CLICK_METHOD) 669 elif isinstance(listener, SeriesHideListerner): 670 if len(seriesTypes) == 0: 671 seriesTypes = [SeriesType.COMMONSERIES] 672 for seriesType in seriesTypes: 673 if seriesType in self._seriesHideListeners: 674 self._seriesHideListeners[seriesType].remove(listener) 675 self.withdrawListener(SeriesHideEvent, listener, 676 _SERIES_HIDE_METHOD) 677 elif isinstance(listener, SeriesLegendItemClickListerner): 678 if len(seriesTypes) == 0: 679 seriesTypes = [SeriesType.COMMONSERIES] 680 for seriesType in seriesTypes: 681 if seriesType in self._seriesLegendItemClickListeners: 682 self._seriesLegendItemClickListeners[ 683 seriesType].remove(listener) 684 self.withdrawListener(SeriesLegendItemClickEvent, listener, 685 _SERIES_LEGENDITEM_CLICK_METHOD) 686 elif isinstance(listener, SeriesShowListerner): 687 if len(seriesTypes) == 0: 688 seriesTypes = [SeriesType.COMMONSERIES] 689 for seriesType in seriesTypes: 690 if seriesType in self._seriesShowListeners: 691 self._seriesShowListeners[seriesType].remove(listener) 692 self.withdrawListener(SeriesShowEvent, listener, 693 _SERIES_SHOW_METHOD) 694 else: 695 iface = seriesTypes 696 super(InvientCharts, self).removeListener(listener, iface)
697 698
699 - def setSeries(self, series):
700 """The data of a chart is defined in terms of L{Series}. This method 701 removes all previously set series of this chart and adds the argument 702 series. If the argument series is null then no actions are taken. 703 704 @param series: 705 A collection of series to set as chart's data 706 """ 707 if series is not None: 708 self._reloadChartSeries = True 709 self._chartSeries.clear() 710 self._seriesCURMap.clear() 711 for seriesData in series: 712 self.addSeries(seriesData)
713 714
715 - def getSeries(self, name):
716 """Returns a series whose name matches the argument name. 717 718 @param name: 719 the name of the series 720 @return: Returns a series with the given name 721 """ 722 for series in self._chartSeries: 723 if series.getName() == name: 724 return series 725 return None
726 727
728 - def getAllSeries(self):
729 """Returns all series associated with this chart. 730 @return: returns all series associated with this chart. 731 """ 732 return self._chartSeries
733 734
735 - def addSeries(self, seriesData):
736 """Adds the argument series to this chart. 737 738 @param seriesData: 739 the series to be added 740 """ 741 # Before sending data to the client, this method sets 742 # axis in all series associated with the chart 743 if self._chartSeries.add(seriesData): 744 self.setAxisInSeriesIfNotSetAlready(seriesData) 745 seriesData.setInvientCharts(self) 746 self.addSeriesCUROperation(SeriesCUR(SeriesCURType.ADD, 747 seriesData.getName())) 748 self.requestRepaint()
749 750
752 for series in self._chartSeries: 753 self.setAxisInSeriesIfNotSetAlready(series)
754 755
756 - def setAxisInSeriesIfNotSetAlready(self, series):
757 if self.getConfig() is not None: 758 if (series.getXAxis() is None 759 and self.getConfig().getXAxes() is not None 760 and len(self.getConfig().getXAxes()) > 0): 761 series.setXAxis(iter(self.getConfig().getXAxes()).next()) # FIXME: iterator 762 if (series.getYAxis() is None 763 and self.getConfig().getYAxes() is not None 764 and len(self.getConfig().getYAxes()) > 0): 765 series.setYAxis(iter(self.getConfig().getYAxes()).next()) # FIXME: iterator
766 767
768 - def removeSeries(self, name_or_seriesData):
769 """Removes a series whose name matches the argument name or the 770 argument seriesData from this chart. 771 772 @param seriesData: 773 the name of the series or the series object to be removed 774 """ 775 if isinstance(name_or_seriesData, Series): 776 seriesData = name_or_seriesData 777 if self._chartSeries.remove(seriesData): 778 seriesData.setInvientCharts(None) 779 self.addSeriesCUROperation(SeriesCUR(SeriesCURType.REMOVE, 780 seriesData.getName())) 781 self.requestRepaint() 782 else: 783 name = name_or_seriesData 784 785 seriesItr = self._chartSeries.copy() 786 for series in seriesItr: 787 if series.getName() == name: 788 self._chartSeries.remove(series) 789 series.setInvientCharts(None) 790 self.addSeriesCUROperation(SeriesCUR(SeriesCURType.REMOVE, 791 series.getName())) 792 self.requestRepaint()
793 794
795 - def addSeriesCUROperation(self, newSeriesCUR):
796 if newSeriesCUR.getName() in self._seriesCURMap: 797 seriesCURSet = self._seriesCURMap.get(newSeriesCUR.getName()) 798 799 # If for a series, no operation is found 800 if (seriesCURSet is None) or (len(seriesCURSet) == 0): 801 seriesCURSet = OrderedSet() 802 seriesCURSet.add(newSeriesCUR) 803 self._seriesCURMap[newSeriesCUR.getName()] = seriesCURSet 804 elif seriesCURSet.contains(newSeriesCUR): 805 seriesCUR = self.getMatchedSeriesCUR(seriesCURSet, 806 newSeriesCUR) 807 808 # In case of series update (due to series.show/hide or 809 # series.setPoints or series.removeAllPoints) 810 # we need to check if all points of a series are set afresh. 811 # If so then set a flag to indicate that instead of adding and 812 # removing points to and from series, set series data in full. 813 if seriesCUR.getType() == SeriesCURType.UPDATE: 814 seriesCUR.setReloadPoints(newSeriesCUR.isReloadPoints()) 815 if newSeriesCUR.isReloadPoints(): 816 seriesCUR.clearTrackedPoints() 817 return True 818 # Operation on a series has already been recorded 819 return False 820 else: 821 seriesCURItr = seriesCURSet.copy() 822 for seriesCUR in seriesCURItr: 823 if seriesCUR.getName() == newSeriesCUR.getName(): 824 if (SeriesCURType.REMOVE == newSeriesCUR.getType() 825 and SeriesCURType.ADD == seriesCUR.getType()): 826 # Remove addition of a series as there is no reason 827 # to add a series and then remove it. E.g. If a new 828 # series is added and then removed then actually 829 # there is nothing to be done 830 seriesCURSet.remove(seriesCUR) 831 return False 832 if (SeriesCURType.UPDATE == newSeriesCUR.getType() 833 and SeriesCURType.ADD == seriesCUR.getType()): 834 # There is no need for update as adding a series 835 # will take care of applying any update to the 836 # series attributes specifically visibility 837 return False 838 if (SeriesCURType.REMOVE == newSeriesCUR.getType() 839 and SeriesCURType.UPDATE == seriesCUR.getType()): 840 # Remove update of a series as there is no reason 841 # to update a series and then remove it. E.g. If 842 # an existing series was updated (for show/hide) 843 # and then removed then series need not be updated 844 # after all it is going to be removed. Hover, the 845 # remove operation must be captured. 846 seriesCURSet.remove(seriesCUR) 847 break 848 seriesCURSet.add(newSeriesCUR) 849 return True 850 else: 851 seriesCURSet = OrderedSet() 852 seriesCURSet.add(newSeriesCUR) 853 self._seriesCURMap[newSeriesCUR.getName()] = seriesCURSet 854 return True
855 856
857 - def addSeriesPointAddedOperation(self, seriesName, point):
858 seriesCURSet = self._seriesCURMap.get(seriesName) 859 if (seriesCURSet is None) or (len(seriesCURSet) == 0): 860 seriesCUR = SeriesCUR(SeriesCURType.UPDATE, seriesName) 861 seriesCUR.trackPointAdded(point) 862 seriesCURSet = OrderedSet() 863 seriesCURSet.add(seriesCUR) 864 self._seriesCURMap[seriesName] = seriesCURSet 865 else: 866 lastSeriesCur = self.getLastSeriesCUR(seriesCURSet) 867 # Track points only if series is updated. 868 # Tracking point is useless in following cases 869 # 1. A new series is added : In this case, a series will be added 870 # with all points so no need to track 871 # 2. A series is removed : In this case, a series will be removed 872 # and hence any point added to the series doesn't carry any 873 # meaning. 874 if (lastSeriesCur.getType() == SeriesCURType.UPDATE 875 and not lastSeriesCur.isReloadPoints()): 876 lastSeriesCur.trackPointAdded(point)
877 878
879 - def getLastSeriesCUR(self, seriesCURSet):
880 if (seriesCURSet is None) or (len(seriesCURSet) == 0): 881 return None 882 lastSeriesCur = None 883 for seriesCur in seriesCURSet: 884 lastSeriesCur = seriesCur 885 return lastSeriesCur
886 887
888 - def getMatchedSeriesCUR(self, seriesCURSet, matchAgainstSeriesCUR):
889 for seriesCur in seriesCURSet: 890 if matchAgainstSeriesCUR == seriesCur: 891 return seriesCur 892 return None
893 894
895 - def addSeriesPointRemovedOperation(self, seriesName, point):
896 seriesCURSet = self._seriesCURMap.get(seriesName) 897 if (seriesCURSet is None) or (len(seriesCURSet) == 0): 898 seriesCUR = SeriesCUR(SeriesCURType.UPDATE, seriesName) 899 seriesCUR.trackPointRemoved(point) 900 seriesCURSet = OrderedSet() 901 seriesCURSet.add(seriesCUR) 902 self._seriesCURMap[seriesName] = seriesCURSet 903 else: 904 lastSeriesCur = self.getLastSeriesCUR(seriesCURSet) 905 # Track points only if series is updated. 906 # Tracking point is useless in following cases 907 # 1. A new series is added : In this case, a series will be added 908 # with all points so no need to track 909 # 2. A series is removed : In this case, a series will be removed 910 # and hence any point removed from the series 911 # doesn't carry any meaning. 912 if (lastSeriesCur.getType() == SeriesCURType.UPDATE 913 and not lastSeriesCur.isReloadPoints()): 914 lastSeriesCur.trackPointRemoved(point)
915 916
917 - def refresh(self):
918 """After a series is added or removed, there is no need to call this 919 method as it is handled implicitly. This method will send updates to 920 the client. This method should be called after adding/removing 921 plotbands and plotlines. This inconsistency will be fixed in next 922 revision. 923 """ 924 super(InvientCharts, self).requestRepaint()
925 926
927 - def print_(self):
928 """Displays a Print dialog of the Webkit to print this chart. Invoking 929 this method causes the Webkit to hide other widgets on the screen and 930 only this chart widget will be visible. Also it prints this chart 931 widget as it is displayed. 932 """ 933 self._isPrint = True 934 self.requestRepaint()
935
936 937 -class MousePosition(object):
938 """This class contain mouse coordinates when a click event occurs 939 on a chart, a series or a point. 940 941 The mouse coordinates are in pixels. 942 943 @author: Invient 944 @author: Richard Lincoln 945 """ 946
947 - def __init__(self, mouseX, mouseY):
948 """Creates this object with given arguments. 949 950 @param mouseX: 951 x position of mouse when a click event occurred, in pixel 952 @param mouseY: 953 y position of mouse when a click event occurred, in pixel 954 """ 955 self._mouseX = mouseX 956 self._mouseY = mouseY
957 958
959 - def getMouseX(self):
960 """@return: Returns x position of mouse when a click event occurred, 961 in pixel 962 """ 963 return self._mouseX
964 965
966 - def getMouseY(self):
967 """@return: Returns y position of mouse when a click event occurred, 968 in pixel 969 """ 970 return self._mouseY
971 972
973 - def __str__(self):
974 return ('MousePosition [mouseX=' + str(self._mouseX) 975 + ', mouseY=' + str(self._mouseY) + ']')
976
977 978 -class PointEventData(object):
979
980 - def __init__(self, seriesName, category, pointX, pointY):
981 self._seriesName = seriesName 982 self._category = category 983 self._pointX = pointX 984 self._pointY = pointY
985
986 - def getSeriesName(self):
987 return self._seriesName
988
989 - def getCategory(self):
990 return self._category
991
992 - def getPointX(self):
993 return self._pointX
994
995 - def getPointY(self):
996 return self._pointY
997
998 - def __str__(self):
999 return ('PointEventData [seriesName=' + self._seriesName 1000 + ', category=' + self._category 1001 + ', pointX=' + str(self._pointX) 1002 + ', pointY=' + str(self._pointY) + ']')
1003
1004 1005 -class PointClickEvent(ComponentEvent):
1006 """Click event. This event is thrown, when any point of this chart is 1007 clicked and the point marker is enabled. The point marker is enabled by 1008 default. 1009 1010 @author: Invient 1011 @author: Richard Lincoln 1012 """ 1013
1014 - def __init__(self, source, chart, category, point, mousePosition):
1015 """New instance of the point click event. 1016 1017 @param source: 1018 the chart object itself 1019 @param chart: 1020 the chart object itself 1021 @param category: 1022 a category to which point is associated in case of 1023 categorized axis, 1024 @param point: 1025 the point on which the click event occurred 1026 @param mousePosition: 1027 the position of a mouse when the click event occurred 1028 """ 1029 super(PointClickEvent, self).__init__(source) 1030 1031 self._chart = chart 1032 self._category = category 1033 self._point = point 1034 self._mousePosition = mousePosition
1035
1036 - def getCategory(self):
1037 """@return: Returns a category to which point is associated in case 1038 of categorized axis only. 1039 """ 1040 return self._category
1041
1042 - def getChart(self):
1043 """@return: Returns the chart object associated with the point""" 1044 return self._chart
1045
1046 - def getPoint(self):
1047 """@return: Returns the point on which the click event occurred""" 1048 return self._point
1049
1050 - def getMousePosition(self):
1051 """@return: Returns the position of a mouse when the click event 1052 occurred""" 1053 return self._mousePosition
1054
1055 1056 -class PointClickListener(object):
1057 """Interface for listening for a L{PointClickEvent} triggered by 1058 L{InvientCharts} 1059 1060 @author: Invient 1061 @author: Richard Lincoln 1062 """ 1063
1064 - def pointClick(self, pointClickEvent):
1065 raise NotImplementedError
1066
1067 1068 -class PointRemoveEvent(ComponentEvent):
1069 """Point remove event. This event is thrown, when any point of this chart 1070 is removed from its series. 1071 1072 This event is EXPERIMENTAL ONLY. 1073 1074 @author: Invient 1075 @author: Richard Lincoln 1076 """ 1077
1078 - def __init__(self, source, chart, category, point):
1079 """New instance of the point remove event. 1080 1081 @param source: 1082 the chart object itself 1083 @param chart: 1084 the chart object itself 1085 @param category: 1086 a category to which point is associated in case of 1087 categorized axis, 1088 @param point: 1089 the point removed 1090 """ 1091 super(PointRemoveEvent, self).__init__(source) 1092 1093 self._chart = chart 1094 self._category = category 1095 self._point = point
1096 1097
1098 - def getCategory(self):
1099 """@return: Returns a category to which point is associated in case 1100 of categorized axis only. 1101 """ 1102 return self._category
1103 1104
1105 - def getChart(self):
1106 """@return: Returns the chart object associated with the point""" 1107 return self._chart
1108 1109
1110 - def getPoint(self):
1111 """@return: Returns the point which has been removed""" 1112 return self._point
1113
1114 1115 -class PointRemoveListener(object):
1116 """Interface for listening for a L{PointRemoveEvent} triggered by 1117 L{InvientCharts} 1118 1119 @author: Invient 1120 @author: Richard Lincoln 1121 """ 1122
1123 - def pointRemove(self, pointRemoveEvent):
1124 raise NotImplementedError
1125
1126 1127 -class PointUnselectEvent(ComponentEvent):
1128 """Point unselect event. This event is thrown, when any point of this 1129 chart is unselected and the point marker is enabled. The point marker 1130 is enabled by default. 1131 1132 @author: Invient 1133 @author: Richard Lincoln 1134 """ 1135
1136 - def __init__(self, source, chart, category, point):
1137 """New instance of the point unselect event. 1138 1139 @param source: 1140 the chart object itself 1141 @param chart: 1142 the chart object itself 1143 @param category: 1144 a category to which point is associated in case of 1145 categorized axis, 1146 @param point: 1147 the point unselected as a result of this event 1148 """ 1149 super(PointUnselectEvent, self).__init__(source) 1150 1151 self._chart = chart 1152 self._category = category 1153 self._point = point
1154
1155 - def getCategory(self):
1156 """@return: Returns a category to which point is associated in case 1157 of categorized axis only. 1158 """ 1159 return self._category
1160
1161 - def getChart(self):
1162 """@return: Returns the chart object associated with the point""" 1163 return self._chart
1164
1165 - def getPoint(self):
1166 """@return: Returns the unselected point""" 1167 return self._point
1168
1169 1170 -class PointUnselectListener(object):
1171 """Interface for listening for a L{PointUnselectEvent} triggered by 1172 L{InvientCharts} 1173 1174 @author: Invient 1175 @author: Richard Lincoln 1176 """ 1177
1178 - def pointUnSelect(self, pointUnSelectEvent):
1179 raise NotImplementedError
1180
1181 1182 -class PointSelectEvent(ComponentEvent):
1183 """Point select event. This event is thrown, when any point of this chart 1184 is selected and the point marker is enabled. The point marker is enabled 1185 by default. 1186 1187 @author: Invient 1188 @author: Richard Lincoln 1189 """ 1190
1191 - def __init__(self, source, chart, category, point):
1192 """New instance of the point select event. 1193 1194 @param source: 1195 the chart object itself 1196 @param chart: 1197 the chart object itself 1198 @param category: 1199 a category to which point is associated in case of 1200 categorized axis, 1201 @param point: 1202 the point selected as a result of this event 1203 """ 1204 super(PointSelectEvent, self).__init__(source) 1205 1206 self._chart = chart 1207 self._category = category 1208 self._point = point
1209
1210 - def getCategory(self):
1211 """@return: Returns a category to which point is associated in case 1212 of categorized axis only. 1213 """ 1214 return self._category
1215
1216 - def getChart(self):
1217 """@return: Returns the chart object associated with the point""" 1218 return self._chart
1219
1220 - def getPoint(self):
1221 """@return: Returns the selected point""" 1222 return self._point
1223
1224 1225 -class PointSelectListener(object):
1226 """Interface for listening for a L{PointSelectListener} triggered by 1227 L{InvientCharts} 1228 1229 @author: Invient 1230 @author: Richard Lincoln 1231 """ 1232
1233 - def pointSelected(self, pointSelectEvent):
1234 raise NotImplementedError
1235 1236 1237 _POINT_CLICK_METHOD = getattr(PointClickListener, 'pointClick') 1238 _POINT_REMOVE_METHOD = getattr(PointRemoveListener, 'pointRemove') 1239 _POINT_SELECT_METHOD = getattr(PointSelectListener, 'pointSelected') 1240 _POINT_UNSELECT_METHOD = getattr(PointUnselectListener, 'pointUnSelect')
1241 1242 1243 1244 1245 -class SeriesClickEvent(ComponentEvent):
1246 """Series click event. This event is thrown, when any series of this 1247 chart is clicked. 1248 1249 @author: Invient 1250 @author: Richard Lincoln 1251 """ 1252
1253 - def __init__(self, source, chart, series, point, mousePosition):
1254 """New instance of the series click event. 1255 1256 @param source: 1257 the chart object itself 1258 @param chart: 1259 the chart object itself 1260 @param series: 1261 the series on which click event occurred 1262 @param point: 1263 the closest point of a series 1264 @param mousePosition: 1265 the position of a mouse when the click event occurred 1266 """ 1267 super(SeriesClickEvent, self).__init__(source) 1268 1269 self._chart = chart 1270 self._series = series 1271 self._point = point 1272 self._mousePosition = mousePosition
1273
1274 - def getChart(self):
1275 """@return: Returns the chart object associated with the point""" 1276 return self._chart
1277
1278 - def getSeries(self):
1279 """@return: Returns the series object on which the click event 1280 occurred""" 1281 return self._series
1282
1283 - def getNearestPoint(self):
1284 """@return: Returns the point of a series closest to the position 1285 where mouse click event occurred. 1286 """ 1287 return self._point
1288
1289 - def getMousePosition(self):
1290 """@return: Returns the position of a mouse when the click event 1291 occurred""" 1292 return self._mousePosition
1293
1294 1295 -class SeriesClickListerner(object):
1296 """Interface for listening for a L{SeriesClickListerner} triggered by 1297 L{InvientCharts} 1298 1299 @author: Invient 1300 @author: Richard Lincoln 1301 """ 1302
1303 - def seriesClick(self, seriesClickEvent):
1304 raise NotImplementedError
1305
1306 1307 -class SeriesHideEvent(ComponentEvent):
1308 """Series Hide event. This event is thrown, when any series of this chart 1309 is hidden. 1310 1311 @author: Invient 1312 @author: Richard Lincoln 1313 """ 1314
1315 - def __init__(self, source, chart, series):
1316 """@param source: 1317 the chart object itself 1318 @param chart: 1319 the chart object itself 1320 @param series: 1321 the series which got hidden 1322 """ 1323 super(SeriesHideEvent, self).__init__(source) 1324 self._chart = chart 1325 self._series = series
1326
1327 - def getChart(self):
1328 """@return: Returns the chart object associated with the point""" 1329 return self._chart
1330
1331 - def getSeries(self):
1332 """@return: Returns the series which got hidden""" 1333 return self._series
1334
1335 1336 -class SeriesHideListerner(object):
1337 """Interface for listening for a L{SeriesHideEvent} triggered by 1338 L{InvientCharts} 1339 1340 @author: Invient 1341 @author: Richard Lincoln 1342 """ 1343
1344 - def seriesHide(self, seriesHideEvent):
1345 raise NotImplementedError
1346
1347 1348 -class SeriesShowEvent(ComponentEvent):
1349 """Series show event. This event is thrown, when any series of this 1350 chart is displayed after a chart is created. 1351 1352 @author: Invient 1353 @author: Richard Lincoln 1354 """ 1355
1356 - def __init__(self, source, chart, series):
1357 """New instance of the series show event. 1358 1359 @param source: 1360 the chart object itself 1361 @param chart: 1362 the chart object itself 1363 @param series: 1364 the series which got displayed 1365 """ 1366 super(SeriesShowEvent, self).__init__(source) 1367 self._chart = chart 1368 self._series = series
1369
1370 - def getChart(self):
1371 """@return: Returns the chart object associated with the series""" 1372 return self._chart
1373
1374 - def getSeries(self):
1375 """@return: Returns the series which got displayed""" 1376 return self._series
1377
1378 1379 -class SeriesShowListerner(object):
1380 """Interface for listening for a L{SeriesShowEvent} triggered by 1381 L{InvientCharts} 1382 1383 @author: Invient 1384 @author: Richard Lincoln 1385 """ 1386
1387 - def seriesShow(self, seriesShowEvent):
1388 raise NotImplementedError
1389
1390 1391 -class SeriesLegendItemClickEvent(ComponentEvent):
1392 """Series legend item click event. This event is thrown, when legend item 1393 is clicked. This event is not applicable for PieChart instead use 1394 L{LegendItemClickEvent} 1395 1396 @author: Invient 1397 @author: Richard Lincoln 1398 """ 1399
1400 - def __init__(self, source, chart, series):
1401 """New instance of the point click event. 1402 1403 @param source: 1404 the chart object itself 1405 @param chart: 1406 the chart object itself 1407 @param series: 1408 the series associated with the legend item 1409 """ 1410 super(SeriesLegendItemClickEvent, self).__init__(source) 1411 self._chart = chart 1412 self._series = series
1413
1414 - def getChart(self):
1415 """@return: Returns the chart object associated with the series""" 1416 return self._chart
1417
1418 - def getSeries(self):
1419 """@return: Returns the series associated with the legend item""" 1420 return self._series
1421
1422 1423 -class SeriesLegendItemClickListerner(object):
1424 """Interface for listening for a L{SeriesLegendItemClickEvent} 1425 triggered by L{InvientCharts} 1426 1427 @author: Invient 1428 @author: Richard Lincoln 1429 """ 1430
1431 - def seriesLegendItemClick(self, seriesLegendItemClickEvent):
1432 raise NotImplementedError
1433 1434 1435 _SERIES_CLICK_METHOD = getattr(SeriesClickListerner, 'seriesClick') 1436 _SERIES_HIDE_METHOD = getattr(SeriesHideListerner, 'seriesHide') 1437 _SERIES_SHOW_METHOD = getattr(SeriesShowListerner, 'seriesShow') 1438 _SERIES_LEGENDITEM_CLICK_METHOD = getattr(SeriesLegendItemClickListerner, 1439 'seriesLegendItemClick')
1440 1441 1442 -class PieChartLegendItemClickEvent(ComponentEvent):
1443 """PieChart legend item click event. This event is thrown, when the 1444 legend item belonging to the pie point (slice) is clicked. 1445 1446 @author: Invient 1447 @author: Richard Lincoln 1448 """ 1449
1450 - def __init__(self, source, chart, point):
1451 """New instance of the piechart legend item click event 1452 1453 @param source: 1454 the chart object itself 1455 @param chart: 1456 the chart object itself 1457 @param point: 1458 the pie point (slice) associated with the legend item 1459 """ 1460 super(PieChartLegendItemClickEvent, self).__init__(source) 1461 self._chart = chart 1462 self._point = point
1463
1464 - def getChart(self):
1465 """@return: Returns the chart object associated with the point""" 1466 return self._chart
1467
1468 - def getPoint(self):
1469 """@return: Returns the pie point (slice) associated with the legend 1470 item""" 1471 return self._point
1472
1473 1474 -class PieChartLegendItemClickListener(object):
1475 """Interface for listening for a L{PieChartLegendItemClickEvent} 1476 triggered by L{InvientCharts} 1477 1478 @author: Invient 1479 @author: Richard Lincoln 1480 """ 1481
1482 - def legendItemClick(self, legendItemClickEvent):
1483 raise NotImplementedError
1484 1485 1486 _LEGENDITEM_CLICK_METHOD = getattr(PieChartLegendItemClickListener, 1487 'legendItemClick')
1488 1489 1490 -class ChartClickEvent(ComponentEvent):
1491 """Chart Click event. This event is thrown, when this chart is clicked. 1492 1493 @author: Invient 1494 @author: Richard Lincoln 1495 """ 1496
1497 - def __init__(self, source, chart, point, mousePosition):
1498 """New instance of the chart click event. 1499 1500 @param source: 1501 the chart object itself 1502 @param chart: 1503 the chart object itself 1504 @param point: 1505 the position where the click event occurred in axes units 1506 @param mousePosition: 1507 the coordinate of mouse where the click event occurred in 1508 pixels 1509 """ 1510 super(ChartClickEvent, self).__init__(source) 1511 1512 self._chart = chart 1513 self._point = point 1514 self._mousePosition = mousePosition
1515 1516
1517 - def getChart(self):
1518 """Returns the chart object on which the click event occurred 1519 1520 @return: Returns the chart object on which the click event occurred 1521 @see: L{InvientCharts} 1522 """ 1523 return self._chart
1524 1525
1526 - def getPoint(self):
1527 """Returns the point representing the position where the click event 1528 occurred in axes units 1529 1530 @return: Returns the point representing the position where the click 1531 event occurred in axes units 1532 @see: L{Point} 1533 """ 1534 return self._point
1535 1536
1537 - def getMousePosition(self):
1538 """Returns the position of a mouse when the click event occurred 1539 1540 @return: Returns the position of a mouse when the click event occurred 1541 @see: L{MousePosition} 1542 """ 1543 return self._mousePosition
1544 1545
1546 - def __str__(self):
1547 return ('ChartClickEvent [point=' + str(self._point) 1548 + ', mousePosition=' + str(self._mousePosition) + ']')
1549
1550 1551 -class ChartClickListener(object):
1552 """Interface for listening for a L{ChartClickEvent} triggered by 1553 L{InvientCharts} 1554 1555 @author: Invient 1556 @author: Richard Lincoln 1557 """ 1558
1559 - def chartClick(self, chartClickEvent):
1560 raise NotImplementedError
1561
1562 1563 -class ChartAddSeriesEvent(ComponentEvent):
1564 """Add series event. This event is thrown, when a series is added to 1565 the chart. 1566 1567 @author: Invient 1568 @author: Richard Lincoln 1569 """ 1570
1571 - def __init__(self, source, chart):
1572 """New instance of the chart add series event. 1573 1574 @param source 1575 @param chart 1576 """ 1577 super(ChartAddSeriesEvent, self).__init__(source) 1578 1579 self._chart = chart
1580
1581 - def getChart(self):
1582 """Returns the chart object to which a series is added 1583 1584 @return: Returns the chart object to which a series has been added. 1585 @see: L{InvientCharts} 1586 """ 1587 return self._chart
1588
1589 1590 -class ChartAddSeriesListener(object):
1591 """Interface for listening for a L{ChartAddSeriesEvent} triggered by 1592 L{InvientCharts} 1593 1594 @author: Invient 1595 @author: Richard Lincoln 1596 """ 1597
1598 - def chartAddSeries(self, chartAddSeriesEvent):
1599 raise NotImplementedError
1600
1601 1602 -class ChartArea(object):
1603 """Defines information on the selected area. 1604 1605 @author: Invient 1606 @author: Richard Lincoln 1607 """ 1608
1609 - def __init__(self, xAxisMin, xAxisMax, yAxisMin, yAxisMax):
1610 self._xAxisMin = xAxisMin 1611 self._xAxisMax = xAxisMax 1612 self._yAxisMin = yAxisMin 1613 self._yAxisMax = yAxisMax
1614
1615 - def getxAxisMin(self):
1616 return self._xAxisMin
1617
1618 - def getxAxisMax(self):
1619 return self._xAxisMax
1620
1621 - def getyAxisMin(self):
1622 return self._yAxisMin
1623
1624 - def getyAxisMax(self):
1625 return self._yAxisMax
1626
1627 - def __str__(self):
1628 return ('ChartSelectedArea [xAxisMin=' + str(self._xAxisMin) 1629 + ', xAxisMax=' + str(self._xAxisMax) 1630 + ', yAxisMin=' + str(self._yAxisMin) 1631 + ', yAxisMax=' + str(self._yAxisMax) + ']')
1632
1633 1634 -class ChartZoomEvent(ComponentEvent):
1635 """Chart zoom event. This event is thrown, when an area of the chart has 1636 been selected. 1637 1638 @author: Invient 1639 @author: Richard Lincoln 1640 """ 1641
1642 - def __init__(self, source, chart, chartArea):
1643 """New instance of the chart zoom event. 1644 1645 @param source 1646 the chart object itself 1647 @param chart 1648 the chart object itself 1649 @param chartArea 1650 the chartArea object containing dimensions of zoomed area 1651 of the chart 1652 """ 1653 super(ChartZoomEvent, self).__init__(source) 1654 1655 self._chart = chart 1656 self._chartArea = chartArea
1657 1658
1659 - def getChart(self):
1660 """Returns the chart object for which the zoom event has occurred 1661 1662 @return: Returns the chart object for which the zoom event has 1663 occurred 1664 """ 1665 return self._chart
1666 1667
1668 - def getChartArea(self):
1669 """Returns the chartArea object containing dimensions of zoomed area 1670 of the chart 1671 1672 @return: Returns the chartArea object containing dimensions of zoomed 1673 area of the chart 1674 """ 1675 return self._chartArea
1676
1677 1678 -class ChartZoomListener(object):
1679 """Interface for listening for a L{ChartZoomEvent} triggered by 1680 L{InvientCharts} 1681 1682 @author: Invient 1683 @author: Richard Lincoln 1684 """ 1685
1686 - def chartZoom(self, chartZoomEvent):
1687 raise NotImplementedError
1688
1689 1690 -class ChartResetZoomEvent(ComponentEvent):
1691 """Chart reset zoom event. This event is thrown, when a chart is reset 1692 by setting its zoom level to normal. 1693 1694 @author: Invient 1695 @author: Richard Lincoln 1696 """ 1697
1698 - def __init__(self, source, chart):
1699 """New instance of the chart reset zoom event 1700 1701 @param source 1702 the chart object itself 1703 @param chart 1704 the chart object itself 1705 """ 1706 super(ChartResetZoomEvent, self).__init__(source) 1707 self._chart = chart
1708 1709
1710 - def getChart(self):
1711 """Returns the chart object for which zoom has been reset to normal 1712 1713 @return: Returns the chart object for which zoom has been reset to 1714 normal 1715 """ 1716 return self._chart
1717
1718 1719 -class ChartResetZoomListener(object):
1720 """Interface for listening for a L{@link ChartResetZoomEvent} triggered 1721 by L{InvientCharts} 1722 1723 @author: Invient 1724 @author: Richard Lincoln 1725 """ 1726
1727 - def chartResetZoom(self, chartResetZoomEvent):
1728 raise NotImplementedError
1729
1730 1731 -class ChartSVGAvailableEvent(ComponentEvent):
1732 """Chart SVG event. This event is thrown, when an SVG string representing 1733 the chart is received or ready. 1734 1735 Note that this event is thrown only once after a 1736 L{ChartSVGAvailableListener} is registered. 1737 1738 @author: Invient 1739 @author: Richard Lincoln 1740 """ 1741
1742 - def __init__(self, source, chart, svg):
1743 """New instance of the chart svg available event. 1744 1745 @param source: 1746 the chart object itself 1747 @param chart: 1748 the chart object itself 1749 @param svg: 1750 an svg string representing the chart object 1751 """ 1752 super(ChartSVGAvailableEvent, self).__init__(source) 1753 self._chart = chart 1754 self._svg = svg
1755 1756
1757 - def getChart(self):
1758 """Returns the chart object for which an svg string representation 1759 is available 1760 1761 @return: Returns the chart object for which an svg string 1762 representation is available 1763 """ 1764 return self._chart
1765 1766
1767 - def getSVG(self):
1768 """@return: Returns an SVG string representing the chart""" 1769 return self._svg
1770
1771 1772 -class ChartSVGAvailableListener(object):
1773 """Interface for listening for a L{ChartSVGAvailableEvent} triggered by 1774 L{InvientCharts}. 1775 1776 The chart can have only one listener of this type registered at any time. 1777 If a listener has already been registered and an attempt is made to 1778 register another listener then the previously registered listener will be 1779 unregistered and the new listener will be registered. 1780 1781 A listener will be called only once after it has been registered though 1782 it will be called again if the same listener is registered again. 1783 1784 @author: Invient 1785 @author: Richard Lincoln 1786 """ 1787
1788 - def svgAvailable(self, chartSVGAvailableEvent):
1789 pass
1790 1791 1792 _CHART_CLICK_METHOD = getattr(ChartClickListener, 'chartClick') 1793 _CHART_ADD_SERIES_METHOD = getattr(ChartAddSeriesListener, 'chartAddSeries') 1794 _CHART_ZOOM_METHOD = getattr(ChartZoomListener, 'chartZoom') 1795 _CHART_RESET_ZOOM_METHOD = getattr(ChartResetZoomListener, 'chartResetZoom') 1796 _CHART_SVG_AVAILABLE_METHOD = getattr(ChartSVGAvailableListener, 1797 'svgAvailable')
1798 1799 1800 -class Point(object):
1801 """This class represents a point of the chart's series. A series can have 1802 one or more points. A point has (X, Y) coordinates. None of the 1803 coordinates are mandatory. The name of a point can be displayed in a 1804 tooltip. 1805 1806 To represent no activity or missing points in the chart, create a point 1807 with both X and Y as null or just Y as null. 1808 1809 It is possible to specify custom configuration for each point. e.g. If a 1810 highest point can be marked in a chart with a different color using this 1811 configuration. 1812 1813 A point cannot be created without a series. It must belong to a series. 1814 However, the point must be added to a series by calling Series.addPoint() 1815 or Series.setPoints() to permanently add point to the series. 1816 1817 @author: Invient 1818 @author: Richard Lincoln 1819 1820 @see: L{DecimalPoint} 1821 @see: L{DateTimePoint} 1822 @see: L{PointConfig} 1823 """ 1824
1825 - def __init__(self, series, name_or_config=None, config=None):
1826 """Creates a point with given arguments. 1827 1828 @param series: 1829 The series to which the point must be associated. 1830 @param name_or_config: 1831 The configuration for this point, or the name of this point 1832 @param config: 1833 The configuration for this point, if any 1834 @exception ValueError: 1835 If the argument series is null 1836 """ 1837 self._id = None 1838 self._isAutosetX = None 1839 self._shift = False 1840 1841 name = None 1842 if name_or_config is not None: 1843 if isinstance(name_or_config, PointConfig): 1844 config = name_or_config 1845 name = None 1846 else: 1847 name = name_or_config 1848 config = None 1849 1850 self._series = series 1851 self._name = name 1852 self._config = config
1853 1854
1855 - def getId(self):
1856 return self._id
1857 1858
1859 - def getName(self):
1860 """@return: Returns name of this point""" 1861 return self._name
1862 1863
1864 - def setName(self, name):
1865 """Sets name of this point 1866 1867 @param name: 1868 name of this point 1869 """ 1870 self._name = name
1871 1872
1873 - def getSeries(self):
1874 """@return: Returns L{Series} associated with this point""" 1875 return self._series
1876 1877
1878 - def getConfig(self):
1879 """@return: Returns L{PointConfig} for this point""" 1880 return self._config
1881 1882
1883 - def setConfig(self, config):
1884 """Sets L{PointConfig} for this point 1885 1886 @param config: 1887 configuration of this point 1888 @see: L{PointConfig} 1889 """ 1890 self._config = config
1891 1892
1893 - def isAutosetX(self):
1894 """@return: Returns true if X value of this point is set 1895 programmatically""" 1896 return self._isAutosetX
1897 1898
1899 - def setAutosetX(self, isAutosetX):
1900 """If the argument is true it indicates that the X value of this point 1901 is set programmatically and user has not specified it. 1902 """ 1903 self._isAutosetX = isAutosetX
1904 1905
1906 - def isShift(self):
1907 """@return: Returns true if a point at the start of the series should 1908 beshifted off when this point is appended otherwise false. 1909 """ 1910 return self._shift
1911 1912
1913 - def setShift(self, shift):
1914 """A value of true means one point is shifted off the start of the 1915 series as one is appended to the end. 1916 """ 1917 self._shift = shift
1918 1919
1920 - def getX(self):
1921 """@return: Returns X value of this point""" 1922 raise NotImplementedError
1923 1924
1925 - def getY(self):
1926 """@return: Returns Y value of this point""" 1927 raise NotImplementedError
1928 1929
1930 - def __str__(self):
1931 return ('Point [id=' + self._id 1932 + ', name=' + self._name 1933 + ', series=' + self._series.getName() 1934 + ', config=' + str(self._config) + ']')
1935
1936 1937 -class DecimalPoint(Point):
1938 """This class represent a point with (X, Y) both as number. It should be 1939 used to add points to L{XYSeries} 1940 1941 @author: Invient 1942 @author: Richard Lincoln 1943 """ 1944
1945 - def __init__(self, *args):
1946 """@param invientCharts: 1947 @param args: 1948 tuple of the form: 1949 - (series) 1950 - the series to which this belongs to 1951 - (series, y) 1952 - the series to which this belongs to 1953 - the y value of this point 1954 - (series, name, y) 1955 - the series to which this belongs to 1956 - the name of this point 1957 - the y value of this point 1958 - (x, y) 1959 - the x value of this point 1960 - the y value of this point 1961 - (series, name, y, config) 1962 - the series to which this belongs to 1963 - the name of this point 1964 - the y value of this point 1965 - the configuration of this point 1966 - (series, y, config) 1967 - the series to which this belongs to 1968 - the y value of this point 1969 - the configuration of this point 1970 - (series, x, y) 1971 - the series to which this belongs to 1972 - the x value of this point 1973 - the y value of this point 1974 - (series, x, y, config) 1975 - the series to which this belongs to 1976 - the x value of this point 1977 - the y value of this point 1978 - the configuration of this point 1979 """ 1980 self._x = None 1981 self._y = None 1982 1983 nargs = len(args) 1984 if nargs == 1: 1985 series, = args 1986 super(DecimalPoint, self).__init__(series) 1987 elif nargs == 2: 1988 if isinstance(args[0], Series): 1989 series, y = args 1990 super(DecimalPoint, self).__init__(series) 1991 self._y = y 1992 else: 1993 x, y = args 1994 super(DecimalPoint, self).__init__(None) 1995 self._x = x 1996 self._y = y 1997 elif nargs == 3: 1998 if isinstance(args[1], (float, int)): 1999 if isinstance(args[2], PointConfig): 2000 series, y, config = args 2001 super(DecimalPoint, self).__init__(series, config) 2002 self._y = y 2003 else: 2004 series, x, y = args 2005 self.__init__(series, x, y, None) 2006 series, x, y = args 2007 self.__init__(series, x, y, None) 2008 else: 2009 series, name, y = args 2010 super(DecimalPoint, self).__init__(series, name) 2011 self._y = y 2012 elif nargs == 4: 2013 if isinstance(args[1], (float, int)): 2014 series, x, y, config = args 2015 super(DecimalPoint, self).__init__(series, config) 2016 self._x = x 2017 self._y = y 2018 else: 2019 series, name, y, config = args 2020 super(DecimalPoint, self).__init__(series, name, config) 2021 self._y = y 2022 else: 2023 raise ValueError
2024 2025
2026 - def getX(self):
2027 return self._x
2028 2029
2030 - def setX(self, x):
2031 """Sets the x value of this point 2032 """ 2033 self._x = x
2034 2035
2036 - def getY(self):
2037 return self._y
2038 2039
2040 - def setY(self, y):
2041 """Sets the y value of this point 2042 """ 2043 self._y = y
2044 2045
2046 - def __str__(self):
2047 return ('DecimalPoint [x=' + self._x 2048 + ', y=' + self._y 2049 + ', id=' + self.getId() 2050 + ', name=' + self.getName() 2051 + ', seriesName=' 2052 + (self._series.getName() 2053 if self._series is not None else '') 2054 + ']')
2055 2056
2057 - def __hash__(self):
2058 prime = 31 2059 result = 1 2060 result = ((prime * result) 2061 + (0 if self._y is None else hash(self._y))) 2062 return result
2063 2064
2065 - def __eq__(self, obj):
2066 if self is obj: 2067 return True 2068 if obj is None: 2069 return False 2070 if self.__class__ != obj.__class__: 2071 return False 2072 other = obj 2073 # If x is null then return always false as x is calculated 2074 # if not specified 2075 if (self._x is None) or (other._x is None): 2076 return False 2077 if not (self._x == other._x): 2078 return False 2079 if self._y is None: 2080 if other._y is not None: 2081 return False 2082 elif other._y is None: 2083 return False 2084 elif cmp(self._y, other._y) != 0: 2085 return False 2086 return True
2087
2088 2089 -class DateTimePoint(Point):
2090 """This class represent a point with (X, Y) both as number. It should 2091 be used to add points to L{DateTimeSeries} 2092 2093 @author: Invient 2094 @author: Richard Lincoln 2095 """ 2096
2097 - def __init__(self, *args):
2098 """@param args: 2099 tuple of the form: 2100 - (series) 2101 - the series to which this belongs to 2102 - (series, y) 2103 - the series to which this belongs to 2104 - the y value of this point 2105 - (series, name, y) 2106 - the series to which this belongs to 2107 - the name of this point 2108 - the y value of this point 2109 - (series, name, y, config) 2110 - the series to which this belongs to 2111 - the name of this point 2112 - the y value of this point 2113 - the configuration of this point 2114 - (series, x, y) 2115 - the series to which this belongs to 2116 - the x value of this point 2117 - the y value of this point 2118 """ 2119 self._x = None 2120 self._y = None 2121 2122 nargs = len(args) 2123 if nargs == 1: 2124 series, = args 2125 super(DateTimePoint, self).__init__(series) 2126 elif nargs == 2: 2127 series, y = args 2128 self.__init__(series, '', y) 2129 elif nargs == 3: 2130 if isinstance(args[1], datetime): 2131 series, x, y = args 2132 self.__init__(series, y) 2133 self._x = x 2134 else: 2135 series, name, y = args 2136 super(DateTimePoint, self).__init__(series, name) 2137 self._y = y 2138 elif nargs == 4: 2139 series, name, y, config = args 2140 super(DateTimePoint, self).__init__(series, name, config) 2141 self._y = y 2142 else: 2143 raise ValueError
2144 2145
2146 - def getX(self):
2147 return self._x
2148 2149
2150 - def setX(self, x):
2151 """Sets the x value of this point 2152 2153 @param x 2154 """ 2155 self._x = x
2156 2157
2158 - def getY(self):
2159 return self._y
2160 2161
2162 - def setY(self, y):
2163 """Sets the y value of this point 2164 2165 @param y 2166 """ 2167 self._y = y
2168 2169
2170 - def __str__(self):
2171 return ('DateTimePoint [x=' 2172 + getDate(self._x, 2173 self._series.isIncludeTime() 2174 if self._series is not None else False) 2175 + ', y=' + str(self._y) + ', id=' + self.getId() 2176 + ', name=' + self.getName() 2177 + ', seriesName=' + (self._series.getName() 2178 if self._series is not None else '') 2179 + ']')
2180 2181
2182 - def __hash__(self):
2183 prime = 31 2184 result = 1 2185 result = ((prime * result) + (0 if self._y is None else hash(self._y))) 2186 return result
2187 2188
2189 - def __eq__(self, obj):
2190 if self is obj: 2191 return True 2192 if obj is None: 2193 return False 2194 if self.__class__ != obj.__class__: 2195 return False 2196 other = obj 2197 # If x is null then return always false as x is calculated if not 2198 # specified 2199 if (self._x is None) or (other._x is None): 2200 return False 2201 pointIncludeTime = (self.getSeries().isIncludeTime() 2202 if isinstance(self.getSeries(), DateTimeSeries) else False) 2203 pointOtherIncludeTime = (other.getSeries().isIncludeTime() 2204 if isinstance(other.getSeries(), DateTimeSeries) else False) 2205 pointX = getDate(self._x, pointIncludeTime) 2206 pointOtherX = getDate(other._x, pointOtherIncludeTime) 2207 if cmp(pointX, pointOtherX) != 0: 2208 return False 2209 if self._y is None: 2210 if other._y is not None: 2211 return False 2212 elif other._y is None: 2213 return False 2214 elif cmp(self._y, other._y) != 0: 2215 return False 2216 return True
2217
2218 2219 -class Series(object):
2220 """This class defines a series of the chart. A series contains a collection 2221 of points. Series can be one of types defined by L{SeriesType}. 2222 2223 Each series must have unique name. If an attempt is made to add two 2224 series with same then only the first added series will be in effect. 2225 2226 If the series type is not specified, it defaults to chart type and the 2227 default chart type is SeriesType.LINE. A series has unique xAxis and 2228 yAxis object associated with it. There is no need to set xAxis and yAxis 2229 unless the chart has more than one one axis of any type and the series 2230 must belong to any of the secondary axis. 2231 2232 It is also possible to specify configuration for individual series and 2233 not just series type. 2234 2235 @author: Invient 2236 @author: Richard Lincoln 2237 """ 2238
2239 - def __init__(self, name, seriesType_or_config=None, config=None):
2240 """Creates a series with given name, type and configuration 2241 2242 @param name: 2243 the name of this series 2244 @param seriesType_or_config: 2245 the type of this series or the configuration for this series 2246 @param config: 2247 the configuration for this series 2248 """ 2249 self._points = OrderedSet() 2250 self._name = '' 2251 self._type = None 2252 self._stack = None 2253 self._xAxis = None 2254 self._yAxis = None 2255 self._config = None 2256 self._invientCharts = None 2257 2258 seriesType = None 2259 if seriesType_or_config is not None: 2260 if isinstance(seriesType_or_config, SeriesType): 2261 seriesType = seriesType_or_config 2262 else: 2263 config = seriesType_or_config 2264 2265 self._name = name 2266 self._type = seriesType 2267 self._config = config
2268 2269
2270 - def getConfig(self):
2271 """@return: Returns the configuration object associated with this 2272 series""" 2273 return self._config
2274 2275
2276 - def getName(self):
2277 """@return: Returns name of this series""" 2278 return self._name
2279 2280
2281 - def setName(self, name):
2282 """Sets name of this series 2283 """ 2284 self._name = name
2285 2286
2287 - def getType(self):
2288 """@return""" 2289 return self._type
2290 2291
2292 - def setType(self, typ):
2293 """Sets type of this series 2294 """ 2295 self._type = typ
2296 2297
2298 - def getStack(self):
2299 """@return: Returns stack of this series""" 2300 return self._stack
2301 2302
2303 - def setStack(self, stack):
2304 """By using this stack property, it is possible to group series in a 2305 stacked chart. Sets stack for this series. If two series belongs to 2306 the same stack then the resultant chart will be stacked chart 2307 """ 2308 self._stack = stack
2309 2310
2311 - def getXAxis(self):
2312 """@return: Returns x-axis associated with this series. 2313 @see: L{Axis} 2314 """ 2315 return self._xAxis
2316 2317
2318 - def setXAxis(self, xAxis):
2319 """Sets x-axis of this series. A series can be associated with at most 2320 one x-axis. 2321 """ 2322 self._xAxis = xAxis
2323 2324
2325 - def getYAxis(self):
2326 """@return: Returns y-axis of this series.""" 2327 return self._yAxis
2328 2329
2330 - def setYAxis(self, yAxis):
2331 """Sets y-axis of this series. A series can be associated with at most 2332 one y-axis. 2333 """ 2334 self._yAxis = yAxis
2335 2336
2337 - def removePoint(self, *points):
2338 pointsRemovedList = list() 2339 for point in points: 2340 if point in self._points: 2341 self._points.remove(point) 2342 pointsRemovedList.append(point) 2343 2344 self.updatePointXValuesIfNotPresent() 2345 2346 for point in pointsRemovedList: 2347 if self._invientCharts is not None: 2348 self._invientCharts.addSeriesPointRemovedOperation( 2349 point.getSeries().getName(), point) 2350 self._invientCharts.requestRepaint()
2351 2352
2353 - def removeAllPoints(self):
2354 """Removes all points in this series""" 2355 self._points.clear() 2356 if self._invientCharts is not None: 2357 cur = SeriesCUR(SeriesCURType.UPDATE, self.getName(), True) 2358 self._invientCharts.addSeriesCUROperation(cur) 2359 self._invientCharts.requestRepaint()
2360 2361
2362 - def addPoint(self, shift, points):
2363 """Adds one or more points into this series, specified as an argument 2364 to this method 2365 2366 @return: Returns null if the argument is null otherwise returns a 2367 collection of points which are added in this series. If a 2368 point has same (x, y) value as any other point in the 2369 argument points then it will not be added. 2370 """ 2371 points = [points] if isinstance(points, Point) else points 2372 2373 if shift: 2374 # Remove first point as other points gets appended at the end 2375 pointsItr = iter(self._points) 2376 try: 2377 p = pointsItr.next() 2378 self._points.remove(p) 2379 except StopIteration: 2380 pass 2381 2382 pointsAddedList = list() 2383 2384 for point in points: 2385 if point not in self._points: 2386 self._points.add(point) 2387 pointsAddedList.append(point) 2388 2389 self.updatePointXValuesIfNotPresent() 2390 2391 # Now record point add event as we need to know x value of a point 2392 for point in pointsAddedList: 2393 if self._invientCharts is not None: 2394 self._invientCharts.addSeriesPointAddedOperation( 2395 point.getSeries().getName(), point) 2396 self._invientCharts.requestRepaint() 2397 2398 return OrderedSet(pointsAddedList)
2399 2400
2401 - def addPointsInternal(self, points):
2402 for point in points: 2403 self._points.add(point)
2404 2405
2406 - def getPoints(self):
2407 """@return: Returns all points of this series. Adding or removing any 2408 point to or from the returned collection will not impact the 2409 chart. To add a point or points, use addPoint() or 2410 removePoint() method. 2411 """ 2412 return OrderedSet(self._points)
2413 2414
2415 - def setPoints(self, points):
2416 """Sets points into this series 2417 2418 @return: Returns null if the argument is null otherwise returns a 2419 collection of points which are set in this series. If a point 2420 has same (x, y) value as any other point in the argument 2421 points then it will not be added. 2422 """ 2423 if points is not None: 2424 self._points.clear() 2425 self.addPointsInternal(points) 2426 self.updatePointXValuesIfNotPresent() 2427 if self._invientCharts is not None: 2428 cur = SeriesCUR(SeriesCURType.UPDATE, self.getName(), True) 2429 self._invientCharts.addSeriesCUROperation(cur) 2430 self._invientCharts.requestRepaint() 2431 return self.getPoints() 2432 return None
2433 2434
2436 """Each of the subclass needs to implement this method to ensure that 2437 each point has appropriate X value even if it is not specified. 2438 """ 2439 pass
2440 2441
2442 - def show(self):
2443 """Show this series""" 2444 self._config = SeriesConfig() if self._config is None else self._config 2445 self._config.setVisible(True) 2446 if self._invientCharts is not None: 2447 cur = SeriesCUR(SeriesCURType.UPDATE, self.getName()) 2448 self._invientCharts.addSeriesCUROperation(cur) 2449 self._invientCharts.requestRepaint()
2450 2451
2452 - def hide(self):
2453 """Hide this series""" 2454 self._config = SeriesConfig() if self._config is None else self._config 2455 self._config.setVisible(False) 2456 if self._invientCharts is not None: 2457 cur = SeriesCUR(SeriesCURType.UPDATE, self.getName()) 2458 self._invientCharts.addSeriesCUROperation(cur) 2459 self._invientCharts.requestRepaint()
2460 2461
2462 - def setInvientCharts(self, invientCharts):
2463 self._invientCharts = invientCharts
2464 2465
2466 - def __hash__(self):
2467 prime = 31 2468 result = 1 2469 result = ((prime * result) 2470 + (0 if self._name is None else hash(self._name))) 2471 return result
2472 2473
2474 - def __eq__(self, obj):
2475 if self is obj: 2476 return True 2477 if obj is None: 2478 return False 2479 if self.__class__ != obj.__class__: 2480 return False 2481 other = obj 2482 if self._name is None: 2483 if other._name is not None: 2484 return False 2485 elif not (self._name == other._name): 2486 return False 2487 return True
2488 2489
2490 - def __str__(self):
2491 return ('Series [points=' + self._points 2492 + ', name=' + self._name 2493 + ', type=' + str(self._type) 2494 + ', stack=' + self._stack 2495 + ', xAxis=' + str(self._xAxis) 2496 + ', yAxis=' + str(self._yAxis) 2497 + ', config=' + str(self._config) + ']')
2498
2499 2500 -class XYSeries(Series):
2501 """This class defines a number series. In this series both X and Y values 2502 must be number. To use date values, use L{DateTimeSeries} 2503 2504 @author: Invient 2505 @author: Richard Lincoln 2506 2507 @see: L{DateTimeSeries} 2508 """ 2509
2510 - def __init__(self, name, seriesType_or_config=None, config=None):
2511 """Creates a series with given name, type and configuration 2512 2513 @param name: 2514 the name of this series 2515 @param seriesType_or_config: 2516 the type of this series or the configuration for this series 2517 @param config: 2518 the configuration for this series 2519 """ 2520 seriesType = None 2521 if seriesType_or_config is not None: 2522 if isinstance(seriesType_or_config, SeriesType): 2523 seriesType = seriesType_or_config 2524 else: 2525 config = seriesType_or_config 2526 2527 super(XYSeries, self).__init__(name, seriesType, config)
2528 2529
2530 - def removePoint(self, *points):
2531 """Removes the specified point from the series 2532 """ 2533 super(XYSeries, self).removePoint(*points)
2534 2535
2536 - def removeAllPoints(self):
2537 super(XYSeries, self).removeAllPoints()
2538 2539
2540 - def addPoint(self, point_or_points, shift=None):
2541 """Appends the specified point(s) into the series if they do not exists 2542 in this series. The points which already exists will not be appended. A 2543 collection of points appended to this series will be returned. 2544 2545 @param point_or_points: 2546 @param shift: 2547 If true then one point is shifted off the start of this 2548 series as one is appended to the end. 2549 @return: Returns a collection of points which are added in this 2550 series. If a point has same (x, y) value as any other point 2551 in the input argument points then it will not be added in 2552 this series. 2553 """ 2554 if shift is None: 2555 points = point_or_points 2556 return super(XYSeries, self).addPoint(False, points) 2557 else: 2558 point = point_or_points 2559 point.setShift(shift) 2560 return super(XYSeries, self).addPoint(shift, point)
2561 2562
2563 - def getPoints(self):
2564 return super(XYSeries, self).getPoints()
2565 2566
2567 - def setSeriesPoints(self, points):
2568 """Sets points into this series. This method removes all of its points 2569 and then add points specified in the method argument. If the argument 2570 is null then no actions are taken. 2571 2572 @param points: 2573 the collection of points to set into this series. 2574 @return: Returns a collection of points which are set in this series. 2575 If a point has same (x, y) value as any other point in the 2576 argument points then it will not be added. 2577 """ 2578 return super(XYSeries, self).setPoints(points)
2579 2580
2582 pointStart = 0 2583 pointInterval = 1 2584 2585 if isinstance(super(XYSeries, self).getConfig(), BaseLineConfig): 2586 config = super(XYSeries, self).getConfig() 2587 if config.getPointStart() is not None: 2588 pointStart = config.getPointStart() 2589 if config.getPointInterval() is not None: 2590 pointInterval = config.getPointInterval() 2591 2592 count = 0 2593 2594 for point in self.getPoints(): 2595 if ((point.getX() is None) 2596 or (point.getX() is not None and point.isAutosetX())): 2597 point.setAutosetX(True) 2598 if count == 0: 2599 point.setX(pointStart) 2600 count += 1 2601 else: 2602 pointStart = pointStart + pointInterval 2603 point.setX(pointStart)
2604
2605 2606 -class DateTimeSeries(Series):
2607 """This class defines a datetime series. In this series, the X value must 2608 be date and Y values must be number. To use number values, use L{XYSeries} 2609 2610 By default, the time of a day is not included in the X value. In order to 2611 include time, use a constructor with argument isIncludeTime and pass true 2612 value for the argument. 2613 2614 @author: Invient 2615 @author: Richard Lincoln 2616 2617 @see: L{XYSeries} 2618 """ 2619
2620 - def __init__(self, invientCharts, *args):
2621 """Creates a series with given name. This series will not consider time 2622 in the X property of L{DateTimePoint}. 2623 2624 @param args: 2625 tuple of the form: 2626 - (name) 2627 - the name of this series 2628 - (name, isIncludeTime) 2629 - the name of this series 2630 - If true then the time in the X property of L{DateTimePoint} 2631 will be considered when drawing the chart. Defaults to false. 2632 - (name, config) 2633 - the name of this series 2634 - the configuration for this series 2635 - (name, config, isIncludeTime) 2636 - the name of this series 2637 - the configuration for this series 2638 - If true then the time in the X property of L{DateTimePoint} 2639 will be considered when drawing the chart. Defaults to false. 2640 - (name, seriesType, isIncludeTime) 2641 - the name of this series 2642 - the type of this series 2643 - If true then the time in the X property of L{DateTimePoint} 2644 will be considered when drawing the chart. Defaults to false. 2645 - (name, seriesType, config) 2646 - the name of this series 2647 - the type of this series 2648 - the configuration for this series 2649 - (name, seriesType, config, isIncludeTime) 2650 - the name of this series 2651 - the type of this series 2652 - the configuration for this series 2653 - If true then the time in the X property of L{DateTimePoint} 2654 will be considered when drawing the chart. Defaults to false. 2655 """ 2656 self._invientCharts = invientCharts 2657 2658 self._includeTime = None 2659 2660 args = args 2661 nargs = len(args) 2662 if nargs == 1: 2663 name, = args 2664 self.__init__(invientCharts, name, False) 2665 elif nargs == 2: 2666 if isinstance(args[1], SeriesType): 2667 name, seriesType = args 2668 self.__init__(invientCharts, name, seriesType, False) 2669 elif isinstance(args[1], SeriesConfig): 2670 name, config = args 2671 self.__init__(invientCharts, name, config, False) 2672 else: 2673 name, isIncludeTime = args 2674 super(DateTimeSeries, self).__init__(name) 2675 self._includeTime = isIncludeTime 2676 elif nargs == 3: 2677 if isinstance(args[1], SeriesType): 2678 if isinstance(args[2], SeriesConfig): 2679 name, seriesType, config = args 2680 self.__init__(invientCharts, name, seriesType, config, False) 2681 else: 2682 name, seriesType, isIncludeTime = args 2683 super(DateTimeSeries, self).__init__(name, seriesType) 2684 self._includeTime = isIncludeTime 2685 else: 2686 name, config, isIncludeTime = args 2687 super(DateTimeSeries, self).__init__(name, config) 2688 self._includeTime = isIncludeTime 2689 elif nargs == 4: 2690 name, seriesType, config, isIncludeTime = args 2691 super(DateTimeSeries, self).__init__(name, seriesType, config) 2692 self._includeTime = isIncludeTime 2693 else: 2694 raise ValueError
2695 2696
2697 - def removePoint(self, *points):
2698 """Removes all points specified as method argument into this series 2699 """ 2700 super(DateTimeSeries, self).removePoint(points)
2701 2702
2703 - def removeAllPoints(self):
2704 super(DateTimeSeries, self).removeAllPoints()
2705 2706
2707 - def addPoint(self, point_or_points, shift=None):
2708 """Appends the specified point(s) into the series if they do not exists in 2709 this series. The points which already exists will not be appended. A 2710 collection of points appended to this series will be returned. 2711 2712 @param point_or_points: 2713 @param shift: 2714 If true then one point is shifted off the start of this 2715 series as one is appended to the end. 2716 @return Returns a collection of points which are added in this 2717 series. If a point has same (x, y) value as any other point 2718 in the input argument points then it will not be added in 2719 this series. 2720 """ 2721 if shift is None: 2722 points = point_or_points 2723 return super(DateTimeSeries, self).addPoint(False, points) 2724 else: 2725 point = point_or_points 2726 point.setShift(shift) 2727 return super(DateTimeSeries, self).addPoint(shift, point)
2728 2729
2730 - def isIncludeTime(self):
2731 """@return: Returns true if the time in the X property of 2732 L{DateTimePoint} will be considered when drawing the 2733 chart otherwise false. 2734 """ 2735 return self._includeTime
2736 2737
2738 - def getPoints(self):
2739 return super(DateTimeSeries, self).getPoints()
2740 2741
2742 - def setSeriesPoints(self, points):
2743 """Sets points into this series. This method removes all of its points 2744 and then add points specified in the method argument. If the argument 2745 is null then no actions are taken. 2746 2747 @param points: 2748 the collection of points to set into this series. 2749 @return: Returns a collection of points which are added in this 2750 series. If a point has same (x, y) value as any other point 2751 in the input argument points then it will not be added in 2752 this series. 2753 """ 2754 return super(DateTimeSeries, self).setPoints(points)
2755 2756
2758 pointStart = self.getDefPointStart() 2759 pointInterval = 3600000 2760 # 1 hour 2761 if isinstance(super(DateTimeSeries, self).getConfig(), BaseLineConfig): 2762 config = super(DateTimeSeries, self).getConfig() 2763 if config.getPointStart() is not None: 2764 pointStart = config.getPointStart() 2765 if config.getPointInterval() is not None: 2766 pointInterval = config.getPointInterval() 2767 prevDate = datetime.fromtimestamp(pointStart / 1e03) 2768 count = 0 2769 for point in self.getPoints(): 2770 if ((point.getX() is None) 2771 or (point.getX() is not None and point.isAutosetX())): 2772 point.setAutosetX(True) 2773 if count == 0: 2774 point.setX(prevDate) 2775 count += 1 2776 else: 2777 point.setX(self.getUpdatedDate(prevDate, pointInterval)) 2778 prevDate = point.getX()
2779 2780 2781 @classmethod
2782 - def getDefPointStart(cls):
2783 # dt = datetime(1970, 1, 1) 2784 # return long(totalseconds(dt - datetime(1970, 1, 1)) * 1e03) 2785 return -3600000.0
2786 2787 2788 @classmethod
2789 - def getUpdatedDate(cls, dt, milliseconds):
2790 ts = getDate(dt) + milliseconds 2791 return datetime.fromtimestamp(ts / 1e03)
2792 2793
2794 - def __str__(self):
2795 return ('DateTimeSeries [includeTime=' + self._includeTime 2796 + ', getConfig()=' + str(self._invientCharts.getConfig()) 2797 + ', getName()=' + self.getName() 2798 + ', getType()=' + str(self.getType()) 2799 + ', getStack()=' + self.getStack() 2800 + ', getXAxis()=' + str(self.getXAxis()) 2801 + ', getYAxis()=' + str(self.getYAxis()) 2802 + ']')
2803
2804 2805 -class SeriesType(object):
2806 COMMONSERIES = None 2807 LINE = None 2808 SPLINE = None 2809 SCATTER = None 2810 AREA = None 2811 AREASPLINE = None 2812 BAR = None 2813 COLUMN = None 2814 PIE = None 2815
2816 - def __init__(self, typ):
2817 self._typ = typ
2818
2819 - def getName(self):
2820 return self._typ
2821 2822 @classmethod
2823 - def values(cls):
2824 return [cls.COMMONSERIES, cls.LINE, cls.SPLINE, cls.SCATTER, 2825 cls.AREA, cls.AREASPLINE, cls.BAR, cls.COLUMN, cls.PIE]
2826 2827 SeriesType.COMMONSERIES = SeriesType('series') 2828 SeriesType.LINE = SeriesType('line') 2829 SeriesType.SPLINE = SeriesType('spline') 2830 SeriesType.SCATTER = SeriesType('scatter') 2831 SeriesType.AREA = SeriesType('area') 2832 SeriesType.AREASPLINE = SeriesType('areaspline') 2833 SeriesType.BAR = SeriesType('bar') 2834 SeriesType.COLUMN = SeriesType('column') 2835 SeriesType.PIE = SeriesType('pie')
2836 2837 2838 -class SeriesCUR(object):
2839
2840 - def getType(self):
2841 return self._type
2842
2843 - def getName(self):
2844 return self._name
2845
2846 - def __init__(self, typ, name, reloadPoints=False):
2847 self._type = typ 2848 self._name = name 2849 self._reloadPoints = reloadPoints 2850 self._pointsAdded = OrderedSet() 2851 self._pointsRemoved = OrderedSet() 2852 2853 super(SeriesCUR, self).__init__()
2854 2855
2856 - def isReloadPoints(self):
2857 """Indicates whether the client/terminal should update series by 2858 setting all data of a series instead of adding or removing individual 2859 points 2860 2861 @return: Returns true if the data of the series must be reloaded 2862 otherwise false. 2863 """ 2864 return self._reloadPoints
2865 2866
2867 - def setReloadPoints(self, reloadPoints):
2868 self._reloadPoints = reloadPoints
2869 2870
2871 - def trackPointAdded(self, point):
2872 self._pointsAdded.add(point)
2873 2874
2875 - def trackPointRemoved(self, point):
2876 # If the point was added earlier and now removed 2877 # then there is no need to record its add/remove operation 2878 # as add of a point is nullified by remove of a point 2879 if not self.removePointIfTrackedAlready(point): 2880 self._pointsRemoved.add(point)
2881 2882
2883 - def removePointIfTrackedAlready(self, point):
2884 # Used to clear all points added/removed when 2885 # series data is set/cleared using series.setPoints() or 2886 # series.removeAllPoints() 2887 return self._pointsAdded.remove(point)
2888 2889
2890 - def clearTrackedPoints(self):
2891 self._pointsAdded.clear() 2892 self._pointsRemoved.clear()
2893 2894
2895 - def getPointsAdded(self):
2896 return self._pointsAdded
2897 2898
2899 - def getPointsRemoved(self):
2900 return self._pointsRemoved
2901 2902
2903 - def __hash__(self):
2904 prime = 31 2905 result = 1 2906 result = ((prime * result) 2907 + (0 if self._name is None else hash(self._name))) 2908 result = ((prime * result) 2909 + (0 if self._type is None else hash(self._type))) 2910 return result
2911 2912
2913 - def __eq__(self, obj):
2914 if self is obj: 2915 return True 2916 if obj is None: 2917 return False 2918 if self.__class__ != obj.__class__: 2919 return False 2920 other = obj 2921 if self._name is None: 2922 if other._name is not None: 2923 return False 2924 elif not (self._name == other._name): 2925 return False 2926 if self._type is None: 2927 if other._type is not None: 2928 return False 2929 elif not (self._type == other._type): 2930 return False 2931 return True
2932 2933
2934 - def __str__(self):
2935 return ('SeriesCUR [type=' + str(self._type) 2936 + ', name=' + self._name 2937 + ', reloadPoints=' + str(self._reloadPoints) 2938 + ', pointsAdded=' + str(self._pointsAdded) 2939 + ', pointsRemoved=' + str(self._pointsRemoved) 2940 + ']')
2941
2942 2943 -class SeriesCURType(object):
2944 2945 ADD = None 2946 UPDATE = None 2947 REMOVE = None 2948
2949 - def __init__(self, name):
2950 self._name = name
2951
2952 - def getName(self):
2953 return self._name
2954 2955 @classmethod
2956 - def values(cls):
2957 return [cls.ADD, cls.UPDATE, cls.REMOVE]
2958 2959 SeriesCURType.ADD = SeriesCURType('Add') 2960 SeriesCURType.UPDATE = SeriesCURType('Update') 2961 SeriesCURType.REMOVE = SeriesCURType('Remove') 2962