Ticket #6: chart-v2.diff
| File chart-v2.diff, 10.5 kB (added by jae@…, 4 years ago) |
|---|
-
chart.py
17 17 18 18 import copy 19 19 import cairo 20 import math 20 21 21 22 from pycha.color import (defaultColorscheme, getColorscheme, hex2rgb, 22 23 DEFAULT_COLOR) … … 231 232 label = str(tick.v) 232 233 else: 233 234 label = tick.label 234 pos = self.yscale * (tick.v - self.minyval)235 pos = 1.0 - (self.yscale * (tick.v - self.minyval)) 235 236 if 0.0 <= pos <= 1.0: 236 237 self.yticks.append((pos, label)) 237 238 … … 252 253 # render methods 253 254 def _renderBackground(self, cx): 254 255 """Renders the background area of the chart""" 256 255 257 if self.options.background.hide: 256 258 return 257 259 258 260 cx.save() 259 cx.set_source_rgb(*hex2rgb(self.options.background.color))260 cx.rectangle(self.area.x, self.area.y, self.area.w, self.area.h)261 cx.fill()262 cx.set_source_rgb(*hex2rgb(self.options.background.lineColor))263 cx.set_line_width(self.options.axis.lineWidth)264 261 265 self._renderLines(cx) 262 if self.options.background.baseColor: 263 cx.set_source_rgb(*hex2rgb(self.options.background.baseColor)) 264 x, y, w, h = 0, 0, self.area.w, self.area.h 265 w += self.options.padding.left + self.options.padding.right 266 h += self.options.padding.top + self.options.padding.bottom 267 cx.rectangle(x, y, w, h) 268 cx.fill() 266 269 270 if self.options.background.chartColor: 271 cx.set_source_rgb(*hex2rgb(self.options.background.chartColor)) 272 cx.rectangle(self.area.x, self.area.y, self.area.w, self.area.h) 273 cx.fill() 274 275 if self.options.background.lineColor: 276 cx.set_source_rgb(*hex2rgb(self.options.background.lineColor)) 277 cx.set_line_width(self.options.axis.lineWidth) 278 self._renderLines(cx) 279 267 280 cx.restore() 268 281 269 282 def _renderLines(self, cx): … … 302 315 cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) 303 316 cx.set_line_width(self.options.axis.lineWidth) 304 317 318 cx.select_font_face(self.options.axis.labelFont, 319 cairo.FONT_SLANT_NORMAL, 320 cairo.FONT_WEIGHT_NORMAL) 321 cx.set_font_size(self.options.axis.labelFontSize) 322 323 if self.options.title: 324 cx.select_font_face(self.options.titleFont, 325 cairo.FONT_SLANT_NORMAL, 326 cairo.FONT_WEIGHT_BOLD) 327 cx.set_font_size(self.options.titleFontSize) 328 329 title = unicode(self.options.title) 330 extents = cx.text_extents(title) 331 titleWidth = extents[2] 332 333 x = self.area.x + self.area.w / 2.0 - titleWidth / 2.0 334 y = cx.font_extents()[0] # font ascent 335 336 cx.move_to(x, y) 337 cx.show_text(title) 338 339 cx.select_font_face(self.options.axis.labelFont, 340 cairo.FONT_SLANT_NORMAL, 341 cairo.FONT_WEIGHT_NORMAL) 342 cx.set_font_size(self.options.axis.labelFontSize) 343 344 305 345 if not self.options.axis.y.hide: 306 346 if self.yticks: 307 347 def drawYLabel(tick): … … 321 361 extents = cx.text_extents(label) 322 362 labelWidth = extents[2] 323 363 labelHeight = extents[3] 324 cx.move_to(x - self.options.axis.tickSize - labelWidth - 5,325 y + labelHeight / 2.0)326 cx.show_text(label)327 364 365 if self.options.axis.y.rotate: 366 radians = math.radians(self.options.axis.y.rotate) 367 cx.move_to( 368 x - self.options.axis.tickSize 369 - (labelWidth * math.cos(radians)) 370 - 4, 371 y + (labelWidth * math.sin(radians)) 372 + labelHeight / (2.0/math.cos(radians)) 373 ) 374 cx.rotate(-radians) 375 cx.show_text(label) 376 cx.rotate(radians) 377 else: 378 cx.move_to( 379 x - self.options.axis.tickSize - labelWidth - 4, 380 y + labelHeight / 2.0) 381 cx.show_text(label) 328 382 return label 383 329 384 for tick in self.yticks: 330 385 drawYLabel(tick) 331 386 387 if self.options.axis.y.label: 388 tickExtents = [cx.text_extents(unicode(tick[1]))[2:4] 389 for tick in self.yticks] 390 tickWidth = 0.0 391 if tickExtents: 392 tickWidth = self.options.axis.tickSize + 4.0 393 widths, heights = zip(*tickExtents) 394 max_width, max_height = max(widths), max(heights) 395 if self.options.axis.y.rotate: 396 radians = math.radians(self.options.axis.y.rotate) 397 max_width = ( 398 (max_width * math.cos(radians)) 399 + (max_height * math.sin(radians)) ) 400 tickWidth += max_width 401 402 cx.new_path() 403 label = unicode(self.options.axis.y.label) 404 labelWidth = cx.text_extents(label)[2] 405 x = self.area.x - tickWidth - 4.0 406 y = self.area.y + .5 * self.area.h + labelWidth / 2 407 cx.move_to(x, y) 408 cx.select_font_face(self.options.axis.labelFont, 409 cairo.FONT_SLANT_NORMAL, 410 cairo.FONT_WEIGHT_BOLD) 411 radians = math.radians(90) 412 cx.rotate(-radians) 413 cx.show_text(label) 414 cx.rotate(radians) 415 cx.select_font_face(self.options.axis.labelFont, 416 cairo.FONT_SLANT_NORMAL, 417 cairo.FONT_WEIGHT_NORMAL) 418 332 419 cx.new_path() 333 420 cx.move_to(self.area.x, self.area.y) 334 421 cx.line_to(self.area.x, self.area.y + self.area.h) … … 337 424 338 425 if not self.options.axis.x.hide: 339 426 if self.xticks: 427 fontAscent = cx.font_extents()[0] 340 428 def drawXLabel(tick): 341 429 if callable(tick): 342 430 return … … 349 437 cx.line_to(x, y + self.options.axis.tickSize) 350 438 cx.close_path() 351 439 cx.stroke() 440 cx.fill() 352 441 442 cx.new_path() 353 443 label = unicode(tick[1]) 354 444 extents = cx.text_extents(label) 355 445 labelWidth = extents[2] 356 446 labelHeight = extents[3] 357 cx.move_to(x - labelWidth / 2.0, 358 y + self.options.axis.tickSize + 10) 359 cx.show_text(label) 447 if self.options.axis.x.rotate: 448 radians = math.radians(self.options.axis.x.rotate) 449 cx.move_to( 450 x - (labelHeight * math.cos(radians)), 451 y + self.options.axis.tickSize 452 + (labelHeight * math.cos(radians)) 453 + 4.0) 454 cx.rotate(radians) 455 cx.show_text(label) 456 cx.rotate(-radians) 457 else: 458 cx.move_to( 459 x - labelWidth / 2.0, 460 y + self.options.axis.tickSize 461 + fontAscent 462 + 4.0 ) 463 cx.show_text(label) 360 464 return label 465 361 466 for tick in self.xticks: 362 467 drawXLabel(tick) 363 468 469 if self.options.axis.x.label: 470 tickExtents = [cx.text_extents(unicode(tick[1]))[2:4] 471 for tick in self.xticks] 472 tickHeight = 0.0 473 if tickExtents: 474 tickHeight = self.options.axis.tickSize + 4.0 475 widths, heights = zip(*tickExtents) 476 max_width, max_height = max(widths), max(heights) 477 if self.options.axis.x.rotate: 478 radians = math.radians(self.options.axis.x.rotate) 479 max_height = ((max_width * math.sin(radians)) 480 + (max_height * math.cos(radians)) ) 481 tickHeight += max_height 482 483 cx.new_path() 484 label = unicode(self.options.axis.x.label) 485 cx.select_font_face(self.options.axis.labelFont, 486 cairo.FONT_SLANT_NORMAL, 487 cairo.FONT_WEIGHT_BOLD) 488 labelWidth = cx.text_extents(label)[2] 489 fontAscent = cx.font_extents()[0] 490 x = self.area.x + self.area.w / 2.0 - labelWidth / 2.0 491 y = self.area.y + self.area.h + tickHeight + fontAscent + 4.0 492 cx.move_to(x, y) 493 cx.show_text(label) 494 cx.select_font_face(self.options.axis.labelFont, 495 cairo.FONT_SLANT_NORMAL, 496 cairo.FONT_WEIGHT_NORMAL) 497 364 498 cx.new_path() 365 499 cx.move_to(self.area.x, self.area.y + self.area.h) 366 500 cx.line_to(self.area.x + self.area.w, self.area.y + self.area.h) … … 456 590 tickCount=10, 457 591 tickPrecision=1, 458 592 range=None, 593 rotate=None, 594 label=None, 459 595 ), 460 596 y=Option( 461 597 hide=False, … … 463 599 tickCount=10, 464 600 tickPrecision=1, 465 601 range=None, 602 rotate=None, 603 label=None, 466 604 ), 467 605 ), 468 606 background=Option( 469 color='#f5f5f5',470 607 hide=False, 608 baseColor=None, 609 chartColor='#f5f5f5', 471 610 lineColor='#ffffff', 472 611 lineWidth=1.5, 473 612 ), … … 497 636 yOriginIsZero=True, 498 637 pieRadius=0.4, 499 638 colorScheme=DEFAULT_COLOR, 639 title=None, 640 titleFont='Tahoma', 641 titleFontSize=12, 500 642 )
