Getting rid of AppController.

This commit is contained in:
tamasmeszaros 2018-11-12 14:46:12 +01:00
parent 33eade5300
commit 98a640ea06
13 changed files with 857 additions and 1941 deletions

View file

@ -1,364 +0,0 @@
#include "AppController.hpp"
#include <slic3r/GUI/GUI.hpp>
#include <future>
#include <chrono>
#include <sstream>
#include <cstdarg>
#include <thread>
#include <unordered_map>
#include <PrintConfig.hpp>
#include <Print.hpp>
#include <PrintExport.hpp>
#include <Geometry.hpp>
#include <Model.hpp>
#include <ModelArrange.hpp>
namespace Slic3r {
class AppControllerGui::PriData {
public:
std::mutex m;
std::thread::id ui_thread;
inline explicit PriData(std::thread::id uit): ui_thread(uit) {}
};
AppControllerGui::AppControllerGui()
:m_pri_data(new PriData(std::this_thread::get_id())) {}
AppControllerGui::~AppControllerGui() {
m_pri_data.reset();
}
bool AppControllerGui::is_main_thread() const
{
return m_pri_data->ui_thread == std::this_thread::get_id();
}
// namespace GUI {
// PresetBundle* get_preset_bundle();
// }
static const PrintObjectStep STEP_SLICE = posSlice;
static const PrintObjectStep STEP_PERIMETERS = posPerimeters;
static const PrintObjectStep STEP_PREPARE_INFILL = posPrepareInfill;
static const PrintObjectStep STEP_INFILL = posInfill;
static const PrintObjectStep STEP_SUPPORTMATERIAL = posSupportMaterial;
static const PrintStep STEP_SKIRT = psSkirt;
static const PrintStep STEP_BRIM = psBrim;
static const PrintStep STEP_WIPE_TOWER = psWipeTower;
ProgresIndicatorPtr AppControllerGui::global_progress_indicator() {
ProgresIndicatorPtr ret;
m_pri_data->m.lock();
ret = m_global_progressind;
m_pri_data->m.unlock();
return ret;
}
void AppControllerGui::global_progress_indicator(ProgresIndicatorPtr gpri)
{
m_pri_data->m.lock();
m_global_progressind = gpri;
m_pri_data->m.unlock();
}
PrintController::PngExportData
PrintController::query_png_export_data(const DynamicPrintConfig& conf)
{
PngExportData ret;
auto c = GUI::get_appctl();
auto zippath = c->query_destination_path("Output zip file", "*.zip",
"export-png",
"out");
ret.zippath = zippath;
ret.width_mm = conf.opt_float("display_width");
ret.height_mm = conf.opt_float("display_height");
ret.width_px = conf.opt_int("display_pixels_x");
ret.height_px = conf.opt_int("display_pixels_y");
auto opt_corr = conf.opt<ConfigOptionFloats>("printer_correction");
if(opt_corr) {
ret.corr_x = opt_corr->values[0];
ret.corr_y = opt_corr->values[1];
ret.corr_z = opt_corr->values[2];
}
ret.exp_time_first_s = conf.opt_float("initial_exposure_time");
ret.exp_time_s = conf.opt_float("exposure_time");
return ret;
}
void PrintController::slice(ProgresIndicatorPtr pri)
{
m_print->set_status_callback([pri](int st, const std::string& msg){
pri->update(unsigned(st), msg);
});
m_print->process();
}
void PrintController::slice()
{
auto ctl = GUI::get_appctl();
auto pri = ctl->global_progress_indicator();
if(!pri) pri = ctl->create_progress_indicator(100, L("Slicing"));
slice(pri);
}
template<> class LayerWriter<Zipper> {
Zipper m_zip;
public:
inline LayerWriter(const std::string& zipfile_path): m_zip(zipfile_path) {}
inline void next_entry(const std::string& fname) { m_zip.next_entry(fname); }
inline std::string get_name() const { return m_zip.get_name(); }
template<class T> inline LayerWriter& operator<<(const T& arg) {
m_zip.stream() << arg; return *this;
}
inline void close() { m_zip.close(); }
};
void PrintController::slice_to_png()
{
// using Pointf3 = Vec3d;
// auto ctl = GUI::get_appctl();
// auto presetbundle = GUI::wxGetApp().preset_bundle;
// assert(presetbundle);
// // FIXME: this crashes in command line mode
// auto pt = presetbundle->printers.get_selected_preset().printer_technology();
// if(pt != ptSLA) {
// ctl->report_issue(IssueType::ERR, L("Printer technology is not SLA!"),
// L("Error"));
// return;
// }
// auto conf = presetbundle->full_config();
// conf.validate();
// auto exd = query_png_export_data(conf);
// if(exd.zippath.empty()) return;
// Print *print = m_print;
// try {
// print->apply_config(conf);
// print->validate();
// } catch(std::exception& e) {
// ctl->report_issue(IssueType::ERR, e.what(), "Error");
// return;
// }
// // TODO: copy the model and work with the copy only
// bool correction = false;
// if(exd.corr_x != 1.0 || exd.corr_y != 1.0 || exd.corr_z != 1.0) {
// correction = true;
//// print->invalidate_all_steps();
//// for(auto po : print->objects) {
//// po->model_object()->scale(
//// Pointf3(exd.corr_x, exd.corr_y, exd.corr_z)
//// );
//// po->model_object()->invalidate_bounding_box();
//// po->reload_model_instances();
//// po->invalidate_all_steps();
//// }
// }
// // Turn back the correction scaling on the model.
// auto scale_back = [this, print, correction, exd]() {
// if(correction) { // scale the model back
//// print->invalidate_all_steps();
//// for(auto po : print->objects) {
//// po->model_object()->scale(
//// Pointf3(1.0/exd.corr_x, 1.0/exd.corr_y, 1.0/exd.corr_z)
//// );
//// po->model_object()->invalidate_bounding_box();
//// po->reload_model_instances();
//// po->invalidate_all_steps();
//// }
// }
// };
// auto print_bb = print->bounding_box();
// Vec2d punsc = unscale(print_bb.size());
// // If the print does not fit into the print area we should cry about it.
// if(px(punsc) > exd.width_mm || py(punsc) > exd.height_mm) {
// std::stringstream ss;
// ss << L("Print will not fit and will be truncated!") << "\n"
// << L("Width needed: ") << px(punsc) << " mm\n"
// << L("Height needed: ") << py(punsc) << " mm\n";
// if(!ctl->report_issue(IssueType::WARN_Q, ss.str(), L("Warning"))) {
// scale_back();
// return;
// }
// }
// auto pri = ctl->create_progress_indicator(
// 200, L("Slicing to zipped png files..."));
// pri->on_cancel([&print](){ print->cancel(); });
// try {
// pri->update(0, L("Slicing..."));
// slice(pri);
// } catch (std::exception& e) {
// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
// scale_back();
// if(print->canceled()) print->restart();
// return;
// }
// auto initstate = unsigned(pri->state());
// print->set_status_callback([pri, initstate](int st, const std::string& msg)
// {
// pri->update(initstate + unsigned(st), msg);
// });
// try {
// print_to<FilePrinterFormat::PNG, Zipper>( *print, exd.zippath,
// exd.width_mm, exd.height_mm,
// exd.width_px, exd.height_px,
// exd.exp_time_s, exd.exp_time_first_s);
// } catch (std::exception& e) {
// ctl->report_issue(IssueType::ERR, e.what(), L("Exception occurred"));
// }
// scale_back();
// if(print->canceled()) print->restart();
// print->set_status_default();
}
const PrintConfig &PrintController::config() const
{
return m_print->config();
}
void ProgressIndicator::message_fmt(
const std::string &fmtstr, ...) {
std::stringstream ss;
va_list args;
va_start(args, fmtstr);
auto fmt = fmtstr.begin();
while (*fmt != '\0') {
if (*fmt == 'd') {
int i = va_arg(args, int);
ss << i << '\n';
} else if (*fmt == 'c') {
// note automatic conversion to integral type
int c = va_arg(args, int);
ss << static_cast<char>(c) << '\n';
} else if (*fmt == 'f') {
double d = va_arg(args, double);
ss << d << '\n';
}
++fmt;
}
va_end(args);
message(ss.str());
}
void AppController::arrange_model()
{
using Coord = libnest2d::TCoord<libnest2d::PointImpl>;
auto ctl = GUI::get_appctl();
if(m_arranging.load()) return;
// to prevent UI reentrancies
m_arranging.store(true);
unsigned count = 0;
for(auto obj : m_model->objects) count += obj->instances.size();
auto pind = ctl->global_progress_indicator();
float pmax = 1.0;
if(pind) {
pmax = pind->max();
// Set the range of the progress to the object count
pind->max(count);
pind->on_cancel([this](){
m_arranging.store(false);
});
}
auto dist = print_ctl()->config().min_object_distance();
// Create the arranger config
auto min_obj_distance = static_cast<Coord>(dist/SCALING_FACTOR);
auto& bedpoints = print_ctl()->config().bed_shape.values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints)
bed.append(Point::new_scale(v(0), v(1)));
if(pind) pind->update(0, L("Arranging objects..."));
try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI
hint.type = arr::BedShapeType::WHO_KNOWS;
arr::arrange(*m_model,
min_obj_distance,
bed,
hint,
false, // create many piles not just one pile
[this, pind, &ctl, count](unsigned rem) {
if(pind)
pind->update(count - rem, L("Arranging objects..."));
ctl->process_events();
}, [this] () { return !m_arranging.load(); });
} catch(std::exception& e) {
std::cerr << e.what() << std::endl;
ctl->report_issue(IssueType::ERR,
L("Could not arrange model objects! "
"Some geometries may be invalid."),
L("Exception occurred"));
}
// Restore previous max value
if(pind) {
pind->max(pmax);
pind->update(0, m_arranging.load() ? L("Arranging done.") :
L("Arranging canceled."));
pind->on_cancel(/*remove cancel function*/);
}
m_arranging.store(false);
}
}

