Skip to content

Feature request: Modularity and extensibility of printed information #357

Description

@milankl

First of all: I think ProgressMeter.jl is great and thanks for putting so much effort into it. Looking through the open and closed issues at least several of them are about adding new features or options of how the information is displayed, e.g. #286 #255 #244 and so I want to propose modular elements of the progress printed that can easily be extended to achieve customization. This is raised from my side because in SpeedyWeather/SpeedyWeather.jl#989 I've been trying to print additional information as part of the progress of a simulation while it's running. I was only able to do this by committing type piracy on ProgressMeter.showspeed so I'm proposing a more general extensible interface that hopefully also means for you as developer less work because people can do more easily their own extensions.

Define current progress elements as <: AbstractProgressElement

To achieve extensibility, I propose to use multiple dispatch of custom types <: ProgressMeter.AbstractProgressElement and to reimplement the currently printed elements as those too, something like

abstract type AbstractProgressElement end     # the abstract type to subtype for custom elements
function print_element end                    # the function to extend for each new type, expected to return a string 

So the "desc" keyword argument is just a string that's returned

mutable struct DescriptionElement <: AbstractProgressElement
    description::String
end

# always pass on the progress as second argument
print_element(E::DescriptionElement, progress::AbstractProgress) = E.description

but if one wanted to style that string you could adapt print_element or add fields to DescriptionElement. The percentage element would be

struct PercentageElement <: AbstractProgressElement end   
print_element(::PercentageElement, progress::AbstractProgress) = # calculate the X% string here from `progress`

and similar the BarElement which would probably have as fields barlen, barglyphs etc. And similar for TimeElement or SpeedElement. Then every progress::AbstractProgress would get a field called elements::NamedTuple, e.g.

mutable struct Progress{E} <: AbstractProgress
    ...
    elements::E
    ...
end

which determines the order in which the elements are concatenated, and allows one to flexibly reuse existing elements or define new ones, e.g.

p = ProgressMeter.Progress(n, elements = (desc = DescriptionElement("Going fast:"), perc = PercentageElement(), bar = BarElement(glyphs = "abcde"))

Why is this extensible?

As a user of this package I can now do

using ProgressMeter

mutable struct MyElement <: ProgressMeter.AbstractProgressElement
    some_information::T
end

function ProgressMeter.print_element(E::MyElement, progress::ProgressMeter.AbstractProgress)
     # return any information from MyElement or the progress as string
end

So at any time I can update my_element = MyElement(...) (or let it just be a pointer to some object that's changing during progress) and the progress meter would automatically pull in that information and print as I like -- without committing type piracy because only I have defined MyElement.

Please let me know what you think but I think this could be a win-win, i.e. in the long term less work for you because many feature requests can be implemented by the users autonomously plus freedom of styles beyond just custom barglyphs etc.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions