Commit af109a0e authored by Guillaume Seguin's avatar Guillaume Seguin

Initial excluder tool implementation (#266)

parent 8e6fa0d8
......@@ -74,6 +74,7 @@ class printcore():
self.tempcb = None #impl (wholeline)
self.recvcb = None #impl (wholeline)
self.sendcb = None #impl (wholeline)
self.preprintsendcb = None #impl (wholeline)
self.printsendcb = None #impl (wholeline)
self.layerchangecb = None #impl (wholeline)
self.errorcb = None #impl (wholeline)
......@@ -414,7 +415,9 @@ class printcore():
if self.startcb:
#callback for printing started
try: self.startcb(resuming)
except: pass
except:
print "Print start callback failed with:"
traceback.print_exc(file = sys.stdout)
while self.printing and self.printer and self.online:
self._sendnext()
self.sentlines = {}
......@@ -423,10 +426,12 @@ class printcore():
if self.endcb:
#callback for printing done
try: self.endcb()
except: pass
except:
print "Print end callback failed with:"
traceback.print_exc(file = sys.stdout)
except:
print "Print thread died due to the following error:"
traceback.print_exc()
traceback.print_exc(file = sys.stdout)
finally:
self.print_thread = None
self._start_sender()
......@@ -466,10 +471,17 @@ class printcore():
if prev_layer != layer:
try: self.layerchangecb(layer)
except: traceback.print_exc()
if self.preprintsendcb:
gline = self.preprintsendcb(gline)
if gline == None:
self.queueindex += 1
self.clear = True
return
tline = gline.raw
if tline.lstrip().startswith(";@"): # check for host command
self.processHostCommand(tline)
self.queueindex += 1
self.clear = True
return
tline = tline.split(";")[0]
......
# This file is part of the Printrun suite.
#
# Printrun 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.
#
# Printrun 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 Printrun. If not, see <http://www.gnu.org/licenses/>.
import wx
from printrun import gviz
from printrun_utils import imagefile, install_locale
install_locale('pronterface')
class ExcluderWindow(gviz.GvizWindow):
def __init__(self, excluder, *args, **kwargs):
super(ExcluderWindow, self).__init__(*args, **kwargs)
self.SetTitle(_("Part excluder: draw rectangles where print instructions should be ignored"))
self.toolbar.AddLabelTool(128, " " + _("Reset selection"), wx.Image(imagefile('reset.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), shortHelp = _("Reset selection"), longHelp = "")
self.Bind(wx.EVT_TOOL, self.reset_selection, id = 128)
self.parent = excluder
self.p.paint_overlay = self.paint_selection
self.p.layerup()
def real_to_gcode(self, x, y):
return (x + self.p.build_dimensions[3],
self.p.build_dimensions[4] + self.p.build_dimensions[1] - y)
def gcode_to_real(self, x, y):
return (x - self.p.build_dimensions[3],
self.p.build_dimensions[1] - (y - self.p.build_dimensions[4]))
def mouse(self, event):
if event.ButtonUp(wx.MOUSE_BTN_LEFT) or event.ButtonUp(wx.MOUSE_BTN_RIGHT):
self.initpos = None
elif event.Dragging() and event.RightIsDown():
e = event.GetPositionTuple()
if not self.initpos or not hasattr(self, "basetrans"):
self.initpos = e
self.basetrans = self.p.translate
self.p.translate = [self.basetrans[0] + (e[0] - self.initpos[0]),
self.basetrans[1] + (e[1] - self.initpos[1])]
self.p.dirty = 1
wx.CallAfter(self.p.Refresh)
elif event.Dragging() and event.LeftIsDown():
x, y = event.GetPositionTuple()
if not hasattr(self, "basetrans"):
self.basetrans = self.p.translate
x = (x - self.basetrans[0]) / self.p.scale[0]
y = (y - self.basetrans[1]) / self.p.scale[1]
x, y = self.real_to_gcode(x, y)
if not self.initpos:
self.initpos = (x, y)
self.basetrans = self.p.translate
self.parent.rectangles.append((0, 0, 0, 0))
else:
pos = (x, y)
x0 = min(self.initpos[0], pos[0])
y0 = min(self.initpos[1], pos[1])
x1 = max(self.initpos[0], pos[0])
y1 = max(self.initpos[1], pos[1])
self.parent.rectangles[-1] = (x0, y0, x1, y1)
wx.CallAfter(self.p.Refresh)
else:
event.Skip()
def _line_scaler(self, orig):
x0, y0 = self.gcode_to_real(orig[0], orig[1])
x0 = self.p.scale[0]*x0 + self.p.translate[0]
y0 = self.p.scale[1]*y0 + self.p.translate[1]
x1, y1 = self.gcode_to_real(orig[2], orig[3])
x1 = self.p.scale[0]*x1 + self.p.translate[0]
y1 = self.p.scale[1]*y1 + self.p.translate[1]
width = max(x0, x1) - min(x0, x1) + 1
height = max(y0, y1) - min(y0, y1) + 1
return (min(x0, x1), min(y0, y1), width, height,)
def paint_selection(self, dc):
dc = wx.GCDC(dc)
dc.SetPen(wx.TRANSPARENT_PEN)
dc.DrawRectangleList([self._line_scaler(rect) for rect in self.parent.rectangles],
None, wx.Brush((200, 200, 200, 150)))
def reset_selection(self, event):
self.parent.rectangles = []
wx.CallAfter(self.p.Refresh)
class Excluder(object):
def __init__(self):
self.rectangles = []
self.window = None
def pop_window(self, gcode, *args, **kwargs):
if not self.window:
self.window = ExcluderWindow(self, *args, **kwargs)
self.window.p.addfile(gcode)
self.window.p.layerup()
self.window.Bind(wx.EVT_CLOSE, self.close_window)
self.window.Show()
else:
self.window.Show()
self.window.Raise()
def close_window(self, event = None):
if self.window:
self.window.Destroy()
self.window = None
if __name__ == '__main__':
import sys
gcode = gcoder.GCode(open(sys.argv[1]))
app = wx.App(False)
ex = Excluder()
ex.pop_window(gcode)
app.MainLoop()
......@@ -45,6 +45,7 @@ if os.name == "nt":
import printcore
from printrun.printrun_utils import pixmapfile, configfile
from printrun.gui import MainWindow
from printrun.excluder import Excluder
import pronsole
from pronsole import dosify, wxSetting, HiddenSetting, StringSetting, SpinSetting, FloatSpinSetting, BooleanSetting, StaticTextSetting
from printrun import gcoder
......@@ -175,6 +176,15 @@ class ComboSetting(wxSetting):
return self.widget
class PronterWindow(MainWindow, pronsole.pronsole):
_fgcode = None
def _get_fgcode(self):
return self._fgcode
def _set_fgcode(self, value):
self._fgcode = value
self.excluder = None
fgcode = property(_get_fgcode, _set_fgcode)
def __init__(self, filename = None, size = winsize):
pronsole.pronsole.__init__(self)
#default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed and endstops at 0, 0 and 0
......@@ -218,6 +228,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.userm105 = 0
self.monitor = 0
self.fgcode = None
self.excluder = None
self.skeinp = None
self.monitor_interval = 3
self.current_pos = [0, 0, 0]
......@@ -292,6 +303,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skeining = 0
self.mini = False
self.p.sendcb = self.sentcb
self.p.preprintsendcb = self.preprintsendcb
self.p.printsendcb = self.printsentcb
self.p.layerchangecb = self.layer_change_cb
self.p.startcb = self.startcb
......@@ -395,6 +407,18 @@ class PronterWindow(MainWindow, pronsole.pronsole):
return
self.sentlines.put_nowait(line)
def preprintsendcb(self, gline):
if not self.excluder or not self.excluder.rectangles:
return gline
for (x0, y0, x1, y1) in self.excluder.rectangles:
if not gline.is_move: continue
if x0 <= gline.current_x <= x1 and y0 <= gline.current_y <= y1:
if gline.e != None and not gline.relative_e:
return gcoder.Line("G92 E%.5f" % gline.e)
else:
return None
return gline
def printsentcb(self, gline):
if gline.is_move and hasattr(self.gwindow, "set_current_gline"):
wx.CallAfter(self.gwindow.set_current_gline, gline)
......@@ -538,6 +562,14 @@ class PronterWindow(MainWindow, pronsole.pronsole):
from printrun import projectlayer
projectlayer.SettingsFrame(self, self.p).Show()
def exclude(self, event):
if not self.fgcode:
wx.CallAfter(self.statusbar.SetStatusText, _("No file loaded. Please use load first."))
return
if not self.excluder:
self.excluder = Excluder()
self.excluder.pop_window(self.fgcode, bgcolor = self.settings.bgcolor)
def popmenu(self):
self.menustrip = wx.MenuBar()
# File menu
......@@ -545,6 +577,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.Bind(wx.EVT_MENU, self.loadfile, m.Append(-1, _("&Open..."), _(" Opens file")))
self.Bind(wx.EVT_MENU, self.do_editgcode, m.Append(-1, _("&Edit..."), _(" Edit open file")))
self.Bind(wx.EVT_MENU, self.clearOutput, m.Append(-1, _("Clear console"), _(" Clear output console")))
self.Bind(wx.EVT_MENU, self.exclude, m.Append(-1, _("Excluder"), _(" Exclude parts of the bed from being printed")))
self.Bind(wx.EVT_MENU, self.project, m.Append(-1, _("Projector"), _(" Project slices")))
self.Bind(wx.EVT_MENU, self.OnExit, m.Append(wx.ID_EXIT, _("E&xit"), _(" Closes the Window")))
self.menustrip.Append(m, _("&File"))
......@@ -1121,6 +1154,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.save_in_rc("set xy_feedrate", "set xy_feedrate %d" % self.settings.xy_feedrate)
self.save_in_rc("set z_feedrate", "set z_feedrate %d" % self.settings.z_feedrate)
self.save_in_rc("set e_feedrate", "set e_feedrate %d" % self.settings.e_feedrate)
if self.excluder:
self.excluder.close_window()
wx.CallAfter(self.gwindow.Destroy)
wx.CallAfter(self.Destroy)
......
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