diff options
author | tamasmeszaros <meszaros.q@gmail.com> | 2019-12-16 11:47:31 +0300 |
---|---|---|
committer | tamasmeszaros <meszaros.q@gmail.com> | 2019-12-16 13:07:43 +0300 |
commit | a9403319b7767e920d7d993e91bf0623c91c7b4b (patch) | |
tree | 42efc2962f4992a229a15bd76772973e9f34ed6a /src/slic3r/GUI/Job.hpp | |
parent | f60ff1c7ce02a577003910902a27414612b8de14 (diff) |
Separate Job, ProgressStatusBar and ProgressIndicator
* Separate GUI::Job
* make use of ProgressIndicator interface
* make ProgressStatusbar independent from GUI::App
Diffstat (limited to 'src/slic3r/GUI/Job.hpp')
-rw-r--r-- | src/slic3r/GUI/Job.hpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/src/slic3r/GUI/Job.hpp b/src/slic3r/GUI/Job.hpp new file mode 100644 index 000000000..9accd0ef3 --- /dev/null +++ b/src/slic3r/GUI/Job.hpp @@ -0,0 +1,154 @@ +#ifndef JOB_HPP +#define JOB_HPP + +#include <atomic> + +#include <slic3r/Utils/Thread.hpp> +#include <slic3r/GUI/I18N.hpp> +#include <slic3r/GUI/ProgressIndicator.hpp> + +#include <wx/event.h> + +#include <boost/thread.hpp> + +namespace Slic3r { namespace GUI { + +// A class to handle UI jobs like arranging and optimizing rotation. +// These are not instant jobs, the user has to be informed about their +// state in the status progress indicator. On the other hand they are +// separated from the background slicing process. Ideally, these jobs should +// run when the background process is not running. +// +// TODO: A mechanism would be useful for blocking the plater interactions: +// objects would be frozen for the user. In case of arrange, an animation +// could be shown, or with the optimize orientations, partial results +// could be displayed. +class Job : public wxEvtHandler +{ + int m_range = 100; + boost::thread m_thread; + std::atomic<bool> m_running{false}, m_canceled{false}; + bool m_finalized = false; + std::shared_ptr<ProgressIndicator> m_progress; + + void run() + { + m_running.store(true); + process(); + m_running.store(false); + + // ensure to call the last status to finalize the job + update_status(status_range(), ""); + } + +protected: + // status range for a particular job + virtual int status_range() const { return 100; } + + // status update, to be used from the work thread (process() method) + void update_status(int st, const wxString &msg = "") + { + auto evt = new wxThreadEvent(); + evt->SetInt(st); + evt->SetString(msg); + wxQueueEvent(this, evt); + } + + bool was_canceled() const { return m_canceled.load(); } + + // Launched just before start(), a job can use it to prepare internals + virtual void prepare() {} + + // Launched when the job is finished. It refreshes the 3Dscene by def. + virtual void finalize() { m_finalized = true; } + + bool is_finalized() const { return m_finalized; } + +public: + Job(std::shared_ptr<ProgressIndicator> pri) : m_progress(pri) + { + Bind(wxEVT_THREAD, [this](const wxThreadEvent &evt) { + auto msg = evt.GetString(); + if (!msg.empty()) + m_progress->set_status_text(msg.ToUTF8().data()); + + if (m_finalized) return; + + m_progress->set_progress(evt.GetInt()); + if (evt.GetInt() == status_range()) { + // set back the original range and cancel callback + m_progress->set_range(m_range); + m_progress->set_cancel_callback(); + wxEndBusyCursor(); + + finalize(); + + // dont do finalization again for the same process + m_finalized = true; + } + }); + } + + Job(const Job &) = delete; + Job(Job &&) = delete; + Job &operator=(const Job &) = delete; + Job &operator=(Job &&) = delete; + + virtual void process() = 0; + + void start() + { // Start the job. No effect if the job is already running + if (!m_running.load()) { + prepare(); + + // Save the current status indicatior range and push the new one + m_range = m_progress->get_range(); + m_progress->set_range(status_range()); + + // init cancellation flag and set the cancel callback + m_canceled.store(false); + m_progress->set_cancel_callback( + [this]() { m_canceled.store(true); }); + + m_finalized = false; + + // Changing cursor to busy + wxBeginBusyCursor(); + + try { // Execute the job + m_thread = create_thread([this] { this->run(); }); + } catch (std::exception &) { + update_status(status_range(), + _(L("ERROR: not enough resources to " + "execute a new job."))); + } + + // The state changes will be undone when the process hits the + // last status value, in the status update handler (see ctor) + } + } + + // To wait for the running job and join the threads. False is + // returned if the timeout has been reached and the job is still + // running. Call cancel() before this fn if you want to explicitly + // end the job. + bool join(int timeout_ms = 0) + { + if (!m_thread.joinable()) return true; + + if (timeout_ms <= 0) + m_thread.join(); + else if (!m_thread.try_join_for(boost::chrono::milliseconds(timeout_ms))) + return false; + + return true; + } + + bool is_running() const { return m_running.load(); } + void cancel() { m_canceled.store(true); } +}; + +} +} + +#endif // JOB_HPP |