@@ -109,7 +109,7 @@
|
||||
"Admin": "Admin",
|
||||
"User": "Benutzer",
|
||||
"WARNING: Are you sure you want to permanently delete this user AND ALL their uploaded files and G-codes?": "WARNUNG: Sind Sie sicher, dass Sie diesen Benutzer UND ALLE seine Dateien löschen wollen?",
|
||||
"CuraEngine Configurations": "CuraEngine-Konfigurationen",
|
||||
"SliceEngine Configurations": "Slicing-Engine-Konfigurationen",
|
||||
"Plater Origin Offset X (mm)": "Druckbett Ursprung Offset X (mm)",
|
||||
"Adjust the X-axis compilation offset for combined files on the build plate.": "X-Achsen-Offset für kombinierte Dateien anpassen.",
|
||||
"Plater Origin Offset Y (mm)": "Druckbett Ursprung Offset Y (mm)",
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
"Admin": "Admin",
|
||||
"User": "User",
|
||||
"WARNING: Are you sure you want to permanently delete this user AND ALL their uploaded files and G-codes?": "WARNING: Are you sure you want to permanently delete this user AND ALL their uploaded files and G-codes?",
|
||||
"CuraEngine Configurations": "CuraEngine Configurations",
|
||||
"SliceEngine Configurations": "SliceEngine Configurations",
|
||||
"Plater Origin Offset X (mm)": "Plater Origin Offset X (mm)",
|
||||
"Adjust the X-axis compilation offset for combined files on the build plate.": "Adjust the X-axis compilation offset for combined files on the build plate.",
|
||||
"Plater Origin Offset Y (mm)": "Plater Origin Offset Y (mm)",
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
"Admin": "管理员",
|
||||
"User": "普通用户",
|
||||
"WARNING: Are you sure you want to permanently delete this user AND ALL their uploaded files and G-codes?": "警告:确定要永久删除该用户以及TA上传的所有文件和切片吗?",
|
||||
"CuraEngine Configurations": "CuraEngine 配置",
|
||||
"SliceEngine Configurations": "切片引擎配置",
|
||||
"Plater Origin Offset X (mm)": "构建板原点偏移 X (mm)",
|
||||
"Adjust the X-axis compilation offset for combined files on the build plate.": "调整多文件在构建板合并切片时的X坐标偏移。",
|
||||
"Plater Origin Offset Y (mm)": "构建板原点偏移 Y (mm)",
|
||||
|
||||
@@ -13,6 +13,7 @@ from app.utils.tasks import merge_and_slice_task, slice_stl_task, simplify_stl_t
|
||||
from app import i18n_dict
|
||||
# import trimesh.repair
|
||||
from app.utils.stl_simplifier import simplify_stl
|
||||
from app.utils.slice_engines import get_all_engines
|
||||
|
||||
|
||||
main_bp = Blueprint('main', __name__)
|
||||
@@ -46,6 +47,8 @@ def settings():
|
||||
default_support_pattern = request.form.get('default_support_pattern', 'tree')
|
||||
default_quality = request.form.get('default_quality', 'base_global_standard.inst.cfg')
|
||||
gcode_upload_folder = request.form.get('gcode_upload_folder', '').strip()
|
||||
slicer_engine = request.form.get('slicer_engine', 'cura')
|
||||
build_plate_model_path = request.form.get('build_plate_model_path', '').strip()
|
||||
|
||||
# update or create config entries
|
||||
config_items = [
|
||||
@@ -57,6 +60,8 @@ def settings():
|
||||
('default_support_pattern', default_support_pattern),
|
||||
('default_quality', default_quality),
|
||||
('gcode_upload_folder', gcode_upload_folder),
|
||||
('slicer_engine', slicer_engine),
|
||||
('build_plate_model_path', build_plate_model_path),
|
||||
('default_guest_stl_quota_mb', request.form.get('default_guest_stl_quota_mb', '0')),
|
||||
('default_guest_gcode_quota_mb', request.form.get('default_guest_gcode_quota_mb', '0')),
|
||||
('default_user_stl_quota_mb', request.form.get('default_user_stl_quota_mb', '0')),
|
||||
@@ -75,8 +80,8 @@ def settings():
|
||||
return redirect(url_for('admin.settings'))
|
||||
|
||||
configs = {c.key: c.value for c in SystemConfig.query.all()}
|
||||
presets = get_quality_presets()
|
||||
return render_template('admin/settings.html', configs=configs, presets=presets)
|
||||
engines = get_all_engines()
|
||||
return render_template('admin/settings.html', configs=configs, engines=engines)
|
||||
|
||||
@admin_bp.route('/users')
|
||||
def users():
|
||||
@@ -192,18 +197,3 @@ def get_bed_dimensions():
|
||||
except:
|
||||
return 200, 200, 200
|
||||
|
||||
def get_quality_presets():
|
||||
try:
|
||||
|
||||
path = os.path.join(current_app.root_path, '..', 'print_config', 'quality', 'creality', 'presets')
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
presets = []
|
||||
for f in files:
|
||||
# name = f.replace('.inst.cfg', '').replace('base_', '').replace('_', ' ')
|
||||
name = f.replace('.inst.cfg', '')
|
||||
presets.append((f, name))
|
||||
presets.sort(key=lambda x: x[1])
|
||||
return presets
|
||||
except:
|
||||
return []
|
||||
|
||||
|
||||
@@ -338,21 +338,6 @@ def get_bed_dimensions():
|
||||
except:
|
||||
return 200, 200, 200
|
||||
|
||||
def get_quality_presets():
|
||||
try:
|
||||
|
||||
path = os.path.join(current_app.root_path, '..', 'print_config', 'quality', 'creality', 'presets')
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
presets = []
|
||||
for f in files:
|
||||
# name = f.replace('.inst.cfg', '').replace('base_', '').replace('_', ' ')
|
||||
name = f.replace('.inst.cfg', '')
|
||||
presets.append((f, name))
|
||||
presets.sort(key=lambda x: x[1])
|
||||
return presets
|
||||
except:
|
||||
return []
|
||||
|
||||
@main_bp.route('/plater')
|
||||
@login_required
|
||||
def plater():
|
||||
@@ -360,7 +345,7 @@ def plater():
|
||||
quota_exceeded = (quota_mb > 0 and current_size >= quota_mb * 1024 * 1024)
|
||||
|
||||
w, h, hd = get_bed_dimensions()
|
||||
presets = get_quality_presets()
|
||||
|
||||
|
||||
configs = {c.key: c.value for c in SystemConfig.query.all()}
|
||||
offset_x = float(configs.get('offset_x', '0.0'))
|
||||
@@ -373,7 +358,7 @@ def plater():
|
||||
|
||||
user_files = PrintFile.query.filter_by(user_id=current_user.id, file_type='stl').order_by(PrintFile.created_at.desc()).all()
|
||||
models = [{'id': f.id, 'name': f.original_filename, 'status': f.status, 'url': url_for('main.serve_proxy_file', file_id=f.id), 'transform_matrix': f.transform_matrix} for f in user_files]
|
||||
return render_template('slice/plater.html', w=w, h=h, hd=hd, presets=presets, last_quality=default_quality, models=models, offset_x=offset_x, offset_y=offset_y, default_infill=default_infill, default_support=default_support, default_support_pattern=default_support_pattern, quota_exceeded=quota_exceeded)
|
||||
return render_template('slice/plater.html', w=w, h=h, hd=hd, last_quality=default_quality, models=models, offset_x=offset_x, offset_y=offset_y, default_infill=default_infill, default_support=default_support, default_support_pattern=default_support_pattern, quota_exceeded=quota_exceeded, configs=configs)
|
||||
|
||||
@main_bp.route('/file/<int:file_id>')
|
||||
@login_required
|
||||
@@ -553,3 +538,20 @@ def merge_and_slice():
|
||||
|
||||
return jsonify({'success': True, 'message': 'Plater slice queued!'})
|
||||
|
||||
|
||||
@main_bp.route('/api/build_plate_model')
|
||||
@login_required
|
||||
def build_plate_model():
|
||||
conf = SystemConfig.query.filter_by(key='build_plate_model_path').first()
|
||||
if conf and conf.value and os.path.exists(conf.value):
|
||||
return send_file(conf.value)
|
||||
abort(404)
|
||||
|
||||
@main_bp.route('/api/engine_options/<engine_name>')
|
||||
@login_required
|
||||
def engine_options(engine_name):
|
||||
from app.utils.slice_engines import get_slicer_engine
|
||||
engine = get_slicer_engine(engine_name)
|
||||
presets = engine.get_quality_presets(current_app)
|
||||
patterns = engine.get_support_patterns()
|
||||
return jsonify({'presets': presets, 'support_patterns': patterns})
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<h5>{{ _('CuraEngine Configurations') }}</h5>
|
||||
<hr>
|
||||
<h5 class="card-title text-primary border-bottom pb-2 mt-4 mb-3">{{ _('SliceEngine Configurations') }}</h5>
|
||||
<form id="settingsForm" onsubmit="submitSettings(event)">
|
||||
<div class="mb-3">
|
||||
<label for="offset_x" class="form-label">{{ _('Plater Origin Offset X (mm)') }}</label>
|
||||
@@ -34,8 +33,13 @@
|
||||
<div class="form-text">{{ _('Absolute path to save locally sliced GCode files (e.g. OctoPrint uploads folder like "/home/pi/.octoprint/uploads"). Leave empty to use system default.') }}</div>
|
||||
</div>
|
||||
|
||||
<h5 class="mt-4">{{ _('Default Plater Settings') }}</h5>
|
||||
<hr>
|
||||
<h5 class="card-title text-primary border-bottom pb-2 mt-4 mb-3"><i class="bi bi-grid-3x3 me-2"></i>{{ _('Default Plater Settings') }}</h5>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="build_plate_model_path" class="form-label">{{ _('Build Plate Model Path (.stl)') }}</label>
|
||||
<input type="text" class="form-control" name="build_plate_model_path" id="build_plate_model_path" value="{{ configs.get('build_plate_model_path', '') }}">
|
||||
<div class="form-text">{{ _('Absolute path to a custom build plate STL model to show in the plater. Leave empty to use none.') }}</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="default_infill" class="form-label">{{ _('Default Infill Density (%)') }}</label>
|
||||
@@ -53,29 +57,32 @@
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="default_support_pattern" class="form-label">{{ _('Default Support Type') }}</label>
|
||||
<select class="form-select" name="default_support_pattern" id="default_support_pattern">
|
||||
<option value="tree" {% if configs.get('default_support_pattern', 'tree') == 'tree' %}selected{% endif %}>{{ _('Tree') }}</option>
|
||||
<option value="lines" {% if configs.get('default_support_pattern', 'tree') == 'lines' %}selected{% endif %}>{{ _('Lines') }}</option>
|
||||
<option value="grid" {% if configs.get('default_support_pattern', 'tree') == 'grid' %}selected{% endif %}>{{ _('Grid') }}</option>
|
||||
<option value="triangles" {% if configs.get('default_support_pattern', 'tree') == 'triangles' %}selected{% endif %}>{{ _('Triangles') }}</option>
|
||||
<option value="concentric" {% if configs.get('default_support_pattern', 'tree') == 'concentric' %}selected{% endif %}>{{ _('Concentric') }}</option>
|
||||
<option value="zigzag" {% if configs.get('default_support_pattern', 'tree') == 'zigzag' %}selected{% endif %}>{{ _('Zig Zag') }}</option>
|
||||
<option value="cross" {% if configs.get('default_support_pattern', 'tree') == 'cross' %}selected{% endif %}>{{ _('Cross') }}</option>
|
||||
<option value="gyroid" {% if configs.get('default_support_pattern', 'tree') == 'gyroid' %}selected{% endif %}>{{ _('Gyroid') }}</option>
|
||||
<option value="honeycomb" {% if configs.get('default_support_pattern', 'tree') == 'honeycomb' %}selected{% endif %}>{{ _('Honeycomb') }}</option>
|
||||
<option value="octagon" {% if configs.get('default_support_pattern', 'tree') == 'octagon' %}selected{% endif %}>{{ _('Octagon') }}</option>
|
||||
<select class="form-select" name="default_support_pattern" id="default_support_pattern" data-selected="{{ configs.get('default_support_pattern', 'tree') }}">
|
||||
<!-- Loaded via JS -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="default_quality" class="form-label">{{ _('Default Quality Profile') }}</label>
|
||||
<select class="form-select" name="default_quality" id="default_quality">
|
||||
{% for key, name in presets %}
|
||||
<option value="{{ key }}" {% if configs.get('default_quality', 'base_global_standard.inst.cfg') == key %}selected{% endif %}>{{ _(name) }}</option>
|
||||
{% endfor %}
|
||||
<select class="form-select" name="default_quality" id="default_quality" data-selected="{{ configs.get('default_quality', 'base_global_standard.inst.cfg') }}">
|
||||
<!-- Loaded via JS -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title text-primary border-bottom pb-2 mt-4 mb-3"><i class="bi bi-cpu me-2"></i>{{ _('Slicing Engine Configurations') }}</h5>
|
||||
<div class="mb-3">
|
||||
<label for="slicer_engine" class="form-label">{{ _('Slicing Engine') }}</label>
|
||||
<select class="form-select" name="slicer_engine" id="slicer_engine">
|
||||
{% for engine in engines %}
|
||||
<option value="{{ engine.name }}" {% if configs.get('slicer_engine', 'cura') == engine.name %}selected{% endif %} {% if not engine.is_available %}disabled{% endif %}>
|
||||
{{ engine.display_name }} {% if not engine.is_available %}({{ _('Not Available') }}){% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class="form-text mt-2"><i class="bi bi-info-circle me-1"></i>{{ _('Select the engine to be used globally. Ensure the selected engine is installed and accessible on the server.') }}</div>
|
||||
</div>
|
||||
|
||||
|
||||
<h5 class="card-title text-primary border-bottom pb-2 mt-4 mb-3"><i class="bi bi-hdd-network me-2"></i>{{ _('Default Storage Quotas (MB)') }}</h5>
|
||||
|
||||
<div class="row">
|
||||
@@ -140,5 +147,42 @@ function submitSettings(event) {
|
||||
btn.innerHTML = originalText;
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const engineSelect = document.getElementById('slicer_engine');
|
||||
const qualitySelect = document.getElementById('default_quality');
|
||||
const patternSelect = document.getElementById('default_support_pattern');
|
||||
|
||||
function updateOptions(engine) {
|
||||
fetch(`/api/engine_options/${engine}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const selQ = qualitySelect.getAttribute('data-selected');
|
||||
qualitySelect.innerHTML = '';
|
||||
data.presets.forEach(p => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.id; opt.textContent = p.name;
|
||||
qualitySelect.appendChild(opt);
|
||||
});
|
||||
if(selQ) qualitySelect.value = selQ;
|
||||
|
||||
const selP = patternSelect.getAttribute('data-selected');
|
||||
patternSelect.innerHTML = '';
|
||||
data.support_patterns.forEach(p => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.id; opt.textContent = p.name;
|
||||
patternSelect.appendChild(opt);
|
||||
});
|
||||
if(selP) patternSelect.value = selP;
|
||||
});
|
||||
}
|
||||
|
||||
engineSelect.addEventListener('change', function() {
|
||||
updateOptions(this.value);
|
||||
});
|
||||
|
||||
// Initial load
|
||||
updateOptions(engineSelect.value);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -95,18 +95,7 @@
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label for="support-pattern" class="form-label text-secondary small mb-1">{{ _('Support Type') }}</label>
|
||||
<select class="form-select form-select-sm" id="support-pattern" {% if default_support == 'false' %}disabled{% endif %}>
|
||||
<option value="tree" {% if default_support_pattern == 'tree' %}selected{% endif %}>{{ _('Tree') }}</option>
|
||||
<option value="lines" {% if default_support_pattern == 'lines' %}selected{% endif %}>{{ _('Lines') }}</option>
|
||||
<option value="grid" {% if default_support_pattern == 'grid' %}selected{% endif %}>{{ _('Grid') }}</option>
|
||||
<option value="triangles" {% if default_support_pattern == 'triangles' %}selected{% endif %}>{{ _('Triangles') }}</option>
|
||||
<option value="concentric" {% if default_support_pattern == 'concentric' %}selected{% endif %}>{{ _('Concentric') }}</option>
|
||||
<option value="zigzag" {% if default_support_pattern == 'zigzag' %}selected{% endif %}>{{ _('Zig Zag') }}</option>
|
||||
<option value="cross" {% if default_support_pattern == 'cross' %}selected{% endif %}>{{ _('Cross') }}</option>
|
||||
<option value="gyroid" {% if default_support_pattern == 'gyroid' %}selected{% endif %}>{{ _('Gyroid') }}</option>
|
||||
<option value="honeycomb" {% if default_support_pattern == 'honeycomb' %}selected{% endif %}>{{ _('Honeycomb') }}</option>
|
||||
<option value="octagon" {% if default_support_pattern == 'octagon' %}selected{% endif %}>{{ _('Octagon') }}</option>
|
||||
</select>
|
||||
<select class="form-select form-select-sm" id="support-pattern" data-selected="{{ default_support_pattern }}" {% if default_support == 'false' %}disabled{% endif %}></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,11 +109,7 @@
|
||||
<div id="collapseQuality" class="collapse" data-bs-parent="#platerSidebarAccordion">
|
||||
<div class="card-body">
|
||||
<div class="mb-3">
|
||||
<select class="form-select bg-light" id="quality">
|
||||
{% for key, name in presets %}
|
||||
<option value="{{ key }}" {% if key == last_quality %}selected{% endif %}>{{ _(name) }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<select class="form-select bg-light" id="quality" data-selected="{{ last_quality }}"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -226,6 +211,18 @@ function initPlater() {
|
||||
axesHelper.position.set(-bedWidth / 2, -bedDepth / 2, 0.2);
|
||||
scene.add(axesHelper);
|
||||
|
||||
{% if configs.get('build_plate_model_path') %}
|
||||
const bpLoader = new THREE.STLLoader();
|
||||
bpLoader.load("{{ url_for('main.build_plate_model') }}", function (geometry) {
|
||||
const material = new THREE.MeshPhongMaterial({ color: 0x999999, specular: 0x111111, shininess: 200 });
|
||||
const mesh = new THREE.Mesh(geometry, material);
|
||||
<!-- mesh.rotation.set(-Math.PI / 2, 0, 0); -->
|
||||
mesh.rotation.set(0, 0, 0);
|
||||
mesh.position.set(0, 0, -0.1); // Slightly below the grid
|
||||
scene.add(mesh);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
// Show Bed Box outline
|
||||
const boxGeo = new THREE.BoxGeometry(bedWidth, bedDepth, bedHeight);
|
||||
const edges = new THREE.EdgesGeometry(boxGeo);
|
||||
@@ -671,8 +668,17 @@ function addModelToPlate(btnElement, fileId, url, name, status) {
|
||||
supportSelect.value = data.settings.support;
|
||||
supportSelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
if (data.settings.support_pattern) document.getElementById('support-pattern').value = data.settings.support_pattern;
|
||||
if (data.settings.quality) document.getElementById('quality').value = data.settings.quality;
|
||||
|
||||
if (data.settings.support_pattern) {
|
||||
const sSelect = document.getElementById('support-pattern');
|
||||
sSelect.setAttribute('data-selected', data.settings.support_pattern);
|
||||
sSelect.value = data.settings.support_pattern;
|
||||
}
|
||||
if (data.settings.quality) {
|
||||
const qSelect = document.getElementById('quality');
|
||||
qSelect.setAttribute('data-selected', data.settings.quality);
|
||||
qSelect.value = data.settings.quality;
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
@@ -948,4 +954,33 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const engine = "{{ configs.get('slicer_engine', 'cura') }}";
|
||||
const qualitySelect = document.getElementById('quality');
|
||||
const patternSelect = document.getElementById('support-pattern');
|
||||
|
||||
fetch(`/api/engine_options/${engine}`)
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
const selQ = qualitySelect.getAttribute('data-selected');
|
||||
qualitySelect.innerHTML = '';
|
||||
data.presets.forEach(p => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.id; opt.textContent = p.name;
|
||||
qualitySelect.appendChild(opt);
|
||||
});
|
||||
if(selQ) qualitySelect.value = selQ;
|
||||
|
||||
const selP = patternSelect.getAttribute('data-selected');
|
||||
patternSelect.innerHTML = '';
|
||||
data.support_patterns.forEach(p => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = p.id; opt.textContent = p.name;
|
||||
patternSelect.appendChild(opt);
|
||||
});
|
||||
if(selP) patternSelect.value = selP;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
from .cura_engine import CuraEngine
|
||||
from .prusa_slicer_engine import PrusaSlicerEngine
|
||||
|
||||
def get_all_engines():
|
||||
"""Returns a list of instantiated engines."""
|
||||
return [
|
||||
CuraEngine(),
|
||||
PrusaSlicerEngine()
|
||||
]
|
||||
|
||||
def get_slicer_engine(engine_name="cura"):
|
||||
"""
|
||||
Factory function to retrieve the requested slicing engine instance.
|
||||
@@ -14,4 +21,4 @@ def get_slicer_engine(engine_name="cura"):
|
||||
return PrusaSlicerEngine()
|
||||
else:
|
||||
# Default fallback
|
||||
return CuraEngine()
|
||||
return CuraEngine()
|
||||
|
||||
@@ -8,6 +8,26 @@ from app.utils.conf_parse import ConfParse
|
||||
class CuraEngine:
|
||||
def __init__(self):
|
||||
self.name = "cura"
|
||||
self.display_name = "UltiMaker Cura"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
# check if CuraEngine is available in PATH
|
||||
result = subprocess.run(["CuraEngine", "help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return result.returncode == 0 or b"Usage:" in result.stdout or b"Usage:" in result.stderr
|
||||
except (FileNotFoundError, OSError):
|
||||
return False
|
||||
self.display_name = "UltiMaker Cura"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
# check if CuraEngine is available in PATH
|
||||
result = subprocess.run(["CuraEngine", "help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return result.returncode == 0 or b"Usage:" in result.stdout or b"Usage:" in result.stderr
|
||||
except (FileNotFoundError, OSError):
|
||||
return False
|
||||
|
||||
def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
|
||||
"""
|
||||
@@ -165,4 +185,31 @@ class CuraEngine:
|
||||
try:
|
||||
os.remove(tmp_def_path)
|
||||
except Exception as e:
|
||||
app.logger.error(f"Failed to delete temp JSON config {tmp_def_path}: {e}")
|
||||
app.logger.error(f"Failed to delete temp JSON config {tmp_def_path}: {e}")
|
||||
|
||||
def get_quality_presets(self, app):
|
||||
try:
|
||||
path = os.path.join(app.root_path, '..', 'print_config', 'cura_engine', 'quality', 'creality', 'presets')
|
||||
if not os.path.exists(path): return []
|
||||
files = [f for f in os.listdir(path) if f.endswith('.inst.cfg')]
|
||||
presets = []
|
||||
for f in files:
|
||||
presets.append({'id': f, 'name': f.replace('.inst.cfg', '')})
|
||||
presets.sort(key=lambda x: x['name'])
|
||||
return presets
|
||||
except:
|
||||
return []
|
||||
|
||||
def get_support_patterns(self):
|
||||
return [
|
||||
{'id': 'tree', 'name': 'Tree'},
|
||||
{'id': 'lines', 'name': 'Lines'},
|
||||
{'id': 'grid', 'name': 'Grid'},
|
||||
{'id': 'triangles', 'name': 'Triangles'},
|
||||
{'id': 'concentric', 'name': 'Concentric'},
|
||||
{'id': 'zigzag', 'name': 'Zig Zag'},
|
||||
{'id': 'cross', 'name': 'Cross'},
|
||||
{'id': 'gyroid', 'name': 'Gyroid'},
|
||||
{'id': 'honeycomb', 'name': 'Honeycomb'},
|
||||
{'id': 'octagon', 'name': 'Octagon'}
|
||||
]
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import os
|
||||
import subprocess
|
||||
import configparser
|
||||
|
||||
class PrusaSlicerEngine:
|
||||
def __init__(self):
|
||||
self.name = "prusa_slicer"
|
||||
self.display_name = "PrusaSlicer"
|
||||
self.is_available = self._check_available()
|
||||
|
||||
def _check_available(self):
|
||||
try:
|
||||
result = subprocess.run(["prusa-slicer", "--help"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return b"Usage:" in result.stdout or b"Slic3r" in result.stdout or b"PrusaSlicer" in result.stdout or result.returncode == 0
|
||||
except (FileNotFoundError, OSError):
|
||||
return False
|
||||
|
||||
def slice(self, app, stl_filepath, gcode_filepath, **kwargs):
|
||||
"""
|
||||
@@ -55,3 +65,25 @@ class PrusaSlicerEngine:
|
||||
except Exception as e:
|
||||
app.logger.error(f"PrusaSlicer Exception: {e}")
|
||||
return False, str(e)
|
||||
|
||||
def get_quality_presets(self, app):
|
||||
all_files = [f for f in os.listdir(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer',"quality")) if f.endswith('.ini')]
|
||||
quality_presets = []
|
||||
for file in all_files:
|
||||
with open(os.path.join(app.root_path, '..', 'print_config', 'prusa_slicer', "quality", file), 'r') as f:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_file(f)
|
||||
if 'metadata' in config:
|
||||
quality_presets.append({
|
||||
'id': file.replace('.ini', ''),
|
||||
'name': config['metadata'].get('show_name', file.replace('.ini', '').replace('_', ' '))
|
||||
})
|
||||
return quality_presets
|
||||
|
||||
def get_support_patterns(self):
|
||||
return [
|
||||
{'id': 'rectilinear', 'name': 'Rectilinear'},
|
||||
{'id': 'grid', 'name': 'Grid'},
|
||||
{'id': 'organic', 'name': 'Organic (Tree)'},
|
||||
{'id': 'snug', 'name': 'Snug'}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user