AppConfig: Support for vendor / model / variant enable state

This commit is contained in:
Vojtech Kral 2018-03-28 11:36:36 +02:00
parent 3fcf194e39
commit 57e47a3296
12 changed files with 267 additions and 142 deletions

View File

@ -70,6 +70,8 @@ our $grey = Wx::Colour->new(200,200,200);
our $LANGUAGE_CHANGE_EVENT = Wx::NewEventType;
# 2) To inform about a change of Preferences.
our $PREFERENCES_EVENT = Wx::NewEventType;
# To inform AppConfig about Slic3r version available online
our $VERSION_ONLINE_EVENT = Wx::NewEventType;
sub OnInit {
my ($self) = @_;
@ -101,7 +103,9 @@ sub OnInit {
$self->{app_config}->set('version', $Slic3r::VERSION);
$self->{app_config}->save;
my $slic3r_update_avail = $self->{app_config}->get("version_check") && $self->{app_config}->get("version_online") != $Slic3r::VERSION;
# my $version_check = $self->{app_config}->get('version_check');
$self->{preset_updater} = Slic3r::PresetUpdater->new($VERSION_ONLINE_EVENT, $self->{app_config});
my $slic3r_update = $self->{app_config}->slic3r_update_avail;
Slic3r::GUI::set_app_config($self->{app_config});
Slic3r::GUI::load_language();
@ -140,15 +144,17 @@ sub OnInit {
# On OSX the UI was not initialized correctly if the wizard was called
# before the UI was up and running.
$self->CallAfter(sub {
if ($slic3r_update_avail) {
# XXX: recreate_GUI ???
if ($slic3r_update) {
# TODO
} elsif ($run_wizard) {
}
# XXX: ?
if ($run_wizard) {
# Run the config wizard, don't offer the "reset user profile" checkbox.
$self->{mainframe}->config_wizard(1);
}
# XXX: recreate_GUI ???
Slic3r::PresetUpdater::download($self->{app_config}, $self->{preset_bundle});
$self->{preset_updater}->download($self->{preset_bundle});
});
# The following event is emited by the C++ menu implementation of application language change.
@ -161,6 +167,14 @@ sub OnInit {
$self->update_ui_from_settings;
});
# The following event is emited by PresetUpdater (C++)
EVT_COMMAND($self, -1, $VERSION_ONLINE_EVENT, sub {
my ($self, $event) = @_;
my $version = $event->GetString;
$self->{app_config}->set('version_online', $version);
$self->{app_config}->save;
});
return 1;
}

View File

@ -213,8 +213,8 @@ add_library(libslic3r_gui STATIC
${LIBDIR}/slic3r/Utils/OctoPrint.hpp
${LIBDIR}/slic3r/Utils/Bonjour.cpp
${LIBDIR}/slic3r/Utils/Bonjour.hpp
${LIBDIR}/slic3r/Utils/PresetUpdate.cpp
${LIBDIR}/slic3r/Utils/PresetUpdate.hpp
${LIBDIR}/slic3r/Utils/PresetUpdater.cpp
${LIBDIR}/slic3r/Utils/PresetUpdater.hpp
)
add_library(admesh STATIC
@ -360,7 +360,7 @@ set(XS_XSP_FILES
${XSP_DIR}/SurfaceCollection.xsp
${XSP_DIR}/TriangleMesh.xsp
${XSP_DIR}/Utils_OctoPrint.xsp
${XSP_DIR}/Utils_PresetUpdate.xsp
${XSP_DIR}/Utils_PresetUpdater.xsp
${XSP_DIR}/XS.xsp
)
foreach (file ${XS_XSP_FILES})

View File

@ -64,6 +64,7 @@ REGISTER_CLASS(PresetCollection, "GUI::PresetCollection");
REGISTER_CLASS(PresetBundle, "GUI::PresetBundle");
REGISTER_CLASS(PresetHints, "GUI::PresetHints");
REGISTER_CLASS(TabIface, "GUI::Tab");
REGISTER_CLASS(PresetUpdater, "PresetUpdater");
REGISTER_CLASS(OctoPrint, "OctoPrint");
SV* ConfigBase__as_hash(ConfigBase* THIS)

View File

@ -1,5 +1,3 @@
#include <GL/glew.h>
#include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/Utils.hpp"
#include "AppConfig.hpp"
@ -9,15 +7,20 @@
#include <string.h>
#include <utility>
#include <assert.h>
#include <vector>
#include <boost/filesystem.hpp>
#include <boost/nowide/cenv.hpp>
#include <boost/nowide/fstream.hpp>
#include <boost/property_tree/ini_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/algorithm/string/predicate.hpp>
namespace Slic3r {
static const std::string VENDOR_PREFIX = "vendor:";
static const std::string MODEL_PREFIX = "model:";
void AppConfig::reset()
{
m_storage.clear();
@ -45,6 +48,8 @@ void AppConfig::set_defaults()
// Version check is enabled by default in the config, but it is not implemented yet. // XXX
if (get("version_check").empty())
set("version_check", "1");
if (get("preset_update").empty())
set("preset_update", "1");
// Use OpenGL 1.1 even if OpenGL 2.0 is available. This is mainly to support some buggy Intel HD Graphics drivers.
// https://github.com/prusa3d/Slic3r/issues/233
if (get("use_legacy_opengl").empty())
@ -67,6 +72,19 @@ void AppConfig::load()
if (! data.empty())
// If there is a non-empty data, then it must be a top-level (without a section) config entry.
m_storage[""][section.first] = data;
} else if (boost::starts_with(section.first, VENDOR_PREFIX)) {
// This is a vendor section listing enabled model / variants
const auto vendor_name = section.first.substr(VENDOR_PREFIX.size());
auto &vendor = m_vendors[vendor_name];
for (const auto &kvp : section.second) {
if (! boost::starts_with(kvp.first, MODEL_PREFIX)) { continue; }
const auto model_name = kvp.first.substr(MODEL_PREFIX.size());
std::vector<std::string> variants;
if (! unescape_strings_cstyle(kvp.second.data(), variants)) { continue; }
for (const auto &variant : variants) {
vendor[model_name].insert(variant);
}
}
} else {
// This must be a section name. Read the entries of a section.
std::map<std::string, std::string> &storage = m_storage[section.first];
@ -96,10 +114,53 @@ void AppConfig::save()
for (const std::pair<std::string, std::string> &kvp : category.second)
c << kvp.first << " = " << kvp.second << std::endl;
}
// Write vendor sections
for (const auto &vendor : m_vendors) {
size_t size_sum = 0;
for (const auto &model : vendor.second) { size_sum += model.second.size(); }
if (size_sum == 0) { continue; }
c << std::endl << "[" << VENDOR_PREFIX << vendor.first << "]" << std::endl;
for (const auto &model : vendor.second) {
if (model.second.size() == 0) { continue; }
const std::vector<std::string> variants(model.second.begin(), model.second.end());
const auto escaped = escape_strings_cstyle(variants);
c << MODEL_PREFIX << model.first << " = " << escaped << std::endl;
}
}
c.close();
m_dirty = false;
}
bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const
{
std::cerr << "AppConfig::get_variant(" << vendor << ", " << model << ", " << variant << ") ";
const auto it_v = m_vendors.find(vendor);
if (it_v == m_vendors.end()) { return false; }
const auto it_m = it_v->second.find(model);
return it_m == it_v->second.end() ? false : it_m->second.find(variant) != it_m->second.end();
}
void AppConfig::set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable)
{
if (enable) {
if (get_variant(vendor, model, variant)) { return; }
m_vendors[vendor][model].insert(variant);
} else {
auto it_v = m_vendors.find(vendor);
if (it_v == m_vendors.end()) { return; }
auto it_m = it_v->second.find(model);
if (it_m == it_v->second.end()) { return; }
auto it_var = it_m->second.find(variant);
if (it_var == it_m->second.end()) { return; }
it_m->second.erase(it_var);
}
// If we got here, there was an update
m_dirty = true;
}
std::string AppConfig::get_last_dir() const
{
const auto it = m_storage.find("recent");
@ -156,6 +217,16 @@ void AppConfig::reset_selections()
}
}
bool AppConfig::version_check_enabled() const
{
return get("version_check") == "1";
}
bool AppConfig::slic3r_update_avail() const
{
return version_check_enabled() && get("version_online") != SLIC3R_VERSION;
}
std::string AppConfig::config_path()
{
return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string();

View File

@ -1,9 +1,12 @@
#ifndef slic3r_AppConfig_hpp_
#define slic3r_AppConfig_hpp_
#include <set>
#include <map>
#include <string>
#include "libslic3r/Config.hpp"
namespace Slic3r {
class AppConfig
@ -65,6 +68,13 @@ public:
void clear_section(const std::string &section)
{ m_storage[section].clear(); }
// TODO: remove / upgrade
// ConfigOptionStrings get_strings(const std::string &section, const std::string &key) const;
// void set_strings(const std::string &section, const std::string &key, const ConfigOptionStrings &value);
// TODO:
bool get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const;
void set_variant(const std::string &vendor, const std::string &model, const std::string &variant, bool enable);
// return recent/skein_directory or recent/config_directory or empty string.
std::string get_last_dir() const;
void update_config_dir(const std::string &dir);
@ -78,6 +88,10 @@ public:
// the first non-default preset when called.
void reset_selections();
// Whether the Slic3r version available online differs from this one
bool version_check_enabled() const;
bool slic3r_update_avail() const;
// Get the default config path from Slic3r::data_dir().
static std::string config_path();
@ -87,6 +101,8 @@ public:
private:
// Map of section, name -> value
std::map<std::string, std::map<std::string, std::string>> m_storage;
// Map of enabled vendors / models / variants
std::map<std::string, std::map<std::string, std::set<std::string>>> m_vendors;
// Has any value been modified since the config.ini has been last saved or loaded?
bool m_dirty;
};

View File

@ -1,104 +0,0 @@
#include "PresetUpdate.hpp"
#include <iostream> // XXX
#include <thread>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include "libslic3r/Utils.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/Utils/Http.hpp"
namespace fs = boost::filesystem;
namespace Slic3r {
struct PresetUpdater::priv
{
PresetBundle *bundle;
fs::path cache_path;
std::thread thread;
priv(PresetBundle *bundle);
void download();
};
PresetUpdater::priv::priv(PresetBundle *bundle) :
bundle(bundle),
cache_path(fs::path(Slic3r::data_dir()) / "cache")
{}
void PresetUpdater::priv::download()
{
std::cerr << "PresetUpdater::priv::download()" << std::endl;
std::cerr << "Bundle vendors: " << bundle->vendors.size() << std::endl;
for (const auto &vendor : bundle->vendors) {
std::cerr << "vendor: " << vendor.name << std::endl;
std::cerr << " URL: " << vendor.config_update_url << std::endl;
// TODO: Proper caching
auto target_path = cache_path / vendor.id;
target_path += ".ini";
std::cerr << "target_path: " << target_path << std::endl;
Http::get(vendor.config_update_url)
.on_complete([&](std::string body, unsigned http_status) {
std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl;
fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(body.c_str(), body.size());
})
.on_error([](std::string body, std::string error, unsigned http_status) {
// TODO: what about errors?
std::cerr << "Error: " << http_status << ", " << error << std::endl;
})
.perform_sync();
}
}
PresetUpdater::PresetUpdater(PresetBundle *preset_bundle) : p(new priv(preset_bundle)) {}
// Public
PresetUpdater::~PresetUpdater()
{
if (p && p->thread.joinable()) {
p->thread.detach();
}
}
void PresetUpdater::download(AppConfig *app_config, PresetBundle *preset_bundle)
{
std::cerr << "PresetUpdater::download()" << std::endl;
auto self = std::make_shared<PresetUpdater>(preset_bundle);
auto thread = std::thread([self](){
self->p->download();
});
self->p->thread = std::move(thread);
}
// TODO: remove
namespace Utils {
void preset_update_check()
{
std::cerr << "preset_update_check()" << std::endl;
// TODO:
// 1. Get a version tag or the whole bundle from the web
// 2. Store into temporary location (?)
// 3. ???
// 4. Profit!
}
}
}

View File

@ -0,0 +1,137 @@
#include "PresetUpdater.hpp"
#include <iostream> // XXX
#include <thread>
#include <boost/algorithm/string.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/fstream.hpp>
#include <wx/app.h>
#include <wx/event.h>
#include "libslic3r/Utils.hpp"
#include "slic3r/GUI/GUI.hpp"
// #include "slic3r/GUI/AppConfig.hpp"
#include "slic3r/GUI/PresetBundle.hpp"
#include "slic3r/Utils/Http.hpp"
namespace fs = boost::filesystem;
namespace Slic3r {
// TODO: proper URL
static const std::string SLIC3R_VERSION_URL = "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version";
enum {
SLIC3R_VERSION_BODY_MAX = 256,
};
struct PresetUpdater::priv
{
int version_online_event;
bool version_check;
bool preset_update;
fs::path cache_path;
bool cancel;
std::thread thread;
priv(int event);
void download(const std::set<VendorProfile> vendors) const;
};
PresetUpdater::priv::priv(int event) :
version_online_event(event),
version_check(false),
preset_update(false),
cache_path(fs::path(Slic3r::data_dir()) / "cache"),
cancel(false)
{}
void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const
{
std::cerr << "PresetUpdater::priv::download()" << std::endl;
if (!version_check) { return; }
// Download current Slic3r version
Http::get(SLIC3R_VERSION_URL)
.size_limit(SLIC3R_VERSION_BODY_MAX)
.on_progress([this](Http::Progress, bool &cancel) {
cancel = this->cancel;
})
.on_complete([&](std::string body, unsigned http_status) {
boost::trim(body);
std::cerr << "Got version: " << http_status << ", body: \"" << body << '"' << std::endl;
wxCommandEvent* evt = new wxCommandEvent(version_online_event);
evt->SetString(body);
GUI::get_app()->QueueEvent(evt);
})
.perform_sync();
if (!preset_update) { return; }
// Donwload vendor preset bundles
std::cerr << "Bundle vendors: " << vendors.size() << std::endl;
for (const auto &vendor : vendors) {
std::cerr << "vendor: " << vendor.name << std::endl;
std::cerr << " URL: " << vendor.config_update_url << std::endl;
if (cancel) { return; }
// TODO: Proper caching
auto target_path = cache_path / vendor.id;
target_path += ".ini";
std::cerr << "target_path: " << target_path << std::endl;
Http::get(vendor.config_update_url)
.on_progress([this](Http::Progress, bool &cancel) {
cancel = this->cancel;
})
.on_complete([&](std::string body, unsigned http_status) {
std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl;
fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(body.c_str(), body.size());
})
.perform_sync();
}
}
PresetUpdater::PresetUpdater(int version_online_event, AppConfig *app_config) :
p(new priv(version_online_event))
{
p->preset_update = app_config->get("preset_update") == "1";
// preset_update implies version_check:
p->version_check = p->preset_update || app_config->get("version_check") == "1";
}
// Public
PresetUpdater::~PresetUpdater()
{
if (p && p->thread.joinable()) {
p->cancel = true;
p->thread.join();
}
}
void PresetUpdater::download(PresetBundle *preset_bundle)
{
std::cerr << "PresetUpdater::download()" << std::endl;
// Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again
// into the closure (but perhaps the compiler can elide this).
std::set<VendorProfile> vendors = preset_bundle->vendors;
p->thread = std::move(std::thread([this, vendors]() {
this->p->download(std::move(vendors));
}));
}
}

View File

@ -12,27 +12,19 @@ class PresetBundle;
class PresetUpdater
{
public:
PresetUpdater(PresetBundle *preset_bundle);
PresetUpdater(int version_online_event, AppConfig *app_config);
PresetUpdater(PresetUpdater &&) = delete;
PresetUpdater(const PresetUpdater &) = delete;
PresetUpdater &operator=(PresetUpdater &&) = delete;
PresetUpdater &operator=(const PresetUpdater &) = delete;
~PresetUpdater();
static void download(AppConfig *app_config, PresetBundle *preset_bundle);
void download(PresetBundle *preset_bundle);
private:
struct priv;
std::unique_ptr<priv> p;
};
// TODO: Remove
namespace Utils {
void preset_update_check();
}
}
#endif

View File

@ -43,4 +43,5 @@
void update_last_output_dir(char *dir);
void reset_selections();
bool slic3r_update_avail() const;
};

