Fixed a bug in the Win32 start wrapper (wrong number of parameters was passed for the GUI slic3r.exe).

Reworked command line processing for the GUI slic3r. Now the config is loaded first, then the model files (also the configs from AMF/3MF are applied), and lastly the free standing parameters are applied.
Fixed unescaping for command line parameters. The string parameters are now not unescaped, string vector parameters are unescaped only if enquoted.
Tab::load_current_preset() - disabled CallAfter for predictability. With CallAfter, it was difficult to call the method in sequence with other methods.
Fixed some missing ->Destroy() calls on dialogs created from MainFrame
Fixed some compiler warnings.
This commit is contained in:
bubnikv 2019-01-09 10:43:17 +01:00
parent 094e3cb565
commit 5b1c1d5922
11 changed files with 194 additions and 92 deletions

View File

@ -604,23 +604,31 @@ bool DynamicConfig::read_cli(int argc, char** argv, t_config_option_keys* extra)
value = argv[++ i];
}
// Store the option value.
const bool existing = this->has(opt_key);
if (ConfigOptionBool* opt = this->opt<ConfigOptionBool>(opt_key, true)) {
opt->value = !no;
} else if (ConfigOptionBools* opt = this->opt<ConfigOptionBools>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->values.push_back(!no);
} else if (ConfigOptionStrings* opt = this->opt<ConfigOptionStrings>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionFloats* opt = this->opt<ConfigOptionFloats>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
} else if (ConfigOptionPoints* opt = this->opt<ConfigOptionPoints>(opt_key, true)) {
if (!existing) opt->values.clear(); // remove the default values
opt->deserialize(value, true);
const bool existing = this->has(opt_key);
ConfigOption *opt_base = this->option(opt_key, true);
ConfigOptionVectorBase *opt_vector = opt_base->is_vector() ? static_cast<ConfigOptionVectorBase*>(opt_base) : nullptr;
if (opt_vector) {
// Vector values will be chained. Repeated use of a parameter will append the parameter or parameters
// to the end of the value.
if (!existing)
// remove the default values
opt_vector->deserialize("", true);
if (opt_base->type() == coBools)
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
// unescaped by the calling shell.
opt_vector->deserialize(value, true);
} else if (opt_base->type() == coBool) {
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
} else if (opt_base->type() == coString) {
// Do not unescape single string values, the unescaping is left to the calling shell.
static_cast<ConfigOptionString*>(opt_base)->value = value;
} else {
this->set_deserialize(opt_key, value, true);
// Any scalar value of a type different from Bool and String.
this->set_deserialize(opt_key, value, false);
}
}
return true;

View File

@ -103,7 +103,7 @@ public:
Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; }
Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; }
Point& operator*=(const double &rhs) { (*this)(0) *= rhs; (*this)(1) *= rhs; return *this; }
Point& operator*=(const double &rhs) { (*this)(0) = coord_t((*this)(0) * rhs); (*this)(1) = coord_t((*this)(1) * rhs); return *this; }
void rotate(double angle);
void rotate(double angle, const Point &center);

View File

