Add webcam timelaps support for windows.

master
Daid 2012-09-02 14:27:24 +02:00
parent 3a5faefb43
commit 4d6bc5efae
3 changed files with 124 additions and 5 deletions

View File

@ -6,6 +6,7 @@ from wx.lib import buttons
from gui import icon from gui import icon
from gui import toolbarUtil from gui import toolbarUtil
from gui import webcam
from util import machineCom from util import machineCom
from util import profile from util import profile
from util import gcodeInterpreter from util import gcodeInterpreter
@ -18,7 +19,6 @@ def printFile(filename):
printWindowMonitorHandle = printProcessMonitor() printWindowMonitorHandle = printProcessMonitor()
printWindowMonitorHandle.loadFile(filename) printWindowMonitorHandle.loadFile(filename)
def startPrintInterface(filename): def startPrintInterface(filename):
#startPrintInterface is called from the main script when we want the printer interface to run in a seperate process. #startPrintInterface is called from the main script when we want the printer interface to run in a seperate process.
# 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). # 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).
@ -97,6 +97,12 @@ class printWindow(wx.Frame):
self.pause = False self.pause = False
self.termHistory = [] self.termHistory = []
self.termHistoryIdx = 0 self.termHistoryIdx = 0
self.cam = None
try:
self.cam = webcam.webcam()
except:
pass
#self.SetIcon(icon.getMainIcon()) #self.SetIcon(icon.getMainIcon())
@ -128,7 +134,7 @@ class printWindow(wx.Frame):
self.sizer.Add(self.progress, pos=(5,0), span=(1,2), flag=wx.EXPAND) self.sizer.Add(self.progress, pos=(5,0), span=(1,2), flag=wx.EXPAND)
nb = wx.Notebook(self.panel) nb = wx.Notebook(self.panel)
self.sizer.Add(nb, pos=(0,3), span=(7,4)) self.sizer.Add(nb, pos=(0,3), span=(7,4), flag=wx.EXPAND)
self.temperaturePanel = wx.Panel(nb) self.temperaturePanel = wx.Panel(nb)
sizer = wx.GridBagSizer(2, 2) sizer = wx.GridBagSizer(2, 2)
@ -233,9 +239,20 @@ class printWindow(wx.Frame):
sizer.AddGrowableRow(0) sizer.AddGrowableRow(0)
nb.AddPage(self.termPanel, 'Term') nb.AddPage(self.termPanel, 'Term')
if self.cam != None:
self.camPage = wx.Panel(nb)
sizer = wx.GridBagSizer(2, 2)
self.camPage.SetSizer(sizer)
nb.AddPage(self.camPage, 'Camera')
self.camPage.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnCameraTimer, self.camPage.timer)
self.camPage.timer.Start(500)
self.camPage.Bind(wx.EVT_ERASE_BACKGROUND, self.OnCameraEraseBackground)
self.sizer.AddGrowableRow(3) self.sizer.AddGrowableRow(3)
self.sizer.AddGrowableCol(0) self.sizer.AddGrowableCol(3)
self.Bind(wx.EVT_CLOSE, self.OnClose) self.Bind(wx.EVT_CLOSE, self.OnClose)
self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect) self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
@ -261,6 +278,23 @@ class printWindow(wx.Frame):
self.UpdateButtonStates() self.UpdateButtonStates()
self.UpdateProgress() self.UpdateProgress()
def OnCameraTimer(self, e):
if self.printIdx != None:
return
self.cam.takeNewImage()
self.camPage.Refresh()
def OnCameraEraseBackground(self, e):
dc = e.GetDC()
if not dc:
dc = wx.ClientDC(self)
rect = self.GetUpdateRegion().GetBox()
dc.SetClippingRect(rect)
dc.SetBackground(wx.Brush(self.camPage.GetBackgroundColour(), wx.SOLID))
dc.Clear()
if self.cam.getLastImage() != None:
dc.DrawBitmap(self.cam.getLastImage(), 0, 0)
def UpdateButtonStates(self): def UpdateButtonStates(self):
self.connectButton.Enable(not self.machineConnected) self.connectButton.Enable(not self.machineConnected)
#self.loadButton.Enable(self.printIdx == None) #self.loadButton.Enable(self.printIdx == None)
@ -287,6 +321,7 @@ class printWindow(wx.Frame):
status += 'Line: -/%d\n' % (len(self.gcodeList)) status += 'Line: -/%d\n' % (len(self.gcodeList))
else: else:
status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList)) status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList))
status += 'Height: %f\n' % (self.currentZ)
self.progress.SetValue(self.printIdx) self.progress.SetValue(self.printIdx)
if self.temp != None: if self.temp != None:
status += 'Temp: %d\n' % (self.temp) status += 'Temp: %d\n' % (self.temp)
@ -317,12 +352,17 @@ class printWindow(wx.Frame):
return return
if self.printIdx != None: if self.printIdx != None:
return return
self.currentZ = -1
if self.cam != None:
self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
self.printIdx = 1 self.printIdx = 1
self.sendLine(0) self.sendLine(0)
self.sendCnt = self.bufferLineCount self.sendCnt = self.bufferLineCount
self.UpdateButtonStates() self.UpdateButtonStates()
def OnCancel(self, e): def OnCancel(self, e):
if self.cam != None:
self.cam.endTimelaps()
self.printIdx = None self.printIdx = None
self.pause = False self.pause = False
self.pauseButton.SetLabel('Pause') self.pauseButton.SetLabel('Pause')
@ -405,6 +445,7 @@ class printWindow(wx.Frame):
gcode = gcodeInterpreter.gcode() gcode = gcodeInterpreter.gcode()
gcode.loadList(gcodeList) gcode.loadList(gcodeList)
print "Loaded: %s (%d)" % (filename, len(gcodeList)) print "Loaded: %s (%d)" % (filename, len(gcodeList))
self.filename = filename
self.gcode = gcode self.gcode = gcode
self.gcodeList = gcodeList self.gcodeList = gcodeList
self.typeList = typeList self.typeList = typeList
@ -442,6 +483,12 @@ class printWindow(wx.Frame):
line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioFill)), line) line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioFill)), line)
if self.typeList[lineNr] == 'SUPPORT': if self.typeList[lineNr] == 'SUPPORT':
line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioSupport)), line) line = re.sub('F([0-9]*)', lambda m: 'F' + str(int(int(m.group(1)) * self.feedrateRatioSupport)), line)
if 'G1' in line and 'Z' in line:
z = float(re.search('Z([0-9\.]*)', line).group(1))
if self.cam != None and self.currentZ != z:
wx.CallAfter(self.cam.takeNewImage)
wx.CallAfter(self.camPage.Refresh)
self.currentZ = z
except: except:
print "Unexpected error:", sys.exc_info() print "Unexpected error:", sys.exc_info()
checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line))) checksum = reduce(lambda x,y:x^y, map(ord, "N%d%s" % (lineNr, line)))
@ -487,6 +534,8 @@ class printWindow(wx.Frame):
if self.sendLine(self.printIdx): if self.sendLine(self.printIdx):
self.printIdx += 1 self.printIdx += 1
else: else:
if self.cam != None:
self.cam.endTimelaps()
self.printIdx = None self.printIdx = None
wx.CallAfter(self.UpdateButtonStates) wx.CallAfter(self.UpdateButtonStates)
wx.CallAfter(self.UpdateProgress) wx.CallAfter(self.UpdateProgress)
@ -576,6 +625,10 @@ class temperatureGraph(wx.Panel):
self.points.pop(0) self.points.pop(0)
def addPoint(self, temp, tempSP, bedTemp, bedTempSP): def addPoint(self, temp, tempSP, bedTemp, bedTempSP):
if bedTemp == None:
bedTemp = 0
if bedTempSP == None:
bedTempSP = 0
self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time())) self.points.append((temp, tempSP, bedTemp, bedTempSP, time.time()))
wx.CallAfter(self.UpdateDrawing) wx.CallAfter(self.UpdateDrawing)

