Go言語  termdash のBarChart（棒グラフ）の使い方（CPU使用率表示）

# 概要

Goのコンソール画面にグラフなどを表示させるパッケージ`termdash`のWidgetの一つ`BarChart`の説明です。サンプルにはCPUの使用率を`gopsutil`パッケージを使って表示します。


`termdash`は以前「[Go言語 コンソールモードでコンソール上にグラフ、ボタン、スクロールテキストを表示するtermdashの紹介](https://qiita.com/h6591/items/2964ec7c8d8e53d5ac0a)」として投稿しています。

# 実行環境

- Windows10
- Go 1.12.5

使用パッケージ

- termdash  
    - https://github.com/mum4k/termdash
    - BarChart 
        - https://github.com/mum4k/termdash/tree/master/widgets/barchart
        - godoc  
            - https://godoc.org/github.com/mum4k/termdash/widgets/barchart
- gposutil 
    - https://github.com/shirou/gopsutil
   


# BarChartのヘルプ

- **bartchart.New(オプション)**
    - BarChartのWidgetを生成します
- BarChart Widgetのメソッド
    -  **ValueCapacity() int**
        -  画面サイズにフィットする棒グラフの数
        -  これはオプションの **BarGap,BarWidth**に依存します
    -  **Values(値のint型スライス, 最大値, オプション)** 
        -  表示するデータを設定します
        -  **bartchart.New**でのオプションをここでも指定できます
-  オプション
    - **bartchart.BarColors** 
        - 棒グラフの棒の色（スライス）
    - **bartchart.BarGap**
        - 棒の間隔
    - **bartchart.BarWidth**
        - 棒の幅
    - **bartchart.Char**
        - 棒のところに指定文字を積み重ねて表示
        - 出力サイズが一文字分の文字を指定。半角カタカナは可。
    - **bartchart.LabelColors**
        - 棒に下に表示されるそれぞれのラベルの色を指定（スライス）
    - **bartchart.Labels**
        - 棒に下に表示されるそれぞれのラベルを指定（スライス）
    - **bartchart.ShowValues**
        - 棒の値を表示することを指定
    - **bartchart.ValueColors**
        - 棒の値の色を指定（スライス）。 棒の中に表示されるので棒の色とは異なるものにする


# サンプル実装

CPUの使用率のサンプル実装です。色や棒の幅などを指定しない例で、幅を指定しないと画面のサイズに合わせて幅を決めます。また、画面をリサイズするとそれに合わせて、棒の幅が変わります。 `ALL`は各CPUの平均です。実行したPCは４コア、８スレッドです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258007/5a421696-2445-3598-0436-b6dd676a6f44.png)

https://github.com/mum4k/termdash/tree/master/widgets/barchart/barchartdemo
を元に改造しました。

`cpu.PercentWithContex`がたまに`context.DeadlineExceeded`を起こすことがあり対処が方法が分からなかったので再実行にしました。

```go:main.go
package main

import (
	"context"
	"fmt"
	"time"

	"github.com/mum4k/termdash"
	"github.com/mum4k/termdash/container"
	"github.com/mum4k/termdash/linestyle"
	"github.com/mum4k/termdash/terminal/termbox"
	"github.com/mum4k/termdash/terminal/terminalapi"
	"github.com/mum4k/termdash/widgets/barchart"
	"github.com/shirou/gopsutil/cpu"
)

func playBarChart(ctx context.Context, bc *barchart.BarChart, numCPU int, delay time.Duration) {
	ticker := time.NewTicker(delay)
	values := make([]int, numCPU+1)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			cpuPercent, err := cpu.PercentWithContext(ctx, 0, true)
			if err != nil {
				continue
			}
			allCPU := 0.0
			for ix, c := range cpuPercent {
				values[ix+1] = int(c)
				allCPU += c
			}
			values[0] = int(allCPU / float64(numCPU))
			if err := bc.Values(values, 100); err != nil {
				panic(err)
			}
		case <-ctx.Done():
			return
		}
	}
}

func main() {
	term, err := termbox.New()
	if err != nil {
		panic(err)
	}
	defer term.Close()
	ctx, cancel := context.WithCancel(context.Background())
	var cpuPercent []float64
	for {
		cpuPercent, err = cpu.PercentWithContext(ctx, 0, true)
		if err == context.DeadlineExceeded {
			continue
		}
		if err != nil {
			panic(err)
		}
		break
	}
	numCPU := len(cpuPercent)
	cpuLabel := make([]string, numCPU+1)
	cpuLabel[0] = "ALL"
	for ix := range cpuPercent {
		cpuLabel[ix+1] = fmt.Sprintf("CPU%d", ix)
	}
	bchart, err := barchart.New(
		barchart.ShowValues(),
		barchart.Labels(cpuLabel),
	)
	if err != nil {
		panic(err)
	}
	go playBarChart(ctx, bchart, numCPU, 1*time.Second)

	cont, err := container.New(
		term,
		container.Border(linestyle.Light),
		container.BorderTitle("PRESS Q TO QUIT"),
		container.PlaceWidget(bchart),
	)
	if err != nil {
		panic(err)
	}
	quitter := func(k *terminalapi.Keyboard) {
		if k.Key == 'q' || k.Key == 'Q' {
			cancel()
		}
	}
	if err := termdash.Run(ctx, term, cont,
		termdash.KeyboardSubscriber(quitter)); err != nil {
		panic(err)
	}
}
```

## 色指定
色は８色ですから背景の黒を除いて７色になります。または最適な値の色と棒の色の組み合わせは分からないので適当です。ラベルと棒を同じ色にしています。 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258007/022d022c-7a2e-1f37-ab09-91fa13ced956.png)


変更箇所付近だけ記載しました。　`//追加`が具体的な変更箇所です。

```go:main.go
//追加
var colors = []cell.Color{
	cell.ColorRed,
	cell.ColorGreen,
	cell.ColorYellow,
	cell.ColorBlue,
	cell.ColorMagenta,
	cell.ColorCyan,
	cell.ColorWhite,
}


func main() {

//**********************************************

	numCPU := len(cpuPercent)
	cpuLabel := make([]string, numCPU+1)
	barColors := make([]cell.Color, numCPU+1)   //追加
	valColors := make([]cell.Color, numCPU+1)   //追加
	labelColors := make([]cell.Color, numCPU+1) //追加
	cpuLabel[0] = "ALL"
	barColors[0] = colors[0]      //追加
	valColors[0] = colors[1]      //追加
	labelColors[0] = barColors[0] //追加
	for ix := range cpuPercent {
		cpuLabel[ix+1] = fmt.Sprintf("CPU%d", ix)
		barColors[ix+1] = colors[(ix+1)%len(colors)] //追加
		valColors[ix+1] = colors[(ix+2)%len(colors)] //追加
		labelColors[ix+1] = barColors[ix+1]          //追加
	}
	bchart, err := barchart.New(
		barchart.ShowValues(),
		barchart.Labels(cpuLabel),
		barchart.BarColors(barColors),     //追加
		barchart.ValueColors(valColors),   //追加
		barchart.LabelColors(labelColors), //追加
	)

//**********************************************

}
```