如何使用 mpb 创建双行进度条?

huangapple go评论72阅读模式
英文:

How to create two-lined progress bar with mpb?

问题

我正在尝试使用mpb创建双行进度条。

假设我有一个包含文件的绝对路径的切片。

list := []string{"C:\Temp.png",  "C:\Temp.png",  "C:\Temp.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%

以此类推。

将“Processing ...”部分和进度条分开的原因如下:

  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的每个示例代码都是在单行中完成的。

英文:

I'm trying to make two-lined progress bar(s) with mpb.

Let's say that I have a slice which contains absolute paths of files.

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

And I want it to be displayed like this:

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

and so on.

The reasons of separting 'Processing <filename> ...' part and progress bar are these:

  1. I want to display additional information about current processing status.
    Processing 01.png ... [Mode: WebP]
    0 / 5 [                    ] 0%
    
  2. Sometimes, I have to process same file two times.
    Processing 01.mp4 ... [Mode: WebM] [Pass: 1/2]
    0 / 5 [                    ] 0%
    
    Processing 01.mp4 ... [Mode: WebM] [Pass: 2/2]
    0 / 5 [                    ] 0%
    

    Note that progress bar isn't changed.

  3. I want to make multiple progress bar at once too.
    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%
    

    Each progress bar should be updated ASAP when something got changed, not 'update every progress bar every 0.5 sec'.

I couldn't find a way of doing this. Every example code of mpb was doing its thing in single-line.

答案1

得分: 2

第一次看到这个库,但在探索了一些代码后,我发现了如何根据我们的需求添加和自定义进度条。我对你在评论中提供的示例进行了一些修改,以演示添加和自定义进度条。

假设你已经知道如何制作自定义装饰器。

要创建一个新的进度条,你可以这样做:

bar := p.New(int64(total),
	mpb.NopStyle(), // 将主进度条样式设置为 nop,这样只会有装饰器
	mpb.BarExtender(extended(mpb.BarStyle().Build()), false), // 在下一行上扩展普通进度条
	mpb.PrependDecorators(
		decor.Any(file),
		decor.Name("Percentage: "),
		decor.NewPercentage("%d"),
		decor.Any(part),
		decor.Any(pass),
	),
)

我只创建了两个进度条作为示例,但你可以创建多个进度条。

在这个示例中,为进度条定义了一个总值,这里是100。

只需将你的装饰器/部分放在 mpb.PrependDecoratorsmpb.AppendDecorators 中。

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

你可以为每个进度条使用相同的装饰器,但我建议为每个进度条创建新的装饰器,以避免可能的冲突。

这将依次显示文件名、百分比、部分和通行证:

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

在这个示例中,每个装饰器根据循环遍历进度条的总值递增1。在这个进度条中,我只遍历了1到100。我将100分为5个部分。当循环变量遇到100的5个部分之一时,进度条的 Part 装饰器将递增1。当循环变量为50时,Pass 装饰器将递增1。

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

这个循环也适用于第二个进度条。我只是将两个进度条的增量状态合并到一个循环中,以使代码更简洁。如果循环变量小于100,则增加第一个进度条的状态。当它为100时,将所有装饰器的增量增加到其总值,然后继续增加第二个进度条的状态,直到循环结束。循环结束后,将第二个进度条的剩余增量增加到总值。

way2 = 4
piece2 = 10

第二个进度条有10个部分和4个通行证,总值为200。

整个代码的输出是:

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  
[==============================================================================]
英文:

First time I see this library but after exploring some code I found how to add new and customize the bars as we want. I have done some modifications to the example which you provided the link in your comment to demonstrative the adding and customizing the bars.

package main

import (
	&quot;fmt&quot;
	&quot;io&quot;
	&quot;math/rand&quot;
	&quot;time&quot;

	&quot;github.com/vbauerster/mpb/v8&quot;
	&quot;github.com/vbauerster/mpb/v8/decor&quot;
)

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

	var piece, piece2 int64

	var way, way2 int64

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

		return &quot;file.extension &quot;
	}

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

		return &quot;file2.extension &quot;
	}

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

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

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

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

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

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

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

		s.Current = way2
		s.Total = 4
		return fmt.Sprintf(&quot; Pass %d/%d&quot;, 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(&quot;Percentage: &quot;),
			decor.NewPercentage(&quot;%d&quot;),
			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(&quot;Percentage: &quot;),
			decor.NewPercentage(&quot;%d&quot;),
			decor.Any(part2),
			decor.Any(pass2),
		),
	)
	// simulating some work
	max := 100 * time.Millisecond

	for i := 0; i &lt; 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 &lt; 100 {
				piece++
			}

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

			if i &lt; 100 {
				way++
			}

			way2++
		}

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

		if i &lt; 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, &quot;\n&quot;)
		return err
	})
}

Assuming you already know how to make custom decorators.

To make a new bar you can do this:

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(&quot;Percentage: &quot;),
			decor.NewPercentage(&quot;%d&quot;),
			decor.Any(part),
			decor.Any(pass),
		),
	)

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

In this example to demonstrative the making new bars I have used New() method but after doing some research I have found 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 Bar instance in method list of the Progress struct in the documentation.

First argument of p.New() is for define a total value for the bar, in this bar it is 100.

Just put your decorators/sections to mpb.PrependDecorators or mpb.AppendDecorators.

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

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

This will respectively show file name, percentage, part and pass:

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

In this example every decoration increases by 1 depending its set value in loop that iterates through total value of the bar. In this bar I just iterated 1 to 100. And I aimed the 100 to 5 parts. When loop variable encounters with one of 5 parts of the 100, the Part decoration of the bar will increase by 1.
And when it comes to 50, the Pass decoration will increase by 1.

for i := 0; i &lt; 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 &lt; 100 {
				piece++
			}

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

			if i &lt; 100 {
				way++
			}

			way2++
		}

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

		if i &lt; 100 {
			bar.Increment()
		}
		bar2.Increment()
	}

This loop will work for second bar too. I just combined two bars' increment status in single loop to make the code shorter. If the loop variable less than 100 increase first bar's status. When it is 100, complete increase all decorations to its total then continue increase the status of the second bar until loop finish. After loop finish complete rest of second bar's increments to total.

way2 = 4
piece2 = 10

The second bar is 10 parts and 4 passes and total value is 200.

And output of the whole code is:

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

huangapple
  • 本文由 发表于 2023年5月14日 18:35:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76246997.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定