Show
Ignore:
Timestamp:
09/04/08 15:44:34 (19 months ago)
Author:
stefan
Message:

initial changes

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • nodebox/branches/try-qt-graphics-view/nodebox/graphics/qt.py

    r286 r486  
    33from random import choice, shuffle 
    44 
    5 from PyQt4.QtGui import QPainterPath, QColor, QTransform, QBrush, QPen, QImage, QPrinter, QPainter, QFontMetrics, QFont 
    6 from PyQt4.QtCore import Qt, QSize, QPointF, QRectF 
     5from PyQt4.QtGui import * 
     6from PyQt4.QtCore import Qt, QSize, QSizeF, QPointF, QRectF 
    77from PyQt4.QtSvg import QSvgGenerator 
    88 
    99from nodebox.util import _copy_attr, _copy_attrs 
     10from nodebox.graphics.qgraphics import * 
    1011 
    1112__all__ = [ 
     
    3637CORNER = "corner" 
    3738 
    38 MOVETO = 0 
    39 LINETO = 1 
    40 CURVETO = 2 
    41 CLOSE = 3 
     39MOVETO = QPainterPath.MoveToElement 
     40LINETO = QPainterPath.LineToElement 
     41CURVETO = QPainterPath.CurveToElement 
     42CLOSE = "close" 
    4243 
    4344LEFT = 0 
     
    6768    '_lineheight':    'lineheight', 
    6869} 
    69  
    70 #def _save(): 
    71 #    NSGraphicsContext.currentContext().saveGraphicsState() 
    72  
    73 #def _restore(): 
    74 #    NSGraphicsContext.currentContext().restoreGraphicsState() 
    7570 
    7671class NodeBoxError(Exception): pass 
     
    201196    strokewidth = property(_get_strokewidth, _set_strokewidth) 
    202197 
     198 
    203199class BezierPath(Grob, TransformMixin, ColorMixin): 
    204200    """A BezierPath provides a wrapper around QPainterPath.""" 
     
    212208        ColorMixin.__init__(self, **kwargs) 
    213209        self._segment_cache = None 
     210        self._qpath_segment_cache = None 
    214211        if path is None: 
    215212            self._qpath = QPainterPath() 
     
    237234    def moveto(self, x, y): 
    238235        self._segment_cache = None 
     236        self._qpath_segment_cache = None 
    239237        self._qpath.moveTo(x, y) 
    240238 
    241239    def lineto(self, x, y): 
    242240        self._segment_cache = None 
     241        self._qpath_segment_cache = None 
    243242        self._qpath.lineTo(x, y) 
    244243 
    245244    def curveto(self, x1, y1, x2, y2, x3, y3): 
    246245        self._segment_cache = None 
     246        self._qpath_segment_cache = None 
    247247        self._qpath.cubicTo(x1, y1, x2, y2, x3, y3) 
    248248 
    249249    def closepath(self): 
    250250        self._segment_cache = None 
     251        self._qpath_segment_cache = None 
    251252        self._qpath.closeSubpath() # XXX: Is this correct? 
    252253 
     
    272273    def rect(self, x, y, width, height): 
    273274        self._segment_cache = None 
     275        self._qpath_segment_cache = None 
    274276        self._qpath.addRect(x, y, width, height) 
    275277         
    276278    def oval(self, x, y, width, height): 
    277279        self._segment_cache = None 
     280        self._qpath_segment_cache = None 
    278281        self._qpath.addEllipse(x, y, width, height) 
    279282         
    280283    def line(self, x1, y1, x2, y2): 
    281284        self._segment_cache = None 
     285        self._qpath_segment_cache = None 
    282286        self._qpath.moveTo(x1, y1) 
    283287        self._qpath.lineTo(x2, y2) 
     
    286290 
    287291    def __getitem__(self, index): 
    288         el = self._qpath.elementAt(index) 
    289         return PathElement(el) 
     292        if self._qpath_segment_cache == None: 
     293            self._set_qpath_segment_cache() 
     294        cmd, el = self._qpath_segment_cache[index] 
     295        return PathElement(cmd, el) 
    290296 
    291297    def __iter__(self): 
     
    294300 
    295301    def __len__(self): 
    296         return self._qpath.elementCount() 
     302        if self._qpath_segment_cache == None: 
     303            self._set_qpath_segment_cache() 
     304        return len(self._qpath_segment_cache) 
     305 
     306    def _set_qpath_segment_cache(self): 
     307        self._qpath_segment_cache = [] 
     308        length = self._qpath.elementCount() 
     309        temp = [] 
     310        for i in range(length): 
     311            el = self._qpath.elementAt(i) 
     312            if el.type in [LINETO, MOVETO, CURVETO, CLOSE]: 
     313                temp.append(i) 
     314        for i in temp: 
     315            el = self._qpath.elementAt(i) 
     316            if el.type in [LINETO, MOVETO, CLOSE]: 
     317                self._qpath_segment_cache.append((el.type, ((el.x, el.y),))) 
     318            elif el.type == CURVETO: 
     319                ctrl1 = self._qpath.elementAt(i+1) 
     320                ctrl2 = self._qpath.elementAt(i+2) 
     321                self._qpath_segment_cache.append((el.type, ((el.x, el.y), (ctrl1.x, ctrl1.y), (ctrl2.x, ctrl2.y)))) 
    297322 
    298323    def extend(self, pathElements): 
    299324        self._segment_cache = None 
     325        self._qpath_segment_cache = None 
    300326        for el in pathElements: 
    301327            if isinstance(el, (list, tuple)): 
     
    313339    def append(self, el): 
    314340        self._segment_cache = None 
     341        self._qpath_segment_cache = None 
    315342        if el.cmd == MOVETO: 
    316343            self.moveto(el.x, el.y) 
     
    344371    transform = property(_get_transform) 
    345372 
    346     def _draw(self, painter): 
    347         painter.save() 
    348         self.transform.concat(painter) 
     373    def _draw(self): 
     374        item = GraphicsPathItem() 
     375        self.transform.concat(item) 
     376        item.setPath(self._qpath) 
    349377        if self._fillcolor: 
    350             painter.fillPath(self._qpath, QBrush(self._fillcolor._rgb)) 
     378            item.setBrush(QBrush(self._fillcolor._rgb)) 
    351379        if self._strokecolor: 
    352380            p = QPen(QBrush(self._strokecolor._rgb), self._strokewidth) 
    353             painter.setPen(p) 
    354             painter.drawPath(self._qpath) 
    355         painter.restore() 
     381            item.setPen(p) 
     382        else: 
     383            item.setPen(QPen(Qt.NoPen)) 
     384        self._scene.addItem(item) 
     385         
     386    def fit(self, x=None, y=None, width=None, height=None, stretch=False): 
     387 
     388        """Fits this path to the specified bounds. 
     389     
     390        All parameters are optional; if no parameters are specified, nothing will happen. 
     391        Specifying a parameter will constrain its value: 
     392     
     393        - x: The path will be positioned at the specified x value 
     394        - y: The path will be positioned at the specified y value 
     395        - width: The path will be of the specified width 
     396        - height: The path will be of the specified height 
     397        - stretch: If both width and height are defined, either stretch the path or 
     398                keep the aspect ratio. 
     399        """ 
     400 
     401        (px, py), (pw, ph) = self.bounds 
     402        t = Transform() 
     403        if x is not None and y is None: 
     404            t.translate(x, py) 
     405        elif x is None and y is not None: 
     406            t.translate(px, y) 
     407        elif x is not None and y is not None: 
     408            t.translate(x, y) 
     409        else: 
     410            t.translate(px, py) 
     411        if width is not None and height is None: 
     412            t.scale(width / pw) 
     413        elif width is None and height is not None: 
     414            t.scale(height / ph) 
     415        elif width is not None and height is not None: 
     416            if stretch: 
     417                t.scale(width /pw, height / ph) 
     418            else: 
     419                t.scale(min(width /pw, height / ph)) 
     420        t.translate(-px, -py) 
     421        self._qpath = t.transformBezierPath(self)._qpath 
     422        self._qpath_segment_cache = None 
    356423         
    357424    ### Mathematics ### 
     
    394461    def addpoint(self, t): 
    395462        import bezier 
    396         self._nsBezierPath = bezier.insert_point(self, t)._nsBezierPath 
     463        self._qpath = bezier.insert_point(self, t)._qpath 
    397464        self._segment_cache = None 
     465        self._qpath_segment_cache = None 
     466 
     467    ### Clipping operations ### 
     468 
     469    def intersects(self, other): 
     470        return self._qpath.intersects(other._qpath) 
     471 
     472    def union(self, other, flatness=0.6): 
     473        return BezierPath(self._ctx, self._qpath.united(other._qpath)) 
     474 
     475    def intersect(self, other, flatness=0.6): 
     476        return BezierPath(self._ctx, self._qpath.intersected(other._qpath)) 
     477 
     478    def difference(self, other, flatness=0.6): 
     479        return BezierPath(self._ctx, self._qpath.subtracted(other._qpath)) 
     480 
     481    def xor(self, other, flatness=0.6): 
     482        union = self._qpath.united(other._qpath) 
     483        intersection = self._qpath.intersected(other._qpath) 
     484        return BezierPath(self._ctx, union.subtracted(intersection)) 
    398485 
    399486class PathElement(object): 
     
    446533        return not self.__eq__(other) 
    447534 
     535 
    448536class ClippingPath(Grob): 
    449537 
     
    457545         
    458546    def _draw(self): 
    459         _save() 
    460547        cp = self.path.transform.transformBezierPath(self.path) 
    461         cp._nsBezierPath.addClip() 
     548        item = GraphicsClipItem(self._scene) 
     549        self._scene.addItem(item) 
     550        item.setPath(cp._qpath)         
     551 
    462552        for grob in self._grobs: 
     553            grob._scene = item 
    463554            grob._draw() 
    464         _restore() 
    465555 
    466556class Rect(BezierPath): 
     
    468558    def __init__(self, ctx, x, y, width, height, **kwargs): 
    469559        warnings.warn("Rect is deprecated. Use BezierPath's rect method.", DeprecationWarning, stacklevel=2) 
    470         r = (x,y), (width,height) 
    471         super(Rect, self).__init__(ctx, NSBezierPath.bezierPathWithRect_(r), **kwargs) 
     560        p=QPainterPath() 
     561        p.addRect(x, y, width, height) 
     562        super(Rect, self).__init__(ctx, p, **kwargs) 
    472563 
    473564    def copy(self): 
     
    478569    def __init__(self, ctx, x, y, width, height, **kwargs): 
    479570        warnings.warn("Oval is deprecated. Use BezierPath's oval method.", DeprecationWarning, stacklevel=2) 
    480         r = (x,y), (width,height) 
    481         super(Oval, self).__init__(ctx, NSBezierPath.bezierPathWithOvalInRect_(r), **kwargs) 
     571        p=QPainterPath() 
     572        p.addEllipse(x, y, width, height) 
     573        super(Oval, self).__init__(ctx, p, **kwargs) 
    482574 
    483575    def copy(self): 
    484576        raise NotImplementedError, "Please don't use Oval anymore" 
    485  
     577         
    486578class Color(object): 
    487579 
     
    519611            args = self._normalizeList(args) 
    520612            h, s, b = args 
     613            if h == 1.0: 
     614                h = .99998 
    521615            clr = QColor.fromHsvF(h, s, b, 1) 
    522616        elif params == 4 and self._ctx._colormode == RGB: # RGB and alpha 
     
    527621            args = self._normalizeList(args) 
    528622            h, s, b, a = args 
     623            if h == 1.0: 
     624                h = .99998 
    529625            clr = QColor.fromHsvF(h, s, b, a) 
    530626        elif params == 4 and self._ctx._colormode == CMYK: # CMYK, no alpha 
     
    557653    qColor = property(_get_qColor) 
    558654         
    559  
    560655    def copy(self): 
    561656        new = self.__class__(self._ctx) 
    562657        new._rgb = QColor(self._rgb) 
    563         new._updateCmyk() 
     658#        new._updateCmyk() 
    564659        return new 
    565660 
     
    573668 
    574669    def _get_hue(self): 
    575         return self._rgb.hueF() 
     670        hue = self._rgb.hueF() 
     671        if round(hue, 4) == 1.0: 
     672            return 1.0 
     673        if hue < 0.0: 
     674            return 0.0 
     675        return hue 
    576676    def _set_hue(self, val): 
    577677        val = self._normalize(val) 
    578         c = self._rgb 
    579         h, s, b, a = c.hueF(), c.saturationF(), c.valueF(), c.alpha() 
     678        if val == 1.0: 
     679           val = .99998 
     680        h, s, b, a = self.hsba 
    580681        self._rgb.setHsvF(val, s, b, a) 
    581682        self._updateCmyk() 
    582683    h = hue = property(_get_hue, _set_hue, doc="the hue of the color") 
     684 
     685    def _get_saturation(self): 
     686        return self._rgb.saturationF() 
     687    def _set_saturation(self, val): 
     688        val = self._normalize(val) 
     689        h, s, b, a = self.hsba 
     690        self._rgb.setHsvF(h, val, b, a) 
     691        self._updateCmyk() 
     692    s = saturation = property(_get_saturation, _set_saturation, doc="the saturation of the color") 
     693 
     694    def _get_brightness(self): 
     695        return self._rgb.valueF() 
     696    def _set_brightness(self, val): 
     697        val = self._normalize(val) 
     698        h, s, b, a = self.hsba 
     699        self._rgb.setHsvF(h, s, val, a) 
     700        self._updateCmyk() 
     701    v = brightness = property(_get_brightness, _set_brightness, doc="the saturation of the color") 
     702 
     703    def _get_hsba(self): 
     704        c = self._rgb 
     705        return c.hueF(), c.saturationF(), c.valueF(), c.alphaF() 
     706    def _set_hsba(self, values): 
     707        values = self._normalizeList(values) 
     708        h, s, b, a = values 
     709        self._rgb.setHsvF(h, s, b, a) 
     710    hsba = property(_get_hsba, _set_hsba, doc="the hue, saturation, brightness and alpha of the color") 
     711 
     712    def _get_red(self): 
     713        return self._rgb.redF() 
     714    def _set_red(self, val): 
     715        val = self._normalize(val) 
     716        r, g, b, a = self.rgba 
     717        self._rgb.setRgbF(val, g, b, a) 
     718        self._updateCmyk()         
     719    r = red = property(_get_red, _set_red, doc="the red component of the color") 
     720 
     721    def _get_green(self): 
     722        return self._rgb.greenF() 
     723    def _set_green(self, val): 
     724        val = self._normalize(val) 
     725        r, g, b, a = self.rgba 
     726        self._rgb.setRgbF(r, val, b, a) 
     727        self._updateCmyk()         
     728    g = green = property(_get_green, _set_green, doc="the green component of the color") 
     729 
     730    def _get_blue(self): 
     731        return self._rgb.blueF() 
     732    def _set_blue(self, val): 
     733        val = self._normalize(val) 
     734        r, g, b, a = self.rgba 
     735        self._rgb.setRgbF(r, g, val, a) 
     736        self._updateCmyk()         
     737    b = blue = property(_get_blue, _set_blue, doc="the blue component of the color") 
     738 
     739    def _get_alpha(self): 
     740        return self._rgb.alphaF() 
     741    def _set_alpha(self, val): 
     742        val = self._normalize(val) 
     743        r, g, b, a = self.rgba 
     744        self._rgb.setRgbF(r, g, b, val) 
     745        self._updateCmyk()         
     746    a = alpha = property(_get_alpha, _set_alpha, doc="the alpha component of the color") 
     747    
     748    def _get_rgba(self): 
     749        c = self._rgb 
     750        return c.redF(), c.greenF(), c.blueF(), c.alphaF() 
     751    def _set_rgba(self, values): 
     752        values = self._normalizeList(values) 
     753        r, g, b, a = values 
     754        self._rgb.setRgbF(r, g, b, a) 
     755    rgba = property(_get_rgba, _set_rgba, doc="the red, green, blue and alpha values of the color") 
     756 
     757    def _get_cyan(self): 
     758        return self._rgb.cyanF() 
     759    def _set_cyan(self, val): 
     760        val = self._normalize(val) 
     761        c, m, y, k, a = self.cmyka 
     762        self._rgb.setCmykF(val, m, y, k, a) 
     763        self._updateRgb()         
     764    c = cyan = property(_get_cyan, _set_cyan, doc="the cyan component of the color") 
     765 
     766    def _get_magenta(self): 
     767        return self._rgb.magentaF() 
     768    def _set_magenta(self, val): 
     769        val = self._normalize(val) 
     770        c, m, y, k, a = self.cmyka 
     771        self._rgb.setCmykF(c, val, y, k, a) 
     772        self._updateRgb()         
     773    m = magenta = property(_get_magenta, _set_magenta, doc="the magenta component of the color") 
     774 
     775    def _get_yellow(self): 
     776        return self._rgb.yellowF() 
     777    def _set_yellow(self, val): 
     778        val = self._normalize(val) 
     779        c, m, y, k, a = self.cmyka 
     780        self._rgb.setCmykF(c, m, val, k, a) 
     781        self._updateRgb()         
     782    y = yellow = property(_get_yellow, _set_yellow, doc="the yellow component of the color") 
     783 
     784    def _get_black(self): 
     785        return self._rgb.blackF() 
     786    def _set_black(self, val): 
     787        val = self._normalize(val) 
     788        c, m, y, k, a = self.cmyka 
     789        self._rgb.setCmykF(c, m, y, val, a) 
     790        self._updateRgb()         
     791    k = black = property(_get_black, _set_black, doc="the black component of the color") 
     792 
     793    def _get_cmyka(self): 
     794        c = self._rgb 
     795        return c.cyanF(), c.magentaF(), c.yellowF(), c.blackF(), c.alphaF() 
     796    cmyka = property(_get_cmyka, doc="a tuple containing the CMYKA values for this color") 
    583797 
    584798    def _normalize(self, v): 
     
    604818        elif isinstance(transform, (list, tuple)): 
    605819            matrix = tuple(transform) 
    606             transform = QTransform() 
    607             transform.setMatrix(*matrix) 
     820            transform = QTransform(*matrix) 
    608821        elif isinstance(transform, QTransform): 
    609822            pass 
     
    622835    def concat(self, painter): 
    623836        trans = painter.transform() 
    624         painter.setTransform(trans * self._qtransform) 
     837        painter.setTransform(self._qtransform * trans) 
    625838 
    626839    def copy(self): 
     
    637850    def _get_matrix(self): 
    638851        q = self._qtransform 
    639         return (q.m11(), q.m12(), q.m13(), q.m21(), q.m22(), q.m23(), q.m31(), q.m32(), q.m33()) 
     852        return (q.m11(), q.m12(), q.m21(), q.m22(), q.m31(), q.m32()) 
    640853    def _set_matrix(self, value): 
    641         self._qtransform.setMatrix(value) 
     854        self._qtransform = QTransform(*value) 
    642855    matrix = property(_get_matrix, _set_matrix) 
    643856 
     
    657870 
    658871    def skew(self, x=0, y=0): 
    659         self._qtransform.shear(x, y) 
     872        import math 
     873        x = math.pi * x / 180. 
     874        y = math.pi * y / 180. 
     875        t = Transform() 
     876        t.matrix = 1, math.tan(y), -math.tan(x), 1, 0, 0 
     877        self.prepend(t) 
    660878 
    661879    def invert(self): 
     
    711929        TransformMixin.__init__(self) 
    712930        if data is not None: 
    713             self._nsImage = QImage(data) 
     931            self._qimage = QImage(data) 
    714932            if self._qimage is None: 
    715933                raise NodeBoxError, "can't read image %r" % path 
     
    731949            if image is None: 
    732950                image = QImage(path) 
     951                pxm = QPixmap.fromImage(image) 
    733952                if image is None: 
    734953                    raise NodeBoxError, "Can't read image %r" % path 
     
    753972 
    754973    def getSize(self): 
    755         return self._nsImage.size() 
     974        return self._qimage.width(), self._qimage.height() 
    756975 
    757976    size = property(getSize) 
    758977 
    759     def _draw(self, painter): 
     978    def _draw(self): 
    760979        """Draw an image on the given coordinates.""" 
    761980 
    762         srcW, srcH = self._qimage.width(), self._qimage.height() 
     981        srcW, srcH = float(self._qimage.width()), float(self._qimage.height()) 
    763982        srcRect = ((0, 0), (srcW, srcH)) 
     983 
     984        if self.debugImage: 
     985            item = QGraphicsRectItem() 
     986        else: 
     987            item = GraphicsImageItem(self._qimage, self.alpha) 
    764988 
    765989        # Width or height given 
     
    771995            elif self.height is not None: 
    772996                factor = self.height / srcH 
    773             painter.save() 
    774997 
    775998            # Center-mode transforms: translate to image center 
     
    7771000                # This is the hardest case: center-mode transformations with given width or height. 
    7781001                # Order is very important in this code. 
    779  
    7801002                # Set the position first, before any of the scaling or transformations are done. 
    7811003                # Context transformations might change the translation, and we don't want that. 
    7821004                t = Transform() 
    7831005                t.translate(self.x, self.y) 
    784                 t.concat(painter) 
    785  
     1006                t.concat(item) 
     1007                 
    7861008                # Set new width and height factors. Note that no scaling is done yet: they're just here 
    787                 # to set the new center of the image according to the scaling factors. 
     1009                # to set the new center of the image according to the scaling factors 
    7881010                srcW = srcW * factor 
    7891011                srcH = srcH * factor 
    7901012 
    791                 # Move image to newly calculated center. 
     1013                # Move image to newly calculated center.                 
    7921014                dX = srcW / 2 
    7931015                dY = srcH / 2 
    7941016                t = Transform() 
    7951017                t.translate(dX, dY) 
    796                 t.concat(painter) 
    797  
     1018                t.concat(item) 
     1019                 
    7981020                # Do current transformation. 
    799                 self._transform.concat(painter) 
    800  
    801                 # Move back to the previous position. 
     1021                self._transform.concat(item) 
     1022 
     1023                # Move back to the previous position.                 
    8021024                t = Transform() 
    8031025                t.translate(-dX, -dY) 
    804                 t.concat(painter) 
    805  
    806                 # Finally, scale the image according to the factors. 
     1026                t.concat(item) 
     1027 
     1028                # Finally, scale the image according to the factors.                 
    8071029                t = Transform() 
    8081030                t.scale(factor) 
    809                 t.concat(painter) 
     1031                t.concat(item)                 
    8101032            else: 
    8111033                # Do current transformation 
    812                 #self._transform.concat() 
     1034                self._transform.concat(item) 
    8131035                # Scale according to width or height factor 
    8141036                t = Transform() 
    8151037                t.translate(self.x, self.y) # Here we add the positioning of the image. 
    8161038                t.scale(factor) 
    817                 t.concat(painter) 
     1039                t.concat(item) 
    8181040 
    8191041            # A debugImage draws a black rectangle instead of an image. 
    8201042            if self.debugImage: 
    821                 painter.setBrush(QBrush(Qt.SolidPattern)) 
    822                 painter.fillRect(QRectF(0, 0, srcW / factor, srcH / factor)) 
    823             else: 
    824                 # TODO: Stuff with composition modes to allow for alpha transparency 
    825                 painter.drawImage(QPointF(0, 0), self._qimage, QRectF(0, 0, srcW, srcH)) 
    826             painter.restore() 
     1043                item.setRect(0, 0, srcW / factor, srcH / factor) 
     1044                item.setPen(QPen(Qt.NoPen)) 
     1045                item.setBrush(QBrush(Qt.black)) 
     1046 
     1047            self._scene.addItem(item)             
    8271048        # No width or height given 
    8281049        else: 
    829             painter.save() 
    8301050            x,y = self.x, self.y 
    8311051            # Center-mode transforms: translate to image center 
     
    8351055                t = Transform() 
    8361056                t.translate(x+deltaX, y+deltaY) 
    837                 t.concat(painter) 
     1057                t.concat(item) 
    8381058                x = -deltaX 
    8391059                y = -deltaY 
    8401060            # Do current transformation 
    841             self._transform.concat(painter) 
     1061            self._transform.concat(item) 
    8421062            # A debugImage draws a black rectangle instead of an image. 
    8431063            if self.debugImage: 
    844                 painter.setBrush(QBrush(Qt.SolidPattern)) 
    845                 painter.fillRect(QRectF(0, 0, srcW, srcH)) 
     1064                item.setRect(x, y, srcW, srcH) 
     1065                item.setPen(QPen(Qt.NoPen)) 
     1066                item.setBrush(QBrush(Qt.black)) 
    8461067            else: 
    847                 # TODO: Stuff with composition modes to allow for alpha transparency 
    848                 painter.drawImage(QPointF(0, 0), self._qimage, QRectF(0, 0, srcW, srcH)) 
    849             painter.restore() 
     1068                t = Transform() 
     1069                t.translate(x, y) 
     1070                t.concat(item) 
     1071            self._scene.addItem(item) 
    8501072 
    8511073class Text(Grob, TransformMixin, ColorMixin): 
     
    8551077 
    8561078    __dummy_color = QColor() 
    857     __alignMap = { LEFT: Qt.AlignLeft, RIGHT: Qt.AlignRight, CENTER: Qt.AlignCenter, JUSTIFY: Qt.AlignJustify } 
     1079    __alignMap = { LEFT: Qt.AlignLeft, RIGHT: Qt.AlignRight, CENTER: Qt.AlignHCenter, JUSTIFY: Qt.AlignJustify } 
    8581080     
    8591081    def __init__(self, ctx, text, x=0, y=0, width=None, height=None, **kwargs): 
     
    8851107 
    8861108    def _get_font(self): 
    887         return QFont(self._fontname, self._fontsize) 
     1109        f = QFont(self._fontname) 
     1110        f.setPointSizeF(self._fontsize / textScaleFactor) 
     1111        return f 
    8881112    _qfont = property(_get_font) 
    8891113 
    890     def _draw(self, painter): 
    891         if self.width is None: 
    892             w = 100000 
    893         else: 
    894             w = self.width 
    895         if self.height is None: 
    896             h = 100000 
    897         else: 
    898             h = self.height 
    899         fm = painter.fontMetrics() 
    900         flags = self.__alignMap[self._align] 
    901         r = fm.boundingRect(self.x, self.y, w, h, flags, self.text) 
    902  
     1114    def _draw(self): 
    9031115        if self._fillcolor is None: return 
    904         x,y = r.x(), r.y() 
     1116        flags = self.__alignMap[self._align] | Qt.TextWordWrap 
     1117        fm = QFontMetricsF(self._qfont)                 
     1118        w = self.width or 100000 
     1119        h = self.height or 100000 
     1120        preferredWidth, preferredHeight = w, h 
     1121        r = fm.boundingRect(QRectF(self.x, self.y, w, h), flags, self.text) 
     1122        x, y = r.x(), r.y() 
    9051123        w, h = r.width(), r.height() 
    906         preferredWidth, preferredHeight = r.width(), r.height() 
    9071124        if self.width is not None: 
    9081125            if self._align == RIGHT: 
     
    9111128                x += preferredWidth/2 - w/2 
    9121129 
    913         painter.save() 
    914         painter.setFont(self._qfont) 
    915         self._fillcolor._set(painter) 
    916         #painter.setBrush(QBrush(self._fillcolor)) 
     1130        textLayout = QTextLayout() 
     1131        textLayout.setFont(self._qfont) 
     1132        textLayout.setText(self.text) 
     1133        opt = QTextOption() 
     1134        opt.setAlignment(self.__alignMap[self._align]) 
     1135        opt.setWrapMode(QTextOption.WrapAtWordBoundaryOrAnywhere) 
     1136        textLayout.setTextOption(opt) 
     1137        textLayout.beginLayout() 
     1138        lineSpacing = (fm.ascent() + fm.descent() + fm.leading()) * self._lineheight 
     1139 
     1140        advance = 0 
     1141        while True: 
     1142            line = textLayout.createLine() 
     1143            if not line.isValid(): 
     1144                break 
     1145            line.setLineWidth(w) 
     1146            line.setPosition(QPointF(0, advance)) 
     1147            advance += lineSpacing 
     1148        textLayout.endLayout() 
     1149 
     1150        item = GraphicsTextItem(textLayout, x, y, w, h) 
     1151        item.setPen(QPen(QBrush(self._fillcolor._rgb), 0)) 
     1152 
    9171153        # Center-mode transforms: translate to image center 
    9181154        if self._transformmode == CENTER: 
     1155            lc = textLayout.lineCount()*lineSpacing 
    9191156            deltaX = w / 2 
    9201157            deltaY = h / 2 
    9211158            t = Transform() 
    922             #t.translate(x+deltaX, y+fm.ascent()+deltaY) 
    923             t.concat(painter) 
    924             self._transform.concat(painter) 
    925             painter.drawText(x, y, w, h, flags, self.text) 
    926             #layoutManager.drawGlyphsForGlyphRange_atPoint_(glyphRange, (-deltaX-dx,-deltaY-dy)) 
    927         else: 
    928             self._transform.concat() 
    929             painter.drawText(x, y, w, h, flags, self.text) 
    930             #layoutManager.drawGlyphsForGlyphRange_atPoint_(glyphRange, (x-dx,y-dy-self.font.defaultLineHeightForFont())) 
    931         painter.restore() 
     1159            t.translate(deltaX,lc/2) 
     1160            t.concat(item) 
     1161            self._transform.concat(item) 
     1162            t = Transform() 
     1163            t.translate(-deltaX,-lc/2) 
     1164            t.concat(item) 
     1165        else: 
     1166            self._transform.concat(item) 
     1167        self._scene.addItem(item) 
    9321168        return (w, h) 
    9331169 
    9341170    def _get_metrics(self): 
    9351171        # TODO: Measure using boundingRect 
    936         flags = self.__alignMap[self._align] 
    937         fm = QFontMetrics(self._qfont) 
    938          
    939         if self.width is None: 
    940             w = 100000 
    941         else: 
    942             w = self.width 
    943         if self.height is None: 
    944             h = 100000 
    945         else: 
    946             h = self.height 
    947  
    948         r = fm.boundingRect(self.x, self.y, w, h, flags, self.text) 
     1172        flags = self.__alignMap[self._align] | Qt.TextWordWrap 
     1173        fm = QFontMetricsF(self._qfont) 
     1174        w = self.width or 100000 
     1175        h = self.height or 100000 
     1176        r = fm.boundingRect(QRectF(self.x, self.y, w, h), flags, self.text) 
    9491177        return r.width(), r.height() 
    9501178    metrics = property(_get_metrics) 
     
    10241252        self.speed = None 
    10251253        self.mousedown = False 
     1254        self._scene = None 
    10261255        self.clear() 
    10271256 
     
    10591288            raise NodeBoxError, "pop: too many canvas pops!" 
    10601289 
    1061     def draw(self, painter): 
     1290    def _setTextScaleFactor(self, factor): 
     1291        global textScaleFactor 
     1292        textScaleFactor = factor 
     1293         
     1294    def draw(self): 
     1295        self._scene.setSceneRect(0,0,self.width,self.height) 
    10621296        if self.background is not None: 
    1063             painter.fillRect(0,0, self.width, self.height, self.background._rgb) 
     1297            self._scene.addRect(QRectF(0,0,self.width,self.height), QPen(Qt.NoPen), QBrush(self.background._rgb)) 
     1298            self._scene.num += 1 
    10641299        for grob in self._grobs: 
    1065             grob._draw(painter) 
     1300            grob._scene = self._scene 
     1301            grob._draw() 
    10661302 
    10671303    def save(self, fname, format=None): 
     
    10731309            svgGen.setFileName(fname) 
    10741310            svgGen.setSize(QSize(self.width, self.height)) 
    1075             painter = QPainter(svgGen) 
    1076             self.draw(painter) 
     1311            painter = QPainter() 
     1312            painter.begin(svgGen) 
     1313            if self._scene: 
     1314                self._scene.render(painter) 
    10771315            painter.end() 
    10781316        elif format == "pdf": 
     
    10811319            printer.setOutputFileName(fname) 
    10821320            printer.setFullPage(True) 
    1083             #printer.setPageSize(QPrinter.Custom) 
    1084             painter = QPainter(printer) 
    1085             self.draw(painter) 
     1321            printer.setPaperSize(QSizeF(self.width, self.height), QPrinter.Point) 
     1322            painter = QPainter() 
     1323            painter.begin(printer) 
     1324            if self._scene: 
     1325                self._scene.render(painter) 
    10861326            painter.end() 
    10871327        elif format in ("png", "tiff", "jpg", "jpeg"): 
    10881328            img = QImage(self.width, self.height, QImage.Format_ARGB32) 
    1089             painter = QPainter(img) 
    1090             painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing) 
    1091             self.draw(painter) 
     1329            painter = QPainter() 
     1330            painter.begin(img) 
     1331            painter.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform) 
     1332            if format in ("jpg", "jpeg"): 
     1333                painter.fillRect(0, 0, self.width, self.height, Qt.white) 
     1334            if self._scene: 
     1335                self._scene.render(painter) 
    10921336            painter.end() 
    10931337            img.save(fname, None, 100)