diff --git a/README.md b/README.md index df4c435..b18e5a0 100644 --- a/README.md +++ b/README.md @@ -5,80 +5,44 @@ Printrun consists of printcore, pronsole and pronterface, and a small collection * pronterface.py is a graphical host software with the same functionality as pronsole * webinterface.py is a browser-usable remote control function for Pronterface -# INSTALLING DEPENDENCIES +# GETTING PRINTRUN + +This section suggests using precompiled binaries, this way you get everything bundled into one single package for an easy installation. + +If you want the newest, shiniest features, you can run Printrun from source using the instructions further down this README. ## Windows A precompiled version is available at http://koti.kapsi.fi/~kliment/printrun/ -Download the following, and install in this order: +## Mac OS X - 1. http://python.org/ftp/python/2.7.2/python-2.7.2.msi - 2. http://pypi.python.org/packages/any/p/pyserial/pyserial-2.5.win32.exe - 3. http://downloads.sourceforge.net/wxpython/wxPython2.8-win32-unicode-2.8.12.0-py27.exe - 4. http://launchpad.net/pyreadline/1.7/1.7/+download/pyreadline-1.7.win32.exe - 5. http://pyglet.googlecode.com/files/pyglet-1.1.4.zip +A precompiled version is available at http://koti.kapsi.fi/~kliment/printrun/ -For the last one, you will need to unpack it, open a command terminal, -go into the the directory you unpacked it in and run -`python setup.py install` +## Linux +### Ubuntu/Debian + +You can run Printrun directly from source, as there are no packages available yet. Fetch and install the dependencies using -## Ubuntu/Debian `sudo apt-get install python-serial python-wxgtk2.8 python-pyglet` -## Fedora 15 +### Fedora 15 and newer + +You can run Printrun directly from source, as there are no packages available yet. Fetch and install the dependencies using + `sudo yum install pyserial wxpython pyglet` -## Mac OS X Lion +### Archlinux -A precompiled version is available at http://koti.kapsi.fi/~kliment/printrun/ +Packages are available in AUR. Just run - 1. Ensure that the active Python is the system version. (`brew uninstall python` or other appropriate incantations) - 2. Download an install [wxPython2.8-osx-unicode] matching to your python version (most likely 2.7 on Lion, - check with: python --version) from: http://wxpython.org/download.php#stable - Known to work PythonWX: http://superb-sea2.dl.sourceforge.net/project/wxpython/wxPython/2.8.12.1/wxPython2.8-osx-unicode-2.8.12.1-universal-py2.7.dmg - 3. Download and unpack pyserial from http://pypi.python.org/packages/source/p/pyserial/pyserial-2.5.tar.gz - 4. In a terminal, change to the folder you unzipped to, then type in: `sudo python setup.py install` - 5. Repeat 4. with http://http://pyglet.googlecode.com/files/pyglet-1.1.4.zip +`yaourt printrun` -The tools will probably run just fine in 64bit on Lion, you don't need to mess -with any of the 32bit settings. In case they don't, try - 5. export VERSIONER_PYTHON_PREFER_32_BIT=yes -in a terminal before running Pronterface - -## Mac OS X (pre Lion) - -A precompiled version is available at http://koti.kapsi.fi/~kliment/printrun/ - - 1. Download and install http://downloads.sourceforge.net/wxpython/wxPython2.8-osx-unicode-2.8.12.0-universal-py2.6.dmg - 2. Grab the source for pyserial from http://pypi.python.org/packages/source/p/pyserial/pyserial-2.5.tar.gz - 3. Unzip pyserial to a folder. Then, in a terminal, change to the folder you unzipped to, then type in: - - `defaults write com.apple.versioner.python Prefer-32-Bit -bool yes` - - `sudo python setup.py install` - -Alternatively, you can run python in 32 bit mode by setting the following environment variable before running the setup.py command: - -This alternative approach is confirmed to work on Mac OS X 10.6.8. - -`export VERSIONER_PYTHON_PREFER_32_BIT=yes` - -`sudo python setup.py install` - -Then repeat the same with http://http://pyglet.googlecode.com/files/pyglet-1.1.4.zip +and enjoy the `pronterface`, `pronsole`, ... commands directly. # USING PRONTERFACE -To use pronterface, you need: - - * python (ideally 2.6.x or 2.7.x), - * pyserial (or python-serial on ubuntu/debian), - * pyglet - * pyreadline (not needed on Linux) and - * wxPython - -Download and install the above, and start pronterface.py +When you're done setting up Printrun, you can start pronterface.py in the directory you unpacked it. Select the port name you are using from the first drop-down, select your baud rate, and hit connect. Load an STL (see the note on skeinforge below) or GCODE file, and you can upload it to SD or print it directly. The "monitor printer" function, when enabled, checks the printer state (temperatures, SD print progress) every 3 seconds. @@ -133,6 +97,73 @@ sender, or the following code example: p.resume() p.disconnect() +# RUNNING FROM SOURCE + +Run Printrun for source if you want to test out the latest features. + +## Dependencies + +To use pronterface, you need: + + * python (ideally 2.6.x or 2.7.x), + * pyserial (or python-serial on ubuntu/debian), + * pyglet + * pyreadline (not needed on Linux) and + * wxPython + +Please see specific instructions for Windows and Mac OS X below. Under Linux, you should use your package manager directly (see the "GETTING PRINTRUN" section) + +## Windows + +Download the following, and install in this order: + + 1. http://python.org/ftp/python/2.7.2/python-2.7.2.msi + 2. http://pypi.python.org/packages/any/p/pyserial/pyserial-2.5.win32.exe + 3. http://downloads.sourceforge.net/wxpython/wxPython2.8-win32-unicode-2.8.12.0-py27.exe + 4. http://launchpad.net/pyreadline/1.7/1.7/+download/pyreadline-1.7.win32.exe + 5. http://pyglet.googlecode.com/files/pyglet-1.1.4.zip + +For the last one, you will need to unpack it, open a command terminal, +go into the the directory you unpacked it in and run +`python setup.py install` + +## Mac OS X Lion + + 1. Ensure that the active Python is the system version. (`brew uninstall python` or other appropriate incantations) + 2. Download an install [wxPython2.8-osx-unicode] matching to your python version (most likely 2.7 on Lion, + check with: python --version) from: http://wxpython.org/download.php#stable + Known to work PythonWX: http://superb-sea2.dl.sourceforge.net/project/wxpython/wxPython/2.8.12.1/wxPython2.8-osx-unicode-2.8.12.1-universal-py2.7.dmg + 3. Download and unpack pyserial from http://pypi.python.org/packages/source/p/pyserial/pyserial-2.5.tar.gz + 4. In a terminal, change to the folder you unzipped to, then type in: `sudo python setup.py install` + 5. Repeat 4. with http://http://pyglet.googlecode.com/files/pyglet-1.1.4.zip + +The tools will probably run just fine in 64bit on Lion, you don't need to mess +with any of the 32bit settings. In case they don't, try + 5. export VERSIONER_PYTHON_PREFER_32_BIT=yes +in a terminal before running Pronterface + +## Mac OS X (pre Lion) + +A precompiled version is available at http://koti.kapsi.fi/~kliment/printrun/ + + 1. Download and install http://downloads.sourceforge.net/wxpython/wxPython2.8-osx-unicode-2.8.12.0-universal-py2.6.dmg + 2. Grab the source for pyserial from http://pypi.python.org/packages/source/p/pyserial/pyserial-2.5.tar.gz + 3. Unzip pyserial to a folder. Then, in a terminal, change to the folder you unzipped to, then type in: + + `defaults write com.apple.versioner.python Prefer-32-Bit -bool yes` + + `sudo python setup.py install` + +Alternatively, you can run python in 32 bit mode by setting the following environment variable before running the setup.py command: + +This alternative approach is confirmed to work on Mac OS X 10.6.8. + +`export VERSIONER_PYTHON_PREFER_32_BIT=yes` + +`sudo python setup.py install` + +Then repeat the same with http://http://pyglet.googlecode.com/files/pyglet-1.1.4.zip + # LICENSE ``` diff --git a/.pronsolerc.example b/dot.pronsolerc.example similarity index 54% rename from .pronsolerc.example rename to dot.pronsolerc.example index 6a2a283..59cff0a 100644 --- a/.pronsolerc.example +++ b/dot.pronsolerc.example @@ -1,17 +1,6 @@ # Sample .pronsolerc file - copy this into your home directory and rename it to .pronsolerc !print "Loaded " + self.rc_filename -macro loud - !if self.p.loud: - !self.p.loud = 0 - !if hasattr(self,"cur_button") and self.cur_button is not None: - !self.onecmd('button %d "loud (off)" /c green loud' % self.cur_button) - !else: - !self.p.loud = 1 - !if hasattr(self,"cur_button") and self.cur_button is not None: - !self.onecmd('button %d "loud (on)" /c yellow loud' % self.cur_button) -button 0 "loud (off)" /c "green" loud - macro fan !global _fan !if '_fan' in globals() and _fan: @@ -24,4 +13,4 @@ macro fan M106 !if hasattr(self,"cur_button") and self.cur_button is not None: !self.onecmd('button %d "fan (on)" /c yellow fan' % self.cur_button) -button 1 "fan (off)" /c "green" fan +button 0 "fan (off)" /c "green" fan diff --git a/plater.py b/plater.py index a0715ce..fee4fbe 100755 --- a/plater.py +++ b/plater.py @@ -35,7 +35,7 @@ from printrun.printrun_utils import pixmapfile glview = False if "-nogl" not in sys.argv: try: - import stlview + from printrun import stlview glview = True except: pass diff --git a/printcore.py b/printcore.py index 7db62fb..c431321 100755 --- a/printcore.py +++ b/printcore.py @@ -179,14 +179,19 @@ class printcore(): #callback for errors try: self.errorcb(line) except: pass + # Teststrings for resend parsing # Firmware exp. result + # line="rs N2 Expected checksum 67" # Teacup 2 if line.lower().startswith("resend") or line.startswith("rs"): - toresend = self.resendfrom - try: - toresend = int(line.replace("N:", " ").replace("N", " ").replace(":", " ").split()[-1]) - except: - if line.startswith("rs"): - toresend = int(line.split()[1]) - self.resendfrom = toresend + line = line.replace("N:"," ").replace("N"," ").replace(":"," ") + linewords = line.split() + while len(linewords) != 0: + try: + toresend = int(linewords.pop(0)) + self.resendfrom = toresend + #print str(toresend) + break + except: + pass self.clear = True self.clear = True diff --git a/pronsole.py b/pronsole.py index 83ad89d..8c1e40e 100755 --- a/pronsole.py +++ b/pronsole.py @@ -621,9 +621,12 @@ class pronsole(cmd.Cmd): def help_disconnect(self): print "Disconnects from the printer" + + def do_load(self,l): + self._do_load(l) - def do_load(self, l): - if len(l) == 0: + def _do_load(self,l): + if len(l)==0: print "No file name given." return print "Loading file:"+l @@ -874,14 +877,16 @@ class pronsole(cmd.Cmd): def default(self, l): if(l[0] in self.commandprefixes.upper()): if(self.p and self.p.online): - print "SENDING:"+l + if(not self.p.loud): + print "SENDING:"+l self.p.send_now(l) else: print "Printer is not online." return elif(l[0] in self.commandprefixes.lower()): if(self.p and self.p.online): - print "SENDING:"+l.upper() + if(not self.p.loud): + print "SENDING:"+l.upper() self.p.send_now(l.upper()) else: print "Printer is not online." diff --git a/pronterface.py b/pronterface.py index fb6639e..4830ea3 100755 --- a/pronterface.py +++ b/pronterface.py @@ -183,6 +183,8 @@ class PronterWindow(wx.Frame, pronsole.pronsole): self.webInterface = None except: print _("CherryPy is not installed. Web Interface Disabled.") + if self.filename is not None: + self.do_load(self.filename) def startcb(self): self.starttime = time.time() @@ -402,9 +404,13 @@ class PronterWindow(wx.Frame, pronsole.pronsole): self.capture_skip[pat] -= 1 self.capture_skip_newline = True return +<<<<<<< HEAD wx.CallAfter(self.logbox.AppendText, l) if self.webInterface: self.webInterface.AppendLog(l) +======= + wx.CallAfter(self.addtexttolog,l); +>>>>>>> experimental def scanserial(self): """scan for available ports. return a list of device names.""" @@ -420,10 +426,17 @@ class PronterWindow(wx.Frame, pronsole.pronsole): pass return baselist+glob.glob('/dev/ttyUSB*') + glob.glob('/dev/ttyACM*') + glob.glob("/dev/tty.*") + glob.glob("/dev/cu.*") + glob.glob("/dev/rfcomm*") +<<<<<<< HEAD def project(self, event): import projectlayer if self.p.online: projectlayer.setframe(self, self.p).Show() +======= + def project(self,event): + from printrun import projectlayer + if(self.p.online): + projectlayer.setframe(self,self.p).Show() +>>>>>>> experimental else: print _("Printer is not online.") if self.webInterface: @@ -447,7 +460,22 @@ class PronterWindow(wx.Frame, pronsole.pronsole): self.Bind(wx.EVT_MENU, self.new_macro, self.macros_menu.Append(-1, _("<&New...>"))) self.Bind(wx.EVT_MENU, lambda *e:options(self), m.Append(-1, _("&Options"), _(" Options dialog"))) +<<<<<<< HEAD self.Bind(wx.EVT_MENU, lambda x: threading.Thread(target = lambda:self.do_skein("set")).start(), m.Append(-1, _("Slicing Settings"), _(" Adjust slicing settings"))) +======= + self.Bind(wx.EVT_MENU, lambda x:threading.Thread(target=lambda :self.do_skein("set")).start(), m.Append(-1,_("Slicing Settings"),_(" Adjust slicing settings"))) + + mItem = m.AppendCheckItem(-1, _("Debug G-code"), + _("Print all G-code sent to and received from the printer.")) + m.Check(mItem.GetId(), self.p.loud) + self.Bind(wx.EVT_MENU, self.setloud, mItem) + + #try: + # from SkeinforgeQuickEditDialog import SkeinforgeQuickEditDialog + # self.Bind(wx.EVT_MENU, lambda *e:SkeinforgeQuickEditDialog(self), m.Append(-1,_("SFACT Quick Settings"),_(" Quickly adjust SFACT settings for active profile"))) + #except: + # pass +>>>>>>> experimental self.menustrip.Append(m, _("&Settings")) self.update_macros_menu() @@ -547,16 +575,27 @@ class PronterWindow(wx.Frame, pronsole.pronsole): uts = self.uppertopsizer = wx.BoxSizer(wx.HORIZONTAL) self.rescanbtn = wx.Button(self.panel,-1, _("Port"), size = buttonSize) self.rescanbtn.SetToolTip(wx.ToolTip("Communication Settings\nClick to rescan ports")) +<<<<<<< HEAD self.rescanbtn.Bind(wx.EVT_BUTTON, self.rescanports) uts.Add(self.rescanbtn, 0, wx.TOP|wx.LEFT, 0) +======= + self.rescanbtn.Bind(wx.EVT_BUTTON,self.rescanports) + uts.Add(self.rescanbtn,0,wx.TOP|wx.LEFT,0) + +>>>>>>> experimental self.serialport = wx.ComboBox(self.panel, -1, choices = self.scanserial(), style = wx.CB_DROPDOWN, size = (100, 25)) self.serialport.SetToolTip(wx.ToolTip("Select Port Printer is connected to")) self.rescanports() uts.Add(self.serialport) +<<<<<<< HEAD uts.Add(wx.StaticText(self.panel,-1, "@"), 0, wx.RIGHT|wx.ALIGN_CENTER, 0) +======= + + uts.Add(wx.StaticText(self.panel,-1,"@"),0,wx.RIGHT|wx.ALIGN_CENTER,0) +>>>>>>> experimental self.baud = wx.ComboBox(self.panel, -1, choices = ["2400", "9600", "19200", "38400", "57600", "115200", "250000"], style = wx.CB_DROPDOWN, size = (100, 25)) @@ -567,6 +606,7 @@ class PronterWindow(wx.Frame, pronsole.pronsole): except: pass uts.Add(self.baud) +<<<<<<< HEAD self.connectbtn = wx.Button(self.panel,-1, _("Connect"), size = buttonSize) uts.Add(self.connectbtn) self.connectbtn.SetToolTip(wx.ToolTip("Connect to the printer")) @@ -582,6 +622,25 @@ class PronterWindow(wx.Frame, pronsole.pronsole): #uts.Add((15,-1), flag = wx.EXPAND) #uts.Add(self.minibtn, 0, wx.ALIGN_CENTER) +======= + + self.connectbtn=wx.Button(self.panel,-1,_("Connect"), size=buttonSize) + self.connectbtn.SetToolTip(wx.ToolTip("Connect to the printer")) + self.connectbtn.Bind(wx.EVT_BUTTON,self.connect) + uts.Add(self.connectbtn) + + self.resetbtn=wx.Button(self.panel,-1,_("Reset"),style=wx.BU_EXACTFIT,size=(-1,buttonSize[1])) + self.resetbtn.Bind(wx.EVT_BUTTON,self.reset) + self.resetbtn.SetToolTip(wx.ToolTip("Reset the printer")) + uts.Add(self.resetbtn) + + #uts.Add((25,-1)) + + #uts.Add((15,-1),flag=wx.EXPAND) + #self.minibtn=wx.Button(self.panel,-1,_("Mini mode"),style=wx.BU_EXACTFIT) + #self.minibtn.Bind(wx.EVT_BUTTON,self.toggleview) + #uts.Add(self.minibtn,0,wx.ALIGN_CENTER) +>>>>>>> experimental #SECOND ROW ubs = self.upperbottomsizer = uts#wx.BoxSizer(wx.HORIZONTAL) @@ -791,8 +850,13 @@ class PronterWindow(wx.Frame, pronsole.pronsole): self.gviz.showall = 1 try: raise "" +<<<<<<< HEAD import printrun.stlview self.gwindow = printrun.stlview.GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (600, 600)) +======= + from printrun import stlview + self.gwindow=stlview.GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size=(600,600)) +>>>>>>> experimental except: self.gwindow = gviz.window([], build_dimensions = self.build_dimensions_list, @@ -1323,6 +1387,7 @@ class PronterWindow(wx.Frame, pronsole.pronsole): else: wx.CallAfter(self.graph.StopPlotting) +<<<<<<< HEAD def sendline(self, e): command = self.commandbox.GetValue() if not len(command): @@ -1330,6 +1395,25 @@ class PronterWindow(wx.Frame, pronsole.pronsole): wx.CallAfter(self.logbox.AppendText, ">>>"+command+"\n") if self.webInterface: self.webInterface.AppendLog(">>>"+command+"\n") +======= + def addtexttolog(self,text): + try: + self.logbox.AppendText(text) + except: + print "attempted to write invalid text to console" + pass + if self.webInterface: + self.webInterface.AppendLog(text) + + def setloud(self,e): + self.p.loud=e.IsChecked() + + def sendline(self,e): + command=self.commandbox.GetValue() + if not len(command): + return + wx.CallAfter(self.addtexttolog,">>>"+command+"\n"); +>>>>>>> experimental self.onecmd(str(command)) self.commandbox.SetSelection(0, len(command)) self.commandbox.history+=[command] @@ -1417,10 +1501,14 @@ class PronterWindow(wx.Frame, pronsole.pronsole): traceback.print_exc() tstring = l.rstrip() #print tstring - if (tstring!="ok") and (tstring!="wait") and ("ok T:" not in tstring): + if (tstring!="ok") and (tstring!="wait") and ("ok T:" not in tstring) and (not self.p.loud): # print "*"+tstring+"*" # print "[" + time.strftime('%H:%M:%S',time.localtime(time.time())) + "] " + tstring +<<<<<<< HEAD wx.CallAfter(self.logbox.AppendText, tstring+"\n") +======= + wx.CallAfter(self.addtexttolog,tstring+"\n"); +>>>>>>> experimental for i in self.recvlisteners: i(l) @@ -1534,6 +1622,7 @@ class PronterWindow(wx.Frame, pronsole.pronsole): def skein(self, filename): wx.CallAfter(self.loadbtn.SetLabel, _("Cancel")) print _("Slicing ") + filename +<<<<<<< HEAD self.cout = StringIO.StringIO() self.filename = filename self.stopsf = 0 @@ -1542,6 +1631,22 @@ class PronterWindow(wx.Frame, pronsole.pronsole): threading.Thread(target = self.skein_monitor).start() def loadfile(self, event, filename = None): +======= + self.cout=StringIO.StringIO() + self.filename=filename + self.stopsf=0 + self.skeining=1 + thread(target=self.skein_func).start() + thread(target=self.skein_monitor).start() + + def do_load(self,l): + if hasattr(self, 'skeining'): + self.loadfile(None, l) + else: + self._do_load(l) + + def loadfile(self,event,filename=None): +>>>>>>> experimental if self.skeining and self.skeinp is not None: self.skeinp.terminate() return diff --git a/testfiles/PCB-milling-and-(comment).gcode b/testfiles/PCB-milling-and-(comment).gcode new file mode 100644 index 0000000..2381f86 --- /dev/null +++ b/testfiles/PCB-milling-and-(comment).gcode @@ -0,0 +1,64 @@ +(Created by G-code exporter) +(Fri Apr 27 22:20:09 2012) +(Board size: 100.00 x 130.00 mm) +(---------------------------------) +G21 +G90 +G0 X14.392 Y30.94113 Z1. +G4 +M104 S255 +G1 Z-0.2 F60 +G1 X14.05334 Y30.60247 +G1 X12.02134 Y30.60247 +G1 X11.598 Y30.85647 +G1 X11.00534 Y31.5338 +G1 X10.074 Y32.5498 +G1 X9.98933 Y34.41247 +G0 Z1. +(RepRap Gen7 v1.4) +G0 Z1. +(R) +G0 X9.9753 Y6.9723 +G1 Z-0.20 F60 +G1 X11.4761 Y6.9723 F250 +G1 X11.8514 Y6.5971 +G1 X11.8514 Y5.8467 +G1 X11.4761 Y5.4715 +G1 X10.3505 Y5.4715 +G1 X11.8514 Y3.9456 +G0 Z1. +G0 X10.3505 Y6.9723 +G1 Z-0.20 F60 +G1 X10.3505 Y3.9456 F250 +G0 Z1. +(e) +G0 X12.7519 Y4.696 +G1 Z-0.20 F60 +G1 X14.2778 Y4.696 F250 +G1 X14.2778 Y5.0962 +G1 X13.9025 Y5.4715 +G1 X13.1271 Y5.4715 +G1 X12.7519 Y5.0962 +G1 X12.7519 Y4.3208 +G1 X13.1271 Y3.9456 +G1 X14.2778 Y3.9456 +G0 Z1. +(p) +G0 X15.5535 Y2.8199 +G1 Z-0.20 F60 +G1 X15.5535 Y5.0962 F250 +G1 X15.1783 Y5.4715 +G1 X15.5535 Y5.0962 +G1 X15.9287 Y5.4715 +G1 X16.6792 Y5.4715 +G1 X17.0544 Y5.0962 +G1 X17.0544 Y4.3208 +G1 X16.6792 Y3.9456 +G1 X15.9287 Y3.9456 +G1 X15.5535 Y4.3208 +G0 Z1. +G4 +M104 S0 +(tool change position) +G1 X2. Y2. Z40. F400 +M2