mirror of https://github.com/cjbassi/gotop.git
Change disk widget into a table
This commit is contained in:
parent
ee1c57c6fe
commit
00e866c104
|
@ -17,7 +17,7 @@
|
|||
branch = "master"
|
||||
name = "github.com/cjbassi/termui"
|
||||
packages = ["."]
|
||||
revision = "50e37fc8a80ca08ddca62be4537c7d49261994a3"
|
||||
revision = "87dbce7f69558bede7c7c7a51324ce4d8cfcb87b"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
|
|
|
@ -19,6 +19,25 @@ func BytesToGB(b uint64) float64 {
|
|||
return float64(b) / math.Pow10(9)
|
||||
}
|
||||
|
||||
func ConvertBytes(b uint64) (float64, string) {
|
||||
if b >= 1000000000 {
|
||||
return BytesToGB(uint64(b)), "GB"
|
||||
} else if b >= 1000000 {
|
||||
return BytesToMB(uint64(b)), "MB"
|
||||
} else if b >= 1000 {
|
||||
return BytesToKB(uint64(b)), "KB"
|
||||
} else {
|
||||
return float64(b), "B"
|
||||
}
|
||||
}
|
||||
|
||||
func Max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func Error(issue, diagnostics string) {
|
||||
ui.Close()
|
||||
fmt.Println("Error caught. Exiting program.")
|
||||
|
|
|
@ -2,6 +2,8 @@ package widgets
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cjbassi/gotop/src/utils"
|
||||
|
@ -9,19 +11,33 @@ import (
|
|||
psDisk "github.com/shirou/gopsutil/disk"
|
||||
)
|
||||
|
||||
type Partition struct {
|
||||
Device string
|
||||
Mount string
|
||||
TotalRead uint64
|
||||
TotalWrite uint64
|
||||
CurRead string
|
||||
CurWrite string
|
||||
UsedPercent int
|
||||
Free string
|
||||
}
|
||||
|
||||
type Disk struct {
|
||||
*ui.Gauge
|
||||
fs string // which filesystem to get the disk usage of
|
||||
interval time.Duration
|
||||
*ui.Table
|
||||
interval time.Duration
|
||||
Partitions map[string]*Partition
|
||||
}
|
||||
|
||||
func NewDisk() *Disk {
|
||||
self := &Disk{
|
||||
Gauge: ui.NewGauge(),
|
||||
fs: "/",
|
||||
interval: time.Second * 5,
|
||||
Table: ui.NewTable(),
|
||||
interval: time.Second,
|
||||
Partitions: make(map[string]*Partition),
|
||||
}
|
||||
self.Label = "Disk Usage"
|
||||
self.Header = []string{"Disk", "Mount", "Used", "Free", "R/s", "W/s"}
|
||||
self.Gap = 2
|
||||
self.ColResizer = self.ColResize
|
||||
|
||||
self.update()
|
||||
|
||||
|
@ -36,7 +52,98 @@ func NewDisk() *Disk {
|
|||
}
|
||||
|
||||
func (self *Disk) update() {
|
||||
usage, _ := psDisk.Usage(self.fs)
|
||||
self.Percent = int(usage.UsedPercent)
|
||||
self.Description = fmt.Sprintf(" (%dGB free)", int(utils.BytesToGB(usage.Free)))
|
||||
Partitions, _ := psDisk.Partitions(false)
|
||||
|
||||
// add partition if it's new
|
||||
for _, Part := range Partitions {
|
||||
device := strings.Replace(Part.Device, "/dev/", "", -1)
|
||||
if _, ok := self.Partitions[device]; !ok {
|
||||
self.Partitions[device] = &Partition{
|
||||
Device: device,
|
||||
Mount: Part.Mountpoint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete a partition if it no longer exists
|
||||
todelete := []string{}
|
||||
for key, _ := range self.Partitions {
|
||||
exists := false
|
||||
for _, Part := range Partitions {
|
||||
device := strings.Replace(Part.Device, "/dev/", "", -1)
|
||||
if key == device {
|
||||
exists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exists {
|
||||
todelete = append(todelete, key)
|
||||
}
|
||||
}
|
||||
for _, val := range todelete {
|
||||
delete(self.Partitions, val)
|
||||
}
|
||||
|
||||
// updates partition info
|
||||
for _, Part := range self.Partitions {
|
||||
usage, _ := psDisk.Usage(Part.Mount)
|
||||
Part.UsedPercent = int(usage.UsedPercent)
|
||||
|
||||
Free, Mag := utils.ConvertBytes(usage.Free)
|
||||
Part.Free = fmt.Sprintf("%3d%s", uint64(Free), Mag)
|
||||
|
||||
ret, _ := psDisk.IOCounters("/dev/" + Part.Device)
|
||||
data := ret[Part.Device]
|
||||
curRead, curWrite := data.ReadBytes, data.WriteBytes
|
||||
if Part.TotalRead != 0 { // if this isn't the first update
|
||||
readRecent := curRead - Part.TotalRead
|
||||
writeRecent := curWrite - Part.TotalWrite
|
||||
|
||||
readFloat, unitRead := utils.ConvertBytes(readRecent)
|
||||
writeFloat, unitWrite := utils.ConvertBytes(writeRecent)
|
||||
readRecent, writeRecent = uint64(readFloat), uint64(writeFloat)
|
||||
Part.CurRead = fmt.Sprintf("%d%s", readRecent, unitRead)
|
||||
Part.CurWrite = fmt.Sprintf("%d%s", writeRecent, unitWrite)
|
||||
} else {
|
||||
Part.CurRead = fmt.Sprintf("%d%s", 0, "B")
|
||||
Part.CurWrite = fmt.Sprintf("%d%s", 0, "B")
|
||||
}
|
||||
Part.TotalRead, Part.TotalWrite = curRead, curWrite
|
||||
}
|
||||
|
||||
// converts self.Partitions into self.Rows which is a [][]String
|
||||
sortedPartitions := []string{}
|
||||
for seriesName := range self.Partitions {
|
||||
sortedPartitions = append(sortedPartitions, seriesName)
|
||||
}
|
||||
sort.Strings(sortedPartitions)
|
||||
|
||||
self.Rows = make([][]string, len(self.Partitions))
|
||||
for i, key := range sortedPartitions {
|
||||
Part := self.Partitions[key]
|
||||
self.Rows[i] = make([]string, 6)
|
||||
self.Rows[i][0] = Part.Device
|
||||
self.Rows[i][1] = Part.Mount
|
||||
self.Rows[i][2] = fmt.Sprintf("%d%%", Part.UsedPercent)
|
||||
self.Rows[i][3] = Part.Free
|
||||
self.Rows[i][4] = Part.CurRead
|
||||
self.Rows[i][5] = Part.CurWrite
|
||||
}
|
||||
}
|
||||
|
||||
// ColResize overrides the default ColResize in the termui table.
|
||||
func (self *Disk) ColResize() {
|
||||
self.ColWidths = []int{
|
||||
4,
|
||||
utils.Max(5, self.X-33),
|
||||
4, 5, 5, 5,
|
||||
}
|
||||
|
||||
self.CellXPos = []int{}
|
||||
cur := 1
|
||||
for _, w := range self.ColWidths {
|
||||
self.CellXPos = append(self.CellXPos, cur)
|
||||
cur += w
|
||||
cur += self.Gap
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,13 +75,11 @@ func (self *Net) update() {
|
|||
self.prevRecvTotal = curRecvTotal
|
||||
self.prevSentTotal = curSentTotal
|
||||
|
||||
// net widget titles
|
||||
// render widget titles
|
||||
for i := 0; i < 2; i++ {
|
||||
var method string // either 'Rx' or 'Tx'
|
||||
var total float64
|
||||
recent := self.Lines[i].Data[len(self.Lines[i].Data)-1]
|
||||
unitTotal := "B"
|
||||
unitRecent := "B"
|
||||
|
||||
if i == 0 {
|
||||
total = float64(curRecvTotal)
|
||||
|
@ -91,21 +89,9 @@ func (self *Net) update() {
|
|||
method = "Tx"
|
||||
}
|
||||
|
||||
if recent >= 1000000 {
|
||||
recent = int(utils.BytesToMB(uint64(recent)))
|
||||
unitRecent = "MB"
|
||||
} else if recent >= 1000 {
|
||||
recent = int(utils.BytesToKB(uint64(recent)))
|
||||
unitRecent = "kB"
|
||||
}
|
||||
|
||||
if total >= 1000000000 {
|
||||
total = utils.BytesToGB(uint64(total))
|
||||
unitTotal = "GB"
|
||||
} else if total >= 1000000 {
|
||||
total = utils.BytesToMB(uint64(total))
|
||||
unitTotal = "MB"
|
||||
}
|
||||
recentFloat, unitRecent := utils.ConvertBytes(uint64(recent))
|
||||
recent = int(recentFloat)
|
||||
total, unitTotal := utils.ConvertBytes(uint64(total))
|
||||
|
||||
self.Lines[i].Title1 = fmt.Sprintf(" Total %s: %5.1f %s", method, total, unitTotal)
|
||||
self.Lines[i].Title2 = fmt.Sprintf(" %s/s: %9d %2s/s", method, recent, unitRecent)
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cjbassi/gotop/src/utils"
|
||||
ui "github.com/cjbassi/termui"
|
||||
psCPU "github.com/shirou/gopsutil/cpu"
|
||||
)
|
||||
|
@ -27,14 +28,13 @@ type Process struct {
|
|||
|
||||
type Proc struct {
|
||||
*ui.Table
|
||||
cpuCount float64
|
||||
interval time.Duration
|
||||
sortMethod string
|
||||
groupedProcs []Process
|
||||
ungroupedProcs []Process
|
||||
group bool
|
||||
KeyPressed chan bool
|
||||
DefaultColWidths []int
|
||||
cpuCount float64
|
||||
interval time.Duration
|
||||
sortMethod string
|
||||
groupedProcs []Process
|
||||
ungroupedProcs []Process
|
||||
group bool
|
||||
KeyPressed chan bool
|
||||
}
|
||||
|
||||
func NewProc(keyPressed chan bool) *Proc {
|
||||
|
@ -49,8 +49,9 @@ func NewProc(keyPressed chan bool) *Proc {
|
|||
}
|
||||
self.Label = "Processes"
|
||||
self.ColResizer = self.ColResize
|
||||
self.DefaultColWidths = []int{5, 10, 4, 4}
|
||||
self.ColWidths = make([]int, 4)
|
||||
self.Cursor = true
|
||||
self.Gap = 3
|
||||
self.PadLeft = 2
|
||||
|
||||
self.UniqueCol = 0
|
||||
if self.group {
|
||||
|
@ -106,30 +107,8 @@ func (self *Proc) Sort() {
|
|||
|
||||
// ColResize overrides the default ColResize in the termui table.
|
||||
func (self *Proc) ColResize() {
|
||||
copy(self.ColWidths, self.DefaultColWidths)
|
||||
|
||||
self.Gap = 3
|
||||
|
||||
self.CellXPos = []int{
|
||||
self.Gap,
|
||||
self.Gap + self.ColWidths[0] + self.Gap,
|
||||
(self.X + 2) - self.Gap - self.ColWidths[3] - self.Gap - self.ColWidths[2],
|
||||
(self.X + 2) - self.Gap - self.ColWidths[3],
|
||||
}
|
||||
|
||||
rowWidth := self.Gap +
|
||||
self.ColWidths[0] + self.Gap +
|
||||
self.ColWidths[1] + self.Gap +
|
||||
self.ColWidths[2] + self.Gap +
|
||||
self.ColWidths[3] + self.Gap
|
||||
|
||||
// only renders a column if it fits
|
||||
if self.X < (rowWidth - self.Gap - self.ColWidths[3]) {
|
||||
self.ColWidths[2] = 0
|
||||
self.ColWidths[3] = 0
|
||||
} else if self.X < rowWidth {
|
||||
self.CellXPos[2] = self.CellXPos[3]
|
||||
self.ColWidths[3] = 0
|
||||
self.ColWidths = []int{
|
||||
5, utils.Max(self.X-26, 10), 4, 4,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,7 +78,6 @@ func (self *Temp) Buffer() *ui.Buffer {
|
|||
s := ui.MaxString(key, (self.X - 4))
|
||||
buf.SetString(1, y+1, s, self.Fg, self.Bg)
|
||||
buf.SetString(self.X-2, y+1, fmt.Sprintf("%2dC", self.Data[key]), fg, self.Bg)
|
||||
|
||||
}
|
||||
|
||||
return buf
|
||||
|
|
|
@ -16,8 +16,10 @@ type Table struct {
|
|||
CellXPos []int // column position
|
||||
ColResizer func() // for widgets that inherit a Table and want to overload the ColResize method
|
||||
Gap int // gap between columns
|
||||
PadLeft int
|
||||
|
||||
Cursor Color
|
||||
Cursor bool
|
||||
CursorColor Color
|
||||
|
||||
UniqueCol int // the column used to identify the selected item
|
||||
SelectedItem string // used to keep the cursor on the correct item if the data changes
|
||||
|
@ -29,7 +31,7 @@ type Table struct {
|
|||
func NewTable() *Table {
|
||||
self := &Table{
|
||||
Block: NewBlock(),
|
||||
Cursor: Theme.TableCursor,
|
||||
CursorColor: Theme.TableCursor,
|
||||
SelectedRow: 0,
|
||||
TopRow: 0,
|
||||
UniqueCol: 0,
|
||||
|
@ -41,20 +43,6 @@ func NewTable() *Table {
|
|||
// ColResize is the default column resizer, but can be overriden.
|
||||
// ColResize calculates the width of each column.
|
||||
func (self *Table) ColResize() {
|
||||
// calculate gap size based on total width
|
||||
self.Gap = 3
|
||||
if self.X < 50 {
|
||||
self.Gap = 1
|
||||
} else if self.X < 75 {
|
||||
self.Gap = 2
|
||||
}
|
||||
|
||||
cur := 0
|
||||
for _, w := range self.ColWidths {
|
||||
cur += self.Gap
|
||||
self.CellXPos = append(self.CellXPos, cur)
|
||||
cur += w
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer implements the Bufferer interface.
|
||||
|
@ -63,13 +51,25 @@ func (self *Table) Buffer() *Buffer {
|
|||
|
||||
self.ColResizer()
|
||||
|
||||
// finds exact column starting position
|
||||
self.CellXPos = []int{}
|
||||
cur := 1 + self.PadLeft
|
||||
for _, w := range self.ColWidths {
|
||||
self.CellXPos = append(self.CellXPos, cur)
|
||||
cur += w
|
||||
cur += self.Gap
|
||||
}
|
||||
|
||||
// prints header
|
||||
for i, h := range self.Header {
|
||||
width := self.ColWidths[i]
|
||||
if width == 0 {
|
||||
continue
|
||||
}
|
||||
h = MaxString(h, self.X-self.CellXPos[i])
|
||||
// don't render column if it doesn't fit in widget
|
||||
if width > (self.X-self.CellXPos[i])+1 {
|
||||
continue
|
||||
}
|
||||
buf.SetString(self.CellXPos[i], 1, h, self.Fg|AttrBold, self.Bg)
|
||||
}
|
||||
|
||||
|
@ -89,16 +89,18 @@ func (self *Table) Buffer() *Buffer {
|
|||
|
||||
// prints cursor
|
||||
bg := self.Bg
|
||||
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
|
||||
bg = self.Cursor
|
||||
for _, width := range self.ColWidths {
|
||||
if width == 0 {
|
||||
continue
|
||||
if self.Cursor {
|
||||
if (self.SelectedItem == "" && rowNum == self.SelectedRow) || (self.SelectedItem != "" && self.SelectedItem == row[self.UniqueCol]) {
|
||||
bg = self.CursorColor
|
||||
for _, width := range self.ColWidths {
|
||||
if width == 0 {
|
||||
continue
|
||||
}
|
||||
buf.SetString(1, y, strings.Repeat(" ", self.X), self.Fg, bg)
|
||||
}
|
||||
buf.SetString(1, y, strings.Repeat(" ", self.X), self.Fg, bg)
|
||||
self.SelectedItem = row[self.UniqueCol]
|
||||
self.SelectedRow = rowNum
|
||||
}
|
||||
self.SelectedItem = row[self.UniqueCol]
|
||||
self.SelectedRow = rowNum
|
||||
}
|
||||
|
||||
// prints each col of the row
|
||||
|
@ -106,9 +108,9 @@ func (self *Table) Buffer() *Buffer {
|
|||
if width == 0 {
|
||||
continue
|
||||
}
|
||||
width = self.X - self.CellXPos[i]
|
||||
if len(self.CellXPos) > i+1 {
|
||||
width -= self.X - self.CellXPos[i+1]
|
||||
// don't render column if width is greater than distance to end of widget
|
||||
if width > (self.X-self.CellXPos[i])+1 {
|
||||
continue
|
||||
}
|
||||
r := MaxString(row[i], width)
|
||||
buf.SetString(self.CellXPos[i], y, r, self.Fg, bg)
|
||||
|
|
Loading…
Reference in New Issue