termplot 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/README.md +124 -54
- data/Rakefile +27 -14
- data/doc/dash.png +0 -0
- data/doc/demo.png +0 -0
- data/doc/file.png +0 -0
- data/doc/memory.png +0 -0
- data/doc/ping.png +0 -0
- data/doc/sin.png +0 -0
- data/doc/tcp.png +0 -0
- data/examples/sample.rb +17 -0
- data/lib/termplot/character_map.rb +15 -4
- data/lib/termplot/cli.rb +16 -3
- data/lib/termplot/colors.rb +7 -0
- data/lib/termplot/commands.rb +27 -0
- data/lib/termplot/consumers.rb +12 -0
- data/lib/termplot/consumers/base_consumer.rb +132 -0
- data/lib/termplot/consumers/command_consumer.rb +14 -0
- data/lib/termplot/consumers/multi_source_consumer.rb +33 -0
- data/lib/termplot/consumers/single_source_consumer.rb +36 -0
- data/lib/termplot/consumers/stdin_consumer.rb +11 -0
- data/lib/termplot/cursors/buffered_console_cursor.rb +1 -1
- data/lib/termplot/cursors/virtual_cursor.rb +4 -0
- data/lib/termplot/dsl/panels.rb +80 -0
- data/lib/termplot/dsl/widgets.rb +128 -0
- data/lib/termplot/file_config.rb +37 -0
- data/lib/termplot/message_broker.rb +108 -0
- data/lib/termplot/options.rb +100 -20
- data/lib/termplot/positioned_widget.rb +8 -0
- data/lib/termplot/producer_options.rb +3 -0
- data/lib/termplot/producers.rb +3 -3
- data/lib/termplot/producers/base_producer.rb +12 -15
- data/lib/termplot/producers/command_producer.rb +25 -9
- data/lib/termplot/producers/stdin_producer.rb +1 -4
- data/lib/termplot/renderable.rb +35 -0
- data/lib/termplot/renderer.rb +16 -257
- data/lib/termplot/renderers.rb +6 -0
- data/lib/termplot/renderers/border_renderer.rb +48 -0
- data/lib/termplot/renderers/text_renderer.rb +73 -0
- data/lib/termplot/shell.rb +13 -9
- data/lib/termplot/utils/ansi_safe_string.rb +68 -0
- data/lib/termplot/version.rb +1 -1
- data/lib/termplot/widget_dsl.rb +130 -0
- data/lib/termplot/widgets.rb +8 -0
- data/lib/termplot/widgets/base_widget.rb +79 -0
- data/lib/termplot/widgets/border.rb +6 -0
- data/lib/termplot/widgets/dataset.rb +50 -0
- data/lib/termplot/widgets/histogram_widget.rb +196 -0
- data/lib/termplot/widgets/statistics.rb +21 -0
- data/lib/termplot/widgets/statistics_widget.rb +104 -0
- data/lib/termplot/widgets/time_series_widget.rb +248 -0
- data/lib/termplot/window.rb +25 -5
- data/termplot.gemspec +1 -6
- metadata +36 -24
- data/doc/MSFT.png +0 -0
- data/doc/cpu.png +0 -0
- data/doc/demo.cast +0 -638
- data/lib/termplot/consumer.rb +0 -75
- data/lib/termplot/cursors/console_cursor.rb +0 -57
- data/lib/termplot/series.rb +0 -37
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Termplot
|
4
|
+
module Consumers
|
5
|
+
autoload :BaseConsumer, "termplot/consumers/base_consumer"
|
6
|
+
autoload :MultiSourceConsumer, "termplot/consumers/multi_source_consumer"
|
7
|
+
autoload :SingleSourceConsumer, "termplot/consumers/single_source_consumer"
|
8
|
+
|
9
|
+
autoload :StdinConsumer, "termplot/consumers/stdin_consumer"
|
10
|
+
autoload :CommandConsumer, "termplot/consumers/command_consumer"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "termplot/renderer"
|
4
|
+
require "termplot/message_broker"
|
5
|
+
require "termplot/shell"
|
6
|
+
|
7
|
+
module Termplot
|
8
|
+
module Consumers
|
9
|
+
class BaseConsumer
|
10
|
+
def initialize(options)
|
11
|
+
@options = options
|
12
|
+
@broker_pool = MessageBrokerPool.new
|
13
|
+
@producer_pool = ProducerPool.new
|
14
|
+
@renderer = Renderer.new(
|
15
|
+
cols: options.cols,
|
16
|
+
rows: options.rows,
|
17
|
+
widgets: positioned_widgets,
|
18
|
+
debug: options.debug
|
19
|
+
)
|
20
|
+
|
21
|
+
@renderer_thread = RendererThread.new(
|
22
|
+
renderer: renderer,
|
23
|
+
broker_pool: broker_pool
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run
|
28
|
+
register_producers_and_brokers
|
29
|
+
broker_pool.on_message { renderer_thread.continue }
|
30
|
+
|
31
|
+
Shell.init(clear: options.full_screen)
|
32
|
+
renderer_thread.start
|
33
|
+
|
34
|
+
# Blocks main thread
|
35
|
+
producer_pool.start_and_block
|
36
|
+
|
37
|
+
# At this point producer threads have all exited, tell renderer to
|
38
|
+
# consume all messages left on the queue
|
39
|
+
renderer_thread.continue while !broker_pool.empty?
|
40
|
+
|
41
|
+
# Close queues
|
42
|
+
broker_pool.shutdown
|
43
|
+
|
44
|
+
# Shutdown renderer
|
45
|
+
renderer_thread.join
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
attr_reader :options,
|
50
|
+
:broker_pool,
|
51
|
+
:producer_pool,
|
52
|
+
:renderer,
|
53
|
+
:renderer_thread
|
54
|
+
|
55
|
+
class RendererThread
|
56
|
+
def initialize(renderer:, broker_pool:, &block)
|
57
|
+
@renderer = renderer
|
58
|
+
@broker_pool = broker_pool
|
59
|
+
@block = block
|
60
|
+
@thread = nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def start
|
64
|
+
@thread = Thread.new do
|
65
|
+
# Pause and wait to be woken for rendering
|
66
|
+
pause
|
67
|
+
while !broker_pool.closed?
|
68
|
+
num_samples = broker_pool.pending_message_count
|
69
|
+
|
70
|
+
if num_samples.zero?
|
71
|
+
pause
|
72
|
+
else
|
73
|
+
broker_pool.flush_messages
|
74
|
+
|
75
|
+
if num_samples > 0
|
76
|
+
renderer.render
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def continue
|
84
|
+
thread.run
|
85
|
+
end
|
86
|
+
|
87
|
+
def pause
|
88
|
+
Thread.stop
|
89
|
+
end
|
90
|
+
|
91
|
+
def join
|
92
|
+
thread.join unless thread.stop?
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
attr_reader :renderer, :broker_pool, :block, :thread
|
97
|
+
end
|
98
|
+
|
99
|
+
class ProducerPool
|
100
|
+
def initialize
|
101
|
+
@producers = []
|
102
|
+
@threads = []
|
103
|
+
end
|
104
|
+
|
105
|
+
def start
|
106
|
+
# Run Producers with producers
|
107
|
+
@threads = producers.map do |producer|
|
108
|
+
Thread.new do
|
109
|
+
producer.run
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def wait
|
115
|
+
threads.each(&:join)
|
116
|
+
end
|
117
|
+
|
118
|
+
def start_and_block
|
119
|
+
start
|
120
|
+
wait
|
121
|
+
end
|
122
|
+
|
123
|
+
def add_producer(producer)
|
124
|
+
producers.push(producer)
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
attr_reader :producers, :threads
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
|
2
|
+
require "termplot/positioned_widget"
|
3
|
+
require "termplot/widgets"
|
4
|
+
require "termplot/producers"
|
5
|
+
|
6
|
+
module Termplot
|
7
|
+
module Consumers
|
8
|
+
class CommandConsumer < SingleSourceConsumer
|
9
|
+
def producer_class
|
10
|
+
Termplot::Producers::CommandProducer
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "termplot/file_config"
|
4
|
+
require "termplot/producers"
|
5
|
+
|
6
|
+
module Termplot
|
7
|
+
module Consumers
|
8
|
+
class MultiSourceConsumer < BaseConsumer
|
9
|
+
def positioned_widgets
|
10
|
+
@positioned_widgets ||= config.positioned_widgets
|
11
|
+
end
|
12
|
+
|
13
|
+
def register_producers_and_brokers
|
14
|
+
config.widget_configs.each do |widget_config|
|
15
|
+
producer = build_producer(widget_config)
|
16
|
+
broker_pool.broker(sender: producer, receiver: widget_config.widget)
|
17
|
+
producer_pool.add_producer(producer)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def config
|
23
|
+
@config ||= FileConfig.new(options).parse_config
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_producer(widget_config)
|
27
|
+
Termplot::Producers::CommandProducer.new(
|
28
|
+
widget_config.producer_options
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
|
2
|
+
require "termplot/positioned_widget"
|
3
|
+
require "termplot/widgets"
|
4
|
+
require "termplot/producers"
|
5
|
+
|
6
|
+
module Termplot
|
7
|
+
module Consumers
|
8
|
+
class SingleSourceConsumer < BaseConsumer
|
9
|
+
def positioned_widgets
|
10
|
+
@positioned_widgets ||= [PositionedWidget.new(row: 0, col: 0, widget: widget)]
|
11
|
+
end
|
12
|
+
|
13
|
+
def register_producers_and_brokers
|
14
|
+
producer = build_producer
|
15
|
+
broker_pool.broker(sender: producer, receiver: widget)
|
16
|
+
producer_pool.add_producer(producer)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def widget
|
21
|
+
return @widget if defined? @widget
|
22
|
+
wigdet_classes = {
|
23
|
+
"timeseries" => "Termplot::Widgets::TimeSeriesWidget",
|
24
|
+
"stats" => "Termplot::Widgets::StatisticsWidget",
|
25
|
+
"hist" => "Termplot::Widgets::HistogramWidget",
|
26
|
+
}
|
27
|
+
klass = Object.const_get(wigdet_classes[options.type])
|
28
|
+
@widget = klass.new(options.to_h)
|
29
|
+
end
|
30
|
+
|
31
|
+
def build_producer
|
32
|
+
producer_class.new(options.producer_options)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require "termplot/commands"
|
2
|
+
require "termplot/dsl/widgets"
|
3
|
+
|
4
|
+
module Termplot
|
5
|
+
module DSL
|
6
|
+
class Panel
|
7
|
+
include Termplot::Commands
|
8
|
+
include Termplot::WidgetDSL
|
9
|
+
|
10
|
+
attr_accessor(
|
11
|
+
:rows,
|
12
|
+
:cols,
|
13
|
+
:start_row,
|
14
|
+
:start_col
|
15
|
+
)
|
16
|
+
|
17
|
+
def initialize(options, children = [])
|
18
|
+
@options = options
|
19
|
+
@children = children
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_dimensions(rows, cols, start_row, start_col)
|
23
|
+
raise "Must be implemented"
|
24
|
+
end
|
25
|
+
|
26
|
+
def flatten
|
27
|
+
children.map(&:flatten).flatten
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
attr_reader :options, :children
|
32
|
+
|
33
|
+
def row(&block)
|
34
|
+
new_row = Row.new(options)
|
35
|
+
new_row.instance_eval(&block)
|
36
|
+
children.push(new_row)
|
37
|
+
end
|
38
|
+
|
39
|
+
def col(&block)
|
40
|
+
new_col = Col.new(options)
|
41
|
+
new_col.instance_eval(&block)
|
42
|
+
children.push(new_col)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class Row < Panel
|
47
|
+
def set_dimensions(rows, cols, start_row, start_col)
|
48
|
+
@rows = rows
|
49
|
+
@cols = cols
|
50
|
+
child_cols = cols / children.count
|
51
|
+
|
52
|
+
children.each_with_index do |child, index|
|
53
|
+
child.set_dimensions(
|
54
|
+
rows,
|
55
|
+
child_cols,
|
56
|
+
start_row,
|
57
|
+
child_cols * index
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Col < Panel
|
64
|
+
def set_dimensions(rows, cols, start_row, start_col)
|
65
|
+
@rows = rows
|
66
|
+
@cols = cols
|
67
|
+
child_rows = rows / children.count
|
68
|
+
|
69
|
+
children.each_with_index do |child, index|
|
70
|
+
child.set_dimensions(
|
71
|
+
child_rows,
|
72
|
+
cols,
|
73
|
+
child_rows * index,
|
74
|
+
start_col
|
75
|
+
)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require "termplot/positioned_widget"
|
2
|
+
require "termplot/widgets"
|
3
|
+
|
4
|
+
module Termplot
|
5
|
+
module WidgetDSL
|
6
|
+
def timeseries(attrs)
|
7
|
+
attrs = merge_defaults(attrs)
|
8
|
+
children.push(TimeSeriesConfig.new(attrs))
|
9
|
+
end
|
10
|
+
|
11
|
+
def statistics(attrs)
|
12
|
+
attrs = merge_defaults(attrs)
|
13
|
+
children.push(StatisticsConfig.new(attrs))
|
14
|
+
end
|
15
|
+
|
16
|
+
def histogram(attrs)
|
17
|
+
attrs = merge_defaults(attrs)
|
18
|
+
children.push(HistogramConfig.new(attrs))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def merge_defaults(attrs)
|
24
|
+
Termplot::Options.default_options.merge(attrs)
|
25
|
+
end
|
26
|
+
|
27
|
+
class WidgetConfig
|
28
|
+
attr_reader(
|
29
|
+
:title,
|
30
|
+
:command,
|
31
|
+
:interval,
|
32
|
+
:col,
|
33
|
+
:row,
|
34
|
+
:cols,
|
35
|
+
:rows,
|
36
|
+
:debug
|
37
|
+
)
|
38
|
+
|
39
|
+
def initialize(opts)
|
40
|
+
@title = opts[:title]
|
41
|
+
|
42
|
+
@command = opts[:command]
|
43
|
+
@interval = opts[:interval]
|
44
|
+
@debug = opts[:debug]
|
45
|
+
|
46
|
+
post_initialize(opts)
|
47
|
+
end
|
48
|
+
|
49
|
+
def post_initialize(opts)
|
50
|
+
# Implemented in subclasses
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_dimensions(rows, cols, start_row, start_col)
|
54
|
+
@rows = rows
|
55
|
+
@cols = cols
|
56
|
+
@row = start_row
|
57
|
+
@col = start_col
|
58
|
+
end
|
59
|
+
|
60
|
+
def flatten
|
61
|
+
self
|
62
|
+
end
|
63
|
+
|
64
|
+
def positioned_widget
|
65
|
+
@positioned_widget ||= PositionedWidget.new(
|
66
|
+
row: row,
|
67
|
+
col: col,
|
68
|
+
widget: widget
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
def widget
|
73
|
+
raise "Must be implemented"
|
74
|
+
end
|
75
|
+
|
76
|
+
def producer_options
|
77
|
+
ProducerOptions.new(command: command, interval: interval)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class TimeSeriesConfig < WidgetConfig
|
82
|
+
attr_reader :color, :line_style
|
83
|
+
def post_initialize(opts)
|
84
|
+
@color = opts[:color]
|
85
|
+
@line_style = opts[:line_style]
|
86
|
+
end
|
87
|
+
|
88
|
+
def widget
|
89
|
+
@widget ||= Termplot::Widgets::TimeSeriesWidget.new(
|
90
|
+
title: title,
|
91
|
+
line_style: line_style,
|
92
|
+
color: color,
|
93
|
+
cols: cols,
|
94
|
+
rows: rows,
|
95
|
+
debug: debug
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class StatisticsConfig < WidgetConfig
|
101
|
+
def widget
|
102
|
+
@widget ||= Termplot::Widgets::StatisticsWidget.new(
|
103
|
+
title: title,
|
104
|
+
cols: cols,
|
105
|
+
rows: rows,
|
106
|
+
debug: debug
|
107
|
+
)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class HistogramConfig < WidgetConfig
|
112
|
+
attr_reader :color
|
113
|
+
def post_initialize(opts)
|
114
|
+
@color = opts[:color]
|
115
|
+
end
|
116
|
+
|
117
|
+
def widget
|
118
|
+
@widget ||= Termplot::Widgets::HistogramWidget.new(
|
119
|
+
title: title,
|
120
|
+
color: color,
|
121
|
+
cols: cols,
|
122
|
+
rows: rows,
|
123
|
+
debug: debug
|
124
|
+
)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|