66
Cura/gui/webcam.py Normal file
View File

@ -0,0 +1,66 @@
import os, glob, subprocess
import wx
try:
#Use the vidcap library directly from the VideoCapture package. (Windows only)
# http://videocapture.sourceforge.net/
# We're using the binary interface, not the python interface, so we don't depend on PIL
import vidcap as win32vidcap
except:
win32vidcap = None
#TODO: We can also use OpenCV for camera capture. This should be cross platform compatible.
class webcam(object):
def __init__(self):
if win32vidcap != None:
self._cam = win32vidcap.new_Dev(0, False)
#self._cam.displaycapturefilterproperties()
#self._cam.displaycapturepinproperties()
else:
raise exception("No camera implementation available")
self._doTimelaps = False
self._bitmap = None
def takeNewImage(self):
buffer, width, height = self._cam.getbuffer()
wxImage = wx.EmptyImage(width, height)
wxImage.SetData(buffer[::-1])
if self._bitmap != None:
del self._bitmap
self._bitmap = wxImage.ConvertToBitmap()
del wxImage
del buffer
if self._doTimelaps:
filename = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg" % (self._snapshotCount)))
self._snapshotCount += 1
self._bitmap.SaveFile(filename, wx.BITMAP_TYPE_JPEG)
return self._bitmap
def getLastImage(self):
return self._bitmap
def startTimelaps(self, filename):
self._cleanTempDir()
self._timelapsFilename = filename
self._snapshotCount = 0
self._doTimelaps = True
def endTimelaps(self):
if self._doTimelaps:
ffmpeg = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../ffmpeg.exe"))
basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap", "__tmp_snap_%04d.jpg"))
subprocess.call([ffmpeg, '-r', '12.5', '-i', basePath, '-vcodec', 'mpeg2video', '-pix_fmt', 'yuv420p', '-r', '25', '-y', '-b:v', '1500k', '-f', 'vob', self._timelapsFilename])
self._doTimelaps = False
def _cleanTempDir(self):
basePath = os.path.normpath(os.path.join(os.path.split(__file__)[0], "../__tmp_snap"))
try:
os.makedirs(basePath)
except:
pass
for filename in glob.iglob(basePath + "/*.jpg"):
os.remove(filename)

View File

@ -44,7 +44,7 @@ class VirtualPrinter():
def write(self, data): def write(self, data):
if self.readList == None: if self.readList == None:
return return
print "Send: %s" % (data.rstrip()) #print "Send: %s" % (data.rstrip())
if 'M104' in data or 'M109' in data: if 'M104' in data or 'M109' in data:
try: try:
self.targetTemp = float(data[data.find('S')+1:]) self.targetTemp = float(data[data.find('S')+1:])
@ -78,7 +78,7 @@ class VirtualPrinter():
if self.readList == None: if self.readList == None:
return '' return ''
time.sleep(0.01) time.sleep(0.01)
print "Recv: %s" % (self.readList[0].rstrip()) #print "Recv: %s" % (self.readList[0].rstrip())
return self.readList.pop(0) return self.readList.pop(0)
def close(self): def close(self):