piddleTK.py :  » GUI » piddle » piddle-1.0.15 » src » piddle » piddleTK2 » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » GUI » piddle 
piddle » piddle 1.0.15 » src » piddle » piddleTK2 » piddleTK.py
""" 
A Tkinter based backend for piddle. 
  
Perry A. Stoll 
  
Created: February 15, 1999 
  
Requires PIL for rotated string support.

Known Problems: 
     - Doesn't handle the interactive commands yet.
     - PIL based canvas inherits lack of underlining strings from piddlePIL
     
You can find the latest version of this file:
    via http://piddle.sourceforge.net
""" 

import Tkinter, tkFont
tk = Tkinter
import piddle 
import string
  
__version__ = "0.3" 
__date__    = "April 8, 1999" 
__author__  = "Perry Stoll,  perry.stoll@mail.com " 

# fixups by chris lee, cwlee@artsci.wustl.edu
# $Id: piddleTK.py,v 1.1 2002/02/09 00:17:29 clee Exp $
# - added drawImage scaling support
# - shifted baseline y parameter in drawString to work around font metric
#         shift due to Tkinter's Canvas text_item object
# - fixed argument names so that argument keywords agreed with piddle.py (passes discipline.py)
#
#
# ToDo: for BaseTKCanvas
#         make sure that fontHeight() is returnng appropriate measure.  Where is this info?
#     
# $Log: piddleTK.py,v $
# Revision 1.1  2002/02/09 00:17:29  clee
# initial checkin
#
# Revision 1.1  2000/04/19 20:49:32  pmagwene
# piddleTK subdirectory organization
#
# Revision 1.6  2000/04/06 01:55:34  pmagwene
# - TKCanvas now uses multiple inheritance from Tkinter.Canvas and piddle.Canvas
#   * for the most part works much like a normal Tkinter.Canvas object
# - TKCanvas draws rotated strings using PIL image, other objects using normal Tk calls
# - Minor fixes to FontManager and TKCanvas so can specify root window other than Tk()
# - Removed Quit/Clear buttons from default canvas
#
# Revision 1.5  2000/03/12 07:07:42  clee
# sync with 1_x
#
# Revision 1.4  2000/02/26 23:12:42  clee
# turn off compression by default on piddlePDF
# add doc string to new pil-based piddleTK
#
# Revision 1.3  2000/02/26 21:23:19  clee
# update that makes PIL based TKCanvas the default Canvas for TK.
# Updated piddletest.py.  Also, added clear() methdo to piddlePIL's
# canvas it clears to "white" is this correct behavior?  Not well
# specified in current documents.
#

class FontManager:
    __alt_faces = { "serif": "Times",
                    "sansserif": "Helvetica",
                    "monospaced": "Courier"}
    def __init__(self, master):
        self.master = master
        self.font_cache = {}

    # the main interface
    def stringWidth(self, s, font):
        tkfont = self.piddleToTkFont(font)
        return tkfont.measure(s)

    def fontHeight(self, font):
        tkfont = self.piddleToTkFont(font)
        return self._tkfontHeight(tkfont) 
    
    def fontAscent(self, font):
        tkfont = self.piddleToTkFont(font)
        return self._tkfontAscent(tkfont)

    def fontDescent(self, font):
        tkfont = self.piddleToTkFont(font)
        return self._tkfontDescent(tkfont)

    def getTkFontString(self, font):
        """Return a string suitable to pass as the -font option to
        to a Tk widget based on the piddle-style FONT"""
        tkfont = self.piddleToTkFont(font)
        # XXX: should just return the internal tk font name?
        # return str(tkfont)
        return ('-family %(family)s -size %(size)s '
                '-weight %(weight)s -slant %(slant)s '
                '-underline %(underline)s' % tkfont.config())
    

    def getTkFontName(self, font):
        """Return a the name associated with the piddle-style FONT"""
        tkfont = self.piddleToTkFont(font)
        return str(tkfont)

    def piddleToTkFont(self, font): 
        """Return a tkFont instance based on the piddle-style FONT"""
        if font is None: 
            return ''

        #default 12 pt, "Times", non-bold, non-italic
        size = 12
        family = "Times"
        weight = "normal"
        slant = "roman"
        underline = "false"

        if font.face:
            # check if the user specified a generic face type
            # like serif or monospaced. check is case-insenstive.
            f = string.lower(font.face)
            if self.__alt_faces.has_key(f):
                family = self.__alt_faces[f]
            else:
                family = font.face
            
        size = font.size or 12
        
        if font.bold:
            weight = "bold"
        if font.italic:
            slant = "italic"
        if font.underline:
            underline = 'true'

        # ugh... is there a better way to do this?
        key = (family,size,weight,slant,underline)

        # check if we've already seen this font.
        if self.font_cache.has_key(key):
            # yep, don't bother creating a new one. just fetch it.
            font = self.font_cache[key]
        else:
            # nope, let's create a new tk font.
            # this way we will return info about the actual font
            # selected by Tk, which may be different than what we ask
            # for if it's not availible.
            font = tkFont.Font(self.master, family=family, size=size, weight=weight,
                               slant=slant,underline=underline)
            self.font_cache[(family,size,weight,slant,underline)] = font
        
        return font

    def _tkfontAscent(self, tkfont):
        return tkfont.metrics("ascent")

    def _tkfontDescent(self, tkfont):
        return tkfont.metrics("descent")


