diff --git a/src/octoprint/filemanager/__init__.py b/src/octoprint/filemanager/__init__.py index 1d73288b..753e8112 100644 --- a/src/octoprint/filemanager/__init__.py +++ b/src/octoprint/filemanager/__init__.py @@ -150,7 +150,7 @@ class FileManager(object): def default_slicer(self): return self._slicing_manager.default_slicer - def slice(self, slicer_name, source_location, source_path, dest_location, dest_path, profile=None, overrides=None, callback=None, callback_args=None): + def slice(self, slicer_name, source_location, source_path, dest_location, dest_path, profile=None, printer_profile_id=None, overrides=None, callback=None, callback_args=None): absolute_source_path = self.get_absolute_path(source_location, source_path) def stlProcessed(source_location, source_path, tmp_path, dest_location, dest_path, start_time, callback, callback_args, _error=None, _cancelled=False): @@ -231,6 +231,7 @@ class FileManager(object): stlProcessed, callback_args=args, overrides=overrides, + printer_profile_id=printer_profile_id, on_progress=self.on_slicing_progress, on_progress_args=(slicer_name, source_location, source_path, dest_location, dest_path)) diff --git a/src/octoprint/printer/profile.py b/src/octoprint/printer/profile.py index 1d158400..f87aab1f 100644 --- a/src/octoprint/printer/profile.py +++ b/src/octoprint/printer/profile.py @@ -11,7 +11,7 @@ import copy import re from octoprint.settings import settings -from octoprint.util import dict_merge +from octoprint.util import dict_merge, dict_clean class SaveError(Exception): pass @@ -25,6 +25,7 @@ class PrinterProfileManager(object): default = dict( id = "_default", name = "Default", + model = "Generic RepRap Printer", color = "default", volume=dict( width = 200, @@ -79,7 +80,7 @@ class PrinterProfileManager(object): return False return self._remove_from_path(self._get_profile_path(identifier)) - def save(self, profile, allow_overwrite=False): + def save(self, profile, allow_overwrite=False, make_default=False): if "id" in profile: identifier = profile["id"] elif "name" in profile: @@ -94,7 +95,12 @@ class PrinterProfileManager(object): settings().set(["printerProfiles", "defaultProfile"], default_profile, defaults=dict(printerProfiles=dict(defaultProfile=self.__class__.default))) profile["id"] = identifier + profile = dict_clean(profile, self.__class__.default) self._save_to_path(self._get_profile_path(identifier), profile, allow_overwrite=allow_overwrite) + + if make_default: + settings().set(["printerProfiles", "default"], identifier) + return self.get(identifier) def get_default(self): @@ -207,3 +213,4 @@ class PrinterProfileManager(object): sanitized_name = ''.join(c for c in name if c in valid_chars) sanitized_name = sanitized_name.replace(" ", "_") return sanitized_name + diff --git a/src/octoprint/server/api/files.py b/src/octoprint/server/api/files.py index 30eec67e..708e3db3 100644 --- a/src/octoprint/server/api/files.py +++ b/src/octoprint/server/api/files.py @@ -334,12 +334,18 @@ def gcodeFileCommand(filename, target): else: profile = None + if "printerProfile" in data.keys() and data["printerProfile"]: + printerProfile = data["printerProfile"] + del data["printerProfile"] + else: + printerProfile = None + override_keys = [k for k in data if k.startswith("profile.") and data[k] is not None] overrides = dict() for key in override_keys: overrides[key[len("profile."):]] = data[key] - ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, overrides=overrides) + ok, result = fileManager.slice(slicer, target, filename, target, gcode_name, profile=profile, printer_profile_id=printerProfile, overrides=overrides) if ok: files = {} location = url_for(".readGcodeFile", target=target, filename=gcode_name, _external=True) diff --git a/src/octoprint/server/api/printer_profiles.py b/src/octoprint/server/api/printer_profiles.py index 0bfba610..2984f3aa 100644 --- a/src/octoprint/server/api/printer_profiles.py +++ b/src/octoprint/server/api/printer_profiles.py @@ -20,7 +20,7 @@ from octoprint.server import printerProfileManager @api.route("/printerProfiles", methods=["GET"]) def printerProfilesList(): all_profiles = printerProfileManager.get_all() - return jsonify(dict(profiles=all_profiles.values())) + return jsonify(dict(profiles=_convert_profiles(all_profiles))) @api.route("/printerProfiles", methods=["POST"]) @restricted_access @@ -53,6 +53,7 @@ def printerProfilesGet(identifier): profile = printerProfileManager.get(identifier) if profile is None: make_response("Unknown profile: %s" % identifier, 404) + return jsonify(_convert_profile(profile)) @api.route("/printerProfiles/", methods=["DELETE"]) @restricted_access @@ -68,7 +69,7 @@ def printerProfilesUpdate(identifier): json_data = request.json if not "profile" in json_data: - return None, None, make_response("No profile included in request", 400) + make_response("No profile included in request", 400) profile = printerProfileManager.get(identifier) if profile is None: @@ -77,14 +78,19 @@ def printerProfilesUpdate(identifier): new_profile = json_data["profile"] new_profile = dict_merge(profile, new_profile) + make_default = False + if "default" in new_profile: + make_default = True + del new_profile["default"] + new_profile["id"] = identifier if not _validate_profile(new_profile): - return None, None, make_response("Combined profile is invalid, missing obligatory values", 400) + make_response("Combined profile is invalid, missing obligatory values", 400) try: - saved_profile = printerProfileManager.save(new_profile, allow_overwrite=True) + saved_profile = printerProfileManager.save(new_profile, allow_overwrite=True, make_default=make_default) except Exception as e: - return None, None, make_response("Could not save profile: %s" % e.message) + make_response("Could not save profile: %s" % e.message) return jsonify(dict(profile=_convert_profile(saved_profile))) @@ -94,13 +100,14 @@ def _convert_profiles(profiles): result[identifier] = _convert_profile(profile) return result -def _convert_profile(profile, default=None): - if default is None: - default = printerProfileManager.get_default()["id"] +def _convert_profile(profile): + default = printerProfileManager.get_default()["id"] + current = printerProfileManager.get_current_or_default()["id"] converted = copy.deepcopy(profile) converted["resource"] = url_for(".printerProfilesGet", identifier=profile["id"], _external=True) converted["default"] = (profile["id"] == default) + converted["current"] = (profile["id"] == current) return converted def _validate_profile(profile): diff --git a/src/octoprint/slicing/__init__.py b/src/octoprint/slicing/__init__.py index e8ec48dd..fa5b1a1b 100644 --- a/src/octoprint/slicing/__init__.py +++ b/src/octoprint/slicing/__init__.py @@ -142,7 +142,7 @@ class SlicingManager(object): import threading slicer_worker_thread = threading.Thread(target=slicer_worker, - args=(slicer, source_path, dest_path, profile_name, overrides, printer_profile_id, callback, callback_args, callback_kwargs)) + args=(slicer, source_path, dest_path, profile_name, overrides, printer_profile, callback, callback_args, callback_kwargs)) slicer_worker_thread.daemon = True slicer_worker_thread.start() return True, None diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 2be215a2..025358fe 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -67,16 +67,17 @@ $(function() { //~~ Initialize view models var loginStateViewModel = new LoginStateViewModel(); + var printerProfilesViewModel = new PrinterProfilesViewModel(); var usersViewModel = new UsersViewModel(loginStateViewModel); - var settingsViewModel = new SettingsViewModel(loginStateViewModel, usersViewModel); - var connectionViewModel = new ConnectionViewModel(loginStateViewModel, settingsViewModel); + var settingsViewModel = new SettingsViewModel(loginStateViewModel, usersViewModel, printerProfilesViewModel); + var connectionViewModel = new ConnectionViewModel(loginStateViewModel, settingsViewModel, printerProfilesViewModel); var timelapseViewModel = new TimelapseViewModel(loginStateViewModel); var printerStateViewModel = new PrinterStateViewModel(loginStateViewModel, timelapseViewModel); var appearanceViewModel = new AppearanceViewModel(settingsViewModel); var temperatureViewModel = new TemperatureViewModel(loginStateViewModel, settingsViewModel); var controlViewModel = new ControlViewModel(loginStateViewModel, settingsViewModel); var terminalViewModel = new TerminalViewModel(loginStateViewModel, settingsViewModel); - var slicingViewModel = new SlicingViewModel(loginStateViewModel, connectionViewModel); + var slicingViewModel = new SlicingViewModel(loginStateViewModel, printerProfilesViewModel); var gcodeFilesViewModel = new GcodeFilesViewModel(printerStateViewModel, loginStateViewModel, slicingViewModel); var gcodeViewModel = new GcodeViewModel(loginStateViewModel, settingsViewModel); var navigationViewModel = new NavigationViewModel(loginStateViewModel, appearanceViewModel, settingsViewModel, usersViewModel); @@ -84,6 +85,7 @@ $(function() { var viewModelMap = { loginStateViewModel: loginStateViewModel, + printerProfilesViewModel: printerProfilesViewModel, usersViewModel: usersViewModel, settingsViewModel: settingsViewModel, connectionViewModel: connectionViewModel, diff --git a/src/octoprint/static/js/app/viewmodels/printerprofiles.js b/src/octoprint/static/js/app/viewmodels/printerprofiles.js index a55851cf..ddedefd8 100644 --- a/src/octoprint/static/js/app/viewmodels/printerprofiles.js +++ b/src/octoprint/static/js/app/viewmodels/printerprofiles.js @@ -17,6 +17,8 @@ function PrinterProfilesViewModel() { [], 5 ); + self.defaultProfile = ko.observable(); + self.currentProfile = ko.observable(); self.editorName = ko.observable(); self.editorColor = ko.observable(); @@ -51,7 +53,53 @@ function PrinterProfilesViewModel() { }; self.fromResponse = function(data) { - self.profiles.updateItems(data.profiles); + var items = []; + var defaultProfile = undefined; + var currentProfile = undefined; + _.each(data.profiles, function(entry) { + if (entry.default) { + defaultProfile = entry.id; + } + if (entry.current) { + currentProfile = entry.id; + } + items.push({ + id: ko.observable(entry.id), + name: ko.observable(entry.name), + model: ko.observable(entry.model), + volume: { + width: ko.observable(entry.volume.width), + depth: ko.observable(entry.volume.depth), + height: ko.observable(entry.volume.height), + formFactor: ko.observable(entry.volume.formFactor) + }, + heatedBed: ko.observable(entry.heatedBed), + axes: { + x: { + speed: ko.observable(entry.axes.x.speed), + inverted: ko.observable(entry.axes.x.inverted) + }, + y: { + speed: ko.observable(entry.axes.y.speed), + inverted: ko.observable(entry.axes.y.inverted) + }, + z: { + speed: ko.observable(entry.axes.z.speed), + inverted: ko.observable(entry.axes.z.inverted) + }, + e: { + speed: ko.observable(entry.axes.e.speed), + inverted: ko.observable(entry.axes.e.inverted) + } + }, + isdefault: ko.observable(entry.default), + iscurrent: ko.observable(entry.current), + resource: ko.observable(entry.resource) + }); + }); + self.profiles.updateItems(items); + self.defaultProfile(defaultProfile); + self.currentProfile(currentProfile); }; self.addProfile = function() { @@ -132,5 +180,9 @@ function PrinterProfilesViewModel() { } return profile; - } + }; + + self.onStartup = function() { + self.requestData(); + }; } \ No newline at end of file diff --git a/src/octoprint/static/js/app/viewmodels/settings.js b/src/octoprint/static/js/app/viewmodels/settings.js index c1aeffb0..7b7524db 100644 --- a/src/octoprint/static/js/app/viewmodels/settings.js +++ b/src/octoprint/static/js/app/viewmodels/settings.js @@ -1,8 +1,9 @@ -function SettingsViewModel(loginStateViewModel, usersViewModel) { +function SettingsViewModel(loginStateViewModel, usersViewModel, printerProfilesViewModel) { var self = this; self.loginState = loginStateViewModel; self.users = usersViewModel; + self.printerProfiles = printerProfilesViewModel; self.api_enabled = ko.observable(undefined); self.api_key = ko.observable(undefined); diff --git a/src/octoprint/static/js/app/viewmodels/slicing.js b/src/octoprint/static/js/app/viewmodels/slicing.js index b7182238..98521939 100644 --- a/src/octoprint/static/js/app/viewmodels/slicing.js +++ b/src/octoprint/static/js/app/viewmodels/slicing.js @@ -1,8 +1,8 @@ -function SlicingViewModel(loginStateViewModel, connectionViewModel) { +function SlicingViewModel(loginStateViewModel, printerProfilesViewModel) { var self = this; self.loginState = loginStateViewModel; - self.connection = connectionViewModel; + self.printerProfiles = printerProfilesViewModel; self.target = undefined; self.file = undefined; @@ -18,12 +18,14 @@ function SlicingViewModel(loginStateViewModel, connectionViewModel) { self.slicers = ko.observableArray(); self.profile = ko.observable(); self.profiles = ko.observableArray(); + self.printerProfile = ko.observable(); self.show = function(target, file) { self.target = target; self.file = file; self.title(_.sprintf(gettext("Slicing %(filename)s"), {filename: self.file})); self.gcodeFilename(self.file.substr(0, self.file.lastIndexOf("."))); + self.printerProfile(self.printerProfiles.currentProfile()); $("#slicing_configuration_dialog").modal("show"); }; @@ -121,6 +123,7 @@ function SlicingViewModel(loginStateViewModel, connectionViewModel) { command: "slice", slicer: self.slicer(), profile: self.profile(), + printerProfile: self.printerProfile(), gcode: gcodeFilename }; diff --git a/src/octoprint/templates/dialogs.jinja2 b/src/octoprint/templates/dialogs.jinja2 index d3b8661f..968be5ca 100644 --- a/src/octoprint/templates/dialogs.jinja2 +++ b/src/octoprint/templates/dialogs.jinja2 @@ -15,7 +15,13 @@
- + +
+
+
+ +
+
diff --git a/src/octoprint/templates/index.jinja2 b/src/octoprint/templates/index.jinja2 index 133c3548..5840b790 100644 --- a/src/octoprint/templates/index.jinja2 +++ b/src/octoprint/templates/index.jinja2 @@ -695,6 +695,7 @@ + diff --git a/src/octoprint/templates/settings.jinja2 b/src/octoprint/templates/settings.jinja2 index e773fe0b..62a3022d 100644 --- a/src/octoprint/templates/settings.jinja2 +++ b/src/octoprint/templates/settings.jinja2 @@ -9,6 +9,7 @@
  • {{ _('Serial Connection') }}
  • {{ _('Printer Parameters') }}
  • +
  • {{ _('Printer Profiles') }}
  • {{ _('Temperatures') }}
  • {{ _('Terminal filters') }}
  • @@ -105,6 +106,31 @@
    + +
    + +

    {{ _('Printer Profiles') }}

    + + + + + + + + + + + + + + + + +
    {{ _('Name') }}{{ _('Model') }}{{ _('Action') }}
    +  |  +
    + +
    diff --git a/src/octoprint/util/__init__.py b/src/octoprint/util/__init__.py index 005b28fe..8f6b1537 100644 --- a/src/octoprint/util/__init__.py +++ b/src/octoprint/util/__init__.py @@ -250,6 +250,23 @@ def dict_merge(a, b): return result +def dict_clean(a, b): + + from copy import deepcopy + if not isinstance(b, dict): + return a + + result = deepcopy(a) + for k, v in a.iteritems(): + if not k in b: + del result[k] + elif isinstance(v, dict): + result[k] = dict_clean(v, b[k]) + else: + result[k] = deepcopy(v) + return result + + class Object(object): pass