View File

@ -1,18 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "slic3r/Utils/PresetUpdate.hpp"
%}
%name{Slic3r::PresetUpdater} class PresetUpdater {
static void download(PresetBundle *preset_bundle);
};
# TODO: remove:
%package{Slic3r::Utils};
void preset_update_check()
%code%{ Slic3r::Utils::preset_update_check(); %};

View File

@ -0,0 +1,11 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "slic3r/Utils/PresetUpdater.hpp"
%}
%name{Slic3r::PresetUpdater} class PresetUpdater {
PresetUpdater(int version_online_event, AppConfig *app_config);
void download(PresetBundle* preset_bundle);
};

View File

@ -239,6 +239,10 @@ Ref<TabIface> O_OBJECT_SLIC3R_T
# ConfigWizard* O_OBJECT_SLIC3R
# Ref<ConfigWizard> O_OBJECT_SLIC3R_T
PresetUpdater* O_OBJECT_SLIC3R
Ref<PresetUpdater> O_OBJECT_SLIC3R_T
Clone<PresetUpdater> O_OBJECT_SLIC3R_T
OctoPrint* O_OBJECT_SLIC3R
Ref<OctoPrint> O_OBJECT_SLIC3R_T
Clone<OctoPrint> O_OBJECT_SLIC3R_T