class BaseTKCanvas(tk.Canvas, piddle.Canvas): 

    __TRANSPARENT = ''                  # transparent for Tk color
    def __init__(self, size=(300,300), name="piddleTK", master = None): 
        piddle.Canvas.__init__(self, size, name)
        tk.Canvas.__init__(self, master,
                           height = size[0],width=size[1])
        self.config(background="white")
        self.width, self.height = size
        
        self._font_manager = FontManager(self)
        self._configure()
        self._item_ids = []
        self._images = []
        
    def _configure(self):
        pass
          
    def _display(self): 
        self.flush()
        self.mainloop() 
  
    def _quit(self): 
        self.quit() 

    # Hmmm...the postscript generated by this causes my Ghostscript to barf... 
    def _to_ps_file(self, filename): 
        self.postscript(file=filename)

    def isInteractive(self):
        return 0

    def onOver(self, event):
        pass

    def onClick(self, event):
        pass

    def onKey(self, event):
        pass
    
    def flush(self): 
        tk.Canvas.update(self) 
  
    def clear(self): 
        map(self.delete,self._item_ids) 
        self._item_ids = [] 
        
    def _colorToTkColor(self, c): 
        return "#%02X%02X%02X" %( int(c.red*255), 
                                  int(c.green*255), 
                                  int(c.blue*255)) 
  
    def _getTkColor(self, color, defaultColor): 
        if color is None: 
            color = defaultColor 
        if color is piddle.transparent: 
            color = self.__TRANSPARENT
        else:
            color = self._colorToTkColor(color) 
        return color 
  
    def drawLine(self, x1, y1, x2, y2, color = None, width = None): 
        color = self._getTkColor(color, self.defaultLineColor) 
        if width is None: 
            width  = self.defaultLineWidth 
        new_item = self.create_line(x1,y1,x2,y2,
                                    fill=color, width=width) 
        self._item_ids.append(new_item) 
  
    # NYI: curve with fill 
    #def drawCurve(self, x1, y1, x2, y2, x3, y3, x4, y4, 
    #              edgeColor=None, edgeWidth=None, fillColor=None, closed=0): 
    # 

    def stringWidth(self, s, font=None):
        return self._font_manager.stringWidth(s, font or self.defaultFont)
    
    def fontAscent(self, font=None):
        return self._font_manager.fontAscent(font or self.defaultFont)

    def fontDescent(self, font=None):
        return self._font_manager.fontDescent(font or self.defaultFont)

    def drawString(self, s, x, y, font=None, color=None, angle=None):
        if angle:
            try:
                self._drawRotatedString(s,x,y,font,color,angle)
                return
            except ImportError:
                print("PIL not available. Using unrotated strings.")
        # fudge factor for TK on linux (at least)
        # strings are being drawn using create_text in canvas
        y = y - self.fontHeight(font) * .28 # empirical
        #y = y - self.fontDescent(font)
    
        color = self._getTkColor(color, self.defaultLineColor)
        font  = self._font_manager.getTkFontString(font or self.defaultFont) 
        new_item = self.create_text(x, y, text=s, 
                                    font=font, fill=color, 
                                    anchor=Tkinter.W) 
        self._item_ids.append(new_item)

    def _drawRotatedString(self, s, x,y, font=None, color=None, angle=0):
        try:
            import piddlePIL, Image, ImageTk
            pp = piddlePIL
        except ImportError:
            raise ImportError("Rotated strings only possible with PIL support")

        pilCan = pp.PILCanvas(size=(self.width,self.height))
        pilCan.defaultFont = self.defaultFont
        pilCan.defaultLineColor = self.defaultLineColor
        
        if '\n' in s or '\r' in s:
            self.drawMultiLineString(s, x,y, font, color, angle)
            return
        if not font: font = pilCan.defaultFont
  
        if not color:
            color = self.defaultLineColor
        if color == piddle.transparent: return

        # draw into an offscreen Image
        tempsize = pilCan.stringWidth(s, font) * 1.2
        tempimg = Image.new('RGB',(tempsize,tempsize), (0,0,0))
        txtimg = Image.new('RGB',(tempsize,tempsize), (255,255,255))

        import ImageDraw
        temppen = ImageDraw.ImageDraw(tempimg)
        temppen.setink( (255,255,255) )
        pilfont = pp._pilFont(font)

        if not pilfont: raise "bad font!", font

        temppen.setfont( pilfont )
        pos = [4, int(tempsize/2 - pilCan.fontAscent(font)) - pilCan.fontDescent(font)]
        temppen.text( pos, s )
        pos[1] = int(tempsize/2)
        
        # rotate
        if angle:
                from math import pi,sin,cos
                tempimg = tempimg.rotate( angle, Image.BILINEAR )
                temppen = ImageDraw.ImageDraw(tempimg)
                radians = -angle * pi/180.0
                r = tempsize/2 - pos[0]
                pos[0] = int(tempsize/2 - r * cos(radians))
                pos[1] = int(pos[1] - r * sin(radians))
                
        ###temppen.rectangle( (pos[0],pos[1],pos[0]+2,pos[1]+2) ) # PATCH for debugging
        # colorize, and copy it in
        mask = tempimg.convert('L').point(lambda c:c)
        temppen.setink( (color.red*255, color.green*255, color.blue*255) )
        temppen.setfill(1)
        temppen.rectangle( (0,0,tempsize,tempsize) )

        txtimg.paste( tempimg, (0,0), mask)
        
        ##Based on code posted by John Michelson in the PIL SIG
        transp = txtimg.convert("RGBA")
        source = transp.split()
        R, G, B, A = 0, 1, 2, 3
        mask = transp.point(lambda i: i < 255 and 255) # use white as transparent
        source[A].paste(mask)
        transp = Image.merge(transp.mode, source)  # build a new multiband image

        self.drawImage(transp, x-pos[0], y-pos[1])


  
    def drawRect(self, x1, y1, x2, y2, edgeColor=None, 
                 edgeWidth=None, fillColor=None): 
        fillColor = self._getTkColor(fillColor, self.defaultFillColor) 
        edgeColor = self._getTkColor(edgeColor, self.defaultLineColor) 
        if edgeWidth is None: 
            edgeWidth  = self.defaultLineWidth 
        new_item = self.create_rectangle(x1,y1,x2,y2, 
                                        fill=fillColor, 
                                        width=edgeWidth, 
                                        outline=edgeColor) 
        self._item_ids.append(new_item) 
          
    # NYI: 
    #def drawRoundRect(self, x1,y1, x2,y2, rx=5, ry=5, 
    #                  edgeColor=None, edgeWidth=None, fillColor=None): 
  
  
    def drawEllipse(self, x1,y1, x2,y2, 
                    edgeColor=None, edgeWidth=None, fillColor=None): 
        fillColor = self._getTkColor(fillColor, self.defaultFillColor) 
        edgeColor = self._getTkColor(edgeColor, self.defaultLineColor) 
        if edgeWidth is None: 
            edgeWidth  = self.defaultLineWidth 
        new_item = self.create_oval(x1, y1, x2, y2, 
                                    fill=fillColor, 
                                    outline=edgeColor,
                                    width=edgeWidth)
        
        self._item_ids.append(new_item) 
  
    def drawArc(self, x1, y1, x2, y2, startAng=0, extent=360, 
                edgeColor=None, edgeWidth=None, fillColor=None): 
        fillColor = self._getTkColor(fillColor, self.defaultFillColor) 
        edgeColor = self._getTkColor(edgeColor, self.defaultLineColor) 
        if edgeWidth is None: 
            edgeWidth  = self.defaultLineWidth 
        new_item = self.create_arc( x1, y1, x2, y2, 
                                     start=startAng, 
                                     extent=extent, 
                                     fill=fillColor, 
                                     width=edgeWidth,
                                     outline=edgeColor) 
        self._item_ids.append(new_item) 
  
    def drawPolygon(self, pointlist, 
                    edgeColor=None, edgeWidth=None, fillColor=None, closed=0): 
        fillColor = self._getTkColor(fillColor, self.defaultFillColor) 
        edgeColor = self._getTkColor(edgeColor, self.defaultLineColor) 
        if edgeWidth is None: 
            edgeWidth  = self.defaultLineWidth 
        if closed:
            # draw a closed shape
            new_item = self.create_polygon( pointlist, 
                                            fill=fillColor, 
                                            width=edgeWidth,
                                            outline=edgeColor)
        else:
            if fillColor == self.__TRANSPARENT:
                # draw open-ended set of lines
                d = { 'fill':edgeColor, 'width': edgeWidth}
                new_item = apply(self.create_line, pointlist, d)
            else:
                # open filled shape.
                # draw it twice:
                #    once as a polygon with no edge outline with the fill color
                #    and once as an open set of lines of the appropriate color
                new_item = self.create_polygon( pointlist, 
                                                fill=fillColor, 
                                                outline=self.__TRANSPARENT)
                
                self._item_ids.append(new_item)
                
                d = { 'fill':edgeColor, 'width': edgeWidth}
                new_item = apply(self.create_line, pointlist, d)
                                                   
        self._item_ids.append(new_item) 
  
    
    #def drawFigure(self, partList, 
    #               edgeColor=None, edgeWidth=None, fillColor=None):
    # use default implementation
          
    def drawImage(self, image, x1, y1, x2=None,y2=None):

        try:
            import ImageTk
        except ImportError:
            raise NotImplementedError('drawImage - require the ImageTk module')

        w,h = image.size
        if not x2 :
            x2 = w + x1
        if not y2 :
            y2 = h + y1

        if (w != x2-x1) or (h != y2-y1) :  # need to scale image
            myimage = image.resize((x2-x1,y2-y1))
        else:
            myimage = image
            
        # unless I keep a copy of this PhotoImage, it seems to be garbage collected
        # and the image is removed from the display after this function. weird
        itk = ImageTk.PhotoImage(myimage, master=self)
        new_item = self.create_image(x1, y1, image=itk, anchor=Tkinter.NW)
        self._item_ids.append(new_item) 
        self._images.append(itk)



TKCanvas = BaseTKCanvas

try :
    import piddlePIL

    class TKCanvasPIL(piddlePIL.PILCanvas):

        """This canvas maintains a PILCanvas as its backbuffer.  Drawing calls
        are made to the backbuffer and flush() sends the image to the screen
        using BaseTKCanvas.  
           You can also save what is displayed to a file in any of the formats
        supported by PIL"""
        
        def  __init__(self, size=(300,300), name='TKCanvas',  master = None) :
            piddlePIL.PILCanvas.__init__(self, size=size, name=name)
            self._tkcanvas = BaseTKCanvas(size=size, name=name, master=master)

        def flush(self) :
            piddlePIL.PILCanvas.flush(self) # call inherited one first
            self._tkcanvas.drawImage(self._image, 0,0)  # self._image should be a PIL image
            self._tkcanvas.flush()

except ImportError:
    TKCanvasPIL = BaseTKCanvas





www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.