Maya C++ API: create a progress window

Leaving some C++ code snippet to display a progress window (MProgressWindow) or progress Bar (MComputation) within Maya's interface. Note that using MProgessWindow or MComputation inside a node's compute() method is unsafe/undefined. I may add that as stated in the documentation you should not modify anything within a compute() method except for the node's output attributes.

Progress window snippet:

#pragma once

#include <string>
#include <time.h>

/**
 * @brief Maya progress window wrapper.
 *
 * Display a progress window in Maya
 *
   Usage:
   @code
    int total = nb_iter;
    Progress_window window("Process in progress", total);

    // will only display progress if operation is running greater than
    // a certain delay
    window.set_delay(0.5); // (optional default = 1 sec)

    for(int iter = 0; iter < total; iter++)
    {
        if(window.is_canceled())
            break;

        //do stuff
        ...


        window.add(1);

    }
    window.stop(); // Optional (performed when the object is destructed)
    @endcode

    @warning Like a lot of things in Maya nodes it's not safe to use a
    progress window (or any UI related functions)
    inside a MPxNode::compute() MPxDeformer::deform() etc.

 *
 */
class Progress_window {
private:

    /// How much we wait before displaying the window
    /// (time represented in clocks ticks) (see time.h clock())
    time_t _delay;

    /// starting time (in clock ticks) (see time.h clock())
    time_t _start;
    bool _is_init;
    bool _interruptable;
    std::string _win_title;

    int _progress;
    int _max_range;

public:
    Progress_window();
    Progress_window(const std::string& win_title, int max_range, bool interuptable = true);
    ~Progress_window();

    void start(const std::string& win_title, int max_range);
    void set_delay(float seconds) {
        _delay = time_t((float)CLOCKS_PER_SEC*seconds);
        _delay = _delay <= 0 ? 0 : _delay;
    }

    void add_progess(int increment = 1);

    void stop();

    /// @brief force window to show regardless of the delay
    /// set with "set_delay()"
    void show();

    /// @return if the canceled button was hit by the user.
    const bool is_canceled()const;
};
#include "progress_window.hpp"

#include <maya_error.hpp> // my custom macros to check errors
#include <maya/MProgressWindow.h>
#include <maya/MString.h>

Progress_window::Progress_window()
    : _delay(CLOCKS_PER_SEC*1)
{
}

// -----------------------------------------------------------------------------

Progress_window::Progress_window(const std::string& win_title,
                                 int max_range,
                                 bool interuptable)
    : _delay(CLOCKS_PER_SEC*1)
    , _interruptable( interuptable )
{
    start(win_title,max_range);
}

// -----------------------------------------------------------------------------

Progress_window::~Progress_window(void)
{
    stop();
}

// -----------------------------------------------------------------------------

void Progress_window::start(const std::string& win_title, int max_range)
{
    _max_range = max_range;
    _win_title = win_title;

    _start = clock();
    _progress = 0;
    _is_init = false;
}

// -----------------------------------------------------------------------------

void Progress_window::stop()
{
    if (_is_init)
    {
        // Maya doc:
        // Destroys the progress window and removes it from the screen.
        // This method also unreserves the progress window,
        // making it available for future reservation.
        mayaCheck( MProgressWindow::endProgress() );
        _is_init = false;
    }
}

// -----------------------------------------------------------------------------

void Progress_window::add_progess(int increment)
{
    if( clock() > (_start + Progress_window::_delay) ) {
        show();
    }

    if (_is_init){
        mayaCheck( MProgressWindow::advanceProgress(increment) );        
    }
    else{
        _progress += increment;
    }
}

// -----------------------------------------------------------------------------

void Progress_window::show()
{
    if(!_is_init )
    {
        // Maya doc: MProgressWindow::reserve()
        // Reserves a progress window for use through this class.
        // This method must be called before setting progress window parameters or starting progress.
        // Returns: true if the progress window was successfully reserved
        if (MProgressWindow::reserve())
        {
            _is_init = true;
            mayaCheck( MProgressWindow::setProgressRange(0, _max_range) );
            mayaCheck( MProgressWindow::setTitle( _win_title.c_str() ) );
            mayaCheck( MProgressWindow::setProgressStatus(_win_title.c_str()) );
            mayaCheck( MProgressWindow::setInterruptable(_interruptable) );
            mayaCheck( MProgressWindow::setProgress(_progress) );

            // Maya doc: Displays the progress window on the screen.
            mayaCheck( MProgressWindow::startProgress() );
        }
    }
}

// -----------------------------------------------------------------------------

const bool Progress_window::is_canceled() const
{
    return _is_init && MProgressWindow::isCancelled();
}

Progress bar snippet:

#pragma once

#include <string>
#include <time.h>

#include <maya/MComputation.h>

/**
 * @brief Maya MComputation wrapper.
 *
 * Display a progress bar in the lower left corner of Maya UI
 * You can also cancel computation with "ESC" key
 *
   Usage:
   @code
    int total = nb_iter;
    Progress_bar a_bar("Process in progress", total);

    // will only display progress if operation is running greater than
    // a certain delay
    a_bar.set_delay(0.5); // (optional default = 1 sec)

    for(int iter = 0; iter < total; iter++)
    {
        // Triggered on escape key:
        if(a_bar.is_canceled())
            break;

        //do stuff
        ...


        a_bar.add(1);

    }
    a_bar.stop(); // Optional (performed when the object is destructed)
    @endcode

    @warning Like a lot of things in Maya nodes it's not safe to use a
    progress bar (or any UI related functions)
    inside a MPxNode::compute() MPxDeformer::deform() etc.

 *
 */
class Progress_bar {
private:

    mutable  MComputation _computation; /* MComputation is not const correct..*/
    /// How much we wait before displaying the window
    /// (time represented in clocks ticks) (see time.h clock())
    time_t _delay;

    /// starting time (in clock ticks) (see time.h clock())
    time_t _start;
    bool _is_init;
    bool _interruptable;
    std::string _win_title;

    int _progress;
    int _max_range;

public:
    Progress_bar();
    Progress_bar(const std::string& win_title, int max_range, bool interuptable = true);
    ~Progress_bar();

    void start(const std::string& win_title, int max_range);
    void set_delay(float seconds) {
        _delay = time_t((float)CLOCKS_PER_SEC*seconds);
        _delay = _delay <= 0 ? 0 : _delay;
    }

    void add_progess(int increment = 1);

    void stop();

    /// @brief force window to show regardless of the delay
    /// set with "set_delay()"
    void show();

    /// @return if the canceled button was hit by the user.
    const bool is_canceled()const;
};
#include "progress_bar.hpp"

#include <maya_error.hpp> // custom macros to hande errors
#include <maya/MString.h>

Progress_bar::Progress_bar()
    : _delay(CLOCKS_PER_SEC*1)
{
}

// -----------------------------------------------------------------------------

Progress_bar::Progress_bar(const std::string& win_title,
                           int max_range,
                           bool interuptable)
    : _delay(CLOCKS_PER_SEC*1)
    , _interruptable( interuptable )
{
    start(win_title,max_range);
}

// -----------------------------------------------------------------------------

Progress_bar::~Progress_bar(void)
{
    stop();
}

// -----------------------------------------------------------------------------

void Progress_bar::start(const std::string& win_title, int max_range)
{    
    _max_range = max_range;
    _win_title = win_title;

    _start = clock();
    _progress = 0;
    _is_init = false;

}

// -----------------------------------------------------------------------------

void Progress_bar::stop()
{
    if (_is_init)
    {
        _computation.endComputation();
        _is_init = false;
    }
}

// -----------------------------------------------------------------------------

void Progress_bar::add_progess(int increment)
{
    _progress += increment;

    if( clock() > (_start + Progress_bar::_delay) ) {
        show();
    }

    if (_is_init){
        _computation.setProgress(_progress);
    }

}

// -----------------------------------------------------------------------------

void Progress_bar::show()
{
    if(!_is_init )
    {
        _is_init = true;

        _computation.beginComputation(true/*show progress bar*/, _interruptable, true /*wait cursor*/);

        if( !_interruptable )
            _computation.setProgressStatus( _win_title.c_str() );

        _computation.setProgressRange(0, _max_range) ;
        _computation.setProgress(_progress);

    }
}

// -----------------------------------------------------------------------------

const bool Progress_bar::is_canceled() const
{    
    return _is_init && _computation.isInterruptRequested();
}

No comments

(optional field, I won't disclose or spam but it's necessary to notify you if I respond to your comment)
All html tags except <b> and <i> will be removed from your comment. You can make links by just typing the url or mail-address.
Anti-spam question: