sknlinechart

package module
v1.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2023 License: MIT Imports: 14 Imported by: 0

README

SknLineChart

Line chart with 120 horizontal, xscale, divisions displayed. The Y scale is limited to 100 divisions. Written in Go using the Fyne GUI framework.

Display Example

Features

  • Multiple Series of datapoints rendered as a single line
  • Series should be the same color. Each point in this chart accepts a themed color name
  • 120 datapoint are displayed on the x scale of chart, with 100 as the default Y value.
  • More than 120 data points causes the earliest points to be rolled off the screen; each series is independently scrolls
  • Data points can be added at any time, causing the series to possible scroll automatically
  • The 120 x limit will throw and error on creation of the chart, or on the replacement of its active dataset.
  • Data point markers are toggled with mouse button 2
  • Hovering over a data point will show a popup near the mouse pointer, showing series, value, index, and timestamp of data under mouse
  • Mouse button 1 will toggle the sticky datapoint popup
  • Labels are available for all four corners of window, include bottom and top centered titles
  • left and right middle labels can be used as scale descriptions
  • Any label left empty will not be displayed.
  • Horizontal and Vertical chart grid lines can also be turned off/on
SknLineChart Interface
// LineChart feature list
type LineChart interface {
// Chart Attributes
IsDataPointMarkersEnabled() bool // mouse button 2 toggles
IsHorizGridLinesEnabled() bool
IsVertGridLinesEnabled() bool
IsMousePointDisplayEnabled() bool // hoverable and mouse button one

SetDataPointMarkers(enable bool)
SetHorizGridLines(enable bool)
SetVertGridLines(enable bool)
SetMousePointDisplay(enable bool)

// Info labels
GetTopLeftLabel() string
GetTitle() string
GetTopRightLabel() string

// Scale legend
GetMiddleLeftLabel() string
GetMiddleRightLabel() string

// Info Labels
GetBottomLeftLabel() string
GetBottomCenteredLabel() string
GetBottomRightLabel() string

SetTopLeftLabel(newValue string)
SetTitle(newValue string)
SetTopRightLabel(newValue string)
SetMiddleLeftLabel(newValue string)
SetMiddleRightLabel(newValue string)
SetBottomLeftLabel(newValue string)
SetBottomCenteredLabel(newValue string)
SetBottomRightLabel(newValue string)

// ApplyDataSeries add a whole data series at once
// expect this will rarely be used, since loading more than 120 point will raise error
ApplyDataSeries(seriesName string, newSeries []LineChartDatapoint) error

// ApplyDataPoint primary method to add another data point to any series
// If series has more than 120 points, point 0 will be rolled out making room for this one
ApplyDataPoint(seriesName string, newDataPoint LineChartDatapoint)

}

Fyne Custom Widget Strategy

/*
 * SknLineChart
 * Custom Fyne 2.0 Widget
 * Strategy
 * 1. Define Widget Named/Exported Struct
 *    1. export fields when possible
 * 2. Define Widget Renderer Named/unExported Struct
 *    1. un-exportable fields when possible
 * 3. Define NewWidget() *ExportedStruct method, related interface should have different name.
 *    1. Define state variables for this widget
 *    2. Extend the BaseWidget
 *       1. If coding SetMinSize(fyne.Size), call resize on BaseWidget 
 *    3. Define Widget required methods
 *       1. CreateRenderer() fyne.WidgetRenderer, call newRenderer() below
 *       2. Renderer has the other required methods, like Refresh(), etc.
 *    4. Define any methods required by additional interfaces, like
 *       desktop.Mouseable for mouse button support
 *       1. MouseDown(me MouseEvent)
 *       2. MouseUp(me MouseEvent)
 *       desktop.Hoverable for mouse movement support
 *       1. MouseIn(me MouseEvent)
 *       2. MouseMoved(me MouseEvent)  used to display data point under mouse
 *       3. MouseOut()
 *
 * 4. Define newRenderer() *notExportedStruct method
 *    1. Create canvas objects to be used in display
 *    2. Initialize there content if practical; not required
 *    3. Implement the required WidgetRenderer methods
 * 	  4. Refresh()               call refresh on each object
 * 	  5. Layout(s fyne.Size)     resize & move objects
 * 	  6. MinSize()  fyne.Size    return the minimum size needed
 * 	  7. Object() []fyne.Canvas  return the objects to be displayed
 * 	  8. Destroy()               cleanup if needed to prevent leaks
 * 5. In general state methods are the public api with or without getters/setters
 *    and the renderer creates the displayable objects, applies state/value to them, and
 *    manages their display.
 *
 * Critical Notes:
 * - if using maps, map[string]interface{}, they will require a mutex to prevent concurrency error cause my concurrent read/writes.
 * - data binding creates new var from source var, and its the new var that should be shared as it is synchronized to changes in bond data var.
 */