View file

@ -1,414 +0,0 @@
#ifndef APPCONTROLLER_HPP
#define APPCONTROLLER_HPP
#include <string>
#include <vector>
#include <memory>
#include <atomic>
#include <iostream>
#include "GUI/ProgressIndicator.hpp"
#include <PrintConfig.hpp>
namespace Slic3r {
class Model;
class Print;
class PrintObject;
class PrintConfig;
class ProgressStatusBar;
class DynamicPrintConfig;
/// A Progress indicator object smart pointer
using ProgresIndicatorPtr = std::shared_ptr<ProgressIndicator>;
using FilePath = std::string;
using FilePathList = std::vector<FilePath>;
/// Common runtime issue types
enum class IssueType {
INFO,
WARN,
WARN_Q, // Warning with a question to continue
ERR,
FATAL
};
/**
* @brief A boilerplate class for creating application logic. It should provide
* features as issue reporting and progress indication, etc...
*
* The lower lever UI independent classes can be manipulated with a subclass
* of this controller class. We can also catch any exceptions that lower level
* methods could throw and display appropriate errors and warnings.
*
* Note that the outer and the inner interface of this class is free from any
* UI toolkit dependencies. We can implement it with any UI framework or make it
* a cli client.
*/
class AppControllerBase {
public:
using Ptr = std::shared_ptr<AppControllerBase>;
inline virtual ~AppControllerBase() {}
/**
* @brief Query some paths from the user.
*
* It should display a file chooser dialog in case of a UI application.
* @param title Title of a possible query dialog.
* @param extensions Recognized file extensions.
* @return Returns a list of paths chosen by the user.
*/
virtual FilePathList query_destination_paths(
const std::string& title,
const std::string& extensions,
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Same as query_destination_paths but works for directories only.
*/
virtual FilePathList query_destination_dirs(
const std::string& title,
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Same as query_destination_paths but returns only one path.
*/
virtual FilePath query_destination_path(
const std::string& title,
const std::string& extensions,
const std::string& functionid = "",
const std::string& hint = "") const = 0;
/**
* @brief Report an issue to the user be it fatal or recoverable.
*
* In a UI app this should display some message dialog.
*
* @param issuetype The type of the runtime issue.
* @param description A somewhat longer description of the issue.
* @param brief A very brief description. Can be used for message dialog
* title.
*/
virtual bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief) = 0;
/**
* @brief Return the global progress indicator for the current controller.
* Can be empty as well.
*
* Only one thread should use the global indicator at a time.
*/
virtual ProgresIndicatorPtr global_progress_indicator() = 0;
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) = 0;
/**
* @brief A predicate telling the caller whether it is the thread that
* created the AppConroller object itself. This probably means that the
* execution is in the UI thread. Otherwise it returns false meaning that
* some worker thread called this function.
* @return Return true for the same caller thread that created this
* object and false for every other.
*/
virtual bool is_main_thread() const = 0;
/**
* @brief The frontend supports asynch execution.
*
* A Graphic UI will support this, a CLI may not. This can be used in
* subclass methods to decide whether to start threads for block free UI.
*
* Note that even a progress indicator's update called regularly can solve
* the blocking UI problem in some cases even when an event loop is present.
* This is how wxWidgets gauge work but creating a separate thread will make
* the UI even more fluent.
*
* @return true if a job or method can be executed asynchronously, false
* otherwise.
*/
virtual bool supports_asynch() const = 0;
virtual void process_events() = 0;
/**
* @brief Create a new progress indicator and return a smart pointer to it.
* @param statenum The number of states for the given procedure.
* @param title The title of the procedure.
* @param firstmsg The message for the first subtask to be displayed.
* @return Smart pointer to the created object.
*/
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg = "") const = 0;
};
/**
* @brief Implementation of AppControllerBase for the GUI app
*/
class AppControllerGui: public AppControllerBase {
private:
class PriData; // Some structure to store progress indication data
// Pimpl data for thread safe progress indication features
std::unique_ptr<PriData> m_pri_data;
public:
AppControllerGui();
virtual ~AppControllerGui();
virtual FilePathList query_destination_paths(
const std::string& title,
const std::string& extensions,
const std::string& functionid,
const std::string& hint) const override;
virtual FilePathList query_destination_dirs(
const std::string& /*title*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePath query_destination_path(
const std::string& title,
const std::string& extensions,
const std::string& functionid,
const std::string& hint) const override;
virtual bool report_issue(IssueType issuetype,
const std::string& description,
const std::string& brief = std::string()) override;
virtual ProgresIndicatorPtr global_progress_indicator() override;
virtual void global_progress_indicator(ProgresIndicatorPtr gpri) override;
virtual bool is_main_thread() const override;
virtual bool supports_asynch() const override;
virtual void process_events() override;
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg) const override;
protected:
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
ProgresIndicatorPtr m_global_progressind;
};
class AppControllerCli: public AppControllerBase {
class CliProgress : public ProgressIndicator {
std::string m_msg, m_title;
public:
virtual void message(const std::string& msg) override {
m_msg = msg;
}
virtual void title(const std::string& title) override {
m_title = title;
}
};
public:
AppControllerCli() {
std::cout << "Cli AppController ready..." << std::endl;
m_global_progressind = std::make_shared<CliProgress>();
}
virtual ~AppControllerCli() {}
virtual FilePathList query_destination_paths(
const std::string& /*title*/,
const std::string& /*extensions*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePathList query_destination_dirs(
const std::string& /*title*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return {}; }
virtual FilePath query_destination_path(
const std::string& /*title*/,
const std::string& /*extensions*/,
const std::string& /*functionid*/,
const std::string& /*hint*/) const override { return "out.zip"; }
virtual bool report_issue(IssueType /*issuetype*/,
const std::string& description,
const std::string& brief) override {
std::cerr << brief << ": " << description << std::endl;
return true;
}
virtual ProgresIndicatorPtr global_progress_indicator() override {
return m_global_progressind;
}
virtual void global_progress_indicator(ProgresIndicatorPtr) override {}
virtual bool is_main_thread() const override { return true; }
virtual bool supports_asynch() const override { return false; }
virtual void process_events() override {}
virtual ProgresIndicatorPtr create_progress_indicator(
unsigned /*statenum*/,
const std::string& /*title*/,
const std::string& /*firstmsg*/) const override {
return std::make_shared<CliProgress>();
}
protected:
// This is a global progress indicator placeholder. In the Slic3r UI it can
// contain the progress indicator on the statusbar.
ProgresIndicatorPtr m_global_progressind;
};
class Zipper {
struct Impl;
std::unique_ptr<Impl> m_impl;
public:
Zipper(const std::string& zipfilepath);
~Zipper();
void next_entry(const std::string& fname);
std::string get_name() const;
std::ostream& stream();
void close();
};
/**
* @brief Implementation of the printing logic.
*/
class PrintController {
Print *m_print = nullptr;
std::function<void()> m_rempools;
protected:
// Data structure with the png export input data
struct PngExportData {
std::string zippath; // output zip file
unsigned long width_px = 1440; // resolution - rows
unsigned long height_px = 2560; // resolution columns
double width_mm = 68.0, height_mm = 120.0; // dimensions in mm
double exp_time_first_s = 35.0; // first exposure time
double exp_time_s = 8.0; // global exposure time
double corr_x = 1.0; // offsetting in x
double corr_y = 1.0; // offsetting in y
double corr_z = 1.0; // offsetting in y
};
// Should display a dialog with the input fields for printing to png
PngExportData query_png_export_data(const DynamicPrintConfig&);
// The previous export data, to pre-populate the dialog
PngExportData m_prev_expdata;
void slice(ProgresIndicatorPtr pri);
public:
// Must be public for perl to use it
explicit inline PrintController(Print *print): m_print(print) {}
PrintController(const PrintController&) = delete;
PrintController(PrintController&&) = delete;
using Ptr = std::unique_ptr<PrintController>;
inline static Ptr create(Print *print) {
return PrintController::Ptr( new PrintController(print) );
}
/**
* @brief Slice the loaded print scene.
*/
void slice();
/**
* @brief Slice the print into zipped png files.
*/
void slice_to_png();
const PrintConfig& config() const;
};
/**
* @brief Top level controller.
*/
class AppController {
Model *m_model = nullptr;
PrintController::Ptr printctl;
std::atomic<bool> m_arranging;
public:
/**
* @brief Get the print controller object.
*
* @return Return a raw pointer instead of a smart one for perl to be able
* to use this function and access the print controller.
*/
PrintController * print_ctl() { return printctl.get(); }
/**
* @brief Set a model object.
*
* @param model A raw pointer to the model object. This can be used from
* perl.
*/
void set_model(Model *model) { m_model = model; }
/**
* @brief Set the print object from perl.
*
* This will create a print controller that will then be accessible from
* perl.
* @param print A print object which can be a perl-ish extension as well.
*/
void set_print(Print *print) {
printctl = PrintController::create(print);
}
/**
* @brief Set up a global progress indicator.
*
* In perl we have a progress indicating status bar on the bottom of the
* window which is defined and created in perl. We can pass the ID-s of the
* gauge and the statusbar id and make a wrapper implementation of the
* ProgressIndicator interface so we can use this GUI widget from C++.
*
* This function should be called from perl.
*
* @param gauge_id The ID of the gague widget of the status bar.
* @param statusbar_id The ID of the status bar.
*/
void set_global_progress_indicator(ProgressStatusBar *prs);
void arrange_model();
};
}
#endif // APPCONTROLLER_HPP

