Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Job.hpp « GUI « slic3r « src - github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: ac31b9bdb04113fb883ad30a3a4baf1b9dae025b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#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; }
    
    
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;
            }
        });
    }
    
    bool is_finalized() const { return m_finalized; }
    
    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