First parts of a webui for Cura
This commit is contained in:
parent
5e1998d6a1
commit
580ebedfa9
17 changed files with 12521 additions and 0 deletions
|
|
@ -52,6 +52,8 @@ def main():
|
|||
help="Internal option, do not use!")
|
||||
parser.add_option("-s", "--slice", action="store_true", dest="slice",
|
||||
help="Slice the given files instead of opening them in Cura")
|
||||
parser.add_option("-w", "--web", action="store_true", dest="webui",
|
||||
help="Start the webui instead of the normal Cura UI")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.profile is not None:
|
||||
|
|
@ -65,6 +67,9 @@ def main():
|
|||
elif options.slice is not None:
|
||||
from Cura.util import sliceRun
|
||||
sliceRun.runSlice(args)
|
||||
elif options.webui:
|
||||
import Cura.webui as webapp
|
||||
webapp.run()
|
||||
else:
|
||||
#Place any unused arguments as last file, so Cura starts with opening those files.
|
||||
if len(args) > 0:
|
||||
|
|
|
|||
91
Cura/webui/__init__.py
Normal file
91
Cura/webui/__init__.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env python
|
||||
# coding=utf-8
|
||||
__author__ = 'Gina Häußge <osd@foosel.net>'
|
||||
|
||||
from flask import Flask, request, render_template, jsonify
|
||||
|
||||
from printer import Printer
|
||||
|
||||
import tempfile
|
||||
|
||||
app = Flask("Cura.webui")
|
||||
printer = Printer()
|
||||
printer.connect()
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
@app.route('/api/printer/connect', methods=['POST'])
|
||||
def connect():
|
||||
printer.connect()
|
||||
return jsonify(state='Connecting')
|
||||
|
||||
@app.route('/api/printer/disconnect', methods=['POST'])
|
||||
def disconnect():
|
||||
printer.disconnect()
|
||||
return jsonify(state='Offline')
|
||||
|
||||
@app.route('/api/printer', methods=['GET'])
|
||||
def printerState():
|
||||
temp = printer.currentTemp
|
||||
bedTemp = printer.currentBedTemp
|
||||
jobData = printer.jobData()
|
||||
|
||||
if jobData != None:
|
||||
return jsonify(state=printer.getStateString(),
|
||||
operational=printer.isOperational(),
|
||||
closedOrError=printer.isClosedOrError(),
|
||||
temp=temp,
|
||||
bedTemp=bedTemp,
|
||||
job=jobData)
|
||||
else:
|
||||
return jsonify(state=printer.getStateString(),
|
||||
temperature=temp,
|
||||
bedTemperature=bedTemp)
|
||||
|
||||
@app.route('/api/printer/messages', methods=['GET'])
|
||||
def printerMessages():
|
||||
return jsonify(messages=printer.messages)
|
||||
|
||||
@app.route('/api/printer/log', methods=['GET'])
|
||||
def printerMessages():
|
||||
return jsonify(log=printer.log)
|
||||
|
||||
@app.route('/api/printer/command', methods=['POST'])
|
||||
def printerCommand():
|
||||
command = request.form['command']
|
||||
printer.command(command)
|
||||
return jsonify(state=printer.getStateString())
|
||||
|
||||
@app.route('/api/printer/temperatures', methods=['GET'])
|
||||
def printerTemperatures():
|
||||
return jsonify(temperatures = printer.temps)
|
||||
|
||||
@app.route('/api/printer/gcode', methods=['POST'])
|
||||
def uploadGcodeFile():
|
||||
file = request.files['gcode_file']
|
||||
if file != None:
|
||||
(handle, filename) = tempfile.mkstemp(suffix='.gcode', prefix='tmp_', text=True)
|
||||
file.save(filename)
|
||||
printer.loadGcode(filename)
|
||||
return jsonify(state=printer.getStateString())
|
||||
|
||||
@app.route('/api/printer/print', methods=['POST'])
|
||||
def printGcode():
|
||||
printer.startPrint()
|
||||
return jsonify(state=printer.getStateString())
|
||||
|
||||
@app.route('/api/printer/pause', methods=['POST'])
|
||||
def pausePrint():
|
||||
printer.togglePausePrint()
|
||||
return jsonify(state=printer.getStateString())
|
||||
|
||||
@app.route('/api/printer/cancel', methods=['POST'])
|
||||
def cancelPrint():
|
||||
printer.cancelPrint()
|
||||
return jsonify(state=printer.getStateString())
|
||||
|
||||
def run():
|
||||
app.debug = True
|
||||
app.run()
|
||||
208
Cura/webui/printer.py
Normal file
208
Cura/webui/printer.py
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
# coding=utf-8
|
||||
__author__ = 'Gina Häußge <osd@foosel.net>'
|
||||
|
||||
import time
|
||||
import os
|
||||
|
||||
import Cura.util.machineCom as machineCom
|
||||
from Cura.util import gcodeInterpreter
|
||||
|
||||
class Printer():
|
||||
def __init__(self):
|
||||
# state
|
||||
self.temps = {
|
||||
'actual': [],
|
||||
'target': [],
|
||||
'actualBed': [],
|
||||
'targetBed': []
|
||||
}
|
||||
self.messages = []
|
||||
self.log = []
|
||||
self.state = None
|
||||
self.currentZ = None
|
||||
self.progress = None
|
||||
self.printTime = None
|
||||
self.printTimeLeft = None
|
||||
self.currentTemp = None
|
||||
self.currentBedTemp = None
|
||||
|
||||
self.gcode = None
|
||||
self.gcodeList = None
|
||||
self.filename = None
|
||||
|
||||
# comm
|
||||
self.comm = None
|
||||
|
||||
def connect(self):
|
||||
if self.comm != None:
|
||||
self.comm.close()
|
||||
self.comm = machineCom.MachineCom(port='VIRTUAL', callbackObject=self)
|
||||
|
||||
def disconnect(self):
|
||||
if self.comm != None:
|
||||
self.comm.close()
|
||||
self.comm = None
|
||||
|
||||
def command(self, command):
|
||||
self.comm.sendCommand(command)
|
||||
|
||||
def mcLog(self, message):
|
||||
self.log.append(message)
|
||||
self.log = self.log[-300:]
|
||||
|
||||
def mcTempUpdate(self, temp, bedTemp, targetTemp, bedTargetTemp):
|
||||
currentTime = time.time()
|
||||
|
||||
self.temps['actual'].append((currentTime, temp))
|
||||
self.temps['actual'] = self.temps['actual'][-300:]
|
||||
|
||||
self.temps['target'].append((currentTime, targetTemp))
|
||||
self.temps['target'] = self.temps['target'][-300:]
|
||||
|
||||
self.temps['actualBed'].append((currentTime, bedTemp))
|
||||
self.temps['actualBed'] = self.temps['actualBed'][-300:]
|
||||
|
||||
self.temps['targetBed'].append((currentTime, bedTargetTemp))
|
||||
self.temps['targetBed'] = self.temps['targetBed'][-300:]
|
||||
|
||||
self.currentTemp = temp
|
||||
self.currentBedTemp = bedTemp
|
||||
|
||||
def mcStateChange(self, state):
|
||||
self.state = state
|
||||
|
||||
def mcMessage(self, message):
|
||||
self.messages.append(message)
|
||||
self.messages = self.message[-300:]
|
||||
|
||||
def mcProgress(self, lineNr):
|
||||
self.printTime = self.comm.getPrintTime()
|
||||
self.printTimeLeft = self.comm.getPrintTimeRemainingEstimate()
|
||||
self.progress = self.comm.getPrintPos()
|
||||
|
||||
def mcZChange(self, newZ):
|
||||
self.currentZ = newZ
|
||||
|
||||
def jobData(self):
|
||||
if self.gcode != None:
|
||||
formattedPrintTime = None
|
||||
if (self.printTime):
|
||||
"%02d:%02d" % (int(self.printTime / 60), int(self.printTime % 60))
|
||||
|
||||
formattedPrintTimeLeft = None
|
||||
if (self.printTimeLeft):
|
||||
"%02d:%02d" % (int(self.printTimeLeft / 60), int(self.printTimeLeft % 60))
|
||||
|
||||
data = {
|
||||
'currentZ': self.currentZ,
|
||||
'line': self.progress,
|
||||
'totalLines': len(self.gcodeList),
|
||||
'printTime': formattedPrintTime,
|
||||
'printTimeLeft': formattedPrintTimeLeft,
|
||||
'filament': "%.2fm %.2fg" % (
|
||||
self.gcode.extrusionAmount / 1000,
|
||||
self.gcode.calculateWeight() * 1000
|
||||
),
|
||||
'estimatedPrintTime': "%02d:%02d" % (
|
||||
int(self.gcode.totalMoveTimeMinute / 60),
|
||||
int(self.gcode.totalMoveTimeMinute % 60)
|
||||
)
|
||||
}
|
||||
else:
|
||||
data = None
|
||||
return data
|
||||
|
||||
def getStateString(self):
|
||||
if self.comm == None:
|
||||
return 'Offline'
|
||||
else:
|
||||
return self.comm.getStateString()
|
||||
|
||||
def isClosedOrError(self):
|
||||
return self.comm == None or self.comm.isClosedOrError()
|
||||
|
||||
def isOperational(self):
|
||||
return self.comm != None and self.comm.isOperational()
|
||||
|
||||
def loadGcode(self, file):
|
||||
if self.comm != None and self.comm.isPrinting():
|
||||
return
|
||||
|
||||
# delete old temporary file
|
||||
if self.filename != None:
|
||||
os.remove(self.filename)
|
||||
|
||||
#Send an initial M110 to reset the line counter to zero.
|
||||
prevLineType = lineType = 'CUSTOM'
|
||||
gcodeList = ["M110"]
|
||||
for line in open(file, 'r'):
|
||||
if line.startswith(';TYPE:'):
|
||||
lineType = line[6:].strip()
|
||||
if ';' in line:
|
||||
line = line[0:line.find(';')]
|
||||
line = line.strip()
|
||||
if len(line) > 0:
|
||||
if prevLineType != lineType:
|
||||
gcodeList.append((line, lineType, ))
|
||||
else:
|
||||
gcodeList.append(line)
|
||||
prevLineType = lineType
|
||||
gcode = gcodeInterpreter.gcode()
|
||||
gcode.loadList(gcodeList)
|
||||
#print "Loaded: %s (%d)" % (filename, len(gcodeList))
|
||||
self.filename = file
|
||||
self.gcode = gcode
|
||||
self.gcodeList = gcodeList
|
||||
|
||||
def startPrint(self):
|
||||
if self.comm == None or not self.comm.isOperational():
|
||||
return
|
||||
if self.gcodeList == None:
|
||||
return
|
||||
if self.comm.isPrinting():
|
||||
return
|
||||
self.currentZ = -1
|
||||
self.comm.printGCode(self.gcodeList)
|
||||
|
||||
def togglePausePrint(self):
|
||||
if self.comm == None:
|
||||
return
|
||||
self.comm.setPause(not self.comm.isPaused())
|
||||
|
||||
def cancelPrint(self):
|
||||
if self.comm == None:
|
||||
return
|
||||
self.comm.cancelPrint()
|
||||
self.comm.sendCommand("M84")
|
||||
|
||||
class Temperature():
|
||||
def __init__(self, actual=None, target=None):
|
||||
self._actual = actual
|
||||
self._target = target
|
||||
|
||||
def actual(self):
|
||||
return self._actual
|
||||
|
||||
def target(self):
|
||||
return self._target
|
||||
|
||||
def asDict(self):
|
||||
return {'actual': self._actual, 'target': self._target}
|
||||
|
||||
class Position():
|
||||
def __init__(self, x=None, y=None, z=None):
|
||||
self._x = x
|
||||
self._y = y
|
||||
self._z = z
|
||||
|
||||
def update(self, x=None, y=None, z=None):
|
||||
if x != None:
|
||||
self._x = x
|
||||
if y != None:
|
||||
self._y = y
|
||||
if z != None:
|
||||
self._z = z
|
||||
|
||||
def get(self):
|
||||
return {'x': self._x, 'y': self._y, 'z': self._z}
|
||||
|
||||
1092
Cura/webui/static/css/bootstrap-responsive.css
vendored
Normal file
1092
Cura/webui/static/css/bootstrap-responsive.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
9
Cura/webui/static/css/bootstrap-responsive.min.css
vendored
Normal file
9
Cura/webui/static/css/bootstrap-responsive.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6039
Cura/webui/static/css/bootstrap.css
vendored
Normal file
6039
Cura/webui/static/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load diff
9
Cura/webui/static/css/bootstrap.min.css
vendored
Normal file
9
Cura/webui/static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
Cura/webui/static/img/glyphicons-halflings-white.png
Normal file
BIN
Cura/webui/static/img/glyphicons-halflings-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
BIN
Cura/webui/static/img/glyphicons-halflings.png
Normal file
BIN
Cura/webui/static/img/glyphicons-halflings.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
2159
Cura/webui/static/js/bootstrap.js
vendored
Normal file
2159
Cura/webui/static/js/bootstrap.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
6
Cura/webui/static/js/bootstrap.min.js
vendored
Normal file
6
Cura/webui/static/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2599
Cura/webui/static/js/jquery.flot.js
Normal file
2599
Cura/webui/static/js/jquery.flot.js
Normal file
File diff suppressed because it is too large
Load diff
88
Cura/webui/static/js/printerstate.js
Normal file
88
Cura/webui/static/js/printerstate.js
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
var printerstate = function() {
|
||||
|
||||
var updateInterval = 500;
|
||||
function update() {
|
||||
function onDataReceived(response) {
|
||||
$("#printer_state").text(response.state);
|
||||
$("#printer_temp").text(response.temp + " °C");
|
||||
$("#printer_bedTemp").text(response.bedTemp + " °C");
|
||||
|
||||
if (response.job) {
|
||||
var currentLine = (response.job.line ? response.job.line : 0);
|
||||
var progress = currentLine * 100 / response.job.totalLines;
|
||||
$("#job_filament").text(response.job.filament);
|
||||
$("#job_estimatedPrintTime").text(response.job.estimatedPrintTime);
|
||||
$("#job_line").text((currentLine == 0 ? "-" : currentLine) + "/" + response.job.totalLines + " " + progress + "%");
|
||||
$("#job_height").text(response.job.currentZ);
|
||||
$("#job_printTime").text(response.job.printTime);
|
||||
$("#job_printTimeLeft").text(response.job.printTimeLeft);
|
||||
$("#job_progressBar").width(progress + "%");
|
||||
} else {
|
||||
$("#job_filament").text("-");
|
||||
$("#job_estimatedPrintTime").text("-");
|
||||
$("#job_line").text("-");
|
||||
$("#job_height").text("-");
|
||||
$("#job_printTime").text("-");
|
||||
$("#job_printTimeLefT").text("-");
|
||||
$("#job_progressBar").width("0%");
|
||||
}
|
||||
|
||||
if (!response.closedOrError) {
|
||||
$("#printer_connect").click(function(){});
|
||||
$("#printer_connect").addClass("disabled");
|
||||
} else {
|
||||
$("#printer_connect").click(connect);
|
||||
$("#printer_connect").removeClass("disabled");
|
||||
}
|
||||
|
||||
$("#job_print").click(function() {
|
||||
$.ajax({
|
||||
url: "/api/printer/print",
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(){}
|
||||
})
|
||||
})
|
||||
$("#job_pause").click(function() {
|
||||
$.ajax({
|
||||
url: "/api/printer/pause",
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(){}
|
||||
})
|
||||
})
|
||||
$("#job_cancel").click(function() {
|
||||
$.ajax({
|
||||
url: "/api/printer/cancel",
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(){}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/api/printer",
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: onDataReceived
|
||||
});
|
||||
|
||||
setTimeout(update, updateInterval);
|
||||
}
|
||||
|
||||
function connect() {
|
||||
$.ajax({
|
||||
url: "/api/printer/connect",
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
printerstate();
|
||||
45
Cura/webui/static/js/temperaturegraph.js
Normal file
45
Cura/webui/static/js/temperaturegraph.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
|
||||
var temperaturegraph = function() {
|
||||
|
||||
var options = {
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: 310,
|
||||
ticks: 10
|
||||
},
|
||||
xaxis: {
|
||||
mode: "time"
|
||||
},
|
||||
legend: {
|
||||
noColumns: 4
|
||||
}
|
||||
};
|
||||
|
||||
var updateInterval = 500;
|
||||
function update() {
|
||||
function onDataReceived(response) {
|
||||
var temps = response.temperatures;
|
||||
var data = [
|
||||
{label: "Actual", color: "#FF4040", data: temps.actual},
|
||||
{label: "Target", color: "#FFA0A0", data: temps.target},
|
||||
{label: "Bed Actual", color: "#4040FF", data: temps.actualBed},
|
||||
{label: "Bed Target", color: "#A0A0FF", data: temps.targetBed}
|
||||
]
|
||||
$.plot($("#temperature-graph"), data, options);
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/api/printer/temperatures",
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: onDataReceived
|
||||
})
|
||||
|
||||
setTimeout(update, updateInterval);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
}
|
||||
|
||||
temperaturegraph();
|
||||
49
Cura/webui/static/js/terminaloutput.js
Normal file
49
Cura/webui/static/js/terminaloutput.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
var terminaloutput = function() {
|
||||
|
||||
var updateInterval = 1000;
|
||||
function update() {
|
||||
function onDataReceived(response) {
|
||||
var log = response.log;
|
||||
var output = '';
|
||||
for (var i = 0; i < log.length; i++) {
|
||||
output += log[i] + '<br>';
|
||||
}
|
||||
|
||||
var container = $("#terminal-output");
|
||||
var autoscroll = (container.scrollTop() == container[0].scrollHeight - container.height);
|
||||
|
||||
container.html(output);
|
||||
|
||||
if (autoscroll) {
|
||||
container.scrollTop(container[0].scrollHeight - container.height())
|
||||
}
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/api/printer/log",
|
||||
type: 'GET',
|
||||
dataType: 'json',
|
||||
success: onDataReceived
|
||||
})
|
||||
|
||||
setTimeout(update, updateInterval);
|
||||
}
|
||||
|
||||
update();
|
||||
|
||||
$("#terminal-send").click(function () {
|
||||
var command = $("#terminal-command").val();
|
||||
$.ajax({
|
||||
url: "/api/printer/command",
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: 'command=' + command,
|
||||
success: function(response) {
|
||||
// do nothing
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
terminaloutput();
|
||||
121
Cura/webui/templates/index.html
Normal file
121
Cura/webui/templates/index.html
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bootstrap 101 Template</title>
|
||||
<link href="{{ url_for('static', filename='css/bootstrap.min.css') }}" rel="stylesheet" media="screen">
|
||||
<script src="http://code.jquery.com/jquery-latest.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.flot.js') }}"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<h1>Cura WebUI</h1>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<h2>Printer</h2>
|
||||
Machine State: <strong id="printer_state"></strong><br>
|
||||
<button class="btn" id="printer_connect">Connect</button>
|
||||
|
||||
<h2>Job</h2>
|
||||
Filament: <strong id="job_filament"></strong><br>
|
||||
Estimated Print Time: <strong id="job_estimatedPrintTime"></strong><br>
|
||||
Line: <strong id="job_line"></strong><br>
|
||||
Height: <strong id="job_currentZ"></strong><br>
|
||||
Print Time: <strong id="job_printTime"></strong><br>
|
||||
Print Time Left: <strong id="job_printTimeLeft"></strong><br>
|
||||
|
||||
<div class="progress">
|
||||
<div class="bar" id="job_progressBar" style="width: 30%;"></div>
|
||||
</div>
|
||||
|
||||
<button class="btn" id="job_print">Print</button>
|
||||
<button class="btn" id="job_pause">Pause</button>
|
||||
<button class="btn" id="job_cancel">Cancel</button>
|
||||
|
||||
<form action="/api/printer/gcode" method="post" enctype="multipart/form-data">
|
||||
<input type="file" name="gcode_file">
|
||||
<button class="btn" type="submit">Load</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="span9 tabbable tabs-left">
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a href="#temp" data-toggle="tab">Temp</a></li>
|
||||
<li><a href="#jog" data-toggle="tab">Jog</a></li>
|
||||
<li><a href="#speed" data-toggle="tab">Speed</a></li>
|
||||
<li><a href="#term" data-toggle="tab">Term</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="temp">
|
||||
<div id="temperature-graph" class="span6 " style="height: 350px"></div>
|
||||
<div class="span3">
|
||||
Temp: <strong id="printer_temp"></strong><br>
|
||||
Bed Temp: <strong id="printer_bedTemp"></strong><br>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="jog">
|
||||
<div class="span9">
|
||||
<div class="row">
|
||||
<div class="span1 offset1 btn-group btn-group-vertical">
|
||||
<button class="btn btn-block btn-large" type="button"><i class="icon-chevron-up"></i></button>
|
||||
<button class="btn btn-block" type="button"><i class="icon-chevron-up"></i></button>
|
||||
<button class="btn btn-block btn-small" type="button"><i class="icon-chevron-up"></i></button>
|
||||
</div>
|
||||
<div class="span1 offset1 btn-group btn-group-vertical">
|
||||
<button class="btn btn-block btn-large" type="button"><i class="icon-chevron-up"></i></button>
|
||||
<button class="btn btn-block" type="button"><i class="icon-chevron-up"></i></button>
|
||||
<button class="btn btn-block btn-small" type="button"><i class="icon-chevron-up"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span1 btn-group btn-group">
|
||||
<button class="btn" type="button"><i class="icon-chevron-left"></i></button>
|
||||
<button class="btn" type="button"><i class="icon-chevron-left"></i></button>
|
||||
<button class="btn" type="button"><i class="icon-chevron-left"></i></button>
|
||||
</div>
|
||||
<div class="span1">
|
||||
<button class="btn btn-block" type="button"><i class="icon-home"></i></button>
|
||||
</div>
|
||||
<div class="span1 btn-group btn-group">
|
||||
<button class="btn" type="button"><i class="icon-chevron-right"></i></button>
|
||||
<button class="btn" type="button"><i class="icon-chevron-right"></i></button>
|
||||
<button class="btn" type="button"><i class="icon-chevron-right"></i></button>
|
||||
</div>
|
||||
<div class="span1">
|
||||
<button class="btn btn-block" type="button"><i class="icon-home"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="span1 offset1 btn-group btn-group-vertical">
|
||||
<button class="btn btn-block btn-small" type="button"><i class="icon-chevron-down"></i></button>
|
||||
<button class="btn btn-block" type="button"><i class="icon-chevron-down"></i></button>
|
||||
<button class="btn btn-block btn-large" type="button"><i class="icon-chevron-down"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="speed">
|
||||
Here be speed settings
|
||||
</div>
|
||||
<div class="tab-pane" id="term">
|
||||
<pre id="terminal-output" class="pre-scrollable"></pre>
|
||||
<div class="input-append">
|
||||
<input type="text" class="span9" id="terminal-command">
|
||||
<button class="btn" type="button" id="terminal-send">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Here be scripts -->
|
||||
<script src="{{ url_for('static', filename='js/printerstate.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/temperaturegraph.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/terminaloutput.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
flask>=0.9
|
||||
PyOpenGL>=3.0.2
|
||||
numpy>=1.6.2
|
||||
pyserial>=2.6
|
||||
|
|
|
|||
Loading…
Reference in a new issue