View file

@ -1,340 +0,0 @@
#include "AppController.hpp"
#include <wx/stdstream.h>
#include <wx/wfstream.h>
#include <wx/zipstrm.h>
#include <thread>
#include <future>
#include <slic3r/GUI/GUI.hpp>
#include <slic3r/GUI/ProgressStatusBar.hpp>
#include <wx/app.h>
#include <wx/filedlg.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/gauge.h>
#include <wx/statusbr.h>
#include <wx/event.h>
// This source file implements the UI dependent methods of the AppControllers.
// It will be clear what is needed to be reimplemented in case of a UI framework
// change or a CLI client creation. In this particular case we use wxWidgets to
// implement everything.
namespace Slic3r {
bool AppControllerGui::supports_asynch() const
{
return true;
}
void AppControllerGui::process_events()
{
wxYieldIfNeeded();
}
FilePathList AppControllerGui::query_destination_paths(
const std::string &title,
const std::string &extensions,
const std::string &/*functionid*/,
const std::string& hint) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.SetFilename(hint);
FilePathList ret;
if(dlg.ShowModal() == wxID_OK) {
wxArrayString paths;
dlg.GetPaths(paths);
for(auto& p : paths) ret.push_back(p.ToStdString());
}
return ret;
}
FilePath AppControllerGui::query_destination_path(
const std::string &title,
const std::string &extensions,
const std::string &/*functionid*/,
const std::string& hint) const
{
wxFileDialog dlg(wxTheApp->GetTopWindow(), _(title) );
dlg.SetWildcard(extensions);
dlg.SetFilename(hint);
FilePath ret;
if(dlg.ShowModal() == wxID_OK) {
ret = FilePath(dlg.GetPath());
}
return ret;
}
bool AppControllerGui::report_issue(IssueType issuetype,
const std::string &description,
const std::string &brief)
{
auto icon = wxICON_INFORMATION;
auto style = wxOK|wxCENTRE;
switch(issuetype) {
case IssueType::INFO: break;
case IssueType::WARN: icon = wxICON_WARNING; break;
case IssueType::WARN_Q: icon = wxICON_WARNING; style |= wxCANCEL; break;
case IssueType::ERR:
case IssueType::FATAL: icon = wxICON_ERROR;
}
auto ret = wxMessageBox(_(description), _(brief), icon | style);
return ret != wxCANCEL;
}
wxDEFINE_EVENT(PROGRESS_STATUS_UPDATE_EVENT, wxCommandEvent);
struct Zipper::Impl {
wxFileName fpath;
wxFFileOutputStream zipfile;
wxZipOutputStream zipstream;
wxStdOutputStream pngstream;
Impl(const std::string& zipfile_path):
fpath(zipfile_path),
zipfile(zipfile_path),
zipstream(zipfile),
pngstream(zipstream)
{
if(!zipfile.IsOk())
throw std::runtime_error(L("Cannot create zip file."));
}
};
Zipper::Zipper(const std::string &zipfilepath)
{
m_impl.reset(new Impl(zipfilepath));
}
Zipper::~Zipper() {}
void Zipper::next_entry(const std::string &fname)
{
m_impl->zipstream.PutNextEntry(fname);
}
std::string Zipper::get_name() const
{
return m_impl->fpath.GetName().ToStdString();
}
std::ostream &Zipper::stream()
{
return m_impl->pngstream;
}
void Zipper::close()
{
m_impl->zipstream.Close();
m_impl->zipfile.Close();
}
namespace {
/*
* A simple thread safe progress dialog implementation that can be used from
* the main thread as well.
*/
class GuiProgressIndicator:
public ProgressIndicator, public wxEvtHandler {
wxProgressDialog m_gauge;
using Base = ProgressIndicator;
wxString m_message;
int m_range; wxString m_title;
bool m_is_asynch = false;
const int m_id = wxWindow::NewControlId();
// status update handler
void _state( wxCommandEvent& evt) {
unsigned st = evt.GetInt();
m_message = evt.GetString();
_state(st);
}
// Status update implementation
void _state( unsigned st) {
if(!m_gauge.IsShown()) m_gauge.ShowModal();
Base::state(st);
if(!m_gauge.Update(static_cast<int>(st), m_message)) {
cancel();
}
}
public:
/// Setting whether it will be used from the UI thread or some worker thread
inline void asynch(bool is) { m_is_asynch = is; }
/// Get the mode of parallel operation.
inline bool asynch() const { return m_is_asynch; }
inline GuiProgressIndicator(int range, const wxString& title,
const wxString& firstmsg) :
m_gauge(title, firstmsg, range, wxTheApp->GetTopWindow(),
wxPD_APP_MODAL | wxPD_AUTO_HIDE | wxPD_CAN_ABORT),
m_message(firstmsg),
m_range(range), m_title(title)
{
Base::max(static_cast<float>(range));
Base::states(static_cast<unsigned>(range));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&GuiProgressIndicator::_state,
this, m_id);
}
virtual void state(float val) override {
state(static_cast<unsigned>(val));
}
void state(unsigned st) {
// send status update event
if(m_is_asynch) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, m_id);
evt->SetInt(st);
evt->SetString(m_message);
wxQueueEvent(this, evt);
} else _state(st);
}
virtual void message(const std::string & msg) override {
m_message = _(msg);
}
virtual void messageFmt(const std::string& fmt, ...) {
va_list arglist;
va_start(arglist, fmt);
m_message = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & title) override {
m_title = _(title);
}
};
}
ProgresIndicatorPtr AppControllerGui::create_progress_indicator(
unsigned statenum,
const std::string& title,
const std::string& firstmsg) const
{
auto pri =
std::make_shared<GuiProgressIndicator>(statenum, title, firstmsg);
// We set up the mode of operation depending of the creator thread's
// identity
pri->asynch(!is_main_thread());
return pri;
}
namespace {
class Wrapper: public ProgressIndicator, public wxEvtHandler {
ProgressStatusBar *m_sbar;
using Base = ProgressIndicator;
wxString m_message;
AppControllerBase& m_ctl;
void showProgress(bool show = true) {
m_sbar->show_progress(show);
}
void _state(unsigned st) {
if( st <= ProgressIndicator::max() ) {
Base::state(st);
m_sbar->set_status_text(m_message);
m_sbar->set_progress(st);
}
}
// status update handler
void _state( wxCommandEvent& evt) {
unsigned st = evt.GetInt(); _state(st);
}
const int id_ = wxWindow::NewControlId();
public:
inline Wrapper(ProgressStatusBar *sbar,
AppControllerBase& ctl):
m_sbar(sbar), m_ctl(ctl)
{
Base::max(static_cast<float>(m_sbar->get_range()));
Base::states(static_cast<unsigned>(m_sbar->get_range()));
Bind(PROGRESS_STATUS_UPDATE_EVENT,
&Wrapper::_state,
this, id_);
}
virtual void state(float val) override {
state(unsigned(val));
}
virtual void max(float val) override {
if(val > 1.0) {
m_sbar->set_range(static_cast<int>(val));
ProgressIndicator::max(val);
}
}
void state(unsigned st) {
if(!m_ctl.is_main_thread()) {
auto evt = new wxCommandEvent(PROGRESS_STATUS_UPDATE_EVENT, id_);
evt->SetInt(st);
wxQueueEvent(this, evt);
} else {
_state(st);
}
}
virtual void message(const std::string & msg) override {
m_message = _(msg);
}
virtual void message_fmt(const std::string& fmt, ...) override {
va_list arglist;
va_start(arglist, fmt);
m_message = wxString::Format(_(fmt), arglist);
va_end(arglist);
}
virtual void title(const std::string & /*title*/) override {}
virtual void on_cancel(CancelFn fn) override {
m_sbar->set_cancel_callback(fn);
Base::on_cancel(fn);
}
};
}
void AppController::set_global_progress_indicator(ProgressStatusBar *prsb)
{
if(prsb) {
auto ctl = GUI::get_appctl();
ctl->global_progress_indicator(std::make_shared<Wrapper>(prsb, *ctl));
}
}
}

