예제 #1
0
    def draw_text(self, g, text, position, x, y, dx, dy, image=None):
        """ Draws a specified string within the specified graphics context using
            the specified *position* and theme information, and positioned
            within the region specified by (x, y, dx, dy ). It will also draw an
            optional image to the left of the text using the same alignment as
            the text.

            A *position* value of None is the same as TOP and LEFT.
        """
        # Set the clipping bounds to the drawable interior region of the theme:
        x, y, dx, dy = g.clipping_bounds = self.bounds(x, y, dx, dy)

        # Calculate the size of the text (if any):
        tdx = tdy = 0
        if text != "":
            lines = text.split("\n")
            sizes = []
            ts = g.text_size
            for line in lines:
                if line.strip() == "":
                    ldx, ldy = ts("|")
                else:
                    ldx, ldy = ts(line)
                    tdx = max(tdx, ldx)

                tdy += ldy
                sizes.append((ldx, ldy))

        # Calculate the size of the image (if any):
        idx = idy = 0
        if image is not None:
            idx = image.width + (4 * (tdx > 0))
            idy = image.height

        # Make sure we have a valid position to use:
        if not isinstance(position, int):
            position = AlignmentMap.get(position, TOP_LEFT)

        # Use the alignment to calculate the 'x' coordinate to draw at:
        if (position & LEFT) != 0:
            tx = bx = x
        elif (position & RIGHT) != 0:
            bx = x + dx
            tx = bx - tdx - idx
        else:
            bx = x + (dx / 2)
            tx = bx - ((tdx + idx) / 2)

        # Use the alignment to calculate the 'y' coordinate to draw at:
        if (position & TOP) != 0:
            ty = iy = y
        elif (position & BOTTOM) != 0:
            ty = iy = y + dy
            ty -= tdy
            iy -= idy
        else:
            my = y + (dy / 2) + 1
            ty = my - (tdy / 2) - 2
            iy = my - (idy / 2)

        # Draw the image (if any):
        if idx > 0:
            g.draw_bitmap(image.bitmap, tx, iy)
            tx += idx

        # Draw the text (if any):
        if tdx > 0:
            g.text_color = self.content_color
            if len(lines) == 1:
                g.draw_text(lines[0], tx, ty)
            else:
                ey = y + dy
                for i, line in enumerate(lines):
                    ldx, ldy = sizes[i]
                    if (i > 0) and (ty + ldy) > ey:
                        break

                    if (position & RIGHT) != 0:
                        tx = bx - ldx
                    elif (position & LEFT) == 0:
                        tx = bx - (ldx / 2)

                    g.draw_text(line, tx, ty)

                    ty += ldy

        # Reset the clipping bounds:
        g.clipping_bounds = None

        # Returns the size of the bounding box for the text and image drawn:
        return (tdx + idx, max(tdy, idy))
예제 #2
0
    def draw_label(self, g, text, position, x, y, dx, dy, image=None):
        """ Draws a specified string within the specified graphics context using
            the specified *position* and theme information, and positioned
            within the region specified by (x, y, dx, dy ). It will also draw an
            optional image to the left of the text using the same alignment as
            the text.

            This is similar to the 'draw_text' method but will only draw a
            single line of text and draws into the label portion of the theme,
            if it has one. If no label area is defined by the theme, then
            nothing is drawn. Returns True if the label was drawn, and False
            otherwise.

            If *position* is None, the default label alignment for the theme is
            used.
        """
        # Get the minimum height needed and exit immediately if there is not
        # enough room to draw the label:
        idx = idy = 0
        if image is not None:
            idx = image.width + 4
            idy = image.height

        g.font = self.label_font
        tdx, tdy = g.text_size(text)
        mdy = max(tdy, idy) + 4
        slice = self.image_slice
        if (slice is None) or (mdy > max(slice.xtop, slice.xbottom)):
            return False

        label = self.label
        xtop = slice.xtop
        xbottom = slice.xbottom
        cl = x + slice.xleft + label.left
        cr = x + dx - slice.xright - label.right
        if ((tdy + label.top + label.bottom) <= xtop) and (xtop >= xbottom):
            by = y + (label.top + xtop - label.bottom) / 2
        else:
            by = y + dy + ((label.top - xbottom - label.bottom) / 2)

        # Calculate the x coordinate for the specified alignment type:
        if position is None:
            position = AlignmentMap.get(self.alignment, CENTER)

        if (position & LEFT) != 0:
            tx = cl
        elif (position & RIGHT) != 0:
            tx = cr - tdx - idx
        else:
            tx = (cl + cr - tdx - idx) / 2

        # Draw the (clipped) image and text string (note that we increase the
        # clipping bounds height a small amount because too tight a bounds can
        # cause clipping of text descenders:
        g.clipping_bounds = (cl, by - (mdy / 2), cr - cl, mdy + 2)
        if idx > 0:
            g.draw_bitmap(image.bitmap, tx, by - (idy / 2) + 1)
            tx += idx

        if tdx > 0:
            g.text_color = self.label_color
            g.draw_text(text, tx, by - (tdy / 2))

        g.clipping_bounds = None

        return True
