1# points.py - functions to plot points 2 3# Copyright (C) 2003 Jeremy S. Sanders 4# Email: Jeremy Sanders <jeremy@jeremysanders.net> 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License along 17# with this program; if not, write to the Free Software Foundation, Inc., 18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19############################################################################### 20 21from __future__ import division 22from .. import qtall as qt 23import numpy as N 24 25from ..helpers.qtloops import plotPathsToPainter 26 27from . import colormap 28 29"""This is the symbol plotting part of Veusz 30 31There are actually several different ways symbols are plotted. 32We choose the most appropriate one for the shape: 33 34QPainterPath symbols plotted with _plotPathSymbols 35line symbols are plotted with _plotLineSymbols 36ploygon symbols are plotted wiht _plotPolygonSymbols 37 38Many of these are implemented as paths internally, and drawn using 39QPainterPaths 40""" 41 42####################################################################### 43## draw symbols which are sets of line segments 44 45linesymbols = { 46 'asterisk': ( ((-0.707, -0.707), (0.707, 0.707)), 47 ((-0.707, 0.707), (0.707, -0.707)), 48 ((-1, 0), (1, 0)), ((0, -1), (0, 1)) ), 49 'lineplus': ( ((-1, 0), (1, 0)), ((0, -1), (0, 1)) ), 50 'linecross': ( ((-0.707, -0.707), (0.707, 0.707)), 51 ((-0.707, 0.707), (0.707, -0.707)) ), 52 'plushair': ( ((-1, 0), (-0.5, 0)), ((1, 0), (0.5, 0)), 53 ((0, -1), (0, -0.5)), ((0, 1), (0, 0.5)) ), 54 'crosshair': ( ((-0.707, -0.707), (-0.354, -0.354)), 55 (( 0.707, 0.707), ( 0.354, 0.354)), 56 (( 0.707, -0.707), ( 0.354, -0.354)), 57 ((-0.707, 0.707), (-0.354, 0.354)) ), 58 'asteriskhair': ( ((-1, 0), (-0.5, 0)), ((1, 0), (0.5, 0)), 59 ((0, -1), (0, -0.5)), ((0, 1), (0, 0.5)), 60 ((-0.707, -0.707), (-0.354, -0.354)), 61 (( 0.707, 0.707), ( 0.354, 0.354)), 62 (( 0.707, -0.707), ( 0.354, -0.354)), 63 ((-0.707, 0.707), (-0.354, 0.354)) ), 64 'linehorz': ( ((-1, 0), (1, 0)), ), 65 'linevert': ( ((0, -1), (0, 1)), ), 66 'linehorzgap': ( ((-1, 0), (-0.5, 0)), ((1, 0), (0.5, 0)) ), 67 'linevertgap': ( ((0, -1), (0, -0.5)), ((0, 1), (0, 0.5)) ), 68 69 # arrows 70 'arrowleft': ( ((1, -0.8), (0, 0), (1, 0.8)), ((2, 0), (0, 0)) ), 71 'arrowleftaway': ( ((-1, -0.8), (-2, 0), (-1, 0.8)), ((-2, 0), (0, 0)) ), 72 'arrowright': ( ((-1, -0.8), (0, 0), (-1, 0.8)), ((-2, 0), (0, 0)) ), 73 'arrowrightaway': ( ((1, -0.8), (2, 0), (1, 0.8)), ((2, 0), (0, 0)) ), 74 'arrowup': ( ((-0.8, 1), (0, 0), (0.8, 1)), ((0, 2), (0, 0)) ), 75 'arrowupaway': ( ((-0.8, -1), (0, -2), (0.8, -1)), ((0, 0), (0, -2)) ), 76 'arrowdown': ( ((-0.8, -1), (0, 0), (0.8, -1)), ((0, -2), (0, 0)) ), 77 'arrowdownaway': ( ((-0.8, 1), (0, 2), (0.8, 1)), ((0, 0), (0, 2)) ), 78 79 # limits 80 'limitlower': ( ((-0.8, -1), (0, 0), (0.8, -1)), ((0, -2), (0, 0)), 81 ((-1, 0), (1, 0)) ), 82 'limitupper': ( ((-0.8, 1), (0, 0), (0.8, 1)), ((0, 2), (0, 0)), 83 ((-1, 0), (1, 0)) ), 84 'limitleft': ( ((1, -0.8), (0, 0), (1, 0.8)), ((2, 0), (0, 0)), 85 ((0, -1), (0, 1)) ), 86 'limitright': ( ((-1, -0.8), (0, 0), (-1, 0.8)), ((-2, 0), (0, 0)), 87 ((0, -1), (0, 1)) ), 88 'limitupperaway': ( ((-0.8, -1), (0, -2), (0.8, -1)), ((0, 0), (0, -2)), 89 ((-1, 0), (1, 0)) ), 90 'limitloweraway': ( ((-0.8, 1), (0, 2), (0.8, 1)), ((0, 0), (0, 2)), 91 ((-1, 0), (1, 0)) ), 92 'limitleftaway': ( ((-1, -0.8), (-2, 0), (-1, 0.8)), ((-2, 0), (0, 0)), 93 ((0, -1), (0, 1)) ), 94 'limitrightaway': ( ((1, -0.8), (2, 0), (1, 0.8)), ((2, 0), (0, 0)), 95 ((0, -1), (0, 1)) ), 96 97 'arrowlowerleftaway':( ((-0.8, 1), (0, 2), (0.8, 1)), 98 ((0, 2), (0, 0), (-2, 0)), 99 ((-1, -0.8), (-2, 0), (-1, 0.8)) ), 100 'arrowlowerrightaway': ( ((1, -0.8), (2, 0), (1, 0.8)), 101 ((2, 0), (0, 0), (0, 2)), 102 ((-0.8, 1), (0, 2), (0.8, 1)) ), 103 'arrowupperleftaway':( ((-0.8, -1), (0, -2), (0.8, -1)), 104 ((0, -2), (0, 0), (-2, 0)), 105 ((-1, -0.8), (-2, 0), (-1, 0.8)) ), 106 'arrowupperrightaway': ( ((-0.8, -1), (0, -2), (0.8, -1)), 107 ((2, 0), (0, 0), (0, -2)), 108 ((1, -0.8), (2, 0), (1, 0.8)) ), 109 110 'lineup': ( ((0, 0), (0, -1)), ), 111 'linedown': ( ((0, 0), (0, 1)), ), 112 'lineleft': ( ((0, 0), (-1, 0)), ), 113 'lineright': ( ((0, 0), (1, 0)), ), 114 115 # for arrows 116 '_linearrow': ( ((-1.8, -1), (0, 0), (-1.8, 1)), ), 117 '_linearrowreverse': ( ((1.8, -1), (0, 0), (1.8, 1)), ), 118 } 119 120def getLinePainterPath(name, size): 121 """Get a painter path for line like objects.""" 122 path = qt.QPainterPath() 123 for lines in linesymbols[name]: 124 path.moveTo(lines[0][0]*size, lines[0][1]*size) 125 for x, y in lines[1:]: 126 path.lineTo(x*size, y*size) 127 return path 128 129####################################################################### 130## draw symbols which are polygons 131 132# X and Y pts for corners of polygons 133polygons = { 134 # make the diamond the same area as the square 135 'diamond': ( (0., 1.414), (1.414, 0.), (0., -1.414), (-1.414, 0.) ), 136 'barhorz': ( (-1, -0.5), (1, -0.5), (1, 0.5), (-1, 0.5) ), 137 'barvert': ( (-0.5, -1), (0.5, -1), (0.5, 1), (-0.5, 1) ), 138 'plus': ( (0.4, 1), (0.4, 0.4), (1, 0.4), (1, -0.4), 139 (0.4, -0.4), (0.4, -1), (-0.4, -1), (-0.4, -0.4), 140 (-1, -0.4), (-1, 0.4), (-0.4, 0.4), (-0.4, 1) ), 141 'octogon': ( (0.414, 1), (1, 0.414), (1, -0.414), (0.414, -1), 142 (-0.414, -1), (-1, -0.414), (-1, 0.414), (-0.414, 1) ), 143 'triangle': ( (0, -1.2), (1.0392, 0.6), (-1.0392, 0.6) ), 144 'triangledown': ( (0, 1.2), (1.0392, -0.6), (-1.0392, -0.6) ), 145 'triangleleft': ( (-1.2, 0), (0.6, 1.0392), (0.6, -1.0392) ), 146 'triangleright': ( (1.2, 0), (-0.6, 1.0392), (-0.6, -1.0392) ), 147 'cross': ( (-0.594, 1.1028), (0, 0.5088), (0.594, 1.1028), 148 (1.1028, 0.594), (0.5088, -0), (1.1028, -0.594), 149 (0.594, -1.1028), (-0, -0.5088), (-0.594, -1.1028), 150 (-1.1028, -0.594), (-0.5088, 0), (-1.1028, 0.594) ), 151 'star': ( (0, -1.2), (-0.27, -0.3708), (-1.1412, -0.3708), 152 (-0.4356, 0.1416), (-0.7056, 0.9708), (-0, 0.4584), 153 (0.7056, 0.9708), (0.4356, 0.1416), (1.1412, -0.3708), 154 (0.27, -0.3708) ), 155 'pentagon': ((0, -1.2), (1.1412, -0.3708), (0.6936, 0.9708), 156 (-0.6936, 0.9708), (-1.1412, -0.3708)), 157 'tievert': ( (-1, -1), (1, -1), (-1, 1), (1, 1) ), 158 'tiehorz': ( (-1, -1), (-1, 1), (1, -1), (1, 1) ), 159 'lozengehorz': ( (0, 0.707), (1.414, 0), (0, -0.707), (-1.414, 0) ), 160 'lozengevert': ( (0, 1.414), (0.707, 0), (0, -1.414), (-0.707, 0) ), 161 162 'star3': ( (0., -1.), (0.173, -0.1), (0.866, 0.5), (0, 0.2), 163 (-0.866, 0.5), (-0.173, -0.1) ), 164 'star4': ( (0.000, 1.000), (-0.354, 0.354), (-1.000, 0.000), 165 (-0.354, -0.354), (0.000, -1.000), (0.354, -0.354), 166 (1.000, -0.000), (0.354, 0.354), ), 167 'star6': ( (0.000, 1.000), (-0.250, 0.433), (-0.866, 0.500), 168 (-0.500, 0.000), (-0.866, -0.500), (-0.250, -0.433), 169 (-0.000, -1.000), (0.250, -0.433), (0.866, -0.500), 170 (0.500, 0.000), (0.866, 0.500), (0.250, 0.433), ), 171 'star8': ( (0.000, 1.000), (-0.191, 0.462), (-0.707, 0.707), 172 (-0.462, 0.191), (-1.000, 0.000), (-0.462, -0.191), 173 (-0.707, -0.707), (-0.191, -0.462), (0.000, -1.000), 174 (0.191, -0.462), (0.707, -0.707), (0.462, -0.191), 175 (1.000, -0.000), (0.462, 0.191), (0.707, 0.707), 176 (0.191, 0.462), ), 177 'hexagon': ( (0, 1), (0.866, 0.5), (0.866, -0.5), 178 (0, -1), (-0.866, -0.5), (-0.866, 0.5), ), 179 'starinvert': ( (0, 1.2), (-0.27, 0.3708), (-1.1412, 0.3708), 180 (-0.4356, -0.1416), (-0.7056, -0.9708), (0, -0.4584), 181 (0.7056, -0.9708), (0.4356, -0.1416), (1.1412, 0.3708), 182 (0.27, 0.3708) ), 183 'squashbox': ( (-1, 1), (0, 0.5), (1, 1), (0.5, 0), 184 (1, -1), (0, -0.5), (-1, -1), (-0.5, 0) ), 185 'plusnarrow': ( (0.2, 1), (0.2, 0.2), (1, 0.2), (1, -0.2), 186 (0.2, -0.2), (0.2, -1), (-0.2, -1), (-0.2, -0.2), 187 (-1, -0.2), (-1, 0.2), (-0.2, 0.2), (-0.2, 1) ), 188 'crossnarrow': ( (-0.566, 0.849), (0, 0.283), (0.566, 0.849), 189 (0.849, 0.566), (0.283, 0), (0.849, -0.566), 190 (0.566, -0.849), (0, -0.283), (-0.566, -0.849), 191 (-0.849, -0.566), (-0.283, 0), (-0.849, 0.566) ), 192 193 'limitupperaway2': ( (-1, 0), (0, 0), (0, -1), (-1, -1), (0, -2), 194 (1, -1), (0, -1), (0, 0), (1, 0) ), 195 'limitloweraway2': ( (-1, 0), (0, 0), (0, 1), (-1, 1), (0, 2), 196 (1, 1), (0, 1), (0, 0), (1, 0) ), 197 'limitleftaway2': ( (0, -1), (0, 0) , (-1, 0), (-1, -1), (-2, 0), 198 (-1, 1), (-1, 0), (0, 0), (0, 1) ), 199 'limitrightaway2': ( (0, -1), (0, 0), (1, 0), (1, -1), (2, 0), 200 (1, 1), (1, 0), (0, 0), (0, 1) ), 201 202 # special arrow symbols 203 '_arrow': ( (0, 0), (-1.8, 1), (-1.4, 0), (-1.8, -1) ), 204 '_arrowtriangle': ( (0, 0), (-1.8, 1), (-1.8, -1) ), 205 '_arrownarrow': ( (0, 0), (-1.8, 0.5), (-1.8, -0.5) ), 206 '_arrowreverse': ( (0, 0), (1.8, 1), (1.4, 0), (1.8, -1) ), 207 } 208 209def addPolyPath(path, vals): 210 """Add a polygon with the list of x,y pts as tuples in vals.""" 211 poly = qt.QPolygonF() 212 for x, y in vals: 213 poly.append( qt.QPointF(x, y) ) 214 path.addPolygon(poly) 215 path.closeSubpath() 216 217def getPolygonPainterPath(name, size): 218 """Create a poly path for a polygon.""" 219 path = qt.QPainterPath() 220 addPolyPath(path, N.array(polygons[name])*size) 221 return path 222 223####################################################################### 224## draw symbols using a QPainterPath 225 226def squarePath(path, size, linewidth): 227 """Square path of size given.""" 228 path.addRect( qt.QRectF(-size, -size, size*2, size*2) ) 229 230def circlePath(path, size, linewidth): 231 """Circle path of size given.""" 232 path.addEllipse( qt.QRectF(-size, -size, size*2, size*2) ) 233 234def circlePlusPath(path, size, linewidth): 235 """Circle path with plus.""" 236 path.addEllipse( qt.QRectF(-size, -size, size*2, size*2) ) 237 path.moveTo(0, -size) 238 path.lineTo(0, size) 239 path.moveTo(-size, 0) 240 path.lineTo(size, 0) 241 242def circleCrossPath(path, size, linewidth): 243 """Circle path with cross.""" 244 path.addEllipse( qt.QRectF(-size, -size, size*2, size*2) ) 245 m = N.sqrt(2.)*size*0.5 246 path.moveTo(-m, -m) 247 path.lineTo(m, m) 248 path.moveTo(-m, m) 249 path.lineTo(m, -m) 250 251def circlePairPathHorz(path, size, linewidth): 252 """2 circles next to each other (horizontal).""" 253 path.addEllipse( qt.QRectF(-size, -size*0.5, size, size) ) 254 path.addEllipse( qt.QRectF(0, -size*0.5, size, size) ) 255 256def circlePairPathVert(path, size, linewidth): 257 """2 circles next to each other (vertical).""" 258 path.addEllipse( qt.QRectF(-size*0.5, -size, size, size) ) 259 path.addEllipse( qt.QRectF(-size*0.5, 0, size, size) ) 260 261def ellipseHorzPath(path, size, linewidth): 262 """Horizontal ellipse path.""" 263 path.addEllipse( qt.QRectF(-size, -size*0.5, size*2, size) ) 264 265def ellipseVertPath(path, size, linewidth): 266 """Vertical ellipse path.""" 267 path.addEllipse( qt.QRectF(-size*0.5, -size, size, size*2) ) 268 269def circleHolePath(path, size, linewidth): 270 """Circle with centre missing.""" 271 circlePath(path, size, linewidth) 272 circlePath(path, size*0.5, linewidth) 273 274def squarePlusPath(path, size, linewidth): 275 """Square with plus sign.""" 276 path.addRect( qt.QRectF(-size, -size, size*2, size*2) ) 277 path.moveTo(0, -size) 278 path.lineTo(0, size) 279 path.moveTo(-size, 0) 280 path.lineTo(size, 0) 281 282def squareCrossPath(path, size, linewidth): 283 """Square with cross sign.""" 284 path.addRect( qt.QRectF(-size, -size, size*2, size*2) ) 285 path.moveTo(-size, -size) 286 path.lineTo(size, size) 287 path.moveTo(-size, size) 288 path.lineTo(size, -size) 289 290def squareHolePath(path, size, linewidth): 291 """Square with centre missing.""" 292 path.addRect( qt.QRectF(-size, -size, size*2, size*2) ) 293 path.addRect( qt.QRectF(-size*0.5, -size*0.5, size, size) ) 294 295def diamondHolePath(path, size, linewidth): 296 """Diamond with centre missing.""" 297 pts = N.array(polygons['diamond'])*size 298 addPolyPath(path, pts) 299 addPolyPath(path, pts*0.5) 300 301def pentagonHolePath(path, size, linewidth): 302 """Pentagon with centre missing.""" 303 pts = N.array(polygons['pentagon'])*size 304 addPolyPath(path, pts) 305 addPolyPath(path, pts*0.5) 306 307def squareRoundedPath(path, size, linewidth): 308 """A square with rounded corners.""" 309 path.addRoundedRect( 310 qt.QRectF(-size, -size, size*2, size*2), 311 50, 50, qt.Qt.RelativeSize) 312 313def dotPath(path, size, linewidth): 314 """Draw a dot.""" 315 path.addEllipse(qt.QRectF( 316 -linewidth*0.5, -linewidth*0.5, linewidth, linewidth)) 317 318def bullseyePath(path, size, linewidth): 319 """A filled circle inside a filled circle.""" 320 path.setFillRule(qt.Qt.WindingFill) 321 circlePath(path, size, linewidth) 322 circlePath(path, size*0.5, linewidth) 323 324def circleDotPath(path, size, linewidth): 325 """A dot inside a circle.""" 326 path.setFillRule(qt.Qt.WindingFill) 327 circlePath(path, size, linewidth) 328 dotPath(path, size, linewidth) 329 330pathsymbols = { 331 'square': squarePath, 332 'circle': circlePath, 333 'circleplus': circlePlusPath, 334 'circlecross': circleCrossPath, 335 'circlepairhorz': circlePairPathHorz, 336 'circlepairvert': circlePairPathVert, 337 'ellipsehorz': ellipseHorzPath, 338 'ellipsevert': ellipseVertPath, 339 'circlehole': circleHolePath, 340 'squareplus': squarePlusPath, 341 'squarecross': squareCrossPath, 342 'squarehole': squareHolePath, 343 'diamondhole': diamondHolePath, 344 'pentagonhole': pentagonHolePath, 345 'squarerounded': squareRoundedPath, 346 'dot': dotPath, 347 'bullseye': bullseyePath, 348 'circledot': circleDotPath, 349 } 350 351def getSymbolPainterPath(name, size, linewidth): 352 """Get a painter path for a symbol shape.""" 353 path = qt.QPainterPath() 354 pathsymbols[name](path, size, linewidth) 355 return path 356 357# translate arrow shapes to point types (we reuse them) 358arrow_translate = { 359 'none': 'none', 360 'arrow': '_arrow', 361 'arrownarrow': '_arrownarrow', 362 'arrowtriangle': '_arrowtriangle', 363 'arrowreverse': '_arrowreverse', 364 'linearrow': '_linearrow', 365 'linearrowreverse': '_linearrowreverse', 366 'bar': 'linevert', 367 'linecross': 'linecross', 368 'asterisk': 'asterisk', 369 'circle': 'circle', 370 'square': 'square', 371 'diamond': 'diamond', 372} 373 374####################################################################### 375## external interfaces 376 377def getPointPainterPath(name, size, linewidth): 378 """Return a painter path for the name and size. 379 380 Returns (painterpath, enablefill).""" 381 if name in linesymbols: 382 return getLinePainterPath(name, size), False 383 elif name in polygons: 384 return getPolygonPainterPath(name, size), True 385 elif name in pathsymbols: 386 return getSymbolPainterPath(name, size, linewidth), True 387 elif name == 'none': 388 return qt.QPainterPath(), True 389 else: 390 raise ValueError("Invalid marker name %s" % name) 391 392# list of codes supported 393MarkerCodes = ( 394 'none', 395 'circle', 'diamond', 'square', 396 'cross', 'plus', 'star', 397 'barhorz', 'barvert', 398 'pentagon', 'hexagon', 'octogon', 'tievert', 'tiehorz', 399 'triangle', 'triangledown', 'triangleleft', 'triangleright', 400 'dot', 'circledot', 'bullseye', 401 'circlehole', 'squarehole', 'diamondhole', 'pentagonhole', 402 'squarerounded', 'squashbox', 403 'ellipsehorz', 'ellipsevert', 404 'lozengehorz', 'lozengevert', 405 'plusnarrow', 'crossnarrow', 406 'circleplus', 'circlecross', 'squareplus', 'squarecross', 407 'star3', 'star4', 'star6', 'star8', 'starinvert', 408 'circlepairhorz', 'circlepairvert', 409 'asterisk', 'lineplus', 'linecross', 410 'plushair', 'crosshair', 'asteriskhair', 411 'linevert', 'linehorz', 'linevertgap', 'linehorzgap', 412 'arrowleft', 'arrowright', 'arrowup', 'arrowdown', 413 'arrowleftaway', 'arrowrightaway', 414 'arrowupaway', 'arrowdownaway', 415 'limitupper', 'limitlower', 'limitleft', 'limitright', 416 'limitupperaway', 'limitloweraway', 417 'limitleftaway', 'limitrightaway', 418 'limitupperaway2', 'limitloweraway2', 419 'limitleftaway2', 'limitrightaway2', 420 'arrowupperleftaway', 'arrowupperrightaway', 421 'arrowlowerrightaway', 'arrowlowerleftaway', 422 'lineup', 'linedown', 'lineleft', 'lineright', 423 ) 424 425def plotMarkers(painter, xpos, ypos, markername, markersize, scaling=None, 426 clip=None, cmap=None, colorvals=None, scaleline=False): 427 """Funtion to plot an array of markers on a painter. 428 429 painter: QPainter 430 xpos, ypos: iterable item of positions 431 markername: name of marker from MarkerCodes 432 markersize: size of marker to plot 433 scaling: scale size of markers by array, or don't in None 434 clip: rectangle if clipping wanted 435 cmap: colormap to use if colorvals is set 436 colorvals: color values 0-1 of each point if used 437 scaleline: if scaling, scale border line width with scaling 438 """ 439 440 # minor optimization 441 if markername == 'none': 442 return 443 444 painter.save() 445 446 # get sharper angles and more exact positions using these settings 447 pen = painter.pen() 448 pen.setJoinStyle( qt.Qt.MiterJoin ) 449 painter.setPen(pen) 450 451 # get path to draw and whether to fill 452 path, fill = getPointPainterPath( 453 markername, markersize, painter.pen().widthF()) 454 if not fill: 455 # turn off brush 456 painter.setBrush( qt.QBrush() ) 457 458 # if using colored points 459 colorimg = None 460 if colorvals is not None: 461 # convert colors to rgb values via a 2D image and pass to function 462 trans = (1-painter.brush().color().alphaF())*100 463 color2d = colorvals.reshape( 1, len(colorvals) ) 464 colorimg = colormap.applyColorMap( 465 cmap, 'linear', color2d, 0., 1., trans) 466 467 plotPathsToPainter(painter, path, xpos, ypos, scaling, clip, colorimg, 468 scaleline) 469 470 painter.restore() 471 472def plotMarker(painter, xpos, ypos, markername, markersize): 473 """Function to plot a marker on a painter, posn xpos, ypos, type and size 474 """ 475 plotMarkers(painter, (xpos,), (ypos,), markername, markersize) 476 477# translate arrow shapes to point types (we reuse them) 478arrow_translate = { 479 'none': 'none', 480 'arrow': '_arrow', 481 'arrownarrow': '_arrownarrow', 482 'arrowtriangle': '_arrowtriangle', 483 'arrowreverse': '_arrowreverse', 484 'linearrow': '_linearrow', 485 'linearrowreverse': '_linearrowreverse', 486 'bar': 'linevert', 487 'linecross': 'linecross', 488 'asterisk': 'asterisk', 489 'circle': 'circle', 490 'square': 'square', 491 'diamond': 'diamond', 492 'lineup': 'lineup', 493 'linedown': 'linedown', 494 'lineextend': 'lineright', 495} 496 497# codes of allowable arrows 498ArrowCodes = ( 'none', 'arrow', 'arrownarrow', 499 'arrowtriangle', 500 'arrowreverse', 501 'linearrow', 'linearrowreverse', 502 'bar', 'linecross', 503 'asterisk', 504 'circle', 'square', 'diamond', 505 'lineup', 'linedown', 506 'lineextend', 507 ) 508 509def plotLineArrow(painter, xpos, ypos, length, angle, 510 arrowsize=0, 511 arrowleft='none', arrowright='none'): 512 """Plot a line or arrow. 513 514 xpos, ypos is the starting point of the line 515 angle is the angle to the horizontal (degrees) 516 arrowleft and arrowright are arrow codes.""" 517 518 painter.save() 519 painter.translate(xpos, ypos) 520 painter.rotate(angle) 521 522 linepen = qt.QPen(painter.pen()) 523 arrowpen = painter.pen() 524 arrowpen.setStyle(qt.Qt.SolidLine) 525 painter.setPen(arrowpen) 526 527 # plot marker at one end of line 528 plotMarker(painter, length, 0., arrow_translate[arrowright], arrowsize) 529 530 # plot reversed marker at other end 531 painter.scale(-1, 1) 532 plotMarker(painter, 0, 0, arrow_translate[arrowleft], arrowsize) 533 534 linepen.setCapStyle(qt.Qt.FlatCap) 535 painter.setPen(linepen) 536 painter.scale(-1, 1) 537 painter.drawLine(qt.QPointF(0, 0), qt.QPointF(length, 0)) 538 539 painter.restore() 540