Add webcam timelaps support for windows.
parent
3a5faefb43
commit
4d6bc5efae
|
@ -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).
|
||||||
|
@ -98,6 +98,12 @@ class printWindow(wx.Frame):
|
||||||
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())
|
||||||
|
|
||||||
self.SetSizer(wx.BoxSizer())
|
self.SetSizer(wx.BoxSizer())
|
||||||
|
@ -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)
|
||||||
|
@ -234,8 +240,19 @@ class printWindow(wx.Frame):
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
|
@ -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):
|
||||||
|
|
Loading…
Reference in New Issue