View file

@ -121,9 +121,6 @@ add_library(libslic3r_gui STATIC
Utils/Time.hpp
Utils/HexFile.cpp
Utils/HexFile.hpp
AppController.hpp
AppController.cpp
AppControllerWx.cpp
)
target_link_libraries(libslic3r_gui libslic3r avrdude)

View file

@ -1,6 +1,5 @@
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "../AppController.hpp"
#include "WipeTowerDialog.hpp"
#include <assert.h>
@ -453,23 +452,4 @@ void desktop_open_datadir_folder()
#endif
}
namespace {
AppControllerPtr g_appctl;
}
AppControllerPtr get_appctl()
{
return g_appctl;
}
void set_cli_appctl()
{
g_appctl = std::make_shared<AppControllerCli>();
}
void set_gui_appctl()
{
g_appctl = std::make_shared<AppControllerGui>();
}
} }

View file

@ -11,7 +11,6 @@
#include "Tab.hpp"
#include "PresetBundle.hpp"
#include "../AppController.hpp"
#include "ProgressStatusBar.hpp"
#include "3DScene.hpp"
#include "Print.hpp"
@ -30,8 +29,6 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
m_no_plater(no_plater),
m_loaded(loaded)
{
m_appController = new Slic3r::AppController();
// Load the icon either from the exe, or from the ico file.
#if _WIN32
{
@ -59,14 +56,6 @@ wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAUL
SLIC3R_VERSION +
_(L(" - Remember to check for updates at http://github.com/prusa3d/slic3r/releases")));
m_appController->set_model(&m_plater->model());
m_appController->set_print(&m_plater->print());
GUI::set_gui_appctl();
// Make the global status bar and its progress indicator available in C++
m_appController->set_global_progress_indicator(m_statusbar);
m_loaded = true;
// initialize layout
@ -373,7 +362,7 @@ void MainFrame::slice_to_png()
{
// m_plater->stop_background_process();
// m_plater->async_apply_config();
m_appController->print_ctl()->slice_to_png();
// m_appController->print_ctl()->slice_to_png();
}
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".

View file

@ -20,7 +20,6 @@ class wxProgressDialog;
namespace Slic3r {
class ProgressStatusBar;
class AppController;
// #define _(s) Slic3r::GUI::I18N::translate((s))
@ -54,7 +53,6 @@ class MainFrame : public wxFrame
wxString m_qs_last_output_file = wxEmptyString;
wxString m_last_config = wxEmptyString;
AppController* m_appController { nullptr };
std::map<std::string, Tab*> m_options_tabs;
wxMenuItem* m_menu_item_reslice_now { nullptr };
@ -97,8 +95,6 @@ public:
void select_tab(size_t tab) const;
void select_view(const std::string& direction);
AppController* app_controller() { return m_appController; }
std::vector<PresetTab>& get_preset_tabs();
Plater* m_plater { nullptr };
@ -110,4 +106,4 @@ public:
} // GUI
} //Slic3r
#endif // slic3r_MainFrame_hpp_
#endif // slic3r_MainFrame_hpp_

