\r
from gui import icon\r
from gui import toolbarUtil\r
+from gui import webcam\r
from util import machineCom\r
from util import profile\r
from util import gcodeInterpreter\r
printWindowMonitorHandle = printProcessMonitor()\r
printWindowMonitorHandle.loadFile(filename)\r
\r
-\r
def startPrintInterface(filename):\r
#startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.\r
# It needs to run in a seperate process, as any running python code blocks the GCode sender pyton code (http://wiki.python.org/moin/GlobalInterpreterLock).\r
self.pause = False\r
self.termHistory = []\r
self.termHistoryIdx = 0\r
+ \r
+ self.cam = None\r
+ try:\r
+ self.cam = webcam.webcam()\r
+ except:\r
+ pass\r
\r
#self.SetIcon(icon.getMainIcon())\r
\r
self.sizer.Add(self.progress, pos=(5,0), span=(1,2), flag=wx.EXPAND)\r
\r
nb = wx.Notebook(self.panel)\r
- self.sizer.Add(nb, pos=(0,3), span=(7,4))\r
+ self.sizer.Add(nb, pos=(0,3), span=(7,4), flag=wx.EXPAND)\r
\r
self.temperaturePanel = wx.Panel(nb)\r
sizer = wx.GridBagSizer(2, 2)\r
sizer.AddGrowableRow(0)\r
\r
nb.AddPage(self.termPanel, 'Term')\r
+ \r
+ if self.cam != None:\r
+ self.camPage = wx.Panel(nb)\r
+ sizer = wx.GridBagSizer(2, 2)\r
+ self.camPage.SetSizer(sizer)\r
+ \r
+ nb.AddPage(self.camPage, 'Camera')\r
+ self.camPage.timer = wx.Timer(self)\r
+ self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPage.timer)\r
+ self.camPage.timer.Start(500)\r
+ self.camPage.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)\r
\r
self.sizer.AddGrowableRow(3)\r
- self.sizer.AddGrowableCol(0)\r
+ self.sizer.AddGrowableCol(3)\r
\r
self.Bind(wx.EVT_CLOSE, self.OnClose)\r
self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)\r
self.UpdateButtonStates()\r
self.UpdateProgress()\r
\r
+ def OnCameraTimer(self, e):\r
+ if self.printIdx != None:\r
+ return\r
+ self.cam.takeNewImage()\r
+ self.camPage.Refresh()\r
+ \r
+ def OnCameraEraseBackground(self, e):\r
+ dc = e.GetDC()\r
+ if not dc:\r
+ dc = wx.ClientDC(self)\r
+ rect = self.GetUpdateRegion().GetBox()\r
+ dc.SetClippingRect(rect)\r
+ dc.SetBackground(wx.Brush(self.camPage.GetBackgroundColour(), wx.SOLID))\r
+ dc.Clear()\r
+ if self.cam.getLastImage() != None:\r
+ dc.DrawBitmap(self.cam.getLastImage(), 0, 0)\r
+\r
def UpdateButtonStates(self):\r
self.connectButton.Enable(not self.machineConnected)\r
#self.loadButton.Enable(self.printIdx == None)\r
status += 'Line: -/%d\n' % (len(self.gcodeList))\r
else:\r
status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList))\r
+ status += 'Height: %f\n' % (self.currentZ)\r
self.progress.SetValue(self.printIdx)\r
if self.temp != None:\r
status += 'Temp: %d\n' % (self.temp)\r
return\r
if self.printIdx != None:\r
return\r
+ self.currentZ = -1\r
+ if self.cam != None:\r
+ self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")\r
self.printIdx = 1\r
self.sendLine(0)\r
self.sendCnt = self.bufferLineCount\r
self.UpdateButtonStates()\r
\r
def OnCancel(self, e):\r
+ if self.cam != None:\r
+ self.cam.endTimelaps()\r
self.printIdx = None\r
self.pause = False\r
self.pauseButton.SetLabel('Pause')\r
gcode = gcodeInterpreter.gcode()\r
gcode.loadList(gcodeList)\r
print "Loaded: %s (%d)" % (filename, len(gcodeList))\r
+ self.filename = filename\r
self.gcode = gcode\r
self.gcodeList = gcodeList\r
self.typeList = typeList\r
line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioFill)), line)\r
if self.typeList[lineNr] == 'SUPPORT':\r
line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioSupport)), line)\r
+ if 'G1' in line and 'Z' in line:\r
+ z = float(re.search('Z([0-9\.]*)', line).group(1))\r
+ if self.cam != None and self.currentZ != z:\r
+ wx.CallAfter(self.cam.takeNewImage)\r
+ wx.CallAfter(self.camPage.Refresh)\r
+ self.currentZ = z\r
except:\r
print "Unexpected error:", sys.exc_info()\r
checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))\r
if self.sendLine(self.printIdx):\r
self.printIdx += 1\r
else:\r
+ if self.cam != None:\r
+ self.cam.endTimelaps()\r
self.printIdx = None\r
wx.CallAfter(self.UpdateButtonStates)\r
wx.CallAfter(self.UpdateProgress)\r
self.points.pop(0)\r
\r
def addPoint(self, temp, tempSP, bedTemp, bedTempSP):\r
+ if bedTemp == None:\r
+ bedTemp = 0\r
+ if bedTempSP == None:\r
+ bedTempSP = 0\r
self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))\r
wx.CallAfter(self.UpdateDrawing)\r
\r
--- /dev/null
+import os, glob, subprocess\r
+import wx\r
+\r
+try:\r
+ #Use the vidcap library directly from the VideoCapture package. (Windows only)\r
+ # http://videocapture.sourceforge.net/\r
+ # We're using the binary interface, not the python interface, so we don't depend on PIL\r
+ import vidcap as win32vidcap\r
+except:\r
+ win32vidcap = None\r
+\r
+#TODO: We can also use OpenCV for camera capture. This should be cross platform compatible.\r
+\r
+class webcam(object):\r
+ def __init__(self):\r
+ if win32vidcap != None:\r
+ self._cam = win32vidcap.new_Dev(0, False)\r
+ #self._cam.displaycapturefilterproperties()\r
+ #self._cam.displaycapturepinproperties()\r
+ else:\r
+ raise exception("No camera implementation available")\r
+ \r
+ self._doTimelaps = False\r
+ self._bitmap = None\r
+ \r
+ def takeNewImage(self):\r
+ buffer, width, height = self._cam.getbuffer()\r
+ wxImage = wx.EmptyImage(width, height)\r
+ wxImage.SetData(buffer[::-1])\r
+ if self._bitmap != None:\r
+ del self._bitmap\r
+ self._bitmap = wxImage.ConvertToBitmap()\r
+ del wxImage\r
+ del buffer\r
+\r
+ if self._doTimelaps:\r
+ filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg" % (self._snapshotCount)))\r
+ self._snapshotCount += 1\r
+ self._bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG)\r
+\r
+ return self._bitmap\r
+ \r
+ def getLastImage(self):\r
+ return self._bitmap\r
+ \r
+ def startTimelaps(self, filename):\r
+ self._cleanTempDir()\r
+ self._timelapsFilename = filename\r
+ self._snapshotCount = 0\r
+ self._doTimelaps = True\r
+ \r
+ def endTimelaps(self):\r
+ if self._doTimelaps:\r
+ ffmpeg = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe"))\r
+ basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg"))\r
+ subprocess.call([ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', '1500k', '-f', 'vob', self._timelapsFilename])\r
+ self._doTimelaps = False\r
+ \r
+ def _cleanTempDir(self):\r
+ basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap"))\r
+ try:\r
+ os.makedirs(basePath)\r
+ except:\r
+ pass\r
+ for filename in glob.iglob(basePath + "/*.jpg"):\r
+ os.remove(filename)\r