Go语言 如何用mpb创建两行进度条?

ryevplcw  于 2023-05-20  发布在  Go
关注(0)|答案(1)|浏览(177)

我正在尝试使用mpb创建两行进度条。
假设我有一个包含文件绝对路径的切片。

list := []string{"C:\Temp\01.png",  "C:\Temp\02.png",  "C:\Temp\03.png",  "C:\Temp\test.png",  "C:\Temp\test01.png"}

我想把它显示成这样:

Processing 01.png ...
0 / 5 [                    ] 0%
Processing 02.png ...
1 / 5 [==                  ] 20%
Processing 03.png ...
2 / 5 [====                ] 40%

等等。
将“正在处理...”部分和进度条分开的原因如下:
1.我想显示有关当前处理状态的其他信息。

Processing 01.png ... [Mode: WebP]
0 / 5 [                    ] 0%

1.有时候,我必须处理同一个文件两次。

Processing 01.mp4 ... [Mode: WebM] [Pass: 1/2]
0 / 5 [                    ] 0%
Processing 01.mp4 ... [Mode: WebM] [Pass: 2/2]
0 / 5 [                    ] 0%

请注意,进度条没有改变。
1.我也想一次做多个进度条。

Processing 01.mp4 ... [Mode: WebM] [Pass: 1/2]
4 / 5 [================    ] 0%
Processing 01.png ... [Mode: WebP]
2 / 5 [========            ] 0%
Processing DONE [Mode: MP3]
5 / 5 [====================] 100%

每个进度条都应该在发生更改时尽快更新,而不是“每0.5秒更新一次进度条”。
我找不到一个方法来做这件事。mpb的每个示例代码都是单行的。

fxnxkyjh

fxnxkyjh1#

我第一次看到这个库,但在探索了一些代码后,我发现如何添加新的和自定义的酒吧,因为我们想要的。我已经做了一些修改的例子,你提供的链接,在您的评论,以示范添加和自定义酒吧。

package main

import (
    "fmt"
    "io"
    "math/rand"
    "time"

    "github.com/vbauerster/mpb/v8"
    "github.com/vbauerster/mpb/v8/decor"
)

func main() {
    p := mpb.New()

    var piece, piece2 int64

    var way, way2 int64

    file := func(_ decor.Statistics) string {

        return "file.extension "
    }

    file2 := func(_ decor.Statistics) string {

        return "file2.extension "
    }

    part := func(s decor.Statistics) string {

        s.Current = piece
        s.Total = 5
        return fmt.Sprintf(" Part %d/%d", s.Current, s.Total)
    }

    part2 := func(s decor.Statistics) string {

        s.Current = piece2
        s.Total = 10
        return fmt.Sprintf(" Part %d/%d", s.Current, s.Total)
    }

    pass := func(s decor.Statistics) string {

        s.Current = way
        s.Total = 2
        return fmt.Sprintf(" Pass %d/%d", s.Current, s.Total)
    }

    pass2 := func(s decor.Statistics) string {

        s.Current = way2
        s.Total = 4
        return fmt.Sprintf(" Pass %d/%d", s.Current, s.Total)
    }
    var total = 100

    bar := p.New(int64(total),
        mpb.NopStyle(), // make main bar style nop, so there are just decorators
        mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // extend wtih normal bar on the next line
        mpb.PrependDecorators(
            decor.Any(file),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part),
            decor.Any(pass),
        ),
    )

    bar2 := p.New(int64(total+100),
        mpb.NopStyle(),
        mpb.BarExtender(extended(mpb.BarStyle().Build()), false),

        mpb.PrependDecorators(
            decor.Any(file2),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part2),
            decor.Any(pass2),
        ),
    )
    // simulating some work
    max := 100 * time.Millisecond

    for i := 0; i < total+100; i++ {

        switch {
        case i == 20 || i == 40 || i == 60 ||
            i == 80 || i == 100 || i == 120 ||
            i == 140 || i == 160 || i == 180:
            piece2++

            if i == 100 {
                way2++

                piece = 5
                way = 2
            } else if i < 100 {
                piece++
            }

        case i == 50 || i == 150:

            if i < 100 {
                way++
            }

            way2++
        }

        time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)

        if i < 100 {
            bar.Increment()
        }
        bar2.Increment()
    }

    way2 = 4
    piece2 = 10
    // wait for our bar to complete and flush
    p.Wait()
}

func extended(base mpb.BarFiller) mpb.BarFiller {
    return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
        err := base.Fill(w, st)
        if err != nil {
            return err
        }
        _, err = io.WriteString(w, "\n")
        return err
    })
}

假设您已经知道如何创建自定义装饰器。
要创建一个新的条形图,可以使用p.New()方法:

bar := p.New(int64(total),
        mpb.NopStyle(), // make main bar style nop, so there are just decorators
        mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // extend wtih normal bar on the next line
        mpb.PrependDecorators(
            decor.Any(file),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part),
            decor.Any(pass),
        ),
    )
  • 在这个例子中,为了演示制作新的酒吧,我使用了New()方法,但有更多的方法来制作一个新的酒吧。区别主要是酒吧的外观。您可以通过在文档 * 中的Progress结构体的方法列表中查找返回Bar示例的方法来找到它们。
  • 例如,我只做了两个酒吧,但你可以做两个以上。

p.New()的第一个参数是指定条形图的值。在我的示例中,两个条形图的值分别为100和200。
只需将装饰器设置为mpb.PrependDecoratorsmpb.AppendDecorators即可。

mpb.PrependDecorators(
            decor.Any(file),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part),
            decor.Any(pass),
        ),
  • 您可以为每个酒吧使用相同的装饰器,但我建议为每个酒吧制作新的装饰器,以避免冲突的可能性。

在我的例子中,两个bar的外部装饰器分别是:
decor.Any(file)decor.Any(part)decor.Any(pass)decor.Any(file2)decor.Any(part2)decor.Any(pass2)
两者的内部装饰器是相同的:
decor.Name("Percentage: ")decor.NewPercentage("%d")

  • 我将解释为什么我使用内部和外部术语来表示装饰,并解释负责增加装饰状态的循环。

这将分别显示文件名、百分比、部分和通过:

file.extension Percentage: 100% Part 5/5 Pass 2/2  
[==============================================================================]

让我们来谈谈负责增加两个酒吧的所有装饰状态的循环。

for i := 0; i < total+100; i++ {

        switch {
        case i == 20 || i == 40 || i == 60 ||
            i == 80 || i == 100 || i == 120 ||
            i == 140 || i == 160 || i == 180:
            piece2++

            if i == 100 {
                way2++

                piece = 5
                way = 2
            } else if i < 100 {
                piece++
            }

        case i == 50 || i == 150:

            if i < 100 {
                way++
            }

            way2++
        }

        time.Sleep(time.Duration(rand.Intn(10)+1) * max / 10)

        if i < 100 {
            bar.Increment()
        }
        bar2.Increment()
    }

我将两个条形图的值设定为相同的步长,即20,以将Part装饰增加1,这将导致100增加5,200增加10。当涉及到Pass装饰时,我瞄准了步骤50,这将导致100增加2,200增加4。当循环变量遇到Part修饰的步骤或Pass的步骤中的任何一个时,相关的修饰增加1。实际上,这些装饰与Percentage装饰无关。装饰的特别之处直接影响到酒吧的价值。在这个例子中,它只会通过这些方法调用增加1:
bar.Increment()bar2.Increment()
因为第一个条的值是100,所以循环将在每1次迭代中增加其百分比,但第二个条的值是200,每2次迭代增加其百分比。

For first bar, loop var = 1 and 2: Percentage: 1% [>], Percentage: 2% [=>]  
For second bar, loop var = 2 and 4: Percentage: 1% [>], Percentage: 2% [=>]
  • 我不知道百分比装饰的所有细节。PartPass花色没有任何百分比和条形值。它们将根据循环变量和步长值从外部增加其状态。这就是为什么我使用内部和外部的概念。但它仍然可以使您的实际程序与外部装饰一起工作,我将在其他示例中展示。

整个代码的输出是:

