Ticket #6: chart-v2.diff

File chart-v2.diff, 10.5 kB (added by jae@…, 4 years ago)

chart.py patch (version 2)

  • chart.py

     
    1717 
    1818import copy 
    1919import cairo 
     20import math 
    2021 
    2122from pycha.color import (defaultColorscheme, getColorscheme, hex2rgb, 
    2223                         DEFAULT_COLOR) 
     
    231232                    label = str(tick.v) 
    232233                else: 
    233234                    label = tick.label 
    234                 pos = self.yscale * (tick.v - self.minyval) 
     235                pos = 1.0 - (self.yscale * (tick.v - self.minyval)) 
    235236                if 0.0 <= pos <= 1.0: 
    236237                    self.yticks.append((pos, label)) 
    237238 
     
    252253    # render methods 
    253254    def _renderBackground(self, cx): 
    254255        """Renders the background area of the chart""" 
     256 
    255257        if self.options.background.hide: 
    256258            return 
    257259 
    258260        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) 
    264261 
    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() 
    266269 
     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 
    267280        cx.restore() 
    268281 
    269282    def _renderLines(self, cx): 
     
    302315        cx.set_source_rgb(*hex2rgb(self.options.axis.lineColor)) 
    303316        cx.set_line_width(self.options.axis.lineWidth) 
    304317 
     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 
    305345        if not self.options.axis.y.hide: 
    306346            if self.yticks: 
    307347                def drawYLabel(tick): 
     
    321361                    extents = cx.text_extents(label) 
    322362                    labelWidth = extents[2] 
    323363                    labelHeight = extents[3] 
    324                     cx.move_to(x - self.options.axis.tickSize - labelWidth - 5, 
    325                                y + labelHeight / 2.0) 
    326                     cx.show_text(label) 
    327364 
     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) 
    328382                    return label 
     383 
    329384                for tick in self.yticks: 
    330385                    drawYLabel(tick) 
    331386 
     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 
    332419            cx.new_path() 
    333420            cx.move_to(self.area.x, self.area.y) 
    334421            cx.line_to(self.area.x, self.area.y + self.area.h) 
     
    337424 
    338425        if not self.options.axis.x.hide: 
    339426            if self.xticks: 
     427                fontAscent = cx.font_extents()[0] 
    340428                def drawXLabel(tick): 
    341429                    if callable(tick): 
    342430                        return 
     
    349437                    cx.line_to(x, y + self.options.axis.tickSize) 
    350438                    cx.close_path() 
    351439                    cx.stroke() 
     440                    cx.fill() 
    352441 
     442                    cx.new_path() 
    353443                    label = unicode(tick[1]) 
    354444                    extents = cx.text_extents(label) 
    355445                    labelWidth = extents[2] 
    356446                    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) 
    360464                    return label 
     465 
    361466                for tick in self.xticks: 
    362467                    drawXLabel(tick) 
    363468 
     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 
    364498            cx.new_path() 
    365499            cx.move_to(self.area.x, self.area.y + self.area.h) 
    366500            cx.line_to(self.area.x + self.area.w, self.area.y + self.area.h) 
     
    456590            tickCount=10, 
    457591            tickPrecision=1, 
    458592            range=None, 
     593            rotate=None, 
     594            label=None, 
    459595        ), 
    460596        y=Option( 
    461597            hide=False, 
     
    463599            tickCount=10, 
    464600            tickPrecision=1, 
    465601            range=None, 
     602            rotate=None, 
     603            label=None, 
    466604        ), 
    467605    ), 
    468606    background=Option( 
    469         color='#f5f5f5', 
    470607        hide=False, 
     608        baseColor=None, 
     609        chartColor='#f5f5f5', 
    471610        lineColor='#ffffff', 
    472611        lineWidth=1.5, 
    473612    ), 
     
    497636    yOriginIsZero=True, 
    498637    pieRadius=0.4, 
    499638    colorScheme=DEFAULT_COLOR, 
     639    title=None, 
     640    titleFont='Tahoma', 
     641    titleFontSize=12, 
    500642)