initial commit
parent
f882674c70
commit
1d1190913f
@ -1,2 +1,4 @@
|
|||||||
__pycache__
|
__pycache__
|
||||||
*.pyc
|
*.pyc
|
||||||
|
*.log
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
# Graphing
|
||||||
|
|
||||||
|
![example graph](graph.png)
|
||||||
|
|
||||||
|
A cli graphing tool to plot anything you want
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#! nix-shell -i bash -p python3 python37Packages.matplotlib
|
||||||
|
|
||||||
|
$(dirname $(realpath $0))/graphing.py "$@"
|
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import time
|
||||||
|
import argparse
|
||||||
|
import matplotlib
|
||||||
|
from graphing import *
|
||||||
|
|
||||||
|
# matplotlib setup
|
||||||
|
matplotlib.pyplot.style.use('dark_background')
|
||||||
|
matplotlib.rcParams['toolbar'] = 'None'
|
||||||
|
|
||||||
|
# arg parser
|
||||||
|
parser = argparse.ArgumentParser(description='Visualize data as graphs')
|
||||||
|
parser.add_argument('title', type=str, help="name of the chart", default=None, nargs='?')
|
||||||
|
parser.add_argument('-n', type=int, help="number of records to show, -1 for no limit. Default 50", default=50, dest="samples")
|
||||||
|
parser.add_argument('-l', '--limits', type=str, help="limits for the y-axis. If not set, use best-fit. Example values are 0-10, 5- (starting at 5), or -10 (lower than 10)")
|
||||||
|
parser.add_argument('-b', '--batch', help="batch processing. Do not use timestamp from when they were read from stdin.", action="store_true")
|
||||||
|
parser.add_argument('-i', '--input', type=str, help="read from file rather than stdin. Also enables batch mode and sets samples to -1.")
|
||||||
|
parser.add_argument('-o', '--output', type=str, help="Write final chart to file")
|
||||||
|
parser.add_argument('-f', '--full', help="fullscreen mode - hide all axis labels and title, only display graph.", action="store_true")
|
||||||
|
parser.add_argument('-x', '--read-x-values', help="read value of x-axis from input. Input is now \"<x>, <y>\" tuple (comma separated)", action="store_true", dest="read_x")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# setup batch and sample count for when using input file
|
||||||
|
if args.input:
|
||||||
|
args.batch = True
|
||||||
|
args.samples = -1
|
||||||
|
|
||||||
|
# parse limit arg
|
||||||
|
args.limits = parse_limits(args.limits)
|
||||||
|
|
||||||
|
# instatiate chart with our args
|
||||||
|
chart = Chart(title=args.title, limits=args.limits, batch=args.batch, samples=args.samples, fullscreen=args.full, show=not args.output, read_x=args.read_x)
|
||||||
|
|
||||||
|
|
||||||
|
# final waiting state
|
||||||
|
try:
|
||||||
|
if not args.input:
|
||||||
|
# read args from stdin
|
||||||
|
while_stdin(chart.read_input)
|
||||||
|
else:
|
||||||
|
with open(args.input) as f:
|
||||||
|
for line in f:
|
||||||
|
chart.read_input(line.strip())
|
||||||
|
|
||||||
|
chart.ui_update(final_draw = True)
|
||||||
|
while not args.output:
|
||||||
|
time.sleep(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
if args.output:
|
||||||
|
chart.save_to_file(args.output)
|
||||||
|
else:
|
||||||
|
chart.ui_update(final_draw = True)
|
@ -0,0 +1,3 @@
|
|||||||
|
from .stdin import while_stdin
|
||||||
|
from .chart import Chart
|
||||||
|
from .helpers import parse_limits
|
@ -0,0 +1,7 @@
|
|||||||
|
from .chart import Chart
|
||||||
|
|
||||||
|
|
||||||
|
class BarChart(Chart):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.draw_opts = "b."
|
@ -0,0 +1,87 @@
|
|||||||
|
from datetime import datetime, timedelta
|
||||||
|
import matplotlib
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.dates as mdates
|
||||||
|
|
||||||
|
class Chart:
|
||||||
|
def __init__(self, samples = 50, title = None, limits = None, batch=False, show=True, fullscreen=False, read_x=False):
|
||||||
|
self.start = datetime.now()
|
||||||
|
self.x = []
|
||||||
|
self.y = []
|
||||||
|
self.samples = samples
|
||||||
|
self.fig = plt.figure()
|
||||||
|
self.axis = self.fig.add_subplot(111, autoscale_on = True, xticklabels = [])
|
||||||
|
|
||||||
|
scale_axis = "both" if not limits else "x"
|
||||||
|
self.axis.autoscale(enable=True, axis=scale_axis, tight = 2)
|
||||||
|
# save limits
|
||||||
|
self.limits = limits
|
||||||
|
self.title = title
|
||||||
|
|
||||||
|
self.axis.set_label(title)
|
||||||
|
self.fig.canvas.draw()
|
||||||
|
self.draw_opts = 'r-'
|
||||||
|
self.line = False
|
||||||
|
|
||||||
|
# upate timeout code (limit to 10fps)
|
||||||
|
self.last_update = datetime.min
|
||||||
|
self.update_timeout = timedelta(seconds=0.1)
|
||||||
|
|
||||||
|
self.read_x = read_x
|
||||||
|
|
||||||
|
# if batch mode, do not use timestamp when read from stdin
|
||||||
|
self.batch = batch
|
||||||
|
self.fullscreen = fullscreen
|
||||||
|
|
||||||
|
if show:
|
||||||
|
plt.show(block=False)
|
||||||
|
|
||||||
|
def ui_update(self, final_draw=False):
|
||||||
|
if len(self.y) < 2:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not final_draw and datetime.now() - self.last_update < self.update_timeout:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.axis.clear()
|
||||||
|
# call plot method that is easily overrideable
|
||||||
|
self.plot()
|
||||||
|
|
||||||
|
if self.limits:
|
||||||
|
self.axis.set_ylim(self.limits)
|
||||||
|
|
||||||
|
if not self.batch:
|
||||||
|
self.axis.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M:%S"))
|
||||||
|
self.fig.autofmt_xdate()
|
||||||
|
|
||||||
|
|
||||||
|
if self.fullscreen:
|
||||||
|
plt.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
|
||||||
|
else:
|
||||||
|
plt.title(self.title)
|
||||||
|
|
||||||
|
self.fig.canvas.draw()
|
||||||
|
self.fig.canvas.flush_events()
|
||||||
|
self.last_update = datetime.now()
|
||||||
|
|
||||||
|
|
||||||
|
def plot(self):
|
||||||
|
|
||||||
|
if self.batch:
|
||||||
|
self.axis.plot(self.y, self.draw_opts)
|
||||||
|
else:
|
||||||
|
self.axis.plot_date(self.x, self.y, self.draw_opts)
|
||||||
|
|
||||||
|
def read_input(self, input):
|
||||||
|
self.y.append(float(input))
|
||||||
|
self.x.append(datetime.now())
|
||||||
|
|
||||||
|
if self.samples > 0 and len(self.y) > self.samples:
|
||||||
|
self.y.pop(0)
|
||||||
|
self.x.pop(0)
|
||||||
|
|
||||||
|
self.ui_update()
|
||||||
|
|
||||||
|
def save_to_file(self, file):
|
||||||
|
self.ui_update(final_draw = True)
|
||||||
|
plt.savefig(file)
|
@ -0,0 +1,22 @@
|
|||||||
|
def parse_limits(limits):
|
||||||
|
if not limits:
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
|
limits = limits.strip()
|
||||||
|
|
||||||
|
if limits[0] == '-':
|
||||||
|
return [None, int(limits[1:])]
|
||||||
|
elif limits[-1] == '-':
|
||||||
|
return [int(limits[:-1]), None]
|
||||||
|
|
||||||
|
limits = limits.split("-")
|
||||||
|
|
||||||
|
if len(limits) != 2:
|
||||||
|
print("unknown limits: " + str(limits) + " - using no limits")
|
||||||
|
return [None, None]
|
||||||
|
|
||||||
|
try:
|
||||||
|
return [int(x) for x in limits]
|
||||||
|
except ValueError as exp:
|
||||||
|
print(exp + " - using no limits")
|
||||||
|
return [None, None]
|
@ -0,0 +1,8 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
def while_stdin(func):
|
||||||
|
while True:
|
||||||
|
line = sys.stdin.readline()
|
||||||
|
if line is "":
|
||||||
|
break
|
||||||
|
func(line[:-1])
|
@ -0,0 +1,169 @@
|
|||||||
|
54.625
|
||||||
|
54.25
|
||||||
|
53.75
|
||||||
|
53.25
|
||||||
|
53
|
||||||
|
52.5
|
||||||
|
52.125
|
||||||
|
51.875
|
||||||
|
51.5
|
||||||
|
51
|
||||||
|
50.75
|
||||||
|
50.375
|
||||||
|
50
|
||||||
|
49.625
|
||||||
|
49.25
|
||||||
|
67.75
|
||||||
|
77.5
|
||||||
|
78.5
|
||||||
|
79.5
|
||||||
|
80.375
|
||||||
|
81.375
|
||||||
|
82.25
|
||||||
|
82.75
|
||||||
|
83
|
||||||
|
83.5
|
||||||
|
83.75
|
||||||
|
84
|
||||||
|
84
|
||||||
|
84
|
||||||
|
84.375
|
||||||
|
84.75
|
||||||
|
84.75
|
||||||
|
84.875
|
||||||
|
84.875
|
||||||
|
85
|
||||||
|
85.25
|
||||||
|
85.5
|
||||||
|
85.5
|
||||||
|
85.75
|
||||||
|
85.75
|
||||||
|
86
|
||||||
|
86.25
|
||||||
|
86.5
|
||||||
|
86.875
|
||||||
|
87
|
||||||
|
87.25
|
||||||
|
87.375
|
||||||
|
87.75
|
||||||
|
88.125
|
||||||
|
88.5
|
||||||
|
88.5
|
||||||
|
88.5
|
||||||
|
88.5
|
||||||
|
88.5
|
||||||
|
88.625
|
||||||
|
88.75
|
||||||
|
89
|
||||||
|
89.125
|
||||||
|
89.25
|
||||||
|
89.25
|
||||||
|
89.5
|
||||||
|
89.75
|
||||||
|
89.875
|
||||||
|
90
|
||||||
|
90
|
||||||
|
90.125
|
||||||
|
90.375
|
||||||
|
90.5
|
||||||
|
90.625
|
||||||
|
90.625
|
||||||
|
90.75
|
||||||
|
90.875
|
||||||
|
91
|
||||||
|
91.125
|
||||||
|
91.375
|
||||||
|
91.5
|
||||||
|
91.5
|
||||||
|
91.5
|
||||||
|
91.75
|
||||||
|
91.75
|
||||||
|
92
|
||||||
|
92
|
||||||
|
92
|
||||||
|
92.125
|
||||||
|
92.25
|
||||||
|
92.25
|
||||||
|
92.25
|
||||||
|
92.375
|
||||||
|
92.5
|
||||||
|
92.5
|
||||||
|
92.5
|
||||||
|
92.5
|
||||||
|
92.5
|
||||||
|
92.375
|
||||||
|
91.875
|
||||||
|
91.25
|
||||||
|
90.75
|
||||||
|
90.125
|
||||||
|
89.625
|
||||||
|
89
|
||||||
|
88.5
|
||||||
|
87.875
|
||||||
|
87.375
|
||||||
|
86.875
|
||||||
|
86.25
|
||||||
|
85.75
|
||||||
|
85.125
|
||||||
|
84.625
|
||||||
|
84
|
||||||
|
83.5
|
||||||
|
82.875
|
||||||
|
82.375
|
||||||
|
81.75
|
||||||
|
81.25
|
||||||
|
80.625
|
||||||
|
80.125
|
||||||
|
79.5
|
||||||
|
79
|
||||||
|
78.375
|
||||||
|
77.875
|
||||||
|
77.25
|
||||||
|
76.75
|
||||||
|
76.25
|
||||||
|
75.625
|
||||||
|
75.125
|
||||||
|
74.5
|
||||||
|
74
|
||||||
|
73.375
|
||||||
|
72.875
|
||||||
|
72.25
|
||||||
|
71.75
|
||||||
|
71.125
|
||||||
|
70.625
|
||||||
|
70
|
||||||
|
69.5
|
||||||
|
68.875
|
||||||
|
68.375
|
||||||
|
67.75
|
||||||
|
67.25
|
||||||
|
66.75
|
||||||
|
66.25
|
||||||
|
65.75
|
||||||
|
65.25
|
||||||
|
64.75
|
||||||
|
64.25
|
||||||
|
63.875
|
||||||
|
63.375
|
||||||
|
62.875
|
||||||
|
62.5
|
||||||
|
62
|
||||||
|
61.5
|
||||||
|
61.125
|
||||||
|
60.625
|
||||||
|
60.125
|
||||||
|
59.75
|
||||||
|
59.25
|
||||||
|
58.875
|
||||||
|
58.5
|
||||||
|
58.125
|
||||||
|
66.75
|
||||||
|
66.25
|
||||||
|
65.625
|
||||||
|
65.25
|
||||||
|
64.625
|
||||||
|
64.125
|
||||||
|
63.625
|
||||||
|
63.125
|
||||||
|
62.625
|
||||||
|
62.125
|
Loading…
Reference in New Issue