Add post processing plugin support.

master
daid303 2012-10-31 11:04:08 +01:00
parent be86d34403
commit 655eb31f4e
4 changed files with 228 additions and 60 deletions

View File

@ -8,6 +8,7 @@ from gui import expertConfig
from gui import preview3d
from gui import sliceProgessPanel
from gui import alterationPanel
from gui import pluginPanel
from gui import preferencesDialog
from gui import configWizard
from gui import firmwareInstall
@ -224,31 +225,10 @@ class mainWindow(configBase.configWindowBase):
validators.warningAbove(c, lambda : (float(profile.getProfileSetting('nozzle_size')) * 3.0 / 4.0), "A bottom layer of more then %.2fmm (3/4 nozzle size) usually give bad results and is not recommended.")
c = configBase.SettingRow(right, "Enable 'skin'", 'enable_skin', False, 'Skin prints the outer lines of the prints twice, each time with half the thickness. This gives the illusion of a higher print quality.')
#Effects page
self.effectList = profile.getEffectsList()
if len(self.effectList) > 0:
self.effectPanel = wx.Panel(nb)
sizer = wx.GridBagSizer(2, 2)
self.effectPanel.SetSizer(sizer)
effectStringList = []
for effect in self.effectList:
effectStringList.append(effect['name'])
self.listbox = wx.ListBox(self.effectPanel, -1, choices=effectStringList)
title = wx.StaticText(self.effectPanel, -1, "Effects:")
title.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD))
addButton = wx.Button(self.effectPanel, -1, '>', style=wx.BU_EXACTFIT)
remButton = wx.Button(self.effectPanel, -1, '<', style=wx.BU_EXACTFIT)
sizer.Add(self.listbox, (1,0), span=(2,1), border=10, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM)
sizer.Add(title, (0,0), border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP)
sizer.Add(addButton, (1,1), border=5, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)
sizer.Add(remButton, (2,1), border=5, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_TOP)
sizer.AddGrowableCol(2)
sizer.AddGrowableRow(1)
sizer.AddGrowableRow(2)
nb.AddPage(self.effectPanel, "Effects")
#Plugin page
self.pluginPanel = pluginPanel.pluginPanel(nb)
if len(self.pluginPanel.pluginList) > 0:
nb.AddPage(self.pluginPanel, "Plugins")
#Alteration page
self.alterationPanel = alterationPanel.alterationPanel(nb)
@ -498,3 +478,4 @@ class mainWindow(configBase.configWindowBase):
super(mainWindow, self).updateProfileToControls()
self.preview3d.updateProfileToControls()
self.alterationPanel.updateProfileToControls()
self.pluginPanel.updateProfileToControls()

140
Cura/gui/pluginPanel.py Normal file
View File