View file

@ -25,6 +25,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/ModelArrange.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/GCode/PreviewData.hpp"
@ -33,7 +34,7 @@
#include "libslic3r/Format/STL.hpp"
#include "libslic3r/Format/AMF.hpp"
#include "libslic3r/Format/3mf.hpp"
#include "slic3r/AppController.hpp"
//#include "slic3r/AppController.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
@ -863,6 +864,7 @@ struct Plater::priv
wxGLCanvas *canvas3D; // TODO: Use GLCanvas3D when we can
Preview *preview;
BackgroundSlicingProcess background_process;
std::atomic<bool> arranging;
wxTimer background_process_timer;
@ -1446,13 +1448,86 @@ void Plater::priv::mirror(Axis axis)
void Plater::priv::arrange()
{
this->background_process.stop();
main_frame->app_controller()->arrange_model();
// don't do anything if currently arranging. Then this is a re-entrance
if(arranging.load()) return;
// Guard the arrange process
arranging.store(true);
_3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange());
this->background_process.stop();
unsigned count = 0;
for(auto obj : model.objects) count += obj->instances.size();
auto prev_range = statusbar()->get_range();
statusbar()->set_range(count);
auto statusfn = [this, count] (unsigned st, const std::string& msg) {
/* // In case we would run the arrange asynchronously
wxCommandEvent event(EVT_PROGRESS_BAR);
event.SetInt(st);
event.SetString(msg);
wxQueueEvent(this->q, event.Clone()); */
statusbar()->set_progress(count - st);
statusbar()->set_status_text(msg);
// ok, this is dangerous, but we are protected by the atomic flag
// 'arranging'. This call is needed for the cancel button to work.
wxYieldIfNeeded();
};
statusbar()->set_cancel_callback([this, statusfn](){
arranging.store(false);
statusfn(0, L("Arranging canceled"));
});
static const std::string arrangestr = L("Arranging");
// FIXME: I don't know how to obtain the minimum distance, it depends
// on printer technology. I guess the following should work but it crashes.
double dist = 6; //PrintConfig::min_object_distance(config);
auto min_obj_distance = static_cast<coord_t>(dist/SCALING_FACTOR);
const auto *bed_shape_opt = config->opt<ConfigOptionPoints>("bed_shape");
assert(bed_shape_opt);
auto& bedpoints = bed_shape_opt->values;
Polyline bed; bed.points.reserve(bedpoints.size());
for(auto& v : bedpoints) bed.append(Point::new_scale(v(0), v(1)));
statusfn(0, arrangestr);
try {
arr::BedShapeHint hint;
// TODO: from Sasha from GUI or
hint.type = arr::BedShapeType::WHO_KNOWS;
arr::arrange(model,
min_obj_distance,
bed,
hint,
false, // create many piles not just one pile
[statusfn](unsigned st) { statusfn(st, arrangestr); },
[this] () { return !arranging.load(); });
} catch(std::exception& /*e*/) {
GUI::show_error(this->q, L("Could not arrange model objects! "
"Some geometries may be invalid."));
}
statusfn(0, L("Arranging done."));
statusbar()->set_range(prev_range);
statusbar()->set_cancel_callback(); // remove cancel button
arranging.store(false);
this->schedule_background_process();
// ignore arrange failures on purpose: user has visual feedback and we don't need to warn him
// when parts don't fit in print bed
// ignore arrange failures on purpose: user has visual feedback and we
// don't need to warn him when parts don't fit in print bed
_3DScene::enable_toolbar_item(canvas3D, "arrange", can_arrange());
update();
}
@ -1908,7 +1983,7 @@ bool Plater::priv::can_delete_all() const
bool Plater::priv::can_arrange() const
{
return !model.objects.empty();
return !model.objects.empty() && !arranging.load();
}
bool Plater::priv::can_mirror() const