@ -1164,12 +1164,11 @@ public:
class DynamicPrintAndCLIConfig : public DynamicPrintConfig
{
public:
DynamicPrintAndCLIConfig() { this->apply(FullPrintConfig::defaults()); this->apply(CLIConfig()); }
DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {}
DynamicPrintAndCLIConfig() {}
DynamicPrintAndCLIConfig(const DynamicPrintAndCLIConfig &other) : DynamicPrintConfig(other) {}
// Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here.
const ConfigDef* def() const override { return &s_def; }
t_config_option_keys keys() const override { return s_def.keys(); }
// Verify whether the opt_key has not been obsoleted or renamed.
// Both opt_key and value may be modified by handle_legacy().

View File

@ -59,10 +59,10 @@ int main(int argc, char **argv)
}
// parse all command line options into a DynamicConfig
DynamicPrintAndCLIConfig config;
DynamicPrintAndCLIConfig all_config;
t_config_option_keys input_files;
// if any option is unsupported, print usage and abort immediately
if (! config.read_cli(argc, argv, &input_files)) {
if (! all_config.read_cli(argc, argv, &input_files)) {
printUsage();
return 0;
}
@ -96,21 +96,15 @@ int main(int argc, char **argv)
// apply command line options to a more handy CLIConfig
CLIConfig cli_config;
cli_config.apply(config, true);
cli_config.apply(all_config, true);
set_data_dir(cli_config.datadir.value);
DynamicPrintConfig print_config;
// Load the extra config values.
DynamicPrintConfig extra_config;
extra_config.apply(all_config, true);
if ((argc == 1 || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) {
#if 1
GUI::GUI_App *gui = new GUI::GUI_App();
GUI::GUI_App::SetInstance(gui);
wxEntry(argc, argv);
#else
std::cout << "GUI support has not been built." << "\n";
#endif
}
// load config files supplied via --load
DynamicPrintConfig print_config;
for (const std::string &file : cli_config.load.values) {
if (! boost::filesystem::exists(file)) {
boost::nowide::cout << "No such file: " << file << std::endl;
@ -126,10 +120,40 @@ int main(int argc, char **argv)
c.normalize();
print_config.apply(c);
}
if ((argc == 1 || cli_config.gui.value) && ! cli_config.no_gui.value && ! cli_config.help.value && cli_config.save.value.empty()) {
#if 1
GUI::GUI_App *gui = new GUI::GUI_App();
GUI::GUI_App::SetInstance(gui);
gui->CallAfter([gui, &input_files, &cli_config, &extra_config, &print_config] {
#if 0
// Load the cummulative config over the currently active profiles.
//FIXME if multiple configs are loaded, only the last one will have an effect.
// We need to decide what to do about loading of separate presets (just print preset, just filament preset etc).
// As of now only the full configs are supported here.
if (! print_config.empty())
gui->mainframe->load_config(print_config);
#endif
if (! cli_config.load.values.empty())
// Load the last config to give it a name at the UI. The name of the preset may be later
// changed by loading an AMF or 3MF.
//FIXME this is not strictly correct, as one may pass a print/filament/printer profile here instead of a full config.
gui->mainframe->load_config_file(cli_config.load.values.back());
// If loading a 3MF file, the config is loaded from the last one.
gui->plater()->load_files(input_files, true, true);
if (! extra_config.empty())
gui->mainframe->load_config(extra_config);
});
return wxEntry(argc, argv);
#else
std::cout << "GUI support has not been built." << "\n";
return -1;
#endif
}
// apply command line options to a more specific DynamicPrintConfig which provides normalize()
// (command line options override --load files)
print_config.apply(config, true);
print_config.apply(extra_config, true);
// write config if requested
if (! cli_config.save.value.empty()) {

View File

@ -10,6 +10,8 @@
#include <wx/glcanvas.h>
#include <wx/debug.h>
#include <boost/algorithm/string/predicate.hpp>
#include "libslic3r/Print.hpp"
#include "libslic3r/Polygon.hpp"
@ -635,28 +637,34 @@ void MainFrame::export_config()
}
// Load a config file containing a Print, Filament & Printer preset.
void MainFrame::load_config_file(wxString file/* = wxEmptyString*/)
void MainFrame::load_config_file()
{
if (file.IsEmpty()) {
if (!wxGetApp().check_unsaved_changes())
return;
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK)
return;
file = dlg->GetPath();
dlg->Destroy();
if (!wxGetApp().check_unsaved_changes())
return;
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", "INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
wxString file;
if (dlg->ShowModal() == wxID_OK)
file = dlg->GetPath();
dlg->Destroy();
if (! file.IsEmpty() && this->load_config_file(file.ToUTF8().data())) {
wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file;
}
}
// Load a config file containing a Print, Filament & Printer preset from command line.
bool MainFrame::load_config_file(const std::string &path)
{
try {
wxGetApp().preset_bundle->load_config_file(file.ToUTF8().data());
wxGetApp().preset_bundle->load_config_file(path);
} catch (const std::exception &ex) {
show_error(this, ex.what());
return;
return false;
}
wxGetApp().load_current_presets();
wxGetApp().app_config->update_config_dir(get_dir_name(file));
m_last_config = file;
wxGetApp().load_current_presets();
return true;
}
void MainFrame::export_configbundle()
@ -700,11 +708,13 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
auto dlg = new wxFileDialog(this, _(L("Select configuration to load:")),
!m_last_config.IsEmpty() ? get_dir_name(m_last_config) : wxGetApp().app_config->get_last_dir(),
"config.ini", file_wildcards(FT_INI), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg->ShowModal() != wxID_OK)
return;
if (dlg->ShowModal() != wxID_OK) {
dlg->Destroy();
return;
}
file = dlg->GetPath();
dlg->Destroy();
}
dlg->Destroy();
}
wxGetApp().app_config->update_config_dir(get_dir_name(file));
@ -727,10 +737,37 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
// Also update the platter with the new presets.
void MainFrame::load_config(const DynamicPrintConfig& config)
{
for (auto tab : wxGetApp().tabs_list)
tab->load_config(config);
if (m_plater)
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();
const auto *opt_printer_technology = config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology");
if (opt_printer_technology != nullptr && opt_printer_technology->value != printer_technology) {
printer_technology = opt_printer_technology->value;
this->plater()->set_printer_technology(printer_technology);
}
#if 0
for (auto tab : wxGetApp().tabs_list)
if (tab->supports_printer_technology(printer_technology)) {
if (tab->name() == "printer")
static_cast<TabPrinter*>(tab)->update_pages();
tab->load_config(config);
}
if (m_plater)
m_plater->on_config_change(config);
#else
// Load the currently selected preset into the GUI, update the preset selection box.
//FIXME this is not quite safe for multi-extruder printers,
// as the number of extruders is not adjusted for the vector values.
// (see PresetBundle::update_multi_material_filament_presets())
// Better to call PresetBundle::load_config() instead?
for (auto tab : wxGetApp().tabs_list)
if (tab->supports_printer_technology(printer_technology)) {
// Only apply keys, which are present in the tab's config. Ignore the other keys.
for (const std::string &opt_key : tab->get_config()->diff(config))
// Ignore print_settings_id, printer_settings_id, filament_settings_id etc.
if (! boost::algorithm::ends_with(opt_key, "_settings_id"))
tab->get_config()->option(opt_key)->set(config.option(opt_key));
}
wxGetApp().load_current_presets();
#endif
}
void MainFrame::select_tab(size_t tab) const
@ -806,14 +843,14 @@ void MainFrame::update_ui_from_settings()
tab->update_ui_from_settings();
}
std::string MainFrame::get_base_name(const wxString full_name) const
std::string MainFrame::get_base_name(const wxString &full_name) const
{
return boost::filesystem::path(full_name).filename().string();
return boost::filesystem::path(full_name.wx_str()).filename().string();
}
std::string MainFrame::get_dir_name(const wxString full_name) const
std::string MainFrame::get_dir_name(const wxString &full_name) const
{
return boost::filesystem::path(full_name).parent_path().string();
return boost::filesystem::path(full_name.wx_str()).parent_path().string();
}

View File

@ -53,8 +53,8 @@ class MainFrame : public wxFrame
PrintHostQueueDialog *m_printhost_queue_dlg;
std::string get_base_name(const wxString full_name) const ;
std::string get_dir_name(const wxString full_name) const ;
std::string get_base_name(const wxString &full_name) const;
std::string get_dir_name(const wxString &full_name) const;
void on_presets_changed(SimpleEvent&);
void on_value_changed(wxCommandEvent&);
@ -86,7 +86,10 @@ public:
void reslice_now();
void repair_stl();
void export_config();
void load_config_file(wxString file = wxEmptyString);
// Query user for the config file and open it.
void load_config_file();
// Open a config file. Return true if loaded.
bool load_config_file(const std::string &path);
void export_configbundle();
void load_configbundle(wxString file = wxEmptyString);
void load_config(const DynamicPrintConfig& config);

View File

@ -2594,6 +2594,16 @@ void Plater::extract_config_from_project()
void Plater::load_files(const std::vector<fs::path>& input_files, bool load_model, bool load_config) { p->load_files(input_files, load_model, load_config); }
// To be called when providing a list of files to the GUI slic3r on command line.
void Plater::load_files(const std::vector<std::string>& input_files, bool load_model, bool load_config)
{
std::vector<fs::path> paths;
paths.reserve(input_files.size());
for (const std::string &path : input_files)
paths.emplace_back(path);
p->load_files(paths, load_model, load_config);
}
void Plater::update() { p->update(); }
void Plater::update_ui_from_settings() { p->update_ui_from_settings(); }

View File

@ -116,6 +116,8 @@ public:
void extract_config_from_project();
void load_files(const std::vector<boost::filesystem::path>& input_files, bool load_model = true, bool load_config = true);
// To be called when providing a list of files to the GUI slic3r on command line.
void load_files(const std::vector<std::string>& input_files, bool load_model = true, bool load_config = true);
void update();
void select_view(const std::string& direction);

View File

@ -2283,7 +2283,7 @@ void TabPrinter::update_sla()
// Initialize the UI from the current preset
void Tab::load_current_preset()
{
auto preset = m_presets->get_edited_preset();
const Preset& preset = m_presets->get_edited_preset();
(preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true);
@ -2302,11 +2302,14 @@ void Tab::load_current_preset()
m_undo_to_sys_btn->Enable(!preset.is_default);
#if 0
// use CallAfter because some field triggers schedule on_change calls using CallAfter,
// and we don't want them to be called after this update_dirty() as they would mark the
// preset dirty again
// (not sure this is true anymore now that update_dirty is idempotent)
wxTheApp->CallAfter([this]{
wxTheApp->CallAfter([this]
#endif
{
// checking out if this Tab exists till this moment
if (!wxGetApp().checked_tab(this))
return;
@ -2354,7 +2357,10 @@ void Tab::load_current_preset()
init_options_list();
update_visibility();
update_changed_ui();
});
}
#if 0
);
#endif
}
//Regerenerate content of the page tree.
@ -2428,7 +2434,6 @@ void Tab::select_preset(std::string preset_name)
bool print_tab = m_presets->type() == Preset::TYPE_PRINT || m_presets->type() == Preset::TYPE_SLA_PRINT;
bool printer_tab = m_presets->type() == Preset::TYPE_PRINTER;
bool canceled = false;
// m_reload_dependent_tabs = {};
m_dependent_tabs = {};
if (current_dirty && !may_discard_current_dirty_preset()) {
canceled = true;

View File

@ -185,7 +185,6 @@ protected:
bool m_disable_tree_sel_changed_event;
bool m_show_incompatible_presets;
std::vector<std::string> m_reload_dependent_tabs = {};
std::vector<Preset::Type> m_dependent_tabs = {};
enum OptStatus { osSystemValue = 1, osInitValue = 2 };
std::map<std::string, int> m_options_list;
@ -265,7 +264,6 @@ public:
DynamicPrintConfig* get_config() { return m_config; }
PresetCollection* get_presets() { return m_presets; }
std::vector<std::string> get_dependent_tabs() { return m_reload_dependent_tabs; }
size_t get_selected_preset_item() { return m_selected_preset_item; }
void on_value_change(const std::string& opt_key, const boost::any& value);

View File

@ -21,6 +21,8 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <stdio.h>
class OpenGLVersionCheck
{
public:
@ -44,8 +46,8 @@ public:
if (RegisterClass(&wc)) {
HWND hwnd = CreateWindowW(wc.lpszClassName, L"slic3r_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this);
if (hwnd) {
this->message_pump_exit = false;
while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! this->message_pump_exit)
message_pump_exit = false;
while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit)
DispatchMessage(&msg);
}
}
@ -55,13 +57,18 @@ public:
void unload_opengl_dll()
{
if (this->hOpenGL) {
FreeLibrary(this->hOpenGL);
BOOL released = FreeLibrary(this->hOpenGL);
if (released)
printf("System OpenGL library released\n");
else
printf("System OpenGL library NOT released\n");
this->hOpenGL = nullptr;
}
}
bool is_version_greater_or_equal_to(unsigned int major, unsigned int minor) const
{
// printf("is_version_greater_or_equal_to, version: %s\n", version.c_str());
std::vector<std::string> tokens;
boost::split(tokens, version, boost::is_any_of(" "), boost::token_compress_on);
if (tokens.empty())
@ -76,6 +83,7 @@ public:
gl_major = ::atoi(numbers[0].c_str());
if (numbers.size() > 1)
gl_minor = ::atoi(numbers[1].c_str());
// printf("Major: %d, minor: %d\n", gl_major, gl_minor);
if (gl_major < major)
return false;
else if (gl_major > major)
@ -85,7 +93,7 @@ public:
}
protected:
bool message_pump_exit = false;
static bool message_pump_exit;
void check(HWND hWnd)
{
@ -131,17 +139,18 @@ protected:
};
HDC ourWindowHandleToDeviceContext = ::GetDC(hWnd);
// Gdi32.dll
// Gdi32.dll
int letWindowsChooseThisPixelFormat = ::ChoosePixelFormat(ourWindowHandleToDeviceContext, &pfd);
// Gdi32.dll
SetPixelFormat(ourWindowHandleToDeviceContext,letWindowsChooseThisPixelFormat, &pfd);
// Opengl32.dll
// Gdi32.dll
SetPixelFormat(ourWindowHandleToDeviceContext, letWindowsChooseThisPixelFormat, &pfd);
// Opengl32.dll
HGLRC glcontext = wglCreateContext(ourWindowHandleToDeviceContext);
wglMakeCurrent(ourWindowHandleToDeviceContext, glcontext);
// Opengl32.dll
const char *data = (const char*)glGetString(GL_VERSION);
if (data != nullptr)
this->version = data;
// printf("check -version: %s\n", version.c_str());
data = (const char*)glGetString(0x8B8C); // GL_SHADING_LANGUAGE_VERSION
if (data != nullptr)
this->glsl_version = data;
@ -153,6 +162,7 @@ protected:
this->renderer = data;
// Opengl32.dll
wglDeleteContext(glcontext);
::ReleaseDC(hWnd, ourWindowHandleToDeviceContext);
this->success = true;
}
@ -166,15 +176,19 @@ protected:
OpenGLVersionCheck *ogl_data = reinterpret_cast<OpenGLVersionCheck*>(pCreate->lpCreateParams);
ogl_data->check(hWnd);
DestroyWindow(hWnd);
ogl_data->message_pump_exit = true;
return 0;
}
case WM_NCDESTROY:
message_pump_exit = true;
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
};
bool OpenGLVersionCheck::message_pump_exit = false;
extern "C" {
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
Slic3rMainFunc slic3r_main = nullptr;
@ -190,6 +204,16 @@ int wmain(int argc, wchar_t **argv)
{
#endif
std::vector<wchar_t*> argv_extended;
argv_extended.emplace_back(argv[0]);
#ifdef SLIC3R_WRAPPER_GUI
std::wstring cmd_gui = L"--gui";
argv_extended.emplace_back(const_cast<wchar_t*>(cmd_gui.data()));
#endif
for (int i = 1; i < argc; ++i)
argv_extended.emplace_back(argv[i]);
argv_extended.emplace_back(nullptr);
OpenGLVersionCheck opengl_version_check;
bool load_mesa = ! opengl_version_check.load_opengl_dll() || ! opengl_version_check.is_version_greater_or_equal_to(2, 0);
@ -213,7 +237,8 @@ int wmain(int argc, wchar_t **argv)
HINSTANCE hInstance_OpenGL = LoadLibraryExW(path_to_mesa, nullptr, 0);
if (hInstance_OpenGL == nullptr) {
printf("MESA OpenGL library was not loaded\n");
}
} else
printf("MESA OpenGL library was loaded sucessfully\n");
}
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
@ -239,15 +264,6 @@ int wmain(int argc, wchar_t **argv)
printf("could not locate the function slic3r_main in slic3r.dll\n");
return -1;
}
std::vector<wchar_t*> argv_extended;
argv_extended.emplace_back(argv[0]);
#ifdef SLIC3R_WRAPPER_GUI
std::wstring cmd_gui = L"--gui";
argv_extended.emplace_back(const_cast<wchar_t*>(cmd_gui.data()));
#endif
for (int i = 1; i < argc; ++ i)
argv_extended.emplace_back(argv[i]);
argv_extended.emplace_back(nullptr);
return slic3r_main(argc, argv_extended.data());
// argc minus the trailing nullptr of the argv
return slic3r_main(argv_extended.size() - 1, argv_extended.data());
}