From 655eb31f4e0be20734ab1490d0a319d25e60e0cb Mon Sep 17 00:00:00 2001 From: daid303 Date: Wed, 31 Oct 2012 11:04:08 +0100 Subject: [PATCH] Add post processing plugin support. --- Cura/gui/mainWindow.py | 31 ++------ Cura/gui/pluginPanel.py | 140 ++++++++++++++++++++++++++++++++++ Cura/gui/sliceProgessPanel.py | 2 +- Cura/util/profile.py | 113 +++++++++++++++++++-------- 4 files changed, 227 insertions(+), 59 deletions(-) create mode 100644 Cura/gui/pluginPanel.py diff --git a/Cura/gui/mainWindow.py b/Cura/gui/mainWindow.py index c20e9c91..6fa27cac 100644 --- a/Cura/gui/mainWindow.py +++ b/Cura/gui/mainWindow.py @@ -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() diff --git a/Cura/gui/pluginPanel.py b/Cura/gui/pluginPanel.py new file mode 100644 index 00000000..fa0599ad --- /dev/null +++ b/Cura/gui/pluginPanel.py @@ -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) diff --git a/Cura/gui/sliceProgessPanel.py b/Cura/gui/sliceProgessPanel.py index cc2a34c1..98cd2496 100644 --- a/Cura/gui/sliceProgessPanel.py +++ b/Cura/gui/sliceProgessPanel.py @@ -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) diff --git a/Cura/util/profile.py b/Cura/util/profile.py index 16d15fec..0458622d 100644 --- a/Cura/util/profile.py +++ b/Cura/util/profile.py @@ -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(): +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 getPluginList(): 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) + 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 runPostProcessingEffects(filename): - pass - #print "runPostProcessingEffects: %s" % (filename) +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) -- 2.30.2