@ -0,0 +1,140 @@
import wx,wx.stc
import sys,math,threading,os
from wx.lib import scrolledpanel
from util import profile
class pluginPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent,-1)
#Plugin page
self.pluginList = profile.getPluginList()
sizer = wx.GridBagSizer(2, 2)
self.SetSizer(sizer)
effectStringList = []
for effect in self.pluginList:
effectStringList.append(effect['name'])
self.listbox = wx.ListBox(self, -1, choices=effectStringList)
title = wx.StaticText(self, -1, "Plugins:")
title.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD))
addButton = wx.Button(self, -1, '>', style=wx.BU_EXACTFIT)
sb = wx.StaticBox(self, label="Enabled plugins")
boxsizer = wx.StaticBoxSizer(sb, wx.VERTICAL)
self.pluginEnabledPanel = scrolledpanel.ScrolledPanel(self)
self.pluginEnabledPanel.SetupScrolling(False, True)
sizer.Add(title, (0,0), border=10, flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.TOP)
sizer.Add(self.listbox, (1,0), span=(2,1), border=10, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM)
sizer.Add(addButton, (1,1), border=5, flag=wx.ALIGN_CENTER_HORIZONTAL|wx.ALIGN_BOTTOM)
sizer.Add(boxsizer, (1,2), span=(2,1), border=10, flag=wx.EXPAND|wx.LEFT|wx.RIGHT|wx.BOTTOM)
boxsizer.Add(self.pluginEnabledPanel, 1, flag=wx.EXPAND)
sizer.AddGrowableCol(2)
sizer.AddGrowableRow(1)
sizer.AddGrowableRow(2)
sizer = wx.BoxSizer(wx.VERTICAL)
self.pluginEnabledPanel.SetSizer(sizer)
self.Bind(wx.EVT_BUTTON, self.OnAdd, addButton)
self.panelList = []
self.updateProfileToControls()
def updateProfileToControls(self):
self.pluginConfig = profile.getPluginConfig()
for p in self.panelList:
p.Show(False)
self.pluginEnabledPanel.GetSizer().Detach(p)
self.panelList = []
for pluginConfig in self.pluginConfig:
self._buildPluginPanel(pluginConfig)
def _buildPluginPanel(self, pluginConfig):
plugin = None
for pluginTest in self.pluginList:
if pluginTest['filename'] == pluginConfig['filename']:
plugin = pluginTest
if plugin == None:
return False
pluginPanel = wx.Panel(self.pluginEnabledPanel)
s = wx.GridBagSizer(2, 2)
pluginPanel.SetSizer(s)
title = wx.StaticText(pluginPanel, -1, plugin['name'])
title.SetFont(wx.Font(wx.SystemSettings.GetFont(wx.SYS_ANSI_VAR_FONT).GetPointSize(), wx.FONTFAMILY_DEFAULT, wx.NORMAL, wx.FONTWEIGHT_BOLD))
remButton = wx.Button(pluginPanel, -1, 'X', style=wx.BU_EXACTFIT)
s.Add(title, pos=(0,0), span=(1,2), flag=wx.ALIGN_BOTTOM|wx.TOP|wx.LEFT|wx.RIGHT, border=5)
s.Add(remButton, pos=(0,2), span=(1,1), flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.ALIGN_RIGHT, border=5)
s.Add(wx.StaticLine(pluginPanel), pos=(1,0), span=(1,3), flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=3)
info = wx.StaticText(pluginPanel, -1, plugin['info'])
info.Wrap(300)
s.Add(info, pos=(2,0), span=(1,3), flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=3)
pluginPanel.paramCtrls = {}
i = 0
for param in plugin['params']:
value = param['default']
if param['name'] in pluginConfig['params']:
value = pluginConfig['params'][param['name']]
ctrl = wx.TextCtrl(pluginPanel, -1, value)
s.Add(wx.StaticText(pluginPanel, -1, param['description']), pos=(3+i,0), span=(1,1), flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,border=3)
s.Add(ctrl, pos=(3+i,2), span=(1,1), flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=3)
ctrl.Bind(wx.EVT_TEXT, self.OnSettingChange)
pluginPanel.paramCtrls[param['name']] = ctrl
i += 1
s.Add(wx.StaticLine(pluginPanel), pos=(3+i,0), span=(1,3), flag=wx.EXPAND|wx.LEFT|wx.RIGHT,border=3)
self.Bind(wx.EVT_BUTTON, self.OnRem, remButton)
s.AddGrowableCol(2)
pluginPanel.SetBackgroundColour(self.GetParent().GetBackgroundColour())
self.pluginEnabledPanel.GetSizer().Add(pluginPanel, flag=wx.EXPAND)
self.pluginEnabledPanel.Layout()
self.pluginEnabledPanel.SetSize((1,1))
self.Layout()
self.pluginEnabledPanel.ScrollChildIntoView(pluginPanel)
self.panelList.append(pluginPanel)
return True
def OnSettingChange(self, e):
for panel in self.panelList:
idx = self.panelList.index(panel)
for k in panel.paramCtrls.keys():
self.pluginConfig[idx]['params'][k] = panel.paramCtrls[k].GetValue()
profile.setPluginConfig(self.pluginConfig)
def OnAdd(self, e):
if self.listbox.GetSelection() < 0:
return
plugin = self.pluginList[self.listbox.GetSelection()]
newConfig = {'filename': plugin['filename'], 'params': {}}
if not self._buildPluginPanel(newConfig):
return
self.pluginConfig.append(newConfig)
profile.setPluginConfig(self.pluginConfig)
def OnRem(self, e):
panel = e.GetEventObject().GetParent()
sizer = self.pluginEnabledPanel.GetSizer()
idx = self.panelList.index(panel)
panel.Show(False)
for p in self.panelList:
sizer.Detach(p)
self.panelList.pop(idx)
for p in self.panelList:
sizer.Add(p, flag=wx.EXPAND)
self.pluginEnabledPanel.Layout()
self.pluginEnabledPanel.SetSize((1,1))
self.Layout()
self.pluginConfig.pop(idx)
profile.setPluginConfig(self.pluginConfig)

View File

@ -168,7 +168,7 @@ class WorkerThread(threading.Thread):
if logLine.startswith('Model error('):
gcodefile.write(';%s\n' % (logLine))
gcodefile.close()
profile.runPostProcessingEffects(gcodeFilename)
profile.runPostProcessingPlugins(gcodeFilename)
self.gcode = gcodeInterpreter.gcode()
self.gcode.load(gcodeFilename)
profile.replaceGCodeTags(gcodeFilename, self.gcode)

View File

@ -4,6 +4,7 @@ from __future__ import division
import __init__
import os, traceback, math, re, zlib, base64, time, sys, platform, glob
import cPickle as pickle
if sys.version_info[0] < 3:
import ConfigParser
else:
@ -73,6 +74,7 @@ profileDefaultSettings = {
'raft_base_material_amount': '100',
'raft_interface_material_amount': '100',
'bottom_thickness': '0.3',
'plugin_config': '',
'add_start_end_gcode': 'True',
'gcode_extension': 'gcode',
@ -531,41 +533,86 @@ def getAlterationFileContents(filename):
alterationContents = ''
return unicode(prefix + re.sub("(.)\{([^\}]*)\}", replaceTagMatch, alterationContents).rstrip() + '\n' + postfix).strip().encode('utf-8')
def getEffectBasePath():
return os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'post_process'))
###### PLUGIN #####
def getEffectsList():
ret = []
for filename in glob.glob(os.path.join(getEffectBasePath(), '*.py')):
filename = os.path.basename(filename)
if filename.startswith('_'):
continue
with open(os.path.join(getEffectBasePath(), filename), "r") as f:
item = {'name': None, 'info': None, 'params': []}
for line in f:
line = line.strip()
if not line.startswith('#'):
break
line = line[1:].split(':', 1)
if len(line) != 2:
continue
if line[0].upper() == 'NAME':
item['name'] = line[1].strip()
elif line[0].upper() == 'INFO':
item['info'] = line[1].strip()
elif line[0].upper() == 'PARAM':
m = re.match('([a-zA-Z]*)\(([a-zA-Z_]*)\) +(.*)', line[1].strip())
if m != None:
item['params'].append({'name': m.group(1), 'type': m.group(2), 'description': m.group(3)})
else:
print "Unknown item in effect meta data: %s %s" % (line[0], line[1])
if item['name'] != None:
ret.append(item)
def getPluginConfig():
try:
return pickle.loads(getProfileSetting('plugin_config'))
except:
return []
def setPluginConfig(config):
putProfileSetting('plugin_config', pickle.dumps(config))
def getPluginBasePaths():
ret = [os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'plugins'))]
if platform.system() != "Windows":
ret.append(os.path.expanduser('~/.cura/plugins/'))
return ret
def runPostProcessingEffects(filename):
pass
#print "runPostProcessingEffects: %s" % (filename)
def getPluginList():
ret = []
for basePath in getPluginBasePaths():
for filename in glob.glob(os.path.join(basePath, '*.py')):
filename = os.path.basename(filename)
if filename.startswith('_'):
continue
with open(os.path.join(basePath, filename), "r") as f:
item = {'filename': filename, 'name': None, 'info': None, 'type': None, 'params': []}
for line in f:
line = line.strip()
if not line.startswith('#'):
break
line = line[1:].split(':', 1)
if len(line) != 2:
continue
if line[0].upper() == 'NAME':
item['name'] = line[1].strip()
elif line[0].upper() == 'INFO':
item['info'] = line[1].strip()
elif line[0].upper() == 'TYPE':
item['type'] = line[1].strip()
elif line[0].upper() == 'PARAM':
m = re.match('([a-zA-Z]*)\(([a-zA-Z_]*)(?:\:([^\)]*))?\) +(.*)', line[1].strip())
if m != None:
item['params'].append({'name': m.group(1), 'type': m.group(2), 'default': m.group(3), 'description': m.group(4)})
else:
print "Unknown item in effect meta data: %s %s" % (line[0], line[1])
if item['name'] != None and item['type'] == 'postprocess':
ret.append(item)
return ret
def runPostProcessingPlugins(gcodefilename):
pluginConfigList = getPluginConfig()
pluginList = getPluginList()
#pythonFile = os.path.join(getEffectBasePath(), 'embedImage.py')
#execfile(pythonFile, {'filename': filename})
for pluginConfig in pluginConfigList:
plugin = None
for pluginTest in pluginList:
if pluginTest['filename'] == pluginConfig['filename']:
plugin = pluginTest
if plugin == None:
continue
pythonFile = None
for basePath in getPluginBasePaths():
testFilename = os.path.join(basePath, pluginConfig['filename'])
if os.path.isfile(testFilename):
pythonFile = testFilename
if pythonFile == None:
continue
locals = {'filename': gcodefilename}
for param in plugin['params']:
value = param['default']
if param['name'] in pluginConfig['params']:
value = pluginConfig['params'][param['name']]
if param['type'] == 'float':
try:
value = float(value)
except:
value = 0.0
locals[param['name']] = value
execfile(pythonFile, locals)