我试着用mpb来做双线进度条(S).

假设我有一个切片,其中包含文件的绝对路径.

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%
    
  2. 有时,我必须处理同一文件两次.
    Processing 01.mp4 ... [Mode: WebM] [Pass: 1/2]
    0 / 5 [                    ] 0%
    
    Processing 01.mp4 ... [Mode: WebM] [Pass: 2/2]
    0 / 5 [                    ] 0%
    
    请注意,进度条不会更改.
  3. 我也想一次做多个进度条.
    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的每个示例代码都在单行中完成任务.

推荐答案

我第一次看到这个库,但在研究了一些代码后,我发现如何添加新的和定制我们想要的栏.我已经做了一些修改的例子,您在您的 comments 中提供了链接,以演示添加和定制的栏.

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
    })
}

假设您已经知道如何创建自定义decorator .

要创建新条形图,可以使用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),
        ),
    )

In this example to demonstrative the making new bars I have used 101 method but there are more ways to make a new bar. The difference is mostly how the bars will look. You can find them by looking methods that returns 102 instance in method list of the103 struct in the 100.

I have only made two bars for example, but you can make more than two.

p.New()的第一个参数是指定条形图的值. 在我的示例中,两个条形图的值分别为p.New()和200.

只要把你的装修工调到mpb.PrependDecoratorsmpb.AppendDecorators就行了.

mpb.PrependDecorators(
            decor.Any(file),
            decor.Name("Percentage: "),
            decor.NewPercentage("%d"),
            decor.Any(part),
            decor.Any(pass),
        ),

You can use the same decorators for every bar but I recommend making new one for every bar to avoid possibility of conflicts.

The external decorators of the two bars in my example are respectively:
decor.Any(file), decor.Any(part) and decor.Any(pass) for the first bar. decor.Any(file2), decor.Any(part2) and decor.Any(pass2) for the second bar.
The internal decorators for both are same:
decor.Name("Percentage: "), decor.NewPercentage("%d").

I will explain why I used the terms internal and external for decors together with explanation of the loop that is responsible for increase the status of the decors.

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

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

让我们来讨论一下负责增加两个wine 吧所有装饰的状态的循环.

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()
    }

I aimed the values of the both bars to same step which is 20 to increase the Part decoration by 1 which will result in 5 increases for the 100 and 10 increases for the 200. When it comes to Pass decoration I aimed the step 50 which will result in 2 increases for the 100, 4 increases for the 200. When the loop variable encounters any of the step for Part decoration or step of Pass the related decoration increase by 1. Actually these decors are not related with Percentage decor. Percentage decor something special to bar's value directly. In this example it will only increase by 1 with these method calls:
bar.Increment(), bar2.Increment().
Because the first bar's value is 100 the loop will increase its percentage in every 1 iteration, but second bar's value is 200, percentage of it increases every 2 iterations.

For first bar, loop var = 1 and 2: Percentage: 1% [>], Percentage: 2% [=>]  
For second bar, loop var = 2 and 4: Percentage: 1% [>], Percentage: 2% [=>]

一百零二

整个代码的输出为:

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  
[==============================================================================]

Additional Example

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
    })
}

This is just for how to change the bar's progress to something different. Also show how these external decors are useful. To make things shorter I will briefly explain what this doing. This example makes a dumb file its size 10MB. Makes a bar its value is file's size. Read 4 bytes from the file then send that value to bar's increment value, to demonstrative how to progress through reading a file. And external decors are used to record how many parts and how many MB passed successfully.
Output:
file.extension Percentage: 55% Part 2/5 Pass 1/2 Size 5/10MB [==========================================>-----------------------------------]
This example would possible without making a concurrent function. But I wanted the divide the progress area from the file read area to make the code simpler that is why I used it. I didn't made the second example multi bar to make the code sorter.

Go相关问答推荐

在字符串与字符串子切片上使用len进行位转移产生意外输出

Go汇编器命名为Constants

如何使用Gio设置标题栏图标

Prometheus 摘要分位数错误

go-jwt 令牌验证错误 - 令牌签名无效:密钥类型无效

当我使用 CircleCI 构建 Go Image 时,我得到runtime/cgo: pthread_create failed: Operation not allowed

Go 中的 YAML 自定义标签

从 eBPF LRU 哈希映射中错误驱逐的元素

regex.ReplaceAll 但如果替换则添加相同数量的字符

CBC Decrypter 解密加密文本,但部分文本被随机字符替换

Protobuf.Any - 从 json.RawMessage 解组

我相信我正确地在 sRGB 和线性 RGB 之间进行了转换,那么为什么深色的结果看起来更糟呢?

未定义 protoc protoc-gen-go 时间戳

使用无服务器工作流 go sdk 时出现间歇性 JSON 解组错误

实现接口的指针的泛型类型是什么?

如何使用带有Electron 表格 ID、Electron 表格名称、表格 ID 和值的 golang 在 googlesheet 中插入数据

如何在 Unmarshal 中使用泛型(转到 1.18)

在 VSCode 中使用命令行参数调试 Go 测试

如何使用通用字段初始化匿名struct数组

Golang LinkedList 删除第一个元素