file.extension Percentage: 9% Part 0/5 Pass 0/2  
[======>-----------------------------------------------------------------------]
file2.extension Percentage: 4% Part 0/10 Pass 0/4  
[===>--------------------------------------------------------------------------]
file.extension Percentage: 49% Part 2/5 Pass 0/2  
[=====================================>----------------------------------------]
file2.extension Percentage: 24% Part 2/10 Pass 0/4  
[==================>-----------------------------------------------------------]
file.extension Percentage: 100% Part 5/5 Pass 2/2  
[==============================================================================]
file2.extension Percentage: 74% Part 7/10 Pass 2/4  
[=========================================================>--------------------]
file.extension Percentage: 100% Part 5/5 Pass 2/2  
[==============================================================================]
file2.extension Percentage: 100% Part 10/10 Pass 4/4  
[==============================================================================]

附加示例

package main

import (
    "bufio"
    "fmt"
    "io"
    "log"
    "os"

    "github.com/vbauerster/mpb/v8"
    "github.com/vbauerster/mpb/v8/decor"
)

func main() {
    p := mpb.New()

    f, err := os.Create("file.extension")
    if err != nil {
        log.Fatal(err)
    }

    // change file size to 10MB
    if err := f.Truncate(1e7); err != nil {
        log.Fatal(err)
    }

    // delete the dumb file when our demonstrative finish
    defer os.Remove("file.extension")

    defer f.Close()

    status, err := f.Stat()
    if err != nil {
        log.Fatal(err)
    }

    sizeOfFile := status.Size()

    var piece, way int64
    var currentMb int64

    file := func(s decor.Statistics) string {
        return status.Name() + " "
    }

    part := func(s decor.Statistics) string {

        s.Current = piece
        s.Total = 5
        return fmt.Sprintf(" Part %d/%d", s.Current, s.Total)
    }

    pass := func(s decor.Statistics) string {

        s.Current = way
        s.Total = 2
        return fmt.Sprintf(" Pass %d/%d", s.Current, s.Total)
    }

    mbInfo := func(s decor.Statistics) string {

        s.Current = currentMb
        s.Total = 10
        return fmt.Sprintf(" Size %d/%dMB", s.Current, s.Total)
    }

    bar := p.New(int64(sizeOfFile),
        mpb.NopStyle(), // make main bar style nop, so there are just decorators
        mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // extend wtih normal bar on the next line
        mpb.PrependDecorators(
            decor.Any(file),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part),
            decor.Any(pass),
            decor.Any(mbInfo),
        ),
    )

    r := bufio.NewReader(f)

    var sendReadBytes = make(chan int)
    var done = make(chan struct{})
    var n int
    var reach int
    valueOfPiece := sizeOfFile / 5

    go func() {
        for {

            buf := make([]byte, 4)

            n, err = r.Read(buf)
            sendReadBytes <- n

            reach += n

            if n == 0 {

                if err != nil {
                    fmt.Println(err)
                    break
                }
                if err == io.EOF {
                    break
                }
                return
            }

            <-done
        }

    }()

    for {
        if reach == int(sizeOfFile) {
            break
        }

        switch int64(reach) {
        case valueOfPiece:
            piece++
        case valueOfPiece * 2:
            piece++
        case sizeOfFile / 2:
            way++
        case valueOfPiece * 3:
            piece++
        case valueOfPiece * 4:
            piece++
        }

        // increase the progress every time 4 bytes
        bar.IncrBy(<-sendReadBytes)

        // convert current progress value to MB
        currentMb = bar.Current() / 1000000

        done <- struct{}{}
    }

    piece++
    way++

    p.Wait()

}

func extended(base mpb.BarFiller) mpb.BarFiller {
    return mpb.BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
        err := base.Fill(w, st)
        if err != nil {
            return err
        }
        _, err = io.WriteString(w, "\n")
        return err
    })
}

这只是为了如何改变酒吧的进展不同的东西。同时展示这些外部装饰是如何有用的。为了使事情更短,我将简要地解释这是做什么。此示例使哑文件大小为10MB。使一个酒吧的价值是文件的大小。从文件中读取4个字节,然后将该值发送到bar的增量值,以演示如何通过阅读文件进行。外部decor用于记录成功通过的零件数量和MB数量。
输出:
file.extension Percentage: 55% Part 2/5 Pass 1/2 Size 5/10MB [==========================================>-----------------------------------]

  • 这个例子可能没有并发函数。但是我想把进度区域和文件读取区域分开,使代码更简单,这就是我使用它的原因。我没有做第二个例子multibar来做代码分类器。*

相关问题