Example
package main

import (
	"fmt"
	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"
	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/theme"
	"github.com/skoona/sknlinechart"
	"math/rand"
	"os"
	"os/signal"
	"syscall"
	"time"
)

func makeChart(title, footer string) (*sknlinechart.LineChartSkn, error) {
	dataPoints := map[string][]sknlinechart.LineChartDatapoint{} // legend, points
	var first, second []sknlinechart.LineChartDatapoint

	rand.NewSource(50.0)
	for x := 1; x < 125; x++ {
		val := rand.Float32() * 100.0
		if val > 30.0 {
			val = 30.0
		} else if val < 5.0 {
			val = 5.0
		}
		first = append(first, sknlinechart.NewLineChartDatapoint(
			val,
			theme.ColorOrange,
			time.Now().Format(time.RFC3339)))
	}
	for x := 1; x < 75; x++ {
		val := rand.Float32() * 40.0
		if val > 60.0 {
			val = 60.0
		} else if val < 35.0 {
			val = 35.0
		}
		second = append(second, sknlinechart.NewLineChartDatapoint(
			val,
			theme.ColorRed,
			time.Now().Format(time.RFC3339)))
	}

	dataPoints["first"] = first
	dataPoints["second"] = second

	lineChart, err := sknlinechart.NewLineChart("Skoona Line Chart", "Example Time Series", &dataPoints)
	if err != nil {
		fmt.Println(err.Error())
	}
	sknlinechart.DebugLoggingEnabled = true

	return lineChart, err
}

func main() {
	windowClosed := false

	gui := app.NewWithID("net.skoona.sknLineChart")
	w := gui.NewWindow("Custom Widget Development")

	w.SetOnClosed(func() {
		windowClosed = true
		fmt.Println("::main() Window Closed")
		time.Sleep(2 * time.Second)
	})

	lineChart, err := makeChart("Skoona Line Chart", "Example Time Series")

	go (func(chart sknlinechart.SknLineChart) {
		var many []sknlinechart.LineChartDatapoint
		for x := 1; x < 120; x++ {
			val := rand.Float32() * 75.0
			if val > 90.0 {
				val = 90.0
			} else if val < 65.0 {
				val = 65.0
			}
			many = append(many, sknlinechart.NewLineChartDatapoint(
				val,
				theme.ColorPurple,
				time.Now().Format(time.RFC3339)))
		}
		time.Sleep(10 * time.Second)
		err = lineChart.ApplyDataSeries("many", many)
		if err != nil {
			fmt.Println("ApplyDataSeries", err.Error())
		}
		time.Sleep(time.Second)
		for i := 0; i < 150; i++ {
			if windowClosed {
				break
			}
			chart.ApplyDataPoint("steady", sknlinechart.NewLineChartDatapoint(
				rand.Float32()*110.0,
				theme.ColorYellow,
				time.Now().Format(time.RFC3339)))
			if windowClosed {
				break
			}
			time.Sleep(time.Second)
		}
	})(lineChart)

	w.Resize(fyne.NewSize(982, 452))
	w.SetContent(container.NewPadded(lineChart))

	go func(w *fyne.Window) {
		systemSignalChannel := make(chan os.Signal, 1)
		signal.Notify(systemSignalChannel, syscall.SIGINT, syscall.SIGTERM)
		sig := <-systemSignalChannel // wait on ctrl-c
		windowClosed = true
		fmt.Println("Signal Received: ", sig.String())
		(*w).Close()
	}(&w)

	w.ShowAndRun()
	time.Sleep(1 * time.Second)
}

Project Layout
├── LICENSE
├── README.md
├── cmd
│   └── sknlinechart
│       └── main.go
├── go.mod
├── go.sum
└── skn
    └── linechart
        ├── datapoint.go
        ├── interfaces.go
        ├── linechart.go
        └── mapsliceutils.go

Contributing
  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request
LICENSE

The application is available as open source under the terms of the MIT License.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	DebugLoggingEnabled = false
)

Functions

func RemoveIndexFromSlice

func RemoveIndexFromSlice[K comparable](index int, slice []K) []K

RemoveIndexFromSlice remove the given index from any type of slice

func ShiftSlice

func ShiftSlice[K comparable](newData K, slice []K) []K

ShiftSlice drops index 0 and append newData to any type of slice

Types

type LineChartDatapoint added in v1.2.5

type LineChartDatapoint interface {
	Value() float32
	SetValue(y float32)

	ColorName() string
	SetColorName(n string)

	Timestamp() string
	SetTimestamp(t string)

	// MarkerPosition internal use only: current data point marker location
	MarkerPosition() (*fyne.Position, *fyne.Position)
	// SetMarkerPosition internal use only: sace location of where data point marker is located
	SetMarkerPosition(top *fyne.Position, bottom *fyne.Position)
}

LineChartDatapoint data container interface for SknLineChart

func NewLineChartDatapoint added in v1.2.5

func NewLineChartDatapoint(value float32, colorName, timestamp string) LineChartDatapoint

type LineChartSkn

type LineChartSkn struct {
	widget.BaseWidget // Inherit from BaseWidget
	desktop.Hoverable // support mouse tracking
	desktop.Mouseable // Mouse Clicks

	DataPointXLimit         int
	EnableDataPointMarkers  bool
	EnableHorizGridLines    bool
	EnableVertGridLines     bool
	EnableMousePointDisplay bool
	TopLeftLabel            string // The text to display in the widget
	TopCenteredLabel        string
	TopRightLabel           string
	LeftMiddleLabel         string
	RightMiddleLabel        string
	BottomLeftLabel         string
	BottomCenteredLabel     string
	BottomRightLabel        string

	// Private: Exposed for Testing; DO NOT USE
	ObjectsCache []fyne.CanvasObject
	// contains filtered or unexported fields
}

LineChartSkn widget to display multiple series of data points which will roll off older point beyond the 120 point limit.

func NewLineChart

func NewLineChart(topTitle, bottomTitle string, dataPoints *map[string][]LineChartDatapoint) (*LineChartSkn, error)

NewLineChart Create the Line Chart be careful not to exceed the series data point limit, which defaults to 120

can return a valid chart object and an error object; errors really should be handled and are caused by data points exceeding the container limit of 120; they will be truncated

func (*LineChartSkn) ApplyDataPoint

func (w *LineChartSkn) ApplyDataPoint(seriesName string, newDataPoint LineChartDatapoint)

ApplyDataPoint adds a new datapoint to an existing series will shift out the oldest point if containers limit is exceeded

func (*LineChartSkn) ApplyDataSeries

func (w *LineChartSkn) ApplyDataSeries(seriesName string, newSeries []LineChartDatapoint) error

ApplyDataSeries adds a new series of data to existing chart set. throws error if new series exceeds containers point limit

func (*LineChartSkn) CreateRenderer

func (w *LineChartSkn) CreateRenderer() fyne.WidgetRenderer

CreateRenderer Create the renderer. This is called by the fyne application

func (*LineChartSkn) GetBottomCenteredLabel

func (w *LineChartSkn) GetBottomCenteredLabel() string

GetBottomCenteredLabel returns text of bottom center label

func (*LineChartSkn) GetBottomLeftLabel

func (w *LineChartSkn) GetBottomLeftLabel() string

GetBottomLeftLabel returns text of bottom left label

func (*LineChartSkn) GetBottomRightLabel

func (w *LineChartSkn) GetBottomRightLabel() string

GetBottomRightLabel returns text of bottom right label

func (*LineChartSkn) GetMiddleLeftLabel

func (w *LineChartSkn) GetMiddleLeftLabel() string

GetMiddleLeftLabel returns text of middle left label

func (*LineChartSkn) GetMiddleRightLabel

func (w *LineChartSkn) GetMiddleRightLabel() string

GetMiddleRightLabel returns text of middle right label

func (*LineChartSkn) GetTitle

func (w *LineChartSkn) GetTitle() string

GetTitle return text of the chart's title from top center

func (*LineChartSkn) GetTopLeftLabel

func (w *LineChartSkn) GetTopLeftLabel() string

GetTopLeftLabel return text from top left label

func (*LineChartSkn) GetTopRightLabel

func (w *LineChartSkn) GetTopRightLabel() string

GetTopRightLabel returns text of top right label

func (*LineChartSkn) IsDataPointMarkersEnabled

func (w *LineChartSkn) IsDataPointMarkersEnabled() bool

IsDataPointMarkersEnabled returns state of chart's use of data point markers on series data

func (*LineChartSkn) IsHorizGridLinesEnabled

func (w *LineChartSkn) IsHorizGridLinesEnabled() bool

IsHorizGridLinesEnabled returns state of chart's display of horizontal grid line

func (*LineChartSkn) IsMousePointDisplayEnabled

func (w *LineChartSkn) IsMousePointDisplayEnabled() bool

IsMousePointDisplayEnabled return state of mouse popups when hovered over a chart datapoint

func (*LineChartSkn) IsVertGridLinesEnabled

func (w *LineChartSkn) IsVertGridLinesEnabled() bool

IsVertGridLinesEnabled returns state of chart's display of vertical grid line

func (*LineChartSkn) MouseDown added in v1.2.5

func (w *LineChartSkn) MouseDown(me *desktop.MouseEvent)

MouseDown btn.2 toggles markers, btn.1 toggles mouse point display

func (*LineChartSkn) MouseIn

func (w *LineChartSkn) MouseIn(*desktop.MouseEvent)

MouseIn unused interface method

func (*LineChartSkn) MouseMoved

func (w *LineChartSkn) MouseMoved(me *desktop.MouseEvent)

MouseMoved interface method to discover which data point is under mouse

func (*LineChartSkn) MouseOut

func (w *LineChartSkn) MouseOut()

MouseOut disable display of mouse data point display

func (*LineChartSkn) MouseUp added in v1.2.5

func (w *LineChartSkn) MouseUp(*desktop.MouseEvent)

MouseUp unused interface method

func (*LineChartSkn) SetBottomCenteredLabel

func (w *LineChartSkn) SetBottomCenteredLabel(newValue string)

SetBottomCenteredLabel changes displayed text, empty disables display

func (*LineChartSkn) SetBottomLeftLabel

func (w *LineChartSkn) SetBottomLeftLabel(newValue string)

SetBottomLeftLabel changes displayed text, empty disables display

func (*LineChartSkn) SetBottomRightLabel

func (w *LineChartSkn) SetBottomRightLabel(newValue string)

SetBottomRightLabel changes displayed text, empty disables display

func (*LineChartSkn) SetDataPointMarkers

func (w *LineChartSkn) SetDataPointMarkers(enable bool)

SetDataPointMarkers enables data point markers on display series points

func (*LineChartSkn) SetHorizGridLines

func (w *LineChartSkn) SetHorizGridLines(enable bool)

SetHorizGridLines enables chart horizontal grid lines

func (*LineChartSkn) SetMiddleLeftLabel

func (w *LineChartSkn) SetMiddleLeftLabel(newValue string)

SetMiddleLeftLabel changes displayed text, empty disables display

func (*LineChartSkn) SetMiddleRightLabel

func (w *LineChartSkn) SetMiddleRightLabel(newValue string)

SetMiddleRightLabel changes displayed text, empty disables display

func (*LineChartSkn) SetMinSize

func (w *LineChartSkn) SetMinSize(s fyne.Size)

SetMinSize override the default min size of chart

func (*LineChartSkn) SetMousePointDisplay

func (w *LineChartSkn) SetMousePointDisplay(enable bool)

SetMousePointDisplay true/false, enables data point display under mouse pointer

func (*LineChartSkn) SetTitle

func (w *LineChartSkn) SetTitle(newValue string)

SetTitle sets text to be display on chart at top center

func (*LineChartSkn) SetTopLeftLabel

func (w *LineChartSkn) SetTopLeftLabel(newValue string)

SetTopLeftLabel sets text to be display on chart at top left

func (*LineChartSkn) SetTopRightLabel

func (w *LineChartSkn) SetTopRightLabel(newValue string)

SetTopRightLabel changes displayed text, empty disables display

func (*LineChartSkn) SetVertGridLines

func (w *LineChartSkn) SetVertGridLines(enable bool)

SetVertGridLines enables chart vertical grid lines

type SknLineChart added in v1.3.0

type SknLineChart interface {
	// Chart Attributes
	IsDataPointMarkersEnabled() bool // mouse button 2 toggles
	IsHorizGridLinesEnabled() bool
	IsVertGridLinesEnabled() bool
	IsMousePointDisplayEnabled() bool // hoverable and mouse button one

	SetDataPointMarkers(enable bool)
	SetHorizGridLines(enable bool)
	SetVertGridLines(enable bool)
	SetMousePointDisplay(enable bool)

	// Info labels
	GetTopLeftLabel() string
	GetTitle() string
	GetTopRightLabel() string

	// Scale legend
	GetMiddleLeftLabel() string
	GetMiddleRightLabel() string

	// Info Labels
	GetBottomLeftLabel() string
	GetBottomCenteredLabel() string
	GetBottomRightLabel() string

	SetTopLeftLabel(newValue string)
	SetTitle(newValue string)
	SetTopRightLabel(newValue string)
	SetMiddleLeftLabel(newValue string)
	SetMiddleRightLabel(newValue string)
	SetBottomLeftLabel(newValue string)
	SetBottomCenteredLabel(newValue string)
	SetBottomRightLabel(newValue string)

	// ApplyDataSeries add a whole data series at once
	// expect this will rarely be used, since loading more than 120 point will raise error
	ApplyDataSeries(seriesName string, newSeries []LineChartDatapoint) error

	// ApplyDataPoint primary method to add another data point to any series
	// If series has more than 120 points, point 0 will be rolled out making room for this one
	ApplyDataPoint(seriesName string, newDataPoint LineChartDatapoint)
}

SknLineChart feature list

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL