1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560 |
- #-----------------------------------------------------------------------------
- # Name: wx.lib.plot.py
- # Purpose: Line, Bar and Scatter Graphs
- #
- # Author: Gordon Williams
- #
- # Created: 2003/11/03
- # RCS-ID: $Id$
- # Copyright: (c) 2002
- # Licence: Use as you wish.
- #-----------------------------------------------------------------------------
- # Included in GRASS GIS instead of importing it from wxPython
- # to overcome bug present in wxPython 3.0.2.
- # See #2558 and #3112
- #
- # 12/15/2003 - Jeff Grimmett (grimmtooth@softhome.net)
- #
- # o 2.5 compatability update.
- # o Renamed to plot.py in the wx.lib directory.
- # o Reworked test frame to work with wx demo framework. This saves a bit
- # of tedious cut and paste, and the test app is excellent.
- #
- # 12/18/2003 - Jeff Grimmett (grimmtooth@softhome.net)
- #
- # o wxScrolledMessageDialog -> ScrolledMessageDialog
- #
- # Oct 6, 2004 Gordon Williams (g_will@cyberus.ca)
- # - Added bar graph demo
- # - Modified line end shape from round to square.
- # - Removed FloatDCWrapper for conversion to ints and ints in arguments
- #
- # Oct 15, 2004 Gordon Williams (g_will@cyberus.ca)
- # - Imported modules given leading underscore to name.
- # - Added Cursor Line Tracking and User Point Labels.
- # - Demo for Cursor Line Tracking and Point Labels.
- # - Size of plot preview frame adjusted to show page better.
- # - Added helper functions PositionUserToScreen and PositionScreenToUser in PlotCanvas.
- # - Added functions GetClosestPoints (all curves) and GetClosestPoint (only closest curve)
- # can be in either user coords or screen coords.
- #
- # Jun 22, 2009 Florian Hoech (florian.hoech@gmx.de)
- # - Fixed exception when drawing empty plots on Mac OS X
- # - Fixed exception when trying to draw point labels on Mac OS X (Mac OS X
- # point label drawing code is still slow and only supports wx.COPY)
- # - Moved label positions away from axis lines a bit
- # - Added PolySpline class and modified demo 1 and 2 to use it
- # - Added center and diagonal lines option (Set/GetEnableCenterLines,
- # Set/GetEnableDiagonals)
- # - Added anti-aliasing option with optional high-resolution mode
- # (Set/GetEnableAntiAliasing, Set/GetEnableHiRes) and demo
- # - Added option to specify exact number of tick marks to use for each axis
- # (SetXSpec(<number>, SetYSpec(<number>) -- work like 'min', but with
- # <number> tick marks)
- # - Added support for background and foreground colours (enabled via
- # SetBackgroundColour/SetForegroundColour on a PlotCanvas instance)
- # - Changed PlotCanvas printing initialization from occuring in __init__ to
- # occur on access. This will postpone any IPP and / or CUPS warnings
- # which appear on stderr on some Linux systems until printing functionality
- # is actually used.
- #
- #
- """
- This is a simple light weight plotting module that can be used with
- Boa or easily integrated into your own wxPython application. The
- emphasis is on small size and fast plotting for large data sets. It
- has a reasonable number of features to do line and scatter graphs
- easily as well as simple bar graphs. It is not as sophisticated or
- as powerful as SciPy Plt or Chaco. Both of these are great packages
- but consume huge amounts of computer resources for simple plots.
- They can be found at http://scipy.com
- This file contains two parts; first the re-usable library stuff, then,
- after a "if __name__=='__main__'" test, a simple frame and a few default
- plots for examples and testing.
- Based on wxPlotCanvas
- Written by K.Hinsen, R. Srinivasan;
- Ported to wxPython Harm van der Heijden, feb 1999
- Major Additions Gordon Williams Feb. 2003 (g_will@cyberus.ca)
- -More style options
- -Zooming using mouse "rubber band"
- -Scroll left, right
- -Grid(graticule)
- -Printing, preview, and page set up (margins)
- -Axis and title labels
- -Cursor xy axis values
- -Doc strings and lots of comments
- -Optimizations for large number of points
- -Legends
- Did a lot of work here to speed markers up. Only a factor of 4
- improvement though. Lines are much faster than markers, especially
- filled markers. Stay away from circles and triangles unless you
- only have a few thousand points.
- Times for 25,000 points
- Line - 0.078 sec
- Markers
- Square - 0.22 sec
- dot - 0.10
- circle - 0.87
- cross,plus - 0.28
- triangle, triangle_down - 0.90
- Thanks to Chris Barker for getting this version working on Linux.
- Zooming controls with mouse (when enabled):
- Left mouse drag - Zoom box.
- Left mouse double click - reset zoom.
- Right mouse click - zoom out centred on click location.
- """
- import string as _string
- import time as _time
- import sys
- import wx
- # Needs NumPy
- import numpy as np
- from core.globalvar import CheckWxVersion
- if CheckWxVersion([3, 0, 0]):
- from wx import PENSTYLE_SOLID
- from wx import PENSTYLE_DOT_DASH
- from wx import PENSTYLE_DOT
- from wx import BRUSHSTYLE_SOLID
- from wx import BRUSHSTYLE_TRANSPARENT
- else:
- from wx import SOLID as PENSTYLE_SOLID
- from wx import SOLID as BRUSHSTYLE_SOLID
- from wx import DOT_DASH as PENSTYLE_DOT_DASH
- from wx import TRANSPARENT as BRUSHSTYLE_TRANSPARENT
- #
- # Plotting classes...
- #
- class PolyPoints:
- """Base Class for lines and markers
- - All methods are private.
- """
- def __init__(self, points, attr):
- self._points = np.array(points).astype(np.float64)
- self._logscale = (False, False)
- self._pointSize = (1.0, 1.0)
- self.currentScale = (1, 1)
- self.currentShift = (0, 0)
- self.scaled = self.points
- self.attributes = {}
- self.attributes.update(self._attributes)
- for name, value in attr.items():
- if name not in self._attributes.keys():
- raise KeyError(
- "Style attribute incorrect. Should be one of %s" % self._attributes.keys())
- self.attributes[name] = value
- def setLogScale(self, logscale):
- self._logscale = logscale
- def __getattr__(self, name):
- if name == 'points':
- if len(self._points) > 0:
- data = np.array(self._points, copy=True)
- if self._logscale[0]:
- data = self.log10(data, 0)
- if self._logscale[1]:
- data = self.log10(data, 1)
- return data
- else:
- return self._points
- else:
- raise AttributeError(name)
- def log10(self, data, ind):
- data = np.compress(data[:, ind] > 0, data, 0)
- data[:, ind] = np.log10(data[:, ind])
- return data
- def boundingBox(self):
- if len(self.points) == 0:
- # no curves to draw
- # defaults to (-1,-1) and (1,1) but axis can be set in Draw
- minXY = np.array([-1.0, -1.0])
- maxXY = np.array([1.0, 1.0])
- else:
- minXY = np.minimum.reduce(self.points)
- maxXY = np.maximum.reduce(self.points)
- return minXY, maxXY
- def scaleAndShift(self, scale=(1, 1), shift=(0, 0)):
- if len(self.points) == 0:
- # no curves to draw
- return
- if (scale is not self.currentScale) or (shift is not self.currentShift):
- # update point scaling
- self.scaled = scale * self.points + shift
- self.currentScale = scale
- self.currentShift = shift
- # else unchanged use the current scaling
- def getLegend(self):
- return self.attributes['legend']
- def getClosestPoint(self, pntXY, pointScaled=True):
- """Returns the index of closest point on the curve, pointXY, scaledXY, distance
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- if pointScaled == True:
- # Using screen coords
- p = self.scaled
- pxy = self.currentScale * np.array(pntXY) + self.currentShift
- else:
- # Using user coords
- p = self.points
- pxy = np.array(pntXY)
- # determine distance for each point
- d = np.sqrt(np.add.reduce((p - pxy) ** 2, 1)) # sqrt(dx^2+dy^2)
- pntIndex = np.argmin(d)
- dist = d[pntIndex]
- return [pntIndex, self.points[pntIndex], self.scaled[pntIndex] / self._pointSize, dist]
- class PolyLine(PolyPoints):
- """Class to define line type and style
- - All methods except __init__ are private.
- """
- _attributes = {'colour': 'black',
- 'width': 1,
- 'style': PENSTYLE_SOLID,
- 'legend': ''}
- def __init__(self, points, **attr):
- """
- Creates PolyLine object
- :param `points`: sequence (array, tuple or list) of (x,y) points making up line
- :keyword `attr`: keyword attributes, default to:
- ========================== ================================
- 'colour'= 'black' wx.Pen Colour any wx.Colour
- 'width'= 1 Pen width
- 'style'= wx.PENSTYLE_SOLID wx.Pen style
- 'legend'= '' Line Legend to display
- ========================== ================================
- """
- PolyPoints.__init__(self, points, attr)
- def draw(self, dc, printerScale, coord=None):
- colour = self.attributes['colour']
- width = self.attributes['width'] * printerScale * self._pointSize[0]
- style = self.attributes['style']
- if not isinstance(colour, wx.Colour):
- colour = wx.NamedColour(colour)
- pen = wx.Pen(colour, width, style)
- pen.SetCap(wx.CAP_BUTT)
- dc.SetPen(pen)
- if coord is None:
- if len(self.scaled): # bugfix for Mac OS X
- dc.DrawLines(self.scaled)
- else:
- dc.DrawLines(coord) # draw legend line
- def getSymExtent(self, printerScale):
- """Width and Height of Marker"""
- h = self.attributes['width'] * printerScale * self._pointSize[0]
- w = 5 * h
- return (w, h)
- class PolySpline(PolyLine):
- """Class to define line type and style
- - All methods except __init__ are private.
- """
- _attributes = {'colour': 'black',
- 'width': 1,
- 'style': PENSTYLE_SOLID,
- 'legend': ''}
- def __init__(self, points, **attr):
- """
- Creates PolyLine object
- :param `points`: sequence (array, tuple or list) of (x,y) points making up spline
- :keyword `attr`: keyword attributes, default to:
- ========================== ================================
- 'colour'= 'black' wx.Pen Colour any wx.Colour
- 'width'= 1 Pen width
- 'style'= wx.PENSTYLE_SOLID wx.Pen style
- 'legend'= '' Line Legend to display
- ========================== ================================
- """
- PolyLine.__init__(self, points, **attr)
- def draw(self, dc, printerScale, coord=None):
- colour = self.attributes['colour']
- width = self.attributes['width'] * printerScale * self._pointSize[0]
- style = self.attributes['style']
- if not isinstance(colour, wx.Colour):
- colour = wx.NamedColour(colour)
- pen = wx.Pen(colour, width, style)
- pen.SetCap(wx.CAP_ROUND)
- dc.SetPen(pen)
- if coord is None:
- if len(self.scaled): # bugfix for Mac OS X
- dc.DrawSpline(self.scaled)
- else:
- dc.DrawLines(coord) # draw legend line
- class PolyMarker(PolyPoints):
- """Class to define marker type and style
- - All methods except __init__ are private.
- """
- _attributes = {'colour': 'black',
- 'width': 1,
- 'size': 2,
- 'fillcolour': None,
- 'fillstyle': BRUSHSTYLE_SOLID,
- 'marker': 'circle',
- 'legend': ''}
- def __init__(self, points, **attr):
- """
- Creates PolyMarker object
- :param `points`: sequence (array, tuple or list) of (x,y) points
- :keyword `attr`: keyword attributes, default to:
- ================================ ================================
- 'colour'= 'black' wx.Pen Colour any wx.Colour
- 'width'= 1 Pen width
- 'size'= 2 Marker size
- 'fillcolour'= same as colour wx.Brush Colour any wx.Colour
- 'fillstyle'= wx.BRUSHSTYLE_SOLID wx.Brush fill style (use wx.BRUSHSTYLE_TRANSPARENT for no fill)
- 'style'= wx.FONTFAMILY_SOLID wx.Pen style
- 'marker'= 'circle' Marker shape
- 'legend'= '' Line Legend to display
- ================================ ================================
- Marker Shapes:
- - 'circle'
- - 'dot'
- - 'square'
- - 'triangle'
- - 'triangle_down'
- - 'cross'
- - 'plus'
- """
- PolyPoints.__init__(self, points, attr)
- def draw(self, dc, printerScale, coord=None):
- colour = self.attributes['colour']
- width = self.attributes['width'] * printerScale * self._pointSize[0]
- size = self.attributes['size'] * printerScale * self._pointSize[0]
- fillcolour = self.attributes['fillcolour']
- fillstyle = self.attributes['fillstyle']
- marker = self.attributes['marker']
- if colour and not isinstance(colour, wx.Colour):
- colour = wx.NamedColour(colour)
- if fillcolour and not isinstance(fillcolour, wx.Colour):
- fillcolour = wx.NamedColour(fillcolour)
- dc.SetPen(wx.Pen(colour, width))
- if fillcolour:
- dc.SetBrush(wx.Brush(fillcolour, fillstyle))
- else:
- dc.SetBrush(wx.Brush(colour, fillstyle))
- if coord is None:
- if len(self.scaled): # bugfix for Mac OS X
- self._drawmarkers(dc, self.scaled, marker, size)
- else:
- self._drawmarkers(dc, coord, marker, size) # draw legend marker
- def getSymExtent(self, printerScale):
- """Width and Height of Marker"""
- s = 5 * self.attributes['size'] * printerScale * self._pointSize[0]
- return (s, s)
- def _drawmarkers(self, dc, coords, marker, size=1):
- f = eval('self._' + marker)
- f(dc, coords, size)
- def _circle(self, dc, coords, size=1):
- fact = 2.5 * size
- wh = 5.0 * size
- rect = np.zeros((len(coords), 4), np.float) + [0.0, 0.0, wh, wh]
- rect[:, 0:2] = coords - [fact, fact]
- dc.DrawEllipseList(rect.astype(np.int32))
- def _dot(self, dc, coords, size=1):
- dc.DrawPointList(coords)
- def _square(self, dc, coords, size=1):
- fact = 2.5 * size
- wh = 5.0 * size
- rect = np.zeros((len(coords), 4), np.float) + [0.0, 0.0, wh, wh]
- rect[:, 0:2] = coords - [fact, fact]
- dc.DrawRectangleList(rect.astype(np.int32))
- def _triangle(self, dc, coords, size=1):
- shape = [(-2.5 * size, 1.44 * size),
- (2.5 * size, 1.44 * size), (0.0, -2.88 * size)]
- poly = np.repeat(coords, 3, 0)
- poly.shape = (len(coords), 3, 2)
- poly += shape
- dc.DrawPolygonList(poly.astype(np.int32))
- def _triangle_down(self, dc, coords, size=1):
- shape = [(-2.5 * size, -1.44 * size),
- (2.5 * size, -1.44 * size), (0.0, 2.88 * size)]
- poly = np.repeat(coords, 3, 0)
- poly.shape = (len(coords), 3, 2)
- poly += shape
- dc.DrawPolygonList(poly.astype(np.int32))
- def _cross(self, dc, coords, size=1):
- fact = 2.5 * size
- for f in [[-fact, -fact, fact, fact], [-fact, fact, fact, -fact]]:
- lines = np.concatenate((coords, coords), axis=1) + f
- dc.DrawLineList(lines.astype(np.int32))
- def _plus(self, dc, coords, size=1):
- fact = 2.5 * size
- for f in [[-fact, 0, fact, 0], [0, -fact, 0, fact]]:
- lines = np.concatenate((coords, coords), axis=1) + f
- dc.DrawLineList(lines.astype(np.int32))
- class PlotGraphics:
- """Container to hold PolyXXX objects and graph labels
- - All methods except __init__ are private.
- """
- def __init__(self, objects, title='', xLabel='', yLabel=''):
- """Creates PlotGraphics object
- objects - list of PolyXXX objects to make graph
- title - title shown at top of graph
- xLabel - label shown on x-axis
- yLabel - label shown on y-axis
- """
- if type(objects) not in [list, tuple]:
- raise TypeError("objects argument should be list or tuple")
- self.objects = objects
- self.title = title
- self.xLabel = xLabel
- self.yLabel = yLabel
- self._pointSize = (1.0, 1.0)
- def setLogScale(self, logscale):
- if type(logscale) != tuple:
- raise TypeError(
- 'logscale must be a tuple of bools, e.g. (False, False)')
- if len(self.objects) == 0:
- return
- for o in self.objects:
- o.setLogScale(logscale)
- def boundingBox(self):
- p1, p2 = self.objects[0].boundingBox()
- for o in self.objects[1:]:
- p1o, p2o = o.boundingBox()
- p1 = np.minimum(p1, p1o)
- p2 = np.maximum(p2, p2o)
- return p1, p2
- def scaleAndShift(self, scale=(1, 1), shift=(0, 0)):
- for o in self.objects:
- o.scaleAndShift(scale, shift)
- def setPrinterScale(self, scale):
- """Thickens up lines and markers only for printing"""
- self.printerScale = scale
- def setXLabel(self, xLabel=''):
- """Set the X axis label on the graph"""
- self.xLabel = xLabel
- def setYLabel(self, yLabel=''):
- """Set the Y axis label on the graph"""
- self.yLabel = yLabel
- def setTitle(self, title=''):
- """Set the title at the top of graph"""
- self.title = title
- def getXLabel(self):
- """Get x axis label string"""
- return self.xLabel
- def getYLabel(self):
- """Get y axis label string"""
- return self.yLabel
- def getTitle(self, title=''):
- """Get the title at the top of graph"""
- return self.title
- def draw(self, dc):
- for o in self.objects:
- # t=_time.clock() # profile info
- o._pointSize = self._pointSize
- o.draw(dc, self.printerScale)
- #dt= _time.clock()-t
- #print(o, "time=", dt)
- def getSymExtent(self, printerScale):
- """Get max width and height of lines and markers symbols for legend"""
- self.objects[0]._pointSize = self._pointSize
- symExt = self.objects[0].getSymExtent(printerScale)
- for o in self.objects[1:]:
- o._pointSize = self._pointSize
- oSymExt = o.getSymExtent(printerScale)
- symExt = np.maximum(symExt, oSymExt)
- return symExt
- def getLegendNames(self):
- """Returns list of legend names"""
- lst = [None] * len(self)
- for i in range(len(self)):
- lst[i] = self.objects[i].getLegend()
- return lst
- def __len__(self):
- return len(self.objects)
- def __getitem__(self, item):
- return self.objects[item]
- #-------------------------------------------------------------------------
- # Main window that you will want to import into your application.
- class PlotCanvas(wx.Panel):
- """
- Subclass of a wx.Panel which holds two scrollbars and the actual
- plotting canvas (self.canvas). It allows for simple general plotting
- of data with zoom, labels, and automatic axis scaling."""
- def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
- size=wx.DefaultSize, style=0, name="plotCanvas"):
- """Constructs a panel, which can be a child of a frame or
- any other non-control window"""
- wx.Panel.__init__(self, parent, id, pos, size, style, name)
- self._isWindowCreated = False
- sizer = wx.FlexGridSizer(2, 2, 0, 0)
- self.canvas = wx.Window(self, -1)
- self.sb_vert = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL)
- self.sb_vert.SetScrollbar(0, 1000, 1000, 1000)
- self.sb_hor = wx.ScrollBar(self, -1, style=wx.SB_HORIZONTAL)
- self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
- sizer.Add(self.canvas, 1, wx.EXPAND)
- sizer.Add(self.sb_vert, 0, wx.EXPAND)
- sizer.Add(self.sb_hor, 0, wx.EXPAND)
- sizer.Add((0, 0))
- sizer.AddGrowableRow(0, 1)
- sizer.AddGrowableCol(0, 1)
- self.sb_vert.Show(False)
- self.sb_hor.Show(False)
- self.SetSizer(sizer)
- self.Fit()
- self.border = (1, 1)
- self.SetBackgroundColour("white")
- # Create some mouse events for zooming
- self.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
- self.canvas.Bind(wx.EVT_LEFT_UP, self.OnMouseLeftUp)
- self.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
- self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.OnMouseDoubleClick)
- self.canvas.Bind(wx.EVT_RIGHT_DOWN, self.OnMouseRightDown)
- # scrollbar events
- self.Bind(wx.EVT_SCROLL_THUMBTRACK, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_PAGEUP, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_PAGEDOWN, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_LINEUP, self.OnScroll)
- self.Bind(wx.EVT_SCROLL_LINEDOWN, self.OnScroll)
- # set curser as cross-hairs
- self.canvas.SetCursor(wx.CROSS_CURSOR)
- ## self.HandCursor = wx.Cursor(Hand.GetImage())
- self.HandCursor = wx.CursorFromImage(Hand.GetImage())
- self.GrabHandCursor = wx.CursorFromImage(GrabHand.GetImage())
- ## self.GrabHandCursor = wx.Cursor(GrabHand.GetImage())
- ## self.MagCursor = wx.Cursor(MagPlus.GetImage())
- self.MagCursor = wx.CursorFromImage(MagPlus.GetImage())
- # Things for printing
- self._print_data = None
- self._pageSetupData = None
- self.printerScale = 1
- self.parent = parent
- # scrollbar variables
- self._sb_ignore = False
- self._adjustingSB = False
- self._sb_xfullrange = 0
- self._sb_yfullrange = 0
- self._sb_xunit = 0
- self._sb_yunit = 0
- self._dragEnabled = False
- self._screenCoordinates = np.array([0.0, 0.0])
- self._logscale = (False, False)
- # Zooming variables
- self._zoomInFactor = 0.5
- self._zoomOutFactor = 2
- self._zoomCorner1 = np.array([0.0, 0.0]) # left mouse down corner
- self._zoomCorner2 = np.array([0.0, 0.0]) # left mouse up corner
- self._zoomEnabled = False
- self._hasDragged = False
- # Drawing Variables
- self.last_draw = None
- self._pointScale = 1
- self._pointShift = 0
- self._xSpec = 'auto'
- self._ySpec = 'auto'
- self._gridEnabled = False
- self._legendEnabled = False
- self._titleEnabled = True
- self._centerLinesEnabled = False
- self._diagonalsEnabled = False
- # Fonts
- self._fontCache = {}
- self._fontSizeAxis = 10
- self._fontSizeTitle = 15
- self._fontSizeLegend = 7
- # pointLabels
- self._pointLabelEnabled = False
- self.last_PointLabel = None
- self._pointLabelFunc = None
- self.canvas.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave)
- if sys.platform != "darwin":
- self._logicalFunction = wx.EQUIV # (NOT src) XOR dst
- else:
- # wx.EQUIV not supported on Mac OS X
- self._logicalFunction = wx.COPY
- self._useScientificNotation = False
- self._antiAliasingEnabled = False
- self._hiResEnabled = False
- self._pointSize = (1.0, 1.0)
- self._fontScale = 1.0
- self.canvas.Bind(wx.EVT_PAINT, self.OnPaint)
- self.canvas.Bind(wx.EVT_SIZE, self.OnSize)
- # OnSize called to make sure the buffer is initialized.
- # This might result in OnSize getting called twice on some
- # platforms at initialization, but little harm done.
- self.OnSize(None) # sets the initial size based on client size
- self._gridColour = wx.BLACK
- if '__WXGTK__' in wx.PlatformInfo:
- self.Bind(wx.EVT_WINDOW_CREATE, self.doSetWindowCreated)
- else:
- self.doSetWindowCreated(None)
- def doSetWindowCreated(self, evt):
- # OnSize called to make sure the buffer is initialized.
- # This might result in OnSize getting called twice on some
- # platforms at initialization, but little harm done.
- self._isWindowCreated = True
- self.OnSize(None)
- def SetCursor(self, cursor):
- self.canvas.SetCursor(cursor)
- def GetGridColour(self):
- return self._gridColour
- def SetGridColour(self, colour):
- if isinstance(colour, wx.Colour):
- self._gridColour = colour
- else:
- self._gridColour = wx.Colour(colour)
- # SaveFile
- def SaveFile(self, fileName=''):
- """Saves the file to the type specified in the extension. If no file
- name is specified a dialog box is provided. Returns True if sucessful,
- otherwise False.
- .bmp Save a Windows bitmap file.
- .xbm Save an X bitmap file.
- .xpm Save an XPM bitmap file.
- .png Save a Portable Network Graphics file.
- .jpg Save a Joint Photographic Experts Group file.
- """
- extensions = {
- "bmp": wx.BITMAP_TYPE_BMP, # Save a Windows bitmap file.
- "xbm": wx.BITMAP_TYPE_XBM, # Save an X bitmap file.
- "xpm": wx.BITMAP_TYPE_XPM, # Save an XPM bitmap file.
- "jpg": wx.BITMAP_TYPE_JPEG, # Save a JPG file.
- "png": wx.BITMAP_TYPE_PNG, # Save a PNG file.
- }
- fType = _string.lower(fileName[-3:])
- dlg1 = None
- while fType not in extensions:
- if dlg1: # FileDialog exists: Check for extension
- dlg2 = wx.MessageDialog(self, 'File name extension\n'
- 'must be one of\nbmp, xbm, xpm, png, or jpg',
- 'File Name Error', wx.OK | wx.ICON_ERROR)
- try:
- dlg2.ShowModal()
- finally:
- dlg2.Destroy()
- # FileDialog doesn't exist: just check one
- else:
- dlg1 = wx.FileDialog(
- self,
- "Choose a file with extension bmp, gif, xbm, xpm, png, or jpg", ".", "",
- "BMP files (*.bmp)|*.bmp|XBM files (*.xbm)|*.xbm|XPM file (*.xpm)|*.xpm|PNG files (*.png)|*.png|JPG files (*.jpg)|*.jpg",
- wx.SAVE | wx.OVERWRITE_PROMPT
- )
- if dlg1.ShowModal() == wx.ID_OK:
- fileName = dlg1.GetPath()
- fType = _string.lower(fileName[-3:])
- else: # exit without saving
- dlg1.Destroy()
- return False
- if dlg1:
- dlg1.Destroy()
- # Save Bitmap
- res = self._Buffer.SaveFile(fileName, extensions[fType])
- return res
- @property
- def print_data(self):
- if not self._print_data:
- self._print_data = wx.PrintData()
- self._print_data.SetPaperId(wx.PAPER_LETTER)
- self._print_data.SetOrientation(wx.LANDSCAPE)
- return self._print_data
- @property
- def pageSetupData(self):
- if not self._pageSetupData:
- self._pageSetupData = wx.PageSetupDialogData()
- self._pageSetupData.SetMarginBottomRight((25, 25))
- self._pageSetupData.SetMarginTopLeft((25, 25))
- self._pageSetupData.SetPrintData(self.print_data)
- return self._pageSetupData
- def PageSetup(self):
- """Brings up the page setup dialog"""
- data = self.pageSetupData
- data.SetPrintData(self.print_data)
- dlg = wx.PageSetupDialog(self.parent, data)
- try:
- if dlg.ShowModal() == wx.ID_OK:
- data = dlg.GetPageSetupData() # returns wx.PageSetupDialogData
- # updates page parameters from dialog
- self.pageSetupData.SetMarginBottomRight(
- data.GetMarginBottomRight())
- self.pageSetupData.SetMarginTopLeft(data.GetMarginTopLeft())
- self.pageSetupData.SetPrintData(data.GetPrintData())
- self._print_data = wx.PrintData(
- data.GetPrintData()) # updates print_data
- finally:
- dlg.Destroy()
- def Printout(self, paper=None):
- """Print current plot."""
- if paper != None:
- self.print_data.SetPaperId(paper)
- pdd = wx.PrintDialogData(self.print_data)
- printer = wx.Printer(pdd)
- out = PlotPrintout(self)
- print_ok = printer.Print(self.parent, out)
- if print_ok:
- self._print_data = wx.PrintData(
- printer.GetPrintDialogData().GetPrintData())
- out.Destroy()
- def PrintPreview(self):
- """Print-preview current plot."""
- printout = PlotPrintout(self)
- printout2 = PlotPrintout(self)
- self.preview = wx.PrintPreview(printout, printout2, self.print_data)
- if not self.preview.IsOk():
- wx.MessageDialog(self, "Print Preview failed.\n"
- "Check that default printer is configured\n",
- "Print error", wx.OK | wx.CENTRE).ShowModal()
- self.preview.SetZoom(40)
- # search up tree to find frame instance
- frameInst = self
- while not isinstance(frameInst, wx.Frame):
- frameInst = frameInst.GetParent()
- frame = wx.PreviewFrame(self.preview, frameInst, "Preview")
- frame.Initialize()
- frame.SetPosition(self.GetPosition())
- frame.SetSize((600, 550))
- frame.Centre(wx.BOTH)
- frame.Show(True)
- def setLogScale(self, logscale):
- if type(logscale) != tuple:
- raise TypeError(
- 'logscale must be a tuple of bools, e.g. (False, False)')
- if self.last_draw is not None:
- graphics, xAxis, yAxis = self.last_draw
- graphics.setLogScale(logscale)
- self.last_draw = (graphics, None, None)
- self.SetXSpec('min')
- self.SetYSpec('min')
- self._logscale = logscale
- def getLogScale(self):
- return self._logscale
- def SetFontSizeAxis(self, point=10):
- """Set the tick and axis label font size (default is 10 point)"""
- self._fontSizeAxis = point
- def GetFontSizeAxis(self):
- """Get current tick and axis label font size in points"""
- return self._fontSizeAxis
- def SetFontSizeTitle(self, point=15):
- """Set Title font size (default is 15 point)"""
- self._fontSizeTitle = point
- def GetFontSizeTitle(self):
- """Get current Title font size in points"""
- return self._fontSizeTitle
- def SetFontSizeLegend(self, point=7):
- """Set Legend font size (default is 7 point)"""
- self._fontSizeLegend = point
- def GetFontSizeLegend(self):
- """Get current Legend font size in points"""
- return self._fontSizeLegend
- def SetShowScrollbars(self, value):
- """Set True to show scrollbars"""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- if value == self.GetShowScrollbars():
- return
- self.sb_vert.Show(value)
- self.sb_hor.Show(value)
- wx.CallAfter(self.Layout)
- def GetShowScrollbars(self):
- """Set True to show scrollbars"""
- return self.sb_vert.IsShown()
- def SetUseScientificNotation(self, useScientificNotation):
- self._useScientificNotation = useScientificNotation
- def GetUseScientificNotation(self):
- return self._useScientificNotation
- def SetEnableAntiAliasing(self, enableAntiAliasing):
- """Set True to enable anti-aliasing."""
- self._antiAliasingEnabled = enableAntiAliasing
- self.Redraw()
- def GetEnableAntiAliasing(self):
- return self._antiAliasingEnabled
- def SetEnableHiRes(self, enableHiRes):
- """Set True to enable high-resolution mode when using anti-aliasing."""
- self._hiResEnabled = enableHiRes
- self.Redraw()
- def GetEnableHiRes(self):
- return self._hiResEnabled
- def SetEnableDrag(self, value):
- """Set True to enable drag."""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- if value:
- if self.GetEnableZoom():
- self.SetEnableZoom(False)
- self.SetCursor(self.HandCursor)
- else:
- self.SetCursor(wx.CROSS_CURSOR)
- self._dragEnabled = value
- def GetEnableDrag(self):
- return self._dragEnabled
- def SetEnableZoom(self, value):
- """Set True to enable zooming."""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- if value:
- if self.GetEnableDrag():
- self.SetEnableDrag(False)
- self.SetCursor(self.MagCursor)
- else:
- self.SetCursor(wx.CROSS_CURSOR)
- self._zoomEnabled = value
- def GetEnableZoom(self):
- """True if zooming enabled."""
- return self._zoomEnabled
- def SetEnableGrid(self, value):
- """Set True, 'Horizontal' or 'Vertical' to enable grid."""
- if value not in [True, False, 'Horizontal', 'Vertical']:
- raise TypeError(
- "Value should be True, False, Horizontal or Vertical")
- self._gridEnabled = value
- self.Redraw()
- def GetEnableGrid(self):
- """True if grid enabled."""
- return self._gridEnabled
- def SetEnableCenterLines(self, value):
- """Set True, 'Horizontal' or 'Vertical' to enable center line(s)."""
- if value not in [True, False, 'Horizontal', 'Vertical']:
- raise TypeError(
- "Value should be True, False, Horizontal or Vertical")
- self._centerLinesEnabled = value
- self.Redraw()
- def GetEnableCenterLines(self):
- """True if grid enabled."""
- return self._centerLinesEnabled
- def SetEnableDiagonals(self, value):
- """Set True, 'Bottomleft-Topright' or 'Bottomright-Topleft' to enable
- center line(s)."""
- if value not in [True, False, 'Bottomleft-Topright', 'Bottomright-Topleft']:
- raise TypeError(
- "Value should be True, False, Bottomleft-Topright or Bottomright-Topleft")
- self._diagonalsEnabled = value
- self.Redraw()
- def GetEnableDiagonals(self):
- """True if grid enabled."""
- return self._diagonalsEnabled
- def SetEnableLegend(self, value):
- """Set True to enable legend."""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- self._legendEnabled = value
- self.Redraw()
- def GetEnableLegend(self):
- """True if Legend enabled."""
- return self._legendEnabled
- def SetEnableTitle(self, value):
- """Set True to enable title."""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- self._titleEnabled = value
- self.Redraw()
- def GetEnableTitle(self):
- """True if title enabled."""
- return self._titleEnabled
- def SetEnablePointLabel(self, value):
- """Set True to enable pointLabel."""
- if value not in [True, False]:
- raise TypeError("Value should be True or False")
- self._pointLabelEnabled = value
- self.Redraw() # will erase existing pointLabel if present
- self.last_PointLabel = None
- def GetEnablePointLabel(self):
- """True if pointLabel enabled."""
- return self._pointLabelEnabled
- def SetPointLabelFunc(self, func):
- """Sets the function with custom code for pointLabel drawing
- ******** more info needed ***************
- """
- self._pointLabelFunc = func
- def GetPointLabelFunc(self):
- """Returns pointLabel Drawing Function"""
- return self._pointLabelFunc
- def Reset(self):
- """Unzoom the plot."""
- self.last_PointLabel = None # reset pointLabel
- if self.last_draw is not None:
- self._Draw(self.last_draw[0])
- def ScrollRight(self, units):
- """Move view right number of axis units."""
- self.last_PointLabel = None # reset pointLabel
- if self.last_draw is not None:
- graphics, xAxis, yAxis = self.last_draw
- xAxis = (xAxis[0] + units, xAxis[1] + units)
- self._Draw(graphics, xAxis, yAxis)
- def ScrollUp(self, units):
- """Move view up number of axis units."""
- self.last_PointLabel = None # reset pointLabel
- if self.last_draw is not None:
- graphics, xAxis, yAxis = self.last_draw
- yAxis = (yAxis[0] + units, yAxis[1] + units)
- self._Draw(graphics, xAxis, yAxis)
- def GetXY(self, event):
- """Wrapper around _getXY, which handles log scales"""
- x, y = self._getXY(event)
- if self.getLogScale()[0]:
- x = np.power(10, x)
- if self.getLogScale()[1]:
- y = np.power(10, y)
- return x, y
- def _getXY(self, event):
- """Takes a mouse event and returns the XY user axis values."""
- x, y = self.PositionScreenToUser(event.GetPosition())
- return x, y
- def PositionUserToScreen(self, pntXY):
- """Converts User position to Screen Coordinates"""
- userPos = np.array(pntXY)
- x, y = userPos * self._pointScale + self._pointShift
- return x, y
- def PositionScreenToUser(self, pntXY):
- """Converts Screen position to User Coordinates"""
- screenPos = np.array(pntXY)
- x, y = (screenPos - self._pointShift) / self._pointScale
- return x, y
- def SetXSpec(self, type='auto'):
- """xSpec- defines x axis type. Can be 'none', 'min' or 'auto'
- where:
- * 'none' - shows no axis or tick mark values
- * 'min' - shows min bounding box values
- * 'auto' - rounds axis range to sensible values
- * <number> - like 'min', but with <number> tick marks
- """
- self._xSpec = type
- def SetYSpec(self, type='auto'):
- """ySpec- defines x axis type. Can be 'none', 'min' or 'auto'
- where:
- * 'none' - shows no axis or tick mark values
- * 'min' - shows min bounding box values
- * 'auto' - rounds axis range to sensible values
- * <number> - like 'min', but with <number> tick marks
- """
- self._ySpec = type
- def GetXSpec(self):
- """Returns current XSpec for axis"""
- return self._xSpec
- def GetYSpec(self):
- """Returns current YSpec for axis"""
- return self._ySpec
- def GetXMaxRange(self):
- xAxis = self._getXMaxRange()
- if self.getLogScale()[0]:
- xAxis = np.power(10, xAxis)
- return xAxis
- def _getXMaxRange(self):
- """Returns (minX, maxX) x-axis range for displayed graph"""
- graphics = self.last_draw[0]
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- xAxis = self._axisInterval(self._xSpec, p1[0], p2[0]) # in user units
- return xAxis
- def GetYMaxRange(self):
- yAxis = self._getYMaxRange()
- if self.getLogScale()[1]:
- yAxis = np.power(10, yAxis)
- return yAxis
- def _getYMaxRange(self):
- """Returns (minY, maxY) y-axis range for displayed graph"""
- graphics = self.last_draw[0]
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
- return yAxis
- def GetXCurrentRange(self):
- xAxis = self._getXCurrentRange()
- if self.getLogScale()[0]:
- xAxis = np.power(10, xAxis)
- return xAxis
- def _getXCurrentRange(self):
- """Returns (minX, maxX) x-axis for currently displayed portion of graph"""
- return self.last_draw[1]
- def GetYCurrentRange(self):
- yAxis = self._getYCurrentRange()
- if self.getLogScale()[1]:
- yAxis = np.power(10, yAxis)
- return yAxis
- def _getYCurrentRange(self):
- """Returns (minY, maxY) y-axis for currently displayed portion of graph"""
- return self.last_draw[2]
- def Draw(self, graphics, xAxis=None, yAxis=None, dc=None):
- """Wrapper around _Draw, which handles log axes"""
- graphics.setLogScale(self.getLogScale())
- # check Axis is either tuple or none
- if type(xAxis) not in [type(None), tuple]:
- raise TypeError(
- "xAxis should be None or (minX,maxX)" + str(type(xAxis)))
- if type(yAxis) not in [type(None), tuple]:
- raise TypeError(
- "yAxis should be None or (minY,maxY)" + str(type(xAxis)))
- # check case for axis = (a,b) where a==b caused by improper zooms
- if xAxis != None:
- if xAxis[0] == xAxis[1]:
- return
- if self.getLogScale()[0]:
- xAxis = np.log10(xAxis)
- if yAxis != None:
- if yAxis[0] == yAxis[1]:
- return
- if self.getLogScale()[1]:
- yAxis = np.log10(yAxis)
- self._Draw(graphics, xAxis, yAxis, dc)
- def _Draw(self, graphics, xAxis=None, yAxis=None, dc=None):
- """\
- Draw objects in graphics with specified x and y axis.
- graphics- instance of PlotGraphics with list of PolyXXX objects
- xAxis - tuple with (min, max) axis range to view
- yAxis - same as xAxis
- dc - drawing context - doesn't have to be specified.
- If it's not, the offscreen buffer is used
- """
- if dc is None:
- # sets new dc and clears it
- dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
- bbr = wx.Brush(self.GetBackgroundColour(), BRUSHSTYLE_SOLID)
- dc.SetBackground(bbr)
- dc.SetBackgroundMode(wx.SOLID)
- dc.Clear()
- if self._antiAliasingEnabled:
- if not isinstance(dc, wx.GCDC):
- try:
- dc = wx.GCDC(dc)
- except Exception:
- pass
- else:
- if self._hiResEnabled:
- # high precision - each logical unit is 1/20 of a point
- dc.SetMapMode(wx.MM_TWIPS)
- self._pointSize = tuple(
- 1.0 / lscale for lscale in dc.GetLogicalScale())
- self._setSize()
- elif self._pointSize != (1.0, 1.0):
- self._pointSize = (1.0, 1.0)
- self._setSize()
- if (sys.platform in ("darwin", "win32") or not isinstance(dc, wx.GCDC) or wx.VERSION >= (2, 9)):
- self._fontScale = sum(self._pointSize) / 2.0
- else:
- # on Linux, we need to correct the font size by a certain factor if wx.GCDC is used,
- # to make text the same size as if wx.GCDC weren't used
- screenppi = map(float, wx.ScreenDC().GetPPI())
- ppi = dc.GetPPI()
- self._fontScale = (screenppi[
- 0] / ppi[0] * self._pointSize[0] + screenppi[1] / ppi[1] * self._pointSize[1]) / 2.0
- graphics._pointSize = self._pointSize
- dc.SetTextForeground(self.GetForegroundColour())
- dc.SetTextBackground(self.GetBackgroundColour())
- # dc.Clear()
- # set font size for every thing but title and legend
- dc.SetFont(self._getFont(self._fontSizeAxis))
- # sizes axis to axis type, create lower left and upper right corners of
- # plot
- if xAxis is None or yAxis is None:
- # One or both axis not specified in Draw
- p1, p2 = graphics.boundingBox() # min, max points of graphics
- if xAxis is None:
- xAxis = self._axisInterval(
- self._xSpec, p1[0], p2[0]) # in user units
- if yAxis is None:
- yAxis = self._axisInterval(self._ySpec, p1[1], p2[1])
- # Adjust bounding box for axis spec
- # lower left corner user scale (xmin,ymin)
- p1[0], p1[1] = xAxis[0], yAxis[0]
- # upper right corner user scale (xmax,ymax)
- p2[0], p2[1] = xAxis[1], yAxis[1]
- else:
- # Both axis specified in Draw
- # lower left corner user scale (xmin,ymin)
- p1 = np.array([xAxis[0], yAxis[0]])
- # upper right corner user scale (xmax,ymax)
- p2 = np.array([xAxis[1], yAxis[1]])
- # saves most recient values
- self.last_draw = (graphics, np.array(xAxis), np.array(yAxis))
- # Get ticks and textExtents for axis if required
- if self._xSpec is not 'none':
- xticks = self._xticks(xAxis[0], xAxis[1])
- else:
- xticks = None
- if xticks:
- # w h of x axis text last number on axis
- xTextExtent = dc.GetTextExtent(xticks[-1][1])
- else:
- xTextExtent = (0, 0) # No text for ticks
- if self._ySpec is not 'none':
- yticks = self._yticks(yAxis[0], yAxis[1])
- else:
- yticks = None
- if yticks:
- if self.getLogScale()[1]:
- yTextExtent = dc.GetTextExtent('-2e-2')
- else:
- yTextExtentBottom = dc.GetTextExtent(yticks[0][1])
- yTextExtentTop = dc.GetTextExtent(yticks[-1][1])
- yTextExtent = (max(yTextExtentBottom[0], yTextExtentTop[0]),
- max(yTextExtentBottom[1], yTextExtentTop[1]))
- else:
- yticks = None
- yTextExtent = (0, 0) # No text for ticks
- # TextExtents for Title and Axis Labels
- titleWH, xLabelWH, yLabelWH = self._titleLablesWH(dc, graphics)
- # TextExtents for Legend
- legendBoxWH, legendSymExt, legendTextExt = self._legendWH(dc, graphics)
- # room around graph area
- # use larger of number width or legend width
- rhsW = max(xTextExtent[0], legendBoxWH[0]) + 5 * self._pointSize[0]
- lhsW = yTextExtent[0] + yLabelWH[1] + 3 * self._pointSize[0]
- bottomH = max(
- xTextExtent[1], yTextExtent[1] / 2.) + xLabelWH[1] + 2 * self._pointSize[1]
- topH = yTextExtent[1] / 2. + titleWH[1]
- # make plot area smaller by text size
- textSize_scale = np.array([rhsW + lhsW, bottomH + topH])
- # shift plot area by this amount
- textSize_shift = np.array([lhsW, bottomH])
- # draw title if requested
- if self._titleEnabled:
- dc.SetFont(self._getFont(self._fontSizeTitle))
- titlePos = (self.plotbox_origin[0] + lhsW + (self.plotbox_size[0] - lhsW - rhsW) / 2. - titleWH[0] / 2.,
- self.plotbox_origin[1] - self.plotbox_size[1])
- dc.DrawText(graphics.getTitle(), titlePos[0], titlePos[1])
- # draw label text
- dc.SetFont(self._getFont(self._fontSizeAxis))
- xLabelPos = (self.plotbox_origin[0] + lhsW + (self.plotbox_size[0] - lhsW - rhsW) / 2. - xLabelWH[0] / 2.,
- self.plotbox_origin[1] - xLabelWH[1])
- dc.DrawText(graphics.getXLabel(), xLabelPos[0], xLabelPos[1])
- yLabelPos = (self.plotbox_origin[0] - 3 * self._pointSize[0],
- self.plotbox_origin[1] - bottomH - (self.plotbox_size[1] - bottomH - topH) / 2. + yLabelWH[0] / 2.)
- if graphics.getYLabel(): # bug fix for Linux
- dc.DrawRotatedText(
- graphics.getYLabel(), yLabelPos[0], yLabelPos[1], 90)
- # drawing legend makers and text
- if self._legendEnabled:
- self._drawLegend(
- dc, graphics, rhsW, topH, legendBoxWH, legendSymExt, legendTextExt)
- # allow for scaling and shifting plotted points
- scale = (self.plotbox_size - textSize_scale) / \
- (p2 - p1) * np.array((1, -1))
- shift = -p1 * scale + self.plotbox_origin + \
- textSize_shift * np.array((1, -1))
- # make available for mouse events
- self._pointScale = scale / self._pointSize
- self._pointShift = shift / self._pointSize
- self._drawAxes(dc, p1, p2, scale, shift, xticks, yticks)
- graphics.scaleAndShift(scale, shift)
- # thicken up lines and markers if printing
- graphics.setPrinterScale(self.printerScale)
- # set clipping area so drawing does not occur outside axis box
- ptx, pty, rectWidth, rectHeight = self._point2ClientCoord(p1, p2)
- # allow graph to overlap axis lines by adding units to width and height
- dc.SetClippingRegion(ptx * self._pointSize[0], pty * self._pointSize[
- 1], rectWidth * self._pointSize[0] + 2, rectHeight * self._pointSize[1] + 1)
- # Draw the lines and markers
- #start = _time.clock()
- graphics.draw(dc)
- # print("entire graphics drawing took: %f second"%(_time.clock() - start))
- # remove the clipping region
- dc.DestroyClippingRegion()
- self._adjustScrollbars()
- def Redraw(self, dc=None):
- """Redraw the existing plot."""
- if self.last_draw is not None:
- graphics, xAxis, yAxis = self.last_draw
- self._Draw(graphics, xAxis, yAxis, dc)
- def Clear(self):
- """Erase the window."""
- self.last_PointLabel = None # reset pointLabel
- dc = wx.BufferedDC(wx.ClientDC(self.canvas), self._Buffer)
- bbr = wx.Brush(self.GetBackgroundColour(), wx.SOLID)
- dc.SetBackground(bbr)
- dc.SetBackgroundMode(wx.SOLID)
- dc.Clear()
- if self._antiAliasingEnabled:
- try:
- dc = wx.GCDC(dc)
- except Exception:
- pass
- dc.SetTextForeground(self.GetForegroundColour())
- dc.SetTextBackground(self.GetBackgroundColour())
- self.last_draw = None
- def Zoom(self, Center, Ratio):
- """ Zoom on the plot
- Centers on the X,Y coords given in Center
- Zooms by the Ratio = (Xratio, Yratio) given
- """
- self.last_PointLabel = None # reset maker
- x, y = Center
- if self.last_draw != None:
- (graphics, xAxis, yAxis) = self.last_draw
- w = (xAxis[1] - xAxis[0]) * Ratio[0]
- h = (yAxis[1] - yAxis[0]) * Ratio[1]
- xAxis = (x - w / 2, x + w / 2)
- yAxis = (y - h / 2, y + h / 2)
- self._Draw(graphics, xAxis, yAxis)
- def GetClosestPoints(self, pntXY, pointScaled=True):
- """Returns list with
- [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- list for each curve.
- Returns [] if no curves are being plotted.
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- if self.last_draw is None:
- # no graph available
- return []
- graphics, xAxis, yAxis = self.last_draw
- l = []
- for curveNum, obj in enumerate(graphics):
- # check there are points in the curve
- if len(obj.points) == 0:
- continue # go to next obj
- #[curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- cn = [curveNum] + \
- [obj.getLegend()] + obj.getClosestPoint(pntXY, pointScaled)
- l.append(cn)
- return l
- def GetClosestPoint(self, pntXY, pointScaled=True):
- """Returns list with
- [curveNumber, legend, index of closest point, pointXY, scaledXY, distance]
- list for only the closest curve.
- Returns [] if no curves are being plotted.
- x, y in user coords
- if pointScaled == True based on screen coords
- if pointScaled == False based on user coords
- """
- # closest points on screen based on screen scaling (pointScaled= True)
- # list [curveNumber, index, pointXY, scaledXY, distance] for each curve
- closestPts = self.GetClosestPoints(pntXY, pointScaled)
- if closestPts == []:
- return [] # no graph present
- # find one with least distance
- dists = [c[-1] for c in closestPts]
- mdist = min(dists) # Min dist
- i = dists.index(mdist) # index for min dist
- return closestPts[i] # this is the closest point on closest curve
- GetClosetPoint = GetClosestPoint
- def UpdatePointLabel(self, mDataDict):
- """Updates the pointLabel point on screen with data contained in
- mDataDict.
- mDataDict will be passed to your function set by
- SetPointLabelFunc. It can contain anything you
- want to display on the screen at the scaledXY point
- you specify.
- This function can be called from parent window with onClick,
- onMotion events etc.
- """
- if self.last_PointLabel != None:
- # compare pointXY
- if np.sometrue(mDataDict["pointXY"] != self.last_PointLabel["pointXY"]):
- # closest changed
- self._drawPointLabel(self.last_PointLabel) # erase old
- self._drawPointLabel(mDataDict) # plot new
- else:
- # just plot new with no erase
- self._drawPointLabel(mDataDict) # plot new
- # save for next erase
- self.last_PointLabel = mDataDict
- # event handlers **********************************
- def OnMotion(self, event):
- if self._zoomEnabled and event.LeftIsDown():
- if self._hasDragged:
- self._drawRubberBand(
- self._zoomCorner1, self._zoomCorner2) # remove old
- else:
- self._hasDragged = True
- self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
- self._drawRubberBand(
- self._zoomCorner1, self._zoomCorner2) # add new
- elif self._dragEnabled and event.LeftIsDown():
- coordinates = event.GetPosition()
- newpos, oldpos = map(np.array, map(
- self.PositionScreenToUser, [coordinates, self._screenCoordinates]))
- dist = newpos - oldpos
- self._screenCoordinates = coordinates
- if self.last_draw is not None:
- graphics, xAxis, yAxis = self.last_draw
- yAxis -= dist[1]
- xAxis -= dist[0]
- self._Draw(graphics, xAxis, yAxis)
- def OnMouseLeftDown(self, event):
- self._zoomCorner1[0], self._zoomCorner1[1] = self._getXY(event)
- self._screenCoordinates = np.array(event.GetPosition())
- if self._dragEnabled:
- self.SetCursor(self.GrabHandCursor)
- self.canvas.CaptureMouse()
- def OnMouseLeftUp(self, event):
- if self._zoomEnabled:
- if self._hasDragged == True:
- self._drawRubberBand(
- self._zoomCorner1, self._zoomCorner2) # remove old
- self._zoomCorner2[0], self._zoomCorner2[1] = self._getXY(event)
- self._hasDragged = False # reset flag
- minX, minY = np.minimum(self._zoomCorner1, self._zoomCorner2)
- maxX, maxY = np.maximum(self._zoomCorner1, self._zoomCorner2)
- self.last_PointLabel = None # reset pointLabel
- if self.last_draw != None:
- self._Draw(
- self.last_draw[0], xAxis=(minX, maxX), yAxis = (minY, maxY), dc = None)
- # else: # A box has not been drawn, zoom in on a point
- # this interfered with the double click, so I've disables it.
- # X,Y = self._getXY(event)
- # self.Zoom( (X,Y), (self._zoomInFactor,self._zoomInFactor) )
- if self._dragEnabled:
- self.SetCursor(self.HandCursor)
- if self.canvas.HasCapture():
- self.canvas.ReleaseMouse()
- def OnMouseDoubleClick(self, event):
- if self._zoomEnabled:
- # Give a little time for the click to be totally finished
- # before (possibly) removing the scrollbars and trigering
- # size events, etc.
- wx.CallLater(200, self.Reset)
- def OnMouseRightDown(self, event):
- if self._zoomEnabled:
- X, Y = self._getXY(event)
- self.Zoom((X, Y), (self._zoomOutFactor, self._zoomOutFactor))
- def OnPaint(self, event):
- # All that is needed here is to draw the buffer to screen
- if self.last_PointLabel != None:
- self._drawPointLabel(self.last_PointLabel) # erase old
- self.last_PointLabel = None
- dc = wx.BufferedPaintDC(self.canvas, self._Buffer)
- if self._antiAliasingEnabled:
- try:
- dc = wx.GCDC(dc)
- except Exception:
- pass
- def OnSize(self, event):
- # The Buffer init is done here, to make sure the buffer is always
- # the same size as the Window
- Size = self.canvas.GetClientSize()
- Size.width = max(1, Size.width)
- Size.height = max(1, Size.height)
- # Make new offscreen bitmap: this bitmap will always have the
- # current drawing in it, so it can be used to save the image to
- # a file, or whatever.
- self._Buffer = wx.EmptyBitmap(Size.width, Size.height)
- self._setSize()
- if not self._isWindowCreated:
- return
- self.last_PointLabel = None # reset pointLabel
- if self.last_draw is None:
- self.Clear()
- else:
- graphics, xSpec, ySpec = self.last_draw
- self._Draw(graphics, xSpec, ySpec)
- def OnLeave(self, event):
- """Used to erase pointLabel when mouse outside window"""
- if self.last_PointLabel != None:
- self._drawPointLabel(self.last_PointLabel) # erase old
- self.last_PointLabel = None
- def OnScroll(self, evt):
- if not self._adjustingSB:
- self._sb_ignore = True
- sbpos = evt.GetPosition()
- if evt.GetOrientation() == wx.VERTICAL:
- fullrange, pagesize = self.sb_vert.GetRange(
- ), self.sb_vert.GetPageSize()
- sbpos = fullrange - pagesize - sbpos
- dist = sbpos * self._sb_xunit - \
- (self._getXCurrentRange()[0] - self._sb_xfullrange)
- self.ScrollUp(dist)
- if evt.GetOrientation() == wx.HORIZONTAL:
- dist = sbpos * self._sb_xunit - \
- (self._getXCurrentRange()[0] - self._sb_xfullrange[0])
- self.ScrollRight(dist)
- # Private Methods **************************************************
- def _setSize(self, width=None, height=None):
- """DC width and height."""
- if width is None:
- (self.width, self.height) = self.canvas.GetClientSize()
- else:
- self.width, self.height = width, height
- self.width *= self._pointSize[0] # high precision
- self.height *= self._pointSize[1] # high precision
- self.plotbox_size = 0.97 * np.array([self.width, self.height])
- xo = 0.5 * (self.width - self.plotbox_size[0])
- yo = self.height - 0.5 * (self.height - self.plotbox_size[1])
- self.plotbox_origin = np.array([xo, yo])
- def _setPrinterScale(self, scale):
- """Used to thicken lines and increase marker size for print out."""
- # line thickness on printer is very thin at 600 dot/in. Markers small
- self.printerScale = scale
- def _printDraw(self, printDC):
- """Used for printing."""
- if self.last_draw != None:
- graphics, xSpec, ySpec = self.last_draw
- self._Draw(graphics, xSpec, ySpec, printDC)
- def _drawPointLabel(self, mDataDict):
- """Draws and erases pointLabels"""
- width = self._Buffer.GetWidth()
- height = self._Buffer.GetHeight()
- if sys.platform != "darwin":
- tmp_Buffer = wx.EmptyBitmap(width, height)
- dcs = wx.MemoryDC()
- dcs.SelectObject(tmp_Buffer)
- dcs.Clear()
- else:
- tmp_Buffer = self._Buffer.GetSubBitmap((0, 0, width, height))
- dcs = wx.MemoryDC(self._Buffer)
- self._pointLabelFunc(dcs, mDataDict) # custom user pointLabel function
- dc = wx.ClientDC(self.canvas)
- dc = wx.BufferedDC(dc, self._Buffer)
- # this will erase if called twice
- dc.Blit(0, 0, width, height, dcs, 0, 0, self._logicalFunction)
- if sys.platform == "darwin":
- self._Buffer = tmp_Buffer
- def _drawLegend(self, dc, graphics, rhsW, topH, legendBoxWH, legendSymExt, legendTextExt):
- """Draws legend symbols and text"""
- # top right hand corner of graph box is ref corner
- trhc = self.plotbox_origin + \
- (self.plotbox_size - [rhsW, topH]) * [1, -1]
- # border space between legend sym and graph box
- legendLHS = .091 * legendBoxWH[0]
- # 1.1 used as space between lines
- lineHeight = max(legendSymExt[1], legendTextExt[1]) * 1.1
- dc.SetFont(self._getFont(self._fontSizeLegend))
- for i in range(len(graphics)):
- o = graphics[i]
- s = i * lineHeight
- if isinstance(o, PolyMarker):
- # draw marker with legend
- pnt = (trhc[0] + legendLHS + legendSymExt[0] / 2.,
- trhc[1] + s + lineHeight / 2.)
- o.draw(dc, self.printerScale, coord=np.array([pnt]))
- elif isinstance(o, PolyLine):
- # draw line with legend
- pnt1 = (trhc[0] + legendLHS, trhc[1] + s + lineHeight / 2.)
- pnt2 = (trhc[0] + legendLHS + legendSymExt[0],
- trhc[1] + s + lineHeight / 2.)
- o.draw(dc, self.printerScale, coord=np.array([pnt1, pnt2]))
- else:
- raise TypeError(
- "object is neither PolyMarker or PolyLine instance")
- # draw legend txt
- pnt = (trhc[0] + legendLHS + legendSymExt[0] + 5 * self._pointSize[0],
- trhc[1] + s + lineHeight / 2. - legendTextExt[1] / 2)
- dc.DrawText(o.getLegend(), pnt[0], pnt[1])
- dc.SetFont(self._getFont(self._fontSizeAxis)) # reset
- def _titleLablesWH(self, dc, graphics):
- """Draws Title and labels and returns width and height for each"""
- # TextExtents for Title and Axis Labels
- dc.SetFont(self._getFont(self._fontSizeTitle))
- if self._titleEnabled:
- title = graphics.getTitle()
- titleWH = dc.GetTextExtent(title)
- else:
- titleWH = (0, 0)
- dc.SetFont(self._getFont(self._fontSizeAxis))
- xLabel, yLabel = graphics.getXLabel(), graphics.getYLabel()
- xLabelWH = dc.GetTextExtent(xLabel)
- yLabelWH = dc.GetTextExtent(yLabel)
- return titleWH, xLabelWH, yLabelWH
- def _legendWH(self, dc, graphics):
- """Returns the size in screen units for legend box"""
- if self._legendEnabled != True:
- legendBoxWH = symExt = txtExt = (0, 0)
- else:
- # find max symbol size
- symExt = graphics.getSymExtent(self.printerScale)
- # find max legend text extent
- dc.SetFont(self._getFont(self._fontSizeLegend))
- txtList = graphics.getLegendNames()
- txtExt = dc.GetTextExtent(txtList[0])
- for txt in graphics.getLegendNames()[1:]:
- txtExt = np.maximum(txtExt, dc.GetTextExtent(txt))
- maxW = symExt[0] + txtExt[0]
- maxH = max(symExt[1], txtExt[1])
- # padding .1 for lhs of legend box and space between lines
- maxW = maxW * 1.1
- maxH = maxH * 1.1 * len(txtList)
- dc.SetFont(self._getFont(self._fontSizeAxis))
- legendBoxWH = (maxW, maxH)
- return (legendBoxWH, symExt, txtExt)
- def _drawRubberBand(self, corner1, corner2):
- """Draws/erases rect box from corner1 to corner2"""
- ptx, pty, rectWidth, rectHeight = self._point2ClientCoord(
- corner1, corner2)
- # draw rectangle
- dc = wx.ClientDC(self.canvas)
- dc.SetPen(wx.Pen(wx.BLACK))
- dc.SetBrush(wx.Brush(wx.WHITE, BRUSHSTYLE_TRANSPARENT))
- dc.SetLogicalFunction(wx.INVERT)
- dc.DrawRectangle(ptx, pty, rectWidth, rectHeight)
- dc.SetLogicalFunction(wx.COPY)
- def _getFont(self, size):
- """Take font size, adjusts if printing and returns wx.Font"""
- s = size * self.printerScale * self._fontScale
- of = self.GetFont()
- # Linux speed up to get font from cache rather than X font server
- key = (int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
- font = self._fontCache.get(key, None)
- if font:
- return font # yeah! cache hit
- else:
- font = wx.Font(
- int(s), of.GetFamily(), of.GetStyle(), of.GetWeight())
- self._fontCache[key] = font
- return font
- def _point2ClientCoord(self, corner1, corner2):
- """Converts user point coords to client screen int coords x,y,width,height"""
- c1 = np.array(corner1)
- c2 = np.array(corner2)
- # convert to screen coords
- pt1 = c1 * self._pointScale + self._pointShift
- pt2 = c2 * self._pointScale + self._pointShift
- # make height and width positive
- pul = np.minimum(pt1, pt2) # Upper left corner
- plr = np.maximum(pt1, pt2) # Lower right corner
- rectWidth, rectHeight = plr - pul
- ptx, pty = pul
- return ptx, pty, rectWidth, rectHeight
- def _axisInterval(self, spec, lower, upper):
- """Returns sensible axis range for given spec"""
- if spec == 'none' or spec == 'min' or isinstance(spec, (float, int)):
- if lower == upper:
- return lower - 0.5, upper + 0.5
- else:
- return lower, upper
- elif spec == 'auto':
- range = upper - lower
- if range == 0.:
- return lower - 0.5, upper + 0.5
- log = np.log10(range)
- power = np.floor(log)
- fraction = log - power
- if fraction <= 0.05:
- power = power - 1
- grid = 10. ** power
- lower = lower - lower % grid
- mod = upper % grid
- if mod != 0:
- upper = upper - mod + grid
- return lower, upper
- elif type(spec) == type(()):
- lower, upper = spec
- if lower <= upper:
- return lower, upper
- else:
- return upper, lower
- else:
- raise ValueError(str(spec) + ': illegal axis specification')
- def _drawAxes(self, dc, p1, p2, scale, shift, xticks, yticks):
- # increases thickness for printing only
- penWidth = self.printerScale * self._pointSize[0]
- dc.SetPen(wx.Pen(self._gridColour, penWidth))
- # set length of tick marks--long ones make grid
- if self._gridEnabled:
- x, y, width, height = self._point2ClientCoord(p1, p2)
- if self._gridEnabled == 'Horizontal':
- yTickLength = (width / 2.0 + 1) * self._pointSize[1]
- xTickLength = 3 * self.printerScale * self._pointSize[0]
- elif self._gridEnabled == 'Vertical':
- yTickLength = 3 * self.printerScale * self._pointSize[1]
- xTickLength = (height / 2.0 + 1) * self._pointSize[0]
- else:
- yTickLength = (width / 2.0 + 1) * self._pointSize[1]
- xTickLength = (height / 2.0 + 1) * self._pointSize[0]
- else:
- # lengthens lines for printing
- yTickLength = 3 * self.printerScale * self._pointSize[1]
- xTickLength = 3 * self.printerScale * self._pointSize[0]
- if self._xSpec is not 'none':
- lower, upper = p1[0], p2[0]
- text = 1
- # miny, maxy and tick lengths
- for y, d in [(p1[1], -xTickLength), (p2[1], xTickLength)]:
- for x, label in xticks:
- pt = scale * np.array([x, y]) + shift
- # draws tick mark d units
- dc.DrawLine(pt[0], pt[1], pt[0], pt[1] + d)
- if text:
- dc.DrawText(
- label, pt[0], pt[1] + 2 * self._pointSize[1])
- a1 = scale * np.array([lower, y]) + shift
- a2 = scale * np.array([upper, y]) + shift
- # draws upper and lower axis line
- dc.DrawLine(a1[0], a1[1], a2[0], a2[1])
- text = 0 # axis values not drawn on top side
- if self._ySpec is not 'none':
- lower, upper = p1[1], p2[1]
- text = 1
- h = dc.GetCharHeight()
- for x, d in [(p1[0], -yTickLength), (p2[0], yTickLength)]:
- for y, label in yticks:
- pt = scale * np.array([x, y]) + shift
- dc.DrawLine(pt[0], pt[1], pt[0] - d, pt[1])
- if text:
- dc.DrawText(label, pt[0] - dc.GetTextExtent(label)[0] - 3 * self._pointSize[0],
- pt[1] - 0.75 * h)
- a1 = scale * np.array([x, lower]) + shift
- a2 = scale * np.array([x, upper]) + shift
- dc.DrawLine(a1[0], a1[1], a2[0], a2[1])
- text = 0 # axis values not drawn on right side
- if self._centerLinesEnabled:
- if self._centerLinesEnabled in ('Horizontal', True):
- y1 = scale[1] * p1[1] + shift[1]
- y2 = scale[1] * p2[1] + shift[1]
- y = (y1 - y2) / 2.0 + y2
- dc.DrawLine(
- scale[0] * p1[0] + shift[0], y, scale[0] * p2[0] + shift[0], y)
- if self._centerLinesEnabled in ('Vertical', True):
- x1 = scale[0] * p1[0] + shift[0]
- x2 = scale[0] * p2[0] + shift[0]
- x = (x1 - x2) / 2.0 + x2
- dc.DrawLine(
- x, scale[1] * p1[1] + shift[1], x, scale[1] * p2[1] + shift[1])
- if self._diagonalsEnabled:
- if self._diagonalsEnabled in ('Bottomleft-Topright', True):
- dc.DrawLine(scale[0] * p1[0] + shift[0], scale[1] * p1[1] +
- shift[1], scale[0] * p2[0] + shift[0], scale[1] * p2[1] + shift[1])
- if self._diagonalsEnabled in ('Bottomright-Topleft', True):
- dc.DrawLine(scale[0] * p1[0] + shift[0], scale[1] * p2[1] +
- shift[1], scale[0] * p2[0] + shift[0], scale[1] * p1[1] + shift[1])
- def _xticks(self, *args):
- if self._logscale[0]:
- return self._logticks(*args)
- else:
- attr = {'numticks': self._xSpec}
- return self._ticks(*args, **attr)
- def _yticks(self, *args):
- if self._logscale[1]:
- return self._logticks(*args)
- else:
- attr = {'numticks': self._ySpec}
- return self._ticks(*args, **attr)
- def _logticks(self, lower, upper):
- #lower,upper = map(np.log10,[lower,upper])
- # print('logticks',lower,upper)
- ticks = []
- mag = np.power(10, np.floor(lower))
- if upper - lower > 6:
- t = np.power(10, np.ceil(lower))
- base = np.power(10, np.floor((upper - lower) / 6))
- def inc(t):
- return t * base - t
- else:
- t = np.ceil(np.power(10, lower) / mag) * mag
- def inc(t):
- return 10 ** int(np.floor(np.log10(t) + 1e-16))
- majortick = int(np.log10(mag))
- while t <= pow(10, upper):
- if majortick != int(np.floor(np.log10(t) + 1e-16)):
- majortick = int(np.floor(np.log10(t) + 1e-16))
- ticklabel = '1e%d' % majortick
- else:
- if upper - lower < 2:
- minortick = int(t / pow(10, majortick) + .5)
- ticklabel = '%de%d' % (minortick, majortick)
- else:
- ticklabel = ''
- ticks.append((np.log10(t), ticklabel))
- t += inc(t)
- if len(ticks) == 0:
- ticks = [(0, '')]
- return ticks
- def _ticks(self, lower, upper, numticks=None):
- if isinstance(numticks, (float, int)):
- ideal = (upper - lower) / float(numticks)
- else:
- ideal = (upper - lower) / 7.
- log = np.log10(ideal)
- power = np.floor(log)
- if isinstance(numticks, (float, int)):
- grid = ideal
- else:
- fraction = log - power
- factor = 1.
- error = fraction
- for f, lf in self._multiples:
- e = np.fabs(fraction - lf)
- if e < error:
- error = e
- factor = f
- grid = factor * 10. ** power
- if self._useScientificNotation and (power > 4 or power < -4):
- format = '%+7.1e'
- elif power >= 0:
- digits = max(1, int(power))
- format = '%' + repr(digits) + '.0f'
- else:
- digits = -int(power)
- format = '%' + repr(digits + 2) + '.' + repr(digits) + 'f'
- ticks = []
- t = -grid * np.floor(-lower / grid)
- while t <= upper:
- if t == -0:
- t = 0
- ticks.append((t, format % (t,)))
- t = t + grid
- return ticks
- _multiples = [(2., np.log10(2.)), (5., np.log10(5.))]
- def _adjustScrollbars(self):
- if self._sb_ignore:
- self._sb_ignore = False
- return
- if not self.GetShowScrollbars():
- return
- self._adjustingSB = True
- needScrollbars = False
- # horizontal scrollbar
- r_current = self._getXCurrentRange()
- r_max = list(self._getXMaxRange())
- sbfullrange = float(self.sb_hor.GetRange())
- r_max[0] = min(r_max[0], r_current[0])
- r_max[1] = max(r_max[1], r_current[1])
- self._sb_xfullrange = r_max
- unit = (r_max[1] - r_max[0]) / float(self.sb_hor.GetRange())
- pos = int((r_current[0] - r_max[0]) / unit)
- if pos >= 0:
- pagesize = int((r_current[1] - r_current[0]) / unit)
- self.sb_hor.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
- self._sb_xunit = unit
- needScrollbars = needScrollbars or (pagesize != sbfullrange)
- else:
- self.sb_hor.SetScrollbar(0, 1000, 1000, 1000)
- # vertical scrollbar
- r_current = self._getYCurrentRange()
- r_max = list(self._getYMaxRange())
- sbfullrange = float(self.sb_vert.GetRange())
- r_max[0] = min(r_max[0], r_current[0])
- r_max[1] = max(r_max[1], r_current[1])
- self._sb_yfullrange = r_max
- unit = (r_max[1] - r_max[0]) / sbfullrange
- pos = int((r_current[0] - r_max[0]) / unit)
- if pos >= 0:
- pagesize = int((r_current[1] - r_current[0]) / unit)
- pos = (sbfullrange - 1 - pos - pagesize)
- self.sb_vert.SetScrollbar(pos, pagesize, sbfullrange, pagesize)
- self._sb_yunit = unit
- needScrollbars = needScrollbars or (pagesize != sbfullrange)
- else:
- self.sb_vert.SetScrollbar(0, 1000, 1000, 1000)
- self.SetShowScrollbars(needScrollbars)
- self._adjustingSB = False
- #-------------------------------------------------------------------------
- # Used to layout the printer page
- class PlotPrintout(wx.Printout):
- """Controls how the plot is made in printing and previewing"""
- # Do not change method names in this class,
- # we have to override wx.Printout methods here!
- def __init__(self, graph):
- """graph is instance of plotCanvas to be printed or previewed"""
- wx.Printout.__init__(self)
- self.graph = graph
- def HasPage(self, page):
- if page == 1:
- return True
- else:
- return False
- def GetPageInfo(self):
- return (1, 1, 1, 1) # disable page numbers
- def OnPrintPage(self, page):
- dc = self.GetDC() # allows using floats for certain functions
- ## print("PPI Printer",self.GetPPIPrinter())
- ## print("PPI Screen", self.GetPPIScreen())
- ## print("DC GetSize", dc.GetSize())
- ## print("GetPageSizePixels", self.GetPageSizePixels())
- # Note PPIScreen does not give the correct number
- # Calulate everything for printer and then scale for preview
- PPIPrinter = self.GetPPIPrinter() # printer dots/inch (w,h)
- # PPIScreen= self.GetPPIScreen() # screen dots/inch (w,h)
- dcSize = dc.GetSize() # DC size
- if self.graph._antiAliasingEnabled and not isinstance(dc, wx.GCDC):
- try:
- dc = wx.GCDC(dc)
- except Exception:
- pass
- else:
- if self.graph._hiResEnabled:
- # high precision - each logical unit is 1/20 of a point
- dc.SetMapMode(wx.MM_TWIPS)
- pageSize = self.GetPageSizePixels() # page size in terms of pixcels
- clientDcSize = self.graph.GetClientSize()
- # find what the margins are (mm)
- margLeftSize, margTopSize = self.graph.pageSetupData.GetMarginTopLeft()
- margRightSize, margBottomSize = self.graph.pageSetupData.GetMarginBottomRight()
- # calculate offset and scale for dc
- pixLeft = margLeftSize * PPIPrinter[0] / 25.4 # mm*(dots/in)/(mm/in)
- pixRight = margRightSize * PPIPrinter[0] / 25.4
- pixTop = margTopSize * PPIPrinter[1] / 25.4
- pixBottom = margBottomSize * PPIPrinter[1] / 25.4
- plotAreaW = pageSize[0] - (pixLeft + pixRight)
- plotAreaH = pageSize[1] - (pixTop + pixBottom)
- # ratio offset and scale to screen size if preview
- if self.IsPreview():
- ratioW = float(dcSize[0]) / pageSize[0]
- ratioH = float(dcSize[1]) / pageSize[1]
- pixLeft *= ratioW
- pixTop *= ratioH
- plotAreaW *= ratioW
- plotAreaH *= ratioH
- # rescale plot to page or preview plot area
- self.graph._setSize(plotAreaW, plotAreaH)
- # Set offset and scale
- dc.SetDeviceOrigin(pixLeft, pixTop)
- # Thicken up pens and increase marker size for printing
- ratioW = float(plotAreaW) / clientDcSize[0]
- ratioH = float(plotAreaH) / clientDcSize[1]
- aveScale = (ratioW + ratioH) / 2
- if self.graph._antiAliasingEnabled and not self.IsPreview():
- scale = dc.GetUserScale()
- dc.SetUserScale(
- scale[0] / self.graph._pointSize[0], scale[1] / self.graph._pointSize[1])
- self.graph._setPrinterScale(aveScale) # tickens up pens for printing
- self.graph._printDraw(dc)
- # rescale back to original
- self.graph._setSize()
- self.graph._setPrinterScale(1)
- self.graph.Redraw() # to get point label scale and shift correct
- return True
- #----------------------------------------------------------------------
- from wx.lib.embeddedimage import PyEmbeddedImage
- MagPlus = PyEmbeddedImage(
- "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAOFJ"
- "REFUeJy1VdEOxCAIo27//8XbuKfuPASGZ0Zisoi2FJABbZM3bY8c13lo5GvbjioBPAUEB0Yc"
- "VZ0iGRRc56Ee8DcikEgrJD8EFpzRegQASiRtBtzuA0hrdRPYQxaEKyJPG6IHyiK3xnNZvUSS"
- "NvUuzgYh0il4y14nCFPk5XgmNbRbQbVotGo9msj47G3UXJ7fuz8Q8FAGEu0/PbZh2D3NoshU"
- "1VUydBGVZKMimlGeErdNGUmf/x7YpjMjcf8HVYvS2adr6aFVlCy/5Ijk9q8SeCR9isJR8SeJ"
- "8pv7S0Wu2Acr0qdj3w7DRAAAAABJRU5ErkJggg==")
- GrabHand = PyEmbeddedImage(
- "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAARFJ"
- "REFUeJy1VdESgzAIS2j//4s3s5fRQ6Rad5M7H0oxCZhWSpK1TjwUBCBJAIBItL1fijlfe1yJ"
- "8noCGC9KgrXO7f0SyZEDAF/H2opsAHv9V/548nplT5Jo7YAFQKQ1RMWzmHUS96suqdBrHkuV"
- "uxpdJjCS8CfGXWdJ2glzcquKSR5c46QOtCpgNyIHj6oieAXg3282QvMX45hy8a8H0VonJZUO"
- "clesjOPg/dhBTq64o1Kacz4Ri2x5RKsf8+wcWQaJJL+A+xRcZHeQeBKjK+5EFiVJ4xy4x2Mn"
- "1Vk4U5/DWmfPieiqbye7a3tV/cCsWKu76K76KUFFchVnhigJ/hmktelm/m3e3b8k+Ec8PqLH"
- "CT4JRfyK9o1xYwAAAABJRU5ErkJggg==")
- Hand = PyEmbeddedImage(
- "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAARBJ"
- "REFUeJytluECwiAIhDn1/Z942/UnjCGoq+6XNeWDC1xAqbKr6zyo61Ibds60J8GBT0yS3IEM"
- "ABuIpJTa4IOLiAAQksuKyixLH1ShHgTgZl8KiALxOsODPoEMkgJ25Su6zoO3ZrjRnI96OLIq"
- "k7dsqOCboDa4XV/nwQEQVeFtmMnvbSJja+oagKBUaLn9hzd7VipRa9ostIv0O1uhzzaqNJxk"
- "hViwDVxqg51kksMg9r2rDDIFwHCap130FBhdMzeAfWg//6Ki5WWQrHSv6EIUeVs0g3wT3J7r"
- "FmWQp/JJDXeRh2TXcJa91zAH2uN2mvXFsrIrsjS8rnftWmWfAiLIStuD9m9h9belvzgS/1fP"
- "X7075IwDENteAAAAAElFTkSuQmCC")
- #---------------------------------------------------------------------------
- # if running standalone...
- #
- # ...a sample implementation using the above
- #
- def _draw1Objects():
- # 100 points sin function, plotted as green circles
- data1 = 2. * np.pi * np.arange(200) / 200.
- data1.shape = (100, 2)
- data1[:, 1] = np.sin(data1[:, 0])
- markers1 = PolyMarker(
- data1, legend='Green Markers', colour='green', marker='circle', size=1)
- # 50 points cos function, plotted as red line
- data1 = 2. * np.pi * np.arange(100) / 100.
- data1.shape = (50, 2)
- data1[:, 1] = np.cos(data1[:, 0])
- lines = PolySpline(data1, legend='Red Line', colour='red')
- # A few more points...
- pi = np.pi
- markers2 = PolyMarker([(0., 0.), (pi / 4., 1.), (pi / 2, 0.),
- (3. * pi / 4., -1)], legend='Cross Legend', colour='blue',
- marker='cross')
- return PlotGraphics([markers1, lines, markers2], "Graph Title", "X Axis", "Y Axis")
- def _draw2Objects():
- # 100 points sin function, plotted as green dots
- data1 = 2. * np.pi * np.arange(200) / 200.
- data1.shape = (100, 2)
- data1[:, 1] = np.sin(data1[:, 0])
- line1 = PolySpline(
- data1, legend='Green Line', colour='green', width=6, style=PENSTYLE_DOT)
- # 50 points cos function, plotted as red dot-dash
- data1 = 2. * np.pi * np.arange(100) / 100.
- data1.shape = (50, 2)
- data1[:, 1] = np.cos(data1[:, 0])
- line2 = PolySpline(
- data1, legend='Red Line', colour='red', width=3, style=PENSTYLE_DOT_DASH)
- # A few more points...
- pi = np.pi
- markers1 = PolyMarker([(0., 0.), (pi / 4., 1.), (pi / 2, 0.),
- (3. * pi / 4., -1)], legend='Cross Hatch Square', colour='blue', width=3, size=6,
- fillcolour='red', fillstyle=wx.CROSSDIAG_HATCH,
- marker='square')
- return PlotGraphics([markers1, line1, line2], "Big Markers with Different Line Styles")
- def _draw3Objects():
- markerList = ['circle', 'dot', 'square', 'triangle', 'triangle_down',
- 'cross', 'plus', 'circle']
- m = []
- for i in range(len(markerList)):
- m.append(PolyMarker([(2 * i + .5, i + .5)], legend=markerList[i], colour='blue',
- marker=markerList[i]))
- return PlotGraphics(m, "Selection of Markers", "Minimal Axis", "No Axis")
- def _draw4Objects():
- # 25,000 point line
- data1 = np.arange(5e5, 1e6, 10)
- data1.shape = (25000, 2)
- line1 = PolyLine(data1, legend='Wide Line', colour='green', width=5)
- # A few more points...
- markers2 = PolyMarker(data1, legend='Square', colour='blue',
- marker='square')
- return PlotGraphics([line1, markers2], "25,000 Points", "Value X", "")
- def _draw5Objects():
- # Empty graph with axis defined but no points/lines
- points = []
- line1 = PolyLine(points, legend='Wide Line', colour='green', width=5)
- return PlotGraphics([line1], "Empty Plot With Just Axes", "Value X", "Value Y")
- def _draw6Objects():
- # Bar graph
- points1 = [(1, 0), (1, 10)]
- line1 = PolyLine(points1, colour='green', legend='Feb.', width=10)
- points1g = [(2, 0), (2, 4)]
- line1g = PolyLine(points1g, colour='red', legend='Mar.', width=10)
- points1b = [(3, 0), (3, 6)]
- line1b = PolyLine(points1b, colour='blue', legend='Apr.', width=10)
- points2 = [(4, 0), (4, 12)]
- line2 = PolyLine(points2, colour='Yellow', legend='May', width=10)
- points2g = [(5, 0), (5, 8)]
- line2g = PolyLine(points2g, colour='orange', legend='June', width=10)
- points2b = [(6, 0), (6, 4)]
- line2b = PolyLine(points2b, colour='brown', legend='July', width=10)
- return PlotGraphics([line1, line1g, line1b, line2, line2g, line2b],
- "Bar Graph - (Turn on Grid, Legend)", "Months", "Number of Students")
- def _draw7Objects():
- # Empty graph with axis defined but no points/lines
- x = np.arange(1, 1000, 1)
- y1 = 4.5 * x ** 2
- y2 = 2.2 * x ** 3
- points1 = np.transpose([x, y1])
- points2 = np.transpose([x, y2])
- line1 = PolyLine(points1, legend='quadratic', colour='blue', width=1)
- line2 = PolyLine(points2, legend='cubic', colour='red', width=1)
- return PlotGraphics([line1, line2], "double log plot", "Value X", "Value Y")
- class TestFrame(wx.Frame):
- def __init__(self, parent, id, title):
- wx.Frame.__init__(self, parent, id, title,
- wx.DefaultPosition, (600, 400))
- # Now Create the menu bar and items
- self.mainmenu = wx.MenuBar()
- menu = wx.Menu()
- menu.Append(200, 'Page Setup...', 'Setup the printer page')
- self.Bind(wx.EVT_MENU, self.OnFilePageSetup, id=200)
- menu.Append(201, 'Print Preview...', 'Show the current plot on page')
- self.Bind(wx.EVT_MENU, self.OnFilePrintPreview, id=201)
- menu.Append(202, 'Print...', 'Print the current plot')
- self.Bind(wx.EVT_MENU, self.OnFilePrint, id=202)
- menu.Append(203, 'Save Plot...', 'Save current plot')
- self.Bind(wx.EVT_MENU, self.OnSaveFile, id=203)
- menu.Append(205, 'E&xit', 'Enough of this already!')
- self.Bind(wx.EVT_MENU, self.OnFileExit, id=205)
- self.mainmenu.Append(menu, '&File')
- menu = wx.Menu()
- menu.Append(206, 'Draw1', 'Draw plots1')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw1, id=206)
- menu.Append(207, 'Draw2', 'Draw plots2')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw2, id=207)
- menu.Append(208, 'Draw3', 'Draw plots3')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw3, id=208)
- menu.Append(209, 'Draw4', 'Draw plots4')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw4, id=209)
- menu.Append(210, 'Draw5', 'Draw plots5')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw5, id=210)
- menu.Append(260, 'Draw6', 'Draw plots6')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw6, id=260)
- menu.Append(261, 'Draw7', 'Draw plots7')
- self.Bind(wx.EVT_MENU, self.OnPlotDraw7, id=261)
- menu.Append(211, '&Redraw', 'Redraw plots')
- self.Bind(wx.EVT_MENU, self.OnPlotRedraw, id=211)
- menu.Append(212, '&Clear', 'Clear canvas')
- self.Bind(wx.EVT_MENU, self.OnPlotClear, id=212)
- menu.Append(213, '&Scale', 'Scale canvas')
- self.Bind(wx.EVT_MENU, self.OnPlotScale, id=213)
- menu.Append(
- 214, 'Enable &Zoom', 'Enable Mouse Zoom', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableZoom, id=214)
- menu.Append(215, 'Enable &Grid', 'Turn on Grid', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableGrid, id=215)
- menu.Append(
- 217, 'Enable &Drag', 'Activates dragging mode', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableDrag, id=217)
- menu.Append(
- 220, 'Enable &Legend', 'Turn on Legend', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableLegend, id=220)
- menu.Append(
- 222, 'Enable &Point Label', 'Show Closest Point', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnablePointLabel, id=222)
- menu.Append(
- 223, 'Enable &Anti-Aliasing', 'Smooth output', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableAntiAliasing, id=223)
- menu.Append(224, 'Enable &High-Resolution AA',
- 'Draw in higher resolution', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableHiRes, id=224)
- menu.Append(
- 226, 'Enable Center Lines', 'Draw center lines', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableCenterLines, id=226)
- menu.Append(
- 227, 'Enable Diagonal Lines', 'Draw diagonal lines', kind=wx.ITEM_CHECK)
- self.Bind(wx.EVT_MENU, self.OnEnableDiagonals, id=227)
- menu.Append(
- 231, 'Set Gray Background', 'Change background colour to gray')
- self.Bind(wx.EVT_MENU, self.OnBackgroundGray, id=231)
- menu.Append(
- 232, 'Set &White Background', 'Change background colour to white')
- self.Bind(wx.EVT_MENU, self.OnBackgroundWhite, id=232)
- menu.Append(
- 233, 'Set Red Label Text', 'Change label text colour to red')
- self.Bind(wx.EVT_MENU, self.OnForegroundRed, id=233)
- menu.Append(
- 234, 'Set &Black Label Text', 'Change label text colour to black')
- self.Bind(wx.EVT_MENU, self.OnForegroundBlack, id=234)
- menu.Append(225, 'Scroll Up 1', 'Move View Up 1 Unit')
- self.Bind(wx.EVT_MENU, self.OnScrUp, id=225)
- menu.Append(230, 'Scroll Rt 2', 'Move View Right 2 Units')
- self.Bind(wx.EVT_MENU, self.OnScrRt, id=230)
- menu.Append(235, '&Plot Reset', 'Reset to original plot')
- self.Bind(wx.EVT_MENU, self.OnReset, id=235)
- self.mainmenu.Append(menu, '&Plot')
- menu = wx.Menu()
- menu.Append(300, '&About', 'About this thing...')
- self.Bind(wx.EVT_MENU, self.OnHelpAbout, id=300)
- self.mainmenu.Append(menu, '&Help')
- self.SetMenuBar(self.mainmenu)
- # A status bar to tell people what's happening
- self.CreateStatusBar(1)
- self.client = PlotCanvas(self)
- # define the function for drawing pointLabels
- self.client.SetPointLabelFunc(self.DrawPointLabel)
- # Create mouse event for showing cursor coords in status bar
- self.client.canvas.Bind(wx.EVT_LEFT_DOWN, self.OnMouseLeftDown)
- # Show closest point when enabled
- self.client.canvas.Bind(wx.EVT_MOTION, self.OnMotion)
- self.Show(True)
- def DrawPointLabel(self, dc, mDataDict):
- """This is the fuction that defines how the pointLabels are plotted
- dc - DC that will be passed
- mDataDict - Dictionary of data that you want to use for the pointLabel
- As an example I have decided I want a box at the curve point
- with some text information about the curve plotted below.
- Any wxDC method can be used.
- """
- # ----------
- dc.SetPen(wx.Pen(wx.BLACK))
- dc.SetBrush(wx.Brush(wx.BLACK, BRUSHSTYLE_SOLID))
- sx, sy = mDataDict["scaledXY"] # scaled x,y of closest point
- # 10by10 square centered on point
- dc.DrawRectangle(sx - 5, sy - 5, 10, 10)
- px, py = mDataDict["pointXY"]
- cNum = mDataDict["curveNum"]
- pntIn = mDataDict["pIndex"]
- legend = mDataDict["legend"]
- # make a string to display
- s = "Crv# %i, '%s', Pt. (%.2f,%.2f), PtInd %i" % (
- cNum, legend, px, py, pntIn)
- dc.DrawText(s, sx, sy + 1)
- # -----------
- def OnMouseLeftDown(self, event):
- s = "Left Mouse Down at Point: (%.4f, %.4f)" % self.client._getXY(
- event)
- self.SetStatusText(s)
- event.Skip() # allows plotCanvas OnMouseLeftDown to be called
- def OnMotion(self, event):
- # show closest point (when enbled)
- if self.client.GetEnablePointLabel() == True:
- # make up dict with info for the pointLabel
- # I've decided to mark the closest point on the closest curve
- dlst = self.client.GetClosestPoint(
- self.client._getXY(event), pointScaled=True)
- if dlst != []: # returns [] if none
- curveNum, legend, pIndex, pointXY, scaledXY, distance = dlst
- # make up dictionary to pass to my user function (see
- # DrawPointLabel)
- mDataDict = {"curveNum": curveNum, "legend": legend, "pIndex": pIndex,
- "pointXY": pointXY, "scaledXY": scaledXY}
- # pass dict to update the pointLabel
- self.client.UpdatePointLabel(mDataDict)
- event.Skip() # go to next handler
- def OnFilePageSetup(self, event):
- self.client.PageSetup()
- def OnFilePrintPreview(self, event):
- self.client.PrintPreview()
- def OnFilePrint(self, event):
- self.client.Printout()
- def OnSaveFile(self, event):
- self.client.SaveFile()
- def OnFileExit(self, event):
- self.Close()
- def OnPlotDraw1(self, event):
- self.resetDefaults()
- self.client.Draw(_draw1Objects())
- def OnPlotDraw2(self, event):
- self.resetDefaults()
- self.client.Draw(_draw2Objects())
- def OnPlotDraw3(self, event):
- self.resetDefaults()
- self.client.SetFont(
- wx.Font(10, wx.FONTFAMILY_SCRIPT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
- self.client.SetFontSizeAxis(20)
- self.client.SetFontSizeLegend(12)
- self.client.SetXSpec('min')
- self.client.SetYSpec('none')
- self.client.Draw(_draw3Objects())
- def OnPlotDraw4(self, event):
- self.resetDefaults()
- drawObj = _draw4Objects()
- self.client.Draw(drawObj)
- # profile
- ## start = _time.clock()
- # for x in range(10):
- # self.client.Draw(drawObj)
- ## print("10 plots of Draw4 took: %f sec."%(_time.clock() - start))
- # profile end
- def OnPlotDraw5(self, event):
- # Empty plot with just axes
- self.resetDefaults()
- drawObj = _draw5Objects()
- # make the axis X= (0,5), Y=(0,10)
- # (default with None is X= (-1,1), Y= (-1,1))
- self.client.Draw(drawObj, xAxis=(0, 5), yAxis= (0, 10))
- def OnPlotDraw6(self, event):
- # Bar Graph Example
- self.resetDefaults()
- # self.client.SetEnableLegend(True) #turn on Legend
- # self.client.SetEnableGrid(True) #turn on Grid
- self.client.SetXSpec('none') # turns off x-axis scale
- self.client.SetYSpec('auto')
- self.client.Draw(_draw6Objects(), xAxis=(0, 7))
- def OnPlotDraw7(self, event):
- # log scale example
- self.resetDefaults()
- self.client.setLogScale((True, True))
- self.client.Draw(_draw7Objects())
- def OnPlotRedraw(self, event):
- self.client.Redraw()
- def OnPlotClear(self, event):
- self.client.Clear()
- def OnPlotScale(self, event):
- if self.client.last_draw != None:
- graphics, xAxis, yAxis = self.client.last_draw
- self.client.Draw(graphics, (1, 3.05), (0, 1))
- def OnEnableZoom(self, event):
- self.client.SetEnableZoom(event.IsChecked())
- self.mainmenu.Check(217, not event.IsChecked())
- def OnEnableGrid(self, event):
- self.client.SetEnableGrid(event.IsChecked())
- def OnEnableDrag(self, event):
- self.client.SetEnableDrag(event.IsChecked())
- self.mainmenu.Check(214, not event.IsChecked())
- def OnEnableLegend(self, event):
- self.client.SetEnableLegend(event.IsChecked())
- def OnEnablePointLabel(self, event):
- self.client.SetEnablePointLabel(event.IsChecked())
- def OnEnableAntiAliasing(self, event):
- self.client.SetEnableAntiAliasing(event.IsChecked())
- def OnEnableHiRes(self, event):
- self.client.SetEnableHiRes(event.IsChecked())
- def OnEnableCenterLines(self, event):
- self.client.SetEnableCenterLines(event.IsChecked())
- def OnEnableDiagonals(self, event):
- self.client.SetEnableDiagonals(event.IsChecked())
- def OnBackgroundGray(self, event):
- self.client.SetBackgroundColour("#CCCCCC")
- self.client.Redraw()
- def OnBackgroundWhite(self, event):
- self.client.SetBackgroundColour("white")
- self.client.Redraw()
- def OnForegroundRed(self, event):
- self.client.SetForegroundColour("red")
- self.client.Redraw()
- def OnForegroundBlack(self, event):
- self.client.SetForegroundColour("black")
- self.client.Redraw()
- def OnScrUp(self, event):
- self.client.ScrollUp(1)
- def OnScrRt(self, event):
- self.client.ScrollRight(2)
- def OnReset(self, event):
- self.client.Reset()
- def OnHelpAbout(self, event):
- from wx.lib.dialogs import ScrolledMessageDialog
- about = ScrolledMessageDialog(self, __doc__, "About...")
- about.ShowModal()
- def resetDefaults(self):
- """Just to reset the fonts back to the PlotCanvas defaults"""
- self.client.SetFont(
- wx.Font(10, wx.FONTFAMILY_SWISS, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL))
- self.client.SetFontSizeAxis(10)
- self.client.SetFontSizeLegend(7)
- self.client.setLogScale((False, False))
- self.client.SetXSpec('auto')
- self.client.SetYSpec('auto')
- def __test():
- class MyApp(wx.App):
- def OnInit(self):
- wx.InitAllImageHandlers()
- frame = TestFrame(None, -1, "PlotCanvas")
- # frame.Show(True)
- self.SetTopWindow(frame)
- return True
- app = MyApp(0)
- app.MainLoop()
- if __name__ == '__main__':
- __test()
|