예제 #3
0
    def draw ( self, g, x, y, dx, dy, visible = None ):
        """ Renders the text (and optional image) using the graphics context *g*
            within the bounds specified by *x*, *y*, *dx*, *dy*. If *visible*
            is not None, it should be a tuple containing the visible bounds of
            the drawing area in the form: (x, y, dx, dy).
        """
        # If we have not yet analyzed the text, do so now:
        if self.info is None:
            self._analyze( g )

        self.tags = tags = []
        end_y     = y + dy
        info      = self.info
        low, high = 0, len( info )
        if (visible is not None) and (high > 0):
            # If there is a visible bounds specified, find the range of visible
            # text fragments we have to render:
            _, vy, _, vdy = visible
            low   = self._info_at( vy - y )
            high  = self._info_at( vy + vdy - y + info[0][ INFO_DY ] )
            end_y = min( end_y, vy + vdy )

        # Get everything ready to go:
        g.clipping_bounds = x, y, dx, dy
        text              = self.full_text
        colors            = self.colors
        alignment         = AlignmentMap.get( self.alignment, TOP_LEFT )

        # Draw the image (if any):
        image = self.image
        if image is not None:
            if alignment & TOP:
                iy = y
            elif alignment & BOTTOM:
                iy = y + dy - image.height
            else:
                iy = y + ((dy - image.height) / 2)

            g.draw_bitmap( image.bitmap, x, iy )
            x += (image.width + 4)

        # Calculate the text starting y point based on the alignment:
        bdy = self.bounds[1]
        if alignment & BOTTOM:
            y += dy - bdy
        elif (alignment & TOP) == 0:
            y += (dy - bdy - 1) / 2

        # Render all of the selected text fragments:
        for i in xrange( low, high ):
            start, end, fy, fdx, fdy, tdx, color_index, tag_index = info[ i ]

            # Get the text y coordinate and exit if we are past the end of the
            # region being drawn into:
            ty = y + fy
            if ty >= end_y:
                break

            # Check for the start of a new line:
            if tdx >= 0:
                cdx = tdx
                cx  = 0

            # Calculate the x coordinate based on the alignment of the text:
            if alignment & LEFT:
                tx = x + cx
            elif alignment & RIGHT:
                tx = x + dx - cdx + cx
            else:
                tx = x + cx + ((dx - cdx) / 2)

            # Set the correct text color (if necessary), then draw the fragment:
            if color_index is not None:
                g.text_color = colors[ color_index ]

            g.draw_text( text[ start: end ], tx, ty )

            # Create the 'tags' table entry (if necessary):
            if tag_index is not None:
                if tag_index >= 0:
                    tags.append( ( tag_index, tx, ty, tx + fdx, ty + fdy ) )

                    # Draw an underline to identify it is a tag:
                    g.opacity = 0.38
                    y0        = ty + fdy - 1
                    g.draw_line( tx, y0, tx + fdx - 1, y0 )
                    g.opacity = 1.0
                else:
                    # Handle the case of a 'separator':
                    y0 = ty + (fdy / 2) + 1
                    g.draw_line( x, y0, x + dx - 1, y0 )

            # Advance to the next text fragment on this line:
            cx += fdx

        # Reset the clipping bounds:
        g.clipping_bounds = None