Commit 48f82b90 authored by unknown's avatar unknown

Merge Complete

parents 94b5ddeb 97964293
# create a simple image slide show using the
# wx.PaintDC surface as a canvas and
# DrawBitmap(bitmap, x, y, bool transparent)
# Source: vegaseat
import wx
import os
import zipfile
import tempfile
import shutil
class MyFrame(wx.Frame):
def __init__(self, parent, mysize):
wx.Frame.__init__(self, parent, wx.ID_ANY, size=mysize)
self.SetBackgroundColour('black')
# milliseconds per frame
self.delay = 60
# number of loops
self.loops = 1
zipfilename = 'images/out.3dlp.zip'
if not zipfile.is_zipfile(zipfilename):
raise Exception(zipfilename + " is not a zip file!")
zip = zipfile.ZipFile(zipfilename, 'r')
self.mytmpdir = tempfile.mkdtemp()
zip.extractall(self.mytmpdir)
image_type = ".bmp"
image_dir = self.mytmpdir
file_list = []
self.name_list = []
for file in os.listdir(image_dir):
path = os.path.join(image_dir, file)
if os.path.isfile(path) and path.endswith(image_type):
# just the file name
self.name_list.append(file)
# full path name
file_list.append(path)
# create a list of image objects
self.image_list = []
for image_file in file_list:
self.image_list.append(wx.Bitmap(image_file))
# bind the panel to the paint event
wx.EVT_PAINT(self, self.onPaint)
def __del__(self):
if self.mytmpdir:
shutil.rmtree(self.mytmpdir)
def onPaint(self, event=None):
# this is the wxPython drawing surface/canvas
dc = wx.PaintDC(self)
while self.loops:
self.loops -= 1
for ix, bmp in enumerate(self.image_list):
# optionally show some image information
w, h = bmp.GetSize()
info = "%s %dx%d" % (self.name_list[ix], w, h)
self.SetTitle(info)
#self.SetSize((w,h))
# draw the image
dc.DrawBitmap(bmp, 0, 0, True)
wx.MilliSleep(self.delay)
# don't clear on fast slide shows to avoid flicker
if self.delay > 200:
dc.Clear()
app = wx.App()
width = 800
frameoffset = 35
height = 600 + frameoffset
MyFrame(None, (width, height)).Show()
app.MainLoop()
\ No newline at end of file
......@@ -30,7 +30,7 @@ class Graph(BufferedCanvas):
#super(Graph, self).__init__(parent, id, pos, size, style)
BufferedCanvas.__init__(self, parent, id)
self.SetSize(wx.Size(170, 100))
self.SetSize(wx.Size(150, 80))
self.extruder0temps = [0]
self.extruder0targettemps = [0]
......
......@@ -13,26 +13,73 @@
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
import wx,time
ID_ABOUT = 101
ID_EXIT = 110
class window(wx.Frame):
def __init__(self,f,size=(600,600),build_dimensions=[200,200,100,0,0,0],grid=(10,50),extrusion_width=0.5):
wx.Frame.__init__(self,None,title="Gcode view, shift to move view, mousewheel to set layer",size=(size[0],size[1]))
self.p=gviz(self,size=size,build_dimensions=build_dimensions,grid=grid,extrusion_width=extrusion_width)
# Set up a status bar for displaying info (Jezmy)
vbox = wx.BoxSizer(wx.VERTICAL)
toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL | wx.NO_BORDER)
toolbar.AddSimpleTool(1, wx.Image('./images/zoom_in.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom In [+]', '')
toolbar.AddSimpleTool(2, wx.Image('./images/zoom_out.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom Out [-]', '')
toolbar.AddSeparator()
toolbar.AddSimpleTool(3, wx.Image('./images/arrow_up.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Up a Layer [U]', '')
toolbar.AddSimpleTool(4, wx.Image('./images/arrow_down.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Down a Layer [D]', '')
toolbar.AddSimpleTool(5, wx.EmptyBitmap(16,16), 'Reset view', '')
toolbar.AddSeparator()
#toolbar.AddSimpleTool(5, wx.Image('./images/inject.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Insert Code at start of this layer', '')
toolbar.Realize()
vbox.Add(toolbar, 0, border=5)
self.SetSizer(vbox)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200,200,1.2), id=1)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200,200,1/1.2), id=2)
self.Bind(wx.EVT_TOOL, lambda x:self.p.layerup(), id=3)
self.Bind(wx.EVT_TOOL, lambda x:self.p.layerdown(), id=4)
self.Bind(wx.EVT_TOOL, self.resetview, id=5)
#self.Bind(wx.EVT_TOOL, lambda x:self.p.inject(), id=5)
self.CreateStatusBar(1);
self.SetStatusText("Layer number and Z position show here when you scroll");
#self.bu=wx.Button(self.p,-1,"U",pos=(0,100),size=(40,140))
#self.bd=wx.Button(self.p,-1,"D",pos=(0,140),size=(40,140))
#self.bi=wx.Button(self.p,-1,"+",pos=(40,100),size=(40,140))
#self.bo=wx.Button(self.p,-1,"-",pos=(40,140),size=(40,140))
#self.bs=wx.Button(self.p, -1, "Inject", pos=(85, 103), size=(50, 20))
#self.bu.SetToolTip(wx.ToolTip("Move up one layer"))
#self.bd.SetToolTip(wx.ToolTip("Move down one layer"))
#self.bi.SetToolTip(wx.ToolTip("Zoom view in"))
#self.bo.SetToolTip(wx.ToolTip("Zoom view out"))
#self.bs.SetToolTip(wx.ToolTip("Insert Code at start of this layer"))
#self.bu.Bind(wx.EVT_BUTTON,lambda x:self.p.layerup())
#self.bd.Bind(wx.EVT_BUTTON,lambda x:self.p.layerdown())
#self.bi.Bind(wx.EVT_BUTTON,lambda x:self.p.zoom(200,200,1.2))
#self.bo.Bind(wx.EVT_BUTTON,lambda x:self.p.zoom(200,200,1/1.2))
#self.bs.Bind(wx.EVT_BUTTON,lambda x:self.p.inject())
s=time.time()
#print time.time()-s
self.initpos=[0,0]
self.p.Bind(wx.EVT_KEY_DOWN,self.key)
#self.bu.Bind(wx.EVT_KEY_DOWN,self.key)
#self.bd.Bind(wx.EVT_KEY_DOWN,self.key)
#self.bi.Bind(wx.EVT_KEY_DOWN,self.key)
#self.bo.Bind(wx.EVT_KEY_DOWN,self.key)
self.Bind(wx.EVT_KEY_DOWN,self.key)
self.p.Bind(wx.EVT_MOUSEWHEEL,self.zoom)
self.Bind(wx.EVT_MOUSEWHEEL,self.zoom)
self.p.Bind(wx.EVT_MOUSE_EVENTS,self.mouse)
self.Bind(wx.EVT_MOUSE_EVENTS,self.mouse)
def resetview(self,event):
self.p.translate=[0.0,0.0]
self.p.scale=self.p.basescale
self.p.zoom(0,0,1.0)
def mouse(self,event):
if event.ButtonUp(wx.MOUSE_BTN_LEFT):
if(self.initpos is not None):
......@@ -51,21 +98,33 @@ class window(wx.Frame):
else:
event.Skip()
def key(self, event):
# Keycode definitions
kup=[85, 315] # Up keys
kdo=[68, 317] # Down Keys
kzi=[388, 316, 61] # Zoom In Keys
kzo=[390, 314, 45] # Zoom Out Keys
x=event.GetKeyCode()
if event.ShiftDown():
#print "Key event - "+str(x)
#if event.ShiftDown():
cx,cy=self.p.translate
if x==wx.WXK_UP:
self.p.zoom(cx,cy,1.2)
if x==wx.WXK_DOWN:
self.p.zoom(cx,cy,1/1.2)
else:
if x==wx.WXK_UP:
# if x==wx.WXK_UP:
# self.p.zoom(cx,cy,1.2)
# if x==wx.WXK_DOWN:
# self.p.zoom(cx,cy,1/1.2)
#else:
# if x==wx.WXK_UP:
# self.p.layerup()
# if x==wx.WXK_DOWN:
# self.p.layerdown()
if x in kup:
self.p.layerup()
if x==wx.WXK_DOWN:
if x in kdo:
self.p.layerdown()
#print x
if x in kzi:
self.p.zoom(cx,cy,1.2)
if x in kzo:
self.p.zoom(cx, cy, 1/1.2)
#print p.lines.keys()
def zoom(self, event):
......@@ -111,6 +170,11 @@ class gviz(wx.Panel):
self.dirty=1
self.blitmap=wx.EmptyBitmap(self.GetClientSize()[0],self.GetClientSize()[1],-1)
def inject(self):
#import pdb; pdb.set_trace()
print"Inject code here..."
print "Layer "+str(self.layerindex +1)+" - Z = "+str(self.layers[self.layerindex])+" mm"
def clear(self):
self.lastpos=[0,0,0,0,0,0,0]
self.lines={}
......@@ -259,6 +323,8 @@ class gviz(wx.Panel):
def _readgcode():
target=self.lastpos[:]
target[5]=0.0
target[6]=0.0
if hilight:
target=self.hilightpos[:]
for i in gcode:
......
......@@ -248,7 +248,7 @@ class stlwin(wx.Frame):
self.SetIcon(wx.Icon("plater.ico", wx.BITMAP_TYPE_ICO))
self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel = wx.Panel(self, -1, size=(150, 600), pos=(0, 0))
self.panel.SetBackgroundColour((10, 10, 10))
#self.panel.SetBackgroundColour((10, 10, 10))
self.l = wx.ListBox(self.panel, size=(300, 180), pos=(0, 30))
self.cl = wx.Button(self.panel, label=_("Clear"), pos=(0, 205))
self.lb = wx.Button(self.panel, label=_("Load"), pos=(0, 0))
......@@ -274,7 +274,7 @@ class stlwin(wx.Frame):
self.ab.Bind(wx.EVT_BUTTON, self.autoplate)
self.basedir = "."
self.models = {}
self.SetBackgroundColour((10, 10, 10))
#self.SetBackgroundColour((10, 10, 10))
self.mainsizer.Add(self.panel)
#self.mainsizer.AddSpacer(10)
if glview:
......
......@@ -226,6 +226,7 @@ class printcore():
pass
while(self.printing and self.printer and self.online):
self._sendnext()
self.sentlines={}
self.log=[]
self.sent=[]
if self.endcb is not None:
......@@ -248,7 +249,6 @@ class printcore():
self._send(self.sentlines[self.resendfrom],self.resendfrom,False)
self.resendfrom+=1
return
self.sentlines={}
self.resendfrom=-1
for i in self.priqueue[:]:
self._send(i)
......
......@@ -15,30 +15,22 @@
import xml.etree.ElementTree
import wx
import time
def parsesvg(name):
et= xml.etree.ElementTree.ElementTree(file=name)
zlast=0
zdiff=0
ol=[]
for i in et.findall("{http://www.w3.org/2000/svg}g")[0].findall("{http://www.w3.org/2000/svg}g"):
z=float(i.get('id').split("z:")[-1])
zdiff=z-zlast
zlast=z
path=i.find('{http://www.w3.org/2000/svg}path')
ol+=[(path.get("d").split("z"))[:-1]]
return ol,zdiff
import os
import zipfile
import tempfile
import shutil
import svg.document as wxpsvgdocument
import imghdr
class dispframe(wx.Frame):
def __init__(self, parent, title, res=(1600,1200),printer=None):
def __init__(self, parent, title, res=(800, 600), printer=None):
wx.Frame.__init__(self, parent=parent, title=title)
self.p=printer
self.pic=wx.StaticBitmap(self)
self.bitmap=wx.EmptyBitmap(*res)
self.bbitmap=wx.EmptyBitmap(*res)
dc=wx.MemoryDC()
self.p = printer
self.pic = wx.StaticBitmap(self)
self.bitmap = wx.EmptyBitmap(*res)
self.bbitmap = wx.EmptyBitmap(*res)
self.slicer = 'Skeinforge'
dc = wx.MemoryDC()
dc.SelectObject(self.bbitmap)
dc.SetBackground(wx.Brush("black"))
dc.Clear()
......@@ -46,134 +38,216 @@ class dispframe(wx.Frame):
self.SetBackgroundColour("black")
self.pic.Hide()
self.pen=wx.Pen("white")
self.brush=wx.Brush("white")
self.pen = wx.Pen("white")
self.brush = wx.Brush("white")
self.SetDoubleBuffered(True)
self.Show()
def drawlayer(self,svg):
def drawlayer(self, image):
try:
dc=wx.MemoryDC()
dc = wx.MemoryDC()
dc.SelectObject(self.bitmap)
dc.SetBackground(wx.Brush("black"))
dc.Clear()
dc.SetPen(self.pen)
dc.SetBrush(self.brush)
for i in svg:
#print i
points=[wx.Point(*map(lambda x:int(round(float(x)*self.scale)),j.strip().split())) for j in i.strip().split("M")[1].split("L")]
dc.DrawPolygon(points,self.size[0]/2,self.size[1]/2)
dc.SelectObject(wx.NullBitmap)
if self.slicer == 'Skeinforge':
for i in image:
#print i
points = [wx.Point(*map(lambda x:int(round(float(x) * self.scale)), j.strip().split())) for j in i.strip().split("M")[1].split("L")]
dc.DrawPolygon(points, self.size[0] / 2, self.size[1] / 2)
elif self.slicer == 'Slic3r':
gc = wx.GraphicsContext_Create(dc)
gc.Translate(*self.offset)
gc.Scale(self.scale, self.scale)
wxpsvgdocument.SVGDocument(image).render(gc)
elif self.slicer == 'bitmap':
dc.DrawBitmap(image, self.offset[0], -self.offset[1], True)
else:
raise Exception(self.slicer + " is an unknown method.")
self.pic.SetBitmap(self.bitmap)
self.pic.Show()
self.Refresh()
#self.pic.SetBitmap(self.bitmap)
except:
raise
pass
def showimgdelay(self,image):
def showimgdelay(self, image):
self.drawlayer(image)
self.pic.Show()
self.Refresh()
# time.sleep(self.interval)
#self.pic.Hide()
self.Refresh()
if self.p!=None and self.p.online:
if self.p != None and self.p.online:
self.p.send_now("G91")
self.p.send_now("G1 Z%f F300"%(self.thickness,))
self.p.send_now("G1 Z%f F300" % (self.thickness,))
self.p.send_now("G90")
def nextimg(self,event):
#print "b"
if self.index<len(self.layers):
i=self.index
#print self.layers[i]
def nextimg(self, event):
if self.index < len(self.layers):
i = self.index
print i
wx.CallAfter(self.showimgdelay,self.layers[i])
wx.FutureCall(1000*self.interval,self.pic.Hide)
self.index+=1
wx.CallAfter(self.showimgdelay, self.layers[i])
wx.FutureCall(1000 * self.interval, self.pic.Hide)
self.index += 1
else:
print "end"
wx.CallAfter(self.pic.Hide)
wx.CallAfter(self.Refresh)
wx.CallAfter(self.ShowFullScreen,0)
wx.CallAfter(self.ShowFullScreen, 0)
wx.CallAfter(self.timer.Stop)
def present(self,layers,interval=0.5,pause=0.2,thickness=0.4,scale=20,size=(800,600)):
def present(self, layers, interval=0.5, pause=0.2, thickness=0.4, scale=20, size=(800, 600), offset=(0, 0)):
wx.CallAfter(self.pic.Hide)
wx.CallAfter(self.Refresh)
self.layers=layers
self.scale=scale
self.thickness=thickness
self.index=0
self.size=size
self.interval=interval
self.timer=wx.Timer(self,1)
self.timer.Bind(wx.EVT_TIMER,self.nextimg)
self.Bind(wx.EVT_TIMER,self.nextimg)
self.timer.Start(1000*interval+1000*pause)
#print "x"
self.layers = layers
self.scale = scale
self.thickness = thickness
self.index = 0
self.size = (size[0] + offset[0], size[1] + offset[1])
self.interval = interval
self.offset = offset
self.timer = wx.Timer(self, 1)
self.timer.Bind(wx.EVT_TIMER, self.nextimg)
self.Bind(wx.EVT_TIMER, self.nextimg)
self.timer.Start(1000 * interval + 1000 * pause)
class setframe(wx.Frame):
def __init__(self,parent,printer=None):
wx.Frame.__init__(self,parent,title="Projector setup")
self.f=dispframe(None,"",printer=printer)
self.panel=wx.Panel(self)
def __init__(self, parent, printer=None):
wx.Frame.__init__(self, parent, title="Projector setup")
self.f = dispframe(None, "", printer=printer)
self.panel = wx.Panel(self)
self.panel.SetBackgroundColour("orange")
self.bload=wx.Button(self.panel,-1,"Load",pos=(0,0))
self.bload.Bind(wx.EVT_BUTTON,self.loadfile)
wx.StaticText(self.panel,-1,"Layer:",pos=(0,30))
wx.StaticText(self.panel,-1,"mm",pos=(130,30))
self.thickness=wx.TextCtrl(self.panel,-1,"0.5",pos=(50,30))
wx.StaticText(self.panel,-1,"Exposure:",pos=(0,60))
wx.StaticText(self.panel,-1,"s",pos=(130,60))
self.interval=wx.TextCtrl(self.panel,-1,"0.5",pos=(50,60))
wx.StaticText(self.panel,-1,"Blank:",pos=(0,90))
wx.StaticText(self.panel,-1,"s",pos=(130,90))
self.delay=wx.TextCtrl(self.panel,-1,"0.5",pos=(50,90))
wx.StaticText(self.panel,-1,"Scale:",pos=(0,120))
wx.StaticText(self.panel,-1,"x",pos=(130,120))
self.scale=wx.TextCtrl(self.panel,-1,"5",pos=(50,120))
wx.StaticText(self.panel,-1,"X:",pos=(160,30))
self.X=wx.TextCtrl(self.panel,-1,"1024",pos=(180,30))
wx.StaticText(self.panel,-1,"Y:",pos=(160,60))
self.Y=wx.TextCtrl(self.panel,-1,"768",pos=(180,60))
self.bload=wx.Button(self.panel,-1,"Present",pos=(0,150))
self.bload.Bind(wx.EVT_BUTTON,self.startdisplay)
self.bload = wx.Button(self.panel, -1, "Load", pos=(0, 0))
self.bload.Bind(wx.EVT_BUTTON, self.loadfile)
wx.StaticText(self.panel, -1, "Layer:", pos=(0, 30))
wx.StaticText(self.panel, -1, "mm", pos=(130, 30))
self.thickness = wx.TextCtrl(self.panel, -1, "0.5", pos=(50, 30))
wx.StaticText(self.panel, -1, "Exposure:", pos=(0, 60))
wx.StaticText(self.panel, -1, "s", pos=(130, 60))
self.interval = wx.TextCtrl(self.panel, -1, "0.5", pos=(50, 60))
wx.StaticText(self.panel, -1, "Blank:", pos=(0, 90))
wx.StaticText(self.panel, -1, "s", pos=(130, 90))
self.delay = wx.TextCtrl(self.panel, -1, "0.5", pos=(50, 90))
wx.StaticText(self.panel, -1, "Scale:", pos=(0, 120))
wx.StaticText(self.panel, -1, "x", pos=(130, 120))
self.scale = wx.TextCtrl(self.panel, -1, "5", pos=(50, 120))
wx.StaticText(self.panel, -1, "X:", pos=(160, 30))
self.X = wx.TextCtrl(self.panel, -1, "1024", pos=(210, 30))
wx.StaticText(self.panel, -1, "Y:", pos=(160, 60))
self.Y = wx.TextCtrl(self.panel, -1, "768", pos=(210, 60))
wx.StaticText(self.panel, -1, "OffsetX:", pos=(160, 90))
self.offsetX = wx.TextCtrl(self.panel, -1, "50", pos=(210, 90))
wx.StaticText(self.panel, -1, "OffsetY:", pos=(160, 120))
self.offsetY = wx.TextCtrl(self.panel, -1, "50", pos=(210, 120))
self.bload = wx.Button(self.panel, -1, "Present", pos=(0, 150))
self.bload.Bind(wx.EVT_BUTTON, self.startdisplay)
wx.StaticText(self.panel, -1, "Fullscreen:", pos=(160, 150))
self.fullscreen = wx.CheckBox(self.panel, -1, pos=(220, 150))
self.fullscreen.SetValue(True)
self.Show()
def loadfile(self,event):
dlg=wx.FileDialog(self,("Open file to print"),style=wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard(("Skeinforge svg files (;*.svg;*.SVG;)"))
def __del__(self):
if hasattr(self, 'image_dir') and self.image_dir != '':
shutil.rmtree(self.image_dir)
def parsesvg(self, name):
et = xml.etree.ElementTree.ElementTree(file=name)
#xml.etree.ElementTree.dump(et)
slicer = 'Slic3r' if et.getroot().find('{http://www.w3.org/2000/svg}metadata') == None else 'Skeinforge'
zlast = 0
zdiff = 0
ol = []
if (slicer == 'Slic3r'):
height = et.getroot().get('height')
width = et.getroot().get('width')
for i in et.findall("{http://www.w3.org/2000/svg}g"):
z = float(i.get('{http://slic3r.org/namespaces/slic3r}z'))
zdiff = z - zlast
zlast = z
svgSnippet = xml.etree.ElementTree.Element('{http://www.w3.org/2000/svg}svg')
svgSnippet.set('height', height + 'mm')
svgSnippet.set('width', width + 'mm')
svgSnippet.set('viewBox', '0 0 ' + height + ' ' + width)
svgSnippet.append(i)
ol += [svgSnippet]
else :
for i in et.findall("{http://www.w3.org/2000/svg}g")[0].findall("{http://www.w3.org/2000/svg}g"):
z = float(i.get('id').split("z:")[-1])
zdiff = z - zlast
zlast = z
path = i.find('{http://www.w3.org/2000/svg}path')
ol += [(path.get("d").split("z"))[:-1]]
return ol, zdiff, slicer
def parse3DLPzip(self, name):
if not zipfile.is_zipfile(name):
raise Exception(name + " is not a zip file!")
acceptedImageTypes = ['gif','tiff','jpg','jpeg','bmp','png']
zipFile = zipfile.ZipFile(name, 'r')
self.image_dir = tempfile.mkdtemp()
zipFile.extractall(self.image_dir)
ol = []
for f in os.listdir(self.image_dir):
path = os.path.join(self.image_dir, f)
if os.path.isfile(path) and imghdr.what(path) in acceptedImageTypes:
ol.append(wx.Bitmap(path))
return ol, -1, "bitmap"
def loadfile(self, event):
dlg = wx.FileDialog(self, ("Open file to print"), style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard(("Slic3r or Skeinforge svg files (;*.svg;*.SVG;);3DLP Zip (;*.3dlp.zip;)"))
if(dlg.ShowModal() == wx.ID_OK):
name=dlg.GetPath()
import os
name = dlg.GetPath()
if not(os.path.exists(name)):
self.status.SetStatusText(("File not found!"))
return
layers=parsesvg(name)
print "Layer thickness detected:",layers[1], "mm"
print len(layers[0]), "layers found, total height", layers[1]*len(layers[0]), "mm"
if name.endswith(".3dlp.zip"):
layers = self.parse3DLPzip(name)
layerHeight = float(self.thickness.GetValue())
else:
layers = self.parsesvg(name)
layerHeight = layers[1]
self.thickness.SetValue(str(layers[1]))
self.layers=layers
print "Layer thickness detected:", layerHeight, "mm"
print len(layers[0]), "layers found, total height", layerHeight * len(layers[0]), "mm"
self.layers = layers
self.f.slicer = layers[2]
dlg.Destroy()
def startdisplay(self,event):
def startdisplay(self, event):
self.f.Raise()
if (self.fullscreen.GetValue()):
self.f.ShowFullScreen(1)
l=self.layers[0][:]
#l=list(reversed(l))
self.f.present(l,thickness=float(self.thickness.GetValue()),interval=float(self.interval.GetValue()),scale=float(self.scale.GetValue()),pause=float(self.delay.GetValue()), size=(float(self.X.GetValue()),float(self.Y.GetValue())))
if __name__=="__main__":
a=wx.App()
l = self.layers[0][:]
self.f.present(l,
thickness=float(self.thickness.GetValue()),
interval=float(self.interval.GetValue()),
scale=float(self.scale.GetValue()),
pause=float(self.delay.GetValue()),
size=(float(self.X.GetValue()), float(self.Y.GetValue())),
offset=(float(self.offsetX.GetValue()), float(self.offsetY.GetValue())))
if __name__ == "__main__":
a = wx.App()
setframe(None).Show()
a.MainLoop()
......@@ -145,7 +145,7 @@ def estimate_duration(g):
y = get_coordinate_value("Y", parts[1:])
if y is None: y=lasty
z = get_coordinate_value("Z", parts[1:])
if z is None: z=lastz
if (z is None) or (z<lastz): z=lastz # Do not increment z if it's below the previous (Lift z on move fix)
e = get_coordinate_value("E", parts[1:])
if e is None: e=laste
f = get_coordinate_value("F", parts[1:])
......@@ -201,6 +201,7 @@ class Settings:
self.e_feedrate = 300
self.slicecommand="python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s"
self.sliceoptscommand="python skeinforge/skeinforge_application/skeinforge.py"
self.final_command = ""
def _set(self,key,value):
try:
......@@ -274,6 +275,7 @@ class pronsole(cmd.Cmd):
self.helpdict["temperature_pla"] = _("Extruder temp for PLA (default: 185 deg C)")
self.helpdict["xy_feedrate"] = _("Feedrate for Control Panel Moves in X and Y (default: 3000mm/min)")
self.helpdict["z_feedrate"] = _("Feedrate for Control Panel Moves in Z (default: 200mm/min)")
self.helpdict["final_command"] = _("Executable to run when the print is finished")
self.commandprefixes='MGT$'
def set_temp_preset(self,key,value):
......@@ -1114,7 +1116,7 @@ class pronsole(cmd.Cmd):
if(self.sdprinting):
self.p.send_now("M27")
time.sleep(interval)
print (self.tempreadings.replace("\r","").replace("T","Hotend").replace("B","Bed").replace("\n","").replace("ok ",""))
#print (self.tempreadings.replace("\r","").replace("T","Hotend").replace("B","Bed").replace("\n","").replace("ok ",""))
if(self.p.printing):
print "Print progress: ", 100*float(self.p.queueindex)/len(self.p.mainqueue), "%"
......@@ -1151,14 +1153,14 @@ class pronsole(cmd.Cmd):
import shlex
if(settings):
param = self.expandcommand(self.settings.sliceoptscommand).replace("\\","\\\\").encode()
print "Entering skeinforge settings: ",param
print "Entering slicer settings: ",param
subprocess.call(shlex.split(param))
else:
param = self.expandcommand(self.settings.slicecommand).encode()
print "Slicing: ",param
params=[i.replace("$s",l[0]).replace("$o",l[0].replace(".stl","_export.gcode").replace(".STL","_export.gcode")).encode() for i in shlex.split(param.replace("\\","\\\\").encode())]
subprocess.call(params)
print "Loading skeined file."
print "Loading sliced file."
self.do_load(l[0].replace(".stl","_export.gcode"))
except Exception,e:
print "Skeinforge execution failed: ",e
......@@ -1174,10 +1176,10 @@ class pronsole(cmd.Cmd):
return glob.glob("*/")+glob.glob("*.stl")
def help_skein(self):
print "Creates a gcode file from an stl model using skeinforge (with tab-completion)"
print "Creates a gcode file from an stl model using the slicer (with tab-completion)"
print "skein filename.stl - create gcode file"
print "skein filename.stl view - create gcode file and view using skeiniso"
print "skein set - adjust skeinforge settings"
print "skein set - adjust slicer settings"
def do_home(self,l):
......
......@@ -38,6 +38,9 @@ StringIO=cStringIO
thread=threading.Thread
winsize=(800,500)
layerindex=0
global buttonSize
buttonSize = (70, 25) # Define sizes for the buttons on top rows
if os.name=="nt":
winsize=(800,530)
try:
......@@ -116,10 +119,10 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
ycol=(180,180,255)
zcol=(180,255,180)
self.cpbuttons=[
[_("Motors off"),("M84"),(1,0),(250,250,250),(1,2)],
[_("Check temp"),("M105"),(3,5),(225,200,200),(1,3)],
[_("Extrude"),("extrude"),(5,0),(225,200,200),(1,2)],
[_("Reverse"),("reverse"),(6,0),(225,200,200),(1,2)],
[_("Motors off"),("M84"),None,(250,250,250),0, _("Switch all motors off")],
[_("Check temp"),("M105"),(2,5),(225,200,200),(1,1), _("Check current hotend temperature")],
[_("Extrude"),("extrude"),(4,0),(225,200,200),(1,2), _("Advance extruder by set length")],
[_("Reverse"),("reverse"),(5,0),(225,200,200),(1,2), _("Reverse extruder by set length")],
]
self.custombuttons=[]
self.btndict={}
......@@ -178,10 +181,15 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
wx.CallAfter(self.pausebtn.Disable)
wx.CallAfter(self.printbtn.SetLabel,_("Print"))
import shlex
param = self.settings.final_command
pararray=[i.replace("$s",str(self.filename)).replace("$t", str(time.strftime('%H:%M:%S', time.gmtime(int(time.time()-self.starttime+self.extra_print_time))))).encode() for i in shlex.split(param.replace("\\","\\\\").encode())]
self.finalp=subprocess.Popen(pararray,stderr=subprocess.STDOUT,stdout=subprocess.PIPE)
def online(self):
print _("Printer is now online.")
self.connectbtn.SetLabel(_("Disconnect"))
self.connectbtn.SetToolTip(wx.ToolTip("Disconnect from the printer"))
self.connectbtn.Bind(wx.EVT_BUTTON,self.disconnect)
for i in self.printerControls:
......@@ -216,8 +224,8 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if("S" in line):
try:
temp=float(line.split("S")[1].split("*")[0])
self.hottgauge.SetTarget(temp)
self.graph.SetExtruder0TargetTemperature(temp)
#self.hottgauge.SetTarget(temp)
wx.CallAfter(self.graph.SetExtruder0TargetTemperature,temp)
except:
pass
try:
......@@ -228,8 +236,8 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if("S" in line):
try:
temp=float(line.split("S")[1].split("*")[0])
self.bedtgauge.SetTarget(temp)
self.graph.SetBedTargetTemperature(temp)
#self.bedtgauge.SetTarget(temp)
wx.CallAfter(self.graph.SetBedTargetTemperature,temp)
except:
pass
try:
......@@ -266,8 +274,8 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.p.send_now("M104 S"+l)
print _("Setting hotend temperature to %f degrees Celsius.") % f
self.hsetpoint=f
self.hottgauge.SetTarget(int(f))
self.graph.SetExtruder0TargetTemperature(int(f))
#self.hottgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetExtruder0TargetTemperature,int(f))
if f>0:
wx.CallAfter(self.htemp.SetValue,l)
self.set("last_temperature",str(f))
......@@ -288,7 +296,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
else:
print _("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0.")
except Exception,x:
print _("You must enter a temperature. (%s)" % (repr(x),));
print _("You must enter a temperature. (%s)" % (repr(x),))
if webavail:
self.webInterface.AddLog("You must enter a temperature. (%s)" % (repr(x),))
......@@ -305,8 +313,8 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.p.send_now("M140 S"+l)
print _("Setting bed temperature to %f degrees Celsius.") % f
self.bsetpoint=f
self.bedtgauge.SetTarget(int(f))
self.graph.SetBedTargetTemperature(int(f))
#self.bedtgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetBedTargetTemperature,int(f))
if f>0:
wx.CallAfter(self.btemp.SetValue,l)
self.set("last_bed_temperature",str(f))
......@@ -323,15 +331,15 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
wx.CallAfter(self.btemp.SetBackgroundColour,"white")
wx.CallAfter(self.btemp.Refresh)
else:
print _("Printer is not online.");
print _("Printer is not online.")
if webavail:
self.webInterface.AddLog("Printer is not online.")
else:
print _("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.");
print _("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.")
if webavail:
self.webInterface.AddLog("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.")
except:
print _("You must enter a temperature.");
print _("You must enter a temperature.")
if webavail:
self.webInterface.AddLog("You must enter a temperature.")
......@@ -352,7 +360,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if dialog.ShowModal()==wx.ID_YES:
self.delete_macro(macro_name)
return
print _("Cancelled.");
print _("Cancelled.")
if webavail:
self.webInterface.AddLog("Cancelled.")
return
......@@ -395,7 +403,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if(self.p.online):
projectlayer.setframe(self,self.p).Show()
else:
print _("Printer is not online.");
print _("Printer is not online.")
if webavail:
self.webInterface.AddLog("Printer is not online.")
......@@ -471,7 +479,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
print _("Name '%s' is being used by built-in command") % macro
return
elif len([c for c in macro if not c.isalnum() and c != "_"]):
print _("Macro name may contain only alphanumeric symbols and underscores");
print _("Macro name may contain only alphanumeric symbols and underscores")
if webavail:
self.webInterface.AddLog("Macro name may contain only alphanumeric symbols and underscores")
return
......@@ -522,62 +530,68 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
#lower section contains the rest of the window - manual controls, console, visualizations
#TOP ROW:
uts=self.uppertopsizer=wx.BoxSizer(wx.HORIZONTAL)
self.rescanbtn=wx.Button(self.panel,-1,_("Port"))
self.rescanbtn=wx.Button(self.panel,-1,_("Port"),size=buttonSize)
self.rescanbtn.SetToolTip(wx.ToolTip("Communication Settings\nClick to rescan ports"))
self.rescanbtn.Bind(wx.EVT_BUTTON,self.rescanports)
uts.Add(self.rescanbtn,0,wx.TOP|wx.LEFT,0)
self.serialport = wx.ComboBox(self.panel, -1,
choices=self.scanserial(),
style=wx.CB_DROPDOWN)
style=wx.CB_DROPDOWN, size=(100, 25))
self.serialport.SetToolTip(wx.ToolTip("Select Port Printer is connected to"))
self.rescanports()
uts.Add(self.serialport)
uts.Add(wx.StaticText(self.panel,-1,"@"),0,wx.RIGHT|wx.ALIGN_CENTER,0)
self.baud = wx.ComboBox(self.panel, -1,
choices=["2400", "9600", "19200", "38400", "57600", "115200", "250000"],
style=wx.CB_DROPDOWN)
style=wx.CB_DROPDOWN, size=(100, 25))
self.baud.SetToolTip(wx.ToolTip("Select Baud rate for printer communication"))
try:
self.baud.SetValue("115200")
self.baud.SetValue(str(self.settings.baudrate))
except:
pass
uts.Add(self.baud)
self.connectbtn=wx.Button(self.panel,-1,_("Connect"))
self.connectbtn=wx.Button(self.panel,-1,_("Connect"), size=buttonSize)
uts.Add(self.connectbtn)
self.connectbtn.SetToolTipString(_("Connect to the printer"))
self.connectbtn.SetToolTip(wx.ToolTip("Connect to the printer"))
self.connectbtn.Bind(wx.EVT_BUTTON,self.connect)
self.resetbtn=wx.Button(self.panel,-1,_("Reset"))
self.resetbtn=wx.Button(self.panel,-1,_("Reset"),style=wx.BU_EXACTFIT,size=(-1,buttonSize[1]))
self.resetbtn.Bind(wx.EVT_BUTTON,self.reset)
self.resetbtn.SetToolTip(wx.ToolTip("Reset the printer"))
uts.Add(self.resetbtn)
self.minibtn=wx.Button(self.panel,-1,_("Mini mode"))
self.minibtn.Bind(wx.EVT_BUTTON,self.toggleview)
#self.minibtn=wx.Button(self.panel,-1,_("Mini mode"),style=wx.BU_EXACTFIT)
#self.minibtn.Bind(wx.EVT_BUTTON,self.toggleview)
uts.Add((25,-1))
self.monitorbox=wx.CheckBox(self.panel,-1,_("Monitor Printer"))
uts.Add(self.monitorbox,0,wx.ALIGN_CENTER)
self.monitorbox.Bind(wx.EVT_CHECKBOX,self.setmonitor)
#uts.Add((25,-1))
uts.Add((15,-1),flag=wx.EXPAND)
uts.Add(self.minibtn,0,wx.ALIGN_CENTER)
#uts.Add((15,-1),flag=wx.EXPAND)
#uts.Add(self.minibtn,0,wx.ALIGN_CENTER)
#SECOND ROW
ubs=self.upperbottomsizer=wx.BoxSizer(wx.HORIZONTAL)
ubs=self.upperbottomsizer=uts#wx.BoxSizer(wx.HORIZONTAL)
self.loadbtn=wx.Button(self.panel,-1,_("Load file"))
self.loadbtn=wx.Button(self.panel,-1,_("Load file"),style=wx.BU_EXACTFIT,size=(-1,buttonSize[1]))
self.loadbtn.Bind(wx.EVT_BUTTON,self.loadfile)
self.loadbtn.SetToolTip(wx.ToolTip("Load a 3D model file"))
ubs.Add(self.loadbtn)
self.platebtn=wx.Button(self.panel,-1,_("Compose"))
self.platebtn=wx.Button(self.panel,-1,_("Compose"),style=wx.BU_EXACTFIT,size=(-1,buttonSize[1]))
self.platebtn.Bind(wx.EVT_BUTTON,self.plate)
self.platebtn.SetToolTip(wx.ToolTip("Simple Plater System"))
#self.printerControls.append(self.uploadbtn)
ubs.Add(self.platebtn)
self.sdbtn=wx.Button(self.panel,-1,_("SD"))
self.sdbtn=wx.Button(self.panel,-1,_("SD"),style=wx.BU_EXACTFIT,size=(-1,buttonSize[1]))
self.sdbtn.Bind(wx.EVT_BUTTON,self.sdmenu)
self.sdbtn.SetToolTip(wx.ToolTip("SD Card Printing"))
self.printerControls.append(self.sdbtn)
ubs.Add(self.sdbtn)
self.printbtn=wx.Button(self.panel,-1,_("Print"))
self.printbtn=wx.Button(self.panel,-1,_("Print"), size=buttonSize)
self.printbtn.Bind(wx.EVT_BUTTON,self.printfile)
self.printbtn.SetToolTip(wx.ToolTip("Start Printing Loaded File"))
self.printbtn.Disable()
ubs.Add(self.printbtn)
self.pausebtn=wx.Button(self.panel,-1,_("Pause"))
self.pausebtn=wx.Button(self.panel,-1,_("Pause"), size=buttonSize)
self.pausebtn.SetToolTip(wx.ToolTip("Pause Current Print"))
self.pausebtn.Bind(wx.EVT_BUTTON,self.pause)
ubs.Add(self.pausebtn)
#Right full view
......@@ -587,10 +601,12 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
lrs.Add(self.logbox,1,wx.EXPAND)
lbrs=wx.BoxSizer(wx.HORIZONTAL)
self.commandbox=wx.TextCtrl(self.panel,style = wx.TE_PROCESS_ENTER)
self.commandbox.SetToolTip(wx.ToolTip("Send commands to printer\n(Type 'help' for simple\nhelp function)"))
self.commandbox.Bind(wx.EVT_TEXT_ENTER,self.sendline)
#self.printerControls.append(self.commandbox)
lbrs.Add(self.commandbox,1)
self.sendbtn=wx.Button(self.panel,-1,_("Send"))
self.sendbtn=wx.Button(self.panel,-1,_("Send"),style=wx.BU_EXACTFIT)
self.sendbtn.SetToolTip(wx.ToolTip("Send Command to Printer"))
self.sendbtn.Bind(wx.EVT_BUTTON,self.sendline)
#self.printerControls.append(self.sendbtn)
lbrs.Add(self.sendbtn)
......@@ -598,72 +614,97 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
#left pane
lls=self.lowerlsizer=wx.GridBagSizer()
lls.Add(wx.StaticText(self.panel,-1,_("mm/min")),pos=(0,4),span=(1,4))
self.xyfeedc=wx.SpinCtrl(self.panel,-1,str(self.settings.xy_feedrate),min=0,max=50000,size=(70,-1))
lls.Add(wx.StaticText(self.panel,-1,_("XY:")),pos=(1,3),span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
lls.Add(self.xyfeedc,pos=(1,4),span=(1,2))
lls.Add(wx.StaticText(self.panel,-1,_("Z:")),pos=(1,6),span=(1,1), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.zfeedc=wx.SpinCtrl(self.panel,-1,str(self.settings.z_feedrate),min=0,max=50000,size=(70,-1))
lls.Add(self.zfeedc,pos=(1,7),span=(1,3))
llts=wx.BoxSizer(wx.HORIZONTAL)
#lls.Add(wx.StaticText(self.panel,-1,_("mm/min")),pos=(0,4),span=(1,4))
lls.Add(llts,pos=(0,0),span=(1,9))
#lls.Add((200,375))
self.xyb = XYButtons(self.panel, self.moveXY, self.homeButtonClicked)
lls.Add(self.xyb, pos=(2,0), span=(1,6), flag=wx.ALIGN_CENTER)
self.zb = ZButtons(self.panel, self.moveZ)
lls.Add(self.zb, pos=(2,7), span=(1,2), flag=wx.ALIGN_CENTER)
szbuttons=wx.GridBagSizer()
self.xyb = XYButtons(self.panel, self.moveXY, self.homeButtonClicked, self.spacebarAction, self.settings.bgcolor)
szbuttons.Add(self.xyb,pos=(0,1),flag=wx.ALIGN_CENTER)
self.zb = ZButtons(self.panel, self.moveZ, self.settings.bgcolor)
szbuttons.Add(self.zb,pos=(0,2),flag=wx.ALIGN_CENTER)
#lls.Add(self.zb, pos=(2,6), span=(1,1), flag=wx.ALIGN_CENTER)
wx.CallAfter(self.xyb.SetFocus)
lls.Add(szbuttons, pos=(1,0), span=(1,8), flag=wx.ALIGN_CENTER)
for i in self.cpbuttons:
btn=wx.Button(self.panel,-1,i[0])#)
btn=wx.Button(self.panel,-1,i[0],style=wx.BU_EXACTFIT)
btn.SetToolTip(wx.ToolTip(i[5]))
btn.SetBackgroundColour(i[3])
btn.SetForegroundColour("black")
btn.properties=i
btn.Bind(wx.EVT_BUTTON,self.procbutton)
self.btndict[i[1]]=btn
self.printerControls.append(btn)
if(i[2]==None):
if(i[4]==0):
llts.Add(btn)
else:
lls.Add(btn,pos=i[2],span=i[4])
self.xyfeedc=wx.SpinCtrl(self.panel,-1,str(self.settings.xy_feedrate),min=0,max=50000,size=(70,-1))
self.xyfeedc.SetToolTip(wx.ToolTip("Set Maximum Speed for X & Y axes (mm/min)"))
llts.Add(wx.StaticText(self.panel,-1,_("XY:")), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
llts.Add(self.xyfeedc)
llts.Add(wx.StaticText(self.panel,-1,_("mm/min Z:")), flag=wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
self.zfeedc=wx.SpinCtrl(self.panel,-1,str(self.settings.z_feedrate),min=0,max=50000,size=(70,-1))
self.zfeedc.SetToolTip(wx.ToolTip("Set Maximum Speed for Z axis (mm/min)"))
llts.Add(self.zfeedc,)
self.monitorbox=wx.CheckBox(self.panel,-1,_("Watch"))
self.monitorbox.SetToolTip(wx.ToolTip("Monitor Temperatures in Graph"))
lls.Add(self.monitorbox,pos=(2,6))
self.monitorbox.Bind(wx.EVT_CHECKBOX,self.setmonitor)
lls.Add(wx.StaticText(self.panel,-1,_("Heater:")),pos=(3,0),span=(1,1),flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
lls.Add(wx.StaticText(self.panel,-1,_("Heat")),pos=(2,0),span=(1,1),flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
htemp_choices=[self.temps[i]+" ("+i+")" for i in sorted(self.temps.keys(),key=lambda x:self.temps[x])]
self.settoff=wx.Button(self.panel,-1,_("Off"),size=(36,-1))
self.settoff=wx.Button(self.panel,-1,_("Off"),size=(36,-1),style=wx.BU_EXACTFIT)
self.settoff.SetToolTip(wx.ToolTip("Switch Hotend Off"))
self.settoff.Bind(wx.EVT_BUTTON,lambda e:self.do_settemp("off"))
self.printerControls.append(self.settoff)
lls.Add(self.settoff,pos=(3,1),span=(1,1))
lls.Add(self.settoff,pos=(2,1),span=(1,1))
if self.settings.last_temperature not in map(float,self.temps.values()):
htemp_choices = [str(self.settings.last_temperature)] + htemp_choices
self.htemp=wx.ComboBox(self.panel, -1,
choices=htemp_choices,style=wx.CB_DROPDOWN, size=(80,-1))
choices=htemp_choices,style=wx.CB_DROPDOWN, size=(70,-1))
self.htemp.SetToolTip(wx.ToolTip("Select Temperature for Hotend"))
self.htemp.Bind(wx.EVT_COMBOBOX,self.htemp_change)
lls.Add(self.htemp,pos=(3,2),span=(1,2))
self.settbtn=wx.Button(self.panel,-1,_("Set"),size=(38,-1))
lls.Add(self.htemp,pos=(2,2),span=(1,2))
self.settbtn=wx.Button(self.panel,-1,_("Set"),size=(38,-1),style=wx.BU_EXACTFIT)
self.settbtn.SetToolTip(wx.ToolTip("Switch Hotend On"))
self.settbtn.Bind(wx.EVT_BUTTON,self.do_settemp)
self.printerControls.append(self.settbtn)
lls.Add(self.settbtn,pos=(3,4),span=(1,1))
lls.Add(self.settbtn,pos=(2,4),span=(1,1))
lls.Add(wx.StaticText(self.panel,-1,_("Bed:")),pos=(4,0),span=(1,1),flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
lls.Add(wx.StaticText(self.panel,-1,_("Bed:")),pos=(3,0),span=(1,1),flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
btemp_choices=[self.bedtemps[i]+" ("+i+")" for i in sorted(self.bedtemps.keys(),key=lambda x:self.temps[x])]
self.setboff=wx.Button(self.panel,-1,_("Off"),size=(36,-1))
self.setboff=wx.Button(self.panel,-1,_("Off"),size=(36,-1),style=wx.BU_EXACTFIT)
self.setboff.SetToolTip(wx.ToolTip("Switch Heated Bed Off"))
self.setboff.Bind(wx.EVT_BUTTON,lambda e:self.do_bedtemp("off"))
self.printerControls.append(self.setboff)
lls.Add(self.setboff,pos=(4,1),span=(1,1))
lls.Add(self.setboff,pos=(3,1),span=(1,1))
if self.settings.last_bed_temperature not in map(float,self.bedtemps.values()):
btemp_choices = [str(self.settings.last_bed_temperature)] + btemp_choices
self.btemp=wx.ComboBox(self.panel, -1,
choices=btemp_choices,style=wx.CB_DROPDOWN, size=(80,-1))
choices=btemp_choices,style=wx.CB_DROPDOWN, size=(70,-1))
self.btemp.SetToolTip(wx.ToolTip("Select Temperature for Heated Bed"))
self.btemp.Bind(wx.EVT_COMBOBOX,self.btemp_change)
lls.Add(self.btemp,pos=(4,2),span=(1,2))
lls.Add(self.btemp,pos=(3,2),span=(1,2))
self.setbbtn=wx.Button(self.panel,-1,_("Set"),size=(38,-1))
self.setbbtn=wx.Button(self.panel,-1,_("Set"),size=(38,-1),style=wx.BU_EXACTFIT)
self.setbbtn.SetToolTip(wx.ToolTip("Switch Heated Bed On"))
self.setbbtn.Bind(wx.EVT_BUTTON,self.do_bedtemp)
self.printerControls.append(self.setbbtn)
lls.Add(self.setbbtn,pos=(4,4),span=(1,1))
lls.Add(self.setbbtn,pos=(3,4),span=(1,1))
self.btemp.SetValue(str(self.settings.last_bed_temperature))
self.htemp.SetValue(str(self.settings.last_temperature))
......@@ -687,29 +728,30 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
#lls.Add(self.btemp,pos=(4,1),span=(1,3))
#lls.Add(self.setbbtn,pos=(4,4),span=(1,2))
self.tempdisp=wx.StaticText(self.panel,-1,"")
lls.Add(self.tempdisp,pos=(4,5),span=(1,3))
self.edist=wx.SpinCtrl(self.panel,-1,"5",min=0,max=1000,size=(60,-1))
self.edist.SetBackgroundColour((225,200,200))
self.edist.SetForegroundColour("black")
lls.Add(self.edist,pos=(5,2),span=(1,1))
lls.Add(wx.StaticText(self.panel,-1,_("mm")),pos=(5,3),span=(1,1))
lls.Add(self.edist,pos=(4,2),span=(1,2))
lls.Add(wx.StaticText(self.panel,-1,_("mm")),pos=(4,4),span=(1,1))
self.edist.SetToolTip(wx.ToolTip("Amount to Extrude or Retract (mm)"))
self.efeedc=wx.SpinCtrl(self.panel,-1,str(self.settings.e_feedrate),min=0,max=50000,size=(60,-1))
self.efeedc.SetToolTip(wx.ToolTip("Extrude / Retract speed (mm/min)"))
self.efeedc.SetBackgroundColour((225,200,200))
self.efeedc.SetForegroundColour("black")
self.efeedc.Bind(wx.EVT_SPINCTRL,self.setfeeds)
lls.Add(self.efeedc,pos=(6,2),span=(1,1))
lls.Add(wx.StaticText(self.panel,-1,_("mm/min")),pos=(6,3),span=(1,1))
lls.Add(self.efeedc,pos=(5,2),span=(1,2))
lls.Add(wx.StaticText(self.panel,-1,_("mm/\nmin")),pos=(5,4),span=(1,1))
self.xyfeedc.Bind(wx.EVT_SPINCTRL,self.setfeeds)
self.zfeedc.Bind(wx.EVT_SPINCTRL,self.setfeeds)
self.zfeedc.SetBackgroundColour((180,255,180))
self.zfeedc.SetForegroundColour("black")
# lls.Add((10,0),pos=(0,11),span=(1,1))
self.hottgauge=TempGauge(self.panel,size=(200,24),title=_("Heater:"),maxval=230)
lls.Add(self.hottgauge,pos=(7,0),span=(1,4))
self.bedtgauge=TempGauge(self.panel,size=(200,24),title=_("Bed:"),maxval=130)
lls.Add(self.bedtgauge,pos=(8,0),span=(1,4))
#self.hottgauge=TempGauge(self.panel,size=(200,24),title=_("Heater:"),maxval=230)
#lls.Add(self.hottgauge,pos=(7,0),span=(1,4))
#self.bedtgauge=TempGauge(self.panel,size=(200,24),title=_("Bed:"),maxval=130)
#lls.Add(self.bedtgauge,pos=(8,0),span=(1,4))
#def scroll_setpoint(e):
# if e.GetWheelRotation()>0:
# self.do_settemp(str(self.hsetpoint+1))
......@@ -718,12 +760,14 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
#self.tgauge.Bind(wx.EVT_MOUSEWHEEL,scroll_setpoint)
self.graph = Graph(self.panel, wx.ID_ANY)
lls.Add(self.graph, pos=(5, 4), span=(4,4), flag=wx.ALIGN_LEFT)
lls.Add(self.graph, pos=(3,5), span=(3,3))
lls.Add(self.tempdisp,pos=(6,0),span=(1,1))
self.gviz=gviz.gviz(self.panel,(300,300),
build_dimensions=self.build_dimensions_list,
grid=(self.settings.preview_grid_step1,self.settings.preview_grid_step2),
extrusion_width=self.settings.preview_extrusion_width)
self.gviz.SetToolTip(wx.ToolTip("Click to examine / edit\n layers of loaded file"))
self.gviz.showall=1
try:
raise ""
......@@ -743,7 +787,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.uppersizer=wx.BoxSizer(wx.VERTICAL)
self.uppersizer.Add(self.uppertopsizer)
self.uppersizer.Add(self.upperbottomsizer)
#self.uppersizer.Add(self.upperbottomsizer)
self.lowersizer=wx.BoxSizer(wx.HORIZONTAL)
self.lowersizer.Add(lls)
......@@ -803,6 +847,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
def showwin(self,event):
if(self.f is not None):
self.gwindow.Show(True)
self.gwindow.SetToolTip(wx.ToolTip("Mousewheel zooms the display\nShift / Mousewheel scrolls layers"))
self.gwindow.Raise()
def setfeeds(self,e):
......@@ -840,9 +885,9 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
allcbs = []
ubs=self.upperbottomsizer
cs=self.centersizer
for item in ubs.GetChildren():
if hasattr(item.GetWindow(),"custombutton"):
allcbs += [(ubs,item.GetWindow())]
#for item in ubs.GetChildren():
# if hasattr(item.GetWindow(),"custombutton"):
# allcbs += [(ubs,item.GetWindow())]
for item in cs.GetChildren():
if hasattr(item.GetWindow(),"custombutton"):
allcbs += [(cs,item.GetWindow())]
......@@ -858,7 +903,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
for i in xrange(len(self.custombuttons)):
btndef = self.custombuttons[i]
try:
b=wx.Button(self.panel,-1,btndef[0])
b=wx.Button(self.panel,-1,btndef[0],style=wx.BU_EXACTFIT)
b.SetToolTip(wx.ToolTip(_("Execute command: ")+btndef[1]))
if len(btndef)>2:
b.SetBackgroundColour(btndef[2])
......@@ -867,7 +912,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
b.SetForegroundColour("#ffffff")
except:
if i == newbuttonbuttonindex:
self.newbuttonbutton=b=wx.Button(self.panel,-1,"+",size=(19,18))
self.newbuttonbutton=b=wx.Button(self.panel,-1,"+",size=(19,18),style=wx.BU_EXACTFIT)
#b.SetFont(wx.Font(12,wx.FONTFAMILY_SWISS,wx.FONTSTYLE_NORMAL,wx.FONTWEIGHT_BOLD))
b.SetForegroundColour("#4444ff")
b.SetToolTip(wx.ToolTip(_("click to add new custom button")))
......@@ -882,14 +927,14 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
#else:
# b.Bind(wx.EVT_BUTTON,lambda e:e.Skip())
self.custombuttonbuttons.append(b)
if i<4:
ubs.Add(b)
else:
cs.Add(b,pos=((i-4)/3,(i-4)%3))
#if i<4:
# ubs.Add(b)
#else:
cs.Add(b,pos=((i)/4,(i)%4))
self.topsizer.Layout()
def help_button(self):
print _('Defines custom button. Usage: button <num> "title" [/c "colour"] command');
print _('Defines custom button. Usage: button <num> "title" [/c "colour"] command')
if webavail:
self.webInterface.AddLog('Defines custom button. Usage: button <num> "title" [/c "colour"] command')
......@@ -913,7 +958,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
pass
command=argstr.strip()
if num<0 or num>=64:
print _("Custom button number should be between 0 and 63");
print _("Custom button number should be between 0 and 63")
if webavail:
self.webInterface.AddLog("Custom button number should be between 0 and 63")
return
......@@ -948,18 +993,6 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
def cbutton_edit(self,e,button=None):
bedit=ButtonEdit(self)
def okhandler(event):
if event.GetId()==wx.ID_OK:
if n==len(self.custombuttons):
self.custombuttons+=[None]
self.custombuttons[n]=[bedit.name.GetValue().strip(),bedit.command.GetValue().strip()]
if bedit.color.GetValue().strip()!="":
self.custombuttons[n]+=[bedit.color.GetValue()]
self.cbutton_save(n,self.custombuttons[n])
bedit.Destroy()
self.cbuttons_reload()
bedit.Bind(wx.EVT_BUTTON,okhandler)
if button is not None:
n = button.custombutton
bedit.name.SetValue(button.properties[0])
......@@ -978,8 +1011,15 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
n = len(self.custombuttons)
while n>0 and self.custombuttons[n-1] is None:
n -= 1
bedit.Show()
if bedit.ShowModal()==wx.ID_OK:
if n==len(self.custombuttons):
self.custombuttons+=[None]
self.custombuttons[n]=[bedit.name.GetValue().strip(),bedit.command.GetValue().strip()]
if bedit.color.GetValue().strip()!="":
self.custombuttons[n]+=[bedit.color.GetValue()]
self.cbutton_save(n,self.custombuttons[n])
bedit.Destroy()
self.cbuttons_reload()
def cbutton_remove(self,e,button):
n = button.custombutton
......@@ -1049,7 +1089,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if self.upperbottomsizer.GetItem(self.newbuttonbutton) is not None:
self.upperbottomsizer.SetItemMinSize(self.newbuttonbutton,obj.GetSize())
self.topsizer.Layout()
self.dragging = wx.Button(self.panel,-1,obj.GetLabel())
self.dragging = wx.Button(self.panel,-1,obj.GetLabel(),style=wx.BU_EXACTFIT)
self.dragging.SetBackgroundColour(obj.GetBackgroundColour())
self.dragging.SetForegroundColour(obj.GetForegroundColour())
self.dragging.sourcebutton = obj
......@@ -1149,16 +1189,26 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.onecmd('home Z')
if corner == 3: # lower-left
self.onecmd('home')
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
def moveXY(self, x, y):
if x != 0:
self.onecmd('move X %s' % x)
if y != 0:
self.onecmd('move Y %s' % y)
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
def moveZ(self, z):
if z != 0:
self.onecmd('move Z %s' % z)
# When user clicks on the Z control, the XY control no longer gets spacebar/repeat signals
self.xyb.clearRepeat()
def spacebarAction(self):
self.zb.repeatLast()
self.xyb.repeatLast()
def procbutton(self,e):
try:
......@@ -1169,7 +1219,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.onecmd(e.GetEventObject().properties[1])
self.cur_button=None
except:
print _("event object missing");
print _("event object missing")
if webavail:
self.webInterface.AddLog("event object missing")
self.cur_button=None
......@@ -1201,16 +1251,16 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.monitor_interval=float(l)
wx.CallAfter(self.monitorbox.SetValue,self.monitor_interval>0)
except:
print _("Invalid period given.");
print _("Invalid period given.")
if webavail:
self.webInterface.AddLog("Invalid period given.")
self.setmonitor(None)
if self.monitor:
print _("Monitoring printer.");
print _("Monitoring printer.")
if webavail:
self.webInterface.AddLog("Monitoring printer.")
else:
print _("Done monitoring.");
print _("Done monitoring.")
if webavail:
self.webInterface.AddLog("Done monitoring.")
......@@ -1218,9 +1268,9 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
def setmonitor(self,e):
self.monitor=self.monitorbox.GetValue()
if self.monitor:
self.graph.StartPlotting(1000)
wx.CallAfter(self.graph.StartPlotting,1000)
else:
self.graph.StopPlotting()
wx.CallAfter(self.graph.StopPlotting)
......@@ -1241,19 +1291,19 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
try:
while(self.statuscheck):
string=""
if(self.p.online):
string+=_("Printer is online. ")
try:
string+=_("Loaded ")+os.path.split(self.filename)[1]+" "
except:
pass
string+=(self.tempreport.replace("\r","").replace("T:",_("Hotend") + ":").replace("B:",_("Bed") + ":").replace("\n","").replace("ok ",""))+" "
#if(self.p.online):
# string+=_("Printer is online. ")
#try:
# string+=_("Loaded ")+os.path.split(self.filename)[1]+" "
#except:
# pass
#string+=(self.tempreport.replace("\r","").replace("T:",_("Hotend") + ":").replace("B:",_("Bed") + ":").replace("\n","").replace("ok ",""))+" "
wx.CallAfter(self.tempdisp.SetLabel,self.tempreport.strip().replace("ok ",""))
try:
self.hottgauge.SetValue(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
self.graph.SetExtruder0Temperature(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
self.bedtgauge.SetValue(float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
self.graph.SetBedTemperature(float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
#self.hottgauge.SetValue(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
wx.CallAfter(self.graph.SetExtruder0Temperature,float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
#self.bedtgauge.SetValue(float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
wx.CallAfter(self.graph.SetBedTemperature,float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
except:
pass
fractioncomplete = 0.0
......@@ -1314,16 +1364,17 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.tempreport=l
wx.CallAfter(self.tempdisp.SetLabel,self.tempreport.strip().replace("ok ",""))
try:
self.hottgauge.SetValue(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
self.graph.SetExtruder0Temperature(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
self.graph.SetBedTemperature(float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
#self.hottgauge.SetValue(float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
wx.CallAfter(self.graph.SetExtruder0Temperature,float(filter(lambda x:x.startswith("T:"),self.tempreport.split())[0].split(":")[1]))
wx.CallAfter(self.graph.SetBedTemperature,float(filter(lambda x:x.startswith("B:"),self.tempreport.split())[0].split(":")[1]))
except:
pass
tstring=l.rstrip()
#print tstring
if(tstring!="ok"):
print tstring
#wx.CallAfter(self.logbox.AppendText,tstring+"\n")
if (tstring!="ok") and (tstring!="wait") and ("ok T:" not in tstring):
# print "*"+tstring+"*"
# print "[" + time.strftime('%H:%M:%S',time.localtime(time.time())) + "] " + tstring
wx.CallAfter(self.logbox.AppendText,tstring+"\n")
for i in self.recvlisteners:
i(l)
......@@ -1392,7 +1443,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
try:
import shlex
param = self.expandcommand(self.settings.slicecommand).encode()
print "Slicing: ",param;
print "Slicing: ",param
if webavail:
self.webInterface.AddLog("Slicing: "+param)
pararray=[i.replace("$s",self.filename).replace("$o",self.filename.replace(".stl","_export.gcode").replace(".STL","_export.gcode")).encode() for i in shlex.split(param.replace("\\","\\\\").encode())]
......@@ -1405,7 +1456,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.skeinp.wait()
self.stopsf=1
except:
print _("Failed to execute slicing software: ");
print _("Failed to execute slicing software: ")
if webavail:
self.webInterface.AddLog("Failed to execute slicing software: ")
self.stopsf=1
......@@ -1489,12 +1540,11 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
if self.p.online:
wx.CallAfter(self.printbtn.Enable)
threading.Thread(target=self.loadviz).start()
dlg.Destroy()
def loadviz(self):
Xtot,Ytot,Ztot,Xmin,Xmax,Ymin,Ymax,Zmin,Zmax = pronsole.measurements(self.f)
print pronsole.totalelength(self.f), _("mm of filament used in this print\n")
print _("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot);
print _("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot)
if webavail:
self.webInterface.AddLog("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot)
print _("the print goes from %f mm to %f mm in Y\nand is %f mm wide\n") % (Ymin, Ymax, Ytot)
......@@ -1593,8 +1643,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
pass
def connect(self,event):
print _("Connecting...");
print _("Connecting...")
port=None
try:
port=self.scanserial()[0]
......@@ -1630,6 +1679,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
self.statuscheck=False
self.connectbtn.SetLabel("Connect")
self.connectbtn.SetToolTip(wx.ToolTip("Connect to the printer"))
self.connectbtn.Bind(wx.EVT_BUTTON,self.connect)
wx.CallAfter(self.printbtn.Disable);
......@@ -1686,6 +1736,7 @@ class PronterWindow(wx.Frame,pronsole.pronsole):
class macroed(wx.Dialog):
"""Really simple editor to edit macro definitions"""
def __init__(self,macro_name,definition,callback,gcode=False):
self.indent_chars = " "
title=" macro %s"
......@@ -1696,19 +1747,22 @@ class macroed(wx.Dialog):
self.callback = callback
self.panel=wx.Panel(self,-1)
titlesizer=wx.BoxSizer(wx.HORIZONTAL)
title = wx.StaticText(self.panel,-1,title%macro_name)
titletext = wx.StaticText(self.panel,-1," _") #title%macro_name)
#title.SetFont(wx.Font(11,wx.NORMAL,wx.NORMAL,wx.BOLD))
titlesizer.Add(title,1)
self.okb = wx.Button(self.panel, -1, _("Save"))
titlesizer.Add(titletext,1)
self.findb = wx.Button(self.panel, -1, _("Find"),style=wx.BU_EXACTFIT) #New button for "Find" (Jezmy)
self.findb.Bind(wx.EVT_BUTTON, self.find)
self.okb = wx.Button(self.panel, -1, _("Save"),style=wx.BU_EXACTFIT)
self.okb.Bind(wx.EVT_BUTTON, self.save)
self.Bind(wx.EVT_CLOSE, self.close)
titlesizer.Add(self.findb)
titlesizer.Add(self.okb)
self.cancelb = wx.Button(self.panel, -1, _("Cancel"))
self.cancelb = wx.Button(self.panel, -1, _("Cancel"),style=wx.BU_EXACTFIT)
self.cancelb.Bind(wx.EVT_BUTTON, self.close)
titlesizer.Add(self.cancelb)
topsizer=wx.BoxSizer(wx.VERTICAL)
topsizer.Add(titlesizer,0,wx.EXPAND)
self.e=wx.TextCtrl(self.panel,style=wx.TE_MULTILINE+wx.HSCROLL,size=(200,200))
self.e=wx.TextCtrl(self.panel,style=wx.TE_MULTILINE+wx.HSCROLL,size=(400,400))
if not self.gcode:
self.e.SetValue(self.unindent(definition))
else:
......@@ -1720,6 +1774,37 @@ class macroed(wx.Dialog):
self.Show()
self.e.SetFocus()
def find(self,ev):
# Ask user what to look for, find it and point at it ... (Jezmy)
S = self.e.GetStringSelection()
if not S :
S = "Z"
FindValue = wx.GetTextFromUser('Please enter a search string:', caption="Search", default_value=S, parent=None)
somecode = self.e.GetValue()
numLines = len(somecode)
position = somecode.find(FindValue, self.e.GetInsertionPoint())
if position == -1 :
# ShowMessage(self,-1, "Not found!")
titletext = wx.TextCtrl(self.panel,-1,"Not Found!")
else:
# self.title.SetValue("Position : "+str(position))
titletext = wx.TextCtrl(self.panel,-1,str(position))
# ananswer = wx.MessageBox(str(numLines)+" Lines detected in file\n"+str(position), "OK")
self.e.SetFocus()
self.e.SetInsertionPoint(position)
self.e.SetSelection(position, position + len(FindValue))
self.e.ShowPosition(position)
def ShowMessage(self, ev , message):
dlg = wxMessageDialog(self, message,
"Info!", wxOK | wxICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
def save(self,ev):
self.Destroy()
if not self.gcode:
......@@ -1813,8 +1898,6 @@ class ButtonEdit(wx.Dialog):
topsizer.Add( (0,0),1)
topsizer.Add(self.CreateStdDialogButtonSizer(wx.OK|wx.CANCEL),0,wx.ALIGN_CENTER)
self.SetSizer(topsizer)
self.handler=None
def macrob_enabler(self,e):
macro = self.command.GetValue()
valid = False
......
......@@ -489,8 +489,8 @@ class TestGlPanel(GLPanel):
v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize)
v[1] = self.bedsize[1] - v[1]
v += [300]
print v
self.add_file("../prusa/metric-prusa/x-end-idler.stl", v)
print "Double-click at "+str(v)+" in "
print self
def forceresize(self):
self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] + 1))
......
"""
"""
import wx
def AddEllipticalArc(self, x, y, w, h, startAngle, endAngle, clockwise=False):
""" Draws an arc of an ellipse within bounding rect (x,y,w,h)
from startArc to endArc (in radians, relative to the horizontal line of the eclipse)"""
if True:
import warnings
warnings.warn("elliptical arcs are not supported")
w = w/2.0
h = h/2.0
self.AddArc(x+w, y+h, ((w+h)/2), startAngle, endAngle, clockwise)
return
else:
#implement in terms of AddArc by applying a transformation matrix
#Sigh this can't work, still need to patch wx to allow
#either a) AddPath that's not a closed path or
#b) allow pushing and popping of states on a path, not just on a context
#a) is possible in GDI+, need to investigate other renderers.
#b) is possible in Quartz and Cairo, but not in GDI+. It could
#possibly be simulated by combining the current transform with option a.
mtx = wx.GraphicsRenderer_GetDefaultRenderer().CreateMatrix()
path = wx.GraphicsRenderer_GetDefaultRenderer().CreatePath()
mtx.Translate(x+(w/2.0), y+(h/2.0))
mtx.Scale(w/2.0, y/2.0)
path.AddArc(0, 0, 1, startAngle, endAngle, clockwise)
path.Transform(mtx)
self.AddPath(path)
self.MoveToPoint(path.GetCurrentPoint())
self.CloseSubpath()
if not hasattr(wx.GraphicsPath, "AddEllipticalArc"):
wx.GraphicsPath.AddEllipticalArc = AddEllipticalArc
del AddEllipticalArc
\ No newline at end of file
"""
Parsers for specific attributes
"""
import urlparse
from pyparsing import (Literal,
Optional, oneOf, Group, StringEnd, Combine, Word, alphas, hexnums,
CaselessLiteral, SkipTo
)
from css.colour import colourValue
import string
##Paint values
none = CaselessLiteral("none").setParseAction(lambda t: ["NONE", ()])
currentColor = CaselessLiteral("currentColor").setParseAction(lambda t: ["CURRENTCOLOR", ()])
def parsePossibleURL(t):
possibleURL, fallback = t[0]
return [urlparse.urlsplit(possibleURL), fallback]
#Normal color declaration
colorDeclaration = none | currentColor | colourValue
urlEnd = (
Literal(")").suppress() +
Optional(Group(colorDeclaration), default=()) +
StringEnd()
)
url = (
CaselessLiteral("URL")
+
Literal("(").suppress()+
Group(SkipTo(urlEnd, include=True).setParseAction(parsePossibleURL))
)
#paint value will parse into a (type, details) tuple.
#For none and currentColor, the details tuple will be the empty tuple
#for CSS color declarations, it will be (type, (R,G,B))
#for URLs, it will be ("URL", ((url tuple), fallback))
#The url tuple will be as returned by urlparse.urlsplit, and can be
#an empty tuple if the parser has an error
#The fallback will be another (type, details) tuple as a parsed
#colorDeclaration, but may be the empty tuple if it is not present
paintValue = url | colorDeclaration
from __future__ import absolute_import
from .transform import transformList
from .inline import inlineStyle
""" CSS at-rules"""
from pyparsing import Literal, Combine
from .identifier import identifier
atkeyword = Combine(Literal("@") + identifier)
\ No newline at end of file
"""
CSS blocks
"""
from pyparsing import nestedExpr
block = nestedExpr(opener="{", closer="}")
"""
Parsing for CSS colour values.
Supported formats:
hex literal short: #fff
hex literal long: #fafafa
rgb bytes: rgb(255,100,0)
rgb percent: rgb(100%,100%,0%)
named color: black
"""
import wx
import string
import urlparse
from pyparsing import nums, Literal, Optional, oneOf, Group, StringEnd, Combine, Word, alphas, hexnums
from ..pathdata import number, sign
number = number.copy()
integerConstant = Word(nums+"+-").setParseAction(lambda t:int(t[0]))
#rgb format parser
comma = Literal(",").suppress()
def clampColourByte(val):
val = int(val)
return min(max(0,val), 255)
def clampColourPerc(val):
val = float(val)
return min(max(0,val), 100)
def parseColorPerc(token):
val = token[0]
val = clampColourPerc(val)
#normalize to bytes
return int(255 * (val / 100.0))
colorByte = Optional(sign) + integerConstant.setParseAction(lambda t: clampColourByte(t[0]))
colorPerc = number.setParseAction(parseColorPerc) + Literal("%").suppress()
rgb = (
Literal("rgb(").setParseAction(lambda t: "RGB") +
(
#integer constants, ie 255,255,255
Group(colorByte + comma + colorByte + comma + colorByte) ^
#percentage values, ie 100%, 50%
Group(colorPerc + comma + colorPerc + comma + colorPerc)
)
+
Literal(")").suppress() + StringEnd()
)
def parseShortHex(t):
return tuple(int(x*2, 16) for x in t[0])
doubleHex = Word(hexnums, exact=2).setParseAction(lambda t: int(t[0], 16))
hexLiteral = (Literal("#").setParseAction(lambda t: "RGB") +
(
Group(doubleHex + doubleHex + doubleHex) |
Word(hexnums, exact=3).setParseAction(parseShortHex)
) + StringEnd()
)
def parseNamedColour(t):
try:
return ["RGB", NamedColours[t[0].lower()]]
except KeyError:
return ["RGB", (0,0,0)]
namedColour = Word(alphas).setParseAction(parseNamedColour)
colourValue = rgb | hexLiteral | namedColour
##constants
NamedColours = {
#~ #html named colours
#~ "black":(0,0,0),
#~ "silver": (0xc0, 0xc0, 0xc0, 255),
#~ "gray": (0x80, 0x80, 0x80),
#~ "white":(255,255,255),
#~ "maroon":(0x80, 0, 0),
#~ "red":(0xff, 0, 0),
#~ "purple":(0x80, 0, 0x80),
#~ "fuchsia":(0xff, 0, 0xff),
#~ "green": (0, 0x80, 0),
#~ "lime": (0, 0xff, 0),
#~ "olive": (0x80, 0x80, 00),
#~ "yellow":(0xff, 0xff, 00),
#~ "navy": (0, 0, 0x80),
#~ "blue": (0, 0, 0xff),
#~ "teal": (0, 0x80, 0x80),
#~ "aqua": (0, 0xff, 0xff),
#expanded named colors from SVG spc
'aliceblue' : (240, 248, 255) ,
'antiquewhite' : (250, 235, 215) ,
'aqua' : ( 0, 255, 255) ,
'aquamarine' : (127, 255, 212) ,
'azure' : (240, 255, 255) ,
'beige' : (245, 245, 220) ,
'bisque' : (255, 228, 196) ,
'black' : ( 0, 0, 0) ,
'blanchedalmond' : (255, 235, 205) ,
'blue' : ( 0, 0, 255) ,
'blueviolet' : (138, 43, 226) ,
'brown' : (165, 42, 42) ,
'burlywood' : (222, 184, 135) ,
'cadetblue' : ( 95, 158, 160) ,
'chartreuse' : (127, 255, 0) ,
'chocolate' : (210, 105, 30) ,
'coral' : (255, 127, 80) ,
'cornflowerblue' : (100, 149, 237) ,
'cornsilk' : (255, 248, 220) ,
'crimson' : (220, 20, 60) ,
'cyan' : ( 0, 255, 255) ,
'darkblue' : ( 0, 0, 139) ,
'darkcyan' : ( 0, 139, 139) ,
'darkgoldenrod' : (184, 134, 11) ,
'darkgray' : (169, 169, 169) ,
'darkgreen' : ( 0, 100, 0) ,
'darkgrey' : (169, 169, 169) ,
'darkkhaki' : (189, 183, 107) ,
'darkmagenta' : (139, 0, 139) ,
'darkolivegreen' : ( 85, 107, 47) ,
'darkorange' : (255, 140, 0) ,
'darkorchid' : (153, 50, 204) ,
'darkred' : (139, 0, 0) ,
'darksalmon' : (233, 150, 122) ,
'darkseagreen' : (143, 188, 143) ,
'darkslateblue' : ( 72, 61, 139) ,
'darkslategray' : ( 47, 79, 79) ,
'darkslategrey' : ( 47, 79, 79) ,
'darkturquoise' : ( 0, 206, 209) ,
'darkviolet' : (148, 0, 211) ,
'deeppink' : (255, 20, 147) ,
'deepskyblue' : ( 0, 191, 255) ,
'dimgray' : (105, 105, 105) ,
'dimgrey' : (105, 105, 105) ,
'dodgerblue' : ( 30, 144, 255) ,
'firebrick' : (178, 34, 34) ,
'floralwhite' : (255, 250, 240) ,
'forestgreen' : ( 34, 139, 34) ,
'fuchsia' : (255, 0, 255) ,
'gainsboro' : (220, 220, 220) ,
'ghostwhite' : (248, 248, 255) ,
'gold' : (255, 215, 0) ,
'goldenrod' : (218, 165, 32) ,
'gray' : (128, 128, 128) ,
'grey' : (128, 128, 128) ,
'green' : ( 0, 128, 0) ,
'greenyellow' : (173, 255, 47) ,
'honeydew' : (240, 255, 240) ,
'hotpink' : (255, 105, 180) ,
'indianred' : (205, 92, 92) ,
'indigo' : ( 75, 0, 130) ,
'ivory' : (255, 255, 240) ,
'khaki' : (240, 230, 140) ,
'lavender' : (230, 230, 250) ,
'lavenderblush' : (255, 240, 245) ,
'lawngreen' : (124, 252, 0) ,
'lemonchiffon' : (255, 250, 205) ,
'lightblue' : (173, 216, 230) ,
'lightcoral' : (240, 128, 128) ,
'lightcyan' : (224, 255, 255) ,
'lightgoldenrodyellow' : (250, 250, 210) ,
'lightgray' : (211, 211, 211) ,
'lightgreen' : (144, 238, 144) ,
'lightgrey' : (211, 211, 211) ,
'lightpink' : (255, 182, 193) ,
'lightsalmon' : (255, 160, 122) ,
'lightseagreen' : ( 32, 178, 170) ,
'lightskyblue' : (135, 206, 250) ,
'lightslategray' : (119, 136, 153) ,
'lightslategrey' : (119, 136, 153) ,
'lightsteelblue' : (176, 196, 222) ,
'lightyellow' : (255, 255, 224) ,
'lime' : ( 0, 255, 0) ,
'limegreen' : ( 50, 205, 50) ,
'linen' : (250, 240, 230) ,
'magenta' : (255, 0, 255) ,
'maroon' : (128, 0, 0) ,
'mediumaquamarine' : (102, 205, 170) ,
'mediumblue' : ( 0, 0, 205) ,
'mediumorchid' : (186, 85, 211) ,
'mediumpurple' : (147, 112, 219) ,
'mediumseagreen' : ( 60, 179, 113) ,
'mediumslateblue' : (123, 104, 238) ,
'mediumspringgreen' : ( 0, 250, 154) ,
'mediumturquoise' : ( 72, 209, 204) ,
'mediumvioletred' : (199, 21, 133) ,
'midnightblue' : ( 25, 25, 112) ,
'mintcream' : (245, 255, 250) ,
'mistyrose' : (255, 228, 225) ,
'moccasin' : (255, 228, 181) ,
'navajowhite' : (255, 222, 173) ,
'navy' : ( 0, 0, 128) ,
'oldlace' : (253, 245, 230) ,
'olive' : (128, 128, 0) ,
'olivedrab' : (107, 142, 35) ,
'orange' : (255, 165, 0) ,
'orangered' : (255, 69, 0) ,
'orchid' : (218, 112, 214) ,
'palegoldenrod' : (238, 232, 170) ,
'palegreen' : (152, 251, 152) ,
'paleturquoise' : (175, 238, 238) ,
'palevioletred' : (219, 112, 147) ,
'papayawhip' : (255, 239, 213) ,
'peachpuff' : (255, 218, 185) ,
'peru' : (205, 133, 63) ,
'pink' : (255, 192, 203) ,
'plum' : (221, 160, 221) ,
'powderblue' : (176, 224, 230) ,
'purple' : (128, 0, 128) ,
'red' : (255, 0, 0) ,
'rosybrown' : (188, 143, 143) ,
'royalblue' : ( 65, 105, 225) ,
'saddlebrown' : (139, 69, 19) ,
'salmon' : (250, 128, 114) ,
'sandybrown' : (244, 164, 96) ,
'seagreen' : ( 46, 139, 87) ,
'seashell' : (255, 245, 238) ,
'sienna' : (160, 82, 45) ,
'silver' : (192, 192, 192) ,
'skyblue' : (135, 206, 235) ,
'slateblue' : (106, 90, 205) ,
'slategray' : (112, 128, 144) ,
'slategrey' : (112, 128, 144) ,
'snow' : (255, 250, 250) ,
'springgreen' : ( 0, 255, 127) ,
'steelblue' : ( 70, 130, 180) ,
'tan' : (210, 180, 140) ,
'teal' : ( 0, 128, 128) ,
'thistle' : (216, 191, 216) ,
'tomato' : (255, 99, 71) ,
'turquoise' : ( 64, 224, 208) ,
'violet' : (238, 130, 238) ,
'wheat' : (245, 222, 179) ,
'white' : (255, 255, 255) ,
'whitesmoke' : (245, 245, 245) ,
'yellow' : (255, 255, 0) ,
'yellowgreen' : (154, 205, 50) ,
}
def fillCSS2SystemColours():
#The system colours require a wxApp to be present to retrieve,
#so if you wnat support for them you'll need
#to call this function after your wxApp instance starts
systemColors = {
"ActiveBorder": wx.SYS_COLOUR_ACTIVEBORDER,
"ActiveCaption": wx.SYS_COLOUR_ACTIVECAPTION,
"AppWorkspace": wx.SYS_COLOUR_APPWORKSPACE,
"Background": wx.SYS_COLOUR_BACKGROUND,
"ButtonFace": wx.SYS_COLOUR_BTNFACE,
"ButtonHighlight": wx.SYS_COLOUR_BTNHIGHLIGHT,
"ButtonShadow": wx.SYS_COLOUR_BTNSHADOW,
"ButtonText": wx.SYS_COLOUR_BTNTEXT,
"CaptionText": wx.SYS_COLOUR_CAPTIONTEXT,
"GrayText": wx.SYS_COLOUR_GRAYTEXT,
"Highlight": wx.SYS_COLOUR_HIGHLIGHT,
"HighlightText": wx.SYS_COLOUR_HIGHLIGHTTEXT,
"InactiveBorder": wx.SYS_COLOUR_INACTIVEBORDER,
"InactiveCaption": wx.SYS_COLOUR_INACTIVECAPTION,
"InfoBackground": wx.SYS_COLOUR_INFOBK,
"InfoText": wx.SYS_COLOUR_INFOTEXT,
"Menu": wx.SYS_COLOUR_MENU,
"MenuText": wx.SYS_COLOUR_MENUTEXT,
"Scrollbar": wx.SYS_COLOUR_SCROLLBAR,
"ThreeDDarkShadow": wx.SYS_COLOUR_3DDKSHADOW,
"ThreeDFace": wx.SYS_COLOUR_3DFACE,
"ThreeDHighlight": wx.SYS_COLOUR_3DHIGHLIGHT,
"ThreeDLightShadow": wx.SYS_COLOUR_3DLIGHT,
"ThreeDShadow": wx.SYS_COLOUR_3DSHADOW,
"Window": wx.SYS_COLOUR_WINDOW,
"WindowFrame": wx.SYS_COLOUR_WINDOWFRAME,
"WindowText": wx.SYS_COLOUR_WINDOWTEXT
}
NamedColours.update(
#strip the alpha from the system colors. Is this really what we want to do?
(k.lower(), wx.SystemSettings.GetColour(v)[:3]) for (k,v) in systemColors.iteritems()
)
\ No newline at end of file
""" Parse CSS identifiers. More complicated than it sounds"""
from pyparsing import Word, Literal, Regex, Combine, Optional, White, oneOf, ZeroOrMore
import string
import re
class White(White):
""" Customize whitespace to match the CSS spec values"""
def __init__(self, ws=" \t\r\n\f", min=1, max=0, exact=0):
super(White, self).__init__(ws, min, max, exact)
escaped = (
Literal("\\").suppress() +
#chr(20)-chr(126) + chr(128)-unichr(sys.maxunicode)
Regex(u"[\u0020-\u007e\u0080-\uffff]", re.IGNORECASE)
)
def convertToUnicode(t):
return unichr(int(t[0], 16))
hex_unicode = (
Literal("\\").suppress() +
Regex("[0-9a-f]{1,6}", re.IGNORECASE) +
Optional(White(exact=1)).suppress()
).setParseAction(convertToUnicode)
escape = hex_unicode | escaped
#any unicode literal outside the 0-127 ascii range
nonascii = Regex(u"[^\u0000-\u007f]")
#single character for starting an identifier.
nmstart = Regex(u"[A-Z]", re.IGNORECASE) | nonascii | escape
nmchar = Regex(u"[0-9A-Z-]", re.IGNORECASE) | nonascii | escape
identifier = Combine(nmstart + ZeroOrMore(nmchar))
\ No newline at end of file
""" Parser for inline CSS in style attributes """
def inlineStyle(styleString):
if not styleString:
return {}
styles = styleString.split(";")
rv = dict(style.split(":") for style in styles if len(style) != 0)
return rv
\ No newline at end of file
"""
Parsing for CSS and CSS-style values, such as transform and filter attributes.
"""
from pyparsing import (Literal, Word, CaselessLiteral,
Optional, Combine, Forward, ZeroOrMore, nums, oneOf, Group, delimitedList)
#some shared definitions from pathdata
from ..pathdata import number, maybeComma
paren = Literal("(").suppress()
cparen = Literal(")").suppress()
def Parenthised(exp):
return Group(paren + exp + cparen)
skewY = Literal("skewY") + Parenthised(number)
skewX = Literal("skewX") + Parenthised(number)
rotate = Literal("rotate") + Parenthised(
number + Optional(maybeComma + number + maybeComma + number)
)
scale = Literal("scale") + Parenthised(
number + Optional(maybeComma + number)
)
translate = Literal("translate") + Parenthised(
number + Optional(maybeComma + number)
)
matrix = Literal("matrix") + Parenthised(
#there's got to be a better way to write this
number + maybeComma +
number + maybeComma +
number + maybeComma +
number + maybeComma +
number + maybeComma +
number
)
transform = (skewY | skewX | rotate | scale | translate | matrix)
transformList = delimitedList(Group(transform), delim=maybeComma)
if __name__ == '__main__':
from tests.test_css import *
unittest.main()
\ No newline at end of file
"""
Parser for various kinds of CSS values as per CSS2 spec section 4.3
"""
from pyparsing import Word, Combine, Optional, Literal, oneOf, CaselessLiteral, StringEnd
def asInt(s,l,t):
return int(t[0])
def asFloat(s,l,t):
return float(t[0])
def asFloatOrInt(s,l,t):
""" Return an int if possible, otherwise a float"""
v = t[0]
try:
return int(v)
except ValueError:
return float(v)
integer = Word("0123456789").setParseAction(asInt)
number = Combine(
Optional(Word("0123456789")) + Literal(".") + Word("01234567890")
| integer
)
number.setName('number')
sign = oneOf("+ -")
signedNumber = Combine(Optional(sign) + number).setParseAction(asFloat)
lengthValue = Combine(Optional(sign) + number).setParseAction(asFloatOrInt)
lengthValue.setName('lengthValue')
#TODO: The physical units like in, mm
lengthUnit = oneOf(['em', 'ex', 'px', 'pt', '%'], caseless=True)
#the spec says that the unit is only optional for a 0 length, but
#there are just too many places where a default is permitted.
#TODO: Maybe should use a ctor like optional to let clients declare it?
length = lengthValue + Optional(lengthUnit, default=None) + StringEnd()
length.leaveWhitespace()
#set the parse action aftward so it doesn't "infect" the parsers that build on it
number.setParseAction(asFloat)
\ No newline at end of file
"""
SVGDocument
"""
import wx
from cStringIO import StringIO
import warnings
import math
from functools import wraps
import pathdata
import css
from svg.css.colour import colourValue
from svg.css import values
from attributes import paintValue
document = """<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="4cm" height="4cm" viewBox="0 0 400 400"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example triangle01- simple example of a 'path'</title>
<desc>A path that draws a triangle</desc>
<rect x="1" y="1" width="398" height="398"
fill="none" stroke="blue" />
<path d="M 100 100 L 300 100 L 200 300 z"
fill="red" stroke="blue" stroke-width="3" />
</svg>"""
makePath = lambda: wx.GraphicsRenderer_GetDefaultRenderer().CreatePath()
def attrAsFloat(node, attr, defaultValue="0"):
val = node.get(attr, defaultValue)
#TODO: process stuff like "inherit" by walking back up the nodes
#fast path optimization - if it's a valid float, don't
#try to parse it.
try:
return float(val)
except ValueError:
return valueToPixels(val)
def valueToPixels(val, defaultUnits="px"):
#TODO manage default units
from pyparsing import ParseException
try:
val, unit = values.length.parseString(val)
except ParseException:
print "***", val
raise
#todo: unit conversion to something other than pixels
return val
def pathHandler(func):
"""decorator for methods which return a path operation
Creates the path they will fill,
and generates the path operations for the node
"""
@wraps(func)
def inner(self, node):
#brush = self.getBrushFromState()
#pen = self.getPenFromState()
#if not (brush or pen):
# return None, []
path = wx.GraphicsRenderer_GetDefaultRenderer().CreatePath()
func(self, node, path)
ops = self.generatePathOps(path)
return path, ops
return inner
class SVGDocument(object):
lastControl = None
brushCache = {}
penCache = {}
def __init__(self, element):
"""
Create an SVG document from an ElementTree node.
"""
self.handlers = {
'{http://www.w3.org/2000/svg}svg':self.addGroupToDocument,
'{http://www.w3.org/2000/svg}a':self.addGroupToDocument,
'{http://www.w3.org/2000/svg}g':self.addGroupToDocument,
'{http://www.w3.org/2000/svg}rect':self.addRectToDocument,
'{http://www.w3.org/2000/svg}circle': self.addCircleToDocument,
'{http://www.w3.org/2000/svg}ellipse': self.addEllipseToDocument,
'{http://www.w3.org/2000/svg}line': self.addLineToDocument,
'{http://www.w3.org/2000/svg}polyline': self.addPolyLineToDocument,
'{http://www.w3.org/2000/svg}polygon': self.addPolygonToDocument,
'{http://www.w3.org/2000/svg}path':self.addPathDataToDocument,
'{http://www.w3.org/2000/svg}text':self.addTextToDocument
}
assert element.tag == '{http://www.w3.org/2000/svg}svg', 'Not an SVG fragment'
self.tree = element
self.paths = {}
self.stateStack = [{}]
path, ops = self.processElement(element)
self.ops = ops
@property
def state(self):
""" Retrieve the current state, without popping"""
return self.stateStack[-1]
def processElement(self, element):
""" Process one element of the XML tree.
Returns the path representing the node,
and an operation list for drawing the node.
Parent nodes should return a path (for hittesting), but
no draw operations
"""
#copy the current state
current = dict(self.state)
current.update(element.items())
current.update(css.inlineStyle(element.get("style", "")))
self.stateStack.append(current)
handler = self.handlers.get(element.tag, lambda *any: (None, None))
path, ops = handler(element)
self.paths[element] = path
self.stateStack.pop()
return path, ops
def createTransformOpsFromNode(self, node):
""" Returns an oplist for transformations.
This applies to a node, not the current state because
the transform stack is saved in the wxGraphicsContext.
This oplist does *not* include the push/pop state commands
"""
ops = []
transform = node.get('transform')
#todo: replace this with a mapping list
if transform:
for transform, args in css.transformList.parseString(transform):
if transform == 'scale':
if len(args) == 1:
x = y = args[0]
else:
x, y = args
ops.append(
(wx.GraphicsContext.Scale, (x, y))
)
if transform == 'translate':
if len(args) == 1:
x = args[0]
y = 0
else:
x, y = args
ops.append(
(wx.GraphicsContext.Translate, (x, y))
)
if transform == 'rotate':
if len(args) == 3:
angle, cx, cy = args
angle = math.radians(angle)
ops.extend([
(wx.GraphicsContext.Translate, (cx, cy)),
(wx.GraphicsContext.Rotate, (angle,)),
(wx.GraphicsContext.Translate, (-cx, -cy)),
])
else:
angle = args[0]
angle = math.radians(angle)
ops.append(
(wx.GraphicsContext.Rotate, (angle,))
)
if transform == 'matrix':
matrix = wx.GraphicsRenderer_GetDefaultRenderer().CreateMatrix(
*args
)
ops.append(
(wx.GraphicsContext.ConcatTransform, (matrix,))
)
if transform == 'skewX':
matrix = wx.GraphicsRenderer_GetDefaultRenderer().CreateMatrix(
1,0,math.tan(math.radians(args[0])),1,0,0
)
ops.append(
(wx.GraphicsContext.ConcatTransform, (matrix,))
)
if transform == 'skewY':
matrix = wx.GraphicsRenderer_GetDefaultRenderer().CreateMatrix(
1,math.tan(math.radians(args[0])),0,1,0,0
)
ops.append(
(wx.GraphicsContext.ConcatTransform, (matrix,))
)
return ops
def addGroupToDocument(self, node):
""" For parent elements: push on a state,
then process all child elements
"""
ops = [
(wx.GraphicsContext.PushState, ())
]
path = makePath()
ops.extend(self.createTransformOpsFromNode(node))
for child in node.getchildren():
cpath, cops = self.processElement(child)
if cpath:
path.AddPath(cpath)
if cops:
ops.extend(cops)
ops.append(
(wx.GraphicsContext.PopState, ())
)
return path, ops
def getFontFromState(self):
font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
family = self.state.get("font-family")
if False:#family:
#print "setting font", family
font.SetFaceName(family)
size = self.state.get("font-size")
#I'm not sure if this is right or not
if size:
val, unit = values.length.parseString(size)
if '__WXMSW__' in wx.PlatformInfo:
i = int(val)
font.SetPixelSize((i, i))
else:
font.SetPointSize(int(val))
return font
def addTextToDocument(self, node):
x, y = [attrAsFloat(node, attr) for attr in ('x', 'y')]
def DoDrawText(context, text, x, y, brush=wx.NullGraphicsBrush):
#SVG spec appears to originate text from the bottom
#rather than the top as with our API. This function
#will measure and then re-orient the text as needed.
w, h = context.GetTextExtent(text)
y -= h
context.DrawText(text, x, y, brush)
font = self.getFontFromState()
brush = self.getBrushFromState()
if not (brush and brush.IsOk()):
brush = wx.BLACK_BRUSH
#normalize whitespace
#TODO: SVG probably has rules for this
text = ' '.join(node.text.split() if node.text else "")
if text is None:
return None, []
ops = [
(wx.GraphicsContext.SetFont, (font, brush.Colour)),
(DoDrawText, (text, x, y))
]
return None, ops
@pathHandler
def addRectToDocument(self, node, path):
x, y, w, h = (attrAsFloat(node, attr) for attr in ['x', 'y', 'width', 'height'])
rx = node.get('rx')
ry = node.get('ry')
if not (w and h):
path.MoveToPoint(x,y) #keep the current point correct
return
if rx or ry:
if rx and ry:
rx, ry = float(rx), float(ry)
elif rx:
rx = ry = float(rx)
elif ry:
rx = ry = float(ry)
#value clamping as per spec section 9.2
rx = min(rx, w/2)
ry = min(ry, h/2)
#origin
path.MoveToPoint(x+rx, y)
path.AddLineToPoint(x+w-rx, y)
#top right
cx = rx * 2
cy = ry * 2
path.AddEllipticalArc(
x+w-cx, y,
cx, cy,
math.radians(270), math.radians(0),
True
)
path.AddLineToPoint(x+w, y+h-ry)
#bottom right
path.AddEllipticalArc(
x+w-cx, y+h-cy,
cx, cy,
math.radians(0), math.radians(90),
True
)
path.AddLineToPoint(x+rx, y+h)
#bottom left
path.AddEllipticalArc(
x, y+h-cy,
cx, cy,
math.radians(90),
math.radians(180),
True
)
path.AddLineToPoint(x, y+ry)
#bottom right
path.AddEllipticalArc(
x, y,
cx, cy,
math.radians(180),
math.radians(270),
True
)
path.CloseSubpath()
else:
path.AddRectangle(
x, y, w, h
)
@pathHandler
def addCircleToDocument(self, node, path):
cx, cy, r = [attrAsFloat(node, attr) for attr in ('cx', 'cy', 'r')]
path.AddCircle(cx, cy, r)
@pathHandler
def addEllipseToDocument(self, node, path):
cx, cy, rx, ry = [float(node.get(attr, 0)) for attr in ('cx', 'cy', 'rx', 'ry')]
#cx, cy are centerpoint.
#rx, ry are radius.
#wxGC coords are the rect of the ellipse bounding box.
if rx <= 0 or ry <= 0:
return
x = cx - rx
y = cy - ry
path.AddEllipse(x, y, rx*2, ry*2)
@pathHandler
def addLineToDocument(self, node, path):
x1, y1, x2, y2 = [attrAsFloat(node, attr) for attr in ('x1', 'y1', 'x2', 'y2')]
path.MoveToPoint(x1, y1)
path.AddLineToPoint(x2, y2)
@pathHandler
def addPolyLineToDocument(self, node, path):
#translate to pathdata and render that
data = "M " + node.get("points")
self.addPathDataToPath(data, path)
@pathHandler
def addPolygonToDocument(self, node, path):
#translate to pathdata and render that
data = "M " + node.get("points") + " Z"
self.addPathDataToPath(data, path)
@pathHandler
def addPathDataToDocument(self, node, path):
self.addPathDataToPath(node.get('d', ''), path)
def addPathDataToPath(self, data, path):
self.lastControl = None
self.lastControlQ = None
self.firstPoints = []
def normalizeStrokes(parseResults):
""" The data comes from the parser in the
form of (command, [list of arguments]).
We translate that to [(command, args[0]), (command, args[1])]
via a generator.
M is special cased because its subsequent arguments
become linetos.
"""
for command, arguments in parseResults:
if not arguments:
yield (command, ())
else:
arguments = iter(arguments)
if command == 'm':
yield (command, arguments.next())
command = "l"
elif command == "M":
yield (command, arguments.next())
command = "L"
for arg in arguments:
yield (command, arg)
#print "data length", len(data)
import time
t = time.time()
parsed = pathdata.svg.parseString(data)
#print "parsed in", time.time()-t
for stroke in normalizeStrokes(parsed):
self.addStrokeToPath(path, stroke)
def generatePathOps(self, path):
""" Look at the current state and generate the
draw operations (fill, stroke, neither) for the path"""
ops = []
brush = self.getBrushFromState(path)
fillRule = self.state.get('fill-rule', 'nonzero')
frMap = {'nonzero':wx.WINDING_RULE, 'evenodd': wx.ODDEVEN_RULE}
fr = frMap.get(fillRule, wx.ODDEVEN_RULE)
if brush:
ops.append(
(wx.GraphicsContext.SetBrush, (brush,))
)
ops.append(
(wx.GraphicsContext.FillPath, (path, fr))
)
pen = self.getPenFromState()
if pen:
ops.append(
(wx.GraphicsContext.SetPen, (pen,))
)
ops.append(
(wx.GraphicsContext.StrokePath, (path,))
)
return ops
def getPenFromState(self):
pencolour = self.state.get('stroke', 'none')
if pencolour == 'currentColor':
pencolour = self.state.get('color', 'none')
if pencolour == 'transparent':
return wx.TRANSPARENT_PEN
if pencolour == 'none':
return wx.NullPen
type, value = colourValue.parseString(pencolour)
if type == 'URL':
warnings.warn("Color servers for stroking not implemented")
return wx.NullPen
else:
if value[:3] == (-1, -1, -1):
return wx.NullPen
pen = wx.Pen(wx.Colour(*value))
width = self.state.get('stroke-width')
if width:
width, units = values.length.parseString(width)
pen.SetWidth(width)
capmap = {
'butt':wx.CAP_BUTT,
'round':wx.CAP_ROUND,
'square':wx.CAP_PROJECTING
}
joinmap = {
'miter':wx.JOIN_MITER,
'round':wx.JOIN_ROUND,
'bevel':wx.JOIN_BEVEL
}
pen.SetCap(capmap.get(self.state.get('stroke-linecap', None), wx.CAP_BUTT))
pen.SetJoin(joinmap.get(self.state.get('stroke-linejoin', None), wx.JOIN_MITER))
return wx.GraphicsRenderer_GetDefaultRenderer().CreatePen(pen)
def getBrushFromState(self, path=None):
brushcolour = self.state.get('fill', 'black').strip()
type, details = paintValue.parseString(brushcolour)
if type == "URL":
url, fallback = details
element = self.resolveURL(url)
if not element:
if fallback:
type, details = fallback
else:
r, g, b, = 0, 0, 0
else:
if element.tag == '{http://www.w3.org/2000/svg}linearGradient':
box = path.GetBox()
x, y, w, h = box.Get()
return wx.GraphicsRenderer.GetDefaultRenderer().CreateLinearGradientBrush(
x,y,x+w,y+h,wx.Colour(0,0,255,128), wx.RED
)
elif element.tag == '{http://www.w3.org/2000/svg}radialGradient':
box = path.GetBox()
x, y, w, h = box.Get()
#print w
mx = wx.GraphicsRenderer.GetDefaultRenderer().CreateMatrix(x,y,w,h)
cx, cy = mx.TransformPoint(0.5, 0.5)
fx, fy = cx, cy
return wx.GraphicsRenderer.GetDefaultRenderer().CreateRadialGradientBrush(
cx,cy,
fx,fy,
(max(w,h))/2,
wx.Colour(0,0,255,128), wx.RED
)
else:
#invlid gradient specified
return wx.NullBrush
r,g,b = 0,0,0
if type == 'CURRENTCOLOR':
type, details = paintValue.parseString(self.state.get('color', 'none'))
if type == 'RGB':
r,g,b = details
elif type == "NONE":
return wx.NullBrush
opacity = self.state.get('fill-opacity', self.state.get('opacity', '1'))
opacity = float(opacity)
opacity = min(max(opacity, 0.0), 1.0)
a = 255 * opacity
#using try/except block instead of
#just setdefault because the wxBrush and wxColour would
#be created every time anyway in order to pass them,
#defeating the purpose of the cache
try:
return SVGDocument.brushCache[(r,g,b,a)]
except KeyError:
return SVGDocument.brushCache.setdefault((r,g,b,a), wx.Brush(wx.Colour(r,g,b,a)))
def resolveURL(self, urlData):
"""
Resolve a URL and return an elementTree Element from it.
Return None if unresolvable
"""
scheme, netloc, path, query, fragment = urlData
if scheme == netloc == path == '':
#horrible. There's got to be a better way?
if self.tree.get("id") == fragment:
return self.tree
else:
for child in self.tree.getiterator():
#print child.get("id")
if child.get("id") == fragment:
return child
return None
else:
return self.resolveRemoteURL(urlData)
def resolveRemoteURL(self, url):
return None
def addStrokeToPath(self, path, stroke):
""" Given a stroke from a path command
(in the form (command, arguments)) create the path
commands that represent it.
TODO: break out into (yet another) class/module,
especially so we can get O(1) dispatch on type?
"""
type, arg = stroke
relative = False
if type == type.lower():
relative = True
ox, oy = path.GetCurrentPoint().Get()
else:
ox = oy = 0
def normalizePoint(arg):
x, y = arg
return x+ox, y+oy
def reflectPoint(point, relativeTo):
x, y = point
a, b = relativeTo
return ((a*2)-x), ((b*2)-y)
type = type.upper()
if type == 'M':
pt = normalizePoint(arg)
self.firstPoints.append(pt)
path.MoveToPoint(pt)
elif type == 'L':
path.AddLineToPoint(normalizePoint(arg))
elif type == 'C':
#control1, control2, endpoint = arg
control1, control2, endpoint = map(
normalizePoint, arg
)
self.lastControl = control2
path.AddCurveToPoint(
control1,
control2,
endpoint
)
#~ cp = path.GetCurrentPoint()
#~ path.AddCircle(c1x, c1y, 5)
#~ path.AddCircle(c2x, c2y, 3)
#~ path.AddCircle(x,y, 7)
#~ path.MoveToPoint(cp)
#~ print "C", control1, control2, endpoint
elif type == 'S':
#control2, endpoint = arg
control2, endpoint = map(
normalizePoint, arg
)
if self.lastControl:
control1 = reflectPoint(self.lastControl, path.GetCurrentPoint())
else:
control1 = path.GetCurrentPoint()
#~ print "S", self.lastControl,":",control1, control2, endpoint
self.lastControl = control2
path.AddCurveToPoint(
control1,
control2,
endpoint
)
elif type == "Q":
(cx, cy), (x,y) = map(normalizePoint, arg)
self.lastControlQ = (cx, cy)
path.AddQuadCurveToPoint(cx, cy, x, y)
elif type == "T":
x, y, = normalizePoint(arg)
if self.lastControlQ:
cx, cy = reflectPoint(self.lastControlQ, path.GetCurrentPoint())
else:
cx, cy = path.GetCurrentPoint()
self.lastControlQ = (cx, cy)
path.AddQuadCurveToPoint(cx, cy, x, y)
elif type == "V":
_, y = normalizePoint((0, arg))
x, _ = path.GetCurrentPoint()
path.AddLineToPoint(x,y)
elif type == "H":
x, _ = normalizePoint((arg, 0))
_, y = path.GetCurrentPoint()
path.AddLineToPoint(x,y)
elif type == "A":
#wxGC currently only supports circular arcs,
#not eliptical ones
(
(rx, ry), #radii of ellipse
angle, #angle of rotation on the ellipse in degrees
(fa, fs), #arc and stroke angle flags
(x, y) #endpoint on the arc
) = arg
x, y = normalizePoint((x,y))
cx, cy = path.GetCurrentPoint()
if (cx, cy) == (x, y):
return #noop
if (rx == 0 or ry == 0):
#no radius is effectively a line
path.AddLineToPoint(x,y)
return
#find the center point for the ellipse
#translation via the angle
angle = angle % 360
angle = math.radians(angle)
#translated endpoint
xPrime = math.cos(angle) * ((cx-x)/2)
xPrime += math.sin(angle) * ((cx-x)/2)
yPrime = -(math.sin(angle)) * ((cy-y)/2)
yPrime += (math.cos(angle)) * ((cy-y)/2)
temp = ((rx**2) * (ry**2)) - ((rx**2) * (yPrime**2)) - ((ry**2) * (xPrime**2))
temp /= ((rx**2) * (yPrime**2)) + ((ry**2)*(xPrime**2))
temp = abs(temp)
try:
temp = math.sqrt(temp)
except ValueError:
import pdb
pdb.set_trace()
cxPrime = temp * ((rx * yPrime) / ry)
cyPrime = temp * -((ry * xPrime) / rx)
if fa == fs:
cxPrime, cyPrime = -cxPrime, -cyPrime
#reflect backwards now for the origin
cnx = math.cos(angle) * cxPrime
cnx += math.sin(angle) * cxPrime
cny = -(math.sin(angle)) * cyPrime
cny += (math.cos(angle)) * cyPrime
cnx += ((cx+x)/2.0)
cny += ((cy+y)/2.0)
#calculate the angle between the two endpoints
lastArc = wx.Point2D(x-cnx, y-cny).GetVectorAngle()
firstArc = wx.Point2D(cx-cnx, cy-cny).GetVectorAngle()
lastArc = math.radians(lastArc)
firstArc = math.radians(firstArc)
#aargh buggines.
#AddArc draws a straight line between
#the endpoints of the arc.
#putting it in a subpath makes the strokes come out
#correctly, but it still only fills the slice
#taking out the MoveToPoint fills correctly.
path.AddEllipse(cnx-rx, cny-ry, rx*2, ry*2)
path.MoveToPoint(x, y)
#~ npath = makePath()
#~ npath.AddEllipticalArc(cnx-rx, cny-ry, rx*2, ry*2, firstArc, lastArc, False)
#~ npath.MoveToPoint(x,y)
#~ path.AddPath(npath)
elif type == 'Z':
#~ Bugginess:
#~ CloseSubpath() doesn't change the
#~ current point, as SVG spec requires.
#~ However, manually moving to the endpoint afterward opens a new subpath
#~ and (apparently) messes with stroked but not filled paths.
#~ This is possibly a bug in GDI+?
#~ Manually closing the path via AddLineTo gives incorrect line join
#~ results
#~ Manually closing the path *and* calling CloseSubpath() appears
#~ to give correct results on win32
pt = self.firstPoints.pop()
path.AddLineToPoint(pt)
path.CloseSubpath()
def render(self, context):
if not hasattr(self, "ops"):
return
for op, args in self.ops:
op(context, *args)
if __name__ == '__main__':
from tests.test_document import *
unittest.main()
"""
SVG path data parser
Usage:
steps = svg.parseString(pathdata)
for command, arguments in steps:
pass
"""
from pyparsing import (ParserElement, Literal, Word, CaselessLiteral,
Optional, Combine, Forward, ZeroOrMore, nums, oneOf, Group, ParseException, OneOrMore)
#ParserElement.enablePackrat()
def Command(char):
""" Case insensitive but case preserving"""
return CaselessPreservingLiteral(char)
def Arguments(token):
return Group(token)
class CaselessPreservingLiteral(CaselessLiteral):
""" Like CaselessLiteral, but returns the match as found
instead of as defined.
"""
def __init__( self, matchString ):
super(CaselessPreservingLiteral,self).__init__( matchString.upper() )
self.name = "'%s'" % matchString
self.errmsg = "Expected " + self.name
self.myException.msg = self.errmsg
def parseImpl( self, instring, loc, doActions=True ):
test = instring[ loc:loc+self.matchLen ]
if test.upper() == self.match:
return loc+self.matchLen, test
#~ raise ParseException( instring, loc, self.errmsg )
exc = self.myException
exc.loc = loc
exc.pstr = instring
raise exc
def Sequence(token):
""" A sequence of the token"""
return OneOrMore(token+maybeComma)
digit_sequence = Word(nums)
sign = oneOf("+ -")
def convertToFloat(s, loc, toks):
try:
return float(toks[0])
except:
raise ParseException(loc, "invalid float format %s"%toks[0])
exponent = CaselessLiteral("e")+Optional(sign)+Word(nums)
#note that almost all these fields are optional,
#and this can match almost anything. We rely on Pythons built-in
#float() function to clear out invalid values - loosely matching like this
#speeds up parsing quite a lot
floatingPointConstant = Combine(
Optional(sign) +
Optional(Word(nums)) +
Optional(Literal(".") + Optional(Word(nums)))+
Optional(exponent)
)
floatingPointConstant.setParseAction(convertToFloat)
number = floatingPointConstant
#same as FP constant but don't allow a - sign
nonnegativeNumber = Combine(
Optional(Word(nums)) +
Optional(Literal(".") + Optional(Word(nums)))+
Optional(exponent)
)
nonnegativeNumber.setParseAction(convertToFloat)
coordinate = number
#comma or whitespace can seperate values all over the place in SVG
maybeComma = Optional(Literal(',')).suppress()
coordinateSequence = Sequence(coordinate)
coordinatePair = (coordinate + maybeComma + coordinate).setParseAction(lambda t: tuple(t))
coordinatePairSequence = Sequence(coordinatePair)
coordinatePairPair = coordinatePair + maybeComma + coordinatePair
coordinatePairPairSequence = Sequence(Group(coordinatePairPair))
coordinatePairTriple = coordinatePair + maybeComma + coordinatePair + maybeComma + coordinatePair
coordinatePairTripleSequence = Sequence(Group(coordinatePairTriple))
#commands
lineTo = Group(Command("L") + Arguments(coordinatePairSequence))
moveTo = Group(Command("M") + Arguments(coordinatePairSequence))
closePath = Group(Command("Z")).setParseAction(lambda t: ('Z', (None,)))
flag = oneOf("1 0").setParseAction(lambda t: bool(int((t[0]))))
arcRadius = (
nonnegativeNumber + maybeComma + #rx
nonnegativeNumber #ry
).setParseAction(lambda t: tuple(t))
arcFlags = (flag + maybeComma + flag).setParseAction(lambda t: tuple(t))
ellipticalArcArgument = Group(
arcRadius + maybeComma + #rx, ry
number + maybeComma +#rotation
arcFlags + #large-arc-flag, sweep-flag
coordinatePair #(x,y)
)
ellipticalArc = Group(Command("A") + Arguments(Sequence(ellipticalArcArgument)))
smoothQuadraticBezierCurveto = Group(Command("T") + Arguments(coordinatePairSequence))
quadraticBezierCurveto = Group(Command("Q") + Arguments(coordinatePairPairSequence))
smoothCurve = Group(Command("S") + Arguments(coordinatePairPairSequence))
curve = Group(Command("C") + Arguments(coordinatePairTripleSequence))
horizontalLine = Group(Command("H") + Arguments(coordinateSequence))
verticalLine = Group(Command("V") + Arguments(coordinateSequence))
drawToCommand = (
lineTo | moveTo | closePath | ellipticalArc | smoothQuadraticBezierCurveto |
quadraticBezierCurveto | smoothCurve | curve | horizontalLine | verticalLine
)
#~ number.debug = True
moveToDrawToCommands = moveTo + ZeroOrMore(drawToCommand)
svg = ZeroOrMore(moveToDrawToCommands)
svg.keepTabs = True
def profile():
import cProfile
p = cProfile.Profile()
p.enable()
ptest()
ptest()
ptest()
p.disable()
p.print_stats()
bpath = """M204.33 139.83 C196.33 133.33 206.68 132.82 206.58 132.58 C192.33 97.08 169.35
81.41 167.58 80.58 C162.12 78.02 159.48 78.26 160.45 76.97 C161.41 75.68 167.72 79.72 168.58
80.33 C193.83 98.33 207.58 132.33 207.58 132.33 C207.58 132.33 209.33 133.33 209.58 132.58
C219.58 103.08 239.58 87.58 246.33 81.33 C253.08 75.08 256.63 74.47 247.33 81.58 C218.58 103.58
210.34 132.23 210.83 132.33 C222.33 134.83 211.33 140.33 211.83 139.83 C214.85 136.81 214.83 145.83 214.83
145.83 C214.83 145.83 231.83 110.83 298.33 66.33 C302.43 63.59 445.83 -14.67 395.83 80.83 C393.24 85.79 375.83
105.83 375.83 105.83 C375.83 105.83 377.33 114.33 371.33 121.33 C370.3 122.53 367.83 134.33 361.83 140.83 C360.14 142.67
361.81 139.25 361.83 140.83 C362.33 170.83 337.76 170.17 339.33 170.33 C348.83 171.33 350.19 183.66 350.33 183.83 C355.83
190.33 353.83 191.83 355.83 194.83 C366.63 211.02 355.24 210.05 356.83 212.83 C360.83 219.83 355.99 222.72 357.33 224.83
C360.83 230.33 354.75 233.84 354.83 235.33 C355.33 243.83 349.67 240.73 349.83 244.33 C350.33 255.33 346.33 250.83 343.83 254.83
C336.33 266.83 333.46 262.38 332.83 263.83 C329.83 270.83 325.81 269.15 324.33 270.83 C320.83 274.83 317.33 274.83 315.83 276.33
C308.83 283.33 304.86 278.39 303.83 278.83 C287.83 285.83 280.33 280.17 277.83 280.33 C270.33 280.83 271.48 279.67 269.33 277.83
C237.83 250.83 219.33 211.83 215.83 206.83 C214.4 204.79 211.35 193.12 212.33 195.83 C214.33 201.33 213.33 250.33 207.83 250.33
C202.33 250.33 201.83 204.33 205.33 195.83 C206.43 193.16 204.4 203.72 201.79 206.83 C196.33 213.33 179.5 250.83 147.59 277.83
C145.42 279.67 146.58 280.83 138.98 280.33 C136.46 280.17 128.85 285.83 112.65 278.83 C111.61 278.39 107.58 283.33 100.49 276.33
C98.97 274.83 95.43 274.83 91.88 270.83 C90.39 269.15 86.31 270.83 83.27 263.83 C82.64 262.38 79.73 266.83 72.13 254.83 C69.6 250.83
65.54 255.33 66.05 244.33 C66.22 240.73 60.48 243.83 60.99 235.33 C61.08 233.84 54.91 230.33 58.45 224.83 C59.81 222.72 54.91 219.83
58.96 212.83 C60.57 210.05 49.04 211.02 59.97 194.83 C62 191.83 59.97 190.33 65.54 183.83 C65.69 183.66 67.06 171.33 76.69 170.33
C78.28 170.17 53.39 170.83 53.9 140.83 C53.92 139.25 55.61 142.67 53.9 140.83 C47.82 134.33 45.32 122.53 44.27 121.33 C38.19 114.33
39.71 105.83 39.71 105.83 C39.71 105.83 22.08 85.79 19.46 80.83 C-31.19 -14.67 114.07 63.59 118.22 66.33 C185.58 110.83 202 145.83
202 145.83 C202 145.83 202.36 143.28 203 141.83 C203.64 140.39 204.56 140.02 204.33 139.83 z"""
def ptest():
svg.parseString(bpath)
if __name__ == '__main__':
#~ from tests.test_pathdata import *
#~ unittest.main()
profile()
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version='1.0' standalone='no'?>
<!--Written in KDevelop (http://www.kdevelop.org/)-->
<svg baseProfile='full' contentScriptType='text/ecmascript' contentStyleType='text/css' height='4003.518px' onload='init()' preserveAspectRatio='xMidYMid meet' version='1.0' width='516.0px' xmlns='http://www.w3.org/2000/svg' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' xmlns:slice='http://www.reprap.org/slice' xmlns:xlink='http://www.w3.org/1999/xlink' zoomAndPan='magnify'>
<metadata>
<slice:layers id='sliceData' controlBoxHeight='720' controlBoxWidth='240' decimalPlacesCarried='4' edgeWidth='0.72' layerHeight='0.4' margin='20' marginTop='60' maxX='11.919' maxY='11.919' maxZ='10.0' minX='-11.919' minY='-11.919' minZ='-2.0' procedureName='carve' svgMinWidth='516' template='svg_layer' textHeight='22.5' unitScale='3.7' version='0.1' yAxisPointingUpward='true' />
</metadata>
<script type='text/ecmascript'>
<![CDATA[
globalMetadata = document.getElementsByTagNameNS('http://www.reprap.org/slice', 'layers')[0];
globalSliceMinX = globalMetadata.getAttribute('minX') * 1;
globalSliceMaxX = globalMetadata.getAttribute('maxX') * 1;
globalSliceMinY = globalMetadata.getAttribute('minY') * 1;
globalSliceMaxY = globalMetadata.getAttribute('maxY') * 1;
globalSliceMinZ = globalMetadata.getAttribute('minZ') * 1;
globalSliceMaxZ = globalMetadata.getAttribute('maxZ') * 1;
//Control var's
globalLayerIndex = 0; //Number of currently viewed layer (zero index)
globalLatitude = 60.0;
globalLongitude = 45.0;
globalControlBoxHeight = globalMetadata.getAttribute('controlBoxHeight') * 1;
globalControlBoxWidth = globalMetadata.getAttribute('controlBoxWidth') * 1;
globalSliding = false;
globalObserving = false
//Display var's
globalMargin = globalMetadata.getAttribute('margin') * 1;
globalMarginTop = globalMetadata.getAttribute('marginTop') * 1;
globalDimensionX = globalSliceMaxX - globalSliceMinX;
globalDimensionY = globalSliceMaxY - globalSliceMinY;
globalDimensionZ = globalSliceMaxZ - globalSliceMinZ;
magnitudeXYSquared = globalDimensionX * globalDimensionX + globalDimensionY * globalDimensionY;
globalDimensionMagnitude = Math.sqrt(magnitudeXYSquared + globalDimensionZ * globalDimensionZ);
globalGlobeTravelRadian = 0.0;
globalGlobeInnerObserverRadius = 0.0;
globalTextHeight = globalMetadata.getAttribute('textHeight') * 1.0;
globalUnitScale = globalMetadata.getAttribute('unitScale') * 1.0;
globalXOffset = globalControlBoxWidth + globalMargin + globalMargin;
globalYOffset = globalMargin + globalMarginTop;
globalZoomScale = 1.0; //Default 1:1 may need smaller scale for large objects
globalScale = globalUnitScale * globalZoomScale;
//Globals to be set in init
globalLayers = [];
globalSlider = 0;
globalThumb = 0;
globalThumbRadius = 0.0;
globalSliderWidthMinusDiameter = 0.0;
function changeScale(newScale) {
globalZoomScale = newScale;
globalScale = globalUnitScale * globalZoomScale;
if (globalZoomScale >=1) { //dont scale line thickness for large display scale
document.getElementById('layerData').setAttributeNS(null, 'stroke-width', 2.0 / (globalScale));
}
}
function changeScaleIso(newScale) {
changeScale(newScale);
viewIso();
}
function changeScaleLayer(newScale) {
changeScale(newScale);
viewLayer();
}
function changeScaleScroll(newScale) {
changeScale(newScale);
viewScroll();
}
function displayIso(latitude, longitude) {
latitude = Math.max(0.0, latitude);
latitude = Math.min(89.0, latitude);
globalLatitude = Math.round(latitude);
globalLongitude = Math.round((longitude + 540.0) % 360.0) - 180.0;
latitudeRadians = getRadians(globalLatitude);
longitudeWiddershinsRadians = getRadians(90.0 - globalLongitude);
unitX = Math.cos(longitudeWiddershinsRadians);
unitY = Math.sin(longitudeWiddershinsRadians);
latitudeOverLayers = globalDimensionZ / globalLayers.length;
globalOneOverUnitScaleString = (1.0 / globalUnitScale).toString();
yScale = -1.0 * globalScale * Math.cos(latitudeRadians);
scaleRotateCenterString = ' scale(' + globalScale + ', ' + yScale + ')';
scaleRotateCenterString += ' rotate(' + (-1.0 * globalLongitude).toString() + ')';
centerX = (-0.5 * globalDimensionX - globalSliceMinX).toString();
centerY = (-0.5 * globalDimensionY - globalSliceMinY).toString();
scaleRotateCenterString += ' translate(' + centerX + ', ' + centerY + ')';
x = 0.5 * globalDimensionMagnitude * globalScale + globalXOffset;
halfLengthMinusOne = 0.5 * (globalLayers.length - 1);
for (var i in globalLayers) {
latitudeZSin = Math.sin(latitudeRadians) * (i - halfLengthMinusOne) * latitudeOverLayers
y = globalScale * (0.5 * globalDimensionMagnitude - latitudeZSin) + globalYOffset;
// y = globalDimensionY * globalScale + globalYOffset - i * unitY * latitudeOverLayers;
transformString = ' translate(' + x + ', ' + y + ')';
globalLayers[i].setAttributeNS(null, 'transform', transformString + scaleRotateCenterString);
}
setText('latitudeIso', 'Latitude: ' + globalLatitude.toString() + '°');
setText('longitudeIso', 'Longitude: ' + globalLongitude.toString() + '°');
globeMoveableRadius = globalGlobeTravelRadian * latitudeRadians + globalGlobeInnerObserverRadius;
globalObserver.setAttribute('cx', globalGlobeCenterX + unitX * globeMoveableRadius);
globalObserver.setAttribute('cy', globalGlobeCenterY - unitY * globeMoveableRadius);
}
function displayIsoByLatitude(latitude) {
displayIso(latitude, globalLongitude);
}
function displayIsoByLongitude(longitude) {
displayIso(globalLatitude, longitude);
}
function displayLayer(layerNum) {
if (globalLayers.length <= 1) {
document.getElementById('maxIndexLayer').setAttributeNS(null, 'visibility', 'hidden');
document.getElementById('minIndexLayer').setAttributeNS(null, 'visibility', 'hidden');
globalSlider.setAttributeNS(null, 'visibility', 'hidden');
globalThumb.setAttributeNS(null, 'visibility', 'hidden');
}
if (layerNum <= 0) {
document.getElementById('decreaseLayerIndex').setAttributeNS(null, 'visibility', 'hidden');
}
else {
document.getElementById('decreaseLayerIndex').setAttributeNS(null, 'visibility', 'visible');
}
if (layerNum >= globalLayers.length - 1) {
document.getElementById('increaseLayerIndex').setAttributeNS(null, 'visibility', 'hidden');
}
else {
document.getElementById('increaseLayerIndex').setAttributeNS(null, 'visibility', 'visible');
}
if (layerNum < 0 || layerNum >= globalLayers.length) {
return
}
globalLayers[globalLayerIndex].setAttributeNS(null, 'visibility', 'hidden');
currentLayerElement = globalLayers[layerNum]
currentLayerElement.setAttributeNS(null, 'visibility', 'visible');
globalLayerIndex = layerNum;
setText('currentIndexLayer', 'Layer: ' + globalLayerIndex.toString() + ', ' + currentLayerElement.getAttribute('id'));
//Slider
if (!globalSliding) {
placeThumb(globalSliderWidthMinusDiameter / (globalLayers.length - 1) * globalLayerIndex + globalThumbRadius);
}
}
function getDegrees(radians) {
return radians / Math.PI * 180.0;
}
function getRadians(degrees) {
return degrees / 180.0 * Math.PI;
}
function getScaleTransformString(scale) {
scaleTransformString = 'scale(' + scale + ' ' + (scale * - 1)
return scaleTransformString + ') translate(' + (globalSliceMinX * - 1) + ' ' + (globalSliceMinY * - 1) + ')';
}
function getWidth() {
return (globalDimensionX * globalScale) + globalXOffset
}
function hideElements(elementNames) {
for (var elementNameIndex in elementNames) {
document.getElementById(elementNames[elementNameIndex]).setAttributeNS(null, 'visibility', 'hidden');
}
}
function init() {
//Find only layer groups
globe = document.getElementById('globe');
globalGlobeCenterX = globe.getAttribute('cx') * 1;
globalGlobeCenterXWindow = globalGlobeCenterX + globalMargin;
globalGlobeCenterY = globe.getAttribute('cy') * 1;
globalGlobeCenterYWindow = globalGlobeCenterX + globalMarginTop;
globalObserver = document.getElementById('observer');
globeRadius = globe.getAttribute('r') * 1.0;
observerRadius = globalObserver.getAttribute('r') * 1.0;
globalGlobeInnerObserverRadius = document.getElementById('cover').getAttribute('r') * 1.0 + observerRadius;
globalGlobeTravelRadian = getDegrees(globeRadius - globalGlobeInnerObserverRadius - observerRadius) / 89.0;
globalSlider = document.getElementById('slider');
globalSliderX = globalSlider.getAttribute('x') * 1;
globalSliderXWindow = globalSliderX + globalMargin
globalThumb = document.getElementById('thumb');
globalThumbRadius = globalThumb.getAttribute('r') * 1;
globalSliderWidthMinusRadius = globalSlider.getAttribute('width') * 1 - globalThumbRadius;
globalSliderWidthMinusDiameter = globalSliderWidthMinusRadius - globalThumbRadius;
var allGroups = document.getElementsByTagName('g');
for (var i = 0; i < allGroups.length; i++) {
if (allGroups[i].id.indexOf('z:') == 0) {
globalLayers.push(allGroups[i]);
}
}
//Slider
layerControlBox = document.getElementById('layerControlBox');
layerControlBox.addEventListener('mouseup', sliderUp, false);
layerControlBox.addEventListener('mousemove', sliderMove, false);
globalSlider.addEventListener('mousedown', sliderDownMove, false);
globalThumb.addEventListener('mousedown', sliderDownMove, false);
//Observer
isoControlBox = document.getElementById('isoControlBox');
isoControlBox.addEventListener('mouseup', observerUp, false);
isoControlBox.addEventListener('mousemove', observerMove, false);
globe.addEventListener('mousedown', observerDownMove, false);
globalObserver.addEventListener('mousedown', observerDownMove, false);
//Control box data
setText('maxIndexLayer', globalLayers.length - 1);
changeScaleLayer(globalZoomScale);
}
function observerDown(event) {
globalObserving = true;
}
function observerDownMove(event) {
globalObserving = true;
observerMove(event);
}
function observerMove(event) {
if (!globalObserving) {
return;
}
observerX = event.clientX - globalGlobeCenterXWindow + window.pageXOffset;
observerY = event.clientY - globalGlobeCenterYWindow + window.pageYOffset;
distanceFromCenter = Math.sqrt(observerX * observerX + observerY * observerY);
latitudeRadians = (distanceFromCenter - globalGlobeInnerObserverRadius) / globalGlobeTravelRadian;
longitudeRadians = Math.atan2(observerX, -observerY);
displayIso(getDegrees(latitudeRadians), getDegrees(longitudeRadians));
}
function observerUp(event) {
globalObserving = false;
}
function placeThumb(value) {
if (globalLayers.length > 1) {
globalThumb.setAttribute('cx', globalSliderX + value);
}
}
function setScaleText(scaleID) {
setText(scaleID, ': ' + globalZoomScale);
}
function setSVG(width, height) {
rootSVG = document.getElementsByTagName('svg')[0];
svgMinWidth = globalMetadata.getAttribute('svgMinWidth') * 1;
height = Math.max(globalControlBoxHeight + globalMargin + globalMarginTop, height);
width = Math.max(svgMinWidth, width);
rootSVG.setAttributeNS(null, 'width', width + 'px')
rootSVG.setAttributeNS(null, 'height', height + 'px')
}
function setText(id, str) {
e = document.getElementById(id)
if (e != null)
e.firstChild.nodeValue = str;
}
function showElements(elementNames) {
for (var elementNameIndex in elementNames) {
document.getElementById(elementNames[elementNameIndex]).setAttributeNS(null, 'visibility', 'visible');
}
}
function sliderDown(event) {
globalSliding = true;
}
function sliderDownMove(event) {
globalSliding = true;
sliderMove(event);
}
function sliderMove(event) {
if (!globalSliding) {
return;
}
value = event.clientX - globalSliderXWindow + window.pageXOffset;
if (value >= globalThumbRadius && value <= globalSliderWidthMinusRadius) {
placeThumb(value);
zoneWidth = globalSliderWidthMinusDiameter / (globalLayers.length);
newLayer = Math.round((value - globalThumbRadius - 0.5 * zoneWidth) / zoneWidth)
if (newLayer != globalLayerIndex) {
displayLayer(newLayer)
}
}
}
function sliderUp(event) {
globalSliding = false;
}
function viewIso() {
height = (globalDimensionMagnitude * globalScale) + globalYOffset;
setSVG((globalDimensionMagnitude * globalScale) + globalXOffset, height);
for (var i in globalLayers) {
// globalLayers[i].setAttributeNS(null, 'transform', 'translate(' + globalMargin + ' ' + height + ')');
globalLayers[i].setAttributeNS(null, 'visibility', 'visible');
globalLayers[i].getElementsByTagName('text')[0].setAttributeNS(null, 'visibility', 'hidden');
pathElements = globalLayers[i].getElementsByTagName('path');
for (var pathElementIndex = 0; pathElementIndex < pathElements.length; pathElementIndex++) {
pathElements[pathElementIndex].setAttributeNS(null, 'transform', '');
}
}
//show control box
hideElements(['decreaseLayerIndex', 'increaseLayerIndex', 'isoViewButton', 'layerControlBox', 'layerViewLabel', 'scrollControlBox', 'scrollViewLabel']);
showElements(['isoControlBox', 'isoViewLabel', 'layerViewButton', 'scrollViewButton']);
displayIso(globalLatitude, globalLongitude);
setScaleText('scaleIso');
}
function viewLayer() {
//Set svg size and view port
height = (globalDimensionY * globalScale) + globalYOffset;
setSVG(getWidth(), height);
//move and hide all layers
for (var i in globalLayers) {
globalLayers[i].setAttributeNS(null, 'transform', 'translate(' + globalXOffset + ' ' + height + ')');
globalLayers[i].setAttributeNS(null, 'visibility', 'hidden');
globalLayers[i].getElementsByTagName('text')[0].setAttributeNS(null, 'visibility', 'hidden');
transform = getScaleTransformString(globalScale);
pathElements = globalLayers[i].getElementsByTagName('path');
for (var pathElementIndex = 0; pathElementIndex < pathElements.length; pathElementIndex++) {
pathElements[pathElementIndex].setAttributeNS(null, 'transform', transform);
}
}
//show control box
hideElements(['isoControlBox', 'isoViewLabel', 'layerViewButton', 'scrollControlBox', 'scrollViewLabel'])
showElements(['isoViewButton', 'layerControlBox', 'layerViewLabel', 'scrollViewButton'])
//show current layer
displayLayer(globalLayerIndex);
setScaleText('scaleLayer');
}
function viewScroll() {
//Set svg size and view port
yDimensionScale = globalDimensionY * globalScale
singleHeight = (globalMargin + yDimensionScale + globalTextHeight)
height = globalLayers.length * singleHeight + globalMargin + globalMargin + globalYOffset;
setSVG(getWidth(), height);
//move and show all layers
for (var i in globalLayers) {
x = globalXOffset;
y = i * singleHeight + yDimensionScale + globalYOffset;
transform = getScaleTransformString(globalScale);
globalLayers[i].setAttributeNS(null, 'transform', 'translate(' + x + ', ' + y + ')');
pathElements = globalLayers[i].getElementsByTagName('path');
for (var pathElementIndex = 0; pathElementIndex < pathElements.length; pathElementIndex++) {
pathElements[pathElementIndex].setAttributeNS(null, 'transform', transform);
}
globalLayers[i].setAttributeNS(null, 'visibility', 'visible');
globalLayers[i].getElementsByTagName('text')[0].setAttributeNS(null, 'visibility', 'visible');
}
//show control box
hideElements(['isoControlBox', 'isoViewLabel', 'layerControlBox', 'layerViewLabel', 'scrollViewButton'])
showElements(['isoViewButton', 'layerViewButton', 'scrollControlBox', 'scrollViewLabel'])
setScaleText('scaleScroll');
}
]]>
</script>
<title >belt_pulley3.stl - Slice Layers</title>
<!--Begin Layer Data -->
<g id='layerData' fill='#556B2F' fill-rule='evenodd' font-family='Arial' font-size='15px' font-weight='bold' inkscape:groupmode='layer' inkscape:label='Slice Layers' stroke='#00F' stroke-width='0.54px'>
<!--id='sliceElementTemplate' must be there or else the slice template will not be found-->
<g id='z:-1.8' inkscape:groupmode='layer' inkscape:label='Layer 0, z:-1.8' transform='translate(280.0, 168.2006)'>
<!--id='layerTextTemplate' must be there so that the text could be removed if necessary-->
<text id='layerTextTemplate-1.8' fill='#000' stroke='none' y='15' >Layer 0, z:-1.8</text>
<path d='M -11.6336 1.4126 L -11.719 0.0 L -11.6336 -1.4126 L -11.3785 -2.8045 L -10.9575 -4.1556 L -10.3766 -5.4461 L -9.6445 -6.6571 L -8.7718 -7.7711 L -7.7711 -8.7718 L -6.6571 -9.6445 L -5.4461 -10.3766 L -4.1556 -10.9575 L -2.8045 -11.3785 L -1.4126 -11.6336 L 0.0 -11.719 L 1.4126 -11.6336 L 2.8045 -11.3785 L 4.1556 -10.9575 L 5.4461 -10.3766 L 6.6571 -9.6445 L 7.7711 -8.7718 L 8.7718 -7.7711 L 9.6445 -6.6571 L 10.3766 -5.4461 L 10.9575 -4.1556 L 11.3785 -2.8045 L 11.6336 -1.4126 L 11.719 0.0 L 11.6336 1.4126 L 11.3785 2.8045 L 10.9575 4.1556 L 10.3766 5.4461 L 9.6445 6.6571 L 8.7718 7.7711 L 7.7711 8.7718 L 6.6571 9.6445 L 5.4461 10.3766 L 4.1556 10.9575 L 2.8045 11.3785 L 1.4126 11.6336 L 0.0 11.719 L -1.4126 11.6336 L -2.8045 11.3785 L -4.1556 10.9575 L -5.4461 10.3766 L -6.6571 9.6445 L -7.7711 8.7718 L -8.7718 7.7711 L -9.6445 6.6571 L -10.3766 5.4461 L -10.9575 4.1556 L -11.3785 2.8045 z M -4.0 -1.5 L -4.0 1.5 L -6.8343 1.5 L -6.6072 2.3119 L -6.3068 3.0372 L -5.9271 3.7242 L -5.4728 4.3644 L -4.9497 4.9497 L -4.3644 5.4728 L -3.7242 5.9271 L -3.0372 6.3068 L -2.3119 6.6072 L -1.5 6.8343 L -1.5 4.0 L 1.5 4.0 L 1.5 6.8343 L 2.3119 6.6072 L 3.0372 6.3068 L 3.7242 5.9271 L 4.3644 5.4728 L 4.9497 4.9497 L 5.4728 4.3644 L 5.9271 3.7242 L 6.3068 3.0372 L 6.6072 2.3119 L 6.8343 1.5 L 4.0 1.5 L 4.0 -1.5 L 6.8343 -1.5 L 6.6072 -2.3119 L 6.3068 -3.0372 L 5.9271 -3.7242 L 5.4728 -4.3644 L 4.9497 -4.9497 L 4.3644 -5.4728 L 3.7242 -5.9271 L 3.0372 -6.3068 L 2.3119 -6.6072 L 1.5 -6.8343 L 1.5 -4.0 L -1.5 -4.0 L -1.5 -6.8343 L -2.3119 -6.6072 L -3.0372 -6.3068 L -3.7242 -5.9271 L -4.3644 -5.4728 L -4.9497 -4.9497 L -5.4728 -4.3644 L -5.9271 -3.7242 L -6.3068 -3.0372 L -6.6072 -2.3119 L -6.8343 -1.5 z' transform='scale(3.7, -3.7) translate(11.919, 11.919)' />
</g>
<g id='z:-1.4' inkscape:groupmode='layer' inkscape:label='Layer 1, z:-1.4' transform='translate(280.0, 298.9012)'>
<!--id='layerTextTemplate' must be there so that the text could be removed if necessary-->
<text id='layerTextTemplate-1.4' fill='#000' stroke='none' y='15' >Layer 1, z:-1.4</text>
<path d='M -11.2365 1.3644 L -11.319 0.0 L -11.2365 -1.3644 L -10.9901 -2.7088 L -10.5835 -4.0138 L -10.0224 -5.2602 L -9.3154 -6.4299 L -8.4724 -7.5059 L -7.5059 -8.4724 L -6.4299 -9.3154 L -5.2602 -10.0224 L -4.0138 -10.5835 L -2.7088 -10.9901 L -1.3644 -11.2365 L 0.0 -11.319 L 1.3644 -11.2365 L 2.7088 -10.9901 L 4.0138 -10.5835 L 5.2602 -10.0224 L 6.4299 -9.3154 L 7.5059 -8.4724 L 8.4724 -7.5059 L 9.3154 -6.4299 L 10.0224 -5.2602 L 10.5835 -4.0138 L 10.9901 -2.7088 L 11.2365 -1.3644 L 11.319 0.0 L 11.2365 1.3644 L 10.9901 2.7088 L 10.5835 4.0138 L 10.0224 5.2602 L 9.3154 6.4299 L 8.4724 7.5059 L 7.5059 8.4724 L 6.4299 9.3154 L 5.2602 10.0224 L 4.0138 10.5835 L 2.7088 10.9901 L 1.3644 11.2365 L 0.0 11.319 L -1.3644 11.2365 L -2.7088 10.9901 L -4.0138 10.5835 L -5.2602 10.0224 L -6.4299 9.3154 L -7.5059 8.4724 L -8.4724 7.5059 L -9.3154 6.4299 L -10.0224 5.2602 L -10.5835 4.0138 L -10.9901 2.7088 z M -4.0 -1.5 L -4.0 1.5 L -6.8343 1.5 L -6.6072 2.3119 L -6.3068 3.0372 L -5.9271 3.7242 L -5.4728 4.3644 L -4.9497 4.9497 L -4.3644 5.4728 L -3.7242 5.9271 L -3.0372 6.3068 L -2.3119 6.6072 L -1.5 6.8343 L -1.5 4.0 L 1.5 4.0 L 1.5 6.8343 L 2.3119 6.6072 L 3.0372 6.3068 L 3.7242 5.9271 L 4.3644 5.4728 L 4.9497 4.9497 L 5.4728 4.3644 L 5.9271 3.7242 L 6.3068 3.0372 L 6.6072 2.3119 L 6.8343 1.5 L 4.0 1.5 L 4.0 -1.5 L 6.8343 -1.5 L 6.6072 -2.3119 L 6.3068 -3.0372 L 5.9271 -3.7242 L 5.4728 -4.3644 L 4.9497 -4.9497 L 4.3644 -5.4728 L 3.7242 -5.9271 L 3.0372 -6.3068 L 2.3119 -6.6072 L 1.5 -6.8343 L 1.5 -4.0 L -1.5 -4.0 L -1.5 -6.8343 L -2.3119 -6.6072 L -3.0372 -6.3068 L -3.7242 -5.9271 L -4.3644 -5.4728 L -4.9497 -4.9497 L -5.4728 -4.3644 L -5.9271 -3.7242 L -6.3068 -3.0372 L -6.6072 -2.3119 L -6.8343 -1.5 z' transform='scale(3.7, -3.7) translate(11.919, 11.919)' />
</g>
</g>
<!--End Layer Data-->
<!--beginningOfControlSection='true' must be there or else the control boxes will be carved-->
<g id='controls' beginningOfControlSection='true' inkscape:groupmode='layer' inkscape:label='Controls'>
<!--id='isoControlBox' must be there so that the controls could be removed if necessary-->
<g id='isoControlBox' fill='#000' font-family='Arial' font-size='15px' font-weight='bold' transform='translate(20, 60)' visibility='hidden'>
<rect fill='silver' height='720' stroke='gray' stroke-width='4px' width='240' />
<circle id='globe' cx='120' cy='120' fill='gray' r='100' />
<circle id='cover' cx='120' cy='120' fill='silver' r='33' />
<circle id='observer' fill='darkslateblue' r='12' />
<text id='latitudeIso' x='20' y='260' >Latitude</text>
<text fill='darkslateblue' onclick='displayIsoByLatitude(globalLatitude-1.0)' x='198' y='260' >&lt;</text>
<text fill='darkslateblue' onclick='displayIsoByLatitude(globalLatitude+1.0)' x='213' y='260' >&gt;</text>
<text id='longitudeIso' x='20' y='280' >Longitude</text>
<text fill='darkslateblue' onclick='displayIsoByLongitude(globalLongitude-1.0)' x='198' y='280' >&lt;</text>
<text fill='darkslateblue' onclick='displayIsoByLongitude(globalLongitude+1.0)' x='213' y='280' >&gt;</text>
<text x='20' y='300' >Scale</text>
<text id='scaleIso' x='65' y='300' >1</text>
<text fill='darkslateblue' onclick='changeScaleIso(globalZoomScale/2)' x='198' y='300' >&lt;</text>
<text fill='darkslateblue' onclick='changeScaleIso(globalZoomScale*2)' x='213' y='300' >&gt;</text>
<g transform='translate(20, 340)'>
<text >Min</text>
<text id='minXIso' y='20' >X: -11.919 mm</text>
<text id='minYIso' y='40' >Y: -11.919 mm</text>
<text id='minZIso' y='60' >Z: -2.0 mm</text>
</g>
<g transform='translate(20, 440)'>
<text >Max</text>
<text id='maxXIso' y='20' >X: 11.919 mm</text>
<text id='maxYIso' y='40' >Y: 11.919 mm</text>
<text id='maxZIso' y='60' >Z: 10.0 mm</text>
</g>
<g transform='translate(20, 540)'>
<text >Dimension</text>
<text id='dimXIso' y='20' >X: 23.838 mm</text>
<text id='dimYIso' y='40' >Y: 23.838 mm</text>
<text id='dimZIso' y='60' >Z: 12.0 mm</text>
</g>
<g transform='translate(20, 640)'>
<text >Statistics</text>
<text id='layerHeightIso' y='20' >Layer Height: 0.4 mm</text>
<text id='numberOfLayersIso' y='40' >Number of Layers: 30</text>
<text id='volumeIso' y='60' >Volume: 1.8836 cm3</text>
</g>
</g>
<!--id='layerControlBox' must be there so that the controls could be removed if necessary-->
<g id='layerControlBox' fill='#000' font-family='Arial' font-size='15px' font-weight='bold' transform='translate(20, 60)' visibility='hidden'>
<rect fill='silver' height='720' stroke='gray' stroke-width='4px' width='240' />
<path d='M 66 164 h76 v6 l18 -9 l-18 -9 v6 h-70 v-70 h6 l-9 -18 l-9 18 h6 z' stroke-width='0' />
<!--<path stroke='#000' stroke-width='3' d='M 20 20 h5 l-5 -10 l-5 10 h5 v35 h35 v-5 l10 5 l-10 5 v-5 h-35 z'/>-->
<text text-anchor='middle' x='68' y='64' >Y</text>
<text x='165' y='166' >X</text>
<text id='minIndexLayer' x='20' y='245' >0</text>
<rect id='slider' fill='gray' height='24' width='170' x='32' y='230' />
<circle id='thumb' cx='42' cy='242' fill='darkslateblue' r='12' />
<text id='maxIndexLayer' x='203' y='245' >1</text>
<text id='currentIndexLayer' x='20' y='280' >Layer</text>
<text id='decreaseLayerIndex' fill='darkslateblue' onclick='displayLayer(globalLayerIndex-1)' x='198' y='280' >&lt;</text>
<text id='increaseLayerIndex' fill='darkslateblue' onclick='displayLayer(globalLayerIndex+1)' x='213' y='280' >&gt;</text>
<text x='20' y='300' >Scale</text>
<text id='scaleLayer' x='65' y='300' >1</text>
<text fill='darkslateblue' onclick='changeScaleLayer(globalZoomScale/2)' x='198' y='300' >&lt;</text>
<text fill='darkslateblue' onclick='changeScaleLayer(globalZoomScale*2)' x='213' y='300' >&gt;</text>
<g transform='translate(20, 340)'>
<text >Min</text>
<text id='minXLayer' y='20' >X: -11.919 mm</text>
<text id='minYLayer' y='40' >Y: -11.919 mm</text>
<text id='minZLayer' y='60' >Z: -2.0 mm</text>
</g>
<g transform='translate(20, 440)'>
<text >Max</text>
<text id='maxXLayer' y='20' >X: 11.919 mm</text>
<text id='maxYLayer' y='40' >Y: 11.919 mm</text>
<text id='maxZLayer' y='60' >Z: 10.0 mm</text>
</g>
<g transform='translate(20, 540)'>
<text >Dimension</text>
<text id='dimXLayer' y='20' >X: 23.838 mm</text>
<text id='dimYLayer' y='40' >Y: 23.838 mm</text>
<text id='dimZLayer' y='60' >Z: 12.0 mm</text>
</g>
<g transform='translate(20, 640)'>
<text >Statistics</text>
<text id='layerHeightLayer' y='20' >Layer Height: 0.4 mm</text>
<text id='numberOfLayersLayer' y='40' >Number of Layers: 30</text>
<text id='volumeLayer' y='60' >Volume: 1.8836 cm3</text>
</g>
</g>
<!--id='scrollControlBox' must be there so that the controls could be removed if necessary-->
<g id='scrollControlBox' fill='#000' font-family='Arial' font-size='15px' font-weight='bold' transform='translate(20, 60)' visibility='visible'>
<rect fill='silver' height='720' stroke='gray' stroke-width='4px' width='240' />
<path d='M 66 164 h76 v6 l18 -9 l-18 -9 v6 h-70 v-70 h6 l-9 -18 l-9 18 h6 z' stroke-width='0' />
<text text-anchor='middle' x='68' y='64' >Y</text>
<text x='165' y='166' >X</text>
<text x='20' y='300' >Scale</text>
<text id='scaleScroll' x='65' y='300' >: 1</text>
<text fill='darkslateblue' onclick='changeScaleScroll(globalZoomScale/2)' x='198' y='300' >&lt;</text>
<text fill='darkslateblue' onclick='changeScaleScroll(globalZoomScale*2)' x='213' y='300' >&gt;</text>
<g transform='translate(20, 340)'>
<text >Min</text>
<text id='minXScroll' y='20' >X: -11.919 mm</text>
<text id='minYScroll' y='40' >Y: -11.919 mm</text>
<text id='minZScroll' y='60' >Z: -2.0 mm</text>
</g>
<g transform='translate(20, 440)'>
<text >Max</text>
<text id='maxXScroll' y='20' >X: 11.919 mm</text>
<text id='maxYScroll' y='40' >Y: 11.919 mm</text>
<text id='maxZScroll' y='60' >Z: 10.0 mm</text>
</g>
<g transform='translate(20, 540)'>
<text >Dimension</text>
<text id='dimXScroll' y='20' >X: 23.838 mm</text>
<text id='dimYScroll' y='40' >Y: 23.838 mm</text>
<text id='dimZScroll' y='60' >Z: 12.0 mm</text>
</g>
<g transform='translate(20, 640)'>
<text >Statistics</text>
<text id='layerHeightScroll' y='20' >Layer Height: 0.4 mm</text>
<text id='numberOfLayersScroll' y='40' >Number of Layers: 30</text>
<text id='volumeScroll' y='60' >Volume: 1.8836 cm3</text>
</g>
</g>
<text id='isoViewButton' fill='darkslateblue' font-size='18px' font-weight='normal' onclick='viewIso()' text-anchor='middle' visibility='visible' x='80' y='30' >[Iso View]</text>
<text id='isoViewLabel' fill='darkslateblue' font-size='24px' font-weight='bold' text-anchor='middle' visibility='hidden' x='76' y='30' >Iso View</text>
<text id='layerViewButton' fill='darkslateblue' font-size='18px' font-weight='normal' onclick='viewLayer()' text-anchor='middle' visibility='visible' x='240' y='30' >[Layer View]</text>
<text id='layerViewLabel' fill='darkslateblue' font-size='24px' font-weight='bold' text-anchor='middle' visibility='hidden' x='236' y='30' >Layer View</text>
<text id='scrollViewButton' fill='darkslateblue' font-size='18px' font-weight='normal' onclick='viewScroll()' text-anchor='middle' visibility='hidden' x='400' y='30' >[Scroll View]</text>
<text id='scrollViewLabel' fill='darkslateblue' font-size='24px' font-weight='bold' text-anchor='middle' visibility='visible' x='396' y='30' >Scroll View</text>
</g>
</svg>
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg width="23.838" height="23.838" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:slic3r="http://slic3r.org/namespaces/slic3r">
<!--
Generated using Slic3r 0.7.2b
http://slic3r.org/
-->
<g id="layer0" slic3r:z="0.03" >
<polygon slic3r:type="contour" points="11.919,23.457361 10.528202,23.373235 9.157683,23.122118 7.827451,22.707599 6.556855,22.135663 5.364459,21.414874 4.267648,20.555585 3.282414,19.570351 2.423125,18.47354 1.702336,17.281144 1.1304,16.010548 0.715881,14.680316 0.464764,13.309797 0.380639,11.919 0.464764,10.528202 0.715881,9.157683 1.1304,7.827451 1.702336,6.556855 2.423125,5.364459 3.282414,4.267648 4.267648,3.282414 5.364459,2.423125 6.556855,1.702336 7.827451,1.1304 9.157683,0.715881 10.528202,0.464764 11.919,0.380639 13.309797,0.464764 14.680316,0.715881 16.010548,1.1304 17.281144,1.702336 18.47354,2.423125 19.570351,3.282414 20.555585,4.267648 21.414874,5.364459 22.135663,6.556855 22.707599,7.827451 23.122118,9.157683 23.373235,10.528202 23.457361,11.919 23.373235,13.309797 23.122118,14.680316 22.707599,16.010548 22.135663,17.281144 21.414874,18.47354 20.555585,19.570351 19.570351,20.555585 18.47354,21.414874 17.281144,22.135663 16.010548,22.707599 14.680316,23.122118 13.309797,23.373235" style="fill: white" />
<polygon slic3r:type="hole" points="10.769,19.167808 10.769,16.269 13.069,16.269 13.069,19.167808 13.554669,19.085248 14.346729,18.85706 15.108288,18.541616 15.829724,18.14289 16.501994,17.665893 17.116627,17.116627 17.665893,16.501994 18.14289,15.829724 18.541616,15.108288 18.85706,14.346729 19.085248,13.554669 19.167808,13.069 16.269,13.069 16.269,10.769 19.167808,10.769 19.085248,10.283331 18.85706,9.491271 18.541616,8.729712 18.14289,8.008276 17.665893,7.336006 17.116627,6.721373 16.501994,6.172107 15.829724,5.69511 15.108288,5.296384 14.346729,4.98094 13.554669,4.752752 13.069,4.670192 13.069,7.569 10.769,7.569 10.769,4.670192 10.283331,4.752752 9.491271,4.98094 8.729712,5.296384 8.008276,5.69511 7.336006,6.172107 6.721373,6.721373 6.172107,7.336006 5.69511,8.008276 5.296384,8.729712 4.98094,9.491271 4.752752,10.283331 4.670192,10.769 7.569,10.769 7.569,13.069 4.670192,13.069 4.752752,13.554669 4.98094,14.346729 5.296384,15.108288 5.69511,15.829724 6.172107,16.501994 6.721373,17.116627 7.336006,17.665893 8.008276,18.14289 8.729712,18.541616 9.491271,18.85706 10.283331,19.085248" style="fill: black" />
</g>
<g id="layer1" slic3r:z="0.11">
<polygon slic3r:type="contour" points="11.919,23.377361 10.537846,23.293818 9.176828,23.04444 7.855816,22.632796 6.594036,22.06483 5.409906,21.349036 4.320698,20.495704 3.342295,19.517301 2.488963,18.428093 1.773169,17.243963 1.205203,15.982183 0.793559,14.661171 0.544181,13.300153 0.460639,11.919 0.544181,10.537846 0.793559,9.176828 1.205203,7.855816 1.773169,6.594036 2.488963,5.409906 3.342295,4.320698 4.320698,3.342295 5.409906,2.488963 6.594036,1.773169 7.855816,1.205203 9.176828,0.793559 10.537846,0.544181 11.919,0.460639 13.300153,0.544181 14.661171,0.793559 15.982183,1.205203 17.243963,1.773169 18.428093,2.488963 19.517301,3.342295 20.495704,4.320698 21.349036,5.409906 22.06483,6.594036 22.632796,7.855816 23.04444,9.176828 23.293818,10.537846 23.377361,11.919 23.293818,13.300153 23.04444,14.661171 22.632796,15.982183 22.06483,17.243963 21.349036,18.428093 20.495704,19.517301 19.517301,20.495704 18.428093,21.349036 17.243963,22.06483 15.982183,22.632796 14.661171,23.04444 13.300153,23.293818" style="fill: white" />
<polygon slic3r:type="hole" points="10.769,19.167808 10.769,16.269 13.069,16.269 13.069,19.167808 13.554669,19.085248 14.346729,18.85706 15.108288,18.541616 15.829724,18.14289 16.501994,17.665893 17.116627,17.116627 17.665893,16.501994 18.14289,15.829724 18.541616,15.108288 18.85706,14.346729 19.085248,13.554669 19.167808,13.069 16.269,13.069 16.269,10.769 19.167808,10.769 19.085248,10.283331 18.85706,9.491271 18.541616,8.729712 18.14289,8.008276 17.665893,7.336006 17.116627,6.721373 16.501994,6.172107 15.829724,5.69511 15.108288,5.296384 14.346729,4.98094 13.554669,4.752752 13.069,4.670192 13.069,7.569 10.769,7.569 10.769,4.670192 10.283331,4.752752 9.491271,4.98094 8.729712,5.296384 8.008276,5.69511 7.336006,6.172107 6.721373,6.721373 6.172107,7.336006 5.69511,8.008276 5.296384,8.729712 4.98094,9.491271 4.752752,10.283331 4.670192,10.769 7.569,10.769 7.569,13.069 4.670192,13.069 4.752752,13.554669 4.98094,14.346729 5.296384,15.108288 5.69511,15.829724 6.172107,16.501994 6.721373,17.116627 7.336006,17.665893 8.008276,18.14289 8.729712,18.541616 9.491271,18.85706 10.283331,19.085248" style="fill: black" />
</g>
<g id="layer2" slic3r:z="0.21">
<polygon slic3r:type="contour" points="11.919,23.277361 10.549901,23.194547 9.200759,22.947343 7.891273,22.539291 6.640509,21.976288 5.466718,21.266742 4.387008,20.42085 3.417149,19.450991 2.571257,18.371281 1.861711,17.19749 1.298708,15.946726 0.890656,14.63724 0.643452,13.288098 0.560639,11.919 0.643452,10.549901 0.890656,9.200759 1.298708,7.891273 1.861711,6.640509 2.571257,5.466718 3.417149,4.387008 4.387008,3.417149 5.466718,2.571257 6.640509,1.861711 7.891273,1.298708 9.200759,0.890656 10.549901,0.643452 11.919,0.560639 13.288098,0.643452 14.63724,0.890656 15.946726,1.298708 17.19749,1.861711 18.371281,2.571257 19.450991,3.417149 20.42085,4.387008 21.266742,5.466718 21.976288,6.640509 22.539291,7.891273 22.947343,9.200759 23.194547,10.549901 23.277361,11.919 23.194547,13.288098 22.947343,14.63724 22.539291,15.946726 21.976288,17.19749 21.266742,18.371281 20.42085,19.450991 19.450991,20.42085 18.371281,21.266742 17.19749,21.976288 15.946726,22.539291 14.63724,22.947343 13.288098,23.194547" style="fill: white" />
<polygon slic3r:type="hole" points="10.769,19.167808 10.769,16.269 13.069,16.269 13.069,19.167808 13.554669,19.085248 14.346729,18.85706 15.108288,18.541616 15.829724,18.14289 16.501994,17.665893 17.116627,17.116627 17.665893,16.501994 18.14289,15.829724 18.541616,15.108288 18.85706,14.346729 19.085248,13.554669 19.167808,13.069 16.269,13.069 16.269,10.769 19.167808,10.769 19.085248,10.283331 18.85706,9.491271 18.541616,8.729712 18.14289,8.008276 17.665893,7.336006 17.116627,6.721373 16.501994,6.172107 15.829724,5.69511 15.108288,5.296384 14.346729,4.98094 13.554669,4.752752 13.069,4.670192 13.069,7.569 10.769,7.569 10.769,4.670192 10.283331,4.752752 9.491271,4.98094 8.729712,5.296384 8.008276,5.69511 7.336006,6.172107 6.721373,6.721373 6.172107,7.336006 5.69511,8.008276 5.296384,8.729712 4.98094,9.491271 4.752752,10.283331 4.670192,10.769 7.569,10.769 7.569,13.069 4.670192,13.069 4.752752,13.554669 4.98094,14.346729 5.296384,15.108288 5.69511,15.829724 6.172107,16.501994 6.721373,17.116627 7.336006,17.665893 8.008276,18.14289 8.729712,18.541616 9.491271,18.85706 10.283331,19.085248" style="fill: black" />
</g>
</svg>
......@@ -47,7 +47,7 @@ class XYButtons(BufferedCanvas):
center = (124, 121)
spacer = 7
def __init__(self, parent, moveCallback=None, cornerCallback=None, ID=-1):
def __init__(self, parent, moveCallback=None, cornerCallback=None, spacebarCallback=None, bgcolor="#FFFFFF", ID=-1):
self.bg_bmp = wx.Image(imagefile("control_xy.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.keypad_bmp = wx.Image(imagefile("arrow_keys.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.keypad_idx = -1
......@@ -56,7 +56,15 @@ class XYButtons(BufferedCanvas):
self.corner = None
self.moveCallback = moveCallback
self.cornerCallback = cornerCallback
self.spacebarCallback = spacebarCallback
self.enabled = False
# Remember the last clicked buttons, so we can repeat when spacebar pressed
self.lastMove = None
self.lastCorner = None
self.bgcolor = wx.Colour()
self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID)
self.SetSize(self.bg_bmp.GetSize())
......@@ -77,6 +85,16 @@ class XYButtons(BufferedCanvas):
self.enabled = True
self.update()
def repeatLast(self):
if self.lastMove:
self.moveCallback(*self.lastMove)
if self.lastCorner:
self.cornerCallback(self.lastCorner)
def clearRepeat(self):
self.lastMove = None
self.lastCorner = None
def distanceToLine(self, pos, x1, y1, x2, y2):
xlen = x2 - x1
ylen = y2 - y1
......@@ -206,6 +224,7 @@ class XYButtons(BufferedCanvas):
def draw(self, dc, w, h):
dc.SetBackground(wx.Brush(self.bgcolor))
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
......@@ -240,8 +259,8 @@ class XYButtons(BufferedCanvas):
r = kpos[2]
gc.DrawEllipse(kpos[0]-r, kpos[1]-r, r*2, r*2)
else:
gc.SetPen(wx.Pen(wx.Colour(255,255,255,0), 4))
gc.SetBrush(wx.Brush(wx.Colour(255,255,255,128)))
gc.SetPen(wx.Pen(self.bgcolor, 0))
gc.SetBrush(wx.Brush(self.bgcolormask))
gc.DrawRectangle(0, 0, w, h)
......@@ -273,8 +292,6 @@ class XYButtons(BufferedCanvas):
self.quadrant = 2
elif evt.GetKeyCode() == wx.WXK_RIGHT:
self.quadrant = 0
elif evt.GetKeyCode() == wx.WXK_SPACE:
pass
else:
evt.Skip()
return
......@@ -283,6 +300,9 @@ class XYButtons(BufferedCanvas):
self.concentric = self.keypad_idx
x, y = self.getMovement()
self.moveCallback(x, y)
elif evt.GetKeyCode() == wx.WXK_SPACE:
self.spacebarCallback()
def OnMotion(self, event):
if not self.enabled:
......@@ -335,9 +355,13 @@ class XYButtons(BufferedCanvas):
if self.quadrant != None:
x, y = self.getMovement()
if self.moveCallback:
self.lastMove = (x, y)
self.lastCorner = None
self.moveCallback(x, y)
elif self.corner != None:
if self.cornerCallback:
self.lastCorner = self.corner
self.lastMove = None
self.cornerCallback(self.corner)
else:
if self.keypad_idx == idx:
......
......@@ -39,13 +39,19 @@ class ZButtons(BufferedCanvas):
3: None
}
def __init__(self, parent, moveCallback=None, ID=-1):
def __init__(self, parent, moveCallback=None, bgcolor="#FFFFFF", ID=-1):
self.bg_bmp = wx.Image(imagefile("control_z.png"),wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.range = None
self.direction = None
self.orderOfMagnitudeIdx = 0 # 0 means '1', 1 means '10', 2 means '100', etc.
self.moveCallback = moveCallback
self.enabled = False
# Remember the last clicked value, so we can repeat when spacebar pressed
self.lastValue = None
self.bgcolor = wx.Colour()
self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID)
......@@ -65,6 +71,13 @@ class ZButtons(BufferedCanvas):
self.enabled = True
self.update()
def repeatLast(self):
if self.lastValue:
self.moveCallback(self.lastValue)
def clearRepeat(self):
self.lastValue = None
def lookupRange(self, ydist):
idx = -1
for d in ZButtons.button_ydistances:
......@@ -93,6 +106,7 @@ class ZButtons(BufferedCanvas):
return (self.lookupRange(abs(ydelta)), sign(ydelta))
def draw(self, dc, w, h):
dc.SetBackground(wx.Brush(self.bgcolor))
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
if self.bg_bmp:
......@@ -114,8 +128,8 @@ class ZButtons(BufferedCanvas):
if self.range != None and self.direction != None:
self.highlight(gc, self.range, self.direction)
else:
gc.SetPen(wx.Pen(wx.Colour(255,255,255,0), 4))
gc.SetBrush(wx.Brush(wx.Colour(255,255,255,128)))
gc.SetPen(wx.Pen(self.bgcolor, 0))
gc.SetBrush(wx.Brush(self.bgcolormask))
gc.DrawRectangle(0, 0, w, h)
## ------ ##
......@@ -143,6 +157,7 @@ class ZButtons(BufferedCanvas):
if r >= 0:
value = math.pow(10, self.orderOfMagnitudeIdx) * math.pow(10, r - 1) * d
if self.moveCallback:
self.lastValue = value
self.moveCallback(value)
def OnLeaveWindow(self, evt):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment