866 lines
38 KiB
Python
866 lines
38 KiB
Python
#!/usr/bin/env python
|
|
|
|
__author__ = "Stefan Blanke (greenarrow) (greenarrow@users.sourceforge.net)"
|
|
__credits__ = ""
|
|
__license__ = "GPL 3.0"
|
|
__version__ = "0.8"
|
|
__licence__ = """
|
|
pyRepRap is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
pyRepRap is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with pyRepRap. If not, see <http://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
# -*- coding: utf-8 -*-
|
|
# generated by wxGlade 0.6.1 on Wed Apr 23 11:49:08 2008
|
|
|
|
import wx, pygame, reprap, time, sys, os, threading
|
|
import replath.wxpygame as wxpygame
|
|
import reprap.shapeplotter, replath.plugins, reprap.toolpath
|
|
import replath
|
|
|
|
appTitle = "RepRap Plot"
|
|
|
|
|
|
plotterPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_IMPORT)
|
|
outputPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_OUTPUT)
|
|
toolheadPlugins = replath.plugins.getPlugins(replath.plugins.PLUGIN_TOOLHEAD)
|
|
plotterList = replath.plugins.listTitles(plotterPlugins)
|
|
outputList = replath.plugins.listTitles(outputPlugins)
|
|
toolheadList = replath.plugins.listTitles(toolheadPlugins)
|
|
|
|
if sys.platform=="win32":
|
|
# Windows (taking path of this script)
|
|
iconPath = os.path.join( os.getcwd(), "graphics" )
|
|
else:
|
|
# Linux
|
|
iconPath = "/usr/local/share/replath/icons/"
|
|
|
|
appIcon = os.path.join(iconPath, "reprap.png")
|
|
|
|
|
|
|
|
# Feedback action definitions
|
|
EVT_FBM_ID = wx.NewId()
|
|
FB_STATUS = 1
|
|
FB_COMPLETE = 2
|
|
FB_ABORTED = 3
|
|
FB_MESSAGE = 4
|
|
|
|
# Feedback message event object. Always used by feedbackmessagehandler (external plotters do not use it)
|
|
class FeedbackMessageEvent(wx.PyEvent):
|
|
"""Simple event to carry arbitrary result data."""
|
|
def __init__(self, action, data):
|
|
"""Init Result Event."""
|
|
wx.PyEvent.__init__(self)
|
|
self.SetEventType(EVT_FBM_ID)
|
|
self.action = action
|
|
self.data = data
|
|
|
|
# Passed to plotters so they can give feedback info to the gui (including when they are finished)
|
|
class FeedbackMessageHandler:
|
|
def __init__(self, parent):
|
|
self.parent = parent
|
|
parent.Connect(-1, -1, EVT_FBM_ID, self.onEvent)
|
|
|
|
def onEvent(self, event):
|
|
if event.action == FB_STATUS:
|
|
self.parent.setStatusBar(event.data)
|
|
elif event.action == FB_COMPLETE:
|
|
self.parent.plotComplete()
|
|
elif event.action == FB_ABORTED:
|
|
self.parent.plotAborted()
|
|
elif event.action == FB_MESSAGE:
|
|
dlg = wx.MessageDialog(self.parent, str(event.data) + '\n', caption = appTitle, style = wx.OK)
|
|
#dlg = wx.MessageDialog(self.parent, 'moo\n', caption = 'moo', style = wx.OK)
|
|
dlg.ShowModal()
|
|
|
|
def plotComplete(self):
|
|
wx.PostEvent( self.parent, FeedbackMessageEvent(FB_COMPLETE, None) )
|
|
|
|
def setStatus(self, text):
|
|
wx.PostEvent( self.parent, FeedbackMessageEvent(FB_STATUS, text) )
|
|
|
|
def setProgressPopup(self, text, progress = 0):
|
|
#if text:
|
|
# #show window
|
|
# self.progressDialog.progress.SetValue(int(progress))
|
|
#else:
|
|
# #hide window
|
|
pass
|
|
|
|
def updateProgressPopup(self, progress):
|
|
#self.progressDialog.progress.SetValue(int(progress))
|
|
pass
|
|
|
|
def showMessagePopup(self, text):
|
|
wx.PostEvent( self.parent, FeedbackMessageEvent(FB_MESSAGE, text) )
|
|
|
|
def aborted(self):
|
|
wx.PostEvent( self.parent, FeedbackMessageEvent(FB_ABORTED, None) )
|
|
|
|
# content of this block not found: did you rename this class?
|
|
pass
|
|
|
|
# Dialog class for preferences
|
|
class PreferencesDialog(wx.Dialog):
|
|
def __init__(self, *args, **kwds):
|
|
# begin wxGlade: PreferencesDialog.__init__
|
|
kwds["style"] = wx.DEFAULT_DIALOG_STYLE
|
|
wx.Dialog.__init__(self, *args, **kwds)
|
|
self.notebook_main = wx.Notebook(self, -1, style=wx.NB_LEFT)
|
|
self.panel_toolheads = wx.Panel(self.notebook_main, -1)
|
|
self.panel_outputs = wx.Panel(self.notebook_main, -1)
|
|
self.panel_inputs = wx.Panel(self.notebook_main, -1)
|
|
self.panel_general = wx.Panel(self.notebook_main, -1)
|
|
self.sizer_7_staticbox = wx.StaticBox(self, -1, "")
|
|
self.sizer_9_staticbox = wx.StaticBox(self.panel_general, -1, "")
|
|
self.label_8 = wx.StaticText(self.panel_general, -1, "Plot offset (mm) : ")
|
|
self.label_9 = wx.StaticText(self.panel_general, -1, "X : ")
|
|
self.text_offsetX = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_10 = wx.StaticText(self.panel_general, -1, "Y :")
|
|
self.text_offsetY = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_11 = wx.StaticText(self.panel_general, -1, "Fill density (lines / mm) :")
|
|
self.text_fillDensity = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_16 = wx.StaticText(self.panel_general, -1, "Circle resolution (lines / mm in c) :")
|
|
self.text_circleResolution = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_1 = wx.StaticText(self.panel_general, -1, "Delay between lines (seconds) :")
|
|
self.text_lineDelay = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_2 = wx.StaticText(self.panel_general, -1, "Zoom step (factor) :")
|
|
self.text_zoomStep = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.label_3 = wx.StaticText(self.panel_general, -1, "Grid division (mm) :")
|
|
self.text_gridDivision = wx.TextCtrl(self.panel_general, -1, "")
|
|
self.checkbox_debug = wx.CheckBox(self.panel_general, -1, "Debug")
|
|
self.notebook_inputs = wx.Notebook(self.panel_inputs, -1, style=wx.NB_LEFT)
|
|
self.notebook_outputs = wx.Notebook(self.panel_outputs, -1, style=wx.NB_LEFT)
|
|
self.notebook_toolheads = wx.Notebook(self.panel_toolheads, -1, style=wx.NB_LEFT)
|
|
self.button_Ok = wx.Button(self, wx.ID_OK, "")
|
|
self.button_Cancel = wx.Button(self, wx.ID_CANCEL, "")
|
|
|
|
self.__set_properties()
|
|
self.__do_layout()
|
|
|
|
self.Bind(wx.EVT_BUTTON, self.hanBtnOk, self.button_Ok)
|
|
self.Bind(wx.EVT_BUTTON, self.hanBtnCancel, self.button_Cancel)
|
|
# end wxGlade
|
|
|
|
def __set_properties(self):
|
|
# begin wxGlade: PreferencesDialog.__set_properties
|
|
self.SetTitle("RepRap Gerber Plotter - Preferences")
|
|
_icon = wx.EmptyIcon()
|
|
_icon.CopyFromBitmap(wx.Bitmap(appIcon))
|
|
self.SetIcon(_icon)
|
|
# end wxGlade
|
|
|
|
def __do_layout(self):
|
|
print "panel load"
|
|
# Dynamically load panels from plugins
|
|
self.preferencePanels = []
|
|
for plugin in outputPlugins:
|
|
if dir(plugin).count('PreferencesPanel'):
|
|
newPanel = plugin.PreferencesPanel(self.notebook_outputs)
|
|
self.preferencePanels.append( newPanel )
|
|
self.notebook_outputs.AddPage(newPanel, plugin.Title)
|
|
for plugin in toolheadPlugins:
|
|
if dir(plugin).count('PreferencesPanel'):
|
|
newPanel = plugin.PreferencesPanel(self.notebook_toolheads)
|
|
self.preferencePanels.append( newPanel )
|
|
self.notebook_toolheads.AddPage(newPanel, plugin.Title)
|
|
for plugin in plotterPlugins:
|
|
if dir(plugin).count('PreferencesPanel'):
|
|
newPanel = plugin.PreferencesPanel(self.notebook_inputs)
|
|
self.preferencePanels.append( newPanel )
|
|
self.notebook_inputs.AddPage(newPanel, plugin.Title)
|
|
|
|
# begin wxGlade: PreferencesDialog.__do_layout
|
|
sizer_7 = wx.StaticBoxSizer(self.sizer_7_staticbox, wx.VERTICAL)
|
|
sizer_5 = wx.BoxSizer(wx.VERTICAL)
|
|
sizer_7_copy = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer_4 = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer_3 = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer_1 = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer_2 = wx.BoxSizer(wx.VERTICAL)
|
|
sizer_9 = wx.StaticBoxSizer(self.sizer_9_staticbox, wx.VERTICAL)
|
|
grid_sizer_2 = wx.FlexGridSizer(7, 3, 4, 0)
|
|
grid_sizer_1 = wx.FlexGridSizer(4, 11, 4, 0)
|
|
grid_sizer_1.Add(self.label_8, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add(self.label_9, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add(self.text_offsetX, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add(self.label_10, 0, wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add(self.text_offsetY, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_1.AddGrowableCol(4)
|
|
grid_sizer_1.AddGrowableCol(8)
|
|
sizer_9.Add(grid_sizer_1, 0, wx.ALL|wx.EXPAND, 10)
|
|
grid_sizer_2.Add(self.label_11, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.text_fillDensity, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.label_16, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.text_circleResolution, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.label_1, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.text_lineDelay, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.label_2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.text_zoomStep, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.label_3, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.text_gridDivision, 0, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add(self.checkbox_debug, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((10, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
grid_sizer_2.AddGrowableCol(2)
|
|
sizer_9.Add(grid_sizer_2, 1, wx.ALL|wx.EXPAND, 10)
|
|
sizer_2.Add(sizer_9, 1, wx.ALL|wx.EXPAND, 10)
|
|
self.panel_general.SetSizer(sizer_2)
|
|
sizer_1.Add(self.notebook_inputs, 1, wx.ALL|wx.EXPAND, 10)
|
|
self.panel_inputs.SetSizer(sizer_1)
|
|
sizer_3.Add(self.notebook_outputs, 1, wx.ALL|wx.EXPAND, 10)
|
|
self.panel_outputs.SetSizer(sizer_3)
|
|
sizer_4.Add(self.notebook_toolheads, 1, wx.ALL|wx.EXPAND, 10)
|
|
self.panel_toolheads.SetSizer(sizer_4)
|
|
self.notebook_main.AddPage(self.panel_general, "General")
|
|
self.notebook_main.AddPage(self.panel_inputs, "Plotter Plugins")
|
|
self.notebook_main.AddPage(self.panel_outputs, "Output Plugins")
|
|
self.notebook_main.AddPage(self.panel_toolheads, "Toolhead Plugins")
|
|
sizer_5.Add(self.notebook_main, 1, wx.EXPAND, 0)
|
|
sizer_5.Add((20, 20), 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_7_copy.Add(self.button_Ok, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
sizer_7_copy.Add(self.button_Cancel, 1, wx.EXPAND|wx.ADJUST_MINSIZE, 0)
|
|
sizer_5.Add(sizer_7_copy, 0, wx.EXPAND, 0)
|
|
sizer_7.Add(sizer_5, 1, wx.ALL|wx.EXPAND|wx.ADJUST_MINSIZE, 16)
|
|
self.SetSizer(sizer_7)
|
|
sizer_7.Fit(self)
|
|
self.Layout()
|
|
self.Centre()
|
|
# end wxGlade
|
|
|
|
|
|
|
|
def setPrefHandler(self, pref):
|
|
self.preferences = pref
|
|
|
|
# Set values of frame control
|
|
def setPrefValues(self):
|
|
self.text_offsetX.SetValue( str(self.preferences.pref_plotOffsetX) )
|
|
self.text_offsetY.SetValue( str(self.preferences.pref_plotOffsetY) )
|
|
self.text_fillDensity.SetValue( str(self.preferences.pref_fillDensity) )
|
|
self.text_lineDelay.SetValue( str(self.preferences.pref_lineDelay) )
|
|
self.checkbox_debug.SetValue( self.preferences.pref_debug )
|
|
self.text_circleResolution.SetValue( str(self.preferences.pref_circleResolution) )
|
|
self.text_gridDivision.SetValue( str(self.preferences.pref_gridDivision) )
|
|
self.text_zoomStep.SetValue( str(self.preferences.pref_zoomStep) )
|
|
|
|
def getPreferences(self):
|
|
return self.preferences
|
|
|
|
# User clicked OK
|
|
def hanBtnOk(self, event): # wxGlade: PreferencesDialog.<event_handler>
|
|
self.preferences.pref_plotOffsetX = float( self.text_offsetX.GetValue() )
|
|
self.preferences.pref_plotOffsetY = float( self.text_offsetY.GetValue() )
|
|
self.preferences.pref_fillDensity = int( self.text_fillDensity.GetValue() )
|
|
self.preferences.pref_lineDelay = float( self.text_lineDelay.GetValue() )
|
|
self.preferences.pref_debug = self.checkbox_debug.GetValue()
|
|
self.preferences.pref_circleResolution = float( self.text_circleResolution.GetValue() )
|
|
self.preferences.pref_gridDivision = float( self.text_gridDivision.GetValue() )
|
|
self.preferences.pref_zoomStep = float( self.text_zoomStep.GetValue() )
|
|
|
|
for p in self.preferencePanels:
|
|
p.savePrefValues()
|
|
self.EndModal(wx.OK)
|
|
|
|
# User clicked Cancel
|
|
def hanBtnCancel(self, event): # wxGlade: PreferencesDialog.<event_handler>
|
|
self.preferences = False
|
|
self.EndModal(wx.CANCEL)
|
|
# end of class PreferencesDialog
|
|
|
|
"""
|
|
# A set of lines for drawing on the screen TODO - put draw back in
|
|
class lineSet:
|
|
def __init__(self, parent, colour):
|
|
self.parent = parent
|
|
self.colour = colour
|
|
self.clear()
|
|
def setColour(colour):
|
|
self.colour = colour
|
|
self.tableChanged = True
|
|
def addLine(self, line):
|
|
self.lines.append(line)
|
|
#self.parent.draw() #not ideal
|
|
#pygame.display.flip()
|
|
self.tableChanged = True
|
|
def getLines(self):
|
|
return self.lines
|
|
def clear(self):
|
|
self.lines = []
|
|
self.tableChanged = True
|
|
def hasChanged(self):
|
|
if self.tableChanged:
|
|
self.tableChanged = False
|
|
return True
|
|
else:
|
|
return False
|
|
"""
|
|
|
|
# Class for pygame canvas
|
|
class DrawCanvas(wxpygame.wxSDLPanel):
|
|
def __init__( self, parent, ID=-1 ):
|
|
wxpygame.wxSDLPanel.__init__(self, parent, ID)
|
|
self.previewScale = 5.0
|
|
self.offsetX, self.offsetY = 0, 0
|
|
|
|
self.black = 0, 0, 0
|
|
self.white = 255, 255, 255
|
|
self.grey = 160, 160, 160
|
|
self.greyGreen = 170, 195, 170
|
|
self.darkGrey = 140, 140, 140
|
|
|
|
self.backroundColour = self.white
|
|
self.baseColour = 166, 211, 166
|
|
self.gridColour = self.greyGreen
|
|
self.previewColour = self.darkGrey
|
|
self.plotColour = self.black
|
|
|
|
pygame.font.init()
|
|
self.font = pygame.font.Font(None, 17)
|
|
self.drawSurface = pygame.Surface( ( 0, 0 ) )
|
|
self.drawn = False
|
|
|
|
def setParent(self, parent):
|
|
self.parent = parent
|
|
self.fullRedraw()
|
|
|
|
def drawText( self, surface, x, y, text, font ):
|
|
text = font.render( text, True, (255, 255, 255), (159, 182, 205) )
|
|
textRect = text.get_rect()
|
|
textRect.centerx = x
|
|
textRect.centery = y
|
|
surface.blit(text, textRect)
|
|
|
|
# Draw plot to widget
|
|
def draw( self ):
|
|
surface = self.getSurface()
|
|
w, h = self.GetSizeTuple()
|
|
if not surface is None:
|
|
surface.fill( self.backroundColour )
|
|
if self.drawn:
|
|
surface.blit( self.drawSurface, (self.offsetX, -self.offsetY) )#limit region to visible - is this needed?
|
|
|
|
for event in pygame.event.get():
|
|
if event.type == pygame.QUIT:
|
|
sys.exit()
|
|
pygame.display.flip()
|
|
|
|
def fullRedraw(self):
|
|
if self.parent.currentToolpath:
|
|
fileOffsetX, fileOffsetY = self.parent.currentToolpath.offsetX, self.parent.currentToolpath.offsetY
|
|
# Find limits
|
|
#if len(self.polygons) > 0:
|
|
minX, minY, maxX, maxY = 1000000, 1000000, -1000000, -1000000
|
|
if len(self.parent.currentToolpath.layers):
|
|
for layer in self.parent.currentToolpath.layers:
|
|
for poly in layer.polygons:
|
|
for p in poly.points:
|
|
minX = min(minX, p.x + fileOffsetX)
|
|
minY = min(minY, p.y + fileOffsetY)
|
|
maxX = max(maxX, p.x + fileOffsetX)
|
|
maxY = max(maxY, p.y + fileOffsetY)
|
|
else:
|
|
minX, minY, maxX, maxY = 0, 0, 0, 0
|
|
border = int(self.parent.preferences.pref_gridDivision * self.previewScale / 2)
|
|
w, h = self.GetSizeTuple()
|
|
width, height = int(maxX * self.previewScale) + (2 * border), int(maxY * self.previewScale) + (2 *border)
|
|
#print "wh", width, height
|
|
if width > 0 and height > 0:
|
|
self.drawSurface = pygame.Surface( ( width, height ) )
|
|
self.drawRect = 0, 0, width, height
|
|
|
|
# Only draw if we have something to show
|
|
#if len(self.polygons) > 0:
|
|
# Draw background
|
|
pygame.draw.rect( self.drawSurface, self.baseColour, self.drawRect, 0)
|
|
# Draw grid
|
|
for x in frange( 0, maxX, self.parent.preferences.pref_gridDivision ):
|
|
pygame.draw.line( self.drawSurface, self.gridColour, ( int( float(x) * self.previewScale ) + border , border ), ( int( float(x) * self.previewScale ) + border , height - border ) )
|
|
for y in frange( 0, maxY, self.parent.preferences.pref_gridDivision ):
|
|
pygame.draw.line( self.drawSurface, self.gridColour, ( border, height - border - int( float(y) * self.previewScale ) ), ( width - border, height - border - int( float(y) * self.previewScale ) ) )
|
|
|
|
# Draw axis labels
|
|
pygame.draw.line( self.drawSurface, self.black, (border, height - border), (border + 50, height - border) )
|
|
pygame.draw.line( self.drawSurface, self.black, (border, height - border), (border, height - 50 - border ) )
|
|
self.drawText( self.drawSurface, border + 25, height - border , "X", self.font )
|
|
self.drawText( self.drawSurface, border, height - border - 25, "Y", self.font )
|
|
#else:
|
|
# # Draw blank white box
|
|
# pygame.draw.rect( self.drawSurface, self.white, self.drawRect, 0)
|
|
|
|
for layer in self.parent.currentToolpath.layers:
|
|
# Draw polygons
|
|
for poly in layer.polygons:
|
|
#print "plotting poly", poly, len(poly.points)
|
|
oldX, oldY = poly.points[0].x, poly.points[0].y
|
|
for ip, p in enumerate(poly.points[ 1: ]):
|
|
if ip < poly.pointsPlotted:
|
|
lineColour = self.black
|
|
else:
|
|
lineColour = self.previewColour
|
|
x1, y1, x2, y2 = self.scaleRect( (oldX + fileOffsetX, oldY + fileOffsetY, p.x + fileOffsetX, p.y + fileOffsetY), self.previewScale )
|
|
pygame.draw.line( self.drawSurface, lineColour, (x1 + border, height - y1 - border), (x2 + border, height - y2 -border) )
|
|
oldX, oldY = p.x, p.y
|
|
# Final line if closed
|
|
if poly.closed:
|
|
x, y = poly.points[0].x, poly.points[0].y
|
|
x1, y1, x2, y2 = self.scaleRect( (oldX + fileOffsetX, oldY + fileOffsetY, x + fileOffsetX, y + fileOffsetY), self.previewScale )
|
|
pygame.draw.line( self.drawSurface, self.previewColour, (x1 + border, height - y1 - border), (x2 + border, height - y2 -border) )
|
|
self.drawn = True
|
|
self.draw()
|
|
|
|
# Simple coordiante manipulation
|
|
def scaleRect( self, rect, scale ):
|
|
x1, y1, x2, y2 = rect
|
|
scaleX = float(scale)
|
|
scaleY = float(scale)
|
|
return int( float(x1) * scale ), int( float(y1) * scale ), int( float(x2) * scale ), int( float(y2) * scale )
|
|
|
|
def offsetRect( self, rect, x, y ):
|
|
x1, y1, x2, y2 = rect
|
|
return x1 + x, y1 + y, x2, y2
|
|
|
|
def offsetLine(self, rect, x, y):
|
|
x1, y1, x2, y2 = rect
|
|
return x1 + x, y1 + y, x2 + x, y2 + y
|
|
|
|
def centreView(self, x, y):
|
|
widthV, heightV = self.GetSizeTuple()
|
|
null, null, widthD, heightD = self.drawRect
|
|
self.offsetX = (widthV - widthD + x) / 2
|
|
self.offsetY = (heightV - heightD + y) / -2
|
|
|
|
# Change draw offset to allow dragging of plot
|
|
def MouseMove(self, event):
|
|
w, h = self.GetSizeTuple()
|
|
if event.LeftIsDown():
|
|
x, y = event.GetPosition()
|
|
if event.Dragging():
|
|
self.offsetX, self.offsetY = x + self.moveDeltaX, self.moveDeltaY - y
|
|
|
|
# Record offsets for use in drag movement
|
|
def OnMouseDown(self, event):
|
|
w, h = self.GetSizeTuple()
|
|
x, y = event.GetPosition()
|
|
#print x, y
|
|
self.moveDeltaX, self.moveDeltaY = self.offsetX - x, self.offsetY + y
|
|
|
|
def OnMouseUp(self, event):
|
|
pass
|
|
|
|
# When mouse wheel moved, zoom in or out
|
|
def OnMouseWheel(self, event):
|
|
w, h = self.GetSizeTuple()
|
|
centreX, centreY = w / 2, h / 2
|
|
x, y = event.GetPosition()
|
|
#deltaX, deltaY = centreX - x, y - centreY
|
|
|
|
if event.GetWheelRotation() > 0:
|
|
self.previewScale = self.previewScale + self.parent.preferences.pref_zoomStep
|
|
elif self.previewScale >= self.parent.preferences.pref_zoomStep:
|
|
self.previewScale = self.previewScale - self.parent.preferences.pref_zoomStep
|
|
self.fullRedraw()
|
|
#null, null, widthD, heightD = self.drawRect
|
|
#self.centreView(x - self.offsetX, h - y - self.offsetY)
|
|
#self.centreView(100, 100)
|
|
|
|
|
|
# Main frame class
|
|
class MyFrame(wx.Frame):
|
|
def __init__(self, *args, **kwds):
|
|
# Create preference handler that stores preferences on itself
|
|
self.preferences = replath.preferences.PreferenceHandler( False, "pyRepRap.conf" )
|
|
# default values for preferences
|
|
self.preferences.pref_plotOffsetX = 10.0
|
|
self.preferences.pref_plotOffsetY = 10.0
|
|
self.preferences.pref_fillDensity = 5
|
|
self.preferences.pref_lineDelay = 0.0
|
|
self.preferences.pref_debug = False
|
|
self.preferences.pref_circleResolution = 3.0
|
|
self.preferences.pref_gridDivision = 5.0
|
|
self.preferences.pref_zoomStep = 1
|
|
|
|
self.preferences.load()
|
|
|
|
self.fileName = False
|
|
self.currentPlotter = False
|
|
self.currentToolpath = False
|
|
|
|
# begin wxGlade: MyFrame.__init__
|
|
kwds["style"] = wx.DEFAULT_FRAME_STYLE
|
|
wx.Frame.__init__(self, *args, **kwds)
|
|
self.panel_1 = wx.Panel(self, -1)
|
|
|
|
# Menu Bar
|
|
self.frmMain_menubar = wx.MenuBar()
|
|
self.mnuFile = wx.Menu()
|
|
self.mnuOpen = wx.MenuItem(self.mnuFile, 0, "&Open\tCtrl+O", "", wx.ITEM_NORMAL)
|
|
self.mnuFile.AppendItem(self.mnuOpen)
|
|
self.mnuFile.AppendSeparator()
|
|
self.mnuPlot = wx.MenuItem(self.mnuFile, 2, "&Plot\tCtrl+P", "", wx.ITEM_NORMAL)
|
|
self.mnuFile.AppendItem(self.mnuPlot)
|
|
self.mnuFile.AppendSeparator()
|
|
self.mnuQuit = wx.MenuItem(self.mnuFile, 1, "&Quit\tCtrl+Q", "", wx.ITEM_NORMAL)
|
|
self.mnuFile.AppendItem(self.mnuQuit)
|
|
self.frmMain_menubar.Append(self.mnuFile, "&File")
|
|
wxglade_tmp_menu = wx.Menu()
|
|
wxglade_tmp_menu.Append(10, "Pr&eferences", "", wx.ITEM_NORMAL)
|
|
self.frmMain_menubar.Append(wxglade_tmp_menu, "&Edit")
|
|
wxglade_tmp_menu = wx.Menu()
|
|
self.mnuAbout = wx.MenuItem(wxglade_tmp_menu, 40, "&About", "", wx.ITEM_NORMAL)
|
|
wxglade_tmp_menu.AppendItem(self.mnuAbout)
|
|
self.frmMain_menubar.Append(wxglade_tmp_menu, "&Help")
|
|
self.SetMenuBar(self.frmMain_menubar)
|
|
# Menu Bar end
|
|
self.frmMain_statusbar = self.CreateStatusBar(1, 0)
|
|
|
|
# Tool Bar
|
|
self.frmMain_toolbar = wx.ToolBar(self, -1, style=wx.TB_HORIZONTAL|wx.TB_TEXT)
|
|
self.SetToolBar(self.frmMain_toolbar)
|
|
self.frmMain_toolbar.AddLabelTool(100, "Open", (wx.Bitmap( os.path.join(iconPath, 'document-open.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Open file", "")
|
|
self.frmMain_toolbar.AddSeparator()
|
|
self.frmMain_toolbar.AddLabelTool(110, "Stop", (wx.Bitmap( os.path.join(iconPath, 'media-playback-stop.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Stop current plotting action", "")
|
|
self.frmMain_toolbar.AddSeparator()
|
|
self.frmMain_toolbar.AddLabelTool(120, "Plot", (wx.Bitmap( os.path.join(iconPath, 'media-playback-start.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "Plot to output plugin", "")
|
|
self.frmMain_toolbar.AddSeparator()
|
|
self.frmMain_toolbar.AddLabelTool(130, "Preferences", (wx.Bitmap( os.path.join(iconPath, 'configure.png'), wx.BITMAP_TYPE_ANY )), wx.NullBitmap, wx.ITEM_NORMAL, "", "")
|
|
# Tool Bar end
|
|
self.static_line_1 = wx.StaticLine(self.panel_1, -1)
|
|
self.label_22 = wx.StaticText(self.panel_1, -1, "Output :")
|
|
self.choice_output = wx.Choice(self.panel_1, -1, choices=[])
|
|
self.label_23 = wx.StaticText(self.panel_1, -1, "Toolhead :")
|
|
self.choice_toolhead = wx.Choice(self.panel_1, -1, choices=[])
|
|
self.static_line_2 = wx.StaticLine(self.panel_1, -1)
|
|
self.pygameCanvas = DrawCanvas(self.panel_1, -1)
|
|
|
|
self.__set_properties()
|
|
self.__do_layout()
|
|
|
|
self.Bind(wx.EVT_MENU, self.onClickOpen, self.mnuOpen)
|
|
self.Bind(wx.EVT_MENU, self.onClickPlot, self.mnuPlot)
|
|
self.Bind(wx.EVT_MENU, self.onClickQuit, self.mnuQuit)
|
|
self.Bind(wx.EVT_MENU, self.onClickPreferences, id=10)
|
|
self.Bind(wx.EVT_MENU, self.onClickAbout, self.mnuAbout)
|
|
self.Bind(wx.EVT_TOOL, self.onClickOpen, id=100)
|
|
self.Bind(wx.EVT_TOOL, self.onClickStop, id=110)
|
|
self.Bind(wx.EVT_TOOL, self.onClickPlot, id=120)
|
|
self.Bind(wx.EVT_TOOL, self.onClickPreferences, id=130)
|
|
# end wxGlade
|
|
|
|
#self.Bind(wx.EVT_CLOSE, self.onCloseWindow)
|
|
self.pygameCanvas.setParent(self)
|
|
self.setStatusBar("RepRap Plot")
|
|
|
|
def __set_properties(self):
|
|
# begin wxGlade: MyFrame.__set_properties
|
|
self.SetTitle("RepRap Plotter")
|
|
_icon = wx.EmptyIcon()
|
|
_icon.CopyFromBitmap(wx.Bitmap(appIcon))
|
|
self.SetIcon(_icon)
|
|
self.SetSize((869, 579))
|
|
self.frmMain_statusbar.SetStatusWidths([-1])
|
|
# statusbar fields
|
|
frmMain_statusbar_fields = ["frmMain_statusbar"]
|
|
for i in range(len(frmMain_statusbar_fields)):
|
|
self.frmMain_statusbar.SetStatusText(frmMain_statusbar_fields[i], i)
|
|
self.frmMain_toolbar.SetToolBitmapSize((32, 32))
|
|
self.frmMain_toolbar.Realize()
|
|
# end wxGlade
|
|
|
|
# Load plugin names into choice lists
|
|
self.choice_output.Append("None")
|
|
for p in outputList:
|
|
self.choice_output.Append(p)
|
|
self.choice_output.SetSelection(0)
|
|
|
|
for p in toolheadList:
|
|
self.choice_toolhead.Append(p)
|
|
self.choice_toolhead.SetSelection(0)
|
|
|
|
def __do_layout(self):
|
|
# begin wxGlade: MyFrame.__do_layout
|
|
sizer_6 = wx.BoxSizer(wx.VERTICAL)
|
|
sizer_main = wx.BoxSizer(wx.VERTICAL)
|
|
sizer_19 = wx.BoxSizer(wx.HORIZONTAL)
|
|
sizer_main.Add(self.static_line_1, 0, wx.ALL|wx.EXPAND, 5)
|
|
sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add(self.label_22, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add(self.choice_output, 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add((20, 1), 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add(self.label_23, 0, wx.ALIGN_CENTER_VERTICAL|wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add((10, 1), 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_19.Add(self.choice_toolhead, 0, wx.ADJUST_MINSIZE, 0)
|
|
sizer_main.Add(sizer_19, 0, wx.EXPAND, 0)
|
|
sizer_main.Add(self.static_line_2, 0, wx.ALL|wx.EXPAND, 5)
|
|
sizer_main.Add(self.pygameCanvas, 5, wx.EXPAND, 1)
|
|
self.panel_1.SetSizer(sizer_main)
|
|
sizer_6.Add(self.panel_1, 1, wx.EXPAND, 0)
|
|
self.SetSizer(sizer_6)
|
|
self.Layout()
|
|
self.Centre()
|
|
# end wxGlade
|
|
|
|
def plotComplete(self):
|
|
self.pygameCanvas.fullRedraw()
|
|
self.pygameCanvas.centreView(0, 0)
|
|
self.currentPlotter.join()
|
|
self.currentPlotter = False
|
|
self.setStatusBar("Plot Complete")
|
|
#dlg = wx.MessageDialog(self, "Plot Complete\n", caption = appTitle, style = wx.OK)
|
|
#dlg.ShowModal()
|
|
|
|
def plotAborted(self):
|
|
self.pygameCanvas.fullRedraw()
|
|
if self.currentPlotter:
|
|
self.currentPlotter.join()
|
|
self.currentPlotter = False
|
|
self.setStatusBar("Plot aborted")
|
|
dlg = wx.MessageDialog(self, "Plot aborted\n", caption = appTitle, style = wx.OK)
|
|
dlg.ShowModal()
|
|
|
|
# Produces strings for file formats in wx file dialog e.g. 'Gerber Files (*.pho)|*.pho'
|
|
def dialogFilterString(self, extensions, name):
|
|
rText = name + " ("
|
|
for e in extensions:
|
|
rText += '*' + e + ';'
|
|
rText = rText[ :-1 ]
|
|
rText += ")|"
|
|
for e in extensions:
|
|
rText += '*' + e + ';'
|
|
# Removed as windows did not like having no end chr
|
|
rText = rText[ :-1 ]
|
|
return rText
|
|
|
|
def importPlot(self, fileName = False, quiet = False):
|
|
if fileName:
|
|
self.fileName = fileName
|
|
else:
|
|
self.fileName = False
|
|
filters = ""
|
|
allFormats = []
|
|
# Produce strings for file formats for wx file dialog
|
|
for p in plotterPlugins:
|
|
filters += self.dialogFilterString( p.SupportedFileExtensions, p.FileTitle ) + '|'
|
|
allFormats += p.SupportedFileExtensions
|
|
# Produce all supported formats string, put it at the start of the list
|
|
filters = self.dialogFilterString( allFormats, "All Supported Files" ) + '|' + filters
|
|
filters = filters[ :-1 ]
|
|
#print "ff [" + filters + "]"
|
|
dialog = wx.FileDialog ( None, message = 'Open Gerber File', wildcard = filters, style = wx.OPEN )
|
|
if dialog.ShowModal() == wx.ID_OK:
|
|
self.fileName = dialog.GetPath()
|
|
self.setStatusBar( "Opened File '" + self.fileName + "'" )
|
|
dialog.Destroy()
|
|
|
|
if self.fileName or fileName:
|
|
self.currentToolpath = reprap.toolpath.Object()
|
|
self.currentToolpath.offsetX, self.currentToolpath.offsetY = self.preferences.pref_plotOffsetX, self.preferences.pref_plotOffsetY
|
|
self.currentPlotter = False
|
|
plugin = False
|
|
cancel = False
|
|
feedbackHandler = FeedbackMessageHandler(self)
|
|
# Find a plugin that claims to handle files with the extension
|
|
for p in plotterPlugins:
|
|
# TODO - change to look for last dot not first, as it gets confused with network locations
|
|
#(fileBaseName, fileExtension)=os.path.splitext(fileName)
|
|
extension = self.fileName[ self.fileName.find('.'): ].lower()
|
|
if p.SupportedFileExtensions.count( extension ) > 0:
|
|
# # Check if the plotter has a preferences dialog and show it if it has one
|
|
if dir(p).count('PreferencesDialog') and not quiet:
|
|
plotPrefDialog = p.PreferencesDialog(None, -1, "")
|
|
app.SetTopWindow(plotPrefDialog)
|
|
if plotPrefDialog.ShowModal() == wx.OK:
|
|
plugin = p
|
|
else:
|
|
cancel = True
|
|
break
|
|
else:
|
|
plugin = p
|
|
|
|
# If a suitable plugin has been found
|
|
if plugin:
|
|
# Create plotter
|
|
self.currentPlotter = plugin.plotter(self.fileName, self.currentToolpath,
|
|
feedbackHandler = feedbackHandler,
|
|
arcResolution = self.preferences.pref_circleResolution,
|
|
fillDensity = self.preferences.pref_fillDensity,
|
|
debug = self.preferences.pref_debug,
|
|
)
|
|
if self.currentPlotter:
|
|
# Start plotter thread
|
|
self.currentPlotter.start()
|
|
elif not cancel :
|
|
dlg = wx.MessageDialog(self, "File format unsupported\n", caption = appTitle, style = wx.OK)
|
|
dlg.ShowModal()
|
|
|
|
def exportPlot(self, fileName = False):
|
|
#self.setStatusBar( "Plotting File '" + self.fileName + "'" )
|
|
# Use user selected output module
|
|
if self.choice_output.GetSelection() > 0:
|
|
outputs = [ self.choice_output.GetSelection() - 1 ]
|
|
else:
|
|
outputs = []
|
|
feedbackHandler = FeedbackMessageHandler(self)
|
|
toolhead = toolheadPlugins[ self.choice_toolhead.GetSelection() ].tool()
|
|
outputPlotters = []
|
|
for o in outputs:
|
|
outputFilename = None
|
|
if outputPlugins[o].FileOutput:
|
|
saveDialog = wx.FileDialog(self, message='Save file as...', wildcard=outputPlugins[o].Wildcard, style=wx.SAVE | wx.OVERWRITE_PROMPT) #defaultDir=dir, defaultFile='',
|
|
if saveDialog.ShowModal() == wx.ID_OK:
|
|
outputFilename = saveDialog.GetPath()
|
|
else:
|
|
outputFilename = None
|
|
saveDialog.Destroy()
|
|
|
|
self.currentPlotter = outputPlugins[o].output(self.currentToolpath, toolhead, feedbackHandler, outputFilename)
|
|
self.currentPlotter.start()
|
|
|
|
|
|
def setStatusBar(self, text):
|
|
self.frmMain_statusbar.SetStatusText( text, 0 )
|
|
|
|
def onClickOpen(self, event): # wxGlade: MyFrame.<event_handler>
|
|
if self.currentPlotter:
|
|
dlg = wx.MessageDialog(self, "Current action must be stopped before opening a file.\n", caption = appTitle, style = wx.OK)
|
|
dlg.ShowModal()
|
|
else:
|
|
self.importPlot()
|
|
|
|
#def onCloseWindow(self, evt):
|
|
# #self.Destroy()
|
|
# wx.Exit()
|
|
|
|
def onClickQuit(self, event): # wxGlade: MyFrame.<event_handler>
|
|
wx.Exit()
|
|
|
|
def onClickPreferences(self, event): # wxGlade: MyFrame.<event_handler>
|
|
dialogPref = PreferencesDialog(None, -1, "")
|
|
app.SetTopWindow(dialogPref)
|
|
|
|
dialogPref.setPrefHandler( self.preferences )
|
|
dialogPref.setPrefValues()
|
|
dialogPref.ShowModal()
|
|
|
|
self.pygameCanvas.fullRedraw()
|
|
|
|
dialogPref.Destroy()
|
|
self.preferences.save()
|
|
|
|
def onClickAbout(self, event): # wxGlade: MyFrame.<event_handler>
|
|
description = "RepRap Plotter is a program for plotting Gerber and CAD files on a RepRap machine."
|
|
info = wx.AboutDialogInfo()
|
|
info.SetIcon(wx.Icon(appIcon, wx.BITMAP_TYPE_PNG))
|
|
info.SetName('About RepRap Plotter')
|
|
info.SetVersion(__version__)
|
|
info.SetDescription(description)
|
|
#info.SetCopyright('')
|
|
info.SetLicence(__licence__)
|
|
info.AddDeveloper(__author__)
|
|
wx.AboutBox(info)
|
|
|
|
def onClickStop(self, event): # wxGlade: MyFrame.<event_handler>
|
|
# tell plotter thread to quit
|
|
if self.currentPlotter:
|
|
self.currentPlotter.terminate()
|
|
else:
|
|
dlg = wx.MessageDialog(self, "Nothing is currently being plotted.\n", caption = appTitle, style = wx.OK)
|
|
dlg.ShowModal()
|
|
|
|
def onClickPlot(self, event): # wxGlade: MyFrame.<event_handler>
|
|
if self.currentPlotter:
|
|
dlg = wx.MessageDialog(self, "Current action must be stopped before plotting.\n", caption = appTitle, style = wx.OK)
|
|
dlg.ShowModal()
|
|
else:
|
|
self.exportPlot()
|
|
|
|
|
|
# end of class MyFrame
|
|
|
|
|
|
# Range function accepting floats (by Dinu Gherman)
|
|
def frange(start, end=None, inc=None):
|
|
if end == None:
|
|
end = start + 0.0
|
|
start = 0.0
|
|
if inc == None:
|
|
inc = 1.0
|
|
L = []
|
|
while 1:
|
|
next = start + len(L) * inc
|
|
if inc > 0 and next >= end:
|
|
break
|
|
elif inc < 0 and next <= end:
|
|
break
|
|
L.append(next)
|
|
return L
|
|
|
|
|
|
# Strip quotations
|
|
def stripQuotes(text):
|
|
if (text[0] == '"' and text[-1] == '"') or (text[0] == "'" and text[-1] == "'"):
|
|
return text[ 1:-1 ]
|
|
else:
|
|
return text
|
|
|
|
|
|
showGui = True
|
|
if len(sys.argv) > 1:
|
|
if sys.argv.count("--nogui") or sys.argv.count("--g") or sys.argv.count("--help") or sys.argv.count("-h") > 0:
|
|
showGui = False
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if showGui:
|
|
app = wx.PySimpleApp(0)
|
|
wx.InitAllImageHandlers()
|
|
frmMain = MyFrame(None, -1, "")
|
|
app.SetTopWindow(frmMain)
|
|
frmMain.Show()
|
|
# Command line arg plot file temp TODO proper
|
|
|
|
if sys.argv.count("--quiet") or sys.argv.count("--q"):
|
|
quiet = True
|
|
else:
|
|
quiet = False
|
|
|
|
if len(sys.argv) > 1:
|
|
frmMain.importPlot( stripQuotes(sys.argv[-1]), quiet )
|
|
|
|
|
|
app.MainLoop()
|
|
else:
|
|
print "TODO : Command line only mode"
|
|
if sys.argv.count("--help"):
|
|
print "Usage: reprapplot [OPTIONS] [FILE]..."
|
|
print "Option GNU long option Meaning"
|
|
print " -h --help Show this message"
|
|
print " -g --nogui Don't show GUI"
|
|
print " -q --quiet Don't ask questions"
|
|
|
|
|
|
|
|
|