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 toolbarUtil
|
||||
from gui import webcam
|
||||
from util import machineCom
|
||||
from util import profile
|
||||
from util import gcodeInterpreter
|
||||
|
@ -18,7 +19,6 @@ def printFile(filename):
|
|||
printWindowMonitorHandle = printProcessMonitor()
|
||||
printWindowMonitorHandle.loadFile(filename)
|
||||
|
||||
|
||||
def startPrintInterface(filename):
|
||||
#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).
|
||||
|
@ -97,6 +97,12 @@ class printWindow(wx.Frame):
|
|||
self.pause = False
|
||||
self.termHistory = []
|
||||
self.termHistoryIdx = 0
|
||||
|
||||
self.cam = None
|
||||
try:
|
||||
self.cam = webcam.webcam()
|
||||
except:
|
||||
pass
|
||||
|
||||
#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)
|
||||
|
||||
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)
|
||||
sizer = wx.GridBagSizer(2, 2)
|
||||
|
@ -233,9 +239,20 @@ class printWindow(wx.Frame):
|
|||
sizer.AddGrowableRow(0)
|
||||
|
||||
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.AddGrowableCol(0)
|
||||
self.sizer.AddGrowableCol(3)
|
||||
|
||||
self.Bind(wx.EVT_CLOSE, self.OnClose)
|
||||
self.connectButton.Bind(wx.EVT_BUTTON, self.OnConnect)
|
||||
|
@ -261,6 +278,23 @@ class printWindow(wx.Frame):
|
|||
self.UpdateButtonStates()
|
||||
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):
|
||||
self.connectButton.Enable(not self.machineConnected)
|
||||
#self.loadButton.Enable(self.printIdx == None)
|
||||
|
@ -287,6 +321,7 @@ class printWindow(wx.Frame):
|
|||
status += 'Line: -/%d\n' % (len(self.gcodeList))
|
||||
else:
|
||||
status += 'Line: %d/%d\n' % (self.printIdx, len(self.gcodeList))
|
||||
status += 'Height: %f\n' % (self.currentZ)
|
||||
self.progress.SetValue(self.printIdx)
|
||||
if self.temp != None:
|
||||
status += 'Temp: %d\n' % (self.temp)
|
||||
|
@ -317,12 +352,17 @@ class printWindow(wx.Frame):
|
|||
return
|
||||
if self.printIdx != None:
|
||||
return
|
||||
self.currentZ = -1
|
||||
if self.cam != None:
|
||||
self.cam.startTimelaps(self.filename[: self.filename.rfind('.')] + ".mpg")
|
||||
self.printIdx = 1
|
||||
self.sendLine(0)
|
||||
self.sendCnt = self.bufferLineCount
|
||||
self.UpdateButtonStates()
|
||||
|
||||
def OnCancel(self, e):
|
||||
if self.cam != None:
|
||||
self.cam.endTimelaps()
|
||||
self.printIdx = None
|
||||
self.pause = False
|
||||
self.pauseButton.SetLabel('Pause')
|
||||
|
@ -405,6 +445,7 @@ class printWindow(wx.Frame):
|
|||
gcode = gcodeInterpreter.gcode()
|
||||
gcode.loadList(gcodeList)
|
||||
print "Loaded: %s (%d)" % (filename, len(gcodeList))
|
||||
self.filename = filename
|
||||
self.gcode = gcode
|
||||
self.gcodeList = gcodeList
|
||||
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)
|
||||
if self.typeList[lineNr] == 'SUPPORT':
|
||||
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:
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
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):
|
||||
self.printIdx += 1
|
||||
else:
|
||||
if self.cam != None:
|
||||
self.cam.endTimelaps()
|
||||
self.printIdx = None
|
||||
wx.CallAfter(self.UpdateButtonStates)
|
||||
wx.CallAfter(self.UpdateProgress)
|
||||
|
@ -576,6 +625,10 @@ class temperatureGraph(wx.Panel):
|
|||
self.points.pop(0)
|
||||
|
||||
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()))
|
||||
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):
|
||||
if self.readList == None:
|
||||
return
|
||||
print "Send: %s" % (data.rstrip())
|
||||
#print "Send: %s" % (data.rstrip())
|
||||
if 'M104' in data or 'M109' in data:
|
||||
try:
|
||||
self.targetTemp = float(data[data.find('S')+1:])
|
||||
|
@ -78,7 +78,7 @@ class VirtualPrinter():
|
|||
if self.readList == None:
|
||||
return ''
|
||||
time.sleep(0.01)
|
||||
print "Recv: %s" % (self.readList[0].rstrip())
|
||||
#print "Recv: %s" % (self.readList[0].rstrip())
|
||||
return self.readList.pop(0)
|
||||
|
||||
def close(self):
|
||||
|
|
Loading…
Reference in New Issue