Commit cc59be4e authored by Elias's avatar Elias

Fetched from Kliment and mergen in ethernet

parents 27cbef59 7ee7e1d9
......@@ -3,3 +3,4 @@
*.swp
*.bak
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
`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`
......@@ -101,9 +111,10 @@ Run Printrun for source if you want to test out the latest features.
To use pronterface, you need:
* python (ideally 2.6.x or 2.7.x),
* pyserial (or python-serial on ubuntu/debian),
* pyserial (or python-serial on ubuntu/debian)
* pyglet
* pyreadline (not needed on Linux) and
* argparse (installed by default with python >= 2.7)
* 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)
......
#!/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 @@
msgid ""
msgstr ""
"Project-Id-Version: Pronterface jm1\n"
"POT-Creation-Date: 2012-08-08 10:09+CEST\n"
"PO-Revision-Date: 2012-03-16 03:50+0100\n"
"POT-Creation-Date: 2013-05-22 12:58+CEST\n"
"PO-Revision-Date: 2013-05-22 14:23+0100\n"
"Last-Translator: Guillaume Seguin <guillaume@segu.in>\n"
"Language-Team: FR <c.laguilhon.debat@gmail.com>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\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"
msgstr "Trouver"
#: printrun/pronterface_widgets.py:36
#: printrun/pronterface_widgets.py:37
msgid "Save"
msgstr "Enregistrer"
#: printrun/pronterface_widgets.py:41 pronterface.py:477 pronterface.py:1535
msgid "Cancel"
msgstr "Annuler"
#: printrun/pronterface_widgets.py:125
#: printrun/pronterface_widgets.py:126
msgid "Edit settings"
msgstr "Modifier les paramètres"
#: printrun/pronterface_widgets.py:127
#: printrun/pronterface_widgets.py:128
msgid "Defaults"
msgstr "Paramètres par défaut"
#: printrun/pronterface_widgets.py:156
#: printrun/pronterface_widgets.py:157
msgid "Custom button"
msgstr "Commande personnalisée"
#: printrun/pronterface_widgets.py:161
#: printrun/pronterface_widgets.py:162
msgid "Button title"
msgstr "Titre du bouton"
#: printrun/pronterface_widgets.py:164
#: printrun/pronterface_widgets.py:165
msgid "Command"
msgstr "Commande"
#: printrun/pronterface_widgets.py:173
#: printrun/pronterface_widgets.py:174
msgid "Color"
msgstr "Couleur"
#: pronterface.py:26
msgid "WX is not installed. This program requires WX to run."
#: pronsole.py:173
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 ""
"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 ""
"Dimensions of Build Platform\n"
" & optional offset of origin\n"
" & optional switch position\n"
"\n"
"Examples:\n"
" XXXxYYY\n"
" XXX,YYY,ZZZ\n"
" XXXxYYYxZZZ+OffX+OffY+OffZ"
" XXXxYYYxZZZ+OffX+OffY+OffZ\n"
"XXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ"
msgstr ""
#: pronterface.py:94
#: pronterface.py:101
msgid "Last Set Temperature for the Heated Print Bed"
msgstr "Dernière température du plateau chauffant définie"
#: pronterface.py:95
#: pronterface.py:102
msgid "Folder of last opened file"
msgstr "Dossier du dernier fichier ouvert"
#: pronterface.py:96
#: pronterface.py:103
msgid "Last Temperature of the Hot End"
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)"
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)"
msgstr "Espacement fin de la grille (défaut : 10)"
#: pronterface.py:99
#: pronterface.py:106
msgid "Coarse Grid Spacing (default: 50)"
msgstr "Espacement large de la grille (défaut : 50)"
#: pronterface.py:100
#: pronterface.py:107
msgid "Pronterface background color (default: #FFFFFF)"
msgstr "Couleur de fond de la Pronterface (défaut : #FFFFFF)"
#: pronterface.py:103
#: pronterface.py:110
msgid "Printer Interface"
msgstr "Interface de l'imprimante"
#: pronterface.py:122
#: pronterface.py:127
msgid "Motors off"
msgstr "Arrêter les moteurs"
#: pronterface.py:122
#: pronterface.py:127
msgid "Switch all motors off"
msgstr "Arrêter tous les moteurs"
#: pronterface.py:123
#: pronterface.py:128
msgid "Check current hotend temperature"
msgstr "Vérifier la température actuelle de la buse"
#: pronterface.py:123
#: pronterface.py:128
msgid "Check temp"
msgstr "Lire les températures"
#: pronterface.py:124
#: pronterface.py:129
msgid "Advance extruder by set length"
msgstr "Extruder sur la longueur donnée"
#: pronterface.py:124
#: pronterface.py:129
msgid "Extrude"
msgstr "Extruder"
#: pronterface.py:125
#: pronterface.py:130
msgid "Reverse"
msgstr "Inverser"
#: pronterface.py:125
#: pronterface.py:130
msgid "Reverse extruder by set length"
msgstr "Inverser l'extrudeur sur la longueur donnée"
#: pronterface.py:143
#: pronterface.py:173
msgid ""
"# I moved all your custom buttons into .pronsolerc.\n"
"# Please don't add them here any more.\n"
......@@ -143,7 +849,7 @@ msgstr ""
"# Veuillez ne plus en ajouter ici.\n"
"# Une sauvegarde de vos anciens boutons est dans le fichier custombtn.old\n"
#: pronterface.py:148
#: pronterface.py:178
msgid ""
"Note!!! You have specified custom buttons in both custombtn.txt and ."
"pronsolerc"
......@@ -151,389 +857,283 @@ msgstr ""
"Remarque! Vous avez spécifié des boutons personnalisés dans custombtn.txt et "
"aussi dans .pronsolerc"
#: pronterface.py:149
#: pronterface.py:179
msgid ""
"Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt"
msgstr ""
"custombtn.txt ignoré. Retirez tous les boutons en cours pour revenir à "
"custombtn.txt"
#: pronterface.py:181
msgid "Failed to start web interface"
msgstr "Échec du lancement de l'interface web"
#: pronterface.py:209
msgid ""
"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
msgid "CherryPy is not installed. Web Interface Disabled."
msgstr "CherryPy n'est pas installé. L'interface web est désactivée."
#: pronterface.py:210
msgid "automatically try to connect to printer on startup"
msgstr "tenter de se connecter automatiquement à l'imprimante au démarrage"
#: pronterface.py:197 pronterface.py:603 pronterface.py:1525
#: pronterface.py:1578 pronterface.py:1705 pronterface.py:1765
#: pronterface.py:1778
msgid "Print"
msgstr "Imprimer"
#: pronterface.py:219
msgid "Print Started at: %s"
msgstr "Impression lancée à : %s"
#: 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."
msgstr "Imprimante connectée."
#: pronterface.py:208
#: pronterface.py:239
msgid "Disconnect"
msgstr "Déconnecter"
#: pronterface.py:331
#: pronterface.py:351
msgid "Setting hotend temperature to %f degrees Celsius."
msgstr "Réglage de la température de la buse à %f degrés Celsius."
#: pronterface.py:334 pronterface.py:356 pronterface.py:428
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
#: pronterface.py:358 pronterface.py:378
msgid "You must enter a temperature. (%s)"
msgstr "Vous devez saisir une température. (%s)"
#: pronterface.py:353
#: pronterface.py:371
msgid "Setting bed temperature to %f degrees Celsius."
msgstr "Réglage de la température du plateau à %f degrés Celsius."
#: pronterface.py:360
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
#: pronterface.py:393
msgid "Do you want to erase the macro?"
msgstr "Voulez-vous effacer la macro ?"
#: pronterface.py:385
#: pronterface.py:397
msgid "Cancelled."
msgstr "Annulé"
#: pronterface.py:436
#: pronterface.py:442
msgid " Opens file"
msgstr " Ouvrir un fichier"
#: pronterface.py:436
#: pronterface.py:442
msgid "&Open..."
msgstr "&Ouvrir..."
#: pronterface.py:437
#: pronterface.py:443
msgid " Edit open file"
msgstr " Éditer le fichier ouvert"
#: pronterface.py:437
#: pronterface.py:443
msgid "&Edit..."
msgstr "&Éditer..."
#: pronterface.py:438
#: pronterface.py:444
msgid " Clear output console"
msgstr " Effacer le contenu de la console de sortie"
#: pronterface.py:438
#: pronterface.py:444
msgid "Clear console"
msgstr "Effacer la console"
#: pronterface.py:439
#: pronterface.py:445
msgid " Project slices"
msgstr " Projeter les couches"
#: pronterface.py:439
#: pronterface.py:445
msgid "Projector"
msgstr "Projecteur"
#: pronterface.py:440
#: pronterface.py:446
msgid " Closes the Window"
msgstr " Quitter le programme"
#: pronterface.py:440
#: pronterface.py:446
msgid "E&xit"
msgstr "&Quitter"
#: pronterface.py:441
#: pronterface.py:447
msgid "&File"
msgstr "&Fichier"
#: pronterface.py:446
#: pronterface.py:452
msgid "&Macros"
msgstr "&Macros"
#: pronterface.py:447
#: pronterface.py:453
msgid "<&New...>"
msgstr "<&Nouvelle...>"
#: pronterface.py:448
#: pronterface.py:454
msgid " Options dialog"
msgstr " Fenêtre des options"
#: pronterface.py:448
#: pronterface.py:454
msgid "&Options"
msgstr "&Options"
#: pronterface.py:450
#: pronterface.py:456
msgid " Adjust slicing settings"
msgstr " Régler les paramètres de slicing"
#: pronterface.py:450
#: pronterface.py:456
msgid "Slicing Settings"
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"
msgstr "&Paramètres"
#: pronterface.py:467
#: pronterface.py:484
msgid "Enter macro name"
msgstr "Saisissez le nom de la macro"
#: pronterface.py:470
#: pronterface.py:487
msgid "Macro name:"
msgstr "Nom :"
#: pronterface.py:473
#: pronterface.py:490
msgid "Ok"
msgstr "Valider"
#: pronterface.py:495
#: pronterface.py:512
msgid "Macro name may contain only ASCII alphanumeric symbols and underscores"
msgstr ""
"Un nom de macro ne peut contenir que des caractères alphanumérique ASCII et "
"des underscore (_)"
#: pronterface.py:500
#: pronterface.py:515
msgid "Name '%s' is being used by built-in command"
msgstr "Le nom '%s' est utilisé par une commande interne"
#: pronterface.py:548
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
#: pronterface.py:583
msgid "SD Upload"
msgstr "Copier sur SD"
#: pronterface.py:873
#: pronterface.py:587
msgid "SD Print"
msgstr "Imprimer depuis SD"
#: pronterface.py:914
#: pronterface.py:628
msgid "Mini mode"
msgstr "Mode réduit"
#: pronterface.py:921
#: pronterface.py:635
msgid "Full mode"
msgstr "Mode complet"
#: pronterface.py:946
#: pronterface.py:660
msgid "Execute command: "
msgstr "Exécuter la commande :"
#: pronterface.py:957
#: pronterface.py:671
msgid "click to add new custom button"
msgstr "Ajouter un bouton personnalisé"
#: pronterface.py:979
#: pronterface.py:693
msgid ""
"Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command"
msgstr ""
"Définit des boutons personnalidés. Utilisation : <numero> \"Libelle\" [/c "
"\"couleur\"] commande"
#: pronterface.py:1003
#: pronterface.py:715
msgid "Custom button number should be between 0 and 63"
msgstr ""
"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'"
msgstr "Editer le bouton personnalisé '%s'"
#: pronterface.py:1098
#: pronterface.py:808
msgid "Move left <<"
msgstr "Déplacer vers la gauche <<"
#: pronterface.py:1101
#: pronterface.py:811
msgid "Move right >>"
msgstr "Déplacer vers la droite >>"
#: pronterface.py:1105
#: pronterface.py:815
msgid "Remove custom button '%s'"
msgstr "Supprimer le bouton personnalisé '%s'"
#: pronterface.py:1108
#: pronterface.py:818
msgid "Add custom button"
msgstr "Ajouter un bouton personnalisé"
#: pronterface.py:1270
#: pronterface.py:987
msgid "event object missing"
msgstr "événement d'objet manquant"
#: pronterface.py:1306
msgid "Invalid period given."
msgstr "La période donnée est invalide"
#: pronterface.py:1311
#: pronterface.py:1018
msgid "Monitoring printer."
msgstr "Imprimante sous surveillance."
#: pronterface.py:1315
msgid "Done monitoring."
msgstr "Surveillance de l'imprimante effectuée."
#: pronterface.py:1033
msgid ""
"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 %%"
msgstr " Impression SD : %04.2f %%"
#: pronterface.py:1358
#: pronterface.py:1087
msgid " Printing: %04.2f%% |"
msgstr " Impression : %04.2f%% |"
#: pronterface.py:1359
#: pronterface.py:1088
msgid " Line# %d of %d lines |"
msgstr " Ligne# %d sur %d lignes |"
#: pronterface.py:1364
#: pronterface.py:1093
msgid " Est: %s of %s remaining | "
msgstr " ETA: %s restant sur %s | "
#: pronterface.py:1366
#: pronterface.py:1095
msgid " Z: %0.2f mm"
msgstr " Z: %0.2f mm"
#: pronterface.py:1439
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
#: pronterface.py:1190
msgid "Pick SD file"
msgstr "Choisir un fichier sur la carte SD"
#: pronterface.py:1466
#: pronterface.py:1190
msgid "Select the file to print"
msgstr "Sélectionnez le fichier à imprimer :"
#: pronterface.py:1501
#: pronterface.py:1222
msgid "Failed to execute slicing software: "
msgstr "Une erreur s'est produite lors du slicing : "
#: pronterface.py:1510
#: pronterface.py:1229
msgid "Slicing..."
msgstr "Slicing..."
#: pronterface.py:1523
msgid ", %d lines"
msgstr ", %d lignes"
#: pronterface.py:1523
msgid "Loaded "
msgstr "Chargé "
#: pronterface.py:1241 pronterface.py:1300
msgid "Loaded %s, %d lines"
msgstr "%s chargé, %d lignes"
#: pronterface.py:1530
#: pronterface.py:1248
msgid "Load File"
msgstr "Charger un fichier"
#: pronterface.py:1536
#: pronterface.py:1254
msgid "Slicing "
msgstr "Slicing "
#: pronterface.py:1555
#: pronterface.py:1279
msgid "Open file to print"
msgstr "Ouvrir un fichier à imprimer"
#: 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 (*.*)|*.*"
......@@ -541,86 +1141,87 @@ msgstr ""
"Fichiers OBJ, STL et GCODE (;*.gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ;)|*."
"gcode;*.gco;*.g;*.stl;*.STL;*.obj;*.OBJ|Tous les fichiers (*.*)|*.*"
#: pronterface.py:1563
msgid "File not found!"
msgstr "Fichier non trouvé"
#: pronterface.py:1577
msgid "Loaded %s, %d lines"
msgstr "%s chargé, %d lignes"
#: pronterface.py:1311
msgid "mm of filament used in this print"
msgstr "mm de filament utilisés pour cette impression"
#: pronterface.py:1588
msgid "mm of filament used in this print\n"
msgstr "mm de filament utilisés pour cette impression\n"
#: pronterface.py:1312
msgid "The print goes:"
msgstr "L'impression va :"
#: pronterface.py:1589 pronterface.py:1591
msgid ""
"the print goes from %f mm to %f mm in X\n"
"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:1313
msgid "- from %.2f mm to %.2f mm in X and is %.2f mm wide"
msgstr "- de %.02f mm à %.02f mm en X et mesure %.02f mm de large"
#: pronterface.py:1592
msgid ""
"the print goes from %f mm to %f mm in Y\n"
"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:1314
msgid "- from %.2f mm to %.2f mm in Y and is %.2f mm deep"
msgstr "- de %.02f mm à %.02f mm en Y et mesure %.02f mm de profondeur"
#: pronterface.py:1593
msgid ""
"the print goes from %f mm to %f mm in Z\n"
"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:1315
msgid "- from %.2f mm to %.2f mm in Z and is %.2f mm high"
msgstr "- de %.02f mm à %.02f mm en Y et mesure %.02f mm de haut"
#: pronterface.py:1622
msgid "No file loaded. Please use load first."
msgstr "Aucun fichier chargé. Veuillez charger un fichier avant."
#: pronterface.py:1316
msgid "Estimated duration: %s"
msgstr "Durée estimée : %s"
#: pronterface.py:1633
#: pronterface.py:1347
msgid "Restart"
msgstr "Recommencer"
#: pronterface.py:1637
#: pronterface.py:1351
msgid "File upload complete"
msgstr "Envoi du fichier terminé"
#: pronterface.py:1656
#: pronterface.py:1370
msgid "Pick SD filename"
msgstr "Lister les fichiers sur la carte SD"
#: pronterface.py:1663
#: pronterface.py:1377
msgid "Paused."
msgstr "En pause."
#: pronterface.py:1674
#: pronterface.py:1390
msgid "Resume"
msgstr "Reprendre"
#: pronterface.py:1688
#: pronterface.py:1404
msgid "Connecting..."
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."
msgstr "Déconnecté."
#: pronterface.py:1771
#: pronterface.py:1499
msgid "Reset."
msgstr "Réinitialisée."
#: pronterface.py:1772
#: pronterface.py:1500
msgid "Are you sure you want to reset the printer?"
msgstr "Etes-vous sûr de vouloir réinitialiser l'imprimante?"
#: pronterface.py:1772
#: pronterface.py:1500
msgid "Reset?"
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 @@
msgid ""
msgstr ""
"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"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
......@@ -15,575 +15,1140 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
#: printrun/pronterface_widgets.py:34
msgid "Find"
#: ./plater.py:246
msgid "Plate building tool"
msgstr ""
#: printrun/pronterface_widgets.py:36
msgid "Save"
#: ./plater.py:252
msgid "Clear"
msgstr ""
#: ./plater.py:253
msgid "Load"
msgstr ""
#: ./plater.py:255 ./plater.py:258
msgid "Export"
msgstr ""
#: ./plater.py:260
msgid "Done"
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"
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"
msgstr ""
#: printrun/pronterface_widgets.py:127
#: ./printrun/pronterface_widgets.py:128
msgid "Defaults"
msgstr ""
#: printrun/pronterface_widgets.py:156
#: ./printrun/pronterface_widgets.py:157
msgid "Custom button"
msgstr ""
#: printrun/pronterface_widgets.py:161
#: ./printrun/pronterface_widgets.py:162
msgid "Button title"
msgstr ""
#: printrun/pronterface_widgets.py:164
#: ./printrun/pronterface_widgets.py:165
msgid "Command"
msgstr ""
#: printrun/pronterface_widgets.py:173
#: ./printrun/pronterface_widgets.py:174
msgid "Color"
msgstr ""
#: pronterface.py:26
msgid "WX is not installed. This program requires WX to run."
#: ./pronsole.py:173
msgid "Communications Speed (default: 115200)"
msgstr ""
#: ./pronsole.py:174
msgid "Heated Build Platform temp for ABS (default: 110 deg C)"
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 ""
"Dimensions of Build Platform\n"
" & optional offset of origin\n"
" & optional switch position\n"
"\n"
"Examples:\n"
" XXXxYYY\n"
" XXX,YYY,ZZZ\n"
" XXXxYYYxZZZ+OffX+OffY+OffZ"
" XXXxYYYxZZZ+OffX+OffY+OffZ\n"
"XXXxYYYxZZZ+OffX+OffY+OffZ+HomeX+HomeY+HomeZ"
msgstr ""
#: pronterface.py:94
#: ./pronterface.py:101
msgid "Last Set Temperature for the Heated Print Bed"
msgstr ""
#: pronterface.py:95
#: ./pronterface.py:102
msgid "Folder of last opened file"
msgstr ""
#: pronterface.py:96
#: ./pronterface.py:103
msgid "Last Temperature of the Hot End"
msgstr ""
#: pronterface.py:97
#: ./pronterface.py:104
msgid "Width of Extrusion in Preview (default: 0.5)"
msgstr ""
#: pronterface.py:98
#: ./pronterface.py:105
msgid "Fine Grid Spacing (default: 10)"
msgstr ""
#: pronterface.py:99
#: ./pronterface.py:106
msgid "Coarse Grid Spacing (default: 50)"
msgstr ""
#: pronterface.py:100
#: ./pronterface.py:107
msgid "Pronterface background color (default: #FFFFFF)"
msgstr ""
#: pronterface.py:103
#: ./pronterface.py:110
msgid "Printer Interface"
msgstr ""
#: pronterface.py:122
#: ./pronterface.py:127
msgid "Motors off"
msgstr ""
#: pronterface.py:122
#: ./pronterface.py:127
msgid "Switch all motors off"
msgstr ""
#: pronterface.py:123
#: ./pronterface.py:128
msgid "Check current hotend temperature"
msgstr ""
#: pronterface.py:123
#: ./pronterface.py:128
msgid "Check temp"
msgstr ""
#: pronterface.py:124
#: ./pronterface.py:129
msgid "Advance extruder by set length"
msgstr ""
#: pronterface.py:124
#: ./pronterface.py:129
msgid "Extrude"
msgstr ""
#: pronterface.py:125
#: ./pronterface.py:130
msgid "Reverse"
msgstr ""
#: pronterface.py:125
#: ./pronterface.py:130
msgid "Reverse extruder by set length"
msgstr ""
#: pronterface.py:143
#: ./pronterface.py:173
msgid ""
"# I moved all your custom buttons into .pronsolerc.\n"
"# Please don't add them here any more.\n"
"# Backup of your old buttons is in custombtn.old\n"
msgstr ""
#: pronterface.py:148
#: ./pronterface.py:178
msgid "Note!!! You have specified custom buttons in both custombtn.txt and .pronsolerc"
msgstr ""
#: pronterface.py:149
#: ./pronterface.py:179
msgid "Ignoring custombtn.txt. Remove all current buttons to revert to custombtn.txt"
msgstr ""
#: pronterface.py:181
msgid "Failed to start web interface"
#: ./pronterface.py:209
msgid "display graphical temperature gauges in addition to the temperatures graph"
msgstr ""
#: pronterface.py:185
msgid "CherryPy is not installed. Web Interface Disabled."
#: ./pronterface.py:210
msgid "automatically try to connect to printer on startup"
msgstr ""
#: pronterface.py:197 pronterface.py:603 pronterface.py:1525
#: pronterface.py:1578 pronterface.py:1705 pronterface.py:1765
#: pronterface.py:1778
msgid "Print"
#: ./pronterface.py:219
msgid "Print Started at: %s"
msgstr ""
#: ./pronterface.py:224
msgid "Print ended at: %(end_time)s and took %(duration)s"
msgstr ""
#: pronterface.py:207
#: ./pronterface.py:238
msgid "Printer is now online."
msgstr ""
#: pronterface.py:208
#: ./pronterface.py:239
msgid "Disconnect"
msgstr ""
#: pronterface.py:331
#: ./pronterface.py:351
msgid "Setting hotend temperature to %f degrees Celsius."
msgstr ""
#: pronterface.py:334 pronterface.py:356 pronterface.py:428
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
#: ./pronterface.py:358 ./pronterface.py:378
msgid "You must enter a temperature. (%s)"
msgstr ""
#: pronterface.py:353
#: ./pronterface.py:371
msgid "Setting bed temperature to %f degrees Celsius."
msgstr ""
#: pronterface.py:360
msgid "You cannot set negative temperatures. To turn the bed off entirely, set its temperature to 0."
msgstr ""
#: pronterface.py:381
#: ./pronterface.py:393
msgid "Do you want to erase the macro?"
msgstr ""
#: pronterface.py:385
#: ./pronterface.py:397
msgid "Cancelled."
msgstr ""
#: pronterface.py:436
#: ./pronterface.py:442
msgid " Opens file"
msgstr ""
#: pronterface.py:436
#: ./pronterface.py:442
msgid "&Open..."
msgstr ""
#: pronterface.py:437
#: ./pronterface.py:443
msgid " Edit open file"
msgstr ""
#: pronterface.py:437
#: ./pronterface.py:443
msgid "&Edit..."
msgstr ""
#: pronterface.py:438
#: ./pronterface.py:444
msgid " Clear output console"
msgstr ""
#: pronterface.py:438
#: ./pronterface.py:444
msgid "Clear console"
msgstr ""
#: pronterface.py:439
#: ./pronterface.py:445
msgid " Project slices"
msgstr ""
#: pronterface.py:439
#: ./pronterface.py:445
msgid "Projector"
msgstr ""
#: pronterface.py:440
#: ./pronterface.py:446
msgid " Closes the Window"
msgstr ""
#: pronterface.py:440
#: ./pronterface.py:446
msgid "E&xit"
msgstr ""
#: pronterface.py:441
#: ./pronterface.py:447
msgid "&File"
msgstr ""
#: pronterface.py:446
#: ./pronterface.py:452
msgid "&Macros"
msgstr ""
#: pronterface.py:447
#: ./pronterface.py:453
msgid "<&New...>"
msgstr ""
#: pronterface.py:448
#: ./pronterface.py:454
msgid " Options dialog"
msgstr ""
#: pronterface.py:448
#: ./pronterface.py:454
msgid "&Options"
msgstr ""
#: pronterface.py:450
#: ./pronterface.py:456
msgid " Adjust slicing settings"
msgstr ""
#: pronterface.py:450
#: ./pronterface.py:456
msgid "Slicing Settings"
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"
msgstr ""
#: pronterface.py:467
#: ./pronterface.py:484
msgid "Enter macro name"
msgstr ""
#: pronterface.py:470
#: ./pronterface.py:487
msgid "Macro name:"
msgstr ""
#: pronterface.py:473
#: ./pronterface.py:490
msgid "Ok"
msgstr ""
#: pronterface.py:495
#: ./pronterface.py:512
msgid "Macro name may contain only ASCII alphanumeric symbols and underscores"
msgstr ""
#: pronterface.py:500
#: ./pronterface.py:515
msgid "Name '%s' is being used by built-in command"
msgstr ""
#: pronterface.py:548
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
#: ./pronterface.py:583
msgid "SD Upload"
msgstr ""
#: pronterface.py:873
#: ./pronterface.py:587
msgid "SD Print"
msgstr ""
#: pronterface.py:914
#: ./pronterface.py:628
msgid "Mini mode"
msgstr ""
#: pronterface.py:921
#: ./pronterface.py:635
msgid "Full mode"
msgstr ""
#: pronterface.py:946
#: ./pronterface.py:660
msgid "Execute command: "
msgstr ""
#: pronterface.py:957
#: ./pronterface.py:671
msgid "click to add new custom button"
msgstr ""
#: pronterface.py:979
#: ./pronterface.py:693
msgid "Defines custom button. Usage: button <num> \"title\" [/c \"colour\"] command"
msgstr ""
#: pronterface.py:1003
#: ./pronterface.py:715
msgid "Custom button number should be between 0 and 63"
msgstr ""
#: pronterface.py:1096
#: ./pronterface.py:806
msgid "Edit custom button '%s'"
msgstr ""
#: pronterface.py:1098
#: ./pronterface.py:808
msgid "Move left <<"
msgstr ""
#: pronterface.py:1101
#: ./pronterface.py:811
msgid "Move right >>"
msgstr ""
#: pronterface.py:1105
#: ./pronterface.py:815
msgid "Remove custom button '%s'"
msgstr ""
#: pronterface.py:1108
#: ./pronterface.py:818
msgid "Add custom button"
msgstr ""
#: pronterface.py:1270
#: ./pronterface.py:987
msgid "event object missing"
msgstr ""
#: pronterface.py:1306
msgid "Invalid period given."
msgstr ""
#: pronterface.py:1311
#: ./pronterface.py:1018
msgid "Monitoring printer."
msgstr ""
#: pronterface.py:1315
msgid "Done monitoring."
#: ./pronterface.py:1033
msgid "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 %%"
msgstr ""
#: pronterface.py:1358
#: ./pronterface.py:1087
msgid " Printing: %04.2f%% |"
msgstr ""
#: pronterface.py:1359
#: ./pronterface.py:1088
msgid " Line# %d of %d lines |"
msgstr ""
#: pronterface.py:1364
#: ./pronterface.py:1093
msgid " Est: %s of %s remaining | "
msgstr ""
#: pronterface.py:1366
#: ./pronterface.py:1095
msgid " Z: %0.2f mm"
msgstr ""
#: pronterface.py:1439
msgid "Opening file failed."
msgstr ""
#: pronterface.py:1445
msgid "Starting print"
msgstr ""
#: pronterface.py:1466
#: ./pronterface.py:1190
msgid "Pick SD file"
msgstr ""
#: pronterface.py:1466
#: ./pronterface.py:1190
msgid "Select the file to print"
msgstr ""
#: pronterface.py:1501
#: ./pronterface.py:1222
msgid "Failed to execute slicing software: "
msgstr ""
#: pronterface.py:1510
#: ./pronterface.py:1229
msgid "Slicing..."
msgstr ""
#: pronterface.py:1523
msgid ", %d lines"
msgstr ""
#: pronterface.py:1523
msgid "Loaded "
#: ./pronterface.py:1241 ./pronterface.py:1300
msgid "Loaded %s, %d lines"
msgstr ""
#: pronterface.py:1530
#: ./pronterface.py:1248
msgid "Load File"
msgstr ""
#: pronterface.py:1536
#: ./pronterface.py:1254
msgid "Slicing "
msgstr ""
#: pronterface.py:1555
#: ./pronterface.py:1279
msgid "Open file to print"
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 (*.*)|*.*"
msgstr ""
#: pronterface.py:1563
msgid "File not found!"
#: ./pronterface.py:1311
msgid "mm of filament used in this print"
msgstr ""
#: pronterface.py:1577
msgid "Loaded %s, %d lines"
#: ./pronterface.py:1312
msgid "The print goes:"
msgstr ""
#: pronterface.py:1588
msgid ""
"mm of filament used in this print\n"
#: ./pronterface.py:1313
msgid "- from %.2f mm to %.2f mm in X and is %.2f mm wide"
msgstr ""
#: pronterface.py:1589 pronterface.py:1591
msgid ""
"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"
#: ./pronterface.py:1314
msgid "- from %.2f mm to %.2f mm in Y and is %.2f mm deep"
msgstr ""
#: pronterface.py:1595
msgid "Estimated duration (pessimistic): "
#: ./pronterface.py:1315
msgid "- from %.2f mm to %.2f mm in Z and is %.2f mm high"
msgstr ""
#: pronterface.py:1622
msgid "No file loaded. Please use load first."
#: ./pronterface.py:1316
msgid "Estimated duration: %s"
msgstr ""
#: pronterface.py:1633
#: ./pronterface.py:1347
msgid "Restart"
msgstr ""
#: pronterface.py:1637
#: ./pronterface.py:1351
msgid "File upload complete"
msgstr ""
#: pronterface.py:1656
#: ./pronterface.py:1370
msgid "Pick SD filename"
msgstr ""
#: pronterface.py:1663
#: ./pronterface.py:1377
msgid "Paused."
msgstr ""
#: pronterface.py:1674
#: ./pronterface.py:1390
msgid "Resume"
msgstr ""
#: pronterface.py:1688
#: ./pronterface.py:1404
msgid "Connecting..."
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."
msgstr ""
#: pronterface.py:1771
#: ./pronterface.py:1499
msgid "Reset."
msgstr ""
#: pronterface.py:1772
#: ./pronterface.py:1500
msgid "Are you sure you want to reset the printer?"
msgstr ""
#: pronterface.py:1772
#: ./pronterface.py:1500
msgid "Reset?"
msgstr ""
......@@ -23,6 +23,9 @@ import platform, os
from GCodeAnalyzer import GCodeAnalyzer
import socket # Network
import re # Regex
from collections import deque
from printrun.GCodeAnalyzer import GCodeAnalyzer
from printrun import gcoder
def control_ttyhup(port, disable_hup):
"""Controls the HUPCL"""
......@@ -47,6 +50,7 @@ class printcore():
"""
self.baud = None
self.port = None
self.analyzer = GCodeAnalyzer()
self.printer = None #Serial instance connected to the printer, None when disconnected
self.clear = 0 #clear to send, enabled after responses
self.online = False #The printer has responded to the initial command and is active
......@@ -63,6 +67,7 @@ class printcore():
self.tempcb = None #impl (wholeline)
self.recvcb = None #impl (wholeline)
self.sendcb = None #impl (wholeline)
self.printsendcb = None #impl (wholeline)
self.errorcb = None #impl (wholeline)
self.startcb = None #impl ()
self.endcb = None #impl ()
......@@ -230,8 +235,8 @@ class printcore():
def _checksum(self, command):
return reduce(lambda x, y:x^y, map(ord, command))
def startprint(self, data, startindex = 0):
"""Start a print, data is an array of gcode commands.
def startprint(self, gcode, startindex = 0):
"""Start a print, gcode is an array of gcode commands.
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.
Printing will then start in a parallel thread.
......@@ -239,12 +244,12 @@ class printcore():
if self.printing or not self.online or not self.printer:
return False
self.printing = True
self.mainqueue = [] + data
self.mainqueue = gcode
self.lineno = 0
self.queueindex = startindex
self.resendfrom = -1
self._send("M110", -1, True)
if len(data) == 0:
if not gcode.lines:
return True
self.clear = False
self.print_thread = Thread(target = self._print)
......@@ -369,7 +374,7 @@ class printcore():
while self.printing and self.printer and self.online:
self._sendnext()
self.sentlines = {}
self.log = []
self.log.clear()
self.sent = []
try:
self.print_thread.join()
......@@ -403,23 +408,26 @@ class printcore():
self.resendfrom += 1
return
self.resendfrom = -1
for i in self.priqueue[:]:
self._send(i)
del self.priqueue[0]
if self.priqueue:
self._send(self.priqueue.pop(0))
return
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
if tline.lstrip().startswith(";@"):
#it is a host command: pop it from the list
self.mainqueue.pop(self.queueindex)
self.processHostCommand(tline)
return
self.processHostCommand(tline)
self.queueindex += 1
return
tline = tline.split(";")[0]
if len(tline) > 0:
self._send(tline, self.lineno, True)
self.lineno += 1
if self.printsendcb:
try: self.printsendcb(gline)
except: pass
else:
self.clear = True
self.queueindex += 1
......@@ -493,18 +501,19 @@ if __name__ == '__main__':
p = printcore(port, baud)
p.loud = loud
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)
try:
if statusreport:
p.loud = False
sys.stdout.write("Progress: 00.0%")
sys.stdout.write("Progress: 00.0%\r")
sys.stdout.flush()
while p.printing:
time.sleep(1)
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()
p.disconnect()
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):
## General methods
##
def draw(self, dc):
def draw(self, dc, w, h):
"""
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
# This file is part of the Printrun suite.
#
# Printrun is free software: you can redistribute it and/or modify
......@@ -16,43 +17,40 @@
import os
import math
import wx
from wx import glcanvas
import time
import threading
import pyglet
pyglet.options['shadow_window'] = False
pyglet.options['debug_gl'] = False
from pyglet.gl import *
pyglet.options['debug_gl'] = True
import stltool
import threading
from pyglet.gl import *
from pyglet import gl
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.'''
def __init__(self, parent, id, pos = wx.DefaultPosition,
size = wx.DefaultSize, style = 0):
# Forcing a no full repaint to stop flickering
style = style | wx.NO_FULL_REPAINT_ON_RESIZE
#call super function
super(GLPanel, self).__init__(parent, id, pos, size, style)
super(wxGLPanel, self).__init__(parent, id, pos, size, style)
#init gl canvas data
self.GLinitialized = False
attribList = (glcanvas.WX_GL_RGBA, # RGBA
glcanvas.WX_GL_DOUBLEBUFFER, # Double Buffered
glcanvas.WX_GL_DEPTH_SIZE, 24) # 24 bit
# Create the canvas
self.sizer = wx.BoxSizer(wx.HORIZONTAL)
self.canvas = glcanvas.GLCanvas(self, attribList = attribList)
self.sizer.Add(self.canvas, 1, wx.EXPAND)
self.SetSizer(self.sizer)
#self.sizer.Fit(self)
self.Layout()
self.sizer.Fit(self)
# bind events
self.canvas.Bind(wx.EVT_ERASE_BACKGROUND, self.processEraseBackgroundEvent)
......@@ -89,12 +87,12 @@ class GLPanel(wx.Panel):
self.OnReshape(size.width, size.height)
self.canvas.Refresh(False)
event.Skip()
#wx.CallAfter(self.Refresh)
def processPaintEvent(self, event):
'''Process the drawing event.'''
self.canvas.SetCurrent()
# This is a 'perfect' time to initialize OpenGL ... only if we need to
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True
......@@ -104,7 +102,7 @@ class GLPanel(wx.Panel):
def Destroy(self):
#clean up the pyglet OpenGL context
#self.pygletcontext.destroy()
self.pygletcontext.destroy()
#call the super method
super(wx.Panel, self).Destroy()
......@@ -116,51 +114,27 @@ class GLPanel(wx.Panel):
#create a pyglet context for this panel
self.pmat = (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.dist = 1000
self.vpmat = None
#normal gl init
glClearColor(0, 0, 0, 1)
glColor3f(1, 0, 0)
glClearColor(0.98, 0.98, 0.78, 1)
glClearDepth(1.0) # set depth value to 1
glDepthFunc(GL_LEQUAL)
glEnable(GL_COLOR_MATERIAL)
glEnable(GL_DEPTH_TEST)
glEnable(GL_CULL_FACE)
# Uncomment this line for a wireframe view
#glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
# 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()
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
self.OnReshape(*self.GetClientSize())
def OnReshape(self, width, height):
'''Reshape the OpenGL viewport based on the dimensions of the window.'''
if not self.GLinitialized:
self.OnInitGL()
self.GLinitialized = True
self.pmat = (GLdouble * 16)()
self.mvmat = (GLdouble * 16)()
self.OnInitGL()
glViewport(0, 0, width, height)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
......@@ -170,8 +144,6 @@ class GLPanel(wx.Panel):
#pyglet stuff
self.vpmat = (GLint * 4)(0, 0, *list(self.GetClientSize()))
glGetDoublev(GL_PROJECTION_MATRIX, self.pmat)
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
#glMatrixMode(GL_PROJECTION)
# Wrap text to the width of the window
if self.GLinitialized:
......@@ -180,13 +152,9 @@ class GLPanel(wx.Panel):
def OnDraw(self, *args, **kwargs):
"""Draw the window."""
#clear the context
self.canvas.SetCurrent()
self.pygletcontext.set_current()
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
#draw objects
self.draw_objects()
#update screen
self.SwapBuffers()
#==========================================================================
......@@ -204,262 +172,8 @@ class GLPanel(wx.Panel):
'''called in the middle of ondraw after the buffer has been cleared'''
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):
TRACKBALLSIZE = r
#float a[3]; /* Axis of rotation */
#float phi; /* how much to rotate about axis */
#float p1[3], p2[3], d[3];
......@@ -537,80 +251,81 @@ def mulquat(q1, rq):
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):
super(TestGlPanel, self).__init__(parent, id, wx.DefaultPosition, size, 0)
def __init__(self, parent, id = wx.ID_ANY, realparent = None):
super(GcodeViewPanel, self).__init__(parent, id, wx.DefaultPosition, wx.DefaultSize, 0)
self.batches = []
self.rot = 0
self.canvas.Bind(wx.EVT_MOUSE_EVENTS, self.move)
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.parent = parent
self.parent = realparent if realparent else parent
self.initpos = None
self.dist = 200
self.bedsize = [200, 200]
self.transv = [0, 0, -self.dist]
self.basequat = [0, 0, 0, 1]
wx.CallAfter(self.forceresize)
self.mousepos = [0, 0]
def double(self, event):
p = event.GetPositionTuple()
sz = self.GetClientSize()
v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize)
v[1] = self.bedsize[1] - v[1]
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 create_objects(self):
'''create opengl objects when opengl is initialized'''
for obj in self.parent.objects:
if obj.model and obj.model.loaded and not obj.model.initialized:
obj.model.init()
def update_object_resize(self):
'''called when the window recieves only if opengl is initialized'''
pass
def draw_objects(self):
'''called in the middle of ondraw after the buffer has been cleared'''
if self.vpmat is None:
return
self.create_objects()
if self.rot == 1:
glLoadIdentity()
glMultMatrixd(self.mvmat)
else:
glLoadIdentity()
glTranslatef(*self.transv)
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)
def move_shape(self, delta):
"""moves shape (selected in l, which is list ListBox of shapes)
by an offset specified in tuple delta.
Positive numbers move to (rigt, down)"""
name = self.parent.l.GetSelection()
if name == wx.NOT_FOUND:
return False
name = self.parent.l.GetString(name)
model = self.parent.models[name]
model.offsets = [
model.offsets[0] + delta[0],
model.offsets[1] + delta[1],
model.offsets[2]
]
self.Refresh()
return True
obj.model.display()
glPopMatrix()
glPopMatrix()
def double(self, event):
if self.parent.clickcb:
self.parent.clickcb(event)
def move(self, event):
"""react to mouse actions:
no mouse: show red mousedrop
LMB: move active object,
with shift rotate viewport
RMB: nothing
with shift move viewport
LMB: rotate viewport
RMB: move viewport
"""
if event.Entering():
self.canvas.SetFocus()
event.Skip()
return
if event.Dragging() and event.LeftIsDown():
if self.initpos == None:
self.initpos = event.GetPositionTuple()
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
p1 = self.initpos
self.initpos = None
......@@ -642,371 +357,225 @@ class TestGlPanel(GLPanel):
if self.initpos is not None:
self.initpos = None
elif event.Dragging() and event.RightIsDown() and event.ShiftDown():
elif event.Dragging() and event.RightIsDown():
if self.initpos is None:
self.initpos = event.GetPositionTuple()
else:
p1 = self.initpos
p2 = event.GetPositionTuple()
sz = self.GetClientSize()
p1 = list(p1)
p2 = list(p2)
p1 = list(p1) + [0]
p2 = list(p2) + [0]
p1[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()
glTranslatef(self.transv[0], self.transv[1], 0)
glTranslatef(0, 0, self.transv[2])
if(self.rot):
if self.rot:
glMultMatrixd(build_rotmatrix(self.basequat))
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
self.rot = 1
self.initpos = None
else:
#mouse is moving without a button press
p = event.GetPositionTuple()
sz = self.GetClientSize()
v = map(lambda m, w, b: b * m / w, p, sz, self.bedsize)
v[1] = self.bedsize[1] - v[1]
self.mousepos = v
def rotate_shape(self, angle):
"""rotates acive shape
positive angle is clockwise
"""
name = self.parent.l.GetSelection()
if name == wx.NOT_FOUND:
return False
name = self.parent.l.GetString(name)
model = self.parent.models[name]
model.rot += angle
event.Skip()
return
event.Skip()
wx.CallAfter(self.Refresh)
def layerup(self):
if not self.parent.model:
return
max_layers = self.parent.model.max_layers
current_layer = self.parent.model.num_layers_to_draw
new_layer = min(max_layers, current_layer + 1)
self.parent.model.num_layers_to_draw = new_layer
wx.CallAfter(self.Refresh)
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):
"""react to mouse wheel actions:
rotate object
with shift zoom viewport
without shift: set max layer
with shift: zoom viewport
"""
z = event.GetWheelRotation()
angle = 10
if not event.ShiftDown():
i = self.parent.l.GetSelection()
if i < 0:
try:
self.parent.setlayerindex(z)
except:
pass
dist = 10
if event.ShiftDown():
if not self.parent.model:
return
if z > 0:
self.rotate_shape(angle / 2)
self.layerup()
else:
self.rotate_shape(-angle / 2)
self.layerdown()
return
if z > 0:
self.transv[2] += angle
self.zoom(dist)
else:
self.transv[2] -= angle
glLoadIdentity()
glTranslatef(*self.transv)
if(self.rot):
glMultMatrixd(build_rotmatrix(self.basequat))
glGetDoublev(GL_MODELVIEW_MATRIX, self.mvmat)
self.rot = 1
self.zoom(-dist)
def keypress(self, event):
"""gets keypress events and moves/rotates acive shape"""
keycode = event.GetKeyCode()
print keycode
step = 5
angle = 18
step = 10
if event.ControlDown():
step = 1
angle = 1
#h
if keycode == 72:
self.move_shape((-step, 0))
#l
if keycode == 76:
self.move_shape((step, 0))
#j
if keycode == 75:
self.move_shape((0, step))
#k
if keycode == 74:
self.move_shape((0, -step))
#[
if keycode == 91:
self.rotate_shape(-angle)
#]
if keycode == 93:
self.rotate_shape(angle)
step = 3
kup = [85, 315] # Up keys
kdo = [68, 317] # Down Keys
kzi = [wx.WXK_PAGEDOWN, 388, 316, 61] # Zoom In Keys
kzo = [wx.WXK_PAGEUP, 390, 314, 45] # Zoom Out Keys
x = event.GetKeyCode()
if x in kup:
self.layerup()
if x in kdo:
self.layerdown()
if x in kzi:
self.zoom(step)
if x in kzo:
self.zoom(-step)
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)
def drawmodel(self, m, n):
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)
class GCObject(object):
def update_object_resize(self):
'''called when the window recieves only if opengl is initialized'''
def __init__(self, model):
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
def draw_objects(self):
'''called in the middle of ondraw after the buffer has been cleared'''
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)
def setlayer(self, *a):
pass
glPopMatrix()
glPopMatrix()
#print "drawn batch"
def addfile(self, gcode = None):
self.model = actors.GcodeModel()
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.'''
def __init__(self, parent, ID, title, pos = wx.DefaultPosition,
size = wx.DefaultSize, style = wx.DEFAULT_FRAME_STYLE):
super(GCFrame, self).__init__(parent, ID, title, pos, (size[0] + 150, size[1]), style)
class d:
def GetSelection(self):
return wx.NOT_FOUND
self.p = self
m = d()
m.offsets = [0, 0, 0]
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:
self.modelindex = -1
elif z < 0:
if self.modelindex > 0:
self.modelindex -= 1
elif self.modelindex == -1:
self.modelindex = len(mlk)
if self.modelindex >= 0:
m.curlayer = mlk[self.modelindex]
wx.CallAfter(self.SetTitle, "Gcode view, shift to move. Layer %d/%d, Z = %f" % (self.modelindex, len(mlk), m.curlayer))
def __init__(self, parent, ID, title, build_dimensions, objects = None,
pos = wx.DefaultPosition, size = wx.DefaultSize,
style = wx.DEFAULT_FRAME_STYLE):
super(GcodeViewFrame, self).__init__(parent, ID, title, pos, size, style)
self.refresh_timer = wx.CallLater(100, self.Refresh)
self.p = self # Hack for backwards compatibility with gviz API
self.clonefrom = objects
self.platform = actors.Platform(build_dimensions)
if objects:
self.model = objects[1].model
else:
m.curlayer = -1
wx.CallAfter(self.SetTitle, "Gcode view, shift to move view, mousewheel to set layer")
self.model = None
self.objects = [GCObject(self.platform), GCObject(None)]
self.glpanel = GcodeViewPanel(self)
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 addfile(self, gcode = None):
if self.clonefrom:
self.model = self.clonefrom[-1].model.copy()
else:
self.model = actors.GcodeModel()
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():
app = wx.App(redirect = False)
frame = GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (400, 400))
if __name__ == "__main__":
import sys
for filename in sys.argv:
if ".gcode" in filename:
frame.addfile(list(open(filename)))
elif ".stl" in filename:
#TODO: add stl here
pass
#frame = wx.Frame(None, -1, "GL Window", size = (400, 400))
#panel = TestGlPanel(frame, size = (300, 300))
app = wx.App(redirect = False)
build_dimensions = [200, 200, 100, 0, 0, 0]
frame = GcodeViewFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (400, 400), build_dimensions = build_dimensions)
gcode = gcoder.GCode(open(sys.argv[1]))
frame.addfile(gcode)
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.Show(True)
app.MainLoop()
app.Destroy()
if __name__ == "__main__":
#import cProfile
#print cProfile.run("main()")
main()
......@@ -22,15 +22,12 @@ from bufferedcanvas import *
class Graph(BufferedCanvas):
'''A class to show a Graph with Pronterface.'''
def __init__(self, parent, id, pos = wx.DefaultPosition,
size = wx.DefaultSize, style = 0):
def __init__(self, parent, id, root, pos = wx.DefaultPosition,
size = wx.Size(150, 80), style = 0):
# Forcing a no full repaint to stop flickering
style = style | wx.NO_FULL_REPAINT_ON_RESIZE
#call super function
#super(Graph, self).__init__(parent, id, pos, size, style)
BufferedCanvas.__init__(self, parent, id)
self.SetSize(wx.Size(150, 80))
super(Graph, self).__init__(parent, id, pos, size, style)
self.root = root
self.extruder0temps = [0]
self.extruder0targettemps = [0]
......@@ -51,18 +48,10 @@ class Graph(BufferedCanvas):
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):
dc = wx.PaintDC(self)
gc = wx.GraphicsContext.Create(dc)
def Destroy(self):
#call the super method
super(wx.Panel, self).Destroy()
def updateTemperatures(self, event):
self.AddBedTemperature(self.bedtemps[-1])
self.AddBedTargetTemperature(self.bedtargettemps[-1])
......@@ -80,9 +69,6 @@ class Graph(BufferedCanvas):
#b = gc.CreateLinearGradientBrush(0, 0, w, h, col1, col2)
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)))
......@@ -240,6 +226,7 @@ class Graph(BufferedCanvas):
self.Refresh()
def draw(self, dc, w, h):
dc.SetBackground(wx.Brush(self.root.settings.bgcolor))
dc.Clear()
gc = wx.GraphicsContext.Create(dc)
self.width = w
......
......@@ -13,6 +13,8 @@
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
import traceback
try:
import wx
except:
......@@ -26,6 +28,7 @@ from printrun import gviz
from printrun.xybuttons import XYButtons
from printrun.zbuttons import ZButtons
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):
button = wx.Button(parent, -1, label, style = style, size = size)
......@@ -45,7 +48,7 @@ class XYZControlsSizer(wx.GridBagSizer):
def __init__(self, root):
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)
root.zb = ZButtons(root.panel, root.moveZ, root.settings.bgcolor)
self.Add(root.zb, pos = (0, 2), flag = wx.ALIGN_CENTER)
......@@ -56,12 +59,12 @@ class LeftPane(wx.GridBagSizer):
def __init__(self, root):
super(LeftPane, self).__init__()
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.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:
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.SetForegroundColour("black")
btn.properties = i
......@@ -71,20 +74,21 @@ class LeftPane(wx.GridBagSizer):
if i.span == 0:
llts.Add(btn)
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.SetToolTip(wx.ToolTip("Set Maximum Speed for X & Y axes (mm/min)"))
llts.Add(wx.StaticText(root.panel,-1, _("XY:")), flag = wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
llts.Add(root.xyfeedc)
llts.Add(wx.StaticText(root.panel,-1, _("mm/min Z:")), flag = wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
llts.Add(wx.StaticText(root.panel,-1, _("mm/min Z:")), flag = wx.ALIGN_RIGHT|wx.ALIGN_CENTER_VERTICAL)
root.zfeedc = wx.SpinCtrl(root.panel,-1, str(root.settings.z_feedrate), min = 0, max = 50000, size = (70,-1))
root.zfeedc.SetToolTip(wx.ToolTip("Set Maximum Speed for Z axis (mm/min)"))
llts.Add(root.zfeedc,)
root.monitorbox = wx.CheckBox(root.panel,-1, _("Watch"))
root.monitorbox.SetValue(bool(root.settings.monitor))
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)
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):
if root.settings.last_temperature not in map(float, root.temps.values()):
htemp_choices = [str(root.settings.last_temperature)] + htemp_choices
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.Bind(wx.EVT_COMBOBOX, root.htemp_change)
......@@ -116,7 +120,7 @@ class LeftPane(wx.GridBagSizer):
if root.settings.last_bed_temperature not in map(float, root.bedtemps.values()):
btemp_choices = [str(root.settings.last_bed_temperature)] + btemp_choices
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.Bind(wx.EVT_COMBOBOX, root.btemp_change)
self.Add(root.btemp, pos = (3, 2), span = (1, 2))
......@@ -146,52 +150,116 @@ class LeftPane(wx.GridBagSizer):
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.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))
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.SetBackgroundColour((225, 200, 200))
root.efeedc.SetForegroundColour("black")
root.efeedc.Bind(wx.EVT_SPINCTRL, root.setfeeds)
self.Add(root.efeedc, pos = (5, 2), span = (1, 2))
self.Add(wx.StaticText(root.panel,-1, _("mm/\nmin")), pos = (5, 4), span = (1, 1))
root.efeedc.Bind(wx.EVT_TEXT, root.setfeeds)
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.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.SetForegroundColour("black")
root.graph = Graph(root.panel, wx.ID_ANY)
self.Add(root.graph, pos = (3, 5), span = (3, 3))
self.Add(root.tempdisp, pos = (6, 0), span = (1, 9))
if root.display_gauges:
root.hottgauge = TempGauge(root.panel, size = (-1, 24), title = _("Heater:"), maxval = 300)
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):
def __init__(self, root):
super(VizPane, self).__init__(wx.VERTICAL)
root.gviz = gviz.gviz(root.panel, (300, 300),
build_dimensions = root.build_dimensions_list,
grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2),
extrusion_width = root.settings.preview_extrusion_width)
root.gviz.SetToolTip(wx.ToolTip("Click to examine / edit\n layers of loaded file"))
root.gviz.showall = 1
try:
raise ""
import printrun.stlview
root.gwindow = printrun.stlview.GCFrame(None, wx.ID_ANY, 'Gcode view, shift to move view, mousewheel to set layer', size = (600, 600))
except:
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),
build_dimensions = root.build_dimensions_list,
grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2),
extrusion_width = root.settings.preview_extrusion_width)
root.gviz.SetToolTip(wx.ToolTip("Click to examine / edit\n layers of loaded file"))
root.gviz.showall = 1
root.gviz.Bind(wx.EVT_LEFT_DOWN, root.showwin)
use3dview = root.settings.viz3d
if use3dview:
try:
import printrun.gcview
objects = None
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:
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([],
build_dimensions = root.build_dimensions_list,
grid = (root.settings.preview_grid_step1, root.settings.preview_grid_step2),
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())
self.Add(root.gviz, 1, flag = wx.SHAPED)
cs = root.centersizer = wx.GridBagSizer()
self.Add(cs, 0, flag = wx.EXPAND)
root.gwindow.Bind(wx.EVT_CLOSE, lambda x: root.gwindow.Hide())
if not isinstance(root.gviz, NoViz):
self.Add(root.gviz.widget, 1, flag = wx.SHAPED)
root.centersizer = wx.GridBagSizer()
self.Add(root.centersizer, 0, flag = wx.EXPAND)
class LogPane(wx.BoxSizer):
......@@ -199,6 +267,7 @@ class LogPane(wx.BoxSizer):
super(LogPane, self).__init__(wx.VERTICAL)
root.lowerrsizer = self
root.logbox = wx.TextCtrl(root.panel, style = wx.TE_MULTILINE, size = (350,-1))
root.logbox.SetMinSize((100,-1))
root.logbox.SetEditable(0)
self.Add(root.logbox, 1, wx.EXPAND)
lbrs = wx.BoxSizer(wx.HORIZONTAL)
......@@ -223,7 +292,7 @@ class MainToolbar(wx.BoxSizer):
root.serialport = wx.ComboBox(root.panel, -1,
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.rescanports()
self.Add(root.serialport)
......@@ -263,10 +332,10 @@ class MainWindow(wx.Frame):
self.mainsizer = wx.BoxSizer(wx.VERTICAL)
self.uppersizer = MainToolbar(self)
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(LogPane(self), 0, wx.EXPAND)
self.mainsizer.Add(self.uppersizer)
self.lowersizer.Add(LogPane(self), 1, wx.EXPAND)
self.mainsizer.Add(self.uppersizer, 0)
self.mainsizer.Add(self.lowersizer, 1, wx.EXPAND)
self.panel.SetSizer(self.mainsizer)
self.status = self.CreateStatusBar()
......@@ -276,6 +345,12 @@ class MainWindow(wx.Frame):
self.mainsizer.Layout()
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
self.pausebtn.Disable()
......
......@@ -12,7 +12,11 @@
#
# You should have received a copy of the GNU General Public License
# along with Printrun. If not, see <http://www.gnu.org/licenses/>.
from Queue import Queue
from collections import deque
import wx, time
from printrun import gcoder
from printrun_utils import imagefile
......@@ -20,64 +24,51 @@ ID_ABOUT = 101
ID_EXIT = 110
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):
wx.Frame.__init__(self, None, title = "Gcode view, shift to move view, mousewheel to set layer", size = (size[0], size[1]))
self.p = gviz(self, size = size, build_dimensions = build_dimensions, grid = grid, extrusion_width = extrusion_width)
wx.Frame.__init__(self, None, title = "Gcode view, shift to move view, mousewheel to set layer", size = size)
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)
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(2, wx.Image(imagefile('zoom_out.png'), wx.BITMAP_TYPE_PNG).ConvertToBitmap(), 'Zoom Out [-]', '')
toolbar.AddSeparator()
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(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.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()
vbox.Add(toolbar, 0, border = 5)
self.SetSizer(vbox)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200, 200, 1.2), id = 1)
self.Bind(wx.EVT_TOOL, lambda x:self.p.zoom(200, 200, 1/1.2), id = 2)
vbox.Add(self.p, 1, wx.EXPAND)
panel.SetSizer(vbox)
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.layerdown(), id = 4)
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.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.p.Bind(wx.EVT_MOUSEWHEEL, self.zoom)
self.Bind(wx.EVT_MOUSEWHEEL, self.zoom)
self.p.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):
self.p.translate = [0.0, 0.0]
self.p.scale = self.p.basescale
......@@ -85,19 +76,17 @@ class window(wx.Frame):
def mouse(self, event):
if event.ButtonUp(wx.MOUSE_BTN_LEFT):
if(self.initpos is not None):
if self.initpos is not None:
self.initpos = None
elif event.Dragging():
e = event.GetPositionTuple()
if self.initpos is None or not hasattr(self, "basetrans"):
self.initpos = e
self.basetrans = self.p.translate
#print self.p.translate, e, self.initpos
self.p.translate = [ self.basetrans[0]+(e[0]-self.initpos[0]),
self.basetrans[1]+(e[1]-self.initpos[1]) ]
self.p.repaint()
self.p.Refresh()
self.p.translate = [self.basetrans[0] + (e[0] - self.initpos[0]),
self.basetrans[1] + (e[1] - self.initpos[1])]
self.p.dirty = 1
wx.CallAfter(self.p.Refresh)
else:
event.Skip()
......@@ -108,18 +97,7 @@ class window(wx.Frame):
kzi = [388, 316, 61] # Zoom In Keys
kzo = [390, 314, 45] # Zoom Out Keys
x = event.GetKeyCode()
#print "Key event - "+str(x)
#if event.ShiftDown():
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:
self.p.layerup()
if x in kdo:
......@@ -129,7 +107,6 @@ class window(wx.Frame):
if x in kzo:
self.p.zoom(cx, cy, 1/1.2)
#print p.lines.keys()
def zoom(self, event):
z = event.GetWheelRotation()
if event.ShiftDown():
......@@ -140,9 +117,22 @@ class window(wx.Frame):
elif z < 0: self.p.zoom(event.GetX(), event.GetY(), 1/1.2)
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]))
self.parent = parent
# Mark canvas as dirty when setting showall
_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.build_dimensions = build_dimensions
self.grid = grid
......@@ -157,7 +147,7 @@ class gviz(wx.Panel):
self.layers = []
self.layerindex = 0
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
penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0))
self.translate = [0.0, 0.0]
......@@ -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.penslist = [self.mainpen, self.travelpen, self.hlpen]+self.fades
self.showall = 0
self.hilight = []
self.hilightarcs = []
self.hilight = deque()
self.hilightarcs = deque()
self.hilightqueue = Queue(0)
self.hilightarcsqueue = Queue(0)
self.dirty = 1
self.blitmap = wx.EmptyBitmap(self.GetClientSize()[0], self.GetClientSize()[1],-1)
......@@ -178,6 +170,14 @@ class gviz(wx.Panel):
print"Inject code here..."
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):
self.lastpos = [0, 0, 0, 0, 0, 0, 0]
self.lines = {}
......@@ -185,47 +185,49 @@ class gviz(wx.Panel):
self.arcs = {}
self.arcpens = {}
self.layers = []
self.hilight = []
self.hilightarcs = []
self.clearhilights()
self.layerindex = 0
self.showall = 0
self.dirty = 1
#self.repaint()
wx.CallAfter(self.Refresh)
def layerup(self):
if(self.layerindex+1<len(self.layers)):
self.layerindex+=1
# Display layer info on statusbar (Jezmy)
self.parent.SetStatusText("Layer "+str(self.layerindex +1)+" - Going Up - Z = "+str(self.layers[self.layerindex])+" mm", 0)
self.repaint()
self.Refresh()
if self.layerindex + 1 < len(self.layers):
self.layerindex += 1
self.parent.SetStatusText("Layer %d - Going Up - Z = %.03f mm" % (self.layerindex + 1, self.layers[self.layerindex]), 0)
self.dirty = 1
wx.CallAfter(self.Refresh)
def layerdown(self):
if(self.layerindex>0):
self.layerindex-=1
# Display layer info on statusbar (Jezmy)
self.parent.SetStatusText("Layer "+str(self.layerindex + 1)+" - Going Down - Z = "+str(self.layers[self.layerindex])+ " mm", 0)
self.repaint()
self.Refresh()
if self.layerindex > 0:
self.layerindex -= 1
self.parent.SetStatusText("Layer %d - Going Down - Z = %.03f mm" % (self.layerindex + 1, self.layers[self.layerindex]), 0)
self.dirty = 1
wx.CallAfter(self.Refresh)
def setlayer(self, layer):
try:
if layer in self.layers:
self.layerindex = self.layers.index(layer)
self.repaint()
wx.CallAfter(self.Refresh)
self.dirty = 1
self.showall = 0
except:
pass
wx.CallAfter(self.Refresh)
def resize(self, event):
size = self.GetClientSize()
size = [max(1.0, size[0]), max(1.0, size[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 update_basescale(self):
self.basescale = 2*[min(float(self.size[0] - 1)/self.build_dimensions[0],
float(self.size[1] - 1)/self.build_dimensions[1])]
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):
if x == -1 and y == -1:
side = min(self.size)
x = y = side / 2
self.scale = [s * factor for s in self.scale]
self.translate = [ x - (x-self.translate[0]) * factor,
......@@ -233,12 +235,35 @@ class gviz(wx.Panel):
penwidth = max(1.0, self.filament_width*((self.scale[0]+self.scale[1])/2.0))
for pen in self.penslist:
pen.SetWidth(penwidth)
#self.dirty = 1
self.repaint()
self.Refresh()
def repaint(self):
self.dirty = 1
wx.CallAfter(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 _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)
dc = wx.MemoryDC()
dc.SelectObject(self.blitmap)
......@@ -252,150 +277,194 @@ class gviz(wx.Panel):
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.SetPen(wx.Pen(wx.Colour(0, 0, 0)))
if not self.showall:
self.size = self.GetSize()
dc.SetBrush(wx.Brush((43, 144, 255)))
dc.DrawRectangle(self.size[0]-15, 0, 15, self.size[1])
dc.SetBrush(wx.Brush((0, 255, 0)))
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)
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:
l = []
for i in self.layers:
dc.DrawLineList(l, self.fades[0])
_drawlines(self.lines[i], self.pens[i])
_drawarcs(self.arcs[i], self.arcpens[i])
self._drawlines(dc, self.lines[i], self.pens[i])
self._drawarcs(dc, self.arcs[i], self.arcpens[i])
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)
_drawarcs(self.hilightarcs, self.hlpen)
if self.layerindex < len(self.layers) and self.layers[self.layerindex] in self.lines:
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)
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):
if self.dirty:
self.dirty = 0
self.repaint_everything()
self.paint_hilights()
dc = wx.PaintDC(self)
if(self.dirty):
self.repaint()
self.dirty = 0
sz = self.GetClientSize()
dc.DrawBitmap(self.blitmap, 0, 0)
del dc
def addfile(self, gcodes = []):
def addfile(self, gcode):
self.clear()
for i in gcodes:
self.addgcode(i)
self.add_parsed_gcodes(gcode.lines)
# 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):
gcode = gcode.split("*")[0]
gcode = gcode.split(";")[0]
gcode = gcode.lower().strip().split()
if len(gcode) == 0:
gcode = gcode.lower().strip()
if not gcode:
return
if gcode[0][0] == 'n':
gcode.pop(0)
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
gline = gcoder.Line(gcode)
gline.parse_coordinates(False)
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):
return x-self.build_dimensions[3]
return x - self.build_dimensions[3]
start_pos = self.hilightpos[:] if hilight else self.lastpos[:]
if gline.command not in ["G0", "G1", "G2", "G3"]:
return
if gcode[0] in [ "g0", "g1" ]:
target = _readgcode()
line = [ _x(start_pos[0]), _y(start_pos[1]), _x(target[0]), _y(target[1]) ]
start_pos = self.hilightpos[:] if hilight else self.lastpos[:]
target = start_pos[:]
target[5] = 0.0
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:
self.lines[ target[2] ] += [line]
self.pens[ target[2] ] += [self.mainpen if target[3] != self.lastpos[3] else self.travelpen]
self.lastpos = target
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)
else:
self.hilight += [line]
self.hilightpos = target
self.dirty = 1
if gcode[0] in [ "g2", "g3" ]:
target = _readgcode()
arc = []
arc += [ _x(start_pos[0]), _y(start_pos[1]) ]
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
self.hilight.append(line)
self.hilightqueue.put_nowait(line)
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]
if not hilight:
self.arcs[ target[2] ] += [arc]
self.arcpens[ target[2] ] += [self.arcpen]
self.lastpos = target
self.arcs[z].append(arc)
self.arcpens[z].append(self.arcpen)
else:
self.hilightarcs += [arc]
self.hilightpos = target
self.hilightarcs.append(arc)
self.hilightarcsqueue.put_nowait(arc)
if not hilight:
self.lastpos = target
self.dirty = 1
else:
self.hilightpos = target
self.Refresh()
if __name__ == '__main__':
import sys
app = wx.App(False)
#main = window(open("/home/kliment/designs/spinner/arm_export.gcode"))
main = window(open("jam.gcode"))
main = window(open(sys.argv[1]))
main.Show()
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):
reindented += self.indent_chars + line + "\n"
return reindented
class options(wx.Dialog):
class PronterOptionsDialog(wx.Dialog):
"""Options editor"""
def __init__(self, pronterface):
wx.Dialog.__init__(self, None, title = _("Edit settings"), style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
topsizer = wx.BoxSizer(wx.VERTICAL)
vbox = wx.StaticBoxSizer(wx.StaticBox(self, label = _("Defaults")) ,wx.VERTICAL)
topsizer.Add(vbox, 1, wx.ALL+wx.EXPAND)
wx.Dialog.__init__(self, parent = None, title = _("Edit settings"), size = (400, 500), style = wx.DEFAULT_DIALOG_STYLE)
panel = wx.Panel(self)
header = wx.StaticBox(panel, label = _("Settings"))
sbox = wx.StaticBoxSizer(header, wx.VERTICAL)
panel2 = wx.Panel(panel)
grid = wx.FlexGridSizer(rows = 0, cols = 2, hgap = 8, vgap = 2)
grid.SetFlexibleDirection( wx.BOTH )
grid.AddGrowableCol( 1 )
grid.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )
vbox.Add(grid, 0, wx.EXPAND)
ctrls = {}
for k, v in sorted(pronterface.settings._all_settings().items()):
ctrls[k, 0] = wx.StaticText(self,-1, k)
ctrls[k, 1] = wx.TextCtrl(self,-1, str(v))
if k in pronterface.helpdict:
ctrls[k, 0].SetToolTipString(pronterface.helpdict.get(k))
ctrls[k, 1].SetToolTipString(pronterface.helpdict.get(k))
grid.Add(ctrls[k, 0], 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.ALIGN_RIGHT)
grid.Add(ctrls[k, 1], 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.EXPAND)
topsizer.Add(self.CreateSeparatedButtonSizer(wx.OK+wx.CANCEL), 0, wx.EXPAND)
self.SetSizer(topsizer)
topsizer.Layout()
topsizer.Fit(self)
if self.ShowModal() == wx.ID_OK:
for k, v in pronterface.settings._all_settings().items():
if ctrls[k, 1].GetValue() != str(v):
pronterface.set(k, str(ctrls[k, 1].GetValue()))
self.Destroy()
grid.SetFlexibleDirection(wx.BOTH)
grid.AddGrowableCol(1)
grid.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
for setting in pronterface.settings._all_settings():
grid.Add(setting.get_label(panel2), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL|wx.ALIGN_RIGHT)
grid.Add(setting.get_widget(panel2), 1, wx.ALIGN_CENTER_VERTICAL|wx.ALL)
panel2.SetSizer(grid)
sbox.Add(panel2, 1, wx.EXPAND)
panel.SetSizer(sbox)
topsizer = wx.BoxSizer(wx.VERTICAL)
topsizer.Add(panel, 1, wx.ALL | wx.EXPAND)
topsizer.Add(self.CreateButtonSizer(wx.OK | wx.CANCEL), 0, wx.ALIGN_RIGHT)
self.SetSizerAndFit(topsizer)
self.SetMinSize(self.GetSize())
def PronterOptions(pronterface):
dialog = PronterOptionsDialog(pronterface)
if dialog.ShowModal() == wx.ID_OK:
for setting in pronterface.settings._all_settings():
old_value = setting.value
setting.update()
if setting.value != old_value:
pronterface.set(setting.name, setting.value)
dialog.Destroy()
class ButtonEdit(wx.Dialog):
"""Custom button edit dialog"""
......@@ -211,6 +214,113 @@ class ButtonEdit(wx.Dialog):
if self.name.GetValue()=="":
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):
label = None
......
......@@ -24,8 +24,8 @@ import time
import threading
import pyglet
pyglet.options['shadow_window'] = False
pyglet.options['debug_gl'] = False
pyglet.options['debug_gl'] = True
from pyglet.gl import *
......@@ -233,7 +233,7 @@ def vdiff(v, o):
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.
vertices = []
normals = []
......@@ -242,8 +242,12 @@ class gcview(object):
self.vlists = []
self.layers = {}
t0 = time.time()
lines = [self.transform(i) for i in lines]
lines = [i for i in lines if i is not None]
if gcode:
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)
t0 = time.time()
layertemp = {}
......@@ -355,29 +359,30 @@ class gcview(object):
epoints = map(lambda x: vadd(E, x), points)
return spoints, epoints, S, E
def transform(self, line):
line = line.split(";")[0]
def transform(self, gline):
cur = self.prev[:]
if len(line) > 0:
if "G1" in line or "G0" in line or "G92" in line:
if("X" in line):
cur[0] = float(line.split("X")[1].split(" ")[0])
if("Y" in line):
cur[1] = float(line.split("Y")[1].split(" ")[0])
if("Z" in line):
cur[2] = float(line.split("Z")[1].split(" ")[0])
if("E" in line):
cur[3] = float(line.split("E")[1].split(" ")[0])
if self.prev == cur:
return None
if self.fline or "G92" in line:
self.prev = cur
self.fline = 0
return None
else:
r = [self.prev, cur]
self.prev = cur
return r
isg92 = (gline.command == "G92")
if gline.is_move or isg92:
if gline.x is not None:
cur[0] = gline.x
if gline.y is not None:
cur[1] = gline.y
if gline.z is not None:
cur[2] = gline.z
if gline.e is not None:
cur[3] = gline.e
if self.prev == cur:
return None
elif self.fline or isg92:
self.prev = cur
self.fline = 0
return None
else:
r = [self.prev, cur]
self.prev = cur
return r
else:
return None
def delete(self):
for i in self.vlists:
......@@ -833,7 +838,7 @@ class GCFrame(wx.Frame):
m.curlayer = 0.0
m.scale = [1.0, 1.0, 1.0]
m.batch = pyglet.graphics.Batch()
m.gc = gcview([], batch = m.batch)
m.gc = gcview(None, batch = m.batch)
self.models = {"": m}
self.l = d()
self.modelindex = 0
......
......@@ -41,7 +41,7 @@ class XYButtons(BufferedCanvas):
center = (124, 121)
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.keypad_bmp = wx.Image(imagefile("arrow_keys.png"), wx.BITMAP_TYPE_PNG).ConvertToBitmap()
self.keypad_idx = -1
......@@ -51,6 +51,7 @@ class XYButtons(BufferedCanvas):
self.moveCallback = moveCallback
self.cornerCallback = cornerCallback
self.spacebarCallback = spacebarCallback
self.zCallback = zcallback
self.enabled = False
# Remember the last clicked buttons, so we can repeat when spacebar pressed
self.lastMove = None
......@@ -60,8 +61,7 @@ class XYButtons(BufferedCanvas):
self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID)
self.SetSize(self.bg_bmp.GetSize())
BufferedCanvas.__init__(self, parent, ID, size=self.bg_bmp.GetSize())
# Set up mouse and keyboard event capture
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
......@@ -109,10 +109,13 @@ class XYButtons(BufferedCanvas):
self.update()
def getMovement(self):
xdir = [1, 0, -1, 0][self.quadrant]
ydir = [0, 1, 0, -1][self.quadrant]
xdir = [1, 0, -1, 0, 0, 0][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)
return (magnitude * xdir, magnitude * ydir)
if not zdir == 0:
magnitude=min(magnitude,10)
return (magnitude * xdir, magnitude * ydir, magnitude * zdir)
def lookupConcentric(self, radius):
idx = 0
......@@ -286,14 +289,21 @@ class XYButtons(BufferedCanvas):
self.quadrant = 2
elif evt.GetKeyCode() == wx.WXK_RIGHT:
self.quadrant = 0
elif evt.GetKeyCode() == wx.WXK_PAGEUP:
self.quadrant = 4
elif evt.GetKeyCode() == wx.WXK_PAGEDOWN:
self.quadrant = 5
else:
evt.Skip()
return
self.concentric = self.keypad_idx
x, y, z = self.getMovement()
if self.moveCallback:
self.concentric = self.keypad_idx
x, y = self.getMovement()
if x!=0 or y!=0 and self.moveCallback:
self.moveCallback(x, y)
if z!=0 and self.zCallback:
self.zCallback(z)
elif evt.GetKeyCode() == wx.WXK_SPACE:
self.spacebarCallback()
......@@ -347,7 +357,7 @@ class XYButtons(BufferedCanvas):
if self.concentric != None:
if self.concentric < len(XYButtons.concentric_circle_radii):
if self.quadrant != None:
x, y = self.getMovement()
x, y, z = self.getMovement()
if self.moveCallback:
self.lastMove = (x, y)
self.lastCorner = None
......
......@@ -46,9 +46,7 @@ class ZButtons(BufferedCanvas):
self.bgcolor.SetFromName(bgcolor)
self.bgcolormask = wx.Colour(self.bgcolor.Red(), self.bgcolor.Green(), self.bgcolor.Blue(), 128)
BufferedCanvas.__init__(self, parent, ID)
self.SetSize(wx.Size(59, 244))
BufferedCanvas.__init__(self, parent, ID, size=wx.Size(59, 244))
# Set up mouse and keyboard event capture
self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
......
......@@ -19,13 +19,17 @@ import cmd, sys
import glob, os, time, datetime
import sys, subprocess
import math, codecs
import shlex
from math import sqrt
from gcoder import GCode
import argparse
import printcore
from printrun.printrun_utils import install_locale
from printrun import gcoder
install_locale('pronterface')
from functools import wraps
if os.name == "nt":
try:
import _winreg
......@@ -44,93 +48,6 @@ except:
def dosify(name):
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():
y_or_n = raw_input("y/n: ")
if y_or_n == "y":
......@@ -139,27 +56,175 @@ def confirm():
return confirm()
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_validate(self, v):
# 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 _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):
# defaults here.
# the initial value determines the type
self.port = ""
self.baudrate = 115200
self.bedtemp_abs = 110
self.bedtemp_pla = 60
self.temperature_abs = 230
self.temperature_pla = 185
self.xy_feedrate = 3000
self.z_feedrate = 200
self.e_feedrate = 300
self.slicecommand = "python skeinforge/skeinforge_application/skeinforge_utilities/skeinforge_craft.py $s"
self.sliceoptscommand = "python skeinforge/skeinforge_application/skeinforge.py"
self.final_command = ""
self._add(StringSetting("port", "", _("Serial port"), _("Port used to communicate with printer")))
self._add(ComboSetting("baudrate", 115200, self._baudrate_list(), _("Baud rate"), _("Communications Speed (default: 115200)")))
self._add(SpinSetting("bedtemp_abs", 110, 0, 400, _("Bed temperature for ABS"), _("Heated Build Platform temp for ABS (default: 110 deg C)")))
self._add(SpinSetting("bedtemp_pla", 60, 0, 400, _("Bed temperature for PLA"), _("Heated Build Platform temp for PLA (default: 60 deg C)")))
self._add(SpinSetting("temperature_abs", 230, 0, 400, _("Bed temperature for ABS"), _("Extruder temp for ABS (default: 230 deg C)")))
self._add(SpinSetting("temperature_pla", 185, 0, 400, _("Bed temperature for PLA"), _("Extruder temp for PLA (default: 185 deg C)")))
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._add(SpinSetting("z_feedrate", 200, 0, 50000, _("Z manual feedrate"), _("Feedrate for Control Panel Moves in Z (default: 200mm/min)")))
self._add(SpinSetting("e_feedrate", 100, 0, 1000, _("E manual feedrate"), _("Feedrate for Control Panel Moves in Extrusions (default: 300mm/min)")))
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._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._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):
try:
......@@ -172,12 +237,15 @@ class Settings:
getattr(self, "_%s_validate"%key)(value)
except AttributeError:
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:
getattr(self, "_%s_cb"%key)(key, value)
except AttributeError:
pass
return value
def _tabcomplete(self, key):
try:
return getattr(self, "_%s_list"%key)()
......@@ -188,8 +256,9 @@ class Settings:
except AttributeError:
pass
return []
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:
......@@ -233,6 +302,7 @@ class pronsole(cmd.Cmd):
self.in_macro = False
self.p.onlinecb = self.online
self.f = None
self.fgcode = None
self.listing = 0
self.sdfiles = []
self.paused = False
......@@ -253,30 +323,14 @@ class pronsole(cmd.Cmd):
self.settings._bedtemp_pla_cb = self.set_temp_preset
self.monitoring = 0
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.webrequested = False
self.web_config = None
self.web_auth_config = None
self.promptstrs = {"offline" : "%(bold)suninitialized>%(normal)s ",
"fallback" : "%(bold)sPC>%(normal)s ",
"macro" : "%(bold)s..>%(normal)s ",
"online" : "%(bold)sT:%(extruder_temp_fancy)s %(progress_fancy)s >%(normal)s "}
def log(self, *msg):
print ''.join(str(i) for i in msg)
print u"".join(unicode(i) for i in msg)
def promptf(self):
"""A function to generate prompts so that we can do dynamic prompts. """
......@@ -611,7 +665,7 @@ class pronsole(cmd.Cmd):
#else:
# self.log("Removed '"+key+"' from '"+self.rc_filename+"'")
except Exception, e:
self.log("Saving failed for", key+":", str(e))
self.log("Saving failed for ", key+":", str(e))
finally:
del rci, rco
......@@ -671,20 +725,21 @@ class pronsole(cmd.Cmd):
def help_disconnect(self):
self.log("Disconnects from the printer")
def do_load(self,l):
self._do_load(l)
def do_load(self, filename):
self._do_load(filename)
def _do_load(self,l):
if len(l)==0:
def _do_load(self, filename):
if not filename:
self.log("No file name given.")
return
self.log("Loading file:"+l)
if not(os.path.exists(l)):
self.log("Loading file:" + filename)
if not os.path.exists(filename):
self.log("File not found!")
return
self.f = [i.replace("\n", "").replace("\r", "") for i in open(l)]
self.filename = l
self.log("Loaded ", l, ", ", len(self.f)," lines.")
self.f = [line.strip() for line in open(filename)]
self.fgcode = gcoder.GCode(self.f)
self.filename = filename
self.log("Loaded %s, %d lines." % (filename, len(self.f)))
def complete_load(self, text, line, begidx, endidx):
s = line.split()
......@@ -700,57 +755,45 @@ class pronsole(cmd.Cmd):
self.log("Loads a gcode file (with tab-completion)")
def do_upload(self, l):
if len(l) == 0:
self.log("No file name given.")
return
self.log("Loading file:"+l.split()[0])
if not(os.path.exists(l.split()[0])):
self.log("File not found!")
names = l.split()
if len(names) == 2:
filename = names[0]
targetname = names[1]
else:
self.log(_("Please enter target name in 8.3 format."))
return
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
self.log("Uploading as ", tname)
self.log(("Uploading "+self.filename))
self.p.send_now("M28 "+tname)
self.log(("Press Ctrl-C to interrupt upload."))
self.p.startprint(self.f)
self._do_load(filename)
self.log(_("Uploading as %s") % targetname)
self.log(_("Uploading %s") % self.filename)
self.p.send_now("M28 " + targetname)
self.log(_("Press Ctrl-C to interrupt upload."))
self.p.startprint(self.fgcode)
try:
sys.stdout.write("Progress: 00.0%")
sys.stdout.write(_("Progress: ") + "00.0%")
sys.stdout.flush()
time.sleep(1)
while self.p.printing:
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()
self.p.send_now("M29 "+tname)
self.sleep(0.2)
self.p.clear = 1
self.listing = 0
self.sdfiles = []
self.recvlisteners+=[self.listfiles]
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.")
self._do_ls(False)
self.log("\b\b\b\b\b100%.")
self.log(_("Upload completed. %s should now be on the card.") % targetname)
return
except:
self.log("...interrupted!")
self.log(_("...interrupted!"))
self.p.pause()
self.p.send_now("M29 "+tname)
self.p.send_now("M29 "+targetname)
time.sleep(0.2)
self.p.clear = 1
self.p.startprint([])
self.log("A partial file named ", tname, " may have been written to the sd card.")
self.p.startprint(None)
self.log(_("A partial file named %s may have been written to the sd card.") % targetname)
def complete_upload(self, text, line, begidx, endidx):
s = line.split()
......@@ -766,44 +809,38 @@ class pronsole(cmd.Cmd):
self.log("Uploads a gcode file to the sd card")
def help_print(self):
if self.f is None:
self.log("Send a loaded gcode file to the printer. Load a file with the load command first.")
if not self.fgcode:
self.log(_("Send a loaded gcode file to the printer. Load a file with the load command first."))
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):
if self.f is None:
self.log("No file loaded. Please use load first.")
if not self.fgcode:
self.log(_("No file loaded. Please use load first."))
return
if not self.p.online:
self.log("Not connected to printer.")
self.log(_("Not connected to printer."))
return
self.log(("printing "+self.filename))
self.log(("You can monitor the print with the monitor command."))
self.p.startprint(self.f)
#self.p.pause()
#self.paused = True
#self.do_resume(None)
self.log(_("Printing %s") % self.filename)
self.log(_("You can monitor the print with the monitor command."))
self.p.startprint(self.fgcode)
def do_pause(self, l):
if self.sdprinting:
self.p.send_now("M25")
else:
if(not self.p.printing):
self.log("Not self.log(ing, cannot pause.")
if not self.p.printing:
self.log(_("Not printing, cannot pause."))
return
self.p.pause()
#self.p.connect()# This seems to work, but is not a good solution.
self.paused = True
#self.do_resume(None)
def help_pause(self):
self.log("Pauses a running print")
self.log(_("Pauses a running print"))
def do_resume(self, l):
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
self.paused = False
if self.sdprinting:
......@@ -813,7 +850,7 @@ class pronsole(cmd.Cmd):
self.p.resume()
def help_resume(self):
self.log("Resumes a paused print.")
self.log(_("Resumes a paused print."))
def emptyline(self):
pass
......@@ -821,38 +858,43 @@ class pronsole(cmd.Cmd):
def do_shell(self, l):
exec(l)
def listfiles(self, line):
def listfiles(self, line, echo = False):
if "Begin file list" in line:
self.listing = 1
elif "End file list" in line:
self.listing = 0
self.recvlisteners.remove(self.listfiles)
if echo:
self.log(_("Files on SD card:"))
self.log("\n".join(self.sdfiles))
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):
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
self.listing = 2
self.sdfiles = []
self.recvlisteners+=[self.listfiles]
self.p.send_now("M20")
time.sleep(0.5)
self.log(" ".join(self.sdfiles))
self._do_ls(True)
def help_ls(self):
self.log("lists files on the SD card")
self.log(_("Lists files on the SD card"))
def waitforsdresponse(self, l):
if "file.open failed" in l:
self.log("Opening file failed.")
self.log(_("Opening file failed."))
self.recvlisteners.remove(self.waitforsdresponse)
return
if "File opened" in l:
self.log(l)
if "File selected" in l:
self.log("Starting print")
self.log(_("Starting print"))
self.p.send_now("M24")
self.sdprinting = 1
#self.recvlisteners.remove(self.waitforsdresponse)
......@@ -875,36 +917,33 @@ class pronsole(cmd.Cmd):
self.p.reset()
def help_reset(self):
self.log("Resets the printer.")
self.log(_("Resets the printer."))
def do_sdprint(self, l):
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
self.listing = 2
self.sdfiles = []
self.recvlisteners+=[self.listfiles]
self.p.send_now("M20")
time.sleep(0.5)
if not (l.lower() in self.sdfiles):
self.log("File is not present on card. Upload it first")
self._do_ls(False)
while self.listfiles in self.recvlisteners:
time.sleep(0.1)
if l.lower() not in self.sdfiles:
self.log(_("File is not present on card. Please upload it first."))
return
self.recvlisteners+=[self.waitforsdresponse]
self.p.send_now("M23 "+l.lower())
self.log("printing file: "+l.lower()+" from SD card.")
self.log("Requesting SD print...")
self.recvlisteners.append(self.waitforsdresponse)
self.p.send_now("M23 " + l.lower())
self.log(_("Printing file: %s from SD card.") % l.lower())
self.log(_("Requesting SD print..."))
time.sleep(1)
def help_sdprint(self):
self.log("print a file from the SD card. Tabcompletes with available file names.")
self.log("sdprint filename.g")
self.log(_("Print a file from the SD card. Tab completes with available file names."))
self.log(_("sdprint filename.g"))
def complete_sdprint(self, text, line, begidx, endidx):
if self.sdfiles==[] and self.p.online:
self.listing = 2
self.recvlisteners+=[self.listfiles]
self.p.send_now("M20")
time.sleep(0.5)
if not self.sdfiles and self.p.online:
self._do_ls(False)
while self.listfiles in self.recvlisteners:
time.sleep(0.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)]
......@@ -927,19 +966,27 @@ class pronsole(cmd.Cmd):
self.log("! os.listdir('.')")
def default(self, l):
if(l[0] in self.commandprefixes.upper()):
if(self.p and self.p.online):
if(not self.p.loud):
if l[0] in self.commandprefixes.upper():
if self.p and self.p.online:
if not self.p.loud:
self.log("SENDING:"+l)
self.p.send_now(l)
else:
self.log("printer is not online.")
self.log(_("Printer is not online."))
return
elif(l[0] in self.commandprefixes.lower()):
if(self.p and self.p.online):
if(not self.p.loud):
elif l[0] in self.commandprefixes.lower():
if self.p and self.p.online:
if not self.p.loud:
self.log("SENDING:"+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:
self.log("printer is not online.")
return
......@@ -951,7 +998,7 @@ class pronsole(cmd.Cmd):
def tempcb(self, 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):
if "dynamic" in l:
......@@ -966,7 +1013,7 @@ class pronsole(cmd.Cmd):
print "Bed: %s/%s" % (self.status.bed_temp, self.status.bed_temp_target)
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):
try:
......@@ -976,22 +1023,22 @@ class pronsole(cmd.Cmd):
f = float(l)
if f>=0:
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():
return
if self.p.online:
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:
self.log("printer is not online.")
self.log(_("Printer is not online."))
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:
self.log("You must enter a temperature.")
self.log(_("You must enter a temperature."))
def help_settemp(self):
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(_("Sets the hotend temperature to the value entered."))
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()]))
def complete_settemp(self, text, line, begidx, endidx):
......@@ -1007,17 +1054,17 @@ class pronsole(cmd.Cmd):
if f>=0:
if self.p.online:
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:
self.log("printer is not online.")
self.log(_("Printer is not online."))
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:
self.log("You must enter a temperature.")
self.log(_("You must enter a temperature."))
def help_bedtemp(self):
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(_("Sets the bed temperature to the value entered."))
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()]))
def complete_bedtemp(self, text, line, begidx, endidx):
......@@ -1026,13 +1073,13 @@ class pronsole(cmd.Cmd):
def do_move(self, l):
if(len(l.split())<2):
self.log("No move specified.")
self.log(_("No move specified."))
return
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
if not self.p.online:
self.log("printer is not online. Unable to move.")
self.log(_("Printer is not online. Unable to move."))
return
l = l.split()
if(l[0].lower()=="x"):
......@@ -1048,13 +1095,13 @@ class pronsole(cmd.Cmd):
feed = self.settings.e_feedrate
axis = "E"
else:
self.log("Unknown axis.")
self.log(_("Unknown axis."))
return
dist = 0
try:
dist = float(l[1])
except:
self.log("Invalid distance")
self.log(_("Invalid distance"))
return
try:
feed = int(l[2])
......@@ -1065,11 +1112,11 @@ class pronsole(cmd.Cmd):
self.p.send_now("G90")
def help_move(self):
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 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("Common amounts are in the tabcomplete list.")
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 %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 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."))
def complete_move(self, text, line, begidx, endidx):
if (len(line.split()) == 2 and line[-1] != " ") or (len(line.split()) == 1 and line[-1]==" "):
......@@ -1089,70 +1136,70 @@ class pronsole(cmd.Cmd):
length = 5#default extrusion length
feed = self.settings.e_feedrate#default speed
if not self.p.online:
self.log("printer is not online. Unable to move.")
self.log("Printer is not online. Unable to extrude.")
return
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
ls = l.split()
if len(ls):
try:
length = float(ls[0])
except:
self.log("Invalid length given.")
self.log(_("Invalid length given."))
if len(ls)>1:
try:
feed = int(ls[1])
except:
self.log("Invalid speed given.")
self.log(_("Invalid speed given."))
if override is not None:
length = override
feed = overridefeed
if length > 0:
self.log("Extruding %fmm of filament."%(length,))
elif length <0:
self.log("Reversing %fmm of filament."%(-1*length,))
self.log(_("Extruding %fmm of filament.") % (length,))
elif length < 0:
self.log(_("Reversing %fmm of filament.") % (-1*length,))
else:
"Length is 0, not doing anything."
self.log(_("Length is 0, not doing anything."))
self.p.send_now("G91")
self.p.send_now("G1 E"+str(length)+" F"+str(feed))
self.p.send_now("G90")
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("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 -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(_("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 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 10 210 - extrudes 10mm of filament at 210mm/min (3.5mm/s)"))
def do_reverse(self, l):
length = 5#default extrusion length
feed = self.settings.e_feedrate#default speed
if not self.p.online:
self.log("printer is not online. Unable to move.")
self.log(_("Printer is not online. Unable to reverse."))
return
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
ls = l.split()
if len(ls):
try:
length = float(ls[0])
except:
self.log("Invalid length given.")
self.log(_("Invalid length given."))
if len(ls)>1:
try:
feed = int(ls[1])
except:
self.log("Invalid speed given.")
self.log(_("Invalid speed given."))
self.do_extrude("", length*-1.0, feed)
def help_reverse(self):
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 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 -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)")
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 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 -5 - EXTRUDES 5mm of filament at 300mm/min (5mm/s)"))
def do_exit(self, l):
if self.status.extruder_temp_target != 0:
......@@ -1169,28 +1216,28 @@ class pronsole(cmd.Cmd):
print "(this will terminate the print)."
if not confirm():
return False
self.log("Exiting program. Goodbye!")
self.log(_("Exiting program. Goodbye!"))
self.p.disconnect()
return True
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):
interval = 5
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
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
self.log("Monitoring printer, use ^C to interrupt.")
self.log(_("Monitoring printer, use ^C to interrupt."))
if len(l):
try:
interval = float(l)
except:
self.log("Invalid period given.")
self.log("Updating values every %f seconds."%(interval,))
self.log(_("Invalid period given."))
self.log(_("Updating values every %f seconds.") % (interval,))
self.monitoring = 1
prev_msg_len = 0
try:
......@@ -1201,10 +1248,10 @@ class pronsole(cmd.Cmd):
time.sleep(interval)
#print (self.tempreadings.replace("\r", "").replace("T", "Hotend").replace("B", "Bed").replace("\n", "").replace("ok ", ""))
if self.p.printing:
preface = "Print progress: "
preface = _("Print progress: ")
progress = 100*float(self.p.queueindex)/len(self.p.mainqueue)
elif self.sdprinting:
preface = "Print progress: "
preface = _("Print progress: ")
progress = self.percentdone
progress = int(progress*10)/10.0 #limit precision
prev_msg = preface + str(progress) + "%"
......@@ -1213,13 +1260,13 @@ class pronsole(cmd.Cmd):
sys.stdout.flush()
prev_msg_len = len(prev_msg)
except KeyboardInterrupt:
if self.silent == False: print "Done monitoring."
if self.silent == False: print _("Done monitoring.")
self.monitoring = 0
def help_monitor(self):
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 2 - Reports temperature and SD print status (if SD printing) every 2 seconds")
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 2 - Reports temperature and SD print status (if SD printing) every 2 seconds"))
def expandcommand(self, c):
return c.replace("$python", sys.executable)
......@@ -1227,31 +1274,30 @@ class pronsole(cmd.Cmd):
def do_skein(self, l):
l = l.split()
if len(l) == 0:
self.log("No file name given.")
self.log(_("No file name given."))
return
settings = 0
if(l[0]=="set"):
settings = 1
else:
self.log("Skeining file:"+l[0])
self.log(_("Skeining file: %s") % l[0])
if not(os.path.exists(l[0])):
self.log("File not found!")
self.log(_("File not found!"))
return
try:
import shlex
if(settings):
if settings:
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))
else:
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())]
subprocess.call(params)
self.log("Loading sliced file.")
self.log(_("Loading sliced file."))
self.do_load(l[0].replace(".stl", "_export.gcode"))
except Exception, e:
self.log("Skeinforge execution failed: ", e)
self.log(_("Skeinforge execution failed: %s") % e)
def complete_skein(self, text, line, begidx, endidx):
s = line.split()
......@@ -1264,18 +1310,17 @@ class pronsole(cmd.Cmd):
return glob.glob("*/")+glob.glob("*.stl")
def help_skein(self):
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 view - create gcode file and view using skeiniso")
self.log("skein set - adjust slicer settings")
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 view - create gcode file and view using skeiniso"))
self.log(_("skein set - adjust slicer settings"))
def do_home(self, l):
if not self.p.online:
self.log("printer is not online. Unable to move.")
self.log(_("Printer is not online. Unable to move."))
return
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
if "x" in l.lower():
self.p.send_now("G28 X0")
......@@ -1290,35 +1335,36 @@ class pronsole(cmd.Cmd):
self.p.send_now("G92 E0")
def help_home(self):
self.log("Homes the printer")
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 z - homes z axis only (Using G28)")
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)")
def parse_cmdline(self, args):
import getopt
opts, args = getopt.getopt(args, "c:e:hw", ["conf = ", "config = ", "help"])
for o, a in opts:
#self.log(repr((o, a)))
if o in ("-c", "--conf", "--config"):
self.load_rc(a)
elif o in ("-h", "--help"):
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()
self.log(_("Homes the printer"))
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 z - homes z axis only (Using G28)"))
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)"))
def add_cmdline_arguments(self, parser):
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 = [])
parser.add_argument('-e','--execute', help = _("executes command after configuration/.pronsolerc is loaded ; macros/settings from these commands are not autosaved"), action = "append", default = [])
parser.add_argument('filename', nargs='?', help = _("file to load"))
def process_cmdline_arguments(self, args):
for config in args.conf:
self.load_rc(config)
if not self.rc_loaded:
self.load_default_rc()
for o, a in opts:
if o == "-e":
self.processing_args = True
self.onecmd(a)
self.processing_args = False
self.processing_args = True
for command in args.execute:
self.onecmd(command)
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 .
# It's default behavior with reagrds to Ctr-C
......
......@@ -26,6 +26,7 @@ except:
print _("WX is not installed. This program requires WX to run.")
raise
import sys, glob, time, datetime, threading, traceback, cStringIO, subprocess
import shlex
from printrun.pronterface_widgets import *
from serial import SerialException
......@@ -45,9 +46,8 @@ import printcore
from printrun.printrun_utils import pixmapfile, configfile
from printrun.gui import MainWindow
import pronsole
def dosify(name):
return os.path.split(name)[1].split(".")[0][:8]+".g"
from pronsole import dosify, wxSetting, HiddenSetting, StringSetting, SpinSetting, FloatSpinSetting, BooleanSetting
from printrun import gcoder
def parse_temperature_report(report, key):
if key in report:
......@@ -81,30 +81,118 @@ class Tee(object):
def flush(self):
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):
def __init__(self, filename = None, size = winsize):
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
self.settings.last_bed_temperature = 0.0
self.settings.last_file_path = ""
self.settings.last_temperature = 0.0
self.settings.preview_extrusion_width = 0.5
self.settings.preview_grid_step1 = 10.
self.settings.preview_grid_step2 = 50.
self.settings.bgcolor = "#FFFFFF"
#default build dimensions are 200x200x100 with 0, 0, 0 in the corner of the bed and endstops at 0, 0 and 0
monitorsetting = BooleanSetting("monitor", False)
monitorsetting.hidden = True
self.settings._add(monitorsetting)
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._add(BooleanSetting("viz3d", False, _("Enable 3D viewer (requires restarting)"), _("Use 3D visualization instead of 2D layered visualization")))
self.settings._add(ComboSetting("mainviz", "2D", ["2D", "3D", "None"], _("Main visualization"), _("Select visualization for main window.")))
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.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
os.putenv("UBUNTU_MENUPROXY", "0")
MainWindow.__init__(self, None, title = _("Printer Interface"), size = size);
......@@ -120,8 +208,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.f = None
self.skeinp = None
self.monitor_interval = 3
self.current_pos = [0, 0, 0]
self.paused = False
self.sentlines = Queue.Queue(30)
self.sentlines = Queue.Queue(0)
self.cpbuttons = [
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")),
......@@ -130,8 +219,9 @@ class PronterWindow(MainWindow, pronsole.pronsole):
]
self.custombuttons = []
self.btndict = {}
self.autoconnect = False
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
......@@ -185,6 +275,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skeining = 0
self.mini = False
self.p.sendcb = self.sentcb
self.p.printsendcb = self.printsentcb
self.p.startcb = self.startcb
self.p.endcb = self.endcb
self.starttime = 0
......@@ -196,18 +287,32 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.predisconnect_layer = None
self.hsetpoint = 0.0
self.bsetpoint = 0.0
if self.autoconnect:
self.connect()
if self.filename is not None:
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):
self.starttime = time.time()
print "Print Started at: " + format_time(self.starttime)
print _("Print Started at: %s") % format_time(self.starttime)
def endcb(self):
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 "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.printbtn.SetLabel, _("Print"))
......@@ -216,7 +321,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
param = self.settings.final_command
if not param:
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())]
self.finalp = subprocess.Popen(pararray, stderr = subprocess.STDOUT, stdout = subprocess.PIPE)
......@@ -237,44 +341,36 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.printbtn.Enable)
def sentcb(self, line):
if "G1" in line:
if "Z" in line:
try:
layer = float(line.split("Z")[1].split()[0])
if layer != self.curlayer:
self.curlayer = layer
self.gviz.hilight = []
threading.Thread(target = wx.CallAfter, args = (self.gviz.setlayer, layer)).start()
except:
pass
try:
self.sentlines.put_nowait(line)
except:
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)
except:
pass
try:
self.sentlines.put_nowait(line)
except:
pass
if "M140" in line:
if "S" in line:
try:
temp = float(line.split("S")[1].split("*")[0])
wx.CallAfter(self.graph.SetBedTargetTemperature, temp)
except:
pass
try:
self.sentlines.put_nowait(line)
except:
pass
gline = gcoder.Line(line)
gline.parse_coordinates(imperial = False)
if gline.is_move:
if gline.z != None:
layer = gline.z
if layer != self.curlayer:
self.curlayer = layer
self.gviz.clearhilights()
wx.CallAfter(self.gviz.setlayer, layer)
elif gline.command in ["M104", "M109"]:
gline.parse_coordinates(imperial = False, force = True)
if gline.s != None:
temp = gline.s
if self.display_gauges: wx.CallAfter(self.hottgauge.SetTarget, temp)
wx.CallAfter(self.graph.SetExtruder0TargetTemperature, temp)
elif gline.command == "M140":
gline.parse_coordinates(imperial = False, force = True)
if gline.s != None:
temp = gline.s
if self.display_gauges: wx.CallAfter(self.bedtgauge.SetTarget, temp)
wx.CallAfter(self.graph.SetBedTargetTemperature, temp)
else:
return
self.sentlines.put_nowait(line)
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 = ""):
try:
......@@ -294,6 +390,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def setbedgui(self, f):
self.bsetpoint = f
if self.display_gauges: self.bedtgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetBedTargetTemperature, int(f))
if f>0:
wx.CallAfter(self.btemp.SetValue, str(f))
......@@ -313,6 +410,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def sethotendgui(self, f):
self.hsetpoint = f
if self.display_gauges: self.hottgauge.SetTarget(int(f))
wx.CallAfter(self.graph.SetExtruder0TargetTemperature, int(f))
if f > 0:
wx.CallAfter(self.htemp.SetValue, str(f))
......@@ -444,7 +542,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.macros_menu = wx.Menu()
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, 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")))
......@@ -574,7 +672,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
obj = e.GetEventObject()
popupmenu = wx.Menu()
item = popupmenu.Append(-1, _("SD Upload"))
if not self.f or not len(self.f):
if not self.f:
item.Enable(False)
self.Bind(wx.EVT_MENU, self.upload, id = item.GetId())
item = popupmenu.Append(-1, _("SD Print"))
......@@ -592,7 +690,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.btemp.SetInsertionPoint, 0)
def showwin(self, event):
if(self.f is not None):
if self.f:
self.gwindow.Show(True)
self.gwindow.SetToolTip(wx.ToolTip("Mousewheel zooms the display\nShift / Mousewheel scrolls layers"))
self.gwindow.Raise()
......@@ -762,8 +860,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if bedit.color.GetValue().strip()!="":
self.custombuttons[n].background = bedit.color.GetValue()
self.cbutton_save(n, self.custombuttons[n])
bedit.Destroy()
self.cbuttons_reload()
wx.CallAfter(bedit.Destroy)
wx.CallAfter(self.cbuttons_reload)
def cbutton_remove(self, e, button):
n = button.custombutton
......@@ -785,7 +883,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.cbutton_save(n+1, self.custombuttons[n+1])
#if self.custombuttons[-1] is None:
# del self.custombuttons[-1]
self.cbuttons_reload()
wx.CallAfter(self.cbuttons_reload)
def editbutton(self, e):
if e.IsCommandEvent() or e.ButtonUp(wx.MOUSE_BTN_RIGHT):
......@@ -932,28 +1030,35 @@ class PronterWindow(MainWindow, pronsole.pronsole):
e.Skip()
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
self.onecmd('home X')
if corner == 1: # upper-right
elif corner == 1: # upper-right
self.onecmd('home Y')
if corner == 2: # lower-right
elif corner == 2: # lower-right
self.onecmd('home Z')
if corner == 3: # lower-left
elif corner == 3: # lower-left
self.onecmd('home')
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
else:
return
self.p.send_now('M114')
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:
self.onecmd('move X %s' % x)
if y != 0:
elif y != 0:
self.onecmd('move Y %s' % y)
# When user clicks on the XY control, the Z control no longer gets spacebar/repeat signals
self.zb.clearRepeat()
else:
return
self.p.send_now('M114')
def moveZ(self, z):
if z != 0:
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
self.xyb.clearRepeat()
......@@ -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 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)
try:
self.gwindow.Destroy()
except:
pass
self.Destroy()
wx.CallAfter(self.gwindow.Destroy)
wx.CallAfter(self.Destroy)
def do_monitor(self, l = ""):
if l.strip()=="":
......@@ -1010,6 +1112,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def setmonitor(self, e):
self.monitor = self.monitorbox.GetValue()
self.set("monitor", self.monitor)
if self.monitor:
wx.CallAfter(self.graph.StartPlotting, 1000)
else:
......@@ -1019,8 +1122,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
try:
self.logbox.AppendText(text)
except:
print "attempted to write invalid text to console"
pass
print _("Attempted to write invalid text to console, which could be due to an invalid baudrate")
def setloud(self,e):
self.p.loud=e.IsChecked()
......@@ -1032,21 +1134,42 @@ class PronterWindow(MainWindow, pronsole.pronsole):
wx.CallAfter(self.addtexttolog, ">>>" + command + "\n");
self.onecmd(str(command))
self.commandbox.SetSelection(0, len(command))
self.commandbox.history+=[command]
self.commandbox.history.append(command)
self.commandbox.histindex = len(self.commandbox.history)
def clearOutput(self, e):
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):
while self.statuscheck:
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
if self.sdprinting:
fractioncomplete = float(self.percentdone / 100.0)
......@@ -1077,11 +1200,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
break
time.sleep(0.25)
while not self.sentlines.empty():
try:
gc = self.sentlines.get_nowait()
wx.CallAfter(self.gviz.addgcode, gc, 1)
except:
break
gc = self.sentlines.get_nowait()
wx.CallAfter(self.gviz.addgcode, gc, 1)
wx.CallAfter(self.status.SetStatusText, _("Not connected to printer."))
def capture(self, func, *args, **kwargs):
......@@ -1104,24 +1224,23 @@ class PronterWindow(MainWindow, pronsole.pronsole):
return retval
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
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:
traceback.print_exc()
self.update_tempdisplay()
isreport = True
tstring = l.rstrip()
#print tstring
if (tstring!="ok") and (tstring!="wait") and ("ok T:" not in tstring) and (not self.p.loud):
# print "*"+tstring+"*"
# print "[" + time.strftime('%H:%M:%S',time.localtime(time.time())) + "] " + tstring
if self.p.loud or (tstring not in ["ok", "wait"] and not isreport):
wx.CallAfter(self.addtexttolog, tstring + "\n");
for i in self.recvlisteners:
i(l)
for listener in self.recvlisteners:
listener(l)
def listfiles(self, line):
def listfiles(self, line, ignored = False):
if "Begin file list" in line:
self.listing = 1
elif "End file list" in line:
......@@ -1129,7 +1248,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.recvlisteners.remove(self.listfiles)
wx.CallAfter(self.filesloaded)
elif self.listing:
self.sdfiles+=[line.replace("\n", "").replace("\r", "").lower()]
self.sdfiles.append(line.strip().lower())
def waitforsdresponse(self, l):
if "file.open failed" in l:
......@@ -1166,6 +1285,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if len(target):
self.recvlisteners+=[self.waitforsdresponse]
self.p.send_now("M23 "+target.lower())
dlg.Destroy()
#print self.sdfiles
def getfiles(self):
......@@ -1180,7 +1300,6 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def skein_func(self):
try:
import shlex
param = self.expandcommand(self.settings.slicecommand).encode()
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())]
......@@ -1207,13 +1326,13 @@ class PronterWindow(MainWindow, pronsole.pronsole):
fn = self.filename
try:
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 = [i.replace("\n", "").replace("\r", "") for i in of]
of.close()
self.f = [line.strip() for line in open(self.filename)]
self.fgcode = gcoder.GCode(self.f)
if self.p.online:
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.printbtn.SetLabel, _("Print"))
......@@ -1251,8 +1370,10 @@ class PronterWindow(MainWindow, pronsole.pronsole):
basedir = os.path.split(self.filename)[0]
except:
pass
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=None
if filename is None:
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 (*.*)|*.*"))
if(filename is not None or dlg.ShowModal() == wx.ID_OK):
if filename is not None:
name = filename
......@@ -1260,6 +1381,8 @@ class PronterWindow(MainWindow, pronsole.pronsole):
name = dlg.GetPath()
if not(os.path.exists(name)):
self.status.SetStatusText(_("File not found!"))
if dlg is not None:
dlg.Destroy()
return
path = os.path.split(name)[0]
if path != self.settings.last_file_path:
......@@ -1270,10 +1393,10 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.skein(name)
else:
self.filename = name
of = open(self.filename)
self.f = [i.replace("\n", "").replace("\r", "") for i in of]
of.close()
self.f = [line.strip() for line in open(self.filename)]
self.fgcode = gcoder.GCode(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.pausebtn.SetLabel, _("Pause"))
wx.CallAfter(self.pausebtn.Disable)
......@@ -1281,26 +1404,21 @@ class PronterWindow(MainWindow, pronsole.pronsole):
if self.p.online:
wx.CallAfter(self.printbtn.Enable)
threading.Thread(target = self.loadviz).start()
if dlg is not None:
dlg.Destroy()
def loadviz(self):
Xtot, Ytot, Ztot, Xmin, Xmax, Ymin, Ymax, Zmin, Zmax = pronsole.measurements(self.f)
print pronsole.totalelength(self.f), _("mm of filament used in this print\n")
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 from %f mm to %f mm in Y\nand is %f mm wide\n") % (Ymin, Ymax, Ytot)
print _("the print goes from %f mm to %f mm in Z\nand is %f mm high\n") % (Zmin, Zmax, Ztot)
try:
print _("Estimated duration (pessimistic): "), pronsole.estimate_duration(self.f)
except:
pass
#import time
#t0 = time.time()
gcode = self.fgcode
print gcode.filament_length, _("mm of filament used in this print")
print _("The print goes:")
print _("- from %.2f mm to %.2f mm in X and is %.2f mm wide") % (gcode.xmin, gcode.xmax, gcode.width)
print _("- from %.2f mm to %.2f mm in Y and is %.2f mm deep") % (gcode.ymin, gcode.ymax, gcode.depth)
print _("- from %.2f mm to %.2f mm in Z and is %.2f mm high") % (gcode.zmin, gcode.zmax, gcode.height)
print _("Estimated duration: %s") % gcode.estimate_duration()
self.gviz.clear()
self.gwindow.p.clear()
self.gviz.addfile(self.f)
#print "generated 2d view in %f s"%(time.time()-t0)
#t0 = time.time()
self.gwindow.p.addfile(self.f)
#print "generated 3d view in %f s"%(time.time()-t0)
self.gviz.addfile(gcode)
self.gwindow.p.addfile(gcode)
self.gviz.showall = 1
wx.CallAfter(self.gviz.Refresh)
......@@ -1315,14 +1433,14 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.send_now("M24")
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."))
return
if not self.p.online:
wx.CallAfter(self.status.SetStatusText, _("Not connected to printer."))
return
self.on_startprint()
self.p.startprint(self.f)
self.p.startprint(self.fgcode)
def on_startprint(self):
wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
......@@ -1339,14 +1457,14 @@ class PronterWindow(MainWindow, pronsole.pronsole):
def uploadtrigger(self, l):
if "Writing to file" in l:
self.uploading = True
self.p.startprint(self.f)
self.p.startprint(self.fgcode)
self.p.endcb = self.endupload
self.recvlisteners.remove(self.uploadtrigger)
elif "open failed, File" in l:
self.recvlisteners.remove(self.uploadtrigger)
def upload(self, event):
if not self.f or not len(self.f):
if not self.f:
return
if not self.p.online:
return
......@@ -1355,6 +1473,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.send_now("M21")
self.p.send_now("M28 "+str(dlg.GetValue()))
self.recvlisteners+=[self.uploadtrigger]
dlg.Destroy()
def pause(self, event):
print _("Paused.")
......@@ -1383,7 +1502,7 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.on_startprint()
threading.Thread(target = self.getfiles).start()
def connect(self, event):
def connect(self, event = None):
print _("Connecting...")
port = None
try:
......@@ -1491,36 +1610,21 @@ class PronterWindow(MainWindow, pronsole.pronsole):
self.p.paused = 0
wx.CallAfter(self.pausebtn.SetLabel, _("Pause"))
self.paused = 0
dlg.Destroy()
def get_build_dimensions(self, bdim):
import re
# 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.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
class PronterApp(wx.App):
mainwindow = None
def __init__(self, *args, **kwargs):
super(PronterApp, self).__init__(*args, **kwargs)
self.mainwindow = PronterWindow()
self.mainwindow.Show()
if __name__ == '__main__':
app = wx.App(False)
main = PronterWindow()
main.Show()
app = PronterApp(False)
try:
app.MainLoop()
except:
except KeyboardInterrupt:
pass
del app
......@@ -145,7 +145,7 @@ setup (
license = "GPLv3",
data_files = data_files,
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,
"install" : install,
"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