Initial commit: IT Site Survey AI v2 with all features
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
#Copyright ReportLab Europe Ltd. 2000-2017
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/__init__.py
|
||||
__version__='3.3.0'
|
||||
__doc__='''Some non-chart widgets'''
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,126 @@
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.validators import *
|
||||
from reportlab.lib.attrmap import *
|
||||
from reportlab.graphics.shapes import Drawing, _DrawingEditorMixin, Group, Polygon
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
|
||||
class AdjustableArrow(Widget):
|
||||
"""This widget draws an arrow (style one).
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor'
|
||||
|
||||
"""
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber,desc='symbol x coordinate'),
|
||||
y = AttrMapValue(isNumber,desc='symbol y coordinate'),
|
||||
dx = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
|
||||
dy = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
|
||||
stemThickness = AttrMapValue(isNumber, 'width of the stem'),
|
||||
stemLength = AttrMapValue(isNumber, 'length of the stem'),
|
||||
headProjection = AttrMapValue(isNumber, 'how much the head projects from the stem'),
|
||||
headLength = AttrMapValue(isNumber, 'length of the head'),
|
||||
headSweep = AttrMapValue(isNumber, 'howmuch the head sweeps back (-ve) or forwards (+ve)'),
|
||||
scale = AttrMapValue(isNumber, 'scaling factor'),
|
||||
fillColor = AttrMapValue(isColorOrNone),
|
||||
strokeColor = AttrMapValue(isColorOrNone),
|
||||
strokeWidth = AttrMapValue(isNumber),
|
||||
boxAnchor = AttrMapValue(isBoxAnchor,desc='anchoring point of the label'),
|
||||
right =AttrMapValue(isBoolean,desc='If True (default) the arrow is horizontal pointing right\nFalse means it points up'),
|
||||
angle = AttrMapValue(isNumber, desc='angle of arrow default (0), right True 0 is horizontal to right else vertical up'),
|
||||
)
|
||||
def __init__(self,**kwds):
|
||||
self._setKeywords(**kwds)
|
||||
self._setKeywords(**dict(
|
||||
x = 0,
|
||||
y = 0,
|
||||
fillColor = colors.red,
|
||||
strokeWidth = 0,
|
||||
strokeColor = None,
|
||||
boxAnchor = 'c',
|
||||
angle = 0,
|
||||
stemThickness = 33,
|
||||
stemLength = 50,
|
||||
headProjection = 15,
|
||||
headLength = 50,
|
||||
headSweep = 0,
|
||||
scale = 1.,
|
||||
right=True,
|
||||
))
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
g = Group()
|
||||
|
||||
x = self.x
|
||||
y = self.y
|
||||
scale = self.scale
|
||||
stemThickness = self.stemThickness*scale
|
||||
stemLength = self.stemLength*scale
|
||||
headProjection = self.headProjection*scale
|
||||
headLength = self.headLength*scale
|
||||
headSweep = self.headSweep*scale
|
||||
w = stemLength+headLength
|
||||
h = 2*headProjection+stemThickness
|
||||
# shift to the boxAnchor
|
||||
boxAnchor = self.boxAnchor
|
||||
if self.right:
|
||||
if boxAnchor in ('sw','w','nw'):
|
||||
dy = -h
|
||||
elif boxAnchor in ('s','c','n'):
|
||||
dy = -h*0.5
|
||||
else:
|
||||
dy = 0
|
||||
if boxAnchor in ('w','c','e'):
|
||||
dx = -w*0.5
|
||||
elif boxAnchor in ('nw','n','ne'):
|
||||
dx = -w
|
||||
else:
|
||||
dx = 0
|
||||
points = [
|
||||
dx, dy+headProjection+stemThickness,
|
||||
dx+stemLength, dy+headProjection+stemThickness,
|
||||
dx+stemLength+headSweep, dy+2*headProjection+stemThickness,
|
||||
dx+stemLength+headLength, dy+0.5*stemThickness+headProjection,
|
||||
dx+stemLength+headSweep, dy,
|
||||
dx+stemLength, dy+headProjection,
|
||||
dx, dy+headProjection,
|
||||
]
|
||||
else:
|
||||
w,h = h,w
|
||||
if boxAnchor in ('nw','n','ne'):
|
||||
dy = -h
|
||||
elif boxAnchor in ('w','c','e'):
|
||||
dy = -h*0.5
|
||||
else:
|
||||
dy = 0
|
||||
if boxAnchor in ('ne','e','se'):
|
||||
dx = -w
|
||||
elif boxAnchor in ('n','c','s'):
|
||||
dx = -w*0.5
|
||||
else:
|
||||
dx = 0
|
||||
points = [
|
||||
dx+headProjection, dy, #sw
|
||||
dx+headProjection+stemThickness, dy, #se
|
||||
dx+headProjection+stemThickness, dy+stemLength,
|
||||
dx+w, dy+stemLength+headSweep,
|
||||
dx+headProjection+0.5*stemThickness, dy+h,
|
||||
dx, dy+stemLength+headSweep,
|
||||
dx+headProjection, dy+stemLength,
|
||||
]
|
||||
|
||||
g.add(Polygon(
|
||||
points = points,
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth = self.strokeWidth,
|
||||
))
|
||||
g.translate(x,y)
|
||||
g.rotate(self.angle)
|
||||
return g
|
||||
|
||||
class AdjustableArrowDrawing(_DrawingEditorMixin,Drawing):
|
||||
def __init__(self,width=100,height=63,*args,**kw):
|
||||
Drawing.__init__(self,width,height,*args,**kw)
|
||||
self._add(self,AdjustableArrow(),name='adjustableArrow',validate=None,desc=None)
|
||||
@@ -0,0 +1,299 @@
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/eventcal.py
|
||||
# Event Calendar widget
|
||||
# author: Andy Robinson
|
||||
|
||||
__version__='3.3.0'
|
||||
__doc__="""This file is a
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.graphics.shapes import Rect, Drawing, Group, String
|
||||
from reportlab.graphics.charts.textlabels import Label
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
|
||||
|
||||
class EventCalendar(Widget):
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.width = 300
|
||||
self.height = 150
|
||||
self.timeColWidth = None # if declared, use it; otherwise auto-size.
|
||||
self.trackRowHeight = 20
|
||||
self.data = [] # list of Event objects
|
||||
self.trackNames = None
|
||||
|
||||
self.startTime = None #displays ALL data on day if not set
|
||||
self.endTime = None # displays ALL data on day if not set
|
||||
self.day = 0
|
||||
|
||||
|
||||
# we will keep any internal geometry variables
|
||||
# here. These are computed by computeSize(),
|
||||
# which is the first thing done when drawing.
|
||||
self._talksVisible = [] # subset of data which will get plotted, cache
|
||||
self._startTime = None
|
||||
self._endTime = None
|
||||
self._trackCount = 0
|
||||
self._colWidths = []
|
||||
self._colLeftEdges = [] # left edge of each column
|
||||
|
||||
def computeSize(self):
|
||||
"Called at start of draw. Sets various column widths"
|
||||
self._talksVisible = self.getRelevantTalks(self.data)
|
||||
self._trackCount = len(self.getAllTracks())
|
||||
self.computeStartAndEndTimes()
|
||||
self._colLeftEdges = [self.x]
|
||||
if self.timeColWidth is None:
|
||||
w = self.width / (1 + self._trackCount)
|
||||
self._colWidths = [w] * (1+ self._trackCount)
|
||||
for i in range(self._trackCount):
|
||||
self._colLeftEdges.append(self._colLeftEdges[-1] + w)
|
||||
else:
|
||||
self._colWidths = [self.timeColWidth]
|
||||
w = (self.width - self.timeColWidth) / self._trackCount
|
||||
for i in range(self._trackCount):
|
||||
self._colWidths.append(w)
|
||||
self._colLeftEdges.append(self._colLeftEdges[-1] + w)
|
||||
|
||||
|
||||
|
||||
def computeStartAndEndTimes(self):
|
||||
"Work out first and last times to display"
|
||||
if self.startTime:
|
||||
self._startTime = self.startTime
|
||||
else:
|
||||
for (title, speaker, trackId, day, start, duration) in self._talksVisible:
|
||||
|
||||
if self._startTime is None: #first one
|
||||
self._startTime = start
|
||||
else:
|
||||
if start < self._startTime:
|
||||
self._startTime = start
|
||||
|
||||
if self.endTime:
|
||||
self._endTime = self.endTime
|
||||
else:
|
||||
for (title, speaker, trackId, day, start, duration) in self._talksVisible:
|
||||
if self._endTime is None: #first one
|
||||
self._endTime = start + duration
|
||||
else:
|
||||
if start + duration > self._endTime:
|
||||
self._endTime = start + duration
|
||||
|
||||
|
||||
|
||||
|
||||
def getAllTracks(self):
|
||||
tracks = []
|
||||
for (title, speaker, trackId, day, hours, duration) in self.data:
|
||||
if trackId is not None:
|
||||
if trackId not in tracks:
|
||||
tracks.append(trackId)
|
||||
tracks.sort()
|
||||
return tracks
|
||||
|
||||
def getRelevantTalks(self, talkList):
|
||||
"Scans for tracks actually used"
|
||||
used = []
|
||||
for talk in talkList:
|
||||
(title, speaker, trackId, day, hours, duration) = talk
|
||||
assert trackId != 0, "trackId must be None or 1,2,3... zero not allowed!"
|
||||
if day == self.day:
|
||||
if (((self.startTime is None) or ((hours + duration) >= self.startTime))
|
||||
and ((self.endTime is None) or (hours <= self.endTime))):
|
||||
used.append(talk)
|
||||
return used
|
||||
|
||||
def scaleTime(self, theTime):
|
||||
"Return y-value corresponding to times given"
|
||||
axisHeight = self.height - self.trackRowHeight
|
||||
# compute fraction between 0 and 1, 0 is at start of period
|
||||
proportionUp = ((theTime - self._startTime) / (self._endTime - self._startTime))
|
||||
y = self.y + axisHeight - (axisHeight * proportionUp)
|
||||
return y
|
||||
|
||||
|
||||
def getTalkRect(self, startTime, duration, trackId, text):
|
||||
"Return shapes for a specific talk"
|
||||
g = Group()
|
||||
y_bottom = self.scaleTime(startTime + duration)
|
||||
y_top = self.scaleTime(startTime)
|
||||
y_height = y_top - y_bottom
|
||||
|
||||
if trackId is None:
|
||||
#spans all columns
|
||||
x = self._colLeftEdges[1]
|
||||
width = self.width - self._colWidths[0]
|
||||
else:
|
||||
#trackId is 1-based and these arrays have the margin info in column
|
||||
#zero, so no need to add 1
|
||||
x = self._colLeftEdges[trackId]
|
||||
width = self._colWidths[trackId]
|
||||
|
||||
lab = Label()
|
||||
lab.setText(text)
|
||||
lab.setOrigin(x + 0.5*width, y_bottom+0.5*y_height)
|
||||
lab.boxAnchor = 'c'
|
||||
lab.width = width
|
||||
lab.height = y_height
|
||||
lab.fontSize = 6
|
||||
|
||||
r = Rect(x, y_bottom, width, y_height, fillColor=colors.cyan)
|
||||
g.add(r)
|
||||
g.add(lab)
|
||||
|
||||
#now for a label
|
||||
# would expect to color-code and add text
|
||||
return g
|
||||
|
||||
def draw(self):
|
||||
self.computeSize()
|
||||
g = Group()
|
||||
|
||||
# time column
|
||||
g.add(Rect(self.x, self.y, self._colWidths[0], self.height - self.trackRowHeight, fillColor=colors.cornsilk))
|
||||
|
||||
# track headers
|
||||
x = self.x + self._colWidths[0]
|
||||
y = self.y + self.height - self.trackRowHeight
|
||||
for trk in range(self._trackCount):
|
||||
wid = self._colWidths[trk+1]
|
||||
r = Rect(x, y, wid, self.trackRowHeight, fillColor=colors.yellow)
|
||||
s = String(x + 0.5*wid, y, 'Track %d' % trk, align='middle')
|
||||
g.add(r)
|
||||
g.add(s)
|
||||
x = x + wid
|
||||
|
||||
for talk in self._talksVisible:
|
||||
(title, speaker, trackId, day, start, duration) = talk
|
||||
r = self.getTalkRect(start, duration, trackId, title + '\n' + speaker)
|
||||
g.add(r)
|
||||
|
||||
|
||||
return g
|
||||
|
||||
|
||||
|
||||
|
||||
def test():
|
||||
"Make a conference event for day 1 of UP Python 2003"
|
||||
|
||||
|
||||
d = Drawing(400,200)
|
||||
|
||||
cal = EventCalendar()
|
||||
cal.x = 50
|
||||
cal.y = 25
|
||||
cal.data = [
|
||||
# these might be better as objects instead of tuples, since I
|
||||
# predict a large number of "optionsl" variables to affect
|
||||
# formatting in future.
|
||||
|
||||
#title, speaker, track id, day, start time (hrs), duration (hrs)
|
||||
# track ID is 1-based not zero-based!
|
||||
('Keynote: Why design another programming language?', 'Guido van Rossum', None, 1, 9.0, 1.0),
|
||||
|
||||
('Siena Web Service Architecture', 'Marc-Andre Lemburg', 1, 1, 10.5, 1.5),
|
||||
('Extreme Programming in Python', 'Chris Withers', 2, 1, 10.5, 1.5),
|
||||
('Pattern Experiences in C++', 'Mark Radford', 3, 1, 10.5, 1.5),
|
||||
('What is the Type of std::toupper()', 'Gabriel Dos Reis', 4, 1, 10.5, 1.5),
|
||||
('Linguistic Variables: Clear Thinking with Fuzzy Logic ', 'Walter Banks', 5, 1, 10.5, 1.5),
|
||||
|
||||
('lunch, short presentations, vendor presentations', '', None, 1, 12.0, 2.0),
|
||||
|
||||
("CORBA? Isn't that obsolete", 'Duncan Grisby', 1, 1, 14.0, 1.5),
|
||||
("Python Design Patterns", 'Duncan Booth', 2, 1, 14.0, 1.5),
|
||||
("Inside Security Checks and Safe Exceptions", 'Brandon Bray', 3, 1, 14.0, 1.5),
|
||||
("Studying at a Distance", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 1, 14.0, 1.5),
|
||||
("Coding Standards - Given the ANSI C Standard why do I still need a coding Standard", 'Randy Marques', 5, 1, 14.0, 1.5),
|
||||
|
||||
("RESTful Python", 'Hamish Lawson', 1, 1, 16.0, 1.5),
|
||||
("Parsing made easier - a radical old idea", 'Andrew Koenig', 2, 1, 16.0, 1.5),
|
||||
("C++ & Multimethods", 'Julian Smith', 3, 1, 16.0, 1.5),
|
||||
("C++ Threading", 'Kevlin Henney', 4, 1, 16.0, 1.5),
|
||||
("The Organisation Strikes Back", 'Alan Griffiths & Sarah Lees', 5, 1, 16.0, 1.5),
|
||||
|
||||
('Birds of a Feather meeting', '', None, 1, 17.5, 2.0),
|
||||
|
||||
('Keynote: In the Spirit of C', 'Greg Colvin', None, 2, 9.0, 1.0),
|
||||
|
||||
('The Infinite Filing Cabinet - object storage in Python', 'Jacob Hallen', 1, 2, 10.5, 1.5),
|
||||
('Introduction to Python and Jython for C++ and Java Programmers', 'Alex Martelli', 2, 2, 10.5, 1.5),
|
||||
('Template metaprogramming in Haskell', 'Simon Peyton Jones', 3, 2, 10.5, 1.5),
|
||||
('Plenty People Programming: C++ Programming in a Group, Workshop with a difference', 'Nico Josuttis', 4, 2, 10.5, 1.5),
|
||||
('Design and Implementation of the Boost Graph Library', 'Jeremy Siek', 5, 2, 10.5, 1.5),
|
||||
|
||||
('lunch, short presentations, vendor presentations', '', None, 2, 12.0, 2.0),
|
||||
|
||||
("Building GUI Applications with PythonCard and PyCrust", 'Andy Todd', 1, 2, 14.0, 1.5),
|
||||
("Integrating Python, C and C++", 'Duncan Booth', 2, 2, 14.0, 1.5),
|
||||
("Secrets and Pitfalls of Templates", 'Nicolai Josuttis & David Vandevoorde', 3, 2, 14.0, 1.5),
|
||||
("Being a Mentor", 'Panel Discussion, Panel to include Alan Lenton & Francis Glassborow', 4, 2, 14.0, 1.5),
|
||||
("The Embedded C Extensions to C", 'Willem Wakker', 5, 2, 14.0, 1.5),
|
||||
|
||||
("Lightning Talks", 'Paul Brian', 1, 2, 16.0, 1.5),
|
||||
("Scripting Java Applications with Jython", 'Anthony Eden', 2, 2, 16.0, 1.5),
|
||||
("Metaprogramming and the Boost Metaprogramming Library", 'David Abrahams', 3, 2, 16.0, 1.5),
|
||||
("A Common Vendor ABI for C++ -- GCC's why, what and not", 'Nathan Sidwell & Gabriel Dos Reis', 4, 2, 16.0, 1.5),
|
||||
("The Timing and Cost of Choices", 'Hubert Matthews', 5, 2, 16.0, 1.5),
|
||||
|
||||
('Birds of a Feather meeting', '', None, 2, 17.5, 2.0),
|
||||
|
||||
('Keynote: The Cost of C & C++ Compatibility', 'Andy Koenig', None, 3, 9.0, 1.0),
|
||||
|
||||
('Prying Eyes: Generic Observer Implementations in C++', 'Andrei Alexandrescu', 1, 2, 10.5, 1.5),
|
||||
('The Roadmap to Generative Programming With C++', 'Ulrich Eisenecker', 2, 2, 10.5, 1.5),
|
||||
('Design Patterns in C++ and C# for the Common Language Runtime', 'Brandon Bray', 3, 2, 10.5, 1.5),
|
||||
('Extreme Hour (XH): (workshop) - Jutta Eckstein and Nico Josuttis', 'Jutta Ecstein', 4, 2, 10.5, 1.5),
|
||||
('The Lambda Library : Unnamed Functions for C++', 'Jaako Jarvi', 5, 2, 10.5, 1.5),
|
||||
|
||||
('lunch, short presentations, vendor presentations', '', None, 3, 12.0, 2.0),
|
||||
|
||||
('Reflective Metaprogramming', 'Daveed Vandevoorde', 1, 3, 14.0, 1.5),
|
||||
('Advanced Template Issues and Solutions (double session)', 'Herb Sutter',2, 3, 14.0, 3),
|
||||
('Concurrent Programming in Java (double session)', 'Angelika Langer', 3, 3, 14.0, 3),
|
||||
('What can MISRA-C (2nd Edition) do for us?', 'Chris Hills', 4, 3, 14.0, 1.5),
|
||||
('C++ Metaprogramming Concepts and Results', 'Walter E Brown', 5, 3, 14.0, 1.5),
|
||||
|
||||
('Binding C++ to Python with the Boost Python Library', 'David Abrahams', 1, 3, 16.0, 1.5),
|
||||
('Using Aspect Oriented Programming for Enterprise Application Integration', 'Arno Schmidmeier', 4, 3, 16.0, 1.5),
|
||||
('Defective C++', 'Marc Paterno', 5, 3, 16.0, 1.5),
|
||||
|
||||
("Speakers' Banquet & Birds of a Feather meeting", '', None, 3, 17.5, 2.0),
|
||||
|
||||
('Keynote: The Internet, Software and Computers - A Report Card', 'Alan Lenton', None, 4, 9.0, 1.0),
|
||||
|
||||
('Multi-Platform Software Development; Lessons from the Boost libraries', 'Beman Dawes', 1, 5, 10.5, 1.5),
|
||||
('The Stability of the C++ ABI', 'Steve Clamage', 2, 5, 10.5, 1.5),
|
||||
('Generic Build Support - A Pragmatic Approach to the Software Build Process', 'Randy Marques', 3, 5, 10.5, 1.5),
|
||||
('How to Handle Project Managers: a survival guide', 'Barb Byro', 4, 5, 10.5, 1.5),
|
||||
|
||||
('lunch, ACCU AGM', '', None, 5, 12.0, 2.0),
|
||||
|
||||
('Sauce: An OO recursive descent parser; its design and implementation.', 'Jon Jagger', 1, 5, 14.0, 1.5),
|
||||
('GNIRTS ESAC REWOL - Bringing the UNIX filters to the C++ iostream library.', 'JC van Winkel', 2, 5, 14.0, 1.5),
|
||||
('Pattern Writing: Live and Direct', 'Frank Buschmann & Kevlin Henney', 3, 5, 14.0, 3.0),
|
||||
('The Future of Programming Languages - A Goldfish Bowl', 'Francis Glassborow and friends', 3, 5, 14.0, 1.5),
|
||||
|
||||
('Honey, I Shrunk the Threads: Compile-time checked multithreaded transactions in C++', 'Andrei Alexandrescu', 1, 5, 16.0, 1.5),
|
||||
('Fun and Functionality with Functors', 'Lois Goldthwaite', 2, 5, 16.0, 1.5),
|
||||
('Agile Enough?', 'Alan Griffiths', 4, 5, 16.0, 1.5),
|
||||
("Conference Closure: A brief plenary session", '', None, 5, 17.5, 0.5),
|
||||
|
||||
]
|
||||
|
||||
#return cal
|
||||
cal.day = 1
|
||||
|
||||
d.add(cal)
|
||||
|
||||
|
||||
for format in ['pdf']:#,'gif','png']:
|
||||
out = d.asString(format)
|
||||
open('eventcal.%s' % format, 'wb').write(out)
|
||||
print('saved eventcal.%s' % format)
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
@@ -0,0 +1,879 @@
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/flags.py
|
||||
# Flag Widgets - a collection of flags as widgets
|
||||
# author: John Precedo (johnp@reportlab.com)
|
||||
|
||||
__version__='3.3.0'
|
||||
__doc__="""This file is a collection of flag graphics as widgets.
|
||||
|
||||
All flags are represented at the ratio of 1:2, even where the official ratio for the flag is something else
|
||||
(such as 3:5 for the German national flag). The only exceptions are for where this would look _very_ wrong,
|
||||
such as the Danish flag whose (ratio is 28:37), or the Swiss flag (which is square).
|
||||
|
||||
Unless otherwise stated, these flags are all the 'national flags' of the countries, rather than their
|
||||
state flags, naval flags, ensigns or any other variants. (National flags are the flag flown by civilians
|
||||
of a country and the ones usually used to represent a country abroad. State flags are the variants used by
|
||||
the government and by diplomatic missions overseas).
|
||||
|
||||
To check on how close these are to the 'official' representations of flags, check the World Flag Database at
|
||||
http://www.flags.ndirect.co.uk/
|
||||
|
||||
The flags this file contains are:
|
||||
|
||||
EU Members:
|
||||
United Kingdom, Austria, Belgium, Denmark, Finland, France, Germany, Greece, Ireland, Italy, Luxembourg,
|
||||
Holland (The Netherlands), Spain, Sweden
|
||||
|
||||
Others:
|
||||
USA, Czech Republic, European Union, Switzerland, Turkey, Brazil
|
||||
|
||||
(Brazilian flag contributed by Publio da Costa Melo [publio@planetarium.com.br]).
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.validators import *
|
||||
from reportlab.lib.attrmap import *
|
||||
from reportlab.graphics.shapes import Line, Rect, Polygon, Drawing, Group, String, Circle, Wedge
|
||||
from reportlab.graphics import renderPDF
|
||||
from reportlab.graphics.widgets.signsandsymbols import _Symbol
|
||||
import copy
|
||||
from math import sin, cos, pi
|
||||
|
||||
validFlag=OneOf(None,
|
||||
'UK',
|
||||
'USA',
|
||||
'Afghanistan',
|
||||
'Austria',
|
||||
'Belgium',
|
||||
'China',
|
||||
'Cuba',
|
||||
'Denmark',
|
||||
'Finland',
|
||||
'France',
|
||||
'Germany',
|
||||
'Greece',
|
||||
'Ireland',
|
||||
'Italy',
|
||||
'Japan',
|
||||
'Luxembourg',
|
||||
'Holland',
|
||||
'Palestine',
|
||||
'Portugal',
|
||||
'Russia',
|
||||
'Spain',
|
||||
'Sweden',
|
||||
'Norway',
|
||||
'CzechRepublic',
|
||||
'Turkey',
|
||||
'Switzerland',
|
||||
'EU',
|
||||
'Brazil'
|
||||
)
|
||||
|
||||
_size = 100.
|
||||
|
||||
class Star(_Symbol):
|
||||
"""This draws a 5-pointed star.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor', 'strokeColor'
|
||||
|
||||
"""
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
angle = AttrMapValue(isNumber, desc='angle in degrees'),
|
||||
)
|
||||
_size = 100.
|
||||
|
||||
def __init__(self):
|
||||
_Symbol.__init__(self)
|
||||
self.size = 100
|
||||
self.fillColor = colors.yellow
|
||||
self.strokeColor = None
|
||||
self.angle = 0
|
||||
|
||||
def demo(self):
|
||||
D = Drawing(200, 100)
|
||||
et = Star()
|
||||
et.x=50
|
||||
et.y=0
|
||||
D.add(et)
|
||||
labelFontSize = 10
|
||||
D.add(String(et.x+(et.size/2.0),(et.y-(1.2*labelFontSize)),
|
||||
et.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
return D
|
||||
|
||||
def draw(self):
|
||||
s = float(self.size) #abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
|
||||
# new algorithm from markers.StarFive
|
||||
R = float(self.size)/2
|
||||
r = R*sin(18*(pi/180.0))/cos(36*(pi/180.0))
|
||||
P = []
|
||||
angle = 90
|
||||
for i in range(5):
|
||||
for radius in R, r:
|
||||
theta = angle*(pi/180.0)
|
||||
P.append(radius*cos(theta))
|
||||
P.append(radius*sin(theta))
|
||||
angle = angle + 36
|
||||
# star specific bits
|
||||
star = Polygon(P,
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=s/50)
|
||||
g.rotate(self.angle)
|
||||
g.shift(self.x+self.dx,self.y+self.dy)
|
||||
g.add(star)
|
||||
|
||||
return g
|
||||
|
||||
class Flag(_Symbol):
|
||||
"""This is a generic flag class that all the flags in this file use as a basis.
|
||||
|
||||
This class basically provides edges and a tidy-up routine to hide any bits of
|
||||
line that overlap the 'outside' of the flag
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor'
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
fillColor = AttrMapValue(isColor, desc='Background color'),
|
||||
border = AttrMapValue(isBoolean, 'Whether a background is drawn'),
|
||||
kind = AttrMapValue(validFlag, desc='Which flag'),
|
||||
)
|
||||
|
||||
_cache = {}
|
||||
|
||||
def __init__(self,**kw):
|
||||
_Symbol.__init__(self)
|
||||
self.kind = None
|
||||
self.size = 100
|
||||
self.fillColor = colors.white
|
||||
self.border=1
|
||||
self.setProperties(kw)
|
||||
|
||||
def availableFlagNames(self):
|
||||
'''return a list of the things we can display'''
|
||||
return [x for x in self._attrMap['kind'].validate._enum if x is not None]
|
||||
|
||||
def _Flag_None(self):
|
||||
s = _size # abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
g.add(Rect(0, 0, s*2, s, fillColor = colors.purple, strokeColor = colors.black, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _borderDraw(self,f):
|
||||
s = self.size # abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
g.add(f)
|
||||
x, y, sW = self.x+self.dx, self.y+self.dy, self.strokeWidth/2.
|
||||
g.insert(0,Rect(-sW, -sW, width=getattr(self,'_width',2*s)+3*sW, height=getattr(self,'_height',s)+2*sW,
|
||||
fillColor = None, strokeColor = self.strokeColor, strokeWidth=sW*2))
|
||||
g.shift(x,y)
|
||||
g.scale(s/_size, s/_size)
|
||||
return g
|
||||
|
||||
def draw(self):
|
||||
kind = self.kind or 'None'
|
||||
f = self._cache.get(kind)
|
||||
if not f:
|
||||
f = getattr(self,'_Flag_'+kind)()
|
||||
self._cache[kind] = f._explode()
|
||||
return self._borderDraw(f)
|
||||
|
||||
def clone(self):
|
||||
return copy.copy(self)
|
||||
|
||||
def demo(self):
|
||||
D = Drawing(200, 100)
|
||||
name = self.availableFlagNames()
|
||||
import time
|
||||
name = name[int(time.time()) % len(name)]
|
||||
fx = Flag()
|
||||
fx.kind = name
|
||||
fx.x = 0
|
||||
fx.y = 0
|
||||
D.add(fx)
|
||||
labelFontSize = 10
|
||||
D.add(String(fx.x+(fx.size/2.0),(fx.y-(1.2*labelFontSize)),
|
||||
name, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
labelFontSize = int(fx.size/4.0)
|
||||
D.add(String(fx.x+(fx.size),(fx.y+((fx.size/2.0))),
|
||||
"SAMPLE", fillColor=colors.gold, textAnchor='middle',
|
||||
fontSize=labelFontSize, fontName="Helvetica-Bold"))
|
||||
return D
|
||||
|
||||
def _Flag_UK(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
w = s*2
|
||||
g.add(Rect(0, 0, w, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0))
|
||||
g.add(Polygon([0,0, s*.225,0, w,s*(1-.1125), w,s, w-s*.225,s, 0, s*.1125], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
|
||||
g.add(Polygon([0,s*(1-.1125), 0, s, s*.225,s, w, s*.1125, w,0, w-s*.225,0], fillColor = colors.mintcream, strokeColor=None, strokeWidth=0))
|
||||
g.add(Polygon([0, s-(s/15.0), (s-((s/10.0)*4)), (s*0.65), (s-(s/10.0)*3), (s*0.65), 0, s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Polygon([0, 0, (s-((s/10.0)*3)), (s*0.35), (s-((s/10.0)*2)), (s*0.35), (s/10.0), 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Polygon([w, s, (s+((s/10.0)*3)), (s*0.65), (s+((s/10.0)*2)), (s*0.65), w-(s/10.0), s], fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Polygon([w, (s/15.0), (s+((s/10.0)*4)), (s*0.35), (s+((s/10.0)*3)), (s*0.35), w, 0], fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(((s*0.42)*2), 0, width=(0.16*s)*2, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(0, (s*0.35), width=w, height=s*0.3, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(((s*0.45)*2), 0, width=(0.1*s)*2, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(0, (s*0.4), width=w, height=s*0.2, fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _Flag_USA(self):
|
||||
s = _size # abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s, fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
for stripecounter in range (13,0, -1):
|
||||
stripeheight = s/13.0
|
||||
if not (stripecounter%2 == 0):
|
||||
stripecolor = colors.red
|
||||
else:
|
||||
stripecolor = colors.mintcream
|
||||
redorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight,
|
||||
fillColor = stripecolor, strokeColor = None, strokeWidth=20)
|
||||
g.add(redorwhiteline)
|
||||
|
||||
bluebox = Rect(0, (s-(stripeheight*7)), width=0.8*s, height=stripeheight*7,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluebox)
|
||||
|
||||
lss = s*0.045
|
||||
lss2 = lss/2.0
|
||||
s9 = s/9.0
|
||||
s7 = s/7.0
|
||||
for starxcounter in range(5):
|
||||
for starycounter in range(4):
|
||||
ls = Star()
|
||||
ls.size = lss
|
||||
ls.x = 0-s/22.0+lss/2.0+s7+starxcounter*s7
|
||||
ls.fillColor = colors.mintcream
|
||||
ls.y = s-(starycounter+1)*s9+lss2
|
||||
g.add(ls)
|
||||
|
||||
for starxcounter in range(6):
|
||||
for starycounter in range(5):
|
||||
ls = Star()
|
||||
ls.size = lss
|
||||
ls.x = 0-(s/22.0)+lss/2.0+s/14.0+starxcounter*s7
|
||||
ls.fillColor = colors.mintcream
|
||||
ls.y = s-(starycounter+1)*s9+(s/18.0)+lss2
|
||||
g.add(ls)
|
||||
return g
|
||||
|
||||
def _Flag_Afghanistan(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
greenbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.limegreen, strokeColor = None, strokeWidth=0)
|
||||
g.add(greenbox)
|
||||
|
||||
blackbox = Rect(0, 0, width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.black, strokeColor = None, strokeWidth=0)
|
||||
g.add(blackbox)
|
||||
return g
|
||||
|
||||
def _Flag_Austria(self):
|
||||
s = _size # abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s, fillColor = colors.mintcream,
|
||||
strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
|
||||
redbox1 = Rect(0, 0, width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redbox1)
|
||||
|
||||
redbox2 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redbox2)
|
||||
return g
|
||||
|
||||
def _Flag_Belgium(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.black, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
|
||||
box1 = Rect(0, 0, width=(s/3.0)*2.0, height=s,
|
||||
fillColor = colors.black, strokeColor = None, strokeWidth=0)
|
||||
g.add(box1)
|
||||
|
||||
box2 = Rect(((s/3.0)*2.0), 0, width=(s/3.0)*2.0, height=s,
|
||||
fillColor = colors.gold, strokeColor = None, strokeWidth=0)
|
||||
g.add(box2)
|
||||
|
||||
box3 = Rect(((s/3.0)*4.0), 0, width=(s/3.0)*2.0, height=s,
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(box3)
|
||||
return g
|
||||
|
||||
def _Flag_China(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
self._width = w = s*1.5
|
||||
g.add(Rect(0, 0, w, s, fillColor=colors.red, strokeColor=None, strokeWidth=0))
|
||||
|
||||
def addStar(x,y,size,angle,g=g,w=s/20.0,x0=0,y0=s/2.0):
|
||||
s = Star()
|
||||
s.fillColor=colors.yellow
|
||||
s.angle = angle
|
||||
s.size = size*w*2
|
||||
s.x = x*w+x0
|
||||
s.y = y*w+y0
|
||||
g.add(s)
|
||||
|
||||
addStar(5,5,3, 0)
|
||||
addStar(10,1,1,36.86989765)
|
||||
addStar(12,3,1,8.213210702)
|
||||
addStar(12,6,1,16.60154960)
|
||||
addStar(10,8,1,53.13010235)
|
||||
return g
|
||||
|
||||
def _Flag_Cuba(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
for i in range(5):
|
||||
stripe = Rect(0, i*s/5.0, width=s*2, height=s/5.0,
|
||||
fillColor = [colors.darkblue, colors.mintcream][i%2],
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(stripe)
|
||||
|
||||
redwedge = Polygon(points = [ 0, 0, 4*s/5.0, (s/2.0), 0, s],
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redwedge)
|
||||
|
||||
star = Star()
|
||||
star.x = 2.5*s/10.0
|
||||
star.y = s/2.0
|
||||
star.size = 3*s/10.0
|
||||
star.fillColor = colors.white
|
||||
g.add(star)
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = None,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
return g
|
||||
|
||||
def _Flag_Denmark(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
self._width = w = s*1.4
|
||||
|
||||
box = Rect(0, 0, w, s,
|
||||
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
whitebox1 = Rect(((s/5.0)*2), 0, width=s/6.0, height=s,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whitebox1)
|
||||
|
||||
whitebox2 = Rect(0, ((s/2.0)-(s/12.0)), width=w, height=s/6.0,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whitebox2)
|
||||
return g
|
||||
|
||||
def _Flag_Finland(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
# crossbox specific bits
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.ghostwhite, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
blueline1 = Rect((s*0.6), 0, width=0.3*s, height=s,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(blueline1)
|
||||
|
||||
blueline2 = Rect(0, (s*0.4), width=s*2, height=s*0.3,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(blueline2)
|
||||
return g
|
||||
|
||||
def _Flag_France(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s, fillColor = colors.navy, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
bluebox = Rect(0, 0, width=((s/3.0)*2.0), height=s,
|
||||
fillColor = colors.blue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluebox)
|
||||
|
||||
whitebox = Rect(((s/3.0)*2.0), 0, width=((s/3.0)*2.0), height=s,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whitebox)
|
||||
|
||||
redbox = Rect(((s/3.0)*4.0), 0, width=((s/3.0)*2.0), height=s,
|
||||
fillColor = colors.red,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(redbox)
|
||||
return g
|
||||
|
||||
def _Flag_Germany(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.gold, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
blackbox1 = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.black, strokeColor = None, strokeWidth=0)
|
||||
g.add(blackbox1)
|
||||
|
||||
redbox1 = Rect(0, (s/3.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
|
||||
g.add(redbox1)
|
||||
return g
|
||||
|
||||
def _Flag_Greece(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s, fillColor = colors.gold,
|
||||
strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
for stripecounter in range (9,0, -1):
|
||||
stripeheight = s/9.0
|
||||
if not (stripecounter%2 == 0):
|
||||
stripecolor = colors.deepskyblue
|
||||
else:
|
||||
stripecolor = colors.mintcream
|
||||
|
||||
blueorwhiteline = Rect(0, (s-(stripeheight*stripecounter)), width=s*2, height=stripeheight,
|
||||
fillColor = stripecolor, strokeColor = None, strokeWidth=20)
|
||||
g.add(blueorwhiteline)
|
||||
|
||||
bluebox1 = Rect(0, ((s)-stripeheight*5), width=(stripeheight*5), height=stripeheight*5,
|
||||
fillColor = colors.deepskyblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluebox1)
|
||||
|
||||
whiteline1 = Rect(0, ((s)-stripeheight*3), width=stripeheight*5, height=stripeheight,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whiteline1)
|
||||
|
||||
whiteline2 = Rect((stripeheight*2), ((s)-stripeheight*5), width=stripeheight, height=stripeheight*5,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whiteline2)
|
||||
|
||||
return g
|
||||
|
||||
def _Flag_Ireland(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.forestgreen, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
whitebox = Rect(((s*2.0)/3.0), 0, width=(2.0*(s*2.0)/3.0), height=s,
|
||||
fillColor = colors.mintcream, strokeColor = None, strokeWidth=0)
|
||||
g.add(whitebox)
|
||||
|
||||
orangebox = Rect(((2.0*(s*2.0)/3.0)), 0, width=(s*2.0)/3.0, height=s,
|
||||
fillColor = colors.darkorange, strokeColor = None, strokeWidth=0)
|
||||
g.add(orangebox)
|
||||
return g
|
||||
|
||||
def _Flag_Italy(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
g.add(Rect(0,0,s*2,s,fillColor=colors.forestgreen,strokeColor=None, strokeWidth=0))
|
||||
g.add(Rect((2*s)/3.0, 0, width=(s*4)/3.0, height=s, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect((4*s)/3.0, 0, width=(s*2)/3.0, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _Flag_Japan(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
w = self._width = s*1.5
|
||||
g.add(Rect(0,0,w,s,fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
|
||||
g.add(Circle(cx=w/2.0,cy=s/2.0,r=0.3*w,fillColor=colors.red,strokeColor=None, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _Flag_Luxembourg(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redbox)
|
||||
|
||||
bluebox = Rect(0, 0, width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.dodgerblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluebox)
|
||||
return g
|
||||
|
||||
def _Flag_Holland(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.mintcream, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
redbox = Rect(0, ((s/3.0)*2.0), width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redbox)
|
||||
|
||||
bluebox = Rect(0, 0, width=s*2.0, height=s/3.0,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluebox)
|
||||
return g
|
||||
|
||||
def _Flag_Portugal(self):
|
||||
return Group()
|
||||
|
||||
def _Flag_Russia(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
w = self._width = s*1.5
|
||||
t = s/3.0
|
||||
g.add(Rect(0, 0, width=w, height=t, fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(0, t, width=w, height=t, fillColor = colors.blue, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(0, 2*t, width=w, height=t, fillColor = colors.mintcream, strokeColor = None, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _Flag_Spain(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
w = self._width = s*1.5
|
||||
g.add(Rect(0, 0, width=w, height=s, fillColor = colors.red, strokeColor = None, strokeWidth=0))
|
||||
g.add(Rect(0, (s/4.0), width=w, height=s/2.0, fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
|
||||
return g
|
||||
|
||||
def _Flag_Sweden(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
self._width = s*1.4
|
||||
box = Rect(0, 0, self._width, s,
|
||||
fillColor = colors.dodgerblue, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
box1 = Rect(((s/5.0)*2), 0, width=s/6.0, height=s,
|
||||
fillColor = colors.gold, strokeColor = None, strokeWidth=0)
|
||||
g.add(box1)
|
||||
|
||||
box2 = Rect(0, ((s/2.0)-(s/12.0)), width=self._width, height=s/6.0,
|
||||
fillColor = colors.gold,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(box2)
|
||||
return g
|
||||
|
||||
def _Flag_Norway(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
self._width = s*1.4
|
||||
|
||||
box = Rect(0, 0, self._width, s,
|
||||
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
box = Rect(0, 0, self._width, s,
|
||||
fillColor = colors.red, strokeColor = colors.black, strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
whiteline1 = Rect(((s*0.2)*2), 0, width=s*0.2, height=s,
|
||||
fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0)
|
||||
g.add(whiteline1)
|
||||
|
||||
whiteline2 = Rect(0, (s*0.4), width=self._width, height=s*0.2,
|
||||
fillColor = colors.ghostwhite, strokeColor = None, strokeWidth=0)
|
||||
g.add(whiteline2)
|
||||
|
||||
blueline1 = Rect(((s*0.225)*2), 0, width=0.1*s, height=s,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(blueline1)
|
||||
|
||||
blueline2 = Rect(0, (s*0.45), width=self._width, height=s*0.1,
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(blueline2)
|
||||
return g
|
||||
|
||||
def _Flag_CzechRepublic(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.mintcream,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
redbox = Rect(0, 0, width=s*2, height=s/2.0,
|
||||
fillColor = colors.red,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(redbox)
|
||||
|
||||
bluewedge = Polygon(points = [ 0, 0, s, (s/2.0), 0, s],
|
||||
fillColor = colors.darkblue, strokeColor = None, strokeWidth=0)
|
||||
g.add(bluewedge)
|
||||
return g
|
||||
|
||||
def _Flag_Palestine(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
box = Rect(0, s/3.0, s*2, s/3.0,
|
||||
fillColor = colors.mintcream,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
greenbox = Rect(0, 0, width=s*2, height=s/3.0,
|
||||
fillColor = colors.limegreen,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(greenbox)
|
||||
|
||||
blackbox = Rect(0, 2*s/3.0, width=s*2, height=s/3.0,
|
||||
fillColor = colors.black,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(blackbox)
|
||||
|
||||
redwedge = Polygon(points = [ 0, 0, 2*s/3.0, (s/2.0), 0, s],
|
||||
fillColor = colors.red, strokeColor = None, strokeWidth=0)
|
||||
g.add(redwedge)
|
||||
return g
|
||||
|
||||
def _Flag_Turkey(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
|
||||
box = Rect(0, 0, s*2, s,
|
||||
fillColor = colors.red,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(box)
|
||||
|
||||
whitecircle = Circle(cx=((s*0.35)*2), cy=s/2.0, r=s*0.3,
|
||||
fillColor = colors.mintcream,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(whitecircle)
|
||||
|
||||
redcircle = Circle(cx=((s*0.39)*2), cy=s/2.0, r=s*0.24,
|
||||
fillColor = colors.red,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(redcircle)
|
||||
|
||||
ws = Star()
|
||||
ws.angle = 15
|
||||
ws.size = s/5.0
|
||||
ws.x = (s*0.5)*2+ws.size/2.0
|
||||
ws.y = (s*0.5)
|
||||
ws.fillColor = colors.mintcream
|
||||
ws.strokeColor = None
|
||||
g.add(ws)
|
||||
return g
|
||||
|
||||
def _Flag_Switzerland(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
self._width = s
|
||||
|
||||
g.add(Rect(0, 0, s, s, fillColor = colors.red, strokeColor = colors.black, strokeWidth=0))
|
||||
g.add(Line((s/2.0), (s/5.5), (s/2), (s-(s/5.5)),
|
||||
fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=(s/5.0)))
|
||||
g.add(Line((s/5.5), (s/2.0), (s-(s/5.5)), (s/2.0),
|
||||
fillColor = colors.mintcream, strokeColor = colors.mintcream, strokeWidth=s/5.0))
|
||||
return g
|
||||
|
||||
def _Flag_EU(self):
|
||||
s = _size
|
||||
g = Group()
|
||||
w = self._width = 1.5*s
|
||||
|
||||
g.add(Rect(0, 0, w, s, fillColor = colors.darkblue, strokeColor = None, strokeWidth=0))
|
||||
centerx=w/2.0
|
||||
centery=s/2.0
|
||||
radius=s/3.0
|
||||
yradius = radius
|
||||
xradius = radius
|
||||
nStars = 12
|
||||
delta = 2*pi/nStars
|
||||
for i in range(nStars):
|
||||
rad = i*delta
|
||||
gs = Star()
|
||||
gs.x=cos(rad)*radius+centerx
|
||||
gs.y=sin(rad)*radius+centery
|
||||
gs.size=s/10.0
|
||||
gs.fillColor=colors.gold
|
||||
g.add(gs)
|
||||
return g
|
||||
|
||||
def _Flag_Brazil(self):
|
||||
s = _size # abbreviate as we will use this a lot
|
||||
g = Group()
|
||||
|
||||
m = s/14.0
|
||||
self._width = w = (m * 20)
|
||||
|
||||
def addStar(x,y,size, g=g, w=w, s=s, m=m):
|
||||
st = Star()
|
||||
st.fillColor=colors.mintcream
|
||||
st.size = size*m
|
||||
st.x = (w/2.0) + (x * (0.35 * m))
|
||||
st.y = (s/2.0) + (y * (0.35 * m))
|
||||
g.add(st)
|
||||
|
||||
g.add(Rect(0, 0, w, s, fillColor = colors.green, strokeColor = None, strokeWidth=0))
|
||||
g.add(Polygon(points = [ 1.7*m, (s/2.0), (w/2.0), s-(1.7*m), w-(1.7*m),(s/2.0),(w/2.0), 1.7*m],
|
||||
fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
|
||||
g.add(Circle(cx=w/2.0, cy=s/2.0, r=3.5*m,
|
||||
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
|
||||
g.add(Wedge((w/2.0)-(2*m), 0, 8.5*m, 50, 98.1, 8.5*m,
|
||||
fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
|
||||
g.add(Wedge((w/2.0), (s/2.0), 3.501*m, 156, 352, 3.501*m,
|
||||
fillColor=colors.mintcream,strokeColor=None, strokeWidth=0))
|
||||
g.add(Wedge((w/2.0)-(2*m), 0, 8*m, 48.1, 100, 8*m,
|
||||
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
|
||||
g.add(Rect(0, 0, w, (s/4.0) + 1.7*m,
|
||||
fillColor = colors.green, strokeColor = None, strokeWidth=0))
|
||||
g.add(Polygon(points = [ 1.7*m,(s/2.0), (w/2.0),s/2.0 - 2*m, w-(1.7*m),(s/2.0) , (w/2.0),1.7*m],
|
||||
fillColor = colors.yellow, strokeColor = None, strokeWidth=0))
|
||||
g.add(Wedge(w/2.0, s/2.0, 3.502*m, 166, 342.1, 3.502*m,
|
||||
fillColor=colors.blue,strokeColor=None, strokeWidth=0))
|
||||
|
||||
addStar(3.2,3.5,0.3)
|
||||
addStar(-8.5,1.5,0.3)
|
||||
addStar(-7.5,-3,0.3)
|
||||
addStar(-4,-5.5,0.3)
|
||||
addStar(0,-4.5,0.3)
|
||||
addStar(7,-3.5,0.3)
|
||||
addStar(-3.5,-0.5,0.25)
|
||||
addStar(0,-1.5,0.25)
|
||||
addStar(1,-2.5,0.25)
|
||||
addStar(3,-7,0.25)
|
||||
addStar(5,-6.5,0.25)
|
||||
addStar(6.5,-5,0.25)
|
||||
addStar(7,-4.5,0.25)
|
||||
addStar(-5.5,-3.2,0.25)
|
||||
addStar(-6,-4.2,0.25)
|
||||
addStar(-1,-2.75,0.2)
|
||||
addStar(2,-5.5,0.2)
|
||||
addStar(4,-5.5,0.2)
|
||||
addStar(5,-7.5,0.2)
|
||||
addStar(5,-5.5,0.2)
|
||||
addStar(6,-5.5,0.2)
|
||||
addStar(-8.8,-3.2,0.2)
|
||||
addStar(2.5,0.5,0.2)
|
||||
addStar(-0.2,-3.2,0.14)
|
||||
addStar(-7.2,-2,0.14)
|
||||
addStar(0,-8,0.1)
|
||||
|
||||
sTmp = "ORDEM E PROGRESSO"
|
||||
nTmp = len(sTmp)
|
||||
delta = 0.850848010347/nTmp
|
||||
radius = 7.9 *m
|
||||
centerx = (w/2.0)-(2*m)
|
||||
centery = 0
|
||||
for i in range(nTmp):
|
||||
rad = 2*pi - i*delta -4.60766922527
|
||||
x=cos(rad)*radius+centerx
|
||||
y=sin(rad)*radius+centery
|
||||
if i == 6:
|
||||
z = 0.35*m
|
||||
else:
|
||||
z= 0.45*m
|
||||
g2 = Group(String(x, y, sTmp[i], fontName='Helvetica-Bold',
|
||||
fontSize = z,strokeColor=None,fillColor=colors.green))
|
||||
g2.rotate(rad)
|
||||
g.add(g2)
|
||||
return g
|
||||
|
||||
def makeFlag(name):
|
||||
flag = Flag()
|
||||
flag.kind = name
|
||||
return flag
|
||||
|
||||
def test():
|
||||
"""This function produces three pdf files with examples of all the signs and symbols from this file.
|
||||
"""
|
||||
# page 1
|
||||
|
||||
labelFontSize = 10
|
||||
|
||||
X = (20,245)
|
||||
|
||||
flags = [
|
||||
'UK',
|
||||
'USA',
|
||||
'Afghanistan',
|
||||
'Austria',
|
||||
'Belgium',
|
||||
'Denmark',
|
||||
'Cuba',
|
||||
'Finland',
|
||||
'France',
|
||||
'Germany',
|
||||
'Greece',
|
||||
'Ireland',
|
||||
'Italy',
|
||||
'Luxembourg',
|
||||
'Holland',
|
||||
'Palestine',
|
||||
'Portugal',
|
||||
'Spain',
|
||||
'Sweden',
|
||||
'Norway',
|
||||
'CzechRepublic',
|
||||
'Turkey',
|
||||
'Switzerland',
|
||||
'EU',
|
||||
'Brazil',
|
||||
]
|
||||
y = Y0 = 530
|
||||
f = 0
|
||||
D = None
|
||||
for name in flags:
|
||||
if not D: D = Drawing(450,650)
|
||||
flag = makeFlag(name)
|
||||
i = flags.index(name)
|
||||
flag.x = X[i%2]
|
||||
flag.y = y
|
||||
D.add(flag)
|
||||
D.add(String(flag.x+(flag.size/2.0),(flag.y-(1.2*labelFontSize)),
|
||||
name, fillColor=colors.black, textAnchor='middle', fontSize=labelFontSize))
|
||||
if i%2: y = y - 125
|
||||
if (i%2 and y<0) or name==flags[-1]:
|
||||
renderPDF.drawToFile(D, 'flags%02d.pdf'%f, 'flags.py - Page #%d'%(f+1))
|
||||
y = Y0
|
||||
f = f+1
|
||||
D = None
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
@@ -0,0 +1,542 @@
|
||||
#Copyright ReportLab Europe Ltd. 2000-2017
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/grids.py
|
||||
__version__='3.3.0'
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.validators import isNumber, isColorOrNone, isBoolean, isListOfNumbers, OneOf, isListOfColors, isNumberOrNone
|
||||
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
||||
from reportlab.graphics.shapes import Drawing, Group, Line, Rect, LineShape, definePath, EmptyClipPath
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
from math import radians
|
||||
from reportlab.graphics.transform import translate, rotate, mmult, transformPoints, inverse
|
||||
from reportlab.lib.utils import flatten
|
||||
|
||||
def frange(start, end=None, inc=None):
|
||||
"A range function, that does accept float increments..."
|
||||
|
||||
if end == None:
|
||||
end = start + 0.0
|
||||
start = 0.0
|
||||
|
||||
if inc == None:
|
||||
inc = 1.0
|
||||
|
||||
L = []
|
||||
end = end - inc*0.0001 #to avoid numrical problems
|
||||
while 1:
|
||||
next = start + len(L) * inc
|
||||
if inc > 0 and next >= end:
|
||||
break
|
||||
elif inc < 0 and next <= end:
|
||||
break
|
||||
L.append(next)
|
||||
|
||||
return L
|
||||
|
||||
|
||||
def makeDistancesList(list):
|
||||
"""Returns a list of distances between adjacent numbers in some input list.
|
||||
|
||||
E.g. [1, 1, 2, 3, 5, 7] -> [0, 1, 1, 2, 2]
|
||||
"""
|
||||
|
||||
d = []
|
||||
for i in range(len(list[:-1])):
|
||||
d.append(list[i+1] - list[i])
|
||||
|
||||
return d
|
||||
|
||||
|
||||
class Grid(Widget):
|
||||
"""This makes a rectangular grid of equidistant stripes.
|
||||
|
||||
The grid contains an outer border rectangle, and stripes
|
||||
inside which can be drawn with lines and/or as solid tiles.
|
||||
The drawing order is: outer rectangle, then lines and tiles.
|
||||
|
||||
The stripes' width is indicated as 'delta'. The sequence of
|
||||
stripes can have an offset named 'delta0'. Both values need
|
||||
to be positive!
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
|
||||
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
|
||||
width = AttrMapValue(isNumber, desc="The grid's width."),
|
||||
height = AttrMapValue(isNumber, desc="The grid's height."),
|
||||
orientation = AttrMapValue(OneOf(('vertical', 'horizontal')),
|
||||
desc='Determines if stripes are vertical or horizontal.'),
|
||||
useLines = AttrMapValue(OneOf((0, 1)),
|
||||
desc='Determines if stripes are drawn with lines.'),
|
||||
useRects = AttrMapValue(OneOf((0, 1)),
|
||||
desc='Determines if stripes are drawn with solid rectangles.'),
|
||||
delta = AttrMapValue(isNumber,
|
||||
desc='Determines the width/height of the stripes.'),
|
||||
delta0 = AttrMapValue(isNumber,
|
||||
desc='Determines the stripes initial width/height offset.'),
|
||||
deltaSteps = AttrMapValue(isListOfNumbers,
|
||||
desc='List of deltas to be used cyclically.'),
|
||||
stripeColors = AttrMapValue(isListOfColors,
|
||||
desc='Colors applied cyclically in the right or upper direction.'),
|
||||
fillColor = AttrMapValue(isColorOrNone,
|
||||
desc='Background color for entire rectangle.'),
|
||||
strokeColor = AttrMapValue(isColorOrNone,
|
||||
desc='Color used for lines.'),
|
||||
strokeWidth = AttrMapValue(isNumber,
|
||||
desc='Width used for lines.'),
|
||||
rectStrokeColor = AttrMapValue(isColorOrNone, desc='Color for outer rect stroke.'),
|
||||
rectStrokeWidth = AttrMapValue(isNumberOrNone, desc='Width for outer rect stroke.'),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.width = 100
|
||||
self.height = 100
|
||||
self.orientation = 'vertical'
|
||||
self.useLines = 0
|
||||
self.useRects = 1
|
||||
self.delta = 20
|
||||
self.delta0 = 0
|
||||
self.deltaSteps = []
|
||||
self.fillColor = colors.white
|
||||
self.stripeColors = [colors.red, colors.green, colors.blue]
|
||||
self.strokeColor = colors.black
|
||||
self.strokeWidth = 2
|
||||
|
||||
|
||||
def demo(self):
|
||||
D = Drawing(100, 100)
|
||||
|
||||
g = Grid()
|
||||
D.add(g)
|
||||
|
||||
return D
|
||||
|
||||
def makeOuterRect(self):
|
||||
strokeColor = getattr(self,'rectStrokeColor',self.strokeColor)
|
||||
strokeWidth = getattr(self,'rectStrokeWidth',self.strokeWidth)
|
||||
if self.fillColor or (strokeColor and strokeWidth):
|
||||
rect = Rect(self.x, self.y, self.width, self.height)
|
||||
rect.fillColor = self.fillColor
|
||||
rect.strokeColor = strokeColor
|
||||
rect.strokeWidth = strokeWidth
|
||||
return rect
|
||||
else:
|
||||
return None
|
||||
|
||||
def makeLinePosList(self, start, isX=0):
|
||||
"Returns a list of positions where to place lines."
|
||||
|
||||
w, h = self.width, self.height
|
||||
if isX:
|
||||
length = w
|
||||
else:
|
||||
length = h
|
||||
if self.deltaSteps:
|
||||
r = [start + self.delta0]
|
||||
i = 0
|
||||
while 1:
|
||||
if r[-1] > start + length:
|
||||
del r[-1]
|
||||
break
|
||||
r.append(r[-1] + self.deltaSteps[i % len(self.deltaSteps)])
|
||||
i = i + 1
|
||||
else:
|
||||
r = frange(start + self.delta0, start + length, self.delta)
|
||||
|
||||
r.append(start + length)
|
||||
if self.delta0 != 0:
|
||||
r.insert(0, start)
|
||||
#print 'Grid.makeLinePosList() -> %s' % r
|
||||
return r
|
||||
|
||||
|
||||
def makeInnerLines(self):
|
||||
# inner grid lines
|
||||
group = Group()
|
||||
|
||||
w, h = self.width, self.height
|
||||
|
||||
if self.useLines == 1:
|
||||
if self.orientation == 'vertical':
|
||||
r = self.makeLinePosList(self.x, isX=1)
|
||||
for x in r:
|
||||
line = Line(x, self.y, x, self.y + h)
|
||||
line.strokeColor = self.strokeColor
|
||||
line.strokeWidth = self.strokeWidth
|
||||
group.add(line)
|
||||
elif self.orientation == 'horizontal':
|
||||
r = self.makeLinePosList(self.y, isX=0)
|
||||
for y in r:
|
||||
line = Line(self.x, y, self.x + w, y)
|
||||
line.strokeColor = self.strokeColor
|
||||
line.strokeWidth = self.strokeWidth
|
||||
group.add(line)
|
||||
|
||||
return group
|
||||
|
||||
|
||||
def makeInnerTiles(self):
|
||||
# inner grid lines
|
||||
group = Group()
|
||||
|
||||
w, h = self.width, self.height
|
||||
|
||||
# inner grid stripes (solid rectangles)
|
||||
if self.useRects == 1:
|
||||
cols = self.stripeColors
|
||||
|
||||
if self.orientation == 'vertical':
|
||||
r = self.makeLinePosList(self.x, isX=1)
|
||||
elif self.orientation == 'horizontal':
|
||||
r = self.makeLinePosList(self.y, isX=0)
|
||||
|
||||
dist = makeDistancesList(r)
|
||||
|
||||
i = 0
|
||||
for j in range(len(dist)):
|
||||
if self.orientation == 'vertical':
|
||||
x = r[j]
|
||||
stripe = Rect(x, self.y, dist[j], h)
|
||||
elif self.orientation == 'horizontal':
|
||||
y = r[j]
|
||||
stripe = Rect(self.x, y, w, dist[j])
|
||||
stripe.fillColor = cols[i % len(cols)]
|
||||
stripe.strokeColor = None
|
||||
group.add(stripe)
|
||||
i = i + 1
|
||||
|
||||
return group
|
||||
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
group = Group()
|
||||
|
||||
group.add(self.makeOuterRect())
|
||||
group.add(self.makeInnerTiles())
|
||||
group.add(self.makeInnerLines(),name='_gridLines')
|
||||
|
||||
return group
|
||||
|
||||
|
||||
class DoubleGrid(Widget):
|
||||
"""This combines two ordinary Grid objects orthogonal to each other.
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
|
||||
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
|
||||
width = AttrMapValue(isNumber, desc="The grid's width."),
|
||||
height = AttrMapValue(isNumber, desc="The grid's height."),
|
||||
grid0 = AttrMapValue(None, desc="The first grid component."),
|
||||
grid1 = AttrMapValue(None, desc="The second grid component."),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.width = 100
|
||||
self.height = 100
|
||||
|
||||
g0 = Grid()
|
||||
g0.x = self.x
|
||||
g0.y = self.y
|
||||
g0.width = self.width
|
||||
g0.height = self.height
|
||||
g0.orientation = 'vertical'
|
||||
g0.useLines = 1
|
||||
g0.useRects = 0
|
||||
g0.delta = 20
|
||||
g0.delta0 = 0
|
||||
g0.deltaSteps = []
|
||||
g0.fillColor = colors.white
|
||||
g0.stripeColors = [colors.red, colors.green, colors.blue]
|
||||
g0.strokeColor = colors.black
|
||||
g0.strokeWidth = 1
|
||||
|
||||
g1 = Grid()
|
||||
g1.x = self.x
|
||||
g1.y = self.y
|
||||
g1.width = self.width
|
||||
g1.height = self.height
|
||||
g1.orientation = 'horizontal'
|
||||
g1.useLines = 1
|
||||
g1.useRects = 0
|
||||
g1.delta = 20
|
||||
g1.delta0 = 0
|
||||
g1.deltaSteps = []
|
||||
g1.fillColor = colors.white
|
||||
g1.stripeColors = [colors.red, colors.green, colors.blue]
|
||||
g1.strokeColor = colors.black
|
||||
g1.strokeWidth = 1
|
||||
|
||||
self.grid0 = g0
|
||||
self.grid1 = g1
|
||||
|
||||
|
||||
## # This gives an AttributeError:
|
||||
## # DoubleGrid instance has no attribute 'grid0'
|
||||
## def __setattr__(self, name, value):
|
||||
## if name in ('x', 'y', 'width', 'height'):
|
||||
## setattr(self.grid0, name, value)
|
||||
## setattr(self.grid1, name, value)
|
||||
|
||||
|
||||
def demo(self):
|
||||
D = Drawing(100, 100)
|
||||
g = DoubleGrid()
|
||||
D.add(g)
|
||||
return D
|
||||
|
||||
|
||||
def draw(self):
|
||||
group = Group()
|
||||
g0, g1 = self.grid0, self.grid1
|
||||
# Order groups to make sure both v and h lines
|
||||
# are visible (works only when there is only
|
||||
# one kind of stripes, v or h).
|
||||
G = g0.useRects == 1 and g1.useRects == 0 and (g0,g1) or (g1,g0)
|
||||
for g in G:
|
||||
group.add(g.makeOuterRect())
|
||||
for g in G:
|
||||
group.add(g.makeInnerTiles())
|
||||
group.add(g.makeInnerLines(),name='_gridLines')
|
||||
|
||||
return group
|
||||
|
||||
|
||||
class ShadedRect(Widget):
|
||||
"""This makes a rectangle with shaded colors between two colors.
|
||||
|
||||
Colors are interpolated linearly between 'fillColorStart'
|
||||
and 'fillColorEnd', both of which appear at the margins.
|
||||
If 'numShades' is set to one, though, only 'fillColorStart'
|
||||
is used.
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber, desc="The grid's lower-left x position."),
|
||||
y = AttrMapValue(isNumber, desc="The grid's lower-left y position."),
|
||||
width = AttrMapValue(isNumber, desc="The grid's width."),
|
||||
height = AttrMapValue(isNumber, desc="The grid's height."),
|
||||
orientation = AttrMapValue(OneOf(('vertical', 'horizontal')), desc='Determines if stripes are vertical or horizontal.'),
|
||||
numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'),
|
||||
fillColorStart = AttrMapValue(isColorOrNone, desc='Start value of the color shade.'),
|
||||
fillColorEnd = AttrMapValue(isColorOrNone, desc='End value of the color shade.'),
|
||||
strokeColor = AttrMapValue(isColorOrNone, desc='Color used for border line.'),
|
||||
strokeWidth = AttrMapValue(isNumber, desc='Width used for lines.'),
|
||||
cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'),
|
||||
)
|
||||
|
||||
def __init__(self,**kw):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.width = 100
|
||||
self.height = 100
|
||||
self.orientation = 'vertical'
|
||||
self.numShades = 20
|
||||
self.fillColorStart = colors.pink
|
||||
self.fillColorEnd = colors.black
|
||||
self.strokeColor = colors.black
|
||||
self.strokeWidth = 2
|
||||
self.cylinderMode = 0
|
||||
self.setProperties(kw)
|
||||
|
||||
def demo(self):
|
||||
D = Drawing(100, 100)
|
||||
g = ShadedRect()
|
||||
D.add(g)
|
||||
|
||||
return D
|
||||
|
||||
def _flipRectCorners(self):
|
||||
"Flip rectangle's corners if width or height is negative."
|
||||
x, y, width, height, fillColorStart, fillColorEnd = self.x, self.y, self.width, self.height, self.fillColorStart, self.fillColorEnd
|
||||
if width < 0 and height > 0:
|
||||
x = x + width
|
||||
width = -width
|
||||
if self.orientation=='vertical': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart
|
||||
elif height<0 and width>0:
|
||||
y = y + height
|
||||
height = -height
|
||||
if self.orientation=='horizontal': fillColorStart, fillColorEnd = fillColorEnd, fillColorStart
|
||||
elif height < 0 and height < 0:
|
||||
x = x + width
|
||||
width = -width
|
||||
y = y + height
|
||||
height = -height
|
||||
return x, y, width, height, fillColorStart, fillColorEnd
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
group = Group()
|
||||
x, y, w, h, c0, c1 = self._flipRectCorners()
|
||||
vertical = self.orientation == 'vertical'
|
||||
cylinderMode = self.cylinderMode
|
||||
linG = getattr(getattr(self,'_canvas',None),'linearGradient',None)
|
||||
if linG:
|
||||
canv = linG.__self__
|
||||
canv.saveState()
|
||||
p = canv.beginPath()
|
||||
p.rect(x, y, w, h)
|
||||
canv.clipPath(p, stroke=0)
|
||||
if cylinderMode:
|
||||
if vertical:
|
||||
linG(x, y, x+w/2, y, (c0,c1), extend=False)
|
||||
linG(x+w/2, y, x+w, y, (c1,c0), extend=False)
|
||||
else:
|
||||
linG(x, y, x, y+h/2, (c0,c1), extend=False)
|
||||
linG(x, y+h/2, x, y+h, (c1,c0), extend=False)
|
||||
else:
|
||||
if vertical:
|
||||
linG(x, y, x+w, y, (c0,c1), extend=False)
|
||||
else:
|
||||
linG(x, y, x, y+h, (c0,c1), extend=False)
|
||||
canv.restoreState()
|
||||
else:
|
||||
numShades = self.numShades
|
||||
if cylinderMode:
|
||||
if not numShades%2: numShades = numShades+1
|
||||
halfNumShades = int((numShades-1)/2) + 1
|
||||
num = float(numShades) # must make it float!
|
||||
if vertical:
|
||||
if numShades == 1:
|
||||
V = [x]
|
||||
else:
|
||||
V = frange(x, x + w, w/num)
|
||||
else:
|
||||
if numShades == 1:
|
||||
V = [y]
|
||||
else:
|
||||
V = frange(y, y + h, h/num)
|
||||
|
||||
for v in V:
|
||||
stripe = vertical and Rect(v, y, w/num, h) or Rect(x, v, w, h/num)
|
||||
if cylinderMode:
|
||||
if V.index(v)>=halfNumShades:
|
||||
col = colors.linearlyInterpolatedColor(c1,c0,V[halfNumShades],V[-1], v)
|
||||
else:
|
||||
col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[halfNumShades], v)
|
||||
else:
|
||||
col = colors.linearlyInterpolatedColor(c0,c1,V[0],V[-1], v)
|
||||
stripe.fillColor = col
|
||||
stripe.strokeColor = col
|
||||
stripe.strokeWidth = 1
|
||||
group.add(stripe)
|
||||
if self.strokeColor and self.strokeWidth>=0:
|
||||
rect = Rect(x, y, w, h)
|
||||
rect.strokeColor = self.strokeColor
|
||||
rect.strokeWidth = self.strokeWidth
|
||||
rect.fillColor = None
|
||||
group.add(rect)
|
||||
return group
|
||||
|
||||
|
||||
def colorRange(c0, c1, n):
|
||||
"Return a range of intermediate colors between c0 and c1"
|
||||
if n==1: return [c0]
|
||||
|
||||
C = []
|
||||
if n>1:
|
||||
lim = n-1
|
||||
for i in range(n):
|
||||
C.append(colors.linearlyInterpolatedColor(c0,c1,0,lim, i))
|
||||
return C
|
||||
|
||||
|
||||
def centroid(P):
|
||||
'''compute average point of a set of points'''
|
||||
cx = 0
|
||||
cy = 0
|
||||
for x,y in P:
|
||||
cx+=x
|
||||
cy+=y
|
||||
n = len(P)
|
||||
return cx/n, cy/n
|
||||
|
||||
def rotatedEnclosingRect(P, angle, rect):
|
||||
'''
|
||||
given P a sequence P of x,y coordinate pairs and an angle in degrees
|
||||
find the centroid of P and the axis at angle theta through it
|
||||
find the extreme points of P wrt axis parallel distance and axis
|
||||
orthogonal distance. Then compute the least rectangle that will still
|
||||
enclose P when rotated by angle. Positive angles correspond to clockwise
|
||||
rotation of the enclosing rect.
|
||||
'''
|
||||
x0, y0 = centroid(P)
|
||||
theta = radians(angle)
|
||||
#translate to the centroid and rotate
|
||||
mx = mmult(translate(x0,y0),rotate(angle))
|
||||
|
||||
#compute min and max of x and y of the rotated points
|
||||
tp = flatten(transformPoints(mx,P))
|
||||
xx = tp[::2]
|
||||
yx = tp[1::2]
|
||||
xn = min(xx)
|
||||
xx = max(xx)
|
||||
yn = min(yx)
|
||||
yx = max(yx)
|
||||
|
||||
#make the enclosing rect and invert the original transform
|
||||
rect.x = xn
|
||||
rect.width = xx-xn
|
||||
rect.y = yn
|
||||
rect.height = yx-yn
|
||||
g = Group(transform=inverse(mx))
|
||||
g.add(rect)
|
||||
return g
|
||||
|
||||
class ShadedPolygon(Widget,LineShape):
|
||||
'''given a list of points [(x0,y0),....] we construct an enclosing
|
||||
shaded rectangle and mask using the polygon points.
|
||||
At angle 0 the shading fillColorStart left --> fillColorEnd right.
|
||||
positive angles rotate the shading clockwise.
|
||||
'''
|
||||
_attrMap = AttrMap(BASE=LineShape,
|
||||
angle = AttrMapValue(isNumber,desc="Shading angle"),
|
||||
fillColorStart = AttrMapValue(isColorOrNone),
|
||||
fillColorEnd = AttrMapValue(isColorOrNone),
|
||||
numShades = AttrMapValue(isNumber, desc='The number of interpolating colors.'),
|
||||
cylinderMode = AttrMapValue(isBoolean, desc='True if shading reverses in middle.'),
|
||||
points = AttrMapValue(isListOfNumbers),
|
||||
)
|
||||
|
||||
def __init__(self,**kw):
|
||||
self.angle = 90
|
||||
self.fillColorStart = colors.red
|
||||
self.fillColorEnd = colors.green
|
||||
self.cylinderMode = 0
|
||||
self.numShades = 50
|
||||
self.points = [-1,-1,2,2,3,-1]
|
||||
LineShape.__init__(self,kw)
|
||||
|
||||
def draw(self):
|
||||
P = self.points
|
||||
P = list(zip(P[::2],P[1::2]))
|
||||
path = definePath([('moveTo',)+P[0]]+[('lineTo',)+x for x in P[1:]]+['closePath'],
|
||||
fillColor=None, strokeColor=None)
|
||||
path.isClipPath = 1
|
||||
g = Group()
|
||||
g.add(path)
|
||||
angle = self.angle % 360
|
||||
orientation = 'horizontal' if 0<=angle<=45 or 315<=angle<=360 or 135<=angle<=225 else 'vertical'
|
||||
rect = ShadedRect(strokeWidth=0,strokeColor=None,orientation=orientation)
|
||||
for k in 'fillColorStart', 'fillColorEnd', 'numShades', 'cylinderMode':
|
||||
setattr(rect,k,getattr(self,k))
|
||||
g.add(rotatedEnclosingRect(P, angle, rect))
|
||||
g.add(EmptyClipPath)
|
||||
path = path.copy()
|
||||
path.isClipPath = 0
|
||||
path.strokeColor = self.strokeColor
|
||||
path.strokeWidth = self.strokeWidth
|
||||
g.add(path)
|
||||
return g
|
||||
|
||||
if __name__=='__main__': #noruntests
|
||||
angle=45
|
||||
D = Drawing(120,120)
|
||||
D.add(ShadedPolygon(points=(10,10,60,60,110,10),strokeColor=None,strokeWidth=1,angle=90,numShades=50,cylinderMode=0))
|
||||
D.save(formats=['pdf','gif'],fnRoot='shobj',outDir='/tmp')
|
||||
@@ -0,0 +1,245 @@
|
||||
#Copyright ReportLab Europe Ltd. 2000-2017
|
||||
#see license.txt for license details
|
||||
|
||||
__version__='3.3.0'
|
||||
__doc__="""This modules defines a collection of markers used in charts.
|
||||
"""
|
||||
|
||||
from reportlab.graphics.shapes import Rect, Circle, Polygon, Drawing, Group
|
||||
from reportlab.graphics.widgets.signsandsymbols import SmileyFace
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
from reportlab.lib.validators import isNumber, isColorOrNone, OneOf, Validator
|
||||
from reportlab.lib.attrmap import AttrMap, AttrMapValue
|
||||
from reportlab.lib.colors import black
|
||||
from reportlab.lib.utils import isClass
|
||||
from reportlab.graphics.widgets.flags import Flag, _Symbol
|
||||
from math import sin, cos, pi
|
||||
_toradians = pi/180.0
|
||||
|
||||
class Marker(Widget):
|
||||
'''A polymorphic class of markers'''
|
||||
_attrMap = AttrMap(BASE=Widget,
|
||||
kind = AttrMapValue(
|
||||
OneOf(None, 'Square', 'Diamond', 'Circle', 'Cross', 'Triangle', 'StarSix',
|
||||
'Pentagon', 'Hexagon', 'Heptagon', 'Octagon', 'StarFive',
|
||||
'FilledSquare', 'FilledCircle', 'FilledDiamond', 'FilledCross',
|
||||
'FilledTriangle','FilledStarSix', 'FilledPentagon', 'FilledHexagon',
|
||||
'FilledHeptagon', 'FilledOctagon', 'FilledStarFive',
|
||||
'Smiley','ArrowHead', 'FilledArrowHead'),
|
||||
desc='marker type name'),
|
||||
size = AttrMapValue(isNumber,desc='marker size'),
|
||||
x = AttrMapValue(isNumber,desc='marker x coordinate'),
|
||||
y = AttrMapValue(isNumber,desc='marker y coordinate'),
|
||||
dx = AttrMapValue(isNumber,desc='marker x coordinate adjustment'),
|
||||
dy = AttrMapValue(isNumber,desc='marker y coordinate adjustment'),
|
||||
angle = AttrMapValue(isNumber,desc='marker rotation'),
|
||||
fillColor = AttrMapValue(isColorOrNone, desc='marker fill colour'),
|
||||
strokeColor = AttrMapValue(isColorOrNone, desc='marker stroke colour'),
|
||||
strokeWidth = AttrMapValue(isNumber, desc='marker stroke width'),
|
||||
arrowBarbDx = AttrMapValue(isNumber, desc='arrow only the delta x for the barbs'),
|
||||
arrowHeight = AttrMapValue(isNumber, desc='arrow only height'),
|
||||
)
|
||||
|
||||
def __init__(self,*args,**kw):
|
||||
self.setProperties(kw)
|
||||
self._setKeywords(
|
||||
kind = None,
|
||||
strokeColor = black,
|
||||
strokeWidth = 0.1,
|
||||
fillColor = None,
|
||||
size = 5,
|
||||
x = 0,
|
||||
y = 0,
|
||||
dx = 0,
|
||||
dy = 0,
|
||||
angle = 0,
|
||||
arrowBarbDx = -1.25,
|
||||
arrowHeight = 1.875,
|
||||
)
|
||||
|
||||
def clone(self,**kwds):
|
||||
n = self.__class__(**self.__dict__)
|
||||
if kwds: n.__dict__.update(kwds)
|
||||
return n
|
||||
|
||||
def _Smiley(self):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
d = self.size/2.0
|
||||
s = SmileyFace()
|
||||
s.fillColor = self.fillColor
|
||||
s.strokeWidth = self.strokeWidth
|
||||
s.strokeColor = self.strokeColor
|
||||
s.x = x-d
|
||||
s.y = y-d
|
||||
s.size = d*2
|
||||
return s
|
||||
|
||||
def _Square(self):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
d = self.size/2.0
|
||||
s = Rect(x-d,y-d,2*d,2*d,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth)
|
||||
return s
|
||||
|
||||
def _Diamond(self):
|
||||
d = self.size/2.0
|
||||
return self._doPolygon((-d,0,0,d,d,0,0,-d))
|
||||
|
||||
def _Circle(self):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
s = Circle(x,y,self.size/2.0,fillColor=self.fillColor,strokeColor=self.strokeColor,strokeWidth=self.strokeWidth)
|
||||
return s
|
||||
|
||||
def _Cross(self):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
s = float(self.size)
|
||||
h, s = s/2, s/6
|
||||
return self._doPolygon((-s,-h,-s,-s,-h,-s,-h,s,-s,s,-s,h,s,h,s,s,h,s,h,-s,s,-s,s,-h))
|
||||
|
||||
def _Triangle(self):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
r = float(self.size)/2
|
||||
c = 30*_toradians
|
||||
s = sin(30*_toradians)*r
|
||||
c = cos(c)*r
|
||||
return self._doPolygon((0,r,-c,-s,c,-s))
|
||||
|
||||
def _StarSix(self):
|
||||
r = float(self.size)/2
|
||||
c = 30*_toradians
|
||||
s = sin(c)*r
|
||||
c = cos(c)*r
|
||||
z = s/2
|
||||
g = c/2
|
||||
return self._doPolygon((0,r,-z,s,-c,s,-s,0,-c,-s,-z,-s,0,-r,z,-s,c,-s,s,0,c,s,z,s))
|
||||
|
||||
def _StarFive(self):
|
||||
R = float(self.size)/2
|
||||
r = R*sin(18*_toradians)/cos(36*_toradians)
|
||||
P = []
|
||||
angle = 90
|
||||
for i in range(5):
|
||||
for radius in R, r:
|
||||
theta = angle*_toradians
|
||||
P.append(radius*cos(theta))
|
||||
P.append(radius*sin(theta))
|
||||
angle = angle + 36
|
||||
return self._doPolygon(P)
|
||||
|
||||
def _Pentagon(self):
|
||||
return self._doNgon(5)
|
||||
|
||||
def _Hexagon(self):
|
||||
return self._doNgon(6)
|
||||
|
||||
def _Heptagon(self):
|
||||
return self._doNgon(7)
|
||||
|
||||
def _Octagon(self):
|
||||
return self._doNgon(8)
|
||||
|
||||
def _ArrowHead(self):
|
||||
s = self.size
|
||||
h = self.arrowHeight
|
||||
b = self.arrowBarbDx
|
||||
return self._doPolygon((0,0,b,-h,s,0,b,h))
|
||||
|
||||
def _doPolygon(self,P):
|
||||
x, y = self.x+self.dx, self.y+self.dy
|
||||
if x or y: P = list(map(lambda i,P=P,A=[x,y]: P[i] + A[i&1], list(range(len(P)))))
|
||||
return Polygon(P, strokeWidth =self.strokeWidth, strokeColor=self.strokeColor, fillColor=self.fillColor)
|
||||
|
||||
def _doFill(self):
|
||||
old = self.fillColor
|
||||
if old is None:
|
||||
self.fillColor = self.strokeColor
|
||||
r = (self.kind and getattr(self,'_'+self.kind[6:]) or Group)()
|
||||
self.fillColor = old
|
||||
return r
|
||||
|
||||
def _doNgon(self,n):
|
||||
P = []
|
||||
size = float(self.size)/2
|
||||
for i in range(n):
|
||||
r = (2.*i/n+0.5)*pi
|
||||
P.append(size*cos(r))
|
||||
P.append(size*sin(r))
|
||||
return self._doPolygon(P)
|
||||
|
||||
_FilledCircle = _doFill
|
||||
_FilledSquare = _doFill
|
||||
_FilledDiamond = _doFill
|
||||
_FilledCross = _doFill
|
||||
_FilledTriangle = _doFill
|
||||
_FilledStarSix = _doFill
|
||||
_FilledPentagon = _doFill
|
||||
_FilledHexagon = _doFill
|
||||
_FilledHeptagon = _doFill
|
||||
_FilledOctagon = _doFill
|
||||
_FilledStarFive = _doFill
|
||||
_FilledArrowHead = _doFill
|
||||
|
||||
def draw(self):
|
||||
if self.kind:
|
||||
m = getattr(self,'_'+self.kind)
|
||||
if self.angle:
|
||||
_x, _dx, _y, _dy = self.x, self.dx, self.y, self.dy
|
||||
self.x, self.dx, self.y, self.dy = 0,0,0,0
|
||||
try:
|
||||
m = m()
|
||||
finally:
|
||||
self.x, self.dx, self.y, self.dy = _x, _dx, _y, _dy
|
||||
if not isinstance(m,Group):
|
||||
_m, m = m, Group()
|
||||
m.add(_m)
|
||||
if self.angle: m.rotate(self.angle)
|
||||
x, y = _x+_dx, _y+_dy
|
||||
if x or y: m.shift(x,y)
|
||||
else:
|
||||
m = m()
|
||||
else:
|
||||
m = Group()
|
||||
return m
|
||||
|
||||
def uSymbol2Symbol(uSymbol,x,y,color):
|
||||
if isClass(uSymbol) and issubclass(uSymbol,Widget):
|
||||
size = 10.
|
||||
symbol = uSymbol()
|
||||
symbol.x = x - (size/2)
|
||||
symbol.y = y - (size/2)
|
||||
try:
|
||||
symbol.size = size
|
||||
symbol.color = color
|
||||
except:
|
||||
pass
|
||||
elif isinstance(uSymbol,Marker) or isinstance(uSymbol,_Symbol):
|
||||
symbol = uSymbol.clone()
|
||||
if isinstance(uSymbol,Marker): symbol.fillColor = symbol.fillColor or color
|
||||
symbol.x, symbol.y = x, y
|
||||
elif callable(uSymbol):
|
||||
symbol = uSymbol(x, y, 5, color)
|
||||
else:
|
||||
symbol = None
|
||||
return symbol
|
||||
|
||||
class _isSymbol(Validator):
|
||||
def test(self,x):
|
||||
return hasattr(x,'__call__') or isinstance(x,Marker) or isinstance(x,_Symbol) or (isClass(x) and issubclass(x,Widget))
|
||||
|
||||
isSymbol = _isSymbol()
|
||||
|
||||
def makeMarker(name,**kw):
|
||||
if Marker._attrMap['kind'].validate(name):
|
||||
m = Marker(**kw)
|
||||
m.kind = name
|
||||
elif name[-5:]=='_Flag' and Flag._attrMap['kind'].validate(name[:-5]):
|
||||
m = Flag(**kw)
|
||||
m.kind = name[:-5]
|
||||
m.size = 10
|
||||
else:
|
||||
raise ValueError("Invalid marker name %s" % name)
|
||||
return m
|
||||
|
||||
if __name__=='__main__':
|
||||
D = Drawing()
|
||||
D.add(Marker())
|
||||
D.save(fnRoot='Marker',formats=['pdf'], outDir='/tmp')
|
||||
@@ -0,0 +1,977 @@
|
||||
#Copyright ReportLab Europe Ltd. 2000-2017
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/signsandsymbols.py
|
||||
# signsandsymbols.py
|
||||
# A collection of new widgets
|
||||
# author: John Precedo (johnp@reportlab.com)
|
||||
|
||||
__version__='3.3.0'
|
||||
__doc__="""This file is a collection of widgets to produce some common signs and symbols.
|
||||
|
||||
Widgets include:
|
||||
|
||||
- ETriangle (an equilateral triangle),
|
||||
- RTriangle (a right angled triangle),
|
||||
- Octagon,
|
||||
- Crossbox,
|
||||
- Tickbox,
|
||||
- SmileyFace,
|
||||
- StopSign,
|
||||
- NoEntry,
|
||||
- NotAllowed (the red roundel from 'no smoking' signs),
|
||||
- NoSmoking,
|
||||
- DangerSign (a black exclamation point in a yellow triangle),
|
||||
- YesNo (returns a tickbox or a crossbox depending on a testvalue),
|
||||
- FloppyDisk,
|
||||
- ArrowOne, and
|
||||
- ArrowTwo
|
||||
- CrossHair
|
||||
"""
|
||||
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.validators import *
|
||||
from reportlab.lib.attrmap import *
|
||||
from reportlab.lib.utils import isStr, asUnicode
|
||||
from reportlab.graphics import shapes
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
from reportlab.graphics import renderPDF
|
||||
|
||||
|
||||
class _Symbol(Widget):
|
||||
"""Abstract base widget
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor', 'strokeColor'
|
||||
"""
|
||||
_nodoc = 1
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber,desc='symbol x coordinate'),
|
||||
y = AttrMapValue(isNumber,desc='symbol y coordinate'),
|
||||
dx = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
|
||||
dy = AttrMapValue(isNumber,desc='symbol x coordinate adjustment'),
|
||||
size = AttrMapValue(isNumber),
|
||||
fillColor = AttrMapValue(isColorOrNone),
|
||||
strokeColor = AttrMapValue(isColorOrNone),
|
||||
strokeWidth = AttrMapValue(isNumber),
|
||||
)
|
||||
def __init__(self):
|
||||
assert self.__class__.__name__!='_Symbol', 'Abstract class _Symbol instantiated'
|
||||
self.x = self.y = self.dx = self.dy = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.red
|
||||
self.strokeColor = None
|
||||
self.strokeWidth = 0.1
|
||||
|
||||
def demo(self):
|
||||
D = shapes.Drawing(200, 100)
|
||||
s = float(self.size)
|
||||
ob = self.__class__()
|
||||
ob.x=50
|
||||
ob.y=0
|
||||
ob.draw()
|
||||
D.add(ob)
|
||||
D.add(shapes.String(ob.x+(s/2),(ob.y-12),
|
||||
ob.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=10))
|
||||
return D
|
||||
|
||||
class ETriangle(_Symbol):
|
||||
"""This draws an equilateral triangle."""
|
||||
|
||||
def __init__(self):
|
||||
_Symbol.__init__(self)
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# Triangle specific bits
|
||||
ae = s*0.125 #(ae = 'an eighth')
|
||||
triangle = shapes.Polygon(points = [
|
||||
self.x, self.y,
|
||||
self.x+s, self.y,
|
||||
self.x+(s/2),self.y+s],
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=s/50.)
|
||||
g.add(triangle)
|
||||
return g
|
||||
|
||||
class RTriangle(_Symbol):
|
||||
"""This draws a right-angled triangle.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor', 'strokeColor'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.green
|
||||
self.strokeColor = None
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# Triangle specific bits
|
||||
ae = s*0.125 #(ae = 'an eighth')
|
||||
triangle = shapes.Polygon(points = [
|
||||
self.x, self.y,
|
||||
self.x+s, self.y,
|
||||
self.x,self.y+s],
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=s/50.)
|
||||
g.add(triangle)
|
||||
return g
|
||||
|
||||
class Octagon(_Symbol):
|
||||
"""This widget draws an Octagon.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor', 'strokeColor'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.yellow
|
||||
self.strokeColor = None
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# Octagon specific bits
|
||||
athird=s/3
|
||||
|
||||
octagon = shapes.Polygon(points=[self.x+athird, self.y,
|
||||
self.x, self.y+athird,
|
||||
self.x, self.y+(athird*2),
|
||||
self.x+athird, self.y+s,
|
||||
self.x+(athird*2), self.y+s,
|
||||
self.x+s, self.y+(athird*2),
|
||||
self.x+s, self.y+athird,
|
||||
self.x+(athird*2), self.y],
|
||||
strokeColor = self.strokeColor,
|
||||
fillColor = self.fillColor,
|
||||
strokeWidth=10)
|
||||
g.add(octagon)
|
||||
return g
|
||||
|
||||
class Crossbox(_Symbol):
|
||||
"""This draws a black box with a red cross in it - a 'checkbox'.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'crossColor', 'strokeColor', 'crosswidth'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
crossColor = AttrMapValue(isColorOrNone),
|
||||
crosswidth = AttrMapValue(isNumber),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.white
|
||||
self.crossColor = colors.red
|
||||
self.strokeColor = colors.black
|
||||
self.crosswidth = 10
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# crossbox specific bits
|
||||
box = shapes.Rect(self.x+1, self.y+1, s-2, s-2,
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=2)
|
||||
g.add(box)
|
||||
|
||||
crossLine1 = shapes.Line(self.x+(s*0.15), self.y+(s*0.15), self.x+(s*0.85), self.y+(s*0.85),
|
||||
fillColor = self.crossColor,
|
||||
strokeColor = self.crossColor,
|
||||
strokeWidth = self.crosswidth)
|
||||
g.add(crossLine1)
|
||||
|
||||
crossLine2 = shapes.Line(self.x+(s*0.15), self.y+(s*0.85), self.x+(s*0.85) ,self.y+(s*0.15),
|
||||
fillColor = self.crossColor,
|
||||
strokeColor = self.crossColor,
|
||||
strokeWidth = self.crosswidth)
|
||||
g.add(crossLine2)
|
||||
|
||||
return g
|
||||
|
||||
|
||||
class Tickbox(_Symbol):
|
||||
"""This draws a black box with a red tick in it - another 'checkbox'.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'tickColor', 'strokeColor', 'tickwidth'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
tickColor = AttrMapValue(isColorOrNone),
|
||||
tickwidth = AttrMapValue(isNumber),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.tickColor = colors.red
|
||||
self.strokeColor = colors.black
|
||||
self.fillColor = colors.white
|
||||
self.tickwidth = 10
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# tickbox specific bits
|
||||
box = shapes.Rect(self.x+1, self.y+1, s-2, s-2,
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=2)
|
||||
g.add(box)
|
||||
|
||||
tickLine = shapes.PolyLine(points = [self.x+(s*0.15), self.y+(s*0.35), self.x+(s*0.35), self.y+(s*0.15),
|
||||
self.x+(s*0.35), self.y+(s*0.15), self.x+(s*0.85) ,self.y+(s*0.85)],
|
||||
fillColor = self.tickColor,
|
||||
strokeColor = self.tickColor,
|
||||
strokeWidth = self.tickwidth)
|
||||
g.add(tickLine)
|
||||
|
||||
return g
|
||||
|
||||
class SmileyFace(_Symbol):
|
||||
"""This draws a classic smiley face.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
_Symbol.__init__(self)
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.yellow
|
||||
self.strokeColor = colors.black
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# SmileyFace specific bits
|
||||
g.add(shapes.Circle(cx=self.x+(s/2), cy=self.y+(s/2), r=s/2,
|
||||
fillColor=self.fillColor, strokeColor=self.strokeColor,
|
||||
strokeWidth=max(s/38.,self.strokeWidth)))
|
||||
|
||||
for i in (1,2):
|
||||
g.add(shapes.Ellipse(self.x+(s/3)*i,self.y+(s/3)*2, s/30, s/10,
|
||||
fillColor=self.strokeColor, strokeColor = self.strokeColor,
|
||||
strokeWidth=max(s/38.,self.strokeWidth)))
|
||||
|
||||
# calculate a pointslist for the mouth
|
||||
# THIS IS A HACK! - don't use if there is a 'shapes.Arc'
|
||||
centerx=self.x+(s/2)
|
||||
centery=self.y+(s/2)
|
||||
radius=s/3
|
||||
yradius = radius
|
||||
xradius = radius
|
||||
startangledegrees=200
|
||||
endangledegrees=340
|
||||
degreedelta = 1
|
||||
pointslist = []
|
||||
a = pointslist.append
|
||||
from math import sin, cos, pi
|
||||
degreestoradians = pi/180.0
|
||||
radiansdelta = degreedelta*degreestoradians
|
||||
startangle = startangledegrees*degreestoradians
|
||||
endangle = endangledegrees*degreestoradians
|
||||
while endangle<startangle:
|
||||
endangle = endangle+2*pi
|
||||
angle = startangle
|
||||
while angle<endangle:
|
||||
x = centerx + cos(angle)*radius
|
||||
y = centery + sin(angle)*yradius
|
||||
a(x); a(y)
|
||||
angle = angle+radiansdelta
|
||||
|
||||
# make the mouth
|
||||
smile = shapes.PolyLine(pointslist,
|
||||
fillColor = self.strokeColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth = max(s/38.,self.strokeWidth))
|
||||
g.add(smile)
|
||||
|
||||
return g
|
||||
|
||||
class StopSign(_Symbol):
|
||||
"""This draws a (British) stop sign.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size'
|
||||
|
||||
"""
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
stopColor = AttrMapValue(isColorOrNone,desc='color of the word stop'),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.strokeColor = colors.black
|
||||
self.fillColor = colors.orangered
|
||||
self.stopColor = colors.ghostwhite
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# stop-sign specific bits
|
||||
athird=s/3
|
||||
|
||||
outerOctagon = shapes.Polygon(points=[self.x+athird, self.y,
|
||||
self.x, self.y+athird,
|
||||
self.x, self.y+(athird*2),
|
||||
self.x+athird, self.y+s,
|
||||
self.x+(athird*2), self.y+s,
|
||||
self.x+s, self.y+(athird*2),
|
||||
self.x+s, self.y+athird,
|
||||
self.x+(athird*2), self.y],
|
||||
strokeColor = self.strokeColor,
|
||||
fillColor = None,
|
||||
strokeWidth=1)
|
||||
g.add(outerOctagon)
|
||||
|
||||
innerOctagon = shapes.Polygon(points=[self.x+athird+(s/75), self.y+(s/75),
|
||||
self.x+(s/75), self.y+athird+(s/75),
|
||||
self.x+(s/75), self.y+(athird*2)-(s/75),
|
||||
self.x+athird+(s/75), self.y+s-(s/75),
|
||||
self.x+(athird*2)-(s/75), (self.y+s)-(s/75),
|
||||
(self.x+s)-(s/75), self.y+(athird*2)-(s/75),
|
||||
(self.x+s)-(s/75), self.y+athird+(s/75),
|
||||
self.x+(athird*2)-(s/75), self.y+(s/75)],
|
||||
strokeColor = None,
|
||||
fillColor = self.fillColor,
|
||||
strokeWidth=0)
|
||||
g.add(innerOctagon)
|
||||
|
||||
if self.stopColor:
|
||||
g.add(shapes.String(self.x+(s*0.5),self.y+(s*0.4),
|
||||
'STOP', fillColor=self.stopColor, textAnchor='middle',
|
||||
fontSize=s/3, fontName="Helvetica-Bold"))
|
||||
|
||||
return g
|
||||
|
||||
|
||||
class NoEntry(_Symbol):
|
||||
"""This draws a (British) No Entry sign - a red circle with a white line on it.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
innerBarColor = AttrMapValue(isColorOrNone,desc='color of the inner bar'),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.strokeColor = colors.black
|
||||
self.fillColor = colors.orangered
|
||||
self.innerBarColor = colors.ghostwhite
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# no-entry-sign specific bits
|
||||
if self.strokeColor:
|
||||
g.add(shapes.Circle(cx = (self.x+(s/2)), cy = (self.y+(s/2)), r = s/2, fillColor = None, strokeColor = self.strokeColor, strokeWidth=1))
|
||||
|
||||
if self.fillColor:
|
||||
g.add(shapes.Circle(cx = (self.x+(s/2)), cy =(self.y+(s/2)), r = ((s/2)-(s/50)), fillColor = self.fillColor, strokeColor = None, strokeWidth=0))
|
||||
|
||||
innerBarColor = self.innerBarColor
|
||||
if innerBarColor:
|
||||
g.add(shapes.Rect(self.x+(s*0.1), self.y+(s*0.4), width=s*0.8, height=s*0.2, fillColor = innerBarColor, strokeColor = innerBarColor, strokeLineCap = 1, strokeWidth = 0))
|
||||
return g
|
||||
|
||||
class NotAllowed(_Symbol):
|
||||
"""This draws a 'forbidden' roundel (as used in the no-smoking sign).
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.strokeColor = colors.red
|
||||
self.fillColor = colors.white
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
strokeColor = self.strokeColor
|
||||
|
||||
# not=allowed specific bits
|
||||
outerCircle = shapes.Circle(cx = (self.x+(s/2)), cy = (self.y+(s/2)), r = (s/2)-(s/10), fillColor = self.fillColor, strokeColor = strokeColor, strokeWidth=s/10.)
|
||||
g.add(outerCircle)
|
||||
|
||||
centerx=self.x+s
|
||||
centery=self.y+(s/2)-(s/6)
|
||||
radius=s-(s/6)
|
||||
yradius = radius/2
|
||||
xradius = radius/2
|
||||
startangledegrees=100
|
||||
endangledegrees=-80
|
||||
degreedelta = 90
|
||||
pointslist = []
|
||||
a = pointslist.append
|
||||
from math import sin, cos, pi
|
||||
degreestoradians = pi/180.0
|
||||
radiansdelta = degreedelta*degreestoradians
|
||||
startangle = startangledegrees*degreestoradians
|
||||
endangle = endangledegrees*degreestoradians
|
||||
while endangle<startangle:
|
||||
endangle = endangle+2*pi
|
||||
angle = startangle
|
||||
while angle<endangle:
|
||||
x = centerx + cos(angle)*radius
|
||||
y = centery + sin(angle)*yradius
|
||||
a(x); a(y)
|
||||
angle = angle+radiansdelta
|
||||
crossbar = shapes.PolyLine(pointslist, fillColor = strokeColor, strokeColor = strokeColor, strokeWidth = s/10.)
|
||||
g.add(crossbar)
|
||||
return g
|
||||
|
||||
|
||||
class NoSmoking(NotAllowed):
|
||||
"""This draws a no-smoking sign.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
NotAllowed.__init__(self)
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = NotAllowed.draw(self)
|
||||
|
||||
# no-smoking-sign specific bits
|
||||
newx = self.x+(s/2)-(s/3.5)
|
||||
newy = self.y+(s/2)-(s/32)
|
||||
cigarrette1 = shapes.Rect(x = newx, y = newy, width = (s/2), height =(s/16),
|
||||
fillColor = colors.ghostwhite, strokeColor = colors.gray, strokeWidth=0)
|
||||
newx=newx+(s/2)+(s/64)
|
||||
g.insert(-1,cigarrette1)
|
||||
|
||||
cigarrette2 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
|
||||
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
|
||||
newx= newx+(s/35)
|
||||
g.insert(-1,cigarrette2)
|
||||
|
||||
cigarrette3 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
|
||||
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
|
||||
newx= newx+(s/35)
|
||||
g.insert(-1,cigarrette3)
|
||||
|
||||
cigarrette4 = shapes.Rect(x = newx, y = newy, width = (s/80), height =(s/16),
|
||||
fillColor = colors.orangered, strokeColor = None, strokeWidth=0)
|
||||
newx= newx+(s/35)
|
||||
g.insert(-1,cigarrette4)
|
||||
|
||||
return g
|
||||
|
||||
|
||||
class DangerSign(_Symbol):
|
||||
"""This draws a 'danger' sign: a yellow box with a black exclamation point.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'strokeColor', 'fillColor', 'strokeWidth'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.strokeColor = colors.black
|
||||
self.fillColor = colors.gold
|
||||
self.strokeWidth = self.size*0.125
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
ew = self.strokeWidth
|
||||
ae = s*0.125 #(ae = 'an eighth')
|
||||
|
||||
|
||||
# danger sign specific bits
|
||||
|
||||
ew = self.strokeWidth
|
||||
ae = s*0.125 #(ae = 'an eighth')
|
||||
|
||||
outerTriangle = shapes.Polygon(points = [
|
||||
self.x, self.y,
|
||||
self.x+s, self.y,
|
||||
self.x+(s/2),self.y+s],
|
||||
fillColor = None,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth=0)
|
||||
g.add(outerTriangle)
|
||||
|
||||
innerTriangle = shapes.Polygon(points = [
|
||||
self.x+(s/50), self.y+(s/75),
|
||||
(self.x+s)-(s/50), self.y+(s/75),
|
||||
self.x+(s/2),(self.y+s)-(s/50)],
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(innerTriangle)
|
||||
|
||||
exmark = shapes.Polygon(points=[
|
||||
((self.x+s/2)-ew/2), self.y+ae*2.5,
|
||||
((self.x+s/2)+ew/2), self.y+ae*2.5,
|
||||
((self.x+s/2)+((ew/2))+(ew/6)), self.y+ae*5.5,
|
||||
((self.x+s/2)-((ew/2))-(ew/6)), self.y+ae*5.5],
|
||||
fillColor = self.strokeColor,
|
||||
strokeColor = None)
|
||||
g.add(exmark)
|
||||
|
||||
exdot = shapes.Polygon(points=[
|
||||
((self.x+s/2)-ew/2), self.y+ae,
|
||||
((self.x+s/2)+ew/2), self.y+ae,
|
||||
((self.x+s/2)+ew/2), self.y+ae*2,
|
||||
((self.x+s/2)-ew/2), self.y+ae*2],
|
||||
fillColor = self.strokeColor,
|
||||
strokeColor = None)
|
||||
g.add(exdot)
|
||||
|
||||
return g
|
||||
|
||||
|
||||
class YesNo(_Symbol):
|
||||
"""This widget draw a tickbox or crossbox depending on 'testValue'.
|
||||
|
||||
If this widget is supplied with a 'True' or 1 as a value for
|
||||
testValue, it will use the tickbox widget. Otherwise, it will
|
||||
produce a crossbox.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'tickcolor', 'crosscolor', 'testValue'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
tickcolor = AttrMapValue(isColor),
|
||||
crosscolor = AttrMapValue(isColor),
|
||||
testValue = AttrMapValue(isBoolean),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.tickcolor = colors.green
|
||||
self.crosscolor = colors.red
|
||||
self.testValue = 1
|
||||
|
||||
def draw(self):
|
||||
if self.testValue:
|
||||
yn=Tickbox()
|
||||
yn.tickColor=self.tickcolor
|
||||
else:
|
||||
yn=Crossbox()
|
||||
yn.crossColor=self.crosscolor
|
||||
yn.x=self.x
|
||||
yn.y=self.y
|
||||
yn.size=self.size
|
||||
yn.draw()
|
||||
return yn
|
||||
|
||||
|
||||
def demo(self):
|
||||
D = shapes.Drawing(200, 100)
|
||||
yn = YesNo()
|
||||
yn.x = 15
|
||||
yn.y = 25
|
||||
yn.size = 70
|
||||
yn.testValue = 0
|
||||
yn.draw()
|
||||
D.add(yn)
|
||||
yn2 = YesNo()
|
||||
yn2.x = 120
|
||||
yn2.y = 25
|
||||
yn2.size = 70
|
||||
yn2.testValue = 1
|
||||
yn2.draw()
|
||||
D.add(yn2)
|
||||
labelFontSize = 8
|
||||
D.add(shapes.String(yn.x+(yn.size/2),(yn.y-(1.2*labelFontSize)),
|
||||
'testValue=0', fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
D.add(shapes.String(yn2.x+(yn2.size/2),(yn2.y-(1.2*labelFontSize)),
|
||||
'testValue=1', fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
labelFontSize = 10
|
||||
D.add(shapes.String(yn.x+85,(yn.y-20),
|
||||
self.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
return D
|
||||
|
||||
class FloppyDisk(_Symbol):
|
||||
"""This widget draws an icon of a floppy disk.
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'diskcolor'
|
||||
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
diskColor = AttrMapValue(isColor),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.diskColor = colors.black
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
|
||||
# floppy disk specific bits
|
||||
diskBody = shapes.Rect(x=self.x, y=self.y+(s/100), width=s, height=s-(s/100),
|
||||
fillColor = self.diskColor,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(diskBody)
|
||||
|
||||
label = shapes.Rect(x=self.x+(s*0.1), y=(self.y+s)-(s*0.5), width=s*0.8, height=s*0.48,
|
||||
fillColor = colors.whitesmoke,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(label)
|
||||
|
||||
labelsplash = shapes.Rect(x=self.x+(s*0.1), y=(self.y+s)-(s*0.1), width=s*0.8, height=s*0.08,
|
||||
fillColor = colors.royalblue,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(labelsplash)
|
||||
|
||||
|
||||
line1 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.6*s), x2=self.x+(s*0.85), y2=self.y+(0.6*s),
|
||||
fillColor = colors.black,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(line1)
|
||||
|
||||
line2 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.7*s), x2=self.x+(s*0.85), y2=self.y+(0.7*s),
|
||||
fillColor = colors.black,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(line2)
|
||||
|
||||
line3 = shapes.Line(x1=self.x+(s*0.15), y1=self.y+(0.8*s), x2=self.x+(s*0.85), y2=self.y+(0.8*s),
|
||||
fillColor = colors.black,
|
||||
strokeColor = colors.black,
|
||||
strokeWidth=0)
|
||||
g.add(line3)
|
||||
|
||||
metalcover = shapes.Rect(x=self.x+(s*0.2), y=(self.y), width=s*0.5, height=s*0.35,
|
||||
fillColor = colors.silver,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(metalcover)
|
||||
|
||||
coverslot = shapes.Rect(x=self.x+(s*0.28), y=(self.y)+(s*0.035), width=s*0.12, height=s*0.28,
|
||||
fillColor = self.diskColor,
|
||||
strokeColor = None,
|
||||
strokeWidth=0)
|
||||
g.add(coverslot)
|
||||
|
||||
return g
|
||||
|
||||
class ArrowOne(_Symbol):
|
||||
"""This widget draws an arrow (style one).
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor'
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.red
|
||||
self.strokeWidth = 0
|
||||
self.strokeColor = None
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
x = self.x
|
||||
y = self.y
|
||||
s2 = s/2
|
||||
s3 = s/3
|
||||
s5 = s/5
|
||||
g.add(shapes.Polygon(points = [
|
||||
x,y+s3,
|
||||
x,y+2*s3,
|
||||
x+s2,y+2*s3,
|
||||
x+s2,y+4*s5,
|
||||
x+s,y+s2,
|
||||
x+s2,y+s5,
|
||||
x+s2,y+s3,
|
||||
],
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth = self.strokeWidth,
|
||||
)
|
||||
)
|
||||
return g
|
||||
|
||||
class ArrowTwo(ArrowOne):
|
||||
"""This widget draws an arrow (style two).
|
||||
|
||||
possible attributes:
|
||||
'x', 'y', 'size', 'fillColor'
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
self.y = 0
|
||||
self.size = 100
|
||||
self.fillColor = colors.blue
|
||||
self.strokeWidth = 0
|
||||
self.strokeColor = None
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
|
||||
# arrow specific bits
|
||||
x = self.x
|
||||
y = self.y
|
||||
s2 = s/2
|
||||
s3 = s/3
|
||||
s5 = s/5
|
||||
s24 = s/24
|
||||
|
||||
g.add(shapes.Polygon(
|
||||
points = [
|
||||
x,y+11*s24,
|
||||
x,y+13*s24,
|
||||
x+18.75*s24, y+13*s24,
|
||||
x+2*s3, y+2*s3,
|
||||
x+s, y+s2,
|
||||
x+2*s3, y+s3,
|
||||
x+18.75*s24, y+11*s24,
|
||||
],
|
||||
fillColor = self.fillColor,
|
||||
strokeColor = self.strokeColor,
|
||||
strokeWidth = self.strokeWidth)
|
||||
)
|
||||
|
||||
return g
|
||||
|
||||
class CrossHair(_Symbol):
|
||||
"""This draws an equilateral triangle."""
|
||||
_attrMap = AttrMap(BASE=_Symbol,
|
||||
innerGap = AttrMapValue(EitherOr((isString,isNumberOrNone)),desc=' gap at centre as "x%" or points or None'),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.x = self.y = self.dx = self.dy = 0
|
||||
self.size = 10
|
||||
self.fillColor = None
|
||||
self.strokeColor = colors.black
|
||||
self.strokeWidth = 0.5
|
||||
self.innerGap = '20%'
|
||||
|
||||
def draw(self):
|
||||
# general widget bits
|
||||
s = float(self.size) # abbreviate as we will use this a lot
|
||||
g = shapes.Group()
|
||||
ig = self.innerGap
|
||||
|
||||
x = self.x+self.dx
|
||||
y = self.y+self.dy
|
||||
hsize = 0.5*self.size
|
||||
if not ig:
|
||||
L = [(x-hsize,y,x+hsize,y), (x,y-hsize,x,y+hsize)]
|
||||
else:
|
||||
if isStr(ig):
|
||||
ig = asUnicode(ig)
|
||||
if ig.endswith(u'%'):
|
||||
gs = hsize*float(ig[:-1])/100.0
|
||||
else:
|
||||
gs = float(ig)*0.5
|
||||
else:
|
||||
gs = ig*0.5
|
||||
L = [(x-hsize,y,x-gs,y), (x+gs,y,x+hsize,y), (x,y-hsize,x,y-gs), (x,y+gs,x,y+hsize)]
|
||||
P = shapes.Path(strokeWidth=self.strokeWidth,strokeColor=self.strokeColor)
|
||||
for x0,y0,x1,y1 in L:
|
||||
P.moveTo(x0,y0)
|
||||
P.lineTo(x1,y1)
|
||||
g.add(P)
|
||||
return g
|
||||
|
||||
|
||||
def test():
|
||||
"""This function produces a pdf with examples of all the signs and symbols from this file.
|
||||
"""
|
||||
labelFontSize = 10
|
||||
D = shapes.Drawing(450,650)
|
||||
cb = Crossbox()
|
||||
cb.x = 20
|
||||
cb.y = 530
|
||||
D.add(cb)
|
||||
D.add(shapes.String(cb.x+(cb.size/2),(cb.y-(1.2*labelFontSize)),
|
||||
cb.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
tb = Tickbox()
|
||||
tb.x = 170
|
||||
tb.y = 530
|
||||
D.add(tb)
|
||||
D.add(shapes.String(tb.x+(tb.size/2),(tb.y-(1.2*labelFontSize)),
|
||||
tb.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
|
||||
yn = YesNo()
|
||||
yn.x = 320
|
||||
yn.y = 530
|
||||
D.add(yn)
|
||||
tempstring = yn.__class__.__name__ + '*'
|
||||
D.add(shapes.String(yn.x+(tb.size/2),(yn.y-(1.2*labelFontSize)),
|
||||
tempstring, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
D.add(shapes.String(130,6,
|
||||
"(The 'YesNo' widget returns a tickbox if testvalue=1, and a crossbox if testvalue=0)", fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize*0.75))
|
||||
|
||||
|
||||
ss = StopSign()
|
||||
ss.x = 20
|
||||
ss.y = 400
|
||||
D.add(ss)
|
||||
D.add(shapes.String(ss.x+(ss.size/2), ss.y-(1.2*labelFontSize),
|
||||
ss.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
ne = NoEntry()
|
||||
ne.x = 170
|
||||
ne.y = 400
|
||||
D.add(ne)
|
||||
D.add(shapes.String(ne.x+(ne.size/2),(ne.y-(1.2*labelFontSize)),
|
||||
ne.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
sf = SmileyFace()
|
||||
sf.x = 320
|
||||
sf.y = 400
|
||||
D.add(sf)
|
||||
D.add(shapes.String(sf.x+(sf.size/2),(sf.y-(1.2*labelFontSize)),
|
||||
sf.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
ds = DangerSign()
|
||||
ds.x = 20
|
||||
ds.y = 270
|
||||
D.add(ds)
|
||||
D.add(shapes.String(ds.x+(ds.size/2),(ds.y-(1.2*labelFontSize)),
|
||||
ds.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
na = NotAllowed()
|
||||
na.x = 170
|
||||
na.y = 270
|
||||
D.add(na)
|
||||
D.add(shapes.String(na.x+(na.size/2),(na.y-(1.2*labelFontSize)),
|
||||
na.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
ns = NoSmoking()
|
||||
ns.x = 320
|
||||
ns.y = 270
|
||||
D.add(ns)
|
||||
D.add(shapes.String(ns.x+(ns.size/2),(ns.y-(1.2*labelFontSize)),
|
||||
ns.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
a1 = ArrowOne()
|
||||
a1.x = 20
|
||||
a1.y = 140
|
||||
D.add(a1)
|
||||
D.add(shapes.String(a1.x+(a1.size/2),(a1.y-(1.2*labelFontSize)),
|
||||
a1.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
a2 = ArrowTwo()
|
||||
a2.x = 170
|
||||
a2.y = 140
|
||||
D.add(a2)
|
||||
D.add(shapes.String(a2.x+(a2.size/2),(a2.y-(1.2*labelFontSize)),
|
||||
a2.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
fd = FloppyDisk()
|
||||
fd.x = 320
|
||||
fd.y = 140
|
||||
D.add(fd)
|
||||
D.add(shapes.String(fd.x+(fd.size/2),(fd.y-(1.2*labelFontSize)),
|
||||
fd.__class__.__name__, fillColor=colors.black, textAnchor='middle',
|
||||
fontSize=labelFontSize))
|
||||
|
||||
renderPDF.drawToFile(D, 'signsandsymbols.pdf', 'signsandsymbols.py')
|
||||
print('wrote file: signsandsymbols.pdf')
|
||||
|
||||
if __name__=='__main__':
|
||||
test()
|
||||
@@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python
|
||||
#Copyright ReportLab Europe Ltd. 2000-2017
|
||||
#see license.txt for license details
|
||||
#history https://hg.reportlab.com/hg-public/reportlab/log/tip/src/reportlab/graphics/widgets/table.py
|
||||
__version__='3.3.0'
|
||||
|
||||
from reportlab.graphics.widgetbase import Widget
|
||||
from reportlab.graphics import shapes
|
||||
from reportlab.lib import colors
|
||||
from reportlab.lib.validators import *
|
||||
from reportlab.lib.attrmap import *
|
||||
|
||||
from reportlab.graphics.shapes import Drawing
|
||||
|
||||
class TableWidget(Widget):
|
||||
"""A two dimensions table of labels
|
||||
"""
|
||||
|
||||
_attrMap = AttrMap(
|
||||
x = AttrMapValue(isNumber, desc="x position of left edge of table"),
|
||||
y = AttrMapValue(isNumber, desc="y position of bottom edge of table"),
|
||||
width = AttrMapValue(isNumber, desc="table width"),
|
||||
height = AttrMapValue(isNumber, desc="table height"),
|
||||
borderStrokeColor = AttrMapValue(isColorOrNone, desc="table border color"),
|
||||
fillColor = AttrMapValue(isColorOrNone, desc="table fill color"),
|
||||
borderStrokeWidth = AttrMapValue(isNumber, desc="border line width"),
|
||||
horizontalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner horizontal lines color"),
|
||||
verticalDividerStrokeColor = AttrMapValue(isColorOrNone, desc="table inner vertical lines color"),
|
||||
horizontalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner horizontal lines width"),
|
||||
verticalDividerStrokeWidth = AttrMapValue(isNumber, desc="table inner vertical lines width"),
|
||||
dividerDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array for dividerLines.'),
|
||||
data = AttrMapValue(None, desc="a list of list of strings to be displayed in the cells"),
|
||||
boxAnchor = AttrMapValue(isBoxAnchor, desc="location of the table anchoring point"),
|
||||
fontName = AttrMapValue(isString, desc="text font in the table"),
|
||||
fontSize = AttrMapValue(isNumber, desc="font size of the table"),
|
||||
fontColor = AttrMapValue(isColorOrNone, desc="font color"),
|
||||
alignment = AttrMapValue(OneOf("left", "right"), desc="Alignment of text within cells"),
|
||||
textAnchor = AttrMapValue(OneOf('start','middle','end','numeric'), desc="Alignment of text within cells"),
|
||||
)
|
||||
|
||||
def __init__(self, x=10, y=10, **kw):
|
||||
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = 200
|
||||
self.height = 100
|
||||
self.borderStrokeColor = colors.black
|
||||
self.fillColor = None
|
||||
self.borderStrokeWidth = 0.5
|
||||
self.horizontalDividerStrokeColor = colors.black
|
||||
self.verticalDividerStrokeColor = colors.black
|
||||
self.horizontalDividerStrokeWidth = 0.5
|
||||
self.verticalDividerStrokeWidth = 0.25
|
||||
self.dividerDashArray = None
|
||||
self.data = [['North','South','East','West'],[100,110,120,130],['A','B','C','D']] # list of rows each row is a list of columns
|
||||
self.boxAnchor = 'nw'
|
||||
#self.fontName = None
|
||||
self.fontSize = 8
|
||||
self.fontColor = colors.black
|
||||
self.alignment = 'right'
|
||||
self.textAnchor = 'start'
|
||||
|
||||
|
||||
for k, v in kw.items():
|
||||
if k in list(self.__class__._attrMap.keys()):
|
||||
setattr(self, k, v)
|
||||
else:
|
||||
raise ValueError('invalid argument supplied for class %s'%self.__class__)
|
||||
|
||||
def demo(self):
|
||||
""" returns a sample of this widget with data
|
||||
"""
|
||||
d = Drawing(400, 200)
|
||||
t = TableWidget()
|
||||
d.add(t, name='table')
|
||||
d.table.dividerDashArray = (1, 3, 2)
|
||||
d.table.verticalDividerStrokeColor = None
|
||||
d.table.borderStrokeWidth = 0
|
||||
d.table.borderStrokeColor = colors.red
|
||||
return d
|
||||
|
||||
def draw(self):
|
||||
""" returns a group of shapes
|
||||
"""
|
||||
g = shapes.Group()
|
||||
|
||||
#overall border and fill
|
||||
if self.borderStrokeColor or self.fillColor: # adds border and filling color
|
||||
rect = shapes.Rect(self.x, self.y, self.width, self.height)
|
||||
rect.fillColor = self.fillColor
|
||||
rect.strokeColor = self.borderStrokeColor
|
||||
rect.strokeWidth = self.borderStrokeWidth
|
||||
g.add(rect)
|
||||
|
||||
#special case - for an empty table we want to avoid divide-by-zero
|
||||
data = self.preProcessData(self.data)
|
||||
rows = len(self.data)
|
||||
cols = len(self.data[0])
|
||||
#print "(rows,cols)=(%s, %s)"%(rows,cols)
|
||||
row_step = self.height / float(rows)
|
||||
col_step = self.width / float(cols)
|
||||
#print "(row_step,col_step)=(%s, %s)"%(row_step,col_step)
|
||||
# draw the grid
|
||||
if self.horizontalDividerStrokeColor:
|
||||
for i in range(rows): # make horizontal lines
|
||||
x1 = self.x
|
||||
x2 = self.x + self.width
|
||||
y = self.y + row_step*i
|
||||
#print 'line (%s, %s), (%s, %s)'%(x1, y, x2, y)
|
||||
line = shapes.Line(x1, y, x2, y)
|
||||
line.strokeDashArray = self.dividerDashArray
|
||||
line.strokeWidth = self.horizontalDividerStrokeWidth
|
||||
line.strokeColor = self.horizontalDividerStrokeColor
|
||||
g.add(line)
|
||||
if self.verticalDividerStrokeColor:
|
||||
for i in range(cols): # make vertical lines
|
||||
x = self.x+col_step*i
|
||||
y1 = self.y
|
||||
y2 = self.y + self.height
|
||||
#print 'line (%s, %s), (%s, %s)'%(x, y1, x, y2)
|
||||
line = shapes.Line(x, y1, x, y2)
|
||||
line.strokeDashArray = self.dividerDashArray
|
||||
line.strokeWidth = self.verticalDividerStrokeWidth
|
||||
line.strokeColor = self.verticalDividerStrokeColor
|
||||
g.add(line)
|
||||
|
||||
# since we plot data from down up, we reverse the list
|
||||
self.data.reverse()
|
||||
for (j, row) in enumerate(self.data):
|
||||
y = self.y + j*row_step + 0.5*row_step - 0.5 * self.fontSize
|
||||
for (i, datum) in enumerate(row):
|
||||
if datum:
|
||||
x = self.x + i*col_step + 0.5*col_step
|
||||
s = shapes.String(x, y, str(datum), textAnchor=self.textAnchor)
|
||||
s.fontName = self.fontName
|
||||
s.fontSize = self.fontSize
|
||||
s.fillColor = self.fontColor
|
||||
g.add(s)
|
||||
return g
|
||||
|
||||
def preProcessData(self, data):
|
||||
"""preprocess and return a new array with at least one row
|
||||
and column (use a None) if needed, and all rows the same
|
||||
length (adding Nones if needed)
|
||||
|
||||
"""
|
||||
if not data:
|
||||
return [[None]]
|
||||
#make all rows have similar number of cells, append None when needed
|
||||
max_row = max( [len(x) for x in data] )
|
||||
for rowNo, row in enumerate(data):
|
||||
if len(row) < max_row:
|
||||
row.extend([None]*(max_row-len(row)))
|
||||
return data
|
||||
|
||||
#test
|
||||
if __name__ == '__main__':
|
||||
d = TableWidget().demo()
|
||||
import os
|
||||
d.save(formats=['pdf'],outDir=os.getcwd(),fnRoot=None)
|
||||
Reference in New Issue
Block a user