Skip to main content
Skip table of contents

Using IProgressEventHandler to report progress when saving a file

📌 Overview

This article explains how to use the new IProgressEventHandler API to report progress when saving a file in Mako. It also compares this new event-based mechanism with the existing tick-based IProgressTick approach, described in Progress Monitoring in Mako, which is commonly used for progress reporting in custom transforms.

ℹ️ Key Concepts: IProgressEventHandler vs IProgressTick

Feature

IProgressTick

IProgressEventHandler

Reporting Style

Tick-based (integer/float increments)

Event-based (specific operation events)

Typical Use

Custom transforms

Saving files

Callback Signature

tick(currentCount, maxCount)

handleEvent(Event evt)

Events Supported

None (just ticks)

Page write start/end, node write start/end

Integration

IProgressMonitor::create(const IProgressTickPtr &progressTick)

IProgressMonitor::setProgressEventHandler(const IProgressEventHandlerPtr &progressEventHandler)

Granularity

Coarse (progress bar style)

Fine (event-driven, detailed)

💪 Usage

  • Use IProgressTick for simple, linear progress reporting (e.g., custom transforms where only a percentage or tick count is needed).

  • Use IProgressEventHandler for more detailed, event-driven reporting (e.g., when you want to track the start/end of pages or nodes during file save operations).

⌨️ Code Examples

Event-Based Progress Reporting with IProgressEventHandler - extracts from Visual Studio Projects/ProgressMonitoring, Java Projects/ProgressMonitoring and Python Projects/ProgressMonitoring.

CPP
class ProgressEventHandler {
public:
    // This section should be customised
    ProgressEventHandler() : m_pageCount(0), m_nodeCount(0), m_nodeDepth(0) {}
    void handleEvent(IProgressEventHandler::Event evt) {
        int id;
        switch (evt) {
        case IProgressEventHandler::eEvtPageWriteStart:
            m_pageCount++;
            m_nodeCount = 0;
            m_nodeDepth = 0;
            m_nodes.clear();
            printf("start of page %d\n", m_pageCount);
            break;
        case IProgressEventHandler::eEvtPageWriteEnd:
            printf("end of page %d, got %d node events\n", m_pageCount, m_nodeCount);
            if (m_nodeDepth != 0) printf("mismatch in node write start/end %d\n", m_nodeDepth);
            break;
        case IProgressEventHandler::eEvtNodeWriteStart:
            id = ++m_nodeCount;
            m_nodeDepth++;
            m_nodes.push_back(id);
            if (id % 500 == 0) {
                printf("start of node %d\n", id);
            }
            break;
        case IProgressEventHandler::eEvtNodeWriteEnd:
            m_nodeDepth--;
            if (m_nodes.empty())
                printf("mismatch, empty nodes\n");
            else {
                id = m_nodes.back();
                m_nodes.pop_back();
                if (id % 500 == 0) {
                    printf("end of node %d\n", id);
                }
            }
            break;
        default:
            break;
        }
    }
    // Everything below this part can be reused
    static std::shared_ptr<ProgressEventHandler> create() {
        std::shared_ptr<ProgressEventHandler> progressEventHandler = std::make_shared<ProgressEventHandler>();
        progressEventHandler->m_progressEventHandler = IProgressEventHandler::create(callback, progressEventHandler.get());
        return progressEventHandler;
    }
    int m_pageCount;
    int m_nodeCount;
    int m_nodeDepth;
    std::deque<int> m_nodes;
    IProgressEventHandlerPtr m_progressEventHandler;
protected:
    static void callback(void* priv, IProgressEventHandler::Event evt) {
        ProgressEventHandler* progressEventHandler = (ProgressEventHandler*)priv;
        if (progressEventHandler)
            progressEventHandler->handleEvent(evt);
    }
};
C#
class ProgressEventHandler : IProgressEventHandlerCallback
{
    private int m_pageCount;
    private int m_nodeCount;
    private int m_nodeDepth;
    private readonly Stack<int> m_nodes = new Stack<int>();

    public ProgressEventHandler()
    {
        m_pageCount = 0;
        m_nodeCount = 0;
        m_nodeDepth = 0;
    }

    public override void handleEvent(IProgressEventHandler.Event evt)
    {
        int id;
        switch (evt)
        {
            case IProgressEventHandler.Event.eEvtPageWriteStart:
                m_pageCount++;
                m_nodeCount = 0;
                m_nodeDepth = 0;
                m_nodes.Clear();
                Console.WriteLine($"start of page {m_pageCount}");
                break;

            case IProgressEventHandler.Event.eEvtPageWriteEnd:
                Console.WriteLine($"end of page {m_pageCount}, got {m_nodeCount} node events");
                if (m_nodeDepth != 0)
                    Console.WriteLine($"mismatch in node write start/end {m_nodeDepth}");
                break;

            case IProgressEventHandler.Event.eEvtNodeWriteStart:
                id = ++m_nodeCount;
                m_nodeDepth++;
                m_nodes.Push(id);
                if (id % 500 == 0)
                    Console.WriteLine($"start of node {id}");
                break;

            case IProgressEventHandler.Event.eEvtNodeWriteEnd:
                m_nodeDepth--;
                if (m_nodes.Count == 0)
                {
                    Console.WriteLine("mismatch, empty nodes");
                }
                else
                {
                    id = m_nodes.Pop();
                    if (id % 500 == 0)
                        Console.WriteLine($"end of node {id}");
                }
                break;

            default:
                break;
        }
    }

    public static ProgressEventHandler Create()
    {
        var handler = new ProgressEventHandler();
        handler.ProgressEvent = IProgressEventHandler.create(handler.getCallbackFunc(), handler.getPriv());
        return handler;
    }

    public IProgressEventHandler ProgressEvent { get; private set; }
}
JAVA
public static class ProgressEventHandler extends IProgressEventHandlerCallback
{
    private int m_pageCount;
    private int m_nodeCount;
    private int m_nodeDepth;
    private final Stack<Integer> m_nodes = new Stack<>();

    private IProgressEventHandler progressEventHandler;

    public ProgressEventHandler()
    {
        m_pageCount = 0;
        m_nodeCount = 0;
        m_nodeDepth = 0;
    }

    @Override
    public void handleEvent(IProgressEventHandler.Event evt)
    {
        int id;
        switch (evt)
        {
            case eEvtPageWriteStart:
                m_pageCount++;
                m_nodeCount = 0;
                m_nodeDepth = 0;
                m_nodes.clear();
                System.out.printf("start of page %d%n", m_pageCount);
                break;

            case eEvtPageWriteEnd:
                System.out.printf("end of page %d, got %d node events%n", m_pageCount, m_nodeCount);
                if (m_nodeDepth != 0)
                    System.out.printf("mismatch in node write start/end %d%n", m_nodeDepth);
                break;

            case eEvtNodeWriteStart:
                id = ++m_nodeCount;
                m_nodeDepth++;
                m_nodes.push(id);
                if (id % 500 == 0)
                    System.out.printf("start of node %d%n", id);
                break;

            case eEvtNodeWriteEnd:
                m_nodeDepth--;
                if (m_nodes.isEmpty())
                {
                    System.out.println("mismatch, empty nodes");
                }
                else
                {
                    id = m_nodes.pop();
                    if (id % 500 == 0)
                        System.out.printf("end of node %d%n", id);
                }
                break;

            default:
                break;
        }
    }

    public static ProgressEventHandler create()
    {
        ProgressEventHandler handler = new ProgressEventHandler();
        handler.progressEventHandler = IProgressEventHandler.create(
                handler.getCallbackFunc(),
                handler.getPriv());
        return handler;
    }

    public IProgressEventHandler getProgressEventHandler()
    {
        return progressEventHandler;
    }
}
PY
class ProgressEventHandler:
    class Callback(IProgressEventHandlerCallback):
        def __init__(self):
            super().__init__()
            self.m_pageCount = 0
            self.m_nodeCount = 0
            self.m_nodeDepth = 0
            self.m_nodes = []

        def handleEvent(self, evt):
            if evt == IProgressEventHandler.eEvtPageWriteStart:
                self.m_pageCount += 1
                self.m_nodeCount = 0
                self.m_nodeDepth = 0
                self.m_nodes.clear()
                print(f"start of page {self.m_pageCount}")

            elif evt == IProgressEventHandler.eEvtPageWriteEnd:
                print(f"end of page {self.m_pageCount}, got {self.m_nodeCount} node events")
                if self.m_nodeDepth != 0:
                    print(f"mismatch in node write start/end {self.m_nodeDepth}")

            elif evt == IProgressEventHandler.eEvtNodeWriteStart:
                self.m_nodeCount += 1
                self.m_nodeDepth += 1
                self.m_nodes.append(self.m_nodeCount)
                if self.m_nodeCount % 500 == 0:
                    print(f"start of node {self.m_nodeCount}")

            elif evt == IProgressEventHandler.eEvtNodeWriteEnd:
                self.m_nodeDepth -= 1
                if not self.m_nodes:
                    print("mismatch, empty nodes")
                else:
                    node_id = self.m_nodes.pop()
                    if node_id % 500 == 0:
                        print(f"end of node {node_id}")

    def __init__(self):
        super().__init__()
        self.callback = ProgressEventHandler.Callback()
        cbFunc = self.callback.getCallbackFunc()
        cbPriv = self.callback.getPriv()
        self.progressEventHandler = IProgressEventHandler.create(cbFunc, cbPriv)

Integration Example: Saving a File with Progress Events

CPP
// Use a ProgressEventHandler for the output to get more detailed information
std::shared_ptr<ProgressEventHandler>    outputProgressHandler = ProgressEventHandler::create();

// Set ProgressEventHandler for output progress monitor
IProgressMonitorPtr outputProgressMonitor = IProgressMonitor::create(abort);
outputProgressMonitor->setProgressEventHandler(outputProgressHandler->m_progressEventHandler);
const IOutputPtr output = IOutput::create(jawsMako, cvtParams.m_outputFileFormat, outputProgressMonitor);
C#
// Use a ProgressEventHandler for the output to get more detailed information
var outputHandler = ProgressEventHandler.Create();

// Set ProgressEventHandler for output progress monitor
IProgressMonitorPtr outputProgressMonitor = IProgressMonitor.create(abort);
outputProgressMonitor.setProgressEventHandler(outputHandler.ProgressEvent);
IOutput output = IOutput.create(jawsMako, cvtParams.OutputFileFormat, outputProgressMonitor);
JAVA
// Use a ProgressEventHandler for the output to get more detailed information
var outputHandler = ProgressEventHandler.create();

// Set ProgressEventHandler for output progress monitor
IProgressMonitor outputProgressMonitor = IProgressMonitor.create(abort);
outputProgressMonitor.setProgressEventHandler(outputHandler.getProgressEventHandler());
IOutput output = IOutput.create(jawsMako, cvtParams.outputFileFormat, outputProgressMonitor);
PY
# Use a ProgressEventHandler for the output to get more detailed information
output_handler = ProgressEventHandler()

# Set ProgressEventHandler for output progress monitor
output_progress_monitor = IProgressMonitor.create(abort)
output_progress_monitor.setProgressEventHandler(output_handler.progressEventHandler)
output = IOutput.create(jaws_mako, cvt_params.output_file_format, output_progress_monitor)

☑️ Conclusion

The IProgressEventHandler API provides a more flexible and granular way to report progress when saving files. For legacy or simple scenarios, IProgressTick remains available. Choose the mechanism that best fits your reporting needs.

📚 Additional Resources

If you need additional help, see our API documentation for detailed information on class/method usage, or raise a support ticket via our customer portal.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.