Commit cc59be4e authored by Elias's avatar Elias

Fetched from Kliment and mergen in ethernet

parents 27cbef59 7ee7e1d9
...@@ -3,3 +3,4 @@ ...@@ -3,3 +3,4 @@
*.swp *.swp
*.bak *.bak
uploads uploads
.DS_Store
# This file is part of the Printrun suite.
#
# Copyright 2013 Francesco Santini francesco.santini@gmail.com
#
# Printrun is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Printrun is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
#
# This code is imported from RepetierHost - Original copyright and license:
# Copyright 2011 repetier repetierdev@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
class GCodeAnalyzer():
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
self.e = 0
self.emax = 0
self.f = 1000
self.lastX = 0
self.lastY = 0
self.lastZ = 0
self.lastE = 0
self.xOffset = 0
self.yOffset = 0
self.zOffset = 0
self.eOffset = 0
self.lastZPrint = 0
self.layerZ = 0
self.relative = False
self.eRelative = False
self.homeX = 0
self.homeY = 0
self.homeZ = 0
self.maxX = 150
self.maxY = 150
self.maxZ = 150
self.minX = 0
self.minY = 0
self.minZ = 0
self.hasHomeX = False
self.hasHomeY = False
self.hasHomeZ = False
# find a code in a gstring line
def findCode(self, gcode, codeStr):
pattern = re.compile(codeStr + "\\s*(-?[\d.]*)",re.I)
m=re.search(pattern, gcode)
if m == None:
return None
else:
return m.group(1)
def Analyze(self, gcode):
gcode = gcode[:gcode.find(";")].lstrip() # remove comments
if gcode.startswith("@"): return # code is a host command
code_g = self.findCode(gcode, "G")
code_m = self.findCode(gcode, "M")
# we have a g_code
if code_g != None:
code_g = int(code_g)
#get movement codes
if code_g == 0 or code_g == 1 or code_g == 2 or code_g == 3:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
eChanged = False;
code_f = self.findCode(gcode, "F")
if code_f != None:
self.f=float(code_f)
code_x = self.findCode(gcode, "X")
code_y = self.findCode(gcode, "Y")
code_z = self.findCode(gcode, "Z")
code_e = self.findCode(gcode, "E")
if self.relative:
if code_x != None: self.x += float(code_x)
if code_y != None: self.y += float(code_y)
if code_z != None: self.z += float(code_z)
if code_e != None:
e = float(code_e)
if e != 0:
eChanged = True
self.e += e
else:
#absolute coordinates
if code_x != None: self.x = self.xOffset + float(code_x)
if code_y != None: self.y = self.yOffset + float(code_y)
if code_z != None: self.z = self.zOffset + float(code_z)
if code_e != None:
e = float(code_e)
if self.eRelative:
if e != 0:
eChanged = True
self.e += e
else:
# e is absolute. Is it changed?
if self.e != self.eOffset + e:
eChanged = True
self.e = self.eOffset + e
#limit checking
if self.x < self.minX: self.x = self.minX
if self.y < self.minY: self.y = self.minY
if self.z < self.minZ: self.z = self.minZ
if self.x > self.maxX: self.x = self.maxX
if self.y > self.maxY: self.y = self.maxY
if self.z > self.maxZ: self.z = self.maxZ
#Repetier has a bunch of limit-checking code here and time calculations: we are leaving them for now
elif code_g == 28 or code_g == 161:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
code_x = self.findCode(gcode, "X")
code_y = self.findCode(gcode, "Y")
code_z = self.findCode(gcode, "Z")
code_e = self.findCode(gcode, "E")
homeAll = False
if code_x == None and code_y == None and code_z == None: homeAll = True
if code_x != None or homeAll:
self.hasHomeX = True
self.xOffset = 0
self.x = self.homeX
if code_y != None or homeAll:
self.hasHomeY = True
self.yOffset = 0
self.y = self.homeY
if code_z != None or homeAll:
self.hasHomeZ = True
self.zOffset = 0
self.z = self.homeZ
if code_e != None:
self.eOffset = 0
self.e = 0
elif code_g == 162:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
code_x = self.findCode(gcode, "X")
code_y = self.findCode(gcode, "Y")
code_z = self.findCode(gcode, "Z")
homeAll = False
if code_x == None and code_y == None and code_z == None: homeAll = True
if code_x != None or homeAll:
self.hasHomeX = True
self.xOffset = 0
self.x = self.maxX
if code_y != None or homeAll:
self.hasHomeY = True
self.yOffset = 0
self.y = self.maxY
if code_z != None or homeAll:
self.hasHomeZ = True
self.zOffset = 0
self.z = self.maxZ
elif code_g == 90: self.relative = False
elif code_g == 91: self.relative = True
elif code_g == 92:
code_x = self.findCode(gcode, "X")
code_y = self.findCode(gcode, "Y")
code_z = self.findCode(gcode, "Z")
code_e = self.findCode(gcode, "E")
if code_x != None:
self.xOffset = self.x - float(code_x)
self.x = self.xOffset
if code_y != None:
self.yOffset = self.y - float(code_y)
self.y = self.yOffset
if code_z != None:
self.zOffset = self.z - float(code_z)
self.z = self.zOffset
if code_e != None:
self.xOffset = self.e - float(code_e)
self.e = self.eOffset
#End code_g != None
if code_m != None:
code_m = int(code_m)
if code_m == 82: self.eRelative = False
elif code_m == 83: self.eRelative = True
def print_status(self):
attrs = vars(self)
print '\n'.join("%s: %s" % item for item in attrs.items())
\ No newline at end of file
...@@ -25,9 +25,19 @@ You can run Printrun directly from source, as there are no packages available ye ...@@ -25,9 +25,19 @@ You can run Printrun directly from source, as there are no packages available ye
`sudo apt-get install python-serial python-wxgtk2.8 python-pyglet` `sudo apt-get install python-serial python-wxgtk2.8 python-pyglet`
### Fedora 15 and newer ### Fedora 17 and newer
You can run Printrun directly from source, as there are no packages available yet. Fetch and install the dependencies using You can install Printrun from official packages. Install the whole package using
`sudo yum install printrun`
Or get only apps you need by
`sudo yum install pronsole` or `pronterface` or `plater`
Adding `--enablerepo updates-testing` option to `yum` might give you newer packages (but also not very tested).
You can also run Printrun directly from source, if the packages are too old for you anyway, or you have Fedora 15 or 16. Fetch and install the dependencies using
`sudo yum install pyserial wxpython pyglet` `sudo yum install pyserial wxpython pyglet`
...@@ -101,9 +111,10 @@ Run Printrun for source if you want to test out the latest features. ...@@ -101,9 +111,10 @@ Run Printrun for source if you want to test out the latest features.
To use pronterface, you need: To use pronterface, you need:
* python (ideally 2.6.x or 2.7.x), * python (ideally 2.6.x or 2.7.x),
* pyserial (or python-serial on ubuntu/debian), * pyserial (or python-serial on ubuntu/debian)
* pyglet * pyglet
* pyreadline (not needed on Linux) and * pyreadline (not needed on Linux) and
* argparse (installed by default with python >= 2.7)
* wxPython * 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) 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)
......
#!/usr/bin/env python
# This file is copied from GCoder.
#
# GCoder is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GCoder is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
import sys
import re
import math
def deltalen(a,b):
d = object()
d.x = b.x - a.x
d.y = b.y - a.y
d.z = b.z - a.z
return math.sqrt((d.x*d.x)+(d.y*d.y)+(d.z*d.z))
class Line(object):
def __init__(self,l):
self._x = None
self._y = None
self._z = None
self.e = None
self.f = 0
self.regex = re.compile("[-]?\d+[.]?\d*")
self.raw = l.upper().lstrip()
self.imperial = False
self.relative = False
self.relative_e = False
if ";" in self.raw:
self.raw = self.raw.split(";")[0]
self._parse_coordinates()
def _to_mm(self,v):
if v and self.imperial:
return v*25.4
return v
def _getx(self):
return self._to_mm(self._x)
def _setx(self,v):
self._x = v
def _gety(self):
return self._to_mm(self._y)
def _sety(self,v):
self._y = v
def _getz(self):
return self._to_mm(self._z)
def _setz(self,v):
self._z = v
def _gete(self):
return self._to_mm(self._e)
def _sete(self,v):
self._e = v
x = property(_getx,_setx)
y = property(_gety,_sety)
z = property(_getz,_setz)
e = property(_gete,_sete)
def command(self):
try:
return self.raw.split(" ")[0]
except:
return ""
def _get_float(self,which):
try:
return float(self.regex.findall(self.raw.split(which)[1])[0])
except:
return None
def _parse_coordinates(self):
try:
if "X" in self.raw:
self._x = self._get_float("X")
except:
pass
try:
if "Y" in self.raw:
self._y = self._get_float("Y")
except:
pass
try:
if "Z" in self.raw:
self._z = self._get_float("Z")
except:
pass
try:
if "E" in self.raw:
self.e = self._get_float("E")
except:
pass
try:
if "F" in self.raw:
self.f = self._get_float("F")
except:
pass
def is_move(self):
return self.command() and ("G1" in self.raw or "G0" in self.raw)
def __str__(self):
return self.raw
class Layer(object):
def __init__(self,lines):
self.lines = lines
def measure(self):
xmin = 999999999
ymin = 999999999
zmin = 0
xmax = -999999999
ymax = -999999999
zmax = -999999999
relative = False
relative_e = False
current_x = 0
current_y = 0
current_z = 0
for line in self.lines:
if line.command() == "G92":
current_x = line.x or current_x
current_y = line.y or current_y
current_z = line.z or current_z
if line.is_move():
x = line.x
y = line.y
z = line.z
if line.relative:
x = current_x + (x or 0)
y = current_y + (y or 0)
z = current_z + (z or 0)
if x and line.e:
if x < xmin:
xmin = x
if x > xmax:
xmax = x
if y and line.e:
if y < ymin:
ymin = y
if y > ymax:
ymax = y
if z:
if z < zmin:
zmin = z
if z > zmax:
zmax = z
current_x = x or current_x
current_y = y or current_y
current_z = z or current_z
return ( (xmin,xmax),(ymin,ymax),(zmin,zmax) )
class GCode(object):
def __init__(self,data):
self.lines = [Line(i) for i in data]
self._preprocess()
self._create_layers()
def _preprocess(self):
#checks for G20, G21, G90 and G91, sets imperial and relative flags
imperial = False
relative = False
relative_e = False
for line in self.lines:
if line.command() == "G20":
imperial = True
elif line.command() == "G21":
imperial = False
elif line.command() == "G90":
relative = False
relative_e = False
elif line.command() == "G91":
relative = True
relative_e = True
elif line.command() == "M82":
relative_e = False
elif line.command() == "M83":
relative_e = True
elif line.is_move():
line.imperial = imperial
line.relative = relative
line.relative_e = relative_e
def _create_layers(self):
self.layers = []
prev_z = None
cur_z = 0
cur_lines = []
layer_index = []
temp_layers = {}
for line in self.lines:
if line.command() == "G92" and line.z != None:
cur_z = line.z
elif line.is_move():
if line.z != None:
if line.relative:
cur_z += line.z
else:
cur_z = line.z
if cur_z != prev_z:
old_lines = temp_layers.pop(prev_z,[])
old_lines += cur_lines
temp_layers[prev_z] = old_lines
if not prev_z in layer_index:
layer_index.append(prev_z)
cur_lines = []
cur_lines.append(line)
prev_z = cur_z
old_lines = temp_layers.pop(prev_z,[])
old_lines += cur_lines
temp_layers[prev_z] = old_lines
if not prev_z in layer_index:
layer_index.append(prev_z)
layer_index.sort()
for idx in layer_index:
cur_lines = temp_layers[idx]
has_movement = False
for l in cur_lines:
if l.is_move() and l.e != None:
has_movement = True
break
if has_movement:
self.layers.append(Layer(cur_lines))
def num_layers(self):
return len(self.layers)
def measure(self):
xmin = 999999999
ymin = 999999999
zmin = 0
xmax = -999999999
ymax = -999999999
zmax = -999999999
for l in self.layers:
xd,yd,zd = l.measure()
if xd[0] < xmin:
xmin = xd[0]
if xd[1] > xmax:
xmax = xd[1]
if yd[0] < ymin:
ymin = yd[0]
if yd[1] > ymax:
ymax = yd[1]
if zd[0] < zmin:
zmin = zd[0]
if zd[1] > zmax:
zmax = zd[1]
self.xmin = xmin
self.xmax = xmax
self.ymin = ymin
self.ymax = ymax
self.zmin = zmin
self.zmax = zmax
self.width = xmax - xmin
self.depth = ymax - ymin
self.height = zmax - zmin
def filament_length(self):
total_e = 0
cur_e = 0
for line in self.lines:
if line.command() == "G92":
if line.e != None:
total_e += cur_e
cur_e = line.e
elif line.is_move() and line.e:
if line.relative_e:
cur_e += line.e
else:
cur_e = line.e
return total_e
def main():
if len(sys.argv) < 2:
print "usage: %s filename.gcode" % sys.argv[0]
return
# d = [i.replace("\n","") for i in open(sys.argv[1])]
# gcode = GCode(d)
gcode = GCode(list(open(sys.argv[1])))
gcode.measure()
print "Dimensions:"
print "\tX: %0.02f - %0.02f (%0.02f)" % (gcode.xmin,gcode.xmax,gcode.width)
print "\tY: %0.02f - %0.02f (%0.02f)" % (gcode.ymin,gcode.ymax,gcode.depth)
print "\tZ: %0.02f - %0.02f (%0.02f)" % (gcode.zmin,gcode.zmax,gcode.height)
print "Filament used: %0.02fmm" % gcode.filament_length()
print "Number of layers: %d" % gcode.num_layers()
if __name__ == '__main__':
main()
...@@ -5,134 +5,840 @@ ...@@ -5,134 +5,840 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Pronterface jm1\n" "Project-Id-Version: Pronterface jm1\n"
"POT-Creation-Date: 2012-08-08 10:09+CEST\n" "POT-Creation-Date: 2013-05-22 12:58+CEST\n"
"PO-Revision-Date: 2012-03-16 03:50+0100\n" "PO-Revision-Date: 2013-05-22 14:23+0100\n"
"Last-Translator: Guillaume Seguin <guillaume@segu.in>\n" "Last-Translator: Guillaume Seguin <guillaume@segu.in>\n"
"Language-Team: FR <c.laguilhon.debat@gmail.com>\n" "Language-Team: FR <c.laguilhon.debat@gmail.com>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
"X-Generator: Poedit 1.5.5\n"
#: printrun/pronterface_widgets.py:34 #: plater.py:246
msgid "Plate building tool"
msgstr ""
#: plater.py:252
msgid "Clear"
msgstr "Vider"
#: plater.py:253
msgid "Load"
msgstr "Charger"
#: plater.py:255 plater.py:258
msgid "Export"
msgstr "Exporter"
#: plater.py:260
msgid "Done"
msgstr "Terminé"
#: plater.py:262 printrun/pronterface_widgets.py:42 pronterface.py:494
#: pronterface.py:1253
msgid "Cancel"
msgstr "Annuler"
#: plater.py:264
msgid "Snap to Z = 0"
msgstr ""
#: plater.py:265
msgid "Put at 100, 100"
msgstr ""
#: plater.py:266
msgid "Delete"
msgstr "Supprimer"
#: plater.py:267
msgid "Auto"
msgstr ""
#: plater.py:291
msgid "Autoplating"
msgstr ""
#: plater.py:319
msgid "Bed full, sorry sir :("
msgstr "Plateau plein, désolé :("
#: plater.py:329
msgid ""
"Are you sure you want to clear the grid? All unsaved changes will be lost."
msgstr ""
#: plater.py:329
msgid "Clear the grid?"
msgstr "Vider la grille ?"
#: plater.py:371
msgid "Pick file to save to"
msgstr "Veuillez choisir le fichier dans lequel enregistrer"
#: plater.py:372
msgid "STL files (;*.stl;*.STL;)"
msgstr ""
#: plater.py:393
msgid "wrote %s"
msgstr ""
#: plater.py:396
msgid "Pick file to load"
msgstr "Veuillez choisir le fichier à charger"
#: plater.py:397
msgid "STL files (;*.stl;*.STL;)|*.stl|OpenSCAD files (;*.scad;)|*.scad"
msgstr ""
#: printrun/gui.py:21 pronterface.py:26
msgid "WX is not installed. This program requires WX to run."
msgstr ""
"wxWidgets n'est pas installé. Ce programme nécessite la librairie wxWidgets "
"pour fonctionner."
#: printrun/gui.py:81
msgid "XY:"
msgstr "XY:"
#: printrun/gui.py:83
msgid "mm/min Z:"
msgstr "mm/min Z:"
#: printrun/gui.py:88
msgid "Watch"
msgstr "Surveiller"
#: printrun/gui.py:93
msgid "Heat:"
msgstr "Buse:"
#: printrun/gui.py:96
msgid "Switch Hotend Off"
msgstr "Éteindre la tête chauffante"
#: printrun/gui.py:96 printrun/gui.py:115
msgid "Off"
msgstr "Off"
#: printrun/gui.py:108
msgid "Switch Hotend On"
msgstr "Allumer la tête chauffante"
#: printrun/gui.py:108 printrun/gui.py:127
msgid "Set"
msgstr "Régler"
#: printrun/gui.py:112 printrun/gui.py:176
msgid "Bed:"
msgstr "Plateau :"
#: printrun/gui.py:115
msgid "Switch Heated Bed Off"
msgstr "Éteindre le plateau chauffant"
#: printrun/gui.py:156
msgid "mm"
msgstr "mm"
#: printrun/gui.py:165
msgid ""
"mm/\n"
"min"
msgstr ""
"mm/\n"
"min"
#: printrun/gui.py:174
msgid "Heater:"
msgstr "Buse:"
#: printrun/gui.py:248
msgid "Send"
msgstr "Envoyer"
#: printrun/gui.py:248
msgid "Send Command to Printer"
msgstr "Envoyer une commande à l'imprimante"
#: printrun/gui.py:256
msgid ""
"Communication Settings\n"
"Click to rescan ports"
msgstr ""
#: printrun/gui.py:256
msgid "Port"
msgstr "Port"
#: printrun/gui.py:277
msgid "Connect to the printer"
msgstr "Se connecter à l'imprimante"
#: printrun/gui.py:277 pronterface.py:1475
msgid "Connect"
msgstr "Connecter"
#: printrun/gui.py:279
msgid "Reset"
msgstr "Réinitialiser"
#: printrun/gui.py:279
msgid "Reset the printer"
msgstr "Remettre à zéro l'imprimante"
#: printrun/gui.py:280
msgid "Load a 3D model file"
msgstr "Charger un modèle 3D"
#: printrun/gui.py:280
msgid "Load file"
msgstr "Charger un fichier"
#: printrun/gui.py:281
msgid "Compose"
msgstr "Composer"
#: printrun/gui.py:281
msgid "Simple Plater System"
msgstr ""
#: printrun/gui.py:282
msgid "SD"
msgstr "SD"
#: printrun/gui.py:282
msgid "SD Card Printing"
msgstr "Impression depuis une carte SD"
#: printrun/gui.py:284
msgid "Start Printing Loaded File"
msgstr "Commencer l'impression du fichier chargé"
#: printrun/gui.py:284 pronterface.py:227 pronterface.py:1243
#: pronterface.py:1301 pronterface.py:1421 pronterface.py:1493
#: pronterface.py:1506
msgid "Print"
msgstr "Imprimer"
#: printrun/gui.py:286
msgid "Pause Current Print"
msgstr "Mettre en pause l'impression"
#: printrun/gui.py:286 pronterface.py:1302 pronterface.py:1345
#: pronterface.py:1397 pronterface.py:1420 pronterface.py:1492
#: pronterface.py:1509
msgid "Pause"
msgstr "Pause"
#: printrun/gui.py:287
msgid "Recover"
msgstr "Récupérer"
#: printrun/gui.py:287
msgid "Recover previous Print"
msgstr "Récupérer l'impression précédente"
#: printrun/gui.py:308 pronsole.py:625 pronsole.py:681 pronterface.py:1113
#: pronterface.py:1339 pronterface.py:1451
msgid "Not connected to printer."
msgstr "Imprimante non connectée."
#: printrun/libtatlin/actors.py:272
msgid "Initialized 3D visualization in %.2f seconds"
msgstr "Visualisation 3D initialisée en %.2f secondes."
#: printrun/libtatlin/actors.py:273
msgid "Vertex count: %d"
msgstr "Nombre de sommets: %d"
#: printrun/pronterface_widgets.py:35
msgid "Find" msgid "Find"
msgstr "Trouver" msgstr "Trouver"
#: printrun/pronterface_widgets.py:36 #: printrun/pronterface_widgets.py:37
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
#: printrun/pronterface_widgets.py:41 pronterface.py:477 pronterface.py:1535 #: printrun/pronterface_widgets.py:126
msgid "Cancel"
msgstr "Annuler"
#: printrun/pronterface_widgets.py:125
msgid "Edit settings" msgid "Edit settings"
msgstr "Modifier les paramètres" msgstr "Modifier les paramètres"
#: printrun/pronterface_widgets.py:127 #: printrun/pronterface_widgets.py:128
msgid "Defaults" msgid "Defaults"
msgstr "Paramètres par défaut" msgstr "Paramètres par défaut"
#: printrun/pronterface_widgets.py:156 #: printrun/pronterface_widgets.py:157
msgid "Custom button" msgid "Custom button"
msgstr "Commande personnalisée" msgstr "Commande personnalisée"
#: printrun/pronterface_widgets.py:161 #: printrun/pronterface_widgets.py:162
msgid "Button title" msgid "Button title"
msgstr "Titre du bouton" msgstr "Titre du bouton"
#: printrun/pronterface_widgets.py:164 #: printrun/pronterface_widgets.py:165
msgid "Command" msgid "Command"
msgstr "Commande" msgstr "Commande"
#: printrun/pronterface_widgets.py:173 #: printrun/pronterface_widgets.py:174
msgid "Color" msgid "Color"
msgstr "Couleur" msgstr "Couleur"
#: pronterface.py:26 #: pronsole.py:173
msgid "WX is not installed. This program requires WX to run." msgid "Communications Speed (default: 115200)"
msgstr "Vitesse de communication (default : 115200)"
#: pronsole.py:174
msgid "Heated Build Platform temp for ABS (default: 110 deg C)"
msgstr ""
#: pronsole.py:175
msgid "Heated Build Platform temp for PLA (default: 60 deg C)"
msgstr ""
#: pronsole.py:176
msgid "Feedrate for Control Panel Moves in Extrusions (default: 300mm/min)"
msgstr ""
#: pronsole.py:177
msgid "Port used to communicate with printer"
msgstr "Port utilisé pour communiquer avec l'imprimante"
#: pronsole.py:178
msgid ""
"Slice command\n"
" default:\n"
" python skeinforge/skeinforge_application/skeinforge_utilities/"
"skeinforge_craft.py $s)"
msgstr ""
#: pronsole.py:179
msgid ""
"Slice settings command\n"
" default:\n"
" python skeinforge/skeinforge_application/skeinforge.py"
msgstr ""
#: pronsole.py:180
msgid "Extruder temp for ABS (default: 230 deg C)"
msgstr ""
#: pronsole.py:181
msgid "Extruder temp for PLA (default: 185 deg C)"
msgstr ""
#: pronsole.py:182
msgid "Feedrate for Control Panel Moves in X and Y (default: 3000mm/min)"
msgstr ""
#: pronsole.py:183
msgid "Feedrate for Control Panel Moves in Z (default: 200mm/min)"
msgstr ""
#: pronsole.py:184
msgid "Executable to run when the print is finished"
msgstr ""
#: pronsole.py:622
msgid "Please enter target name in 8.3 format."
msgstr "Veuillez entrer un nom au format 8.3."
#: pronsole.py:628
msgid "Uploading as %s"
msgstr "Envoi en tant que %s"
#: pronsole.py:629
msgid "Uploading %s"
msgstr "Envoi de %s"
#: pronsole.py:631
msgid "Press Ctrl-C to interrupt upload."
msgstr "Appuyer sur Ctrl-C pour interrompre l'envoi."
#: pronsole.py:634
msgid "Progress: "
msgstr "Progrès :"
#: pronsole.py:646
msgid "Upload completed. %s should now be on the card."
msgstr "Envoi terminé. %s devrait maintenant se trouver sur la carte."
#: pronsole.py:649
msgid "...interrupted!"
msgstr "...interrompu !"
#: pronsole.py:655
msgid "A partial file named %s may have been written to the sd card."
msgstr "Un fichier incomplet nommé %s peut avoir été écrit sur la carte SD."
#: pronsole.py:672
msgid ""
"Send a loaded gcode file to the printer. Load a file with the load command "
"first."
msgstr ""
#: pronsole.py:674
msgid "Send a loaded gcode file to the printer. You have %s loaded right now."
msgstr ""
#: pronsole.py:678 pronterface.py:1336
msgid "No file loaded. Please use load first."
msgstr "Aucun fichier chargé. Veuillez charger un fichier avant."
#: pronsole.py:683
msgid "Printing %s"
msgstr "Impression de %s"
#: pronsole.py:684
msgid "You can monitor the print with the monitor command."
msgstr ""
#: pronsole.py:692
msgid "Not printing, cannot pause."
msgstr "Pas d'impression en cours, impossible de mettre en pause."
#: pronsole.py:698
msgid "Pauses a running print"
msgstr "Met en pause l'impression en cours"
#: pronsole.py:702
msgid "Not paused, unable to resume. Start a print first."
msgstr ""
"Pas d'impression en pause, impossible de continuer. Veuillez d'abord lancer "
"une impression."
#: pronsole.py:712
msgid "Resumes a paused print."
msgstr "Continue une impression en pause."
#: pronsole.py:727
msgid "Files on SD card:"
msgstr "Fichiers sur la carte SD :"
#: pronsole.py:741 pronsole.py:783 pronsole.py:1088
msgid "Printer is not online. Please connect to it first."
msgstr "Imprimante déconnectée. Veuillez vous y connecter."
#: pronsole.py:746
msgid "Lists files on the SD card"
msgstr "Liste les fichiers sur la carte SD"
#: pronsole.py:750 pronterface.py:1163
msgid "Opening file failed."
msgstr "L'ouverture du fichier a échoué"
#: pronsole.py:756 pronterface.py:1169
msgid "Starting print"
msgstr "Début de l'impression..."
#: pronsole.py:779
msgid "Resets the printer."
msgstr "Remet à zéro l'imprimante."
#: pronsole.py:789
msgid "File is not present on card. Please upload it first."
msgstr "Le fichier n'est pas sur la carte, veuillez d'abord l'y envoyer."
#: pronsole.py:793
msgid "Printing file: %s from SD card."
msgstr "Impression du fichier %s depuis la carte SD"
#: pronsole.py:794
msgid "Requesting SD print..."
msgstr "Demande de l'impression depuis la carte SD..."
#: pronsole.py:798
msgid "Print a file from the SD card. Tab completes with available file names."
msgstr ""
#: pronsole.py:799
msgid "sdprint filename.g"
msgstr ""
#: pronsole.py:834 pronsole.py:842 pronsole.py:892 pronsole.py:918
#: pronterface.py:354 pronterface.py:374 pronterface.py:436
msgid "Printer is not online."
msgstr "Imprimante déconnectée."
#: pronsole.py:875
msgid "Read the extruder and bed temperature."
msgstr "Lire la température de l'extrudeuse et du plateau."
#: pronsole.py:885
msgid ""
"%s is a high temperature to set your extruder to. Are you sure you want to "
"do that?"
msgstr ""
#: pronsole.py:890
msgid "Setting hotend temperature to %s degrees Celsius."
msgstr "Réglage de la température de la buse à %s degrés Celsius."
#: pronsole.py:894 pronterface.py:356
msgid ""
"You cannot set negative temperatures. To turn the hotend off entirely, set "
"its temperature to 0."
msgstr ""
"Vous ne pouvez pas régler une température négative. Pour éteindre la buse, "
"réglez sa température à 0°C."
#: pronsole.py:896 pronsole.py:922
msgid "You must enter a temperature."
msgstr "Vous devez saisir une température."
#: pronsole.py:899
msgid "Sets the hotend temperature to the value entered."
msgstr "Règle de la température de la buse à la valeur saisie."
#: pronsole.py:900 pronsole.py:926
msgid "Enter either a temperature in celsius or one of the following keywords"
msgstr ""
#: pronsole.py:916
msgid "Setting bed temperature to %s degrees Celsius."
msgstr "Réglage de la température du plateau à %s degrés Celsius."
#: pronsole.py:920 pronterface.py:376
msgid ""
"You cannot set negative temperatures. To turn the bed off entirely, set its "
"temperature to 0."
msgstr ""
"Vous ne pouvez pas régler une température négative. Pour désactiver votre "
"plateau chauffant, réglez sa température à 0°C."
#: pronsole.py:925
msgid "Sets the bed temperature to the value entered."
msgstr "Règle de la température du plateau à la valeur saisie."
#: pronsole.py:935
msgid "No move specified."
msgstr "Aucun déplacement spécifié."
#: pronsole.py:938 pronsole.py:1041 pronsole.py:1182
msgid ""
"Printer is currently printing. Please pause the print before you issue "
"manual commands."
msgstr ""
#: pronsole.py:941 pronsole.py:1179
msgid "Printer is not online. Unable to move."
msgstr "Imprimante déconnectée. Impossible de bouger."
#: pronsole.py:957
msgid "Unknown axis."
msgstr "Axe inconnu."
#: pronsole.py:963
msgid "Invalid distance"
msgstr "Distance invalide"
#: pronsole.py:974
msgid "Move an axis. Specify the name of the axis and the amount. "
msgstr ""
#: pronsole.py:975
msgid ""
"move X 10 will move the X axis forward by 10mm at %s mm/min (default XY "
"speed)"
msgstr ""
#: pronsole.py:976
msgid "move Y 10 5000 will move the Y axis forward by 10mm at 5000mm/min"
msgstr ""
#: pronsole.py:977
msgid ""
"move Z -1 will move the Z axis down by 1mm at %s mm/min (default Z speed)"
msgstr ""
#: pronsole.py:978
msgid "Common amounts are in the tabcomplete list."
msgstr "" msgstr ""
"wxWidgets n'est pas installé. Ce programme nécessite la librairie wxWidgets "
"pour fonctionner."
#: pronterface.py:93 #: pronsole.py:1008 pronsole.py:1048
msgid "Invalid length given."
msgstr "La longueur donnée est invalide."
#: pronsole.py:1013 pronsole.py:1053
msgid "Invalid speed given."
msgstr "La vitesse donnée est invalide."
#: pronsole.py:1018
msgid "Extruding %fmm of filament."
msgstr ""
#: pronsole.py:1020
msgid "Reversing %fmm of filament."
msgstr ""
#: pronsole.py:1022
msgid "Length is 0, not doing anything."
msgstr "Longueur nulle, rien à faire."
#: pronsole.py:1028
msgid ""
"Extrudes a length of filament, 5mm by default, or the number of mm given as "
"a parameter"
msgstr ""
#: pronsole.py:1029
msgid "extrude - extrudes 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1030
msgid "extrude 20 - extrudes 20mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1031
msgid "extrude -5 - REVERSES 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1032
msgid "extrude 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"
msgstr ""
#: pronsole.py:1038
msgid "Printer is not online. Unable to reverse."
msgstr "Imprimante déconnectée. Impossible de retirer."
#: pronsole.py:1057
msgid ""
"Reverses the extruder, 5mm by default, or the number of mm given as a "
"parameter"
msgstr ""
#: pronsole.py:1058
msgid "reverse - reverses 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1059
msgid "reverse 20 - reverses 20mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1060
msgid "reverse 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"
msgstr ""
#: pronsole.py:1061
msgid "reverse -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: pronsole.py:1078
msgid "Exiting program. Goodbye!"
msgstr "Fin du programme. Au revoir !"
#: pronsole.py:1083
msgid "Disconnects from the printer and exits the program."
msgstr "Déconnecte l'imprimante et quitte le programme."
#: pronsole.py:1091
msgid "Printer is not printing. Please print something before monitoring."
msgstr ""
"L'imprimante n'est pas en cours d'impression. Veuillez lancer une impression "
"avant d'activer le suivi."
#: pronsole.py:1093
msgid "Monitoring printer, use ^C to interrupt."
msgstr "Imprimante sous surveillance, utiliser ^C pour interrompre."
#: pronsole.py:1098 pronterface.py:1015
msgid "Invalid period given."
msgstr "La période donnée est invalide"
#: pronsole.py:1099
msgid "Updating values every %f seconds."
msgstr "Mise à jour des valeurs toutes les %f secondes."
#: pronsole.py:1110 pronsole.py:1113
msgid "Print progress: "
msgstr "Progrès de l'impression : "
#: pronsole.py:1122 pronterface.py:1020
msgid "Done monitoring."
msgstr "Surveillance de l'imprimante effectuée."
#: pronsole.py:1126
msgid "Monitor a machine's temperatures and an SD print's status."
msgstr ""
#: pronsole.py:1127
msgid ""
"monitor - Reports temperature and SD print status (if SD printing) every 5 "
"seconds"
msgstr ""
#: pronsole.py:1128
msgid ""
"monitor 2 - Reports temperature and SD print status (if SD printing) every 2 "
"seconds"
msgstr ""
#: pronsole.py:1136
msgid "No file name given."
msgstr "Aucun nom de fichier fourni."
#: pronsole.py:1142
msgid "Skeining file: %s"
msgstr ""
#: pronsole.py:1144 pronterface.py:1287
msgid "File not found!"
msgstr "Fichier non trouvé"
#: pronsole.py:1149
msgid "Entering slicer settings: %s"
msgstr ""
#: pronsole.py:1153
msgid "Slicing: "
msgstr "Slicing :"
#: pronsole.py:1156
msgid "Loading sliced file."
msgstr "Chargement du fichier slicé."
#: pronsole.py:1159
msgid "Skeinforge execution failed: %s"
msgstr ""
#: pronsole.py:1172
msgid ""
"Creates a gcode file from an stl model using the slicer (with tab-completion)"
msgstr ""
#: pronsole.py:1173
msgid "skein filename.stl - create gcode file"
msgstr ""
#: pronsole.py:1174
msgid "skein filename.stl view - create gcode file and view using skeiniso"
msgstr ""
#: pronsole.py:1175
#, fuzzy
msgid "skein set - adjust slicer settings"
msgstr " Régler les paramètres de slicing"
#: pronsole.py:1197
msgid "Homes the printer"
msgstr ""
#: pronsole.py:1198
msgid "home - homes all axes and zeroes the extruder(Using G28 and G92)"
msgstr ""
#: pronsole.py:1199
msgid "home xy - homes x and y axes (Using G28)"
msgstr ""
#: pronsole.py:1200
msgid "home z - homes z axis only (Using G28)"
msgstr ""
#: pronsole.py:1201
msgid "home e - set extruder position to zero (Using G92)"
msgstr ""
#: pronsole.py:1202
msgid "home xyze - homes all axes and zeroes the extruder (Using G28 and G92)"
msgstr ""
#: pronsole.py:1205
msgid ""
"load this file on startup instead of .pronsolerc ; you may chain config "
"files, if so settings auto-save will use the last specified file"
msgstr ""
#: pronsole.py:1206
msgid ""
"executes command after configuration/.pronsolerc is loaded ; macros/settings "
"from these commands are not autosaved"
msgstr ""
#: pronsole.py:1207
msgid "file to load"
msgstr "fichier à charger"
#: pronterface.py:100
msgid "" msgid ""
"Dimensions of Build Platform\n" "Dimensions of Build Platform\n"
" & optional offset of origin\n" " & optional offset of origin\n"
" & optional switch position\n"
"\n" "\n"
"Examples:\n" "Examples:\n"
" XXXxYYY\n" " XXXxYYY\n"
" XXX,YYY,ZZZ\n" " XXX,YYY,ZZZ\n"
" XXXxYYYxZZZ+OffX+OffY+OffZ" " XXXxYYYxZZZ+OffX+OffY+OffZ\n"
"XXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ"
msgstr "" msgstr ""
#: pronterface.py:94 #: pronterface.py:101
msgid "Last Set Temperature for the Heated Print Bed" msgid "Last Set Temperature for the Heated Print Bed"
msgstr "Dernière température du plateau chauffant définie" msgstr "Dernière température du plateau chauffant définie"
#: pronterface.py:95 #: pronterface.py:102
msgid "Folder of last opened file" msgid "Folder of last opened file"
msgstr "Dossier du dernier fichier ouvert" msgstr "Dossier du dernier fichier ouvert"
#: pronterface.py:96 #: pronterface.py:103
msgid "Last Temperature of the Hot End" msgid "Last Temperature of the Hot End"
msgstr "Dernière température de la buse définie" msgstr "Dernière température de la buse définie"
#: pronterface.py:97 #: pronterface.py:104
msgid "Width of Extrusion in Preview (default: 0.5)" msgid "Width of Extrusion in Preview (default: 0.5)"
msgstr "Largeur de l'extrusion dans la prévisualisation (défaut : 0.5)" msgstr "Largeur de l'extrusion dans la prévisualisation (défaut : 0.5)"
#: pronterface.py:98 #: pronterface.py:105
msgid "Fine Grid Spacing (default: 10)" msgid "Fine Grid Spacing (default: 10)"
msgstr "Espacement fin de la grille (défaut : 10)" msgstr "Espacement fin de la grille (défaut : 10)"
#: pronterface.py:99 #: pronterface.py:106
msgid "Coarse Grid Spacing (default: 50)" msgid "Coarse Grid Spacing (default: 50)"
msgstr "Espacement large de la grille (défaut : 50)" msgstr "Espacement large de la grille (défaut : 50)"
#: pronterface.py:100 #: pronterface.py:107
msgid "Pronterface background color (default: #FFFFFF)" msgid "Pronterface background color (default: #FFFFFF)"
msgstr "Couleur de fond de la Pronterface (défaut : #FFFFFF)" msgstr "Couleur de fond de la Pronterface (défaut : #FFFFFF)"
#: pronterface.py:103 #: pronterface.py:110
msgid "Printer Interface" msgid "Printer Interface"
msgstr "Interface de l'imprimante" msgstr "Interface de l'imprimante"
#: pronterface.py:122 #: pronterface.py:127
msgid "Motors off" msgid "Motors off"
msgstr "Arrêter les moteurs" msgstr "Arrêter les moteurs"
#: pronterface.py:122 #: pronterface.py:127
msgid "Switch all motors off" msgid "Switch all motors off"
msgstr "Arrêter tous les moteurs" msgstr "Arrêter tous les moteurs"
#: pronterface.py:123 #: pronterface.py:128
msgid "Check current hotend temperature" msgid "Check current hotend temperature"
msgstr "Vérifier la température actuelle de la buse" msgstr "Vérifier la température actuelle de la buse"
#: pronterface.py:123 #: pronterface.py:128
msgid "Check temp" msgid "Check temp"
msgstr "Lire les températures" msgstr "Lire les températures"
#: pronterface.py:124 #: pronterface.py:129
msgid "Advance extruder by set length" msgid "Advance extruder by set length"
msgstr "Extruder sur la longueur donnée" msgstr "Extruder sur la longueur donnée"
#: pronterface.py:124 #: pronterface.py:129
msgid "Extrude" msgid "Extrude"
msgstr "Extruder" msgstr "Extruder"
#: pronterface.py:125 #: pronterface.py:130
msgid "Reverse" msgid "Reverse"
msgstr "Inverser" msgstr "Inverser"
#: pronterface.py:125 #: pronterface.py:130
msgid "Reverse extruder by set length" msgid "Reverse extruder by set length"
msgstr "Inverser l'extrudeur sur la longueur donnée" msgstr "Inverser l'extrudeur sur la longueur donnée"
#: pronterface.py:143 #: pronterface.py:173
msgid "" msgid ""
"# I moved all your custom buttons into .pronsolerc.\n" "# I moved all your custom buttons into .pronsolerc.\n"
"# Please don't add them here any more.\n" "# Please don't add them here any more.\n"
...@@ -143,7 +849,7 @@ msgstr "" ...@@ -143,7 +849,7 @@ msgstr ""
"# Veuillez ne plus en ajouter ici.\n" "# Veuillez ne plus en ajouter ici.\n"
"# Une sauvegarde de vos anciens boutons est dans le fichier custombtn.old\n" "# Une sauvegarde de vos anciens boutons est dans le fichier custombtn.old\n"
#: pronterface.py:148 #: pronterface.py:178
msgid "" msgid ""
"Note!!! You have specified custom buttons in both custombtn.txt and ." "Note!!! You have specified custom buttons in both custombtn.txt and ."
"pronsolerc" "pronsolerc"
...@@ -151,389 +857,283 @@ msgstr "" ...@@ -151,389 +857,283 @@ msgstr ""
"Remarque! Vous avez spécifié des boutons personnalisés dans custombtn.txt et " "Remarque! Vous avez spécifié des boutons personnalisés dans custombtn.txt et "
"aussi dans .pronsolerc" "aussi dans .pronsolerc"
#: pronterface.py:149 #: pronterface.py:179
msgid "" msgid ""
"Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt" "Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt"
msgstr "" msgstr ""
"custombtn.txt ignoré. Retirez tous les boutons en cours pour revenir à " "custombtn.txt ignoré. Retirez tous les boutons en cours pour revenir à "
"custombtn.txt" "custombtn.txt"
#: pronterface.py:181 #: pronterface.py:209
msgid "Failed to start web interface" msgid ""
msgstr "Échec du lancement de l'interface web" "display graphical temperature gauges in addition to the temperatures graph"
msgstr "afficher des jauges de température graphiques en plus du graphe"
#: pronterface.py:185 #: pronterface.py:210
msgid "CherryPy is not installed. Web Interface Disabled." msgid "automatically try to connect to printer on startup"
msgstr "CherryPy n'est pas installé. L'interface web est désactivée." msgstr "tenter de se connecter automatiquement à l'imprimante au démarrage"
#: pronterface.py:197 pronterface.py:603 pronterface.py:1525 #: pronterface.py:219
#: pronterface.py:1578 pronterface.py:1705 pronterface.py:1765 msgid "Print Started at: %s"
#: pronterface.py:1778 msgstr "Impression lancée à : %s"
msgid "Print"
msgstr "Imprimer" #: pronterface.py:224
msgid "Print ended at: %(end_time)s and took %(duration)s"
msgstr "Impression terminée à : %(end_time)s après avoir duré %(duration)s"
#: pronterface.py:207 #: pronterface.py:238
msgid "Printer is now online." msgid "Printer is now online."
msgstr "Imprimante connectée." msgstr "Imprimante connectée."
#: pronterface.py:208 #: pronterface.py:239
msgid "Disconnect" msgid "Disconnect"
msgstr "Déconnecter" msgstr "Déconnecter"
#: pronterface.py:331 #: pronterface.py:351
msgid "Setting hotend temperature to %f degrees Celsius." msgid "Setting hotend temperature to %f degrees Celsius."
msgstr "Réglage de la température de la buse à %f degrés Celsius." msgstr "Réglage de la température de la buse à %f degrés Celsius."
#: pronterface.py:334 pronterface.py:356 pronterface.py:428 #: pronterface.py:358 pronterface.py:378
msgid "Printer is not online."
msgstr "Imprimante déconnectée."
#: pronterface.py:336
msgid ""
"You cannot set negative temperatures. To turn the hotend off entirely, set "
"its temperature to 0."
msgstr ""
"Vous ne pouvez pas régler une température négative. Pour éteindre la buse, "
"réglez sa température à 0°C."
#: pronterface.py:338 pronterface.py:364
msgid "You must enter a temperature. (%s)" msgid "You must enter a temperature. (%s)"
msgstr "Vous devez saisir une température. (%s)" msgstr "Vous devez saisir une température. (%s)"
#: pronterface.py:353 #: pronterface.py:371
msgid "Setting bed temperature to %f degrees Celsius." msgid "Setting bed temperature to %f degrees Celsius."
msgstr "Réglage de la température du plateau à %f degrés Celsius." msgstr "Réglage de la température du plateau à %f degrés Celsius."
#: pronterface.py:360 #: pronterface.py:393
msgid ""
"You cannot set negative temperatures. To turn the bed off entirely, set its "
"temperature to 0."
msgstr ""
"Vous ne pouvez pas régler une température négative. Pour désactiver votre "
"plateau chauffant, réglez sa température à 0°C."
#: pronterface.py:381
msgid "Do you want to erase the macro?" msgid "Do you want to erase the macro?"
msgstr "Voulez-vous effacer la macro ?" msgstr "Voulez-vous effacer la macro ?"
#: pronterface.py:385 #: pronterface.py:397
msgid "Cancelled." msgid "Cancelled."
msgstr "Annulé" msgstr "Annulé"
#: pronterface.py:436 #: pronterface.py:442
msgid " Opens file" msgid " Opens file"
msgstr " Ouvrir un fichier" msgstr " Ouvrir un fichier"
#: pronterface.py:436 #: pronterface.py:442
msgid "&Open..." msgid "&Open..."
msgstr "&Ouvrir..." msgstr "&Ouvrir..."
#: pronterface.py:437 #: pronterface.py:443
msgid " Edit open file" msgid " Edit open file"
msgstr " Éditer le fichier ouvert" msgstr " Éditer le fichier ouvert"
#: pronterface.py:437 #: pronterface.py:443
msgid "&Edit..." msgid "&Edit..."
msgstr "&Éditer..." msgstr "&Éditer..."
#: pronterface.py:438 #: pronterface.py:444
msgid " Clear output console" msgid " Clear output console"
msgstr " Effacer le contenu de la console de sortie" msgstr " Effacer le contenu de la console de sortie"
#: pronterface.py:438 #: pronterface.py:444
msgid "Clear console" msgid "Clear console"
msgstr "Effacer la console" msgstr "Effacer la console"
#: pronterface.py:439 #: pronterface.py:445
msgid " Project slices" msgid " Project slices"
msgstr " Projeter les couches" msgstr " Projeter les couches"
#: pronterface.py:439 #: pronterface.py:445
msgid "Projector" msgid "Projector"
msgstr "Projecteur" msgstr "Projecteur"
#: pronterface.py:440 #: pronterface.py:446
msgid " Closes the Window" msgid " Closes the Window"
msgstr " Quitter le programme" msgstr " Quitter le programme"
#: pronterface.py:440 #: pronterface.py:446
msgid "E&xit" msgid "E&xit"
msgstr "&Quitter" msgstr "&Quitter"
#: pronterface.py:441 #: pronterface.py:447
msgid "&File" msgid "&File"
msgstr "&Fichier" msgstr "&Fichier"
#: pronterface.py:446 #: pronterface.py:452
msgid "&Macros" msgid "&Macros"
msgstr "&Macros" msgstr "&Macros"
#: pronterface.py:447 #: pronterface.py:453
msgid "<&New...>" msgid "<&New...>"
msgstr "<&Nouvelle...>" msgstr "<&Nouvelle...>"
#: pronterface.py:448 #: pronterface.py:454
msgid " Options dialog" msgid " Options dialog"
msgstr " Fenêtre des options" msgstr " Fenêtre des options"
#: pronterface.py:448 #: pronterface.py:454
msgid "&Options" msgid "&Options"
msgstr "&Options" msgstr "&Options"
#: pronterface.py:450 #: pronterface.py:456
msgid " Adjust slicing settings" msgid " Adjust slicing settings"
msgstr " Régler les paramètres de slicing" msgstr " Régler les paramètres de slicing"
#: pronterface.py:450 #: pronterface.py:456
msgid "Slicing Settings" msgid "Slicing Settings"
msgstr "Paramètres de slicing" msgstr "Paramètres de slicing"
#: pronterface.py:452 #: pronterface.py:458
msgid "Debug G-code"
msgstr ""
#: pronterface.py:459
msgid "Print all G-code sent to and received from the printer."
msgstr ""
#: pronterface.py:469
msgid "&Settings" msgid "&Settings"
msgstr "&Paramètres" msgstr "&Paramètres"
#: pronterface.py:467 #: pronterface.py:484
msgid "Enter macro name" msgid "Enter macro name"
msgstr "Saisissez le nom de la macro" msgstr "Saisissez le nom de la macro"
#: pronterface.py:470 #: pronterface.py:487
msgid "Macro name:" msgid "Macro name:"
msgstr "Nom :" msgstr "Nom :"
#: pronterface.py:473 #: pronterface.py:490
msgid "Ok" msgid "Ok"
msgstr "Valider" msgstr "Valider"
#: pronterface.py:495 #: pronterface.py:512
msgid "Macro name may contain only ASCII alphanumeric symbols and underscores" msgid "Macro name may contain only ASCII alphanumeric symbols and underscores"
msgstr "" msgstr ""
"Un nom de macro ne peut contenir que des caractères alphanumérique ASCII et " "Un nom de macro ne peut contenir que des caractères alphanumérique ASCII et "
"des underscore (_)" "des underscore (_)"
#: pronterface.py:500 #: pronterface.py:515
msgid "Name '%s' is being used by built-in command" msgid "Name '%s' is being used by built-in command"
msgstr "Le nom '%s' est utilisé par une commande interne" msgstr "Le nom '%s' est utilisé par une commande interne"
#: pronterface.py:548 #: pronterface.py:583
msgid "Port"
msgstr "Port"
#: pronterface.py:570 pronterface.py:1747
msgid "Connect"
msgstr "Connecter"
#: pronterface.py:574
msgid "Reset"
msgstr "Réinitialiser"
#: pronterface.py:589
msgid "Load file"
msgstr "Charger un fichier"
#: pronterface.py:593
msgid "Compose"
msgstr "Composer"
#: pronterface.py:598
msgid "SD"
msgstr "SD"
#: pronterface.py:608 pronterface.py:1579 pronterface.py:1631
#: pronterface.py:1681 pronterface.py:1704 pronterface.py:1764
#: pronterface.py:1781
msgid "Pause"
msgstr "Pause"
#: pronterface.py:612
msgid "Recover"
msgstr "Récupérer"
#: pronterface.py:630
msgid "Send"
msgstr "Envoyer"
#: pronterface.py:671
msgid "XY:"
msgstr "XY:"
#: pronterface.py:673
msgid "mm/min Z:"
msgstr "mm/min Z:"
#: pronterface.py:678
msgid "Watch"
msgstr "Surveiller"
#: pronterface.py:683
msgid "Heat:"
msgstr "Buse:"
#: pronterface.py:686 pronterface.py:709
msgid "Off"
msgstr "Off"
#: pronterface.py:700 pronterface.py:723
msgid "Set"
msgstr "Régler"
#: pronterface.py:706
msgid "Bed:"
msgstr "Plateau :"
#: pronterface.py:756
msgid "mm"
msgstr "mm"
#: pronterface.py:764
msgid ""
"mm/\n"
"min"
msgstr ""
"mm/\n"
"min"
#: pronterface.py:821 pronterface.py:1387 pronterface.py:1625
#: pronterface.py:1723
msgid "Not connected to printer."
msgstr "Imprimante non connectée."
#: pronterface.py:869
msgid "SD Upload" msgid "SD Upload"
msgstr "Copier sur SD" msgstr "Copier sur SD"
#: pronterface.py:873 #: pronterface.py:587
msgid "SD Print" msgid "SD Print"
msgstr "Imprimer depuis SD" msgstr "Imprimer depuis SD"
#: pronterface.py:914 #: pronterface.py:628
msgid "Mini mode" msgid "Mini mode"
msgstr "Mode réduit" msgstr "Mode réduit"
#: pronterface.py:921 #: pronterface.py:635
msgid "Full mode" msgid "Full mode"
msgstr "Mode complet" msgstr "Mode complet"
#: pronterface.py:946 #: pronterface.py:660
msgid "Execute command: " msgid "Execute command: "
msgstr "Exécuter la commande :" msgstr "Exécuter la commande :"
#: pronterface.py:957 #: pronterface.py:671
msgid "click to add new custom button" msgid "click to add new custom button"
msgstr "Ajouter un bouton personnalisé" msgstr "Ajouter un bouton personnalisé"
#: pronterface.py:979 #: pronterface.py:693
msgid "" msgid ""
"Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command" "Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command"
msgstr "" msgstr ""
"Définit des boutons personnalidés. Utilisation : <numero> \"Libelle\" [/c " "Définit des boutons personnalidés. Utilisation : <numero> \"Libelle\" [/c "
"\"couleur\"] commande" "\"couleur\"] commande"
#: pronterface.py:1003 #: pronterface.py:715
msgid "Custom button number should be between 0 and 63" msgid "Custom button number should be between 0 and 63"
msgstr "" msgstr ""
"Les numéros des boutons personnalisés doivent être compris entre 0 et 63." "Les numéros des boutons personnalisés doivent être compris entre 0 et 63."
#: pronterface.py:1096 #: pronterface.py:806
msgid "Edit custom button '%s'" msgid "Edit custom button '%s'"
msgstr "Editer le bouton personnalisé '%s'" msgstr "Editer le bouton personnalisé '%s'"
#: pronterface.py:1098 #: pronterface.py:808
msgid "Move left <<" msgid "Move left <<"
msgstr "Déplacer vers la gauche <<" msgstr "Déplacer vers la gauche <<"
#: pronterface.py:1101 #: pronterface.py:811
msgid "Move right >>" msgid "Move right >>"
msgstr "Déplacer vers la droite >>" msgstr "Déplacer vers la droite >>"
#: pronterface.py:1105 #: pronterface.py:815
msgid "Remove custom button '%s'" msgid "Remove custom button '%s'"
msgstr "Supprimer le bouton personnalisé '%s'" msgstr "Supprimer le bouton personnalisé '%s'"
#: pronterface.py:1108 #: pronterface.py:818
msgid "Add custom button" msgid "Add custom button"
msgstr "Ajouter un bouton personnalisé" msgstr "Ajouter un bouton personnalisé"
#: pronterface.py:1270 #: pronterface.py:987
msgid "event object missing" msgid "event object missing"
msgstr "événement d'objet manquant" msgstr "événement d'objet manquant"
#: pronterface.py:1306 #: pronterface.py:1018
msgid "Invalid period given."
msgstr "La période donnée est invalide"
#: pronterface.py:1311
msgid "Monitoring printer." msgid "Monitoring printer."
msgstr "Imprimante sous surveillance." msgstr "Imprimante sous surveillance."
#: pronterface.py:1315 #: pronterface.py:1033
msgid "Done monitoring." msgid ""
msgstr "Surveillance de l'imprimante effectuée." "Attempted to write invalid text to console, which could be due to an invalid "
"baudrate"
msgstr ""
#: pronterface.py:1355 #: pronterface.py:1084
msgid " SD printing:%04.2f %%" msgid " SD printing:%04.2f %%"
msgstr " Impression SD : %04.2f %%" msgstr " Impression SD : %04.2f %%"
#: pronterface.py:1358 #: pronterface.py:1087
msgid " Printing: %04.2f%% |" msgid " Printing: %04.2f%% |"
msgstr " Impression : %04.2f%% |" msgstr " Impression : %04.2f%% |"
#: pronterface.py:1359 #: pronterface.py:1088
msgid " Line# %d of %d lines |" msgid " Line# %d of %d lines |"
msgstr " Ligne# %d sur %d lignes |" msgstr " Ligne# %d sur %d lignes |"
#: pronterface.py:1364 #: pronterface.py:1093
msgid " Est: %s of %s remaining | " msgid " Est: %s of %s remaining | "
msgstr " ETA: %s restant sur %s | " msgstr " ETA: %s restant sur %s | "
#: pronterface.py:1366 #: pronterface.py:1095
msgid " Z: %0.2f mm" msgid " Z: %0.2f mm"
msgstr " Z: %0.2f mm" msgstr " Z: %0.2f mm"
#: pronterface.py:1439 #: pronterface.py:1190
msgid "Opening file failed."
msgstr "L'ouverture du fichier a échoué"
#: pronterface.py:1445
msgid "Starting print"
msgstr "Début de l'impression..."
#: pronterface.py:1466
msgid "Pick SD file" msgid "Pick SD file"
msgstr "Choisir un fichier sur la carte SD" msgstr "Choisir un fichier sur la carte SD"
#: pronterface.py:1466 #: pronterface.py:1190
msgid "Select the file to print" msgid "Select the file to print"
msgstr "Sélectionnez le fichier à imprimer :" msgstr "Sélectionnez le fichier à imprimer :"
#: pronterface.py:1501 #: pronterface.py:1222
msgid "Failed to execute slicing software: " msgid "Failed to execute slicing software: "
msgstr "Une erreur s'est produite lors du slicing : " msgstr "Une erreur s'est produite lors du slicing : "
#: pronterface.py:1510 #: pronterface.py:1229
msgid "Slicing..." msgid "Slicing..."
msgstr "Slicing..." msgstr "Slicing..."
#: pronterface.py:1523 #: pronterface.py:1241 pronterface.py:1300
msgid ", %d lines" msgid "Loaded %s, %d lines"
msgstr ", %d lignes" msgstr "%s chargé, %d lignes"
#: pronterface.py:1523
msgid "Loaded "
msgstr "Chargé "
#: pronterface.py:1530 #: pronterface.py:1248
msgid "Load File" msgid "Load File"
msgstr "Charger un fichier" msgstr "Charger un fichier"
#: pronterface.py:1536 #: pronterface.py:1254
msgid "Slicing " msgid "Slicing "
msgstr "Slicing " msgstr "Slicing "
#: pronterface.py:1555 #: pronterface.py:1279
msgid "Open file to print" msgid "Open file to print"
msgstr "Ouvrir un fichier à imprimer" msgstr "Ouvrir un fichier à imprimer"
#: pronterface.py:1556 #: pronterface.py:1280
msgid "" msgid ""
"OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*." "OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*."
"gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*" "gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*"
...@@ -541,86 +1141,87 @@ msgstr "" ...@@ -541,86 +1141,87 @@ msgstr ""
"Fichiers OBJ, STL et GCODE (;*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ;)|*." "Fichiers OBJ, STL et GCODE (;*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ;)|*."
"gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|Tous les fichiers (*.*)|*.*" "gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|Tous les fichiers (*.*)|*.*"
#: pronterface.py:1563 #: pronterface.py:1311
msgid "File not found!" msgid "mm of filament used in this print"
msgstr "Fichier non trouvé" msgstr "mm de filament utilisés pour cette impression"
#: pronterface.py:1577
msgid "Loaded %s, %d lines"
msgstr "%s chargé, %d lignes"
#: pronterface.py:1588 #: pronterface.py:1312
msgid "mm of filament used in this print\n" msgid "The print goes:"
msgstr "mm de filament utilisés pour cette impression\n" msgstr "L'impression va :"
#: pronterface.py:1589 pronterface.py:1591 #: pronterface.py:1313
msgid "" msgid "- from %.2f mm to %.2f mm in X and is %.2f mm wide"
"the print goes from %f mm to %f mm in X\n" msgstr "- de %.02f mm à %.02f mm en X et mesure %.02f mm de large"
"and is %f mm wide\n"
msgstr ""
"L'impression va de %f mm à %f mm en X\n"
"et mesure %f mm de large\n"
#: pronterface.py:1592 #: pronterface.py:1314
msgid "" msgid "- from %.2f mm to %.2f mm in Y and is %.2f mm deep"
"the print goes from %f mm to %f mm in Y\n" msgstr "- de %.02f mm à %.02f mm en Y et mesure %.02f mm de profondeur"
"and is %f mm wide\n"
msgstr ""
"L'impression va de %f mm à %f mm en Y\n"
"et mesure %f mm de large\n"
#: pronterface.py:1593 #: pronterface.py:1315
msgid "" msgid "- from %.2f mm to %.2f mm in Z and is %.2f mm high"
"the print goes from %f mm to %f mm in Z\n" msgstr "- de %.02f mm à %.02f mm en Y et mesure %.02f mm de haut"
"and is %f mm high\n"
msgstr ""
"L'impression va de %f mm à %f mm en Y\n"
"et mesure %f mm de haut\n"
#: pronterface.py:1595
msgid "Estimated duration (pessimistic): "
msgstr "Durée estimée (pessimiste) : "
#: pronterface.py:1622 #: pronterface.py:1316
msgid "No file loaded. Please use load first." msgid "Estimated duration: %s"
msgstr "Aucun fichier chargé. Veuillez charger un fichier avant." msgstr "Durée estimée : %s"
#: pronterface.py:1633 #: pronterface.py:1347
msgid "Restart" msgid "Restart"
msgstr "Recommencer" msgstr "Recommencer"
#: pronterface.py:1637 #: pronterface.py:1351
msgid "File upload complete" msgid "File upload complete"
msgstr "Envoi du fichier terminé" msgstr "Envoi du fichier terminé"
#: pronterface.py:1656 #: pronterface.py:1370
msgid "Pick SD filename" msgid "Pick SD filename"
msgstr "Lister les fichiers sur la carte SD" msgstr "Lister les fichiers sur la carte SD"
#: pronterface.py:1663 #: pronterface.py:1377
msgid "Paused." msgid "Paused."
msgstr "En pause." msgstr "En pause."
#: pronterface.py:1674 #: pronterface.py:1390
msgid "Resume" msgid "Resume"
msgstr "Reprendre" msgstr "Reprendre"
#: pronterface.py:1688 #: pronterface.py:1404
msgid "Connecting..." msgid "Connecting..."
msgstr "Connexion en cours..." msgstr "Connexion en cours..."
#: pronterface.py:1738 #: pronterface.py:1430
msgid "Error: You are trying to connect to a non-exisiting port."
msgstr ""
#: pronterface.py:1432
msgid "Error: You don't have permission to open %s."
msgstr ""
#: pronterface.py:1433
msgid "You might need to add yourself to the dialout group."
msgstr ""
#: pronterface.py:1466
msgid "Disconnected." msgid "Disconnected."
msgstr "Déconnecté." msgstr "Déconnecté."
#: pronterface.py:1771 #: pronterface.py:1499
msgid "Reset." msgid "Reset."
msgstr "Réinitialisée." msgstr "Réinitialisée."
#: pronterface.py:1772 #: pronterface.py:1500
msgid "Are you sure you want to reset the printer?" msgid "Are you sure you want to reset the printer?"
msgstr "Etes-vous sûr de vouloir réinitialiser l'imprimante?" msgstr "Etes-vous sûr de vouloir réinitialiser l'imprimante?"
#: pronterface.py:1772 #: pronterface.py:1500
msgid "Reset?" msgid "Reset?"
msgstr "Réinitialiser ?" msgstr "Réinitialiser ?"
#~ msgid "Failed to start web interface"
#~ msgstr "Échec du lancement de l'interface web"
#~ msgid "CherryPy is not installed. Web Interface Disabled."
#~ msgstr "CherryPy n'est pas installé. L'interface web est désactivée."
#~ msgid ", %d lines"
#~ msgstr ", %d lignes"
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2012-08-08 10:09+CEST\n" "POT-Creation-Date: 2013-05-22 12:58+CEST\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -15,575 +15,1140 @@ msgstr "" ...@@ -15,575 +15,1140 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n" "Generated-By: pygettext.py 1.5\n"
#: printrun/pronterface_widgets.py:34 #: ./plater.py:246
msgid "Find" msgid "Plate building tool"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:36 #: ./plater.py:252
msgid "Save" msgid "Clear"
msgstr ""
#: ./plater.py:253
msgid "Load"
msgstr ""
#: ./plater.py:255 ./plater.py:258
msgid "Export"
msgstr ""
#: ./plater.py:260
msgid "Done"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:41 pronterface.py:477 pronterface.py:1535 #: ./plater.py:262 ./printrun/pronterface_widgets.py:42 ./pronterface.py:494
#: ./pronterface.py:1253
msgid "Cancel" msgid "Cancel"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:125 #: ./plater.py:264
msgid "Snap to Z = 0"
msgstr ""
#: ./plater.py:265
msgid "Put at 100, 100"
msgstr ""
#: ./plater.py:266
msgid "Delete"
msgstr ""
#: ./plater.py:267
msgid "Auto"
msgstr ""
#: ./plater.py:291
msgid "Autoplating"
msgstr ""
#: ./plater.py:319
msgid "Bed full, sorry sir :("
msgstr ""
#: ./plater.py:329
msgid "Are you sure you want to clear the grid? All unsaved changes will be lost."
msgstr ""
#: ./plater.py:329
msgid "Clear the grid?"
msgstr ""
#: ./plater.py:371
msgid "Pick file to save to"
msgstr ""
#: ./plater.py:372
msgid "STL files (;*.stl;*.STL;)"
msgstr ""
#: ./plater.py:393
msgid "wrote %s"
msgstr ""
#: ./plater.py:396
msgid "Pick file to load"
msgstr ""
#: ./plater.py:397
msgid "STL files (;*.stl;*.STL;)|*.stl|OpenSCAD files (;*.scad;)|*.scad"
msgstr ""
#: ./printrun/gui.py:21 ./pronterface.py:26
msgid "WX is not installed. This program requires WX to run."
msgstr ""
#: ./printrun/gui.py:81
msgid "XY:"
msgstr ""
#: ./printrun/gui.py:83
msgid "mm/min Z:"
msgstr ""
#: ./printrun/gui.py:88
msgid "Watch"
msgstr ""
#: ./printrun/gui.py:93
msgid "Heat:"
msgstr ""
#: ./printrun/gui.py:96
msgid "Switch Hotend Off"
msgstr ""
#: ./printrun/gui.py:96 ./printrun/gui.py:115
msgid "Off"
msgstr ""
#: ./printrun/gui.py:108
msgid "Switch Hotend On"
msgstr ""
#: ./printrun/gui.py:108 ./printrun/gui.py:127
msgid "Set"
msgstr ""
#: ./printrun/gui.py:112 ./printrun/gui.py:176
msgid "Bed:"
msgstr ""
#: ./printrun/gui.py:115
msgid "Switch Heated Bed Off"
msgstr ""
#: ./printrun/gui.py:156
msgid "mm"
msgstr ""
#: ./printrun/gui.py:165
msgid ""
"mm/\n"
"min"
msgstr ""
#: ./printrun/gui.py:174
msgid "Heater:"
msgstr ""
#: ./printrun/gui.py:248
msgid "Send"
msgstr ""
#: ./printrun/gui.py:248
msgid "Send Command to Printer"
msgstr ""
#: ./printrun/gui.py:256
msgid ""
"Communication Settings\n"
"Click to rescan ports"
msgstr ""
#: ./printrun/gui.py:256
msgid "Port"
msgstr ""
#: ./printrun/gui.py:277
msgid "Connect to the printer"
msgstr ""
#: ./printrun/gui.py:277 ./pronterface.py:1475
msgid "Connect"
msgstr ""
#: ./printrun/gui.py:279
msgid "Reset"
msgstr ""
#: ./printrun/gui.py:279
msgid "Reset the printer"
msgstr ""
#: ./printrun/gui.py:280
msgid "Load a 3D model file"
msgstr ""
#: ./printrun/gui.py:280
msgid "Load file"
msgstr ""
#: ./printrun/gui.py:281
msgid "Compose"
msgstr ""
#: ./printrun/gui.py:281
msgid "Simple Plater System"
msgstr ""
#: ./printrun/gui.py:282
msgid "SD"
msgstr ""
#: ./printrun/gui.py:282
msgid "SD Card Printing"
msgstr ""
#: ./printrun/gui.py:284
msgid "Start Printing Loaded File"
msgstr ""
#: ./printrun/gui.py:284 ./pronterface.py:227 ./pronterface.py:1243
#: ./pronterface.py:1301 ./pronterface.py:1421 ./pronterface.py:1493
#: ./pronterface.py:1506
msgid "Print"
msgstr ""
#: ./printrun/gui.py:286
msgid "Pause Current Print"
msgstr ""
#: ./printrun/gui.py:286 ./pronterface.py:1302 ./pronterface.py:1345
#: ./pronterface.py:1397 ./pronterface.py:1420 ./pronterface.py:1492
#: ./pronterface.py:1509
msgid "Pause"
msgstr ""
#: ./printrun/gui.py:287
msgid "Recover"
msgstr ""
#: ./printrun/gui.py:287
msgid "Recover previous Print"
msgstr ""
#: ./printrun/gui.py:308 ./pronsole.py:625 ./pronsole.py:681
#: ./pronterface.py:1113 ./pronterface.py:1339 ./pronterface.py:1451
msgid "Not connected to printer."
msgstr ""
#: ./printrun/libtatlin/actors.py:272
msgid "Initialized 3D visualization in %.2f seconds"
msgstr ""
#: ./printrun/libtatlin/actors.py:273
msgid "Vertex count: %d"
msgstr ""
#: ./printrun/pronterface_widgets.py:35
msgid "Find"
msgstr ""
#: ./printrun/pronterface_widgets.py:37
msgid "Save"
msgstr ""
#: ./printrun/pronterface_widgets.py:126
msgid "Edit settings" msgid "Edit settings"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:127 #: ./printrun/pronterface_widgets.py:128
msgid "Defaults" msgid "Defaults"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:156 #: ./printrun/pronterface_widgets.py:157
msgid "Custom button" msgid "Custom button"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:161 #: ./printrun/pronterface_widgets.py:162
msgid "Button title" msgid "Button title"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:164 #: ./printrun/pronterface_widgets.py:165
msgid "Command" msgid "Command"
msgstr "" msgstr ""
#: printrun/pronterface_widgets.py:173 #: ./printrun/pronterface_widgets.py:174
msgid "Color" msgid "Color"
msgstr "" msgstr ""
#: pronterface.py:26 #: ./pronsole.py:173
msgid "WX is not installed. This program requires WX to run." msgid "Communications Speed (default: 115200)"
msgstr ""
#: ./pronsole.py:174
msgid "Heated Build Platform temp for ABS (default: 110 deg C)"
msgstr "" msgstr ""
#: pronterface.py:93 #: ./pronsole.py:175
msgid "Heated Build Platform temp for PLA (default: 60 deg C)"
msgstr ""
#: ./pronsole.py:176
msgid "Feedrate for Control Panel Moves in Extrusions (default: 300mm/min)"
msgstr ""
#: ./pronsole.py:177
msgid "Port used to communicate with printer"
msgstr ""
#: ./pronsole.py:178
msgid ""
"Slice command\n"
" default:\n"
" python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s)"
msgstr ""
#: ./pronsole.py:179
msgid ""
"Slice settings command\n"
" default:\n"
" python skeinforge/skeinforge_application/skeinforge.py"
msgstr ""
#: ./pronsole.py:180
msgid "Extruder temp for ABS (default: 230 deg C)"
msgstr ""
#: ./pronsole.py:181
msgid "Extruder temp for PLA (default: 185 deg C)"
msgstr ""
#: ./pronsole.py:182
msgid "Feedrate for Control Panel Moves in X and Y (default: 3000mm/min)"
msgstr ""
#: ./pronsole.py:183
msgid "Feedrate for Control Panel Moves in Z (default: 200mm/min)"
msgstr ""
#: ./pronsole.py:184
msgid "Executable to run when the print is finished"
msgstr ""
#: ./pronsole.py:622
msgid "Please enter target name in 8.3 format."
msgstr ""
#: ./pronsole.py:628
msgid "Uploading as %s"
msgstr ""
#: ./pronsole.py:629
msgid "Uploading %s"
msgstr ""
#: ./pronsole.py:631
msgid "Press Ctrl-C to interrupt upload."
msgstr ""
#: ./pronsole.py:634
msgid "Progress: "
msgstr ""
#: ./pronsole.py:646
msgid "Upload completed. %s should now be on the card."
msgstr ""
#: ./pronsole.py:649
msgid "...interrupted!"
msgstr ""
#: ./pronsole.py:655
msgid "A partial file named %s may have been written to the sd card."
msgstr ""
#: ./pronsole.py:672
msgid "Send a loaded gcode file to the printer. Load a file with the load command first."
msgstr ""
#: ./pronsole.py:674
msgid "Send a loaded gcode file to the printer. You have %s loaded right now."
msgstr ""
#: ./pronsole.py:678 ./pronterface.py:1336
msgid "No file loaded. Please use load first."
msgstr ""
#: ./pronsole.py:683
msgid "Printing %s"
msgstr ""
#: ./pronsole.py:684
msgid "You can monitor the print with the monitor command."
msgstr ""
#: ./pronsole.py:692
msgid "Not printing, cannot pause."
msgstr ""
#: ./pronsole.py:698
msgid "Pauses a running print"
msgstr ""
#: ./pronsole.py:702
msgid "Not paused, unable to resume. Start a print first."
msgstr ""
#: ./pronsole.py:712
msgid "Resumes a paused print."
msgstr ""
#: ./pronsole.py:727
msgid "Files on SD card:"
msgstr ""
#: ./pronsole.py:741 ./pronsole.py:783 ./pronsole.py:1088
msgid "Printer is not online. Please connect to it first."
msgstr ""
#: ./pronsole.py:746
msgid "Lists files on the SD card"
msgstr ""
#: ./pronsole.py:750 ./pronterface.py:1163
msgid "Opening file failed."
msgstr ""
#: ./pronsole.py:756 ./pronterface.py:1169
msgid "Starting print"
msgstr ""
#: ./pronsole.py:779
msgid "Resets the printer."
msgstr ""
#: ./pronsole.py:789
msgid "File is not present on card. Please upload it first."
msgstr ""
#: ./pronsole.py:793
msgid "Printing file: %s from SD card."
msgstr ""
#: ./pronsole.py:794
msgid "Requesting SD print..."
msgstr ""
#: ./pronsole.py:798
msgid "Print a file from the SD card. Tab completes with available file names."
msgstr ""
#: ./pronsole.py:799
msgid "sdprint filename.g"
msgstr ""
#: ./pronsole.py:834 ./pronsole.py:842 ./pronsole.py:892 ./pronsole.py:918
#: ./pronterface.py:354 ./pronterface.py:374 ./pronterface.py:436
msgid "Printer is not online."
msgstr ""
#: ./pronsole.py:875
msgid "Read the extruder and bed temperature."
msgstr ""
#: ./pronsole.py:885
msgid "%s is a high temperature to set your extruder to. Are you sure you want to do that?"
msgstr ""
#: ./pronsole.py:890
msgid "Setting hotend temperature to %s degrees Celsius."
msgstr ""
#: ./pronsole.py:894 ./pronterface.py:356
msgid "You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0."
msgstr ""
#: ./pronsole.py:896 ./pronsole.py:922
msgid "You must enter a temperature."
msgstr ""
#: ./pronsole.py:899
msgid "Sets the hotend temperature to the value entered."
msgstr ""
#: ./pronsole.py:900 ./pronsole.py:926
msgid "Enter either a temperature in celsius or one of the following keywords"
msgstr ""
#: ./pronsole.py:916
msgid "Setting bed temperature to %s degrees Celsius."
msgstr ""
#: ./pronsole.py:920 ./pronterface.py:376
msgid "You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0."
msgstr ""
#: ./pronsole.py:925
msgid "Sets the bed temperature to the value entered."
msgstr ""
#: ./pronsole.py:935
msgid "No move specified."
msgstr ""
#: ./pronsole.py:938 ./pronsole.py:1041 ./pronsole.py:1182
msgid "Printer is currently printing. Please pause the print before you issue manual commands."
msgstr ""
#: ./pronsole.py:941 ./pronsole.py:1179
msgid "Printer is not online. Unable to move."
msgstr ""
#: ./pronsole.py:957
msgid "Unknown axis."
msgstr ""
#: ./pronsole.py:963
msgid "Invalid distance"
msgstr ""
#: ./pronsole.py:974
msgid "Move an axis. Specify the name of the axis and the amount. "
msgstr ""
#: ./pronsole.py:975
msgid "move X 10 will move the X axis forward by 10mm at %s mm/min (default XY speed)"
msgstr ""
#: ./pronsole.py:976
msgid "move Y 10 5000 will move the Y axis forward by 10mm at 5000mm/min"
msgstr ""
#: ./pronsole.py:977
msgid "move Z -1 will move the Z axis down by 1mm at %s mm/min (default Z speed)"
msgstr ""
#: ./pronsole.py:978
msgid "Common amounts are in the tabcomplete list."
msgstr ""
#: ./pronsole.py:1008 ./pronsole.py:1048
msgid "Invalid length given."
msgstr ""
#: ./pronsole.py:1013 ./pronsole.py:1053
msgid "Invalid speed given."
msgstr ""
#: ./pronsole.py:1018
msgid "Extruding %fmm of filament."
msgstr ""
#: ./pronsole.py:1020
msgid "Reversing %fmm of filament."
msgstr ""
#: ./pronsole.py:1022
msgid "Length is 0, not doing anything."
msgstr ""
#: ./pronsole.py:1028
msgid "Extrudes a length of filament, 5mm by default, or the number of mm given as a parameter"
msgstr ""
#: ./pronsole.py:1029
msgid "extrude - extrudes 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1030
msgid "extrude 20 - extrudes 20mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1031
msgid "extrude -5 - REVERSES 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1032
msgid "extrude 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"
msgstr ""
#: ./pronsole.py:1038
msgid "Printer is not online. Unable to reverse."
msgstr ""
#: ./pronsole.py:1057
msgid "Reverses the extruder, 5mm by default, or the number of mm given as a parameter"
msgstr ""
#: ./pronsole.py:1058
msgid "reverse - reverses 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1059
msgid "reverse 20 - reverses 20mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1060
msgid "reverse 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"
msgstr ""
#: ./pronsole.py:1061
msgid "reverse -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)"
msgstr ""
#: ./pronsole.py:1078
msgid "Exiting program. Goodbye!"
msgstr ""
#: ./pronsole.py:1083
msgid "Disconnects from the printer and exits the program."
msgstr ""
#: ./pronsole.py:1091
msgid "Printer is not printing. Please print something before monitoring."
msgstr ""
#: ./pronsole.py:1093
msgid "Monitoring printer, use ^C to interrupt."
msgstr ""
#: ./pronsole.py:1098 ./pronterface.py:1015
msgid "Invalid period given."
msgstr ""
#: ./pronsole.py:1099
msgid "Updating values every %f seconds."
msgstr ""
#: ./pronsole.py:1110 ./pronsole.py:1113
msgid "Print progress: "
msgstr ""
#: ./pronsole.py:1122 ./pronterface.py:1020
msgid "Done monitoring."
msgstr ""
#: ./pronsole.py:1126
msgid "Monitor a machine's temperatures and an SD print's status."
msgstr ""
#: ./pronsole.py:1127
msgid "monitor - Reports temperature and SD print status (if SD printing) every 5 seconds"
msgstr ""
#: ./pronsole.py:1128
msgid "monitor 2 - Reports temperature and SD print status (if SD printing) every 2 seconds"
msgstr ""
#: ./pronsole.py:1136
msgid "No file name given."
msgstr ""
#: ./pronsole.py:1142
msgid "Skeining file: %s"
msgstr ""
#: ./pronsole.py:1144 ./pronterface.py:1287
msgid "File not found!"
msgstr ""
#: ./pronsole.py:1149
msgid "Entering slicer settings: %s"
msgstr ""
#: ./pronsole.py:1153
msgid "Slicing: "
msgstr ""
#: ./pronsole.py:1156
msgid "Loading sliced file."
msgstr ""
#: ./pronsole.py:1159
msgid "Skeinforge execution failed: %s"
msgstr ""
#: ./pronsole.py:1172
msgid "Creates a gcode file from an stl model using the slicer (with tab-completion)"
msgstr ""
#: ./pronsole.py:1173
msgid "skein filename.stl - create gcode file"
msgstr ""
#: ./pronsole.py:1174
msgid "skein filename.stl view - create gcode file and view using skeiniso"
msgstr ""
#: ./pronsole.py:1175
msgid "skein set - adjust slicer settings"
msgstr ""
#: ./pronsole.py:1197
msgid "Homes the printer"
msgstr ""
#: ./pronsole.py:1198
msgid "home - homes all axes and zeroes the extruder(Using G28 and G92)"
msgstr ""
#: ./pronsole.py:1199
msgid "home xy - homes x and y axes (Using G28)"
msgstr ""
#: ./pronsole.py:1200
msgid "home z - homes z axis only (Using G28)"
msgstr ""
#: ./pronsole.py:1201
msgid "home e - set extruder position to zero (Using G92)"
msgstr ""
#: ./pronsole.py:1202
msgid "home xyze - homes all axes and zeroes the extruder (Using G28 and G92)"
msgstr ""
#: ./pronsole.py:1205
msgid "load this file on startup instead of .pronsolerc ; you may chain config files, if so settings auto-save will use the last specified file"
msgstr ""
#: ./pronsole.py:1206
msgid "executes command after configuration/.pronsolerc is loaded ; macros/settings from these commands are not autosaved"
msgstr ""
#: ./pronsole.py:1207
msgid "file to load"
msgstr ""
#: ./pronterface.py:100
msgid "" msgid ""
"Dimensions of Build Platform\n" "Dimensions of Build Platform\n"
" & optional offset of origin\n" " & optional offset of origin\n"
" & optional switch position\n"
"\n" "\n"
"Examples:\n" "Examples:\n"
" XXXxYYY\n" " XXXxYYY\n"
" XXX,YYY,ZZZ\n" " XXX,YYY,ZZZ\n"
" XXXxYYYxZZZ+OffX+OffY+OffZ" " XXXxYYYxZZZ+OffX+OffY+OffZ\n"
"XXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ"
msgstr "" msgstr ""
#: pronterface.py:94 #: ./pronterface.py:101
msgid "Last Set Temperature for the Heated Print Bed" msgid "Last Set Temperature for the Heated Print Bed"
msgstr "" msgstr ""
#: pronterface.py:95 #: ./pronterface.py:102
msgid "Folder of last opened file" msgid "Folder of last opened file"
msgstr "" msgstr ""
#: pronterface.py:96 #: ./pronterface.py:103
msgid "Last Temperature of the Hot End" msgid "Last Temperature of the Hot End"
msgstr "" msgstr ""
#: pronterface.py:97 #: ./pronterface.py:104
msgid "Width of Extrusion in Preview (default: 0.5)" msgid "Width of Extrusion in Preview (default: 0.5)"
msgstr "" msgstr ""
#: pronterface.py:98 #: ./pronterface.py:105
msgid "Fine Grid Spacing (default: 10)" msgid "Fine Grid Spacing (default: 10)"
msgstr "" msgstr ""
#: pronterface.py:99 #: ./pronterface.py:106
msgid "Coarse Grid Spacing (default: 50)" msgid "Coarse Grid Spacing (default: 50)"
msgstr "" msgstr ""
#: pronterface.py:100 #: ./pronterface.py:107
msgid "Pronterface background color (default: #FFFFFF)" msgid "Pronterface background color (default: #FFFFFF)"
msgstr "" msgstr ""
#: pronterface.py:103 #: ./pronterface.py:110
msgid "Printer Interface" msgid "Printer Interface"
msgstr "" msgstr ""
#: pronterface.py:122 #: ./pronterface.py:127
msgid "Motors off" msgid "Motors off"
msgstr "" msgstr ""
#: pronterface.py:122 #: ./pronterface.py:127
msgid "Switch all motors off" msgid "Switch all motors off"
msgstr "" msgstr ""
#: pronterface.py:123 #: ./pronterface.py:128
msgid "Check current hotend temperature" msgid "Check current hotend temperature"
msgstr "" msgstr ""
#: pronterface.py:123 #: ./pronterface.py:128
msgid "Check temp" msgid "Check temp"
msgstr "" msgstr ""
#: pronterface.py:124 #: ./pronterface.py:129
msgid "Advance extruder by set length" msgid "Advance extruder by set length"
msgstr "" msgstr ""
#: pronterface.py:124 #: ./pronterface.py:129
msgid "Extrude" msgid "Extrude"
msgstr "" msgstr ""
#: pronterface.py:125 #: ./pronterface.py:130
msgid "Reverse" msgid "Reverse"
msgstr "" msgstr ""
#: pronterface.py:125 #: ./pronterface.py:130
msgid "Reverse extruder by set length" msgid "Reverse extruder by set length"
msgstr "" msgstr ""
#: pronterface.py:143 #: ./pronterface.py:173
msgid "" msgid ""
"# I moved all your custom buttons into .pronsolerc.\n" "# I moved all your custom buttons into .pronsolerc.\n"
"# Please don't add them here any more.\n" "# Please don't add them here any more.\n"
"# Backup of your old buttons is in custombtn.old\n" "# Backup of your old buttons is in custombtn.old\n"
msgstr "" msgstr ""
#: pronterface.py:148 #: ./pronterface.py:178
msgid "Note!!! You have specified custom buttons in both custombtn.txt and .pronsolerc" msgid "Note!!! You have specified custom buttons in both custombtn.txt and .pronsolerc"
msgstr "" msgstr ""
#: pronterface.py:149 #: ./pronterface.py:179
msgid "Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt" msgid "Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt"
msgstr "" msgstr ""
#: pronterface.py:181 #: ./pronterface.py:209
msgid "Failed to start web interface" msgid "display graphical temperature gauges in addition to the temperatures graph"
msgstr "" msgstr ""
#: pronterface.py:185 #: ./pronterface.py:210
msgid "CherryPy is not installed. Web Interface Disabled." msgid "automatically try to connect to printer on startup"
msgstr "" msgstr ""
#: pronterface.py:197 pronterface.py:603 pronterface.py:1525 #: ./pronterface.py:219
#: pronterface.py:1578 pronterface.py:1705 pronterface.py:1765 msgid "Print Started at: %s"
#: pronterface.py:1778 msgstr ""
msgid "Print"
#: ./pronterface.py:224
msgid "Print ended at: %(end_time)s and took %(duration)s"
msgstr "" msgstr ""
#: pronterface.py:207 #: ./pronterface.py:238
msgid "Printer is now online." msgid "Printer is now online."
msgstr "" msgstr ""
#: pronterface.py:208 #: ./pronterface.py:239
msgid "Disconnect" msgid "Disconnect"
msgstr "" msgstr ""
#: pronterface.py:331 #: ./pronterface.py:351
msgid "Setting hotend temperature to %f degrees Celsius." msgid "Setting hotend temperature to %f degrees Celsius."
msgstr "" msgstr ""
#: pronterface.py:334 pronterface.py:356 pronterface.py:428 #: ./pronterface.py:358 ./pronterface.py:378
msgid "Printer is not online."
msgstr ""
#: pronterface.py:336
msgid "You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0."
msgstr ""
#: pronterface.py:338 pronterface.py:364
msgid "You must enter a temperature. (%s)" msgid "You must enter a temperature. (%s)"
msgstr "" msgstr ""
#: pronterface.py:353 #: ./pronterface.py:371
msgid "Setting bed temperature to %f degrees Celsius." msgid "Setting bed temperature to %f degrees Celsius."
msgstr "" msgstr ""
#: pronterface.py:360 #: ./pronterface.py:393
msgid "You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0."
msgstr ""
#: pronterface.py:381
msgid "Do you want to erase the macro?" msgid "Do you want to erase the macro?"
msgstr "" msgstr ""
#: pronterface.py:385 #: ./pronterface.py:397
msgid "Cancelled." msgid "Cancelled."
msgstr "" msgstr ""
#: pronterface.py:436 #: ./pronterface.py:442
msgid " Opens file" msgid " Opens file"
msgstr "" msgstr ""
#: pronterface.py:436 #: ./pronterface.py:442
msgid "&Open..." msgid "&Open..."
msgstr "" msgstr ""
#: pronterface.py:437 #: ./pronterface.py:443
msgid " Edit open file" msgid " Edit open file"
msgstr "" msgstr ""
#: pronterface.py:437 #: ./pronterface.py:443
msgid "&Edit..." msgid "&Edit..."
msgstr "" msgstr ""
#: pronterface.py:438 #: ./pronterface.py:444
msgid " Clear output console" msgid " Clear output console"
msgstr "" msgstr ""
#: pronterface.py:438 #: ./pronterface.py:444
msgid "Clear console" msgid "Clear console"
msgstr "" msgstr ""
#: pronterface.py:439 #: ./pronterface.py:445
msgid " Project slices" msgid " Project slices"
msgstr "" msgstr ""
#: pronterface.py:439 #: ./pronterface.py:445
msgid "Projector" msgid "Projector"
msgstr "" msgstr ""
#: pronterface.py:440 #: ./pronterface.py:446
msgid " Closes the Window" msgid " Closes the Window"
msgstr "" msgstr ""
#: pronterface.py:440 #: ./pronterface.py:446
msgid "E&xit" msgid "E&xit"
msgstr "" msgstr ""
#: pronterface.py:441 #: ./pronterface.py:447
msgid "&File" msgid "&File"
msgstr "" msgstr ""
#: pronterface.py:446 #: ./pronterface.py:452
msgid "&Macros" msgid "&Macros"
msgstr "" msgstr ""
#: pronterface.py:447 #: ./pronterface.py:453
msgid "<&New...>" msgid "<&New...>"
msgstr "" msgstr ""
#: pronterface.py:448 #: ./pronterface.py:454
msgid " Options dialog" msgid " Options dialog"
msgstr "" msgstr ""
#: pronterface.py:448 #: ./pronterface.py:454
msgid "&Options" msgid "&Options"
msgstr "" msgstr ""
#: pronterface.py:450 #: ./pronterface.py:456
msgid " Adjust slicing settings" msgid " Adjust slicing settings"
msgstr "" msgstr ""
#: pronterface.py:450 #: ./pronterface.py:456
msgid "Slicing Settings" msgid "Slicing Settings"
msgstr "" msgstr ""
#: pronterface.py:452 #: ./pronterface.py:458
msgid "Debug G-code"
msgstr ""
#: ./pronterface.py:459
msgid "Print all G-code sent to and received from the printer."
msgstr ""
#: ./pronterface.py:469
msgid "&Settings" msgid "&Settings"
msgstr "" msgstr ""
#: pronterface.py:467 #: ./pronterface.py:484
msgid "Enter macro name" msgid "Enter macro name"
msgstr "" msgstr ""
#: pronterface.py:470 #: ./pronterface.py:487
msgid "Macro name:" msgid "Macro name:"
msgstr "" msgstr ""
#: pronterface.py:473 #: ./pronterface.py:490
msgid "Ok" msgid "Ok"
msgstr "" msgstr ""
#: pronterface.py:495 #: ./pronterface.py:512
msgid "Macro name may contain only ASCII alphanumeric symbols and underscores" msgid "Macro name may contain only ASCII alphanumeric symbols and underscores"
msgstr "" msgstr ""
#: pronterface.py:500 #: ./pronterface.py:515
msgid "Name '%s' is being used by built-in command" msgid "Name '%s' is being used by built-in command"
msgstr "" msgstr ""
#: pronterface.py:548 #: ./pronterface.py:583
msgid "Port"
msgstr ""
#: pronterface.py:570 pronterface.py:1747
msgid "Connect"
msgstr ""
#: pronterface.py:574
msgid "Reset"
msgstr ""
#: pronterface.py:589
msgid "Load file"
msgstr ""
#: pronterface.py:593
msgid "Compose"
msgstr ""
#: pronterface.py:598
msgid "SD"
msgstr ""
#: pronterface.py:608 pronterface.py:1579 pronterface.py:1631
#: pronterface.py:1681 pronterface.py:1704 pronterface.py:1764
#: pronterface.py:1781
msgid "Pause"
msgstr ""
#: pronterface.py:612
msgid "Recover"
msgstr ""
#: pronterface.py:630
msgid "Send"
msgstr ""
#: pronterface.py:671
msgid "XY:"
msgstr ""
#: pronterface.py:673
msgid "mm/min Z:"
msgstr ""
#: pronterface.py:678
msgid "Watch"
msgstr ""
#: pronterface.py:683
msgid "Heat:"
msgstr ""
#: pronterface.py:686 pronterface.py:709
msgid "Off"
msgstr ""
#: pronterface.py:700 pronterface.py:723
msgid "Set"
msgstr ""
#: pronterface.py:706
msgid "Bed:"
msgstr ""
#: pronterface.py:756
msgid "mm"
msgstr ""
#: pronterface.py:764
msgid ""
"mm/\n"
"min"
msgstr ""
#: pronterface.py:821 pronterface.py:1387 pronterface.py:1625
#: pronterface.py:1723
msgid "Not connected to printer."
msgstr ""
#: pronterface.py:869
msgid "SD Upload" msgid "SD Upload"
msgstr "" msgstr ""
#: pronterface.py:873 #: ./pronterface.py:587
msgid "SD Print" msgid "SD Print"
msgstr "" msgstr ""
#: pronterface.py:914 #: ./pronterface.py:628
msgid "Mini mode" msgid "Mini mode"
msgstr "" msgstr ""
#: pronterface.py:921 #: ./pronterface.py:635
msgid "Full mode" msgid "Full mode"
msgstr "" msgstr ""
#: pronterface.py:946 #: ./pronterface.py:660
msgid "Execute command: " msgid "Execute command: "
msgstr "" msgstr ""
#: pronterface.py:957 #: ./pronterface.py:671
msgid "click to add new custom button" msgid "click to add new custom button"
msgstr "" msgstr ""
#: pronterface.py:979 #: ./pronterface.py:693
msgid "Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command" msgid "Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command"
msgstr "" msgstr ""
#: pronterface.py:1003 #: ./pronterface.py:715
msgid "Custom button number should be between 0 and 63" msgid "Custom button number should be between 0 and 63"
msgstr "" msgstr ""
#: pronterface.py:1096 #: ./pronterface.py:806
msgid "Edit custom button '%s'" msgid "Edit custom button '%s'"
msgstr "" msgstr ""
#: pronterface.py:1098 #: ./pronterface.py:808
msgid "Move left <<" msgid "Move left <<"
msgstr "" msgstr ""
#: pronterface.py:1101 #: ./pronterface.py:811
msgid "Move right >>" msgid "Move right >>"
msgstr "" msgstr ""
#: pronterface.py:1105 #: ./pronterface.py:815
msgid "Remove custom button '%s'" msgid "Remove custom button '%s'"
msgstr "" msgstr ""
#: pronterface.py:1108 #: ./pronterface.py:818
msgid "Add custom button" msgid "Add custom button"
msgstr "" msgstr ""
#: pronterface.py:1270 #: ./pronterface.py:987
msgid "event object missing" msgid "event object missing"
msgstr "" msgstr ""
#: pronterface.py:1306 #: ./pronterface.py:1018
msgid "Invalid period given."
msgstr ""
#: pronterface.py:1311
msgid "Monitoring printer." msgid "Monitoring printer."
msgstr "" msgstr ""
#: pronterface.py:1315 #: ./pronterface.py:1033
msgid "Done monitoring." msgid "Attempted to write invalid text to console, which could be due to an invalid baudrate"
msgstr "" msgstr ""
#: pronterface.py:1355 #: ./pronterface.py:1084
msgid " SD printing:%04.2f %%" msgid " SD printing:%04.2f %%"
msgstr "" msgstr ""
#: pronterface.py:1358 #: ./pronterface.py:1087
msgid " Printing: %04.2f%% |" msgid " Printing: %04.2f%% |"
msgstr "" msgstr ""
#: pronterface.py:1359 #: ./pronterface.py:1088
msgid " Line# %d of %d lines |" msgid " Line# %d of %d lines |"
msgstr "" msgstr ""
#: pronterface.py:1364 #: ./pronterface.py:1093
msgid " Est: %s of %s remaining | " msgid " Est: %s of %s remaining | "
msgstr "" msgstr ""
#: pronterface.py:1366 #: ./pronterface.py:1095
msgid " Z: %0.2f mm" msgid " Z: %0.2f mm"
msgstr "" msgstr ""
#: pronterface.py:1439 #: ./pronterface.py:1190
msgid "Opening file failed."
msgstr ""
#: pronterface.py:1445
msgid "Starting print"
msgstr ""
#: pronterface.py:1466
msgid "Pick SD file" msgid "Pick SD file"
msgstr "" msgstr ""
#: pronterface.py:1466 #: ./pronterface.py:1190
msgid "Select the file to print" msgid "Select the file to print"
msgstr "" msgstr ""
#: pronterface.py:1501 #: ./pronterface.py:1222
msgid "Failed to execute slicing software: " msgid "Failed to execute slicing software: "
msgstr "" msgstr ""
#: pronterface.py:1510 #: ./pronterface.py:1229
msgid "Slicing..." msgid "Slicing..."
msgstr "" msgstr ""
#: pronterface.py:1523 #: ./pronterface.py:1241 ./pronterface.py:1300
msgid ", %d lines" msgid "Loaded %s, %d lines"
msgstr ""
#: pronterface.py:1523
msgid "Loaded "
msgstr "" msgstr ""
#: pronterface.py:1530 #: ./pronterface.py:1248
msgid "Load File" msgid "Load File"
msgstr "" msgstr ""
#: pronterface.py:1536 #: ./pronterface.py:1254
msgid "Slicing " msgid "Slicing "
msgstr "" msgstr ""
#: pronterface.py:1555 #: ./pronterface.py:1279
msgid "Open file to print" msgid "Open file to print"
msgstr "" msgstr ""
#: pronterface.py:1556 #: ./pronterface.py:1280
msgid "OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*" msgid "OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*"
msgstr "" msgstr ""
#: pronterface.py:1563 #: ./pronterface.py:1311
msgid "File not found!" msgid "mm of filament used in this print"
msgstr "" msgstr ""
#: pronterface.py:1577 #: ./pronterface.py:1312
msgid "Loaded %s, %d lines" msgid "The print goes:"
msgstr "" msgstr ""
#: pronterface.py:1588 #: ./pronterface.py:1313
msgid "" msgid "- from %.2f mm to %.2f mm in X and is %.2f mm wide"
"mm of filament used in this print\n"
msgstr "" msgstr ""
#: pronterface.py:1589 pronterface.py:1591 #: ./pronterface.py:1314
msgid "" msgid "- from %.2f mm to %.2f mm in Y and is %.2f mm deep"
"the print goes from %f mm to %f mm in X\n"
"and is %f mm wide\n"
msgstr ""
#: pronterface.py:1592
msgid ""
"the print goes from %f mm to %f mm in Y\n"
"and is %f mm wide\n"
msgstr ""
#: pronterface.py:1593
msgid ""
"the print goes from %f mm to %f mm in Z\n"
"and is %f mm high\n"
msgstr "" msgstr ""
#: pronterface.py:1595 #: ./pronterface.py:1315
msgid "Estimated duration (pessimistic): " msgid "- from %.2f mm to %.2f mm in Z and is %.2f mm high"
msgstr "" msgstr ""
#: pronterface.py:1622 #: ./pronterface.py:1316
msgid "No file loaded. Please use load first." msgid "Estimated duration: %s"
msgstr "" msgstr ""
#: pronterface.py:1633 #: ./pronterface.py:1347
msgid "Restart" msgid "Restart"
msgstr "" msgstr ""
#: pronterface.py:1637 #: ./pronterface.py:1351
msgid "File upload complete" msgid "File upload complete"
msgstr "" msgstr ""
#: pronterface.py:1656 #: ./pronterface.py:1370
msgid "Pick SD filename" msgid "Pick SD filename"
msgstr "" msgstr ""
#: pronterface.py:1663 #: ./pronterface.py:1377
msgid "Paused." msgid "Paused."
msgstr "" msgstr ""
#: pronterface.py:1674 #: ./pronterface.py:1390
msgid "Resume" msgid "Resume"
msgstr "" msgstr ""
#: pronterface.py:1688 #: ./pronterface.py:1404
msgid "Connecting..." msgid "Connecting..."
msgstr "" msgstr ""
#: pronterface.py:1738 #: ./pronterface.py:1430
msgid "Error: You are trying to connect to a non-exisiting port."
msgstr ""
#: ./pronterface.py:1432
msgid "Error: You don't have permission to open %s."
msgstr ""
#: ./pronterface.py:1433
msgid "You might need to add yourself to the dialout group."
msgstr ""
#: ./pronterface.py:1466
msgid "Disconnected." msgid "Disconnected."
msgstr "" msgstr ""
#: pronterface.py:1771 #: ./pronterface.py:1499
msgid "Reset." msgid "Reset."
msgstr "" msgstr ""
#: pronterface.py:1772 #: ./pronterface.py:1500
msgid "Are you sure you want to reset the printer?" msgid "Are you sure you want to reset the printer?"
msgstr "" msgstr ""
#: pronterface.py:1772 #: ./pronterface.py:1500
msgid "Reset?" msgid "Reset?"
msgstr "" msgstr ""
...@@ -23,6 +23,9 @@ import platform, os ...@@ -23,6 +23,9 @@ import platform, os
from GCodeAnalyzer import GCodeAnalyzer from GCodeAnalyzer import GCodeAnalyzer
import socket # Network import socket # Network
import re # Regex import re # Regex
from collections import deque
from printrun.GCodeAnalyzer import GCodeAnalyzer
from printrun import gcoder
def control_ttyhup(port, disable_hup): def control_ttyhup(port, disable_hup):
"""Controls the HUPCL""" """Controls the HUPCL"""
...@@ -47,6 +50,7 @@ class printcore(): ...@@ -47,6 +50,7 @@ class printcore():
""" """
self.baud = None self.baud = None
self.port = None self.port = None
self.analyzer = GCodeAnalyzer()
self.printer = None #Serial instance connected to the printer, None when disconnected self.printer = None #Serial instance connected to the printer, None when disconnected
self.clear = 0 #clear to send, enabled after responses self.clear = 0 #clear to send, enabled after responses
self.online = False #The printer has responded to the initial command and is active self.online = False #The printer has responded to the initial command and is active
...@@ -63,6 +67,7 @@ class printcore(): ...@@ -63,6 +67,7 @@ class printcore():
self.tempcb = None #impl (wholeline) self.tempcb = None #impl (wholeline)
self.recvcb = None #impl (wholeline) self.recvcb = None #impl (wholeline)
self.sendcb = None #impl (wholeline) self.sendcb = None #impl (wholeline)
self.printsendcb = None #impl (wholeline)
self.errorcb = None #impl (wholeline) self.errorcb = None #impl (wholeline)
self.startcb = None #impl () self.startcb = None #impl ()
self.endcb = None #impl () self.endcb = None #impl ()
...@@ -230,8 +235,8 @@ class printcore(): ...@@ -230,8 +235,8 @@ class printcore():
def _checksum(self, command): def _checksum(self, command):
return reduce(lambda x, y:x^y, map(ord, command)) return reduce(lambda x, y:x^y, map(ord, command))
def startprint(self, data, startindex = 0): def startprint(self, gcode, startindex = 0):
"""Start a print, data is an array of gcode commands. """Start a print, gcode is an array of gcode commands.
returns True on success, False if already printing. returns True on success, False if already printing.
The print queue will be replaced with the contents of the data array, the next line will be set to 0 and the firmware notified. The print queue will be replaced with the contents of the data array, the next line will be set to 0 and the firmware notified.
Printing will then start in a parallel thread. Printing will then start in a parallel thread.
...@@ -239,12 +244,12 @@ class printcore(): ...@@ -239,12 +244,12 @@ class printcore():
if self.printing or not self.online or not self.printer: if self.printing or not self.online or not self.printer:
return False return False
self.printing = True self.printing = True
self.mainqueue = [] + data self.mainqueue = gcode
self.lineno = 0 self.lineno = 0
self.queueindex = startindex self.queueindex = startindex
self.resendfrom = -1 self.resendfrom = -1
self._send("M110", -1, True) self._send("M110", -1, True)
if len(data) == 0: if not gcode.lines:
return True return True
self.clear = False self.clear = False
self.print_thread = Thread(target = self._print) self.print_thread = Thread(target = self._print)
...@@ -369,7 +374,7 @@ class printcore(): ...@@ -369,7 +374,7 @@ class printcore():
while self.printing and self.printer and self.online: while self.printing and self.printer and self.online:
self._sendnext() self._sendnext()
self.sentlines = {} self.sentlines = {}
self.log = [] self.log.clear()
self.sent = [] self.sent = []
try: try:
self.print_thread.join() self.print_thread.join()
...@@ -403,23 +408,26 @@ class printcore(): ...@@ -403,23 +408,26 @@ class printcore():
self.resendfrom += 1 self.resendfrom += 1
return return
self.resendfrom = -1 self.resendfrom = -1
for i in self.priqueue[:]: if self.priqueue:
self._send(i) self._send(self.priqueue.pop(0))
del self.priqueue[0]
return return
if self.printing and self.queueindex < len(self.mainqueue): if self.printing and self.queueindex < len(self.mainqueue):
tline = self.mainqueue[self.queueindex] (layer, line) = self.mainqueue.idxs(self.queueindex)
gline = self.mainqueue.all_layers[layer].lines[line]
tline = gline.raw
#check for host command #check for host command
if tline.lstrip().startswith(";@"): if tline.lstrip().startswith(";@"):
#it is a host command: pop it from the list
self.mainqueue.pop(self.queueindex)
self.processHostCommand(tline) self.processHostCommand(tline)
self.queueindex += 1
return return
tline = tline.split(";")[0] tline = tline.split(";")[0]
if len(tline) > 0: if len(tline) > 0:
self._send(tline, self.lineno, True) self._send(tline, self.lineno, True)
self.lineno += 1 self.lineno += 1
if self.printsendcb:
try: self.printsendcb(gline)
except: pass
else: else:
self.clear = True self.clear = True
self.queueindex += 1 self.queueindex += 1
...@@ -493,18 +501,19 @@ if __name__ == '__main__': ...@@ -493,18 +501,19 @@ if __name__ == '__main__':
p = printcore(port, baud) p = printcore(port, baud)
p.loud = loud p.loud = loud
time.sleep(2) time.sleep(2)
gcode = [i.replace("\n", "") for i in open(filename)] gcode = [i.strip() for i in open(filename)]
gcode = gcoder.GCode(gcode)
p.startprint(gcode) p.startprint(gcode)
try: try:
if statusreport: if statusreport:
p.loud = False p.loud = False
sys.stdout.write("Progress: 00.0%") sys.stdout.write("Progress: 00.0%\r")
sys.stdout.flush() sys.stdout.flush()
while p.printing: while p.printing:
time.sleep(1) time.sleep(1)
if statusreport: if statusreport:
sys.stdout.write("%02.1f%%\r" % (100 * float(p.queueindex) / len(p.mainqueue),) ) sys.stdout.write("Progress: %02.1f%%\r" % (100 * float(p.queueindex) / len(p.mainqueue),) )
sys.stdout.flush() sys.stdout.flush()
p.disconnect() p.disconnect()
sys.exit(0) sys.exit(0)
......
# This file is part of the Printrun suite.
#
# Copyright 2013 Francesco Santini francesco.santini@gmail.com
#
# Printrun is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Printrun is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
#
# This code is imported from RepetierHost - Original copyright and license:
# Copyright 2011 repetier repetierdev@gmail.com
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import re
import gcoder
class GCodeAnalyzer():
def __init__(self):
self.x = 0
self.y = 0
self.z = 0
self.e = 0
self.emax = 0
self.f = 1000
self.lastX = 0
self.lastY = 0
self.lastZ = 0
self.lastE = 0
self.xOffset = 0
self.yOffset = 0
self.zOffset = 0
self.eOffset = 0
self.lastZPrint = 0
self.layerZ = 0
self.relative = False
self.eRelative = False
self.homeX = 0
self.homeY = 0
self.homeZ = 0
self.maxX = 150
self.maxY = 150
self.maxZ = 150
self.minX = 0
self.minY = 0
self.minZ = 0
self.hasHomeX = False
self.hasHomeY = False
self.hasHomeZ = False
def Analyze(self, gcode):
gline = gcoder.Line(gcode)
if gline.command.startswith(";@"): return # code is a host command
code_g = int(gline.command[1:]) if gline.command.startswith("G") else None
code_m = int(gline.command[1:]) if gline.command.startswith("M") else None
#get movement codes
if gline.is_move:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
eChanged = False;
code_f = gline.f
if code_f != None:
self.f = code_f
code_x = gline.x
code_y = gline.y
code_z = gline.z
code_e = gline.e
if self.relative:
if code_x != None: self.x += code_x
if code_y != None: self.y += code_y
if code_z != None: self.z += code_z
if code_e != None:
if code_e != 0:
eChanged = True
self.e += code_e
else:
#absolute coordinates
if code_x != None: self.x = self.xOffset + code_x
if code_y != None: self.y = self.yOffset + code_y
if code_z != None: self.z = self.zOffset + code_z
if code_e != None:
if self.eRelative:
if code_e != 0:
eChanged = True
self.e += code_e
else:
# e is absolute. Is it changed?
if self.e != self.eOffset + code_e:
eChanged = True
self.e = self.eOffset + code_e
#limit checking
if self.x < self.minX: self.x = self.minX
if self.y < self.minY: self.y = self.minY
if self.z < self.minZ: self.z = self.minZ
if self.x > self.maxX: self.x = self.maxX
if self.y > self.maxY: self.y = self.maxY
if self.z > self.maxZ: self.z = self.maxZ
#Repetier has a bunch of limit-checking code here and time calculations: we are leaving them for now
elif code_g == 28 or code_g == 161:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
code_x = gline.x
code_y = gline.y
code_z = gline.z
code_e = gline.e
homeAll = False
if code_x == None and code_y == None and code_z == None: homeAll = True
if code_x != None or homeAll:
self.hasHomeX = True
self.xOffset = 0
self.x = self.homeX
if code_y != None or homeAll:
self.hasHomeY = True
self.yOffset = 0
self.y = self.homeY
if code_z != None or homeAll:
self.hasHomeZ = True
self.zOffset = 0
self.z = self.homeZ
if code_e != None:
self.eOffset = 0
self.e = 0
elif code_g == 162:
self.lastX = self.x
self.lastY = self.y
self.lastZ = self.z
self.lastE = self.e
code_x = gline.x
code_y = gline.y
code_z = gline.z
homeAll = False
if code_x == None and code_y == None and code_z == None: homeAll = True
if code_x != None or homeAll:
self.hasHomeX = True
self.xOffset = 0
self.x = self.maxX
if code_y != None or homeAll:
self.hasHomeY = True
self.yOffset = 0
self.y = self.maxY
if code_z != None or homeAll:
self.hasHomeZ = True
self.zOffset = 0
self.z = self.maxZ
elif code_g == 90: self.relative = False
elif code_g == 91: self.relative = True
elif code_g == 92:
code_x = gline.x
code_y = gline.y
code_z = gline.z
code_e = gline.e
if code_x != None:
self.xOffset = self.x - float(code_x)
self.x = self.xOffset
if code_y != None:
self.yOffset = self.y - float(code_y)
self.y = self.yOffset
if code_z != None:
self.zOffset = self.z - float(code_z)
self.z = self.zOffset
if code_e != None:
self.xOffset = self.e - float(code_e)
self.e = self.eOffset
#End code_g != None
if code_m != None:
code_m = int(code_m)
if code_m == 82: self.eRelative = False
elif code_m == 83: self.eRelative = True
def print_status(self):
attrs = vars(self)
print '\n'.join("%s: %s" % item for item in attrs.items())
...@@ -77,7 +77,7 @@ class BufferedCanvas(wx.Panel): ...@@ -77,7 +77,7 @@ class BufferedCanvas(wx.Panel):
## General methods ## General methods
## ##
def draw(self, dc): def draw(self, dc, w, h):
""" """
Stub: called when the canvas needs to be re-drawn. Stub: called when the canvas needs to be re-drawn.
""" """
......
#!/usr/bin/env python
# This file is copied from GCoder.
#
# GCoder is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# GCoder is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
import sys
import re
import math
import datetime
from array import array
gcode_parsed_args = ["x", "y", "e", "f", "z", "p", "i", "j", "s"]
gcode_exp = re.compile("\([^\(\)]*\)|;.*|[/\*].*\n|[a-z][-+]?[0-9]*\.?[0-9]*")
m114_exp = re.compile("\([^\(\)]*\)|[/\*].*\n|[A-Z]:?[-+]?[0-9]*\.?[0-9]*")
move_gcodes = ["G0", "G1", "G2", "G3"]
class Line(object):
__slots__ = ('x','y','z','e','f','i','j','s','p',
'raw','split_raw',
'command','is_move',
'relative','relative_e',
'current_x', 'current_y', 'current_z', 'extruding',
'gcview_end_vertex')
def __init__(self, l):
self.raw = l
self.split_raw = gcode_exp.findall(self.raw.lower())
self.command = self.split_raw[0].upper() if not self.split_raw[0].startswith("n") else self.split_raw[1].upper()
self.is_move = self.command in move_gcodes
def __getattr__(self, name):
return None
def parse_coordinates(self, imperial = False, force = False):
# Not a G-line, we don't want to parse its arguments
if not force and not self.command[0] == "G":
return
if imperial:
for bit in self.split_raw:
code = bit[0]
if code in gcode_parsed_args and len(bit) > 1:
setattr(self, code, 25.4*float(bit[1:]))
else:
for bit in self.split_raw:
code = bit[0]
if code in gcode_parsed_args and len(bit) > 1:
setattr(self, code, float(bit[1:]))
del self.split_raw
def __repr__(self):
return self.raw
class Layer(object):
lines = None
duration = None
def __init__(self, lines):
self.lines = lines
def _preprocess(self, current_x, current_y, current_z):
xmin = float("inf")
ymin = float("inf")
zmin = 0
xmax = float("-inf")
ymax = float("-inf")
zmax = float("-inf")
relative = False
relative_e = False
for line in self.lines:
if not line.is_move and line.command != "G92":
continue
if line.is_move:
x = line.x
y = line.y
z = line.z
if line.relative:
x = current_x + (x or 0)
y = current_y + (y or 0)
z = current_z + (z or 0)
current_x = x or current_x
current_y = y or current_y
current_z = z or current_z
if line.e:
if x:
xmin = min(xmin, x)
xmax = max(xmax, x)
if y:
ymin = min(ymin, y)
ymax = max(ymax, y)
if current_z:
zmin = min(zmin, current_z)
zmax = max(zmax, current_z)
else:
current_x = line.x or current_x
current_y = line.y or current_y
current_z = line.z or current_z
line.current_x = current_x
line.current_y = current_y
line.current_z = current_z
return (current_x, current_y, current_z), (xmin, xmax), (ymin, ymax), (zmin, zmax)
class GCode(object):
lines = None
layers = None
all_layers = None
layer_idxs = None
line_idxs = None
append_layer = None
append_layer_id = None
imperial = False
relative = False
relative_e = False
filament_length = None
xmin = None
xmax = None
ymin = None
ymax = None
zmin = None
zmax = None
width = None
depth = None
height = None
def __init__(self,data):
self.lines = [Line(l2) for l2 in
(l.strip() for l in data)
if l2]
self._preprocess_lines()
self._preprocess_extrusion()
self._create_layers()
self._preprocess_layers()
def __len__(self):
return len(self.line_idxs)
def append(self, command):
command = command.strip()
if not command:
return
gline = Line(command)
self.lines.append(gline)
self._preprocess([gline])
self.append_layer.lines.append(gline)
self.layer_idxs.append(self.append_layer_id)
self.line_idxs.append(len(self.append_layer.lines))
def _preprocess_lines(self, lines = None):
"""Checks for G20, G21, G90 and G91, sets imperial and relative flags"""
if not lines:
lines = self.lines
imperial = self.imperial
relative = self.relative
relative_e = self.relative_e
for line in self.lines:
if line.is_move:
line.relative = relative
line.relative_e = relative_e
elif line.command == "G20":
imperial = True
elif line.command == "G21":
imperial = False
elif line.command == "G90":
relative = False
relative_e = False
elif line.command == "G91":
relative = True
relative_e = True
elif line.command == "M82":
relative_e = False
elif line.command == "M83":
relative_e = True
if line.command[0] == "G":
line.parse_coordinates(imperial)
self.imperial = imperial
self.relative = relative
self.relative_e = relative_e
def _preprocess_extrusion(self):
total_e = 0
max_e = 0
cur_e = 0
for line in self.lines:
if line.e == None:
continue
if line.is_move:
if line.relative_e:
line.extruding = line.e != 0
total_e += line.e
else:
line.extruding = line.e != cur_e
total_e += line.e - cur_e
cur_e = line.e
max_e = max(max_e, total_e)
elif line.command == "G92":
cur_e = line.e
self.filament_length = max_e
# FIXME : looks like this needs to be tested with list Z on move
def _create_layers(self):
layers = {}
all_layers = []
layer_idxs = []
line_idxs = []
layer_id = 0
layer_line = 0
prev_z = None
cur_z = 0
cur_lines = []
for line in self.lines:
if line.command == "G92" and line.z != None:
cur_z = line.z
elif line.is_move:
if line.z != None:
if line.relative:
cur_z += line.z
else:
cur_z = line.z
if cur_z != prev_z:
all_layers.append(Layer(cur_lines))
old_lines = layers.get(prev_z, [])
old_lines += cur_lines
layers[prev_z] = old_lines
cur_lines = []
layer_id += 1
layer_line = 0
cur_lines.append(line)
layer_idxs.append(layer_id)
line_idxs.append(layer_line)
layer_line += 1
prev_z = cur_z
if cur_lines:
all_layers.append(Layer(cur_lines))
old_lines = layers.pop(prev_z, [])
old_lines += cur_lines
layers[prev_z] = old_lines
for idx in layers.keys():
cur_lines = layers[idx]
has_movement = False
for l in layers[idx]:
if l.is_move and l.e != None:
has_movement = True
break
if has_movement:
layers[idx] = Layer(cur_lines)
else:
del layers[idx]
self.append_layer_id = len(all_layers)
self.append_layer = Layer([])
all_layers.append(self.append_layer)
self.all_layers = all_layers
self.layers = layers
self.layer_idxs = array('I', layer_idxs)
self.line_idxs = array('I', line_idxs)
def idxs(self, i):
return self.layer_idxs[i], self.line_idxs[i]
def num_layers(self):
return len(self.layers)
def _preprocess_layers(self):
xmin = float("inf")
ymin = float("inf")
zmin = 0
xmax = float("-inf")
ymax = float("-inf")
zmax = float("-inf")
current_x = 0
current_y = 0
current_z = 0
for l in self.all_layers:
(current_x, current_y, current_z), (xm, xM), (ym, yM), (zm, zM) = l._preprocess(current_x, current_y, current_z)
xmin = min(xm, xmin)
xmax = max(xM, xmax)
ymin = min(ym, ymin)
ymax = max(yM, ymax)
zmin = min(zm, zmin)
zmax = max(zM, zmax)
self.xmin = xmin
self.xmax = xmax
self.ymin = ymin
self.ymax = ymax
self.zmin = zmin
self.zmax = zmax
self.width = xmax - xmin
self.depth = ymax - ymin
self.height = zmax - zmin
def estimate_duration(self):
lastx = lasty = lastz = laste = lastf = 0.0
x = y = z = e = f = 0.0
currenttravel = 0.0
totaltravel = 0.0
moveduration = 0.0
totalduration = 0.0
acceleration = 1500.0 #mm/s/s ASSUMING THE DEFAULT FROM SPRINTER !!!!
layerduration = 0.0
layerbeginduration = 0.0
layercount = 0
#TODO:
# get device caps from firmware: max speed, acceleration/axis (including extruder)
# calculate the maximum move duration accounting for above ;)
for layer in self.all_layers:
for line in layer.lines:
if line.command not in ["G1", "G0", "G4"]:
continue
if line.command == "G4":
moveduration = line.p
if not moveduration:
continue
else:
moveduration /= 1000.0
else:
x = line.x if line.x != None else lastx
y = line.y if line.y != None else lasty
e = line.e if line.e != None else laste
f = line.f / 60.0 if line.f != None else lastf # mm/s vs mm/m => divide by 60
# given last feedrate and current feedrate calculate the distance needed to achieve current feedrate.
# if travel is longer than req'd distance, then subtract distance to achieve full speed, and add the time it took to get there.
# then calculate the time taken to complete the remaining distance
currenttravel = math.hypot(x - lastx, y - lasty)
# FIXME: review this better
# this looks wrong : there's little chance that the feedrate we'll decelerate to is the previous feedrate
# shouldn't we instead look at three consecutive moves ?
distance = 2 * abs(((lastf + f) * (f - lastf) * 0.5) / acceleration) # multiply by 2 because we have to accelerate and decelerate
if distance <= currenttravel and lastf + f != 0 and f != 0:
# Unsure about this formula -- iXce reviewing this code
moveduration = 2 * distance / (lastf + f)
currenttravel -= distance
moveduration += currenttravel/f
else:
moveduration = math.sqrt(2 * distance / acceleration) # probably buggy : not taking actual travel into account
totalduration += moveduration
lastx = x
lasty = y
laste = e
lastf = f
layer.duration = totalduration - layerbeginduration
layerbeginduration = totalduration
return "%d layers, %s" % (len(self.layers), str(datetime.timedelta(seconds = int(totalduration))))
def main():
if len(sys.argv) < 2:
print "usage: %s filename.gcode" % sys.argv[0]
return
gcode = GCode(open(sys.argv[1]))
print "Dimensions:"
print "\tX: %0.02f - %0.02f (%0.02f)" % (gcode.xmin,gcode.xmax,gcode.width)
print "\tY: %0.02f - %0.02f (%0.02f)" % (gcode.ymin,gcode.ymax,gcode.depth)
print "\tZ: %0.02f - %0.02f (%0.02f)" % (gcode.zmin,gcode.zmax,gcode.height)
print "Filament used: %0.02fmm" % gcode.filament_length
print "Number of layers: %d" % gcode.num_layers()
print "Estimated duration: %s" % gcode.estimate_duration()
if __name__ == '__main__':
main()
#!/usr/bin/python #!/usr/bin/python
# This file is part of the Printrun suite. # This file is part of the Printrun suite.
# #
# Printrun is free software: you can redistribute it and/or modify # Printrun is free software: you can redistribute it and/or modify
...@@ -16,43 +17,40 @@ ...@@ -16,43 +17,40 @@
import os import os
import math import math
import wx import wx
from wx import glcanvas from wx import glcanvas
import time
import threading
import pyglet import pyglet
pyglet.options['shadow_window'] = False pyglet.options['debug_gl'] = True
pyglet.options['debug_gl'] = False
from pyglet.gl import *
import stltool from pyglet.gl import *
from pyglet import gl
import threading from pyglet.graphics.vertexbuffer import create_buffer
from printrun import gcoder
from printrun import stltool
from printrun.libtatlin import actors
class GLPanel(wx.Panel): class wxGLPanel(wx.Panel):
'''A simple class for using OpenGL with wxPython.''' '''A simple class for using OpenGL with wxPython.'''
def __init__(self, parent, id, pos = wx.DefaultPosition, def __init__(self, parent, id, pos = wx.DefaultPosition,
size = wx.DefaultSize, style = 0): size = wx.DefaultSize, style = 0):
# Forcing a no full repaint to stop flickering # Forcing a no full repaint to stop flickering
style = style | wx.NO_FULL_REPAINT_ON_RESIZE style = style | wx.NO_FULL_REPAINT_ON_RESIZE
#call super function super(wxGLPanel, self).__init__(parent, id, pos, size, style)
super(GLPanel, self).__init__(parent, id, pos, size, style)
#init gl canvas data
self.GLinitialized = False self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit
# Create the canvas
self.sizer = wx.BoxSizer(wx.HORIZONTAL) self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.canvas = glcanvas.GLCanvas(self, attribList = attribList) self.canvas = glcanvas.GLCanvas(self, attribList = attribList)
self.sizer.Add(self.canvas, 1, wx.EXPAND) self.sizer.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(self.sizer) self.SetSizer(self.sizer)
#self.sizer.Fit(self) self.sizer.Fit(self)
self.Layout()
# bind events # bind events
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent) self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
...@@ -89,12 +87,12 @@ class GLPanel(wx.Panel): ...@@ -89,12 +87,12 @@ class GLPanel(wx.Panel):
self.OnReshape(size.width, size.height) self.OnReshape(size.width, size.height)
self.canvas.Refresh(False) self.canvas.Refresh(False)
event.Skip() event.Skip()
#wx.CallAfter(self.Refresh)
def processPaintEvent(self, event): def processPaintEvent(self, event):
'''Process the drawing event.''' '''Process the drawing event.'''
self.canvas.SetCurrent() self.canvas.SetCurrent()
# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized: if not self.GLinitialized:
self.OnInitGL() self.OnInitGL()
self.GLinitialized = True self.GLinitialized = True
...@@ -104,7 +102,7 @@ class GLPanel(wx.Panel): ...@@ -104,7 +102,7 @@ class GLPanel(wx.Panel):
def Destroy(self): def Destroy(self):
#clean up the pyglet OpenGL context #clean up the pyglet OpenGL context
#self.pygletcontext.destroy() self.pygletcontext.destroy()
#call the super method #call the super method
super(wx.Panel, self).Destroy() super(wx.Panel, self).Destroy()
...@@ -116,51 +114,27 @@ class GLPanel(wx.Panel): ...@@ -116,51 +114,27 @@ class GLPanel(wx.Panel):
#create a pyglet context for this panel #create a pyglet context for this panel
self.pmat = (GLdouble * 16)() self.pmat = (GLdouble * 16)()
self.mvmat = (GLdouble * 16)() self.mvmat = (GLdouble * 16)()
self.pygletcontext = Context(current_context) self.pygletcontext = gl.Context(gl.current_context)
self.pygletcontext.canvas = self
self.pygletcontext.set_current() self.pygletcontext.set_current()
self.dist = 1000 self.dist = 1000
self.vpmat = None self.vpmat = None
#normal gl init #normal gl init
glClearColor(0, 0, 0, 1) glClearColor(0.98, 0.98, 0.78, 1)
glColor3f(1, 0, 0) glClearDepth(1.0) # set depth value to 1
glDepthFunc(GL_LEQUAL)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST) glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE) glEnable(GL_CULL_FACE)
# Uncomment this line for a wireframe view glEnable(GL_BLEND)
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
self.OnReshape(*self.GetClientSize())
# Simple light setup. On Windows GL_LIGHT0 is enabled by default,
# but this is not the case on Linux or Mac, so remember to always
# include it.
glEnable(GL_LIGHTING)
glEnable(GL_LIGHT0)
glEnable(GL_LIGHT1)
# Define a simple function to create ctypes arrays of floats:
def vec(*args):
return (GLfloat * len(args))(*args)
glLightfv(GL_LIGHT0, GL_POSITION, vec(.5, .5, 1, 0))
glLightfv(GL_LIGHT0, GL_SPECULAR, vec(.5, .5, 1, 1))
glLightfv(GL_LIGHT0, GL_DIFFUSE, vec(1, 1, 1, 1))
glLightfv(GL_LIGHT1, GL_POSITION, vec(1, 0, .5, 0))
glLightfv(GL_LIGHT1, GL_DIFFUSE, vec(.5, .5, .5, 1))
glLightfv(GL_LIGHT1, GL_SPECULAR, vec(1, 1, 1, 1))
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.5, 0, 0.3, 1))
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, vec(1, 1, 1, 1))
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50)
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, vec(0, 0.1, 0, 0.9))
#create objects to draw
#self.create_objects()
def OnReshape(self, width, height): def OnReshape(self, width, height):
'''Reshape the OpenGL viewport based on the dimensions of the window.''' '''Reshape the OpenGL viewport based on the dimensions of the window.'''
if not self.GLinitialized: if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True self.GLinitialized = True
self.pmat = (GLdouble * 16)() self.OnInitGL()
self.mvmat = (GLdouble * 16)()
glViewport(0, 0, width, height) glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION) glMatrixMode(GL_PROJECTION)
glLoadIdentity() glLoadIdentity()
...@@ -170,8 +144,6 @@ class GLPanel(wx.Panel): ...@@ -170,8 +144,6 @@ class GLPanel(wx.Panel):
#pyglet stuff #pyglet stuff
self.vpmat = (GLint * 4)(0, 0, *list(self.GetClientSize())) self.vpmat = (GLint * 4)(0, 0, *list(self.GetClientSize()))
glGetDoublev(GL_PROJECTION_MATRIX, self.pmat) glGetDoublev(GL_PROJECTION_MATRIX, self.pmat)
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
#glMatrixMode(GL_PROJECTION)
# Wrap text to the width of the window # Wrap text to the width of the window
if self.GLinitialized: if self.GLinitialized:
...@@ -180,13 +152,9 @@ class GLPanel(wx.Panel): ...@@ -180,13 +152,9 @@ class GLPanel(wx.Panel):
def OnDraw(self, *args, **kwargs): def OnDraw(self, *args, **kwargs):
"""Draw the window.""" """Draw the window."""
#clear the context
self.canvas.SetCurrent()
self.pygletcontext.set_current() self.pygletcontext.set_current()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#draw objects
self.draw_objects() self.draw_objects()
#update screen
self.SwapBuffers() self.SwapBuffers()
#========================================================================== #==========================================================================
...@@ -204,262 +172,8 @@ class GLPanel(wx.Panel): ...@@ -204,262 +172,8 @@ class GLPanel(wx.Panel):
'''called in the middle of ondraw after the buffer has been cleared''' '''called in the middle of ondraw after the buffer has been cleared'''
pass pass
def _dist(dist):
"""return axis length, or 0 if None"""
if dist is None:
return 0
else:
return float(dist)
class gcpoint(object):
"""gcode point
stub for first line"""
def __init__(self, x = 0, y = 0, z = 0, e = 0):
self.x = x
self.y = y
self.z = z
self.e = e
self.length = 0
class gcline(object):
"""gcode move line
Once initialised, it knows its position, length and extrusion ratio
Returns lines into gcview batch()
"""
def __init__(self, x = None, y = None, z = None, e = None, f = None, prev_gcline = None, orgline = False):
if prev_gcline is None:
self.prev_gcline = gcpoint()
else:
self.prev_gcline = prev_gcline
if x is None:
self.x = self.prev_gcline.x
else:
self.x = float(x)
if y is None:
self.y = self.prev_gcline.y
else:
self.y = float(y)
if z is None:
self.z = self.prev_gcline.z
else:
self.z = float(z)
if e is None:
self.e = self.prev_gcline.e
else:
self.e = float(e)
self.f = f
self.orgline = orgline
self.calc_delta()
self.calc_len()
def __str__(self):
return u"line from %s,%s,%s to %s,%s,%s with extrusion ratio %s and feedrate %s\n%s" % (
self.prev_gcline.x,
self.prev_gcline.y,
self.prev_gcline.z,
self.x,
self.y,
self.z,
self.extrusion_ratio,
self.f,
self.orgline,
)
def calc_delta(self, prev_gcline = None):
if prev_gcline is None:
prev_gcline = self.prev_gcline
if self.prev_gcline is not None:
self.dx = self.x - prev_gcline.x
self.dy = self.y - prev_gcline.y
self.dz = self.z - prev_gcline.z
self.de = self.e - prev_gcline.e
else:
self.dx = self.x
self.dy = self.y
self.dz = self.z
self.de = self.e
def calc_len(self):
if self.dz != 0:
self.length = math.sqrt(self.dx**2 + self.dy**2 + self.dz**2)
else:
self.length = math.sqrt(self.dx**2 + self.dy**2)
if self.de:
self.extrusion_ratio = self.length / self.de
else:
self.extrusion_ratio = 0
def glline(self):
return [
self.prev_gcline.x,
self.prev_gcline.y,
self.prev_gcline.z,
self.x,
self.y,
self.z,
]
def glcolor(self, upper_limit = None, lower_limit = 0, max_feedrate = 0):
if self.extrusion_ratio == 0:
return [255, 255, 255, 0, 0, 0]
else:
blue_color = 0
green_color = 0
if upper_limit is not None:
if self.extrusion_ratio <= lower_limit:
blue_color = 0
else:
blue_color = int ((self.extrusion_ratio - lower_limit) / (upper_limit - lower_limit) * 255)
else:
blue_color = 0
if max_feedrate > 0 and self.f > 0:
green_color = int((self.f/max_feedrate) * 255)
if green_color > 255:
green_color = 255
if green_color < 0:
green_color = 0
if blue_color > 255:
blue_color = 255
if blue_color < 0:
blue_color = 0
return[255, green_color, blue_color, 128, green_color, blue_color/4]
def float_from_line(axe, line):
return float(line.split(axe)[1].split(" ")[0])
class gcThreadRenderer(threading.Thread):
def __init__(self, gcview, lines):
threading.Thread.__init__(self)
self.gcview = gcview
self.lines = lines
print "q init"
def run(self):
for line in self.lines:
layer_name = line.z
if line.z not in self.gcview.layers:
self.gcview.layers[line.z] = pyglet.graphics.Batch()
self.gcview.layerlist = self.gcview.layers.keys()
self.gcview.layerlist.sort()
self.gcview.layers[line.z].add(2, GL_LINES, None, ("v3f", line.glline()), ("c3B", line.glcolor(self.gcview.upper_limit, self.gcview.lower_limit, self.gcview.max_feedrate)))
self.gcview.t2 = time.time()
print "Rendered lines in %fs" % (self.gcview.t2-self.gcview.t1)
class gcview(object):
"""gcode visualiser
Holds opengl objects for all layers
"""
def __init__(self, lines, batch, w = 0.5, h = 0.5):
if len(lines) == 0:
return
print "Loading %s lines" % (len(lines))
#End pos of previous mode
self.prev = gcpoint()
# Correction for G92 moves
self.delta = [0, 0, 0, 0]
self.layers = {}
self.t0 = time.time()
self.lastf = 0
lines = [self.transform(i) for i in lines]
lines = [i for i in lines if i is not None]
self.t1 = time.time()
print "transformed %s lines in %fs" % (len(lines), self.t1- self.t0)
self.upper_limit = 0
self.lower_limit = None
self.max_feedrate = 0
for line in lines:
if line.extrusion_ratio and line.length > 0.005: #lines shorter than 0.003 can have large extrusion ratio
if line.extrusion_ratio > self.upper_limit:
self.upper_limit = line.extrusion_ratio
if self.lower_limit is None or line.extrusion_ratio < self.lower_limit:
self.lower_limit = line.extrusion_ratio
if line.f > self.max_feedrate:
self.max_feedrate = line.f
#print upper_limit, lower_limit
#self.render_gl(lines)
q = gcThreadRenderer(self, lines)
q.setDaemon(True)
q.start()
def transform(self, line):
"""transforms line of gcode into gcline object (or None if its not move)
Tracks coordinates across resets in self.delta
"""
orgline = line
line = line.split(";")[0]
cur = [None, None, None, None, None]
if len(line) > 0:
if "G92" in line:
#Recalculate delta on G92 (reset)
if("X" in line):
try:
self.delta[0] = float_from_line("X", line) + self.prev.x
except:
self.delta[0] = 0
if("Y" in line):
try:
self.delta[1] = float_from_line("Y", line) + self.prev.y
except:
self.delta[1] = 0
if("Z" in line):
try:
self.delta[2] = float_from_line("Z", line) + self.prev.z
except:
self.delta[2] = 0
if("E" in line):
try:
self.delta[3] = float_from_line("E", line) + self.prev.e
except:
self.delta[3] = 0
return None
if "G1" in line or "G0" in line:
#Create new gcline
if("X" in line):
cur[0] = float_from_line("X", line) + self.delta[0]
if("Y" in line):
cur[1] = float_from_line("Y", line) + self.delta[1]
if("Z" in line):
cur[2] = float_from_line("Z", line) + self.delta[2]
if("E" in line):
cur[3] = float_from_line("E", line) + self.delta[3]
if "F" in line:
cur[4] = float_from_line("F", line)
if cur == [None, None, None, None, None]:
return None
else:
#print cur
if cur[4] is None:
cur[4] = self.lastf
else:
self.lastf = cur[4]
r = gcline(x = cur[0], y = cur[1], z = cur[2], e = cur[3], f = cur[4], prev_gcline = self.prev, orgline = orgline)
self.prev = r
return r
return None
def delete(self):
#for i in self.vlists:
# i.delete()
#self.vlists = []
pass
def trackball(p1x, p1y, p2x, p2y, r): def trackball(p1x, p1y, p2x, p2y, r):
TRACKBALLSIZE = r TRACKBALLSIZE = r
#float a[3]; /* Axis of rotation */ #float a[3]; /* Axis of rotation */
#float phi; /* how much to rotate about axis */ #float phi; /* how much to rotate about axis */
#float p1[3], p2[3], d[3]; #float p1[3], p2[3], d[3];
...@@ -537,80 +251,81 @@ def mulquat(q1, rq): ...@@ -537,80 +251,81 @@ def mulquat(q1, rq):
q1[3] * rq[3] - q1[0] * rq[0] - q1[1] * rq[1] - q1[2] * rq[2]] q1[3] * rq[3] - q1[0] * rq[0] - q1[1] * rq[1] - q1[2] * rq[2]]
class TestGlPanel(GLPanel): class GcodeViewPanel(wxGLPanel):
def __init__(self, parent, size, id = wx.ID_ANY): def __init__(self, parent, id = wx.ID_ANY, realparent = None):
super(TestGlPanel, self).__init__(parent, id, wx.DefaultPosition, size, 0) super(GcodeViewPanel, self).__init__(parent, id, wx.DefaultPosition, wx.DefaultSize, 0)
self.batches = [] self.batches = []
self.rot = 0 self.rot = 0
self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move) self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move)
self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double) self.canvas.Bind(wx.EVT_LEFT_DCLICK, self.double)
self.initialized = 1 self.canvas.Bind(wx.EVT_KEY_DOWN, self.keypress)
self.initialized = 0
self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel) self.canvas.Bind(wx.EVT_MOUSEWHEEL, self.wheel)
self.parent = parent self.parent = realparent if realparent else parent
self.initpos = None self.initpos = None
self.dist = 200 self.dist = 200
self.bedsize = [200, 200] self.bedsize = [200, 200]
self.transv = [0, 0, -self.dist] self.transv = [0, 0, -self.dist]
self.basequat = [0, 0, 0, 1] self.basequat = [0, 0, 0, 1]
wx.CallAfter(self.forceresize)
self.mousepos = [0, 0] self.mousepos = [0, 0]
def double(self, event): def create_objects(self):
p = event.GetPositionTuple() '''create opengl objects when opengl is initialized'''
sz = self.GetClientSize() for obj in self.parent.objects:
v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize) if obj.model and obj.model.loaded and not obj.model.initialized:
v[1] = self.bedsize[1] - v[1] obj.model.init()
v += [300]
print v
self.add_file("../prusa/metric-prusa/x-end-idler.stl", v)
def forceresize(self):
self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] + 1))
self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] - 1))
threading.Thread(target = self.update).start()
self.initialized = 0
def move_shape(self, delta): def update_object_resize(self):
"""moves shape (selected in l, which is list ListBox of shapes) '''called when the window recieves only if opengl is initialized'''
by an offset specified in tuple delta. pass
Positive numbers move to (rigt, down)"""
name = self.parent.l.GetSelection() def draw_objects(self):
if name == wx.NOT_FOUND: '''called in the middle of ondraw after the buffer has been cleared'''
return False if self.vpmat is None:
return
name = self.parent.l.GetString(name) self.create_objects()
model = self.parent.models[name] if self.rot == 1:
model.offsets = [ glLoadIdentity()
model.offsets[0] + delta[0], glMultMatrixd(self.mvmat)
model.offsets[1] + delta[1], else:
model.offsets[2] glLoadIdentity()
] glTranslatef(*self.transv)
self.Refresh()
return True glPushMatrix()
glTranslatef(-self.parent.platform.width/2, -self.parent.platform.depth/2, 0)
for obj in self.parent.objects:
if not obj.model or not obj.model.loaded or not obj.model.initialized:
continue
glPushMatrix()
glTranslatef(*(obj.offsets))
glRotatef(obj.rot, 0.0, 0.0, 1.0)
glScalef(*obj.scale)
obj.model.display()
glPopMatrix()
glPopMatrix()
def double(self, event):
if self.parent.clickcb:
self.parent.clickcb(event)
def move(self, event): def move(self, event):
"""react to mouse actions: """react to mouse actions:
no mouse: show red mousedrop no mouse: show red mousedrop
LMB: move active object, LMB: rotate viewport
with shift rotate viewport RMB: move viewport
RMB: nothing
with shift move viewport
""" """
if event.Entering():
self.canvas.SetFocus()
event.Skip()
return
if event.Dragging() and event.LeftIsDown(): if event.Dragging() and event.LeftIsDown():
if self.initpos == None: if self.initpos == None:
self.initpos = event.GetPositionTuple() self.initpos = event.GetPositionTuple()
else: else:
if not event.ShiftDown():
currentpos = event.GetPositionTuple()
delta = (
(currentpos[0] - self.initpos[0]),
-(currentpos[1] - self.initpos[1])
)
self.move_shape(delta)
self.initpos = None
return
#print self.initpos #print self.initpos
p1 = self.initpos p1 = self.initpos
self.initpos = None self.initpos = None
...@@ -642,371 +357,225 @@ class TestGlPanel(GLPanel): ...@@ -642,371 +357,225 @@ class TestGlPanel(GLPanel):
if self.initpos is not None: if self.initpos is not None:
self.initpos = None self.initpos = None
elif event.Dragging() and event.RightIsDown() and event.ShiftDown(): elif event.Dragging() and event.RightIsDown():
if self.initpos is None: if self.initpos is None:
self.initpos = event.GetPositionTuple() self.initpos = event.GetPositionTuple()
else: else:
p1 = self.initpos p1 = self.initpos
p2 = event.GetPositionTuple() p2 = event.GetPositionTuple()
sz = self.GetClientSize() sz = self.GetClientSize()
p1 = list(p1) p1 = list(p1) + [0]
p2 = list(p2) p2 = list(p2) + [0]
p1[1] *= -1 p1[1] *= -1
p2[1] *= -1 p2[1] *= -1
sz = list(sz) + [1]
sz[0] *= 2
sz[1] *= 2
self.transv = map(lambda x, y, z, c: c - self.dist * (x - y) / z, list(p1) + [0], list(p2) + [0], list(sz) + [1], self.transv) self.transv = map(lambda x, y, z, c: c - self.dist * (x - y) / z, p1, p2, sz, self.transv)
glLoadIdentity() glLoadIdentity()
glTranslatef(self.transv[0], self.transv[1], 0) glTranslatef(self.transv[0], self.transv[1], 0)
glTranslatef(0, 0, self.transv[2]) glTranslatef(0, 0, self.transv[2])
if(self.rot): if self.rot:
glMultMatrixd(build_rotmatrix(self.basequat)) glMultMatrixd(build_rotmatrix(self.basequat))
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat) glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
self.rot = 1 self.rot = 1
self.initpos = None self.initpos = None
else: else:
#mouse is moving without a button press event.Skip()
p = event.GetPositionTuple() return
sz = self.GetClientSize() event.Skip()
v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize) wx.CallAfter(self.Refresh)
v[1] = self.bedsize[1] - v[1]
self.mousepos = v
def rotate_shape(self, angle): def layerup(self):
"""rotates acive shape if not self.parent.model:
positive angle is clockwise return
""" max_layers = self.parent.model.max_layers
name = self.parent.l.GetSelection() current_layer = self.parent.model.num_layers_to_draw
if name == wx.NOT_FOUND: new_layer = min(max_layers, current_layer + 1)
return False self.parent.model.num_layers_to_draw = new_layer
name = self.parent.l.GetString(name) wx.CallAfter(self.Refresh)
model = self.parent.models[name]
model.rot += angle def layerdown(self):
if not self.parent.model:
return
current_layer = self.parent.model.num_layers_to_draw
new_layer = max(1, current_layer - 1)
self.parent.model.num_layers_to_draw = new_layer
wx.CallAfter(self.Refresh)
def zoom(self, dist):
self.transv[2] += dist
glLoadIdentity()
glTranslatef(*self.transv)
if self.rot:
glMultMatrixd(build_rotmatrix(self.basequat))
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
self.rot = 1
wx.CallAfter(self.Refresh)
def wheel(self, event): def wheel(self, event):
"""react to mouse wheel actions: """react to mouse wheel actions:
rotate object without shift: set max layer
with shift zoom viewport with shift: zoom viewport
""" """
z = event.GetWheelRotation() z = event.GetWheelRotation()
angle = 10 dist = 10
if not event.ShiftDown(): if event.ShiftDown():
i = self.parent.l.GetSelection() if not self.parent.model:
if i < 0:
try:
self.parent.setlayerindex(z)
except:
pass
return return
if z > 0: if z > 0:
self.rotate_shape(angle / 2) self.layerup()
else: else:
self.rotate_shape(-angle / 2) self.layerdown()
return return
if z > 0: if z > 0:
self.transv[2] += angle self.zoom(dist)
else: else:
self.transv[2] -= angle self.zoom(-dist)
glLoadIdentity()
glTranslatef(*self.transv)
if(self.rot):
glMultMatrixd(build_rotmatrix(self.basequat))
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
self.rot = 1
def keypress(self, event): def keypress(self, event):
"""gets keypress events and moves/rotates acive shape""" """gets keypress events and moves/rotates acive shape"""
keycode = event.GetKeyCode() keycode = event.GetKeyCode()
print keycode step = 10
step = 5
angle = 18
if event.ControlDown(): if event.ControlDown():
step = 1 step = 3
angle = 1 kup = [85, 315] # Up keys
#h kdo = [68, 317] # Down Keys
if keycode == 72: kzi = [wx.WXK_PAGEDOWN, 388, 316, 61] # Zoom In Keys
self.move_shape((-step, 0)) kzo = [wx.WXK_PAGEUP, 390, 314, 45] # Zoom Out Keys
#l x = event.GetKeyCode()
if keycode == 76: if x in kup:
self.move_shape((step, 0)) self.layerup()
#j if x in kdo:
if keycode == 75: self.layerdown()
self.move_shape((0, step)) if x in kzi:
#k self.zoom(step)
if keycode == 74: if x in kzo:
self.move_shape((0, -step)) self.zoom(-step)
#[
if keycode == 91:
self.rotate_shape(-angle)
#]
if keycode == 93:
self.rotate_shape(angle)
event.Skip() event.Skip()
def update(self):
while(1):
dt = 0.05
time.sleep(0.05)
try:
wx.CallAfter(self.Refresh)
except:
return
def anim(self, obj):
g = 50 * 9.8
v = 20
dt = 0.05
basepos = obj.offsets[2]
obj.offsets[2] += obj.animoffset
while obj.offsets[2] > -1:
time.sleep(dt)
obj.offsets[2] -= v * dt
v += g * dt
if(obj.offsets[2] < 0):
obj.scale[2] *= 1 - 3 * dt
#return
v = v / 4
while obj.offsets[2] < basepos:
time.sleep(dt)
obj.offsets[2] += v * dt
v -= g * dt
obj.scale[2] *= 1 + 5 * dt
obj.scale[2] = 1.0
def create_objects(self):
'''create opengl objects when opengl is initialized'''
self.initialized = 1
wx.CallAfter(self.Refresh) wx.CallAfter(self.Refresh)
def drawmodel(self, m, n): class GCObject(object):
batch = pyglet.graphics.Batch()
stl = stlview(m.facets, batch = batch)
m.batch = batch
m.animoffset = 300
#print m
#threading.Thread(target = self.anim, args = (m, )).start()
wx.CallAfter(self.Refresh)
def update_object_resize(self): def __init__(self, model):
'''called when the window recieves only if opengl is initialized''' self.offsets = [0, 0, 0]
self.rot = 0
self.curlayer = 0.0
self.scale = [1.0, 1.0, 1.0]
self.batch = pyglet.graphics.Batch()
self.model = model
class GcodeViewMainWrapper(object):
def __init__(self, parent, build_dimensions):
self.glpanel = GcodeViewPanel(parent, realparent = self)
self.glpanel.SetMinSize((150, 150))
self.clickcb = None
self.widget = self.glpanel
self.refresh_timer = wx.CallLater(100, self.Refresh)
self.p = self # Hack for backwards compatibility with gviz API
self.platform = actors.Platform(build_dimensions)
self.model = None
self.objects = [GCObject(self.platform), GCObject(None)]
def __getattr__(self, name):
return getattr(self.glpanel, name)
def set_current_gline(self, gline):
if gline.is_move and self.model and self.model.loaded:
self.model.printed_until = gline.gcview_end_vertex
if not self.refresh_timer.IsRunning():
self.refresh_timer.Start()
def addgcode(self, *a):
pass pass
def draw_objects(self): def setlayer(self, *a):
'''called in the middle of ondraw after the buffer has been cleared''' pass
if self.vpmat is None:
return
if not self.initialized:
self.create_objects()
#glLoadIdentity()
#print list(self.pmat)
if self.rot == 1:
glLoadIdentity()
glMultMatrixd(self.mvmat)
else:
glLoadIdentity()
glTranslatef(*self.transv)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
glBegin(GL_LINES)
glNormal3f(0, 0, 1)
rows = 10
cols = 10
zheight = 50
for i in xrange(-rows, rows + 1):
if i % 5 == 0:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
else:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
glVertex3f(10 * -cols, 10 * i, 0)
glVertex3f(10 * cols, 10 * i, 0)
for i in xrange(-cols, cols + 1):
if i % 5 == 0:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
else:
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.2, 0.2, 0.2, 1))
glVertex3f(10 * i, 10 * -rows, 0)
glVertex3f(10 * i, 10 * rows, 0)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.6, 0.6, 0.6, 1))
glVertex3f(10 * -cols, 10 * -rows, 0)
glVertex3f(10 * -cols, 10 * -rows, zheight)
glVertex3f(10 * cols, 10 * rows, 0)
glVertex3f(10 * cols, 10 * rows, zheight)
glVertex3f(10 * cols, 10 * -rows, 0)
glVertex3f(10 * cols, 10 * -rows, zheight)
glVertex3f(10 * -cols, 10 * rows, 0)
glVertex3f(10 * -cols, 10 * rows, zheight)
glVertex3f(10 * -cols, 10 * rows, zheight)
glVertex3f(10 * cols, 10 * rows, zheight)
glVertex3f(10 * cols, 10 * rows, zheight)
glVertex3f(10 * cols, 10 * -rows, zheight)
glVertex3f(10 * cols, 10 * -rows, zheight)
glVertex3f(10 * -cols, 10 * -rows, zheight)
glVertex3f(10 * -cols, 10 * -rows, zheight)
glVertex3f(10 * -cols, 10 * rows, zheight)
glEnd()
glPushMatrix()
glTranslatef(self.mousepos[0] - self.bedsize[0] / 2, self.mousepos[1] - self.bedsize[1] / 2, 0)
glBegin(GL_TRIANGLES)
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(1, 0, 0, 1))
glNormal3f(0, 0, 1)
glVertex3f(2, 2, 0)
glVertex3f(-2, 2, 0)
glVertex3f(-2, -2, 0)
glVertex3f(2, -2, 0)
glVertex3f(2, 2, 0)
glVertex3f(-2, -2, 0)
glEnd()
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.3, 0.7, 0.5, 1))
#glTranslatef(0, 40, 0)
glPopMatrix()
glPushMatrix()
glTranslatef(-100, -100, 0)
glEnable(GL_LINE_SMOOTH)
glEnable(GL_BLEND)
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST)
glLineWidth (1.5)
for i in self.parent.models.values():
glPushMatrix()
glTranslatef(*(i.offsets))
glRotatef(i.rot, 0.0, 0.0, 1.0)
glScalef(*i.scale)
#glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, vec(0.93, 0.37, 0.25, 1))
glEnable(GL_COLOR_MATERIAL)
if i.curlayer == -1:
# curlayer == -1 means we are over the top.
glLineWidth (0.8)
[i.gc.layers[j].draw() for j in i.gc.layerlist]
else:
glLineWidth (0.6)
tmpindex = i.gc.layerlist.index(i.curlayer)
if tmpindex >= 5:
thin_layer = i.gc.layerlist[tmpindex - 5]
[i.gc.layers[j].draw() for j in i.gc.layerlist if j <= thin_layer]
if tmpindex > 4:
glLineWidth (0.9)
i.gc.layers[i.gc.layerlist[tmpindex - 4]].draw()
if tmpindex > 3:
glLineWidth (1.1)
i.gc.layers[i.gc.layerlist[tmpindex - 3]].draw()
if tmpindex > 2:
glLineWidth (1.3)
i.gc.layers[i.gc.layerlist[tmpindex - 2]].draw()
if tmpindex > 1:
glLineWidth (2.2)
i.gc.layers[i.gc.layerlist[tmpindex - 1]].draw()
glLineWidth (3.5)
i.gc.layers[i.curlayer].draw()
glLineWidth (1.5)
glDisable(GL_COLOR_MATERIAL)
glPopMatrix() def addfile(self, gcode = None):
glPopMatrix() self.model = actors.GcodeModel()
#print "drawn batch" if gcode:
self.model.load_data(gcode)
self.objects[-1].model = self.model
wx.CallAfter(self.Refresh)
def clear(self):
self.model = None
self.objects[-1].model = None
wx.CallAfter(self.Refresh)
class GCFrame(wx.Frame): class GcodeViewFrame(wx.Frame):
'''A simple class for using OpenGL with wxPython.''' '''A simple class for using OpenGL with wxPython.'''
def __init__(self, parent, ID, title, pos = wx.DefaultPosition, def __init__(self, parent, ID, title, build_dimensions, objects = None,
size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE): pos = wx.DefaultPosition, size = wx.DefaultSize,
super(GCFrame, self).__init__(parent, ID, title, pos, (size[0] + 150, size[1]), style) style = wx.DEFAULT_FRAME_STYLE):
super(GcodeViewFrame, self).__init__(parent, ID, title, pos, size, style)
class d: self.refresh_timer = wx.CallLater(100, self.Refresh)
def GetSelection(self): self.p = self # Hack for backwards compatibility with gviz API
return wx.NOT_FOUND self.clonefrom = objects
self.p = self self.platform = actors.Platform(build_dimensions)
m = d() if objects:
m.offsets = [0, 0, 0] self.model = objects[1].model
m.rot = 0
m.curlayer = -1
m.scale = [1.0, 1.0, 1.0]
m.batch = pyglet.graphics.Batch()
m.gc = gcview([], batch = m.batch)
self.models = {"GCODE": m}
self.l = d()
self.modelindex = 0
self.GLPanel1 = TestGlPanel(self, size)
def addfile(self, gcode = []):
self.models["GCODE"].gc.delete()
self.models["GCODE"].gc = gcview(gcode, batch = self.models["GCODE"].batch)
self.setlayerindex(None)
def clear(self):
self.models["GCODE"].gc.delete()
self.models["GCODE"].gc = gcview([], batch = self.models["GCODE"].batch)
def Show(self, arg = True):
wx.Frame.Show(self, arg)
self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] + 1))
self.SetClientSize((self.GetClientSize()[0], self.GetClientSize()[1] - 1))
self.Refresh()
wx.FutureCall(500, self.GLPanel1.forceresize)
#threading.Thread(target = self.update).start()
#self.initialized = 0
def setlayerindex(self, z):
m = self.models["GCODE"]
try:
mlk = m.gc.layerlist
except:
mlk = []
if z is None:
self.modelindex = -1
elif z > 0:
if self.modelindex < len(mlk) - 1:
if self.modelindex > -1:
self.modelindex += 1
else: else:
self.modelindex = -1 self.model = None
elif z < 0: self.objects = [GCObject(self.platform), GCObject(None)]
if self.modelindex > 0: self.glpanel = GcodeViewPanel(self)
self.modelindex -= 1
elif self.modelindex == -1: def set_current_gline(self, gline):
self.modelindex = len(mlk) if gline.is_move and self.model and self.model.loaded:
self.model.printed_until = gline.gcview_end_vertex
if self.modelindex >= 0: if not self.refresh_timer.IsRunning():
m.curlayer = mlk[self.modelindex] self.refresh_timer.Start()
wx.CallAfter(self.SetTitle, "Gcode view, shift to move. Layer %d/%d, Z = %f" % (self.modelindex, len(mlk), m.curlayer))
def addfile(self, gcode = None):
if self.clonefrom:
self.model = self.clonefrom[-1].model.copy()
else: else:
m.curlayer = -1 self.model = actors.GcodeModel()
wx.CallAfter(self.SetTitle, "Gcode view, shift to move view, mousewheel to set layer") if gcode:
self.model.load_data(gcode)
self.objects[-1].model = self.model
wx.CallAfter(self.Refresh)
def clear(self):
self.model = None
self.objects[-1].model = None
wx.CallAfter(self.Refresh)
def main(): if __name__ == "__main__":
app = wx.App(redirect = False)
frame = GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (400, 400))
import sys import sys
for filename in sys.argv: app = wx.App(redirect = False)
if ".gcode" in filename: build_dimensions = [200, 200, 100, 0, 0, 0]
frame.addfile(list(open(filename))) frame = GcodeViewFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (400, 400), build_dimensions = build_dimensions)
elif ".stl" in filename: gcode = gcoder.GCode(open(sys.argv[1]))
#TODO: add stl here frame.addfile(gcode)
pass
first_move = None
for i in range(len(gcode.lines)):
if gcode.lines[i].is_move:
first_move = gcode.lines[i]
break
last_move = None
for i in range(len(gcode.lines)-1,-1,-1):
if gcode.lines[i].is_move:
last_move = gcode.lines[i]
break
nsteps = 20
steptime = 500
lines = [first_move] + [gcode.lines[int(float(i)*(len(gcode.lines)-1)/nsteps)] for i in range(1, nsteps)] + [last_move]
current_line = 0
def setLine():
global current_line
frame.set_current_gline(lines[current_line])
current_line = (current_line + 1) % len(lines)
timer.Start()
timer = wx.CallLater(steptime, setLine)
timer.Start()
#frame = wx.Frame(None, -1, "GL Window", size = (400, 400))
#panel = TestGlPanel(frame, size = (300, 300))
frame.Show(True) frame.Show(True)
app.MainLoop() app.MainLoop()
app.Destroy() app.Destroy()
if __name__ == "__main__":
#import cProfile
#print cProfile.run("main()")
main()
...@@ -22,15 +22,12 @@ from bufferedcanvas import * ...@@ -22,15 +22,12 @@ from bufferedcanvas import *
class Graph(BufferedCanvas): class Graph(BufferedCanvas):
'''A class to show a Graph with Pronterface.''' '''A class to show a Graph with Pronterface.'''
def __init__(self, parent, id, pos = wx.DefaultPosition, def __init__(self, parent, id, root, pos = wx.DefaultPosition,
size = wx.DefaultSize, style = 0): size = wx.Size(150, 80), style = 0):
# Forcing a no full repaint to stop flickering # Forcing a no full repaint to stop flickering
style = style | wx.NO_FULL_REPAINT_ON_RESIZE style = style | wx.NO_FULL_REPAINT_ON_RESIZE
#call super function super(Graph, self).__init__(parent, id, pos, size, style)
#super(Graph, self).__init__(parent, id, pos, size, style) self.root = root
BufferedCanvas.__init__(self, parent, id)
self.SetSize(wx.Size(150, 80))
self.extruder0temps = [0] self.extruder0temps = [0]
self.extruder0targettemps = [0] self.extruder0targettemps = [0]
...@@ -51,18 +48,10 @@ class Graph(BufferedCanvas): ...@@ -51,18 +48,10 @@ class Graph(BufferedCanvas):
self._lastyvalue = 0 self._lastyvalue = 0
#self.sizer = wx.BoxSizer(wx.HORIZONTAL)
#self.sizer.Add(wx.Button(self, -1, "Button1", (0, 0)))
#self.SetSizer(self.sizer)
def OnPaint(self, evt): def OnPaint(self, evt):
dc = wx.PaintDC(self) dc = wx.PaintDC(self)
gc = wx.GraphicsContext.Create(dc) gc = wx.GraphicsContext.Create(dc)
def Destroy(self):
#call the super method
super(wx.Panel, self).Destroy()
def updateTemperatures(self, event): def updateTemperatures(self, event):
self.AddBedTemperature(self.bedtemps[-1]) self.AddBedTemperature(self.bedtemps[-1])
self.AddBedTargetTemperature(self.bedtargettemps[-1]) self.AddBedTargetTemperature(self.bedtargettemps[-1])
...@@ -80,9 +69,6 @@ class Graph(BufferedCanvas): ...@@ -80,9 +69,6 @@ class Graph(BufferedCanvas):
#b = gc.CreateLinearGradientBrush(0, 0, w, h, col1, col2) #b = gc.CreateLinearGradientBrush(0, 0, w, h, col1, col2)
gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 0), 4)) gc.SetPen(wx.Pen(wx.Colour(255, 0, 0, 0), 4))
#gc.SetBrush(gc.CreateBrush(wx.Brush(wx.Colour(245, 245, 255, 252))))
#gc.SetBrush(b)
gc.DrawRectangle(0, 0, self.width, self.height)
#gc.SetBrush(wx.Brush(wx.Colour(245, 245, 255, 52))) #gc.SetBrush(wx.Brush(wx.Colour(245, 245, 255, 52)))
...@@ -240,6 +226,7 @@ class Graph(BufferedCanvas): ...@@ -240,6 +226,7 @@ class Graph(BufferedCanvas):
self.Refresh() self.Refresh()
def draw(self, dc, w, h): def draw(self, dc, w, h):
dc.SetBackground(wx.Brush(self.root.settings.bgcolor))
dc.Clear() dc.Clear()
gc = wx.GraphicsContext.Create(dc) gc = wx.GraphicsContext.Create(dc)
self.width = w self.width = w
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>. # along with Printrun. If not, see <http://www.gnu.org/licenses/>.
import traceback
try: try:
import wx import wx
except: except:
...@@ -26,6 +28,7 @@ from printrun import gviz ...@@ -26,6 +28,7 @@ from printrun import gviz
from printrun.xybuttons import XYButtons from printrun.xybuttons import XYButtons
from printrun.zbuttons import ZButtons from printrun.zbuttons import ZButtons
from printrun.graph import Graph from printrun.graph import Graph
from printrun.pronterface_widgets import TempGauge
def make_button(parent, label, callback, tooltip, container = None, size = wx.DefaultSize, style = 0): def make_button(parent, label, callback, tooltip, container = None, size = wx.DefaultSize, style = 0):
button = wx.Button(parent, -1, label, style = style, size = size) button = wx.Button(parent, -1, label, style = style, size = size)
...@@ -45,7 +48,7 @@ class XYZControlsSizer(wx.GridBagSizer): ...@@ -45,7 +48,7 @@ class XYZControlsSizer(wx.GridBagSizer):
def __init__(self, root): def __init__(self, root):
super(XYZControlsSizer, self).__init__() super(XYZControlsSizer, self).__init__()
root.xyb = XYButtons(root.panel, root.moveXY, root.homeButtonClicked, root.spacebarAction, root.settings.bgcolor) root.xyb = XYButtons(root.panel, root.moveXY, root.homeButtonClicked, root.spacebarAction, root.settings.bgcolor, zcallback=root.moveZ)
self.Add(root.xyb, pos = (0, 1), flag = wx.ALIGN_CENTER) self.Add(root.xyb, pos = (0, 1), flag = wx.ALIGN_CENTER)
root.zb = ZButtons(root.panel, root.moveZ, root.settings.bgcolor) root.zb = ZButtons(root.panel, root.moveZ, root.settings.bgcolor)
self.Add(root.zb, pos = (0, 2), flag = wx.ALIGN_CENTER) self.Add(root.zb, pos = (0, 2), flag = wx.ALIGN_CENTER)
...@@ -56,12 +59,12 @@ class LeftPane(wx.GridBagSizer): ...@@ -56,12 +59,12 @@ class LeftPane(wx.GridBagSizer):
def __init__(self, root): def __init__(self, root):
super(LeftPane, self).__init__() super(LeftPane, self).__init__()
llts = wx.BoxSizer(wx.HORIZONTAL) llts = wx.BoxSizer(wx.HORIZONTAL)
self.Add(llts, pos = (0, 0), span = (1, 9)) self.Add(llts, pos = (0, 0), span = (1, 6))
self.xyzsizer = XYZControlsSizer(root) self.xyzsizer = XYZControlsSizer(root)
self.Add(self.xyzsizer, pos = (1, 0), span = (1, 8), flag = wx.ALIGN_CENTER) self.Add(self.xyzsizer, pos = (1, 0), span = (1, 6), flag = wx.ALIGN_CENTER)
for i in root.cpbuttons: for i in root.cpbuttons:
btn = make_button(root.panel, i.label, root.procbutton, i.tooltip, style = wx.BU_EXACTFIT) btn = make_button(root.panel, i.label, root.procbutton, i.tooltip)
btn.SetBackgroundColour(i.background) btn.SetBackgroundColour(i.background)
btn.SetForegroundColour("black") btn.SetForegroundColour("black")
btn.properties = i btn.properties = i
...@@ -71,7 +74,7 @@ class LeftPane(wx.GridBagSizer): ...@@ -71,7 +74,7 @@ class LeftPane(wx.GridBagSizer):
if i.span == 0: if i.span == 0:
llts.Add(btn) llts.Add(btn)
else: else:
self.Add(btn, pos = i.pos, span = i.span) self.Add(btn, pos = i.pos, span = i.span, flag = wx.EXPAND)
root.xyfeedc = wx.SpinCtrl(root.panel,-1, str(root.settings.xy_feedrate), min = 0, max = 50000, size = (70,-1)) root.xyfeedc = wx.SpinCtrl(root.panel,-1, str(root.settings.xy_feedrate), min = 0, max = 50000, size = (70,-1))
root.xyfeedc.SetToolTip(wx.ToolTip("Set Maximum Speed for X & Y axes (mm/min)")) root.xyfeedc.SetToolTip(wx.ToolTip("Set Maximum Speed for X & Y axes (mm/min)"))
...@@ -83,8 +86,9 @@ class LeftPane(wx.GridBagSizer): ...@@ -83,8 +86,9 @@ class LeftPane(wx.GridBagSizer):
llts.Add(root.zfeedc,) llts.Add(root.zfeedc,)
root.monitorbox = wx.CheckBox(root.panel,-1, _("Watch")) root.monitorbox = wx.CheckBox(root.panel,-1, _("Watch"))
root.monitorbox.SetValue(bool(root.settings.monitor))
root.monitorbox.SetToolTip(wx.ToolTip("Monitor Temperatures in Graph")) root.monitorbox.SetToolTip(wx.ToolTip("Monitor Temperatures in Graph"))
self.Add(root.monitorbox, pos = (2, 6)) self.Add(root.monitorbox, pos = (3, 5))
root.monitorbox.Bind(wx.EVT_CHECKBOX, root.setmonitor) root.monitorbox.Bind(wx.EVT_CHECKBOX, root.setmonitor)
self.Add(wx.StaticText(root.panel,-1, _("Heat:")), pos = (2, 0), span = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT) self.Add(wx.StaticText(root.panel,-1, _("Heat:")), pos = (2, 0), span = (1, 1), flag = wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT)
...@@ -97,7 +101,7 @@ class LeftPane(wx.GridBagSizer): ...@@ -97,7 +101,7 @@ class LeftPane(wx.GridBagSizer):
if root.settings.last_temperature not in map(float, root.temps.values()): if root.settings.last_temperature not in map(float, root.temps.values()):
htemp_choices = [str(root.settings.last_temperature)] + htemp_choices htemp_choices = [str(root.settings.last_temperature)] + htemp_choices
root.htemp = wx.ComboBox(root.panel, -1, root.htemp = wx.ComboBox(root.panel, -1,
choices = htemp_choices, style = wx.CB_DROPDOWN, size = (70,-1)) choices = htemp_choices, style = wx.CB_DROPDOWN, size = (80,-1))
root.htemp.SetToolTip(wx.ToolTip("Select Temperature for Hotend")) root.htemp.SetToolTip(wx.ToolTip("Select Temperature for Hotend"))
root.htemp.Bind(wx.EVT_COMBOBOX, root.htemp_change) root.htemp.Bind(wx.EVT_COMBOBOX, root.htemp_change)
...@@ -116,7 +120,7 @@ class LeftPane(wx.GridBagSizer): ...@@ -116,7 +120,7 @@ class LeftPane(wx.GridBagSizer):
if root.settings.last_bed_temperature not in map(float, root.bedtemps.values()): if root.settings.last_bed_temperature not in map(float, root.bedtemps.values()):
btemp_choices = [str(root.settings.last_bed_temperature)] + btemp_choices btemp_choices = [str(root.settings.last_bed_temperature)] + btemp_choices
root.btemp = wx.ComboBox(root.panel, -1, root.btemp = wx.ComboBox(root.panel, -1,
choices = btemp_choices, style = wx.CB_DROPDOWN, size = (70,-1)) choices = btemp_choices, style = wx.CB_DROPDOWN, size = (80,-1))
root.btemp.SetToolTip(wx.ToolTip("Select Temperature for Heated Bed")) root.btemp.SetToolTip(wx.ToolTip("Select Temperature for Heated Bed"))
root.btemp.Bind(wx.EVT_COMBOBOX, root.btemp_change) root.btemp.Bind(wx.EVT_COMBOBOX, root.btemp_change)
self.Add(root.btemp, pos = (3, 2), span = (1, 2)) self.Add(root.btemp, pos = (3, 2), span = (1, 2))
...@@ -146,52 +150,116 @@ class LeftPane(wx.GridBagSizer): ...@@ -146,52 +150,116 @@ class LeftPane(wx.GridBagSizer):
root.tempdisp = wx.StaticText(root.panel,-1, "") root.tempdisp = wx.StaticText(root.panel,-1, "")
root.edist = wx.SpinCtrl(root.panel,-1, "5", min = 0, max = 1000, size = (60,-1)) root.edist = wx.SpinCtrl(root.panel,-1, "5", min = 0, max = 1000, size = (70,-1))
root.edist.SetBackgroundColour((225, 200, 200)) root.edist.SetBackgroundColour((225, 200, 200))
root.edist.SetForegroundColour("black") root.edist.SetForegroundColour("black")
self.Add(root.edist, pos = (4, 2), span = (1, 2)) self.Add(root.edist, pos = (4, 2), span = (1, 2), flag = wx.EXPAND | wx.RIGHT, border = 10)
self.Add(wx.StaticText(root.panel,-1, _("mm")), pos = (4, 4), span = (1, 1)) self.Add(wx.StaticText(root.panel,-1, _("mm")), pos = (4, 4), span = (1, 1))
root.edist.SetToolTip(wx.ToolTip("Amount to Extrude or Retract (mm)")) root.edist.SetToolTip(wx.ToolTip("Amount to Extrude or Retract (mm)"))
root.efeedc = wx.SpinCtrl(root.panel,-1, str(root.settings.e_feedrate), min = 0, max = 50000, size = (60,-1)) root.efeedc = wx.SpinCtrl(root.panel,-1, str(root.settings.e_feedrate), min = 0, max = 50000, size = (70,-1))
root.efeedc.SetToolTip(wx.ToolTip("Extrude / Retract speed (mm/min)")) root.efeedc.SetToolTip(wx.ToolTip("Extrude / Retract speed (mm/min)"))
root.efeedc.SetBackgroundColour((225, 200, 200)) root.efeedc.SetBackgroundColour((225, 200, 200))
root.efeedc.SetForegroundColour("black") root.efeedc.SetForegroundColour("black")
root.efeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds) root.efeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds)
self.Add(root.efeedc, pos = (5, 2), span = (1, 2)) root.efeedc.Bind(wx.EVT_TEXT, root.setfeeds)
self.Add(wx.StaticText(root.panel,-1, _("mm/\nmin")), pos = (5, 4), span = (1, 1)) self.Add(root.efeedc, pos = (5, 2), span = (1, 2), flag = wx.EXPAND | wx.RIGHT, border = 10)
self.Add(wx.StaticText(root.panel,-1, _("mm/\nmin")), pos = (5, 4), span = (2, 1))
root.xyfeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds) root.xyfeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds)
root.zfeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds) root.zfeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds)
root.xyfeedc.Bind(wx.EVT_TEXT, root.setfeeds)
root.zfeedc.Bind(wx.EVT_TEXT, root.setfeeds)
root.zfeedc.SetBackgroundColour((180, 255, 180)) root.zfeedc.SetBackgroundColour((180, 255, 180))
root.zfeedc.SetForegroundColour("black") root.zfeedc.SetForegroundColour("black")
root.graph = Graph(root.panel, wx.ID_ANY) if root.display_gauges:
self.Add(root.graph, pos = (3, 5), span = (3, 3)) root.hottgauge = TempGauge(root.panel, size = (-1, 24), title = _("Heater:"), maxval = 300)
self.Add(root.tempdisp, pos = (6, 0), span = (1, 9)) self.Add(root.hottgauge, pos = (7, 0), span = (1, 6), flag = wx.EXPAND)
root.bedtgauge = TempGauge(root.panel, size = (-1, 24), title = _("Bed:"), maxval = 150)
self.Add(root.bedtgauge, pos = (8, 0), span = (1, 6), flag = wx.EXPAND)
def hotendgauge_scroll_setpoint(e):
rot = e.GetWheelRotation()
if rot > 0:
root.do_settemp(str(root.hsetpoint + 1))
elif rot < 0:
root.do_settemp(str(max(0, root.hsetpoint - 1)))
def bedgauge_scroll_setpoint(e):
rot = e.GetWheelRotation()
if rot > 0:
root.do_settemp(str(root.bsetpoint + 1))
elif rot < 0:
root.do_settemp(str(max(0, root.bsetpoint - 1)))
root.hottgauge.Bind(wx.EVT_MOUSEWHEEL, hotendgauge_scroll_setpoint)
root.bedtgauge.Bind(wx.EVT_MOUSEWHEEL, bedgauge_scroll_setpoint)
self.Add(root.tempdisp, pos = (9, 0), span = (1, 6))
else:
self.Add(root.tempdisp, pos = (7, 0), span = (1, 6))
root.graph = Graph(root.panel, wx.ID_ANY, root)
self.Add(root.graph, pos = (4, 5), span = (3, 1))
class NoViz(object):
showall = False
def clear(self, *a):
pass
def addfile(self, *a, **kw):
pass
def addgcode(self, *a, **kw):
pass
def Refresh(self, *a):
pass
def setlayer(self, *a):
pass
class VizPane(wx.BoxSizer): class VizPane(wx.BoxSizer):
def __init__(self, root): def __init__(self, root):
super(VizPane, self).__init__(wx.VERTICAL) super(VizPane, self).__init__(wx.VERTICAL)
if root.settings.mainviz == "None":
root.gviz = NoViz()
use2dview = root.settings.mainviz == "2D"
if root.settings.mainviz == "3D":
try:
import printrun.gcview
root.gviz = printrun.gcview.GcodeViewMainWrapper(root.panel, root.build_dimensions_list)
root.gviz.clickcb = root.showwin
except:
use2dview = True
print "3D view mode requested, but we failed to initialize it."
print "Falling back to 2D view, and here is the backtrace:"
traceback.print_exc()
if use2dview:
root.gviz = gviz.gviz(root.panel, (300, 300), root.gviz = gviz.gviz(root.panel, (300, 300),
build_dimensions = root.build_dimensions_list, build_dimensions = root.build_dimensions_list,
grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2), grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2),
extrusion_width = root.settings.preview_extrusion_width) extrusion_width = root.settings.preview_extrusion_width)
root.gviz.SetToolTip(wx.ToolTip("Click to examine / edit\n layers of loaded file")) root.gviz.SetToolTip(wx.ToolTip("Click to examine / edit\n layers of loaded file"))
root.gviz.showall = 1 root.gviz.showall = 1
root.gviz.Bind(wx.EVT_LEFT_DOWN, root.showwin)
use3dview = root.settings.viz3d
if use3dview:
try: try:
raise "" import printrun.gcview
import printrun.stlview objects = None
root.gwindow = printrun.stlview.GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (600, 600)) if isinstance(root.gviz, printrun.gcview.GcodeViewMainWrapper):
objects = root.gviz.objects
root.gwindow = printrun.gcview.GcodeViewFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (600, 600), build_dimensions = root.build_dimensions_list, objects = objects)
except: except:
use3dview = False
print "3D view mode requested, but we failed to initialize it."
print "Falling back to 2D view, and here is the backtrace:"
traceback.print_exc()
if not use3dview:
root.gwindow = gviz.window([], root.gwindow = gviz.window([],
build_dimensions = root.build_dimensions_list, build_dimensions = root.build_dimensions_list,
grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2), grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2),
extrusion_width = root.settings.preview_extrusion_width) extrusion_width = root.settings.preview_extrusion_width)
root.gviz.Bind(wx.EVT_LEFT_DOWN, root.showwin) root.gwindow.Bind(wx.EVT_CLOSE, lambda x: root.gwindow.Hide())
root.gwindow.Bind(wx.EVT_CLOSE, lambda x:root.gwindow.Hide()) if not isinstance(root.gviz, NoViz):
self.Add(root.gviz, 1, flag = wx.SHAPED) self.Add(root.gviz.widget, 1, flag = wx.SHAPED)
cs = root.centersizer = wx.GridBagSizer() root.centersizer = wx.GridBagSizer()
self.Add(cs, 0, flag = wx.EXPAND) self.Add(root.centersizer, 0, flag = wx.EXPAND)
class LogPane(wx.BoxSizer): class LogPane(wx.BoxSizer):
...@@ -199,6 +267,7 @@ class LogPane(wx.BoxSizer): ...@@ -199,6 +267,7 @@ class LogPane(wx.BoxSizer):
super(LogPane, self).__init__(wx.VERTICAL) super(LogPane, self).__init__(wx.VERTICAL)
root.lowerrsizer = self root.lowerrsizer = self
root.logbox = wx.TextCtrl(root.panel, style = wx.TE_MULTILINE, size = (350,-1)) root.logbox = wx.TextCtrl(root.panel, style = wx.TE_MULTILINE, size = (350,-1))
root.logbox.SetMinSize((100,-1))
root.logbox.SetEditable(0) root.logbox.SetEditable(0)
self.Add(root.logbox, 1, wx.EXPAND) self.Add(root.logbox, 1, wx.EXPAND)
lbrs = wx.BoxSizer(wx.HORIZONTAL) lbrs = wx.BoxSizer(wx.HORIZONTAL)
...@@ -223,7 +292,7 @@ class MainToolbar(wx.BoxSizer): ...@@ -223,7 +292,7 @@ class MainToolbar(wx.BoxSizer):
root.serialport = wx.ComboBox(root.panel, -1, root.serialport = wx.ComboBox(root.panel, -1,
choices = root.scanserial(), choices = root.scanserial(),
style = wx.CB_DROPDOWN, size = (150, 25)) style = wx.CB_DROPDOWN, size = (-1, 25))
root.serialport.SetToolTip(wx.ToolTip("Select Port Printer is connected to")) root.serialport.SetToolTip(wx.ToolTip("Select Port Printer is connected to"))
root.rescanports() root.rescanports()
self.Add(root.serialport) self.Add(root.serialport)
...@@ -263,10 +332,10 @@ class MainWindow(wx.Frame): ...@@ -263,10 +332,10 @@ class MainWindow(wx.Frame):
self.mainsizer = wx.BoxSizer(wx.VERTICAL) self.mainsizer = wx.BoxSizer(wx.VERTICAL)
self.uppersizer = MainToolbar(self) self.uppersizer = MainToolbar(self)
self.lowersizer = wx.BoxSizer(wx.HORIZONTAL) self.lowersizer = wx.BoxSizer(wx.HORIZONTAL)
self.lowersizer.Add(LeftPane(self)) self.lowersizer.Add(LeftPane(self), 0)
self.lowersizer.Add(VizPane(self), 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL) self.lowersizer.Add(VizPane(self), 1, wx.EXPAND|wx.ALIGN_CENTER_HORIZONTAL)
self.lowersizer.Add(LogPane(self), 0, wx.EXPAND) self.lowersizer.Add(LogPane(self), 1, wx.EXPAND)
self.mainsizer.Add(self.uppersizer) self.mainsizer.Add(self.uppersizer, 0)
self.mainsizer.Add(self.lowersizer, 1, wx.EXPAND) self.mainsizer.Add(self.lowersizer, 1, wx.EXPAND)
self.panel.SetSizer(self.mainsizer) self.panel.SetSizer(self.mainsizer)
self.status = self.CreateStatusBar() self.status = self.CreateStatusBar()
...@@ -276,6 +345,12 @@ class MainWindow(wx.Frame): ...@@ -276,6 +345,12 @@ class MainWindow(wx.Frame):
self.mainsizer.Layout() self.mainsizer.Layout()
self.mainsizer.Fit(self) self.mainsizer.Fit(self)
# This prevents resizing below a reasonnable value
# We sum the lowersizer (left pane / viz / log) min size
# the toolbar height and the statusbar/menubar sizes
minsize = self.lowersizer.GetMinSize() # lower pane
minsize[1] += self.uppersizer.GetMinSize()[1] # toolbar height
self.SetMinSize(self.ClientToWindowSize(minsize)) # client to window
# disable all printer controls until we connect to a printer # disable all printer controls until we connect to a printer
self.pausebtn.Disable() self.pausebtn.Disable()
......
...@@ -12,7 +12,11 @@ ...@@ -12,7 +12,11 @@
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>. # along with Printrun. If not, see <http://www.gnu.org/licenses/>.
from Queue import Queue
from collections import deque
import wx, time import wx, time
from printrun import gcoder
from printrun_utils import imagefile from printrun_utils import imagefile
...@@ -20,64 +24,51 @@ ID_ABOUT = 101 ...@@ -20,64 +24,51 @@ ID_ABOUT = 101
ID_EXIT = 110 ID_EXIT = 110
class window(wx.Frame): class window(wx.Frame):
def __init__(self, f, size = (600, 600), build_dimensions = [200, 200, 100, 0, 0, 0], grid = (10, 50), extrusion_width = 0.5): def __init__(self, f, size = (600, 600), build_dimensions = [200, 200, 100, 0, 0, 0], grid = (10, 50), extrusion_width = 0.5):
wx.Frame.__init__(self, None, title = "Gcode view, shift to move view, mousewheel to set layer", size = (size[0], size[1])) wx.Frame.__init__(self, None, title = "Gcode view, shift to move view, mousewheel to set layer", size = size)
self.p = gviz(self, size = size, build_dimensions = build_dimensions, grid = grid, extrusion_width = extrusion_width)
self.CreateStatusBar(1);
self.SetStatusText("Layer number and Z position show here when you scroll");
panel = wx.Panel(self, -1)
self.p = gviz(panel, size = size, build_dimensions = build_dimensions, grid = grid, extrusion_width = extrusion_width, realparent = self)
vbox = wx.BoxSizer(wx.VERTICAL) vbox = wx.BoxSizer(wx.VERTICAL)
toolbar = wx.ToolBar(self, -1, style = wx.TB_HORIZONTAL | wx.NO_BORDER) toolbar = wx.ToolBar(panel, -1, style = wx.TB_HORIZONTAL | wx.NO_BORDER)
toolbar.AddSimpleTool(1, wx.Image(imagefile('zoom_in.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom In [+]', '') toolbar.AddSimpleTool(1, wx.Image(imagefile('zoom_in.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom In [+]', '')
toolbar.AddSimpleTool(2, wx.Image(imagefile('zoom_out.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom Out [-]', '') toolbar.AddSimpleTool(2, wx.Image(imagefile('zoom_out.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom Out [-]', '')
toolbar.AddSeparator() toolbar.AddSeparator()
toolbar.AddSimpleTool(3, wx.Image(imagefile('arrow_up.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Up a Layer [U]', '') toolbar.AddSimpleTool(3, wx.Image(imagefile('arrow_up.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Up a Layer [U]', '')
toolbar.AddSimpleTool(4, wx.Image(imagefile('arrow_down.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Down a Layer [D]', '') toolbar.AddSimpleTool(4, wx.Image(imagefile('arrow_down.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Move Down a Layer [D]', '')
toolbar.AddSimpleTool(5, wx.EmptyBitmap(16, 16), 'Reset view', '') toolbar.AddSimpleTool(5, wx.Image(imagefile('reset.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Reset view', '')
toolbar.AddSeparator() toolbar.AddSeparator()
#toolbar.AddSimpleTool(5, wx.Image('./images/inject.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Insert Code at start of this layer', '') #toolbar.AddSimpleTool(6, wx.Image(imagefile('inject.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Insert Code at start of this layer', '')
toolbar.Realize() toolbar.Realize()
vbox.Add(toolbar, 0, border = 5) vbox.Add(toolbar, 0, border = 5)
self.SetSizer(vbox) vbox.Add(self.p, 1, wx.EXPAND)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200, 200, 1.2), id = 1) panel.SetSizer(vbox)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200, 200, 1/1.2), id = 2) self.SetMinSize(self.ClientToWindowSize(vbox.GetMinSize()))
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(-1, -1, 1.2), id = 1)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(-1, -1, 1/1.2), id = 2)
self.Bind(wx.EVT_TOOL, lambda x:self.p.layerup(), id = 3) self.Bind(wx.EVT_TOOL, lambda x:self.p.layerup(), id = 3)
self.Bind(wx.EVT_TOOL, lambda x:self.p.layerdown(), id = 4) self.Bind(wx.EVT_TOOL, lambda x:self.p.layerdown(), id = 4)
self.Bind(wx.EVT_TOOL, self.resetview, id = 5) self.Bind(wx.EVT_TOOL, self.resetview, id = 5)
#self.Bind(wx.EVT_TOOL, lambda x:self.p.inject(), id = 5) #self.Bind(wx.EVT_TOOL, lambda x:self.p.inject(), id = 6)
self.CreateStatusBar(1);
self.SetStatusText("Layer number and Z position show here when you scroll");
#self.bu = wx.Button(self.p,-1, "U", pos = (0, 100), size = (40, 140))
#self.bd = wx.Button(self.p,-1, "D", pos = (0, 140), size = (40, 140))
#self.bi = wx.Button(self.p,-1, "+", pos = (40, 100), size = (40, 140))
#self.bo = wx.Button(self.p,-1, "-", pos = (40, 140), size = (40, 140))
#self.bs = wx.Button(self.p, -1, "Inject", pos = (85, 103), size = (50, 20))
#self.bu.SetToolTip(wx.ToolTip("Move up one layer"))
#self.bd.SetToolTip(wx.ToolTip("Move down one layer"))
#self.bi.SetToolTip(wx.ToolTip("Zoom view in"))
#self.bo.SetToolTip(wx.ToolTip("Zoom view out"))
#self.bs.SetToolTip(wx.ToolTip("Insert Code at start of this layer"))
#self.bu.Bind(wx.EVT_BUTTON, lambda x:self.p.layerup())
#self.bd.Bind(wx.EVT_BUTTON, lambda x:self.p.layerdown())
#self.bi.Bind(wx.EVT_BUTTON, lambda x:self.p.zoom(200, 200, 1.2))
#self.bo.Bind(wx.EVT_BUTTON, lambda x:self.p.zoom(200, 200, 1/1.2))
#self.bs.Bind(wx.EVT_BUTTON, lambda x:self.p.inject())
s = time.time()
#print time.time()-s
self.initpos = [0, 0] self.initpos = [0, 0]
self.p.Bind(wx.EVT_KEY_DOWN, self.key) self.p.Bind(wx.EVT_KEY_DOWN, self.key)
#self.bu.Bind(wx.EVT_KEY_DOWN, self.key)
#self.bd.Bind(wx.EVT_KEY_DOWN, self.key)
#self.bi.Bind(wx.EVT_KEY_DOWN, self.key)
#self.bo.Bind(wx.EVT_KEY_DOWN, self.key)
self.Bind(wx.EVT_KEY_DOWN, self.key) self.Bind(wx.EVT_KEY_DOWN, self.key)
self.p.Bind(wx.EVT_MOUSEWHEEL, self.zoom) self.p.Bind(wx.EVT_MOUSEWHEEL, self.zoom)
self.Bind(wx.EVT_MOUSEWHEEL, self.zoom) self.Bind(wx.EVT_MOUSEWHEEL, self.zoom)
self.p.Bind(wx.EVT_MOUSE_EVENTS, self.mouse) self.p.Bind(wx.EVT_MOUSE_EVENTS, self.mouse)
self.Bind(wx.EVT_MOUSE_EVENTS, self.mouse) self.Bind(wx.EVT_MOUSE_EVENTS, self.mouse)
if f:
gcode = gcoder.GCode(f)
self.p.addfile(gcode)
def set_current_gline(self, gline):
return
def resetview(self, event): def resetview(self, event):
self.p.translate = [0.0, 0.0] self.p.translate = [0.0, 0.0]
self.p.scale = self.p.basescale self.p.scale = self.p.basescale
...@@ -85,19 +76,17 @@ class window(wx.Frame): ...@@ -85,19 +76,17 @@ class window(wx.Frame):
def mouse(self, event): def mouse(self, event):
if event.ButtonUp(wx.MOUSE_BTN_LEFT): if event.ButtonUp(wx.MOUSE_BTN_LEFT):
if(self.initpos is not None): if self.initpos is not None:
self.initpos = None self.initpos = None
elif event.Dragging(): elif event.Dragging():
e = event.GetPositionTuple() e = event.GetPositionTuple()
if self.initpos is None or not hasattr(self, "basetrans"): if self.initpos is None or not hasattr(self, "basetrans"):
self.initpos = e self.initpos = e
self.basetrans = self.p.translate self.basetrans = self.p.translate
#print self.p.translate, e, self.initpos self.p.translate = [self.basetrans[0] + (e[0] - self.initpos[0]),
self.p.translate = [ self.basetrans[0]+(e[0]-self.initpos[0]), self.basetrans[1] + (e[1] - self.initpos[1])]
self.basetrans[1]+(e[1]-self.initpos[1]) ] self.p.dirty = 1
self.p.repaint() wx.CallAfter(self.p.Refresh)
self.p.Refresh()
else: else:
event.Skip() event.Skip()
...@@ -108,18 +97,7 @@ class window(wx.Frame): ...@@ -108,18 +97,7 @@ class window(wx.Frame):
kzi = [388, 316, 61] # Zoom In Keys kzi = [388, 316, 61] # Zoom In Keys
kzo = [390, 314, 45] # Zoom Out Keys kzo = [390, 314, 45] # Zoom Out Keys
x = event.GetKeyCode() x = event.GetKeyCode()
#print "Key event - "+str(x)
#if event.ShiftDown():
cx, cy = self.p.translate cx, cy = self.p.translate
# if x == wx.WXK_UP:
# self.p.zoom(cx, cy, 1.2)
# if x == wx.WXK_DOWN:
# self.p.zoom(cx, cy, 1/1.2)
#else:
# if x == wx.WXK_UP:
# self.p.layerup()
# if x == wx.WXK_DOWN:
# self.p.layerdown()
if x in kup: if x in kup:
self.p.layerup() self.p.layerup()
if x in kdo: if x in kdo:
...@@ -129,7 +107,6 @@ class window(wx.Frame): ...@@ -129,7 +107,6 @@ class window(wx.Frame):
if x in kzo: if x in kzo:
self.p.zoom(cx, cy, 1/1.2) self.p.zoom(cx, cy, 1/1.2)
#print p.lines.keys()
def zoom(self, event): def zoom(self, event):
z = event.GetWheelRotation() z = event.GetWheelRotation()
if event.ShiftDown(): if event.ShiftDown():
...@@ -140,9 +117,22 @@ class window(wx.Frame): ...@@ -140,9 +117,22 @@ class window(wx.Frame):
elif z < 0: self.p.zoom(event.GetX(), event.GetY(), 1/1.2) elif z < 0: self.p.zoom(event.GetX(), event.GetY(), 1/1.2)
class gviz(wx.Panel): class gviz(wx.Panel):
def __init__(self, parent, size = (200, 200), build_dimensions = [200, 200, 100, 0, 0, 0], grid = (10, 50), extrusion_width = 0.5):
wx.Panel.__init__(self, parent,-1, size = (size[0], size[1])) # Mark canvas as dirty when setting showall
self.parent = parent _showall = 0
def _get_showall(self):
return self._showall
def _set_showall(self, showall):
if showall != self._showall:
self.dirty = 1
self._showall = showall
showall = property(_get_showall, _set_showall)
def __init__(self, parent, size = (200, 200), build_dimensions = [200, 200, 100, 0, 0, 0], grid = (10, 50), extrusion_width = 0.5, realparent = None):
wx.Panel.__init__(self, parent, -1, size = size)
self.widget = self
self.SetMinSize((150, 150))
self.parent = realparent if realparent else parent
self.size = size self.size = size
self.build_dimensions = build_dimensions self.build_dimensions = build_dimensions
self.grid = grid self.grid = grid
...@@ -157,7 +147,7 @@ class gviz(wx.Panel): ...@@ -157,7 +147,7 @@ class gviz(wx.Panel):
self.layers = [] self.layers = []
self.layerindex = 0 self.layerindex = 0
self.filament_width = extrusion_width # set it to 0 to disable scaling lines with zoom self.filament_width = extrusion_width # set it to 0 to disable scaling lines with zoom
self.basescale = [min(float(size[0])/build_dimensions[0], float(size[1])/build_dimensions[1])]*2 self.update_basescale()
self.scale = self.basescale self.scale = self.basescale
penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0)) penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0))
self.translate = [0.0, 0.0] self.translate = [0.0, 0.0]
...@@ -168,8 +158,10 @@ class gviz(wx.Panel): ...@@ -168,8 +158,10 @@ class gviz(wx.Panel):
self.fades = [wx.Pen(wx.Colour(250-0.6**i*100, 250-0.6**i*100, 200-0.4**i*50), penwidth) for i in xrange(6)] self.fades = [wx.Pen(wx.Colour(250-0.6**i*100, 250-0.6**i*100, 200-0.4**i*50), penwidth) for i in xrange(6)]
self.penslist = [self.mainpen, self.travelpen, self.hlpen]+self.fades self.penslist = [self.mainpen, self.travelpen, self.hlpen]+self.fades
self.showall = 0 self.showall = 0
self.hilight = [] self.hilight = deque()
self.hilightarcs = [] self.hilightarcs = deque()
self.hilightqueue = Queue(0)
self.hilightarcsqueue = Queue(0)
self.dirty = 1 self.dirty = 1
self.blitmap = wx.EmptyBitmap(self.GetClientSize()[0], self.GetClientSize()[1],-1) self.blitmap = wx.EmptyBitmap(self.GetClientSize()[0], self.GetClientSize()[1],-1)
...@@ -178,6 +170,14 @@ class gviz(wx.Panel): ...@@ -178,6 +170,14 @@ class gviz(wx.Panel):
print"Inject code here..." print"Inject code here..."
print "Layer "+str(self.layerindex +1)+" - Z = "+str(self.layers[self.layerindex])+" mm" print "Layer "+str(self.layerindex +1)+" - Z = "+str(self.layers[self.layerindex])+" mm"
def clearhilights(self):
self.hilight.clear()
self.hilightarcs.clear()
while not self.hilightqueue.empty():
self.hilightqueue.get_nowait()
while not self.hilightarcsqueue.empty():
self.hilightarcsqueue.get_nowait()
def clear(self): def clear(self):
self.lastpos = [0, 0, 0, 0, 0, 0, 0] self.lastpos = [0, 0, 0, 0, 0, 0, 0]
self.lines = {} self.lines = {}
...@@ -185,47 +185,49 @@ class gviz(wx.Panel): ...@@ -185,47 +185,49 @@ class gviz(wx.Panel):
self.arcs = {} self.arcs = {}
self.arcpens = {} self.arcpens = {}
self.layers = [] self.layers = []
self.hilight = [] self.clearhilights()
self.hilightarcs = []
self.layerindex = 0 self.layerindex = 0
self.showall = 0 self.showall = 0
self.dirty = 1 self.dirty = 1
#self.repaint() wx.CallAfter(self.Refresh)
def layerup(self): def layerup(self):
if(self.layerindex+1<len(self.layers)): if self.layerindex + 1 < len(self.layers):
self.layerindex+=1 self.layerindex += 1
# Display layer info on statusbar (Jezmy) self.parent.SetStatusText("Layer %d - Going Up - Z = %.03f mm" % (self.layerindex + 1, self.layers[self.layerindex]), 0)
self.parent.SetStatusText("Layer "+str(self.layerindex +1)+" - Going Up - Z = "+str(self.layers[self.layerindex])+" mm", 0) self.dirty = 1
self.repaint() wx.CallAfter(self.Refresh)
self.Refresh()
def layerdown(self): def layerdown(self):
if(self.layerindex>0): if self.layerindex > 0:
self.layerindex-=1 self.layerindex -= 1
# Display layer info on statusbar (Jezmy) self.parent.SetStatusText("Layer %d - Going Down - Z = %.03f mm" % (self.layerindex + 1, self.layers[self.layerindex]), 0)
self.parent.SetStatusText("Layer "+str(self.layerindex + 1)+" - Going Down - Z = "+str(self.layers[self.layerindex])+ " mm", 0) self.dirty = 1
self.repaint() wx.CallAfter(self.Refresh)
self.Refresh()
def setlayer(self, layer): def setlayer(self, layer):
try: if layer in self.layers:
self.layerindex = self.layers.index(layer) self.layerindex = self.layers.index(layer)
self.repaint() self.dirty = 1
wx.CallAfter(self.Refresh)
self.showall = 0 self.showall = 0
except: wx.CallAfter(self.Refresh)
pass
def resize(self, event): def update_basescale(self):
size = self.GetClientSize() self.basescale = 2*[min(float(self.size[0] - 1)/self.build_dimensions[0],
size = [max(1.0, size[0]), max(1.0, size[1])] float(self.size[1] - 1)/self.build_dimensions[1])]
self.size = [max(1.0, self.size[0]), max(1.0, self.size[1])]
newsize = min(float(size[0])/self.size[0], float(size[1])/self.size[1])
self.size = self.GetClientSize()
wx.CallAfter(self.zoom, 0, 0, newsize)
def resize(self, event):
oldside = max(1.0, min(self.size))
self.size = self.GetClientSizeTuple()
self.update_basescale()
newside = max(1.0, min(self.size))
zoomratio = float(newside) / oldside
wx.CallLater(200, self.zoom, 0, 0, zoomratio)
def zoom(self, x, y, factor): def zoom(self, x, y, factor):
if x == -1 and y == -1:
side = min(self.size)
x = y = side / 2
self.scale = [s * factor for s in self.scale] self.scale = [s * factor for s in self.scale]
self.translate = [ x - (x-self.translate[0]) * factor, self.translate = [ x - (x-self.translate[0]) * factor,
...@@ -233,12 +235,35 @@ class gviz(wx.Panel): ...@@ -233,12 +235,35 @@ class gviz(wx.Panel):
penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0)) penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0))
for pen in self.penslist: for pen in self.penslist:
pen.SetWidth(penwidth) pen.SetWidth(penwidth)
#self.dirty = 1 self.dirty = 1
self.repaint() wx.CallAfter(self.Refresh)
self.Refresh()
def _line_scaler(self, x):
return (self.scale[0]*x[0]+self.translate[0],
self.scale[1]*x[1]+self.translate[1],
self.scale[0]*x[2]+self.translate[0],
self.scale[1]*x[3]+self.translate[1],)
def repaint(self): def _arc_scaler(self, x):
return (self.scale[0]*x[0]+self.translate[0],
self.scale[1]*x[1]+self.translate[1],
self.scale[0]*x[2]+self.translate[0],
self.scale[1]*x[3]+self.translate[1],
self.scale[0]*x[4]+self.translate[0],
self.scale[1]*x[5]+self.translate[1],)
def _drawlines(self, dc, lines, pens):
scaled_lines = map(self._line_scaler, lines)
dc.DrawLineList(scaled_lines, pens)
def _drawarcs(self, dc, arcs, pens):
scaled_arcs = map(self._arc_scaler, arcs)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
for i in range(len(scaled_arcs)):
dc.SetPen(pens[i] if type(pens) == list else pens)
dc.DrawArc(*scaled_arcs[i])
def repaint_everything(self):
self.blitmap = wx.EmptyBitmap(self.GetClientSize()[0], self.GetClientSize()[1],-1) self.blitmap = wx.EmptyBitmap(self.GetClientSize()[0], self.GetClientSize()[1],-1)
dc = wx.MemoryDC() dc = wx.MemoryDC()
dc.SelectObject(self.blitmap) dc.SelectObject(self.blitmap)
...@@ -252,150 +277,194 @@ class gviz(wx.Panel): ...@@ -252,150 +277,194 @@ class gviz(wx.Panel):
for y in xrange(int(self.build_dimensions[1]/grid_unit)+1): for y in xrange(int(self.build_dimensions[1]/grid_unit)+1):
dc.DrawLine(self.translate[0], self.translate[1]+y*self.scale[1]*grid_unit, self.translate[0]+self.scale[0]*self.build_dimensions[0], self.translate[1]+y*self.scale[1]*grid_unit) dc.DrawLine(self.translate[0], self.translate[1]+y*self.scale[1]*grid_unit, self.translate[0]+self.scale[0]*self.build_dimensions[0], self.translate[1]+y*self.scale[1]*grid_unit)
dc.SetPen(wx.Pen(wx.Colour(0, 0, 0))) dc.SetPen(wx.Pen(wx.Colour(0, 0, 0)))
if not self.showall: if not self.showall:
self.size = self.GetSize()
dc.SetBrush(wx.Brush((43, 144, 255))) dc.SetBrush(wx.Brush((43, 144, 255)))
dc.DrawRectangle(self.size[0]-15, 0, 15, self.size[1]) dc.DrawRectangle(self.size[0]-15, 0, 15, self.size[1])
dc.SetBrush(wx.Brush((0, 255, 0))) dc.SetBrush(wx.Brush((0, 255, 0)))
if len(self.layers): if len(self.layers):
dc.DrawRectangle(self.size[0]-14, (1.0-(1.0*(self.layerindex+1))/len(self.layers))*self.size[1], 13, self.size[1]-1) dc.DrawRectangle(self.size[0]-14, (1.0-(1.0*(self.layerindex+1))/len(self.layers))*self.size[1], 13, self.size[1]-1)
def _drawlines(lines, pens):
def _scaler(x):
return (self.scale[0]*x[0]+self.translate[0],
self.scale[1]*x[1]+self.translate[1],
self.scale[0]*x[2]+self.translate[0],
self.scale[1]*x[3]+self.translate[1],)
scaled_lines = map(_scaler, lines)
dc.DrawLineList(scaled_lines, pens)
def _drawarcs(arcs, pens):
def _scaler(x):
return (self.scale[0]*x[0]+self.translate[0],
self.scale[1]*x[1]+self.translate[1],
self.scale[0]*x[2]+self.translate[0],
self.scale[1]*x[3]+self.translate[1],
self.scale[0]*x[4]+self.translate[0],
self.scale[1]*x[5]+self.translate[1],)
scaled_arcs = map(_scaler, arcs)
for i in range(len(scaled_arcs)):
dc.SetPen(pens[i] if type(pens).__name__ == 'list' else pens)
dc.SetBrush(wx.TRANSPARENT_BRUSH)
dc.DrawArc(*scaled_arcs[i])
if self.showall: if self.showall:
l = [] l = []
for i in self.layers: for i in self.layers:
dc.DrawLineList(l, self.fades[0]) self._drawlines(dc, self.lines[i], self.pens[i])
_drawlines(self.lines[i], self.pens[i]) self._drawarcs(dc, self.arcs[i], self.arcpens[i])
_drawarcs(self.arcs[i], self.arcpens[i])
return return
if self.layerindex<len(self.layers) and self.layers[self.layerindex] in self.lines.keys():
for layer_i in xrange(max(0, self.layerindex-6), self.layerindex):
#print i, self.layerindex, self.layerindex-i
_drawlines(self.lines[self.layers[layer_i]], self.fades[self.layerindex-layer_i-1])
_drawarcs(self.arcs[self.layers[layer_i]], self.fades[self.layerindex-layer_i-1])
_drawlines(self.lines[self.layers[self.layerindex]], self.pens[self.layers[self.layerindex]])
_drawarcs(self.arcs[self.layers[self.layerindex]], self.arcpens[self.layers[self.layerindex]])
_drawlines(self.hilight, self.hlpen) if self.layerindex < len(self.layers) and self.layers[self.layerindex] in self.lines:
_drawarcs(self.hilightarcs, self.hlpen) for layer_i in range(max(0, self.layerindex - 6), self.layerindex):
self._drawlines(dc, self.lines[self.layers[layer_i]], self.fades[self.layerindex - layer_i - 1])
self._drawarcs(dc, self.arcs[self.layers[layer_i]], self.fades[self.layerindex - layer_i - 1])
self._drawlines(dc, self.lines[self.layers[self.layerindex]], self.pens[self.layers[self.layerindex]])
self._drawarcs(dc, self.arcs[self.layers[self.layerindex]], self.arcpens[self.layers[self.layerindex]])
self._drawlines(dc, self.hilight, self.hlpen)
self._drawarcs(dc, self.hilightarcs, self.hlpen)
self.paint_hilights(dc)
dc.SelectObject(wx.NullBitmap) dc.SelectObject(wx.NullBitmap)
def paint_hilights(self, dc = None):
if self.hilightqueue.empty() and self.hilightarcsqueue.empty():
return
hl = []
if not dc:
dc = wx.MemoryDC()
dc.SelectObject(self.blitmap)
while not self.hilightqueue.empty():
hl.append(self.hilightqueue.get_nowait())
self._drawlines(dc, hl, self.hlpen)
hlarcs = []
while not self.hilightarcsqueue.empty():
hlarcs.append(self.hilightarcsqueue.get_nowait())
self._drawarcs(dc, hlarcs, self.hlpen)
def paint(self, event): def paint(self, event):
dc = wx.PaintDC(self) if self.dirty:
if(self.dirty):
self.repaint()
self.dirty = 0 self.dirty = 0
sz = self.GetClientSize() self.repaint_everything()
self.paint_hilights()
dc = wx.PaintDC(self)
dc.DrawBitmap(self.blitmap, 0, 0) dc.DrawBitmap(self.blitmap, 0, 0)
del dc
def addfile(self, gcodes = []): def addfile(self, gcode):
self.clear() self.clear()
for i in gcodes: self.add_parsed_gcodes(gcode.lines)
self.addgcode(i)
# FIXME : there's code duplication going on there, we should factor it (but
# the reason addgcode is not factored as a add_parsed_gcodes([gline]) is
# because when loading a file there's no hilight, so it simply lets us not
# do the if hilight: all the time for nothing when loading a lot of lines
def add_parsed_gcodes(self, lines):
def _y(y):
return self.build_dimensions[1] - (y - self.build_dimensions[4])
def _x(x):
return x - self.build_dimensions[3]
for gline in lines:
if gline.command not in ["G0", "G1", "G2", "G3"]:
continue
target = self.lastpos[:]
target[5] = 0.0
target[6] = 0.0
if gline.relative:
if gline.x != None: target[0] += gline.x
if gline.y != None: target[1] += gline.y
if gline.z != None: target[2] += gline.z
else:
if gline.x != None: target[0] = gline.x
if gline.y != None: target[1] = gline.y
if gline.z != None: target[2] = gline.z
if gline.e != None:
if gline.relative_e:
target[3] += gline.e
else:
target[3] = gline.e
if gline.f != None: target[4] = gline.f
if gline.i != None: target[5] = gline.i
if gline.j != None: target[6] = gline.j
z = target[2]
if z not in self.layers:
self.lines[z] = []
self.pens[z] = []
self.arcs[z] = []
self.arcpens[z] = []
self.layers.append(z)
start_pos = self.lastpos[:]
if gline.command in ["G0", "G1"]:
self.lines[z].append((_x(start_pos[0]), _y(start_pos[1]), _x(target[0]), _y(target[1])))
self.pens[z].append(self.mainpen if target[3] != self.lastpos[3] else self.travelpen)
elif gline.command in ["G2", "G3"]:
# startpos, endpos, arc center
arc = [_x(start_pos[0]), _y(start_pos[1]),
_x(target[0]), _y(target[1]),
_x(start_pos[0] + target[5]), _y(start_pos[1] + target[6])]
if gline.command == "G2": # clockwise, reverse endpoints
arc[0], arc[1], arc[2], arc[3] = arc[2], arc[3], arc[0], arc[1]
self.arcs[z].append(arc)
self.arcpens[z].append(self.arcpen)
self.lastpos = target
self.dirty = 1
self.Refresh()
def addgcode(self, gcode = "M105", hilight = 0): def addgcode(self, gcode = "M105", hilight = 0):
gcode = gcode.split("*")[0] gcode = gcode.split("*")[0]
gcode = gcode.split(";")[0] gcode = gcode.split(";")[0]
gcode = gcode.lower().strip().split() gcode = gcode.lower().strip()
if len(gcode) == 0: if not gcode:
return return
if gcode[0][0] == 'n': gline = gcoder.Line(gcode)
gcode.pop(0) gline.parse_coordinates(False)
def _readgcode():
target = self.lastpos[:]
target[5]=0.0
target[6]=0.0
if hilight:
target = self.hilightpos[:]
for i in gcode:
if i[0]=="x":
target[0]=float(i[1:])
elif i[0]=="y":
target[1]=float(i[1:])
elif i[0]=="z":
target[2]=float(i[1:])
elif i[0]=="e":
target[3]=float(i[1:])
elif i[0]=="f":
target[4]=float(i[1:])
elif i[0]=="i":
target[5]=float(i[1:])
elif i[0]=="j":
target[6]=float(i[1:])
if not hilight:
if not target[2] in self.lines.keys():
self.lines[target[2]]=[]
self.pens[target[2]]=[]
self.arcs[target[2]]=[]
self.arcpens[target[2]]=[]
self.layers+=[target[2]]
return target
def _y(y): def _y(y):
return self.build_dimensions[1]-(y-self.build_dimensions[4]) return self.build_dimensions[1] - (y - self.build_dimensions[4])
def _x(x): def _x(x):
return x-self.build_dimensions[3] return x - self.build_dimensions[3]
if gline.command not in ["G0", "G1", "G2", "G3"]:
return
start_pos = self.hilightpos[:] if hilight else self.lastpos[:] start_pos = self.hilightpos[:] if hilight else self.lastpos[:]
if gcode[0] in [ "g0", "g1" ]: target = start_pos[:]
target = _readgcode() target[5] = 0.0
line = [ _x(start_pos[0]), _y(start_pos[1]), _x(target[0]), _y(target[1]) ] target[6] = 0.0
if gline.x != None: target[0] = gline.x
if gline.y != None: target[1] = gline.y
if gline.z != None: target[2] = gline.z
if gline.e != None: target[3] = gline.e
if gline.f != None: target[4] = gline.f
if gline.i != None: target[5] = gline.i
if gline.j != None: target[6] = gline.j
z = target[2]
if not hilight and z not in self.layers:
self.lines[z] = []
self.pens[z] = []
self.arcs[z] = []
self.arcpens[z] = []
self.layers.append(z)
if gline.command in ["G0", "G1"]:
line = [_x(start_pos[0]), _y(start_pos[1]), _x(target[0]), _y(target[1])]
if not hilight: if not hilight:
self.lines[ target[2] ] += [line] self.lines[z].append((_x(start_pos[0]), _y(start_pos[1]), _x(target[0]), _y(target[1])))
self.pens[ target[2] ] += [self.mainpen if target[3] != self.lastpos[3] else self.travelpen] self.pens[z].append(self.mainpen if target[3] != self.lastpos[3] else self.travelpen)
self.lastpos = target
else: else:
self.hilight += [line] self.hilight.append(line)
self.hilightpos = target self.hilightqueue.put_nowait(line)
self.dirty = 1 elif gline.command in ["G2", "G3"]:
# startpos, endpos, arc center
if gcode[0] in [ "g2", "g3" ]: arc = [_x(start_pos[0]), _y(start_pos[1]),
target = _readgcode() _x(target[0]), _y(target[1]),
arc = [] _x(start_pos[0] + target[5]), _y(start_pos[1] + target[6])]
arc += [ _x(start_pos[0]), _y(start_pos[1]) ] if gline.command == "G2": # clockwise, reverse endpoints
arc += [ _x(target[0]), _y(target[1]) ]
arc += [ _x(start_pos[0] + target[5]), _y(start_pos[1] + target[6]) ] # center
if gcode[0] == "g2": # clockwise, reverse endpoints
arc[0], arc[1], arc[2], arc[3] = arc[2], arc[3], arc[0], arc[1] arc[0], arc[1], arc[2], arc[3] = arc[2], arc[3], arc[0], arc[1]
if not hilight: if not hilight:
self.arcs[ target[2] ] += [arc] self.arcs[z].append(arc)
self.arcpens[ target[2] ] += [self.arcpen] self.arcpens[z].append(self.arcpen)
else:
self.hilightarcs.append(arc)
self.hilightarcsqueue.put_nowait(arc)
if not hilight:
self.lastpos = target self.lastpos = target
self.dirty = 1
else: else:
self.hilightarcs += [arc]
self.hilightpos = target self.hilightpos = target
self.dirty = 1 self.Refresh()
if __name__ == '__main__': if __name__ == '__main__':
import sys
app = wx.App(False) app = wx.App(False)
#main = window(open("/home/kliment/designs/spinner/arm_export.gcode")) main = window(open(sys.argv[1]))
main = window(open("jam.gcode"))
main.Show() main.Show()
app.MainLoop() app.MainLoop()
# -*- coding: utf-8 -*-
# Copyright (C) 2013 Guillaume Seguin
# Copyright (C) 2011 Denis Kobozev
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import time
import numpy
import math
import sys
from pyglet.gl import *
from pyglet import gl
from pyglet.graphics.vertexbuffer import create_buffer
from . import vector
from printrun.printrun_utils import install_locale
install_locale('pronterface')
def compile_display_list(func, *options):
display_list = glGenLists(1)
glNewList(display_list, GL_COMPILE)
func(*options)
glEndList()
return display_list
def numpy2vbo(nparray, target = GL_ARRAY_BUFFER, usage = GL_STATIC_DRAW):
vbo = create_buffer(nparray.nbytes, target = target, usage = usage, vbo = True)
vbo.bind()
vbo.set_data(nparray.ctypes.data)
return vbo
class BoundingBox(object):
"""
A rectangular box (cuboid) enclosing a 3D model, defined by lower and upper corners.
"""
def __init__(self, upper_corner, lower_corner):
self.upper_corner = upper_corner
self.lower_corner = lower_corner
@property
def width(self):
width = abs(self.upper_corner[0] - self.lower_corner[0])
return round(width, 2)
@property
def depth(self):
depth = abs(self.upper_corner[1] - self.lower_corner[1])
return round(depth, 2)
@property
def height(self):
height = abs(self.upper_corner[2] - self.lower_corner[2])
return round(height, 2)
class Platform(object):
"""
Platform on which models are placed.
"""
graduations_major = 10
def __init__(self, build_dimensions):
self.width = build_dimensions[0]
self.depth = build_dimensions[1]
self.height = build_dimensions[2]
self.xoffset = build_dimensions[3]
self.yoffset = build_dimensions[4]
self.zoffset = build_dimensions[5]
self.color_grads_minor = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.1)
self.color_grads_interm = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.2)
self.color_grads_major = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.33)
self.color_fill = (0xaf / 255, 0xdf / 255, 0x5f / 255, 0.05)
self.initialized = False
self.loaded = True
def init(self):
self.display_list = compile_display_list(self.draw)
self.initialized = True
def draw(self):
glPushMatrix()
glTranslatef(self.xoffset, self.yoffset, self.zoffset)
def color(i):
if i % self.graduations_major == 0:
glColor4f(*self.color_grads_major)
elif i % (self.graduations_major / 2) == 0:
glColor4f(*self.color_grads_interm)
else:
glColor4f(*self.color_grads_minor)
# draw the grid
glBegin(GL_LINES)
for i in range(0, int(math.ceil(self.width + 1))):
color(i)
glVertex3f(float(i), 0.0, 0.0)
glVertex3f(float(i), self.depth, 0.0)
for i in range(0, int(math.ceil(self.depth + 1))):
color(i)
glVertex3f(0, float(i), 0.0)
glVertex3f(self.width, float(i), 0.0)
glEnd()
# draw fill
glColor4f(*self.color_fill)
glRectf(0.0, 0.0, float(self.width), float(self.depth))
glPopMatrix()
def display(self, mode_2d=False):
glCallList(self.display_list)
class Model(object):
"""
Parent class for models that provides common functionality.
"""
AXIS_X = (1, 0, 0)
AXIS_Y = (0, 1, 0)
AXIS_Z = (0, 0, 1)
letter_axis_map = {
'x': AXIS_X,
'y': AXIS_Y,
'z': AXIS_Z,
}
axis_letter_map = dict([(v, k) for k, v in letter_axis_map.items()])
def __init__(self, offset_x=0, offset_y=0):
self.offset_x = offset_x
self.offset_y = offset_y
self.init_model_attributes()
def init_model_attributes(self):
"""
Set/reset saved properties.
"""
self.invalidate_bounding_box()
self.modified = False
def invalidate_bounding_box(self):
self._bounding_box = None
@property
def bounding_box(self):
"""
Get a bounding box for the model.
"""
if self._bounding_box is None:
self._bounding_box = self._calculate_bounding_box()
return self._bounding_box
def _calculate_bounding_box(self):
"""
Calculate an axis-aligned box enclosing the model.
"""
# swap rows and columns in our vertex arrays so that we can do max and
# min on axis 1
xyz_rows = self.vertices.reshape(-1, order='F').reshape(3, -1)
lower_corner = xyz_rows.min(1)
upper_corner = xyz_rows.max(1)
box = BoundingBox(upper_corner, lower_corner)
return box
@property
def width(self):
return self.bounding_box.width
@property
def depth(self):
return self.bounding_box.depth
@property
def height(self):
return self.bounding_box.height
def movement_angle(src, dst, precision=0):
x = dst[0] - src[0]
y = dst[1] - src[1]
angle = math.degrees(math.atan2(y, -x)) # negate x for clockwise rotation angle
return round(angle, precision)
class GcodeModel(Model):
"""
Model for displaying Gcode data.
"""
# vertices for arrow to display the direction of movement
arrow = numpy.require([
[0.0, 0.0, 0.0],
[0.4, -0.1, 0.0],
[0.4, 0.1, 0.0],
], 'f')
color_printed = (0.2, 0.75, 0, 0.6)
loaded = False
def load_data(self, model_data, callback=None):
t_start = time.time()
vertex_list = []
color_list = []
self.layer_stops = [0]
arrow_list = []
num_layers = len(model_data.all_layers)
prev_pos = (0, 0, 0)
for layer_idx, layer in enumerate(model_data.all_layers):
for gline in layer.lines:
if not gline.is_move:
continue
vertex_list.append(prev_pos)
current_pos = (gline.current_x, gline.current_y, gline.current_z)
vertex_list.append(current_pos)
arrow = self.arrow
# position the arrow with respect to movement
arrow = vector.rotate(arrow, movement_angle(prev_pos, current_pos), 0.0, 0.0, 1.0)
arrow_list.extend(arrow)
vertex_color = self.movement_color(gline)
color_list.append(vertex_color)
prev_pos = current_pos
gline.gcview_end_vertex = len(vertex_list)
self.layer_stops.append(len(vertex_list))
if callback:
callback(layer_idx + 1, num_layers)
self.vertices = numpy.array(vertex_list, dtype = GLfloat)
self.colors = numpy.array(color_list, dtype = GLfloat)
self.arrows = numpy.array(arrow_list, dtype = GLfloat)
# by translating the arrow vertices outside of the loop, we achieve a
# significant performance gain thanks to numpy. it would be really nice
# if we could rotate in a similar fashion...
self.arrows = self.arrows + self.vertices[1::2].repeat(3, 0)
# for every pair of vertices of the model, there are 3 vertices for the arrow
assert len(self.arrows) == ((len(self.vertices) // 2) * 3), \
'The 2:3 ratio of model vertices to arrow vertices does not hold.'
self.max_layers = len(self.layer_stops) - 1
self.num_layers_to_draw = self.max_layers
self.printed_until = -1
self.arrows_enabled = False
self.initialized = False
self.loaded = True
t_end = time.time()
print >> sys.stderr, _('Initialized 3D visualization in %.2f seconds') % (t_end - t_start)
print >> sys.stderr, _('Vertex count: %d') % len(self.vertices)
def copy(self):
copy = GcodeModel()
for var in ["vertices", "arrows", "colors", "max_layers", "num_layers_to_draw", "printed_until", "arrows_enabled", "layer_stops"]:
setattr(copy, var, getattr(self, var))
copy.loaded = True
copy.initialized = False
return copy
def movement_color(self, move):
"""
Return the color to use for particular type of movement.
"""
# default movement color is gray
color = [0.6, 0.6, 0.6, 0.6]
"""
extruder_on = (move.flags & Movement.FLAG_EXTRUDER_ON or
move.delta_e > 0)
outer_perimeter = (move.flags & Movement.FLAG_PERIMETER and
move.flags & Movement.FLAG_PERIMETER_OUTER)
if extruder_on and outer_perimeter:
color = [0.0, 0.875, 0.875, 0.6] # cyan
elif extruder_on and move.flags & Movement.FLAG_PERIMETER:
color = [0.0, 1.0, 0.0, 0.6] # green
elif extruder_on and move.flags & Movement.FLAG_LOOP:
color = [1.0, 0.875, 0.0, 0.6] # yellow
elif extruder_on:
color = [1.0, 0.0, 0.0, 0.6] # red
"""
if move.extruding:
color = [1.0, 0.0, 0.0, 0.6] # red
return color
# ------------------------------------------------------------------------
# DRAWING
# ------------------------------------------------------------------------
def init(self):
self.vertex_buffer = numpy2vbo(self.vertices)
self.vertex_color_buffer = numpy2vbo(self.colors.repeat(2, 0)) # each pair of vertices shares the color
if self.arrows_enabled:
self.arrow_buffer = numpy2vbo(self.arrows)
self.arrow_color_buffer = numpy2vbo(self.colors.repeat(3, 0)) # each triplet of vertices shares the color
self.initialized = True
def display(self, mode_2d=False):
glPushMatrix()
glTranslatef(self.offset_x, self.offset_y, 0)
glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_COLOR_ARRAY)
self._display_movements(mode_2d)
if self.arrows_enabled:
self._display_arrows()
glDisableClientState(GL_COLOR_ARRAY)
glDisableClientState(GL_VERTEX_ARRAY)
glPopMatrix()
def _display_movements(self, mode_2d=False):
self.vertex_buffer.bind()
glVertexPointer(3, GL_FLOAT, 0, None)
if mode_2d:
glScale(1.0, 1.0, 0.0) # discard z coordinates
start = self.layer_stops[self.num_layers_to_draw - 1]
end = self.layer_stops[self.num_layers_to_draw] - start
else: # 3d
start = 0
end = self.layer_stops[self.num_layers_to_draw]
glDisableClientState(GL_COLOR_ARRAY)
glColor4f(*self.color_printed)
printed_end = min(self.printed_until, end)
if start < printed_end:
glDrawArrays(GL_LINES, start, printed_end)
glEnableClientState(GL_COLOR_ARRAY)
self.vertex_color_buffer.bind()
glColorPointer(4, GL_FLOAT, 0, None)
start = max(self.printed_until, 0)
end = end - start
if start >= 0 and end > 0:
glDrawArrays(GL_LINES, start, end)
self.vertex_buffer.unbind()
self.vertex_color_buffer.unbind()
def _display_arrows(self):
self.arrow_buffer.bind()
glVertexPointer(3, GL_FLOAT, 0, None)
self.arrow_color_buffer.bind()
glColorPointer(4, GL_FLOAT, 0, None)
start = (self.layer_stops[self.num_layers_to_draw - 1] // 2) * 3
end = (self.layer_stops[self.num_layers_to_draw] // 2) * 3
glDrawArrays(GL_TRIANGLES, start, end - start)
self.arrow_buffer.unbind()
self.arrow_color_buffer.unbind()
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Denis Kobozev
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
from __future__ import division
import numpy
import math
_identity_matrix = [
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0],
]
_rotation_matrix_cache = {}
def identity_matrix():
return numpy.require(_identity_matrix[:], 'f')
def rotation_matrix(angle, x, y, z):
angle_r = math.radians(angle)
c = math.cos(angle_r)
s = math.sin(angle_r)
C = 1 - c
matrix = numpy.require([
[x ** 2 * C + c, x * y * C - z * s, x * z * C + y * s],
[y * x * C + z * s, y ** 2 * C + c, y * z * C - x * s],
[x * z * C - y * s, y * z * C + x * s, z ** 2 * C + c],
], 'f')
return matrix
def translate(vertices, x, y, z):
translated = vertices + numpy.array([x, y, z], 'f')
return translated
def rotate(vertices, angle, x, y, z):
key = (angle, x, y, z)
if key not in _rotation_matrix_cache:
_rotation_matrix_cache[key] = rotation_matrix(angle, x, y, z)
matrix = _rotation_matrix_cache[key]
rotated = numpy.dot(vertices, matrix)
return rotated
...@@ -120,36 +120,39 @@ class MacroEditor(wx.Dialog): ...@@ -120,36 +120,39 @@ class MacroEditor(wx.Dialog):
reindented += self.indent_chars + line + "\n" reindented += self.indent_chars + line + "\n"
return reindented return reindented
class options(wx.Dialog): class PronterOptionsDialog(wx.Dialog):
"""Options editor""" """Options editor"""
def __init__(self, pronterface): def __init__(self, pronterface):
wx.Dialog.__init__(self, None, title = _("Edit settings"), style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) wx.Dialog.__init__(self, parent = None, title = _("Edit settings"), size = (400, 500), style = wx.DEFAULT_DIALOG_STYLE)
topsizer = wx.BoxSizer(wx.VERTICAL) panel = wx.Panel(self)
vbox = wx.StaticBoxSizer(wx.StaticBox(self, label = _("Defaults")) ,wx.VERTICAL) header = wx.StaticBox(panel, label = _("Settings"))
topsizer.Add(vbox, 1, wx.ALL+wx.EXPAND) sbox = wx.StaticBoxSizer(header, wx.VERTICAL)
panel2 = wx.Panel(panel)
grid = wx.FlexGridSizer(rows = 0, cols = 2, hgap = 8, vgap = 2) grid = wx.FlexGridSizer(rows = 0, cols = 2, hgap = 8, vgap = 2)
grid.SetFlexibleDirection( wx.BOTH ) grid.SetFlexibleDirection(wx.BOTH)
grid.AddGrowableCol( 1 ) grid.AddGrowableCol(1)
grid.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED ) grid.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
vbox.Add(grid, 0, wx.EXPAND) for setting in pronterface.settings._all_settings():
ctrls = {} grid.Add(setting.get_label(panel2), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.ALIGN_RIGHT)
for k, v in sorted(pronterface.settings._all_settings().items()): grid.Add(setting.get_widget(panel2), 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL)
ctrls[k, 0] = wx.StaticText(self,-1, k) panel2.SetSizer(grid)
ctrls[k, 1] = wx.TextCtrl(self,-1, str(v)) sbox.Add(panel2, 1, wx.EXPAND)
if k in pronterface.helpdict: panel.SetSizer(sbox)
ctrls[k, 0].SetToolTipString(pronterface.helpdict.get(k)) topsizer = wx.BoxSizer(wx.VERTICAL)
ctrls[k, 1].SetToolTipString(pronterface.helpdict.get(k)) topsizer.Add(panel, 1, wx.ALL | wx.EXPAND)
grid.Add(ctrls[k, 0], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.ALIGN_RIGHT) topsizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT)
grid.Add(ctrls[k, 1], 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND) self.SetSizerAndFit(topsizer)
topsizer.Add(self.CreateSeparatedButtonSizer(wx.OK+wx.CANCEL), 0, wx.EXPAND) self.SetMinSize(self.GetSize())
self.SetSizer(topsizer)
topsizer.Layout() def PronterOptions(pronterface):
topsizer.Fit(self) dialog = PronterOptionsDialog(pronterface)
if self.ShowModal() == wx.ID_OK: if dialog.ShowModal() == wx.ID_OK:
for k, v in pronterface.settings._all_settings().items(): for setting in pronterface.settings._all_settings():
if ctrls[k, 1].GetValue() != str(v): old_value = setting.value
pronterface.set(k, str(ctrls[k, 1].GetValue())) setting.update()
self.Destroy() if setting.value != old_value:
pronterface.set(setting.name, setting.value)
dialog.Destroy()
class ButtonEdit(wx.Dialog): class ButtonEdit(wx.Dialog):
"""Custom button edit dialog""" """Custom button edit dialog"""
...@@ -211,6 +214,113 @@ class ButtonEdit(wx.Dialog): ...@@ -211,6 +214,113 @@ class ButtonEdit(wx.Dialog):
if self.name.GetValue()=="": if self.name.GetValue()=="":
self.name.SetValue(macro) self.name.SetValue(macro)
class TempGauge(wx.Panel):
def __init__(self, parent, size = (200, 22), title = "", maxval = 240, gaugeColour = None):
wx.Panel.__init__(self, parent,-1, size = size)
self.Bind(wx.EVT_PAINT, self.paint)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.width, self.height = size
self.title = title
self.max = maxval
self.gaugeColour = gaugeColour
self.value = 0
self.setpoint = 0
self.recalc()
def recalc(self):
mmax = max(int(self.setpoint*1.05), self.max)
self.scale = float(self.width-2)/float(mmax)
self.ypt = max(16, int(self.scale*max(self.setpoint, self.max/6)))
def SetValue(self, value):
self.value = value
wx.CallAfter(self.Refresh)
def SetTarget(self, value):
self.setpoint = value
wx.CallAfter(self.Refresh)
def interpolatedColour(self, val, vmin, vmid, vmax, cmin, cmid, cmax):
if val < vmin: return cmin
if val > vmax: return cmax
if val <= vmid:
lo, hi, val, valhi = cmin, cmid, val-vmin, vmid-vmin
else:
lo, hi, val, valhi = cmid, cmax, val-vmid, vmax-vmid
vv = float(val)/valhi
rgb = lo.Red()+(hi.Red()-lo.Red())*vv, lo.Green()+(hi.Green()-lo.Green())*vv, lo.Blue()+(hi.Blue()-lo.Blue())*vv
rgb = map(lambda x:x*0.8, rgb)
return wx.Colour(*map(int, rgb))
def paint(self, ev):
self.width, self.height = self.GetClientSizeTuple()
self.recalc()
x0, y0, x1, y1, xE, yE = 1, 1, self.ypt+1, 1, self.width+1-2, 20
dc = wx.PaintDC(self)
dc.SetBackground(wx.Brush((255, 255, 255)))
dc.Clear()
cold, medium, hot = wx.Colour(0, 167, 223), wx.Colour(239, 233, 119), wx.Colour(210, 50.100)
gauge1, gauge2 = wx.Colour(255, 255, 210), (self.gaugeColour or wx.Colour(234, 82, 0))
shadow1, shadow2 = wx.Colour(110, 110, 110), wx.Colour(255, 255, 255)
gc = wx.GraphicsContext.Create(dc)
# draw shadow first
# corners
gc.SetBrush(gc.CreateRadialGradientBrush(xE-7, 9, xE-7, 9, 8, shadow1, shadow2))
gc.DrawRectangle(xE-7, 1, 8, 8)
gc.SetBrush(gc.CreateRadialGradientBrush(xE-7, 17, xE-7, 17, 8, shadow1, shadow2))
gc.DrawRectangle(xE-7, 17, 8, 8)
gc.SetBrush(gc.CreateRadialGradientBrush(x0+6, 17, x0+6, 17, 8, shadow1, shadow2))
gc.DrawRectangle(0, 17, x0+6, 8)
# edges
gc.SetBrush(gc.CreateLinearGradientBrush(xE-6, 0, xE+1, 0, shadow1, shadow2))
gc.DrawRectangle(xE-7, 9, 8, 8)
gc.SetBrush(gc.CreateLinearGradientBrush(x0, yE-2, x0, yE+5, shadow1, shadow2))
gc.DrawRectangle(x0+6, yE-2, xE-12, 7)
# draw gauge background
gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0, x1+1, y1, cold, medium))
gc.DrawRoundedRectangle(x0, y0, x1+4, yE, 6)
gc.SetBrush(gc.CreateLinearGradientBrush(x1-2, y1, xE, y1, medium, hot))
gc.DrawRoundedRectangle(x1-2, y1, xE-x1, yE, 6)
# draw gauge
width = 12
w1 = y0+9-width/2
w2 = w1+width
value = x0+max(10, min(self.width+1-2, int(self.value*self.scale)))
#gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0+3, x0, y0+15, gauge1, gauge2))
#gc.SetBrush(gc.CreateLinearGradientBrush(0, 3, 0, 15, wx.Colour(255, 255, 255), wx.Colour(255, 90, 32)))
gc.SetBrush(gc.CreateLinearGradientBrush(x0, y0+3, x0, y0+15, gauge1, self.interpolatedColour(value, x0, x1, xE, cold, medium, hot)))
val_path = gc.CreatePath()
val_path.MoveToPoint(x0, w1)
val_path.AddLineToPoint(value, w1)
val_path.AddLineToPoint(value+2, w1+width/4)
val_path.AddLineToPoint(value+2, w2-width/4)
val_path.AddLineToPoint(value, w2)
#val_path.AddLineToPoint(value-4, 10)
val_path.AddLineToPoint(x0, w2)
gc.DrawPath(val_path)
# draw setpoint markers
setpoint = x0+max(10, int(self.setpoint*self.scale))
gc.SetBrush(gc.CreateBrush(wx.Brush(wx.Colour(0, 0, 0))))
setp_path = gc.CreatePath()
setp_path.MoveToPoint(setpoint-4, y0)
setp_path.AddLineToPoint(setpoint+4, y0)
setp_path.AddLineToPoint(setpoint, y0+5)
setp_path.MoveToPoint(setpoint-4, yE)
setp_path.AddLineToPoint(setpoint+4, yE)
setp_path.AddLineToPoint(setpoint, yE-5)
gc.DrawPath(setp_path)
# draw readout
text = u"T\u00B0 %u/%u"%(self.value, self.setpoint)
#gc.SetFont(gc.CreateFont(wx.Font(12, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD), wx.WHITE))
#gc.DrawText(text, 29,-2)
gc.SetFont(gc.CreateFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD), wx.WHITE))
gc.DrawText(self.title, x0+19, y0+4)
gc.DrawText(text, x0+119, y0+4)
gc.SetFont(gc.CreateFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD)))
gc.DrawText(self.title, x0+18, y0+3)
gc.DrawText(text, x0+118, y0+3)
class SpecialButton(object): class SpecialButton(object):
label = None label = None
......
...@@ -24,8 +24,8 @@ import time ...@@ -24,8 +24,8 @@ import time
import threading import threading
import pyglet import pyglet
pyglet.options['shadow_window'] = False pyglet.options['debug_gl'] = True
pyglet.options['debug_gl'] = False
from pyglet.gl import * from pyglet.gl import *
...@@ -233,7 +233,7 @@ def vdiff(v, o): ...@@ -233,7 +233,7 @@ def vdiff(v, o):
class gcview(object): class gcview(object):
def __init__(self, lines, batch, w = 0.5, h = 0.5): def __init__(self, gcode, batch, w = 0.5, h = 0.5):
# Create the vertex and normal arrays. # Create the vertex and normal arrays.
vertices = [] vertices = []
normals = [] normals = []
...@@ -242,8 +242,12 @@ class gcview(object): ...@@ -242,8 +242,12 @@ class gcview(object):
self.vlists = [] self.vlists = []
self.layers = {} self.layers = {}
t0 = time.time() t0 = time.time()
lines = [self.transform(i) for i in lines] if gcode:
lines = [i for i in lines if i is not None] lines = [self.transform(gline) for gline in gcode.lines]
lines = [line for line in lines if line]
print len(lines)
else:
lines = []
print "transformed lines in %fs" % (time.time() - t0) print "transformed lines in %fs" % (time.time() - t0)
t0 = time.time() t0 = time.time()
layertemp = {} layertemp = {}
...@@ -355,22 +359,21 @@ class gcview(object): ...@@ -355,22 +359,21 @@ class gcview(object):
epoints = map(lambda x: vadd(E, x), points) epoints = map(lambda x: vadd(E, x), points)
return spoints, epoints, S, E return spoints, epoints, S, E
def transform(self, line): def transform(self, gline):
line = line.split(";")[0]
cur = self.prev[:] cur = self.prev[:]
if len(line) > 0: isg92 = (gline.command == "G92")
if "G1" in line or "G0" in line or "G92" in line: if gline.is_move or isg92:
if("X" in line): if gline.x is not None:
cur[0] = float(line.split("X")[1].split(" ")[0]) cur[0] = gline.x
if("Y" in line): if gline.y is not None:
cur[1] = float(line.split("Y")[1].split(" ")[0]) cur[1] = gline.y
if("Z" in line): if gline.z is not None:
cur[2] = float(line.split("Z")[1].split(" ")[0]) cur[2] = gline.z
if("E" in line): if gline.e is not None:
cur[3] = float(line.split("E")[1].split(" ")[0]) cur[3] = gline.e
if self.prev == cur: if self.prev == cur:
return None return None
if self.fline or "G92" in line: elif self.fline or isg92:
self.prev = cur self.prev = cur
self.fline = 0 self.fline = 0
return None return None
...@@ -378,6 +381,8 @@ class gcview(object): ...@@ -378,6 +381,8 @@ class gcview(object):
r = [self.prev, cur] r = [self.prev, cur]
self.prev = cur self.prev = cur
return r return r
else:
return None
def delete(self): def delete(self):
for i in self.vlists: for i in self.vlists:
...@@ -833,7 +838,7 @@ class GCFrame(wx.Frame): ...@@ -833,7 +838,7 @@ class GCFrame(wx.Frame):
m.curlayer = 0.0 m.curlayer = 0.0
m.scale = [1.0, 1.0, 1.0] m.scale = [1.0, 1.0, 1.0]
m.batch = pyglet.graphics.Batch() m.batch = pyglet.graphics.Batch()
m.gc = gcview([], batch = m.batch) m.gc = gcview(None, batch = m.batch)
self.models = {"": m} self.models = {"": m}
self.l = d() self.l = d()
self.modelindex = 0 self.modelindex = 0
......
...@@ -41,7 +41,7 @@ class XYButtons(BufferedCanvas): ...@@ -41,7 +41,7 @@ class XYButtons(BufferedCanvas):
center = (124, 121) center = (124, 121)
spacer = 7 spacer = 7
def __init__(self, parent, moveCallback = None, cornerCallback = None, spacebarCallback = None, bgcolor = "#FFFFFF", ID=-1): def __init__(self, parent, moveCallback = None, cornerCallback = None, spacebarCallback = None, bgcolor = "#FFFFFF", ID=-1, zcallback=None):
self.bg_bmp = wx.Image(imagefile("control_xy.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap() self.bg_bmp = wx.Image(imagefile("control_xy.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.keypad_bmp = wx.Image(imagefile("arrow_keys.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap() self.keypad_bmp = wx.Image(imagefile("arrow_keys.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.keypad_idx = -1 self.keypad_idx = -1
...@@ -51,6 +51,7 @@ class XYButtons(BufferedCanvas): ...@@ -51,6 +51,7 @@ class XYButtons(BufferedCanvas):
self.moveCallback = moveCallback self.moveCallback = moveCallback
self.cornerCallback = cornerCallback self.cornerCallback = cornerCallback
self.spacebarCallback = spacebarCallback self.spacebarCallback = spacebarCallback
self.zCallback = zcallback
self.enabled = False self.enabled = False
# Remember the last clicked buttons, so we can repeat when spacebar pressed # Remember the last clicked buttons, so we can repeat when spacebar pressed
self.lastMove = None self.lastMove = None
...@@ -60,8 +61,7 @@ class XYButtons(BufferedCanvas): ...@@ -60,8 +61,7 @@ class XYButtons(BufferedCanvas):
self.bgcolor.SetFromName(bgcolor) self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128) self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID) BufferedCanvas.__init__(self, parent, ID, size=self.bg_bmp.GetSize())
self.SetSize(self.bg_bmp.GetSize())
# Set up mouse and keyboard event capture # Set up mouse and keyboard event capture
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
...@@ -109,10 +109,13 @@ class XYButtons(BufferedCanvas): ...@@ -109,10 +109,13 @@ class XYButtons(BufferedCanvas):
self.update() self.update()
def getMovement(self): def getMovement(self):
xdir = [1, 0, -1, 0][self.quadrant] xdir = [1, 0, -1, 0, 0, 0][self.quadrant]
ydir = [0, 1, 0, -1][self.quadrant] ydir = [0, 1, 0, -1, 0 ,0][self.quadrant]
zdir = [0, 0, 0, 0, 1 ,-1][self.quadrant]
magnitude = math.pow(10, self.concentric-1) magnitude = math.pow(10, self.concentric-1)
return (magnitude * xdir, magnitude * ydir) if not zdir == 0:
magnitude=min(magnitude,10)
return (magnitude * xdir, magnitude * ydir, magnitude * zdir)
def lookupConcentric(self, radius): def lookupConcentric(self, radius):
idx = 0 idx = 0
...@@ -286,14 +289,21 @@ class XYButtons(BufferedCanvas): ...@@ -286,14 +289,21 @@ class XYButtons(BufferedCanvas):
self.quadrant = 2 self.quadrant = 2
elif evt.GetKeyCode() == wx.WXK_RIGHT: elif evt.GetKeyCode() == wx.WXK_RIGHT:
self.quadrant = 0 self.quadrant = 0
elif evt.GetKeyCode() == wx.WXK_PAGEUP:
self.quadrant = 4
elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
self.quadrant = 5
else: else:
evt.Skip() evt.Skip()
return return
if self.moveCallback:
self.concentric = self.keypad_idx self.concentric = self.keypad_idx
x, y = self.getMovement() x, y, z = self.getMovement()
if x!=0 or y!=0 and self.moveCallback:
self.moveCallback(x, y) self.moveCallback(x, y)
if z!=0 and self.zCallback:
self.zCallback(z)
elif evt.GetKeyCode() == wx.WXK_SPACE: elif evt.GetKeyCode() == wx.WXK_SPACE:
self.spacebarCallback() self.spacebarCallback()
...@@ -347,7 +357,7 @@ class XYButtons(BufferedCanvas): ...@@ -347,7 +357,7 @@ class XYButtons(BufferedCanvas):
if self.concentric != None: if self.concentric != None:
if self.concentric < len(XYButtons.concentric_circle_radii): if self.concentric < len(XYButtons.concentric_circle_radii):
if self.quadrant != None: if self.quadrant != None:
x, y = self.getMovement() x, y, z = self.getMovement()
if self.moveCallback: if self.moveCallback:
self.lastMove = (x, y) self.lastMove = (x, y)
self.lastCorner = None self.lastCorner = None
......
...@@ -46,9 +46,7 @@ class ZButtons(BufferedCanvas): ...@@ -46,9 +46,7 @@ class ZButtons(BufferedCanvas):
self.bgcolor.SetFromName(bgcolor) self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128) self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID) BufferedCanvas.__init__(self, parent, ID, size=wx.Size(59, 244))
self.SetSize(wx.Size(59, 244))
# Set up mouse and keyboard event capture # Set up mouse and keyboard event capture
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
......
...@@ -19,13 +19,17 @@ import cmd, sys ...@@ -19,13 +19,17 @@ import cmd, sys
import glob, os, time, datetime import glob, os, time, datetime
import sys, subprocess import sys, subprocess
import math, codecs import math, codecs
import shlex
from math import sqrt from math import sqrt
from gcoder import GCode import argparse
import printcore import printcore
from printrun.printrun_utils import install_locale from printrun.printrun_utils import install_locale
from printrun import gcoder
install_locale('pronterface') install_locale('pronterface')
from functools import wraps
if os.name == "nt": if os.name == "nt":
try: try:
import _winreg import _winreg
...@@ -44,93 +48,6 @@ except: ...@@ -44,93 +48,6 @@ except:
def dosify(name): def dosify(name):
return os.path.split(name)[1].split(".")[0][:8]+".g" return os.path.split(name)[1].split(".")[0][:8]+".g"
def measurements(g):
gcode = GCode(g)
gcode.measure()
return (gcode.width, gcode.depth, gcode.height, gcode.xmin, gcode.xmax, gcode.ymin, gcode.ymax, gcode.zmin, gcode.zmax)
def totalelength(g):
gcode = GCode(g)
return gcode.filament_length()
def get_coordinate_value(axis, parts):
for i in parts:
if (axis in i):
return float(i[1:])
return None
def hypot3d(X1, Y1, Z1, X2 = 0.0, Y2 = 0.0, Z2 = 0.0):
return math.hypot(X2-X1, math.hypot(Y2-Y1, Z2-Z1))
def estimate_duration(g):
lastx = lasty = lastz = laste = lastf = 0.0
x = y = z = e = f = 0.0
currenttravel = 0.0
totaltravel = 0.0
moveduration = 0.0
totalduration = 0.0
acceleration = 1500.0 #mm/s/s ASSUMING THE DEFAULT FROM SPRINTER !!!!
layerduration = 0.0
layerbeginduration = 0.0
layercount = 0
#TODO:
# get device caps from firmware: max speed, acceleration/axis (including extruder)
# calculate the maximum move duration accounting for above ;)
# self.log(".... estimating ....")
for i in g:
i = i.split(";")[0]
if "G4" in i or "G1" in i:
if "G4" in i:
parts = i.split(" ")
moveduration = get_coordinate_value("P", parts[1:])
if moveduration is None:
continue
else:
moveduration /= 1000.0
if "G1" in i:
parts = i.split(" ")
x = get_coordinate_value("X", parts[1:])
if x is None: x = lastx
y = get_coordinate_value("Y", parts[1:])
if y is None: y = lasty
z = get_coordinate_value("Z", parts[1:])
if (z is None) or (z<lastz): z = lastz # Do not increment z if it's below the previous (Lift z on move fix)
e = get_coordinate_value("E", parts[1:])
if e is None: e = laste
f = get_coordinate_value("F", parts[1:])
if f is None: f = lastf
else: f /= 60.0 # mm/s vs mm/m
# given last feedrate and current feedrate calculate the distance needed to achieve current feedrate.
# if travel is longer than req'd distance, then subtract distance to achieve full speed, and add the time it took to get there.
# then calculate the time taken to complete the remaining distance
currenttravel = hypot3d(x, y, z, lastx, lasty, lastz)
distance = abs(2* ((lastf+f) * (f-lastf) * 0.5 ) / acceleration) #2x because we have to accelerate and decelerate
if distance <= currenttravel and ( lastf + f )!=0 and f!=0:
moveduration = 2 * distance / ( lastf + f )
currenttravel -= distance
moveduration += currenttravel/f
else:
moveduration = math.sqrt( 2 * distance / acceleration )
totalduration += moveduration
if z > lastz:
layercount +=1
#self.log("layer z: ", lastz, " will take: ", time.strftime('%H:%M:%S', time.gmtime(totalduration-layerbeginduration)))
layerbeginduration = totalduration
lastx = x
lasty = y
lastz = z
laste = e
lastf = f
#self.log("Total Duration: " #, time.strftime('%H:%M:%S', time.gmtime(totalduration)))
return "{0:d} layers, ".format(int(layercount)) + str(datetime.timedelta(seconds = int(totalduration)))
def confirm(): def confirm():
y_or_n = raw_input("y/n: ") y_or_n = raw_input("y/n: ")
if y_or_n == "y": if y_or_n == "y":
...@@ -139,27 +56,175 @@ def confirm(): ...@@ -139,27 +56,175 @@ def confirm():
return confirm() return confirm()
return False return False
class Settings: def setting_add_tooltip(func):
@wraps(func)
def decorator(self, *args, **kwargs):
widget = func(self, *args, **kwargs)
if self.help:
widget.SetToolTipString(self.help)
return widget
return decorator
class Setting(object):
hidden = False
def __init__(self, name, default, label = None, help = None):
self.name = name
self.default = default
self._value = default
self.label = label
self.help = help
def _get_value(self):
return self._value
def _set_value(self, value):
raise NotImplementedError
value = property(_get_value, _set_value)
@setting_add_tooltip
def get_label(self, parent):
import wx
widget = wx.StaticText(parent, -1, self.label or self.name)
return widget
@setting_add_tooltip
def get_widget(self, parent):
return self.get_specific_widget(parent)
def get_specific_widget(self, parent):
raise NotImplementedError
def set(self, value):
raise NotImplementedError
def get(self):
raise NotImplementedError
def update(self):
raise NotImplementedError
def __str__(self):
return self.name
def __repr__(self):
return self.name
class HiddenSetting(Setting):
hidden = True
def _set_value(self, value):
self._value = value
value = property(Setting._get_value, _set_value)
class wxSetting(Setting):
widget = None
def _set_value(self, value):
self._value = value
if self.widget:
self.widget.SetValue(value)
value = property(Setting._get_value, _set_value)
def update(self):
self.value = self.widget.GetValue()
class StringSetting(wxSetting):
def get_specific_widget(self, parent):
import wx
self.widget = wx.TextCtrl(parent, -1, str(self.value))
return self.widget
class ComboSetting(wxSetting):
def __init__(self, name, default, choices, label = None, help = None):
super(ComboSetting, self).__init__(name, default, label, help)
self.choices = choices
def get_specific_widget(self, parent):
import wx
self.widget = wx.ComboBox(parent, -1, str(self.value), choices = self.choices, style = wx.CB_DROPDOWN)
return self.widget
class SpinSetting(wxSetting):
def __init__(self, name, default, min, max, label = None, help = None):
super(SpinSetting, self).__init__(name, default, label, help)
self.min = min
self.max = max
def get_specific_widget(self, parent):
import wx
self.widget = wx.SpinCtrl(parent, -1, min = self.min, max = self.max)
self.widget.SetValue(self.value)
return self.widget
class FloatSpinSetting(SpinSetting):
def get_specific_widget(self, parent):
from wx.lib.agw.floatspin import FloatSpin
self.widget = FloatSpin(parent, -1, value = self.value, min_val = self.min, max_val = self.max, digits = 2)
return self.widget
class BooleanSetting(wxSetting):
def _get_value(self):
return bool(self._value)
def _set_value(self, value):
self._value = value
value = property(_get_value, _set_value)
def get_specific_widget(self, parent):
import wx
self.widget = wx.CheckBox(parent, -1)
self.widget.SetValue(bool(self.value))
return self.widget
class Settings(object):
#def _temperature_alias(self): return {"pla":210, "abs":230, "off":0} #def _temperature_alias(self): return {"pla":210, "abs":230, "off":0}
#def _temperature_validate(self, v): #def _temperature_validate(self, v):
# if v < 0: raise ValueError("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0.") # if v < 0: raise ValueError("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0.")
#def _bedtemperature_alias(self): return {"pla":60, "abs":110, "off":0} #def _bedtemperature_alias(self): return {"pla":60, "abs":110, "off":0}
def _baudrate_list(self): return ["2400", "9600", "19200", "38400", "57600", "115200"] def _baudrate_list(self): return ["2400", "9600", "19200", "38400", "57600", "115200", "250000"]
def __init__(self): def __init__(self):
# defaults here. # defaults here.
# the initial value determines the type # the initial value determines the type
self.port = "" self._add(StringSetting("port", "", _("Serial port"), _("Port used to communicate with printer")))
self.baudrate = 115200 self._add(ComboSetting("baudrate", 115200, self._baudrate_list(), _("Baud rate"), _("Communications Speed (default: 115200)")))
self.bedtemp_abs = 110 self._add(SpinSetting("bedtemp_abs", 110, 0, 400, _("Bed temperature for ABS"), _("Heated Build Platform temp for ABS (default: 110 deg C)")))
self.bedtemp_pla = 60 self._add(SpinSetting("bedtemp_pla", 60, 0, 400, _("Bed temperature for PLA"), _("Heated Build Platform temp for PLA (default: 60 deg C)")))
self.temperature_abs = 230 self._add(SpinSetting("temperature_abs", 230, 0, 400, _("Bed temperature for ABS"), _("Extruder temp for ABS (default: 230 deg C)")))
self.temperature_pla = 185 self._add(SpinSetting("temperature_pla", 185, 0, 400, _("Bed temperature for PLA"), _("Extruder temp for PLA (default: 185 deg C)")))
self.xy_feedrate = 3000 self._add(SpinSetting("xy_feedrate", 3000, 0, 50000, _("X & Y manual feedrate"), _("Feedrate for Control Panel Moves in X and Y (default: 3000mm/min)")))
self.z_feedrate = 200 self._add(SpinSetting("z_feedrate", 200, 0, 50000, _("Z manual feedrate"), _("Feedrate for Control Panel Moves in Z (default: 200mm/min)")))
self.e_feedrate = 300 self._add(SpinSetting("e_feedrate", 100, 0, 1000, _("E manual feedrate"), _("Feedrate for Control Panel Moves in Extrusions (default: 300mm/min)")))
self.slicecommand = "python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s" self._add(StringSetting("slicecommand", "python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s", _("Slice command"), _("Slice command\n default:\n python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s)")))
self.sliceoptscommand = "python skeinforge/skeinforge_application/skeinforge.py" self._add(StringSetting("sliceoptscommand", "python skeinforge/skeinforge_application/skeinforge.py", _("Slicer options command"), _("Slice settings command\n default:\n python skeinforge/skeinforge_application/skeinforge.py")))
self.final_command = "" self._add(StringSetting("final_command", "", _("Final command"), _("Executable to run when the print is finished")))
_settings = []
def __setattr__(self, name, value):
if name.startswith("_"):
return object.__setattr__(self, name, value)
if isinstance(value, Setting):
if not value.hidden:
self._settings.append(value)
object.__setattr__(self, "_" + name, value)
elif hasattr(self, "_" + name):
getattr(self, "_" + name).value = value
else:
setattr(self, name, StringSetting(name = name, default = value))
def __getattr__(self, name):
if name.startswith("_"):
return object.__getattribute__(self, name)
return getattr(self, "_" + name).value
def _add(self, setting):
setattr(self, setting.name, setting)
def _set(self, key, value): def _set(self, key, value):
try: try:
...@@ -172,12 +237,15 @@ class Settings: ...@@ -172,12 +237,15 @@ class Settings:
getattr(self, "_%s_validate"%key)(value) getattr(self, "_%s_validate"%key)(value)
except AttributeError: except AttributeError:
pass pass
setattr(self, key, type(getattr(self, key))(value)) t = type(getattr(self, key))
if t == bool and value == "False": setattr(self, key, False)
else: setattr(self, key, t(value))
try: try:
getattr(self, "_%s_cb"%key)(key, value) getattr(self, "_%s_cb"%key)(key, value)
except AttributeError: except AttributeError:
pass pass
return value return value
def _tabcomplete(self, key): def _tabcomplete(self, key):
try: try:
return getattr(self, "_%s_list"%key)() return getattr(self, "_%s_list"%key)()
...@@ -188,8 +256,9 @@ class Settings: ...@@ -188,8 +256,9 @@ class Settings:
except AttributeError: except AttributeError:
pass pass
return [] return []
def _all_settings(self): def _all_settings(self):
return dict([(k, getattr(self, k)) for k in self.__dict__.keys() if not k.startswith("_")]) return self._settings
class Status: class Status:
...@@ -233,6 +302,7 @@ class pronsole(cmd.Cmd): ...@@ -233,6 +302,7 @@ class pronsole(cmd.Cmd):
self.in_macro = False self.in_macro = False
self.p.onlinecb = self.online self.p.onlinecb = self.online
self.f = None self.f = None
self.fgcode = None
self.listing = 0 self.listing = 0
self.sdfiles = [] self.sdfiles = []
self.paused = False self.paused = False
...@@ -253,30 +323,14 @@ class pronsole(cmd.Cmd): ...@@ -253,30 +323,14 @@ class pronsole(cmd.Cmd):
self.settings._bedtemp_pla_cb = self.set_temp_preset self.settings._bedtemp_pla_cb = self.set_temp_preset
self.monitoring = 0 self.monitoring = 0
self.silent = False self.silent = False
self.helpdict = {}
self.helpdict["baudrate"] = _("Communications Speed (default: 115200)")
self.helpdict["bedtemp_abs"] = _("Heated Build Platform temp for ABS (default: 110 deg C)")
self.helpdict["bedtemp_pla"] = _("Heated Build Platform temp for PLA (default: 60 deg C)")
self.helpdict["e_feedrate"] = _("Feedrate for Control Panel Moves in Extrusions (default: 300mm/min)")
self.helpdict["port"] = _("Port used to communicate with printer")
self.helpdict["slicecommand"] = _("Slice command\n default:\n python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s)")
self.helpdict["sliceoptscommand"] = _("Slice settings command\n default:\n python skeinforge/skeinforge_application/skeinforge.py")
self.helpdict["temperature_abs"] = _("Extruder temp for ABS (default: 230 deg C)")
self.helpdict["temperature_pla"] = _("Extruder temp for PLA (default: 185 deg C)")
self.helpdict["xy_feedrate"] = _("Feedrate for Control Panel Moves in X and Y (default: 3000mm/min)")
self.helpdict["z_feedrate"] = _("Feedrate for Control Panel Moves in Z (default: 200mm/min)")
self.helpdict["final_command"] = _("Executable to run when the print is finished")
self.commandprefixes='MGT$' self.commandprefixes='MGT$'
self.webrequested = False
self.web_config = None
self.web_auth_config = None
self.promptstrs = {"offline" : "%(bold)suninitialized>%(normal)s ", self.promptstrs = {"offline" : "%(bold)suninitialized>%(normal)s ",
"fallback" : "%(bold)sPC>%(normal)s ", "fallback" : "%(bold)sPC>%(normal)s ",
"macro" : "%(bold)s..>%(normal)s ", "macro" : "%(bold)s..>%(normal)s ",
"online" : "%(bold)sT:%(extruder_temp_fancy)s %(progress_fancy)s >%(normal)s "} "online" : "%(bold)sT:%(extruder_temp_fancy)s %(progress_fancy)s >%(normal)s "}
def log(self, *msg): def log(self, *msg):
print ''.join(str(i) for i in msg) print u"".join(unicode(i) for i in msg)
def promptf(self): def promptf(self):
"""A function to generate prompts so that we can do dynamic prompts. """ """A function to generate prompts so that we can do dynamic prompts. """
...@@ -611,7 +665,7 @@ class pronsole(cmd.Cmd): ...@@ -611,7 +665,7 @@ class pronsole(cmd.Cmd):
#else: #else:
# self.log("Removed '"+key+"' from '"+self.rc_filename+"'") # self.log("Removed '"+key+"' from '"+self.rc_filename+"'")
except Exception, e: except Exception, e:
self.log("Saving failed for", key+":", str(e)) self.log("Saving failed for ", key+":", str(e))
finally: finally:
del rci, rco del rci, rco
...@@ -671,20 +725,21 @@ class pronsole(cmd.Cmd): ...@@ -671,20 +725,21 @@ class pronsole(cmd.Cmd):
def help_disconnect(self): def help_disconnect(self):
self.log("Disconnects from the printer") self.log("Disconnects from the printer")
def do_load(self,l): def do_load(self, filename):
self._do_load(l) self._do_load(filename)
def _do_load(self,l): def _do_load(self, filename):
if len(l)==0: if not filename:
self.log("No file name given.") self.log("No file name given.")
return return
self.log("Loading file:"+l) self.log("Loading file:" + filename)
if not(os.path.exists(l)): if not os.path.exists(filename):
self.log("File not found!") self.log("File not found!")
return return
self.f = [i.replace("\n", "").replace("\r", "") for i in open(l)] self.f = [line.strip() for line in open(filename)]
self.filename = l self.fgcode = gcoder.GCode(self.f)
self.log("Loaded ", l, ", ", len(self.f)," lines.") self.filename = filename
self.log("Loaded %s, %d lines." % (filename, len(self.f)))
def complete_load(self, text, line, begidx, endidx): def complete_load(self, text, line, begidx, endidx):
s = line.split() s = line.split()
...@@ -700,57 +755,45 @@ class pronsole(cmd.Cmd): ...@@ -700,57 +755,45 @@ class pronsole(cmd.Cmd):
self.log("Loads a gcode file (with tab-completion)") self.log("Loads a gcode file (with tab-completion)")
def do_upload(self, l): def do_upload(self, l):
if len(l) == 0: names = l.split()
self.log("No file name given.") if len(names) == 2:
return filename = names[0]
self.log("Loading file:"+l.split()[0]) targetname = names[1]
if not(os.path.exists(l.split()[0])): else:
self.log("File not found!") self.log(_("Please enter target name in 8.3 format."))
return return
if not self.p.online: if not self.p.online:
self.log("Not connected to printer.") self.log(_("Not connected to printer."))
return
self.f = [i.replace("\n", "") for i in open(l.split()[0])]
self.filename = l.split()[0]
self.log("Loaded ", l, ", ", len(self.f)," lines.")
tname = ""
if len(l.split())>1:
tname = l.split()[1]
else:
self.log("please enter target name in 8.3 format.")
return return
self.log("Uploading as ", tname) self._do_load(filename)
self.log(("Uploading "+self.filename)) self.log(_("Uploading as %s") % targetname)
self.p.send_now("M28 "+tname) self.log(_("Uploading %s") % self.filename)
self.log(("Press Ctrl-C to interrupt upload.")) self.p.send_now("M28 " + targetname)
self.p.startprint(self.f) self.log(_("Press Ctrl-C to interrupt upload."))
self.p.startprint(self.fgcode)
try: try:
sys.stdout.write("Progress: 00.0%") sys.stdout.write(_("Progress: ") + "00.0%")
sys.stdout.flush() sys.stdout.flush()
time.sleep(1) time.sleep(1)
while self.p.printing: while self.p.printing:
time.sleep(1) time.sleep(1)
sys.stdout.write("\b\b\b\b\b%04.1f%%" % (100*float(self.p.queueindex)/len(self.p.mainqueue),) ) sys.stdout.write("\b\b\b\b\b%04.1f%%" % (100*float(self.p.queueindex)/len(self.p.mainqueue),))
sys.stdout.flush() sys.stdout.flush()
self.p.send_now("M29 "+tname) self.p.send_now("M29 "+tname)
self.sleep(0.2) self.sleep(0.2)
self.p.clear = 1 self.p.clear = 1
self.listing = 0 self._do_ls(False)
self.sdfiles = [] self.log("\b\b\b\b\b100%.")
self.recvlisteners+=[self.listfiles] self.log(_("Upload completed. %s should now be on the card.") % targetname)
self.p.send_now("M20")
time.sleep(0.5)
self.log("\b\b\b\b\b100%. Upload completed. ", tname, " should now be on the card.")
return return
except: except:
self.log("...interrupted!") self.log(_("...interrupted!"))
self.p.pause() self.p.pause()
self.p.send_now("M29 "+tname) self.p.send_now("M29 "+targetname)
time.sleep(0.2) time.sleep(0.2)
self.p.clear = 1 self.p.clear = 1
self.p.startprint([]) self.p.startprint(None)
self.log("A partial file named ", tname, " may have been written to the sd card.") self.log(_("A partial file named %s may have been written to the sd card.") % targetname)
def complete_upload(self, text, line, begidx, endidx): def complete_upload(self, text, line, begidx, endidx):
s = line.split() s = line.split()
...@@ -766,44 +809,38 @@ class pronsole(cmd.Cmd): ...@@ -766,44 +809,38 @@ class pronsole(cmd.Cmd):
self.log("Uploads a gcode file to the sd card") self.log("Uploads a gcode file to the sd card")
def help_print(self): def help_print(self):
if self.f is None: if not self.fgcode:
self.log("Send a loaded gcode file to the printer. Load a file with the load command first.") self.log(_("Send a loaded gcode file to the printer. Load a file with the load command first."))
else: else:
self.log("Send a loaded gcode file to the printer. You have "+self.filename+" loaded right now.") self.log(_("Send a loaded gcode file to the printer. You have %s loaded right now.") % self.filename)
def do_print(self, l): def do_print(self, l):
if self.f is None: if not self.fgcode:
self.log("No file loaded. Please use load first.") self.log(_("No file loaded. Please use load first."))
return return
if not self.p.online: if not self.p.online:
self.log("Not connected to printer.") self.log(_("Not connected to printer."))
return return
self.log(("printing "+self.filename)) self.log(_("Printing %s") % self.filename)
self.log(("You can monitor the print with the monitor command.")) self.log(_("You can monitor the print with the monitor command."))
self.p.startprint(self.f) self.p.startprint(self.fgcode)
#self.p.pause()
#self.paused = True
#self.do_resume(None)
def do_pause(self, l): def do_pause(self, l):
if self.sdprinting: if self.sdprinting:
self.p.send_now("M25") self.p.send_now("M25")
else: else:
if(not self.p.printing): if not self.p.printing:
self.log("Not self.log(ing, cannot pause.") self.log(_("Not printing, cannot pause."))
return return
self.p.pause() self.p.pause()
#self.p.connect()# This seems to work, but is not a good solution.
self.paused = True self.paused = True
#self.do_resume(None)
def help_pause(self): def help_pause(self):
self.log("Pauses a running print") self.log(_("Pauses a running print"))
def do_resume(self, l): def do_resume(self, l):
if not self.paused: if not self.paused:
self.log("Not paused, unable to resume. Start a print first.") self.log(_("Not paused, unable to resume. Start a print first."))
return return
self.paused = False self.paused = False
if self.sdprinting: if self.sdprinting:
...@@ -813,7 +850,7 @@ class pronsole(cmd.Cmd): ...@@ -813,7 +850,7 @@ class pronsole(cmd.Cmd):
self.p.resume() self.p.resume()
def help_resume(self): def help_resume(self):
self.log("Resumes a paused print.") self.log(_("Resumes a paused print."))
def emptyline(self): def emptyline(self):
pass pass
...@@ -821,38 +858,43 @@ class pronsole(cmd.Cmd): ...@@ -821,38 +858,43 @@ class pronsole(cmd.Cmd):
def do_shell(self, l): def do_shell(self, l):
exec(l) exec(l)
def listfiles(self, line): def listfiles(self, line, echo = False):
if "Begin file list" in line: if "Begin file list" in line:
self.listing = 1 self.listing = 1
elif "End file list" in line: elif "End file list" in line:
self.listing = 0 self.listing = 0
self.recvlisteners.remove(self.listfiles) self.recvlisteners.remove(self.listfiles)
if echo:
self.log(_("Files on SD card:"))
self.log("\n".join(self.sdfiles))
elif self.listing: elif self.listing:
self.sdfiles+=[line.replace("\n", "").replace("\r", "").lower()] self.sdfiles.append(line.strip().lower())
def _do_ls(self, echo):
# FIXME: this was 2, but I think it should rather be 0 as in do_upload
self.listing = 0
self.sdfiles = []
self.recvlisteners.append(lambda l: self.listfiles(l, echo))
self.p.send_now("M20")
def do_ls(self, l): def do_ls(self, l):
if not self.p.online: if not self.p.online:
self.log("printer is not online. Try connect to it first.") self.log(_("Printer is not online. Please connect to it first."))
return return
self.listing = 2 self._do_ls(True)
self.sdfiles = []
self.recvlisteners+=[self.listfiles]
self.p.send_now("M20")
time.sleep(0.5)
self.log(" ".join(self.sdfiles))
def help_ls(self): def help_ls(self):
self.log("lists files on the SD card") self.log(_("Lists files on the SD card"))
def waitforsdresponse(self, l): def waitforsdresponse(self, l):
if "file.open failed" in l: if "file.open failed" in l:
self.log("Opening file failed.") self.log(_("Opening file failed."))
self.recvlisteners.remove(self.waitforsdresponse) self.recvlisteners.remove(self.waitforsdresponse)
return return
if "File opened" in l: if "File opened" in l:
self.log(l) self.log(l)
if "File selected" in l: if "File selected" in l:
self.log("Starting print") self.log(_("Starting print"))
self.p.send_now("M24") self.p.send_now("M24")
self.sdprinting = 1 self.sdprinting = 1
#self.recvlisteners.remove(self.waitforsdresponse) #self.recvlisteners.remove(self.waitforsdresponse)
...@@ -875,36 +917,33 @@ class pronsole(cmd.Cmd): ...@@ -875,36 +917,33 @@ class pronsole(cmd.Cmd):
self.p.reset() self.p.reset()
def help_reset(self): def help_reset(self):
self.log("Resets the printer.") self.log(_("Resets the printer."))
def do_sdprint(self, l): def do_sdprint(self, l):
if not self.p.online: if not self.p.online:
self.log("printer is not online. Try connect to it first.") self.log(_("Printer is not online. Please connect to it first."))
return return
self.listing = 2 self._do_ls(False)
self.sdfiles = [] while self.listfiles in self.recvlisteners:
self.recvlisteners+=[self.listfiles] time.sleep(0.1)
self.p.send_now("M20") if l.lower() not in self.sdfiles:
time.sleep(0.5) self.log(_("File is not present on card. Please upload it first."))
if not (l.lower() in self.sdfiles):
self.log("File is not present on card. Upload it first")
return return
self.recvlisteners+=[self.waitforsdresponse] self.recvlisteners.append(self.waitforsdresponse)
self.p.send_now("M23 "+l.lower()) self.p.send_now("M23 " + l.lower())
self.log("printing file: "+l.lower()+" from SD card.") self.log(_("Printing file: %s from SD card.") % l.lower())
self.log("Requesting SD print...") self.log(_("Requesting SD print..."))
time.sleep(1) time.sleep(1)
def help_sdprint(self): def help_sdprint(self):
self.log("print a file from the SD card. Tabcompletes with available file names.") self.log(_("Print a file from the SD card. Tab completes with available file names."))
self.log("sdprint filename.g") self.log(_("sdprint filename.g"))
def complete_sdprint(self, text, line, begidx, endidx): def complete_sdprint(self, text, line, begidx, endidx):
if self.sdfiles==[] and self.p.online: if not self.sdfiles and self.p.online:
self.listing = 2 self._do_ls(False)
self.recvlisteners+=[self.listfiles] while self.listfiles in self.recvlisteners:
self.p.send_now("M20") time.sleep(0.1)
time.sleep(0.5)
if (len(line.split()) == 2 and line[-1] != " ") or (len(line.split()) == 1 and line[-1]==" "): if (len(line.split()) == 2 and line[-1] != " ") or (len(line.split()) == 1 and line[-1]==" "):
return [i for i in self.sdfiles if i.startswith(text)] return [i for i in self.sdfiles if i.startswith(text)]
...@@ -927,19 +966,27 @@ class pronsole(cmd.Cmd): ...@@ -927,19 +966,27 @@ class pronsole(cmd.Cmd):
self.log("! os.listdir('.')") self.log("! os.listdir('.')")
def default(self, l): def default(self, l):
if(l[0] in self.commandprefixes.upper()): if l[0] in self.commandprefixes.upper():
if(self.p and self.p.online): if self.p and self.p.online:
if(not self.p.loud): if not self.p.loud:
self.log("SENDING:"+l) self.log("SENDING:"+l)
self.p.send_now(l) self.p.send_now(l)
else: else:
self.log("printer is not online.") self.log(_("Printer is not online."))
return return
elif(l[0] in self.commandprefixes.lower()): elif l[0] in self.commandprefixes.lower():
if(self.p and self.p.online): if self.p and self.p.online:
if(not self.p.loud): if not self.p.loud:
self.log("SENDING:"+l.upper()) self.log("SENDING:"+l.upper())
self.p.send_now(l.upper()) self.p.send_now(l.upper())
else:
self.log(_("Printer is not online."))
return
elif l[0] == "@":
if self.p and self.p.online:
if not self.p.loud:
self.log("SENDING:"+l[1:])
self.p.send_now(l[1:])
else: else:
self.log("printer is not online.") self.log("printer is not online.")
return return
...@@ -951,7 +998,7 @@ class pronsole(cmd.Cmd): ...@@ -951,7 +998,7 @@ class pronsole(cmd.Cmd):
def tempcb(self, l): def tempcb(self, l):
if "T:" in l: if "T:" in l:
self.log(l.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", "")) self.log(l.strip().replace("T", "Hotend").replace("B", "Bed").replace("ok ", ""))
def do_gettemp(self, l): def do_gettemp(self, l):
if "dynamic" in l: if "dynamic" in l:
...@@ -966,7 +1013,7 @@ class pronsole(cmd.Cmd): ...@@ -966,7 +1013,7 @@ class pronsole(cmd.Cmd):
print "Bed: %s/%s" % (self.status.bed_temp, self.status.bed_temp_target) print "Bed: %s/%s" % (self.status.bed_temp, self.status.bed_temp_target)
def help_gettemp(self): def help_gettemp(self):
self.log("Read the extruder and bed temperature.") self.log(_("Read the extruder and bed temperature."))
def do_settemp(self, l): def do_settemp(self, l):
try: try:
...@@ -976,22 +1023,22 @@ class pronsole(cmd.Cmd): ...@@ -976,22 +1023,22 @@ class pronsole(cmd.Cmd):
f = float(l) f = float(l)
if f>=0: if f>=0:
if f > 250: if f > 250:
print f, " is a high temperature to set your extruder to. Are you sure you want to do that?" print _("%s is a high temperature to set your extruder to. Are you sure you want to do that?") % f
if not confirm(): if not confirm():
return return
if self.p.online: if self.p.online:
self.p.send_now("M104 S"+l) self.p.send_now("M104 S"+l)
self.log("Setting hotend temperature to ", f, " degrees Celsius.") self.log(_("Setting hotend temperature to %s degrees Celsius.") % f)
else: else:
self.log("printer is not online.") self.log(_("Printer is not online."))
else: else:
self.log("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0.") self.log(_("You cannot set negative temperatures. To turn the hotend off entirely, set its temperature to 0."))
except: except:
self.log("You must enter a temperature.") self.log(_("You must enter a temperature."))
def help_settemp(self): def help_settemp(self):
self.log("Sets the hotend temperature to the value entered.") self.log(_("Sets the hotend temperature to the value entered."))
self.log("Enter either a temperature in celsius or one of the following keywords") self.log(_("Enter either a temperature in celsius or one of the following keywords"))
self.log(", ".join([i+"("+self.temps[i]+")" for i in self.temps.keys()])) self.log(", ".join([i+"("+self.temps[i]+")" for i in self.temps.keys()]))
def complete_settemp(self, text, line, begidx, endidx): def complete_settemp(self, text, line, begidx, endidx):
...@@ -1007,17 +1054,17 @@ class pronsole(cmd.Cmd): ...@@ -1007,17 +1054,17 @@ class pronsole(cmd.Cmd):
if f>=0: if f>=0:
if self.p.online: if self.p.online:
self.p.send_now("M140 S"+l) self.p.send_now("M140 S"+l)
self.log("Setting bed temperature to ", f, " degrees Celsius.") self.log(_("Setting bed temperature to %s degrees Celsius.") % f)
else: else:
self.log("printer is not online.") self.log(_("Printer is not online."))
else: else:
self.log("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0.") self.log(_("You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0."))
except: except:
self.log("You must enter a temperature.") self.log(_("You must enter a temperature."))
def help_bedtemp(self): def help_bedtemp(self):
self.log("Sets the bed temperature to the value entered.") self.log(_("Sets the bed temperature to the value entered."))
self.log("Enter either a temperature in celsius or one of the following keywords") self.log(_("Enter either a temperature in celsius or one of the following keywords"))
self.log(", ".join([i+"("+self.bedtemps[i]+")" for i in self.bedtemps.keys()])) self.log(", ".join([i+"("+self.bedtemps[i]+")" for i in self.bedtemps.keys()]))
def complete_bedtemp(self, text, line, begidx, endidx): def complete_bedtemp(self, text, line, begidx, endidx):
...@@ -1026,13 +1073,13 @@ class pronsole(cmd.Cmd): ...@@ -1026,13 +1073,13 @@ class pronsole(cmd.Cmd):
def do_move(self, l): def do_move(self, l):
if(len(l.split())<2): if(len(l.split())<2):
self.log("No move specified.") self.log(_("No move specified."))
return return
if self.p.printing: if self.p.printing:
self.log("printer is currently printing. Please pause the print before you issue manual commands.") self.log(_("Printer is currently printing. Please pause the print before you issue manual commands."))
return return
if not self.p.online: if not self.p.online:
self.log("printer is not online. Unable to move.") self.log(_("Printer is not online. Unable to move."))
return return
l = l.split() l = l.split()
if(l[0].lower()=="x"): if(l[0].lower()=="x"):
...@@ -1048,13 +1095,13 @@ class pronsole(cmd.Cmd): ...@@ -1048,13 +1095,13 @@ class pronsole(cmd.Cmd):
feed = self.settings.e_feedrate feed = self.settings.e_feedrate
axis = "E" axis = "E"
else: else:
self.log("Unknown axis.") self.log(_("Unknown axis."))
return return
dist = 0 dist = 0
try: try:
dist = float(l[1]) dist = float(l[1])
except: except:
self.log("Invalid distance") self.log(_("Invalid distance"))
return return
try: try:
feed = int(l[2]) feed = int(l[2])
...@@ -1065,11 +1112,11 @@ class pronsole(cmd.Cmd): ...@@ -1065,11 +1112,11 @@ class pronsole(cmd.Cmd):
self.p.send_now("G90") self.p.send_now("G90")
def help_move(self): def help_move(self):
self.log("Move an axis. Specify the name of the axis and the amount. ") self.log(_("Move an axis. Specify the name of the axis and the amount. "))
self.log("move X 10 will move the X axis forward by 10mm at ", self.settings.xy_feedrate, "mm/min (default XY speed)") self.log(_("move X 10 will move the X axis forward by 10mm at %s mm/min (default XY speed)") % self.settings.xy_feedrate)
self.log("move Y 10 5000 will move the Y axis forward by 10mm at 5000mm/min") self.log(_("move Y 10 5000 will move the Y axis forward by 10mm at 5000mm/min"))
self.log("move Z -1 will move the Z axis down by 1mm at ", self.settings.z_feedrate, "mm/min (default Z speed)") self.log(_("move Z -1 will move the Z axis down by 1mm at %s mm/min (default Z speed)") % self.settings.z_feedrate)
self.log("Common amounts are in the tabcomplete list.") self.log(_("Common amounts are in the tabcomplete list."))
def complete_move(self, text, line, begidx, endidx): def complete_move(self, text, line, begidx, endidx):
if (len(line.split()) == 2 and line[-1] != " ") or (len(line.split()) == 1 and line[-1]==" "): if (len(line.split()) == 2 and line[-1] != " ") or (len(line.split()) == 1 and line[-1]==" "):
...@@ -1089,70 +1136,70 @@ class pronsole(cmd.Cmd): ...@@ -1089,70 +1136,70 @@ class pronsole(cmd.Cmd):
length = 5#default extrusion length length = 5#default extrusion length
feed = self.settings.e_feedrate#default speed feed = self.settings.e_feedrate#default speed
if not self.p.online: if not self.p.online:
self.log("printer is not online. Unable to move.") self.log("Printer is not online. Unable to extrude.")
return return
if self.p.printing: if self.p.printing:
self.log("printer is currently printing. Please pause the print before you issue manual commands.") self.log("Printer is currently printing. Please pause the print before you issue manual commands.")
return return
ls = l.split() ls = l.split()
if len(ls): if len(ls):
try: try:
length = float(ls[0]) length = float(ls[0])
except: except:
self.log("Invalid length given.") self.log(_("Invalid length given."))
if len(ls)>1: if len(ls)>1:
try: try:
feed = int(ls[1]) feed = int(ls[1])
except: except:
self.log("Invalid speed given.") self.log(_("Invalid speed given."))
if override is not None: if override is not None:
length = override length = override
feed = overridefeed feed = overridefeed
if length > 0: if length > 0:
self.log("Extruding %fmm of filament."%(length,)) self.log(_("Extruding %fmm of filament.") % (length,))
elif length <0: elif length < 0:
self.log("Reversing %fmm of filament."%(-1*length,)) self.log(_("Reversing %fmm of filament.") % (-1*length,))
else: else:
"Length is 0, not doing anything." self.log(_("Length is 0, not doing anything."))
self.p.send_now("G91") self.p.send_now("G91")
self.p.send_now("G1 E"+str(length)+" F"+str(feed)) self.p.send_now("G1 E"+str(length)+" F"+str(feed))
self.p.send_now("G90") self.p.send_now("G90")
def help_extrude(self): def help_extrude(self):
self.log("Extrudes a length of filament, 5mm by default, or the number of mm given as a parameter") self.log(_("Extrudes a length of filament, 5mm by default, or the number of mm given as a parameter"))
self.log("extrude - extrudes 5mm of filament at 300mm/min (5mm/s)") self.log(_("extrude - extrudes 5mm of filament at 300mm/min (5mm/s)"))
self.log("extrude 20 - extrudes 20mm of filament at 300mm/min (5mm/s)") self.log(_("extrude 20 - extrudes 20mm of filament at 300mm/min (5mm/s)"))
self.log("extrude -5 - REVERSES 5mm of filament at 300mm/min (5mm/s)") self.log(_("extrude -5 - REVERSES 5mm of filament at 300mm/min (5mm/s)"))
self.log("extrude 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)") self.log(_("extrude 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"))
def do_reverse(self, l): def do_reverse(self, l):
length = 5#default extrusion length length = 5#default extrusion length
feed = self.settings.e_feedrate#default speed feed = self.settings.e_feedrate#default speed
if not self.p.online: if not self.p.online:
self.log("printer is not online. Unable to move.") self.log(_("Printer is not online. Unable to reverse."))
return return
if self.p.printing: if self.p.printing:
self.log("printer is currently printing. Please pause the print before you issue manual commands.") self.log(_("Printer is currently printing. Please pause the print before you issue manual commands."))
return return
ls = l.split() ls = l.split()
if len(ls): if len(ls):
try: try:
length = float(ls[0]) length = float(ls[0])
except: except:
self.log("Invalid length given.") self.log(_("Invalid length given."))
if len(ls)>1: if len(ls)>1:
try: try:
feed = int(ls[1]) feed = int(ls[1])
except: except:
self.log("Invalid speed given.") self.log(_("Invalid speed given."))
self.do_extrude("", length*-1.0, feed) self.do_extrude("", length*-1.0, feed)
def help_reverse(self): def help_reverse(self):
self.log("Reverses the extruder, 5mm by default, or the number of mm given as a parameter") self.log(_("Reverses the extruder, 5mm by default, or the number of mm given as a parameter"))
self.log("reverse - reverses 5mm of filament at 300mm/min (5mm/s)") self.log(_("reverse - reverses 5mm of filament at 300mm/min (5mm/s)"))
self.log("reverse 20 - reverses 20mm of filament at 300mm/min (5mm/s)") self.log(_("reverse 20 - reverses 20mm of filament at 300mm/min (5mm/s)"))
self.log("reverse 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)") self.log(_("reverse 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"))
self.log("reverse -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)") self.log(_("reverse -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)"))
def do_exit(self, l): def do_exit(self, l):
if self.status.extruder_temp_target != 0: if self.status.extruder_temp_target != 0:
...@@ -1169,28 +1216,28 @@ class pronsole(cmd.Cmd): ...@@ -1169,28 +1216,28 @@ class pronsole(cmd.Cmd):
print "(this will terminate the print)." print "(this will terminate the print)."
if not confirm(): if not confirm():
return False return False
self.log("Exiting program. Goodbye!") self.log(_("Exiting program. Goodbye!"))
self.p.disconnect() self.p.disconnect()
return True return True
def help_exit(self): def help_exit(self):
self.log("Disconnects from the printer and exits the program.") self.log(_("Disconnects from the printer and exits the program."))
def do_monitor(self, l): def do_monitor(self, l):
interval = 5 interval = 5
if not self.p.online: if not self.p.online:
self.log("printer is not online. Please connect first.") self.log(_("Printer is not online. Please connect to it first."))
return return
if not (self.p.printing or self.sdprinting): if not (self.p.printing or self.sdprinting):
self.log("Printer not printing. Please print something before monitoring.") self.log(_("Printer is not printing. Please print something before monitoring."))
return return
self.log("Monitoring printer, use ^C to interrupt.") self.log(_("Monitoring printer, use ^C to interrupt."))
if len(l): if len(l):
try: try:
interval = float(l) interval = float(l)
except: except:
self.log("Invalid period given.") self.log(_("Invalid period given."))
self.log("Updating values every %f seconds."%(interval,)) self.log(_("Updating values every %f seconds.") % (interval,))
self.monitoring = 1 self.monitoring = 1
prev_msg_len = 0 prev_msg_len = 0
try: try:
...@@ -1201,10 +1248,10 @@ class pronsole(cmd.Cmd): ...@@ -1201,10 +1248,10 @@ class pronsole(cmd.Cmd):
time.sleep(interval) time.sleep(interval)
#print (self.tempreadings.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", "")) #print (self.tempreadings.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", ""))
if self.p.printing: if self.p.printing:
preface = "Print progress: " preface = _("Print progress: ")
progress = 100*float(self.p.queueindex)/len(self.p.mainqueue) progress = 100*float(self.p.queueindex)/len(self.p.mainqueue)
elif self.sdprinting: elif self.sdprinting:
preface = "Print progress: " preface = _("Print progress: ")
progress = self.percentdone progress = self.percentdone
progress = int(progress*10)/10.0 #limit precision progress = int(progress*10)/10.0 #limit precision
prev_msg = preface + str(progress) + "%" prev_msg = preface + str(progress) + "%"
...@@ -1213,13 +1260,13 @@ class pronsole(cmd.Cmd): ...@@ -1213,13 +1260,13 @@ class pronsole(cmd.Cmd):
sys.stdout.flush() sys.stdout.flush()
prev_msg_len = len(prev_msg) prev_msg_len = len(prev_msg)
except KeyboardInterrupt: except KeyboardInterrupt:
if self.silent == False: print "Done monitoring." if self.silent == False: print _("Done monitoring.")
self.monitoring = 0 self.monitoring = 0
def help_monitor(self): def help_monitor(self):
self.log("Monitor a machine's temperatures and an SD print's status.") self.log(_("Monitor a machine's temperatures and an SD print's status."))
self.log("monitor - Reports temperature and SD print status (if SD printing) every 5 seconds") self.log(_("monitor - Reports temperature and SD print status (if SD printing) every 5 seconds"))
self.log("monitor 2 - Reports temperature and SD print status (if SD printing) every 2 seconds") self.log(_("monitor 2 - Reports temperature and SD print status (if SD printing) every 2 seconds"))
def expandcommand(self, c): def expandcommand(self, c):
return c.replace("$python", sys.executable) return c.replace("$python", sys.executable)
...@@ -1227,31 +1274,30 @@ class pronsole(cmd.Cmd): ...@@ -1227,31 +1274,30 @@ class pronsole(cmd.Cmd):
def do_skein(self, l): def do_skein(self, l):
l = l.split() l = l.split()
if len(l) == 0: if len(l) == 0:
self.log("No file name given.") self.log(_("No file name given."))
return return
settings = 0 settings = 0
if(l[0]=="set"): if(l[0]=="set"):
settings = 1 settings = 1
else: else:
self.log("Skeining file:"+l[0]) self.log(_("Skeining file: %s") % l[0])
if not(os.path.exists(l[0])): if not(os.path.exists(l[0])):
self.log("File not found!") self.log(_("File not found!"))
return return
try: try:
import shlex if settings:
if(settings):
param = self.expandcommand(self.settings.sliceoptscommand).replace("\\", "\\\\").encode() param = self.expandcommand(self.settings.sliceoptscommand).replace("\\", "\\\\").encode()
self.log("Entering slicer settings: ", param) self.log(_("Entering slicer settings: %s") % param)
subprocess.call(shlex.split(param)) subprocess.call(shlex.split(param))
else: else:
param = self.expandcommand(self.settings.slicecommand).encode() param = self.expandcommand(self.settings.slicecommand).encode()
self.log("Slicing: ", param) self.log(_("Slicing: ") % param)
params = [i.replace("$s", l[0]).replace("$o", l[0].replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())] params = [i.replace("$s", l[0]).replace("$o", l[0].replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
subprocess.call(params) subprocess.call(params)
self.log("Loading sliced file.") self.log(_("Loading sliced file."))
self.do_load(l[0].replace(".stl", "_export.gcode")) self.do_load(l[0].replace(".stl", "_export.gcode"))
except Exception, e: except Exception, e:
self.log("Skeinforge execution failed: ", e) self.log(_("Skeinforge execution failed: %s") % e)
def complete_skein(self, text, line, begidx, endidx): def complete_skein(self, text, line, begidx, endidx):
s = line.split() s = line.split()
...@@ -1264,18 +1310,17 @@ class pronsole(cmd.Cmd): ...@@ -1264,18 +1310,17 @@ class pronsole(cmd.Cmd):
return glob.glob("*/")+glob.glob("*.stl") return glob.glob("*/")+glob.glob("*.stl")
def help_skein(self): def help_skein(self):
self.log("Creates a gcode file from an stl model using the slicer (with tab-completion)") self.log(_("Creates a gcode file from an stl model using the slicer (with tab-completion)"))
self.log("skein filename.stl - create gcode file") self.log(_("skein filename.stl - create gcode file"))
self.log("skein filename.stl view - create gcode file and view using skeiniso") self.log(_("skein filename.stl view - create gcode file and view using skeiniso"))
self.log("skein set - adjust slicer settings") self.log(_("skein set - adjust slicer settings"))
def do_home(self, l): def do_home(self, l):
if not self.p.online: if not self.p.online:
self.log("printer is not online. Unable to move.") self.log(_("Printer is not online. Unable to move."))
return return
if self.p.printing: if self.p.printing:
self.log("printer is currently printing. Please pause the print before you issue manual commands.") self.log(_("Printer is currently printing. Please pause the print before you issue manual commands."))
return return
if "x" in l.lower(): if "x" in l.lower():
self.p.send_now("G28 X0") self.p.send_now("G28 X0")
...@@ -1290,35 +1335,36 @@ class pronsole(cmd.Cmd): ...@@ -1290,35 +1335,36 @@ class pronsole(cmd.Cmd):
self.p.send_now("G92 E0") self.p.send_now("G92 E0")
def help_home(self): def help_home(self):
self.log("Homes the printer") self.log(_("Homes the printer"))
self.log("home - homes all axes and zeroes the extruder(Using G28 and G92)") self.log(_("home - homes all axes and zeroes the extruder(Using G28 and G92)"))
self.log("home xy - homes x and y axes (Using G28)") self.log(_("home xy - homes x and y axes (Using G28)"))
self.log("home z - homes z axis only (Using G28)") self.log(_("home z - homes z axis only (Using G28)"))
self.log("home e - set extruder position to zero (Using G92)") self.log(_("home e - set extruder position to zero (Using G92)"))
self.log("home xyze - homes all axes and zeroes the extruder (Using G28 and G92)") self.log(_("home xyze - homes all axes and zeroes the extruder (Using G28 and G92)"))
def parse_cmdline(self, args): def add_cmdline_arguments(self, parser):
import getopt parser.add_argument('-c','--conf','--config', help = _("load this file on startup instead of .pronsolerc ; you may chain config files, if so settings auto-save will use the last specified file"), action = "append", default = [])
opts, args = getopt.getopt(args, "c:e:hw", ["conf = ", "config = ", "help"]) parser.add_argument('-e','--execute', help = _("executes command after configuration/.pronsolerc is loaded ; macros/settings from these commands are not autosaved"), action = "append", default = [])
for o, a in opts: parser.add_argument('filename', nargs='?', help = _("file to load"))
#self.log(repr((o, a)))
if o in ("-c", "--conf", "--config"): def process_cmdline_arguments(self, args):
self.load_rc(a) for config in args.conf:
elif o in ("-h", "--help"): self.load_rc(config)
self.log("Usage: "+sys.argv[0]+' [-c filename [-c filename2 ... ] ] [-e "command" ...]')
self.log(" -c | --conf | --config - override startup .pronsolerc file")
self.log(" may chain config files, settings auto-save will go into last file in the chain")
self.log(' -e <command> - executes command after configuration/.pronsolerc is loaded')
self.log(" macros/settings from these commands are not autosaved")
sys.exit()
if not self.rc_loaded: if not self.rc_loaded:
self.load_default_rc() self.load_default_rc()
for o, a in opts:
if o == "-e":
self.processing_args = True self.processing_args = True
self.onecmd(a) for command in args.execute:
self.onecmd(command)
self.processing_args = False self.processing_args = False
if args.filename:
self.do_load(args.filename)
def parse_cmdline(self, args):
parser = argparse.ArgumentParser(description = 'Printrun 3D printer interface')
self.add_cmdline_arguments(parser)
args = [arg for arg in args if not arg.startswith("-psn")]
args = parser.parse_args(args = args)
self.process_cmdline_arguments(args)
# We replace this function, defined in cmd.py . # We replace this function, defined in cmd.py .
# It's default behavior with reagrds to Ctr-C # It's default behavior with reagrds to Ctr-C
......
...@@ -26,6 +26,7 @@ except: ...@@ -26,6 +26,7 @@ except:
print _("WX is not installed. This program requires WX to run.") print _("WX is not installed. This program requires WX to run.")
raise raise
import sys, glob, time, datetime, threading, traceback, cStringIO, subprocess import sys, glob, time, datetime, threading, traceback, cStringIO, subprocess
import shlex
from printrun.pronterface_widgets import * from printrun.pronterface_widgets import *
from serial import SerialException from serial import SerialException
...@@ -45,9 +46,8 @@ import printcore ...@@ -45,9 +46,8 @@ import printcore
from printrun.printrun_utils import pixmapfile, configfile from printrun.printrun_utils import pixmapfile, configfile
from printrun.gui import MainWindow from printrun.gui import MainWindow
import pronsole import pronsole
from pronsole import dosify, wxSetting, HiddenSetting, StringSetting, SpinSetting, FloatSpinSetting, BooleanSetting
def dosify(name): from printrun import gcoder
return os.path.split(name)[1].split(".")[0][:8]+".g"
def parse_temperature_report(report, key): def parse_temperature_report(report, key):
if key in report: if key in report:
...@@ -81,30 +81,118 @@ class Tee(object): ...@@ -81,30 +81,118 @@ class Tee(object):
def flush(self): def flush(self):
self.stdout.flush() self.stdout.flush()
def parse_build_dimensions(bdim):
# a string containing up to six numbers delimited by almost anything
# first 0-3 numbers specify the build volume, no sign, always positive
# remaining 0-3 numbers specify the coordinates of the "southwest" corner of the build platform
# "XXX,YYY"
# "XXXxYYY+xxx-yyy"
# "XXX,YYY,ZZZ+xxx+yyy-zzz"
# etc
bdl = re.findall("([-+]?[0-9]*\.?[0-9]*)", bdim)
defaults = [200, 200, 100, 0, 0, 0, 0, 0, 0]
bdl = filter(None, bdl)
bdl_float = [float(value) if value else defaults[i] for i, value in enumerate(bdl)]
if len(bdl_float) < len(defaults):
bdl_float += [defaults[i] for i in range(len(bdl_float), len(defaults))]
return bdl_float
class BuildDimensionsSetting(wxSetting):
widgets = None
def _set_value(self, value):
self._value = value
if self.widgets:
self._set_widgets_values(value)
value = property(wxSetting._get_value, _set_value)
def _set_widgets_values(self, value):
build_dimensions_list = parse_build_dimensions(value)
for i in range(len(self.widgets)):
self.widgets[i].SetValue(build_dimensions_list[i])
def get_widget(self, parent):
from wx.lib.agw.floatspin import FloatSpin
import wx
build_dimensions = parse_build_dimensions(self.value)
self.widgets = []
w = lambda val, m, M: self.widgets.append(FloatSpin(parent, -1, value = val, min_val = m, max_val = M, digits = 2))
addlabel = lambda name, pos: self.widget.Add(wx.StaticText(parent, -1, name), pos = pos, flag = wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border = 5)
addwidget = lambda *pos: self.widget.Add(self.widgets[-1], pos = pos, flag = wx.RIGHT, border = 5)
self.widget = wx.GridBagSizer()
addlabel(_("Width"), (0, 0))
w(build_dimensions[0], 0, 2000)
addwidget(0, 1)
addlabel(_("Depth"), (0, 2))
w(build_dimensions[1], 0, 2000)
addwidget(0, 3)
addlabel(_("Height"), (0, 4))
w(build_dimensions[2], 0, 2000)
addwidget(0, 5)
addlabel(_("X offset"), (1, 0))
w(build_dimensions[3], -2000, 2000)
addwidget(1, 1)
addlabel(_("Y offset"), (1, 2))
w(build_dimensions[4], -2000, 2000)
addwidget(1, 3)
addlabel(_("Z offset"), (1, 4))
w(build_dimensions[5], -2000, 2000)
addwidget(1, 5)
addlabel(_("X home pos."), (2, 0))
w(build_dimensions[6], -2000, 2000)
self.widget.Add(self.widgets[-1], pos = (2, 1))
addlabel(_("Y home pos."), (2, 2))
w(build_dimensions[7], -2000, 2000)
self.widget.Add(self.widgets[-1], pos = (2, 3))
addlabel(_("Z home pos."), (2, 4))
w(build_dimensions[8], -2000, 2000)
self.widget.Add(self.widgets[-1], pos = (2, 5))
return self.widget
def update(self):
values = [float(w.GetValue()) for w in self.widgets]
self.value = "%.02fx%.02fx%.02f%+.02f%+.02f%+.02f%+.02f%+.02f%+.02f" % tuple(values)
class StringSetting(wxSetting):
def get_specific_widget(self, parent):
import wx
self.widget = wx.TextCtrl(parent, -1, str(self.value))
return self.widget
class ComboSetting(wxSetting):
def __init__(self, name, default, choices, label = None, help = None):
super(ComboSetting, self).__init__(name, default, label, help)
self.choices = choices
def get_specific_widget(self, parent):
import wx
self.widget = wx.ComboBox(parent, -1, str(self.value), choices = self.choices, style = wx.CB_DROPDOWN)
return self.widget
class PronterWindow(MainWindow, pronsole.pronsole): class PronterWindow(MainWindow, pronsole.pronsole):
def __init__(self, filename = None, size = winsize): def __init__(self, filename = None, size = winsize):
pronsole.pronsole.__init__(self) pronsole.pronsole.__init__(self)
self.settings.build_dimensions = '200x200x100+0+0+0+0+0+0' #default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed #default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed and endstops at 0, 0 and 0
self.settings.last_bed_temperature = 0.0 monitorsetting = BooleanSetting("monitor", False)
self.settings.last_file_path = "" monitorsetting.hidden = True
self.settings.last_temperature = 0.0 self.settings._add(monitorsetting)
self.settings.preview_extrusion_width = 0.5 self.settings._add(BuildDimensionsSetting("build_dimensions", "200x200x100+0+0+0+0+0+0", _("Build dimensions"), _("Dimensions of Build Platform\n & optional offset of origin\n & optional switch position\n\nExamples:\n XXXxYYY\n XXX,YYY,ZZZ\n XXXxYYYxZZZ+OffX+OffY+OffZ\nXXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ")))
self.settings.preview_grid_step1 = 10. self.settings._add(BooleanSetting("viz3d", False, _("Enable 3D viewer (requires restarting)"), _("Use 3D visualization instead of 2D layered visualization")))
self.settings.preview_grid_step2 = 50. self.settings._add(ComboSetting("mainviz", "2D", ["2D", "3D", "None"], _("Main visualization"), _("Select visualization for main window.")))
self.settings.bgcolor = "#FFFFFF" self.settings._add(HiddenSetting("last_bed_temperature", 0.0))
self.settings._add(HiddenSetting("last_file_path", ""))
self.settings._add(HiddenSetting("last_temperature", 0.0))
self.settings._add(FloatSpinSetting("preview_extrusion_width", 0.5, 0, 10, _("Preview extrusion width"), _("Width of Extrusion in Preview (default: 0.5)")))
self.settings._add(SpinSetting("preview_grid_step1", 10., 0, 200, _("Fine grid spacing"), _("Fine Grid Spacing (default: 10)")))
self.settings._add(SpinSetting("preview_grid_step2", 50., 0, 200, _("Coarse grid spacing"), _("Coarse Grid Spacing (default: 50)")))
self.settings._add(StringSetting("bgcolor", "#FFFFFF", _("Background color"), _("Pronterface background color (default: #FFFFFF)")))
self.pauseScript = "pause.gcode" self.pauseScript = "pause.gcode"
self.endScript = "end.gcode" self.endScript = "end.gcode"
self.helpdict["build_dimensions"] = _("Dimensions of Build Platform\n & optional offset of origin\n & optional switch position\n\nExamples:\n XXXxYYY\n XXX,YYY,ZZZ\n XXXxYYYxZZZ+OffX+OffY+OffZ\nXXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ")
self.helpdict["last_bed_temperature"] = _("Last Set Temperature for the Heated Print Bed")
self.helpdict["last_file_path"] = _("Folder of last opened file")
self.helpdict["last_temperature"] = _("Last Temperature of the Hot End")
self.helpdict["preview_extrusion_width"] = _("Width of Extrusion in Preview (default: 0.5)")
self.helpdict["preview_grid_step1"] = _("Fine Grid Spacing (default: 10)")
self.helpdict["preview_grid_step2"] = _("Coarse Grid Spacing (default: 50)")
self.helpdict["bgcolor"] = _("Pronterface background color (default: #FFFFFF)")
self.filename = filename self.filename = filename
os.putenv("UBUNTU_MENUPROXY", "0") os.putenv("UBUNTU_MENUPROXY", "0")
MainWindow.__init__(self, None, title = _("Printer Interface"), size = size); MainWindow.__init__(self, None, title = _("Printer Interface"), size = size);
...@@ -120,8 +208,9 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -120,8 +208,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.f = None self.f = None
self.skeinp = None self.skeinp = None
self.monitor_interval = 3 self.monitor_interval = 3
self.current_pos = [0, 0, 0]
self.paused = False self.paused = False
self.sentlines = Queue.Queue(30) self.sentlines = Queue.Queue(0)
self.cpbuttons = [ self.cpbuttons = [
SpecialButton(_("Motors off"), ("M84"), (250, 250, 250), None, 0, _("Switch all motors off")), SpecialButton(_("Motors off"), ("M84"), (250, 250, 250), None, 0, _("Switch all motors off")),
SpecialButton(_("Check temp"), ("M105"), (225, 200, 200), (2, 5), (1, 1), _("Check current hotend temperature")), SpecialButton(_("Check temp"), ("M105"), (225, 200, 200), (2, 5), (1, 1), _("Check current hotend temperature")),
...@@ -130,8 +219,9 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -130,8 +219,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
] ]
self.custombuttons = [] self.custombuttons = []
self.btndict = {} self.btndict = {}
self.autoconnect = False
self.parse_cmdline(sys.argv[1:]) self.parse_cmdline(sys.argv[1:])
self.build_dimensions_list = self.get_build_dimensions(self.settings.build_dimensions) self.build_dimensions_list = parse_build_dimensions(self.settings.build_dimensions)
#initialize the code analyzer with the correct sizes. There must be a more general way to do so #initialize the code analyzer with the correct sizes. There must be a more general way to do so
...@@ -185,6 +275,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -185,6 +275,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skeining = 0 self.skeining = 0
self.mini = False self.mini = False
self.p.sendcb = self.sentcb self.p.sendcb = self.sentcb
self.p.printsendcb = self.printsentcb
self.p.startcb = self.startcb self.p.startcb = self.startcb
self.p.endcb = self.endcb self.p.endcb = self.endcb
self.starttime = 0 self.starttime = 0
...@@ -196,18 +287,32 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -196,18 +287,32 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.predisconnect_layer = None self.predisconnect_layer = None
self.hsetpoint = 0.0 self.hsetpoint = 0.0
self.bsetpoint = 0.0 self.bsetpoint = 0.0
if self.autoconnect:
self.connect()
if self.filename is not None: if self.filename is not None:
self.do_load(self.filename) self.do_load(self.filename)
if self.settings.monitor:
self.setmonitor(None)
def add_cmdline_arguments(self, parser):
pronsole.pronsole.add_cmdline_arguments(self, parser)
parser.add_argument('-g','--gauges', help = _("display graphical temperature gauges in addition to the temperatures graph"), action = "store_true")
parser.add_argument('-a','--autoconnect', help = _("automatically try to connect to printer on startup"), action = "store_true")
def process_cmdline_arguments(self, args):
pronsole.pronsole.process_cmdline_arguments(self, args)
self.display_gauges = args.gauges
self.autoconnect = args.autoconnect
def startcb(self): def startcb(self):
self.starttime = time.time() self.starttime = time.time()
print "Print Started at: " + format_time(self.starttime) print _("Print Started at: %s") % format_time(self.starttime)
def endcb(self): def endcb(self):
if self.p.queueindex == 0: if self.p.queueindex == 0:
print "Print ended at: " + format_time(time.time())
print_duration = int(time.time () - self.starttime + self.extra_print_time) print_duration = int(time.time () - self.starttime + self.extra_print_time)
print "and took: " + format_duration(print_duration) print _("Print ended at: %(end_time)s and took %(duration)s") % {"end_time": format_time(time.time()),
"duration": format_duration(print_duration)}
wx.CallAfter(self.pausebtn.Disable) wx.CallAfter(self.pausebtn.Disable)
wx.CallAfter(self.printbtn.SetLabel, _("Print")) wx.CallAfter(self.printbtn.SetLabel, _("Print"))
...@@ -216,7 +321,6 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -216,7 +321,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
param = self.settings.final_command param = self.settings.final_command
if not param: if not param:
return return
import shlex
pararray = [i.replace("$s", str(self.filename)).replace("$t", format_duration(print_duration)).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())] pararray = [i.replace("$s", str(self.filename)).replace("$t", format_duration(print_duration)).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
self.finalp = subprocess.Popen(pararray, stderr = subprocess.STDOUT, stdout = subprocess.PIPE) self.finalp = subprocess.Popen(pararray, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
...@@ -237,44 +341,36 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -237,44 +341,36 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.printbtn.Enable) wx.CallAfter(self.printbtn.Enable)
def sentcb(self, line): def sentcb(self, line):
if "G1" in line: gline = gcoder.Line(line)
if "Z" in line: gline.parse_coordinates(imperial = False)
try: if gline.is_move:
layer = float(line.split("Z")[1].split()[0]) if gline.z != None:
layer = gline.z
if layer != self.curlayer: if layer != self.curlayer:
self.curlayer = layer self.curlayer = layer
self.gviz.hilight = [] self.gviz.clearhilights()
threading.Thread(target = wx.CallAfter, args = (self.gviz.setlayer, layer)).start() wx.CallAfter(self.gviz.setlayer, layer)
except: elif gline.command in ["M104", "M109"]:
pass gline.parse_coordinates(imperial = False, force = True)
try: if gline.s != None:
self.sentlines.put_nowait(line) temp = gline.s
except: if self.display_gauges: wx.CallAfter(self.hottgauge.SetTarget, temp)
pass
#threading.Thread(target = self.gviz.addgcode, args = (line, 1)).start()
#self.gwindow.p.addgcode(line, hilight = 1)
if "M104" in line or "M109" in line:
if "S" in line:
try:
temp = float(line.split("S")[1].split("*")[0])
wx.CallAfter(self.graph.SetExtruder0TargetTemperature, temp) wx.CallAfter(self.graph.SetExtruder0TargetTemperature, temp)
except: elif gline.command == "M140":
pass gline.parse_coordinates(imperial = False, force = True)
try: if gline.s != None:
self.sentlines.put_nowait(line) temp = gline.s
except: if self.display_gauges: wx.CallAfter(self.bedtgauge.SetTarget, temp)
pass
if "M140" in line:
if "S" in line:
try:
temp = float(line.split("S")[1].split("*")[0])
wx.CallAfter(self.graph.SetBedTargetTemperature, temp) wx.CallAfter(self.graph.SetBedTargetTemperature, temp)
except: else:
pass return
try:
self.sentlines.put_nowait(line) self.sentlines.put_nowait(line)
except:
pass def printsentcb(self, gline):
if gline.is_move and hasattr(self.gwindow, "set_current_gline"):
wx.CallAfter(self.gwindow.set_current_gline, gline)
if gline.is_move and hasattr(self.gviz, "set_current_gline"):
wx.CallAfter(self.gviz.set_current_gline, gline)
def do_extrude(self, l = ""): def do_extrude(self, l = ""):
try: try:
...@@ -294,6 +390,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -294,6 +390,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def setbedgui(self, f): def setbedgui(self, f):
self.bsetpoint = f self.bsetpoint = f
if self.display_gauges: self.bedtgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetBedTargetTemperature, int(f)) wx.CallAfter(self.graph.SetBedTargetTemperature, int(f))
if f>0: if f>0:
wx.CallAfter(self.btemp.SetValue, str(f)) wx.CallAfter(self.btemp.SetValue, str(f))
...@@ -313,6 +410,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -313,6 +410,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def sethotendgui(self, f): def sethotendgui(self, f):
self.hsetpoint = f self.hsetpoint = f
if self.display_gauges: self.hottgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetExtruder0TargetTemperature, int(f)) wx.CallAfter(self.graph.SetExtruder0TargetTemperature, int(f))
if f > 0: if f > 0:
wx.CallAfter(self.htemp.SetValue, str(f)) wx.CallAfter(self.htemp.SetValue, str(f))
...@@ -444,7 +542,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -444,7 +542,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.macros_menu = wx.Menu() self.macros_menu = wx.Menu()
m.AppendSubMenu(self.macros_menu, _("&Macros")) m.AppendSubMenu(self.macros_menu, _("&Macros"))
self.Bind(wx.EVT_MENU, self.new_macro, self.macros_menu.Append(-1, _("<&New...>"))) 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"))) self.Bind(wx.EVT_MENU, lambda *e: PronterOptions(self), m.Append(-1, _("&Options"), _(" Options dialog")))
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")))
...@@ -574,7 +672,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -574,7 +672,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
obj = e.GetEventObject() obj = e.GetEventObject()
popupmenu = wx.Menu() popupmenu = wx.Menu()
item = popupmenu.Append(-1, _("SD Upload")) item = popupmenu.Append(-1, _("SD Upload"))
if not self.f or not len(self.f): if not self.f:
item.Enable(False) item.Enable(False)
self.Bind(wx.EVT_MENU, self.upload, id = item.GetId()) self.Bind(wx.EVT_MENU, self.upload, id = item.GetId())
item = popupmenu.Append(-1, _("SD Print")) item = popupmenu.Append(-1, _("SD Print"))
...@@ -592,7 +690,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -592,7 +690,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.btemp.SetInsertionPoint, 0) wx.CallAfter(self.btemp.SetInsertionPoint, 0)
def showwin(self, event): def showwin(self, event):
if(self.f is not None): if self.f:
self.gwindow.Show(True) self.gwindow.Show(True)
self.gwindow.SetToolTip(wx.ToolTip("Mousewheel zooms the display\nShift / Mousewheel scrolls layers")) self.gwindow.SetToolTip(wx.ToolTip("Mousewheel zooms the display\nShift / Mousewheel scrolls layers"))
self.gwindow.Raise() self.gwindow.Raise()
...@@ -762,8 +860,8 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -762,8 +860,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if bedit.color.GetValue().strip()!="": if bedit.color.GetValue().strip()!="":
self.custombuttons[n].background = bedit.color.GetValue() self.custombuttons[n].background = bedit.color.GetValue()
self.cbutton_save(n, self.custombuttons[n]) self.cbutton_save(n, self.custombuttons[n])
bedit.Destroy() wx.CallAfter(bedit.Destroy)
self.cbuttons_reload() wx.CallAfter(self.cbuttons_reload)
def cbutton_remove(self, e, button): def cbutton_remove(self, e, button):
n = button.custombutton n = button.custombutton
...@@ -785,7 +883,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -785,7 +883,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.cbutton_save(n+1, self.custombuttons[n+1]) self.cbutton_save(n+1, self.custombuttons[n+1])
#if self.custombuttons[-1] is None: #if self.custombuttons[-1] is None:
# del self.custombuttons[-1] # del self.custombuttons[-1]
self.cbuttons_reload() wx.CallAfter(self.cbuttons_reload)
def editbutton(self, e): def editbutton(self, e):
if e.IsCommandEvent() or e.ButtonUp(wx.MOUSE_BTN_RIGHT): if e.IsCommandEvent() or e.ButtonUp(wx.MOUSE_BTN_RIGHT):
...@@ -932,28 +1030,35 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -932,28 +1030,35 @@ class PronterWindow(MainWindow, pronsole.pronsole):
e.Skip() e.Skip()
def homeButtonClicked(self, corner): def homeButtonClicked(self, corner):
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
if corner == 0: # upper-left if corner == 0: # upper-left
self.onecmd('home X') self.onecmd('home X')
if corner == 1: # upper-right elif corner == 1: # upper-right
self.onecmd('home Y') self.onecmd('home Y')
if corner == 2: # lower-right elif corner == 2: # lower-right
self.onecmd('home Z') self.onecmd('home Z')
if corner == 3: # lower-left elif corner == 3: # lower-left
self.onecmd('home') self.onecmd('home')
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals else:
self.zb.clearRepeat() return
self.p.send_now('M114')
def moveXY(self, x, y): def moveXY(self, x, y):
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
if x != 0: if x != 0:
self.onecmd('move X %s' % x) self.onecmd('move X %s' % x)
if y != 0: elif y != 0:
self.onecmd('move Y %s' % y) self.onecmd('move Y %s' % y)
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals else:
self.zb.clearRepeat() return
self.p.send_now('M114')
def moveZ(self, z): def moveZ(self, z):
if z != 0: if z != 0:
self.onecmd('move Z %s' % z) self.onecmd('move Z %s' % z)
self.p.send_now('M114')
# When user clicks on the Z control, the XY control no longer gets spacebar/repeat signals # When user clicks on the Z control, the XY control no longer gets spacebar/repeat signals
self.xyb.clearRepeat() self.xyb.clearRepeat()
...@@ -985,11 +1090,8 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -985,11 +1090,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.save_in_rc("set xy_feedrate", "set xy_feedrate %d" % self.settings.xy_feedrate) self.save_in_rc("set xy_feedrate", "set xy_feedrate %d" % self.settings.xy_feedrate)
self.save_in_rc("set z_feedrate", "set z_feedrate %d" % self.settings.z_feedrate) self.save_in_rc("set z_feedrate", "set z_feedrate %d" % self.settings.z_feedrate)
self.save_in_rc("set e_feedrate", "set e_feedrate %d" % self.settings.e_feedrate) self.save_in_rc("set e_feedrate", "set e_feedrate %d" % self.settings.e_feedrate)
try: wx.CallAfter(self.gwindow.Destroy)
self.gwindow.Destroy() wx.CallAfter(self.Destroy)
except:
pass
self.Destroy()
def do_monitor(self, l = ""): def do_monitor(self, l = ""):
if l.strip()=="": if l.strip()=="":
...@@ -1010,6 +1112,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1010,6 +1112,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def setmonitor(self, e): def setmonitor(self, e):
self.monitor = self.monitorbox.GetValue() self.monitor = self.monitorbox.GetValue()
self.set("monitor", self.monitor)
if self.monitor: if self.monitor:
wx.CallAfter(self.graph.StartPlotting, 1000) wx.CallAfter(self.graph.StartPlotting, 1000)
else: else:
...@@ -1019,8 +1122,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1019,8 +1122,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
try: try:
self.logbox.AppendText(text) self.logbox.AppendText(text)
except: except:
print "attempted to write invalid text to console" print _("Attempted to write invalid text to console, which could be due to an invalid baudrate")
pass
def setloud(self,e): def setloud(self,e):
self.p.loud=e.IsChecked() self.p.loud=e.IsChecked()
...@@ -1032,21 +1134,42 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1032,21 +1134,42 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.addtexttolog, ">>>" + command + "\n"); wx.CallAfter(self.addtexttolog, ">>>" + command + "\n");
self.onecmd(str(command)) self.onecmd(str(command))
self.commandbox.SetSelection(0, len(command)) self.commandbox.SetSelection(0, len(command))
self.commandbox.history+=[command] self.commandbox.history.append(command)
self.commandbox.histindex = len(self.commandbox.history) self.commandbox.histindex = len(self.commandbox.history)
def clearOutput(self, e): def clearOutput(self, e):
self.logbox.Clear() self.logbox.Clear()
def update_tempdisplay(self):
try:
hotend_temp = parse_temperature_report(self.tempreport, "T:")
wx.CallAfter(self.graph.SetExtruder0Temperature, hotend_temp)
if self.display_gauges: wx.CallAfter(self.hottgauge.SetValue, hotend_temp)
bed_temp = parse_temperature_report(self.tempreport, "B:")
wx.CallAfter(self.graph.SetBedTemperature, bed_temp)
if self.display_gauges: wx.CallAfter(self.bedtgauge.SetValue, bed_temp)
except:
traceback.print_exc()
def update_pos(self, l):
bits = gcoder.m114_exp.findall(l)
x = None
y = None
z = None
for bit in bits:
if x is None and bit.startswith("X"):
x = float(bit[1:].replace(":",""))
elif y is None and bit.startswith("Y"):
y = float(bit[1:].replace(":",""))
elif z is None and bit.startswith("Z"):
z = float(bit[1:].replace(":",""))
if x is not None: self.current_pos[0] = x
if y is not None: self.current_pos[1] = y
if z is not None: self.current_pos[2] = z
def statuschecker(self): def statuschecker(self):
while self.statuscheck: while self.statuscheck:
string = "" string = ""
wx.CallAfter(self.tempdisp.SetLabel, self.tempreport.strip().replace("ok ", ""))
try:
wx.CallAfter(self.graph.SetExtruder0Temperature, parse_temperature_report(self.tempreport, "T:"))
wx.CallAfter(self.graph.SetBedTemperature, parse_temperature_report(self.tempreport, "B:"))
except:
pass
fractioncomplete = 0.0 fractioncomplete = 0.0
if self.sdprinting: if self.sdprinting:
fractioncomplete = float(self.percentdone / 100.0) fractioncomplete = float(self.percentdone / 100.0)
...@@ -1077,11 +1200,8 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1077,11 +1200,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
break break
time.sleep(0.25) time.sleep(0.25)
while not self.sentlines.empty(): while not self.sentlines.empty():
try:
gc = self.sentlines.get_nowait() gc = self.sentlines.get_nowait()
wx.CallAfter(self.gviz.addgcode, gc, 1) wx.CallAfter(self.gviz.addgcode, gc, 1)
except:
break
wx.CallAfter(self.status.SetStatusText, _("Not connected to printer.")) wx.CallAfter(self.status.SetStatusText, _("Not connected to printer."))
def capture(self, func, *args, **kwargs): def capture(self, func, *args, **kwargs):
...@@ -1104,24 +1224,23 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1104,24 +1224,23 @@ class PronterWindow(MainWindow, pronsole.pronsole):
return retval return retval
def recvcb(self, l): def recvcb(self, l):
if "T:" in l: isreport = False
if "ok C:" in l or "Count" in l:
self.posreport = l
self.update_pos(l)
isreport = True
if "ok T:" in l:
self.tempreport = l self.tempreport = l
wx.CallAfter(self.tempdisp.SetLabel, self.tempreport.strip().replace("ok ", "")) wx.CallAfter(self.tempdisp.SetLabel, self.tempreport.strip().replace("ok ", ""))
try: self.update_tempdisplay()
wx.CallAfter(self.graph.SetExtruder0Temperature, parse_temperature_report(self.tempreport, "T:")) isreport = True
wx.CallAfter(self.graph.SetBedTemperature, parse_temperature_report(self.tempreport, "B:"))
except:
traceback.print_exc()
tstring = l.rstrip() tstring = l.rstrip()
#print tstring if self.p.loud or (tstring not in ["ok", "wait"] and not isreport):
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
wx.CallAfter(self.addtexttolog, tstring + "\n"); wx.CallAfter(self.addtexttolog, tstring + "\n");
for i in self.recvlisteners: for listener in self.recvlisteners:
i(l) listener(l)
def listfiles(self, line): def listfiles(self, line, ignored = False):
if "Begin file list" in line: if "Begin file list" in line:
self.listing = 1 self.listing = 1
elif "End file list" in line: elif "End file list" in line:
...@@ -1129,7 +1248,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1129,7 +1248,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.recvlisteners.remove(self.listfiles) self.recvlisteners.remove(self.listfiles)
wx.CallAfter(self.filesloaded) wx.CallAfter(self.filesloaded)
elif self.listing: elif self.listing:
self.sdfiles+=[line.replace("\n", "").replace("\r", "").lower()] self.sdfiles.append(line.strip().lower())
def waitforsdresponse(self, l): def waitforsdresponse(self, l):
if "file.open failed" in l: if "file.open failed" in l:
...@@ -1166,6 +1285,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1166,6 +1285,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if len(target): if len(target):
self.recvlisteners+=[self.waitforsdresponse] self.recvlisteners+=[self.waitforsdresponse]
self.p.send_now("M23 "+target.lower()) self.p.send_now("M23 "+target.lower())
dlg.Destroy()
#print self.sdfiles #print self.sdfiles
def getfiles(self): def getfiles(self):
...@@ -1180,7 +1300,6 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1180,7 +1300,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def skein_func(self): def skein_func(self):
try: try:
import shlex
param = self.expandcommand(self.settings.slicecommand).encode() param = self.expandcommand(self.settings.slicecommand).encode()
print "Slicing: ", param print "Slicing: ", param
pararray = [i.replace("$s", self.filename).replace("$o", self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())] pararray = [i.replace("$s", self.filename).replace("$o", self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode")).encode() for i in shlex.split(param.replace("\\", "\\\\").encode())]
...@@ -1207,13 +1326,13 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1207,13 +1326,13 @@ class PronterWindow(MainWindow, pronsole.pronsole):
fn = self.filename fn = self.filename
try: try:
self.filename = self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode").replace(".obj", "_export.gcode").replace(".OBJ", "_export.gcode") self.filename = self.filename.replace(".stl", "_export.gcode").replace(".STL", "_export.gcode").replace(".obj", "_export.gcode").replace(".OBJ", "_export.gcode")
of = open(self.filename) self.f = [line.strip() for line in open(self.filename)]
self.f = [i.replace("\n", "").replace("\r", "") for i in of] self.fgcode = gcoder.GCode(self.f)
of.close()
if self.p.online: if self.p.online:
wx.CallAfter(self.printbtn.Enable) wx.CallAfter(self.printbtn.Enable)
wx.CallAfter(self.status.SetStatusText, _("Loaded ")+self.filename+_(", %d lines") % (len(self.f),)) wx.CallAfter(self.status.SetStatusText, _("Loaded %s, %d lines") % (self.filename, len(self.f),))
print _("Loaded %s, %d lines") % (self.filename, len(self.f),)
wx.CallAfter(self.pausebtn.Disable) wx.CallAfter(self.pausebtn.Disable)
wx.CallAfter(self.printbtn.SetLabel, _("Print")) wx.CallAfter(self.printbtn.SetLabel, _("Print"))
...@@ -1251,6 +1370,8 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1251,6 +1370,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
basedir = os.path.split(self.filename)[0] basedir = os.path.split(self.filename)[0]
except: except:
pass pass
dlg=None
if filename is None:
dlg = wx.FileDialog(self, _("Open file to print"), basedir, style = wx.FD_OPEN|wx.FD_FILE_MUST_EXIST) dlg = wx.FileDialog(self, _("Open file to print"), basedir, style = wx.FD_OPEN|wx.FD_FILE_MUST_EXIST)
dlg.SetWildcard(_("OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*")) dlg.SetWildcard(_("OBJ, STL, and GCODE files (*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ)|*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|All Files (*.*)|*.*"))
if(filename is not None or dlg.ShowModal() == wx.ID_OK): if(filename is not None or dlg.ShowModal() == wx.ID_OK):
...@@ -1260,6 +1381,8 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1260,6 +1381,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
name = dlg.GetPath() name = dlg.GetPath()
if not(os.path.exists(name)): if not(os.path.exists(name)):
self.status.SetStatusText(_("File not found!")) self.status.SetStatusText(_("File not found!"))
if dlg is not None:
dlg.Destroy()
return return
path = os.path.split(name)[0] path = os.path.split(name)[0]
if path != self.settings.last_file_path: if path != self.settings.last_file_path:
...@@ -1270,10 +1393,10 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1270,10 +1393,10 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skein(name) self.skein(name)
else: else:
self.filename = name self.filename = name
of = open(self.filename) self.f = [line.strip() for line in open(self.filename)]
self.f = [i.replace("\n", "").replace("\r", "") for i in of] self.fgcode = gcoder.GCode(self.f)
of.close()
self.status.SetStatusText(_("Loaded %s, %d lines") % (name, len(self.f))) self.status.SetStatusText(_("Loaded %s, %d lines") % (name, len(self.f)))
print _("Loaded %s, %d lines") % (name, len(self.f))
wx.CallAfter(self.printbtn.SetLabel, _("Print")) wx.CallAfter(self.printbtn.SetLabel, _("Print"))
wx.CallAfter(self.pausebtn.SetLabel, _("Pause")) wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
wx.CallAfter(self.pausebtn.Disable) wx.CallAfter(self.pausebtn.Disable)
...@@ -1281,26 +1404,21 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1281,26 +1404,21 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if self.p.online: if self.p.online:
wx.CallAfter(self.printbtn.Enable) wx.CallAfter(self.printbtn.Enable)
threading.Thread(target = self.loadviz).start() threading.Thread(target = self.loadviz).start()
if dlg is not None:
dlg.Destroy()
def loadviz(self): def loadviz(self):
Xtot, Ytot, Ztot, Xmin, Xmax, Ymin, Ymax, Zmin, Zmax = pronsole.measurements(self.f) gcode = self.fgcode
print pronsole.totalelength(self.f), _("mm of filament used in this print\n") print gcode.filament_length, _("mm of filament used in this print")
print _("the print goes from %f mm to %f mm in X\nand is %f mm wide\n") % (Xmin, Xmax, Xtot) print _("The print goes:")
print _("the print goes from %f mm to %f mm in Y\nand is %f mm wide\n") % (Ymin, Ymax, Ytot) print _("- from %.2f mm to %.2f mm in X and is %.2f mm wide") % (gcode.xmin, gcode.xmax, gcode.width)
print _("the print goes from %f mm to %f mm in Z\nand is %f mm high\n") % (Zmin, Zmax, Ztot) print _("- from %.2f mm to %.2f mm in Y and is %.2f mm deep") % (gcode.ymin, gcode.ymax, gcode.depth)
try: print _("- from %.2f mm to %.2f mm in Z and is %.2f mm high") % (gcode.zmin, gcode.zmax, gcode.height)
print _("Estimated duration (pessimistic): "), pronsole.estimate_duration(self.f) print _("Estimated duration: %s") % gcode.estimate_duration()
except:
pass
#import time
#t0 = time.time()
self.gviz.clear() self.gviz.clear()
self.gwindow.p.clear() self.gwindow.p.clear()
self.gviz.addfile(self.f) self.gviz.addfile(gcode)
#print "generated 2d view in %f s"%(time.time()-t0) self.gwindow.p.addfile(gcode)
#t0 = time.time()
self.gwindow.p.addfile(self.f)
#print "generated 3d view in %f s"%(time.time()-t0)
self.gviz.showall = 1 self.gviz.showall = 1
wx.CallAfter(self.gviz.Refresh) wx.CallAfter(self.gviz.Refresh)
...@@ -1315,14 +1433,14 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1315,14 +1433,14 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.send_now("M24") self.p.send_now("M24")
return return
if self.f is None or not len(self.f): if not self.f:
wx.CallAfter(self.status.SetStatusText, _("No file loaded. Please use load first.")) wx.CallAfter(self.status.SetStatusText, _("No file loaded. Please use load first."))
return return
if not self.p.online: if not self.p.online:
wx.CallAfter(self.status.SetStatusText, _("Not connected to printer.")) wx.CallAfter(self.status.SetStatusText, _("Not connected to printer."))
return return
self.on_startprint() self.on_startprint()
self.p.startprint(self.f) self.p.startprint(self.fgcode)
def on_startprint(self): def on_startprint(self):
wx.CallAfter(self.pausebtn.SetLabel, _("Pause")) wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
...@@ -1339,14 +1457,14 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1339,14 +1457,14 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def uploadtrigger(self, l): def uploadtrigger(self, l):
if "Writing to file" in l: if "Writing to file" in l:
self.uploading = True self.uploading = True
self.p.startprint(self.f) self.p.startprint(self.fgcode)
self.p.endcb = self.endupload self.p.endcb = self.endupload
self.recvlisteners.remove(self.uploadtrigger) self.recvlisteners.remove(self.uploadtrigger)
elif "open failed, File" in l: elif "open failed, File" in l:
self.recvlisteners.remove(self.uploadtrigger) self.recvlisteners.remove(self.uploadtrigger)
def upload(self, event): def upload(self, event):
if not self.f or not len(self.f): if not self.f:
return return
if not self.p.online: if not self.p.online:
return return
...@@ -1355,6 +1473,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1355,6 +1473,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.send_now("M21") self.p.send_now("M21")
self.p.send_now("M28 "+str(dlg.GetValue())) self.p.send_now("M28 "+str(dlg.GetValue()))
self.recvlisteners+=[self.uploadtrigger] self.recvlisteners+=[self.uploadtrigger]
dlg.Destroy()
def pause(self, event): def pause(self, event):
print _("Paused.") print _("Paused.")
...@@ -1383,7 +1502,7 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1383,7 +1502,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.on_startprint() self.on_startprint()
threading.Thread(target = self.getfiles).start() threading.Thread(target = self.getfiles).start()
def connect(self, event): def connect(self, event = None):
print _("Connecting...") print _("Connecting...")
port = None port = None
try: try:
...@@ -1491,36 +1610,21 @@ class PronterWindow(MainWindow, pronsole.pronsole): ...@@ -1491,36 +1610,21 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.paused = 0 self.p.paused = 0
wx.CallAfter(self.pausebtn.SetLabel, _("Pause")) wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
self.paused = 0 self.paused = 0
dlg.Destroy()
def get_build_dimensions(self, bdim): class PronterApp(wx.App):
import re
# a string containing up to six numbers delimited by almost anything mainwindow = None
# first 0-3 numbers specify the build volume, no sign, always positive
# remaining 0-3 numbers specify the coordinates of the "southwest" corner of the build platform def __init__(self, *args, **kwargs):
# "XXX,YYY" super(PronterApp, self).__init__(*args, **kwargs)
# "XXXxYYY+xxx-yyy" self.mainwindow = PronterWindow()
# "XXX,YYY,ZZZ+xxx+yyy-zzz" self.mainwindow.Show()
# etc
bdl = re.match(
"[^\d+-]*(\d+)?" + # X build size
"[^\d+-]*(\d+)?" + # Y build size
"[^\d+-]*(\d+)?" + # Z build size
"[^\d+-]*([+-]\d+)?" + # X corner coordinate
"[^\d+-]*([+-]\d+)?" + # Y corner coordinate
"[^\d+-]*([+-]\d+)?" + # Z corner coordinate
"[^\d+-]*([+-]\d+)?" + # X endstop
"[^\d+-]*([+-]\d+)?" + # Y endstop
"[^\d+-]*([+-]\d+)?" # Z endstop
,bdim).groups()
defaults = [200, 200, 100, 0, 0, 0, 0, 0, 0]
bdl_float = [float(value) if value else defaults[i] for i, value in enumerate(bdl)]
return bdl_float
if __name__ == '__main__': if __name__ == '__main__':
app = wx.App(False) app = PronterApp(False)
main = PronterWindow()
main.Show()
try: try:
app.MainLoop() app.MainLoop()
except: except KeyboardInterrupt:
pass pass
del app
...@@ -145,7 +145,7 @@ setup ( ...@@ -145,7 +145,7 @@ setup (
license = "GPLv3", license = "GPLv3",
data_files = data_files, data_files = data_files,
packages = ["printrun", "printrun.svg"], packages = ["printrun", "printrun.svg"],
scripts = ["pronsole.py", "pronterface.py", "plater.py", "printcore.py", "gcoder.py"], scripts = ["pronsole.py", "pronterface.py", "plater.py", "printcore.py"],
cmdclass = {"uninstall" : uninstall, cmdclass = {"uninstall" : uninstall,
"install" : install, "install" : install,
"install_data" : install_data} "install_data" : install_data}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment