Fix: generic filaments not showing on list (#13037)

This commit is contained in:
Sabriel-Koh
2026-04-06 14:27:49 +08:00
committed by GitHub
parent ff77db77db
commit b79d3e1701
4 changed files with 90 additions and 63 deletions

View File

@@ -653,7 +653,7 @@ public:
int match_quality = -1; int match_quality = -1;
for (; i < n; ++i) for (; i < n; ++i)
// Since we use the filament selection from Wizard, it's needed to control the preset visibility too // Since we use the filament selection from Wizard, it's needed to control the preset visibility too
if (m_presets[i].is_compatible) { if (m_presets[i].is_compatible && m_presets[i].is_visible) {
int this_match_quality = prefered_condition(m_presets[i]); int this_match_quality = prefered_condition(m_presets[i]);
if (this_match_quality > match_quality) { if (this_match_quality > match_quality) {
if (match_quality == std::numeric_limits<int>::max()) if (match_quality == std::numeric_limits<int>::max())

View File

@@ -734,10 +734,17 @@ void PresetBundle::reset_project_embedded_presets()
Preset& current_printer = this->printers.get_selected_preset(); Preset& current_printer = this->printers.get_selected_preset();
const std::vector<std::string> &prefered_filament_profiles = current_printer.config.option<ConfigOptionStrings>("default_filament_profile")->values; const std::vector<std::string> &prefered_filament_profiles = current_printer.config.option<ConfigOptionStrings>("default_filament_profile")->values;
const std::string prefered_filament_profile = prefered_filament_profiles.empty() ? std::string() : prefered_filament_profiles.front(); const std::string prefered_filament_profile = prefered_filament_profiles.empty() ? std::string() : prefered_filament_profiles.front();
if (!prefered_filament_profile.empty()) if (!prefered_filament_profile.empty()) {
filament_presets[i] = prefered_filament_profile; // Check if preferred filament exists and is visible
else const Preset* preferred_preset = this->filaments.find_preset(prefered_filament_profile, false);
filament_presets[i] = this->filaments.first_visible().name; if (preferred_preset && preferred_preset->is_visible) {
filament_presets[i] = prefered_filament_profile;
} else {
// Fall back to first visible filament
filament_presets[i] = this->filaments.first_visible().name;
}
} else
filament_presets[i] = this->filaments.first_visible().name;
} }
} }
} }
@@ -1970,8 +1977,14 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
initial_print_profile_name = prefered_print_profile; initial_print_profile_name = prefered_print_profile;
const std::vector<std::string>& prefered_filament_profiles = preferred_printer->config.option<ConfigOptionStrings>("default_filament_profile")->values; const std::vector<std::string>& prefered_filament_profiles = preferred_printer->config.option<ConfigOptionStrings>("default_filament_profile")->values;
if ((!initial_filament_profile_name.compare("Default Filament")) && (prefered_filament_profiles.size() > 0)) if ((!initial_filament_profile_name.compare("Default Filament")) && (prefered_filament_profiles.size() > 0)) {
initial_filament_profile_name = prefered_filament_profiles[0]; // Check if preferred filament is visible
const Preset* preferred_preset = this->filaments.find_preset(prefered_filament_profiles[0], false);
if (preferred_preset && preferred_preset->is_visible) {
initial_filament_profile_name = prefered_filament_profiles[0];
}
// If not visible, keep the default "Default Filament" which will be resolved later
}
} }
// Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found. // Selects the profile, leaves it to -1 if the initial profile name is empty or if it was not found.
@@ -4452,7 +4465,7 @@ void PresetBundle::update_compatible(PresetSelectCompatibleType select_other_pri
int operator()(const Preset &preset) const int operator()(const Preset &preset) const
{ {
// Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile. // Don't match any properties of the "-- default --" profile or the external profiles when switching printer profile.
if (preset.is_default || preset.is_external) if (preset.is_default || preset.is_external || !preset.is_visible)
return 0; return 0;
if (! m_prefered_alias.empty() && m_prefered_alias == preset.alias) if (! m_prefered_alias.empty() && m_prefered_alias == preset.alias)
// Matching an alias, always take this preset with priority. // Matching an alias, always take this preset with priority.

View File

@@ -7,6 +7,7 @@
#include <string.h> #include <string.h>
#include "I18N.hpp" #include "I18N.hpp"
#include "libslic3r/AppConfig.hpp" #include "libslic3r/AppConfig.hpp"
#include "libslic3r/Config.hpp"
#include "libslic3r/PresetBundle.hpp" #include "libslic3r/PresetBundle.hpp"
#include "slic3r/GUI/wxExtensions.hpp" #include "slic3r/GUI/wxExtensions.hpp"
#include "slic3r/GUI/GUI_App.hpp" #include "slic3r/GUI/GUI_App.hpp"
@@ -438,6 +439,50 @@ void GuideFrame::OnScriptMessage(wxWebViewEvent &evt)
wxString s2 = OneSelect["model"]; wxString s2 = OneSelect["model"];
if (s1.compare(s2) == 0) { if (s1.compare(s2) == 0) {
m_ProfileJson["model"][m]["nozzle_selected"] = m_ProfileJson["model"][m]["nozzle_diameter"]; m_ProfileJson["model"][m]["nozzle_selected"] = m_ProfileJson["model"][m]["nozzle_diameter"];
// Automatically select default materials for this printer model
// This mirrors the behavior of the old ConfigWizard::select_default_materials_for_printer_model()
if (TmpModel.contains("materials") && !TmpModel["materials"].is_null()) {
std::string materials_str;
// Handle both string and JSON array formats for materials
if (TmpModel["materials"].is_string()) {
materials_str = TmpModel["materials"].get<std::string>();
} else if (TmpModel["materials"].is_array()) {
// Convert JSON array to semicolon-separated string for unescape_strings_cstyle
for (const auto& material : TmpModel["materials"]) {
if (!materials_str.empty()) materials_str += ";";
materials_str += material.get<std::string>();
}
} else {
materials_str = "";
}
boost::trim(materials_str);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Processing default_materials for printer: " << s1.ToStdString() << " - materials: " << materials_str;
// Use the same parsing logic as ConfigWizard::select_default_materials_for_printer_model()
// This calls unescape_strings_cstyle() just like Preset.cpp:298 does
std::vector<std::string> materials;
if (Slic3r::unescape_strings_cstyle(materials_str, materials)) {
for (const std::string& material : materials) {
if (!material.empty()) {
// Mark this filament as selected if it exists in our filament list
// This mirrors appconfig_new.set(section, material, "true") from ConfigWizard.cpp:2150
if (m_ProfileJson["filament"].contains(material)) {
m_ProfileJson["filament"][material]["selected"] = 1;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Automatically selected default filament: " << material;
} else {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Default filament '" << material << "' not found in available filaments for printer: " << s1.ToStdString();
}
}
}
} else {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Malformed default_materials field: " << materials_str << " for printer: " << s1.ToStdString();
}
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " No default_materials defined for printer: " << s1.ToStdString();
}
break; break;
} }
} }
@@ -838,45 +883,8 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
// Not switch filament // Not switch filament
//get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament); //get_first_added_material_preset(AppConfig::SECTION_FILAMENTS, first_added_filament);
// For each @System filament, check if a vendor-specific override exists
// in the loaded profiles. If so, replace the @System variant with the
// override (e.g. replace "Generic ABS @System" with BBL "Generic ABS").
// When printers from the default bundle are also selected, keep @System
// too since those printers need it.
static const std::string system_suffix = " @System";
auto it_default = enabled_vendors.find(PresetBundle::ORCA_DEFAULT_BUNDLE);
bool has_default_bundle_printer = it_default != enabled_vendors.end() && !it_default->second.empty();
bool has_filament_profiles = m_ProfileJson.contains("filament");
// Check if any non-default vendor has selected printers
bool has_vendor_printer = false;
for (const auto& [vendor, models] : enabled_vendors) {
if (vendor != PresetBundle::ORCA_DEFAULT_BUNDLE && !models.empty()) {
has_vendor_printer = true;
break;
}
}
std::map<std::string, std::string> supplemented_filaments;
for (const auto& [name, value] : enabled_filaments) {
if (name.size() > system_suffix.size() &&
name.compare(name.size() - system_suffix.size(), system_suffix.size(), system_suffix) == 0) {
std::string short_name = name.substr(0, name.size() - system_suffix.size());
if (has_vendor_printer && has_filament_profiles && m_ProfileJson["filament"].contains(short_name)) {
supplemented_filaments[short_name] = value;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Replacing @System filament: '" << name << "' -> '" << short_name << "'";
if (has_default_bundle_printer) {
supplemented_filaments[name] = value;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Also keeping '" << name << "' for default bundle printers";
}
continue;
}
}
supplemented_filaments[name] = value;
}
//update the app_config //update the app_config
app_config->set_section(AppConfig::SECTION_FILAMENTS, supplemented_filaments); app_config->set_section(AppConfig::SECTION_FILAMENTS, enabled_filaments);
app_config->set_vendors(m_appconfig_new); app_config->set_vendors(m_appconfig_new);
if (check_unsaved_preset_changes) if (check_unsaved_preset_changes)
@@ -886,10 +894,10 @@ bool GuideFrame::apply_config(AppConfig *app_config, PresetBundle *preset_bundle
// If the active filament is not in the wizard-selected filaments, switch to the first // If the active filament is not in the wizard-selected filaments, switch to the first
// compatible wizard-selected filament. This handles the first-run case where load_presets // compatible wizard-selected filament. This handles the first-run case where load_presets
// falls back to "Generic PLA" even though the user selected a different filament. // falls back to "Generic PLA" even though the user selected a different filament.
bool active_filament_selected = supplemented_filaments.empty() bool active_filament_selected = enabled_filaments.empty()
|| supplemented_filaments.count(preset_bundle->filament_presets.front()) > 0; || enabled_filaments.count(preset_bundle->filament_presets.front()) > 0;
if (!active_filament_selected) { if (!active_filament_selected) {
for (const auto& [filament_name, _] : supplemented_filaments) { for (const auto& [filament_name, _] : enabled_filaments) {
const Preset* preset = preset_bundle->filaments.find_preset(filament_name); const Preset* preset = preset_bundle->filaments.find_preset(filament_name);
if (preset && preset->is_visible && preset->is_compatible) { if (preset && preset->is_visible && preset->is_compatible) {
preset_bundle->filaments.select_preset_by_name(filament_name, true); preset_bundle->filaments.select_preset_by_name(filament_name, true);
@@ -1124,21 +1132,23 @@ int GuideFrame::LoadProfileData()
return 0; return 0;
} }
//sync to web wxGetApp().CallAfter([this] {
std::string strAll = m_ProfileJson.dump(-1, ' ', false, json::error_handler_t::ignore); if (!m_destroy) {
//sync to appconfig first to populate current selections
SaveProfileData();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished, json contents: " << std::endl << strAll; //sync to web after selections are populated
json m_Res = json::object(); std::string strAll = m_ProfileJson.dump(-1, ' ', false, json::error_handler_t::ignore);
m_Res["command"] = "userguide_profile_load_finish";
m_Res["sequence_id"] = "10001";
wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true));
if (!m_destroy)
wxGetApp().CallAfter([this, strJS] { RunScript(strJS); });
//sync to appconfig BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ", finished, json contents: " << std::endl << strAll;
if (!m_destroy) json m_Res = json::object();
wxGetApp().CallAfter([this] { SaveProfileData(); }); m_Res["command"] = "userguide_profile_load_finish";
m_Res["sequence_id"] = "10001";
wxString strJS = wxString::Format("HandleStudio(%s)", m_Res.dump(-1, ' ', true));
RunScript(strJS);
}
});
} catch (std::exception& e) { } catch (std::exception& e) {
// wxLogMessage("GUIDE: load_profile_error %s ", e.what()); // wxLogMessage("GUIDE: load_profile_error %s ", e.what());
// wxMessageBox(e.what(), "", MB_OK); // wxMessageBox(e.what(), "", MB_OK);

View File

@@ -420,7 +420,9 @@ int DropDown::hoverIndex()
{ {
if (hover_item < 0) if (hover_item < 0)
return -1; return -1;
if (count == items.size()) // BUG FIX: Can't take the shortcut if subDropDown exists (which means there are groups)
// because we need to detect group headers which return negative indices
if (count == items.size() && subDropDown == nullptr)
return hover_item; return hover_item;
int index = -1; int index = -1;
std::set<wxString> groups; std::set<wxString> groups;
@@ -448,7 +450,9 @@ int DropDown::selectedItem()
{ {
if (selection < 0) if (selection < 0)
return -1; return -1;
if (count == items.size()) // BUG FIX: Can't take the shortcut if subDropDown exists (which means there are groups)
// because the visual position differs from the actual item index when groups are shown
if (count == items.size() && subDropDown == nullptr)
return selection; return selection;
auto & sel = items[selection]; auto & sel = items[selection];
if (group.IsEmpty() ? !sel.group.IsEmpty() : sel.group != group) if (group.IsEmpty() ? !sel.group.IsEmpty() : sel.group != group)